diff options
Diffstat (limited to 'engines/kyra/sequence')
-rw-r--r-- | engines/kyra/sequence/seqplayer.cpp | 658 | ||||
-rw-r--r-- | engines/kyra/sequence/seqplayer.h | 124 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_darkmoon.cpp | 1643 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_eob.cpp | 1152 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_hof.cpp | 3515 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_hof.h | 74 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_lok.cpp | 2116 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_lol.cpp | 1538 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_mr.cpp | 237 | ||||
-rw-r--r-- | engines/kyra/sequence/sequences_v2.cpp | 130 |
10 files changed, 11187 insertions, 0 deletions
diff --git a/engines/kyra/sequence/seqplayer.cpp b/engines/kyra/sequence/seqplayer.cpp new file mode 100644 index 0000000000..d039a352f8 --- /dev/null +++ b/engines/kyra/sequence/seqplayer.cpp @@ -0,0 +1,658 @@ +/* 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 "kyra/sequence/seqplayer.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" + +#include "common/system.h" + +#define SEQOP(n, x) { n, &SeqPlayer::x, #x } + +namespace Kyra { + +SeqPlayer::SeqPlayer(KyraEngine_LoK *vm, OSystem *system) { + _vm = vm; + _system = system; + + _screen = vm->screen(); + _sound = vm->sound(); + _res = vm->resource(); + + _copyViewOffs = false; + _specialBuffer = 0; + + for (int i = 0; i < ARRAYSIZE(_handShapes); ++i) + _handShapes[i] = 0; + for (int i = 0; i < ARRAYSIZE(_seqMovies); ++i) + _seqMovies[i].movie = 0; +} + +SeqPlayer::~SeqPlayer() { + freeHandShapes(); + + for (int i = 0; i < ARRAYSIZE(_seqMovies); ++i) { + if (!_seqMovies[i].movie) + continue; + _seqMovies[i].movie->close(); + delete _seqMovies[i].movie; + _seqMovies[i].movie = 0; + } +} + +uint8 *SeqPlayer::setPanPages(int pageNum, int shape) { + uint8 *panPage = 0; + const uint8 *data = _screen->getCPagePtr(pageNum); + uint16 numShapes = READ_LE_UINT16(data); + if (shape < numShapes) { + uint32 offs = 0; + if (_vm->gameFlags().useAltShapeHeader) + offs = READ_LE_UINT32(data + 2 + shape * 4); + else + offs = READ_LE_UINT16(data + 2 + shape * 2); + + if (offs != 0) { + data += offs; + uint16 sz = READ_LE_UINT16(data + 6); + panPage = new uint8[sz]; + assert(panPage); + memcpy(panPage, data, sz); + } + } + return panPage; +} + +void SeqPlayer::makeHandShapes() { + _screen->loadBitmap("WRITING.CPS", 3, 3, &_screen->getPalette(0)); + if (_vm->gameFlags().platform == Common::kPlatformMacintosh || _vm->gameFlags().platform == Common::kPlatformAmiga) { + freeHandShapes(); + + int pageBackUp = _screen->_curPage; + _screen->_curPage = 2; + _handShapes[0] = _screen->encodeShape(0, 0, 88, 122, 0); + assert(_handShapes[0]); + _handShapes[1] = _screen->encodeShape(88, 0, 80, 117, 0); + assert(_handShapes[1]); + _handShapes[2] = _screen->encodeShape(168, 0, 117, 124, 0); + assert(_handShapes[2]); + _screen->_curPage = pageBackUp; + } else { + for (int i = 0; i < ARRAYSIZE(_handShapes); ++i) { + if (_handShapes[i]) + delete[] _handShapes[i]; + _handShapes[i] = setPanPages(3, i); + assert(_handShapes[i]); + } + } +} + +void SeqPlayer::freeHandShapes() { + for (int i = 0; i < ARRAYSIZE(_handShapes); ++i) { + delete[] _handShapes[i]; + _handShapes[i] = 0; + } +} + +void SeqPlayer::s1_wsaOpen() { + uint8 wsaObj = *_seqData++; + assert(wsaObj < ARRAYSIZE(_seqMovies)); + uint8 offscreenDecode = *_seqData++; + _seqWsaCurDecodePage = _seqMovies[wsaObj].page = (offscreenDecode == 0) ? 0 : 3; + if (!_seqMovies[wsaObj].movie) + _seqMovies[wsaObj].movie = _vm->createWSAMovie(); + _seqMovies[wsaObj].movie->open(_vm->seqWSATable()[wsaObj], offscreenDecode, 0); + _seqMovies[wsaObj].frame = 0; + _seqMovies[wsaObj].numFrames = _seqMovies[wsaObj].movie->frames() - 1; +} + +void SeqPlayer::s1_wsaClose() { + uint8 wsaObj = *_seqData++; + assert(wsaObj < ARRAYSIZE(_seqMovies)); + if (_seqMovies[wsaObj].movie) + _seqMovies[wsaObj].movie->close(); +} + +void SeqPlayer::s1_wsaPlayFrame() { + uint8 wsaObj = *_seqData++; + assert(wsaObj < ARRAYSIZE(_seqMovies)); + int16 frame = (int8)*_seqData++; + _seqMovies[wsaObj].pos.x = READ_LE_UINT16(_seqData); _seqData += 2; + _seqMovies[wsaObj].pos.y = *_seqData++; + assert(_seqMovies[wsaObj].movie); + _seqMovies[wsaObj].movie->displayFrame(frame, _seqMovies[wsaObj].page, _seqMovies[wsaObj].pos.x, _seqMovies[wsaObj].pos.y, 0, 0, 0); + _seqMovies[wsaObj].frame = frame; +} + +void SeqPlayer::s1_wsaPlayNextFrame() { + uint8 wsaObj = *_seqData++; + assert(wsaObj < ARRAYSIZE(_seqMovies)); + int16 frame = ++_seqMovies[wsaObj].frame; + if (frame > _seqMovies[wsaObj].numFrames) { + frame = 0; + _seqMovies[wsaObj].frame = 0; + } + _seqMovies[wsaObj].movie->displayFrame(frame, _seqMovies[wsaObj].page, _seqMovies[wsaObj].pos.x, _seqMovies[wsaObj].pos.y, 0, 0, 0); +} + +void SeqPlayer::s1_wsaPlayPrevFrame() { + uint8 wsaObj = *_seqData++; + assert(wsaObj < ARRAYSIZE(_seqMovies)); + int16 frame = --_seqMovies[wsaObj].frame; + if (frame < 0) { + frame = _seqMovies[wsaObj].numFrames; + _seqMovies[wsaObj].frame = frame; + } else { + _seqMovies[wsaObj].movie->displayFrame(frame, _seqMovies[wsaObj].page, _seqMovies[wsaObj].pos.x, _seqMovies[wsaObj].pos.y, 0, 0, 0); + } +} + +void SeqPlayer::s1_drawShape() { + uint8 shapeNum = *_seqData++; + int x = READ_LE_UINT16(_seqData); _seqData += 2; + int y = *_seqData++; + _screen->drawShape(2, _handShapes[shapeNum], x, y, 0, 0, 0); +} + +void SeqPlayer::s1_waitTicks() { + uint16 ticks = READ_LE_UINT16(_seqData); _seqData += 2; + _vm->delay(ticks * _vm->tickLength()); +} + +void SeqPlayer::s1_copyWaitTicks() { + s1_copyView(); + s1_waitTicks(); +} + +void SeqPlayer::s1_shuffleScreen() { + _screen->shuffleScreen(0, 16, 320, 128, 2, 0, 0, false); + if (_specialBuffer) + _screen->copyRegionToBuffer(2, 0, 16, 320, 128, _specialBuffer); + _screen->_curPage = 0; +} + +void SeqPlayer::s1_copyView() { + int h = !_copyViewOffs ? 120 : 128; + + if (_specialBuffer && !_copyViewOffs) + _screen->copyToPage0(16, h, 3, _specialBuffer); + else + _screen->copyRegion(0, 16, 0, 16, 320, h, 2, 0); +} + +void SeqPlayer::s1_loopInit() { + uint8 seqLoop = *_seqData++; + if (seqLoop < ARRAYSIZE(_seqLoopTable)) + _seqLoopTable[seqLoop].ptr = _seqData; + else + _seqQuitFlag = true; +} + +void SeqPlayer::s1_loopInc() { + uint8 seqLoop = *_seqData++; + uint16 seqLoopCount = READ_LE_UINT16(_seqData); _seqData += 2; + if (_seqLoopTable[seqLoop].count == 0xFFFF) { + _seqLoopTable[seqLoop].count = seqLoopCount - 1; + _seqData = _seqLoopTable[seqLoop].ptr; + } else if (_seqLoopTable[seqLoop].count == 0) { + _seqLoopTable[seqLoop].count = 0xFFFF; + _seqLoopTable[seqLoop].ptr = 0; + } else { + --_seqLoopTable[seqLoop].count; + _seqData = _seqLoopTable[seqLoop].ptr; + } +} + +void SeqPlayer::s1_skip() { + uint8 a = *_seqData++; + warning("STUB: s1_skip(%d)", a); +} + +void SeqPlayer::s1_loadPalette() { + uint8 colNum = *_seqData++; + + if (_vm->gameFlags().platform == Common::kPlatformAmiga) { + if (!colNum) + _screen->copyPalette(0, 6); + else if (colNum == 3) + _screen->copyPalette(0, 7); + else if (colNum == 4) + _screen->copyPalette(0, 3); + + _screen->setScreenPalette(_screen->getPalette(0)); + } else { + _screen->loadPalette(_vm->seqCOLTable()[colNum], _screen->getPalette(0)); + } +} + +void SeqPlayer::s1_loadBitmap() { + uint8 cpsNum = *_seqData++; + _screen->loadBitmap(_vm->seqCPSTable()[cpsNum], 3, 3, &_screen->getPalette(0)); +} + +void SeqPlayer::s1_fadeToBlack() { + _screen->fadeToBlack(); +} + +void SeqPlayer::s1_printText() { + static const uint8 colorMap[] = { 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0 }; + uint8 txt = *_seqData++; + + if (!_vm->textEnabled()) + return; + + if (_vm->gameFlags().platform == Common::kPlatformAmiga) + _screen->fillRect(0, 180, 319, 195, 0); + else + _screen->fillRect(0, 180, 319, 195, 12); + _screen->setTextColorMap(colorMap); + if (!_seqDisplayTextFlag) { + const char *str = _vm->seqTextsTable()[txt]; + int x = (Screen::SCREEN_W - _screen->getTextWidth(str)) / 2; + _screen->printText(str, x, 180, 0xF, 0xC); + } else { + _seqDisplayedTextTimer = _system->getMillis() + 1000 / ((_vm->gameFlags().lang == Common::FR_FRA) ? 120 : 60); + _seqDisplayedText = txt; + _seqDisplayedChar = 0; + const char *str = _vm->seqTextsTable()[_seqDisplayedText]; + _seqDisplayedTextX = (Screen::SCREEN_W - _screen->getTextWidth(str)) / 2; + } +} + +void SeqPlayer::s1_printTalkText() { + uint8 txt = *_seqData++; + int x = READ_LE_UINT16(_seqData); _seqData += 2; + int y = *_seqData++; + uint8 fillColor = *_seqData++; + + if (!_vm->textEnabled()) + return; + + int b; + if (_seqTalkTextPrinted && !_seqTalkTextRestored) { + if (_seqWsaCurDecodePage != 0 && !_specialBuffer) + b = 2; + else + b = 0; + _vm->text()->restoreTalkTextMessageBkgd(2, b); + } + _seqTalkTextPrinted = true; + _seqTalkTextRestored = false; + if (_seqWsaCurDecodePage != 0 && !_specialBuffer) + b = 2; + else + b = 0; + _vm->text()->printTalkTextMessage(_vm->seqTextsTable()[txt], x, y, fillColor, b, 2); +} + +void SeqPlayer::s1_restoreTalkText() { + if (_seqTalkTextPrinted && !_seqTalkTextRestored && _vm->textEnabled()) { + int b; + if (_seqWsaCurDecodePage != 0 && !_specialBuffer) + b = 2; + else + b = 0; + _vm->text()->restoreTalkTextMessageBkgd(2, b); + _seqTalkTextRestored = true; + } +} + +void SeqPlayer::s1_clearCurrentScreen() { + if (_vm->gameFlags().platform == Common::kPlatformAmiga) + _screen->fillRect(10, 180, 319, 195, 0); + else + _screen->fillRect(10, 180, 319, 196, 0xC); +} + +void SeqPlayer::s1_break() { + // Do nothing +} + +void SeqPlayer::s1_fadeFromBlack() { + _screen->fadeFromBlack(); +} + +void SeqPlayer::s1_copyRegion() { + uint8 srcPage = *_seqData++; + uint8 dstPage = *_seqData++; + _screen->copyRegion(0, 0, 0, 0, 320, 200, srcPage, dstPage); +} + +void SeqPlayer::s1_copyRegionSpecial() { + static const uint8 colorMap[] = { 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0 }; + const char *copyStr = 0; + if (!_vm->gameFlags().isTalkie) + copyStr = "Copyright (c) 1992 Westwood Studios"; + else + copyStr = "Copyright (c) 1992,1993 Westwood Studios"; + + uint8 so = *_seqData++; + switch (so) { + case 0: + if (_vm->gameFlags().platform == Common::kPlatformAmiga) + _screen->copyRegion(0, 0, 0, 47, 312, 76, 2, 0); + else + _screen->copyRegion(0, 0, 0, 47, 320, 77, 2, 0); + break; + case 1: + if (_vm->gameFlags().platform == Common::kPlatformAmiga) + _screen->copyRegion(0, 0, 8, 47, 312, 55, 2, 0); + else + _screen->copyRegion(0, 0, 0, 47, 320, 56, 2, 0); + break; + case 2: + if (_vm->gameFlags().platform == Common::kPlatformAmiga) { + _screen->copyRegion(104, 72, 104, 72, 40, 87, 2, 0); + _screen->copyRegion(128, 159, 128, 159, 32, 17, 2, 0); + _screen->copyRegion(160, 105, 160, 105, 32, 9, 2, 0); + _screen->copyRegion(200, 83, 200, 83, 88, 93, 2, 0); + } else { + _screen->copyRegion(107, 72, 107, 72, 43, 87, 2, 0); + _screen->copyRegion(130, 159, 130, 159, 35, 17, 2, 0); + _screen->copyRegion(165, 105, 165, 105, 32, 9, 2, 0); + _screen->copyRegion(206, 83, 206, 83, 94, 93, 2, 0); + } + break; + case 3: + _screen->copyRegion(152, 56, 152, 56, 48, 48, 2, 0); + break; + case 4: { + _screen->_charWidth = -2; + const int x = (Screen::SCREEN_W - _screen->getTextWidth(copyStr)) / 2; + const int y = 179; + _screen->setTextColorMap(colorMap); + if (_vm->gameFlags().platform != Common::kPlatformAmiga) + _screen->printText(copyStr, x + 1, y + 1, 0xB, 0xC); + _screen->printText(copyStr, x, y, 0xF, 0xC); + } break; + case 5: + _screen->_curPage = 2; + break; + default: + error("Invalid subopcode %d for s1_copyRegionSpecial", so); + } +} + +void SeqPlayer::s1_fillRect() { + int x1 = READ_LE_UINT16(_seqData); _seqData += 2; + int y1 = *_seqData++; + int x2 = READ_LE_UINT16(_seqData); _seqData += 2; + int y2 = *_seqData++; + uint8 color = *_seqData++; + uint8 page = *_seqData++; + _screen->fillRect(x1, y1, x2, y2, color, page); +} + +void SeqPlayer::s1_playEffect() { + uint8 track = *_seqData++; + _vm->delay(3 * _vm->tickLength()); + + if (_vm->gameFlags().platform == Common::kPlatformPC98) { + if (track > 21 && track < 38) + track -= 22; + else + return; + } + + _sound->playSoundEffect(track); +} + +void SeqPlayer::s1_playTrack() { + uint8 msg = *_seqData++; + + if (msg == 0 && _vm->gameFlags().platform == Common::kPlatformPC98) { + _sound->haltTrack(); + } else if (msg == 1) { + _sound->beginFadeOut(); + } else { + _sound->haltTrack(); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns) + msg += 2; + _sound->playTrack(msg); + } +} + +void SeqPlayer::s1_allocTempBuffer() { + if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) { + _seqQuitFlag = true; + } else { + if (!_specialBuffer && !_copyViewOffs) { + _specialBuffer = new uint8[40960]; + assert(_specialBuffer); + _screen->copyRegionToBuffer(2, 0, 16, 320, 128, _specialBuffer); + } + } +} + +void SeqPlayer::s1_textDisplayEnable() { + _seqDisplayTextFlag = true; +} + +void SeqPlayer::s1_textDisplayDisable() { + _seqDisplayTextFlag = false; +} + +void SeqPlayer::s1_endOfScript() { + _seqQuitFlag = true; +} + +void SeqPlayer::s1_loadIntroVRM() { + _res->loadPakFile("INTRO.VRM"); +} + +void SeqPlayer::s1_playVocFile() { + _vm->snd_voiceWaitForFinish(false); + uint8 a = *_seqData++; + if (_vm->speechEnabled()) + _vm->snd_playVoiceFile(a); +} + +void SeqPlayer::s1_miscUnk3() { + warning("STUB: s1_miscUnk3"); +} + +void SeqPlayer::s1_prefetchVocFile() { + _seqData++; + // we do not have to prefetch the vocfiles on modern systems +} + +bool SeqPlayer::playSequence(const uint8 *seqData, bool skipSeq) { + assert(seqData); + + static const SeqEntry floppySeqProcs[] = { + // 0x00 + SEQOP(3, s1_wsaOpen), + SEQOP(2, s1_wsaClose), + SEQOP(6, s1_wsaPlayFrame), + SEQOP(2, s1_wsaPlayNextFrame), + // 0x04 + SEQOP(2, s1_wsaPlayPrevFrame), + SEQOP(5, s1_drawShape), + SEQOP(3, s1_waitTicks), + SEQOP(3, s1_copyWaitTicks), + // 0x08 + SEQOP(1, s1_shuffleScreen), + SEQOP(1, s1_copyView), + SEQOP(2, s1_loopInit), + SEQOP(4, s1_loopInc), + // 0x0C + SEQOP(2, s1_loadPalette), + SEQOP(2, s1_loadBitmap), + SEQOP(1, s1_fadeToBlack), + SEQOP(2, s1_printText), + // 0x10 + SEQOP(6, s1_printTalkText), + SEQOP(1, s1_restoreTalkText), + SEQOP(1, s1_clearCurrentScreen), + SEQOP(1, s1_break), + // 0x14 + SEQOP(1, s1_fadeFromBlack), + SEQOP(3, s1_copyRegion), + SEQOP(2, s1_copyRegionSpecial), + SEQOP(9, s1_fillRect), + // 0x18 + SEQOP(2, s1_playEffect), + SEQOP(2, s1_playTrack), + SEQOP(1, s1_allocTempBuffer), + SEQOP(1, s1_textDisplayEnable), + // 0x1C + SEQOP(1, s1_textDisplayDisable), + SEQOP(1, s1_endOfScript) + }; + + static const SeqEntry cdromSeqProcs[] = { + // 0x00 + SEQOP(3, s1_wsaOpen), + SEQOP(2, s1_wsaClose), + SEQOP(6, s1_wsaPlayFrame), + SEQOP(2, s1_wsaPlayNextFrame), + // 0x04 + SEQOP(2, s1_wsaPlayPrevFrame), + SEQOP(5, s1_drawShape), + SEQOP(3, s1_waitTicks), + SEQOP(3, s1_waitTicks), + // 0x08 + SEQOP(3, s1_copyWaitTicks), + SEQOP(1, s1_shuffleScreen), + SEQOP(1, s1_copyView), + SEQOP(2, s1_loopInit), + // 0x0C + SEQOP(4, s1_loopInc), + SEQOP(4, s1_loopInc), + SEQOP(2, s1_skip), + SEQOP(2, s1_loadPalette), + // 0x10 + SEQOP(2, s1_loadBitmap), + SEQOP(1, s1_fadeToBlack), + SEQOP(2, s1_printText), + SEQOP(6, s1_printTalkText), + // 0x14 + SEQOP(1, s1_restoreTalkText), + SEQOP(1, s1_clearCurrentScreen), + SEQOP(1, s1_break), + SEQOP(1, s1_fadeFromBlack), + // 0x18 + SEQOP(3, s1_copyRegion), + SEQOP(2, s1_copyRegionSpecial), + SEQOP(9, s1_fillRect), + SEQOP(2, s1_playEffect), + // 0x1C + SEQOP(2, s1_playTrack), + SEQOP(1, s1_allocTempBuffer), + SEQOP(1, s1_textDisplayEnable), + SEQOP(1, s1_textDisplayDisable), + // 0x20 + SEQOP(1, s1_endOfScript), + SEQOP(1, s1_loadIntroVRM), + SEQOP(2, s1_playVocFile), + SEQOP(1, s1_miscUnk3), + // 0x24 + SEQOP(2, s1_prefetchVocFile) + }; + + const SeqEntry *commands; + int numCommands; + + if (_vm->gameFlags().isTalkie) { + commands = cdromSeqProcs; + numCommands = ARRAYSIZE(cdromSeqProcs); + } else { + commands = floppySeqProcs; + numCommands = ARRAYSIZE(floppySeqProcs); + } + + bool seqSkippedFlag = false; + + _seqData = seqData; + + _seqDisplayedTextTimer = 0xFFFFFFFF; + _seqDisplayTextFlag = false; + _seqDisplayedTextX = 0; + _seqDisplayedText = 0; + _seqDisplayedChar = 0; + _seqTalkTextRestored = false; + _seqTalkTextPrinted = false; + + _seqQuitFlag = false; + _seqWsaCurDecodePage = 0; + + for (int i = 0; i < 20; ++i) { + _seqLoopTable[i].ptr = 0; + _seqLoopTable[i].count = 0xFFFF; + } + + memset(_seqMovies, 0, sizeof(_seqMovies)); + + _screen->_curPage = 0; + while (!_seqQuitFlag && !_vm->shouldQuit()) { + if (skipSeq && _vm->seq_skipSequence()) { + while (1) { + uint8 code = *_seqData; + if (commands[code].proc == &SeqPlayer::s1_endOfScript || commands[code].proc == &SeqPlayer::s1_break) + break; + + _seqData += commands[code].len; + } + skipSeq = false; + seqSkippedFlag = true; + } + // used in Kallak writing intro + if (_seqDisplayTextFlag && _seqDisplayedTextTimer != 0xFFFFFFFF && _vm->textEnabled()) { + if (_seqDisplayedTextTimer < _system->getMillis()) { + char charStr[3]; + charStr[0] = _vm->seqTextsTable()[_seqDisplayedText][_seqDisplayedChar]; + charStr[1] = charStr[2] = '\0'; + if (_vm->gameFlags().lang == Common::JA_JPN) + charStr[1] = _vm->seqTextsTable()[_seqDisplayedText][++_seqDisplayedChar]; + _screen->printText(charStr, _seqDisplayedTextX, 180, 0xF, 0xC); + _seqDisplayedTextX += _screen->getCharWidth((uint8)charStr[0]); + ++_seqDisplayedChar; + + if (_vm->seqTextsTable()[_seqDisplayedText][_seqDisplayedChar] == '\0') + _seqDisplayedTextTimer = 0xFFFFFFFF; + else + _seqDisplayedTextTimer = _system->getMillis() + 1000 / ((_vm->gameFlags().lang == Common::FR_FRA) ? 120 : 60); + } + } + + uint8 seqCode = *_seqData++; + if (seqCode < numCommands) { + SeqProc currentProc = commands[seqCode].proc; + debugC(5, kDebugLevelSequence, "0x%.4X seqCode = %d (%s)", (uint16)(_seqData - 1 - seqData), seqCode, commands[seqCode].desc); + (this->*currentProc)(); + } else { + error("Invalid sequence opcode %d called from 0x%.04X", seqCode, (uint16)(_seqData - 1 - seqData)); + } + + _screen->updateScreen(); + } + delete[] _specialBuffer; + _specialBuffer = 0; + + for (uint i = 0; i < ARRAYSIZE(_seqMovies); ++i) { + delete _seqMovies[i].movie; + _seqMovies[i].movie = 0; + } + return seqSkippedFlag; +} + + +} // End of namespace Kyra diff --git a/engines/kyra/sequence/seqplayer.h b/engines/kyra/sequence/seqplayer.h new file mode 100644 index 0000000000..8ca7fbac1b --- /dev/null +++ b/engines/kyra/sequence/seqplayer.h @@ -0,0 +1,124 @@ +/* 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 KYRA_SEQPLAYER_H +#define KYRA_SEQPLAYER_H + +#include "kyra/engine/kyra_lok.h" + +namespace Kyra { + +class SeqPlayer { +public: + SeqPlayer(KyraEngine_LoK *vm, OSystem *system); + ~SeqPlayer(); + + void setCopyViewOffs(bool offs) { + _copyViewOffs = offs; + } + + void makeHandShapes(); + void freeHandShapes(); + + bool playSequence(const uint8 *seqData, bool skipSeq); + + uint8 *setPanPages(int pageNum, int shape); +protected: + KyraEngine_LoK *_vm; + OSystem *_system; + Screen *_screen; + Sound *_sound; + Resource *_res; + + uint8 *_handShapes[3]; + bool _copyViewOffs; + + typedef void (SeqPlayer::*SeqProc)(); + struct SeqEntry { + uint8 len; + SeqProc proc; + const char *desc; + }; + + // the sequence procs + void s1_wsaOpen(); + void s1_wsaClose(); + void s1_wsaPlayFrame(); + void s1_wsaPlayNextFrame(); + void s1_wsaPlayPrevFrame(); + void s1_drawShape(); + void s1_waitTicks(); + void s1_copyWaitTicks(); + void s1_shuffleScreen(); + void s1_copyView(); + void s1_loopInit(); + void s1_loopInc(); + void s1_skip(); + void s1_loadPalette(); + void s1_loadBitmap(); + void s1_fadeToBlack(); + void s1_printText(); + void s1_printTalkText(); + void s1_restoreTalkText(); + void s1_clearCurrentScreen(); + void s1_break(); + void s1_fadeFromBlack(); + void s1_copyRegion(); + void s1_copyRegionSpecial(); + void s1_fillRect(); + void s1_playEffect(); + void s1_playTrack(); + void s1_allocTempBuffer(); + void s1_textDisplayEnable(); + void s1_textDisplayDisable(); + void s1_endOfScript(); + void s1_loadIntroVRM(); + void s1_playVocFile(); + void s1_miscUnk3(); + void s1_prefetchVocFile(); + + struct SeqMovie { + Movie *movie; + int32 page; + int16 frame; + int16 numFrames; + Common::Point pos; + }; + + const uint8 *_seqData; + uint8 *_specialBuffer; + SeqMovie _seqMovies[12]; + SeqLoop _seqLoopTable[20]; + uint16 _seqWsaCurDecodePage; + uint32 _seqDisplayedTextTimer; + bool _seqDisplayTextFlag; + uint8 _seqDisplayedText; + uint8 _seqDisplayedChar; + uint16 _seqDisplayedTextX; + bool _seqTalkTextPrinted; + bool _seqTalkTextRestored; + bool _seqQuitFlag; +}; + +} // End of namespace Kyra + +#endif diff --git a/engines/kyra/sequence/sequences_darkmoon.cpp b/engines/kyra/sequence/sequences_darkmoon.cpp new file mode 100644 index 0000000000..532591e9b0 --- /dev/null +++ b/engines/kyra/sequence/sequences_darkmoon.cpp @@ -0,0 +1,1643 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/engine/darkmoon.h" +#include "kyra/graphics/screen_eob.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" + +#include "common/system.h" + +#include "base/version.h" + +namespace Kyra { + +class DarkmoonSequenceHelper { +friend class DarkMoonEngine; +public: + enum Mode { + kIntro, + kFinale + }; + + DarkmoonSequenceHelper(OSystem *system, DarkMoonEngine *vm, Screen_EoB *screen, Mode mode); + ~DarkmoonSequenceHelper(); + + void loadScene(int index, int pageNum); + void animCommand(int index, int del = -1); + + void printText(int index, int color); + void fadeText(); + + void update(int srcPage); + + void setPalette(int index); + void fadePalette(int index, int del); + void copyPalette(int srcIndex, int destIndex); + + void initDelayedPaletteFade(int palIndex, int rate); + bool processDelayedPaletteFade(); + + void delay(uint32 ticks); + void waitForSongNotifier(int index, bool introUpdateAnim = false); + +private: + void setPaletteWithoutTextColor(int index); + + OSystem *_system; + DarkMoonEngine *_vm; + Screen_EoB *_screen; + + struct Config { + Config(const char *const *str, const char *const *cpsfiles, const uint8 **cpsdata, const char *const *pal, const DarkMoonShapeDef **shp, const DarkMoonAnimCommand **anim, bool loadScenePalette, bool paletteFading, bool animCmdRestorePalette, bool shapeBackgroundFading, int animPalOffset, int animType1ShapeDim, bool animCmd5SetPalette, int animCmd5ExtraPage) : strings(str), cpsFiles(cpsfiles), cpsData(cpsdata), palFiles(pal), shapeDefs(shp), animData(anim), loadScenePal(loadScenePalette), palFading(paletteFading), animCmdRestorePal(animCmdRestorePalette), shpBackgroundFading(shapeBackgroundFading), animPalOffs(animPalOffset), animCmd1ShapeFrame(animType1ShapeDim), animCmd5SetPal(animCmd5SetPalette), animCmd5AltPage(animCmd5ExtraPage) {} + const char *const *strings; + const char *const *cpsFiles; + const uint8 **cpsData; + const char *const *palFiles; + const DarkMoonShapeDef **shapeDefs; + const DarkMoonAnimCommand **animData; + bool loadScenePal; + bool palFading; + bool animCmdRestorePal; + bool shpBackgroundFading; + int animPalOffs; + int animCmd1ShapeFrame; + bool animCmd5SetPal; + int animCmd5AltPage; + }; + + const Config *_config; + + Palette *_palettes[13]; + uint8 *_fadingTables[7]; + + const uint8 **_shapes; + + uint32 _fadePalTimer; + int _fadePalRate; + int _fadePalIndex; + + Screen::FontId _prevFont; + + static const char *const _palFilesIntroVGA[]; + static const char *const _palFilesIntroEGA[]; + static const char *const _palFilesFinaleVGA[]; + static const char *const _palFilesFinaleEGA[]; +}; + +int DarkMoonEngine::mainMenu() { + int menuChoice = _menuChoiceInit; + _menuChoiceInit = 0; + + _sound->selectAudioResourceSet(kMusicIntro); + _sound->loadSoundFile("INTRO"); + + Screen::FontId of = _screen->_currentFont; + int op = 0; + Common::SeekableReadStream *s = 0; + + while (menuChoice >= 0 && !shouldQuit()) { + switch (menuChoice) { + case 0: { + if (_flags.platform == Common::kPlatformFMTowns) { + _screen->loadPalette("MENU.PAL", _screen->getPalette(0)); + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->loadEoBBitmap("MENU", 0, 3, 3, 2); + } else { + s = _res->createReadStream("XENU.CPS"); + if (s) { + s->read(_screen->getPalette(0).getData(), 768); + _screen->loadFileDataToPage(s, 3, 64000); + delete s; + } else { + _screen->loadBitmap("MENU.CPS", 3, 3, &_screen->getPalette(0)); + } + + if (_configRenderMode == Common::kRenderEGA) + _screen->loadPalette("MENU.EGA", _screen->getPalette(0)); + } + + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->convertPage(3, 2, 0); + + of = _screen->setFont(Screen::FID_6_FNT); + op = _screen->setCurPage(2); + Common::String versionString(Common::String::format("ScummVM %s", gScummVMVersion)); + _screen->printText(versionString.c_str(), 267 - versionString.size() * 6, _flags.platform == Common::kPlatformFMTowns ? 152 : 160, 13, 0); + _screen->setFont(of); + _screen->_curPage = op; + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->shadeRect(78, 99, 249, 141, 4); + _screen->updateScreen(); + _allowImport = true; + menuChoice = mainMenuLoop(); + _allowImport = false; + } break; + + case 1: + // load game in progress + menuChoice = -1; + break; + + case 2: + // create new party + menuChoice = -2; + break; + + case 3: + // transfer party + menuChoice = -3; + break; + + case 4: + // play intro + seq_playIntro(); + menuChoice = 0; + break; + + case 5: + // quit + menuChoice = -5; + break; + } + } + + return shouldQuit() ? -5 : menuChoice; +} + +int DarkMoonEngine::mainMenuLoop() { + int sel = -1; + do { + _screen->setScreenDim(6); + _gui->simpleMenu_setup(6, 0, _mainMenuStrings, -1, 0, 0); + + while (sel == -1 && !shouldQuit()) + sel = _gui->simpleMenu_process(6, _mainMenuStrings, 0, -1, 0); + } while ((sel < 0 || sel > 5) && !shouldQuit()); + + if (_flags.platform == Common::kPlatformFMTowns && sel == 2) { + townsUtilitiesMenu(); + sel = -1; + } + + return sel + 1; +} + +void DarkMoonEngine::townsUtilitiesMenu() { + _screen->copyRegion(78, 99, 78, 99, 172, 43, 2, 0, Screen::CR_NO_P_CHECK); + int sel = -1; + do { + _gui->simpleMenu_setup(8, 0, _utilMenuStrings, -1, 0, 0); + while (sel == -1 && !shouldQuit()) + sel = _gui->simpleMenu_process(8, _utilMenuStrings, 0, -1, 0); + if (sel == 0) { + _config2431 ^= true; + sel = -1; + } + } while ((sel < 0 || sel > 1) && !shouldQuit()); +} + +void DarkMoonEngine::seq_playIntro() { + DarkmoonSequenceHelper sq(_system, this, _screen, DarkmoonSequenceHelper::kIntro); + + _screen->setCurPage(0); + _screen->clearCurPage(); + + snd_stopSound(); + + sq.loadScene(4, 2); + sq.loadScene(0, 2); + sq.delay(1); + + if (!skipFlag() && !shouldQuit()) + snd_playSong(12); + + _screen->copyRegion(0, 0, 8, 8, 304, 128, 2, 0, Screen::CR_NO_P_CHECK); + sq.setPalette(9); + sq.fadePalette(0, 3); + + _screen->setCurPage(2); + _screen->setClearScreenDim(17); + _screen->setCurPage(0); + + removeInputTop(); + sq.delay(18); + + sq.animCommand(3, 18); + sq.animCommand(6, 18); + sq.animCommand(0); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 229 : 1); + + sq.animCommand(_configRenderMode == Common::kRenderEGA ? 12 : 11); + sq.animCommand(7, 6); + sq.animCommand(2, 6); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 447 : 2); + + sq.animCommand(_configRenderMode == Common::kRenderEGA ? 39 : 38); + sq.animCommand(3); + sq.animCommand(8); + sq.animCommand(1, 10); + sq.animCommand(0, 6); + sq.animCommand(2); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 670 : 3); + + _screen->setClearScreenDim(17); + _screen->setCurPage(2); + _screen->setClearScreenDim(17); + _screen->setCurPage(0); + + sq.animCommand(_configRenderMode == Common::kRenderEGA ? 41 : 40); + sq.animCommand(7, 18); + + sq.printText(0, 16); // You were settling... + sq.animCommand(7, 90); + sq.fadeText(); + + sq.printText(1, 16); // Then a note was slipped to you + sq.animCommand(8); + sq.animCommand(2, 72); + sq.fadeText(); + + sq.printText(2, 16); // It was from your friend Khelben Blackstaff... + sq.animCommand(2); + sq.animCommand(6, 36); + sq.animCommand(3); + sq.fadeText(); + + sq.printText(3, 16); // The message was urgent. + + sq.loadScene(1, 2); + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 1380 : 4); + + // intro scroll + if (!skipFlag() && !shouldQuit()) { + if (_configRenderMode == Common::kRenderEGA) { + for (int i = 0; i < 35; i++) { + uint32 endtime = _system->getMillis() + 2 * _tickLength; + _screen->copyRegion(16, 8, 8, 8, 296, 128, 0, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(i << 3, 0, 304, 8, 8, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + if (i == 12) + sq.animCommand(42); + else if (i == 25) + snd_playSoundEffect(11); + delayUntil(endtime); + } + } else { + for (int i = 0; i < 280; i += 3) { + uint32 endtime = _system->getMillis() + _tickLength; + _screen->copyRegion(11, 8, 8, 8, 301, 128, 0, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(i, 0, 309, 8, 3, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + if (i == 96) + sq.animCommand(42); + delayUntil(endtime); + } + } + } + + _screen->copyRegion(8, 8, 0, 0, 304, 128, 0, 2, Screen::CR_NO_P_CHECK); + sq.animCommand(4); + sq.fadeText(); + sq.delay(10); + + sq.loadScene(2, 2); + sq.update(2); + sq.delay(10); + + sq.printText(4, 16); // What could Khelben want? + sq.delay(25); + + sq.loadScene(3, 2); + sq.delay(54); + sq.animCommand(13); + _screen->copyRegion(104, 16, 96, 8, 120, 100, 0, 2, Screen::CR_NO_P_CHECK); + sq.fadeText(); + + sq.printText(5, 15); // Welcome, please come in + sq.animCommand(10); + sq.animCommand(10); + sq.animCommand(9); + sq.animCommand(9); + sq.fadeText(); + + sq.printText(6, 15); // Khelben awaits you in his study + for (int i = 0; i < 3; i++) + sq.animCommand(10); + sq.animCommand(9); + sq.animCommand(14); + sq.loadScene(5, 2); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 2037 : 5); + + sq.fadeText(); + _screen->clearCurPage(); + _screen->updateScreen(); + + for (int i = 0; i < 6; i++) + sq.animCommand(15); + + if (_configRenderMode == Common::kRenderEGA && !skipFlag() && !shouldQuit()) { + _screen->loadPalette("INTRO.EGA", _screen->getPalette(0)); + _screen->setScreenPalette(_screen->getPalette(0)); + } + + sq.loadScene(6, 2); + sq.loadScene(7, 2); + _screen->clearCurPage(); + sq.update(2); + + sq.animCommand(16); + sq.printText(7, 15); // Thank you for coming so quickly + sq.animCommand(16); + sq.animCommand(17); + for (int i = 0; i < 3; i++) + sq.animCommand(16); + sq.fadeText(); + sq.animCommand(16); + + sq.loadScene(8, 2); + sq.update(2); + sq.animCommand(32); + sq.printText(8, 15); // I am troubled my friend + sq.animCommand(33); + sq.animCommand(33); + for (int i = 0; i < 4; i++) + sq.animCommand(32); + sq.fadeText(); + + sq.printText(9, 15); // Ancient evil stirs in the Temple Darkmoon + sq.animCommand(33); + sq.animCommand(43); + sq.animCommand(33); + for (int i = 0; i < 3; i++) + sq.animCommand(32); + sq.fadeText(); + + sq.printText(10, 15); // I fear for the safety of our city + for (int i = 0; i < 4; i++) + sq.animCommand(33); + sq.animCommand(32); + sq.animCommand(32); + + sq.loadScene(9, 2); + sq.fadeText(); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 3000 : 6); + + sq.update(2); + sq.animCommand(34); + + sq.printText(11, 15); // I need your help + for (int i = 0; i < 3; i++) + sq.animCommand(34); + sq.animCommand(35); + for (int i = 0; i < 4; i++) + sq.animCommand(34); + sq.fadeText(); + + sq.loadScene(12, 2); + sq.update(2); + sq.loadScene(6, 2); + sq.animCommand(18); + + sq.printText(12, 15); // Three nights ago I sent forth a scout + sq.animCommand(19); + sq.animCommand(20); + sq.animCommand(22); + sq.animCommand(19); + sq.animCommand(20); + sq.animCommand(18); + sq.fadeText(); + + sq.printText(13, 15); // She has not yet returned + sq.animCommand(20); + sq.animCommand(19); + sq.animCommand(23); + sq.animCommand(24); + sq.animCommand(20); + sq.animCommand(19); + sq.animCommand(17); + sq.animCommand(18); + sq.fadeText(); + + sq.printText(14, 15); // I fear for her safety + sq.animCommand(19); + sq.animCommand(20); + sq.animCommand(20); + sq.animCommand(18); + sq.animCommand(25); + sq.animCommand(18); + sq.animCommand(18); + sq.fadeText(); + sq.animCommand(18); + sq.animCommand(18); + + sq.printText(15, 15); // Take this coin + sq.animCommand(28); + sq.animCommand(19); + sq.animCommand(20); + sq.animCommand(18); + sq.animCommand(18); + sq.fadeText(); + + sq.loadScene(10, 2); + _screen->clearCurPage(); + _screen->updateScreen(); + + sq.animCommand(37, 18); + sq.animCommand(36, 36); + + sq.loadScene(12, 2); + _screen->clearCurPage(); + sq.update(2); + + sq.loadScene(11, 2); + sq.printText(16, 15); // I will use it to contact you + sq.animCommand(19); + sq.animCommand(20); + sq.animCommand(20); + sq.animCommand(18); + sq.animCommand(18); + sq.fadeText(); + + sq.printText(17, 15); // You must act quickly + sq.animCommand(19); + sq.animCommand(20); + sq.animCommand(19); + sq.animCommand(18); + sq.animCommand(18); + sq.fadeText(); + sq.animCommand(18); + + sq.printText(18, 15); // I will teleport you near Darkmoon + sq.animCommand(20); + sq.animCommand(27); + sq.animCommand(20); + sq.animCommand(19); + sq.animCommand(18); + sq.animCommand(18); + sq.fadeText(); + sq.animCommand(18); + + sq.printText(19, 15); // May luck be with you my friend + sq.animCommand(19); + sq.animCommand(19); + sq.animCommand(20); + sq.animCommand(18); + sq.fadeText(); + sq.animCommand(29); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 4475 : 7); + + sq.animCommand(30); + sq.animCommand(31); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 4825 : 8, true); + + if (skipFlag() || shouldQuit()) { + snd_fadeOut(); + } else { + _screen->setScreenDim(17); + _screen->clearCurDim(); + snd_playSoundEffect(14); + + if (_configRenderMode != Common::kRenderEGA) + sq.fadePalette(10, 1); + _screen->setClearScreenDim(18); + sq.delay(6); + if (_configRenderMode != Common::kRenderEGA) + sq.fadePalette(9, 1); + _screen->clearCurPage(); + } + sq.fadePalette(9, 10); +} + +void DarkMoonEngine::seq_playFinale() { + DarkmoonSequenceHelper sq(_system, this, _screen, DarkmoonSequenceHelper::kFinale); + + _screen->setCurPage(0); + + _sound->loadSoundFile(_flags.platform == Common::kPlatformFMTowns ? "FINALE" : "FINALE1"); + snd_stopSound(); + sq.delay(3); + + _screen->clearCurPage(); + _screen->clearPage(2); + _screen->updateScreen(); + + sq.loadScene(0, 2); + sq.delay(18); + + if (!skipFlag() && !shouldQuit()) + snd_playSong(1); + sq.update(2); + + sq.loadScene(1, 2); + + sq.animCommand(0); + sq.animCommand(0); + for (int i = 0; i < 3; i++) + sq.animCommand(2); + sq.animCommand(1); + sq.animCommand(2); + sq.animCommand(2); + + sq.printText(0, 10); // Finally, Dran has been defeated + for (int i = 0; i < 7; i++) + sq.animCommand(2); + sq.fadeText(); + sq.animCommand(2); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 475 : 1); + + sq.printText(1, 10); // Suddenly, your friend Khelben appears + sq.animCommand(4); + for (int i = 0; i < 3; i++) + sq.animCommand(2); + sq.fadeText(); + + sq.printText(2, 15); // Greetings, my victorious friends + for (int i = 0; i < 4; i++) + sq.animCommand(5); + sq.animCommand(2); + sq.animCommand(2); + sq.fadeText(); + sq.animCommand(6); + + sq.printText(3, 15); // You have defeated Dran + for (int i = 0; i < 5; i++) + sq.animCommand(5); + sq.animCommand(2); + sq.animCommand(2); + sq.fadeText(); + + sq.printText(4, 15); // I did not know Dran was a dragon + for (int i = 0; i < 4; i++) + sq.animCommand(5); + sq.animCommand(2); + sq.animCommand(2); + sq.fadeText(); + + sq.printText(5, 15); // He must have been over 300 years old + for (int i = 0; i < 4; i++) + sq.animCommand(5); + sq.animCommand(2); + sq.animCommand(2); + sq.fadeText(); + + sq.printText(6, 15); // His power is gone + for (int i = 0; i < 3; i++) + sq.animCommand(5); + sq.animCommand(2); + sq.animCommand(2); + sq.fadeText(); + + sq.printText(7, 15); // But Darkmoon is still a source of great evil + for (int i = 0; i < 4; i++) + sq.animCommand(5); + sq.animCommand(2); + sq.animCommand(2); + sq.fadeText(); + + sq.printText(8, 15); // And many of his minions remain + for (int i = 0; i < 4; i++) + sq.animCommand(5); + sq.animCommand(2); + sq.animCommand(2); + sq.fadeText(); + + sq.loadScene(2, 2); + sq.update(2); + sq.loadScene(3, 2); + _screen->copyRegion(8, 8, 0, 0, 304, 128, 0, 2, Screen::CR_NO_P_CHECK); + + sq.printText(9, 15); // Now we must leave this place + sq.animCommand(7); + sq.animCommand(8); + sq.animCommand(7); + sq.animCommand(7, 36); + sq.fadeText(); + + sq.printText(10, 15); // So my forces can destroy it.. + for (int i = 0; i < 3; i++) + sq.animCommand(7); + sq.animCommand(8); + sq.animCommand(7); + sq.animCommand(7, 36); + sq.animCommand(8, 18); + sq.fadeText(); + + sq.printText(11, 15); // Follow me + sq.animCommand(7, 18); + sq.animCommand(9, 18); + sq.animCommand(8, 18); + sq.fadeText(); + + sq.loadScene(7, 2); + + if (_configRenderMode != Common::kRenderEGA) + sq.copyPalette(3, 0); + + sq.loadScene(4, 2); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 2030 : 2); + + _screen->clearCurPage(); + sq.update(2); + + sq.loadScene(8, 2); + sq.loadScene(6, 6); + sq.delay(10); + + sq.printText(12, 10); // Powerful mages stand ready for the final assault... + sq.delay(90); + sq.fadeText(); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 2200 : 3); + + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(7); + sq.delay(8); + + sq.animCommand(10); + sq.animCommand(13); + sq.initDelayedPaletteFade(4, 1); + + sq.animCommand(14); + sq.animCommand(13); + sq.animCommand(14); + sq.animCommand(14); + sq.animCommand(13); + sq.initDelayedPaletteFade(2, 1); + + sq.animCommand(15); + sq.animCommand(14); + sq.animCommand(13); + sq.animCommand(15); + sq.animCommand(15); + sq.animCommand(11); + + sq.printText(13, 10); // The temple's evil is very strong + sq.delay(72); + sq.fadeText(); + + sq.printText(14, 10); // It must not be allowed... + sq.delay(72); + sq.fadeText(); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 2752 : 4); + + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(7); + sq.delay(8); + + sq.animCommand(10); + sq.initDelayedPaletteFade(5, 1); + sq.animCommand(13); + sq.animCommand(14); + sq.animCommand(13); + sq.animCommand(14); + sq.animCommand(13); + sq.animCommand(13); + sq.animCommand(14); + sq.animCommand(14); + sq.animCommand(13); + sq.animCommand(12); + for (int i = 0; i < 4; i++) + sq.animCommand(16); + sq.animCommand(17); + sq.animCommand(18); + + sq.printText(15, 10); // The temple ceases to exist + sq.initDelayedPaletteFade(6, 1); + sq.delay(36); + + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(11); + + sq.delay(54); + sq.fadeText(); + sq.loadScene(12, 2); + + sq.waitForSongNotifier(_flags.platform == Common::kPlatformFMTowns ? 3475 : 5); + + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(6); + + if (!skipFlag() && !shouldQuit()) { + if (_configRenderMode != Common::kRenderEGA) + sq.setPaletteWithoutTextColor(0); + _screen->crossFadeRegion(0, 0, 8, 8, 304, 128, 2, 0); + } + sq.delay(18); + + sq.printText(16, 15); // My friends, our work is done + sq.animCommand(20); + sq.animCommand(19); + sq.animCommand(19, 36); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(12); + sq.fadeText(); + + sq.printText(17, 15); // Thank you + sq.animCommand(19); + sq.animCommand(20, 36); + sq.fadeText(); + + sq.printText(18, 15); // You have earned my deepest respect + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(11); + sq.animCommand(20); + sq.animCommand(19); + sq.animCommand(19); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(11); + sq.delay(36); + sq.fadeText(); + + sq.printText(19, 15); // We will remember you always + sq.animCommand(19); + sq.animCommand(19, 18); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(11); + sq.animCommand(20, 18); + sq.fadeText(); + + sq.delay(28); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(12); + sq.delay(3); + + sq.loadScene(5, 2); + if (skipFlag() || shouldQuit()) { + _screen->copyRegion(0, 0, 8, 8, 304, 128, 2, 0, Screen::CR_NO_P_CHECK); + } else { + snd_playSoundEffect(6); + if (_configRenderMode != Common::kRenderEGA) + sq.setPaletteWithoutTextColor(0); + _screen->crossFadeRegion(0, 0, 8, 8, 304, 128, 2, 0); + } + + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(12); + sq.delay(5); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(11); + sq.delay(11); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(12); + sq.delay(7); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(11); + sq.delay(12); + if (!skipFlag() && !shouldQuit()) + snd_playSoundEffect(12); + + removeInputTop(); + resetSkipFlag(true); + + sq.loadScene(10, 2); + sq.loadScene(9, 2); + + snd_stopSound(); + sq.delay(3); + + if (_flags.platform != Common::kPlatformFMTowns) + _sound->loadSoundFile("FINALE2"); + + sq.delay(18); + if (!skipFlag() && !shouldQuit()) + snd_playSong(_flags.platform == Common::kPlatformFMTowns ? 16 : 1); + + int temp = 0; + const uint8 *creditsData = (_flags.platform == Common::kPlatformFMTowns) ? _res->fileData("CREDITS.TXT", 0) : _staticres->loadRawData(kEoB2CreditsData, temp); + + seq_playCredits(&sq, creditsData, 18, 2, 6, 2); + + if (_flags.platform == Common::kPlatformFMTowns) + delete[] creditsData; + + sq.delay(90); + + resetSkipFlag(true); + + if (_configRenderMode != Common::kRenderEGA) { + sq.setPalette(11); + sq.fadePalette(9, 10); + } + + _screen->clearCurPage(); + sq.loadScene(11, 2); + + static const uint8 finPortraitPos[] = { 0x50, 0x50, 0xD0, 0x50, 0x50, 0x90, 0xD0, 0x90, 0x90, 0x50, 0x90, 0x90 }; + + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + if (i > 3) + _screen->drawShape(2, sq._shapes[6 + i], finPortraitPos[i << 1] - 16, finPortraitPos[(i << 1) + 1] - 16, 0); + _screen->drawShape(2, _characters[i].faceShape, finPortraitPos[i << 1], finPortraitPos[(i << 1) + 1], 0); + } + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + + if (_flags.platform == Common::kPlatformFMTowns) + sq.copyPalette(12, 0); + + sq.setPalette(9); + sq.fadePalette(0, 18); + + while (!skipFlag() && !shouldQuit()) + delay(_tickLength); + + snd_stopSound(); + sq.fadePalette(9, 10); +} + +void DarkMoonEngine::seq_playCredits(DarkmoonSequenceHelper *sq, const uint8 *data, int sd, int backupPage, int tempPage, int speed) { + if (!data) + return; + + _screen->setFont(Screen::FID_8_FNT); + _screen->setScreenDim(sd); + + const ScreenDim *dm = _screen->_curDim; + + _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 0, backupPage, Screen::CR_NO_P_CHECK); + + struct CreditsDataItem { + int16 x; + int16 y; + const void *data; + char *str; + uint8 crlf; + uint8 size; + uint8 dataType; + } items[36]; + memset(items, 0, sizeof(items)); + + const char *pos = (const char *)data; + uint32 end = _system->getMillis(); + uint32 cur = 0; + int i = 0; + + do { + for (bool loop = true; loop;) { + sq->processDelayedPaletteFade(); + cur = _system->getMillis(); + if (end <= cur) + break; + delay(MIN<uint32>(_tickLength, end - cur)); + } + + end = _system->getMillis() + speed * _tickLength; + + for (; i < 35 && *pos; i++) { + int16 nextY = i ? items[i].y + items[i].size + (items[i].size >> 2) : dm->h; + + const char *posOld = pos; + pos = strchr(pos, 0x0D); + if (!pos) + pos = strchr(posOld, 0x00); + + items[i + 1].crlf = *pos++; + + if (*posOld == 2) { + const uint8 *shp = sq->_shapes[(*++posOld) - 1]; + items[i + 1].data = shp; + items[i + 1].size = shp[1]; + items[i + 1].x = (dm->w - shp[2]) << 2; + items[i + 1].dataType = 1; + delete[] items[i + 1].str; + items[i + 1].str = 0; + + } else { + if (*posOld == 1) { + posOld++; + items[i + 1].size = 6; + } else { + items[i + 1].size = _screen->getFontWidth(); + } + + items[i + 1].dataType = 0; + + int l = pos - posOld; + if (items[i + 1].crlf != 0x0D) + l++; + + delete[] items[i + 1].str; + items[i + 1].str = new char[l]; + memcpy(items[i + 1].str, posOld, l); + items[i + 1].str[l - 1] = 0; + items[i + 1].data = 0; + items[i + 1].x = (((dm->w << 3) - (strlen(items[i + 1].str) * items[i + 1].size)) >> 1) + 1; + } + + items[i + 1].y = nextY; + } + + _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, backupPage, tempPage, Screen::CR_NO_P_CHECK); + + for (int h = 0; h < i; h++) { + if (items[h + 1].y < dm->h) { + if (items[h + 1].dataType == 1) { + _screen->drawShape(tempPage, (const uint8 *)items[h + 1].data, items[h + 1].x, items[h + 1].y, sd); + } else { + _screen->setCurPage(tempPage); + + if (items[h + 1].size == 6) + _screen->setFont(Screen::FID_6_FNT); + + _screen->printText(items[h + 1].str, (dm->sx << 3) + items[h + 1].x - 1, dm->sy + items[h + 1].y + 1, 12, 0); + _screen->printText(items[h + 1].str, (dm->sx << 3) + items[h + 1].x, dm->sy + items[h + 1].y, 240, 0); + + if (items[h + 1].size == 6) + _screen->setFont(Screen::FID_8_FNT); + + _screen->setCurPage(0); + } + } + + items[h + 1].y -= 2; + } + + _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, tempPage, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + if (-items[1].size > items[1].y) { + delete[] items[1].str; + --i; + for (int t = 1; t <= i; t++) + memcpy(&items[t], &items[t + 1], sizeof(CreditsDataItem)); + items[i + 1].str = 0; + } + + if (i < 35 && ((items[i].y + items[i].size) < (dm->sy + dm->h))) { + resetSkipFlag(true); + break; + } + + sq->processDelayedPaletteFade(); + } while (!skipFlag() && i && !shouldQuit()); + + for (i = 0; i < 35; i++) + delete[] items[i].str; +} + +DarkmoonSequenceHelper::DarkmoonSequenceHelper(OSystem *system, DarkMoonEngine *vm, Screen_EoB *screen, Mode mode) : + _system(system), _vm(vm), _screen(screen) { + + int size = 0; + + if (mode == kIntro) { + _config = new Config( + _vm->staticres()->loadStrings(kEoB2IntroStrings, size), + _vm->staticres()->loadStrings(kEoB2IntroCPSFiles, size), + new const uint8*[13], + _vm->_configRenderMode == Common::kRenderEGA ? _palFilesIntroEGA : _palFilesIntroVGA, + new const DarkMoonShapeDef*[13], + new const DarkMoonAnimCommand *[44], + false, + false, + true, + true, + 0, + 0, + false, + 2 + ); + + for (int i = 0; i < 44; i++) + _config->animData[i] = _vm->staticres()->loadEoB2SeqData(kEoB2IntroAnimData00 + i, size); + + for (int i = 0; i < 13; i++) + _config->cpsData[i] = _vm->staticres()->loadRawData(kEoB2IntroCpsDataStreet1 + i, size); + + memset(_config->shapeDefs, 0, 13 * sizeof(DarkMoonShapeDef*)); + _config->shapeDefs[0] = _vm->staticres()->loadEoB2ShapeData(kEoB2IntroShapes00, size); + _config->shapeDefs[1] = _vm->staticres()->loadEoB2ShapeData(kEoB2IntroShapes01, size); + _config->shapeDefs[4] = _vm->staticres()->loadEoB2ShapeData(kEoB2IntroShapes04, size); + _config->shapeDefs[7] = _vm->staticres()->loadEoB2ShapeData(kEoB2IntroShapes07, size); + + } else { + _config = new Config( + _vm->staticres()->loadStrings(kEoB2FinaleStrings, size), + _vm->staticres()->loadStrings(kEoB2FinaleCPSFiles, size), + new const uint8*[13], + _vm->_configRenderMode == Common::kRenderEGA ? _palFilesFinaleEGA : _palFilesFinaleVGA, + new const DarkMoonShapeDef*[13], + new const DarkMoonAnimCommand *[21], + true, + true, + false, + false, + 1, + 18, + true, + 6 + ); + + for (int i = 0; i < 21; i++) + _config->animData[i] = _vm->staticres()->loadEoB2SeqData(kEoB2FinaleAnimData00 + i, size); + + for (int i = 0; i < 13; i++) + _config->cpsData[i] = _vm->staticres()->loadRawData(kEoB2FinaleCpsDataDragon1 + i, size); + + memset(_config->shapeDefs, 0, 13 * sizeof(DarkMoonShapeDef*)); + _config->shapeDefs[0] = _vm->staticres()->loadEoB2ShapeData(kEoB2FinaleShapes00, size); + _config->shapeDefs[3] = _vm->staticres()->loadEoB2ShapeData(kEoB2FinaleShapes03, size); + _config->shapeDefs[7] = _vm->staticres()->loadEoB2ShapeData(kEoB2FinaleShapes07, size); + _config->shapeDefs[9] = _vm->staticres()->loadEoB2ShapeData(kEoB2FinaleShapes09, size); + _config->shapeDefs[10] = _vm->staticres()->loadEoB2ShapeData(kEoB2FinaleShapes10, size); + } + + _screen->enableHiColorMode(false); + + for (int i = 0; _config->palFiles[i]; i++) { + if (i < 4) + _palettes[i] = &_screen->getPalette(i); + else + _palettes[i] = new Palette(256); + _screen->loadPalette(_config->palFiles[i], *_palettes[i]); + } + + for (int i = 9; i < 13; ++i) + _palettes[i] = new Palette(256); + + _palettes[9]->fill(0, 256, 0); + _palettes[10]->fill(0, 256, 63); + _palettes[11]->fill(0, 256, 0); + + if (_vm->gameFlags().platform == Common::kPlatformFMTowns) + _screen->loadPalette("PALETTE.COL", *_palettes[12]); + + for (int i = 0; i < 7; i++) + _fadingTables[i] = 0; + + uint8 *fadeData = (_vm->_configRenderMode != Common::kRenderCGA && _vm->_configRenderMode != Common::kRenderEGA) ? _vm->resource()->fileData("FADING.DAT", 0) : 0; + + if (fadeData) { + for (int i = 0; i < 7; i++) { + _fadingTables[i] = new uint8[256]; + memcpy(_fadingTables[i], fadeData + (i << 8), 256); + } + } else { + if (_vm->_configRenderMode != Common::kRenderCGA && _vm->_configRenderMode != Common::kRenderEGA) { + uint8 *pal = _vm->resource()->fileData("PALETTE1.PAL", 0); + for (int i = 0; i < 7; i++) + _screen->createFadeTable(pal, _fadingTables[i], 18, (i + 1) * 36); + delete[] pal; + } + } + + delete[] fadeData; + + _shapes = new const uint8*[30]; + memset(_shapes, 0, 30 * sizeof(uint8 *)); + + _fadePalTimer = 0; + _fadePalRate = 0; + + _screen->setScreenPalette(*_palettes[0]); + _prevFont = _screen->setFont(_vm->gameFlags().platform == Common::kPlatformFMTowns ? Screen::FID_SJIS_LARGE_FNT : Screen::FID_8_FNT); + _screen->hideMouse(); + + _vm->delay(150); + _vm->_eventList.clear(); + _vm->_allowSkip = true; +} + +DarkmoonSequenceHelper::~DarkmoonSequenceHelper() { + for (int i = 4; _config->palFiles[i]; i++) + delete _palettes[i]; + for (int i = 9; i < 13; ++i) + delete _palettes[i]; + + for (int i = 0; i < 7; i++) + delete[] _fadingTables[i]; + + for (int i = 0; i < 30; i++) + delete[] _shapes[i]; + delete[] _shapes; + + delete[] _config->animData; + delete[] _config->shapeDefs; + delete[] _config->cpsData; + delete _config; + + _screen->enableHiColorMode(true); + _screen->clearCurPage(); + _screen->setFont(_prevFont); + _screen->showMouse(); + _screen->updateScreen(); + + _system->delayMillis(150); + _vm->resetSkipFlag(true); + _vm->_allowSkip = false; +} + +void DarkmoonSequenceHelper::loadScene(int index, int pageNum) { + char file[13] = ""; + Common::SeekableReadStream *s = 0; + uint32 chunkID = 0; + + if (_config->cpsFiles) { + strcpy(file, _config->cpsFiles[index]); + s = _vm->resource()->createReadStream(file); + } + + if (s) { + chunkID = s->readUint32LE(); + s->seek(0); + } + + if (_config->cpsData[index]) { + _screen->decodeSHP(_config->cpsData[index], pageNum); + } else if (s && chunkID == MKTAG('F', 'O', 'R', 'M')) { + // The original code also handles files with FORM chunks and ILBM and PBM sub chunks. This will probably be necessary for Amiga versions. + // The DOS versions do not need this, but still have the code for it. We error out for now. + error("DarkmoonSequenceHelper::loadScene(): CPS file loading failure in scene %d - unhandled FORM chunk encountered", index); + } else if (s && file[0] != 'X') { + delete s; + _screen->loadBitmap(_config->cpsFiles[index], pageNum | 1, pageNum | 1, _palettes[0]); + } else { + if (!s) { + file[0] = 'X'; + s = _vm->resource()->createReadStream(file); + } + + if (!s) + error("DarkmoonSequenceHelper::loadScene(): CPS file loading failure in scene %d", index); + + if (_config->loadScenePal) + s->read(_palettes[0]->getData(), 768); + else + s->seek(768); + _screen->loadFileDataToPage(s, 3, 64000); + delete s; + } + + int cp = _screen->setCurPage(pageNum); + + if (_config->shapeDefs[index]) { + for (const DarkMoonShapeDef *df = _config->shapeDefs[index]; df->w; df++) { + uint16 shapeIndex = (df->index < 0) ? df->index * -1 : df->index; + if (_shapes[shapeIndex]) + delete[] _shapes[shapeIndex]; + _shapes[shapeIndex] = _screen->encodeShape(df->x, df->y, df->w, df->h, (df->index >> 8) != 0); + } + } + + _screen->setCurPage(cp); + + if (_vm->_configRenderMode == Common::kRenderEGA) + setPalette(0); + + _screen->convertPage(pageNum | 1, pageNum, 0); + + if ((pageNum == 0 || pageNum == 1) && !_vm->skipFlag() && !_vm->shouldQuit()) + _screen->updateScreen(); +} + +void DarkmoonSequenceHelper::animCommand(int index, int del) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + uint32 end = 0; + + for (const DarkMoonAnimCommand *s = _config->animData[index]; s->command != 0xFF && !_vm->skipFlag() && !_vm->shouldQuit(); s++) { + int palIndex = s->pal + _config->animPalOffs; + int x = s->x1; + if (x >= Screen::SCREEN_W) + x >>= 1; + int y = s->y1; + int x2 = 0; + uint16 shapeW = 0; + uint16 shapeH = 0; + + switch (s->command) { + case 0: + // flash palette + if (_vm->_configRenderMode != Common::kRenderEGA && s->pal) + setPaletteWithoutTextColor(palIndex); + delay(s->delay); + if (_vm->_configRenderMode != Common::kRenderEGA && _config->animCmdRestorePal && s->pal) + setPaletteWithoutTextColor(0); + break; + + case 1: + // draw shape, then restore background + shapeW = _shapes[s->obj][2]; + shapeH = _shapes[s->obj][3]; + + if (_config->animCmd1ShapeFrame == 18) { + _screen->setScreenDim(18); + x -= (_screen->_curDim->sx << 3); + y -= _screen->_curDim->sy; + if (x < 0) + shapeW -= ((-x >> 3) + 1); + else + x2 = x; + } + + _screen->drawShape(0, _shapes[s->obj], x, y, _config->animCmd1ShapeFrame); + + if (_vm->_configRenderMode != Common::kRenderEGA && s->pal) + setPaletteWithoutTextColor(palIndex); + else + _screen->updateScreen(); + + delay(s->delay); + + if (_config->animCmd1ShapeFrame == 0) { + if (_vm->_configRenderMode != Common::kRenderEGA && s->pal) + setPaletteWithoutTextColor(0); + _screen->copyRegion(x - 8, y - 8, x, y, (shapeW + 1) << 3, shapeH, 2, 0, Screen::CR_NO_P_CHECK); + } else { + _screen->copyRegion(x2, y, x2 + (_screen->_curDim->sx << 3), y + _screen->_curDim->sy, (shapeW + 1) << 3, shapeH, 2, 0, Screen::CR_NO_P_CHECK); + } + + _screen->updateScreen(); + break; + + case 2: + // draw shape + _screen->drawShape(_screen->_curPage, _shapes[s->obj], x, y, 0); + + if (_vm->_configRenderMode != Common::kRenderEGA && s->pal) + setPaletteWithoutTextColor(palIndex); + else if (!_screen->_curPage) + _screen->updateScreen(); + + delay(s->delay); + + if (_vm->_configRenderMode != Common::kRenderEGA && _config->animCmdRestorePal && s->pal) + setPaletteWithoutTextColor(0); + break; + + case 3: + case 4: + // fade shape in or out or restore background + if (!_config->shpBackgroundFading) + break; + + if (_vm->_configRenderMode == Common::kRenderEGA) { + if (palIndex) + _screen->drawShape(0, _shapes[s->obj], s->x1, y, 0); + else + _screen->copyRegion(s->x1 - 8, s->y1 - 8, s->x1, s->y1, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + delay(s->delay /** 7*/); + } else { + _screen->enableShapeBackgroundFading(true); + _screen->setShapeFadingLevel(1); + + end = _system->getMillis() + s->delay * _vm->tickLength(); + + if (palIndex) { + _screen->setFadeTable(_fadingTables[palIndex - 1]); + + _screen->copyRegion(s->x1 - 8, s->y1 - 8, 0, 0, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 2, 4, Screen::CR_NO_P_CHECK); + _screen->drawShape(4, _shapes[s->obj], s->x1 & 7, 0, 0); + _screen->copyRegion(0, 0, s->x1, s->y1, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 4, 0, Screen::CR_NO_P_CHECK); + } else { + _screen->copyRegion(s->x1 - 8, s->y1 - 8, s->x1, s->y1, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 2, 0, Screen::CR_NO_P_CHECK); + } + _screen->updateScreen(); + + _vm->delayUntil(end); + _screen->enableShapeBackgroundFading(false); + _screen->setShapeFadingLevel(0); + } + break; + + case 5: + // copy region + if (_config->animCmd5SetPal && s->pal) + setPaletteWithoutTextColor(palIndex); + + _screen->copyRegion(s->x2 << 3, s->y2, s->x1, s->y1, s->w << 3, s->h, s->obj ? _config->animCmd5AltPage : 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + delay(s->delay); + break; + + case 6: + // play sound effect + if (s->obj != 0xFF) + _vm->snd_playSoundEffect(s->obj); + break; + + case 7: + // restore background (only used in EGA mode) + delay(s->delay); + _screen->copyRegion(s->x1 - 8, s->y1 - 8, s->x1, s->y1, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + break; + + default: + error("DarkmoonSequenceHelper::animCommand(): Unknown animation opcode encountered."); + break; + } + } + + if (del > 0) + delay(del); +} + +void DarkmoonSequenceHelper::printText(int index, int color) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + _screen->setClearScreenDim(17); + uint8 col1 = 15; + + if (_vm->_configRenderMode != Common::kRenderEGA) { + _palettes[0]->copy(*_palettes[0], color, 1, 255); + setPalette(0); + col1 = 255; + } + + char *temp = new char[strlen(_config->strings[index]) + 1]; + char *str = temp; + strcpy(str, _config->strings[index]); + + const ScreenDim *dm = _screen->_curDim; + int fontHeight = _screen->getFontHeight() + 1; + + for (int yOffs = 0; *str; yOffs += fontHeight) { + char *cr = strchr(str, 13); + + if (cr) + *cr = 0; + + uint32 len = strlen(str); + _screen->printText(str, (dm->sx + ((dm->w - len) >> 1)) << 3, dm->sy + yOffs, col1, dm->unkA); + + if (cr) { + *cr = 13; + str = cr + 1; + } else { + str += len; + } + } + + delete[] temp; + _screen->updateScreen(); +} + +void DarkmoonSequenceHelper::fadeText() { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + if (_vm->_configRenderMode != Common::kRenderEGA) + _screen->fadeTextColor(_palettes[0], 255, 8); + _screen->clearCurDim(); +} + +void DarkmoonSequenceHelper::update(int srcPage) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + _screen->copyRegion(0, 0, 8, 8, 304, 128, srcPage, 0, Screen::CR_NO_P_CHECK); + + if (_vm->_configRenderMode != Common::kRenderEGA) + setPaletteWithoutTextColor(0); +} + +void DarkmoonSequenceHelper::setPaletteWithoutTextColor(int index) { + if (_vm->_configRenderMode == Common::kRenderEGA || _vm->skipFlag() || _vm->shouldQuit()) + return; + + if (!memcmp(_palettes[11]->getData(), _palettes[index]->getData(), 765)) + return; + + _palettes[11]->copy(*_palettes[index], 0, 255); + _palettes[11]->copy(*_palettes[0], 255, 1, 255); + setPalette(11); + + _screen->updateScreen(); + _system->delayMillis(10); +} + +void DarkmoonSequenceHelper::setPalette(int index) { + _screen->setScreenPalette(*_palettes[index]); +} + +void DarkmoonSequenceHelper::fadePalette(int index, int del) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + if (_vm->_configRenderMode == Common::kRenderEGA) { + setPalette(index); + _screen->updateScreen(); + } else { + _screen->fadePalette(*_palettes[index], del * _vm->tickLength()); + } +} + +void DarkmoonSequenceHelper::copyPalette(int srcIndex, int destIndex) { + _palettes[destIndex]->copy(*_palettes[srcIndex]); +} + +void DarkmoonSequenceHelper::initDelayedPaletteFade(int palIndex, int rate) { + _palettes[11]->copy(*_palettes[0]); + + _fadePalIndex = palIndex; + _fadePalRate = rate; + _fadePalTimer = _system->getMillis() + 2 * _vm->_tickLength; +} + +bool DarkmoonSequenceHelper::processDelayedPaletteFade() { + if (_vm->skipFlag() || _vm->shouldQuit()) + return true; + + if (_vm->_configRenderMode == Common::kRenderEGA || !_fadePalRate || (_system->getMillis() <= _fadePalTimer)) + return false; + + if (_screen->delayedFadePalStep(_palettes[_fadePalIndex], _palettes[0], _fadePalRate)) { + setPaletteWithoutTextColor(0); + _fadePalTimer = _system->getMillis() + 3 * _vm->_tickLength; + } else { + _fadePalRate = 0; + } + + return false; +} + +void DarkmoonSequenceHelper::delay(uint32 ticks) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + uint32 end = _system->getMillis() + ticks * _vm->_tickLength; + + if (_config->palFading) { + do { + if (processDelayedPaletteFade()) + break; + _vm->updateInput(); + } while (end > _system->getMillis()); + processDelayedPaletteFade(); + + } else { + _vm->delayUntil(end); + } +} + +void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim) { + int seq = 0; + while (_vm->sound()->checkTrigger() < index && !(_vm->skipFlag() || _vm->shouldQuit())) { + if (introUpdateAnim) { + animCommand(30 | seq); + seq ^= 1; + } + + if (_config->palFading) + processDelayedPaletteFade(); + + _vm->updateInput(); + } +} + +const char *const DarkmoonSequenceHelper::_palFilesIntroVGA[] = { + "PALETTE1.PAL", + "PALETTE3.PAL", + "PALETTE2.PAL", + "PALETTE4.PAL", + 0 +}; + +const char *const DarkmoonSequenceHelper::_palFilesIntroEGA[] = { + "PALETTE0.PAL", + "PALETTE3.PAL", + "PALETTE2.PAL", + "PALETTE4.PAL", + 0 +}; + +const char *const DarkmoonSequenceHelper::_palFilesFinaleVGA[] = { + "FINALE_0.PAL", + "FINALE_0.PAL", + "FINALE_1.PAL", + "FINALE_2.PAL", + "FINALE_3.PAL", + "FINALE_4.PAL", + "FINALE_5.PAL", + "FINALE_6.PAL", + "FINALE_7.PAL", + 0 +}; + +const char *const DarkmoonSequenceHelper::_palFilesFinaleEGA[] = { + "FINALE_0.PAL", + "FINALE_0.PAL", + "FINALE_1.PAL", + "FINALE_2.PAL", + "FINALE_3.PAL", + "FINALE_4.PAL", + "FINALE_5.PAL", + "FINALE_0.PAL", + "FINALE_0.PAL", + 0 +}; + +void DarkMoonEngine::seq_nightmare() { + Screen::FontId of = _screen->setFont(Screen::FID_6_FNT); + if (_flags.lang == Common::JA_JPN) + _screen->clearCurDim(); + _screen->copyRegion(0, 0, 0, 120, 176, 24, 12, 2, Screen::CR_NO_P_CHECK); + + initDialogueSequence(); + gui_drawDialogueBox(); + + _txt->printDialogueText(99, 0); + snd_playSoundEffect(54); + + static const uint8 seqX[] = { 0, 20, 0, 20 }; + static const uint8 seqY[] = { 0, 0, 96, 96 }; + static const uint8 seqDelay[] = { 12, 7, 7, 12 }; + + for (const int8 *i = _dreamSteps; *i != -1; ++i) { + drawSequenceBitmap("DREAM", 0, seqX[*i], seqY[*i], 0); + delay(seqDelay[*i] * _tickLength); + } + + _txt->printDialogueText(20, _okStrings[0]); + + restoreAfterDialogueSequence(); + + _screen->setFont(of); +} + +void DarkMoonEngine::seq_kheldran() { + Screen::FontId of = _screen->setFont(Screen::FID_6_FNT); + + initDialogueSequence(); + gui_drawDialogueBox(); + + static const char file[] = "KHELDRAN"; + _screen->set16bitShadingLevel(4); + _txt->printDialogueText(_kheldranStrings[0]); + drawSequenceBitmap(file, 0, 0, 0, 0); + _txt->printDialogueText(20, _moreStrings[0]); + snd_playSoundEffect(56); + drawSequenceBitmap(file, 0, 20, 0, 0); + delay(10 * _tickLength); + drawSequenceBitmap(file, 0, 0, 96, 0); + delay(10 * _tickLength); + drawSequenceBitmap(file, 0, 20, 96, 0); + delay(7 * _tickLength); + _txt->printDialogueText(76, _okStrings[0]); + + restoreAfterDialogueSequence(); + + _screen->setFont(of); +} + +void DarkMoonEngine::seq_dranDragonTransformation() { + Screen::FontId of = _screen->setFont(Screen::FID_6_FNT); + + initDialogueSequence(); + gui_drawDialogueBox(); + + static const char file[] = "DRANX"; + drawSequenceBitmap(file, 0, 0, 0, 0); + _txt->printDialogueText(120, _moreStrings[0]); + snd_playSoundEffect(56); + drawSequenceBitmap(file, 0, 20, 0, 0); + delay(7 * _tickLength); + drawSequenceBitmap(file, 0, 0, 96, 0); + delay(7 * _tickLength); + drawSequenceBitmap(file, 0, 20, 96, 0); + delay(18 * _tickLength); + + restoreAfterDialogueSequence(); + + _screen->setFont(of); +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/sequence/sequences_eob.cpp b/engines/kyra/sequence/sequences_eob.cpp new file mode 100644 index 0000000000..a04c5f75cd --- /dev/null +++ b/engines/kyra/sequence/sequences_eob.cpp @@ -0,0 +1,1152 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/engine/eob.h" +#include "kyra/graphics/screen_eob.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" + +#include "common/system.h" + +#include "base/version.h" + +namespace Kyra { + +class EoBIntroPlayer { +public: + EoBIntroPlayer(EoBEngine *vm, Screen_EoB *screen); + ~EoBIntroPlayer() {} + + void start(); + +private: + void openingCredits(); + void tower(); + void orb(); + void waterdeepEntry(); + void king(); + void hands(); + void waterdeepExit(); + void tunnel(); + + void loadAndSetPalette(const char *filename); + void copyBlurRegion(int x1, int y1, int x2, int y2, int w, int h, int step); + void boxMorphTransition(int targetDestX, int targetDestY, int targetFinalX, int targetFinalY, int targetSrcX, int targetSrcY, int targetFinalW, int targetFinalH, int originX1, int originY1, int originW, int originH); + void whirlTransition(); + + EoBEngine *_vm; + Screen_EoB *_screen; + + const char *const *_filesOpening; + const char *const *_filesTower; + const char *const *_filesOrb; + const char *const *_filesWdEntry; + const char *const *_filesKing; + const char *const *_filesHands; + const char *const *_filesWdExit; + const char *const *_filesTunnel; + const uint8 *_openingFrmDelay; + const uint8 *_wdEncodeX; + const uint8 *_wdEncodeY; + const uint8 *_wdEncodeWH; + const uint16 *_wdDsX; + const uint8 *_wdDsY; + const uint8 *_tvlX1; + const uint8 *_tvlY1; + const uint8 *_tvlX2; + const uint8 *_tvlY2; + const uint8 *_tvlW; + const uint8 *_tvlH; +}; + +EoBIntroPlayer::EoBIntroPlayer(EoBEngine *vm, Screen_EoB *screen) : _vm(vm), _screen(screen) { + int temp = 0; + _filesOpening = _vm->staticres()->loadStrings(kEoB1IntroFilesOpening, temp); + _filesTower = _vm->staticres()->loadStrings(kEoB1IntroFilesTower, temp); + _filesOrb = _vm->staticres()->loadStrings(kEoB1IntroFilesOrb, temp); + _filesWdEntry = _vm->staticres()->loadStrings(kEoB1IntroFilesWdEntry, temp); + _filesKing = _vm->staticres()->loadStrings(kEoB1IntroFilesKing, temp); + _filesHands = _vm->staticres()->loadStrings(kEoB1IntroFilesHands, temp); + _filesWdExit = _vm->staticres()->loadStrings(kEoB1IntroFilesWdExit, temp); + _filesTunnel = _vm->staticres()->loadStrings(kEoB1IntroFilesTunnel, temp); + _openingFrmDelay = _vm->staticres()->loadRawData(kEoB1IntroOpeningFrmDelay, temp); + _wdEncodeX = _vm->staticres()->loadRawData(kEoB1IntroWdEncodeX, temp); + _wdEncodeY = _vm->staticres()->loadRawData(kEoB1IntroWdEncodeY, temp); + _wdEncodeWH = _vm->staticres()->loadRawData(kEoB1IntroWdEncodeWH, temp); + _wdDsX = _vm->staticres()->loadRawDataBe16(kEoB1IntroWdDsX, temp); + _wdDsY = _vm->staticres()->loadRawData(kEoB1IntroWdDsY, temp); + _tvlX1 = _vm->staticres()->loadRawData(kEoB1IntroTvlX1, temp); + _tvlY1 = _vm->staticres()->loadRawData(kEoB1IntroTvlY1, temp); + _tvlX2 = _vm->staticres()->loadRawData(kEoB1IntroTvlX2, temp); + _tvlY2 = _vm->staticres()->loadRawData(kEoB1IntroTvlY2, temp); + _tvlW = _vm->staticres()->loadRawData(kEoB1IntroTvlW, temp); + _tvlH = _vm->staticres()->loadRawData(kEoB1IntroTvlH, temp); +} + +void EoBIntroPlayer::start() { + _vm->_allowSkip = true; + openingCredits(); + + if (!_vm->shouldQuit() && !_vm->skipFlag()) { + _vm->snd_playSong(2); + _screen->loadBitmap((_vm->_configRenderMode == Common::kRenderCGA || _vm->_configRenderMode == Common::kRenderEGA) ? "TITLE-E.CMP" : "TITLE-V.CMP", 3, 5, 0); + _screen->convertPage(5, 2, _vm->_cgaMappingDefault); + _screen->crossFadeRegion(0, 0, 0, 0, 320, 200, 2, 0); + _vm->delay(120 * _vm->_tickLength); + } + + Common::SeekableReadStream *s = _vm->resource()->createReadStream("TEXT.RAW"); + if (s) { + s->seek(768); + _screen->loadFileDataToPage(s, 5, s->size() - 768); + delete s; + } else { + _screen->loadBitmap("TEXT.CMP", 3, 5, 0); + } + _screen->convertPage(5, 6, _vm->_cgaMappingAlt); + + tower(); + orb(); + waterdeepEntry(); + king(); + hands(); + waterdeepExit(); + tunnel(); + + whirlTransition(); + _vm->snd_stopSound(); + _vm->_allowSkip = false; +} + +void EoBIntroPlayer::openingCredits() { + loadAndSetPalette(_filesOpening[5]); + + _screen->loadBitmap(_filesOpening[4], 5, 3, 0); + _screen->convertPage(3, 0, _vm->_cgaMappingAlt); + _screen->updateScreen(); + + _vm->snd_playSong(1); + _vm->delay(_openingFrmDelay[0] * _vm->_tickLength); + + for (int i = 0; i < 4 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + _screen->loadBitmap(_filesOpening[i], 5, 3, 0); + uint32 nextFrameTimer = _vm->_system->getMillis() + _openingFrmDelay[i + 1] * _vm->_tickLength; + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + _screen->crossFadeRegion(0, 50, 0, 50, 320, 102, 4, 0); + _vm->delayUntil(nextFrameTimer); + } +} + +void EoBIntroPlayer::tower() { + if (_vm->shouldQuit() || _vm->skipFlag()) + return; + + _screen->loadBitmap(_filesTower[1], 5, 3, 0); + _screen->setCurPage(2); + uint8 *shp = _screen->encodeShape(0, 0, 16, 56, true, _vm->_cgaMappingAlt); + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + _screen->clearCurPage(); + + for (int i = 0; i < 200; i += 64) + _screen->copyRegion(128, 104, 96, i, 128, 64, 4, 2, Screen::CR_NO_P_CHECK); + + _screen->fillRect(0, 184, 319, 199, 12); + int cp = _screen->setCurPage(0); + whirlTransition(); + loadAndSetPalette(_filesTower[0]); + + _screen->setCurPage(cp); + _screen->clearCurPage(); + + for (int i = 0; i < 200; i += 64) + _screen->copyRegion(128, 104, 0, i, 128, 64, 4, 2, Screen::CR_NO_P_CHECK); + + _screen->setCurPage(0); + + for (int i = 0; i < 64 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 2) { + uint32 end = _vm->_system->getMillis() + 2 * _vm->_tickLength; + _screen->copyRegion(0, 142 - i, 96, 0, 128, i + 1, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 96, i + 1, 128, 167 - i, 2, 0, Screen::CR_NO_P_CHECK); + if (!i) + _screen->copyRegion(0, 0, 0, 168, 320, 32, 6, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + for (int i = 0; i < 24 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 2) { + uint32 end = _vm->_system->getMillis() + 2 * _vm->_tickLength; + _screen->copyRegion(0, 79 - i, 96, 0, 24, 65 + i, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(104, 79 - i, 200, 0, 24, 65 + i, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(24, 110, 120, i + 31, 80, 34, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(152, 0, 120, 32, 80, i + 1, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 96, 65 + i, 128, 103 - i, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + for (int i = 0; i < 56 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 2) { + uint32 end = _vm->_system->getMillis() + 2 * _vm->_tickLength; + _screen->copyRegion(0, 56, 96, i, 24, 54, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(104, 56, 200, i, 24, 54, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 110, 96, 54 + i, 128, 34, 4, 0, Screen::CR_NO_P_CHECK); + + if (i < 32) { + _screen->fillRect(128, 0, 255, i + 1, 12, 2); + _screen->copyRegion(152, 0, 120, 32, 80, i + 25, 4, 0, Screen::CR_NO_P_CHECK); + } else { + _screen->fillRect(128, 0, 255, i + 1, 12, 2); + _screen->copyRegion(152, i + 1, 120, 32 + i + 1, 80, 23, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(152, 0, 152, 32, 80, i + 1, 4, 2, Screen::CR_NO_P_CHECK); + } + + _screen->drawShape(2, shp, 128, i - 55, 0); + _screen->copyRegion(128, 0, 96, 0, 128, i + 1, 2, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 96, i + 89, 128, 79 - i, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _screen->copyRegion(0, 32, 0, 168, 320, 32, 6, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(65 * _vm->_tickLength); + delete[] shp; +} + +void EoBIntroPlayer::orb() { + if (_vm->shouldQuit() || _vm->skipFlag()) + return; + + uint8 *shp[5]; + _screen->loadBitmap(_filesOrb[0], 5, 3, 0); + _screen->setCurPage(2); + shp[4] = _screen->encodeShape(0, 0, 20, 136, true, _vm->_cgaMappingAlt); + _screen->loadBitmap(_filesOrb[1], 5, 3, 0); + shp[3] = _screen->encodeShape(16, 0, 16, 104, true, _vm->_cgaMappingAlt); + + _screen->fillRect(0, 0, 127, 103, 12); + for (int i = 1; i < 4; i++) { + copyBlurRegion(128, 0, 0, 0, 128, 104, i); + shp[3 - i] = _screen->encodeShape(0, 0, 16, 104, true, _vm->_cgaMappingAlt); + } + + _screen->fillRect(0, 0, 159, 135, 12); + _screen->setCurPage(0); + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + _screen->clearCurPage(); + + _vm->snd_playSoundEffect(6); + + for (int i = -1; i < 4 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + 3 * _vm->_tickLength; + if (i >= 0) + _screen->drawShape(2, shp[i], 16, 16, 0); + _screen->drawShape(2, shp[4], 0, 0, 0); + _screen->copyRegion(0, 0, 80, 24, 160, 136, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _screen->copyRegion(0, 64, 0, 168, 320, 16, 6, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(40 * _vm->_tickLength); + + _vm->snd_playSoundEffect(6); + + for (int i = 3; i > -2 && !_vm->shouldQuit() && !_vm->skipFlag(); i--) { + uint32 end = _vm->_system->getMillis() + 3 * _vm->_tickLength; + _screen->fillRect(16, 16, 143, 119, 12, 2); + if (i >= 0) + _screen->drawShape(2, shp[i], 16, 16, 0); + _screen->drawShape(2, shp[4], 0, 0, 0); + _screen->copyRegion(0, 0, 80, 24, 160, 136, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _vm->delay(40 * _vm->_tickLength); + + for (int i = 0; i < 5; i++) + delete[] shp[i]; +} + +void EoBIntroPlayer::waterdeepEntry() { + if (_vm->shouldQuit() || _vm->skipFlag()) + return; + + uint8 *shp[4]; + uint8 *shp2[31]; + uint8 *shp3[3]; + + loadAndSetPalette(_filesWdEntry[0]); + _screen->loadBitmap(_filesWdEntry[1], 5, 3, 0); + _screen->setCurPage(2); + shp[3] = _screen->encodeShape(0, 0, 20, 136, true, _vm->_cgaMappingAlt); + for (int i = 1; i < 4; i++) { + copyBlurRegion(0, 0, 0, 0, 160, 136, i); + shp[3 - i] = _screen->encodeShape(0, 0, 20, 136, true, _vm->_cgaMappingAlt); + } + _screen->setCurPage(0); + + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + _screen->fillRect(0, 168, 319, 199, 12, 0); + _vm->snd_playSoundEffect(6); + + for (int i = 0; i < 4 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + 3 * _vm->_tickLength; + _screen->drawShape(0, shp[i], 80, 24, 0); + delete[] shp[i]; + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _screen->copyRegion(0, 80, 0, 168, 320, 16, 6, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(50 * _vm->_tickLength); + + _screen->setCurPage(2); + shp[0] = _screen->encodeShape(20, 0, 20, 136, true, _vm->_cgaMappingAlt); + _screen->loadBitmap(_filesWdEntry[2], 5, 3, 0); + shp[1] = _screen->encodeShape(0, 0, 20, 136, true, _vm->_cgaMappingAlt); + shp[2] = _screen->encodeShape(20, 0, 20, 136, true, _vm->_cgaMappingAlt); + _screen->loadBitmap(_filesWdEntry[3], 5, 3, 0); + + for (int i = 0; i < 31; i++) + shp2[i] = _screen->encodeShape(_wdEncodeX[i], 136 + (_wdEncodeY[i] << 3), _wdEncodeWH[i], _wdEncodeWH[i] << 3, true, _vm->_cgaMappingAlt); + for (int i = 0; i < 3; i++) + shp3[i] = _screen->encodeShape(5 * i, 152, 5, 32, true, _vm->_cgaMappingAlt); + + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + + for (int i = 0; i < 3 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + 3 * _vm->_tickLength; + _screen->fillRect(0, 0, 159, 135, 12, 2); + _screen->drawShape(2, shp[i], 0, 0, 0); + _screen->copyRegion(0, 0, 80, 24, 160, 136, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _screen->copyRegion(0, 0, 80, 24, 160, 136, 4, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(4 * _vm->_tickLength); + _screen->copyRegion(160, 0, 80, 24, 160, 136, 4, 0, Screen::CR_NO_P_CHECK); + _screen->fillRect(0, 168, 319, 199, 12, 0); + _screen->updateScreen(); + _vm->delay(4 * _vm->_tickLength); + _screen->copyRegion(0, 184, 40, 184, 232, 16, 4, 0, Screen::CR_NO_P_CHECK); + + int cx = 264; + int cy = 11; + + for (int i = 0; i < 70 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + 3 * _vm->_tickLength; + + _screen->copyRegion(cx - 2, cy - 2, 0, 0, 48, 36, 4, 4, Screen::CR_NO_P_CHECK); + _screen->drawShape(4, shp3[((i & 3) == 3) ? 1 : (i & 3)], cx, cy, 0); + _screen->copyRegion(cx - 2, cy - 2, cx - 82, cy + 22, 48, 36, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, cx - 2, cy - 2, 48, 36, 4, 4, Screen::CR_NO_P_CHECK); + cx--; + cy++; + + for (int ii = 0; ii < 5; ii++) { + int s = _vm->_rnd.getRandomNumber(255) % 31; + _screen->drawShape(0, shp2[s], _wdDsX[s] - 80, _wdDsY[s] + 24, 0); + } + + if (!(_vm->_rnd.getRandomNumber(255) & 7)) + _vm->snd_playSoundEffect(_vm->_rnd.getRandomBit() ? 5 : 14); + + _screen->updateScreen(); + _vm->delayUntil(end); + } + + for (int i = 0; i < 3; i++) { + delete[] shp[i]; + delete[] shp3[i]; + } + + for (int i = 0; i < 31; i++) + delete[] shp2[i]; +} + +void EoBIntroPlayer::king() { + if (_vm->shouldQuit() || _vm->skipFlag()) + return; + + _screen->loadBitmap(_filesKing[0], 5, 3, 0); + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + + int x = 15; + int y = 14; + int w = 1; + int h = 1; + + for (int i = 0; i < 10 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->copyRegion(x << 3, y << 3, x << 3, y << 3, w << 3, h << 3, 4, 0, Screen::CR_NO_P_CHECK); + if (x > 6) + x --; + if (y > 0) + y -= 2; + w += 3; + if (x + w > 34) + w = 34 - x; + h += 3; + if (y + h > 23) + h = 23 - y; + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _vm->delay(25 * _vm->_tickLength); + + uint8 *shp[4]; + int16 dy[4]; + int16 stepY[4]; + + static const uint8 advEncX[] = { 0, 6, 12, 19 }; + static const uint8 advEncW[] = { 6, 6, 7, 6 }; + static const int8 modY[] = { -4, -8, -2, -2, 1, 0, 0, 0 }; + + _screen->loadBitmap(_filesKing[1], 5, 3, 0); + _screen->setCurPage(2); + for (int i = 0; i < 4; i++) { + shp[i] = _screen->encodeShape(advEncX[i], 0, advEncW[i], 98, true, _vm->_cgaMappingAlt); + dy[i] = 180 + ((_vm->_rnd.getRandomNumber(255) & 3) << 3); + stepY[i] = (i * 5) & 3; + } + + _screen->copyPage(0, 4); + + for (bool runloop = true; runloop && !_vm->shouldQuit() && !_vm->skipFlag();) { + runloop = false; + uint32 end = _vm->_system->getMillis() + 2 * _vm->_tickLength; + + for (int i = 0; i < 4; i++) { + if (dy[i] <= 82) + continue; + stepY[i] = (stepY[i] + 1) & 7; + dy[i] += modY[stepY[i]]; + + if (dy[i] < 82) + dy[i] = 82; + + if (dy[i] < 180) { + _screen->copyRegion((advEncX[i] + 8) << 3, dy[i] - 2, 0, dy[i] - 2, advEncW[i] << 3, 182 - dy[i], 4, 4, Screen::CR_NO_P_CHECK); + _screen->drawShape(4, shp[i], 0, dy[i], 0); + _screen->copyRegion(0, dy[i] - 2, (advEncX[i] + 8) << 3, dy[i] - 2, advEncW[i] << 3, 182 - dy[i], 4, 0, Screen::CR_NO_P_CHECK); + } + + runloop = true; + } + + if (!(_vm->_rnd.getRandomNumber(255) & 3)) + _vm->snd_playSoundEffect(7); + + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _screen->copyRegion(0, 96, 0, 160, 320, 32, 6, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(70 * _vm->_tickLength); + + for (int i = 0; i < 4; i++) + delete[] shp[i]; +} + +void EoBIntroPlayer::hands() { + if (_vm->shouldQuit() || _vm->skipFlag()) + return; + + _screen->setCurPage(2); + uint8 *shp1 = _screen->encodeShape(0, 140, 21, 60, true, _vm->_cgaMappingAlt); + uint8 *shp2 = _screen->encodeShape(21, 140, 12, 60, true, _vm->_cgaMappingAlt); + _screen->loadBitmap(_filesHands[0], 3, 5, 0); + + _screen->fillRect(0, 160, 319, 199, 12, 0); + _screen->fillRect(0, 0, 191, 63, 157, 2); + _screen->drawShape(2, shp1, 0, 4, 0); + _screen->drawShape(2, shp2, 151, 4, 0); + boxMorphTransition(25, 8, 18, 4, 3, 0, 21, 8, 6, 0, 28, 23); + _screen->copyRegion(0, 128, 0, 176, 320, 16, 6, 0, Screen::CR_NO_P_CHECK); + + _screen->updateScreen(); + _vm->delay(15 * _vm->_tickLength); + _vm->snd_playSoundEffect(11); + + for (int i = -22; i <= 20 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 4) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->fillRect(0, 0, 167, 63, 157); + _screen->drawShape(2, shp1, i, 4, 0); + _screen->drawShape(2, shp2, 105 - i, 4, 0); + _screen->copyRegion(0, 0, 144, 32, 168, 64, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _vm->snd_playSoundEffect(10); + + delete[] shp1; + delete[] shp2; + _vm->delay(15 * _vm->_tickLength); + + _screen->setCurPage(4); + shp1 = _screen->encodeShape(17, 0, 11, 120, true, _vm->_cgaMappingAlt); + shp2 = _screen->encodeShape(28, 112, 1, 31, true, _vm->_cgaMappingAlt); + uint8 *shp3 = _screen->encodeShape(9, 138, 14, 54, true, _vm->_cgaMappingAlt); + + _screen->setCurPage(2); + _screen->fillRect(0, 0, 135, 63, 157); + _screen->drawShape(2, shp1, 32, -80, 0); + _screen->drawShape(2, shp2, 40, -16, 0); + boxMorphTransition(18, 16, 10, 12, 0, 0, 17, 8, 17, 3, 25, 10); + _vm->delay(15 * _vm->_tickLength); + + for (int i = -80; i <= 0 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 4) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->fillRect(0, 0, 135, 63, 157); + _screen->drawShape(2, shp1, 32, i, 0); + _screen->drawShape(2, shp2, 40, i + 64, 0); + _screen->copyRegion(0, 0, 80, 96, 136, 64, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _vm->snd_playSoundEffect(12); + _vm->delay(5 * _vm->_tickLength); + + for (int i = 0; i > -54 && !_vm->shouldQuit() && !_vm->skipFlag(); i -= 4) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->fillRect(0, 0, 135, 63, 157); + _screen->drawShape(2, shp3, 12, 64 + i, 0); + _screen->drawShape(2, shp1, 32, i, 0); + _screen->copyRegion(0, 0, 80, 96, 136, 64, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + delete[] shp1; + delete[] shp2; + delete[] shp3; + _vm->delay(15 * _vm->_tickLength); + + _screen->setCurPage(4); + shp1 = _screen->encodeShape(0, 0, 17, 136, true, _vm->_cgaMappingAlt); + shp2 = _screen->encodeShape(0, 136, 9, 48, true, _vm->_cgaMappingAlt); + + _screen->setCurPage(2); + _screen->fillRect(0, 0, 143, 95, 157); + _screen->drawShape(2, shp1, -56, -56, 0); + _screen->drawShape(2, shp2, 52, 49, 0); + boxMorphTransition(9, 6, 0, 0, 0, 0, 18, 12, 8, 11, 21, 10); + _vm->delay(15 * _vm->_tickLength); + _vm->snd_playSoundEffect(11); + + for (int i = -56; i <= -8 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 4) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->fillRect(0, 0, 143, 95, 157); + _screen->drawShape(2, shp1, i, i, 0); + _screen->drawShape(2, shp2, (i == -8) ? 55 : 52, (i == -8) ? 52 : 49, 0); + _screen->copyRegion(0, 0, 0, 0, 144, 96, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _vm->snd_playSoundEffect(10); + delete[] shp1; + delete[] shp2; + _vm->delay(30 * _vm->_tickLength); + + _screen->setCurPage(4); + shp1 = _screen->encodeShape(28, 0, 11, 40, true, _vm->_cgaMappingAlt); + shp2 = _screen->encodeShape(28, 40, 10, 72, true, _vm->_cgaMappingAlt); + + _screen->setCurPage(2); + _screen->fillRect(0, 0, 87, 112, 157); + _screen->drawShape(2, shp2, 0, 90, 0); + boxMorphTransition(20, 13, 15, 6, 0, 0, 11, 14, 0, 0, 24, 16); + _vm->delay(15 * _vm->_tickLength); + + int dy = 90; + for (int i = -40; i <= 0 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 4) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->fillRect(0, 0, 87, 112, 157); + _screen->drawShape(2, shp2, 0, dy, 0); + _screen->copyRegion(0, 0, 120, 48, 88, 112, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + dy -= 5; + } + + _vm->snd_playSoundEffect(13); + + for (int i = -40; i <= 0 && !_vm->shouldQuit() && !_vm->skipFlag(); i += 4) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->fillRect(0, 0, 87, 39, 157); + _screen->drawShape(2, shp1, 0, i, 0); + _screen->copyRegion(0, 0, 120, 48, 88, 112, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + delete[] shp1; + delete[] shp2; + _vm->delay(48 * _vm->_tickLength); +} + +void EoBIntroPlayer::waterdeepExit() { + if (_vm->shouldQuit() || _vm->skipFlag()) + return; + + uint8 *shp2[31]; + uint8 *shp3[3]; + + _screen->loadBitmap(_filesWdExit[0], 5, 3, 0); + _screen->setCurPage(2); + for (int i = 0; i < 31; i++) + shp2[i] = _screen->encodeShape(_wdEncodeX[i], 136 + (_wdEncodeY[i] << 3), _wdEncodeWH[i], _wdEncodeWH[i] << 3, true, _vm->_cgaMappingAlt); + for (int i = 0; i < 3; i++) + shp3[i] = _screen->encodeShape(5 * i + 15, 152, 5, 32, true, _vm->_cgaMappingAlt); + uint8 *shp1 = _screen->encodeShape(31, 136, 5, 32, true, _vm->_cgaMappingAlt); + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + _screen->copyRegion(0, 0, 0, 136, 48, 36, 4, 4, Screen::CR_NO_P_CHECK); + _screen->fillRect(0, 168, 319, 199, 12, 0); + _screen->copyRegion(160, 0, 80, 24, 160, 136, 4, 0, Screen::CR_NO_P_CHECK); + + int cx = 140; + int cy = 128; + + for (int i = 0; i < 70 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + 3 * _vm->_tickLength; + int fx = cx - 2; + if (fx < 160) + fx = 160; + int fy = cy - 2; + if (fy > 98) + fy = 98; + + _screen->copyRegion(fx, fy, 0, 0, 48, 36, 4, 4, Screen::CR_NO_P_CHECK); + _screen->drawShape(4, shp3[((i & 3) == 3) ? 1 : (i & 3)], cx, cy, 0); + _screen->drawShape(4, shp1, 160, 104, 0); + _screen->copyRegion(fx, fy, fx - 80, fy + 24, 48, 36, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, fx, fy, 48, 36, 4, 4, Screen::CR_NO_P_CHECK); + cx++; + cy--; + + for (int ii = 0; ii < 5; ii++) { + int s = _vm->_rnd.getRandomNumber(255) % 31; + _screen->drawShape(0, shp2[s], _wdDsX[s] - 80, _wdDsY[s] + 24, 0); + } + + if (!(_vm->_rnd.getRandomNumber(255) & 7)) + _vm->snd_playSoundEffect(_vm->_rnd.getRandomBit() ? 5 : 14); + + _screen->updateScreen(); + _vm->delayUntil(end); + } + + for (int i = 0; i < 3; i++) + delete[] shp3[i]; + + for (int i = 0; i < 31; i++) + delete[] shp2[i]; + delete[] shp1; + + _screen->setCurPage(0); + _screen->fillRect(0, 168, 319, 199, 12, 0); + _screen->copyRegion(0, 136, 0, 0, 48, 36, 0, 4, Screen::CR_NO_P_CHECK); + + loadAndSetPalette(_filesWdExit[1]); + _screen->loadBitmap(_filesWdExit[2], 3, 5, 0); + _screen->convertPage(5, 2, _vm->_cgaMappingAlt); + whirlTransition(); + _vm->delay(6 * _vm->_tickLength); + + _screen->copyRegion(0, 144, 0, 184, 320, 16, 6, 0, Screen::CR_NO_P_CHECK); + + cx = 0; + cy = 136; + int dy = 0; + for (int i = 0; i < 19 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->copyRegion(cx, cy, 80, dy + 16, 160, 8, 2, 0, Screen::CR_NO_P_CHECK); + cy += 8; + dy += 8; + if (i == 6) { + cx = 160; + cy = 0; + } + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _vm->snd_playSong(3); + _vm->delay(60 * _vm->_tickLength); + + for (int i = 0; i < 56 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->copyRegion(0, 136 + i, 80, 16, 160, 56 - i, 2, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(160, 0, 80, 72 - i, 160, 96 + i, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + for (int i = 1; i < 48 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->copyRegion(160, i, 80, 16, 160, 152, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _screen->loadBitmap(_filesWdExit[3], 3, 5, 0); + _screen->convertPage(5, 2, _vm->_cgaMappingAlt); + _vm->delay(30 * _vm->_tickLength); + _screen->setCurPage(0); + _screen->fillRect(0, 16, 319, 31, 12); + _screen->fillRect(0, 136, 319, 199, 12); + _screen->copyRegion(0, 0, 80, 32, 160, 120, 2, 0, Screen::CR_NO_P_CHECK); + loadAndSetPalette(_filesWdExit[4]); + _screen->updateScreen(); + _vm->delay(50 * _vm->_tickLength); +} + +void EoBIntroPlayer::tunnel() { + if (_vm->shouldQuit() || _vm->skipFlag()) + return; + + _screen->setCurPage(4); + uint8 *shp2 = _screen->encodeShape(20, 0, 20, 120, true, _vm->_cgaMappingAlt); + uint8 *shp1 = _screen->encodeShape(0, 0, 20, 120, true, _vm->_cgaMappingAlt); + _vm->drawBlockObject(1, 4, shp2, 160, 0, 0); + _vm->drawBlockObject(1, 4, shp1, 0, 0, 0); + delete[] shp1; + delete[] shp2; + + for (int i = 0; i < 3 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + 8 * _vm->_tickLength; + _screen->copyRegion(0, 0, 80, 32, 160, 120, 4, 0, Screen::CR_NO_P_CHECK); + _vm->snd_playSoundEffect(7); + _screen->updateScreen(); + _vm->delayUntil(end); + _screen->copyRegion(0, 0, 80, 32, 160, 120, 2, 0, Screen::CR_NO_P_CHECK); + _vm->snd_playSoundEffect(7); + end = _vm->_system->getMillis() + 8 * _vm->_tickLength; + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _screen->copyRegion(0, 160, 0, 184, 320, 16, 6, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(18 * _vm->_tickLength); + _screen->copyRegion(160, 0, 80, 32, 160, 120, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(5 * _vm->_tickLength); + _screen->copyRegion(0, 122, 80, 32, 160, 60, 2, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(160, 122, 80, 92, 160, 60, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(5 * _vm->_tickLength); + _screen->copyRegion(160, 0, 80, 32, 160, 120, 4, 0, Screen::CR_NO_P_CHECK); + for (int i = 0; i < 6; i++) + _screen->copyRegion(i * 48, 185, 56, (i << 3) + 24, 48, 8, 2, 2, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(5 * _vm->_tickLength); + _screen->copyRegion(0, 0, 80, 32, 160, 120, 2, 0, Screen::CR_NO_P_CHECK); + + _screen->loadBitmap(_filesTunnel[0], 5, 3, 0); + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + _screen->updateScreen(); + _vm->delay(40 * _vm->_tickLength); + + _screen->copyRegion(264, 0, 136, 56, 48, 48, 4, 0, Screen::CR_NO_P_CHECK); + _vm->snd_playSoundEffect(8); + _screen->copyRegion(0, 0, 0, 0, 320, 184, 0, 2, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(16 * _vm->_tickLength); + _vm->snd_playSoundEffect(4); + + for (int i = 0; i < 30 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + if (i == 0) + _screen->fillRect(0, 168, 319, 199, 12, 0); + _screen->copyRegion(80, 25 + (_vm->_rnd.getRandomNumber(255) & 7), 80, 24, 160, 144, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delayUntil(end); + } + + _vm->snd_playSoundEffect(9); + + for (int i = 0; i < 6 && !_vm->shouldQuit() && !_vm->skipFlag(); i++) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->copyRegion(_tvlX1[i] << 3, _tvlY1[i], _tvlX2[i] << 3, _tvlY2[i], _tvlW[i] << 3, _tvlH[i], 4, 2, Screen::CR_NO_P_CHECK); + for (int ii = 0; ii < 4 && !_vm->shouldQuit() && !_vm->skipFlag(); ii++) { + _screen->updateScreen(); + _vm->delayUntil(end); + end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->copyRegion(80, 25 + (_vm->_rnd.getRandomNumber(255) & 7), 80, 24, 160, 144, 2, 0, Screen::CR_NO_P_CHECK); + } + } + _screen->copyRegion(0, 0, 0, 0, 320, 168, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(40 * _vm->_tickLength); + + _screen->loadBitmap(_filesTunnel[1], 5, 3, 0); + _screen->convertPage(3, 4, _vm->_cgaMappingAlt); + _vm->snd_playSoundEffect(6); + _screen->copyRegion(0, 0, 80, 32, 160, 120, 4, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(2 * _vm->_tickLength); + _screen->copyRegion(160, 0, 80, 32, 160, 120, 4, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _vm->delay(2 * _vm->_tickLength); + _screen->copyRegion(0, 120, 80, 30, 160, 64, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(160, 120, 80, 94, 160, 64, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 176, 0, 184, 320, 16, 6, 0, Screen::CR_NO_P_CHECK); + _screen->setCurPage(0); + _screen->updateScreen(); + _vm->delay(50 * _vm->_tickLength); +} + +void EoBIntroPlayer::loadAndSetPalette(const char *filename) { + if (_vm->_configRenderMode == Common::kRenderCGA || _vm->_configRenderMode == Common::kRenderEGA) + return; + _screen->loadPalette(filename, _screen->getPalette(0)); + _screen->getPalette(0).fill(0, 1, 0); + _screen->setScreenPalette(_screen->getPalette(0)); +} + +void EoBIntroPlayer::copyBlurRegion(int x1, int y1, int x2, int y2, int w, int h, int step) { + const uint8 *ptr2 = _screen->getCPagePtr(3) + y1 * 320 + x1; + + if (step == 1) { + while (h > 0) { + int dx = x2; + for (int i = 0; i < w; i += 2) { + _screen->setPagePixel(3, dx++, y2, ptr2[i]); + _screen->setPagePixel(3, dx++, y2, 0); + } + dx = x2; + y2++; + ptr2 += 320; + for (int i = 0; i < w; i += 2) { + _screen->setPagePixel(3, dx++, y2, 0); + _screen->setPagePixel(3, dx++, y2, ptr2[i + 1]); + } + y2++; + ptr2 += 320; + h -= 2; + } + } else if (step == 2) { + while (h > 0) { + int dx = x2; + for (int i = 0; i < w; i += 2) { + _screen->setPagePixel(3, dx++, y2, ptr2[i]); + _screen->setPagePixel(3, dx++, y2, 0); + } + dx = x2; + y2++; + ptr2 += 320; + for (int i = 0; i < w; i++) + _screen->setPagePixel(3, dx++, y2, 0); + + y2++; + ptr2 += 320; + h -= 2; + } + } else if (step == 3) { + for (int i = 0; i < h; i++) { + int dx = x2; + if ((i % 3) == 0) { + int ii = 0; + for (; ii < w - 3; ii += 3) { + _screen->setPagePixel(3, dx++, y2, ptr2[ii]); + _screen->setPagePixel(3, dx++, y2, 0); + _screen->setPagePixel(3, dx++, y2, 0); + } + for (; ii < w; ii++) + _screen->setPagePixel(3, dx++, y2, 0); + } else { + for (int ii = 0; ii < w; ii++) + _screen->setPagePixel(3, dx++, y2, 0); + } + y2++; + ptr2 += 320; + } + } +} + +void EoBIntroPlayer::boxMorphTransition(int targetDestX, int targetDestY, int targetFinalX, int targetFinalY, int targetSrcX, int targetSrcY, int targetFinalW, int targetFinalH, int originX1, int originY1, int originW, int originH) { + int originX2 = originX1 + originW; + int originY2 = originY1 + originH; + if (originY2 > 21) + originY2 = 21; + + int w = 1; + int h = 1; + for (bool runloop = true; runloop && !_vm->shouldQuit() && !_vm->skipFlag();) { + uint32 end = _vm->_system->getMillis() + _vm->_tickLength; + _screen->copyRegion(targetSrcX << 3, targetSrcY << 3, targetDestX << 3, targetDestY << 3, w << 3, h << 3, 2, 0, Screen::CR_NO_P_CHECK); + if (originX1 < targetDestX) + _screen->copyRegion(312, 0, originX1 << 3, 0, 8, 176, 0, 0, Screen::CR_NO_P_CHECK); + if (originY1 < targetDestY) + _screen->copyRegion(0, 192, 0, originY1 << 3, 320, 8, 0, 0, Screen::CR_NO_P_CHECK); + if ((targetFinalX + targetFinalW) <= originX2) + _screen->copyRegion(312, 0, originX2 << 3, 0, 8, 176, 0, 0, Screen::CR_NO_P_CHECK); + if ((targetFinalY + targetFinalH) <= originY2) + _screen->copyRegion(0, 192, 0, originY2 << 3, 320, 8, 0, 0, Screen::CR_NO_P_CHECK); + + if (!(targetDestX != targetFinalX || targetDestY != targetFinalY || w != targetFinalW || h != targetFinalH || originX1 < targetFinalX || originY1 < targetFinalY || (targetFinalX + targetFinalW) < originX2 || (targetFinalY + targetFinalH) < originY2)) + runloop = false; + + int v = targetFinalX - targetDestX; + v = (v < 0) ? -1 : ((v > 0) ? 1 : 0); + targetDestX += v; + v = targetFinalY - targetDestY; + v = (v < 0) ? -1 : ((v > 0) ? 1 : 0); + targetDestY += v; + + if (w != targetFinalW) + w += 2; + if (w > targetFinalW) + w = targetFinalW; + + if (h != targetFinalH) + h += 2; + if (h > targetFinalH) + h = targetFinalH; + + if (++originX1 > targetFinalX) + originX1 = targetFinalX; + + if (++originY1 > targetFinalY) + originY1 = targetFinalY; + + if ((targetFinalX + targetFinalW) < originX2) + originX2--; + + if ((targetFinalY + targetFinalH) < originY2) + originY2--; + + _screen->updateScreen(); + _vm->delayUntil(end); + } +} + +void EoBIntroPlayer::whirlTransition() { + for (int i = 0; i < 2; i++) { + for (int ii = 0; ii < 8; ii++) { + uint32 e = _vm->_system->getMillis() + 3; + if (ii & 1) { + for (int iii = i + ii; iii < 320; iii += 8) + _screen->drawClippedLine(iii, 0, iii, 199, 12); + } else { + for (int iii = i + ii; iii < 200; iii += 8) + _screen->drawClippedLine(0, iii, 319, iii, 12); + } + _screen->updateScreen(); + uint32 c = _vm->_system->getMillis(); + if (e > c) + _vm->_system->delayMillis(e - c); + } + } +} + +int EoBEngine::mainMenu() { + int menuChoice = _menuChoiceInit; + _menuChoiceInit = 0; + + Screen::FontId of = _screen->_currentFont; + + while (menuChoice >= 0 && !shouldQuit()) { + switch (menuChoice) { + case 0: { + if (_configRenderMode != Common::kRenderEGA) + _screen->loadPalette("EOBPAL.COL", _screen->getPalette(0)); + _screen->loadEoBBitmap("INTRO", _cgaMappingDefault, 5, 3, 2); + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->_curPage = 2; + of = _screen->setFont(Screen::FID_6_FNT); + Common::String versionString(Common::String::format("ScummVM %s", gScummVMVersion)); + _screen->printText(versionString.c_str(), 280 - versionString.size() * 6, 153, _screen->getPagePixel(2, 0, 0), 0); + _screen->setFont(of); + _screen->fillRect(0, 159, 319, 199, _screen->getPagePixel(2, 0, 0)); + gui_drawBox(77, 165, 173, 29, 14, 13, 12); + gui_drawBox(76, 164, 175, 31, 14, 13, -1); + _screen->_curPage = 0; + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _allowImport = true; + menuChoice = mainMenuLoop(); + _allowImport = false; + } break; + + case 1: + // load game in progress + menuChoice = -1; + break; + + case 2: + // create new party + menuChoice = -2; + break; + + case 3: + // quit + menuChoice = -5; + break; + + case 4: + // intro + _sound->loadSoundFile("SOUND"); + _screen->hideMouse(); + seq_playIntro(); + _screen->showMouse(); + _sound->loadSoundFile("ADLIB"); + menuChoice = 0; + break; + } + } + + return shouldQuit() ? -5 : menuChoice; +} + +int EoBEngine::mainMenuLoop() { + int sel = -1; + do { + _screen->setScreenDim(28); + _gui->simpleMenu_setup(8, 0, _mainMenuStrings, -1, 0, 0); + + while (sel == -1 && !shouldQuit()) + sel = _gui->simpleMenu_process(8, _mainMenuStrings, 0, -1, 0); + } while ((sel < 0 || sel > 5) && !shouldQuit()); + + return sel + 1; +} + +void EoBEngine::seq_playIntro() { + EoBIntroPlayer(this, _screen).start(); +} + +void EoBEngine::seq_playFinale() { + Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT"); + _screen->loadFileDataToPage(s, 5, 32000); + delete s; + + snd_playSoundEffect(20); + + _txt->resetPageBreakString(); + _txt->setWaitButtonMode(1); + _txt->setupField(12, true); + gui_drawBox(0, 0, 176, 175, guiSettings()->colors.frame1, guiSettings()->colors.frame2, guiSettings()->colors.fill); + _txt->printDialogueText(51, _moreStrings[0]); + + if (!checkScriptFlags(0x1FFE)) { + _screen->fadeToBlack(); + return; + } + + _txt->printDialogueText(_finBonusStrings[0]); + for (int i = 0; i < 6; i++) { + _txt->printDialogueText(_finBonusStrings[1]); + if (_characters[i].flags & 1) + _txt->printDialogueText(_characters[i].name); + } + + uint32 password = 0; + for (int i = 0; i < 4; i++) { + if (!(_characters[i].flags & 1)) + continue; + + int len = strlen(_characters[i].name); + for (int ii = 0; ii < len; ii++) { + uint32 c = _characters[i].name[ii]; + password += (c * c); + } + } + + _txt->printDialogueText(Common::String::format(_finBonusStrings[2], password).c_str(), true); + _screen->fadeToBlack(); +} + +void EoBEngine::seq_xdeath() { + uint8 *shapes1[5]; + uint8 *shapes2; + + _screen->loadShapeSetBitmap("XDEATH2", 5, 3); + for (int i = 0; i < 4; i++) + shapes1[i] = _screen->encodeShape(i / 2 * 14, i / 2 * 88, 14, 88, true, _cgaMappingDefault); + _screen->loadShapeSetBitmap("XDEATH3", 5, 3); + shapes2 = _screen->encodeShape(22, 0, 16, 95, true, _cgaMappingDefault); + _screen->loadEoBBitmap("XDEATH1", _cgaMappingDefault, 5, 3, -1); + _screen->convertPage(3, 2, _cgaMappingDefault); + _screen->setCurPage(0); + + for (int i = 0; i < 10; i++) { + if (i == 2) + snd_playSoundEffect(72); + else if (i == 4 || i == 6) + snd_playSoundEffect(54); + else + snd_playSoundEffect(34); + + if (i < 6) { + _screen->copyRegion((i % 3) * 104, i / 3 * 88, 32, 10, 104, 88, 2, 0, Screen::CR_NO_P_CHECK); + } else { + snd_playSoundEffect(42); + _screen->drawShape(0, shapes1[i - 6], 32, 10, 0); + } + + _screen->updateScreen(); + delay(4 * _tickLength); + } + + const ScreenDim *dm = _screen->getScreenDim(5); + _screen->modifyScreenDim(5, dm->sx, 8, dm->w, dm->h); + _screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 5, Screen::CR_NO_P_CHECK); + + for (int i = 0; i < 19; i++) { + snd_playSoundEffect(119); + _screen->copyRegion(0, 0, 0, 0, 176, 120, 5, 2, Screen::CR_NO_P_CHECK); + _screen->drawShape(2, shapes2, 24, i * 5 - 90, 5); + _screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + delay(2 * _tickLength); + } + + _screen->modifyScreenDim(5, dm->sx, 0, dm->w, dm->h); + + snd_playSoundEffect(5); + delay(60 * _tickLength); + + for (int i = 0; i < 4; i++) + delete[] shapes1[i]; + delete[] shapes2; + + gui_drawPlayField(false); + gui_drawAllCharPortraitsWithStats(); +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/sequence/sequences_hof.cpp b/engines/kyra/sequence/sequences_hof.cpp new file mode 100644 index 0000000000..5f41faad15 --- /dev/null +++ b/engines/kyra/sequence/sequences_hof.cpp @@ -0,0 +1,3515 @@ +/* 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 "kyra/engine/kyra_hof.h" +#include "kyra/graphics/screen_hof.h" +#include "kyra/graphics/screen_lol.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" +#include "kyra/sequence/sequences_hof.h" +#include "kyra/engine/timer.h" + +#include "common/system.h" + +namespace Kyra { + +enum SequenceID { + kSequenceNoLooping = -1, + kSequenceVirgin = 0, + kSequenceWestwood, + kSequenceTitle, + kSequenceOverview, + kSequenceLibrary, + kSequenceHand, + kSequencePoint, + kSequenceZanfaun, + + kSequenceFunters, + kSequenceFerb, + kSequenceFish, + kSequenceFheep, + kSequenceFarmer, + kSequenceFuards, + kSequenceFirates, + kSequenceFrash, + + kSequenceHoFDemoVirgin, + kSequenceHoFDemoWestwood, + kSequenceHoFDemoTitle, + kSequenceHoFDemoHill, + kSequenceHoFDemoOuthome, + kSequenceHoFDemoWharf, + kSequenceHoFDemoDinob, + kSequenceHoFDemoFisher, + +// The following enums remain active even if LoL is disabled + kSequenceLoLDemoScene1, + kSequenceLoLDemoText1, + kSequenceLoLDemoScene2, + kSequenceLoLDemoText2, + kSequenceLoLDemoScene3, + kSequenceLoLDemoText3, + kSequenceLoLDemoScene4, + kSequenceLoLDemoText4, + kSequenceLoLDemoScene5, + kSequenceLoLDemoText5, + kSequenceLoLDemoScene6, + + kSequenceArraySize +}; + +enum NestedSequenceID { + kNestedSequenceFiggle = 0, + + kNestedSequenceOver1, + kNestedSequenceOver2, + kNestedSequenceForest, + kNestedSequenceDragon, + kNestedSequenceDarm, + kNestedSequenceLibrary2, + kNestedSequenceLibrary3, + kNestedSequenceMarco, + kNestedSequenceHand1a, + kNestedSequenceHand1b, + kNestedSequenceHand1c, + kNestedSequenceHand2, + kNestedSequenceHand3, + kNestedSequenceHand4, + + kNestedSequenceHoFDemoWharf2, + kNestedSequenceHoFDemoDinob2, + kNestedSequenceHoFDemoWater, + kNestedSequenceHoFDemoBail, + kNestedSequenceHoFDemoDig, + + kNestedSequenceArraySize +}; + +typedef int (SeqPlayer_HOF::*SeqProc)(WSAMovie_v2 *, int, int, int); + +struct SeqPlayerConfig { + SeqPlayerConfig(const HoFSeqData *data, const SeqProc *callbacks, const SeqProc *nestedCallbacks) : seq(data->seq), seqProc(callbacks), numSeq(data->numSeq), nestedSeq(data->nestedSeq), nestedSeqProc(nestedCallbacks), numNestedSeq(data->numNestedSeq) {} + const HoFSequence *seq; + const SeqProc *seqProc; + int numSeq; + const HoFNestedSequence *nestedSeq; + const SeqProc *nestedSeqProc; + int numNestedSeq; +}; + +class SeqPlayer_HOF { +public: + SeqPlayer_HOF(KyraEngine_v1 *vm, Screen_v2 *screen, OSystem *system, bool startupSaveLoadable = false); + ~SeqPlayer_HOF(); + + int play(SequenceID firstScene, SequenceID loopStartScene); + void pause(bool toggle); + + static SeqPlayer_HOF *instance() { return _instance; } + +private: + // Init + void setupCallbacks(); + + // Playback loop + void runLoop(); + void playScenes(); + + bool checkAbortPlayback(); + bool checkPlaybackStatus(); + + bool _abortRequested; + uint32 _pauseStart; + + // Sequence transitions + void doTransition(int type); + void nestedFrameAnimTransition(int srcPage, int dstPage, int delaytime, int steps, int x, int y, int w, int h, int openClose, int directionFlags); + void nestedFrameFadeTransition(const char *cmpFile); + + // Animations + void playAnimation(WSAMovie_v2 *wsaObj, int startFrame, int numFrames, int frameRate, int x, int y, const SeqProc callback, Palette *fadePal1, Palette *fadePal2, int fadeRate, bool restoreScreen); + void playDialogueAnimation(uint16 strID, uint16 soundID, int textColor, int textPosX, int textPosY, int textWidth, WSAMovie_v2 *wsaObj, int animStartFrame, int animLastFrame, int animPosX, int animPosY); + + void startNestedAnimation(int animSlot, int sequenceID); + void closeNestedAnimation(int animSlot); + void unloadNestedAnimation(int animSlot); + void doNestedFrameTransition(int transitionType, int animSlot); + void updateAllNestedAnimations(); + bool updateNestedAnimation(int animSlot); + + struct AnimSlot { + SeqProc callback; + WSAMovie_v2 *movie; + const FrameControl *control; + int16 flags; + uint16 startFrame; + uint16 endFrame; + uint16 frameDelay; + uint32 nextFrame; + uint16 currentFrame; + uint16 lastFrame; + uint16 x; + uint16 y; + uint16 fadeInTransitionType; + uint16 fadeOutTransitionType; + }; + + AnimSlot _animSlots[8]; + + bool _updateAnimations; + uint32 _animDuration; + int _animCurrentFrame; + int _callbackCurrentFrame; + + // The only reason to declare these here (instead of just locally) is being able to increase them after pausing the Engine + uint32 _specialAnimTimeOutTotal; + uint32 _specialAnimFrameTimeOut; + + // Subtitles/Dialogue/Sound + void playSoundEffect(uint16 id, int16 vol); + void playSoundAndDisplaySubTitle(uint16 id); + void printFadingText(uint16 strID, int x, int y, const uint8 *colorMap, uint8 textcolor); + + int displaySubTitle(uint16 strID, uint16 posX, uint16 posY, int duration, uint16 width); + void updateSubTitles(); + char *preprocessString(const char *str, int width); + void waitForSubTitlesTimeout(); + uint32 ticksTillSubTitlesTimeout(); + void resetAllTextSlots(); + + void fadeOutMusic(); + + struct TextSlot { + uint16 strIndex; + uint16 x; + uint16 y; + uint16 width; + int32 duration; + uint32 startTime; + int16 textcolor; + }; + + TextSlot _textSlots[10]; + + char *_tempString; + + uint8 _textColor[2]; + uint8 _textColorMap[16]; + int _textDuration[33]; + + const char *const *_sequenceStrings; + const char *const *_sequenceSoundList; + int _sequenceSoundListSize; + + static const uint8 _textColorPresets[]; + + // HOF credits + void playHoFTalkieCredits(); + void displayHoFTalkieScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData = 0, const char *const *specialData = 0); + + bool _talkieFinaleExtraFlag; + + // HOF+LOL demo specific + void updateDemoAdText(int bottom, int top); + + ActiveItemAnim _hofDemoActiveItemAnim[5]; + const HoFSeqItemAnimData *_hofDemoAnimData; + + uint32 _fisherAnimCurTime; + int _scrollProgressCounter; + + uint8 *_hofDemoShapeData; + uint8 *_hofDemoItemShapes[20]; + + // Misc + void delayTicks(uint32 ticks); + void delayUntil(uint32 dest); + void setCountDown(uint32 ticks); + bool countDownRunning(); + + uint32 _countDownRemainder; + uint32 _countDownLastUpdate; + + enum SeqPlayerTargetInfo { + kHoF = 0, + kHoFDemo, + kLoLDemo + }; + + SeqPlayerTargetInfo _target; + int _firstScene, _loopStartScene, _curScene, _preventSkipBeforeScene, _lastScene; + bool _startupSaveLoadable, _isFinale, _preventLooping; + + SeqPlayerConfig *_config; + + MainMenu *_menu; + int _result; + + bool _abortPlayback; + + KyraEngine_v1 *_vm; + Screen_v2 *_screen; + // We might consider getting rid of Screen_HoF, since there are only 2 methods left in that class anyway + Screen_HoF *_screenHoF; + OSystem *_system; + + static SeqPlayer_HOF *_instance; + +private: + // Sequence specific callback functions + int cbHOF_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_title(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_overview(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_library(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_point(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_zanfaun(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOF_over1(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_over2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_forest(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_dragon(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_darm(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_library2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_marco(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand1a(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand1b(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand1c(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_hand3(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOF_funters(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_ferb(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_fish(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_fheep(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_farmer(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_fuards(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_firates(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOF_frash(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOF_figgle(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOFDEMO_virgin(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_title(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_hill(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_outhome(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_wharf(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_dinob(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_fisher(WSAMovie_v2 *wsaObj, int x, int y, int frm); + + int cbHOFDEMO_wharf2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_dinob2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_water(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_bail(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbHOFDEMO_dig(WSAMovie_v2 *wsaObj, int x, int y, int frm); + +#ifdef ENABLE_LOL + int cbLOLDEMO_scene1(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene2(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene3(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene4(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene5(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_text5(WSAMovie_v2 *wsaObj, int x, int y, int frm); + int cbLOLDEMO_scene6(WSAMovie_v2 *wsaObj, int x, int y, int frm); +#endif // ENABLE_LOL +}; + +SeqPlayer_HOF *SeqPlayer_HOF::_instance = 0; + +SeqPlayer_HOF::SeqPlayer_HOF(KyraEngine_v1 *vm, Screen_v2 *screen, OSystem *system, bool startupSaveLoadable) : _vm(vm), _screen(screen), _system(system), _startupSaveLoadable(startupSaveLoadable) { + // We use a static pointer for pauseEngine functionality. Since we don't + // ever need more than one SeqPlayer_HOF object at the same time we keep + // this simple and just add an assert to detect typos, regressions, etc. + assert(_instance == 0); + + memset(_animSlots, 0, sizeof(_animSlots)); + memset(_textSlots, 0, sizeof(_textSlots)); + memset(_hofDemoActiveItemAnim, 0, sizeof(_hofDemoActiveItemAnim)); + + _screenHoF = _vm->game() == GI_KYRA2 ? (Screen_HoF*)screen : 0; + _config = 0; + _result = 0; + _sequenceSoundList = 0; + _hofDemoAnimData = 0; + _hofDemoShapeData = 0; + _isFinale = false; + _preventLooping = false; + _menu = 0; + _abortRequested = false; + _pauseStart = 0; + + _updateAnimations = false; + _animDuration = 0; + _animCurrentFrame = 0; + _callbackCurrentFrame = 0; + + _abortPlayback = false; + _curScene = 0; + _preventSkipBeforeScene = -1; + _lastScene = 0; + + _scrollProgressCounter = 0; + _fisherAnimCurTime = 0; + + _tempString = new char[200]; + + _countDownRemainder = 0; + _countDownLastUpdate = 0; + + int tempSize = 0; + _vm->resource()->unloadAllPakFiles(); + _vm->resource()->loadPakFile(StaticResource::staticDataFilename()); + const char *const *files = _vm->staticres()->loadStrings(k2SeqplayPakFiles, tempSize); + _vm->resource()->loadFileList(files, tempSize); + + _sequenceStrings = _vm->staticres()->loadStrings(k2SeqplayStrings, tempSize); + uint8 multiplier = (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) ? 12 : 8; + for (int i = 0; i < MIN(33, tempSize); i++) + _textDuration[i] = (int)strlen(_sequenceStrings[i]) * multiplier; + + if (_sequenceSoundList) { + for (int i = 0; i < _sequenceSoundListSize; i++) { + if (_sequenceSoundList[i]) + delete[] _sequenceSoundList[i]; + } + delete[] _sequenceSoundList; + _sequenceSoundList = 0; + } + + const char *const *seqSoundList = _vm->staticres()->loadStrings(k2SeqplaySfxFiles, _sequenceSoundListSize); + + // replace sequence talkie files with localized versions + const char *const *tlkfiles = _vm->staticres()->loadStrings(k2SeqplayTlkFiles, tempSize); + char **tmpSndLst = new char *[_sequenceSoundListSize]; + + for (int i = 0; i < _sequenceSoundListSize; i++) { + const int len = strlen(seqSoundList[i]); + + tmpSndLst[i] = new char[len + 1]; + tmpSndLst[i][0] = 0; + + if (tlkfiles && len > 1) { + for (int ii = 0; ii < tempSize; ii++) { + if (strlen(tlkfiles[ii]) > 1 && !scumm_stricmp(&seqSoundList[i][1], &tlkfiles[ii][1])) + strcpy(tmpSndLst[i], tlkfiles[ii]); + } + } + + if (tmpSndLst[i][0] == 0) + strcpy(tmpSndLst[i], seqSoundList[i]); + } + + tlkfiles = seqSoundList = 0; + _vm->staticres()->unloadId(k2SeqplayTlkFiles); + _vm->staticres()->unloadId(k2SeqplaySfxFiles); + _sequenceSoundList = tmpSndLst; + + if (_vm->gameFlags().platform == Common::kPlatformPC98) + _vm->sound()->loadSoundFile("SOUND.DAT"); + + _screen->loadFont(_screen->FID_GOLDFONT_FNT, "GOLDFONT.FNT"); + _screen->setFont(_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_GOLDFONT_FNT); + + if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) { + if (_vm->game() == GI_KYRA2) { + _hofDemoAnimData = _vm->staticres()->loadHoFSeqItemAnimData(k2SeqplayShapeAnimData, tempSize); + uint8 *shp = _vm->resource()->fileData("ICONS.SHP", 0); + uint32 outsize = READ_LE_UINT16(shp + 4); + _hofDemoShapeData = new uint8[outsize]; + Screen::decodeFrame4(shp + 10, _hofDemoShapeData, outsize); + for (int i = 0; i < 20; i++) + _hofDemoItemShapes[i] = _screen->getPtrToShape(_hofDemoShapeData, i); + delete[] shp; + } + } else { + const MainMenu::StaticData data = { + { _sequenceStrings[97], _sequenceStrings[96], _sequenceStrings[95], _sequenceStrings[98], 0 }, + { 0x01, 0x04, 0x0C, 0x04, 0x00, 0xD7, 0xD6 }, + { 0xD8, 0xDA, 0xD9, 0xD8 }, + (_vm->gameFlags().lang == Common::JA_JPN) ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT, 240 + }; + + _menu = new MainMenu(_vm); + _menu->init(data, MainMenu::Animation()); + } + + _instance = this; +} + +SeqPlayer_HOF::~SeqPlayer_HOF() { + _instance = 0; + + if (_sequenceSoundList) { + for (int i = 0; i < _sequenceSoundListSize; i++) { + if (_sequenceSoundList[i]) + delete[] _sequenceSoundList[i]; + } + delete[] _sequenceSoundList; + _sequenceSoundList = NULL; + } + + delete[] _tempString; + delete[] _hofDemoShapeData; + delete _menu; + + if (_vm->game() != GI_LOL) + _screen->setFont(_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); +} + +int SeqPlayer_HOF::play(SequenceID firstScene, SequenceID loopStartScene) { + bool incompatibleData = false; + AudioResourceSet soundSet = kMusicIntro; + _firstScene = firstScene; + _loopStartScene = loopStartScene; + _preventLooping = false; + _result = 0; + + if (firstScene >= kSequenceArraySize || firstScene < kSequenceVirgin || loopStartScene >= kSequenceArraySize || loopStartScene < kSequenceNoLooping) { + return 0; + } else if (firstScene >= kSequenceLoLDemoScene1) { +#ifndef ENABLE_LOL + error("SeqPlayer_HOF::play(): The Lands of Lore sub engine (including this non-interactive demo) has been disabled in this build"); +#endif + incompatibleData = (_vm->game() != GI_LOL); + _firstScene -= kSequenceLoLDemoScene1; + if (loopStartScene != kSequenceNoLooping) + _loopStartScene -= kSequenceLoLDemoScene1; + _lastScene = kSequenceLoLDemoScene6 - kSequenceLoLDemoScene1; + _target = kLoLDemo; + _screen->_charWidth = 0; + } else if (firstScene >= kSequenceHoFDemoVirgin) { + incompatibleData = (_vm->game() != GI_KYRA2 || !_vm->gameFlags().isDemo || _vm->gameFlags().isTalkie); + _firstScene -= kSequenceHoFDemoVirgin; + if (loopStartScene != kSequenceNoLooping) + _loopStartScene -= kSequenceHoFDemoVirgin; + _lastScene = kSequenceHoFDemoFisher - kSequenceHoFDemoVirgin; + _target = kHoFDemo; + _screen->_charWidth = -2; + } else { + _isFinale = _preventLooping = firstScene > kSequenceZanfaun; + incompatibleData = (_vm->game() != GI_KYRA2 || (_vm->gameFlags().isDemo && (!_vm->gameFlags().isTalkie || _isFinale))); + _target = kHoF; + _screen->_charWidth = -2; + if (_isFinale) { + soundSet = kMusicFinale; + _lastScene = kSequenceFrash; + } else { + _lastScene = kSequenceZanfaun; + } + } + + if (incompatibleData) + error("SeqPlayer_HOF::play(): Specified sequences do not match the available sequence data for this target"); + + _vm->sound()->selectAudioResourceSet(soundSet); + _vm->sound()->loadSoundFile(0); + + setupCallbacks(); + runLoop(); + + return _result; +} + +void SeqPlayer_HOF::pause(bool toggle) { + if (toggle) { + _pauseStart = _system->getMillis(); + } else { + uint32 pausedTime = _system->getMillis() - _pauseStart; + _pauseStart = 0; + + _countDownLastUpdate += pausedTime; + _fisherAnimCurTime += pausedTime; + _specialAnimTimeOutTotal += pausedTime; + _specialAnimFrameTimeOut += pausedTime; + + for (int i = 0; i < 10; i++) { + if (_textSlots[i].duration != -1) + _textSlots[i].startTime += pausedTime; + } + + for (int i = 0; i < 8; i++) { + if (_animSlots[i].flags != -1) + _animSlots[i].nextFrame += pausedTime; + } + } +} + +void SeqPlayer_HOF::setupCallbacks() { +#define SCB(x) &SeqPlayer_HOF::cbHOF_##x + static const SeqProc seqCallbacksHoF[] = { 0, SCB(westwood), SCB(title), SCB(overview), SCB(library), SCB(hand), SCB(point), SCB(zanfaun), SCB(funters), SCB(ferb), SCB(fish), SCB(fheep), SCB(farmer), SCB(fuards), SCB(firates), SCB(frash) }; + static const SeqProc nestedSeqCallbacksHoF[] = { SCB(figgle), SCB(over1), SCB(over2), SCB(forest), SCB(dragon), SCB(darm), SCB(library2), SCB(library2), SCB(marco), SCB(hand1a), SCB(hand1b), SCB(hand1c), SCB(hand2), SCB(hand3), 0 }; +#undef SCB +#define SCB(x) &SeqPlayer_HOF::cbHOFDEMO_##x + static const SeqProc seqCallbacksHoFDemo[] = { SCB(virgin), SCB(westwood), SCB(title), SCB(hill), SCB(outhome), SCB(wharf), SCB(dinob), SCB(fisher) }; + static const SeqProc nestedSeqCallbacksHoFDemo[] = { SCB(wharf2), SCB(dinob2), SCB(water), SCB(bail), SCB(dig), 0 }; +#undef SCB +#ifdef ENABLE_LOL +#define SCB(x) &SeqPlayer_HOF::cbLOLDEMO_##x + static const SeqProc seqCallbacksLoLDemo[] = { SCB(scene1), 0, SCB(scene2), 0, SCB(scene3), 0, SCB(scene4), 0, SCB(scene5), SCB(text5), SCB(scene6), 0 }; +#undef SCB +#else + static const SeqProc seqCallbacksLoLDemo[] = { 0 }; +#endif + static const SeqProc nestedSeqCallbacksLoLDemo[] = { 0 }; + + static const SeqProc *const seqCallbacks[] = { seqCallbacksHoF, seqCallbacksHoFDemo, seqCallbacksLoLDemo}; + static const SeqProc *const nestedSeqCallbacks[] = { nestedSeqCallbacksHoF, nestedSeqCallbacksHoFDemo, nestedSeqCallbacksLoLDemo}; + + int tmpSize = 0; + delete _config; + _config = new SeqPlayerConfig(_vm->staticres()->loadHoFSequenceData(k2SeqplaySeqData, tmpSize), seqCallbacks[_target], nestedSeqCallbacks[_target]); +} + +void SeqPlayer_HOF::runLoop() { + memset(_animSlots, 0, sizeof(_animSlots)); + memset(_textSlots, 0, sizeof(_textSlots)); + memset(_hofDemoActiveItemAnim, 0, sizeof(_hofDemoActiveItemAnim)); + for (int i = 0; i < 8; ++i) + _animSlots[i].flags = -1; + + _screen->clearPage(10); + _screen->clearPage(12); + _screen->hideMouse(); + int oldPage = _screen->setCurPage(2); + + for (int i = 0; i < 4; ++i) + _screen->getPalette(i).clear(); + + _updateAnimations = false; + _animCurrentFrame = 0; + _textColor[0] = _textColor[1] = 0; + _curScene = _firstScene; + + do { + playScenes(); + doTransition(0); + resetAllTextSlots(); + fadeOutMusic(); + _firstScene = ((!_startupSaveLoadable || _preventLooping) && _curScene >= _loopStartScene) ? kSequenceNoLooping : _loopStartScene; + } while (!_vm->shouldQuit() && _firstScene != kSequenceNoLooping); + + checkPlaybackStatus(); + + for (int i = 0; i < 8; i++) + unloadNestedAnimation(i); + + if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) + _screen->fadeToBlack(); + else if (!_isFinale && !_startupSaveLoadable) + _result = 1; + + if (!_result) + delayTicks(75); + + _screen->setCurPage(oldPage); + _screen->_charWidth = 0; + _screen->showMouse(); +} + +void SeqPlayer_HOF::playScenes() { + _vm->sound()->stopAllSoundEffects(); + _curScene = _firstScene; + + _screen->copyPalette(1, 0); + WSAMovie_v2 anim(_vm); + _abortRequested = false; + + _scrollProgressCounter = 0; + + while (!_vm->shouldQuit()) { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + + _callbackCurrentFrame = 0; + + if (_curScene > _lastScene) + break; + + const Kyra::HoFSequence &sq = _config->seq[_curScene]; + + if (sq.flags & 2) { + _screen->loadBitmap(sq.cpsFile, 2, 2, &_screen->getPalette(0)); + _screen->setScreenPalette(_screen->getPalette(0)); + } else { + _screen->setCurPage(2); + _screen->clearPage(2); + _screen->loadPalette("GOLDFONT.COL", _screen->getPalette(0)); + } + + if (_config->seqProc[_curScene] && !(_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie)) + (this->*_config->seqProc[_curScene])(0, 0, 0, -1); + + if (sq.flags & 1) { + anim.open(sq.wsaFile, 0, &_screen->getPalette(0)); + if (!(sq.flags & 2)) + anim.displayFrame(0, 2, sq.xPos, sq.yPos, 0x4000, 0, 0); + } + + if (sq.flags & 4) { + int cp = _screen->setCurPage(2); + Screen::FontId cf = _screen->setFont(_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_GOLDFONT_FNT); + + if (sq.stringIndex1 != -1) + _screen->printText(_sequenceStrings[sq.stringIndex1], (320 - _screen->getTextWidth(_sequenceStrings[sq.stringIndex1])) / 2, 100 - _screen->getFontHeight(), 1, 0); + + if (sq.stringIndex2 != -1) + _screen->printText(_sequenceStrings[sq.stringIndex2], (320 - _screen->getTextWidth(_sequenceStrings[sq.stringIndex2])) / 2, 100, 1, 0); + + _screen->setFont(cf); + _screen->setCurPage(cp); + } + + _screen->copyPage(2, 12); + _screen->copyPage(0, 2); + _screen->copyPage(2, 10); + _screen->copyPage(12, 2); + + doTransition(sq.fadeInTransitionType); + + if (!(checkAbortPlayback() || _vm->shouldQuit() || _result)) { + _screen->copyPage(2, 0); + _screen->updateScreen(); + } + + if (sq.flags & 1) { + playAnimation(&anim, sq.startFrame, sq.numFrames, sq.duration, sq.xPos, sq.yPos, _config->seqProc[_curScene], &_screen->getPalette(1), &_screen->getPalette(0), 30, 0); + anim.close(); + } else { + _animDuration = sq.duration; + setCountDown(_animDuration); + + while (!checkAbortPlayback() && !_vm->shouldQuit() && (countDownRunning() || _updateAnimations)) { + uint32 endFrame = (_system->getMillis() + _vm->tickLength()) & ~(_vm->tickLength() - 1); + updateAllNestedAnimations(); + + if (_config->seqProc[_curScene]) + (this->*_config->seqProc[_curScene])(0, 0, 0, 0); + + updateSubTitles(); + + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->copyPage(12, 2); + + do { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + } while (_system->getMillis() < endFrame); + } + } + + if (_config->seqProc[_curScene] && !(_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie)) + (this->*_config->seqProc[_curScene])(0, 0, 0, -2); + + uint32 textTimeOut = ticksTillSubTitlesTimeout(); + setCountDown(sq.timeout < textTimeOut ? textTimeOut : sq.timeout); + + while (!checkAbortPlayback() && !_vm->shouldQuit() && (countDownRunning() || _updateAnimations)) { + updateAllNestedAnimations(); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->copyPage(12, 2); + } + + doTransition(sq.fadeOutTransitionType); + _curScene++; + } + + resetAllTextSlots(); + _vm->sound()->haltTrack(); + _vm->sound()->voiceStop(); + + if ((!checkAbortPlayback() || _vm->shouldQuit()) && _vm->gameFlags().isDemo) + _curScene = -1; +} + +bool SeqPlayer_HOF::checkAbortPlayback() { + Common::Event event; + + if (_vm->skipFlag()) { + _abortRequested = true; + _vm->resetSkipFlag(); + } + + if (_abortRequested) + return true; + + while (_system->getEventManager()->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + if (event.kbd.keycode == Common::KEYCODE_q && event.kbd.hasFlags(Common::KBD_CTRL)) { + _abortRequested = true; + _vm->quitGame(); + return true; + } else if (event.kbd.keycode != Common::KEYCODE_ESCAPE && event.kbd.keycode != Common::KEYCODE_RETURN && event.kbd.keycode != Common::KEYCODE_SPACE) { + continue; + } + // fall through + case Common::EVENT_LBUTTONDOWN: + case Common::EVENT_RBUTTONDOWN: + case Common::EVENT_LBUTTONUP: + case Common::EVENT_RBUTTONUP: + _abortRequested = true; + return true; + default: + break; + } + } + + return false; +} + +bool SeqPlayer_HOF::checkPlaybackStatus() { + _updateAnimations = false; + + if (_curScene <= _preventSkipBeforeScene || (_curScene == _loopStartScene && !_isFinale)) { + _abortRequested = false; + return false; + } + + if (_loopStartScene == kSequenceNoLooping) { + doTransition(0); + fadeOutMusic(); + _abortPlayback = true; + } + + return true; +} + +void SeqPlayer_HOF::doTransition(int type) { + for (int i = 0; i < 8; i++) + closeNestedAnimation(i); + + switch (type) { + case 0: + _screen->fadeToBlack(36); + _screen->getPalette(0).clear(); + _screen->getPalette(1).clear(); + break; + + case 1: + playSoundAndDisplaySubTitle(_vm->_rnd.getRandomBit()); + + _screen->getPalette(0).fill(0, 256, 0x3F); + _screen->fadePalette(_screen->getPalette(0), 16); + + _screen->copyPalette(1, 0); + break; + + case 3: + _screen->copyPage(2, 0); + _screen->fadePalette(_screen->getPalette(0), 16); + _screen->copyPalette(1, 0); + break; + + case 4: + _screen->copyPage(2, 0); + _screen->fadePalette(_screen->getPalette(0), 36); + _screen->copyPalette(1, 0); + break; + + case 5: + _screen->copyPage(2, 0); + break; + + case 6: + // UNUSED + // seq_loadBLD("library.bld"); + break; + + case 7: + // UNUSED + // seq_loadBLD("marco.bld"); + break; + + case 8: + _screen->fadeToBlack(16); + _screen->getPalette(0).clear(); + _screen->getPalette(1).clear(); + + delayTicks(120); + break; + + case 9: { + Palette &pal = _screen->getPalette(0); + for (int i = 0; i < 255; i++) + pal.fill(i, 1, (pal[3 * i] + pal[3 * i + 1] + pal[3 * i + 2]) / 3); + pal.fill(255, 1, 0x3F); + + _screen->fadePalette(pal, 64); + _screen->copyPalette(1, 0); + } break; + + default: + break; + } +} + +void SeqPlayer_HOF::nestedFrameAnimTransition(int srcPage, int dstPage, int delaytime, int steps, int x, int y, int w, int h, int openClose, int directionFlags) { + if (openClose) { + for (int i = 1; i < steps; i++) { + uint32 endtime = _system->getMillis() + delaytime * _vm->tickLength(); + + int w2 = (((w * 256) / steps) * i) / 256; + int h2 = (((h * 256) / steps) * i) / 256; + + int ym = (directionFlags & 2) ? (h - h2) : 0; + int xm = (directionFlags & 1) ? (w - w2) : 0; + + _screen->wsaFrameAnimationStep(0, 0, x + xm, y + ym, w, h, w2, h2, srcPage, dstPage, 0); + + _screen->copyPage(dstPage, 6); + _screen->copyPage(dstPage, 0); + _screen->updateScreen(); + + _screen->copyPage(12, dstPage); + delayUntil(endtime); + } + + _screen->wsaFrameAnimationStep(0, 0, x, y, w, h, w, h, srcPage, dstPage, 0); + _screen->copyPage(dstPage, 6); + _screen->copyPage(dstPage, 0); + _screen->updateScreen(); + } else { + _screen->copyPage(12, dstPage); + for (int i = steps; i; i--) { + uint32 endtime = _system->getMillis() + delaytime * _vm->tickLength(); + + int w2 = (((w * 256) / steps) * i) / 256; + int h2 = (((h * 256) / steps) * i) / 256; + + int ym = (directionFlags & 2) ? (h - h2) : 0; + int xm = (directionFlags & 1) ? (w - w2) : 0; + + _screen->wsaFrameAnimationStep(0, 0, x + xm, y + ym, w, h, w2, h2, srcPage, dstPage, 0); + + _screen->copyPage(dstPage, 6); + _screen->copyPage(dstPage, 0); + _screen->updateScreen(); + + _screen->copyPage(12, dstPage); + delayUntil(endtime); + } + } +} + +void SeqPlayer_HOF::nestedFrameFadeTransition(const char *cmpFile) { + _screen->copyPage(10, 2); + _screen->copyPage(4, 10); + _screen->clearPage(6); + _screen->loadBitmap(cmpFile, 6, 6, 0); + _screen->copyPage(12, 4); + + for (int i = 0; i < 3; i++) { + uint32 endtime = _system->getMillis() + 4 * _vm->tickLength(); + assert(_screenHoF); + _screenHoF->cmpFadeFrameStep(4, 320, 200, 0, 0, 2, 320, 200, 0, 0, 320, 200, 6); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + delayUntil(endtime); + } + + _screen->copyPage(4, 0); + _screen->updateScreen(); + _screen->copyPage(4, 2); + _screen->copyPage(4, 6); + _screen->copyPage(10, 4); +} + +void SeqPlayer_HOF::playAnimation(WSAMovie_v2 *wsaObj, int startFrame, int lastFrame, int frameRate, int x, int y, const SeqProc callback, Palette *fadePal1, Palette *fadePal2, int fadeRate, bool restoreScreen) { + bool finished = false; + uint32 startTime = _system->getMillis(); + + int origW = wsaObj ? wsaObj->width() : 0; + int origH = wsaObj ? wsaObj->height() : 0; + int drwX = x; + int drwY = y; + int drwW = origW; + int drwH = origH; + + _animDuration = frameRate; + + if (wsaObj) { + if (x < 0) { + drwW += x; + drwX = 0; + } + + if (y < 0) { + drwH += y; + drwY = 0; + } + + if (x + origW > 319) + origW = 320 - x; + + if (y + origH > 199) + origW = 200 - y; + } + + int8 frameStep = (startFrame > lastFrame) ? -1 : 1; + _animCurrentFrame = startFrame; + + while (!_vm->shouldQuit() && !finished) { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + + setCountDown(_animDuration); + + if (wsaObj || callback) + _screen->copyPage(12, 2); + + int frameIndex = _animCurrentFrame; + if (wsaObj) + frameIndex %= wsaObj->frames(); + + if (callback) + (this->*callback)(wsaObj, x, y, frameIndex); + + if (wsaObj) + wsaObj->displayFrame(frameIndex, 2, x, y, 0, 0, 0); + + _screen->copyPage(2, 12); + + updateAllNestedAnimations(); + updateSubTitles(); + + if ((wsaObj || callback) && (!(checkAbortPlayback() || _vm->shouldQuit() || _result))) { + _screen->copyPage(2, 0); + _screen->updateScreen(); + } + + while (!_vm->shouldQuit()) { + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + + if (fadePal1 && fadePal2) { + if (!_screen->timedPaletteFadeStep(fadePal1->getData(), fadePal2->getData(), _system->getMillis() - startTime, fadeRate * _vm->tickLength()) && !wsaObj) + break; + } + + if ((wsaObj || callback) && (!(checkAbortPlayback() || _vm->shouldQuit() || _result))) { + _screen->copyPage(2, 0); + _screen->updateScreen(); + } + + updateSubTitles(); + + if (!countDownRunning()) + break; + } + + if (wsaObj) { + _animCurrentFrame += frameStep; + if ((frameStep > 0 && _animCurrentFrame >= lastFrame) || (frameStep < 0 && _animCurrentFrame < lastFrame)) + finished = true; + } + + if (restoreScreen && (wsaObj || callback)) { + _screen->copyPage(12, 2); + _screen->copyRegion(drwX, drwY, drwX, drwY, drwW, drwH, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + } + } +} + +void SeqPlayer_HOF::playDialogueAnimation(uint16 strID, uint16 soundID, int textColor, int textPosX, int textPosY, int textWidth, WSAMovie_v2 *wsaObj, int animStartFrame, int animLastFrame, int animPosX, int animPosY) { + int dur = int(strlen(_sequenceStrings[strID])) * (_vm->gameFlags().isTalkie ? 7 : 15); + if (_vm->textEnabled()) { + int slot = displaySubTitle(strID, textPosX, textPosY, dur, textWidth); + if (slot >= 0) + _textSlots[slot].textcolor = textColor; + } + _specialAnimTimeOutTotal = _system->getMillis() + dur * _vm->tickLength(); + int curframe = animStartFrame; + + if (soundID && _vm->speechEnabled()) { + while (_vm->sound()->voiceIsPlaying() && !_abortPlayback) + delayTicks(1); + playSoundAndDisplaySubTitle(soundID); + } + + while (_system->getMillis() < _specialAnimTimeOutTotal && !_abortPlayback) { + if (animLastFrame < 0) { + int t = ABS(animLastFrame); + if (t < curframe) + curframe = t; + } + + if (ABS(animLastFrame) < curframe) + curframe = animStartFrame; + + _specialAnimFrameTimeOut = _system->getMillis() + _animDuration * _vm->tickLength(); + setCountDown(_animDuration); + + if (wsaObj) + wsaObj->displayFrame(curframe % wsaObj->frames(), 2, animPosX, animPosY, 0, 0, 0); + + _screen->copyPage(2, 12); + updateSubTitles(); + delayUntil(MIN(_specialAnimFrameTimeOut, _specialAnimTimeOutTotal)); + + if (_vm->speechEnabled() && !_vm->textEnabled() && !_vm->snd_voiceIsPlaying()) + break; + + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + break; + + _screen->copyPage(2, 0); + _screen->updateScreen(); + curframe++; + } + + if (_abortPlayback) + _vm->sound()->voiceStop(); + + if (ABS(animLastFrame) < curframe) + curframe = ABS(animLastFrame); + + if (curframe == animStartFrame) + curframe++; + + _animCurrentFrame = curframe; +} + +void SeqPlayer_HOF::startNestedAnimation(int animSlot, int sequenceID) { + if (_animSlots[animSlot].flags != -1) + return; + + if (_target == kLoLDemo) { + return; + } else if (_target == kHoFDemo) { + assert(sequenceID >= kNestedSequenceHoFDemoWharf2); + sequenceID -= kNestedSequenceHoFDemoWharf2; + } + + HoFNestedSequence s = _config->nestedSeq[sequenceID]; + + if (!_animSlots[animSlot].movie) { + _animSlots[animSlot].movie = new WSAMovie_v2(_vm); + assert(_animSlots[animSlot].movie); + } + + _animSlots[animSlot].movie->close(); + + _animSlots[animSlot].movie->open(s.wsaFile, 0, 0); + + if (!_animSlots[animSlot].movie->opened()) { + delete _animSlots[animSlot].movie; + _animSlots[animSlot].movie = 0; + return; + } + + _animSlots[animSlot].endFrame = s.endFrame; + _animSlots[animSlot].startFrame = _animSlots[animSlot].currentFrame = s.startframe; + _animSlots[animSlot].frameDelay = s.frameDelay; + _animSlots[animSlot].callback = _config->nestedSeqProc[sequenceID]; + _animSlots[animSlot].control = s.wsaControl; + + _animSlots[animSlot].flags = s.flags | 1; + _animSlots[animSlot].x = s.x; + _animSlots[animSlot].y = s.y; + _animSlots[animSlot].fadeInTransitionType = s.fadeInTransitionType; + _animSlots[animSlot].fadeOutTransitionType = s.fadeOutTransitionType; + _animSlots[animSlot].lastFrame = 0xFFFF; + + doNestedFrameTransition(s.fadeInTransitionType, animSlot); + + if (!s.fadeInTransitionType) + updateNestedAnimation(animSlot); + + _animSlots[animSlot].nextFrame = _system->getMillis() & ~(_vm->tickLength() - 1); +} + +void SeqPlayer_HOF::closeNestedAnimation(int animSlot) { + if (_animSlots[animSlot].flags == -1) + return; + + _animSlots[animSlot].flags = -1; + doNestedFrameTransition(_animSlots[animSlot].fadeOutTransitionType, animSlot); + _animSlots[animSlot].movie->close(); +} + +void SeqPlayer_HOF::unloadNestedAnimation(int animSlot) { + if (_animSlots[animSlot].movie) { + _animSlots[animSlot].movie->close(); + delete _animSlots[animSlot].movie; + _animSlots[animSlot].movie = 0; + } +} + +void SeqPlayer_HOF::doNestedFrameTransition(int transitionType, int animSlot) { + int xa = 0, ya = 0; + transitionType--; + if (!_animSlots[animSlot].movie || _abortPlayback || _vm->shouldQuit()) + return; + + switch (transitionType) { + case 0: + xa = -_animSlots[animSlot].movie->xAdd(); + ya = -_animSlots[animSlot].movie->yAdd(); + _animSlots[animSlot].movie->displayFrame(0, 8, xa, ya, 0, 0, 0); + nestedFrameAnimTransition(8, 2, 7, 8, _animSlots[animSlot].movie->xAdd(), _animSlots[animSlot].movie->yAdd(), + _animSlots[animSlot].movie->width(), _animSlots[animSlot].movie->height(), 1, 2); + break; + + case 1: + xa = -_animSlots[animSlot].movie->xAdd(); + ya = -_animSlots[animSlot].movie->yAdd(); + _animSlots[animSlot].movie->displayFrame(0, 8, xa, ya, 0, 0, 0); + nestedFrameAnimTransition(8, 2, 7, 8, _animSlots[animSlot].movie->xAdd(), _animSlots[animSlot].movie->yAdd(), + _animSlots[animSlot].movie->width(), _animSlots[animSlot].movie->height(), 1, 1); + break; + + case 2: + waitForSubTitlesTimeout(); + xa = -_animSlots[animSlot].movie->xAdd(); + ya = -_animSlots[animSlot].movie->yAdd(); + _animSlots[animSlot].movie->displayFrame(21, 8, xa, ya, 0, 0, 0); + nestedFrameAnimTransition(8, 2, 7, 8, _animSlots[animSlot].movie->xAdd(), _animSlots[animSlot].movie->yAdd(), + _animSlots[animSlot].movie->width(), _animSlots[animSlot].movie->height(), 0, 2); + break; + + case 3: + _screen->copyPage(2, 10); + _animSlots[animSlot].movie->displayFrame(0, 2, 0, 0, 0, 0, 0); + _screen->copyPage(2, 12); + nestedFrameFadeTransition("scene2.cmp"); + break; + + case 4: + _screen->copyPage(2, 10); + _animSlots[animSlot].movie->displayFrame(0, 2, 0, 0, 0, 0, 0); + _screen->copyPage(2, 12); + nestedFrameFadeTransition("scene3.cmp"); + break; + + default: + break; + } +} + +void SeqPlayer_HOF::updateAllNestedAnimations() { + for (int i = 0; i < 8; i++) { + if (_animSlots[i].flags != -1) { + if (updateNestedAnimation(i)) + closeNestedAnimation(i); + } + } +} + +bool SeqPlayer_HOF::updateNestedAnimation(int animSlot) { + uint16 currentFrame = _animSlots[animSlot].currentFrame; + uint32 curTick = _system->getMillis() & ~(_vm->tickLength() - 1); + + if (_animSlots[animSlot].callback && currentFrame != _animSlots[animSlot].lastFrame) { + _animSlots[animSlot].lastFrame = currentFrame; + currentFrame = (this->*_animSlots[animSlot].callback)(_animSlots[animSlot].movie, _animSlots[animSlot].x, _animSlots[animSlot].y, currentFrame); + } + + if (_animSlots[animSlot].movie) { + if (_animSlots[animSlot].flags & 0x20) { + _animSlots[animSlot].movie->displayFrame(_animSlots[animSlot].control[currentFrame].index, 2, _animSlots[animSlot].x, _animSlots[animSlot].y, 0x4000, 0, 0); + _animSlots[animSlot].frameDelay = _animSlots[animSlot].control[currentFrame].delay; + } else { + _animSlots[animSlot].movie->displayFrame(currentFrame % _animSlots[animSlot].movie->frames(), 2, _animSlots[animSlot].x, _animSlots[animSlot].y, 0x4000, 0, 0); + } + } + + if (_animSlots[animSlot].flags & 0x10) { + currentFrame = (curTick - _animSlots[animSlot].nextFrame) / (_animSlots[animSlot].frameDelay * _vm->tickLength()); + } else { + int diff = (curTick - _animSlots[animSlot].nextFrame) / (_animSlots[animSlot].frameDelay * _vm->tickLength()); + if (diff > 0) { + currentFrame++; + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + _animSlots[animSlot].nextFrame += ((curTick - _animSlots[animSlot].nextFrame) * 2 / 3); + else + _animSlots[animSlot].nextFrame = curTick; + } + } + + bool res = false; + + if (currentFrame >= _animSlots[animSlot].endFrame) { + int sw = ((_animSlots[animSlot].flags & 0x1E) - 2); + switch (sw) { + case 0: + res = true; + currentFrame = _animSlots[animSlot].endFrame; + _screen->copyPage(2, 12); + break; + + case 6: + case 8: + currentFrame = _animSlots[animSlot].endFrame - 1; + break; + + case 2: + case 10: + currentFrame = _animSlots[animSlot].startFrame; + break; + + default: + currentFrame = _animSlots[animSlot].endFrame - 1; + res = true; + } + } + + _animSlots[animSlot].currentFrame = currentFrame; + return res; +} + +void SeqPlayer_HOF::playSoundEffect(uint16 id, int16 vol) { + assert(id < _sequenceSoundListSize); + _vm->sound()->voicePlay(_sequenceSoundList[id], 0, vol); +} + +void SeqPlayer_HOF::playSoundAndDisplaySubTitle(uint16 id) { + assert(id < _sequenceSoundListSize); + + if (id < 12 && !_vm->gameFlags().isDemo && _vm->textEnabled()) + displaySubTitle(id, 160, 168, _textDuration[id], 160); + + _vm->sound()->voicePlay(_sequenceSoundList[id], 0); +} + +void SeqPlayer_HOF::printFadingText(uint16 strID, int x, int y, const uint8 *colorMap, uint8 textcolor) { + uint8 cmap[16]; + + if (checkAbortPlayback()) + checkPlaybackStatus(); + + if (_abortPlayback || _abortRequested || _vm->shouldQuit() || _result) + return; + + Screen::FontId of = _screen->setFont(Screen::FID_8_FNT); + _screen->getPalette(0).fill(254, 2, 63); + _screen->setPaletteIndex(252, 63, 32, 48); + cmap[0] = colorMap[0]; + cmap[1] = 253; + memcpy(&cmap[2], &colorMap[2], 14); + uint8 col0 = _textColor[0]; + + _textColor[0] = 253; + _screen->setTextColorMap(cmap); + resetAllTextSlots(); + displaySubTitle(strID, x, y, 128, 120); + updateSubTitles(); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->getPalette(0).copy(_screen->getPalette(0), textcolor, 1, 253); + _screen->fadePalette(_screen->getPalette(0), 24); + + _textColor[0] = textcolor; + _screen->setTextColorMap(colorMap); + resetAllTextSlots(); + displaySubTitle(strID, x, y, 128, 120); + updateSubTitles(); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _screen->getPalette(0).fill(253, 1, 0); + _screen->fadePalette(_screen->getPalette(0), 1); + + _screen->copyPage(2, 12); + resetAllTextSlots(); + + _textColor[0] = col0; + + _screen->setFont(of); +} + +int SeqPlayer_HOF::displaySubTitle(uint16 strIndex, uint16 posX, uint16 posY, int duration, uint16 width) { + for (int i = 0; i < 10; i++) { + if (_textSlots[i].duration != -1) { + if (i < 9) + continue; + else + return -1; + } + + _textSlots[i].strIndex = strIndex; + _textSlots[i].x = posX; + _textSlots[i].y = posY; + _textSlots[i].duration = duration * _vm->tickLength(); + _textSlots[i].width = width; + _textSlots[i].startTime = _system->getMillis(); + _textSlots[i].textcolor = -1; + + return i; + } + return -1; +} + +void SeqPlayer_HOF::updateSubTitles() { + int curPage = _screen->setCurPage(2); + char outputStr[70]; + + for (int i = 0; i < 10; i++) { + if (_textSlots[i].startTime + _textSlots[i].duration > _system->getMillis() && _textSlots[i].duration != -1) { + + char *srcStr = preprocessString(_sequenceStrings[_textSlots[i].strIndex], _textSlots[i].width); + int yPos = _textSlots[i].y; + + while (*srcStr) { + uint32 linePos = 0; + for (; *srcStr; linePos++) { + if (*srcStr == '\r') + break; + outputStr[linePos] = *srcStr; + srcStr++; + } + outputStr[linePos] = 0; + if (*srcStr == '\r') + srcStr++; + + uint8 textColor = (_textSlots[i].textcolor >= 0) ? _textSlots[i].textcolor : _textColor[0]; + _screen->printText(outputStr, _textSlots[i].x - (_screen->getTextWidth(outputStr) / 2), yPos, textColor, 0); + yPos += 10; + } + } else { + _textSlots[i].duration = -1; + } + } + + _screen->setCurPage(curPage); +} + +char *SeqPlayer_HOF::preprocessString(const char *srcStr, int width) { + char *dstStr = _tempString; + int lineStart = 0; + int linePos = 0; + + while (*srcStr) { + while (*srcStr && *srcStr != ' ') + dstStr[lineStart + linePos++] = *srcStr++; + dstStr[lineStart + linePos] = 0; + + int len = _screen->getTextWidth(&dstStr[lineStart]); + if (width >= len && *srcStr) { + dstStr[lineStart + linePos++] = *srcStr++; + } else { + dstStr[lineStart + linePos] = '\r'; + lineStart += linePos + 1; + linePos = 0; + if (*srcStr) + srcStr++; + } + } + dstStr[lineStart + linePos] = 0; + + return strlen(_tempString) ? dstStr : 0; +} + +void SeqPlayer_HOF::waitForSubTitlesTimeout() { + uint32 timeOut = _system->getMillis() + ticksTillSubTitlesTimeout() * _vm->tickLength(); + + if (_vm->textEnabled()) { + delayUntil(timeOut); + } else if (_vm->speechEnabled()) { + while (_vm->snd_voiceIsPlaying()) + delayTicks(1); + } + + resetAllTextSlots(); +} + +uint32 SeqPlayer_HOF::ticksTillSubTitlesTimeout() { + uint32 longest = 0; + + for (int i = 0; i < 10; i++) { + uint32 timeOut = (_textSlots[i].duration + _textSlots[i].startTime); + uint32 curtime = _system->getMillis(); + if (_textSlots[i].duration != -1 && timeOut > curtime) { + timeOut -= curtime; + if (longest < timeOut) + longest = timeOut; + } + } + + uint32 tl = _vm->tickLength(); + return (longest + (tl - 1)) / tl; +} + +void SeqPlayer_HOF::resetAllTextSlots() { + for (int i = 0; i < 10; i++) + _textSlots[i].duration = -1; +} + +void SeqPlayer_HOF::fadeOutMusic() { + _vm->sound()->beginFadeOut(); + delayTicks(80); +} + +void SeqPlayer_HOF::playHoFTalkieCredits() { + static const uint8 colormap[] = {0, 0, 102, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + static const ScreenDim d = { 0x00, 0x0C, 0x28, 0xB4, 0xFF, 0x00, 0x00, 0x00 }; + + _screen->loadBitmap("finale.cps", 3, 3, &_screen->getPalette(0)); + _screen->setFont(Screen::FID_GOLDFONT_FNT); + + int talkieCreditsSize, talkieCreditsSpecialSize; + const uint8 *talkieCredits = _vm->staticres()->loadRawData(k2SeqplayCredits, talkieCreditsSize); + const char *const *talkieCreditsSpecial = _vm->staticres()->loadStrings(k2SeqplayCreditsSpecial, talkieCreditsSpecialSize); + + _vm->sound()->selectAudioResourceSet(kMusicIngame); + _vm->sound()->loadSoundFile(3); + _vm->sound()->playTrack(3); + + _screen->setTextColorMap(colormap); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + _screen->fadeFromBlack(); + + _screen->_charWidth = -2; + uint8 *dataPtr = new uint8[0xAFD]; + memcpy(dataPtr, talkieCredits, talkieCreditsSize); + _vm->staticres()->unloadId(k2SeqplayCredits); + + displayHoFTalkieScrollText(dataPtr, &d, 2, 6, 5, 1, Screen::FID_GOLDFONT_FNT, Screen::FID_GOLDFONT_FNT, 0, talkieCreditsSpecial); + delayTicks(8); + + delete[] dataPtr; + _vm->staticres()->unloadId(k2SeqplayCreditsSpecial); + _vm->sound()->selectAudioResourceSet(kMusicFinale); + _vm->sound()->loadSoundFile(0); +} + +void SeqPlayer_HOF::displayHoFTalkieScrollText(uint8 *data, const ScreenDim *d, int tempPage1, int tempPage2, int speed, + int step, Screen::FontId fid1, Screen::FontId fid2, const uint8 *shapeData, const char *const *specialData) { + if (!data) + return; + + static const char mark[] = { 5, 13, 0 }; + + _screen->clearPage(tempPage1); + _screen->clearPage(tempPage2); + _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, 0, tempPage1); + + struct ScrollTextData { + int16 x; + int16 y; + uint8 *text; + byte unk1; + byte height; + byte adjust; + + ScrollTextData() { + x = 0; // 0 11 + y = 0; // 2 13 + text = 0; // 4 15 + unk1 = 0; // 8 19 + height = 0; // 9 20 + adjust = 0; // 10 21 + } + }; + + ScrollTextData *textData = new ScrollTextData[36]; + uint8 *ptr = data; + + bool loop = true; + int cnt = 0; + + while (loop) { + uint32 loopEnd = _system->getMillis() + speed * _vm->tickLength(); + + while (cnt < 35 && *ptr) { + uint16 cH; + + if (cnt) + cH = textData[cnt].y + textData[cnt].height + (textData[cnt].height >> 3); + else + cH = d->h; + + char *str = (char *)ptr; + + ptr = (uint8 *)strpbrk(str, mark); + if (!ptr) + ptr = (uint8 *)strchr(str, 0); + + textData[cnt + 1].unk1 = *ptr; + *ptr = 0; + if (textData[cnt + 1].unk1) + ptr++; + + if (*str == 3 || *str == 4) + textData[cnt + 1].adjust = *str++; + else + textData[cnt + 1].adjust = 0; + + _screen->setFont(fid1); + + if (*str == 1) { + _screen->setFont(fid2); + str++; + } else if (*str == 2) { + str++; + } + + textData[cnt + 1].height = _screen->getFontHeight(); + + switch (textData[cnt + 1].adjust) { + case 3: + textData[cnt + 1].x = 157 - _screen->getTextWidth(str); + break; + case 4: + textData[cnt + 1].x = 161; + break; + default: + textData[cnt + 1].x = (((d->w << 3) - _screen->getTextWidth(str)) >> 1) + 1; + } + + if (textData[cnt].unk1 == 5) + cH -= (textData[cnt].height + (textData[cnt].height >> 3)); + + textData[cnt + 1].y = cH; + textData[cnt + 1].text = (uint8 *)str; + cnt++; + } + + _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, tempPage1, tempPage2); + + int cnt2 = 0; + bool palCycle = 0; + + while (cnt2 < cnt) { + const char *str = (const char *)textData[cnt2 + 1].text; + const char *str2 = str; + int16 cW = textData[cnt2 + 1].x - 10; + int16 cH = textData[cnt2 + 1].y; + int x = (d->sx << 3) + cW; + int y = d->sy + cH; + int col1 = 255; + + if (cH < d->h) { + _screen->setCurPage(tempPage2); + _screen->setFont(fid1); + if (textData[cnt2 + 1].height != _screen->getFontHeight()) + _screen->setFont(fid2); + + if (specialData) { + if (!strcmp(str, specialData[0])) { + col1 = 112; + char cChar[2] = " "; + while (*str2) { + cChar[0] = *str2; + _screen->printText(cChar, x, y, col1++, 0); + x += _screen->getCharWidth((uint8)*str2++); + } + palCycle = true; + } else if (!strcmp(str, specialData[1])) { + col1 = 133; + char cChar[2] = " "; + while (*str2) { + cChar[0] = *str2; + _screen->printText(cChar, x, y, col1--, 0); + x += _screen->getCharWidth((uint8)*str2++); + } + palCycle = true; + } else { + _screen->printText(str, x, y, col1, 0); + } + } else { + _screen->printText(str, x, y, col1, 0); + } + _screen->setCurPage(0); + } + + textData[cnt2 + 1].y -= step; + cnt2++; + } + + _screen->copyRegion(d->sx << 3, d->sy, d->sx << 3, d->sy, d->w << 3, d->h, tempPage2, 0); + _screen->updateScreen(); + + if (textData[1].y < -10) { + textData[1].text += strlen((char *)textData[1].text); + textData[1].text[0] = textData[1].unk1; + cnt--; + memmove(&textData[1], &textData[2], cnt * sizeof(ScrollTextData)); + } + + if (palCycle) { + for (int col = 133; col > 112; col--) + _screen->getPalette(0).copy(_screen->getPalette(0), col - 1, 1, col); + _screen->getPalette(0).copy(_screen->getPalette(0), 133, 1, 112); + _screen->setScreenPalette(_screen->getPalette(0)); + } + + delayUntil(loopEnd); + + if ((cnt < 36) && ((d->sy + d->h) > (textData[cnt].y + textData[cnt].height)) && !_abortPlayback) { + delayTicks(500); + cnt = 0; + } + + if (checkAbortPlayback()) + if (checkPlaybackStatus()) + loop = false; + + if (!cnt || _abortPlayback) + loop = false; + } + + _vm->sound()->beginFadeOut(); + _screen->fadeToBlack(); + + _abortPlayback = _abortRequested = false; + + delete[] textData; +} + +void SeqPlayer_HOF::updateDemoAdText(int bottom, int top) { + int dstY, dstH, srcH; + + static const ScreenDim d = { 0x00, 0x00, 0x28, 0x320, 0xFF, 0xFE, 0x00, 0x00 }; + + if (_scrollProgressCounter - (top - 1) < 0) { + dstY = top - _scrollProgressCounter; + dstH = _scrollProgressCounter; + srcH = 0; + } else { + dstY = 0; + srcH = _scrollProgressCounter - top; + dstH = (400 - srcH <= top) ? 400 - srcH : top; + } + + if (dstH > 0) { + if (_hofDemoAnimData) { + for (int i = 0; i < 4; i++) { + const HoFSeqItemAnimData *def = &_hofDemoAnimData[i]; + ActiveItemAnim *a = &_hofDemoActiveItemAnim[i]; + + _screen->fillRect(12, def->y - 8, 28, def->y + 8, 0, 4); + _screen->drawShape(4, _hofDemoItemShapes[def->itemIndex + def->frames[a->currentFrame]], 12, def->y - 8, 0, 0); + if (_callbackCurrentFrame % 2 == 0) + a->currentFrame = (a->currentFrame + 1) % 20; + } + } + _screen->copyRegionEx(4, 0, srcH, 2, 2, dstY + bottom, 320, dstH, &d); + } +} + +void SeqPlayer_HOF::delayTicks(uint32 ticks) { + uint32 len = ticks * _vm->tickLength(); + while (len && !_vm->shouldQuit() && !checkAbortPlayback()) { + uint32 step = (len >= 10) ? 10 : len; + _system->delayMillis(step); + len -= step; + } +} + +void SeqPlayer_HOF::delayUntil(uint32 dest) { + for (uint32 ct = _system->getMillis(); ct < dest && !_vm->shouldQuit() && !checkAbortPlayback(); ) { + uint32 step = (dest - ct >= 10) ? 10 : (dest - ct); + _system->delayMillis(step); + ct = _system->getMillis(); + } +} + +void SeqPlayer_HOF::setCountDown(uint32 ticks) { + _countDownRemainder = ticks * _vm->tickLength(); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + _countDownRemainder = _countDownRemainder * 2 / 3; + _countDownLastUpdate = _system->getMillis() & ~(_vm->tickLength() - 1); +} + +bool SeqPlayer_HOF::countDownRunning() { + uint32 cur = _system->getMillis(); + uint32 step = cur - _countDownLastUpdate; + _countDownLastUpdate = cur; + _countDownRemainder = (step <= _countDownRemainder) ? _countDownRemainder - step : 0; + return _countDownRemainder; +} + +#define CASE_ALT(dosCase, towns98Case)\ + case dosCase:\ + case towns98Case:\ + if (!((_callbackCurrentFrame == towns98Case && (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98)) || (_callbackCurrentFrame == dosCase && _vm->gameFlags().platform == Common::kPlatformDOS)))\ + break; + +int SeqPlayer_HOF::cbHOF_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == -2) { + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + delayTicks(300); + } else if (!frm) { + _vm->sound()->playTrack(2); + } + + return 0; +} + +int SeqPlayer_HOF::cbHOF_title(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 1) { + _vm->sound()->playTrack(3); + } else if (frm == 25 && _startupSaveLoadable) { + int cp = _screen->setCurPage(0); + _screen->showMouse(); + _system->updateScreen(); + _result = _menu->handle(11) + 1; + _updateAnimations = false; + + if (_result == 1 || _result == 3) { + _curScene = _lastScene; + _preventLooping = true; + } + + if (_result == 2) { + _result = 0; + } else if (_result == 4) { + setCountDown(200); + _vm->quitGame(); + } + + _screen->hideMouse(); + _screen->setCurPage(cp); + } else if (frm == 25) { + setCountDown(200); + } + + return 0; +} + +int SeqPlayer_HOF::cbHOF_overview(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint8 *tmpPal = _screen->getPalette(3).getData() + 0x101; + memset(tmpPal, 0, 256); + uint32 frameEnd = 0; + + switch (_callbackCurrentFrame) { + case 0: + _updateAnimations = true; + fadeOutMusic(); + _vm->sound()->playTrack(4); + frameEnd = _system->getMillis() + 60 * _vm->tickLength(); + + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + _screen->setTextColorMap(_textColorMap); + + delayUntil(frameEnd); + break; + + case 1: + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x40, 0, 0, 0, 0x100, true); + for (int i = 0; i < 256; i++) + tmpPal[_screen->getPalette(3)[i]] = 1; + + for (int i = 0; i < 256; i++) { + int v = (tmpPal[i] == 1) ? i : _screen->getPalette(3)[i]; + v *= 3; + _screen->getPalette(2)[3 * i] = _screen->getPalette(0)[v]; + _screen->getPalette(2)[3 * i + 1] = _screen->getPalette(0)[v + 1]; + _screen->getPalette(2)[3 * i + 2] = _screen->getPalette(0)[v + 2]; + } + break; + + case 40: + startNestedAnimation(0, kNestedSequenceOver1); + break; + + case 60: + startNestedAnimation(1, kNestedSequenceOver2); + break; + + case 120: + playSoundAndDisplaySubTitle(0); + break; + + case 200: + waitForSubTitlesTimeout(); + _screen->fadePalette(_screen->getPalette(2), 64); + break; + + case 201: + _screen->setScreenPalette(_screen->getPalette(2)); + _screen->updateScreen(); + _screen->applyOverlay(0, 0, 320, 200, 2, _screen->getPalette(3).getData()); + _screen->copyPage(2, 12); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->updateScreen(); + closeNestedAnimation(0); + closeNestedAnimation(1); + break; + + case 282: + startNestedAnimation(0, kNestedSequenceForest); + playSoundAndDisplaySubTitle(1); + break; + + CASE_ALT(434, 354) + closeNestedAnimation(0); + startNestedAnimation(0, kNestedSequenceDragon); + break; + + CASE_ALT(540, 400) + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + setCountDown(0); + _updateAnimations = false; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_library(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_callbackCurrentFrame) { + case 0: + _updateAnimations = true; + _vm->sound()->playTrack(5); + + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + + _screen->setTextColorMap(_textColorMap); + break; + + case 1: + startNestedAnimation(0, kNestedSequenceLibrary3); + playSoundAndDisplaySubTitle(4); + break; + + case 100: + waitForSubTitlesTimeout(); + + _screen->copyPage(12, 2); + _screen->applyOverlay(0, 0, 320, 200, 2, _screen->getPalette(3).getData()); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + _screen->copyPage(2, 12); + + closeNestedAnimation(0); + startNestedAnimation(0, kNestedSequenceDarm); + break; + + case 104: + playSoundAndDisplaySubTitle(5); + break; + + case 240: + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + startNestedAnimation(0, kNestedSequenceLibrary2); + break; + + case 340: + closeNestedAnimation(0); + _screen->applyOverlay(0, 0, 320, 200, 2, _screen->getPalette(3).getData()); + _screen->copyPage(2, 12); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + + startNestedAnimation(0, kNestedSequenceMarco); + playSoundAndDisplaySubTitle(6); + break; + + CASE_ALT(660, 480) + _screen->copyPage(2, 12); + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + setCountDown(0); + _updateAnimations = false; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_hand(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_callbackCurrentFrame) { + case 0: + _updateAnimations = true; + _vm->sound()->playTrack(6); + + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + + _screen->setTextColorMap(_textColorMap); + break; + + case 1: + startNestedAnimation(0, kNestedSequenceHand1a); + startNestedAnimation(1, kNestedSequenceHand1b); + startNestedAnimation(2, kNestedSequenceHand1c); + playSoundAndDisplaySubTitle(7); + break; + + case 201: + waitForSubTitlesTimeout(); + _screen->applyOverlay(0, 0, 320, 200, 2, _screen->getPalette(3).getData()); + _screen->copyPage(2, 12); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + closeNestedAnimation(0); + closeNestedAnimation(1); + closeNestedAnimation(2); + startNestedAnimation(0, kNestedSequenceHand2); + playSoundAndDisplaySubTitle(8); + break; + + CASE_ALT(395, 260) + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + startNestedAnimation(1, kNestedSequenceHand3); + playSoundAndDisplaySubTitle(9); + break; + + CASE_ALT(500, 365) + waitForSubTitlesTimeout(); + closeNestedAnimation(1); + startNestedAnimation(0, kNestedSequenceHand4); + break; + + CASE_ALT(540, 405) + playSoundAndDisplaySubTitle(10); + break; + + CASE_ALT(630, 484) + waitForSubTitlesTimeout(); + closeNestedAnimation(0); + setCountDown(0); + _updateAnimations = false; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_point(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == -2) { + waitForSubTitlesTimeout(); + setCountDown(0); + } + + switch (_callbackCurrentFrame) { + case -2: + waitForSubTitlesTimeout(); + break; + + case 0: + _vm->sound()->playTrack(7); + + _textColor[1] = 0xF7; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + _screen->setTextColorMap(_textColorMap); + assert(_screenHoF); + _screenHoF->generateGrayOverlay(_screen->getPalette(0), _screen->getPalette(3).getData(), 0x24, 0, 0, 0, 0x100, false); + break; + + case 1: + playSoundAndDisplaySubTitle(11); + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_zanfaun(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == -2) { + waitForSubTitlesTimeout(); + setCountDown(0); + return 0; + } + + switch (_callbackCurrentFrame) { + case 0: + _vm->sound()->playTrack(8); + + _textColor[1] = 0xFD; + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = _screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 255) & 0xFF; + _screen->setTextColorMap(_textColorMap); + break; + + case 1: + if (_vm->gameFlags().isTalkie) { + playDialogueAnimation(21, 13, -1, 140, 70, 160, wsaObj, 0, 8, x, y); + } else { + displaySubTitle(21, 140, 70, 200, 160); + _animDuration = 200; + } + break; + + case 2: + case 11: + case 21: + if (!_vm->gameFlags().isTalkie) + _animDuration = 12; + break; + + case 9: + if (_vm->gameFlags().isTalkie) + playDialogueAnimation(13, 14, -1, 140, (_vm->gameFlags().lang == Common::FR_FRA + || _vm->gameFlags().lang == Common::DE_DEU) ? 50 : 70, 160, wsaObj, 9, 15, x, y); + break; + + case 10: + if (!_vm->gameFlags().isTalkie) { + waitForSubTitlesTimeout(); + displaySubTitle(13, 140, 50, _textDuration[13], 160); + _animDuration = 300; + } + break; + + case 16: + if (_vm->gameFlags().isTalkie) + playDialogueAnimation(18, 15, -1, 140, (_vm->gameFlags().lang == Common::FR_FRA) ? 50 : + (_vm->gameFlags().lang == Common::DE_DEU ? 40 : 70), 160, wsaObj, 10, 16, x, y); + break; + + case 17: + if (_vm->gameFlags().isTalkie) + _animDuration = 12; + break; + + case 20: + if (!_vm->gameFlags().isTalkie) { + waitForSubTitlesTimeout(); + displaySubTitle(18, 160, 50, _textDuration[18], 160); + _animDuration = 200; + } + break; + + case 26: + waitForSubTitlesTimeout(); + break; + + case 46: + if (_vm->gameFlags().isTalkie) { + playDialogueAnimation(16, 16, -1, 200, 50, 120, wsaObj, 46, 46, x, y); + } else { + waitForSubTitlesTimeout(); + displaySubTitle(16, 200, 50, _textDuration[16], 120); + } + + setCountDown(120); + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_over1(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 2) + waitForSubTitlesTimeout(); + else if (frm == 3) + playSoundAndDisplaySubTitle(12); + return frm; +} + +int SeqPlayer_HOF::cbHOF_over2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 1) + playSoundAndDisplaySubTitle(12); + return frm; +} + +int SeqPlayer_HOF::cbHOF_forest(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 11) + waitForSubTitlesTimeout(); + else if (frm == 12) + playSoundAndDisplaySubTitle(2); + + return frm; +} + +int SeqPlayer_HOF::cbHOF_dragon(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 11) + waitForSubTitlesTimeout(); + else if (frm == 3) + playSoundAndDisplaySubTitle(3); + return frm; +} + +int SeqPlayer_HOF::cbHOF_darm(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + return frm; +} + +int SeqPlayer_HOF::cbHOF_library2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + return frm; +} + +int SeqPlayer_HOF::cbHOF_marco(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 36) { + waitForSubTitlesTimeout(); + setCountDown(0); + } + return frm; +} + +int SeqPlayer_HOF::cbHOF_hand1a(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + return frm; +} + +int SeqPlayer_HOF::cbHOF_hand1b(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 15) + frm = 12; + return frm; +} + +int SeqPlayer_HOF::cbHOF_hand1c(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 8) + frm = 4; + return frm; +} + +int SeqPlayer_HOF::cbHOF_hand2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + return frm; +} + +int SeqPlayer_HOF::cbHOF_hand3(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + return frm; +} + +int SeqPlayer_HOF::cbHOF_funters(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; + uint16 voiceIndex = 0; + + switch (frm) { + case -2: + doTransition(9); + break; + + case 0: + _vm->sound()->playTrack(3); + + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); + + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(81, 240, 70, _textColorMap, 252); + printFadingText(82, 240, 90, _textColorMap, _textColor[0]); + _screen->copyPage(2, 12); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 28 : 24); + delayUntil(frameEnd); + _textColor[0] = 1; + + if (_vm->gameFlags().isTalkie) { + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA) ? 70 : 78; + subTitleFirstFrame = 9; + subTitleLastFrame = 15; + voiceIndex = 34; + } else { + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA) ? 78 : 70; + subTitleFirstFrame = 0; + subTitleLastFrame = 8; + } + subTitleX = (_vm->gameFlags().lang == Common::FR_FRA) ? 84 : 88; + subTitleW = 100; + + playDialogueAnimation(22, voiceIndex, 187, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + break; + + case 9: + case 16: + if (!((frm == 9 && !_vm->gameFlags().isTalkie) || (frm == 16 && _vm->gameFlags().isTalkie))) + break; + + _animDuration = 12; + + if (_vm->gameFlags().lang == Common::FR_FRA) { + subTitleX = 80; + subTitleW = 112; + } else { + subTitleX = (_vm->gameFlags().lang == Common::DE_DEU) ? 84 : 96; + subTitleW = 100; + } + + if (_vm->gameFlags().isTalkie) { + subTitleFirstFrame = 0; + subTitleLastFrame = 8; + voiceIndex = 35; + } else { + subTitleFirstFrame = 9; + subTitleLastFrame = 15; + } + subTitleY = 70; + + playDialogueAnimation(23, voiceIndex, 137, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 17; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_ferb(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; + uint16 voiceIndex = 0; + + switch (frm) { + case -2: + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(34, 240, _vm->gameFlags().isTalkie ? 60 : 40, _textColorMap, 252); + printFadingText(35, 240, _vm->gameFlags().isTalkie ? 70 : 50, _textColorMap, _textColor[0]); + printFadingText(36, 240, _vm->gameFlags().isTalkie ? 90 : 70, _textColorMap, 252); + printFadingText(37, 240, _vm->gameFlags().isTalkie ? 100 : 90, _textColorMap, _textColor[0]); + printFadingText(38, 240, _vm->gameFlags().isTalkie ? 120 : 110, _textColorMap, 252); + printFadingText(39, 240, _vm->gameFlags().isTalkie ? 130 : 120, _textColorMap, _textColor[0]); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + printFadingText(103, 240, 130, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); + break; + + case 0: + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 255; + _screen->setTextColorMap(_textColorMap); + break; + + case 5: + if (!_vm->gameFlags().isTalkie) + playSoundAndDisplaySubTitle(18); + _animDuration = 16; + + if (_vm->gameFlags().isTalkie) { + subTitleFirstFrame = 5; + subTitleLastFrame = 8; + voiceIndex = 22; + } else { + subTitleLastFrame = 14; + } + subTitleX = 116; + subTitleY = 90; + subTitleW = 60; + + playDialogueAnimation(24, voiceIndex, 149, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + break; + + case 11: + if (_vm->gameFlags().isTalkie) + playDialogueAnimation(24, 22, 149, 116, 90, 60, wsaObj, 11, 14, x, y); + break; + + case 16: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 23 : 19); + _animDuration = _vm->gameFlags().isTalkie ? 20 : 16; + + if (_vm->gameFlags().lang == Common::FR_FRA) { + subTitleY = 48; + subTitleW = 88; + } else { + subTitleY = 60; + subTitleW = 100; + } + subTitleX = 60; + + if (_vm->gameFlags().isTalkie) + voiceIndex = 36; + + playDialogueAnimation(25, voiceIndex, 143, subTitleX, subTitleY, subTitleW, wsaObj, 16, 25, x, y); + _animDuration = 16; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_fish(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + uint16 voiceIndex = 0; + + switch (frm) { + case -2: + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + + printFadingText(40, 240, _vm->gameFlags().isTalkie ? 55 : 40, _textColorMap, 252); + printFadingText(41, 240, _vm->gameFlags().isTalkie ? 65 : 50, _textColorMap, _textColor[0]); + printFadingText(42, 240, _vm->gameFlags().isTalkie ? 75 : 60, _textColorMap, _textColor[0]); + printFadingText(43, 240, _vm->gameFlags().isTalkie ? 95 : 80, _textColorMap, 252); + printFadingText(44, 240, _vm->gameFlags().isTalkie ? 105 : 90, _textColorMap, _textColor[0]); + printFadingText(93, 240, _vm->gameFlags().isTalkie ? 125 : 110, _textColorMap, 252); + printFadingText(94, 240, _vm->gameFlags().isTalkie ? 135 : 120, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); + break; + + case 0: + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); + break; + + case 4: + subTitleX = 94; + subTitleY = 42; + subTitleW = 100; + if (_vm->gameFlags().isTalkie) + voiceIndex = 37; + playDialogueAnimation(26, voiceIndex, 149, subTitleX, subTitleY, subTitleW, wsaObj, 3, 12, x, y); + break; + + case 14: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 19 : 15); + break; + + case 23: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 20 : 16); + break; + + case 29: + subTitleX = (_vm->gameFlags().lang == Common::DE_DEU) ? 82 : ((_vm->gameFlags().lang == Common::FR_FRA) ? 92 : 88); + subTitleY = 40; + subTitleW = 100; + + if (_vm->gameFlags().isTalkie) { + if (_vm->gameFlags().lang == Common::DE_DEU) + subTitleY = 35; + voiceIndex = 38; + } + + playDialogueAnimation(27, voiceIndex, 187, subTitleX, subTitleY, subTitleW, wsaObj, 28, 34, x, y); + break; + + case 45: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 21 : 17); + break; + + case 50: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 29 : 25); + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_fheep(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; + uint16 voiceIndex = 0; + + switch (frm) { + case -2: + _screen->copyPage(12, 2); + _screen->copyPage(2, 0); + _screen->updateScreen(); + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(49, 240, 20, _textColorMap, 252); + printFadingText(50, 240, 30, _textColorMap, _textColor[0]); + printFadingText(51, 240, 40, _textColorMap, _textColor[0]); + printFadingText(52, 240, 50, _textColorMap, _textColor[0]); + printFadingText(53, 240, 60, _textColorMap, _textColor[0]); + printFadingText(54, 240, 70, _textColorMap, _textColor[0]); + printFadingText(55, 240, 80, _textColorMap, _textColor[0]); + printFadingText(56, 240, 90, _textColorMap, _textColor[0]); + printFadingText(57, 240, 100, _textColorMap, _textColor[0]); + printFadingText(58, 240, 110, _textColorMap, _textColor[0]); + printFadingText(60, 240, 120, _textColorMap, _textColor[0]); + printFadingText(61, 240, 130, _textColorMap, _textColor[0]); + printFadingText(62, 240, 140, _textColorMap, _textColor[0]); + printFadingText(63, 240, 150, _textColorMap, _textColor[0]); + printFadingText(64, 240, 160, _textColorMap, _textColor[0]); + + delayUntil(frameEnd); + setCountDown(0); + break; + + case 0: + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); + break; + + case 2: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 25 : 21); + + if (_vm->gameFlags().lang == Common::FR_FRA) { + subTitleX = 92; + subTitleY = 72; + } else { + subTitleX = (_vm->gameFlags().lang == Common::DE_DEU) ? 90 : 98; + subTitleY = 84; + } + + if (_vm->gameFlags().isTalkie) { + subTitleFirstFrame = 8; + subTitleLastFrame = 9; + voiceIndex = 39; + } else { + subTitleFirstFrame = 2; + subTitleLastFrame = -8; + } + subTitleW = 100; + + playDialogueAnimation(28, voiceIndex, -1, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 4; + break; + + case 9: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 24 : 20); + _animDuration = 100; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_farmer(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + uint16 voiceIndex = 0; + + switch (frm) { + case -2: + _screen->copyPage(12, 2); + _screen->copyPage(2, 0); + _screen->updateScreen(); + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(45, 240, 40, _textColorMap, 252); + printFadingText(46, 240, 50, _textColorMap, _textColor[0]); + printFadingText(47, 240, 60, _textColorMap, _textColor[0]); + printFadingText(83, 240, 80, _textColorMap, 252); + printFadingText(48, 240, 90, _textColorMap, _textColor[0]); + printFadingText(65, 240, 110, _textColorMap, 252); + printFadingText(66, 240, 120, _textColorMap, _textColor[0]); + printFadingText(67, 240, 130, _textColorMap, _textColor[0]); + printFadingText(68, 240, 140, _textColorMap, _textColor[0]); + printFadingText(69, 240, 150, _textColorMap, _textColor[0]); + if (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98) + printFadingText(104, 240, 160, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); + break; + + case 0: + _textColor[1] = 1 + (_screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 254) & 0xFF); + memset(_textColorMap, _textColor[1], 16); + _textColorMap[1] = _textColor[0] = 1 + (_screen->findLeastDifferentColor(_textColorPresets + 3, _screen->getPalette(0), 1, 254) & 0xFF); + _screen->setTextColorMap(_textColorMap); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 30 : 26); + break; + + case 6: + if (_vm->gameFlags().isTalkie) + playSoundAndDisplaySubTitle(18); + break; + + case 12: + if (!_vm->gameFlags().isTalkie) + playSoundAndDisplaySubTitle(14); + + subTitleX = 90; + subTitleY = 30; + subTitleW = 100; + + if (_vm->gameFlags().isTalkie) { + if (_vm->gameFlags().lang == Common::FR_FRA || _vm->gameFlags().lang == Common::DE_DEU) { + subTitleX = 75; + subTitleY = 25; + } + voiceIndex = 40; + } + + playDialogueAnimation(29, voiceIndex, 150, subTitleX, subTitleY, subTitleW, wsaObj, 12, -21, x, y); + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_fuards(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + int subTitleFirstFrame = 0; + int subTitleLastFrame = 0; + + uint16 voiceIndex = 0; + + switch (frm) { + case -2: + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(70, 240, 20, _textColorMap, 252); + printFadingText(71, 240, 30, _textColorMap, _textColor[0]); + printFadingText(72, 240, 40, _textColorMap, _textColor[0]); + printFadingText(73, 240, 50, _textColorMap, _textColor[0]); + printFadingText(74, 240, 60, _textColorMap, _textColor[0]); + printFadingText(75, 240, 70, _textColorMap, _textColor[0]); + printFadingText(101, 240, 80, _textColorMap, _textColor[0]); + printFadingText(102, 240, 90, _textColorMap, _textColor[0]); + printFadingText(87, 240, 100, _textColorMap, _textColor[0]); + printFadingText(88, 240, 110, _textColorMap, _textColor[0]); + printFadingText(89, 240, 120, _textColorMap, _textColor[0]); + printFadingText(90, 240, 130, _textColorMap, _textColor[0]); + printFadingText(91, 240, 140, _textColorMap, _textColor[0]); + printFadingText(92, 240, 150, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); + break; + + case 0: + for (int i = 0; i < 0x300; i++) + _screen->getPalette(0)[i] &= 0x3F; + _textColor[1] = 0xCf; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFE; + + _screen->setTextColorMap(_textColorMap); + break; + + case 6: + _animDuration = 20; + + if (_vm->gameFlags().isTalkie) { + subTitleX = 82; + subTitleFirstFrame = 16; + subTitleLastFrame = 21; + voiceIndex = 41; + } else { + subTitleX = 62; + subTitleFirstFrame = 9; + subTitleLastFrame = 13; + } + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA || _vm->gameFlags().lang == Common::DE_DEU) ? 88 :100; + subTitleW = 80; + + playDialogueAnimation(30, voiceIndex, 137, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 8; + break; + + case 9: + case 16: + if (_vm->gameFlags().isTalkie) { + if (frm == 16) + break; + subTitleX = 64; + subTitleFirstFrame = 9; + subTitleLastFrame = 13; + voiceIndex = 42; + } else { + if (frm == 9) + break; + subTitleX = 80; + subTitleFirstFrame = 16; + subTitleLastFrame = 21; + } + subTitleY = 100; + subTitleW = 100; + + playDialogueAnimation(31, voiceIndex, 143, subTitleX, subTitleY, subTitleW, wsaObj, subTitleFirstFrame, subTitleLastFrame, x, y); + if (_vm->gameFlags().isTalkie) + _animCurrentFrame = 21; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_firates(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + uint32 frameEnd = 0; + int subTitleX = 0; + int subTitleY = 0; + int subTitleW = 0; + uint16 voiceIndex = 0; + + switch (frm) { + case -2: + _screen->copyPage(12, 2); + _screen->copyPage(2, 0); + _screen->updateScreen(); + doTransition(9); + frameEnd = _system->getMillis() + 480 * _vm->tickLength(); + printFadingText(76, 240, 40, _textColorMap, 252); + printFadingText(77, 240, 50, _textColorMap, 252); + printFadingText(78, 240, 60, _textColorMap, _textColor[0]); + printFadingText(79, 240, 70, _textColorMap, _textColor[0]); + printFadingText(80, 240, 80, _textColorMap, _textColor[0]); + printFadingText(84, 240, 100, _textColorMap, 252); + printFadingText(85, 240, 110, _textColorMap, _textColor[0]); + printFadingText(99, 240, 130, _textColorMap, 252); + printFadingText(100, 240, 140, _textColorMap, _textColor[0]); + delayUntil(frameEnd); + setCountDown(0); + break; + + case 0: + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); + break; + + case 6: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 31 : 27); + break; + + case 14: + case 15: + if (!((frm == 15 && !_vm->gameFlags().isTalkie) || (frm == 14 && _vm->gameFlags().isTalkie))) + break; + + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 31 : 27); + + if (_vm->gameFlags().lang == Common::DE_DEU) { + subTitleX = 82; + subTitleY = 84; + subTitleW = 140; + } else { + subTitleX = 74; + subTitleY = (_vm->gameFlags().lang == Common::FR_FRA) ? 96: 108; + subTitleW = 80; + } + + if (_vm->gameFlags().isTalkie) + voiceIndex = 43; + + playDialogueAnimation(32, voiceIndex, 137, subTitleX, subTitleY, subTitleW, wsaObj, 14, 16, x, y); + break; + + case 28: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 32 : 28); + break; + + case 29: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 33 : 29); + break; + + case 31: + if (_vm->gameFlags().isTalkie) + voiceIndex = 44; + + subTitleX = 90; + subTitleY = (_vm->gameFlags().lang == Common::DE_DEU) ? 60 : 76; + subTitleW = 80; + + playDialogueAnimation(33, voiceIndex, 143, subTitleX, subTitleY, subTitleW, wsaObj, 31, 34, x, y); + break; + + case 35: + _animDuration = 300; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_frash(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + int tmp = 0; + + switch (frm) { + case -2: + _screen->setCurPage(2); + _screen->clearCurPage(); + _screen->copyPage(2, 12); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _callbackCurrentFrame = 0; + startNestedAnimation(0, kNestedSequenceFiggle); + break; + + case -1: + if (_vm->gameFlags().isTalkie) + playHoFTalkieCredits(); + _talkieFinaleExtraFlag = _vm->gameFlags().isTalkie; + break; + + case 0: + if (_callbackCurrentFrame == 1) { + _vm->sound()->playTrack(4); + _textColor[1] = _screen->findLeastDifferentColor(_textColorPresets, _screen->getPalette(0), 1, 255) & 0xFF; + memset(_textColorMap, _textColor[1], 16); + _textColor[0] = _textColorMap[1] = 0xFF; + _screen->setTextColorMap(_textColorMap); + } + _animDuration = 10; + break; + + case 1: + if (_callbackCurrentFrame < 20 && _talkieFinaleExtraFlag) { + _animCurrentFrame = 0; + } else { + _animDuration = _vm->gameFlags().isTalkie ? 500 : (300 + _vm->_rnd.getRandomNumberRng(1, 300)); + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 26 : 22); + if (_talkieFinaleExtraFlag) { + _callbackCurrentFrame = 3; + _talkieFinaleExtraFlag = false; + } + } + break; + + case 2: + _animDuration = 20; + break; + + case 3: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 27 : 23); + _animDuration = _vm->gameFlags().isTalkie ? 500 : (300 + _vm->_rnd.getRandomNumberRng(1, 300)); + break; + + case 4: + _animDuration = 10; + break; + + case 5: + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 27 : 23); + tmp = _callbackCurrentFrame / 6; + if (tmp == 2) + _animDuration = _vm->gameFlags().isTalkie ? 7 : (1 + _vm->_rnd.getRandomNumberRng(1, 10)); + else if (tmp < 2) + _animDuration = _vm->gameFlags().isTalkie ? 500 : (300 + _vm->_rnd.getRandomNumberRng(1, 300)); + break; + + case 6: + _animDuration = 10; + tmp = _callbackCurrentFrame / 6; + if (tmp == 2) + _animCurrentFrame = 4; + else if (tmp < 2) + _animCurrentFrame = 0; + break; + + case 7: + _callbackCurrentFrame = 0; + _animDuration = 5; + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 26 : 22); + break; + + case 11: + if (_callbackCurrentFrame < 8) + _animCurrentFrame = 8; + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOF_figgle(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (_callbackCurrentFrame == 10) + setCountDown(0); + if (_callbackCurrentFrame == 10 || _callbackCurrentFrame == 5 || _callbackCurrentFrame == 7) + playSoundAndDisplaySubTitle(_vm->gameFlags().isTalkie ? 45 : 30); + + _callbackCurrentFrame++; + return frm; +} + +int SeqPlayer_HOF::cbHOFDEMO_virgin(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (!frm) + delayTicks(50); + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_westwood(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (!frm) + _vm->sound()->playTrack(2); + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_title(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (!frm) { + _vm->sound()->playTrack(3); + } else if (frm == 25) { + delayTicks(60); + setCountDown(0); + doTransition(0); + } + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_hill(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (!frm) { + _vm->sound()->playTrack(4); + } else if (frm == 25) { + startNestedAnimation(0, kNestedSequenceHoFDemoWater); + _animDuration--; + } else if (frm > 25 && frm < 50) { + if (_animDuration > 3) + _animDuration--; + } else if (frm == 95) { + _animDuration = 70; + } else if (frm == 96) { + _animDuration = 7; + } else if (frm == 129) { + closeNestedAnimation(0); + } + + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_outhome(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (frm) { + case 12: + playSoundAndDisplaySubTitle(4); + break; + + case 32: + playSoundAndDisplaySubTitle(7); + break; + + case 36: + playSoundAndDisplaySubTitle(10); + break; + + case 57: + playSoundAndDisplaySubTitle(9); + break; + + case 80: + case 96: + case 149: + _animDuration = 70; + break; + + case 81: + case 97: + _animDuration = 5; + break; + + case 110: + playSoundAndDisplaySubTitle(5); + break; + + case 137: + playSoundAndDisplaySubTitle(6); + break; + } + + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_wharf(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (!_callbackCurrentFrame) + startNestedAnimation(0, kNestedSequenceHoFDemoWharf2); + + switch (frm) { + case 0: + playSoundAndDisplaySubTitle(11); + break; + + case 5: + if ((_callbackCurrentFrame / 8) <= 2 || _animSlots[0].flags != -1) + _animCurrentFrame = 0; + else + closeNestedAnimation(0); + break; + + case 6: + closeNestedAnimation(0); + break; + + case 8: + case 10: + playSoundAndDisplaySubTitle(2); + break; + + case 13: + playSoundAndDisplaySubTitle(7); + break; + + case 16: + playSoundAndDisplaySubTitle(12); + break; + + default: + break; + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_dinob(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 0) { + if (!(_callbackCurrentFrame/8)) { + startNestedAnimation(0, kNestedSequenceHoFDemoDinob2); + _animCurrentFrame = 0; + } + } else if (frm == 3) { + if (_animSlots[0].flags != -1) { + _animCurrentFrame = 0; + } else { + closeNestedAnimation(0); + _screen->copyPage(2, 12); + } + } else if (frm == 4) { + closeNestedAnimation(0); + } + + _callbackCurrentFrame++; + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_fisher(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (((_system->getMillis() - _fisherAnimCurTime) / (5 * _vm->tickLength())) > 0) { + _fisherAnimCurTime = _system->getMillis(); + if (!_callbackCurrentFrame) { + startNestedAnimation(0, kNestedSequenceHoFDemoBail); + startNestedAnimation(1, kNestedSequenceHoFDemoDig); + } + + if (_scrollProgressCounter >= 0x18F && !_callbackCurrentFrame) + return 0; + + if (!_callbackCurrentFrame) { + _screen->loadBitmap("adtext.cps", 4, 4, 0); + _screen->loadBitmap("adtext2.cps", 6, 6, 0); + _screen->copyPageMemory(6, 0, 4, 64000, 1024); + _screen->copyPageMemory(6, 1023, 6, 0, 64000); + _scrollProgressCounter = 0; + } + + updateDemoAdText(24, 144); + _callbackCurrentFrame++; + if (_callbackCurrentFrame < 0x256 || _callbackCurrentFrame > 0x31C) { + if (_callbackCurrentFrame < 0x174 || _callbackCurrentFrame > 0x1D7) { + if (_callbackCurrentFrame < 0x84 || _callbackCurrentFrame > 0xE7) { + _scrollProgressCounter++; + } + } + } + + if (_callbackCurrentFrame > 0x31E) { + closeNestedAnimation(0); + closeNestedAnimation(1); + setCountDown(0); + _screen->copyPage(2, 12); + } + + } else { + updateDemoAdText(24, 144); + } + return 0; +} + +int SeqPlayer_HOF::cbHOFDEMO_wharf2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 69) + _animCurrentFrame = 8; + + return frm; +} + +int SeqPlayer_HOF::cbHOFDEMO_dinob2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (frm) { + case 19: + playSoundAndDisplaySubTitle(13); + break; + + case 54: + playSoundAndDisplaySubTitle(15); + break; + + case 61: + playSoundAndDisplaySubTitle(16); + break; + + case 69: + playSoundAndDisplaySubTitle(14); + break; + + case 77: + playSoundAndDisplaySubTitle(13); + break; + + case 79: + _animCurrentFrame = 4; + break; + } + + return frm; +} + +int SeqPlayer_HOF::cbHOFDEMO_water(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 1) + playSoundAndDisplaySubTitle(11); + return frm; +} + +int SeqPlayer_HOF::cbHOFDEMO_bail(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + return frm; +} + +int SeqPlayer_HOF::cbHOFDEMO_dig(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + return frm; +} + +#ifdef ENABLE_LOL +int SeqPlayer_HOF::cbLOLDEMO_scene1(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + Palette &tmpPal = _screen->getPalette(2); + + if (!(_callbackCurrentFrame % 100)) { + if (_callbackCurrentFrame == 0) { + _vm->sound()->haltTrack(); + _vm->sound()->playTrack(6); + } + tmpPal.copy(_screen->getPalette(0)); + + for (int i = 3; i < 768; i++) { + tmpPal[i] = ((int)tmpPal[i] * 120) / 64; + if (tmpPal[i] > 0x3F) + tmpPal[i] = 0x3F; + } + + playSoundAndDisplaySubTitle(_vm->_rnd.getRandomBit()); + _screen->setScreenPalette(tmpPal); + _screen->updateScreen(); + _vm->delay(8); + } else { + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->updateScreen(); + if (_callbackCurrentFrame == 40) + playSoundAndDisplaySubTitle(3); + } + + _callbackCurrentFrame++; + return frm; +} + +int SeqPlayer_HOF::cbLOLDEMO_scene2(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (frm - 17) { + case 0: + _animDuration = 8; + break; + case 3: + case 6: + case 9: + playSoundEffect(8, 255 - ((26 - frm) << 3)); + break; + case 15: + playSoundAndDisplaySubTitle(9); + break; + case 18: + playSoundAndDisplaySubTitle(2); + break; + default: + break; + } + _callbackCurrentFrame++; + return frm; +} + +int SeqPlayer_HOF::cbLOLDEMO_scene3(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (frm == 1) + playSoundAndDisplaySubTitle(6); + else if (frm == 24) + playSoundAndDisplaySubTitle(7); + + _callbackCurrentFrame++; + return frm; +} + +int SeqPlayer_HOF::cbLOLDEMO_scene4(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (frm) { + case 11: + case 14: + case 17: + case 20: + playSoundEffect(8, 255 - ((22 - frm) << 3)); + break; + case 22: + playSoundAndDisplaySubTitle(11); + break; + case 24: + playSoundAndDisplaySubTitle(8); + break; + case 30: + playSoundAndDisplaySubTitle(15); + break; + case 34: + playSoundAndDisplaySubTitle(14); + break; + case 38: + playSoundAndDisplaySubTitle(13); + break; + case 42: + playSoundAndDisplaySubTitle(12); + break; + default: + break; + } + + _callbackCurrentFrame++; + return frm; +} + +int SeqPlayer_HOF::cbLOLDEMO_scene5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + switch (_callbackCurrentFrame++) { + case 0: + case 4: + case 6: + case 8: + case 10: + case 14: + case 16: + case 18: + case 20: + case 22: + case 24: + case 26: + case 28: + case 30: + playSoundEffect(15, 255 - ((31 - frm) << 3)); + break; + case 32: + playSoundAndDisplaySubTitle(16); + break; + case 42: + playSoundAndDisplaySubTitle(6); + break; + default: + break; + } + return frm; +} + +int SeqPlayer_HOF::cbLOLDEMO_text5(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + if (_callbackCurrentFrame++ == 100) + playSoundAndDisplaySubTitle(5); + return frm; +} + +int SeqPlayer_HOF::cbLOLDEMO_scene6(WSAMovie_v2 *wsaObj, int x, int y, int frm) { + while (_scrollProgressCounter < 290) { + setCountDown(6); + if (!_callbackCurrentFrame) { + _screen->loadBitmap("adtext.cps", 4, 4, 0); + _screen->loadBitmap("adtext2.cps", 6, 6, 0); + _screen->copyPageMemory(6, 0, 4, 64000, 1024); + _screen->copyPageMemory(6, 1023, 6, 0, 64000); + _scrollProgressCounter = 0; + } + + if (_callbackCurrentFrame % 175) { + _screen->setScreenPalette(_screen->getPalette(0)); + } else { + Palette &tmpPal = _screen->getPalette(2); + tmpPal.copy(_screen->getPalette(0)); + + for (int i = 3; i < 0x300; i++) { + tmpPal[i] = ((int)tmpPal[i] * 120) / 64; + if (tmpPal[i] > 0x3F) + tmpPal[i] = 0x3F; + } + + playSoundAndDisplaySubTitle(_vm->_rnd.getRandomBit()); + _screen->setScreenPalette(tmpPal); + _screen->updateScreen(); + _vm->delay(8); + } + + if (_callbackCurrentFrame == 40 || _callbackCurrentFrame == 80 || _callbackCurrentFrame == 150 || _callbackCurrentFrame == 300) + playSoundAndDisplaySubTitle(3); + + _screen->copyPage(12, 2); + updateDemoAdText(70, 130); + _screen->copyPage(2, 0); + _screen->updateScreen(); + _callbackCurrentFrame++; + if (_callbackCurrentFrame < 128 || _callbackCurrentFrame > 207) + _scrollProgressCounter++; + + while (countDownRunning()) + delayTicks(1); + } + _screen->copyPage(2, 12); + + return 0; +} +#endif // ENABLE_LOL + +#undef CASE_ALT + +const uint8 SeqPlayer_HOF::_textColorPresets[] = { 0x01, 0x01, 0x00, 0x3F, 0x3F, 0x3F }; + +void KyraEngine_HoF::seq_showStarcraftLogo() { + WSAMovie_v2 *ci = new WSAMovie_v2(this); + assert(ci); + _screen->clearPage(2); + _res->loadPakFile("INTROGEN.PAK"); + int endframe = ci->open("CI.WSA", 0, &_screen->getPalette(0)); + _res->unloadPakFile("INTROGEN.PAK"); + if (!ci->opened()) { + delete ci; + return; + } + _screen->hideMouse(); + ci->displayFrame(0, 2, 0, 0, 0, 0, 0); + _screen->copyPage(2, 0); + _screen->fadeFromBlack(); + for (int i = 1; i < endframe; i++) { + uint32 end = _system->getMillis() + 50; + if (skipFlag()) + break; + ci->displayFrame(i, 2, 0, 0, 0, 0, 0); + _screen->copyPage(2, 0); + _screen->updateScreen(); + uint32 cur = _system->getMillis(); + if (end > cur) + delay(end - cur); + else + updateInput(); + } + if (!skipFlag()) { + uint32 end = _system->getMillis() + 50; + ci->displayFrame(0, 2, 0, 0, 0, 0, 0); + _screen->copyPage(2, 0); + _screen->updateScreen(); + uint32 cur = _system->getMillis(); + if (end > cur) + delay(end - cur); + else + updateInput(); + } + _screen->fadeToBlack(); + _screen->showMouse(); + + _eventList.clear(); + delete ci; +} + +int KyraEngine_HoF::seq_playIntro() { + bool startupSaveLoadable = saveFileLoadable(0); + return SeqPlayer_HOF(this, _screen, _system, startupSaveLoadable).play(kSequenceVirgin, startupSaveLoadable ? kSequenceTitle : kSequenceNoLooping); +} + +int KyraEngine_HoF::seq_playOutro() { + return SeqPlayer_HOF(this, _screen, _system).play(kSequenceFunters, kSequenceFrash); +} + +int KyraEngine_HoF::seq_playDemo() { + SeqPlayer_HOF(this, _screen, _system).play(kSequenceHoFDemoVirgin, kSequenceHoFDemoVirgin); + return 4; +} + +void KyraEngine_HoF::seq_pausePlayer(bool toggle) { + SeqPlayer_HOF *activePlayer = SeqPlayer_HOF::instance(); + if (activePlayer) + activePlayer->pause(toggle); +} + +#ifdef ENABLE_LOL +int LoLEngine::playDemo() { + SeqPlayer_HOF(this, _screen, _system).play(kSequenceLoLDemoScene1, kSequenceLoLDemoScene1); + return -1; +} + +void LoLEngine::pauseDemoPlayer(bool toggle) { + SeqPlayer_HOF *activePlayer = SeqPlayer_HOF::instance(); + if (activePlayer) + activePlayer->pause(toggle); +} +#endif // ENABLE_LOL + +#pragma mark - +#pragma mark - Ingame sequences +#pragma mark - + +void KyraEngine_HoF::seq_makeBookOrCauldronAppear(int type) { + _screen->hideMouse(); + showMessage(0, 0xCF); + + if (type == 1) + seq_makeBookAppear(); + else if (type == 2) + loadInvWsa("CAULDRON.WSA", 1, 6, 0, -2, -2, 1); + + _screen->copyRegionToBuffer(2, 0, 0, 320, 200, _screenBuffer); + _screen->loadBitmap("_PLAYALL.CPS", 3, 3, 0); + + static const uint8 bookCauldronRects[] = { + 0x46, 0x90, 0x7F, 0x2B, // unknown rect (maybe unused?) + 0xCE, 0x90, 0x2C, 0x2C, // book rect + 0xFA, 0x90, 0x46, 0x2C // cauldron rect + }; + + int x = bookCauldronRects[type*4+0]; + int y = bookCauldronRects[type*4+1]; + int w = bookCauldronRects[type*4+2]; + int h = bookCauldronRects[type*4+3]; + _screen->copyRegion(x, y, x, y, w, h, 2, 0, Screen::CR_NO_P_CHECK); + + _screen->copyBlockToPage(2, 0, 0, 320, 200, _screenBuffer); + + if (type == 2) { + int32 countdown = _rnd.getRandomNumberRng(45, 80); + _timer->setCountdown(2, countdown * 60); + } + + _screen->showMouse(); +} + +void KyraEngine_HoF::seq_makeBookAppear() { + _screen->hideMouse(); + + displayInvWsaLastFrame(); + + showMessage(0, 0xCF); + + loadInvWsa("BOOK2.WSA", 0, 4, 2, -1, -1, 0); + + uint8 *rect = new uint8[_screen->getRectSize(_invWsa.w, _invWsa.h)]; + assert(rect); + + _screen->copyRegionToBuffer(_invWsa.page, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, rect); + + _invWsa.running = false; + snd_playSoundEffect(0xAF); + + while (true) { + _invWsa.timer = _system->getMillis() + _invWsa.delay * _tickLength; + + _screen->copyBlockToPage(_invWsa.page, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, rect); + + _invWsa.wsa->displayFrame(_invWsa.curFrame, _invWsa.page, 0, 0, 0x4000, 0, 0); + + if (_invWsa.page) + _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, _invWsa.page, 0, Screen::CR_NO_P_CHECK); + + ++_invWsa.curFrame; + + if (_invWsa.curFrame >= _invWsa.lastFrame && !shouldQuit()) + break; + + switch (_invWsa.curFrame) { + case 39: + snd_playSoundEffect(0xCA); + break; + + case 50: + snd_playSoundEffect(0x6A); + break; + + case 72: + snd_playSoundEffect(0xCB); + break; + + case 85: + snd_playSoundEffect(0x38); + break; + + default: + break; + } + + do { + update(); + } while (_invWsa.timer > _system->getMillis() && !skipFlag()); + } + + closeInvWsa(); + delete[] rect; + _invWsa.running = false; + + _screen->showMouse(); +} + +} // End of namespace Kyra diff --git a/engines/kyra/sequence/sequences_hof.h b/engines/kyra/sequence/sequences_hof.h new file mode 100644 index 0000000000..95d9260530 --- /dev/null +++ b/engines/kyra/sequence/sequences_hof.h @@ -0,0 +1,74 @@ +/* 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 KYRA_SEQUENCES_HOF_H +#define KYRA_SEQUENCES_HOF_H + +#include "kyra/engine/kyra_v2.h" + +namespace Kyra { + +struct HoFSequence { + const char *wsaFile; + const char *cpsFile; + uint16 flags; + uint8 fadeInTransitionType; + uint8 fadeOutTransitionType; + int16 stringIndex1; + int16 stringIndex2; + uint16 startFrame; + uint16 numFrames; + uint16 duration; + uint16 xPos; + uint16 yPos; + uint16 timeout; +}; + +struct HoFNestedSequence { + const char *wsaFile; + const FrameControl *wsaControl; + uint16 flags; + uint16 startframe; + uint16 endFrame; + uint16 frameDelay; + uint16 x; + uint16 y; + uint16 fadeInTransitionType; + uint16 fadeOutTransitionType; +}; + +struct HoFSeqData { + const HoFSequence *seq; + int numSeq; + const HoFNestedSequence *nestedSeq; + int numNestedSeq; +}; + +struct HoFSeqItemAnimData { + int16 itemIndex; + uint16 y; + const uint16 *frames; +}; + +} // End of namespace Kyra + +#endif diff --git a/engines/kyra/sequence/sequences_lok.cpp b/engines/kyra/sequence/sequences_lok.cpp new file mode 100644 index 0000000000..faddf762fb --- /dev/null +++ b/engines/kyra/sequence/sequences_lok.cpp @@ -0,0 +1,2116 @@ +/* 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 "kyra/engine/kyra_lok.h" +#include "kyra/sequence/seqplayer.h" +#include "kyra/resource/resource.h" +#include "kyra/engine/sprites.h" +#include "kyra/graphics/wsamovie.h" +#include "kyra/graphics/animator_lok.h" +#include "kyra/engine/timer.h" +#include "kyra/sound/sound.h" + +#include "common/system.h" +#include "common/savefile.h" +#include "common/list.h" + +namespace Kyra { + +void KyraEngine_LoK::seq_demo() { + snd_playTheme(0, 2); + + _screen->loadBitmap("START.CPS", 7, 7, &_screen->getPalette(0)); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 6, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _screen->fadeFromBlack(); + delay(60 * _tickLength); + _screen->fadeToBlack(); + + _screen->clearPage(0); + _screen->loadBitmap("TOP.CPS", 7, 7, 0); + _screen->loadBitmap("BOTTOM.CPS", 5, 5, &_screen->getPalette(0)); + _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 0); + _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 0); + _screen->updateScreen(); + _screen->fadeFromBlack(); + + _seq->playSequence(_seq_WestwoodLogo, true); + delay(60 * _tickLength); + _seq->playSequence(_seq_KyrandiaLogo, true); + + _screen->fadeToBlack(); + _screen->clearPage(2); + _screen->clearPage(0); + + _seq->playSequence(_seq_Demo1, true); + + _screen->clearPage(0); + _seq->playSequence(_seq_Demo2, true); + + _screen->clearPage(0); + _seq->playSequence(_seq_Demo3, true); + + _screen->clearPage(0); + _seq->playSequence(_seq_Demo4, true); + + _screen->clearPage(0); + _screen->loadBitmap("FINAL.CPS", 7, 7, &_screen->getPalette(0)); + _screen->_curPage = 0; + _screen->copyRegion(0, 0, 0, 0, 320, 200, 6, 0); + _screen->updateScreen(); + _screen->fadeFromBlack(); + delay(60 * _tickLength); + _screen->fadeToBlack(); + _sound->haltTrack(); +} + +void KyraEngine_LoK::seq_intro() { + if (_flags.isTalkie) + _res->loadPakFile("INTRO.VRM"); + + static const IntroProc introProcTable[] = { + &KyraEngine_LoK::seq_introPublisherLogos, + &KyraEngine_LoK::seq_introLogos, + &KyraEngine_LoK::seq_introStory, + &KyraEngine_LoK::seq_introMalcolmTree, + &KyraEngine_LoK::seq_introKallakWriting, + &KyraEngine_LoK::seq_introKallakMalcolm + }; + + Common::InSaveFile *in; + if ((in = _saveFileMan->openForLoading(getSavegameFilename(0)))) { + delete in; + _skipIntroFlag = true; + } else { + _skipIntroFlag = !_flags.isDemo; + } + + _seq->setCopyViewOffs(true); + _screen->setFont(_flags.lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); + if (_flags.platform != Common::kPlatformFMTowns && _flags.platform != Common::kPlatformPC98 && _flags.platform != Common::kPlatformAmiga) + snd_playTheme(0, 2); + _text->setTalkCoords(144); + + for (int i = 0; i < ARRAYSIZE(introProcTable) && !seq_skipSequence(); ++i) { + if ((this->*introProcTable[i])() && !shouldQuit()) { + resetSkipFlag(); + _screen->fadeToBlack(); + _screen->clearPage(0); + } + } + + _text->setTalkCoords(136); + delay(30 * _tickLength); + _seq->setCopyViewOffs(false); + _sound->haltTrack(); + _sound->voiceStop(); + + if (_flags.isTalkie) + _res->unloadPakFile("INTRO.VRM"); +} + +bool KyraEngine_LoK::seq_introPublisherLogos() { + if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) { + _screen->loadBitmap("LOGO.CPS", 3, 3, &_screen->getPalette(0)); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + _screen->fadeFromBlack(); + delay(90 * _tickLength); + if (!_abortIntroFlag) { + _screen->fadeToBlack(); + snd_playWanderScoreViaMap(_flags.platform == Common::kPlatformFMTowns ? 57 : 2, 0); + } + } else if (_flags.platform == Common::kPlatformMacintosh && _res->exists("MP_GOLD.CPS")) { + _screen->loadPalette("MP_GOLD.COL", _screen->getPalette(0)); + _screen->loadBitmap("MP_GOLD.CPS", 3, 3, 0); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0); + _screen->updateScreen(); + _screen->fadeFromBlack(); + delay(120 * _tickLength); + if (!_abortIntroFlag) + _screen->fadeToBlack(); + } + + return _abortIntroFlag; +} + +bool KyraEngine_LoK::seq_introLogos() { + _screen->clearPage(0); + + if (_flags.platform == Common::kPlatformAmiga) { + _screen->loadPaletteTable("INTRO.PAL", 0); + _screen->loadBitmap("BOTTOM.CPS", 3, 5, 0); + _screen->loadBitmap("TOP.CPS", 3, 3, 0); + _screen->copyRegion(0, 0, 0, 111, 320, 64, 2, 0); + _screen->copyRegion(0, 91, 0, 8, 320, 109, 2, 0); + _screen->copyRegion(0, 0, 0, 0, 320, 190, 0, 2); + } else { + _screen->loadBitmap("TOP.CPS", 7, 7, 0); + _screen->loadBitmap("BOTTOM.CPS", 5, 5, &_screen->getPalette(0)); + _screen->copyRegion(0, 91, 0, 8, 320, 103, 6, 0); + _screen->copyRegion(0, 0, 0, 111, 320, 64, 6, 0); + } + + _screen->_curPage = 0; + _screen->updateScreen(); + _screen->fadeFromBlack(); + + if (_seq->playSequence(_seq_WestwoodLogo, skipFlag()) || shouldQuit()) + return true; + + delay(60 * _tickLength); + + if (_flags.platform == Common::kPlatformAmiga) { + _screen->copyPalette(0, 1); + _screen->setScreenPalette(_screen->getPalette(0)); + } + + Screen::FontId of = _screen->setFont(Screen::FID_8_FNT); + + if (_seq->playSequence(_seq_KyrandiaLogo, skipFlag()) || shouldQuit()) + return true; + + _screen->setFont(of); + _screen->fillRect(0, 179, 319, 199, 0); + + if (shouldQuit()) + return false; + + if (_flags.platform == Common::kPlatformAmiga) { + _screen->copyPalette(0, 2); + _screen->fadeToBlack(); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 4, 0); + _screen->fadeFromBlack(); + } else { + _screen->copyRegion(0, 91, 0, 8, 320, 104, 6, 2); + _screen->copyRegion(0, 0, 0, 112, 320, 64, 6, 2); + + uint32 start = _system->getMillis(); + bool doneFlag = false; + int oldDistance = 0; + + do { + uint32 now = _system->getMillis(); + + // The smallest y2 we ever draw the screen for is 65. + int distance = (now - start) / _tickLength; + if (distance > 112) { + distance = 112; + doneFlag = true; + } + + if (distance > oldDistance) { + int y1 = 8 + distance; + int h1 = 168 - distance; + int y2 = 176 - distance; + int h2 = distance; + + _screen->copyRegion(0, y1, 0, 8, 320, h1, 2, 0); + if (h2 > 0) + _screen->copyRegion(0, 64, 0, y2, 320, h2, 4, 0); + _screen->updateScreen(); + } + + oldDistance = distance; + delay(10); + } while (!doneFlag && !shouldQuit() && !_abortIntroFlag); + } + + if (_abortIntroFlag || shouldQuit()) + return true; + + return _seq->playSequence(_seq_Forest, true); +} + +bool KyraEngine_LoK::seq_introStory() { + _screen->clearPage(3); + _screen->clearPage(0); + + // HACK: The Italian fan translation uses an special text screen here + // so we show it even when text is disabled + if (!textEnabled() && speechEnabled() && _flags.lang != Common::IT_ITA) + return false; + + if (((_flags.lang == Common::EN_ANY || _flags.lang == Common::RU_RUS) && !_flags.isTalkie && _flags.platform == Common::kPlatformDOS) || _flags.platform == Common::kPlatformAmiga) + _screen->loadBitmap("TEXT.CPS", 3, 3, &_screen->getPalette(0)); + else if (_flags.lang == Common::EN_ANY || _flags.lang == Common::JA_JPN) + _screen->loadBitmap("TEXT_ENG.CPS", 3, 3, &_screen->getPalette(0)); + else if (_flags.lang == Common::DE_DEU) + _screen->loadBitmap("TEXT_GER.CPS", 3, 3, &_screen->getPalette(0)); + else if (_flags.lang == Common::FR_FRA) + _screen->loadBitmap("TEXT_FRE.CPS", 3, 3, &_screen->getPalette(0)); + else if (_flags.lang == Common::ES_ESP) + _screen->loadBitmap("TEXT_SPA.CPS", 3, 3, &_screen->getPalette(0)); + else if (_flags.lang == Common::IT_ITA && !_flags.isTalkie) + _screen->loadBitmap("TEXT_ITA.CPS", 3, 3, &_screen->getPalette(0)); + else if (_flags.lang == Common::IT_ITA && _flags.isTalkie) + _screen->loadBitmap("TEXT_ENG.CPS", 3, 3, &_screen->getPalette(0)); + else + warning("no story graphics file found"); + + if (_flags.platform == Common::kPlatformAmiga) + _screen->setScreenPalette(_screen->getPalette(4)); + else + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->copyPage(3, 0); + + if (_flags.lang == Common::JA_JPN) { + const int y1 = 175; + int x1, x2, y2, col1; + const char *s1, *s2; + + if (_flags.platform == Common::kPlatformFMTowns) { + s1 = _seq_textsTable[18]; + s2 = _seq_textsTable[19]; + x1 = (Screen::SCREEN_W - _screen->getTextWidth(s1)) / 2; + x2 = (Screen::SCREEN_W - _screen->getTextWidth(s2)) / 2; + uint8 colorMap[] = { 0, 15, 12, 12 }; + _screen->setTextColor(colorMap, 0, 3); + y2 = 184; + col1 = 5; + + } else { + s1 = _storyStrings[0]; + s2 = _storyStrings[1]; + x1 = x2 = 54; + y2 = 185; + col1 = 15; + } + + _screen->printText(s1, x1, y1, col1, 8); + _screen->printText(s2, x2, y2, col1, 8); + } + + _screen->updateScreen(); + delay(360 * _tickLength); + + return _abortIntroFlag; +} + +bool KyraEngine_LoK::seq_introMalcolmTree() { + _screen->_curPage = 0; + _screen->clearPage(3); + return _seq->playSequence(_seq_MalcolmTree, true); +} + +bool KyraEngine_LoK::seq_introKallakWriting() { + _seq->makeHandShapes(); + _screen->setAnimBlockPtr(5060); + _screen->_charWidth = -2; + _screen->clearPage(3); + const bool skipped = _seq->playSequence(_seq_KallakWriting, true); + _seq->freeHandShapes(); + + return skipped; +} + +bool KyraEngine_LoK::seq_introKallakMalcolm() { + _screen->clearPage(3); + return _seq->playSequence(_seq_KallakMalcolm, true); +} + +void KyraEngine_LoK::seq_createAmuletJewel(int jewel, int page, int noSound, int drawOnly) { + static const uint16 specialJewelTable[] = { + 0x167, 0x162, 0x15D, 0x158, 0x153, 0xFFFF + }; + static const uint16 specialJewelTable1[] = { + 0x14F, 0x154, 0x159, 0x15E, 0x163, 0xFFFF + }; + static const uint16 specialJewelTable2[] = { + 0x150, 0x155, 0x15A, 0x15F, 0x164, 0xFFFF + }; + static const uint16 specialJewelTable3[] = { + 0x151, 0x156, 0x15B, 0x160, 0x165, 0xFFFF + }; + static const uint16 specialJewelTable4[] = { + 0x152, 0x157, 0x15C, 0x161, 0x166, 0xFFFF + }; + if (!noSound) + snd_playSoundEffect(0x5F); + _screen->hideMouse(); + if (!drawOnly) { + for (int i = 0; specialJewelTable[i] != 0xFFFF; ++i) { + _screen->drawShape(page, _shapes[specialJewelTable[i]], _amuletX2[jewel], _amuletY2[jewel], 0, 0); + _screen->updateScreen(); + delayWithTicks(3); + } + + const uint16 *opcodes = 0; + switch (jewel - 1) { + case 0: + opcodes = specialJewelTable1; + break; + + case 1: + opcodes = specialJewelTable2; + break; + + case 2: + opcodes = specialJewelTable3; + break; + + case 3: + opcodes = specialJewelTable4; + break; + } + + if (opcodes) { + for (int i = 0; opcodes[i] != 0xFFFF; ++i) { + _screen->drawShape(page, _shapes[opcodes[i]], _amuletX2[jewel], _amuletY2[jewel], 0, 0); + _screen->updateScreen(); + delayWithTicks(3); + } + } + } + _screen->drawShape(page, _shapes[323 + jewel], _amuletX2[jewel], _amuletY2[jewel], 0, 0); + _screen->updateScreen(); + _screen->showMouse(); + setGameFlag(0x55 + jewel); +} + +void KyraEngine_LoK::seq_brandonHealing() { + if (!(_deathHandler & 8)) + return; + if (_currentCharacter->sceneId == 210) { + if (_beadStateVar == 4 || _beadStateVar == 6) + return; + } + _screen->hideMouse(); + checkAmuletAnimFlags(); + assert(_healingShapeTable); + setupShapes123(_healingShapeTable, 22, 0); + _animator->setBrandonAnimSeqSize(3, 48); + snd_playSoundEffect(0x53); + for (int i = 123; i <= 144; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + for (int i = 125; i >= 123; --i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + _animator->resetBrandonAnimSeqSize(); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + freeShapes123(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_brandonHealing2() { + _screen->hideMouse(); + checkAmuletAnimFlags(); + assert(_healingShape2Table); + setupShapes123(_healingShape2Table, 30, 0); + resetBrandonPoisonFlags(); + _animator->setBrandonAnimSeqSize(3, 48); + snd_playSoundEffect(0x50); + for (int i = 123; i <= 152; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + _animator->resetBrandonAnimSeqSize(); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + freeShapes123(); + _screen->showMouse(); + assert(_poisonGone); + characterSays(2010, _poisonGone[0], 0, -2); + characterSays(2011, _poisonGone[1], 0, -2); +} + +void KyraEngine_LoK::seq_poisonDeathNow(int now) { + if (!(_brandonStatusBit & 1)) + return; + ++_poisonDeathCounter; + if (now) + _poisonDeathCounter = 2; + if (_poisonDeathCounter >= 2) { + snd_playWanderScoreViaMap(1, 1); + assert(_thePoison); + characterSays(7000, _thePoison[0], 0, -2); + characterSays(7001, _thePoison[1], 0, -2); + seq_poisonDeathNowAnim(); + _deathHandler = 3; + } else { + assert(_thePoison); + characterSays(7002, _thePoison[2], 0, -2); + characterSays(7004, _thePoison[3], 0, -2); + } +} + +void KyraEngine_LoK::seq_poisonDeathNowAnim() { + _screen->hideMouse(); + checkAmuletAnimFlags(); + assert(_posionDeathShapeTable); + setupShapes123(_posionDeathShapeTable, 20, 0); + _animator->setBrandonAnimSeqSize(8, 48); + + _currentCharacter->currentAnimFrame = 124; + _animator->animRefreshNPC(0); + delayWithTicks(30); + + _currentCharacter->currentAnimFrame = 123; + _animator->animRefreshNPC(0); + delayWithTicks(30); + + for (int i = 125; i <= 139; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + delayWithTicks(60); + + for (int i = 140; i <= 142; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + delayWithTicks(60); + + _animator->resetBrandonAnimSeqSize(); + freeShapes123(); + _animator->restoreAllObjectBackgrounds(); + _currentCharacter->x1 = _currentCharacter->x2 = -1; + _currentCharacter->y1 = _currentCharacter->y2 = -1; + _animator->preserveAllBackgrounds(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_playFluteAnimation() { + _screen->hideMouse(); + checkAmuletAnimFlags(); + setupShapes123(_fluteAnimShapeTable, 36, 0); + _animator->setBrandonAnimSeqSize(3, 75); + for (int i = 123; i <= 130; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(2); + } + + int delayTime = 0, soundType = 0; + if (queryGameFlag(0x85)) { + snd_playSoundEffect(0x63); + delayTime = 9; + soundType = 3; + } else if (!queryGameFlag(0x86)) { + snd_playSoundEffect(0x61); + delayTime = 2; + soundType = 1; + setGameFlag(0x86); + } else { + snd_playSoundEffect(0x62); + delayTime = 2; + soundType = 2; + } + + for (int i = 131; i <= 158; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(delayTime); + } + + for (int i = 126; i >= 123; --i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(delayTime); + } + _animator->resetBrandonAnimSeqSize(); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + freeShapes123(); + _screen->showMouse(); + + if (soundType == 1) { + assert(_fluteString); + characterSays(1000, _fluteString[0], 0, -2); + } else if (soundType == 2) { + assert(_fluteString); + characterSays(1001, _fluteString[1], 0, -2); + } +} + +void KyraEngine_LoK::seq_winterScroll1() { + _screen->hideMouse(); + checkAmuletAnimFlags(); + assert(_winterScrollTable); + assert(_winterScroll1Table); + assert(_winterScroll2Table); + setupShapes123(_winterScrollTable, 7, 0); + _animator->setBrandonAnimSeqSize(5, 66); + + for (int i = 123; i <= 129; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + freeShapes123(); + snd_playSoundEffect(0x20); + + uint8 numFrames, midpoint; + if (_flags.isTalkie) { + numFrames = 18; + midpoint = 136; + } else { + numFrames = 35; + midpoint = 147; + } + setupShapes123(_winterScroll1Table, numFrames, 0); + for (int i = 123; i < midpoint; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + if (_currentCharacter->sceneId == 41 && !queryGameFlag(0xA2)) { + snd_playSoundEffect(0x20); + _sprites->_anims[0].play = false; + _animator->sprites()[0].active = 0; + _sprites->_anims[1].play = true; + _animator->sprites()[1].active = 1; + + if (_flags.platform != Common::kPlatformAmiga) + setGameFlag(0xA2); + } + + for (int i = midpoint; i < 123 + numFrames; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + if (_currentCharacter->sceneId == 117 && !queryGameFlag(0xB3)) { + for (int i = 0; i <= 7; ++i) { + _sprites->_anims[i].play = false; + _animator->sprites()[i].active = 0; + } + + if (_flags.platform == Common::kPlatformAmiga) { + _screen->copyPalette(0, 11); + } else { + _screen->getPalette(0).copy(palTable2()[0], 0, 20, 228); + _screen->fadePalette(_screen->getPalette(0), 72); + _screen->setScreenPalette(_screen->getPalette(0)); + setGameFlag(0xB3); + } + } else { + delayWithTicks(120); + } + + freeShapes123(); + setupShapes123(_winterScroll2Table, 4, 0); + + for (int i = 123; i <= 126; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + _animator->resetBrandonAnimSeqSize(); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + freeShapes123(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_winterScroll2() { + _screen->hideMouse(); + checkAmuletAnimFlags(); + assert(_winterScrollTable); + setupShapes123(_winterScrollTable, 7, 0); + _animator->setBrandonAnimSeqSize(5, 66); + + for (int i = 123; i <= 128; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + delayWithTicks(120); + + for (int i = 127; i >= 123; --i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + _animator->resetBrandonAnimSeqSize(); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + freeShapes123(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_makeBrandonInv() { + if (_deathHandler == 8) + return; + + if (_currentCharacter->sceneId == 210) { + if (_beadStateVar == 4 || _beadStateVar == 6) + return; + } + + _screen->hideMouse(); + checkAmuletAnimFlags(); + _brandonStatusBit |= 0x20; + _timer->setCountdown(18, 2700); + _brandonStatusBit |= 0x40; + snd_playSoundEffect(0x77); + _brandonInvFlag = 0; + while (_brandonInvFlag <= 0x100) { + _animator->animRefreshNPC(0); + delayWithTicks(10); + _brandonInvFlag += 0x10; + } + _brandonStatusBit &= 0xFFBF; + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_makeBrandonNormal() { + _screen->hideMouse(); + _brandonStatusBit |= 0x40; + snd_playSoundEffect(0x77); + _brandonInvFlag = 0x100; + while (_brandonInvFlag >= 0) { + _animator->animRefreshNPC(0); + delayWithTicks(10); + _brandonInvFlag -= 0x10; + } + _brandonInvFlag = 0; + _brandonStatusBit &= 0xFF9F; + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_makeBrandonNormal2() { + _screen->hideMouse(); + assert(_brandonToWispTable); + setupShapes123(_brandonToWispTable, 26, 0); + _animator->setBrandonAnimSeqSize(5, 48); + _brandonStatusBit &= 0xFFFD; + snd_playSoundEffect(0x6C); + for (int i = 138; i >= 123; --i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + _animator->setBrandonAnimSeqSize(3, 48); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + + if (_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245) + _screen->fadeSpecialPalette(31, 234, 13, 4); + else if (_currentCharacter->sceneId >= 118 && _currentCharacter->sceneId <= 186) + _screen->fadeSpecialPalette(14, 228, 15, 4); + + freeShapes123(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_makeBrandonWisp() { + if (_deathHandler == 8) + return; + + if (_currentCharacter->sceneId == 210) { + if (_beadStateVar == 4 || _beadStateVar == 6) + return; + } + _screen->hideMouse(); + checkAmuletAnimFlags(); + assert(_brandonToWispTable); + setupShapes123(_brandonToWispTable, 26, 0); + _animator->setBrandonAnimSeqSize(5, 48); + snd_playSoundEffect(0x6C); + for (int i = 123; i <= 138; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + _brandonStatusBit |= 2; + + if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) + _timer->setCountdown(14, 18000); + else + _timer->setCountdown(14, 7200); + + _animator->_brandonDrawFrame = 113; + _brandonStatusBit0x02Flag = 1; + _currentCharacter->currentAnimFrame = 113; + _animator->animRefreshNPC(0); + _animator->updateAllObjectShapes(); + + if (_flags.platform == Common::kPlatformAmiga) { + if ((_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245) || + (_currentCharacter->sceneId >= 118 && _currentCharacter->sceneId <= 186)) + _screen->fadePalette(_screen->getPalette(10), 0x54); + } else { + if (_currentCharacter->sceneId >= 229 && _currentCharacter->sceneId <= 245) + _screen->fadeSpecialPalette(30, 234, 13, 4); + else if (_currentCharacter->sceneId >= 118 && _currentCharacter->sceneId <= 186) + _screen->fadeSpecialPalette(14, 228, 15, 4); + } + + freeShapes123(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_dispelMagicAnimation() { + if (_deathHandler == 8) + return; + if (_currentCharacter->sceneId == 210) { + if (_beadStateVar == 4 || _beadStateVar == 6) + return; + } + _screen->hideMouse(); + // TODO + // This condition is always false. Is this a typo or a bug in the original? + if (_currentCharacter->sceneId == 210 && _currentCharacter->sceneId < 160) + _currentCharacter->facing = 3; + if (_malcolmFlag == 7 && _beadStateVar == 3) { + _beadStateVar = 6; + _unkEndSeqVar5 = 2; + _malcolmFlag = 10; + } + checkAmuletAnimFlags(); + setGameFlag(0xEE); + assert(_magicAnimationTable); + setupShapes123(_magicAnimationTable, 5, 0); + _animator->setBrandonAnimSeqSize(8, 49); + snd_playSoundEffect(0x15); + for (int i = 123; i <= 127; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + delayWithTicks(120); + + for (int i = 127; i >= 123; --i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(10); + } + _animator->resetBrandonAnimSeqSize(); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + freeShapes123(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_fillFlaskWithWater(int item, int type) { + int newItem = -1; + + static const uint8 flaskTable1[] = { 0x46, 0x48, 0x4A, 0x4C }; + static const uint8 flaskTable2[] = { 0x47, 0x49, 0x4B, 0x4D }; + + if (item >= 60 && item <= 77) { + assert(_flaskFull); + characterSays(8006, _flaskFull[0], 0, -2); + } else if (item == 78) { + assert(type >= 0 && type < ARRAYSIZE(flaskTable1)); + newItem = flaskTable1[type]; + } else if (item == 79) { + assert(type >= 0 && type < ARRAYSIZE(flaskTable2)); + newItem = flaskTable2[type]; + } + + if (newItem == -1) + return; + + setMouseItem(newItem); + _itemInHand = newItem; + + assert(_fullFlask); + assert(type < _fullFlask_Size && type >= 0); + + static const uint16 voiceEntries[] = { + 0x1F40, 0x1F41, 0x1F42, 0x1F45 + }; + assert(type < ARRAYSIZE(voiceEntries)); + + characterSays(voiceEntries[type], _fullFlask[type], 0, -2); +} + +void KyraEngine_LoK::seq_playDrinkPotionAnim(int item, int unk2, int flags) { + if (_flags.platform == Common::kPlatformAmiga) { + uint8 r, g, b; + + switch (item) { + case 60: case 61: + // 0xC22 + r = 50; + g = 8; + b = 8; + break; + + case 62: case 63: case 76: + case 77: + // 0x00E + r = 0; + g = 0; + b = 58; + break; + + case 64: case 65: + // 0xFF5 + r = 63; + g = 63; + b = 21; + break; + + case 66: + // 0x090 + r = 0; + g = 37; + b = 0; + break; + + case 67: + // 0xC61 + r = 50; + g = 25; + b = 4; + break; + + case 68: + // 0xE2E + r = 58; + g = 8; + b = 58; + break; + + case 69: + // 0xBBB + r = 46; + g = 46; + b = 46; + break; + + default: + // 0xFFF + r = 63; + g = 63; + b = 63; + } + + _screen->setPaletteIndex(16, r, g, b); + } else { + uint8 red, green, blue; + + switch (item) { + case 60: case 61: + red = 63; + green = blue = 6; + break; + + case 62: case 63: + red = green = 0; + blue = 67; + break; + + case 64: case 65: + red = 84; + green = 78; + blue = 14; + break; + + case 66: + red = blue = 0; + green = 48; + break; + + case 67: + red = 100; + green = 48; + blue = 23; + break; + + case 68: + red = 73; + green = 0; + blue = 89; + break; + + case 69: + red = green = 73; + blue = 86; + break; + + default: + red = 33; + green = 66; + blue = 100; + } + + red = red * 0x3F / 100; + green = green * 0x3F / 100; + blue = blue * 0x3F / 100; + + _screen->setPaletteIndex(0xFE, red, green, blue); + } + + _screen->hideMouse(); + checkAmuletAnimFlags(); + _currentCharacter->facing = 5; + _animator->animRefreshNPC(0); + assert(_drinkAnimationTable); + setupShapes123(_drinkAnimationTable, 9, flags); + _animator->setBrandonAnimSeqSize(5, 54); + + for (int i = 123; i <= 131; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(5); + } + + snd_playSoundEffect(0x34); + + for (int i = 0; i < 2; ++i) { + _currentCharacter->currentAnimFrame = 130; + _animator->animRefreshNPC(0); + delayWithTicks(7); + _currentCharacter->currentAnimFrame = 131; + _animator->animRefreshNPC(0); + delayWithTicks(7); + } + + if (unk2) { + // XXX + } + + for (int i = 131; i >= 123; --i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(5); + } + + _animator->resetBrandonAnimSeqSize(); + _currentCharacter->currentAnimFrame = 7; + _animator->animRefreshNPC(0); + freeShapes123(); + + if (_flags.platform != Common::kPlatformAmiga) + _screen->setPaletteIndex(0xFE, 30, 30, 30); + + _screen->showMouse(); +} + +int KyraEngine_LoK::seq_playEnd() { + if (_endSequenceSkipFlag) + return 0; + + if (_deathHandler == 8) + return 0; + + _screen->_curPage = 2; + if (_endSequenceNeedLoading) { + snd_playWanderScoreViaMap(50, 1); + setupPanPages(); + + if (_flags.platform == Common::kPlatformAmiga) { + _sound->loadSoundFile(kMusicFinale); + + // The original started song 0 directly here. Since our player + // uses 0, 1 for stop and fade we start song 0 with 2 + _sound->playTrack(2); + } + + _finalA = createWSAMovie(); + assert(_finalA); + _finalA->open("finala.wsa", 1, 0); + + _finalB = createWSAMovie(); + assert(_finalB); + _finalB->open("finalb.wsa", 1, 0); + + _finalC = createWSAMovie(); + assert(_finalC); + _endSequenceNeedLoading = 0; + _finalC->open("finalc.wsa", 1, 0); + + _screen->_curPage = 0; + _beadStateVar = 0; + _malcolmFlag = 0; + _unkEndSeqVar2 = _system->getMillis() + 600 * _tickLength; + + _screen->copyRegion(312, 0, 312, 0, 8, 136, 0, 2); + } + + // TODO: better handling. This timer shouldn't count when the menu is open or something. + if (_unkEndSeqVar2 != -1) { + if (_system->getMillis() > (uint32)_unkEndSeqVar2) { + _unkEndSeqVar2 = -1; + if (!_malcolmFlag) + _malcolmFlag = 1; + } + } + + if (handleMalcolmFlag()) { + _beadStateVar = 0; + _malcolmFlag = 12; + handleMalcolmFlag(); + handleBeadState(); + closeFinalWsa(); + if (_deathHandler == 8) { + _screen->_curPage = 0; + checkAmuletAnimFlags(); + seq_brandonToStone(); + delay(60 * _tickLength); + return 1; + } else { + _endSequenceSkipFlag = 1; + if (_text->printed()) + _text->restoreTalkTextMessageBkgd(2, 0); + + _screen->_curPage = 0; + _screen->hideMouse(); + + if (_flags.platform != Common::kPlatformAmiga) + _screen->fadeSpecialPalette(32, 228, 20, 60); + + delay(60 * _tickLength); + + _screen->loadBitmap("GEMHEAL.CPS", 3, 3, &_screen->getPalette(0)); + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->shuffleScreen(8, 8, 304, 128, 2, 0, 1, 0); + + uint32 nextTime = _system->getMillis() + 120 * _tickLength; + + _finalA = createWSAMovie(); + assert(_finalA); + _finalA->open("finald.wsa", 1, 0); + + delayUntil(nextTime); + snd_playSoundEffect(0x40); + for (int i = 0; i < 22; ++i) { + delayUntil(nextTime); + if (i == 4) + snd_playSoundEffect(0x3E); + else if (i == 20) + snd_playSoundEffect(_flags.platform == Common::kPlatformPC98 ? 0x13 : 0x0E); + nextTime = _system->getMillis() + 8 * _tickLength; + _finalA->displayFrame(i, 0, 8, 8, 0, 0, 0); + _screen->updateScreen(); + } + delete _finalA; + + _finalA = 0; + seq_playEnding(); + return 1; + } + } else { + handleBeadState(); + _screen->bitBlitRects(); + _screen->updateScreen(); + _screen->_curPage = 0; + } + return 0; +} + +void KyraEngine_LoK::seq_brandonToStone() { + _screen->hideMouse(); + assert(_brandonStoneTable); + setupShapes123(_brandonStoneTable, 14, 0); + _animator->setBrandonAnimSeqSize(5, 51); + + for (int i = 123; i <= 136; ++i) { + _currentCharacter->currentAnimFrame = i; + _animator->animRefreshNPC(0); + delayWithTicks(8); + } + + _animator->resetBrandonAnimSeqSize(); + freeShapes123(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_playEnding() { + if (shouldQuit()) + return; + _screen->hideMouse(); + _screen->_curPage = 0; + _screen->fadeToBlack(); + + if (_flags.platform == Common::kPlatformAmiga) { + _screen->loadBitmap("GEMCUT.CPS", 3, 3, &_screen->getPalette(0)); + _screen->copyRegion(232, 136, 176, 56, 56, 56, 2, 2); + _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 0, 2, Screen::CR_NO_P_CHECK); + } else { + _screen->loadBitmap("REUNION.CPS", 3, 3, &_screen->getPalette(0)); + _screen->copyRegion(8, 8, 8, 8, 304, 128, 2, 0); + } + + _screen->_curPage = 0; + // XXX + assert(_homeString); + drawSentenceCommand(_homeString[0], 179); + + _screen->getPalette(2).clear(); + _screen->setScreenPalette(_screen->getPalette(2)); + + _seqPlayerFlag = true; + _seq->playSequence(_seq_Reunion, false); + _screen->fadeToBlack(); + _seqPlayerFlag = false; + + _screen->showMouse(); + + // To avoid any remaining input events, we remove the queue + // over here. + _eventList.clear(); + + if (_flags.platform == Common::kPlatformAmiga) { + _screen->_charWidth = -2; + _screen->setCurPage(2); + + _screen->getPalette(2).clear(); + _screen->setScreenPalette(_screen->getPalette(2)); + + while (!shouldQuit()) { + seq_playCreditsAmiga(); + delayUntil(_system->getMillis() + 300 * _tickLength); + } + } else { + seq_playCredits(); + } +} + +namespace { +struct CreditsLine { + int16 x, y; + Screen::FontId font; + uint8 *str; +}; +} // end of anonymous namespace + +void KyraEngine_LoK::seq_playCredits() { + static const uint8 colorMap[] = { 0, 0, 0xC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + static const char stringTerms[] = { 0x5, 0xD, 0x0}; + + typedef Common::List<CreditsLine> CreditsLineList; + CreditsLineList lines; + + _screen->enableInterfacePalette(false); + + _screen->hideMouse(); + if (!_flags.isTalkie) { + _screen->loadFont(Screen::FID_CRED6_FNT, "CREDIT6.FNT"); + _screen->loadFont(Screen::FID_CRED8_FNT, "CREDIT8.FNT"); + + _screen->setFont(Screen::FID_CRED8_FNT); + } else + _screen->setFont(Screen::FID_8_FNT); + + _screen->loadBitmap("CHALET.CPS", 4, 4, &_screen->getPalette(0)); + + _screen->setCurPage(0); + _screen->clearCurPage(); + _screen->setTextColorMap(colorMap); + _screen->_charWidth = -1; + + // we only need this for the FM-TOWNS version + if (_flags.platform == Common::kPlatformFMTowns && _configMusic == 1) + snd_playWanderScoreViaMap(53, 1); + + uint8 *buffer = 0; + uint32 size = 0; + + if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) { + int sizeTmp = 0; + const uint8 *bufferTmp = _staticres->loadRawData(k1CreditsStrings, sizeTmp); + buffer = new uint8[sizeTmp]; + assert(buffer); + memcpy(buffer, bufferTmp, sizeTmp); + size = sizeTmp; + _staticres->unloadId(k1CreditsStrings); + } else { + buffer = _res->fileData("CREDITS.TXT", &size); + assert(buffer); + } + + uint8 *nextString = buffer; + uint8 *currentString = buffer; + int currentY = 200; + + do { + currentString = nextString; + nextString = (uint8 *)strpbrk((char *)currentString, stringTerms); + if (!nextString) + nextString = (uint8 *)strchr((char *)currentString, 0); + + CreditsLine line; + + int lineEndCode = nextString[0]; + *nextString = 0; + if (lineEndCode != 0) + nextString++; + + int alignment = 0; + if (*currentString == 3 || *currentString == 4) { + alignment = *currentString; + currentString++; + } + + if (*currentString == 1) { + currentString++; + + if (!_flags.isTalkie) + _screen->setFont(Screen::FID_CRED6_FNT); + } else if (*currentString == 2) { + currentString++; + + if (!_flags.isTalkie) + _screen->setFont(Screen::FID_CRED8_FNT); + } + + line.font = _screen->_currentFont; + + if (alignment == 3) + line.x = 157 - _screen->getTextWidth((const char *)currentString); + else if (alignment == 4) + line.x = 161; + else + line.x = (320 - _screen->getTextWidth((const char *)currentString)) / 2 + 1; + + line.y = currentY; + if (lineEndCode != 5) + currentY += 10; + + line.str = currentString; + + lines.push_back(line); + } while (*nextString); + + _screen->setCurPage(2); + + _screen->getPalette(2).clear(); + _screen->setScreenPalette(_screen->getPalette(2)); + + _screen->copyRegion(0, 32, 0, 32, 320, 128, 4, 0, Screen::CR_NO_P_CHECK); + _screen->fadePalette(_screen->getPalette(0), 0x5A); + + bool finished = false; + int bottom = 201; + while (!finished && !shouldQuit()) { + uint32 startLoop = _system->getMillis(); + + if (bottom > 175) { + _screen->copyRegion(0, 32, 0, 32, 320, 128, 4, 2, Screen::CR_NO_P_CHECK); + bottom = 0; + + for (CreditsLineList::iterator it = lines.begin(); it != lines.end();) { + if (it->y < 0) { + it = lines.erase(it); + continue; + } + + if (it->y < 200) { + if (it->font != _screen->_currentFont) + _screen->setFont(it->font); + + _screen->printText((const char *)it->str, it->x, it->y, 15, 0); + } + + it->y--; + if (it->y > bottom) + bottom = it->y; + + ++it; + } + + _screen->copyRegion(0, 32, 0, 32, 320, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + } + + if (checkInput(0, false)) { + removeInputTop(); + finished = true; + } + + uint32 now = _system->getMillis(); + uint32 nextLoop = startLoop + _tickLength * 5; + + if (nextLoop > now) + _system->delayMillis(nextLoop - now); + } + + delete[] buffer; + + _screen->fadeToBlack(); + _screen->clearCurPage(); + _screen->showMouse(); +} + +void KyraEngine_LoK::seq_playCreditsAmiga() { + _screen->setFont(Screen::FID_8_FNT); + + _screen->loadBitmap("CHALET.CPS", 4, 2, &_screen->getPalette(0)); + _screen->copyPage(2, 0); + + _screen->getPalette(0).fill(16, 1, 63); + _screen->fadePalette(_screen->getPalette(0), 0x5A); + _screen->updateScreen(); + + const char *theEnd = "THE END"; + + const int width = _screen->getTextWidth(theEnd) + 1; + int x = (320 - width) / 2 + 1; + + _screen->copyRegion(x, 8, x, 8, width, 56, 0, 2, Screen::CR_NO_P_CHECK); + _screen->copyRegion(x, 8, 0, 8, width, 11, 0, 2, Screen::CR_NO_P_CHECK); + _screen->printText(theEnd, 0, 10, 31, 0); + + for (int y = 18, h = 1; y >= 10 && !shouldQuit(); --y, ++h) { + uint32 endTime = _system->getMillis() + 3 * _tickLength; + + _screen->copyRegion(0, y, x, 8, width, h, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + delayUntil(endTime); + } + + for (int y = 8; y <= 62 && !shouldQuit(); ++y) { + uint32 endTime = _system->getMillis() + 3 * _tickLength; + + _screen->copyRegion(x, y, 0, 8, width, 11, 2, 2, Screen::CR_NO_P_CHECK); + _screen->printText(theEnd, 0, 9, 31, 0); + _screen->copyRegion(0, 8, x, y, width, 11, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + delayUntil(endTime); + } + + int size = 0; + const char *creditsData = (const char *)_staticres->loadRawData(k1CreditsStrings, size); + + char stringBuffer[81]; + memset(stringBuffer, 0, sizeof(stringBuffer)); + + const char *cur = creditsData; + char *specialString = stringBuffer; + bool fillRectFlag = false, subWidth = false, centerFlag = false; + x = 0; + int specialX = 0; + + const int fontHeight = _screen->getFontHeight(); + + do { + char code = *cur; + + if (code == 3) { + fillRectFlag = subWidth = true; + } else if (code == 5) { + centerFlag = true; + } else if (code == 4) { + if (fillRectFlag) { + _screen->fillRect(0, 0, 319, 20, 0); + + if (subWidth) + specialX = 157 - _screen->getTextWidth(stringBuffer); + + _screen->printText(stringBuffer, specialX + 8, 0, 31, 0); + } + + specialString = stringBuffer; + *specialString = 0; + + x = 161; + } else if (code == 13) { + if (!fillRectFlag) + _screen->fillRect(0, 0, 319, 20, 0); + + uint32 nextTime = _system->getMillis() + 8 * _tickLength; + + if (centerFlag) + x = (320 - _screen->getTextWidth(stringBuffer)) / 2 - 8; + + _screen->printText(stringBuffer, x + 8, 0, 31, 0); + + for (int i = 0; i < fontHeight && !shouldQuit(); ++i) { + _screen->copyRegion(0, 141, 0, 140, 320, 59, 0, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, i, 0, 198, 320, 3, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + delayUntil(nextTime); + nextTime = _system->getMillis() + 8 * _tickLength; + } + + specialString = stringBuffer; + *specialString = 0; + + centerFlag = fillRectFlag = false; + } else { + *specialString++ = code; + *specialString = 0; + } + + if (checkInput(0, false)) { + removeInputTop(); + break; + } + } while (++cur != (creditsData + size) && !shouldQuit()); +} + +bool KyraEngine_LoK::seq_skipSequence() const { + return shouldQuit() || _abortIntroFlag; +} + +int KyraEngine_LoK::handleMalcolmFlag() { + switch (_malcolmFlag) { + case 1: + _malcolmFrame = 0; + _malcolmFlag = 2; + _malcolmTimer2 = 0; + // fall through + + case 2: + if (_system->getMillis() >= _malcolmTimer2) { + _finalA->displayFrame(_malcolmFrame, 0, 8, 46, 0, 0, 0); + _screen->updateScreen(); + _malcolmTimer2 = _system->getMillis() + 8 * _tickLength; + ++_malcolmFrame; + if (_malcolmFrame > 13) { + _malcolmFlag = 3; + _malcolmTimer1 = _system->getMillis() + 180 * _tickLength; + } + } + break; + + case 3: + if (_system->getMillis() < _malcolmTimer1) { + if (_system->getMillis() >= _malcolmTimer2) { + _malcolmFrame = _rnd.getRandomNumberRng(14, 17); + _finalA->displayFrame(_malcolmFrame, 0, 8, 46, 0, 0, 0); + _screen->updateScreen(); + _malcolmTimer2 = _system->getMillis() + 8 * _tickLength; + } + } else { + _malcolmFlag = 4; + _malcolmFrame = 18; + } + break; + + case 4: + if (_system->getMillis() >= _malcolmTimer2) { + _finalA->displayFrame(_malcolmFrame, 0, 8, 46, 0, 0, 0); + _screen->updateScreen(); + _malcolmTimer2 = _system->getMillis() + 8 * _tickLength; + ++_malcolmFrame; + if (_malcolmFrame > 25) { + _malcolmFrame = 26; + _malcolmFlag = 5; + _beadStateVar = 1; + } + } + break; + + case 5: + if (_system->getMillis() >= _malcolmTimer2) { + _finalA->displayFrame(_malcolmFrame, 0, 8, 46, 0, 0, 0); + _screen->updateScreen(); + _malcolmTimer2 = _system->getMillis() + 8 * _tickLength; + ++_malcolmFrame; + if (_malcolmFrame > 31) { + _malcolmFrame = 32; + _malcolmFlag = 6; + } + } + break; + + case 6: + if (_unkEndSeqVar4) { + if (_malcolmFrame <= 33 && _system->getMillis() >= _malcolmTimer2) { + _finalA->displayFrame(_malcolmFrame, 0, 8, 46, 0, 0, 0); + _screen->updateScreen(); + _malcolmTimer2 = _system->getMillis() + 8 * _tickLength; + ++_malcolmFrame; + if (_malcolmFrame > 33) { + _malcolmFlag = 7; + _malcolmFrame = 32; + _unkEndSeqVar5 = 0; + } + } + } + break; + + case 7: + if (_unkEndSeqVar5 == 1) { + _malcolmFlag = 8; + _malcolmFrame = 34; + } else if (_unkEndSeqVar5 == 2) { + _malcolmFlag = 3; + _malcolmTimer1 = _system->getMillis() + 180 * _tickLength; + } + break; + + case 8: + if (_system->getMillis() >= _malcolmTimer2) { + _finalA->displayFrame(_malcolmFrame, 0, 8, 46, 0, 0, 0); + _screen->updateScreen(); + _malcolmTimer2 = _system->getMillis() + 8 * _tickLength; + ++_malcolmFrame; + if (_malcolmFrame > 37) { + _malcolmFlag = 0; + _deathHandler = 8; + return 1; + } + } + break; + + case 9: + snd_playSoundEffect(12); + snd_playSoundEffect(12); + for (int i = 0; i < 18; ++i) { + _malcolmTimer2 = _system->getMillis() + 4 * _tickLength; + _finalC->displayFrame(i, 0, 16, 50, 0, 0, 0); + _screen->updateScreen(); + delayUntil(_malcolmTimer2); + } + snd_playWanderScoreViaMap(51, 1); + delay(60 * _tickLength); + _malcolmFlag = 0; + return 1; + + case 10: + if (!_beadStateVar) { + handleBeadState(); + _screen->bitBlitRects(); + assert(_veryClever); + _text->printTalkTextMessage(_veryClever[0], 60, 31, 5, 0, 2); + _malcolmTimer2 = _system->getMillis() + 180 * _tickLength; + _malcolmFlag = 11; + } + break; + + case 11: + if (_system->getMillis() >= _malcolmTimer2) { + _text->restoreTalkTextMessageBkgd(2, 0); + _malcolmFlag = 3; + _malcolmTimer1 = _system->getMillis() + 180 * _tickLength; + } + break; + + default: + break; + } + + return 0; +} + +int KyraEngine_LoK::handleBeadState() { + static const int table1[] = { + -1, -2, -4, -5, -6, -7, -6, -5, + -4, -2, -1, 0, 1, 2, 4, 5, + 6, 7, 6, 5, 4, 2, 1, 0, 0 + }; + + static const int table2[] = { + 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 5, 5, 4, 4, + 3, 3, 2, 2, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + switch (_beadStateVar) { + case 0: + if (_beadState1.x != -1 && _endSequenceBackUpRect) { + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + } + + _beadState1.x = -1; + _beadState1.tableIndex = 0; + _beadStateTimer1 = 0; + _beadStateTimer2 = 0; + _lastDisplayedPanPage = 0; + return 1; + + case 1: + if (_beadState1.x != -1) { + if (_endSequenceBackUpRect) { + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + } + _beadState1.x = -1; + _beadState1.tableIndex = 0; + } + _beadStateVar = 2; + break; + + case 2: + if (_system->getMillis() >= _beadStateTimer1) { + int x = 0, y = 0; + _beadStateTimer1 = _system->getMillis() + 4 * _tickLength; + if (_beadState1.x == -1) { + assert(_panPagesTable); + _beadState1.width2 = _animator->fetchAnimWidth(_panPagesTable[19], 256); + _beadState1.width = ((_beadState1.width2 + 7) >> 3) + 1; + _beadState1.height = _animator->fetchAnimHeight(_panPagesTable[19], 256); + if (!_endSequenceBackUpRect) { + _endSequenceBackUpRect = new uint8[(_beadState1.width * _beadState1.height) << 3]; + assert(_endSequenceBackUpRect); + memset(_endSequenceBackUpRect, 0, ((_beadState1.width * _beadState1.height) << 3) * sizeof(uint8)); + } + x = _beadState1.x = 60; + y = _beadState1.y = 40; + initBeadState(x, y, x, 25, 8, &_beadState2); + } else { + if (processBead(_beadState1.x, _beadState1.y, x, y, &_beadState2)) { + _beadStateVar = 3; + _beadStateTimer2 = _system->getMillis() + 240 * _tickLength; + _unkEndSeqVar4 = 0; + _beadState1.dstX = _beadState1.x; + _beadState1.dstY = _beadState1.y; + return 0; + } else { + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + _beadState1.x = x; + _beadState1.y = y; + } + } + + _screen->copyRegionToBuffer(_screen->_curPage, x, y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0); + + if (_lastDisplayedPanPage > 17) + _lastDisplayedPanPage = 0; + + _screen->addBitBlitRect(x, y, _beadState1.width2, _beadState1.height); + } + break; + + case 3: + if (_system->getMillis() >= _beadStateTimer1) { + _beadStateTimer1 = _system->getMillis() + 4 * _tickLength; + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + + _beadState1.x = _beadState1.dstX + table1[_beadState1.tableIndex]; + _beadState1.y = _beadState1.dstY + table2[_beadState1.tableIndex]; + _screen->copyRegionToBuffer(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + + _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], _beadState1.x, _beadState1.y, 0, 0); + if (_lastDisplayedPanPage >= 17) + _lastDisplayedPanPage = 0; + + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + + ++_beadState1.tableIndex; + if (_beadState1.tableIndex > 24) { + _beadState1.tableIndex = 0; + _unkEndSeqVar4 = 1; + } + if (_system->getMillis() > _beadStateTimer2 && _malcolmFlag == 7 && !_unkAmuletVar && !_text->printed()) { + snd_playSoundEffect(0x0B); + if (_currentCharacter->x1 > 233 && _currentCharacter->x1 < 305 && _currentCharacter->y1 > 85 && _currentCharacter->y1 < 105 && + (_brandonStatusBit & 0x20)) { + _beadState1.unk8 = 290; + _beadState1.unk9 = 40; + _beadStateVar = 5; + } else { + _beadStateVar = 4; + _beadState1.unk8 = _currentCharacter->x1 - 4; + _beadState1.unk9 = _currentCharacter->y1 - 30; + } + + if (_text->printed()) + _text->restoreTalkTextMessageBkgd(2, 0); + + initBeadState(_beadState1.x, _beadState1.y, _beadState1.unk8, _beadState1.unk9, 12, &_beadState2); + _lastDisplayedPanPage = 18; + } + } + break; + + case 4: + if (_system->getMillis() >= _beadStateTimer1) { + int x = 0, y = 0; + _beadStateTimer1 = _system->getMillis() + _tickLength; + if (processBead(_beadState1.x, _beadState1.y, x, y, &_beadState2)) { + if (_brandonStatusBit & 20) { + _unkEndSeqVar5 = 2; + _beadStateVar = 6; + } else { + snd_playWanderScoreViaMap(52, 1); + snd_playSoundEffect(0x0C); + _unkEndSeqVar5 = 1; + _beadStateVar = 0; + } + } else { + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + _beadState1.x = x; + _beadState1.y = y; + _screen->copyRegionToBuffer(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0); + if (_lastDisplayedPanPage > 17) { + _lastDisplayedPanPage = 0; + } + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + } + } + break; + + case 5: + if (_system->getMillis() >= _beadStateTimer1) { + _beadStateTimer1 = _system->getMillis() + _tickLength; + int x = 0, y = 0; + if (processBead(_beadState1.x, _beadState1.y, x, y, &_beadState2)) { + if (_beadState2.dstX == 290) { + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + uint32 nextRun = 0; + for (int i = 0; i < 8; ++i) { + nextRun = _system->getMillis() + _tickLength; + _finalB->displayFrame(i, 0, 224, 8, 0, 0, 0); + _screen->updateScreen(); + delayUntil(nextRun); + } + snd_playSoundEffect(0x0D); + for (int i = 7; i >= 0; --i) { + nextRun = _system->getMillis() + _tickLength; + _finalB->displayFrame(i, 0, 224, 8, 0, 0, 0); + _screen->updateScreen(); + delayUntil(nextRun); + } + initBeadState(_beadState1.x, _beadState1.y, 63, 60, 12, &_beadState2); + } else { + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + _beadState1.x = -1; + _beadState1.tableIndex = 0; + _beadStateVar = 0; + _malcolmFlag = 9; + } + } else { + _screen->copyBlockToPage(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + _beadState1.x = x; + _beadState1.y = y; + _screen->copyRegionToBuffer(_screen->_curPage, _beadState1.x, _beadState1.y, _beadState1.width << 3, _beadState1.height, _endSequenceBackUpRect); + _screen->drawShape(2, _panPagesTable[_lastDisplayedPanPage++], x, y, 0, 0); + if (_lastDisplayedPanPage > 17) + _lastDisplayedPanPage = 0; + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + } + } + break; + + case 6: + _screen->drawShape(2, _panPagesTable[19], _beadState1.x, _beadState1.y, 0, 0); + _screen->addBitBlitRect(_beadState1.x, _beadState1.y, _beadState1.width2, _beadState1.height); + _beadStateVar = 0; + break; + + default: + break; + } + + return 0; +} + +void KyraEngine_LoK::initBeadState(int x, int y, int x2, int y2, int unk, BeadState *ptr) { + ptr->unk9 = unk; + int xDiff = x2 - x; + int yDiff = y2 - y; + int unk1 = 0, unk2 = 0; + if (xDiff > 0) + unk1 = 1; + else if (xDiff == 0) + unk1 = 0; + else + unk1 = -1; + + + if (yDiff > 0) + unk2 = 1; + else if (yDiff == 0) + unk2 = 0; + else + unk2 = -1; + + xDiff = ABS(xDiff); + yDiff = ABS(yDiff); + + ptr->y = 0; + ptr->x = 0; + ptr->width = xDiff; + ptr->height = yDiff; + ptr->dstX = x2; + ptr->dstY = y2; + ptr->width2 = unk1; + ptr->unk8 = unk2; +} + +int KyraEngine_LoK::processBead(int x, int y, int &x2, int &y2, BeadState *ptr) { + if (x == ptr->dstX && y == ptr->dstY) + return 1; + + int xPos = x, yPos = y; + if (ptr->width >= ptr->height) { + for (int i = 0; i < ptr->unk9; ++i) { + ptr->y += ptr->height; + if (ptr->y >= ptr->width) { + ptr->y -= ptr->width; + yPos += ptr->unk8; + } + xPos += ptr->width2; + } + } else { + for (int i = 0; i < ptr->unk9; ++i) { + ptr->x += ptr->width; + if (ptr->x >= ptr->height) { + ptr->x -= ptr->height; + xPos += ptr->width2; + } + yPos += ptr->unk8; + } + } + + int temp = ABS(x - ptr->dstX); + if (ptr->unk9 > temp) + xPos = ptr->dstX; + temp = ABS(y - ptr->dstY); + if (ptr->unk9 > temp) + yPos = ptr->dstY; + x2 = xPos; + y2 = yPos; + return 0; +} + +void KyraEngine_LoK::setupPanPages() { + _screen->savePageToDisk("BKGD.PG", 2); + _screen->loadBitmap("BEAD.CPS", 3, 3, 0); + if (_flags.platform == Common::kPlatformMacintosh || _flags.platform == Common::kPlatformAmiga) { + int pageBackUp = _screen->_curPage; + _screen->_curPage = 2; + + delete[] _panPagesTable[19]; + _panPagesTable[19] = _screen->encodeShape(0, 0, 16, 9, 0); + assert(_panPagesTable[19]); + + int curX = 16; + for (int i = 0; i < 19; ++i) { + delete[] _panPagesTable[i]; + _panPagesTable[i] = _screen->encodeShape(curX, 0, 8, 5, 0); + assert(_panPagesTable[i]); + curX += 8; + } + + _screen->_curPage = pageBackUp; + } else { + for (int i = 0; i <= 19; ++i) { + delete[] _panPagesTable[i]; + _panPagesTable[i] = _seq->setPanPages(3, i); + assert(_panPagesTable[i]); + } + } + _screen->loadPageFromDisk("BKGD.PG", 2); +} + +void KyraEngine_LoK::freePanPages() { + delete[] _endSequenceBackUpRect; + _endSequenceBackUpRect = 0; + for (int i = 0; i <= 19; ++i) { + delete[] _panPagesTable[i]; + _panPagesTable[i] = 0; + } +} + +void KyraEngine_LoK::closeFinalWsa() { + delete _finalA; + _finalA = 0; + delete _finalB; + _finalB = 0; + delete _finalC; + _finalC = 0; + freePanPages(); + _endSequenceNeedLoading = 1; +} + +void KyraEngine_LoK::updateKyragemFading() { + if (_flags.platform == Common::kPlatformAmiga) { + // The AMIGA version seems to have no fading for the Kyragem. The code does not + // alter the screen palette. + // + // TODO: Check this in the original. + return; + } + + static const uint8 kyraGemPalette[0x28] = { + 0x3F, 0x3B, 0x38, 0x34, 0x32, 0x2F, 0x2C, 0x29, 0x25, 0x22, + 0x1F, 0x1C, 0x19, 0x16, 0x12, 0x0F, 0x0C, 0x0A, 0x06, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + if (_system->getMillis() < _kyragemFadingState.timerCount) + return; + + _kyragemFadingState.timerCount = _system->getMillis() + 4 * _tickLength; + + int palPos = 684; + for (int i = 0; i < 20; ++i) { + _screen->getPalette(0)[palPos++] = kyraGemPalette[i + _kyragemFadingState.rOffset]; + _screen->getPalette(0)[palPos++] = kyraGemPalette[i + _kyragemFadingState.gOffset]; + _screen->getPalette(0)[palPos++] = kyraGemPalette[i + _kyragemFadingState.bOffset]; + } + + _screen->setScreenPalette(_screen->getPalette(0)); + + switch (_kyragemFadingState.nextOperation) { + case 0: + --_kyragemFadingState.bOffset; + if (_kyragemFadingState.bOffset >= 1) + return; + _kyragemFadingState.nextOperation = 1; + break; + + case 1: + ++_kyragemFadingState.rOffset; + if (_kyragemFadingState.rOffset < 19) + return; + _kyragemFadingState.nextOperation = 2; + break; + + case 2: + --_kyragemFadingState.gOffset; + if (_kyragemFadingState.gOffset >= 1) + return; + _kyragemFadingState.nextOperation = 3; + break; + + case 3: + ++_kyragemFadingState.bOffset; + if (_kyragemFadingState.bOffset < 19) + return; + _kyragemFadingState.nextOperation = 4; + break; + + case 4: + --_kyragemFadingState.rOffset; + if (_kyragemFadingState.rOffset >= 1) + return; + _kyragemFadingState.nextOperation = 5; + break; + + case 5: + ++_kyragemFadingState.gOffset; + if (_kyragemFadingState.gOffset < 19) + return; + _kyragemFadingState.nextOperation = 0; + break; + + default: + break; + } + + _kyragemFadingState.timerCount = _system->getMillis() + 120 * _tickLength; +} + +void KyraEngine_LoK::drawJewelPress(int jewel, int drawSpecial) { + _screen->hideMouse(); + int shape = 0; + + if (drawSpecial) + shape = 0x14E; + else + shape = jewel + 0x149; + + snd_playSoundEffect(0x45); + _screen->drawShape(0, _shapes[shape], _amuletX2[jewel], _amuletY2[jewel], 0, 0); + _screen->updateScreen(); + delayWithTicks(2); + + if (drawSpecial) + shape = 0x148; + else + shape = jewel + 0x143; + + _screen->drawShape(0, _shapes[shape], _amuletX2[jewel], _amuletY2[jewel], 0, 0); + _screen->updateScreen(); + _screen->showMouse(); +} + +void KyraEngine_LoK::drawJewelsFadeOutStart() { + static const uint16 jewelTable1[] = { 0x164, 0x15F, 0x15A, 0x155, 0x150, 0xFFFF }; + static const uint16 jewelTable2[] = { 0x163, 0x15E, 0x159, 0x154, 0x14F, 0xFFFF }; + static const uint16 jewelTable3[] = { 0x166, 0x160, 0x15C, 0x157, 0x152, 0xFFFF }; + static const uint16 jewelTable4[] = { 0x165, 0x161, 0x15B, 0x156, 0x151, 0xFFFF }; + for (int i = 0; jewelTable1[i] != 0xFFFF; ++i) { + if (queryGameFlag(0x57)) + _screen->drawShape(0, _shapes[jewelTable1[i]], _amuletX2[2], _amuletY2[2], 0, 0); + if (queryGameFlag(0x59)) + _screen->drawShape(0, _shapes[jewelTable3[i]], _amuletX2[4], _amuletY2[4], 0, 0); + if (queryGameFlag(0x56)) + _screen->drawShape(0, _shapes[jewelTable2[i]], _amuletX2[1], _amuletY2[1], 0, 0); + if (queryGameFlag(0x58)) + _screen->drawShape(0, _shapes[jewelTable4[i]], _amuletX2[3], _amuletY2[3], 0, 0); + _screen->updateScreen(); + delayWithTicks(3); + } +} + +void KyraEngine_LoK::drawJewelsFadeOutEnd(int jewel) { + static const uint16 jewelTable[] = { 0x153, 0x158, 0x15D, 0x162, 0x148, 0xFFFF }; + int newDelay = 0; + + switch (jewel - 1) { + case 2: + if (_currentCharacter->sceneId >= 109 && _currentCharacter->sceneId <= 198) + newDelay = 18900; + else + newDelay = 8100; + break; + + default: + newDelay = 3600; + } + + setGameFlag(0xF1); + _timer->setCountdown(19, newDelay); + _screen->hideMouse(); + for (int i = 0; jewelTable[i] != 0xFFFF; ++i) { + uint16 shape = jewelTable[i]; + if (queryGameFlag(0x57)) + _screen->drawShape(0, _shapes[shape], _amuletX2[2], _amuletY2[2], 0, 0); + if (queryGameFlag(0x59)) + _screen->drawShape(0, _shapes[shape], _amuletX2[4], _amuletY2[4], 0, 0); + if (queryGameFlag(0x56)) + _screen->drawShape(0, _shapes[shape], _amuletX2[1], _amuletY2[1], 0, 0); + if (queryGameFlag(0x58)) + _screen->drawShape(0, _shapes[shape], _amuletX2[3], _amuletY2[3], 0, 0); + + _screen->updateScreen(); + delayWithTicks(3); + } + _screen->showMouse(); +} + +} // End of namespace Kyra diff --git a/engines/kyra/sequence/sequences_lol.cpp b/engines/kyra/sequence/sequences_lol.cpp new file mode 100644 index 0000000000..55c0eb1493 --- /dev/null +++ b/engines/kyra/sequence/sequences_lol.cpp @@ -0,0 +1,1538 @@ +/* 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. + * + */ + +#ifdef ENABLE_LOL + +#include "kyra/engine/lol.h" +#include "kyra/graphics/screen_lol.h" +#include "kyra/resource/resource.h" +#include "kyra/sound/sound.h" + +#include "base/version.h" + +#include "common/system.h" + +namespace Kyra { + +#pragma mark - Intro + +int LoLEngine::processPrologue() { + // There are two non-interactive demos (one which plays the intro and another one) which plays a number of specific scenes. + // We try to identify the latter one by looking for a specific file. + _res->loadPakFile("GENERAL.PAK"); + if (_flags.isDemo && _res->exists("scene1.cps")) { + return playDemo(); + } else { + setupPrologueData(true); + if (!saveFileLoadable(0) || _flags.isDemo) + showIntro(); + } + + if (_flags.isDemo) { + _screen->fadePalette(_screen->getPalette(1), 30, 0); + _screen->loadBitmap("FINAL.CPS", 2, 2, &_screen->getPalette(0)); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->fadePalette(_screen->getPalette(0), 30, 0); + delayWithTicks(300); + _screen->fadePalette(_screen->getPalette(1), 60, 0); + + setupPrologueData(false); + return -1; + } + + preInit(); + + Common::String versionString(Common::String::format("ScummVM %s", gScummVMVersion)); + + int processSelection = -1; + while (!shouldQuit() && processSelection == -1) { + _screen->loadBitmap("TITLE.CPS", 2, 2, &_screen->getPalette(0)); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + + _screen->setFont(Screen::FID_6_FNT); + // Original version: (260|193) "V CD1.02 D" + const int width = _screen->getTextWidth(versionString.c_str()); + _screen->fprintString("%s", 320 - width, 193, 0x67, 0x00, 0x04, versionString.c_str()); + _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); + + _screen->fadePalette(_screen->getPalette(0), 0x1E); + _screen->updateScreen(); + + _eventList.clear(); + int selection = mainMenu(); + + if (selection != 3) { + _screen->hideMouse(); + + // Unlike the original, we add a nice fade to black + _screen->getPalette(0).clear(); + _screen->fadeToBlack(0x54); + } + + switch (selection) { + case -1: + // This is sent on RTL for example, if we would not have any + // special case for this the default path would call quitGame + // and thus make the next game launched from the launcher + // quit instantly. + break; + + case 0: // New game + processSelection = 0; + break; + + case 1: // Show intro + showIntro(); + break; + + case 2: { // "Lore of the Lands" (only CD version) + HistoryPlayer history(this); + history.play(); + } break; + + case 3: // Load game + if (_gui->runMenu(_gui->_loadMenu)) + processSelection = 3; + break; + + case 4: // Quit game + default: + quitGame(); + updateInput(); + } + } + + if (processSelection == 0) { + _sound->loadSoundFile(0); + _sound->playTrack(6); + chooseCharacter(); + _sound->playTrack(1); + _screen->fadeToBlack(); + } + + setupPrologueData(false); + + return processSelection; +} + +void LoLEngine::setupPrologueData(bool load) { + static const char *const fileListCD[] = { + "GENERAL.PAK", "INTROVOC.PAK", "STARTUP.PAK", "INTRO1.PAK", + "INTRO2.PAK", "INTRO3.PAK", "INTRO4.PAK", "INTRO5.PAK", + "INTRO6.PAK", "INTRO7.PAK", "INTRO8.PAK", "INTRO9.PAK", + "HISTORY.PAK", 0 + }; + + static const char *const fileListFloppy[] = { + "INTRO.PAK", "INTROVOC.PAK", 0 + }; + + static const char *const fileListTowns[] = { + "INTRO.PAK", "TINTROVO.PAK", 0 + }; + + const char *const *fileList = _flags.isTalkie ? fileListCD : (_flags.platform == Common::kPlatformFMTowns ? fileListTowns : fileListFloppy); + + char filename[32]; + for (uint i = 0; fileList[i]; ++i) { + filename[0] = '\0'; + + if (_flags.isTalkie) { + strcpy(filename, _languageExt[_lang]); + strcat(filename, "/"); + } + + strcat(filename, fileList[i]); + + if (load) { + if (!_res->loadPakFile(filename)) + error("Couldn't load file: '%s'", filename); + } else { + _res->unloadPakFile(filename); + } + } + + _screen->clearPage(0); + _screen->clearPage(3); + + if (load) { + _chargenWSA = new WSAMovie_v2(this); + assert(_chargenWSA); + + //_charSelection = -1; + _charSelectionInfoResult = -1; + + _selectionAnimFrames[0] = _selectionAnimFrames[2] = 0; + _selectionAnimFrames[1] = _selectionAnimFrames[3] = 1; + + memset(_selectionAnimTimers, 0, sizeof(_selectionAnimTimers)); + _screen->getPalette(1).clear(); + + _sound->selectAudioResourceSet(kMusicIntro); + + // We have three sound.dat files, one for the intro, one for the + // end sequence and one for ingame, each contained in a different + // PAK file. Therefore a new call to loadSoundFile() is required + // whenever the PAK file configuration changes. + if (_flags.platform == Common::kPlatformPC98) + _sound->loadSoundFile("SOUND.DAT"); + + if (_flags.isDemo) + _sound->loadSoundFile("LOREINTR"); + } else { + delete _chargenWSA; _chargenWSA = 0; + + _screen->getPalette(0).clear(); + _screen->setScreenPalette(_screen->getPalette(0)); + + if (shouldQuit()) + return; + + _eventList.clear(); + _sound->selectAudioResourceSet(kMusicIntro); + } +} + +void LoLEngine::showIntro() { + _tim = new TIMInterpreter(this, _screen, _system); + assert(_tim); + + if (_flags.platform == Common::kPlatformPC98) + showStarcraftLogo(); + + _screen->getPalette(0).clear(); + _screen->setScreenPalette(_screen->getPalette(0)); + + _screen->clearPage(0); + _screen->clearPage(4); + _screen->clearPage(8); + + TIM *intro = _tim->load("LOLINTRO.TIM", &_timIntroOpcodes); + + _screen->loadFont(Screen::FID_8_FNT, "NEW8P.FNT"); + _screen->loadFont(Screen::FID_INTRO_FNT, "INTRO.FNT"); + _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT); + + _tim->resetFinishedFlag(); + _tim->setLangData("LOLINTRO.DIP"); + + _screen->hideMouse(); + + uint32 palNextFadeStep = 0; + while (!_tim->finished() && !shouldQuit() && !skipFlag()) { + updateInput(); + _tim->exec(intro, false); + if (!_flags.isDemo && _flags.platform != Common::kPlatformPC98) + _screen->checkedPageUpdate(8, 4); + + if (_tim->_palDiff) { + if (palNextFadeStep < _system->getMillis()) { + _tim->_palDelayAcc += _tim->_palDelayInc; + palNextFadeStep = _system->getMillis() + ((_tim->_palDelayAcc >> 8) * _tickLength); + _tim->_palDelayAcc &= 0xFF; + + if (!_screen->fadePalStep(_screen->getPalette(0), _tim->_palDiff)) { + _screen->setScreenPalette(_screen->getPalette(0)); + _tim->_palDiff = 0; + } + } + } + + _system->delayMillis(10); + _screen->updateScreen(); + } + _screen->showMouse(); + _sound->voiceStop(); + _sound->beginFadeOut(); + + _eventList.clear(); + + _tim->unload(intro); + _tim->clearLangData(); + + for (int i = 0; i < TIM::kWSASlots; i++) + _tim->freeAnimStruct(i); + + delete _tim; + _tim = 0; + + _screen->fadePalette(_screen->getPalette(1), 30, 0); +} + +int LoLEngine::chooseCharacter() { + _tim = new TIMInterpreter(this, _screen, _system); + assert(_tim); + + _tim->setLangData("LOLINTRO.DIP"); + + _screen->loadFont(Screen::FID_9_FNT, "FONT9P.FNT"); + + _screen->loadBitmap("ITEMICN.SHP", 3, 3, 0); + _screen->setMouseCursor(0, 0, _screen->getPtrToShape(_screen->getCPagePtr(3), 0)); + + while (!_screen->isMouseVisible()) + _screen->showMouse(); + + _screen->loadBitmap("CHAR.CPS", 2, 2, &_screen->getPalette(0)); + _screen->loadBitmap("BACKGRND.CPS", 4, 4, &_screen->getPalette(0)); + + if (!_chargenWSA->open("CHARGEN.WSA", 1, 0)) + error("Couldn't load CHARGEN.WSA"); + + _chargenWSA->displayFrame(0, 2, 113, 0, 0, 0, 0); + + _screen->setFont((_flags.lang == Common::JA_JPN && _flags.use16ColorMode) ? Screen::FID_SJIS_FNT : Screen::FID_9_FNT); + _screen->_curPage = 2; + + if (_flags.platform == Common::kPlatformPC98 && _flags.use16ColorMode) { + _screen->fillRect(17, 29, 94, 97, 17); + _screen->fillRect(68, 167, 310, 199, 17); + _screen->drawClippedLine(68, 166, 311, 166, 238); + _screen->drawClippedLine(68, 166, 68, 199, 238); + _screen->drawClippedLine(311, 166, 311, 199, 238); + + _screen->_curPage = 4; + _screen->fillRect(17, 29, 94, 97, 17); + _screen->_curPage = 2; + + for (int i = 0; i < 4; ++i) { + _screen->printText(_charNamesJapanese[i], _charPosXPC98[i], 168, 0xC1, 0x00); + + Screen::FontId old = _screen->setFont(Screen::FID_SJIS_FNT); + for (int j = 0; j < 3; ++j) { + Common::String attribString = Common::String::format("%2d", _charPreviews[i].attrib[j]); + _screen->printText(attribString.c_str(), _charPosXPC98[i] + 16, 176 + j * 8, 0x81, 0x00); + } + _screen->setFont(old); + } + + _screen->printText(_tim->getCTableEntry(51), 72, 176, 0x81, 0x00); + _screen->printText(_tim->getCTableEntry(53), 72, 184, 0x81, 0x00); + _screen->printText(_tim->getCTableEntry(55), 72, 192, 0x81, 0x00); + } else { + const char *const *previewNames = (_flags.lang == Common::RU_RUS && !_flags.isTalkie) ? _charPreviewNamesRussianFloppy : (_flags.lang == Common::JA_JPN ? _charNamesJapanese : _charPreviewNamesDefault); + for (int i = 0; i < 4; ++i) { + _screen->fprintStringIntro("%s", _charPreviews[i].x + 16, _charPreviews[i].y + 36, 0xC0, 0x00, 0x9C, 0x120, previewNames[i]); + _screen->fprintStringIntro("%d", _charPreviews[i].x + 21, _charPreviews[i].y + 48, 0x98, 0x00, 0x9C, 0x220, _charPreviews[i].attrib[0]); + _screen->fprintStringIntro("%d", _charPreviews[i].x + 21, _charPreviews[i].y + 56, 0x98, 0x00, 0x9C, 0x220, _charPreviews[i].attrib[1]); + _screen->fprintStringIntro("%d", _charPreviews[i].x + 21, _charPreviews[i].y + 64, 0x98, 0x00, 0x9C, 0x220, _charPreviews[i].attrib[2]); + } + + _screen->fprintStringIntro("%s", 36, 173, 0x98, 0x00, 0x9C, 0x20, _tim->getCTableEntry(51)); + _screen->fprintStringIntro("%s", 36, 181, 0x98, 0x00, 0x9C, 0x20, _tim->getCTableEntry(53)); + _screen->fprintStringIntro("%s", 36, 189, 0x98, 0x00, 0x9C, 0x20, _tim->getCTableEntry(55)); + } + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->_curPage = 0; + + if (_flags.use16ColorMode) + _screen->loadPalette("LOL.NOL", _screen->getPalette(0)); + + _screen->fadePalette(_screen->getPalette(0), 30, 0); + + bool kingIntro = true; + while (!shouldQuit()) { + if (kingIntro) + kingSelectionIntro(); + + if (_charSelection < 0) + processCharacterSelection(); + + if (shouldQuit()) + break; + + if (_charSelection == 100) { + kingIntro = true; + _charSelection = -1; + continue; + } + + _screen->copyRegion(0, 0, 0, 0, 112, 120, 4, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _screen->showMouse(); + + if (selectionCharInfo(_charSelection) == -1) { + _charSelection = -1; + kingIntro = false; + } else { + break; + } + + delay(10); + } + + if (shouldQuit()) + return -1; + + uint32 waitTime = _system->getMillis() + 420 * _tickLength; + while (waitTime > _system->getMillis() && !skipFlag() && !shouldQuit()) { + updateInput(); + _system->delayMillis(10); + } + + // HACK: Remove all input events + _eventList.clear(); + + _tim->clearLangData(); + + delete _tim; + _tim = 0; + + return _charSelection; +} + +void LoLEngine::kingSelectionIntro() { + _screen->copyRegion(0, 0, 0, 0, 112, 120, 4, 0, Screen::CR_NO_P_CHECK); + int y = 38; + + if (_flags.platform == Common::kPlatformPC98) { + for (int i = 0; i < 5; ++i) + _screen->printText(_tim->getCTableEntry(57 + i), 16, 32 + i * 8, 0xC1, 0x00); + } else { + for (int i = 0; i < 5; ++i) + _screen->fprintStringIntro("%s", 8, y + i * 10, 0x32, 0x00, 0x9C, 0x20, _tim->getCTableEntry(57 + i)); + } + + if (_flags.isTalkie) + _sound->voicePlay("KING01", &_speechHandle); + + int index = 4; + while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && _charSelection == -1 && !shouldQuit() && !skipFlag()) { + index = MAX(index, 4); + + _chargenWSA->displayFrame(_chargenFrameTable[index], 0, 113, 0, 0, 0, 0); + _screen->copyRegion(_selectionPosTable[_selectionChar1IdxTable[index] * 2 + 0], _selectionPosTable[_selectionChar1IdxTable[index] * 2 + 1], _charPreviews[0].x, _charPreviews[0].y, 32, 32, 4, 0); + _screen->copyRegion(_selectionPosTable[_selectionChar2IdxTable[index] * 2 + 0], _selectionPosTable[_selectionChar2IdxTable[index] * 2 + 1], _charPreviews[1].x, _charPreviews[1].y, 32, 32, 4, 0); + _screen->copyRegion(_selectionPosTable[_selectionChar3IdxTable[index] * 2 + 0], _selectionPosTable[_selectionChar3IdxTable[index] * 2 + 1], _charPreviews[2].x, _charPreviews[2].y, 32, 32, 4, 0); + _screen->copyRegion(_selectionPosTable[_selectionChar4IdxTable[index] * 2 + 0], _selectionPosTable[_selectionChar4IdxTable[index] * 2 + 1], _charPreviews[3].x, _charPreviews[3].y, 32, 32, 4, 0); + _screen->updateScreen(); + + uint32 waitEnd = _system->getMillis() + 7 * _tickLength; + while (waitEnd > _system->getMillis() && _charSelection == -1 && !shouldQuit() && !skipFlag()) { + _charSelection = getCharSelection(); + _system->delayMillis(10); + } + + if (speechEnabled()) + index = (index + 1) % 22; + else if (++index >= 27) + break; + } + + resetSkipFlag(); + + _chargenWSA->displayFrame(0x10, 0, 113, 0, 0, 0, 0); + _screen->updateScreen(); + _sound->voiceStop(&_speechHandle); +} + +void LoLEngine::kingSelectionReminder() { + _screen->copyRegion(0, 0, 0, 0, 112, 120, 4, 0, Screen::CR_NO_P_CHECK); + int y = 48; + + if (_flags.platform == Common::kPlatformPC98) { + _screen->printText(_tim->getCTableEntry(62), 16, 32, 0xC1, 0x00); + _screen->printText(_tim->getCTableEntry(63), 16, 40, 0xC1, 0x00); + } else { + _screen->fprintStringIntro("%s", 8, y, 0x32, 0x00, 0x9C, 0x20, _tim->getCTableEntry(62)); + _screen->fprintStringIntro("%s", 8, y + 10, 0x32, 0x00, 0x9C, 0x20, _tim->getCTableEntry(63)); + } + + if (_flags.isTalkie) + _sound->voicePlay("KING02", &_speechHandle); + + int index = 0; + while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && _charSelection == -1 && !shouldQuit() && index < 15) { + _chargenWSA->displayFrame(_chargenFrameTable[index + 9], 0, 113, 0, 0, 0, 0); + _screen->copyRegion(_selectionPosTable[_reminderChar1IdxTable[index] * 2 + 0], _selectionPosTable[_reminderChar1IdxTable[index] * 2 + 1], _charPreviews[0].x, _charPreviews[0].y, 32, 32, 4, 0); + _screen->copyRegion(_selectionPosTable[_reminderChar2IdxTable[index] * 2 + 0], _selectionPosTable[_reminderChar2IdxTable[index] * 2 + 1], _charPreviews[1].x, _charPreviews[1].y, 32, 32, 4, 0); + _screen->copyRegion(_selectionPosTable[_reminderChar3IdxTable[index] * 2 + 0], _selectionPosTable[_reminderChar3IdxTable[index] * 2 + 1], _charPreviews[2].x, _charPreviews[2].y, 32, 32, 4, 0); + _screen->copyRegion(_selectionPosTable[_reminderChar4IdxTable[index] * 2 + 0], _selectionPosTable[_reminderChar4IdxTable[index] * 2 + 1], _charPreviews[3].x, _charPreviews[3].y, 32, 32, 4, 0); + _screen->updateScreen(); + + uint32 waitEnd = _system->getMillis() + 8 * _tickLength; + while (waitEnd > _system->getMillis() && !shouldQuit()) { + _charSelection = getCharSelection(); + _system->delayMillis(10); + } + + if (speechEnabled()) + index = (index + 1) % 22; + else if (++index >= 27) + break; + } + + _sound->voiceStop(&_speechHandle); +} + +void LoLEngine::kingSelectionOutro() { + if (_flags.isTalkie) + _sound->voicePlay("KING03", &_speechHandle); + + int index = 0; + while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && !shouldQuit() && !skipFlag()) { + index = MAX(index, 4); + + _chargenWSA->displayFrame(_chargenFrameTable[index], 0, 113, 0, 0, 0, 0); + _screen->updateScreen(); + + uint32 waitEnd = _system->getMillis() + 8 * _tickLength; + while (waitEnd > _system->getMillis() && !shouldQuit() && !skipFlag()) { + updateInput(); + _system->delayMillis(10); + } + + if (speechEnabled()) + index = (index + 1) % 22; + else if (++index >= 27) + break; + } + + resetSkipFlag(); + + _chargenWSA->displayFrame(0x10, 0, 113, 0, 0, 0, 0); + _screen->updateScreen(); + _sound->voiceStop(&_speechHandle); +} + +void LoLEngine::processCharacterSelection() { + _charSelection = -1; + while (!shouldQuit() && _charSelection == -1) { + uint32 nextKingMessage = _system->getMillis() + 900 * _tickLength; + + while (nextKingMessage > _system->getMillis() && _charSelection == -1 && !shouldQuit()) { + updateSelectionAnims(); + _charSelection = getCharSelection(); + _system->delayMillis(10); + } + + if (_charSelection == -1) + kingSelectionReminder(); + } +} + +void LoLEngine::updateSelectionAnims() { + for (int i = 0; i < 4; ++i) { + if (_system->getMillis() < _selectionAnimTimers[i]) + continue; + + const int index = _selectionAnimIndexTable[_selectionAnimFrames[i] + i * 2]; + _screen->copyRegion(_selectionPosTable[index * 2 + 0], _selectionPosTable[index * 2 + 1], _charPreviews[i].x, _charPreviews[i].y, 32, 32, 4, 0); + + int delayTime = 0; + if (_selectionAnimFrames[i] == 1) + delayTime = _rnd.getRandomNumberRng(0, 31) + 80; + else + delayTime = _rnd.getRandomNumberRng(0, 3) + 10; + + _selectionAnimTimers[i] = _system->getMillis() + delayTime * _tickLength; + _selectionAnimFrames[i] = (_selectionAnimFrames[i] + 1) % 2; + } + + _screen->updateScreen(); +} + +int LoLEngine::selectionCharInfo(int character) { + if (character < 0) + return -1; + + char filename[16]; + char vocFilename[6]; + strcpy(vocFilename, "000X0"); + + switch (character) { + case 0: + strcpy(filename, "FACE09.SHP"); + vocFilename[3] = 'A'; + break; + + case 1: + strcpy(filename, "FACE01.SHP"); + vocFilename[3] = 'M'; + break; + + case 2: + strcpy(filename, "FACE08.SHP"); + vocFilename[3] = 'K'; + break; + + case 3: + strcpy(filename, "FACE05.SHP"); + vocFilename[3] = 'C'; + break; + + default: + break; + } + + _screen->loadBitmap(filename, 9, 9, 0); + _screen->copyRegion(0, 122, 0, 122, 320, 78, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(_charPreviews[character].x - 3, _charPreviews[character].y - 3, 8, 127, 38, 38, 2, 0); + + static const uint8 charSelectInfoIdx[] = { 0x1D, 0x22, 0x27, 0x2C }; + const int idx = charSelectInfoIdx[character]; + + if (_flags.platform == Common::kPlatformPC98) { + for (int i = 0; i < 5; ++i) + _screen->printText(_tim->getCTableEntry(idx + i), 60, 128 + i * 8, 0x41, 0x00); + + _screen->printText(_tim->getCTableEntry(69), 112, 168, 0x01, 0x00); + } else { + for (int i = 0; i < 5; ++i) + _screen->fprintStringIntro("%s", 50, 127 + i * 10, 0x53, 0x00, 0xCF, 0x20, _tim->getCTableEntry(idx + i)); + + _screen->fprintStringIntro("%s", 100, 168, 0x32, 0x00, 0xCF, 0x20, _tim->getCTableEntry(69)); + } + + selectionCharInfoIntro(vocFilename); + if (_charSelectionInfoResult == -1) { + while (_charSelectionInfoResult == -1 && !shouldQuit()) { + _charSelectionInfoResult = selectionCharAccept(); + _system->delayMillis(10); + } + } + + if (_charSelectionInfoResult != 1) { + _charSelectionInfoResult = -1; + _screen->copyRegion(0, 122, 0, 122, 320, 78, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + return -1; + } + + _screen->copyRegion(48, 127, 48, 127, 272, 60, 4, 0, Screen::CR_NO_P_CHECK); + _screen->hideMouse(); + _screen->copyRegion(48, 127, 48, 160, 272, 35, 4, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 0, 0, 112, 120, 4, 0, Screen::CR_NO_P_CHECK); + + if (_flags.platform == Common::kPlatformPC98) { + for (int i = 0; i < 5; ++i) + _screen->printText(_tim->getCTableEntry(64 + i), 16, 32 + i * 8, 0xC1, 0x00); + } else { + for (int i = 0; i < 5; ++i) + _screen->fprintStringIntro("%s", 3, 28 + i * 10, 0x32, 0x00, 0x9C, 0x20, _tim->getCTableEntry(64 + i)); + } + + resetSkipFlag(); + kingSelectionOutro(); + return character; +} + +void LoLEngine::selectionCharInfoIntro(char *file) { + int index = 0; + file[4] = '0'; + bool processAnim = true; + + while (_charSelectionInfoResult == -1 && !shouldQuit()) { + if (speechEnabled() && !_sound->isVoicePresent(file)) + break; + + if (_flags.isTalkie) + _sound->voicePlay(file, &_speechHandle); + + int i = 0; + while ((!speechEnabled() || (speechEnabled() && _sound->voiceIsPlaying(&_speechHandle))) && _charSelectionInfoResult == -1 && !shouldQuit()) { + _screen->drawShape(0, _screen->getPtrToShape(_screen->getCPagePtr(9), _charInfoFrameTable[i]), 11, 130, 0, 0); + _screen->updateScreen(); + + uint32 nextFrame = _system->getMillis() + 8 * _tickLength; + while (nextFrame > _system->getMillis() && _charSelectionInfoResult == -1 && !shouldQuit()) { + _charSelectionInfoResult = selectionCharAccept(); + _system->delayMillis(10); + } + + if (speechEnabled() || processAnim) + i = (i + 1) % 32; + if (i == 0) + processAnim = false; + } + + _sound->voiceStop(&_speechHandle); + file[4] = ++index + '0'; + } + + _screen->drawShape(0, _screen->getPtrToShape(_screen->getCPagePtr(9), 0), 11, 130, 0, 0); + _screen->updateScreen(); +} + +int LoLEngine::getCharSelection() { + int inputFlag = checkInput(0, false) & 0xCF; + removeInputTop(); + + if (inputFlag == 200) { + for (int i = 0; i < 4; ++i) { + if (_charPreviews[i].x <= _mouseX && _mouseX <= _charPreviews[i].x + 31 && + _charPreviews[i].y <= _mouseY && _mouseY <= _charPreviews[i].y + 31) + return i; + } + } + + return -1; +} + +int LoLEngine::selectionCharAccept() { + int inputFlag = checkInput(0, false) & 0xCF; + removeInputTop(); + + if (inputFlag == 200) { + if (88 <= _mouseX && _mouseX <= 128 && 180 <= _mouseY && _mouseY <= 194) + return 1; + if (196 <= _mouseX && _mouseX <= 236 && 180 <= _mouseY && _mouseY <= 194) + return 0; + } + + return -1; +} + +void LoLEngine::showStarcraftLogo() { + WSAMovie_v2 *ci = new WSAMovie_v2(this); + assert(ci); + + _screen->clearPage(0); + _screen->clearPage(2); + + int endframe = ci->open("ci01.wsa", 0, &_screen->getPalette(0)); + if (!ci->opened()) { + delete ci; + return; + } + _screen->hideMouse(); + ci->displayFrame(0, 2, 32, 80, 0, 0, 0); + _screen->copyPage(2, 0); + _screen->fadeFromBlack(); + int inputFlag = 0; + for (int i = 0; i < endframe; i++) { + inputFlag = checkInput(0) & 0xFF; + if (shouldQuit() || inputFlag) + break; + ci->displayFrame(i, 2, 32, 80, 0, 0, 0); + _screen->copyPage(2, 0); + _screen->updateScreen(); + delay(4 * _tickLength); + } + + if (!(shouldQuit() || inputFlag)) { + _sound->voicePlay("star2", &_speechHandle); + while (_sound->voiceIsPlaying(&_speechHandle) && !(shouldQuit() || inputFlag)) { + inputFlag = checkInput(0) & 0xFF; + delay(_tickLength); + } + } + + _screen->fadeToBlack(); + _screen->showMouse(); + + _eventList.clear(); + delete ci; +} + +// history player + +HistoryPlayer::HistoryPlayer(LoLEngine *vm) : _system(vm->_system), _vm(vm), _screen(vm->screen()) { + _x = _y = _width = _height = 0; + _frame = _fireFrame = 0; + _nextFireTime = 0; + + _wsa = new WSAMovie_v2(vm); + assert(_wsa); + _fireWsa = new WSAMovie_v2(vm); + assert(_fireWsa); +} + +HistoryPlayer::~HistoryPlayer() { + delete _wsa; + delete _fireWsa; +} + +void HistoryPlayer::play() { + int dataSize = 0; + const char *data = (const char *)_vm->staticres()->loadRawData(kLoLHistory, dataSize); + + if (!data) + error("Could not load history data"); + + _screen->loadFont(Screen::FID_9_FNT, "FONT9P.FNT"); + + Palette pal(256); + pal.fill(0, 256, 0); + _screen->fadePalette(pal, 0x1E); + + _screen->loadBitmap("BACKGND.CPS", 8, 8, &pal); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 8, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 8, 2, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + _screen->fadePalette(pal, 0x82); + + _screen->copyRegion(_x, _y, _x, _y, _width, _height, 2, 0); + _screen->updateScreen(); + + pal.fill(0, 256, 0); + _screen->setFont(Screen::FID_9_FNT); + + char tempWsaFilename[16]; + char voiceFilename[13]; + // the 'a' *has* to be lowercase + strcpy(voiceFilename, "PS_1a"); + + int part = 0; + Sound *sound = _vm->sound(); + + Common::Functor0Mem<void, HistoryPlayer> palFade(this, &HistoryPlayer::updateFire); + + for (; voiceFilename[3] <= '9' && !_vm->shouldQuit() && !_vm->skipFlag(); ++voiceFilename[3], voiceFilename[4] = 'a') { + while (!_vm->shouldQuit() && !_vm->skipFlag()) { + if (!sound->isVoicePresent(voiceFilename)) + break; + + if (data[part * 15] == voiceFilename[3] && data[part * 15 + 1] == voiceFilename[4]) { + switch (part) { + case 0: + loadWsa(&data[part * 15 + 2]); + playWsa(true); + sound->voicePlay(voiceFilename); + break; + + case 1: case 2: case 8: + case 16: case 25: + sound->voicePlay(voiceFilename); + playWsa(true); + break; + + case 3: case 7: case 10: + case 17: case 23: case 26: + sound->voicePlay(voiceFilename); + playWsa(true); + restoreWsaBkgd(); + loadWsa(&data[part * 15 + 2]); + playWsa(true); + break; + + case 6: + sound->voicePlay(voiceFilename); + playWsa(false); + restoreWsaBkgd(); + loadWsa(&data[part * 15 + 2]); + playWsa(true); + _vm->delayWithTicks(30); + playWsa(true); + break; + + case 9: + sound->voicePlay(voiceFilename); + loadWsa(&data[part * 15 + 2]); + playWsa(true); + break; + + case 22: + playWsa(false); + restoreWsaBkgd(); + loadWsa(&data[part * 15 + 2]); + _vm->delayWithTicks(30); + sound->voicePlay(voiceFilename); + playWsa(true); + + strcpy(tempWsaFilename, &data[part * 15]); + + for (int i = 1; i < 4 && !_vm->shouldQuit(); ++i) { + uint32 nextTime = _system->getMillis() + 30 * _vm->tickLength(); + tempWsaFilename[8] = 'a' + i; + + loadWsa(&tempWsaFilename[2]); + _vm->delayUntil(nextTime); + + playWsa(true); + } + + tempWsaFilename[8] = 'e'; + loadWsa(&tempWsaFilename[2]); + break; + + case 29: + sound->voicePlay(voiceFilename); + playWsa(false); + restoreWsaBkgd(); + loadWsa(&data[part * 15 + 2]); + + _fireWsa->open("FIRE.WSA", 0, 0); + playWsa(true); + _fireFrame = 0; + + for (int i = 0; i < 12 && !_vm->shouldQuit(); ++i, ++_fireFrame) { + uint32 nextTime = _system->getMillis() + 3 * _vm->tickLength(); + + if (_fireFrame > 4) + _fireFrame = 0; + + _fireWsa->displayFrame(_fireFrame, 0, 75, 51, 0, 0, 0); + _screen->updateScreen(); + _vm->delayUntil(nextTime); + } + + _screen->loadPalette("DRACPAL.PAL", pal); + _screen->fadePalette(pal, 0x78, &palFade); + + while (sound->voiceIsPlaying() && !_vm->shouldQuit()) { + uint32 nextTime = _system->getMillis() + 3 * _vm->tickLength(); + + ++_fireFrame; + if (_fireFrame > 4) + _fireFrame = 0; + + _fireWsa->displayFrame(_fireFrame, 0, 75, 51, 0, 0, 0); + _screen->updateScreen(); + _vm->delayUntil(nextTime); + } + + _fireFrame = 0; + for (int i = 0; i < 10; ++i, ++_fireFrame) { + uint32 nextTime = _system->getMillis() + 3 * _vm->tickLength(); + + if (_fireFrame > 4) + _fireFrame = 0; + + _fireWsa->displayFrame(_fireFrame, 0, 75, 51, 0, 0, 0); + _screen->updateScreen(); + _vm->delayUntil(nextTime); + } + + break; + + default: + sound->voicePlay(voiceFilename); + playWsa(false); + restoreWsaBkgd(); + loadWsa(&data[part * 15 + 2]); + playWsa(true); + break; + } + + ++part; + } else { + sound->voicePlay(voiceFilename); + } + + while (sound->voiceIsPlaying() && !_vm->shouldQuit() && !_vm->skipFlag()) + _vm->delay(10); + + if (_vm->skipFlag()) + sound->voiceStop(); + + ++voiceFilename[4]; + } + } + + if (_vm->skipFlag()) + _vm->_eventList.clear(); + + pal.fill(0, 256, 63); + if (_fireWsa->opened()) + _screen->fadePalette(pal, 0x3C, &palFade); + else + _screen->fadePalette(pal, 0x3C); + + _screen->clearPage(0); + pal.fill(0, 256, 0); + _screen->fadePalette(pal, 0x3C); + + if (_vm->skipFlag()) + _vm->_eventList.clear(); +} + +void HistoryPlayer::loadWsa(const char *filename) { + if (_wsa->opened()) + _wsa->close(); + + Palette pal(256); + if (!_wsa->open(filename, 3, &pal)) + error("Could not load WSA file: '%s'", filename); + _screen->setScreenPalette(pal); + + _x = _wsa->xAdd(); + _y = _wsa->yAdd(); + _width = _wsa->width(); + _height = _wsa->height(); + _frame = 1; +} + +void HistoryPlayer::playWsa(bool direction) { + const int tickLength = _vm->tickLength(); + + for (int i = 0; i < 15 && !_vm->shouldQuit(); ++i) { + uint32 nextTime = _system->getMillis() + 3 * tickLength; + + _wsa->displayFrame(_frame, 2, 0, 0, 0, 0, 0); + _screen->copyRegion(_x, _y, _x, _y, _width, _height, 2, 0); + _screen->updateScreen(); + _vm->delayUntil(nextTime); + + if (direction) + ++_frame; + else + --_frame; + } +} + +void HistoryPlayer::restoreWsaBkgd() { + _screen->copyRegion(_x, _y, _x, _y, _width, _height, 8, 0); + _screen->copyRegion(_x, _y, _x, _y, _width, _height, 8, 2); + _screen->updateScreen(); +} + +void HistoryPlayer::updateFire() { + if (_system->getMillis() > _nextFireTime) { + _fireWsa->displayFrame(_fireFrame, 0, 75, 51, 0, 0, 0); + _fireFrame = (_fireFrame + 1) % 5; + _nextFireTime = _system->getMillis() + 4 * _vm->tickLength(); + } + + _screen->updateScreen(); +} + +// outro + +void LoLEngine::setupEpilogueData(bool load) { + static const char *const fileListCD[] = { + "GENERAL.PAK", "INTROVOC.PAK", "STARTUP.PAK", + "FINALE.PAK", "FINALE1.PAK", "FINALE2.PAK", 0 + }; + + static const char *const fileListFloppy[] = { + "GENERAL.PAK", "INTRO.PAK", "FINALE1.PAK", "FINALE2.PAK", 0 + }; + + static const char *const fileListTowns[] = { + "GENERAL.PAK", "INTRO.PAK", "FINALE1.PAK", "TFINALE2.PAK", 0 + }; + + const char *const *fileList = _flags.isTalkie ? fileListCD : (_flags.platform == Common::kPlatformFMTowns ? fileListTowns : fileListFloppy); + assert(fileList); + + char filename[32]; + for (uint i = 0; fileList[i]; ++i) { + filename[0] = '\0'; + + if (_flags.isTalkie) { + strcpy(filename, _languageExt[_lang]); + strcat(filename, "/"); + } + + strcat(filename, fileList[i]); + + if (load) { + if (!_res->loadPakFile(filename)) + error("Couldn't load file: '%s'", filename); + } else { + _res->unloadPakFile(filename); + } + } + + _screen->clearPage(0); + _screen->clearPage(3); + + if (load) { + _sound->selectAudioResourceSet(kMusicFinale); + + // We have three sound.dat files, one for the intro, one for the + // end sequence and one for ingame, each contained in a different + // PAK file. Therefore a new call to loadSoundFile() is required + // whenever the PAK file configuration changes. + if (_flags.platform == Common::kPlatformPC98) + _sound->loadSoundFile("SOUND.DAT"); + } else { + _screen->getPalette(0).clear(); + _screen->setScreenPalette(_screen->getPalette(0)); + + if (shouldQuit()) + return; + + _eventList.clear(); + _sound->selectAudioResourceSet(kMusicIntro); + } +} + +void LoLEngine::showOutro(int character, bool maxDifficulty) { + setupEpilogueData(true); + TIMInterpreter *timBackUp = _tim; + _tim = new TIMInterpreter(this, _screen, _system); + + _screen->getPalette(0).clear(); + _screen->setScreenPalette(_screen->getPalette(0)); + + _screen->clearPage(0); + _screen->clearPage(4); + _screen->clearPage(8); + + TIM *outro = _tim->load("LOLFINAL.TIM", &_timOutroOpcodes); + assert(outro); + outro->lolCharacter = character; + + _screen->loadFont(Screen::FID_6_FNT, "NEW6P.FNT"); + _screen->loadFont(Screen::FID_INTRO_FNT, "INTRO.FNT"); + + _tim->resetFinishedFlag(); + _tim->setLangData("LOLFINAL.DIP"); + + _screen->hideMouse(); + + uint32 palNextFadeStep = 0; + while (!_tim->finished() && !shouldQuit() && !skipFlag()) { + updateInput(); + _tim->exec(outro, false); + + if (_tim->_palDiff) { + if (palNextFadeStep < _system->getMillis()) { + _tim->_palDelayAcc += _tim->_palDelayInc; + palNextFadeStep = _system->getMillis() + ((_tim->_palDelayAcc >> 8) * _tickLength); + _tim->_palDelayAcc &= 0xFF; + + if (!_screen->fadePalStep(_screen->getPalette(0), _tim->_palDiff)) { + _screen->setScreenPalette(_screen->getPalette(0)); + _tim->_palDiff = 0; + } + } + } + + _system->delayMillis(10); + _screen->updateScreen(); + } + removeInputTop(); + _screen->showMouse(); + _sound->voiceStop(); + _sound->beginFadeOut(); + + _eventList.clear(); + + _tim->unload(outro); + + for (int i = 0; i < TIM::kWSASlots; i++) + _tim->freeAnimStruct(i); + + _screen->fadeToBlack(30); + + if (!shouldQuit()) + showCredits(); + + _eventList.clear(); + + if (!shouldQuit()) { + switch (character) { + case 0: + _screen->loadBitmap("KIERAN.CPS", 3, 3, &_screen->getPalette(0)); + break; + + case 1: + _screen->loadBitmap("AK'SHEL.CPS", 3, 3, &_screen->getPalette(0)); + break; + + case 2: + _screen->loadBitmap("MICHAEL.CPS", 3, 3, &_screen->getPalette(0)); + break; + + case 3: + _screen->loadBitmap("CONRAD.CPS", 3, 3, &_screen->getPalette(0)); + break; + + default: + _screen->clearPage(3); + _screen->getPalette(0).clear(); + } + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + if (maxDifficulty && !_flags.use16ColorMode) + _tim->displayText(0x8000, 0, 0xDC); + _screen->updateScreen(); + _screen->fadePalette(_screen->getPalette(0), 30, 0); + + while (!checkInput(0) && !shouldQuit()) + delay(_tickLength); + + _screen->fadeToBlack(30); + } + + _tim->clearLangData(); + delete _tim; + _tim = timBackUp; + + setupEpilogueData(false); +} + +void LoLEngine::showCredits() { + for (int i = 0; i < 255; ++i) + _outroShapeTable[i] = i; + + if (_flags.use16ColorMode) + for (int i = 1; i < 16; ++i) + _outroShapeTable[i] = (i << 4) | i; + else + _outroShapeTable[255] = 0; + + _sound->haltTrack(); + _sound->loadSoundFile("LOREFINL"); + _sound->playTrack(4); + + _screen->hideMouse(); + + static const uint8 colorMap[] = { 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x6F, 0x6F, 0x6D }; + _screen->_charWidth = 0; + + _screen->loadBitmap("ROOM.CPS", 2, 2, &_screen->getPalette(0)); + + if (!_flags.use16ColorMode) { + _screen->setTextColorMap(colorMap); + _screen->getPalette(0).fill(_screen->getPalette(0).getNumColors() - 1, 1, 0); + } + + _screen->fadeToBlack(30); + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + + _screen->_charOffset = 0; + + char *credits = 0; + + if (_flags.platform == Common::kPlatformPC98) { + int size = 0; + const uint8 *internCredits = _staticres->loadRawData(kLoLCredits, size); + assert(size > 0); + + credits = new char[size]; + assert(credits); + + memcpy(credits, internCredits, size); + _staticres->unloadId(kLoLCredits); + } else { + credits = (char *)_res->fileData("CREDITS.TXT", 0); + } + + processCredits(credits, 21, 4, 5); + delete[] credits; + + uint32 endTime = _system->getMillis() + 120 * _tickLength; + while (endTime > _system->getMillis() && !shouldQuit()) { + if (checkInput(0)) + break; + delay(_tickLength); + } + + _sound->beginFadeOut(); + _screen->fadeToBlack(30); + + _screen->clearCurPage(); + _screen->updateScreen(); + _screen->showMouse(); +} + +void LoLEngine::processCredits(char *t, int dimState, int page, int delayTime) { + if (!t) + return; + + _screen->setScreenDim(dimState); + _screen->clearPage(page); + _screen->clearPage(6); + + _screen->loadBitmap("DOOR.SHP", 5, 5, 0); + uint8 *doorShape = _screen->makeShapeCopy(_screen->getCPagePtr(5), 0); + assert(doorShape); + + _screen->drawShape(0, doorShape, 0, 0, 22, 0x10); + _screen->drawShape(0, doorShape, 0, 0, 23, 0x11); + + int curShapeFile = 0; + uint8 *shapes[12]; + memset(shapes, 0, sizeof(shapes)); + + loadOutroShapes(curShapeFile++, shapes); + uint8 *monsterPal = 0; + + if (_flags.use16ColorMode) { + _screen->loadPalette("LOL.NOL", _screen->getPalette(0)); + } else { + monsterPal = _res->fileData("MONSTERS.PAL", 0); + assert(monsterPal); + _screen->getPalette(0).copy(monsterPal, 0, 40, 88); + } + + _screen->fadePalette(_screen->getPalette(0), 30); + + uint32 waitTimer = _system->getMillis(); + + struct CreditsString { + int16 x, y; + char *str; + uint8 code; + uint8 height; + uint8 alignment; + } strings[37]; + memset(strings, 0, sizeof(strings)); + + int countStrings = 0; + char *str = t; + + int frameCounter = 0; + int monsterAnimFrame = 0; + bool needNewShape = false; + bool doorRedraw = true; + + uint8 *animBlock = new uint8[40960]; + assert(animBlock); + memset(animBlock, 0, 40960); + int inputFlag = 0; + + do { + while (_system->getMillis() < waitTimer && !shouldQuit()) + delay(_tickLength); + waitTimer = _system->getMillis() + delayTime * _tickLength; + + while (countStrings < 35 && str[0]) { + int y = 0; + + if (!countStrings) { + y = _screen->_curDim->h; + } else { + y = strings[countStrings].y + strings[countStrings].height; + y += strings[countStrings].height >> 3; + } + + char *curString = str; + str = (char *)strpbrk(str, "\x05\x0D"); + if (!str) + str = strchr(curString, 0); + + CreditsString &s = strings[countStrings + 1]; + s.code = str[0]; + str[0] = 0; + + if (s.code) + ++str; + + s.alignment = 0; + if (*curString == 3 || *curString == 4) + s.alignment = *curString++; + + _screen->setFont(Screen::FID_6_FNT); + + if (*curString == 1 || *curString == 2) + ++curString; + s.height = _screen->getFontHeight(); + + if (s.alignment == 3) + s.x = 0; + else if (s.alignment == 4) + s.x = 300 - _screen->getTextWidth(curString); + else + s.x = ((_screen->_curDim->w << 3) - _screen->getTextWidth(curString)) / 2; + + if (strings[countStrings].code == 5) + y -= strings[countStrings].height + (strings[countStrings].height >> 3); + + s.y = y; + s.str = curString; + + // WORKAROUND: The original did supply some texts, which wouldn't fit on one line. + // To display them properly, we will break them into two separate entries. The original + // just did not display these lines at all. (At least not in LordHoto's tests with DOSBox). + if (s.x + _screen->getTextWidth(s.str) > Screen::SCREEN_W) { + char *nextLine = 0; + char *lastSeparator = 0; + + int backupX = s.x; + + while (s.x + _screen->getTextWidth(s.str) > Screen::SCREEN_W) { + char *sep = strrchr(s.str, ' '); + + if (lastSeparator) + *lastSeparator = ' '; + + lastSeparator = sep; + + if (lastSeparator) { + *lastSeparator = 0; + nextLine = lastSeparator + 1; + + s.x = MAX(((_screen->_curDim->w << 3) - _screen->getTextWidth(s.str)) / 2, 0); + } else { + // It seems we ca not find any whitespace, thus we are better safe and + // do not break up the line into two parts. (This is just paranoia) + nextLine = 0; + break; + } + } + + s.x = backupX; + + if (nextLine) { + ++countStrings; + + // Center old string + s.alignment = 0; + s.x = ((_screen->_curDim->w << 3) - _screen->getTextWidth(s.str)) / 2; + + // Add new string, also centered + CreditsString &n = strings[countStrings + 1]; + n.y = s.y + s.height + (s.height >> 3); + n.height = s.height; + n.alignment = 0; + n.code = s.code; + n.str = nextLine; + n.x = ((_screen->_curDim->w << 3) - _screen->getTextWidth(n.str)) / 2; + } + } + + ++countStrings; + } + + ++frameCounter; + if (frameCounter % 3) { + _screen->copyRegion(0, 0, 0, 0, 320, 200, 6, page, Screen::CR_NO_P_CHECK); + } else { + if (!monsterAnimFrame && doorRedraw) { + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, page, Screen::CR_NO_P_CHECK); + _screen->drawShape(page, doorShape, 0, 0, 22, 0x10); + _screen->drawShape(page, doorShape, 0, 0, 23, 0x11); + + --frameCounter; + doorRedraw = false; + } else { + if (!monsterAnimFrame) + _screen->setScreenPalette(_screen->getPalette(0)); + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, page, Screen::CR_NO_P_CHECK); + + uint8 *monsterShape = shapes[_outroFrameTable[monsterAnimFrame]]; + + int doorSD = 0; + int doorX = 0, doorY = 0; + int monsterX = 0, monsterY = 0; + + bool isRightMonster = ((curShapeFile - 1) & 1) != 0; + + if (isRightMonster) { + doorSD = 23; + doorX = _outroRightDoorPos[monsterAnimFrame * 2 + 0]; + doorY = _outroRightDoorPos[monsterAnimFrame * 2 + 1]; + + monsterX = _outroRightMonsterPos[monsterAnimFrame * 2 + 0]; + monsterY = _outroRightMonsterPos[monsterAnimFrame * 2 + 1]; + + _screen->drawShape(page, doorShape, 0, 0, 22, 0x10); + } else { + doorSD = 22; + doorX = _outroLeftDoorPos[monsterAnimFrame * 2 + 0]; + doorY = _outroLeftDoorPos[monsterAnimFrame * 2 + 1]; + + monsterX = _outroLeftMonsterPos[monsterAnimFrame * 2 + 0]; + monsterY = _outroLeftMonsterPos[monsterAnimFrame * 2 + 1]; + + _screen->drawShape(page, doorShape, 0, 0, 23, 0x11); + } + + if (monsterAnimFrame >= 8) + _screen->drawShape(page, doorShape, doorX, doorY, doorSD, (doorSD == 22) ? 0 : 1); + + _screen->drawShape(page, monsterShape, monsterX, monsterY, 0, 0x104 | ((!isRightMonster || monsterAnimFrame < 20) ? 0 : 1), _outroShapeTable, 1, _outroMonsterScaleTableX[monsterAnimFrame], _outroMonsterScaleTableY[monsterAnimFrame]); + + if (monsterAnimFrame < 8) + _screen->drawShape(page, doorShape, doorX, doorY, doorSD, (doorSD == 22) ? 0 : 1); + + _screen->copyRegion(0, 0, 0, 0, 320, 200, page, 6, Screen::CR_NO_P_CHECK); + doorRedraw = true; + + monsterAnimFrame = (monsterAnimFrame + 1) % 24; + needNewShape = !monsterAnimFrame; + } + } + + for (int i = 0; i < countStrings; ++i) { + CreditsString &s = strings[i + 1]; + int x = s.x, y = s.y; + + if (y < _screen->_curDim->h) { + _screen->_curPage = page; + _screen->setFont(Screen::FID_6_FNT); + if (_flags.use16ColorMode) { + _screen->printText(s.str, (_screen->_curDim->sx << 3) + x + 1, _screen->_curDim->sy + y + 1, 0x44, 0x00); + _screen->printText(s.str, (_screen->_curDim->sx << 3) + x, _screen->_curDim->sy + y, 0x33, 0x00); + } else { + _screen->printText(s.str, (_screen->_curDim->sx << 3) + x, _screen->_curDim->sy + y, 0xDC, 0x00); + } + _screen->_curPage = 0; + } + + --s.y; + } + + _screen->copyToPage0(_screen->_curDim->sy, _screen->_curDim->h, page, animBlock); + + if (strings[1].y < -10) { + strings[1].str += strlen(strings[1].str); + strings[1].str[0] = strings[1].code; + --countStrings; + memmove(&strings[1], &strings[2], countStrings * sizeof(CreditsString)); + } + + if (needNewShape) { + ++curShapeFile; + if (curShapeFile == 16) + curShapeFile += 2; + if (curShapeFile == 6) + curShapeFile += 2; + curShapeFile = curShapeFile % 28; + + loadOutroShapes(curShapeFile, shapes); + + if (!_flags.use16ColorMode) { + _screen->getPalette(0).copy(monsterPal, curShapeFile * 40, 40, 88); + _screen->setScreenPalette(_screen->getPalette(0)); + } + + needNewShape = false; + } + + _screen->updateScreen(); + inputFlag = checkInput(0); + removeInputTop(); + } while (countStrings && !(inputFlag && !(inputFlag & 0x800)) && !shouldQuit()); + removeInputTop(); + + delete[] animBlock; + delete[] doorShape; + delete[] monsterPal; + for (int i = 0; i < 12; ++i) + delete[] shapes[i]; +} + +void LoLEngine::loadOutroShapes(int file, uint8 **storage) { + _screen->loadBitmap(_outroShapeFileTable[file], 5, 5, 0); + + for (int i = 0; i < 12; ++i) { + delete[] storage[i]; + if (i < 8) + storage[i] = _screen->makeShapeCopy(_screen->getCPagePtr(5), i); + else + storage[i] = _screen->makeShapeCopy(_screen->getCPagePtr(5), i + 4); + } +} + +} // End of namespace Kyra + +#endif // ENABLE_LOL diff --git a/engines/kyra/sequence/sequences_mr.cpp b/engines/kyra/sequence/sequences_mr.cpp new file mode 100644 index 0000000000..abfd0d8cbb --- /dev/null +++ b/engines/kyra/sequence/sequences_mr.cpp @@ -0,0 +1,237 @@ +/* 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 "kyra/engine/kyra_mr.h" +#include "kyra/resource/resource.h" + +namespace Kyra { + +void KyraEngine_MR::showBadConscience() { + if (_badConscienceShown) + return; + + _badConscienceShown = true; + _badConscienceAnim = _rnd.getRandomNumberRng(0, 2); + if (_currentChapter == 2) + _badConscienceAnim = 5; + else if (_currentChapter == 3) + _badConscienceAnim = 3; + else if (_currentChapter == 4 && _rnd.getRandomNumberRng(1, 100) <= 25) + _badConscienceAnim = 6; + else if (_currentChapter == 5 && _rnd.getRandomNumberRng(1, 100) <= 25) + _badConscienceAnim = 7; + + if (_characterShapeFile == 9) + _badConscienceAnim = 4; + + _badConsciencePosition = (_mainCharacter.x1 <= 160); + + if (_goodConscienceShown) + _badConsciencePosition = !_goodConsciencePosition; + + int anim = _badConscienceAnim + (_badConsciencePosition ? 0 : 8); + TalkObject &talkObject = _talkObjectList[1]; + + if (_badConsciencePosition) + talkObject.x = 290; + else + talkObject.x = 30; + talkObject.y = 30; + + static const char *const animFilenames[] = { + "GUNFL00.WSA", "GUNFL01.WSA", "GUNFL02.WSA", "GUNFL03.WSA", "GUNFL04.WSA", "GUNFL05.WSA", "GUNFL06.WSA", "GUNFL07.WSA", + "GUNFR00.WSA", "GUNFR01.WSA", "GUNFR02.WSA", "GUNFR03.WSA", "GUNFR04.WSA", "GUNFR05.WSA", "GUNFR06.WSA", "GUNFR07.WSA" + }; + + setupSceneAnimObject(0x0E, 9, 0, 187, -1, -1, -1, -1, 0, 0, 0, -1, animFilenames[anim]); + for (uint i = 0; i <= _badConscienceFrameTable[_badConscienceAnim]; ++i) { + if (i == 8) + snd_playSoundEffect(0x1B, 0xC8); + updateSceneAnim(0x0E, i); + delay(3*_tickLength, true); + } + + if (_mainCharacter.animFrame < 50 || _mainCharacter.animFrame > 87) + return; + + if (_mainCharacter.y1 == -1 || (_mainCharacter.x1 != -1 && _mainCharacter.animFrame == 87) || _mainCharacter.animFrame == 87) { + _mainCharacter.animFrame = 87; + } else { + if (_badConsciencePosition) + _mainCharacter.facing = 3; + else + _mainCharacter.facing = 5; + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + } + + updateCharacterAnim(0); + refreshAnimObjectsIfNeed(); +} + +void KyraEngine_MR::hideBadConscience() { + if (!_badConscienceShown) + return; + + _badConscienceShown = false; + for (int frame = _badConscienceFrameTable[_badConscienceAnim+8]; frame >= 0; --frame) { + if (frame == 15) + snd_playSoundEffect(0x31, 0xC8); + updateSceneAnim(0x0E, frame); + delay(1*_tickLength, true); + } + + updateSceneAnim(0x0E, -1); + update(); + removeSceneAnimObject(0x0E, 1); + setNextIdleAnimTimer(); +} + +void KyraEngine_MR::showGoodConscience() { + if (_goodConscienceShown) + return; + + _goodConscienceShown = true; + ++_goodConscienceAnim; + _goodConscienceAnim %= 5; + + setNextIdleAnimTimer(); + _goodConsciencePosition = (_mainCharacter.x1 <= 160); + + if (_badConscienceShown) + _goodConsciencePosition = !_badConsciencePosition; + + int anim = _goodConscienceAnim + (_goodConsciencePosition ? 0 : 5); + TalkObject &talkObject = _talkObjectList[87]; + + if (_goodConsciencePosition) + talkObject.x = 290; + else + talkObject.x = 30; + talkObject.y = 30; + + static const char *const animFilenames[] = { + "STUFL00.WSA", "STUFL02.WSA", "STUFL04.WSA", "STUFL03.WSA", "STUFL01.WSA", + "STUFR00.WSA", "STUFR02.WSA", "STUFR04.WSA", "STUFR03.WSA", "STUFR01.WSA" + }; + + setupSceneAnimObject(0x0F, 9, 0, 187, -1, -1, -1, -1, 0, 0, 0, -1, animFilenames[anim]); + for (uint i = 0; i <= _goodConscienceFrameTable[_goodConscienceAnim]; ++i) { + if (i == 10) + snd_playSoundEffect(0x7F, 0xC8); + updateSceneAnim(0x0F, i); + delay(2*_tickLength, true); + } + + if (_mainCharacter.animFrame < 50 || _mainCharacter.animFrame > 87) + return; + + if (_mainCharacter.y1 == -1 || (_mainCharacter.x1 != -1 && _mainCharacter.animFrame == 87) || _mainCharacter.animFrame == 87) { + _mainCharacter.animFrame = 87; + } else { + if (_goodConsciencePosition) + _mainCharacter.facing = 3; + else + _mainCharacter.facing = 5; + _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing]; + } + + updateCharacterAnim(0); + refreshAnimObjectsIfNeed(); +} + +void KyraEngine_MR::hideGoodConscience() { + if (!_goodConscienceShown) + return; + + _goodConscienceShown = false; + for (int frame = _goodConscienceFrameTable[_goodConscienceAnim+5]; frame >= 0; --frame) { + if (frame == 17) + snd_playSoundEffect(0x31, 0xC8); + updateSceneAnim(0x0F, frame); + delay(1*_tickLength, true); + } + + updateSceneAnim(0x0F, -1); + update(); + removeSceneAnimObject(0x0F, 1); + setNextIdleAnimTimer(); +} + +void KyraEngine_MR::eelScript() { + if (_chatText) + return; + _screen->hideMouse(); + + if (_inventoryState) + hideInventory(); + removeHandItem(); + + objectChat((const char *)getTableEntry(_cCodeFile, 35), 0, 204, 35); + objectChat((const char *)getTableEntry(_cCodeFile, 40), 0, 204, 40); + + setGameFlag(0xD1); + + snd_playSoundEffect(0x2A, 0xC8); + + setGameFlag(0x171); + + switch (_characterShapeFile-1) { + case 0: + runAnimationScript("EELS01.EMC", 0, 0, 1, 1); + break; + + case 1: + runAnimationScript("EELS02.EMC", 0, 0, 1, 1); + break; + + case 2: + runAnimationScript("EELS03.EMC", 0, 0, 1, 1); + break; + + case 3: + runAnimationScript("EELS04.EMC", 0, 0, 1, 1); + break; + + default: + resetGameFlag(0x171); + runAnimationScript("EELS00.EMC", 0, 0, 1, 1); + } + + changeChapter(2, 29, 0, 4); + _screen->showMouse(); +} + +int KyraEngine_MR::initAnimationShapes(uint8 *filedata) { + const int lastEntry = MIN(_animShapeLastEntry, 41); + for (int i = 0; i < lastEntry; ++i) + _gameShapes[9+i] = _screen->getPtrToShape(filedata, i); + return lastEntry; +} + +void KyraEngine_MR::uninitAnimationShapes(int count, uint8 *filedata) { + for (int i = 0; i < count; ++i) + _gameShapes[9+i] = 0; + delete[] filedata; + setNextIdleAnimTimer(); +} + +} // End of namespace Kyra diff --git a/engines/kyra/sequence/sequences_v2.cpp b/engines/kyra/sequence/sequences_v2.cpp new file mode 100644 index 0000000000..4c32357684 --- /dev/null +++ b/engines/kyra/sequence/sequences_v2.cpp @@ -0,0 +1,130 @@ +/* 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 "kyra/engine/kyra_v2.h" +#include "kyra/resource/resource.h" + +#include "common/system.h" + +namespace Kyra { + +void KyraEngine_v2::runAnimationScript(const char *filename, int allowSkip, int resetChar, int newShapes, int shapeUnload) { + memset(&_animationScriptData, 0, sizeof(_animationScriptData)); + memset(&_animationScriptState, 0, sizeof(_animationScriptState)); + + if (!_emc->load(filename, &_animationScriptData, &_opcodesAnimation)) + error("Couldn't load temporary script '%s'", filename); + + _emc->init(&_animationScriptState, &_animationScriptData); + _emc->start(&_animationScriptState, 0); + + _animResetFrame = -1; + + if (_animShapeFiledata && newShapes) { + uninitAnimationShapes(_animShapeCount, _animShapeFiledata); + _animShapeFiledata = 0; + _animShapeCount = 0; + } + + while (_emc->isValid(&_animationScriptState)) + _emc->run(&_animationScriptState); + + uint8 *fileData = 0; + + if (newShapes) + _animShapeFiledata = _res->fileData(_animShapeFilename, 0); + + fileData = _animShapeFiledata; + + if (!fileData) { + _emc->unload(&_animationScriptData); + return; + } + + if (newShapes) + _animShapeCount = initAnimationShapes(fileData); + + processAnimationScript(allowSkip, resetChar); + + if (shapeUnload) { + uninitAnimationShapes(_animShapeCount, fileData); + _animShapeCount = 0; + _animShapeFiledata = 0; + } + + _emc->unload(&_animationScriptData); +} + +void KyraEngine_v2::processAnimationScript(int allowSkip, int resetChar) { + setCharacterAnimDim(_animShapeWidth, _animShapeHeight); + + _emc->init(&_animationScriptState, &_animationScriptData); + _emc->start(&_animationScriptState, 1); + + resetSkipFlag(); + + while (_emc->isValid(&_animationScriptState)) { + _animNeedUpdate = false; + while (_emc->isValid(&_animationScriptState) && !_animNeedUpdate) + _emc->run(&_animationScriptState); + + if (_animNewFrame < 0) + continue; + + _mainCharacter.animFrame = _animNewFrame + _desc.animScriptFrameAdd; + updateCharacterAnim(0); + if (_chatText) + updateWithText(); + else + update(); + + uint32 delayEnd = _system->getMillis() + _animDelayTime * _tickLength; + + while ((!skipFlag() || !allowSkip) && _system->getMillis() < delayEnd) + delay(10, true); + + if (skipFlag()) { + resetSkipFlag(); + if (allowSkip) + break; + } + } + + if (resetChar) { + if (_animResetFrame >= 0) { + _mainCharacter.animFrame = _animResetFrame + _desc.animScriptFrameAdd; + updateCharacterAnim(0); + if (_chatText) + updateWithText(); + else + update(); + } + + _mainCharacter.animFrame = _desc.characterFrameTable[_mainCharacter.facing]; + updateCharacterAnim(0); + } + + _animResetFrame = -1; + resetCharacterAnimDim(); +} + +} // End of namespace Kyra |