]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - menu.c
md3 tag attachments (implemented but untested), also the capability to attach any...
[xonotic/darkplaces.git] / menu.c
diff --git a/menu.c b/menu.c
index 10439bd4ce76eefc6e57fb05343c8f99b7c4b10d..3e5a07c4eeed03f4affbb6c6d730034f341a7288 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -25,6 +25,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define TYPE_GAME 2
 #define TYPE_BOTH 3
 
+mempool_t *menu_mempool;
+
 int NehGameType;
 
 enum m_state_e m_state;
@@ -142,8 +144,8 @@ void M_Background(int width, int height)
        menu_height = height;
        menu_x = (vid.conwidth - menu_width) * 0.5;
        menu_y = (vid.conheight - menu_height) * 0.5;
-       DrawQ_Fill(menu_x, menu_y, menu_width, menu_height, 0, 0, 0, 0.5, 0);
-       //DrawQ_Fill(0, 0, vid.conwidth, vid.conheight, 0, 0, 0, 0.5, 0);
+       //DrawQ_Fill(menu_x, menu_y, menu_width, menu_height, 0, 0, 0, 0.5, 0);
+       DrawQ_Fill(0, 0, vid.conwidth, vid.conheight, 0, 0, 0, 0.5, 0);
 }
 
 /*
@@ -568,13 +570,15 @@ void M_SinglePlayer_Draw (void)
        p = Draw_CachePic ("gfx/ttl_sgl.lmp");
 
        // Transfusion doesn't have a single player mode
-       if (gamemode == GAME_TRANSFUSION || gamemode == GAME_NEXUIZ)
+       if (gamemode == GAME_TRANSFUSION || gamemode == GAME_NEXUIZ || gamemode == GAME_GOODVSBAD2)
        {
                M_DrawPic ((320 - p->width) / 2, 4, "gfx/ttl_sgl.lmp");
 
                M_DrawTextBox (60, 8 * 8, 23, 4);
                if (gamemode == GAME_NEXUIZ)
                        M_PrintWhite (95, 10 * 8, "Nexuiz is for");
+               else if (gamemode == GAME_GOODVSBAD2)
+                       M_PrintWhite (95, 10 * 8, "Good Vs Bad 2 is for");
                else
                        M_PrintWhite (95, 10 * 8, "Transfusion is for");
                M_PrintWhite (83, 11 * 8, "multiplayer play only");
@@ -595,7 +599,7 @@ void M_SinglePlayer_Draw (void)
 
 void M_SinglePlayer_Key (int key)
 {
-       if (gamemode == GAME_TRANSFUSION || gamemode == GAME_NEXUIZ)
+       if (gamemode == GAME_TRANSFUSION || gamemode == GAME_NEXUIZ || gamemode == GAME_GOODVSBAD2)
        {
                if (key == K_ESCAPE || key == K_ENTER)
                        m_state = m_main;
@@ -890,17 +894,16 @@ void M_MultiPlayer_Key (int key)
 //=============================================================================
 /* SETUP MENU */
 
-int            setup_cursor = 4;
-int            setup_cursor_table[] = {40, 56, 80, 104, 140};
+int            setup_cursor = 3;
+int            setup_cursor_table[] = {40, 64, 88, 124};
 
-char   setup_hostname[16];
-char   setup_myname[16];
+char   setup_myname[32];
 int            setup_oldtop;
 int            setup_oldbottom;
 int            setup_top;
 int            setup_bottom;
 
-#define        NUM_SETUP_CMDS  5
+#define        NUM_SETUP_CMDS  4
 
 void M_Menu_Setup_f (void)
 {
@@ -908,62 +911,17 @@ void M_Menu_Setup_f (void)
        m_state = m_setup;
        m_entersound = true;
        strcpy(setup_myname, cl_name.string);
-       strcpy(setup_hostname, hostname.string);
        setup_top = setup_oldtop = cl_color.integer >> 4;
        setup_bottom = setup_oldbottom = cl_color.integer & 15;
 }
 
-// LordHavoc: rewrote this code greatly
-void M_MenuPlayerTranslate (qbyte *translation, int top, int bottom)
-{
-       int i;
-       unsigned int trans[4096];
-       qbyte *data, *f;
-       static qbyte pixels[4096];
-       static int menuplyr_width, menuplyr_height, menuplyr_top, menuplyr_bottom, menuplyr_load = true, menuplyr_failed = false;
-
-       if (menuplyr_failed)
-               return;
-       if (menuplyr_top == top && menuplyr_bottom == bottom)
-               return;
-
-       menuplyr_top = top;
-       menuplyr_bottom = bottom;
-
-       if (menuplyr_load)
-       {
-               menuplyr_load = false;
-               f = FS_LoadFile("gfx/menuplyr.lmp", true);
-               if (!f)
-               {
-                       menuplyr_failed = true;
-                       return;
-               }
-               data = LoadLMPAs8Bit (f, 0, 0);
-               Mem_Free(f);
-               if (image_width * image_height > 4096)
-               {
-                       Con_Printf("M_MenuPlayerTranslate: image larger than 4096 pixel buffer\n");
-                       Mem_Free(data);
-                       menuplyr_failed = true;
-                       return;
-               }
-               menuplyr_width = image_width;
-               menuplyr_height = image_height;
-               memcpy(pixels, data, menuplyr_width * menuplyr_height);
-               Mem_Free(data);
-       }
-
-       M_BuildTranslationTable (menuplyr_top*16, menuplyr_bottom*16);
-
-       for (i = 0;i < menuplyr_width * menuplyr_height;i++)
-               trans[i] = palette_complete[translation[pixels[i]]];
-
-       Draw_NewPic("gfx/menuplyr.lmp", menuplyr_width, menuplyr_height, true, (qbyte *)trans);
-}
+static int menuplyr_width, menuplyr_height, menuplyr_top, menuplyr_bottom, menuplyr_load;
+static qbyte *menuplyr_pixels;
+static unsigned int *menuplyr_translated;
 
 void M_Setup_Draw (void)
 {
+       int i;
        cachepic_t      *p;
 
        M_Background(320, 200);
@@ -972,33 +930,58 @@ void M_Setup_Draw (void)
        p = Draw_CachePic ("gfx/p_multi.lmp");
        M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
 
-       M_Print (64, 40, "Hostname");
+       M_Print (64, 40, "Your name");
        M_DrawTextBox (160, 32, 16, 1);
-       M_Print (168, 40, setup_hostname);
-
-       M_Print (64, 56, "Your name");
-       M_DrawTextBox (160, 48, 16, 1);
-       M_Print (168, 56, setup_myname);
+       M_Print (168, 40, setup_myname);
 
-       M_Print (64, 80, "Shirt color");
-       M_Print (64, 104, "Pants color");
-
-       M_DrawTextBox (64, 140-8, 14, 1);
-       M_Print (72, 140, "Accept Changes");
+       if (gamemode != GAME_GOODVSBAD2)
+       {
+               M_Print (64, 64, "Shirt color");
+               M_Print (64, 88, "Pants color");
+       }
 
-       M_DrawPic (160, 64, "gfx/bigbox.lmp");
+       M_DrawTextBox (64, 124-8, 14, 1);
+       M_Print (72, 124, "Accept Changes");
 
        // LordHavoc: rewrote this code greatly
-       M_MenuPlayerTranslate (translationTable, setup_top, setup_bottom);
-       M_DrawPic (172, 72, "gfx/menuplyr.lmp");
+       if (menuplyr_load)
+       {
+               qbyte *data, *f;
+               menuplyr_load = false;
+               menuplyr_top = -1;
+               menuplyr_bottom = -1;
+               if ((f = FS_LoadFile("gfx/menuplyr.lmp", true)))
+               {
+                       data = LoadLMPAs8Bit (f, 0, 0);
+                       menuplyr_width = image_width;
+                       menuplyr_height = image_height;
+                       Mem_Free(f);
+                       menuplyr_pixels = Mem_Alloc(menu_mempool, menuplyr_width * menuplyr_height);
+                       menuplyr_translated = Mem_Alloc(menu_mempool, menuplyr_width * menuplyr_height * 4);
+                       memcpy(menuplyr_pixels, data, menuplyr_width * menuplyr_height);
+                       Mem_Free(data);
+               }
+       }
 
-       M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
+       if (menuplyr_pixels)
+       {
+               if (menuplyr_top != setup_top || menuplyr_bottom != setup_bottom)
+               {
+                       menuplyr_top = setup_top;
+                       menuplyr_bottom = setup_bottom;
+                       M_BuildTranslationTable(menuplyr_top*16, menuplyr_bottom*16);
+                       for (i = 0;i < menuplyr_width * menuplyr_height;i++)
+                               menuplyr_translated[i] = palette_complete[translationTable[menuplyr_pixels[i]]];
+                       Draw_NewPic("gfx/menuplyr.lmp", menuplyr_width, menuplyr_height, true, (qbyte *)menuplyr_translated);
+               }
+               M_DrawPic(160, 48, "gfx/bigbox.lmp");
+               M_DrawPic(172, 56, "gfx/menuplyr.lmp");
+       }
 
        if (setup_cursor == 0)
-               M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
-
-       if (setup_cursor == 1)
                M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
+       else
+               M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
 }
 
 
@@ -1027,37 +1010,35 @@ void M_Setup_Key (int k)
                break;
 
        case K_LEFTARROW:
-               if (setup_cursor < 2)
+               if (setup_cursor < 1)
                        return;
                S_LocalSound ("misc/menu3.wav");
-               if (setup_cursor == 2)
+               if (setup_cursor == 1)
                        setup_top = setup_top - 1;
-               if (setup_cursor == 3)
+               if (setup_cursor == 2)
                        setup_bottom = setup_bottom - 1;
                break;
        case K_RIGHTARROW:
-               if (setup_cursor < 2)
+               if (setup_cursor < 1)
                        return;
 forward:
                S_LocalSound ("misc/menu3.wav");
-               if (setup_cursor == 2)
+               if (setup_cursor == 1)
                        setup_top = setup_top + 1;
-               if (setup_cursor == 3)
+               if (setup_cursor == 2)
                        setup_bottom = setup_bottom + 1;
                break;
 
        case K_ENTER:
-               if (setup_cursor == 0 || setup_cursor == 1)
+               if (setup_cursor == 0)
                        return;
 
-               if (setup_cursor == 2 || setup_cursor == 3)
+               if (setup_cursor == 1 || setup_cursor == 2)
                        goto forward;
 
-               // setup_cursor == 4 (OK)
+               // setup_cursor == 3 (Accept changes)
                if (strcmp(cl_name.string, setup_myname) != 0)
                        Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) );
-               if (strcmp(hostname.string, setup_hostname) != 0)
-                       Cvar_Set("hostname", setup_hostname);
                if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom)
                        Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) );
                m_entersound = true;
@@ -1066,12 +1047,6 @@ forward:
 
        case K_BACKSPACE:
                if (setup_cursor == 0)
-               {
-                       if (strlen(setup_hostname))
-                               setup_hostname[strlen(setup_hostname)-1] = 0;
-               }
-
-               if (setup_cursor == 1)
                {
                        if (strlen(setup_myname))
                                setup_myname[strlen(setup_myname)-1] = 0;
@@ -1082,15 +1057,6 @@ forward:
                if (k < 32 || k > 127)
                        break;
                if (setup_cursor == 0)
-               {
-                       l = strlen(setup_hostname);
-                       if (l < 15)
-                       {
-                               setup_hostname[l+1] = 0;
-                               setup_hostname[l] = k;
-                       }
-               }
-               if (setup_cursor == 1)
                {
                        l = strlen(setup_myname);
                        if (l < 15)
@@ -1140,7 +1106,7 @@ void M_DrawCheckbox (int x, int y, int on)
 }
 
 
-#define OPTIONS_ITEMS 28
+#define OPTIONS_ITEMS 31
 
 int options_cursor;
 
@@ -1151,67 +1117,61 @@ void M_Menu_Options_f (void)
        m_entersound = true;
 }
 
+extern cvar_t snd_staticvolume;
 extern cvar_t gl_delayfinish;
 extern cvar_t slowmo;
 extern dllhandle_t jpeg_dll;
 
 void M_Menu_Options_AdjustSliders (int dir)
 {
+       int optnum;
        S_LocalSound ("misc/menu3.wav");
 
-       switch (options_cursor)
-       {
-       case 6:
+       optnum = 6;
+       if (options_cursor == optnum++)
                Cvar_SetValueQuick (&scr_2dresolution, bound(0, scr_2dresolution.value + dir * 0.2, 1));
-               break;
-       case 7:
+       else if (options_cursor == optnum++)
+               Cvar_SetValueQuick (&scr_conspeed, bound(0, scr_conspeed.value + dir * 100, 1000));
+       else if (options_cursor == optnum++)
+               Cvar_SetValueQuick (&scr_conalpha, bound(0, scr_conalpha.value + dir * 0.2, 1));
+       else if (options_cursor == optnum++)
+               Cvar_SetValueQuick (&scr_conbrightness, bound(0, scr_conbrightness.value + dir * 0.2, 1));
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&scr_viewsize, bound(30, scr_viewsize.value + dir * 10, 120));
-               break;
-       case 8:
-               if (jpeg_dll != NULL)
-                       Cvar_SetValueQuick (&scr_screenshot_jpeg, !scr_screenshot_jpeg.integer);
-               break;
-       case 9:
+       else if (options_cursor == optnum++)
+               Cvar_SetValueQuick (&scr_screenshot_jpeg, !scr_screenshot_jpeg.integer);
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&r_sky, !r_sky.integer);
-               break;
-       case 10:
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&v_overbrightbits, bound(0, v_overbrightbits.integer + dir, 4));
-               break;
-       case 11:
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&gl_combine, !gl_combine.integer);
-               break;
-       case 12:
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&gl_dither, !gl_dither.integer);
-               break;
-       case 13:
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&gl_delayfinish, !gl_delayfinish.integer);
-               break;
-       case 14:
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&slowmo, bound(0, slowmo.value + dir * 0.25, 5));
-               break;
-       case 15: // music volume
-               #ifdef _WIN32
+       else if (options_cursor == optnum++)
+#ifdef _WIN32
                Cvar_SetValueQuick (&bgmvolume, bound(0, bgmvolume.value + dir * 1.0, 1));
-               #else
+#else
                Cvar_SetValueQuick (&bgmvolume, bound(0, bgmvolume.value + dir * 0.1, 1));
-               #endif
-               break;
-       case 16: // sfx volume
+#endif
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&volume, bound(0, volume.value + dir * 0.1, 1));
-               break;
-       case 17:
+       else if (options_cursor == optnum++)
+               Cvar_SetValueQuick (&snd_staticvolume, bound(0, snd_staticvolume.value + dir * 0.1, 1));
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&crosshair, bound(0, crosshair.integer + dir, 5));
-               break;
-       case 18:
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&crosshair_size, bound(1, crosshair_size.value + dir, 5));
-               break;
-       case 19: // static crosshair
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&crosshair_static, !crosshair_static.integer);
-               break;
-       case 20: // show framerate
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&showfps, !showfps.integer);
-               break;
-       case 21: // always run
+       else if (options_cursor == optnum++)
+       {
                if (cl_forwardspeed.value > 200)
                {
                        Cvar_SetValueQuick (&cl_forwardspeed, 200);
@@ -1222,26 +1182,19 @@ void M_Menu_Options_AdjustSliders (int dir)
                        Cvar_SetValueQuick (&cl_forwardspeed, 400);
                        Cvar_SetValueQuick (&cl_backspeed, 400);
                }
-               break;
-       case 22: // lookspring
+       }
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&lookspring, !lookspring.integer);
-               break;
-       case 23: // lookstrafe
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&lookstrafe, !lookstrafe.integer);
-               break;
-       case 24: // mouse speed
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&sensitivity, bound(1, sensitivity.value + dir * 0.5, 50));
-               break;
-       case 25: // mouse look
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&freelook, !freelook.integer);
-               break;
-       case 26: // invert mouse
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&m_pitch, -m_pitch.value);
-               break;
-       case 27: // windowed mouse
+       else if (options_cursor == optnum++)
                Cvar_SetValueQuick (&vid_mouse, !vid_mouse.integer);
-               break;
-       }
 }
 
 void M_Options_Draw (void)
@@ -1263,9 +1216,14 @@ void M_Options_Draw (void)
        M_Print(16, y, "       Effects Options");y += 8;
        M_Print(16, y, " Color Control Options");y += 8;
        M_Print(16, y, "         2D Resolution");M_DrawSlider(220, y, scr_2dresolution.value, 0, 1);y += 8;
+       M_Print(16, y, "         Console Speed");M_DrawSlider(220, y, scr_conspeed.value, 0, 1000);y += 8;
+       M_Print(16, y, "         Console Alpha");M_DrawSlider(220, y, scr_conalpha.value, 0, 1);y += 8;
+       M_Print(16, y, "    Conback Brightness");M_DrawSlider(220, y, scr_conbrightness.value, 0, 1);y += 8;
        M_Print(16, y, "           Screen size");M_DrawSlider(220, y, scr_viewsize.value, 30, 120);y += 8;
        M_ItemPrint(16, y, "      JPEG screenshots", jpeg_dll != NULL);M_DrawCheckbox(220, y, scr_screenshot_jpeg.integer);y += 8;
        M_Print(16, y, "                   Sky");M_DrawCheckbox(220, y, r_sky.integer);y += 8;
+       // LordHavoc: FIXME: overbright needs to be disabled in GAME_GOODVSBAD2 but combine should not be disabled
+       // LordHavoc: perhaps it's time for Overbright Bits to die, and a r_lightmapintensity option to be added?
        M_Print(16, y, "       Overbright Bits");M_DrawSlider(220, y, v_overbrightbits.value, 0, 4);y += 8;
        M_Print(16, y, "       Texture Combine");M_DrawCheckbox(220, y, gl_combine.integer);y += 8;
        M_Print(16, y, "             Dithering");M_DrawCheckbox(220, y, gl_dither.integer);y += 8;
@@ -1273,6 +1231,12 @@ void M_Options_Draw (void)
        M_ItemPrint(16, y, "        Game Speed", sv.active);M_DrawSlider(220, y, slowmo.value, 0, 5);y += 8;
        M_ItemPrint(16, y, "       CD Music Volume", cdaudioinitialized);M_DrawSlider(220, y, bgmvolume.value, 0, 1);y += 8;
        M_ItemPrint(16, y, "          Sound Volume", snd_initialized);M_DrawSlider(220, y, volume.value, 0, 1);y += 8;
+       if (gamemode == GAME_GOODVSBAD2)
+               M_ItemPrint(16, y, "          Music Volume", snd_initialized);
+       else
+               M_ItemPrint(16, y, "  Ambient Sound Volume", snd_initialized);
+       M_DrawSlider(220, y, snd_staticvolume.value, 0, 1);
+       y += 8;
        M_Print(16, y, "             Crosshair");M_DrawSlider(220, y, crosshair.value, 0, 5);y += 8;
        M_Print(16, y, "        Crosshair Size");M_DrawSlider(220, y, crosshair_size.value, 1, 5);y += 8;
        M_Print(16, y, "      Static Crosshair");M_DrawCheckbox(220, y, crosshair_static.integer);y += 8;
@@ -1352,7 +1316,7 @@ void M_Options_Key (int k)
        }
 }
 
-#define        OPTIONS_EFFECTS_ITEMS   16
+#define        OPTIONS_EFFECTS_ITEMS   20
 
 int options_effects_cursor;
 
@@ -1368,6 +1332,7 @@ extern cvar_t r_detailtextures;
 extern cvar_t cl_particles;
 extern cvar_t cl_explosions;
 extern cvar_t cl_stainmaps;
+extern cvar_t cl_decals;
 extern cvar_t r_explosionclip;
 extern cvar_t r_dlightmap;
 extern cvar_t r_modellights;
@@ -1383,59 +1348,50 @@ extern cvar_t cl_particles_blood_alpha;
 
 void M_Menu_Options_Effects_AdjustSliders (int dir)
 {
+       int optnum;
        S_LocalSound ("misc/menu3.wav");
 
-       switch (options_effects_cursor)
-       {
-       case 0:
+       optnum = 0;
+       if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&r_modellights, bound(0, r_modellights.value + dir, 8));
-               break;
-       case 1:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&r_dlightmap, !r_dlightmap.integer);
-               break;
-       case 2:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&r_coronas, !r_coronas.integer);
-               break;
-       case 3:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&gl_flashblend, !gl_flashblend.integer);
-               break;
-       case 4:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles, !cl_particles.integer);
-               break;
-       case 5:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_explosions, !cl_explosions.integer);
-               break;
-       case 6:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&r_explosionclip, !r_explosionclip.integer);
-               break;
-       case 7:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_stainmaps, !cl_stainmaps.integer);
-               break;
-       case 8:
+       else if (options_effects_cursor == optnum++)
+               Cvar_SetValueQuick (&cl_decals, !cl_decals.integer);
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&r_detailtextures, !r_detailtextures.integer);
-               break;
-       case 9:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles_bulletimpacts, !cl_particles_bulletimpacts.integer);
-               break;
-       case 10:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles_smoke, !cl_particles_smoke.integer);
-               break;
-       case 11:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles_sparks, !cl_particles_sparks.integer);
-               break;
-       case 12:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles_bubbles, !cl_particles_bubbles.integer);
-               break;
-       case 13:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles_blood, !cl_particles_blood.integer);
-               break;
-       case 14:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles_blood_size, bound(2, cl_particles_blood_size.value + dir * 1, 20));
-               break;
-       case 15:
+       else if (options_effects_cursor == optnum++)
                Cvar_SetValueQuick (&cl_particles_blood_alpha, bound(0.2, cl_particles_blood_alpha.value + dir * 0.1, 1));
-               break;
-       }
+       else if (options_effects_cursor == optnum++)
+               Cvar_SetValueQuick (&r_lerpmodels, !r_lerpmodels.integer);
+       else if (options_effects_cursor == optnum++)
+               Cvar_SetValueQuick (&r_lerpsprites, !r_lerpsprites.integer);
+       else if (options_effects_cursor == optnum++)
+               Cvar_SetValueQuick (&r_waterscroll, bound(0, r_waterscroll.value + dir * 0.5, 10));
 }
 
 void M_Options_Effects_Draw (void)
@@ -1458,6 +1414,7 @@ void M_Options_Effects_Draw (void)
        M_Print(16, y, "            Explosions");M_DrawCheckbox(220, y, cl_explosions.integer);y += 8;
        M_Print(16, y, "    Explosion Clipping");M_DrawCheckbox(220, y, r_explosionclip.integer);y += 8;
        M_Print(16, y, "             Stainmaps");M_DrawCheckbox(220, y, cl_stainmaps.integer);y += 8;
+       M_Print(16, y, "                Decals");M_DrawCheckbox(220, y, cl_decals.integer);y += 8;
        M_Print(16, y, "      Detail Texturing");M_DrawCheckbox(220, y, r_detailtextures.integer);y += 8;
        M_Print(16, y, "        Bullet Impacts");M_DrawCheckbox(220, y, cl_particles_bulletimpacts.integer);y += 8;
        M_Print(16, y, "                 Smoke");M_DrawCheckbox(220, y, cl_particles_smoke.integer);y += 8;
@@ -1466,6 +1423,9 @@ void M_Options_Effects_Draw (void)
        M_Print(16, y, "                 Blood");M_DrawCheckbox(220, y, cl_particles_blood.integer);y += 8;
        M_Print(16, y, "            Blood Size");M_DrawSlider(220, y, cl_particles_blood_size.value, 2, 20);y += 8;
        M_Print(16, y, "         Blood Opacity");M_DrawSlider(220, y, cl_particles_blood_alpha.value, 0.2, 1);y += 8;
+       M_Print(16, y, "   Model Interpolation");M_DrawCheckbox(220, y, r_lerpmodels.integer);y += 8;
+       M_Print(16, y, "  Sprite Interpolation");M_DrawCheckbox(220, y, r_lerpsprites.integer);y += 8;
+       M_Print(16, y, "        Water Movement");M_DrawSlider(220, y, r_waterscroll.value, 0, 10);y += 8;
 
        // cursor
        M_DrawCharacter(200, 32 + options_effects_cursor*8, 12+((int)(realtime*4)&1));
@@ -1529,86 +1489,100 @@ void M_Menu_Options_ColorControl_f (void)
 
 void M_Menu_Options_ColorControl_AdjustSliders (int dir)
 {
+       int optnum;
        float f;
        S_LocalSound ("misc/menu3.wav");
 
-       switch (options_colorcontrol_cursor)
-       {
-       case 1:
+       optnum = 1;
+       if (options_colorcontrol_cursor == optnum++)
                Cvar_SetValueQuick (&v_hwgamma, !v_hwgamma.integer);
-               break;
-       case 2:
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 0);
                Cvar_SetValueQuick (&v_gamma, bound(1, v_gamma.value + dir * 0.125, 5));
-               break;
-       case 3:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 0);
                Cvar_SetValueQuick (&v_contrast, bound(1, v_contrast.value + dir * 0.125, 5));
-               break;
-       case 4:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 0);
                Cvar_SetValueQuick (&v_brightness, bound(0, v_brightness.value + dir * 0.05, 0.8));
-               break;
-       case 5:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, !v_color_enable.integer);
-               break;
-       case 6:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_black_r, bound(0, v_color_black_r.value + dir * 0.0125, 0.8));
-               break;
-       case 7:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_black_g, bound(0, v_color_black_g.value + dir * 0.0125, 0.8));
-               break;
-       case 8:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_black_b, bound(0, v_color_black_b.value + dir * 0.0125, 0.8));
-               break;
-       case 9:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                f = bound(0, (v_color_black_r.value + v_color_black_g.value + v_color_black_b.value) / 3 + dir * 0.0125, 0.8);
                Cvar_SetValueQuick (&v_color_black_r, f);
                Cvar_SetValueQuick (&v_color_black_g, f);
                Cvar_SetValueQuick (&v_color_black_b, f);
-               break;
-       case 10:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_grey_r, bound(0, v_color_grey_r.value + dir * 0.0125, 0.95));
-               break;
-       case 11:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_grey_g, bound(0, v_color_grey_g.value + dir * 0.0125, 0.95));
-               break;
-       case 12:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_grey_b, bound(0, v_color_grey_b.value + dir * 0.0125, 0.95));
-               break;
-       case 13:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                f = bound(0, (v_color_grey_r.value + v_color_grey_g.value + v_color_grey_b.value) / 3 + dir * 0.0125, 0.95);
                Cvar_SetValueQuick (&v_color_grey_r, f);
                Cvar_SetValueQuick (&v_color_grey_g, f);
                Cvar_SetValueQuick (&v_color_grey_b, f);
-               break;
-       case 14:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_white_r, bound(1, v_color_white_r.value + dir * 0.125, 5));
-               break;
-       case 15:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_white_g, bound(1, v_color_white_g.value + dir * 0.125, 5));
-               break;
-       case 16:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                Cvar_SetValueQuick (&v_color_white_b, bound(1, v_color_white_b.value + dir * 0.125, 5));
-               break;
-       case 17:
+       }
+       else if (options_colorcontrol_cursor == optnum++)
+       {
                Cvar_SetValueQuick (&v_color_enable, 1);
                f = bound(1, (v_color_white_r.value + v_color_white_g.value + v_color_white_b.value) / 3 + dir * 0.125, 5);
                Cvar_SetValueQuick (&v_color_white_r, f);
                Cvar_SetValueQuick (&v_color_white_g, f);
                Cvar_SetValueQuick (&v_color_white_b, f);
-               break;
        }
 }
 
@@ -1806,6 +1780,29 @@ char *transfusionbindnames[][2] =
 {"impulse 20",         "observer mode"}
 };
 
+char *goodvsbad2bindnames[][2] =
+{
+{"impulse 69",         "Power 1"},
+{"impulse 70",         "Power 2"},
+{"impulse 71",         "Power 3"},
+{"+jump",                      "jump / swim up"},
+{"+forward",           "walk forward"},
+{"+back",                      "backpedal"},
+{"+left",                      "turn left"},
+{"+right",                     "turn right"},
+{"+speed",                     "run"},
+{"+moveleft",          "step left"},
+{"+moveright",                 "step right"},
+{"+strafe",            "sidestep"},
+{"+lookup",            "look up"},
+{"+lookdown",          "look down"},
+{"centerview",                 "center view"},
+{"+mlook",                     "mouse look"},
+{"kill",                       "kill yourself"},
+{"+moveup",                    "swim up"},
+{"+movedown",          "swim down"}
+};
+
 int numcommands;
 char *(*bindnames)[2];
 
@@ -2287,79 +2284,69 @@ void M_Help_Key (int key)
 //=============================================================================
 /* QUIT MENU */
 
-int            msgNumber;
+char *m_quit_message[9];
 int            m_quit_prevstate;
 qboolean       wasInMenus;
 
-char *quitMessage [] =
+
+int M_QuitMessage(char *line1, char *line2, char *line3, char *line4, char *line5, char *line6, char *line7, char *line8)
 {
-/* .........1.........2.... */
-/*
-  "  Are you gonna quit    ",
-  "  this game just like   ",
-  "   everything else?     ",
-  "                        ",
-
-  " Milord, methinks that  ",
-  "   thou art a lowly     ",
-  " quitter. Is this true? ",
-  "                        ",
-
-  " Do I need to bust your ",
-  "  face open for trying  ",
-  "        to quit?        ",
-  "                        ",
-
-  " Man, I oughta smack you",
-  "   for trying to quit!  ",
-  "     Press Y to get     ",
-  "      smacked out.      ",
-
-  " Press Y to quit like a ",
-  "   big loser in life.   ",
-  "  Press N to stay proud ",
-  "    and successful!     ",
-
-  "   If you press Y to    ",
-  "  quit, I will summon   ",
-  "  Satan all over your   ",
-  "      hard drive!       ",
-
-  "  Um, Asmodeus dislikes ",
-  " his children trying to ",
-  " quit. Press Y to return",
-  "   to your Tinkertoys.  ",
-
-  "  If you quit now, I'll ",
-  "  throw a blanket-party ",
-  "   for you next time!   ",
-  "                        "
-  */
-
-/* .........1.........2.... */
-  "                        ",
-  "    Tired of fragging   ",
-  "        already?        ",
-  "                        ",
-
-  "                        ",
-  "  Quit now and forfeit  ",
-  "     your bodycount?    ",
-  "                        ",
-
-  "                        ",
-  "    Are you sure you    ",
-  "      want to quit?     ",
-  "                        ",
-
-  "                        ",
-  "   Off to do something  ",
-  "      constructive?     ",
-  "                        ",
+       m_quit_message[0] = line1;
+       m_quit_message[1] = line2;
+       m_quit_message[2] = line3;
+       m_quit_message[3] = line4;
+       m_quit_message[4] = line5;
+       m_quit_message[5] = line6;
+       m_quit_message[6] = line7;
+       m_quit_message[7] = line8;
+       m_quit_message[8] = NULL;
+       return 1;
+}
+
+int M_ChooseQuitMessage(int request)
+{
+       switch (gamemode)
+       {
+       case GAME_NORMAL:
+       case GAME_HIPNOTIC:
+       case GAME_ROGUE:
+       case GAME_NEHAHRA:
+               if (request-- == 0) return M_QuitMessage("Are you gonna quit","this game just like","everything else?",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Milord, methinks that","thou art a lowly","quitter. Is this true?",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Do I need to bust your","face open for trying","to quit?",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Man, I oughta smack you","for trying to quit!","Press Y to get","smacked out.",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Press Y to quit like a","big loser in life.","Press N to stay proud","and successful!",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("If you press Y to","quit, I will summon","Satan all over your","hard drive!",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Um, Asmodeus dislikes","his children trying to","quit. Press Y to return","to your Tinkertoys.",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("If you quit now, I'll","throw a blanket-party","for you next time!",NULL,NULL,NULL,NULL,NULL);
+               break;
+       case GAME_GOODVSBAD2:
+               if (request-- == 0) return M_QuitMessage("Press Yes To Quit","...","Yes",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Do you really want to","Quit?","Play Good vs bad 3!",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("All your quit are","belong to long duck","dong",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Press Y to quit","","But are you too legit?",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("This game was made by","e@chip-web.com","It is by far the best","game ever made.",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Even I really dont","know of a game better","Press Y to quit","like rougue chedder",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("After you stop playing","tell the guys who made","counterstrike to just","kill themselves now",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Press Y to exit to DOS","","SSH login as user Y","to exit to Linux",NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Press Y like you","were waanderers","from Ys'",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("This game was made in","Nippon like the SS","announcer's saying ipon",NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("you","want to quit?",NULL,NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Please stop playing","this stupid game",NULL,NULL,NULL,NULL,NULL,NULL);
+               break;
+       default:
+               if (request-- == 0) return M_QuitMessage("Tired of fragging already?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Quit now and forfeit your bodycount?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Are you sure you want to quit?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+               if (request-- == 0) return M_QuitMessage("Off to do something constructive?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+               break;
+       }
+       return 0;
 };
 
 void M_Menu_Quit_f (void)
 {
+       int n;
        if (m_state == m_quit)
                return;
        wasInMenus = (key_dest == key_menu);
@@ -2367,7 +2354,10 @@ void M_Menu_Quit_f (void)
        m_quit_prevstate = m_state;
        m_state = m_quit;
        m_entersound = true;
-       msgNumber = rand()&3; //&7;
+       // count how many there are
+       for (n = 0;M_ChooseQuitMessage(n);n++);
+       // choose one
+       M_ChooseQuitMessage(rand() % n);
 }
 
 
@@ -2398,18 +2388,28 @@ void M_Quit_Key (int key)
        default:
                break;
        }
-
 }
 
-
 void M_Quit_Draw (void)
 {
-       M_Background(208, 48);
-       M_DrawTextBox(0, 0, 24, 4);
-       M_Print(8,  8, quitMessage[msgNumber*4+0]);
-       M_Print(8, 16, quitMessage[msgNumber*4+1]);
-       M_Print(8, 24, quitMessage[msgNumber*4+2]);
-       M_Print(8, 32, quitMessage[msgNumber*4+3]);
+       int i, l, linelength, firstline, lastline, lines;
+       for (i = 0, linelength = 0, firstline = 9999, lastline = -1;m_quit_message[i];i++)
+       {
+               if ((l = strlen(m_quit_message[i])))
+               {
+                       if (firstline > i)
+                               firstline = i;
+                       if (lastline < i)
+                               lastline = i;
+                       if (linelength < l)
+                               linelength = l;
+               }
+       }
+       lines = (lastline - firstline) + 1;
+       M_Background(linelength * 8 + 16, lines * 8 + 16);
+       M_DrawTextBox(0, 0, linelength, lines);
+       for (i = 0, l = firstline;i < lines;i++, l++)
+               M_Print(8 + 4 * (linelength - strlen(m_quit_message[l])), 8 + 8 * i, m_quit_message[l]);
 }
 
 //=============================================================================
@@ -2682,7 +2682,7 @@ episode_t quakeepisodes[] =
        {"Deathmatch Arena", 32, 6}
 };
 
-//MED 01/06/97 added hipnotic levels
+ //MED 01/06/97 added hipnotic levels
 level_t     hipnoticlevels[] =
 {
    {"start", "Command HQ"},  // 0
@@ -2845,12 +2845,31 @@ episode_t       transfusionepisodes[] =
        {"Conversions", 33, 5}
 };
 
+level_t goodvsbad2levels[] =
+{
+       {"rts", "Many Paths"},  // 0
+       {"chess", "Chess, Scott Hess"},                         // 1
+       {"dot", "Big Wall"},
+       {"city2", "The Big City"},
+       {"bwall", "0 G like Psychic TV"},
+       {"snow", "Wireframed"},
+       {"telep", "Infinite Falling"},
+       {"faces", "Facing Bases"},
+       {"island", "Adventure Islands"},
+};
+
+episode_t goodvsbad2episodes[] =
+{
+       {"Levels? Bevels!", 0, 8},
+};
+
 gamelevels_t sharewarequakegame = {"Shareware Quake", quakelevels, quakeepisodes, 2};
 gamelevels_t registeredquakegame = {"Quake", quakelevels, quakeepisodes, 7};
 gamelevels_t hipnoticgame = {"Scourge of Armagon", hipnoticlevels, hipnoticepisodes, 6};
 gamelevels_t roguegame = {"Dissolution of Eternity", roguelevels, rogueepisodes, 4};
 gamelevels_t nehahragame = {"Nehahra", nehahralevels, nehahraepisodes, 4};
 gamelevels_t transfusiongame = {"Transfusion", transfusionlevels, transfusionepisodes, 7};
+gamelevels_t goodvsbad2game = {"Good Vs. Bad 2", goodvsbad2levels, goodvsbad2episodes, 1};
 
 typedef struct
 {
@@ -2867,6 +2886,7 @@ gameinfo_t gamelist[] =
        {GAME_ROGUE, &roguegame, &roguegame},
        {GAME_NEHAHRA, &nehahragame, &nehahragame},
        {GAME_TRANSFUSION, &transfusiongame, &transfusiongame},
+       {GAME_GOODVSBAD2, &goodvsbad2game, &goodvsbad2game},
        {-1, &sharewarequakegame, &registeredquakegame} // final fallback
 };
 
@@ -2900,8 +2920,8 @@ void M_Menu_GameOptions_f (void)
 }
 
 
-int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 104, 120, 128};
-#define        NUM_GAMEOPTIONS 10
+int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 104, 132, 152, 160};
+#define        NUM_GAMEOPTIONS 11
 int            gameoptions_cursor;
 
 void M_GameOptions_Draw (void)
@@ -2922,104 +2942,105 @@ void M_GameOptions_Draw (void)
        M_Print (0, 56, "      Max players");
        M_Print (160, 56, va("%i", maxplayers) );
 
-       M_Print (0, 64, "        Game Type");
-       if (gamemode == GAME_TRANSFUSION)
+       if (gamemode != GAME_GOODVSBAD2)
        {
-               if (!deathmatch.integer)
-                       Cvar_SetValue("deathmatch", 1);
-               if (deathmatch.integer == 2)
-                       M_Print (160, 64, "Capture the Flag");
-               else
-                       M_Print (160, 64, "Blood Bath");
-       }
-       else
-       {
-               if (!coop.integer && !deathmatch.integer)
-                       Cvar_SetValue("deathmatch", 1);
-               if (coop.integer)
-                       M_Print (160, 64, "Cooperative");
+               M_Print (0, 64, "        Game Type");
+               if (gamemode == GAME_TRANSFUSION)
+               {
+                       if (!deathmatch.integer)
+                               Cvar_SetValue("deathmatch", 1);
+                       if (deathmatch.integer == 2)
+                               M_Print (160, 64, "Capture the Flag");
+                       else
+                               M_Print (160, 64, "Blood Bath");
+               }
                else
-                       M_Print (160, 64, "Deathmatch");
-       }
-
-       M_Print (0, 72, "        Teamplay");
-       if (gamemode == GAME_ROGUE)
-       {
-               char *msg;
-
-               switch((int)teamplay.integer)
                {
-                       case 1: msg = "No Friendly Fire"; break;
-                       case 2: msg = "Friendly Fire"; break;
-                       case 3: msg = "Tag"; break;
-                       case 4: msg = "Capture the Flag"; break;
-                       case 5: msg = "One Flag CTF"; break;
-                       case 6: msg = "Three Team CTF"; break;
-                       default: msg = "Off"; break;
+                       if (!coop.integer && !deathmatch.integer)
+                               Cvar_SetValue("deathmatch", 1);
+                       if (coop.integer)
+                               M_Print (160, 64, "Cooperative");
+                       else
+                               M_Print (160, 64, "Deathmatch");
                }
-               M_Print (160, 72, msg);
-       }
-       else if (gamemode == GAME_TRANSFUSION)
-       {
-               char *msg;
 
-               switch (teamplay.integer)
+               M_Print (0, 72, "        Teamplay");
+               if (gamemode == GAME_ROGUE)
                {
-                       case 0: msg = "Off"; break;
-                       case 2: msg = "Friendly Fire"; break;
-                       default: msg = "No Friendly Fire"; break;
-               }
-               M_Print (160, 72, msg);
-       }
-       else
-       {
-               char *msg;
+                       char *msg;
 
-               switch((int)teamplay.integer)
+                       switch((int)teamplay.integer)
+                       {
+                               case 1: msg = "No Friendly Fire"; break;
+                               case 2: msg = "Friendly Fire"; break;
+                               case 3: msg = "Tag"; break;
+                               case 4: msg = "Capture the Flag"; break;
+                               case 5: msg = "One Flag CTF"; break;
+                               case 6: msg = "Three Team CTF"; break;
+                               default: msg = "Off"; break;
+                       }
+                       M_Print (160, 72, msg);
+               }
+               else
                {
-                       case 1: msg = "No Friendly Fire"; break;
-                       case 2: msg = "Friendly Fire"; break;
-                       default: msg = "Off"; break;
+                       char *msg;
+
+                       switch (teamplay.integer)
+                       {
+                               case 0: msg = "Off"; break;
+                               case 2: msg = "Friendly Fire"; break;
+                               default: msg = "No Friendly Fire"; break;
+                       }
+                       M_Print (160, 72, msg);
                }
-               M_Print (160, 72, msg);
-       }
 
-       M_Print (0, 80, "            Skill");
-       if (skill.integer == 0)
-               M_Print (160, 80, "Easy difficulty");
-       else if (skill.integer == 1)
-               M_Print (160, 80, "Normal difficulty");
-       else if (skill.integer == 2)
-               M_Print (160, 80, "Hard difficulty");
-       else
-               M_Print (160, 80, "Nightmare difficulty");
+               M_Print (0, 80, "            Skill");
+               if (skill.integer == 0)
+                       M_Print (160, 80, "Easy difficulty");
+               else if (skill.integer == 1)
+                       M_Print (160, 80, "Normal difficulty");
+               else if (skill.integer == 2)
+                       M_Print (160, 80, "Hard difficulty");
+               else
+                       M_Print (160, 80, "Nightmare difficulty");
 
-       M_Print (0, 88, "       Frag Limit");
-       if (fraglimit.integer == 0)
-               M_Print (160, 88, "none");
-       else
-               M_Print (160, 88, va("%i frags", fraglimit.integer));
+               M_Print (0, 88, "       Frag Limit");
+               if (fraglimit.integer == 0)
+                       M_Print (160, 88, "none");
+               else
+                       M_Print (160, 88, va("%i frags", fraglimit.integer));
 
-       M_Print (0, 96, "       Time Limit");
-       if (timelimit.integer == 0)
-               M_Print (160, 96, "none");
-       else
-               M_Print (160, 96, va("%i minutes", timelimit.integer));
+               M_Print (0, 96, "       Time Limit");
+               if (timelimit.integer == 0)
+                       M_Print (160, 96, "none");
+               else
+                       M_Print (160, 96, va("%i minutes", timelimit.integer));
+       }
 
        M_Print (0, 104, "    Public server");
        M_Print (160, 104, (sv_public.integer == 0) ? "no" : "yes");
 
+       M_Print (0, 120, "      Server name");
+       M_DrawTextBox (0, 124, 38, 1);
+       M_Print (8, 132, hostname.string);
+
        g = lookupgameinfo();
 
-       M_Print (0, 120, "         Episode");
-       M_Print (160, 120, g->episodes[startepisode].description);
+       if (gamemode != GAME_GOODVSBAD2)
+       {
+               M_Print (0, 152, "         Episode");
+               M_Print (160, 152, g->episodes[startepisode].description);
+       }
 
-       M_Print (0, 128, "           Level");
-       M_Print (160, 128, g->levels[g->episodes[startepisode].firstLevel + startlevel].description);
-       M_Print (160, 136, g->levels[g->episodes[startepisode].firstLevel + startlevel].name);
+       M_Print (0, 160, "           Level");
+       M_Print (160, 160, g->levels[g->episodes[startepisode].firstLevel + startlevel].description);
+       M_Print (160, 168, g->levels[g->episodes[startepisode].firstLevel + startlevel].name);
 
 // line cursor
-       M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
+       if (gameoptions_cursor == 8)
+               M_DrawCharacter (8 + 8 * strlen(hostname.string), gameoptions_cursor_table[gameoptions_cursor], 10+((int)(realtime*4)&1));
+       else
+               M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
 
        if (m_serverInfoMessage)
        {
@@ -3034,9 +3055,7 @@ void M_GameOptions_Draw (void)
                        M_Print (x, 170, " havoc@telefragged.com  ");
                }
                else
-               {
                        m_serverInfoMessage = false;
-               }
        }
 }
 
@@ -3061,6 +3080,8 @@ void M_NetStart_Change (int dir)
                break;
 
        case 2:
+               if (gamemode == GAME_GOODVSBAD2)
+                       break;
                if (gamemode == GAME_TRANSFUSION)
                {
                        if (deathmatch.integer == 2) // changing from CTF to BloodBath
@@ -3084,6 +3105,8 @@ void M_NetStart_Change (int dir)
                break;
 
        case 3:
+               if (gamemode == GAME_GOODVSBAD2)
+                       break;
                if (gamemode == GAME_ROGUE)
                        count = 6;
                else
@@ -3097,6 +3120,8 @@ void M_NetStart_Change (int dir)
                break;
 
        case 4:
+               if (gamemode == GAME_GOODVSBAD2)
+                       break;
                Cvar_SetValueQuick (&skill, skill.integer + dir);
                if (skill.integer > 3)
                        Cvar_SetValueQuick (&skill, 0);
@@ -3105,6 +3130,8 @@ void M_NetStart_Change (int dir)
                break;
 
        case 5:
+               if (gamemode == GAME_GOODVSBAD2)
+                       break;
                Cvar_SetValueQuick (&fraglimit, fraglimit.integer + dir*10);
                if (fraglimit.integer > 100)
                        Cvar_SetValueQuick (&fraglimit, 0);
@@ -3113,6 +3140,8 @@ void M_NetStart_Change (int dir)
                break;
 
        case 6:
+               if (gamemode == GAME_GOODVSBAD2)
+                       break;
                Cvar_SetValueQuick (&timelimit, timelimit.value + dir*5);
                if (timelimit.value > 60)
                        Cvar_SetValueQuick (&timelimit, 0);
@@ -3125,6 +3154,11 @@ void M_NetStart_Change (int dir)
                break;
 
        case 8:
+               break;
+
+       case 9:
+               if (gamemode == GAME_GOODVSBAD2)
+                       break;
                startepisode += dir;
                g = lookupgameinfo();
 
@@ -3137,7 +3171,7 @@ void M_NetStart_Change (int dir)
                startlevel = 0;
                break;
 
-       case 9:
+       case 10:
                startlevel += dir;
                g = lookupgameinfo();
 
@@ -3153,6 +3187,8 @@ void M_NetStart_Change (int dir)
 void M_GameOptions_Key (int key)
 {
        gamelevels_t *g;
+       int l;
+       char hostnamebuf[128];
 
        switch (key)
        {
@@ -3203,6 +3239,35 @@ void M_GameOptions_Key (int key)
 
                M_NetStart_Change (1);
                break;
+
+       case K_BACKSPACE:
+               if (gameoptions_cursor == 8)
+               {
+                       l = strlen(hostname.string);
+                       if (l)
+                       {
+                               l = min(l - 1, 37);
+                               memcpy(hostnamebuf, hostname.string, l);
+                               hostnamebuf[l] = 0;
+                               Cvar_Set("hostname", hostnamebuf);
+                       }
+               }
+               break;
+
+       default:
+               if (key < 32 || key > 127)
+                       break;
+               if (gameoptions_cursor == 8)
+               {
+                       l = strlen(hostname.string);
+                       if (l < 37)
+                       {
+                               memcpy(hostnamebuf, hostname.string, l);
+                               hostnamebuf[l] = key;
+                               hostnamebuf[l+1] = 0;
+                               Cvar_Set("hostname", hostnamebuf);
+                       }
+               }
        }
 }
 
@@ -3294,6 +3359,10 @@ void M_ServerList_Key(int k)
 
 void M_Init (void)
 {
+       menu_mempool = Mem_AllocPool("Menu");
+       menuplyr_load = true;
+       menuplyr_pixels = NULL;
+
        Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
 
        Cmd_AddCommand ("menu_main", M_Menu_Main_f);
@@ -3316,6 +3385,11 @@ void M_Init (void)
                numcommands = sizeof(transfusionbindnames) / sizeof(transfusionbindnames[0]);
                bindnames = transfusionbindnames;
        }
+       else if (gamemode == GAME_GOODVSBAD2)
+       {
+               numcommands = sizeof(goodvsbad2bindnames) / sizeof(goodvsbad2bindnames[0]);
+               bindnames = goodvsbad2bindnames;
+       }
        else
        {
                numcommands = sizeof(quakebindnames) / sizeof(quakebindnames[0]);