aboutsummaryrefslogtreecommitdiff
path: root/sword2/function.cpp
diff options
context:
space:
mode:
authorTorbjörn Andersson2003-12-31 13:14:07 +0000
committerTorbjörn Andersson2003-12-31 13:14:07 +0000
commit6899aa23cb49adcddb905adcac2e9ade475e4be6 (patch)
tree45948bc1a336446de71410b9a2952fc0803aa690 /sword2/function.cpp
parent28eb8c4b76f5eb72569f7ab4138e59092ec8f15c (diff)
downloadscummvm-rg350-6899aa23cb49adcddb905adcac2e9ade475e4be6.tar.gz
scummvm-rg350-6899aa23cb49adcddb905adcac2e9ade475e4be6.tar.bz2
scummvm-rg350-6899aa23cb49adcddb905adcac2e9ade475e4be6.zip
I still don't know how to draw the credits like the original did, but at
least the credits text is showing now. svn-id: r12053
Diffstat (limited to 'sword2/function.cpp')
-rw-r--r--sword2/function.cpp361
1 files changed, 314 insertions, 47 deletions
diff --git a/sword2/function.cpp b/sword2/function.cpp
index 243f68fdcf..3fede7a7e6 100644
--- a/sword2/function.cpp
+++ b/sword2/function.cpp
@@ -18,6 +18,7 @@
*/
#include "common/stdafx.h"
+#include "common/file.h"
#include "sword2/sword2.h"
#include "sword2/defs.h"
#include "sword2/interpreter.h"
@@ -365,84 +366,350 @@ int32 Logic::fnResetGlobals(int32 *params) {
return IR_CONT;
}
+// 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;
+ Memory *sprite;
+};
+
+#define CREDITS_FONT_HEIGHT 20
+
int32 Logic::fnPlayCredits(int32 *params) {
// This function just quits the game if this is the playable demo, ie.
// credits are NOT played in the demo any more!
// params: none
- if (!DEMO) {
- uint8 oldPal[1024];
- uint8 tmpPal[1024];
- int32 music_length;
- int32 pars[2];
+ if (DEMO) {
+ _vm->closeGame();
+ return IR_CONT;
+ }
- _vm->_sound->saveMusicState();
+ // Prepare for the credits by fading down, stoping the music, etc.
- _vm->_sound->muteFx(true);
- _vm->_sound->muteSpeech(true);
- _vm->_sound->stopMusic();
+ _vm->setMouse(0);
- memcpy(oldPal, _vm->_graphics->_palCopy, 1024);
- memset(tmpPal, 0, 1024);
+ _vm->_sound->saveMusicState();
- _vm->_graphics->waitForFade();
- _vm->_graphics->fadeDown();
- _vm->_graphics->waitForFade();
+ _vm->_sound->muteFx(true);
+ _vm->_sound->muteSpeech(true);
+ _vm->_sound->stopMusic();
- tmpPal[4] = 255;
- tmpPal[5] = 255;
- tmpPal[6] = 255;
- _vm->_graphics->setPalette(0, 256, tmpPal, RDPAL_INSTANT);
+ _vm->_graphics->waitForFade();
+ _vm->_graphics->fadeDown();
+ _vm->_graphics->waitForFade();
- // Play the credits music. Is it enough with just one
- // repetition of it?
+ _vm->_graphics->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.
- pars[0] = 309;
- pars[1] = FX_SPOT;
- fnPlayMusic(pars);
+ SpriteInfo spriteInfo;
+ File f;
+ int i;
- music_length = 1000 * _vm->_sound->musicTimeRemaining();
+ // Read the "Smacker" logo
- debug(0, "Credits music length: ~%d ms", music_length);
+ f.open("credits.bmp");
- _vm->_graphics->closeMenuImmediately();
+ if (!f.isOpen()) {
+ warning("Can't find credits.bmp");
+ return IR_CONT;
+ }
- while (_vm->_sound->musicTimeRemaining()) {
- _vm->_graphics->clearScene();
- _vm->_graphics->setNeedFullRedraw();
+ uint16 logoWidth = f.readUint16LE();
+ uint16 logoHeight = f.readUint16LE();
- // FIXME: Draw the credits text. The actual text
- // messages are stored in credits.clu, and I'm guessing
- // that credits.bmp or font.clu may be the font.
+ uint8 palette[1024];
+
+ 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;
+ }
+
+ uint8 *logoData = (uint8 *) malloc(logoWidth * logoHeight);
+
+ f.read(logoData, logoWidth * logoHeight);
+ f.close();
+
+ _vm->_graphics->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;
+ }
+
+ char textLine[80];
+
+ f.open("credits.clu");
+ if (!f.isOpen()) {
+ warning("Can't find credits.clu");
+ return IR_CONT;
+ }
- _vm->_graphics->updateDisplay();
+ int lineTop = 400;
+ int lineCount = 0;
+ int pos = 0;
- KeyboardEvent ke;
+ textLine[0] = 0;
- if (_vm->_input->readKey(&ke) == RD_OK && ke.keycode == 27)
- break;
+ int paragraphStart = 0;
+ bool hasCenterMark = false;
- _vm->_system->delay_msecs(30);
+ while (1) {
+ if (lineCount >= ARRAYSIZE(creditsLines)) {
+ warning("Too many credits lines");
+ break;
}
- fnStopMusic(NULL);
- _vm->_sound->restoreMusicState();
+ byte b = f.readByte();
+
+ if (f.ioFailed())
+ break;
+
+ // Remember that the current paragraph has at least once center
+ // mark. If a paragraph has no center marks, it should be
+ // centered.
+
+ if (b == '^')
+ hasCenterMark = true;
+
+ if (b == '^' && pos != 0) {
+ textLine[pos] = 0;
+
+ creditsLines[lineCount].top = lineTop;
+ creditsLines[lineCount].height = CREDITS_FONT_HEIGHT;
+ creditsLines[lineCount].type = LINE_LEFT;
+ creditsLines[lineCount].str = strdup(textLine);
+
+ lineCount++;
+ textLine[0] = '^';
+ pos = 1;
+ } else if (b == 0x0a) {
+ creditsLines[lineCount].top = lineTop;
+
+ if (textLine[0] == '^') {
+ creditsLines[lineCount].str = strdup(textLine + 1);
+ creditsLines[lineCount].type = LINE_RIGHT;
+ } else {
+ creditsLines[lineCount].str = strdup(textLine);
+ creditsLines[lineCount].type = LINE_LEFT;
+ }
+
+ if (strcmp(textLine, "@") == 0) {
+ creditsLines[lineCount].height = logoHeight;
+ lineTop += logoHeight;
+ } else {
+ creditsLines[lineCount].height = CREDITS_FONT_HEIGHT;
+ lineTop += CREDITS_FONT_HEIGHT;
+ }
+
+ if (strlen(textLine) > 0)
+ lineCount++;
+ else {
+ if (!hasCenterMark)
+ for (int j = paragraphStart; j < lineCount; j++)
+ creditsLines[j].type = LINE_CENTER;
+
+ paragraphStart = lineCount;
+ hasCenterMark = false;
+ }
+
+ pos = 0;
+ } else if (b == 0x0d) {
+ textLine[pos++] = 0;
+ } else
+ textLine[pos++] = b;
+ }
+
+ f.close();
+
+ // The paragraph detection above won't find the last paragraph, so we
+ // have to deal with it separately.
+
+ if (!hasCenterMark)
+ for (int j = paragraphStart; j < lineCount; j++)
+ creditsLines[j].type = LINE_CENTER;
+
+ // 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, and there is no attempt at synchronizing
+ // it with the credits scroll.
+
+ int32 pars[2];
+
+ pars[0] = 309;
+ pars[1] = FX_SPOT;
+ fnPlayMusic(pars);
+
+ _vm->_graphics->clearScene();
+ _vm->_graphics->setNeedFullRedraw();
+ _vm->_graphics->updateDisplay();
+ _vm->_graphics->fadeUp(0);
+
+ spriteInfo.scale = 0;
+ spriteInfo.scaledWidth = 0;
+ spriteInfo.scaledHeight = 0;
+ spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS;
+ spriteInfo.blend = 0;
+
+ int scrollPos = 0;
+
+ while (scrollPos < lineTop + CREDITS_FONT_HEIGHT) {
+ _vm->_graphics->clearScene();
+ _vm->_graphics->setNeedFullRedraw();
+
+ for (i = 0; i < lineCount; i++) {
+ // Free any sprites that have scrolled off the screen
+
+ if (creditsLines[i].top + creditsLines[i].height < scrollPos) {
+ if (creditsLines[i].sprite) {
+ _vm->_memory->freeMemory(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 (!creditsLines[i].sprite) {
+ debug(2, "Creating sprite '%s'", creditsLines[i].str);
+ creditsLines[i].sprite = _vm->_fontRenderer->makeTextSprite((uint8 *) creditsLines[i].str, 600, 14, _vm->_speechFontId, 0);
+ }
+
+ FrameHeader *frame = (FrameHeader *) creditsLines[i].sprite->ad;
+
+ spriteInfo.y = creditsLines[i].top - scrollPos;
+ spriteInfo.w = frame->width;
+ spriteInfo.h = frame->height;
+ spriteInfo.data = creditsLines[i].sprite->ad + sizeof(FrameHeader);
+
+ switch (creditsLines[i].type) {
+ case LINE_LEFT:
+ spriteInfo.x = 640 / 2 - 5 - frame->width;
+ break;
+ case LINE_RIGHT:
+ spriteInfo.x = 640 / 2 + 5;
+ break;
+ case LINE_CENTER:
+ if (strcmp(creditsLines[i].str, "@") == 0) {
+ spriteInfo.data = logoData;
+ spriteInfo.x = (640 - logoWidth) / 2;
+ spriteInfo.w = logoWidth;
+ spriteInfo.h = logoHeight;
+ } else
+ spriteInfo.x = (640 - frame->width) / 2;
+ break;
+ }
+
+ _vm->_graphics->drawSprite(&spriteInfo);
+ }
+ }
- _vm->_graphics->setPalette(0, 256, oldPal, RDPAL_FADE);
- _vm->_graphics->fadeUp();
_vm->_graphics->updateDisplay();
- _vm->buildDisplay();
- _vm->_graphics->waitForFade();
- _vm->_sound->muteFx(false);
- _vm->_sound->muteSpeech(false);
+ KeyboardEvent ke;
+
+ if (_vm->_input->readKey(&ke) == RD_OK && ke.keycode == 27) {
+ fnStopMusic(NULL);
+ break;
+ }
+
+ _vm->_system->delay_msecs(20);
+
+ scrollPos++;
}
- if (_vm->_features & GF_DEMO) {
- _vm->closeGame();
+ // 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)
+ _vm->_memory->freeMemory(creditsLines[i].sprite);
}
+ if (logoData)
+ free(logoData);
+
+ _vm->_graphics->fadeDown();
+ _vm->_graphics->waitForFade();
+
+ // The music should have stopped by now, but I suppose there is a
+ // slim chance it hasn't on a really, really fast computer.
+
+ while (_vm->_sound->musicTimeRemaining())
+ _vm->_system->delay_msecs(100);
+
+ _vm->_sound->restoreMusicState();
+ _vm->_sound->muteFx(false);
+ _vm->_sound->muteSpeech(false);
+
+ _vm->_thisScreen.new_palette = 99;
+
+ if (!_vm->_mouseStatus || _choosing)
+ _vm->setMouse(NORMAL_MOUSE_ID);
+
+ if (DEAD)
+ _vm->buildSystemMenu();
+
return IR_CONT;
}