summaryrefslogtreecommitdiff
path: root/src/uqm/credits.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/uqm/credits.c')
-rw-r--r--src/uqm/credits.c839
1 files changed, 839 insertions, 0 deletions
diff --git a/src/uqm/credits.c b/src/uqm/credits.c
new file mode 100644
index 0000000..1889484
--- /dev/null
+++ b/src/uqm/credits.c
@@ -0,0 +1,839 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "credits.h"
+
+#include "controls.h"
+#include "colors.h"
+#include "options.h"
+#include "oscill.h"
+#include "comm.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "settings.h"
+#include "sounds.h"
+#include "setup.h"
+#include "libs/graphics/drawable.h"
+#include <math.h>
+
+// Rates in pixel lines per second
+#define CREDITS_BASE_RATE 9
+#define CREDITS_MAX_RATE 130
+// Maximum frame rate
+#define CREDITS_FRAME_RATE 36
+
+#define CREDITS_TIMEOUT (ONE_SECOND * 5)
+
+#define TRANS_COLOR BRIGHT_BLUE_COLOR
+
+// Positive or negative scroll rate in pixel lines per second
+static int CreditsRate;
+
+static BOOLEAN OutTakesRunning;
+static BOOLEAN CreditsRunning;
+static STRING CreditsTab;
+static FRAME CreditsBack;
+
+// Context used for drawing to the screen
+static CONTEXT DrawContext;
+// Context used for pre-rendering a credits frame
+static CONTEXT LocalContext;
+// Pre-rendered frame, possibly with a cutout
+static FRAME CreditsFrame;
+// Size of the credits "window" (normally screen size)
+static EXTENT CreditsExtent;
+
+typedef struct
+{
+ FRAME frame;
+ int strIndex;
+} CreditTextFrame;
+
+#define MAX_CREDIT_FRAMES 32
+// Circular text frame buffer for scrolling
+// Text frames are generated as needed, and when a text frame scrolls
+// of the screen, it is destroyed
+static CreditTextFrame textFrames[MAX_CREDIT_FRAMES];
+// Index of first active frame in the circular buffer (the first frame
+// is the one on top)
+static int firstFrame;
+// Index of last active frame in the circular buffer + 1
+static int lastFrame;
+// Total height of all active frames in the circular buffer
+static int totalHeight;
+// Current vertical offset into the first text frame
+static int curFrameOfs;
+
+typedef struct
+{
+ int size;
+ RESOURCE res;
+ FONT font;
+} FONT_SIZE_DEF;
+
+static FONT_SIZE_DEF CreditsFont[] =
+{
+ { 13, PT13AA_FONT, 0 },
+ { 17, PT17AA_FONT, 0 },
+ { 45, PT45AA_FONT, 0 },
+ { 0, 0, 0 },
+};
+
+
+static FRAME
+Credits_MakeTransFrame (int w, int h, Color TransColor)
+{
+ FRAME OldFrame;
+ FRAME f;
+
+ f = CaptureDrawable (CreateDrawable (WANT_PIXMAP, w, h, 1));
+ SetFrameTransparentColor (f, TransColor);
+
+ OldFrame = SetContextFGFrame (f);
+ SetContextBackGroundColor (TransColor);
+ ClearDrawable ();
+ SetContextFGFrame (OldFrame);
+
+ return f;
+}
+
+static int
+ParseTextLines (TEXT *Lines, int MaxLines, char *Buffer)
+{
+ int i;
+ const char* pEnd = Buffer + strlen (Buffer);
+
+ for (i = 0; i < MaxLines && Buffer < pEnd; ++i, ++Lines)
+ {
+ char* pTerm = strchr (Buffer, '\n');
+ if (!pTerm)
+ pTerm = Buffer + strlen (Buffer);
+ *pTerm = '\0'; /* terminate string */
+ Lines->pStr = Buffer;
+ Lines->CharCount = ~0;
+ Buffer = pTerm + 1;
+ }
+ return i;
+}
+
+#define MAX_TEXT_LINES 50
+#define MAX_TEXT_COLS 5
+
+static FRAME
+Credits_RenderTextFrame (CONTEXT TempContext, int *istr, int dir,
+ Color BackColor, Color ForeColor)
+{
+ FRAME f;
+ CONTEXT OldContext;
+ FRAME OldFrame;
+ TEXT TextLines[MAX_TEXT_LINES];
+ char *pStr = NULL;
+ int size;
+ char salign[32];
+ char *scol;
+ int scaned;
+ int i, rows, cnt;
+ char buf[2048];
+ FONT_SIZE_DEF *fdef;
+ SIZE leading;
+ TEXT t;
+ RECT r;
+ typedef struct
+ {
+ TEXT_ALIGN align;
+ COORD basex;
+ } col_format_t;
+ col_format_t colfmt[MAX_TEXT_COLS];
+
+ if (*istr < 0 || *istr >= GetStringTableCount (CreditsTab))
+ { // check if next one is within range
+ int next_s = *istr + dir;
+
+ if (next_s < 0 || next_s >= GetStringTableCount (CreditsTab))
+ return 0;
+
+ *istr = next_s;
+ }
+
+ // skip empty lines
+ while (*istr >= 0 && *istr < GetStringTableCount (CreditsTab))
+ {
+ pStr = GetStringAddress (
+ SetAbsStringTableIndex (CreditsTab, *istr));
+ *istr += dir;
+ if (pStr && *pStr != '\0')
+ break;
+ }
+
+ if (!pStr || *pStr == '\0')
+ return 0;
+
+ if (2 != sscanf (pStr, "%d %31s %n", &size, salign, &scaned)
+ || size <= 0)
+ return 0;
+ pStr += scaned;
+
+ utf8StringCopy (buf, sizeof (buf), pStr);
+ rows = ParseTextLines (TextLines, MAX_TEXT_LINES, buf);
+ if (rows == 0)
+ return 0;
+ // parse text columns
+ for (i = 0, cnt = rows; i < rows; ++i)
+ {
+ char *nextcol;
+ int icol;
+
+ // we abuse the baseline here, but only a tiny bit
+ // every line starts at col 0
+ TextLines[i].baseline.x = 0;
+ TextLines[i].baseline.y = i + 1;
+
+ for (icol = 1, nextcol = strchr (TextLines[i].pStr, '\t');
+ icol < MAX_TEXT_COLS && nextcol;
+ ++icol, nextcol = strchr (nextcol, '\t'))
+ {
+ *nextcol = '\0';
+ ++nextcol;
+
+ if (cnt < MAX_TEXT_LINES)
+ {
+ TextLines[cnt].pStr = nextcol;
+ TextLines[cnt].CharCount = ~0;
+ TextLines[cnt].baseline.x = icol;
+ TextLines[cnt].baseline.y = i + 1;
+ ++cnt;
+ }
+ }
+ }
+
+ // init alignments
+ for (i = 0; i < MAX_TEXT_COLS; ++i)
+ {
+ colfmt[i].align = ALIGN_LEFT;
+ colfmt[i].basex = CreditsExtent.width / 64;
+ }
+
+ // find the right font
+ for (fdef = CreditsFont; fdef->size && size > fdef->size; ++fdef)
+ ;
+ if (!fdef->size)
+ return 0;
+
+ t.align = ALIGN_LEFT;
+ t.baseline.x = 100; // any value will do
+ t.baseline.y = 100; // any value will do
+ t.pStr = " ";
+ t.CharCount = 1;
+
+ OldContext = SetContext (TempContext);
+
+ // get font dimensions
+ SetContextFont (fdef->font);
+ GetContextFontLeading (&leading);
+ // get left/right margin
+ TextRect (&t, &r, NULL);
+
+ // parse text column alignment
+ for (i = 0, scol = strtok (salign, ",");
+ scol && i < MAX_TEXT_COLS;
+ ++i, scol = strtok (NULL, ","))
+ {
+ char c;
+ int x;
+ int n;
+
+ // default
+ colfmt[i].align = ALIGN_LEFT;
+ colfmt[i].basex = r.extent.width;
+
+ n = sscanf (scol, "%c/%d", &c, &x);
+ if (n < 1)
+ { // DOES NOT COMPUTE! :)
+ continue;
+ }
+
+ switch (c)
+ {
+ case 'L':
+ colfmt[i].align = ALIGN_LEFT;
+ if (n >= 2)
+ colfmt[i].basex = x;
+ break;
+ case 'C':
+ colfmt[i].align = ALIGN_CENTER;
+ if (n >= 2)
+ colfmt[i].basex = x;
+ else
+ colfmt[i].basex = CreditsExtent.width / 2;
+ break;
+ case 'R':
+ colfmt[i].align = ALIGN_RIGHT;
+ if (n >= 2)
+ colfmt[i].basex = x;
+ else
+ colfmt[i].basex = CreditsExtent.width - r.extent.width;
+ break;
+ }
+ }
+
+ for (i = 0; i < cnt; ++i)
+ {
+ // baseline contains coords in row/col quantities
+ col_format_t *fmt = colfmt + TextLines[i].baseline.x;
+
+ TextLines[i].align = fmt->align;
+ TextLines[i].baseline.x = fmt->basex;
+ TextLines[i].baseline.y *= leading;
+ }
+
+ f = Credits_MakeTransFrame (CreditsExtent.width, leading * rows + (leading >> 1),
+ BackColor);
+ OldFrame = SetContextFGFrame (f);
+ // draw text
+ SetContextForeGroundColor (ForeColor);
+ for (i = 0; i < cnt; ++i)
+ font_DrawText (TextLines + i);
+
+ SetContextFGFrame (OldFrame);
+ SetContext (OldContext);
+
+ return f;
+}
+
+static inline int
+frameIndex (int index)
+{
+ // Make sure index is positive before %
+ return (index + MAX_CREDIT_FRAMES) % MAX_CREDIT_FRAMES;
+}
+
+static void
+RenderCreditsScreen (CONTEXT targetContext)
+{
+ CONTEXT oldContext;
+ STAMP s;
+ int i;
+
+ oldContext = SetContext (targetContext);
+ // draw background
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = CreditsBack;
+ DrawStamp (&s);
+
+ // draw text frames
+ s.origin.y = -curFrameOfs;
+ for (i = firstFrame; i != lastFrame; i = frameIndex (i + 1))
+ {
+ RECT fr;
+
+ s.frame = textFrames[i].frame;
+ DrawStamp (&s);
+ GetFrameRect (s.frame, &fr);
+ s.origin.y += fr.extent.height;
+ }
+
+ if (OutTakesRunning)
+ { // Cut out the Outtakes rect
+ SetContextForeGroundColor (TRANS_COLOR);
+ DrawFilledRectangle (&CommWndRect);
+ }
+
+ SetContext (oldContext);
+}
+
+static void
+InitCredits (void)
+{
+ RECT ctxRect;
+ CONTEXT oldContext;
+ FRAME targetFrame;
+
+ memset (textFrames, 0, sizeof textFrames);
+
+ LocalContext = CreateContext ("Credits.LocalContext");
+ DrawContext = CreateContext ("Credits.DrawContext");
+
+ targetFrame = GetContextFGFrame ();
+ GetContextClipRect (&ctxRect);
+ CreditsExtent = ctxRect.extent;
+
+ // prep our local context
+ oldContext = SetContext (LocalContext);
+ // Local screen copy. We draw everything to this frame, then cut
+ // the Outtakes rect out and draw this frame to the screen.
+ CreditsFrame = Credits_MakeTransFrame (CreditsExtent.width,
+ CreditsExtent.height, TRANS_COLOR);
+ SetContextFGFrame (CreditsFrame);
+
+ // The first credits frame is fake, the height of the screen,
+ // so that the credits can roll in from the bottom
+ textFrames[0].frame = Credits_MakeTransFrame (1, CreditsExtent.height,
+ TRANS_COLOR);
+ textFrames[0].strIndex = -1;
+ firstFrame = 0;
+ lastFrame = firstFrame + 1;
+
+ totalHeight = GetFrameHeight (textFrames[0].frame);
+ curFrameOfs = 0;
+
+ // We use an own screen draw context to avoid collisions
+ SetContext (DrawContext);
+ SetContextFGFrame (targetFrame);
+
+ SetContext (oldContext);
+
+ // Prepare the first screen frame
+ RenderCreditsScreen (LocalContext);
+
+ CreditsRate = CREDITS_BASE_RATE;
+ CreditsRunning = TRUE;
+}
+
+static void
+freeCreditTextFrame (CreditTextFrame *tf)
+{
+ DestroyDrawable (ReleaseDrawable (tf->frame));
+ tf->frame = NULL;
+}
+
+static void
+UninitCredits (void)
+{
+ DestroyContext (DrawContext);
+ DrawContext = NULL;
+ DestroyContext (LocalContext);
+ LocalContext = NULL;
+
+ // free remaining frames
+ DestroyDrawable (ReleaseDrawable (CreditsFrame));
+ CreditsFrame = NULL;
+ for ( ; firstFrame != lastFrame; firstFrame = frameIndex (firstFrame + 1))
+ freeCreditTextFrame (&textFrames[firstFrame]);
+}
+
+static int
+calcDeficitHeight (void)
+{
+ int i;
+ int maxPos;
+
+ maxPos = -curFrameOfs;
+ for (i = firstFrame; i != lastFrame; i = frameIndex (i + 1))
+ {
+ RECT fr;
+
+ GetFrameRect (textFrames[i].frame, &fr);
+ maxPos += fr.extent.height;
+ }
+
+ return CreditsExtent.height - maxPos;
+}
+
+static void
+processCreditsFrame (void)
+{
+ static TimeCount NextTime;
+ TimeCount Now = GetTimeCounter ();
+
+ if (Now >= NextTime)
+ {
+ RECT fr;
+ CONTEXT OldContext;
+ int rate, direction, dirstep;
+ int deficitHeight;
+ STAMP s;
+
+ rate = abs (CreditsRate);
+ if (rate != 0)
+ {
+ // scroll direction; forward or backward
+ direction = CreditsRate / rate;
+ // step in pixels
+ dirstep = (rate + CREDITS_FRAME_RATE - 1) / CREDITS_FRAME_RATE;
+ rate = ONE_SECOND * dirstep / rate;
+ // step is also directional
+ dirstep *= direction;
+ }
+ else
+ { // scroll stopped
+ direction = 0;
+ dirstep = 0;
+ // one second interframe
+ rate = ONE_SECOND;
+ }
+
+ NextTime = GetTimeCounter () + rate;
+
+ // draw the credits
+ // comm animations play with contexts so we need to make
+ // sure the context is not desynced
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = CreditsFrame;
+
+ OldContext = SetContext (DrawContext);
+ DrawStamp (&s);
+ SetContext (OldContext);
+ FlushGraphics ();
+
+ // prepare next screen frame
+ deficitHeight = calcDeficitHeight ();
+ curFrameOfs += dirstep;
+ // cap scroll
+ if (curFrameOfs < -(CreditsExtent.height / 20))
+ { // at the begining, deceleration
+ if (CreditsRate < 0)
+ CreditsRate -= CreditsRate / 10 - 1;
+ }
+ else if (deficitHeight > CreditsExtent.height / 25)
+ { // frame deficit -- credits almost over, deceleration
+ if (CreditsRate > 0)
+ CreditsRate -= CreditsRate / 10 + 1;
+
+ CreditsRunning = (CreditsRate != 0);
+ }
+ else if (!CreditsRunning)
+ { // resumed
+ CreditsRunning = TRUE;
+ }
+
+ if (firstFrame != lastFrame)
+ { // clean up frames that scrolled off the screen
+ if (direction > 0)
+ { // forward scroll
+ GetFrameRect (textFrames[firstFrame].frame, &fr);
+ if (curFrameOfs >= fr.extent.height)
+ { // past this frame already
+ totalHeight -= fr.extent.height;
+ freeCreditTextFrame (&textFrames[firstFrame]);
+ // next frame
+ firstFrame = frameIndex (firstFrame + 1);
+ curFrameOfs -= fr.extent.height;
+ }
+ }
+ else if (direction < 0)
+ { // backward scroll
+ int index = frameIndex (lastFrame - 1);
+ int framePos;
+
+ GetFrameRect (textFrames[index].frame, &fr);
+ framePos = totalHeight - curFrameOfs - fr.extent.height;
+ if (framePos >= CreditsExtent.height)
+ { // past this frame already
+ lastFrame = index;
+ totalHeight -= fr.extent.height;
+ freeCreditTextFrame (&textFrames[lastFrame]);
+ }
+ }
+ }
+
+ // render new text frames if needed
+ if (direction > 0)
+ { // forward scroll
+ int next_s = 0;
+
+ // get next string
+ if (firstFrame != lastFrame)
+ next_s = textFrames[frameIndex (lastFrame - 1)].strIndex + 1;
+
+ while (totalHeight - curFrameOfs < CreditsExtent.height
+ && next_s < GetStringTableCount (CreditsTab))
+ {
+ CreditTextFrame *tf = &textFrames[lastFrame];
+
+ tf->frame = Credits_RenderTextFrame (LocalContext, &next_s,
+ direction, BLACK_COLOR, CREDITS_TEXT_COLOR);
+ tf->strIndex = next_s - 1;
+ if (tf->frame)
+ {
+ GetFrameRect (tf->frame, &fr);
+ totalHeight += fr.extent.height;
+
+ lastFrame = frameIndex (lastFrame + 1);
+ }
+ }
+ }
+ else if (direction < 0)
+ { // backward scroll
+ int next_s = GetStringTableCount (CreditsTab) - 1;
+
+ // get next string
+ if (firstFrame != lastFrame)
+ next_s = textFrames[firstFrame].strIndex - 1;
+
+ while (curFrameOfs < 0 && next_s >= 0)
+ {
+ int index = frameIndex (firstFrame - 1);
+ CreditTextFrame *tf = &textFrames[index];
+
+ tf->frame = Credits_RenderTextFrame (LocalContext, &next_s,
+ direction, BLACK_COLOR, CREDITS_TEXT_COLOR);
+ tf->strIndex = next_s + 1;
+ if (tf->frame)
+ {
+ GetFrameRect (tf->frame, &fr);
+ totalHeight += fr.extent.height;
+
+ firstFrame = index;
+ curFrameOfs += fr.extent.height;
+ }
+ }
+ }
+
+ // draw next screen frame
+ RenderCreditsScreen (LocalContext);
+ }
+}
+
+static BOOLEAN
+LoadCredits (void)
+{
+ FONT_SIZE_DEF *fdef;
+
+ CreditsTab = CaptureStringTable (LoadStringTable (CREDITS_STRTAB));
+ if (!CreditsTab)
+ return FALSE;
+ CreditsBack = CaptureDrawable (LoadGraphic (CREDITS_BACK_ANIM));
+ // load fonts
+ for (fdef = CreditsFont; fdef->size; ++fdef)
+ fdef->font = LoadFont (fdef->res);
+
+ return TRUE;
+}
+
+static void
+FreeCredits (void)
+{
+ FONT_SIZE_DEF *fdef;
+
+ DestroyStringTable (ReleaseStringTable (CreditsTab));
+ CreditsTab = NULL;
+
+ DestroyDrawable (ReleaseDrawable (CreditsBack));
+ CreditsBack = NULL;
+
+ // free fonts
+ for (fdef = CreditsFont; fdef->size; ++fdef)
+ {
+ DestroyFont (fdef->font);
+ fdef->font = NULL;
+ }
+}
+
+static void
+OutTakes (void)
+{
+#define NUM_OUTTAKES 15
+ static CONVERSATION outtake_list[NUM_OUTTAKES] =
+ {
+ ZOQFOTPIK_CONVERSATION,
+ TALKING_PET_CONVERSATION,
+ ORZ_CONVERSATION,
+ UTWIG_CONVERSATION,
+ THRADD_CONVERSATION,
+ SUPOX_CONVERSATION,
+ SYREEN_CONVERSATION,
+ SHOFIXTI_CONVERSATION,
+ PKUNK_CONVERSATION,
+ YEHAT_CONVERSATION,
+ DRUUGE_CONVERSATION,
+ URQUAN_CONVERSATION,
+ VUX_CONVERSATION,
+ BLACKURQ_CONVERSATION,
+ ARILOU_CONVERSATION
+ };
+
+ BOOLEAN oldsubtitles = optSubtitles;
+ int i = 0;
+
+ // Outtakes have no voice tracks, so the subtitles are always on
+ optSubtitles = TRUE;
+ sliderDisabled = TRUE;
+ oscillDisabled = TRUE;
+
+ for (i = 0; (i < NUM_OUTTAKES) &&
+ !(GLOBAL (CurrentActivity) & CHECK_ABORT); i++)
+ {
+ SetCommIntroMode (CIM_CROSSFADE_WINDOW, 0);
+ InitCommunication (outtake_list[i]);
+ }
+
+ optSubtitles = oldsubtitles;
+ sliderDisabled = FALSE;
+ oscillDisabled = FALSE;
+}
+
+typedef struct
+{
+ // standard state required by DoInput
+ BOOLEAN (*InputFunc) (void *pInputState);
+
+ BOOLEAN AllowCancel;
+ BOOLEAN AllowSpeedChange;
+ BOOLEAN CloseWhenDone;
+ DWORD CloseTimeOut;
+
+} CREDITS_INPUT_STATE;
+
+static BOOLEAN
+DoCreditsInput (void *pIS)
+{
+ CREDITS_INPUT_STATE *pCIS = (CREDITS_INPUT_STATE *) pIS;
+
+ if (CreditsRunning)
+ { // cancel timeout if resumed (or just running)
+ pCIS->CloseTimeOut = 0;
+ }
+
+ if ((GLOBAL (CurrentActivity) & CHECK_ABORT)
+ || (pCIS->AllowCancel &&
+ (PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL]))
+ )
+ { // aborted
+ return FALSE;
+ }
+
+ if (pCIS->AllowSpeedChange
+ && (PulsedInputState.menu[KEY_MENU_UP]
+ || PulsedInputState.menu[KEY_MENU_DOWN]))
+ { // speed adjustment
+ int newrate = CreditsRate;
+ int step = abs (CreditsRate) / 5 + 1;
+
+ if (PulsedInputState.menu[KEY_MENU_DOWN])
+ newrate += step;
+ else if (PulsedInputState.menu[KEY_MENU_UP])
+ newrate -= step;
+ if (newrate < -CREDITS_MAX_RATE)
+ newrate = -CREDITS_MAX_RATE;
+ else if (newrate > CREDITS_MAX_RATE)
+ newrate = CREDITS_MAX_RATE;
+
+ CreditsRate = newrate;
+ }
+
+ if (!CreditsRunning)
+ { // always allow cancelling once credits run through
+ pCIS->AllowCancel = TRUE;
+ }
+
+ if (!CreditsRunning && pCIS->CloseWhenDone)
+ { // auto-close controlled by timeout
+ if (pCIS->CloseTimeOut == 0)
+ { // set timeout
+ pCIS->CloseTimeOut = GetTimeCounter () + CREDITS_TIMEOUT;
+ }
+ else if (GetTimeCounter () > pCIS->CloseTimeOut)
+ { // all done!
+ return FALSE;
+ }
+ }
+
+ if (!CreditsRunning
+ && (PulsedInputState.menu[KEY_MENU_SELECT]
+ || PulsedInputState.menu[KEY_MENU_CANCEL]))
+ { // credits finished and exit requested
+ return FALSE;
+ }
+
+ SleepThread (ONE_SECOND / CREDITS_FRAME_RATE);
+
+ return TRUE;
+}
+
+static void
+on_input_frame (void)
+{
+ processCreditsFrame ();
+}
+
+void
+Credits (BOOLEAN WithOuttakes)
+{
+ MUSIC_REF hMusic;
+ CREDITS_INPUT_STATE cis;
+ RECT screenRect;
+ STAMP s;
+
+ hMusic = LoadMusic (CREDITS_MUSIC);
+
+ SetContext (ScreenContext);
+ SetContextClipRect (NULL);
+ GetContextClipRect (&screenRect);
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+
+ if (!LoadCredits ())
+ return;
+
+ // Fade in the background
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = CreditsBack;
+ DrawStamp (&s);
+ FadeScreen (FadeAllToColor, ONE_SECOND / 2);
+
+ // set the position of outtakes comm
+ CommWndRect.corner.x = (screenRect.extent.width - CommWndRect.extent.width)
+ / 2;
+ CommWndRect.corner.y = 5;
+
+ InitCredits ();
+ SetInputCallback (on_input_frame);
+
+ if (WithOuttakes)
+ {
+ OutTakesRunning = TRUE;
+ OutTakes ();
+ OutTakesRunning = FALSE;
+ }
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (hMusic)
+ PlayMusic (hMusic, TRUE, 1);
+
+ // nothing to do now but wait until credits
+ // are done or canceled by user
+ cis.InputFunc = DoCreditsInput;
+ cis.AllowCancel = !WithOuttakes;
+ cis.CloseWhenDone = !WithOuttakes;
+ cis.AllowSpeedChange = TRUE;
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ DoInput (&cis, TRUE);
+ }
+
+ SetInputCallback (NULL);
+ FadeMusic (0, ONE_SECOND / 2);
+ UninitCredits ();
+
+ SetContext (ScreenContext);
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+ FlushColorXForms ();
+
+ if (hMusic)
+ {
+ StopMusic ();
+ DestroyMusic (hMusic);
+ }
+ FadeMusic (NORMAL_VOLUME, 0);
+
+ FreeCredits ();
+}