Index: ChangeLog
===================================================================
--- ChangeLog	(revision 10)
+++ ChangeLog	(revision 16)
@@ -1,3 +1,19 @@
+2006-04-24 Timothy 'TiM' Oliver <timothyoliver@bigpond.com>
+        - Incorporated scroll controls into the demonstrations user interface.
+          Great care was mainatained to ensure relative case was preserved.
+
+2006-04-22 Thilo Schulz <arny@ats.s.bawue.de>
+	- Fixed a small uncleanness that is probably of no real consequence
+	  to gameplay.
+	- Fixed some game issues.
+	  Bots can now join password protected servers, too.
+	  For more infos see:
+	  http://www.quake3world.com/ubb/Forum4/HTML/006208.html
+
+2006-04-13 Thilo Schulz <arny@ats.s.bawue.de>
+	- Changed demo names to be stored in lowercase in the menu
+	  structures for unix' case sensitivity.
+
 2006-04-08 Thilo Schulz <arny@ats.s.bawue.de>
 	- Added a bit of verboseness to the init_tonextint() function.
 
Index: Code-DM/game/g_main.c
===================================================================
--- Code-DM/game/g_main.c	(revision 10)
+++ Code-DM/game/g_main.c	(revision 16)
@@ -1203,7 +1203,9 @@
 qboolean levelExiting = qfalse;
 void ExitLevel (void) {
 	int		i;
-
+	char nextmap[MAX_STRING_CHARS];
+	char d1[MAX_STRING_CHARS];
+               
 	levelExiting = qtrue;
 
 	//bot interbreeding
@@ -1223,8 +1225,16 @@
 		return;	
 	}
 
+	trap_Cvar_VariableStringBuffer( "nextmap", nextmap, sizeof(nextmap) );
+	trap_Cvar_VariableStringBuffer( "d1", d1, sizeof(d1) );
 
-	trap_SendConsoleCommand( EXEC_APPEND, "vstr nextmap\n" );
+	if( !Q_stricmp( nextmap, "map_restart 0" ) && Q_stricmp( d1, "" ) ) {
+		trap_Cvar_Set( "nextmap", "vstr d2" );
+		trap_SendConsoleCommand( EXEC_APPEND, "vstr d1\n" );
+	} else {
+		trap_SendConsoleCommand( EXEC_APPEND, "vstr nextmap\n" );
+	}
+
 	level.changemap = NULL;
 	level.intermissiontime = 0;
 
@@ -1621,7 +1631,12 @@
 		if ( level.warmupTime < 0 ) {
 			if ( level.numPlayingClients == 2 ) {
 				// fudge by -1 to account for extra delays
-				level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
+				if ( g_warmup.integer > 1 ) {
+					level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000;
+				} else {
+					level.warmupTime = 0;
+				}
+
 				trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
 			}
 			return;
Index: Code-DM/game/q_shared.c
===================================================================
--- Code-DM/game/q_shared.c	(revision 10)
+++ Code-DM/game/q_shared.c	(revision 16)
@@ -38,11 +38,21 @@
 COM_StripExtension
 ============
 */
-void COM_StripExtension( const char *in, char *out ) {
-	while ( *in && *in != '.' ) {
-		*out++ = *in++;
+void COM_StripExtension(const char *in, char *out, int destsize)
+{
+	int		length;
+
+	Q_strncpyz(out, in, destsize);
+
+	length = strlen(out)-1;
+	while (length > 0 && out[length] != '.')
+	{
+		length--;
+		if (out[length] == '/')
+		return;         // no extension
 	}
-	*out = 0;
+	if (length)
+		out[length] = 0;
 }
 
 
Index: Code-DM/game/q_shared.h
===================================================================
--- Code-DM/game/q_shared.h	(revision 10)
+++ Code-DM/game/q_shared.h	(revision 16)
@@ -620,7 +620,7 @@
 float Com_Clamp( float min, float max, float value );
 
 char	*COM_SkipPath( char *pathname );
-void	COM_StripExtension( const char *in, char *out );
+void	COM_StripExtension(const char *in, char *out, int destsize);
 void	COM_DefaultExtension( char *path, int maxSize, const char *extension );
 
 void	COM_BeginParseSession( void );
Index: Code-DM/game/g_client.c
===================================================================
--- Code-DM/game/g_client.c	(revision 10)
+++ Code-DM/game/g_client.c	(revision 16)
@@ -1576,11 +1576,15 @@
 		return "Banned.";
 	}
 
-	// check for a password
-	value = Info_ValueForKey (userinfo, "password");
-	if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) &&
-		strcmp( g_password.string, value) != 0) {
-		return "Invalid password";
+	// Let bots connect on password protected servers, too.
+	if (!isBot && strcmp(value, "localhost"))
+	{
+		// check for a password
+		value = Info_ValueForKey (userinfo, "password");
+		if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) &&
+			strcmp( g_password.string, value) != 0) {
+			return "Invalid password";
+		}
 	}
 
 	// they can connect
@@ -2478,6 +2482,16 @@
 		ClientUserinfoChanged( level.sortedClients[0] );
 	}
 
+	if( g_gametype.integer == GT_TOURNAMENT &&
+		ent->client->sess.sessionTeam == TEAM_FREE &&
+		level.intermissiontime ) {
+
+		trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
+		level.restarted = qtrue;
+		level.changemap = NULL;
+		level.intermissiontime = 0;
+	}
+
 	trap_UnlinkEntity (ent);
 	ent->s.modelindex = 0;
 	ent->inuse = qfalse;
Index: Code-DM/cgame/cg_weapons.c
===================================================================
--- Code-DM/cgame/cg_weapons.c	(revision 10)
+++ Code-DM/cgame/cg_weapons.c	(revision 16)
@@ -95,7 +95,7 @@
 //	}
 
 	strcpy( path, item->view_model );
-	COM_StripExtension( path, path );
+	COM_StripExtension(path, path, sizeof(path));
 	strcat( path, "_flash.md3" );
 	weaponInfo->flashModel = trap_R_RegisterModel( path );
 
@@ -109,7 +109,7 @@
 	}
 	for (i=0; i< numBarrels; i++) {
 		Q_strncpyz( path, item->view_model, MAX_QPATH );
-		COM_StripExtension( path, path );
+		COM_StripExtension(path, path, sizeof(path));
 		if (i)
 		{
 			strcat( path, va("_barrel%d.md3", i+1));
@@ -120,7 +120,7 @@
 	}
 
 	strcpy( path, item->view_model );
-	COM_StripExtension( path, path );
+	COM_StripExtension(path, path, sizeof(path));
 	strcat( path, "_hand.md3" );
 	weaponInfo->handsModel = trap_R_RegisterModel( path );
 
Index: Code-DM/cgame/cg_servercmds.c
===================================================================
--- Code-DM/cgame/cg_servercmds.c	(revision 10)
+++ Code-DM/cgame/cg_servercmds.c	(revision 16)
@@ -215,7 +215,7 @@
 		cg.intermissionStarted = atoi( str );
 	} else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) {
 		cgs.gameModels[ num-CS_MODELS ] = trap_R_RegisterModel( str );
-	} else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_MODELS ) {
+	} else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS ) {
 		if ( str[0] != '*' ) {	// player specific sounds don't register here
 			cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str );
 		}
Index: Code-DM/cgame/cg_main.c
===================================================================
--- Code-DM/cgame/cg_main.c	(revision 10)
+++ Code-DM/cgame/cg_main.c	(revision 16)
@@ -1347,7 +1347,7 @@
 	char	fullFileName[MAX_QPATH];
 	char	objtext[MAX_OBJ_TEXT_LENGTH];
 
-	COM_StripExtension( cgs.mapname, fileName );
+	COM_StripExtension(cgs.mapname, fileName, sizeof(fileName));
 	CG_LanguageFilename( fileName, "efo", fullFileName);
 
 	len = trap_FS_FOpenFile( fullFileName, &f, FS_READ );
Index: Code-DM/ui/ui_playermodel.c
===================================================================
--- Code-DM/ui/ui_playermodel.c	(revision 10)
+++ Code-DM/ui/ui_playermodel.c	(revision 16)
@@ -518,7 +518,7 @@
 		{
 			filelen = strlen(fileptr);
 
-			COM_StripExtension(fileptr,skinname);
+			COM_StripExtension(fileptr, skinname, sizeof(skinname));
 
 			// look for icon_????
 			if (!Q_stricmpn(skinname,"icon_",5))
Index: Code-DM/ui/ui_demo2.c
===================================================================
--- Code-DM/ui/ui_demo2.c	(revision 10)
+++ Code-DM/ui/ui_demo2.c	(revision 16)
@@ -18,6 +18,8 @@
 #define ID_MAINMENU				10
 #define ID_ENGAGE				11
 #define ID_LIST					12
+#define ID_UP					13
+#define ID_DOWN					14
 
 #define ID_DEMOCOMMENT1			110
 #define ID_DEMOCOMMENT2			111
@@ -34,6 +36,9 @@
 
 #define PIC_UNDERLINE			"menu/common/underline.tga"
 
+#define PIC_UPARROW				"menu/common/arrow_up_16.tga"
+#define PIC_DNARROW				"menu/common/arrow_dn_16.tga"
+
 typedef struct 
 {
 	menuframework_s	menu;
@@ -41,6 +46,8 @@
 	menubitmap_s	main;
 	menubitmap_s	engage;
 	menulist_s		list;
+	menubitmap_s	upArrow;
+	menubitmap_s	downArrow;
 
 	qhandle_t		currentGameTopLeft;		// Upper left corner of current game box
 	qhandle_t		currentGameBotLeft;		// Bottom left corner of current game box
@@ -96,6 +103,59 @@
 };
 
 
+/*
+=================
+DemoMenu_PopulateList
+TiM: Fills the control
+list with values from the array,
+based off of an offset generated
+by the scroll buttons
+=================
+*/
+void DemoMenu_PopulateList ( int startingIndex ) {
+	int		i=0, len;
+	char*	demoName;
+	
+	while (g_demoline[i])
+	{
+		if (i >= s_demos.list.numitems)
+		{
+			break;
+		}
+
+		demoName = s_demos.demolist[startingIndex+i];
+
+		//TiM - Error trapping. Although this should never happen
+		if ( !demoName ) {
+			((menubitmap_s *)g_demoline[i])->generic.flags	= QMF_INACTIVE|QMF_HIDDEN;
+			continue;
+		}
+
+		// No demos???
+		if (s_demos.list.numitems == 1)
+		{
+			if (!strcmp( demoName, menu_normal_text[MNT_NO_DEMOS_FOUND]))
+			{
+				((menubitmap_s *)g_demoline[i])->generic.flags	= QMF_INACTIVE;
+			}
+		}
+
+		// strip extension
+		len = strlen( demoName );
+		if ( len>7 && !Q_stricmp(demoName +  len - 7,".efdemo"))
+		{
+			demoName[len-7] = '\0';
+		}
+
+		//Q_strupr(demoName);
+
+		((menubitmap_s *)g_demoline[i])->textPtr		= demoName;
+		((menubitmap_s *)g_demoline[i])->generic.flags	= QMF_HIGHLIGHT_IF_FOCUS;
+
+		i++;
+	}
+}
+
 /*
 =================
 DemoMenu_Graphics
@@ -149,9 +209,13 @@
 
 	UI_DrawHandlePic(205, 168,  277,  18, uis.whiteShader);			// Top bar
 	UI_DrawHandlePic(189, 193,  16,  224, uis.whiteShader);			// Left column
-	UI_DrawHandlePic(485, 193,  16,  224, uis.whiteShader);			// Right column
 	UI_DrawHandlePic(205, 424,  277,   8, uis.whiteShader);			// Bottom bar
 
+	//TiM - Arrow Boxes 
+	UI_DrawHandlePic(485, 193,  16,  16, uis.whiteShader);		// Up Arrow
+	UI_DrawHandlePic(485, 212,  16,  187, uis.whiteShader);		// Right column
+	UI_DrawHandlePic(485, 402,  16,  16, uis.whiteShader);		// Down Arrow
+
 	UI_DrawProportionalString(  124,  67, "67B",UI_TINYFONT, colorTable[CT_BLACK]);
 
 	UI_DrawProportionalString( 220, 169, menu_normal_text[MNT_CURRENTDEMOSAVAILABLE], UI_SMALLFONT, colorTable[CT_BLACK]);
@@ -188,6 +252,29 @@
 
 	switch( ((menucommon_s*)ptr)->id ) 
 	{
+		
+	case ID_UP:
+			s_demos.currentDemoIndex--;
+
+			if ( s_demos.currentDemoIndex < 0 )
+				s_demos.currentDemoIndex = 0;
+
+			DemoMenu_PopulateList( s_demos.currentDemoIndex );
+			break;
+
+		case ID_DOWN:
+			s_demos.currentDemoIndex++;
+
+			//TiM - cap it when the final entry comes into view
+			if ( s_demos.list.numitems > MAX_DEMODISP 
+				 && ( s_demos.currentDemoIndex + MAX_DEMODISP ) > s_demos.list.numitems )
+			{
+				s_demos.currentDemoIndex = s_demos.list.numitems-MAX_DEMODISP;
+			}
+
+			DemoMenu_PopulateList( s_demos.currentDemoIndex );
+			break;
+
 		case ID_DEMOCOMMENT1:
 		case ID_DEMOCOMMENT2:
 		case ID_DEMOCOMMENT3:
@@ -200,11 +287,10 @@
 		case ID_DEMOCOMMENT10:
 		case ID_DEMOCOMMENT11:
 		case ID_DEMOCOMMENT12:
-			index = ((menucommon_s*)ptr)->id - ID_DEMOCOMMENT1;
-			if (((menubitmap_s *)g_demoline[index])->textPtr)
+			index = ((menucommon_s*)ptr)->id - ID_DEMOCOMMENT1 + s_demos.currentDemoIndex;
+			if ( s_demos.demolist[index] )
 			{
-				s_demos.currentDemoIndex = index;
-				s_demos.currentFile.textPtr = (((menubitmap_s *)g_demoline[index])->textPtr);
+				s_demos.currentFile.textPtr = s_demos.demolist[index];
 				//make it so
 				s_demos.engage.generic.flags	= QMF_HIGHLIGHT_IF_FOCUS;
 			}
@@ -247,9 +333,14 @@
 	s_demos.currentGameTopLeft = trap_R_RegisterShaderNoMip("menu/common/corner_ul_18_24.tga");
 	s_demos.currentGameBotLeft = trap_R_RegisterShaderNoMip("menu/common/corner_ll_18_18.tga");
 	s_demos.currentGameTopRight = trap_R_RegisterShaderNoMip("menu/common/corner_ur_18_24.tga");
+
 	s_demos.directoryUpperCorner1 = trap_R_RegisterShaderNoMip("menu/common/corner_ul_16_18.tga");
 	s_demos.directoryLowerCorner1 = trap_R_RegisterShaderNoMip("menu/common/corner_ll_8_16.tga");
 	s_demos.directoryUpperCorner2 = trap_R_RegisterShaderNoMip("menu/common/corner_ur_16_18.tga");
+
+	trap_R_RegisterShaderNoMip(PIC_UPARROW);
+	trap_R_RegisterShaderNoMip(PIC_DNARROW);
+
 	trap_R_RegisterShaderNoMip(PIC_UNDERLINE);
 
 }
@@ -332,6 +423,35 @@
 	s_demos.currentFile.textPtr				= NULL;
 	s_demos.currentFile.textcolor			= CT_YELLOW;
 
+	//TiM - Scroll Buttons
+	s_demos.upArrow.generic.type			= MTYPE_BITMAP;
+	s_demos.upArrow.generic.flags			= (QMF_INACTIVE|QMF_GRAYED); //Disabled by default
+	s_demos.upArrow.generic.x				= 486;
+	s_demos.upArrow.generic.y				= 195;
+	s_demos.upArrow.generic.name			= PIC_UPARROW;
+	s_demos.upArrow.generic.id				= ID_UP;
+	s_demos.upArrow.generic.callback		= Demos_MenuEvent;
+	s_demos.upArrow.width					= 14;
+	s_demos.upArrow.height					= 14;
+	s_demos.upArrow.color					= CT_DKGOLD1;
+	s_demos.upArrow.color2					= CT_LTGOLD1;
+	s_demos.upArrow.textcolor				= CT_BLACK;
+	s_demos.upArrow.textcolor2				= CT_WHITE;
+
+	s_demos.downArrow.generic.type			= MTYPE_BITMAP;
+	s_demos.downArrow.generic.flags			= (QMF_INACTIVE|QMF_GRAYED);
+	s_demos.downArrow.generic.x				= 486;
+	s_demos.downArrow.generic.y				= 404;
+	s_demos.downArrow.generic.name			= PIC_DNARROW;
+	s_demos.downArrow.generic.id			= ID_DOWN;
+	s_demos.downArrow.generic.callback		= Demos_MenuEvent;
+	s_demos.downArrow.width					= 14;
+	s_demos.downArrow.height				= 14;
+	s_demos.downArrow.color					= CT_DKGOLD1;
+	s_demos.downArrow.color2				= CT_LTGOLD1;
+	s_demos.downArrow.textcolor				= CT_BLACK;
+	s_demos.downArrow.textcolor2			= CT_WHITE;
+
 	s_demos.list.generic.type				= MTYPE_SCROLLLIST;
 	s_demos.list.generic.flags				= QMF_PULSEIFFOCUS;
 	s_demos.list.generic.callback			= Demos_MenuEvent;
@@ -384,47 +504,47 @@
 		s_demos.list.numitems = MAX_DEMOS;
 	}
 
+	//TiM - If the list is longer than we can fit, enable the scroll buttons
+	if ( s_demos.list.numitems > MAX_DEMODISP ) {
+		s_demos.upArrow.generic.flags = QMF_HIGHLIGHT_IF_FOCUS;	
+		s_demos.downArrow.generic.flags = QMF_HIGHLIGHT_IF_FOCUS;	
+	}
+
 	// Point fields to demo names
 	i=0;
 	demoname = s_demos.names;
-	while (g_demoline[i])
-	{
-		if (i >= s_demos.list.numitems)
-		{
-			break;
-		}
 
-		((menubitmap_s *)g_demoline[i])->textPtr		= demoname;
-		((menubitmap_s *)g_demoline[i])->generic.flags	= QMF_HIGHLIGHT_IF_FOCUS;
+	//TiM - instead of sending the list names directly to the controls, we'll populate an array
+	//with all of them, and then specify which ones to display afterwards.
+	while ( i < s_demos.list.numitems ) {
+		if ( !demoname )
+			break;
+		
+		// strip extension
+		len = strlen( demoname );
+		if ( len>7 && !Q_stricmp(demoname +  len - 7,".efdemo"))
+		{
+			demoname[len-7] = '\0';
+		}
+		//Q_strupr(demoname);
+
+		//insert into the array
+		s_demos.demolist[i] = demoname;
+
+		//increment
+		demoname += len + 1;
+		i++;
+	}
 
-		// No demos???
-		if (s_demos.list.numitems == 1)
-		{
-			if (!strcmp( demoname, menu_normal_text[MNT_NO_DEMOS_FOUND]))
-			{
-				((menubitmap_s *)g_demoline[i])->generic.flags	= QMF_INACTIVE;
-			}
-		}
+	//Populate the controls with the values from the array	
+	DemoMenu_PopulateList( s_demos.currentDemoIndex ); 
 
-		// strip extension
-		len = strlen( demoname );
-		if ( len>7 && !Q_stricmp(demoname +  len - 7,".efdemo"))
-		{
-			demoname[len-7] = '\0';
-		}
-
-		Q_strupr(demoname);
-
-		demoname += len + 1;
-
-		i++;
-	}
-
 	Menu_AddItem( &s_demos.menu, &s_demos.main );
 //	Menu_AddItem( &s_demos.menu, &s_demos.list );
 	Menu_AddItem( &s_demos.menu, &s_demos.engage );
 	Menu_AddItem( &s_demos.menu, &s_demos.currentFile );
-
+	Menu_AddItem( &s_demos.menu, &s_demos.upArrow );
+	Menu_AddItem( &s_demos.menu, &s_demos.downArrow );
 }
 
 
Index: Code-DM/ui/ui_players.c
===================================================================
--- Code-DM/ui/ui_players.c	(revision 10)
+++ Code-DM/ui/ui_players.c	(revision 16)
@@ -70,7 +70,7 @@
 	}
 
 	strcpy( path, item->world_model );
-	COM_StripExtension( path, path );
+	COM_StripExtension(path, path, sizeof(path));
 	strcat( path, "_flash.md3" );
 	pi->flashModel = trap_R_RegisterModel( path );
 
