diff options
author | Strangerke | 2013-06-26 23:11:34 +0200 |
---|---|---|
committer | Strangerke | 2013-06-26 23:11:34 +0200 |
commit | 6e2d567bca53b6ffee771b4105e2e73dbd73f5b4 (patch) | |
tree | 9880f0c496263ffb6928248d495ce4172dabed18 /engines/hopkins | |
parent | ac387835e4527c1814919093b4e4bc9798d5742d (diff) | |
parent | 6716fa39a6fb2a3925576288c256688c5aadd7e9 (diff) | |
download | scummvm-rg350-6e2d567bca53b6ffee771b4105e2e73dbd73f5b4.tar.gz scummvm-rg350-6e2d567bca53b6ffee771b4105e2e73dbd73f5b4.tar.bz2 scummvm-rg350-6e2d567bca53b6ffee771b4105e2e73dbd73f5b4.zip |
Merge branch 'master' of https://github.com/scummvm/scummvm into mortevielle
Conflicts:
engines/engines.mk
Diffstat (limited to 'engines/hopkins')
37 files changed, 23903 insertions, 0 deletions
diff --git a/engines/hopkins/anim.cpp b/engines/hopkins/anim.cpp new file mode 100644 index 0000000000..2ec9cec009 --- /dev/null +++ b/engines/hopkins/anim.cpp @@ -0,0 +1,797 @@ +/* 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 "hopkins/anim.h" + +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" + +#include "common/system.h" +#include "graphics/palette.h" +#include "common/file.h" +#include "common/rect.h" +#include "engines/util.h" + +namespace Hopkins { + +AnimationManager::AnimationManager(HopkinsEngine *vm) { + _vm = vm; + _clearAnimationFl = false; + for (int i = 0; i < 8; ++i) + Common::fill((byte *)&Bank[i], (byte *)&Bank[i] + sizeof(BankItem), 0); + for (int i = 0; i < 35; ++i) + Common::fill((byte *)&_animBqe[i], (byte *)&_animBqe[i] + sizeof(BqeAnimItem), 0); +} + +void AnimationManager::clearAll() { + initAnimBqe(); +} + +/** + * Play Animation + * @param filename Filename of animation to play + * @param rate1 Delay amount before starting animation + * @param rate2 Delay amount between animation frames + * @param rate3 Delay amount after animation finishes + */ +void AnimationManager::playAnim(const Common::String &hiresName, const Common::String &lowresName, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl) { + Common::File f; + + if (_vm->shouldQuit()) + return; + + _vm->_events->mouseOff(); + + byte *screenP = _vm->_graphicsMan->_backBuffer; + + if (!f.open(hiresName)) { + if (!f.open(lowresName)) + error("Files not found: %s - %s", hiresName.c_str(), lowresName.c_str()); + } + + f.skip(6); + f.read(_vm->_graphicsMan->_palette, 800); + f.skip(4); + size_t nbytes = f.readUint32LE(); + f.skip(14); + f.read(screenP, nbytes); + + if (_clearAnimationFl) + _vm->_graphicsMan->clearScreen(); + + if (skipSeqFl) { + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + } else { + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + _vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + } + _vm->_events->_rateCounter = 0; + _vm->_events->_escKeyFl = false; + _vm->_soundMan->loadAnimSound(); + + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + // Do pre-animation delay + do { + if (_vm->_events->_escKeyFl) + break; + + _vm->_events->refreshEvents(); + } while (!_vm->shouldQuit() && _vm->_events->_rateCounter < rate1); + } + + if (!_vm->_events->_escKeyFl) { + _vm->_events->_rateCounter = 0; + int frameNumber = 0; + while (!_vm->shouldQuit()) { + ++frameNumber; + _vm->_soundMan->playAnimSound(frameNumber); + + byte imageStr[17]; + // Read frame header + if (f.read(imageStr, 16) != 16) + break; + imageStr[16] = 0; + if (strncmp((const char *)imageStr, "IMAGE=", 6)) + break; + + f.read(screenP, READ_LE_UINT32(imageStr + 8)); + + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + do { + if (_vm->_events->_escKeyFl) + break; + + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } while (!_vm->shouldQuit() && _vm->_events->_rateCounter < rate2); + } + + if (!_vm->_events->_escKeyFl) { + _vm->_events->_rateCounter = 0; + if (*screenP != kByteStop) + _vm->_graphicsMan->copyVideoVbe16(screenP); + + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + _vm->_soundMan->checkSoundEnd(); + } + } + } + + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE && !_vm->_events->_escKeyFl) { + // Do post-animation delay + do { + if (_vm->_events->_escKeyFl) + break; + + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } while (_vm->_events->_rateCounter < rate3); + } + + if (!_vm->_events->_escKeyFl) { + _vm->_events->_rateCounter = 0; + _vm->_soundMan->checkSoundEnd(); + } + + if (_vm->_graphicsMan->_fadingFl) { + byte *screenCopy = _vm->_globals->allocMemory(307200); + + f.seek(6); + f.read(_vm->_graphicsMan->_palette, 800); + f.skip(4); + nbytes = f.readUint32LE(); + f.skip(14); + f.read(screenP, nbytes); + + memcpy(screenCopy, screenP, 307200); + + for (;;) { + byte imageStr[17]; + if (f.read(imageStr, 16) != 16) + break; + imageStr[16] = 0; + + if (strncmp((const char *)imageStr, "IMAGE=", 6)) + break; + + f.read(screenP, READ_LE_UINT32(imageStr + 8)); + if (*screenP != kByteStop) + _vm->_graphicsMan->copyWinscanVbe3(screenP, screenCopy); + } + _vm->_graphicsMan->fadeOutDefaultLength(screenCopy); + _vm->_globals->freeMemory(screenCopy); + } + + _vm->_graphicsMan->_fadingFl = false; + f.close(); + _vm->_graphicsMan->_skipVideoLockFl = false; + + _vm->_events->mouseOn(); +} + +/** + * Play Animation, type 2 + */ +void AnimationManager::playAnim2(const Common::String &hiresName, const Common::String &lowresName, uint32 rate1, uint32 rate2, uint32 rate3) { + int oldScrollPosX = 0; + byte *screenP = NULL; + Common::File f; + + if (_vm->shouldQuit()) + return; + + _vm->_events->mouseOff(); + + while (!_vm->shouldQuit()) { + memcpy(_vm->_graphicsMan->_oldPalette, _vm->_graphicsMan->_palette, 769); + + _vm->_graphicsMan->backupScreen(); + + if (!_vm->_graphicsMan->_lineNbr) + _vm->_graphicsMan->_scrollOffset = 0; + + screenP = _vm->_graphicsMan->_backBuffer; + if (!f.open(hiresName)) { + if (!f.open(lowresName)) + error("Error opening files: %s - %s", hiresName.c_str(), lowresName.c_str()); + } + + f.skip(6); + f.read(_vm->_graphicsMan->_palette, 800); + f.skip(4); + size_t nbytes = f.readUint32LE(); + f.skip(14); + + f.read(screenP, nbytes); + + _vm->_graphicsMan->clearPalette(); + oldScrollPosX = _vm->_graphicsMan->_scrollPosX; + _vm->_graphicsMan->setScreenWidth(SCREEN_WIDTH); + _vm->_graphicsMan->scrollScreen(0); + _vm->_graphicsMan->clearScreen(); + _vm->_graphicsMan->_maxX = SCREEN_WIDTH; + + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + _vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + + _vm->_events->_rateCounter = 0; + _vm->_events->_escKeyFl = false; + _vm->_soundMan->loadAnimSound(); + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + while (!_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate1) { + _vm->_events->refreshEvents(); + } + } + break; + } + + if (!_vm->_events->_escKeyFl) { + _vm->_events->_rateCounter = 0; + int frameNumber = 0; + for (;;) { + if (_vm->_events->_escKeyFl) + break; + ++frameNumber; + _vm->_soundMan->playAnimSound(frameNumber); + byte imageStr[17]; + if (f.read(imageStr, 16) != 16) + break; + imageStr[16] = 0; + + if (strncmp((const char *)imageStr, "IMAGE=", 6)) + break; + + f.read(screenP, READ_LE_UINT32(imageStr + 8)); + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + while (!_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate2) { + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } + } + + _vm->_events->_rateCounter = 0; + if (*screenP != kByteStop) + _vm->_graphicsMan->copyVideoVbe16(screenP); + + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + _vm->_soundMan->checkSoundEnd(); + } + + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + while (!_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate3) { + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } + } + } + + _vm->_graphicsMan->_skipVideoLockFl = false; + f.close(); + + if (_vm->_graphicsMan->_fadingFl) { + f.seek(6); + f.read(_vm->_graphicsMan->_palette, 800); + f.skip(4); + size_t nbytes = f.readUint32LE(); + f.skip(14); + f.read(screenP, nbytes); + byte *ptra = _vm->_globals->allocMemory(307200); + memcpy(ptra, screenP, 307200); + + for (;;) { + byte imageStr[17]; + if (f.read(imageStr, 16) != 16) + break; + imageStr[16] = 0; + + if (strncmp((const char *)imageStr, "IMAGE=", 6)) + break; + + f.read(screenP, READ_LE_UINT32(imageStr + 8)); + if (*screenP != kByteStop) + _vm->_graphicsMan->copyWinscanVbe3(screenP, ptra); + } + _vm->_graphicsMan->fadeOutDefaultLength(ptra); + ptra = _vm->_globals->freeMemory(ptra); + } + _vm->_graphicsMan->_fadingFl = false; + + _vm->_graphicsMan->restoreScreen(); + + memcpy(_vm->_graphicsMan->_palette, _vm->_graphicsMan->_oldPalette, 769); + _vm->_graphicsMan->clearPalette(); + _vm->_graphicsMan->clearScreen(); + + _vm->_graphicsMan->_scrollPosX = oldScrollPosX; + _vm->_graphicsMan->scrollScreen(oldScrollPosX); + if (_vm->_graphicsMan->_largeScreenFl) { + _vm->_graphicsMan->setScreenWidth(2 * SCREEN_WIDTH); + _vm->_graphicsMan->_maxX = 2 * SCREEN_WIDTH; + _vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_frontBuffer, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + } else { + _vm->_graphicsMan->setScreenWidth(SCREEN_WIDTH); + _vm->_graphicsMan->_maxX = SCREEN_WIDTH; + _vm->_graphicsMan->clearScreen(); + _vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_frontBuffer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + } + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + _vm->_graphicsMan->fadeInShort(); + _vm->_graphicsMan->updateScreen(); + + _vm->_events->mouseOn(); +} + +/** + * Load Animation + */ +void AnimationManager::loadAnim(const Common::String &animName) { + clearAnim(); + + Common::String filename = animName + ".ANI"; + Common::File f; + if (!f.open(filename)) + error("Failed to open %s", filename.c_str()); + + int filesize = f.size(); + int nbytes = filesize - 115; + + char header[10]; + char dummyBuf[15]; + char filename1[15]; + char filename2[15]; + char filename3[15]; + char filename4[15]; + char filename5[15]; + char filename6[15]; + + f.read(header, 10); + f.read(dummyBuf, 15); + f.read(filename1, 15); + f.read(filename2, 15); + f.read(filename3, 15); + f.read(filename4, 15); + f.read(filename5, 15); + f.read(filename6, 15); + + if (READ_BE_UINT32(header) != MKTAG('A', 'N', 'I', 'S')) + error("Invalid animation File: %s", filename.c_str()); + + const char *files[6] = { &filename1[0], &filename2[0], &filename3[0], &filename4[0], + &filename5[0], &filename6[0] }; + + for (int idx = 0; idx <= 5; ++idx) { + if (files[idx][0]) { + if (!f.exists(files[idx])) + error("Missing file %s in animation File: %s", files[idx], filename.c_str()); + if (loadSpriteBank(idx + 1, files[idx])) + error("Invalid sprite bank in animation File: %s", filename.c_str()); + } + } + + byte *data = _vm->_globals->allocMemory(nbytes + 1); + f.read(data, nbytes); + f.close(); + + for (int idx = 1; idx <= 20; ++idx) + searchAnim(data, idx, nbytes); + + _vm->_globals->freeMemory(data); +} + +/** + * Clear animation + */ +void AnimationManager::clearAnim() { + for (int idx = 0; idx < 35; ++idx) { + _animBqe[idx]._data = _vm->_globals->freeMemory(_animBqe[idx]._data); + _animBqe[idx]._enabledFl = false; + } + + for (int idx = 0; idx < 8; ++idx) { + Bank[idx]._data = _vm->_globals->freeMemory(Bank[idx]._data); + Bank[idx]._loadedFl = false; + Bank[idx]._filename = ""; + Bank[idx]._fileHeader = 0; + } +} + +/** + * Load Sprite Bank + */ +int AnimationManager::loadSpriteBank(int idx, const Common::String &filename) { + int result = 0; + Bank[idx]._loadedFl = true; + Bank[idx]._filename = filename; + + byte *fileDataPtr = _vm->_fileIO->loadFile(filename); + + Bank[idx]._fileHeader = 0; + if (fileDataPtr[1] == 'L' && fileDataPtr[2] == 'E') + Bank[idx]._fileHeader = 1; + else if (fileDataPtr[1] == 'O' && fileDataPtr[2] == 'R') + Bank[idx]._fileHeader = 2; + + if (!Bank[idx]._fileHeader) { + _vm->_globals->freeMemory(fileDataPtr); + Bank[idx]._loadedFl = false; + result = -1; + } + + Bank[idx]._data = fileDataPtr; + + int objectDataIdx = 0; + for(objectDataIdx = 0; objectDataIdx <= 249; objectDataIdx++) { + int width = _vm->_objectsMan->getWidth(fileDataPtr, objectDataIdx); + int height = _vm->_objectsMan->getHeight(fileDataPtr, objectDataIdx); + if (!width && !height) + break; + } + + if (objectDataIdx > 249) { + _vm->_globals->freeMemory(fileDataPtr); + Bank[idx]._loadedFl = false; + result = -2; + } + Bank[idx]._objDataIdx = objectDataIdx; + + Common::String ofsFilename = Bank[idx]._filename; + char ch; + do { + ch = ofsFilename.lastChar(); + ofsFilename.deleteLastChar(); + } while (ch != '.'); + ofsFilename += ".OFS"; + + Common::File f; + if (f.exists(ofsFilename)) { + byte *ofsData = _vm->_fileIO->loadFile(ofsFilename); + byte *curOfsData = ofsData; + for (int objIdx = 0; objIdx < Bank[idx]._objDataIdx; ++objIdx, curOfsData += 8) { + int x1 = READ_LE_INT16(curOfsData); + int y1 = READ_LE_INT16(curOfsData + 2); + int x2 = READ_LE_INT16(curOfsData + 4); + int y2 = READ_LE_INT16(curOfsData + 6); + + _vm->_objectsMan->setOffsetXY(Bank[idx]._data, objIdx, x1, y1, 0); + if (Bank[idx]._fileHeader == 2) + _vm->_objectsMan->setOffsetXY(Bank[idx]._data, objIdx, x2, y2, 1); + } + + _vm->_globals->freeMemory(ofsData); + result = 0; + } + + return result; +} + +/** + * Search Animation + */ +void AnimationManager::searchAnim(const byte *data, int animIndex, int bufSize) { + for (int dataIdx = 0; dataIdx <= bufSize; dataIdx++) { + if (READ_BE_UINT32(&data[dataIdx]) == MKTAG('A', 'N', 'I', 'M')) { + int entryIndex = data[dataIdx + 4]; + if (animIndex == entryIndex) { + int curBufferPos = dataIdx + 5; + int count = 0; + bool innerLoopCond = false; + do { + if (READ_BE_UINT32(&data[curBufferPos]) == MKTAG('A', 'N', 'I', 'M') || READ_BE_UINT24(&data[curBufferPos]) == MKTAG24('F', 'I', 'N')) + innerLoopCond = true; + if (bufSize < curBufferPos) { + _animBqe[animIndex]._enabledFl = false; + _animBqe[animIndex]._data = NULL; + return; + } + ++curBufferPos; + ++count; + } while (!innerLoopCond); + _animBqe[animIndex]._data = _vm->_globals->allocMemory(count + 50); + _animBqe[animIndex]._enabledFl = true; + memcpy(_animBqe[animIndex]._data, data + dataIdx + 5, 20); + + byte *dataP = _animBqe[animIndex]._data; + int curDestDataIndx = 20; + int curSrcDataIndx = dataIdx + 25; + + for (int i = 0; i <= 4999; i++) { + memcpy(dataP + curDestDataIndx, data + curSrcDataIndx, 10); + if (!READ_LE_UINT16(data + curSrcDataIndx + 4)) + break; + curDestDataIndx += 10; + curSrcDataIndx += 10; + } + break; + } + } + if (READ_BE_UINT24(&data[dataIdx]) == MKTAG24('F', 'I', 'N')) + break; + } +} + +/** + * Play sequence + */ +void AnimationManager::playSequence(const Common::String &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipEscFl, bool skipSeqFl, bool noColFl) { + if (_vm->shouldQuit()) + return; + + _vm->_events->_mouseFl = false; + if (!noColFl) { + _vm->_events->refreshScreenAndEvents(); + + _vm->_graphicsMan->backupScreen(); + + if (!_vm->_graphicsMan->_lineNbr) + _vm->_graphicsMan->_scrollOffset = 0; + } + byte *screenP = _vm->_graphicsMan->_backBuffer; + Common::File f; + if (!f.open(file)) + error("Error opening file - %s", file.c_str()); + + f.skip(6); + f.read(_vm->_graphicsMan->_palette, 800); + f.skip(4); + size_t nbytes = f.readUint32LE(); + f.skip(14); + f.read(screenP, nbytes); + + if (skipSeqFl) { + if (!_vm->getIsDemo()) { + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + } + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + } else { + _vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + } + bool skipFl = false; + if (noColFl) + _vm->_graphicsMan->fadeInDefaultLength(screenP); + _vm->_events->_rateCounter = 0; + _vm->_events->_escKeyFl = false; + _vm->_soundMan->loadAnimSound(); + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + do { + if (_vm->shouldQuit() || (_vm->_events->_escKeyFl && !skipEscFl)) { + skipFl = true; + break; + } + + _vm->_events->_escKeyFl = false; + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } while (_vm->_events->_rateCounter < rate1); + } + _vm->_events->_rateCounter = 0; + if (!skipFl) { + int soundNumber = 0; + for (;;) { + ++soundNumber; + _vm->_soundMan->playAnimSound(soundNumber); + byte imageStr[17]; + if (f.read(imageStr, 16) != 16) + break; + imageStr[16] = 0; + + if (strncmp((const char *)imageStr, "IMAGE=", 6)) + break; + + f.read(screenP, READ_LE_UINT32(imageStr + 8)); + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + do { + if (_vm->shouldQuit() || (_vm->_events->_escKeyFl && !skipEscFl)) { + skipFl = true; + break; + } + + _vm->_events->_escKeyFl = false; + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } while (_vm->_events->_rateCounter < rate2); + } + + if (skipFl) + break; + + _vm->_events->_rateCounter = 0; + if (*screenP != kByteStop) + _vm->_graphicsMan->copyVideoVbe16a(screenP); + + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + _vm->_soundMan->checkSoundEnd(); + } + } + + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE && !skipFl) { + do { + if (_vm->shouldQuit() || (_vm->_events->_escKeyFl && !skipEscFl)) { + skipFl = true; + break; + } + + _vm->_events->_escKeyFl = false; + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } while (_vm->_events->_rateCounter < rate3); + } + + if (!skipFl) + _vm->_events->_rateCounter = 0; + + _vm->_graphicsMan->_skipVideoLockFl = false; + f.close(); + + if (!noColFl) { + _vm->_graphicsMan->restoreScreen(); + + _vm->_events->_mouseFl = true; + } +} + +/** + * Play Sequence type 2 + */ +void AnimationManager::playSequence2(const Common::String &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl) { + byte *screenP; + int frameNumber; + Common::File f; + + if (_vm->shouldQuit()) + return; + + _vm->_events->_mouseFl = false; + screenP = _vm->_graphicsMan->_backBuffer; + + if (!f.open(file)) + error("File not found - %s", file.c_str()); + + f.skip(6); + f.read(_vm->_graphicsMan->_palette, 800); + f.skip(4); + size_t nbytes = f.readUint32LE(); + f.skip(14); + f.read(screenP, nbytes); + + if (skipSeqFl) { + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + } else { + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + _vm->_graphicsMan->display8BitRect(screenP, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + } + _vm->_events->_rateCounter = 0; + _vm->_events->_escKeyFl = false; + _vm->_soundMan->loadAnimSound(); + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + do { + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } while (!_vm->shouldQuit() && !_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate1); + } + + if (!_vm->_events->_escKeyFl) { + _vm->_events->_rateCounter = 0; + frameNumber = 0; + while (!_vm->shouldQuit()) { + _vm->_soundMan->playAnimSound(frameNumber++); + + byte imageStr[17]; + if (f.read(imageStr, 16) != 16) + break; + imageStr[16] = 0; + + if (strncmp((const char *)imageStr, "IMAGE=", 6)) + break; + + f.read(screenP, READ_LE_UINT32(imageStr + 8)); + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + do { + _vm->_events->refreshEvents(); + } while (!_vm->shouldQuit() && !_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate2); + } + + _vm->_events->_rateCounter = 0; + if (*screenP != kByteStop) + _vm->_graphicsMan->copyVideoVbe16a(screenP); + + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); + _vm->_soundMan->checkSoundEnd(); + } + } + + if (_vm->_globals->_eventMode == EVENTMODE_IGNORE) { + // Wait for third rate delay + do { + _vm->_events->refreshEvents(); + _vm->_soundMan->checkSoundEnd(); + } while (!_vm->shouldQuit() && !_vm->_events->_escKeyFl && _vm->_events->_rateCounter < rate3); + } + + _vm->_events->_rateCounter = 0; + + if (_vm->_graphicsMan->_fadingFl) { + byte *ptra = _vm->_globals->allocMemory(307200); + + f.seek(6); + f.read(_vm->_graphicsMan->_palette, 800); + f.skip(4); + nbytes = f.readUint32LE(); + f.skip(14); + f.read(screenP, nbytes); + + memcpy(ptra, screenP, 307200); + for (;;) { + byte imageStr[17]; + if (f.read(imageStr, 16) != 16) + break; + imageStr[16] = 0; + + if (strncmp((const char *)imageStr, "IMAGE=", 6)) + break; + + f.read(screenP, READ_LE_UINT32(imageStr + 8)); + if (*screenP != kByteStop) + _vm->_graphicsMan->copyWinscanVbe(screenP, ptra); + } + _vm->_graphicsMan->fadeOutDefaultLength(ptra); + ptra = _vm->_globals->freeMemory(ptra); + } + _vm->_graphicsMan->_fadingFl = false; + + f.close(); + _vm->_events->_mouseFl = true; +} + +void AnimationManager::initAnimBqe() { + for (int idx = 0; idx < 35; ++idx) { + _animBqe[idx]._data = NULL; + _animBqe[idx]._enabledFl = false; + } + + for (int idx = 0; idx < 8; ++idx) { + Bank[idx]._data = NULL; + Bank[idx]._loadedFl = false; + Bank[idx]._filename = ""; + Bank[idx]._fileHeader = 0; + } +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/anim.h b/engines/hopkins/anim.h new file mode 100644 index 0000000000..bf9b55aaae --- /dev/null +++ b/engines/hopkins/anim.h @@ -0,0 +1,78 @@ +/* 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. + * + */ + +#ifndef HOPKINS_ANIM_H +#define HOPKINS_ANIM_H + +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/str.h" +#include "graphics/surface.h" + +namespace Hopkins { + +struct BankItem { + byte *_data; + bool _loadedFl; + Common::String _filename; + int _fileHeader; + int _objDataIdx; +}; + +struct BqeAnimItem { + byte *_data; + bool _enabledFl; +}; + +class HopkinsEngine; + +class AnimationManager { +private: + bool _clearAnimationFl; + + HopkinsEngine *_vm; + + void initAnimBqe(); + int loadSpriteBank(int idx, const Common::String &filename); + void searchAnim(const byte *data, int animIndex, int count); + +public: + BqeAnimItem _animBqe[35]; + BankItem Bank[8]; + + AnimationManager(HopkinsEngine *vm); + void clearAll(); + + void loadAnim(const Common::String &animName); + void clearAnim(); + void playAnim(const Common::String &hiresName, const Common::String &lowresName, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl = false); + void playAnim2(const Common::String &hiresName, const Common::String &lowresName, uint32 rate1, uint32 rate2, uint32 rate3); + void playSequence(const Common::String &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipEscFl, bool skipSeqFl, bool noColFl = false); + void playSequence2(const Common::String &file, uint32 rate1, uint32 rate2, uint32 rate3, bool skipSeqFl = false); + + void setClearAnimFlag() { _clearAnimationFl = true; } + void unsetClearAnimFlag() { _clearAnimationFl = false; } +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_ANIM_H */ diff --git a/engines/hopkins/computer.cpp b/engines/hopkins/computer.cpp new file mode 100644 index 0000000000..f7b923badf --- /dev/null +++ b/engines/hopkins/computer.cpp @@ -0,0 +1,1226 @@ +/* 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 "hopkins/computer.h" + +#include "hopkins/font.h" +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" +#include "hopkins/objects.h" + +#include "common/system.h" +#include "common/file.h" +#include "common/textconsole.h" + +namespace Hopkins { + +ComputerManager::ComputerManager(HopkinsEngine *vm) { + _vm = vm; + + for (int i = 0; i < 50; i++) { + _menuText[i]._actvFl = false; + _menuText[i]._lineSize = 0; + memset(_menuText[i]._line, 0, 90); + } + Common::fill(&_inputBuf[0], &_inputBuf[200], '\0'); + _breakoutSpr = NULL; + _textColor = 0; + _breakoutLevel = (int16 *)NULL; + _breakoutBrickNbr = 0; + _breakoutScore = 0; + _breakoutLives = 0; + _breakoutSpeed = 0; + _ballRightFl = false; + _ballUpFl = false; + _breakoutLevelNbr = 0; + _padPositionX = 0; + _minBreakoutMoveSpeed = 0; + _maxBreakoutMoveSpeed = 0; + _lastBreakoutMoveSpeed = 0; + _breakoutHiscore = 0; +} + +/** + * Sets up textual entry mode. Used by the code for Hopkins computer. + */ +void ComputerManager::setVideoMode() { + setTextMode(); +} + +/** + * Sets up Textual entry mode + */ +void ComputerManager::setTextMode() { + _vm->_graphicsMan->clearPalette(); + _vm->_graphicsMan->clearScreen(); + + _vm->_graphicsMan->_lineNbr = SCREEN_WIDTH; + _vm->_fontMan->_font = _vm->_globals->freeMemory(_vm->_fontMan->_font); + + Common::String filename = "STFONT.SPR"; + Common::File f; + if (!f.exists(filename)) + filename = "FONTE.SPR"; // Used by the BeOS and OS/2 versions as an alternative + _vm->_fontMan->_font = _vm->_fileIO->loadFile(filename); + _vm->_fontMan->_fontFixedWidth = 8; + _vm->_fontMan->_fontFixedHeight = 8; + + _vm->_graphicsMan->loadImage("WINTEXT"); + _vm->_graphicsMan->fadeInLong(); + loadMenu(); + _vm->_events->_mouseFl = false; +} + +/** + * Clear the screen + */ +void ComputerManager::clearScreen() { + _vm->_graphicsMan->loadImage("WINTEXT"); + _vm->_graphicsMan->fadeInLong(); +} + +/** + * Sets the text mode color + */ +void ComputerManager::setTextColor(int col) { + _textColor = col; +} + +/** + * Sets the text position. + * @param yp Y position + * @param xp X position + * @remarks Yes, the reverse co-ordinate pair is really like that in the original game. + */ +void ComputerManager::setTextPosition(int yp, int xp) { + _textPosition.x = xp << 3; + _textPosition.y = yp << 4; +} + +/** + * Show a computer in the FBI office + * @param mode Which computer to display + */ +void ComputerManager::showComputer(ComputerEnum mode) { + _vm->_events->_escKeyFl = false; + _vm->_graphicsMan->resetDirtyRects(); + setVideoMode(); + setTextColor(4); + setTextPosition(2, 4); + if (mode == COMPUTER_HOPKINS) + outText(Common::String(_menuText[0]._line)); + else if (mode == COMPUTER_SAMANTHA) + outText(Common::String(_menuText[1]._line)); + else // COMPUTER_PUBLIC + outText(Common::String(_menuText[2]._line)); + + setTextColor(1); + if (mode == COMPUTER_PUBLIC) { + setTextPosition(10, 8); + outText(Common::String(_menuText[3]._line)); + } + setTextPosition(12, 28); + outText(Common::String(_menuText[4]._line)); + setTextPosition(14, 35); + + displayMessage(280, 224, 8); + bool passwordMatch = false; + if ((mode == COMPUTER_HOPKINS) && !strcmp(_inputBuf, "HOPKINS")) + passwordMatch = true; + else if ((mode == COMPUTER_SAMANTHA) && !strcmp(_inputBuf, "328MHZA")) + passwordMatch = true; + else if ((mode == COMPUTER_PUBLIC) && !strcmp(_inputBuf, "ALLFREE")) + passwordMatch = true; + + if (passwordMatch) { + while (!_vm->shouldQuit()) { + _vm->_events->_escKeyFl = false; + clearScreen(); + setTextColor(4); + setTextPosition(2, 4); + if (mode == COMPUTER_HOPKINS) + outText(Common::String(_menuText[0]._line)); + else if (mode == COMPUTER_SAMANTHA) + outText(Common::String(_menuText[1]._line)); + else if (mode == COMPUTER_PUBLIC) + outText(Common::String(_menuText[2]._line)); + setTextColor(15); + setTextPosition(8, 25); + setTextColor(15); + outText2(Common::String(_menuText[6]._line)); + setTextPosition(20, 25); + outText2(Common::String(_menuText[7]._line)); + if (mode == COMPUTER_HOPKINS) { + setTextPosition(10, 25); + outText2(Common::String(_menuText[8]._line)); + setTextPosition(12, 25); + outText2(Common::String(_menuText[9]._line)); + setTextPosition(14, 25); + outText2(Common::String(_menuText[10]._line)); + setTextPosition(16, 25); + outText2(Common::String(_menuText[11]._line)); + } else if (mode == COMPUTER_SAMANTHA) { + setTextPosition(10, 25); +// outText2(Common::String(_menuText[0x95A])); <=== CHECKME: Unexpected value! replaced by the following line, for consistancy + outText2(Common::String(_menuText[12]._line)); + setTextPosition(12, 25); + outText2(Common::String(_menuText[13]._line)); + setTextPosition(14, 25); + outText2(Common::String(_menuText[14]._line)); + setTextPosition(16, 25); + outText2(Common::String(_menuText[15]._line)); + setTextPosition(18, 25); + outText2(Common::String(_menuText[16]._line)); + } + + bool numericFlag = false; + char keyPressed; + do { + keyPressed = _vm->_events->waitKeyPress(); + if (_vm->shouldQuit()) + return; + + if ((keyPressed >= '0') && (keyPressed <= '9')) + numericFlag = true; + } while (!numericFlag); + + // 0 - Quit + if (keyPressed == '0') + break; + // 1 - Games + if (keyPressed == '1') { + displayGamesSubMenu(); + } else if (mode == COMPUTER_HOPKINS) { + clearScreen(); + setTextColor(4); + setTextPosition(2, 4); + outText(Common::String(_menuText[0]._line)); + setTextColor(15); + switch (keyPressed) { + case '2': + readText(1); + break; + case '3': + readText(2); + break; + case '4': + readText(3); + break; + case '5': + readText(4); + break; + } + } else if (mode == COMPUTER_SAMANTHA) { + clearScreen(); + setTextColor(4); + setTextPosition(2, 4); + outText(Common::String(_menuText[1]._line)); + setTextColor(15); + switch (keyPressed) { + case '2': + readText(6); + break; + case '3': + readText(7); + break; + case '4': + readText(8); + break; + case '5': + readText(9); + break; + case '6': + readText(10); + _vm->_globals->_saveData->_data[svField270] = 4; + break; + } + } + } + _vm->_graphicsMan->clearScreen(); + _vm->_graphicsMan->updateScreen(); + restoreFBIRoom(); + } else { + // Password doesn't match - Access Denied + setTextColor(4); + setTextPosition(16, 25); + outText(Common::String(_menuText[5]._line)); + _vm->_events->refreshScreenAndEvents(); + _vm->_events->delay(1000); + + memset(_vm->_graphicsMan->_frontBuffer, 0, 307199); + _vm->_graphicsMan->clearScreen(); + _vm->_graphicsMan->updateScreen(); + restoreFBIRoom(); + _vm->_events->mouseOff(); + } + + if (mode == COMPUTER_HOPKINS) + _vm->_globals->_exitId = 13; + else // Free access or Samantha + _vm->_globals->_exitId = 14; + + _vm->_graphicsMan->resetDirtyRects(); +} + +static const char _englishText[] = +"% ****** FBI COMPUTER NUMBER 4985 ****** J.HOPKINS COMPUTER ******\n" +"% ****** FBI COMPUTER NUMBER 4998 ****** S.COLLINS COMPUTER ******\n" +"% ****** FBI COMPUTER NUMBER 4997 ****** ACCES FREE COMPUTER ******\n" +"% PASSWORD IS: ALLFREE\n% ENTER CURRENT PASSWORD\n" +"% ****** ACCES DENIED ******\n" +"% 1) *** GAME ***\n" +"% 0) QUIT COMPUTER\n" +"% 2) STRANGE CADAVER\n" +"% 3) STRANGE CADAVER\n" +"% 4) SENATOR FERGUSSON\n" +"% 5) DOG KILLER\n" +"% 2) SCIENTIST KIDNAPPED.\n" +"% 3) SCIENTIST KIDNAPPED (next).\n" +"% 4) SCIENTIST KIDNAPPED (next).\n" +"% 5) SCIENTIST KIDNAPPED (next).\n" +"% 6) SCIENTIST KIDNAPPED (next).\n" +"%% fin\n"; + +static const char _frenchText[] = +"% ****** FBI COMPUTER NUMBER 4985 ****** J.HOPKINS COMPUTER ******\n" +"% ****** FBI COMPUTER NUMBER 4998 ****** S.COLLINS COMPUTER ******\n" +"% ****** FBI COMPUTER NUMBER 4997 ****** ACCES FREE COMPUTER ******\n" +"% PASSWORD IS: ALLFREE\n" +"% ENTER CURRENT PASSWORD\n" +"% ****** ACCES DENIED ******\n" +"% 1) *** CASSE BRIQUE ***\n" +"% 0) QUITTER L'ORDINATEUR\n" +"% 2) CADAVRE SANS TETE\n" +"% 3) CADAVRE SANS TETE\n" +"% 4) AGRESSION DU SENATEUR\n" +"% 5) LES CHIENS TUEURS\n" +"% 2) DISPARITIONS DE CHERCHEURS.\n" +"% 3) DISPARITIONS (suite).\n" +"% 4) DISPARITIONS (suite).\n" +"% 5) DISPARITIONS (suite).\n" +"% 6) DISPARITIONS (suite).\n" +"%% fin\n"; + +static const char _spanishText[] = +"% **** ORDENADOR DEL FBI NUMERO 4985 **** ORDENADOR J.HOPKINS *****\n" +"% **** ORDENADOR DEL FBI NUMERO 4998 **** ORDENADOR S.COLLINS *****\n" +"% *** ORDENADOR DEL FBI NUMERO 4997 *** ORDENADOR DE ACCESO LIBRE ***\n" +"% LA CONTRASE\0245A ES: ALLFREE\n" +"% ESCRIBE CONTRASE\0245A ACTUAL\n" +"% **** ACCESO DENEGADO ****\n" +"% 1) *** JUEGO ***\n" +"% 0) SALIR DEL ORDENADOR\n" +"% 2) CADAVER EXTRA\0245O\n" +"% 3) CADAVER EXTRA\0245O\n" +"% 4) SENADOR FERGUSSON\n" +"% 5) MATAPERROS\n" +"% 2) CIENTIFICO SECUESTRADO.\n" +"% 3) CIENTIFICO SECUESTRADO (siguiente).\n" +"% 4) CIENTIFICO SECUESTRADO (siguiente).\n" +"% 5) CIENTIFICO SECUESTRADO (siguiente).\n" +"% 6) CIENTIFICO SECUESTRADO (siguiente).\n" +"%% fin\n"; + +/** + * Load Menu data + */ +void ComputerManager::loadMenu() { + char *ptr; + if (_vm->_fileIO->fileExists("COMPUTAN.TXT")) { + ptr = (char *)_vm->_fileIO->loadFile("COMPUTAN.TXT"); + } else if (_vm->_globals->_language == LANG_FR) { + ptr = (char *)_vm->_globals->allocMemory(sizeof(_frenchText)); + strcpy(ptr, _frenchText); + } else if (_vm->_globals->_language == LANG_SP) { + ptr = (char *)_vm->_globals->allocMemory(sizeof(_spanishText)); + strcpy(ptr, _spanishText); + } else { + ptr = (char *)_vm->_globals->allocMemory(sizeof(_englishText)); + strcpy(ptr, _englishText); + } + + char *tmpPtr = ptr; + int lineNum = 0; + int strPos; + bool loopCond = false; + + do { + if (tmpPtr[0] == '%') { + if (tmpPtr[1] == '%') { + loopCond = true; + break; + } + _menuText[lineNum]._actvFl = 1; + strPos = 0; + while (strPos <= 89) { + char curChar = tmpPtr[strPos + 2]; + if (curChar == '%' || curChar == 10) + break; + _menuText[lineNum]._line[strPos++] = curChar; + } + if (strPos <= 89) { + _menuText[lineNum]._line[strPos] = 0; + _menuText[lineNum]._lineSize = strPos - 1; + } + ++lineNum; + } + ++tmpPtr; + } while (!loopCond); + _vm->_globals->freeMemory((byte *)ptr); +} + +void ComputerManager::displayMessage(int xp, int yp, int textIdx) { + char curChar; + + int x1 = xp; + int x2 = 0; + + int textIndex = 0; + bool oldMouseFlag = _vm->_events->_mouseFl; + _vm->_events->_mouseFl = false; + + _vm->_fontMan->displayTextVesa(xp, yp, "_", 252); + do { + curChar = _vm->_events->waitKeyPress(); + if (_vm->shouldQuit()) + return; + + char mappedChar = '*'; + + if ((curChar == '-') || ((curChar >= '0') && (curChar <= '9')) || ((curChar >= 'A') && (curChar <= 'Z'))) + mappedChar = curChar; + else if ((curChar >= 'a') && (curChar <= 'z')) + mappedChar = curChar - 32; + + // BackSpace + if (curChar == 8 && textIndex > 0) { + _inputBuf[textIndex--] = 0; + x1 -= _vm->_fontMan->_fontFixedWidth; + x2 = x1 + 2 * _vm->_fontMan->_fontFixedWidth; + _vm->_graphicsMan->copyRect(_vm->_graphicsMan->_backBuffer, x1, yp, 3 * _vm->_fontMan->_fontFixedWidth, 12, _vm->_graphicsMan->_frontBuffer, x1, yp); + _vm->_graphicsMan->addDirtyRect(x1, yp, x2, yp + 12); + _vm->_fontMan->displayTextVesa(x1, yp, "_", 252); + } + if (mappedChar != '*') { + char newChar = mappedChar; + _vm->_graphicsMan->copyRect(_vm->_graphicsMan->_backBuffer, x1, yp, _vm->_fontMan->_fontFixedWidth, 12, _vm->_graphicsMan->_frontBuffer, x1, yp); + _vm->_graphicsMan->addDirtyRect(x1, yp, _vm->_fontMan->_fontFixedWidth + x1, yp + 12); + _inputBuf[textIndex] = newChar; + + Common::String charString = Common::String::format("%c_", newChar); + _vm->_fontMan->displayTextVesa(x1, yp, charString, 252); + ++textIndex; + x1 += _vm->_fontMan->_fontFixedWidth; + } + _vm->_events->refreshScreenAndEvents(); + } while (textIndex != textIdx && curChar != 13); + + _vm->_graphicsMan->copyRect(_vm->_graphicsMan->_backBuffer, x1, yp, _vm->_fontMan->_fontFixedWidth, 12, _vm->_graphicsMan->_frontBuffer, x1, yp); + _vm->_graphicsMan->addDirtyRect(x1, yp, _vm->_fontMan->_fontFixedWidth + x1, yp + 12); + + _vm->_events->refreshScreenAndEvents(); + _inputBuf[textIndex] = 0; + _vm->_events->_mouseFl = oldMouseFlag; +} + +/** + * Outputs a text string + */ +void ComputerManager::outText(const Common::String &msg) { + _vm->_fontMan->renderTextDisplay(_textPosition.x, _textPosition.y, msg, _textColor); +} + +/** + * Outputs a text string + */ +void ComputerManager::outText2(const Common::String &msg) { + _vm->_fontMan->displayTextVesa(_textPosition.x, _textPosition.y, msg, _textColor); +} + +/** + * Restores the scene for the FBI headquarters room + */ +void ComputerManager::restoreFBIRoom() { + _vm->_graphicsMan->fadeOutShort(); + + _vm->_globals->freeMemory(_vm->_fontMan->_font); + _vm->_fontMan->_font = _vm->_fileIO->loadFile("FONTE3.SPR"); + _vm->_fontMan->_fontFixedWidth = 12; + _vm->_fontMan->_fontFixedHeight = 21; + + _vm->_events->_mouseFl = true; +} + +/** + * Display texts for the given menu entry + */ +void ComputerManager::readText(int idx) { + _vm->_events->_escKeyFl = false; + + Common::String filename; + if (_vm->_globals->_language == LANG_EN) + filename = "THOPKAN.TXT"; + else if (_vm->_globals->_language == LANG_FR) + filename = "THOPK.TXT"; + else if (_vm->_globals->_language == LANG_SP) + filename = "THOPKES.TXT"; + + byte *ptr = _vm->_fileIO->loadFile(filename); + uint16 fileSize = _vm->_fileIO->fileSize(filename); + int pos; + for (pos = 0; pos < fileSize; pos++) { + if (ptr[pos] == '%') { + Common::String numStr = Common::String::format("%c%c", ptr[pos + 1], ptr[pos + 2]); + if (idx == atol(numStr.c_str())) + break; + } + } + if (pos > fileSize - 3) + error("Error with Hopkins computer file"); + + pos += 3; + int lineNum = 5; + Common::String curStr = ""; + byte curChar; + do { + curChar = ptr[pos]; + if (curChar == 13) { + setTextPosition(lineNum, 1); + outText(curStr); + + ++lineNum; + _vm->_events->refreshScreenAndEvents(); + curStr = ""; + } else if (curChar != '%') { + curStr += curChar; + } + ++pos; + assert(pos <= fileSize); + } while (curChar != '%'); + + _vm->_events->waitKeyPress(); + ptr = _vm->_globals->freeMemory(ptr); +} + +/** + * Display breakout when Games sub-menu is selected + */ +void ComputerManager::displayGamesSubMenu() { + const byte *oldSpriteData = _vm->_objectsMan->_sprite[0]._spriteData; + uint oldSpeed = _vm->_globals->_speed; + + _vm->_globals->_speed = 1; + _vm->_events->changeMouseCursor(0); + _breakoutSpr = NULL; + _vm->_events->_breakoutFl = true; + _breakoutLevel = (int16 *)NULL; + _breakoutBrickNbr = 0; + _breakoutScore = 0; + _breakoutLives = 5; + _breakoutSpeed = 1; + _ballRightFl = false; + _ballUpFl = false; + _breakoutLevelNbr = 0; + _vm->_graphicsMan->_minY = 0; + _vm->_graphicsMan->_maxX = 320; + _vm->_graphicsMan->_maxY = 200; + _vm->_soundMan->loadSample(1, "SOUND37.WAV"); + _vm->_soundMan->loadSample(2, "SOUND38.WAV"); + _vm->_soundMan->loadSample(3, "SOUND39.WAV"); + _breakoutSpr = _vm->_fileIO->loadFile("CASSE.SPR"); + loadHiscore(); + setModeVGA256(); + + newLevel(); + _vm->_graphicsMan->updateScreen(); + + playBreakout(); + _vm->_graphicsMan->resetDirtyRects(); + _breakoutSpr = _vm->_globals->freeMemory(_breakoutSpr); + _breakoutLevel = (int16 *)_vm->_globals->freeMemory((byte *)_breakoutLevel); + _vm->_objectsMan->_sprite[0]._spriteData = oldSpriteData; + + _vm->_soundMan->removeSample(1); + _vm->_soundMan->removeSample(2); + _vm->_soundMan->removeSample(3); + _vm->_globals->_speed = oldSpeed; + _vm->_events->_breakoutFl = false; + setVideoMode(); + setTextColor(15); + clearScreen(); + _vm->_graphicsMan->_maxX = 680; + _vm->_graphicsMan->_minY = 0; + _vm->_graphicsMan->_maxY = 460; +} + +/** + * Load Highscore from file + */ +void ComputerManager::loadHiscore() { + byte *ptr = _vm->_globals->allocMemory(100); + _vm->_saveLoad->load("HISCORE.DAT", ptr); + + for (int scoreIndex = 0; scoreIndex < 6; ++scoreIndex) { + for (int i = 0; i < 5; ++i) { + char nextChar = ptr[(16 * scoreIndex) + i]; + if (!nextChar) + nextChar = ' '; + _score[scoreIndex]._name += nextChar; + } + + for (int i = 0; i < 9; ++i) { + char nextChar = ptr[(scoreIndex * 16) + 6 + i]; + if (!nextChar) + nextChar = '0'; + _score[scoreIndex]._score += nextChar; + } + } + + _vm->_globals->freeMemory(ptr); + _breakoutHiscore = atol(_score[5]._score.c_str()); +} + +/** + * VGA 256 col + */ +void ComputerManager::setModeVGA256() { + _vm->_graphicsMan->clearScreen(); + _vm->_graphicsMan->clearPalette(); + _vm->_graphicsMan->setScreenWidth(320); +} + +/** + * Load new level + */ +void ComputerManager::newLevel() { + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->removeSprite(1); + ++_breakoutLives; + if (_breakoutLives > 11) + _breakoutLives = 11; + _vm->_graphicsMan->loadVgaImage("CASSEF.PCX"); + displayLives(); + _breakoutLevel = (int16 *)_vm->_globals->freeMemory((byte *)_breakoutLevel); + + ++_breakoutLevelNbr; + Common::String file; + Common::File f; + while (!_vm->shouldQuit()) { + file = Common::String::format("TAB%d.TAB", _breakoutLevelNbr); + if (f.open(file)) + break; + + _breakoutLevelNbr = 1; + } + f.close(); + + _breakoutLevel = (int16 *)_vm->_fileIO->loadFile(file); + displayBricks(); + + _vm->_objectsMan->addStaticSprite(_breakoutSpr, Common::Point(150, 192), 0, 13, 0, false, 0, 0); + _vm->_objectsMan->addStaticSprite(_breakoutSpr, Common::Point(164, 187), 1, 14, 0, false, 0, 0); + + _ballPosition = Common::Point(164, 187); + _padPositionX = 150; + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->animateSprite(1); + + _vm->_events->mouseOn(); + _vm->_soundMan->playSample(3, 5); +} + +/** + * Display bricks in breakout game + */ +void ComputerManager::displayBricks() { + _breakoutBrickNbr = 0; + _breakoutSpeed = 1; + int16 *level = _breakoutLevel; + + int cellLeft; + int cellTop; + int cellType; + for (int levelIdx = 0; ; levelIdx += 6) { + cellLeft = (int16)FROM_LE_16(level[levelIdx]); + if (cellLeft == -1) + break; + cellTop = FROM_LE_16(level[levelIdx + 1]); + cellType = FROM_LE_16(level[levelIdx + 4]); + + if (cellType <= 6) + ++_breakoutBrickNbr; + + switch (cellType) { + case 1: + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 21); + break; + case 2: + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 22); + break; + case 3: + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 17); + break; + case 4: + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 20); + break; + case 5: + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 19); + break; + case 6: + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 18); + break; + case 31: + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellTop, 23); + break; + } + } + + displayScore(); + + // Refresh the entire screen + _vm->_graphicsMan->addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + _vm->_graphicsMan->updateScreen(); +} + +/** + * Display Lives in breakout game + */ +void ComputerManager::displayLives() { + for (int i = 0, xp = 10; i <= 11; i++, xp += 7) + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, xp, 10, 15); + + for (int i = 0, xp = 10; i < _breakoutLives - 1; i++, xp += 7) + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, xp, 10, 14); + + _vm->_graphicsMan->updateScreen(); +} + +/** + * Main function for breakout game + */ +void ComputerManager::playBreakout() { + int lastBreakoutEvent = 0; + while (!_vm->shouldQuit()) { + while (!_vm->shouldQuit()) { + // Set up the racket and ball + _vm->_events->mouseOff(); + _ballPosition = Common::Point(_padPositionX + 14, 187); + _vm->_objectsMan->setSpriteY(1, 187); + _vm->_objectsMan->setSpriteX(1, _ballPosition.x); + + _vm->_graphicsMan->resetDirtyRects(); + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->fadeInBreakout(); + + // Wait for mouse press to start playing + do { + _padPositionX = _vm->_events->getMouseX(); + if (_vm->_events->_mousePos.x <= 4) + _padPositionX = 5; + if (_padPositionX > 282) + _padPositionX = 282; + _vm->_objectsMan->setSpriteX(0, _padPositionX); + _vm->_objectsMan->setSpriteX(1, _padPositionX + 14); + _vm->_objectsMan->setSpriteY(1, 187); + _vm->_events->refreshScreenAndEvents(); + } while (!_vm->shouldQuit() && _vm->_events->getMouseButton() != 1); + + _breakoutSpeed = 1; + _ballPosition = Common::Point(_padPositionX + 14, 187); + _ballRightFl = (_padPositionX > 135); + _ballUpFl = false; + + // Play loop + do { + _vm->_soundMan->checkSounds(); + + _padPositionX = _vm->_events->getMouseX(); + if (_vm->_events->_mousePos.x <= 4) + _padPositionX = 5; + if (_padPositionX > 282) + _padPositionX = 282; + _vm->_objectsMan->setSpriteX(0, _padPositionX); + lastBreakoutEvent = moveBall(); + _vm->_events->refreshScreenAndEvents(); + } while (!_vm->shouldQuit() && !lastBreakoutEvent); + if (lastBreakoutEvent != 1) + break; + + --_breakoutLives; + + if (_breakoutLives) { + displayLives(); + if (_breakoutLives) + continue; + } + + _vm->_graphicsMan->fadeOutBreakout(); + _vm->_events->mouseOn(); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->removeSprite(1); + if (_breakoutScore > _breakoutHiscore) + getScoreName(); + if (displayHiscores() != 1) + break; + + _breakoutBrickNbr = 0; + _breakoutScore = 0; + _breakoutLives = 4; + _breakoutSpeed = 1; + _ballRightFl = false; + _ballUpFl = false; + _breakoutLevelNbr = 0; + loadHiscore(); + newLevel(); + } + if (lastBreakoutEvent != 2) + return; + _vm->_graphicsMan->fadeOutBreakout(); + newLevel(); + } +} + +/** + * Show the high scores for the Breakout game + * @return The selected button index: 1 = Game, 2 = Quit + */ +int ComputerManager::displayHiscores() { + _vm->_graphicsMan->resetDirtyRects(); + loadHiscore(); + _vm->_graphicsMan->loadVgaImage("HISCORE.PCX"); + byte *ptr = _vm->_fileIO->loadFile("ALPHA.SPR"); + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + + int yp; + int xp; + // Loop for displaying the scores + for (int scoreIndex = 0; scoreIndex <= 5; scoreIndex++) { + yp = 19 * scoreIndex; + yp += 46; + + // Display the characters of the name + for (int i = 0; i <= 5; i++) + displayHiscoreLine(ptr, 9 * i + 69, yp, _score[scoreIndex]._name[i]); + + // Display the digits of the score + for (int i = 0; i <= 8; i++) + displayHiscoreLine(ptr, 9 * i + 199, yp, _score[scoreIndex]._score[i]); + } + + _vm->_graphicsMan->fadeInBreakout(); + _vm->_graphicsMan->resetDirtyRects(); + int buttonIndex = 0; + do { + _vm->_events->refreshEvents(); + xp = _vm->_events->getMouseX(); + yp = _vm->_events->getMouseY(); + + if (_vm->_events->getMouseButton() == 1 && ABS(xp - 79) <= 33 && ABS(yp - 396) <= 13) + buttonIndex = 1; + else if (_vm->_events->getMouseButton() == 1 && ABS(xp - 583) <= 32 && ABS(yp - 396) <= 13) + buttonIndex = 2; + + _vm->_events->refreshScreenAndEvents(); + } while (!buttonIndex && !_vm->shouldQuit()); + + _vm->_events->mouseOff(); + _vm->_graphicsMan->fadeOutBreakout(); + _vm->_globals->freeMemory(ptr); + return buttonIndex; +} + +/** + * Display a screen to enter player name in the case of a new hiscore + */ +void ComputerManager::getScoreName() { + _vm->_graphicsMan->loadVgaImage("NAME.PCX"); + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + byte *ptr = _vm->_fileIO->loadFile("ALPHA.SPR"); + _vm->_graphicsMan->fadeInBreakout(); + for (int strPos = 0; strPos <= 4; strPos++) { + displayHiscoreLine(ptr, 9 * strPos + 140, 78, 1); + + char curChar = toupper(_vm->_events->waitKeyPress()); + if ((curChar < '0') || (curChar > 'Z')) + curChar = ' '; + if ((curChar > '9') && (curChar < 'A')) + curChar = ' '; + + _score[5]._name.setChar(curChar, strPos); + displayHiscoreLine(ptr, 9 * strPos + 140, 78, curChar); + + for (int idx = 0; idx < 12; ++idx) + _vm->_events->refreshScreenAndEvents(); + } + _score[5]._score = " "; + + char score[16]; + sprintf(score, "%d", _breakoutScore); + int scoreLen = 0; + do + ++scoreLen; + while (score[scoreLen]); + + for (int i = scoreLen, scorePos = 8; i >= 0; i--) { + _score[5]._score.setChar(score[i], scorePos--); + } + _vm->_graphicsMan->fadeOutBreakout(); + _vm->_globals->freeMemory(ptr); + saveScore(); +} + +/** + * Display current score + */ +void ComputerManager::displayScore() { + Common::String scoreStr = Common::String::format("%d", _breakoutScore); + int strSize = scoreStr.size(); + for (int i = strSize - 1, idx = 0; i >= 0; i--) { + displayScoreChar(idx++, scoreStr[i]); + } +} + +/** + * Display a character of the score + */ +void ComputerManager::displayScoreChar(int charPos, int charDisp) { + int xp; + switch (charPos) { + case 1: + xp = 190; + break; + case 2: + xp = 180; + break; + case 3: + xp = 167; + break; + case 4: + xp = 157; + break; + case 5: + xp = 147; + break; + case 9: + xp = 134; + break; + default: + xp = 200; + break; + } + + int idx = 3; + if (charDisp >= '0' && charDisp <= '9') + idx = charDisp - 45; + + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, xp, 11, idx); +} + +/** + * Save Hiscore in file + */ +void ComputerManager::saveScore() { + int scores[6]; + // Load high scores in an array + for (int i = 0; i <= 5; i++) { + scores[i] = atol(_score[i]._score.c_str()); + if (!scores[i]) + scores[i] = 5; + } + + int scorePlace[6]; + // order high scores + for (int scorePlaceIdx = 0; scorePlaceIdx <= 5; scorePlaceIdx++) { + for(int i = 0;;i++) { + int curScore = scores[i]; + if (curScore && scores[0] <= curScore && scores[1] <= curScore && scores[2] <= curScore && scores[3] <= curScore + && scores[4] <= curScore && scores[5] <= curScore) { + scorePlace[scorePlaceIdx] = i; + scores[i] = 0; + break; + } + } + } + + byte *ptr = _vm->_globals->allocMemory(100); + memset(ptr, 0, 99); + for (int scorePlaceIdx = 0; scorePlaceIdx <= 5; scorePlaceIdx++) { + int curBufPtr = 16 * scorePlaceIdx; + for (int namePos = 0; namePos <= 4; namePos++) { + char curChar = _score[scorePlace[scorePlaceIdx]]._name[namePos]; + if (!curChar) + curChar = ' '; + ptr[curBufPtr + namePos] = curChar; + }; + + ptr[curBufPtr + 5] = 0; + + for (int scorePos = 0; scorePos <= 8; scorePos++) { + char curChar = _score[scorePlace[scorePlaceIdx]]._score[scorePos]; + if (!curChar) + curChar = '0'; + ptr[curBufPtr + 6 + scorePos] = curChar; + }; + ptr[curBufPtr + 15] = 0; + } + + _vm->_saveLoad->saveFile("HISCORE.DAT", ptr, 100); + _vm->_globals->freeMemory(ptr); +} + +/** + * Display parts of the hiscore line + */ +void ComputerManager::displayHiscoreLine(const byte *objectData, int x, int y, int curChar) { + int idx = 36; + + if (curChar == 100) + idx = 0; + else if (curChar >= '0' && curChar <= '9') + idx = curChar - '0'; + else if (curChar >= 'A' && curChar <= 'Z') + idx = curChar - 'A' + 10; + else if (curChar == 1) + idx = 37; + _vm->_graphicsMan->fastDisplay2(objectData, x, y, idx); +} + +/** + * Handle ball moves + */ +int ComputerManager::moveBall() { + //(signed int)(6.0 * (long double)_vm->getRandomNumber( rand() / 2147483648.0) + 1; + // TODO: Figure out random number + int randVal = _vm->getRandomNumber(6); + switch (_breakoutSpeed) { + case 1: + _minBreakoutMoveSpeed = 1; + _maxBreakoutMoveSpeed = 1; + break; + case 2: + _minBreakoutMoveSpeed = 1; + _maxBreakoutMoveSpeed = 2; + break; + case 3: + _minBreakoutMoveSpeed = 2; + _maxBreakoutMoveSpeed = 2; + break; + case 4: + _minBreakoutMoveSpeed = 3; + _maxBreakoutMoveSpeed = 2; + break; + } + + int moveSpeed = _minBreakoutMoveSpeed; + if (_lastBreakoutMoveSpeed == _minBreakoutMoveSpeed) + moveSpeed = _maxBreakoutMoveSpeed; + + if (_ballUpFl) + _ballPosition.y += moveSpeed; + else + _ballPosition.y -= moveSpeed; + + if (_ballRightFl) + _ballPosition.x += moveSpeed; + else + _ballPosition.x -= moveSpeed; + + _lastBreakoutMoveSpeed = moveSpeed; + if (_ballPosition.x <= 6) { + _vm->_soundMan->playSample(2, 6); + _ballPosition.x = randVal + 6; + _ballRightFl = !_ballRightFl; + } else if (_ballPosition.x > 307) { + _vm->_soundMan->playSample(2, 6); + _ballPosition.x = 307 - randVal; + _ballRightFl = !_ballRightFl; + } + + if (_ballPosition.y <= 6) { + _vm->_soundMan->playSample(2, 6); + _ballPosition.y = randVal + 7; + _ballUpFl = !_ballUpFl; + } else if (_ballPosition.y >= 186 && _ballPosition.y <= 194) { + _vm->_soundMan->playSample(2, 6); + int ballPosXRight = _ballPosition.x + 6; + if ((_ballPosition.x > _padPositionX - 2) && (ballPosXRight < _padPositionX + 36)) { + _ballUpFl = false; + if (ballPosXRight <= _padPositionX + 15) { + _ballRightFl = false; + if (_ballPosition.x >= _padPositionX && ballPosXRight <= _padPositionX + 5) + _ballPosition.x -= 4; + if (_ballPosition.x >= _padPositionX + 5 && _ballPosition.x + 6 <= _padPositionX + 10) + _ballPosition.x -= 2; + } + if (_ballPosition.x >= _padPositionX + 19 && _ballPosition.x + 6 <= _padPositionX + 36) { + _ballRightFl = true; + if (_ballPosition.x >= _padPositionX + 29) + _ballPosition.x += 4; + if (_ballPosition.x >= _padPositionX + 24 && _ballPosition.x + 6 <= _padPositionX + 29) + _ballPosition.x += 2; + } + } + } + + int retVal = 0; + if (_ballPosition.y > 194) + retVal = 1; + checkBallCollisions(); + _vm->_objectsMan->setSpriteX(1, _ballPosition.x); + _vm->_objectsMan->setSpriteY(1, _ballPosition.y); + if (!_breakoutBrickNbr) + retVal = 2; + return retVal; +} + +/** + * Check ball collision with bricks + */ +void ComputerManager::checkBallCollisions() { + int cellLeft; + + bool brickDestroyedFl = false; + // TODO: Check if correct + int randVal = _vm->getRandomNumber(6) + 1; + int ballLeft = _ballPosition.x; + int ballTop = _ballPosition.y; + int ballRight = _ballPosition.x + 6; + int ballBottom = _ballPosition.y + 6; + int16 *level = _breakoutLevel; + uint16 levelIdx = 0; + do { + cellLeft = level[levelIdx]; + int cellUp = level[levelIdx + 1]; + int cellRight = level[levelIdx + 2]; + int cellBottom = level[levelIdx + 3]; + int cellType = level[levelIdx + 4]; + if (level[levelIdx + 5] == 1 && cellLeft != -1) { + bool collisionFl = false; + if (ballTop <= cellBottom && ballBottom >= cellBottom) { + if (ballLeft >= cellLeft && ballRight <= cellRight) { + collisionFl = true; + _ballUpFl = true; + } + if ((ballRight >= cellLeft) && (ballLeft <= cellLeft)) { + collisionFl = true; + _ballUpFl = true; + _ballRightFl = false; + if (cellType == 31) + _ballPosition.x -= randVal; + } + if ((ballLeft <= cellRight) && (ballRight >= cellRight)) { + collisionFl = true; + _ballUpFl = true; + _ballRightFl = true; + if (cellType == 31) + _ballPosition.x += randVal; + } + } + if (ballBottom >= cellUp && ballTop <= cellUp) { + if (ballLeft >= cellLeft && ballRight <= cellRight) { + collisionFl = true; + _ballUpFl = false; + } + if ((ballRight >= cellLeft) && (ballLeft <= cellLeft)) { + collisionFl = true; + _ballUpFl = false; + _ballRightFl = false; + if (cellType == 31) + _ballPosition.x -= 2; + } + if ((ballLeft <= cellRight) && (ballRight >= cellRight)) { + collisionFl = true; + _ballUpFl = false; + _ballRightFl = true; + if (cellType == 31) + _ballPosition.x += randVal; + } + } + if ((ballTop >= cellUp) && (ballBottom <= cellBottom)) { + if ((ballRight >= cellLeft) && (ballLeft <= cellLeft)) { + collisionFl = true; + _ballRightFl = false; + if (cellType == 31) + _ballPosition.x -= randVal; + } + if ((ballLeft <= cellRight) && (ballRight >= cellRight)) { + collisionFl = true; + _ballRightFl = true; + if (cellType == 31) + _ballPosition.x += randVal; + } + } + if (collisionFl) { + if (cellType == 31) { + _vm->_soundMan->playSample(2, 6); + } else { + _vm->_soundMan->playSample(1, 5); + _vm->_graphicsMan->fastDisplay2(_breakoutSpr, cellLeft, cellUp, 16); + switch (cellType) { + case 1: + _breakoutScore += 10; + break; + case 2: + _breakoutScore += 5; + break; + case 3: + _breakoutScore += 50; + if (_breakoutSpeed <= 1) + _breakoutSpeed = 2; + if (_breakoutBrickNbr <= 19) + _breakoutSpeed = 3; + break; + case 4: + _breakoutScore += 20; + break; + case 5: + _breakoutScore += 30; + if (_breakoutSpeed <= 1) + _breakoutSpeed = 2; + break; + case 6: + _breakoutScore += 40; + break; + } + displayScore(); + --_breakoutBrickNbr; + level[levelIdx + 5] = 0; + brickDestroyedFl = true; + } + } + } + + if (brickDestroyedFl) + cellLeft = -1; + levelIdx += 6; + } while (cellLeft != -1); +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/computer.h b/engines/hopkins/computer.h new file mode 100644 index 0000000000..cdd653f793 --- /dev/null +++ b/engines/hopkins/computer.h @@ -0,0 +1,106 @@ +/* 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. + * + */ + +#ifndef HOPKINS_COMPUTER_H +#define HOPKINS_COMPUTER_H + +#include "common/scummsys.h" +#include "common/str.h" +#include "common/rect.h" + +namespace Hopkins { + +class HopkinsEngine; + +struct MenuItem { + bool _actvFl; + int _lineSize; + char _line[90]; +}; + +struct ScoreItem { + Common::String _name; + Common::String _score; +}; + +enum ComputerEnum { COMPUTER_HOPKINS = 1, COMPUTER_SAMANTHA = 2, COMPUTER_PUBLIC = 3 }; + +class ComputerManager { +private: + HopkinsEngine *_vm; + MenuItem _menuText[50]; + char _inputBuf[200]; + ScoreItem _score[6]; + int _textColor; + Common::Point _textPosition; + Common::Point _ballPosition; + byte *_breakoutSpr; + int16 *_breakoutLevel; + int _breakoutBrickNbr; + int _breakoutScore; + int _breakoutLives; + int _breakoutSpeed; + bool _ballRightFl; + bool _ballUpFl; + int _breakoutLevelNbr; + int _padPositionX; + int _breakoutHiscore; + int _minBreakoutMoveSpeed; + int _maxBreakoutMoveSpeed; + int _lastBreakoutMoveSpeed; + + void loadMenu(); + void restoreFBIRoom(); + void setVideoMode(); + void setTextMode(); + void clearScreen(); + void setTextColor(int col); + void setTextPosition(int yp, int xp); + void outText(const Common::String &msg); + void outText2(const Common::String &msg); + void readText(int idx); + void loadHiscore(); + void newLevel(); + void setModeVGA256(); + void displayLives(); + void displayBricks(); + void displayGamesSubMenu(); + int displayHiscores(); + void displayHiscoreLine(const byte *objectData, int x, int y, int curChar); + void displayMessage(int xp, int yp, int textIdx); + void displayScore(); + void displayScoreChar(int charPos, int charDisp); + void getScoreName(); + void playBreakout(); + int moveBall(); + void saveScore(); + void checkBallCollisions(); + +public: + ComputerManager(HopkinsEngine *vm); + + void showComputer(ComputerEnum mode); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_COMPUTER_H */ diff --git a/engines/hopkins/debugger.cpp b/engines/hopkins/debugger.cpp new file mode 100644 index 0000000000..f111eb50d3 --- /dev/null +++ b/engines/hopkins/debugger.cpp @@ -0,0 +1,90 @@ +/* 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 "hopkins/debugger.h" + +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" + +namespace Hopkins { + +Debugger::Debugger(HopkinsEngine *vm) : GUI::Debugger() { + _vm = vm; + DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit)); + DCmd_Register("rects", WRAP_METHOD(Debugger, cmd_DirtyRects)); + DCmd_Register("teleport", WRAP_METHOD(Debugger, cmd_Teleport)); + DCmd_Register("show_room", WRAP_METHOD(Debugger, cmd_ShowCurrentRoom)); + DCmd_Register("zones", WRAP_METHOD(Debugger, cmd_Zones)); + DCmd_Register("lines", WRAP_METHOD(Debugger, cmd_Lines)); +} + +// Turns dirty rects on or off +bool Debugger::cmd_DirtyRects(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("%s: [on | off]\n", argv[0]); + return true; + } else { + _vm->_graphicsMan->_showDirtyRects = !strcmp(argv[1], "on"); + return false; + } +} + +// Change room number +bool Debugger::cmd_Teleport(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("%s: [Room number]\n", argv[0]); + return true; + } else { + _vm->_globals->_exitId = atoi(argv[1]); + return false; + } +} + +// Display room number +bool Debugger::cmd_ShowCurrentRoom(int argc, const char **argv) { + DebugPrintf("Current room: %d\n", _vm->_globals->_curRoomNum); + return true; +} + +bool Debugger::cmd_Zones(int argc, const char **argv) { +if (argc != 2) { + DebugPrintf("%s: [on | off]\n", argv[0]); + return true; + } else { + _vm->_graphicsMan->_showZones = !strcmp(argv[1], "on"); + return false; + } +} + +bool Debugger::cmd_Lines(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("%s: [on | off]\n", argv[0]); + return true; + } else { + _vm->_graphicsMan->_showLines = !strcmp(argv[1], "on"); + return false; + } +} + + +} // End of namespace Hopkins diff --git a/engines/hopkins/debugger.h b/engines/hopkins/debugger.h new file mode 100644 index 0000000000..746c54a675 --- /dev/null +++ b/engines/hopkins/debugger.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +#ifndef HOPKINS_DEBUGGER_H +#define HOPKINS_DEBUGGER_H + +#include "common/scummsys.h" +#include "gui/debugger.h" + +namespace Hopkins { + +class HopkinsEngine; + +class Debugger : public GUI::Debugger { +private: + HopkinsEngine *_vm; + +public: + Debugger(HopkinsEngine *vm); + virtual ~Debugger() {} + + bool cmd_DirtyRects(int argc, const char **argv); + bool cmd_Teleport(int argc, const char **argv); + bool cmd_ShowCurrentRoom(int argc, const char **argv); + bool cmd_Zones(int argc, const char **argv); + bool cmd_Lines(int argc, const char **argv); +}; + +} // End of namespace Hopkins + +#endif diff --git a/engines/hopkins/detection.cpp b/engines/hopkins/detection.cpp new file mode 100644 index 0000000000..9d16b0ab51 --- /dev/null +++ b/engines/hopkins/detection.cpp @@ -0,0 +1,192 @@ +/* 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 "hopkins/hopkins.h" + +#include "base/plugins.h" +#include "common/savefile.h" +#include "common/str-array.h" +#include "common/memstream.h" +#include "engines/advancedDetector.h" +#include "common/system.h" +#include "graphics/colormasks.h" +#include "graphics/surface.h" + +#define MAX_SAVES 99 + +namespace Hopkins { + +struct HopkinsGameDescription { + ADGameDescription desc; +}; + +uint32 HopkinsEngine::getFeatures() const { + return _gameDescription->desc.flags; +} + +Common::Language HopkinsEngine::getLanguage() const { + return _gameDescription->desc.language; +} + +Common::Platform HopkinsEngine::getPlatform() const { + return _gameDescription->desc.platform; +} + +bool HopkinsEngine::getIsDemo() const { + return _gameDescription->desc.flags & ADGF_DEMO; +} + +} // End of namespace Hopkins + +static const PlainGameDescriptor hopkinsGames[] = { + {"hopkins", "Hopkins FBI"}, + {0, 0} +}; + +#include "hopkins/detection_tables.h" + +const static char *directoryGlobs[] = { + "voice", + "link", + 0 +}; + +class HopkinsMetaEngine : public AdvancedMetaEngine { +public: + HopkinsMetaEngine() : AdvancedMetaEngine(Hopkins::gameDescriptions, sizeof(Hopkins::HopkinsGameDescription), hopkinsGames) { + _maxScanDepth = 3; + _directoryGlobs = directoryGlobs; + } + + virtual const char *getName() const { + return "Hopkins Engine"; + } + + virtual const char *getOriginalCopyright() const { + return "Hopkins FBI (c)1997-2003 MP Entertainment"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const; + virtual void removeSaveState(const char *target, int slot) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; +}; + +bool HopkinsMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail); +} + +bool Hopkins::HopkinsEngine::hasFeature(EngineFeature f) const { + return + (f == kSupportsRTL) || + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); +} + +bool HopkinsMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + const Hopkins::HopkinsGameDescription *gd = (const Hopkins::HopkinsGameDescription *)desc; + if (gd) { + *engine = new Hopkins::HopkinsEngine(syst, gd); + } + return gd != 0; +} + +SaveStateList HopkinsMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String saveDesc; + Common::String pattern = Common::String::format("%s.0??", target); + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order + + Hopkins::hopkinsSavegameHeader header; + + SaveStateList saveList; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { + const char *ext = strrchr(file->c_str(), '.'); + int slot = ext ? atoi(ext + 1) : -1; + + if (slot >= 0 && slot < MAX_SAVES) { + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); + + if (in) { + if (Hopkins::SaveLoadManager::readSavegameHeader(in, header)) { + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); + + header._thumbnail->free(); + delete header._thumbnail; + } + + delete in; + } + } + } + + return saveList; +} + +int HopkinsMetaEngine::getMaximumSaveSlot() const { + return MAX_SAVES; +} + +void HopkinsMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String filename = Common::String::format("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(filename); +} + +SaveStateDescriptor HopkinsMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String filename = Common::String::format("%s.%03d", target, slot); + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(filename); + + if (f) { + Hopkins::hopkinsSavegameHeader header; + Hopkins::SaveLoadManager::readSavegameHeader(f, header); + delete f; + + // Create the return descriptor + SaveStateDescriptor desc(slot, header._saveName); + desc.setThumbnail(header._thumbnail); + desc.setSaveDate(header._year, header._month, header._day); + desc.setSaveTime(header._hour, header._minute); + desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME); + + return desc; + } + + return SaveStateDescriptor(); +} + + +#if PLUGIN_ENABLED_DYNAMIC(HOPKINS) +REGISTER_PLUGIN_DYNAMIC(HOPKINS, PLUGIN_TYPE_ENGINE, HopkinsMetaEngine); +#else +REGISTER_PLUGIN_STATIC(HOPKINS, PLUGIN_TYPE_ENGINE, HopkinsMetaEngine); +#endif diff --git a/engines/hopkins/detection_tables.h b/engines/hopkins/detection_tables.h new file mode 100644 index 0000000000..c3ff563f6f --- /dev/null +++ b/engines/hopkins/detection_tables.h @@ -0,0 +1,197 @@ +/* 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. + * + */ + +namespace Hopkins { + +static const HopkinsGameDescription gameDescriptions[] = { + { + // Hopkins FBI Linux Demo UK 1.00 and 1.02 + { + "hopkins", + "Linux Demo", + { + {"RES_VAN.RES", 0, "29414c05be8f9fe794c61572a65def12", 16060544}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformLinux, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI OS/2, provided by Strangerke + { + "hopkins", + 0, + { + {"ENG_VOI.RES", 0, "fa5789d1d8c19d160bce44a33e742fdf", 66860711}, + {"CREAN.TXT", 0, "e13aa69d9e043f066776e1d0ef98fdf5", 1871}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformOS2, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI BeOS, provided by Strangerke & Eriktorbjorn + { + "hopkins", + 0, + { + {"ENG_VOI.RES", 0, "fa5789d1d8c19d160bce44a33e742fdf", 66860711}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformBeOS, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI Win95 Spanish + { + "hopkins", + 0, + { + {"RES_VES.RES", 0, "77ee08896466ae88cc1af3bf1a0bf78c", 32882302}, + AD_LISTEND + }, + Common::ES_ESP, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI Win95 UK, provided by Strangerke, alexbevi, greencis + { + "hopkins", + 0, + { + {"RES_VAN.RES", 0, "f1693ac0b0859c8ecd8cb30ff43cf55f", 38296346}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI Win95 RU, provided by greencis in bug #3613068 + { + "hopkins", + 0, + { + {"res_van.res", 0, "bf17c710e184a25a6c8e9d1d9503c38e", 32197685}, + AD_LISTEND + }, + Common::RU_RUS, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI Linux, provided by Strangerke + { + "hopkins", + 0, + { + {"RES_VFR.RES", 0, "0490d4d1aa71075ebf71cc79e5dc7894", 39817945}, + AD_LISTEND + }, + Common::FR_FRA, + Common::kPlatformLinux, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI Linux, provided by Strangerke + { + "hopkins", + 0, + { + {"RES_VAN.RES", 0, "29414c05be8f9fe794c61572a65def12", 38832455}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformLinux, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI Win95, French, provided by SylvainTV + { + "hopkins", + 0, + { + {"RES_VFR.RES", 0, "b8a3849063c9eeefe80e82cfce1ad3cd", 39269361}, + AD_LISTEND + }, + Common::FR_FRA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + + }, + { + // Hopkins FBI Win95 Demo, provided by Strangerke + // CHECKME: No voice! a second file is required though... Also, it has multi-language support + { + "hopkins", + "Win95 Demo", + { + {"Hopkins.exe", 0, "0c9ebfe371f4dcf84a49f333f04839a0", 376897}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + }, + { + // Hopkins FBI Win95 Polish Demo, provided by Strangerke + { + "hopkins", + "Win95 Demo", + { + {"RES_VAN.RES", 0, "8262cfba261c200af4451902689dffe0", 12233202}, + AD_LISTEND + }, + Common::PL_POL, + Common::kPlatformWindows, + ADGF_DEMO, + GUIO1(GUIO_NONE) + }, + }, + { AD_TABLE_END_MARKER } +}; + +} // End of namespace Hopkins diff --git a/engines/hopkins/dialogs.cpp b/engines/hopkins/dialogs.cpp new file mode 100644 index 0000000000..6cdfbf47d1 --- /dev/null +++ b/engines/hopkins/dialogs.cpp @@ -0,0 +1,783 @@ +/* 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 "hopkins/dialogs.h" + +#include "hopkins/events.h" +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" +#include "hopkins/sound.h" + +#include "common/scummsys.h" +#include "common/config-manager.h" +#include "common/events.h" +#include "common/file.h" +#include "common/util.h" + +namespace Hopkins { + +DialogsManager::DialogsManager(HopkinsEngine *vm) { + _vm = vm; + _inventFl = false; + _inventDisplayedFl = false; + _removeInventFl = false; + _inventX = _inventY = 0; + _oldInventX = 0; + _inventWidth = _inventHeight = 0; + _inventWin1 = NULL; + _inventBuf2 = NULL; + _inventoryIcons = NULL; +} + +DialogsManager::~DialogsManager() { + _vm->_globals->freeMemory(_inventWin1); + _vm->_globals->freeMemory(_inventBuf2); + _vm->_globals->freeMemory(_inventoryIcons); +} + +void DialogsManager::clearAll() { + _inventWin1 = NULL; + _inventBuf2 = NULL; +} + +void DialogsManager::loadIcons() { + _inventoryIcons = _vm->_fileIO->loadFile("ICONE.SPR"); +} + +void DialogsManager::drawInvent(Common::Point oldBorder, int oldBorderSpriteIndex, Common::Point newBorder, int newBorderSpriteIndex) { + if (!_inventDisplayedFl) + return; + + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, _inventWin1, _inventX, _inventY, _inventWidth, _inventHeight); + if (oldBorder.x && oldBorder.y) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _inventBuf2, oldBorder.x + 300, oldBorder.y + 300, oldBorderSpriteIndex + 1); + if (newBorder.x && newBorder.y) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _inventBuf2, newBorder.x + 300, newBorder.y + 300, newBorderSpriteIndex); + _vm->_graphicsMan->addDirtyRect(_inventX, _inventY, _inventX + _inventWidth, _inventY + _inventHeight); +} + +void DialogsManager::showOptionsDialog() { + _vm->_events->changeMouseCursor(0); + _vm->_events->refreshScreenAndEvents(); + Common::String filename; + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + filename = "OPTION.SPR"; + else { + if (_vm->_globals->_language == LANG_FR) + filename = "OPTIFR.SPR"; + else if (_vm->_globals->_language == LANG_EN) + filename = "OPTIAN.SPR"; + else if (_vm->_globals->_language == LANG_SP) + filename = "OPTIES.SPR"; + } + + _vm->_globals->_optionDialogSpr = _vm->_fileIO->loadFile(filename); + _vm->_globals->_optionDialogFl = true; + + int scrollOffset = _vm->_graphicsMan->_scrollOffset; + bool doneFlag = false; + do { + if (_vm->_events->getMouseButton()) { + Common::Point mousePos(_vm->_events->getMouseX(), _vm->_events->getMouseY()); + + if (!_vm->_soundMan->_musicOffFl) { + if (mousePos.x >= scrollOffset + 300 && mousePos.y > 113 && mousePos.x <= scrollOffset + 327 && mousePos.y <= 138) { + // Change the music volume + ++_vm->_soundMan->_musicVolume; + + if (_vm->_soundMan->_musicVolume <= 12) + _vm->_soundMan->playSoundFile("bruit2.wav"); + else + _vm->_soundMan->_musicVolume = 12; + _vm->_soundMan->setMODMusicVolume(_vm->_soundMan->_musicVolume); + + _vm->_soundMan->updateScummVMSoundSettings(); + } + + if (!_vm->_soundMan->_musicOffFl && mousePos.x >= scrollOffset + 331 && mousePos.y > 113 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 138) { + --_vm->_soundMan->_musicVolume; + if (_vm->_soundMan->_musicVolume >= 0) + _vm->_soundMan->playSoundFile("bruit2.wav"); + else + _vm->_soundMan->_musicVolume = 0; + + _vm->_soundMan->setMODMusicVolume(_vm->_soundMan->_musicVolume); + + _vm->_soundMan->updateScummVMSoundSettings(); + } + } + if (!_vm->_soundMan->_soundOffFl) { + // increase volume + if (mousePos.x >= scrollOffset + 300 && mousePos.y > 140 && mousePos.x <= scrollOffset + 327 && mousePos.y <= 165) { + ++_vm->_soundMan->_soundVolume; + if (_vm->_soundMan->_soundVolume <= 16) + _vm->_soundMan->playSoundFile("bruit2.wav"); + else + _vm->_soundMan->_soundVolume = 16; + _vm->_soundMan->setMODSampleVolume(); + + _vm->_soundMan->updateScummVMSoundSettings(); + } + + // Decrease volume + if (!_vm->_soundMan->_soundOffFl && mousePos.x >= scrollOffset + 331 && mousePos.y > 140 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 165) { + --_vm->_soundMan->_soundVolume; + if (_vm->_soundMan->_soundVolume >= 0) + _vm->_soundMan->playSoundFile("bruit2.wav"); + else + _vm->_soundMan->_soundVolume = 0; + _vm->_soundMan->setMODSampleVolume(); + + _vm->_soundMan->updateScummVMSoundSettings(); + } + } + + if (!_vm->_soundMan->_voiceOffFl) { + if (mousePos.x >= scrollOffset + 300 && mousePos.y > 167 && mousePos.x <= scrollOffset + 327 && mousePos.y <= 192) { + ++_vm->_soundMan->_voiceVolume; + + if (_vm->_soundMan->_voiceVolume <= 16) + _vm->_soundMan->playSoundFile("bruit2.wav"); + else + _vm->_soundMan->_voiceVolume = 16; + _vm->_soundMan->setMODVoiceVolume(); + + _vm->_soundMan->updateScummVMSoundSettings(); + } + + if (!_vm->_soundMan->_voiceOffFl && mousePos.x >= scrollOffset + 331 && mousePos.y > 167 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 192) { + --_vm->_soundMan->_voiceVolume; + if (_vm->_soundMan->_voiceVolume >= 0) + _vm->_soundMan->playSoundFile("bruit2.wav"); + else + _vm->_soundMan->_voiceVolume = 0; + _vm->_soundMan->setMODVoiceVolume(); + + _vm->_soundMan->updateScummVMSoundSettings(); + } + } + + if (mousePos.x >= scrollOffset + 431) { + if (mousePos.y > 194 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 219) + _vm->_soundMan->_textOffFl = !_vm->_soundMan->_textOffFl; + + if (mousePos.x >= scrollOffset + 431) { + if (mousePos.y > 167 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 192) { + _vm->_soundMan->_voiceOffFl = !_vm->_soundMan->_voiceOffFl; + + _vm->_soundMan->updateScummVMSoundSettings(); + } + if (mousePos.x >= scrollOffset + 431) { + if (mousePos.y > 113 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 138) { + if (_vm->_soundMan->_musicOffFl) { + _vm->_soundMan->_musicOffFl = false; + _vm->_soundMan->setMODMusicVolume(_vm->_soundMan->_musicVolume); + } else { + _vm->_soundMan->_musicOffFl = true; + _vm->_soundMan->setMODMusicVolume(0); + } + + _vm->_soundMan->updateScummVMSoundSettings(); + } + + if (mousePos.x >= scrollOffset + 431 && mousePos.y > 140 && mousePos.x <= scrollOffset + 489 && mousePos.y <= 165) { + _vm->_soundMan->_soundOffFl = !_vm->_soundMan->_soundOffFl; + + _vm->_soundMan->updateScummVMSoundSettings(); + } + } + } + } + + if (mousePos.x >= scrollOffset + 175 && mousePos.y > 285 && mousePos.x <= scrollOffset + 281 && mousePos.y <= 310) { + _vm->_globals->_exitId = 300; + doneFlag = true; + } + if (mousePos.x >= scrollOffset + 355 && mousePos.y > 285 && mousePos.x <= scrollOffset + 490 && mousePos.y <= 310) + doneFlag = true; + if (mousePos.x >= scrollOffset + 300 && mousePos.y > 194 && mousePos.x <= scrollOffset + 358 && mousePos.y <= 219) { + switch (_vm->_graphicsMan->_scrollSpeed) { + case 1: + _vm->_graphicsMan->_scrollSpeed = 2; + break; + case 2: + _vm->_graphicsMan->_scrollSpeed = 4; + break; + case 4: + _vm->_graphicsMan->_scrollSpeed = 8; + break; + case 8: + _vm->_graphicsMan->_scrollSpeed = 16; + break; + case 16: + _vm->_graphicsMan->_scrollSpeed = 32; + break; + case 32: + _vm->_graphicsMan->_scrollSpeed = 48; + break; + case 48: + _vm->_graphicsMan->_scrollSpeed = 64; + break; + case 64: + _vm->_graphicsMan->_scrollSpeed = 128; + break; + case 128: + _vm->_graphicsMan->_scrollSpeed = 160; + break; + case 160: + _vm->_graphicsMan->_scrollSpeed = 320; + break; + case 320: + _vm->_graphicsMan->_scrollSpeed = 1; + break; + } + } + + // Values are blocked, thus handling the zone is useless + //if (mousePos.x >= _vm->_graphicsManager->ofscroll + 348 && mousePos.y > 248 && mousePos.x <= _vm->_graphicsManager->ofscroll + 394 && mousePos.y <= 273) + // _vm->_globals->_speed = 2; + + if ( mousePos.x < scrollOffset + 165 || mousePos.x > scrollOffset + 496 + || mousePos.y < 107 || mousePos.y > 318) + doneFlag = true; + } + + if (_vm->_globals->_speed == 1) + _vm->_globals->_menuSpeed = 6; + else if (_vm->_globals->_speed == 2) + _vm->_globals->_menuSpeed = 5; + else if (_vm->_globals->_speed == 3) + _vm->_globals->_menuSpeed = 4; + + _vm->_globals->_menuTextOff = !_vm->_soundMan->_textOffFl ? 7 : 8; + _vm->_globals->_menuVoiceOff = !_vm->_soundMan->_voiceOffFl ? 7 : 8; + _vm->_globals->_menuSoundOff = !_vm->_soundMan->_soundOffFl ? 7 : 8; + _vm->_globals->_menuMusicOff = !_vm->_soundMan->_musicOffFl ? 7 : 8; + + _vm->_globals->_menuDisplayType = 9; + + switch (_vm->_graphicsMan->_scrollSpeed) { + case 1: + _vm->_globals->_menuScrollSpeed = 12; + break; + case 2: + _vm->_globals->_menuScrollSpeed = 13; + break; + case 4: + _vm->_globals->_menuScrollSpeed = 14; + break; + case 8: + _vm->_globals->_menuScrollSpeed = 15; + break; + case 16: + _vm->_globals->_menuScrollSpeed = 16; + break; + case 32: + _vm->_globals->_menuScrollSpeed = 17; + break; + case 48: + _vm->_globals->_menuScrollSpeed = 18; + break; + case 64: + _vm->_globals->_menuScrollSpeed = 19; + break; + case 128: + _vm->_globals->_menuScrollSpeed = 20; + break; + case 160: + _vm->_globals->_menuScrollSpeed = 21; + break; + case 320: + _vm->_globals->_menuScrollSpeed = 22; + break; + case 640: + _vm->_globals->_menuScrollSpeed = 23; + break; + } + + _vm->_events->refreshScreenAndEvents(); + } while (!doneFlag); + + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, scrollOffset + 164, + 107, 335, 215, _vm->_graphicsMan->_frontBuffer, scrollOffset + 164, 107); + _vm->_graphicsMan->addDirtyRect(scrollOffset + 164, 107, scrollOffset + 498, 320); + + _vm->_globals->_optionDialogSpr = _vm->_globals->freeMemory(_vm->_globals->_optionDialogSpr); + _vm->_globals->_optionDialogFl = false; +} + +void DialogsManager::showInventory() { + if (_removeInventFl || _inventDisplayedFl || _vm->_globals->_disableInventFl) + return; + + _vm->_graphicsMan->_scrollStatus = 1; + _vm->_objectsMan->_eraseVisibleCounter = 4; + _vm->_objectsMan->_visibleFl = false; + for (int i = 0; i <= 1; i++) { + inventAnim(); + _vm->_events->getMouseX(); + _vm->_events->getMouseY(); + _vm->_events->refreshScreenAndEvents(); + } + _inventWin1 = NULL; + + bool loopFl; + do { + loopFl = false; + _vm->_events->_curMouseButton = 0; + _vm->_events->_mouseButton = 0; + _vm->_globals->_disableInventFl = true; + _vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100); + + Common::String filename; + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + filename = "INVENT.SPR"; + else { + switch (_vm->_globals->_language) { + case LANG_EN: + filename = "INVENTAN.SPR"; + break; + case LANG_FR: + filename = "INVENTFR.SPR"; + break; + case LANG_SP: + filename = "INVENTES.SPR"; + break; + } + } + + Common::File f; + if (!f.open(filename)) + error("Error opening file - %s", filename.c_str()); + + size_t filesize = f.size(); + _inventWin1 = _vm->_globals->allocMemory(filesize); + _vm->_fileIO->readStream(f, _inventWin1, filesize); + f.close(); + + _inventBuf2 = _vm->_fileIO->loadFile("INVENT2.SPR"); + + _inventX = _vm->_graphicsMan->_scrollOffset + 152; + _inventY = 114; + _inventWidth = _vm->_objectsMan->getWidth(_inventWin1, 0); + _inventHeight = _vm->_objectsMan->getHeight(_inventWin1, 0); + + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _inventWin1, _inventX + 300, 414, 0, 0, 0, false); + int curPosY = 0; + int inventCount = 0; + for (int inventLine = 1; inventLine <= 5; inventLine++) { + int curPosX = 0; + for (int inventCol = 1; inventCol <= 6; inventCol++) { + ++inventCount; + int inventIdx = _vm->_globals->_inventory[inventCount]; + // The last two zones are not reserved for the inventory: Options and Save/Load + if (inventIdx && inventCount <= 29) { + byte *obj = _vm->_objectsMan->loadObjectFromFile(inventIdx, false); + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, obj, _inventX + curPosX + 6, + curPosY + 120, _vm->_objectsMan->getObjectWidth(), _vm->_objectsMan->getObjectHeight()); + _vm->_globals->freeMemory(obj); + } + curPosX += 54; + }; + curPosY += 38; + } + _vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, _inventWin1, _inventX, _inventY, _inventWidth, _inventHeight); + _vm->_events->_curMouseButton = 0; + int newInventoryItem = 0; + + // Main loop to select an inventory item + while (!_vm->shouldQuit()) { + // Turn on drawing the inventory dialog in the event manager + _inventDisplayedFl = true; + + int mousePosX = _vm->_events->getMouseX(); + int mousePosY = _vm->_events->getMouseY(); + int mouseButton = _vm->_events->getMouseButton(); + int oldInventoryItem = newInventoryItem; + newInventoryItem = _vm->_linesMan->checkInventoryHotspots(mousePosX, mousePosY); + if (newInventoryItem != oldInventoryItem) + _vm->_objectsMan->initBorder(newInventoryItem); + int cursorId = _vm->_events->_mouseCursorId; + if (cursorId != 1 && cursorId != 2 && cursorId != 3 && cursorId != 16) { + if (mouseButton == 2) { + _vm->_objectsMan->nextObjectIcon(newInventoryItem); + cursorId = _vm->_events->_mouseCursorId; + if (cursorId != 23) + _vm->_events->changeMouseCursor(cursorId); + } + } + cursorId = _vm->_events->_mouseCursorId; + if (mouseButton == 1) { + if (cursorId == 1 || cursorId == 2 || cursorId == 3 || cursorId == 16 || !cursorId) + break; + _vm->_objectsMan->takeInventoryObject(_vm->_globals->_inventory[newInventoryItem]); + if (_vm->_events->_mouseCursorId == 8) + break; + + _vm->_script->_tempObjectFl = true; + _vm->_globals->_saveData->_data[svLastObjectIndex] = _vm->_objectsMan->_curObjectIndex; + _vm->_globals->_saveData->_data[svLastInventoryItem] = _vm->_globals->_inventory[newInventoryItem]; + _vm->_globals->_saveData->_data[svLastInvMouseCursor] = _vm->_events->_mouseCursorId; + _vm->_objectsMan->loadObjectIniFile(); + _vm->_script->_tempObjectFl = false; + + if (_vm->_soundMan->_voiceOffFl) { + do + _vm->_events->refreshScreenAndEvents(); + while (!_vm->_globals->_exitId && _vm->_events->getMouseButton() != 1); + _vm->_fontMan->hideText(9); + } + if (_vm->_globals->_exitId) { + if (_vm->_globals->_exitId == 2) { + _vm->_globals->_exitId = 0; + break; + } + + _vm->_globals->_exitId = 0; + _inventBuf2 = _vm->_globals->freeMemory(_inventBuf2); + _inventWin1 = _vm->_globals->freeMemory(_inventWin1); + loopFl = true; + break; + } else + _inventDisplayedFl = true; + } + if (_removeInventFl) + break; + _vm->_events->refreshScreenAndEvents(); + if (_vm->_globals->_screenId >= 35 && _vm->_globals->_screenId <= 40) + _vm->_objectsMan->handleSpecialGames(); + } + } while (loopFl); + + _vm->_fontMan->hideText(9); + if (_inventDisplayedFl) { + _inventDisplayedFl = false; + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, _inventX, 114, _inventWidth, _inventHeight, _vm->_graphicsMan->_frontBuffer, _inventX, 114); + _vm->_graphicsMan->addDirtyRect(_inventX, 114, _inventX + _inventWidth, _inventWidth + 114); + _vm->_objectsMan->_refreshBobMode10Fl = true; + } + + _inventWin1 = _vm->_globals->freeMemory(_inventWin1); + _inventBuf2 = _vm->_globals->freeMemory(_inventBuf2); + + int cursorId = _vm->_events->_mouseCursorId; + if (cursorId == 1) + showOptionsDialog(); + else if (cursorId == 3) + showLoadGame(); + else if (cursorId == 2) + showSaveGame(); + + _vm->_events->_mouseCursorId = 4; + _vm->_events->changeMouseCursor(4); + _vm->_objectsMan->_oldBorderPos = Common::Point(0, 0); + _vm->_objectsMan->_borderPos = Common::Point(0, 0); + _vm->_globals->_disableInventFl = false; + _vm->_graphicsMan->_scrollStatus = 0; +} + +/** + * Inventory Animations + */ +void DialogsManager::inventAnim() { + if (_vm->_globals->_disableInventFl) + return; + + if (_vm->_objectsMan->_eraseVisibleCounter && !_vm->_objectsMan->_visibleFl) { + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, _oldInventX, 27, 48, 38, + _vm->_graphicsMan->_frontBuffer, _oldInventX, 27); + _vm->_graphicsMan->addDirtyRect(_oldInventX, 27, _oldInventX + 48, 65); + --_vm->_objectsMan->_eraseVisibleCounter; + } + + if (_vm->_objectsMan->_visibleFl) { + if (_oldInventX <= 1) + _oldInventX = 2; + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, _oldInventX, 27, 48, 38, + _vm->_graphicsMan->_frontBuffer, _oldInventX, 27); + + _vm->_graphicsMan->addDirtyRect(_oldInventX, 27, _oldInventX + 48, 65); + int newOffset = _vm->_graphicsMan->_scrollOffset + 2; + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _inventoryIcons, newOffset + 300, 327, 0); + _vm->_graphicsMan->addDirtyRect(newOffset, 27, newOffset + 45, 62); + _oldInventX = newOffset; + } + + if (_vm->_globals->_saveData->_data[svField357] == 1) { + if (_vm->_globals->_saveData->_data[svField353] == 1) + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 832, 325, 0, 0, 0, false); + if (_vm->_globals->_saveData->_data[svField355] == 1) + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 866, 325, 1, 0, 0, false); + _vm->_graphicsMan->addDirtyRect(532, 25, 560, 60); + _vm->_graphicsMan->addDirtyRect(566, 25, 594, 60); + } + if (_vm->_globals->_saveData->_data[svField356] == 1) { + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 832, 325, 0, 0, 0, false); + _vm->_graphicsMan->addDirtyRect(532, 25, 560, 60); + } + + if (_vm->_globals->_saveData->_data[svField354] == 1) { + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_headSprites, 832, 325, 0, 0, 0, false); + _vm->_graphicsMan->addDirtyRect(532, 25, 560, 60); + } +} + +/** + * Test dialog opening + */ +void DialogsManager::testDialogOpening() { + if (_vm->_globals->_cityMapEnabledFl) + _vm->_events->_gameKey = KEY_NONE; + + if ((_vm->_events->_gameKey == KEY_NONE) || _inventFl) + return; + + DIALOG_KEY key = _vm->_events->_gameKey; + _vm->_events->_gameKey = KEY_NONE; + _inventFl = true; + + switch (key) { + case KEY_INVENTORY: + showInventory(); + break; + case KEY_OPTIONS: + _vm->_graphicsMan->_scrollStatus = 1; + showOptionsDialog(); + _vm->_graphicsMan->_scrollStatus = 0; + break; + case KEY_LOAD: + _vm->_graphicsMan->_scrollStatus = 1; + showLoadGame(); + _vm->_graphicsMan->_scrollStatus = 0; + break; + case KEY_SAVE: + _vm->_graphicsMan->_scrollStatus = 1; + showSaveGame(); + _vm->_graphicsMan->_scrollStatus = 0; + break; + default: + break; + } + + _inventFl = false; + _vm->_events->_gameKey = KEY_NONE; +} + +/** + * Load Game dialog + */ +void DialogsManager::showLoadGame() { + _vm->_events->refreshScreenAndEvents(); + showSaveLoad(MODE_LOAD); + + int slotNumber; + do { + slotNumber = searchSavegames(); + _vm->_events->refreshScreenAndEvents(); + } while (!_vm->shouldQuit() && (!slotNumber || _vm->_events->getMouseButton() != 1)); + _vm->_objectsMan->_saveLoadFl = false; + int16 startPosX = _vm->_events->_startPos.x + 183; + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, startPosX, 60, 274, 353, _vm->_graphicsMan->_frontBuffer, startPosX, 60); + _vm->_graphicsMan->addDirtyRect(startPosX, 60, startPosX + 274, 413); + _vm->_objectsMan->_refreshBobMode10Fl = true; + _vm->_objectsMan->_saveLoadSprite = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite); + _vm->_objectsMan->_saveLoadSprite2 = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite2); + _vm->_objectsMan->_saveLoadX = 0; + _vm->_objectsMan->_saveLoadY = 0; + + if (slotNumber != 7) { + _vm->_saveLoad->loadGame(slotNumber); + } + + _vm->_objectsMan->changeObject(14); +} + +/** + * Save Game dialog + */ +void DialogsManager::showSaveGame() { + _vm->_events->refreshScreenAndEvents(); + + showSaveLoad(MODE_SAVE); + int slotNumber; + do { + slotNumber = searchSavegames(); + _vm->_events->refreshScreenAndEvents(); + } while (!_vm->shouldQuit() && (!slotNumber || _vm->_events->getMouseButton() != 1)); + + _vm->_objectsMan->_saveLoadFl = false; + int16 startPosX = _vm->_events->_startPos.x + 183; + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, startPosX, 60, 274, 353, _vm->_graphicsMan->_frontBuffer, startPosX, 60); + _vm->_graphicsMan->addDirtyRect(startPosX, 60, startPosX + 274, 413); + _vm->_objectsMan->_refreshBobMode10Fl = true; + _vm->_objectsMan->_saveLoadSprite = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite); + _vm->_objectsMan->_saveLoadSprite2 = _vm->_globals->freeMemory(_vm->_objectsMan->_saveLoadSprite2); + _vm->_objectsMan->_saveLoadX = 0; + _vm->_objectsMan->_saveLoadY = 0; + + if (slotNumber != 7) { + // Since the original GUI doesn't support save names, use a default name + Common::String saveName = Common::String::format("Save #%d", slotNumber); + + _vm->_events->refreshScreenAndEvents(); + // Save the game + _vm->_saveLoad->saveGame(slotNumber, saveName); + } +} + +/** + * Load/Save dialog + */ +void DialogsManager::showSaveLoad(SaveLoadMode mode) { + Common::String filename; + + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + filename = "SAVE.SPR"; + else { + switch (_vm->_globals->_language) { + case LANG_EN: + filename = "SAVEAN.SPR"; + break; + case LANG_FR: + filename = "SAVEFR.SPR"; + break; + case LANG_SP: + filename = "SAVEES.SPR"; + break; + } + } + + _vm->_objectsMan->_saveLoadSprite = _vm->_objectsMan->loadSprite(filename); + _vm->_objectsMan->_saveLoadSprite2 = _vm->_objectsMan->loadSprite("SAVE2.SPR"); + int16 startPosX = _vm->_events->_startPos.x; + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 483, 360, 0); + + if (_vm->_globals->_language == LANG_FR) { + if (mode == MODE_SAVE) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 525, 375, 1); + else if (mode == MODE_LOAD) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 515, 375, 2); + } else { + if (mode == MODE_SAVE) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 535, 372, 1); + else if (mode == MODE_LOAD) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, startPosX + 539, 372, 2); + } + + for (int slotNumber = 1; slotNumber <= 6; ++slotNumber) { + hopkinsSavegameHeader header; + if (_vm->_saveLoad->readSavegameHeader(slotNumber, header)) { + Graphics::Surface thumb8; + _vm->_saveLoad->convertThumb16To8(header._thumbnail, &thumb8); + + byte *thumb = (byte *)thumb8.pixels; + + int16 startPosX_ = _vm->_events->_startPos.x; + switch (slotNumber) { + case 1: + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 190, 112, 128, 87); + break; + case 2: + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 323, 112, 128, 87); + break; + case 3: + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 190, 203, 128, 87); + break; + case 4: + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 323, 203, 128, 87); + break; + case 5: + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 190, 294, 128, 87); + break; + case 6: + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, thumb, startPosX_ + 323, 294, 128, 87); + break; + } + + thumb8.free(); + header._thumbnail->free(); + delete header._thumbnail; + } + } + + _vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, _vm->_objectsMan->_saveLoadSprite, _vm->_events->_startPos.x + 183, 60, 274, 353); + _vm->_objectsMan->_saveLoadFl = true; + _vm->_objectsMan->_saveLoadX = 0; + _vm->_objectsMan->_saveLoadY = 0; +} + +/** + * Search savegames + */ +int DialogsManager::searchSavegames() { + int xp = _vm->_events->getMouseX(); + int yp = _vm->_events->getMouseY(); + + int16 startPosX = _vm->_graphicsMan->_scrollOffset = _vm->_events->_startPos.x; + + int slotNumber = 0; + if (yp >= 112 && yp <= 198) { + if (xp > startPosX + 189 && xp < startPosX + 318) { + slotNumber = 1; + _vm->_objectsMan->_saveLoadX = 189; + _vm->_objectsMan->_saveLoadY = 111; + } else if (xp > startPosX + 322 && xp < startPosX + 452) { + slotNumber = 2; + _vm->_objectsMan->_saveLoadX = 322; + _vm->_objectsMan->_saveLoadY = 111; + } + } else if (yp >= 203 && yp <= 289) { + if (xp > startPosX + 189 && xp < startPosX + 318) { + slotNumber = 3; + _vm->_objectsMan->_saveLoadX = 189; + _vm->_objectsMan->_saveLoadY = 202; + } else if (xp > startPosX + 322 && xp < startPosX + 452) { + slotNumber = 4; + _vm->_objectsMan->_saveLoadX = 322; + _vm->_objectsMan->_saveLoadY = 202; + } + } else if (yp >= 294 && yp <= 380) { + if (xp > startPosX + 189 && xp < startPosX + 318) { + slotNumber = 5; + _vm->_objectsMan->_saveLoadX = 189; + _vm->_objectsMan->_saveLoadY = 293; + } else if (xp > startPosX + 322 && xp < startPosX + 452) { + slotNumber = 6; + _vm->_objectsMan->_saveLoadX = 322; + _vm->_objectsMan->_saveLoadY = 293; + } + } else if (yp >= 388 && yp <= 404 && xp > startPosX + 273 && xp < startPosX + 355) { + slotNumber = 7; + _vm->_objectsMan->_saveLoadX = 0; + _vm->_objectsMan->_saveLoadY = 0; + } else { + slotNumber = 0; + _vm->_objectsMan->_saveLoadX = 0; + _vm->_objectsMan->_saveLoadY = 0; + } + + return slotNumber; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/dialogs.h b/engines/hopkins/dialogs.h new file mode 100644 index 0000000000..246b80cd3e --- /dev/null +++ b/engines/hopkins/dialogs.h @@ -0,0 +1,77 @@ +/* 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. + * + */ + +#ifndef HOPKINS_DIALOGS_H +#define HOPKINS_DIALOGS_H + +#include "common/scummsys.h" +#include "common/system.h" +#include "common/error.h" +#include "common/rect.h" + +namespace Hopkins { + +class HopkinsEngine; + +enum SaveLoadMode { MODE_SAVE = 1, MODE_LOAD = 2 }; + +/** + * Class for manging game dialogs + */ +class DialogsManager { +private: + byte *_inventWin1; + byte *_inventBuf2; + byte *_inventoryIcons; + bool _inventDisplayedFl; + bool _removeInventFl; + int _inventX, _inventY; + int _inventWidth, _inventHeight; + int _oldInventX; + + HopkinsEngine *_vm; + + void showSaveLoad(SaveLoadMode mode); + int searchSavegames(); +public: + bool _inventFl; + + DialogsManager(HopkinsEngine *vm); + ~DialogsManager(); + void inventAnim(); + void showInventory(); + void showLoadGame(); + void showSaveGame(); + void showOptionsDialog(); + void testDialogOpening(); + void clearAll(); + + void drawInvent(Common::Point oldBorder, int oldBorderSpriteIndex, Common::Point newBorder, int newBorderSpriteIndex); + void loadIcons(); + + void disableInvent() { _removeInventFl = true; } + void enableInvent() { _removeInventFl = false; } +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_MENU_H */ diff --git a/engines/hopkins/events.cpp b/engines/hopkins/events.cpp new file mode 100644 index 0000000000..51c66c4f92 --- /dev/null +++ b/engines/hopkins/events.cpp @@ -0,0 +1,542 @@ +/* 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 "hopkins/events.h" + +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/hopkins.h" +#include "hopkins/sound.h" + +#include "common/system.h" +#include "common/textconsole.h" +#include "graphics/cursorman.h" + +namespace Hopkins { + +EventsManager::EventsManager(HopkinsEngine *vm) { + _vm = vm; + _mouseFl = false; + _mouseLinuxFl = false; + _mouseSizeX = _mouseSizeY = 0; + _mouseOffset.x = _mouseOffset.y = 0; + _startPos.x = _startPos.y = 0; + _breakoutFl = false; + _mouseSpriteId = 0; + _curMouseButton = 0; + _mouseButton = 0; + _mouseCursor = NULL; + _gameCounter = 0; + _rateCounter = 0; + _escKeyFl = false; + _gameKey = KEY_NONE; + _mouseCursorId = 0; + _oldIconId = 0; + _objectBuf = NULL; + + Common::fill(&_keyState[0], &_keyState[256], false); + _priorCounterTime = _priorFrameTime = g_system->getMillis(); +} + +EventsManager::~EventsManager() { + _vm->_globals->freeMemory(_objectBuf); + _vm->_globals->freeMemory(_mouseCursor); +} + +void EventsManager::clearAll() { + _vm->_globals->freeMemory(_objectBuf); + _objectBuf = _vm->_globals->allocMemory(2500); +} + +void EventsManager::initMouseData() { + if (_vm->getPlatform() == Common::kPlatformLinux) + _mouseLinuxFl = true; + else + _mouseLinuxFl = false; + + if (_mouseLinuxFl) { + _mouseSizeX = 52; + _mouseSizeY = 32; + } else { + _mouseSizeX = 34; + _mouseSizeY = 20; + } + + switch (_vm->_globals->_language) { + case LANG_EN: + if (!_mouseLinuxFl) + _mouseCursor = _vm->_fileIO->loadFile("SOUAN.SPR"); + else + _mouseCursor = _vm->_fileIO->loadFile("LSOUAN.SPR"); + break; + case LANG_FR: + if (!_mouseLinuxFl) + _mouseCursor = _vm->_fileIO->loadFile("SOUFR.SPR"); + else + _mouseCursor = _vm->_fileIO->loadFile("LSOUFR.SPR"); + break; + case LANG_SP: + _mouseCursor = _vm->_fileIO->loadFile("SOUES.SPR"); + break; + } +} + +// Mouse On +void EventsManager::setMouseOn() { + _mouseFl = true; + + if (_mouseLinuxFl) { + _mouseSizeX = 52; + _mouseSizeY = 32; + } else { + _mouseSizeX = 34; + _mouseSizeY = 20; + } + + _mouseOffset.x = 0; + _mouseOffset.y = 0; + + if (!_breakoutFl) + setMouseXY(300, 200); + else + setMouseXY(150, 100); +} + +/** + * Set Mouse position + */ +void EventsManager::setMouseXY(Common::Point pos) { + g_system->warpMouse(pos.x, pos.y); +} + +/** + * Set Mouse position + */ +void EventsManager::setMouseXY(int xp, int yp) { + g_system->warpMouse(xp, yp); +} + +/** + * Get Mouse X + */ +int EventsManager::getMouseX() { + _mousePos.x = _startPos.x + g_system->getEventManager()->getMousePos().x; + _mousePos.y = g_system->getEventManager()->getMousePos().y; + + return _mousePos.x + _mouseOffset.x; +} + +/** + * Get Mouse Y + */ +int EventsManager::getMouseY() { + _mousePos.x = _startPos.x + g_system->getEventManager()->getMousePos().x; + _mousePos.y = g_system->getEventManager()->getMousePos().y; + + return _mousePos.y + _mouseOffset.y; +} + +/** + * Get Mouse Button + */ +int EventsManager::getMouseButton() { + refreshEvents(); + return _curMouseButton; +} + +/** + * Mouse Off + */ +void EventsManager::mouseOff() { + _mouseFl = false; + CursorMan.showMouse(false); +} + +/** + * Mouse On + */ +void EventsManager::mouseOn() { + setMouseOn(); + _mouseFl = true; + CursorMan.showMouse(true); +} + +/** + * Change Mouse Cursor + */ +void EventsManager::changeMouseCursor(int id) { + int cursorId = id; + + if (_mouseCursorId == 23) + return; + + if (id == 4 && _mouseCursorId == 4 && _vm->_globals->_freezeCharacterFl) + cursorId = 0; + if (cursorId == 25) + cursorId = 5; + + if (_oldIconId != cursorId || !cursorId) { + _oldIconId = cursorId; + _mouseSpriteId = cursorId; + + updateCursor(); + } +} + +/** + * Check Events + */ +void EventsManager::refreshEvents() { + _vm->_soundMan->checkSounds(); + + pollEvents(); +} + +void EventsManager::checkForNextFrameCounter() { + int32 delayAmount = 10 - (g_system->getMillis() - _priorCounterTime); + if (delayAmount > 0) + _vm->_system->delayMillis(delayAmount); + + // Check for whether to increment the game counter + uint32 milli = g_system->getMillis(); + while ((milli - _priorCounterTime) >= 10) { + _priorCounterTime += 10; + _rateCounter += 3; + } + + // Check for next game frame + if ((milli - _priorFrameTime) >= GAME_FRAME_TIME) { + ++_gameCounter; + _priorFrameTime = milli; + _vm->_graphicsMan->updateScreen(); + + // Signal the ScummVM debugger + _vm->_debug->onFrame(); + } +} + +void EventsManager::delay(int totalMilli) { + uint32 delayEnd = g_system->getMillis() + totalMilli; + + while (!_vm->shouldQuit() && g_system->getMillis() < delayEnd) { + g_system->delayMillis(10); + } +} + +void EventsManager::pollEvents() { + checkForNextFrameCounter(); + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) { + // Handle keypress + switch (event.type) { + case Common::EVENT_QUIT: + case Common::EVENT_RTL: + return; + + case Common::EVENT_KEYDOWN: + _keyState[(byte)toupper(event.kbd.ascii)] = true; + handleKey(event); + return; + case Common::EVENT_KEYUP: + _keyState[(byte)toupper(event.kbd.ascii)] = false; + return; + case Common::EVENT_LBUTTONDOWN: + _mouseButton = 1; + return; + case Common::EVENT_RBUTTONDOWN: + _mouseButton = 2; + return; + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + _mouseButton = 0; + return; + default: + break; + } + } + + for (char chr = 'A'; chr <= 'Z'; chr++) + _keyState[(byte)chr] = false; + + for (char chr = '0'; chr <= '9'; chr++) + _keyState[(byte)chr] = false; +} + +void EventsManager::handleKey(const Common::Event &event) { + _escKeyFl = (event.kbd.keycode == Common::KEYCODE_ESCAPE); + + if (event.kbd.keycode == Common::KEYCODE_i || event.kbd.keycode == Common::KEYCODE_TAB) + _gameKey = KEY_INVENTORY; + else if (event.kbd.keycode == Common::KEYCODE_F5) + _gameKey = KEY_SAVE; + else if (event.kbd.keycode == Common::KEYCODE_F7) + _gameKey = KEY_LOAD; + else if (event.kbd.keycode == Common::KEYCODE_F1 || event.kbd.keycode == Common::KEYCODE_o) + _gameKey = KEY_OPTIONS; + + // Check for debugger + if ((event.kbd.keycode == Common::KEYCODE_d) && (event.kbd.flags & Common::KBD_CTRL)) { + // Attach to the debugger + _vm->_debug->attach(); + _vm->_debug->onFrame(); + } + +} + +/** + * Waits for a keypress, ignoring mouse events + * @return Keypress, or -1 if game quit was requested + */ +int EventsManager::waitKeyPress() { + char foundChar = '\0'; + + while (!foundChar) { + if (_vm->shouldQuit()) + return -1; + + for (char ch = 'A'; ch <= 'Z'; ++ch) { + if (_keyState[(byte)ch]) { + foundChar = ch; + break; + } + } + + for (char ch = '0'; ch <= '9'; ++ch) { + if (_keyState[(byte)ch]) { + foundChar = ch; + break; + } + } + + if (_keyState[(byte)'.']) + foundChar = '.'; + else if (_keyState[8]) + // BACKSPACE + foundChar = 8; + else if (_keyState[13]) + // ENTER + foundChar = 13; + else if (_keyState[(byte)' ']) + foundChar = ' '; + + refreshScreenAndEvents(); + } + + // Wait for keypress release + while (_keyState[(byte)foundChar] && !_vm->shouldQuit()) { + refreshScreenAndEvents(); + g_system->delayMillis(10); + } + + // Return character + return foundChar; +} + +void EventsManager::refreshScreenAndEvents() { + int bottom = 0; + int right = 0; + int height = 0; + int width = 0; + int xp = 0; + int yp = 0; + + if (_mouseFl) { + int mouseWidth = 20; + if (!_mouseLinuxFl) + mouseWidth = 10; + int mouseHeight = 20; + if (!_mouseLinuxFl) + mouseHeight = 15; + xp = _mousePos.x - mouseWidth; + yp = _mousePos.y; + width = _mouseSizeX; + height = _mouseSizeY; + if (_mouseCursorId == 23) { + width = _vm->_objectsMan->getObjectWidth(); + height = _vm->_objectsMan->getObjectHeight(); + } else { + if (_breakoutFl) { + if (xp < _vm->_graphicsMan->_minX) + xp = _vm->_graphicsMan->_minX; + if (_mousePos.y < _vm->_graphicsMan->_minY) + yp = _vm->_graphicsMan->_minY; + if (_mouseSizeX + xp >= _vm->_graphicsMan->_maxX) + width = _mouseSizeX - (_mouseSizeX + xp - _vm->_graphicsMan->_maxX); + if (yp + _mouseSizeY >= _vm->_graphicsMan->_maxY) + height = _vm->_graphicsMan->_maxY - yp; + } else { + if (xp < _vm->_graphicsMan->_minX) + xp = _vm->_graphicsMan->_minX - mouseWidth; + mouseHeight = (int16)mouseHeight; + if (_mousePos.y < _vm->_graphicsMan->_minY - mouseHeight) + yp = _vm->_graphicsMan->_minY - mouseHeight; + if (_mouseSizeX + xp >= _vm->_graphicsMan->_maxX) + width = _mouseSizeX - (_mouseSizeX + xp - _vm->_graphicsMan->_maxX - mouseWidth); + if (yp + _mouseSizeY >= mouseHeight + _vm->_graphicsMan->_maxY) + height = _vm->_graphicsMan->_maxY - mouseHeight - yp; + } + right = xp + width; + bottom = yp + height; + } + } + + if (!_vm->_globals->_linuxEndDemoFl) + _vm->_objectsMan->displaySprite(); + if (!_mouseFl) { + updateCursor(); + } else if (_mouseCursorId == 23) { + if (yp < _vm->_graphicsMan->_maxY && xp < _vm->_graphicsMan->_maxX) { + if (width + xp > _vm->_graphicsMan->_maxX) + width = _vm->_graphicsMan->_maxX - xp; + if (yp + height > _vm->_graphicsMan->_maxY) + height = _vm->_graphicsMan->_maxY - yp; + if (width > 1 && height > 1) { + updateCursor(); + } + } + } else if (yp < _vm->_graphicsMan->_maxY && xp < _vm->_graphicsMan->_maxX && width > 1 && height > 1) { + updateCursor(); + _vm->_graphicsMan->addDirtyRect(xp, yp, right, bottom); + } + + _vm->_globals->_speed = 2; + bool externalLoopFl = false; + do { + while (!_vm->shouldQuit()) { + checkForNextFrameCounter(); + bool innerLoopFl = false; + + while (!_vm->shouldQuit() && (_breakoutFl || _vm->_globals->_eventMode != EVENTMODE_IGNORE)) { + checkForNextFrameCounter(); + + if (!_breakoutFl) { + innerLoopFl = true; + break; + } + if (_rateCounter > 1) { + externalLoopFl = true; + break; + } + } + if (innerLoopFl || _vm->_globals->_speed != 2) + break; + if (externalLoopFl ||_rateCounter > 9) { + externalLoopFl = true; + break; + } + } + if (externalLoopFl) + break; + } while (!_vm->shouldQuit() && _vm->_globals->_eventMode == EVENTMODE_CREDITS && _rateCounter <= 15); + _vm->_globals->_speed = 2; + _rateCounter = 0; + if (!_vm->_graphicsMan->_largeScreenFl || _vm->_graphicsMan->_scrollStatus == 1) { + _vm->_graphicsMan->displayDirtyRects(); + } else { + if (_vm->_graphicsMan->_scrollStatus != 2) { + if (getMouseX() > _vm->_graphicsMan->_scrollPosX + 620) + _vm->_graphicsMan->_scrollPosX += _vm->_graphicsMan->_scrollSpeed; + if (getMouseX() < _vm->_graphicsMan->_scrollPosX + 10) + _vm->_graphicsMan->_scrollPosX -= _vm->_graphicsMan->_scrollSpeed; + } + _vm->_graphicsMan->_scrollPosX = CLIP(_vm->_graphicsMan->_scrollPosX, 0, SCREEN_WIDTH); + if (_vm->_graphicsMan->_oldScrollPosX == _vm->_graphicsMan->_scrollPosX) { + _vm->_graphicsMan->displayDirtyRects(); + } else { + _vm->_fontMan->hideText(9); + _vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_frontBuffer, _vm->_graphicsMan->_scrollPosX, 20, SCREEN_WIDTH, 440, 0, 20); + _vm->_graphicsMan->resetRefreshRects(); + _vm->_graphicsMan->addRefreshRect(0, 20, SCREEN_WIDTH, SCREEN_HEIGHT - 20); + + _vm->_graphicsMan->resetDirtyRects(); + + _startPos.x = _vm->_graphicsMan->_scrollPosX; + _vm->_graphicsMan->_scrollOffset = _vm->_graphicsMan->_scrollPosX; + } + _vm->_graphicsMan->_oldScrollPosX = _vm->_graphicsMan->_scrollPosX; + _startPos.x = _vm->_graphicsMan->_scrollPosX; + _vm->_graphicsMan->_scrollOffset = _vm->_graphicsMan->_scrollPosX; + } + _curMouseButton = _mouseButton; + _mouseButton = 0; + _vm->_soundMan->checkSoundEnd(); + refreshEvents(); +} + +void EventsManager::updateCursor() { + // Backup the current sprite clipping bounds and reset them + Common::Rect clipBounds(_vm->_graphicsMan->_minX, _vm->_graphicsMan->_minY, + _vm->_graphicsMan->_maxX, _vm->_graphicsMan->_maxY); + _vm->_graphicsMan->_minX = _vm->_graphicsMan->_minY = 0; + _vm->_graphicsMan->_maxX = _vm->_objectsMan->getObjectWidth(); + _vm->_graphicsMan->_maxY = _vm->_objectsMan->getObjectHeight(); + int pitch = _vm->_graphicsMan->_lineNbr2; + _vm->_graphicsMan->_lineNbr2 = _vm->_objectsMan->getObjectWidth(); + + // Create the temporary cursor surface + byte *cursorSurface = new byte[_vm->_objectsMan->getObjectHeight() * _vm->_objectsMan->getObjectWidth()]; + Common::fill(cursorSurface, cursorSurface + _vm->_objectsMan->getObjectHeight() * _vm->_objectsMan->getObjectWidth(), 0); + + if (_mouseCursorId != 23) { + // Draw standard cursor + _vm->_graphicsMan->drawVesaSprite(cursorSurface, _mouseCursor, 300, 300, _mouseSpriteId); + } else { + // Draw the active inventory object + _vm->_graphicsMan->drawCompressedSprite(cursorSurface, _objectBuf, 300, 300, 0, 0, 0, false); + } + + // Reset the clipping bounds + _vm->_graphicsMan->_minX = clipBounds.left; + _vm->_graphicsMan->_minY = clipBounds.top; + _vm->_graphicsMan->_maxX = clipBounds.right; + _vm->_graphicsMan->_maxY = clipBounds.bottom; + _vm->_graphicsMan->_lineNbr2 = pitch; + + // Create a cursor palette + Graphics::PixelFormat pixelFormat = g_system->getScreenFormat(); + + byte *cursorPalette = new byte[3 * PALETTE_SIZE]; + uint16 *paletteColors = (uint16 *)_vm->_graphicsMan->_palettePixels; + + for (int i = 0; i < PALETTE_SIZE; i++) { + uint8 r, g, b; + pixelFormat.colorToRGB(READ_LE_UINT16(&paletteColors[i]), r, g, b); + cursorPalette[3 * i] = r; + cursorPalette[3 * i + 1] = g; + cursorPalette[3 * i + 2] = b; + } + + // Calculate the X offset within the pointer image to the actual cursor data + int xOffset = !_mouseLinuxFl ? 10 : 20; + + // Set the ScummVM cursor from the surface + CursorMan.replaceCursorPalette(cursorPalette, 0, PALETTE_SIZE - 1); + CursorMan.replaceCursor(cursorSurface, _vm->_objectsMan->getObjectWidth(), _vm->_objectsMan->getObjectHeight(), + xOffset, 0, 0, true); + + // Delete the cursor surface and palette + delete[] cursorPalette; + delete[] cursorSurface; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/events.h b/engines/hopkins/events.h new file mode 100644 index 0000000000..f4dedce1c5 --- /dev/null +++ b/engines/hopkins/events.h @@ -0,0 +1,94 @@ +/* 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. + * + */ + +#ifndef HOPKINS_EVENTS_H +#define HOPKINS_EVENTS_H + +#include "common/scummsys.h" +#include "common/events.h" +#include "common/str.h" + +namespace Hopkins { + +#define GAME_FRAME_RATE 50 +#define GAME_FRAME_TIME (1000 / GAME_FRAME_RATE) + +class HopkinsEngine; + +enum DIALOG_KEY { KEY_NONE = 0, KEY_INVENTORY = 1, KEY_OPTIONS = 2, KEY_SAVE = 3, KEY_LOAD = 4 }; + +class EventsManager { +private: + int _oldIconId; + uint32 _priorCounterTime; + uint32 _priorFrameTime; + bool _keyState[256]; + bool _mouseLinuxFl; + int _mouseSizeX, _mouseSizeY; + + HopkinsEngine *_vm; + + void pollEvents(); + void handleKey(const Common::Event &event); + void checkForNextFrameCounter(); + void updateCursor(); + +public: + DIALOG_KEY _gameKey; + uint32 _rateCounter; + uint32 _gameCounter; + bool _escKeyFl; + bool _mouseFl; + bool _breakoutFl; + Common::Point _startPos; + Common::Point _mousePos; + Common::Point _mouseOffset; + int _mouseSpriteId; + int _curMouseButton; + int _mouseButton; + int _mouseCursorId; + byte *_objectBuf; + byte *_mouseCursor; + + EventsManager(HopkinsEngine *vm); + ~EventsManager(); + void clearAll(); + void initMouseData(); + + void delay(int totalMilli); + void changeMouseCursor(int id); + void refreshEvents(); + int waitKeyPress(); + int getMouseX(); + int getMouseY(); + int getMouseButton(); + void setMouseXY(Common::Point pos); + void setMouseXY(int xp, int yp); + void mouseOn(); + void mouseOff(); + void setMouseOn(); + void refreshScreenAndEvents(); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_EVENTS_H */ diff --git a/engines/hopkins/files.cpp b/engines/hopkins/files.cpp new file mode 100644 index 0000000000..2390ebbdf8 --- /dev/null +++ b/engines/hopkins/files.cpp @@ -0,0 +1,271 @@ +/* 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 "hopkins/files.h" + +#include "hopkins/hopkins.h" +#include "hopkins/globals.h" + +#include "common/system.h" +#include "common/debug.h" +#include "common/file.h" +#include "common/str.h" +#include "common/savefile.h" + +namespace Hopkins { + +FileManager::FileManager(HopkinsEngine *vm) { + _vm = vm; + + _catalogPos = 0; + _catalogSize = 0; +} + +/** + * Load a file + */ +byte *FileManager::loadFile(const Common::String &file) { + Common::File f; + if (!f.open(file)) + error("Error opening %s", file.c_str()); + + // Allocate space for the file contents + size_t filesize = f.size(); + byte *data = _vm->_globals->allocMemory(filesize); + if (!data) + error("Error allocating space for file being loaded - %s", file.c_str()); + + readStream(f, data, filesize); + f.close(); + + return data; +} + +/** + * Read a given number of bytes from a Stream into a pre-allocated buffer + */ +int FileManager::readStream(Common::ReadStream &stream, void *buf, size_t nbytes) { + return stream.read(buf, nbytes); +} + +/** + * Initialize censorship based on blood.dat file + */ +void FileManager::initCensorship() { + _vm->_globals->_censorshipFl = false; + + // If file doesn't exist, fallback to uncensored + if (fileExists("BLOOD.DAT")) { + char *data = (char *)loadFile("BLOOD.DAT"); + + if ((data[6] == 'u' && data[7] == 'k') || (data[6] == 'U' && data[7] == 'K')) + _vm->_globals->_censorshipFl = true; + + _vm->_globals->freeMemory((byte *)data); + } +} + +/** + * Check if a file is present + */ +bool FileManager::fileExists(const Common::String &file) { + Common::File f; + + return f.exists(file); +} + +/** + * Search file in Cat file + */ +byte *FileManager::searchCat(const Common::String &file, CatMode mode, bool &fileFoundFl) { + byte *ptr = NULL; + fileFoundFl = true; + Common::File f; + + Common::String filename = file; + Common::String secondaryFilename = ""; + filename.toUppercase(); + + switch (mode) { + case RES_INI: + if (!f.exists("RES_INI.CAT")) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile("RES_INI.CAT"); + secondaryFilename = "RES_INI.RES"; + break; + + case RES_REP: + if (!f.exists("RES_REP.CAT")) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile("RES_REP.CAT"); + secondaryFilename = "RES_REP.RES"; + break; + + case RES_LIN: + if (!f.exists("RES_LIN.CAT")) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile("RES_LIN.CAT"); + secondaryFilename = "RES_LIN.RES"; + break; + + case RES_PER: + if (!f.exists("RES_PER.CAT")) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile("RES_PER.CAT"); + secondaryFilename = "RES_PER.RES"; + break; + + case RES_PIC: + if (!f.exists("PIC.CAT")) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile("PIC.CAT"); + break; + + case RES_SAN: + if (!f.exists("RES_SAN.CAT")) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile("RES_SAN.CAT"); + break; + + case RES_SLI: + if (!f.exists("RES_SLI.CAT")) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile("RES_SLI.CAT"); + break; + + case RES_VOI: { + Common::String tmpFilename; + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + tmpFilename = "ENG_VOI.CAT"; + // Win95 and Linux versions uses another set of names + else { + switch (_vm->_globals->_language) { + case LANG_EN: + tmpFilename = "RES_VAN.CAT"; + break; + case LANG_FR: + tmpFilename = "RES_VFR.CAT"; + break; + case LANG_SP: + tmpFilename = "RES_VES.CAT"; + break; + } + } + + if (!f.exists(tmpFilename)) { + fileFoundFl = false; + return NULL; + } + + ptr = loadFile(tmpFilename); + break; + } + + default: + break; + } + + // Scan for an entry in the catalogue + byte *result; + bool matchFlag = false; + int offsetVal = 0; + + while (!matchFlag) { + Common::String name = (const char *)ptr + offsetVal; + + if (name == filename) { + // Found entry for file, so get it's details from the catalogue entry + const byte *pData = ptr + offsetVal; + _catalogPos = READ_LE_UINT32(pData + 15); + _catalogSize = READ_LE_UINT32(pData + 19); + matchFlag = true; + } + + if (name == "FINIS") { + _vm->_globals->freeMemory(ptr); + fileFoundFl = false; + return NULL; + } + + offsetVal += 23; + } + + _vm->_globals->freeMemory(ptr); + + if (secondaryFilename != "") { + if (!f.open(secondaryFilename)) + error("CHARGE_FICHIER"); + + f.seek(_catalogPos); + + byte *catData = _vm->_globals->allocMemory(_catalogSize); + if (catData == NULL) + error("CHARGE_FICHIER"); + + readStream(f, catData, _catalogSize); + f.close(); + result = catData; + } else { + result = NULL; + } + + return result; +} + +/** + * Returns the size of a file. Throws an error if the file can't be found + */ +uint32 FileManager::fileSize(const Common::String &filename) { + Common::File f; + uint32 size; + + if (!f.open(filename)) + error("Could not find file %s", filename.c_str()); + + size = f.size(); + f.close(); + + return size; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/files.h b/engines/hopkins/files.h new file mode 100644 index 0000000000..5e5eaa755c --- /dev/null +++ b/engines/hopkins/files.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +#ifndef HOPKINS_FILES_H +#define HOPKINS_FILES_H + +#include "common/scummsys.h" +#include "common/hash-str.h" +#include "common/str.h" +#include "common/stream.h" + +namespace Hopkins { + +class HopkinsEngine; + +// RES_ANI = 4 has been removed because it's not used +enum CatMode { RES_INI = 1, RES_REP = 2, RES_LIN = 3, RES_PER = 5, + RES_PIC = 6, RES_SAN = 7, RES_SLI = 8, RES_VOI = 9 }; + +class FileManager { +public: + uint32 _catalogPos; + uint32 _catalogSize; + + HopkinsEngine *_vm; + + FileManager(HopkinsEngine *vm); + + bool fileExists(const Common::String &file); + byte *loadFile(const Common::String &file); + int readStream(Common::ReadStream &stream, void *buf, size_t nbytes); + void initCensorship(); + byte *searchCat(const Common::String &file, CatMode mode, bool &fileFoundFl); + uint32 fileSize(const Common::String &filename); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_GLOBALS_H */ diff --git a/engines/hopkins/font.cpp b/engines/hopkins/font.cpp new file mode 100644 index 0000000000..ac0eee2866 --- /dev/null +++ b/engines/hopkins/font.cpp @@ -0,0 +1,496 @@ +/* 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 "hopkins/font.h" + +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" +#include "hopkins/objects.h" + +#include "common/system.h" +#include "common/file.h" +#include "common/textconsole.h" + +namespace Hopkins { + +FontManager::FontManager(HopkinsEngine *vm) { + _vm = vm; + clearAll(); +} + +FontManager::~FontManager() { + _vm->_globals->freeMemory(_font); + _vm->_globals->freeMemory(_zoneText); +} + +void FontManager::loadZoneText() { + switch (_vm->_globals->_language) { + case LANG_EN: + _zoneText = _vm->_fileIO->loadFile("ZONEAN.TXT"); + break; + case LANG_FR: + _zoneText = _vm->_fileIO->loadFile("ZONE01.TXT"); + break; + case LANG_SP: + _zoneText = _vm->_fileIO->loadFile("ZONEES.TXT"); + break; + } +} + +void FontManager::clearAll() { + _font = NULL; + _fontFixedHeight = 0; + _fontFixedWidth = 0; + + for (int idx = 0; idx < 12; ++idx) { + Common::fill((byte *)&_text[idx], (byte *)&_text[idx] + sizeof(TxtItem), 0); + + _textList[idx]._enabledFl = false; + _textList[idx]._height = 0; + _textList[idx]._width = 0; + _textList[idx]._pos.x = 0; + _textList[idx]._pos.y = 0; + } + + for (int idx = 0; idx < 21; idx++) + _textSortArray[idx] = 0; + + _oldName = Common::String(""); + _indexName = Common::String(""); + + for (int idx = 0; idx < 4048; idx++) + _index[idx] = 0; + + _tempText = NULL; + _zoneText = NULL; + + _boxWidth = 240; +} + +void FontManager::initData() { + _font = _vm->_fileIO->loadFile("FONTE3.SPR"); + _fontFixedWidth = 12; + _fontFixedHeight = 21; + loadZoneText(); +} +/** + * Display Text + */ +void FontManager::showText(int idx) { + if ((idx - 5) > MAX_TEXT) + error("Attempted to display text > MAX_TEXT."); + + TxtItem &txt = _text[idx - 5]; + txt._textOnFl = true; + txt._textLoadedFl = false; + + txt._textBlock = _vm->_globals->freeMemory(txt._textBlock); +} + +/** + * Hide text + */ +void FontManager::hideText(int idx) { + if ((idx - 5) > MAX_TEXT) + error("Attempted to display text > MAX_TEXT."); + + TxtItem &txt = _text[idx - 5]; + txt._textOnFl = false; + txt._textLoadedFl = false; + txt._textBlock = _vm->_globals->freeMemory(txt._textBlock); +} + +/** + * Set Text Color + */ +void FontManager::setTextColor(int idx, byte colByte) { + _text[idx - 5]._color = colByte; +} + +/** + * Set Text Optimal Color + */ +void FontManager::setOptimalColor(int idx1, int idx2, int idx3, int idx4) { + setTextColor(idx1, 255); + setTextColor(idx2, 255); + setTextColor(idx3, 255); + setTextColor(idx4, 253); +} + +/** + * Init text structure + */ +void FontManager::initTextBuffers(int idx, int messageId, const Common::String &filename, int xp, int yp, int textType, int length, int color) { + assert(idx - 5 >= 0 && (idx - 5) <= MAX_TEXT); + + TxtItem &txt = _text[idx - 5]; + txt._textOnFl = false; + txt._filename = filename; + txt._pos.x = xp; + txt._pos.y = yp; + txt._messageId = messageId; + txt._textType = textType; + txt._length = length; + txt._color = color; +} + +// Box +void FontManager::box(int idx, int messageId, const Common::String &filename, int xp, int yp) { + int textPosX = xp; + if (idx < 0) + error("Bad number for text"); + _fontFixedWidth = 11; + + _boxWidth = 11 * _text[idx]._length; + if (_text[idx]._textLoadedFl) { + int textType = _text[idx]._textType; + if (textType != 6 && textType != 1 && textType != 3 && textType != 5) { + int yCurrent = yp + 5; + for (int lineNum = 0; lineNum < _text[idx]._lineCount; ++lineNum) { + displayText(xp + 5, yCurrent, _text[idx]._lines[lineNum], _text[idx]._color); + yCurrent += _fontFixedHeight + 1; + } + } else { + int height = _text[idx]._height; + int width = _text[idx]._width; + _vm->_graphicsMan->restoreSurfaceRect( + _vm->_graphicsMan->_frontBuffer, + _text[idx]._textBlock, + xp, + yp, + _text[idx]._width, + _text[idx]._height); + _vm->_graphicsMan->addDirtyRect(xp, yp, xp + width, yp + height); + } + } else { + int lineCount = 0; + for (int i = 0; i <= 19; i++) + _textSortArray[i] = 0; + + _text[idx]._textLoadedFl = true; + Common::String file = filename; + if (strncmp(file.c_str(), _oldName.c_str(), strlen(file.c_str())) != 0) { + // Starting to access a new file, so read in the index file for the file + _oldName = file; + _indexName = Common::String(file.c_str(), file.size() - 3); + _indexName += "IND"; + + Common::File f; + if (!f.open(_indexName)) + error("Error opening file - %s", _indexName.c_str()); + int filesize = f.size(); + for (int i = 0; i < (filesize / 4); ++i) + _index[i] = f.readUint32LE(); + f.close(); + } + int bufSize; + if (filename[0] != 'Z' || filename[1] != 'O') { + Common::File f; + if (!f.open(file)) + error("Error opening file - %s", _indexName.c_str()); + + bufSize = 2048; + f.seek(_index[messageId]); + + _tempText = _vm->_globals->allocMemory(2058); + if (_tempText == NULL) + error("Error allocating text"); + + Common::fill(&_tempText[0], &_tempText[2058], 0); + f.read(_tempText, 2048); + f.close(); + } else { + bufSize = 100; + _tempText = _vm->_globals->allocMemory(110); + Common::fill(&_tempText[0], &_tempText[110], 0); + memcpy(_tempText, _zoneText + _index[messageId], 96); + WRITE_LE_UINT16((uint16 *)_tempText + 48, READ_LE_INT16(_zoneText + _index[messageId] + 96)); + } + byte *curTempTextPtr = _tempText; + for (int i = 0; i < bufSize; i++) { + byte curChar = *curTempTextPtr; + if ((byte)(*curTempTextPtr + 46) > 27) { + if ((byte)(curChar + 80) > 27) { + if ((byte)(curChar - 65) <= 25 || (byte)(curChar - 97) <= 25) + curChar = 32; + } else { + curChar -= 79; + } + } else { + curChar += 111; + } + *curTempTextPtr = curChar; + curTempTextPtr++; + }; + + int textLength; + for (textLength = 0; textLength < bufSize; textLength++) { + byte curChar = _tempText[textLength]; + if (curChar == '\r' || curChar == '\n') { + _tempText[textLength] = 0; + if (!_text[idx]._length) + break; + } + } + + if (bufSize && bufSize > textLength) { + _text[idx]._length = textLength; + _boxWidth = 0; + + for (int curStrIdx = 0; curStrIdx < textLength + 1; curStrIdx++) { + byte curChar = _tempText[curStrIdx]; + if (curChar <= 31) + curChar = ' '; + _boxWidth += _vm->_objectsMan->getWidth(_font, curChar - 32); + } + + _boxWidth += 2; + _text[idx]._pos.x = 320 - abs(_boxWidth / 2); + textPosX = _vm->_events->_startPos.x + _text[idx]._pos.x; + lineCount = 1; + _text[idx]._lines[0] = Common::String((const char *)_tempText, textLength); + } else { + if (!_boxWidth) + _boxWidth = 240; + int tempTextIdx = 0; + int lineSize; + byte curChar; + do { + int curLineSize = 0; + int ptrb = _boxWidth - 4; + for (;;) { + lineSize = curLineSize; + do + curChar = _tempText[tempTextIdx + curLineSize++]; + while (curChar != ' ' && curChar != '%'); + if (curLineSize >= ptrb / _fontFixedWidth) { + if (curChar == '%') + curChar = ' '; + break; + } + if (curChar == '%') { + lineSize = curLineSize; + break; + } + } + + // WORKAROUND: Perhaps due to the usage of ScummVM strings here, recalculate what the + // actual length of the line to be copied will be. Otherwise, you can see artifacts, + // such as a single character beyond the end of string NULL. + int actualSize = 0; + while (actualSize < lineSize && _tempText[tempTextIdx + actualSize]) + ++actualSize; + + _text[idx]._lines[lineCount] = Common::String((const char *)_tempText + tempTextIdx, actualSize); + _textSortArray[lineCount++] = lineSize; + + tempTextIdx += lineSize; + } while (curChar != '%'); + + for (int i = 0; i <= 19; i++) { + if (_textSortArray[i] <= 0) { + _textSortArray[i] = 0; + } else { + int ptrc = 0; + for (int curIdx = 0; curIdx < _textSortArray[i] - 1; curIdx++) { + Common::String &line = _text[idx]._lines[i]; + byte curChar2 = (curIdx >= (int)line.size()) ? '\0' : line.c_str()[curIdx]; + if (curChar2 <= 31) + curChar2 = ' '; + ptrc += _vm->_objectsMan->getWidth(_font, (byte)curChar2 - 32); + } + _textSortArray[i] = ptrc; + } + } + for (int i = 0; i <= 19; i++) { + for (int j = i + 1; j != i; j = (j + 1) % 20) { + if (_textSortArray[i] < _textSortArray[j]) + _textSortArray[i] = 0; + } + }; + + for (int i = 0; i <= 19; i++) { + if (_textSortArray[i]) + _boxWidth = _textSortArray[i]; + } + + if ((_text[idx]._textType < 2) || (_text[idx]._textType > 3)) { + int i; + for (i = xp - _vm->_events->_startPos.x; _boxWidth + i > 638 && i > -2 && _text[idx]._textType; i -= 2) + ; + _text[idx]._pos.x = i; + textPosX = _vm->_events->_startPos.x + i; + } else { + _text[idx]._pos.x = textPosX; + } + } + int posX = textPosX; + int posY = yp; + int saveWidth = _boxWidth + 10; + int saveHeight = (_fontFixedHeight + 1) * lineCount + 12; + if (_text[idx]._textType == 6) { + _text[idx]._pos.x = 315 - abs(saveWidth / 2); + textPosX = posX = _vm->_events->_startPos.x + _text[idx]._pos.x; + _text[idx]._pos.y = posY = 50; + } + int textType = _text[idx]._textType; + if (textType == 1 || textType == 3 || textType == 5 || textType == 6) { + int size = saveHeight * saveWidth; + byte *ptrd = _vm->_globals->allocMemory(size); + if (ptrd == NULL) + error("Cutting a block for text box (%d)", size); + + _vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, ptrd, posX, posY, saveWidth, saveHeight); + _vm->_graphicsMan->fillSurface(ptrd, _vm->_graphicsMan->_colorTable, size); + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, ptrd, posX, posY, saveWidth, saveHeight); + _vm->_globals->freeMemory(ptrd); + + _vm->_graphicsMan->drawHorizontalLine(_vm->_graphicsMan->_frontBuffer, posX, posY, saveWidth, (byte)-2); + _vm->_graphicsMan->drawHorizontalLine(_vm->_graphicsMan->_frontBuffer, posX, saveHeight + posY, saveWidth, (byte)-2); + _vm->_graphicsMan->drawVerticalLine(_vm->_graphicsMan->_frontBuffer, posX, posY, saveHeight, (byte)-2); + _vm->_graphicsMan->drawVerticalLine(_vm->_graphicsMan->_frontBuffer, saveWidth + posX, posY, saveHeight, (byte)-2); + } + _text[idx]._lineCount = lineCount; + int textPosY = posY + 5; + + for (int lineNum = 0; lineNum < lineCount; ++lineNum) { + displayText(textPosX + 5, textPosY, _text[idx]._lines[lineNum], _text[idx]._color); + textPosY += _fontFixedHeight + 1; + } + + int blockWidth = saveWidth + 1; + int blockHeight = saveHeight + 1; + + _text[idx]._width = blockWidth; + _text[idx]._height = blockHeight; + textType = _text[idx]._textType; + if (textType == 6 || textType == 1 || textType == 3 || textType == 5) { + _text[idx]._textBlock = _vm->_globals->freeMemory(_text[idx]._textBlock); + int blockSize = blockHeight * blockWidth; + byte *ptre = _vm->_globals->allocMemory(blockSize + 20); + if (ptre == NULL) + error("Cutting a block for text box (%d)", blockSize); + + _text[idx]._textBlock = ptre; + _text[idx]._width = blockWidth; + _text[idx]._height = blockHeight; + _vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_frontBuffer, _text[idx]._textBlock, posX, posY, _text[idx]._width, blockHeight); + } + _tempText = _vm->_globals->freeMemory(_tempText); + } +} + +/** + * Directly display text (using a VESA segment) + */ +void FontManager::displayTextVesa(int xp, int yp, const Common::String &message, int col) { + int charIndex; + int currentX = xp; + + const char *srcP = message.c_str(); + for (;;) { + byte currChar = *srcP++; + if (!currChar) + break; + if (currChar >= 32) { + charIndex = currChar - 32; + _vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, currentX, yp, currChar - 32, col); + currentX += _vm->_objectsMan->getWidth(_font, charIndex); + } + } + + _vm->_graphicsMan->addDirtyRect(xp, yp, currentX, yp + 12); +} + +/** + * Directly display text + */ +void FontManager::displayText(int xp, int yp, const Common::String &message, int col) { + for (uint idx = 0; idx < message.size(); ++idx) { + byte currentChar = (byte)message[idx]; + + if (currentChar > 31) { + int characterIndex = currentChar - 32; + _vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, xp, yp, characterIndex, col); + _vm->_graphicsMan->addDirtyRect(xp, yp, xp + _vm->_objectsMan->getWidth(_font, characterIndex) + 1, yp + _vm->_objectsMan->getHeight(_font, characterIndex) + 1); + xp += _vm->_objectsMan->getWidth(_font, characterIndex); + } + } +} + +/** + * Compute character width and render text using variable width fonts + */ +void FontManager::renderTextDisplay(int xp, int yp, const Common::String &msg, int col) { + const char *srcP = msg.c_str(); + int charEndPosX = xp; + int fontCol = col; + byte curChar = *srcP++; + while (curChar) { + if (curChar == '&') { + fontCol = 2; + curChar = *srcP++; + } + if (curChar == '$') { + fontCol = 4; + curChar = *srcP++; + } + if (!curChar) + break; + if (curChar >= 32) { + byte printChar = curChar - 32; + _vm->_graphicsMan->displayFont(_vm->_graphicsMan->_frontBuffer, _font, charEndPosX, yp, printChar, fontCol); + + // UGLY HACK: For some obscure reason, the BeOS and OS/2 versions use another font file, which doesn't have variable width. + // All the fonts have a length of 9, which results in completely broken text in the computer. + // This horrible workaround fixes the English versions of the game. So far, no other languages are known for those platforms. + // Just in case, all the accentuated characters are handled properly, which *should* be OK for the other languages too. + int charWidth; + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) { + if ((curChar >= 'A' && curChar <= 'Z') || (curChar >= 'a' && curChar <= 'z' && curChar != 'm' && curChar != 'w') || (curChar >= '0' && curChar <= '9') || curChar == '*' || (curChar >= 128 && curChar <= 168)) + charWidth = _vm->_objectsMan->getWidth(_font, printChar) - 1; + else if (curChar == 'm' || curChar == 'w') + charWidth = _vm->_objectsMan->getWidth(_font, printChar); + else + charWidth = 6; + } else + charWidth = _vm->_objectsMan->getWidth(_font, printChar); + + int charStartPosX = charEndPosX; + charEndPosX += charWidth; + _vm->_graphicsMan->addDirtyRect(charStartPosX, yp, charEndPosX, yp + 12); + if (_vm->_events->_escKeyFl) { + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + _vm->_events->refreshScreenAndEvents(); + } else { + _vm->_globals->_eventMode = EVENTMODE_ALT; + _vm->_events->refreshScreenAndEvents(); + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + } + } + curChar = *srcP++; + } +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/font.h b/engines/hopkins/font.h new file mode 100644 index 0000000000..93e807ea4b --- /dev/null +++ b/engines/hopkins/font.h @@ -0,0 +1,98 @@ +/* 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. + * + */ + +#ifndef HOPKINS_FONT_H +#define HOPKINS_FONT_H + +#include "common/scummsys.h" +#include "common/events.h" +#include "common/str.h" + +namespace Hopkins { + +#define MAX_TEXT 11 + +class HopkinsEngine; + +struct TxtItem { + bool _textOnFl; + Common::String _filename; + Common::Point _pos; + int _messageId; + int _lineCount; + Common::String _lines[10]; + int _textType; + int _length; + byte *_textBlock; + int16 _width; + int16 _height; + bool _textLoadedFl; + int _color; +}; + +struct TxtItemList { + bool _enabledFl; + Common::Point _pos; + int16 _width; + int16 _height; +}; + +class FontManager { +private: + HopkinsEngine *_vm; + + void setTextColor(int idx, byte colByte); + + int _textSortArray[21]; + Common::String _oldName; + Common::String _indexName; + int _index[4048]; + byte *_tempText; + byte *_zoneText; + int _boxWidth; + + void loadZoneText(); +public: + byte *_font; + int _fontFixedWidth; + int _fontFixedHeight; + TxtItem _text[12]; + TxtItemList _textList[12]; + + FontManager(HopkinsEngine *vm); + ~FontManager(); + void clearAll(); + void initData(); + + void showText(int idx); + void hideText(int idx); + void initTextBuffers(int idx, int messageId, const Common::String &filename, int xp, int yp, int textType, int length, int color); + void displayText(int xp, int yp, const Common::String &message, int col); + void displayTextVesa(int xp, int yp, const Common::String &message, int col); + void renderTextDisplay(int xp, int yp, const Common::String &msg, int col); + void setOptimalColor(int idx1, int idx2, int idx3, int idx4); + void box(int idx, int messageId, const Common::String &filename, int xp, int yp); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_FONT_H */ diff --git a/engines/hopkins/globals.cpp b/engines/hopkins/globals.cpp new file mode 100644 index 0000000000..28f22ed99e --- /dev/null +++ b/engines/hopkins/globals.cpp @@ -0,0 +1,215 @@ +/* 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 "hopkins/globals.h" + +#include "hopkins/files.h" +#include "hopkins/font.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" + +#include "common/textconsole.h" +#include "common/file.h" + +namespace Hopkins { + +// Default data for the Hopkins array + +const int HOPKINS_PERSO_0[] = { + 0, -2, 0, -3, 0, -6, 0, -1, 0, -3, 0, -3, 0, -5, 0, -3, 0, -6, 0, -3, 0, -3, 0, -3, + 9, -4, 8, -4, 6, -2, 9, -2, 9, -3, 9, -3, 9, -4, 9, -2, 9, -2, 8, -2, 9, -3, 9, -2, + 13, 0, 13, 0, 13, 0, 13, 0, 14, 0, 13, 0, 13, 0, 12, 0, 12, 0, 14, 0, 13, 0, 14, 0, + 10, 3, 9, 3, 10, 4, 8, 2, 7, 1, 10, 2, 9, 2, 7, 4, 7, 3, 8, 0, 9, 1, 9, 1, 0, 4, 0, + 4, 0, 6, 0, 3, 0, 4, 0, 3, 0, 4, 0, 4, 0, 6, 0, 3, 0, 3, 0, 3 +}; + +const int HOPKINS_PERSO_1[] = { + 0, -2, 0, -2, 0, -5, 0, -1, 0, -2, 0, -2, 0, -4, 0, -2, 0, -5, 0, -2, 0, -2, 0, -2, + 11, 0, 10, 0, 11, 0, 11, 0, 11, 0, 11, 0, 12, 0, 11, 0, 9, 0, 10, 0, 11, 0, 11, 0, + 11, 0, 10, 0, 11, 0, 11, 0, 11, 0, 11, 0, 12, 0, 11, 0, 9, 0, 10, 0, 11, 0, 11, 0, + 11, 0, 10, 0, 11, 0, 11, 0, 11, 0, 11, 0, 12, 0, 11, 0, 9, 0, 10, 0, 11, 0, 11, 0, + 0, 3, 0, 3, 0, 5, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 5, 0, 3, 0, 3, 0, 3 +}; + +const int HOPKINS_PERSO_2[] = { + 0, -2, 0, 0, 0, -3, 0, -2, 0, -2, 0, -1, 0, -2, 0, -1, 0, -3, 0, -2, 0, -2, 0, -2, + 8, 0, 9, 0, 5, 0, 9, 0, 7, 0, 7, 0, 7, 0, 7, 0, 6, 0, 7, 0, 6, 0, 9, 0, 8, 0, 9, 0, + 5, 0, 9, 0, 7, 0, 7, 0, 7, 0, 7, 0, 6, 0, 7, 0, 6, 0, 9, 0, 8, 0, 9, 0, 5, 0, 9, 0, + 7, 0, 7, 0, 7, 0, 7, 0, 6, 0, 7, 0, 6, 0, 9, 0, 0, 2, 0, 0, 0, 2, 0, 1, 0, 2, 0, 2, + 0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0, 2 +}; + +Globals::Globals(HopkinsEngine *vm) { + _vm = vm; + + // Initialize array properties + for (int i = 0; i < 500; ++i) + _spriteSize[i] = 0; + for (int i = 0; i < 70; ++i) + Common::fill((byte *)&_hopkinsItem[i], (byte *)&_hopkinsItem[i] + sizeof(HopkinsItem), 0); + + for (int i = 0; i < 36; ++i) + _inventory[i] = 0; + + // Initialize fields + _language = LANG_EN; + + _linuxEndDemoFl = false; + _speed = 1; + _eventMode = EVENTMODE_DEFAULT; + _exitId = 0; + _characterSpriteBuf = 0; + _screenId = 0; + _prevScreenId = 0; + _characterMaxPosY = 0; + _menuScrollSpeed = 0; + _menuSpeed = 0; + _menuSoundOff = 0; + _menuVoiceOff = 0; + _menuMusicOff = 0; + _menuTextOff = 0; + _menuDisplayType = 0; + _checkDistanceFl = false; + _characterType = CHARACTER_HOPKINS; + _actionMoveTo = false; + _actionDirection = DIR_NONE; + + _creditsStartX = -1; + _creditsEndX = -1; + _creditsStartY = -1; + _creditsEndY = -1; + _creditsPosY = 0; + _creditsLineNumb = 0; + memset(_creditsItem, 0, 12000); + _creditsStep = 0; + + _oceanDirection = DIR_NONE; + + // Initialize pointers + _levelSpriteBuf = NULL; + _saveData = NULL; + _answerBuffer = NULL; + _characterSpriteBuf = NULL; + _optionDialogSpr = NULL; + + // Reset flags + _censorshipFl = false; + _disableInventFl = false; + _freezeCharacterFl = false; + _optionDialogFl = false; + _introSpeechOffFl = false; + _cityMapEnabledFl = false; + + _baseMapColor = 50; + _curRoomNum = 0; +} + +Globals::~Globals() { + freeMemory(_levelSpriteBuf); + freeMemory((byte *)_saveData); + freeMemory(_answerBuffer); + freeMemory(_characterSpriteBuf); + free(NULL); +} + +void Globals::setConfig() { + // CHECKME: Should be in Globals() but it doesn't work + // The Polish version is a translation of the English version. The filenames are the same. + // The Russian version looks like a translation of the English version, based on the filenames. + switch (_vm->getLanguage()) { + case Common::EN_ANY: + case Common::PL_POL: + case Common::RU_RUS: + _language = LANG_EN; + break; + case Common::FR_FRA: + _language = LANG_FR; + break; + case Common::ES_ESP: + _language = LANG_SP; + break; + default: + warning("Unknown language in internal language mapping"); + break; + } + // End of CHECKME + + switch (_language) { + case LANG_EN: + _zoneFilename = "ZONEAN.TXT"; + _textFilename = "TEXTEAN.TXT"; + break; + case LANG_FR: + _zoneFilename = "ZONE01.TXT"; + _textFilename = "TEXTE01.TXT"; + break; + case LANG_SP: + _zoneFilename = "ZONEES.TXT"; + _textFilename = "TEXTEES.TXT"; + break; + } +} + +void Globals::clearAll() { + _vm->_fontMan->clearAll(); + _vm->_dialog->clearAll(); + _answerBuffer = NULL; + _levelSpriteBuf = NULL; + _saveData = NULL; + _vm->_objectsMan->_curObjectIndex = 0; + + _vm->_linesMan->clearAll(); + _vm->_objectsMan->clearAll(); + + _saveData = (Savegame *)malloc(sizeof(Savegame)); + memset(_saveData, 0, sizeof(Savegame)); + + _vm->_events->clearAll(); +} + +void Globals::loadCharacterData() { + const int *srcList[] = { HOPKINS_PERSO_0, HOPKINS_PERSO_1, HOPKINS_PERSO_2 }; + const int *srcP = srcList[_characterType]; + + for (int idx = 0; idx < 240 / 4; ++idx) { + _hopkinsItem[idx]._speedX = *srcP++; + _hopkinsItem[idx]._speedY = *srcP++; + } + + _vm->_objectsMan->resetOldFrameIndex(); + _vm->_objectsMan->resetOldDirection(); +} + +byte *Globals::allocMemory(int count) { + byte *result = (byte *)malloc(count); + if (!result) + result = NULL; + return result; +} + +byte *Globals::freeMemory(byte *p) { + if (p) + free(p); + return NULL; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/globals.h b/engines/hopkins/globals.h new file mode 100644 index 0000000000..94512c3d26 --- /dev/null +++ b/engines/hopkins/globals.h @@ -0,0 +1,225 @@ +/* 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. + * + */ + +#ifndef HOPKINS_GLOBALS_H +#define HOPKINS_GLOBALS_H + +#include "common/scummsys.h" +#include "common/str.h" +#include "common/events.h" + +namespace Hopkins { + +struct HopkinsItem { + int _speedX; + int _speedY; +}; + +struct CharacterLocation { + Common::Point _pos; + int _startSpriteIndex; + int _location; + int _zoomFactor; +}; + +enum SauvegardeOffset { + svLastMouseCursor = 1 + , svLastZoneNum = 2 + , svLastObjectIndex = 3 + , svDialogField4 = 4 + , svLastScreenId = 5 + , svLastPrevScreenId = 6 + , svLastInventoryItem = 8 + , svLastInvMouseCursor = 9 + , svLastSavegameSlot = 10 + , svFreedHostageFl = 80 + , svField94 = 94 + , svField95 = 95 + , svForestAvailableFl = 113 + , svHutBurningFl = 117 + , svHopkinsCloneFl = 121 + , svAlternateSpriteFl = 122 + , svHeavenGuardGoneFl = 123 + , svField132 = 132 + , svField133 = 133 + , svGameWonFl = 135 + , svCinemaCurtainCond1 = 166 + , svCinemaCurtainCond2 = 167 + , svBankAttackAnimPlayedFl = 170 + , svCopCall1PlayedFl = 171 + , svCopCall2PlayedFl = 172 + , svField173 = 173 + , svField176 = 176 + , svPoolDogGoneFl = 177 + , svCinemaDogGoneFl = 181 + , svField183 = 183 + , svField184 = 184 + , svField186 = 186 + , svField188 = 188 + , svField200 = 200 + , svField214 = 214 + , svBombBoxOpenedFl = 220 + , svBombDisarmedFl = 225 + , svField228 = 228 + , svField231 = 231 + , svField253 = 253 + , svField261 = 261 + , svField270 = 270 + , svField300 = 300 + , svBaseElevatorCond1 = 311 + , svBaseFireFl = 312 + , svSecondElevatorAvailableFl = 318 + , svField320 = 320 + , svEscapeLeftJailFl = 330 + , svField333 = 333 + , svField338 = 338 + , svField339 = 339 + , svField340 = 340 + , svField341 = 341 + , svField352 = 352 + , svField353 = 353 + , svField354 = 354 + , svField355 = 355 + , svField356 = 356 + , svField357 = 357 + , svField399 = 399 + , svField401 = 401 +}; + +// As Script engine directly access savegame fields, +// refactoring it in separated fields properly named is impossible +struct Savegame { + byte _data[2050]; + CharacterLocation _cloneHopkins; + CharacterLocation _realHopkins; + CharacterLocation _samantha; + int16 _inventory[35]; // Originally at offset 1300 of data array + int16 _mapCarPosX; + int16 _mapCarPosY; +}; + +struct CreditItem { + bool _actvFl; + int _color; + int _linePosY; + int _lineSize; + byte _line[50]; +}; + +enum Language { LANG_EN = 0, LANG_FR = 1, LANG_SP = 2}; + +enum PlayerCharacter { CHARACTER_HOPKINS = 0, CHARACTER_HOPKINS_CLONE = 1, CHARACTER_SAMANTHA = 2 }; + +enum Directions { + DIR_NONE = -1, + DIR_UP = 1, + DIR_UP_RIGHT = 2, + DIR_RIGHT = 3, + DIR_DOWN_RIGHT = 4, + DIR_DOWN = 5, + DIR_DOWN_LEFT = 6, + DIR_LEFT = 7, + DIR_UP_LEFT = 8 +}; + +enum EventMode { + EVENTMODE_DEFAULT = 0, + EVENTMODE_IGNORE = 1, + EVENTMODE_CREDITS = 3, + EVENTMODE_ALT = 4 +}; + +class HopkinsEngine; + +/** + * Engine Globals + */ +class Globals { +private: + HopkinsEngine *_vm; + +public: + bool _disableInventFl; + bool _cityMapEnabledFl; + bool _linuxEndDemoFl; + bool _censorshipFl; + bool _introSpeechOffFl; + int _exitId; + Directions _oceanDirection; + int _actionDirection; + int _inventory[36]; + int _screenId; + int _prevScreenId; + int _characterMaxPosY; + int _baseMapColor; + int _spriteSize[500]; + PlayerCharacter _characterType; + uint _speed; + byte *_answerBuffer; + Savegame *_saveData; + Language _language; + HopkinsItem _hopkinsItem[70]; + + CreditItem _creditsItem[200]; + int _creditsLineNumb; + int _creditsStep; + int _creditsPosY; + int _creditsStartX; + int _creditsEndX; + int _creditsStartY; + int _creditsEndY; + + int _menuSpeed; + int _menuSoundOff; + int _menuTextOff; + int _menuVoiceOff; + int _menuMusicOff; + int _menuDisplayType; + int _menuScrollSpeed; + + byte *_optionDialogSpr; + bool _optionDialogFl; + + bool _actionMoveTo; + bool _freezeCharacterFl; + bool _checkDistanceFl; + byte *_characterSpriteBuf; + Common::String _zoneFilename; + Common::String _textFilename; + byte *_levelSpriteBuf; + + EventMode _eventMode; + + Globals(HopkinsEngine *vm); + ~Globals(); + byte *allocMemory(int count); + byte *freeMemory(byte *p); + void setConfig(); + void clearAll(); + void loadCharacterData(); + + int _curRoomNum; +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_GLOBALS_H */ diff --git a/engines/hopkins/graphics.cpp b/engines/hopkins/graphics.cpp new file mode 100644 index 0000000000..ebc5cfa8da --- /dev/null +++ b/engines/hopkins/graphics.cpp @@ -0,0 +1,1933 @@ +/* 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 "hopkins/graphics.h" + +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/hopkins.h" + +#include "common/system.h" +#include "graphics/palette.h" +#include "graphics/decoders/pcx.h" +#include "common/file.h" +#include "common/rect.h" +#include "engines/util.h" + +namespace Hopkins { + +GraphicsManager::GraphicsManager(HopkinsEngine *vm) { + _vm = vm; + + _lockCounter = 0; + _initGraphicsFl = false; + _screenWidth = _screenHeight = 0; + _screenLineSize = 0; + _palettePixels = NULL; + _lineNbr = 0; + _videoPtr = NULL; + _scrollOffset = 0; + _scrollPosX = 0; + _largeScreenFl = false; + _oldScrollPosX = 0; + _backBuffer = NULL; + _frontBuffer = NULL; + _screenBuffer = NULL; + _backupScreen = NULL; + _showDirtyRects = false; + + _lineNbr2 = 0; + _enlargedX = _enlargedY = 0; + _enlargedXFl = _enlargedYFl = false; + _fadeDefaultSpeed = 15; + _fadingFl = false; + _skipVideoLockFl = false; + _scrollStatus = 0; + _minX = 0; + _minY = 20; + _maxX = SCREEN_WIDTH * 2; + _maxY = SCREEN_HEIGHT - 20; + _posXClipped = _posYClipped = 0; + _clipX1 = _clipY1 = 0; + _clipFl = false; + _reduceX = _reducedY = 0; + _zoomOutFactor = 0; + _width = 0; + _specialWidth = 0; + _showZones = false; + _showLines = false; + + Common::fill(&_paletteBuffer[0], &_paletteBuffer[PALETTE_SIZE * 2], 0); + Common::fill(&_colorTable[0], &_colorTable[PALETTE_EXT_BLOCK_SIZE], 0); + Common::fill(&_palette[0], &_palette[PALETTE_EXT_BLOCK_SIZE], 0); + Common::fill(&_oldPalette[0], &_oldPalette[PALETTE_EXT_BLOCK_SIZE], 0); + + if (_vm->getIsDemo()) { + if (_vm->getPlatform() == Common::kPlatformLinux) + // CHECKME: Should be false? + _manualScroll = true; + else + _manualScroll = false; + _scrollSpeed = 16; + } else { + _manualScroll = false; + _scrollSpeed = 32; + } + + _noFadingFl = false; +} + +GraphicsManager::~GraphicsManager() { + _vm->_globals->freeMemory(_backBuffer); + _vm->_globals->freeMemory(_frontBuffer); + _vm->_globals->freeMemory(_screenBuffer); + _vm->_globals->freeMemory(_backupScreen); +} + +void GraphicsManager::setGraphicalMode(int width, int height) { + if (!_initGraphicsFl) { + Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0); + initGraphics(width, height, true, &pixelFormat16); + + // Init surfaces + _backBuffer = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT); + _frontBuffer = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT); + _screenBuffer = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT); + + _videoPtr = NULL; + _screenWidth = width; + _screenHeight = height; + + _screenLineSize = SCREEN_WIDTH * 2; + _palettePixels = _paletteBuffer; + _lineNbr = width; + + _initGraphicsFl = true; + } else { + error("setGraphicalMode called multiple times"); + } +} + +/** + * (try to) Lock Screen + */ +void GraphicsManager::lockScreen() { + if (!_skipVideoLockFl) { + if (_lockCounter++ == 0) { + _videoPtr = _screenBuffer; + _screenLineSize = SCREEN_WIDTH * 2; + } + } +} + +/** + * (try to) Unlock Screen + */ +void GraphicsManager::unlockScreen() { + assert(_videoPtr); + if (--_lockCounter == 0) { + _videoPtr = NULL; + } +} + +/** + * Clear Screen + */ +void GraphicsManager::clearScreen() { + lockScreen(); + assert(_videoPtr); + + Common::fill(_screenBuffer, _screenBuffer + _screenLineSize * _screenHeight, 0); + addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + unlockScreen(); +} + +void GraphicsManager::clearVesaScreen() { + Common::fill(_backBuffer, _backBuffer + _screenLineSize * _screenHeight, 0); + Common::fill(_frontBuffer, _frontBuffer + _screenLineSize * _screenHeight, 0); + addDirtyRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); +} + +/** + * Load Image + */ +void GraphicsManager::loadImage(const Common::String &file) { + Common::String filename = Common::String::format("%s.PCX", file.c_str()); + loadScreen(filename); + initColorTable(165, 170, _palette); +} + +/** + * Load VGA Image + */ +void GraphicsManager::loadVgaImage(const Common::String &file) { + setScreenWidth(SCREEN_WIDTH); + clearScreen(); + loadPCX320(_backBuffer, file, _palette); + memcpy(_frontBuffer, _backBuffer, 64000); + setScreenWidth(320); + _maxX = 320; + + copy16bFromSurfaceScaleX2(_frontBuffer); + addRefreshRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + fadeInBreakout(); +} + +/** + * Load Screen + */ +void GraphicsManager::loadScreen(const Common::String &file) { + Common::File f; + assert(!_videoPtr); + + bool flag = true; + bool fileFoundFl = false; + _vm->_fileIO->searchCat(file, RES_PIC, fileFoundFl); + if (!fileFoundFl) { + if (!f.open(file)) + error("loadScreen - %s", file.c_str()); + + f.seek(0, SEEK_END); + f.close(); + flag = false; + } + + scrollScreen(0); + loadPCX640(_backBuffer, file, _palette, flag); + + _scrollPosX = 0; + _oldScrollPosX = 0; + clearPalette(); + + if (!_largeScreenFl) { + setScreenWidth(SCREEN_WIDTH); + _maxX = SCREEN_WIDTH; + clearScreen(); + + display8BitRect(_backBuffer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + } else { + setScreenWidth(SCREEN_WIDTH * 2); + _maxX = SCREEN_WIDTH * 2; + clearScreen(); + + if (_manualScroll) + display8BitRect(_backBuffer, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + } + + memcpy(_frontBuffer, _backBuffer, SCREEN_WIDTH * 2 * SCREEN_HEIGHT); +} + +void GraphicsManager::initColorTable(int minIndex, int maxIndex, byte *palette) { + for (int idx = 0; idx < 256; ++idx) + _colorTable[idx] = idx; + + translateSurface(_colorTable, palette, 256, minIndex, maxIndex); + + for (int idx = 0; idx < 256; ++idx) { + byte v = _colorTable[idx]; + if (v > 27 || !v) + _colorTable[idx] = 0; + } + + _colorTable[0] = 1; +} + +/** + * Scroll Screen + */ +void GraphicsManager::scrollScreen(int amount) { + int result = CLIP(amount, 0, SCREEN_WIDTH); + _vm->_events->_startPos.x = result; + _scrollOffset = result; + _scrollPosX = result; +} + +void GraphicsManager::translateSurface(byte *destP, const byte *srcP, int count, int minThreshold, int maxThreshold) { + byte *destPosP = destP; + for (int idx = 0; idx < count; ++idx) { + int palIndex = *destPosP; + int srcOffset = 3 * palIndex; + int col1 = srcP[srcOffset] + srcP[srcOffset + 1] + srcP[srcOffset + 2]; + + for (int idx2 = 0; idx2 < 38; ++idx2) { + srcOffset = 3 * idx2; + int col2 = srcP[srcOffset] + srcP[srcOffset + 1] + srcP[srcOffset + 2]; + + col2 += minThreshold; + if (col2 < col1) + continue; + + col2 -= maxThreshold; + if (col2 > col1) + continue; + + *destPosP = (idx2 == 0) ? 1 : idx2; + break; + } + destPosP++; + } +} + +void GraphicsManager::fillSurface(byte *surface, byte *col, int size) { + byte dataVal; + + byte *dataP = surface; + for (int count = size - 1; count; count--){ + dataVal = *dataP; + *dataP = col[dataVal]; + dataP++; + } +} + +void GraphicsManager::loadPCX640(byte *surface, const Common::String &file, byte *palette, bool typeFlag) { + Common::File f; + Graphics::PCXDecoder pcxDecoder; + + // Clear the passed surface + memset(surface, 0, SCREEN_WIDTH * 2 * SCREEN_HEIGHT); + + if (typeFlag) { + // Load PCX from within the PIC resource + if (!f.open("PIC.RES")) + error("Error opening PIC.RES."); + f.seek(_vm->_fileIO->_catalogPos); + } else { + // Load stand alone PCX file + if (!f.open(file)) + error("Error opening PCX %s.", file.c_str()); + } + + // Decode the PCX + if (!pcxDecoder.loadStream(f)) + error("Error decoding PCX %s", file.c_str()); + + const Graphics::Surface *s = pcxDecoder.getSurface(); + + // Copy out the dimensions and pixels of the decoded surface + _largeScreenFl = s->w > SCREEN_WIDTH; + Common::copy((byte *)s->pixels, (byte *)s->pixels + (s->pitch * s->h), surface); + + // Copy out the palette + const byte *palSrc = pcxDecoder.getPalette(); + Common::copy((const byte *)palSrc, (const byte *)palSrc + PALETTE_BLOCK_SIZE, palette); + + f.close(); +} + +void GraphicsManager::loadPCX320(byte *surface, const Common::String &file, byte *palette) { + Common::File f; + if (!f.open(file)) + error("File not found - %s", file.c_str()); + + size_t filesize = f.size(); + + f.read(surface, 128); + int imageSize = filesize - 896; + byte *ptr = _vm->_globals->allocMemory(65024); + size_t curBufSize; + int imageNumb; + int imageDataSize; + if (imageSize >= 64000) { + imageNumb = imageSize / 64000 + 1; + imageDataSize = abs(64000 * (imageSize / 64000) - imageSize); + f.read(ptr, 64000); + curBufSize = 64000; + } else { + imageNumb = 1; + imageDataSize = imageSize; + f.read(ptr, imageSize); + curBufSize = imageSize; + } + imageNumb--; + size_t curByteIdx = 0; + for (int i = 0; i < 64000; i++) { + if (curByteIdx == curBufSize) { + curByteIdx = 0; + --imageNumb; + curBufSize = 64000; + if (!imageNumb) + curBufSize = imageDataSize; + f.read(ptr, curBufSize); + } + byte curByte = ptr[curByteIdx++]; + if (curByte > 192) { + int repeatCount = curByte - 192; + if (curByteIdx == curBufSize) { + curByteIdx = 0; + --imageNumb; + curBufSize = 64000; + if (imageNumb == 1) + curBufSize = imageDataSize; + f.read(ptr, curBufSize); + } + curByte = ptr[curByteIdx++]; + for (; repeatCount; repeatCount--) + surface[i++] = curByte; + + --i; + } else { + surface[i] = curByte; + } + } + + f.seek(filesize - 768); + f.read(palette, 768); + f.close(); + + _vm->_globals->freeMemory(ptr); +} + +// Clear Palette +void GraphicsManager::clearPalette() { + // As weird as it sounds, this is what the original Linux executable does, + // and not a full array clear. + _paletteBuffer[0] = 0; +} + +void GraphicsManager::setScreenWidth(int pitch) { + _lineNbr = _lineNbr2 = pitch; +} + +/** + * Copies data from a 8-bit palette surface into the 16-bit screen + */ +void GraphicsManager::display8BitRect(const byte *surface, int xs, int ys, int width, int height, int destX, int destY) { + lockScreen(); + + assert(_videoPtr); + const byte *srcP = xs + _lineNbr2 * ys + surface; + byte *destP = (byte *)_videoPtr + destX * 2 + _screenLineSize * destY; + + for (int yp = 0; yp < height; ++yp) { + // Copy over the line, using the source pixels as lookups into the pixels palette + const byte *lineSrcP = srcP; + byte *lineDestP = destP; + + for (int xp = 0; xp < width; ++xp) { + lineDestP[0] = _palettePixels[lineSrcP[0] * 2]; + lineDestP[1] = _palettePixels[(lineSrcP[0] * 2) + 1]; + lineDestP += 2; + lineSrcP++; + } + // Move to the start of the next line + srcP += _lineNbr2; + destP += _screenLineSize; + } + + unlockScreen(); + addRefreshRect(destX, destY, destX + width, destY + height); +} + +void GraphicsManager::displayScaled8BitRect(const byte *surface, int xp, int yp, int width, int height, int destX, int destY) { + int xCtr; + const byte *palette; + int savedXCount; + byte *loopDestP; + const byte *loopSrcP; + int yCtr; + + assert(_videoPtr); + const byte *srcP = surface + xp + 320 * yp; + byte *destP = (byte *)_videoPtr + 30 * _screenLineSize + destX + destX + destX + destX + _screenLineSize * 2 * destY; + int yCount = height; + int xCount = width; + + do { + yCtr = yCount; + xCtr = xCount; + loopSrcP = srcP; + loopDestP = destP; + savedXCount = xCount; + palette = _palettePixels; + + do { + destP[0] = destP[2] = destP[_screenLineSize] = destP[_screenLineSize + 2] = palette[2 * srcP[0]]; + destP[1] = destP[3] = destP[_screenLineSize + 1] = destP[_screenLineSize + 3] = palette[(2 * srcP[0]) + 1]; + ++srcP; + destP += 4; + --xCtr; + } while (xCtr); + + xCount = savedXCount; + destP = loopDestP + _screenLineSize * 2; + srcP = loopSrcP + 320; + yCount = yCtr - 1; + } while (yCtr != 1); + + addRefreshRect(destX, destY, destX + width, destY + width); +} + +/** + * Fade in. the step number is determine by parameter. + */ +void GraphicsManager::fadeIn(const byte *palette, int step, const byte *surface) { + byte palData2[PALETTE_BLOCK_SIZE]; + int fadeStep; + if (step > 1) + fadeStep = step; + else + fadeStep = 2; + // Initialize temporary palette + Common::fill(&palData2[0], &palData2[PALETTE_BLOCK_SIZE], 0); + + // Set current palette to black + setPaletteVGA256(palData2); + + // Loop through fading in the palette + for (int fadeIndex = 0; fadeIndex < fadeStep; ++fadeIndex) { + for (int palOffset = 0; palOffset < PALETTE_BLOCK_SIZE; palOffset += 3) { + palData2[palOffset + 0] = fadeIndex * palette[palOffset + 0] / (fadeStep - 1); + palData2[palOffset + 1] = fadeIndex * palette[palOffset + 1] / (fadeStep - 1); + palData2[palOffset + 2] = fadeIndex * palette[palOffset + 2] / (fadeStep - 1); + } + + // Set the transition palette and refresh the screen + setPaletteVGA256(palData2); + display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + updateScreen(); + + // Added a delay in order to see the fading + _vm->_events->delay(20); + } + + // Set the final palette + setPaletteVGA256(palette); + + // Refresh the screen + display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + updateScreen(); +} + +/** + * Fade out. the step number is determine by parameter. + */ +void GraphicsManager::fadeOut(const byte *palette, int step, const byte *surface) { + byte palData[PALETTE_BLOCK_SIZE]; + if ((step > 1) && (palette) && (!_vm->_events->_escKeyFl)) { + int fadeStep = step; + for (int fadeIndex = 0; fadeIndex < fadeStep; fadeIndex++) { + for (int palOffset = 0; palOffset < PALETTE_BLOCK_SIZE; palOffset += 3) { + palData[palOffset + 0] = (fadeStep - fadeIndex - 1) * palette[palOffset + 0] / (fadeStep - 1); + palData[palOffset + 1] = (fadeStep - fadeIndex - 1) * palette[palOffset + 1] / (fadeStep - 1); + palData[palOffset + 2] = (fadeStep - fadeIndex - 1) * palette[palOffset + 2] / (fadeStep - 1); + } + + setPaletteVGA256(palData); + display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + updateScreen(); + + _vm->_events->delay(20); + } + } + + // No initial palette, or end of fading + for (int i = 0; i < PALETTE_BLOCK_SIZE; i++) + palData[i] = 0; + + setPaletteVGA256(palData); + display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + + updateScreen(); +} + +/** + * Short fade in. The step number is 1, the default step number is also set to 1. + */ +void GraphicsManager::fadeInShort() { + _fadeDefaultSpeed = 1; + fadeIn(_palette, 1, (const byte *)_frontBuffer); +} + +/** + * Short fade out. The step number is 1, the default step number is also set to 1. + */ +void GraphicsManager::fadeOutShort() { + _fadeDefaultSpeed = 1; + fadeOut(_palette, 1, (const byte *)_frontBuffer); +} + +/** + * Long fade in. The step number is 20, the default step number is also set to 15. + */ +void GraphicsManager::fadeInLong() { + _fadeDefaultSpeed = 15; + fadeIn(_palette, 20, (const byte *)_frontBuffer); +} + +/** + * Long fade out. The step number is 20, the default step number is also set to 15. + */ +void GraphicsManager::fadeOutLong() { + _fadeDefaultSpeed = 15; + fadeOut(_palette, 20, (const byte *)_frontBuffer); +} + +/** + * Fade in. The step number used is the default step number. + */ +void GraphicsManager::fadeInDefaultLength(const byte *surface) { + assert(surface); + fadeIn(_palette, _fadeDefaultSpeed, surface); +} + +/** + * Fade out. The step number used is the default step number. + */ +void GraphicsManager::fadeOutDefaultLength(const byte *surface) { + assert(surface); + fadeOut(_palette, _fadeDefaultSpeed, surface); +} + +/** + * Fade in used by for the breakout mini-game + */ +void GraphicsManager::fadeInBreakout() { + setPaletteVGA256(_palette); + copy16bFromSurfaceScaleX2(_frontBuffer); + updateScreen(); +} + +/** + * Fade out used by for the breakout mini-game + */ +void GraphicsManager::fadeOutBreakout() { + byte palette[PALETTE_EXT_BLOCK_SIZE]; + + memset(palette, 0, PALETTE_EXT_BLOCK_SIZE); + setPaletteVGA256(palette); + copy16bFromSurfaceScaleX2(_frontBuffer); + updateScreen(); +} + +void GraphicsManager::setPaletteVGA256(const byte *palette) { + changePalette(palette); +} + +void GraphicsManager::setPaletteVGA256WithRefresh(const byte *palette, const byte *surface) { + changePalette(palette); + display8BitRect(surface, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + updateScreen(); +} + +void GraphicsManager::setColorPercentage(int palIndex, int r, int g, int b) { + int palOffset = 3 * palIndex; + _palette[palOffset] = 255 * r / 100; + _palette[palOffset + 1] = 255 * g / 100; + _palette[palOffset + 2] = 255 * b / 100; +} + +void GraphicsManager::setColorPercentage2(int palIndex, int r, int g, int b) { + int rv = 255 * r / 100; + int gv = 255 * g / 100; + int bv = 255 * b / 100; + + int palOffset = 3 * palIndex; + _palette[palOffset] = rv; + _palette[palOffset + 1] = gv; + _palette[palOffset + 2] = bv; + + WRITE_UINT16(&_paletteBuffer[2 * palIndex], mapRGB(rv, gv, bv)); +} + +void GraphicsManager::changePalette(const byte *palette) { + const byte *srcP = &palette[0]; + for (int idx = 0; idx < PALETTE_SIZE; ++idx, srcP += 3) { + WRITE_UINT16(&_paletteBuffer[2 * idx], mapRGB(srcP[0], srcP[1], srcP[2])); + } +} + +uint16 GraphicsManager::mapRGB(byte r, byte g, byte b) { + Graphics::PixelFormat format = g_system->getScreenFormat(); + + return (r >> format.rLoss) << format.rShift + | (g >> format.gLoss) << format.gShift + | (b >> format.bLoss) << format.bShift; +} + +void GraphicsManager::updateScreen() { + // Display any aras of the screen that need refreshing + displayDirtyRects(); + displayRefreshRects(); + + // Extra checks for debug information + if (_showZones) + displayZones(); + + if (_showLines) + displayLines(); + + // Update the screen + g_system->updateScreen(); +} + +void GraphicsManager::copyWinscanVbe3(const byte *srcData, byte *destSurface) { + byte srcByte; + byte destLen1; + byte *destSlice1P; + byte destLen2; + byte *destSlice2P; + + int rleValue = 0; + int destOffset = 0; + const byte *srcP = srcData; + for (;;) { + srcByte = srcP[0]; + if (srcByte == kByteStop) + return; + if (srcByte == 211) { + destLen1 = srcP[1]; + rleValue = srcP[2]; + destSlice1P = destOffset + destSurface; + destOffset += destLen1; + memset(destSlice1P, rleValue, destLen1); + srcP += 3; + } else if (srcByte < 222) { + if (srcByte > 211) { + destLen2 = (byte)(srcP[0] + 45); + rleValue = srcP[1]; + destSlice2P = destOffset + destSurface; + destOffset += destLen2; + memset(destSlice2P, rleValue, destLen2); + srcP += 2; + } else { + destSurface[destOffset] = srcByte; + ++srcP; + ++destOffset; + } + } else if (srcByte < kSetOffset) { + destOffset += (byte)(srcP[0] + 35); + srcP++; + } else if (srcByte == k8bVal) { + destOffset += srcP[1]; + srcP += 2; + } else if (srcByte == k16bVal) { + destOffset += READ_LE_UINT16(srcP + 1); + srcP += 3; + } else { + destOffset += READ_LE_UINT32(srcP + 1); + srcP += 5; + } + } +} + +void GraphicsManager::copyVideoVbe16(const byte *srcData) { + const byte *srcP = srcData; + int destOffset = 0; + + lockScreen(); + assert(_videoPtr); + + for (;;) { + byte srcByte = srcP[0]; + if (srcByte >= 222) { + if (srcByte == kByteStop) + break; + + if (srcByte < kSetOffset) { + destOffset += srcByte - 221; + srcByte = *++srcP; + } else if (srcByte == k8bVal) { + destOffset += srcP[1]; + srcByte = srcP[2]; + srcP += 2; + } else if (srcByte == k16bVal) { + destOffset += READ_LE_UINT16(srcP + 1); + srcByte = srcP[3]; + srcP += 3; + } else { + destOffset += READ_LE_UINT32(srcP + 1); + srcByte = srcP[5]; + srcP += 5; + } + } + + if (destOffset > SCREEN_WIDTH * SCREEN_HEIGHT) { + warning("HACK: Stopping anim, out of bounds - 0x%x %d", srcByte, destOffset); + break; + } + + if (srcByte > 210) { + if (srcByte == 211) { + int pixelCount = srcP[1]; + int pixelIndex = srcP[2]; + byte *destP = (byte *)_videoPtr + destOffset * 2; + destOffset += pixelCount; + + while (pixelCount--) { + destP[0] = _palettePixels[2 * pixelIndex]; + destP[1] = _palettePixels[(2 * pixelIndex) + 1]; + destP += 2; + } + + srcP += 3; + } else { + int pixelCount = srcByte - 211; + int pixelIndex = srcP[1]; + byte *destP = (byte *)_videoPtr + destOffset * 2; + destOffset += pixelCount; + + while (pixelCount--) { + destP[0] = _palettePixels[2 * pixelIndex]; + destP[1] = _palettePixels[(2 * pixelIndex) + 1]; + destP += 2; + } + + srcP += 2; + } + } else { + byte *destP = (byte *)_videoPtr + destOffset * 2; + destP[0] = _palettePixels[2 * srcByte]; + destP[1] = _palettePixels[(2 * srcByte) + 1]; + ++srcP; + ++destOffset; + } + } + unlockScreen(); +} + +void GraphicsManager::copyVideoVbe16a(const byte *srcData) { + byte srcByte; + int destOffset = 0; + const byte *srcP = srcData; + + lockScreen(); + for (;;) { + srcByte = srcP[0]; + if (srcByte == kByteStop) + break; + if (srcP[0] > kByteStop) { + if (srcByte == k8bVal) { + destOffset += srcP[1]; + srcByte = srcP[2]; + srcP += 2; + } else if (srcByte == k16bVal) { + destOffset += READ_LE_UINT16(srcP + 1); + srcByte = srcP[3]; + srcP += 3; + } else { + destOffset += READ_LE_UINT32(srcP + 1); + srcByte = srcP[5]; + srcP += 5; + } + } + + WRITE_LE_UINT16((byte *)_videoPtr + destOffset * 2, READ_LE_UINT16(_palettePixels + 2 * srcByte)); + ++srcP; + ++destOffset; + } + unlockScreen(); +} + +void GraphicsManager::copySurfaceRect(const byte *srcSurface, byte *destSurface, int xs, int ys, int width, int height) { + const byte *srcP; + byte *destP; + int rowCount; + int rowCount2; + + // TODO: This code in the original is potentially dangerous, as it doesn't clip the area to within + // the screen, and so thus can read areas outside of the allocated surface buffer + srcP = xs + _lineNbr2 * ys + srcSurface; + destP = destSurface; + rowCount = height; + do { + rowCount2 = rowCount; + if (width & 1) { + memcpy(destP, srcP, width); + srcP += width; + destP += width; + } else if (width & 2) { + for (int i = width >> 1; i; --i) { + destP[0] = srcP[0]; + destP[1] = srcP[1]; + srcP += 2; + destP += 2; + } + } else { + memcpy(destP, srcP, 4 * (width >> 2)); + srcP += 4 * (width >> 2); + destP += 4 * (width >> 2); + } + srcP = _lineNbr2 + srcP - width; + rowCount = rowCount2 - 1; + } while (rowCount2 != 1); +} + +/** + * Draws a sprite onto the screen + * @param surface Destination surface + * @param spriteData The raw data for a sprite set + * @param xp X co-ordinate. For some reason, starts from 300 = first column + * @param yp Y co-ordinate. FOr some reason, starts from 300 = top row + * @param spriteIndex Index of the sprite to draw + */ +void GraphicsManager::drawVesaSprite(byte *surface, const byte *spriteData, int xp, int yp, int spriteIndex) { + // Get a pointer to the start of the desired sprite + const byte *spriteP = spriteData + 3; + for (int i = spriteIndex; i; --i) + spriteP += READ_LE_UINT32(spriteP) + 16; + + _posXClipped = 0; + _posYClipped = 0; + _clipFl = false; + + spriteP += 4; + int width = READ_LE_UINT16(spriteP); + spriteP += 2; + int height = READ_LE_UINT16(spriteP); + + // Clip X + _clipX1 = width; + if ((xp + width) <= _minX + 300) + return; + if (xp < _minX + 300) { + _posXClipped = _minX + 300 - xp; + _clipFl = true; + } + + // Clip Y + _clipY1 = height; + if (yp <= 0) + return; + if (yp < _minY + 300) { + _posYClipped = _minY + 300 - yp; + _clipFl = true; + } + + // Clip X1 + if (xp >= _maxX + 300) + return; + if (xp + width > _maxX + 300) { + int xAmount = width + 10 - (xp + width - (_maxX + 300)); + if (xAmount <= 10) + return; + + _clipX1 = xAmount - 10; + _clipFl = true; + } + + // Clip Y1 + if (yp >= _maxY + 300) + return; + if (yp + height > _maxY + 300) { + int yAmount = height + 10 - (yp + height - (_maxY + 300)); + if (yAmount <= 10) + return; + + // _clipY1 is always positive thanks to the previous check + _clipY1 = yAmount - 10; + _clipFl = true; + } + + // Sprite display + + // Set up source + spriteP += 6; + int srcOffset = READ_LE_UINT16(spriteP); + spriteP += 4; + const byte *srcP = spriteP; + spriteP += srcOffset; + + // Set up surface destination + byte *destP = surface + (yp - 300) * _lineNbr2 + (xp - 300); + + // Handling for clipped versus non-clipped + if (_clipFl) { + // Clipped version + for (int yc = 0; yc < _clipY1; ++yc, destP += _lineNbr2) { + byte *tempDestP = destP; + byte byteVal; + int xc = 0; + + while ((byteVal = *srcP) != 253) { + ++srcP; + width = READ_LE_UINT16(srcP); + srcP += 2; + + if (byteVal == 254) { + // Copy pixel range + for (int xv = 0; xv < width; ++xv, ++xc, ++spriteP, ++tempDestP) { + if (_posYClipped == 0 && xc >= _posXClipped && xc < _clipX1) + *tempDestP = *spriteP; + } + } else { + // Skip over bytes + tempDestP += width; + xc += width; + } + } + + if (_posYClipped > 0) + --_posYClipped; + srcP += 3; + } + } else { + // Non-clipped + for (int yc = 0; yc < height; ++yc, destP += _lineNbr2) { + byte *tempDestP = destP; + byte byteVal; + + while ((byteVal = *srcP) != 253) { + ++srcP; + width = READ_LE_UINT16(srcP); + srcP += 2; + + if (byteVal == 254) { + // Copy pixel range + Common::copy(spriteP, spriteP + width, tempDestP); + spriteP += width; + } + + tempDestP += width; + } + + // Skip over control byte and width + srcP += 3; + } + } +} + +void GraphicsManager::endDisplayBob() { + for (int idx = 1; idx <= 20; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + _vm->_objectsMan->hideBob(idx); + } + + _vm->_events->refreshScreenAndEvents(); + _vm->_events->refreshScreenAndEvents(); + + for (int idx = 1; idx <= 20; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + _vm->_objectsMan->resetBob(idx); + } + + for (int idx = 1; idx < 36; ++idx) { + _vm->_objectsMan->_lockedAnims[idx]._enableFl = false; + } + + for (int idx = 1; idx <= 20; ++idx) { + _vm->_animMan->_animBqe[idx]._enabledFl = false; + } +} + +void GraphicsManager::displayAllBob() { + for (int idx = 1; idx <= 20; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + _vm->_objectsMan->displayBob(idx); + } +} + +void GraphicsManager::resetDirtyRects() { + _dirtyRects.clear(); +} + +void GraphicsManager::resetRefreshRects() { + _refreshRects.clear(); +} + +// Add a game area dirty rectangle +void GraphicsManager::addDirtyRect(int x1, int y1, int x2, int y2) { + x1 = CLIP(x1, _minX, _maxX); + y1 = CLIP(y1, _minY, _maxY); + x2 = CLIP(x2, _minX, _maxX); + y2 = CLIP(y2, _minY, _maxY); + + if ((x2 > x1) && (y2 > y1)) + addRectToArray(_dirtyRects, Common::Rect(x1, y1, x2, y2)); +} + +// Add a refresh rect +void GraphicsManager::addRefreshRect(int x1, int y1, int x2, int y2) { + x1 = MAX(x1, 0); + y1 = MAX(y1, 0); + x2 = MIN(x2, SCREEN_WIDTH); + y2 = MIN(y2, SCREEN_HEIGHT); + + if ((x2 > x1) && (y2 > y1)) + addRectToArray(_refreshRects, Common::Rect(x1, y1, x2, y2)); +} + +void GraphicsManager::addRectToArray(Common::Array<Common::Rect> &rects, const Common::Rect &newRect) { + // Scan for an intersection with existing rects + uint rectIndex; + for (rectIndex = 0; rectIndex < rects.size(); ++rectIndex) { + Common::Rect &r = rects[rectIndex]; + + if (r.intersects(newRect)) { + // Rect either intersects or is completely inside existing one, so extend existing one as necessary + r.extend(newRect); + break; + } + } + if (rectIndex == rects.size()) { + // Rect not intersecting any existing one, so add it in + assert(rects.size() < DIRTY_RECTS_SIZE); + rects.push_back(newRect); + } + + // Take care of merging the existing rect list. This is done as a separate check even if + // a previous extending above has been done, since the merging of the new rect above may + // result in further rects now able to be merged + + for (int srcIndex = rects.size() - 1; srcIndex > 0; --srcIndex) { + const Common::Rect &srcRect = rects[srcIndex]; + + // Loop through all the other rects to see if it intersects them + for (int destIndex = srcIndex - 1; destIndex >= 0; --destIndex) { + if (rects[destIndex].intersects(srcRect)) { + // Found an intersection, so extend the found one, and delete the original + rects[destIndex].extend(srcRect); + rects.remove_at(srcIndex); + break; + } + } + } +} + +// Draw any game dirty rects onto the screen intermediate surface +void GraphicsManager::displayDirtyRects() { + if (_dirtyRects.size() == 0) + return; + + lockScreen(); + + // Refresh the entire screen + for (uint idx = 0; idx < _dirtyRects.size(); ++idx) { + Common::Rect &r = _dirtyRects[idx]; + Common::Rect dstRect; + + if (_vm->_events->_breakoutFl) { + displayScaled8BitRect(_frontBuffer, r.left, r.top, r.right - r.left, r.bottom - r.top, r.left, r.top); + dstRect.left = r.left * 2; + dstRect.top = r.top * 2 + 30; + dstRect.setWidth((r.right - r.left) * 2); + dstRect.setHeight((r.bottom - r.top) * 2); + } else if (r.right > _vm->_events->_startPos.x && r.left < _vm->_events->_startPos.x + SCREEN_WIDTH) { + r.left = MAX<int16>(r.left, _vm->_events->_startPos.x); + r.right = MIN<int16>(r.right, (int16)_vm->_events->_startPos.x + SCREEN_WIDTH); + + display8BitRect(_frontBuffer, r.left, r.top, r.right - r.left, r.bottom - r.top, r.left - _vm->_events->_startPos.x, r.top); + + dstRect.left = r.left - _vm->_events->_startPos.x; + dstRect.top = r.top; + dstRect.setWidth(r.right - r.left); + dstRect.setHeight(r.bottom - r.top); + } + + // If it's a valid rect, then add it to the list of areas to refresh on the screen + if (dstRect.isValidRect() && dstRect.width() > 0 && dstRect.height() > 0) + addRectToArray(_refreshRects, dstRect); + } + + unlockScreen(); + resetDirtyRects(); +} + +void GraphicsManager::displayRefreshRects() { + Graphics::Surface *screenSurface = NULL; + if (_showDirtyRects) { + screenSurface = g_system->lockScreen(); + g_system->copyRectToScreen(_screenBuffer, _screenLineSize, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + } + + // Loop through copying over any specified rects to the screen + for (uint idx = 0; idx < _refreshRects.size(); ++idx) { + const Common::Rect &r = _refreshRects[idx]; + + byte *srcP = _screenBuffer + _screenLineSize * r.top + (r.left * 2); + g_system->copyRectToScreen(srcP, _screenLineSize, r.left, r.top, r.width(), r.height()); + + if (_showDirtyRects) + screenSurface->frameRect(r, 0xffffff); + } + + if (_showDirtyRects) + g_system->unlockScreen(); + + resetRefreshRects(); +} + +/** + * Display any zones for the current room + */ +void GraphicsManager::displayZones() { + Graphics::Surface *screenSurface = g_system->lockScreen(); + + for (int bobZoneId = 0; bobZoneId <= 48; bobZoneId++) { + int bobId = _vm->_linesMan->_bobZone[bobZoneId]; + if (bobId) { + // Get the rectangle for the zone + Common::Rect r(_vm->_objectsMan->_bob[bobId]._oldX, _vm->_objectsMan->_bob[bobId]._oldY, + _vm->_objectsMan->_bob[bobId]._oldX + _vm->_objectsMan->_bob[bobId]._oldWidth, + _vm->_objectsMan->_bob[bobId]._oldY + _vm->_objectsMan->_bob[bobId]._oldHeight); + + displayDebugRect(screenSurface, r, 0xff0000); + } + } + + for (int squareZoneId = 0; squareZoneId <= 99; squareZoneId++) { + if (_vm->_linesMan->_zone[squareZoneId]._enabledFl && _vm->_linesMan->_squareZone[squareZoneId]._enabledFl) { + Common::Rect r(_vm->_linesMan->_squareZone[squareZoneId]._left, _vm->_linesMan->_squareZone[squareZoneId]._top, + _vm->_linesMan->_squareZone[squareZoneId]._right, _vm->_linesMan->_squareZone[squareZoneId]._bottom); + + displayDebugRect(screenSurface, r, 0x00ff00); + } + } + + g_system->unlockScreen(); +} + +/** + * Display any zones for the current room + */ +void GraphicsManager::displayLines() { + Graphics::Surface *screenSurface = g_system->lockScreen(); + + uint16* pixels = (uint16*)screenSurface->pixels; + + for (int lineIndex = 0; lineIndex < _vm->_linesMan->_linesNumb; lineIndex++) { + int i = 0; + do { + int x = _vm->_linesMan->_lineItem[lineIndex]._lineData[i] - _scrollPosX; + int y = _vm->_linesMan->_lineItem[lineIndex]._lineData[i+1]; + if (x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT) { + pixels[ y * screenSurface->w + x ] = 0xffff; + } + i += 2; + } + while(_vm->_linesMan->_lineItem[lineIndex]._lineData[i] != -1); + } + + g_system->unlockScreen(); +} + + +void GraphicsManager::displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect, uint32 color) { + Common::Rect r = srcRect; + + // Move for scrolling offset and adjust to crop on-screen + r.translate(-_scrollPosX, 0); + r.left = MAX(r.left, (int16)0); + r.top = MAX(r.top, (int16)0); + r.right = MIN(r.right, (int16)SCREEN_WIDTH); + r.bottom = MIN(r.bottom, (int16)SCREEN_HEIGHT); + + // If there's an on-screen portion, display it + if (r.isValidRect()) + surface->frameRect(r, color); +} + +/** + * Fast Display of either a compressed or vesa sprite + */ +void GraphicsManager::fastDisplay(const byte *spriteData, int xp, int yp, int spriteIndex, bool addSegment) { + int width = _vm->_objectsMan->getWidth(spriteData, spriteIndex); + int height = _vm->_objectsMan->getHeight(spriteData, spriteIndex); + + if (*spriteData == 78) { + drawCompressedSprite(_backBuffer, spriteData, xp + 300, yp + 300, spriteIndex, 0, 0, false); + drawCompressedSprite(_frontBuffer, spriteData, xp + 300, yp + 300, spriteIndex, 0, 0, false); + } else { + drawVesaSprite(_frontBuffer, spriteData, xp + 300, yp + 300, spriteIndex); + drawVesaSprite(_backBuffer, spriteData, xp + 300, yp + 300, spriteIndex); + } + if (addSegment) + addDirtyRect(xp, yp, xp + width, yp + height); +} + +void GraphicsManager::fastDisplay2(const byte *objectData, int xp, int yp, int idx, bool addSegment) { + int width = _vm->_objectsMan->getWidth(objectData, idx); + int height = _vm->_objectsMan->getHeight(objectData, idx); + if (*objectData == 78) { + drawCompressedSprite(_backBuffer, objectData, xp + 300, yp + 300, idx, 0, 0, false); + drawCompressedSprite(_frontBuffer, objectData, xp + 300, yp + 300, idx, 0, 0, false); + } else { + drawVesaSprite(_frontBuffer, objectData, xp + 300, yp + 300, idx); + drawVesaSprite(_backBuffer, objectData, xp + 300, yp + 300, idx); + } + if (addSegment) + addDirtyRect(xp, yp, xp + width, yp + height); +} + +/** + * Copy from surface to video buffer, scale 2x. + */ +void GraphicsManager::copy16bFromSurfaceScaleX2(const byte *surface) { + byte *palPtr; + int curPixel; + + lockScreen(); + + assert(_videoPtr); + const byte *curSurface = surface; + byte *destPtr = 30 * _screenLineSize + (byte *)_videoPtr; + for (int y = 200; y; y--) { + byte *oldDestPtr = destPtr; + for (int x = 320; x; x--) { + curPixel = 2 * *curSurface; + palPtr = _palettePixels + curPixel; + destPtr[0] = destPtr[2] = destPtr[_screenLineSize] = destPtr[_screenLineSize + 2] = palPtr[0]; + destPtr[1] = destPtr[3] = destPtr[_screenLineSize + 1] = destPtr[_screenLineSize + 3] = palPtr[1]; + ++curSurface; + destPtr += 4; + } + destPtr = _screenLineSize * 2 + oldDestPtr; + } + + unlockScreen(); +} + +void GraphicsManager::restoreSurfaceRect(byte *destSurface, const byte *src, int xp, int yp, int width, int height) { + int yCtr; + + byte *destP = xp + _lineNbr2 * yp + destSurface; + int yNext = height; + const byte *srcP = src; + do { + yCtr = yNext; + if (width & 1) { + memcpy(destP, srcP, width); + srcP += width; + destP += width; + } else if (width & 2) { + for (int i = width >> 1; i; --i) { + destP[0] = srcP[0]; + destP[1] = srcP[1]; + srcP += 2; + destP += 2; + } + } else { + memcpy(destP, srcP, 4 * (width >> 2)); + srcP += 4 * (width >> 2); + destP += 4 * (width >> 2); + } + destP = _lineNbr2 + destP - width; + yNext = yCtr - 1; + } while (yCtr != 1); +} + +/** + * Compute the value of a parameter plus a given percentage + */ +int GraphicsManager::zoomIn(int val, int percentage) { + if (val) + val += percentage * (long int)val / 100; + + return val; +} + +/** + * Compute the value of a parameter minus a given percentage + */ +int GraphicsManager::zoomOut(int val, int percentage) { + if (val) + val -= percentage * (long int)val / 100; + + return val; +} + +// Display 'Perfect?' +void GraphicsManager::drawCompressedSprite(byte *surface, const byte *srcData, int xp300, int yp300, int frameIndex, int zoom1, int zoom2, bool flipFl) { + const byte *spriteStartP = srcData + 3; + for (int i = frameIndex; i; --i) + spriteStartP += READ_LE_UINT32(spriteStartP) + 16; + + const byte *spriteSizeP = spriteStartP + 4; + int spriteWidth = READ_LE_INT16(spriteSizeP); + spriteSizeP += 2; + int spriteHeight2 = READ_LE_INT16(spriteSizeP); + int spriteHeight1 = spriteHeight2; + const byte *spritePixelsP = spriteSizeP + 10; + _posXClipped = 0; + _posYClipped = 0; + _clipX1 = 0; + _clipY1 = 0; + if ((xp300 <= _minX) || (yp300 <= _minY) || (xp300 >= _maxX + 300) || (yp300 >= _maxY + 300)) + return; + + // Clipped values are greater or equal to zero, thanks to the previous test + _clipX1 = _maxX + 300 - xp300; + _clipY1 = _maxY + 300 - yp300; + + // _minX is never negative, and should be always 0 + // The previous check insures that xp300 it's always greater to it + // After this check, posXClipped is always positive + if (xp300 < _minX + 300) + _posXClipped = _minX + 300 - xp300; + + // Ditto. + if (yp300 < _minY + 300) + _posYClipped = _minY + 300 - yp300; + + byte *dest1P = xp300 + _lineNbr2 * (yp300 - 300) - 300 + surface; + if (zoom2) { + _enlargedX = 0; + _enlargedY = 0; + _enlargedYFl = false; + _enlargedXFl = false; + _width = spriteWidth; + int zoomedWidth = zoomIn(spriteWidth, zoom2); + int zoomedHeight = zoomIn(spriteHeight1, zoom2); + if (flipFl) { + byte *clippedDestP = zoomedWidth + dest1P; + if (_posYClipped) { + if (_posYClipped < 0 || _posYClipped >= zoomedHeight) + return; + int hiddenHeight = 0; + while (zoomIn(++hiddenHeight, zoom2) < _posYClipped) + ; + spritePixelsP += _width * hiddenHeight; + clippedDestP += _lineNbr2 * _posYClipped; + zoomedHeight -= _posYClipped; + } + if (zoomedHeight > _clipY1) + zoomedHeight = _clipY1; + if (_posXClipped) { + if (_posXClipped >= zoomedWidth) + return; + zoomedWidth -= _posXClipped; + } + if (zoomedWidth > _clipX1) { + int clippedZoomedWidth = zoomedWidth - _clipX1; + clippedDestP -= clippedZoomedWidth; + int closestWidth = 0; + while (zoomIn(++closestWidth, zoom2) < clippedZoomedWidth) + ; + spritePixelsP += closestWidth; + zoomedWidth = _clipX1; + } + int curHeight; + do { + for (;;) { + curHeight = zoomedHeight; + byte *oldDestP = clippedDestP; + const byte *oldSpritePixelsP = spritePixelsP; + _enlargedXFl = false; + _enlargedX = 0; + for (int i = zoomedWidth; i; _enlargedXFl = false, i--) { + for (;;) { + if (*spritePixelsP) + *clippedDestP = *spritePixelsP; + --clippedDestP; + ++spritePixelsP; + if (!_enlargedXFl) + _enlargedX += zoom2; + if (_enlargedX >= 0 && _enlargedX < 100) + break; + _enlargedX -= 100; + --spritePixelsP; + _enlargedXFl = true; + --i; + if (!i) + break; + } + } + spritePixelsP = _width + oldSpritePixelsP; + clippedDestP = _lineNbr2 + oldDestP; + if (!_enlargedYFl) + _enlargedY += zoom2; + if (_enlargedY >= 0 && _enlargedY < 100) + break; + _enlargedY -= 100; + spritePixelsP = oldSpritePixelsP; + _enlargedYFl = true; + zoomedHeight = curHeight - 1; + if (curHeight == 1) + return; + } + _enlargedYFl = false; + zoomedHeight = curHeight - 1; + } while (curHeight != 1); + } else { + if (_posYClipped) { + if (_posYClipped >= zoomedHeight) + return; + int closerHeight = 0; + while (zoomIn(++closerHeight, zoom2) < _posYClipped) + ; + spritePixelsP += _width * closerHeight; + dest1P += _lineNbr2 * _posYClipped; + zoomedHeight -= _posYClipped; + } + if (zoomedHeight > _clipY1) + zoomedHeight = _clipY1; + if (_posXClipped) { + if (_posXClipped >= zoomedWidth) + return; + int closerWidth = 0; + while (zoomIn(++closerWidth, zoom2) < _posXClipped) + ; + spritePixelsP += closerWidth; + dest1P += _posXClipped; + zoomedWidth = zoomedWidth - _posXClipped; + } + if (zoomedWidth > _clipX1) + zoomedWidth = _clipX1; + + int curHeight; + do { + for (;;) { + curHeight = zoomedHeight; + byte *oldDest1P = dest1P; + const byte *oldSpritePixelsP = spritePixelsP; + _enlargedXFl = false; + _enlargedX = 0; + for (int i = zoomedWidth; i; _enlargedXFl = false, i--) { + for (;;) { + if (*spritePixelsP) + *dest1P = *spritePixelsP; + ++dest1P; + ++spritePixelsP; + if (!_enlargedXFl) + _enlargedX += zoom2; + if (_enlargedX >= 0 && _enlargedX < 100) + break; + _enlargedX -= 100; + --spritePixelsP; + _enlargedXFl = true; + --i; + if (!i) + break; + } + } + spritePixelsP = _width + oldSpritePixelsP; + dest1P = _lineNbr2 + oldDest1P; + if (!_enlargedYFl) + _enlargedY += zoom2; + if (_enlargedY >= 0 && _enlargedY < 100) + break; + _enlargedY -= 100; + spritePixelsP = oldSpritePixelsP; + _enlargedYFl = true; + zoomedHeight = curHeight - 1; + if (curHeight == 1) + return; + } + _enlargedYFl = false; + zoomedHeight = curHeight - 1; + } while (curHeight != 1); + } + } else if (zoom1) { + _reduceX = 0; + _reducedY = 0; + _width = spriteWidth; + _zoomOutFactor = zoom1; + if (zoom1 < 100) { + int zoomedSpriteWidth = zoomOut(spriteWidth, _zoomOutFactor); + if (flipFl) { + byte *curDestP = zoomedSpriteWidth + dest1P; + do { + byte *oldDestP = curDestP; + _reducedY += _zoomOutFactor; + if (_reducedY >= 0 && _reducedY < 100) { + _reduceX = 0; + int curWidth = zoomedSpriteWidth; + for (int i = _width; i; i--) { + _reduceX += _zoomOutFactor; + if (_reduceX >= 0 && _reduceX < 100) { + if (curWidth >= _posXClipped && curWidth < _clipX1 && *spritePixelsP) + *curDestP = *spritePixelsP; + --curDestP; + ++spritePixelsP; + --curWidth; + } else { + _reduceX -= 100; + ++spritePixelsP; + } + } + curDestP = _lineNbr2 + oldDestP; + } else { + _reducedY -= 100; + spritePixelsP += _width; + } + --spriteHeight2; + } while (spriteHeight2); + } else { + do { + int oldSpriteHeight = spriteHeight2; + byte *oldDest1P = dest1P; + _reducedY += _zoomOutFactor; + if (_reducedY >= 0 && _reducedY < 100) { + _reduceX = 0; + int curX = 0; + for (int i = _width; i; i--) { + _reduceX += _zoomOutFactor; + if (_reduceX >= 0 && _reduceX < 100) { + if (curX >= _posXClipped && curX < _clipX1 && *spritePixelsP) + *dest1P = *spritePixelsP; + ++dest1P; + ++spritePixelsP; + ++curX; + } else { + _reduceX -= 100; + ++spritePixelsP; + } + } + spriteHeight2 = oldSpriteHeight; + dest1P = _lineNbr2 + oldDest1P; + } else { + _reducedY -= 100; + spritePixelsP += _width; + } + --spriteHeight2; + } while (spriteHeight2); + } + } + } else { + _width = spriteWidth; + if (flipFl) { + byte *dest2P = spriteWidth + dest1P; + _specialWidth = spriteWidth; + if (_posYClipped) { + if (_posYClipped >= spriteHeight1 || spriteHeight1 < 0) + return; + spritePixelsP += spriteWidth * _posYClipped; + dest2P += _lineNbr2 * _posYClipped; + spriteHeight1 -= _posYClipped; + } + if (spriteHeight1 > _clipY1) + spriteHeight1 = _clipY1; + + if (_posXClipped >= spriteWidth) + return; + spriteWidth -= _posXClipped; + + if (spriteWidth > _clipX1) { + int clippedWidth = spriteWidth - _clipX1; + spritePixelsP += clippedWidth; + dest2P -= clippedWidth; + spriteWidth = _clipX1; + } + int yCtr2; + do { + yCtr2 = spriteHeight1; + byte *destCopy2P = dest2P; + const byte *spritePixelsCopy2P = spritePixelsP; + for (int xCtr2 = spriteWidth; xCtr2; xCtr2--) { + if (*spritePixelsP) + *dest2P = *spritePixelsP; + ++spritePixelsP; + --dest2P; + } + spritePixelsP = _specialWidth + spritePixelsCopy2P; + dest2P = _lineNbr2 + destCopy2P; + spriteHeight1 = yCtr2 - 1; + } while (yCtr2 != 1); + } else { + _specialWidth = spriteWidth; + if (_posYClipped) { + if (_posYClipped >= spriteHeight1 || spriteHeight1 < 0) + return; + spritePixelsP += spriteWidth * _posYClipped; + dest1P += _lineNbr2 * _posYClipped; + spriteHeight1 -= _posYClipped; + } + if (spriteHeight1 > _clipY1) + spriteHeight1 = _clipY1; + if (_posXClipped) { + if (_posXClipped >= spriteWidth) + return; + spritePixelsP += _posXClipped; + dest1P += _posXClipped; + spriteWidth -= _posXClipped; + } + if (spriteWidth > _clipX1) + spriteWidth = _clipX1; + int yCtr1; + do { + yCtr1 = spriteHeight1; + byte *dest1CopyP = dest1P; + const byte *spritePixelsCopyP = spritePixelsP; + for (int xCtr1 = spriteWidth; xCtr1; xCtr1--) { + if (*spritePixelsP) + *dest1P = *spritePixelsP; + ++dest1P; + ++spritePixelsP; + } + spritePixelsP = _specialWidth + spritePixelsCopyP; + dest1P = _lineNbr2 + dest1CopyP; + spriteHeight1 = yCtr1 - 1; + } while (yCtr1 != 1); + } + } +} + +void GraphicsManager::copySurface(const byte *surface, int x1, int y1, int width, int height, byte *destSurface, int destX, int destY) { + int left = x1; + int top = y1; + int croppedWidth = width; + int croppedHeight = height; + + if (x1 < _minX) { + croppedWidth = width - (_minX - x1); + left = _minX; + } + if (y1 < _minY) { + croppedHeight = height - (_minY - y1); + top = _minY; + } + + if (top + croppedHeight > _maxY) + croppedHeight = _maxY - top; + if (left + croppedWidth > _maxX) + croppedWidth = _maxX - left; + + if (croppedWidth > 0 && croppedHeight > 0) { + int height2 = croppedHeight; + copyRect(surface, left, top, croppedWidth, croppedHeight, destSurface, destX, destY); + addDirtyRect(left, top, left + croppedWidth, top + height2); + } +} + +void GraphicsManager::copyRect(const byte *srcSurface, int x1, int y1, uint16 width, int height, byte *destSurface, int destX, int destY) { + const byte *srcP = x1 + _lineNbr2 * y1 + srcSurface; + byte *destP = destX + _lineNbr2 * destY + destSurface; + int yp = height; + int yCurrent; + do { + yCurrent = yp; + memcpy(destP, srcP, 4 * (width >> 2)); + const byte *src2P = (srcP + 4 * (width >> 2)); + byte *dest2P = (destP + 4 * (width >> 2)); + int pitch = width - 4 * (width >> 2); + memcpy(dest2P, src2P, pitch); + destP = (dest2P + pitch + _lineNbr2 - width); + srcP = (src2P + pitch + _lineNbr2 - width); + yp = yCurrent - 1; + } while (yCurrent != 1); +} + +// Display Font +void GraphicsManager::displayFont(byte *surface, const byte *spriteData, int xp, int yp, int characterIndex, int color) { + const byte *spriteDataP = spriteData + 3; + for (int i = characterIndex; i; --i) + spriteDataP += READ_LE_UINT32(spriteDataP) + 16; + + int spriteWidth = 0; + int spriteHeight = 0; + const byte *spriteSizeP = spriteDataP + 4; + spriteWidth = READ_LE_INT16(spriteSizeP); + spriteSizeP += 2; + spriteHeight = READ_LE_INT16(spriteSizeP); + const byte *spritePixelsP = spriteSizeP + 10; + byte *destP = surface + xp + _lineNbr2 * yp; + _width = spriteWidth; + + int yCtr; + do { + yCtr = spriteHeight; + byte *destLineP = destP; + for (int xCtr = spriteWidth; xCtr; xCtr--) { + byte destByte = *spritePixelsP; + if (*spritePixelsP) { + if (destByte == 252) + destByte = color; + *destP = destByte; + } + + ++destP; + ++spritePixelsP; + } + destP = _lineNbr2 + destLineP; + spriteHeight = yCtr - 1; + } while (yCtr != 1); +} + +void GraphicsManager::initScreen(const Common::String &file, int mode, bool initializeScreen) { + Common::String filename = file + ".ini"; + bool fileFoundFl = false; + + byte *ptr = _vm->_fileIO->searchCat(filename, RES_INI, fileFoundFl); + + if (!fileFoundFl) { + ptr = _vm->_fileIO->loadFile(filename); + } + + if (!mode) { + filename = file + ".spr"; + _vm->_globals->_levelSpriteBuf = _vm->_globals->freeMemory(_vm->_globals->_levelSpriteBuf); + if (initializeScreen) { + fileFoundFl = false; + _vm->_globals->_levelSpriteBuf = _vm->_fileIO->searchCat(filename, RES_SLI, fileFoundFl); + if (!fileFoundFl) { + _vm->_globals->_levelSpriteBuf = _vm->_fileIO->loadFile(filename); + } else { + _vm->_globals->_levelSpriteBuf = _vm->_fileIO->loadFile("RES_SLI.RES"); + } + } + } + if (READ_BE_UINT24(ptr) != MKTAG24('I', 'N', 'I')) { + error("Invalid INI File %s", file.c_str()); + } else { + bool doneFlag = false; + int dataOffset = 1; + + do { + int dataVal1 = _vm->_script->handleOpcode(ptr + 20 * dataOffset); + if (_vm->shouldQuit()) + return; + + if (dataVal1 == 2) + dataOffset = _vm->_script->handleGoto((ptr + 20 * dataOffset)); + if (dataVal1 == 3) + dataOffset = _vm->_script->handleIf(ptr, dataOffset); + if (dataOffset == -1) + error("Error, defective IFF"); + if (dataVal1 == 1 || dataVal1 == 4) + ++dataOffset; + if (!dataVal1 || dataVal1 == 5) + doneFlag = true; + } while (!doneFlag); + } + _vm->_globals->freeMemory(ptr); + _vm->_globals->_answerBuffer = _vm->_globals->freeMemory(_vm->_globals->_answerBuffer); + + filename = file + ".rep"; + fileFoundFl = false; + byte *dataP = _vm->_fileIO->searchCat(filename, RES_REP, fileFoundFl); + if (!fileFoundFl) + dataP = _vm->_fileIO->loadFile(filename); + + _vm->_globals->_answerBuffer = dataP; + _vm->_objectsMan->_forceZoneFl = true; + _vm->_objectsMan->_changeVerbFl = false; +} + +void GraphicsManager::displayScreen(bool initPalette) { + if (initPalette) + initColorTable(50, 65, _palette); + + if (_lineNbr == SCREEN_WIDTH) + fillSurface(_frontBuffer, _colorTable, SCREEN_WIDTH * SCREEN_HEIGHT); + else if (_lineNbr == (SCREEN_WIDTH * 2)) + fillSurface(_frontBuffer, _colorTable, SCREEN_WIDTH * SCREEN_HEIGHT * 2); + + display8BitRect(_frontBuffer, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + memcpy(_backBuffer, _frontBuffer, 614399); + updateScreen(); +} + +void GraphicsManager::copyWinscanVbe(const byte *src, byte *dest) { + int destOffset = 0; + const byte *srcPtr = src; + for (;;) { + byte byteVal = *srcPtr; + if (byteVal == kByteStop) + return; + if (*srcPtr > kByteStop) { + if (byteVal == k8bVal) { + destOffset += srcPtr[1]; + byteVal = srcPtr[2]; + srcPtr += 2; + } else if (byteVal == k16bVal) { + destOffset += READ_LE_UINT16(srcPtr + 1); + byteVal = srcPtr[3]; + srcPtr += 3; + } else { + destOffset += READ_LE_UINT32(srcPtr + 1); + byteVal = srcPtr[5]; + srcPtr += 5; + } + } + dest[destOffset] = byteVal; + ++srcPtr; + ++destOffset; + } +} + +// Reduce Screen +void GraphicsManager::reduceScreenPart(const byte *srcSurface, byte *destSurface, int xp, int yp, int width, int height, int zoom) { + const byte *srcP = xp + _lineNbr2 * yp + srcSurface; + byte *destP = destSurface; + _zoomOutFactor = zoom; + _width = width; + _reduceX = 0; + _reducedY = 0; + if (zoom < 100) { + for (int yCtr = 0; yCtr < height; ++yCtr, srcP += _lineNbr2) { + _reducedY += _zoomOutFactor; + if (_reducedY < 100) { + _reduceX = 0; + const byte *lineSrcP = srcP; + + for (int xCtr = 0; xCtr < _width; ++xCtr) { + _reduceX += _zoomOutFactor; + if (_reduceX < 100) { + *destP++ = *lineSrcP++; + } else { + _reduceX -= 100; + ++lineSrcP; + } + } + } else { + _reducedY -= 100; + } + } + } +} + +/** + * Draw horizontal line + */ +void GraphicsManager::drawHorizontalLine(byte *surface, int xp, int yp, uint16 width, byte col) { + memset(surface + xp + _lineNbr2 * yp, col, width); +} + +/** + * Draw vertical line + */ +void GraphicsManager::drawVerticalLine(byte *surface, int xp, int yp, int height, byte col) { + byte *destP = surface + xp + _lineNbr2 * yp; + + for (int yCtr = height; yCtr; yCtr--) { + *destP = col; + destP += _lineNbr2; + } +} + +/** + * Backup the current screen + */ +void GraphicsManager::backupScreen() { + // Allocate a new data block for the screen, if necessary + if (_vm->_graphicsMan->_backupScreen == NULL) + _vm->_graphicsMan->_backupScreen = _vm->_globals->allocMemory(SCREEN_WIDTH * 2 * SCREEN_HEIGHT); + + // Backup the screen + Common::copy(_vm->_graphicsMan->_backBuffer, _vm->_graphicsMan->_backBuffer + + SCREEN_WIDTH * 2 * SCREEN_HEIGHT, _vm->_graphicsMan->_backupScreen); +} + +/** + * Restore a previously backed up screen + */ +void GraphicsManager::restoreScreen() { + assert(_vm->_graphicsMan->_backupScreen); + + // Restore the screen and free the buffer + Common::copy(_vm->_graphicsMan->_backupScreen, _vm->_graphicsMan->_backupScreen + + SCREEN_WIDTH * 2 * SCREEN_HEIGHT, _vm->_graphicsMan->_backBuffer); + _vm->_globals->freeMemory(_vm->_graphicsMan->_backupScreen); + _backupScreen = NULL; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/graphics.h b/engines/hopkins/graphics.h new file mode 100644 index 0000000000..268db7fc2b --- /dev/null +++ b/engines/hopkins/graphics.h @@ -0,0 +1,193 @@ +/* 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. + * + */ + +#ifndef HOPKINS_GRAPHICS_H +#define HOPKINS_GRAPHICS_H + +#include "common/scummsys.h" +#include "common/array.h" +#include "common/endian.h" +#include "common/rect.h" +#include "common/str.h" +#include "graphics/surface.h" + +namespace Hopkins { + +#define DIRTY_RECTS_SIZE 250 +#define PALETTE_SIZE 256 +#define PALETTE_BLOCK_SIZE (PALETTE_SIZE * 3) +#define PALETTE_EXT_BLOCK_SIZE 800 +static const byte kSetOffset = 251; +static const byte kByteStop = 252; +static const byte k8bVal = 253; +static const byte k16bVal = 254; + +struct RGB8 { + byte r; + byte g; + byte b; +}; + +class HopkinsEngine; + +class GraphicsManager { +private: + HopkinsEngine *_vm; + + int _lockCounter; + bool _initGraphicsFl; + int _screenWidth; + int _screenHeight; + byte *_videoPtr; + int _width; + int _posXClipped, _posYClipped; + bool _clipFl; + int _specialWidth; + + int _enlargedX, _enlargedY; + bool _enlargedXFl, _enlargedYFl; + int _clipX1, _clipY1; + int _reduceX, _reducedY; + int _zoomOutFactor; + + bool _manualScroll; + + void loadScreen(const Common::String &file); + void loadPCX640(byte *surface, const Common::String &file, byte *palette, bool typeFlag); + void loadPCX320(byte *surface, const Common::String &file, byte *palette); + void fadeIn(const byte *palette, int step, const byte *surface); + void fadeOut(const byte *palette, int step, const byte *surface); + void changePalette(const byte *palette); + uint16 mapRGB(byte r, byte g, byte b); + void copy16bFromSurfaceScaleX2(const byte *surface); + + void translateSurface(byte *destP, const byte *srcP, int count, int minThreshold, int maxThreshold); + void displayScaled8BitRect(const byte *surface, int xp, int yp, int width, int height, int destX, int destY); + + void lockScreen(); + void unlockScreen(); +public: + byte _paletteBuffer[PALETTE_SIZE * 2]; + byte _colorTable[PALETTE_EXT_BLOCK_SIZE]; + byte _palette[PALETTE_EXT_BLOCK_SIZE]; + byte _oldPalette[PALETTE_EXT_BLOCK_SIZE]; + byte *_backBuffer; + byte *_frontBuffer; + byte *_screenBuffer; + byte *_backupScreen; + bool _largeScreenFl; + bool _noFadingFl; + bool _fadingFl; + bool _skipVideoLockFl; + int _scrollOffset; + int _scrollPosX; + int _oldScrollPosX; + int _scrollSpeed; + int _lineNbr; + int _lineNbr2; + int _minX, _minY; + int _maxX, _maxY; + int _scrollStatus; + int _fadeDefaultSpeed; + int _screenLineSize; + + /** + * The _dirtyRects list contains paletted game areas that need to be redrawn. + * The _dstrect array is the list of areas of the screen that ScummVM needs to be redrawn. + * Some areas, such as the animation managers, skip the _dirtyRects and use _dstrec directly. + */ + Common::Array<Common::Rect> _dirtyRects; + Common::Array<Common::Rect> _refreshRects; + bool _showDirtyRects; + bool _showZones; + bool _showLines; + + byte *_palettePixels; +public: + GraphicsManager(HopkinsEngine *vm); + ~GraphicsManager(); + + void clearPalette(); + void clearScreen(); + void clearVesaScreen(); + void resetDirtyRects(); + void resetRefreshRects(); + void addDirtyRect(int x1, int y1, int x2, int y2); + void addDirtyRect(const Common::Rect &r) { addDirtyRect(r.left, r.top, r.right, r.bottom); } + void addRefreshRect(int x1, int y1, int x2, int y2); + void addRectToArray(Common::Array<Common::Rect> &rects, const Common::Rect &newRect); + void displayDirtyRects(); + void displayRefreshRects(); + void displayZones(); + void displayLines(); + void displayDebugRect(Graphics::Surface *surface, const Common::Rect &srcRect, uint32 color = 0xffffff); + void copySurface(const byte *surface, int x1, int y1, int width, int height, byte *destSurface, int destX, int destY); + void loadImage(const Common::String &file); + void loadVgaImage(const Common::String &file); + void fadeInLong(); + void fadeInBreakout(); + void fadeInDefaultLength(const byte *surface); + void fadeInShort(); + void fadeOutDefaultLength(const byte *surface); + void fadeOutBreakout(); + void fadeOutLong(); + void fadeOutShort(); + void copyWinscanVbe3(const byte *srcData, byte *destSurface); + void copyWinscanVbe(const byte *srcP, byte *destP); + void copyVideoVbe16(const byte *srcData); + void copyVideoVbe16a(const byte *srcData); + void copySurfaceRect(const byte *srcSurface, byte *destSurface, int xs, int ys, int width, int height); + void restoreSurfaceRect(byte *destSurface, const byte *src, int xp, int yp, int width, int height); + void displayFont(byte *surface, const byte *spriteData, int xp, int yp, int characterIndex, int color); + void drawHorizontalLine(byte *surface, int xp, int yp, uint16 width, byte col); + void drawVerticalLine(byte *surface, int xp, int yp, int height, byte col); + void initColorTable(int minIndex, int maxIndex, byte *palette); + void setGraphicalMode(int width, int height); + void setPaletteVGA256(const byte *palette); + void setPaletteVGA256WithRefresh(const byte *palette, const byte *surface); + void scrollScreen(int amount); + int zoomIn(int v, int percentage); + int zoomOut(int v, int percentage); + void initScreen(const Common::String &file, int mode, bool initializeScreen); + void displayAllBob(); + void endDisplayBob(); + void updateScreen(); + void reduceScreenPart(const byte *srcSruface, byte *destSurface, int xp, int yp, int width, int height, int zoom); + void setScreenWidth(int pitch); + + void setColorPercentage(int palIndex, int r, int g, int b); + void setColorPercentage2(int palIndex, int r, int g, int b); + void fastDisplay(const byte *spriteData, int xp, int yp, int spriteIndex, bool addSegment = true); + void fastDisplay2(const byte *objectData, int xp, int yp, int idx, bool addSegment = true); + void drawCompressedSprite(byte *surface, const byte *srcData, int xp300, int yp300, int frameIndex, int zoom1, int zoom2, bool flipFl); + void copyRect(const byte *srcSurface, int x1, int y1, uint16 width, int height, byte *destSurface, int destX, int destY); + void drawVesaSprite(byte *surface, const byte *spriteData, int xp, int yp, int spriteIndex); + void display8BitRect(const byte *surface, int xs, int ys, int width, int height, int destX, int destY); + void fillSurface(byte *surface, byte *col, int size); + void displayScreen(bool initPalette); + void backupScreen(); + void restoreScreen(); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_GRAPHICS_H */ diff --git a/engines/hopkins/hopkins.cpp b/engines/hopkins/hopkins.cpp new file mode 100644 index 0000000000..b773808c50 --- /dev/null +++ b/engines/hopkins/hopkins.cpp @@ -0,0 +1,2903 @@ +/* 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 "hopkins/hopkins.h" +#include "hopkins/graphics.h" +#include "hopkins/files.h" +#include "hopkins/saveload.h" +#include "hopkins/sound.h" +#include "hopkins/talk.h" + +#include "common/scummsys.h" +#include "common/config-manager.h" +#include "common/debug-channels.h" +#include "common/events.h" +#include "common/file.h" + +namespace Hopkins { + +HopkinsEngine::HopkinsEngine(OSystem *syst, const HopkinsGameDescription *gameDesc) : Engine(syst), + _gameDescription(gameDesc), _randomSource("Hopkins") { + DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level"); + _animMan = new AnimationManager(this); + _computer = new ComputerManager(this); + _dialog = new DialogsManager(this); + _debug = new Debugger(this); + _events = new EventsManager(this); + _fileIO = new FileManager(this); + _fontMan = new FontManager(this); + _globals = new Globals(this); + _graphicsMan = new GraphicsManager(this); + _linesMan = new LinesManager(this); + _menuMan = new MenuManager(this); + _objectsMan = new ObjectsManager(this); + _saveLoad = new SaveLoadManager(this); + _script = new ScriptManager(this); + _soundMan = new SoundManager(this); + _talkMan = new TalkManager(this); + + _startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1; +} + +HopkinsEngine::~HopkinsEngine() { + delete _talkMan; + delete _soundMan; + delete _script; + delete _saveLoad; + delete _objectsMan; + delete _menuMan; + delete _linesMan; + delete _graphicsMan; + delete _globals; + delete _fontMan; + delete _fileIO; + delete _events; + delete _debug; + delete _dialog; + delete _computer; + delete _animMan; +} + +Common::String HopkinsEngine::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); +} + +/** + * Returns true if it is currently okay to restore a game + */ +bool HopkinsEngine::canLoadGameStateCurrently() { + return !_globals->_exitId && !_globals->_cityMapEnabledFl && _events->_mouseFl && _globals->_curRoomNum != 0; +} + +/** + * Returns true if it is currently okay to save the game + */ +bool HopkinsEngine::canSaveGameStateCurrently() { + return !_globals->_exitId && !_globals->_cityMapEnabledFl && _events->_mouseFl + && _globals->_curRoomNum != 0 && !isUnderwaterSubScene(); +} + +/** + * Load the savegame at the specified slot index + */ +Common::Error HopkinsEngine::loadGameState(int slot) { + return _saveLoad->loadGame(slot); +} + +/** + * Save the game to the given slot index, and with the given name + */ +Common::Error HopkinsEngine::saveGameState(int slot, const Common::String &desc) { + return _saveLoad->saveGame(slot, desc); +} + +Common::Error HopkinsEngine::run() { + _saveLoad->initSaves(); + + _globals->setConfig(); + _fileIO->initCensorship(); + initializeSystem(); + + if (!getIsDemo()) + runFull(); + else if (getPlatform() == Common::kPlatformLinux) + runLinuxDemo(); + else if (getPlatform() == Common::kPlatformWindows) + runWin95Demo(); + else { + warning("Unhandled version, switching to Linux demo. Please report this version to ScummVM developers"); + runLinuxDemo(); + } + + return Common::kNoError; +} + +bool HopkinsEngine::runWin95Demo() { + _objectsMan->loadObjects(); + _objectsMan->changeObject(14); + _objectsMan->addObject(14); + _objectsMan->_helicopterFl = false; + + _globals->_eventMode = EVENTMODE_IGNORE; + + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + + if (_startGameSlot == -1) { + _graphicsMan->loadImage("H2"); + _graphicsMan->fadeInLong(); + + if (!_events->_escKeyFl) + playIntro(); + } + + _events->_rateCounter = 0; + _globals->_eventMode = EVENTMODE_IGNORE; + _globals->_speed = 1; + _events->delay(500); + _globals->_eventMode = EVENTMODE_DEFAULT; + if (_events->_rateCounter > 475) + _globals->_speed = 2; + if (_events->_rateCounter > 700) + _globals->_speed = 3; + + if (_startGameSlot == -1) + _graphicsMan->fadeOutShort(); + + _globals->_eventMode = EVENTMODE_IGNORE; + _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR"); + + _globals->_characterType = CHARACTER_HOPKINS; + _objectsMan->_mapCarPosX = _objectsMan->_mapCarPosY = 0; + memset(_globals->_saveData, 0, 2000); + _globals->_exitId = 0; + + if (getLanguage() != Common::PL_POL) + if (!displayAdultDisclaimer()) + return Common::kNoError; + + if (_startGameSlot != -1) + _saveLoad->loadGame(_startGameSlot); + + for (;;) { + if (_globals->_exitId == 300) + _globals->_exitId = 0; + + if (!_globals->_exitId) { + _globals->_exitId = _menuMan->menu(); + if (_globals->_exitId == -1) { + _globals->_characterSpriteBuf = _globals->freeMemory(_globals->_characterSpriteBuf); + restoreSystem(); + return false; + } + } + + if (shouldQuit()) + return false; + + _globals->_curRoomNum = _globals->_exitId; + + switch (_globals->_exitId) { + case 1: + // Handles room: Apartment + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM01", "IM01", "ANIM01", "IM01", 2, true); + break; + + case 3: + // - Displays bank attack when leaving the apartment + // - Handles room: bottom of the apartment + if (!_globals->_saveData->_data[svBankAttackAnimPlayedFl]) { + _soundMan->playSound(3); + if (getPlatform() == Common::kPlatformOS2 || getPlatform() == Common::kPlatformBeOS) + _graphicsMan->loadImage("fond"); + else { + if (_globals->_language == LANG_FR) + _graphicsMan->loadImage("fondfr"); + else if (_globals->_language == LANG_EN) + _graphicsMan->loadImage("fondan"); + else if (_globals->_language == LANG_SP) + _graphicsMan->loadImage("fondes"); + } + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->_specialSoundNum = 2; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + if (!_globals->_censorshipFl) + _animMan->playAnim("BANQUE.ANM", "BANKUK.ANM", 200, 28, 200); + else + _animMan->playAnim("BANQUE.ANM", "BANKUK.ANM", 200, 28, 200); + _soundMan->_specialSoundNum = 0; + _soundMan->removeSample(1); + _soundMan->removeSample(2); + _soundMan->removeSample(3); + _soundMan->removeSample(4); + _graphicsMan->fadeOutShort(); + _globals->_saveData->_data[svBankAttackAnimPlayedFl] = 1; + } + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM03", "IM03", "ANIM03", "IM03", 2, false); + break; + + case 4: + // Handle room: City map + _globals->_disableInventFl = true; + _objectsMan->handleCityMap(); + _globals->_disableInventFl = false; + break; + + case 5: + // Handle room: Outside the bank + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 455; + + if (_globals->_saveData->_data[svFreedHostageFl]) { + if (_globals->_saveData->_data[svFreedHostageFl] == 1) + _objectsMan->sceneControl2("IM05", "IM05A", "ANIM05B", "IM05", 3, false); + } else { + _objectsMan->sceneControl2("IM05", "IM05", "ANIM05", "IM05", 3, false); + } + break; + + case 6: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 460; + _objectsMan->sceneControl2("IM06", "IM06", "ANIM06", "IM06", 2, true); + break; + + case 7: + if (_globals->_saveData->_data[svBombBoxOpenedFl]) + _objectsMan->sceneControl("BOMBEB", "BOMBE", "BOMBE", "BOMBE", 2, true); + else + _objectsMan->sceneControl("BOMBEA", "BOMBE", "BOMBE", "BOMBE", 2, true); + break; + + case 8: + _linesMan->setMaxLineIdx(15); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM08", "IM08", "ANIM08", "IM08", 2, true); + break; + + case 9: + _globals->_characterMaxPosY = 440; + _linesMan->setMaxLineIdx(20); + if (_globals->_saveData->_data[svBombDisarmedFl]) + _objectsMan->sceneControl2("IM09", "IM09", "ANIM09", "IM09", 10, true); + else + bombExplosion(); + break; + + case 10: + _objectsMan->sceneControl("IM10", "IM10", "ANIM10", "IM10", 9, false); + break; + + case 11: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM11", "IM11", "ANIM11", "IM11", 2, false); + break; + + case 12: + _globals->_characterMaxPosY = 450; + _linesMan->setMaxLineIdx(20); + if (_globals->_saveData->_data[svBombDisarmedFl]) { + if (_globals->_language == LANG_FR) + _graphicsMan->loadImage("ENDFR"); + else + _graphicsMan->loadImage("ENDUK"); + _graphicsMan->fadeInLong(); + _events->mouseOn(); + do + _events->refreshScreenAndEvents(); + while (_events->getMouseButton() != 1); + _graphicsMan->fadeOutLong(); + restoreSystem(); + } else + bombExplosion(); + break; + + case 13: + case 14: + case 15: + handleNotAvailable(11); + break; + + case 16: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + case 33: + case 32: + case 34: + handleNotAvailable(4); + break; + + case 17: + handleNotAvailable(1); + break; + + case 111: + _objectsMan->sceneControl("IM111", "IM111", "ANIM111", "IM111", 10, false); + break; + + case 112: + _objectsMan->sceneControl("IM112", "IM112", "ANIM112", "IM112", 10, false); + break; + + case 113: + _globals->_exitId = 0; + _globals->_prevScreenId = _globals->_screenId; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_screenId; + _globals->_screenId = 113; + _globals->_saveData->_data[svLastScreenId] = _globals->_screenId; + _computer->showComputer(COMPUTER_HOPKINS); + _graphicsMan->clearScreen(); + _graphicsMan->updateScreen(); + memset(_graphicsMan->_frontBuffer, 0, 307200); + memset(_graphicsMan->_backBuffer, 0, 307200); + _graphicsMan->clearPalette(); + _graphicsMan->resetDirtyRects(); + break; + + case 114: + _globals->_prevScreenId = _globals->_screenId; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_screenId; + _globals->_screenId = 114; + _globals->_saveData->_data[svLastScreenId] = _globals->_screenId; + _globals->_exitId = 0; + _computer->showComputer(COMPUTER_SAMANTHA); + _graphicsMan->clearScreen(); + break; + + case 115: + _globals->_exitId = 0; + _globals->_prevScreenId = _globals->_screenId; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_screenId; + _globals->_screenId = 115; + _globals->_saveData->_data[svLastScreenId] = _globals->_screenId; + _computer->showComputer(COMPUTER_PUBLIC); + _graphicsMan->clearScreen(); + break; + + case 150: + _soundMan->playSound(28); + _globals->_eventMode = EVENTMODE_ALT; // CHECKME! + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _animMan->playAnim("JOUR1A.ANM", "JOUR1A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + + case 151: + if (_fileIO->fileExists("JOUR3A.ANM")) { + // The Polish demo uses the animation file than the complete versions + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR3A.ANM", "JOUR3A.ANM", 12, 12, 2000); + } else { + // The other demos only display a nag screen + _soundMan->playSound(28); + _globals->_eventMode = EVENTMODE_ALT; // CHECKME! + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _graphicsMan->loadImage("njour3a"); + _graphicsMan->fadeInLong(); + _events->delay(5000); + _graphicsMan->fadeOutLong(); + } + + _globals->_exitId = 300; + _globals->_eventMode = EVENTMODE_DEFAULT; + break; + + case 152: + _soundMan->playSound(28); + _globals->_eventMode = EVENTMODE_ALT; // CHECKME! + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _animMan->playAnim("JOUR4A.ANM", "JOUR4A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + } + } + return true; +} + +bool HopkinsEngine::runLinuxDemo() { + _objectsMan->loadObjects(); + _objectsMan->changeObject(14); + _objectsMan->addObject(14); + _objectsMan->_helicopterFl = false; + + _events->mouseOff(); + + _graphicsMan->clearScreen(); + + if (_startGameSlot == -1) { + _graphicsMan->loadImage("LINUX"); + _graphicsMan->fadeInLong(); + _events->delay(1500); + _graphicsMan->fadeOutLong(); + + _graphicsMan->loadImage("H2"); + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + + if (!_events->_escKeyFl) + playIntro(); + } + + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR"); + _globals->_characterType = CHARACTER_HOPKINS; + _objectsMan->_mapCarPosX = _objectsMan->_mapCarPosY = 0; + memset(_globals->_saveData, 0, 2000); + _globals->_exitId = 0; + + if (_startGameSlot != -1) + _saveLoad->loadGame(_startGameSlot); + + for (;;) { + if (_globals->_exitId == 300) + _globals->_exitId = 0; + + if (!_globals->_exitId) { + _globals->_exitId = _menuMan->menu(); + if (_globals->_exitId == -1) { + if (!shouldQuit()) + endLinuxDemo(); + _globals->_characterSpriteBuf = _globals->freeMemory(_globals->_characterSpriteBuf); + restoreSystem(); + } + } + + if (shouldQuit()) + return false; + + _globals->_curRoomNum = _globals->_exitId; + + switch (_globals->_exitId) { + case 17: + case 18: + case 19: + case 20: + case 22: + case 23: + case 24: + case 27: + case 28: + case 29: + case 30: + case 31: + case 32: + case 34: + case 38: + displayNotAvailable(); + break; + + case 1: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM01", "IM01", "ANIM01", "IM01", 1, true); + break; + + case 3: + if (!_globals->_saveData->_data[svBankAttackAnimPlayedFl]) { + _soundMan->playSound(3); + if (getPlatform() == Common::kPlatformOS2 || getPlatform() == Common::kPlatformBeOS) + _graphicsMan->loadImage("fond"); + else { + if (_globals->_language == LANG_FR) + _graphicsMan->loadImage("fondfr"); + else if (_globals->_language == LANG_EN) + _graphicsMan->loadImage("fondan"); + else if (_globals->_language == LANG_SP) + _graphicsMan->loadImage("fondes"); + } + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->_specialSoundNum = 2; + + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _graphicsMan->_fadingFl = true; + + if (!_globals->_censorshipFl) + _animMan->playAnim("BANQUE.ANM", "BANKUK.ANM", 200, 28, 200); + else + _animMan->playAnim("BANKUK.ANM", "BANQUE.ANM", 200, 28, 200); + _soundMan->_specialSoundNum = 0; + _soundMan->removeSample(1); + _soundMan->removeSample(2); + _soundMan->removeSample(3); + _soundMan->removeSample(4); + _globals->_saveData->_data[svBankAttackAnimPlayedFl] = 1; + } + + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM03", "IM03", "ANIM03", "IM03", 2, false); + break; + + case 4: + _globals->_disableInventFl = true; + _objectsMan->handleCityMap(); + _globals->_disableInventFl = false; + break; + + case 5: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 455; + if (_globals->_saveData->_data[svFreedHostageFl] == 1) + _objectsMan->sceneControl2("IM05", "IM05A", "ANIM05B", "IM05", 3, false); + else + _objectsMan->sceneControl2("IM05", "IM05", "ANIM05", "IM05", 3, false); + break; + + case 6: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 460; + _objectsMan->sceneControl2("IM06", "IM06", "ANIM06", "IM06", 2, true); + break; + + case 7: + if (_globals->_saveData->_data[svBombBoxOpenedFl]) + _objectsMan->sceneControl("BOMBEB", "BOMBE", "BOMBE", "BOMBE", 2, true); + else + _objectsMan->sceneControl("BOMBEA", "BOMBE", "BOMBE", "BOMBE", 2, true); + break; + + case 8: + _linesMan->setMaxLineIdx(15); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM08", "IM08", "ANIM08", "IM08", 2, true); + break; + + case 9: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 440; + + if (!_globals->_saveData->_data[svBombDisarmedFl]) + bombExplosion(); + else + _objectsMan->sceneControl2("IM09", "IM09", "ANIM09", "IM09", 10, true); + break; + + case 10: + _objectsMan->sceneControl("IM10", "IM10", "ANIM10", "IM10", 9, false); + break; + + case 11: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM11", "IM11", "ANIM11", "IM11", 2, false); + break; + + case 12: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 450; + if (_globals->_saveData->_data[svBombDisarmedFl]) + _objectsMan->sceneControl2("IM12", "IM12", "ANIM12", "IM12", 1, false); + else + bombExplosion(); + break; + + case 13: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM13", "IM13", "ANIM13", "IM13", 1, true); + break; + + case 14: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM14", "IM14", "ANIM14", "IM14", 1, true); + break; + + case 15: + _objectsMan->sceneControl("IM15", "IM15", "ANIM15", "IM15", 29, false); + break; + + case 16: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + + if (_globals->_saveData->_data[svForestAvailableFl] == 1) { + _objectsMan->sceneControl2("IM16", "IM16A", "ANIM16", "IM16", 7, true); + } else if (!_globals->_saveData->_data[svForestAvailableFl]) { + _objectsMan->sceneControl2("IM16", "IM16", "ANIM16", "IM16", 7, true); + } + break; + + case 25: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM25", "IM25", "ANIM25", "IM25", 30, true); + break; + + case 26: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM26", "IM26", "ANIM26", "IM26", 30, true); + + case 33: + _objectsMan->sceneControl("IM33", "IM33", "ANIM33", "IM33", 8, false); + break; + + case 35: + displayEndDemo(); + break; + + case 111: + _objectsMan->sceneControl("IM111", "IM111", "ANIM111", "IM111", 10, false); + break; + + case 112: + _objectsMan->sceneControl("IM112", "IM112", "ANIM112", "IM112", 10, false); + break; + + case 113: + _globals->_exitId = 0; + _globals->_prevScreenId = _globals->_screenId; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_screenId; + _globals->_screenId = 113; + _globals->_saveData->_data[svLastScreenId] = 113; + _computer->showComputer(COMPUTER_HOPKINS); + + _graphicsMan->clearScreen(); + _graphicsMan->updateScreen(); + memset(_graphicsMan->_frontBuffer, 0, 307200); + memset(_graphicsMan->_backBuffer, 0, 307200); + _graphicsMan->clearPalette(); + _graphicsMan->resetDirtyRects(); + break; + + case 114: + _globals->_exitId = 0; + _globals->_prevScreenId = _globals->_screenId; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_screenId; + _globals->_screenId = 114; + _globals->_saveData->_data[svLastScreenId] = 114; + _computer->showComputer(COMPUTER_SAMANTHA); + _graphicsMan->clearScreen(); + break; + + case 115: + _globals->_exitId = 0; + _globals->_prevScreenId = _globals->_screenId; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_screenId; + _globals->_screenId = 115; + _globals->_saveData->_data[svLastScreenId] = 115; + _computer->showComputer(COMPUTER_PUBLIC); + _graphicsMan->clearScreen(); + break; + + case 150: + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR1A.ANM", "JOUR1A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + + case 151: + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR3A.ANM", "JOUR3A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + + case 152: + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR4A.ANM", "JOUR4A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + } + } + return true; +} + +bool HopkinsEngine::runFull() { + if (_startGameSlot == -1 && getPlatform() == Common::kPlatformLinux) + _soundMan->playSound(16); + + _objectsMan->loadObjects(); + _objectsMan->changeObject(14); + _objectsMan->addObject(14); + + if (getPlatform() == Common::kPlatformLinux) { + _objectsMan->_helicopterFl = false; + _events->mouseOff(); + // No code has been added to display the version as it's wrong + // in my copy: it mentions a Win95 version v4 using DirectDraw (Strangerke) + } else if (getPlatform() == Common::kPlatformWindows) { + _objectsMan->_helicopterFl = false; + _globals->_eventMode = EVENTMODE_IGNORE; + // This code displays the game version. + // It wasn't present in the original and could be put in the debugger + // It has been added there for debug purposes + if (_startGameSlot == -1) { + _graphicsMan->loadImage("VERSW"); + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + } + _graphicsMan->clearVesaScreen(); + } else { + // This piece of code, though named "display_version" in the original, + // displays a "loading please wait" screen. + if (_startGameSlot == -1) { + _graphicsMan->loadImage("VERSW"); + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + } + _graphicsMan->clearVesaScreen(); + + _globals->_eventMode = EVENTMODE_IGNORE; + } + + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + + if (_startGameSlot == -1) { + if (getPlatform() == Common::kPlatformLinux) { + _graphicsMan->loadImage("H2"); + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + _globals->_speed = 2; + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->_fadingFl = true; + _animMan->playAnim("MP.ANM", "MP.ANM", 10, 16, 200); + } else { + _animMan->playAnim("MP.ANM", "MP.ANM", 10, 16, 200); + _graphicsMan->fadeOutLong(); + } + } + + _events->mouseOff(); + + if (!_events->_escKeyFl && _startGameSlot == -1) { + playIntro(); + if (shouldQuit()) + return false; + } + + if (getPlatform() != Common::kPlatformLinux && _startGameSlot == -1) { + _graphicsMan->fadeOutShort(); + _graphicsMan->loadImage("H2"); + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + } + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR"); + _globals->_characterType = CHARACTER_HOPKINS; + _objectsMan->_mapCarPosX = _objectsMan->_mapCarPosY = 0; + memset(_globals->_saveData, 0, 2000); + + _globals->_exitId = 0; + + + if (_startGameSlot != -1) { + _soundMan->playSound(28); + _saveLoad->loadGame(_startGameSlot); + } + + for (;;) { + if (_globals->_exitId == 300) + _globals->_exitId = 0; + if (!_globals->_exitId) { + _globals->_exitId = _menuMan->menu(); + if (_globals->_exitId == -1) { + _globals->_characterSpriteBuf = _globals->freeMemory(_globals->_characterSpriteBuf); + restoreSystem(); + return false; + } + } + + if (shouldQuit()) + return false; + + _globals->_curRoomNum = _globals->_exitId; + + switch (_globals->_exitId) { + case 1: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM01", "IM01", "ANIM01", "IM01", 1, true); + break; + + case 3: + if (!_globals->_saveData->_data[svBankAttackAnimPlayedFl]) { + // Play the bank attack animation + _soundMan->playSound(3); + if (getPlatform() == Common::kPlatformOS2 || getPlatform() == Common::kPlatformBeOS) + _graphicsMan->loadImage("fond"); + else { + if (_globals->_language == LANG_FR) + _graphicsMan->loadImage("fondfr"); + else if (_globals->_language == LANG_EN) + _graphicsMan->loadImage("fondan"); + else if (_globals->_language == LANG_SP) + _graphicsMan->loadImage("fondes"); + } + _graphicsMan->fadeInLong(); + _events->delay(500); + _graphicsMan->fadeOutLong(); + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->_specialSoundNum = 2; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + if (getPlatform() == Common::kPlatformLinux || getPlatform() == Common::kPlatformWindows) { + if (getPlatform() == Common::kPlatformLinux) + _graphicsMan->_fadingFl = true; + + if (!_globals->_censorshipFl) + _animMan->playAnim("BANQUE.ANM", "BANKUK.ANM", 200, 28, 200); + else + _animMan->playAnim("BANKUK.ANM", "BANQUE.ANM", 200, 28, 200); + } else { + _animMan->playAnim("BANQUE.ANM", "BANKUK.ANM", 200, 28, 200); + } + + _soundMan->_specialSoundNum = 0; + _soundMan->removeSample(1); + _soundMan->removeSample(2); + _soundMan->removeSample(3); + _soundMan->removeSample(4); + + if (getPlatform() != Common::kPlatformLinux) { + // Copy the end of the animation into the secondary buffer and fade out the screen + Common::fill(_graphicsMan->_frontBuffer, _graphicsMan->_frontBuffer + + SCREEN_WIDTH * 2 * SCREEN_HEIGHT, 0); + _graphicsMan->fadeOutLong(); + } + + _globals->_saveData->_data[svBankAttackAnimPlayedFl] = 1; + } + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM03", "IM03", "ANIM03", "IM03", 2, false); + break; + + case 4: + _globals->_disableInventFl = true; + _objectsMan->handleCityMap(); + _globals->_disableInventFl = false; + break; + + case 5: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 455; + if (_globals->_saveData->_data[svFreedHostageFl] == 1) + _objectsMan->sceneControl2("IM05", "IM05A", "ANIM05B", "IM05", 3, false); + else + _objectsMan->sceneControl2("IM05", "IM05", "ANIM05", "IM05", 3, false); + break; + + case 6: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 460; + _objectsMan->sceneControl2("IM06", "IM06", "ANIM06", "IM06", 2, true); + break; + + case 7: + if (_globals->_saveData->_data[svBombBoxOpenedFl]) + _objectsMan->sceneControl("BOMBEB", "BOMBE", "BOMBE", "BOMBE", 2, true); + else + _objectsMan->sceneControl("BOMBEA", "BOMBE", "BOMBE", "BOMBE", 2, true); + break; + + case 8: + _linesMan->setMaxLineIdx(15); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM08", "IM08", "ANIM08", "IM08", 2, true); + break; + + case 9: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 440; + if (_globals->_saveData->_data[svBombDisarmedFl]) + _objectsMan->sceneControl2("IM09", "IM09", "ANIM09", "IM09", 10, true); + else + bombExplosion(); + break; + + case 10: + _objectsMan->sceneControl("IM10", "IM10", "ANIM10", "IM10", 9, false); + break; + + case 11: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 450; + _objectsMan->sceneControl2("IM11", "IM11", "ANIM11", "IM11", 2, false); + break; + + case 12: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 450; + if (_globals->_saveData->_data[svBombDisarmedFl]) + _objectsMan->sceneControl2("IM12", "IM12", "ANIM12", "IM12", 1, false); + else + bombExplosion(); + break; + + case 13: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM13", "IM13", "ANIM13", "IM13", 1, true); + break; + + case 14: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM14", "IM14", "ANIM14", "IM14", 1, true); + break; + + case 15: + if (getPlatform() == Common::kPlatformLinux || getPlatform() == Common::kPlatformWindows) + _objectsMan->sceneControl("IM15", "IM15", "ANIM15", "IM15", 29, false); + else + _objectsMan->sceneControl("IM15", "IM15", "ANIM15", "IM15", 18, false); + break; + + case 16: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + if (_globals->_saveData->_data[svForestAvailableFl] == 1) + _objectsMan->sceneControl2("IM16", "IM16A", "ANIM16", "IM16", 7, true); + else + _objectsMan->sceneControl2("IM16", "IM16", "ANIM16", "IM16", 7, true); + break; + + case 17: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 440; + if (_globals->_saveData->_data[svHutBurningFl] == 1) + _objectsMan->sceneControl2("IM17", "IM17A", "ANIM17", "IM17", 11, true); + else if (!_globals->_saveData->_data[svHutBurningFl]) + _objectsMan->sceneControl2("IM17", "IM17", "ANIM17", "IM17", 11, true); + if (_globals->_exitId == 18) { + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _soundMan->stopSound(); + if (getPlatform() == Common::kPlatformLinux) { + _soundMan->playSound(29); + _graphicsMan->_fadingFl = true; + _animMan->playAnim("PURG1A.ANM", "PURG1.ANM", 12, 18, 50); + } else if (getPlatform() == Common::kPlatformWindows) { + _soundMan->playSound(29); + _animMan->playAnim("PURG1A.ANM", "PURG1.ANM", 12, 18, 50); + _graphicsMan->fadeOutShort(); + } else { + _soundMan->playSound(6); + _animMan->playAnim("PURG1A.ANM", "PURG1.ANM", 12, 18, 50); + _graphicsMan->fadeOutShort(); + } + _globals->_eventMode = EVENTMODE_DEFAULT; + } + break; + + case 18: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + if (getPlatform() == Common::kPlatformLinux || getPlatform() == Common::kPlatformWindows) + _objectsMan->sceneControl2("IM18", "IM18", "ANIM18", "IM18", 29, false); + else + _objectsMan->sceneControl2("IM18", "IM18", "ANIM18", "IM18", 6, false); + break; + + case 19: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 440; + if (_globals->_saveData->_data[svHeavenGuardGoneFl]) + _objectsMan->sceneControl2("IM19", "IM19A", "ANIM19", "IM19", 6, true); + else + _objectsMan->sceneControl2("IM19", "IM19", "ANIM19", "IM19", 6, true); + break; + + case 20: + _linesMan->setMaxLineIdx(10); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM20", "IM20", "ANIM20", "IM20", 6, true); + if (_globals->_exitId == 17) { + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->stopSound(); + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _soundMan->playSound(6); + if (getPlatform() == Common::kPlatformLinux) + _graphicsMan->_fadingFl = true; + _animMan->playAnim("PURG2A.ANM", "PURG2.ANM", 12, 18, 50); + if (getPlatform() != Common::kPlatformLinux) + _graphicsMan->fadeOutShort(); + _globals->_eventMode = EVENTMODE_DEFAULT; + } + break; + + case 22: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM22", "IM22", "ANIM22", "IM22", 6, true); + break; + + case 23: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM23", "IM23", "ANIM23", "IM23", 6, true); + break; + + case 24: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + if (_globals->_saveData->_data[svCinemaDogGoneFl] == 1) + _objectsMan->sceneControl2("IM24", "IM24A", "ANIM24", "IM24", 1, true); + else + _objectsMan->sceneControl2("IM24", "IM24", "ANIM24", "IM24", 1, true); + break; + + case 25: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 445; + if (getPlatform() == Common::kPlatformLinux || getPlatform() == Common::kPlatformWindows) + _objectsMan->sceneControl2("IM25", "IM25", "ANIM25", "IM25", 30, true); + else + _objectsMan->sceneControl2("IM25", "IM25", "ANIM25", "IM25", 8, true); + break; + + case 26: + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 435; + if (getPlatform() == Common::kPlatformLinux || getPlatform() == Common::kPlatformWindows) + _objectsMan->sceneControl2("IM26", "IM26", "ANIM26", "IM26", 30, true); + else + _objectsMan->sceneControl2("IM26", "IM26", "ANIM26", "IM26", 8, true); + break; + + case 27: + _linesMan->setMaxLineIdx(15); + _globals->_characterMaxPosY = 440; + if (_globals->_saveData->_data[svPoolDogGoneFl] == 1) + _objectsMan->sceneControl2("IM27", "IM27A", "ANIM27", "IM27", 27, true); + else + _objectsMan->sceneControl2("IM27", "IM27", "ANIM27", "IM27", 27, true); + break; + + case 28: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 450; + if (_globals->_saveData->_data[svCinemaCurtainCond1] != 1 || _globals->_saveData->_data[svCinemaCurtainCond2] != 1) + _objectsMan->sceneControl2("IM28", "IM28", "ANIM28", "IM28", 1, false); + else + _objectsMan->sceneControl2("IM28A", "IM28", "ANIM28", "IM28", 1, false); + break; + + case 29: + _linesMan->setMaxLineIdx(50); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM29", "IM29", "ANIM29", "IM29", 1, true); + break; + + case 30: + // Shooting + _linesMan->setMaxLineIdx(15); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM30", "IM30", "ANIM30", "IM30", 24, false); + break; + + case 31: + // Shooting target + _objectsMan->sceneControl("IM31", "IM31", "ANIM31", "IM31", 10, true); + break; + + case 32: + _linesMan->setMaxLineIdx(20); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM32", "IM32", "ANIM32", "IM32", 2, true); + break; + + case 33: + _objectsMan->sceneControl("IM33", "IM33", "ANIM33", "IM33", 8, false); + break; + + case 34: + // In the airport, before the flight cut-scene + _objectsMan->sceneControl("IM34", "IM34", "ANIM34", "IM34", 2, false); + break; + + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: { + _linesMan->setMaxLineIdx(40); + _globals->_characterMaxPosY = 435; + _globals->_disableInventFl = false; + _objectsMan->_forestFl = true; + Common::String im = Common::String::format("IM%d", _globals->_exitId); + _soundMan->playSound(13); + if (_objectsMan->_forestSprite == NULL) { + _objectsMan->_forestSprite = _objectsMan->loadSprite("HOPDEG.SPR"); + _soundMan->loadSample(1, "SOUND41.WAV"); + } + _objectsMan->sceneControl2(im, im, "BANDIT", im, 13, false); + if (_globals->_exitId < 35 || _globals->_exitId > 49) { + _objectsMan->_forestSprite = _globals->freeMemory(_objectsMan->_forestSprite); + _objectsMan->_forestFl = false; + _soundMan->removeSample(1); + } + break; + } + + case 50: + // Flight cut scene + playPlaneCutscene(); + _globals->_exitId = 51; + break; + + case 51: + _linesMan->setMaxLineIdx(10); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM51", "IM51", "ANIM51", "IM51", 14, true); + break; + + case 52: + _linesMan->setMaxLineIdx(15); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM52", "IM52", "ANIM52", "IM52", 14, true); + break; + + case 54: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM54", "IM54", "ANIM54", "IM54", 14, true); + break; + + case 55: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 460; + _objectsMan->sceneControl2("IM55", "IM55", "ANIM55", "IM55", 14, false); + break; + + case 56: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM56", "IM56", "ANIM56", "IM56", 14, false); + break; + + case 57: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM57", "IM57", "ANIM57", "IM57", 14, true); + break; + + case 58: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM58", "IM58", "ANIM58", "IM58", 14, false); + break; + + case 59: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM59", "IM59", "ANIM59", "IM59", 21, false); + break; + + case 60: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM60", "IM60", "ANIM60", "IM60", 21, false); + break; + + case 61: + if (_globals->_saveData->_data[svBaseElevatorCond1] == 1 && !_globals->_saveData->_data[svBaseFireFl]) + handleConflagration(); + _objectsMan->sceneControl("IM61", "IM61", "ANIM61", "IM61", 21, false); + break; + + case 62: + _linesMan->setMaxLineIdx(8); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM62", "IM62", NULL, "IM62", 21, false); + break; + + case 63: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM63", "IM63", "ANIM63", "IM63", 21, false); + break; + + case 64: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM64", "IM64", "ANIM64", "IM64", 21, true); + break; + + case 65: + _linesMan->setMaxLineIdx(30); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM65", "IM65", "ANIM65", "IM65", 21, false); + break; + + case 66: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM66", "IM66", "ANIM66", "IM66", 21, false); + break; + + case 67: + _linesMan->setMaxLineIdx(8); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM67", "IM67", NULL, "IM67", 21, false); + break; + + case 68: + _linesMan->setMaxLineIdx(8); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM68", "IM68", "ANIM68", "IM68", 21, true); + break; + + case 69: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM69", "IM69", "ANIM69", "IM69", 21, false); + break; + + case 70: + _linesMan->setMaxLineIdx(8); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM70", "IM70", NULL, "IM70", 21, false); + break; + + case 71: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 445; + _objectsMan->sceneControl2("IM71", "IM71", "ANIM71", "IM71", 21, false); + break; + + case 73: + _linesMan->setMaxLineIdx(15); + _globals->_characterMaxPosY = 445; + if (_globals->_saveData->_data[svSecondElevatorAvailableFl] == 1) + _objectsMan->sceneControl2("IM73", "IM73A", "ANIM73", "IM73", 21, true); + else + _objectsMan->sceneControl2("IM73", "IM73", "ANIM73", "IM73", 21, true); + break; + + case 75: + playSubmarineCutscene(); + break; + + case 77: + handleOceanMaze(77, "OCEAN01", DIR_RIGHT, 0, 84, 0, 0, 25); + break; + + case 78: + handleOceanMaze(78, "OCEAN02", DIR_UP, 0, 91, 84, 0, 25); + break; + + case 79: + handleOceanMaze(79, "OCEAN03", DIR_LEFT, 87, 0, 0, 83, 25); + break; + + case 80: + handleOceanMaze(80, "OCEAN04", DIR_UP, 86, 88, 0, 81, 25); + break; + + case 81: + handleOceanMaze(81, "OCEAN05", DIR_UP, 91, 82, 80, 85, 25); + break; + + case 82: + handleOceanMaze(82, "OCEAN06", DIR_LEFT, 81, 0, 88, 0, 25); + break; + + case 83: + handleOceanMaze(83, "OCEAN07", DIR_UP, 89, 0, 79, 88, 25); + break; + + case 84: + handleOceanMaze(84, "OCEAN08", DIR_UP, 77, 0, 0, 78, 25); + break; + + case 85: + handleOceanMaze(85, "OCEAN09", DIR_UP, 0, 0, 81, 0, 25); + break; + + case 86: + handleOceanMaze(86, "OCEAN10", DIR_UP, 0, 80, 0, 91, 25); + break; + + case 87: + handleOceanMaze(87, "OCEAN11", DIR_RIGHT, 0, 79, 90, 0, 25); + break; + + case 88: + handleOceanMaze(88, "OCEAN12", DIR_UP, 80, 0, 83, 82, 25); + break; + + case 89: + handleOceanMaze(89, "OCEAN13", DIR_RIGHT, 0, 83, 0, 0, 25); + break; + + case 90: + playUnderwaterBaseCutscene(); + break; + + case 91: + handleOceanMaze(91, "OCEAN15", DIR_RIGHT, 78, 81, 86, 0, 25); + break; + + case 93: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 445; + if (_globals->_saveData->_data[svEscapeLeftJailFl]) { + if (getPlatform() == Common::kPlatformLinux || getPlatform() == Common::kPlatformWindows) + _objectsMan->sceneControl2("IM93", "IM93C", "ANIM93", "IM93", 29, true); + else + _objectsMan->sceneControl2("IM93", "IM93C", "ANIM93", "IM93", 26, true); + } else { + if (getPlatform() == Common::kPlatformLinux || getPlatform() == Common::kPlatformWindows) + _objectsMan->sceneControl2("IM93", "IM93", "ANIM93", "IM93", 29, true); + else + _objectsMan->sceneControl2("IM93", "IM93", "ANIM93", "IM93", 26, true); + } + break; + + case 94: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 440; + _objectsMan->sceneControl2("IM94", "IM94", "ANIM94", "IM94", 19, true); + break; + + case 95: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM95", "IM95", "ANIM95", "IM95", 19, false); + break; + + case 96: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM96", "IM96", "ANIM96", "IM96", 19, false); + break; + + case 97: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM97", "IM97", "ANIM97", "IM97", 19, false); + if (_globals->_exitId == 18) { + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->stopSound(); + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _soundMan->playSound(6); + _animMan->playAnim("PURG1A.ANM", "PURG1.ANM", 12, 18, 50); + _graphicsMan->fadeOutShort(); + _globals->_eventMode = EVENTMODE_DEFAULT; + } + break; + + case 98: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM98", "IM98", "ANIM98", "IM98", 19, true); + break; + + case 99: + _linesMan->setMaxLineIdx(5); + _globals->_characterMaxPosY = 435; + _objectsMan->sceneControl2("IM99", "IM99", "ANIM99", "IM99", 19, true); + break; + + case 100: + playEnding(); + break; + + case 111: + _objectsMan->sceneControl("IM111", "IM111", "ANIM111", "IM111", 10, false); + break; + + case 112: + _objectsMan->sceneControl("IM112", "IM112", "ANIM112", "IM112", 10, false); + break; + + case 113: + _globals->_prevScreenId = _globals->_screenId; + _globals->_screenId = 113; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_prevScreenId; + _globals->_saveData->_data[svLastScreenId] = _globals->_screenId; + _globals->_exitId = 0; + _computer->showComputer(COMPUTER_HOPKINS); + _graphicsMan->clearScreen(); + _graphicsMan->updateScreen(); + memset(_graphicsMan->_frontBuffer, 0, 307200); + memset(_graphicsMan->_backBuffer, 0, 307200); + _graphicsMan->clearPalette(); + _graphicsMan->resetDirtyRects(); + break; + + case 114: + _globals->_exitId = 0; + _globals->_prevScreenId = _globals->_screenId; + _globals->_screenId = 114; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_prevScreenId; + _globals->_saveData->_data[svLastScreenId] = _globals->_screenId; + _computer->showComputer(COMPUTER_SAMANTHA); + _graphicsMan->clearScreen(); + break; + + case 115: + _globals->_prevScreenId = _globals->_screenId; + _globals->_screenId = 115; + _globals->_saveData->_data[svLastPrevScreenId] = _globals->_prevScreenId; + _globals->_saveData->_data[svLastScreenId] = _globals->_screenId; + _globals->_exitId = 0; + _computer->showComputer(COMPUTER_PUBLIC); + _graphicsMan->clearScreen(); + break; + + case 150: + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + if (getPlatform() == Common::kPlatformLinux) + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR1A.ANM", "JOUR1A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + + case 151: + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + if (getPlatform() == Common::kPlatformLinux) + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR3A.ANM", "JOUR3A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + + case 152: + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + if (getPlatform() == Common::kPlatformLinux) + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR4A.ANM", "JOUR4A.ANM", 12, 12, 2000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + break; + + case 194: + case 195: + case 196: + case 197: + case 198: + case 199: + _globals->_characterSpriteBuf = _globals->freeMemory(_globals->_characterSpriteBuf); + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->stopSound(); + _soundMan->playSound(23); + _globals->_exitId = handleBaseMap(); // Handles the base map (non-Windows) + //_globals->_exitId = WBASE(); // Handles the 3D Doom level (Windows) + _soundMan->stopSound(); + _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR"); + _globals->_characterType = CHARACTER_HOPKINS; + _globals->_eventMode = EVENTMODE_DEFAULT; + _graphicsMan->_lineNbr = SCREEN_WIDTH; + break; + } + } + _globals->_characterSpriteBuf = _globals->freeMemory(_globals->_characterSpriteBuf); + restoreSystem(); + return true; +} + +int HopkinsEngine::getRandomNumber(int maxNumber) { + return _randomSource.getRandomNumber(maxNumber); +} + +void HopkinsEngine::initializeSystem() { + // Set graphics mode + _graphicsMan->setGraphicalMode(SCREEN_WIDTH, SCREEN_HEIGHT); + + // Synchronize the sound settings from ScummVM + _soundMan->syncSoundSettings(); + + const Common::FSNode gameDataDir(ConfMan.get("path")); + SearchMan.addSubDirectoryMatching(gameDataDir, "SYSTEM"); + SearchMan.addSubDirectoryMatching(gameDataDir, "LINK"); + SearchMan.addSubDirectoryMatching(gameDataDir, "BUFFER"); + SearchMan.addSubDirectoryMatching(gameDataDir, "ANIM"); + SearchMan.addSubDirectoryMatching(gameDataDir, "ANM"); + SearchMan.addSubDirectoryMatching(gameDataDir, "BASE"); + SearchMan.addSubDirectoryMatching(gameDataDir, "MUSIC"); + SearchMan.addSubDirectoryMatching(gameDataDir, "SEQ"); + SearchMan.addSubDirectoryMatching(gameDataDir, "SAVE"); + SearchMan.addSubDirectoryMatching(gameDataDir, "SOUND"); + SearchMan.addSubDirectoryMatching(gameDataDir, "SVGA"); + SearchMan.addSubDirectoryMatching(gameDataDir, "VOICE"); + SearchMan.addSubDirectoryMatching(gameDataDir, "TSVGA"); + + _globals->clearAll(); + + _events->initMouseData(); + _fontMan->initData(); + + _dialog->loadIcons(); + _objectsMan->_headSprites = _fileIO->loadFile("TETE.SPR"); + + _events->setMouseOn(); + _events->_mouseFl = false; + + _globals->loadCharacterData(); + + _events->_mouseOffset.x = 0; + _events->_mouseOffset.y = 0; +} + +/** + * Play the intro of the game + */ +void HopkinsEngine::playIntro() { + // Win95 EN demo doesn't include the intro + if ((getLanguage() == Common::EN_ANY) && (getPlatform() == Common::kPlatformWindows) && (getIsDemo())) + return; + + byte paletteData[PALETTE_EXT_BLOCK_SIZE]; + byte paletteData2[PALETTE_EXT_BLOCK_SIZE]; + + memset(&paletteData, 0, PALETTE_EXT_BLOCK_SIZE); + _events->refreshScreenAndEvents(); + _events->_mouseFl = false; + _globals->_eventMode = EVENTMODE_IGNORE; + _events->refreshScreenAndEvents(); + _soundMan->playSound(16); + _animMan->setClearAnimFlag(); + + _animMan->playAnim("J1.ANM", "J1.ANM", 12, 12, 50); + if (shouldQuit() || _events->_escKeyFl) + return; + _events->mouseOff(); + _soundMan->mixVoice(1, 3); + _animMan->playAnim("J2.ANM", "J2.ANM", 12, 12, 50); + + if (shouldQuit() || _events->_escKeyFl) + return; + + _events->mouseOff(); + _soundMan->mixVoice(2, 3); + _animMan->playAnim("J3.ANM", "J3.ANM", 12, 12, 50); + + if (shouldQuit() || _events->_escKeyFl) + return; + + _events->mouseOff(); + _soundMan->mixVoice(3, 3); + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _graphicsMan->updateScreen(); + _soundMan->playSound(11); + _graphicsMan->loadImage("intro1"); + _graphicsMan->scrollScreen(0); + _graphicsMan->_scrollOffset = 0; + _graphicsMan->setColorPercentage(252, 100, 100, 100); + _graphicsMan->setColorPercentage(253, 100, 100, 100); + _graphicsMan->setColorPercentage(251, 100, 100, 100); + _graphicsMan->setColorPercentage(254, 0, 0, 0); + + _events->delay(500); + + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->fadeInLong(); + if (_graphicsMan->_largeScreenFl) { + _graphicsMan->_scrollStatus = 2; + _graphicsMan->_scrollPosX = 0; + + bool loopCond = false; + do { + _graphicsMan->_scrollPosX += 2; + if (_graphicsMan->_scrollPosX > (SCREEN_WIDTH - 2)) { + _graphicsMan->_scrollPosX = SCREEN_WIDTH; + loopCond = true; + } + + if (_events->getMouseX() < _graphicsMan->_scrollPosX + 10) + _events->setMouseXY(_events->_mousePos.x + 4, _events->getMouseY()); + _events->refreshScreenAndEvents(); + } while (!shouldQuit() && !loopCond && _graphicsMan->_scrollPosX != SCREEN_WIDTH); + + _events->refreshScreenAndEvents(); + _graphicsMan->_scrollStatus = 0; + + if (shouldQuit()) + return; + } + + _soundMan->mixVoice(4, 3); + _graphicsMan->fadeOutLong(); + _graphicsMan->_scrollStatus = 0; + _graphicsMan->loadImage("intro2"); + _graphicsMan->scrollScreen(0); + _animMan->loadAnim("INTRO2"); + _graphicsMan->displayAllBob(); + _soundMan->playSound(23); + _objectsMan->stopBobAnimation(3); + _objectsMan->stopBobAnimation(5); + _graphicsMan->_scrollOffset = 0; + _graphicsMan->setColorPercentage(252, 100, 100, 100); + _graphicsMan->setColorPercentage(253, 100, 100, 100); + _graphicsMan->setColorPercentage(251, 100, 100, 100); + _graphicsMan->setColorPercentage(254, 0, 0, 0); + + for (int i = 0; i <= 4; i++) + _events->refreshScreenAndEvents(); + + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->fadeInLong(); + for (uint i = 0; i < 200 / _globals->_speed; ++i) + _events->refreshScreenAndEvents(); + + _objectsMan->setBobAnimation(3); + _soundMan->mixVoice(5, 3); + _objectsMan->stopBobAnimation(3); + _events->refreshScreenAndEvents(); + memcpy(&paletteData2, _graphicsMan->_palette, 796); + + _graphicsMan->setPaletteVGA256WithRefresh(paletteData, _graphicsMan->_frontBuffer); + _graphicsMan->endDisplayBob(); + + if (shouldQuit() || _events->_escKeyFl) + return; + + _soundMan->_specialSoundNum = 5; + _graphicsMan->_fadingFl = true; + _animMan->playAnim("ELEC.ANM", "ELEC.ANM", 10, 26, 200); + _soundMan->_specialSoundNum = 0; + + if (shouldQuit() || _events->_escKeyFl) + return; + + _graphicsMan->loadImage("intro2"); + _graphicsMan->scrollScreen(0); + _animMan->loadAnim("INTRO2"); + _graphicsMan->displayAllBob(); + _soundMan->playSound(23); + _objectsMan->stopBobAnimation(3); + _objectsMan->stopBobAnimation(5); + _objectsMan->stopBobAnimation(1); + _graphicsMan->_scrollOffset = 0; + _graphicsMan->setColorPercentage(252, 100, 100, 100); + _graphicsMan->setColorPercentage(253, 100, 100, 100); + _graphicsMan->setColorPercentage(251, 100, 100, 100); + _graphicsMan->setColorPercentage(254, 0, 0, 0); + + for (int i = 0; i <= 3; i++) + _events->refreshScreenAndEvents(); + + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->setPaletteVGA256WithRefresh(paletteData2, _graphicsMan->_frontBuffer); + + int introIndex = 0; + while (!shouldQuit() && !_events->_escKeyFl) { + if (introIndex == 12) { + _objectsMan->setBobAnimation(3); + _events->refreshScreenAndEvents(); + _soundMan->mixVoice(6, 3); + _events->refreshScreenAndEvents(); + _objectsMan->stopBobAnimation(3); + } + + Common::copy(&paletteData2[0], &paletteData2[PALETTE_BLOCK_SIZE], &_graphicsMan->_palette[0]); + + for (int i = 1, maxPalVal = 4 * introIndex; i <= PALETTE_BLOCK_SIZE; i++) { + if (_graphicsMan->_palette[i] > maxPalVal) + _graphicsMan->_palette[i] -= maxPalVal; + } + + _graphicsMan->setPaletteVGA256WithRefresh(_graphicsMan->_palette, _graphicsMan->_frontBuffer); + + for (int i = 1; i < 2 * introIndex; i++) + _events->refreshScreenAndEvents(); + + _graphicsMan->setPaletteVGA256WithRefresh(paletteData2, _graphicsMan->_frontBuffer); + + for (int i = 1; i < 20 - introIndex; i++) + _events->refreshScreenAndEvents(); + + introIndex += 2; + if (introIndex > 15) { + _graphicsMan->setPaletteVGA256WithRefresh(paletteData, _graphicsMan->_frontBuffer); + for (uint j = 1; j < 100 / _globals->_speed; ++j) + _events->refreshScreenAndEvents(); + + _objectsMan->setBobAnimation(3); + _soundMan->mixVoice(7, 3); + _objectsMan->stopBobAnimation(3); + + for (uint k = 1; k < 60 / _globals->_speed; ++k) + _events->refreshScreenAndEvents(); + _objectsMan->setBobAnimation(5); + for (uint l = 0; l < 20 / _globals->_speed; ++l) + _events->refreshScreenAndEvents(); + + Common::copy(&paletteData2[0], &paletteData2[PALETTE_BLOCK_SIZE], &_graphicsMan->_palette[0]); + _graphicsMan->setPaletteVGA256WithRefresh(_graphicsMan->_palette, _graphicsMan->_frontBuffer); + + for (uint m = 0; m < 50 / _globals->_speed; ++m) { + if (m == 30 / _globals->_speed) { + _objectsMan->setBobAnimation(3); + _soundMan->mixVoice(8, 3); + _objectsMan->stopBobAnimation(3); + } + + _events->refreshScreenAndEvents(); + } + + _graphicsMan->fadeOutLong(); + _graphicsMan->endDisplayBob(); + _soundMan->playSound(3); + _soundMan->_specialSoundNum = 1; + _animMan->setClearAnimFlag(); + _animMan->playAnim("INTRO1.ANM", "INTRO1.ANM", 10, 24, 18); + _soundMan->_specialSoundNum = 0; + if (shouldQuit() || _events->_escKeyFl) + return; + + _animMan->playAnim("INTRO2.ANM", "INTRO2.ANM", 10, 24, 18); + if (shouldQuit() || _events->_escKeyFl) + return; + + _animMan->playAnim("INTRO3.ANM", "INTRO3.ANM", 10, 24, 200); + if (shouldQuit() || _events->_escKeyFl) + return; + + _graphicsMan->_fadingFl = true; + _animMan->unsetClearAnimFlag(); + _animMan->playAnim("J4.ANM", "J4.ANM", 12, 12, 1000); + break; + } + } + + _events->_escKeyFl = false; +} + +/** + * If in demo, displays a 'not available' screen and returns to the city map + */ +void HopkinsEngine::displayNotAvailable() { + if (!getIsDemo()) + return; + + if (_globals->_language == LANG_FR) + _graphicsMan->loadImage("ndfr"); + else + _graphicsMan->loadImage("nduk"); + + _graphicsMan->fadeInLong(); + if (_soundMan->_voiceOffFl) + _events->delay(500); + else + _soundMan->mixVoice(628, 4); + + _graphicsMan->fadeOutLong(); + _globals->_exitId = 4; +} + +void HopkinsEngine::handleNotAvailable(int nextScreen) { + // Use the code of the Linux demo instead of the code of the Windows demo. + // The behavior is somewhat better, and common code is easier to maintain. + displayNotAvailable(); + _globals->_exitId = nextScreen; +} + +void HopkinsEngine::displayEndDemo() { + _soundMan->playSound(28); + if (_globals->_language == LANG_FR) + _graphicsMan->loadImage("endfr"); + else + _graphicsMan->loadImage("enduk"); + + _graphicsMan->fadeInLong(); + _events->delay(1500); + _graphicsMan->fadeOutLong(); + _globals->_exitId = 0; +} + +void HopkinsEngine::bombExplosion() { + _graphicsMan->_lineNbr = SCREEN_WIDTH; + _graphicsMan->setScreenWidth(SCREEN_WIDTH); + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->_specialSoundNum = 199; + _graphicsMan->_fadingFl = true; + _animMan->playAnim("BOMBE2A.ANM", "BOMBE2.ANM", 50, 14, 500); + _soundMan->_specialSoundNum = 0; + _graphicsMan->loadImage("IM15"); + _animMan->loadAnim("ANIM15"); + _graphicsMan->displayAllBob(); + _objectsMan->stopBobAnimation(7); + + for (int idx = 0; idx < 5; ++idx) { + _events->refreshScreenAndEvents(); + } + + _graphicsMan->fadeInLong(); + _events->mouseOff(); + + for (int idx = 0; idx < 20; ++idx) { + _events->refreshScreenAndEvents(); + } + + _globals->_introSpeechOffFl = true; + _talkMan->startStaticCharacterDialogue("vire.pe2"); + _globals->_introSpeechOffFl = false; + _objectsMan->setBobAnimation(7); + + for (int idx = 0; idx < 100; ++idx) { + _events->refreshScreenAndEvents(); + } + + _graphicsMan->fadeOutLong(); + _graphicsMan->endDisplayBob(); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 151; +} + +void HopkinsEngine::restoreSystem() { + quitGame(); + _events->refreshEvents(); +} + +void HopkinsEngine::endLinuxDemo() { + _globals->_linuxEndDemoFl = true; + _graphicsMan->resetDirtyRects(); + _objectsMan->_forestFl = false; + _events->_breakoutFl = false; + _globals->_disableInventFl = true; + _graphicsMan->loadImage("BOX"); + _soundMan->playSound(28); + _graphicsMan->fadeInLong(); + _events->mouseOn(); + _events->changeMouseCursor(0); + _events->_mouseCursorId = 0; + _events->_mouseSpriteId = 0; + + bool mouseClicked = false; + + do { + _events->refreshScreenAndEvents(); + + if (_events->getMouseButton() == 1) + mouseClicked = true; + } while (!mouseClicked && !shouldQuit()); + + // Original tried to open a web browser link here. Since ScummVM doesn't support + // that, it's being skipped in favor of simply exiting + + _graphicsMan->fadeOutLong(); +} + +void HopkinsEngine::handleConflagration() { + _globals->_disableInventFl = true; + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->loadImage("IM71"); + _animMan->loadAnim("ANIM71"); + _graphicsMan->setColorPercentage(252, 100, 100, 100); + _graphicsMan->setColorPercentage(253, 100, 100, 100); + _graphicsMan->setColorPercentage(251, 100, 100, 100); + _graphicsMan->setColorPercentage(254, 0, 0, 0); + _graphicsMan->displayAllBob(); + + for (int cpt = 0; cpt <= 4; cpt++) + _events->refreshScreenAndEvents(); + + _graphicsMan->fadeInLong(); + _globals->_eventMode = EVENTMODE_IGNORE; + + for (int cpt = 0; cpt <= 249; cpt++) + _events->refreshScreenAndEvents(); + + _globals->_introSpeechOffFl = true; + _talkMan->startAnimatedCharacterDialogue("SVGARD1.pe2"); + _globals->_introSpeechOffFl = false; + + for (int cpt = 0; cpt <= 49; cpt++) + _events->refreshScreenAndEvents(); + + _graphicsMan->fadeOutLong(); + _graphicsMan->endDisplayBob(); + _globals->_saveData->_data[svBaseFireFl] = 1; + _globals->_disableInventFl = false; +} + +void HopkinsEngine::playSubmarineCutscene() { + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->_lineNbr = SCREEN_WIDTH; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _soundMan->playSound(25); + _animMan->setClearAnimFlag(); + _animMan->playAnim("BASE00A.ANM", "BASE00.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("BASE05A.ANM", "BASE05.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("BASE10A.ANM", "BASE10.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("BASE20A.ANM", "BASE20.ANM", 10, 18, 18); + // CHECKME: The original code was doing the opposite test, which was a bug. + if (!_events->_escKeyFl) + _animMan->playAnim("BASE30A.ANM", "BASE30.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("BASE40A.ANM", "BASE40.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("BASE50A.ANM", "BASE50.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("OC00A.ANM", "OC00.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("OC05A.ANM", "OC05.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("OC10A.ANM", "OC10.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("OC20A.ANM", "OC20.ANM", 10, 18, 18); + if (!_events->_escKeyFl) { + _graphicsMan->_fadingFl = true; + _animMan->playAnim("OC30A.ANM", "OC30.ANM", 10, 18, 18); + } + + _events->_escKeyFl = false; + _animMan->unsetClearAnimFlag(); + _globals->_exitId = 85; +} + +void HopkinsEngine::playUnderwaterBaseCutscene() { + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _soundMan->playSound(26); + _globals->_eventMode = EVENTMODE_IGNORE; + _globals->_disableInventFl = true; + _graphicsMan->_fadingFl = true; + _animMan->playSequence("abase.seq", 50, 15, 50, false, false, true); + _graphicsMan->loadImage("IM92"); + _animMan->loadAnim("ANIM92"); + _graphicsMan->displayAllBob(); + _objectsMan->loadLinkFile("IM92"); +/* + for (int cpt = 0; cpt <= 4 && !shouldQuit(); cpt++) + _eventsManager->refreshScreenAndEvents(); +*/ + _graphicsMan->fadeInLong(); + _objectsMan->enableHidingBehavior(); + + do + _events->refreshScreenAndEvents(); + while (!shouldQuit() && _objectsMan->getBobAnimDataIdx(8) != 22); + + if (!shouldQuit()) { + _graphicsMan->fadeOutLong(); + _graphicsMan->endDisplayBob(); + _objectsMan->resetHidingItems(); + _globals->_disableInventFl = false; + _globals->_exitId = 93; + _globals->_eventMode = EVENTMODE_DEFAULT; + } +} + +void HopkinsEngine::playEnding() { + _globals->_characterSpriteBuf = _globals->freeMemory(_globals->_characterSpriteBuf); + _dialog->disableInvent(); + _globals->_disableInventFl = true; + _graphicsMan->_scrollOffset = 0; + _globals->_cityMapEnabledFl = false; + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->playSound(26); + _linesMan->_route = NULL; + _globals->_freezeCharacterFl = true; + _globals->_exitId = 0; + _soundMan->loadSample(1, "SOUND90.WAV"); + _graphicsMan->loadImage("IM100"); + _animMan->loadAnim("ANIM100"); + _graphicsMan->displayAllBob(); + _events->mouseOn(); + _objectsMan->stopBobAnimation(7); + _objectsMan->stopBobAnimation(8); + _objectsMan->stopBobAnimation(9); + _graphicsMan->setColorPercentage(252, 100, 100, 100); + _graphicsMan->setColorPercentage(253, 100, 100, 100); + _graphicsMan->setColorPercentage(251, 100, 100, 100); + _graphicsMan->setColorPercentage(254, 0, 0, 0); + _events->changeMouseCursor(0); + + for (int cpt = 0; cpt <= 4; cpt++) + _events->refreshScreenAndEvents(); + + _graphicsMan->fadeInLong(); + _globals->_eventMode = EVENTMODE_IGNORE; + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(6) != 54); + + _globals->_introSpeechOffFl = true; + _talkMan->startAnimatedCharacterDialogue("GM4.PE2"); + _globals->_disableInventFl = true; + _objectsMan->stopBobAnimation(6); + _objectsMan->stopBobAnimation(10); + _objectsMan->setBobAnimation(9); + _objectsMan->setBobAnimation(7); + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(7) != 54); + + _soundMan->playSample(1); + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(7) != 65); + + _globals->_introSpeechOffFl = true; + _talkMan->startAnimatedCharacterDialogue("DUELB4.PE2"); + _events->mouseOff(); + _globals->_disableInventFl = true; + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(7) != 72); + + _globals->_introSpeechOffFl = true; + _talkMan->startAnimatedCharacterDialogue("DUELH1.PE2"); + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(7) != 81); + + _globals->_introSpeechOffFl = true; + _talkMan->startAnimatedCharacterDialogue("DUELB5.PE2"); + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(7) != 120); + + _objectsMan->stopBobAnimation(7); + if (_globals->_saveData->_data[svGameWonFl] == 1) { + _soundMan->_specialSoundNum = 200; + _soundMan->_skipRefreshFl = true; + _graphicsMan->_fadingFl = true; + _animMan->playAnim("BERM.ANM", "BERM.ANM", 100, 24, 300); + _graphicsMan->endDisplayBob(); + _soundMan->removeSample(1); + _graphicsMan->loadImage("PLAN3"); + _graphicsMan->fadeInLong(); + + _events->_rateCounter = 0; + if (!_events->_escKeyFl) { + do + _events->refreshEvents(); + while (_events->_rateCounter < 2000 / _globals->_speed && !_events->_escKeyFl); + } + _events->_escKeyFl = false; + _graphicsMan->fadeOutLong(); + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->_specialSoundNum = 0; + _graphicsMan->_fadingFl = true; + _animMan->playAnim("JOUR2A.anm", "JOUR2A.anm", 12, 12, 1000); + _soundMan->playSound(11); + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + _animMan->playAnim("FF1a.anm", "FF1.anm", 18, 18, 9); + _animMan->playAnim("FF1a.anm", "FF1.anm", 9, 18, 9); + _animMan->playAnim("FF1a.anm", "FF1.anm", 9, 18, 18); + _animMan->playAnim("FF1a.anm", "FF1.anm", 9, 18, 9); + _animMan->playAnim("FF2a.anm", "FF2.anm", 24, 24, 100); + _events->mouseOff(); + displayCredits(); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + _dialog->enableInvent(); + _globals->_disableInventFl = false; + } else { + _soundMan->_specialSoundNum = 200; + _soundMan->_skipRefreshFl = true; + _animMan->playAnim2("BERM.ANM", "BERM.ANM", 100, 24, 300); + _objectsMan->stopBobAnimation(7); + _objectsMan->setBobAnimation(8); + _globals->_introSpeechOffFl = true; + _talkMan->startAnimatedCharacterDialogue("GM5.PE2"); + _globals->_disableInventFl = true; + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(8) != 5); + + _soundMan->directPlayWav("SOUND41.WAV"); + + do + _events->refreshScreenAndEvents(); + while (_objectsMan->getBobAnimDataIdx(8) != 21); + + _graphicsMan->fadeOutLong(); + _graphicsMan->endDisplayBob(); + _soundMan->removeSample(1); + _soundMan->playSound(16); + _globals->_eventMode = EVENTMODE_IGNORE; + _soundMan->_specialSoundNum = 0; + _dialog->enableInvent(); + _globals->_disableInventFl = false; + _animMan->playAnim("JOUR4A.ANM", "JOUR4A.ANM", 12, 12, 1000); + _globals->_eventMode = EVENTMODE_DEFAULT; + _globals->_exitId = 300; + } + _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR"); + _globals->_characterType = CHARACTER_HOPKINS; + _globals->_eventMode = EVENTMODE_DEFAULT; +} + +void HopkinsEngine::playPlaneCutscene() { + _soundMan->playSound(28); + _globals->_eventMode = EVENTMODE_IGNORE; + _graphicsMan->clearScreen(); + _graphicsMan->clearPalette(); + + _animMan->unsetClearAnimFlag(); + _animMan->playAnim("AEROP00A.ANM", "AEROP00.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("SEROP10A.ANM", "SEROP10A.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("AEROP20A.ANM", "AEROP20.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("AEROP30A.ANM", "AEROP30.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("AEROP40A.ANM", "AEROP40.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("AEROP50A.ANM", "AEROP50.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("AEROP60A.ANM", "AEROP60.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("AEROP70A.ANM", "AEROP70.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("TRANS00A.ANM", "TRANS00.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("TRANS10A.ANM", "TRANS10.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("TRANS15A.ANM", "TRANS15.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("TRANS20A.ANM", "TRANS20.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("TRANS30A.ANM", "TRANS30.ANM", 10, 18, 18); + if (!_events->_escKeyFl) + _animMan->playAnim("TRANS40A.ANM", "TRANS40.ANM", 10, 18, 18); + if (!_events->_escKeyFl) { + _graphicsMan->_fadingFl = true; + _animMan->playAnim("PARA00A.ANM", "PARA00.ANM", 9, 9, 9); + } else { + _graphicsMan->fadeOutShort(); + } + + _events->_escKeyFl = false; + _animMan->unsetClearAnimFlag(); +} + +void HopkinsEngine::loadBaseMap() { + Common::String filename = Common::String::format("%s.PCX", "PBASE"); + Common::File f; + + if (f.exists(filename)) { + // PBASE file exists, so go ahead and load it + _graphicsMan->loadImage("PBASE"); + } else { + // PBASE file doesn't exist, so draw a substitute screen + drawBaseMap(); + } +} + +void HopkinsEngine::drawBaseMap() { + memset(_graphicsMan->_backBuffer, 0, SCREEN_WIDTH * 2 * SCREEN_HEIGHT); + + // List of rectangle areas to draw for exit points + const int rects[] = { + 181, 66, 181 + 16, 66 + 22, + 353, 116, 353 + 22, 116 + 16, + 483, 250, 483 + 20, 250 + 25, + 471, 326, 471 + 27, 326 + 20, + 162, 365, 162 + 21, 365 + 23, + 106, 267, 106 + 20, 267 + 26 + }; + + // Loop through displaying + const int *rectP = &rects[0]; + for (int rectIndex = 0; rectIndex < 6; ++rectIndex, rectP += 4) { + Common::Rect r(rectP[0], rectP[1], rectP[2], rectP[3]); + + for (int yp = r.top; yp <= r.bottom; ++yp) { + byte *pDest = _graphicsMan->_backBuffer + yp * SCREEN_WIDTH + r.left; + Common::fill(pDest, pDest + r.width(), 0xff); + } + } + + // Copy the calculated screen + memcpy(_graphicsMan->_frontBuffer, _graphicsMan->_backBuffer, SCREEN_WIDTH * 2 * SCREEN_HEIGHT); + + // Write some explanatory text + _fontMan->displayText(40, 200, "ScummVM base map - select a square for different rooms", 255); +} + +int HopkinsEngine::handleBaseMap() { + _globals->_disableInventFl = true; + + // Load the map image + loadBaseMap(); + + // Set needed colors + _graphicsMan->setColorPercentage(252, 100, 100, 100); + _graphicsMan->setColorPercentage(253, 100, 100, 100); + _graphicsMan->setColorPercentage(251, 100, 100, 100); + _graphicsMan->setColorPercentage(254, 0, 0, 0); + _events->changeMouseCursor(0); + _graphicsMan->fadeInLong(); + bool loopCond = false; + int zone; + do { + if (shouldQuit()) + return 0; + + int mouseButton = _events->getMouseButton(); + int posX = _events->getMouseX(); + int posY = _events->getMouseY(); + zone = 0; + if ((posX - 181 <= 16) && (posY - 66 <= 22) && + (posX - 181 >= 0) && (posY - 66 >= 0)) + zone = 1; + if ((posX - 353 <= 22) && (posY - 116 <= 19) && + (posX - 353 >= 0) && (posY - 116 >= 0)) + zone = 2; + if ((posX - 483 <= 20) && (posY - 250 <= 25) && + (posX - 483 >= 0) && (posY - 250 >= 0)) + zone = 3; + if ((posX - 471 <= 27) && (posY - 326 <= 20) && + (posX - 471 >= 0) && (posY - 326 >= 0)) + zone = 4; + if ((posX - 162 <= 21) && (posY - 365 <= 23) && + (posX - 162 >= 0) && (posY - 365 >= 0)) + zone = 5; + if ((posX - 106 <= 20) && (posY - 267 <= 26) && + (posX - 106 >= 0) && (posY - 267 >= 0)) + zone = 6; + if (zone) { + _events->changeMouseCursor(4); + _globals->_baseMapColor += 25; + if (_globals->_baseMapColor > 100) + _globals->_baseMapColor = 0; + _graphicsMan->setColorPercentage2(251, _globals->_baseMapColor, _globals->_baseMapColor, _globals->_baseMapColor); + } else { + _events->changeMouseCursor(0); + _graphicsMan->setColorPercentage2(251, 100, 100, 100); + } + _events->refreshScreenAndEvents(); + if ((mouseButton == 1) && zone) + loopCond = true; + } while (!loopCond); + + _globals->_disableInventFl = false; + _graphicsMan->fadeOutLong(); + + int result; + switch (zone) { + case 1: + result = 94; + break; + case 2: + result = 95; + break; + case 3: + result = 96; + break; + case 4: + result = 97; + break; + case 5: + result = 98; + break; + case 6: + result = 99; + break; + default: + result = 0; + break; + } + return result; +} + +void HopkinsEngine::loadCredits() { + _globals->_creditsPosY = 440; + _globals->_creditsStep = 45; + byte *bufPtr; + Common::String filename; + switch (_globals->_language) { + case LANG_EN: + filename = "CREAN.TXT"; + break; + case LANG_FR: + filename = "CREFR.TXT"; + break; + case LANG_SP: + filename = "CREES.TXT"; + break; + default: + error("Unhandled language"); + break; + } + + if (!_fileIO->fileExists(filename)) { + _globals->_creditsLineNumb = 1; + _globals->_creditsItem[0]._color = '1'; + _globals->_creditsItem[0]._actvFl = true; + _globals->_creditsItem[0]._linePosY = _globals->_creditsPosY; + strcpy((char *)_globals->_creditsItem[0]._line, "The End"); + _globals->_creditsItem[0]._lineSize = 7; + return; + } + + bufPtr = _fileIO->loadFile(filename); + + byte *curPtr = bufPtr; + int idxLines = 0; + bool loopCond = false; + do { + if (*curPtr == '%') { + if (curPtr[1] == '%') { + loopCond = true; + break; + } + _globals->_creditsItem[idxLines]._color = curPtr[1]; + _globals->_creditsItem[idxLines]._actvFl = true; + _globals->_creditsItem[idxLines]._linePosY = _globals->_creditsPosY + idxLines * _globals->_creditsStep; + + int idxBuf = 0; + for(; idxBuf < 49; idxBuf++) { + byte curChar = curPtr[idxBuf + 3]; + if (curChar == '%' || curChar == 10) + break; + _globals->_creditsItem[idxLines]._line[idxBuf] = curChar; + } + _globals->_creditsItem[idxLines]._line[idxBuf] = 0; + _globals->_creditsItem[idxLines]._lineSize = idxBuf - 1; + curPtr = curPtr + idxBuf + 2; + ++idxLines; + } else { + curPtr++; + } + _globals->_creditsLineNumb = idxLines; + } while (!loopCond); + + _globals->freeMemory(bufPtr); +} + +void HopkinsEngine::displayCredits(int startPosY, byte *buffer, char color) { + byte *bufPtr = buffer; + int strWidth = 0; + byte curChar; + for (;;) { + curChar = *bufPtr++; + if (!curChar) + break; + if (curChar > 31) + strWidth += _objectsMan->getWidth(_fontMan->_font, curChar - 32); + } + int startPosX = 320 - strWidth / 2; + int endPosX = strWidth + startPosX; + int endPosY = startPosY + 12; + if ((_globals->_creditsStartX == -1) && (_globals->_creditsEndX == -1) && (_globals->_creditsStartY == -1) && (_globals->_creditsEndY == -1)) { + _globals->_creditsStartX = startPosX; + _globals->_creditsEndX = endPosX; + _globals->_creditsStartY = startPosY; + _globals->_creditsEndY = endPosY; + } + + _globals->_creditsStartX = MIN(_globals->_creditsStartX, startPosX); + _globals->_creditsEndX = MAX(_globals->_creditsEndX, endPosX); + _globals->_creditsStartY = MIN(_globals->_creditsStartY, startPosY); + _globals->_creditsEndY = MAX(_globals->_creditsEndY, endPosY); + + Common::String message = Common::String((char *)buffer); + _fontMan->displayText(startPosX, startPosY, message, color); +} + +void HopkinsEngine::displayCredits() { + loadCredits(); + _globals->_creditsPosY = 436; + _graphicsMan->loadImage("GENERIC"); + _graphicsMan->fadeInLong(); + _soundMan->playSound(28); + _events->_mouseFl = false; + _globals->_eventMode = EVENTMODE_CREDITS; + _globals->_creditsStartX = _globals->_creditsEndX = _globals->_creditsStartY = _globals->_creditsEndY = -1; + int soundId = 28; + + do { + for (int i = 0; i < _globals->_creditsLineNumb; ++i) { + if (_globals->_creditsItem[i]._actvFl) { + int nextY = _globals->_creditsPosY + i * _globals->_creditsStep; + _globals->_creditsItem[i]._linePosY = nextY; + + if ((nextY >= 51) && (nextY <= 460)) { + int col = 0; + switch (_globals->_creditsItem[i]._color) { + case '1': + col = 163; + break; + case '2': + col = 161; + break; + case '3': + col = 162; + break; + default: + warning("Unknown color, default to col #1"); + col = 163; + break; + } + if (_globals->_creditsItem[i]._lineSize != -1) + displayCredits(nextY, _globals->_creditsItem[i]._line, col); + } + } + } + --_globals->_creditsPosY; + if (_globals->_creditsStartX != -1 || _globals->_creditsEndX != -1 || _globals->_creditsStartY != -1 || _globals->_creditsEndY != -1) { + _events->refreshScreenAndEvents(); + _graphicsMan->copySurface(_graphicsMan->_backBuffer, 60, 50, 520, 430, _graphicsMan->_frontBuffer, 60, 50); + } else { + _events->refreshScreenAndEvents(); + } + if (_globals->_creditsItem[_globals->_creditsLineNumb - 1]._linePosY <= 39) { + _globals->_creditsPosY = 440; + ++soundId; + if (soundId > 31) + soundId = 28; + _soundMan->playSound(soundId); + } + _globals->_creditsStartX = -1; + _globals->_creditsEndX = -1; + _globals->_creditsStartY = -1; + _globals->_creditsEndY = -1; + } while ((_events->getMouseButton() != 1) && (!shouldQuit())); + _graphicsMan->fadeOutLong(); + _globals->_eventMode = EVENTMODE_IGNORE; + _events->_mouseFl = true; +} + +void HopkinsEngine::handleOceanMouseEvents() { + _fontMan->hideText(9); + if (_events->_mouseCursorId != 16) + return; + + _events->getMouseX(); + if (_objectsMan->_zoneNum <= 0) + return; + + int oldPosX = _events->getMouseX(); + int oldPosY = _events->getMouseY(); + bool displAnim = false; + int oldX; + switch (_objectsMan->_zoneNum) { + case 1: + switch (_globals->_oceanDirection) { + case DIR_UP: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "27,26,25,24,23,22,21,20,19,18,-1,", 6, false); + break; + case DIR_RIGHT: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,-1,", 6, false); + break; + case DIR_DOWN: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "9,10,11,12,13,14,15,16,17,18,-1,", 6, false); + break; + default: + break; + } + + _globals->_oceanDirection = DIR_LEFT; + _globals->_exitId = 1; + oldX = _objectsMan->getSpriteX(0); + for (;;) { + if (_globals->_speed == 1) + oldX -= 2; + else if (_globals->_speed == 2) + oldX -= 4; + else if (_globals->_speed == 3) + oldX -= 6; + _objectsMan->setSpriteX(0, oldX); + setSubmarineSprites(); + _events->refreshScreenAndEvents(); + if (_events->getMouseButton() == 1 && oldPosX == _events->getMouseX() && _events->getMouseY() == oldPosY) { + displAnim = true; + break; + } + + if (oldX <= -100) + break; + } + break; + case 2: + switch (_globals->_oceanDirection) { + case DIR_UP: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "27,28,29,30,31,32,33,34,35,36,-1,", 6, false); + break; + case DIR_DOWN: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "9,8,7,6,5,4,3,2,1,0,-1,", 6, false); + break; + case DIR_LEFT: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,-1,", 6, false); + break; + default: + break; + } + _globals->_oceanDirection = DIR_RIGHT; + _globals->_exitId = 2; + oldX = _objectsMan->getSpriteX(0); + for (;;) { + if (_globals->_speed == 1) + oldX += 2; + else if (_globals->_speed == 2) + oldX += 4; + else if (_globals->_speed == 3) + oldX += 6; + _objectsMan->setSpriteX(0, oldX); + setSubmarineSprites(); + _events->refreshScreenAndEvents(); + if (_events->getMouseButton() == 1 && oldPosX == _events->getMouseX() && _events->getMouseY() == oldPosY) { + displAnim = true; + break; + } + if (oldX > 499) + break; + } + break; + case 3: + switch (_globals->_oceanDirection) { + case DIR_RIGHT: + oldX = _objectsMan->getSpriteX(0); + do { + if (_globals->_speed == 1) + oldX += 2; + else if (_globals->_speed == 2) + oldX += 4; + else if (_globals->_speed == 3) + oldX += 6; + _objectsMan->setSpriteX(0, oldX); + setSubmarineSprites(); + _events->refreshScreenAndEvents(); + if (_events->getMouseButton() == 1 && oldPosX == _events->getMouseX() && _events->getMouseY() == oldPosY) { + displAnim = true; + break; + } + } while (oldX <= 235); + if (!displAnim) + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "36,35,34,33,32,31,30,29,28,27,-1,", 6, false); + break; + case DIR_DOWN: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,-1,", 6, false); + break; + case DIR_LEFT: + oldX = _objectsMan->getSpriteX(0); + do { + if (_globals->_speed == 1) + oldX -= 2; + else if (_globals->_speed == 2) + oldX -= 4; + else if (_globals->_speed == 3) + oldX -= 6; + _objectsMan->setSpriteX(0, oldX); + setSubmarineSprites(); + _events->refreshScreenAndEvents(); + if (_events->getMouseButton() == 1 && oldPosX == _events->getMouseX() && _events->getMouseY() == oldPosY) { + displAnim = true; + break; + } + } while (oldX > 236); + if (!displAnim) + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "18,19,20,21,22,23,24,25,26,27,-1,", 6, false); + break; + default: + break; + } + _globals->_oceanDirection = DIR_UP; + _globals->_exitId = 3; + break; + case 4: + switch (_globals->_oceanDirection) { + case DIR_UP: + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,-1,", 6, false); + break; + case DIR_RIGHT: + oldX = _objectsMan->getSpriteX(0); + do { + if (_globals->_speed == 1) + oldX += 2; + else if (_globals->_speed == 2) + oldX += 4; + else if (_globals->_speed == 3) + oldX += 6; + _objectsMan->setSpriteX(0, oldX); + setSubmarineSprites(); + _events->refreshScreenAndEvents(); + if (_events->getMouseButton() == 1 && oldPosX == _events->getMouseX() && _events->getMouseY() == oldPosY) { + displAnim = true; + break; + } + } while (oldX <= 235); + if (!displAnim) + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "0,1,2,3,4,5,6,7,8,9,-1,", 6, false); + break; + case DIR_LEFT: + oldX = _objectsMan->getSpriteX(0); + for (;;) { + if (_globals->_speed == 1) + oldX -= 2; + else if (_globals->_speed == 2) + oldX -= 4; + else if (_globals->_speed == 3) + oldX -= 6; + _objectsMan->setSpriteX(0, oldX); + setSubmarineSprites(); + _events->refreshScreenAndEvents(); + if (_events->getMouseButton() == 1 && oldPosX == _events->getMouseX() && _events->getMouseY() == oldPosY) + break; + + if (oldX <= 236) { + if (!displAnim) + _objectsMan->showSpecialActionAnimationWithFlip(_globals->_characterSpriteBuf, "18,17,16,15,14,13,12,11,10,9,-1,", 6, false); + break; + } + } + break; + default: + break; + } + _globals->_oceanDirection = DIR_DOWN; + _globals->_exitId = 4; + break; + } +} + +void HopkinsEngine::setSubmarineSprites() { + switch (_globals->_oceanDirection) { + case DIR_UP: + _objectsMan->setSpriteIndex(0, 27); + break; + case DIR_RIGHT: + _objectsMan->setSpriteIndex(0, 0); + break; + case DIR_DOWN: + _objectsMan->setSpriteIndex(0, 9); + break; + case DIR_LEFT: + _objectsMan->setSpriteIndex(0, 18); + break; + default: + break; + } +} + +void HopkinsEngine::handleOceanMaze(int16 curExitId, Common::String backgroundFilename, Directions defaultDirection, int16 exit1, int16 exit2, int16 exit3, int16 exit4, int16 soundId) { + _globals->_cityMapEnabledFl = false; + _graphicsMan->_noFadingFl = false; + _globals->_freezeCharacterFl = false; + _globals->_exitId = 0; + _globals->_disableInventFl = true; + _soundMan->playSound(soundId); + _globals->_characterSpriteBuf = _fileIO->loadFile("VAISSEAU.SPR"); + if (backgroundFilename.size()) + _graphicsMan->loadImage(backgroundFilename); + + if (curExitId == 77) + _objectsMan->loadLinkFile("IM77"); + else if (curExitId == 84) + _objectsMan->loadLinkFile("IM84"); + else if (curExitId == 91) + _objectsMan->loadLinkFile("IM91"); + else + _objectsMan->loadLinkFile("ocean"); + + if (!exit1) + _linesMan->disableZone(1); + if (!exit2) + _linesMan->disableZone(2); + if (!exit3) + _linesMan->disableZone(3); + if (!exit4) + _linesMan->disableZone(4); + + if (_globals->_oceanDirection == DIR_NONE) + _globals->_oceanDirection = defaultDirection; + + switch (_globals->_oceanDirection) { + case DIR_UP: + _objectsMan->_characterPos.x = 236; + _objectsMan->_startSpriteIndex = 27; + break; + case DIR_RIGHT: + _objectsMan->_characterPos.x = -20; + _objectsMan->_startSpriteIndex = 0; + break; + case DIR_DOWN: + _objectsMan->_characterPos.x = 236; + _objectsMan->_startSpriteIndex = 9; + break; + case DIR_LEFT: + _objectsMan->_characterPos.x = 415; + _objectsMan->_startSpriteIndex = 18; + break; + default: + break; + } + + _objectsMan->addStaticSprite(_globals->_characterSpriteBuf, Common::Point(_objectsMan->_characterPos.x, 110), 0, _objectsMan->_startSpriteIndex, 0, false, 0, 0); + _graphicsMan->setColorPercentage(252, 100, 100, 100); + _graphicsMan->setColorPercentage(253, 100, 100, 100); + _graphicsMan->setColorPercentage(251, 100, 100, 100); + _graphicsMan->setColorPercentage(254, 0, 0, 0); + _objectsMan->animateSprite(0); + _linesMan->_route = NULL; + _events->mouseOn(); + _events->changeMouseCursor(4); + + for (int cpt = 0; cpt <= 4; cpt++) + _events->refreshScreenAndEvents(); + + if (!_graphicsMan->_noFadingFl) + _graphicsMan->fadeInLong(); + _graphicsMan->_noFadingFl = false; + _globals->_eventMode = EVENTMODE_IGNORE; + + for (;;) { + int mouseButton = _events->getMouseButton(); + if (mouseButton && mouseButton == 1) + handleOceanMouseEvents(); + _linesMan->checkZone(); + setSubmarineSprites(); + + _events->refreshScreenAndEvents(); + if (_globals->_exitId || shouldQuit()) + break; + } + + if (_globals->_exitId == 1) + _globals->_exitId = exit1; + else if (_globals->_exitId == 2) + _globals->_exitId = exit2; + else if (_globals->_exitId == 3) + _globals->_exitId = exit3; + else if (_globals->_exitId == 4) + _globals->_exitId = exit4; + _graphicsMan->fadeOutLong(); + _objectsMan->removeSprite(0); + _objectsMan->clearScreen(); + _globals->_characterSpriteBuf = _fileIO->loadFile("PERSO.SPR"); + _globals->_characterType = CHARACTER_HOPKINS; +} + +void HopkinsEngine::syncSoundSettings() { + Engine::syncSoundSettings(); + + _soundMan->syncSoundSettings(); +} + +bool HopkinsEngine::displayAdultDisclaimer() { + int xp, yp; + int buttonIndex; + + _graphicsMan->_minX = 0; + _graphicsMan->_minY = 0; + _graphicsMan->_maxX = SCREEN_WIDTH; + _graphicsMan->_maxY = SCREEN_HEIGHT - 1; + _events->_breakoutFl = false; + _objectsMan->_forestFl = false; + _globals->_disableInventFl = true; + _globals->_exitId = 0; + + _graphicsMan->loadImage("ADULT"); + _graphicsMan->fadeInLong(); + _events->mouseOn(); + _events->changeMouseCursor(0); + _events->_mouseCursorId = 0; + _events->_mouseSpriteId = 0; + + do { + xp = _events->getMouseX(); + yp = _events->getMouseY(); + + buttonIndex = 0; + if (xp >= 37 && xp <= 169 && yp >= 406 && yp <= 445) + buttonIndex = 2; + else if (xp >= 424 && xp <= 602 && yp >= 406 && yp <= 445) + buttonIndex = 1; + + _events->refreshScreenAndEvents(); + } while (!shouldQuit() && (buttonIndex == 0 || _events->getMouseButton() != 1)); + + _globals->_disableInventFl = false; + _graphicsMan->fadeOutLong(); + + if (buttonIndex != 2) { + // Quit game + return false; + } else { + // Continue + _graphicsMan->_minX = 0; + _graphicsMan->_maxY = 20; + _graphicsMan->_maxX = SCREEN_WIDTH; + _graphicsMan->_maxY = SCREEN_HEIGHT - 20; + return true; + } +} + +bool HopkinsEngine::isUnderwaterSubScene() { + return (_globals->_curRoomNum >= 77) && (_globals->_curRoomNum <= 89); +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/hopkins.h b/engines/hopkins/hopkins.h new file mode 100644 index 0000000000..398e41a4d2 --- /dev/null +++ b/engines/hopkins/hopkins.h @@ -0,0 +1,188 @@ +/* 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. + * + */ + +#ifndef HOPKINS_HOPKINS_H +#define HOPKINS_HOPKINS_H + +#include "hopkins/anim.h" +#include "hopkins/computer.h" +#include "hopkins/debugger.h" +#include "hopkins/dialogs.h" +#include "hopkins/events.h" +#include "hopkins/files.h" +#include "hopkins/font.h" +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/lines.h" +#include "hopkins/menu.h" +#include "hopkins/objects.h" +#include "hopkins/saveload.h" +#include "hopkins/script.h" +#include "hopkins/sound.h" +#include "hopkins/talk.h" + +#include "common/scummsys.h" +#include "common/system.h" +#include "common/error.h" +#include "common/random.h" +#include "common/hash-str.h" +#include "common/util.h" +#include "engines/engine.h" +#include "graphics/surface.h" + +/** + * This is the namespace of the Hopkins engine. + * + * Status of this engine: In Development + * + * Games using this engine: + * - Hopkins FBI + */ +namespace Hopkins { + +#define DEBUG_BASIC 1 +#define DEBUG_INTERMEDIATE 2 +#define DEBUG_DETAILED 3 + +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 + +enum HopkinsDebugChannels { + kDebugPath = 1 << 0 +}; + +/** + * A wrapper macro used around three character constants, like 'END', to + * ensure portability. Typical usage: MKTAG24('E','N','D'). + */ +#define MKTAG24(a0,a1,a2) ((uint32)((a2) | (a1) << 8 | ((a0) << 16))) + +#define READ_LE_INT16(x) (int16) READ_LE_UINT16(x) + +struct HopkinsGameDescription; + +class HopkinsEngine : public Engine { +private: + const HopkinsGameDescription *_gameDescription; + Common::RandomSource _randomSource; + + void initializeSystem(); + + void displayNotAvailable(); + void restoreSystem(); + void endLinuxDemo(); + void displayEndDemo(); + void bombExplosion(); + void handleConflagration(); + void playSubmarineCutscene(); + void playUnderwaterBaseCutscene(); + void playPlaneCutscene(); + void playEnding(); + bool isUnderwaterSubScene(); + + /** + * Displays the map screen in the underground base. + */ + int handleBaseMap(); + + /** + * Loads the base map from the PBASE file + */ + void loadBaseMap(); + + /** + * Draws a simple base map for the Windows version, which implemented a 'Wolfenstein 3D' + * style shooter for the base, rather than having a map + */ + void drawBaseMap(); + + void handleOceanMouseEvents(); + void setSubmarineSprites(); + void handleOceanMaze(int16 curExitId, Common::String backgroundFilename, Directions defaultDirection, int16 exit1, int16 exit2, int16 exit3, int16 exit4, int16 soundId); + void loadCredits(); + void displayCredits(int startPosY, byte *buffer, char color); + void displayCredits(); + void handleNotAvailable(int nextScreen); + + bool runWin95Demo(); + bool runLinuxDemo(); + bool runFull(); + + /** + * Show warning screen about the game being adults only. + */ + bool displayAdultDisclaimer(); +protected: + // Engine APIs + virtual Common::Error run(); + virtual bool hasFeature(EngineFeature f) const; + +public: + AnimationManager *_animMan; + ComputerManager *_computer; + DialogsManager *_dialog; + Debugger *_debug; + EventsManager *_events; + FileManager *_fileIO; + FontManager *_fontMan; + Globals *_globals; + GraphicsManager *_graphicsMan; + LinesManager *_linesMan; + MenuManager *_menuMan; + ObjectsManager *_objectsMan; + SaveLoadManager *_saveLoad; + ScriptManager *_script; + SoundManager *_soundMan; + TalkManager *_talkMan; + +public: + HopkinsEngine(OSystem *syst, const HopkinsGameDescription *gameDesc); + virtual ~HopkinsEngine(); + void GUIError(const Common::String &msg); + + uint32 getFeatures() const; + Common::Language getLanguage() const; + Common::Platform getPlatform() const; + uint16 getVersion() const; + bool getIsDemo() const; + + int getRandomNumber(int maxNumber); + Common::String generateSaveName(int slotNumber); + virtual bool canLoadGameStateCurrently(); + virtual bool canSaveGameStateCurrently(); + virtual Common::Error loadGameState(int slot); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + int _startGameSlot; + /** + * Run the introduction sequence + */ + void playIntro(); + + /** + * Synchronizes the sound settings from ScummVM into the engine + */ + virtual void syncSoundSettings(); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_HOPKINS_H */ diff --git a/engines/hopkins/lines.cpp b/engines/hopkins/lines.cpp new file mode 100644 index 0000000000..aa708fdfb2 --- /dev/null +++ b/engines/hopkins/lines.cpp @@ -0,0 +1,2956 @@ +/* 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 "hopkins/lines.h" + +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" + +#include "common/system.h" +#include "common/textconsole.h" + +namespace Hopkins { + +LinesManager::LinesManager(HopkinsEngine *vm) { + _vm = vm; + + for (int i = 0; i < MAX_LINES + 1; ++i) + Common::fill((byte *)&_zoneLine[i], (byte *)&_zoneLine[i] + sizeof(LigneZoneItem), 0); + + for (int i = 0; i < MAX_LINES; ++i) + Common::fill((byte *)&_lineItem[i], (byte *)&_lineItem[i] + sizeof(LigneItem), 0); + + for (int i = 0; i < 4000; ++i) + Common::fill((byte *)&_smoothRoute[i], (byte *)&_smoothRoute[i] + sizeof(SmoothItem), 0); + + for (int i = 0; i < 8001; ++i) + _bestRoute[i].set(0, 0, DIR_NONE); + + for (int i = 0; i < 101; ++i) { + Common::fill((byte *)&_segment[i], (byte *)&_segment[i] + sizeof(SegmentItem), 0); + Common::fill((byte *)&_squareZone[i], (byte *)&_squareZone[i] + sizeof(SquareZoneItem), 0); + } + + for (int i = 0; i < 105; ++i) { + _bobZone[i] = 0; + _bobZoneFl[i] = false; + } + + for (int i = 0; i < 106; ++i) + Common::fill((byte *)&_zone[i], (byte *)&_zone[i] + sizeof(ZoneItem), 0); + + _linesNumb = 0; + _newLineIdx = 0; + _newLineDataIdx = 0; + _newRouteIdx = 0; + _newPosX = 0; + _newPosY = 0; + _smoothMoveDirection = DIR_NONE; + _lastLine = 0; + _maxLineIdx = 0; + _pathFindingMaxDepth = 0; + _testRoute0 = NULL; + _testRoute1 = NULL; + _testRoute2 = NULL; + _lineBuf = NULL; + _route = NULL; + _currentSegmentId = 0; + _largeBuf = NULL; + _zoneSkipCount = 0; + _hotspotTextColor = 0; + _forceHideText = false; + _oldMouseZoneId = 0; + _oldMouseX = 0; + _oldMouseY = 0; + _oldRouteFromX = 0; + _oldRouteFromY = 0; + _oldRouteDestX = 0; + _oldRouteDestY = 0; + _oldZoneNum = 0; +} + +LinesManager::~LinesManager() { + _vm->_globals->freeMemory(_largeBuf); + if (_testRoute0) + delete[] _testRoute0; + if (_testRoute1) + delete[] _testRoute1; + if (_testRoute2) + delete[] _testRoute2; +} + +int LigneItem::appendToRouteInc(int from, int to, RouteItem *route, int index) { + debugC(5, kDebugPath, "appendToRouteInc(%d, %d, route, %d)", from, to, index); + if (to == -1) + to = _lineDataEndIdx; + + for (int i = from; i < to; ++i) + route[index++].set(_lineData[2*i], _lineData[2*i+1], _directionRouteInc); + return index; +} + +int LigneItem::appendToRouteDec(int from, int to, RouteItem *route, int index) { + debugC(5, kDebugPath, "appendToRouteDecc(%d, %d, route, %d)", from, to, index); + if (from == -1) + from = _lineDataEndIdx - 1; + + for (int i = from; i > to; --i) + route[index++].set(_lineData[2*i], _lineData[2*i+1], _directionRouteDec); + return index; +} + +/** + * Load lines + */ +void LinesManager::loadLines(const Common::String &file) { + debugC(5, kDebugPath, "loadLines(%s)", file.c_str()); + resetLines(); + _linesNumb = 0; + _lastLine = 0; + byte *ptr = _vm->_fileIO->loadFile(file); + for (int idx = 0; READ_LE_INT16((uint16 *)ptr + (idx * 5)) != -1; idx++) { + addLine(idx, + (Directions)READ_LE_INT16((uint16 *)ptr + (idx * 5)), + READ_LE_INT16((uint16 *)ptr + (idx * 5) + 1), + READ_LE_INT16((uint16 *)ptr + (idx * 5) + 2), + READ_LE_INT16((uint16 *)ptr + (idx * 5) + 3), + READ_LE_INT16((uint16 *)ptr + (idx * 5) + 4)); + } + initRoute(); + _vm->_globals->freeMemory(ptr); +} + +/** + * Check Hotspots in Inventory screen + * Returns the ID of the hotspot under mouse + */ +int LinesManager::checkInventoryHotspots(int posX, int posY) { + debugC(5, kDebugPath, "checkInventoryHotspots(%d, %d)", posX, posY); + int hotspotId = 0; + if (posY >= 120 && posY <= 153) + hotspotId = checkInventoryHotspotsRow(posX, 1, false); + if (posY >= 154 && posY <= 191) + hotspotId = checkInventoryHotspotsRow(posX, 7, false); + if (posY >= 192 && posY <= 229) + hotspotId = checkInventoryHotspotsRow(posX, 13, false); + if (posY >= 230 && posY <= 267) + hotspotId = checkInventoryHotspotsRow(posX, 19, false); + if (posY >= 268 && posY <= 306) + hotspotId = checkInventoryHotspotsRow(posX, 25, true); + if (posY >= 268 && posY <= 288 && posX >= _vm->_graphicsMan->_scrollOffset + 424 && posX <= _vm->_graphicsMan->_scrollOffset + 478) + hotspotId = 30; + if (posY >= 290 && posY <= 306 && posX >= _vm->_graphicsMan->_scrollOffset + 424 && posX <= _vm->_graphicsMan->_scrollOffset + 478) + hotspotId = 31; + if (posY < 114 || posY > 306 || posX < _vm->_graphicsMan->_scrollOffset + 152 || posX > _vm->_graphicsMan->_scrollOffset + 484) + hotspotId = 32; + + return hotspotId; +} + +/** + * Check the hotspots in an inventory line + * Returns the hotspot Id under the mouse, if any. + */ +int LinesManager::checkInventoryHotspotsRow(int posX, int minZoneNum, bool lastRow) { + debugC(5, kDebugPath, "checkInventoryHotspotsRow(%d, %d, %d)", posX, minZoneNum, lastRow ? 1 : 0); + int result = minZoneNum; + + if (posX >= _vm->_graphicsMan->_scrollOffset + 158 && posX < _vm->_graphicsMan->_scrollOffset + 208) + return result; + + if (posX >= _vm->_graphicsMan->_scrollOffset + 208 && posX < _vm->_graphicsMan->_scrollOffset + 266) { + result += 1; + return result; + } + + if (posX >= _vm->_graphicsMan->_scrollOffset + 266 && posX < _vm->_graphicsMan->_scrollOffset + 320) { + result += 2; + return result; + } + + if (posX >= _vm->_graphicsMan->_scrollOffset + 320 && posX < _vm->_graphicsMan->_scrollOffset + 370) { + result += 3; + return result; + } + + if (posX >= _vm->_graphicsMan->_scrollOffset + 370 && posX < _vm->_graphicsMan->_scrollOffset + 424) { + result += 4; + return result; + } + + if (!lastRow && posX >= _vm->_graphicsMan->_scrollOffset + 424 && posX <= _vm->_graphicsMan->_scrollOffset + 478) { + result += 5; + return result; + } + + return 0; +} + +/** + * Add Zone Line + */ +void LinesManager::addZoneLine(int idx, int fromX, int fromY, int destX, int destY, int bobZoneIdx) { + debugC(5, kDebugPath, "addZoneLine(%d, %d, %d, %d, %d, %d)", idx, fromX, fromY, destX, destY, bobZoneIdx); + int16 *zoneData; + + if (fromX == fromY && fromY == destX && fromY == destY) { + _bobZoneFl[bobZoneIdx] = true; + _bobZone[bobZoneIdx] = fromY; + } else { + assert(idx < MAX_LINES + 1); + _zoneLine[idx]._zoneData = (int16 *)_vm->_globals->freeMemory((byte *)_zoneLine[idx]._zoneData); + + int distX = abs(fromX - destX); + int distY = abs(fromY - destY); + int maxDist = 1; + if (distX <= distY) + maxDist += distY; + else + maxDist += distX; + + zoneData = (int16 *)_vm->_globals->allocMemory(2 * sizeof(int16) * maxDist + (4 * sizeof(int16))); + assert(zoneData); + + _zoneLine[idx]._zoneData = zoneData; + + int16 *dataP = zoneData; + int stepX = 1000 * distX / maxDist; + int stepY = 1000 * distY / maxDist; + if (destX < fromX) + stepX = -stepX; + if (destY < fromY) + stepY = -stepY; + int smoothPosX = 1000 * fromX; + int smoothPosY = 1000 * fromY; + for (int i = 0; i < maxDist; i++) { + *dataP++ = smoothPosX / 1000; + *dataP++ = smoothPosY / 1000; + + smoothPosX += stepX; + smoothPosY += stepY; + } + *dataP++ = -1; + *dataP++ = -1; + + _zoneLine[idx]._count = maxDist; + _zoneLine[idx]._bobZoneIdx = bobZoneIdx; + } +} + +/** + * Add Line + */ +void LinesManager::addLine(int lineIdx, Directions direction, int fromX, int fromY, int destX, int destY) { + debugC(5, kDebugPath, "addLine(%d, %d, %d, %d, %d, %d)", lineIdx, direction, fromX, fromY, destX, destY); + assert(lineIdx < MAX_LINES); + + if (_linesNumb < lineIdx) + _linesNumb = lineIdx; + + _lineItem[lineIdx]._lineData = (int16 *)_vm->_globals->freeMemory((byte *)_lineItem[lineIdx]._lineData); + int distX = abs(fromX - destX) + 1; + int distY = abs(fromY - destY) + 1; + int maxDist = distY; + if (distX > maxDist) + maxDist = distX; + + byte *zoneData = _vm->_globals->allocMemory(4 * maxDist + 8); + assert(zoneData); + + Common::fill(zoneData, zoneData + 4 * maxDist + 8, 0); + _lineItem[lineIdx]._lineData = (int16 *)zoneData; + + int16 *curLineData = _lineItem[lineIdx]._lineData; + int stepX = 1000 * distX / (maxDist - 1); + int stepY = 1000 * distY / (maxDist - 1); + if (destX < fromX) + stepX = -stepX; + if (destY < fromY) + stepY = -stepY; + int dirX = (int)stepX / 1000; // -1: Left, 0: None, 1: Right + int dirY = (int)stepY / 1000; // -1: Up, 0: None, 1: Right + if (!dirX) { + if (dirY == -1) { + _lineItem[lineIdx]._directionRouteInc = DIR_UP; + _lineItem[lineIdx]._directionRouteDec = DIR_DOWN; + } else if (dirY == 1) { + _lineItem[lineIdx]._directionRouteInc = DIR_DOWN; + _lineItem[lineIdx]._directionRouteDec = DIR_UP; + } + // If dirY == 0, no move + } else if (dirX == 1) { + if (dirY == -1) { + _lineItem[lineIdx]._directionRouteInc = DIR_UP_RIGHT; + _lineItem[lineIdx]._directionRouteDec = DIR_DOWN_LEFT; + } else if (!dirY) { + _lineItem[lineIdx]._directionRouteInc = DIR_RIGHT; + _lineItem[lineIdx]._directionRouteDec = DIR_LEFT; + } else if (dirY == 1) { + _lineItem[lineIdx]._directionRouteInc = DIR_DOWN_RIGHT; + _lineItem[lineIdx]._directionRouteDec = DIR_UP_LEFT; + } + } else if (dirX == -1) { + if (dirY == 1) { + _lineItem[lineIdx]._directionRouteInc = DIR_DOWN_LEFT; + _lineItem[lineIdx]._directionRouteDec = DIR_UP_RIGHT; + } else if (!dirY) { + _lineItem[lineIdx]._directionRouteInc = DIR_LEFT; + _lineItem[lineIdx]._directionRouteDec = DIR_RIGHT; + } else if (dirY == -1) { + _lineItem[lineIdx]._directionRouteInc = DIR_UP_LEFT; + _lineItem[lineIdx]._directionRouteDec = DIR_DOWN_RIGHT; + } + } + + // Second pass to soften cases where dirY == 0 + if (dirX == 1) { + if (stepY > 250 && stepY <= 999) { + _lineItem[lineIdx]._directionRouteInc = DIR_DOWN_RIGHT; + _lineItem[lineIdx]._directionRouteDec = DIR_UP_LEFT; + } else if (stepY < -250 && stepY > -1000) { + _lineItem[lineIdx]._directionRouteInc = DIR_UP_RIGHT; + _lineItem[lineIdx]._directionRouteDec = DIR_DOWN_LEFT; + } + } else if (dirX == -1) { + if (stepY > 250 && stepY <= 999) { + _lineItem[lineIdx]._directionRouteInc = DIR_DOWN_LEFT; + _lineItem[lineIdx]._directionRouteDec = DIR_UP_RIGHT; + } else if (stepY < -250 && stepY > -1000) { + // In the original code, the test was on positive values and + // was impossible to meet. + _lineItem[lineIdx]._directionRouteInc = DIR_UP_LEFT; + _lineItem[lineIdx]._directionRouteDec = DIR_DOWN_RIGHT; + } + } + + stepX = 1000 * distX / maxDist; + stepY = 1000 * distY / maxDist; + if (destX < fromX) + stepX = -stepX; + if (destY < fromY) + stepY = -stepY; + int smoothPosX = 1000 * fromX; + int smoothPosY = 1000 * fromY; + for (int i = 0; i < maxDist - 1; i++) { + curLineData[0] = smoothPosX / 1000; + curLineData[1] = smoothPosY / 1000; + curLineData += 2; + + smoothPosX += stepX; + smoothPosY += stepY; + } + curLineData[0] = destX; + curLineData[1] = destY; + + curLineData += 2; + curLineData[0] = -1; + curLineData[1] = -1; + + _lineItem[lineIdx]._lineDataEndIdx = maxDist; + _lineItem[lineIdx]._direction = direction; + + ++_linesNumb; +} + +/** + * Check collision line + */ +bool LinesManager::checkCollisionLine(int xp, int yp, int *foundDataIdx, int *foundLineIdx, int startLineIdx, int endLineIdx) { + debugC(5, kDebugPath, "checkCollisionLine(%d, %d, foundDataIdx, foundLineIdx, %d, %d)", xp, yp, startLineIdx ,endLineIdx); + int16 *lineData; + + int left = xp + 4; + int right = xp - 4; + int top = yp + 4; + int bottom = yp - 4; + + *foundDataIdx = -1; + *foundLineIdx = -1; + + for (int curLineIdx = startLineIdx; curLineIdx <= endLineIdx; curLineIdx++) { + lineData = _lineItem[curLineIdx]._lineData; + + if (lineData == NULL) + continue; + + bool collisionFl = true; + int lineStartX = lineData[0]; + int lineStartY = lineData[1]; + int lineDataIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx; + int lineEndX = lineData[lineDataIdx - 2]; + int lineEndY = lineData[lineDataIdx - 1]; + if (lineStartX >= lineEndX) { + if (right > lineStartX || left < lineEndX) + collisionFl = false; + } else { // lineStartX < lineEndX + if (left < lineStartX || right > lineEndX) + collisionFl = false; + } + if (lineStartY >= lineEndY) { + if (bottom > lineStartY || top < lineEndY) + collisionFl = false; + } else { // lineStartY < lineEndY + if (top < lineStartY || bottom > lineEndY) + collisionFl = false; + } + + if (!collisionFl) + continue; + + for (int idx = 0; idx < _lineItem[curLineIdx]._lineDataEndIdx; idx++) { + int lineX = lineData[0]; + int lineY = lineData[1]; + lineData += 2; + + if ((xp == lineX || xp + 1 == lineX) && (yp == lineY || yp + 1 == lineY)) { + *foundDataIdx = idx; + *foundLineIdx = curLineIdx; + return true; + } + } + } + return false; +} + +/** + * Init route + */ +void LinesManager::initRoute() { + debugC(5, kDebugPath, "initRoute()"); + int lineX = _lineItem[0]._lineData[0]; + int lineY = _lineItem[0]._lineData[1]; + + int lineIdx = 1; + for (;;) { + int curDataIdx = _lineItem[lineIdx]._lineDataEndIdx; + int16 *curLineData = _lineItem[lineIdx]._lineData; + + int curLineX = curLineData[2 * curDataIdx - 2]; + int curLineY = curLineData[2 * curDataIdx - 1]; + if (_vm->_graphicsMan->_maxX == curLineX || _vm->_graphicsMan->_maxY == curLineY || + _vm->_graphicsMan->_minX == curLineX || _vm->_graphicsMan->_minY == curLineY || + (lineX == curLineX && lineY == curLineY)) + break; + if (lineIdx == MAX_LINES) + error("ERROR - LAST LINE NOT FOUND"); + + int16 *nextLineData = _lineItem[lineIdx + 1]._lineData; + if (!nextLineData) + break; + if (nextLineData[0] != curLineX && nextLineData[1] != curLineY) + break; + ++lineIdx; + } + + _lastLine = lineIdx; + for (int idx = 1; idx < MAX_LINES; idx++) { + if ((_lineItem[idx]._lineDataEndIdx < _maxLineIdx) && (idx != _lastLine + 1)) { + _lineItem[idx]._directionRouteInc = _lineItem[idx - 1]._directionRouteInc; + _lineItem[idx]._directionRouteDec = _lineItem[idx - 1]._directionRouteDec; + } + } +} + +// Avoid obstacle +int LinesManager::avoidObstacle(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route) { + debugC(5, kDebugPath, "avoidObstacle(%d, %d, %d, %d, %d, route)", lineIdx, lineDataIdx, routeIdx, destLineIdx, destLineDataIdx); + int curLineIdx = lineIdx; + int curLineDataIdx = lineDataIdx; + int curRouteIdx = routeIdx; + if (lineIdx < destLineIdx) { + curRouteIdx = _lineItem[lineIdx].appendToRouteInc(lineDataIdx, -1, route, curRouteIdx); + + for (int i = lineIdx + 1; i < destLineIdx; i++) + curRouteIdx = _lineItem[i].appendToRouteInc(0, -1, route, curRouteIdx); + + curLineDataIdx = 0; + curLineIdx = destLineIdx; + } + if (curLineIdx > destLineIdx) { + curRouteIdx = _lineItem[curLineIdx].appendToRouteDec(curLineDataIdx, 0, route, curRouteIdx); + for (int i = curLineIdx - 1; i > destLineIdx; i--) + curRouteIdx = _lineItem[i].appendToRouteDec(-1, 0, route, curRouteIdx); + curLineDataIdx = _lineItem[destLineIdx]._lineDataEndIdx - 1; + curLineIdx = destLineIdx; + } + if (curLineIdx == destLineIdx) { + if (destLineDataIdx >= curLineDataIdx) { + curRouteIdx = _lineItem[destLineIdx].appendToRouteInc(curLineDataIdx, destLineDataIdx, route, curRouteIdx); + } else { + curRouteIdx = _lineItem[destLineIdx].appendToRouteDec(curLineDataIdx, destLineDataIdx, route, curRouteIdx); + } + } + return curRouteIdx; +} + +// Avoid Obstacle, taking into account start/End lind Idx +int LinesManager::avoidObstacleOnSegment(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route, int startLineIdx, int endLineIdx) { + debugC(5, kDebugPath, "avoidObstacleOnSegment(%d, %d, %d, %d, %d, route, %d, %d)", lineIdx, lineDataIdx, routeIdx, destLineIdx, destLineDataIdx, startLineIdx, endLineIdx); + int curLineIdx = lineIdx; + int curLineDataIdx = lineDataIdx; + int curRouteIdx = routeIdx; + if (destLineIdx < lineIdx) { + curRouteIdx = _lineItem[lineIdx].appendToRouteInc(lineDataIdx, -1, route, curRouteIdx); + int wrkLineIdx = lineIdx + 1; + if (wrkLineIdx == endLineIdx + 1) + wrkLineIdx = startLineIdx; + while (destLineIdx != wrkLineIdx) { + curRouteIdx = _lineItem[wrkLineIdx].appendToRouteInc(0, -1, route, curRouteIdx); + ++wrkLineIdx; + if (endLineIdx + 1 == wrkLineIdx) + wrkLineIdx = startLineIdx; + } + curLineDataIdx = 0; + curLineIdx = destLineIdx; + } + if (destLineIdx > curLineIdx) { + curRouteIdx = _lineItem[curLineIdx].appendToRouteDec(curLineDataIdx, 0, route, curRouteIdx); + int wrkLineIdx = curLineIdx - 1; + if (wrkLineIdx == startLineIdx - 1) + wrkLineIdx = endLineIdx; + while (destLineIdx != wrkLineIdx) { + curRouteIdx = _lineItem[wrkLineIdx].appendToRouteDec(-1, 0, route, curRouteIdx); + --wrkLineIdx; + if (startLineIdx - 1 == wrkLineIdx) + wrkLineIdx = endLineIdx; + } + curLineDataIdx = _lineItem[destLineIdx]._lineDataEndIdx - 1; + curLineIdx = destLineIdx; + } + if (destLineIdx == curLineIdx) { + if (destLineDataIdx >= curLineDataIdx) { + curRouteIdx = _lineItem[destLineIdx].appendToRouteInc(curLineDataIdx, destLineDataIdx, route, curRouteIdx); + } else { + curRouteIdx = _lineItem[destLineIdx].appendToRouteDec(curLineDataIdx, destLineDataIdx, route, curRouteIdx); + } + } + return curRouteIdx; +} + +bool LinesManager::MIRACLE(int fromX, int fromY, int lineIdx, int destLineIdx, int routeIdx) { + debugC(5, kDebugPath, "MIRACLE(%d, %d, %d, %d, %d)", fromX, fromY, lineIdx, destLineIdx, routeIdx); + int newLinesDataIdx = 0; + int newLinesIdx = 0; + int lineIdxLeft = 0; + int lineDataIdxLeft = 0; + int lineIdxRight = 0; + int lineDataIdxRight = 0; + int linesIdxUp = 0; + int linesDataIdxUp = 0; + int lineIdxDown = 0; + int lineDataIdxDown = 0; + + int curX = fromX; + int curY = fromY; + int curLineIdx = lineIdx; + int tmpRouteIdx = routeIdx; + int dummyDataIdx; + if (checkCollisionLine(fromX, fromY, &dummyDataIdx, &curLineIdx, 0, _linesNumb)) { + switch (_lineItem[curLineIdx]._direction) { + case DIR_UP: + curY -= 2; + break; + case DIR_UP_RIGHT: + curY -= 2; + curX += 2; + break; + case DIR_RIGHT: + curX += 2; + break; + case DIR_DOWN_RIGHT: + curY += 2; + curX += 2; + break; + case DIR_DOWN: + curY += 2; + break; + case DIR_DOWN_LEFT: + curY += 2; + curX -= 2; + break; + case DIR_LEFT: + curX -= 2; + break; + case DIR_UP_LEFT: + curY -= 2; + curX -= 2; + break; + default: + break; + } + } + + int stepVertIncCount = 0; + for (int i = curY; curY + 200 > i; i++) { + if (checkCollisionLine(curX, i, &lineDataIdxDown, &lineIdxDown, 0, _lastLine) == 1 && lineIdxDown <= _lastLine) + break; + lineDataIdxDown = 0; + lineIdxDown = -1; + ++stepVertIncCount; + } + + int stepVertDecCount = 0; + for (int i = curY; curY - 200 < i; i--) { + if (checkCollisionLine(curX, i, &linesDataIdxUp, &linesIdxUp, 0, _lastLine) == 1 && linesIdxUp <= _lastLine) + break; + linesDataIdxUp = 0; + linesIdxUp = -1; + ++stepVertDecCount; + } + + int stepHoriIncCount = 0; + for (int i = curX; curX + 200 > i; i++) { + if (checkCollisionLine(i, curY, &lineDataIdxRight, &lineIdxRight, 0, _lastLine) == 1 && lineIdxRight <= _lastLine) + break; + lineDataIdxRight = 0; + lineIdxRight = -1; + ++stepHoriIncCount; + } + + int stepHoriDecCount = 0; + for (int i = curX; curX - 200 < i; i--) { + if (checkCollisionLine(i, curY, &lineDataIdxLeft, &lineIdxLeft, 0, _lastLine) == 1 && lineIdxLeft <= _lastLine) + break; + lineDataIdxLeft = 0; + lineIdxLeft = -1; + ++stepHoriDecCount; + } + + if (destLineIdx > curLineIdx) { + if (linesIdxUp != -1 && linesIdxUp <= curLineIdx) + linesIdxUp = -1; + if (lineIdxRight != -1 && curLineIdx >= lineIdxRight) + lineIdxRight = -1; + if (lineIdxDown != -1 && curLineIdx >= lineIdxDown) + lineIdxDown = -1; + if (lineIdxLeft != -1 && curLineIdx >= lineIdxLeft) + lineIdxLeft = -1; + if (linesIdxUp != -1 && destLineIdx < linesIdxUp) + linesIdxUp = -1; + if (lineIdxRight != -1 && destLineIdx < lineIdxRight) + lineIdxRight = -1; + if (lineIdxDown != -1 && destLineIdx < lineIdxDown) + lineIdxDown = -1; + if (lineIdxLeft != -1 && destLineIdx < lineIdxLeft) + lineIdxLeft = -1; + } else if (destLineIdx < curLineIdx) { + if (linesIdxUp != -1 && linesIdxUp >= curLineIdx) + linesIdxUp = -1; + if (lineIdxRight != -1 && curLineIdx <= lineIdxRight) + lineIdxRight = -1; + if (lineIdxDown != -1 && curLineIdx <= lineIdxDown) + lineIdxDown = -1; + if (lineIdxLeft != -1 && curLineIdx <= lineIdxLeft) + lineIdxLeft = -1; + if (linesIdxUp != -1 && destLineIdx > linesIdxUp) + linesIdxUp = -1; + if (lineIdxRight != -1 && destLineIdx > lineIdxRight) + lineIdxRight = -1; + if (lineIdxDown != -1 && destLineIdx > lineIdxDown) + lineIdxDown = -1; + if (lineIdxLeft != -1 && destLineIdx > lineIdxLeft) + lineIdxLeft = -1; + } + if (linesIdxUp != -1 || lineIdxRight != -1 || lineIdxDown != -1 || lineIdxLeft != -1) { + Directions newDir = DIR_NONE; + if (destLineIdx > curLineIdx) { + if (lineIdxDown <= linesIdxUp && lineIdxRight <= linesIdxUp && lineIdxLeft <= linesIdxUp && linesIdxUp > curLineIdx) + newDir = DIR_UP; + if (lineIdxDown <= lineIdxRight && linesIdxUp <= lineIdxRight && lineIdxLeft <= lineIdxRight && curLineIdx < lineIdxRight) + newDir = DIR_RIGHT; + if (linesIdxUp <= lineIdxDown && lineIdxRight <= lineIdxDown && lineIdxLeft <= lineIdxDown && curLineIdx < lineIdxDown) + newDir = DIR_DOWN; + if (lineIdxDown <= lineIdxLeft && lineIdxRight <= lineIdxLeft && linesIdxUp <= lineIdxLeft && curLineIdx < lineIdxLeft) + newDir = DIR_LEFT; + } else if (destLineIdx < curLineIdx) { + if (linesIdxUp == -1) + linesIdxUp = INVALID_LINE_VALUE; + if (lineIdxRight == -1) + lineIdxRight = INVALID_LINE_VALUE; + if (lineIdxDown == -1) + lineIdxDown = INVALID_LINE_VALUE; + if (lineIdxLeft == -1) + lineIdxLeft = INVALID_LINE_VALUE; + if (linesIdxUp != INVALID_LINE_VALUE && lineIdxDown >= linesIdxUp && lineIdxRight >= linesIdxUp && lineIdxLeft >= linesIdxUp && linesIdxUp < curLineIdx) + newDir = DIR_UP; + if (lineIdxRight != INVALID_LINE_VALUE && lineIdxDown >= lineIdxRight && linesIdxUp >= lineIdxRight && lineIdxLeft >= lineIdxRight && curLineIdx > lineIdxRight) + newDir = DIR_RIGHT; + if (lineIdxDown != INVALID_LINE_VALUE && linesIdxUp >= lineIdxDown && lineIdxRight >= lineIdxDown && lineIdxLeft >= lineIdxDown && curLineIdx > lineIdxDown) + newDir = DIR_DOWN; + if (lineIdxLeft != INVALID_LINE_VALUE && lineIdxDown >= lineIdxLeft && lineIdxRight >= lineIdxLeft && linesIdxUp >= lineIdxLeft && curLineIdx > lineIdxLeft) + newDir = DIR_LEFT; + } + + switch(newDir) { + case DIR_UP: + newLinesIdx = linesIdxUp; + newLinesDataIdx = linesDataIdxUp; + for (int i = 0; i < stepVertDecCount; i++) { + if (checkCollisionLine(curX, curY - i, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) { + int tmpRouteIdxUp = computeRouteIdx(linesIdxUp, linesDataIdxUp, curX, curY - i, curX, curY - stepVertDecCount, tmpRouteIdx, &_bestRoute[0]); + if (tmpRouteIdxUp == -1) + return false; + tmpRouteIdx = tmpRouteIdxUp; + if (_newPosY != -1) + i = _newPosY - curY; + } + _bestRoute[tmpRouteIdx].set(curX, curY - i, DIR_UP); + tmpRouteIdx++; + } + _newLineIdx = newLinesIdx; + _newLineDataIdx = newLinesDataIdx; + _newRouteIdx = tmpRouteIdx; + return true; + break; + case DIR_RIGHT: + newLinesIdx = lineIdxRight; + newLinesDataIdx = lineDataIdxRight; + for (int i = 0; i < stepHoriIncCount; i++) { + if (checkCollisionLine(i + curX, curY, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) { + int tmpRouteIdxRight = computeRouteIdx(linesIdxUp, linesDataIdxUp, i + curX, curY, stepHoriIncCount + curX, curY, tmpRouteIdx, &_bestRoute[0]); + if (tmpRouteIdxRight == -1) + return false; + tmpRouteIdx = tmpRouteIdxRight; + if (_newPosX != -1) + i = _newPosX - curX; + } + _bestRoute[tmpRouteIdx].set(i + curX, curY, DIR_RIGHT); + tmpRouteIdx++; + } + _newLineIdx = newLinesIdx; + _newLineDataIdx = newLinesDataIdx; + _newRouteIdx = tmpRouteIdx; + return true; + break; + case DIR_DOWN: + newLinesIdx = lineIdxDown; + newLinesDataIdx = lineDataIdxDown; + for (int i = 0; i < stepVertIncCount; i++) { + if (checkCollisionLine(curX, i + curY, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) { + int tmpRouteIdxDown = computeRouteIdx(linesIdxUp, linesDataIdxUp, curX, i + curY, curX, stepVertIncCount + curY, tmpRouteIdx, &_bestRoute[0]); + if (tmpRouteIdxDown == -1) + return false; + tmpRouteIdx = tmpRouteIdxDown; + if (_newPosY != -1) + i = curY - _newPosY; + } + _bestRoute[tmpRouteIdx].set(curX, i + curY, DIR_DOWN); + tmpRouteIdx++; + } + _newLineIdx = newLinesIdx; + _newLineDataIdx = newLinesDataIdx; + _newRouteIdx = tmpRouteIdx; + return true; + break; + case DIR_LEFT: + newLinesIdx = lineIdxLeft; + newLinesDataIdx = lineDataIdxLeft; + for (int i = 0; i < stepHoriDecCount; i++) { + if (checkCollisionLine(curX - i, curY, &linesDataIdxUp, &linesIdxUp, _lastLine + 1, _linesNumb) && _lastLine < linesIdxUp) { + int tmpRouteIdxLeft = computeRouteIdx(linesIdxUp, linesDataIdxUp, curX - i, curY, curX - stepHoriDecCount, curY, tmpRouteIdx, &_bestRoute[0]); + if (tmpRouteIdxLeft == -1) + return false; + tmpRouteIdx = tmpRouteIdxLeft; + if (_newPosX != -1) + i = curX - _newPosX; + } + _bestRoute[tmpRouteIdx].set(curX - i, curY, DIR_LEFT); + tmpRouteIdx++; + } + _newLineIdx = newLinesIdx; + _newLineDataIdx = newLinesDataIdx; + _newRouteIdx = tmpRouteIdx; + return true; + break; + default: + break; + } + } + return false; +} + +int LinesManager::computeRouteIdx(int lineIdx, int dataIdx, int fromX, int fromY, int destX, int destY, int routerIdx, RouteItem *route) { + debugC(5, kDebugPath, "computeRouteIdx(%d, %d, %d, %d, %d, %d, %d)", lineIdx, dataIdx, fromX, fromY, destX, destY, routerIdx); + int result = routerIdx; + ++_pathFindingMaxDepth; + if (_pathFindingMaxDepth > 10) { + warning("PathFinding - Max depth reached"); + route[routerIdx].invalidate(); + return -1; + } + int lineX = _lineItem[lineIdx]._lineData[0]; + int lineY = _lineItem[lineIdx]._lineData[1]; + int startLineIdx = lineIdx; + + int curLineDataEndIdx; + bool loopCond = false; + for (;;) { + int curLineIdx = startLineIdx - 1; + int endLineIdx = 2 * _lineItem[startLineIdx - 1]._lineDataEndIdx; + + int16 *lineData = _lineItem[startLineIdx - 1]._lineData; + if (lineData == NULL) + break; + while (lineData[endLineIdx - 2] != lineX || lineY != lineData[endLineIdx - 1]) { + --curLineIdx; + if (_lastLine - 1 != curLineIdx) { + endLineIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx; + lineData = _lineItem[curLineIdx]._lineData; + if (lineData) + continue; + } + loopCond = true; + break; + } + if (loopCond) + break; + + startLineIdx = curLineIdx; + lineX = lineData[0]; + lineY = lineData[1]; + } + + int lastIdx = _lineItem[lineIdx]._lineDataEndIdx - 1; + int lastPosX = _lineItem[lineIdx]._lineData[(2 * lastIdx)]; + int lastPosY = _lineItem[lineIdx]._lineData[(2 * lastIdx) + 1]; + int endLineIdx = lineIdx; + int foundLineIdx, foundDataIdx; + loopCond = false; + for (;;) { + int curLineIdx = endLineIdx + 1; + int nextLineDataEndIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx; + int16 *lineData = _lineItem[curLineIdx]._lineData; + if (lineData == NULL) + break; + for (;;) { + curLineDataEndIdx = nextLineDataEndIdx; + if (lineData[0] == lastPosX && lastPosY == lineData[1]) + break; + + ++curLineIdx; + if (curLineIdx != _linesNumb + 1) { + nextLineDataEndIdx = 2 * _lineItem[curLineIdx]._lineDataEndIdx; + lineData = _lineItem[curLineIdx]._lineData; + if (lineData) + continue; + } + loopCond = true; + break; + } + if (loopCond) + break; + + endLineIdx = curLineIdx; + lastPosX = lineData[curLineDataEndIdx - 2]; + lastPosY = lineData[curLineDataEndIdx - 1]; + } + + int distX = abs(fromX - destX) + 1; + int distY = abs(fromY - destY) + 1; + int maxDist = distY; + if (distX > distY) + maxDist = distX; + int stepX = 1000 * distX / maxDist; + int stepY = 1000 * distY / maxDist; + int smoothPosX = 1000 * fromX; + int smoothPosY = 1000 * fromY; + if (destX < fromX) + stepX = -stepX; + if (destY < fromY) + stepY = -stepY; + if (maxDist > 800) + maxDist = 800; + + Common::fill(&_lineBuf[0], &_lineBuf[1000], 0); + int bugLigIdx = 0; + for (int i = 0; i < maxDist + 1; i++) { + _lineBuf[bugLigIdx] = smoothPosX / 1000; + _lineBuf[bugLigIdx + 1] = smoothPosY / 1000; + smoothPosX += stepX; + smoothPosY += stepY; + bugLigIdx += 2; + } + bugLigIdx -= 2; + int destDataIdx = 0; + int destLineIdx = -1; + int bufX = 0; + int bufY = 0; + for (int i = maxDist + 1; i > 0; i--) { + if (checkCollisionLine(_lineBuf[bugLigIdx], _lineBuf[bugLigIdx + 1], &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx) && _lastLine < foundLineIdx) { + destLineIdx = foundLineIdx; + destDataIdx = foundDataIdx; + bufX = _lineBuf[bugLigIdx]; + bufY = _lineBuf[bugLigIdx + 1]; + break; + } + bugLigIdx -= 2; + } + int maxLineX = 0; + int minLineX = 0; + int maxLineY = 0; + int minLineY = 0; + for (int i = startLineIdx; i <= endLineIdx; ++i) { + int16 *lineData = _lineItem[i]._lineData; + if (lineData == NULL) + error("error in genial routine"); + if (i == startLineIdx) { + minLineY = MIN(lineData[1], lineData[2 * _lineItem[i]._lineDataEndIdx - 1]); + maxLineY = MAX(lineData[1], lineData[2 * _lineItem[i]._lineDataEndIdx - 1]); + + minLineX = MIN(lineData[0], lineData[2 * _lineItem[i]._lineDataEndIdx - 2]); + maxLineX = MAX(lineData[0], lineData[2 * _lineItem[i]._lineDataEndIdx - 2]); + } else { + if (lineData[1] < lineData[2 * _lineItem[i]._lineDataEndIdx - 1] && lineData[1] < minLineY) + minLineY = lineData[1]; + if (lineData[2 * _lineItem[i]._lineDataEndIdx - 1] < lineData[1] && lineData[2 * _lineItem[i]._lineDataEndIdx - 1] < minLineY) + minLineY = lineData[2 * _lineItem[i]._lineDataEndIdx - 1]; + if (lineData[1] > lineData[2 * _lineItem[i]._lineDataEndIdx - 1] && lineData[1] > maxLineY) + maxLineY = lineData[1]; + if (lineData[2 * _lineItem[i]._lineDataEndIdx - 1] > lineData[1] && lineData[2 * _lineItem[i]._lineDataEndIdx - 1] > maxLineY) + maxLineY = lineData[2 * _lineItem[i]._lineDataEndIdx - 1]; + if (lineData[0] < lineData[2 * _lineItem[i]._lineDataEndIdx - 2] && minLineX > lineData[0]) + minLineX = lineData[0]; + if (lineData[2 * _lineItem[i]._lineDataEndIdx - 2] < lineData[0] && minLineX > lineData[2 * _lineItem[i]._lineDataEndIdx - 2]) + minLineX = lineData[2 * _lineItem[i]._lineDataEndIdx - 2]; + if (lineData[0] > lineData[2 * _lineItem[i]._lineDataEndIdx - 2] && maxLineX < lineData[0]) + maxLineX = lineData[0]; + if (lineData[2 * _lineItem[i]._lineDataEndIdx - 2] > lineData[0] && maxLineX < lineData[2 * _lineItem[i]._lineDataEndIdx - 2]) + maxLineX = lineData[2 * _lineItem[i]._lineDataEndIdx - 2]; + } + } + + minLineX -= 2; + minLineY -= 2; + maxLineX += 2; + maxLineY += 2; + if (destX >= minLineX && destX <= maxLineX && destY >= minLineY && destY <= maxLineY) { + int curY = destY; + int linesIdxUp = -1; + do { + --curY; + if (checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) { + linesIdxUp = foundLineIdx; + break; + } + } while (curY && curY >= minLineY); + + curY = destY; + int lineIdxDown = -1; + do { + ++curY; + if (checkCollisionLine(destX, curY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) { + lineIdxDown = foundLineIdx; + break; + } + } while (curY < _vm->_globals->_characterMaxPosY && curY < maxLineY); + + int curX = destX; + int lineIdxRight = -1; + do { + ++curX; + if (checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) { + lineIdxRight = foundLineIdx; + break; + } + } while (curX < _vm->_graphicsMan->_maxX && curX < maxLineX); + + curX = destX; + int lineIdxLeft = -1; + do { + --curX; + if (checkCollisionLine(curX, destY, &foundDataIdx, &foundLineIdx, startLineIdx, endLineIdx)) { + lineIdxLeft = foundLineIdx; + break; + } + } while (curX > 0 && curX > minLineX); + + if (lineIdxRight != -1 && lineIdxLeft != -1 && linesIdxUp != -1 && lineIdxDown != -1) { + route[routerIdx].invalidate(); + return -1; + } + } + if (bufX < fromX - 1 || bufX > fromX + 1 || bufY < fromY - 1 || bufY > fromY + 1) { + _newPosX = bufX; + _newPosY = bufY; + if (lineIdx < destLineIdx) { + int stepCount = 0; + int curLineIdx = lineIdx; + do { + if (curLineIdx == startLineIdx - 1) + curLineIdx = endLineIdx; + ++stepCount; + --curLineIdx; + if (curLineIdx == startLineIdx - 1) + curLineIdx = endLineIdx; + } while (destLineIdx != curLineIdx); + if (abs(destLineIdx - lineIdx) == stepCount) { + if (dataIdx > abs(_lineItem[lineIdx]._lineDataEndIdx / 2)) { + result = avoidObstacle(lineIdx, dataIdx, routerIdx, destLineIdx, destDataIdx, route); + } else { + result = avoidObstacleOnSegment(lineIdx, dataIdx, routerIdx, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx); + } + } + if (abs(destLineIdx - lineIdx) < stepCount) + result = avoidObstacle(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route); + if (stepCount < abs(destLineIdx - lineIdx)) + result = avoidObstacleOnSegment(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx); + } + if (lineIdx > destLineIdx) { + int destStepCount = abs(lineIdx - destLineIdx); + int curLineIdx = lineIdx; + int curStepCount = 0; + do { + if (curLineIdx == endLineIdx + 1) + curLineIdx = startLineIdx; + ++curStepCount; + ++curLineIdx; + if (curLineIdx == endLineIdx + 1) + curLineIdx = startLineIdx; + } while (destLineIdx != curLineIdx); + if (destStepCount == curStepCount) { + if (dataIdx > abs(_lineItem[lineIdx]._lineDataEndIdx / 2)) { + result = avoidObstacleOnSegment(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx); + } else { + result = avoidObstacle(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route); + } + } + if (destStepCount < curStepCount) + result = avoidObstacle(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route); + if (curStepCount < destStepCount) + result = avoidObstacleOnSegment(lineIdx, dataIdx, result, destLineIdx, destDataIdx, route, startLineIdx, endLineIdx); + } + if (lineIdx == destLineIdx) + result = avoidObstacle(lineIdx, dataIdx, result, lineIdx, destDataIdx, route); + for(;;) { + if (!checkCollisionLine(_newPosX, _newPosY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb)) + break; + + switch (_lineItem[foundLineIdx]._direction) { + case DIR_UP: + --_newPosY; + break; + case DIR_UP_RIGHT: + --_newPosY; + ++_newPosX; + break; + case DIR_RIGHT: + ++_newPosX; + break; + case DIR_DOWN_RIGHT: + ++_newPosY; + ++_newPosX; + break; + case DIR_DOWN: + ++_newPosY; + break; + case DIR_DOWN_LEFT: + ++_newPosY; + --_newPosX; + break; + case DIR_LEFT: + --_newPosX; + break; + case DIR_UP_LEFT: + --_newPosY; + --_newPosX; + break; + default: + break; + } + } + } else { + _newPosX = -1; + _newPosY = -1; + } + return result; +} + +// Find Route from a point to the other +RouteItem *LinesManager::findRoute(int fromX, int fromY, int destX, int destY) { + debugC(5, kDebugPath, "findRoute(%d, %d, %d, %d)", fromX, fromY, destX, destY); + int foundLineIdx; + int foundDataIdx; + int curLineY = 0; + int curLineX = 0; + int stepArr[9]; + int deltaArr[9]; + int collLineDataIdxArr[9]; + int collLineIdxArr[9]; + + int clipDestX = destX; + int clipDestY = destY; + int curLineIdx = 0; + int curLineDataIdx = 0; + int lineIdx = 0; + int lineDataIdx = 0; + Directions newDir = DIR_NONE; + if (destY <= 24) + clipDestY = 25; + if (!_vm->_globals->_checkDistanceFl) { + if (abs(fromX - _oldRouteFromX) <= 4 && abs(fromY - _oldRouteFromY) <= 4 && + abs(_oldRouteDestX - destX) <= 4 && abs(_oldRouteDestY - clipDestY) <= 4) + return NULL; + + if (abs(fromX - destX) <= 4 && abs(fromY - clipDestY) <= 4) + return NULL; + + if (_oldZoneNum > 0 && _vm->_objectsMan->_zoneNum > 0 && _oldZoneNum == _vm->_objectsMan->_zoneNum) + return NULL; + } + _vm->_globals->_checkDistanceFl = false; + _oldZoneNum = _vm->_objectsMan->_zoneNum; + _oldRouteFromX = fromX; + _oldRouteDestX = destX; + _oldRouteFromY = fromY; + _oldRouteDestY = clipDestY; + _pathFindingMaxDepth = 0; + int routeIdx = 0; + if (destX <= 19) + clipDestX = 20; + if (clipDestY <= 19) + clipDestY = 20; + if (clipDestX > _vm->_graphicsMan->_maxX - 10) + clipDestX = _vm->_graphicsMan->_maxX - 10; + if (clipDestY > _vm->_globals->_characterMaxPosY) + clipDestY = _vm->_globals->_characterMaxPosY; + + if (abs(fromX - clipDestX) <= 3 && abs(fromY - clipDestY) <= 3) + return NULL; + + for (int i = 0; i <= 8; ++i) { + collLineIdxArr[i] = -1; + collLineDataIdxArr[i] = 0; + deltaArr[i] = INVALID_LINE_VALUE; + stepArr[i] = INVALID_LINE_VALUE; + } + + if (characterRoute(fromX, fromY, clipDestX, clipDestY, -1, -1, 0) == 1) + return _bestRoute; + + int tmpDelta = 0; + for (int tmpY = clipDestY; tmpY < _vm->_graphicsMan->_maxY; tmpY++, tmpDelta++) { + if (checkCollisionLine(clipDestX, tmpY, &collLineDataIdxArr[DIR_DOWN], &collLineIdxArr[DIR_DOWN], 0, _lastLine) && collLineIdxArr[DIR_DOWN] <= _lastLine) + break; + collLineDataIdxArr[DIR_DOWN] = 0; + collLineIdxArr[DIR_DOWN] = -1; + } + deltaArr[DIR_DOWN] = tmpDelta; + + tmpDelta = 0; + for (int tmpY = clipDestY; tmpY > _vm->_graphicsMan->_minY; tmpY--, tmpDelta++) { + if (checkCollisionLine(clipDestX, tmpY, &collLineDataIdxArr[DIR_UP], &collLineIdxArr[DIR_UP], 0, _lastLine) && collLineIdxArr[DIR_UP] <= _lastLine) + break; + collLineDataIdxArr[DIR_UP] = 0; + collLineIdxArr[DIR_UP] = -1; + if (deltaArr[DIR_DOWN] < tmpDelta && collLineIdxArr[DIR_DOWN] != -1) + break; + } + deltaArr[DIR_UP] = tmpDelta; + + tmpDelta = 0; + for (int tmpX = clipDestX; tmpX < _vm->_graphicsMan->_maxX; tmpX++) { + if (checkCollisionLine(tmpX, clipDestY, &collLineDataIdxArr[DIR_RIGHT], &collLineIdxArr[DIR_RIGHT], 0, _lastLine) && collLineIdxArr[DIR_RIGHT] <= _lastLine) + break; + collLineDataIdxArr[DIR_RIGHT] = 0; + collLineIdxArr[DIR_RIGHT] = -1; + ++tmpDelta; + if (deltaArr[DIR_UP] < tmpDelta && collLineIdxArr[DIR_UP] != -1) + break; + if (deltaArr[DIR_DOWN] < tmpDelta && collLineIdxArr[DIR_DOWN] != -1) + break; + } + deltaArr[DIR_RIGHT] = tmpDelta; + + tmpDelta = 0; + for (int tmpX = clipDestX; tmpX > _vm->_graphicsMan->_minX; tmpX--) { + if (checkCollisionLine(tmpX, clipDestY, &collLineDataIdxArr[DIR_LEFT], &collLineIdxArr[DIR_LEFT], 0, _lastLine) && collLineIdxArr[DIR_LEFT] <= _lastLine) + break; + collLineDataIdxArr[DIR_LEFT] = 0; + collLineIdxArr[DIR_LEFT] = -1; + ++tmpDelta; + if (deltaArr[DIR_UP] < tmpDelta && collLineIdxArr[DIR_UP] != -1) + break; + if (deltaArr[DIR_DOWN] < tmpDelta && collLineIdxArr[DIR_DOWN] != -1) + break; + if (deltaArr[DIR_RIGHT] < tmpDelta && collLineIdxArr[DIR_RIGHT] != -1) + break; + } + deltaArr[DIR_LEFT] = tmpDelta; + + if (collLineIdxArr[DIR_UP] < 0 || _lastLine < collLineIdxArr[DIR_UP]) + collLineIdxArr[DIR_UP] = -1; + if (collLineIdxArr[DIR_RIGHT] < 0 || _lastLine < collLineIdxArr[DIR_RIGHT]) + collLineIdxArr[DIR_RIGHT] = -1; + if (collLineIdxArr[DIR_DOWN] < 0 || _lastLine < collLineIdxArr[DIR_DOWN]) + collLineIdxArr[DIR_DOWN] = -1; + if (collLineIdxArr[DIR_LEFT] < 0 || _lastLine < collLineIdxArr[DIR_LEFT]) + collLineIdxArr[DIR_LEFT] = -1; + if (collLineIdxArr[DIR_UP] < 0) + deltaArr[DIR_UP] = INVALID_LINE_VALUE; + if (collLineIdxArr[DIR_RIGHT] < 0) + deltaArr[DIR_RIGHT] = INVALID_LINE_VALUE; + if (collLineIdxArr[DIR_DOWN] < 0) + deltaArr[DIR_DOWN] = INVALID_LINE_VALUE; + if (collLineIdxArr[DIR_LEFT] < 0) + deltaArr[DIR_LEFT] = INVALID_LINE_VALUE; + if (collLineIdxArr[DIR_UP] == -1 && collLineIdxArr[DIR_RIGHT] == -1 && collLineIdxArr[DIR_DOWN] == -1 && collLineIdxArr[DIR_LEFT] == -1) + return NULL; + + if (collLineIdxArr[DIR_DOWN] != -1 && deltaArr[DIR_UP] >= deltaArr[DIR_DOWN] && deltaArr[DIR_RIGHT] >= deltaArr[DIR_DOWN] && deltaArr[DIR_LEFT] >= deltaArr[DIR_DOWN]) { + curLineIdx = collLineIdxArr[DIR_DOWN]; + curLineDataIdx = collLineDataIdxArr[DIR_DOWN]; + } else if (collLineIdxArr[DIR_UP] != -1 && deltaArr[DIR_DOWN] >= deltaArr[DIR_UP] && deltaArr[DIR_RIGHT] >= deltaArr[DIR_UP] && deltaArr[DIR_LEFT] >= deltaArr[DIR_UP]) { + curLineIdx = collLineIdxArr[DIR_UP]; + curLineDataIdx = collLineDataIdxArr[DIR_UP]; + } else if (collLineIdxArr[DIR_RIGHT] != -1 && deltaArr[DIR_UP] >= deltaArr[DIR_RIGHT] && deltaArr[DIR_DOWN] >= deltaArr[DIR_RIGHT] && deltaArr[DIR_LEFT] >= deltaArr[DIR_RIGHT]) { + curLineIdx = collLineIdxArr[DIR_RIGHT]; + curLineDataIdx = collLineDataIdxArr[DIR_RIGHT]; + } else if (collLineIdxArr[DIR_LEFT] != -1 && deltaArr[DIR_DOWN] >= deltaArr[DIR_LEFT] && deltaArr[DIR_RIGHT] >= deltaArr[DIR_LEFT] && deltaArr[DIR_UP] >= deltaArr[DIR_LEFT]) { + curLineIdx = collLineIdxArr[DIR_LEFT]; + curLineDataIdx = collLineDataIdxArr[DIR_LEFT]; + } + + for (int i = 0; i <= 8; ++i) { + collLineIdxArr[i] = -1; + collLineDataIdxArr[i] = 0; + deltaArr[i] = INVALID_LINE_VALUE; + stepArr[i] = INVALID_LINE_VALUE; + } + + tmpDelta = 0; + for (int tmpY = fromY; tmpY < _vm->_graphicsMan->_maxY; tmpY++, tmpDelta++) { + if (checkCollisionLine(fromX, tmpY, &collLineDataIdxArr[DIR_DOWN], &collLineIdxArr[DIR_DOWN], 0, _lastLine) && collLineIdxArr[DIR_DOWN] <= _lastLine) + break; + collLineDataIdxArr[DIR_DOWN] = 0; + collLineIdxArr[DIR_DOWN] = -1; + } + deltaArr[DIR_DOWN] = tmpDelta + 1; + + tmpDelta = 0; + for (int tmpY = fromY; tmpY > _vm->_graphicsMan->_minY; tmpY--) { + if (checkCollisionLine(fromX, tmpY, &collLineDataIdxArr[DIR_UP], &collLineIdxArr[DIR_UP], 0, _lastLine) && collLineIdxArr[DIR_UP] <= _lastLine) + break; + collLineDataIdxArr[DIR_UP] = 0; + collLineIdxArr[DIR_UP] = -1; + ++tmpDelta; + if (collLineIdxArr[DIR_DOWN] != -1 && tmpDelta > 80) + break; + } + deltaArr[DIR_UP] = tmpDelta + 1; + + tmpDelta = 0; + for (int tmpX = fromX; tmpX < _vm->_graphicsMan->_maxX; tmpX++) { + if (checkCollisionLine(tmpX, fromY, &collLineDataIdxArr[DIR_RIGHT], &collLineIdxArr[DIR_RIGHT], 0, _lastLine) && collLineIdxArr[DIR_RIGHT] <= _lastLine) + break; + collLineDataIdxArr[DIR_RIGHT] = 0; + collLineIdxArr[DIR_RIGHT] = -1; + ++tmpDelta; + if ((collLineIdxArr[DIR_DOWN] != -1 || collLineIdxArr[DIR_UP] != -1) && (tmpDelta > 100)) + break; + } + deltaArr[DIR_RIGHT] = tmpDelta + 1; + + tmpDelta = 0; + for (int tmpX = fromX; tmpX > _vm->_graphicsMan->_minX; tmpX--) { + if (checkCollisionLine(tmpX, fromY, &collLineDataIdxArr[DIR_LEFT], &collLineIdxArr[DIR_LEFT], 0, _lastLine) && collLineIdxArr[DIR_LEFT] <= _lastLine) + break; + collLineDataIdxArr[DIR_LEFT] = 0; + collLineIdxArr[DIR_LEFT] = -1; + ++tmpDelta; + if ((collLineIdxArr[DIR_DOWN] != -1 || collLineIdxArr[DIR_UP] != -1 || collLineIdxArr[DIR_RIGHT] != -1) && (tmpDelta > 100)) + break; + } + deltaArr[DIR_LEFT] = tmpDelta + 1; + + if (collLineIdxArr[DIR_UP] != -1) + stepArr[DIR_UP] = abs(collLineIdxArr[DIR_UP] - curLineIdx); + + if (collLineIdxArr[DIR_RIGHT] != -1) + stepArr[DIR_RIGHT] = abs(collLineIdxArr[DIR_RIGHT] - curLineIdx); + + if (collLineIdxArr[DIR_DOWN] != -1) + stepArr[DIR_DOWN] = abs(collLineIdxArr[DIR_DOWN] - curLineIdx); + + if (collLineIdxArr[DIR_LEFT] != -1) + stepArr[DIR_LEFT] = abs(collLineIdxArr[DIR_LEFT] - curLineIdx); + + if (collLineIdxArr[DIR_UP] == -1 && collLineIdxArr[DIR_RIGHT] == -1 && collLineIdxArr[DIR_DOWN] == -1 && collLineIdxArr[DIR_LEFT] == -1) + error("Nearest point not found"); + + int delta = 0; + if (collLineIdxArr[DIR_UP] != -1 && stepArr[DIR_RIGHT] >= stepArr[DIR_UP] && stepArr[DIR_DOWN] >= stepArr[DIR_UP] && stepArr[DIR_LEFT] >= stepArr[DIR_UP]) { + lineIdx = collLineIdxArr[DIR_UP]; + delta = deltaArr[DIR_UP]; + newDir = DIR_UP; + lineDataIdx = collLineDataIdxArr[DIR_UP]; + } else if (collLineIdxArr[DIR_DOWN] != -1 && stepArr[DIR_RIGHT] >= stepArr[DIR_DOWN] && stepArr[DIR_UP] >= stepArr[DIR_DOWN] && stepArr[DIR_LEFT] >= stepArr[DIR_DOWN]) { + lineIdx = collLineIdxArr[DIR_DOWN]; + delta = deltaArr[DIR_DOWN]; + newDir = DIR_DOWN; + lineDataIdx = collLineDataIdxArr[DIR_DOWN]; + } else if (collLineIdxArr[DIR_RIGHT] != -1 && stepArr[DIR_UP] >= stepArr[DIR_RIGHT] && stepArr[DIR_DOWN] >= stepArr[DIR_RIGHT] && stepArr[DIR_LEFT] >= stepArr[DIR_RIGHT]) { + lineIdx = collLineIdxArr[DIR_RIGHT]; + delta = deltaArr[DIR_RIGHT]; + newDir = DIR_RIGHT; + lineDataIdx = collLineDataIdxArr[DIR_RIGHT]; + } else if (collLineIdxArr[DIR_LEFT] != -1 && stepArr[DIR_UP] >= stepArr[DIR_LEFT] && stepArr[DIR_DOWN] >= stepArr[DIR_LEFT] && stepArr[DIR_RIGHT] >= stepArr[DIR_LEFT]) { + lineIdx = collLineIdxArr[DIR_LEFT]; + delta = deltaArr[DIR_LEFT]; + newDir = DIR_LEFT; + lineDataIdx = collLineDataIdxArr[DIR_LEFT]; + } + + int bestRouteNum = characterRoute(fromX, fromY, clipDestX, clipDestY, lineIdx, curLineIdx, 0); + + if (bestRouteNum == 1) + return _bestRoute; + + if (bestRouteNum == 2) { + lineIdx = _newLineIdx; + lineDataIdx = _newLineDataIdx; + routeIdx = _newRouteIdx; + } else { + switch (newDir) { + case DIR_UP: + for (int deltaY = 0; deltaY < delta; deltaY++) { + if (checkCollisionLine(fromX, fromY - deltaY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb) && _lastLine < foundLineIdx) { + int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, fromX, fromY - deltaY, fromX, fromY - delta, routeIdx, _bestRoute); + if (tmpRouteIdx == -1) { + _bestRoute[routeIdx].invalidate(); + return &_bestRoute[0]; + } + routeIdx = tmpRouteIdx; + if (_newPosY != -1) + deltaY = fromY - _newPosY; + } + _bestRoute[routeIdx].set(fromX, fromY - deltaY, DIR_UP); + routeIdx++; + } + break; + case DIR_DOWN: + for (int deltaY = 0; deltaY < delta; deltaY++) { + if (checkCollisionLine(fromX, deltaY + fromY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb) + && _lastLine < foundLineIdx) { + int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, fromX, deltaY + fromY, fromX, delta + fromY, routeIdx, &_bestRoute[0]); + if (tmpRouteIdx == -1) { + _bestRoute[routeIdx].invalidate(); + return &_bestRoute[0]; + } + routeIdx = tmpRouteIdx; + if (_newPosY != -1) + deltaY = _newPosY - fromY; + } + _bestRoute[routeIdx].set(fromX, fromY + deltaY, DIR_DOWN); + routeIdx++; + } + break; + case DIR_LEFT: + for (int deltaX = 0; deltaX < delta; deltaX++) { + if (checkCollisionLine(fromX - deltaX, fromY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb) && _lastLine < foundLineIdx) { + int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, fromX - deltaX, fromY, fromX - delta, fromY, routeIdx, &_bestRoute[0]); + if (tmpRouteIdx == -1) { + _bestRoute[routeIdx].invalidate(); + return &_bestRoute[0]; + } + routeIdx = tmpRouteIdx; + if (_newPosX != -1) + deltaX = fromX - _newPosX; + } + _bestRoute[routeIdx].set(fromX - deltaX, fromY, DIR_LEFT); + routeIdx++; + } + break; + case DIR_RIGHT: + for (int deltaX = 0; deltaX < delta; deltaX++) { + if (checkCollisionLine(deltaX + fromX, fromY, &foundDataIdx, &foundLineIdx, _lastLine + 1, _linesNumb) && _lastLine < foundLineIdx) { + int tmpRouteIdx = computeRouteIdx(foundLineIdx, foundDataIdx, deltaX + fromX, fromY, delta + fromX, fromY, routeIdx, &_bestRoute[0]); + if (tmpRouteIdx == -1) { + _bestRoute[routeIdx].invalidate(); + return &_bestRoute[0]; + } + routeIdx = tmpRouteIdx; + if (_newPosX != -1) + deltaX = _newPosX - fromX; + } + _bestRoute[routeIdx].set(fromX + deltaX, fromY, DIR_RIGHT); + routeIdx++; + } + break; + default: + break; + } + } + + bool loopCond; + do { + loopCond = false; + if (lineIdx < curLineIdx) { + for (int i = lineDataIdx; _lineItem[lineIdx]._lineDataEndIdx > i; ++i) { + curLineX = _lineItem[lineIdx]._lineData[2 * i]; + curLineY = _lineItem[lineIdx]._lineData[2 * i + 1]; + _bestRoute[routeIdx].set(_lineItem[lineIdx]._lineData[2 * i], _lineItem[lineIdx]._lineData[2 * i + 1], _lineItem[lineIdx]._directionRouteInc); + routeIdx++; + } + for (int idx = lineIdx + 1; idx < curLineIdx; idx++) { + for (int dataIdx = 0; _lineItem[idx]._lineDataEndIdx > dataIdx; dataIdx++) { + curLineX = _lineItem[idx]._lineData[2 * dataIdx]; + curLineY = _lineItem[idx]._lineData[2 * dataIdx + 1]; + _bestRoute[routeIdx].set(_lineItem[idx]._lineData[2 * dataIdx], _lineItem[idx]._lineData[2 * dataIdx + 1], _lineItem[idx]._directionRouteInc); + routeIdx++; + if (_lineItem[idx]._lineDataEndIdx > 30 && dataIdx == _lineItem[idx]._lineDataEndIdx / 2) { + bestRouteNum = characterRoute(_lineItem[idx]._lineData[2 * dataIdx], _lineItem[idx]._lineData[2 * dataIdx + 1], clipDestX, clipDestY, idx, curLineIdx, routeIdx); + if (bestRouteNum == 1) + return &_bestRoute[0]; + if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, idx, curLineIdx, routeIdx)) { + lineIdx = _newLineIdx; + lineDataIdx = _newLineDataIdx; + routeIdx = _newRouteIdx; + loopCond = true; + break; + } + } + } + + if (loopCond) + break; + + bestRouteNum = characterRoute(curLineX, curLineY, clipDestX, clipDestY, idx, curLineIdx, routeIdx); + if (bestRouteNum == 1) + return &_bestRoute[0]; + if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, idx, curLineIdx, routeIdx)) { + lineIdx = _newLineIdx; + lineDataIdx = _newLineDataIdx; + routeIdx = _newRouteIdx; + loopCond = true; + break; + } + } + if (loopCond) + continue; + + lineDataIdx = 0; + lineIdx = curLineIdx; + } + if (lineIdx > curLineIdx) { + for (int dataIdx = lineDataIdx; dataIdx > 0; dataIdx--) { + curLineX = _lineItem[lineIdx]._lineData[2 * dataIdx]; + curLineY = _lineItem[lineIdx]._lineData[2 * dataIdx + 1]; + + _bestRoute[routeIdx].set(_lineItem[lineIdx]._lineData[2 * dataIdx], _lineItem[lineIdx]._lineData[2 * dataIdx + 1], _lineItem[lineIdx]._directionRouteDec); + routeIdx++; + } + for (int i = lineIdx - 1; i > curLineIdx; i--) { + for (int dataIdx = _lineItem[i]._lineDataEndIdx - 1; dataIdx > -1; dataIdx--) { + curLineX = _lineItem[i]._lineData[2 * dataIdx]; + curLineY = _lineItem[i]._lineData[2 * dataIdx + 1]; + _bestRoute[routeIdx].set(_lineItem[i]._lineData[2 * dataIdx], _lineItem[i]._lineData[2 * dataIdx + 1], _lineItem[i]._directionRouteDec); + routeIdx++; + if (_lineItem[i]._lineDataEndIdx > 30 && dataIdx == _lineItem[i]._lineDataEndIdx / 2) { + bestRouteNum = characterRoute(curLineX, curLineY, clipDestX, clipDestY, i, curLineIdx, routeIdx); + if (bestRouteNum == 1) + return &_bestRoute[0]; + if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, i, curLineIdx, routeIdx)) { + lineIdx = _newLineIdx; + lineDataIdx = _newLineDataIdx; + routeIdx = _newRouteIdx; + loopCond = true; + break; + } + } + } + + if (loopCond) + break; + + bestRouteNum = characterRoute(curLineX, curLineY, clipDestX, clipDestY, i, curLineIdx, routeIdx); + if (bestRouteNum == 1) + return &_bestRoute[0]; + if (bestRouteNum == 2 || MIRACLE(curLineX, curLineY, i, curLineIdx, routeIdx)) { + lineIdx = _newLineIdx; + lineDataIdx = _newLineDataIdx; + routeIdx = _newRouteIdx; + loopCond = true; + break; + } + } + + if (!loopCond) { + lineDataIdx = _lineItem[curLineIdx]._lineDataEndIdx - 1; + lineIdx = curLineIdx; + } + } + } while (loopCond); + + if (lineIdx == curLineIdx) { + if (lineDataIdx <= curLineDataIdx) + routeIdx = _lineItem[curLineIdx].appendToRouteInc(lineDataIdx, curLineDataIdx, _bestRoute, routeIdx); + else + routeIdx = _lineItem[curLineIdx].appendToRouteDec(lineDataIdx, curLineDataIdx, _bestRoute, routeIdx); + } + if (characterRoute(_bestRoute[routeIdx - 1]._x, _bestRoute[routeIdx - 1]._y, clipDestX, clipDestY, -1, -1, routeIdx) != 1) { + _bestRoute[routeIdx].invalidate(); + } + + return &_bestRoute[0]; +} + +void LinesManager::useRoute0(int idx, int curRouteIdx) { + debugC(5, kDebugPath, "useRoute0(%d, %d)", idx, curRouteIdx); + if (idx) { + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute0[i++]; + } while (_testRoute0[i].isValid()); + } + _bestRoute[curRouteIdx].invalidate(); +} + +void LinesManager::useRoute1(int idx, int curRouteIdx) { + debugC(5, kDebugPath, "useRoute1(%d, %d)", idx, curRouteIdx); + if (idx) { + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute1[i++]; + } while (_testRoute1[i].isValid()); + } + _bestRoute[curRouteIdx].invalidate(); +} + +void LinesManager::useRoute2(int idx, int curRouteIdx) { + debugC(5, kDebugPath, "useRoute2(%d, %d)", idx, curRouteIdx); + if (idx) { + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute2[i++]; + } while (_testRoute2[i].isValid()); + } + _bestRoute[curRouteIdx].invalidate(); +} + +int LinesManager::characterRoute(int fromX, int fromY, int destX, int destY, int startLineIdx, int endLineIdx, int routeIdx) { + debugC(5, kDebugPath, "characterRoute(%d, %d, %d, %d, %d, %d, %d)", fromX, fromY, destX, destY, startLineIdx, endLineIdx, routeIdx); + int collDataIdxRoute2 = 0; + bool colResult = false; + + int curX = fromX; + int curY = fromY; + int curRouteIdx = routeIdx; + bool dummyLineFl = false; + if (startLineIdx == -1 && endLineIdx == -1) + dummyLineFl = true; + int foundDataIdx; + int foundLineIdx = startLineIdx; + if (checkCollisionLine(fromX, fromY, &foundDataIdx, &foundLineIdx, 0, _linesNumb)) { + switch (_lineItem[foundLineIdx]._direction) { + case DIR_UP: + curY -= 2; + break; + case DIR_UP_RIGHT: + curY -= 2; + curX += 2; + break; + case DIR_RIGHT: + curX += 2; + break; + case DIR_DOWN_RIGHT: + curY += 2; + curX += 2; + break; + case DIR_DOWN: + curY += 2; + break; + case DIR_DOWN_LEFT: + curY += 2; + curX -= 2; + break; + case DIR_LEFT: + curX -= 2; + break; + case DIR_UP_LEFT: + curY -= 2; + curX -= 2; + break; + default: + break; + } + } + int oldX = curX; + int oldY = curY; + int idxRoute0 = 0; + int collLineIdxRoute0 = -1; + int collLineIdxRoute1 = -1; + int collLineIdxRoute2 = -1; + + int distX, distY; + int repeatFlag = 0; + int collDataIdxRoute0 = 0; + int collDataIdxRoute1 = 0; + for (;;) { + int newX = curX; + int newY = curY; + if (destX >= curX - 2 && destX <= curX + 2 && destY >= curY - 2 && destY <= curY + 2) { + _testRoute0[idxRoute0].invalidate(); + useRoute0(idxRoute0, curRouteIdx); + return 1; + } + distX = abs(curX - destX) + 1; + distY = abs(curY - destY) + 1; + int maxDist; + if (distX > distY) + maxDist = distX; + else + maxDist = distY; + maxDist--; + assert(maxDist != 0); + int stepX = 1000 * distX / maxDist; + int stepY = 1000 * distY / maxDist; + if (destX < curX) + stepX = -stepX; + if (destY < curY) + stepY = -stepY; + int vertDirection = (int16)stepX / 1000; + int horzDirection = (int16)stepY / 1000; + Directions newDirection = DIR_NONE; + if (horzDirection == -1 && (stepX >= 0 && stepX <= 150)) + newDirection = DIR_UP; + if (vertDirection == 1 && (stepY >= -1 && stepY <= 150)) + newDirection = DIR_RIGHT; + if (horzDirection == 1 && (stepX >= -150 && stepX <= 150)) + newDirection = DIR_DOWN; + if (vertDirection == -1 && (stepY >= -150 && stepY <= 150)) + newDirection = DIR_LEFT; + if (horzDirection == -1 && (stepX >= -150 && stepX <= 0)) + newDirection = DIR_UP; + + if (newDirection == DIR_NONE && !checkSmoothMove(curX, newY, destX, destY) && !makeSmoothMove(curX, newY, destX, destY)) { + newDirection = _smoothMoveDirection; + int smoothRouteIdx = 0; + for (smoothRouteIdx = 0; _smoothRoute[smoothRouteIdx]._posX != -1 && _smoothRoute[smoothRouteIdx]._posY != -1; ++smoothRouteIdx) { + if (checkCollisionLine(_smoothRoute[smoothRouteIdx]._posX, _smoothRoute[smoothRouteIdx]._posY, &collDataIdxRoute0, &collLineIdxRoute0, 0, _linesNumb)) { + if (collLineIdxRoute0 > _lastLine) + collLineIdxRoute0 = -1; + break; + } + + _testRoute0[idxRoute0].set(_smoothRoute[smoothRouteIdx]._posX, _smoothRoute[smoothRouteIdx]._posY, newDirection); + idxRoute0++; + + if (repeatFlag == 1) { + repeatFlag = 2; + break; + } + } + + if (repeatFlag != 2 && _smoothRoute[smoothRouteIdx]._posX != -1 && _smoothRoute[smoothRouteIdx]._posY != -1) + break; + + repeatFlag = 1; + newX = _smoothRoute[smoothRouteIdx - 1]._posX; + newY = _smoothRoute[smoothRouteIdx - 1]._posY; + } + int newDistX = abs(newX - destX) + 1; + int newDistY = abs(newY - destY) + 1; + int newMaxDist = newDistY; + if (newDistX > newDistY) + newMaxDist = newDistX; + if (newMaxDist <= 10) { + _testRoute0[idxRoute0].invalidate(); + useRoute0(idxRoute0, curRouteIdx); + return 1; + } + int newStepX = 1000 * newDistX / (newMaxDist - 1); + int newStepY = 1000 * newDistY / (newMaxDist - 1); + if (destX < newX) + newStepX = -newStepX; + if (destY < newY) + newStepY = -newStepY; + int newVertDirection = newStepX / 1000; + int newHorzDirection = newStepY / 1000; + int newSmoothX = 1000 * newX; + int newSmoothY = 1000 * newY; + int curPosX = newSmoothX / 1000; + int curPosY = newSmoothY / 1000; + if (!(newStepX / 1000) && newHorzDirection == -1) + newDirection = DIR_UP; + if (newVertDirection == 1) { + if (newHorzDirection == -1) + newDirection = DIR_UP_RIGHT; + if (!newHorzDirection) + newDirection = DIR_RIGHT; + if (newHorzDirection == 1) + newDirection = DIR_DOWN_RIGHT; + } + if (!newVertDirection && newHorzDirection == 1) + newDirection = DIR_DOWN; + if ((newVertDirection != -1) && (newHorzDirection == -1)) { + if (newStepX >= 0 && newStepX < 510) + newDirection = DIR_UP; + else if (newStepX >= 510 && newStepX <= 1000) + newDirection = DIR_UP_RIGHT; + } else { + if (newHorzDirection == 1) + newDirection = DIR_DOWN_LEFT; + else if (!newHorzDirection) + newDirection = DIR_LEFT; + else if (newHorzDirection == -1) { + if (newStepX >= 0 && newStepX < 510) + newDirection = DIR_UP; + else if (newStepX >= 510 && newStepX <= 1000) + newDirection = DIR_UP_RIGHT; + else + newDirection = DIR_UP_LEFT; + } + } + if (newVertDirection == 1) { + if (newStepY >= -1000 && newStepY <= -510) + newDirection = DIR_UP_RIGHT; + if (newStepY >= -510 && newStepY <= 510) + newDirection = DIR_RIGHT; + if (newStepY >= 510 && newStepY <= 1000) + newDirection = DIR_DOWN_RIGHT; + } + if (newHorzDirection == 1) { + if (newStepX >= 510 && newStepX <= 1000) + newDirection = DIR_DOWN_RIGHT; + if (newStepX >= -510 && newStepX <= 510) + newDirection = DIR_DOWN; + if (newStepX >= -1000 && newStepX <= -510) + newDirection = DIR_DOWN_LEFT; + } + if (newVertDirection == -1) { + if (newStepY >= 510 && newStepY <= 1000) + newDirection = DIR_DOWN_LEFT; + if (newStepY >= -510 && newStepY <= 510) + newDirection = DIR_LEFT; + if (newStepY >= -1000 && newStepY <= -510) + newDirection = DIR_UP_LEFT; + } + if (newHorzDirection == -1) { + if (newStepX >= -1000 && newStepX <= -510) + newDirection = DIR_UP_LEFT; + if (newStepX >= -510 && newStepX <= 0) + newDirection = DIR_UP; + } + if (newMaxDist + 1 <= 0) { + _testRoute0[idxRoute0].invalidate(); + useRoute0(idxRoute0, curRouteIdx); + return 1; + } + int curDist = 0; + while (!checkCollisionLine(curPosX, curPosY, &collDataIdxRoute0, &collLineIdxRoute0, 0, _linesNumb)) { + _testRoute0[idxRoute0].set(curPosX, curPosY, newDirection); + newSmoothX += newStepX; + newSmoothY += newStepY; + curPosX = newSmoothX / 1000; + curPosY = newSmoothY / 1000; + idxRoute0++; + ++curDist; + if (curDist >= newMaxDist + 1) { + _testRoute0[idxRoute0].invalidate(); + useRoute0(idxRoute0, curRouteIdx); + return 1; + } + } + if (_lastLine >= collLineIdxRoute0) + break; + int tmpRouteIdx = computeRouteIdx(collLineIdxRoute0, collDataIdxRoute0, curPosX, curPosY, destX, destY, idxRoute0, _testRoute0); + if (tmpRouteIdx == -1) { + useRoute0(idxRoute0, curRouteIdx); + return 1; + } + idxRoute0 = tmpRouteIdx; + if (_newPosX != -1 || _newPosY != -1) { + collLineIdxRoute0 = -1; + break; + } + curX = -1; + curY = -1; + } + + _testRoute0[idxRoute0].invalidate(); + + int idxRoute1 = 0; + int posXRoute1 = oldX; + int posYRoute1 = oldY; + + while (true) { + + if (destX >= posXRoute1 - 2 && destX <= posXRoute1 + 2 && destY >= posYRoute1 - 2 && destY <= posYRoute1 + 2) { + _testRoute1[idxRoute1].invalidate(); + useRoute1(idxRoute1, curRouteIdx); + return 1; + } + while (posXRoute1 != destX) { + if (checkCollisionLine(posXRoute1, posYRoute1, &collDataIdxRoute1, &collLineIdxRoute1, 0, _linesNumb)) { + if (collLineIdxRoute1 > _lastLine) + collLineIdxRoute1 = -1; + break; + } + + if (posXRoute1 < destX) + _testRoute1[idxRoute1++].set(posXRoute1++, posYRoute1, DIR_RIGHT); + else + _testRoute1[idxRoute1++].set(posXRoute1--, posYRoute1, DIR_LEFT); + } + if (posXRoute1 != destX) + break; + + int curPosY = posYRoute1; + while (curPosY != destY) { + if (checkCollisionLine(destX, curPosY, &collDataIdxRoute1, &collLineIdxRoute1, 0, _linesNumb)) { + if (collLineIdxRoute1 <= _lastLine) + break; + + int tmpRouteIdx = computeRouteIdx(collLineIdxRoute1, collDataIdxRoute1, destX, curPosY, destX, destY, idxRoute1, _testRoute1); + if (tmpRouteIdx == -1) { + useRoute1(idxRoute1, curRouteIdx); + return 1; + } + idxRoute1 = tmpRouteIdx; + if (_newPosX != -1 && _newPosY != -1) + break; + } + + if (curPosY < destY) + _testRoute1[idxRoute1++].set(destX, curPosY++, DIR_DOWN); + else + _testRoute1[idxRoute1++].set(destX, curPosY--, DIR_UP); + } + if (curPosY == destY) { + _testRoute1[idxRoute1].invalidate(); + useRoute1(idxRoute1, curRouteIdx); + return 1; + } + if (collLineIdxRoute1 <= _lastLine) + break; + posXRoute1 = _newPosX; + posYRoute1 = _newPosY; + bool colRes = checkCollisionLine(_newPosX, _newPosY, &collDataIdxRoute1, &collLineIdxRoute1, 0, _lastLine); + if (colRes && collLineIdxRoute1 <= _lastLine) + break; + } + + _testRoute1[idxRoute1].invalidate(); + idxRoute1 = 0; + int posXRoute2 = oldX; + int posYRoute2 = oldY; + while (true) { + int curPosX; + if (destX >= posXRoute2 - 2 && destX <= posXRoute2 + 2 && destY >= posYRoute2 - 2 && destY <= posYRoute2 + 2) { + _testRoute2[idxRoute1].invalidate(); + useRoute2(idxRoute1, curRouteIdx); + return 1; + } + + int curPosYRoute2 = posYRoute2; + while (curPosYRoute2 != destY) { + if (checkCollisionLine(posXRoute2, curPosYRoute2, &collDataIdxRoute2, &collLineIdxRoute2, 0, _linesNumb)) { + if (collLineIdxRoute2 > _lastLine) + collLineIdxRoute2 = -1; + break; + } + + if (curPosYRoute2 < destY) + _testRoute2[idxRoute1++].set(posXRoute2, curPosYRoute2++, DIR_DOWN); + else + _testRoute2[idxRoute1++].set(posXRoute2, curPosYRoute2--, DIR_UP); + } + if (curPosYRoute2 != destY) + break; + + curPosX = posXRoute2; + while (curPosX != destX) { + if (checkCollisionLine(curPosX, destY, &collDataIdxRoute2, &collLineIdxRoute2, 0, _linesNumb)) { + if (collLineIdxRoute2 <= _lastLine) + break; + + int tmpRouteIdx = computeRouteIdx(collLineIdxRoute2, collDataIdxRoute2, curPosX, destY, destX, destY, idxRoute1, _testRoute2); + if (tmpRouteIdx == -1) { + useRoute2(idxRoute1, curRouteIdx); + return 1; + } + idxRoute1 = tmpRouteIdx; + if (_newPosX != -1 && _newPosY != -1) + break; + } + + if (curPosX < destX) + _testRoute2[idxRoute1++].set(curPosX++, destY, DIR_RIGHT); + else + _testRoute2[idxRoute1++].set(curPosX--, destY, DIR_LEFT); + } + if (curPosX == destX) { + collLineIdxRoute2 = -1; + _testRoute2[idxRoute1].invalidate(); + useRoute2(idxRoute1, curRouteIdx); + return 1; + } + if (collLineIdxRoute2 <= _lastLine) + break; + + posXRoute2 = _newPosX; + posYRoute2 = _newPosY; + colResult = checkCollisionLine(_newPosX, _newPosY, &collDataIdxRoute2, &collLineIdxRoute2, 0, _lastLine); + if (colResult && collLineIdxRoute2 <= _lastLine) + break; + } + + _testRoute2[idxRoute1].invalidate(); + + if (!dummyLineFl) { + if (endLineIdx > foundLineIdx) { + if (_testRoute0[0]._x != -1 && collLineIdxRoute0 > foundLineIdx && collLineIdxRoute1 <= collLineIdxRoute0 && collLineIdxRoute2 <= collLineIdxRoute0 && endLineIdx >= collLineIdxRoute0) { + _newLineIdx = collLineIdxRoute0; + _newLineDataIdx = collDataIdxRoute0; + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute0[i++]; + } while (_testRoute0[i].isValid()); + _newRouteIdx = curRouteIdx; + return 2; + } + if (_testRoute1[0]._x != -1 && foundLineIdx < collLineIdxRoute1 && collLineIdxRoute2 <= collLineIdxRoute1 && collLineIdxRoute0 <= collLineIdxRoute1 && endLineIdx >= collLineIdxRoute1) { + _newLineIdx = collLineIdxRoute1; + _newLineDataIdx = collDataIdxRoute1; + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute1[i++]; + } while (_testRoute1[i].isValid()); + _newRouteIdx = curRouteIdx; + return 2; + } + if (_testRoute2[0]._x != -1 && foundLineIdx < collLineIdxRoute2 && collLineIdxRoute1 < collLineIdxRoute2 && collLineIdxRoute0 < collLineIdxRoute2 && endLineIdx >= collLineIdxRoute2) { + _newLineIdx = collLineIdxRoute2; + _newLineDataIdx = collDataIdxRoute2; + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute2[i++]; + } while (_testRoute2[i].isValid()); + _newRouteIdx = curRouteIdx; + return 2; + } + } + if (endLineIdx < foundLineIdx) { + if (collLineIdxRoute0 == -1) + collLineIdxRoute0 = INVALID_LINE_VALUE; + if (collLineIdxRoute1 == -1) + collLineIdxRoute0 = INVALID_LINE_VALUE; + if (collLineIdxRoute2 == -1) + collLineIdxRoute0 = INVALID_LINE_VALUE; + if (_testRoute1[0]._x != -1 && collLineIdxRoute1 < foundLineIdx && collLineIdxRoute2 >= collLineIdxRoute1 && collLineIdxRoute0 >= collLineIdxRoute1 && endLineIdx <= collLineIdxRoute1) { + _newLineIdx = collLineIdxRoute1; + _newLineDataIdx = collDataIdxRoute1; + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute1[i++]; + } while (_testRoute1[i].isValid()); + _newRouteIdx = curRouteIdx; + return 2; + } + if (_testRoute2[0]._x != -1 && foundLineIdx > collLineIdxRoute2 && collLineIdxRoute1 >= collLineIdxRoute2 && collLineIdxRoute0 >= collLineIdxRoute2 && endLineIdx <= collLineIdxRoute2) { + _newLineIdx = collLineIdxRoute2; + _newLineDataIdx = collDataIdxRoute2; + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute2[i++]; + } while (_testRoute2[i].isValid()); + _newRouteIdx = curRouteIdx; + return 2; + } + + if (_testRoute0[0]._x != -1 && foundLineIdx > collLineIdxRoute0 && collLineIdxRoute1 >= collLineIdxRoute0 && collLineIdxRoute2 >= collLineIdxRoute0 && endLineIdx <= collLineIdxRoute0) { + _newLineIdx = collLineIdxRoute0; + _newLineDataIdx = collDataIdxRoute0; + int i = 0; + do { + assert(curRouteIdx <= 8000); + _bestRoute[curRouteIdx++] = _testRoute0[i++]; + } while (_testRoute0[i].isValid()); + _newRouteIdx = curRouteIdx; + return 2; + } + } + } + return 0; +} + +RouteItem *LinesManager::cityMapCarRoute(int x1, int y1, int x2, int y2) { + debugC(5, kDebugPath, "cityMapCarRoute(%d, %d, %d, %d)", x1, y1, x2, y2); + RouteItem *result; + int arrDelta[10]; + int arrDataIdx[10]; + int arrLineIdx[10]; + + int clipX2 = x2; + int clipY2 = y2; + if (x2 <= 14) + clipX2 = 15; + if (y2 <= 14) + clipY2 = 15; + if (clipX2 > _vm->_graphicsMan->_maxX - 10) + clipX2 = _vm->_graphicsMan->_maxX - 10; + if (clipY2 > 445) + clipY2 = 440; + + int delta = 0; + for (delta = 0; clipY2 + delta < _vm->_graphicsMan->_maxY; delta++) { + if (checkCollisionLine(clipX2, clipY2 + delta, &arrDataIdx[DIR_DOWN], &arrLineIdx[DIR_DOWN], 0, _lastLine) && arrLineIdx[DIR_DOWN] <= _lastLine) + break; + arrDataIdx[DIR_DOWN] = 0; + arrLineIdx[DIR_DOWN] = -1; + } + arrDelta[DIR_DOWN] = delta; + + for (delta = 0; clipY2 - delta > _vm->_graphicsMan->_minY; delta++) { + if (checkCollisionLine(clipX2, clipY2 - delta , &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _lastLine) && arrLineIdx[DIR_UP] <= _lastLine) + break; + arrDataIdx[DIR_UP] = 0; + arrLineIdx[DIR_UP] = -1; + if (arrDelta[DIR_DOWN] < delta && arrLineIdx[DIR_DOWN] != -1) + break; + } + arrDelta[DIR_UP] = delta; + + for (delta = 0; clipX2 + delta < _vm->_graphicsMan->_maxX; delta++) { + if (checkCollisionLine(clipX2 + delta, clipY2, &arrDataIdx[DIR_RIGHT], &arrLineIdx[DIR_RIGHT], 0, _lastLine) && arrLineIdx[DIR_RIGHT] <= _lastLine) + break; + arrDataIdx[DIR_RIGHT] = 0; + arrLineIdx[DIR_RIGHT] = -1; + if ((arrDelta[DIR_UP] <= delta && arrLineIdx[DIR_UP] != -1) || (arrDelta[DIR_DOWN] <= delta && arrLineIdx[DIR_DOWN] != -1)) + break; + } + arrDelta[DIR_RIGHT] = delta; + + for (delta = 0; clipX2 - delta > _vm->_graphicsMan->_minX; delta++) { + if (checkCollisionLine(clipX2 - delta, clipY2, &arrDataIdx[DIR_LEFT], &arrLineIdx[DIR_LEFT], 0, _lastLine) && arrLineIdx[DIR_LEFT] <= _lastLine) + break; + arrDataIdx[DIR_LEFT] = 0; + arrLineIdx[DIR_LEFT] = -1; + if ((arrDelta[DIR_UP] <= delta && arrLineIdx[DIR_UP] != -1) || (arrDelta[DIR_RIGHT] <= delta && arrLineIdx[DIR_RIGHT] != -1) || (arrDelta[DIR_DOWN] <= delta && arrLineIdx[DIR_DOWN] != -1)) + break; + } + arrDelta[DIR_LEFT] = delta; + + if (arrLineIdx[DIR_UP] == -1) + arrDelta[DIR_UP] = INVALID_LINE_VALUE; + if (arrLineIdx[DIR_RIGHT] == -1) + arrDelta[DIR_RIGHT] = INVALID_LINE_VALUE; + if (arrLineIdx[DIR_DOWN] == -1) + arrDelta[DIR_DOWN] = INVALID_LINE_VALUE; + if (arrLineIdx[DIR_LEFT] == -1) + arrDelta[DIR_LEFT] = INVALID_LINE_VALUE; + if (arrLineIdx[DIR_UP] != -1 || arrLineIdx[DIR_RIGHT] != -1 || arrLineIdx[DIR_DOWN] != -1 || arrLineIdx[DIR_LEFT] != -1) { + int curLineDataIdx = 0; + int curLineIdx = 0; + if (arrLineIdx[DIR_DOWN] != -1 && arrDelta[DIR_UP] >= arrDelta[DIR_DOWN] && arrDelta[DIR_RIGHT] >= arrDelta[DIR_DOWN] && arrDelta[DIR_LEFT] >= arrDelta[DIR_DOWN]) { + curLineIdx = arrLineIdx[DIR_DOWN]; + curLineDataIdx = arrDataIdx[DIR_DOWN]; + } else if (arrLineIdx[DIR_UP] != -1 && arrDelta[DIR_DOWN] >= arrDelta[DIR_UP] && arrDelta[DIR_RIGHT] >= arrDelta[DIR_UP] && arrDelta[DIR_LEFT] >= arrDelta[DIR_UP]) { + curLineIdx = arrLineIdx[DIR_UP]; + curLineDataIdx = arrDataIdx[DIR_UP]; + } else if (arrLineIdx[DIR_RIGHT] != -1 && arrDelta[DIR_UP] >= arrDelta[DIR_RIGHT] && arrDelta[DIR_DOWN] >= arrDelta[DIR_RIGHT] && arrDelta[DIR_LEFT] >= arrDelta[DIR_RIGHT]) { + curLineIdx = arrLineIdx[DIR_RIGHT]; + curLineDataIdx = arrDataIdx[DIR_RIGHT]; + } else if (arrLineIdx[DIR_LEFT] != -1 && arrDelta[DIR_DOWN] >= arrDelta[DIR_LEFT] && arrDelta[DIR_RIGHT] >= arrDelta[DIR_LEFT] && arrDelta[DIR_UP] >= arrDelta[DIR_LEFT]) { + curLineIdx = arrLineIdx[DIR_LEFT]; + curLineDataIdx = arrDataIdx[DIR_LEFT]; + } + + for (int i = 0; i <= 8; i++) { + arrLineIdx[i] = -1; + arrDataIdx[i] = 0; + arrDelta[i] = INVALID_LINE_VALUE; + } + + int superRouteIdx = 0; + int curRouteDataIdx = 0; + int curRouteLineIdx = 0; + if (checkCollisionLine(x1, y1, &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _lastLine)) { + curRouteLineIdx = arrLineIdx[DIR_UP]; + curRouteDataIdx = arrDataIdx[DIR_UP]; + } else if (checkCollisionLine(x1, y1, &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _linesNumb)) { + int curRouteIdx = 0; + int curRouteX; + for (;;) { + curRouteX = _testRoute2[curRouteIdx]._x; + int curRouteY = _testRoute2[curRouteIdx]._y; + Directions curRouteDir = _testRoute2[curRouteIdx]._dir; + curRouteIdx++; + + if (checkCollisionLine(curRouteX, curRouteY, &arrDataIdx[DIR_UP], &arrLineIdx[DIR_UP], 0, _lastLine)) + break; + + _bestRoute[superRouteIdx].set(curRouteX, curRouteY, curRouteDir); + + _testRoute0[superRouteIdx].set(curRouteX, curRouteY, curRouteDir); + superRouteIdx++; + if (curRouteX == -1) + break;; + } + if (curRouteX != -1) { + curRouteLineIdx = arrLineIdx[DIR_UP]; + curRouteDataIdx = arrDataIdx[DIR_UP]; + } + } else { + curRouteLineIdx = 1; + curRouteDataIdx = 1; + superRouteIdx = 0; + } + bool loopFl = true; + while (loopFl) { + loopFl = false; + if (curRouteLineIdx < curLineIdx) { + superRouteIdx = _lineItem[curRouteLineIdx].appendToRouteInc(curRouteDataIdx, _lineItem[curRouteLineIdx]._lineDataEndIdx - 2, _bestRoute, superRouteIdx); + for (int j = curRouteLineIdx + 1; j < curLineIdx; ++j) { + if (PLAN_TEST(_lineItem[j]._lineData[0], _lineItem[j]._lineData[1], superRouteIdx, j, curLineIdx)) { + curRouteLineIdx = _newLineIdx; + curRouteDataIdx = _newLineDataIdx; + superRouteIdx = _newRouteIdx; + loopFl = true; + break; + } + if (_lineItem[j]._lineDataEndIdx - 2 > 0) { + superRouteIdx = _lineItem[j].appendToRouteInc(0, _lineItem[j]._lineDataEndIdx - 2, _bestRoute, superRouteIdx); + } + } + if (loopFl) + continue; + curRouteDataIdx = 0; + curRouteLineIdx = curLineIdx; + } + if (curRouteLineIdx > curLineIdx) { + superRouteIdx = _lineItem[curRouteLineIdx].appendToRouteDec(curRouteDataIdx, 0, _bestRoute, superRouteIdx); + for (int l = curRouteLineIdx - 1; l > curLineIdx; --l) { + if (PLAN_TEST(_lineItem[l]._lineData[2 * _lineItem[l]._lineDataEndIdx - 2], _lineItem[l]._lineData[2 * _lineItem[l]._lineDataEndIdx - 1], superRouteIdx, l, curLineIdx)) { + curRouteLineIdx = _newLineIdx; + curRouteDataIdx = _newLineDataIdx; + superRouteIdx = _newRouteIdx; + loopFl = true; + break; + } + + superRouteIdx = _lineItem[l].appendToRouteDec(_lineItem[l]._lineDataEndIdx - 2, 0, _bestRoute, superRouteIdx); + } + if (loopFl) + continue; + + curRouteDataIdx = _lineItem[curLineIdx]._lineDataEndIdx - 1; + curRouteLineIdx = curLineIdx; + } + if (curRouteLineIdx == curLineIdx) { + if (curRouteDataIdx <= curLineDataIdx) { + superRouteIdx = _lineItem[curLineIdx].appendToRouteInc(curRouteDataIdx, curLineDataIdx, _bestRoute, superRouteIdx); + } else { + superRouteIdx = _lineItem[curLineIdx].appendToRouteDec(curRouteDataIdx, curLineDataIdx, _bestRoute, superRouteIdx); + } + } + } + _bestRoute[superRouteIdx].invalidate(); + result = &_bestRoute[0]; + } else { + result = NULL; + } + return result; +} + +bool LinesManager::checkSmoothMove(int fromX, int fromY, int destX, int destY) { + debugC(5, kDebugPath, "checkSmoothMove(%d, %d, %d, %d)", fromX, fromY, destX, destY); + int distX = abs(fromX - destX) + 1; + int distY = abs(fromY - destY) + 1; + if (distX > distY) + distY = distX; + if (distY <= 10) + return true; + + int stepX = 1000 * distX / (distY - 1); + int stepY = 1000 * distY / (distY - 1); + if (destX < fromX) + stepX = -stepX; + if (destY < fromY) + stepY = -stepY; + + int smoothPosX = 1000 * fromX; + int smoothPosY = 1000 * fromY; + int newPosX = fromX; + int newPosY = fromY; + + if (distY + 1 > 0) { + int stepCount = 0; + int foundLineIdx; + int foundDataIdx; + while (!checkCollisionLine(newPosX, newPosY, &foundDataIdx, &foundLineIdx, 0, _linesNumb) || foundLineIdx > _lastLine) { + smoothPosX += stepX; + smoothPosY += stepY; + newPosX = smoothPosX / 1000; + newPosY = smoothPosY / 1000; + ++stepCount; + if (stepCount >= distY + 1) + return false; + } + return true; + } + return false; +} + +bool LinesManager::makeSmoothMove(int fromX, int fromY, int destX, int destY) { + debugC(5, kDebugPath, "makeSmoothMove(%d, %d, %d, %d)", fromX, fromY, destX, destY); + int curX = fromX; + int curY = fromY; + if (fromX > destX && destY > fromY) { + int hopkinsIdx = 36; + int smoothIdx = 0; + int stepCount = 0; + while (curX > destX && destY > curY) { + int realSpeedX = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedX; + int realSpeedY = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedY; + int spriteSize = _vm->_globals->_spriteSize[curY]; + if (spriteSize < 0) { + realSpeedX = _vm->_graphicsMan->zoomOut(realSpeedX, -spriteSize); + realSpeedY = _vm->_graphicsMan->zoomOut(realSpeedY, -spriteSize); + } else if (spriteSize > 0) { + realSpeedX = _vm->_graphicsMan->zoomIn(realSpeedX, spriteSize); + realSpeedY = _vm->_graphicsMan->zoomIn(realSpeedY, spriteSize); + } + int oldY = curY; + for (int i = 0; i < realSpeedX; i++) { + --curX; + _smoothRoute[smoothIdx]._posX = curX; + if (curY != oldY + realSpeedY) + curY++; + _smoothRoute[smoothIdx]._posY = curY; + smoothIdx++; + } + ++hopkinsIdx; + if (hopkinsIdx == 48) + hopkinsIdx = 36; + ++stepCount; + } + if (stepCount > 5) { + _smoothRoute[smoothIdx]._posX = -1; + _smoothRoute[smoothIdx]._posY = -1; + _smoothMoveDirection = DIR_DOWN_LEFT; + return false; + } + } else if (fromX < destX && destY > fromY) { + int hopkinsIdx = 36; + int smoothIdx = 0; + int stepCount = 0; + while (curX < destX && destY > curY) { + int realSpeedX = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedX; + int realSpeedY = _vm->_globals->_hopkinsItem[hopkinsIdx]._speedY; + int spriteSize = _vm->_globals->_spriteSize[curY]; + if (spriteSize < 0) { + realSpeedX = _vm->_graphicsMan->zoomOut(realSpeedX, -spriteSize); + realSpeedY = _vm->_graphicsMan->zoomOut(realSpeedY, -spriteSize); + } else if (spriteSize > 0) { + realSpeedX = _vm->_graphicsMan->zoomIn(realSpeedX, spriteSize); + realSpeedY = _vm->_graphicsMan->zoomIn(realSpeedY, spriteSize); + } + int oldY = curY; + for (int i = 0; i < realSpeedX; i++) { + ++curX; + _smoothRoute[smoothIdx]._posX = curX; + if (curY != oldY + realSpeedY) + curY++; + _smoothRoute[smoothIdx]._posY = curY; + smoothIdx++; + } + ++hopkinsIdx; + if (hopkinsIdx == 48) + hopkinsIdx = 36; + ++stepCount; + } + if (stepCount > 5) { + _smoothRoute[smoothIdx]._posX = -1; + _smoothRoute[smoothIdx]._posY = -1; + _smoothMoveDirection = DIR_DOWN_RIGHT; + return false; + } + } else if (fromX > destX && destY < fromY) { + int hopkinsIdx = 12; + int smoothIdx = 0; + int stepCount = 0; + while (curX > destX && destY < curY) { + int realSpeedX = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedX, 25); + int realSpeedY = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedY, 25); + int oldY = curY; + for (int i = 0; i < realSpeedX; i++) { + --curX; + _smoothRoute[smoothIdx]._posX = curX; + if ((uint16)curY != (uint16)oldY + realSpeedY) + curY--; + _smoothRoute[smoothIdx]._posY = curY; + smoothIdx++; + } + ++hopkinsIdx; + if (hopkinsIdx == 24) + hopkinsIdx = 12; + ++stepCount; + } + if (stepCount > 5) { + _smoothRoute[smoothIdx]._posX = -1; + _smoothRoute[smoothIdx]._posY = -1; + _smoothMoveDirection = DIR_UP_LEFT; + return false; + } + } else if (fromX < destX && destY < fromY) { + int hopkinsIdx = 12; + int smoothIdx = 0; + int stepCount = 0; + while (curX < destX && destY < curY) { + int oldY = curY; + int realSpeedX = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedX, 25); + int realSpeedY = _vm->_graphicsMan->zoomOut(_vm->_globals->_hopkinsItem[hopkinsIdx]._speedY, 25); + for (int i = 0; i < realSpeedX; i++) { + ++curX; + _smoothRoute[smoothIdx]._posX = curX; + if ((uint16)curY != (uint16)oldY + realSpeedY) + curY--; + _smoothRoute[smoothIdx]._posY = curY; + smoothIdx++; + } + ++hopkinsIdx; + if (hopkinsIdx == 24) + hopkinsIdx = 12; + ++stepCount; + } + + if (stepCount > 5) { + _smoothRoute[smoothIdx]._posX = -1; + _smoothRoute[smoothIdx]._posY = -1; + _smoothMoveDirection = DIR_UP_RIGHT; + return false; + } + } + return true; +} + +bool LinesManager::PLAN_TEST(int paramX, int paramY, int superRouteIdx, int paramStartLineIdx, int paramEndLineIdx) { + debugC(5, kDebugPath, "PLAN_TEST(%d, %d, %d, %d, %d)", paramX, paramY, superRouteIdx, paramStartLineIdx, paramEndLineIdx); + int sideTestUp; + int sideTestDown; + int sideTestLeft; + int sideTestRight; + int lineIdxTestUp; + int lineIdxTestDown; + int lineIdxTestLeft; + int lineIdxTestRight; + int dataIdxTestUp; + int dataIdxTestDown; + int dataIdxTestLeft; + int dataIdxTestRight; + + int idxTestUp = testLine(paramX, paramY - 2, &sideTestUp, &lineIdxTestUp, &dataIdxTestUp); + int idxTestDown = testLine(paramX, paramY + 2, &sideTestDown, &lineIdxTestDown, &dataIdxTestDown); + int idxTestLeft = testLine(paramX - 2, paramY, &sideTestLeft, &lineIdxTestLeft, &dataIdxTestLeft); + int idxTestRight = testLine(paramX + 2, paramY, &sideTestRight, &lineIdxTestRight, &dataIdxTestRight); + if (idxTestUp == -1 && idxTestDown == -1 && idxTestLeft == -1 && idxTestRight == -1) + return false; + + // Direction: 1 = Up, 2 = Down, 3 = Left, 4 = Right + int direction; + if (paramStartLineIdx == -1 || paramEndLineIdx == -1) { + if (idxTestUp != -1) + direction = 1; + else if (idxTestDown != -1) + direction = 2; + else if (idxTestLeft != -1) + direction = 3; + else if (idxTestRight != -1) + direction = 4; + else + return false; + } else { + int stepCountUp = 100; + int stepCountDown = 100; + int stepCountLeft = 100; + int stepCountRight = 100; + int paramStepCount = abs(paramStartLineIdx - paramEndLineIdx); + if (idxTestUp != -1) { + stepCountUp = abs(lineIdxTestUp - paramEndLineIdx); + } + if (idxTestDown != -1) { + stepCountDown = abs(lineIdxTestDown - paramEndLineIdx); + } + if (idxTestLeft != -1) { + stepCountLeft = abs(lineIdxTestLeft - paramEndLineIdx); + } + if (idxTestRight != -1) { + stepCountRight = abs(lineIdxTestRight - paramEndLineIdx); + } + + if (stepCountUp < paramStepCount && stepCountUp <= stepCountDown && stepCountUp <= stepCountLeft && stepCountUp <= stepCountRight) + direction = 1; + else if (paramStepCount > stepCountDown && stepCountUp >= stepCountDown && stepCountLeft >= stepCountDown && stepCountRight >= stepCountDown) + direction = 2; + else if (stepCountLeft < paramStepCount && stepCountLeft <= stepCountUp && stepCountLeft <= stepCountDown && stepCountLeft <= stepCountRight) + direction = 3; + else if (stepCountRight < paramStepCount && stepCountRight <= stepCountUp && stepCountRight <= stepCountDown && stepCountRight <= stepCountLeft) + direction = 4; + else + return false; + } + + int sideTest = 0; + int idxTest = 0; + if (direction == 1) { + idxTest = idxTestUp; + sideTest = sideTestUp; + _newLineIdx = lineIdxTestUp; + _newLineDataIdx = dataIdxTestUp; + } else if (direction == 2) { + idxTest = idxTestDown; + sideTest = sideTestDown; + _newLineIdx = lineIdxTestDown; + _newLineDataIdx = dataIdxTestDown; + } else if (direction == 3) { + idxTest = idxTestLeft; + sideTest = sideTestLeft; + _newLineIdx = lineIdxTestLeft; + _newLineDataIdx = dataIdxTestLeft; + } else if (direction == 4) { + idxTest = idxTestRight; + sideTest = sideTestRight; + _newLineIdx = lineIdxTestRight; + _newLineDataIdx = dataIdxTestRight; + } + + int routeIdx = superRouteIdx; + if (sideTest == 1) { + routeIdx = _lineItem[idxTest].appendToRouteInc(0, -1, _bestRoute, routeIdx); + } else if (sideTest == 2) { + routeIdx = _lineItem[idxTest].appendToRouteDec(-1, -1, _bestRoute, routeIdx); + } + _newRouteIdx = routeIdx; + return true; +} + +// Test line +int LinesManager::testLine(int paramX, int paramY, int *testValue, int *foundLineIdx, int *foundDataIdx) { + debugC(5, kDebugPath, "testLine(%d, %d, testValue, foundLineIdx, foundDataIdx)", paramX, paramY); + int16 *lineData; + int lineDataEndIdx; + int collLineIdx; + int collDataIdx; + + for (int idx = _lastLine + 1; idx < _linesNumb + 1; idx++) { + lineData = _lineItem[idx]._lineData; + lineDataEndIdx = _lineItem[idx]._lineDataEndIdx; + if (!lineData) + continue; + + if (lineData[0] == paramX && lineData[1] == paramY) { + *testValue = 1; + int posX = lineData[2 * (lineDataEndIdx - 1)]; + int posY = lineData[2 * (lineDataEndIdx - 1) + 1]; + if (_lineItem[idx]._directionRouteInc == DIR_DOWN || _lineItem[idx]._directionRouteInc == DIR_UP) + posY += 2; + if (_lineItem[idx]._directionRouteInc == DIR_RIGHT || _lineItem[idx]._directionRouteDec == DIR_LEFT) + posX += 2; + if (!checkCollisionLine(posX, posY, &collDataIdx, &collLineIdx, 0, _lastLine)) + error("Error in test line"); + *foundLineIdx = collLineIdx; + *foundDataIdx = collDataIdx; + return idx; + } + + if (lineDataEndIdx > 0) { + if (lineData[2 * (lineDataEndIdx - 1)] == paramX && lineData[2 * (lineDataEndIdx - 1) + 1] == paramY) { + *testValue = 2; + int posX = lineData[0]; + int posY = lineData[1]; + if (_lineItem[idx]._directionRouteInc == DIR_DOWN || _lineItem[idx]._directionRouteInc == DIR_UP) + posY -= 2; + if (_lineItem[idx]._directionRouteInc == DIR_RIGHT || _lineItem[idx]._directionRouteDec == DIR_LEFT) + posX -= 2; + if (!checkCollisionLine(posX, posY, &collDataIdx, &collLineIdx, 0, _lastLine)) + error("Error in test line"); + *foundLineIdx = collLineIdx; + *foundDataIdx = collDataIdx; + return idx; + } + } + } + return -1; +} + +int LinesManager::computeYSteps(int idx) { + debugC(5, kDebugPath, "computeYSteps(%d)", idx); + int zoomPct = _vm->_globals->_spriteSize[idx]; + if (_vm->_globals->_characterType == CHARACTER_HOPKINS_CLONE) { + if (zoomPct < 0) + zoomPct = -zoomPct; + zoomPct = 20 * (5 * zoomPct - 100) / -80; + } else if (_vm->_globals->_characterType == CHARACTER_SAMANTHA) { + if (zoomPct < 0) + zoomPct = -zoomPct; + zoomPct = 20 * (5 * zoomPct - 165) / -67; + } + + int retVal = 25; + if (zoomPct < 0) + retVal = _vm->_graphicsMan->zoomOut(25, -zoomPct); + else if (zoomPct > 0) + retVal = _vm->_graphicsMan->zoomIn(25, zoomPct); + + return retVal; +} + +void LinesManager::optimizeRoute(RouteItem *route) { + debugC(5, kDebugPath, "optimizeRoute(route)"); + if (route[0]._x == -1 && route[0]._y == -1) + return; + + int routeIdx = 0; + Directions oldDir = DIR_NONE; + int route0Y = route[0]._y; + Directions curDir = route[0]._dir; + + for (;;) { + if (oldDir != DIR_NONE && curDir != oldDir) { + int oldRouteIdx = routeIdx; + int routeCount = 0; + int yStep = computeYSteps(route0Y); + int curRouteX = route[routeIdx]._x; + int curRouteY = route[routeIdx]._y; + while (curRouteX != -1 || curRouteY != -1) { + int idx = routeIdx; + ++routeIdx; + ++routeCount; + if (route[idx]._dir != curDir) + break; + curRouteX = route[routeIdx]._x; + curRouteY = route[routeIdx]._y; + } + if (routeCount < yStep) { + int idx = oldRouteIdx; + for (int i = 0; i < routeCount; i++) { + route[idx]._dir = oldDir; + idx++; + } + curDir = oldDir; + } + routeIdx = oldRouteIdx; + if (curRouteX == -1 && curRouteY == -1) + break; + } + routeIdx++; + oldDir = curDir; + route0Y = route[routeIdx]._y; + curDir = route[routeIdx]._dir; + if (route[routeIdx]._x == -1 && route0Y == -1) + break; + } +} + +int LinesManager::getMouseZone() { + debugC(9, kDebugPath, "getMouseZone()"); + int result; + + int xp = _vm->_events->_mousePos.x + _vm->_events->_mouseOffset.x; + int yp = _vm->_events->_mousePos.y + _vm->_events->_mouseOffset.y; + if ((_vm->_events->_mousePos.y + _vm->_events->_mouseOffset.y) > 19) { + for (int bobZoneId = 0; bobZoneId <= 48; bobZoneId++) { + int bobId = _bobZone[bobZoneId]; + if (bobId && _bobZoneFl[bobZoneId] && _vm->_objectsMan->_bob[bobId]._bobMode && _vm->_objectsMan->_bob[bobId]._frameIndex != 250 && + !_vm->_objectsMan->_bob[bobId]._disabledAnimationFl && xp > _vm->_objectsMan->_bob[bobId]._oldX && + xp < _vm->_objectsMan->_bob[bobId]._oldWidth + _vm->_objectsMan->_bob[bobId]._oldX && yp > _vm->_objectsMan->_bob[bobId]._oldY) { + if (yp < _vm->_objectsMan->_bob[bobId]._oldHeight + _vm->_objectsMan->_bob[bobId]._oldY) { + if (_zone[bobZoneId]._spriteIndex == -1) { + _zone[bobZoneId]._destX = 0; + _zone[bobZoneId]._destY = 0; + } + if (!_zone[bobZoneId]._destX && !_zone[bobZoneId]._destY) { + _zone[bobZoneId]._destX = _vm->_objectsMan->_bob[bobId]._oldWidth + _vm->_objectsMan->_bob[bobId]._oldX; + _zone[bobZoneId]._destY = _vm->_objectsMan->_bob[bobId]._oldHeight + _vm->_objectsMan->_bob[bobId]._oldY + 6; + _zone[bobZoneId]._spriteIndex = -1; + } + + // WORKAROUND: Avoid allowing hotspots that should remain non-interactive + if (bobZoneId == 24 && _vm->_globals->_curRoomNum == 14) + continue; + + return bobZoneId; + } + } + } + _currentSegmentId = 0; + for (int squareZoneId = 0; squareZoneId <= 99; squareZoneId++) { + if (_zone[squareZoneId]._enabledFl && _squareZone[squareZoneId]._enabledFl + && _squareZone[squareZoneId]._left <= xp && _squareZone[squareZoneId]._right >= xp + && _squareZone[squareZoneId]._top <= yp && _squareZone[squareZoneId]._bottom >= yp) { + if (_squareZone[squareZoneId]._squareZoneFl) + return _zoneLine[_squareZone[squareZoneId]._minZoneLineIdx]._bobZoneIdx; + + _segment[_currentSegmentId]._minZoneLineIdx = _squareZone[squareZoneId]._minZoneLineIdx; + _segment[_currentSegmentId]._maxZoneLineIdx = _squareZone[squareZoneId]._maxZoneLineIdx; + ++_currentSegmentId; + } + } + if (!_currentSegmentId) + return -1; + + + int colRes1 = 0; + for (int yCurrent = yp; yCurrent >= 0; --yCurrent) { + colRes1 = checkCollision(xp, yCurrent); + if (colRes1 != -1 && _zone[colRes1]._enabledFl) + break; + } + + if (colRes1 == -1) + return -1; + + int colRes2 = 0; + for (int j = yp; j < _vm->_graphicsMan->_maxY; ++j) { + colRes2 = checkCollision(xp, j); + if (colRes2 != -1 && _zone[colRes1]._enabledFl) + break; + } + + if (colRes2 == -1) + return -1; + + int colRes3 = 0; + for (int k = xp; k >= 0; --k) { + colRes3 = checkCollision(k, yp); + if (colRes3 != -1 && _zone[colRes1]._enabledFl) + break; + } + if (colRes3 == -1) + return -1; + + int colRes4 = 0; + for (int xCurrent = xp; _vm->_graphicsMan->_maxX > xCurrent; ++xCurrent) { + colRes4 = checkCollision(xCurrent, yp); + if (colRes4 != -1 && _zone[colRes1]._enabledFl) + break; + } + if (colRes1 == colRes2 && colRes1 == colRes3 && colRes1 == colRes4) + result = colRes1; + else + result = -1; + + } else { + result = 0; + } + return result; +} + +int LinesManager::checkCollision(int xp, int yp) { + debugC(7, kDebugPath, "checkCollision(%d, %d)", xp, yp); + if (_currentSegmentId <= 0) + return -1; + + int xMax = xp + 4; + int xMin = xp - 4; + + for (int idx = 0; idx <= _currentSegmentId; ++idx) { + int curZoneLineIdx = _segment[idx]._minZoneLineIdx; + if (_segment[idx]._maxZoneLineIdx < curZoneLineIdx) + continue; + + int yMax = yp + 4; + int yMin = yp - 4; + + do { + LigneZoneItem *curZoneLine = &_zoneLine[curZoneLineIdx]; + int16 *dataP = curZoneLine->_zoneData; + if (dataP) { + int count = curZoneLine->_count; + int startX = dataP[0]; + int startY = dataP[1]; + int destX = dataP[count * 2 - 2]; + int destY = dataP[count * 2 - 1]; + + bool flag = true; + if ((startX < destX && (xMax < startX || xMin > destX)) || + (startX >= destX && (xMin > startX || xMax < destX)) || + (startY < destY && (yMax < startY || yMin > destY)) || + (startY >= destY && (yMin > startY || yMax < destY))) + flag = false; + + if (flag && curZoneLine->_count > 0) { + for (int i = 0; i < count; ++i) { + int xCheck = *dataP++; + int yCheck = *dataP++; + + if ((xp == xCheck || (xp + 1) == xCheck) && (yp == yCheck)) + return curZoneLine->_bobZoneIdx; + } + } + } + } while (++curZoneLineIdx <= _segment[idx]._maxZoneLineIdx); + } + + return -1; +} + +// Square Zone +void LinesManager::initSquareZones() { + debugC(5, kDebugPath, "initSquareZones()"); + for (int idx = 0; idx < 100; ++idx) { + SquareZoneItem *curZone = &_squareZone[idx]; + curZone->_enabledFl = false; + curZone->_squareZoneFl = false; + curZone->_left = 1280; + curZone->_right = 0; + curZone->_top = 460; + curZone->_bottom = 0; + curZone->_minZoneLineIdx = 401; + curZone->_maxZoneLineIdx = 0; + } + + for (int idx = 0; idx < MAX_LINES + 1; ++idx) { + int16 *dataP = _zoneLine[idx]._zoneData; + if (dataP == NULL) + continue; + + SquareZoneItem *curZone = &_squareZone[_zoneLine[idx]._bobZoneIdx]; + curZone->_enabledFl = true; + curZone->_maxZoneLineIdx = MAX(curZone->_maxZoneLineIdx, idx); + curZone->_minZoneLineIdx = MIN(curZone->_minZoneLineIdx, idx); + + for (int i = 0; i < _zoneLine[idx]._count; i++) { + int zoneX = *dataP++; + int zoneY = *dataP++; + + curZone->_left = MIN(curZone->_left, zoneX); + curZone->_right = MAX(curZone->_right, zoneX); + curZone->_top = MIN(curZone->_top, zoneY); + curZone->_bottom = MAX(curZone->_bottom, zoneY); + } + } + + for (int idx = 0; idx < 100; idx++) { + int zoneWidth = abs(_squareZone[idx]._left - _squareZone[idx]._right); + int zoneHeight = abs(_squareZone[idx]._top - _squareZone[idx]._bottom); + if (zoneWidth == zoneHeight) + _squareZone[idx]._squareZoneFl = true; + } +} + +void LinesManager::clearAll() { + debugC(5, kDebugPath, "clearAll()"); + for (int idx = 0; idx < 105; ++idx) { + _zone[idx]._destX = 0; + _zone[idx]._destY = 0; + _zone[idx]._spriteIndex = 0; + } + + _testRoute0 = NULL; + _testRoute1 = NULL; + _testRoute2 = NULL; + _lineBuf = NULL; + _route = NULL; + + for (int idx = 0; idx < MAX_LINES; ++idx) { + _lineItem[idx]._lineDataEndIdx = 0; + _lineItem[idx]._direction = DIR_NONE; + _lineItem[idx]._directionRouteInc = DIR_NONE; + _lineItem[idx]._directionRouteDec = DIR_NONE; + _lineItem[idx]._lineData = NULL; + + _zoneLine[idx]._count = 0; + _zoneLine[idx]._bobZoneIdx = 0; + _zoneLine[idx]._zoneData = NULL; + } + + for (int idx = 0; idx < 100; ++idx) + _squareZone[idx]._enabledFl = false; + + _testRoute0 = new RouteItem[8334]; + _testRoute1 = new RouteItem[8334]; + _testRoute2 = new RouteItem[8334]; + if (!_testRoute0) + _testRoute0 = NULL; + if (!_testRoute1) + _testRoute1 = NULL; + if (!_testRoute2) + _testRoute2 = NULL; + + _largeBuf = _vm->_globals->allocMemory(10000); + _lineBuf = (int16 *)(_largeBuf); +} + +/** + * Clear all zones and reset nextLine + */ +void LinesManager::clearAllZones() { + debugC(5, kDebugPath, "clearAllZones()"); + for (int idx = 0; idx < MAX_LINES; ++idx) + removeZoneLine(idx); +} + +/** + * Remove Zone Line + */ +void LinesManager::removeZoneLine(int idx) { + debugC(5, kDebugPath, "removeZoneLine(%d)", idx); + assert(idx < MAX_LINES + 1); + _zoneLine[idx]._zoneData = (int16 *)_vm->_globals->freeMemory((byte *)_zoneLine[idx]._zoneData); +} + +void LinesManager::resetLines() { + debugC(5, kDebugPath, "resetLines()"); + for (int idx = 0; idx < MAX_LINES; ++idx) { + _lineItem[idx]._lineData = (int16 *)_vm->_globals->freeMemory((byte *)_lineItem[idx]._lineData); + _lineItem[idx]._lineDataEndIdx = 0; + _lineItem[idx]._lineData = NULL; + } +} + +void LinesManager::setMaxLineIdx(int idx) { + debugC(5, kDebugPath, "setMaxLineIdx(%d)", idx); + _maxLineIdx = idx; +} + +void LinesManager::resetLastLine() { + debugC(5, kDebugPath, "resetLastLine()"); + _lastLine = 0; +} + +void LinesManager::resetLinesNumb() { + debugC(5, kDebugPath, "resetLinesNumb()"); + _linesNumb = 0; +} + +void LinesManager::enableZone(int idx) { + debugC(5, kDebugPath, "enableZone(%d)", idx); + if (_bobZone[idx]) { + _bobZoneFl[idx] = true; + } else { + _zone[idx]._enabledFl = true; + } +} + +void LinesManager::disableZone(int idx) { + debugC(5, kDebugPath, "disableZone(%d)", idx); + if (_bobZone[idx]) { + _bobZoneFl[idx] = false; + } else { + _zone[idx]._enabledFl = false; + } +} + +void LinesManager::checkZone() { + debugC(9, kDebugPath, "checkZone()"); + int mouseX = _vm->_events->getMouseX(); + int mouseY = _vm->_events->getMouseY(); + int oldMouseY = mouseY; + if (_vm->_globals->_cityMapEnabledFl + || _vm->_events->_startPos.x >= mouseX + || (mouseY = _vm->_graphicsMan->_scrollOffset + 54, mouseX >= mouseY) + || (mouseY = oldMouseY - 1, mouseY < 0 || mouseY > 59)) { + if (_vm->_objectsMan->_visibleFl) + _vm->_objectsMan->_eraseVisibleCounter = 4; + _vm->_objectsMan->_visibleFl = false; + } else { + _vm->_objectsMan->_visibleFl = true; + } + if (_vm->_objectsMan->_forceZoneFl) { + _zoneSkipCount = 100; + _oldMouseZoneId = -1; + _oldMouseX = -200; + _oldMouseY = -220; + _vm->_objectsMan->_forceZoneFl = false; + } + + _zoneSkipCount++; + if (_zoneSkipCount <= 1) + return; + + if (_vm->_globals->_freezeCharacterFl || (_route == NULL) || _zoneSkipCount > 4) { + _zoneSkipCount = 0; + int zoneId; + if (_oldMouseX != mouseX || _oldMouseY != oldMouseY) { + zoneId = getMouseZone(); + + // WORKAROUND: Incorrect hotspot zones in the guard's control room + if (_vm->_globals->_curRoomNum == 71 && (zoneId == 14 || zoneId == 12 || zoneId == 17)) + zoneId = _oldMouseZoneId; + } else { + zoneId = _oldMouseZoneId; + } + if (_oldMouseZoneId != zoneId) { + _vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100); + _vm->_events->_mouseCursorId = 4; + _vm->_events->changeMouseCursor(4); + if (_forceHideText) { + _vm->_fontMan->hideText(5); + _forceHideText = false; + return; + } + } + if (zoneId != -1) { + if (_zone[zoneId]._verbFl1 || _zone[zoneId]._verbFl2 || + _zone[zoneId]._verbFl3 || _zone[zoneId]._verbFl4 || + _zone[zoneId]._verbFl5 || _zone[zoneId]._verbFl6 || + _zone[zoneId]._verbFl7 || _zone[zoneId]._verbFl8 || + _zone[zoneId]._verbFl9 || _zone[zoneId]._verbFl10) { + if (_oldMouseZoneId != zoneId) { + _vm->_fontMan->initTextBuffers(5, _zone[zoneId]._messageId, _vm->_globals->_zoneFilename, 0, 430, 0, 0, 252); + _vm->_fontMan->showText(5); + _forceHideText = true; + } + _hotspotTextColor += 25; + if (_hotspotTextColor > 100) + _hotspotTextColor = 0; + _vm->_graphicsMan->setColorPercentage2(251, _hotspotTextColor, _hotspotTextColor, _hotspotTextColor); + if (_vm->_events->_mouseCursorId == 4) { + if (_zone[zoneId]._verbFl1 == 2) { + _vm->_events->changeMouseCursor(16); + _vm->_events->_mouseCursorId = 16; + _vm->_objectsMan->setVerb(16); + } + } + } else { + _vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100); + _vm->_events->_mouseCursorId = 4; + _vm->_events->changeMouseCursor(4); + } + } + _vm->_objectsMan->_zoneNum = zoneId; + _oldMouseX = mouseX; + _oldMouseY = oldMouseY; + _oldMouseZoneId = zoneId; + if (_vm->_globals->_freezeCharacterFl && (_vm->_events->_mouseCursorId == 4)) { + if (zoneId != -1 && zoneId != 0) + _vm->_objectsMan->handleRightButton(); + } + if ((_vm->_globals->_cityMapEnabledFl && zoneId == -1) || !zoneId) { + _vm->_objectsMan->setVerb(0); + _vm->_events->_mouseCursorId = 0; + _vm->_events->changeMouseCursor(0); + } + } +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/lines.h b/engines/hopkins/lines.h new file mode 100644 index 0000000000..b32dc6e2a5 --- /dev/null +++ b/engines/hopkins/lines.h @@ -0,0 +1,198 @@ +/* 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. + * + */ + +#ifndef HOPKINS_LINES_H +#define HOPKINS_LINES_H + +#include "hopkins/globals.h" + +#include "common/scummsys.h" +#include "common/str.h" + +namespace Hopkins { + +class HopkinsEngine; + +struct LigneZoneItem { + int _count; + int _bobZoneIdx; + int16 *_zoneData; +}; + +#define INVALID_LINE_VALUE 1300 + +#define MAX_LINES 400 + +struct RouteItem; + +struct LigneItem { + int _lineDataEndIdx; + Directions _direction; + Directions _directionRouteInc; + Directions _directionRouteDec; + int16 *_lineData; + + int appendToRouteInc(int from, int to, RouteItem *route, int index); + int appendToRouteDec(int from, int to, RouteItem *route, int index); +}; + +struct SmoothItem { + int _posX; + int _posY; +}; + +struct SegmentItem { + int _minZoneLineIdx; + int _maxZoneLineIdx; +}; + +struct SquareZoneItem { + bool _enabledFl; + int _left; + int _right; + int _top; + int _bottom; + int _minZoneLineIdx; + int _maxZoneLineIdx; + bool _squareZoneFl; +}; + +struct ZoneItem { + int _destX; + int _destY; + int _spriteIndex; + int _verbFl1; + int _verbFl2; + int _verbFl3; + int _verbFl4; + int _verbFl5; + int _verbFl6; + int _verbFl7; + int _verbFl8; + int _verbFl9; + int _verbFl10; + bool _enabledFl; + int _messageId; +}; + +struct RouteItem { + int16 _x; + int16 _y; + Directions _dir; + bool isValid() const { return _x != -1 || _y != -1; } + void invalidate() { _x = _y = -1; _dir = DIR_NONE; } + void set(int16 X, int16 Y, Directions dir) { _x = X; _y = Y; _dir = dir; } +}; + + +class LinesManager { +private: + HopkinsEngine *_vm; + + bool _forceHideText; + int _hotspotTextColor; + int _pathFindingMaxDepth; + SmoothItem _smoothRoute[4000]; + Directions _smoothMoveDirection; + LigneZoneItem _zoneLine[MAX_LINES+1]; + SegmentItem _segment[101]; + int _currentSegmentId; + int _maxLineIdx; + int _lastLine; + int _newLineIdx; + int _newLineDataIdx; + int _newRouteIdx; + int _newPosX; + int _newPosY; + int _oldMouseX, _oldMouseY; + int _oldRouteFromX; + int _oldRouteFromY; + int _oldRouteDestX; + int _oldRouteDestY; + int _oldZoneNum; + + byte *_largeBuf; + RouteItem *_testRoute0; + RouteItem *_testRoute1; + int16 *_lineBuf; + RouteItem _bestRoute[8001]; + int _zoneSkipCount; + int _oldMouseZoneId; + + int avoidObstacle(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route); + int avoidObstacleOnSegment(int lineIdx, int lineDataIdx, int routeIdx, int destLineIdx, int destLineDataIdx, RouteItem *route, int startLineIdx, int endLineIdx); + int checkInventoryHotspotsRow(int posX, int minZoneNum, bool lastRow); + void removeZoneLine(int idx); + void removeLine(int idx); + int checkCollision(int xp, int yp); + bool checkCollisionLine(int xp, int yp, int *foundDataIdx, int *foundLineIdx, int startLineIdx, int endLineIdx); + bool checkSmoothMove(int fromX, int fromY, int destX, int destY); + bool makeSmoothMove(int fromX, int fromY, int destX, int destY); + int characterRoute(int fromX, int fromY, int destX, int destY, int startLineIdx, int endLineIdx, int routeIdx); + int testLine(int paramX, int paramY, int *testValue, int *foundLineIdx, int *foundDataIdx); + void useRoute0(int idx, int curRouteIdx); + void useRoute1(int idx, int curRouteIdx); + void useRoute2(int idx, int curRouteIdx); + int computeYSteps(int idx); + int computeRouteIdx(int lineIdx, int dataIdx, int fromX, int fromY, int destX, int destY, int routerIdx, RouteItem *route); + + bool MIRACLE(int fromX, int fromY, int lineIdx, int destLineIdx, int routeIdx); + bool PLAN_TEST(int paramX, int paramY, int superRouteIdx, int paramStartLineIdx, int paramEndLineIdx); + +public: + RouteItem *_route; + RouteItem *_testRoute2; + + int _bobZone[105]; + bool _bobZoneFl[105]; + ZoneItem _zone[106]; + SquareZoneItem _squareZone[101]; + LigneItem _lineItem[MAX_LINES]; + int _linesNumb; + + LinesManager(HopkinsEngine *vm); + ~LinesManager(); + void clearAll(); + + void setMaxLineIdx(int idx); + int checkInventoryHotspots(int posX, int posY); + void addZoneLine(int idx, int fromX, int fromY, int destX, int destY, int bobZoneIdx); + void loadLines(const Common::String &file); + void addLine(int lineIdx, Directions direction, int fromX, int fromY, int destX, int destY); + void initRoute(); + RouteItem *findRoute(int fromX, int fromY, int destX, int destY); + RouteItem *cityMapCarRoute(int x1, int y1, int x2, int y2); + void clearAllZones(); + void initSquareZones(); + void resetLines(); + void resetLinesNumb(); + void resetLastLine(); + void enableZone(int idx); + void disableZone(int idx); + void checkZone(); + int getMouseZone(); + void optimizeRoute(RouteItem *route); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_FONT_H */ diff --git a/engines/hopkins/menu.cpp b/engines/hopkins/menu.cpp new file mode 100644 index 0000000000..455f4ad8d4 --- /dev/null +++ b/engines/hopkins/menu.cpp @@ -0,0 +1,168 @@ +/* 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 "hopkins/menu.h" + +#include "hopkins/dialogs.h" +#include "hopkins/files.h" +#include "hopkins/hopkins.h" +#include "hopkins/globals.h" +#include "hopkins/events.h" +#include "hopkins/graphics.h" +#include "hopkins/sound.h" + +#include "common/scummsys.h" +#include "common/events.h" +#include "common/file.h" +#include "common/util.h" + +namespace Hopkins { + +enum MenuSelection { MENU_NONE = 0, PLAY_GAME = 1, LOAD_GAME = 2, OPTIONS = 3, INTRODUCTION = 4, QUIT = 5 }; + +MenuManager::MenuManager(HopkinsEngine *vm) { + _vm = vm; +} + +int MenuManager::menu() { + byte *spriteData = NULL; + MenuSelection menuIndex; + Common::Point mousePos; + signed int result; + int frameIndex[] = { 0, 0, 0, 0, 0 }; + + if (_vm->shouldQuit()) + return -1; + + result = 0; + while (!_vm->shouldQuit()) { + _vm->_objectsMan->_forestFl = false; + _vm->_events->_breakoutFl = false; + _vm->_globals->_disableInventFl = true; + _vm->_globals->_exitId = 0; + + for (int idx = 0; idx < 31; ++idx) + _vm->_globals->_inventory[idx] = 0; + + memset(_vm->_globals->_saveData, 0, 2000); + _vm->_objectsMan->addObject(14); + memset(frameIndex, 0, sizeof(int) * ARRAYSIZE(frameIndex)); + + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + _vm->_graphicsMan->loadImage("MENU"); + else if (_vm->_globals->_language == LANG_EN) + _vm->_graphicsMan->loadImage("MENUAN"); + else if (_vm->_globals->_language == LANG_FR) + _vm->_graphicsMan->loadImage("MENUFR"); + else if (_vm->_globals->_language == LANG_SP) + _vm->_graphicsMan->loadImage("MENUES"); + + _vm->_graphicsMan->fadeInLong(); + + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + spriteData = _vm->_objectsMan->loadSprite("MENU.SPR"); + else if (_vm->_globals->_language == LANG_EN) + spriteData = _vm->_objectsMan->loadSprite("MENUAN.SPR"); + else if (_vm->_globals->_language == LANG_FR) + spriteData = _vm->_objectsMan->loadSprite("MENUFR.SPR"); + else if (_vm->_globals->_language == LANG_SP) + spriteData = _vm->_objectsMan->loadSprite("MENUES.SPR"); + + _vm->_events->mouseOn(); + _vm->_events->changeMouseCursor(0); + _vm->_events->_mouseCursorId = 0; + _vm->_events->_mouseSpriteId = 0; + + _vm->_soundMan->playSound(28); + + // Loop to make menu selection + bool selectionMade = false; + do { + if (_vm->shouldQuit()) + return -1; + + menuIndex = MENU_NONE; + mousePos = Common::Point(_vm->_events->getMouseX(), _vm->_events->getMouseY()); + + if (mousePos.x >= 232 && mousePos.x <= 408) { + if (mousePos.y >= 261 && mousePos.y <= 284) + menuIndex = PLAY_GAME; + else if (mousePos.y >= 293 && mousePos.y <= 316) + menuIndex = LOAD_GAME; + else if (mousePos.y >= 325 && mousePos.y <= 347) + menuIndex = OPTIONS; + else if (mousePos.y >= 356 && mousePos.y <= 379) + menuIndex = INTRODUCTION; + else if (mousePos.y >= 388 && mousePos.y <= 411) + menuIndex = QUIT; + } + + memset(frameIndex, 0, sizeof(int) * ARRAYSIZE(frameIndex)); + if (menuIndex > MENU_NONE) + frameIndex[menuIndex - 1] = 1; + + _vm->_graphicsMan->fastDisplay(spriteData, 230, 259, frameIndex[0]); + _vm->_graphicsMan->fastDisplay(spriteData, 230, 291, frameIndex[1] + 2); + _vm->_graphicsMan->fastDisplay(spriteData, 230, 322, frameIndex[2] + 4); + _vm->_graphicsMan->fastDisplay(spriteData, 230, 354, frameIndex[3] + 6); + _vm->_graphicsMan->fastDisplay(spriteData, 230, 386, frameIndex[4] + 8); + _vm->_events->refreshScreenAndEvents(); + + if (_vm->_events->getMouseButton() == 1 && menuIndex != MENU_NONE) + selectionMade = true; + } while (!selectionMade); + + if (menuIndex > MENU_NONE) { + _vm->_graphicsMan->fastDisplay(spriteData, 230, 259 + 32 * (menuIndex - 1), 10 + (menuIndex - 1)); + _vm->_events->refreshScreenAndEvents(); + _vm->_events->delay(200); + } + + if (menuIndex == PLAY_GAME) { + result = 1; + break; + } else if (menuIndex == LOAD_GAME) { + _vm->_globals->_exitId = -1; + _vm->_dialog->showLoadGame(); + + if (_vm->_globals->_exitId != -1) { + result = _vm->_globals->_exitId; + break; + } + _vm->_globals->_exitId = 0; + } else if (menuIndex == OPTIONS) { + _vm->_dialog->showOptionsDialog(); + } else if (menuIndex == INTRODUCTION) { + _vm->playIntro(); + } else if (menuIndex == QUIT) { + result = -1; + break; + } + } + + _vm->_globals->freeMemory(spriteData); + _vm->_globals->_disableInventFl = false; + _vm->_graphicsMan->fadeOutLong(); + return result; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/menu.h b/engines/hopkins/menu.h new file mode 100644 index 0000000000..e926c29dbd --- /dev/null +++ b/engines/hopkins/menu.h @@ -0,0 +1,46 @@ +/* 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. + * + */ + +#ifndef HOPKINS_MENU_H +#define HOPKINS_MENU_H + +#include "common/scummsys.h" +#include "common/system.h" +#include "common/error.h" + +namespace Hopkins { + +class HopkinsEngine; + +class MenuManager { +private: + HopkinsEngine *_vm; + +public: + MenuManager(HopkinsEngine *vm); + + int menu(); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_MENU_H */ diff --git a/engines/hopkins/module.mk b/engines/hopkins/module.mk new file mode 100644 index 0000000000..5c1a7dd478 --- /dev/null +++ b/engines/hopkins/module.mk @@ -0,0 +1,29 @@ +MODULE := engines/hopkins + +MODULE_OBJS := \ + anim.o \ + computer.o \ + debugger.o \ + detection.o \ + dialogs.o \ + events.o \ + files.o \ + font.o \ + graphics.o \ + globals.o \ + hopkins.o \ + lines.o \ + menu.o \ + objects.o \ + saveload.o \ + script.o \ + sound.o \ + talk.o + +# This module can be built as a plugin +ifeq ($(ENABLE_HOPKINS), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/hopkins/objects.cpp b/engines/hopkins/objects.cpp new file mode 100644 index 0000000000..f2f547557f --- /dev/null +++ b/engines/hopkins/objects.cpp @@ -0,0 +1,4061 @@ +/* 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 "hopkins/objects.h" + +#include "hopkins/dialogs.h" +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/hopkins.h" + +#include "common/system.h" +#include "graphics/palette.h" +#include "common/file.h" +#include "common/rect.h" +#include "engines/util.h" + +namespace Hopkins { + +ObjectsManager::ObjectsManager(HopkinsEngine *vm) { + _vm = vm; + + for (int i = 0; i < 6; ++i) + Common::fill((byte *)&_sprite[i], (byte *)&_sprite[i] + sizeof(SpriteItem), 0); + + for (int i = 0; i < 36; ++i) { + Common::fill((byte *)&_bob[i], (byte *)&_bob[i] + sizeof(BobItem), 0); + Common::fill((byte *)&_lockedAnims[i], (byte *)&_lockedAnims[i] + sizeof(LockAnimItem), 0); + } + + for (int i = 0; i < 30; ++i) { + Common::fill((byte *)&_vBob[i], (byte *)&_vBob[i] + sizeof(VBobItem), 0); + } + + for (int i = 0; i < 300; ++i) + Common::fill((byte *)&_objectAuthIcons[i], (byte *)&_objectAuthIcons[i] + sizeof(ObjectAuthIcon), 0); + + _sortedDisplayCount = 0; + for (int i = 0; i < 51; ++i) + Common::fill((byte *)&_sortedDisplay[i], (byte *)&_sortedDisplay[i] + sizeof(SortItem), 0); + + for (int i = 0; i < 25; ++i) + Common::fill((byte *)&_hidingItem[i], (byte *)&_hidingItem[i] + sizeof(HidingItem), 0); + + for (int i = 0; i < 6; ++i) + _hidingItemData[i] = NULL; + + for (int i = 0; i < 6; ++i) + Common::fill((byte *)&_liste[i], (byte *)&_liste[i] + sizeof(ListeItem), 0); + + for (int i = 0; i < 35; ++i) + Common::fill((byte *)&_liste2[i], (byte *)&_liste2[i] + sizeof(ListeItem), 0); + + _helicopterFl = false; + _priorityFl = false; + _oldBorderPos = Common::Point(0, 0); + _oldBorderSpriteIndex = 0; + _borderPos = Common::Point(0, 0); + _borderSpriteIndex = 0; + _saveLoadX = _saveLoadY = 0; + _oldCharacterPosX = _oldCharacterPosY = 0; + _eraseVisibleCounter = 0; + _saveLoadSprite = NULL; + _saveLoadSprite2 = NULL; + _spritePtr = NULL; + _oldSpriteData = NULL; + _saveLoadFl = false; + _visibleFl = false; + _zoneNum = 0; + _forceZoneFl = false; + _changeVerbFl = false; + _verb = 0; + _changeHeadFl = false; + _disableFl = false; + _twoCharactersFl = false; + _characterPos = Common::Point(0, 0); + _startSpriteIndex = 0; + _jumpVerb = 0; + _jumpZone = 0; + _oldSpriteIndex = 0; + _oldFrameIndex = 0; + _oldFlipFl = false; + _curObjectIndex = 0; + _forestFl = false; + _mapCarPosX = _mapCarPosY = 0; + _forestSprite = NULL; + _gestureBuf = NULL; + _curGestureFile = 0; + _headSprites = NULL; + _homeRateCounter = 0; + _lastDirection = DIR_NONE; + _oldDirection = DIR_NONE; + _oldDirectionSpriteIdx = 59; + _objectWidth = _objectHeight = 0; + _hidingActiveFl = false; + _curObjectFileNum = 0; + _objectDataBuf = NULL; + _charactersEnabledFl = false; + _refreshBobMode10Fl = false; +} + +ObjectsManager::~ObjectsManager() { + _vm->_globals->freeMemory(_forestSprite); + _vm->_globals->freeMemory(_gestureBuf); + _vm->_globals->freeMemory(_headSprites); + _vm->_globals->freeMemory(_objectDataBuf); + clearVBob(); + + for (int idx = 0; idx < 6; ++idx) + _hidingItemData[idx] = _vm->_globals->freeMemory(_hidingItemData[idx]); +} + +void ObjectsManager::clearAll() { + _forestFl = false; + _forestSprite = _vm->_globals->freeMemory(_forestSprite); + _curGestureFile = 0; + _gestureBuf = _vm->_globals->freeMemory(_gestureBuf); + _curObjectFileNum = 0; + + for (int idx = 0; idx < 6; ++idx) + _hidingItemData[idx] = _vm->_globals->freeMemory(_hidingItemData[idx]); + + _objectDataBuf = _vm->_globals->freeMemory(_objectDataBuf); + initVBob(); +} + +// Load Object +void ObjectsManager::loadObjects() { + byte *data = _vm->_fileIO->loadFile("OBJET.DAT"); + byte *srcP = data; + + for (int idx = 0; idx < 300; ++idx) { + ObjectAuthIcon *objectAuthIcon = &_objectAuthIcons[idx]; + objectAuthIcon->_objectFileNum = *srcP++; + objectAuthIcon->_idx = *srcP++; + objectAuthIcon->_flag1 = *srcP++; + objectAuthIcon->_flag2 = *srcP++; + objectAuthIcon->_flag3 = *srcP++; + objectAuthIcon->_flag4 = *srcP++; + objectAuthIcon->_flag5 = *srcP++; + objectAuthIcon->_flag6 = *srcP++; + } + + _vm->_globals->freeMemory(data); +} + +// Reset Hiding Items +void ObjectsManager::resetHidingItems() { + for (int idx = 1; idx <= 5; ++idx) { + _hidingItemData[idx] = _vm->_globals->freeMemory(_hidingItemData[idx]); + } + + for (int idx = 0; idx <= 20; ++idx) { + HidingItem *hid = &_hidingItem[idx]; + hid->_spriteData = NULL; + hid->_x = 0; + hid->_y = 0; + hid->_spriteIndex = 0; + hid->_useCount = 0; + hid->_width = 0; + hid->_height = 0; + hid->_resetUseCount = false; + hid->_yOffset = 0; + } + + _hidingActiveFl = false; +} + +/** + * Change Object + */ +void ObjectsManager::changeObject(int objIndex) { + _vm->_events->_objectBuf = loadObjectFromFile(objIndex, true); + _curObjectIndex = objIndex; +} + +byte *ObjectsManager::loadObjectFromFile(int objIndex, bool mode) { + byte *dataP = NULL; + int objectFileNum = _objectAuthIcons[objIndex]._objectFileNum; + int idx = _objectAuthIcons[objIndex]._idx; + + if (mode) + ++idx; + + if (objectFileNum != _curObjectFileNum) { + if (_objectDataBuf) + removeObjectDataBuf(); + if (objectFileNum == 1) { + _objectDataBuf = loadSprite("OBJET1.SPR"); + } + _curObjectFileNum = objectFileNum; + } + + int width = getWidth(_objectDataBuf, idx); + int height = getHeight(_objectDataBuf, idx); + _objectWidth = width; + _objectHeight = height; + + if (mode) { + sprite_alone(_objectDataBuf, _vm->_events->_objectBuf, idx); + dataP = _vm->_events->_objectBuf; + } else { + dataP = _vm->_globals->allocMemory(height * width); + if (dataP == NULL) + error("CAPTURE_OBJET"); + + capture_mem_sprite(_objectDataBuf, dataP, idx); + } + + return dataP; +} + +/** + * Remove an Object from the inventory + */ +void ObjectsManager::removeObject(int objIndex) { + int idx; + for (idx = 1; idx <= 32; ++idx) { + if (_vm->_globals->_inventory[idx] == objIndex) + break; + } + + if (idx <= 32) { + if (idx == 32) { + _vm->_globals->_inventory[32] = 0; + } else { + for (int i = idx; i < 32; ++i) + _vm->_globals->_inventory[i] = _vm->_globals->_inventory[i + 1]; + } + } + changeObject(14); + +} + +/** + * Set Offset XY + */ +void ObjectsManager::setOffsetXY(byte *data, int idx, int xp, int yp, bool isSize) { + byte *startP = data + 3; + for (int i = idx; i; --i) + startP += READ_LE_UINT32(startP) + 16; + + byte *rectP = startP + 8; + if (isSize) { + // Set size + byte *pointP = rectP + 4; + WRITE_LE_UINT16(pointP, xp); + WRITE_LE_UINT16(pointP + 2, yp); + } else { + // Set position + WRITE_LE_UINT16(rectP, xp); + WRITE_LE_UINT16(rectP + 2, yp); + } +} + +int ObjectsManager::getOffsetX(const byte *spriteData, int spriteIndex, bool isSize) { + const byte *data = spriteData + 3; + for (int i = spriteIndex; i; --i) + data += READ_LE_UINT32(data) + 16; + + int result; + if (isSize) + result = READ_LE_INT16(data + 12); + else + result = READ_LE_INT16(data + 8); + + return result; +} + +int ObjectsManager::getOffsetY(const byte *spriteData, int spriteIndex, bool isSize) { + const byte *data = spriteData + 3; + for (int i = spriteIndex; i; --i) + data += READ_LE_UINT32(data) + 16; + + int result; + if (isSize) + result = READ_LE_INT16(data + 14); + else + result = READ_LE_INT16(data + 10); + + return result; +} + +/** + * Get Width + */ +int ObjectsManager::getWidth(const byte *objectData, int idx) { + const byte *rectP = objectData + 3; + for (int i = idx; i; --i) + rectP += READ_LE_UINT32(rectP) + 16; + + return READ_LE_INT16(rectP + 4); +} + +/** + * Get height + */ +int ObjectsManager::getHeight(const byte *objectData, int idx) { + const byte *rectP = objectData + 3; + for (int i = idx; i; --i) + rectP += READ_LE_UINT32(rectP) + 16; + + return READ_LE_INT16(rectP + 6); +} + +void ObjectsManager::sprite_alone(const byte *objectData, byte *sprite, int objIndex) { + const byte *objP = objectData + 3; + for (int i = objIndex; i; --i) { + objP += READ_LE_UINT32(objP) + 16; + } + + objP += 4; + int result = READ_LE_INT16(objP) * READ_LE_INT16(objP + 2); + + memcpy(sprite + 3, objP - 4, result + 16); +} + +void ObjectsManager::capture_mem_sprite(const byte *objectData, byte *sprite, int objIndex) { + const byte *objP = objectData + 3; + for (int i = objIndex; i; --i) { + objP += READ_LE_UINT32(objP) + 16; + } + + objP += 4; + int result = READ_LE_INT16(objP) * READ_LE_INT16(objP + 2); + memcpy(sprite, objP + 12, result); +} + +void ObjectsManager::removeObjectDataBuf() { + _curObjectFileNum = 0; + _objectDataBuf = _vm->_globals->freeMemory(_objectDataBuf); +} + +/** + * Load Sprite from file + */ +byte *ObjectsManager::loadSprite(const Common::String &file) { + return _vm->_fileIO->loadFile(file); +} + +/** + * Add Object + */ +void ObjectsManager::addObject(int objIndex) { + int arrIndex = 0; + for (;;) { + ++arrIndex; + if ((!_vm->_globals->_inventory[arrIndex]) || (arrIndex == 32)) + break; + } + + _vm->_globals->_inventory[arrIndex] = objIndex; +} + +/** + * Display Sprite + */ +void ObjectsManager::displaySprite() { + int clipX; + int clipY; + bool loopCondFl; + uint16 arr[50]; + + // Handle copying any background areas that text are going to be drawn on + _sortedDisplayCount = 0; + for (int idx = 0; idx <= 10; ++idx) { + TxtItemList *curTxtList = &_vm->_fontMan->_textList[idx]; + if (curTxtList->_enabledFl && _vm->_fontMan->_text[idx]._textType != 2) { + clipX = curTxtList->_pos.x - 2; + + if (clipX < _vm->_graphicsMan->_minX) + clipX = _vm->_graphicsMan->_minX; + + clipY = curTxtList->_pos.y - 2; + if (clipY < _vm->_graphicsMan->_minY) + clipY = _vm->_graphicsMan->_minY; + + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, clipX, clipY, + curTxtList->_width + 4, curTxtList->_height + 4, _vm->_graphicsMan->_frontBuffer, clipX, clipY); + curTxtList->_enabledFl = false; + } + } + + if (!_charactersEnabledFl) { + for (int idx = 0; idx < MAX_SPRITE; ++idx) { + ListeItem *curList = &_liste[idx]; + if (curList->_visibleFl) { + clipX = curList->_posX - 2; + if (clipX < _vm->_graphicsMan->_minX) + clipX = _vm->_graphicsMan->_minX; + + clipY = curList->_posY - 2; + if (clipY < _vm->_graphicsMan->_minY) + clipY = _vm->_graphicsMan->_minY; + + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, clipX, clipY, + curList->_width + 4, curList->_height + 4, _vm->_graphicsMan->_frontBuffer, clipX, clipY); + curList->_visibleFl = false; + } + } + } + + displayBobAnim(); + displayVBob(); + + if (!_charactersEnabledFl) { + // Handle drawing characters on the screen + for (int idx = 0; idx < MAX_SPRITE; ++idx) { + _liste[idx]._visibleFl = false; + SpriteItem *curSpr = &_sprite[idx]; + if (curSpr->_animationType == 1) { + computeSprite(idx); + if (curSpr->_activeFl) + beforeSort(SORT_SPRITE, idx, curSpr->_height + curSpr->_destY); + } + } + + if (_hidingActiveFl) + checkHidingItem(); + } + + if (_priorityFl && _sortedDisplayCount) { + for (int i = 1; i <= 48; i++) + arr[i] = i; + + do { + loopCondFl = false; + for (int sortIdx = 1; sortIdx < _sortedDisplayCount; sortIdx++) { + if (_sortedDisplay[arr[sortIdx]]._priority > _sortedDisplay[arr[sortIdx + 1]]._priority) { + SWAP(arr[sortIdx], arr[sortIdx + 1]); + loopCondFl = true; + } + } + } while (loopCondFl); + + for (int sortIdx = 1; sortIdx < _sortedDisplayCount + 1; sortIdx++) { + int idx = arr[sortIdx]; + switch (_sortedDisplay[idx]._sortMode) { + case SORT_BOB: + setBobInfo(_sortedDisplay[idx]._index); + break; + case SORT_SPRITE: + showSprite(_sortedDisplay[idx]._index); + break; + case SORT_HIDING: + displayHiding(_sortedDisplay[idx]._index); + break; + default: + break; + } + _sortedDisplay[idx]._sortMode = SORT_NONE; + } + } else { + for (int idx = 1; idx < _sortedDisplayCount + 1; ++idx) { + switch (_sortedDisplay[idx]._sortMode) { + case SORT_BOB: + setBobInfo(_sortedDisplay[idx]._index); + break; + case SORT_SPRITE: + showSprite(_sortedDisplay[idx]._index); + break; + case SORT_HIDING: + displayHiding(_sortedDisplay[idx]._index); + break; + default: + break; + } + _sortedDisplay[idx]._sortMode = SORT_NONE; + } + } + + // Reset the Sort array + for (int idx = 0; idx < 50; ++idx) { + SortItem *disp = &_sortedDisplay[idx]; + disp->_sortMode = SORT_NONE; + disp->_index = 0; + disp->_priority = 0; + } + + _sortedDisplayCount = 0; + + _vm->_dialog->drawInvent(_oldBorderPos, _oldBorderSpriteIndex, _borderPos, _borderSpriteIndex); + + if (_saveLoadFl) { + int16 posX = _vm->_events->_startPos.x; + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, _saveLoadSprite, posX + 183, 60, 274, 353); + if (_saveLoadX && _saveLoadY) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _saveLoadSprite2, _saveLoadX + posX + 300, _saveLoadY + 300, 0); + + _vm->_graphicsMan->addDirtyRect(posX + 183, 60, posX + 457, 413); + } + + // If the Options dialog is activated, draw the elements + if (_vm->_globals->_optionDialogFl) { + int16 posX = _vm->_events->_startPos.x; + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 464, 407, 0); + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 657, 556, _vm->_globals->_menuSpeed); + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 731, 495, _vm->_globals->_menuTextOff); + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 731, 468, _vm->_globals->_menuVoiceOff); + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 731, 441, _vm->_globals->_menuSoundOff); + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 731, 414, _vm->_globals->_menuMusicOff); + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 600, 522, _vm->_globals->_menuDisplayType); + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _vm->_globals->_optionDialogSpr, + posX + 611, 502, _vm->_globals->_menuScrollSpeed); + _vm->_graphicsMan->addDirtyRect(posX + 164, 107, posX + 498, 320); + } + + // Loop to draw any on-screen text + for (int idx = 0; idx <= 10; ++idx) { + TxtItem *curTxt = &_vm->_fontMan->_text[idx]; + if (curTxt->_textOnFl) { + TxtItemList *curTxtList = &_vm->_fontMan->_textList[idx]; + if ((curTxt->_textType < 2) || (curTxt->_textType > 3)) + _vm->_fontMan->box(idx, curTxt->_messageId, curTxt->_filename, _vm->_events->_startPos.x + curTxt->_pos.x, curTxt->_pos.y); + else + _vm->_fontMan->box(idx, curTxt->_messageId, curTxt->_filename, curTxt->_pos.x, curTxt->_pos.y); + curTxtList->_enabledFl = true; + + if ((curTxt->_textType < 2) || (curTxt->_textType > 3)) + curTxtList->_pos.x = _vm->_events->_startPos.x + curTxt->_pos.x; + else + curTxtList->_pos.x = curTxt->_pos.x; + + curTxtList->_pos.y = curTxt->_pos.y; + curTxtList->_width = curTxt->_width; + curTxtList->_height = curTxt->_height; + + if (curTxtList->_pos.x < _vm->_graphicsMan->_minX) + curTxtList->_pos.x = _vm->_graphicsMan->_minX - 1; + if (curTxtList->_pos.y < _vm->_graphicsMan->_minY) + curTxtList->_pos.y = _vm->_graphicsMan->_minY - 1; + + int posX = curTxtList->_pos.x; + if (curTxtList->_width + posX > _vm->_graphicsMan->_maxX) + curTxtList->_width = _vm->_graphicsMan->_maxX - posX; + int posY = curTxtList->_pos.y; + if (curTxtList->_height + posY > _vm->_graphicsMan->_maxY) + curTxtList->_height = _vm->_graphicsMan->_maxY - posY; + if (curTxtList->_width <= 0 || curTxtList->_height <= 0) + curTxtList->_enabledFl = false; + } + } + + _vm->_dialog->inventAnim(); +} + +void ObjectsManager::resetBob(int idx) { + BobItem &bob = _bob[idx]; + ListeItem &item = _liste2[idx]; + + bob._bobMode = 0; + bob._spriteData = NULL; + bob._xp = 0; + bob._yp = 0; + bob._frameIndex = 0; + bob._animDataIdx = 0; + bob._moveChange1 = 0; + bob._moveChange2 = 0; + bob._disabledAnimationFl = false; + bob._animData = NULL; + bob._bobMode10 = false; + bob._bobModeChange = 0; + bob._modeChangeCtr = 0; + bob._modeChangeUnused = 0; + bob._disableFl = false; + bob._zoomFactor = 0; + bob._flipFl = false; + bob._oldX2 = 0; + + item._visibleFl = false; + item._posX = 0; + item._posY = 0; + item._width = 0; + item._height = 0; +} + +void ObjectsManager::setBobInfo(int idx) { + BobItem *curBob = &_bob[idx]; + + if (!curBob->_activeFl) + return; + + int xp = curBob->_oldX; + int yp = curBob->_oldY; + + if (curBob->_isSpriteFl) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, curBob->_spriteData, + xp + 300, yp + 300, curBob->_frameIndex); + else + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, + curBob->_spriteData, xp + 300, yp + 300, curBob->_frameIndex, + curBob->_zoomOutFactor, curBob->_zooInmFactor, curBob->_flipFl); + + ListeItem *curLst = &_liste2[idx]; + curLst->_visibleFl = true; + curLst->_posX = xp; + curLst->_posY = yp; + + curLst->_width = curBob->_oldWidth; + curLst->_height = curBob->_oldHeight; + + if (curLst->_posX < _vm->_graphicsMan->_minX) { + curLst->_width -= _vm->_graphicsMan->_minX - curLst->_posX; + curLst->_posX = _vm->_graphicsMan->_minX; + } + + if (curLst->_posY < _vm->_graphicsMan->_minY) { + curLst->_height -= _vm->_graphicsMan->_minY - curLst->_posY; + curLst->_posY = _vm->_graphicsMan->_minY; + } + + if (curLst->_width + curLst->_posX > _vm->_graphicsMan->_maxX) + curLst->_width = _vm->_graphicsMan->_maxX - curLst->_posX; + + if (curLst->_height + curLst->_posY > _vm->_graphicsMan->_maxY) + curLst->_height = _vm->_graphicsMan->_maxY - curLst->_posY; + + if (curLst->_width <= 0 || curLst->_height <= 0) + curLst->_visibleFl = false; + + if (curLst->_visibleFl) + _vm->_graphicsMan->addDirtyRect(curLst->_posX, curLst->_posY, curLst->_posX + curLst->_width, curLst->_posY + curLst->_height); +} + +void ObjectsManager::displayBob(int idx) { + BobItem *curBob = &_bob[idx]; + + _priorityFl = true; + + if (curBob->_bobMode) + return; + + resetBob(idx); + + const byte *data = _vm->_animMan->_animBqe[idx]._data; + int bankIdx = READ_LE_INT16(data); + if (!bankIdx) + return; + if ((!_vm->_animMan->Bank[bankIdx]._loadedFl) || (!READ_LE_UINT16(data + 24))) + return; + + + int16 bobModeChange = READ_LE_INT16(data + 2); + int16 modeChangeUnused = READ_LE_INT16(data + 4); + // data[6] isn't used, read skipped + int16 newModeChangeCtr = READ_LE_INT16(data + 8); + + if (!bobModeChange) + bobModeChange = 1; + if (!newModeChangeCtr) + newModeChangeCtr = -1; + + curBob->_isSpriteFl = false; + + if (_vm->_animMan->Bank[bankIdx]._fileHeader == 1) { + curBob->_isSpriteFl = true; + curBob->_zoomFactor = 0; + curBob->_flipFl = false; + } + + curBob->_animData = _vm->_animMan->_animBqe[idx]._data; + curBob->_bobMode = 10; + curBob->_spriteData = _vm->_animMan->Bank[bankIdx]._data; + + curBob->_bobModeChange = bobModeChange; + curBob->_modeChangeCtr = newModeChangeCtr; + curBob->_modeChangeUnused = modeChangeUnused; +} + +void ObjectsManager::hideBob(int idx) { + BobItem *curBob = &_bob[idx]; + if ((curBob->_bobMode == 3) || (curBob->_bobMode == 10)) + curBob->_bobMode++; +} + +void ObjectsManager::setBobOffset(int idx, int offset) { + _bob[idx]._oldX2 = offset; +} + +void ObjectsManager::computeHideCounter(int idx) { + HidingItem *hid = &_hidingItem[idx]; + if (hid->_useCount == 0) + return; + + for (int i = 0; i <= 20; i++) { + BobItem *curBob = &_bob[i]; + if ((curBob->_bobMode) && (!curBob->_disabledAnimationFl) && (!curBob->_disableFl) && (curBob->_frameIndex != 250)) { + int oldRight = curBob->_oldX + curBob->_oldWidth; + int oldBottom = curBob->_oldY + curBob->_oldHeight; + int hiddenRight = hid->_x + hid->_width; + + if ((oldBottom > hid->_y) && (oldBottom < hid->_yOffset + hid->_height + hid->_y)) { + if ((oldRight >= hid->_x && oldRight <= hiddenRight) + // CHECKME: The original was doing the test two times. This looks like an + // original bug + // || (cachedRight >= curBob->_oldWidth && curBob->_oldWidth >= hid->_x) + || (hiddenRight >= curBob->_oldWidth && curBob->_oldWidth >= hid->_x) + || (curBob->_oldWidth >= hid->_x && oldRight <= hiddenRight) + || (curBob->_oldWidth <= hid->_x && oldRight >= hiddenRight)) + ++hid->_useCount; + } + } + } +} + +void ObjectsManager::initBobVariables(int idx) { + BobItem *bob = &_bob[idx]; + + bob->_activeFl = false; + if (bob->_isSpriteFl) { + bob->_flipFl = false; + bob->_zoomFactor = 0; + } + + int spriteIdx = bob->_frameIndex; + if (spriteIdx == 250) + return; + + int deltaY, deltaX; + if (bob->_flipFl) { + deltaX = getOffsetX(bob->_spriteData, spriteIdx, true); + deltaY = getOffsetY(bob->_spriteData, bob->_frameIndex, true); + } else { + deltaX = getOffsetX(bob->_spriteData, spriteIdx, false); + deltaY = getOffsetY(bob->_spriteData, bob->_frameIndex, false); + } + + int negZoom = 0; + int posZoom = 0; + if (bob->_zoomFactor < 0) + negZoom = CLIP(-bob->_zoomFactor, 0, 95); + else + posZoom = bob->_zoomFactor; + + if (posZoom) { + if (deltaX >= 0) + deltaX = _vm->_graphicsMan->zoomIn(deltaX, posZoom); + else + deltaX = -_vm->_graphicsMan->zoomIn(-deltaX, posZoom); + + if (deltaY >= 0) + deltaY = _vm->_graphicsMan->zoomIn(deltaY, posZoom); + else + deltaY = -_vm->_graphicsMan->zoomIn(abs(deltaX), posZoom); + } + + if (negZoom) { + if (deltaX >= 0) + deltaX = _vm->_graphicsMan->zoomOut(deltaX, negZoom); + else + deltaX = -_vm->_graphicsMan->zoomOut(-deltaX, negZoom); + + if (deltaY >= 0) + deltaY = _vm->_graphicsMan->zoomOut(deltaY, negZoom); + else + deltaY = -_vm->_graphicsMan->zoomOut(abs(deltaX), negZoom); + } + + int newX = bob->_xp - deltaX; + int newY = bob->_yp - deltaY; + bob->_activeFl = true; + bob->_oldX = newX; + bob->_oldY = newY; + bob->_zooInmFactor = posZoom; + bob->_zoomOutFactor = negZoom; + + ListeItem *curList = &_liste2[idx]; + curList->_visibleFl = true; + curList->_posX = newX; + curList->_posY = newY; + + int width = getWidth(bob->_spriteData, bob->_frameIndex); + int height = getHeight(bob->_spriteData, bob->_frameIndex); + + if (posZoom) { + width = _vm->_graphicsMan->zoomIn(width, posZoom); + height = _vm->_graphicsMan->zoomIn(height, posZoom); + } else if (negZoom) { + width = _vm->_graphicsMan->zoomOut(width, negZoom); + height = _vm->_graphicsMan->zoomOut(height, negZoom); + } + + curList->_width = width; + curList->_height = height; + bob->_oldWidth = width; + bob->_oldHeight = height; +} + +void ObjectsManager::checkHidingItem() { + for (int hidingItemIdx = 0; hidingItemIdx <= 19; hidingItemIdx++) { + HidingItem *hid = &_hidingItem[hidingItemIdx]; + if (hid->_useCount == 0) + continue; + + int _oldUseCount = hid->_useCount; + for (int spriteIdx = 0; spriteIdx <= 4; spriteIdx++) { + const SpriteItem *spr = &_sprite[spriteIdx]; + if (spr->_animationType == 1 && spr->_spriteIndex != 250) { + int right = spr->_width + spr->_destX; + int bottom = spr->_height + spr->_destY; + int hidingRight = hid->_width + hid->_x; + + if (bottom > hid->_y && bottom < (hid->_yOffset + hid->_height + hid->_y)) { + if ((right >= hid->_x && right <= hidingRight) + // CHECKME: The original was doing the test two times. This looks like an + // original bug + // || (hidingRight >= spr->_destX && hid->_x <= spr->_destX) + || (hidingRight >= spr->_destX && hid->_x <= spr->_destX) + || (hid->_x <= spr->_destX && right <= hidingRight) + || (hid->_x >= spr->_destX && right >= hidingRight)) + ++hid->_useCount; + } + } + } + + computeHideCounter(hidingItemIdx); + if (hid->_useCount != _oldUseCount) { + int priority = hid->_yOffset + hid->_height + hid->_y; + if (priority > 440) + priority = 500; + + beforeSort(SORT_HIDING, hidingItemIdx, priority); + hid->_useCount = 1; + hid->_resetUseCount = true; + } else if (hid->_resetUseCount) { + hid->_resetUseCount = false; + hid->_useCount = 1; + } + + } +} + +void ObjectsManager::showSprite(int idx) { + SpriteItem *spr = &_sprite[idx]; + if (!spr->_activeFl) + return; + + if (spr->_rleFl) + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, spr->_spriteData, + spr->_destX + 300, spr->_destY + 300, spr->_spriteIndex); + else + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, spr->_spriteData, + spr->_destX + 300, spr->_destY + 300, spr->_spriteIndex, spr->_reducePct, spr->_zoomPct, spr->_flipFl); + + ListeItem *list = &_liste[idx]; + list->_width = spr->_width; + list->_height = spr->_height; + + if (list->_posX < _vm->_graphicsMan->_minX) { + list->_width -= _vm->_graphicsMan->_minX - list->_posX; + list->_posX = _vm->_graphicsMan->_minX; + } + + if (list->_posY < _vm->_graphicsMan->_minY) { + list->_height -= _vm->_graphicsMan->_minY - list->_posY; + list->_posY = _vm->_graphicsMan->_minY; + } + + list->_width = MIN(list->_width, _vm->_graphicsMan->_maxX - list->_posX); + list->_height = MIN(list->_height, _vm->_graphicsMan->_maxY - list->_posY); + + if (list->_width <= 0 || list->_height <= 0) + list->_visibleFl = false; + + if (list->_visibleFl) + _vm->_graphicsMan->addDirtyRect( list->_posX, list->_posY, list->_posX + list->_width, list->_posY + list->_height); +} + +void ObjectsManager::displayHiding(int idx) { + HidingItem *hid = &_hidingItem[idx]; + + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, _hidingItemData[1], + hid->_x + 300, hid->_y + 300, hid->_spriteIndex); + _vm->_graphicsMan->addDirtyRect(hid->_x, hid->_y, hid->_x + hid->_width, hid->_y + hid->_height); +} + +// Compute Sprite +void ObjectsManager::computeSprite(int idx) { + SpriteItem *spr = &_sprite[idx]; + + spr->_activeFl = false; + int spriteIndex = spr->_spriteIndex; + if (spriteIndex == 250) + return; + + int offX; + int offY; + if (spr->_flipFl) { + offX = getOffsetX(spr->_spriteData, spriteIndex, true); + offY = getOffsetY(spr->_spriteData, spr->_spriteIndex, true); + } else { + offX = getOffsetX(spr->_spriteData, spriteIndex, false); + offY = getOffsetY(spr->_spriteData, spr->_spriteIndex, false); + } + + int tmpX = spr->_deltaX + offX; + int deltaX = tmpX; + int tmpY = spr->_deltaY + offY; + int deltaY = tmpY; + int zoomPercent = 0; + int reducePercent = 0; + + if (spr->_zoomFactor < 0) { + reducePercent = -spr->_zoomFactor; + if (reducePercent > 95) + reducePercent = 95; + } else + zoomPercent = spr->_zoomFactor; + + if (zoomPercent) { + if (tmpX >= 0) + deltaX = _vm->_graphicsMan->zoomIn(tmpX, zoomPercent); + else + deltaX = -_vm->_graphicsMan->zoomIn(-tmpX, zoomPercent); + + if (tmpY >= 0) { + deltaY = _vm->_graphicsMan->zoomIn(tmpY, zoomPercent); + } else { + tmpY = abs(tmpX); + deltaY = -_vm->_graphicsMan->zoomIn(tmpY, zoomPercent); + } + } else if (reducePercent) { + if (tmpX >= 0) + deltaX = _vm->_graphicsMan->zoomOut(tmpX, reducePercent); + else + deltaX = -_vm->_graphicsMan->zoomOut(-tmpX, reducePercent); + + if (tmpY >= 0) { + deltaY = _vm->_graphicsMan->zoomOut(tmpY, reducePercent); + } else { + tmpY = abs(tmpX); + deltaY = -_vm->_graphicsMan->zoomOut(tmpY, reducePercent); + } + } + + int newPosX = spr->_spritePos.x - deltaX; + int newPosY = spr->_spritePos.y - deltaY; + spr->_destX = newPosX; + spr->_destY = newPosY; + spr->_activeFl = true; + spr->_zoomPct = zoomPercent; + spr->_reducePct = reducePercent; + + _liste[idx]._visibleFl = true; + _liste[idx]._posX = newPosX; + _liste[idx]._posY = newPosY; + + int width = getWidth(spr->_spriteData, spr->_spriteIndex); + int height = getHeight(spr->_spriteData, spr->_spriteIndex); + + if (zoomPercent) { + width = _vm->_graphicsMan->zoomIn(width, zoomPercent); + height = _vm->_graphicsMan->zoomIn(height, zoomPercent); + } else if (reducePercent) { + height = _vm->_graphicsMan->zoomOut(height, reducePercent); + width = _vm->_graphicsMan->zoomOut(width, reducePercent); + } + + spr->_width = width; + spr->_height = height; +} + +// Before Sort +void ObjectsManager::beforeSort(SortMode sortMode, int index, int priority) { + ++_sortedDisplayCount; + assert(_sortedDisplayCount <= 48); + + _sortedDisplay[_sortedDisplayCount]._sortMode = sortMode; + _sortedDisplay[_sortedDisplayCount]._index = index; + _sortedDisplay[_sortedDisplayCount]._priority = priority; +} + +// Display BOB Anim +void ObjectsManager::displayBobAnim() { + for (int idx = 1; idx <= 35; idx++) { + BobItem *bob = &_bob[idx]; + if (idx <= 20 && _charactersEnabledFl) { + bob->_bobMode10 = false; + continue; + } + + if (bob->_bobMode != 10) + continue; + + bob->_bobMode10 = false; + if (bob->_animData == NULL || bob->_disabledAnimationFl || bob->_modeChangeCtr == 0 || bob->_modeChangeCtr < -1) { + if (bob->_bobModeChange == 1 || bob->_bobModeChange == 2) + bob->_bobMode10 = true; + continue; + } + + if (bob->_moveChange1 == bob->_moveChange2) { + bob->_bobMode10 = true; + } else { + bob->_moveChange2++; + bob->_bobMode10 = false; + } + + if (!bob->_bobMode10) { + if (bob->_bobModeChange == 1 || bob->_bobModeChange == 2) + bob->_bobMode10 = true; + continue; + } + + byte *dataPtr = bob->_animData + 20; + int dataIdx = bob->_animDataIdx; + bob->_xp = READ_LE_INT16(dataPtr + 2 * dataIdx); + if (_lockedAnims[idx]._enableFl) + bob->_xp = _lockedAnims[idx]._posX; + if ( _charactersEnabledFl && idx > 20) + bob->_xp += _vm->_events->_startPos.x; + + bob->_yp = READ_LE_INT16(dataPtr + 2 * dataIdx + 2); + bob->_moveChange1 = READ_LE_INT16(dataPtr + 2 * dataIdx + 4); + bob->_zoomFactor = READ_LE_INT16(dataPtr + 2 * dataIdx + 6); + bob->_frameIndex = dataPtr[2 * dataIdx + 8]; + bob->_flipFl = (dataPtr[2 * dataIdx + 9] != 0); + bob->_animDataIdx += 5; + + if (bob->_moveChange1 > 0) { + bob->_moveChange1 /= _vm->_globals->_speed; + if (bob->_moveChange1 > 0) { + bob->_moveChange2 = 1; + if (bob->_bobModeChange == 1 || bob->_bobModeChange == 2) + bob->_bobMode10 = true; + continue; + } + + bob->_moveChange1 = 1; + } + if (!bob->_moveChange1) { + if (bob->_modeChangeCtr > 0) + bob->_modeChangeCtr--; + if (bob->_modeChangeCtr != -1 && bob->_modeChangeCtr <= 0) { + bob->_bobMode = 11; + } else { + bob->_animDataIdx = 0; + byte *bobData = bob->_animData + 20; + bob->_xp = READ_LE_INT16(bobData); + + if (_lockedAnims[idx]._enableFl) + bob->_xp = _lockedAnims[idx]._posX; + if (_charactersEnabledFl && idx > 20) + bob->_xp += _vm->_events->_startPos.x; + + bob->_yp = READ_LE_INT16(bobData + 2); + bob->_moveChange1 = READ_LE_INT16(bobData + 4); + bob->_zoomFactor = READ_LE_INT16(bobData + 6); + bob->_frameIndex = bobData[8]; + bob->_flipFl = (bobData[9] != 0); + bob->_animDataIdx += 5; + + if (bob->_moveChange1 > 0) { + bob->_moveChange1 /= _vm->_globals->_speed; + // Original code. It can't be negative, so the check is on == 0 + if (bob->_moveChange1 <= 0) + bob->_moveChange1 = 1; + } + } + } + + bob->_moveChange2 = 1; + if (bob->_bobModeChange == 1 || bob->_bobModeChange == 2) + bob->_bobMode10 = true; + } + + if (!_charactersEnabledFl && _refreshBobMode10Fl) { + for (int i = 0; i < 35; i++) { + BobItem *curBob = &_bob[i]; + if (curBob->_bobMode == 10 && !curBob->_disabledAnimationFl) + curBob->_bobMode10 = true; + } + } + + _refreshBobMode10Fl = false; + + for (int i = 1; i <= 35; i++) { + BobItem *curBob = &_bob[i]; + ListeItem *curList = &_liste2[i]; + if (i > 20 || !_charactersEnabledFl) { + if ((curBob->_bobMode == 10) && (curBob->_bobMode10)) { + if ((curBob->_bobModeChange != 2) && (curBob->_bobModeChange != 4)) { + if (curList->_visibleFl) { + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, curList->_posX, curList->_posY, + curList->_width, curList->_height, _vm->_graphicsMan->_frontBuffer, curList->_posX, curList->_posY); + curList->_visibleFl = false; + } + } + } + + if (curBob->_bobMode == 11) { + if (curList->_visibleFl) { + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, curList->_posX, curList->_posY, + curList->_width, curList->_height, _vm->_graphicsMan->_frontBuffer, curList->_posX, curList->_posY); + curList->_visibleFl = false; + } + + curBob->_bobMode = 0; + } + } + } + + for (int i = 1; i <= 35; i++) { + BobItem *curBob = &_bob[i]; + curBob->_oldY = 0; + if (curBob->_bobMode == 10 && !curBob->_disabledAnimationFl && curBob->_bobMode10) { + initBobVariables(i); + int priority = curBob->_oldX2 + curBob->_oldHeight + curBob->_oldY; + + if (priority > 450) + priority = 600; + + if (curBob->_activeFl) + beforeSort(SORT_BOB, i, priority); + } + } +} + +// Display VBOB +void ObjectsManager::displayVBob() { + int width, height; + + for (int idx = 0; idx <= 29; idx++) { + VBobItem *vbob = &_vBob[idx]; + if (vbob->_displayMode == 4) { + width = getWidth(vbob->_spriteData, vbob->_frameIndex); + height = getHeight(vbob->_spriteData, vbob->_frameIndex); + + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_backBuffer, vbob->_surface, + vbob->_xp, vbob->_yp, width, height); + + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, vbob->_surface, + vbob->_xp, vbob->_yp, width, height); + + _vm->_graphicsMan->addDirtyRect(vbob->_xp, vbob->_yp, vbob->_xp + width, height + vbob->_yp); + vbob->_surface = _vm->_globals->freeMemory(vbob->_surface); + + vbob->_displayMode = 0; + vbob->_spriteData = NULL; + vbob->_xp = 0; + vbob->_yp = 0; + vbob->_oldX = 0; + vbob->_oldY = 0; + vbob->_frameIndex = 0; + vbob->_oldFrameIndex = 0; + vbob->_oldSpriteData = NULL; + } + + if (vbob->_displayMode == 3) { + width = getWidth(vbob->_oldSpriteData, vbob->_oldFrameIndex); + height = getHeight(vbob->_oldSpriteData, vbob->_oldFrameIndex); + + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_backBuffer, vbob->_surface, + vbob->_oldX, vbob->_oldY, width, height); + + _vm->_graphicsMan->restoreSurfaceRect(_vm->_graphicsMan->_frontBuffer, vbob->_surface, + vbob->_oldX, vbob->_oldY, width, height); + + _vm->_graphicsMan->addDirtyRect(vbob->_oldX, vbob->_oldY, vbob->_oldX + width, vbob->_oldY + height); + + vbob->_displayMode = 1; + vbob->_oldSpriteData = vbob->_spriteData; + + vbob->_surface = _vm->_globals->freeMemory(vbob->_surface); + + vbob->_oldX = vbob->_xp; + vbob->_oldY = vbob->_yp; + vbob->_oldFrameIndex = vbob->_frameIndex; + } + + if (vbob->_displayMode == 1) { + width = getWidth(vbob->_spriteData, vbob->_frameIndex); + height = getHeight(vbob->_spriteData, vbob->_frameIndex); + + vbob->_surface = _vm->_globals->freeMemory(vbob->_surface); + + byte *surface = _vm->_globals->allocMemory(height * width); + vbob->_surface = surface; + + _vm->_graphicsMan->copySurfaceRect(_vm->_graphicsMan->_backBuffer, surface, + vbob->_xp, vbob->_yp, width, height); + + if (*vbob->_spriteData == 78) { + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_backBuffer, vbob->_spriteData, + vbob->_xp + 300, vbob->_yp + 300, vbob->_frameIndex, 0, 0, false); + + _vm->_graphicsMan->drawCompressedSprite(_vm->_graphicsMan->_frontBuffer, vbob->_spriteData, + vbob->_xp + 300, vbob->_yp + 300, vbob->_frameIndex, 0, 0, false); + } else { + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_frontBuffer, vbob->_spriteData, + vbob->_xp + 300, vbob->_yp + 300, vbob->_frameIndex); + + _vm->_graphicsMan->drawVesaSprite(_vm->_graphicsMan->_backBuffer, vbob->_spriteData, + vbob->_xp + 300, vbob->_yp + 300, vbob->_frameIndex); + } + + _vm->_graphicsMan->addDirtyRect(vbob->_xp, vbob->_yp , vbob->_xp + width, vbob->_yp + height); + vbob->_displayMode = 2; + } + } +} + +/** + * Get Sprite X coordinate + */ +int ObjectsManager::getSpriteX(int idx) { + assert(idx <= MAX_SPRITE); + return _sprite[idx]._spritePos.x; +} + +/** + * Get Sprite Y coordinate + */ +int ObjectsManager::getSpriteY(int idx) { + assert(idx <= MAX_SPRITE); + return _sprite[idx]._spritePos.y; +} + +/** + * Clear sprite structure + */ +void ObjectsManager::clearSprite() { + for (int idx = 0; idx < MAX_SPRITE; idx++) { + _sprite[idx]._spriteData = NULL; + _sprite[idx]._animationType = 0; + } + + for (int idx = 0; idx < MAX_SPRITE; idx++) { + ListeItem *list = &_liste[idx]; + list->_visibleFl = false; + list->_posX = 0; + list->_posY = 0; + list->_width = 0; + list->_height = 0; + } +} + +void ObjectsManager::animateSprite(int idx) { + assert(idx <= MAX_SPRITE); + _sprite[idx]._animationType = 1; +} + +void ObjectsManager::addStaticSprite(const byte *spriteData, Common::Point pos, int idx, int spriteIndex, int zoomFactor, bool flipFl, int deltaX, int deltaY) { + assert(idx <= MAX_SPRITE); + + SpriteItem *spr = &_sprite[idx]; + spr->_spriteData = spriteData; + spr->_spritePos = pos; + spr->_spriteIndex = spriteIndex; + spr->_zoomFactor = zoomFactor; + spr->_flipFl = flipFl; + spr->_deltaX = deltaX; + spr->_deltaY = deltaY; + spr->_animationType = 0; + + if (READ_BE_UINT24(spriteData) == MKTAG24('R', 'L', 'E')) { + spr->_rleFl = true; + spr->_zoomFactor = 0; + spr->_flipFl = false; + } else + spr->_rleFl = false; + +} + +/** + * Freeze sprite animation and free its memory + */ +void ObjectsManager::removeSprite(int idx) { + // Type 3 was also used by freeSprite(), which has been removed as it wasn't used + _sprite[idx]._animationType = 3; +} + +/** + * Set Sprite X coordinate + */ +void ObjectsManager::setSpriteX(int idx, int xp) { + assert(idx <= MAX_SPRITE); + _sprite[idx]._spritePos.x = xp; +} + +/** + * Set Sprite Y coordinate + */ +void ObjectsManager::setSpriteY(int idx, int yp) { + assert(idx <= MAX_SPRITE); + _sprite[idx]._spritePos.y = yp; +} + +/** + * Set Sprite Index + */ +void ObjectsManager::setSpriteIndex(int idx, int spriteIndex) { + assert(idx <= MAX_SPRITE); + _sprite[idx]._spriteIndex = spriteIndex; +} + +// Set Sprite Size +void ObjectsManager::setSpriteZoom(int idx, int zoomFactor) { + assert(idx <= MAX_SPRITE); + if (!_sprite[idx]._rleFl) + _sprite[idx]._zoomFactor = zoomFactor; +} + +void ObjectsManager::setFlipSprite(int idx, bool flipFl) { + assert(idx <= MAX_SPRITE); + if (!_sprite[idx]._rleFl) + _sprite[idx]._flipFl = flipFl; +} + +void ObjectsManager::goHome() { + if (_vm->_linesMan->_route == NULL) + return; + + if (_homeRateCounter > 1) { + --_homeRateCounter; + return; + } + + int newPosX; + int newPosY; + Directions newDirection; + + int oldPosX = 0; + int oldPosY = 0; + int oldFrameIdx = 0; + _homeRateCounter = 0; + if (_oldDirection == DIR_NONE) { + computeAndSetSpriteSize(); + newPosX = _vm->_linesMan->_route->_x; + newPosY = _vm->_linesMan->_route->_y; + newDirection = _vm->_linesMan->_route->_dir; + _vm->_linesMan->_route++; + + if (newPosX != -1 || newPosY != -1) { + _oldDirection = newDirection; + _oldDirectionSpriteIdx = newDirection + 59; + _oldFrameIndex = 0; + _oldCharacterPosX = newPosX; + _oldCharacterPosY = newPosY; + } else { + setSpriteIndex(0, _oldDirection + 59); + _vm->_globals->_actionDirection = DIR_NONE; + int zoneId; + if (_vm->_globals->_actionMoveTo) + zoneId = _vm->_globals->_saveData->_data[svLastZoneNum]; + else + zoneId = _zoneNum; + _vm->_linesMan->_route = NULL; + computeAndSetSpriteSize(); + setFlipSprite(0, false); + _homeRateCounter = 0; + _vm->_linesMan->_route = NULL; + _oldDirection = DIR_NONE; + if (zoneId > 0) { + ZoneItem *curZone = &_vm->_linesMan->_zone[zoneId]; + if (curZone->_destX && curZone->_destY && curZone->_destY != 31) { + if (curZone->_spriteIndex == -1) { + curZone->_destX = 0; + curZone->_destY = 0; + curZone->_spriteIndex = 0; + } else { + setSpriteIndex(0, curZone->_spriteIndex); + _vm->_globals->_actionDirection = curZone->_spriteIndex - 59; + } + } + } + } + _homeRateCounter = 0; + return; + } + if (_oldDirection == DIR_RIGHT) { + if (_oldFrameIndex < 24 || _oldFrameIndex > 35) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 24; + } else { + int deltaX = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedX; + int deltaY = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY; + + if (_sprite[0]._zoomFactor < 0) { + deltaX = _vm->_graphicsMan->zoomOut(deltaX, -_sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } else if (_sprite[0]._zoomFactor > 0) { + deltaX = _vm->_graphicsMan->zoomIn(deltaX, _sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = _oldCharacterPosX + deltaX; + oldPosY = _oldCharacterPosY + deltaY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 35) + oldFrameIdx = 24; + } + _homeRateCounter = 5 / _vm->_globals->_speed; + } + if (_oldDirection == DIR_LEFT) { + if (_oldFrameIndex < 24 || _oldFrameIndex > 35) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 24; + } else { + int deltaX = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedX; + int deltaY = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY; + if (_sprite[0]._zoomFactor < 0) { + deltaX = _vm->_graphicsMan->zoomOut(deltaX, -_sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } else if (_sprite[0]._zoomFactor > 0) { + deltaX = _vm->_graphicsMan->zoomIn(deltaX, _sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = _oldCharacterPosX - deltaX; + oldPosY = _oldCharacterPosY - deltaY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 35) + oldFrameIdx = 24; + } + _homeRateCounter = 5 / _vm->_globals->_speed; + } + if (_oldDirection == DIR_UP) { + if (_oldFrameIndex > 11) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 0; + } else { + int deltaY = abs(_vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY); + if (_sprite[0]._zoomFactor < 0) { + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } else if (_sprite[0]._zoomFactor > 0) { + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY - deltaY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 11) + oldFrameIdx = 0; + } + _homeRateCounter = 4 / _vm->_globals->_speed; + } + + if (_oldDirection == DIR_DOWN) { + if (_oldFrameIndex < 48 || _oldFrameIndex > 59) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 48; + } else { + int deltaY = abs(_vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY); + if (_sprite[0]._zoomFactor < 0) { + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } else if (_sprite[0]._zoomFactor > 0) { + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = _oldCharacterPosX; + oldPosY = deltaY + _oldCharacterPosY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 59) + oldFrameIdx = 48; + } + _homeRateCounter = 4 / _vm->_globals->_speed; + } + if (_oldDirection == DIR_UP_RIGHT) { + if (_oldFrameIndex < 12 || _oldFrameIndex > 23) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 12; + } else { + int deltaX = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedX; + int deltaY = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY; + if (_sprite[0]._zoomFactor < 0) { + deltaX = _vm->_graphicsMan->zoomOut(deltaX, -_sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } + if (_sprite[0]._zoomFactor > 0) { + deltaX = _vm->_graphicsMan->zoomIn(deltaX, _sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = deltaX + _oldCharacterPosX; + oldPosY = _oldCharacterPosY + deltaY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 23) + oldFrameIdx = 12; + } + _homeRateCounter = 5 / _vm->_globals->_speed; + } + if (_oldDirection == DIR_UP_LEFT) { + if (_oldFrameIndex < 12 || _oldFrameIndex > 23) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 12; + } else { + int deltaX = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedX; + int deltaY = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY; + if (_sprite[0]._zoomFactor < 0) { + deltaX = _vm->_graphicsMan->zoomOut(deltaX, -_sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } else if (_sprite[0]._zoomFactor > 0) { + deltaX = _vm->_graphicsMan->zoomIn(deltaX, _sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = _oldCharacterPosX - deltaX; + oldPosY = _oldCharacterPosY + deltaY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 23) + oldFrameIdx = 12; + } + _homeRateCounter = 5 / _vm->_globals->_speed; + } + if (_oldDirection == DIR_DOWN_RIGHT) { + if (_oldFrameIndex < 36 || _oldFrameIndex > 47) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 36; + } else { + int deltaX = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedX; + int deltaY = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY; + if (_sprite[0]._zoomFactor < 0) { + deltaX = _vm->_graphicsMan->zoomOut(deltaX, -_sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } + if (_sprite[0]._zoomFactor > 0) { + deltaX = _vm->_graphicsMan->zoomIn(deltaX, _sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = deltaX + _oldCharacterPosX; + oldPosY = _oldCharacterPosY + deltaY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 47) + oldFrameIdx = 36; + } + _homeRateCounter = 5 / _vm->_globals->_speed; + } + if (_oldDirection == DIR_DOWN_LEFT) { + if (_oldFrameIndex < 36 || _oldFrameIndex > 47) { + oldPosX = _oldCharacterPosX; + oldPosY = _oldCharacterPosY; + oldFrameIdx = 36; + } else { + int deltaX = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedX; + int deltaY = _vm->_globals->_hopkinsItem[_oldFrameIndex]._speedY; + if (_sprite[0]._zoomFactor < 0) { + deltaX = _vm->_graphicsMan->zoomOut(deltaX, -_sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomOut(deltaY, -_sprite[0]._zoomFactor); + } + if (_sprite[0]._zoomFactor > 0) { + deltaX = _vm->_graphicsMan->zoomIn(deltaX, _sprite[0]._zoomFactor); + deltaY = _vm->_graphicsMan->zoomIn(deltaY, _sprite[0]._zoomFactor); + } + oldPosX = _oldCharacterPosX - deltaX; + oldPosY = _oldCharacterPosY + deltaY; + oldFrameIdx = _oldFrameIndex + 1; + if (oldFrameIdx > 47) + oldFrameIdx = 36; + } + _homeRateCounter = 5 / _vm->_globals->_speed; + } + bool loopCond = false; + do { + newPosX = _vm->_linesMan->_route->_x; + newPosY = _vm->_linesMan->_route->_y; + newDirection = (Directions)_vm->_linesMan->_route->_dir; + _vm->_linesMan->_route++; + + if (newPosX == -1 && newPosY == -1) { + int zoneId; + if (_vm->_globals->_actionMoveTo) + zoneId = _vm->_globals->_saveData->_data[svLastZoneNum]; + else + zoneId = _zoneNum; + setSpriteIndex(0, _oldDirection + 59); + _vm->_globals->_actionDirection = DIR_NONE; + _vm->_linesMan->_route = NULL; + computeAndSetSpriteSize(); + setFlipSprite(0, false); + _homeRateCounter = 0; + _oldDirection = DIR_NONE; + _oldCharacterPosX = getSpriteX(0); + _oldCharacterPosY = getSpriteY(0); + + if (zoneId > 0) { + ZoneItem *curZone = &_vm->_linesMan->_zone[zoneId]; + if (curZone->_destX && curZone->_destY && curZone->_destY != 31) { + if ( curZone->_spriteIndex == -1) { + curZone->_destX = 0; + curZone->_destY = 0; + curZone->_spriteIndex = 0; + } else { + setSpriteIndex(0, curZone->_spriteIndex); + _vm->_globals->_actionDirection = curZone->_spriteIndex - 59; + } + } + } + _homeRateCounter = 0; + return; + } + if (_oldDirection != newDirection) + break; + if ((newDirection == DIR_RIGHT && newPosX >= oldPosX) || (_oldDirection == DIR_LEFT && newPosX <= oldPosX) || + (_oldDirection == DIR_UP && newPosY <= oldPosY) || (_oldDirection == DIR_DOWN && newPosY >= oldPosY) || + (_oldDirection == DIR_UP_RIGHT && newPosX >= oldPosX) || (_oldDirection == DIR_UP_LEFT && newPosX <= oldPosX) || + (_oldDirection == DIR_DOWN_RIGHT && newPosX >= oldPosX) || (_oldDirection == DIR_DOWN_LEFT && newPosX <= oldPosX)) + loopCond = true; + } while (!loopCond); + if (loopCond) { + computeAndSetSpriteSize(); + if ((_oldDirection == DIR_DOWN_LEFT) || (_oldDirection == DIR_LEFT) || (_oldDirection == DIR_UP_LEFT)) + setFlipSprite(0, true); + + if ((_oldDirection == DIR_UP) || (_oldDirection == DIR_UP_RIGHT) || (_oldDirection == DIR_RIGHT) || + (_oldDirection == DIR_DOWN_RIGHT) || (_oldDirection == DIR_DOWN)) + setFlipSprite(0, false); + + setSpriteX(0, newPosX); + setSpriteY(0, newPosY); + setSpriteIndex(0, oldFrameIdx); + } else { + if ((_oldDirection == DIR_DOWN_LEFT) || (_oldDirection == DIR_LEFT) || (_oldDirection == DIR_UP_LEFT)) + setFlipSprite(0, true); + + if ((_oldDirection == DIR_UP) || (_oldDirection == DIR_UP_RIGHT) || (_oldDirection == DIR_RIGHT) || + (_oldDirection == DIR_DOWN_RIGHT) || (_oldDirection == DIR_DOWN)) + setFlipSprite(0, false); + _homeRateCounter = 0; + } + _oldDirection = newDirection; + _oldDirectionSpriteIdx = newDirection + 59; + _oldFrameIndex = oldFrameIdx; + _oldCharacterPosX = newPosX; + _oldCharacterPosY = newPosY; +} + +void ObjectsManager::goHome2() { + if (_vm->_linesMan->_route == NULL) + return; + + int realSpeed = 2; + if (_vm->_globals->_speed == 2) + realSpeed = 4; + else if (_vm->_globals->_speed == 3) + realSpeed = 6; + + int countColisionPixel = 0; + + for (;;) { + int nexPosX = _vm->_linesMan->_route->_x; + int newPosY = _vm->_linesMan->_route->_y; + Directions newDirection = (Directions)_vm->_linesMan->_route->_dir; + _vm->_linesMan->_route++; + + if ((nexPosX == -1) && (newPosY == -1)) + break; + + ++countColisionPixel; + if (countColisionPixel >= realSpeed) { + _lastDirection = newDirection; + setSpriteX(0, nexPosX); + setSpriteY(0, newPosY); + switch (_lastDirection) { + case DIR_UP: + setSpriteIndex(0, 4); + break; + case DIR_RIGHT: + setSpriteIndex(0, 5); + break; + case DIR_DOWN: + setSpriteIndex(0, 6); + break; + case DIR_LEFT: + setSpriteIndex(0, 7); + break; + default: + break; + } + + return; + } + } + + switch (_lastDirection) { + case DIR_UP: + setSpriteIndex(0, 0); + break; + case DIR_RIGHT: + setSpriteIndex(0, 1); + break; + case DIR_DOWN: + setSpriteIndex(0, 2); + break; + case DIR_LEFT: + setSpriteIndex(0, 3); + break; + default: + break; + } + + _vm->_linesMan->_route = NULL; +} + +/** + * Load Zone + */ +void ObjectsManager::loadZone(const Common::String &file) { + for (int i = 1; i <= 100; i++) { + ZoneItem *curZone = &_vm->_linesMan->_zone[i]; + curZone->_destX = 0; + curZone->_destY = 0; + curZone->_spriteIndex = 0; + curZone->_verbFl1 = 0; + curZone->_verbFl2 = 0; + curZone->_verbFl3 = 0; + curZone->_verbFl4 = 0; + curZone->_verbFl5 = 0; + curZone->_verbFl6 = 0; + curZone->_verbFl7 = 0; + curZone->_verbFl8 = 0; + curZone->_verbFl9 = 0; + curZone->_verbFl10 = 0; + curZone->_messageId = 0; + curZone->_enabledFl = false; + } + + Common::File f; + if (!f.exists(file)) + error("File not found : %s", file.c_str()); + + byte *ptr = _vm->_fileIO->loadFile(file); + int bufId = 0; + int zoneLineIdx = 0; + int bobZoneIdx; + do { + bobZoneIdx = READ_LE_INT16((uint16 *)ptr + bufId); + if (bobZoneIdx != -1) { + _vm->_linesMan->addZoneLine( + zoneLineIdx, + READ_LE_UINT16((uint16 *)ptr + bufId + 1), + READ_LE_UINT16((uint16 *)ptr + bufId + 2), + READ_LE_UINT16((uint16 *)ptr + bufId + 3), + READ_LE_UINT16((uint16 *)ptr + bufId + 4), + bobZoneIdx); + _vm->_linesMan->_zone[bobZoneIdx]._enabledFl = true; + } + bufId += 5; + ++zoneLineIdx; + } while (bobZoneIdx != -1); + + for (int i = 1; i <= 100; i++) { + ZoneItem *curZone = &_vm->_linesMan->_zone[i]; + curZone->_destX = READ_LE_INT16((uint16 *)ptr + bufId); + curZone->_destY = READ_LE_INT16((uint16 *)ptr + bufId + 1); + curZone->_spriteIndex = READ_LE_INT16((uint16 *)ptr + bufId + 2); + bufId += 3; + } + + byte *verbData = (ptr + 10 * zoneLineIdx + 606); + bufId = 0; + for (int i = 1; i <= 100; i++) { + ZoneItem *curZone = &_vm->_linesMan->_zone[i]; + curZone->_verbFl1 = verbData[bufId]; + curZone->_verbFl2 = verbData[bufId + 1]; + curZone->_verbFl3 = verbData[bufId + 2]; + curZone->_verbFl4 = verbData[bufId + 3]; + curZone->_verbFl5 = verbData[bufId + 4]; + curZone->_verbFl6 = verbData[bufId + 5]; + curZone->_verbFl7 = verbData[bufId + 6]; + curZone->_verbFl8 = verbData[bufId + 7]; + curZone->_verbFl9 = verbData[bufId + 8]; + curZone->_verbFl10 = verbData[bufId + 9]; + + bufId += 10; + } + verbData += 1010; + for (int i = 0; i < 100; i++) + _vm->_linesMan->_zone[i + 1]._messageId = READ_LE_UINT16(verbData + 2 * i); + + _vm->_globals->freeMemory(ptr); + _vm->_linesMan->initSquareZones(); +} + +void ObjectsManager::handleCityMap() { + _vm->_dialog->_inventFl = false; + _vm->_events->_gameKey = KEY_NONE; + _vm->_linesMan->setMaxLineIdx(1); + _vm->_globals->_characterMaxPosY = 440; + _vm->_globals->_cityMapEnabledFl = true; + _vm->_graphicsMan->_noFadingFl = false; + _vm->_globals->_freezeCharacterFl = false; + _spritePtr = NULL; + _vm->_globals->_exitId = 0; + _vm->_globals->_checkDistanceFl = true; + _vm->_soundMan->playSound(31); + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + _vm->_graphicsMan->loadImage("PLAN"); + _vm->_linesMan->loadLines("PLAN.OB2"); + loadHidingItems("PLAN.CA2"); + loadZone("PLAN.ZO2"); + _spritePtr = _vm->_fileIO->loadFile("VOITURE.SPR"); + _vm->_animMan->loadAnim("PLAN"); + _vm->_graphicsMan->displayAllBob(); + _vm->_graphicsMan->initScreen("PLAN", 2, false); + for (int i = 0; i <= 15; i++) + disableHidingItem(i); + disableHidingItem(19); + disableHidingItem(20); + enableHidingBehavior(); + + if (!_mapCarPosX && !_mapCarPosY) { + _mapCarPosX = 900; + _mapCarPosY = 319; + } + addStaticSprite(_spritePtr, Common::Point(_mapCarPosX, _mapCarPosY), 0, 1, 0, false, 5, 5); + _vm->_events->setMouseXY(_mapCarPosX, _mapCarPosY); + _vm->_events->mouseOn(); + _vm->_graphicsMan->scrollScreen(getSpriteX(0) - 320); + _vm->_graphicsMan->_scrollOffset = getSpriteX(0) - 320; + animateSprite(0); + _vm->_linesMan->_route = NULL; + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + + for (int i = 0; i <= 4; i++) + _vm->_events->refreshScreenAndEvents(); + + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + _vm->_graphicsMan->fadeInLong(); + _vm->_events->changeMouseCursor(4); + _vm->_graphicsMan->_noFadingFl = false; + + bool loopCond = false; + do { + int mouseButton = _vm->_events->getMouseButton(); + if (mouseButton) { + // First cop call : Go to the bank and free the hostages + if (_vm->_globals->_saveData->_data[svBankAttackAnimPlayedFl] == 1 && !_vm->_globals->_saveData->_data[svCopCall1PlayedFl]) { + _vm->_globals->_saveData->_data[svCopCall1PlayedFl] = 1; + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("APPEL1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + mouseButton = 0; + } + // Second cop call: Helico has been found in the empty lot + if (_vm->_globals->_saveData->_data[svFreedHostageFl] == 1 && !_vm->_globals->_saveData->_data[svCopCall2PlayedFl]) { + _vm->_globals->_saveData->_data[svCopCall2PlayedFl] = 1; + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("APPEL2.pe2"); + _vm->_globals->_introSpeechOffFl = false; + mouseButton = 0; + _vm->_events->_curMouseButton = 0; + } + if (mouseButton == 1) + handleLeftButton(); + } + + _vm->_linesMan->checkZone(); + goHome2(); + + if (_vm->_linesMan->_route == NULL && _vm->_globals->_actionMoveTo) + paradise(); + _vm->_events->refreshScreenAndEvents(); + + if (_vm->_globals->_exitId) + loopCond = true; + } while (!_vm->shouldQuit() && !loopCond); + + if (!_vm->_graphicsMan->_noFadingFl) + _vm->_graphicsMan->fadeOutLong(); + _vm->_globals->_eventMode = EVENTMODE_DEFAULT; + _vm->_graphicsMan->_noFadingFl = false; + _mapCarPosX = getSpriteX(0); + _mapCarPosY = getSpriteY(0); + removeSprite(0); + _spritePtr = _vm->_globals->freeMemory(_spritePtr); + clearScreen(); + _vm->_globals->_cityMapEnabledFl = false; +} + +/** + * Handle Left button + */ +void ObjectsManager::handleLeftButton() { + _vm->_fontMan->hideText(9); + int destX = _vm->_events->getMouseX(); + int destY = _vm->_events->getMouseY(); + + if (!_vm->_dialog->_inventFl && !_vm->_globals->_cityMapEnabledFl && + destX > _vm->_graphicsMan->_scrollOffset - 30 && destX < _vm->_graphicsMan->_scrollOffset + 50 && + destY > -30 && destY < 50) { + int oldMouseCursor = _vm->_events->_mouseCursorId; + _vm->_dialog->_inventFl = true; + _vm->_dialog->showInventory(); + _vm->_dialog->_inventFl = false; + _vm->_events->_gameKey = KEY_NONE; + if (!_vm->_globals->_exitId) { + _vm->_dialog->_inventFl = false; + _vm->_events->_mouseCursorId = oldMouseCursor; + } + return; + } + if (_vm->_globals->_saveData->_data[svField354] == 1 && !_vm->_globals->_cityMapEnabledFl + && destX >= 533 && destX <= 559 && destY >= 26 && destY <= 59) { + changeCharacterHead(CHARACTER_HOPKINS_CLONE, CHARACTER_HOPKINS); + return; + } + if (_vm->_globals->_saveData->_data[svField356] == 1 && !_vm->_globals->_cityMapEnabledFl + && destX >= 533 && destX <= 559 && destY >= 26 && destY <= 48) { + changeCharacterHead(CHARACTER_SAMANTHA, CHARACTER_HOPKINS); + return; + } + if (_vm->_globals->_saveData->_data[svField357] == 1) { + if (_vm->_globals->_saveData->_data[svField353] == 1 && !_vm->_globals->_cityMapEnabledFl + && destX >= 533 && destX <= 559 && destY >= 26 && destY <= 59) { + changeCharacterHead(CHARACTER_HOPKINS, CHARACTER_HOPKINS_CLONE); + return; + } + if (_vm->_globals->_saveData->_data[svField355] == 1 && !_vm->_globals->_cityMapEnabledFl + && destX >= 567 && destX <= 593 && destY >= 26 && destY <= 59) { + changeCharacterHead(CHARACTER_HOPKINS, CHARACTER_SAMANTHA); + return; + } + } + if (_vm->_globals->_cityMapEnabledFl && _vm->_globals->_actionMoveTo) { + _vm->_linesMan->checkZone(); + if (_zoneNum <= 0) + return; + int routeIdx = 0; + do { + _vm->_linesMan->_testRoute2[routeIdx] = _vm->_linesMan->_route[routeIdx]; + ++routeIdx; + } while (_vm->_linesMan->_route[routeIdx]._x != -1); + + _vm->_linesMan->_testRoute2[routeIdx].invalidate(); + } + + if (_vm->_globals->_actionMoveTo) { + _vm->_linesMan->checkZone(); + _vm->_globals->_actionMoveTo = false; + _vm->_globals->_saveData->_data[svLastMouseCursor] = 0; + _vm->_globals->_saveData->_data[svLastZoneNum] = 0; + } + + if (_vm->_globals->_cityMapEnabledFl && (_vm->_events->_mouseCursorId != 4 || _zoneNum <= 0)) + return; + if (_zoneNum != -1 && _zoneNum != 0) { + ZoneItem *curZone = &_vm->_linesMan->_zone[_zoneNum]; + if (curZone->_destX && curZone->_destY && curZone->_destY != 31) { + destX = curZone->_destX; + destY = curZone->_destY; + } + } + _vm->_globals->_actionMoveTo = false; + RouteItem *oldRoute = _vm->_linesMan->_route; + _vm->_linesMan->_route = NULL; + if (_forestFl && _zoneNum >= 20 && _zoneNum <= 23) { + if (getSpriteY(0) > 374 && getSpriteY(0) <= 410) { + _vm->_linesMan->_route = NULL; + setSpriteIndex(0, _oldDirectionSpriteIdx); + _vm->_globals->_actionDirection = DIR_NONE; + _vm->_linesMan->_route = NULL; + computeAndSetSpriteSize(); + setFlipSprite(0, false); + _homeRateCounter = 0; + _oldDirection = DIR_NONE; + } else { + _vm->_linesMan->_route = _vm->_linesMan->findRoute(getSpriteX(0), getSpriteY(0), getSpriteX(0), 390); + if (_vm->_linesMan->_route) + _vm->_linesMan->optimizeRoute(_vm->_linesMan->_route); + _oldCharacterPosX = getSpriteX(0); + _oldCharacterPosY = getSpriteY(0); + _homeRateCounter = 0; + if (_vm->_linesMan->_route || oldRoute == _vm->_linesMan->_route) { + _oldDirection = DIR_NONE; + } else { + _vm->_linesMan->_route = oldRoute; + } + } + } else { + if (!_vm->_globals->_freezeCharacterFl && !_vm->_globals->_cityMapEnabledFl) { + _vm->_linesMan->_route = _vm->_linesMan->findRoute(getSpriteX(0), getSpriteY(0), destX, destY); + if (_vm->_linesMan->_route) + _vm->_linesMan->optimizeRoute(_vm->_linesMan->_route); + _oldCharacterPosX = getSpriteX(0); + _oldCharacterPosY = getSpriteY(0); + _homeRateCounter = 0; + if (_vm->_linesMan->_route || oldRoute == _vm->_linesMan->_route) + _oldDirection = DIR_NONE; + else + _vm->_linesMan->_route = oldRoute; + } + } + + if (!_vm->_globals->_freezeCharacterFl && _vm->_globals->_cityMapEnabledFl) + _vm->_linesMan->_route = _vm->_linesMan->cityMapCarRoute(getSpriteX(0), getSpriteY(0), destX, destY); + + if (_zoneNum != -1 && _zoneNum != 0) { + if (_vm->_events->_mouseCursorId == 23) + _vm->_globals->_saveData->_data[svLastMouseCursor] = 5; + else + _vm->_globals->_saveData->_data[svLastMouseCursor] = _vm->_events->_mouseCursorId; + + if (_vm->_globals->_cityMapEnabledFl) + _vm->_globals->_saveData->_data[svLastMouseCursor] = 6; + _vm->_globals->_saveData->_data[svLastZoneNum] = _zoneNum; + _vm->_globals->_saveData->_data[svLastObjectIndex] = _curObjectIndex; + _vm->_globals->_actionMoveTo = true; + } + _vm->_fontMan->hideText(5); + _vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100); + if (_vm->_globals->_screenId == 20 && _vm->_globals->_saveData->_data[svField132] == 1 + && _curObjectIndex == 20 && _zoneNum == 12 + && _vm->_events->_mouseCursorId == 23) { + // Special case for throwing darts at the switch in Purgatory - the player shouldn't move + _vm->_linesMan->_route = NULL; + getSpriteX(0); + getSpriteY(0); + } +} + +void ObjectsManager::paradise() { + char result = _vm->_globals->_saveData->_data[svLastMouseCursor]; + if (result && _vm->_globals->_saveData->_data[svLastZoneNum] && result != 4 && result > 3) { + _vm->_fontMan->hideText(5); + if (!_forestFl || _zoneNum < 20 || _zoneNum > 23) { + if (_vm->_graphicsMan->_largeScreenFl) { + _vm->_graphicsMan->_scrollStatus = 2; + if (_vm->_events->_startPos.x + 320 - getSpriteX(0) > 160) { + bool loopCond = false; + do { + _vm->_graphicsMan->_scrollPosX -= _vm->_graphicsMan->_scrollSpeed; + if (_vm->_graphicsMan->_scrollPosX < 0) { + _vm->_graphicsMan->_scrollPosX = 0; + loopCond = true; + } + if (_vm->_graphicsMan->_scrollPosX > SCREEN_WIDTH) { + _vm->_graphicsMan->_scrollPosX = SCREEN_WIDTH; + loopCond = true; + } + if (_vm->_events->getMouseX() > _vm->_graphicsMan->_scrollPosX + 620) + _vm->_events->setMouseXY(_vm->_events->_mousePos.x - 4, _vm->_events->getMouseY()); + + _vm->_events->refreshScreenAndEvents(); + } while (!loopCond && _vm->_events->_startPos.x > getSpriteX(0) - 320); + } else if (_vm->_events->_startPos.x + 320 - getSpriteX(0) < -160) { + bool loopCond = false; + do { + _vm->_graphicsMan->_scrollPosX += _vm->_graphicsMan->_scrollSpeed; + if (_vm->_graphicsMan->_scrollPosX < 0) { + _vm->_graphicsMan->_scrollPosX = 0; + loopCond = true; + } + if (_vm->_graphicsMan->_scrollPosX > SCREEN_WIDTH) { + _vm->_graphicsMan->_scrollPosX = SCREEN_WIDTH; + loopCond = true; + } + if (_vm->_events->getMouseX() < _vm->_graphicsMan->_scrollPosX + 10) + _vm->_events->setMouseXY(_vm->_events->_mousePos.x + 4, _vm->_events->getMouseY()); + + _vm->_events->refreshScreenAndEvents(); + } while (!loopCond && _vm->_events->_startPos.x < getSpriteX(0) - 320); + } + if (_vm->_events->getMouseX() > _vm->_graphicsMan->_scrollPosX + 620) + _vm->_events->setMouseXY(_vm->_graphicsMan->_scrollPosX + 610, 0); + if (_vm->_events->getMouseX() < _vm->_graphicsMan->_scrollPosX + 10) + _vm->_events->setMouseXY(_vm->_graphicsMan->_scrollPosX + 10, 0); + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->_scrollStatus = 0; + } + _vm->_talkMan->handleAnswer(_vm->_globals->_saveData->_data[svLastZoneNum], _vm->_globals->_saveData->_data[svLastMouseCursor]); + } else { + _vm->_talkMan->handleForestAnswser(_vm->_globals->_saveData->_data[svLastZoneNum], _vm->_globals->_saveData->_data[svLastMouseCursor]); + } + _vm->_events->changeMouseCursor(4); + if (_zoneNum != -1 && _zoneNum != 0 && !_vm->_linesMan->_zone[_zoneNum]._enabledFl) { + _zoneNum = -1; + _forceZoneFl = true; + } + if (_zoneNum != _vm->_globals->_saveData->_data[svLastZoneNum] || _zoneNum == -1 || _zoneNum == 0) { + _vm->_events->_mouseCursorId = 4; + _changeVerbFl = false; + } else { + _vm->_events->_mouseCursorId = _vm->_globals->_saveData->_data[svLastMouseCursor]; + if (_changeVerbFl) { + nextVerbIcon(); + _changeVerbFl = false; + } + if (_vm->_events->_mouseCursorId == 5) + _vm->_events->_mouseCursorId = 4; + } + if (_vm->_events->_mouseCursorId != 23) + _vm->_events->changeMouseCursor(_vm->_events->_mouseCursorId); + _zoneNum = 0; + _vm->_globals->_saveData->_data[svLastMouseCursor] = 0; + _vm->_globals->_saveData->_data[svLastZoneNum] = 0; + } + if (_vm->_globals->_cityMapEnabledFl) { + _vm->_events->_mouseCursorId = 0; + _vm->_events->changeMouseCursor(0); + } + if (_vm->_globals->_freezeCharacterFl && _vm->_events->_mouseCursorId == 4) { + if (_zoneNum != -1 && _zoneNum != 0) + handleRightButton(); + } + _vm->_globals->_actionMoveTo = false; +} + +/** + * Clear Screen + */ +void ObjectsManager::clearScreen() { + clearSprite(); + _vm->_graphicsMan->endDisplayBob(); + _vm->_fontMan->hideText(5); + _vm->_fontMan->hideText(9); + clearVBob(); + _vm->_animMan->clearAnim(); + _vm->_linesMan->clearAllZones(); + _vm->_linesMan->resetLines(); + resetHidingItems(); + + for (int i = 0; i <= 48; i++) { + _vm->_linesMan->_bobZone[i] = 0; + _vm->_linesMan->_bobZoneFl[i] = false; + } + _vm->_events->_mouseCursorId = 4; + _verb = 4; + _zoneNum = 0; + _forceZoneFl = true; + _vm->_linesMan->resetLinesNumb(); + _vm->_linesMan->resetLastLine(); + _vm->_linesMan->_route = NULL; + _vm->_globals->_answerBuffer = _vm->_globals->freeMemory(_vm->_globals->_answerBuffer); + _vm->_globals->_levelSpriteBuf = _vm->_globals->freeMemory(_vm->_globals->_levelSpriteBuf); + _vm->_events->_startPos.x = 0; + _vm->_events->_mouseSpriteId = 0; + _vm->_globals->_saveData->_data[svLastMouseCursor] = 0; + _vm->_globals->_saveData->_data[svLastZoneNum] = 0; + _vm->_globals->_actionMoveTo = false; + _forceZoneFl = true; + _changeVerbFl = false; + _vm->_linesMan->_route = NULL; + _oldDirection = DIR_NONE; + _vm->_graphicsMan->resetDirtyRects(); +} + +/** + * Change the currently active player face / Head + * @param oldCharacter Previously played character + * @param newCharacter New character to play + */ +void ObjectsManager::changeCharacterHead(PlayerCharacter oldCharacter, PlayerCharacter newCharacter) { + CharacterLocation *loc; + + _changeHeadFl = true; + _vm->_graphicsMan->copySurface(_vm->_graphicsMan->_backBuffer, 532, 25, 65, 40, _vm->_graphicsMan->_frontBuffer, 532, 25); + _vm->_graphicsMan->addDirtyRect(532, 25, 597, 65); + _vm->_globals->_checkDistanceFl = true; + _vm->_linesMan->_route = NULL; + + if (oldCharacter == CHARACTER_SAMANTHA && newCharacter == CHARACTER_HOPKINS + && _vm->_globals->_saveData->_realHopkins._location == _vm->_globals->_screenId) { + _changeHeadFl = false; + loc = &_vm->_globals->_saveData->_samantha; + loc->_pos.x = getSpriteX(0); + loc->_pos.y = getSpriteY(0); + loc->_startSpriteIndex = 64; + loc->_location = _vm->_globals->_screenId; + loc->_zoomFactor = _sprite[0]._zoomFactor; + + removeSprite(1); + addStaticSprite(_headSprites, loc->_pos, 1, 3, loc->_zoomFactor, false, 20, 127); + animateSprite(1); + removeSprite(0); + + _vm->_globals->_saveData->_data[svField354] = 0; + _vm->_globals->_saveData->_data[svField356] = 0; + _vm->_globals->_saveData->_data[svField357] = 1; + + loc = &_vm->_globals->_saveData->_realHopkins; + _vm->_globals->_characterSpriteBuf = _vm->_fileIO->loadFile("PERSO.SPR"); + _vm->_globals->_characterType = CHARACTER_HOPKINS; + addStaticSprite(_vm->_globals->_characterSpriteBuf, loc->_pos, 0, 64, loc->_zoomFactor, false, 34, 190); + animateSprite(0); + _vm->_globals->loadCharacterData(); + } else if (oldCharacter == CHARACTER_HOPKINS && newCharacter == CHARACTER_SAMANTHA + && _vm->_globals->_saveData->_samantha._location == _vm->_globals->_screenId) { + _changeHeadFl = false; + loc = &_vm->_globals->_saveData->_realHopkins; + loc->_pos.x = getSpriteX(0); + loc->_pos.y = getSpriteY(0); + loc->_startSpriteIndex = 64; + loc->_location = _vm->_globals->_screenId; + loc->_zoomFactor = _sprite[0]._zoomFactor; + + removeSprite(1); + addStaticSprite(_headSprites, loc->_pos, 1, 2, loc->_zoomFactor, false, 34, 190); + animateSprite(1); + removeSprite(0); + + _vm->_globals->_saveData->_data[svField354] = 0; + _vm->_globals->_saveData->_data[svField356] = 1; + _vm->_globals->_saveData->_data[svField357] = 0; + + loc = &_vm->_globals->_saveData->_samantha; + _vm->_globals->_characterSpriteBuf = _vm->_fileIO->loadFile("PSAMAN.SPR"); + _vm->_globals->_characterType = CHARACTER_SAMANTHA; + addStaticSprite(_vm->_globals->_characterSpriteBuf, loc->_pos, 0, 64, loc->_zoomFactor, false, 20, 127); + animateSprite(0); + _vm->_globals->loadCharacterData(); + } else { + switch (oldCharacter) { + case CHARACTER_HOPKINS: + loc = &_vm->_globals->_saveData->_realHopkins; + loc->_pos.x = getSpriteX(0); + loc->_pos.y = getSpriteY(0); + loc->_startSpriteIndex = 64; + loc->_location = _vm->_globals->_screenId; + loc->_zoomFactor = _sprite[0]._zoomFactor; + break; + case CHARACTER_HOPKINS_CLONE: + loc = &_vm->_globals->_saveData->_cloneHopkins; + loc->_pos.x = getSpriteX(0); + loc->_pos.y = getSpriteY(0); + loc->_startSpriteIndex = 64; + loc->_location = _vm->_globals->_screenId; + loc->_zoomFactor = _sprite[0]._zoomFactor; + break; + case CHARACTER_SAMANTHA: + loc = &_vm->_globals->_saveData->_samantha; + loc->_pos.x = getSpriteX(0); + loc->_pos.y = getSpriteY(0); + loc->_startSpriteIndex = 64; + loc->_location = _vm->_globals->_screenId; + loc->_zoomFactor = _sprite[0]._zoomFactor; + break; + default: + break; + } + + switch (newCharacter) { + case CHARACTER_HOPKINS: + _vm->_globals->_saveData->_data[svHopkinsCloneFl] = 0; + _vm->_globals->_saveData->_data[svField354] = 0; + _vm->_globals->_saveData->_data[svField356] = 0; + _vm->_globals->_saveData->_data[svField357] = 1; + _vm->_globals->_exitId = _vm->_globals->_saveData->_realHopkins._location; + break; + case CHARACTER_HOPKINS_CLONE: + _vm->_globals->_saveData->_data[svHopkinsCloneFl] = 1; + _vm->_globals->_saveData->_data[svField354] = 1; + _vm->_globals->_saveData->_data[svField356] = 0; + _vm->_globals->_saveData->_data[svField357] = 0; + _vm->_globals->_exitId = _vm->_globals->_saveData->_cloneHopkins._location; + break; + case CHARACTER_SAMANTHA: + _vm->_globals->_saveData->_data[svHopkinsCloneFl] = 0; + _vm->_globals->_saveData->_data[svField354] = 0; + _vm->_globals->_saveData->_data[svField356] = 1; + _vm->_globals->_saveData->_data[svField357] = 0; + _vm->_globals->_exitId = _vm->_globals->_saveData->_samantha._location; + break; + } + } +} + +// Check Size +void ObjectsManager::computeAndSetSpriteSize() { + int size = _vm->_globals->_spriteSize[getSpriteY(0)]; + if (_vm->_globals->_characterType == CHARACTER_HOPKINS_CLONE) { + size = 20 * (5 * abs(size) - 100) / -80; + } else if (_vm->_globals->_characterType == CHARACTER_SAMANTHA) { + size = 20 * (5 * abs(size) - 165) / -67; + } + setSpriteZoom(0, size); +} + +/** + * Get next verb icon (or text) + */ +void ObjectsManager::nextVerbIcon() { + _vm->_events->_mouseCursorId++; + + for(;;) { + if (_vm->_events->_mouseCursorId == 4) { + if (!_vm->_globals->_freezeCharacterFl || _zoneNum == -1 || _zoneNum == 0) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 5 || _vm->_events->_mouseCursorId == 6) { + _vm->_events->_mouseCursorId = 6; + if (_vm->_linesMan->_zone[_zoneNum]._verbFl1 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 7) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl2 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 8) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl3 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 9) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl4 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 10) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl5 == 1) + return; + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 11) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl6 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 12) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl7 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 13) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl8 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 14) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl9 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 15) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl10 == 1) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 16) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl1 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 17) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl4 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 18) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl5 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 19) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl6 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 20) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl7 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 21) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl10 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 22) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl8 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 23) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl3 == 2) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 24) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl4 == 3) + return; + + ++_vm->_events->_mouseCursorId; + } + + if (_vm->_events->_mouseCursorId == 25) { + if (_vm->_linesMan->_zone[_zoneNum]._verbFl9 == 2) + return; + } + _vm->_events->_mouseCursorId = 4; + } +} + +/** + * Handle Right button + */ +void ObjectsManager::handleRightButton() { + if (_zoneNum != -1 && _zoneNum != 0) { + nextVerbIcon(); + if (_vm->_events->_mouseCursorId != 23) + _vm->_events->changeMouseCursor(_vm->_events->_mouseCursorId); + _verb = _vm->_events->_mouseCursorId; + } +} + +/** + * Prepare border used to highlight the place below mouse cursor, in the inventory. + * Also set the mouse cursor + */ +void ObjectsManager::initBorder(int zoneIdx) { + _oldBorderPos = _borderPos; + _oldBorderSpriteIndex = _borderSpriteIndex; + if (zoneIdx >= 1 && zoneIdx <= 6) + _borderPos.y = 120; + else if (zoneIdx >= 7 && zoneIdx <= 12) + _borderPos.y = 158; + else if (zoneIdx >= 13 && zoneIdx <= 18) + _borderPos.y = 196; + else if (zoneIdx >= 19 && zoneIdx <= 24) + _borderPos.y = 234; + else if (zoneIdx >= 25 && zoneIdx <= 29) + _borderPos.y = 272; + else if (zoneIdx == 30) + _borderPos.y = 272; + else if (zoneIdx == 31) + _borderPos.y = 290; + + if (zoneIdx == 1 || zoneIdx == 7 || zoneIdx == 13 || zoneIdx == 19 || zoneIdx == 25) + _borderPos.x = _vm->_graphicsMan->_scrollOffset + 158; + else if (zoneIdx == 2 || zoneIdx == 8 || zoneIdx == 14 || zoneIdx == 20 || zoneIdx == 26) + _borderPos.x = _vm->_graphicsMan->_scrollOffset + 212; + else if (zoneIdx == 3 || zoneIdx == 9 || zoneIdx == 15 || zoneIdx == 21 || zoneIdx == 27) + _borderPos.x = _vm->_graphicsMan->_scrollOffset + 266; + else if (zoneIdx == 4 || zoneIdx == 10 || zoneIdx == 16 || zoneIdx == 22 || zoneIdx == 28) + _borderPos.x = _vm->_graphicsMan->_scrollOffset + 320; + else if (zoneIdx == 5 || zoneIdx == 11 || zoneIdx == 17 || zoneIdx == 23 || zoneIdx == 29) + _borderPos.x = _vm->_graphicsMan->_scrollOffset + 374; + else if (zoneIdx == 6 || zoneIdx == 12 || zoneIdx == 18 || zoneIdx == 24 || zoneIdx == 30 || zoneIdx == 31) + _borderPos.x = _vm->_graphicsMan->_scrollOffset + 428; + + if (zoneIdx >= 1 && zoneIdx <= 29) + _borderSpriteIndex = 0; + else if (zoneIdx == 30 || zoneIdx == 31) + _borderSpriteIndex = 2; + else if (!zoneIdx || zoneIdx == 32) { + _borderPos = Common::Point(0, 0); + _borderSpriteIndex = 0; + } + + if (!zoneIdx) + _vm->_events->_mouseCursorId = 0; + else if (zoneIdx >= 1 && zoneIdx <= 28) + _vm->_events->_mouseCursorId = 8; + else if (zoneIdx == 29) + _vm->_events->_mouseCursorId = 1; + else if (zoneIdx == 30) + _vm->_events->_mouseCursorId = 2; + else if (zoneIdx == 31) + _vm->_events->_mouseCursorId = 3; + else if (zoneIdx == 32) + _vm->_events->_mouseCursorId = 16; + + if (zoneIdx >= 1 && zoneIdx <= 28 && !_vm->_globals->_inventory[zoneIdx]) { + _vm->_events->_mouseCursorId = 0; + _borderPos = Common::Point(0, 0); + _borderSpriteIndex = 0; + } + + if (_vm->_events->_mouseCursorId != 23) + _vm->_events->changeMouseCursor(_vm->_events->_mouseCursorId); + _vm->_events->getMouseX(); + _vm->_events->getMouseY(); +} + +/** + * Get next icon for an object in the inventory + */ +void ObjectsManager::nextObjectIcon(int idx) { + int cursorId = _vm->_events->_mouseCursorId; + ObjectAuthIcon *curAuthIco = &_objectAuthIcons[_vm->_globals->_inventory[idx]]; + + if (cursorId == 0 || cursorId == 2 || cursorId == 3 || cursorId == 16) + return; + + int nextCursorId = cursorId + 1; + if (nextCursorId > 25) + nextCursorId = 6; + + do { + if (nextCursorId == 2 || nextCursorId == 5 || nextCursorId == 6) { + _vm->_events->_mouseCursorId = 6; + if (curAuthIco->_flag1 == 1) + return; + nextCursorId++; + } + if (nextCursorId == 7) { + _vm->_events->_mouseCursorId = 7; + if (curAuthIco->_flag2 == 1) + return; + nextCursorId++; + } + if (nextCursorId == 8) { + _vm->_events->_mouseCursorId = 8; + return; + } + if (nextCursorId == 9 || nextCursorId == 10) { + _vm->_events->_mouseCursorId = 10; + if (curAuthIco->_flag6 == 1) + return; + nextCursorId = 11; + } + + if (nextCursorId == 11) { + _vm->_events->_mouseCursorId = 11; + if (curAuthIco->_flag3 == 1) + return; + nextCursorId++; + } + + if (nextCursorId == 12 || nextCursorId == 13) { + _vm->_events->_mouseCursorId = 13; + if (curAuthIco->_flag4 == 1) + return; + nextCursorId = 14; + } + + if (nextCursorId == 14 || nextCursorId == 15) { + _vm->_events->_mouseCursorId = 15; + if (curAuthIco->_flag5 == 1) + return; + nextCursorId = 23; + } + + if (nextCursorId >= 16 && nextCursorId <= 23) { + _vm->_events->_mouseCursorId = 23; + if (curAuthIco->_flag5 == 2) + return; + nextCursorId = 24; + } + + if (nextCursorId == 24 || nextCursorId == 25) { + _vm->_events->_mouseCursorId = 25; + } + + nextCursorId = 6; + } while (curAuthIco->_flag6 != 2); +} + +void ObjectsManager::takeInventoryObject(int idx) { + if (_vm->_events->_mouseCursorId == 8) + changeObject(idx); +} + +void ObjectsManager::loadObjectIniFile() { + byte *data; + Common::String file; + int lastOpcodeResult = 1; + + file = "OBJET1.ini"; + bool fileFoundFl = false; + data = _vm->_fileIO->searchCat(file, RES_INI, fileFoundFl); + if (!fileFoundFl) { + data = _vm->_fileIO->loadFile(file); + if (data == NULL) + error("INI file %s not found", file.c_str()); + } + + if (READ_BE_UINT24(data) != MKTAG24('I', 'N', 'I')) + error("File %s is not an INI file", file.c_str()); + + for (;;) { + int opcodeType = _vm->_script->handleOpcode(data + 20 * lastOpcodeResult); + if (_vm->shouldQuit()) + return; + + if (opcodeType == 2) + lastOpcodeResult = _vm->_script->handleGoto(data + 20 * lastOpcodeResult); + else if (opcodeType == 3) + lastOpcodeResult = _vm->_script->handleIf(data, lastOpcodeResult); + + if (lastOpcodeResult == -1) + error("defective IFF function"); + + if (opcodeType == 1 || opcodeType == 4) + ++lastOpcodeResult; + else if (!opcodeType || opcodeType == 5) + break; + } + + _vm->_globals->freeMemory(data); +} + +void ObjectsManager::handleSpecialGames() { + byte *oldPalette; + + switch (_vm->_globals->_screenId) { + case 5: + if ((getSpriteY(0) > 399) || _vm->_globals->_saveData->_data[svField173]) + break; + + _vm->_globals->_saveData->_data[svField173] = 1; + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("flicspe1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + + if (_vm->_globals->_censorshipFl) + break; + + oldPalette = _vm->_globals->allocMemory(1000); + memcpy(oldPalette, _vm->_graphicsMan->_palette, 769); + + _vm->_graphicsMan->backupScreen(); + + if (!_vm->_graphicsMan->_lineNbr) + _vm->_graphicsMan->_scrollOffset = 0; + _vm->_graphicsMan->displayScreen(true); + _vm->_soundMan->_specialSoundNum = 198; + _charactersEnabledFl = true; + _vm->_animMan->unsetClearAnimFlag(); + _vm->_animMan->playAnim("OTAGE.ANM", "OTAGE.ANM", 1, 24, 500, true); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_graphicsMan->displayScreen(false); + + _vm->_graphicsMan->restoreScreen(); + + _charactersEnabledFl = false; + memcpy(_vm->_graphicsMan->_palette, oldPalette, 769); + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + _vm->_globals->freeMemory(oldPalette); + _vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_backBuffer, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + memcpy(_vm->_graphicsMan->_frontBuffer, _vm->_graphicsMan->_backBuffer, 614399); + + _vm->_graphicsMan->_scrollStatus = 0; + _vm->_graphicsMan->updateScreen(); + break; + case 20: + _vm->_globals->_saveData->_data[svField132] = (getSpriteX(0) > 65 && getSpriteX(0) <= 124 && getSpriteY(0) > 372 && getSpriteY(0) <= 398) ? 1 : 0; + break; + case 35: + if (_vm->_globals->_prevScreenId == 16) + handleForest(35, 500, 555, 100, 440, 1); + else if (_vm->_globals->_prevScreenId == 36) + handleForest(35, 6, 84, 100, 440, 4); + break; + case 36: + if (_vm->_globals->_prevScreenId == 35) + handleForest(36, 551, 633, 100, 440, 2); + else if (_vm->_globals->_prevScreenId == 37) + handleForest(36, 6, 84, 100, 440, 4); + break; + case 37: + if (_vm->_globals->_prevScreenId == 36) + handleForest(37, 551, 633, 100, 440, 1); + else if (_vm->_globals->_prevScreenId == 38) + handleForest(37, 392, 529, 100, 440, 2); + break; + case 38: + if (_vm->_globals->_prevScreenId == 37) + handleForest(38, 133, 252, 100, 440, 4); + else if (_vm->_globals->_prevScreenId == 39) + handleForest(38, 6, 84, 100, 440, 3); + break; + case 39: + if (_vm->_globals->_prevScreenId == 38) + handleForest(39, 551, 633, 100, 440, 2); + else if (_vm->_globals->_prevScreenId == 40) + handleForest(39, 6, 84, 100, 440, 3); + break; + case 40: + if (_vm->_globals->_prevScreenId == 39) + handleForest(40, 133, 252, 100, 440, 4); + else if (_vm->_globals->_prevScreenId == 41) + handleForest(40, 392, 529, 100, 440, 2); + break; + case 41: + if (_vm->_globals->_prevScreenId == 40) + handleForest(41, 551, 633, 100, 440, 1); + else if (_vm->_globals->_prevScreenId == 17) + handleForest(41, 6, 84, 100, 440, 3); + break; + case 57: + _vm->_globals->_disableInventFl = true; + if (_vm->_globals->_saveData->_data[svField261] == 1 && getBobAnimDataIdx(5) == 37) { + stopBobAnimation(5); + setBobAnimDataIdx(5, 0); + setBobAnimation(6); + _vm->_globals->_saveData->_data[svField261] = 2; + _vm->_linesMan->disableZone(15); + _vm->_soundMan->playSoundFile("SOUND75.WAV"); + } + if (_vm->_globals->_saveData->_data[svField261] == 2 && getBobAnimDataIdx(6) == 6) { + stopBobAnimation(6); + setBobAnimDataIdx(6, 0); + setBobAnimation(7); + _vm->_linesMan->enableZone(14); + _vm->_globals->_saveData->_data[svField261] = 3; + } + _vm->_globals->_disableInventFl = false; + break; + case 93: + if (_vm->_globals->_saveData->_data[svField333]) + break; + + _vm->_globals->_disableInventFl = true; + do + _vm->_events->refreshScreenAndEvents(); + while (getBobAnimDataIdx(8) != 3); + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("GM3.PE2"); + stopBobAnimation(8); + _vm->_globals->_saveData->_data[svField333] = 1; + _vm->_globals->_disableInventFl = false; + break; + } +} + +void ObjectsManager::quickDisplayBobSprite(int idx) { + int startPos = 10 * idx; + if (!READ_LE_UINT16(_vm->_talkMan->_characterAnim + startPos + 4)) + return; + + int xp = READ_LE_INT16(_vm->_talkMan->_characterAnim + startPos); + int yp = READ_LE_INT16(_vm->_talkMan->_characterAnim + startPos + 2); + int spriteIndex = _vm->_talkMan->_characterAnim[startPos + 8]; + + _vm->_graphicsMan->fastDisplay(_vm->_talkMan->_characterSprite, xp, yp, spriteIndex); +} + +void ObjectsManager::initVbob(const byte *src, int idx, int xp, int yp, int frameIndex) { + if (idx > 29) + error("MAX_VBOB exceeded"); + + VBobItem *vbob = &_vBob[idx]; + if (vbob->_displayMode <= 1) { + vbob->_displayMode = 1; + vbob->_xp = xp; + vbob->_yp = yp; + vbob->_frameIndex = frameIndex; + vbob->_oldX = xp; + vbob->_oldY = yp; + vbob->_oldFrameIndex = frameIndex; + vbob->_spriteData = src; + vbob->_oldSpriteData = src; + vbob->_surface = _vm->_globals->freeMemory(vbob->_surface); + } else if (vbob->_displayMode == 2 || vbob->_displayMode == 4) { + vbob->_displayMode = 3; + vbob->_oldX = vbob->_xp; + vbob->_oldY = vbob->_yp; + vbob->_oldSpriteData = vbob->_spriteData; + vbob->_oldFrameIndex = vbob->_frameIndex; + vbob->_xp = xp; + vbob->_yp = yp; + vbob->_frameIndex = frameIndex; + vbob->_spriteData = src; + } +} + +void ObjectsManager::disableVbob(int idx) { + if (idx > 29) + error("MAX_VBOB exceeded"); + + VBobItem *vbob = &_vBob[idx]; + if (vbob->_displayMode <= 1) + vbob->_displayMode = 0; + else + vbob->_displayMode = 4; +} + +void ObjectsManager::doActionBack(int idx) { + if (_curGestureFile != 1) { + _gestureBuf = _vm->_globals->freeMemory(_gestureBuf); + _curGestureFile = 1; + _gestureBuf = _vm->_fileIO->loadFile("DOS.SPR"); + } + + switch (idx) { + case 1: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,8,8,8,8,8,8,7,6,5,4,3,2,1,0,-1,", 8, false); + break; + case 2: + showSpecialActionAnimationWithFlip(_gestureBuf, "0,1,2,3,4,5,6,7,8,9,10,11,12,13,-1,", 8, false); + break; + case 3: + showSpecialActionAnimation(_gestureBuf, "12,11,10,9,8,7,6,5,4,3,2,1,0,-1,", 8); + break; + case 4: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,8,8,8,8,8,8,9,10,11,12,13,12,11,12,13,12,11,12,13,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,", 8, false); + break; + case 5: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,19,20,21,-1,", 8, false); + break; + case 6: + showSpecialActionAnimation(_gestureBuf, "20,19,18,17,16,15,-1,", 8); + break; + case 7: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,19,20,21,22,23,24,-1,", 8, false); + break; + case 8: + showSpecialActionAnimation(_gestureBuf, "23,22,21,20,19,18,17,16,15,-1,", 8); + break; + case 9: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,19,20,21,22,23,24,-1,", 8, false); + break; + case 10: + showSpecialActionAnimation(_gestureBuf, "23,22,21,20,19,18,17,16,15,-1,", 8); + break; + } +} + +void ObjectsManager::doActionRight(int idx) { + if (_curGestureFile != 3) { + _gestureBuf = _vm->_globals->freeMemory(_gestureBuf); + _curGestureFile = 3; + _gestureBuf = _vm->_fileIO->loadFile("PROFIL.SPR"); + } + + switch (idx) { + case 1: + showActionAnimation(_gestureBuf, "20,19,18,17,16,15,14,13,13,13,13,13,14,15,16,17,18,19,20,-1,", 8, false); + break; + case 2: + showSpecialActionAnimationWithFlip(_gestureBuf, "1,2,3,4,5,6,7,8,-1,", 8, false); + break; + case 3: + showSpecialActionAnimation(_gestureBuf, "9,10,11,12,13,14,15,16,17,18,19,20,-1,", 8); + break; + case 4: + showActionAnimation(_gestureBuf, "1,2,3,4,5,6,7,8,8,7,6,5,4,3,2,1,-1,", 8, false); + break; + case 5: + showSpecialActionAnimationWithFlip(_gestureBuf, "23,24,25,-1,", 8, false); + break; + case 6: + showSpecialActionAnimation(_gestureBuf, "24,23,-1,", 8); + break; + case 7: + showSpecialActionAnimationWithFlip(_gestureBuf, "23,24,25,26,27,-1,", 8, false); + break; + case 8: + showSpecialActionAnimation(_gestureBuf, "26,25,24,23,-1,", 8); + break; + case 9: + showSpecialActionAnimationWithFlip(_gestureBuf, "23,24,25,26,27,28,29,-1,", 8, false); + break; + case 10: + showSpecialActionAnimation(_gestureBuf, "28,27,26,25,24,23,-1,", 8); + break; + } +} + +void ObjectsManager::doActionDiagRight(int idx) { + if (_curGestureFile != 4) { + _gestureBuf = _vm->_globals->freeMemory(_gestureBuf); + _curGestureFile = 4; + _gestureBuf = _vm->_fileIO->loadFile("3Q.SPR"); + } + + switch (idx) { + case 1: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,8,8,8,8,8,7,6,5,4,3,2,1,0,-1,", 8, false); + break; + case 2: + showSpecialActionAnimationWithFlip(_gestureBuf, "0,1,2,3,4,5,6,7,8,9,10,11,12,-1,", 8, false); + break; + case 3: + showSpecialActionAnimation(_gestureBuf, "11,10,9,8,7,6,5,4,3,2,1,0,-1,", 8); + break; + case 4: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,8,9,10,11,12,11,12,11,12,11,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,", 8, false); + break; + case 5: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,-1,", 8, false); + break; + case 6: + showSpecialActionAnimation(_gestureBuf, "17,16,15,-1,", 8); + break; + case 7: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,19,20,-1,", 8, false); + break; + case 8: + showSpecialActionAnimation(_gestureBuf, "19,18,17,16,15,-1,", 8); + break; + case 9: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,19,20,21,-1,", 8, false); + break; + case 10: + showSpecialActionAnimation(_gestureBuf, "20,19,18,17,15,-1,", 8); + break; + } +} + +void ObjectsManager::doActionFront(int idx) { + if (_curGestureFile != 2) { + _gestureBuf = _vm->_globals->freeMemory(_gestureBuf); + _curGestureFile = 2; + _gestureBuf = _vm->_fileIO->loadFile("FACE.SPR"); + } + + switch (idx) { + case 1: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,9,9,9,9,9,9,7,6,5,4,3,2,1,0,-1,", 8, false); + break; + case 2: + showSpecialActionAnimationWithFlip(_gestureBuf, "0,1,2,3,4,5,6,7,9,10,11,12,13,14,15,-1,", 8, false); + break; + case 3: + showSpecialActionAnimation(_gestureBuf, "14,13,12,11,10,9,7,6,5,4,3,2,1,0,-1,", 8); + break; + case 4: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,9,10,11,12,13,14,13,12,11,10,9,7,6,5,4,3,2,1,0,-1,", 8, false); + break; + } +} + +void ObjectsManager::doActionDiagLeft(int idx) { + if (_curGestureFile != 4) { + _gestureBuf = _vm->_globals->freeMemory(_gestureBuf); + _curGestureFile = 4; + _gestureBuf = _vm->_fileIO->loadFile("3Q.SPR"); + } + + switch (idx) { + case 1: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,8,8,8,8,8,7,6,5,4,3,2,1,0,-1,", 8, true); + break; + case 2: + showSpecialActionAnimationWithFlip(_gestureBuf, "0,1,2,3,4,5,6,7,8,9,10,11,12,-1,", 8, true); + break; + case 3: + showSpecialActionAnimation(_gestureBuf, "11,10,9,8,7,6,5,4,3,2,1,0,-1,", 8); + break; + case 4: + showActionAnimation(_gestureBuf, "0,1,2,3,4,5,6,7,8,9,10,11,12,11,12,11,12,11,12,11,10,9,8,7,6,5,4,3,2,1,0,-1,", 8, true); + break; + case 5: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,-1,", 8, true); + break; + case 6: + showSpecialActionAnimation(_gestureBuf, "17,16,15,-1,", 8); + break; + case 7: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,19,20,-1,", 8, true); + break; + case 8: + showSpecialActionAnimation(_gestureBuf, "19,18,17,16,15,-1,", 8); + break; + case 9: + showSpecialActionAnimationWithFlip(_gestureBuf, "15,16,17,18,19,20,21,-1,", 8, true); + break; + case 10: + showSpecialActionAnimation(_gestureBuf, "20,19,18,17,15,-1,", 8); + break; + } +} + +void ObjectsManager::doActionLeft(int idx) { + if (_curGestureFile != 3) { + _gestureBuf = _vm->_globals->freeMemory(_gestureBuf); + _curGestureFile = 3; + _gestureBuf = _vm->_fileIO->loadFile("PROFIL.SPR"); + } + + switch (idx) { + case 1: + showActionAnimation(_gestureBuf, "20,19,18,17,16,15,14,13,13,13,13,13,14,15,16,17,18,19,20,-1,", 8, true); + break; + case 2: + showSpecialActionAnimationWithFlip(_gestureBuf, "1,2,3,4,5,6,7,8,-1,", 8, true); + break; + case 3: + showSpecialActionAnimation(_gestureBuf, "9,10,11,12,13,14,15,16,17,18,19,20,-1,", 8); + break; + case 4: + showActionAnimation(_gestureBuf, "1,2,3,4,5,6,7,8,8,7,6,5,4,3,2,1,-1,", 8, true); + break; + case 5: + showSpecialActionAnimationWithFlip(_gestureBuf, "23,24,25,-1,", 8, true); + break; + case 6: + showSpecialActionAnimation(_gestureBuf, "24,23,-1,", 8); + break; + case 7: + showSpecialActionAnimationWithFlip(_gestureBuf, "23,24,25,26,27,-1,", 8, true); + break; + case 8: + showSpecialActionAnimation(_gestureBuf, "26,25,24,23,-1,", 8); + break; + case 9: + showSpecialActionAnimationWithFlip(_gestureBuf, "23,24,25,26,27,28,29,-1,", 8, true); + break; + case 10: + showSpecialActionAnimation(_gestureBuf, "28,27,26,25,24,23,-1,", 8); + break; + } +} + +void ObjectsManager::setAndPlayAnim(int idx, int animIdx, int destPosi, bool animAction) { + // Set Hopkins animation and position + setBobAnimation(idx); + setBobAnimDataIdx(idx, animIdx); + + // Make Hopkins walk to the expected place + do { + _vm->_events->refreshScreenAndEvents(); + } while (destPosi != getBobAnimDataIdx(idx)); + + if (!animAction) + stopBobAnimation(idx); + else { + BobItem *bob = &_bob[idx]; + _vm->_graphicsMan->fastDisplay(bob->_spriteData, bob->_oldX, bob->_oldY, bob->_frameIndex); + stopBobAnimation(idx); + _vm->_events->refreshScreenAndEvents(); + } +} + +int ObjectsManager::getBobAnimDataIdx(int idx) { + return _bob[idx]._animDataIdx / 5; +} + +void ObjectsManager::setBobAnimDataIdx(int idx, int animIdx) { + BobItem *bob = &_bob[idx]; + bob->_animDataIdx = 5 * animIdx; + bob->_moveChange1 = 0; + bob->_moveChange2 = 0; +} + +/** + * Set Hopkins animation + */ +void ObjectsManager::setBobAnimation(int idx) { + assert(idx < 36); + BobItem *bob = &_bob[idx]; + if (!bob->_disabledAnimationFl) + return; + + bob->_disabledAnimationFl = false; + bob->_animDataIdx = 5; + bob->_frameIndex = 250; + bob->_moveChange1 = 0; + bob->_moveChange2 = 0; +} + +/** + * Stop Hopkins animation + */ +void ObjectsManager::stopBobAnimation(int idx) { + assert(idx < 36); + _bob[idx]._disabledAnimationFl = true; +} + +/** + * Get X position + */ +int ObjectsManager::getBobPosX(int idx) { + return _bob[idx]._xp; +} + +void ObjectsManager::loadLinkFile(const Common::String &file, bool skipDetails) { + Common::File f; + Common::String filename = file + ".LNK"; + bool fileFoundFl = false; + byte *ptr = _vm->_fileIO->searchCat(filename, RES_LIN, fileFoundFl); + size_t nbytes = _vm->_fileIO->_catalogSize; + if (!fileFoundFl) { + if (!f.open(filename)) + error("Error opening file - %s", filename.c_str()); + + nbytes = f.size(); + ptr = _vm->_globals->allocMemory(nbytes); + if (ptr == NULL) + error("INILINK"); + _vm->_fileIO->readStream(f, ptr, nbytes); + f.close(); + } + if (!skipDetails) { + for (int idx = 0; idx < 500; ++idx) + _vm->_globals->_spriteSize[idx] = READ_LE_INT16((uint16 *)ptr + idx); + + resetHidingItems(); + + Common::String filename2 = Common::String((const char *)ptr + 1000); + if (!filename2.empty()) { + fileFoundFl = false; + _hidingItemData[1] = _vm->_fileIO->searchCat(filename2, RES_SLI, fileFoundFl); + + if (!fileFoundFl) { + _hidingItemData[1] = _vm->_fileIO->loadFile(filename2); + } else { + _hidingItemData[1] = _vm->_fileIO->loadFile("RES_SLI.RES"); + } + + int curDataCacheId = 60; + byte *curDataPtr = ptr + 1000; + for (int hidingIdx = 0; hidingIdx <= 21; hidingIdx++) { + HidingItem *hid = &_hidingItem[hidingIdx]; + int curSpriteId = READ_LE_INT16(curDataPtr + 2 * curDataCacheId); + hid->_spriteIndex = curSpriteId; + hid->_x = READ_LE_INT16(curDataPtr + 2 * curDataCacheId + 2); + hid->_y = READ_LE_INT16(curDataPtr + 2 * curDataCacheId + 4); + hid->_yOffset = READ_LE_INT16(curDataPtr + 2 * curDataCacheId + 8); + + if (!_hidingItemData[1]) { + hid->_useCount = 0; + } else { + hid->_spriteData = _hidingItemData[1]; + hid->_width = getWidth(_hidingItemData[1], curSpriteId); + hid->_height = getHeight(_hidingItemData[1], curSpriteId); + hid->_useCount = 1; + } + if (!hid->_x && !hid->_y && !hid->_spriteIndex) + hid->_useCount = 0; + + curDataCacheId += 5; + } + enableHidingBehavior(); + } + } + + _vm->_linesMan->resetLines(); + for (size_t idx = 0; idx < nbytes - 3; idx++) { + if (READ_BE_UINT24(&ptr[idx]) == MKTAG24('O', 'B', '2')) { + byte *curDataPtr = &ptr[idx + 4]; + int lineDataIdx = 0; + int curLineIdx = 0; + _vm->_linesMan->resetLinesNumb(); + Directions curDirection; + do { + curDirection = (Directions)READ_LE_INT16(curDataPtr + 2 * lineDataIdx); + if (curDirection != DIR_NONE) { + _vm->_linesMan->addLine( + curLineIdx, + curDirection, + READ_LE_INT16(curDataPtr + 2 * lineDataIdx + 2), + READ_LE_INT16(curDataPtr + 2 * lineDataIdx + 4), + READ_LE_INT16(curDataPtr + 2 * lineDataIdx + 6), + READ_LE_INT16(curDataPtr + 2 * lineDataIdx + 8)); + } + lineDataIdx += 5; + ++curLineIdx; + } while (curDirection != DIR_NONE); + _vm->_linesMan->initRoute(); + } + } + + if (!skipDetails) { + for (size_t idx = 0; idx < nbytes - 3; idx++) { + if (READ_BE_UINT24(&ptr[idx]) == MKTAG24('Z', 'O', '2')) { + byte *curDataPtr = &ptr[idx + 4]; + int curDataIdx = 0; + for (int i = 1; i <= 100; i++) { + ZoneItem *curZone = &_vm->_linesMan->_zone[i]; + curZone->_destX = 0; + curZone->_destY = 0; + curZone->_spriteIndex = 0; + curZone->_verbFl1 = 0; + curZone->_verbFl2 = 0; + curZone->_verbFl3 = 0; + curZone->_verbFl4 = 0; + curZone->_verbFl5 = 0; + curZone->_verbFl6 = 0; + curZone->_verbFl7 = 0; + curZone->_verbFl8 = 0; + curZone->_verbFl9 = 0; + curZone->_verbFl10 = 0; + curZone->_messageId = 0; + } + + int curLineIdx = 0; + for (;;) { + int bobZoneId = READ_LE_INT16(curDataPtr + 2 * curDataIdx); + if (bobZoneId != -1) { + _vm->_linesMan->addZoneLine( + curLineIdx, + READ_LE_INT16(curDataPtr + 2 * curDataIdx + 2), + READ_LE_INT16(curDataPtr + 2 * curDataIdx + 4), + READ_LE_INT16(curDataPtr + 2 * curDataIdx + 6), + READ_LE_INT16(curDataPtr + 2 * curDataIdx + 8), + bobZoneId); + _vm->_linesMan->_zone[bobZoneId]._enabledFl = true; + } + curDataIdx += 5; + ++curLineIdx; + if (bobZoneId == -1) + break; + } + for (int i = 1; i <= 100; i++) { + ZoneItem *curZone = &_vm->_linesMan->_zone[i]; + curZone->_destX = READ_LE_INT16(curDataPtr + 2 * curDataIdx); + curZone->_destY = READ_LE_INT16(curDataPtr + 2 * curDataIdx + 2); + curZone->_spriteIndex = READ_LE_INT16(curDataPtr + 2 * curDataIdx + 4); + curDataIdx += 3; + } + + byte *verbData = ptr + idx + (10 * curLineIdx + 606) + 4; + for (int i = 1; i <= 100; i++) { + int j = (i - 1) * 10; + ZoneItem *curZone = &_vm->_linesMan->_zone[i]; + curZone->_verbFl1 = verbData[j]; + curZone->_verbFl2 = verbData[j + 1]; + curZone->_verbFl3 = verbData[j + 2]; + curZone->_verbFl4 = verbData[j + 3]; + curZone->_verbFl5 = verbData[j + 4]; + curZone->_verbFl6 = verbData[j + 5]; + curZone->_verbFl7 = verbData[j + 6]; + curZone->_verbFl8 = verbData[j + 7]; + curZone->_verbFl9 = verbData[j + 8]; + curZone->_verbFl10 = verbData[j + 9]; + } + int dep = 1010; + for (int i = 1; i <= 100; i++) { + _vm->_linesMan->_zone[i]._messageId = READ_LE_INT16(verbData + dep); + dep += 2; + } + _vm->_linesMan->initSquareZones(); + } + } + } + _vm->_globals->freeMemory(ptr); +} + +void ObjectsManager::sceneSpecialIni() { + switch (_vm->_globals->_screenId) { + case 17: + if (_vm->_globals->_prevScreenId == 20) { + _vm->_globals->_disableInventFl = true; + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + for (int i = 0; i <= 4; i++) + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->fadeInLong(); + animateSprite(0); + for (int i = 0; i <= 4; i++) + _vm->_events->refreshScreenAndEvents(); + initVbob(_vm->_globals->_levelSpriteBuf, 5, 15, 28, 1); + _vm->_fontMan->hideText(9); + bool displayedTxtFl = false; + if (!_vm->_soundMan->_textOffFl) { + _vm->_fontMan->initTextBuffers(9, 383, _vm->_globals->_textFilename, 220, 72, 6, 36, 253); + _vm->_fontMan->showText(9); + displayedTxtFl = true; + } + if (!_vm->_soundMan->_voiceOffFl) + _vm->_soundMan->mixVoice(383, 4, displayedTxtFl); + _vm->_globals->_saveData->_data[svField270] = 1; + _vm->_globals->_saveData->_data[svField300] = 1; + _vm->_globals->_saveData->_data[svField320] = 1; + if (_vm->_soundMan->_voiceOffFl) { + for (int i = 0; i <= 199; i++) + _vm->_events->refreshScreenAndEvents(); + } + _vm->_fontMan->hideText(9); + disableVbob(5); + for (int i = 0; i <= 3; i++) + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->_noFadingFl = true; + _vm->_globals->_disableInventFl = false; + } + break; + + case 18: + if (_vm->_globals->_prevScreenId == 17) { + _vm->_events->_mouseSpriteId = 4; + for (int i = 0; i <= 4; i++) + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->fadeInLong(); + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + _vm->_globals->_disableInventFl = false; + _vm->_graphicsMan->_noFadingFl = true; + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("MAGE1.pe2"); + _vm->_graphicsMan->_noFadingFl = true; + _vm->_globals->_disableInventFl = false; + } + break; + + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + _vm->_linesMan->_bobZone[20] = 1; + _vm->_linesMan->_bobZone[21] = 2; + _vm->_linesMan->_bobZone[22] = 3; + _vm->_linesMan->_bobZone[23] = 4; + _vm->_linesMan->_bobZoneFl[20] = true; + _vm->_linesMan->_bobZoneFl[21] = true; + _vm->_linesMan->_bobZoneFl[22] = true; + _vm->_linesMan->_bobZoneFl[23] = true; + enableVerb(20, 5); + enableVerb(21, 5); + enableVerb(22, 5); + enableVerb(23, 5); + _vm->_linesMan->_zone[20]._messageId = 30; + _vm->_linesMan->_zone[21]._messageId = 30; + _vm->_linesMan->_zone[22]._messageId = 30; + _vm->_linesMan->_zone[23]._messageId = 30; + for (int i = svField200; i <= svField214; i++) { + if (_vm->_globals->_saveData->_data[i] != 2) + _vm->_globals->_saveData->_data[i] = 0; + } + break; + + case 73: + if (!_vm->_globals->_saveData->_data[svSecondElevatorAvailableFl]) { + resetHidingUseCount(0); + resetHidingUseCount(1); + } + break; + + case 93: + if (!_vm->_globals->_saveData->_data[svField333]) + setBobAnimation(8); + break; + } +} + +void ObjectsManager::setMultiBobAnim(int idx1, int idx2, int anim1Idx, int anim2Idx) { + if (idx1 != -1) + setBobAnimation(idx1); + if (idx2 != -1) + setBobAnimation(idx2); + if (idx1 != -1) + setBobAnimDataIdx(idx1, anim1Idx); + if (idx2 != -1) + setBobAnimDataIdx(idx2, anim2Idx); +} + +void ObjectsManager::checkEventBobAnim(int idx, int animIdx, int animDataIdx, int a4) { + _vm->_events->_curMouseButton = 0; + _vm->_events->_mouseButton = 0; + + if (a4 != 3) { + setBobAnimation(idx); + setBobAnimDataIdx(idx, animIdx); + } + + do { + _vm->_events->refreshScreenAndEvents(); + if (_vm->_events->_curMouseButton) + break; + } while (animDataIdx != getBobAnimDataIdx(idx)); + if (!a4) + stopBobAnimation(idx); +} + +void ObjectsManager::disableVerb(int idx, int a2) { + ZoneItem *curZone = &_vm->_linesMan->_zone[idx]; + switch (a2) { + case 6: + case 16: + curZone->_verbFl1 = 0; + break; + case 7: + curZone->_verbFl2 = 0; + break; + case 5: + case 8: + curZone->_verbFl3 = 0; + break; + case 9: + case 17: + case 24: + curZone->_verbFl4 = 0; + break; + case 10: + case 18: + curZone->_verbFl5 = 0; + break; + case 11: + case 19: + curZone->_verbFl6 = 0; + break; + case 12: + case 20: + curZone->_verbFl7 = 0; + break; + case 13: + case 22: + curZone->_verbFl8 = 0; + break; + case 14: + case 21: + case 25: + curZone->_verbFl9 = 0; + break; + case 15: + curZone->_verbFl10 = 0; + break; + } + _changeVerbFl = true; +} + +void ObjectsManager::enableVerb(int idx, int a2) { + ZoneItem *curZone = &_vm->_linesMan->_zone[idx]; + + switch (a2) { + case 5: + curZone->_verbFl3 = 2; + break; + case 6: + curZone->_verbFl1 = 1; + break; + case 7: + curZone->_verbFl2 = 1; + break; + case 8: + curZone->_verbFl3 = 1; + break; + case 9: + curZone->_verbFl4 = 1; + break; + case 10: + curZone->_verbFl5 = 1; + break; + case 11: + curZone->_verbFl6 = 1; + break; + case 12: + curZone->_verbFl7 = 1; + break; + case 13: + curZone->_verbFl8 = 1; + break; + case 14: + curZone->_verbFl8 = 1; + break; + case 15: + curZone->_verbFl9 = 1; + break; + case 16: + curZone->_verbFl1 = 2; + break; + case 17: + curZone->_verbFl4 = 2; + break; + case 18: + curZone->_verbFl5 = 2; + break; + case 19: + curZone->_verbFl6 = 2; + break; + case 20: + curZone->_verbFl7 = 2; + break; + case 21: + curZone->_verbFl9 = 2; + break; + case 22: + curZone->_verbFl8 = 2; + break; + case 24: + curZone->_verbFl4 = 3; + break; + case 25: + curZone->_verbFl9 = 2; + break; + } +} + +void ObjectsManager::showActionAnimation(const byte *spriteData, const Common::String &actionStr, int speed, bool flipFl) { + Common::String tmpStr = ""; + int realSpeed = speed; + if (_vm->_globals->_speed == 2) + realSpeed = speed / 2; + else if (_vm->_globals->_speed == 3) + realSpeed = speed / 3; + const byte *oldSpriteData = _sprite[0]._spriteData; + int spriteIndex = _sprite[0]._spriteIndex; + bool oldFlipFl = _sprite[0]._flipFl; + _sprite[0]._flipFl = flipFl; + + int idx = 0; + for (int strPos = 0; ; strPos++) { + bool tokenCompleteFl = false; + char curChar = actionStr[strPos]; + if (curChar == ',') { + idx = atoi(tmpStr.c_str()); + tmpStr = ""; + tokenCompleteFl = true; + } else { + tmpStr += curChar; + } + + if (tokenCompleteFl) { + if (idx == -1) { + _sprite[0]._spriteData = oldSpriteData; + _sprite[0]._spriteIndex = spriteIndex; + _sprite[0]._flipFl = oldFlipFl; + } else { + _sprite[0]._spriteData = spriteData; + _sprite[0]._spriteIndex = idx; + } + for (int i = 0; i < realSpeed; i++) + _vm->_events->refreshScreenAndEvents(); + if (idx == -1) + break; + } + } +} + +void ObjectsManager::showSpecialActionAnimationWithFlip(const byte *spriteData, const Common::String &animationSeq, int speed, bool flipFl) { + Common::String tmpStr = ""; + + int realSpeed = speed; + if (_vm->_globals->_speed == 2) + realSpeed = speed / 2; + else if (_vm->_globals->_speed == 3) + realSpeed = speed / 3; + + _oldSpriteData = _sprite[0]._spriteData; + _oldSpriteIndex = _sprite[0]._spriteIndex; + _oldFlipFl = _sprite[0]._flipFl; + _sprite[0]._flipFl = flipFl; + + uint strPos = 0; + int spriteIndex = 0; + do { + bool completeTokenFl = false; + do { + char nextChar = animationSeq[strPos]; + if ((animationSeq[strPos] == ',') || (strPos == animationSeq.size() - 1)) { + // Safeguard: if the sequence doesn't end with a coma, simulate it's present. + if (animationSeq[strPos] != ',') + tmpStr += nextChar; + spriteIndex = atoi(tmpStr.c_str()); + tmpStr = ""; + completeTokenFl = true; + } else { + tmpStr += nextChar; + } + ++strPos; + } while (!completeTokenFl); + + if (spriteIndex != -1) { + _sprite[0]._spriteData = spriteData; + _sprite[0]._spriteIndex = spriteIndex; + } + for (int i = 0; i < realSpeed; i++) + _vm->_events->refreshScreenAndEvents(); + } while (spriteIndex != -1); +} + +void ObjectsManager::showSpecialActionAnimation(const byte *spriteData, const Common::String &animString, int speed) { + Common::String tmpStr = ""; + int realSpeed = speed; + if (_vm->_globals->_speed == 2) + realSpeed = speed / 2; + else if (_vm->_globals->_speed == 3) + realSpeed = speed / 3; + + int spriteIndex = 0; + bool completeTokenFl; + char nextChar; + + for (int idx = 0; ; idx++) { + completeTokenFl = false; + nextChar = animString[idx]; + if (nextChar == ',') { + spriteIndex = atoi(tmpStr.c_str()); + tmpStr = ""; + completeTokenFl = true; + } else { + tmpStr += nextChar; + } + + if (completeTokenFl) { + if (spriteIndex == -1) { + _sprite[0]._spriteData = _oldSpriteData; + _sprite[0]._spriteIndex = _oldSpriteIndex; + _sprite[0]._flipFl = _oldFlipFl; + } else { + _sprite[0]._spriteData = spriteData; + _sprite[0]._spriteIndex = spriteIndex; + } + + for (int i = 0; i < realSpeed; i++) + _vm->_events->refreshScreenAndEvents(); + + if (spriteIndex == -1) + break; + } + } +} + +void ObjectsManager::handleForest(int screenId, int minX, int maxX, int minY, int maxY, int idx) { + int savegameIdx = screenId; + if (_vm->_globals->_screenId != screenId) + return; + + switch (_vm->_globals->_screenId) { + case 35: + if (idx > 2) + savegameIdx = 201; + else + savegameIdx = 200; + break; + case 36: + if (idx > 2) + savegameIdx = 203; + else + savegameIdx = 202; + break; + case 37: + if (idx > 2) + savegameIdx = 205; + else + savegameIdx = 204; + break; + case 38: + if (idx > 2) + savegameIdx = 207; + else + savegameIdx = 206; + break; + case 39: + if (idx > 2) + savegameIdx = 209; + else + savegameIdx = 208; + break; + case 40: + if (idx > 2) + savegameIdx = 211; + else + savegameIdx = 210; + break; + case 41: + if (idx > 2) + savegameIdx = 213; + else + savegameIdx = 212; + break; + } + + if (_vm->_globals->_saveData->_data[savegameIdx] == 2) + return; + + if (_vm->_globals->_saveData->_data[savegameIdx]) { + if (_vm->_globals->_saveData->_data[savegameIdx] == 1) { + if (((idx == 1 || idx == 2) && getBobAnimDataIdx(idx) == 26) || ((idx == 3 || idx == 4) && getBobAnimDataIdx(idx) == 27)) { + _vm->_dialog->disableInvent(); + _vm->_soundMan->playSample(1); + _vm->_globals->_saveData->_data[savegameIdx] = 4; + } + } + if (_vm->_globals->_saveData->_data[savegameIdx] == 4) { + if (idx >= 1 && idx <= 4 && getBobAnimDataIdx(idx) > 30) + _vm->_globals->_saveData->_data[savegameIdx] = 3; + } + if (_vm->_globals->_saveData->_data[savegameIdx] == 3) { + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playAnim("CREVE2.ANM", "CREVE2.ANM", 100, 24, 500); + _vm->_globals->_exitId = 150; + _vm->_graphicsMan->_noFadingFl = true; + hideBob(1); + hideBob(2); + hideBob(3); + hideBob(4); + } + } else if (minX < getSpriteX(0) + && maxX > getSpriteX(0) + && minY < getSpriteY(0) + && maxY > getSpriteY(0)) { + if (idx >= 1 && idx <= 4) + setBobAnimation(idx); + _vm->_globals->_saveData->_data[savegameIdx] = 1; + } +} + +void ObjectsManager::lockAnimX(int idx, int x) { + _lockedAnims[idx]._enableFl = true; + _lockedAnims[idx]._posX = x; +} + +/** + * Game scene control method + */ +void ObjectsManager::sceneControl(const Common::String &backgroundFile, const Common::String &linkFile, + const Common::String &animFile, const Common::String &s4, int soundNum, bool initializeScreen) { + _vm->_dialog->_inventFl = false; + _vm->_events->_gameKey = KEY_NONE; + _vm->_dialog->enableInvent(); + _vm->_graphicsMan->_scrollOffset = 0; + _vm->_globals->_cityMapEnabledFl = false; + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + _vm->_soundMan->playSound(soundNum); + _vm->_linesMan->_route = NULL; + _vm->_globals->_freezeCharacterFl = true; + _vm->_globals->_exitId = 0; + if (!backgroundFile.empty()) + _vm->_graphicsMan->loadImage(backgroundFile); + if (!linkFile.empty()) + loadLinkFile(linkFile); + if (!animFile.empty()) + _vm->_animMan->loadAnim(animFile); + _vm->_graphicsMan->displayAllBob(); + if (!s4.empty()) { + if (initializeScreen) + _vm->_graphicsMan->initScreen(s4, 0, initializeScreen); + else + _vm->_graphicsMan->initScreen(s4, 2, initializeScreen); + } + _vm->_events->mouseOn(); + if (_vm->_globals->_screenId == 61) { + addStaticSprite(_vm->_globals->_characterSpriteBuf, Common::Point(330, 418), 0, 60, 0, false, 34, 190); + animateSprite(0); + _vm->_linesMan->_route = NULL; + computeAndSetSpriteSize(); + } + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + _vm->_events->changeMouseCursor(4); + for (int i = 0; i <= 4; i++) + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->fadeInLong(); + if (_vm->_globals->_screenId == 61) { + _vm->_animMan->playSequence("OUVRE.SEQ", 10, 4, 10, false, false); + stopBobAnimation(3); + _vm->_globals->_checkDistanceFl = true; + _oldCharacterPosX = getSpriteX(0); + _oldDirection = DIR_NONE; + _homeRateCounter = 0; + _vm->_linesMan->_route = NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(getSpriteX(0), getSpriteY(0), 330, 345); + _vm->_globals->_checkDistanceFl = true; + do { + goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route); + setSpriteIndex(0, 64); + } + do { + int mouseButton = _vm->_events->getMouseButton(); + if (mouseButton == 1) { + handleLeftButton(); + mouseButton = 1; + } else if (mouseButton == 2) + handleRightButton(); + _vm->_dialog->testDialogOpening(); + _vm->_linesMan->checkZone(); + if (_vm->_globals->_actionMoveTo) + paradise(); + if (!_vm->_globals->_exitId) + _vm->_events->refreshScreenAndEvents(); + + if (_vm->_globals->_exitId) + break; + } while (!_vm->shouldQuit()); + if (_vm->shouldQuit()) + return; + + _vm->_graphicsMan->fadeOutLong(); + if (!animFile.empty()) + _vm->_graphicsMan->endDisplayBob(); + if (_vm->_globals->_screenId == 61) + removeSprite(0); + clearScreen(); + _vm->_globals->_eventMode = EVENTMODE_DEFAULT; +} + +/** + * Game scene control method + */ +void ObjectsManager::sceneControl2(const Common::String &backgroundFile, const Common::String &linkFile, + const Common::String &animFile, const Common::String &s4, int soundNum, bool initializeScreen) { + _vm->_dialog->_inventFl = false; + _vm->_events->_gameKey = KEY_NONE; + _verb = 4; + _vm->_graphicsMan->_scrollOffset = 0; + _vm->_dialog->enableInvent(); + _vm->_globals->_cityMapEnabledFl = false; + _vm->_graphicsMan->_noFadingFl = false; + _vm->_globals->_freezeCharacterFl = false; + _vm->_globals->_exitId = 0; + _vm->_globals->_checkDistanceFl = true; + _vm->_soundMan->playSound(soundNum); + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + if (!backgroundFile.empty()) + _vm->_graphicsMan->loadImage(backgroundFile); + if (!linkFile.empty()) + loadLinkFile(linkFile); + if (!animFile.empty()) { + _vm->_animMan->loadAnim(animFile); + _vm->_graphicsMan->displayAllBob(); + } + if (!s4.empty()) { + if (initializeScreen) + _vm->_graphicsMan->initScreen(s4, 0, initializeScreen); + else + _vm->_graphicsMan->initScreen(s4, 2, initializeScreen); + } + _vm->_events->mouseOn(); + _vm->_events->_mouseCursorId = 4; + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + if (_vm->_globals->_characterType != CHARACTER_HOPKINS && !_vm->_globals->_saveData->_data[svAlternateSpriteFl] && !_vm->_globals->_saveData->_data[svField356]) { + _vm->_globals->_characterSpriteBuf = _vm->_fileIO->loadFile("PERSO.SPR"); + _vm->_globals->_characterType = CHARACTER_HOPKINS; + } + + if (_vm->_globals->_characterType == CHARACTER_HOPKINS && _vm->_globals->_saveData->_data[svAlternateSpriteFl] == 1) { + _vm->_globals->_characterSpriteBuf = _vm->_fileIO->loadFile("HOPFEM.SPR"); + _vm->_globals->_characterType = CHARACTER_HOPKINS_CLONE; + } + + if (_vm->_globals->_characterType != CHARACTER_SAMANTHA && _vm->_globals->_saveData->_data[svField356] == 1) { + _vm->_globals->_characterSpriteBuf = _vm->_fileIO->loadFile("PSAMAN.SPR"); + _vm->_globals->_characterType = CHARACTER_SAMANTHA; + } + _vm->_globals->loadCharacterData(); + switch (_vm->_globals->_characterType) { + case CHARACTER_HOPKINS: + addStaticSprite(_vm->_globals->_characterSpriteBuf, _characterPos, 0, _startSpriteIndex, 0, false, 34, 190); + break; + case CHARACTER_HOPKINS_CLONE: + addStaticSprite(_vm->_globals->_characterSpriteBuf, _characterPos, 0, _startSpriteIndex, 0, false, 28, 155); + break; + case CHARACTER_SAMANTHA: + addStaticSprite(_vm->_globals->_characterSpriteBuf, _characterPos, 0, _startSpriteIndex, 0, false, 20, 127); + break; + } + _vm->_events->setMouseXY(_characterPos); + if (_vm->_graphicsMan->_largeScreenFl) + _vm->_graphicsMan->_scrollPosX = (int16)getSpriteX(0) - 320; + computeAndSetSpriteSize(); + animateSprite(0); + enableHidingBehavior(); + _vm->_linesMan->_route = NULL; + computeAndSetSpriteSize(); + sceneSpecialIni(); + _vm->_events->_mouseSpriteId = 4; + _oldCharacterPosX = _characterPos.x; + _oldCharacterPosY = _characterPos.y; + _oldDirection = DIR_NONE; + _homeRateCounter = 0; + + for (int idx = 0; idx < 5; ++idx) + _vm->_events->refreshScreenAndEvents(); + + _vm->_globals->_eventMode = EVENTMODE_IGNORE; + if (!_vm->_graphicsMan->_noFadingFl) + _vm->_graphicsMan->fadeInLong(); + _vm->_graphicsMan->_noFadingFl = false; + _vm->_events->changeMouseCursor(4); + + int xCheck = 0; + int yCheck = 0; + + bool breakFlag = false; + while (!_vm->shouldQuit() && !breakFlag) { + int mouseButtons = _vm->_events->getMouseButton(); + if (mouseButtons) { + if (mouseButtons == 1) { + if (_verb == 16 && _vm->_events->_mouseCursorId == 16) { + int xp = _vm->_events->getMouseX(); + int yp = _vm->_events->getMouseY(); + + if ((xCheck == xp) && (yCheck == yp)) { + _vm->_linesMan->_route = NULL; + paradise(); + if (_vm->_globals->_exitId) + breakFlag = true; + } + xCheck = xp; + yCheck = yp; + } + handleLeftButton(); + } else if (mouseButtons == 2) { + handleRightButton(); + } + } + if (!_vm->_globals->_exitId) { + _vm->_dialog->testDialogOpening(); + _vm->_linesMan->checkZone(); + if (_vm->_linesMan->_route == NULL + || (goHome(), _vm->_linesMan->_route == NULL)) { + if (_vm->_globals->_actionMoveTo) + paradise(); + } + handleSpecialGames(); + _vm->_events->refreshScreenAndEvents(); + if (!_vm->_globals->_exitId) + continue; + } + breakFlag = true; + } + + if (_vm->_globals->_exitId != 8 || _vm->_globals->_screenId != 5 || !_helicopterFl) { + if (!_vm->_graphicsMan->_noFadingFl) + _vm->_graphicsMan->fadeOutLong(); + _vm->_graphicsMan->_noFadingFl = false; + removeSprite(0); + if (_twoCharactersFl) { + removeSprite(1); + _twoCharactersFl = false; + } + if (!animFile.empty()) + _vm->_graphicsMan->endDisplayBob(); + clearScreen(); + } else { + _helicopterFl = false; + } + _vm->_globals->_eventMode = EVENTMODE_DEFAULT; +} + +void ObjectsManager::setVerb(int id) { + _verb = id; +} + +void ObjectsManager::resetHidingUseCount(int idx) { + _hidingItem[idx]._useCount = 0; +} + +void ObjectsManager::setHidingUseCount(int idx) { + _hidingItem[idx]._useCount = 1; +} + +// Load Hiding Items +void ObjectsManager::loadHidingItems(const Common::String &file) { + resetHidingItems(); + byte *ptr = _vm->_fileIO->loadFile(file); + Common::String filename = Common::String((const char *)ptr); + + Common::File f; + if (!f.exists(filename)) + return; + + byte *spriteData = _vm->_fileIO->loadFile(filename); + _hidingItemData[1] = spriteData; + int curBufIdx = 60; + for (int i = 0; i <= 21; i++) { + HidingItem *hid = &_hidingItem[i]; + hid->_spriteIndex = READ_LE_INT16((uint16 *)ptr + curBufIdx); + hid->_x = READ_LE_INT16((uint16 *)ptr + curBufIdx + 1); + hid->_y = READ_LE_INT16((uint16 *)ptr + curBufIdx + 2); + hid->_yOffset = READ_LE_INT16((uint16 *)ptr + curBufIdx + 4); + if (spriteData == NULL) { + hid->_useCount = 0; + } else { + hid->_spriteData = spriteData; + hid->_width = getWidth(spriteData, hid->_spriteIndex); + hid->_height = getHeight(spriteData, hid->_spriteIndex); + hid->_useCount = 1; + } + + if ( !hid->_x && !hid->_y && !hid->_spriteIndex) + hid->_useCount = 0; + curBufIdx += 5; + } + enableHidingBehavior(); + _vm->_globals->freeMemory(ptr); +} + +void ObjectsManager::initVBob() { + for (int idx = 0; idx < 30; ++idx) { + VBobItem *vbob = &_vBob[idx]; + vbob->_displayMode = 0; + vbob->_xp = 0; + vbob->_yp = 0; + vbob->_frameIndex = 0; + vbob->_surface = NULL; + vbob->_spriteData = NULL; + vbob->_oldSpriteData = NULL; + } +} + +void ObjectsManager::clearVBob() { + for (int idx = 0; idx < 30; ++idx) { + VBobItem *vbob = &_vBob[idx]; + vbob->_displayMode = 0; + vbob->_xp = 0; + vbob->_yp = 0; + vbob->_frameIndex = 0; + vbob->_surface = _vm->_globals->freeMemory(vbob->_surface); + vbob->_spriteData = NULL; + vbob->_oldSpriteData = NULL; + } +} + +void ObjectsManager::disableHidingItem(int idx) { + assert(idx < 36); + _bob[idx]._disableFl = true; +} + +void ObjectsManager::enableHidingBehavior() { + _hidingActiveFl = true; +} + +void ObjectsManager::disableHidingBehavior() { + _hidingActiveFl = false; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/objects.h b/engines/hopkins/objects.h new file mode 100644 index 0000000000..5f1f5b1f59 --- /dev/null +++ b/engines/hopkins/objects.h @@ -0,0 +1,338 @@ +/* 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. + * + */ + +#ifndef HOPKINS_OBJECTS_H +#define HOPKINS_OBJECTS_H + +#include "hopkins/globals.h" + +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/str.h" + +#define MAX_SPRITE 5 +namespace Hopkins { + +struct ObjectAuthIcon { + byte _objectFileNum; + byte _idx; + byte _flag1; + byte _flag2; + byte _flag3; + byte _flag4; + byte _flag5; + byte _flag6; +}; + +struct SpriteItem { + int _animationType; + const byte *_spriteData; + Common::Point _spritePos; + int _zoomFactor; + bool _flipFl; + int _spriteIndex; + int _deltaX; + int _deltaY; + bool _rleFl; + bool _activeFl; + int _destX; + int _destY; + int _width; + int _height; + int _zoomPct; + int _reducePct; +}; + +struct BobItem { + int _bobMode; + byte *_spriteData; + int _xp; + int _yp; + int _frameIndex; + int _animDataIdx; + int _moveChange1; + int _moveChange2; + bool _disabledAnimationFl; + byte *_animData; + bool _bobMode10; + int _bobModeChange; + int _modeChangeCtr; + int _modeChangeUnused; + bool _disableFl; // Set to true in B_CACHE_OFF() + int _zoomFactor; + bool _flipFl; + bool _isSpriteFl; + bool _activeFl; + int _oldX; + int _oldY; + int _oldWidth; + int _oldHeight; + int _oldX2; + int _zooInmFactor; + int _zoomOutFactor; +}; + +struct HidingItem { + int _x; + int _y; + int _spriteIndex; + int _width; + int _height; + int _useCount; + byte *_spriteData; + bool _resetUseCount; + int _yOffset; +}; + +struct LockAnimItem { + bool _enableFl; + int _posX; +}; + +struct VBobItem { + const byte *_spriteData; + int _displayMode; + int _xp; + int _yp; + int _frameIndex; + byte *_surface; + int _oldX; + int _oldY; + int _oldFrameIndex; + const byte *_oldSpriteData; +}; + +struct ListeItem { + bool _visibleFl; + int _posX; + int _posY; + int _width; + int _height; +}; + +/** + * Mode for SortItem records + */ +enum SortMode { SORT_NONE = 0, SORT_BOB = 1, SORT_SPRITE = 2, SORT_HIDING = 3 }; + +/** + * Structure to represent a pending display of either a Bob, Sprite, or Cache Item. + */ +struct SortItem { + SortMode _sortMode; + int _index; + int _priority; +}; + +class HopkinsEngine; + +class ObjectsManager { +private: + HopkinsEngine *_vm; + + int _objectWidth, _objectHeight; + int _oldBorderSpriteIndex; + int _borderSpriteIndex; + byte *_spritePtr; + const byte *_oldSpriteData; + int _verb; + int _oldSpriteIndex; + int _oldFrameIndex; + int _oldDirectionSpriteIdx; + Directions _oldDirection; + Directions _lastDirection; + bool _oldFlipFl; + int _curGestureFile; + byte *_gestureBuf; + int _homeRateCounter; + int _sortedDisplayCount; + SortItem _sortedDisplay[51]; + byte *_hidingItemData[6]; + HidingItem _hidingItem[25]; + bool _hidingActiveFl; + ObjectAuthIcon _objectAuthIcons[300]; + int _curObjectFileNum; + byte *_objectDataBuf; + + VBobItem _vBob[30]; + ListeItem _liste[6]; + ListeItem _liste2[35]; + + void initVBob(); + void clearVBob(); + + void sprite_alone(const byte *objectData, byte *sprite, int objIndex); + void removeObjectDataBuf(); + + int getOffsetX(const byte *spriteData, int spriteIndex, bool isSize); + int getOffsetY(const byte *spriteData, int spriteIndex, bool isSize); + + void capture_mem_sprite(const byte *objectData, byte *sprite, int objIndex); + void setBobInfo(int idx); + void computeHideCounter(int idx); + void initBobVariables(int idx); + + void checkHidingItem(); + void displayHiding(int idx); + void computeSprite(int idx); + void beforeSort(SortMode sortMode, int index, int priority); + void displayBobAnim(); + void displayVBob(); + void showSprite(int idx); + void clearSprite(); + void setSpriteZoom(int idx, int zoomFactor); + + void loadZone(const Common::String &file); + void changeCharacterHead(PlayerCharacter oldCharacter, PlayerCharacter newCharacter); + void goHome2(); + + void nextVerbIcon(); + void handleForest(int screenId, int minX, int maxX, int minY, int maxY, int idx); + + void sceneSpecialIni(); + void showActionAnimation(const byte *spriteData, const Common::String &actionStr, int speed, bool flipFl); +public: + bool _disableFl; + bool _forestFl; + bool _visibleFl; + bool _saveLoadFl; + bool _forceZoneFl; + bool _changeVerbFl; + bool _helicopterFl; + bool _twoCharactersFl; + bool _changeHeadFl; + bool _priorityFl; + int _jumpVerb; + int _jumpZone; + int _zoneNum; + int _eraseVisibleCounter; + int _curObjectIndex; + int _startSpriteIndex; + int _saveLoadX, _saveLoadY; + int _mapCarPosX, _mapCarPosY; + int _oldCharacterPosX, _oldCharacterPosY; + Common::Point _borderPos; + Common::Point _oldBorderPos; + Common::Point _characterPos; + byte *_forestSprite; + byte *_saveLoadSprite; + byte *_saveLoadSprite2; + byte *_headSprites; + SpriteItem _sprite[6]; + BobItem _bob[36]; + LockAnimItem _lockedAnims[36]; + bool _charactersEnabledFl; + bool _refreshBobMode10Fl; + + ObjectsManager(HopkinsEngine *vm); + ~ObjectsManager(); + + void clearAll(); + + int getWidth(const byte *objectData, int idx); + int getHeight(const byte *objectData, int idx); + byte *loadSprite(const Common::String &file); + void loadLinkFile(const Common::String &file, bool OBSSEUL = false); + void addStaticSprite(const byte *spriteData, Common::Point pos, int idx, int spriteIndex, int zoomFactor, bool flipFl, int deltaX, int deltaY); + void animateSprite(int idx); + void removeSprite(int idx); + void setSpriteX(int idx, int xp); + void setSpriteY(int idx, int yp); + int getSpriteX(int idx); + int getSpriteY(int idx); + void setSpriteIndex(int idx, int spriteIndex); + void displaySprite(); + void computeAndSetSpriteSize(); + void setFlipSprite(int idx, bool flip); + + int getBobAnimDataIdx(int idx); + void initBorder(int zoneIdx); + void nextObjectIcon(int idx); + void takeInventoryObject(int idx); + void handleSpecialGames(); + + void addObject(int objIndex); + void changeObject(int objIndex); + void removeObject(int objIndex); + + void resetBob(int idx); + void hideBob(int idx); + void displayBob(int idx); + void setBobOffset(int idx, int offset); + void setBobAnimDataIdx(int idx, int animIdx); + void setBobAnimation(int idx); + void stopBobAnimation(int idx); + int getBobPosX(int idx); + + void handleCityMap(); + void clearScreen(); + void disableVerb(int idx, int a2); + void enableVerb(int idx, int a2); + void lockAnimX(int idx, int x); + void handleLeftButton(); + void handleRightButton(); + void setOffsetXY(byte *data, int idx, int xp, int yp, bool isSize); + void setVerb(int id); + + void doActionBack(int idx); + void doActionRight(int idx); + void doActionFront(int idx); + void doActionLeft(int idx); + void doActionDiagRight(int idx); + void doActionDiagLeft(int idx); + void loadObjects(); + byte *loadObjectFromFile(int objIndex, bool mode); + void resetHidingItems(); + void resetHidingUseCount(int idx); + void setHidingUseCount(int idx); + void loadHidingItems(const Common::String &file); + void enableHidingBehavior(); + void disableHidingBehavior(); + void disableHidingItem(int idx); + + void resetHomeRateCounter() { _homeRateCounter = 0; } + void resetOldFrameIndex() { _oldFrameIndex = -1; } + void resetOldDirection() { _oldDirection = DIR_NONE; } + int getObjectWidth() { return _objectWidth; } + int getObjectHeight() { return _objectHeight; } + + void showSpecialActionAnimationWithFlip(const byte *spriteData, const Common::String &animationSeq, int speed, bool flipFl); + void showSpecialActionAnimation(const byte *spriteData, const Common::String &animString, int speed); + void checkEventBobAnim(int idx, int animIdx, int animDataIdx, int a4); + void setMultiBobAnim(int idx1, int idx2, int anim1Idx, int anim2Idx); + void loadObjectIniFile(); + void quickDisplayBobSprite(int idx); + void initVbob(const byte *src, int idx, int xp, int yp, int frameIndex); + void disableVbob(int idx); + void setAndPlayAnim(int idx, int animIdx, int destPosi, bool animAction); + + void sceneControl(const Common::String &backgroundFile, const Common::String &linkFile, + const Common::String &animFile, const Common::String &s4, int soundNum, bool initializeScreen); + void sceneControl2(const Common::String &backgroundFile, const Common::String &linkFile, + const Common::String &animFile, const Common::String &s4, int soundNum, bool initializeScreen); + void goHome(); + void paradise(); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_OBJECTS_H */ diff --git a/engines/hopkins/saveload.cpp b/engines/hopkins/saveload.cpp new file mode 100644 index 0000000000..98fb15046e --- /dev/null +++ b/engines/hopkins/saveload.cpp @@ -0,0 +1,337 @@ +/* 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 "hopkins/saveload.h" + +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/hopkins.h" + + +#include "common/system.h" +#include "common/savefile.h" +#include "graphics/surface.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" + +namespace Hopkins { + +const char *SAVEGAME_STR = "HOPKINS"; +#define SAVEGAME_STR_SIZE 13 + +SaveLoadManager::SaveLoadManager(HopkinsEngine *vm) { + _vm = vm; +} + +bool SaveLoadManager::save(const Common::String &file, const void *buf, size_t n) { + Common::OutSaveFile *savefile = g_system->getSavefileManager()->openForSaving(file); + + if (savefile) { + size_t bytesWritten = savefile->write(buf, n); + savefile->finalize(); + delete savefile; + + return bytesWritten == n; + } else + return false; +} + +// Save File +bool SaveLoadManager::saveFile(const Common::String &file, const void *buf, size_t n) { + return save(file, buf, n); +} + +void SaveLoadManager::initSaves() { + Common::String dataFilename = "HISCORE.DAT"; + byte data[100]; + Common::fill(&data[0], &data[100], 0); + + saveFile(dataFilename, data, 100); +} + +void SaveLoadManager::load(const Common::String &file, byte *buf) { + Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading(file); + if (savefile == NULL) + error("Error opening file - %s", file.c_str()); + + int32 filesize = savefile->size(); + savefile->read(buf, filesize); + delete savefile; +} + +bool SaveLoadManager::readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header) { + char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; + header._thumbnail = NULL; + + // Validate the header Id + in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); + if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE)) + return false; + + header._version = in->readByte(); + if (header._version > HOPKINS_SAVEGAME_VERSION) + return false; + + // Read in the string + header._saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') header._saveName += ch; + + // Get the thumbnail + header._thumbnail = Graphics::loadThumbnail(*in); + if (!header._thumbnail) + return false; + + // Read in save date/time + header._year = in->readSint16LE(); + header._month = in->readSint16LE(); + header._day = in->readSint16LE(); + header._hour = in->readSint16LE(); + header._minute = in->readSint16LE(); + header._totalFrames = in->readUint32LE(); + + return true; +} + +void SaveLoadManager::writeSavegameHeader(Common::OutSaveFile *out, hopkinsSavegameHeader &header) { + // Write out a savegame header + out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1); + + out->writeByte(HOPKINS_SAVEGAME_VERSION); + + // Write savegame name + out->write(header._saveName.c_str(), header._saveName.size() + 1); + + // Create a thumbnail and save it + Graphics::Surface *thumb = new Graphics::Surface(); + createThumbnail(thumb); + Graphics::saveThumbnail(*out, *thumb); + thumb->free(); + delete thumb; + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + out->writeSint16LE(td.tm_year + 1900); + out->writeSint16LE(td.tm_mon + 1); + out->writeSint16LE(td.tm_mday); + out->writeSint16LE(td.tm_hour); + out->writeSint16LE(td.tm_min); + out->writeUint32LE(_vm->_events->_gameCounter); +} + +Common::Error SaveLoadManager::saveGame(int slot, const Common::String &saveName) { + /* Pack any necessary data into the savegame data structure */ + // Set the selected slot number + _vm->_globals->_saveData->_data[svLastSavegameSlot] = slot; + + // Set up the inventory + for (int i = 0; i < 35; ++i) + _vm->_globals->_saveData->_inventory[i] = _vm->_globals->_inventory[i]; + + _vm->_globals->_saveData->_mapCarPosX = _vm->_objectsMan->_mapCarPosX; + _vm->_globals->_saveData->_mapCarPosY = _vm->_objectsMan->_mapCarPosY; + + /* Create the savegame */ + Common::OutSaveFile *savefile = g_system->getSavefileManager()->openForSaving(_vm->generateSaveName(slot)); + if (!savefile) + return Common::kCreatingFileFailed; + + // Set up the serializer + Common::Serializer serializer(NULL, savefile); + + // Write out the savegame header + hopkinsSavegameHeader header; + header._saveName = saveName; + header._version = HOPKINS_SAVEGAME_VERSION; + writeSavegameHeader(savefile, header); + + // Write out the savegame data + syncSavegameData(serializer, header._version); + + // Save file complete + savefile->finalize(); + delete savefile; + + return Common::kNoError; +} + +Common::Error SaveLoadManager::loadGame(int slot) { + // Try and open the save file for reading + Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading( + _vm->generateSaveName(slot)); + if (!savefile) + return Common::kReadingFailed; + + // Set up the serializer + Common::Serializer serializer(savefile, NULL); + + // Read in the savegame header + hopkinsSavegameHeader header; + readSavegameHeader(savefile, header); + if (header._thumbnail) + header._thumbnail->free(); + delete header._thumbnail; + + // Read in the savegame data + syncSavegameData(serializer, header._version); + + // Loading save file complete + delete savefile; + + // Unpack the inventory + for (int i = 0; i < 35; ++i) + _vm->_globals->_inventory[i] = _vm->_globals->_saveData->_inventory[i]; + + // Set variables from loaded data as necessary + _vm->_globals->_saveData->_data[svLastSavegameSlot] = slot; + _vm->_globals->_exitId = _vm->_globals->_saveData->_data[svLastScreenId]; + _vm->_globals->_saveData->_data[svLastPrevScreenId] = 0; + _vm->_globals->_screenId = 0; + _vm->_objectsMan->_mapCarPosX = _vm->_globals->_saveData->_mapCarPosX; + _vm->_objectsMan->_mapCarPosY = _vm->_globals->_saveData->_mapCarPosY; + + return Common::kNoError; +} + +bool SaveLoadManager::readSavegameHeader(int slot, hopkinsSavegameHeader &header) { + // Try and open the save file for reading + Common::InSaveFile *savefile = g_system->getSavefileManager()->openForLoading( + _vm->generateSaveName(slot)); + if (!savefile) + return false; + + bool result = readSavegameHeader(savefile, header); + delete savefile; + return result; +} + +#define REDUCE_AMOUNT 80 + +void SaveLoadManager::createThumbnail(Graphics::Surface *s) { + int w = _vm->_graphicsMan->zoomOut(SCREEN_WIDTH, REDUCE_AMOUNT); + int h = _vm->_graphicsMan->zoomOut(SCREEN_HEIGHT - 40, REDUCE_AMOUNT); + + Graphics::Surface thumb8; + thumb8.create(w, h, Graphics::PixelFormat::createFormatCLUT8()); + + _vm->_graphicsMan->reduceScreenPart(_vm->_graphicsMan->_frontBuffer, (byte *)thumb8.pixels, + _vm->_events->_startPos.x, 20, SCREEN_WIDTH, SCREEN_HEIGHT - 40, 80); + + // Convert the 8-bit pixel to 16 bit surface + s->create(w, h, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); + + const byte *srcP = (const byte *)thumb8.pixels; + uint16 *destP = (uint16 *)s->pixels; + + for (int yp = 0; yp < h; ++yp) { + // Copy over the line, using the source pixels as lookups into the pixels palette + const byte *lineSrcP = srcP; + uint16 *lineDestP = destP; + + for (int xp = 0; xp < w; ++xp) + *lineDestP++ = *(uint16 *)&_vm->_graphicsMan->_palettePixels[*lineSrcP++ * 2]; + + // Move to the start of the next line + srcP += w; + destP += w; + } + thumb8.free(); +} + +void SaveLoadManager::syncSavegameData(Common::Serializer &s, int version) { + s.syncBytes(&_vm->_globals->_saveData->_data[0], 2050); + syncCharacterLocation(s, _vm->_globals->_saveData->_cloneHopkins); + syncCharacterLocation(s, _vm->_globals->_saveData->_realHopkins); + syncCharacterLocation(s, _vm->_globals->_saveData->_samantha); + + for (int i = 0; i < 35; ++i) + s.syncAsSint16LE(_vm->_globals->_saveData->_inventory[i]); + + if (version > 1) { + s.syncAsSint16LE(_vm->_globals->_saveData->_mapCarPosX); + s.syncAsSint16LE(_vm->_globals->_saveData->_mapCarPosY); + } else { + _vm->_globals->_saveData->_mapCarPosX = _vm->_globals->_saveData->_mapCarPosY = 0; + } + +} + +void SaveLoadManager::syncCharacterLocation(Common::Serializer &s, CharacterLocation &item) { + s.syncAsSint16LE(item._pos.x); + s.syncAsSint16LE(item._pos.y); + s.syncAsSint16LE(item._startSpriteIndex); + s.syncAsSint16LE(item._location); + s.syncAsSint16LE(item._zoomFactor); +} + +void SaveLoadManager::convertThumb16To8(Graphics::Surface *thumb16, Graphics::Surface *thumb8) { + thumb8->create(thumb16->w, thumb16->h, Graphics::PixelFormat::createFormatCLUT8()); + Graphics::PixelFormat pixelFormat16(2, 5, 6, 5, 0, 11, 5, 0, 0); + + byte paletteR[PALETTE_SIZE]; + byte paletteG[PALETTE_SIZE]; + byte paletteB[PALETTE_SIZE]; + for (int palIndex = 0; palIndex < PALETTE_SIZE; ++palIndex) { + uint16 p = READ_UINT16(&_vm->_graphicsMan->_palettePixels[palIndex * 2]); + pixelFormat16.colorToRGB(p, paletteR[palIndex], paletteG[palIndex], paletteB[palIndex]); + } + + const uint16 *srcP = (const uint16 *)thumb16->pixels; + byte *destP = (byte *)thumb8->pixels; + + for (int yp = 0; yp < thumb16->h; ++yp) { + const uint16 *lineSrcP = srcP; + byte *lineDestP = destP; + + for (int xp = 0; xp < thumb16->w; ++xp) { + byte r, g, b; + pixelFormat16.colorToRGB(*lineSrcP++, r, g, b); + + // Do like in the original and show thumbnail as a grayscale picture + int lum = (r * 21 + g * 72 + b * 7) / 100; + r = g = b = lum; + + // Scan the palette for the closest match + int difference = 99999, foundIndex = 0; + for (int palIndex = 0; palIndex < PALETTE_SIZE; ++palIndex) { + byte rCurrent = paletteR[palIndex]; + byte gCurrent = paletteG[palIndex]; + byte bCurrent = paletteB[palIndex]; + + int diff = ABS((int)r - (int)rCurrent) + ABS((int)g - (int)gCurrent) + ABS((int)b - (int)bCurrent); + if (diff < difference) { + difference = diff; + foundIndex = palIndex; + } + } + + *lineDestP++ = foundIndex; + } + + // Move to the start of the next line + srcP += thumb16->w; + destP += thumb16->w; + } +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/saveload.h b/engines/hopkins/saveload.h new file mode 100644 index 0000000000..6fee814180 --- /dev/null +++ b/engines/hopkins/saveload.h @@ -0,0 +1,78 @@ +/* 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. + * + */ + +#ifndef HOPKINS_SAVELOAD_H +#define HOPKINS_SAVELOAD_H + +#include "hopkins/globals.h" +#include "hopkins/graphics.h" + +#include "common/scummsys.h" +#include "common/savefile.h" +#include "common/serializer.h" +#include "common/str.h" + +namespace Hopkins { + +class HopkinsEngine; + +#define HOPKINS_SAVEGAME_VERSION 2 + +struct hopkinsSavegameHeader { + uint8 _version; + Common::String _saveName; + Graphics::Surface *_thumbnail; + int _year, _month, _day; + int _hour, _minute; + int _totalFrames; +}; + +class SaveLoadManager { +private: + HopkinsEngine *_vm; + + void createThumbnail(Graphics::Surface *s); + void syncSavegameData(Common::Serializer &s, int version); + void syncCharacterLocation(Common::Serializer &s, CharacterLocation &item); +public: + SaveLoadManager(HopkinsEngine *vm); + + void initSaves(); + bool save(const Common::String &file, const void *buf, size_t n); + bool saveFile(const Common::String &file, const void *buf, size_t n); + void load(const Common::String &file, byte *buf); + + static bool readSavegameHeader(Common::InSaveFile *in, hopkinsSavegameHeader &header); + void writeSavegameHeader(Common::OutSaveFile *out, hopkinsSavegameHeader &header); + bool readSavegameHeader(int slot, hopkinsSavegameHeader &header); + Common::Error saveGame(int slot, const Common::String &saveName); + Common::Error loadGame(int slot); + + /** + * Converts a 16-bit thumbnail to 8 bit paletted using the currently active palette. + */ + void convertThumb16To8(Graphics::Surface *thumb16, Graphics::Surface *thumb8); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_SAVELOAD_H */ diff --git a/engines/hopkins/script.cpp b/engines/hopkins/script.cpp new file mode 100644 index 0000000000..7e150624b8 --- /dev/null +++ b/engines/hopkins/script.cpp @@ -0,0 +1,2628 @@ +/* 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 "hopkins/objects.h" +#include "hopkins/dialogs.h" +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/sound.h" +#include "hopkins/hopkins.h" + +#include "common/system.h" +#include "graphics/palette.h" +#include "common/file.h" +#include "common/rect.h" +#include "engines/util.h" + +namespace Hopkins { + +ScriptManager::ScriptManager(HopkinsEngine *vm) { + _vm = vm; + _tempObjectFl = false; +} + +int ScriptManager::handleOpcode(const byte *dataP) { + if (READ_BE_UINT16(dataP) != MKTAG16('F', 'C')) + return 0; + + int opcodeType = 0; + int vbobFrameIndex = 0; + + uint32 signature24 = READ_BE_UINT24(&dataP[2]); + switch (signature24) { + case MKTAG24('T', 'X', 'T'): { + vbobFrameIndex = dataP[6]; + int mesgId = READ_LE_INT16(dataP + 13); + opcodeType = 1; + if (!_tempObjectFl) { + if (_vm->_globals->_saveData->_data[svField356] == 1) { + if (mesgId == 53) + mesgId = 644; + if (mesgId == 624) + mesgId = 639; + if (mesgId == 627) + mesgId = 630; + if (mesgId == 625) + mesgId = 639; + if (mesgId == 8) + mesgId = 637; + if (mesgId == 557) + mesgId = 636; + if (mesgId == 51) + mesgId = 644; + if (mesgId == 287) + mesgId = 636; + if (mesgId == 619) + mesgId = 633; + if (mesgId == 620) + mesgId = 634; + if (mesgId == 622) + mesgId = 644; + if (mesgId == 297) + mesgId = 636; + if (mesgId == 612 || mesgId == 613 || mesgId == 614 || mesgId == 134) + mesgId = 636; + if (mesgId == 615) + mesgId = 635; + if (mesgId == 618) + mesgId = 632; + if (mesgId == 611) + mesgId = 642; + if (mesgId == 610) + mesgId = 641; + if (mesgId == 18) + mesgId = 643; + if (mesgId == 602) + mesgId = 645; + if (mesgId == 603) + mesgId = 646; + if (mesgId == 604) + mesgId = 647; + if (mesgId == 607) + mesgId = 650; + if (mesgId == 605) + mesgId = 648; + if (mesgId == 606) + mesgId = 649; + if (mesgId == 601) + mesgId = 652; + if (mesgId == 37) + mesgId = 636; + if (mesgId == 595) + mesgId = 633; + if (mesgId == 596) + mesgId = 634; + if (mesgId == 532) + mesgId = 636; + if (mesgId == 599) + mesgId = 636; + if (mesgId == 363) + mesgId = 636; + } + if (!_vm->_soundMan->_soundOffFl && _vm->_soundMan->_soundFl) { + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_soundMan->_soundFl); + } + bool displayedTxtFl = false; + if (!_vm->_soundMan->_textOffFl) { + int textPosX = READ_LE_INT16(dataP + 9); + int textPosY = READ_LE_INT16(dataP + 11); + _vm->_fontMan->initTextBuffers(9, mesgId, _vm->_globals->_textFilename, 2 * textPosX, 2 * textPosY + 40, 6, dataP[7], 253); + _vm->_fontMan->showText(9); + displayedTxtFl = true; + } + if (!_vm->_soundMan->_voiceOffFl) + _vm->_soundMan->mixVoice(mesgId, 4, displayedTxtFl); + } else { // if (_tempObjectFl) + if (_vm->_globals->_saveData->_data[svField356]) { + _vm->_fontMan->initTextBuffers(9, 635, _vm->_globals->_textFilename, 55, 20, dataP[8], 35, 253); + bool displayedTxtFl = false; + if (!_vm->_soundMan->_textOffFl) { + _vm->_fontMan->showText(9); + displayedTxtFl = true; + } + + if (!_vm->_soundMan->_voiceOffFl) + _vm->_soundMan->mixVoice(635, 4, displayedTxtFl); + } else { + int textPosX = READ_LE_INT16(dataP + 9); + if (_vm->_globals->_language == LANG_FR && !_vm->_soundMan->_textOffFl) + _vm->_fontMan->initTextBuffers(9, mesgId, "OBJET1.TXT", 2 * textPosX, 60, 6, dataP[7], 253); + else if (_vm->_globals->_language == LANG_EN && !_vm->_soundMan->_textOffFl) + _vm->_fontMan->initTextBuffers(9, mesgId, "OBJETAN.TXT", 2 * textPosX, 60, 6, dataP[7], 253); + else if (_vm->_globals->_language == LANG_SP && !_vm->_soundMan->_textOffFl) { + _vm->_fontMan->initTextBuffers(9, mesgId, "OBJETES.TXT", 2 * textPosX, 60, 6, dataP[7], 253); + } + + bool displayedTxtFl = false; + if (!_vm->_soundMan->_textOffFl) { + _vm->_fontMan->showText(9); + displayedTxtFl = true; + } + + if (!_vm->_soundMan->_voiceOffFl) + _vm->_soundMan->mixVoice(mesgId, 5, displayedTxtFl); + } + } + break; + } + case MKTAG24('B', 'O', 'B'): + if (!_vm->_objectsMan->_disableFl) { + int vbobIdx = dataP[5]; + vbobFrameIndex = dataP[6]; + int moveChange = dataP[7]; + int vbobPosX = READ_LE_INT16(dataP + 8); + int vbobPosY = READ_LE_INT16(dataP + 10); + if (vbobIdx == 52) { + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, vbobPosX, READ_LE_INT16(dataP + 10), vbobFrameIndex); + } else if (vbobIdx == 51) { + _vm->_objectsMan->quickDisplayBobSprite(vbobFrameIndex); + } else if (vbobIdx != 50) { + _vm->_objectsMan->initVbob(_vm->_globals->_levelSpriteBuf, vbobIdx, vbobPosX, vbobPosY, vbobFrameIndex); + if (moveChange) + moveChange /= _vm->_globals->_speed; + if (moveChange > 1) { + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + --moveChange; + _vm->_events->refreshScreenAndEvents(); + } while (moveChange); + } + } + } + opcodeType = 1; + break; + case MKTAG24('S', 'T', 'P'): + if (!_vm->_objectsMan->_disableFl) { + // HACK: This piece of code is a replacement to the missing STE opcode when entering the FBI lab. + if (_vm->_globals->_curRoomNum == 10) { + _vm->_globals->_prevScreenId = _vm->_globals->_screenId; + _vm->_globals->_saveData->_data[svLastPrevScreenId] = _vm->_globals->_screenId; + _vm->_globals->_screenId = _vm->_globals->_saveData->_data[svLastScreenId] = 10; + } + _vm->_objectsMan->_twoCharactersFl = false; + _vm->_objectsMan->_characterPos.x = READ_LE_INT16(dataP + 6); + _vm->_objectsMan->_characterPos.y = READ_LE_INT16(dataP + 8); + _vm->_objectsMan->_startSpriteIndex = dataP[5]; + if (_vm->_objectsMan->_changeHeadFl) { + if (_vm->_globals->_saveData->_data[svField354] == 1 + && _vm->_globals->_saveData->_cloneHopkins._pos.x && _vm->_globals->_saveData->_cloneHopkins._pos.y + && _vm->_globals->_saveData->_cloneHopkins._startSpriteIndex && _vm->_globals->_saveData->_cloneHopkins._location) { + + _vm->_objectsMan->_characterPos = _vm->_globals->_saveData->_cloneHopkins._pos; + _vm->_objectsMan->_startSpriteIndex = _vm->_globals->_saveData->_cloneHopkins._startSpriteIndex; + } + if (_vm->_globals->_saveData->_data[svField356] == 1 + && _vm->_globals->_saveData->_samantha._pos.x && _vm->_globals->_saveData->_samantha._pos.y + && _vm->_globals->_saveData->_samantha._startSpriteIndex && _vm->_globals->_saveData->_samantha._location) { + _vm->_objectsMan->_characterPos = _vm->_globals->_saveData->_samantha._pos; + _vm->_objectsMan->_startSpriteIndex = _vm->_globals->_saveData->_samantha._startSpriteIndex; + } + if (_vm->_globals->_saveData->_data[svField357] == 1 + && _vm->_globals->_saveData->_realHopkins._pos.x && _vm->_globals->_saveData->_realHopkins._pos.y + && _vm->_globals->_saveData->_realHopkins._startSpriteIndex && _vm->_globals->_saveData->_realHopkins._location) { + _vm->_objectsMan->_characterPos = _vm->_globals->_saveData->_realHopkins._pos; + _vm->_objectsMan->_startSpriteIndex = _vm->_globals->_saveData->_realHopkins._startSpriteIndex; + } + } + if (_vm->_globals->_saveData->_data[svField356] == 1 + && _vm->_globals->_saveData->_realHopkins._location == _vm->_globals->_screenId) { + _vm->_objectsMan->addStaticSprite( + _vm->_objectsMan->_headSprites, + _vm->_globals->_saveData->_realHopkins._pos, + 1, + 2, + _vm->_globals->_saveData->_realHopkins._zoomFactor, + false, + 34, + 190); + _vm->_objectsMan->animateSprite(1); + _vm->_objectsMan->_twoCharactersFl = true; + } + if (_vm->_globals->_saveData->_data[svField357] == 1 + && _vm->_globals->_saveData->_data[svField355] == 1 + && _vm->_globals->_saveData->_samantha._location == _vm->_globals->_screenId) { + _vm->_objectsMan->addStaticSprite( + _vm->_objectsMan->_headSprites, + _vm->_globals->_saveData->_samantha._pos, + 1, + 3, + _vm->_globals->_saveData->_samantha._zoomFactor, + false, + 20, + 127); + _vm->_objectsMan->animateSprite(1); + _vm->_objectsMan->_twoCharactersFl = true; + } + } + opcodeType = 1; + _vm->_objectsMan->_changeHeadFl = false; + break; + case MKTAG24('S', 'T', 'E'): + if (!_vm->_objectsMan->_disableFl) { + _vm->_globals->_prevScreenId = _vm->_globals->_screenId; + _vm->_globals->_saveData->_data[svLastPrevScreenId] = _vm->_globals->_screenId; + _vm->_globals->_screenId = _vm->_globals->_saveData->_data[svLastScreenId] = dataP[5]; + vbobFrameIndex = dataP[6]; + } + opcodeType = 1; + break; + case MKTAG24('B', 'O', 'F'): + if (!_vm->_objectsMan->_disableFl) + _vm->_objectsMan->disableVbob(READ_LE_INT16(dataP + 5)); + opcodeType = 1; + break; + case MKTAG24('P', 'E', 'R'): { + int specialOpcode = READ_LE_INT16(dataP + 5); + if (!_vm->_globals->_saveData->_data[svAlternateSpriteFl] && !_vm->_globals->_saveData->_data[svField356]) { + vbobFrameIndex = 0; + + switch (specialOpcode) { + case 1: + case 14: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(4); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(4); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(4); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(4); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(4); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(4); + break; + case 2: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(7); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(7); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(7); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(7); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(7); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(7); + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(8); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(8); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(8); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(8); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(8); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(8); + break; + case 19: + case 4: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(1); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(1); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(1); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(1); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(1); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(1); + break; + case 5: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(5); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(5); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(5); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(5); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(5); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(5); + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(6); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(6); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(6); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(6); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(6); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(6); + break; + case 17: + case 7: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(2); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(2); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(2); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(2); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(2); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(2); + break; + case 18: + case 8: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(3); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(3); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(3); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(3); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(3); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(3); + break; + case 9: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(5); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(5); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(5); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(5); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(5); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(5); + break; + case 10: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(6); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(6); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(6); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(6); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(6); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(6); + break; + case 15: + case 11: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(7); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(7); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(7); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(7); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(7); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(7); + break; + case 16: + case 12: + if (_vm->_globals->_actionDirection == DIR_UP) + _vm->_objectsMan->doActionBack(8); + if (_vm->_globals->_actionDirection == DIR_RIGHT) + _vm->_objectsMan->doActionRight(8); + if (_vm->_globals->_actionDirection == DIR_UP_RIGHT) + _vm->_objectsMan->doActionDiagRight(8); + if (_vm->_globals->_actionDirection == DIR_DOWN) + _vm->_objectsMan->doActionFront(8); + if (_vm->_globals->_actionDirection == DIR_UP_LEFT) + _vm->_objectsMan->doActionDiagLeft(8); + if (_vm->_globals->_actionDirection == DIR_LEFT) + _vm->_objectsMan->doActionLeft(8); + break; + } + } + opcodeType = 1; + break; + } + case MKTAG24('M', 'U', 'S'): + opcodeType = 1; + break; + case MKTAG24('W', 'A', 'I'): { + uint frameNumb = READ_LE_UINT16(dataP + 5) / _vm->_globals->_speed; + if (!frameNumb) + frameNumb = 1; + for (uint i = 0; i < frameNumb + 1; i++) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } + opcodeType = 1; + break; + } + case MKTAG24('O', 'B', 'P'): + opcodeType = 1; + _vm->_objectsMan->addObject(READ_LE_INT16(dataP + 5)); + break; + case MKTAG24('O', 'B', 'M'): + opcodeType = 1; + _vm->_objectsMan->removeObject(READ_LE_INT16(dataP + 5)); + break; + case MKTAG24('G', 'O', 'T'): + opcodeType = 2; + break; + case MKTAG24('Z', 'O', 'N'): + _vm->_linesMan->enableZone(READ_LE_INT16(dataP + 5)); + opcodeType = 1; + break; + case MKTAG24('Z', 'O', 'F'): + _vm->_linesMan->disableZone(READ_LE_INT16(dataP + 5)); + opcodeType = 1; + break; + case MKTAG24('E', 'X', 'I'): + opcodeType = 5; + break; + case MKTAG24('S', 'O', 'R'): + _vm->_globals->_exitId = READ_LE_INT16(dataP + 5); + opcodeType = 5; + break; + case MKTAG24('B', 'C', 'A'): + _vm->_objectsMan->disableHidingItem(READ_LE_INT16(dataP + 5)); + opcodeType = 1; + break; + case MKTAG24('A', 'N', 'I'): { + int animId = READ_LE_INT16(dataP + 5); + if (animId <= 100) + _vm->_objectsMan->setBobAnimation(animId); + else + _vm->_objectsMan->stopBobAnimation(animId - 100); + opcodeType = 1; + break; + } + case MKTAG24('S', 'P', 'E'): + switch (READ_LE_INT16(dataP + 5)) { + case 6: + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setAndPlayAnim(20, 0, 14, true); + break; + + case 7: + _vm->_talkMan->startAnimatedCharacterDialogue("rueh1.pe2"); + break; + + case 8: + _vm->_talkMan->startAnimatedCharacterDialogue("ruef1.pe2"); + break; + + case 10: + _vm->_talkMan->startAnimatedCharacterDialogue("bqeflic1.pe2"); + break; + + case 11: + _vm->_talkMan->startAnimatedCharacterDialogue("bqeflic2.pe2"); + break; + + case 12: + // Bank - negotiations between Hopkins and one of the killers + _vm->_fontMan->hideText(9); + _vm->_events->refreshScreenAndEvents(); + _vm->_events->refreshScreenAndEvents(); + _vm->_talkMan->startAnimatedCharacterDialogue("bqetueur.pe2"); + break; + + case 13: + // Bank - after negotiations, Hopkins enters the bank + _vm->_events->_mouseButton = _vm->_events->_curMouseButton; + _vm->_globals->_disableInventFl = true; + _vm->_graphicsMan->fadeOutLong(); + _vm->_objectsMan->disableHidingBehavior(); + _vm->_objectsMan->removeSprite(0); + _vm->_fontMan->hideText(5); + _vm->_fontMan->hideText(9); + _vm->_graphicsMan->endDisplayBob(); + _vm->_objectsMan->clearScreen(); + + if ((_vm->getPlatform() != Common::kPlatformWindows) || !_vm->getIsDemo()) { + _vm->_soundMan->playSoundFile("SOUND17.WAV"); + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playSequence2("HELICO.SEQ", 10, 4, 10); + } + + _vm->_animMan->loadAnim("otage"); + _vm->_graphicsMan->loadImage("IM05"); + _vm->_graphicsMan->displayAllBob(); + + for (int i = 0; i <= 4; i++) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } + + _vm->_events->mouseOff(); + _vm->_graphicsMan->fadeInDefaultLength(_vm->_graphicsMan->_frontBuffer); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(3) != 100); + _vm->_graphicsMan->fadeOutDefaultLength(_vm->_graphicsMan->_frontBuffer); + _vm->_graphicsMan->endDisplayBob(); + + // If uncensored, rip the throat of the hostage + if (!_vm->_globals->_censorshipFl) { + _vm->_soundMan->_specialSoundNum = 16; + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playAnim("EGORGE.ANM", "EGORGE.ANM", 50, 28, 500); + _vm->_soundMan->_specialSoundNum = 0; + } + _vm->_animMan->loadAnim("ASCEN"); + _vm->_events->mouseOff(); + _vm->_graphicsMan->loadImage("ASCEN"); + _vm->_graphicsMan->displayAllBob(); + + for (int i = 0; i <= 4; i++) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } + + _vm->_events->mouseOff(); + _vm->_graphicsMan->fadeInDefaultLength(_vm->_graphicsMan->_frontBuffer); + _vm->_objectsMan->checkEventBobAnim(1, 0, 17, 3); + _vm->_graphicsMan->fadeOutDefaultLength(_vm->_graphicsMan->_frontBuffer); + _vm->_graphicsMan->endDisplayBob(); + + if ((_vm->getPlatform() == Common::kPlatformWindows) && _vm->getIsDemo()) + _vm->_soundMan->playSoundFile("SOUND17.WAV"); + + _vm->_soundMan->_specialSoundNum = 14; + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playSequence2("ASSOM.SEQ", 10, 4, 500); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_globals->_disableInventFl = false; + _vm->_objectsMan->_helicopterFl = true; + break; + + case 16: + _vm->_talkMan->startAnimatedCharacterDialogue("ftoubib.pe2"); + break; + + case 17: + _vm->_talkMan->startAnimatedCharacterDialogue("flic2b.pe2"); + break; + + case 18: + _vm->_talkMan->startAnimatedCharacterDialogue("fjour.pe2"); + break; + + case 20: + _vm->_talkMan->startAnimatedCharacterDialogue("PUNK.pe2"); + break; + + case 21: + _vm->_talkMan->startAnimatedCharacterDialogue("MEDLEG.pe2"); + break; + + case 22: + _vm->_talkMan->animateObject("CADAVRE1.pe2"); + break; + + case 23: + _vm->_talkMan->startStaticCharacterDialogue("CHERCHE1.pe2"); + break; + + case 25: + _vm->_talkMan->startAnimatedCharacterDialogue("AGENT1.pe2"); + break; + + case 26: + _vm->_talkMan->startAnimatedCharacterDialogue("AGENT2.pe2"); + break; + + case 27: + if (_vm->_globals->_saveData->_data[svField94] != 1 || _vm->_globals->_saveData->_data[svField95] != 1) + _vm->_talkMan->startAnimatedCharacterDialogue("STANDAR.pe2"); + else + _vm->_talkMan->startAnimatedCharacterDialogue("STANDAR1.pe2"); + break; + + case 29: + _vm->_globals->_disableInventFl = true; + _vm->_talkMan->animateObject("TELEP.pe2"); + _vm->_globals->_disableInventFl = false; + break; + + case 32: + _vm->_talkMan->startAnimatedCharacterDialogue("SAMAN.pe2"); + break; + + case 35: + if (!_vm->_soundMan->_soundOffFl) { + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_soundMan->_soundFl); + } + _vm->_talkMan->startAnimatedCharacterDialogue("PTLAB.pe2"); + break; + + case 36: + if (_vm->_globals->_saveData->_data[svField270] == 2 && _vm->_globals->_saveData->_data[svField94] == 1 && _vm->_globals->_saveData->_data[svField95] == 1) + _vm->_globals->_saveData->_data[svField270] = 3; + + switch (_vm->_globals->_saveData->_data[svField270]) { + case 0: + _vm->_talkMan->startStaticCharacterDialogue("PATRON0.pe2"); + break; + case 1: + _vm->_talkMan->startStaticCharacterDialogue("PATRON1.pe2"); + break; + case 2: + _vm->_talkMan->startStaticCharacterDialogue("PATRON2.pe2"); + break; + case 3: + _vm->_talkMan->startStaticCharacterDialogue("PATRON3.pe2"); + break; + default: + if (_vm->_globals->_saveData->_data[svField270] > 3) { + _vm->_talkMan->startStaticCharacterDialogue("PATRON4.pe2"); + _vm->_globals->_saveData->_data[svField270] = 5; + } + break; + } + break; + + case 37: + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playSequence2("corde.SEQ", 32, 32, 100); + _vm->_graphicsMan->_noFadingFl = true; + break; + + case 38: + _vm->_soundMan->loadSample(1, "SOUND44.WAV"); + _vm->_soundMan->loadSample(2, "SOUND42.WAV"); + _vm->_soundMan->loadSample(3, "SOUND41.WAV"); + _vm->_soundMan->_specialSoundNum = 17; + _vm->_animMan->playSequence("grenade.SEQ", 1, 32, 100, false, false); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playAnim("CREVE17.ANM", "CREVE17.ANM", 24, 24, 200); + _vm->_soundMan->removeSample(1); + _vm->_soundMan->removeSample(2); + _vm->_soundMan->removeSample(3); + _vm->_graphicsMan->_noFadingFl = true; + break; + + case 40: + _vm->_talkMan->startAnimatedCharacterDialogue("MAGE.pe2"); + break; + + case 41: + _vm->_talkMan->startAnimatedCharacterDialogue("MORT3.pe2"); + break; + + case 42: + _vm->_talkMan->startAnimatedCharacterDialogue("MORT2.pe2"); + break; + + case 43: + _vm->_talkMan->startAnimatedCharacterDialogue("MORT1.pe2"); + break; + + case 44: + _vm->_talkMan->startAnimatedCharacterDialogue("MORT3A.pe2"); + break; + + case 45: + _vm->_talkMan->startAnimatedCharacterDialogue("FEM3.pe2"); + break; + + case 46: { + _vm->_globals->_checkDistanceFl = true; + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 564, 420); + _vm->_objectsMan->_zoneNum = -1; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_objectsMan->removeSprite(0); + _vm->_globals->_checkDistanceFl = true; + _vm->_soundMan->loadSample(1, "SOUND44.WAV"); + _vm->_soundMan->loadSample(2, "SOUND45.WAV"); + _vm->_objectsMan->setMultiBobAnim(9, 10, 0, 0); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(9)) { + case 4: + case 16: + case 28: + if (!playFl) { + _vm->_soundMan->playSample(1); + playFl = true; + } + break; + case 5: + case 17: + case 29: + playFl = false; + break; + } + + switch (_vm->_objectsMan->getBobAnimDataIdx(10)) { + case 10: + case 22: + case 33: + if (!playFl) { + _vm->_soundMan->playSample(2); + playFl = true; + } + break; + case 11: + playFl = false; + break; + case 12: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 249, 1); + break; + case 23: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 249, 2); + playFl = false; + break; + case 34: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 249, 3); + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(9) == 36) + break; + } + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->stopBobAnimation(9); + _vm->_objectsMan->stopBobAnimation(10); + _vm->_soundMan->removeSample(1); + _vm->_soundMan->removeSample(2); + break; + } + + case 47: + _vm->_talkMan->startAnimatedCharacterDialogue("BARMAN.pe2"); + break; + + case 48: + _vm->_talkMan->startAnimatedCharacterDialogue("SAMAN2.pe2"); + break; + + case 49: { + _vm->_objectsMan->disableHidingBehavior(); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setMultiBobAnim(9, 10, 0, 0); + + int endIdx; + if (_vm->_globals->_saveData->_data[svField133] == 1) + endIdx = 41; + else + endIdx = 12; + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(9)) { + case 4: + case 18: + if (!playFl) { + _vm->_soundMan->directPlayWav("SOUND44.WAV"); + playFl = true; + } + break; + case 5: + case 9: + playFl = false; + break; + } + + switch (_vm->_objectsMan->getBobAnimDataIdx(10)) { + case 11: + if (!playFl) { + _vm->_soundMan->directPlayWav("SOUND45.WAV"); + playFl = true; + } + break; + case 12: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(9) == endIdx) + break; + } + + if (endIdx == 12) { + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->stopBobAnimation(9); + } + _vm->_objectsMan->enableHidingBehavior(); + break; + } + + case 50: + _vm->_soundMan->playSoundFile("SOUND46.WAv"); + _vm->_objectsMan->setAndPlayAnim(11, 0, 23, false); + break; + + case 51: { + _vm->_graphicsMan->fadeOutLong(); + _vm->_objectsMan->disableHidingBehavior(); + _vm->_objectsMan->removeSprite(0); + _vm->_fontMan->hideText(5); + _vm->_fontMan->hideText(9); + _vm->_graphicsMan->endDisplayBob(); + _vm->_graphicsMan->loadImage("IM20f"); + _vm->_animMan->loadAnim("ANIM20f"); + _vm->_graphicsMan->displayAllBob(); + _vm->_events->mouseOff(); + _vm->_graphicsMan->fadeInLong(); + _vm->_soundMan->loadWav("SOUND46.WAV", 1); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + if (_vm->_objectsMan->getBobAnimDataIdx(12) == 5 && !playFl) { + _vm->_soundMan->playWav(1); + playFl = true; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(12) == 34) + break; + } + _vm->_objectsMan->stopBobAnimation(2); + _vm->_graphicsMan->fadeOutLong(); + _vm->_graphicsMan->_noFadingFl = true; + _vm->_globals->_exitId = 20; + break; + } + + case 52: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("GARDE.PE2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 53: + _vm->_talkMan->startAnimatedCharacterDialogue("GARDE1.pe2"); + break; + + case 54: + _vm->_talkMan->startAnimatedCharacterDialogue("GARDE2.pe2"); + break; + + case 55: + _vm->_objectsMan->stopBobAnimation(1); + _vm->_objectsMan->setAndPlayAnim(15, 0, 12, false); + _vm->_objectsMan->stopBobAnimation(15); + _vm->_objectsMan->loadLinkFile("IM19a", true); + break; + + case 56: + _vm->_globals->_characterSpriteBuf = _vm->_fileIO->loadFile("HOPFEM.SPR"); + _vm->_globals->_characterType = CHARACTER_HOPKINS_CLONE; + _vm->_globals->_saveData->_data[svAlternateSpriteFl] = 1; + _vm->_globals->loadCharacterData(); + _vm->_objectsMan->_sprite[0]._deltaX = 28; + _vm->_objectsMan->_sprite[0]._deltaY = 155; + _vm->_objectsMan->_sprite[0]._spriteData = _vm->_globals->_characterSpriteBuf; + _vm->_objectsMan->computeAndSetSpriteSize(); + break; + + case 57: + _vm->_globals->_characterSpriteBuf = _vm->_fileIO->loadFile("PERSO.SPR"); + _vm->_globals->_characterType = CHARACTER_HOPKINS; + _vm->_globals->_saveData->_data[svAlternateSpriteFl] = 0; + _vm->_globals->loadCharacterData(); + _vm->_objectsMan->_sprite[0]._deltaX = 34; + _vm->_objectsMan->_sprite[0]._deltaY = 190; + _vm->_objectsMan->_sprite[0]._spriteData = _vm->_globals->_characterSpriteBuf; + _vm->_objectsMan->computeAndSetSpriteSize(); + break; + + case 58: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("Gm1.PE2"); + _vm->_globals->_saveData->_data[svField176] = 1; + _vm->_globals->_saveData->_data[svField270] = 2; + _vm->_globals->_introSpeechOffFl = false; + break; + + case 59: { + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_oldCharacterPosX = _vm->_objectsMan->getSpriteX(0); + _vm->_objectsMan->resetOldDirection(); + _vm->_objectsMan->resetHomeRateCounter(); + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 445, 332); + _vm->_globals->_checkDistanceFl = true; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(7); + _vm->_objectsMan->setBobAnimDataIdx(7, 0); + bool playFl = false; + _vm->_soundMan->loadSample(1, "SOUND40.WAV"); + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(7)) { + case 10: + case 18: + if (!playFl) { + _vm->_soundMan->playSample(1); + playFl = true; + } + break; + case 11: + playFl = false; + break; + case 19: + _vm->_objectsMan->setBobAnimation(3); + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(3) == 48) + break; + } + _vm->_soundMan->removeSample(1); + _vm->_objectsMan->setSpriteIndex(0, 62); + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->setBobAnimation(6); + _vm->_objectsMan->stopBobAnimation(7); + _vm->_objectsMan->stopBobAnimation(3); + break; + } + + case 62: + _vm->_talkMan->animateObject("SBCADA.pe2"); + break; + + case 65: + _vm->_talkMan->animateObject("ScCADA.pe2"); + break; + + case 80: { + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(12); + _vm->_objectsMan->setBobAnimation(13); + _vm->_objectsMan->setBobAnimDataIdx(12, 0); + _vm->_objectsMan->setBobAnimDataIdx(13, 0); + _vm->_soundMan->loadWav("SOUND44.WAV", 1); + _vm->_soundMan->loadWav("SOUND71.WAV", 2); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(12)) { + case 4: + if (!playFl) { + _vm->_soundMan->playWav(1); + playFl = true; + } + break; + case 5: + playFl = false; + break; + } + + switch (_vm->_objectsMan->getBobAnimDataIdx(4)) { + case 5: + if (!playFl) { + _vm->_soundMan->playWav(2); + playFl = true; + } + break; + case 6: + playFl = false; + break; + } + + if (_vm->_objectsMan->getBobAnimDataIdx(13) == 8) { + _vm->_objectsMan->stopBobAnimation(13); + _vm->_objectsMan->stopBobAnimation(3); + _vm->_objectsMan->setBobAnimation(4); + _vm->_objectsMan->setBobAnimDataIdx(4, 0); + _vm->_objectsMan->setBobAnimDataIdx(13, 0); + } + + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(4) == 16) + break; + } + _vm->_objectsMan->stopBobAnimation(12); + _vm->_objectsMan->stopBobAnimation(4); + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->loadLinkFile("IM27a", true); + break; + } + + case 81: { + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_oldCharacterPosX = _vm->_objectsMan->getSpriteX(0); + _vm->_objectsMan->resetOldDirection(); + _vm->_objectsMan->resetHomeRateCounter(); + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 119, 268); + _vm->_globals->_checkDistanceFl = true; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(11); + _vm->_objectsMan->setBobAnimation(8); + _vm->_objectsMan->setBobAnimDataIdx(11, 0); + _vm->_objectsMan->setBobAnimDataIdx(8, 0); + _vm->_soundMan->loadWav("SOUND44.WAV", 1); + _vm->_soundMan->loadWav("SOUND48.WAV", 2); + _vm->_soundMan->loadWav("SOUND49.WAV", 3); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(11)) { + case 4: + if (!playFl) { + _vm->_soundMan->playWav(1); + playFl = true; + } + break; + case 5: + playFl = false; + break; + } + + switch (_vm->_objectsMan->getBobAnimDataIdx(8)) { + case 11: + if (!playFl) { + _vm->_soundMan->playWav(2); + playFl = true; + } + break; + case 12: + playFl = false; + break; + } + + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(8) == 32) + break; + } + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 201, 14, 1); + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->stopBobAnimation(11); + _vm->_objectsMan->stopBobAnimation(8); + _vm->_objectsMan->setBobAnimation(5); + _vm->_objectsMan->setBobAnimation(6); + _vm->_objectsMan->setBobAnimDataIdx(5, 0); + _vm->_objectsMan->setBobAnimDataIdx(6, 0); + _vm->_soundMan->playWav(3); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(5) != 74); + _vm->_objectsMan->stopBobAnimation(5); + _vm->_objectsMan->stopBobAnimation(6); + _vm->_objectsMan->setBobAnimation(9); + _vm->_objectsMan->setBobAnimation(7); + break; + } + + case 83: + _vm->_talkMan->startAnimatedCharacterDialogue("CVIGIL.pe2"); + break; + + case 84: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("CVIGIL1.PE2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 85: + _vm->_objectsMan->stopBobAnimation(3); + _vm->_objectsMan->setBobAnimation(5); + _vm->_objectsMan->setBobAnimDataIdx(5, 0); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(5) != 6); + _vm->_objectsMan->stopBobAnimation(5); + _vm->_objectsMan->setBobAnimation(6); + _vm->_objectsMan->loadLinkFile("IM24a", true); + break; + + case 86: + if (_vm->_globals->_saveData->_data[svField231] == 1) { + _vm->_talkMan->startAnimatedCharacterDialogue("chotess1.pe2"); + } else { + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("chotesse.pe2"); + _vm->_globals->_introSpeechOffFl = false; + } + break; + + case 87: + if (_vm->_globals->_saveData->_data[svField188]) + _vm->_talkMan->startAnimatedCharacterDialogue("stand2.pe2"); + else + _vm->_talkMan->startAnimatedCharacterDialogue("stand1.pe2"); + break; + + case 88: + // Shooting target - Shooting at target + if (_vm->_globals->_saveData->_data[svField183] == 1) { + _vm->_objectsMan->setBobAnimDataIdx(1, 0); + _vm->_objectsMan->setBobAnimDataIdx(2, 0); + _vm->_objectsMan->setBobAnimation(1); + _vm->_objectsMan->setBobAnimation(2); + _vm->_soundMan->loadSample(1, "SOUND40.WAV"); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(1)) { + case 1: + case 3: + case 5: + case 7: + if (!playFl) { + _vm->_soundMan->playSample(1); + playFl = true; + } + break; + case 2: + case 4: + case 6: + case 8: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(1) == 9) + break; + } + _vm->_objectsMan->stopBobAnimation(1); + _vm->_objectsMan->stopBobAnimation(2); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 283, 160, 6); + _vm->_soundMan->removeSample(1); + } + if (_vm->_globals->_saveData->_data[svField183] == 2) { + _vm->_objectsMan->setBobAnimDataIdx(1, 0); + _vm->_objectsMan->setBobAnimDataIdx(3, 0); + _vm->_objectsMan->setBobAnimation(1); + _vm->_objectsMan->setBobAnimation(3); + _vm->_soundMan->loadSample(1, "SOUND40.WAV"); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(1)) { + case 1: + case 3: + case 5: + case 7: + if (!playFl) { + _vm->_soundMan->playSample(1); + playFl = true; + } + break; + case 2: + case 4: + case 6: + case 8: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(1) == 9) + break; + } + _vm->_objectsMan->stopBobAnimation(1); + _vm->_objectsMan->stopBobAnimation(3); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 283, 161, 8); + _vm->_soundMan->removeSample(1); + } + break; + + case 90: + // Shooting target - Using the level + _vm->_soundMan->playSoundFile("SOUND52.WAV"); + if (!_vm->_globals->_saveData->_data[svField186]) { + _vm->_animMan->playSequence("CIB5A.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 0); + } else if (_vm->_globals->_saveData->_data[svField186] == 1) { + _vm->_animMan->playSequence("CIB5C.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 0); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 329, 87, 2); + } + break; + + case 91: + _vm->_soundMan->playSoundFile("SOUND52.WAV"); + if (!_vm->_globals->_saveData->_data[svField186]) { + _vm->_animMan->playSequence("CIB5B.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 5); + } else if (_vm->_globals->_saveData->_data[svField186] == 1) { + _vm->_animMan->playSequence("CIB5D.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 5); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 283, 160, 6); + } + break; + + case 92: + _vm->_soundMan->playSoundFile("SOUND52.WAV"); + if (!_vm->_globals->_saveData->_data[svField184]) { + _vm->_animMan->playSequence("CIB6A.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 0); + } else if (_vm->_globals->_saveData->_data[svField184] == 1) { + _vm->_animMan->playSequence("CIB6C.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 0); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 293, 139, 3); + } + break; + + case 93: + _vm->_soundMan->playSoundFile("SOUND52.WAV"); + if (!_vm->_globals->_saveData->_data[svField184]) { + _vm->_animMan->playSequence("CIB6B.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 5); + } else if (_vm->_globals->_saveData->_data[svField184] == 1) { + _vm->_animMan->playSequence("CIB6D.SEQ", 1, 12, 1, false, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 155, 29, 5); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 283, 161, 8); + } + break; + + case 94: + if (!_vm->_globals->_saveData->_data[svField228]) + _vm->_talkMan->startAnimatedCharacterDialogue("flicn.pe2"); + else if (_vm->_globals->_saveData->_data[svField228] == 1) + _vm->_talkMan->startAnimatedCharacterDialogue("flicn1.pe2"); + break; + + case 95: + _vm->_objectsMan->setBobAnimation(9); + _vm->_objectsMan->setBobAnimation(10); + _vm->_objectsMan->setBobAnimation(12); + _vm->_objectsMan->setBobAnimDataIdx(9, 0); + _vm->_objectsMan->setBobAnimDataIdx(10, 0); + _vm->_objectsMan->setBobAnimDataIdx(12, 0); + _vm->_objectsMan->removeSprite(0); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(9) != 15); + _vm->_objectsMan->stopBobAnimation(9); + _vm->_objectsMan->animateSprite(0); + _vm->_soundMan->playSoundFile("SOUND50.WAV"); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(12) != 117); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 830, 122, 0); + _vm->_objectsMan->stopBobAnimation(12); + _vm->_objectsMan->stopBobAnimation(10); + _vm->_objectsMan->setBobAnimation(11); + break; + + case 98: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("CVIGIL2.PE2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 100: + _vm->_talkMan->startAnimatedCharacterDialogue("tourist.pe2"); + break; + + case 101: + _vm->_talkMan->startAnimatedCharacterDialogue("tahi1.pe2"); + break; + + case 103: + // Dice game + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("tourist1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + _vm->_animMan->playAnim2("T421A.ANM", "T421.ANM", 100, 14, 500); + _vm->_events->refreshScreenAndEvents(); + _vm->_events->refreshScreenAndEvents(); + _vm->_events->refreshScreenAndEvents(); + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("tourist2.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 104: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("tourist3.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 105: + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_oldCharacterPosX = _vm->_objectsMan->getSpriteX(0); + _vm->_objectsMan->resetOldDirection(); + _vm->_objectsMan->resetHomeRateCounter(); + _vm->_linesMan->_route = (RouteItem *)NULL; + switch (_vm->_globals->_saveData->_data[svField253]) { + case 1: + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 201, 294); + break; + case 2: + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 158, 338); + break; + default: + if (_vm->_globals->_saveData->_data[svField253] > 2) + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 211, 393); + break; + } + _vm->_globals->_checkDistanceFl = true; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setSpriteIndex(0, 60); + _vm->_soundMan->loadSample(1, "SOUND63.WAV"); + if (_vm->_globals->_saveData->_data[svField253] > 2) { + _vm->_objectsMan->setBobAnimation(4); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(4)) { + case 9: + case 32: + case 55: + if (!playFl) { + _vm->_soundMan->playSample(1); + playFl = true; + } + break; + case 10: + case 33: + case 56: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(4) == 72) + break; + } + _vm->_objectsMan->stopBobAnimation(4); + } + if (_vm->_globals->_saveData->_data[svField253] == 1) { + _vm->_objectsMan->setBobAnimation(6); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(6)) { + case 9: + case 32: + case 55: + if (!playFl) { + _vm->_soundMan->playSample(1); + playFl = true; + } + break; + case 10: + case 33: + case 56: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(6) == 72) + break; + } + _vm->_objectsMan->stopBobAnimation(6); + } + if (_vm->_globals->_saveData->_data[svField253] == 2) { + _vm->_objectsMan->setBobAnimation(5); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(5)) { + case 9: + case 32: + case 55: + if (!playFl) { + _vm->_soundMan->playSample(1); + playFl = true; + } + break; + case 10: + case 33: + case 56: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(5) == 72) + break; + } + _vm->_objectsMan->stopBobAnimation(5); + } + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->doActionBack(1); + _vm->_soundMan->removeSample(1); + break; + + case 106: + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(4); + _vm->_objectsMan->setBobAnimDataIdx(4, 0); + _vm->_soundMan->loadWav("SOUND61.WAV", 1); + _vm->_soundMan->loadWav("SOUND62.WAV", 2); + _vm->_soundMan->loadWav("SOUND61.WAV", 3); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(4) != 10); + _vm->_soundMan->playWav(1); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(4) != 18); + _vm->_soundMan->playWav(2); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(4) != 62); + _vm->_soundMan->playWav(3); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(4) != 77); + _vm->_objectsMan->stopBobAnimation(4); + _vm->_objectsMan->animateSprite(0); + break; + + case 107: + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(5); + _vm->_objectsMan->setBobAnimDataIdx(5, 0); + _vm->_soundMan->loadWav("SOUND61.WAV", 1); + _vm->_soundMan->loadWav("SOUND62.WAV", 2); + _vm->_soundMan->loadWav("SOUND61.WAV", 3); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(5) != 10); + _vm->_soundMan->playWav(1); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(5) != 18); + _vm->_soundMan->playWav(2); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(5) != 38); + _vm->_soundMan->playWav(3); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(5) != 53); + _vm->_objectsMan->stopBobAnimation(5); + _vm->_objectsMan->animateSprite(0); + break; + + case 108: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("peche1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 109: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("peche2.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 110: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("peche3.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 111: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("peche4.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 112: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("teint1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 113: + _vm->_talkMan->startAnimatedCharacterDialogue("teint.pe2"); + break; + + case 114: + _vm->_talkMan->startAnimatedCharacterDialogue("tahibar.pe2"); + break; + + case 115: + _vm->_talkMan->startAnimatedCharacterDialogue("ilebar.pe2"); + break; + + case 116: + _vm->_talkMan->startAnimatedCharacterDialogue("Profred.pe2"); + break; + + case 170: + _vm->_talkMan->startAnimatedCharacterDialogue("GRED.pe2"); + break; + + case 171: { + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("gred1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_oldCharacterPosX = _vm->_objectsMan->getSpriteX(0); + _vm->_objectsMan->resetOldDirection(); + _vm->_objectsMan->resetHomeRateCounter(); + _vm->_globals->_checkDistanceFl = true; + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 361, 325); + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_zoneNum = -1; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_globals->_exitId = 59; + break; + } + + case 172: + _vm->_talkMan->startAnimatedCharacterDialogue("GBLEU.pe2"); + break; + + case 173: { + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("gbleu1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_oldCharacterPosX = _vm->_objectsMan->getSpriteX(0); + _vm->_objectsMan->resetOldDirection(); + _vm->_objectsMan->resetHomeRateCounter(); + _vm->_globals->_checkDistanceFl = true; + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 361, 325); + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_zoneNum = -1; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_globals->_exitId = 59; + break; + } + + case 174: + _vm->_talkMan->startAnimatedCharacterDialogue("Profbl.pe2"); + break; + + case 175: + _vm->_objectsMan->setSpriteIndex(0, 55); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(9); + _vm->_objectsMan->setBobAnimation(10); + _vm->_objectsMan->setBobOffset(10, 300); + _vm->_soundMan->playSoundFile("SOUND44.WAV"); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(10) != 7); + _vm->_objectsMan->setBobAnimation(6); + _vm->_objectsMan->stopBobAnimation(3); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(6) != 10); + _vm->_soundMan->playSoundFile("SOUND71.WAV"); + _vm->_objectsMan->setBobAnimation(7); + _vm->_objectsMan->stopBobAnimation(4); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(7) != 15); + _vm->_objectsMan->stopBobAnimation(5); + _vm->_objectsMan->setBobAnimation(8); + _vm->_soundMan->playSoundFile("SOUND70.WAV"); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(8) != 76); + _vm->_objectsMan->stopBobAnimation(6); + _vm->_objectsMan->stopBobAnimation(7); + _vm->_objectsMan->stopBobAnimation(8); + _vm->_objectsMan->stopBobAnimation(9); + _vm->_objectsMan->stopBobAnimation(10); + _vm->_objectsMan->animateSprite(0); + break; + + case 176: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("gred2.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 177: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("gbleu2.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 200: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("Gm2.PE2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 201: + _vm->_objectsMan->setBobAnimation(3); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(3) != 18); + _vm->_objectsMan->stopBobAnimation(3); + _vm->_objectsMan->setBobAnimation(4); + break; + + case 202: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("SVGARD2.PE2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 203: + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(4); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(4) == 18) + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 18, 334, 0, false); + } while (_vm->_objectsMan->getBobAnimDataIdx(4) != 26); + _vm->_objectsMan->stopBobAnimation(4); + _vm->_objectsMan->animateSprite(0); + break; + + case 204: { + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(3); + _vm->_soundMan->loadWav("SOUND67.WAV", 1); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(3)) { + case 10: + if (!playFl) { + _vm->_soundMan->playWav(1); + playFl = true; + } + break; + case 11: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(3) == 50) + break; + } + _vm->_objectsMan->stopBobAnimation(3); + _vm->_objectsMan->animateSprite(0); + break; + } + + case 205: { + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(4); + _vm->_soundMan->loadWav("SOUND69.WAV", 1); + bool playFl = false; + for (;;) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + switch (_vm->_objectsMan->getBobAnimDataIdx(4)) { + case 10: + if (!playFl) { + _vm->_soundMan->playWav(1); + playFl = true; + } + break; + case 11: + playFl = false; + break; + } + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(4) == 24) + break; + } + _vm->_objectsMan->stopBobAnimation(4); + _vm->_objectsMan->animateSprite(0); + break; + } + + case 207: + _vm->_talkMan->animateObject("PANNEAU.PE2"); + break; + + case 208: { + _vm->_globals->_disableInventFl = true; + if (_vm->_globals->_saveData->_data[svLastPrevScreenId] != _vm->_globals->_saveData->_data[svField401]) { + _vm->_soundMan->_specialSoundNum = 208; + _vm->_animMan->playSequence("SORT.SEQ", 10, 4, 10, true, false); + _vm->_soundMan->_specialSoundNum = 0; + } + _vm->_globals->_checkDistanceFl = true; + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 330, 418); + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_zoneNum = 0; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_objectsMan->setSpriteIndex(0, 64); + _vm->_globals->_exitId = _vm->_globals->_saveData->_data[svField401]; + _vm->_globals->_disableInventFl = false; + break; + } + + case 209: { + _vm->_objectsMan->setBobAnimDataIdx(1, 0); + _vm->_objectsMan->setBobAnimDataIdx(2, 0); + _vm->_objectsMan->setSpriteIndex(0, 60); + _vm->_objectsMan->stopBobAnimation(4); + _vm->_objectsMan->setBobAnimation(1); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(1) != 9); + _vm->_objectsMan->stopBobAnimation(1); + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_globals->_checkDistanceFl = true; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 330, 314); + _vm->_objectsMan->_zoneNum = 0; + _vm->_globals->_checkDistanceFl = true; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_objectsMan->setSpriteIndex(0, 64); + _vm->_objectsMan->setBobAnimation(2); + _vm->_soundMan->playSoundFile("SOUND66.WAV"); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(2) != 10); + _vm->_objectsMan->stopBobAnimation(2); + _vm->_objectsMan->setBobAnimation(4); + break; + } + + case 210: + _vm->_soundMan->_specialSoundNum = 210; + _vm->_animMan->playSequence2("SECRET1.SEQ", 1, 12, 1, true); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 192, 152, 0); + _vm->_objectsMan->setBobAnimation(9); + _vm->_objectsMan->loadLinkFile("IM73a", true); + _vm->_objectsMan->enableHidingBehavior(); + _vm->_objectsMan->setHidingUseCount(0); + _vm->_objectsMan->setHidingUseCount(1); + _vm->_graphicsMan->setColorPercentage2(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage2(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage2(254, 0, 0, 0); + break; + + case 211: + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->disableHidingBehavior(); + _vm->_soundMan->_specialSoundNum = 211; + _vm->_animMan->playSequence("SECRET2.SEQ", 1, 12, 100, false, true); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_graphicsMan->_noFadingFl = true; + _vm->_graphicsMan->fadeOutShort(); + + for (int i = 1; i <= 39; i++) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } + + _vm->_graphicsMan->setColorPercentage2(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage2(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage2(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage2(254, 0, 0, 0); + break; + + case 215: + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("aviat.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 216: + // Discuss with pilot just before Flight cutscene + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("aviat1.pe2"); + _vm->_globals->_introSpeechOffFl = false; + break; + + case 229: + _vm->_soundMan->_specialSoundNum = 229; + _vm->_animMan->playSequence("MUR.SEQ", 1, 12, 1, false, false); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 340, 157, 2); + break; + + case 230: { + _vm->_objectsMan->loadLinkFile("IM93a", true); + _vm->_objectsMan->enableHidingBehavior(); + _vm->_globals->_checkDistanceFl = true; + _vm->_objectsMan->_oldCharacterPosX = _vm->_objectsMan->getSpriteX(0); + _vm->_objectsMan->resetOldDirection(); + _vm->_objectsMan->resetHomeRateCounter(); + _vm->_globals->_checkDistanceFl = true; + _vm->_linesMan->_route = (RouteItem *)NULL; + _vm->_linesMan->_route = _vm->_linesMan->findRoute(_vm->_objectsMan->getSpriteX(0), _vm->_objectsMan->getSpriteY(0), 488, 280); + _vm->_globals->_checkDistanceFl = true; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_objectsMan->goHome(); + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_linesMan->_route != (RouteItem *)NULL); + _vm->_objectsMan->removeSprite(0); + bool playFl = false; + _vm->_objectsMan->setBobAnimation(7); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + if (_vm->_objectsMan->getBobAnimDataIdx(7) == 9 && !playFl) { + playFl = true; + _vm->_soundMan->playSoundFile("SOUND81.WAV"); + } + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(7) != 15); + _vm->_objectsMan->stopBobAnimation(7); + _vm->_objectsMan->setSpriteX(0, 476); + _vm->_objectsMan->setSpriteY(0, 278); + _vm->_objectsMan->animateSprite(0); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 337, 154, 3); + _vm->_objectsMan->loadLinkFile("IM93c", true); + _vm->_objectsMan->enableHidingBehavior(); + break; + } + + case 231: + _vm->_objectsMan->disableHidingBehavior(); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(12); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(12) != 6); + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("PRMORT.pe2"); + _vm->_globals->_introSpeechOffFl = false; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(12) != 12); + _vm->_objectsMan->animateSprite(0); + _vm->_objectsMan->stopBobAnimation(12); + _vm->_objectsMan->enableHidingBehavior(); + break; + + case 233: { + _vm->_objectsMan->disableHidingBehavior(); + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setBobAnimation(11); + bool playFl = false; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + if (_vm->_objectsMan->getBobAnimDataIdx(11) == 10 && !playFl) + playFl = true; + } while (_vm->_objectsMan->getBobAnimDataIdx(11) != 13); + _vm->_objectsMan->stopBobAnimation(11); + _vm->_objectsMan->enableHidingBehavior(); + _vm->_objectsMan->setBobAnimation(13); + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } while (_vm->_objectsMan->getBobAnimDataIdx(13) != 48); + _vm->_globals->_introSpeechOffFl = true; + _vm->_talkMan->startAnimatedCharacterDialogue("HRADIO.PE2"); + _vm->_globals->_introSpeechOffFl = false; + _vm->_graphicsMan->fadeOutLong(); + _vm->_objectsMan->stopBobAnimation(13); + _vm->_graphicsMan->_noFadingFl = true; + _vm->_globals->_exitId = 94; + break; + } + + case 236: { + if (_vm->_globals->_saveData->_data[svField341]) { + switch (_vm->_globals->_saveData->_data[svField341]) { + case 1: + vbobFrameIndex = 6; + break; + case 2: + vbobFrameIndex = 5; + break; + case 3: + vbobFrameIndex = 4; + break; + } + _vm->_soundMan->playSoundFile("SOUND83.WAV"); + _vm->_objectsMan->setAndPlayAnim(vbobFrameIndex, 26, 50, false); + + switch (_vm->_globals->_saveData->_data[svField341]) { + case 1: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 27, 117, 0); + _vm->_globals->_saveData->_data[svField338] = 0; + break; + case 2: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 145, 166, 2); + _vm->_globals->_saveData->_data[svField339] = 0; + break; + case 3: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 296, 212, 4); + _vm->_globals->_saveData->_data[svField340] = 0; + break; + } + } + _vm->_soundMan->playSoundFile("SOUND83.WAV"); + _vm->_objectsMan->setAndPlayAnim(6, 0, 23, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 27, 117, 1); + break; + } + + case 237: { + switch (_vm->_globals->_saveData->_data[svField341]) { + case 1: + vbobFrameIndex = 6; + break; + case 2: + vbobFrameIndex = 5; + break; + case 3: + vbobFrameIndex = 4; + break; + } + + if (_vm->_globals->_saveData->_data[svField341]) { + _vm->_soundMan->playSoundFile("SOUND83.WAV"); + _vm->_objectsMan->setAndPlayAnim(vbobFrameIndex, 26, 50, false); + + switch (_vm->_globals->_saveData->_data[svField341]) { + case 1: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 27, 117, 0); + _vm->_globals->_saveData->_data[svField338] = 0; + break; + case 2: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 145, 166, 2); + _vm->_globals->_saveData->_data[svField339] = 0; + break; + case 3: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 296, 212, 4); + _vm->_globals->_saveData->_data[svField340] = 0; + break; + } + } + + _vm->_soundMan->playSoundFile("SOUND83.WAV"); + _vm->_objectsMan->setAndPlayAnim(5, 0, 23, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 145, 166, 3); + break; + } + + case 238: { + switch (_vm->_globals->_saveData->_data[svField341]) { + case 1: + vbobFrameIndex = 6; + break; + case 2: + vbobFrameIndex = 5; + break; + case 3: + vbobFrameIndex = 4; + break; + } + + if (_vm->_globals->_saveData->_data[svField341]) { + _vm->_soundMan->playSoundFile("SOUND83.WAV"); + _vm->_objectsMan->setAndPlayAnim(vbobFrameIndex, 26, 50, false); + switch (_vm->_globals->_saveData->_data[svField341]) { + case 1: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 27, 117, 0); + _vm->_globals->_saveData->_data[svField338] = 0; + break; + case 2: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 145, 166, 2); + _vm->_globals->_saveData->_data[svField339] = 0; + break; + case 3: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 296, 212, 4); + _vm->_globals->_saveData->_data[svField340] = 0; + break; + } + } + _vm->_soundMan->playSoundFile("SOUND83.WAV"); + _vm->_objectsMan->setAndPlayAnim(4, 0, 23, false); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 296, 212, 5); + break; + } + + case 239: + _vm->_objectsMan->removeSprite(0); + _vm->_soundMan->playSoundFile("SOUND84.WAV"); + _vm->_objectsMan->setAndPlayAnim(16, 0, 10, false); + break; + + case 240: { + _vm->_objectsMan->setBobAnimation(1); + bool soundFlag = false; + do { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + switch (_vm->_objectsMan->getBobAnimDataIdx(1)) { + case 12: + if (!soundFlag) { + _vm->_soundMan->playSoundFile("SOUND86.WAV"); + soundFlag = true; + } + break; + case 13: + // The original was starting then stopping sound at 25 + // It looked wrong so the check was set on 26 + case 26: + soundFlag = false; + break; + case 25: + if (!soundFlag) { + _vm->_soundMan->playSoundFile("SOUND85.WAV"); + soundFlag = true; + } + break; + } + } while (_vm->_objectsMan->getBobAnimDataIdx(1) != 32); + _vm->_objectsMan->stopBobAnimation(1); + _vm->_objectsMan->setBobAnimation(2); + _vm->_fontMan->hideText(9); + bool displayedTxtFl = false; + if (!_vm->_soundMan->_textOffFl) { + _vm->_fontMan->initTextBuffers(9, 617, _vm->_globals->_textFilename, 91, 41, 3, 30, 253); + _vm->_fontMan->showText(9); + displayedTxtFl = true; + } + if (!_vm->_soundMan->_voiceOffFl) + _vm->_soundMan->mixVoice(617, 4, displayedTxtFl); + for (int i = 0; i <= 29; i++) { + if (_vm->shouldQuit()) + return -1; // Exiting game + + _vm->_events->refreshScreenAndEvents(); + } + CharacterLocation *realHopkins = &_vm->_globals->_saveData->_realHopkins; + realHopkins->_pos.x = _vm->_objectsMan->getSpriteX(0); + realHopkins->_pos.y = _vm->_objectsMan->getSpriteY(0); + realHopkins->_startSpriteIndex = 57; + realHopkins->_location = 97; + _vm->_globals->_saveData->_data[svHopkinsCloneFl] = 1; + _vm->_globals->_saveData->_data[svField352] = 1; + _vm->_globals->_saveData->_data[svField353] = 1; + _vm->_globals->_saveData->_data[svField354] = 1; + break; + } + + case 241: + _vm->_talkMan->startAnimatedCharacterDialogue("RECEP.PE2"); + break; + + // Resurrect Samantha's clone + case 242: { + _vm->_soundMan->playSoundFile("SOUND87.WAV"); + _vm->_animMan->playSequence("RESUF.SEQ", 1, 24, 1, false, true); + + CharacterLocation *samantha = &_vm->_globals->_saveData->_samantha; + samantha->_pos.x = 404; + samantha->_pos.y = 395; + samantha->_startSpriteIndex = 64; + samantha->_location = _vm->_globals->_screenId; + samantha->_zoomFactor = -(100 * (67 - (100 - abs(_vm->_globals->_spriteSize[790 / 2]))) / 67); + + _vm->_globals->_saveData->_data[svField357] = 1; + _vm->_globals->_saveData->_data[svField354] = 0; + _vm->_globals->_saveData->_data[svField356] = 0; + _vm->_globals->_saveData->_data[svField355] = 1; + _vm->_objectsMan->_twoCharactersFl = true; + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 373, 191, 3); + _vm->_objectsMan->addStaticSprite(_vm->_objectsMan->_headSprites, samantha->_pos, 1, 3, samantha->_zoomFactor, false, 20, 127); + _vm->_objectsMan->animateSprite(1); + break; + } + + case 243: + _vm->_soundMan->playSoundFile("SOUND88.WAV"); + if (_vm->_globals->_saveData->_data[svField341] == 2) { + _vm->_animMan->playSequence("RESU.SEQ", 2, 24, 2, false, true); + } else { + _vm->_objectsMan->setAndPlayAnim(7, 0, 14, false); + } + break; + + case 245: + _vm->_soundMan->playSoundFile("SOUND89.WAV"); + _vm->_objectsMan->setAndPlayAnim(5, 0, 6, false); + _vm->_linesMan->_zone[4]._destX = 276; + _vm->_objectsMan->enableVerb(4, 19); + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 285, 379, 0); + _vm->_globals->_saveData->_data[svField399] = 1; + break; + + case 246: + _vm->_objectsMan->removeSprite(0); + _vm->_objectsMan->setAndPlayAnim(6, 0, 15, false); + _vm->_objectsMan->_charactersEnabledFl = true; + _vm->_graphicsMan->displayScreen(true); + _vm->_animMan->playSequence2("TUNNEL.SEQ", 1, 18, 20, true); + _vm->_graphicsMan->_noFadingFl = true; + _vm->_graphicsMan->fadeOutLong(); + _vm->_objectsMan->_charactersEnabledFl = false; + _vm->_globals->_exitId = 100; + break; + + case 600: + if (!_vm->getIsDemo()) { + _vm->_graphicsMan->_fadingFl = true; + _vm->_graphicsMan->_fadeDefaultSpeed = 1; + _vm->_animMan->playAnim("BOMBE1A.ANM", "BOMBE1.ANM", 100, 18, 100); + } + _vm->_graphicsMan->loadImage("BOMBEB"); + _vm->_graphicsMan->setColorPercentage(252, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(251, 100, 100, 100); + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + _vm->_graphicsMan->initScreen("BOMBE", 2, true); + _vm->_graphicsMan->fadeInShort(); + break; + + case 601: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 163, 7, false); + _vm->_objectsMan->setAndPlayAnim(2, 0, 16, true); + break; + + case 602: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 163, 7, false); + _vm->_objectsMan->setAndPlayAnim(4, 0, 16, true); + break; + + case 603: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 163, 7, false); + _vm->_objectsMan->setAndPlayAnim(3, 0, 16, true); + _vm->_soundMan->_specialSoundNum = 199; + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playAnim("BOMBE2A.ANM", "BOMBE2.ANM", 50, 14, 500); + _vm->_soundMan->_specialSoundNum = 0; + memset(_vm->_graphicsMan->_frontBuffer, 0, 614400); + _vm->_graphicsMan->_noFadingFl = true; + _vm->_globals->_exitId = 151; + break; + + case 604: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 163, 7, false); + _vm->_objectsMan->setAndPlayAnim(1, 0, 16, true); + _vm->_soundMan->_specialSoundNum = 199; + _vm->_animMan->playAnim("BOMBE2A.ANM", "BOMBE2.ANM", 50, 14, 500); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_graphicsMan->_noFadingFl = true; + memset(_vm->_graphicsMan->_frontBuffer, 0, 614400); + _vm->_globals->_exitId = 151; + break; + + case 605: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 163, 7, false); + _vm->_objectsMan->setAndPlayAnim(5, 0, 16, true); + _vm->_graphicsMan->fadeOutShort(); + _vm->_soundMan->_specialSoundNum = 199; + _vm->_graphicsMan->_fadingFl = true; + _vm->_animMan->playAnim("BOMBE2A.ANM", "BOMBE2.ANM", 50, 14, 500); + _vm->_soundMan->_specialSoundNum = 0; + _vm->_graphicsMan->_noFadingFl = true; + memset(_vm->_graphicsMan->_frontBuffer, 0, 614400); + _vm->_globals->_exitId = 151; + break; + + case 606: + _vm->_graphicsMan->fastDisplay(_vm->_globals->_levelSpriteBuf, 513, 163, 7, false); + _vm->_objectsMan->setAndPlayAnim(6, 0, 16, true); + if ((_vm->getPlatform() != Common::kPlatformWindows) || !_vm->getIsDemo()) { + _vm->_animMan->playAnim("BOMBE3A.ANM", "BOMBE3.ANM", 50, 14, 500); + memset(_vm->_graphicsMan->_frontBuffer, 0, 614400); + } + _vm->_globals->_exitId = 6; + break; + + case 607: + // Display bomb plan + if (!_vm->getIsDemo()) { + memcpy(_vm->_graphicsMan->_oldPalette, _vm->_graphicsMan->_palette, 769); + _vm->_animMan->playAnim2("PLAN.ANM", "PLAN.ANM", 50, 10, 800); + } + _vm->_graphicsMan->resetDirtyRects(); + break; + + case 608: + _vm->_objectsMan->stopBobAnimation(2); + _vm->_objectsMan->stopBobAnimation(3); + _vm->_objectsMan->stopBobAnimation(4); + _vm->_objectsMan->stopBobAnimation(6); + _vm->_objectsMan->stopBobAnimation(11); + _vm->_objectsMan->stopBobAnimation(10); + break; + + case 609: + _vm->_objectsMan->setBobAnimation(2); + _vm->_objectsMan->setBobAnimation(3); + _vm->_objectsMan->setBobAnimation(4); + _vm->_objectsMan->setBobAnimation(6); + _vm->_objectsMan->setBobAnimation(11); + _vm->_objectsMan->setBobAnimation(10); + break; + + case 610: + _vm->_objectsMan->stopBobAnimation(5); + _vm->_objectsMan->stopBobAnimation(7); + _vm->_objectsMan->stopBobAnimation(8); + _vm->_objectsMan->stopBobAnimation(9); + _vm->_objectsMan->stopBobAnimation(12); + _vm->_objectsMan->stopBobAnimation(13); + break; + + case 611: + _vm->_objectsMan->setBobAnimation(5); + _vm->_objectsMan->setBobAnimation(7); + _vm->_objectsMan->setBobAnimation(8); + _vm->_objectsMan->setBobAnimation(9); + _vm->_objectsMan->setBobAnimation(12); + _vm->_objectsMan->setBobAnimation(13); + break; + } + opcodeType = 1; + break; + case MKTAG24('E', 'I', 'F'): + opcodeType = 4; + break; + case MKTAG24('V', 'A', 'L'): { + opcodeType = 1; + int idx = READ_LE_INT16(dataP + 5); + assert(idx >= 0 && idx < 2050); + _vm->_globals->_saveData->_data[idx] = dataP[7]; + break; + } + case MKTAG24('A', 'D', 'D'): + opcodeType = 1; + _vm->_globals->_saveData->_data[READ_LE_INT16(dataP + 5)] += dataP[7]; + break; + case MKTAG24('B', 'O', 'S'): + opcodeType = 1; + _vm->_objectsMan->setBobOffset(READ_LE_INT16(dataP + 5), READ_LE_INT16(dataP + 7)); + break; + case MKTAG24('V', 'O', 'N'): + _vm->_objectsMan->enableVerb(READ_LE_INT16(dataP + 5), READ_LE_INT16(dataP + 7)); + opcodeType = 1; + break; + case MKTAG24('Z', 'C', 'H'): + _vm->_linesMan->_zone[READ_LE_INT16(dataP + 5)]._messageId = READ_LE_INT16(dataP + 7); + opcodeType = 1; + break; + case MKTAG24('J', 'U', 'M'): + _vm->_objectsMan->_jumpZone = READ_LE_INT16(dataP + 5); + _vm->_objectsMan->_jumpVerb = READ_LE_INT16(dataP + 7); + opcodeType = 6; + break; + case MKTAG24('S', 'O', 'U'): { + int soundNum = READ_LE_INT16(dataP + 5); + + Common::String file = Common::String::format("SOUND%d.WAV", soundNum); + _vm->_soundMan->playSoundFile(file); + opcodeType = 1; + break; + } + case MKTAG24('V', 'O', 'F'): + _vm->_objectsMan->disableVerb(READ_LE_INT16(dataP + 5), READ_LE_INT16(dataP + 7)); + opcodeType = 1; + break; + case MKTAG24('I', 'I', 'F'): + opcodeType = 3; + break; + default: + warning("Unhandled opcode %c%c%c", dataP[2], dataP[3], dataP[4]); + break; + } + + return opcodeType; +} + + +int ScriptManager::handleGoto(const byte *dataP) { + return READ_LE_INT16(dataP + 5); +} + +int ScriptManager::handleIf(const byte *dataP, int offset) { + int newOffset; + int curOffset = offset; + bool loopFl; + do { + loopFl = false; + int tmpOffset = curOffset; + int opcodeType; + do { + if (_vm->shouldQuit()) + return 0; // Exiting game + + ++tmpOffset; + if (tmpOffset > 400) + error("Control if failed"); + opcodeType = checkOpcode(dataP + 20 * tmpOffset); + } while (opcodeType != 4); // EIF + newOffset = tmpOffset; + tmpOffset = curOffset; + do { + if (_vm->shouldQuit()) + return 0; // Exiting game + + ++tmpOffset; + if (tmpOffset > 400) + error("Control if failed "); + if (checkOpcode(dataP + 20 * tmpOffset) == 3) { // IIF + curOffset = newOffset; + loopFl = true; + break; + } + } while (newOffset != tmpOffset); + } while (loopFl); + + const byte *buf = dataP + 20 * offset; + byte oper = buf[13]; + byte oper2 = buf[14]; + byte operType = buf[15]; + int saveDataIdx1 = READ_LE_INT16(buf + 5); + int compVal1 = READ_LE_INT16(buf + 7); + bool check1Fl = false; + if ((oper == 1 && _vm->_globals->_saveData->_data[saveDataIdx1] == compVal1) || + (oper == 2 && _vm->_globals->_saveData->_data[saveDataIdx1] != compVal1) || + (oper == 3 && _vm->_globals->_saveData->_data[saveDataIdx1] <= compVal1) || + (oper == 4 && _vm->_globals->_saveData->_data[saveDataIdx1] >= compVal1) || + (oper == 5 && _vm->_globals->_saveData->_data[saveDataIdx1] > compVal1) || + (oper == 6 && _vm->_globals->_saveData->_data[saveDataIdx1] < compVal1)) + check1Fl = true; + + bool check2Fl = false; + if (operType != 3) { + int saveDataIdx2 = READ_LE_INT16(buf + 9); + int compVal2 = READ_LE_INT16(buf + 11); + if ((oper2 == 1 && compVal2 == _vm->_globals->_saveData->_data[saveDataIdx2]) || + (oper2 == 2 && compVal2 != _vm->_globals->_saveData->_data[saveDataIdx2]) || + (oper2 == 3 && compVal2 >= _vm->_globals->_saveData->_data[saveDataIdx2]) || + (oper2 == 4 && compVal2 <= _vm->_globals->_saveData->_data[saveDataIdx2]) || + (oper2 == 5 && compVal2 < _vm->_globals->_saveData->_data[saveDataIdx2]) || + (oper2 == 6 && compVal2 > _vm->_globals->_saveData->_data[saveDataIdx2])) + check2Fl = true; + } + + if ((operType == 3) && check1Fl) { + return (offset + 1); + } else if ((operType == 1) && check1Fl && check2Fl) { + return (offset + 1); + } else if ((operType == 2) && (check1Fl || check2Fl)) { + return (offset + 1); + } + + return (newOffset + 1); +} + +int ScriptManager::checkOpcode(const byte *dataP) { + int result = 0; + if (READ_BE_UINT16(dataP) != MKTAG16('F', 'C')) + return result; + + uint32 signature24 = READ_BE_UINT24(&dataP[2]); + switch (signature24) { + case MKTAG24('A', 'N', 'I'): + case MKTAG24('B', 'C', 'A'): + case MKTAG24('B', 'O', 'B'): + case MKTAG24('B', 'O', 'F'): + case MKTAG24('B', 'O', 'S'): + case MKTAG24('M', 'U', 'S'): + case MKTAG24('O', 'B', 'M'): + case MKTAG24('O', 'B', 'P'): + case MKTAG24('P', 'E', 'R'): + case MKTAG24('S', 'O', 'U'): + case MKTAG24('S', 'P', 'E'): + case MKTAG24('T', 'X', 'T'): + case MKTAG24('V', 'A', 'L'): + case MKTAG24('V', 'O', 'F'): + case MKTAG24('V', 'O', 'N'): + case MKTAG24('Z', 'C', 'H'): + case MKTAG24('Z', 'O', 'F'): + case MKTAG24('Z', 'O', 'N'): + result = 1; + break; + case MKTAG24('G', 'O', 'T'): + result = 2; + break; + case MKTAG24('I', 'I', 'F'): + result = 3; + break; + case MKTAG24('E', 'I', 'F'): + result = 4; + break; + case MKTAG24('E', 'X', 'I'): + case MKTAG24('S', 'O', 'R'): + result = 5; + break; + case MKTAG24('J', 'U', 'M'): + result = 6; + break; +// default: +// warning("Unhandled opcode %c%c%c", dataP[2], dataP[3], dataP[4]); + } + return result; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/script.h b/engines/hopkins/script.h new file mode 100644 index 0000000000..2a22e18ccb --- /dev/null +++ b/engines/hopkins/script.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +#ifndef HOPKINS_SCRIPT_H +#define HOPKINS_SCRIPT_H + +#include "hopkins/globals.h" + +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/str.h" + +namespace Hopkins { + +class ScriptManager { +private: + HopkinsEngine *_vm; + int checkOpcode(const byte *dataP); +public: + bool _tempObjectFl; + + ScriptManager(HopkinsEngine *vm); + + int handleOpcode(const byte *dataP); + int handleIf(const byte *dataP, int offset); + int handleGoto(const byte *dataP); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_SCRIPT_H */ diff --git a/engines/hopkins/sound.cpp b/engines/hopkins/sound.cpp new file mode 100644 index 0000000000..bf816c08a4 --- /dev/null +++ b/engines/hopkins/sound.cpp @@ -0,0 +1,905 @@ +/* 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 "hopkins/sound.h" + +#include "hopkins/globals.h" +#include "hopkins/hopkins.h" + +#include "audio/decoders/adpcm_intern.h" +#include "common/system.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/textconsole.h" +#include "audio/audiostream.h" +#include "audio/mods/module.h" +#include "audio/mods/protracker.h" +#include "audio/decoders/raw.h" + +namespace Hopkins { + +class APC_ADPCMStream : public Audio::DVI_ADPCMStream { +public: + APC_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) : DVI_ADPCMStream(stream, disposeAfterUse, stream->size(), rate, channels, 0) { + stream->seek(-12, SEEK_CUR); + _status.ima_ch[0].last = _startValue[0] = stream->readUint32LE(); + _status.ima_ch[1].last = _startValue[1] = stream->readUint32LE(); + stream->seek(4, SEEK_CUR); + } + + void reset() { + DVI_ADPCMStream::reset(); + _status.ima_ch[0].last = _startValue[0]; + _status.ima_ch[1].last = _startValue[1]; + } + +private: + int16 _startValue[2]; +}; + +Audio::RewindableAudioStream *makeAPCStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + if (stream->readUint32BE() != MKTAG('C', 'R', 'Y', 'O')) + return 0; + if (stream->readUint32BE() != MKTAG('_', 'A', 'P', 'C')) + return 0; + stream->readUint32BE(); // version + stream->readUint32LE(); // out size + uint32 rate = stream->readUint32LE(); + stream->skip(8); // initial values, will be handled by the class + bool stereo = stream->readUint32LE() != 0; + + return new APC_ADPCMStream(stream, disposeAfterUse, rate, stereo ? 2 : 1); +} + +class TwaAudioStream : public Audio::AudioStream { +public: + TwaAudioStream(Common::String name, Common::SeekableReadStream *stream) { + _name = name; + _cueSheet.clear(); + _cueStream = NULL; + _cue = 0; + _loadedCue = -1; + + for (;;) { + char buf[3]; + stream->read(buf, 3); + + if (buf[0] == 'x' || stream->eos()) + break; + + _cueSheet.push_back(atol(buf)); + } + + for (_cue = 0; _cue < _cueSheet.size(); _cue++) { + if (loadCue(_cue)) + break; + } + } + + ~TwaAudioStream() { + delete _cueStream; + _cueStream = NULL; + } + + virtual bool isStereo() const { + return _cueStream ? _cueStream->isStereo() : true; + } + + virtual int getRate() const { + return _cueStream ? _cueStream->getRate() : 22050; + } + + virtual bool endOfData() const { + return _cueStream == NULL; + } + + virtual int readBuffer(int16 *buffer, const int numSamples) { + if (!_cueStream) + return 0; + + int16 *buf = buffer; + int samplesLeft = numSamples; + + while (samplesLeft) { + if (_cueStream) { + int readSamples = _cueStream->readBuffer(buf, samplesLeft); + buf += readSamples; + samplesLeft -= readSamples; + } + + if (samplesLeft > 0) { + if (++_cue >= _cueSheet.size()) { + _cue = 0; + } + loadCue(_cue); + } + } + + return numSamples; + } + +protected: + bool loadCue(int nr) { + if (_loadedCue == _cueSheet[nr]) { + _cueStream->rewind(); + return true; + } + + delete _cueStream; + _cueStream = NULL; + _loadedCue = _cueSheet[nr]; + + Common::String filename = Common::String::format("%s_%02d", _name.c_str(), _cueSheet[nr]); + Common::File *file = new Common::File(); + + if (file->open(filename + ".APC")) { + _cueStream = makeAPCStream(file, DisposeAfterUse::YES); + return true; + } + + if (file->open(filename + ".WAV")) { + _cueStream = Audio::makeWAVStream(file, DisposeAfterUse::YES); + return true; + } + + if (file->open(filename + ".RAW")) { + _cueStream = Audio::makeRawStream(file, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES); + return true; + } + + warning("TwaAudioStream::loadCue: Missing cue %d (%s)", nr, filename.c_str()); + _loadedCue = -1; + delete file; + return false; + } + +private: + Common::String _name; + Common::Array<int> _cueSheet; + Audio::RewindableAudioStream *_cueStream; + uint _cue; + int _loadedCue; +}; + +Audio::AudioStream *makeTwaStream(Common::String name, Common::SeekableReadStream *stream) { + return new TwaAudioStream(name, stream); +} + +SoundManager::SoundManager(HopkinsEngine *vm) { + _vm = vm; + + _specialSoundNum = 0; + _soundVolume = 0; + _voiceVolume = 0; + _musicVolume = 0; + _soundOffFl = true; + _musicOffFl = true; + _voiceOffFl = true; + _textOffFl = false; + _soundFl = false; + _skipRefreshFl = false; + _currentSoundIndex = 0; + _oldSoundNumber = 0; + _modPlayingFl = false; + + for (int i = 0; i < VOICE_COUNT; ++i) + Common::fill((byte *)&_voice[i], (byte *)&_voice[i] + sizeof(VoiceItem), 0); + for (int i = 0; i < SWAV_COUNT; ++i) + Common::fill((byte *)&_sWav[i], (byte *)&_sWav[i] + sizeof(SwavItem), 0); + for (int i = 0; i < SOUND_COUNT; ++i) + Common::fill((byte *)&_sound[i], (byte *)&_sound[i] + sizeof(SoundItem), 0); + Common::fill((byte *)&_music, (byte *)&_music + sizeof(MusicItem), 0); +} + +SoundManager::~SoundManager() { + stopMusic(); + delMusic(); + _vm->_mixer->stopHandle(_musicHandle); + _modPlayingFl = false; +} + +void SoundManager::checkSoundEnd() { + if (!_soundOffFl && _soundFl) { + if (!checkVoiceStatus(1)) { + stopVoice(1); + delWav(_currentSoundIndex); + } + } +} + +void SoundManager::loadAnimSound() { + switch (_specialSoundNum) { + case 2: + loadSample(5, "mitra1.wav"); + loadSample(1, "tir2.wav"); + loadSample(2, "sound6.wav"); + loadSample(3, "sound5.WAV"); + loadSample(4, "sound4.WAV"); + break; + case 5: + loadWav("CRIE.WAV", 1); + break; + case 14: + loadWav("SOUND14.WAV", 1); + break; + case 16: + loadWav("SOUND16.WAV", 1); + break; + case 198: + loadWav("SOUND3.WAV", 1); + break; + case 199: + loadWav("SOUND22.WAV", 1); + break; + case 200: + mixVoice(682, 1); + break; + case 208: + loadWav("SOUND77.WAV", 1); + break; + case 210: + loadWav("SOUND78.WAV", 1); + break; + case 211: + loadWav("SOUND78.WAV", 1); + break; + case 229: + loadWav("SOUND80.WAV", 1); + loadWav("SOUND82.WAV", 2); + break; + } +} + +void SoundManager::playAnimSound(int soundNumber) { + if (!_vm->_globals->_censorshipFl && _specialSoundNum == 2) { + switch (soundNumber) { + case 20: + playSample(5); + break; + case 57: + case 63: + case 69: + playSample(1); + break; + case 75: + playSample(2); + break; + case 109: + playSample(3); + break; + case 122: + playSample(4); + break; + } + } else if (_specialSoundNum == 1 && soundNumber == 17) + playSoundFile("SOUND42.WAV"); + else if (_specialSoundNum == 5 && soundNumber == 19) + playWav(1); + else if (_specialSoundNum == 14 && soundNumber == 625) + playWav(1); + else if (_specialSoundNum == 16 && soundNumber == 25) + playWav(1); + else if (_specialSoundNum == 17) { + if (soundNumber == 6) + playSample(1); + else if (soundNumber == 14) + playSample(2); + else if (soundNumber == 67) + playSample(3); + } else if (_specialSoundNum == 198 && soundNumber == 15) + playWav(1); + else if (_specialSoundNum == 199 && soundNumber == 72) + playWav(1); + else if (_specialSoundNum == 208 && soundNumber == 40) + playWav(1); + else if (_specialSoundNum == 210 && soundNumber == 2) + playWav(1); + else if (_specialSoundNum == 211 && soundNumber == 22) + playWav(1); + else if (_specialSoundNum == 229) { + if (soundNumber == 15) + playWav(1); + else if (soundNumber == 91) + playWav(2); + } +} + +static const char *const modSounds[] = { + "appart", "ville", "Rock", "police", "deep", + "purgat", "riviere", "SUSPENS", "labo", "cadavre", + "cabane", "purgat2", "foret", "ile", "ile2", + "hopkins", "peur", "URAVOLGA", "BASE", "cadavre2", + "usine", "chien", "coeur", "stand", "ocean", + "base3", "gloop", "cant", "feel", "lost", + "tobac" +}; + +void SoundManager::playSound(int soundNumber) { + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) { + if (soundNumber > 27) + return; + } + + if (_oldSoundNumber != soundNumber || !_modPlayingFl) { + if (_modPlayingFl) + stopSound(); + + playMod(modSounds[soundNumber - 1]); + _oldSoundNumber = soundNumber; + } +} + +void SoundManager::stopSound() { + stopVoice(0); + stopVoice(1); + stopVoice(2); + if (_soundFl) + delWav(_currentSoundIndex); + + for (int i = 1; i <= 48; ++i) + removeWavSample(i); + + if (_modPlayingFl) { + stopMusic(); + delMusic(); + _modPlayingFl = false; + } +} + +void SoundManager::playMod(const Common::String &file) { + if (_musicOffFl) + return; + Common::String modFile = file; + + // HACK + if (modFile == "URAVOLGA" && (_vm->getPlatform() == Common::kPlatformWindows || _vm->getPlatform() == Common::kPlatformLinux)) + modFile = "peur"; + + // The Windows/Linux version chops off the music file names to 5 characters + if (modFile.size() > 5 && (_vm->getPlatform() == Common::kPlatformWindows || _vm->getPlatform() == Common::kPlatformLinux)) { + if (!modFile.hasSuffix("2")) { + while (modFile.size() > 5) + modFile.deleteLastChar(); + } else { + while (modFile.size() > 4) + modFile.deleteLastChar(); + modFile += "2"; + } + } + if (_modPlayingFl) { + stopMusic(); + delMusic(); + _modPlayingFl = false; + } + + loadMusic(modFile); + playMusic(); + _modPlayingFl = true; +} + +void SoundManager::loadMusic(const Common::String &file) { + if (_music._active) + delMusic(); + + Common::File f; + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) { + Common::String filename = Common::String::format("%s.MOD", file.c_str()); + + if (!f.open(filename)) + error("Error opening file %s", filename.c_str()); + + Modules::Module *module; + Audio::AudioStream *modStream = Audio::makeProtrackerStream(&f, 0, 44100, true, &module); + + // WORKAROUND: This song is played at the empty lot where the + // bank robbers have left the helicopter. The MOD file appears + // to be slightly broken. Almost half of it is just the same + // noise repeating. We fix this by only playing the working + // part of it. The result is pretty close to the Windows music. + if (file.equalsIgnoreCase("cadavre")) { + module->songlen = 3; + } + + _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, modStream); + + } else { + Common::String filename = Common::String::format("%s.TWA", file.c_str()); + + if (!f.open(filename)) + error("Error opening file %s", filename.c_str()); + + Audio::AudioStream *twaStream = makeTwaStream(file.c_str(), &f); + _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, twaStream); + f.close(); + } + + _music._active = true; +} + +void SoundManager::playMusic() { +} + +void SoundManager::stopMusic() { + _vm->_mixer->stopHandle(_musicHandle); +} + +void SoundManager::delMusic() { + _music._active = false; +} + +void SoundManager::checkSounds() { + checkVoiceActivity(); +} + +/** + * Checks voices to see if they're finished + */ +void SoundManager::checkVoiceActivity() { + // Check the status of each voice. + bool hasActiveVoice = false; + for (int i = 0; i < VOICE_COUNT; ++i) + hasActiveVoice |= checkVoiceStatus(i); + + if (!hasActiveVoice && _soundFl) { + _soundFl = false; + _currentSoundIndex = 0; + } +} + +bool SoundManager::mixVoice(int voiceId, int voiceMode, bool dispTxtFl) { + int fileNumber; + bool breakFlag; + Common::String prefix; + Common::String filename; + Common::File f; + size_t catPos, catLen; + + fileNumber = voiceId; + if (_voiceOffFl) + return false; + + if ((voiceMode == 1 || voiceMode == 2) + && ( voiceId == 4 || voiceId == 16 || voiceId == 121 + || voiceId == 142 || voiceId == 182 || voiceId == 191 + || voiceId == 212 || voiceId == 225 || voiceId == 239 + || voiceId == 245 || voiceId == 297 || voiceId == 308 + || voiceId == 333 || voiceId == 348 || voiceId == 352 + || voiceId == 358 || voiceId == 364 || voiceId == 371 + || voiceId == 394 || voiceId == 414 || voiceId == 429 + || voiceId == 442 || voiceId == 446 || voiceId == 461 + || voiceId == 468 || voiceId == 476 || voiceId == 484 + || voiceId == 491 || voiceId == 497 || voiceId == 501 + || voiceId == 511 || voiceId == 520 || voiceId == 536 + || voiceId == 554 || voiceId == 566 || voiceId == 573 + || voiceId == 632 || voiceId == 645)) + fileNumber = 684; + + if (voiceMode == 1 || voiceMode == 2) + prefix = "DF"; + else if (voiceMode == 3) + prefix = "IF"; + else if (voiceMode == 4) + prefix = "TF"; + else if (voiceMode == 5) + prefix = "OF"; + + // BeOS and OS/2 versions are using a slightly different speech order during intro + // This map those values to the ones used by the Win95 and Linux versions + int mappedFileNumber = fileNumber; + if (voiceMode == 3 && (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)) { + if (fileNumber == 4) + mappedFileNumber = 0; + else if (fileNumber > 4) + mappedFileNumber = fileNumber - 1; + } + + filename = Common::String::format("%s%d", prefix.c_str(), mappedFileNumber); + + bool fileFoundFl = false; + _vm->_fileIO->searchCat(filename + ".WAV", RES_VOI, fileFoundFl); + if (fileFoundFl) { + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + filename = "ENG_VOI.RES"; + // Win95 and Linux versions uses another set of names + else if (_vm->_globals->_language == LANG_FR) + filename = "RES_VFR.RES"; + else if (_vm->_globals->_language == LANG_EN) + filename = "RES_VAN.RES"; + else if (_vm->_globals->_language == LANG_SP) + filename = "RES_VES.RES"; + + catPos = _vm->_fileIO->_catalogPos; + catLen = _vm->_fileIO->_catalogSize; + } else { + _vm->_fileIO->searchCat(filename + ".APC", RES_VOI, fileFoundFl); + if (fileFoundFl) { + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + filename = "ENG_VOI.RES"; + // Win95 and Linux versions uses another set of names + else if (_vm->_globals->_language == LANG_FR) + filename = "RES_VFR.RES"; + else if (_vm->_globals->_language == LANG_EN) + filename = "RES_VAN.RES"; + else if (_vm->_globals->_language == LANG_SP) + filename = "RES_VES.RES"; + + catPos = _vm->_fileIO->_catalogPos; + catLen = _vm->_fileIO->_catalogSize; + } else { + _vm->_fileIO->searchCat(filename + ".RAW", RES_VOI, fileFoundFl); + if (fileFoundFl) { + if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) + filename = "ENG_VOI.RES"; + // Win95 and Linux versions uses another set of names + else if (_vm->_globals->_language == LANG_FR) + filename = "RES_VFR.RES"; + else if (_vm->_globals->_language == LANG_EN) + filename = "RES_VAN.RES"; + else if (_vm->_globals->_language == LANG_SP) + filename = "RES_VES.RES"; + + catPos = _vm->_fileIO->_catalogPos; + catLen = _vm->_fileIO->_catalogSize; + } else { + if (!f.exists(filename + ".WAV")) { + if (!f.exists(filename + ".APC")) + return false; + filename = filename + ".APC"; + } else + filename = filename + ".WAV"; + + catPos = 0; + catLen = 0; + } + } + } + int oldMusicVol = _musicVolume; + if (!loadVoice(filename, catPos, catLen, _sWav[20])) { + // This case only concerns the English Win95 demo + // If it's not possible to load the voice, we force the active flag + // to false in order to make sure the missing buffer won't be played + // accidentally later + _sWav[20]._active = false; + } else { + _sWav[20]._active = true; + + // Reduce music volume during speech + if (!_musicOffFl && _musicVolume > 2) { + _musicVolume -= _musicVolume * 9 / 20; + setMODMusicVolume(_musicVolume); + } + } + playVoice(); + + _vm->_events->_escKeyFl = false; + + // Loop for playing voice + breakFlag = false; + do { + if (_specialSoundNum != 4 && !_skipRefreshFl) + _vm->_events->refreshScreenAndEvents(); + if (_vm->_events->getMouseButton()) + break; + _vm->_events->refreshEvents(); + if (_vm->_events->_escKeyFl) + break; + // We only check the voice status if the file has been loaded properly + // This avoids skipping completely the talk animations in the Win95 UK Demo + if (!checkVoiceStatus(2) && _sWav[20]._active) + breakFlag = true; + // This is specific to the Win95 UK Demo again: if nothing is displayed, + // don't wait for a click event. + if (!_sWav[20]._active && !dispTxtFl) + break; + } while (!_vm->shouldQuit() && !breakFlag); + + + stopVoice(2); + removeWavSample(20); + + // Speech is over, set the music volume back to normal + _musicVolume = oldMusicVol; + if (!_musicOffFl && _musicVolume > 2) { + setMODMusicVolume(_musicVolume); + } + _vm->_events->_escKeyFl = false; + _skipRefreshFl = false; + return true; +} + +void SoundManager::removeSample(int soundIndex) { + if (checkVoiceStatus(1)) + stopVoice(1); + if (checkVoiceStatus(2)) + stopVoice(2); + removeWavSample(soundIndex); + _sound[soundIndex]._active = false; +} + +void SoundManager::playSoundFile(const Common::String &file) { + if (_soundOffFl) + return; + + // Fallback for the menu option. + // The BeOS and OS/2 versions don't play sound at this point. + // sound20 sounds very close to bruit2 from the linux and Win95 versions. + Common::File f; + Common::String filename; + if (file == "bruit2.wav" && !f.exists(file)) + filename = "sound20.wav"; + else + filename = file; + + if (_soundFl) + delWav(_currentSoundIndex); + loadWav(filename, 1); + playWav(1); +} + +void SoundManager::directPlayWav(const Common::String &file) { + if (_soundOffFl) + return; + + loadWav(file, 1); + playWav(1); +} + +void SoundManager::setMODSampleVolume() { + for (int idx = 0; idx < SWAV_COUNT; ++idx) { + if (idx != 20 && _sWav[idx]._active) { + int volume = _soundVolume * 255 / 16; + _vm->_mixer->setChannelVolume(_sWav[idx]._soundHandle, volume); + } + } +} + +void SoundManager::setMODVoiceVolume() { + if (_sWav[20]._active) { + int volume = _voiceVolume * 255 / 16; + _vm->_mixer->setChannelVolume(_sWav[20]._soundHandle, volume); + } +} + +void SoundManager::setMODMusicVolume(int volume) { + if (_vm->_mixer->isSoundHandleActive(_musicHandle)) + _vm->_mixer->setChannelVolume(_musicHandle, volume * 255 / 16); +} + +void SoundManager::loadSample(int wavIndex, const Common::String &file) { + loadWavSample(wavIndex, file, false); + _sound[wavIndex]._active = true; +} + +void SoundManager::playSample(int wavIndex, int voiceMode) { + if (_soundOffFl || !_sound[wavIndex]._active) + return; + + if (_soundFl) + delWav(_currentSoundIndex); + + switch (voiceMode) { + case 5: + // Case added to identify the former PLAY_SAMPLE2 calls + case 9: + if (checkVoiceStatus(1)) + stopVoice(1); + playWavSample(1, wavIndex); + break; + case 6: + if (checkVoiceStatus(2)) + stopVoice(1); + playWavSample(2, wavIndex); + break; + default: + break; + } +} + +bool SoundManager::checkVoiceStatus(int voiceIndex) { + if (_voice[voiceIndex]._status) { + int wavIndex = _voice[voiceIndex]._wavIndex; + if (_sWav[wavIndex]._audioStream && _sWav[wavIndex]._audioStream->endOfStream()) + stopVoice(voiceIndex); + } + + return _voice[voiceIndex]._status; +} + +void SoundManager::stopVoice(int voiceIndex) { + if (_voice[voiceIndex]._status) { + _voice[voiceIndex]._status = false; + int wavIndex = _voice[voiceIndex]._wavIndex; + if (_sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl) + removeWavSample(wavIndex); + } + _voice[voiceIndex]._status = false; +} + +void SoundManager::playVoice() { + if (!_sWav[20]._active) + return; + + if (!_voice[2]._status) { + int wavIndex = _voice[2]._wavIndex; + if (_sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl) + removeWavSample(wavIndex); + } + + playWavSample(2, 20); +} + +bool SoundManager::removeWavSample(int wavIndex) { + if (!_sWav[wavIndex]._active) + return false; + + _vm->_mixer->stopHandle(_sWav[wavIndex]._soundHandle); + delete _sWav[wavIndex]._audioStream; + _sWav[wavIndex]._audioStream = NULL; + _sWav[wavIndex]._active = false; + + return true; +} + +bool SoundManager::loadVoice(const Common::String &filename, size_t fileOffset, size_t entryLength, SwavItem &item) { + Common::File f; + if (!f.open(filename)) { + // Fallback to APC... + if (!f.open(setExtension(filename, ".APC"))) { + // The English demo doesn't include the speech file. + // This avoids it to crash when discussing with other characters + if (!_vm->getIsDemo()) + error("Could not open %s for reading", filename.c_str()); + return false; + } + } + + f.seek(fileOffset); + item._audioStream = makeSoundStream(f.readStream((entryLength == 0) ? f.size() : entryLength)); + f.close(); + + return true; +} + +void SoundManager::loadWavSample(int wavIndex, const Common::String &filename, bool freeSample) { + if (_sWav[wavIndex]._active) + removeWavSample(wavIndex); + + if (loadVoice(filename, 0, 0, _sWav[wavIndex])) { + _sWav[wavIndex]._active = true; + _sWav[wavIndex]._freeSampleFl = freeSample; + } else{ + _sWav[wavIndex]._active = false; + } +} + +void SoundManager::loadWav(const Common::String &file, int wavIndex) { + loadWavSample(wavIndex, file, true); +} + +void SoundManager::playWav(int wavIndex) { + if (_soundFl || _soundOffFl) + return; + + _soundFl = true; + _currentSoundIndex = wavIndex; + playWavSample(1, wavIndex); +} + +void SoundManager::delWav(int wavIndex) { + if (!removeWavSample(wavIndex)) + return; + + if (checkVoiceStatus(1)) + stopVoice(1); + + _currentSoundIndex = 0; + _soundFl = false; +} + +void SoundManager::playWavSample(int voiceIndex, int wavIndex) { + if (!_sWav[wavIndex]._active) + warning("Bad handle"); + + if (_voice[voiceIndex]._status && _sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl) + removeWavSample(wavIndex); + + _voice[voiceIndex]._status = true; + _voice[voiceIndex]._wavIndex = wavIndex; + + int volume = (voiceIndex == 2) ? _voiceVolume * 255 / 16 : _soundVolume * 255 / 16; + + // If the handle is still in use, stop it. Otherwise we'll lose the + // handle to that sound. This can currently happen (but probably + // shouldn't) when skipping a movie. + if (_vm->_mixer->isSoundHandleActive(_sWav[wavIndex]._soundHandle)) + _vm->_mixer->stopHandle(_sWav[wavIndex]._soundHandle); + + // Start the voice playing + _sWav[wavIndex]._audioStream->rewind(); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sWav[wavIndex]._soundHandle, + _sWav[wavIndex]._audioStream, -1, volume, 0, DisposeAfterUse::NO); +} + +void SoundManager::syncSoundSettings() { + bool muteAll = false; + if (ConfMan.hasKey("mute")) + muteAll = ConfMan.getBool("mute"); + + // Update the mute settings + _musicOffFl = muteAll || (ConfMan.hasKey("music_mute") && ConfMan.getBool("music_mute")); + _soundOffFl = muteAll || (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute")); + _voiceOffFl = muteAll || (ConfMan.hasKey("speech_mute") && ConfMan.getBool("speech_mute")); + + // Update the volume levels + _musicVolume = MIN(255, ConfMan.getInt("music_volume")) * 16 / 255; + _soundVolume = MIN(255, ConfMan.getInt("sfx_volume")) * 16 / 255; + _voiceVolume = MIN(255, ConfMan.getInt("speech_volume")) * 16 / 255; + + // Update any active sounds + for (int idx = 0; idx < SWAV_COUNT; ++idx) { + if (_sWav[idx]._active) { + int volume = (idx == 20) ? (_voiceVolume * 255 / 16) : (_soundVolume * 255 / 16); + _vm->_mixer->setChannelVolume(_sWav[idx]._soundHandle, volume); + } + } + if (_vm->_mixer->isSoundHandleActive(_musicHandle)) { + _vm->_mixer->setChannelVolume(_musicHandle, _musicVolume * 255 / 16); + } +} + +void SoundManager::updateScummVMSoundSettings() { + ConfMan.setBool("mute", _musicOffFl && _soundOffFl && _voiceOffFl); + ConfMan.setBool("music_mute", _musicOffFl); + ConfMan.setBool("sfx_mute", _soundOffFl); + ConfMan.setBool("speech_mute", _voiceOffFl); + + ConfMan.setInt("music_volume", _musicVolume * 255 / 16); + ConfMan.setInt("sfx_volume", _soundVolume * 255 / 16); + ConfMan.setInt("speech_volume", _voiceVolume * 255 / 16); + + ConfMan.flushToDisk(); +} + +/** + * Creates an audio stream based on a passed raw stream + */ +Audio::RewindableAudioStream *SoundManager::makeSoundStream(Common::SeekableReadStream *stream) { + if (_vm->getPlatform() == Common::kPlatformWindows) + return makeAPCStream(stream, DisposeAfterUse::YES); + else if (_vm->getPlatform() == Common::kPlatformLinux) + return Audio::makeWAVStream(stream, DisposeAfterUse::YES); + else + return Audio::makeRawStream(stream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES); +} + +// Blatant rip from gob engine. Hi DrMcCoy! +Common::String SoundManager::setExtension(const Common::String &str, const Common::String &ext) { + if (str.empty()) + return str; + + const char *dot = strrchr(str.c_str(), '.'); + if (dot) + return Common::String(str.c_str(), dot - str.c_str()) + ext; + + return str + ext; +} +} // End of namespace Hopkins diff --git a/engines/hopkins/sound.h b/engines/hopkins/sound.h new file mode 100644 index 0000000000..f1d047ae8b --- /dev/null +++ b/engines/hopkins/sound.h @@ -0,0 +1,138 @@ +/* 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. + * + */ + +#ifndef HOPKINS_SOUND_H +#define HOPKINS_SOUND_H + +#include "common/scummsys.h" +#include "common/str.h" +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" +#include "audio/mixer.h" + +namespace Hopkins { + +class VoiceItem { +public: + bool _status; + int _wavIndex; +}; + +class SwavItem { +public: + bool _active; + Audio::RewindableAudioStream *_audioStream; + Audio::SoundHandle _soundHandle; + bool _freeSampleFl; +}; + +class MusicItem { +public: + bool _active; +}; + +class SoundItem { +public: + bool _active; +}; + +#define VOICE_COUNT 3 +#define SWAV_COUNT 50 +#define SOUND_COUNT 10 + +class HopkinsEngine; + +class SoundManager { +private: + HopkinsEngine *_vm; + + Audio::SoundHandle _musicHandle; + int _currentSoundIndex; + bool _modPlayingFl; + int _oldSoundNumber; + + VoiceItem _voice[VOICE_COUNT]; + SwavItem _sWav[SWAV_COUNT]; + SoundItem _sound[SOUND_COUNT]; + MusicItem _music; + + void playMod(const Common::String &file); + void loadMusic(const Common::String &file); + void playMusic(); + void stopMusic(); + void delMusic(); + bool checkVoiceStatus(int voiceIndex); + bool loadVoice(const Common::String &filename, size_t fileOffset, size_t entryLength, SwavItem &item); + void stopVoice(int voiceIndex); + void playVoice(); + void delWav(int wavIndex); + void checkVoiceActivity(); + Common::String setExtension(const Common::String &str, const Common::String &ext); + Audio::RewindableAudioStream *makeSoundStream(Common::SeekableReadStream *stream); + bool removeWavSample(int wavIndex); + void loadWavSample(int wavIndex, const Common::String &filename, bool freeSample); + void playWavSample(int voiceIndex, int wavIndex); + +public: + bool _musicOffFl; + bool _soundOffFl; + bool _voiceOffFl; + bool _textOffFl; + bool _soundFl; + bool _skipRefreshFl; + int _musicVolume; + int _soundVolume; + int _voiceVolume; + int _specialSoundNum; +public: + SoundManager(HopkinsEngine *vm); + ~SoundManager(); + + void loadAnimSound(); + void playAnimSound(int soundNumber); + + void loadSample(int wavIndex, const Common::String &file); + void playSample(int wavIndex, int voiceMode = 9); + void removeSample(int soundIndex); + + void checkSoundEnd(); + void checkSounds(); + void playSoundFile(const Common::String &file); + void playSound(int soundNumber); + void stopSound(); + + void updateScummVMSoundSettings(); + void syncSoundSettings(); + bool mixVoice(int voiceId, int voiceMode, bool displTxtFl = false); + + void setMODMusicVolume(int volume); + void setMODSampleVolume(); + void setMODVoiceVolume(); + + void loadWav(const Common::String &file, int wavIndex); + void playWav(int wavIndex); + void directPlayWav(const Common::String &file2); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_SOUND_H */ diff --git a/engines/hopkins/talk.cpp b/engines/hopkins/talk.cpp new file mode 100644 index 0000000000..736ec9865c --- /dev/null +++ b/engines/hopkins/talk.cpp @@ -0,0 +1,1081 @@ +/* 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 "hopkins/talk.h" + +#include "hopkins/files.h" +#include "hopkins/globals.h" +#include "hopkins/graphics.h" +#include "hopkins/hopkins.h" +#include "hopkins/objects.h" + +#include "common/system.h" +#include "common/endian.h" +#include "common/file.h" +#include "common/textconsole.h" + +namespace Hopkins { + +TalkManager::TalkManager(HopkinsEngine *vm) { + _vm = vm; + _characterBuffer = NULL; + _characterPalette = NULL; + _characterSprite = NULL; + _characterAnim = NULL; + _characterSize = 0; + _dialogueMesgId1 = _dialogueMesgId2 = _dialogueMesgId3 = _dialogueMesgId4 = 0; + _paletteBufferIdx = 0; +} + +void TalkManager::startAnimatedCharacterDialogue(const Common::String &filename) { + Common::String spriteFilename; + + _vm->_fontMan->hideText(5); + _vm->_fontMan->hideText(9); + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->_scrollStatus = 1; + bool oldDisableInventFl = _vm->_globals->_disableInventFl; + _vm->_globals->_disableInventFl = true; + bool fileFoundFl = false; + _characterBuffer = _vm->_fileIO->searchCat(filename, RES_PER, fileFoundFl); + _characterSize = _vm->_fileIO->_catalogSize; + if (!fileFoundFl) { + _characterBuffer = _vm->_fileIO->loadFile(filename); + _characterSize = _vm->_fileIO->fileSize(filename); + } + + _vm->_globals->_saveData->_data[svDialogField4] = 0; + + getStringFromBuffer(40, spriteFilename, (const char *)_characterBuffer); + getStringFromBuffer(0, _questionsFilename, (const char *)_characterBuffer); + getStringFromBuffer(20, _answersFilename, (const char *)_characterBuffer); + if (_vm->_globals->_language == LANG_FR) { + _answersFilename = _questionsFilename = "RUE.TXT"; + } else if (_vm->_globals->_language == LANG_EN) { + _answersFilename = _questionsFilename = "RUEAN.TXT"; + } else if (_vm->_globals->_language == LANG_SP) { + _answersFilename = _questionsFilename = "RUEES.TXT"; + } + _dialogueMesgId1 = READ_LE_INT16((uint16 *)_characterBuffer + 40); + _paletteBufferIdx = 20 * READ_LE_INT16((uint16 *)_characterBuffer + 42) + 110; + fileFoundFl = false; + _characterSprite = _vm->_fileIO->searchCat(spriteFilename, RES_SAN, fileFoundFl); + if (!fileFoundFl) { + _characterSprite = _vm->_objectsMan->loadSprite(spriteFilename); + } else { + _characterSprite = _vm->_objectsMan->loadSprite("RES_SAN.RES"); + } + + _vm->_graphicsMan->backupScreen(); + + if (!_vm->_graphicsMan->_lineNbr) + _vm->_graphicsMan->_scrollOffset = 0; + _vm->_graphicsMan->displayScreen(true); + _vm->_objectsMan->_charactersEnabledFl = true; + searchCharacterPalette(_paletteBufferIdx, false); + startCharacterAnim0(_paletteBufferIdx, false); + initCharacterAnim(); + _dialogueMesgId2 = _dialogueMesgId1 + 1; + _dialogueMesgId3 = _dialogueMesgId1 + 2; + _dialogueMesgId4 = _dialogueMesgId1 + 3; + int oldMouseCursorId = _vm->_events->_mouseCursorId; + _vm->_events->_mouseCursorId = 4; + _vm->_events->changeMouseCursor(0); + if (!_vm->_globals->_introSpeechOffFl) { + int answer = 0; + int dlgAnswer; + do { + dlgAnswer = dialogQuestion(false); + if (dlgAnswer != _dialogueMesgId4) + answer = dialogAnswer(dlgAnswer, false); + if (answer == -1) + dlgAnswer = _dialogueMesgId4; + _vm->_events->refreshScreenAndEvents(); + } while (dlgAnswer != _dialogueMesgId4); + } + if (_vm->_globals->_introSpeechOffFl) { + int idx = 1; + int answer; + do + answer = dialogAnswer(idx++, false); + while (answer != -1); + } + clearCharacterAnim(); + _vm->_globals->_introSpeechOffFl = false; + _characterBuffer = _vm->_globals->freeMemory(_characterBuffer); + _characterSprite = _vm->_globals->freeMemory(_characterSprite); + _vm->_graphicsMan->displayScreen(false); + + _vm->_graphicsMan->restoreScreen(); + + _vm->_objectsMan->_charactersEnabledFl = false; + _vm->_events->_mouseCursorId = oldMouseCursorId; + + _vm->_events->changeMouseCursor(oldMouseCursorId); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + + if (_vm->getIsDemo() == false) + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + + _vm->_graphicsMan->initColorTable(145, 150, _vm->_graphicsMan->_palette); + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + _vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_backBuffer, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + memcpy(_vm->_graphicsMan->_frontBuffer, _vm->_graphicsMan->_backBuffer, 614399); + _vm->_globals->_disableInventFl = oldDisableInventFl; + _vm->_graphicsMan->updateScreen(); + for (int i = 0; i <= 4; i++) + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->_scrollStatus = 0; +} + +void TalkManager::startStaticCharacterDialogue(const Common::String &filename) { + // TODO: The original disables the mouse cursor here + bool oldDisableInventFl = _vm->_globals->_disableInventFl; + _vm->_globals->_disableInventFl = true; + bool fileFoundFl = false; + _characterBuffer = _vm->_fileIO->searchCat(filename, RES_PER, fileFoundFl); + _characterSize = _vm->_fileIO->_catalogSize; + if (!fileFoundFl) { + _characterBuffer = _vm->_fileIO->loadFile(filename); + _characterSize = _vm->_fileIO->fileSize(filename); + } + + _vm->_globals->_saveData->_data[svDialogField4] = 0; + + getStringFromBuffer(0, _questionsFilename, (const char *)_characterBuffer); + getStringFromBuffer(20, _answersFilename, (const char *)_characterBuffer); + + switch (_vm->_globals->_language) { + case LANG_EN: + _questionsFilename = "RUEAN.TXT"; + _answersFilename = "RUEAN.TXT"; + break; + case LANG_FR: + _questionsFilename = "RUE.TXT"; + _answersFilename = "RUE.TXT"; + break; + case LANG_SP: + _questionsFilename = "RUEES.TXT"; + _answersFilename = "RUEES.TXT"; + break; + } + + _dialogueMesgId1 = READ_LE_INT16((uint16 *)_characterBuffer + 40); + _paletteBufferIdx = 20 * READ_LE_INT16((uint16 *)_characterBuffer + 42) + 110; + searchCharacterPalette(_paletteBufferIdx, false); + _dialogueMesgId2 = _dialogueMesgId1 + 1; + _dialogueMesgId3 = _dialogueMesgId1 + 2; + _dialogueMesgId4 = _dialogueMesgId1 + 3; + int oldMouseCursorId = _vm->_events->_mouseCursorId; + _vm->_events->_mouseCursorId = 4; + _vm->_events->changeMouseCursor(0); + + if (!_vm->_globals->_introSpeechOffFl) { + int answer; + do { + answer = dialogQuestion(true); + if (answer != _dialogueMesgId4) { + if (dialogAnswer(answer, true) == -1) + answer = _dialogueMesgId4; + } + } while (answer != _dialogueMesgId4); + } + + if (_vm->_globals->_introSpeechOffFl) { + int idx = 1; + int answer; + do + answer = dialogAnswer(idx++, true); + while (answer != -1); + } + + _characterBuffer = _vm->_globals->freeMemory(_characterBuffer); + _vm->_events->_mouseCursorId = oldMouseCursorId; + + _vm->_events->changeMouseCursor(oldMouseCursorId); + _vm->_graphicsMan->initColorTable(145, 150, _vm->_graphicsMan->_palette); + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + // TODO: The original re-enables the mouse cursor here + _vm->_globals->_disableInventFl = oldDisableInventFl; +} + +void TalkManager::getStringFromBuffer(int srcStart, Common::String &dest, const char *srcData) { + dest = Common::String(srcData + srcStart); +} + +int TalkManager::dialogQuestion(bool animatedFl) { + if (animatedFl) { + uint16 *bufPtr = (uint16 *)_characterBuffer + 48; + int curVal = READ_LE_INT16(bufPtr); + if (curVal != 0) + _vm->_objectsMan->setBobAnimation(curVal); + if (curVal != 1) + _vm->_objectsMan->setBobAnimation(READ_LE_INT16(bufPtr + 1)); + if (curVal != 2) + _vm->_objectsMan->setBobAnimation(READ_LE_INT16(bufPtr + 2)); + if (curVal != 3) + _vm->_objectsMan->setBobAnimation(READ_LE_INT16(bufPtr + 3)); + if (curVal != 4) + _vm->_objectsMan->setBobAnimation(READ_LE_INT16(bufPtr + 4)); + } else { + dialogWait(); + } + + int sentence1LineNumb = countBoxLines(_dialogueMesgId1, _questionsFilename); + int sentence2LineNumb = countBoxLines(_dialogueMesgId2, _questionsFilename); + int sentence3LineNumb = countBoxLines(_dialogueMesgId3, _questionsFilename); + int sentence4LineNumb = countBoxLines(_dialogueMesgId4, _questionsFilename); + + int sentence4PosY = 420 - 20 * sentence4LineNumb; + int sentence3PosY = sentence4PosY - 20 * sentence3LineNumb; + int sentence2PosY = sentence3PosY - 20 * sentence2LineNumb; + int sentence1PosY = sentence2PosY - 20 * sentence1LineNumb; + + _vm->_fontMan->initTextBuffers(5, _dialogueMesgId1, _questionsFilename, 5, sentence1PosY, 0, 65, 255); + _vm->_fontMan->initTextBuffers(6, _dialogueMesgId2, _questionsFilename, 5, sentence2PosY, 0, 65, 255); + _vm->_fontMan->initTextBuffers(7, _dialogueMesgId3, _questionsFilename, 5, sentence3PosY, 0, 65, 255); + _vm->_fontMan->initTextBuffers(8, _dialogueMesgId4, _questionsFilename, 5, sentence4PosY, 0, 65, 255); + _vm->_fontMan->showText(5); + _vm->_fontMan->showText(6); + _vm->_fontMan->showText(7); + _vm->_fontMan->showText(8); + + int retVal = -1; + bool loopCond = false; + do { + int mousePosY = _vm->_events->getMouseY(); + if (sentence1PosY < mousePosY && mousePosY < (sentence2PosY - 1)) { + _vm->_fontMan->setOptimalColor(6, 7, 8, 5); + retVal = _dialogueMesgId1; + } + if (sentence2PosY < mousePosY && mousePosY < (sentence3PosY - 1)) { + _vm->_fontMan->setOptimalColor(5, 7, 8, 6); + retVal = _dialogueMesgId2; + } + if (sentence3PosY < mousePosY && mousePosY < (sentence4PosY - 1)) { + _vm->_fontMan->setOptimalColor(5, 6, 8, 7); + retVal = _dialogueMesgId3; + } + if (sentence4PosY < mousePosY && mousePosY < 419) { + _vm->_fontMan->setOptimalColor(5, 6, 7, 8); + retVal = _dialogueMesgId4; + } + + _vm->_events->refreshScreenAndEvents(); + if (_vm->_events->getMouseButton()) + loopCond = true; + if (retVal == -1) + loopCond = false; + } while (!_vm->shouldQuit() && !loopCond); + + _vm->_soundMan->mixVoice(retVal, 1); + _vm->_fontMan->hideText(5); + _vm->_fontMan->hideText(6); + _vm->_fontMan->hideText(7); + _vm->_fontMan->hideText(8); + + if (animatedFl) { + uint16 *bufPtr = (uint16 *)_characterBuffer + 48; + + int curVal = READ_LE_INT16(bufPtr); + if (curVal != 0) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 1); + if (curVal != 1) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 2); + if (curVal != 2) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 3); + if (curVal != 3) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 4); + if (curVal != 4) + _vm->_objectsMan->stopBobAnimation(curVal); + } else { + dialogTalk(); + } + + _vm->_events->refreshScreenAndEvents(); + return retVal; +} + +int TalkManager::dialogAnswer(int idx, bool animatedFl) { + int charIdx; + byte *charBuf; + for (charBuf = _characterBuffer + 110, charIdx = 0; READ_LE_INT16(charBuf) != idx; charBuf += 20) { + ++charIdx; + if (READ_LE_INT16((uint16 *)_characterBuffer + 42) < charIdx) + return -1; + } + + int mesgId = READ_LE_INT16((uint16 *)charBuf + 1); + int mesgPosX = READ_LE_INT16((uint16 *)charBuf + 2); + int mesgPosY = READ_LE_INT16((uint16 *)charBuf + 3); + int mesgLength = READ_LE_INT16((uint16 *)charBuf + 4); + _dialogueMesgId1 = READ_LE_INT16((uint16 *)charBuf + 5); + _dialogueMesgId2 = READ_LE_INT16((uint16 *)charBuf + 6); + _dialogueMesgId3 = READ_LE_INT16((uint16 *)charBuf + 7); + int frameNumb = READ_LE_INT16((uint16 *)charBuf + 8); + + int curBufVal = READ_LE_INT16((uint16 *)charBuf + 9); + if (curBufVal) + _vm->_globals->_saveData->_data[svDialogField4] = curBufVal; + + if (!frameNumb) + frameNumb = 10; + if (animatedFl) { + uint16 *bufPtr = (uint16 *)_characterBuffer + 43; + int curVal = READ_LE_INT16(bufPtr); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 1); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 2); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 3); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 4); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + } else { + dialogAnim(); + } + + bool displayedTxtFl = false; + if (!_vm->_soundMan->_textOffFl) { + _vm->_fontMan->initTextBuffers(9, mesgId, _answersFilename, mesgPosX, mesgPosY, 5, mesgLength, 252); + _vm->_fontMan->showText(9); + displayedTxtFl = true; + } + if (!_vm->_soundMan->mixVoice(mesgId, 1, displayedTxtFl)) { + _vm->_events->_curMouseButton = 0; + _vm->_events->_mouseButton = 0; + + if (_vm->getIsDemo()) { + for (int i = 0; i < frameNumb; i++) { + _vm->_events->refreshScreenAndEvents(); + } + } else { + for (int i = 0; i < frameNumb; i++) { + _vm->_events->refreshScreenAndEvents(); + if (_vm->_events->_mouseButton || _vm->_events->_curMouseButton) + break; + if (_vm->_events->getMouseButton() && i + 1 > abs(frameNumb / 5)) + break; + } + } + } + + if (!_vm->_soundMan->_textOffFl) + _vm->_fontMan->hideText(9); + if (animatedFl) { + uint16 *bufPtr = (uint16 *)_characterBuffer + 43; + int curVal = READ_LE_INT16(bufPtr); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 1); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 2); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 3); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + + curVal = READ_LE_INT16(bufPtr + 4); + if (curVal) + _vm->_objectsMan->stopBobAnimation(curVal); + } else { + dialogEndTalk(); + } + int result = 0; + if (!_dialogueMesgId1) + result = -1; + + return result; +} + +void TalkManager::searchCharacterPalette(int startIdx, bool dark) { + int palettePos = 0; + size_t curIdx = startIdx; + for (;;) { + if (READ_BE_UINT24(&_characterBuffer[curIdx]) == MKTAG24('P', 'A', 'L')) { + palettePos = curIdx; + break; + } + ++curIdx; + if (_characterSize == curIdx) + return; + } + + _characterPalette = _characterBuffer + palettePos + 5; + _characterPalette[0] = 0; + _characterPalette[1] = 0; + _characterPalette[2] = 0; + _characterPalette[759] = 255; + _characterPalette[760] = 255; + _characterPalette[762] = 0; + _characterPalette[763] = 0; + _characterPalette[764] = 0; + _characterPalette[765] = 224; + _characterPalette[766] = 224; + _characterPalette[767] = 255; + + if (!dark) + _characterPalette[761] = 86; + else + _characterPalette[761] = 255; + + _vm->_graphicsMan->setPaletteVGA256(_characterPalette); + _vm->_graphicsMan->initColorTable(145, 150, _characterPalette); +} + +void TalkManager::dialogWait() { + for (int idx = 26; idx <= 30; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + displayBobDialogAnim(idx); + } +} + +void TalkManager::dialogTalk() { + for (int idx = 26; idx <= 30; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + _vm->_objectsMan->hideBob(idx); + } + + for (int idx = 26; idx <= 30; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + _vm->_objectsMan->resetBob(idx); + } +} + +void TalkManager::dialogEndTalk() { + for (int idx = 21; idx <= 25; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + _vm->_objectsMan->hideBob(idx); + } + + _vm->_events->refreshScreenAndEvents(); + _vm->_events->refreshScreenAndEvents(); + + for (int idx = 21; idx <= 25; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + _vm->_objectsMan->resetBob(idx); + } +} + +int TalkManager::countBoxLines(int idx, const Common::String &file) { + _vm->_fontMan->_fontFixedWidth = 11; + + // Build up the filename + Common::String filename; + Common::String dest; + filename = dest = file; + while (filename.lastChar() != '.') + filename.deleteLastChar(); + filename += "IND"; + + Common::File f; + if (!f.open(filename)) + error("Could not open file - %s", filename.c_str()); + int filesize = f.size(); + assert(filesize < 16188); + + uint32 indexData[4047]; + for (int i = 0; i < (filesize / 4); ++i) + indexData[i] = f.readUint32LE(); + f.close(); + + if (!f.open(dest)) + error("Error opening file - %s", dest.c_str()); + + f.seek(indexData[idx]); + byte *decryptBuf = _vm->_globals->allocMemory(2058); + assert(decryptBuf); + + f.read(decryptBuf, 2048); + f.close(); + + // Decrypt buffer + byte *curDecryptPtr = decryptBuf; + for (int i = 0; i < 2048; i++) { + char curByte = *curDecryptPtr; + if ((byte)(curByte + 46) > 27) { + if ((byte)(curByte + 80) > 27) { + if ((curByte >= 'A' && curByte <= 'Z') || (curByte >= 'a' && curByte <= 'z')) + curByte = ' '; + } else { + curByte -= 79; + } + } else { + curByte += 111; + } + *curDecryptPtr = curByte; + curDecryptPtr++; + } + + // Separate strings + for (int i = 0; i < 2048; i++) { + if ( decryptBuf[i] == 10 || decryptBuf[i] == 13) + decryptBuf[i] = 0; + } + + // Check size of each strings in order to compute box width + int curBufIndx = 0; + int lineCount = 0; + int lineSize = 0; + char curChar; + do { + int curLineSize = 0; + for (;;) { + lineSize = curLineSize; + do { + curChar = decryptBuf[curBufIndx + curLineSize]; + ++curLineSize; + } while (curChar != ' ' && curChar != '%'); + + if (curLineSize >= MIN_LETTERS_PER_LINE - 1) { + if (curChar == '%') + curChar = ' '; + break; + } + + if (curChar == '%') { + lineSize = curLineSize; + break; + } + } + ++lineCount; + curBufIndx += lineSize; + } while (curChar != '%'); + _vm->_globals->freeMemory(decryptBuf); + return lineCount; +} + +void TalkManager::dialogAnim() { + for (int idx = 21; idx <= 25; ++idx) { + if (_vm->_animMan->_animBqe[idx]._enabledFl) + displayBobDialogAnim(idx); + } +} + +void TalkManager::displayBobDialogAnim(int idx) { + _vm->_objectsMan->_priorityFl = true; + if (!_vm->_objectsMan->_bob[idx]._bobMode) { + _vm->_objectsMan->resetBob(idx); + byte *bqeData = _vm->_animMan->_animBqe[idx]._data; + int newMode = READ_LE_INT16(bqeData + 2); + if (!newMode) + newMode = 1; + if (READ_LE_INT16(bqeData + 24)) { + _vm->_objectsMan->_bob[idx]._isSpriteFl = true; + _vm->_objectsMan->_bob[idx]._zoomFactor = 0; + _vm->_objectsMan->_bob[idx]._flipFl = false; + _vm->_objectsMan->_bob[idx]._animData = _vm->_animMan->_animBqe[idx]._data; + _vm->_objectsMan->_bob[idx]._bobMode = 10; + bqeData = _characterSprite; + _vm->_objectsMan->_bob[idx]._spriteData = _characterSprite; + _vm->_objectsMan->_bob[idx]._bobModeChange = newMode; + _vm->_objectsMan->_bob[idx]._modeChangeCtr = -1; + _vm->_objectsMan->_bob[idx]._modeChangeUnused = 0; + } + } +} + +void TalkManager::startCharacterAnim0(int startIdx, bool readOnlyFl) { + int animIdx = 0; + size_t curIdx = startIdx; + for (;;) { + if (READ_BE_UINT32(&_characterBuffer[curIdx]) == MKTAG('A', 'N', 'I', 'M') && _characterBuffer[curIdx + 4] == 1) { + animIdx = curIdx; + break; + } + ++curIdx; + if (_characterSize == curIdx) + return; + } + _characterAnim = _characterBuffer + animIdx + 25; + if (!readOnlyFl) { + int idx = 0; + do { + if (!READ_LE_INT16(&_characterAnim[2 * idx + 4])) + break; + if (_vm->_globals->_speed != 501) + _vm->_graphicsMan->fastDisplay(_characterSprite, _vm->_events->_startPos.x + READ_LE_INT16(&_characterAnim[2 * idx]), + READ_LE_INT16(&_characterAnim[2 * idx + 2]), _characterAnim[2 * idx + 8]); + idx += 5; + } while (_vm->_globals->_speed != 501); + } +} + +/** + * Initialize character animation + */ +void TalkManager::initCharacterAnim() { + uint16 *bufPtr = (uint16 *)_characterBuffer + 43; + byte *animPtr = _characterBuffer + 110; + int curVal = READ_LE_INT16(bufPtr); + if (curVal) + searchCharacterAnim(21, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 1); + if (curVal) + searchCharacterAnim(22, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 2); + if (curVal) + searchCharacterAnim(23, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 3); + if (curVal) + searchCharacterAnim(24, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 4); + if (curVal) + searchCharacterAnim(25, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 5); + if (curVal) + searchCharacterAnim(26, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 6); + if (curVal) + searchCharacterAnim(27, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 7); + if (curVal) + searchCharacterAnim(28, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 8); + if (curVal) + searchCharacterAnim(29, animPtr, curVal, _characterSize); + + curVal = READ_LE_INT16(bufPtr + 9); + if (curVal) + searchCharacterAnim(30, animPtr, curVal, _characterSize); +} + +void TalkManager::clearCharacterAnim() { + for (int idx = 21; idx <= 34; ++idx) { + _vm->_animMan->_animBqe[idx]._data = _vm->_globals->freeMemory(_vm->_animMan->_animBqe[idx]._data); + _vm->_animMan->_animBqe[idx]._enabledFl = false; + } +} + +bool TalkManager::searchCharacterAnim(int idx, const byte *bufPerso, int animId, int bufferSize) { + bool result = false; + + for (int bufPos = 0; bufPos <= bufferSize; bufPos++) { + if (READ_BE_UINT32(bufPerso + bufPos) == MKTAG('A', 'N', 'I', 'M') && bufPerso[bufPos + 4] == animId) { + int bufIndx = bufPos + 5; + const byte *curPtr = bufPerso + bufIndx; + int animLength = 0; + bool loopCond = false; + do { + if (READ_BE_UINT32(curPtr) == MKTAG('A', 'N', 'I', 'M') || READ_BE_UINT24(curPtr) == MKTAG24('F', 'I', 'N')) + loopCond = true; + if (bufIndx > bufferSize) { + _vm->_animMan->_animBqe[idx]._enabledFl = false; + _vm->_animMan->_animBqe[idx]._data = NULL; + return false; + } + ++bufIndx; + ++animLength; + ++curPtr; + } while (!loopCond); + _vm->_animMan->_animBqe[idx]._data = _vm->_globals->allocMemory(animLength + 50); + _vm->_animMan->_animBqe[idx]._enabledFl = true; + memcpy(_vm->_animMan->_animBqe[idx]._data, (const byte *)(bufPerso + bufPos + 5), 20); + int bqeVal = READ_LE_INT16(bufPos + bufPerso + 29); + WRITE_LE_UINT16(_vm->_animMan->_animBqe[idx]._data + 20, READ_LE_INT16(bufPos + bufPerso + 25)); + WRITE_LE_UINT16(_vm->_animMan->_animBqe[idx]._data + 22, READ_LE_INT16(bufPos + bufPerso + 27)); + WRITE_LE_UINT16(_vm->_animMan->_animBqe[idx]._data + 24, bqeVal); + WRITE_LE_UINT16(_vm->_animMan->_animBqe[idx]._data + 26, READ_LE_INT16(bufPos + bufPerso + 31)); + _vm->_animMan->_animBqe[idx]._data[28] = bufPerso[bufPos + 33]; + _vm->_animMan->_animBqe[idx]._data[29] = bufPerso[bufPos + 34]; + byte *bqeCurData = _vm->_animMan->_animBqe[idx]._data + 20; + const byte *curBufPerso = bufPos + bufPerso + 25; + for (int i = 1; i < 5000; i++) { + bqeCurData += 10; + curBufPerso += 10; + if (!bqeVal) + break; + bqeVal = READ_LE_INT16(curBufPerso + 4); + WRITE_LE_UINT16(bqeCurData, READ_LE_INT16(curBufPerso)); + WRITE_LE_UINT16(bqeCurData + 2, READ_LE_INT16(curBufPerso + 2)); + WRITE_LE_UINT16(bqeCurData + 4, bqeVal); + WRITE_LE_UINT16(bqeCurData + 6, READ_LE_INT16(curBufPerso + 6)); + bqeCurData[8] = curBufPerso[8]; + bqeCurData[9] = curBufPerso[9]; + } + result = true; + } + if (READ_BE_UINT24(&bufPerso[bufPos]) == MKTAG24('F', 'I', 'N')) + result = true; + + if (result) + break; + } + + return result; +} + +void TalkManager::handleAnswer(int zone, int verb) { + byte zoneObj = zone; + byte verbObj = verb; + + bool outerLoopFl; + byte *ptr = NULL; + do { + outerLoopFl = false; + bool tagFound = false; + if (_vm->_globals->_answerBuffer == NULL) + return; + + byte *curAnswerBuf = _vm->_globals->_answerBuffer; + for (;;) { + if (READ_BE_UINT24(curAnswerBuf) == MKTAG24('F', 'I', 'N')) + return; + if (READ_BE_UINT24(curAnswerBuf) == MKTAG24('C', 'O', 'D')) { + if (curAnswerBuf[3] == zoneObj && curAnswerBuf[4] == verbObj) + tagFound = true; + } + if (!tagFound) + curAnswerBuf++; + else + break; + } + + // 'COD' tag found + curAnswerBuf += 5; + ptr = _vm->_globals->allocMemory(620); + assert(ptr); + memset(ptr, 0, 620); + uint16 curAnswerIdx = 0; + int idx = 0; + bool innerLoopCond = false; + do { + tagFound = false; + if (READ_BE_UINT16(&curAnswerBuf[curAnswerIdx]) == MKTAG16('F', 'C')) { + ++idx; + assert(idx < (620 / 20)); + + byte *answerBuf = (ptr + 20 * idx); + uint16 anwerIdx = 0; + do { + assert(anwerIdx < 20); + answerBuf[anwerIdx++] = curAnswerBuf[curAnswerIdx++]; + if (READ_BE_UINT16(&curAnswerBuf[curAnswerIdx]) == MKTAG16('F', 'F')) { + tagFound = true; + answerBuf[anwerIdx] = 'F'; + answerBuf[anwerIdx + 1] = 'F'; + ++curAnswerIdx; + } + } while (!tagFound); + } + if (!tagFound) { + uint32 signature24 = READ_BE_UINT24(&curAnswerBuf[curAnswerIdx]); + if (signature24 == MKTAG24('C', 'O', 'D') || signature24 == MKTAG24('F', 'I', 'N')) + innerLoopCond = true; + } + curAnswerBuf += curAnswerIdx + 1; + curAnswerIdx = 0; + } while (!innerLoopCond); + innerLoopCond = false; + int lastOpcodeResult = 1; + do { + int opcodeType = _vm->_script->handleOpcode(ptr + 20 * lastOpcodeResult); + if (_vm->shouldQuit()) + return; + + if (opcodeType == 2) + // GOTO + lastOpcodeResult = _vm->_script->handleGoto(ptr + 20 * lastOpcodeResult); + else if (opcodeType == 3) + // IF + lastOpcodeResult = _vm->_script->handleIf(ptr, lastOpcodeResult); + + if (lastOpcodeResult == -1) + error("Invalid IFF function"); + + if (opcodeType == 1 || opcodeType == 4) + // Already handled opcode or END IF + ++lastOpcodeResult; + else if (!opcodeType || opcodeType == 5) + // EXIT + innerLoopCond = true; + else if (opcodeType == 6) { + // JUMP + _vm->_globals->freeMemory(ptr); + zoneObj = _vm->_objectsMan->_jumpZone; + verbObj = _vm->_objectsMan->_jumpVerb; + outerLoopFl = true; + break; + } + } while (!innerLoopCond); + } while (outerLoopFl); + _vm->_globals->freeMemory(ptr); + _vm->_globals->_saveData->_data[svLastZoneNum] = 0; + return; +} + +void TalkManager::handleForestAnswser(int zone, int verb) { + int indx = 0; + if (verb != 5 || _vm->_globals->_saveData->_data[svLastObjectIndex] != 4) + return; + + if (zone == 22 || zone == 23) { + _vm->_objectsMan->setFlipSprite(0, false); + _vm->_objectsMan->setSpriteIndex(0, 62); + _vm->_objectsMan->showSpecialActionAnimationWithFlip(_vm->_objectsMan->_forestSprite, "2,3,4,5,6,7,8,9,10,11,12,-1,", 4, false); + if (zone == 22) { + _vm->_objectsMan->lockAnimX(6, _vm->_objectsMan->getBobPosX(3)); + _vm->_objectsMan->lockAnimX(8, _vm->_objectsMan->getBobPosX(3)); + } else { // zone == 23 + _vm->_objectsMan->lockAnimX(6, _vm->_objectsMan->getBobPosX(4)); + _vm->_objectsMan->lockAnimX(8, _vm->_objectsMan->getBobPosX(4)); + } + _vm->_objectsMan->stopBobAnimation(3); + _vm->_objectsMan->stopBobAnimation(4); + _vm->_objectsMan->setBobAnimation(6); + _vm->_soundMan->playSample(1); + _vm->_objectsMan->showSpecialActionAnimation(_vm->_objectsMan->_forestSprite, "13,14,15,14,13,12,13,14,15,16,-1,", 4); + do + _vm->_events->refreshScreenAndEvents(); + while (_vm->_objectsMan->getBobAnimDataIdx(6) < 12); + _vm->_objectsMan->stopBobAnimation(6); + _vm->_objectsMan->setBobAnimation(8); + + switch (_vm->_globals->_screenId) { + case 35: + indx = 201; + break; + case 36: + indx = 203; + break; + case 37: + indx = 205; + break; + case 38: + indx = 207; + break; + case 39: + indx = 209; + break; + case 40: + indx = 211; + break; + case 41: + indx = 213; + break; + } + _vm->_globals->_saveData->_data[indx] = 2; + _vm->_linesMan->disableZone(22); + _vm->_linesMan->disableZone(23); + } else if (zone == 20 || zone == 21) { + _vm->_objectsMan->setFlipSprite(0, true); + _vm->_objectsMan->setSpriteIndex(0, 62); + _vm->_objectsMan->showSpecialActionAnimationWithFlip(_vm->_objectsMan->_forestSprite, "2,3,4,5,6,7,8,9,10,11,12,-1,", 4, true); + if (zone == 20) { + _vm->_objectsMan->lockAnimX(5, _vm->_objectsMan->getBobPosX(1)); + _vm->_objectsMan->lockAnimX(7, _vm->_objectsMan->getBobPosX(1)); + } else { // zone == 21 + _vm->_objectsMan->lockAnimX(5, _vm->_objectsMan->getBobPosX(2)); + _vm->_objectsMan->lockAnimX(7, _vm->_objectsMan->getBobPosX(2)); + } + _vm->_objectsMan->stopBobAnimation(1); + _vm->_objectsMan->stopBobAnimation(2); + _vm->_objectsMan->setBobAnimation(5); + _vm->_soundMan->playSample(1); + _vm->_objectsMan->showSpecialActionAnimation(_vm->_objectsMan->_forestSprite, "13,14,15,14,13,12,13,14,15,16,-1,", 4); + do + _vm->_events->refreshScreenAndEvents(); + while (_vm->_objectsMan->getBobAnimDataIdx(5) < 12); + _vm->_objectsMan->stopBobAnimation(5); + _vm->_objectsMan->setBobAnimation(7); + switch (_vm->_globals->_screenId) { + case 35: + indx = 200; + break; + case 36: + indx = 202; + break; + case 37: + indx = 204; + break; + case 38: + indx = 206; + break; + case 39: + indx = 208; + break; + case 40: + indx = 210; + break; + case 41: + indx = 212; + break; + } + _vm->_globals->_saveData->_data[indx] = 2; + _vm->_linesMan->disableZone(21); + _vm->_linesMan->disableZone(20); + } +} + +void TalkManager::animateObject(const Common::String &filename) { + _vm->_fontMan->hideText(5); + _vm->_fontMan->hideText(9); + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->_scrollStatus = 1; + _vm->_linesMan->clearAllZones(); + _vm->_linesMan->resetLines(); + _vm->_objectsMan->resetHidingItems(); + + for (int i = 0; i <= 44; i++) + _vm->_linesMan->_bobZone[i] = 0; + + _vm->_objectsMan->_zoneNum = -1; + _vm->_events->_mouseCursorId = 4; + _vm->_events->changeMouseCursor(0); + bool fileFoundFl = false; + _characterBuffer = _vm->_fileIO->searchCat(filename, RES_PER, fileFoundFl); + _characterSize = _vm->_fileIO->_catalogSize; + if (!fileFoundFl) { + _characterBuffer = _vm->_fileIO->loadFile(filename); + _characterSize = _vm->_fileIO->fileSize(filename); + } + Common::String screenFilename; + Common::String spriteFilename; + Common::String curScreenFilename; + getStringFromBuffer(40, spriteFilename, (const char *)_characterBuffer); + getStringFromBuffer(0, screenFilename, (const char *)_characterBuffer); + getStringFromBuffer(20, curScreenFilename, (const char *)_characterBuffer); + + if (curScreenFilename == "NULL") + curScreenFilename = Common::String::format("IM%d", _vm->_globals->_screenId); + + fileFoundFl = false; + _characterSprite = _vm->_fileIO->searchCat(spriteFilename, RES_SAN, fileFoundFl); + if (!fileFoundFl) + _characterSprite = _vm->_objectsMan->loadSprite(spriteFilename); + else + _characterSprite = _vm->_objectsMan->loadSprite("RES_SAN.RES"); + + _vm->_graphicsMan->backupScreen(); + + if (!_vm->_graphicsMan->_lineNbr) + _vm->_graphicsMan->_scrollOffset = 0; + _vm->_graphicsMan->displayScreen(true); + _paletteBufferIdx = 20 * READ_LE_INT16((uint16 *)_characterBuffer + 42) + 110; + _vm->_graphicsMan->displayScreen(true); + _vm->_objectsMan->_charactersEnabledFl = true; + searchCharacterPalette(_paletteBufferIdx, true); + startCharacterAnim0(_paletteBufferIdx, false); + byte *oldAnswerBufferPtr = _vm->_globals->_answerBuffer; + _vm->_globals->_answerBuffer = NULL; + _vm->_globals->_freezeCharacterFl = true; + _vm->_objectsMan->loadLinkFile(screenFilename); + _vm->_objectsMan->_charactersEnabledFl = true; + _vm->_globals->_actionMoveTo = false; + _vm->_objectsMan->_zoneNum = -1; + initCharacterAnim(); + dialogAnim(); + dialogWait(); + _vm->_graphicsMan->initScreen(screenFilename, 2, true); + _vm->_globals->_freezeCharacterFl = true; + _vm->_objectsMan->_forceZoneFl = true; + _vm->_objectsMan->_zoneNum = -1; + do { + int mouseButton = _vm->_events->getMouseButton(); + if (mouseButton == 1) + _vm->_objectsMan->handleLeftButton(); + else if (mouseButton == 2) + _vm->_objectsMan->handleRightButton(); + + _vm->_linesMan->checkZone(); + if (_vm->_globals->_actionMoveTo) + _vm->_objectsMan->paradise(); + _vm->_events->refreshScreenAndEvents(); + } while (!_vm->_globals->_exitId); + dialogEndTalk(); + dialogTalk(); + clearCharacterAnim(); + clearCharacterAnim(); + _vm->_globals->_introSpeechOffFl = false; + _characterBuffer = _vm->_globals->freeMemory(_characterBuffer); + _characterSprite = _vm->_globals->freeMemory(_characterSprite); + _vm->_graphicsMan->displayScreen(false); + _vm->_linesMan->clearAllZones(); + _vm->_linesMan->resetLines(); + _vm->_objectsMan->resetHidingItems(); + for (int i = 0; i <= 44; i++) + _vm->_linesMan->_bobZone[i] = 0; + + _vm->_globals->freeMemory(_vm->_globals->_answerBuffer); + _vm->_globals->_answerBuffer = oldAnswerBufferPtr; + _vm->_objectsMan->_disableFl = true; + _vm->_objectsMan->loadLinkFile(curScreenFilename); + _vm->_graphicsMan->initScreen(curScreenFilename, 2, true); + _vm->_objectsMan->_disableFl = false; + _vm->_globals->_freezeCharacterFl = false; + if (_vm->_globals->_exitId == 101) + _vm->_globals->_exitId = 0; + + _vm->_graphicsMan->restoreScreen(); + + _vm->_objectsMan->_charactersEnabledFl = false; + _vm->_events->_mouseCursorId = 4; + _vm->_events->changeMouseCursor(4); + _vm->_graphicsMan->setColorPercentage(253, 100, 100, 100); + + if (!_vm->getIsDemo()) + _vm->_graphicsMan->setColorPercentage(254, 0, 0, 0); + + _vm->_graphicsMan->initColorTable(145, 150, _vm->_graphicsMan->_palette); + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + _vm->_graphicsMan->display8BitRect(_vm->_graphicsMan->_backBuffer, _vm->_events->_startPos.x, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + _vm->_graphicsMan->setPaletteVGA256(_vm->_graphicsMan->_palette); + memcpy(_vm->_graphicsMan->_frontBuffer, _vm->_graphicsMan->_backBuffer, 614399); + _vm->_globals->_disableInventFl = false; + _vm->_graphicsMan->updateScreen(); + for (int i = 0; i <= 4; i++) + _vm->_events->refreshScreenAndEvents(); + _vm->_graphicsMan->_scrollStatus = 0; +} + +} // End of namespace Hopkins diff --git a/engines/hopkins/talk.h b/engines/hopkins/talk.h new file mode 100644 index 0000000000..678f52090a --- /dev/null +++ b/engines/hopkins/talk.h @@ -0,0 +1,78 @@ +/* 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. + * + */ + +#ifndef HOPKINS_TALK_H +#define HOPKINS_TALK_H + +#include "common/scummsys.h" +#include "common/str.h" + +namespace Hopkins { + +class HopkinsEngine; + +#define MIN_LETTERS_PER_LINE 65 + +class TalkManager { +private: + HopkinsEngine *_vm; + + Common::String _questionsFilename; + Common::String _answersFilename; + byte *_characterBuffer; + byte *_characterPalette; + size_t _characterSize; + int _dialogueMesgId1, _dialogueMesgId2; + int _dialogueMesgId3, _dialogueMesgId4; + int _paletteBufferIdx; + + void getStringFromBuffer(int srcStart, Common::String &dest, const char *srcData); + int dialogQuestion(bool animatedFl); + int dialogAnswer(int idx, bool animatedFl); + void searchCharacterPalette(int startIdx, bool dark); + void dialogWait(); + void dialogTalk(); + void dialogEndTalk(); + void startCharacterAnim0(int startIndedx, bool readOnlyFl); + void initCharacterAnim(); + void clearCharacterAnim(); + bool searchCharacterAnim(int idx, const byte *bufPerso, int animId, int bufferSize); + int countBoxLines(int idx, const Common::String &file); + void dialogAnim(); + void displayBobDialogAnim(int idx); + +public: + byte *_characterAnim; + byte *_characterSprite; + + TalkManager(HopkinsEngine *vm); + + void startStaticCharacterDialogue(const Common::String &filename); + void startAnimatedCharacterDialogue(const Common::String &filename); + void animateObject(const Common::String &filename); + void handleAnswer(int zone, int verb); + void handleForestAnswser(int zone, int verb); +}; + +} // End of namespace Hopkins + +#endif /* HOPKINS_TALK_H */ |