aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/sequence/sequences_lol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra/sequence/sequences_lol.cpp')
-rw-r--r--engines/kyra/sequence/sequences_lol.cpp1538
1 files changed, 1538 insertions, 0 deletions
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