Files
scummvm-cursorfix/engines/mm/xeen/worldofxeen/clouds_cutscenes.cpp
2026-02-02 04:50:13 +01:00

1313 lines
33 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "mm/xeen/worldofxeen/clouds_cutscenes.h"
#include "mm/xeen/sound.h"
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
#define WAIT(TIME) if (_subtitles.wait(TIME)) return false
#define ROTATE_BG screen.horizMerge(_mergeX); \
_mergeX = (_mergeX + 1) % SCREEN_WIDTH
#define LOAD_VORTEX loadScreen(Common::Path(Common::String::format("vort%02u.frm", cloudsCtr))); \
if (++cloudsCtr > 20) \
cloudsCtr = 1
bool CloudsCutscenes::showCloudsIntro() {
EventsManager &events = *g_vm->_events;
FileManager &files = *g_vm->_files;
Screen &screen = *g_vm->_screen;
Sound &sound = *g_vm->_sound;
bool darkCc = files._ccNum;
files.setGameCc(0);
sound._musicSide = 0;
_subtitles.reset();
bool seenIntro = showCloudsTitle() && showCloudsIntroInner();
events.clearEvents();
// Roll up the scroll again
sound.songCommand(50);
doScroll(true, false);
sound.stopAllAudio();
screen.freePages();
files.setGameCc(darkCc ? 1 : 0);
return seenIntro;
}
bool CloudsCutscenes::showCloudsTitle() {
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
// Initial logo display
screen.loadPalette("intro1.pal");
screen.loadBackground("logobak.raw");
screen.saveBackground();
screen.update();
screen.fadeIn(128);
SpriteResource logo[2] = {
SpriteResource("logo.vga"), SpriteResource("logo1.vga")
};
sound.playFX(1);
for (int idx = 0; idx < 80; ++idx) {
screen.restoreBackground();
logo[idx / 65].draw(0, idx % 65);
screen.update();
switch (idx) {
case 37:
sound.playFX(0);
sound.playFX(53);
sound.playSound("fire.voc");
break;
case 52:
case 60:
sound.playFX(3);
break;
case 64:
sound.playFX(2);
break;
case 66:
sound.playFX(52);
sound.playSound("meangro&.voc");
break;
default:
break;
}
WAIT(2);
}
screen.restoreBackground();
screen.update();
WAIT(30);
screen.fadeOut(8);
logo[0].clear();
logo[1].clear();
return true;
}
bool CloudsCutscenes::showCloudsIntroInner() {
EventsManager &events = *_vm->_events;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
Windows &windows = *_vm->_windows;
SpriteResource stars("stars.vga"), intro1("intro1.vga"),
lake("lake.vga"), xeen("xeen.vga"), wizTower("wiztower.vga"),
wizTower2("wiztwer2.vga"), lake2("lake2.vga"), lake3("lake3.vga"),
xeen1("xeen1.vga");
// Show the production splash screen
sound.playSong("mm4theme.m");
screen.loadBackground("jvc.raw");
screen.loadPalette("mm4.pal");
screen.update();
screen.fadeIn();
WAIT(30);
screen.fadeOut(8);
// Clouds of Xeen title with vertically scrolling background
screen.loadPalette("intro.pal");
screen.loadBackground("blank.raw");
screen.saveBackground();
stars.draw(0, 0);
stars.draw(0, 1, Common::Point(160, 0));
screen.loadPage(0);
intro1.draw(0, 0);
screen.loadPage(1);
bool fadeFlag = true;
for (int yCtr = SCREEN_HEIGHT, yScroll = 0, xeenCtr = -1; yCtr > 0; --yCtr, ++yScroll) {
screen.vertMerge(yScroll);
if (yCtr < 160) {
xeen.draw(0, 0);
}
if (yCtr < 100) {
xeen.draw(0, 0);
if (++xeenCtr < 14)
xeen1.draw(0, xeenCtr);
}
screen.update();
if (fadeFlag) {
screen.fadeIn();
fadeFlag = false;
}
WAIT(1);
}
// Remainder of vertical scrolling of background
screen.restoreBackground();
intro1.draw(0, 0);
screen.loadPage(0);
lake.draw(0, 0);
screen.loadPage(1);
bool drawFlag = false;
for (int yCtr = SCREEN_HEIGHT - 1, yScroll = 1, lakeCtr = 0; yCtr > 0; --yCtr, ++yScroll) {
screen.vertMerge(yScroll);
if (yCtr < 95) {
if (++lakeCtr >= 44 || drawFlag) {
lakeCtr = 0;
drawFlag = true;
} else {
lake3.draw(0, lakeCtr, Common::Point(0, yCtr));
}
}
xeen.draw(0, 0);
screen.update();
WAIT(1);
}
screen.freePages();
// Flying creatures moving horizontally
lake.draw(0, 0);
screen.saveBackground();
int frameNum = 0;
events.updateGameCounter();
for (int idx = 0; idx < 100; ++idx) {
frameNum = (frameNum + 1) % 43;
screen.restoreBackground();
lake2.draw(0, frameNum, Common::Point(0, 0), SPRFLAG_800);
WAIT(1);
}
// Zoom in on a closeup of the wizardry tower
const int XLIST1[16] = { 0, 5, 10, 15, 20, 25, 30, 35, 40, 44, 48, 52, 56, 60, 64, 68 };
const int XLIST2[16] = { 160, 155, 150, 145, 140, 135, 130, 125, 120, 114, 108, 102, 96, 90, 84, 78 };
const int YLIST[23] = { 0, 6, 12, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 51 };
for (int idx = 15; idx >= 0; --idx) {
events.updateGameCounter();
screen.restoreBackground();
lake2.draw(0, frameNum, Common::Point(0, 0), SPRFLAG_800);
frameNum = (frameNum + 1) % 43;
wizTower.draw(0, 0, Common::Point(XLIST1[idx], YLIST[idx]), 0, idx);
wizTower.draw(0, 1, Common::Point(XLIST2[idx], YLIST[idx]), 0, idx);
screen.update();
WAIT(1);
}
// Cloaked figure walks horizontally
wizTower.draw(0, 0);
wizTower.draw(0, 1, Common::Point(160, 0));
screen.saveBackground();
for (int idx = 0; idx < 39; ++idx) {
screen.restoreBackground();
wizTower2.draw(0, idx);
screen.update();
WAIT(2);
}
screen.fadeOut();
lake2.clear();
lake3.clear();
xeen1.clear();
// All the lines whilst the scroll is open
SpriteResource groupo("groupo.vga"), group("group.vga"),
crodo("crodo.vga"), box("box.vga");
groupo.draw(0, 0);
groupo.draw(0, 1, Common::Point(160, 0));
crodo.draw(0, 0, Common::Point(0, -5));
windows[0].writeString(Res.CLOUDS_INTRO1);
// Unroll a scroll
if (doScroll(false, true))
return false;
sound.setMusicPercent(60);
screen.restoreBackground();
screen.update();
_subtitles.setLine(0);
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
// Loop through each spoken line
int ctr1 = 0, ctr2 = 0, ctr3 = 0, ctr4 = 0, ctr5 = 0, totalCtr = 0;
for (int lineCtr = 0; lineCtr < 14; ++lineCtr) {
if (lineCtr != 6 && lineCtr != 7) {
// Set subtitle to display (presuming subtitles are turned on)
switch (lineCtr) {
case 0:
_subtitles.setLine(0);
break;
case 1:
_subtitles.setLine(1);
break;
case 5:
_subtitles.setLine(2);
break;
case 11:
_subtitles.setLine(3);
break;
default:
break;
}
// Play the next sample
sound.playVoice(_INTRO_VOCS[lineCtr]);
}
events.timeMark1();
for (int frameCtr = 0, lookup = 0; sound.isSoundPlaying() ||
(_subtitles.active() && (lineCtr == 0 || lineCtr == 4 || lineCtr == 10 || lineCtr == 13)); ) {
groupo.draw(0, 0);
groupo.draw(0, 1, Common::Point(160, 0));
switch (lineCtr) {
case 2:
ctr1 = (ctr1 + 1) % 5;
group.draw(0, ctr1);
ctr4 = (ctr4 + 1) % 9;
break;
case 4:
ctr4 = (ctr4 + 1) % 9 + 9;
break;
case 8:
case 12:
ctr2 = (ctr2 + 1) % 3;
ctr4 = (ctr4 + 1) % 9;
ctr3 = (ctr3 + 1) % 6 + 3;
break;
case 9:
case 13:
ctr3 = (ctr3 + 1) % 3;
group.draw(0, ctr3 + 43, Common::Point(178, 134));
ctr4 = (ctr4 + 1) % 9;
ctr2 = (ctr2 % 15) + 3;
break;
default:
ctr4 = (ctr4 + 1) % 9;
ctr2 = (ctr2 + 1) % 15 + 3;
ctr3 = (ctr3 + 1) % 6 + 3;
break;
}
group.draw(0, ctr4 + 5, Common::Point(0, 99));
group.draw(0, ctr2 + 24, Common::Point(202, 12));
if ((++totalCtr % 30) == 0)
group.draw(0, 43, Common::Point(178, 134));
switch (lineCtr) {
case 2:
case 4:
case 8:
case 9:
case 12:
case 13: {
crodo.draw(0, 0, Common::Point(0, -5));
windows[0].writeString(Res.CLOUDS_INTRO1, false);
ctr5 = (ctr5 + 1) % 19;
WAIT(1);
continue;
}
default:
crodo.draw(0, frameCtr, Common::Point(0, -5));
if (lookup > 30)
lookup = 30;
frameCtr = _INTRO_FRAMES_VALS[_INTRO_FRAMES_LOOKUP[lineCtr]][lookup];
windows[0].writeString(Res.CLOUDS_INTRO1, false);
ctr5 = (ctr5 + 1) % 19;
break;
}
uint expiry = _INTRO_FRAMES_WAIT[_INTRO_FRAMES_LOOKUP[lineCtr]][lookup];
do {
WAIT(1);
} while (events.timeElapsed1() < expiry || (!sound.isSoundPlaying() && ttsMan && ttsMan->isSpeaking()));
++lookup;
if (!sound._fxOn && lookup > 30)
lookup = 0;
}
if (!sound._fxOn)
lineCtr = 20;
if (lineCtr == 5)
sound.playVoice(_INTRO_VOCS[6]);
else if (lineCtr == 6)
sound.playVoice(_INTRO_VOCS[7]);
}
return true;
}
void CloudsCutscenes::showCloudsEnding(uint finalScore) {
EventsManager &events = *g_vm->_events;
FileManager &files = *g_vm->_files;
Sound &sound = *g_vm->_sound;
bool darkCc = files._ccNum;
files.setGameCc(0);
_subtitles.reset();
_mirror.load("mirror.end");
_mirrBack.load("mirrback.end");
_mergeX = 0;
doScroll(true, false);
if (showCloudsEnding1())
if (showCloudsEnding2())
if (showCloudsEnding3())
if (showCloudsEnding4(finalScore))
showCloudsEnding5();
events.clearEvents();
sound.stopAllAudio();
files.setGameCc(darkCc ? 1 : 0);
if (!g_vm->shouldExit())
doScroll(true, false);
}
bool CloudsCutscenes::showCloudsEnding1() {
FileManager &files = *_vm->_files;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
files._ccNum = false;
files.setGameCc(0);
// Show the castle with swirling clouds and lightning
SpriteResource prec;
prec.load("prec.end");
screen.loadBackground("blank.raw");
screen.loadPalette("mm4e.pal");
loadScreen(Common::Path(Common::String::format("prec00%02u.frm", 1)));
prec.draw(0, 0);
prec.draw(0, 1, Common::Point(160, 0));
screen.fadeIn();
WAIT(15);
sound.playFX(1);
sound.playFX(34);
// Initial animation of vortex & lightning in the sky
for (int idx = 1; idx < 42; ++idx) {
// Load up the background frame of swirling clouds
loadScreen(Common::Path(Common::String::format("prec00%02u.frm", idx)));
// Render castle in front of it
prec.draw(0, 0, Common::Point(0, 0));
prec.draw(0, 1, Common::Point(160, 0));
screen.update();
switch (idx) {
case 8:
case 18:
case 21:
sound.playFX(33);
break;
case 19:
case 25:
sound.playFX(34);
break;
default:
break;
}
WAIT(3);
}
prec.clear();
SpriteResource cast1[7], cast2[7], darkLord[3];
for (int idx = 1; idx < 7; ++idx)
cast1[idx - 1].load(Common::Path(Common::String::format("cast%02d.end", idx)));
for (int idx = 1; idx < 7; ++idx)
cast2[idx - 1].load(Common::Path(Common::String::format("casb%02d.end", idx)));
for (int idx = 1; idx < 4; ++idx)
darkLord[idx - 1].load(Common::Path(Common::String::format("darklrd%d.end", idx)));
// Castle close-up
int cloudsCtr = 1;
for (int idx = 1; idx < 16; ++idx) {
LOAD_VORTEX;
cast1[0].draw(0, 0);
cast2[0].draw(0, 0, Common::Point(0, 100));
WAIT(3);
}
screen.loadPalette("mm4.pal");
screen.fadeIn(0x81);
// Castle gets destroyed / sucked into the vortex
const byte COUNTS1[6] = { 9, 3, 2, 2, 3, 15 };
bool flag = false;
for (int idx1 = 1; idx1 < 7; ++idx1) {
for (int idx2 = 0; idx2 < COUNTS1[idx1 - 1]; ++idx2) {
if (flag && !sound.isSoundPlaying()) {
flag = false;
sound.playFX(34);
} else if (!flag && idx1 == 1 && idx2 == 6) {
flag = true;
sound.playVoice("xeenlaff.voc");
}
switch (cloudsCtr) {
case 0:
case 1:
case 5:
case 9:
case 15:
sound.playFX(34);
break;
case 2:
case 7:
case 10:
case 13:
sound.playFX(33);
break;
default:
break;
}
LOAD_VORTEX;
cast1[idx1 - 1].draw(0, idx2, Common::Point(0, 0));
cast2[idx1 - 1].draw(0, idx2, Common::Point(0, 100));
WAIT(3);
}
}
// Fade in of Alamar
for (int idx = 0; idx < 16; ++idx) {
LOAD_VORTEX;
if (idx < 7)
darkLord[0].draw(0, idx);
else if (idx < 11)
darkLord[1].draw(0, idx - 7);
else
darkLord[2].draw(0, idx - 11);
switch (cloudsCtr - 1) {
case 0:
case 4:
case 8:
case 14:
sound.playFX(34);
break;
case 1:
case 6:
case 9:
case 12:
sound.playFX(33);
break;
default:
break;
}
WAIT(3);
}
sound.setMusicPercent(60);
_subtitles.setLine(11);
// Alamar's monologue
for (int idx = 0; idx < (sound._subtitles ? 4 : 3); ++idx) {
switch (idx) {
case 0:
// You have defeated my general, Lord Xeen
sound.playVoice("dark1.voc");
break;
case 1:
// And foiled my plans to conquer this world
sound.playVoice("dark2.voc");
break;
case 2:
// But the Dark Side will always be mine
sound.playVoice("dark3.voc");
break;
default:
// Laugh
sound.playVoice("darklaff.voc");
sound.setMusicPercent(75);
break;
}
do {
LOAD_VORTEX;
darkLord[2].draw(0, getSpeakingFrame(2, 6));
switch (cloudsCtr - 1) {
case 0:
case 4:
case 8:
case 14:
sound.playFX(34);
break;
case 1:
case 6:
case 9:
case 12:
sound.playFX(33);
break;
default:
break;
}
_subtitles.show();
WAIT(3);
} while (sound.isSoundPlaying() || (idx == 3 && _subtitles.active()));
}
if (!sound._subtitles) {
// Laugh
sound.playVoice("darklaff.voc");
sound.setMusicPercent(75);
}
// Alamar fade out
for (int idx = 12; idx >= 0; --idx) {
LOAD_VORTEX;
if (idx < 7)
darkLord[0].draw(0, idx);
else if (idx < 11)
darkLord[1].draw(0, idx - 7);
else
darkLord[2].draw(0, idx - 11);
switch (cloudsCtr - 1) {
case 0:
case 4:
case 8:
case 14:
sound.playFX(34);
break;
case 1:
case 6:
case 9:
case 12:
sound.playFX(33);
break;
default:
break;
}
WAIT(3);
}
sound.stopSound();
sound.playSong("endgame.m");
screen.fadeOut();
return true;
}
bool CloudsCutscenes::showCloudsEnding2() {
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
SpriteResource king("king.end"), people("people.end"), crodo("crodo.end"),
kingCord("kingcord.end");
// Later at Castle Burlock
screen.loadPalette("endgame.pal");
screen.loadBackground("later.raw");
screen.fadeIn();
WAIT(100);
screen.fadeOut();
// Horizontal pan to the right within throne room
screen.loadBackground("throne1.raw");
screen.loadPage(0);
screen.loadBackground("throne2.raw");
screen.loadPage(1);
int xp2 = SCREEN_WIDTH;
bool fadeFlag = true;
for (int ctr = SCREEN_WIDTH, xp1 = 117, xp3 = 0; ctr > 0; --ctr, xp1 -= 2, ++xp3) {
screen.horizMerge(xp3);
people.draw(0, 0, Common::Point(xp1, 68), SPRFLAG_800);
if (xp3 > 250) {
crodo.draw(0, 0, Common::Point(xp2, 68), SPRFLAG_800);
xp2 -= 2;
if (xp2 < 181)
xp2 = 181;
}
if (ctr % 2) {
WAIT(1);
}
if (fadeFlag) {
screen.fadeIn();
fadeFlag = false;
}
}
screen.horizMerge(SCREEN_WIDTH);
crodo.draw(0, 0, Common::Point(xp2, 68), SPRFLAG_800);
screen.freePages();
WAIT(5);
Graphics::ManagedSurface savedBg;
savedBg.create(screen.w, screen.h, screen.format);
savedBg.blitFrom(screen);
// Close up of King Roland
const int XLIST1[13] = { 0, -5, -10, -15, -20, -25, -30, -33, -27, -22, -17 };
const int XLIST2[13] = { 160, 145, 130, 115, 100, 85, 70, 57, 53, 48, 42, 39, 34 };
const int YLIST[13] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4 };
for (int idx = 12; idx >= 0; --idx) {
screen.blitFrom(savedBg);
king.draw(0, 0, Common::Point(XLIST1[idx], YLIST[idx]), 0, idx);
king.draw(0, 1, Common::Point(XLIST2[idx], YLIST[idx]), 0, idx);
WAIT(1);
}
// Congratulations adventurers
const char *const VOC_NAMES[3] = { "king1.voc", "king2.voc", "king3.voc" };
_subtitles.setLine(12);
for (int idx = 0; idx < 3; ++idx) {
sound.playVoice(VOC_NAMES[idx]);
do {
king.draw(0, 0, Common::Point(0, 0));
king.draw(0, 1, Common::Point(160, 0));
int frame = getSpeakingFrame(1, 6);
if (frame > 1)
king.draw(0, frame);
_subtitles.show();
WAIT(3);
} while (sound.isSoundPlaying() || (idx == 2 && _subtitles.active()));
king.draw(0, 0, Common::Point(0, 0));
king.draw(0, 1, Common::Point(160, 0));
WAIT(1);
}
screen.fadeOut();
return true;
}
const byte MONSTER_INDEXES[73] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 76,
23, 16, 17, 80, 19, 20, 83, 22, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 84,
47, 48, 49, 50, 51, 52, 53, 55, 56, 57, 58, 59, 60, 61, 62,
63, 64, 65, 66, 67, 68, 70, 71, 72, 73, 75, 88, 89
};
const int8 XARRAY[8] = { -2, -1, 0, 1, 2, 1, 0, -1 };
const int8 YARRAY[8] = { -2, 0, 2, 0, -1, 0, 2, 0 };
bool CloudsCutscenes::showCloudsEnding3() {
Map &map = *_vm->_map;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
SpriteResource monSprites, attackSprites;
SpriteResource kingCord("kingcord.end"), room("room.end"), bigSky("bigsky.end");
Graphics::ManagedSurface savedBg;
int counter1 = 0;
// Show the mirror room
screen.loadPalette("mirror.pal");
screen.loadBackground("miror-s.raw");
screen.loadPage(0);
screen.loadPage(1);
room.draw(0, 0, Common::Point(0, 0));
room.draw(0, 1, Common::Point(160, 0));
screen.fadeIn();
for (int idx = 0; idx < 83; ++idx) {
screen.horizMerge(idx);
room.draw(0, 0, Common::Point(0, 0));
room.draw(0, 1, Common::Point(160, 0));
WAIT(1);
}
// Zooming into the mirror
screen.freePages();
savedBg.create(screen.w, screen.h, screen.format);
savedBg.blitFrom(screen);
const int XLIST3[9] = { 0, -5, -10, -15, -24, -30, -39, -50, -59 };
const int YLIST3[9] = { 0, 12, 25, 37, 46, 52, 59, 64, 68 };
for (int idx = 8; idx >= 0; --idx) {
screen.blitFrom(savedBg);
bigSky.draw(0, 0, Common::Point(XLIST3[idx], YLIST3[idx]), 0, idx);
_mirrBack.draw(0, 0, Common::Point(XLIST3[idx], YLIST3[idx]), 0, idx);
WAIT(1);
}
// Roland and Crodo moving in to look at mirror
const int DELTA = 2;
for (int idx = 0, xc1 = -115, yp = SCREEN_HEIGHT, xc2 = 335;
idx < 115; idx += DELTA, xc1 += DELTA, yp -= DELTA, xc2 -= DELTA) {
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
kingCord.draw(0, 0, Common::Point(xc1, yp), SPRFLAG_800);
kingCord.draw(0, 1, Common::Point(xc2, yp), SPRFLAG_800);
WAIT(1);
}
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
kingCord.draw(0, 0, Common::Point(0, 85), SPRFLAG_800);
kingCord.draw(0, 1, Common::Point(220, 85), SPRFLAG_800);
// Loop through showing each monster
for (int monsterCtr = 0; monsterCtr < 73; ++monsterCtr) {
MonsterStruct &mon = map._monsterData[MONSTER_INDEXES[monsterCtr]];
monSprites.load(Common::Path(Common::String::format("%03d.mon", mon._imageNumber)));
attackSprites.load(Common::Path(Common::String::format("%03d.att", mon._imageNumber)));
for (int frameCtr = 0; frameCtr < 8; ++frameCtr) {
ROTATE_BG;
counter1 = (counter1 + 1) % 8;
Common::Point monPos(31, 10);
if (mon._flying) {
monPos.x += XARRAY[counter1];
monPos.y += YARRAY[counter1];
}
_mirrBack.draw(0, 0);
monSprites.draw(0, frameCtr, monPos);
_mirror.draw(0, 0);
kingCord.draw(0, 0, Common::Point(0, 85), SPRFLAG_800);
kingCord.draw(0, 1, Common::Point(220, 85), SPRFLAG_800);
WAIT(1);
}
for (int frameCtr = 0; frameCtr < 3; ++frameCtr) {
if (frameCtr == 2)
sound.playVoice(Common::Path(Common::String::format("%s.voc", mon._attackVoc.c_str())));
ROTATE_BG;
counter1 = (counter1 + 1) % 8;
Common::Point monPos(31, 10);
if (mon._flying) {
monPos.x += XARRAY[counter1];
monPos.y += YARRAY[counter1];
}
_mirrBack.draw(0, 0);
attackSprites.draw(0, frameCtr, monPos);
_mirror.draw(0, 0);
kingCord.draw(0, 0, Common::Point(0, 85), SPRFLAG_800);
kingCord.draw(0, 1, Common::Point(220, 85), SPRFLAG_800);
WAIT(1);
}
for (int idx = 0; idx < 15; ++idx) {
ROTATE_BG;
counter1 = (counter1 + 1) % 8;
Common::Point monPos(31, 10);
if (mon._flying) {
monPos.x += XARRAY[counter1];
monPos.y += YARRAY[counter1];
}
_mirrBack.draw(0, 0);
attackSprites.draw(0, 2, monPos);
_mirror.draw(0, 0);
kingCord.draw(0, 0, Common::Point(0, 85), SPRFLAG_800);
kingCord.draw(0, 1, Common::Point(220, 85), SPRFLAG_800);
WAIT(1);
}
int powNum = getSpeakingFrame(0, 5);
sound.stopSound();
sound.playSound(Common::Path(Common::String::format("pow%d.voc", powNum)));
for (int idx = 0; idx < 7; ++idx) {
ROTATE_BG;
counter1 = (counter1 + 1) % 8;
Common::Point monPos(31, 10);
if (mon._flying) {
monPos.x += XARRAY[counter1];
monPos.y += YARRAY[counter1];
}
_mirrBack.draw(0, 0);
attackSprites.draw(0, 2, monPos);
_mirror.draw(0, 0);
kingCord.draw(0, 0, Common::Point(0, 85), SPRFLAG_800);
kingCord.draw(0, 1, Common::Point(220, 85), SPRFLAG_800);
WAIT(1);
}
}
doScroll(true, false);
return true;
}
bool CloudsCutscenes::showCloudsEnding4(uint finalScore) {
EventsManager &events = *_vm->_events;
Screen &screen = *_vm->_screen;
Windows &windows = *_vm->_windows;
SpriteResource endText("endtext.end");
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
doScroll(false, false);
// Congratulations your final score
for (int idx = 0; idx < 19; ++idx) {
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
endText.draw(0, idx);
WAIT(1);
}
// Random animation of score numbers
int frames[10];
const int FRAMEX[10] = { 64, 83, 102, 121, 140, 159, 178, 197, 216, 235 };
for (int idx1 = 0; idx1 < 30; ++idx1) {
for (int idx2 = 0; idx2 < 10; ++idx2)
frames[idx2] = getSpeakingFrame(20, 29);
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
endText.draw(0, 19);
for (int idx2 = 0; idx2 < 10; ++idx2)
endText.draw(0, frames[idx2], Common::Point(FRAMEX[idx2], 73));
WAIT(2);
}
// Animate changing the score digits to the actual final score
Common::String scoreStr = Common::String::format("%.10u", finalScore);
for (int idx1 = 0; idx1 < 10; ++idx1) {
for (int idx2 = 0; idx2 < 10; ++idx2)
frames[idx2] = getSpeakingFrame(20, 29);
for (int idx2 = 0; idx2 <= idx1; ++idx2)
frames[9 - idx2] = (byte)scoreStr[9 - idx2] - 28;
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
endText.draw(0, 19);
for (int idx2 = 0; idx2 < 10; ++idx2)
endText.draw(0, frames[idx2], Common::Point(FRAMEX[idx2], 73));
WAIT(2);
}
// Move the score vertically down
for (int idx1 = 0; idx1 < 38; ++idx1) {
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
endText.draw(0, 19);
for (int idx2 = 0; idx2 < 10; ++idx2)
endText.draw(0, frames[idx2], Common::Point(FRAMEX[idx2], 73 + idx1));
WAIT(1);
}
// Show two screens worth of text, with prompt to press a key
windows[28].setBounds(Common::Rect(63, 60, 254, 160));
for (int idx = 1; idx <= 2; ++idx) {
events.clearEvents();
do {
ROTATE_BG;
_mirrBack.draw(0, 0);
_mirror.draw(0, 0);
endText.draw(0, 19);
for (int idx2 = 0; idx2 < 10; ++idx2)
endText.draw(0, frames[idx2], Common::Point(FRAMEX[idx2], 110));
windows[28].writeString(idx == 1 ? Res.CLOUDS_CONGRATULATIONS1 :
Res.CLOUDS_CONGRATULATIONS2);
events.updateGameCounter();
events.wait(1, false);
} while (!events.isKeyMousePressed());
}
doScroll(true, false);
screen.fadeOut();
return true;
}
bool CloudsCutscenes::showCloudsEnding5() {
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
SpriteResource king("king.end");
king.draw(0, 0, Common::Point(0, 0));
king.draw(0, 1, Common::Point(160, 0));
screen.fadeIn();
_subtitles.setLine(13);
sound.playVoice("king4.voc");
do {
king.draw(0, 0, Common::Point(0, 0));
king.draw(0, 1, Common::Point(160, 0));
int frame = getSpeakingFrame(1, 6);
if (frame > 1)
king.draw(0, frame);
WAIT(3);
} while (sound.isSoundPlaying() || _subtitles.active());
king.draw(0, 0, Common::Point(0, 0));
king.draw(0, 1, Common::Point(160, 0));
WAIT(1);
return true;
}
void CloudsCutscenes::loadScreen(const Common::Path &name) {
Screen &screen = *_vm->_screen;
File fSrc(name);
byte *destP = (byte *)screen.getPixels();
byte *destEndP = (byte *)destP + SCREEN_WIDTH * SCREEN_HEIGHT;
// Setup reference arrays
#define ARRAY_SIZE 314
#define ARRAY_LAST1 ((ARRAY_SIZE - 1) * 2)
#define ARRAY_LAST2 ((ARRAY_SIZE - 1) * 2 + 1)
#define BUFFER_SIZE 0x1000
uint array2[ARRAY_SIZE * 2], array3[ARRAY_SIZE * 2];
uint array4[ARRAY_SIZE * 3];
byte buffer[BUFFER_SIZE];
for (int idx = 0; idx < ARRAY_SIZE; ++idx) {
array3[idx] = 1;
array4[idx + 627] = idx * 2;
array2[idx] = idx * 2 + (ARRAY_SIZE * 4 - 2);
}
for (int ctr = 0, idx = 0, idx2 = ARRAY_SIZE; ctr < (ARRAY_SIZE - 1); ++ctr, idx += 2, ++idx2) {
array3[idx2] = array3[idx] + array3[idx + 1];
array2[idx2] = idx * 2;
array4[idx] = array4[idx + 1] = idx2 * 2;
}
array4[ARRAY_LAST1] = 0;
array3[ARRAY_LAST2] = (uint)-1;
array2[ARRAY_LAST2] = 4036;
uint16 bits = 0x8000;
// Get the decompressed size and default buffer contents
uint16 bytePair;
fSrc.read((byte *)&bytePair, 2);
Common::fill((uint16 *)buffer, (uint16 *)(buffer + BUFFER_SIZE),
bytePair);
int count = fSrc.readUint16BE();
assert(count == (SCREEN_WIDTH * SCREEN_HEIGHT));
for (int byteIdx = 0; byteIdx < count; ) {
assert(fSrc.pos() < fSrc.size());
int vMin = array2[(ARRAY_SIZE - 1) * 2];
int vThreshold = ARRAY_SIZE * 4 - 2;
while (vMin < vThreshold) {
bool flag = (bits & 0x8000);
bits <<= 1;
if (!bits) {
bits = fSrc.readUint16BE();
flag = (bits & 0x8000);
bits = (bits << 1) | 1;
}
vMin = array2[vMin / 2 + (flag ? 1 : 0)];
}
vMin -= vThreshold;
if (array3[ARRAY_LAST1] == 0x8000) {
for (int ctr = 0, ctr2 = 0; ctr < (ARRAY_SIZE * 2); ++ctr) {
if (array2[ctr] >= (ARRAY_SIZE * 4 - 1)) {
array3[ctr2] = (array3[ctr] + 1) / 2;
array2[ctr2] = array2[ctr];
++ctr2;
}
}
for (int ctr = 0, ctr2 = ARRAY_SIZE; ctr < ARRAY_SIZE; ctr += 2, ++ctr2) {
int currVal = array3[ctr] + array3[ctr + 1];
array3[ctr2] = currVal;
int ctr3 = ctr2;
do {
--ctr3;
} while (array3[ctr3] >= array3[ctr2]);
++ctr3;
int diff = ctr2 - ctr3;
uint *pDest = &array3[ctr2];
Common::copy(pDest - 1, pDest - 1 + diff, pDest);
array3[ctr3] = currVal;
pDest = &array2[ctr2];
Common::copy(pDest - 1, pDest - 1 + diff, pDest);
array2[ctr3] = ctr * 2;
}
uint *arrEndP = &array4[ARRAY_SIZE * 2 - 1];
for (int ctr = 0, val = 0; ctr < ARRAY_SIZE * 2; ++ctr, val += 2) {
uint *arrP = &array4[array2[ctr] / 2];
if (arrP < arrEndP)
*arrP = val;
}
}
int offset = array4[627 + vMin / 2] / 2;
do {
int offset2 = offset;
uint val = ++array3[offset2];
if (val > array3[offset2 + 1]) {
while (val > array3[++offset2])
;
--offset2;
array3[offset] = array3[offset2];
array3[offset2] = val;
int offset3 = array2[offset] / 2;
array4[offset3] = offset2 * 2;
if ((offset3 * 2) < (ARRAY_SIZE * 4 - 2))
array4[offset3 + 1] = offset2 * 2;
int offset4 = array2[offset2] / 2;
array2[offset2] = offset3 * 2;
array4[offset4] = offset * 2;
if ((offset4 * 2) < (ARRAY_SIZE * 4 - 2))
array4[offset4 + 1] = offset * 2;
array2[offset] = offset4 * 2;
offset = offset2;
}
} while ((offset = array4[offset] / 2) != 0);
vMin /= 2;
if (vMin < 256) {
// Single byte write to destination
*destP++ = (byte)vMin;
int buffOffset = array2[ARRAY_LAST2];
array2[ARRAY_LAST2] = (buffOffset + 1) & 0xfff;
buffer[buffOffset] = (byte)vMin;
++byteIdx;
continue;
}
uint16 bitsLow = bits, bitsHigh = 0;
for (int ctr = 8; ctr > 0; --ctr) {
bool highBit = bitsLow & 0x8000;
bitsLow <<= 1;
if (bitsLow) {
bitsHigh = (bitsHigh << 1) | (highBit ? 1 : 0);
} else {
bitsLow = fSrc.readUint16BE();
byte loBit = 1;
do {
bitsHigh = (bitsHigh << 1) | ((bitsLow & 0x8000) ? 1 : 0);
bitsLow = (bitsLow << 1) | (loBit ? 1 : 0);
loBit = 0;
} while (--ctr > 0);
break;
}
}
bits = bitsLow;
int t2Val = _DECODE_TABLE2[bitsHigh] << 6;
int tCount = _DECODE_TABLE1[bitsHigh] - 2;
for (int ctr = 0; ctr < tCount; ++ctr) {
bool highBit = bits & 0x8000;
bits <<= 1;
if (!bits) {
bits = fSrc.readUint16BE();
highBit = bits & 0x8000;
bits = (bits << 1) | 1;
}
bitsHigh = (bitsHigh << 1) | (highBit ? 1 : 0);
}
t2Val |= (bitsHigh & 0x3F);
uint &last2 = array2[ARRAY_LAST2];
int buffOffset = last2 - t2Val - 1;
for (int ctr = 0; ctr < vMin - 253; ++ctr, ++buffOffset) {
buffOffset &= 0xfff;
byte b = buffer[buffOffset];
*destP++ = b;
buffer[last2] = b;
last2 = (last2 + 1) & 0xfff;
++byteIdx;
}
}
assert(destP == destEndP);
screen.markAllDirty();
}
const char *const CloudsCutscenes::_INTRO_VOCS[14] = {
"crodo1.voc", "crodo2.voc", "iamking.voc", "crodo3.voc",
"ya1.voc", "crodo4a.voc", "crodo4b.voc", "crodo4c.voc",
"xeenlaff.voc", "tiger2&.voc", "crodo5.voc", "crodo6.voc",
"xeenlaff.voc", "tiger2&.voc"
};
const int CloudsCutscenes::_INTRO_FRAMES_LOOKUP[14] = {
0, 1, 0, 2, 0, 3, 4, 5, 0, 0, 6, 7, 0, 0
};
const int CloudsCutscenes::_INTRO_FRAMES_VALS[8][32] = {
{
4, 2, 3, 0, 2, 3, 2, 0, 1, 1, 3, 4, 3, 2, 4, 2,
3, 4, 3, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, {
3, 2, 3, 2, 4, 3, 0, 3, 2, 2, 3, 1, 2, 3, 3, 3,
2, 3, 2, 3, 2, 0, 3, 2, 0, 0, 0, 0, 0, 0, 2, 4
}, {
3, 1, 2, 3, 0, 3, 4, 3, 2, 3, 0, 3, 2, 3, 2, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3
}, {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 3
}, {
4, 2, 2, 3, 2, 3, 3, 4, 2, 4, 2, 0, 3, 2, 3, 2,
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 2, 3
}, {
2, 0, 2, 3, 2, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 3, 2, 3, 1
}, {
3, 2, 0, 2, 4, 2, 3, 2, 3, 2, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 3, 4, 0, 2
}, {
3, 2, 4, 1, 2, 4, 3, 2, 3, 0, 2, 2, 0, 3, 2, 3,
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}
};
const uint CloudsCutscenes::_INTRO_FRAMES_WAIT[8][32] = {
{
2, 5, 6, 9, 10, 11, 12, 13, 14, 23, 25, 29, 31, 35, 38, 41,
42, 45, 50, 52, 55, 56, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0
}, {
1, 4, 6, 8, 9, 11, 13, 15, 17, 18, 19, 22, 28, 29, 30, 31,
0, 39, 0, 44, 0, 50, 51, 0, 54, 0, 0, 0, 0, 0, 0, 4
}, {
6, 9, 11, 13, 15, 19, 21, 23, 25, 27, 28, 31, 35, 39, 40, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 7
}, {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 4
}, {
5, 9, 10, 11, 13, 15, 18, 23, 26, 31, 33, 36, 37, 41, 43, 45,
48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 12
}, {
14, 17, 20, 23, 27, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 8, 11, 13
}, {
15, 16, 17, 19, 21, 24, 24, 27, 34, 35, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 10, 11, 13
}, {
17, 19, 22, 23, 26, 30, 32, 34, 40, 43, 47, 52, 53, 55, 57, 60,
62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}
};
const byte CloudsCutscenes::_DECODE_TABLE1[256] = {
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
};
const byte CloudsCutscenes::_DECODE_TABLE2[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11,
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19,
20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23,
24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31,
32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39,
40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63
};
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM