aboutsummaryrefslogtreecommitdiff
path: root/sword2/build_display.cpp
diff options
context:
space:
mode:
authorTorbjörn Andersson2005-05-02 05:41:01 +0000
committerTorbjörn Andersson2005-05-02 05:41:01 +0000
commitcd6e22a3b15129a6e474f2752815643a44aff07d (patch)
tree3c37d25ceaae6fe4594164195a47f17752009c97 /sword2/build_display.cpp
parentb56824d0cf0e559133f1649291cb1e02fc7fc47b (diff)
downloadscummvm-rg350-cd6e22a3b15129a6e474f2752815643a44aff07d.tar.gz
scummvm-rg350-cd6e22a3b15129a6e474f2752815643a44aff07d.tar.bz2
scummvm-rg350-cd6e22a3b15129a6e474f2752815643a44aff07d.zip
Cleanup. Most of is simply moves the credits code out of Logic and into
Screen. I've also added an unused "splash screen" function that displays the image that used to be shown by CacheNewCluster() while copying a data file from CD to hard disk. ScummVM doesn't do that, but it's a nice image that I wish we could do something useful with, and it's easier to have the code here for reference than having to dig through old revisions of the resource manager. svn-id: r17894
Diffstat (limited to 'sword2/build_display.cpp')
-rw-r--r--sword2/build_display.cpp421
1 files changed, 421 insertions, 0 deletions
diff --git a/sword2/build_display.cpp b/sword2/build_display.cpp
index cb07a5e510..0bd185f7fa 100644
--- a/sword2/build_display.cpp
+++ b/sword2/build_display.cpp
@@ -32,6 +32,7 @@
#include "sword2/maketext.h"
#include "sword2/mouse.h"
#include "sword2/resman.h"
+#include "sword2/sound.h"
namespace Sword2 {
@@ -624,4 +625,424 @@ void Screen::registerFrame(ObjectMouse *ob_mouse, ObjectGraphic *ob_graph, Objec
}
}
+// FIXME:
+//
+// The original credits used a different font. I think it's stored in the
+// font.clu file, but I don't know how to interpret it.
+//
+// The original used the entire screen. This version cuts off the top and
+// bottom of the screen, because that's where the menus would usually be.
+//
+// The original had some sort of smoke effect at the bottom of the screen.
+
+enum {
+ LINE_LEFT,
+ LINE_CENTER,
+ LINE_RIGHT
+};
+
+struct CreditsLine {
+ char *str;
+ byte type;
+ int top;
+ int height;
+ byte *sprite;
+};
+
+#define CREDITS_FONT_HEIGHT 25
+#define CREDITS_LINE_SPACING 20
+
+void Screen::rollCredits() {
+ ScreenInfo *screenInfo = getScreenInfo();
+ uint32 loopingMusicId = _vm->_sound->getLoopingMusicId();
+
+ // Prepare for the credits by fading down, stoping the music, etc.
+
+ _vm->_mouse->setMouse(0);
+
+ _vm->_sound->muteFx(true);
+ _vm->_sound->muteSpeech(true);
+
+ waitForFade();
+ fadeDown();
+ waitForFade();
+
+ _vm->_mouse->closeMenuImmediately();
+
+ // There are three files which I believe are involved in showing the
+ // credits:
+ //
+ // credits.bmp - The "Smacker" logo, stored as follows:
+ //
+ // width 2 bytes, little endian
+ // height 2 bytes, little endian
+ // palette 3 * 256 bytes
+ // data width * height bytes
+ //
+ // Note that the maximum colour component in the palette is 0x3F.
+ // This is the same resolution as the _paletteMatch table. I doubt
+ // that this is a coincidence, but let's use the image palette
+ // directly anyway, just to be safe.
+ //
+ // credits.clu - The credits text
+ //
+ // This is simply a text file with CRLF line endings.
+ // '^' is not shown, but used to mark the center of the line.
+ // '@' is used as a placeholder for the "Smacker" logo. At least
+ // when it appears alone.
+ // Remaining lines are centered.
+ //
+ // fonts.clu - The credits font?
+ //
+ // FIXME: At this time I don't know how to interpret fonts.clu. For
+ // now, let's just the standard speech font instead.
+
+ SpriteInfo spriteInfo;
+ File f;
+ int i;
+
+ // Read the "Smacker" logo
+
+ uint16 logoWidth = 0;
+ uint16 logoHeight = 0;
+ byte *logoData = NULL;
+ byte palette[256 * 4];
+
+ if (f.open("credits.bmp")) {
+ logoWidth = f.readUint16LE();
+ logoHeight = f.readUint16LE();
+
+ for (i = 0; i < 256; i++) {
+ palette[i * 4 + 0] = f.readByte() << 2;
+ palette[i * 4 + 1] = f.readByte() << 2;
+ palette[i * 4 + 2] = f.readByte() << 2;
+ palette[i * 4 + 3] = 0;
+ }
+
+ logoData = (byte *) malloc(logoWidth * logoHeight);
+
+ f.read(logoData, logoWidth * logoHeight);
+ f.close();
+ } else {
+ warning("Can't find credits.bmp");
+ memset(palette, 0, sizeof(palette));
+ palette[14 * 4 + 0] = 252;
+ palette[14 * 4 + 1] = 252;
+ palette[14 * 4 + 2] = 252;
+ palette[14 * 4 + 3] = 0;
+ }
+
+ setPalette(0, 256, palette, RDPAL_INSTANT);
+
+ // Read the credits text
+
+ // This should be plenty
+ CreditsLine creditsLines[350];
+
+ for (i = 0; i < ARRAYSIZE(creditsLines); i++) {
+ creditsLines[i].str = NULL;
+ creditsLines[i].sprite = NULL;
+ }
+
+ if (!f.open("credits.clu")) {
+ warning("Can't find credits.clu");
+ return;
+ }
+
+ int lineTop = 400;
+ int lineCount = 0;
+ int paragraphStart = 0;
+ bool hasCenterMark = false;
+
+ while (1) {
+ if (lineCount >= ARRAYSIZE(creditsLines)) {
+ warning("Too many credits lines");
+ break;
+ }
+
+ char buffer[80];
+ char *line = f.readLine(buffer, sizeof(buffer));
+
+ if (!line || *line == 0) {
+ if (!hasCenterMark) {
+ for (i = paragraphStart; i < lineCount; i++)
+ creditsLines[i].type = LINE_CENTER;
+ }
+ paragraphStart = lineCount;
+ hasCenterMark = false;
+ if (paragraphStart == lineCount)
+ lineTop += CREDITS_LINE_SPACING;
+
+ if (!line)
+ break;
+
+ continue;
+ }
+
+ char *center_mark = strchr(line, '^');
+
+ if (center_mark) {
+ // The current paragraph has at least one center mark.
+ hasCenterMark = true;
+
+ if (center_mark != line) {
+ // The center mark is somewhere inside the
+ // line. Split it into left and right side.
+ *center_mark = 0;
+
+ creditsLines[lineCount].top = lineTop;
+ creditsLines[lineCount].height = CREDITS_FONT_HEIGHT;
+ creditsLines[lineCount].type = LINE_LEFT;
+ creditsLines[lineCount].str = strdup(line);
+
+ lineCount++;
+
+ if (lineCount >= ARRAYSIZE(creditsLines)) {
+ warning("Too many credits lines");
+ break;
+ }
+
+ *center_mark = '^';
+ }
+
+ line = center_mark;
+ }
+
+ creditsLines[lineCount].top = lineTop;
+
+ if (*line == '^') {
+ creditsLines[lineCount].type = LINE_RIGHT;
+ line++;
+ } else
+ creditsLines[lineCount].type = LINE_LEFT;
+
+ if (strcmp(line, "@") == 0) {
+ creditsLines[lineCount].height = logoHeight;
+ lineTop += logoHeight;
+ } else {
+ creditsLines[lineCount].height = CREDITS_FONT_HEIGHT;
+ lineTop += CREDITS_LINE_SPACING;
+ }
+
+ creditsLines[lineCount].str = strdup(line);
+ lineCount++;
+ }
+
+ f.close();
+
+ // We could easily add some ScummVM stuff to the credits, if we wanted
+ // to. On the other hand, anyone with the attention span to actually
+ // read all the credits probably already knows. :-)
+
+ // Start the music and roll the credits
+
+ // The credits music (which can also be heard briefly in the "carib"
+ // cutscene) is played once.
+
+ _vm->_sound->streamCompMusic(309, false);
+
+ clearScene();
+ fadeUp(0);
+
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
+ spriteInfo.blend = 0;
+
+ int startLine = 0;
+ int scrollPos = 0;
+
+ bool abortCredits = false;
+
+ int scrollSteps = lineTop + CREDITS_FONT_HEIGHT;
+ uint32 musicStart = _vm->getMillis();
+
+ // Ideally the music should last just a tiny bit longer than the
+ // credits. Note that musicTimeRemaining() will return 0 if the music
+ // is muted, so we need a sensible fallback for that case.
+
+ uint32 musicLength = MAX((int32) (1000 * (_vm->_sound->musicTimeRemaining() - 3)), 25 * (int32) scrollSteps);
+
+ while (scrollPos < scrollSteps && !_vm->_quit) {
+ bool foundStartLine = false;
+
+ clearScene();
+
+ for (i = startLine; i < lineCount; i++) {
+ // Free any sprites that have scrolled off the screen
+
+ if (creditsLines[i].top + creditsLines[i].height < scrollPos) {
+ if (creditsLines[i].sprite) {
+ free(creditsLines[i].sprite);
+ creditsLines[i].sprite = NULL;
+ debug(2, "Freeing sprite '%s'", creditsLines[i].str);
+ }
+ if (creditsLines[i].str) {
+ free(creditsLines[i].str);
+ creditsLines[i].str = NULL;
+ }
+ } else if (creditsLines[i].top < scrollPos + 400) {
+ if (!foundStartLine) {
+ startLine = i;
+ foundStartLine = true;
+ }
+
+ if (!creditsLines[i].sprite) {
+ debug(2, "Creating sprite '%s'", creditsLines[i].str);
+ creditsLines[i].sprite = _vm->_fontRenderer->makeTextSprite((byte *) creditsLines[i].str, 600, 14, _vm->_speechFontId, 0);
+ }
+
+ FrameHeader *frame = (FrameHeader *) creditsLines[i].sprite;
+
+ spriteInfo.y = creditsLines[i].top - scrollPos;
+ spriteInfo.w = frame->width;
+ spriteInfo.h = frame->height;
+ spriteInfo.data = creditsLines[i].sprite + sizeof(FrameHeader);
+
+ switch (creditsLines[i].type) {
+ case LINE_LEFT:
+ spriteInfo.x = RENDERWIDE / 2 - 5 - frame->width;
+ break;
+ case LINE_RIGHT:
+ spriteInfo.x = RENDERWIDE / 2 + 5;
+ break;
+ case LINE_CENTER:
+ if (strcmp(creditsLines[i].str, "@") == 0) {
+ spriteInfo.data = logoData;
+ spriteInfo.x = (RENDERWIDE - logoWidth) / 2;
+ spriteInfo.w = logoWidth;
+ spriteInfo.h = logoHeight;
+ } else
+ spriteInfo.x = (RENDERWIDE - frame->width) / 2;
+ break;
+ }
+
+ if (spriteInfo.data)
+ drawSprite(&spriteInfo);
+ } else
+ break;
+ }
+
+ updateDisplay();
+
+ KeyboardEvent *ke = _vm->keyboardEvent();
+
+ if (ke && ke->keycode == 27) {
+ if (!abortCredits) {
+ abortCredits = true;
+ fadeDown();
+ }
+ }
+
+ if (abortCredits && getFadeStatus() == RDFADE_BLACK)
+ break;
+
+ _vm->sleepUntil(musicStart + (musicLength * scrollPos) / scrollSteps);
+ scrollPos++;
+ }
+
+ // We're done. Clean up and try to put everything back where it was
+ // before the credits.
+
+ for (i = 0; i < lineCount; i++) {
+ if (creditsLines[i].str)
+ free(creditsLines[i].str);
+ if (creditsLines[i].sprite)
+ free(creditsLines[i].sprite);
+ }
+
+ if (logoData)
+ free(logoData);
+
+ if (!abortCredits) {
+ // The music should either have stopped or be about to stop, so
+ // wait for it to really happen.
+
+ while (_vm->_sound->musicTimeRemaining() && !_vm->_quit) {
+ updateDisplay(false);
+ _vm->_system->delayMillis(100);
+ }
+ }
+
+ if (_vm->_quit)
+ return;
+
+ _vm->_sound->muteFx(false);
+ _vm->_sound->muteSpeech(false);
+
+ if (loopingMusicId)
+ _vm->_sound->streamCompMusic(loopingMusicId, true);
+ else
+ _vm->_sound->stopMusic(false);
+
+ screenInfo->new_palette = 99;
+
+ if (!_vm->_mouse->getMouseStatus() || _vm->_mouse->isChoosing())
+ _vm->_mouse->setMouse(NORMAL_MOUSE_ID);
+
+ if (Logic::_scriptVars[DEAD])
+ _vm->_mouse->buildSystemMenu();
+}
+
+// This image used to be shown by CacheNewCluster() while copying a data file
+// from the CD to the hard disk. ScummVM doesn't do that, so the image is never
+// shown. It'd be nice if we could do something useful with it some day...
+
+void Screen::splashScreen() {
+ byte *bgfile = _vm->_resman->openResource(2950);
+
+ initialiseBackgroundLayer(NULL);
+ initialiseBackgroundLayer(NULL);
+ initialiseBackgroundLayer(_vm->fetchBackgroundLayer(bgfile));
+ initialiseBackgroundLayer(NULL);
+ initialiseBackgroundLayer(NULL);
+
+ setPalette(0, 256, _vm->fetchPalette(bgfile), RDPAL_FADE);
+ renderParallax(_vm->fetchBackgroundLayer(bgfile), 2);
+
+ closeBackgroundLayer();
+
+ byte *loadingBar = _vm->_resman->openResource(2951);
+ AnimHeader *animHead = _vm->fetchAnimHeader(loadingBar);
+ FrameHeader *frame = _vm->fetchFrameHeader(loadingBar, 0);
+ CdtEntry *cdt = _vm->fetchCdtEntry(loadingBar, 0);
+
+ SpriteInfo barSprite;
+
+ barSprite.x = cdt->x;
+ barSprite.y = cdt->y;
+ barSprite.w = frame->width;
+ barSprite.h = frame->height;
+ barSprite.scale = 0;
+ barSprite.scaledWidth = 0;
+ barSprite.scaledHeight = 0;
+ barSprite.type = RDSPR_RLE256FAST | RDSPR_TRANS;
+ barSprite.blend = 0;
+ barSprite.colourTable = 0;
+ barSprite.data = (byte *) (frame + 1);
+
+ drawSprite(&barSprite);
+
+ fadeUp();
+ waitForFade();
+
+ for (int i = 0; i < animHead->noAnimFrames; i++) {
+ frame = _vm->fetchFrameHeader(loadingBar, i);
+ barSprite.data = (byte *) (frame + 1);
+
+ barSprite.x = cdt->x;
+ barSprite.y = cdt->y;
+
+ drawSprite(&barSprite);
+ updateDisplay();
+ _vm->_system->delayMillis(30);
+ }
+
+ _vm->_resman->closeResource(2951);
+
+ fadeDown();
+ waitForFade();
+}
+
} // End of namespace Sword2