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
This commit is contained in:
Torbjörn Andersson 2003-12-31 13:14:07 +00:00
parent 28eb8c4b76
commit 6899aa23cb

View file

@ -18,6 +18,7 @@
*/ */
#include "common/stdafx.h" #include "common/stdafx.h"
#include "common/file.h"
#include "sword2/sword2.h" #include "sword2/sword2.h"
#include "sword2/defs.h" #include "sword2/defs.h"
#include "sword2/interpreter.h" #include "sword2/interpreter.h"
@ -365,84 +366,350 @@ int32 Logic::fnResetGlobals(int32 *params) {
return IR_CONT; 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) { int32 Logic::fnPlayCredits(int32 *params) {
// This function just quits the game if this is the playable demo, ie. // This function just quits the game if this is the playable demo, ie.
// credits are NOT played in the demo any more! // credits are NOT played in the demo any more!
// params: none // params: none
if (!DEMO) { if (DEMO) {
uint8 oldPal[1024]; _vm->closeGame();
uint8 tmpPal[1024]; return IR_CONT;
int32 music_length; }
int32 pars[2];
_vm->_sound->saveMusicState(); // Prepare for the credits by fading down, stoping the music, etc.
_vm->_sound->muteFx(true); _vm->setMouse(0);
_vm->_sound->muteSpeech(true);
_vm->_sound->stopMusic();
memcpy(oldPal, _vm->_graphics->_palCopy, 1024); _vm->_sound->saveMusicState();
memset(tmpPal, 0, 1024);
_vm->_graphics->waitForFade(); _vm->_sound->muteFx(true);
_vm->_graphics->fadeDown(); _vm->_sound->muteSpeech(true);
_vm->_graphics->waitForFade(); _vm->_sound->stopMusic();
tmpPal[4] = 255; _vm->_graphics->waitForFade();
tmpPal[5] = 255; _vm->_graphics->fadeDown();
tmpPal[6] = 255; _vm->_graphics->waitForFade();
_vm->_graphics->setPalette(0, 256, tmpPal, RDPAL_INSTANT);
// Play the credits music. Is it enough with just one _vm->_graphics->closeMenuImmediately();
// repetition of it?
pars[0] = 309; // There are three files which I believe are involved in showing the
pars[1] = FX_SPOT; // credits:
fnPlayMusic(pars); //
// 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.
music_length = 1000 * _vm->_sound->musicTimeRemaining(); SpriteInfo spriteInfo;
File f;
int i;
debug(0, "Credits music length: ~%d ms", music_length); // Read the "Smacker" logo
_vm->_graphics->closeMenuImmediately(); f.open("credits.bmp");
while (_vm->_sound->musicTimeRemaining()) { if (!f.isOpen()) {
_vm->_graphics->clearScene(); warning("Can't find credits.bmp");
_vm->_graphics->setNeedFullRedraw(); return IR_CONT;
}
// FIXME: Draw the credits text. The actual text uint16 logoWidth = f.readUint16LE();
// messages are stored in credits.clu, and I'm guessing uint16 logoHeight = f.readUint16LE();
// that credits.bmp or font.clu may be the font.
_vm->_graphics->updateDisplay(); uint8 palette[1024];
KeyboardEvent ke; 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;
}
if (_vm->_input->readKey(&ke) == RD_OK && ke.keycode == 27) uint8 *logoData = (uint8 *) malloc(logoWidth * logoHeight);
break;
_vm->_system->delay_msecs(30); 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;
}
int lineTop = 400;
int lineCount = 0;
int pos = 0;
textLine[0] = 0;
int paragraphStart = 0;
bool hasCenterMark = false;
while (1) {
if (lineCount >= ARRAYSIZE(creditsLines)) {
warning("Too many credits lines");
break;
} }
fnStopMusic(NULL); byte b = f.readByte();
_vm->_sound->restoreMusicState();
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->_graphics->updateDisplay();
_vm->buildDisplay();
_vm->_graphics->waitForFade();
_vm->_sound->muteFx(false); KeyboardEvent ke;
_vm->_sound->muteSpeech(false);
if (_vm->_input->readKey(&ke) == RD_OK && ke.keycode == 27) {
fnStopMusic(NULL);
break;
}
_vm->_system->delay_msecs(20);
scrollPos++;
} }
if (_vm->_features & GF_DEMO) { // We're done. Clean up and try to put everything back where it was
_vm->closeGame(); // 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; return IR_CONT;
} }