summaryrefslogtreecommitdiff
path: root/src/ui/ui_main.c
diff options
context:
space:
mode:
authorMikko Tiusanen <ams@daug.net>2014-05-04 01:18:52 +0300
committerMikko Tiusanen <ams@daug.net>2014-05-04 01:18:52 +0300
commit01beb9919b95479d8be040bec74abc5cc67a5e43 (patch)
tree65f0b79e793848491832756a4c3a32b23668fab3 /src/ui/ui_main.c
parent191d731da136b7ee959a17e63111c9146219a768 (diff)
Initial import.
Diffstat (limited to 'src/ui/ui_main.c')
-rw-r--r--src/ui/ui_main.c4646
1 files changed, 4646 insertions, 0 deletions
diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c
new file mode 100644
index 0000000..6487fae
--- /dev/null
+++ b/src/ui/ui_main.c
@@ -0,0 +1,4646 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2009 Darklegion Development
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Tremulous is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Tremulous; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+/*
+=======================================================================
+
+USER INTERFACE MAIN
+
+=======================================================================
+*/
+
+#include "ui_local.h"
+
+uiInfo_t uiInfo;
+
+static const char *netSources[ ] =
+{
+ "Internet",
+ "Mplayer",
+ "LAN",
+ "Favorites"
+};
+
+static const int numNetSources = sizeof( netSources ) / sizeof( const char* );
+
+static const char *netnames[ ] =
+{
+ "???",
+ "UDP",
+ "IPX",
+ NULL
+};
+
+/*
+================
+cvars
+================
+*/
+
+typedef struct
+{
+ vmCvar_t *vmCvar;
+ char *cvarName;
+ char *defaultString;
+ int cvarFlags;
+}
+
+cvarTable_t;
+
+vmCvar_t ui_browserShowFull;
+vmCvar_t ui_browserShowEmpty;
+
+vmCvar_t ui_dedicated;
+vmCvar_t ui_netSource;
+vmCvar_t ui_selectedMap;
+vmCvar_t ui_lastServerRefresh_0;
+vmCvar_t ui_lastServerRefresh_1;
+vmCvar_t ui_lastServerRefresh_2;
+vmCvar_t ui_lastServerRefresh_3;
+vmCvar_t ui_lastServerRefresh_0_time;
+vmCvar_t ui_lastServerRefresh_1_time;
+vmCvar_t ui_lastServerRefresh_2_time;
+vmCvar_t ui_lastServerRefresh_3_time;
+vmCvar_t ui_smallFont;
+vmCvar_t ui_bigFont;
+vmCvar_t ui_findPlayer;
+vmCvar_t ui_serverStatusTimeOut;
+vmCvar_t ui_textWrapCache;
+vmCvar_t ui_developer;
+
+vmCvar_t ui_emoticons;
+vmCvar_t ui_winner;
+vmCvar_t ui_chatCommands;
+
+static cvarTable_t cvarTable[ ] =
+{
+ { &ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE },
+ { &ui_browserShowEmpty, "ui_browserShowEmpty", "1", CVAR_ARCHIVE },
+
+ { &ui_dedicated, "ui_dedicated", "0", CVAR_ARCHIVE },
+ { &ui_netSource, "ui_netSource", "0", CVAR_ARCHIVE },
+ { &ui_selectedMap, "ui_selectedMap", "0", CVAR_ARCHIVE },
+ { &ui_lastServerRefresh_0, "ui_lastServerRefresh_0", "", CVAR_ARCHIVE},
+ { &ui_lastServerRefresh_1, "ui_lastServerRefresh_1", "", CVAR_ARCHIVE},
+ { &ui_lastServerRefresh_2, "ui_lastServerRefresh_2", "", CVAR_ARCHIVE},
+ { &ui_lastServerRefresh_3, "ui_lastServerRefresh_3", "", CVAR_ARCHIVE},
+ { &ui_lastServerRefresh_0, "ui_lastServerRefresh_0_time", "", CVAR_ARCHIVE},
+ { &ui_lastServerRefresh_1, "ui_lastServerRefresh_1_time", "", CVAR_ARCHIVE},
+ { &ui_lastServerRefresh_2, "ui_lastServerRefresh_2_time", "", CVAR_ARCHIVE},
+ { &ui_lastServerRefresh_3, "ui_lastServerRefresh_3_time", "", CVAR_ARCHIVE},
+ { &ui_smallFont, "ui_smallFont", "0.2", CVAR_ARCHIVE | CVAR_LATCH },
+ { &ui_bigFont, "ui_bigFont", "0.5", CVAR_ARCHIVE | CVAR_LATCH },
+ { &ui_findPlayer, "ui_findPlayer", "", CVAR_ARCHIVE},
+ { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE},
+ { &ui_textWrapCache, "ui_textWrapCache", "1", CVAR_ARCHIVE },
+ { &ui_developer, "ui_developer", "0", CVAR_ARCHIVE | CVAR_CHEAT },
+ { &ui_emoticons, "cg_emoticons", "1", CVAR_LATCH | CVAR_ARCHIVE },
+ { &ui_winner, "ui_winner", "", CVAR_ROM },
+ { &ui_chatCommands, "ui_chatCommands", "1", CVAR_ARCHIVE }
+};
+
+static int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] );
+
+/*
+================
+vmMain
+
+This is the only way control passes into the module.
+This must be the very first function compiled into the .qvm file
+================
+*/
+void UI_Init( qboolean );
+void UI_Shutdown( void );
+void UI_KeyEvent( int key, qboolean down );
+void UI_MouseEvent( int dx, int dy );
+int UI_MousePosition( void );
+void UI_SetMousePosition( int x, int y );
+void UI_Refresh( int realtime );
+qboolean UI_IsFullscreen( void );
+void UI_SetActiveMenu( uiMenuCommand_t menu );
+Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
+ int arg4, int arg5, int arg6, int arg7,
+ int arg8, int arg9, int arg10, int arg11 )
+{
+ switch( command )
+ {
+ case UI_GETAPIVERSION:
+ return UI_API_VERSION;
+
+ case UI_INIT:
+ UI_Init( arg0 );
+ return 0;
+
+ case UI_SHUTDOWN:
+ UI_Shutdown();
+ return 0;
+
+ case UI_KEY_EVENT:
+ UI_KeyEvent( arg0, arg1 );
+ return 0;
+
+ case UI_MOUSE_EVENT:
+ UI_MouseEvent( arg0, arg1 );
+ return 0;
+
+ case UI_MOUSE_POSITION:
+ return UI_MousePosition( );
+
+ case UI_SET_MOUSE_POSITION:
+ UI_SetMousePosition( arg0, arg1 );
+ return 0;
+
+ case UI_REFRESH:
+ UI_Refresh( arg0 );
+ return 0;
+
+ case UI_IS_FULLSCREEN:
+ return UI_IsFullscreen( );
+
+ case UI_SET_ACTIVE_MENU:
+ UI_SetActiveMenu( arg0 );
+ return 0;
+
+ case UI_CONSOLE_COMMAND:
+ return UI_ConsoleCommand( arg0 );
+
+ case UI_DRAW_CONNECT_SCREEN:
+ UI_DrawConnectScreen( arg0 );
+ return 0;
+ }
+
+ return -1;
+}
+
+
+
+void AssetCache( void )
+{
+ int i;
+
+ uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR );
+ uiInfo.uiDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR );
+ uiInfo.uiDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN );
+ uiInfo.uiDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP );
+ uiInfo.uiDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT );
+ uiInfo.uiDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT );
+ uiInfo.uiDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB );
+ uiInfo.uiDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR );
+ uiInfo.uiDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB );
+
+ if( ui_emoticons.integer )
+ {
+ uiInfo.uiDC.Assets.emoticonCount = BG_LoadEmoticons(
+ uiInfo.uiDC.Assets.emoticons, MAX_EMOTICONS );
+ }
+ else
+ uiInfo.uiDC.Assets.emoticonCount = 0;
+
+ for( i = 0; i < uiInfo.uiDC.Assets.emoticonCount; i++ )
+ {
+ uiInfo.uiDC.Assets.emoticons[ i ].shader = trap_R_RegisterShaderNoMip(
+ va( "emoticons/%s_%dx1.tga", uiInfo.uiDC.Assets.emoticons[ i ].name,
+ uiInfo.uiDC.Assets.emoticons[ i ].width ) );
+ }
+}
+
+void UI_DrawSides( float x, float y, float w, float h, float size )
+{
+ float sizeY;
+
+ UI_AdjustFrom640( &x, &y, &w, &h );
+ sizeY = size * uiInfo.uiDC.yscale;
+ size *= uiInfo.uiDC.xscale;
+
+ trap_R_DrawStretchPic( x, y + sizeY, size, h - ( sizeY * 2.0f ), 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
+ trap_R_DrawStretchPic( x + w - size, y + sizeY, size, h - ( sizeY * 2.0f ), 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
+}
+
+void UI_DrawTopBottom( float x, float y, float w, float h, float size )
+{
+ UI_AdjustFrom640( &x, &y, &w, &h );
+ size *= uiInfo.uiDC.yscale;
+ trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
+ trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
+}
+
+/*
+================
+UI_DrawRect
+
+Coordinates are 640*480 virtual values
+=================
+*/
+void UI_DrawRect( float x, float y, float width, float height, float size, const float *color )
+{
+ trap_R_SetColor( color );
+
+ UI_DrawTopBottom( x, y, width, height, size );
+ UI_DrawSides( x, y, width, height, size );
+
+ trap_R_SetColor( NULL );
+}
+
+/*
+==================
+UI_ServerInfoIsValid
+
+Return false if the infostring contains nonprinting characters,
+ or if the hostname is blank/undefined
+==================
+*/
+static qboolean UI_ServerInfoIsValid( char *info )
+{
+ char *c;
+ int len = 0;
+
+ for( c = info; *c; c++ )
+ {
+ if( !isprint( *c ) )
+ return qfalse;
+ }
+
+ for( c = Info_ValueForKey( info, "hostname" ); *c; c++ )
+ {
+ if( isgraph( *c ) )
+ len++;
+ }
+
+ if( len )
+ return qtrue;
+ else
+ return qfalse;
+}
+
+/*
+==================
+UI_InsertServerIntoDisplayList
+==================
+*/
+static void UI_InsertServerIntoDisplayList( int num, int position )
+{
+ int i;
+ static char info[MAX_STRING_CHARS];
+
+ if( position < 0 || position > uiInfo.serverStatus.numDisplayServers )
+ return;
+
+ trap_LAN_GetServerInfo( ui_netSource.integer, num, info, MAX_STRING_CHARS );
+
+ if( !UI_ServerInfoIsValid( info ) ) // don't list servers with invalid info
+ return;
+
+ uiInfo.serverStatus.numDisplayServers++;
+
+ for( i = uiInfo.serverStatus.numDisplayServers; i > position; i-- )
+ uiInfo.serverStatus.displayServers[i] = uiInfo.serverStatus.displayServers[i-1];
+
+ uiInfo.serverStatus.displayServers[position] = num;
+}
+
+/*
+==================
+UI_RemoveServerFromDisplayList
+==================
+*/
+static void UI_RemoveServerFromDisplayList( int num )
+{
+ int i, j;
+ static char info[MAX_STRING_CHARS];
+
+ for( i = 0; i < uiInfo.serverStatus.numDisplayServers; i++ )
+ {
+ if( uiInfo.serverStatus.displayServers[i] == num )
+ {
+ uiInfo.serverStatus.numDisplayServers--;
+
+ trap_LAN_GetServerInfo( ui_netSource.integer, num, info, MAX_STRING_CHARS );
+
+ for( j = i; j < uiInfo.serverStatus.numDisplayServers; j++ )
+ uiInfo.serverStatus.displayServers[j] = uiInfo.serverStatus.displayServers[j+1];
+
+ return;
+ }
+ }
+}
+
+/*
+==================
+UI_BinaryServerInsertion
+==================
+*/
+static void UI_BinaryServerInsertion( int num )
+{
+ int mid, offset, res, len;
+
+ // use binary search to insert server
+ len = uiInfo.serverStatus.numDisplayServers;
+ mid = len;
+ offset = 0;
+ res = 0;
+
+ while( mid > 0 )
+ {
+ mid = len >> 1;
+ //
+ res = trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey,
+ uiInfo.serverStatus.sortDir, num, uiInfo.serverStatus.displayServers[offset+mid] );
+ // if equal
+
+ if( res == 0 )
+ {
+ UI_InsertServerIntoDisplayList( num, offset + mid );
+ return;
+ }
+
+ // if larger
+ else if( res == 1 )
+ {
+ offset += mid;
+ len -= mid;
+ }
+
+ // if smaller
+ else
+ len -= mid;
+ }
+
+ if( res == 1 )
+ offset++;
+
+ UI_InsertServerIntoDisplayList( num, offset );
+}
+
+typedef struct
+{
+ char *name, *altName;
+}
+
+serverStatusCvar_t;
+
+serverStatusCvar_t serverStatusCvars[] = {
+ {"sv_hostname", "Name"},
+ {"Address", ""},
+ {"gamename", "Game name"},
+ {"mapname", "Map"},
+ {"version", ""},
+ {"protocol", ""},
+ {"timelimit", ""},
+ {NULL, NULL}
+ };
+
+/*
+==================
+UI_SortServerStatusInfo
+==================
+*/
+
+static int UI_SortServerStatusCompare( const void *a, const void *b )
+{
+ const char **la = (const char **)a;
+ const char **lb = (const char **)b;
+
+ return strcmp( la[0], lb[0] );
+}
+
+static void UI_SortServerStatusInfo( serverStatusInfo_t *info )
+{
+ int i, j, index;
+ char *tmp1, *tmp2;
+
+ index = 0;
+
+ for( i = 0; serverStatusCvars[i].name; i++ )
+ {
+ for( j = 0; j < info->numLines; j++ )
+ {
+ if( !info->lines[j][1] || info->lines[j][1][0] )
+ continue;
+
+ if( !Q_stricmp( serverStatusCvars[i].name, info->lines[j][0] ) )
+ {
+ // swap lines
+ tmp1 = info->lines[index][0];
+ tmp2 = info->lines[index][3];
+ info->lines[index][0] = info->lines[j][0];
+ info->lines[index][3] = info->lines[j][3];
+ info->lines[j][0] = tmp1;
+ info->lines[j][3] = tmp2;
+ //
+
+ if( strlen( serverStatusCvars[i].altName ) )
+ info->lines[index][0] = serverStatusCvars[i].altName;
+
+ index++;
+ }
+ }
+ }
+
+ // sort remaining cvars
+ qsort( info->lines + index,
+ info->numLines - index, sizeof( info->lines[ 0 ] ),
+ UI_SortServerStatusCompare );
+}
+
+/*
+==================
+UI_GetServerStatusInfo
+==================
+*/
+static int UI_GetServerStatusInfo( const char *serverAddress, serverStatusInfo_t *info )
+{
+ char *p, *score, *ping, *name;
+ int i, len;
+
+ if( !info )
+ {
+ trap_LAN_ServerStatus( serverAddress, NULL, 0 );
+ return qfalse;
+ }
+
+ memset( info, 0, sizeof( *info ) );
+
+ if( trap_LAN_ServerStatus( serverAddress, info->text, sizeof( info->text ) ) )
+ {
+ Q_strncpyz( info->address, serverAddress, sizeof( info->address ) );
+ p = info->text;
+ info->numLines = 0;
+ info->lines[info->numLines][0] = "Address";
+ info->lines[info->numLines][1] = "";
+ info->lines[info->numLines][2] = "";
+ info->lines[info->numLines][3] = info->address;
+ info->numLines++;
+ // get the cvars
+
+ while( p && *p )
+ {
+ p = strchr( p, '\\' );
+
+ if( !p ) break;
+
+ *p++ = '\0';
+
+ if( *p == '\\' )
+ break;
+
+ info->lines[info->numLines][0] = p;
+ info->lines[info->numLines][1] = "";
+ info->lines[info->numLines][2] = "";
+
+ p = strchr( p, '\\' );
+
+ if( !p ) break;
+
+ *p++ = '\0';
+
+ info->lines[info->numLines][3] = p;
+ info->numLines++;
+
+ if( info->numLines >= MAX_SERVERSTATUS_LINES )
+ break;
+ }
+
+ UI_SortServerStatusInfo( info );
+
+ // get the player list
+ if( info->numLines < MAX_SERVERSTATUS_LINES - 3 )
+ {
+ // empty line
+ info->lines[info->numLines][0] = "";
+ info->lines[info->numLines][1] = "";
+ info->lines[info->numLines][2] = "";
+ info->lines[info->numLines][3] = "";
+ info->numLines++;
+ // header
+ info->lines[info->numLines][0] = "num";
+ info->lines[info->numLines][1] = "score";
+ info->lines[info->numLines][2] = "ping";
+ info->lines[info->numLines][3] = "name";
+ info->numLines++;
+ // parse players
+ i = 0;
+ len = 0;
+
+ while( p && *p )
+ {
+ if( *p == '\\' )
+ *p++ = '\0';
+
+ if( !p )
+ break;
+
+ score = p;
+
+ p = strchr( p, ' ' );
+
+ if( !p )
+ break;
+
+ *p++ = '\0';
+
+ ping = p;
+
+ p = strchr( p, ' ' );
+
+ if( !p )
+ break;
+
+ *p++ = '\0';
+
+ name = p;
+
+ Com_sprintf( &info->pings[len], sizeof( info->pings ) - len, "%d", i );
+
+ info->lines[info->numLines][0] = &info->pings[len];
+
+ len += strlen( &info->pings[len] ) + 1;
+
+ info->lines[info->numLines][1] = score;
+ info->lines[info->numLines][2] = ping;
+ info->lines[info->numLines][3] = name;
+ info->numLines++;
+
+ if( info->numLines >= MAX_SERVERSTATUS_LINES )
+ break;
+
+ p = strchr( p, '\\' );
+
+ if( !p )
+ break;
+
+ *p++ = '\0';
+
+ //
+ i++;
+ }
+ }
+
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+==================
+stristr
+==================
+*/
+static char *stristr( char *str, char *charset )
+{
+ int i;
+
+ while( *str )
+ {
+ for( i = 0; charset[i] && str[i]; i++ )
+ if( toupper( charset[i] ) != toupper( str[i] ) ) break;
+
+ if( !charset[i] ) return str;
+
+ str++;
+ }
+
+ return NULL;
+}
+
+/*
+==================
+UI_BuildFindPlayerList
+==================
+*/
+static void UI_FeederSelection( int feederID, int index );
+
+static void UI_BuildFindPlayerList( qboolean force )
+{
+ static int numFound, numTimeOuts;
+ int i, j, k, resend;
+ serverStatusInfo_t info;
+ char name[MAX_NAME_LENGTH+2];
+ char infoString[MAX_STRING_CHARS];
+ qboolean duplicate;
+
+ if( !force )
+ {
+ if( !uiInfo.nextFindPlayerRefresh || uiInfo.nextFindPlayerRefresh > uiInfo.uiDC.realTime )
+ return;
+ }
+ else
+ {
+ memset( &uiInfo.pendingServerStatus, 0, sizeof( uiInfo.pendingServerStatus ) );
+ uiInfo.numFoundPlayerServers = 0;
+ uiInfo.currentFoundPlayerServer = 0;
+ trap_Cvar_VariableStringBuffer( "ui_findPlayer", uiInfo.findPlayerName, sizeof( uiInfo.findPlayerName ) );
+ Q_CleanStr( uiInfo.findPlayerName );
+ // should have a string of some length
+
+ if( !strlen( uiInfo.findPlayerName ) )
+ {
+ uiInfo.nextFindPlayerRefresh = 0;
+ return;
+ }
+
+ // set resend time
+ resend = ui_serverStatusTimeOut.integer / 2 - 10;
+
+ if( resend < 50 )
+ resend = 50;
+
+ trap_Cvar_Set( "cl_serverStatusResendTime", va( "%d", resend ) );
+ // reset all server status requests
+ trap_LAN_ServerStatus( NULL, NULL, 0 );
+ //
+ uiInfo.numFoundPlayerServers = 1;
+ Com_sprintf( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
+ sizeof( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1] ),
+ "searching %d...", uiInfo.pendingServerStatus.num );
+ numFound = 0;
+ numTimeOuts++;
+ }
+
+ for( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ )
+ {
+ // if this pending server is valid
+
+ if( uiInfo.pendingServerStatus.server[i].valid )
+ {
+ // try to get the server status for this server
+
+ if( UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, &info ) )
+ {
+ //
+ numFound++;
+ // parse through the server status lines
+
+ for( j = 0; j < info.numLines; j++ )
+ {
+ // should have ping info
+
+ if( !info.lines[j][2] || !info.lines[j][2][0] )
+ continue;
+
+ // clean string first
+ Q_strncpyz( name, info.lines[j][3], sizeof( name ) );
+
+ Q_CleanStr( name );
+
+ duplicate = qfalse;
+
+ for( k = 0; k < uiInfo.numFoundPlayerServers - 1; k++ )
+ {
+ if( Q_strncmp( uiInfo.foundPlayerServerAddresses[ k ],
+ uiInfo.pendingServerStatus.server[ i ].adrstr,
+ MAX_ADDRESSLENGTH ) == 0 )
+ duplicate = qtrue;
+ }
+
+ // if the player name is a substring
+ if( stristr( name, uiInfo.findPlayerName ) && !duplicate )
+ {
+ // add to found server list if we have space (always leave space for a line with the number found)
+
+ if( uiInfo.numFoundPlayerServers < MAX_FOUNDPLAYER_SERVERS - 1 )
+ {
+ //
+ Q_strncpyz( uiInfo.foundPlayerServerAddresses[uiInfo.numFoundPlayerServers-1],
+ uiInfo.pendingServerStatus.server[i].adrstr,
+ sizeof( uiInfo.foundPlayerServerAddresses[0] ) );
+ Q_strncpyz( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
+ uiInfo.pendingServerStatus.server[i].name,
+ sizeof( uiInfo.foundPlayerServerNames[0] ) );
+ uiInfo.numFoundPlayerServers++;
+ }
+ else
+ {
+ // can't add any more so we're done
+ uiInfo.pendingServerStatus.num = uiInfo.serverStatus.numDisplayServers;
+ }
+ }
+ }
+
+ Com_sprintf( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
+ sizeof( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1] ),
+ "searching %d/%d...", numFound, uiInfo.pendingServerStatus.num );
+ // retrieved the server status so reuse this spot
+ uiInfo.pendingServerStatus.server[i].valid = qfalse;
+ }
+ }
+
+ // if empty pending slot or timed out
+ if( !uiInfo.pendingServerStatus.server[i].valid ||
+ uiInfo.pendingServerStatus.server[i].startTime < uiInfo.uiDC.realTime - ui_serverStatusTimeOut.integer )
+ {
+ if( uiInfo.pendingServerStatus.server[i].valid )
+ numTimeOuts++;
+
+ // reset server status request for this address
+ UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, NULL );
+
+ // reuse pending slot
+ uiInfo.pendingServerStatus.server[i].valid = qfalse;
+
+ // if we didn't try to get the status of all servers in the main browser yet
+ if( uiInfo.pendingServerStatus.num < uiInfo.serverStatus.numDisplayServers )
+ {
+ uiInfo.pendingServerStatus.server[i].startTime = uiInfo.uiDC.realTime;
+ trap_LAN_GetServerAddressString( ui_netSource.integer,
+ uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num],
+ uiInfo.pendingServerStatus.server[i].adrstr,
+ sizeof( uiInfo.pendingServerStatus.server[i].adrstr ) );
+
+ trap_LAN_GetServerInfo( ui_netSource.integer,
+ uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num],
+ infoString, sizeof( infoString ) );
+
+ Q_strncpyz( uiInfo.pendingServerStatus.server[i].name,
+ Info_ValueForKey( infoString, "hostname" ),
+ sizeof( uiInfo.pendingServerStatus.server[0].name ) );
+
+ uiInfo.pendingServerStatus.server[i].valid = qtrue;
+ uiInfo.pendingServerStatus.num++;
+ Com_sprintf( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
+ sizeof( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1] ),
+ "searching %d/%d...", numFound, uiInfo.pendingServerStatus.num );
+ }
+ }
+ }
+
+ for( i = 0; i < MAX_SERVERSTATUSREQUESTS; i++ )
+ {
+ if( uiInfo.pendingServerStatus.server[i].valid )
+ break;
+ }
+
+ // if still trying to retrieve server status info
+ if( i < MAX_SERVERSTATUSREQUESTS )
+ uiInfo.nextFindPlayerRefresh = uiInfo.uiDC.realTime + 25;
+ else
+ {
+ // add a line that shows the number of servers found
+
+ if( !uiInfo.numFoundPlayerServers )
+ {
+ Com_sprintf( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
+ sizeof( uiInfo.foundPlayerServerAddresses[0] ), "no servers found" );
+ }
+ else
+ {
+ Com_sprintf( uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
+ sizeof( uiInfo.foundPlayerServerAddresses[0] ),
+ "%d server%s found with player %s", uiInfo.numFoundPlayerServers - 1,
+ uiInfo.numFoundPlayerServers == 2 ? "" : "s", uiInfo.findPlayerName );
+ }
+
+ uiInfo.nextFindPlayerRefresh = 0;
+ // show the server status info for the selected server
+ UI_FeederSelection( FEEDER_FINDPLAYER, uiInfo.currentFoundPlayerServer );
+ }
+}
+
+/*
+==================
+UI_BuildServerStatus
+==================
+*/
+static void UI_BuildServerStatus( qboolean force )
+{
+ if( uiInfo.nextFindPlayerRefresh )
+ return;
+
+ if( !force )
+ {
+ if( !uiInfo.nextServerStatusRefresh || uiInfo.nextServerStatusRefresh > uiInfo.uiDC.realTime )
+ return;
+ }
+ else
+ {
+ Menu_SetFeederSelection( NULL, FEEDER_SERVERSTATUS, 0, NULL );
+ uiInfo.serverStatusInfo.numLines = 0;
+ // reset all server status requests
+ trap_LAN_ServerStatus( NULL, NULL, 0 );
+ }
+
+ if( uiInfo.serverStatus.currentServer < 0 ||
+ uiInfo.serverStatus.currentServer > uiInfo.serverStatus.numDisplayServers ||
+ uiInfo.serverStatus.numDisplayServers == 0 )
+ return;
+
+ if( UI_GetServerStatusInfo( uiInfo.serverStatusAddress, &uiInfo.serverStatusInfo ) )
+ {
+ uiInfo.nextServerStatusRefresh = 0;
+ UI_GetServerStatusInfo( uiInfo.serverStatusAddress, NULL );
+ }
+ else
+ uiInfo.nextServerStatusRefresh = uiInfo.uiDC.realTime + 500;
+}
+
+/*
+==================
+UI_BuildServerDisplayList
+==================
+*/
+static void UI_BuildServerDisplayList( qboolean force )
+{
+ int i, count, clients, maxClients, ping, len, visible;
+ char info[MAX_STRING_CHARS];
+ static int numinvisible;
+
+ if( !( force || uiInfo.uiDC.realTime > uiInfo.serverStatus.nextDisplayRefresh ) )
+ return;
+
+ // if we shouldn't reset
+ if( force == 2 )
+ force = 0;
+
+ // do motd updates here too
+ trap_Cvar_VariableStringBuffer( "cl_motdString", uiInfo.serverStatus.motd, sizeof( uiInfo.serverStatus.motd ) );
+
+ len = strlen( uiInfo.serverStatus.motd );
+
+ if( len != uiInfo.serverStatus.motdLen )
+ {
+ uiInfo.serverStatus.motdLen = len;
+ uiInfo.serverStatus.motdWidth = -1;
+ }
+
+ if( force )
+ {
+ numinvisible = 0;
+ // clear number of displayed servers
+ uiInfo.serverStatus.numDisplayServers = 0;
+ uiInfo.serverStatus.numPlayersOnServers = 0;
+ // set list box index to zero
+ Menu_SetFeederSelection( NULL, FEEDER_SERVERS, 0, NULL );
+ // mark all servers as visible so we store ping updates for them
+ trap_LAN_MarkServerVisible( ui_netSource.integer, -1, qtrue );
+ }
+
+ // get the server count (comes from the master)
+ count = trap_LAN_GetServerCount( ui_netSource.integer );
+
+ if( count == -1 || ( ui_netSource.integer == AS_LOCAL && count == 0 ) )
+ {
+ // still waiting on a response from the master
+ uiInfo.serverStatus.numDisplayServers = 0;
+ uiInfo.serverStatus.numPlayersOnServers = 0;
+ uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 500;
+ return;
+ }
+
+ visible = qfalse;
+
+ for( i = 0; i < count; i++ )
+ {
+ // if we already got info for this server
+
+ if( !trap_LAN_ServerIsVisible( ui_netSource.integer, i ) )
+ continue;
+
+ visible = qtrue;
+ // get the ping for this server
+ ping = trap_LAN_GetServerPing( ui_netSource.integer, i );
+
+ if( ping > 0 || ui_netSource.integer == AS_FAVORITES )
+ {
+ trap_LAN_GetServerInfo( ui_netSource.integer, i, info, MAX_STRING_CHARS );
+
+ clients = atoi( Info_ValueForKey( info, "clients" ) );
+ uiInfo.serverStatus.numPlayersOnServers += clients;
+
+ if( ui_browserShowEmpty.integer == 0 )
+ {
+ if( clients == 0 )
+ {
+ trap_LAN_MarkServerVisible( ui_netSource.integer, i, qfalse );
+ continue;
+ }
+ }
+
+ if( ui_browserShowFull.integer == 0 )
+ {
+ maxClients = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
+
+ if( clients == maxClients )
+ {
+ trap_LAN_MarkServerVisible( ui_netSource.integer, i, qfalse );
+ continue;
+ }
+ }
+
+ // make sure we never add a favorite server twice
+ if( ui_netSource.integer == AS_FAVORITES )
+ UI_RemoveServerFromDisplayList( i );
+
+ // insert the server into the list
+ UI_BinaryServerInsertion( i );
+
+ // done with this server
+ if( ping > 0 )
+ {
+ trap_LAN_MarkServerVisible( ui_netSource.integer, i, qfalse );
+ numinvisible++;
+ }
+ }
+ }
+
+ uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime;
+
+ // if there were no servers visible for ping updates
+
+ if( !visible )
+ {
+// UI_StopServerRefresh();
+// uiInfo.serverStatus.nextDisplayRefresh = 0;
+ }
+}
+
+
+/*
+=================
+UI_StopServerRefresh
+=================
+*/
+static void UI_StopServerRefresh( void )
+{
+ int count;
+
+ if( !uiInfo.serverStatus.refreshActive )
+ {
+ // not currently refreshing
+ return;
+ }
+
+ uiInfo.serverStatus.refreshActive = qfalse;
+ Com_Printf( "%d servers listed in browser with %d players.\n",
+ uiInfo.serverStatus.numDisplayServers,
+ uiInfo.serverStatus.numPlayersOnServers );
+ count = trap_LAN_GetServerCount( ui_netSource.integer );
+
+ if( count - uiInfo.serverStatus.numDisplayServers > 0 )
+ {
+ Com_Printf( "%d servers not listed due to packet loss, invalid info,"
+ " or pings higher than %d\n",
+ count - uiInfo.serverStatus.numDisplayServers,
+ ( int ) trap_Cvar_VariableValue( "cl_maxPing" ) );
+ }
+
+}
+
+/*
+=================
+UI_DoServerRefresh
+=================
+*/
+static void UI_DoServerRefresh( void )
+{
+ qboolean wait = qfalse;
+
+ if( !uiInfo.serverStatus.refreshActive )
+ return;
+
+ if( ui_netSource.integer != AS_FAVORITES )
+ {
+ if( ui_netSource.integer == AS_LOCAL )
+ {
+ if( !trap_LAN_GetServerCount( ui_netSource.integer ) )
+ wait = qtrue;
+ }
+ else
+ {
+ if( trap_LAN_GetServerCount( ui_netSource.integer ) < 0 )
+ wait = qtrue;
+ }
+ }
+
+ if( uiInfo.uiDC.realTime < uiInfo.serverStatus.refreshtime )
+ {
+ if( wait )
+ return;
+ }
+
+ // if still trying to retrieve pings
+ if( trap_LAN_UpdateVisiblePings( ui_netSource.integer ) )
+ uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
+ else if( !wait )
+ {
+ // get the last servers in the list
+ UI_BuildServerDisplayList( 2 );
+ // stop the refresh
+ UI_StopServerRefresh();
+ }
+
+ //
+ UI_BuildServerDisplayList( qfalse );
+}
+
+/*
+=================
+UI_UpdatePendingPings
+=================
+*/
+static void UI_UpdatePendingPings( void )
+{
+ trap_LAN_ResetPings( ui_netSource.integer );
+ uiInfo.serverStatus.refreshActive = qtrue;
+ uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
+
+}
+
+/*
+=================
+UI_StartServerRefresh
+=================
+*/
+static void UI_StartServerRefresh( qboolean full )
+{
+ int time;
+ qtime_t q;
+
+ time = trap_RealTime( &q );
+ trap_Cvar_Set( va( "ui_lastServerRefresh_%i_time", ui_netSource.integer ),
+ va( "%i", time ) );
+ trap_Cvar_Set( va( "ui_lastServerRefresh_%i", ui_netSource.integer ),
+ va( "%04i-%02i-%02i %02i:%02i:%02i",
+ q.tm_year+1900, q.tm_mon+1, q.tm_mday,
+ q.tm_hour, q.tm_min, q.tm_sec ) );
+
+ if( !full )
+ {
+ UI_UpdatePendingPings();
+ return;
+ }
+
+ uiInfo.serverStatus.refreshActive = qtrue;
+ uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 1000;
+ // clear number of displayed servers
+ uiInfo.serverStatus.numDisplayServers = 0;
+ uiInfo.serverStatus.numPlayersOnServers = 0;
+ // mark all servers as visible so we store ping updates for them
+ trap_LAN_MarkServerVisible( ui_netSource.integer, -1, qtrue );
+ // reset all the pings
+ trap_LAN_ResetPings( ui_netSource.integer );
+ //
+
+ if( ui_netSource.integer == AS_LOCAL )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
+ uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
+ return;
+ }
+
+ uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000;
+
+ if( ui_netSource.integer == AS_GLOBAL || ui_netSource.integer == AS_MPLAYER )
+ {
+ qboolean global = ui_netSource.integer == AS_GLOBAL;
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d 70 full empty\n",
+ global ? 0 : 1 ) );
+ }
+}
+
+int frameCount = 0;
+int startTime;
+
+#define UI_FPS_FRAMES 4
+void UI_Refresh( int realtime )
+{
+ static int index;
+ static int previousTimes[UI_FPS_FRAMES];
+
+ //if( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
+ // return;
+ //}
+
+ uiInfo.uiDC.frameTime = realtime - uiInfo.uiDC.realTime;
+ uiInfo.uiDC.realTime = realtime;
+
+ previousTimes[index % UI_FPS_FRAMES] = uiInfo.uiDC.frameTime;
+ index++;
+
+ if( index > UI_FPS_FRAMES )
+ {
+ int i, total;
+ // average multiple frames together to smooth changes out a bit
+ total = 0;
+
+ for( i = 0 ; i < UI_FPS_FRAMES ; i++ )
+ total += previousTimes[i];
+
+ if( !total )
+ total = 1;
+
+ uiInfo.uiDC.FPS = 1000 * UI_FPS_FRAMES / total;
+ }
+
+ UI_UpdateCvars();
+
+ if( Menu_Count() > 0 )
+ {
+ Menu_UpdateAll( );
+ Menu_PaintAll( );
+ UI_DoServerRefresh( );
+ UI_BuildServerStatus( qfalse );
+ UI_BuildFindPlayerList( qfalse );
+ UI_UpdateNews( qfalse );
+ }
+
+ // draw cursor
+ UI_SetColor( NULL );
+
+ if( trap_Key_GetCatcher( ) == KEYCATCH_UI && !trap_Cvar_VariableValue( "ui_hideCursor" ) )
+ {
+ UI_DrawHandlePic( uiInfo.uiDC.cursorx - ( 16.0f * uiInfo.uiDC.aspectScale ), uiInfo.uiDC.cursory - 16.0f,
+ 32.0f * uiInfo.uiDC.aspectScale, 32.0f, uiInfo.uiDC.Assets.cursor );
+ }
+}
+
+/*
+=================
+UI_Shutdown
+=================
+*/
+void UI_Shutdown( void )
+{
+ trap_LAN_SaveCachedServers();
+}
+
+qboolean Asset_Parse( int handle )
+{
+ pc_token_t token;
+ const char *tempStr;
+
+ if( !trap_Parse_ReadToken( handle, &token ) )
+ return qfalse;
+
+ if( Q_stricmp( token.string, "{" ) != 0 )
+ return qfalse;
+
+ while( 1 )
+ {
+ memset( &token, 0, sizeof( pc_token_t ) );
+
+ if( !trap_Parse_ReadToken( handle, &token ) )
+ return qfalse;
+
+ if( Q_stricmp( token.string, "}" ) == 0 )
+ return qtrue;
+
+ // font
+ if( Q_stricmp( token.string, "font" ) == 0 )
+ {
+ int pointSize;
+
+ if( !PC_String_Parse( handle, &tempStr ) || !PC_Int_Parse( handle, &pointSize ) )
+ return qfalse;
+
+ trap_R_RegisterFont( tempStr, pointSize, &uiInfo.uiDC.Assets.textFont );
+ uiInfo.uiDC.Assets.fontRegistered = qtrue;
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "smallFont" ) == 0 )
+ {
+ int pointSize;
+
+ if( !PC_String_Parse( handle, &tempStr ) || !PC_Int_Parse( handle, &pointSize ) )
+ return qfalse;
+
+ trap_R_RegisterFont( tempStr, pointSize, &uiInfo.uiDC.Assets.smallFont );
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "bigFont" ) == 0 )
+ {
+ int pointSize;
+
+ if( !PC_String_Parse( handle, &tempStr ) || !PC_Int_Parse( handle, &pointSize ) )
+ return qfalse;
+
+ trap_R_RegisterFont( tempStr, pointSize, &uiInfo.uiDC.Assets.bigFont );
+ continue;
+ }
+
+
+ // gradientbar
+ if( Q_stricmp( token.string, "gradientbar" ) == 0 )
+ {
+ if( !PC_String_Parse( handle, &tempStr ) )
+ return qfalse;
+
+ uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( tempStr );
+ continue;
+ }
+
+ // enterMenuSound
+ if( Q_stricmp( token.string, "menuEnterSound" ) == 0 )
+ {
+ if( !PC_String_Parse( handle, &tempStr ) )
+ return qfalse;
+
+ uiInfo.uiDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse );
+ continue;
+ }
+
+ // exitMenuSound
+ if( Q_stricmp( token.string, "menuExitSound" ) == 0 )
+ {
+ if( !PC_String_Parse( handle, &tempStr ) )
+ return qfalse;
+
+ uiInfo.uiDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse );
+ continue;
+ }
+
+ // itemFocusSound
+ if( Q_stricmp( token.string, "itemFocusSound" ) == 0 )
+ {
+ if( !PC_String_Parse( handle, &tempStr ) )
+ return qfalse;
+
+ uiInfo.uiDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse );
+ continue;
+ }
+
+ // menuBuzzSound
+ if( Q_stricmp( token.string, "menuBuzzSound" ) == 0 )
+ {
+ if( !PC_String_Parse( handle, &tempStr ) )
+ return qfalse;
+
+ uiInfo.uiDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse );
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "cursor" ) == 0 )
+ {
+ if( !PC_String_Parse( handle, &uiInfo.uiDC.Assets.cursorStr ) )
+ return qfalse;
+
+ uiInfo.uiDC.Assets.cursor = trap_R_RegisterShaderNoMip( uiInfo.uiDC.Assets.cursorStr );
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "fadeClamp" ) == 0 )
+ {
+ if( !PC_Float_Parse( handle, &uiInfo.uiDC.Assets.fadeClamp ) )
+ return qfalse;
+
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "fadeCycle" ) == 0 )
+ {
+ if( !PC_Int_Parse( handle, &uiInfo.uiDC.Assets.fadeCycle ) )
+ return qfalse;
+
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "fadeAmount" ) == 0 )
+ {
+ if( !PC_Float_Parse( handle, &uiInfo.uiDC.Assets.fadeAmount ) )
+ return qfalse;
+
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "shadowX" ) == 0 )
+ {
+ if( !PC_Float_Parse( handle, &uiInfo.uiDC.Assets.shadowX ) )
+ return qfalse;
+
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "shadowY" ) == 0 )
+ {
+ if( !PC_Float_Parse( handle, &uiInfo.uiDC.Assets.shadowY ) )
+ return qfalse;
+
+ continue;
+ }
+
+ if( Q_stricmp( token.string, "shadowColor" ) == 0 )
+ {
+ if( !PC_Color_Parse( handle, &uiInfo.uiDC.Assets.shadowColor ) )
+ return qfalse;
+
+ uiInfo.uiDC.Assets.shadowFadeClamp = uiInfo.uiDC.Assets.shadowColor[3];
+ continue;
+ }
+
+ }
+
+ return qfalse;
+}
+
+void UI_Report( void )
+{
+ String_Report();
+}
+
+void UI_ParseMenu( const char *menuFile )
+{
+ int handle;
+ pc_token_t token;
+
+ handle = trap_Parse_LoadSource( menuFile );
+
+ if( !handle )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: Menu file %s not found\n",
+ menuFile );
+ return;
+ }
+
+ while( 1 )
+ {
+ memset( &token, 0, sizeof( pc_token_t ) );
+
+ if( !trap_Parse_ReadToken( handle, &token ) )
+ break;
+
+ //if( Q_stricmp( token, "{" ) ) {
+ // Com_Printf( "Missing { in menu file\n" );
+ // break;
+ //}
+
+ //if( menuCount == MAX_MENUS ) {
+ // Com_Printf( "Too many menus!\n" );
+ // break;
+ //}
+
+ if( token.string[0] == '}' )
+ break;
+
+ if( Q_stricmp( token.string, "assetGlobalDef" ) == 0 )
+ {
+ if( Asset_Parse( handle ) )
+ continue;
+ else
+ break;
+ }
+
+ if( Q_stricmp( token.string, "menudef" ) == 0 )
+ {
+ // start a new menu
+ Menu_New( handle );
+ }
+ }
+
+ trap_Parse_FreeSource( handle );
+}
+
+qboolean Load_Menu( int handle )
+{
+ pc_token_t token;
+
+ if( !trap_Parse_ReadToken( handle, &token ) )
+ return qfalse;
+
+ if( token.string[0] != '{' )
+ return qfalse;
+
+ while( 1 )
+ {
+ if( !trap_Parse_ReadToken( handle, &token ) )
+ return qfalse;
+
+ if( token.string[0] == 0 )
+ return qfalse;
+
+ if( token.string[0] == '}' )
+ return qtrue;
+
+ UI_ParseMenu( token.string );
+ }
+
+ return qfalse;
+}
+
+void UI_LoadMenus( const char *menuFile, qboolean reset )
+{
+ pc_token_t token;
+ int handle;
+ int start;
+
+ start = trap_Milliseconds();
+
+ handle = trap_Parse_LoadSource( menuFile );
+
+ if( !handle )
+ trap_Error( va( S_COLOR_RED "menu list '%s' not found, unable to continue!\n", menuFile ) );
+
+ if( reset )
+ Menu_Reset();
+
+ while( 1 )
+ {
+ if( !trap_Parse_ReadToken( handle, &token ) )
+ break;
+
+ if( token.string[0] == 0 || token.string[0] == '}' )
+ break;
+
+ if( token.string[0] == '}' )
+ break;
+
+ if( Q_stricmp( token.string, "loadmenu" ) == 0 )
+ {
+ if( Load_Menu( handle ) )
+ continue;
+ else
+ break;
+ }
+ }
+
+ // Com_Printf( "UI menu file '%s' loaded in %d msec\n", menuFile, trap_Milliseconds() - start );
+
+ trap_Parse_FreeSource( handle );
+}
+
+void UI_LoadHelp( const char *helpFile )
+{
+ pc_token_t token;
+ int handle, start;
+ char title[ 64 ], buffer[ 2048 ];
+
+ start = trap_Milliseconds();
+
+ handle = trap_Parse_LoadSource( helpFile );
+ if( !handle )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: help file '%s' not found!\n",
+ helpFile );
+ return;
+ }
+
+ if( !trap_Parse_ReadToken( handle, &token ) ||
+ token.string[0] == 0 || token.string[0] != '{' )
+ {
+ Com_Printf( S_COLOR_YELLOW "WARNING: help file '%s' does not start with "
+ "'{'\n", helpFile );
+ return;
+ }
+
+ uiInfo.helpCount = 0;
+ title[ 0 ] = 0;
+ while( 1 )
+ {
+ if( !trap_Parse_ReadToken( handle, &token ) ||
+ token.string[0] == 0 || token.string[0] == '}' )
+ break;
+
+ if( token.string[0] == '{' )
+ {
+ buffer[ 0 ] = 0;
+ Q_strcat( buffer, sizeof( buffer ), title );
+ Q_strcat( buffer, sizeof( buffer ), "\n\n" );
+ while( trap_Parse_ReadToken( handle, &token ) &&
+ token.string[0] != 0 && token.string[0] != '}' )
+ {
+ Q_strcat( buffer, sizeof( buffer ), token.string );
+ }
+
+ uiInfo.helpList[ uiInfo.helpCount ].text = String_Alloc( title );
+ uiInfo.helpList[ uiInfo.helpCount ].v.text = String_Alloc( buffer );
+ uiInfo.helpList[ uiInfo.helpCount ].type = INFOTYPE_TEXT;
+ uiInfo.helpCount++;
+ title[ 0 ] = 0;
+ }
+ else
+ Q_strcat( title, sizeof( title ), token.string );
+ }
+
+ trap_Parse_FreeSource( handle );
+
+ Com_Printf( "UI help file '%s' loaded in %d msec (%d infopanes)\n",
+ helpFile, trap_Milliseconds() - start, uiInfo.helpCount );
+}
+
+void UI_Load( void )
+{
+ char lastName[2048];
+ menuDef_t *menu = Menu_GetFocused();
+
+ if( menu && menu->window.name )
+ strcpy( lastName, menu->window.name );
+
+ String_Init();
+
+ UI_LoadMenus( "ui/menus.txt", qtrue );
+ UI_LoadMenus( "ui/ingame.txt", qfalse );
+ UI_LoadMenus( "ui/tremulous.txt", qfalse );
+ UI_LoadHelp( "ui/help.txt" );
+ Menus_CloseAll( );
+ Menus_ActivateByName( lastName );
+
+}
+
+/*
+===============
+UI_GetCurrentAlienStage
+===============
+*/
+static stage_t UI_GetCurrentAlienStage( void )
+{
+ char buffer[ MAX_TOKEN_CHARS ];
+ stage_t stage, dummy;
+
+ trap_Cvar_VariableStringBuffer( "ui_stages", buffer, sizeof( buffer ) );
+ sscanf( buffer, "%d %d", ( int * ) & stage , ( int * ) & dummy );
+
+ return stage;
+}
+
+/*
+===============
+UI_GetCurrentHumanStage
+===============
+*/
+static stage_t UI_GetCurrentHumanStage( void )
+{
+ char buffer[ MAX_TOKEN_CHARS ];
+ stage_t stage, dummy;
+
+ trap_Cvar_VariableStringBuffer( "ui_stages", buffer, sizeof( buffer ) );
+ sscanf( buffer, "%d %d", ( int * ) & dummy, ( int * ) & stage );
+
+ return stage;
+}
+
+/*
+===============
+UI_DrawInfoPane
+===============
+*/
+static void UI_DrawInfoPane( menuItem_t *item, rectDef_t *rect, float text_x, float text_y,
+ float scale, int textalign, int textvalign, vec4_t color, int textStyle )
+{
+ int value = 0;
+ const char *s = "";
+ char *string = "";
+
+ int class, credits;
+ char ui_currentClass[ MAX_STRING_CHARS ];
+
+ trap_Cvar_VariableStringBuffer( "ui_currentClass", ui_currentClass, MAX_STRING_CHARS );
+
+ sscanf( ui_currentClass, "%d %d", &class, &credits );
+
+ switch( item->type )
+ {
+ case INFOTYPE_TEXT:
+ s = item->v.text;
+ break;
+
+ case INFOTYPE_CLASS:
+ value = ( BG_ClassCanEvolveFromTo( class, item->v.pclass, credits,
+ UI_GetCurrentAlienStage(), 0 ) +
+ ALIEN_CREDITS_PER_KILL - 1 ) / ALIEN_CREDITS_PER_KILL;
+
+ if( value < 1 )
+ {
+ s = va( "%s\n\n%s",
+ BG_ClassConfig( item->v.pclass )->humanName,
+ BG_Class( item->v.pclass )->info );
+ }
+ else
+ {
+ s = va( "%s\n\n%s\n\nFrags: %d",
+ BG_ClassConfig( item->v.pclass )->humanName,
+ BG_Class( item->v.pclass )->info,
+ value );
+ }
+
+ break;
+
+ case INFOTYPE_WEAPON:
+ value = BG_Weapon( item->v.weapon )->price;
+
+ if( value == 0 )
+ {
+ s = va( "%s\n\n%s\n\nCredits: Free",
+ BG_Weapon( item->v.weapon )->humanName,
+ BG_Weapon( item->v.weapon )->info );
+ }
+ else
+ {
+ s = va( "%s\n\n%s\n\nCredits: %d",
+ BG_Weapon( item->v.weapon )->humanName,
+ BG_Weapon( item->v.weapon )->info,
+ value );
+ }
+
+ break;
+
+ case INFOTYPE_UPGRADE:
+ value = BG_Upgrade( item->v.upgrade )->price;
+
+ if( value == 0 )
+ {
+ s = va( "%s\n\n%s\n\nCredits: Free",
+ BG_Upgrade( item->v.upgrade )->humanName,
+ BG_Upgrade( item->v.upgrade )->info );
+ }
+ else
+ {
+ s = va( "%s\n\n%s\n\nCredits: %d",
+ BG_Upgrade( item->v.upgrade )->humanName,
+ BG_Upgrade( item->v.upgrade )->info,
+ value );
+ }
+
+ break;
+
+ case INFOTYPE_BUILDABLE:
+ value = BG_Buildable( item->v.buildable )->buildPoints;
+
+ switch( BG_Buildable( item->v.buildable )->team )
+ {
+ case TEAM_ALIENS:
+ string = "Sentience";
+ break;
+
+ case TEAM_HUMANS:
+ string = "Power";
+ break;
+
+ default:
+ break;
+ }
+
+ if( value == 0 )
+ {
+ s = va( "%s\n\n%s",
+ BG_Buildable( item->v.buildable )->humanName,
+ BG_Buildable( item->v.buildable )->info );
+ }
+ else
+ {
+ s = va( "%s\n\n%s\n\n%s: %d",
+ BG_Buildable( item->v.buildable )->humanName,
+ BG_Buildable( item->v.buildable )->info,
+ string, value );
+ }
+
+ break;
+ }
+
+ UI_DrawTextBlock( rect, text_x, text_y, color, scale,
+ textalign, textvalign, textStyle, s );
+}
+
+
+static void UI_DrawServerMapPreview( rectDef_t *rect, float scale, vec4_t color )
+{
+ if( uiInfo.serverStatus.currentServerCinematic >= 0 )
+ {
+ trap_CIN_RunCinematic( uiInfo.serverStatus.currentServerCinematic );
+ trap_CIN_SetExtents( uiInfo.serverStatus.currentServerCinematic, rect->x, rect->y, rect->w, rect->h );
+ trap_CIN_DrawCinematic( uiInfo.serverStatus.currentServerCinematic );
+ }
+ else if( uiInfo.serverStatus.currentServerPreview > 0 )
+ UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.serverStatus.currentServerPreview );
+ else
+ UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip( "gfx/2d/load_screen" ) );
+}
+
+static void UI_DrawSelectedMapPreview( rectDef_t *rect, float scale, vec4_t color )
+{
+ int map = ui_selectedMap.integer;
+
+ if( map < 0 || map > uiInfo.mapCount )
+ {
+ ui_selectedMap.integer = 0;
+ trap_Cvar_Set( "ui_selectedMap", "0" );
+ map = 0;
+ }
+
+ if( uiInfo.mapList[map].cinematic >= -1 )
+ {
+ if( uiInfo.mapList[map].cinematic == -1 )
+ uiInfo.mapList[map].cinematic = trap_CIN_PlayCinematic( va( "%s.roq", uiInfo.mapList[map].mapLoadName ),
+ 0, 0, 0, 0, ( CIN_loop | CIN_silent ) );
+
+ if( uiInfo.mapList[map].cinematic >= 0 )
+ {
+ trap_CIN_RunCinematic( uiInfo.mapList[map].cinematic );
+ trap_CIN_SetExtents( uiInfo.mapList[map].cinematic, rect->x, rect->y, rect->w, rect->h );
+ trap_CIN_DrawCinematic( uiInfo.mapList[map].cinematic );
+ }
+ else
+ uiInfo.mapList[map].cinematic = -2;
+ }
+ else
+ {
+ if( uiInfo.mapList[map].levelShot == -1 )
+ uiInfo.mapList[map].levelShot = trap_R_RegisterShaderNoMip( uiInfo.mapList[map].imageName );
+
+ if( uiInfo.mapList[map].levelShot > 0 )
+ UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.mapList[map].levelShot );
+ else
+ UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip( "gfx/2d/load_screen" ) );
+ }
+}
+
+static void UI_DrawSelectedMapName( rectDef_t *rect, float scale, vec4_t color, int textStyle )
+{
+ int map = ui_selectedMap.integer;
+
+ if( map >= 0 && map < uiInfo.mapCount )
+ UI_Text_Paint( rect->x, rect->y, scale, color, uiInfo.mapList[map].mapName, 0, 0, textStyle );
+}
+
+static const char *UI_OwnerDrawText( int ownerDraw )
+{
+ const char *s = NULL;
+
+ switch( ownerDraw )
+ {
+ case UI_NETSOURCE:
+ if( ui_netSource.integer < 0 || ui_netSource.integer >= numNetSources )
+ ui_netSource.integer = 0;
+
+ s = netSources[ui_netSource.integer];
+ break;
+
+ case UI_KEYBINDSTATUS:
+ if( Display_KeyBindPending() )
+ s = "Waiting for new key... Press ESCAPE to cancel";
+ else
+ s = "Press ENTER or CLICK to change, Press BACKSPACE to clear";
+
+ break;
+
+ case UI_SERVERREFRESHDATE:
+ if( uiInfo.serverStatus.refreshActive )
+ {
+#define MAX_DOTS 5
+ int numServers = trap_LAN_GetServerCount( ui_netSource.integer );
+ int numDots = ( uiInfo.uiDC.realTime / 500 ) % ( MAX_DOTS + 1 );
+ char dots[ MAX_DOTS + 1 ];
+ int i;
+
+ for( i = 0; i < numDots; i++ )
+ dots[ i ] = '.';
+
+ dots[ i ] = '\0';
+
+ s = numServers < 0 ? va( "Waiting for response%s", dots ) :
+ va( "Getting info for %d servers (ESC to cancel)%s", numServers, dots );
+ }
+ else
+ s = va( "Refresh Time: %s", UI_Cvar_VariableString( va( "ui_lastServerRefresh_%i", ui_netSource.integer ) ) );
+
+ break;
+
+ case UI_SERVERMOTD:
+ s = uiInfo.serverStatus.motd;
+ break;
+
+ default:
+ break;
+ }
+
+ return s;
+}
+
+static int UI_OwnerDrawWidth( int ownerDraw, float scale )
+{
+ const char *s = NULL;
+
+ switch( ownerDraw )
+ {
+ case UI_NETSOURCE:
+ case UI_KEYBINDSTATUS:
+ case UI_SERVERREFRESHDATE:
+ case UI_SERVERMOTD:
+ s = UI_OwnerDrawText( ownerDraw );
+ break;
+
+ default:
+ break;
+ }
+
+ if( s )
+ return UI_Text_Width( s, scale );
+
+ return 0;
+}
+
+/*
+===============
+UI_BuildPlayerList
+===============
+*/
+static void UI_BuildPlayerList( void )
+{
+ uiClientState_t cs;
+ int n, count, team, team2, playerTeamNumber;
+ char info[MAX_INFO_STRING];
+
+ trap_GetClientState( &cs );
+ trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING );
+ uiInfo.playerNumber = cs.clientNum;
+ team = atoi( Info_ValueForKey( info, "t" ) );
+ trap_GetConfigString( CS_SERVERINFO, info, sizeof( info ) );
+ count = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
+ uiInfo.playerCount = 0;
+ uiInfo.myTeamCount = 0;
+ uiInfo.myPlayerIndex = 0;
+ playerTeamNumber = 0;
+
+ for( n = 0; n < count; n++ )
+ {
+ trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING );
+
+ if( info[0] )
+ {
+ Com_ClientListParse( &uiInfo.ignoreList[ uiInfo.playerCount ],
+ Info_ValueForKey( info, "ig" ) );
+ Q_strncpyz( uiInfo.rawPlayerNames[uiInfo.playerCount],
+ Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
+ Q_strncpyz( uiInfo.playerNames[uiInfo.playerCount],
+ Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
+ Q_CleanStr( uiInfo.playerNames[uiInfo.playerCount] );
+ uiInfo.clientNums[uiInfo.playerCount] = n;
+
+ if( n == uiInfo.playerNumber )
+ uiInfo.myPlayerIndex = uiInfo.playerCount;
+
+ uiInfo.playerCount++;
+
+ team2 = atoi( Info_ValueForKey( info, "t" ) );
+
+ if( team2 == team )
+ {
+ Q_strncpyz( uiInfo.rawTeamNames[uiInfo.myTeamCount],
+ Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
+ Q_strncpyz( uiInfo.teamNames[uiInfo.myTeamCount],
+ Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
+ Q_CleanStr( uiInfo.teamNames[uiInfo.myTeamCount] );
+ uiInfo.teamClientNums[uiInfo.myTeamCount] = n;
+
+ if( uiInfo.playerNumber == n )
+ playerTeamNumber = uiInfo.myTeamCount;
+
+ uiInfo.myTeamCount++;
+ }
+ }
+ }
+}
+
+static void UI_DrawGLInfo( rectDef_t *rect, float scale, int textalign, int textvalign,
+ vec4_t color, int textStyle, float text_x, float text_y )
+{
+ char buffer[ 4096 ];
+
+ Com_sprintf( buffer, sizeof( buffer ), "VENDOR: %s\nVERSION: %s\n"
+ "PIXELFORMAT: color(%d-bits) Z(%d-bits) stencil(%d-bits)\n%s",
+ uiInfo.uiDC.glconfig.vendor_string, uiInfo.uiDC.glconfig.renderer_string,
+ uiInfo.uiDC.glconfig.colorBits, uiInfo.uiDC.glconfig.depthBits,
+ uiInfo.uiDC.glconfig.stencilBits, uiInfo.uiDC.glconfig.extensions_string );
+
+ UI_DrawTextBlock( rect, text_x, text_y, color, scale,
+ textalign, textvalign, textStyle, buffer );
+}
+
+// FIXME: table drive
+//
+static void UI_OwnerDraw( float x, float y, float w, float h,
+ float text_x, float text_y, int ownerDraw,
+ int ownerDrawFlags, int align,
+ int textalign, int textvalign, float borderSize,
+ float scale, vec4_t foreColor, vec4_t backColor,
+ qhandle_t shader,
+ int textStyle )
+{
+ rectDef_t rect;
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ switch( ownerDraw )
+ {
+ case UI_TEAMINFOPANE:
+ UI_DrawInfoPane( &uiInfo.teamList[ uiInfo.teamIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_ACLASSINFOPANE:
+ UI_DrawInfoPane( &uiInfo.alienClassList[ uiInfo.alienClassIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_AUPGRADEINFOPANE:
+ UI_DrawInfoPane( &uiInfo.alienUpgradeList[ uiInfo.alienUpgradeIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_HITEMINFOPANE:
+ UI_DrawInfoPane( &uiInfo.humanItemList[ uiInfo.humanItemIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_HBUYINFOPANE:
+ UI_DrawInfoPane( &uiInfo.humanArmouryBuyList[ uiInfo.humanArmouryBuyIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_HSELLINFOPANE:
+ UI_DrawInfoPane( &uiInfo.humanArmourySellList[ uiInfo.humanArmourySellIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_ABUILDINFOPANE:
+ UI_DrawInfoPane( &uiInfo.alienBuildList[ uiInfo.alienBuildIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_HBUILDINFOPANE:
+ UI_DrawInfoPane( &uiInfo.humanBuildList[ uiInfo.humanBuildIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_HELPINFOPANE:
+ UI_DrawInfoPane( &uiInfo.helpList[ uiInfo.helpIndex ],
+ &rect, text_x, text_y, scale, textalign, textvalign, foreColor, textStyle );
+ break;
+
+ case UI_NETMAPPREVIEW:
+ UI_DrawServerMapPreview( &rect, scale, foreColor );
+ break;
+
+ case UI_SELECTEDMAPPREVIEW:
+ UI_DrawSelectedMapPreview( &rect, scale, foreColor );
+ break;
+
+ case UI_SELECTEDMAPNAME:
+ UI_DrawSelectedMapName( &rect, scale, foreColor, textStyle );
+ break;
+
+ case UI_GLINFO:
+ UI_DrawGLInfo( &rect, scale, textalign, textvalign, foreColor, textStyle, text_x, text_y );
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+static qboolean UI_OwnerDrawVisible( int flags )
+{
+ qboolean vis = qtrue;
+ uiClientState_t cs;
+ team_t team;
+ char info[ MAX_INFO_STRING ];
+
+ trap_GetClientState( &cs );
+ trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING );
+ team = atoi( Info_ValueForKey( info, "t" ) );
+
+
+ while( flags )
+ {
+ if( flags & UI_SHOW_NOTSPECTATING )
+ {
+ if( team == TEAM_NONE )
+ vis = qfalse;
+
+ flags &= ~UI_SHOW_NOTSPECTATING;
+ }
+
+ if( flags & UI_SHOW_VOTEACTIVE )
+ {
+ if( !trap_Cvar_VariableValue( "ui_voteActive" ) )
+ vis = qfalse;
+
+ flags &= ~UI_SHOW_VOTEACTIVE;
+ }
+
+ if( flags & UI_SHOW_CANVOTE )
+ {
+ if( trap_Cvar_VariableValue( "ui_voteActive" ) )
+ vis = qfalse;
+
+ flags &= ~UI_SHOW_CANVOTE;
+ }
+
+ if( flags & UI_SHOW_TEAMVOTEACTIVE )
+ {
+ if( team == TEAM_ALIENS )
+ {
+ if( !trap_Cvar_VariableValue( "ui_alienTeamVoteActive" ) )
+ vis = qfalse;
+ }
+ else if( team == TEAM_HUMANS )
+ {
+ if( !trap_Cvar_VariableValue( "ui_humanTeamVoteActive" ) )
+ vis = qfalse;
+ }
+
+ flags &= ~UI_SHOW_TEAMVOTEACTIVE;
+ }
+
+ if( flags & UI_SHOW_CANTEAMVOTE )
+ {
+ if( team == TEAM_ALIENS )
+ {
+ if( trap_Cvar_VariableValue( "ui_alienTeamVoteActive" ) )
+ vis = qfalse;
+ }
+ else if( team == TEAM_HUMANS )
+ {
+ if( trap_Cvar_VariableValue( "ui_humanTeamVoteActive" ) )
+ vis = qfalse;
+ }
+
+ flags &= ~UI_SHOW_CANTEAMVOTE;
+ }
+
+ if( flags & UI_SHOW_FAVORITESERVERS )
+ {
+ // this assumes you only put this type of display flag on something showing in the proper context
+
+ if( ui_netSource.integer != AS_FAVORITES )
+ vis = qfalse;
+
+ flags &= ~UI_SHOW_FAVORITESERVERS;
+ }
+
+ if( flags & UI_SHOW_NOTFAVORITESERVERS )
+ {
+ // this assumes you only put this type of display flag on something showing in the proper context
+
+ if( ui_netSource.integer == AS_FAVORITES )
+ vis = qfalse;
+
+ flags &= ~UI_SHOW_NOTFAVORITESERVERS;
+ }
+ else
+ flags = 0;
+ }
+
+ return vis;
+}
+
+static qboolean UI_NetSource_HandleKey( int key )
+{
+ if( key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER )
+ {
+ if( key == K_MOUSE2 )
+ {
+ ui_netSource.integer--;
+
+ if( ui_netSource.integer == AS_MPLAYER )
+ ui_netSource.integer--;
+ }
+ else
+ {
+ ui_netSource.integer++;
+
+ if( ui_netSource.integer == AS_MPLAYER )
+ ui_netSource.integer++;
+ }
+
+ if( ui_netSource.integer >= numNetSources )
+ ui_netSource.integer = 0;
+ else if( ui_netSource.integer < 0 )
+ ui_netSource.integer = numNetSources - 1;
+
+ UI_BuildServerDisplayList( qtrue );
+
+ if( ui_netSource.integer != AS_GLOBAL )
+ UI_StartServerRefresh( qtrue );
+
+ trap_Cvar_Set( "ui_netSource", va( "%d", ui_netSource.integer ) );
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+static qboolean UI_OwnerDrawHandleKey( int ownerDraw, int key )
+{
+ switch( ownerDraw )
+ {
+ case UI_NETSOURCE:
+ UI_NetSource_HandleKey( key );
+ break;
+
+ default:
+ break;
+ }
+
+ return qfalse;
+}
+
+
+/*
+=================
+UI_ServersQsortCompare
+=================
+*/
+static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 )
+{
+ return trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey,
+ uiInfo.serverStatus.sortDir, *( int* )arg1, *( int* )arg2 );
+}
+
+
+/*
+=================
+UI_ServersSort
+=================
+*/
+void UI_ServersSort( int column, qboolean force )
+{
+ if( !force )
+ {
+ if( uiInfo.serverStatus.sortKey == column )
+ return;
+ }
+
+ uiInfo.serverStatus.sortKey = column;
+ qsort( &uiInfo.serverStatus.displayServers[0], uiInfo.serverStatus.numDisplayServers,
+ sizeof( int ), UI_ServersQsortCompare );
+}
+
+/*
+===============
+UI_LoadTeams
+===============
+*/
+static void UI_LoadTeams( void )
+{
+ uiInfo.teamCount = 4;
+
+ uiInfo.teamList[ 0 ].text = "Aliens";
+ uiInfo.teamList[ 0 ].cmd = "cmd team aliens\n";
+ uiInfo.teamList[ 0 ].type = INFOTYPE_TEXT;
+ uiInfo.teamList[ 0 ].v.text =
+ "The Alien Team\n\n"
+ "The Aliens' strengths are in movement and the ability to "
+ "quickly construct new bases quickly. They possess a range "
+ "of abilities including basic melee attacks, movement-"
+ "crippling poisons and more.";
+
+ uiInfo.teamList[ 1 ].text = "Humans";
+ uiInfo.teamList[ 1 ].cmd = "cmd team humans\n";
+ uiInfo.teamList[ 1 ].type = INFOTYPE_TEXT;
+ uiInfo.teamList[ 1 ].v.text =
+ "The Human Team\n\n"
+ "The humans are the masters of technology. Although their "
+ "bases take long to construct, their automated defense "
+ "ensures they stay built. A wide range of upgrades and "
+ "weapons are available to the humans, each contributing "
+ "to eradicate the alien threat.";
+
+ uiInfo.teamList[ 2 ].text = "Spectate";
+ uiInfo.teamList[ 2 ].cmd = "cmd team spectate\n";
+ uiInfo.teamList[ 2 ].type = INFOTYPE_TEXT;
+ uiInfo.teamList[ 2 ].v.text = "Watch the game without playing.";
+
+ uiInfo.teamList[ 3 ].text = "Auto select";
+ uiInfo.teamList[ 3 ].cmd = "cmd team auto\n";
+ uiInfo.teamList[ 3 ].type = INFOTYPE_TEXT;
+ uiInfo.teamList[ 3 ].v.text = "Join the team with the least players.";
+}
+
+/*
+===============
+UI_AddClass
+===============
+*/
+
+static void UI_AddClass( class_t class )
+{
+ uiInfo.alienClassList[ uiInfo.alienClassCount ].text =
+ BG_ClassConfig( class )->humanName;
+ uiInfo.alienClassList[ uiInfo.alienClassCount ].cmd =
+ String_Alloc( va( "cmd class %s\n", BG_Class( class )->name ) );
+ uiInfo.alienClassList[ uiInfo.alienClassCount ].type = INFOTYPE_CLASS;
+
+ uiInfo.alienClassList[ uiInfo.alienClassCount ].v.pclass = class;
+
+ uiInfo.alienClassCount++;
+}
+
+/*
+===============
+UI_LoadAlienClasses
+===============
+*/
+static void UI_LoadAlienClasses( void )
+{
+ uiInfo.alienClassCount = 0;
+
+ if( BG_ClassIsAllowed( PCL_ALIEN_LEVEL0 ) )
+ UI_AddClass( PCL_ALIEN_LEVEL0 );
+
+ if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0_UPG ) &&
+ BG_ClassAllowedInStage( PCL_ALIEN_BUILDER0_UPG, UI_GetCurrentAlienStage( ) ) )
+ UI_AddClass( PCL_ALIEN_BUILDER0_UPG );
+ else if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0 ) )
+ UI_AddClass( PCL_ALIEN_BUILDER0 );
+}
+
+/*
+===============
+UI_AddItem
+===============
+*/
+static void UI_AddItem( weapon_t weapon )
+{
+ uiInfo.humanItemList[ uiInfo.humanItemCount ].text =
+ BG_Weapon( weapon )->humanName;
+ uiInfo.humanItemList[ uiInfo.humanItemCount ].cmd =
+ String_Alloc( va( "cmd class %s\n", BG_Weapon( weapon )->name ) );
+ uiInfo.humanItemList[ uiInfo.humanItemCount ].type = INFOTYPE_WEAPON;
+ uiInfo.humanItemList[ uiInfo.humanItemCount ].v.weapon = weapon;
+
+ uiInfo.humanItemCount++;
+}
+
+/*
+===============
+UI_LoadHumanItems
+===============
+*/
+static void UI_LoadHumanItems( void )
+{
+ uiInfo.humanItemCount = 0;
+
+ if( BG_WeaponIsAllowed( WP_MACHINEGUN ) )
+ UI_AddItem( WP_MACHINEGUN );
+
+ if( BG_WeaponIsAllowed( WP_HBUILD ) )
+ UI_AddItem( WP_HBUILD );
+}
+
+/*
+===============
+UI_ParseCarriageList
+===============
+*/
+static void UI_ParseCarriageList( void )
+{
+ int i;
+ char carriageCvar[ MAX_TOKEN_CHARS ];
+ char *iterator;
+ char buffer[ MAX_TOKEN_CHARS ];
+ char *bufPointer;
+
+ trap_Cvar_VariableStringBuffer( "ui_carriage", carriageCvar, sizeof( carriageCvar ) );
+ iterator = carriageCvar;
+
+ uiInfo.weapons = 0;
+ uiInfo.upgrades = 0;
+
+ //simple parser to give rise to weapon/upgrade list
+
+ while( iterator && iterator[ 0 ] != '$' )
+ {
+ bufPointer = buffer;
+
+ if( iterator[ 0 ] == 'W' )
+ {
+ iterator++;
+
+ while( iterator[ 0 ] != ' ' )
+ *bufPointer++ = *iterator++;
+
+ *bufPointer++ = '\n';
+
+ i = atoi( buffer );
+
+ uiInfo.weapons |= ( 1 << i );
+ }
+ else if( iterator[ 0 ] == 'U' )
+ {
+ iterator++;
+
+ while( iterator[ 0 ] != ' ' )
+ *bufPointer++ = *iterator++;
+
+ *bufPointer++ = '\n';
+
+ i = atoi( buffer );
+
+ uiInfo.upgrades |= ( 1 << i );
+ }
+
+ iterator++;
+ }
+}
+
+/*
+===============
+UI_LoadHumanArmouryBuys
+===============
+*/
+static void UI_LoadHumanArmouryBuys( void )
+{
+ int i, j = 0;
+ stage_t stage = UI_GetCurrentHumanStage( );
+ int slots = 0;
+
+ UI_ParseCarriageList( );
+
+ for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
+ {
+ if( uiInfo.weapons & ( 1 << i ) )
+ slots |= BG_Weapon( i )->slots;
+ }
+
+ for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
+ {
+ if( uiInfo.upgrades & ( 1 << i ) )
+ slots |= BG_Upgrade( i )->slots;
+ }
+
+ uiInfo.humanArmouryBuyCount = 0;
+
+ for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
+ {
+ if( BG_Weapon( i )->team == TEAM_HUMANS &&
+ BG_Weapon( i )->purchasable &&
+ BG_WeaponAllowedInStage( i, stage ) &&
+ BG_WeaponIsAllowed( i ) &&
+ !( BG_Weapon( i )->slots & slots ) &&
+ !( uiInfo.weapons & ( 1 << i ) ) )
+ {
+ uiInfo.humanArmouryBuyList[ j ].text = BG_Weapon( i )->humanName;
+ uiInfo.humanArmouryBuyList[ j ].cmd =
+ String_Alloc( va( "cmd buy %s\n", BG_Weapon( i )->name ) );
+ uiInfo.humanArmouryBuyList[ j ].type = INFOTYPE_WEAPON;
+ uiInfo.humanArmouryBuyList[ j ].v.weapon = i;
+
+ j++;
+
+ uiInfo.humanArmouryBuyCount++;
+ }
+ }
+
+ for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
+ {
+ if( BG_Upgrade( i )->team == TEAM_HUMANS &&
+ BG_Upgrade( i )->purchasable &&
+ BG_UpgradeAllowedInStage( i, stage ) &&
+ BG_UpgradeIsAllowed( i ) &&
+ !( BG_Upgrade( i )->slots & slots ) &&
+ !( uiInfo.upgrades & ( 1 << i ) ) )
+ {
+ uiInfo.humanArmouryBuyList[ j ].text = BG_Upgrade( i )->humanName;
+ uiInfo.humanArmouryBuyList[ j ].cmd =
+ String_Alloc( va( "cmd buy %s\n", BG_Upgrade( i )->name ) );
+ uiInfo.humanArmouryBuyList[ j ].type = INFOTYPE_UPGRADE;
+ uiInfo.humanArmouryBuyList[ j ].v.upgrade = i;
+
+ j++;
+
+ uiInfo.humanArmouryBuyCount++;
+ }
+ }
+}
+
+/*
+===============
+UI_LoadHumanArmourySells
+===============
+*/
+static void UI_LoadHumanArmourySells( void )
+{
+ int i, j = 0;
+
+ uiInfo.humanArmourySellCount = 0;
+ UI_ParseCarriageList( );
+
+ for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
+ {
+ if( uiInfo.weapons & ( 1 << i ) )
+ {
+ uiInfo.humanArmourySellList[ j ].text = BG_Weapon( i )->humanName;
+ uiInfo.humanArmourySellList[ j ].cmd =
+ String_Alloc( va( "cmd sell %s\n", BG_Weapon( i )->name ) );
+ uiInfo.humanArmourySellList[ j ].type = INFOTYPE_WEAPON;
+ uiInfo.humanArmourySellList[ j ].v.weapon = i;
+
+ j++;
+
+ uiInfo.humanArmourySellCount++;
+ }
+ }
+
+ for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
+ {
+ if( uiInfo.upgrades & ( 1 << i ) )
+ {
+ uiInfo.humanArmourySellList[ j ].text = BG_Upgrade( i )->humanName;
+ uiInfo.humanArmourySellList[ j ].cmd =
+ String_Alloc( va( "cmd sell %s\n", BG_Upgrade( i )->name ) );
+ uiInfo.humanArmourySellList[ j ].type = INFOTYPE_UPGRADE;
+ uiInfo.humanArmourySellList[ j ].v.upgrade = i;
+
+ j++;
+
+ uiInfo.humanArmourySellCount++;
+ }
+ }
+}
+
+/*
+===============
+UI_ArmouryRefreshCb
+===============
+*/
+static void UI_ArmouryRefreshCb( void *data )
+{
+ int oldWeapons = uiInfo.weapons;
+ int oldUpgrades = uiInfo.upgrades;
+
+ UI_ParseCarriageList( );
+
+ if( uiInfo.weapons != oldWeapons || uiInfo.upgrades != oldUpgrades )
+ {
+ UI_LoadHumanArmouryBuys( );
+ UI_LoadHumanArmourySells( );
+ UI_RemoveCaptureFunc( );
+ }
+}
+
+/*
+===============
+UI_LoadAlienUpgrades
+===============
+*/
+static void UI_LoadAlienUpgrades( void )
+{
+ int i, j = 0;
+
+ int class, credits;
+ char ui_currentClass[ MAX_STRING_CHARS ];
+ stage_t stage = UI_GetCurrentAlienStage( );
+
+ trap_Cvar_VariableStringBuffer( "ui_currentClass", ui_currentClass, MAX_STRING_CHARS );
+
+ sscanf( ui_currentClass, "%d %d", &class, &credits );
+
+ uiInfo.alienUpgradeCount = 0;
+
+ for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
+ {
+ if( BG_ClassCanEvolveFromTo( class, i, credits, stage, 0 ) >= 0 )
+ {
+ uiInfo.alienUpgradeList[ j ].text = BG_ClassConfig( i )->humanName;
+ uiInfo.alienUpgradeList[ j ].cmd =
+ String_Alloc( va( "cmd class %s\n", BG_Class( i )->name ) );
+ uiInfo.alienUpgradeList[ j ].type = INFOTYPE_CLASS;
+ uiInfo.alienUpgradeList[ j ].v.pclass = i;
+
+ j++;
+
+ uiInfo.alienUpgradeCount++;
+ }
+ }
+}
+
+/*
+===============
+UI_LoadAlienBuilds
+===============
+*/
+static void UI_LoadAlienBuilds( void )
+{
+ int i, j = 0;
+ stage_t stage;
+
+ UI_ParseCarriageList( );
+ stage = UI_GetCurrentAlienStage( );
+
+ uiInfo.alienBuildCount = 0;
+
+ for( i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++ )
+ {
+ if( BG_Buildable( i )->team == TEAM_ALIENS &&
+ BG_Buildable( i )->buildWeapon & uiInfo.weapons &&
+ BG_BuildableAllowedInStage( i, stage ) &&
+ BG_BuildableIsAllowed( i ) )
+ {
+ uiInfo.alienBuildList[ j ].text = BG_Buildable( i )->humanName;
+ uiInfo.alienBuildList[ j ].cmd =
+ String_Alloc( va( "cmd build %s\n", BG_Buildable( i )->name ) );
+ uiInfo.alienBuildList[ j ].type = INFOTYPE_BUILDABLE;
+ uiInfo.alienBuildList[ j ].v.buildable = i;
+
+ j++;
+
+ uiInfo.alienBuildCount++;
+ }
+ }
+}
+
+/*
+===============
+UI_LoadHumanBuilds
+===============
+*/
+static void UI_LoadHumanBuilds( void )
+{
+ int i, j = 0;
+ stage_t stage;
+
+ UI_ParseCarriageList( );
+ stage = UI_GetCurrentHumanStage( );
+
+ uiInfo.humanBuildCount = 0;
+
+ for( i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++ )
+ {
+ if( BG_Buildable( i )->team == TEAM_HUMANS &&
+ BG_Buildable( i )->buildWeapon & uiInfo.weapons &&
+ BG_BuildableAllowedInStage( i, stage ) &&
+ BG_BuildableIsAllowed( i ) )
+ {
+ uiInfo.humanBuildList[ j ].text = BG_Buildable( i )->humanName;
+ uiInfo.humanBuildList[ j ].cmd =
+ String_Alloc( va( "cmd build %s\n", BG_Buildable( i )->name ) );
+ uiInfo.humanBuildList[ j ].type = INFOTYPE_BUILDABLE;
+ uiInfo.humanBuildList[ j ].v.buildable = i;
+
+ j++;
+
+ uiInfo.humanBuildCount++;
+ }
+ }
+}
+
+/*
+===============
+UI_LoadMods
+===============
+*/
+static void UI_LoadMods( void )
+{
+ int numdirs;
+ char dirlist[2048];
+ char *dirptr;
+ char *descptr;
+ int i;
+ int dirlen;
+
+ uiInfo.modCount = 0;
+ numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof( dirlist ) );
+ dirptr = dirlist;
+
+ for( i = 0; i < numdirs; i++ )
+ {
+ dirlen = strlen( dirptr ) + 1;
+ descptr = dirptr + dirlen;
+ uiInfo.modList[uiInfo.modCount].modName = String_Alloc( dirptr );
+ uiInfo.modList[uiInfo.modCount].modDescr = String_Alloc( descptr );
+ dirptr += dirlen + strlen( descptr ) + 1;
+ uiInfo.modCount++;
+
+ if( uiInfo.modCount >= MAX_MODS )
+ break;
+ }
+
+}
+
+
+/*
+===============
+UI_LoadMovies
+===============
+*/
+static void UI_LoadMovies( void )
+{
+ char movielist[4096];
+ char *moviename;
+ int i, len;
+
+ uiInfo.movieCount = trap_FS_GetFileList( "video", "roq", movielist, 4096 );
+
+ if( uiInfo.movieCount )
+ {
+ if( uiInfo.movieCount > MAX_MOVIES )
+ uiInfo.movieCount = MAX_MOVIES;
+
+ moviename = movielist;
+
+ for( i = 0; i < uiInfo.movieCount; i++ )
+ {
+ len = strlen( moviename );
+
+ if( !Q_stricmp( moviename + len - 4, ".roq" ) )
+ moviename[len-4] = '\0';
+
+ Q_strupr( moviename );
+ uiInfo.movieList[i] = String_Alloc( moviename );
+ moviename += len + 1;
+ }
+ }
+
+}
+
+
+
+/*
+===============
+UI_LoadDemos
+===============
+*/
+static void UI_LoadDemos( void )
+{
+ char demolist[4096];
+ char demoExt[32];
+ char *demoname;
+ int i, len;
+
+ Com_sprintf( demoExt, sizeof( demoExt ), "dm_%d", ( int )trap_Cvar_VariableValue( "protocol" ) );
+
+ uiInfo.demoCount = trap_FS_GetFileList( "demos", demoExt, demolist, 4096 );
+
+ Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", ( int )trap_Cvar_VariableValue( "protocol" ) );
+
+ if( uiInfo.demoCount )
+ {
+ if( uiInfo.demoCount > MAX_DEMOS )
+ uiInfo.demoCount = MAX_DEMOS;
+
+ demoname = demolist;
+
+ for( i = 0; i < uiInfo.demoCount; i++ )
+ {
+ len = strlen( demoname );
+
+ if( !Q_stricmp( demoname + len - strlen( demoExt ), demoExt ) )
+ demoname[len-strlen( demoExt )] = '\0';
+
+ uiInfo.demoList[i] = String_Alloc( demoname );
+ demoname += len + 1;
+ }
+ }
+
+}
+
+static void UI_Update( const char *name )
+{
+ int val = trap_Cvar_VariableValue( name );
+
+ if( Q_stricmp( name, "ui_SetName" ) == 0 )
+ trap_Cvar_Set( "name", UI_Cvar_VariableString( "ui_Name" ) );
+ else if( Q_stricmp( name, "ui_setRate" ) == 0 )
+ {
+ float rate = trap_Cvar_VariableValue( "rate" );
+
+ if( rate >= 5000 )
+ {
+ trap_Cvar_Set( "cl_maxpackets", "30" );
+ trap_Cvar_Set( "cl_packetdup", "1" );
+ }
+ else if( rate >= 4000 )
+ {
+ trap_Cvar_Set( "cl_maxpackets", "15" );
+ trap_Cvar_Set( "cl_packetdup", "2" ); // favor less prediction errors when there's packet loss
+ }
+ else
+ {
+ trap_Cvar_Set( "cl_maxpackets", "15" );
+ trap_Cvar_Set( "cl_packetdup", "1" ); // favor lower bandwidth
+ }
+ }
+ else if( Q_stricmp( name, "ui_GetName" ) == 0 )
+ trap_Cvar_Set( "ui_Name", UI_Cvar_VariableString( "name" ) );
+ else if( Q_stricmp( name, "r_colorbits" ) == 0 )
+ {
+ switch( val )
+ {
+ case 0:
+ trap_Cvar_SetValue( "r_depthbits", 0 );
+ trap_Cvar_SetValue( "r_stencilbits", 0 );
+ break;
+
+ case 16:
+ trap_Cvar_SetValue( "r_depthbits", 16 );
+ trap_Cvar_SetValue( "r_stencilbits", 0 );
+ break;
+
+ case 32:
+ trap_Cvar_SetValue( "r_depthbits", 24 );
+ break;
+ }
+ }
+ else if( Q_stricmp( name, "r_lodbias" ) == 0 )
+ {
+ switch( val )
+ {
+ case 0:
+ trap_Cvar_SetValue( "r_subdivisions", 4 );
+ break;
+
+ case 1:
+ trap_Cvar_SetValue( "r_subdivisions", 12 );
+ break;
+
+ case 2:
+ trap_Cvar_SetValue( "r_subdivisions", 20 );
+ break;
+ }
+ }
+ else if( Q_stricmp( name, "ui_glCustom" ) == 0 )
+ {
+ switch( val )
+ {
+ case 0: // high quality
+ trap_Cvar_SetValue( "r_subdivisions", 4 );
+ trap_Cvar_SetValue( "r_vertexlight", 0 );
+ trap_Cvar_SetValue( "r_lodbias", 0 );
+ trap_Cvar_SetValue( "r_colorbits", 32 );
+ trap_Cvar_SetValue( "r_depthbits", 24 );
+ trap_Cvar_SetValue( "r_picmip", 0 );
+ trap_Cvar_SetValue( "r_texturebits", 32 );
+ trap_Cvar_SetValue( "r_fastSky", 0 );
+ trap_Cvar_SetValue( "r_inGameVideo", 1 );
+ trap_Cvar_SetValue( "cg_shadows", 1 );
+ trap_Cvar_SetValue( "cg_bounceParticles", 1 );
+ trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
+ break;
+
+ case 1: // normal
+ trap_Cvar_SetValue( "r_subdivisions", 12 );
+ trap_Cvar_SetValue( "r_vertexlight", 0 );
+ trap_Cvar_SetValue( "r_lodbias", 0 );
+ trap_Cvar_SetValue( "r_colorbits", 0 );
+ trap_Cvar_SetValue( "r_depthbits", 24 );
+ trap_Cvar_SetValue( "r_picmip", 1 );
+ trap_Cvar_SetValue( "r_texturebits", 0 );
+ trap_Cvar_SetValue( "r_fastSky", 0 );
+ trap_Cvar_SetValue( "r_inGameVideo", 1 );
+ trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
+ trap_Cvar_SetValue( "cg_shadows", 0 );
+ trap_Cvar_SetValue( "cg_bounceParticles", 0 );
+ break;
+
+ case 2: // fast
+ trap_Cvar_SetValue( "r_subdivisions", 8 );
+ trap_Cvar_SetValue( "r_vertexlight", 0 );
+ trap_Cvar_SetValue( "r_lodbias", 1 );
+ trap_Cvar_SetValue( "r_colorbits", 0 );
+ trap_Cvar_SetValue( "r_depthbits", 0 );
+ trap_Cvar_SetValue( "r_picmip", 1 );
+ trap_Cvar_SetValue( "r_texturebits", 0 );
+ trap_Cvar_SetValue( "cg_shadows", 0 );
+ trap_Cvar_SetValue( "r_fastSky", 1 );
+ trap_Cvar_SetValue( "r_inGameVideo", 0 );
+ trap_Cvar_SetValue( "cg_bounceParticles", 0 );
+ trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" );
+ break;
+
+ case 3: // fastest
+ trap_Cvar_SetValue( "r_subdivisions", 20 );
+ trap_Cvar_SetValue( "r_vertexlight", 1 );
+ trap_Cvar_SetValue( "r_lodbias", 2 );
+ trap_Cvar_SetValue( "r_colorbits", 16 );
+ trap_Cvar_SetValue( "r_depthbits", 16 );
+ trap_Cvar_SetValue( "r_picmip", 2 );
+ trap_Cvar_SetValue( "r_texturebits", 16 );
+ trap_Cvar_SetValue( "cg_shadows", 0 );
+ trap_Cvar_SetValue( "r_fastSky", 1 );
+ trap_Cvar_SetValue( "r_inGameVideo", 0 );
+ trap_Cvar_SetValue( "cg_bounceParticles", 0 );
+ trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" );
+ break;
+ }
+ }
+ else if( Q_stricmp( name, "ui_mousePitch" ) == 0 )
+ {
+ if( val == 0 )
+ trap_Cvar_SetValue( "m_pitch", 0.022f );
+ else
+ trap_Cvar_SetValue( "m_pitch", -0.022f );
+ }
+}
+
+//FIXME: lookup table
+static void UI_RunMenuScript( char **args )
+{
+ const char *name, *name2;
+ char buff[1024];
+ const char *cmd;
+
+ if( String_Parse( args, &name ) )
+ {
+ if( Q_stricmp( name, "StartServer" ) == 0 )
+ {
+ trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, ui_dedicated.integer ) );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n",
+ uiInfo.mapList[ui_selectedMap.integer].mapLoadName ) );
+ }
+ else if( Q_stricmp( name, "resetDefaults" ) == 0 )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "exec default.cfg\n" );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "cvar_restart\n" );
+ Controls_SetDefaults();
+ trap_Cvar_Set( "com_introPlayed", "1" );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" );
+ }
+ else if( Q_stricmp( name, "loadArenas" ) == 0 )
+ {
+ UI_LoadArenas();
+ Menu_SetFeederSelection( NULL, FEEDER_MAPS, 0, "createserver" );
+ }
+ else if( Q_stricmp( name, "loadServerInfo" ) == 0 )
+ UI_ServerInfo();
+ else if( Q_stricmp( name, "getNews" ) == 0 )
+ UI_UpdateNews( qtrue );
+ else if( Q_stricmp( name, "saveControls" ) == 0 )
+ Controls_SetConfig( qtrue );
+ else if( Q_stricmp( name, "loadControls" ) == 0 )
+ Controls_GetConfig();
+ else if (Q_stricmp(name, "clearError") == 0) {
+ trap_Cvar_Set("com_errorMessage", "");
+ } else if (Q_stricmp(name, "downloadIgnore") == 0) {
+ trap_Cvar_Set("com_downloadPrompt", va("%d", DLP_IGNORE));
+ } else if (Q_stricmp(name, "downloadCURL") == 0) {
+ trap_Cvar_Set("com_downloadPrompt", va("%d", DLP_CURL));
+ } else if (Q_stricmp(name, "downloadUDP") == 0) {
+ trap_Cvar_Set("com_downloadPrompt", va("%d", DLP_UDP));
+ } else if (Q_stricmp(name, "RefreshServers") == 0) {
+ UI_StartServerRefresh(qtrue);
+ UI_BuildServerDisplayList(qtrue);
+ } else if (Q_stricmp(name, "InitServerList") == 0) {
+ int time = trap_RealTime( NULL );
+ int last;
+ int sortColumn;
+
+ // set up default sorting
+
+ if( !uiInfo.serverStatus.sorted && Int_Parse( args, &sortColumn ) )
+ {
+ uiInfo.serverStatus.sortKey = sortColumn;
+ uiInfo.serverStatus.sortDir = 0;
+ }
+
+ // refresh if older than 3 days or if list is empty
+ last = atoi( UI_Cvar_VariableString( va( "ui_lastServerRefresh_%i_time",
+ ui_netSource.integer ) ) );
+
+ if( trap_LAN_GetServerCount( ui_netSource.integer ) < 1 ||
+ ( time - last ) > 3600 )
+ {
+ UI_StartServerRefresh( qtrue );
+ UI_BuildServerDisplayList( qtrue );
+ }
+ }
+ else if( Q_stricmp( name, "RefreshFilter" ) == 0 )
+ {
+ UI_StartServerRefresh( qfalse );
+ UI_BuildServerDisplayList( qtrue );
+ }
+ else if( Q_stricmp( name, "LoadDemos" ) == 0 )
+ UI_LoadDemos();
+ else if( Q_stricmp( name, "LoadMovies" ) == 0 )
+ UI_LoadMovies();
+ else if( Q_stricmp( name, "LoadMods" ) == 0 )
+ UI_LoadMods();
+ else if( Q_stricmp( name, "LoadTeams" ) == 0 )
+ UI_LoadTeams( );
+ else if( Q_stricmp( name, "JoinTeam" ) == 0 )
+ {
+ if( ( cmd = uiInfo.teamList[ uiInfo.teamIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+ }
+ else if( Q_stricmp( name, "LoadHumanItems" ) == 0 )
+ UI_LoadHumanItems( );
+ else if( Q_stricmp( name, "SpawnWithHumanItem" ) == 0 )
+ {
+ if( ( cmd = uiInfo.humanItemList[ uiInfo.humanItemIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+ }
+ else if( Q_stricmp( name, "LoadAlienClasses" ) == 0 )
+ UI_LoadAlienClasses( );
+ else if( Q_stricmp( name, "SpawnAsAlienClass" ) == 0 )
+ {
+ if( ( cmd = uiInfo.alienClassList[ uiInfo.alienClassIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+ }
+ else if( Q_stricmp( name, "LoadHumanArmouryBuys" ) == 0 )
+ UI_LoadHumanArmouryBuys( );
+ else if( Q_stricmp( name, "BuyFromArmoury" ) == 0 )
+ {
+ if( ( cmd = uiInfo.humanArmouryBuyList[ uiInfo.humanArmouryBuyIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+
+ UI_InstallCaptureFunc( UI_ArmouryRefreshCb, NULL, 1000 );
+ }
+ else if( Q_stricmp( name, "LoadHumanArmourySells" ) == 0 )
+ UI_LoadHumanArmourySells( );
+ else if( Q_stricmp( name, "SellToArmoury" ) == 0 )
+ {
+ if( ( cmd = uiInfo.humanArmourySellList[ uiInfo.humanArmourySellIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+
+ UI_InstallCaptureFunc( UI_ArmouryRefreshCb, NULL, 1000 );
+ }
+ else if( Q_stricmp( name, "LoadAlienUpgrades" ) == 0 )
+ {
+ UI_LoadAlienUpgrades( );
+
+ //disallow the menu if it would be empty
+
+ if( uiInfo.alienUpgradeCount <= 0 )
+ Menus_CloseAll( );
+ }
+ else if( Q_stricmp( name, "UpgradeToNewClass" ) == 0 )
+ {
+ if( ( cmd = uiInfo.alienUpgradeList[ uiInfo.alienUpgradeIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+ }
+ else if( Q_stricmp( name, "LoadAlienBuilds" ) == 0 )
+ UI_LoadAlienBuilds( );
+ else if( Q_stricmp( name, "BuildAlienBuildable" ) == 0 )
+ {
+ if( ( cmd = uiInfo.alienBuildList[ uiInfo.alienBuildIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+ }
+ else if( Q_stricmp( name, "LoadHumanBuilds" ) == 0 )
+ UI_LoadHumanBuilds( );
+ else if( Q_stricmp( name, "BuildHumanBuildable" ) == 0 )
+ {
+ if( ( cmd = uiInfo.humanBuildList[ uiInfo.humanBuildIndex ].cmd ) )
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+ }
+ else if( Q_stricmp( name, "Say" ) == 0 )
+ {
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ trap_Cvar_VariableStringBuffer( "ui_sayBuffer", buffer, sizeof( buffer ) );
+
+ if( !buffer[ 0 ] )
+ ;
+ else if( ui_chatCommands.integer && ( buffer[ 0 ] == '/' ||
+ buffer[ 0 ] == '\\' ) )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "%s\n", buffer + 1 ) );
+ }
+ else if( uiInfo.chatTeam )
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "say_team \"^3%s\"\n", buffer ) );
+ else
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "say \"%s\"\n", buffer ) );
+ }
+ else if( Q_stricmp( name, "SayKeydown" ) == 0 )
+ {
+ if( ui_chatCommands.integer )
+ {
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ trap_Cvar_VariableStringBuffer( "ui_sayBuffer", buffer, sizeof( buffer ) );
+
+ if( buffer[ 0 ] == '/' || buffer[ 0 ] == '\\' )
+ Menus_ReplaceActiveByName( "say_command" );
+ else if( uiInfo.chatTeam )
+ Menus_ReplaceActiveByName( "say_team" );
+ else
+ Menus_ReplaceActiveByName( "say" );
+ }
+ }
+ else if( Q_stricmp( name, "playMovie" ) == 0 )
+ {
+ if( uiInfo.previewMovie >= 0 )
+ trap_CIN_StopCinematic( uiInfo.previewMovie );
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "cinematic %s.roq 2\n", uiInfo.movieList[uiInfo.movieIndex] ) );
+ }
+ else if( Q_stricmp( name, "RunMod" ) == 0 )
+ {
+ trap_Cvar_Set( "fs_game", uiInfo.modList[uiInfo.modIndex].modName );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" );
+ }
+ else if( Q_stricmp( name, "RunDemo" ) == 0 )
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "demo %s\n", uiInfo.demoList[uiInfo.demoIndex] ) );
+ else if( Q_stricmp( name, "Tremulous" ) == 0 )
+ {
+ trap_Cvar_Set( "fs_game", "" );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" );
+ }
+ else if( Q_stricmp( name, "closeJoin" ) == 0 )
+ {
+ if( uiInfo.serverStatus.refreshActive )
+ {
+ UI_StopServerRefresh();
+ uiInfo.serverStatus.nextDisplayRefresh = 0;
+ uiInfo.nextServerStatusRefresh = 0;
+ uiInfo.nextFindPlayerRefresh = 0;
+ UI_BuildServerDisplayList( qtrue );
+ }
+ else
+ {
+ Menus_CloseByName( "joinserver" );
+ Menus_ActivateByName( "main" );
+ }
+ }
+ else if( Q_stricmp( name, "StopRefresh" ) == 0 )
+ {
+ UI_StopServerRefresh();
+ uiInfo.serverStatus.nextDisplayRefresh = 0;
+ uiInfo.nextServerStatusRefresh = 0;
+ uiInfo.nextFindPlayerRefresh = 0;
+ }
+ else if( Q_stricmp( name, "UpdateFilter" ) == 0 )
+ {
+ if( ui_netSource.integer == AS_LOCAL )
+ UI_StartServerRefresh( qtrue );
+
+ UI_BuildServerDisplayList( qtrue );
+ UI_FeederSelection( FEEDER_SERVERS, 0 );
+ }
+ else if( Q_stricmp( name, "ServerStatus" ) == 0 )
+ {
+ trap_LAN_GetServerAddressString( ui_netSource.integer,
+ uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer],
+ uiInfo.serverStatusAddress, sizeof( uiInfo.serverStatusAddress ) );
+ UI_BuildServerStatus( qtrue );
+ }
+ else if( Q_stricmp( name, "FoundPlayerServerStatus" ) == 0 )
+ {
+ Q_strncpyz( uiInfo.serverStatusAddress,
+ uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer],
+ sizeof( uiInfo.serverStatusAddress ) );
+ UI_BuildServerStatus( qtrue );
+ Menu_SetFeederSelection( NULL, FEEDER_FINDPLAYER, 0, NULL );
+ }
+ else if( Q_stricmp( name, "FindPlayer" ) == 0 )
+ {
+ UI_BuildFindPlayerList( qtrue );
+ // clear the displayed server status info
+ uiInfo.serverStatusInfo.numLines = 0;
+ Menu_SetFeederSelection( NULL, FEEDER_FINDPLAYER, 0, NULL );
+ }
+ else if( Q_stricmp( name, "JoinServer" ) == 0 )
+ {
+ if( uiInfo.serverStatus.currentServer >= 0 &&
+ uiInfo.serverStatus.currentServer < uiInfo.serverStatus.numDisplayServers )
+ {
+ trap_LAN_GetServerAddressString( ui_netSource.integer,
+ uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer],
+ buff, 1024 );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", buff ) );
+ }
+ }
+ else if( Q_stricmp( name, "FoundPlayerJoinServer" ) == 0 )
+ {
+ if( uiInfo.currentFoundPlayerServer >= 0 &&
+ uiInfo.currentFoundPlayerServer < uiInfo.numFoundPlayerServers )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n",
+ uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer] ) );
+ }
+ }
+ else if( Q_stricmp( name, "Quit" ) == 0 )
+ trap_Cmd_ExecuteText( EXEC_APPEND, "quit" );
+ else if( Q_stricmp( name, "Leave" ) == 0 )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" );
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ Menus_CloseAll( );
+ Menus_ActivateByName( "main" );
+ }
+ else if( Q_stricmp( name, "ServerSort" ) == 0 )
+ {
+ int sortColumn;
+
+ if( Int_Parse( args, &sortColumn ) )
+ {
+ // if same column we're already sorting on then flip the direction
+
+ if( sortColumn == uiInfo.serverStatus.sortKey )
+ uiInfo.serverStatus.sortDir = !uiInfo.serverStatus.sortDir;
+
+ // make sure we sort again
+ UI_ServersSort( sortColumn, qtrue );
+
+ uiInfo.serverStatus.sorted = qtrue;
+ }
+ }
+ else if( Q_stricmp( name, "closeingame" ) == 0 )
+ {
+ trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
+ trap_Key_ClearStates();
+ trap_Cvar_Set( "cl_paused", "0" );
+ Menus_CloseAll( );
+ }
+ else if( Q_stricmp( name, "voteMap" ) == 0 )
+ {
+ if( ui_selectedMap.integer >= 0 && ui_selectedMap.integer < uiInfo.mapCount )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote map %s\n",
+ uiInfo.mapList[ui_selectedMap.integer].mapLoadName ) );
+ }
+ }
+ else if( Q_stricmp( name, "voteNextMap" ) == 0 )
+ {
+ if( ui_selectedMap.integer >= 0 && ui_selectedMap.integer < uiInfo.mapCount )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote nextmap %s\n",
+ uiInfo.mapList[ui_selectedMap.integer].mapLoadName ) );
+ }
+ }
+ else if( Q_stricmp( name, "voteKick" ) == 0 )
+ {
+ if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount )
+ {
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ trap_Cvar_VariableStringBuffer( "ui_reason", buffer, sizeof( buffer ) );
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote kick %d %s\n",
+ uiInfo.clientNums[ uiInfo.playerIndex ],
+ buffer ) );
+ trap_Cvar_Set( "ui_reason", "" );
+ }
+ }
+ else if( Q_stricmp( name, "voteMute" ) == 0 )
+ {
+ if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount )
+ {
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ trap_Cvar_VariableStringBuffer( "ui_reason", buffer, sizeof( buffer ) );
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote mute %d %s\n",
+ uiInfo.clientNums[ uiInfo.playerIndex ],
+ buffer ) );
+ trap_Cvar_Set( "ui_reason", "" );
+ }
+ }
+ else if( Q_stricmp( name, "voteUnMute" ) == 0 )
+ {
+ if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote unmute %d\n",
+ uiInfo.clientNums[ uiInfo.playerIndex ] ) );
+ }
+ }
+ else if( Q_stricmp( name, "voteTeamKick" ) == 0 )
+ {
+ if( uiInfo.teamPlayerIndex >= 0 && uiInfo.teamPlayerIndex < uiInfo.myTeamCount )
+ {
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ trap_Cvar_VariableStringBuffer( "ui_reason", buffer, sizeof( buffer ) );
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote kick %d %s\n",
+ uiInfo.teamClientNums[ uiInfo.teamPlayerIndex ],
+ buffer ) );
+ trap_Cvar_Set( "ui_reason", "" );
+ }
+ }
+ else if( Q_stricmp( name, "voteTeamDenyBuild" ) == 0 )
+ {
+ if( uiInfo.teamPlayerIndex >= 0 && uiInfo.teamPlayerIndex < uiInfo.myTeamCount )
+ {
+ char buffer[ MAX_CVAR_VALUE_STRING ];
+ trap_Cvar_VariableStringBuffer( "ui_reason", buffer, sizeof( buffer ) );
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote denybuild %d %s\n",
+ uiInfo.teamClientNums[ uiInfo.teamPlayerIndex ],
+ buffer ) );
+ trap_Cvar_Set( "ui_reason", "" );
+ }
+ }
+ else if( Q_stricmp( name, "voteTeamAllowBuild" ) == 0 )
+ {
+ if( uiInfo.teamPlayerIndex >= 0 && uiInfo.teamPlayerIndex < uiInfo.myTeamCount )
+ {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote allowbuild %d\n",
+ uiInfo.teamClientNums[ uiInfo.teamPlayerIndex ] ) );
+ }
+ }
+ else if( Q_stricmp( name, "addFavorite" ) == 0 )
+ {
+ if( ui_netSource.integer != AS_FAVORITES )
+ {
+ char name[MAX_NAME_LENGTH];
+ char addr[MAX_NAME_LENGTH];
+ int res;
+
+ trap_LAN_GetServerInfo( ui_netSource.integer,
+ uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer],
+ buff, MAX_STRING_CHARS );
+ name[0] = addr[0] = '\0';
+ Q_strncpyz( name, Info_ValueForKey( buff, "hostname" ), MAX_NAME_LENGTH );
+ Q_strncpyz( addr, Info_ValueForKey( buff, "addr" ), MAX_NAME_LENGTH );
+
+ if( strlen( name ) > 0 && strlen( addr ) > 0 )
+ {
+ res = trap_LAN_AddServer( AS_FAVORITES, name, addr );
+
+ if( res == 0 )
+ {
+ // server already in the list
+ Com_Printf( "Favorite already in list\n" );
+ }
+ else if( res == -1 )
+ {
+ // list full
+ Com_Printf( "Favorite list full\n" );
+ }
+ else
+ {
+ // successfully added
+ Com_Printf( "Added favorite server %s\n", addr );
+ }
+ }
+ }
+ }
+ else if( Q_stricmp( name, "deleteFavorite" ) == 0 )
+ {
+ if( ui_netSource.integer == AS_FAVORITES )
+ {
+ char addr[MAX_NAME_LENGTH];
+ trap_LAN_GetServerInfo( ui_netSource.integer,
+ uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer],
+ buff, MAX_STRING_CHARS );
+ addr[0] = '\0';
+ Q_strncpyz( addr, Info_ValueForKey( buff, "addr" ), MAX_NAME_LENGTH );
+
+ if( strlen( addr ) > 0 )
+ trap_LAN_RemoveServer( AS_FAVORITES, addr );
+ }
+ }
+ else if( Q_stricmp( name, "createFavorite" ) == 0 )
+ {
+ if( ui_netSource.integer == AS_FAVORITES )
+ {
+ char name[MAX_NAME_LENGTH];
+ char addr[MAX_NAME_LENGTH];
+ int res;
+
+ name[0] = addr[0] = '\0';
+ Q_strncpyz( name, UI_Cvar_VariableString( "ui_favoriteName" ), MAX_NAME_LENGTH );
+ Q_strncpyz( addr, UI_Cvar_VariableString( "ui_favoriteAddress" ), MAX_NAME_LENGTH );
+
+ if( strlen( name ) > 0 && strlen( addr ) > 0 )
+ {
+ res = trap_LAN_AddServer( AS_FAVORITES, name, addr );
+
+ if( res == 0 )
+ {
+ // server already in the list
+ Com_Printf( "Favorite already in list\n" );
+ }
+ else if( res == -1 )
+ {
+ // list full
+ Com_Printf( "Favorite list full\n" );
+ }
+ else
+ {
+ // successfully added
+ Com_Printf( "Added favorite server %s\n", addr );
+ }
+ }
+ }
+ }
+ else if( Q_stricmp( name, "glCustom" ) == 0 )
+ trap_Cvar_Set( "ui_glCustom", "4" );
+ else if( Q_stricmp( name, "update" ) == 0 )
+ {
+ if( String_Parse( args, &name2 ) )
+ UI_Update( name2 );
+ }
+ else if( Q_stricmp( name, "InitIgnoreList" ) == 0 )
+ UI_BuildPlayerList();
+ else if( Q_stricmp( name, "ToggleIgnore" ) == 0 )
+ {
+ if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount )
+ {
+ if( Com_ClientListContains( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] ) )
+ {
+ Com_ClientListRemove( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "unignore %i\n",
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
+ }
+ else
+ {
+ Com_ClientListAdd( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "ignore %i\n",
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
+ }
+ }
+ }
+ else if( Q_stricmp( name, "IgnorePlayer" ) == 0 )
+ {
+ if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount )
+ {
+ if( !Com_ClientListContains( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] ) )
+ {
+ Com_ClientListAdd( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "ignore %i\n",
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
+ }
+ }
+ }
+ else if( Q_stricmp( name, "UnIgnorePlayer" ) == 0 )
+ {
+ if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount )
+ {
+ if( Com_ClientListContains( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] ) )
+ {
+ Com_ClientListRemove( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "unignore %i\n",
+ uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
+ }
+ }
+ }
+ else
+ Com_Printf( "unknown UI script %s\n", name );
+ }
+}
+
+static int UI_FeederInitialise( int feederID );
+
+/*
+==================
+UI_FeederCount
+==================
+*/
+static int UI_FeederCount( int feederID )
+{
+ if( feederID == FEEDER_CINEMATICS )
+ return uiInfo.movieCount;
+ else if( feederID == FEEDER_MAPS )
+ return uiInfo.mapCount;
+ else if( feederID == FEEDER_SERVERS )
+ return uiInfo.serverStatus.numDisplayServers;
+ else if( feederID == FEEDER_SERVERSTATUS )
+ return uiInfo.serverStatusInfo.numLines;
+ else if( feederID == FEEDER_NEWS )
+ return uiInfo.newsInfo.numLines;
+ else if( feederID == FEEDER_FINDPLAYER )
+ return uiInfo.numFoundPlayerServers;
+ else if( feederID == FEEDER_PLAYER_LIST )
+ {
+ if( uiInfo.uiDC.realTime > uiInfo.playerRefresh )
+ {
+ uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
+ UI_BuildPlayerList();
+ }
+
+ return uiInfo.playerCount;
+ }
+ else if( feederID == FEEDER_TEAM_LIST )
+ {
+ if( uiInfo.uiDC.realTime > uiInfo.playerRefresh )
+ {
+ uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
+ UI_BuildPlayerList();
+ }
+
+ return uiInfo.myTeamCount;
+ }
+ else if( feederID == FEEDER_IGNORE_LIST )
+ return uiInfo.playerCount;
+ else if( feederID == FEEDER_HELP_LIST )
+ return uiInfo.helpCount;
+ else if( feederID == FEEDER_MODS )
+ return uiInfo.modCount;
+ else if( feederID == FEEDER_DEMOS )
+ return uiInfo.demoCount;
+ else if( feederID == FEEDER_TREMTEAMS )
+ return uiInfo.teamCount;
+ else if( feederID == FEEDER_TREMHUMANITEMS )
+ return uiInfo.humanItemCount;
+ else if( feederID == FEEDER_TREMALIENCLASSES )
+ return uiInfo.alienClassCount;
+ else if( feederID == FEEDER_TREMHUMANARMOURYBUY )
+ return uiInfo.humanArmouryBuyCount;
+ else if( feederID == FEEDER_TREMHUMANARMOURYSELL )
+ return uiInfo.humanArmourySellCount;
+ else if( feederID == FEEDER_TREMALIENUPGRADE )
+ return uiInfo.alienUpgradeCount;
+ else if( feederID == FEEDER_TREMALIENBUILD )
+ return uiInfo.alienBuildCount;
+ else if( feederID == FEEDER_TREMHUMANBUILD )
+ return uiInfo.humanBuildCount;
+ else if( feederID == FEEDER_RESOLUTIONS )
+ {
+ if( UI_FeederInitialise( feederID ) == uiInfo.numResolutions )
+ return uiInfo.numResolutions + 1;
+ else
+ return uiInfo.numResolutions;
+ }
+
+ return 0;
+}
+
+static const char *UI_SelectedMap( int index, int *actual )
+{
+ int i, c;
+ c = 0;
+ *actual = 0;
+
+ for( i = 0; i < uiInfo.mapCount; i++ )
+ {
+ if( c == index )
+ {
+ *actual = i;
+ return uiInfo.mapList[i].mapName;
+ }
+ else
+ c++;
+ }
+
+ return "";
+}
+
+static int GCD( int a, int b )
+{
+ int c;
+
+ while( b != 0 )
+ {
+ c = a % b;
+ a = b;
+ b = c;
+ }
+
+ return a;
+}
+
+static const char *UI_DisplayAspectString( int w, int h )
+{
+ int gcd = GCD( w, h );
+
+ w /= gcd;
+ h /= gcd;
+
+ // For some reason 8:5 is usually referred to as 16:10
+ if( w == 8 && h == 5 )
+ {
+ w = 16;
+ h = 10;
+ }
+
+ return va( "%d:%d", w, h );
+}
+
+static const char *UI_FeederItemText( int feederID, int index, int column, qhandle_t *handle )
+{
+ if( handle )
+ *handle = -1;
+
+ if( feederID == FEEDER_MAPS )
+ {
+ int actual;
+ return UI_SelectedMap( index, &actual );
+ }
+ else if( feederID == FEEDER_SERVERS )
+ {
+ if( index >= 0 && index < UI_FeederCount( feederID ) )
+ {
+ static char info[MAX_STRING_CHARS];
+ static char clientBuff[ 32 ];
+ static char cleaned[ MAX_STRING_CHARS ];
+ static int lastColumn = -1;
+ static int lastTime = 0;
+ int ping;
+
+ if( lastColumn != column || lastTime > uiInfo.uiDC.realTime + 5000 )
+ {
+ trap_LAN_GetServerInfo( ui_netSource.integer, uiInfo.serverStatus.displayServers[index],
+ info, MAX_STRING_CHARS );
+ lastColumn = column;
+ lastTime = uiInfo.uiDC.realTime;
+ }
+
+ ping = atoi( Info_ValueForKey( info, "ping" ) );
+
+ UI_EscapeEmoticons( cleaned, Info_ValueForKey( info, "hostname" ), sizeof( cleaned ) );
+
+ switch( column )
+ {
+ case SORT_HOST:
+ if( ping <= 0 )
+ return Info_ValueForKey( info, "addr" );
+ else
+ {
+ static char hostname[1024];
+
+ if( ui_netSource.integer == AS_LOCAL )
+ {
+ Com_sprintf( hostname, sizeof( hostname ), "%s [%s]", cleaned,
+ netnames[atoi( Info_ValueForKey( info, "nettype" ) )] );
+ return hostname;
+ }
+ else
+ {
+ char *text;
+ char *label;
+
+ label = Info_ValueForKey( info, "label" );
+ if( label[0] )
+ {
+ // First char of the label response is a sorting tag. Skip it.
+ label+= 1;
+
+ Com_sprintf( hostname, sizeof( hostname ), "%s %s",
+ label, cleaned );
+ }
+ else
+ {
+ Com_sprintf( hostname, sizeof( hostname ), "%s",
+ cleaned );
+ }
+
+ // Strip leading whitespace
+ text = hostname;
+
+ while( *text != '\0' && *text == ' ' )
+ text++;
+
+ return text;
+ }
+ }
+
+ case SORT_GAME:
+ return Info_ValueForKey( info, "game" );
+
+ case SORT_MAP:
+ return Info_ValueForKey( info, "mapname" );
+
+ case SORT_CLIENTS:
+ Com_sprintf( clientBuff, sizeof( clientBuff ), "%s (%s)",
+ Info_ValueForKey( info, "clients" ), Info_ValueForKey( info, "sv_maxclients" ) );
+ return clientBuff;
+
+ case SORT_PING:
+ if( ping <= 0 )
+ return "...";
+ else
+ return Info_ValueForKey( info, "ping" );
+ }
+ }
+ }
+ else if( feederID == FEEDER_SERVERSTATUS )
+ {
+ if( index >= 0 && index < uiInfo.serverStatusInfo.numLines )
+ {
+ if( column >= 0 && column < 4 )
+ return uiInfo.serverStatusInfo.lines[index][column];
+ }
+ }
+ else if( feederID == FEEDER_NEWS )
+ {
+ if( index >= 0 && index < uiInfo.newsInfo.numLines )
+ return uiInfo.newsInfo.text[index];
+ }
+ else if( feederID == FEEDER_FINDPLAYER )
+ {
+ if( index >= 0 && index < uiInfo.numFoundPlayerServers )
+ return uiInfo.foundPlayerServerNames[index];
+ }
+ else if( feederID == FEEDER_PLAYER_LIST )
+ {
+ if( index >= 0 && index < uiInfo.playerCount )
+ return uiInfo.playerNames[index];
+ }
+ else if( feederID == FEEDER_TEAM_LIST )
+ {
+ if( index >= 0 && index < uiInfo.myTeamCount )
+ return uiInfo.teamNames[index];
+ }
+ else if( feederID == FEEDER_IGNORE_LIST )
+ {
+ if( index >= 0 && index < uiInfo.playerCount )
+ {
+ switch( column )
+ {
+ case 1:
+ // am I ignoring him
+ return Com_ClientListContains( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
+ uiInfo.clientNums[ index ] ) ? "X" : "";
+
+ case 2:
+ // is he ignoring me
+ return Com_ClientListContains( &uiInfo.ignoreList[ index ],
+ uiInfo.playerNumber ) ? "X" : "";
+
+ default:
+ return uiInfo.playerNames[index];
+ }
+ }
+ }
+ else if( feederID == FEEDER_HELP_LIST )
+ {
+ if( index >= 0 && index < uiInfo.helpCount )
+ return uiInfo.helpList[ index ].text;
+ }
+ else if( feederID == FEEDER_MODS )
+ {
+ if( index >= 0 && index < uiInfo.modCount )
+ {
+ if( uiInfo.modList[index].modDescr && *uiInfo.modList[index].modDescr )
+ return uiInfo.modList[index].modDescr;
+ else
+ return uiInfo.modList[index].modName;
+ }
+ }
+ else if( feederID == FEEDER_CINEMATICS )
+ {
+ if( index >= 0 && index < uiInfo.movieCount )
+ return uiInfo.movieList[index];
+ }
+ else if( feederID == FEEDER_DEMOS )
+ {
+ if( index >= 0 && index < uiInfo.demoCount )
+ return uiInfo.demoList[index];
+ }
+ else if( feederID == FEEDER_TREMTEAMS )
+ {
+ if( index >= 0 && index < uiInfo.teamCount )
+ return uiInfo.teamList[ index ].text;
+ }
+ else if( feederID == FEEDER_TREMHUMANITEMS )
+ {
+ if( index >= 0 && index < uiInfo.humanItemCount )
+ return uiInfo.humanItemList[ index ].text;
+ }
+ else if( feederID == FEEDER_TREMALIENCLASSES )
+ {
+ if( index >= 0 && index < uiInfo.alienClassCount )
+ return uiInfo.alienClassList[ index ].text;
+ }
+ else if( feederID == FEEDER_TREMHUMANARMOURYBUY )
+ {
+ if( index >= 0 && index < uiInfo.humanArmouryBuyCount )
+ return uiInfo.humanArmouryBuyList[ index ].text;
+ }
+ else if( feederID == FEEDER_TREMHUMANARMOURYSELL )
+ {
+ if( index >= 0 && index < uiInfo.humanArmourySellCount )
+ return uiInfo.humanArmourySellList[ index ].text;
+ }
+ else if( feederID == FEEDER_TREMALIENUPGRADE )
+ {
+ if( index >= 0 && index < uiInfo.alienUpgradeCount )
+ return uiInfo.alienUpgradeList[ index ].text;
+ }
+ else if( feederID == FEEDER_TREMALIENBUILD )
+ {
+ if( index >= 0 && index < uiInfo.alienBuildCount )
+ return uiInfo.alienBuildList[ index ].text;
+ }
+ else if( feederID == FEEDER_TREMHUMANBUILD )
+ {
+ if( index >= 0 && index < uiInfo.humanBuildCount )
+ return uiInfo.humanBuildList[ index ].text;
+ }
+ else if( feederID == FEEDER_RESOLUTIONS )
+ {
+ static char resolution[MAX_STRING_CHARS];
+ int w, h;
+
+ if( index >= 0 && index < uiInfo.numResolutions )
+ {
+ w = uiInfo.resolutions[ index ].w;
+ h = uiInfo.resolutions[ index ].h;
+
+ Com_sprintf( resolution, sizeof( resolution ), "%dx%d (%s)", w, h,
+ UI_DisplayAspectString( w, h ) );
+
+ return resolution;
+ }
+
+ w = (int)trap_Cvar_VariableValue( "r_width" );
+ h = (int)trap_Cvar_VariableValue( "r_height" );
+ Com_sprintf( resolution, sizeof( resolution ), "Custom (%dx%d)", w, h );
+
+ return resolution;
+ }
+
+ return "";
+}
+
+
+static qhandle_t UI_FeederItemImage( int feederID, int index )
+{
+ if( feederID == FEEDER_MAPS )
+ {
+ int actual;
+ UI_SelectedMap( index, &actual );
+ index = actual;
+
+ if( index >= 0 && index < uiInfo.mapCount )
+ {
+ if( uiInfo.mapList[index].levelShot == -1 )
+ uiInfo.mapList[index].levelShot = trap_R_RegisterShaderNoMip( uiInfo.mapList[index].imageName );
+
+ return uiInfo.mapList[index].levelShot;
+ }
+ }
+
+ return 0;
+}
+
+static void UI_FeederSelection( int feederID, int index )
+{
+ static char info[MAX_STRING_CHARS];
+
+ if( feederID == FEEDER_MAPS )
+ {
+ int actual, map;
+ map = ui_selectedMap.integer;
+
+ if( uiInfo.mapList[map].cinematic >= 0 )
+ {
+ trap_CIN_StopCinematic( uiInfo.mapList[map].cinematic );
+ uiInfo.mapList[map].cinematic = -1;
+ }
+
+ UI_SelectedMap( index, &actual );
+
+ ui_selectedMap.integer = actual;
+ trap_Cvar_Set( "ui_selectedMap", va( "%d", actual ) );
+ uiInfo.mapList[ui_selectedMap.integer].cinematic =
+ trap_CIN_PlayCinematic( va( "%s.roq", uiInfo.mapList[ui_selectedMap.integer].mapLoadName ),
+ 0, 0, 0, 0, ( CIN_loop | CIN_silent ) );
+ }
+ else if( feederID == FEEDER_SERVERS )
+ {
+ const char *mapName = NULL;
+
+ uiInfo.serverStatus.currentServer = index;
+ trap_LAN_GetServerInfo( ui_netSource.integer, uiInfo.serverStatus.displayServers[index],
+ info, MAX_STRING_CHARS );
+ uiInfo.serverStatus.currentServerPreview =
+ trap_R_RegisterShaderNoMip( va( "levelshots/%s", Info_ValueForKey( info, "mapname" ) ) );
+
+ if( uiInfo.serverStatus.currentServerCinematic >= 0 )
+ {
+ trap_CIN_StopCinematic( uiInfo.serverStatus.currentServerCinematic );
+ uiInfo.serverStatus.currentServerCinematic = -1;
+ }
+
+ mapName = Info_ValueForKey( info, "mapname" );
+
+ if( mapName && *mapName )
+ {
+ uiInfo.serverStatus.currentServerCinematic =
+ trap_CIN_PlayCinematic( va( "%s.roq", mapName ), 0, 0, 0, 0, ( CIN_loop | CIN_silent ) );
+ }
+ }
+ else if( feederID == FEEDER_SERVERSTATUS )
+ {
+ }
+ else if( feederID == FEEDER_FINDPLAYER )
+ {
+ uiInfo.currentFoundPlayerServer = index;
+ //
+
+ if( index < uiInfo.numFoundPlayerServers - 1 )
+ {
+ // build a new server status for this server
+ Q_strncpyz( uiInfo.serverStatusAddress,
+ uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer],
+ sizeof( uiInfo.serverStatusAddress ) );
+ Menu_SetFeederSelection( NULL, FEEDER_SERVERSTATUS, 0, NULL );
+ UI_BuildServerStatus( qtrue );
+ }
+ }
+ else if( feederID == FEEDER_PLAYER_LIST )
+ uiInfo.playerIndex = index;
+ else if( feederID == FEEDER_TEAM_LIST )
+ uiInfo.teamPlayerIndex = index;
+ else if( feederID == FEEDER_IGNORE_LIST )
+ uiInfo.ignoreIndex = index;
+ else if( feederID == FEEDER_HELP_LIST )
+ uiInfo.helpIndex = index;
+ else if( feederID == FEEDER_MODS )
+ uiInfo.modIndex = index;
+ else if( feederID == FEEDER_CINEMATICS )
+ {
+ uiInfo.movieIndex = index;
+
+ if( uiInfo.previewMovie >= 0 )
+ trap_CIN_StopCinematic( uiInfo.previewMovie );
+
+ uiInfo.previewMovie = -1;
+ }
+ else if( feederID == FEEDER_DEMOS )
+ uiInfo.demoIndex = index;
+ else if( feederID == FEEDER_TREMTEAMS )
+ uiInfo.teamIndex = index;
+ else if( feederID == FEEDER_TREMHUMANITEMS )
+ uiInfo.humanItemIndex = index;
+ else if( feederID == FEEDER_TREMALIENCLASSES )
+ uiInfo.alienClassIndex = index;
+ else if( feederID == FEEDER_TREMHUMANARMOURYBUY )
+ uiInfo.humanArmouryBuyIndex = index;
+ else if( feederID == FEEDER_TREMHUMANARMOURYSELL )
+ uiInfo.humanArmourySellIndex = index;
+ else if( feederID == FEEDER_TREMALIENUPGRADE )
+ uiInfo.alienUpgradeIndex = index;
+ else if( feederID == FEEDER_TREMALIENBUILD )
+ uiInfo.alienBuildIndex = index;
+ else if( feederID == FEEDER_TREMHUMANBUILD )
+ uiInfo.humanBuildIndex = index;
+ else if( feederID == FEEDER_RESOLUTIONS )
+ {
+ if( index >= 0 && index < uiInfo.numResolutions )
+ {
+ trap_Cvar_Set( "r_width", va( "%d", uiInfo.resolutions[ index ].w ) );
+ trap_Cvar_Set( "r_height", va( "%d", uiInfo.resolutions[ index ].h ) );
+ }
+
+ uiInfo.resolutionIndex = index;
+ }
+}
+
+static int UI_FeederInitialise( int feederID )
+{
+ if( feederID == FEEDER_RESOLUTIONS )
+ {
+ int i;
+ int w = trap_Cvar_VariableValue( "r_width" );
+ int h = trap_Cvar_VariableValue( "r_height" );
+
+ for( i = 0; i < uiInfo.numResolutions; i++ )
+ {
+ if( w == uiInfo.resolutions[ i ].w && h == uiInfo.resolutions[ i ].h )
+ return i;
+ }
+
+ return uiInfo.numResolutions;
+ }
+
+ return 0;
+}
+
+static void UI_Pause( qboolean b )
+{
+ if( b )
+ {
+ // pause the game and set the ui keycatcher
+ trap_Cvar_Set( "cl_paused", "1" );
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ }
+ else
+ {
+ // unpause the game and clear the ui keycatcher
+ trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
+ trap_Key_ClearStates();
+ trap_Cvar_Set( "cl_paused", "0" );
+ }
+}
+
+static int UI_PlayCinematic( const char *name, float x, float y, float w, float h )
+{
+ return trap_CIN_PlayCinematic( name, x, y, w, h, ( CIN_loop | CIN_silent ) );
+}
+
+static void UI_StopCinematic( int handle )
+{
+ if( handle >= 0 )
+ trap_CIN_StopCinematic( handle );
+ else
+ {
+ handle = abs( handle );
+
+ if( handle == UI_NETMAPCINEMATIC )
+ {
+ if( uiInfo.serverStatus.currentServerCinematic >= 0 )
+ {
+ trap_CIN_StopCinematic( uiInfo.serverStatus.currentServerCinematic );
+ uiInfo.serverStatus.currentServerCinematic = -1;
+ }
+ }
+ }
+}
+
+static void UI_DrawCinematic( int handle, float x, float y, float w, float h )
+{
+ trap_CIN_SetExtents( handle, x, y, w, h );
+ trap_CIN_DrawCinematic( handle );
+}
+
+static void UI_RunCinematicFrame( int handle )
+{
+ trap_CIN_RunCinematic( handle );
+}
+
+static float UI_GetValue( int ownerDraw )
+{
+ return 0.0f;
+}
+
+/*
+=================
+UI_ParseResolutions
+=================
+*/
+void UI_ParseResolutions( void )
+{
+ char buf[ MAX_STRING_CHARS ];
+ char w[ 16 ], h[ 16 ];
+ char *p;
+ const char *out;
+ char *s = NULL;
+
+ trap_Cvar_VariableStringBuffer( "r_availableModes", buf, sizeof( buf ) );
+ p = buf;
+ uiInfo.numResolutions = 0;
+
+ while( String_Parse( &p, &out ) )
+ {
+ Q_strncpyz( w, out, sizeof( w ) );
+ s = strchr( w, 'x' );
+ if( !s )
+ return;
+
+ *s++ = '\0';
+ Q_strncpyz( h, s, sizeof( h ) );
+
+ uiInfo.resolutions[ uiInfo.numResolutions ].w = atoi( w );
+ uiInfo.resolutions[ uiInfo.numResolutions ].h = atoi( h );
+ uiInfo.numResolutions++;
+ }
+}
+
+/*
+=================
+UI_Init
+=================
+*/
+void UI_Init( qboolean inGameLoad )
+{
+ int start;
+
+ BG_InitClassConfigs( );
+ BG_InitAllowedGameElements( );
+
+ uiInfo.inGameLoad = inGameLoad;
+
+ UI_RegisterCvars();
+ UI_InitMemory();
+
+ // cache redundant calulations
+ trap_GetGlconfig( &uiInfo.uiDC.glconfig );
+
+ // for 640x480 virtualized screen
+ uiInfo.uiDC.yscale = uiInfo.uiDC.glconfig.vidHeight * ( 1.0f / 480.0f );
+ uiInfo.uiDC.xscale = uiInfo.uiDC.glconfig.vidWidth * ( 1.0f / 640.0f );
+
+ // wide screen
+ uiInfo.uiDC.aspectScale = ( ( 640.0f * uiInfo.uiDC.glconfig.vidHeight ) /
+ ( 480.0f * uiInfo.uiDC.glconfig.vidWidth ) );
+
+ uiInfo.uiDC.smallFontScale = trap_Cvar_VariableValue( "ui_smallFont" );
+ uiInfo.uiDC.bigFontScale = trap_Cvar_VariableValue( "ui_bigFont" );
+
+ uiInfo.uiDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip;
+ uiInfo.uiDC.setColor = &UI_SetColor;
+ uiInfo.uiDC.drawHandlePic = &UI_DrawHandlePic;
+ uiInfo.uiDC.drawStretchPic = &trap_R_DrawStretchPic;
+ uiInfo.uiDC.registerModel = &trap_R_RegisterModel;
+ uiInfo.uiDC.modelBounds = &trap_R_ModelBounds;
+ uiInfo.uiDC.fillRect = &UI_FillRect;
+ uiInfo.uiDC.drawRect = &UI_DrawRect;
+ uiInfo.uiDC.drawSides = &UI_DrawSides;
+ uiInfo.uiDC.drawTopBottom = &UI_DrawTopBottom;
+ uiInfo.uiDC.clearScene = &trap_R_ClearScene;
+ uiInfo.uiDC.drawSides = &UI_DrawSides;
+ uiInfo.uiDC.addRefEntityToScene = &trap_R_AddRefEntityToScene;
+ uiInfo.uiDC.renderScene = &trap_R_RenderScene;
+ uiInfo.uiDC.registerFont = &trap_R_RegisterFont;
+ uiInfo.uiDC.ownerDrawItem = &UI_OwnerDraw;
+ uiInfo.uiDC.getValue = &UI_GetValue;
+ uiInfo.uiDC.ownerDrawVisible = &UI_OwnerDrawVisible;
+ uiInfo.uiDC.runScript = &UI_RunMenuScript;
+ uiInfo.uiDC.setCVar = trap_Cvar_Set;
+ uiInfo.uiDC.getCVarString = trap_Cvar_VariableStringBuffer;
+ uiInfo.uiDC.getCVarValue = trap_Cvar_VariableValue;
+ uiInfo.uiDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode;
+ uiInfo.uiDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode;
+ uiInfo.uiDC.startLocalSound = &trap_S_StartLocalSound;
+ uiInfo.uiDC.ownerDrawHandleKey = &UI_OwnerDrawHandleKey;
+ uiInfo.uiDC.feederCount = &UI_FeederCount;
+ uiInfo.uiDC.feederItemImage = &UI_FeederItemImage;
+ uiInfo.uiDC.feederItemText = &UI_FeederItemText;
+ uiInfo.uiDC.feederSelection = &UI_FeederSelection;
+ uiInfo.uiDC.feederInitialise = &UI_FeederInitialise;
+ uiInfo.uiDC.setBinding = &trap_Key_SetBinding;
+ uiInfo.uiDC.getBindingBuf = &trap_Key_GetBindingBuf;
+ uiInfo.uiDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf;
+ uiInfo.uiDC.executeText = &trap_Cmd_ExecuteText;
+ uiInfo.uiDC.Error = &Com_Error;
+ uiInfo.uiDC.Print = &Com_Printf;
+ uiInfo.uiDC.Pause = &UI_Pause;
+ uiInfo.uiDC.ownerDrawWidth = &UI_OwnerDrawWidth;
+ uiInfo.uiDC.ownerDrawText = &UI_OwnerDrawText;
+ uiInfo.uiDC.registerSound = &trap_S_RegisterSound;
+ uiInfo.uiDC.startBackgroundTrack = &trap_S_StartBackgroundTrack;
+ uiInfo.uiDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack;
+ uiInfo.uiDC.playCinematic = &UI_PlayCinematic;
+ uiInfo.uiDC.stopCinematic = &UI_StopCinematic;
+ uiInfo.uiDC.drawCinematic = &UI_DrawCinematic;
+ uiInfo.uiDC.runCinematicFrame = &UI_RunCinematicFrame;
+
+ Init_Display( &uiInfo.uiDC );
+
+ String_Init();
+
+ uiInfo.uiDC.whiteShader = trap_R_RegisterShaderNoMip( "white" );
+
+ AssetCache();
+
+ start = trap_Milliseconds();
+
+ UI_LoadMenus( "ui/menus.txt", qtrue );
+ UI_LoadMenus( "ui/ingame.txt", qfalse );
+ UI_LoadMenus( "ui/tremulous.txt", qfalse );
+ UI_LoadHelp( "ui/help.txt" );
+
+ Menus_CloseAll( );
+
+ trap_LAN_LoadCachedServers();
+
+ // sets defaults for ui temp cvars
+ trap_Cvar_Set( "ui_mousePitch", ( trap_Cvar_VariableValue( "m_pitch" ) >= 0 ) ? "0" : "1" );
+
+ uiInfo.serverStatus.currentServerCinematic = -1;
+ uiInfo.previewMovie = -1;
+
+ UI_ParseResolutions( );
+}
+
+
+/*
+=================
+UI_KeyEvent
+=================
+*/
+void UI_KeyEvent( int key, qboolean down )
+{
+ if( Menu_Count() > 0 )
+ {
+ menuDef_t *menu = Menu_GetFocused();
+
+ if( menu )
+ {
+ if( key == K_ESCAPE && down && !Menus_AnyFullScreenVisible() )
+ Menus_CloseAll( );
+ else
+ Menu_HandleKey( menu, key, down );
+ }
+ else
+ {
+ trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
+ trap_Key_ClearStates();
+ trap_Cvar_Set( "cl_paused", "0" );
+ }
+ }
+}
+
+/*
+=================
+UI_MouseEvent
+=================
+*/
+void UI_MouseEvent( int dx, int dy )
+{
+ // update mouse screen position
+ uiInfo.uiDC.cursorx += ( dx * uiInfo.uiDC.aspectScale );
+
+ if( uiInfo.uiDC.cursorx < 0 )
+ uiInfo.uiDC.cursorx = 0;
+ else if( uiInfo.uiDC.cursorx > SCREEN_WIDTH )
+ uiInfo.uiDC.cursorx = SCREEN_WIDTH;
+
+ uiInfo.uiDC.cursory += dy;
+
+ if( uiInfo.uiDC.cursory < 0 )
+ uiInfo.uiDC.cursory = 0;
+ else if( uiInfo.uiDC.cursory > SCREEN_HEIGHT )
+ uiInfo.uiDC.cursory = SCREEN_HEIGHT;
+
+ if( Menu_Count( ) > 0 )
+ Display_MouseMove( NULL, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory );
+}
+
+/*
+=================
+UI_MousePosition
+=================
+*/
+int UI_MousePosition( void )
+{
+ return (int)rint( uiInfo.uiDC.cursorx ) |
+ (int)rint( uiInfo.uiDC.cursory ) << 16;
+}
+
+/*
+=================
+UI_SetMousePosition
+=================
+*/
+void UI_SetMousePosition( int x, int y )
+{
+ uiInfo.uiDC.cursorx = x;
+ uiInfo.uiDC.cursory = y;
+
+ if( Menu_Count( ) > 0 )
+ Display_MouseMove( NULL, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory );
+}
+
+void UI_SetActiveMenu( uiMenuCommand_t menu )
+{
+ char buf[256];
+
+ // this should be the ONLY way the menu system is brought up
+ // enusure minumum menu data is cached
+
+ if( Menu_Count() > 0 )
+ {
+ vec3_t v;
+ v[0] = v[1] = v[2] = 0;
+
+ switch( menu )
+ {
+ case UIMENU_NONE:
+ trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
+ trap_Key_ClearStates();
+ trap_Cvar_Set( "cl_paused", "0" );
+ Menus_CloseAll( );
+
+ return;
+
+ case UIMENU_MAIN:
+ trap_Cvar_Set( "sv_killserver", "1" );
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ Menus_CloseAll( );
+ Menus_ActivateByName( "main" );
+ trap_Cvar_VariableStringBuffer( "com_errorMessage", buf, sizeof( buf ) );
+
+ if( strlen( buf ) )
+ {
+ if( trap_Cvar_VariableValue( "com_errorCode" ) == ERR_SERVERDISCONNECT )
+ Menus_ActivateByName( "drop_popmenu" );
+ else
+ Menus_ActivateByName( "error_popmenu" );
+ }
+
+ return;
+
+ case UIMENU_INGAME:
+ trap_Cvar_Set( "cl_paused", "1" );
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ UI_BuildPlayerList();
+ Menus_CloseAll( );
+ Menus_ActivateByName( "ingame" );
+ return;
+ }
+ }
+}
+
+qboolean UI_IsFullscreen( void )
+{
+ return Menus_AnyFullScreenVisible( );
+}
+
+
+
+static connstate_t lastConnState;
+static char lastLoadingText[MAX_INFO_VALUE];
+
+static void UI_ReadableSize ( char *buf, int bufsize, int value )
+{
+ if( value > 1024 * 1024 * 1024 )
+ { // gigs
+ Com_sprintf( buf, bufsize, "%d", value / ( 1024 * 1024 * 1024 ) );
+ Com_sprintf( buf + strlen( buf ), bufsize - strlen( buf ), ".%02d GB",
+ ( value % ( 1024 * 1024 * 1024 ) ) * 100 / ( 1024 * 1024 * 1024 ) );
+ }
+ else if( value > 1024 * 1024 )
+ { // megs
+ Com_sprintf( buf, bufsize, "%d", value / ( 1024 * 1024 ) );
+ Com_sprintf( buf + strlen( buf ), bufsize - strlen( buf ), ".%02d MB",
+ ( value % ( 1024 * 1024 ) ) * 100 / ( 1024 * 1024 ) );
+ }
+ else if( value > 1024 )
+ { // kilos
+ Com_sprintf( buf, bufsize, "%d KB", value / 1024 );
+ }
+ else
+ { // bytes
+ Com_sprintf( buf, bufsize, "%d bytes", value );
+ }
+}
+
+// Assumes time is in msec
+static void UI_PrintTime ( char *buf, int bufsize, int time )
+{
+ time /= 1000; // change to seconds
+
+ if( time > 3600 )
+ { // in the hours range
+ Com_sprintf( buf, bufsize, "%d hr %d min", time / 3600, ( time % 3600 ) / 60 );
+ }
+ else if( time > 60 )
+ { // mins
+ Com_sprintf( buf, bufsize, "%d min %d sec", time / 60, time % 60 );
+ }
+ else
+ { // secs
+ Com_sprintf( buf, bufsize, "%d sec", time );
+ }
+}
+
+// FIXME: move to ui_shared.c?
+void Text_PaintCenter( float x, float y, float scale, vec4_t color, const char *text, float adjust )
+{
+ int len = UI_Text_Width( text, scale );
+ UI_Text_Paint( x - len / 2, y, scale, color, text, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE );
+}
+
+void Text_PaintCenter_AutoWrapped( float x, float y, float xmax, float ystep, float scale, vec4_t color, const char *str, float adjust )
+{
+ int width;
+ char *s1, *s2, *s3;
+ char c_bcp;
+ char buf[1024];
+
+ if( !str || str[0] == '\0' )
+ return;
+
+ Q_strncpyz( buf, str, sizeof( buf ) );
+
+ s1 = s2 = s3 = buf;
+
+ while( 1 )
+ {
+ do
+ s3++;
+ while( *s3 != ' ' && *s3 != '\0' );
+
+ c_bcp = *s3;
+
+ *s3 = '\0';
+
+ width = UI_Text_Width( s1, scale );
+
+ *s3 = c_bcp;
+
+ if( width > xmax )
+ {
+ if( s1 == s2 )
+ {
+ // fuck, don't have a clean cut, we'll overflow
+ s2 = s3;
+ }
+
+ *s2 = '\0';
+ Text_PaintCenter( x, y, scale, color, s1, adjust );
+ y += ystep;
+
+ if( c_bcp == '\0' )
+ {
+ // that was the last word
+ // we could start a new loop, but that wouldn't be much use
+ // even if the word is too long, we would overflow it (see above)
+ // so just print it now if needed
+ s2++;
+
+ if( *s2 != '\0' ) // if we are printing an overflowing line we have s2 == s3
+ Text_PaintCenter( x, y, scale, color, s2, adjust );
+
+ break;
+ }
+
+ s2++;
+ s1 = s2;
+ s3 = s2;
+ }
+ else
+ {
+ s2 = s3;
+
+ if( c_bcp == '\0' ) // we reached the end
+ {
+ Text_PaintCenter( x, y, scale, color, s1, adjust );
+ break;
+ }
+ }
+ }
+}
+
+
+static void UI_DisplayDownloadInfo( const char *downloadName, float centerPoint, float yStart, float scale )
+{
+ static char dlText[] = "Downloading:";
+ static char etaText[] = "Estimated time left:";
+ static char xferText[] = "Transfer rate:";
+
+ int downloadSize, downloadCount, downloadTime;
+ char dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64];
+ int xferRate;
+ int leftWidth;
+ const char *s;
+
+ downloadSize = trap_Cvar_VariableValue( "cl_downloadSize" );
+ downloadCount = trap_Cvar_VariableValue( "cl_downloadCount" );
+ downloadTime = trap_Cvar_VariableValue( "cl_downloadTime" );
+
+ leftWidth = 320;
+
+ UI_SetColor( colorWhite );
+ Text_PaintCenter( centerPoint, yStart + 112, scale, colorWhite, dlText, 0 );
+ Text_PaintCenter( centerPoint, yStart + 192, scale, colorWhite, etaText, 0 );
+ Text_PaintCenter( centerPoint, yStart + 248, scale, colorWhite, xferText, 0 );
+
+ if( downloadSize > 0 )
+ s = va( "%s (%d%%)", downloadName, ( int )( ( float )downloadCount * 100.0f / downloadSize ) );
+ else
+ s = downloadName;
+
+ Text_PaintCenter( centerPoint, yStart + 136, scale, colorWhite, s, 0 );
+
+ UI_ReadableSize( dlSizeBuf, sizeof dlSizeBuf, downloadCount );
+ UI_ReadableSize( totalSizeBuf, sizeof totalSizeBuf, downloadSize );
+
+ if( downloadCount < 4096 || !downloadTime )
+ {
+ Text_PaintCenter( leftWidth, yStart + 216, scale, colorWhite, "estimating", 0 );
+ Text_PaintCenter( leftWidth, yStart + 160, scale, colorWhite, va( "(%s of %s copied)", dlSizeBuf, totalSizeBuf ), 0 );
+ }
+ else
+ {
+ if( ( uiInfo.uiDC.realTime - downloadTime ) / 1000 )
+ xferRate = downloadCount / ( ( uiInfo.uiDC.realTime - downloadTime ) / 1000 );
+ else
+ xferRate = 0;
+
+ UI_ReadableSize( xferRateBuf, sizeof xferRateBuf, xferRate );
+
+ // Extrapolate estimated completion time
+
+ if( downloadSize && xferRate )
+ {
+ int n = downloadSize / xferRate; // estimated time for entire d/l in secs
+
+ // We do it in K (/1024) because we'd overflow around 4MB
+ UI_PrintTime ( dlTimeBuf, sizeof dlTimeBuf,
+ ( n - ( ( ( downloadCount / 1024 ) * n ) / ( downloadSize / 1024 ) ) ) * 1000 );
+
+ Text_PaintCenter( leftWidth, yStart + 216, scale, colorWhite, dlTimeBuf, 0 );
+ Text_PaintCenter( leftWidth, yStart + 160, scale, colorWhite, va( "(%s of %s copied)", dlSizeBuf, totalSizeBuf ), 0 );
+ }
+ else
+ {
+ Text_PaintCenter( leftWidth, yStart + 216, scale, colorWhite, "estimating", 0 );
+
+ if( downloadSize )
+ Text_PaintCenter( leftWidth, yStart + 160, scale, colorWhite, va( "(%s of %s copied)", dlSizeBuf, totalSizeBuf ), 0 );
+ else
+ Text_PaintCenter( leftWidth, yStart + 160, scale, colorWhite, va( "(%s copied)", dlSizeBuf ), 0 );
+ }
+
+ if( xferRate )
+ Text_PaintCenter( leftWidth, yStart + 272, scale, colorWhite, va( "%s/Sec", xferRateBuf ), 0 );
+ }
+}
+
+/*
+========================
+UI_DrawConnectScreen
+========================
+*/
+void UI_DrawConnectScreen( qboolean overlay )
+{
+ char *s;
+ uiClientState_t cstate;
+ char info[MAX_INFO_VALUE];
+ char text[256];
+ float centerPoint, yStart, scale;
+
+ menuDef_t *menu = Menus_FindByName( "Connect" );
+
+
+ if( !overlay && menu )
+ Menu_Paint( menu, qtrue );
+
+ if( !overlay )
+ {
+ centerPoint = 320;
+ yStart = 130;
+ scale = 0.5f;
+ }
+ else
+ {
+ centerPoint = 320;
+ yStart = 32;
+ scale = 0.6f;
+ return;
+ }
+
+ // see what information we should display
+ trap_GetClientState( &cstate );
+
+ info[0] = '\0';
+
+ if( trap_GetConfigString( CS_SERVERINFO, info, sizeof( info ) ) )
+ Text_PaintCenter( centerPoint, yStart, scale, colorWhite, va( "Loading %s", Info_ValueForKey( info, "mapname" ) ), 0 );
+
+ if( !Q_stricmp( cstate.servername, "localhost" ) )
+ Text_PaintCenter( centerPoint, yStart + 48, scale, colorWhite,
+ " ", ITEM_TEXTSTYLE_SHADOWEDMORE );
+ else
+ {
+ Com_sprintf( text, sizeof( text ), "Connecting to %s", cstate.servername );
+ Text_PaintCenter( centerPoint, yStart + 48, scale, colorWhite, text , ITEM_TEXTSTYLE_SHADOWEDMORE );
+ }
+
+
+ // display global MOTD at bottom
+ Text_PaintCenter( centerPoint, 600, scale, colorWhite, Info_ValueForKey( cstate.updateInfoString, "motd" ), 0 );
+
+ // print any server info (server full, bad version, etc)
+ if( cstate.connState < CA_CONNECTED )
+ Text_PaintCenter( centerPoint, yStart + 176, scale, colorWhite, cstate.messageString, 0 );
+
+ if( lastConnState > cstate.connState )
+ lastLoadingText[0] = '\0';
+
+ lastConnState = cstate.connState;
+
+ switch( cstate.connState )
+ {
+ case CA_CONNECTING:
+ s = va( "Awaiting connection...%i", cstate.connectPacketCount );
+ break;
+
+ case CA_CHALLENGING:
+ s = va( "Awaiting challenge...%i", cstate.connectPacketCount );
+ break;
+
+ case CA_CONNECTED:
+ {
+ char downloadName[MAX_INFO_VALUE];
+ int prompt = trap_Cvar_VariableValue( "com_downloadPrompt" );
+
+ if( prompt & DLP_SHOW ) {
+ Com_Printf("Opening download prompt...\n");
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ Menus_ActivateByName("download_popmenu");
+ trap_Cvar_Set( "com_downloadPrompt", "0" );
+ }
+
+ trap_Cvar_VariableStringBuffer( "cl_downloadName", downloadName, sizeof( downloadName ) );
+
+ if( *downloadName )
+ {
+ UI_DisplayDownloadInfo( downloadName, centerPoint, yStart, scale );
+ return;
+ }
+ }
+
+ s = "Awaiting gamestate...";
+ break;
+
+ case CA_LOADING:
+ return;
+
+ case CA_PRIMED:
+ return;
+
+ default:
+ return;
+ }
+
+
+ if( Q_stricmp( cstate.servername, "localhost" ) )
+ Text_PaintCenter( centerPoint, yStart + 80, scale, colorWhite, s, 0 );
+
+ // password required / connection rejected information goes here
+}
+
+/*
+=================
+UI_RegisterCvars
+=================
+*/
+void UI_RegisterCvars( void )
+{
+ int i;
+ cvarTable_t *cv;
+
+ for( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ )
+ trap_Cvar_Register( cv->vmCvar, cv->cvarName, cv->defaultString, cv->cvarFlags );
+}
+
+/*
+=================
+UI_UpdateCvars
+=================
+*/
+void UI_UpdateCvars( void )
+{
+ int i;
+ cvarTable_t *cv;
+
+ for( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ )
+ trap_Cvar_Update( cv->vmCvar );
+}
+
+/*
+=================
+UI_UpdateNews
+=================
+*/
+void UI_UpdateNews( qboolean begin )
+{
+ char newsString[ MAX_NEWS_STRING ];
+ const char *c;
+ const char *wrapped;
+ int line = 0;
+ int linePos = 0;
+ qboolean finished;
+
+ if( begin && !uiInfo.newsInfo.refreshActive ) {
+ uiInfo.newsInfo.refreshtime = uiInfo.uiDC.realTime + 10000;
+ uiInfo.newsInfo.refreshActive = qtrue;
+ }
+ else if( !uiInfo.newsInfo.refreshActive ) // do nothing
+ return;
+ else if( uiInfo.uiDC.realTime > uiInfo.newsInfo.refreshtime ) {
+ strcpy( uiInfo.newsInfo.text[ 0 ],
+ "^1Error: Timed out while contacting the server.");
+ uiInfo.newsInfo.numLines = 1;
+ return;
+ }
+
+ // start the news fetching
+ finished = trap_GetNews( begin );
+
+ // parse what comes back. Parse newlines and otherwise chop when necessary
+ trap_Cvar_VariableStringBuffer( "cl_newsString", newsString,
+ sizeof( newsString ) );
+
+ // FIXME remove magic width constant
+ wrapped = Item_Text_Wrap( newsString, 0.25f, 325 * uiInfo.uiDC.aspectScale );
+
+ for( c = wrapped; *c != '\0'; ++c ) {
+ if( linePos == (MAX_NEWS_LINEWIDTH - 1) || *c == '\n' ) {
+ uiInfo.newsInfo.text[ line ][ linePos ] = '\0';
+
+ if( line == ( MAX_NEWS_LINES - 1 ) )
+ break;
+
+ linePos = 0;
+ line++;
+
+ if( *c != '\n' ) {
+ uiInfo.newsInfo.text[ line ][ linePos ] = *c;
+ linePos++;
+ }
+ } else if( isprint( *c ) ) {
+ uiInfo.newsInfo.text[ line ][ linePos ] = *c;
+ linePos++;
+ }
+ }
+
+ uiInfo.newsInfo.text[ line ] [linePos ] = '\0';
+ uiInfo.newsInfo.numLines = line + 1;
+
+ if( finished )
+ uiInfo.newsInfo.refreshActive = qfalse;
+}
+