shithub: choc

Download patch

ref: 3228ddb99f72d00c089fc7a87d1edfec2c4519fc
parent: 4c9a85fa61a4298b342a76d4ac128c036b199055
author: James Haley <[email protected]>
date: Tue Sep 14 12:42:09 EDT 2010

Fully implemented all finale code, including "intermission" speeches by
Macil and all three endings.

Subversion-branch: /branches/strife-branch
Subversion-revision: 2086

--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -262,6 +262,7 @@
          WI_Drawer ();
          break;
      */
+
     case GS_FINALE:
         F_Drawer ();
         break;
@@ -268,6 +269,9 @@
 
     case GS_DEMOSCREEN:
         D_PageDrawer ();
+        break;
+    
+    default:
         break;
     }
     
--- a/src/strife/d_main.h
+++ b/src/strife/d_main.h
@@ -54,6 +54,7 @@
 
 extern  gameaction_t    gameaction;
 extern  boolean         isregistered;   // villsa [STRIFE]
+extern  boolean        isdemoversion;  // haleyjd [STRIFE]
 extern  boolean         stonecold;      // villsa [STRIFE]
 extern  boolean         workparm;       // villsa [STRIFE]
 
--- a/src/strife/doomdef.h
+++ b/src/strife/doomdef.h
@@ -55,7 +55,7 @@
 typedef enum
 {
     GS_LEVEL,
-    GS_INTERMISSION,
+    GS_UNKNOWN,
     GS_FINALE,
     GS_DEMOSCREEN,
 } gamestate_t;
@@ -234,19 +234,19 @@
     tk_quest30,
     tk_quest31,
     tk_quest32,   // most likely unused
-    tk_numquests,
+    tk_numquests
 } questtype_t;
 
 // haleyjd 09/12/10: [STRIFE]
 // flag values for each quest.
 enum
-{
+{ //  Name      Flag from bitnum    Purpose, if known
     QF_QUEST1  = (1 << tk_quest1),
     QF_QUEST2  = (1 << tk_quest2),
     QF_QUEST3  = (1 << tk_quest3), 
     QF_QUEST4  = (1 << tk_quest4),
     QF_QUEST5  = (1 << tk_quest5),
-    QF_QUEST6  = (1 << tk_quest6),
+    QF_QUEST6  = (1 << tk_quest6),  // Destroyed the Power Coupling
     QF_QUEST7  = (1 << tk_quest7),
     QF_QUEST8  = (1 << tk_quest8),
     QF_QUEST9  = (1 << tk_quest9),
@@ -265,14 +265,14 @@
     QF_QUEST22 = (1 << tk_quest22),
     QF_QUEST23 = (1 << tk_quest23),
     QF_QUEST24 = (1 << tk_quest24),
-    QF_QUEST25 = (1 << tk_quest25),
+    QF_QUEST25 = (1 << tk_quest25), // Destroyed the Converter
     QF_QUEST26 = (1 << tk_quest26),
     QF_QUEST27 = (1 << tk_quest27),
     QF_QUEST28 = (1 << tk_quest28),
-    QF_QUEST29 = (1 << tk_quest29),
+    QF_QUEST29 = (1 << tk_quest29), // Destroyed the Mines Transmitter
     QF_QUEST30 = (1 << tk_quest30),
     QF_QUEST31 = (1 << tk_quest31),
-    QF_QUEST32 = (1 << tk_quest32), // note seems to be unused
+    QF_QUEST32 = (1 << tk_quest32), // Note: seems to be unused
     
     QF_ALLQUESTS  = (QF_QUEST31 + (QF_QUEST31 - 1)) // does not include bit 32!
 };
--- a/src/strife/f_finale.c
+++ b/src/strife/f_finale.c
@@ -2,7 +2,9 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1996 Rogue Entertainment / Velocity, Inc.
 // Copyright(C) 2005 Simon Howard
+// Copyright(C) 2010 James Haley, Samuel Villareal
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -22,6 +24,8 @@
 // DESCRIPTION:
 //	Game completion, final screen animation.
 //
+// [STRIFE] Module marked finished 2010-09-13 22:56
+//
 //-----------------------------------------------------------------------------
 
 
@@ -45,6 +49,8 @@
 #include "doomstat.h"
 #include "r_state.h"
 
+#include "p_dialog.h" // [STRIFE]
+
 typedef enum
 {
     F_STAGE_TEXT,
@@ -67,6 +73,8 @@
 int   slideshow_tics;
 int   slideshow_state;
 
+// haleyjd 09/13/10: [STRIFE] All this is unused.
+/*
 #define	TEXTSPEED	3
 #define	TEXTWAIT	250
 
@@ -109,6 +117,7 @@
 
 char*	finaletext;
 char*	finaleflat;
+*/
 
 void	F_StartCast (void);
 void	F_CastTicker (void);
@@ -115,9 +124,53 @@
 boolean F_CastResponder (event_t *ev);
 void	F_CastDrawer (void);
 
+// [STRIFE] - Slideshow states enumeration
+enum
+{
+    // Exit states
+    SLIDE_EXITHACK    = -99, // Hacky exit - start a new dialog
+    SLIDE_EXIT        =  -1, // Exit to next finale state
+    SLIDE_CHOCO       =  -2, // haleyjd: This state is Choco-specific... see below.
+
+    // Unknown
+    SLIDE_UNKNOWN     =   0, // Dunno what it's for, possibly unused
+
+    // MAP03 - Macil's Programmer exposition
+    SLIDE_PROGRAMMER1 =   1, 
+    SLIDE_PROGRAMMER2,
+    SLIDE_PROGRAMMER3,
+    SLIDE_PROGRAMMER4, // Next state = -99
+
+    // MAP10 - Macil's Sigil exposition
+    SLIDE_SIGIL1      =   5,
+    SLIDE_SIGIL2,
+    SLIDE_SIGIL3,
+    SLIDE_SIGIL4, // Next state = -99
+
+    // MAP29 - Endings
+    // Good Ending
+    SLIDE_GOODEND1    =  10,
+    SLIDE_GOODEND2,
+    SLIDE_GOODEND3,
+    SLIDE_GOODEND4, // Next state = -1
+
+    // Bad Ending
+    SLIDE_BADEND1     =  14,
+    SLIDE_BADEND2,
+    SLIDE_BADEND3, // Next state = -1
+
+    // Blah Ending
+    SLIDE_BLAHEND1    = 17,
+    SLIDE_BLAHEND2,
+    SLIDE_BLAHEND3 // Next state = -1
+};
+
 //
 // F_StartFinale
 //
+// [STRIFE]
+// haleyjd 09/13/10: Modified to drive slideshow sequences.
+//
 void F_StartFinale (void)
 {
     patch_t *panel;
@@ -135,132 +188,307 @@
     panel = (patch_t *)W_CacheLumpName(slideshow_panel, PU_CACHE);
     V_DrawPatch(0, 0, panel);
 
-    // haleyjd: NOT FINISHED
-#if 0
     switch(gamemap)
     {
-    case 3:
-        slideshow_state = 1;
+    case 3:  // Macil's exposition on the Programmer
+        slideshow_state = SLIDE_PROGRAMMER1;
         break;
-    case 9:
-        slideshow_state = -99;
+    case 9:  // Super hack for death of Programmer
+        slideshow_state = SLIDE_EXITHACK; 
         break;
-    case 10:
-        slideshow_state = 5;
+    case 10: // Macil's exposition on the Sigil
+        slideshow_state = SLIDE_SIGIL1;
         break;
-    case 29:
+    case 29: // Endings
         if(!netgame)
         {
-            if(players[0].health < 0)
-                slideshow_state = 17;
+            if(players[0].health <= 0)            // Bad ending 
+                slideshow_state = SLIDE_BADEND1;  // - Humanity goes extinct
             else
             {
+                if((players[0].questflags & QF_QUEST25) && // Converter destroyed
+                   (players[0].questflags & QF_QUEST27))   // ????
+                {
+                    // Good ending - You get the hot babe.
+                    slideshow_state = SLIDE_GOODEND1; 
+                }
+                else
+                {
+                    // Blah ending - You win the battle, but fail at life.
+                    slideshow_state = SLIDE_BLAHEND1;
+                }
             }
         }
+        break;
+    case 34: // For the demo version ending
+        slideshow_state = SLIDE_EXIT;
+        break;
     }
-#endif
 
-    // Find the right screen and set the text and background
-
-    for (i=0; i<arrlen(textscreens); ++i)
-    {
-        textscreen_t *screen = &textscreens[i];
-
-        // Hack for Chex Quest
-
-        if (gameversion == exe_chex && screen->mission == doom)
-        {
-            screen->level = 5;
-        }
-
-        if (gamemission == screen->mission
-         && (gamemission != doom || gameepisode == screen->episode)
-         && gamemap == screen->level)
-        {
-            finaletext = screen->text;
-            finaleflat = screen->background;
-        }
-    }
-
-    // Do dehacked substitutions of strings
-  
-    finaletext = DEH_String(finaletext);
-    finaleflat = DEH_String(finaleflat);
-    
+    S_ChangeMusic(mus_dark, 1);
+    slideshow_tics = 7;
     finalestage = F_STAGE_TEXT;
     finalecount = 0;
-	
 }
 
-
-
+//
+// F_Responder
+//
+// [STRIFE] Verified unmodified
+//
 boolean F_Responder (event_t *event)
 {
     if (finalestage == F_STAGE_CAST)
-	return F_CastResponder (event);
-	
+        return F_CastResponder (event);
+
     return false;
 }
 
+//
+// F_WaitTicker
+//
+// [STRIFE] New function
+// haleyjd 09/13/10: This is called from G_Ticker if gamestate is 1, but we
+// have no idea for what it's supposed to be. It may in fact be unused.
+// STRIFE-TODO: Determine if this is really used or not!
+//
+void F_WaitTicker(void)
+{
+    if(++finalecount >= 250)
+    {
+        gamestate   = GS_FINALE;
+        finalestage = 0;
+        finalecount = 0;
+    }
+}
 
+// 
+// F_DoSlideShow
 //
+// [STRIFE] New function
+// haleyjd 09/13/10: Handles slideshow states. Begging to be tabulated!
+//
+static void F_DoSlideShow(void)
+{
+    patch_t *patch;
+
+    switch(slideshow_state)
+    {
+    case SLIDE_UNKNOWN: // state #0, seems to be unused
+        slideshow_tics = 700;
+        slideshow_state = SLIDE_EXIT;
+        // falls through into state 1, so above is pointless? ...
+
+    case SLIDE_PROGRAMMER1: // state #1
+        slideshow_panel = DEH_String("SS2F1");
+        I_StartVoice(DEH_String("MAC10"));
+        slideshow_state = SLIDE_PROGRAMMER2;
+        slideshow_tics = 315;
+        break;
+    case SLIDE_PROGRAMMER2: // state #2
+        slideshow_panel = DEH_String("SS2F2");
+        I_StartVoice(DEH_String("MAC11"));
+        slideshow_state = SLIDE_PROGRAMMER3;
+        slideshow_tics = 350;
+        break;
+    case SLIDE_PROGRAMMER3: // state #3
+        slideshow_panel = DEH_String("SS2F3");
+        I_StartVoice(DEH_String("MAC12"));
+        slideshow_state = SLIDE_PROGRAMMER4;
+        slideshow_tics = 420;
+        break;
+    case SLIDE_PROGRAMMER4: // state #4
+        slideshow_panel = DEH_String("SS2F4");
+        I_StartVoice(DEH_String("MAC13"));
+        slideshow_state = SLIDE_EXITHACK; // End of slides
+        slideshow_tics = 595;
+        break;
+
+    case SLIDE_SIGIL1: // state #5
+        slideshow_panel = DEH_String("SS3F1");
+        I_StartVoice("MAC16");
+        slideshow_state = SLIDE_SIGIL2;
+        slideshow_tics = 350;
+        break;
+    case SLIDE_SIGIL2: // state #6
+        slideshow_panel = DEH_String("SS3F2");
+        I_StartVoice("MAC17");
+        slideshow_state = SLIDE_SIGIL3;
+        slideshow_tics = 420;
+        break;
+    case SLIDE_SIGIL3: // state #7
+        slideshow_panel = DEH_String("SS3F3");
+        I_StartVoice(DEH_String("MAC18"));
+        slideshow_tics = 420;
+        slideshow_state = SLIDE_SIGIL4;
+        break;
+    case SLIDE_SIGIL4: // state #8
+        slideshow_panel = DEH_String("SS3F4");
+        I_StartVoice(DEH_String("MAC19"));
+        slideshow_tics = 385;
+        slideshow_state = SLIDE_EXITHACK; // End of slides
+        break;
+
+    case SLIDE_GOODEND1: // state #10
+        slideshow_panel = DEH_String("SS4F2");
+        S_StartMusic(mus_happy);
+        I_StartVoice(DEH_String("RIE01"));
+        slideshow_state = SLIDE_GOODEND2;
+        slideshow_tics = 455;
+        break;
+    case SLIDE_GOODEND2: // state #11
+        slideshow_panel = DEH_String("SS4F2");
+        I_StartVoice(DEH_String("BBX01"));
+        slideshow_state = SLIDE_GOODEND3;
+        slideshow_tics = 385;
+        break;
+    case SLIDE_GOODEND3: // state #12
+        slideshow_panel = DEH_String("SS4F3");
+        I_StartVoice(DEH_String("BBX02"));
+        slideshow_state = SLIDE_GOODEND4;
+        slideshow_tics = 490;
+        break;
+    case SLIDE_GOODEND4: // state #13
+        slideshow_panel = DEH_String("SS4F4");
+        slideshow_state = SLIDE_EXIT; // Go to end credits
+        slideshow_tics = 980;
+        break;
+
+    case SLIDE_BADEND1: // state #14
+        S_StartMusic(mus_sad);
+        slideshow_panel = DEH_String("SS5F1");
+        I_StartVoice(DEH_String("SS501b"));
+        slideshow_state = SLIDE_BADEND2;
+        slideshow_tics = 385;
+        break;
+    case SLIDE_BADEND2: // state #15
+        slideshow_panel = DEH_String("SS5F2");
+        I_StartVoice(DEH_String("SS502b"));
+        slideshow_state = SLIDE_BADEND3;
+        slideshow_tics = 350;
+        break;
+    case SLIDE_BADEND3: // state #16
+        slideshow_panel = DEH_String("SS5F3");
+        I_StartVoice(DEH_String("SS503b"));
+        slideshow_state = SLIDE_EXIT; // Go to end credits
+        slideshow_tics = 385;
+        break;
+
+    case SLIDE_BLAHEND1: // state #17
+        S_StartMusic(mus_end);
+        slideshow_panel = DEH_String("SS6F1");
+        I_StartVoice(DEH_String("SS601A"));
+        slideshow_state = SLIDE_BLAHEND2;
+        slideshow_tics = 280;
+        break;
+    case SLIDE_BLAHEND2: // state #18
+        S_StartMusic(mus_end);
+        slideshow_panel = DEH_String("SS6F2");
+        I_StartVoice(DEH_String("SS602A"));
+        slideshow_state = SLIDE_BLAHEND3;
+        slideshow_tics = 280;
+        break;
+    case SLIDE_BLAHEND3: // state #19
+        S_StartMusic(mus_end);
+        slideshow_panel = DEH_String("SS6F3");
+        I_StartVoice(DEH_String("SS603A"));
+        slideshow_state = SLIDE_EXIT; // Go to credits
+        slideshow_tics = 315;
+        break;
+
+    case SLIDE_EXITHACK: // state -99: super hack state
+        gamestate = GS_LEVEL;
+        P_DialogStartP1();
+        break;
+    case SLIDE_EXIT: // state -1: proceed to next finale stage
+        finalecount = 0;
+        finalestage = F_STAGE_ARTSCREEN;
+        wipegamestate = -1;
+        S_StartMusic(mus_fast);
+        slideshow_state = SLIDE_CHOCO; // haleyjd: see below...
+        break;
+    case SLIDE_CHOCO: 
+        // haleyjd 09/14/10: This wouldn't be necessary except that Choco
+        // doesn't support the V_MarkRect dirty rectangles system. This
+        // just so happens to have hidden the fact that the ending
+        // does a screenfade every ~19 seconds due to remaining stuck in
+        // SLIDE_EXIT state above, UNLESS the menus were active - the
+        // V_MarkRect calls in the menu system cause it to be visible. 
+        // This means that in order to get the same behavior as the vanilla
+        // EXE, I need different code. So, come to this state and only set 
+        // wipegamestate if menuactive is true.
+        finalecount = 0;
+        finalestage = F_STAGE_ARTSCREEN;
+        if(menuactive)
+            wipegamestate = -1;
+        S_StartMusic(mus_fast);
+        slideshow_state = SLIDE_CHOCO; // remain here.
+        break;
+    default:
+        break;
+    }
+
+    finalecount = 0;
+    patch = (patch_t *)W_CacheLumpName(DEH_String("PANEL0"), PU_CACHE);
+    V_DrawPatch(0, 0, patch);
+}
+
+//
 // F_Ticker
 //
+// [STRIFE] Modifications for new finales
+// haleyjd 09/13/10: Calls F_DoSlideShow
+//
 void F_Ticker (void)
 {
-    size_t		i;
-    
+    size_t          i;
+
     // check for skipping
-    if ( (gamemode == commercial)
-      && ( finalecount > 50) )
+    if (finalecount > 50) // [STRIFE] No commercial check
     {
-      // go on to the next level
-      for (i=0 ; i<MAXPLAYERS ; i++)
-	if (players[i].cmd.buttons)
-	  break;
-				
-      if (i < MAXPLAYERS)
-      {	
-	if (gamemap == 30)
-	  F_StartCast ();
-	else
-	  gameaction = ga_worlddone;
-      }
+        // go on to the next level
+        for (i=0 ; i<MAXPLAYERS ; i++)
+            if (players[i].cmd.buttons)
+                break;
+
+        if (i < MAXPLAYERS)
+            finalecount = slideshow_tics; // [STRIFE]
     }
     
     // advance animation
     finalecount++;
-	
+
     if (finalestage == F_STAGE_CAST)
-    {
-	F_CastTicker ();
-	return;
-    }
-	
+        F_CastTicker ();
+    else if(finalecount > slideshow_tics) // [STRIFE] Advance slideshow
+        F_DoSlideShow();
+
+    // [STRIFE]: Rest is unused
+    /*
     if ( gamemode == commercial)
-	return;
-		
+        return;
+
     if (finalestage == F_STAGE_TEXT
-     && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
+        && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
     {
-	finalecount = 0;
-	finalestage = F_STAGE_ARTSCREEN;
-	wipegamestate = -1;		// force a wipe
-	if (gameepisode == 3)
-	    S_StartMusic (mus_logo);    // villsa [STRIFE] TODO - fix music
+        finalecount = 0;
+        finalestage = F_STAGE_ARTSCREEN;
+        wipegamestate = -1;		// force a wipe
+        if (gameepisode == 3)
+            S_StartMusic (mus_logo);
     }
+    */
 }
 
+// haleyjd 09/13/10: Not present in Strife: Cast drawing functions
 
+#include "hu_stuff.h"
+extern	patch_t *hu_font[HU_FONTSIZE];
 
+/*
 //
 // F_TextWrite
 //
-
-#include "hu_stuff.h"
-extern	patch_t *hu_font[HU_FONTSIZE];
-
-
 void F_TextWrite (void)
 {
     byte*	src;
@@ -328,6 +556,7 @@
     }
 	
 }
+*/
 
 //
 // Final DOOM 2 animation
@@ -336,31 +565,31 @@
 //
 typedef struct
 {
-    char		*name;
-    mobjtype_t	type;
+    int         isindemo; // [STRIFE] Changed from name, which is in mobjinfo
+    mobjtype_t  type;
 } castinfo_t;
 
-// villsa [STRIFE] TODO - proper cast fix
-castinfo_t	castorder[] = {
-    {CC_ZOMBIE, MT_PLAYER},
-    {CC_SHOTGUN, MT_PLAYER},
-    {CC_HEAVY, MT_PLAYER},
-    {CC_IMP, MT_PLAYER},
-    {CC_DEMON, MT_PLAYER},
-    {CC_LOST, MT_PLAYER},
-    {CC_CACO, MT_PLAYER},
-    {CC_HELL, MT_PLAYER},
-    {CC_BARON, MT_PLAYER},
-    {CC_ARACH, MT_PLAYER},
-    {CC_PAIN, MT_PLAYER},
-    {CC_REVEN, MT_PLAYER},
-    {CC_MANCU, MT_PLAYER},
-    {CC_ARCH, MT_PLAYER},
-    {CC_SPIDER, MT_PLAYER},
-    {CC_CYBER, MT_PLAYER},
-    {CC_HERO, MT_PLAYER},
-
-    {NULL,0}
+// haleyjd: [STRIFE] A new cast order was defined, however it is unused in any
+// of the released versions of Strife, even including the demo version :(
+castinfo_t      castorder[] = {
+    { 1, MT_PLAYER     },
+    { 1, MT_BEGGAR1    },
+    { 1, MT_PEASANT2_A },
+    { 1, MT_REBEL1     },
+    { 1, MT_GUARD1     },
+    { 1, MT_CRUSADER   },
+    { 1, MT_RLEADER2   },
+    { 0, MT_SENTINEL   },
+    { 0, MT_STALKER    },
+    { 0, MT_PROGRAMMER },
+    { 0, MT_REAVER     },
+    { 0, MT_PGUARD     },
+    { 0, MT_INQUISITOR },
+    { 0, MT_PRIEST     },
+    { 0, MT_SPECTRE_A  },
+    { 0, MT_BISHOP     },
+    { 0, MT_ENTITY     },
+    { 1, NUMMOBJTYPES  }
 };
 
 int		castnum;
@@ -371,25 +600,33 @@
 int		castonmelee;
 boolean		castattacking;
 
+extern	gamestate_t     wipegamestate;
 
 //
 // F_StartCast
 //
-extern	gamestate_t     wipegamestate;
-
-
+// haleyjd 09/13/10: [STRIFE] Heavily modified, yet unused.
+// Evidence suggests this was meant to be started from a menu item.
+// See m_menu.c for more info.
+//
 void F_StartCast (void)
 {
-    wipegamestate = -1;		// force a screen wipe
+    usergame = false;
+    gameaction = ga_nothing;
+    viewactive = false;
+    automapactive = false;
     castnum = 0;
+    gamestate = GS_FINALE;
     caststate = &states[mobjinfo[castorder[castnum].type].seestate];
     casttics = caststate->tics;
+    if(casttics > 50)
+        casttics = 50;
+    wipegamestate = -1;             // force a screen wipe
     castdeath = false;
     finalestage = F_STAGE_CAST;
     castframes = 0;
     castonmelee = 0;
     castattacking = false;
-    S_ChangeMusic(mus_logo, true);  // villsa [STRIFE] TODO - fix music
 }
 
 
@@ -396,107 +633,99 @@
 //
 // F_CastTicker
 //
+// [STRIFE] Heavily modified, but unused.
+// haleyjd 09/13/10: Yeah, I bothered translating this even though it isn't
+// going to be seen, in part because I hope some Strife port or another will
+// pick it up and finish it, adding it as the optional menu item it was 
+// meant to be, or just adding it as part of the ending sequence.
+//
 void F_CastTicker (void)
 {
-    int		st;
-    int		sfx = sfx_swish;    // villsa [STRIFE] TODO - fix me!
-	
+    int         st;
+
     if (--casttics > 0)
-	return;			// not time to change state yet
-		
+        return;                  // not time to change state yet
+
     if (caststate->tics == -1 || caststate->nextstate == S_NULL)
     {
-	// switch from deathstate to next monster
-	castnum++;
-	castdeath = false;
-	if (castorder[castnum].name == NULL)
-	    castnum = 0;
-	if (mobjinfo[castorder[castnum].type].seesound)
-	    S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);
-	caststate = &states[mobjinfo[castorder[castnum].type].seestate];
-	castframes = 0;
+        // switch from deathstate to next monster
+        castnum++;
+        castdeath = false;
+        if (isdemoversion)
+        {
+            // [STRIFE] Demo version had a shorter cast
+            if(!castorder[castnum].isindemo)
+                castnum = 0;
+        }
+        // [STRIFE] Break on type == NUMMOBJTYPES rather than name == NULL
+        if (castorder[castnum].type == NUMMOBJTYPES)
+            castnum = 0;
+        if (mobjinfo[castorder[castnum].type].seesound)
+            S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);
+        caststate = &states[mobjinfo[castorder[castnum].type].seestate];
+        castframes = 0;
     }
     else
     {
-	// just advance to next state in animation
-	if (caststate == &states[S_PLAY_05])    // villsa [STRIFE] TODO - update later
-	    goto stopattack;	// Oh, gross hack!
-	st = caststate->nextstate;
-	caststate = &states[st];
-	castframes++;
-	
-	// sound hacks....
-	/*switch (st)
-	{
-            // villsa [STRIFE] TODO - fix sounds
-	  case S_PLAY_ATK1:	sfx = sfx_swish; break;
-	  case S_POSS_ATK2:	sfx = sfx_swish; break;
-	  case S_SPOS_ATK2:	sfx = sfx_swish; break;
-	  case S_VILE_ATK2:	sfx = sfx_swish; break;
-	  case S_SKEL_FIST2:	sfx = sfx_swish; break;
-	  case S_SKEL_FIST4:	sfx = sfx_swish; break;
-	  case S_SKEL_MISS2:	sfx = sfx_swish; break;
-	  case S_FATT_ATK8:
-	  case S_FATT_ATK5:
-	  case S_FATT_ATK2:	sfx = sfx_swish; break;
-	  case S_CPOS_ATK2:
-	  case S_CPOS_ATK3:
-	  case S_CPOS_ATK4:	sfx = sfx_swish; break;
-	  case S_TROO_ATK3:	sfx = sfx_swish; break;
-	  case S_SARG_ATK2:	sfx = sfx_swish; break;
-	  case S_BOSS_ATK2:
-	  case S_BOS2_ATK2:
-	  case S_HEAD_ATK2:	sfx = sfx_swish; break;
-	  case S_SKULL_ATK2:	sfx = sfx_swish; break;
-	  case S_SPID_ATK2:
-	  case S_SPID_ATK3:	sfx = sfx_swish; break;
-	  case S_BSPI_ATK2:	sfx = sfx_plasma; break;
-	  case S_CYBER_ATK2:
-	  case S_CYBER_ATK4:
-	  case S_CYBER_ATK6:	sfx = sfx_rlaunc; break;
-	  case S_PAIN_ATK3:	sfx = sfx_swish; break;
-	  default: sfx = 0; break;
-	}*/
-		
-	if (sfx)
-	    S_StartSound (NULL, sfx);
+        int sfx = 0;
+
+        // just advance to next state in animation
+        if (caststate == &states[S_PLAY_05])    // villsa [STRIFE] - updated
+            goto stopattack;	// Oh, gross hack!
+        st = caststate->nextstate;
+        caststate = &states[st];
+        castframes++;
+
+        if (st != mobjinfo[castorder[castnum].type].meleestate &&
+            st != mobjinfo[castorder[castnum].type].missilestate)
+        {
+            if (st == S_PLAY_05)
+                sfx = sfx_rifle;
+            else
+                sfx = 0;
+        }
+        else
+            sfx = mobjinfo[castorder[castnum].type].attacksound;
+
+        if (sfx)
+            S_StartSound (NULL, sfx);
     }
-	
-    if (castframes == 12)
+
+    if (!castdeath && castframes == 12)
     {
-	// go into attack frame
-	castattacking = true;
-	if (castonmelee)
-	    caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
-	else
-	    caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
-	castonmelee ^= 1;
-	if (caststate == &states[S_NULL])
-	{
-	    if (castonmelee)
-		caststate=
-		    &states[mobjinfo[castorder[castnum].type].meleestate];
-	    else
-		caststate=
-		    &states[mobjinfo[castorder[castnum].type].missilestate];
-	}
+        // go into attack frame
+        castattacking = true;
+        if (castonmelee)
+            caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
+        else
+            caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
+        castonmelee ^= 1;
+        if (caststate == &states[S_NULL])
+        {
+            if (castonmelee)
+                caststate = &states[mobjinfo[castorder[castnum].type].meleestate];
+            else
+                caststate = &states[mobjinfo[castorder[castnum].type].missilestate];
+        }
     }
-	
+
     if (castattacking)
     {
-	if (castframes == 24
-	    ||	caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
-	{
-	  stopattack:
-	    castattacking = false;
-	    castframes = 0;
-	    caststate = &states[mobjinfo[castorder[castnum].type].seestate];
-	}
+        if (castframes == 24
+            ||	caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
+        {
+stopattack:
+            castattacking = false;
+            castframes = 0;
+            caststate = &states[mobjinfo[castorder[castnum].type].seestate];
+        }
     }
-	
+
     casttics = caststate->tics;
-    if (casttics == -1)
-	casttics = 15;
+    if (casttics > 50) // [STRIFE] Cap tics
+        casttics = 50;
+    else if (casttics == -1)
+        casttics = 15;
 }
 
 
@@ -503,28 +732,37 @@
 //
 // F_CastResponder
 //
-
+// [STRIFE] This still exists in Strife but is never used.
+// It was used at some point in development, however, as they made
+// numerous modifications to the cast call system.
+//
 boolean F_CastResponder (event_t* ev)
 {
     if (ev->type != ev_keydown)
-	return false;
-		
+        return false;
+
     if (castdeath)
-	return true;			// already in dying frames
-		
+        return true;                    // already in dying frames
+
     // go into death frame
     castdeath = true;
     caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
     casttics = caststate->tics;
+    if(casttics > 50) // [STRIFE] Upper bound on casttics
+        casttics = 50;
     castframes = 0;
     castattacking = false;
     if (mobjinfo[castorder[castnum].type].deathsound)
-	S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);
-	
+        S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);
+
     return true;
 }
 
-
+//
+// F_CastPrint
+//
+// [STRIFE] Verified unmodified, and unused.
+//
 void F_CastPrint (char* text)
 {
     char*	ch;
@@ -575,11 +813,14 @@
 	
 }
 
-
+// haleyjd 09/13/10: [STRIFE] Unfortunately they removed whatever was
+// partway finished of this function from the binary, as there is no
+// trace of it. This means we cannot know for sure what the cast call
+// would have looked like. :(
+/*
 //
 // F_CastDrawer
 //
-
 void F_CastDrawer (void)
 {
     spritedef_t*	sprdef;
@@ -605,11 +846,15 @@
     else
 	V_DrawPatch(160, 170, patch);
 }
+*/
 
-
+#ifdef STRIFE_DEMO_CODE
 //
 // F_DrawPatchCol
 //
+// [STRIFE] Verified unmodified, but not present in 1.2
+// It WAS present in the demo version, however...
+//
 void
 F_DrawPatchCol
 ( int		x,
@@ -640,66 +885,61 @@
 	column = (column_t *)(  (byte *)column + column->length + 4 );
     }
 }
+#endif
 
-
 //
-// F_BunnyScroll
+// F_DrawMap34End
 //
-void F_BunnyScroll (void)
+// [STRIFE] Modified from F_BunnyScroll
+// * In 1.2 and up this just causes a weird black screen.
+// * In the demo version, it was an actual scroll between two screens.
+// I have implemented both code segments, though only the black screen
+// one will currently be used, as full demo version support isn't looking
+// likely right now.
+//
+void F_DrawMap34End (void)
 {
     signed int  scrolled;
-    int		x;
-    patch_t*	p1;
-    patch_t*	p2;
-    char	name[10];
-    int		stage;
-    static int	laststage;
-		
-    p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL);
-    p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL);
+    int         x;
+    patch_t*    p1;
+    patch_t*    p2;
+    char        name[10];
+    int         stage;
+    static int  laststage;
 
+    p1 = W_CacheLumpName (DEH_String("credit"),  PU_LEVEL);
+    p2 = W_CacheLumpName (DEH_String("vellogo"), PU_LEVEL);
+
     V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
-	
-    scrolled = (320 - ((signed int) finalecount-230)/2);
+
+
+    scrolled = (320 - ((signed int) finalecount-430)/2);
     if (scrolled > 320)
-	scrolled = 320;
+        scrolled = 320;
     if (scrolled < 0)
-	scrolled = 0;
-		
+        scrolled = 0;
+
+#ifdef STRIFE_DEMO_CODE
     for ( x=0 ; x<SCREENWIDTH ; x++)
     {
-	if (x+scrolled < 320)
-	    F_DrawPatchCol (x, p1, x+scrolled);
-	else
-	    F_DrawPatchCol (x, p2, x+scrolled - 320);		
+        if (x+scrolled < 320)
+            F_DrawPatchCol (x, p1, x+scrolled);
+        else
+            F_DrawPatchCol (x, p2, x+scrolled - 320);
     }
-	
-    if (finalecount < 1130)
-	return;
-    if (finalecount < 1180)
+#else
+    // wtf this is supposed to do, I have no idea!
+    x = 1;
+    do
     {
-        V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
-                    (SCREENHEIGHT - 8 * 8) / 2, 
-                    W_CacheLumpName(DEH_String("END0"), PU_CACHE));
-	laststage = 0;
-	return;
+        x += 11;
     }
-	
-    stage = (finalecount-1180) / 5;
-    if (stage > 6)
-	stage = 6;
-    if (stage > laststage)
-    {
-	S_StartSound (NULL, sfx_swish); // villsa [STRIFE] TODO - fix sounds
-	laststage = stage;
-    }
-	
-    DEH_snprintf(name, 10, "END%i", stage);
-    V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, 
-                (SCREENHEIGHT - 8 * 8) / 2, 
-                W_CacheLumpName (name,PU_CACHE));
+    while(x < 320);
+#endif
 }
 
+// haleyjd 09/13/10: [STRIFE] Unused.
+/*
 static void F_ArtScreenDrawer(void)
 {
     char *lumpname;
@@ -737,24 +977,42 @@
         V_DrawPatch (0, 0, W_CacheLumpName(lumpname, PU_CACHE));
     }
 }
+*/
 
 //
 // F_Drawer
 //
+// [STRIFE]
+// haleyjd 09/13/10: Modified for slideshow, demo version, etc.
+//
 void F_Drawer (void)
 {
     switch (finalestage)
     {
-        case F_STAGE_CAST:
-            F_CastDrawer();
-            break;
-        case F_STAGE_TEXT:
-            F_TextWrite();
-            break;
-        case F_STAGE_ARTSCREEN:
-            F_ArtScreenDrawer();
-            break;
+    case F_STAGE_CAST:
+        // Cast didn't have a drawer in any released version
+        //F_CastDrawer();
+        break;
+    case F_STAGE_TEXT:
+        // Draw slideshow panel
+        {
+            patch_t *slide = W_CacheLumpName(slideshow_panel, PU_CACHE);
+            V_DrawPatch(0, 0, slide);
+        }
+        break;
+    case F_STAGE_ARTSCREEN:
+        if(gamemap <= 29)
+        {
+            // draw credits
+            patch_t *credits = W_CacheLumpName(DEH_String("CREDIT"), PU_CACHE);
+            V_DrawPatch(0, 0, credits);
+        }
+        else if(gamemap == 34)
+        {
+            // demo version - does nothing meaningful in the final version
+            F_DrawMap34End();
+        }
+        break;
     }
 }
-
 
--- a/src/strife/f_finale.h
+++ b/src/strife/f_finale.h
@@ -41,6 +41,9 @@
 // Called by main loop.
 void F_Ticker (void);
 
+// haleyjd: [STRIFE] Called from G_Ticker as well...
+void F_WaitTicker(void);
+
 // Called by main loop.
 void F_Drawer (void);
 
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -1145,6 +1145,9 @@
         WI_Ticker (); 
         break; 
         */
+    case GS_UNKNOWN: // STRIFE-TODO: What is this? is it ever used??
+        F_WaitTicker();
+        break;
 
     case GS_FINALE: 
         F_Ticker (); 
--- a/src/strife/p_dialog.c
+++ b/src/strife/p_dialog.c
@@ -1228,6 +1228,17 @@
 }
 
 //
+// P_DialogStartP1
+//
+// [STRIFE] New function
+// haleyjd 09/13/10: This is a hack used by the finale system.
+//
+void P_DialogStartP1(void)
+{
+    P_DialogStart(&players[0]);
+}
+
+//
 // P_DialogStart
 //
 // villsa [STRIFE] New function
--- a/src/strife/p_dialog.h
+++ b/src/strife/p_dialog.h
@@ -76,6 +76,7 @@
 void            P_DialogStart(player_t *player);
 void            P_DialogDoChoice(int choice);
 boolean         P_GiveItemToPlayer(player_t *player, int sprnum, mobjtype_t type);
+void            P_DialogStartP1(void);
 mapdialog_t*    P_DialogFind(mobjtype_t type, int jumptoconv);
 
 #endif
--- a/src/strife/p_inter.c
+++ b/src/strife/p_inter.c
@@ -670,9 +670,9 @@
 
         if(gamemap == 29 && !netgame)
         {
-            // villsa [STRIFE] TODO
-            //F_StartSlideShow();
-            //return;
+            // haleyjd 09/13/10: [STRIFE] Give player the bad ending.
+            F_StartFinale();
+            return;
         }
 
         // villsa [STRIFE] spit out inventory items when killed