/* 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. * * $URL$ * $Id$ * */ #include "kyra/kyra.h" #include "kyra/kyra_v2.h" #include "kyra/screen.h" #include "kyra/wsamovie.h" #include "kyra/sound.h" #include "kyra/text.h" #include "common/system.h" namespace Kyra { void KyraEngine_v2::seq_playSequences(int startSeq, int endSeq) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_playSequences(%i, %i)", startSeq, endSeq); _skipFlag = false; if (endSeq == -1) endSeq = startSeq; static const Sequence sequences[] = { // type, filename, callback, framedelay, duration, numframes, timeOut, fadeOut {2, "virgin.cps", 0, 100, 0, 1, true, true}, {1, "westwood.wsa", &KyraEngine_v2::seq_introWestwood, 6, 160, 18, true, true}, {1, "title.wsa", &KyraEngine_v2::seq_introTitle, 6, 10, 26, false, false}, {2, "over.cps", &KyraEngine_v2::seq_introOverview, 16, 30, 1, false, true}, {2, "library.cps", &KyraEngine_v2::seq_introLibrary, 16, 30, 1, false, true}, {2, "hand.cps", &KyraEngine_v2::seq_introHand, 16, 90, 1, false, true}, {1, "point.wsa", &KyraEngine_v2::seq_introPoint, 16, 30, 1, false, true}, {1, "zanfaun.wsa", &KyraEngine_v2::seq_introZanFaun, 16, 90, 1, false, true} }; assert(startSeq >= 0 && endSeq < ARRAYSIZE(sequences) && startSeq <= endSeq); _activeWSA = new ActiveWSA[8]; assert(_activeWSA); memset(_activeWSA, 0, sizeof(ActiveWSA) * 8); _activeChat = new ActiveChat[10]; assert(_activeChat); memset(_activeChat, 0, sizeof(ActiveChat) * 10); seq_resetAllChatEntries(); _screen->hideMouse(); int oldPage = _screen->setCurPage(2); uint8 pal[768]; memset(pal, 0, sizeof(pal)); for (int i = startSeq; i <= endSeq && !_skipFlag; i++) { uint32 seqDelay = 0; int seqNum = 0; _screen->setScreenPalette(pal); _screen->clearPage(0); if (sequences[i].type == 2) { _screen->loadBitmap(sequences[i].filename, 2, 2, _screen->_currentPalette); _screen->updateScreen(); seqDelay = sequences[i].frameDelay * _tickLength; } else if(sequences[i].type == 1) { seq_loadWSA(0, sequences[i].filename, sequences[i].frameDelay); seqDelay = sequences[i].duration * _tickLength; } if (sequences[i].callback) (*this.*sequences[i].callback)(seqNum++); seq_playWSAs(); _screen->copyPage(2, 0); _screen->updateScreen(); _screen->fadeFromBlack(40); seqDelay += _system->getMillis(); bool mayEndLoop = sequences[i].timeOut; // Skip the movie if esc is pressed or the mouse is clicked // However, don't skip the menu movie, to match the behavior of the original interpreter while ((!_quitFlag && !_skipFlag) || i == kSequenceTitle) { uint32 startTime = _system->getMillis(); if (sequences[i].callback) { int newTime = (*this.*sequences[i].callback)(seqNum++); if (newTime != -1) { seqDelay = newTime * _tickLength + _system->getMillis(); mayEndLoop = true; } } seq_playWSAs(); _screen->copyPage(2, 0); seq_showChats(); _screen->updateScreen(); uint32 currTime = _system->getMillis(); if (seqDelay <= currTime && mayEndLoop) { break; } else { uint32 loopTime = currTime - startTime; delay(loopTime < _tickLength ? loopTime : _tickLength); } } if (sequences[i].fadeOut) _screen->fadeToBlack(40); if (sequences[i].type == 1) seq_unloadWSA(0); _screen->clearPage(2); } _screen->setCurPage(oldPage); _screen->showMouse(); for (int i = 0; i < 8; i++) seq_unloadWSA(i); delete[] _activeWSA; delete[] _activeChat; } // FIXME: This part needs game dialogs, as it's not part of the intro, but // rather a game video. It has speech only in the CD version int KyraEngine_v2::seq_introZanFaun(int seqNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introZanFaun(%i)", seqNum); static const SequenceControl zanFaunWSAControl[] = { {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}, {6, 6}, {7, 6}, {8, 6}, {9, 6}, {10, 6}, {11, 6}, {12, 6}, {13, 6}, {14, 6}, {15, 6}, {16, 6}, {17, 6}, {18, 6}, {19, 6}, {20, 6}, {21, 6}, {22, 6}, {23, 6}, {23, 6}, {22, 6}, {21, 6}, {20, 6}, {19, 6}, {18, 6}, {17, 6}, {16, 6}, {15, 6}, {14, 6}, {13, 6}, {12, 6}, {11, 6}, {10, 6}, {9, 6}, {8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {8, 6}, {9, 6}, {10, 6}, {-1, -1} }; switch (seqNum) { case 0: _sound->playTrack(8); //XXX: palette stuff //XXX: load dialogs break; case 1: seq_loadWSA(1, "zanfaun.wsa", 9, 0, zanFaunWSAControl); break; case 0x294: seq_waitForChatsToFinish(); seq_unloadWSA(1); return 0; default: break; } return -1; } int KyraEngine_v2::seq_introPoint(int seqNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introPoint(%i)", seqNum); switch (seqNum) { case 0: _sound->playTrack(7); break; case 1: seq_loadWSA(1, "point.wsa", 9); seq_playIntroChat(11); // "Zanthia, youngest of the royal mystics has been selected" break; case 0x96: seq_waitForChatsToFinish(); seq_unloadWSA(1); return 0; default: break; } return -1; } int KyraEngine_v2::seq_introHand(int seqNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introHand(%i)", seqNum); // XXX: commented out to prevent compiler warnings /*static const SequenceControl hand1bWSAControl[] = { {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {5, 6}, {6, 6}, {7, 6}, {8, 6}, {9, 6}, {10, 6}, {11, 6}, {11, 12}, {12, 12}, {13, 12}, {12, 12}, {11, 12}, {-1, -1} }; static const SequenceControl hand1cWSAControl[] = { {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {4, 6}, {5, 64}, {5, 6}, {-1, -1} };*/ static const SequenceControl hand2WSAControl[] = { {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {0, 6}, {1, 6}, {-1, -1} }; static const SequenceControl hand3WSAControl[] = { {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {1, 6}, {2, 6}, {1, 6}, {0, 6}, {-1, -1} }; static const SequenceControl hand4WSAControl[] = { {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {0, 6}, {1, 6}, {2, 6}, {3, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}, {-1, -1} }; switch (seqNum) { case 0: _sound->playTrack(6); //palette stuff break; case 1: // XXX: these show as garbage. New frame encode? /*seq_loadWSA(1, "hand1a.wsa", 9); seq_loadWSA(2, "hand1b.wsa", 9, 0, hand1bWSAControl); seq_loadWSA(3, "hand1c.wsa", 9, 0, hand1cWSAControl);*/ seq_playIntroChat(7); // "Luckily, the Hand was experienced in these matters" break; case 0xc9: // palette stuff seq_loadWSA(4, "hand2.wsa", 9, 0, hand2WSAControl); seq_waitForChatsToFinish(); seq_playIntroChat(8); // "and finally, a plan was approved" break; case 0x18b: seq_loadWSA(5, "hand3.wsa", 9, 0, hand3WSAControl); seq_waitForChatsToFinish(); seq_playIntroChat(9); // "which required a magic anchorstone" break; case 0x1f4: seq_loadWSA(6, "hand4.wsa", 9, 0, hand4WSAControl); seq_waitForChatsToFinish(); seq_playIntroChat(10); // "to be retrieved from the center of the world" break; case 0x320: seq_waitForChatsToFinish(); /*seq_unloadWSA(1); seq_unloadWSA(2); seq_unloadWSA(3);*/ seq_unloadWSA(4); seq_unloadWSA(5); seq_unloadWSA(6); return 0; } return -1; } int KyraEngine_v2::seq_introLibrary(int seqNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introLibrary(%i)", seqNum); static const SequenceControl libraryWSAControl[] = { {0, 10}, {1, 10}, {2, 10}, {3, 10}, {4, 10}, {5, 10}, {6, 10}, {7, 10}, {8, 10}, {9, 10}, {8, 10}, {7, 10}, {6, 10}, {5, 40}, {4, 10}, {3, 10}, {2, 10}, {1, 10}, {-1, -1} }; switch (seqNum) { case 0: _sound->playTrack(5); seq_playIntroChat(4); // "The royal mystics are baffled" //XXX: palette stuff break; case 1: seq_loadWSA(1, "library.wsa", 9, 0, libraryWSAControl); break; case 0x64: seq_waitForChatsToFinish(); // unk1 = 7; // palette/screen stuff seq_loadWSA(2, "darm.wsa", 9); break; case 0x68: seq_waitForChatsToFinish(); seq_playIntroChat(5); // "Every reference has been consulted" break; case 0xF0: seq_waitForChatsToFinish(); seq_loadWSA(3, "library.wsa", 9); break; case 0x154: seq_waitForChatsToFinish(); // palette stuff seq_loadWSA(4, "marco.wsa", 9); seq_playIntroChat(6); // "Even Marko and his new valet have been allowed" break; case 0x294: seq_waitForChatsToFinish(); seq_unloadWSA(1); seq_unloadWSA(2); seq_unloadWSA(3); seq_unloadWSA(4); return 0; default: break; } return -1; } int KyraEngine_v2::seq_introOverview(int seqNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introOverview(%i)", seqNum); switch (seqNum) { case 0: _sound->playTrack(4); break; case 40: seq_loadWSA(1, "over1.wsa", 10, &KyraEngine_v2::seq_introOverviewOver1); break; case 60: seq_loadWSA(2, "over2.wsa", 9); break; case 120: seq_playIntroChat(0); // "Kyrandia is disappearing!" break; case 200: seq_waitForChatsToFinish(); // XXX: fade to grey _screen->k2IntroFadeToGrey(40); break; case 201: // XXX break; case 282: seq_waitForChatsToFinish(); seq_loadWSA(3, "forest.wsa", 6, &KyraEngine_v2::seq_introOverviewForest); seq_playIntroChat(1); // "Rock by rock..." break; case 434: seq_waitForChatsToFinish(); seq_loadWSA(4, "dragon.wsa", 6, &KyraEngine_v2::seq_introOverviewDragon); break; case 540: seq_waitForChatsToFinish(); seq_unloadWSA(1); seq_unloadWSA(2); seq_unloadWSA(3); seq_unloadWSA(4); return 0; break; } return -1; } void KyraEngine_v2::seq_introOverviewOver1(int currentFrame) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introOverviewOver1(%i)", currentFrame); if (currentFrame == 2) seq_waitForChatsToFinish(); else if(currentFrame == 3) seq_playIntroChat(12); } void KyraEngine_v2::seq_introOverviewForest(int currentFrame) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introOverviewForest(%i)", currentFrame); if (currentFrame == 11) { seq_waitForChatsToFinish(); } else if(currentFrame == 12) { delay(25); seq_playIntroChat(2); // "...and tree by tree..." } } void KyraEngine_v2::seq_introOverviewDragon(int currentFrame) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introOverviewDragon(%i)", currentFrame); if (currentFrame == 3) seq_playIntroChat(3); // "Kyrandia ceases to exist!" else if(currentFrame == 11) seq_waitForChatsToFinish(); } int KyraEngine_v2::seq_introTitle(int seqNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introtitle(%i)", seqNum); if (seqNum == 1) { _sound->playTrack(3); } else if (seqNum == 25) { // XXX: handle menu return 200; } return -1; } int KyraEngine_v2::seq_introWestwood(int seqNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_introWestwood(%i)", seqNum); if (seqNum == 0) _sound->playTrack(2); return -1; } void KyraEngine_v2::seq_playIntroChat(uint8 chatNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_playIntroChat(%i)", chatNum); assert(chatNum < _introSoundListSize); if (chatNum < 12) seq_setChatEntry(chatNum, 160, 168, _introStringsDuration[chatNum], 160); _sound->voicePlay(_introSoundList[chatNum]); } void KyraEngine_v2::seq_waitForChatsToFinish() { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_waitForChatsToFinish()"); uint32 longest = 0; for (int i = 0; i < 10; i++) { if (_activeChat[i].duration != -1) { uint32 currChatTime = _activeChat[i].duration + _activeChat[i].startTime; if ( currChatTime > longest) longest = currChatTime; } } uint32 now = _system->getMillis(); if (longest > now) delay(longest - now); } void KyraEngine_v2::seq_resetAllChatEntries() { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_resetAllChatEntries()"); for (int i = 0; i < 10; i++) _activeChat[i].duration = -1; } void KyraEngine_v2::seq_setChatEntry(uint16 strIndex, uint16 posX, uint16 posY, int duration, uint16 unk1) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_setChatEntry(%i, %i, %i, %i, %i)", strIndex, posX, posY, duration, unk1); for (int i = 0; i < 10; i++) { if (_activeChat[i].duration != -1) continue; _activeChat[i].strIndex = strIndex; _activeChat[i].x = posX; _activeChat[i].y = posY; _activeChat[i].duration = duration * _tickLength; _activeChat[i].field_8 = unk1; _activeChat[i].startTime = _system->getMillis(); return; } } void KyraEngine_v2::seq_showChats() { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_showChats()"); uint32 now = _system->getMillis(); for (int i = 0; i < 10; i++) { if (_activeChat[i].duration != -1) { if ((_activeChat[i].startTime + (uint32)_activeChat[i].duration) > now) { assert(_activeChat[i].strIndex < _introStringsSize); _text->printIntroTextMessage(_introStrings[_activeChat[i].strIndex], _activeChat[i].x, _activeChat[i].y + 12, 0xfe, 150 /*_activeChat[i].field_8*/, 0x0, 0, Screen::FID_GOLDFONT_FNT); } else _activeChat[i].duration = -1; } } } void KyraEngine_v2::seq_playWSAs() { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_playWSAs()"); uint32 currTime = _system->getMillis(); for (int i = 0; i < 8; i++) { int currentFrame, frameDelay; if (_activeWSA[i].control) { int8 nextFrame = _activeWSA[i].control[_activeWSA[i].currentFrame].frameIndex; if (nextFrame == -1) continue; currentFrame = nextFrame; frameDelay = _activeWSA[i].control[_activeWSA[i].currentFrame].frameDelay; } else { if (_activeWSA[i].currentFrame >= _activeWSA[i].endFrame) continue; currentFrame = _activeWSA[i].currentFrame; frameDelay = _activeWSA[i].frameDelay; } _activeWSA[i].movie->displayFrame(currentFrame); if (_activeWSA[i].movie && currTime >= _activeWSA[i].nextFrame) { if (_activeWSA[i].callback != 0) (*this.*_activeWSA[i].callback)(currentFrame); _activeWSA[i].currentFrame++; _activeWSA[i].nextFrame = currTime + frameDelay * _tickLength; } } } void KyraEngine_v2::seq_loadWSA(int wsaNum, const char *filename, int frameDelay, void (KyraEngine_v2::*callback)(int), const SequenceControl *control) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_loadWSA(%i, %s, %i, %i)", wsaNum, filename, frameDelay, callback ? true : false); _activeWSA[wsaNum].movie = new WSAMovieV2(this); assert(_activeWSA[wsaNum].movie); _activeWSA[wsaNum].endFrame = _activeWSA[wsaNum].movie->open(filename, 0, _screen->_currentPalette); assert(_activeWSA[wsaNum].movie->opened()); _activeWSA[wsaNum].currentFrame = 0; _activeWSA[wsaNum].frameDelay = frameDelay; _activeWSA[wsaNum].nextFrame = _system->getMillis(); _activeWSA[wsaNum].movie->setX(0); _activeWSA[wsaNum].movie->setY(0); _activeWSA[wsaNum].movie->setDrawPage(_screen->_curPage); _activeWSA[wsaNum].callback = callback; _activeWSA[wsaNum].control = control; } void KyraEngine_v2::seq_unloadWSA(int wsaNum) { debugC(9, kDebugLevelMain, "KyraEngine_v2::seq_unloadWSA(%i)", wsaNum); if (_activeWSA[wsaNum].movie) { _activeWSA[wsaNum].movie->close(); delete _activeWSA[wsaNum].movie; _activeWSA[wsaNum].movie = 0; } } } // end of namespace Kyra