Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
/* 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/>.
*
*/
#ifndef XEEN_WORLDOFXEEN_CLOUDS_CUTSCENES_H
#define XEEN_WORLDOFXEEN_CLOUDS_CUTSCENES_H
#include "mm/xeen/cutscenes.h"
#include "mm/xeen/subtitles.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
class CloudsCutscenes : public Cutscenes {
private:
static const char *const _INTRO_VOCS[14];
static const int _INTRO_FRAMES_LOOKUP[14];
static const int _INTRO_FRAMES_VALS[8][32];
static const uint _INTRO_FRAMES_WAIT[8][32];
static const byte _DECODE_TABLE1[256];
static const byte _DECODE_TABLE2[256];
private:
Subtitles _subtitles;
SpriteResource _mirror, _mirrBack;
int _mergeX;
private:
void loadScreen(const Common::Path &name);
/**
* Shows the Clouds of Xeen title screen
*/
bool showCloudsTitle();
/**
* Inner implementation of the Clouds of Xeen intro sequence
*/
bool showCloudsIntroInner();
/**
* Shows part 1 of the Clouds of Xeen ending, with the castle being destroyed
*/
bool showCloudsEnding1();
/**
* Shows part 2 of the Clouds of Xeen ending, King Roland's throneroom
*/
bool showCloudsEnding2();
/**
* Shows part 3 of the Clouds of Xeen ending, which shows a display
* of the game's monsters
*/
bool showCloudsEnding3();
/**
* Shows part 4 of the Clouds of Xeen ending, the final score
*/
bool showCloudsEnding4(uint finalScore);
/**
* Shows part 5 of the Clouds of Xeen ending, final king display
*/
bool showCloudsEnding5();
public:
CloudsCutscenes(XeenEngine *vm) : Cutscenes(vm), _mergeX(0) {}
/**
* Shows the Clouds of Xeen intro sequence
*/
bool showCloudsIntro();
/**
* Shows the Clouds of Xeen ending sequence
*/
void showCloudsEnding(uint finalScore);
};
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,131 @@
/* 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/>.
*
*/
#ifndef XEEN_WORLDOFXEEN_DARKSIDE_CUTSCENES_H
#define XEEN_WORLDOFXEEN_DARKSIDE_CUTSCENES_H
#include "mm/xeen/cutscenes.h"
namespace MM {
namespace Xeen {
class XeenEngine;
namespace WorldOfXeen {
class DarkSideCutscenes : public Cutscenes {
private:
Subtitles _subtitles;
SpriteResource _ball, _claw, _dragon1;
private:
/**
* Initial animation of the Dragon Pharoah moving his hands to the
* crystal ball, and then moving them back again
* @param fadeIn If set, fades in screen after first frame
*/
bool rubCrystalBall(bool fadeIn = false);
/**
* Animates the dragon pharoah
* @param frame Animation frame number
* @param showBall Flag whether to draw the crystal ball
*/
void animatePharoah(int frame, bool showBall = true);
/**
* Shows the Darkside of Xeen title screen
*/
bool showDarkSideTitle(bool seenIntro);
/**
* Shows part 1 of the Dark Side intro - up to the point where
* Dragon Pharoah ends "contact the Queen"
*/
bool showDarkSideIntro1();
/**
* Shows part 2 of the Dark Side intro
*/
bool showDarkSideIntro2();
/**
* Shows part 3 of the Dark Side intro
*/
bool showDarkSideIntro3();
/**
* Shows the World of Xeen logo from the end of the Dark Side intro
*/
bool showWorldOfXeenLogo();
/**
* Shows part 1 of the Dark Side ending, everything up to Corak appearing
*/
bool showDarkSideEnding1();
/**
* Shows part 2 of the Dark Side ending, exposition until start of fighting
*/
bool showDarkSideEnding2();
/**
* Shows part 3 of the Dark Side ending, the fighting
*/
bool showDarkSideEnding3();
/**
* Shows part 4 of the Dark Side ending, the self-destruct and castle explosion
*/
bool showDarkSideEnding4();
/**
* Last part of the Dark Side ending, shows the final score and saves the game/
*/
void showDarkSideScore(uint endingScore);
/**
* Shows the Pharaoh ending screen where score text is shown
*/
bool showPharaohEndTextInner(const char *msg1, const char *msg2, const char *msg3);
protected:
/**
* Shows the Pharaoh ending screen where score text is shown
*/
bool showPharaohEndText(const char *msg1, const char *msg2 = nullptr, const char *msg3 = nullptr);
public:
DarkSideCutscenes(XeenEngine *vm) : Cutscenes(vm) {}
/**
* Shows the Darkside of Xeen intro sequence
*/
bool showDarkSideIntro(bool seenIntro);
/**
* Shows the Darkside of Xeen ending sequence
*/
void showDarkSideEnding(uint endingScore);
};
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,201 @@
/* 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/worldofxeen.h"
#include "mm/xeen/worldofxeen/darkside_cutscenes.h"
#include "mm/xeen/worldofxeen/clouds_cutscenes.h"
#include "mm/xeen/worldofxeen/worldofxeen_menu.h"
#include "mm/xeen/sound.h"
#include "common/config-manager.h"
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
WorldOfXeenEngine::WorldOfXeenEngine(OSystem *syst, const MightAndMagicGameDescription *gameDesc)
: XeenEngine(syst, gameDesc), WorldOfXeenCutscenes(this) {
}
void WorldOfXeenEngine::death() {
Window &w = (*_windows)[0];
_sound->stopAllAudio();
SpriteResource fireSprites[4] = {
SpriteResource("fire1.vga"),
SpriteResource("fire2.vga"),
SpriteResource("fire3.vga"),
SpriteResource("fire4.vga")
};
SpriteResource deathSprites("death.vga"), death1Sprites("death1.vga");
const int Y_LIST[] = {
196, 187, 179, 169, 159, 147, 138, 127, 113, 101, 86,
73, 60, 48, 36, 23, 10, 0, 0
};
Graphics::ManagedSurface savedBg;
savedBg.copyFrom(*_screen);
fireSprites[0].draw(0, 0, Common::Point(0, 0));
fireSprites[0].draw(0, 1, Common::Point(160, 0));
w.update();
_sound->playSound("fire.voc");
// Fire will vertically consume the screen
for (int idx = 2; idx < 36; idx += 2) {
_events->updateGameCounter();
_screen->blitFrom(savedBg);
fireSprites[idx / 10].draw(0, idx % 10, Common::Point(0, 0));
fireSprites[idx / 10].draw(0, (idx % 10) + 1, Common::Point(160, 0));
for (int yCtr = 0, frame = 0; yCtr < (idx / 2); ++yCtr, frame += 2) {
deathSprites.draw(0, frame, Common::Point(0, Y_LIST[yCtr]));
deathSprites.draw(0, frame + 1, Common::Point(160, Y_LIST[yCtr]));
}
w.update();
_events->wait(1, false);
}
deathSprites.draw(0, 34, Common::Point(0, 0));
deathSprites.draw(0, 35, Common::Point(160, 0));
w.update();
savedBg.blitFrom(*_screen);
_sound->playSound(_files->_ccNum ? "laff1.voc" : "xeenlaff.voc", _files->_ccNum, 0);
// Animation of Xeen or Alamar laughing
for (int idx = 0, idx2 = 0; idx < (_files->_ccNum ? 10 : 23); ++idx) {
_events->updateGameCounter();
_screen->blitFrom(savedBg);
if (idx != 0)
death1Sprites.draw(0, idx - 1);
w.update();
if (_files->_ccNum) {
_events->wait(2, false);
} else {
if (idx == 1 || idx == 11)
_sound->playFX(33);
_events->wait(2, false);
if (idx == 15)
_sound->playFX(34);
}
if (idx == (_files->_ccNum ? 9 : 10)) {
if (idx2 < (_files->_ccNum ? 2 : 1)) {
idx = -1;
++idx2;
}
}
if (!_sound->isSoundPlaying())
idx = 23;
}
while (_sound->isSoundPlaying())
_events->wait(1, false);
_screen->blitFrom(savedBg);
w.update();
}
void WorldOfXeenEngine::dream() {
if (g_vm->getGameID() == GType_Clouds)
// Dreams only available for Dark Side & World
return;
Windows &windows = *_windows;
Graphics::ManagedSurface savedBg;
savedBg.copyFrom(*_screen);
_screen->fadeOut();
_events->hideCursor();
_screen->loadBackground("scene1.raw");
windows[0].update();
_screen->fadeIn();
_events->updateGameCounter();
while (!shouldExit() && _events->timeElapsed() < 7)
_events->pollEventsAndWait();
_sound->playSound("dreams2.voc", 1, 0);
while (!shouldExit() && _sound->isSoundPlaying())
_events->pollEventsAndWait();
_sound->playSound("laff1.voc", 1, 0);
while (!shouldExit() && _sound->isSoundPlaying())
_events->pollEventsAndWait();
_events->updateGameCounter();
while (!shouldExit() && _events->timeElapsed() < 7)
_events->pollEventsAndWait();
_screen->fadeOut();
_events->setCursor(0);
_screen->blitFrom(savedBg);
windows[0].update();
_screen->fadeIn();
}
void WorldOfXeenEngine::showCutscene(const Common::String &name, int status, uint score) {
_sound->stopAllAudio();
_events->clearEvents();
if (name == "ENDGAME")
showCloudsEnding(score);
else if (name == "ENDGAME2")
showDarkSideEnding(score);
else if (name == "WORLDEND")
showWorldOfXeenEnding((GooberState)status, score);
_screen->freePages();
_sound->stopAllAudio();
_events->clearEvents();
_gameMode = GMODE_MENU;
}
void WorldOfXeenEngine::showStartup() {
bool seenIntro = ConfMan.hasKey("seen_intro") && ConfMan.getBool("seen_intro");
bool completedIntro;
if (getGameID() == GType_Clouds)
completedIntro = showCloudsIntro();
else
completedIntro = showDarkSideIntro(seenIntro);
if (!seenIntro && completedIntro) {
ConfMan.setBool("seen_intro", true);
ConfMan.flushToDisk();
}
_gameMode = GMODE_MENU;
}
void WorldOfXeenEngine::showMainMenu() {
WorldOfXeenMainMenuContainer::show();
}
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,74 @@
/* 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/>.
*
*/
#ifndef XEEN_WORLDOFXEEN_WORLDOFXEEN_H
#define XEEN_WORLDOFXEEN_WORLDOFXEEN_H
#include "mm/xeen/xeen.h"
#include "mm/xeen/worldofxeen/worldofxeen_cutscenes.h"
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
/**
* Implements a descendant of the base Xeen engine to handle
* Clouds of Xeen, Darkside of Xeen, and Worlds of Xeen specific
* game code
*/
class WorldOfXeenEngine: public XeenEngine, public WorldOfXeenCutscenes {
protected:
/**
* Show the starting sequence/intro
*/
void showStartup() override;
/**
* Show the startup menu
*/
void showMainMenu() override;
/**
* Death cutscene
*/
void death() override;
public:
WorldOfXeenEngine(OSystem *syst, const MightAndMagicGameDescription *gameDesc);
~WorldOfXeenEngine() override {}
/**
* Show a cutscene
*/
void showCutscene(const Common::String &name, int status, uint score) override;
/**
* Dream sequence
*/
void dream() override;
};
#define WOX_VM (*(::MM::Xeen::WorldOfXeen::WorldOfXeenEngine *)g_vm)
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,699 @@
/* 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/worldofxeen_cutscenes.h"
#include "mm/xeen/sound.h"
#include "mm/xeen/xeen.h"
#define WAIT(TIME) if (_subtitles.wait(TIME)) return false
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
void WorldOfXeenCutscenes::showWorldOfXeenEnding(GooberState state, uint score) {
FileManager &files = *_vm->_files;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
files.setGameCc(2);
_goober = state;
_finalScore = score;
screen.loadPalette("skymain.pal");
if (worldEnding1())
if (worldEnding2())
worldEnding3();
sound.stopAllAudio();
screen.fadeOut();
screen.freePages();
files.setGameCc(1);
}
bool WorldOfXeenCutscenes::worldEnding1() {
EventsManager &events = *_vm->_events;
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
Windows &windows = *_vm->_windows;
Window &w0 = windows[0];
Graphics::ManagedSurface savedBg(SCREEN_WIDTH, SCREEN_HEIGHT);
SpriteResource::setClippedBottom(185);
sound.playSong("outday3.m");
if (!showPharaohEndText(Res.WORLD_END_TEXT[0]))
return false;
screen.loadBackground("skymain.raw");
savedBg.blitFrom(screen);
screen.loadBackground("twrsky1.raw");
screen.loadPage(0);
screen.loadPage(1);
SpriteResource sc02("sc02.eg2"), tower1("tower1.eg2"), tower2("tower2.eg2"),
sc3a("sc3a.eg2"), sc06("sc06.eg2"), sc14("sc14.eg2"), sc13("sc13.eg2"),
sc17("sc17.eg2"), cube("cube.eg2"), hands("hands.eg2"),
sc10("sc10.eg2"), staff("staff.eg2");
SpriteResource sc3b[2] = {
SpriteResource("sc3b1.eg2"), SpriteResource("sc3b2.eg2")
};
SpriteResource sc20[4] = {
SpriteResource("sc20a.eg2"), SpriteResource("sc20b.eg2"),
SpriteResource("sc20c.eg2"), SpriteResource("sc20d.eg2")
};
SpriteResource sc22[2] = {
SpriteResource("sc22a.eg2"), SpriteResource("sc22b.eg2")
};
// Fade out the screen and the music
sound.songCommand(223);
windows[41].writeString("\x1\xD");
screen.fadeOut();
while (!_vm->shouldExit() && sound.isSoundPlaying())
events.pollEventsAndWait();
// And so the call went out to the people through the lands of Xeen that the
// prophecy was nearing completion.
sound.playSong("nwblksmt.m");
screen.blitFrom(savedBg);
setSubtitle(Res.WORLD_END_TEXT[1]);
w0.update();
screen.fadeIn();
WAIT(60);
// Dragon approaching tower in distance
for (int idx = 0; idx < 50; ++idx) {
if (idx == 9 || idx == 22 || idx == 33 || idx == 44)
sound.playSound("whoosh.voc");
screen.blitFrom(savedBg);
sc02.draw(0, idx);
setSubtitle(Res.WORLD_END_TEXT[1]);
w0.update();
WAIT(2);
}
// Dragon landing on tower
for (int idx = 0; idx < 40; ++idx) {
screen.horizMerge();
tower1.draw(0, 0, Common::Point(0, 0), SPRFLAG_800);
sc3a.draw(0, idx, Common::Point(91, 86), SPRFLAG_800);
w0.update();
WAIT(2);
}
// They came in great numbers to witness the momentous occasion
int frame = 40, frame2 = 0;
for (int idx = 0, xp = 0; idx < SCREEN_WIDTH; idx += 5) {
screen.horizMerge(xp);
tower1.draw(0, 0, Common::Point(idx, 0), SPRFLAG_800);
sc3a.draw(0, frame, Common::Point(idx + 91, 86), SPRFLAG_800);
tower2.draw(0, 0, Common::Point(idx - SCREEN_WIDTH, 0), SPRFLAG_800);
tower2.draw(0, 1, Common::Point(idx - (SCREEN_WIDTH / 2), 0), SPRFLAG_800);
sc3b[frame2 / 30].draw(0, frame2 % 30, Common::Point(idx - 277, 65), SPRFLAG_800);
frame = (frame + 1) % 57;
if (idx > (SCREEN_WIDTH / 2))
frame2 = (frame2 + 1) % 60;
xp -= 2;
if (xp < 1)
xp = SCREEN_WIDTH;
if (idx > 120)
setSubtitle(Res.WORLD_END_TEXT[2]);
w0.update();
WAIT(2);
}
for (; frame2 < 60; ++frame2) {
screen.horizMerge(frame);
tower2.draw(0, 0, Common::Point(0, 0), SPRFLAG_800);
tower2.draw(0, 1, Common::Point(SCREEN_WIDTH / 2, 0), SPRFLAG_800);
sc3b[frame2 / 30].draw(0, frame2 % 30, Common::Point(43, 65), SPRFLAG_800);
setSubtitle(Res.WORLD_END_TEXT[2]);
w0.update();
WAIT(2);
}
screen.freePages();
screen.fadeOut();
screen.loadBackground("foura.raw");
savedBg.blitFrom(screen);
// Crowd sitting down in chamber
sc06.draw(0, 0, Common::Point(26, 75));
w0.update();
screen.fadeIn();
for (int idx = 0; idx < 26; ++idx) {
screen.blitFrom(savedBg);
sc06.draw(0, idx, Common::Point(26, 75));
WAIT(2);
}
// The Dragon Pharoah presided over the ceremony
screen.fadeOut();
screen.loadBackground("eg140001.raw");
savedBg.blitFrom(screen);
w0.update();
setSubtitle(Res.WORLD_END_TEXT[3]);
screen.fadeIn();
for (int idx1 = 0; idx1 < 2; ++idx1) {
for (int idx2 = 0; idx2 < 15; ++idx2) {
screen.blitFrom(savedBg);
sc14.draw(0, idx2, Common::Point(141, 63));
setSubtitle(Res.WORLD_END_TEXT[3]);
w0.update();
WAIT(2);
}
}
// Queen Kalindra presented the Cube of Power
screen.loadBackground("eg100001.raw");
screen.loadPage(0);
savedBg.blitFrom(screen);
for (int idx1 = 0; idx1 < 2; ++idx1) {
for (int idx2 = 0; idx2 < 6; ++idx2) {
screen.blitFrom(savedBg);
sc13.draw(0, idx2, Common::Point(26, 21));
setSubtitle(Res.WORLD_END_TEXT[4]);
w0.update();
WAIT(2);
}
}
// Queen Kalindra lifts up the Cube of Power
screen.blitFrom(savedBg);
sc13.draw(0, 5, Common::Point(26, 21));
savedBg.blitFrom(screen);
setSubtitle(Res.WORLD_END_TEXT[4]);
w0.update();
frame = 0;
for (int idx = 185; idx > 68; idx -= 3) {
if (!sound.isSoundPlaying())
sound.playSound("cast.voc");
screen.blitFrom(savedBg);
sc17.draw(41, 0, Common::Point(33, idx), SPRFLAG_BOTTOM_CLIPPED);
sc17.draw(41, frame, Common::Point(33, idx), SPRFLAG_BOTTOM_CLIPPED);
setSubtitle(Res.WORLD_END_TEXT[4]);
w0.update();
WAIT(2);
// WORKAROUND: sc17.eg2 has 17 frames, but the last is malformed. So skip it
frame = (frame + 1) % 16;
}
for (int idx = 0; idx < 16; ++idx) {
if (!sound.isSoundPlaying())
sound.playSound("cast.voc");
screen.blitFrom(savedBg);
sc17.draw(41, 0, Common::Point(33, 68), SPRFLAG_BOTTOM_CLIPPED);
sc17.draw(41, idx, Common::Point(33, 68), SPRFLAG_BOTTOM_CLIPPED);
setSubtitle(Res.WORLD_END_TEXT[4]);
w0.update();
WAIT(2);
}
screen.blitFrom(savedBg);
sc17.draw(0, 0, Common::Point(33, 68), SPRFLAG_BOTTOM_CLIPPED);
setSubtitle(Res.WORLD_END_TEXT[4]);
w0.update();
screen.fadeOut();
screen.loadBackground("eg140001.raw");
savedBg.blitFrom(screen);
w0.update();
screen.fadeIn();
for (int idx1 = 0; idx1 < 2; ++idx1) {
for (int idx2 = 0; idx2 < 15; ++idx2) {
screen.blitFrom(savedBg);
sc14.draw(0, idx2, Common::Point(141, 63));
w0.update();
WAIT(3);
}
}
// Prince Roland presented the Xeen Sceptre
screen.horizMerge(0);
savedBg.blitFrom(screen);
for (int idx1 = 0; idx1 < 3; ++idx1) {
for (int idx2 = 0; idx2 < 5; ++idx2) {
screen.blitFrom(savedBg);
sc10.draw(0, idx2, Common::Point(26, 21));
setSubtitle(Res.WORLD_END_TEXT[5]);
w0.update();
WAIT(3);
}
}
screen.blitFrom(savedBg);
setSubtitle(Res.WORLD_END_TEXT[5]);
w0.update();
SpriteResource::setClippedBottom(185);
for (int yp = 185; yp > 13; yp -= 6) {
screen.blitFrom(savedBg);
staff.draw(41, 0, Common::Point(196, yp), SPRFLAG_BOTTOM_CLIPPED);
setSubtitle(Res.WORLD_END_TEXT[5]);
w0.update();
WAIT(2);
}
WAIT(30);
screen.fadeOut();
screen.loadBackground("eg140001.raw");
savedBg.blitFrom(screen);
w0.update();
screen.fadeIn();
for (int idx1 = 0; idx1 < 2; ++idx1) {
for (int idx2 = 0; idx2 < 15; ++idx2) {
screen.blitFrom(savedBg);
sc14.draw(0, idx2, Common::Point(141, 63));
w0.update();
WAIT(3);
}
}
// Together, they placed the Cube of Power...
SpriteResource::setClippedBottom(118);
screen.loadBackground("tablmain.raw");
savedBg.blitFrom(screen);
screen.loadPage(1);
sc20[0].draw(0, 0, Common::Point(26, 55));
hands.draw(0, 0, Common::Point(58, 17));
cube.draw(0, 0, Common::Point(101, 11), SPRFLAG_BOTTOM_CLIPPED);
w0.update();
sound.playSound("cast.voc");
for (int idx = 0; idx < 5; ++idx) {
if (!sound.isSoundPlaying())
sound.playSound("cast.voc");
screen.blitFrom(savedBg);
sc20[0].draw(0, 0, Common::Point(26, 55));
hands.draw(0, 0, Common::Point(58, 17));
cube.draw(0, idx, Common::Point(101, 11), SPRFLAG_BOTTOM_CLIPPED);
setSubtitle(Res.WORLD_END_TEXT[6]);
w0.update();
WAIT(2);
}
frame = 0;
for (int idx = 0; idx < 16; ++idx) {
if (!sound.isSoundPlaying())
sound.playSound("cast.voc");
screen.blitFrom(savedBg);
sc20[0].draw(0, 0, Common::Point(26, 55));
hands.draw(0, idx, Common::Point(58, 17));
cube.draw(0, frame, Common::Point(101, 11), SPRFLAG_BOTTOM_CLIPPED);
setSubtitle(Res.WORLD_END_TEXT[6]);
w0.update();
WAIT(2);
frame = (frame + 1) % 5;
}
// Cube lowers into table
frame = 0;
for (int yp = 11; yp < 82; ++yp) {
if (!sound.isSoundPlaying())
sound.playSound("cast.voc");
screen.blitFrom(savedBg);
sc20[0].draw(0, 0, Common::Point(26, 55));
cube.draw(0, frame, Common::Point(101, yp), SPRFLAG_BOTTOM_CLIPPED);
setSubtitle(Res.WORLD_END_TEXT[6]);
w0.update();
WAIT(2);
frame = (frame + 1) % 5;
}
sound.stopSound();
// Table is activating
sound.playSound("click.voc");
sound.playSound("padspell.voc");
for (int idx = 0; idx < 14; ++idx) {
if (idx == 10)
sound.playSound("padspell.voc");
screen.blitFrom(savedBg);
sc20[idx / 7].draw(0, idx % 7, Common::Point(26, 55));
setSubtitle(Res.WORLD_END_TEXT[6]);
w0.update();
WAIT(2);
}
for (int idx = 0; idx < 17; ++idx) {
if (idx == 10)
sound.playSound("padspell.voc");
screen.blitFrom(savedBg);
sc20[2].draw(0, idx, Common::Point(26, 55));
setSubtitle(Res.WORLD_END_TEXT[6]);
w0.update();
WAIT(3);
}
for (int idx = 0; idx < 6; ++idx) {
screen.blitFrom(savedBg);
sc20[3].draw(0, idx, Common::Point(26, 55));
setSubtitle(Res.WORLD_END_TEXT[6]);
w0.update();
WAIT(4);
}
screen.blitFrom(savedBg);
setSubtitle(Res.WORLD_END_TEXT[6]);
w0.update();
WAIT(30);
// And the Scepotre, onto the Altar of Joining
screen.horizMerge(SCREEN_WIDTH);
savedBg.blitFrom(screen);
screen.freePages();
for (int idx = 0; idx < 35; ++idx) {
if (idx == 3)
sound.playSound("click.voc");
else if (idx == 5 || idx == 15)
sound.playSound("elect.voc", 1, 0);
else if (idx == 22)
sound.playSound("explosio.voc");
screen.blitFrom(savedBg);
sc22[idx / 20].draw(0, idx % 20, Common::Point(112, 17));
setSubtitle(Res.WORLD_END_TEXT[7]);
w0.update();
WAIT(3);
}
screen.fadeOut();
return true;
}
bool WorldOfXeenCutscenes::worldEnding2() {
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
Windows &windows = *_vm->_windows;
Window &w0 = windows[0];
Graphics::ManagedSurface savedBg(SCREEN_WIDTH, SCREEN_HEIGHT);
SpriteResource sc23[8] = {
SpriteResource("sc23a.eg2"), SpriteResource("sc23b.eg2"),
SpriteResource("sc23c.eg2"), SpriteResource("sc23d.eg2"),
SpriteResource("sc23e.eg2"), SpriteResource("sc23f.eg2"),
SpriteResource("sc23g.eg2"), SpriteResource("sc23h.eg2")
};
SpriteResource sc24("sc24.eg2");
screen.loadBackground("eg23prt2.raw");
savedBg.blitFrom(screen);
sc23[0].draw(0, 0);
w0.update();
screen.fadeIn();
for (int idx = 0; idx < 61; ++idx) {
if (idx == 2 || idx == 15 || idx == 25 || idx == 33 || idx == 41)
sound.playSound("gascompr.voc");
sc23[idx / 8].draw(0, idx % 8);
w0.update();
WAIT(4);
}
// Far out view of light emerging from tower
screen.loadBackground("skymain.raw");
savedBg.blitFrom(screen);
sc24.draw(0, 0, Common::Point(103, 6));
for (int idx = 0; idx < 35; ++idx) {
if (idx == 2 || idx == 15 || idx == 21)
sound.playSound("photon.voc", 1, 0);
screen.blitFrom(savedBg);
sc24.draw(0, idx, Common::Point(103, 6));
WAIT(3);
}
for (int idx = 20; idx < 35; ++idx) {
screen.blitFrom(savedBg);
sc24.draw(0, idx, Common::Point(103, 6));
WAIT(3);
}
screen.fadeOut();
// Green power lines spreading to four corners of the top half of Xeen
SpriteResource sc25("sc25.eg2"), sc262("sc262.eg2"), sc263("sc263.eg2"),
sc264("sc264.eg2");
SpriteResource sc261[2] = {
SpriteResource("sc261a.eg2"), SpriteResource("sc261b.eg2")
};
screen.loadBackground("eg250001.raw");
screen.loadPalette("eg250001.pal");
w0.update();
screen.fadeIn();
sound.playSound("comet.voc");
for (int idx = 0; idx < 52; ++idx) {
if (idx == 28)
sound.playSound("click.voc");
if (!sound.isSoundPlaying())
sound.playSound("comet.voc");
sc25.draw(0, idx);
w0.update();
WAIT(2);
}
sound.stopSound();
// Closeup view of the four collectors receiving the power beams
screen.loadBackground("blank.raw");
screen.loadPalette("skymain.pal");
sc261[0].draw(0, 0, Common::Point(7, 4));
sc262.draw(0, 0, Common::Point(86, 4));
sc263.draw(0, 0, Common::Point(164, 4));
sc264.draw(0, 0, Common::Point(242, 4));
w0.update();
screen.fadeIn(0x81);
int frame1 = 0, frame2 = 0, frame3 = 0, frame4 = 0;
for (int idx = 0; idx < 78; ++idx) {
sc261[frame4 / 14].draw(0, frame4 % 14, Common::Point(7, 4));
sc262.draw(0, frame1, Common::Point(86, 4));
sc263.draw(0, frame2, Common::Point(164, 4));
sc264.draw(0, frame3, Common::Point(242, 4));
if (idx == 10 || idx == 28 || idx == 43 || idx == 56)
sound.playSound("photon.voc", 1, 0);
frame4 = (frame4 + 1) % 28;
frame1 = (frame1 + 1) % 9;
frame2 = (frame2 + 1) % 19;
frame3 = (frame3 + 1) % 10;
if (idx > 12 && frame4 < 13)
frame4 = 13;
if (idx < 23)
frame1 = 0;
else if (idx > 26 && frame1 < 5)
frame1 = 5;
if (idx < 34)
frame2 = 0;
else if (idx > 43 && frame2 < 12)
frame2 = 12;
if (idx < 53)
frame3 = 0;
else if (idx > 57 && frame3 < 5)
frame3 = 5;
w0.update();
WAIT(2);
}
screen.fadeOut();
// Four beams arc across bottom of Xeen
SpriteResource sc27("sc27.eg2");
screen.loadBackground("eg270001.raw");
screen.loadPalette("eg250001.pal");
screen.fadeIn();
for (int idx = 0; idx < 89; ++idx) {
if (!sound.isSoundPlaying())
sound.playSound("comet.voc");
if (idx == 19 || idx == 60)
sound.playSound("click.voc");
sc27.draw(0, idx);
w0.update();
WAIT(2);
}
sound.stopSound();
screen.fadeOut();
return true;
}
bool WorldOfXeenCutscenes::worldEnding3() {
Screen &screen = *_vm->_screen;
Sound &sound = *_vm->_sound;
Windows &windows = *_vm->_windows;
Window &w0 = windows[0];
Graphics::ManagedSurface savedBg(SCREEN_WIDTH, SCREEN_HEIGHT);
SpriteResource sc30("sc30.eg2");
SpriteResource sc28[14] = {
SpriteResource("sc28a.eg2", 2), SpriteResource("sc28b1.eg2", 2),
SpriteResource("sc28c.eg2", 2), SpriteResource("sc28d.eg2", 2),
SpriteResource("sc28e.eg2", 2), SpriteResource("sc28f.eg2", 2),
SpriteResource("sc28g.eg2", 2), SpriteResource("sc28h.eg2", 2),
SpriteResource("sc28i.eg2", 2), SpriteResource("sc28j.eg2", 2),
SpriteResource("sc28k.eg2", 2), SpriteResource("sc28l.eg2", 2),
SpriteResource("sc28m.eg2", 2), SpriteResource("sc28n.eg2", 2),
};
// Transformation of Xeen into a globe
screen.loadBackground("eg280001.raw");
screen.loadPalette("eg250001.pal");
savedBg.blitFrom(screen);
w0.update();
screen.fadeIn();
for (int idx = 0; idx < 138; ++idx) {
if (!sound.isSoundPlaying() && idx > 98)
sound.playSound("rumble.voc");
sc28[idx / 10].draw(0, idx % 10, Common::Point(52, 15));
w0.update();
WAIT(2);
}
sound.stopSound();
// White screen with explosion sound
screen.loadPalette("white.pal");
screen.fadeIn();
sound.playSound("explosio.voc");
WAIT(10);
screen.loadPalette("eg250001.pal");
screen.fadeOut();
// With the prophecy complete, the two sides of Xeen were united as one
for (int idx1 = 0; idx1 < 20; ++idx1) {
for (int idx2 = 0; idx2 < 4; ++idx2) {
sc30.draw(0, idx2);
setSubtitle2(Res.WORLD_END_TEXT[8]);
w0.update();
if (!idx1 && !idx2)
screen.fadeIn();
if (idx1 == 17)
sound.songCommand(207);
WAIT(2);
}
}
screen.fadeOut();
while (sound.isSoundPlaying()) {
WAIT(2);
}
sound.playSong("outday3.m");
Common::String gooberStr = Res.GOOBER[_goober];
Common::String congratsStr1 = Common::String::format(Res.WORLD_CONGRATULATIONS, _finalScore);
showPharaohEndText(congratsStr1.c_str(),
_goober == NON_GOOBER ? nullptr :
Common::String::format(Res.WORLD_CONGRATULATIONS2, gooberStr.c_str()).c_str()
);
return true;
}
void WorldOfXeenCutscenes::setSubtitle(const Common::String &msg) {
Windows &windows = *_vm->_windows;
Window &w = windows[28];
const char *const FORMAT1 = "\v000\t000\f38\x3""c%s";
w.setBounds(Common::Rect(2, 157, SCREEN_WIDTH, 157 + 61));
w.writeString(Common::String::format(FORMAT1, msg.c_str()));
const char *const FORMAT2 = "\v000\t000\f39\x3""c%s";
w.setBounds(Common::Rect(1, 156, SCREEN_WIDTH - 1, 156 + 60));
w.writeString(Common::String::format(FORMAT2, msg.c_str()));
}
void WorldOfXeenCutscenes::setSubtitle2(const Common::String &msg) {
Windows &windows = *_vm->_windows;
Window &w = windows[28];
const char *const FORMAT1 = "\v000\t000\f05\x3""c%s";
w.setBounds(Common::Rect(2, 157, SCREEN_WIDTH, SCREEN_HEIGHT - 2));
w.writeString(Common::String::format(FORMAT1, msg.c_str()));
const char *const FORMAT2 = "\v000\t000\f11\x3""c%s";
w.setBounds(Common::Rect(1, 156, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 4));
w.writeString(Common::String::format(FORMAT2, msg.c_str()));
}
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,86 @@
/* 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/>.
*
*/
#ifndef XEEN_WORLDOFXEEN_WORLDOFXEEN_CUTSCENES_H
#define XEEN_WORLDOFXEEN_WORLDOFXEEN_CUTSCENES_H
#include "mm/xeen/worldofxeen/clouds_cutscenes.h"
#include "mm/xeen/worldofxeen/darkside_cutscenes.h"
#include "mm/xeen/xeen.h"
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
enum GooberState {
NON_GOOBER = 0, GOOBER = 1, SUPER_GOOBER = 2
};
class WorldOfXeenCutscenes : public CloudsCutscenes,
public DarkSideCutscenes {
private:
XeenEngine *_vm;
Subtitles _subtitles;
GooberState _goober;
uint _finalScore;
private:
/**
* Sets a subtitle during the world of xeen ending
*/
void setSubtitle(const Common::String &msg);
/**
* Sets a subtitle during the world of xeen ending
*/
void setSubtitle2(const Common::String &msg);
/**
* Part 1 of World of Xeen ending, up to the point the Cube and Scepter have been
* placed onto the Altar of Joining
*/
bool worldEnding1();
/**
* Part 2 of World of Xeen ending, showing beams of light erupting from Altar,
* and four beams arcing across the top and bottom sides of Xeen
*/
bool worldEnding2();
/**
* Part 3 of World of Xeen ending, with further beams covering Xeen, and it
* transforming the world into a sphere
*/
bool worldEnding3();
public:
WorldOfXeenCutscenes(XeenEngine *vm) : _vm(vm), CloudsCutscenes(vm),
DarkSideCutscenes(vm), _goober(NON_GOOBER), _finalScore(0) {}
/**
* Shows the World of Xeen ending sequence
*/
void showWorldOfXeenEnding(GooberState state, uint score);
};
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM
#endif

View File

@@ -0,0 +1,746 @@
/* 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 "common/scummsys.h"
#include "mm/xeen/worldofxeen/worldofxeen_menu.h"
#include "mm/xeen/worldofxeen/worldofxeen.h"
#include "mm/xeen/dialogs/credits_screen.h"
#include "mm/xeen/dialogs/dialogs_difficulty.h"
#include "mm/xeen/resources.h"
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
#ifdef USE_TTS
static const uint8 kOtherOptionsMaxButtonCount = 5;
static const char *worldMainMenuButtons[] = {
"Start a New Game\nLoad a Saved Game\nCredits\nOther Options\n", // English
"Neues Spiel\nSpielstand laden\nCredits\nWeitere Optionen\n", // German
"Nouvelle Partie\nCharger une Partie\nCredits\nOptions\n", // French
"Partida nueva\nCargar una partida\nCr\44ditos\nOtras opciones\n" // Spanish
// No Russian or Chinese versions exist
};
static const char *worldOtherOptionsButtons[] = {
"View Darkside Intro\nView Clouds Intro\nView Darkside End\nView Clouds End\nView World End\n", // English
"Darkside-Intro\nClouds-Intro\nDarkside-Ende\nClouds-Ende\nWorld-Ende\n", // German
"Intro de Darkside\nIntro des Nuages\nFin de Darkside\nFin des Nuages\nFin de World\n", // French
"Intro de Darkside\nIntro de Clouds\nVer Final de Darkside\nVer Final de Clouds\nVer Final de World\n", // Spanish
"", // Russian (no version exists)
// Chinese
"\xb6\xc2\xb7\x74\xbb\xe2\xa5\x44\xa4\xb6\xd2\xd0\n" // 黑暗領主介紹
"\xb6\xb3\xa4\xb6\xd2\xd0\n" // 雲介紹
"\xb6\xc2\xb7\x74\xbb\xe2\xa5\x44\xa5\xbd\xa7\xc0\n" // 黑暗領主末尾
"\xb6\xb3\xa5\xbd\xa7\xc0\n" // 雲末尾
"\xa5\x40\xac\xc9\n" // 世界末尾
};
static const char *cloudsMainMenuButtons[] = {
"Start a New Game\nLoad a Saved Game\nCredits\nView Endgame\n", // English
"Neues Spiel\nSpielstand laden\nCredits\nEndspiel ansehen\n", // German
"Nouvelle Partie\nCharger une Partie\nCr\202dits\nVoir la fin", // French
"", // Spanish (no version exists)
// Russian
"\x8d\xa0\xe7\xa0\xe2\xec \xad\xae\xa2\xe3\xee \xa8\xa3\xe0\xe3\n" // Начать новую игру
"\x87\xa0\xa3\xe0\xe3\xa7\xa8\xe2\xec \xa8\xa3\xe0\xe3\n" // Загрузить игру
"\x91\xae\xa7\xa4\xa0\xe2\xa5\xab\xa8\n" // Создатели
"\x94\xa8\xad\xa0\xab\xec\xad\xeb\xa9 \xe0\xae\xab\xa8\xaa\n", // Финальный ролик
// Chinese
"\xb6\x7d\xa9\x6c\xb7\x73\xb9\x43\xc0\xb8\n" // 開始新遊戲
"\xb8\xfc\xa4\x4a\xb6\x69\xab\xd7\xc0\xc9\n" // 載入進度檔
"\xb9\x43\xc0\xb8\xbb\x73\xa7\x40\xb8\x73\n" // 遊戲製作群
"\xac\x64\xac\xdd\xb4\xdd\xa7\xbd\n" // 查看殘局
};
static const char *darksideMainMenuButtons[] = {
"Start\nLoad\nView Credits\nOther Options\n", // English
"Start\nLaden\nCredits\nWeitere Optionen\n", // German
"Nouveau\nPartie\nCredits\nOptions\n", // French
"", // Spanish (no version exists)
"", // Russian (no version exists)
// Chinese
"\xb6\x7d\xa9\x6c\xb7\x73\xb9\x43\xc0\xb8\n" // 開始新遊戲
"\xb8\xfc\xa4\x4a\xc2\xc2\xc0\xc9\n" // 載入舊檔
"\xf7\x54\xac\xdd\xb3\x5d\xad\x70\xb8\x73\n" // 覾看設計群
"\xa8\xe4\xa5\x4c\xbf\xef\xb6\xb5\n" // 其他選項
};
#endif
enum CloudsMainMenuButtonTTSTextIndex {
kCloudsNew = 0,
kCloudsLoad = 1,
kCloudsCredits = 2,
kCloudsEndgame = 3
};
enum DarksideWorldMainMenuButtonTTSTextIndex {
kDarksideWorldStart = 0,
kDarksideWorldLoad = 1,
kDarksideWorldCredits = 2,
kDarksideWorldOther = 3
};
enum OtherOptionsButtonTTSTextIndex {
kDarksideIntro = 0,
kCloudsIntro = 1,
kDarksideEnd = 2,
kCloudsEnd = 3,
kWorldEnd = 4
};
void MainMenuContainer::show() {
MainMenuContainer *menu;
switch (g_vm->getGameID()) {
case GType_Clouds:
menu = new CloudsMainMenuContainer();
break;
case GType_DarkSide:
menu = new DarkSideMainMenuContainer();
break;
case GType_WorldOfXeen:
if (g_vm->getIsCD())
menu = new WorldOfXeenCDMainMenuContainer();
else
menu = new WorldOfXeenMainMenuContainer();
break;
default:
error("Invalid game");
}
menu->execute();
delete menu;
}
MainMenuContainer::MainMenuContainer(const char *spritesName1, const char *spritesName2, const char *spritesName3) :
_animateCtr(0), _dialog(nullptr) {
g_vm->_files->setGameCc(g_vm->getGameID() == GType_Clouds ? 0 : 1);
_backgroundSprites.resize(1 + (spritesName2 ? 1 : 0) + (spritesName3 ? 1 : 0));
_backgroundSprites[0].load(spritesName1);
if (spritesName2)
_backgroundSprites[1].load(spritesName2);
if (spritesName3)
_backgroundSprites[2].load(spritesName3);
_frameCount = 0;
for (uint idx = 0; idx < _backgroundSprites.size(); ++idx)
_frameCount += _backgroundSprites[idx].size();
}
MainMenuContainer::~MainMenuContainer() {
delete _dialog;
g_vm->_windows->closeAll();
g_vm->_sound->stopAllAudio();
g_vm->_events->clearEvents();
}
void MainMenuContainer::draw() {
g_vm->_screen->restoreBackground();
_animateCtr = (_animateCtr + 1) % _frameCount;
// Draw the next background frame
uint frameNum = _animateCtr;
for (uint idx = 0; idx < _backgroundSprites.size(); ++idx) {
if (frameNum < _backgroundSprites[idx].size()) {
_backgroundSprites[idx].draw(0, frameNum);
return;
} else {
frameNum -= _backgroundSprites[idx].size();
}
}
}
void MainMenuContainer::execute() {
EventsManager &events = *g_vm->_events;
Screen &screen = *g_vm->_screen;
bool showFlag = false;
// Show the cursor
events.clearEvents();
events.setCursor(0);
events.showCursor();
screen.doScroll(true, false);
if (_dialog) {
_dialog->_ttsVoiceText = true;
}
while (!g_vm->shouldExit() && g_vm->_gameMode == GMODE_NONE) {
// Draw the menu
draw();
if (_dialog) {
_dialog->draw();
#ifdef USE_TTS
_dialog->checkHoverOverButton();
#endif
_dialog->_ttsVoiceText = false;
}
// Fade/scroll in screen if first frame showing screen
if (!showFlag) {
display();
screen.doScroll(false, true);
showFlag = true;
}
// Check for events
events.updateGameCounter();
if (events.wait(4, true)) {
if (_dialog) {
// There's a dialog active, so let it handle the event
_dialog->handleEvents();
// If dialog was removed as a result of the event, flag screen for re-showing,
// such as returning to main menu from the Credits screen
if (!_dialog)
showFlag = false;
} else {
// No active dialog. If Escape pressed, exit game entirely. Otherwise,
// open up the main menu dialog
PendingEvent pe;
if (events.getEvent(pe) && pe._keyState.keycode == Common::KEYCODE_ESCAPE)
g_vm->_gameMode = GMODE_QUIT;
events.clearEvents();
showMenuDialog();
}
}
}
}
/*------------------------------------------------------------------------*/
CloudsMainMenuContainer::CloudsMainMenuContainer() : MainMenuContainer("intro.vga") {
}
void CloudsMainMenuContainer::display() {
FileManager &files = *g_vm->_files;
Screen &screen = *g_vm->_screen;
Sound &sound = *g_vm->_sound;
sound._musicSide = 0;
files.setGameCc(0);
screen.loadPalette("mm4.pal");
screen.loadBackground("intro.raw");
screen.saveBackground();
if (!sound.isMusicPlaying())
sound.playSong("inn.m");
}
void CloudsMainMenuContainer::showMenuDialog() {
setOwner(new CloudsMenuDialog(this));
}
/*------------------------------------------------------------------------*/
DarkSideMainMenuContainer::DarkSideMainMenuContainer() : MainMenuContainer("title2a.int") {
Screen &screen = *g_vm->_screen;
Sound &sound = *g_vm->_sound;
screen.loadPalette("dark.pal");
screen.fadeIn(0x81);
sound._musicSide = 1;
sound.playSong("newbrigh.m");
_background.load("title2.int");
}
void DarkSideMainMenuContainer::display() {
FileManager &files = *g_vm->_files;
Screen &screen = *g_vm->_screen;
Sound &sound = *g_vm->_sound;
_background.draw(0, 0, Common::Point(0, 0));
_background.draw(0, 1, Common::Point(160, 0));
sound._musicSide = 1;
files.setGameCc(1);
screen.loadPalette("dark.pal");
screen.saveBackground();
if (!sound.isMusicPlaying())
sound.playSong("newbrigh.m");
}
void DarkSideMainMenuContainer::showMenuDialog() {
setOwner(new DarkSideMenuDialog(this));
}
/*------------------------------------------------------------------------*/
WorldOfXeenMainMenuContainer::WorldOfXeenMainMenuContainer() : MainMenuContainer("world.int") {
}
void WorldOfXeenMainMenuContainer::display() {
FileManager &files = *g_vm->_files;
Screen &screen = *g_vm->_screen;
Sound &sound = *g_vm->_sound;
sound._musicSide = 1;
files.setGameCc(1);
screen.loadPalette("dark.pal");
screen.loadBackground("world.raw");
screen.saveBackground();
if (!sound.isMusicPlaying())
sound.playSong("newbrigh.m");
}
void WorldOfXeenMainMenuContainer::showMenuDialog() {
setOwner(new WorldMenuDialog(this));
}
/*------------------------------------------------------------------------*/
WorldOfXeenCDMainMenuContainer::WorldOfXeenCDMainMenuContainer() : MainMenuContainer("world0.int", "world1.int", "world2.int") {
}
void WorldOfXeenCDMainMenuContainer::display() {
FileManager &files = *g_vm->_files;
Screen &screen = *g_vm->_screen;
Sound &sound = *g_vm->_sound;
sound._musicSide = 1;
files.setGameCc(1);
screen.loadPalette("dark.pal");
screen.loadBackground("world.raw");
screen.saveBackground();
if (!sound.isMusicPlaying())
sound.playSong("newbrigh.m");
}
void WorldOfXeenCDMainMenuContainer::showMenuDialog() {
setOwner(new WorldMenuDialog(this));
}
/*------------------------------------------------------------------------*/
bool MainMenuDialog::handleEvents() {
FileManager &files = *g_vm->_files;
checkEvents(g_vm);
int difficulty;
if (Res.KeyConstants.CloudsOfXeenMenu.KEY_START_NEW_GAME == _buttonValue) {
// Start new game
difficulty = DifficultyDialog::show(g_vm);
if (difficulty == -1)
return true;
// Load a new game state and set the difficulty
g_vm->_saves->newGame();
g_vm->_party->_difficulty = (Difficulty)difficulty;
g_vm->_gameMode = GMODE_PLAY_GAME;
} else if (Res.KeyConstants.CloudsOfXeenMenu.KEY_LOAD_GAME == _buttonValue) {
// Load existing game
int ccNum = files._ccNum;
g_vm->_saves->newGame();
if (!g_vm->_saves->loadGame()) {
files.setGameCc(ccNum);
return true;
}
g_vm->_gameMode = GMODE_PLAY_GAME;
} else if (Res.KeyConstants.CloudsOfXeenMenu.KEY_SHOW_CREDITS == _buttonValue) {
// Show credits
CreditsScreen::show(g_vm);
} else if (Common::KEYCODE_ESCAPE == _buttonValue) {
// Exit dialog (returning to just the animated background)
} else {
return false;
}
// If this point is reached, delete the dialog itself, which will return the main menu
// to it's default "No dialog showing" state
delete this;
return true;
}
/*------------------------------------------------------------------------*/
CloudsMenuDialog::CloudsMenuDialog(MainMenuContainer *owner) : MainMenuDialog(owner) {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.setBounds(Common::Rect(72, 25, 248, g_vm->_gameWon[0] ? 175 : 150));
w.open();
loadButtons();
#ifdef USE_TTS
setButtonTexts(cloudsMainMenuButtons[_vm->_ttsLanguage]);
#endif
}
CloudsMenuDialog::~CloudsMenuDialog() {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.close();
}
void CloudsMenuDialog::loadButtons() {
_buttonSprites.load("start.icn");
addButton(Common::Rect(93, 53, 227, 73), Res.KeyConstants.CloudsOfXeenMenu.KEY_START_NEW_GAME, &_buttonSprites, kCloudsNew);
addButton(Common::Rect(93, 78, 227, 98), Res.KeyConstants.CloudsOfXeenMenu.KEY_LOAD_GAME, &_buttonSprites, kCloudsLoad);
addButton(Common::Rect(93, 103, 227, 123), Res.KeyConstants.CloudsOfXeenMenu.KEY_SHOW_CREDITS, &_buttonSprites, kCloudsCredits);
if (g_vm->_gameWon[0])
addButton(Common::Rect(93, 128, 227, 148), Res.KeyConstants.CloudsOfXeenMenu.KEY_VIEW_ENDGAME, &_buttonSprites, kCloudsEndgame);
}
void CloudsMenuDialog::draw() {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.frame();
w.writeString(Common::String::format(Res.OPTIONS_MENU, Res.GAME_NAMES[0], g_vm->_gameWon[0] ? 117 : 92, 1992), _ttsVoiceText);
drawButtons(&w);
}
bool CloudsMenuDialog::handleEvents() {
if (MainMenuDialog::handleEvents())
return true;
if (Res.KeyConstants.CloudsOfXeenMenu.KEY_VIEW_ENDGAME == _buttonValue) {
if (g_vm->_gameWon[0]) {
// Close the window
delete this;
// Show clouds ending
WOX_VM._sound->stopAllAudio();
WOX_VM.showCloudsEnding(g_vm->_finalScore);
return true;
}
}
return false;
}
/*------------------------------------------------------------------------*/
DarkSideMenuDialog::DarkSideMenuDialog(MainMenuContainer *owner) : MainMenuDialog(owner), _firstDraw(true) {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.setBounds(Common::Rect(72, 25, 248, 150));
w.open();
loadButtons();
#ifdef USE_TTS
setButtonTexts(darksideMainMenuButtons[_vm->_ttsLanguage]);
#endif
}
DarkSideMenuDialog::~DarkSideMenuDialog() {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.close();
}
void DarkSideMenuDialog::loadButtons() {
addButton(Common::Rect(124, 87, 177, 97), Common::KEYCODE_s, nullptr, kDarksideWorldStart);
addButton(Common::Rect(126, 98, 173, 108), Common::KEYCODE_l, nullptr, kDarksideWorldLoad);
addButton(Common::Rect(91, 110, 209, 120), Common::KEYCODE_c, nullptr, kDarksideWorldCredits);
addButton(Common::Rect(85, 121, 216, 131), Common::KEYCODE_o, nullptr, kDarksideWorldOther);
}
void DarkSideMenuDialog::draw() {
Screen &screen = *g_vm->_screen;
EventsManager &events = *g_vm->_events;
Sound &sound = *g_vm->_sound;
Windows &windows = *g_vm->_windows;
if (!_firstDraw)
return;
SpriteResource kludgeSprites("kludge.int");
SpriteResource title2Sprites[8] = {
SpriteResource("title2b.int"), SpriteResource("title2c.int"),
SpriteResource("title2d.int"), SpriteResource("title2e.int"),
SpriteResource("title2f.int"), SpriteResource("title2g.int"),
SpriteResource("title2h.int"), SpriteResource("title2i.int"),
};
screen.loadBackground("title2b.raw");
kludgeSprites.draw(0, 0, Common::Point(85, 86));
screen.saveBackground();
sound.playSound("elect.voc");
for (int i = 0; i < 30 && !g_vm->shouldExit(); ++i) {
events.updateGameCounter();
screen.restoreBackground();
title2Sprites[i / 4].draw(0, i % 4);
windows[0].update();
if (i == 19)
sound.stopSound();
if (events.wait(2))
break;
}
events.clearEvents();
sound.stopSound();
screen.restoreBackground();
windows[0].update();
_firstDraw = false;
}
bool DarkSideMenuDialog::handleEvents() {
if (MainMenuDialog::handleEvents())
return true;
switch (_buttonValue) {
case Common::KEYCODE_o: {
// Show other options dialog
// Remove this dialog
MainMenuContainer *owner = _owner;
delete this;
// Set the new options dialog
owner->setOwner(new OtherOptionsDialog(owner));
return true;
}
default:
break;
}
return false;
}
/*------------------------------------------------------------------------*/
WorldMenuDialog::WorldMenuDialog(MainMenuContainer *owner) : MainMenuDialog(owner) {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.setBounds(Common::Rect(72, 25, 248, 175));
w.open();
loadButtons();
#ifdef USE_TTS
setButtonTexts(worldMainMenuButtons[_vm->_ttsLanguage]);
#endif
}
WorldMenuDialog::~WorldMenuDialog() {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.close();
}
void WorldMenuDialog::loadButtons() {
_buttonSprites.load("start.icn");
addButton(Common::Rect(93, 53, 227, 73), Common::KEYCODE_s, &_buttonSprites, kDarksideWorldStart);
addButton(Common::Rect(93, 78, 227, 98), Common::KEYCODE_l, &_buttonSprites, kDarksideWorldLoad);
addButton(Common::Rect(93, 103, 227, 123), Common::KEYCODE_c, &_buttonSprites, kDarksideWorldCredits);
addButton(Common::Rect(93, 128, 227, 148), Common::KEYCODE_o, &_buttonSprites, kDarksideWorldOther);
}
void WorldMenuDialog::draw() {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.frame();
w.writeString(Common::String::format(Res.OPTIONS_MENU, Res.GAME_NAMES[2], 117, 1993), _ttsVoiceText);
drawButtons(&w);
}
bool WorldMenuDialog::handleEvents() {
if (MainMenuDialog::handleEvents())
return true;
switch (_buttonValue) {
case Common::KEYCODE_o: {
// Show other options dialog
// Remove this dialog
MainMenuContainer *owner = _owner;
delete this;
// Set the new options dialog
owner->setOwner(new OtherOptionsDialog(owner));
return true;
}
default:
break;
}
return false;
}
/*------------------------------------------------------------------------*/
OtherOptionsDialog::OtherOptionsDialog(MainMenuContainer *owner) : MenuContainerDialog(owner) {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
int height = (g_vm->getGameID() == GType_WorldOfXeen ? 25 : 0)
+ (g_vm->getGameID() == GType_WorldOfXeen && g_vm->_gameWon[0] ? 25 : 0)
+ (g_vm->_gameWon[1] ? 25 : 0)
+ (g_vm->_gameWon[2] ? 25 : 0)
+ 75;
w.setBounds(Common::Rect(72, 25, 248, 25 + height));
w.open();
loadButtons();
}
OtherOptionsDialog::~OtherOptionsDialog() {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.close();
}
void OtherOptionsDialog::loadButtons() {
_buttonSprites.load("special.icn");
Common::Rect r(93, 53, 227, 73);
// View Darkside Intro
addButton(r, Common::KEYCODE_d, &_buttonSprites, kDarksideIntro);
r.translate(0, 25);
// View Clouds Intro
if (g_vm->getGameID() == GType_WorldOfXeen) {
addButton(r, Common::KEYCODE_c, &_buttonSprites, kCloudsIntro);
r.translate(0, 25);
} else {
addButton(Common::Rect(), Common::KEYCODE_INVALID, nullptr, kCloudsIntro);
}
// View Darkside End
if (g_vm->_gameWon[1]) {
addButton(r, Common::KEYCODE_e, &_buttonSprites, kDarksideEnd);
r.translate(0, 25);
} else {
addButton(Common::Rect(), Common::KEYCODE_INVALID, nullptr, kDarksideEnd);
}
// View Clouds End
if (g_vm->_gameWon[0]) {
addButton(r, Common::KEYCODE_v, &_buttonSprites, kCloudsEnd);
r.translate(0, 25);
} else {
addButton(Common::Rect(), Common::KEYCODE_INVALID, nullptr, kCloudsEnd);
}
// View World End
if (g_vm->_gameWon[2]) {
addButton(r, Common::KEYCODE_w, &_buttonSprites, kWorldEnd);
} else {
addButton(Common::Rect(), Common::KEYCODE_INVALID, nullptr, kWorldEnd);
}
#ifdef USE_TTS
uint index = 0;
addNextTextToButtons(worldOtherOptionsButtons[_vm->_ttsLanguage], index, kOtherOptionsMaxButtonCount);
#endif
}
void OtherOptionsDialog::draw() {
Windows &windows = *g_vm->_windows;
Window &w = windows[GAME_WINDOW];
w.frame();
w.writeString(Common::String::format(Res.OPTIONS_MENU,
Res.GAME_NAMES[g_vm->getGameID() == GType_WorldOfXeen ? 2 : 1],
w.getBounds().height() - 33, 1993), _ttsVoiceText);
drawButtons(&w);
}
bool OtherOptionsDialog::handleEvents() {
Sound &sound = *g_vm->_sound;
checkEvents(g_vm);
switch (_buttonValue) {
case Common::KEYCODE_d:
delete this;
sound.stopAllAudio();
WOX_VM._events->hideCursor();
WOX_VM.showDarkSideIntro(false);
WOX_VM._events->showCursor();
break;
case Common::KEYCODE_c:
if (g_vm->getGameID() == GType_WorldOfXeen) {
delete this;
sound.stopAllAudio();
WOX_VM._events->hideCursor();
WOX_VM.showCloudsIntro();
WOX_VM._events->showCursor();
}
break;
case Common::KEYCODE_e:
if (g_vm->_gameWon[1]) {
delete this;
sound.stopAllAudio();
WOX_VM._events->hideCursor();
WOX_VM.showDarkSideEnding(g_vm->_finalScore);
WOX_VM._events->showCursor();
}
break;
case Common::KEYCODE_v:
if (g_vm->_gameWon[0]) {
delete this;
sound.stopAllAudio();
WOX_VM._events->hideCursor();
WOX_VM.showCloudsEnding(g_vm->_finalScore);
WOX_VM._events->showCursor();
}
break;
case Common::KEYCODE_w:
if (g_vm->_gameWon[2]) {
delete this;
sound.stopAllAudio();
WOX_VM._events->hideCursor();
WOX_VM.showWorldOfXeenEnding(NON_GOOBER, g_vm->_finalScore);
WOX_VM._events->showCursor();
}
break;
case Common::KEYCODE_ESCAPE:
// Exit dialog
delete this;
break;
default:
return false;
}
return true;
}
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM

View File

@@ -0,0 +1,325 @@
/* 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/>.
*
*/
#ifndef XEEN_WORLDOFXEEN_WORLDOFXEEN_MENU_H
#define XEEN_WORLDOFXEEN_WORLDOFXEEN_MENU_H
#include "mm/xeen/xeen.h"
#include "mm/xeen/dialogs/dialogs.h"
#include "common/array.h"
namespace MM {
namespace Xeen {
namespace WorldOfXeen {
class MenuContainerDialog;
class MainMenuContainer {
private:
uint _animateCtr;
uint _frameCount;
Common::Array<SpriteResource> _backgroundSprites;
MenuContainerDialog *_dialog;
protected:
/**
* Draws the main menu background
*/
void draw();
/**
* Called when the menu screen is first shown
*/
virtual void display() = 0;
/**
* Shows the main menu dialog
*/
virtual void showMenuDialog() = 0;
public:
/**
* Show the main menu for the correct game
*/
static void show();
public:
/**
* Constructor
*/
MainMenuContainer(const char *spritesName1, const char *spritesName2 = nullptr, const char *spritesName3 = nullptr);
/**
* Destructor
*/
virtual ~MainMenuContainer();
/**
* Execute the menu
*/
void execute();
/**
* Sets the dialog being displayed in the menu
*/
void setOwner(MenuContainerDialog *dlalog) {
_dialog = dlalog;
}
};
class CloudsMainMenuContainer : public MainMenuContainer {
protected:
/**
* Called when the menu screen is first shown
*/
void display() override;
/**
* Shows the main menu dialog
*/
void showMenuDialog() override;
public:
CloudsMainMenuContainer();
};
class DarkSideMainMenuContainer : public MainMenuContainer {
private:
SpriteResource _background;
protected:
/**
* Called when the menu screen is first shown
*/
void display() override;
/**
* Shows the main menu dialog
*/
void showMenuDialog() override;
public:
DarkSideMainMenuContainer();
};
class WorldOfXeenMainMenuContainer : public MainMenuContainer {
protected:
/**
* Called when the menu screen is first shown
*/
void display() override;
/**
* Shows the main menu dialog
*/
void showMenuDialog() override;
public:
WorldOfXeenMainMenuContainer();
};
class WorldOfXeenCDMainMenuContainer : public MainMenuContainer {
protected:
/**
* Called when the menu screen is first shown
*/
void display() override;
/**
* Shows the main menu dialog
*/
void showMenuDialog() override;
public:
WorldOfXeenCDMainMenuContainer();
};
class MenuContainerDialog : public ButtonContainer {
protected:
MainMenuContainer *_owner;
public:
bool _ttsVoiceText;
public:
/**
* Constructor
*/
MenuContainerDialog(MainMenuContainer *owner) : ButtonContainer(g_vm), _owner(owner), _ttsVoiceText(true) {}
/**
* Destructor
*/
~MenuContainerDialog() override {
_owner->setOwner(nullptr);
}
/**
* Draws the dialog
*/
virtual void draw() = 0;
/**
* Handles events
*/
virtual bool handleEvents() = 0;
};
class MainMenuDialog : public MenuContainerDialog {
public:
/**
* Constructor
*/
MainMenuDialog(MainMenuContainer *owner) : MenuContainerDialog(owner) {}
/**
* Destructor
*/
~MainMenuDialog() override {}
/**
* Draws the dialog
*/
void draw() override = 0;
/**
* Handles events
*/
bool handleEvents() override;
};
class CloudsMenuDialog : public MainMenuDialog {
private:
SpriteResource _buttonSprites;
private:
/**
* Loads buttons for the dialog
*/
void loadButtons();
public:
/**
* Constructor
*/
CloudsMenuDialog(MainMenuContainer *owner);
/**
* Destructor
*/
~CloudsMenuDialog() override;
/**
* Draws the dialog
*/
void draw() override;
/**
* Handles events
*/
bool handleEvents() override;
};
class DarkSideMenuDialog : public MainMenuDialog {
private:
SpriteResource _buttonSprites;
bool _firstDraw;
private:
/**
* Loads buttons for the dialog
*/
void loadButtons();
public:
/**
* Constructor
*/
DarkSideMenuDialog(MainMenuContainer *owner);
/**
* Destructor
*/
~DarkSideMenuDialog() override;
/**
* Draws the dialog
*/
void draw() override;
/**
* Handles events
*/
bool handleEvents() override;
};
class WorldMenuDialog : public MainMenuDialog {
private:
SpriteResource _buttonSprites;
private:
/**
* Loads buttons for the dialog
*/
void loadButtons();
public:
/**
* Constructor
*/
WorldMenuDialog(MainMenuContainer *owner);
/**
* Destructor
*/
~WorldMenuDialog() override;
/**
* Draws the dialog
*/
void draw() override;
/**
* Handles events
*/
bool handleEvents() override;
};
class OtherOptionsDialog : public MenuContainerDialog {
private:
SpriteResource _buttonSprites;
private:
/**
* Loads buttons for the dialog
*/
void loadButtons();
public:
/**
* Constructor
*/
OtherOptionsDialog(MainMenuContainer *owner);
/**
* Destructor
*/
~OtherOptionsDialog() override;
/**
* Draws the dialog
*/
void draw() override;
/**
* Handles events
*/
bool handleEvents() override;
};
} // End of namespace WorldOfXeen
} // End of namespace Xeen
} // End of namespace MM
#endif