From 4afa6d18a4d08dcf903bb9a2973ed0b1123e1c6c Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Thu, 13 Aug 2009 15:55:12 +0000 Subject: Implemented support for the "Lore of the Lands" special of Lands of Lore CD. svn-id: r43349 --- dists/engine-data/kyra.dat | Bin 257348 -> 257836 bytes engines/kyra/lol.h | 34 +++- engines/kyra/resource.h | 22 +-- engines/kyra/sequences_lol.cpp | 274 +++++++++++++++++++++++++++++++- engines/kyra/staticres.cpp | 44 ++--- tools/create_kyradat/create_kyradat.cpp | 4 +- tools/create_kyradat/create_kyradat.h | 2 + tools/create_kyradat/lol_cd.h | 8 +- tools/create_kyradat/misc.h | 6 + 9 files changed, 354 insertions(+), 40 deletions(-) diff --git a/dists/engine-data/kyra.dat b/dists/engine-data/kyra.dat index da57e2ce2a..eb3ff24c2a 100644 Binary files a/dists/engine-data/kyra.dat and b/dists/engine-data/kyra.dat differ diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h index 06950779b1..1c89a7a1eb 100644 --- a/engines/kyra/lol.h +++ b/engines/kyra/lol.h @@ -301,6 +301,7 @@ friend class GUI_LoL; friend class TextDisplayer_LoL; friend class TIMInterpreter_LoL; friend class Debugger_LoL; +friend class HistoryPlayer; public: LoLEngine(OSystem *system, const GameFlags &flags); ~LoLEngine(); @@ -1124,14 +1125,14 @@ private: uint16 _dmScaleH; int _lastMouseRegion; - int _seqWindowX1, _seqWindowY1, _seqWindowX2, _seqWindowY2, _seqTrigger; - int _spsWindowX, _spsWindowY, _spsWindowW, _spsWindowH; + int _seqWindowX1, _seqWindowY1, _seqWindowX2, _seqWindowY2, _seqTrigger; + int _spsWindowX, _spsWindowY, _spsWindowW, _spsWindowH; uint8 *_tempBuffer5120; - const char *const * _levelDatList; + const char * const *_levelDatList; int _levelDatListSize; - const char *const * _levelShpList; + const char * const *_levelShpList; int _levelShpListSize; const int8 *_dscUnk1; @@ -1476,6 +1477,31 @@ private: LevelTempData *_lvlTempData[29]; }; +class HistoryPlayer { +public: + HistoryPlayer(LoLEngine *vm); + ~HistoryPlayer(); + + void play(); +private: + OSystem *_system; + LoLEngine *_vm; + Screen *_screen; + + int _x, _y, _width, _height; + int _frame; + Movie *_wsa; + + void loadWsa(const char *filename); + void playWsa(bool direction); + void restoreWsaBkgd(); + + Movie *_fireWsa; + int _fireFrame; + uint32 _nextFireTime; + void updateFire(); +}; + } // end of namespace Kyra #endif diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h index 07fb2e05b7..3dc6c5ff2f 100644 --- a/engines/kyra/resource.h +++ b/engines/kyra/resource.h @@ -282,16 +282,18 @@ enum kKyraResources { kLolButtonList7, kLolButtonList8, - lolLegendData, - lolMapCursorOvl, - lolMapStringId, - //lolMapPal, - - lolSpellbookAnim, - lolSpellbookCoords, - lolHealShapeFrames, - lolLightningDefs, - lolFireballCoords, + kLolLegendData, + kLolMapCursorOvl, + kLolMapStringId, + //kLolMapPal, + + kLolSpellbookAnim, + kLolSpellbookCoords, + kLolHealShapeFrames, + kLolLightningDefs, + kLolFireballCoords, + + kLolHistory, #endif // ENABLE_LOL kMaxResIDs diff --git a/engines/kyra/sequences_lol.cpp b/engines/kyra/sequences_lol.cpp index 5826b2b09e..ae23d130ef 100644 --- a/engines/kyra/sequences_lol.cpp +++ b/engines/kyra/sequences_lol.cpp @@ -89,8 +89,10 @@ int LoLEngine::processPrologue() { showIntro(); break; - case 2: // "Lore of the Lands" (only CD version) - 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)) @@ -121,7 +123,8 @@ 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", 0 + "INTRO6.PAK", "INTRO7.PAK", "INTRO8.PAK", "INTRO9.PAK", + "HISTORY.PAK", 0 }; static const char * const fileListFloppyExtracted[] = { @@ -683,6 +686,271 @@ void LoLEngine::showStarcraftLogo() { 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 + strncpy(voiceFilename, "PS_1a", sizeof(voiceFilename)); + + int part = 0; + Sound *sound = _vm->sound(); + + Common::Functor0Mem palFade(this, &HistoryPlayer::updateFire); + + for (; voiceFilename[3] <= '9' && !_vm->shouldQuit() && !_vm->skipFlag(); ++voiceFilename[3], voiceFilename[4] = 'a') { + while (!_vm->shouldQuit() && !_vm->skipFlag()) { + if (!sound->voiceFileIsPresent(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(); + _vm->resetSkipFlag(); + } + + ++voiceFilename[4]; + } + + if (_vm->skipFlag()) + _vm->resetSkipFlag(); + } + + if (_vm->skipFlag()) + _vm->resetSkipFlag(); + + 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); +} + +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) { diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 866a39b921..51288f31df 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -44,7 +44,7 @@ namespace Kyra { -#define RESFILE_VERSION 49 +#define RESFILE_VERSION 50 namespace { bool checkKyraDat(Common::SeekableReadStream *file) { @@ -444,16 +444,18 @@ bool StaticResource::init() { { kLolButtonList7, kLolRawDataBe16, "BUTTON7.LST" }, { kLolButtonList8, kLolRawDataBe16, "BUTTON84.LST" }, - { lolLegendData, kRawData, "MAPLGND.DEF" }, - { lolMapCursorOvl, kRawData, "MAPCURSOR.PAL" }, - { lolMapStringId, kLolRawDataBe16, "MAPSTRID.LST" }, - //{ lolMapPal, kRawData, "MAP.PAL" }, + { kLolLegendData, kRawData, "MAPLGND.DEF" }, + { kLolMapCursorOvl, kRawData, "MAPCURSOR.PAL" }, + { kLolMapStringId, kLolRawDataBe16, "MAPSTRID.LST" }, + //{ kLolMapPal, kRawData, "MAP.PAL" }, - { lolSpellbookAnim, kRawData, "MBOOKA.DEF" }, - { lolSpellbookCoords, kRawData, "MBOOKC.DEF" }, - { lolHealShapeFrames, kRawData, "MHEAL.SHP" }, - { lolLightningDefs, kRawData, "MLGHTNG.DEF" }, - { lolFireballCoords, kLolRawDataBe16, "MFIREBLL.DEF" }, + { kLolSpellbookAnim, kRawData, "MBOOKA.DEF" }, + { kLolSpellbookCoords, kRawData, "MBOOKC.DEF" }, + { kLolHealShapeFrames, kRawData, "MHEAL.SHP" }, + { kLolLightningDefs, kRawData, "MLGHTNG.DEF" }, + { kLolFireballCoords, kLolRawDataBe16, "MFIREBLL.DEF" }, + + { kLolHistory, kRawData, "HISTORY.FLS" }, { 0, 0, 0 } }; @@ -1875,10 +1877,10 @@ void LoLEngine::initStaticResource() { _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList7, _buttonList7Size); _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList8, _buttonList8Size); - _autoMapStrings = _staticres->loadRawDataBe16(lolMapStringId, _autoMapStringsSize); + _autoMapStrings = _staticres->loadRawDataBe16(kLolMapStringId, _autoMapStringsSize); int tmpSize = 0; - const uint8 *tmp = _staticres->loadRawData(lolLegendData, tmpSize); + const uint8 *tmp = _staticres->loadRawData(kLolLegendData, tmpSize); tmpSize /= 5; if (tmp) { _defaultLegendData = new MapLegendData[tmpSize]; @@ -1889,19 +1891,19 @@ void LoLEngine::initStaticResource() { _defaultLegendData[i].stringId = READ_LE_UINT16(tmp); tmp += 2; } - _staticres->unloadId(lolLegendData); + _staticres->unloadId(kLolLegendData); } - tmp = _staticres->loadRawData(lolMapCursorOvl, tmpSize); + tmp = _staticres->loadRawData(kLolMapCursorOvl, tmpSize); _mapCursorOverlay = new uint8[tmpSize]; memcpy(_mapCursorOverlay, tmp, tmpSize); - _staticres->unloadId(lolMapCursorOvl); + _staticres->unloadId(kLolMapCursorOvl); - _updateSpellBookCoords = _staticres->loadRawData(lolSpellbookCoords, _updateSpellBookCoordsSize); - _updateSpellBookAnimData = _staticres->loadRawData(lolSpellbookAnim, _updateSpellBookAnimDataSize); - _healShapeFrames = _staticres->loadRawData(lolHealShapeFrames, _healShapeFramesSize); + _updateSpellBookCoords = _staticres->loadRawData(kLolSpellbookCoords, _updateSpellBookCoordsSize); + _updateSpellBookAnimData = _staticres->loadRawData(kLolSpellbookAnim, _updateSpellBookAnimDataSize); + _healShapeFrames = _staticres->loadRawData(kLolHealShapeFrames, _healShapeFramesSize); - tmp = _staticres->loadRawData(lolLightningDefs, tmpSize); + tmp = _staticres->loadRawData(kLolLightningDefs, tmpSize); if (tmp) { _lightningProps = new LightningProperty[5]; for (int i = 0; i < 5; i++) { @@ -1909,10 +1911,10 @@ void LoLEngine::initStaticResource() { _lightningProps[i].frameDiv = tmp[(i << 2) + 1]; _lightningProps[i].sfxId = READ_LE_UINT16(&tmp[(i << 2) + 2]); } - _staticres->unloadId(lolLightningDefs); + _staticres->unloadId(kLolLightningDefs); } - _fireBallCoords = (const int16*)_staticres->loadRawDataBe16(lolFireballCoords, _fireBallCoordsSize); + _fireBallCoords = (const int16*)_staticres->loadRawDataBe16(kLolFireballCoords, _fireBallCoordsSize); _buttonCallbacks.clear(); _buttonCallbacks.reserve(95); diff --git a/tools/create_kyradat/create_kyradat.cpp b/tools/create_kyradat/create_kyradat.cpp index 780282bab2..011550d1cd 100644 --- a/tools/create_kyradat/create_kyradat.cpp +++ b/tools/create_kyradat/create_kyradat.cpp @@ -31,7 +31,7 @@ #include "md5.h" enum { - kKyraDatVersion = 49, + kKyraDatVersion = 50, kIndexSize = 12 }; @@ -347,6 +347,8 @@ const ExtractFilename extractFilenames[] = { { lolLightningDefs, kTypeRawData, "MLGHTNG.DEF" }, { lolFireballCoords, lolTypeRaw16, "MFIREBLL.DEF" }, + { lolHistory, kTypeRawData, "HISTORY.FLS" }, + { -1, 0, 0 } }; diff --git a/tools/create_kyradat/create_kyradat.h b/tools/create_kyradat/create_kyradat.h index 13f6966f83..e7aa292265 100644 --- a/tools/create_kyradat/create_kyradat.h +++ b/tools/create_kyradat/create_kyradat.h @@ -257,6 +257,8 @@ enum kExtractID { lolLightningDefs, lolFireballCoords, + lolHistory, + kMaxResIDs }; diff --git a/tools/create_kyradat/lol_cd.h b/tools/create_kyradat/lol_cd.h index fa36d1b91d..dfdd5e1bc3 100644 --- a/tools/create_kyradat/lol_cd.h +++ b/tools/create_kyradat/lol_cd.h @@ -1,3 +1,8 @@ +const ExtractEntry lolCDFile1E[] = { + { lolHistory, 0x1FAA0, 0x1FC71 }, + { -1, 0, 0 } +}; + const ExtractEntry lolCDFile2[] = { { lolCharacterDefs, 0x00029D60, 0x0002A1F2 }, { lolIngameSfxFiles, 0x0002A330, 0x0002AC22 }, @@ -84,6 +89,7 @@ const ExtractEntry lolCDFile2[] = { }; const Game lolGames[] = { - { kLol, EN_ANY, kLolCD, "263998ec600afca1cc7b935c473df670", lolCDFile2}, + { kLol, EN_ANY, kLolCD, "9d1778314de80598c0b0d032e2a1a1cf", lolCDFile1E }, + { kLol, EN_ANY, kLolCD, "263998ec600afca1cc7b935c473df670", lolCDFile2 }, GAME_DUMMY_ENTRY }; diff --git a/tools/create_kyradat/misc.h b/tools/create_kyradat/misc.h index 1dac208ade..1117ad1d05 100644 --- a/tools/create_kyradat/misc.h +++ b/tools/create_kyradat/misc.h @@ -481,6 +481,11 @@ const int kyra3Need[] = { -1 }; +const int lolCDFile1ENeed[] = { + lolHistory, + -1 +}; + const int lolCDFile2Need[] = { lolCharacterDefs, lolIngameSfxFiles, @@ -604,6 +609,7 @@ const GameNeed gameNeedTable[] = { { kKyra3, -1, kyra3Need }, + { kLol, kLolCD, lolCDFile1ENeed }, { kLol, kLolCD, lolCDFile2Need }, { -1, -1, 0 } -- cgit v1.2.3