/* 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/darkmoon.h" #include "kyra/screen_eob.h" #include "kyra/resource.h" #include "kyra/sound.h" #include "common/system.h" #include "base/version.h" namespace Kyra { class DarkmoonSequenceHelper { friend class DarkMoonEngine; public: enum Mode { kIntro, kFinale }; struct Config { Config(Mode m, const char *const *str, const char *const *cps, const char *const *pal, const DarkMoonShapeDef **shp, const DarkMoonAnimCommand **anim, bool paletteFading) : mode(m), strings(str), cpsFiles(cps), palFiles(pal), shapeDefs(shp), animData(anim), palFading(paletteFading) {} Mode mode; const char *const *strings; const char *const *cpsFiles; const char *const *palFiles; const DarkMoonShapeDef **shapeDefs; const DarkMoonAnimCommand **animData; bool palFading; }; DarkmoonSequenceHelper(OSystem *system, DarkMoonEngine *vm, Screen_EoB *screen, const Config *config); ~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; const Config *_config; Palette *_palettes[12]; const uint8 **_shapes; uint32 _fadePalTimer; int _fadePalRate; int _fadePalIndex; }; int DarkMoonEngine::mainMenu() { int menuChoice = _menuChoiceInit; _menuChoiceInit = 0; _sound->loadSoundFile("INTRO"); Screen::FontId of = _screen->_currentFont; int op = 0; Common::SeekableReadStream *s = 0; while (menuChoice >= 0 && !shouldQuit()) { switch (menuChoice) { case 0: { 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, 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->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()); return sel + 1; } void DarkMoonEngine::seq_playIntro() { DarkmoonSequenceHelper::Config config(DarkmoonSequenceHelper::kIntro, _introStrings, _cpsFilesIntro, _configRenderMode == Common::kRenderEGA ? _palFilesIntroEGA : _palFilesIntroVGA, _shapesIntro, _animIntro, false); DarkmoonSequenceHelper sq(_system, this, _screen, &config); _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(1); sq.animCommand(_configRenderMode == Common::kRenderEGA ? 12 : 11); sq.animCommand(7, 6); sq.animCommand(2, 6); sq.waitForSongNotifier(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(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(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(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(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(7); sq.animCommand(30); sq.animCommand(31); sq.waitForSongNotifier(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::Config config(DarkmoonSequenceHelper::kFinale, _finaleStrings, _cpsFilesFinale, _configRenderMode == Common::kRenderEGA ? _palFilesFinaleEGA : _palFilesFinaleVGA, _shapesFinale, _animFinale, true); DarkmoonSequenceHelper sq(_system, this, _screen, &config); _screen->setCurPage(0); _screen->setFont(Screen::FID_8_FNT); _sound->loadSoundFile("FINALE1"); snd_stopSound(); sq.delay(3); _screen->clearCurPage(); _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(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(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(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(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(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); _sound->loadSoundFile("FINALE2"); sq.delay(18); if (!skipFlag() && !shouldQuit()) snd_playSong(1); seq_playCredits(&sq, _creditsData, 18, 2, 6, 2); 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); 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->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(_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, const Config *config) : _system(system), _vm(vm), _screen(screen), _config(config) { 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]); } _palettes[9] = new Palette(256); _palettes[9]->fill(0, 256, 0); _palettes[10] = new Palette(256); _palettes[10]->fill(0, 256, 63); _palettes[11] = new Palette(256); _palettes[11]->fill(0, 256, 0); _shapes = new const uint8*[30]; memset(_shapes, 0, 30 * sizeof(uint8 *)); _fadePalTimer = 0; _fadePalRate = 0; _screen->setScreenPalette(*_palettes[0]); _screen->setFont(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]; delete _palettes[9]; delete _palettes[10]; delete _palettes[11]; for (int i = 0; i < 30; i++) delete[] _shapes[i]; delete[] _shapes; _screen->clearCurPage(); _screen->showMouse(); _screen->updateScreen(); _system->delayMillis(150); _vm->resetSkipFlag(true); _vm->_allowSkip = false; } void DarkmoonSequenceHelper::loadScene(int index, int pageNum) { char file[13]; strcpy(file, _config->cpsFiles[index]); Common::SeekableReadStream *s = _vm->resource()->createReadStream(file); uint32 chunkID = 0; if (s) { chunkID = s->readUint32LE(); s->seek(0); } 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->mode == kFinale) 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 = _config->mode == kFinale ? (s->pal + 1) : s->pal; int x = s->x1; 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->mode == kIntro && s->pal) setPaletteWithoutTextColor(0); break; case 1: // draw shape, then restore background shapeW = _shapes[s->obj][2]; shapeH = _shapes[s->obj][3]; if (_config->mode == kFinale) { _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->mode == kIntro ? 0 : 18); if (_vm->_configRenderMode != Common::kRenderEGA && s->pal) setPaletteWithoutTextColor(palIndex); else _screen->updateScreen(); delay(s->delay); if (_config->mode == kIntro) { 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->mode == kIntro && s->pal) setPaletteWithoutTextColor(0); break; case 3: case 4: // fade shape in or out or restore background if (_config->mode == kFinale) 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->setShapeFadeMode(0, true); _screen->setShapeFadeMode(1, true); end = _system->getMillis() + s->delay * _vm->tickLength(); if (palIndex) { _screen->setFadeTableIndex(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->setShapeFadeMode(0, false); _screen->setShapeFadeMode(1, false); } break; case 5: // copy region if (_config->mode == kFinale && s->pal) setPaletteWithoutTextColor(palIndex); _screen->copyRegion(s->x2 << 3, s->y2, s->x1, s->y1, s->w << 3, s->h, (s->obj && _config->mode == kFinale) ? 6 : 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; for (int yOffs = 0; *str; yOffs += 9) { 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(); } } void DarkMoonEngine::seq_nightmare() { Screen::FontId of = _screen->setFont(Screen::FID_6_FNT); _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"; _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