aboutsummaryrefslogtreecommitdiff
path: root/code/q3_ui
diff options
context:
space:
mode:
Diffstat (limited to 'code/q3_ui')
-rw-r--r--code/q3_ui/ui_addbots.c412
-rw-r--r--code/q3_ui/ui_atoms.c1261
-rw-r--r--code/q3_ui/ui_cdkey.c291
-rw-r--r--code/q3_ui/ui_cinematics.c350
-rw-r--r--code/q3_ui/ui_confirm.c293
-rw-r--r--code/q3_ui/ui_connect.c266
-rw-r--r--code/q3_ui/ui_controls2.c1664
-rw-r--r--code/q3_ui/ui_credits.c181
-rw-r--r--code/q3_ui/ui_demo2.c291
-rw-r--r--code/q3_ui/ui_display.c265
-rw-r--r--code/q3_ui/ui_gameinfo.c815
-rw-r--r--code/q3_ui/ui_ingame.c349
-rw-r--r--code/q3_ui/ui_loadconfig.c274
-rw-r--r--code/q3_ui/ui_local.h802
-rw-r--r--code/q3_ui/ui_login.c208
-rw-r--r--code/q3_ui/ui_main.c249
-rw-r--r--code/q3_ui/ui_menu.c419
-rw-r--r--code/q3_ui/ui_mfield.c439
-rw-r--r--code/q3_ui/ui_mods.c247
-rw-r--r--code/q3_ui/ui_network.c281
-rw-r--r--code/q3_ui/ui_options.c229
-rw-r--r--code/q3_ui/ui_playermodel.c731
-rw-r--r--code/q3_ui/ui_players.c1249
-rw-r--r--code/q3_ui/ui_playersettings.c513
-rw-r--r--code/q3_ui/ui_preferences.c419
-rw-r--r--code/q3_ui/ui_qmenu.c1745
-rw-r--r--code/q3_ui/ui_rankings.c420
-rw-r--r--code/q3_ui/ui_rankstatus.c209
-rw-r--r--code/q3_ui/ui_removebots.c342
-rw-r--r--code/q3_ui/ui_saveconfig.c212
-rw-r--r--code/q3_ui/ui_serverinfo.c273
-rw-r--r--code/q3_ui/ui_servers2.c1643
-rw-r--r--code/q3_ui/ui_setup.c327
-rw-r--r--code/q3_ui/ui_signup.c286
-rw-r--r--code/q3_ui/ui_sound.c316
-rw-r--r--code/q3_ui/ui_sparena.c50
-rw-r--r--code/q3_ui/ui_specifyleague.c333
-rw-r--r--code/q3_ui/ui_specifyserver.c213
-rw-r--r--code/q3_ui/ui_splevel.c1013
-rw-r--r--code/q3_ui/ui_sppostgame.c644
-rw-r--r--code/q3_ui/ui_spreset.c194
-rw-r--r--code/q3_ui/ui_spskill.c329
-rw-r--r--code/q3_ui/ui_startserver.c1976
-rw-r--r--code/q3_ui/ui_team.c200
-rw-r--r--code/q3_ui/ui_teamorders.c447
-rw-r--r--code/q3_ui/ui_video.c1289
46 files changed, 24959 insertions, 0 deletions
diff --git a/code/q3_ui/ui_addbots.c b/code/q3_ui/ui_addbots.c
new file mode 100644
index 0000000..8b9f448
--- /dev/null
+++ b/code/q3_ui/ui_addbots.c
@@ -0,0 +1,412 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+ADD BOTS MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FIGHT0 "menu/art/accept_0"
+#define ART_FIGHT1 "menu/art/accept_1"
+#define ART_BACKGROUND "menu/art/addbotframe"
+#define ART_ARROWS "menu/art/arrows_vert_0"
+#define ART_ARROWUP "menu/art/arrows_vert_top"
+#define ART_ARROWDOWN "menu/art/arrows_vert_bot"
+
+#define ID_BACK 10
+#define ID_GO 11
+#define ID_LIST 12
+#define ID_UP 13
+#define ID_DOWN 14
+#define ID_SKILL 15
+#define ID_TEAM 16
+#define ID_BOTNAME0 20
+#define ID_BOTNAME1 21
+#define ID_BOTNAME2 22
+#define ID_BOTNAME3 23
+#define ID_BOTNAME4 24
+#define ID_BOTNAME5 25
+#define ID_BOTNAME6 26
+
+
+typedef struct {
+ menuframework_s menu;
+ menubitmap_s arrows;
+ menubitmap_s up;
+ menubitmap_s down;
+ menutext_s bots[7];
+ menulist_s skill;
+ menulist_s team;
+ menubitmap_s go;
+ menubitmap_s back;
+
+ int numBots;
+ int delay;
+ int baseBotNum;
+ int selectedBotNum;
+ int sortedBotNums[MAX_BOTS];
+ char botnames[7][32];
+} addBotsMenuInfo_t;
+
+static addBotsMenuInfo_t addBotsMenuInfo;
+
+
+/*
+=================
+UI_AddBotsMenu_FightEvent
+=================
+*/
+static void UI_AddBotsMenu_FightEvent( void* ptr, int event ) {
+ const char *team;
+ int skill;
+
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ team = addBotsMenuInfo.team.itemnames[addBotsMenuInfo.team.curvalue];
+ skill = addBotsMenuInfo.skill.curvalue + 1;
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va("addbot %s %i %s %i\n",
+ addBotsMenuInfo.botnames[addBotsMenuInfo.selectedBotNum], skill, team, addBotsMenuInfo.delay) );
+
+ addBotsMenuInfo.delay += 1500;
+}
+
+
+/*
+=================
+UI_AddBotsMenu_BotEvent
+=================
+*/
+static void UI_AddBotsMenu_BotEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ addBotsMenuInfo.bots[addBotsMenuInfo.selectedBotNum].color = color_orange;
+ addBotsMenuInfo.selectedBotNum = ((menucommon_s*)ptr)->id - ID_BOTNAME0;
+ addBotsMenuInfo.bots[addBotsMenuInfo.selectedBotNum].color = color_white;
+}
+
+
+/*
+=================
+UI_AddBotsMenu_BackEvent
+=================
+*/
+static void UI_AddBotsMenu_BackEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+ UI_PopMenu();
+}
+
+
+/*
+=================
+UI_AddBotsMenu_SetBotNames
+=================
+*/
+static void UI_AddBotsMenu_SetBotNames( void ) {
+ int n;
+ const char *info;
+
+ for ( n = 0; n < 7; n++ ) {
+ info = UI_GetBotInfoByNumber( addBotsMenuInfo.sortedBotNums[addBotsMenuInfo.baseBotNum + n] );
+ Q_strncpyz( addBotsMenuInfo.botnames[n], Info_ValueForKey( info, "name" ), sizeof(addBotsMenuInfo.botnames[n]) );
+ }
+
+}
+
+
+/*
+=================
+UI_AddBotsMenu_UpEvent
+=================
+*/
+static void UI_AddBotsMenu_UpEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ if( addBotsMenuInfo.baseBotNum > 0 ) {
+ addBotsMenuInfo.baseBotNum--;
+ UI_AddBotsMenu_SetBotNames();
+ }
+}
+
+
+/*
+=================
+UI_AddBotsMenu_DownEvent
+=================
+*/
+static void UI_AddBotsMenu_DownEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ if( addBotsMenuInfo.baseBotNum + 7 < addBotsMenuInfo.numBots ) {
+ addBotsMenuInfo.baseBotNum++;
+ UI_AddBotsMenu_SetBotNames();
+ }
+}
+
+
+/*
+=================
+UI_AddBotsMenu_GetSortedBotNums
+=================
+*/
+static int QDECL UI_AddBotsMenu_SortCompare( const void *arg1, const void *arg2 ) {
+ int num1, num2;
+ const char *info1, *info2;
+ const char *name1, *name2;
+
+ num1 = *(int *)arg1;
+ num2 = *(int *)arg2;
+
+ info1 = UI_GetBotInfoByNumber( num1 );
+ info2 = UI_GetBotInfoByNumber( num2 );
+
+ name1 = Info_ValueForKey( info1, "name" );
+ name2 = Info_ValueForKey( info2, "name" );
+
+ return Q_stricmp( name1, name2 );
+}
+
+static void UI_AddBotsMenu_GetSortedBotNums( void ) {
+ int n;
+
+ // initialize the array
+ for( n = 0; n < addBotsMenuInfo.numBots; n++ ) {
+ addBotsMenuInfo.sortedBotNums[n] = n;
+ }
+
+ qsort( addBotsMenuInfo.sortedBotNums, addBotsMenuInfo.numBots, sizeof(addBotsMenuInfo.sortedBotNums[0]), UI_AddBotsMenu_SortCompare );
+}
+
+
+/*
+=================
+UI_AddBotsMenu_Draw
+=================
+*/
+static void UI_AddBotsMenu_Draw( void ) {
+ UI_DrawBannerString( 320, 16, "ADD BOTS", UI_CENTER, color_white );
+ UI_DrawNamedPic( 320-233, 240-166, 466, 332, ART_BACKGROUND );
+
+ // standard menu drawing
+ Menu_Draw( &addBotsMenuInfo.menu );
+}
+
+
+/*
+=================
+UI_AddBotsMenu_Init
+=================
+*/
+static const char *skillNames[] = {
+ "I Can Win",
+ "Bring It On",
+ "Hurt Me Plenty",
+ "Hardcore",
+ "Nightmare!",
+ NULL
+};
+
+static const char *teamNames1[] = {
+ "Free",
+ NULL
+};
+
+static const char *teamNames2[] = {
+ "Red",
+ "Blue",
+ NULL
+};
+
+static void UI_AddBotsMenu_Init( void ) {
+ int n;
+ int y;
+ int gametype;
+ int count;
+ char info[MAX_INFO_STRING];
+
+ trap_GetConfigString(CS_SERVERINFO, info, MAX_INFO_STRING);
+ gametype = atoi( Info_ValueForKey( info,"g_gametype" ) );
+
+ memset( &addBotsMenuInfo, 0 ,sizeof(addBotsMenuInfo) );
+ addBotsMenuInfo.menu.draw = UI_AddBotsMenu_Draw;
+ addBotsMenuInfo.menu.fullscreen = qfalse;
+ addBotsMenuInfo.menu.wrapAround = qtrue;
+ addBotsMenuInfo.delay = 1000;
+
+ UI_AddBots_Cache();
+
+ addBotsMenuInfo.numBots = UI_GetNumBots();
+ count = addBotsMenuInfo.numBots < 7 ? addBotsMenuInfo.numBots : 7;
+
+ addBotsMenuInfo.arrows.generic.type = MTYPE_BITMAP;
+ addBotsMenuInfo.arrows.generic.name = ART_ARROWS;
+ addBotsMenuInfo.arrows.generic.flags = QMF_INACTIVE;
+ addBotsMenuInfo.arrows.generic.x = 200;
+ addBotsMenuInfo.arrows.generic.y = 128;
+ addBotsMenuInfo.arrows.width = 64;
+ addBotsMenuInfo.arrows.height = 128;
+
+ addBotsMenuInfo.up.generic.type = MTYPE_BITMAP;
+ addBotsMenuInfo.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ addBotsMenuInfo.up.generic.x = 200;
+ addBotsMenuInfo.up.generic.y = 128;
+ addBotsMenuInfo.up.generic.id = ID_UP;
+ addBotsMenuInfo.up.generic.callback = UI_AddBotsMenu_UpEvent;
+ addBotsMenuInfo.up.width = 64;
+ addBotsMenuInfo.up.height = 64;
+ addBotsMenuInfo.up.focuspic = ART_ARROWUP;
+
+ addBotsMenuInfo.down.generic.type = MTYPE_BITMAP;
+ addBotsMenuInfo.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ addBotsMenuInfo.down.generic.x = 200;
+ addBotsMenuInfo.down.generic.y = 128+64;
+ addBotsMenuInfo.down.generic.id = ID_DOWN;
+ addBotsMenuInfo.down.generic.callback = UI_AddBotsMenu_DownEvent;
+ addBotsMenuInfo.down.width = 64;
+ addBotsMenuInfo.down.height = 64;
+ addBotsMenuInfo.down.focuspic = ART_ARROWDOWN;
+
+ for( n = 0, y = 120; n < count; n++, y += 20 ) {
+ addBotsMenuInfo.bots[n].generic.type = MTYPE_PTEXT;
+ addBotsMenuInfo.bots[n].generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ addBotsMenuInfo.bots[n].generic.id = ID_BOTNAME0 + n;
+ addBotsMenuInfo.bots[n].generic.x = 320 - 56;
+ addBotsMenuInfo.bots[n].generic.y = y;
+ addBotsMenuInfo.bots[n].generic.callback = UI_AddBotsMenu_BotEvent;
+ addBotsMenuInfo.bots[n].string = addBotsMenuInfo.botnames[n];
+ addBotsMenuInfo.bots[n].color = color_orange;
+ addBotsMenuInfo.bots[n].style = UI_LEFT|UI_SMALLFONT;
+ }
+
+ y += 12;
+ addBotsMenuInfo.skill.generic.type = MTYPE_SPINCONTROL;
+ addBotsMenuInfo.skill.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ addBotsMenuInfo.skill.generic.x = 320;
+ addBotsMenuInfo.skill.generic.y = y;
+ addBotsMenuInfo.skill.generic.name = "Skill:";
+ addBotsMenuInfo.skill.generic.id = ID_SKILL;
+ addBotsMenuInfo.skill.itemnames = skillNames;
+ addBotsMenuInfo.skill.curvalue = Com_Clamp( 0, 4, (int)trap_Cvar_VariableValue( "g_spSkill" ) - 1 );
+
+ y += SMALLCHAR_HEIGHT;
+ addBotsMenuInfo.team.generic.type = MTYPE_SPINCONTROL;
+ addBotsMenuInfo.team.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ addBotsMenuInfo.team.generic.x = 320;
+ addBotsMenuInfo.team.generic.y = y;
+ addBotsMenuInfo.team.generic.name = "Team: ";
+ addBotsMenuInfo.team.generic.id = ID_TEAM;
+ if( gametype >= GT_TEAM ) {
+ addBotsMenuInfo.team.itemnames = teamNames2;
+ }
+ else {
+ addBotsMenuInfo.team.itemnames = teamNames1;
+ addBotsMenuInfo.team.generic.flags = QMF_GRAYED;
+ }
+
+ addBotsMenuInfo.go.generic.type = MTYPE_BITMAP;
+ addBotsMenuInfo.go.generic.name = ART_FIGHT0;
+ addBotsMenuInfo.go.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ addBotsMenuInfo.go.generic.id = ID_GO;
+ addBotsMenuInfo.go.generic.callback = UI_AddBotsMenu_FightEvent;
+ addBotsMenuInfo.go.generic.x = 320+128-128;
+ addBotsMenuInfo.go.generic.y = 256+128-64;
+ addBotsMenuInfo.go.width = 128;
+ addBotsMenuInfo.go.height = 64;
+ addBotsMenuInfo.go.focuspic = ART_FIGHT1;
+
+ addBotsMenuInfo.back.generic.type = MTYPE_BITMAP;
+ addBotsMenuInfo.back.generic.name = ART_BACK0;
+ addBotsMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ addBotsMenuInfo.back.generic.id = ID_BACK;
+ addBotsMenuInfo.back.generic.callback = UI_AddBotsMenu_BackEvent;
+ addBotsMenuInfo.back.generic.x = 320-128;
+ addBotsMenuInfo.back.generic.y = 256+128-64;
+ addBotsMenuInfo.back.width = 128;
+ addBotsMenuInfo.back.height = 64;
+ addBotsMenuInfo.back.focuspic = ART_BACK1;
+
+ addBotsMenuInfo.baseBotNum = 0;
+ addBotsMenuInfo.selectedBotNum = 0;
+ addBotsMenuInfo.bots[0].color = color_white;
+
+ UI_AddBotsMenu_GetSortedBotNums();
+ UI_AddBotsMenu_SetBotNames();
+
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.arrows );
+
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.up );
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.down );
+ for( n = 0; n < count; n++ ) {
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.bots[n] );
+ }
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.skill );
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.team );
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.go );
+ Menu_AddItem( &addBotsMenuInfo.menu, &addBotsMenuInfo.back );
+}
+
+
+/*
+=================
+UI_AddBots_Cache
+=================
+*/
+void UI_AddBots_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT0 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT1 );
+ trap_R_RegisterShaderNoMip( ART_BACKGROUND );
+ trap_R_RegisterShaderNoMip( ART_ARROWS );
+ trap_R_RegisterShaderNoMip( ART_ARROWUP );
+ trap_R_RegisterShaderNoMip( ART_ARROWDOWN );
+}
+
+
+/*
+=================
+UI_AddBotsMenu
+=================
+*/
+void UI_AddBotsMenu( void ) {
+ UI_AddBotsMenu_Init();
+ UI_PushMenu( &addBotsMenuInfo.menu );
+}
diff --git a/code/q3_ui/ui_atoms.c b/code/q3_ui/ui_atoms.c
new file mode 100644
index 0000000..c798bdd
--- /dev/null
+++ b/code/q3_ui/ui_atoms.c
@@ -0,0 +1,1261 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/**********************************************************************
+ UI_ATOMS.C
+
+ User interface building blocks and support functions.
+**********************************************************************/
+#include "ui_local.h"
+
+uiStatic_t uis;
+qboolean m_entersound; // after a frame, so caching won't disrupt the sound
+
+void QDECL Com_Error( int level, const char *error, ... ) {
+ va_list argptr;
+ char text[1024];
+
+ va_start (argptr, error);
+ Q_vsnprintf (text, sizeof(text), error, argptr);
+ va_end (argptr);
+
+ trap_Error( va("%s", text) );
+}
+
+void QDECL Com_Printf( const char *msg, ... ) {
+ va_list argptr;
+ char text[1024];
+
+ va_start (argptr, msg);
+ Q_vsnprintf (text, sizeof(text), msg, argptr);
+ va_end (argptr);
+
+ trap_Print( va("%s", text) );
+}
+
+/*
+=================
+UI_ClampCvar
+=================
+*/
+float UI_ClampCvar( float min, float max, float value )
+{
+ if ( value < min ) return min;
+ if ( value > max ) return max;
+ return value;
+}
+
+/*
+=================
+UI_StartDemoLoop
+=================
+*/
+void UI_StartDemoLoop( void ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "d1\n" );
+}
+
+/*
+=================
+UI_PushMenu
+=================
+*/
+void UI_PushMenu( menuframework_s *menu )
+{
+ int i;
+ menucommon_s* item;
+
+ // avoid stacking menus invoked by hotkeys
+ for (i=0 ; i<uis.menusp ; i++)
+ {
+ if (uis.stack[i] == menu)
+ {
+ uis.menusp = i;
+ break;
+ }
+ }
+
+ if (i == uis.menusp)
+ {
+ if (uis.menusp >= MAX_MENUDEPTH)
+ trap_Error("UI_PushMenu: menu stack overflow");
+
+ uis.stack[uis.menusp++] = menu;
+ }
+
+ uis.activemenu = menu;
+
+ // default cursor position
+ menu->cursor = 0;
+ menu->cursor_prev = 0;
+
+ m_entersound = qtrue;
+
+ trap_Key_SetCatcher( KEYCATCH_UI );
+
+ // force first available item to have focus
+ for (i=0; i<menu->nitems; i++)
+ {
+ item = (menucommon_s *)menu->items[i];
+ if (!(item->flags & (QMF_GRAYED|QMF_MOUSEONLY|QMF_INACTIVE)))
+ {
+ menu->cursor_prev = -1;
+ Menu_SetCursor( menu, i );
+ break;
+ }
+ }
+
+ uis.firstdraw = qtrue;
+}
+
+/*
+=================
+UI_PopMenu
+=================
+*/
+void UI_PopMenu (void)
+{
+ trap_S_StartLocalSound( menu_out_sound, CHAN_LOCAL_SOUND );
+
+ uis.menusp--;
+
+ if (uis.menusp < 0)
+ trap_Error ("UI_PopMenu: menu stack underflow");
+
+ if (uis.menusp) {
+ uis.activemenu = uis.stack[uis.menusp-1];
+ uis.firstdraw = qtrue;
+ }
+ else {
+ UI_ForceMenuOff ();
+ }
+}
+
+void UI_ForceMenuOff (void)
+{
+ uis.menusp = 0;
+ uis.activemenu = NULL;
+
+ trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
+ trap_Key_ClearStates();
+ trap_Cvar_Set( "cl_paused", "0" );
+}
+
+/*
+=================
+UI_LerpColor
+=================
+*/
+void UI_LerpColor(vec4_t a, vec4_t b, vec4_t c, float t)
+{
+ int i;
+
+ // lerp and clamp each component
+ for (i=0; i<4; i++)
+ {
+ c[i] = a[i] + t*(b[i]-a[i]);
+ if (c[i] < 0)
+ c[i] = 0;
+ else if (c[i] > 1.0)
+ c[i] = 1.0;
+ }
+}
+
+/*
+=================
+UI_DrawProportionalString2
+=================
+*/
+static int propMap[128][3] = {
+{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
+{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
+
+{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
+{0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1}, {0, 0, -1},
+
+{0, 0, PROP_SPACE_WIDTH}, // SPACE
+{11, 122, 7}, // !
+{154, 181, 14}, // "
+{55, 122, 17}, // #
+{79, 122, 18}, // $
+{101, 122, 23}, // %
+{153, 122, 18}, // &
+{9, 93, 7}, // '
+{207, 122, 8}, // (
+{230, 122, 9}, // )
+{177, 122, 18}, // *
+{30, 152, 18}, // +
+{85, 181, 7}, // ,
+{34, 93, 11}, // -
+{110, 181, 6}, // .
+{130, 152, 14}, // /
+
+{22, 64, 17}, // 0
+{41, 64, 12}, // 1
+{58, 64, 17}, // 2
+{78, 64, 18}, // 3
+{98, 64, 19}, // 4
+{120, 64, 18}, // 5
+{141, 64, 18}, // 6
+{204, 64, 16}, // 7
+{162, 64, 17}, // 8
+{182, 64, 18}, // 9
+{59, 181, 7}, // :
+{35,181, 7}, // ;
+{203, 152, 14}, // <
+{56, 93, 14}, // =
+{228, 152, 14}, // >
+{177, 181, 18}, // ?
+
+{28, 122, 22}, // @
+{5, 4, 18}, // A
+{27, 4, 18}, // B
+{48, 4, 18}, // C
+{69, 4, 17}, // D
+{90, 4, 13}, // E
+{106, 4, 13}, // F
+{121, 4, 18}, // G
+{143, 4, 17}, // H
+{164, 4, 8}, // I
+{175, 4, 16}, // J
+{195, 4, 18}, // K
+{216, 4, 12}, // L
+{230, 4, 23}, // M
+{6, 34, 18}, // N
+{27, 34, 18}, // O
+
+{48, 34, 18}, // P
+{68, 34, 18}, // Q
+{90, 34, 17}, // R
+{110, 34, 18}, // S
+{130, 34, 14}, // T
+{146, 34, 18}, // U
+{166, 34, 19}, // V
+{185, 34, 29}, // W
+{215, 34, 18}, // X
+{234, 34, 18}, // Y
+{5, 64, 14}, // Z
+{60, 152, 7}, // [
+{106, 151, 13}, // '\'
+{83, 152, 7}, // ]
+{128, 122, 17}, // ^
+{4, 152, 21}, // _
+
+{134, 181, 5}, // '
+{5, 4, 18}, // A
+{27, 4, 18}, // B
+{48, 4, 18}, // C
+{69, 4, 17}, // D
+{90, 4, 13}, // E
+{106, 4, 13}, // F
+{121, 4, 18}, // G
+{143, 4, 17}, // H
+{164, 4, 8}, // I
+{175, 4, 16}, // J
+{195, 4, 18}, // K
+{216, 4, 12}, // L
+{230, 4, 23}, // M
+{6, 34, 18}, // N
+{27, 34, 18}, // O
+
+{48, 34, 18}, // P
+{68, 34, 18}, // Q
+{90, 34, 17}, // R
+{110, 34, 18}, // S
+{130, 34, 14}, // T
+{146, 34, 18}, // U
+{166, 34, 19}, // V
+{185, 34, 29}, // W
+{215, 34, 18}, // X
+{234, 34, 18}, // Y
+{5, 64, 14}, // Z
+{153, 152, 13}, // {
+{11, 181, 5}, // |
+{180, 152, 13}, // }
+{79, 93, 17}, // ~
+{0, 0, -1} // DEL
+};
+
+static int propMapB[26][3] = {
+{11, 12, 33},
+{49, 12, 31},
+{85, 12, 31},
+{120, 12, 30},
+{156, 12, 21},
+{183, 12, 21},
+{207, 12, 32},
+
+{13, 55, 30},
+{49, 55, 13},
+{66, 55, 29},
+{101, 55, 31},
+{135, 55, 21},
+{158, 55, 40},
+{204, 55, 32},
+
+{12, 97, 31},
+{48, 97, 31},
+{82, 97, 30},
+{118, 97, 30},
+{153, 97, 30},
+{185, 97, 25},
+{213, 97, 30},
+
+{11, 139, 32},
+{42, 139, 51},
+{93, 139, 32},
+{126, 139, 31},
+{158, 139, 25},
+};
+
+#define PROPB_GAP_WIDTH 4
+#define PROPB_SPACE_WIDTH 12
+#define PROPB_HEIGHT 36
+
+/*
+=================
+UI_DrawBannerString
+=================
+*/
+static void UI_DrawBannerString2( int x, int y, const char* str, vec4_t color )
+{
+ const char* s;
+ unsigned char ch;
+ float ax;
+ float ay;
+ float aw;
+ float ah;
+ float frow;
+ float fcol;
+ float fwidth;
+ float fheight;
+
+ // draw the colored text
+ trap_R_SetColor( color );
+
+ ax = x * uis.xscale + uis.bias;
+ ay = y * uis.yscale;
+
+ s = str;
+ while ( *s )
+ {
+ ch = *s & 127;
+ if ( ch == ' ' ) {
+ ax += ((float)PROPB_SPACE_WIDTH + (float)PROPB_GAP_WIDTH)* uis.xscale;
+ }
+ else if ( ch >= 'A' && ch <= 'Z' ) {
+ ch -= 'A';
+ fcol = (float)propMapB[ch][0] / 256.0f;
+ frow = (float)propMapB[ch][1] / 256.0f;
+ fwidth = (float)propMapB[ch][2] / 256.0f;
+ fheight = (float)PROPB_HEIGHT / 256.0f;
+ aw = (float)propMapB[ch][2] * uis.xscale;
+ ah = (float)PROPB_HEIGHT * uis.yscale;
+ trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, uis.charsetPropB );
+ ax += (aw + (float)PROPB_GAP_WIDTH * uis.xscale);
+ }
+ s++;
+ }
+
+ trap_R_SetColor( NULL );
+}
+
+void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color ) {
+ const char * s;
+ int ch;
+ int width;
+ vec4_t drawcolor;
+
+ // find the width of the drawn text
+ s = str;
+ width = 0;
+ while ( *s ) {
+ ch = *s;
+ if ( ch == ' ' ) {
+ width += PROPB_SPACE_WIDTH;
+ }
+ else if ( ch >= 'A' && ch <= 'Z' ) {
+ width += propMapB[ch - 'A'][2] + PROPB_GAP_WIDTH;
+ }
+ s++;
+ }
+ width -= PROPB_GAP_WIDTH;
+
+ switch( style & UI_FORMATMASK ) {
+ case UI_CENTER:
+ x -= width / 2;
+ break;
+
+ case UI_RIGHT:
+ x -= width;
+ break;
+
+ case UI_LEFT:
+ default:
+ break;
+ }
+
+ if ( style & UI_DROPSHADOW ) {
+ drawcolor[0] = drawcolor[1] = drawcolor[2] = 0;
+ drawcolor[3] = color[3];
+ UI_DrawBannerString2( x+2, y+2, str, drawcolor );
+ }
+
+ UI_DrawBannerString2( x, y, str, color );
+}
+
+
+int UI_ProportionalStringWidth( const char* str ) {
+ const char * s;
+ int ch;
+ int charWidth;
+ int width;
+
+ s = str;
+ width = 0;
+ while ( *s ) {
+ ch = *s & 127;
+ charWidth = propMap[ch][2];
+ if ( charWidth != -1 ) {
+ width += charWidth;
+ width += PROP_GAP_WIDTH;
+ }
+ s++;
+ }
+
+ width -= PROP_GAP_WIDTH;
+ return width;
+}
+
+static void UI_DrawProportionalString2( int x, int y, const char* str, vec4_t color, float sizeScale, qhandle_t charset )
+{
+ const char* s;
+ unsigned char ch;
+ float ax;
+ float ay;
+ float aw = 0;
+ float ah;
+ float frow;
+ float fcol;
+ float fwidth;
+ float fheight;
+
+ // draw the colored text
+ trap_R_SetColor( color );
+
+ ax = x * uis.xscale + uis.bias;
+ ay = y * uis.yscale;
+
+ s = str;
+ while ( *s )
+ {
+ ch = *s & 127;
+ if ( ch == ' ' ) {
+ aw = (float)PROP_SPACE_WIDTH * uis.xscale * sizeScale;
+ }
+ else if ( propMap[ch][2] != -1 ) {
+ fcol = (float)propMap[ch][0] / 256.0f;
+ frow = (float)propMap[ch][1] / 256.0f;
+ fwidth = (float)propMap[ch][2] / 256.0f;
+ fheight = (float)PROP_HEIGHT / 256.0f;
+ aw = (float)propMap[ch][2] * uis.xscale * sizeScale;
+ ah = (float)PROP_HEIGHT * uis.yscale * sizeScale;
+ trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol+fwidth, frow+fheight, charset );
+ }
+
+ ax += (aw + (float)PROP_GAP_WIDTH * uis.xscale * sizeScale);
+ s++;
+ }
+
+ trap_R_SetColor( NULL );
+}
+
+/*
+=================
+UI_ProportionalSizeScale
+=================
+*/
+float UI_ProportionalSizeScale( int style ) {
+ if( style & UI_SMALLFONT ) {
+ return PROP_SMALL_SIZE_SCALE;
+ }
+
+ return 1.00;
+}
+
+
+/*
+=================
+UI_DrawProportionalString
+=================
+*/
+void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color ) {
+ vec4_t drawcolor;
+ int width;
+ float sizeScale;
+
+ sizeScale = UI_ProportionalSizeScale( style );
+
+ switch( style & UI_FORMATMASK ) {
+ case UI_CENTER:
+ width = UI_ProportionalStringWidth( str ) * sizeScale;
+ x -= width / 2;
+ break;
+
+ case UI_RIGHT:
+ width = UI_ProportionalStringWidth( str ) * sizeScale;
+ x -= width;
+ break;
+
+ case UI_LEFT:
+ default:
+ break;
+ }
+
+ if ( style & UI_DROPSHADOW ) {
+ drawcolor[0] = drawcolor[1] = drawcolor[2] = 0;
+ drawcolor[3] = color[3];
+ UI_DrawProportionalString2( x+2, y+2, str, drawcolor, sizeScale, uis.charsetProp );
+ }
+
+ if ( style & UI_INVERSE ) {
+ drawcolor[0] = color[0] * 0.7;
+ drawcolor[1] = color[1] * 0.7;
+ drawcolor[2] = color[2] * 0.7;
+ drawcolor[3] = color[3];
+ UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, uis.charsetProp );
+ return;
+ }
+
+ if ( style & UI_PULSE ) {
+ drawcolor[0] = color[0] * 0.7;
+ drawcolor[1] = color[1] * 0.7;
+ drawcolor[2] = color[2] * 0.7;
+ drawcolor[3] = color[3];
+ UI_DrawProportionalString2( x, y, str, color, sizeScale, uis.charsetProp );
+
+ drawcolor[0] = color[0];
+ drawcolor[1] = color[1];
+ drawcolor[2] = color[2];
+ drawcolor[3] = 0.5 + 0.5 * sin( uis.realtime / PULSE_DIVISOR );
+ UI_DrawProportionalString2( x, y, str, drawcolor, sizeScale, uis.charsetPropGlow );
+ return;
+ }
+
+ UI_DrawProportionalString2( x, y, str, color, sizeScale, uis.charsetProp );
+}
+
+/*
+=================
+UI_DrawProportionalString_Wrapped
+=================
+*/
+void UI_DrawProportionalString_AutoWrapped( int x, int y, int xmax, int ystep, const char* str, int style, vec4_t color ) {
+ int width;
+ char *s1,*s2,*s3;
+ char c_bcp;
+ char buf[1024];
+ float sizeScale;
+
+ if (!str || str[0]=='\0')
+ return;
+
+ sizeScale = UI_ProportionalSizeScale( style );
+
+ 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_ProportionalStringWidth(s1) * sizeScale;
+ *s3 = c_bcp;
+ if (width > xmax) {
+ if (s1==s2)
+ {
+ // fuck, don't have a clean cut, we'll overflow
+ s2 = s3;
+ }
+ *s2 = '\0';
+ UI_DrawProportionalString(x, y, s1, style, color);
+ 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
+ UI_DrawProportionalString(x, y, s2, style, color);
+ break;
+ }
+ s2++;
+ s1 = s2;
+ s3 = s2;
+ }
+ else
+ {
+ s2 = s3;
+ if (c_bcp == '\0') // we reached the end
+ {
+ UI_DrawProportionalString(x, y, s1, style, color);
+ break;
+ }
+ }
+ }
+}
+
+/*
+=================
+UI_DrawString2
+=================
+*/
+static void UI_DrawString2( int x, int y, const char* str, vec4_t color, int charw, int charh )
+{
+ const char* s;
+ char ch;
+ int forceColor = qfalse; //APSFIXME;
+ vec4_t tempcolor;
+ float ax;
+ float ay;
+ float aw;
+ float ah;
+ float frow;
+ float fcol;
+
+ if (y < -charh)
+ // offscreen
+ return;
+
+ // draw the colored text
+ trap_R_SetColor( color );
+
+ ax = x * uis.xscale + uis.bias;
+ ay = y * uis.yscale;
+ aw = charw * uis.xscale;
+ ah = charh * uis.yscale;
+
+ s = str;
+ while ( *s )
+ {
+ if ( Q_IsColorString( s ) )
+ {
+ if ( !forceColor )
+ {
+ memcpy( tempcolor, g_color_table[ColorIndex(s[1])], sizeof( tempcolor ) );
+ tempcolor[3] = color[3];
+ trap_R_SetColor( tempcolor );
+ }
+ s += 2;
+ continue;
+ }
+
+ ch = *s & 255;
+ if (ch != ' ')
+ {
+ frow = (ch>>4)*0.0625;
+ fcol = (ch&15)*0.0625;
+ trap_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + 0.0625, frow + 0.0625, uis.charset );
+ }
+
+ ax += aw;
+ s++;
+ }
+
+ trap_R_SetColor( NULL );
+}
+
+/*
+=================
+UI_DrawString
+=================
+*/
+void UI_DrawString( int x, int y, const char* str, int style, vec4_t color )
+{
+ int len;
+ int charw;
+ int charh;
+ vec4_t newcolor;
+ vec4_t lowlight;
+ float *drawcolor;
+ vec4_t dropcolor;
+
+ if( !str ) {
+ return;
+ }
+
+ if ((style & UI_BLINK) && ((uis.realtime/BLINK_DIVISOR) & 1))
+ return;
+
+ if (style & UI_SMALLFONT)
+ {
+ charw = SMALLCHAR_WIDTH;
+ charh = SMALLCHAR_HEIGHT;
+ }
+ else if (style & UI_GIANTFONT)
+ {
+ charw = GIANTCHAR_WIDTH;
+ charh = GIANTCHAR_HEIGHT;
+ }
+ else
+ {
+ charw = BIGCHAR_WIDTH;
+ charh = BIGCHAR_HEIGHT;
+ }
+
+ if (style & UI_PULSE)
+ {
+ lowlight[0] = 0.8*color[0];
+ lowlight[1] = 0.8*color[1];
+ lowlight[2] = 0.8*color[2];
+ lowlight[3] = 0.8*color[3];
+ UI_LerpColor(color,lowlight,newcolor,0.5+0.5*sin(uis.realtime/PULSE_DIVISOR));
+ drawcolor = newcolor;
+ }
+ else
+ drawcolor = color;
+
+ switch (style & UI_FORMATMASK)
+ {
+ case UI_CENTER:
+ // center justify at x
+ len = strlen(str);
+ x = x - len*charw/2;
+ break;
+
+ case UI_RIGHT:
+ // right justify at x
+ len = strlen(str);
+ x = x - len*charw;
+ break;
+
+ default:
+ // left justify at x
+ break;
+ }
+
+ if ( style & UI_DROPSHADOW )
+ {
+ dropcolor[0] = dropcolor[1] = dropcolor[2] = 0;
+ dropcolor[3] = drawcolor[3];
+ UI_DrawString2(x+2,y+2,str,dropcolor,charw,charh);
+ }
+
+ UI_DrawString2(x,y,str,drawcolor,charw,charh);
+}
+
+/*
+=================
+UI_DrawChar
+=================
+*/
+void UI_DrawChar( int x, int y, int ch, int style, vec4_t color )
+{
+ char buff[2];
+
+ buff[0] = ch;
+ buff[1] = '\0';
+
+ UI_DrawString( x, y, buff, style, color );
+}
+
+qboolean UI_IsFullscreen( void ) {
+ if ( uis.activemenu && ( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
+ return uis.activemenu->fullscreen;
+ }
+
+ return qfalse;
+}
+
+static void NeedCDAction( qboolean result ) {
+ if ( !result ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
+ }
+}
+
+static void NeedCDKeyAction( qboolean result ) {
+ if ( !result ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
+ }
+}
+
+void UI_SetActiveMenu( uiMenuCommand_t menu ) {
+ // this should be the ONLY way the menu system is brought up
+ // enusure minumum menu data is cached
+ Menu_Cache();
+
+ switch ( menu ) {
+ case UIMENU_NONE:
+ UI_ForceMenuOff();
+ return;
+ case UIMENU_MAIN:
+ UI_MainMenu();
+ return;
+ case UIMENU_NEED_CD:
+ UI_ConfirmMenu( "Insert the CD", 0, NeedCDAction );
+ return;
+ case UIMENU_BAD_CD_KEY:
+ UI_ConfirmMenu( "Bad CD Key", 0, NeedCDKeyAction );
+ return;
+ case UIMENU_INGAME:
+ /*
+ //GRank
+ UI_RankingsMenu();
+ return;
+ */
+ trap_Cvar_Set( "cl_paused", "1" );
+ UI_InGameMenu();
+ return;
+
+ case UIMENU_TEAM:
+ case UIMENU_POSTGAME:
+ default:
+#ifndef NDEBUG
+ Com_Printf("UI_SetActiveMenu: bad enum %d\n", menu );
+#endif
+ break;
+ }
+}
+
+/*
+=================
+UI_KeyEvent
+=================
+*/
+void UI_KeyEvent( int key, int down ) {
+ sfxHandle_t s;
+
+ if (!uis.activemenu) {
+ return;
+ }
+
+ if (!down) {
+ return;
+ }
+
+ if (uis.activemenu->key)
+ s = uis.activemenu->key( key );
+ else
+ s = Menu_DefaultKey( uis.activemenu, key );
+
+ if ((s > 0) && (s != menu_null_sound))
+ trap_S_StartLocalSound( s, CHAN_LOCAL_SOUND );
+}
+
+/*
+=================
+UI_MouseEvent
+=================
+*/
+void UI_MouseEvent( int dx, int dy )
+{
+ int i;
+ menucommon_s* m;
+
+ if (!uis.activemenu)
+ return;
+
+ // update mouse screen position
+ uis.cursorx += dx;
+ if (uis.cursorx < 0)
+ uis.cursorx = 0;
+ else if (uis.cursorx > SCREEN_WIDTH)
+ uis.cursorx = SCREEN_WIDTH;
+
+ uis.cursory += dy;
+ if (uis.cursory < 0)
+ uis.cursory = 0;
+ else if (uis.cursory > SCREEN_HEIGHT)
+ uis.cursory = SCREEN_HEIGHT;
+
+ // region test the active menu items
+ for (i=0; i<uis.activemenu->nitems; i++)
+ {
+ m = (menucommon_s*)uis.activemenu->items[i];
+
+ if (m->flags & (QMF_GRAYED|QMF_INACTIVE))
+ continue;
+
+ if ((uis.cursorx < m->left) ||
+ (uis.cursorx > m->right) ||
+ (uis.cursory < m->top) ||
+ (uis.cursory > m->bottom))
+ {
+ // cursor out of item bounds
+ continue;
+ }
+
+ // set focus to item at cursor
+ if (uis.activemenu->cursor != i)
+ {
+ Menu_SetCursor( uis.activemenu, i );
+ ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor_prev]))->flags &= ~QMF_HASMOUSEFOCUS;
+
+ if ( !(((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags & QMF_SILENT ) ) {
+ trap_S_StartLocalSound( menu_move_sound, CHAN_LOCAL_SOUND );
+ }
+ }
+
+ ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags |= QMF_HASMOUSEFOCUS;
+ return;
+ }
+
+ if (uis.activemenu->nitems > 0) {
+ // out of any region
+ ((menucommon_s*)(uis.activemenu->items[uis.activemenu->cursor]))->flags &= ~QMF_HASMOUSEFOCUS;
+ }
+}
+
+char *UI_Argv( int arg ) {
+ static char buffer[MAX_STRING_CHARS];
+
+ trap_Argv( arg, buffer, sizeof( buffer ) );
+
+ return buffer;
+}
+
+
+char *UI_Cvar_VariableString( const char *var_name ) {
+ static char buffer[MAX_STRING_CHARS];
+
+ trap_Cvar_VariableStringBuffer( var_name, buffer, sizeof( buffer ) );
+
+ return buffer;
+}
+
+
+/*
+=================
+UI_Cache
+=================
+*/
+void UI_Cache_f( void ) {
+ MainMenu_Cache();
+ InGame_Cache();
+ ConfirmMenu_Cache();
+ PlayerModel_Cache();
+ PlayerSettings_Cache();
+ Controls_Cache();
+ Demos_Cache();
+ UI_CinematicsMenu_Cache();
+ Preferences_Cache();
+ ServerInfo_Cache();
+ SpecifyServer_Cache();
+ ArenaServers_Cache();
+ StartServer_Cache();
+ ServerOptions_Cache();
+ DriverInfo_Cache();
+ GraphicsOptions_Cache();
+ UI_DisplayOptionsMenu_Cache();
+ UI_SoundOptionsMenu_Cache();
+ UI_NetworkOptionsMenu_Cache();
+ UI_SPLevelMenu_Cache();
+ UI_SPSkillMenu_Cache();
+ UI_SPPostgameMenu_Cache();
+ TeamMain_Cache();
+ UI_AddBots_Cache();
+ UI_RemoveBots_Cache();
+ UI_SetupMenu_Cache();
+// UI_LoadConfig_Cache();
+// UI_SaveConfigMenu_Cache();
+ UI_BotSelectMenu_Cache();
+ UI_CDKeyMenu_Cache();
+ UI_ModsMenu_Cache();
+
+}
+
+
+/*
+=================
+UI_ConsoleCommand
+=================
+*/
+qboolean UI_ConsoleCommand( int realTime ) {
+ char *cmd;
+
+ cmd = UI_Argv( 0 );
+
+ // ensure minimum menu data is available
+ Menu_Cache();
+
+ if ( Q_stricmp (cmd, "levelselect") == 0 ) {
+ UI_SPLevelMenu_f();
+ return qtrue;
+ }
+
+ if ( Q_stricmp (cmd, "postgame") == 0 ) {
+ UI_SPPostgameMenu_f();
+ return qtrue;
+ }
+
+ if ( Q_stricmp (cmd, "ui_cache") == 0 ) {
+ UI_Cache_f();
+ return qtrue;
+ }
+
+ if ( Q_stricmp (cmd, "ui_cinematics") == 0 ) {
+ UI_CinematicsMenu_f();
+ return qtrue;
+ }
+
+ if ( Q_stricmp (cmd, "ui_teamOrders") == 0 ) {
+ UI_TeamOrdersMenu_f();
+ return qtrue;
+ }
+
+ if ( Q_stricmp (cmd, "iamacheater") == 0 ) {
+ UI_SPUnlock_f();
+ return qtrue;
+ }
+
+ if ( Q_stricmp (cmd, "iamamonkey") == 0 ) {
+ UI_SPUnlockMedals_f();
+ return qtrue;
+ }
+
+ if ( Q_stricmp (cmd, "ui_cdkey") == 0 ) {
+ UI_CDKeyMenu_f();
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+=================
+UI_Shutdown
+=================
+*/
+void UI_Shutdown( void ) {
+}
+
+/*
+=================
+UI_Init
+=================
+*/
+void UI_Init( void ) {
+ UI_RegisterCvars();
+
+ UI_InitGameinfo();
+
+ // cache redundant calulations
+ trap_GetGlconfig( &uis.glconfig );
+
+ // for 640x480 virtualized screen
+ uis.xscale = uis.glconfig.vidWidth * (1.0/640.0);
+ uis.yscale = uis.glconfig.vidHeight * (1.0/480.0);
+ if ( uis.glconfig.vidWidth * 480 > uis.glconfig.vidHeight * 640 ) {
+ // wide screen
+ uis.bias = 0.5 * ( uis.glconfig.vidWidth - ( uis.glconfig.vidHeight * (640.0/480.0) ) );
+ uis.xscale = uis.yscale;
+ }
+ else {
+ // no wide screen
+ uis.bias = 0;
+ }
+
+ // initialize the menu system
+ Menu_Cache();
+
+ uis.activemenu = NULL;
+ uis.menusp = 0;
+}
+
+/*
+================
+UI_AdjustFrom640
+
+Adjusted for resolution and screen aspect ratio
+================
+*/
+void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) {
+ // expect valid pointers
+ *x = *x * uis.xscale + uis.bias;
+ *y *= uis.yscale;
+ *w *= uis.xscale;
+ *h *= uis.yscale;
+}
+
+void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
+ qhandle_t hShader;
+
+ hShader = trap_R_RegisterShaderNoMip( picname );
+ UI_AdjustFrom640( &x, &y, &width, &height );
+ trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
+}
+
+void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) {
+ float s0;
+ float s1;
+ float t0;
+ float t1;
+
+ if( w < 0 ) { // flip about vertical
+ w = -w;
+ s0 = 1;
+ s1 = 0;
+ }
+ else {
+ s0 = 0;
+ s1 = 1;
+ }
+
+ if( h < 0 ) { // flip about horizontal
+ h = -h;
+ t0 = 1;
+ t1 = 0;
+ }
+ else {
+ t0 = 0;
+ t1 = 1;
+ }
+
+ UI_AdjustFrom640( &x, &y, &w, &h );
+ trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, hShader );
+}
+
+/*
+================
+UI_FillRect
+
+Coordinates are 640*480 virtual values
+=================
+*/
+void UI_FillRect( float x, float y, float width, float height, const float *color ) {
+ trap_R_SetColor( color );
+
+ UI_AdjustFrom640( &x, &y, &width, &height );
+ trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, uis.whiteShader );
+
+ trap_R_SetColor( NULL );
+}
+
+/*
+================
+UI_DrawRect
+
+Coordinates are 640*480 virtual values
+=================
+*/
+void UI_DrawRect( float x, float y, float width, float height, const float *color ) {
+ trap_R_SetColor( color );
+
+ UI_AdjustFrom640( &x, &y, &width, &height );
+
+ trap_R_DrawStretchPic( x, y, width, 1, 0, 0, 0, 0, uis.whiteShader );
+ trap_R_DrawStretchPic( x, y, 1, height, 0, 0, 0, 0, uis.whiteShader );
+ trap_R_DrawStretchPic( x, y + height - 1, width, 1, 0, 0, 0, 0, uis.whiteShader );
+ trap_R_DrawStretchPic( x + width - 1, y, 1, height, 0, 0, 0, 0, uis.whiteShader );
+
+ trap_R_SetColor( NULL );
+}
+
+void UI_SetColor( const float *rgba ) {
+ trap_R_SetColor( rgba );
+}
+
+void UI_UpdateScreen( void ) {
+ trap_UpdateScreen();
+}
+
+/*
+=================
+UI_Refresh
+=================
+*/
+void UI_Refresh( int realtime )
+{
+ uis.frametime = realtime - uis.realtime;
+ uis.realtime = realtime;
+
+ if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
+ return;
+ }
+
+ UI_UpdateCvars();
+
+ if ( uis.activemenu )
+ {
+ if (uis.activemenu->fullscreen)
+ {
+ // draw the background
+ if( uis.activemenu->showlogo ) {
+ UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackShader );
+ }
+ else {
+ UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackNoLogoShader );
+ }
+ }
+
+ if (uis.activemenu->draw)
+ uis.activemenu->draw();
+ else
+ Menu_Draw( uis.activemenu );
+
+ if( uis.firstdraw ) {
+ UI_MouseEvent( 0, 0 );
+ uis.firstdraw = qfalse;
+ }
+ }
+
+ // draw cursor
+ UI_SetColor( NULL );
+ UI_DrawHandlePic( uis.cursorx-16, uis.cursory-16, 32, 32, uis.cursor);
+
+#ifndef NDEBUG
+ if (uis.debug)
+ {
+ // cursor coordinates
+ UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed );
+ }
+#endif
+
+ // delay playing the enter sound until after the
+ // menu has been drawn, to avoid delay while
+ // caching images
+ if (m_entersound)
+ {
+ trap_S_StartLocalSound( menu_in_sound, CHAN_LOCAL_SOUND );
+ m_entersound = qfalse;
+ }
+}
+
+void UI_DrawTextBox (int x, int y, int width, int lines)
+{
+ UI_FillRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorBlack );
+ UI_DrawRect( x + BIGCHAR_WIDTH/2, y + BIGCHAR_HEIGHT/2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorWhite );
+}
+
+qboolean UI_CursorInRect (int x, int y, int width, int height)
+{
+ if (uis.cursorx < x ||
+ uis.cursory < y ||
+ uis.cursorx > x+width ||
+ uis.cursory > y+height)
+ return qfalse;
+
+ return qtrue;
+}
diff --git a/code/q3_ui/ui_cdkey.c b/code/q3_ui/ui_cdkey.c
new file mode 100644
index 0000000..ec3029b
--- /dev/null
+++ b/code/q3_ui/ui_cdkey.c
@@ -0,0 +1,291 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+CD KEY MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ART_FRAME "menu/art/cut_frame"
+#define ART_ACCEPT0 "menu/art/accept_0"
+#define ART_ACCEPT1 "menu/art/accept_1"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+
+#define ID_CDKEY 10
+#define ID_ACCEPT 11
+#define ID_BACK 12
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s frame;
+
+ menufield_s cdkey;
+
+ menubitmap_s accept;
+ menubitmap_s back;
+} cdkeyMenuInfo_t;
+
+static cdkeyMenuInfo_t cdkeyMenuInfo;
+
+
+/*
+===============
+UI_CDKeyMenu_Event
+===============
+*/
+static void UI_CDKeyMenu_Event( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_ACCEPT:
+ if( cdkeyMenuInfo.cdkey.field.buffer[0] ) {
+ trap_SetCDKey( cdkeyMenuInfo.cdkey.field.buffer );
+ }
+ UI_PopMenu();
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+=================
+UI_CDKeyMenu_PreValidateKey
+=================
+*/
+static int UI_CDKeyMenu_PreValidateKey( const char *key ) {
+ char ch;
+
+ if( strlen( key ) != 16 ) {
+ return 1;
+ }
+
+ while( ( ch = *key++ ) ) {
+ switch( ch ) {
+ case '2':
+ case '3':
+ case '7':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'g':
+ case 'h':
+ case 'j':
+ case 'l':
+ case 'p':
+ case 'r':
+ case 's':
+ case 't':
+ case 'w':
+ continue;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+=================
+UI_CDKeyMenu_DrawKey
+=================
+*/
+static void UI_CDKeyMenu_DrawKey( void *self ) {
+ menufield_s *f;
+ qboolean focus;
+ int style;
+ char c;
+ float *color;
+ int x, y;
+ int val;
+
+ f = (menufield_s *)self;
+
+ focus = (f->generic.parent->cursor == f->generic.menuPosition);
+
+ style = UI_LEFT;
+ if( focus ) {
+ color = color_yellow;
+ }
+ else {
+ color = color_orange;
+ }
+
+ x = 320 - 8 * BIGCHAR_WIDTH;
+ y = 240 - BIGCHAR_HEIGHT / 2;
+ UI_FillRect( x, y, 16 * BIGCHAR_WIDTH, BIGCHAR_HEIGHT, listbar_color );
+ UI_DrawString( x, y, f->field.buffer, style, color );
+
+ // draw cursor if we have focus
+ if( focus ) {
+ if ( trap_Key_GetOverstrikeMode() ) {
+ c = 11;
+ } else {
+ c = 10;
+ }
+
+ style &= ~UI_PULSE;
+ style |= UI_BLINK;
+
+ UI_DrawChar( x + f->field.cursor * BIGCHAR_WIDTH, y, c, style, color_white );
+ }
+
+ val = UI_CDKeyMenu_PreValidateKey( f->field.buffer );
+ if( val == 1 ) {
+ UI_DrawProportionalString( 320, 376, "Please enter your CD Key", UI_CENTER|UI_SMALLFONT, color_yellow );
+ }
+ else if ( val == 0 ) {
+ UI_DrawProportionalString( 320, 376, "The CD Key appears to be valid, thank you", UI_CENTER|UI_SMALLFONT, color_white );
+ }
+ else {
+ UI_DrawProportionalString( 320, 376, "The CD Key is not valid", UI_CENTER|UI_SMALLFONT, color_red );
+ }
+}
+
+
+/*
+===============
+UI_CDKeyMenu_Init
+===============
+*/
+static void UI_CDKeyMenu_Init( void ) {
+ trap_Cvar_Set( "ui_cdkeychecked", "1" );
+
+ UI_CDKeyMenu_Cache();
+
+ memset( &cdkeyMenuInfo, 0, sizeof(cdkeyMenuInfo) );
+ cdkeyMenuInfo.menu.wrapAround = qtrue;
+ cdkeyMenuInfo.menu.fullscreen = qtrue;
+
+ cdkeyMenuInfo.banner.generic.type = MTYPE_BTEXT;
+ cdkeyMenuInfo.banner.generic.x = 320;
+ cdkeyMenuInfo.banner.generic.y = 16;
+ cdkeyMenuInfo.banner.string = "CD KEY";
+ cdkeyMenuInfo.banner.color = color_white;
+ cdkeyMenuInfo.banner.style = UI_CENTER;
+
+ cdkeyMenuInfo.frame.generic.type = MTYPE_BITMAP;
+ cdkeyMenuInfo.frame.generic.name = ART_FRAME;
+ cdkeyMenuInfo.frame.generic.flags = QMF_INACTIVE;
+ cdkeyMenuInfo.frame.generic.x = 142;
+ cdkeyMenuInfo.frame.generic.y = 118;
+ cdkeyMenuInfo.frame.width = 359;
+ cdkeyMenuInfo.frame.height = 256;
+
+ cdkeyMenuInfo.cdkey.generic.type = MTYPE_FIELD;
+ cdkeyMenuInfo.cdkey.generic.name = "CD Key:";
+ cdkeyMenuInfo.cdkey.generic.flags = QMF_LOWERCASE;
+ cdkeyMenuInfo.cdkey.generic.x = 320 - BIGCHAR_WIDTH * 2.5;
+ cdkeyMenuInfo.cdkey.generic.y = 240 - BIGCHAR_HEIGHT / 2;
+ cdkeyMenuInfo.cdkey.field.widthInChars = 16;
+ cdkeyMenuInfo.cdkey.field.maxchars = 16;
+ cdkeyMenuInfo.cdkey.generic.ownerdraw = UI_CDKeyMenu_DrawKey;
+
+ cdkeyMenuInfo.accept.generic.type = MTYPE_BITMAP;
+ cdkeyMenuInfo.accept.generic.name = ART_ACCEPT0;
+ cdkeyMenuInfo.accept.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ cdkeyMenuInfo.accept.generic.id = ID_ACCEPT;
+ cdkeyMenuInfo.accept.generic.callback = UI_CDKeyMenu_Event;
+ cdkeyMenuInfo.accept.generic.x = 640;
+ cdkeyMenuInfo.accept.generic.y = 480-64;
+ cdkeyMenuInfo.accept.width = 128;
+ cdkeyMenuInfo.accept.height = 64;
+ cdkeyMenuInfo.accept.focuspic = ART_ACCEPT1;
+
+ cdkeyMenuInfo.back.generic.type = MTYPE_BITMAP;
+ cdkeyMenuInfo.back.generic.name = ART_BACK0;
+ cdkeyMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ cdkeyMenuInfo.back.generic.id = ID_BACK;
+ cdkeyMenuInfo.back.generic.callback = UI_CDKeyMenu_Event;
+ cdkeyMenuInfo.back.generic.x = 0;
+ cdkeyMenuInfo.back.generic.y = 480-64;
+ cdkeyMenuInfo.back.width = 128;
+ cdkeyMenuInfo.back.height = 64;
+ cdkeyMenuInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.banner );
+ Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.frame );
+ Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.cdkey );
+ Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.accept );
+ if( uis.menusp ) {
+ Menu_AddItem( &cdkeyMenuInfo.menu, &cdkeyMenuInfo.back );
+ }
+
+ trap_GetCDKey( cdkeyMenuInfo.cdkey.field.buffer, cdkeyMenuInfo.cdkey.field.maxchars + 1 );
+ if( trap_VerifyCDKey( cdkeyMenuInfo.cdkey.field.buffer, NULL ) == qfalse ) {
+ cdkeyMenuInfo.cdkey.field.buffer[0] = 0;
+ }
+}
+
+
+/*
+=================
+UI_CDKeyMenu_Cache
+=================
+*/
+void UI_CDKeyMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_ACCEPT0 );
+ trap_R_RegisterShaderNoMip( ART_ACCEPT1 );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FRAME );
+}
+
+
+/*
+===============
+UI_CDKeyMenu
+===============
+*/
+void UI_CDKeyMenu( void ) {
+ UI_CDKeyMenu_Init();
+ UI_PushMenu( &cdkeyMenuInfo.menu );
+}
+
+
+/*
+===============
+UI_CDKeyMenu_f
+===============
+*/
+void UI_CDKeyMenu_f( void ) {
+ UI_CDKeyMenu();
+}
diff --git a/code/q3_ui/ui_cinematics.c b/code/q3_ui/ui_cinematics.c
new file mode 100644
index 0000000..e2fd778
--- /dev/null
+++ b/code/q3_ui/ui_cinematics.c
@@ -0,0 +1,350 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+
+#define VERTICAL_SPACING 30
+
+#define ID_BACK 10
+#define ID_CIN_IDLOGO 11
+#define ID_CIN_INTRO 12
+#define ID_CIN_TIER1 13
+#define ID_CIN_TIER2 14
+#define ID_CIN_TIER3 15
+#define ID_CIN_TIER4 16
+#define ID_CIN_TIER5 17
+#define ID_CIN_TIER6 18
+#define ID_CIN_TIER7 19
+#define ID_CIN_END 20
+
+
+typedef struct {
+ menuframework_s menu;
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menutext_s cin_idlogo;
+ menutext_s cin_intro;
+ menutext_s cin_tier1;
+ menutext_s cin_tier2;
+ menutext_s cin_tier3;
+ menutext_s cin_tier4;
+ menutext_s cin_tier5;
+ menutext_s cin_tier6;
+ menutext_s cin_tier7;
+ menutext_s cin_end;
+ menubitmap_s back;
+} cinematicsMenuInfo_t;
+
+static cinematicsMenuInfo_t cinematicsMenuInfo;
+
+static char *cinematics[] = {
+ "idlogo",
+ "intro",
+ "tier1",
+ "tier2",
+ "tier3",
+ "tier4",
+ "tier5",
+ "tier6",
+ "tier7",
+ "end"
+};
+
+/*
+===============
+UI_CinematicsMenu_BackEvent
+===============
+*/
+static void UI_CinematicsMenu_BackEvent( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ UI_PopMenu();
+}
+
+
+/*
+===============
+UI_CinematicsMenu_Event
+===============
+*/
+static void UI_CinematicsMenu_Event( void *ptr, int event ) {
+ int n;
+
+ if (event != QM_ACTIVATED)
+ return;
+
+ n = ((menucommon_s*)ptr)->id - ID_CIN_IDLOGO;
+ trap_Cvar_Set( "nextmap", va( "ui_cinematics %i", n ) );
+ if( uis.demoversion && ((menucommon_s*)ptr)->id == ID_CIN_END ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic demoEnd.RoQ 1\n" );
+ }
+ else {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "disconnect; cinematic %s.RoQ\n", cinematics[n] ) );
+ }
+}
+
+
+/*
+===============
+UI_CinematicsMenu_Init
+===============
+*/
+static void UI_CinematicsMenu_Init( void ) {
+ int y;
+
+ UI_CinematicsMenu_Cache();
+
+ memset( &cinematicsMenuInfo, 0, sizeof(cinematicsMenuInfo) );
+ cinematicsMenuInfo.menu.fullscreen = qtrue;
+
+ cinematicsMenuInfo.banner.generic.type = MTYPE_BTEXT;
+ cinematicsMenuInfo.banner.generic.x = 320;
+ cinematicsMenuInfo.banner.generic.y = 16;
+ cinematicsMenuInfo.banner.string = "CINEMATICS";
+ cinematicsMenuInfo.banner.color = color_white;
+ cinematicsMenuInfo.banner.style = UI_CENTER;
+
+ cinematicsMenuInfo.framel.generic.type = MTYPE_BITMAP;
+ cinematicsMenuInfo.framel.generic.name = ART_FRAMEL;
+ cinematicsMenuInfo.framel.generic.flags = QMF_INACTIVE;
+ cinematicsMenuInfo.framel.generic.x = 0;
+ cinematicsMenuInfo.framel.generic.y = 78;
+ cinematicsMenuInfo.framel.width = 256;
+ cinematicsMenuInfo.framel.height = 329;
+
+ cinematicsMenuInfo.framer.generic.type = MTYPE_BITMAP;
+ cinematicsMenuInfo.framer.generic.name = ART_FRAMER;
+ cinematicsMenuInfo.framer.generic.flags = QMF_INACTIVE;
+ cinematicsMenuInfo.framer.generic.x = 376;
+ cinematicsMenuInfo.framer.generic.y = 76;
+ cinematicsMenuInfo.framer.width = 256;
+ cinematicsMenuInfo.framer.height = 334;
+
+ y = 100;
+ cinematicsMenuInfo.cin_idlogo.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_idlogo.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_idlogo.generic.x = 320;
+ cinematicsMenuInfo.cin_idlogo.generic.y = y;
+ cinematicsMenuInfo.cin_idlogo.generic.id = ID_CIN_IDLOGO;
+ cinematicsMenuInfo.cin_idlogo.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_idlogo.string = "ID LOGO";
+ cinematicsMenuInfo.cin_idlogo.color = color_red;
+ cinematicsMenuInfo.cin_idlogo.style = UI_CENTER;
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_intro.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_intro.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_intro.generic.x = 320;
+ cinematicsMenuInfo.cin_intro.generic.y = y;
+ cinematicsMenuInfo.cin_intro.generic.id = ID_CIN_INTRO;
+ cinematicsMenuInfo.cin_intro.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_intro.string = "INTRO";
+ cinematicsMenuInfo.cin_intro.color = color_red;
+ cinematicsMenuInfo.cin_intro.style = UI_CENTER;
+ if( uis.demoversion ) {
+ cinematicsMenuInfo.cin_intro.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_tier1.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_tier1.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_tier1.generic.x = 320;
+ cinematicsMenuInfo.cin_tier1.generic.y = y;
+ cinematicsMenuInfo.cin_tier1.generic.id = ID_CIN_TIER1;
+ cinematicsMenuInfo.cin_tier1.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_tier1.string = "Tier 1";
+ cinematicsMenuInfo.cin_tier1.color = color_red;
+ cinematicsMenuInfo.cin_tier1.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 1 ) ) {
+ cinematicsMenuInfo.cin_tier1.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_tier2.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_tier2.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_tier2.generic.x = 320;
+ cinematicsMenuInfo.cin_tier2.generic.y = y;
+ cinematicsMenuInfo.cin_tier2.generic.id = ID_CIN_TIER2;
+ cinematicsMenuInfo.cin_tier2.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_tier2.string = "Tier 2";
+ cinematicsMenuInfo.cin_tier2.color = color_red;
+ cinematicsMenuInfo.cin_tier2.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 2 ) ) {
+ cinematicsMenuInfo.cin_tier2.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_tier3.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_tier3.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_tier3.generic.x = 320;
+ cinematicsMenuInfo.cin_tier3.generic.y = y;
+ cinematicsMenuInfo.cin_tier3.generic.id = ID_CIN_TIER3;
+ cinematicsMenuInfo.cin_tier3.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_tier3.string = "Tier 3";
+ cinematicsMenuInfo.cin_tier3.color = color_red;
+ cinematicsMenuInfo.cin_tier3.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 3 ) ) {
+ cinematicsMenuInfo.cin_tier3.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_tier4.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_tier4.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_tier4.generic.x = 320;
+ cinematicsMenuInfo.cin_tier4.generic.y = y;
+ cinematicsMenuInfo.cin_tier4.generic.id = ID_CIN_TIER4;
+ cinematicsMenuInfo.cin_tier4.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_tier4.string = "Tier 4";
+ cinematicsMenuInfo.cin_tier4.color = color_red;
+ cinematicsMenuInfo.cin_tier4.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 4 ) ) {
+ cinematicsMenuInfo.cin_tier4.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_tier5.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_tier5.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_tier5.generic.x = 320;
+ cinematicsMenuInfo.cin_tier5.generic.y = y;
+ cinematicsMenuInfo.cin_tier5.generic.id = ID_CIN_TIER5;
+ cinematicsMenuInfo.cin_tier5.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_tier5.string = "Tier 5";
+ cinematicsMenuInfo.cin_tier5.color = color_red;
+ cinematicsMenuInfo.cin_tier5.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 5 ) ) {
+ cinematicsMenuInfo.cin_tier5.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_tier6.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_tier6.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_tier6.generic.x = 320;
+ cinematicsMenuInfo.cin_tier6.generic.y = y;
+ cinematicsMenuInfo.cin_tier6.generic.id = ID_CIN_TIER6;
+ cinematicsMenuInfo.cin_tier6.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_tier6.string = "Tier 6";
+ cinematicsMenuInfo.cin_tier6.color = color_red;
+ cinematicsMenuInfo.cin_tier6.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 6 ) ) {
+ cinematicsMenuInfo.cin_tier6.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_tier7.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_tier7.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_tier7.generic.x = 320;
+ cinematicsMenuInfo.cin_tier7.generic.y = y;
+ cinematicsMenuInfo.cin_tier7.generic.id = ID_CIN_TIER7;
+ cinematicsMenuInfo.cin_tier7.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_tier7.string = "Tier 7";
+ cinematicsMenuInfo.cin_tier7.color = color_red;
+ cinematicsMenuInfo.cin_tier7.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 7 ) ) {
+ cinematicsMenuInfo.cin_tier7.generic.flags |= QMF_GRAYED;
+ }
+
+ y += VERTICAL_SPACING;
+ cinematicsMenuInfo.cin_end.generic.type = MTYPE_PTEXT;
+ cinematicsMenuInfo.cin_end.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.cin_end.generic.x = 320;
+ cinematicsMenuInfo.cin_end.generic.y = y;
+ cinematicsMenuInfo.cin_end.generic.id = ID_CIN_END;
+ cinematicsMenuInfo.cin_end.generic.callback = UI_CinematicsMenu_Event;
+ cinematicsMenuInfo.cin_end.string = "END";
+ cinematicsMenuInfo.cin_end.color = color_red;
+ cinematicsMenuInfo.cin_end.style = UI_CENTER;
+ if( !UI_CanShowTierVideo( 8 ) ) {
+ cinematicsMenuInfo.cin_end.generic.flags |= QMF_GRAYED;
+ }
+
+ cinematicsMenuInfo.back.generic.type = MTYPE_BITMAP;
+ cinematicsMenuInfo.back.generic.name = ART_BACK0;
+ cinematicsMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ cinematicsMenuInfo.back.generic.id = ID_BACK;
+ cinematicsMenuInfo.back.generic.callback = UI_CinematicsMenu_BackEvent;
+ cinematicsMenuInfo.back.generic.x = 0;
+ cinematicsMenuInfo.back.generic.y = 480-64;
+ cinematicsMenuInfo.back.width = 128;
+ cinematicsMenuInfo.back.height = 64;
+ cinematicsMenuInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.banner );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.framel );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.framer );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_idlogo );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_intro );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier1 );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier2 );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier3 );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier4 );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier5 );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier6 );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_tier7 );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.cin_end );
+ Menu_AddItem( &cinematicsMenuInfo.menu, &cinematicsMenuInfo.back );
+}
+
+
+/*
+=================
+UI_CinematicsMenu_Cache
+=================
+*/
+void UI_CinematicsMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+}
+
+
+/*
+===============
+UI_CinematicsMenu
+===============
+*/
+void UI_CinematicsMenu( void ) {
+ UI_CinematicsMenu_Init();
+ UI_PushMenu( &cinematicsMenuInfo.menu );
+}
+
+
+/*
+===============
+UI_CinematicsMenu_f
+===============
+*/
+void UI_CinematicsMenu_f( void ) {
+ int n;
+
+ n = atoi( UI_Argv( 1 ) );
+ UI_CinematicsMenu();
+ Menu_SetCursorToItem( &cinematicsMenuInfo.menu, cinematicsMenuInfo.menu.items[n + 3] );
+}
diff --git a/code/q3_ui/ui_confirm.c b/code/q3_ui/ui_confirm.c
new file mode 100644
index 0000000..a5f6f16
--- /dev/null
+++ b/code/q3_ui/ui_confirm.c
@@ -0,0 +1,293 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+CONFIRMATION MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ART_CONFIRM_FRAME "menu/art/cut_frame"
+
+#define ID_CONFIRM_NO 10
+#define ID_CONFIRM_YES 11
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s no;
+ menutext_s yes;
+
+ int slashX;
+ const char * question;
+ void (*draw)( void );
+ void (*action)( qboolean result );
+
+ int style;
+ const char **lines;
+} confirmMenu_t;
+
+
+static confirmMenu_t s_confirm;
+
+
+/*
+=================
+ConfirmMenu_Event
+=================
+*/
+static void ConfirmMenu_Event( void* ptr, int event ) {
+ qboolean result;
+
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ UI_PopMenu();
+
+ if( ((menucommon_s*)ptr)->id == ID_CONFIRM_NO ) {
+ result = qfalse;
+ }
+ else {
+ result = qtrue;
+ }
+
+ if( s_confirm.action ) {
+ s_confirm.action( result );
+ }
+}
+
+
+/*
+=================
+ConfirmMenu_Key
+=================
+*/
+static sfxHandle_t ConfirmMenu_Key( int key ) {
+ switch ( key ) {
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ key = K_TAB;
+ break;
+
+ case 'n':
+ case 'N':
+ ConfirmMenu_Event( &s_confirm.no, QM_ACTIVATED );
+ break;
+
+ case 'y':
+ case 'Y':
+ ConfirmMenu_Event( &s_confirm.yes, QM_ACTIVATED );
+ break;
+ }
+
+ return Menu_DefaultKey( &s_confirm.menu, key );
+}
+
+
+/*
+=================
+MessaheMenu_Draw
+=================
+*/
+static void MessageMenu_Draw( void ) {
+ int i,y;
+
+ UI_DrawNamedPic( 142, 118, 359, 256, ART_CONFIRM_FRAME );
+
+ y = 188;
+ for(i=0; s_confirm.lines[i]; i++)
+ {
+ UI_DrawProportionalString( 320, y, s_confirm.lines[i], s_confirm.style, color_red );
+ y += 18;
+ }
+
+ Menu_Draw( &s_confirm.menu );
+
+ if( s_confirm.draw ) {
+ s_confirm.draw();
+ }
+}
+
+/*
+=================
+ConfirmMenu_Draw
+=================
+*/
+static void ConfirmMenu_Draw( void ) {
+ UI_DrawNamedPic( 142, 118, 359, 256, ART_CONFIRM_FRAME );
+ UI_DrawProportionalString( 320, 204, s_confirm.question, s_confirm.style, color_red );
+ UI_DrawProportionalString( s_confirm.slashX, 265, "/", UI_LEFT|UI_INVERSE, color_red );
+
+ Menu_Draw( &s_confirm.menu );
+
+ if( s_confirm.draw ) {
+ s_confirm.draw();
+ }
+}
+
+
+/*
+=================
+ConfirmMenu_Cache
+=================
+*/
+void ConfirmMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_CONFIRM_FRAME );
+}
+
+
+/*
+=================
+UI_ConfirmMenu_Stlye
+=================
+*/
+void UI_ConfirmMenu_Style( const char *question, int style, void (*draw)( void ), void (*action)( qboolean result ) ) {
+ uiClientState_t cstate;
+ int n1, n2, n3;
+ int l1, l2, l3;
+
+ // zero set all our globals
+ memset( &s_confirm, 0, sizeof(s_confirm) );
+
+ ConfirmMenu_Cache();
+
+ n1 = UI_ProportionalStringWidth( "YES/NO" );
+ n2 = UI_ProportionalStringWidth( "YES" ) + PROP_GAP_WIDTH;
+ n3 = UI_ProportionalStringWidth( "/" ) + PROP_GAP_WIDTH;
+ l1 = 320 - ( n1 / 2 );
+ l2 = l1 + n2;
+ l3 = l2 + n3;
+ s_confirm.slashX = l2;
+
+ s_confirm.question = question;
+ s_confirm.draw = draw;
+ s_confirm.action = action;
+ s_confirm.style = style;
+
+ s_confirm.menu.draw = ConfirmMenu_Draw;
+ s_confirm.menu.key = ConfirmMenu_Key;
+ s_confirm.menu.wrapAround = qtrue;
+
+ trap_GetClientState( &cstate );
+ if ( cstate.connState >= CA_CONNECTED ) {
+ s_confirm.menu.fullscreen = qfalse;
+ }
+ else {
+ s_confirm.menu.fullscreen = qtrue;
+ }
+
+ s_confirm.yes.generic.type = MTYPE_PTEXT;
+ s_confirm.yes.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_confirm.yes.generic.callback = ConfirmMenu_Event;
+ s_confirm.yes.generic.id = ID_CONFIRM_YES;
+ s_confirm.yes.generic.x = l1;
+ s_confirm.yes.generic.y = 264;
+ s_confirm.yes.string = "YES";
+ s_confirm.yes.color = color_red;
+ s_confirm.yes.style = UI_LEFT;
+
+ s_confirm.no.generic.type = MTYPE_PTEXT;
+ s_confirm.no.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_confirm.no.generic.callback = ConfirmMenu_Event;
+ s_confirm.no.generic.id = ID_CONFIRM_NO;
+ s_confirm.no.generic.x = l3;
+ s_confirm.no.generic.y = 264;
+ s_confirm.no.string = "NO";
+ s_confirm.no.color = color_red;
+ s_confirm.no.style = UI_LEFT;
+
+ Menu_AddItem( &s_confirm.menu, &s_confirm.yes );
+ Menu_AddItem( &s_confirm.menu, &s_confirm.no );
+
+ UI_PushMenu( &s_confirm.menu );
+
+ Menu_SetCursorToItem( &s_confirm.menu, &s_confirm.no );
+}
+
+/*
+=================
+UI_ConfirmMenu
+=================
+*/
+void UI_ConfirmMenu( const char *question, void (*draw)( void ), void (*action)( qboolean result ) ) {
+ UI_ConfirmMenu_Style(question, UI_CENTER|UI_INVERSE, draw, action);
+}
+
+/*
+=================
+UI_Message
+hacked over from Confirm stuff
+=================
+*/
+void UI_Message( const char **lines ) {
+ uiClientState_t cstate;
+ int n1, l1;
+
+ // zero set all our globals
+ memset( &s_confirm, 0, sizeof(s_confirm) );
+
+ ConfirmMenu_Cache();
+
+ n1 = UI_ProportionalStringWidth( "OK" );
+ l1 = 320 - ( n1 / 2 );
+
+ s_confirm.lines = lines;
+ s_confirm.style = UI_CENTER|UI_INVERSE|UI_SMALLFONT;
+
+ s_confirm.menu.draw = MessageMenu_Draw;
+ s_confirm.menu.key = ConfirmMenu_Key;
+ s_confirm.menu.wrapAround = qtrue;
+
+ trap_GetClientState( &cstate );
+ if ( cstate.connState >= CA_CONNECTED ) {
+ s_confirm.menu.fullscreen = qfalse;
+ }
+ else {
+ s_confirm.menu.fullscreen = qtrue;
+ }
+
+ s_confirm.yes.generic.type = MTYPE_PTEXT;
+ s_confirm.yes.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_confirm.yes.generic.callback = ConfirmMenu_Event;
+ s_confirm.yes.generic.id = ID_CONFIRM_YES;
+ s_confirm.yes.generic.x = l1;
+ s_confirm.yes.generic.y = 280;
+ s_confirm.yes.string = "OK";
+ s_confirm.yes.color = color_red;
+ s_confirm.yes.style = UI_LEFT;
+
+ Menu_AddItem( &s_confirm.menu, &s_confirm.yes );
+
+ UI_PushMenu( &s_confirm.menu );
+
+ Menu_SetCursorToItem( &s_confirm.menu, &s_confirm.yes );
+}
diff --git a/code/q3_ui/ui_connect.c b/code/q3_ui/ui_connect.c
new file mode 100644
index 0000000..27db956
--- /dev/null
+++ b/code/q3_ui/ui_connect.c
@@ -0,0 +1,266 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+/*
+===============================================================================
+
+CONNECTION SCREEN
+
+===============================================================================
+*/
+
+qboolean passwordNeeded = qtrue;
+menufield_s passwordField;
+
+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 );
+ }
+}
+
+static void UI_DisplayDownloadInfo( const char *downloadName ) {
+ 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 width, leftWidth;
+ int style = UI_LEFT|UI_SMALLFONT|UI_DROPSHADOW;
+ const char *s;
+
+ downloadSize = trap_Cvar_VariableValue( "cl_downloadSize" );
+ downloadCount = trap_Cvar_VariableValue( "cl_downloadCount" );
+ downloadTime = trap_Cvar_VariableValue( "cl_downloadTime" );
+
+ leftWidth = width = UI_ProportionalStringWidth( dlText ) * UI_ProportionalSizeScale( style );
+ width = UI_ProportionalStringWidth( etaText ) * UI_ProportionalSizeScale( style );
+ if (width > leftWidth) leftWidth = width;
+ width = UI_ProportionalStringWidth( xferText ) * UI_ProportionalSizeScale( style );
+ if (width > leftWidth) leftWidth = width;
+ leftWidth += 16;
+
+ UI_DrawProportionalString( 8, 128, dlText, style, color_white );
+ UI_DrawProportionalString( 8, 160, etaText, style, color_white );
+ UI_DrawProportionalString( 8, 224, xferText, style, color_white );
+
+ if (downloadSize > 0) {
+ s = va( "%s (%d%%)", downloadName, (int)( (float)downloadCount * 100.0f / downloadSize ) );
+ } else {
+ s = downloadName;
+ }
+
+ UI_DrawProportionalString( leftWidth, 128, s, style, color_white );
+
+ UI_ReadableSize( dlSizeBuf, sizeof dlSizeBuf, downloadCount );
+ UI_ReadableSize( totalSizeBuf, sizeof totalSizeBuf, downloadSize );
+
+ if (downloadCount < 4096 || !downloadTime) {
+ UI_DrawProportionalString( leftWidth, 160, "estimating", style, color_white );
+ UI_DrawProportionalString( leftWidth, 192,
+ va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), style, color_white );
+ } else {
+ if ( (uis.realtime - downloadTime) / 1000) {
+ xferRate = downloadCount / ((uis.realtime - downloadTime) / 1000);
+ //xferRate = (int)( ((float)downloadCount) / elapsedTime);
+ } 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
+ n = (n - (((downloadCount/1024) * n) / (downloadSize/1024))) * 1000;
+
+ UI_PrintTime ( dlTimeBuf, sizeof dlTimeBuf, n );
+ //(n - (((downloadCount/1024) * n) / (downloadSize/1024))) * 1000);
+
+ UI_DrawProportionalString( leftWidth, 160,
+ dlTimeBuf, style, color_white );
+ UI_DrawProportionalString( leftWidth, 192,
+ va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), style, color_white );
+ } else {
+ UI_DrawProportionalString( leftWidth, 160,
+ "estimating", style, color_white );
+ if (downloadSize) {
+ UI_DrawProportionalString( leftWidth, 192,
+ va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), style, color_white );
+ } else {
+ UI_DrawProportionalString( leftWidth, 192,
+ va("(%s copied)", dlSizeBuf), style, color_white );
+ }
+ }
+
+ if (xferRate) {
+ UI_DrawProportionalString( leftWidth, 224,
+ va("%s/Sec", xferRateBuf), style, color_white );
+ }
+ }
+}
+
+/*
+========================
+UI_DrawConnectScreen
+
+This will also be overlaid on the cgame info screen during loading
+to prevent it from blinking away too rapidly on local or lan games.
+========================
+*/
+void UI_DrawConnectScreen( qboolean overlay ) {
+ char *s;
+ uiClientState_t cstate;
+ char info[MAX_INFO_VALUE];
+
+ Menu_Cache();
+
+ if ( !overlay ) {
+ // draw the dialog background
+ UI_SetColor( color_white );
+ UI_DrawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, uis.menuBackShader );
+ }
+
+ // see what information we should display
+ trap_GetClientState( &cstate );
+
+ info[0] = '\0';
+ if( trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ) ) {
+ UI_DrawProportionalString( 320, 16, va( "Loading %s", Info_ValueForKey( info, "mapname" ) ), UI_BIGFONT|UI_CENTER|UI_DROPSHADOW, color_white );
+ }
+
+ UI_DrawProportionalString( 320, 64, va("Connecting to %s", cstate.servername), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color );
+ //UI_DrawProportionalString( 320, 96, "Press Esc to abort", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color );
+
+ // display global MOTD at bottom
+ UI_DrawProportionalString( SCREEN_WIDTH/2, SCREEN_HEIGHT-32,
+ Info_ValueForKey( cstate.updateInfoString, "motd" ), UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color );
+
+ // print any server info (server full, bad version, etc)
+ if ( cstate.connState < CA_CONNECTED ) {
+ UI_DrawProportionalString_AutoWrapped( 320, 192, 630, 20, cstate.messageString, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color );
+ }
+
+#if 0
+ // display password field
+ if ( passwordNeeded ) {
+ s_ingame_menu.x = SCREEN_WIDTH * 0.50 - 128;
+ s_ingame_menu.nitems = 0;
+ s_ingame_menu.wrapAround = qtrue;
+
+ passwordField.generic.type = MTYPE_FIELD;
+ passwordField.generic.name = "Password:";
+ passwordField.generic.callback = 0;
+ passwordField.generic.x = 10;
+ passwordField.generic.y = 180;
+ Field_Clear( &passwordField.field );
+ passwordField.width = 256;
+ passwordField.field.widthInChars = 16;
+ Q_strncpyz( passwordField.field.buffer, Cvar_VariableString("password"),
+ sizeof(passwordField.field.buffer) );
+
+ Menu_AddItem( &s_ingame_menu, ( void * ) &s_customize_player_action );
+
+ MField_Draw( &passwordField );
+ }
+#endif
+
+ if ( lastConnState > cstate.connState ) {
+ lastLoadingText[0] = '\0';
+ }
+ lastConnState = cstate.connState;
+
+ switch ( cstate.connState ) {
+ case CA_CONNECTING:
+ s = va("Awaiting challenge...%i", cstate.connectPacketCount);
+ break;
+ case CA_CHALLENGING:
+ s = va("Awaiting connection...%i", cstate.connectPacketCount);
+ break;
+ case CA_CONNECTED: {
+ char downloadName[MAX_INFO_VALUE];
+
+ trap_Cvar_VariableStringBuffer( "cl_downloadName", downloadName, sizeof(downloadName) );
+ if (*downloadName) {
+ UI_DisplayDownloadInfo( downloadName );
+ return;
+ }
+ }
+ s = "Awaiting gamestate...";
+ break;
+ case CA_LOADING:
+ return;
+ case CA_PRIMED:
+ return;
+ default:
+ return;
+ }
+
+ UI_DrawProportionalString( 320, 128, s, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, color_white );
+
+ // password required / connection rejected information goes here
+}
+
+
+/*
+===================
+UI_KeyConnect
+===================
+*/
+void UI_KeyConnect( int key ) {
+ if ( key == K_ESCAPE ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" );
+ return;
+ }
+}
diff --git a/code/q3_ui/ui_controls2.c b/code/q3_ui/ui_controls2.c
new file mode 100644
index 0000000..907ad00
--- /dev/null
+++ b/code/q3_ui/ui_controls2.c
@@ -0,0 +1,1664 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+CONTROLS MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+
+
+typedef struct {
+ char *command;
+ char *label;
+ int id;
+ int anim;
+ int defaultbind1;
+ int defaultbind2;
+ int bind1;
+ int bind2;
+} bind_t;
+
+typedef struct
+{
+ char* name;
+ float defaultvalue;
+ float value;
+} configcvar_t;
+
+#define SAVE_NOOP 0
+#define SAVE_YES 1
+#define SAVE_NO 2
+#define SAVE_CANCEL 3
+
+// control sections
+#define C_MOVEMENT 0
+#define C_LOOKING 1
+#define C_WEAPONS 2
+#define C_MISC 3
+#define C_MAX 4
+
+#define ID_MOVEMENT 100
+#define ID_LOOKING 101
+#define ID_WEAPONS 102
+#define ID_MISC 103
+#define ID_DEFAULTS 104
+#define ID_BACK 105
+#define ID_SAVEANDEXIT 106
+#define ID_EXIT 107
+
+// bindable actions
+#define ID_SHOWSCORES 0
+#define ID_USEITEM 1
+#define ID_SPEED 2
+#define ID_FORWARD 3
+#define ID_BACKPEDAL 4
+#define ID_MOVELEFT 5
+#define ID_MOVERIGHT 6
+#define ID_MOVEUP 7
+#define ID_MOVEDOWN 8
+#define ID_LEFT 9
+#define ID_RIGHT 10
+#define ID_STRAFE 11
+#define ID_LOOKUP 12
+#define ID_LOOKDOWN 13
+#define ID_MOUSELOOK 14
+#define ID_CENTERVIEW 15
+#define ID_ZOOMVIEW 16
+#define ID_WEAPON1 17
+#define ID_WEAPON2 18
+#define ID_WEAPON3 19
+#define ID_WEAPON4 20
+#define ID_WEAPON5 21
+#define ID_WEAPON6 22
+#define ID_WEAPON7 23
+#define ID_WEAPON8 24
+#define ID_WEAPON9 25
+#define ID_ATTACK 26
+#define ID_WEAPPREV 27
+#define ID_WEAPNEXT 28
+#define ID_GESTURE 29
+#define ID_CHAT 30
+#define ID_CHAT2 31
+#define ID_CHAT3 32
+#define ID_CHAT4 33
+
+// all others
+#define ID_FREELOOK 34
+#define ID_INVERTMOUSE 35
+#define ID_ALWAYSRUN 36
+#define ID_AUTOSWITCH 37
+#define ID_MOUSESPEED 38
+#define ID_JOYENABLE 39
+#define ID_JOYTHRESHOLD 40
+#define ID_SMOOTHMOUSE 41
+
+#define ANIM_IDLE 0
+#define ANIM_RUN 1
+#define ANIM_WALK 2
+#define ANIM_BACK 3
+#define ANIM_JUMP 4
+#define ANIM_CROUCH 5
+#define ANIM_STEPLEFT 6
+#define ANIM_STEPRIGHT 7
+#define ANIM_TURNLEFT 8
+#define ANIM_TURNRIGHT 9
+#define ANIM_LOOKUP 10
+#define ANIM_LOOKDOWN 11
+#define ANIM_WEAPON1 12
+#define ANIM_WEAPON2 13
+#define ANIM_WEAPON3 14
+#define ANIM_WEAPON4 15
+#define ANIM_WEAPON5 16
+#define ANIM_WEAPON6 17
+#define ANIM_WEAPON7 18
+#define ANIM_WEAPON8 19
+#define ANIM_WEAPON9 20
+#define ANIM_WEAPON10 21
+#define ANIM_ATTACK 22
+#define ANIM_GESTURE 23
+#define ANIM_DIE 24
+#define ANIM_CHAT 25
+
+typedef struct
+{
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menubitmap_s player;
+
+ menutext_s movement;
+ menutext_s looking;
+ menutext_s weapons;
+ menutext_s misc;
+
+ menuaction_s walkforward;
+ menuaction_s backpedal;
+ menuaction_s stepleft;
+ menuaction_s stepright;
+ menuaction_s moveup;
+ menuaction_s movedown;
+ menuaction_s turnleft;
+ menuaction_s turnright;
+ menuaction_s sidestep;
+ menuaction_s run;
+ menuaction_s machinegun;
+ menuaction_s chainsaw;
+ menuaction_s shotgun;
+ menuaction_s grenadelauncher;
+ menuaction_s rocketlauncher;
+ menuaction_s lightning;
+ menuaction_s railgun;
+ menuaction_s plasma;
+ menuaction_s bfg;
+ menuaction_s attack;
+ menuaction_s prevweapon;
+ menuaction_s nextweapon;
+ menuaction_s lookup;
+ menuaction_s lookdown;
+ menuaction_s mouselook;
+ menuradiobutton_s freelook;
+ menuaction_s centerview;
+ menuaction_s zoomview;
+ menuaction_s gesture;
+ menuradiobutton_s invertmouse;
+ menuslider_s sensitivity;
+ menuradiobutton_s smoothmouse;
+ menuradiobutton_s alwaysrun;
+ menuaction_s showscores;
+ menuradiobutton_s autoswitch;
+ menuaction_s useitem;
+ playerInfo_t playerinfo;
+ qboolean changesmade;
+ menuaction_s chat;
+ menuaction_s chat2;
+ menuaction_s chat3;
+ menuaction_s chat4;
+ menuradiobutton_s joyenable;
+ menuslider_s joythreshold;
+ int section;
+ qboolean waitingforkey;
+ char playerModel[64];
+ vec3_t playerViewangles;
+ vec3_t playerMoveangles;
+ int playerLegs;
+ int playerTorso;
+ int playerWeapon;
+ qboolean playerChat;
+
+ menubitmap_s back;
+ menutext_s name;
+} controls_t;
+
+static controls_t s_controls;
+
+static vec4_t controls_binding_color = {1.00f, 0.43f, 0.00f, 1.00f};
+
+static bind_t g_bindings[] =
+{
+ {"+scores", "show scores", ID_SHOWSCORES, ANIM_IDLE, K_TAB, -1, -1, -1},
+ {"+button2", "use item", ID_USEITEM, ANIM_IDLE, K_ENTER, -1, -1, -1},
+ {"+speed", "run / walk", ID_SPEED, ANIM_RUN, K_SHIFT, -1, -1, -1},
+ {"+forward", "walk forward", ID_FORWARD, ANIM_WALK, K_UPARROW, -1, -1, -1},
+ {"+back", "backpedal", ID_BACKPEDAL, ANIM_BACK, K_DOWNARROW, -1, -1, -1},
+ {"+moveleft", "step left", ID_MOVELEFT, ANIM_STEPLEFT, ',', -1, -1, -1},
+ {"+moveright", "step right", ID_MOVERIGHT, ANIM_STEPRIGHT, '.', -1, -1, -1},
+ {"+moveup", "up / jump", ID_MOVEUP, ANIM_JUMP, K_SPACE, -1, -1, -1},
+ {"+movedown", "down / crouch", ID_MOVEDOWN, ANIM_CROUCH, 'c', -1, -1, -1},
+ {"+left", "turn left", ID_LEFT, ANIM_TURNLEFT, K_LEFTARROW, -1, -1, -1},
+ {"+right", "turn right", ID_RIGHT, ANIM_TURNRIGHT, K_RIGHTARROW, -1, -1, -1},
+ {"+strafe", "sidestep / turn", ID_STRAFE, ANIM_IDLE, K_ALT, -1, -1, -1},
+ {"+lookup", "look up", ID_LOOKUP, ANIM_LOOKUP, K_PGDN, -1, -1, -1},
+ {"+lookdown", "look down", ID_LOOKDOWN, ANIM_LOOKDOWN, K_DEL, -1, -1, -1},
+ {"+mlook", "mouse look", ID_MOUSELOOK, ANIM_IDLE, '/', -1, -1, -1},
+ {"centerview", "center view", ID_CENTERVIEW, ANIM_IDLE, K_END, -1, -1, -1},
+ {"+zoom", "zoom view", ID_ZOOMVIEW, ANIM_IDLE, -1, -1, -1, -1},
+ {"weapon 1", "gauntlet", ID_WEAPON1, ANIM_WEAPON1, '1', -1, -1, -1},
+ {"weapon 2", "machinegun", ID_WEAPON2, ANIM_WEAPON2, '2', -1, -1, -1},
+ {"weapon 3", "shotgun", ID_WEAPON3, ANIM_WEAPON3, '3', -1, -1, -1},
+ {"weapon 4", "grenade launcher", ID_WEAPON4, ANIM_WEAPON4, '4', -1, -1, -1},
+ {"weapon 5", "rocket launcher", ID_WEAPON5, ANIM_WEAPON5, '5', -1, -1, -1},
+ {"weapon 6", "lightning", ID_WEAPON6, ANIM_WEAPON6, '6', -1, -1, -1},
+ {"weapon 7", "railgun", ID_WEAPON7, ANIM_WEAPON7, '7', -1, -1, -1},
+ {"weapon 8", "plasma gun", ID_WEAPON8, ANIM_WEAPON8, '8', -1, -1, -1},
+ {"weapon 9", "BFG", ID_WEAPON9, ANIM_WEAPON9, '9', -1, -1, -1},
+ {"+attack", "attack", ID_ATTACK, ANIM_ATTACK, K_CTRL, -1, -1, -1},
+ {"weapprev", "prev weapon", ID_WEAPPREV, ANIM_IDLE, '[', -1, -1, -1},
+ {"weapnext", "next weapon", ID_WEAPNEXT, ANIM_IDLE, ']', -1, -1, -1},
+ {"+button3", "gesture", ID_GESTURE, ANIM_GESTURE, K_MOUSE3, -1, -1, -1},
+ {"messagemode", "chat", ID_CHAT, ANIM_CHAT, 't', -1, -1, -1},
+ {"messagemode2", "chat - team", ID_CHAT2, ANIM_CHAT, -1, -1, -1, -1},
+ {"messagemode3", "chat - target", ID_CHAT3, ANIM_CHAT, -1, -1, -1, -1},
+ {"messagemode4", "chat - attacker", ID_CHAT4, ANIM_CHAT, -1, -1, -1, -1},
+ {(char*)NULL, (char*)NULL, 0, 0, -1, -1, -1, -1},
+};
+
+static configcvar_t g_configcvars[] =
+{
+ {"cl_run", 0, 0},
+ {"m_pitch", 0, 0},
+ {"cg_autoswitch", 0, 0},
+ {"sensitivity", 0, 0},
+ {"in_joystick", 0, 0},
+ {"joy_threshold", 0, 0},
+ {"m_filter", 0, 0},
+ {"cl_freelook", 0, 0},
+ {NULL, 0, 0}
+};
+
+static menucommon_s *g_movement_controls[] =
+{
+ (menucommon_s *)&s_controls.alwaysrun,
+ (menucommon_s *)&s_controls.run,
+ (menucommon_s *)&s_controls.walkforward,
+ (menucommon_s *)&s_controls.backpedal,
+ (menucommon_s *)&s_controls.stepleft,
+ (menucommon_s *)&s_controls.stepright,
+ (menucommon_s *)&s_controls.moveup,
+ (menucommon_s *)&s_controls.movedown,
+ (menucommon_s *)&s_controls.turnleft,
+ (menucommon_s *)&s_controls.turnright,
+ (menucommon_s *)&s_controls.sidestep,
+ NULL
+};
+
+static menucommon_s *g_weapons_controls[] = {
+ (menucommon_s *)&s_controls.attack,
+ (menucommon_s *)&s_controls.nextweapon,
+ (menucommon_s *)&s_controls.prevweapon,
+ (menucommon_s *)&s_controls.autoswitch,
+ (menucommon_s *)&s_controls.chainsaw,
+ (menucommon_s *)&s_controls.machinegun,
+ (menucommon_s *)&s_controls.shotgun,
+ (menucommon_s *)&s_controls.grenadelauncher,
+ (menucommon_s *)&s_controls.rocketlauncher,
+ (menucommon_s *)&s_controls.lightning,
+ (menucommon_s *)&s_controls.railgun,
+ (menucommon_s *)&s_controls.plasma,
+ (menucommon_s *)&s_controls.bfg,
+ NULL,
+};
+
+static menucommon_s *g_looking_controls[] = {
+ (menucommon_s *)&s_controls.sensitivity,
+ (menucommon_s *)&s_controls.smoothmouse,
+ (menucommon_s *)&s_controls.invertmouse,
+ (menucommon_s *)&s_controls.lookup,
+ (menucommon_s *)&s_controls.lookdown,
+ (menucommon_s *)&s_controls.mouselook,
+ (menucommon_s *)&s_controls.freelook,
+ (menucommon_s *)&s_controls.centerview,
+ (menucommon_s *)&s_controls.zoomview,
+ (menucommon_s *)&s_controls.joyenable,
+ (menucommon_s *)&s_controls.joythreshold,
+ NULL,
+};
+
+static menucommon_s *g_misc_controls[] = {
+ (menucommon_s *)&s_controls.showscores,
+ (menucommon_s *)&s_controls.useitem,
+ (menucommon_s *)&s_controls.gesture,
+ (menucommon_s *)&s_controls.chat,
+ (menucommon_s *)&s_controls.chat2,
+ (menucommon_s *)&s_controls.chat3,
+ (menucommon_s *)&s_controls.chat4,
+ NULL,
+};
+
+static menucommon_s **g_controls[] = {
+ g_movement_controls,
+ g_looking_controls,
+ g_weapons_controls,
+ g_misc_controls,
+};
+
+/*
+=================
+Controls_InitCvars
+=================
+*/
+static void Controls_InitCvars( void )
+{
+ int i;
+ configcvar_t* cvarptr;
+
+ cvarptr = g_configcvars;
+ for (i=0; ;i++,cvarptr++)
+ {
+ if (!cvarptr->name)
+ break;
+
+ // get current value
+ cvarptr->value = trap_Cvar_VariableValue( cvarptr->name );
+
+ // get default value
+ trap_Cvar_Reset( cvarptr->name );
+ cvarptr->defaultvalue = trap_Cvar_VariableValue( cvarptr->name );
+
+ // restore current value
+ trap_Cvar_SetValue( cvarptr->name, cvarptr->value );
+ }
+}
+
+/*
+=================
+Controls_GetCvarDefault
+=================
+*/
+static float Controls_GetCvarDefault( char* name )
+{
+ configcvar_t* cvarptr;
+ int i;
+
+ cvarptr = g_configcvars;
+ for (i=0; ;i++,cvarptr++)
+ {
+ if (!cvarptr->name)
+ return (0);
+
+ if (!strcmp(cvarptr->name,name))
+ break;
+ }
+
+ return (cvarptr->defaultvalue);
+}
+
+/*
+=================
+Controls_GetCvarValue
+=================
+*/
+static float Controls_GetCvarValue( char* name )
+{
+ configcvar_t* cvarptr;
+ int i;
+
+ cvarptr = g_configcvars;
+ for (i=0; ;i++,cvarptr++)
+ {
+ if (!cvarptr->name)
+ return (0);
+
+ if (!strcmp(cvarptr->name,name))
+ break;
+ }
+
+ return (cvarptr->value);
+}
+
+
+/*
+=================
+Controls_UpdateModel
+=================
+*/
+static void Controls_UpdateModel( int anim ) {
+ VectorClear( s_controls.playerViewangles );
+ VectorClear( s_controls.playerMoveangles );
+ s_controls.playerViewangles[YAW] = 180 - 30;
+ s_controls.playerMoveangles[YAW] = s_controls.playerViewangles[YAW];
+ s_controls.playerLegs = LEGS_IDLE;
+ s_controls.playerTorso = TORSO_STAND;
+ s_controls.playerWeapon = -1;
+ s_controls.playerChat = qfalse;
+
+ switch( anim ) {
+ case ANIM_RUN:
+ s_controls.playerLegs = LEGS_RUN;
+ break;
+
+ case ANIM_WALK:
+ s_controls.playerLegs = LEGS_WALK;
+ break;
+
+ case ANIM_BACK:
+ s_controls.playerLegs = LEGS_BACK;
+ break;
+
+ case ANIM_JUMP:
+ s_controls.playerLegs = LEGS_JUMP;
+ break;
+
+ case ANIM_CROUCH:
+ s_controls.playerLegs = LEGS_IDLECR;
+ break;
+
+ case ANIM_TURNLEFT:
+ s_controls.playerViewangles[YAW] += 90;
+ break;
+
+ case ANIM_TURNRIGHT:
+ s_controls.playerViewangles[YAW] -= 90;
+ break;
+
+ case ANIM_STEPLEFT:
+ s_controls.playerLegs = LEGS_WALK;
+ s_controls.playerMoveangles[YAW] = s_controls.playerViewangles[YAW] + 90;
+ break;
+
+ case ANIM_STEPRIGHT:
+ s_controls.playerLegs = LEGS_WALK;
+ s_controls.playerMoveangles[YAW] = s_controls.playerViewangles[YAW] - 90;
+ break;
+
+ case ANIM_LOOKUP:
+ s_controls.playerViewangles[PITCH] = -45;
+ break;
+
+ case ANIM_LOOKDOWN:
+ s_controls.playerViewangles[PITCH] = 45;
+ break;
+
+ case ANIM_WEAPON1:
+ s_controls.playerWeapon = WP_GAUNTLET;
+ break;
+
+ case ANIM_WEAPON2:
+ s_controls.playerWeapon = WP_MACHINEGUN;
+ break;
+
+ case ANIM_WEAPON3:
+ s_controls.playerWeapon = WP_SHOTGUN;
+ break;
+
+ case ANIM_WEAPON4:
+ s_controls.playerWeapon = WP_GRENADE_LAUNCHER;
+ break;
+
+ case ANIM_WEAPON5:
+ s_controls.playerWeapon = WP_ROCKET_LAUNCHER;
+ break;
+
+ case ANIM_WEAPON6:
+ s_controls.playerWeapon = WP_LIGHTNING;
+ break;
+
+ case ANIM_WEAPON7:
+ s_controls.playerWeapon = WP_RAILGUN;
+ break;
+
+ case ANIM_WEAPON8:
+ s_controls.playerWeapon = WP_PLASMAGUN;
+ break;
+
+ case ANIM_WEAPON9:
+ s_controls.playerWeapon = WP_BFG;
+ break;
+
+ case ANIM_WEAPON10:
+ s_controls.playerWeapon = WP_GRAPPLING_HOOK;
+ break;
+
+ case ANIM_ATTACK:
+ s_controls.playerTorso = TORSO_ATTACK;
+ break;
+
+ case ANIM_GESTURE:
+ s_controls.playerTorso = TORSO_GESTURE;
+ break;
+
+ case ANIM_DIE:
+ s_controls.playerLegs = BOTH_DEATH1;
+ s_controls.playerTorso = BOTH_DEATH1;
+ s_controls.playerWeapon = WP_NONE;
+ break;
+
+ case ANIM_CHAT:
+ s_controls.playerChat = qtrue;
+ break;
+
+ default:
+ break;
+ }
+
+ UI_PlayerInfo_SetInfo( &s_controls.playerinfo, s_controls.playerLegs, s_controls.playerTorso, s_controls.playerViewangles, s_controls.playerMoveangles, s_controls.playerWeapon, s_controls.playerChat );
+}
+
+
+/*
+=================
+Controls_Update
+=================
+*/
+static void Controls_Update( void ) {
+ int i;
+ int j;
+ int y;
+ menucommon_s **controls;
+ menucommon_s *control;
+
+ // disable all controls in all groups
+ for( i = 0; i < C_MAX; i++ ) {
+ controls = g_controls[i];
+ for( j = 0; (control = controls[j]) ; j++ ) {
+ control->flags |= (QMF_HIDDEN|QMF_INACTIVE);
+ }
+ }
+
+ controls = g_controls[s_controls.section];
+
+ // enable controls in active group (and count number of items for vertical centering)
+ for( j = 0; (control = controls[j]) ; j++ ) {
+ control->flags &= ~(QMF_GRAYED|QMF_HIDDEN|QMF_INACTIVE);
+ }
+
+ // position controls
+ y = ( SCREEN_HEIGHT - j * SMALLCHAR_HEIGHT ) / 2;
+ for( j = 0; (control = controls[j]) ; j++, y += SMALLCHAR_HEIGHT ) {
+ control->x = 320;
+ control->y = y;
+ control->left = 320 - 19*SMALLCHAR_WIDTH;
+ control->right = 320 + 21*SMALLCHAR_WIDTH;
+ control->top = y;
+ control->bottom = y + SMALLCHAR_HEIGHT;
+ }
+
+ if( s_controls.waitingforkey ) {
+ // disable everybody
+ for( i = 0; i < s_controls.menu.nitems; i++ ) {
+ ((menucommon_s*)(s_controls.menu.items[i]))->flags |= QMF_GRAYED;
+ }
+
+ // enable action item
+ ((menucommon_s*)(s_controls.menu.items[s_controls.menu.cursor]))->flags &= ~QMF_GRAYED;
+
+ // don't gray out player's name
+ s_controls.name.generic.flags &= ~QMF_GRAYED;
+
+ return;
+ }
+
+ // enable everybody
+ for( i = 0; i < s_controls.menu.nitems; i++ ) {
+ ((menucommon_s*)(s_controls.menu.items[i]))->flags &= ~QMF_GRAYED;
+ }
+
+ // makes sure flags are right on the group selection controls
+ s_controls.looking.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ s_controls.movement.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ s_controls.weapons.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ s_controls.misc.generic.flags &= ~(QMF_GRAYED|QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+
+ s_controls.looking.generic.flags |= QMF_PULSEIFFOCUS;
+ s_controls.movement.generic.flags |= QMF_PULSEIFFOCUS;
+ s_controls.weapons.generic.flags |= QMF_PULSEIFFOCUS;
+ s_controls.misc.generic.flags |= QMF_PULSEIFFOCUS;
+
+ // set buttons
+ switch( s_controls.section ) {
+ case C_MOVEMENT:
+ s_controls.movement.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.movement.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ break;
+
+ case C_LOOKING:
+ s_controls.looking.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.looking.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ break;
+
+ case C_WEAPONS:
+ s_controls.weapons.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.weapons.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ break;
+
+ case C_MISC:
+ s_controls.misc.generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_controls.misc.generic.flags |= (QMF_HIGHLIGHT|QMF_HIGHLIGHT_IF_FOCUS);
+ break;
+ }
+}
+
+
+/*
+=================
+Controls_DrawKeyBinding
+=================
+*/
+static void Controls_DrawKeyBinding( void *self )
+{
+ menuaction_s* a;
+ int x;
+ int y;
+ int b1;
+ int b2;
+ qboolean c;
+ char name[32];
+ char name2[32];
+
+ a = (menuaction_s*) self;
+
+ x = a->generic.x;
+ y = a->generic.y;
+
+ c = (Menu_ItemAtCursor( a->generic.parent ) == a);
+
+ b1 = g_bindings[a->generic.id].bind1;
+ if (b1 == -1)
+ strcpy(name,"???");
+ else
+ {
+ trap_Key_KeynumToStringBuf( b1, name, 32 );
+ Q_strupr(name);
+
+ b2 = g_bindings[a->generic.id].bind2;
+ if (b2 != -1)
+ {
+ trap_Key_KeynumToStringBuf( b2, name2, 32 );
+ Q_strupr(name2);
+
+ strcat( name, " or " );
+ strcat( name, name2 );
+ }
+ }
+
+ if (c)
+ {
+ UI_FillRect( a->generic.left, a->generic.top, a->generic.right-a->generic.left+1, a->generic.bottom-a->generic.top+1, listbar_color );
+
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, g_bindings[a->generic.id].label, UI_RIGHT|UI_SMALLFONT, text_color_highlight );
+ UI_DrawString( x + SMALLCHAR_WIDTH, y, name, UI_LEFT|UI_SMALLFONT|UI_PULSE, text_color_highlight );
+
+ if (s_controls.waitingforkey)
+ {
+ UI_DrawChar( x, y, '=', UI_CENTER|UI_BLINK|UI_SMALLFONT, text_color_highlight);
+ UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Waiting for new key ... ESCAPE to cancel", UI_SMALLFONT|UI_CENTER|UI_PULSE, colorWhite );
+ }
+ else
+ {
+ UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, text_color_highlight);
+ UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.78, "Press ENTER or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite );
+ UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.82, "Press BACKSPACE to clear", UI_SMALLFONT|UI_CENTER, colorWhite );
+ }
+ }
+ else
+ {
+ if (a->generic.flags & QMF_GRAYED)
+ {
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, g_bindings[a->generic.id].label, UI_RIGHT|UI_SMALLFONT, text_color_disabled );
+ UI_DrawString( x + SMALLCHAR_WIDTH, y, name, UI_LEFT|UI_SMALLFONT, text_color_disabled );
+ }
+ else
+ {
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, g_bindings[a->generic.id].label, UI_RIGHT|UI_SMALLFONT, controls_binding_color );
+ UI_DrawString( x + SMALLCHAR_WIDTH, y, name, UI_LEFT|UI_SMALLFONT, controls_binding_color );
+ }
+ }
+}
+
+/*
+=================
+Controls_StatusBar
+=================
+*/
+static void Controls_StatusBar( void *self )
+{
+ UI_DrawString(SCREEN_WIDTH * 0.50, SCREEN_HEIGHT * 0.80, "Use Arrow Keys or CLICK to change", UI_SMALLFONT|UI_CENTER, colorWhite );
+}
+
+
+/*
+=================
+Controls_DrawPlayer
+=================
+*/
+static void Controls_DrawPlayer( void *self ) {
+ menubitmap_s *b;
+ char buf[MAX_QPATH];
+
+ trap_Cvar_VariableStringBuffer( "model", buf, sizeof( buf ) );
+ if ( strcmp( buf, s_controls.playerModel ) != 0 ) {
+ UI_PlayerInfo_SetModel( &s_controls.playerinfo, buf );
+ strcpy( s_controls.playerModel, buf );
+ Controls_UpdateModel( ANIM_IDLE );
+ }
+
+ b = (menubitmap_s*) self;
+ UI_DrawPlayer( b->generic.x, b->generic.y, b->width, b->height, &s_controls.playerinfo, uis.realtime/2 );
+}
+
+
+/*
+=================
+Controls_GetKeyAssignment
+=================
+*/
+static void Controls_GetKeyAssignment (char *command, int *twokeys)
+{
+ int count;
+ int j;
+ char b[256];
+
+ twokeys[0] = twokeys[1] = -1;
+ count = 0;
+
+ for ( j = 0; j < 256; j++ )
+ {
+ trap_Key_GetBindingBuf( j, b, 256 );
+ if ( *b == 0 ) {
+ continue;
+ }
+ if ( !Q_stricmp( b, command ) ) {
+ twokeys[count] = j;
+ count++;
+ if (count == 2)
+ break;
+ }
+ }
+}
+
+/*
+=================
+Controls_GetConfig
+=================
+*/
+static void Controls_GetConfig( void )
+{
+ int i;
+ int twokeys[2];
+ bind_t* bindptr;
+
+ // put the bindings into a local store
+ bindptr = g_bindings;
+
+ // iterate each command, get its numeric binding
+ for (i=0; ;i++,bindptr++)
+ {
+ if (!bindptr->label)
+ break;
+
+ Controls_GetKeyAssignment(bindptr->command, twokeys);
+
+ bindptr->bind1 = twokeys[0];
+ bindptr->bind2 = twokeys[1];
+ }
+
+ s_controls.invertmouse.curvalue = Controls_GetCvarValue( "m_pitch" ) < 0;
+ s_controls.smoothmouse.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "m_filter" ) );
+ s_controls.alwaysrun.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_run" ) );
+ s_controls.autoswitch.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cg_autoswitch" ) );
+ s_controls.sensitivity.curvalue = UI_ClampCvar( 2, 30, Controls_GetCvarValue( "sensitivity" ) );
+ s_controls.joyenable.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "in_joystick" ) );
+ s_controls.joythreshold.curvalue = UI_ClampCvar( 0.05f, 0.75f, Controls_GetCvarValue( "joy_threshold" ) );
+ s_controls.freelook.curvalue = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_freelook" ) );
+}
+
+/*
+=================
+Controls_SetConfig
+=================
+*/
+static void Controls_SetConfig( void )
+{
+ int i;
+ bind_t* bindptr;
+
+ // set the bindings from the local store
+ bindptr = g_bindings;
+
+ // iterate each command, get its numeric binding
+ for (i=0; ;i++,bindptr++)
+ {
+ if (!bindptr->label)
+ break;
+
+ if (bindptr->bind1 != -1)
+ {
+ trap_Key_SetBinding( bindptr->bind1, bindptr->command );
+
+ if (bindptr->bind2 != -1)
+ trap_Key_SetBinding( bindptr->bind2, bindptr->command );
+ }
+ }
+
+ if ( s_controls.invertmouse.curvalue )
+ trap_Cvar_SetValue( "m_pitch", -fabs( trap_Cvar_VariableValue( "m_pitch" ) ) );
+ else
+ trap_Cvar_SetValue( "m_pitch", fabs( trap_Cvar_VariableValue( "m_pitch" ) ) );
+
+ trap_Cvar_SetValue( "m_filter", s_controls.smoothmouse.curvalue );
+ trap_Cvar_SetValue( "cl_run", s_controls.alwaysrun.curvalue );
+ trap_Cvar_SetValue( "cg_autoswitch", s_controls.autoswitch.curvalue );
+ trap_Cvar_SetValue( "sensitivity", s_controls.sensitivity.curvalue );
+ trap_Cvar_SetValue( "in_joystick", s_controls.joyenable.curvalue );
+ trap_Cvar_SetValue( "joy_threshold", s_controls.joythreshold.curvalue );
+ trap_Cvar_SetValue( "cl_freelook", s_controls.freelook.curvalue );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "in_restart\n" );
+}
+
+/*
+=================
+Controls_SetDefaults
+=================
+*/
+static void Controls_SetDefaults( void )
+{
+ int i;
+ bind_t* bindptr;
+
+ // set the bindings from the local store
+ bindptr = g_bindings;
+
+ // iterate each command, set its default binding
+ for (i=0; ;i++,bindptr++)
+ {
+ if (!bindptr->label)
+ break;
+
+ bindptr->bind1 = bindptr->defaultbind1;
+ bindptr->bind2 = bindptr->defaultbind2;
+ }
+
+ s_controls.invertmouse.curvalue = Controls_GetCvarDefault( "m_pitch" ) < 0;
+ s_controls.smoothmouse.curvalue = Controls_GetCvarDefault( "m_filter" );
+ s_controls.alwaysrun.curvalue = Controls_GetCvarDefault( "cl_run" );
+ s_controls.autoswitch.curvalue = Controls_GetCvarDefault( "cg_autoswitch" );
+ s_controls.sensitivity.curvalue = Controls_GetCvarDefault( "sensitivity" );
+ s_controls.joyenable.curvalue = Controls_GetCvarDefault( "in_joystick" );
+ s_controls.joythreshold.curvalue = Controls_GetCvarDefault( "joy_threshold" );
+ s_controls.freelook.curvalue = Controls_GetCvarDefault( "cl_freelook" );
+}
+
+/*
+=================
+Controls_MenuKey
+=================
+*/
+static sfxHandle_t Controls_MenuKey( int key )
+{
+ int id;
+ int i;
+ qboolean found;
+ bind_t* bindptr;
+ found = qfalse;
+
+ if (!s_controls.waitingforkey)
+ {
+ switch (key)
+ {
+ case K_BACKSPACE:
+ case K_DEL:
+ case K_KP_DEL:
+ key = -1;
+ break;
+
+ case K_MOUSE2:
+ case K_ESCAPE:
+ if (s_controls.changesmade)
+ Controls_SetConfig();
+ goto ignorekey;
+
+ default:
+ goto ignorekey;
+ }
+ }
+ else
+ {
+ if (key & K_CHAR_FLAG)
+ goto ignorekey;
+
+ switch (key)
+ {
+ case K_ESCAPE:
+ s_controls.waitingforkey = qfalse;
+ Controls_Update();
+ return (menu_out_sound);
+
+ case '`':
+ goto ignorekey;
+ }
+ }
+
+ s_controls.changesmade = qtrue;
+
+ if (key != -1)
+ {
+ // remove from any other bind
+ bindptr = g_bindings;
+ for (i=0; ;i++,bindptr++)
+ {
+ if (!bindptr->label)
+ break;
+
+ if (bindptr->bind2 == key)
+ bindptr->bind2 = -1;
+
+ if (bindptr->bind1 == key)
+ {
+ bindptr->bind1 = bindptr->bind2;
+ bindptr->bind2 = -1;
+ }
+ }
+ }
+
+ // assign key to local store
+ id = ((menucommon_s*)(s_controls.menu.items[s_controls.menu.cursor]))->id;
+ bindptr = g_bindings;
+ for (i=0; ;i++,bindptr++)
+ {
+ if (!bindptr->label)
+ break;
+
+ if (bindptr->id == id)
+ {
+ found = qtrue;
+ if (key == -1)
+ {
+ if( bindptr->bind1 != -1 ) {
+ trap_Key_SetBinding( bindptr->bind1, "" );
+ bindptr->bind1 = -1;
+ }
+ if( bindptr->bind2 != -1 ) {
+ trap_Key_SetBinding( bindptr->bind2, "" );
+ bindptr->bind2 = -1;
+ }
+ }
+ else if (bindptr->bind1 == -1) {
+ bindptr->bind1 = key;
+ }
+ else if (bindptr->bind1 != key && bindptr->bind2 == -1) {
+ bindptr->bind2 = key;
+ }
+ else
+ {
+ trap_Key_SetBinding( bindptr->bind1, "" );
+ trap_Key_SetBinding( bindptr->bind2, "" );
+ bindptr->bind1 = key;
+ bindptr->bind2 = -1;
+ }
+ break;
+ }
+ }
+
+ s_controls.waitingforkey = qfalse;
+
+ if (found)
+ {
+ Controls_Update();
+ return (menu_out_sound);
+ }
+
+ignorekey:
+ return Menu_DefaultKey( &s_controls.menu, key );
+}
+
+/*
+=================
+Controls_ResetDefaults_Action
+=================
+*/
+static void Controls_ResetDefaults_Action( qboolean result ) {
+ if( !result ) {
+ return;
+ }
+
+ s_controls.changesmade = qtrue;
+ Controls_SetDefaults();
+ Controls_Update();
+}
+
+/*
+=================
+Controls_ResetDefaults_Draw
+=================
+*/
+static void Controls_ResetDefaults_Draw( void ) {
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This will reset all", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "controls to their default values.", UI_CENTER|UI_SMALLFONT, color_yellow );
+}
+
+/*
+=================
+Controls_MenuEvent
+=================
+*/
+static void Controls_MenuEvent( void* ptr, int event )
+{
+ switch (((menucommon_s*)ptr)->id)
+ {
+ case ID_MOVEMENT:
+ if (event == QM_ACTIVATED)
+ {
+ s_controls.section = C_MOVEMENT;
+ Controls_Update();
+ }
+ break;
+
+ case ID_LOOKING:
+ if (event == QM_ACTIVATED)
+ {
+ s_controls.section = C_LOOKING;
+ Controls_Update();
+ }
+ break;
+
+ case ID_WEAPONS:
+ if (event == QM_ACTIVATED)
+ {
+ s_controls.section = C_WEAPONS;
+ Controls_Update();
+ }
+ break;
+
+ case ID_MISC:
+ if (event == QM_ACTIVATED)
+ {
+ s_controls.section = C_MISC;
+ Controls_Update();
+ }
+ break;
+
+ case ID_DEFAULTS:
+ if (event == QM_ACTIVATED)
+ {
+ UI_ConfirmMenu( "SET TO DEFAULTS?", Controls_ResetDefaults_Draw, Controls_ResetDefaults_Action );
+ }
+ break;
+
+ case ID_BACK:
+ if (event == QM_ACTIVATED)
+ {
+ if (s_controls.changesmade)
+ Controls_SetConfig();
+ UI_PopMenu();
+ }
+ break;
+
+ case ID_SAVEANDEXIT:
+ if (event == QM_ACTIVATED)
+ {
+ Controls_SetConfig();
+ UI_PopMenu();
+ }
+ break;
+
+ case ID_EXIT:
+ if (event == QM_ACTIVATED)
+ {
+ UI_PopMenu();
+ }
+ break;
+
+ case ID_FREELOOK:
+ case ID_MOUSESPEED:
+ case ID_INVERTMOUSE:
+ case ID_SMOOTHMOUSE:
+ case ID_ALWAYSRUN:
+ case ID_AUTOSWITCH:
+ case ID_JOYENABLE:
+ case ID_JOYTHRESHOLD:
+ if (event == QM_ACTIVATED)
+ {
+ s_controls.changesmade = qtrue;
+ }
+ break;
+ }
+}
+
+/*
+=================
+Controls_ActionEvent
+=================
+*/
+static void Controls_ActionEvent( void* ptr, int event )
+{
+ if (event == QM_LOSTFOCUS)
+ {
+ Controls_UpdateModel( ANIM_IDLE );
+ }
+ else if (event == QM_GOTFOCUS)
+ {
+ Controls_UpdateModel( g_bindings[((menucommon_s*)ptr)->id].anim );
+ }
+ else if ((event == QM_ACTIVATED) && !s_controls.waitingforkey)
+ {
+ s_controls.waitingforkey = 1;
+ Controls_Update();
+ }
+}
+
+/*
+=================
+Controls_InitModel
+=================
+*/
+static void Controls_InitModel( void )
+{
+ memset( &s_controls.playerinfo, 0, sizeof(playerInfo_t) );
+
+ UI_PlayerInfo_SetModel( &s_controls.playerinfo, UI_Cvar_VariableString( "model" ) );
+
+ Controls_UpdateModel( ANIM_IDLE );
+}
+
+/*
+=================
+Controls_InitWeapons
+=================
+*/
+static void Controls_InitWeapons( void ) {
+ gitem_t * item;
+
+ for ( item = bg_itemlist + 1 ; item->classname ; item++ ) {
+ if ( item->giType != IT_WEAPON ) {
+ continue;
+ }
+ trap_R_RegisterModel( item->world_model[0] );
+ }
+}
+
+/*
+=================
+Controls_MenuInit
+=================
+*/
+static void Controls_MenuInit( void )
+{
+ static char playername[32];
+
+ // zero set all our globals
+ memset( &s_controls, 0 ,sizeof(controls_t) );
+
+ Controls_Cache();
+
+ s_controls.menu.key = Controls_MenuKey;
+ s_controls.menu.wrapAround = qtrue;
+ s_controls.menu.fullscreen = qtrue;
+
+ s_controls.banner.generic.type = MTYPE_BTEXT;
+ s_controls.banner.generic.flags = QMF_CENTER_JUSTIFY;
+ s_controls.banner.generic.x = 320;
+ s_controls.banner.generic.y = 16;
+ s_controls.banner.string = "CONTROLS";
+ s_controls.banner.color = color_white;
+ s_controls.banner.style = UI_CENTER;
+
+ s_controls.framel.generic.type = MTYPE_BITMAP;
+ s_controls.framel.generic.name = ART_FRAMEL;
+ s_controls.framel.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_controls.framel.generic.x = 0;
+ s_controls.framel.generic.y = 78;
+ s_controls.framel.width = 256;
+ s_controls.framel.height = 329;
+
+ s_controls.framer.generic.type = MTYPE_BITMAP;
+ s_controls.framer.generic.name = ART_FRAMER;
+ s_controls.framer.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_controls.framer.generic.x = 376;
+ s_controls.framer.generic.y = 76;
+ s_controls.framer.width = 256;
+ s_controls.framer.height = 334;
+
+ s_controls.looking.generic.type = MTYPE_PTEXT;
+ s_controls.looking.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_controls.looking.generic.id = ID_LOOKING;
+ s_controls.looking.generic.callback = Controls_MenuEvent;
+ s_controls.looking.generic.x = 152;
+ s_controls.looking.generic.y = 240 - 2 * PROP_HEIGHT;
+ s_controls.looking.string = "LOOK";
+ s_controls.looking.style = UI_RIGHT;
+ s_controls.looking.color = color_red;
+
+ s_controls.movement.generic.type = MTYPE_PTEXT;
+ s_controls.movement.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_controls.movement.generic.id = ID_MOVEMENT;
+ s_controls.movement.generic.callback = Controls_MenuEvent;
+ s_controls.movement.generic.x = 152;
+ s_controls.movement.generic.y = 240 - PROP_HEIGHT;
+ s_controls.movement.string = "MOVE";
+ s_controls.movement.style = UI_RIGHT;
+ s_controls.movement.color = color_red;
+
+ s_controls.weapons.generic.type = MTYPE_PTEXT;
+ s_controls.weapons.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_controls.weapons.generic.id = ID_WEAPONS;
+ s_controls.weapons.generic.callback = Controls_MenuEvent;
+ s_controls.weapons.generic.x = 152;
+ s_controls.weapons.generic.y = 240;
+ s_controls.weapons.string = "SHOOT";
+ s_controls.weapons.style = UI_RIGHT;
+ s_controls.weapons.color = color_red;
+
+ s_controls.misc.generic.type = MTYPE_PTEXT;
+ s_controls.misc.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_controls.misc.generic.id = ID_MISC;
+ s_controls.misc.generic.callback = Controls_MenuEvent;
+ s_controls.misc.generic.x = 152;
+ s_controls.misc.generic.y = 240 + PROP_HEIGHT;
+ s_controls.misc.string = "MISC";
+ s_controls.misc.style = UI_RIGHT;
+ s_controls.misc.color = color_red;
+
+ s_controls.back.generic.type = MTYPE_BITMAP;
+ s_controls.back.generic.name = ART_BACK0;
+ s_controls.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_controls.back.generic.x = 0;
+ s_controls.back.generic.y = 480-64;
+ s_controls.back.generic.id = ID_BACK;
+ s_controls.back.generic.callback = Controls_MenuEvent;
+ s_controls.back.width = 128;
+ s_controls.back.height = 64;
+ s_controls.back.focuspic = ART_BACK1;
+
+ s_controls.player.generic.type = MTYPE_BITMAP;
+ s_controls.player.generic.flags = QMF_INACTIVE;
+ s_controls.player.generic.ownerdraw = Controls_DrawPlayer;
+ s_controls.player.generic.x = 400;
+ s_controls.player.generic.y = -40;
+ s_controls.player.width = 32*10;
+ s_controls.player.height = 56*10;
+
+ s_controls.walkforward.generic.type = MTYPE_ACTION;
+ s_controls.walkforward.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.walkforward.generic.callback = Controls_ActionEvent;
+ s_controls.walkforward.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.walkforward.generic.id = ID_FORWARD;
+
+ s_controls.backpedal.generic.type = MTYPE_ACTION;
+ s_controls.backpedal.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.backpedal.generic.callback = Controls_ActionEvent;
+ s_controls.backpedal.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.backpedal.generic.id = ID_BACKPEDAL;
+
+ s_controls.stepleft.generic.type = MTYPE_ACTION;
+ s_controls.stepleft.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.stepleft.generic.callback = Controls_ActionEvent;
+ s_controls.stepleft.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.stepleft.generic.id = ID_MOVELEFT;
+
+ s_controls.stepright.generic.type = MTYPE_ACTION;
+ s_controls.stepright.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.stepright.generic.callback = Controls_ActionEvent;
+ s_controls.stepright.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.stepright.generic.id = ID_MOVERIGHT;
+
+ s_controls.moveup.generic.type = MTYPE_ACTION;
+ s_controls.moveup.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.moveup.generic.callback = Controls_ActionEvent;
+ s_controls.moveup.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.moveup.generic.id = ID_MOVEUP;
+
+ s_controls.movedown.generic.type = MTYPE_ACTION;
+ s_controls.movedown.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.movedown.generic.callback = Controls_ActionEvent;
+ s_controls.movedown.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.movedown.generic.id = ID_MOVEDOWN;
+
+ s_controls.turnleft.generic.type = MTYPE_ACTION;
+ s_controls.turnleft.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.turnleft.generic.callback = Controls_ActionEvent;
+ s_controls.turnleft.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.turnleft.generic.id = ID_LEFT;
+
+ s_controls.turnright.generic.type = MTYPE_ACTION;
+ s_controls.turnright.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.turnright.generic.callback = Controls_ActionEvent;
+ s_controls.turnright.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.turnright.generic.id = ID_RIGHT;
+
+ s_controls.sidestep.generic.type = MTYPE_ACTION;
+ s_controls.sidestep.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.sidestep.generic.callback = Controls_ActionEvent;
+ s_controls.sidestep.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.sidestep.generic.id = ID_STRAFE;
+
+ s_controls.run.generic.type = MTYPE_ACTION;
+ s_controls.run.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.run.generic.callback = Controls_ActionEvent;
+ s_controls.run.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.run.generic.id = ID_SPEED;
+
+ s_controls.chainsaw.generic.type = MTYPE_ACTION;
+ s_controls.chainsaw.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.chainsaw.generic.callback = Controls_ActionEvent;
+ s_controls.chainsaw.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.chainsaw.generic.id = ID_WEAPON1;
+
+ s_controls.machinegun.generic.type = MTYPE_ACTION;
+ s_controls.machinegun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.machinegun.generic.callback = Controls_ActionEvent;
+ s_controls.machinegun.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.machinegun.generic.id = ID_WEAPON2;
+
+ s_controls.shotgun.generic.type = MTYPE_ACTION;
+ s_controls.shotgun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.shotgun.generic.callback = Controls_ActionEvent;
+ s_controls.shotgun.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.shotgun.generic.id = ID_WEAPON3;
+
+ s_controls.grenadelauncher.generic.type = MTYPE_ACTION;
+ s_controls.grenadelauncher.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.grenadelauncher.generic.callback = Controls_ActionEvent;
+ s_controls.grenadelauncher.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.grenadelauncher.generic.id = ID_WEAPON4;
+
+ s_controls.rocketlauncher.generic.type = MTYPE_ACTION;
+ s_controls.rocketlauncher.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.rocketlauncher.generic.callback = Controls_ActionEvent;
+ s_controls.rocketlauncher.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.rocketlauncher.generic.id = ID_WEAPON5;
+
+ s_controls.lightning.generic.type = MTYPE_ACTION;
+ s_controls.lightning.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.lightning.generic.callback = Controls_ActionEvent;
+ s_controls.lightning.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.lightning.generic.id = ID_WEAPON6;
+
+ s_controls.railgun.generic.type = MTYPE_ACTION;
+ s_controls.railgun.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.railgun.generic.callback = Controls_ActionEvent;
+ s_controls.railgun.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.railgun.generic.id = ID_WEAPON7;
+
+ s_controls.plasma.generic.type = MTYPE_ACTION;
+ s_controls.plasma.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.plasma.generic.callback = Controls_ActionEvent;
+ s_controls.plasma.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.plasma.generic.id = ID_WEAPON8;
+
+ s_controls.bfg.generic.type = MTYPE_ACTION;
+ s_controls.bfg.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.bfg.generic.callback = Controls_ActionEvent;
+ s_controls.bfg.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.bfg.generic.id = ID_WEAPON9;
+
+ s_controls.attack.generic.type = MTYPE_ACTION;
+ s_controls.attack.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.attack.generic.callback = Controls_ActionEvent;
+ s_controls.attack.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.attack.generic.id = ID_ATTACK;
+
+ s_controls.prevweapon.generic.type = MTYPE_ACTION;
+ s_controls.prevweapon.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.prevweapon.generic.callback = Controls_ActionEvent;
+ s_controls.prevweapon.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.prevweapon.generic.id = ID_WEAPPREV;
+
+ s_controls.nextweapon.generic.type = MTYPE_ACTION;
+ s_controls.nextweapon.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.nextweapon.generic.callback = Controls_ActionEvent;
+ s_controls.nextweapon.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.nextweapon.generic.id = ID_WEAPNEXT;
+
+ s_controls.lookup.generic.type = MTYPE_ACTION;
+ s_controls.lookup.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.lookup.generic.callback = Controls_ActionEvent;
+ s_controls.lookup.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.lookup.generic.id = ID_LOOKUP;
+
+ s_controls.lookdown.generic.type = MTYPE_ACTION;
+ s_controls.lookdown.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.lookdown.generic.callback = Controls_ActionEvent;
+ s_controls.lookdown.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.lookdown.generic.id = ID_LOOKDOWN;
+
+ s_controls.mouselook.generic.type = MTYPE_ACTION;
+ s_controls.mouselook.generic.flags = QMF_LEFT_JUSTIFY|QMF_HIGHLIGHT_IF_FOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.mouselook.generic.callback = Controls_ActionEvent;
+ s_controls.mouselook.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.mouselook.generic.id = ID_MOUSELOOK;
+
+ s_controls.freelook.generic.type = MTYPE_RADIOBUTTON;
+ s_controls.freelook.generic.flags = QMF_SMALLFONT;
+ s_controls.freelook.generic.x = SCREEN_WIDTH/2;
+ s_controls.freelook.generic.name = "free look";
+ s_controls.freelook.generic.id = ID_FREELOOK;
+ s_controls.freelook.generic.callback = Controls_MenuEvent;
+ s_controls.freelook.generic.statusbar = Controls_StatusBar;
+
+ s_controls.centerview.generic.type = MTYPE_ACTION;
+ s_controls.centerview.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.centerview.generic.callback = Controls_ActionEvent;
+ s_controls.centerview.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.centerview.generic.id = ID_CENTERVIEW;
+
+ s_controls.zoomview.generic.type = MTYPE_ACTION;
+ s_controls.zoomview.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.zoomview.generic.callback = Controls_ActionEvent;
+ s_controls.zoomview.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.zoomview.generic.id = ID_ZOOMVIEW;
+
+ s_controls.useitem.generic.type = MTYPE_ACTION;
+ s_controls.useitem.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.useitem.generic.callback = Controls_ActionEvent;
+ s_controls.useitem.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.useitem.generic.id = ID_USEITEM;
+
+ s_controls.showscores.generic.type = MTYPE_ACTION;
+ s_controls.showscores.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.showscores.generic.callback = Controls_ActionEvent;
+ s_controls.showscores.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.showscores.generic.id = ID_SHOWSCORES;
+
+ s_controls.invertmouse.generic.type = MTYPE_RADIOBUTTON;
+ s_controls.invertmouse.generic.flags = QMF_SMALLFONT;
+ s_controls.invertmouse.generic.x = SCREEN_WIDTH/2;
+ s_controls.invertmouse.generic.name = "invert mouse";
+ s_controls.invertmouse.generic.id = ID_INVERTMOUSE;
+ s_controls.invertmouse.generic.callback = Controls_MenuEvent;
+ s_controls.invertmouse.generic.statusbar = Controls_StatusBar;
+
+ s_controls.smoothmouse.generic.type = MTYPE_RADIOBUTTON;
+ s_controls.smoothmouse.generic.flags = QMF_SMALLFONT;
+ s_controls.smoothmouse.generic.x = SCREEN_WIDTH/2;
+ s_controls.smoothmouse.generic.name = "smooth mouse";
+ s_controls.smoothmouse.generic.id = ID_SMOOTHMOUSE;
+ s_controls.smoothmouse.generic.callback = Controls_MenuEvent;
+ s_controls.smoothmouse.generic.statusbar = Controls_StatusBar;
+
+ s_controls.alwaysrun.generic.type = MTYPE_RADIOBUTTON;
+ s_controls.alwaysrun.generic.flags = QMF_SMALLFONT;
+ s_controls.alwaysrun.generic.x = SCREEN_WIDTH/2;
+ s_controls.alwaysrun.generic.name = "always run";
+ s_controls.alwaysrun.generic.id = ID_ALWAYSRUN;
+ s_controls.alwaysrun.generic.callback = Controls_MenuEvent;
+ s_controls.alwaysrun.generic.statusbar = Controls_StatusBar;
+
+ s_controls.autoswitch.generic.type = MTYPE_RADIOBUTTON;
+ s_controls.autoswitch.generic.flags = QMF_SMALLFONT;
+ s_controls.autoswitch.generic.x = SCREEN_WIDTH/2;
+ s_controls.autoswitch.generic.name = "autoswitch weapons";
+ s_controls.autoswitch.generic.id = ID_AUTOSWITCH;
+ s_controls.autoswitch.generic.callback = Controls_MenuEvent;
+ s_controls.autoswitch.generic.statusbar = Controls_StatusBar;
+
+ s_controls.sensitivity.generic.type = MTYPE_SLIDER;
+ s_controls.sensitivity.generic.x = SCREEN_WIDTH/2;
+ s_controls.sensitivity.generic.flags = QMF_SMALLFONT;
+ s_controls.sensitivity.generic.name = "mouse speed";
+ s_controls.sensitivity.generic.id = ID_MOUSESPEED;
+ s_controls.sensitivity.generic.callback = Controls_MenuEvent;
+ s_controls.sensitivity.minvalue = 2;
+ s_controls.sensitivity.maxvalue = 30;
+ s_controls.sensitivity.generic.statusbar = Controls_StatusBar;
+
+ s_controls.gesture.generic.type = MTYPE_ACTION;
+ s_controls.gesture.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.gesture.generic.callback = Controls_ActionEvent;
+ s_controls.gesture.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.gesture.generic.id = ID_GESTURE;
+
+ s_controls.chat.generic.type = MTYPE_ACTION;
+ s_controls.chat.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.chat.generic.callback = Controls_ActionEvent;
+ s_controls.chat.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.chat.generic.id = ID_CHAT;
+
+ s_controls.chat2.generic.type = MTYPE_ACTION;
+ s_controls.chat2.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.chat2.generic.callback = Controls_ActionEvent;
+ s_controls.chat2.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.chat2.generic.id = ID_CHAT2;
+
+ s_controls.chat3.generic.type = MTYPE_ACTION;
+ s_controls.chat3.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.chat3.generic.callback = Controls_ActionEvent;
+ s_controls.chat3.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.chat3.generic.id = ID_CHAT3;
+
+ s_controls.chat4.generic.type = MTYPE_ACTION;
+ s_controls.chat4.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_GRAYED|QMF_HIDDEN;
+ s_controls.chat4.generic.callback = Controls_ActionEvent;
+ s_controls.chat4.generic.ownerdraw = Controls_DrawKeyBinding;
+ s_controls.chat4.generic.id = ID_CHAT4;
+
+ s_controls.joyenable.generic.type = MTYPE_RADIOBUTTON;
+ s_controls.joyenable.generic.flags = QMF_SMALLFONT;
+ s_controls.joyenable.generic.x = SCREEN_WIDTH/2;
+ s_controls.joyenable.generic.name = "joystick";
+ s_controls.joyenable.generic.id = ID_JOYENABLE;
+ s_controls.joyenable.generic.callback = Controls_MenuEvent;
+ s_controls.joyenable.generic.statusbar = Controls_StatusBar;
+
+ s_controls.joythreshold.generic.type = MTYPE_SLIDER;
+ s_controls.joythreshold.generic.x = SCREEN_WIDTH/2;
+ s_controls.joythreshold.generic.flags = QMF_SMALLFONT;
+ s_controls.joythreshold.generic.name = "joystick threshold";
+ s_controls.joythreshold.generic.id = ID_JOYTHRESHOLD;
+ s_controls.joythreshold.generic.callback = Controls_MenuEvent;
+ s_controls.joythreshold.minvalue = 0.05f;
+ s_controls.joythreshold.maxvalue = 0.75f;
+ s_controls.joythreshold.generic.statusbar = Controls_StatusBar;
+
+ s_controls.name.generic.type = MTYPE_PTEXT;
+ s_controls.name.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
+ s_controls.name.generic.x = 320;
+ s_controls.name.generic.y = 440;
+ s_controls.name.string = playername;
+ s_controls.name.style = UI_CENTER;
+ s_controls.name.color = text_color_normal;
+
+ Menu_AddItem( &s_controls.menu, &s_controls.banner );
+ Menu_AddItem( &s_controls.menu, &s_controls.framel );
+ Menu_AddItem( &s_controls.menu, &s_controls.framer );
+ Menu_AddItem( &s_controls.menu, &s_controls.player );
+ Menu_AddItem( &s_controls.menu, &s_controls.name );
+
+ Menu_AddItem( &s_controls.menu, &s_controls.looking );
+ Menu_AddItem( &s_controls.menu, &s_controls.movement );
+ Menu_AddItem( &s_controls.menu, &s_controls.weapons );
+ Menu_AddItem( &s_controls.menu, &s_controls.misc );
+
+ Menu_AddItem( &s_controls.menu, &s_controls.sensitivity );
+ Menu_AddItem( &s_controls.menu, &s_controls.smoothmouse );
+ Menu_AddItem( &s_controls.menu, &s_controls.invertmouse );
+ Menu_AddItem( &s_controls.menu, &s_controls.lookup );
+ Menu_AddItem( &s_controls.menu, &s_controls.lookdown );
+ Menu_AddItem( &s_controls.menu, &s_controls.mouselook );
+ Menu_AddItem( &s_controls.menu, &s_controls.freelook );
+ Menu_AddItem( &s_controls.menu, &s_controls.centerview );
+ Menu_AddItem( &s_controls.menu, &s_controls.zoomview );
+ Menu_AddItem( &s_controls.menu, &s_controls.joyenable );
+ Menu_AddItem( &s_controls.menu, &s_controls.joythreshold );
+
+ Menu_AddItem( &s_controls.menu, &s_controls.alwaysrun );
+ Menu_AddItem( &s_controls.menu, &s_controls.run );
+ Menu_AddItem( &s_controls.menu, &s_controls.walkforward );
+ Menu_AddItem( &s_controls.menu, &s_controls.backpedal );
+ Menu_AddItem( &s_controls.menu, &s_controls.stepleft );
+ Menu_AddItem( &s_controls.menu, &s_controls.stepright );
+ Menu_AddItem( &s_controls.menu, &s_controls.moveup );
+ Menu_AddItem( &s_controls.menu, &s_controls.movedown );
+ Menu_AddItem( &s_controls.menu, &s_controls.turnleft );
+ Menu_AddItem( &s_controls.menu, &s_controls.turnright );
+ Menu_AddItem( &s_controls.menu, &s_controls.sidestep );
+
+ Menu_AddItem( &s_controls.menu, &s_controls.attack );
+ Menu_AddItem( &s_controls.menu, &s_controls.nextweapon );
+ Menu_AddItem( &s_controls.menu, &s_controls.prevweapon );
+ Menu_AddItem( &s_controls.menu, &s_controls.autoswitch );
+ Menu_AddItem( &s_controls.menu, &s_controls.chainsaw );
+ Menu_AddItem( &s_controls.menu, &s_controls.machinegun );
+ Menu_AddItem( &s_controls.menu, &s_controls.shotgun );
+ Menu_AddItem( &s_controls.menu, &s_controls.grenadelauncher );
+ Menu_AddItem( &s_controls.menu, &s_controls.rocketlauncher );
+ Menu_AddItem( &s_controls.menu, &s_controls.lightning );
+ Menu_AddItem( &s_controls.menu, &s_controls.railgun );
+ Menu_AddItem( &s_controls.menu, &s_controls.plasma );
+ Menu_AddItem( &s_controls.menu, &s_controls.bfg );
+
+ Menu_AddItem( &s_controls.menu, &s_controls.showscores );
+ Menu_AddItem( &s_controls.menu, &s_controls.useitem );
+ Menu_AddItem( &s_controls.menu, &s_controls.gesture );
+ Menu_AddItem( &s_controls.menu, &s_controls.chat );
+ Menu_AddItem( &s_controls.menu, &s_controls.chat2 );
+ Menu_AddItem( &s_controls.menu, &s_controls.chat3 );
+ Menu_AddItem( &s_controls.menu, &s_controls.chat4 );
+
+ Menu_AddItem( &s_controls.menu, &s_controls.back );
+
+ trap_Cvar_VariableStringBuffer( "name", s_controls.name.string, 16 );
+ Q_CleanStr( s_controls.name.string );
+
+ // initialize the configurable cvars
+ Controls_InitCvars();
+
+ // initialize the current config
+ Controls_GetConfig();
+
+ // intialize the model
+ Controls_InitModel();
+
+ // intialize the weapons
+ Controls_InitWeapons ();
+
+ // initial default section
+ s_controls.section = C_LOOKING;
+
+ // update the ui
+ Controls_Update();
+}
+
+
+/*
+=================
+Controls_Cache
+=================
+*/
+void Controls_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+}
+
+
+/*
+=================
+UI_ControlsMenu
+=================
+*/
+void UI_ControlsMenu( void ) {
+ Controls_MenuInit();
+ UI_PushMenu( &s_controls.menu );
+}
diff --git a/code/q3_ui/ui_credits.c b/code/q3_ui/ui_credits.c
new file mode 100644
index 0000000..7baee61
--- /dev/null
+++ b/code/q3_ui/ui_credits.c
@@ -0,0 +1,181 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+CREDITS
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+typedef struct {
+ menuframework_s menu;
+ int frame;
+} creditsmenu_t;
+
+static creditsmenu_t s_credits;
+
+
+/*
+===============
+UI_CreditMenu_Draw_ioq3
+===============
+*/
+static void UI_CreditMenu_Draw_ioq3( void ) {
+ int y;
+ int i;
+
+ // These are all people that have made commits to Subversion, and thus
+ // probably incomplete.
+ // (These are in alphabetical order, for the defense of everyone's egos.)
+ static const char *names[] = {
+ "Tim Angus",
+ "Vincent Cojot",
+ "Ryan C. Gordon",
+ "Aaron Gyes",
+ "Ludwig Nussel",
+ "Julian Priestley",
+ "Scirocco Six",
+ "Thilo Schulz",
+ "Zachary J. Slater",
+ "Tony J. White",
+ "...and many, many others!", // keep this one last.
+ NULL
+ };
+
+ y = 12;
+ UI_DrawProportionalString( 320, y, "ioquake3 contributors:", UI_CENTER|UI_SMALLFONT, color_white );
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+
+ for (i = 0; names[i]; i++) {
+ UI_DrawProportionalString( 320, y, names[i], UI_CENTER|UI_SMALLFONT, color_white );
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ }
+
+ UI_DrawString( 320, 459, "http://www.ioquake3.org/", UI_CENTER|UI_SMALLFONT, color_red );
+}
+
+
+/*
+=================
+UI_CreditMenu_Key
+=================
+*/
+static sfxHandle_t UI_CreditMenu_Key( int key ) {
+ if( key & K_CHAR_FLAG ) {
+ return 0;
+ }
+
+ s_credits.frame++;
+ if (s_credits.frame == 1) {
+ s_credits.menu.draw = UI_CreditMenu_Draw_ioq3;
+ } else {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
+ }
+ return 0;
+}
+
+
+/*
+===============
+UI_CreditMenu_Draw
+===============
+*/
+static void UI_CreditMenu_Draw( void ) {
+ int y;
+
+ y = 12;
+ UI_DrawProportionalString( 320, y, "id Software is:", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Programming", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "John Carmack, Robert A. Duffy, Jim Dose'", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Art", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Adrian Carmack, Kevin Cloud,", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Kenneth Scott, Seneca Menard, Fred Nilsson", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Game Designer", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Graeme Devine", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Level Design", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Tim Willits, Christian Antkow, Paul Jaquays", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "CEO", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Todd Hollenshead", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Director of Business Development", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Marty Stratton", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Biz Assist and id Mom", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Donna Jackson", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.42 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Development Assistance", UI_CENTER|UI_SMALLFONT, color_white );
+ y += PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawProportionalString( 320, y, "Eric Webb", UI_CENTER|UI_SMALLFONT, color_white );
+
+ y += 1.35 * PROP_HEIGHT * PROP_SMALL_SIZE_SCALE;
+ UI_DrawString( 320, y, "To order: 1-800-idgames www.quake3arena.com www.idsoftware.com", UI_CENTER|UI_SMALLFONT, color_red );
+ y += SMALLCHAR_HEIGHT;
+ UI_DrawString( 320, y, "Quake III Arena(c) 1999-2000, Id Software, Inc. All Rights Reserved", UI_CENTER|UI_SMALLFONT, color_red );
+}
+
+
+/*
+===============
+UI_CreditMenu
+===============
+*/
+void UI_CreditMenu( void ) {
+ /* This UI_FillRect() hack will blank the borders if you're in widescreen,
+ so you get a completely black background instead of stripes from the
+ previous frame on each side of the credits.. */
+ const float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+ UI_FillRect(0 - uis.bias, 0, (640.0f / uis.xscale) * 2.0f, 480.0f / uis.yscale, black);
+
+ memset( &s_credits, 0 ,sizeof(s_credits) );
+
+ s_credits.menu.draw = UI_CreditMenu_Draw;
+ s_credits.menu.key = UI_CreditMenu_Key;
+ s_credits.menu.fullscreen = qtrue;
+ UI_PushMenu ( &s_credits.menu );
+}
diff --git a/code/q3_ui/ui_demo2.c b/code/q3_ui/ui_demo2.c
new file mode 100644
index 0000000..a3810bb
--- /dev/null
+++ b/code/q3_ui/ui_demo2.c
@@ -0,0 +1,291 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+DEMOS MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_GO0 "menu/art/play_0"
+#define ART_GO1 "menu/art/play_1"
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_ARROWS "menu/art/arrows_horz_0"
+#define ART_ARROWLEFT "menu/art/arrows_horz_left"
+#define ART_ARROWRIGHT "menu/art/arrows_horz_right"
+
+#define MAX_DEMOS 128
+#define NAMEBUFSIZE ( MAX_DEMOS * 16 )
+
+#define ID_BACK 10
+#define ID_GO 11
+#define ID_LIST 12
+#define ID_RIGHT 13
+#define ID_LEFT 14
+
+#define ARROWS_WIDTH 128
+#define ARROWS_HEIGHT 48
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menulist_s list;
+
+ menubitmap_s arrows;
+ menubitmap_s left;
+ menubitmap_s right;
+ menubitmap_s back;
+ menubitmap_s go;
+
+ int numDemos;
+ char names[NAMEBUFSIZE];
+ char *demolist[MAX_DEMOS];
+} demos_t;
+
+static demos_t s_demos;
+
+
+/*
+===============
+Demos_MenuEvent
+===============
+*/
+static void Demos_MenuEvent( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_GO:
+ UI_ForceMenuOff ();
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "demo %s\n",
+ s_demos.list.itemnames[s_demos.list.curvalue]) );
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+
+ case ID_LEFT:
+ ScrollList_Key( &s_demos.list, K_LEFTARROW );
+ break;
+
+ case ID_RIGHT:
+ ScrollList_Key( &s_demos.list, K_RIGHTARROW );
+ break;
+ }
+}
+
+
+/*
+=================
+UI_DemosMenu_Key
+=================
+*/
+static sfxHandle_t UI_DemosMenu_Key( int key ) {
+ menucommon_s *item;
+
+ item = Menu_ItemAtCursor( &s_demos.menu );
+
+ return Menu_DefaultKey( &s_demos.menu, key );
+}
+
+
+/*
+===============
+Demos_MenuInit
+===============
+*/
+static void Demos_MenuInit( void ) {
+ int i;
+ int len;
+ char *demoname, extension[32];
+
+ memset( &s_demos, 0 ,sizeof(demos_t) );
+ s_demos.menu.key = UI_DemosMenu_Key;
+
+ Demos_Cache();
+
+ s_demos.menu.fullscreen = qtrue;
+ s_demos.menu.wrapAround = qtrue;
+
+ s_demos.banner.generic.type = MTYPE_BTEXT;
+ s_demos.banner.generic.x = 320;
+ s_demos.banner.generic.y = 16;
+ s_demos.banner.string = "DEMOS";
+ s_demos.banner.color = color_white;
+ s_demos.banner.style = UI_CENTER;
+
+ s_demos.framel.generic.type = MTYPE_BITMAP;
+ s_demos.framel.generic.name = ART_FRAMEL;
+ s_demos.framel.generic.flags = QMF_INACTIVE;
+ s_demos.framel.generic.x = 0;
+ s_demos.framel.generic.y = 78;
+ s_demos.framel.width = 256;
+ s_demos.framel.height = 329;
+
+ s_demos.framer.generic.type = MTYPE_BITMAP;
+ s_demos.framer.generic.name = ART_FRAMER;
+ s_demos.framer.generic.flags = QMF_INACTIVE;
+ s_demos.framer.generic.x = 376;
+ s_demos.framer.generic.y = 76;
+ s_demos.framer.width = 256;
+ s_demos.framer.height = 334;
+
+ s_demos.arrows.generic.type = MTYPE_BITMAP;
+ s_demos.arrows.generic.name = ART_ARROWS;
+ s_demos.arrows.generic.flags = QMF_INACTIVE;
+ s_demos.arrows.generic.x = 320-ARROWS_WIDTH/2;
+ s_demos.arrows.generic.y = 400;
+ s_demos.arrows.width = ARROWS_WIDTH;
+ s_demos.arrows.height = ARROWS_HEIGHT;
+
+ s_demos.left.generic.type = MTYPE_BITMAP;
+ s_demos.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_demos.left.generic.x = 320-ARROWS_WIDTH/2;
+ s_demos.left.generic.y = 400;
+ s_demos.left.generic.id = ID_LEFT;
+ s_demos.left.generic.callback = Demos_MenuEvent;
+ s_demos.left.width = ARROWS_WIDTH/2;
+ s_demos.left.height = ARROWS_HEIGHT;
+ s_demos.left.focuspic = ART_ARROWLEFT;
+
+ s_demos.right.generic.type = MTYPE_BITMAP;
+ s_demos.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_demos.right.generic.x = 320;
+ s_demos.right.generic.y = 400;
+ s_demos.right.generic.id = ID_RIGHT;
+ s_demos.right.generic.callback = Demos_MenuEvent;
+ s_demos.right.width = ARROWS_WIDTH/2;
+ s_demos.right.height = ARROWS_HEIGHT;
+ s_demos.right.focuspic = ART_ARROWRIGHT;
+
+ s_demos.back.generic.type = MTYPE_BITMAP;
+ s_demos.back.generic.name = ART_BACK0;
+ s_demos.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_demos.back.generic.id = ID_BACK;
+ s_demos.back.generic.callback = Demos_MenuEvent;
+ s_demos.back.generic.x = 0;
+ s_demos.back.generic.y = 480-64;
+ s_demos.back.width = 128;
+ s_demos.back.height = 64;
+ s_demos.back.focuspic = ART_BACK1;
+
+ s_demos.go.generic.type = MTYPE_BITMAP;
+ s_demos.go.generic.name = ART_GO0;
+ s_demos.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_demos.go.generic.id = ID_GO;
+ s_demos.go.generic.callback = Demos_MenuEvent;
+ s_demos.go.generic.x = 640;
+ s_demos.go.generic.y = 480-64;
+ s_demos.go.width = 128;
+ s_demos.go.height = 64;
+ s_demos.go.focuspic = ART_GO1;
+
+ s_demos.list.generic.type = MTYPE_SCROLLLIST;
+ s_demos.list.generic.flags = QMF_PULSEIFFOCUS;
+ s_demos.list.generic.callback = Demos_MenuEvent;
+ s_demos.list.generic.id = ID_LIST;
+ s_demos.list.generic.x = 118;
+ s_demos.list.generic.y = 130;
+ s_demos.list.width = 16;
+ s_demos.list.height = 14;
+ Com_sprintf(extension, sizeof(extension), "dm_%d", (int)trap_Cvar_VariableValue( "protocol" ) );
+ s_demos.list.numitems = trap_FS_GetFileList( "demos", extension, s_demos.names, NAMEBUFSIZE );
+ s_demos.list.itemnames = (const char **)s_demos.demolist;
+ s_demos.list.columns = 3;
+
+ if (!s_demos.list.numitems) {
+ strcpy( s_demos.names, "No Demos Found." );
+ s_demos.list.numitems = 1;
+
+ //degenerate case, not selectable
+ s_demos.go.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ }
+ else if (s_demos.list.numitems > MAX_DEMOS)
+ s_demos.list.numitems = MAX_DEMOS;
+
+ demoname = s_demos.names;
+ for ( i = 0; i < s_demos.list.numitems; i++ ) {
+ s_demos.list.itemnames[i] = demoname;
+
+ // strip extension
+ len = strlen( demoname );
+ if (!Q_stricmp(demoname + len - 4,".dm3"))
+ demoname[len-4] = '\0';
+
+// Q_strupr(demoname);
+
+ demoname += len + 1;
+ }
+
+ Menu_AddItem( &s_demos.menu, &s_demos.banner );
+ Menu_AddItem( &s_demos.menu, &s_demos.framel );
+ Menu_AddItem( &s_demos.menu, &s_demos.framer );
+ Menu_AddItem( &s_demos.menu, &s_demos.list );
+ Menu_AddItem( &s_demos.menu, &s_demos.arrows );
+ Menu_AddItem( &s_demos.menu, &s_demos.left );
+ Menu_AddItem( &s_demos.menu, &s_demos.right );
+ Menu_AddItem( &s_demos.menu, &s_demos.back );
+ Menu_AddItem( &s_demos.menu, &s_demos.go );
+}
+
+/*
+=================
+Demos_Cache
+=================
+*/
+void Demos_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_GO0 );
+ trap_R_RegisterShaderNoMip( ART_GO1 );
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_ARROWS );
+ trap_R_RegisterShaderNoMip( ART_ARROWLEFT );
+ trap_R_RegisterShaderNoMip( ART_ARROWRIGHT );
+}
+
+/*
+===============
+UI_DemosMenu
+===============
+*/
+void UI_DemosMenu( void ) {
+ Demos_MenuInit();
+ UI_PushMenu( &s_demos.menu );
+}
diff --git a/code/q3_ui/ui_display.c b/code/q3_ui/ui_display.c
new file mode 100644
index 0000000..5980c1a
--- /dev/null
+++ b/code/q3_ui/ui_display.c
@@ -0,0 +1,265 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+DISPLAY OPTIONS MENU
+
+=======================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+
+#define ID_GRAPHICS 10
+#define ID_DISPLAY 11
+#define ID_SOUND 12
+#define ID_NETWORK 13
+#define ID_BRIGHTNESS 14
+#define ID_SCREENSIZE 15
+#define ID_BACK 16
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menutext_s graphics;
+ menutext_s display;
+ menutext_s sound;
+ menutext_s network;
+
+ menuslider_s brightness;
+ menuslider_s screensize;
+
+ menubitmap_s back;
+} displayOptionsInfo_t;
+
+static displayOptionsInfo_t displayOptionsInfo;
+
+
+/*
+=================
+UI_DisplayOptionsMenu_Event
+=================
+*/
+static void UI_DisplayOptionsMenu_Event( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_GRAPHICS:
+ UI_PopMenu();
+ UI_GraphicsOptionsMenu();
+ break;
+
+ case ID_DISPLAY:
+ break;
+
+ case ID_SOUND:
+ UI_PopMenu();
+ UI_SoundOptionsMenu();
+ break;
+
+ case ID_NETWORK:
+ UI_PopMenu();
+ UI_NetworkOptionsMenu();
+ break;
+
+ case ID_BRIGHTNESS:
+ trap_Cvar_SetValue( "r_gamma", displayOptionsInfo.brightness.curvalue / 10.0f );
+ break;
+
+ case ID_SCREENSIZE:
+ trap_Cvar_SetValue( "cg_viewsize", displayOptionsInfo.screensize.curvalue * 10 );
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+UI_DisplayOptionsMenu_Init
+===============
+*/
+static void UI_DisplayOptionsMenu_Init( void ) {
+ int y;
+
+ memset( &displayOptionsInfo, 0, sizeof(displayOptionsInfo) );
+
+ UI_DisplayOptionsMenu_Cache();
+ displayOptionsInfo.menu.wrapAround = qtrue;
+ displayOptionsInfo.menu.fullscreen = qtrue;
+
+ displayOptionsInfo.banner.generic.type = MTYPE_BTEXT;
+ displayOptionsInfo.banner.generic.flags = QMF_CENTER_JUSTIFY;
+ displayOptionsInfo.banner.generic.x = 320;
+ displayOptionsInfo.banner.generic.y = 16;
+ displayOptionsInfo.banner.string = "SYSTEM SETUP";
+ displayOptionsInfo.banner.color = color_white;
+ displayOptionsInfo.banner.style = UI_CENTER;
+
+ displayOptionsInfo.framel.generic.type = MTYPE_BITMAP;
+ displayOptionsInfo.framel.generic.name = ART_FRAMEL;
+ displayOptionsInfo.framel.generic.flags = QMF_INACTIVE;
+ displayOptionsInfo.framel.generic.x = 0;
+ displayOptionsInfo.framel.generic.y = 78;
+ displayOptionsInfo.framel.width = 256;
+ displayOptionsInfo.framel.height = 329;
+
+ displayOptionsInfo.framer.generic.type = MTYPE_BITMAP;
+ displayOptionsInfo.framer.generic.name = ART_FRAMER;
+ displayOptionsInfo.framer.generic.flags = QMF_INACTIVE;
+ displayOptionsInfo.framer.generic.x = 376;
+ displayOptionsInfo.framer.generic.y = 76;
+ displayOptionsInfo.framer.width = 256;
+ displayOptionsInfo.framer.height = 334;
+
+ displayOptionsInfo.graphics.generic.type = MTYPE_PTEXT;
+ displayOptionsInfo.graphics.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ displayOptionsInfo.graphics.generic.id = ID_GRAPHICS;
+ displayOptionsInfo.graphics.generic.callback = UI_DisplayOptionsMenu_Event;
+ displayOptionsInfo.graphics.generic.x = 216;
+ displayOptionsInfo.graphics.generic.y = 240 - 2 * PROP_HEIGHT;
+ displayOptionsInfo.graphics.string = "GRAPHICS";
+ displayOptionsInfo.graphics.style = UI_RIGHT;
+ displayOptionsInfo.graphics.color = color_red;
+
+ displayOptionsInfo.display.generic.type = MTYPE_PTEXT;
+ displayOptionsInfo.display.generic.flags = QMF_RIGHT_JUSTIFY;
+ displayOptionsInfo.display.generic.id = ID_DISPLAY;
+ displayOptionsInfo.display.generic.callback = UI_DisplayOptionsMenu_Event;
+ displayOptionsInfo.display.generic.x = 216;
+ displayOptionsInfo.display.generic.y = 240 - PROP_HEIGHT;
+ displayOptionsInfo.display.string = "DISPLAY";
+ displayOptionsInfo.display.style = UI_RIGHT;
+ displayOptionsInfo.display.color = color_red;
+
+ displayOptionsInfo.sound.generic.type = MTYPE_PTEXT;
+ displayOptionsInfo.sound.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ displayOptionsInfo.sound.generic.id = ID_SOUND;
+ displayOptionsInfo.sound.generic.callback = UI_DisplayOptionsMenu_Event;
+ displayOptionsInfo.sound.generic.x = 216;
+ displayOptionsInfo.sound.generic.y = 240;
+ displayOptionsInfo.sound.string = "SOUND";
+ displayOptionsInfo.sound.style = UI_RIGHT;
+ displayOptionsInfo.sound.color = color_red;
+
+ displayOptionsInfo.network.generic.type = MTYPE_PTEXT;
+ displayOptionsInfo.network.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ displayOptionsInfo.network.generic.id = ID_NETWORK;
+ displayOptionsInfo.network.generic.callback = UI_DisplayOptionsMenu_Event;
+ displayOptionsInfo.network.generic.x = 216;
+ displayOptionsInfo.network.generic.y = 240 + PROP_HEIGHT;
+ displayOptionsInfo.network.string = "NETWORK";
+ displayOptionsInfo.network.style = UI_RIGHT;
+ displayOptionsInfo.network.color = color_red;
+
+ y = 240 - 1 * (BIGCHAR_HEIGHT+2);
+ displayOptionsInfo.brightness.generic.type = MTYPE_SLIDER;
+ displayOptionsInfo.brightness.generic.name = "Brightness:";
+ displayOptionsInfo.brightness.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ displayOptionsInfo.brightness.generic.callback = UI_DisplayOptionsMenu_Event;
+ displayOptionsInfo.brightness.generic.id = ID_BRIGHTNESS;
+ displayOptionsInfo.brightness.generic.x = 400;
+ displayOptionsInfo.brightness.generic.y = y;
+ displayOptionsInfo.brightness.minvalue = 5;
+ displayOptionsInfo.brightness.maxvalue = 20;
+ if( !uis.glconfig.deviceSupportsGamma ) {
+ displayOptionsInfo.brightness.generic.flags |= QMF_GRAYED;
+ }
+
+ y += BIGCHAR_HEIGHT+2;
+ displayOptionsInfo.screensize.generic.type = MTYPE_SLIDER;
+ displayOptionsInfo.screensize.generic.name = "Screen Size:";
+ displayOptionsInfo.screensize.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ displayOptionsInfo.screensize.generic.callback = UI_DisplayOptionsMenu_Event;
+ displayOptionsInfo.screensize.generic.id = ID_SCREENSIZE;
+ displayOptionsInfo.screensize.generic.x = 400;
+ displayOptionsInfo.screensize.generic.y = y;
+ displayOptionsInfo.screensize.minvalue = 3;
+ displayOptionsInfo.screensize.maxvalue = 10;
+
+ displayOptionsInfo.back.generic.type = MTYPE_BITMAP;
+ displayOptionsInfo.back.generic.name = ART_BACK0;
+ displayOptionsInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ displayOptionsInfo.back.generic.callback = UI_DisplayOptionsMenu_Event;
+ displayOptionsInfo.back.generic.id = ID_BACK;
+ displayOptionsInfo.back.generic.x = 0;
+ displayOptionsInfo.back.generic.y = 480-64;
+ displayOptionsInfo.back.width = 128;
+ displayOptionsInfo.back.height = 64;
+ displayOptionsInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.banner );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.framel );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.framer );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.graphics );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.display );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.sound );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.network );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.brightness );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.screensize );
+ Menu_AddItem( &displayOptionsInfo.menu, ( void * ) &displayOptionsInfo.back );
+
+ displayOptionsInfo.brightness.curvalue = trap_Cvar_VariableValue("r_gamma") * 10;
+ displayOptionsInfo.screensize.curvalue = trap_Cvar_VariableValue( "cg_viewsize")/10;
+}
+
+
+/*
+===============
+UI_DisplayOptionsMenu_Cache
+===============
+*/
+void UI_DisplayOptionsMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+}
+
+
+/*
+===============
+UI_DisplayOptionsMenu
+===============
+*/
+void UI_DisplayOptionsMenu( void ) {
+ UI_DisplayOptionsMenu_Init();
+ UI_PushMenu( &displayOptionsInfo.menu );
+ Menu_SetCursorToItem( &displayOptionsInfo.menu, &displayOptionsInfo.display );
+}
diff --git a/code/q3_ui/ui_gameinfo.c b/code/q3_ui/ui_gameinfo.c
new file mode 100644
index 0000000..d9deacd
--- /dev/null
+++ b/code/q3_ui/ui_gameinfo.c
@@ -0,0 +1,815 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+//
+// gameinfo.c
+//
+
+#include "ui_local.h"
+
+
+//
+// arena and bot info
+//
+
+#define POOLSIZE 128 * 1024
+
+int ui_numBots;
+static char *ui_botInfos[MAX_BOTS];
+
+static int ui_numArenas;
+static char *ui_arenaInfos[MAX_ARENAS];
+
+static int ui_numSinglePlayerArenas;
+static int ui_numSpecialSinglePlayerArenas;
+
+static char memoryPool[POOLSIZE];
+static int allocPoint, outOfMemory;
+
+
+/*
+===============
+UI_Alloc
+===============
+*/
+void *UI_Alloc( int size ) {
+ char *p;
+
+ if ( allocPoint + size > POOLSIZE ) {
+ outOfMemory = qtrue;
+ return NULL;
+ }
+
+ p = &memoryPool[allocPoint];
+
+ allocPoint += ( size + 31 ) & ~31;
+
+ return p;
+}
+
+/*
+===============
+UI_InitMemory
+===============
+*/
+void UI_InitMemory( void ) {
+ allocPoint = 0;
+ outOfMemory = qfalse;
+}
+
+/*
+===============
+UI_ParseInfos
+===============
+*/
+int UI_ParseInfos( char *buf, int max, char *infos[] ) {
+ char *token;
+ int count;
+ char key[MAX_TOKEN_CHARS];
+ char info[MAX_INFO_STRING];
+
+ count = 0;
+
+ while ( 1 ) {
+ token = COM_Parse( &buf );
+ if ( !token[0] ) {
+ break;
+ }
+ if ( strcmp( token, "{" ) ) {
+ Com_Printf( "Missing { in info file\n" );
+ break;
+ }
+
+ if ( count == max ) {
+ Com_Printf( "Max infos exceeded\n" );
+ break;
+ }
+
+ info[0] = '\0';
+ while ( 1 ) {
+ token = COM_ParseExt( &buf, qtrue );
+ if ( !token[0] ) {
+ Com_Printf( "Unexpected end of info file\n" );
+ break;
+ }
+ if ( !strcmp( token, "}" ) ) {
+ break;
+ }
+ Q_strncpyz( key, token, sizeof( key ) );
+
+ token = COM_ParseExt( &buf, qfalse );
+ if ( !token[0] ) {
+ strcpy( token, "<NULL>" );
+ }
+ Info_SetValueForKey( info, key, token );
+ }
+ //NOTE: extra space for arena number
+ infos[count] = UI_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
+ if (infos[count]) {
+ strcpy(infos[count], info);
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+===============
+UI_LoadArenasFromFile
+===============
+*/
+static void UI_LoadArenasFromFile( char *filename ) {
+ int len;
+ fileHandle_t f;
+ char buf[MAX_ARENAS_TEXT];
+
+ len = trap_FS_FOpenFile( filename, &f, FS_READ );
+ if ( !f ) {
+ trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) );
+ return;
+ }
+ if ( len >= MAX_ARENAS_TEXT ) {
+ trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) );
+ trap_FS_FCloseFile( f );
+ return;
+ }
+
+ trap_FS_Read( buf, len, f );
+ buf[len] = 0;
+ trap_FS_FCloseFile( f );
+
+ ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] );
+}
+
+/*
+===============
+UI_LoadArenas
+===============
+*/
+static void UI_LoadArenas( void ) {
+ int numdirs;
+ vmCvar_t arenasFile;
+ char filename[128];
+ char dirlist[2048];
+ char* dirptr;
+ int i, n;
+ int dirlen;
+ char *type;
+ char *tag;
+ int singlePlayerNum, specialNum, otherNum;
+
+ ui_numArenas = 0;
+
+ trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM );
+ if( *arenasFile.string ) {
+ UI_LoadArenasFromFile(arenasFile.string);
+ }
+ else {
+ UI_LoadArenasFromFile("scripts/arenas.txt");
+ }
+
+ // get all arenas from .arena files
+ numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 2048 );
+ dirptr = dirlist;
+ for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
+ dirlen = strlen(dirptr);
+ strcpy(filename, "scripts/");
+ strcat(filename, dirptr);
+ UI_LoadArenasFromFile(filename);
+ }
+ trap_Print( va( "%i arenas parsed\n", ui_numArenas ) );
+ if (outOfMemory) trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n");
+
+ // set initial numbers
+ for( n = 0; n < ui_numArenas; n++ ) {
+ Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", n ) );
+ }
+
+ // go through and count single players levels
+ ui_numSinglePlayerArenas = 0;
+ ui_numSpecialSinglePlayerArenas = 0;
+ for( n = 0; n < ui_numArenas; n++ ) {
+ // determine type
+ type = Info_ValueForKey( ui_arenaInfos[n], "type" );
+
+ // if no type specified, it will be treated as "ffa"
+ if( !*type ) {
+ continue;
+ }
+
+ if( strstr( type, "single" ) ) {
+ // check for special single player arenas (training, final)
+ tag = Info_ValueForKey( ui_arenaInfos[n], "special" );
+ if( *tag ) {
+ ui_numSpecialSinglePlayerArenas++;
+ continue;
+ }
+
+ ui_numSinglePlayerArenas++;
+ }
+ }
+
+ n = ui_numSinglePlayerArenas % ARENAS_PER_TIER;
+ if( n != 0 ) {
+ ui_numSinglePlayerArenas -= n;
+ trap_Print( va( "%i arenas ignored to make count divisible by %i\n", n, ARENAS_PER_TIER ) );
+ }
+
+ // go through once more and assign number to the levels
+ singlePlayerNum = 0;
+ specialNum = singlePlayerNum + ui_numSinglePlayerArenas;
+ otherNum = specialNum + ui_numSpecialSinglePlayerArenas;
+ for( n = 0; n < ui_numArenas; n++ ) {
+ // determine type
+ type = Info_ValueForKey( ui_arenaInfos[n], "type" );
+
+ // if no type specified, it will be treated as "ffa"
+ if( *type ) {
+ if( strstr( type, "single" ) ) {
+ // check for special single player arenas (training, final)
+ tag = Info_ValueForKey( ui_arenaInfos[n], "special" );
+ if( *tag ) {
+ Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", specialNum++ ) );
+ continue;
+ }
+
+ Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", singlePlayerNum++ ) );
+ continue;
+ }
+ }
+
+ Info_SetValueForKey( ui_arenaInfos[n], "num", va( "%i", otherNum++ ) );
+ }
+}
+
+/*
+===============
+UI_GetArenaInfoByNumber
+===============
+*/
+const char *UI_GetArenaInfoByNumber( int num ) {
+ int n;
+ char *value;
+
+ if( num < 0 || num >= ui_numArenas ) {
+ trap_Print( va( S_COLOR_RED "Invalid arena number: %i\n", num ) );
+ return NULL;
+ }
+
+ for( n = 0; n < ui_numArenas; n++ ) {
+ value = Info_ValueForKey( ui_arenaInfos[n], "num" );
+ if( *value && atoi(value) == num ) {
+ return ui_arenaInfos[n];
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+===============
+UI_GetArenaInfoByNumber
+===============
+*/
+const char *UI_GetArenaInfoByMap( const char *map ) {
+ int n;
+
+ for( n = 0; n < ui_numArenas; n++ ) {
+ if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "map" ), map ) == 0 ) {
+ return ui_arenaInfos[n];
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+===============
+UI_GetSpecialArenaInfo
+===============
+*/
+const char *UI_GetSpecialArenaInfo( const char *tag ) {
+ int n;
+
+ for( n = 0; n < ui_numArenas; n++ ) {
+ if( Q_stricmp( Info_ValueForKey( ui_arenaInfos[n], "special" ), tag ) == 0 ) {
+ return ui_arenaInfos[n];
+ }
+ }
+
+ return NULL;
+}
+
+/*
+===============
+UI_LoadBotsFromFile
+===============
+*/
+static void UI_LoadBotsFromFile( char *filename ) {
+ int len;
+ fileHandle_t f;
+ char buf[MAX_BOTS_TEXT];
+
+ len = trap_FS_FOpenFile( filename, &f, FS_READ );
+ if ( !f ) {
+ trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) );
+ return;
+ }
+ if ( len >= MAX_BOTS_TEXT ) {
+ trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) );
+ trap_FS_FCloseFile( f );
+ return;
+ }
+
+ trap_FS_Read( buf, len, f );
+ buf[len] = 0;
+ trap_FS_FCloseFile( f );
+
+ ui_numBots += UI_ParseInfos( buf, MAX_BOTS - ui_numBots, &ui_botInfos[ui_numBots] );
+ if (outOfMemory) trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all bots\n");
+}
+
+/*
+===============
+UI_LoadBots
+===============
+*/
+static void UI_LoadBots( void ) {
+ vmCvar_t botsFile;
+ int numdirs;
+ char filename[128];
+ char dirlist[1024];
+ char* dirptr;
+ int i;
+ int dirlen;
+
+ ui_numBots = 0;
+
+ trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM );
+ if( *botsFile.string ) {
+ UI_LoadBotsFromFile(botsFile.string);
+ }
+ else {
+ UI_LoadBotsFromFile("scripts/bots.txt");
+ }
+
+ // get all bots from .bot files
+ numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
+ dirptr = dirlist;
+ for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
+ dirlen = strlen(dirptr);
+ strcpy(filename, "scripts/");
+ strcat(filename, dirptr);
+ UI_LoadBotsFromFile(filename);
+ }
+ trap_Print( va( "%i bots parsed\n", ui_numBots ) );
+}
+
+
+/*
+===============
+UI_GetBotInfoByNumber
+===============
+*/
+char *UI_GetBotInfoByNumber( int num ) {
+ if( num < 0 || num >= ui_numBots ) {
+ trap_Print( va( S_COLOR_RED "Invalid bot number: %i\n", num ) );
+ return NULL;
+ }
+ return ui_botInfos[num];
+}
+
+
+/*
+===============
+UI_GetBotInfoByName
+===============
+*/
+char *UI_GetBotInfoByName( const char *name ) {
+ int n;
+ char *value;
+
+ for ( n = 0; n < ui_numBots ; n++ ) {
+ value = Info_ValueForKey( ui_botInfos[n], "name" );
+ if ( !Q_stricmp( value, name ) ) {
+ return ui_botInfos[n];
+ }
+ }
+
+ return NULL;
+}
+
+
+//
+// single player game info
+//
+
+/*
+===============
+UI_GetBestScore
+
+Returns the player's best finish on a given level, 0 if the have not played the level
+===============
+*/
+void UI_GetBestScore( int level, int *score, int *skill ) {
+ int n;
+ int skillScore;
+ int bestScore;
+ int bestScoreSkill;
+ char arenaKey[16];
+ char scores[MAX_INFO_VALUE];
+
+ if( !score || !skill ) {
+ return;
+ }
+
+ if( level < 0 || level > ui_numArenas ) {
+ return;
+ }
+
+ bestScore = 0;
+ bestScoreSkill = 0;
+
+ for( n = 1; n <= 5; n++ ) {
+ trap_Cvar_VariableStringBuffer( va( "g_spScores%i", n ), scores, MAX_INFO_VALUE );
+
+ Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
+ skillScore = atoi( Info_ValueForKey( scores, arenaKey ) );
+
+ if( skillScore < 1 || skillScore > 8 ) {
+ continue;
+ }
+
+ if( !bestScore || skillScore <= bestScore ) {
+ bestScore = skillScore;
+ bestScoreSkill = n;
+ }
+ }
+
+ *score = bestScore;
+ *skill = bestScoreSkill;
+}
+
+
+/*
+===============
+UI_SetBestScore
+
+Set the player's best finish for a level
+===============
+*/
+void UI_SetBestScore( int level, int score ) {
+ int skill;
+ int oldScore;
+ char arenaKey[16];
+ char scores[MAX_INFO_VALUE];
+
+ // validate score
+ if( score < 1 || score > 8 ) {
+ return;
+ }
+
+ // validate skill
+ skill = (int)trap_Cvar_VariableValue( "g_spSkill" );
+ if( skill < 1 || skill > 5 ) {
+ return;
+ }
+
+ // get scores
+ trap_Cvar_VariableStringBuffer( va( "g_spScores%i", skill ), scores, MAX_INFO_VALUE );
+
+ // see if this is better
+ Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
+ oldScore = atoi( Info_ValueForKey( scores, arenaKey ) );
+ if( oldScore && oldScore <= score ) {
+ return;
+ }
+
+ // update scores
+ Info_SetValueForKey( scores, arenaKey, va( "%i", score ) );
+ trap_Cvar_Set( va( "g_spScores%i", skill ), scores );
+}
+
+
+/*
+===============
+UI_LogAwardData
+===============
+*/
+void UI_LogAwardData( int award, int data ) {
+ char key[16];
+ char awardData[MAX_INFO_VALUE];
+ int oldValue;
+
+ if( data == 0 ) {
+ return;
+ }
+
+ if( award > AWARD_PERFECT ) {
+ trap_Print( va( S_COLOR_RED "Bad award %i in UI_LogAwardData\n", award ) );
+ return;
+ }
+
+ trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) );
+
+ Com_sprintf( key, sizeof(key), "a%i", award );
+ oldValue = atoi( Info_ValueForKey( awardData, key ) );
+
+ Info_SetValueForKey( awardData, key, va( "%i", oldValue + data ) );
+ trap_Cvar_Set( "g_spAwards", awardData );
+}
+
+
+/*
+===============
+UI_GetAwardLevel
+===============
+*/
+int UI_GetAwardLevel( int award ) {
+ char key[16];
+ char awardData[MAX_INFO_VALUE];
+
+ trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, sizeof(awardData) );
+
+ Com_sprintf( key, sizeof(key), "a%i", award );
+ return atoi( Info_ValueForKey( awardData, key ) );
+}
+
+
+/*
+===============
+UI_TierCompleted
+===============
+*/
+int UI_TierCompleted( int levelWon ) {
+ int level;
+ int n;
+ int tier;
+ int score;
+ int skill;
+ const char *info;
+
+ tier = levelWon / ARENAS_PER_TIER;
+ level = tier * ARENAS_PER_TIER;
+
+ if( tier == UI_GetNumSPTiers() ) {
+ info = UI_GetSpecialArenaInfo( "training" );
+ if( levelWon == atoi( Info_ValueForKey( info, "num" ) ) ) {
+ return 0;
+ }
+ info = UI_GetSpecialArenaInfo( "final" );
+ if( !info || levelWon == atoi( Info_ValueForKey( info, "num" ) ) ) {
+ return tier + 1;
+ }
+ return -1;
+ }
+
+ for( n = 0; n < ARENAS_PER_TIER; n++, level++ ) {
+ UI_GetBestScore( level, &score, &skill );
+ if ( score != 1 ) {
+ return -1;
+ }
+ }
+ return tier + 1;
+}
+
+
+/*
+===============
+UI_ShowTierVideo
+===============
+*/
+qboolean UI_ShowTierVideo( int tier ) {
+ char key[16];
+ char videos[MAX_INFO_VALUE];
+
+ if( tier <= 0 ) {
+ return qfalse;
+ }
+
+ trap_Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) );
+
+ Com_sprintf( key, sizeof(key), "tier%i", tier );
+ if( atoi( Info_ValueForKey( videos, key ) ) ) {
+ return qfalse;
+ }
+
+ Info_SetValueForKey( videos, key, va( "%i", 1 ) );
+ trap_Cvar_Set( "g_spVideos", videos );
+
+ return qtrue;
+}
+
+
+/*
+===============
+UI_CanShowTierVideo
+===============
+*/
+qboolean UI_CanShowTierVideo( int tier ) {
+ char key[16];
+ char videos[MAX_INFO_VALUE];
+
+ if( !tier ) {
+ return qfalse;
+ }
+
+ if( uis.demoversion && tier != 8 ) {
+ return qfalse;
+ }
+
+ trap_Cvar_VariableStringBuffer( "g_spVideos", videos, sizeof(videos) );
+
+ Com_sprintf( key, sizeof(key), "tier%i", tier );
+ if( atoi( Info_ValueForKey( videos, key ) ) ) {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+
+/*
+===============
+UI_GetCurrentGame
+
+Returns the next level the player has not won
+===============
+*/
+int UI_GetCurrentGame( void ) {
+ int level;
+ int rank;
+ int skill;
+ const char *info;
+
+ info = UI_GetSpecialArenaInfo( "training" );
+ if( info ) {
+ level = atoi( Info_ValueForKey( info, "num" ) );
+ UI_GetBestScore( level, &rank, &skill );
+ if ( !rank || rank > 1 ) {
+ return level;
+ }
+ }
+
+ for( level = 0; level < ui_numSinglePlayerArenas; level++ ) {
+ UI_GetBestScore( level, &rank, &skill );
+ if ( !rank || rank > 1 ) {
+ return level;
+ }
+ }
+
+ info = UI_GetSpecialArenaInfo( "final" );
+ if( !info ) {
+ return -1;
+ }
+ return atoi( Info_ValueForKey( info, "num" ) );
+}
+
+
+/*
+===============
+UI_NewGame
+
+Clears the scores and sets the difficutly level
+===============
+*/
+void UI_NewGame( void ) {
+ trap_Cvar_Set( "g_spScores1", "" );
+ trap_Cvar_Set( "g_spScores2", "" );
+ trap_Cvar_Set( "g_spScores3", "" );
+ trap_Cvar_Set( "g_spScores4", "" );
+ trap_Cvar_Set( "g_spScores5", "" );
+ trap_Cvar_Set( "g_spAwards", "" );
+ trap_Cvar_Set( "g_spVideos", "" );
+}
+
+
+/*
+===============
+UI_GetNumArenas
+===============
+*/
+int UI_GetNumArenas( void ) {
+ return ui_numArenas;
+}
+
+
+/*
+===============
+UI_GetNumSPArenas
+===============
+*/
+int UI_GetNumSPArenas( void ) {
+ return ui_numSinglePlayerArenas;
+}
+
+
+/*
+===============
+UI_GetNumSPTiers
+===============
+*/
+int UI_GetNumSPTiers( void ) {
+ return ui_numSinglePlayerArenas / ARENAS_PER_TIER;
+}
+
+
+/*
+===============
+UI_GetNumBots
+===============
+*/
+int UI_GetNumBots( void ) {
+ return ui_numBots;
+}
+
+
+/*
+===============
+UI_SPUnlock_f
+===============
+*/
+void UI_SPUnlock_f( void ) {
+ char arenaKey[16];
+ char scores[MAX_INFO_VALUE];
+ int level;
+ int tier;
+
+ // get scores for skill 1
+ trap_Cvar_VariableStringBuffer( "g_spScores1", scores, MAX_INFO_VALUE );
+
+ // update scores
+ for( level = 0; level < ui_numSinglePlayerArenas + ui_numSpecialSinglePlayerArenas; level++ ) {
+ Com_sprintf( arenaKey, sizeof( arenaKey ), "l%i", level );
+ Info_SetValueForKey( scores, arenaKey, "1" );
+ }
+ trap_Cvar_Set( "g_spScores1", scores );
+
+ // unlock cinematics
+ for( tier = 1; tier <= 8; tier++ ) {
+ UI_ShowTierVideo( tier );
+ }
+
+ trap_Print( "All levels unlocked at skill level 1\n" );
+
+ UI_SPLevelMenu_ReInit();
+}
+
+
+/*
+===============
+UI_SPUnlockMedals_f
+===============
+*/
+void UI_SPUnlockMedals_f( void ) {
+ int n;
+ char key[16];
+ char awardData[MAX_INFO_VALUE];
+
+ trap_Cvar_VariableStringBuffer( "g_spAwards", awardData, MAX_INFO_VALUE );
+
+ for( n = 0; n < 6; n++ ) {
+ Com_sprintf( key, sizeof(key), "a%i", n );
+ Info_SetValueForKey( awardData, key, "100" );
+ }
+
+ trap_Cvar_Set( "g_spAwards", awardData );
+
+ trap_Print( "All levels unlocked at 100\n" );
+}
+
+
+/*
+===============
+UI_InitGameinfo
+===============
+*/
+void UI_InitGameinfo( void ) {
+
+ UI_InitMemory();
+ UI_LoadArenas();
+ UI_LoadBots();
+
+ uis.demoversion = qfalse;
+}
diff --git a/code/q3_ui/ui_ingame.c b/code/q3_ui/ui_ingame.c
new file mode 100644
index 0000000..b687162
--- /dev/null
+++ b/code/q3_ui/ui_ingame.c
@@ -0,0 +1,349 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+INGAME MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define INGAME_FRAME "menu/art/addbotframe"
+//#define INGAME_FRAME "menu/art/cut_frame"
+#define INGAME_MENU_VERTICAL_SPACING 28
+
+#define ID_TEAM 10
+#define ID_ADDBOTS 11
+#define ID_REMOVEBOTS 12
+#define ID_SETUP 13
+#define ID_SERVERINFO 14
+#define ID_LEAVEARENA 15
+#define ID_RESTART 16
+#define ID_QUIT 17
+#define ID_RESUME 18
+#define ID_TEAMORDERS 19
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menubitmap_s frame;
+ menutext_s team;
+ menutext_s setup;
+ menutext_s server;
+ menutext_s leave;
+ menutext_s restart;
+ menutext_s addbots;
+ menutext_s removebots;
+ menutext_s teamorders;
+ menutext_s quit;
+ menutext_s resume;
+} ingamemenu_t;
+
+static ingamemenu_t s_ingame;
+
+
+/*
+=================
+InGame_RestartAction
+=================
+*/
+static void InGame_RestartAction( qboolean result ) {
+ if( !result ) {
+ return;
+ }
+
+ UI_PopMenu();
+ trap_Cmd_ExecuteText( EXEC_APPEND, "map_restart 0\n" );
+}
+
+
+/*
+=================
+InGame_QuitAction
+=================
+*/
+static void InGame_QuitAction( qboolean result ) {
+ if( !result ) {
+ return;
+ }
+ UI_PopMenu();
+ UI_CreditMenu();
+}
+
+
+/*
+=================
+InGame_Event
+=================
+*/
+void InGame_Event( void *ptr, int notification ) {
+ if( notification != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_TEAM:
+ UI_TeamMainMenu();
+ break;
+
+ case ID_SETUP:
+ UI_SetupMenu();
+ break;
+
+ case ID_LEAVEARENA:
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" );
+ break;
+
+ case ID_RESTART:
+ UI_ConfirmMenu( "RESTART ARENA?", 0, InGame_RestartAction );
+ break;
+
+ case ID_QUIT:
+ UI_ConfirmMenu( "EXIT GAME?", 0, InGame_QuitAction );
+ break;
+
+ case ID_SERVERINFO:
+ UI_ServerInfoMenu();
+ break;
+
+ case ID_ADDBOTS:
+ UI_AddBotsMenu();
+ break;
+
+ case ID_REMOVEBOTS:
+ UI_RemoveBotsMenu();
+ break;
+
+ case ID_TEAMORDERS:
+ UI_TeamOrdersMenu();
+ break;
+
+ case ID_RESUME:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+=================
+InGame_MenuInit
+=================
+*/
+void InGame_MenuInit( void ) {
+ int y;
+ uiClientState_t cs;
+ char info[MAX_INFO_STRING];
+ int team;
+
+ memset( &s_ingame, 0 ,sizeof(ingamemenu_t) );
+
+ InGame_Cache();
+
+ s_ingame.menu.wrapAround = qtrue;
+ s_ingame.menu.fullscreen = qfalse;
+
+ s_ingame.frame.generic.type = MTYPE_BITMAP;
+ s_ingame.frame.generic.flags = QMF_INACTIVE;
+ s_ingame.frame.generic.name = INGAME_FRAME;
+ s_ingame.frame.generic.x = 320-233;//142;
+ s_ingame.frame.generic.y = 240-166;//118;
+ s_ingame.frame.width = 466;//359;
+ s_ingame.frame.height = 332;//256;
+
+ //y = 96;
+ y = 88;
+ s_ingame.team.generic.type = MTYPE_PTEXT;
+ s_ingame.team.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.team.generic.x = 320;
+ s_ingame.team.generic.y = y;
+ s_ingame.team.generic.id = ID_TEAM;
+ s_ingame.team.generic.callback = InGame_Event;
+ s_ingame.team.string = "START";
+ s_ingame.team.color = color_red;
+ s_ingame.team.style = UI_CENTER|UI_SMALLFONT;
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.addbots.generic.type = MTYPE_PTEXT;
+ s_ingame.addbots.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.addbots.generic.x = 320;
+ s_ingame.addbots.generic.y = y;
+ s_ingame.addbots.generic.id = ID_ADDBOTS;
+ s_ingame.addbots.generic.callback = InGame_Event;
+ s_ingame.addbots.string = "ADD BOTS";
+ s_ingame.addbots.color = color_red;
+ s_ingame.addbots.style = UI_CENTER|UI_SMALLFONT;
+ if( !trap_Cvar_VariableValue( "sv_running" ) || !trap_Cvar_VariableValue( "bot_enable" ) || (trap_Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER)) {
+ s_ingame.addbots.generic.flags |= QMF_GRAYED;
+ }
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.removebots.generic.type = MTYPE_PTEXT;
+ s_ingame.removebots.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.removebots.generic.x = 320;
+ s_ingame.removebots.generic.y = y;
+ s_ingame.removebots.generic.id = ID_REMOVEBOTS;
+ s_ingame.removebots.generic.callback = InGame_Event;
+ s_ingame.removebots.string = "REMOVE BOTS";
+ s_ingame.removebots.color = color_red;
+ s_ingame.removebots.style = UI_CENTER|UI_SMALLFONT;
+ if( !trap_Cvar_VariableValue( "sv_running" ) || !trap_Cvar_VariableValue( "bot_enable" ) || (trap_Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER)) {
+ s_ingame.removebots.generic.flags |= QMF_GRAYED;
+ }
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.teamorders.generic.type = MTYPE_PTEXT;
+ s_ingame.teamorders.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.teamorders.generic.x = 320;
+ s_ingame.teamorders.generic.y = y;
+ s_ingame.teamorders.generic.id = ID_TEAMORDERS;
+ s_ingame.teamorders.generic.callback = InGame_Event;
+ s_ingame.teamorders.string = "TEAM ORDERS";
+ s_ingame.teamorders.color = color_red;
+ s_ingame.teamorders.style = UI_CENTER|UI_SMALLFONT;
+ if( !(trap_Cvar_VariableValue( "g_gametype" ) >= GT_TEAM) ) {
+ s_ingame.teamorders.generic.flags |= QMF_GRAYED;
+ }
+ else {
+ trap_GetClientState( &cs );
+ trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING );
+ team = atoi( Info_ValueForKey( info, "t" ) );
+ if( team == TEAM_SPECTATOR ) {
+ s_ingame.teamorders.generic.flags |= QMF_GRAYED;
+ }
+ }
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.setup.generic.type = MTYPE_PTEXT;
+ s_ingame.setup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.setup.generic.x = 320;
+ s_ingame.setup.generic.y = y;
+ s_ingame.setup.generic.id = ID_SETUP;
+ s_ingame.setup.generic.callback = InGame_Event;
+ s_ingame.setup.string = "SETUP";
+ s_ingame.setup.color = color_red;
+ s_ingame.setup.style = UI_CENTER|UI_SMALLFONT;
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.server.generic.type = MTYPE_PTEXT;
+ s_ingame.server.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.server.generic.x = 320;
+ s_ingame.server.generic.y = y;
+ s_ingame.server.generic.id = ID_SERVERINFO;
+ s_ingame.server.generic.callback = InGame_Event;
+ s_ingame.server.string = "SERVER INFO";
+ s_ingame.server.color = color_red;
+ s_ingame.server.style = UI_CENTER|UI_SMALLFONT;
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.restart.generic.type = MTYPE_PTEXT;
+ s_ingame.restart.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.restart.generic.x = 320;
+ s_ingame.restart.generic.y = y;
+ s_ingame.restart.generic.id = ID_RESTART;
+ s_ingame.restart.generic.callback = InGame_Event;
+ s_ingame.restart.string = "RESTART ARENA";
+ s_ingame.restart.color = color_red;
+ s_ingame.restart.style = UI_CENTER|UI_SMALLFONT;
+ if( !trap_Cvar_VariableValue( "sv_running" ) ) {
+ s_ingame.restart.generic.flags |= QMF_GRAYED;
+ }
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.resume.generic.type = MTYPE_PTEXT;
+ s_ingame.resume.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.resume.generic.x = 320;
+ s_ingame.resume.generic.y = y;
+ s_ingame.resume.generic.id = ID_RESUME;
+ s_ingame.resume.generic.callback = InGame_Event;
+ s_ingame.resume.string = "RESUME GAME";
+ s_ingame.resume.color = color_red;
+ s_ingame.resume.style = UI_CENTER|UI_SMALLFONT;
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.leave.generic.type = MTYPE_PTEXT;
+ s_ingame.leave.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.leave.generic.x = 320;
+ s_ingame.leave.generic.y = y;
+ s_ingame.leave.generic.id = ID_LEAVEARENA;
+ s_ingame.leave.generic.callback = InGame_Event;
+ s_ingame.leave.string = "LEAVE ARENA";
+ s_ingame.leave.color = color_red;
+ s_ingame.leave.style = UI_CENTER|UI_SMALLFONT;
+
+ y += INGAME_MENU_VERTICAL_SPACING;
+ s_ingame.quit.generic.type = MTYPE_PTEXT;
+ s_ingame.quit.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_ingame.quit.generic.x = 320;
+ s_ingame.quit.generic.y = y;
+ s_ingame.quit.generic.id = ID_QUIT;
+ s_ingame.quit.generic.callback = InGame_Event;
+ s_ingame.quit.string = "EXIT GAME";
+ s_ingame.quit.color = color_red;
+ s_ingame.quit.style = UI_CENTER|UI_SMALLFONT;
+
+ Menu_AddItem( &s_ingame.menu, &s_ingame.frame );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.team );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.addbots );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.removebots );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.teamorders );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.setup );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.server );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.restart );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.resume );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.leave );
+ Menu_AddItem( &s_ingame.menu, &s_ingame.quit );
+}
+
+
+/*
+=================
+InGame_Cache
+=================
+*/
+void InGame_Cache( void ) {
+ trap_R_RegisterShaderNoMip( INGAME_FRAME );
+}
+
+
+/*
+=================
+UI_InGameMenu
+=================
+*/
+void UI_InGameMenu( void ) {
+ // force as top level menu
+ uis.menusp = 0;
+
+ // set menu cursor to a nice location
+ uis.cursorx = 319;
+ uis.cursory = 80;
+
+ InGame_MenuInit();
+ UI_PushMenu( &s_ingame.menu );
+}
diff --git a/code/q3_ui/ui_loadconfig.c b/code/q3_ui/ui_loadconfig.c
new file mode 100644
index 0000000..ef7ae41
--- /dev/null
+++ b/code/q3_ui/ui_loadconfig.c
@@ -0,0 +1,274 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=============================================================================
+
+LOAD CONFIG MENU
+
+=============================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FIGHT0 "menu/art/load_0"
+#define ART_FIGHT1 "menu/art/load_1"
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_ARROWS "menu/art/arrows_horz_0"
+#define ART_ARROWLEFT "menu/art/arrows_horz_left"
+#define ART_ARROWRIGHT "menu/art/arrows_horz_right"
+
+#define MAX_CONFIGS 128
+#define NAMEBUFSIZE ( MAX_CONFIGS * 16 )
+
+#define ID_BACK 10
+#define ID_GO 11
+#define ID_LIST 12
+#define ID_LEFT 13
+#define ID_RIGHT 14
+
+#define ARROWS_WIDTH 128
+#define ARROWS_HEIGHT 48
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menulist_s list;
+
+ menubitmap_s arrows;
+ menubitmap_s left;
+ menubitmap_s right;
+ menubitmap_s back;
+ menubitmap_s go;
+
+ char names[NAMEBUFSIZE];
+ char* configlist[MAX_CONFIGS];
+} configs_t;
+
+static configs_t s_configs;
+
+
+/*
+===============
+LoadConfig_MenuEvent
+===============
+*/
+static void LoadConfig_MenuEvent( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch ( ((menucommon_s*)ptr)->id ) {
+ case ID_GO:
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "exec %s\n", s_configs.list.itemnames[s_configs.list.curvalue] ) );
+ UI_PopMenu();
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+
+ case ID_LEFT:
+ ScrollList_Key( &s_configs.list, K_LEFTARROW );
+ break;
+
+ case ID_RIGHT:
+ ScrollList_Key( &s_configs.list, K_RIGHTARROW );
+ break;
+ }
+}
+
+
+/*
+===============
+LoadConfig_MenuInit
+===============
+*/
+static void LoadConfig_MenuInit( void ) {
+ int i;
+ int len;
+ char *configname;
+
+ UI_LoadConfig_Cache();
+
+ memset( &s_configs, 0 ,sizeof(configs_t) );
+ s_configs.menu.wrapAround = qtrue;
+ s_configs.menu.fullscreen = qtrue;
+
+ s_configs.banner.generic.type = MTYPE_BTEXT;
+ s_configs.banner.generic.x = 320;
+ s_configs.banner.generic.y = 16;
+ s_configs.banner.string = "LOAD CONFIG";
+ s_configs.banner.color = color_white;
+ s_configs.banner.style = UI_CENTER;
+
+ s_configs.framel.generic.type = MTYPE_BITMAP;
+ s_configs.framel.generic.name = ART_FRAMEL;
+ s_configs.framel.generic.flags = QMF_INACTIVE;
+ s_configs.framel.generic.x = 0;
+ s_configs.framel.generic.y = 78;
+ s_configs.framel.width = 256;
+ s_configs.framel.height = 329;
+
+ s_configs.framer.generic.type = MTYPE_BITMAP;
+ s_configs.framer.generic.name = ART_FRAMER;
+ s_configs.framer.generic.flags = QMF_INACTIVE;
+ s_configs.framer.generic.x = 376;
+ s_configs.framer.generic.y = 76;
+ s_configs.framer.width = 256;
+ s_configs.framer.height = 334;
+
+ s_configs.arrows.generic.type = MTYPE_BITMAP;
+ s_configs.arrows.generic.name = ART_ARROWS;
+ s_configs.arrows.generic.flags = QMF_INACTIVE;
+ s_configs.arrows.generic.x = 320-ARROWS_WIDTH/2;
+ s_configs.arrows.generic.y = 400;
+ s_configs.arrows.width = ARROWS_WIDTH;
+ s_configs.arrows.height = ARROWS_HEIGHT;
+
+ s_configs.left.generic.type = MTYPE_BITMAP;
+ s_configs.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_configs.left.generic.x = 320-ARROWS_WIDTH/2;
+ s_configs.left.generic.y = 400;
+ s_configs.left.generic.id = ID_LEFT;
+ s_configs.left.generic.callback = LoadConfig_MenuEvent;
+ s_configs.left.width = ARROWS_WIDTH/2;
+ s_configs.left.height = ARROWS_HEIGHT;
+ s_configs.left.focuspic = ART_ARROWLEFT;
+
+ s_configs.right.generic.type = MTYPE_BITMAP;
+ s_configs.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_configs.right.generic.x = 320;
+ s_configs.right.generic.y = 400;
+ s_configs.right.generic.id = ID_RIGHT;
+ s_configs.right.generic.callback = LoadConfig_MenuEvent;
+ s_configs.right.width = ARROWS_WIDTH/2;
+ s_configs.right.height = ARROWS_HEIGHT;
+ s_configs.right.focuspic = ART_ARROWRIGHT;
+
+ s_configs.back.generic.type = MTYPE_BITMAP;
+ s_configs.back.generic.name = ART_BACK0;
+ s_configs.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_configs.back.generic.id = ID_BACK;
+ s_configs.back.generic.callback = LoadConfig_MenuEvent;
+ s_configs.back.generic.x = 0;
+ s_configs.back.generic.y = 480-64;
+ s_configs.back.width = 128;
+ s_configs.back.height = 64;
+ s_configs.back.focuspic = ART_BACK1;
+
+ s_configs.go.generic.type = MTYPE_BITMAP;
+ s_configs.go.generic.name = ART_FIGHT0;
+ s_configs.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_configs.go.generic.id = ID_GO;
+ s_configs.go.generic.callback = LoadConfig_MenuEvent;
+ s_configs.go.generic.x = 640;
+ s_configs.go.generic.y = 480-64;
+ s_configs.go.width = 128;
+ s_configs.go.height = 64;
+ s_configs.go.focuspic = ART_FIGHT1;
+
+ // scan for configs
+ s_configs.list.generic.type = MTYPE_SCROLLLIST;
+ s_configs.list.generic.flags = QMF_PULSEIFFOCUS;
+ s_configs.list.generic.callback = LoadConfig_MenuEvent;
+ s_configs.list.generic.id = ID_LIST;
+ s_configs.list.generic.x = 118;
+ s_configs.list.generic.y = 130;
+ s_configs.list.width = 16;
+ s_configs.list.height = 14;
+ s_configs.list.numitems = trap_FS_GetFileList( "", "cfg", s_configs.names, NAMEBUFSIZE );
+ s_configs.list.itemnames = (const char **)s_configs.configlist;
+ s_configs.list.columns = 3;
+
+ if (!s_configs.list.numitems) {
+ strcpy(s_configs.names,"No Files Found.");
+ s_configs.list.numitems = 1;
+
+ //degenerate case, not selectable
+ s_configs.go.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ }
+ else if (s_configs.list.numitems > MAX_CONFIGS)
+ s_configs.list.numitems = MAX_CONFIGS;
+
+ configname = s_configs.names;
+ for ( i = 0; i < s_configs.list.numitems; i++ ) {
+ s_configs.list.itemnames[i] = configname;
+
+ // strip extension
+ len = strlen( configname );
+ if (!Q_stricmp(configname + len - 4,".cfg"))
+ configname[len-4] = '\0';
+
+ Q_strupr(configname);
+
+ configname += len + 1;
+ }
+
+ Menu_AddItem( &s_configs.menu, &s_configs.banner );
+ Menu_AddItem( &s_configs.menu, &s_configs.framel );
+ Menu_AddItem( &s_configs.menu, &s_configs.framer );
+ Menu_AddItem( &s_configs.menu, &s_configs.list );
+ Menu_AddItem( &s_configs.menu, &s_configs.arrows );
+ Menu_AddItem( &s_configs.menu, &s_configs.left );
+ Menu_AddItem( &s_configs.menu, &s_configs.right );
+ Menu_AddItem( &s_configs.menu, &s_configs.back );
+ Menu_AddItem( &s_configs.menu, &s_configs.go );
+}
+
+/*
+=================
+UI_LoadConfig_Cache
+=================
+*/
+void UI_LoadConfig_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT0 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT1 );
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_ARROWS );
+ trap_R_RegisterShaderNoMip( ART_ARROWLEFT );
+ trap_R_RegisterShaderNoMip( ART_ARROWRIGHT );
+}
+
+
+/*
+===============
+UI_LoadConfigMenu
+===============
+*/
+void UI_LoadConfigMenu( void ) {
+ LoadConfig_MenuInit();
+ UI_PushMenu( &s_configs.menu );
+}
+
diff --git a/code/q3_ui/ui_local.h b/code/q3_ui/ui_local.h
new file mode 100644
index 0000000..464298e
--- /dev/null
+++ b/code/q3_ui/ui_local.h
@@ -0,0 +1,802 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#ifndef __UI_LOCAL_H__
+#define __UI_LOCAL_H__
+
+#include "../qcommon/q_shared.h"
+#include "../renderer/tr_types.h"
+//NOTE: include the ui_public.h from the new UI
+#include "../ui/ui_public.h"
+//redefine to old API version
+#undef UI_API_VERSION
+#define UI_API_VERSION 4
+#include "../client/keycodes.h"
+#include "../game/bg_public.h"
+
+typedef void (*voidfunc_f)(void);
+
+extern vmCvar_t ui_ffa_fraglimit;
+extern vmCvar_t ui_ffa_timelimit;
+
+extern vmCvar_t ui_tourney_fraglimit;
+extern vmCvar_t ui_tourney_timelimit;
+
+extern vmCvar_t ui_team_fraglimit;
+extern vmCvar_t ui_team_timelimit;
+extern vmCvar_t ui_team_friendly;
+
+extern vmCvar_t ui_ctf_capturelimit;
+extern vmCvar_t ui_ctf_timelimit;
+extern vmCvar_t ui_ctf_friendly;
+
+extern vmCvar_t ui_arenasFile;
+extern vmCvar_t ui_botsFile;
+extern vmCvar_t ui_spScores1;
+extern vmCvar_t ui_spScores2;
+extern vmCvar_t ui_spScores3;
+extern vmCvar_t ui_spScores4;
+extern vmCvar_t ui_spScores5;
+extern vmCvar_t ui_spAwards;
+extern vmCvar_t ui_spVideos;
+extern vmCvar_t ui_spSkill;
+
+extern vmCvar_t ui_spSelection;
+
+extern vmCvar_t ui_browserMaster;
+extern vmCvar_t ui_browserGameType;
+extern vmCvar_t ui_browserSortKey;
+extern vmCvar_t ui_browserShowFull;
+extern vmCvar_t ui_browserShowEmpty;
+
+extern vmCvar_t ui_brassTime;
+extern vmCvar_t ui_drawCrosshair;
+extern vmCvar_t ui_drawCrosshairNames;
+extern vmCvar_t ui_marks;
+
+extern vmCvar_t ui_server1;
+extern vmCvar_t ui_server2;
+extern vmCvar_t ui_server3;
+extern vmCvar_t ui_server4;
+extern vmCvar_t ui_server5;
+extern vmCvar_t ui_server6;
+extern vmCvar_t ui_server7;
+extern vmCvar_t ui_server8;
+extern vmCvar_t ui_server9;
+extern vmCvar_t ui_server10;
+extern vmCvar_t ui_server11;
+extern vmCvar_t ui_server12;
+extern vmCvar_t ui_server13;
+extern vmCvar_t ui_server14;
+extern vmCvar_t ui_server15;
+extern vmCvar_t ui_server16;
+
+extern vmCvar_t ui_cdkey;
+extern vmCvar_t ui_cdkeychecked;
+extern vmCvar_t ui_ioq3;
+
+
+//
+// ui_qmenu.c
+//
+
+#define RCOLUMN_OFFSET ( BIGCHAR_WIDTH )
+#define LCOLUMN_OFFSET (-BIGCHAR_WIDTH )
+
+#define SLIDER_RANGE 10
+#define MAX_EDIT_LINE 256
+
+#define MAX_MENUDEPTH 8
+#define MAX_MENUITEMS 64
+
+#define MTYPE_NULL 0
+#define MTYPE_SLIDER 1
+#define MTYPE_ACTION 2
+#define MTYPE_SPINCONTROL 3
+#define MTYPE_FIELD 4
+#define MTYPE_RADIOBUTTON 5
+#define MTYPE_BITMAP 6
+#define MTYPE_TEXT 7
+#define MTYPE_SCROLLLIST 8
+#define MTYPE_PTEXT 9
+#define MTYPE_BTEXT 10
+
+#define QMF_BLINK ((unsigned int) 0x00000001)
+#define QMF_SMALLFONT ((unsigned int) 0x00000002)
+#define QMF_LEFT_JUSTIFY ((unsigned int) 0x00000004)
+#define QMF_CENTER_JUSTIFY ((unsigned int) 0x00000008)
+#define QMF_RIGHT_JUSTIFY ((unsigned int) 0x00000010)
+#define QMF_NUMBERSONLY ((unsigned int) 0x00000020) // edit field is only numbers
+#define QMF_HIGHLIGHT ((unsigned int) 0x00000040)
+#define QMF_HIGHLIGHT_IF_FOCUS ((unsigned int) 0x00000080) // steady focus
+#define QMF_PULSEIFFOCUS ((unsigned int) 0x00000100) // pulse if focus
+#define QMF_HASMOUSEFOCUS ((unsigned int) 0x00000200)
+#define QMF_NOONOFFTEXT ((unsigned int) 0x00000400)
+#define QMF_MOUSEONLY ((unsigned int) 0x00000800) // only mouse input allowed
+#define QMF_HIDDEN ((unsigned int) 0x00001000) // skips drawing
+#define QMF_GRAYED ((unsigned int) 0x00002000) // grays and disables
+#define QMF_INACTIVE ((unsigned int) 0x00004000) // disables any input
+#define QMF_NODEFAULTINIT ((unsigned int) 0x00008000) // skip default initialization
+#define QMF_OWNERDRAW ((unsigned int) 0x00010000)
+#define QMF_PULSE ((unsigned int) 0x00020000)
+#define QMF_LOWERCASE ((unsigned int) 0x00040000) // edit field is all lower case
+#define QMF_UPPERCASE ((unsigned int) 0x00080000) // edit field is all upper case
+#define QMF_SILENT ((unsigned int) 0x00100000)
+
+// callback notifications
+#define QM_GOTFOCUS 1
+#define QM_LOSTFOCUS 2
+#define QM_ACTIVATED 3
+
+typedef struct _tag_menuframework
+{
+ int cursor;
+ int cursor_prev;
+
+ int nitems;
+ void *items[MAX_MENUITEMS];
+
+ void (*draw) (void);
+ sfxHandle_t (*key) (int key);
+
+ qboolean wrapAround;
+ qboolean fullscreen;
+ qboolean showlogo;
+} menuframework_s;
+
+typedef struct
+{
+ int type;
+ const char *name;
+ int id;
+ int x, y;
+ int left;
+ int top;
+ int right;
+ int bottom;
+ menuframework_s *parent;
+ int menuPosition;
+ unsigned int flags;
+
+ void (*callback)( void *self, int event );
+ void (*statusbar)( void *self );
+ void (*ownerdraw)( void *self );
+} menucommon_s;
+
+typedef struct {
+ int cursor;
+ int scroll;
+ int widthInChars;
+ char buffer[MAX_EDIT_LINE];
+ int maxchars;
+} mfield_t;
+
+typedef struct
+{
+ menucommon_s generic;
+ mfield_t field;
+} menufield_s;
+
+typedef struct
+{
+ menucommon_s generic;
+
+ float minvalue;
+ float maxvalue;
+ float curvalue;
+
+ float range;
+} menuslider_s;
+
+typedef struct
+{
+ menucommon_s generic;
+
+ int oldvalue;
+ int curvalue;
+ int numitems;
+ int top;
+
+ const char **itemnames;
+
+ int width;
+ int height;
+ int columns;
+ int seperation;
+} menulist_s;
+
+typedef struct
+{
+ menucommon_s generic;
+} menuaction_s;
+
+typedef struct
+{
+ menucommon_s generic;
+ int curvalue;
+} menuradiobutton_s;
+
+typedef struct
+{
+ menucommon_s generic;
+ char* focuspic;
+ char* errorpic;
+ qhandle_t shader;
+ qhandle_t focusshader;
+ int width;
+ int height;
+ float* focuscolor;
+} menubitmap_s;
+
+typedef struct
+{
+ menucommon_s generic;
+ char* string;
+ int style;
+ float* color;
+} menutext_s;
+
+extern void Menu_Cache( void );
+extern void Menu_Focus( menucommon_s *m );
+extern void Menu_AddItem( menuframework_s *menu, void *item );
+extern void Menu_AdjustCursor( menuframework_s *menu, int dir );
+extern void Menu_Draw( menuframework_s *menu );
+extern void *Menu_ItemAtCursor( menuframework_s *m );
+extern sfxHandle_t Menu_ActivateItem( menuframework_s *s, menucommon_s* item );
+extern void Menu_SetCursor( menuframework_s *s, int cursor );
+extern void Menu_SetCursorToItem( menuframework_s *m, void* ptr );
+extern sfxHandle_t Menu_DefaultKey( menuframework_s *s, int key );
+extern void Bitmap_Init( menubitmap_s *b );
+extern void Bitmap_Draw( menubitmap_s *b );
+extern void ScrollList_Draw( menulist_s *l );
+extern sfxHandle_t ScrollList_Key( menulist_s *l, int key );
+extern sfxHandle_t menu_in_sound;
+extern sfxHandle_t menu_move_sound;
+extern sfxHandle_t menu_out_sound;
+extern sfxHandle_t menu_buzz_sound;
+extern sfxHandle_t menu_null_sound;
+extern sfxHandle_t weaponChangeSound;
+extern vec4_t menu_text_color;
+extern vec4_t menu_grayed_color;
+extern vec4_t menu_dark_color;
+extern vec4_t menu_highlight_color;
+extern vec4_t menu_red_color;
+extern vec4_t menu_black_color;
+extern vec4_t menu_dim_color;
+extern vec4_t color_black;
+extern vec4_t color_white;
+extern vec4_t color_yellow;
+extern vec4_t color_blue;
+extern vec4_t color_orange;
+extern vec4_t color_red;
+extern vec4_t color_dim;
+extern vec4_t name_color;
+extern vec4_t list_color;
+extern vec4_t listbar_color;
+extern vec4_t text_color_disabled;
+extern vec4_t text_color_normal;
+extern vec4_t text_color_highlight;
+
+extern char *ui_medalNames[];
+extern char *ui_medalPicNames[];
+extern char *ui_medalSounds[];
+
+//
+// ui_mfield.c
+//
+extern void MField_Clear( mfield_t *edit );
+extern void MField_KeyDownEvent( mfield_t *edit, int key );
+extern void MField_CharEvent( mfield_t *edit, int ch );
+extern void MField_Draw( mfield_t *edit, int x, int y, int style, vec4_t color );
+extern void MenuField_Init( menufield_s* m );
+extern void MenuField_Draw( menufield_s *f );
+extern sfxHandle_t MenuField_Key( menufield_s* m, int* key );
+
+//
+// ui_menu.c
+//
+extern void MainMenu_Cache( void );
+extern void UI_MainMenu(void);
+extern void UI_RegisterCvars( void );
+extern void UI_UpdateCvars( void );
+
+//
+// ui_credits.c
+//
+extern void UI_CreditMenu( void );
+
+//
+// ui_ingame.c
+//
+extern void InGame_Cache( void );
+extern void UI_InGameMenu(void);
+
+//
+// ui_confirm.c
+//
+extern void ConfirmMenu_Cache( void );
+extern void UI_ConfirmMenu( const char *question, void (*draw)( void ), void (*action)( qboolean result ) );
+extern void UI_ConfirmMenu_Style( const char *question, int style, void (*draw)( void ), void (*action)( qboolean result ) );
+extern void UI_Message( const char **lines );
+
+//
+// ui_setup.c
+//
+extern void UI_SetupMenu_Cache( void );
+extern void UI_SetupMenu(void);
+
+//
+// ui_team.c
+//
+extern void UI_TeamMainMenu( void );
+extern void TeamMain_Cache( void );
+
+//
+// ui_connect.c
+//
+extern void UI_DrawConnectScreen( qboolean overlay );
+
+//
+// ui_controls2.c
+//
+extern void UI_ControlsMenu( void );
+extern void Controls_Cache( void );
+
+//
+// ui_demo2.c
+//
+extern void UI_DemosMenu( void );
+extern void Demos_Cache( void );
+
+//
+// ui_cinematics.c
+//
+extern void UI_CinematicsMenu( void );
+extern void UI_CinematicsMenu_f( void );
+extern void UI_CinematicsMenu_Cache( void );
+
+//
+// ui_mods.c
+//
+extern void UI_ModsMenu( void );
+extern void UI_ModsMenu_Cache( void );
+
+//
+// ui_cdkey.c
+//
+extern void UI_CDKeyMenu( void );
+extern void UI_CDKeyMenu_Cache( void );
+extern void UI_CDKeyMenu_f( void );
+
+//
+// ui_playermodel.c
+//
+extern void UI_PlayerModelMenu( void );
+extern void PlayerModel_Cache( void );
+
+//
+// ui_playersettings.c
+//
+extern void UI_PlayerSettingsMenu( void );
+extern void PlayerSettings_Cache( void );
+
+//
+// ui_preferences.c
+//
+extern void UI_PreferencesMenu( void );
+extern void Preferences_Cache( void );
+
+//
+// ui_specifyleague.c
+//
+extern void UI_SpecifyLeagueMenu( void );
+extern void SpecifyLeague_Cache( void );
+
+//
+// ui_specifyserver.c
+//
+extern void UI_SpecifyServerMenu( void );
+extern void SpecifyServer_Cache( void );
+
+//
+// ui_servers2.c
+//
+#define MAX_FAVORITESERVERS 16
+
+extern void UI_ArenaServersMenu( void );
+extern void ArenaServers_Cache( void );
+
+//
+// ui_startserver.c
+//
+extern void UI_StartServerMenu( qboolean multiplayer );
+extern void StartServer_Cache( void );
+extern void ServerOptions_Cache( void );
+extern void UI_BotSelectMenu( char *bot );
+extern void UI_BotSelectMenu_Cache( void );
+
+//
+// ui_serverinfo.c
+//
+extern void UI_ServerInfoMenu( void );
+extern void ServerInfo_Cache( void );
+
+//
+// ui_video.c
+//
+extern void UI_GraphicsOptionsMenu( void );
+extern void GraphicsOptions_Cache( void );
+extern void DriverInfo_Cache( void );
+
+//
+// ui_players.c
+//
+
+//FIXME ripped from cg_local.h
+typedef struct {
+ int oldFrame;
+ int oldFrameTime; // time when ->oldFrame was exactly on
+
+ int frame;
+ int frameTime; // time when ->frame will be exactly on
+
+ float backlerp;
+
+ float yawAngle;
+ qboolean yawing;
+ float pitchAngle;
+ qboolean pitching;
+
+ int animationNumber; // may include ANIM_TOGGLEBIT
+ animation_t *animation;
+ int animationTime; // time when the first frame of the animation will be exact
+} lerpFrame_t;
+
+typedef struct {
+ // model info
+ qhandle_t legsModel;
+ qhandle_t legsSkin;
+ lerpFrame_t legs;
+
+ qhandle_t torsoModel;
+ qhandle_t torsoSkin;
+ lerpFrame_t torso;
+
+ qhandle_t headModel;
+ qhandle_t headSkin;
+
+ animation_t animations[MAX_ANIMATIONS];
+
+ qhandle_t weaponModel;
+ qhandle_t barrelModel;
+ qhandle_t flashModel;
+ vec3_t flashDlightColor;
+ int muzzleFlashTime;
+
+ // currently in use drawing parms
+ vec3_t viewAngles;
+ vec3_t moveAngles;
+ weapon_t currentWeapon;
+ int legsAnim;
+ int torsoAnim;
+
+ // animation vars
+ weapon_t weapon;
+ weapon_t lastWeapon;
+ weapon_t pendingWeapon;
+ int weaponTimer;
+ int pendingLegsAnim;
+ int torsoAnimationTimer;
+
+ int pendingTorsoAnim;
+ int legsAnimationTimer;
+
+ qboolean chat;
+ qboolean newModel;
+
+ qboolean barrelSpinning;
+ float barrelAngle;
+ int barrelTime;
+
+ int realWeapon;
+} playerInfo_t;
+
+void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time );
+void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model );
+void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNum, qboolean chat );
+qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName );
+
+//
+// ui_atoms.c
+//
+typedef struct {
+ int frametime;
+ int realtime;
+ int cursorx;
+ int cursory;
+ int menusp;
+ menuframework_s* activemenu;
+ menuframework_s* stack[MAX_MENUDEPTH];
+ glconfig_t glconfig;
+ qboolean debug;
+ qhandle_t whiteShader;
+ qhandle_t menuBackShader;
+ qhandle_t menuBackNoLogoShader;
+ qhandle_t charset;
+ qhandle_t charsetProp;
+ qhandle_t charsetPropGlow;
+ qhandle_t charsetPropB;
+ qhandle_t cursor;
+ qhandle_t rb_on;
+ qhandle_t rb_off;
+ float xscale;
+ float yscale;
+ float bias;
+ qboolean demoversion;
+ qboolean firstdraw;
+} uiStatic_t;
+
+extern void UI_Init( void );
+extern void UI_Shutdown( void );
+extern void UI_KeyEvent( int key, int down );
+extern void UI_MouseEvent( int dx, int dy );
+extern void UI_Refresh( int realtime );
+extern qboolean UI_ConsoleCommand( int realTime );
+extern float UI_ClampCvar( float min, float max, float value );
+extern void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname );
+extern void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader );
+extern void UI_FillRect( float x, float y, float width, float height, const float *color );
+extern void UI_DrawRect( float x, float y, float width, float height, const float *color );
+extern void UI_UpdateScreen( void );
+extern void UI_SetColor( const float *rgba );
+extern void UI_LerpColor(vec4_t a, vec4_t b, vec4_t c, float t);
+extern void UI_DrawBannerString( int x, int y, const char* str, int style, vec4_t color );
+extern float UI_ProportionalSizeScale( int style );
+extern void UI_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color );
+extern void UI_DrawProportionalString_AutoWrapped( int x, int ystart, int xmax, int ystep, const char* str, int style, vec4_t color );
+extern int UI_ProportionalStringWidth( const char* str );
+extern void UI_DrawString( int x, int y, const char* str, int style, vec4_t color );
+extern void UI_DrawChar( int x, int y, int ch, int style, vec4_t color );
+extern qboolean UI_CursorInRect (int x, int y, int width, int height);
+extern void UI_AdjustFrom640( float *x, float *y, float *w, float *h );
+extern void UI_DrawTextBox (int x, int y, int width, int lines);
+extern qboolean UI_IsFullscreen( void );
+extern void UI_SetActiveMenu( uiMenuCommand_t menu );
+extern void UI_PushMenu ( menuframework_s *menu );
+extern void UI_PopMenu (void);
+extern void UI_ForceMenuOff (void);
+extern char *UI_Argv( int arg );
+extern char *UI_Cvar_VariableString( const char *var_name );
+extern void UI_Refresh( int time );
+extern void UI_StartDemoLoop( void );
+extern qboolean m_entersound;
+extern uiStatic_t uis;
+
+//
+// ui_spLevel.c
+//
+void UI_SPLevelMenu_Cache( void );
+void UI_SPLevelMenu( void );
+void UI_SPLevelMenu_f( void );
+void UI_SPLevelMenu_ReInit( void );
+
+//
+// ui_spArena.c
+//
+void UI_SPArena_Start( const char *arenaInfo );
+
+//
+// ui_spPostgame.c
+//
+void UI_SPPostgameMenu_Cache( void );
+void UI_SPPostgameMenu_f( void );
+
+//
+// ui_spSkill.c
+//
+void UI_SPSkillMenu( const char *arenaInfo );
+void UI_SPSkillMenu_Cache( void );
+
+//
+// ui_syscalls.c
+//
+void trap_Print( const char *string );
+void trap_Error( const char *string );
+int trap_Milliseconds( void );
+void trap_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags );
+void trap_Cvar_Update( vmCvar_t *vmCvar );
+void trap_Cvar_Set( const char *var_name, const char *value );
+float trap_Cvar_VariableValue( const char *var_name );
+void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize );
+void trap_Cvar_SetValue( const char *var_name, float value );
+void trap_Cvar_Reset( const char *name );
+void trap_Cvar_Create( const char *var_name, const char *var_value, int flags );
+void trap_Cvar_InfoStringBuffer( int bit, char *buffer, int bufsize );
+int trap_Argc( void );
+void trap_Argv( int n, char *buffer, int bufferLength );
+void trap_Cmd_ExecuteText( int exec_when, const char *text ); // don't use EXEC_NOW!
+int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode );
+void trap_FS_Read( void *buffer, int len, fileHandle_t f );
+void trap_FS_Write( const void *buffer, int len, fileHandle_t f );
+void trap_FS_FCloseFile( fileHandle_t f );
+int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize );
+int trap_FS_Seek( fileHandle_t f, long offset, int origin ); // fsOrigin_t
+qhandle_t trap_R_RegisterModel( const char *name );
+qhandle_t trap_R_RegisterSkin( const char *name );
+qhandle_t trap_R_RegisterShaderNoMip( const char *name );
+void trap_R_ClearScene( void );
+void trap_R_AddRefEntityToScene( const refEntity_t *re );
+void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts );
+void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b );
+void trap_R_RenderScene( const refdef_t *fd );
+void trap_R_SetColor( const float *rgba );
+void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader );
+void trap_UpdateScreen( void );
+int trap_CM_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName );
+void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum );
+sfxHandle_t trap_S_RegisterSound( const char *sample, qboolean compressed );
+void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen );
+void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen );
+void trap_Key_SetBinding( int keynum, const char *binding );
+qboolean trap_Key_IsDown( int keynum );
+qboolean trap_Key_GetOverstrikeMode( void );
+void trap_Key_SetOverstrikeMode( qboolean state );
+void trap_Key_ClearStates( void );
+int trap_Key_GetCatcher( void );
+void trap_Key_SetCatcher( int catcher );
+void trap_GetClipboardData( char *buf, int bufsize );
+void trap_GetClientState( uiClientState_t *state );
+void trap_GetGlconfig( glconfig_t *glconfig );
+int trap_GetConfigString( int index, char* buff, int buffsize );
+int trap_LAN_GetServerCount( int source );
+void trap_LAN_GetServerAddressString( int source, int n, char *buf, int buflen );
+void trap_LAN_GetServerInfo( int source, int n, char *buf, int buflen );
+int trap_LAN_GetPingQueueCount( void );
+int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen );
+void trap_LAN_ClearPing( int n );
+void trap_LAN_GetPing( int n, char *buf, int buflen, int *pingtime );
+void trap_LAN_GetPingInfo( int n, char *buf, int buflen );
+int trap_MemoryRemaining( void );
+void trap_GetCDKey( char *buf, int buflen );
+void trap_SetCDKey( char *buf );
+
+qboolean trap_VerifyCDKey( const char *key, const char *chksum);
+
+void trap_SetPbClStatus( int status );
+
+//
+// ui_addbots.c
+//
+void UI_AddBots_Cache( void );
+void UI_AddBotsMenu( void );
+
+//
+// ui_removebots.c
+//
+void UI_RemoveBots_Cache( void );
+void UI_RemoveBotsMenu( void );
+
+//
+// ui_teamorders.c
+//
+extern void UI_TeamOrdersMenu( void );
+extern void UI_TeamOrdersMenu_f( void );
+extern void UI_TeamOrdersMenu_Cache( void );
+
+//
+// ui_loadconfig.c
+//
+void UI_LoadConfig_Cache( void );
+void UI_LoadConfigMenu( void );
+
+//
+// ui_saveconfig.c
+//
+void UI_SaveConfigMenu_Cache( void );
+void UI_SaveConfigMenu( void );
+
+//
+// ui_display.c
+//
+void UI_DisplayOptionsMenu_Cache( void );
+void UI_DisplayOptionsMenu( void );
+
+//
+// ui_sound.c
+//
+void UI_SoundOptionsMenu_Cache( void );
+void UI_SoundOptionsMenu( void );
+
+//
+// ui_network.c
+//
+void UI_NetworkOptionsMenu_Cache( void );
+void UI_NetworkOptionsMenu( void );
+
+//
+// ui_gameinfo.c
+//
+typedef enum {
+ AWARD_ACCURACY,
+ AWARD_IMPRESSIVE,
+ AWARD_EXCELLENT,
+ AWARD_GAUNTLET,
+ AWARD_FRAGS,
+ AWARD_PERFECT
+} awardType_t;
+
+const char *UI_GetArenaInfoByNumber( int num );
+const char *UI_GetArenaInfoByMap( const char *map );
+const char *UI_GetSpecialArenaInfo( const char *tag );
+int UI_GetNumArenas( void );
+int UI_GetNumSPArenas( void );
+int UI_GetNumSPTiers( void );
+
+char *UI_GetBotInfoByNumber( int num );
+char *UI_GetBotInfoByName( const char *name );
+int UI_GetNumBots( void );
+
+void UI_GetBestScore( int level, int *score, int *skill );
+void UI_SetBestScore( int level, int score );
+int UI_TierCompleted( int levelWon );
+qboolean UI_ShowTierVideo( int tier );
+qboolean UI_CanShowTierVideo( int tier );
+int UI_GetCurrentGame( void );
+void UI_NewGame( void );
+void UI_LogAwardData( int award, int data );
+int UI_GetAwardLevel( int award );
+
+void UI_SPUnlock_f( void );
+void UI_SPUnlockMedals_f( void );
+
+void UI_InitGameinfo( void );
+
+//GRank
+
+//
+// ui_rankings.c
+//
+void Rankings_DrawText( void* self );
+void Rankings_DrawName( void* self );
+void Rankings_DrawPassword( void* self );
+void Rankings_Cache( void );
+void UI_RankingsMenu( void );
+
+//
+// ui_login.c
+//
+void Login_Cache( void );
+void UI_LoginMenu( void );
+
+//
+// ui_signup.c
+//
+void Signup_Cache( void );
+void UI_SignupMenu( void );
+
+//
+// ui_rankstatus.c
+//
+void RankStatus_Cache( void );
+void UI_RankStatusMenu( void );
+
+#endif
diff --git a/code/q3_ui/ui_login.c b/code/q3_ui/ui_login.c
new file mode 100644
index 0000000..a0df46b
--- /dev/null
+++ b/code/q3_ui/ui_login.c
@@ -0,0 +1,208 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+//
+// ui_login.c
+//
+
+#include "ui_local.h"
+
+
+#define LOGIN_FRAME "menu/art/cut_frame"
+
+#define ID_NAME 100
+#define ID_NAME_BOX 101
+#define ID_PASSWORD 102
+#define ID_PASSWORD_BOX 103
+#define ID_LOGIN 104
+#define ID_CANCEL 105
+
+
+typedef struct
+{
+ menuframework_s menu;
+ menubitmap_s frame;
+ menutext_s name;
+ menufield_s name_box;
+ menutext_s password;
+ menufield_s password_box;
+ menutext_s login;
+ menutext_s cancel;
+} login_t;
+
+static login_t s_login;
+
+static menuframework_s s_login_menu;
+static menuaction_s s_login_login;
+static menuaction_s s_login_cancel;
+
+static vec4_t s_login_color_prompt = {1.00, 0.43, 0.00, 1.00};
+
+/*
+===============
+Login_MenuEvent
+===============
+*/
+static void Login_MenuEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_LOGIN:
+ // set name ``
+ //trap_Cvar_Set( "name", s_login.name_box.field.buffer );
+ /*
+ trap_Cvar_Set( "rank_name", s_login.name_box.field.buffer );
+ trap_Cvar_Set( "rank_pwd", s_login.password_box.field.buffer );
+ */
+
+ // login
+ trap_CL_UI_RankUserLogin(
+ s_login.name_box.field.buffer,
+ s_login.password_box.field.buffer );
+
+ UI_ForceMenuOff();
+ break;
+
+ case ID_CANCEL:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+Login_MenuInit
+===============
+*/
+void Login_MenuInit( void ) {
+ int y;
+
+ memset( &s_login, 0, sizeof(s_login) );
+
+ Login_Cache();
+
+ s_login.menu.wrapAround = qtrue;
+ s_login.menu.fullscreen = qfalse;
+
+ s_login.frame.generic.type = MTYPE_BITMAP;
+ s_login.frame.generic.flags = QMF_INACTIVE;
+ s_login.frame.generic.name = LOGIN_FRAME;
+ s_login.frame.generic.x = 142; //320-233;
+ s_login.frame.generic.y = 118; //240-166;
+ s_login.frame.width = 359; //466;
+ s_login.frame.height = 256; //332;
+
+ y = 214;
+
+ s_login.name.generic.type = MTYPE_PTEXT;
+ s_login.name.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE;
+ s_login.name.generic.id = ID_NAME;
+ s_login.name.generic.x = 310;
+ s_login.name.generic.y = y;
+ s_login.name.string = "NAME";
+ s_login.name.style = UI_RIGHT|UI_SMALLFONT;
+ s_login.name.color = s_login_color_prompt;
+
+ s_login.name_box.generic.type = MTYPE_FIELD;
+ s_login.name_box.generic.ownerdraw = Rankings_DrawName;
+ s_login.name_box.generic.name = "";
+ s_login.name_box.generic.flags = 0;
+ s_login.name_box.generic.x = 330;
+ s_login.name_box.generic.y = y;
+ s_login.name_box.field.widthInChars = 16;
+ s_login.name_box.field.maxchars = 16;
+ y += 20;
+
+ s_login.password.generic.type = MTYPE_PTEXT;
+ s_login.password.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE;
+ s_login.password.generic.id = ID_PASSWORD;
+ s_login.password.generic.x = 310;
+ s_login.password.generic.y = y;
+ s_login.password.string = "PASSWORD";
+ s_login.password.style = UI_RIGHT|UI_SMALLFONT;
+ s_login.password.color = s_login_color_prompt;
+
+ s_login.password_box.generic.type = MTYPE_FIELD;
+ s_login.password_box.generic.ownerdraw = Rankings_DrawPassword;
+ s_login.password_box.generic.name = "";
+ s_login.password_box.generic.flags = 0;
+ s_login.password_box.generic.x = 330;
+ s_login.password_box.generic.y = y;
+ s_login.password_box.field.widthInChars = 16;
+ s_login.password_box.field.maxchars = 16;
+ y += 40;
+
+ s_login.login.generic.type = MTYPE_PTEXT;
+ s_login.login.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_login.login.generic.id = ID_LOGIN;
+ s_login.login.generic.callback = Login_MenuEvent;
+ s_login.login.generic.x = 310;
+ s_login.login.generic.y = y;
+ s_login.login.string = "LOGIN";
+ s_login.login.style = UI_RIGHT|UI_SMALLFONT;
+ s_login.login.color = colorRed;
+
+ s_login.cancel.generic.type = MTYPE_PTEXT;
+ s_login.cancel.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_login.cancel.generic.id = ID_CANCEL;
+ s_login.cancel.generic.callback = Login_MenuEvent;
+ s_login.cancel.generic.x = 330;
+ s_login.cancel.generic.y = y;
+ s_login.cancel.string = "CANCEL";
+ s_login.cancel.style = UI_LEFT|UI_SMALLFONT;
+ s_login.cancel.color = colorRed;
+ y += 20;
+
+ Menu_AddItem( &s_login.menu, (void*) &s_login.frame );
+ Menu_AddItem( &s_login.menu, (void*) &s_login.name );
+ Menu_AddItem( &s_login.menu, (void*) &s_login.name_box );
+ Menu_AddItem( &s_login.menu, (void*) &s_login.password );
+ Menu_AddItem( &s_login.menu, (void*) &s_login.password_box );
+ Menu_AddItem( &s_login.menu, (void*) &s_login.login );
+ Menu_AddItem( &s_login.menu, (void*) &s_login.cancel );
+}
+
+
+/*
+===============
+Login_Cache
+===============
+*/
+void Login_Cache( void ) {
+ trap_R_RegisterShaderNoMip( LOGIN_FRAME );
+}
+
+
+/*
+===============
+UI_LoginMenu
+===============
+*/
+void UI_LoginMenu( void ) {
+ Login_MenuInit();
+ UI_PushMenu ( &s_login.menu );
+}
+
+
diff --git a/code/q3_ui/ui_main.c b/code/q3_ui/ui_main.c
new file mode 100644
index 0000000..9876b92
--- /dev/null
+++ b/code/q3_ui/ui_main.c
@@ -0,0 +1,249 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; 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"
+
+
+/*
+================
+vmMain
+
+This is the only way control passes into the module.
+This must be the very first function compiled into the .qvm file
+================
+*/
+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();
+ 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_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;
+ case UI_HASUNIQUECDKEY: // mod authors need to observe this
+ return qtrue; // change this to qfalse for mods!
+ }
+
+ return -1;
+}
+
+
+/*
+================
+cvars
+================
+*/
+
+typedef struct {
+ vmCvar_t *vmCvar;
+ char *cvarName;
+ char *defaultString;
+ int cvarFlags;
+} cvarTable_t;
+
+vmCvar_t ui_ffa_fraglimit;
+vmCvar_t ui_ffa_timelimit;
+
+vmCvar_t ui_tourney_fraglimit;
+vmCvar_t ui_tourney_timelimit;
+
+vmCvar_t ui_team_fraglimit;
+vmCvar_t ui_team_timelimit;
+vmCvar_t ui_team_friendly;
+
+vmCvar_t ui_ctf_capturelimit;
+vmCvar_t ui_ctf_timelimit;
+vmCvar_t ui_ctf_friendly;
+
+vmCvar_t ui_arenasFile;
+vmCvar_t ui_botsFile;
+vmCvar_t ui_spScores1;
+vmCvar_t ui_spScores2;
+vmCvar_t ui_spScores3;
+vmCvar_t ui_spScores4;
+vmCvar_t ui_spScores5;
+vmCvar_t ui_spAwards;
+vmCvar_t ui_spVideos;
+vmCvar_t ui_spSkill;
+
+vmCvar_t ui_spSelection;
+
+vmCvar_t ui_browserMaster;
+vmCvar_t ui_browserGameType;
+vmCvar_t ui_browserSortKey;
+vmCvar_t ui_browserShowFull;
+vmCvar_t ui_browserShowEmpty;
+
+vmCvar_t ui_brassTime;
+vmCvar_t ui_drawCrosshair;
+vmCvar_t ui_drawCrosshairNames;
+vmCvar_t ui_marks;
+
+vmCvar_t ui_server1;
+vmCvar_t ui_server2;
+vmCvar_t ui_server3;
+vmCvar_t ui_server4;
+vmCvar_t ui_server5;
+vmCvar_t ui_server6;
+vmCvar_t ui_server7;
+vmCvar_t ui_server8;
+vmCvar_t ui_server9;
+vmCvar_t ui_server10;
+vmCvar_t ui_server11;
+vmCvar_t ui_server12;
+vmCvar_t ui_server13;
+vmCvar_t ui_server14;
+vmCvar_t ui_server15;
+vmCvar_t ui_server16;
+
+vmCvar_t ui_cdkeychecked;
+vmCvar_t ui_ioq3;
+
+static cvarTable_t cvarTable[] = {
+ { &ui_ffa_fraglimit, "ui_ffa_fraglimit", "20", CVAR_ARCHIVE },
+ { &ui_ffa_timelimit, "ui_ffa_timelimit", "0", CVAR_ARCHIVE },
+
+ { &ui_tourney_fraglimit, "ui_tourney_fraglimit", "0", CVAR_ARCHIVE },
+ { &ui_tourney_timelimit, "ui_tourney_timelimit", "15", CVAR_ARCHIVE },
+
+ { &ui_team_fraglimit, "ui_team_fraglimit", "0", CVAR_ARCHIVE },
+ { &ui_team_timelimit, "ui_team_timelimit", "20", CVAR_ARCHIVE },
+ { &ui_team_friendly, "ui_team_friendly", "1", CVAR_ARCHIVE },
+
+ { &ui_ctf_capturelimit, "ui_ctf_capturelimit", "8", CVAR_ARCHIVE },
+ { &ui_ctf_timelimit, "ui_ctf_timelimit", "30", CVAR_ARCHIVE },
+ { &ui_ctf_friendly, "ui_ctf_friendly", "0", CVAR_ARCHIVE },
+
+ { &ui_arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM },
+ { &ui_botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM },
+ { &ui_spScores1, "g_spScores1", "", CVAR_ARCHIVE },
+ { &ui_spScores2, "g_spScores2", "", CVAR_ARCHIVE },
+ { &ui_spScores3, "g_spScores3", "", CVAR_ARCHIVE },
+ { &ui_spScores4, "g_spScores4", "", CVAR_ARCHIVE },
+ { &ui_spScores5, "g_spScores5", "", CVAR_ARCHIVE },
+ { &ui_spAwards, "g_spAwards", "", CVAR_ARCHIVE },
+ { &ui_spVideos, "g_spVideos", "", CVAR_ARCHIVE },
+ { &ui_spSkill, "g_spSkill", "2", CVAR_ARCHIVE | CVAR_LATCH },
+
+ { &ui_spSelection, "ui_spSelection", "", CVAR_ROM },
+
+ { &ui_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE },
+ { &ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE },
+ { &ui_browserSortKey, "ui_browserSortKey", "4", CVAR_ARCHIVE },
+ { &ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE },
+ { &ui_browserShowEmpty, "ui_browserShowEmpty", "1", CVAR_ARCHIVE },
+
+ { &ui_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE },
+ { &ui_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE },
+ { &ui_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE },
+ { &ui_marks, "cg_marks", "1", CVAR_ARCHIVE },
+
+ { &ui_server1, "server1", "", CVAR_ARCHIVE },
+ { &ui_server2, "server2", "", CVAR_ARCHIVE },
+ { &ui_server3, "server3", "", CVAR_ARCHIVE },
+ { &ui_server4, "server4", "", CVAR_ARCHIVE },
+ { &ui_server5, "server5", "", CVAR_ARCHIVE },
+ { &ui_server6, "server6", "", CVAR_ARCHIVE },
+ { &ui_server7, "server7", "", CVAR_ARCHIVE },
+ { &ui_server8, "server8", "", CVAR_ARCHIVE },
+ { &ui_server9, "server9", "", CVAR_ARCHIVE },
+ { &ui_server10, "server10", "", CVAR_ARCHIVE },
+ { &ui_server11, "server11", "", CVAR_ARCHIVE },
+ { &ui_server12, "server12", "", CVAR_ARCHIVE },
+ { &ui_server13, "server13", "", CVAR_ARCHIVE },
+ { &ui_server14, "server14", "", CVAR_ARCHIVE },
+ { &ui_server15, "server15", "", CVAR_ARCHIVE },
+ { &ui_server16, "server16", "", CVAR_ARCHIVE },
+
+ { &ui_cdkeychecked, "ui_cdkeychecked", "0", CVAR_ROM },
+ { &ui_ioq3, "ui_ioq3", "1", CVAR_ROM }
+};
+
+static int cvarTableSize = sizeof(cvarTable) / sizeof(cvarTable[0]);
+
+
+/*
+=================
+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 );
+ }
+}
diff --git a/code/q3_ui/ui_menu.c b/code/q3_ui/ui_menu.c
new file mode 100644
index 0000000..73cddd0
--- /dev/null
+++ b/code/q3_ui/ui_menu.c
@@ -0,0 +1,419 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+MAIN MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ID_SINGLEPLAYER 10
+#define ID_MULTIPLAYER 11
+#define ID_SETUP 12
+#define ID_DEMOS 13
+#define ID_CINEMATICS 14
+#define ID_TEAMARENA 15
+#define ID_MODS 16
+#define ID_EXIT 17
+
+#define MAIN_BANNER_MODEL "models/mapobjects/banner/banner5.md3"
+#define MAIN_MENU_VERTICAL_SPACING 34
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s singleplayer;
+ menutext_s multiplayer;
+ menutext_s setup;
+ menutext_s demos;
+ menutext_s cinematics;
+ menutext_s teamArena;
+ menutext_s mods;
+ menutext_s exit;
+
+ qhandle_t bannerModel;
+} mainmenu_t;
+
+
+static mainmenu_t s_main;
+
+typedef struct {
+ menuframework_s menu;
+ char errorMessage[4096];
+} errorMessage_t;
+
+static errorMessage_t s_errorMessage;
+
+/*
+=================
+MainMenu_ExitAction
+=================
+*/
+static void MainMenu_ExitAction( qboolean result ) {
+ if( !result ) {
+ return;
+ }
+ UI_PopMenu();
+ UI_CreditMenu();
+}
+
+
+
+/*
+=================
+Main_MenuEvent
+=================
+*/
+void Main_MenuEvent (void* ptr, int event) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_SINGLEPLAYER:
+ UI_SPLevelMenu();
+ break;
+
+ case ID_MULTIPLAYER:
+ UI_ArenaServersMenu();
+ break;
+
+ case ID_SETUP:
+ UI_SetupMenu();
+ break;
+
+ case ID_DEMOS:
+ UI_DemosMenu();
+ break;
+
+ case ID_CINEMATICS:
+ UI_CinematicsMenu();
+ break;
+
+ case ID_MODS:
+ UI_ModsMenu();
+ break;
+
+ case ID_TEAMARENA:
+ trap_Cvar_Set( "fs_game", "missionpack");
+ trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" );
+ break;
+
+ case ID_EXIT:
+ UI_ConfirmMenu( "EXIT GAME?", 0, MainMenu_ExitAction );
+ break;
+ }
+}
+
+
+/*
+===============
+MainMenu_Cache
+===============
+*/
+void MainMenu_Cache( void ) {
+ s_main.bannerModel = trap_R_RegisterModel( MAIN_BANNER_MODEL );
+}
+
+sfxHandle_t ErrorMessage_Key(int key)
+{
+ trap_Cvar_Set( "com_errorMessage", "" );
+ UI_MainMenu();
+ return (menu_null_sound);
+}
+
+/*
+===============
+Main_MenuDraw
+TTimo: this function is common to the main menu and errorMessage menu
+===============
+*/
+
+static void Main_MenuDraw( void ) {
+ refdef_t refdef;
+ refEntity_t ent;
+ vec3_t origin;
+ vec3_t angles;
+ float adjust;
+ float x, y, w, h;
+ vec4_t color = {0.5, 0, 0, 1};
+
+ // setup the refdef
+
+ memset( &refdef, 0, sizeof( refdef ) );
+
+ refdef.rdflags = RDF_NOWORLDMODEL;
+
+ AxisClear( refdef.viewaxis );
+
+ x = 0;
+ y = 0;
+ w = 640;
+ h = 120;
+ UI_AdjustFrom640( &x, &y, &w, &h );
+ refdef.x = x;
+ refdef.y = y;
+ refdef.width = w;
+ refdef.height = h;
+
+ adjust = 0; // JDC: Kenneth asked me to stop this 1.0 * sin( (float)uis.realtime / 1000 );
+ refdef.fov_x = 60 + adjust;
+ refdef.fov_y = 19.6875 + adjust;
+
+ refdef.time = uis.realtime;
+
+ origin[0] = 300;
+ origin[1] = 0;
+ origin[2] = -32;
+
+ trap_R_ClearScene();
+
+ // add the model
+
+ memset( &ent, 0, sizeof(ent) );
+
+ adjust = 5.0 * sin( (float)uis.realtime / 5000 );
+ VectorSet( angles, 0, 180 + adjust, 0 );
+ AnglesToAxis( angles, ent.axis );
+ ent.hModel = s_main.bannerModel;
+ VectorCopy( origin, ent.origin );
+ VectorCopy( origin, ent.lightingOrigin );
+ ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
+ VectorCopy( ent.origin, ent.oldorigin );
+
+ trap_R_AddRefEntityToScene( &ent );
+
+ trap_R_RenderScene( &refdef );
+
+ if (strlen(s_errorMessage.errorMessage))
+ {
+ UI_DrawProportionalString_AutoWrapped( 320, 192, 600, 20, s_errorMessage.errorMessage, UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color );
+ }
+ else
+ {
+ // standard menu drawing
+ Menu_Draw( &s_main.menu );
+ }
+
+ if (uis.demoversion) {
+ UI_DrawProportionalString( 320, 372, "DEMO FOR MATURE AUDIENCES DEMO", UI_CENTER|UI_SMALLFONT, color );
+ UI_DrawString( 320, 400, "Quake III Arena(c) 1999-2000, Id Software, Inc. All Rights Reserved", UI_CENTER|UI_SMALLFONT, color );
+ } else {
+ UI_DrawString( 320, 450, "Quake III Arena(c) 1999-2000, Id Software, Inc. All Rights Reserved", UI_CENTER|UI_SMALLFONT, color );
+ }
+}
+
+
+/*
+===============
+UI_TeamArenaExists
+===============
+*/
+static qboolean UI_TeamArenaExists( void ) {
+ int numdirs;
+ char dirlist[2048];
+ char *dirptr;
+ char *descptr;
+ int i;
+ int dirlen;
+
+ numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) );
+ dirptr = dirlist;
+ for( i = 0; i < numdirs; i++ ) {
+ dirlen = strlen( dirptr ) + 1;
+ descptr = dirptr + dirlen;
+ if (Q_stricmp(dirptr, "missionpack") == 0) {
+ return qtrue;
+ }
+ dirptr += dirlen + strlen(descptr) + 1;
+ }
+ return qfalse;
+}
+
+
+/*
+===============
+UI_MainMenu
+
+The main menu only comes up when not in a game,
+so make sure that the attract loop server is down
+and that local cinematics are killed
+===============
+*/
+void UI_MainMenu( void ) {
+ int y;
+ qboolean teamArena = qfalse;
+ int style = UI_CENTER | UI_DROPSHADOW;
+
+ trap_Cvar_Set( "sv_killserver", "1" );
+
+ if( !uis.demoversion && !ui_cdkeychecked.integer ) {
+ char key[17];
+
+ trap_GetCDKey( key, sizeof(key) );
+ if( trap_VerifyCDKey( key, NULL ) == qfalse ) {
+ UI_CDKeyMenu();
+ return;
+ }
+ }
+
+ memset( &s_main, 0 ,sizeof(mainmenu_t) );
+ memset( &s_errorMessage, 0 ,sizeof(errorMessage_t) );
+
+ // com_errorMessage would need that too
+ MainMenu_Cache();
+
+ trap_Cvar_VariableStringBuffer( "com_errorMessage", s_errorMessage.errorMessage, sizeof(s_errorMessage.errorMessage) );
+ if (strlen(s_errorMessage.errorMessage))
+ {
+ s_errorMessage.menu.draw = Main_MenuDraw;
+ s_errorMessage.menu.key = ErrorMessage_Key;
+ s_errorMessage.menu.fullscreen = qtrue;
+ s_errorMessage.menu.wrapAround = qtrue;
+ s_errorMessage.menu.showlogo = qtrue;
+
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ uis.menusp = 0;
+ UI_PushMenu ( &s_errorMessage.menu );
+
+ return;
+ }
+
+ s_main.menu.draw = Main_MenuDraw;
+ s_main.menu.fullscreen = qtrue;
+ s_main.menu.wrapAround = qtrue;
+ s_main.menu.showlogo = qtrue;
+
+ y = 134;
+ s_main.singleplayer.generic.type = MTYPE_PTEXT;
+ s_main.singleplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.singleplayer.generic.x = 320;
+ s_main.singleplayer.generic.y = y;
+ s_main.singleplayer.generic.id = ID_SINGLEPLAYER;
+ s_main.singleplayer.generic.callback = Main_MenuEvent;
+ s_main.singleplayer.string = "SINGLE PLAYER";
+ s_main.singleplayer.color = color_red;
+ s_main.singleplayer.style = style;
+
+ y += MAIN_MENU_VERTICAL_SPACING;
+ s_main.multiplayer.generic.type = MTYPE_PTEXT;
+ s_main.multiplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.multiplayer.generic.x = 320;
+ s_main.multiplayer.generic.y = y;
+ s_main.multiplayer.generic.id = ID_MULTIPLAYER;
+ s_main.multiplayer.generic.callback = Main_MenuEvent;
+ s_main.multiplayer.string = "MULTIPLAYER";
+ s_main.multiplayer.color = color_red;
+ s_main.multiplayer.style = style;
+
+ y += MAIN_MENU_VERTICAL_SPACING;
+ s_main.setup.generic.type = MTYPE_PTEXT;
+ s_main.setup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.setup.generic.x = 320;
+ s_main.setup.generic.y = y;
+ s_main.setup.generic.id = ID_SETUP;
+ s_main.setup.generic.callback = Main_MenuEvent;
+ s_main.setup.string = "SETUP";
+ s_main.setup.color = color_red;
+ s_main.setup.style = style;
+
+ y += MAIN_MENU_VERTICAL_SPACING;
+ s_main.demos.generic.type = MTYPE_PTEXT;
+ s_main.demos.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.demos.generic.x = 320;
+ s_main.demos.generic.y = y;
+ s_main.demos.generic.id = ID_DEMOS;
+ s_main.demos.generic.callback = Main_MenuEvent;
+ s_main.demos.string = "DEMOS";
+ s_main.demos.color = color_red;
+ s_main.demos.style = style;
+
+ y += MAIN_MENU_VERTICAL_SPACING;
+ s_main.cinematics.generic.type = MTYPE_PTEXT;
+ s_main.cinematics.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.cinematics.generic.x = 320;
+ s_main.cinematics.generic.y = y;
+ s_main.cinematics.generic.id = ID_CINEMATICS;
+ s_main.cinematics.generic.callback = Main_MenuEvent;
+ s_main.cinematics.string = "CINEMATICS";
+ s_main.cinematics.color = color_red;
+ s_main.cinematics.style = style;
+
+ if (UI_TeamArenaExists()) {
+ teamArena = qtrue;
+ y += MAIN_MENU_VERTICAL_SPACING;
+ s_main.teamArena.generic.type = MTYPE_PTEXT;
+ s_main.teamArena.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.teamArena.generic.x = 320;
+ s_main.teamArena.generic.y = y;
+ s_main.teamArena.generic.id = ID_TEAMARENA;
+ s_main.teamArena.generic.callback = Main_MenuEvent;
+ s_main.teamArena.string = "TEAM ARENA";
+ s_main.teamArena.color = color_red;
+ s_main.teamArena.style = style;
+ }
+
+ y += MAIN_MENU_VERTICAL_SPACING;
+ s_main.mods.generic.type = MTYPE_PTEXT;
+ s_main.mods.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.mods.generic.x = 320;
+ s_main.mods.generic.y = y;
+ s_main.mods.generic.id = ID_MODS;
+ s_main.mods.generic.callback = Main_MenuEvent;
+ s_main.mods.string = "MODS";
+ s_main.mods.color = color_red;
+ s_main.mods.style = style;
+
+ y += MAIN_MENU_VERTICAL_SPACING;
+ s_main.exit.generic.type = MTYPE_PTEXT;
+ s_main.exit.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_main.exit.generic.x = 320;
+ s_main.exit.generic.y = y;
+ s_main.exit.generic.id = ID_EXIT;
+ s_main.exit.generic.callback = Main_MenuEvent;
+ s_main.exit.string = "EXIT";
+ s_main.exit.color = color_red;
+ s_main.exit.style = style;
+
+ Menu_AddItem( &s_main.menu, &s_main.singleplayer );
+ Menu_AddItem( &s_main.menu, &s_main.multiplayer );
+ Menu_AddItem( &s_main.menu, &s_main.setup );
+ Menu_AddItem( &s_main.menu, &s_main.demos );
+ Menu_AddItem( &s_main.menu, &s_main.cinematics );
+ if (teamArena) {
+ Menu_AddItem( &s_main.menu, &s_main.teamArena );
+ }
+ Menu_AddItem( &s_main.menu, &s_main.mods );
+ Menu_AddItem( &s_main.menu, &s_main.exit );
+
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ uis.menusp = 0;
+ UI_PushMenu ( &s_main.menu );
+
+}
diff --git a/code/q3_ui/ui_mfield.c b/code/q3_ui/ui_mfield.c
new file mode 100644
index 0000000..d7b13fe
--- /dev/null
+++ b/code/q3_ui/ui_mfield.c
@@ -0,0 +1,439 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+/*
+===================
+MField_Draw
+
+Handles horizontal scrolling and cursor blinking
+x, y, are in pixels
+===================
+*/
+void MField_Draw( mfield_t *edit, int x, int y, int style, vec4_t color ) {
+ int len;
+ int charw;
+ int drawLen;
+ int prestep;
+ int cursorChar;
+ char str[MAX_STRING_CHARS];
+
+ drawLen = edit->widthInChars;
+ len = strlen( edit->buffer ) + 1;
+
+ // guarantee that cursor will be visible
+ if ( len <= drawLen ) {
+ prestep = 0;
+ } else {
+ if ( edit->scroll + drawLen > len ) {
+ edit->scroll = len - drawLen;
+ if ( edit->scroll < 0 ) {
+ edit->scroll = 0;
+ }
+ }
+ prestep = edit->scroll;
+ }
+
+ if ( prestep + drawLen > len ) {
+ drawLen = len - prestep;
+ }
+
+ // extract <drawLen> characters from the field at <prestep>
+ if ( drawLen >= MAX_STRING_CHARS ) {
+ trap_Error( "drawLen >= MAX_STRING_CHARS" );
+ }
+ memcpy( str, edit->buffer + prestep, drawLen );
+ str[ drawLen ] = 0;
+
+ UI_DrawString( x, y, str, style, color );
+
+ // draw the cursor
+ if (!(style & UI_PULSE)) {
+ return;
+ }
+
+ if ( trap_Key_GetOverstrikeMode() ) {
+ cursorChar = 11;
+ } else {
+ cursorChar = 10;
+ }
+
+ style &= ~UI_PULSE;
+ style |= UI_BLINK;
+
+ if (style & UI_SMALLFONT)
+ {
+ charw = SMALLCHAR_WIDTH;
+ }
+ else if (style & UI_GIANTFONT)
+ {
+ charw = GIANTCHAR_WIDTH;
+ }
+ else
+ {
+ charw = BIGCHAR_WIDTH;
+ }
+
+ if (style & UI_CENTER)
+ {
+ len = strlen(str);
+ x = x - len*charw/2;
+ }
+ else if (style & UI_RIGHT)
+ {
+ len = strlen(str);
+ x = x - len*charw;
+ }
+
+ UI_DrawChar( x + ( edit->cursor - prestep ) * charw, y, cursorChar, style & ~(UI_CENTER|UI_RIGHT), color );
+}
+
+/*
+================
+MField_Paste
+================
+*/
+void MField_Paste( mfield_t *edit ) {
+ char pasteBuffer[64];
+ int pasteLen, i;
+
+ trap_GetClipboardData( pasteBuffer, 64 );
+
+ // send as if typed, so insert / overstrike works properly
+ pasteLen = strlen( pasteBuffer );
+ for ( i = 0 ; i < pasteLen ; i++ ) {
+ MField_CharEvent( edit, pasteBuffer[i] );
+ }
+}
+
+/*
+=================
+MField_KeyDownEvent
+
+Performs the basic line editing functions for the console,
+in-game talk, and menu fields
+
+Key events are used for non-printable characters, others are gotten from char events.
+=================
+*/
+void MField_KeyDownEvent( mfield_t *edit, int key ) {
+ int len;
+
+ // shift-insert is paste
+ if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && trap_Key_IsDown( K_SHIFT ) ) {
+ MField_Paste( edit );
+ return;
+ }
+
+ len = strlen( edit->buffer );
+
+ if ( key == K_DEL || key == K_KP_DEL ) {
+ if ( edit->cursor < len ) {
+ memmove( edit->buffer + edit->cursor,
+ edit->buffer + edit->cursor + 1, len - edit->cursor );
+ }
+ return;
+ }
+
+ if ( key == K_RIGHTARROW || key == K_KP_RIGHTARROW )
+ {
+ if ( edit->cursor < len ) {
+ edit->cursor++;
+ }
+ if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
+ {
+ edit->scroll++;
+ }
+ return;
+ }
+
+ if ( key == K_LEFTARROW || key == K_KP_LEFTARROW )
+ {
+ if ( edit->cursor > 0 ) {
+ edit->cursor--;
+ }
+ if ( edit->cursor < edit->scroll )
+ {
+ edit->scroll--;
+ }
+ return;
+ }
+
+ if ( key == K_HOME || key == K_KP_HOME || ( tolower(key) == 'a' && trap_Key_IsDown( K_CTRL ) ) ) {
+ edit->cursor = 0;
+ edit->scroll = 0;
+ return;
+ }
+
+ if ( key == K_END || key == K_KP_END || ( tolower(key) == 'e' && trap_Key_IsDown( K_CTRL ) ) ) {
+ edit->cursor = len;
+ edit->scroll = len - edit->widthInChars + 1;
+ if (edit->scroll < 0)
+ edit->scroll = 0;
+ return;
+ }
+
+ if ( key == K_INS || key == K_KP_INS ) {
+ trap_Key_SetOverstrikeMode( !trap_Key_GetOverstrikeMode() );
+ return;
+ }
+}
+
+/*
+==================
+MField_CharEvent
+==================
+*/
+void MField_CharEvent( mfield_t *edit, int ch ) {
+ int len;
+
+ if ( ch == 'v' - 'a' + 1 ) { // ctrl-v is paste
+ MField_Paste( edit );
+ return;
+ }
+
+ if ( ch == 'c' - 'a' + 1 ) { // ctrl-c clears the field
+ MField_Clear( edit );
+ return;
+ }
+
+ len = strlen( edit->buffer );
+
+ if ( ch == 'h' - 'a' + 1 ) { // ctrl-h is backspace
+ if ( edit->cursor > 0 ) {
+ memmove( edit->buffer + edit->cursor - 1,
+ edit->buffer + edit->cursor, len + 1 - edit->cursor );
+ edit->cursor--;
+ if ( edit->cursor < edit->scroll )
+ {
+ edit->scroll--;
+ }
+ }
+ return;
+ }
+
+ if ( ch == 'a' - 'a' + 1 ) { // ctrl-a is home
+ edit->cursor = 0;
+ edit->scroll = 0;
+ return;
+ }
+
+ if ( ch == 'e' - 'a' + 1 ) { // ctrl-e is end
+ edit->cursor = len;
+ edit->scroll = edit->cursor - edit->widthInChars + 1;
+ if (edit->scroll < 0)
+ edit->scroll = 0;
+ return;
+ }
+
+ //
+ // ignore any other non printable chars
+ //
+ if ( ch < 32 ) {
+ return;
+ }
+
+ if ( !trap_Key_GetOverstrikeMode() ) {
+ if ((edit->cursor == MAX_EDIT_LINE - 1) || (edit->maxchars && edit->cursor >= edit->maxchars))
+ return;
+ } else {
+ // insert mode
+ if (( len == MAX_EDIT_LINE - 1 ) || (edit->maxchars && len >= edit->maxchars))
+ return;
+ memmove( edit->buffer + edit->cursor + 1, edit->buffer + edit->cursor, len + 1 - edit->cursor );
+ }
+
+ edit->buffer[edit->cursor] = ch;
+ if (!edit->maxchars || edit->cursor < edit->maxchars-1)
+ edit->cursor++;
+
+ if ( edit->cursor >= edit->widthInChars )
+ {
+ edit->scroll++;
+ }
+
+ if ( edit->cursor == len + 1) {
+ edit->buffer[edit->cursor] = 0;
+ }
+}
+
+/*
+==================
+MField_Clear
+==================
+*/
+void MField_Clear( mfield_t *edit ) {
+ edit->buffer[0] = 0;
+ edit->cursor = 0;
+ edit->scroll = 0;
+}
+
+/*
+==================
+MenuField_Init
+==================
+*/
+void MenuField_Init( menufield_s* m ) {
+ int l;
+ int w;
+ int h;
+
+ MField_Clear( &m->field );
+
+ if (m->generic.flags & QMF_SMALLFONT)
+ {
+ w = SMALLCHAR_WIDTH;
+ h = SMALLCHAR_HEIGHT;
+ }
+ else
+ {
+ w = BIGCHAR_WIDTH;
+ h = BIGCHAR_HEIGHT;
+ }
+
+ if (m->generic.name) {
+ l = (strlen( m->generic.name )+1) * w;
+ }
+ else {
+ l = 0;
+ }
+
+ m->generic.left = m->generic.x - l;
+ m->generic.top = m->generic.y;
+ m->generic.right = m->generic.x + w + m->field.widthInChars*w;
+ m->generic.bottom = m->generic.y + h;
+}
+
+/*
+==================
+MenuField_Draw
+==================
+*/
+void MenuField_Draw( menufield_s *f )
+{
+ int x;
+ int y;
+ int w;
+ int h;
+ int style;
+ qboolean focus;
+ float *color;
+
+ x = f->generic.x;
+ y = f->generic.y;
+
+ if (f->generic.flags & QMF_SMALLFONT)
+ {
+ w = SMALLCHAR_WIDTH;
+ h = SMALLCHAR_HEIGHT;
+ style = UI_SMALLFONT;
+ }
+ else
+ {
+ w = BIGCHAR_WIDTH;
+ h = BIGCHAR_HEIGHT;
+ style = UI_BIGFONT;
+ }
+
+ if (Menu_ItemAtCursor( f->generic.parent ) == f) {
+ focus = qtrue;
+ style |= UI_PULSE;
+ }
+ else {
+ focus = qfalse;
+ }
+
+ if (f->generic.flags & QMF_GRAYED)
+ color = text_color_disabled;
+ else if (focus)
+ color = text_color_highlight;
+ else
+ color = text_color_normal;
+
+ if ( focus )
+ {
+ // draw cursor
+ UI_FillRect( f->generic.left, f->generic.top, f->generic.right-f->generic.left+1, f->generic.bottom-f->generic.top+1, listbar_color );
+ UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|style, color);
+ }
+
+ if ( f->generic.name ) {
+ UI_DrawString( x - w, y, f->generic.name, style|UI_RIGHT, color );
+ }
+
+ MField_Draw( &f->field, x + w, y, style, color );
+}
+
+/*
+==================
+MenuField_Key
+==================
+*/
+sfxHandle_t MenuField_Key( menufield_s* m, int* key )
+{
+ int keycode;
+
+ keycode = *key;
+
+ switch ( keycode )
+ {
+ case K_KP_ENTER:
+ case K_ENTER:
+ case K_JOY1:
+ case K_JOY2:
+ case K_JOY3:
+ case K_JOY4:
+ // have enter go to next cursor point
+ *key = K_TAB;
+ break;
+
+ case K_TAB:
+ case K_KP_DOWNARROW:
+ case K_DOWNARROW:
+ case K_KP_UPARROW:
+ case K_UPARROW:
+ break;
+
+ default:
+ if ( keycode & K_CHAR_FLAG )
+ {
+ keycode &= ~K_CHAR_FLAG;
+
+ if ((m->generic.flags & QMF_UPPERCASE) && Q_islower( keycode ))
+ keycode -= 'a' - 'A';
+ else if ((m->generic.flags & QMF_LOWERCASE) && Q_isupper( keycode ))
+ keycode -= 'A' - 'a';
+ else if ((m->generic.flags & QMF_NUMBERSONLY) && Q_isalpha( keycode ))
+ return (menu_buzz_sound);
+
+ MField_CharEvent( &m->field, keycode);
+ }
+ else
+ MField_KeyDownEvent( &m->field, keycode );
+ break;
+ }
+
+ return (0);
+}
+
+
diff --git a/code/q3_ui/ui_mods.c b/code/q3_ui/ui_mods.c
new file mode 100644
index 0000000..e144375
--- /dev/null
+++ b/code/q3_ui/ui_mods.c
@@ -0,0 +1,247 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FIGHT0 "menu/art/load_0"
+#define ART_FIGHT1 "menu/art/load_1"
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+
+#define MAX_MODS 64
+#define NAMEBUFSIZE ( MAX_MODS * 48 )
+#define GAMEBUFSIZE ( MAX_MODS * 16 )
+
+#define ID_BACK 10
+#define ID_GO 11
+#define ID_LIST 12
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menulist_s list;
+
+ menubitmap_s back;
+ menubitmap_s go;
+
+ char description[NAMEBUFSIZE];
+ char fs_game[GAMEBUFSIZE];
+
+ char *descriptionPtr;
+ char *fs_gamePtr;
+
+ char *descriptionList[MAX_MODS];
+ char *fs_gameList[MAX_MODS];
+} mods_t;
+
+static mods_t s_mods;
+
+
+/*
+===============
+UI_Mods_MenuEvent
+===============
+*/
+static void UI_Mods_MenuEvent( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch ( ((menucommon_s*)ptr)->id ) {
+ case ID_GO:
+ trap_Cvar_Set( "fs_game", s_mods.fs_gameList[s_mods.list.curvalue] );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" );
+ UI_PopMenu();
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+UI_Mods_ParseInfos
+===============
+*/
+static void UI_Mods_ParseInfos( char *modDir, char *modDesc ) {
+ s_mods.fs_gameList[s_mods.list.numitems] = s_mods.fs_gamePtr;
+ Q_strncpyz( s_mods.fs_gamePtr, modDir, 16 );
+
+ s_mods.descriptionList[s_mods.list.numitems] = s_mods.descriptionPtr;
+ Q_strncpyz( s_mods.descriptionPtr, modDesc, 48 );
+
+ s_mods.list.itemnames[s_mods.list.numitems] = s_mods.descriptionPtr;
+ s_mods.descriptionPtr += strlen( s_mods.descriptionPtr ) + 1;
+ s_mods.fs_gamePtr += strlen( s_mods.fs_gamePtr ) + 1;
+ s_mods.list.numitems++;
+}
+
+
+/*
+===============
+UI_Mods_LoadMods
+===============
+*/
+static void UI_Mods_LoadMods( void ) {
+ int numdirs;
+ char dirlist[2048];
+ char *dirptr;
+ char *descptr;
+ int i;
+ int dirlen;
+
+ s_mods.list.itemnames = (const char **)s_mods.descriptionList;
+ s_mods.descriptionPtr = s_mods.description;
+ s_mods.fs_gamePtr = s_mods.fs_game;
+
+ // always start off with baseq3
+ s_mods.list.numitems = 1;
+ s_mods.list.itemnames[0] = s_mods.descriptionList[0] = "Quake III Arena";
+ s_mods.fs_gameList[0] = "";
+
+ numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) );
+ dirptr = dirlist;
+ for( i = 0; i < numdirs; i++ ) {
+ dirlen = strlen( dirptr ) + 1;
+ descptr = dirptr + dirlen;
+ UI_Mods_ParseInfos( dirptr, descptr);
+ dirptr += dirlen + strlen(descptr) + 1;
+ }
+
+ trap_Print( va( "%i mods parsed\n", s_mods.list.numitems ) );
+ if (s_mods.list.numitems > MAX_MODS) {
+ s_mods.list.numitems = MAX_MODS;
+ }
+}
+
+
+/*
+===============
+UI_Mods_MenuInit
+===============
+*/
+static void UI_Mods_MenuInit( void ) {
+ UI_ModsMenu_Cache();
+
+ memset( &s_mods, 0 ,sizeof(mods_t) );
+ s_mods.menu.wrapAround = qtrue;
+ s_mods.menu.fullscreen = qtrue;
+
+ s_mods.banner.generic.type = MTYPE_BTEXT;
+ s_mods.banner.generic.x = 320;
+ s_mods.banner.generic.y = 16;
+ s_mods.banner.string = "MODS";
+ s_mods.banner.color = color_white;
+ s_mods.banner.style = UI_CENTER;
+
+ s_mods.framel.generic.type = MTYPE_BITMAP;
+ s_mods.framel.generic.name = ART_FRAMEL;
+ s_mods.framel.generic.flags = QMF_INACTIVE;
+ s_mods.framel.generic.x = 0;
+ s_mods.framel.generic.y = 78;
+ s_mods.framel.width = 256;
+ s_mods.framel.height = 329;
+
+ s_mods.framer.generic.type = MTYPE_BITMAP;
+ s_mods.framer.generic.name = ART_FRAMER;
+ s_mods.framer.generic.flags = QMF_INACTIVE;
+ s_mods.framer.generic.x = 376;
+ s_mods.framer.generic.y = 76;
+ s_mods.framer.width = 256;
+ s_mods.framer.height = 334;
+
+ s_mods.back.generic.type = MTYPE_BITMAP;
+ s_mods.back.generic.name = ART_BACK0;
+ s_mods.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_mods.back.generic.id = ID_BACK;
+ s_mods.back.generic.callback = UI_Mods_MenuEvent;
+ s_mods.back.generic.x = 0;
+ s_mods.back.generic.y = 480-64;
+ s_mods.back.width = 128;
+ s_mods.back.height = 64;
+ s_mods.back.focuspic = ART_BACK1;
+
+ s_mods.go.generic.type = MTYPE_BITMAP;
+ s_mods.go.generic.name = ART_FIGHT0;
+ s_mods.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_mods.go.generic.id = ID_GO;
+ s_mods.go.generic.callback = UI_Mods_MenuEvent;
+ s_mods.go.generic.x = 640;
+ s_mods.go.generic.y = 480-64;
+ s_mods.go.width = 128;
+ s_mods.go.height = 64;
+ s_mods.go.focuspic = ART_FIGHT1;
+
+ // scan for mods
+ s_mods.list.generic.type = MTYPE_SCROLLLIST;
+ s_mods.list.generic.flags = QMF_PULSEIFFOCUS|QMF_CENTER_JUSTIFY;
+ s_mods.list.generic.callback = UI_Mods_MenuEvent;
+ s_mods.list.generic.id = ID_LIST;
+ s_mods.list.generic.x = 320;
+ s_mods.list.generic.y = 130;
+ s_mods.list.width = 48;
+ s_mods.list.height = 14;
+
+ UI_Mods_LoadMods();
+
+ Menu_AddItem( &s_mods.menu, &s_mods.banner );
+ Menu_AddItem( &s_mods.menu, &s_mods.framel );
+ Menu_AddItem( &s_mods.menu, &s_mods.framer );
+ Menu_AddItem( &s_mods.menu, &s_mods.list );
+ Menu_AddItem( &s_mods.menu, &s_mods.back );
+ Menu_AddItem( &s_mods.menu, &s_mods.go );
+}
+
+/*
+=================
+UI_Mods_Cache
+=================
+*/
+void UI_ModsMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT0 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT1 );
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+}
+
+
+/*
+===============
+UI_ModsMenu
+===============
+*/
+void UI_ModsMenu( void ) {
+ UI_Mods_MenuInit();
+ UI_PushMenu( &s_mods.menu );
+}
diff --git a/code/q3_ui/ui_network.c b/code/q3_ui/ui_network.c
new file mode 100644
index 0000000..0b4bf83
--- /dev/null
+++ b/code/q3_ui/ui_network.c
@@ -0,0 +1,281 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+NETWORK OPTIONS MENU
+
+=======================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+
+#define ID_GRAPHICS 10
+#define ID_DISPLAY 11
+#define ID_SOUND 12
+#define ID_NETWORK 13
+#define ID_RATE 14
+#define ID_BACK 15
+
+
+static const char *rate_items[] = {
+ "<= 28.8K",
+ "33.6K",
+ "56K",
+ "ISDN",
+ "LAN/Cable/xDSL",
+ NULL
+};
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menutext_s graphics;
+ menutext_s display;
+ menutext_s sound;
+ menutext_s network;
+
+ menulist_s rate;
+
+ menubitmap_s back;
+} networkOptionsInfo_t;
+
+static networkOptionsInfo_t networkOptionsInfo;
+
+
+/*
+=================
+UI_NetworkOptionsMenu_Event
+=================
+*/
+static void UI_NetworkOptionsMenu_Event( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_GRAPHICS:
+ UI_PopMenu();
+ UI_GraphicsOptionsMenu();
+ break;
+
+ case ID_DISPLAY:
+ UI_PopMenu();
+ UI_DisplayOptionsMenu();
+ break;
+
+ case ID_SOUND:
+ UI_PopMenu();
+ UI_SoundOptionsMenu();
+ break;
+
+ case ID_NETWORK:
+ break;
+
+ case ID_RATE:
+ if( networkOptionsInfo.rate.curvalue == 0 ) {
+ trap_Cvar_SetValue( "rate", 2500 );
+ }
+ else if( networkOptionsInfo.rate.curvalue == 1 ) {
+ trap_Cvar_SetValue( "rate", 3000 );
+ }
+ else if( networkOptionsInfo.rate.curvalue == 2 ) {
+ trap_Cvar_SetValue( "rate", 4000 );
+ }
+ else if( networkOptionsInfo.rate.curvalue == 3 ) {
+ trap_Cvar_SetValue( "rate", 5000 );
+ }
+ else if( networkOptionsInfo.rate.curvalue == 4 ) {
+ trap_Cvar_SetValue( "rate", 25000 );
+ }
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+UI_NetworkOptionsMenu_Init
+===============
+*/
+static void UI_NetworkOptionsMenu_Init( void ) {
+ int y;
+ int rate;
+
+ memset( &networkOptionsInfo, 0, sizeof(networkOptionsInfo) );
+
+ UI_NetworkOptionsMenu_Cache();
+ networkOptionsInfo.menu.wrapAround = qtrue;
+ networkOptionsInfo.menu.fullscreen = qtrue;
+
+ networkOptionsInfo.banner.generic.type = MTYPE_BTEXT;
+ networkOptionsInfo.banner.generic.flags = QMF_CENTER_JUSTIFY;
+ networkOptionsInfo.banner.generic.x = 320;
+ networkOptionsInfo.banner.generic.y = 16;
+ networkOptionsInfo.banner.string = "SYSTEM SETUP";
+ networkOptionsInfo.banner.color = color_white;
+ networkOptionsInfo.banner.style = UI_CENTER;
+
+ networkOptionsInfo.framel.generic.type = MTYPE_BITMAP;
+ networkOptionsInfo.framel.generic.name = ART_FRAMEL;
+ networkOptionsInfo.framel.generic.flags = QMF_INACTIVE;
+ networkOptionsInfo.framel.generic.x = 0;
+ networkOptionsInfo.framel.generic.y = 78;
+ networkOptionsInfo.framel.width = 256;
+ networkOptionsInfo.framel.height = 329;
+
+ networkOptionsInfo.framer.generic.type = MTYPE_BITMAP;
+ networkOptionsInfo.framer.generic.name = ART_FRAMER;
+ networkOptionsInfo.framer.generic.flags = QMF_INACTIVE;
+ networkOptionsInfo.framer.generic.x = 376;
+ networkOptionsInfo.framer.generic.y = 76;
+ networkOptionsInfo.framer.width = 256;
+ networkOptionsInfo.framer.height = 334;
+
+ networkOptionsInfo.graphics.generic.type = MTYPE_PTEXT;
+ networkOptionsInfo.graphics.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ networkOptionsInfo.graphics.generic.id = ID_GRAPHICS;
+ networkOptionsInfo.graphics.generic.callback = UI_NetworkOptionsMenu_Event;
+ networkOptionsInfo.graphics.generic.x = 216;
+ networkOptionsInfo.graphics.generic.y = 240 - 2 * PROP_HEIGHT;
+ networkOptionsInfo.graphics.string = "GRAPHICS";
+ networkOptionsInfo.graphics.style = UI_RIGHT;
+ networkOptionsInfo.graphics.color = color_red;
+
+ networkOptionsInfo.display.generic.type = MTYPE_PTEXT;
+ networkOptionsInfo.display.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ networkOptionsInfo.display.generic.id = ID_DISPLAY;
+ networkOptionsInfo.display.generic.callback = UI_NetworkOptionsMenu_Event;
+ networkOptionsInfo.display.generic.x = 216;
+ networkOptionsInfo.display.generic.y = 240 - PROP_HEIGHT;
+ networkOptionsInfo.display.string = "DISPLAY";
+ networkOptionsInfo.display.style = UI_RIGHT;
+ networkOptionsInfo.display.color = color_red;
+
+ networkOptionsInfo.sound.generic.type = MTYPE_PTEXT;
+ networkOptionsInfo.sound.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ networkOptionsInfo.sound.generic.id = ID_SOUND;
+ networkOptionsInfo.sound.generic.callback = UI_NetworkOptionsMenu_Event;
+ networkOptionsInfo.sound.generic.x = 216;
+ networkOptionsInfo.sound.generic.y = 240;
+ networkOptionsInfo.sound.string = "SOUND";
+ networkOptionsInfo.sound.style = UI_RIGHT;
+ networkOptionsInfo.sound.color = color_red;
+
+ networkOptionsInfo.network.generic.type = MTYPE_PTEXT;
+ networkOptionsInfo.network.generic.flags = QMF_RIGHT_JUSTIFY;
+ networkOptionsInfo.network.generic.id = ID_NETWORK;
+ networkOptionsInfo.network.generic.callback = UI_NetworkOptionsMenu_Event;
+ networkOptionsInfo.network.generic.x = 216;
+ networkOptionsInfo.network.generic.y = 240 + PROP_HEIGHT;
+ networkOptionsInfo.network.string = "NETWORK";
+ networkOptionsInfo.network.style = UI_RIGHT;
+ networkOptionsInfo.network.color = color_red;
+
+ y = 240 - 1 * (BIGCHAR_HEIGHT+2);
+ networkOptionsInfo.rate.generic.type = MTYPE_SPINCONTROL;
+ networkOptionsInfo.rate.generic.name = "Data Rate:";
+ networkOptionsInfo.rate.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ networkOptionsInfo.rate.generic.callback = UI_NetworkOptionsMenu_Event;
+ networkOptionsInfo.rate.generic.id = ID_RATE;
+ networkOptionsInfo.rate.generic.x = 400;
+ networkOptionsInfo.rate.generic.y = y;
+ networkOptionsInfo.rate.itemnames = rate_items;
+
+ networkOptionsInfo.back.generic.type = MTYPE_BITMAP;
+ networkOptionsInfo.back.generic.name = ART_BACK0;
+ networkOptionsInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ networkOptionsInfo.back.generic.callback = UI_NetworkOptionsMenu_Event;
+ networkOptionsInfo.back.generic.id = ID_BACK;
+ networkOptionsInfo.back.generic.x = 0;
+ networkOptionsInfo.back.generic.y = 480-64;
+ networkOptionsInfo.back.width = 128;
+ networkOptionsInfo.back.height = 64;
+ networkOptionsInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.banner );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.framel );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.framer );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.graphics );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.display );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.sound );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.network );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.rate );
+ Menu_AddItem( &networkOptionsInfo.menu, ( void * ) &networkOptionsInfo.back );
+
+ rate = trap_Cvar_VariableValue( "rate" );
+ if( rate <= 2500 ) {
+ networkOptionsInfo.rate.curvalue = 0;
+ }
+ else if( rate <= 3000 ) {
+ networkOptionsInfo.rate.curvalue = 1;
+ }
+ else if( rate <= 4000 ) {
+ networkOptionsInfo.rate.curvalue = 2;
+ }
+ else if( rate <= 5000 ) {
+ networkOptionsInfo.rate.curvalue = 3;
+ }
+ else {
+ networkOptionsInfo.rate.curvalue = 4;
+ }
+}
+
+
+/*
+===============
+UI_NetworkOptionsMenu_Cache
+===============
+*/
+void UI_NetworkOptionsMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+}
+
+
+/*
+===============
+UI_NetworkOptionsMenu
+===============
+*/
+void UI_NetworkOptionsMenu( void ) {
+ UI_NetworkOptionsMenu_Init();
+ UI_PushMenu( &networkOptionsInfo.menu );
+ Menu_SetCursorToItem( &networkOptionsInfo.menu, &networkOptionsInfo.network );
+}
diff --git a/code/q3_ui/ui_options.c b/code/q3_ui/ui_options.c
new file mode 100644
index 0000000..288bd4e
--- /dev/null
+++ b/code/q3_ui/ui_options.c
@@ -0,0 +1,229 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+/*
+=======================================================================
+
+SYSTEM CONFIGURATION MENU
+
+=======================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+
+#define ID_GRAPHICS 10
+#define ID_DISPLAY 11
+#define ID_SOUND 12
+#define ID_NETWORK 13
+#define ID_BACK 14
+
+#define VERTICAL_SPACING 34
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menutext_s graphics;
+ menutext_s display;
+ menutext_s sound;
+ menutext_s network;
+ menubitmap_s back;
+} optionsmenu_t;
+
+static optionsmenu_t s_options;
+
+
+/*
+=================
+Options_Event
+=================
+*/
+static void Options_Event( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_GRAPHICS:
+ UI_GraphicsOptionsMenu();
+ break;
+
+ case ID_DISPLAY:
+ UI_DisplayOptionsMenu();
+ break;
+
+ case ID_SOUND:
+ UI_SoundOptionsMenu();
+ break;
+
+ case ID_NETWORK:
+ UI_NetworkOptionsMenu();
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+SystemConfig_Cache
+===============
+*/
+void SystemConfig_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+}
+
+/*
+===============
+Options_MenuInit
+===============
+*/
+void Options_MenuInit( void ) {
+ int y;
+ uiClientState_t cstate;
+
+ memset( &s_options, 0, sizeof(optionsmenu_t) );
+
+ SystemConfig_Cache();
+ s_options.menu.wrapAround = qtrue;
+
+ trap_GetClientState( &cstate );
+ if ( cstate.connState >= CA_CONNECTED ) {
+ s_options.menu.fullscreen = qfalse;
+ }
+ else {
+ s_options.menu.fullscreen = qtrue;
+ }
+
+ s_options.banner.generic.type = MTYPE_BTEXT;
+ s_options.banner.generic.flags = QMF_CENTER_JUSTIFY;
+ s_options.banner.generic.x = 320;
+ s_options.banner.generic.y = 16;
+ s_options.banner.string = "SYSTEM SETUP";
+ s_options.banner.color = color_white;
+ s_options.banner.style = UI_CENTER;
+
+ s_options.framel.generic.type = MTYPE_BITMAP;
+ s_options.framel.generic.name = ART_FRAMEL;
+ s_options.framel.generic.flags = QMF_INACTIVE;
+ s_options.framel.generic.x = 8;
+ s_options.framel.generic.y = 76;
+ s_options.framel.width = 256;
+ s_options.framel.height = 334;
+
+ s_options.framer.generic.type = MTYPE_BITMAP;
+ s_options.framer.generic.name = ART_FRAMER;
+ s_options.framer.generic.flags = QMF_INACTIVE;
+ s_options.framer.generic.x = 376;
+ s_options.framer.generic.y = 76;
+ s_options.framer.width = 256;
+ s_options.framer.height = 334;
+
+ y = 168;
+ s_options.graphics.generic.type = MTYPE_PTEXT;
+ s_options.graphics.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_options.graphics.generic.callback = Options_Event;
+ s_options.graphics.generic.id = ID_GRAPHICS;
+ s_options.graphics.generic.x = 320;
+ s_options.graphics.generic.y = y;
+ s_options.graphics.string = "GRAPHICS";
+ s_options.graphics.color = color_red;
+ s_options.graphics.style = UI_CENTER;
+
+ y += VERTICAL_SPACING;
+ s_options.display.generic.type = MTYPE_PTEXT;
+ s_options.display.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_options.display.generic.callback = Options_Event;
+ s_options.display.generic.id = ID_DISPLAY;
+ s_options.display.generic.x = 320;
+ s_options.display.generic.y = y;
+ s_options.display.string = "DISPLAY";
+ s_options.display.color = color_red;
+ s_options.display.style = UI_CENTER;
+
+ y += VERTICAL_SPACING;
+ s_options.sound.generic.type = MTYPE_PTEXT;
+ s_options.sound.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_options.sound.generic.callback = Options_Event;
+ s_options.sound.generic.id = ID_SOUND;
+ s_options.sound.generic.x = 320;
+ s_options.sound.generic.y = y;
+ s_options.sound.string = "SOUND";
+ s_options.sound.color = color_red;
+ s_options.sound.style = UI_CENTER;
+
+ y += VERTICAL_SPACING;
+ s_options.network.generic.type = MTYPE_PTEXT;
+ s_options.network.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_options.network.generic.callback = Options_Event;
+ s_options.network.generic.id = ID_NETWORK;
+ s_options.network.generic.x = 320;
+ s_options.network.generic.y = y;
+ s_options.network.string = "NETWORK";
+ s_options.network.color = color_red;
+ s_options.network.style = UI_CENTER;
+
+ s_options.back.generic.type = MTYPE_BITMAP;
+ s_options.back.generic.name = ART_BACK0;
+ s_options.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_options.back.generic.callback = Options_Event;
+ s_options.back.generic.id = ID_BACK;
+ s_options.back.generic.x = 0;
+ s_options.back.generic.y = 480-64;
+ s_options.back.width = 128;
+ s_options.back.height = 64;
+ s_options.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.banner );
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.framel );
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.framer );
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.graphics );
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.display );
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.sound );
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.network );
+ Menu_AddItem( &s_options.menu, ( void * ) &s_options.back );
+}
+
+
+/*
+===============
+UI_SystemConfigMenu
+===============
+*/
+void UI_SystemConfigMenu( void ) {
+ Options_MenuInit();
+ UI_PushMenu ( &s_options.menu );
+}
diff --git a/code/q3_ui/ui_playermodel.c b/code/q3_ui/ui_playermodel.c
new file mode 100644
index 0000000..e247149
--- /dev/null
+++ b/code/q3_ui/ui_playermodel.c
@@ -0,0 +1,731 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+#define MODEL_BACK0 "menu/art/back_0"
+#define MODEL_BACK1 "menu/art/back_1"
+#define MODEL_SELECT "menu/art/opponents_select"
+#define MODEL_SELECTED "menu/art/opponents_selected"
+#define MODEL_FRAMEL "menu/art/frame1_l"
+#define MODEL_FRAMER "menu/art/frame1_r"
+#define MODEL_PORTS "menu/art/player_models_ports"
+#define MODEL_ARROWS "menu/art/gs_arrows_0"
+#define MODEL_ARROWSL "menu/art/gs_arrows_l"
+#define MODEL_ARROWSR "menu/art/gs_arrows_r"
+
+#define LOW_MEMORY (5 * 1024 * 1024)
+
+static char* playermodel_artlist[] =
+{
+ MODEL_BACK0,
+ MODEL_BACK1,
+ MODEL_SELECT,
+ MODEL_SELECTED,
+ MODEL_FRAMEL,
+ MODEL_FRAMER,
+ MODEL_PORTS,
+ MODEL_ARROWS,
+ MODEL_ARROWSL,
+ MODEL_ARROWSR,
+ NULL
+};
+
+#define PLAYERGRID_COLS 4
+#define PLAYERGRID_ROWS 4
+#define MAX_MODELSPERPAGE (PLAYERGRID_ROWS*PLAYERGRID_COLS)
+
+#define MAX_PLAYERMODELS 256
+
+#define ID_PLAYERPIC0 0
+#define ID_PLAYERPIC1 1
+#define ID_PLAYERPIC2 2
+#define ID_PLAYERPIC3 3
+#define ID_PLAYERPIC4 4
+#define ID_PLAYERPIC5 5
+#define ID_PLAYERPIC6 6
+#define ID_PLAYERPIC7 7
+#define ID_PLAYERPIC8 8
+#define ID_PLAYERPIC9 9
+#define ID_PLAYERPIC10 10
+#define ID_PLAYERPIC11 11
+#define ID_PLAYERPIC12 12
+#define ID_PLAYERPIC13 13
+#define ID_PLAYERPIC14 14
+#define ID_PLAYERPIC15 15
+#define ID_PREVPAGE 100
+#define ID_NEXTPAGE 101
+#define ID_BACK 102
+
+typedef struct
+{
+ menuframework_s menu;
+ menubitmap_s pics[MAX_MODELSPERPAGE];
+ menubitmap_s picbuttons[MAX_MODELSPERPAGE];
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menubitmap_s ports;
+ menutext_s banner;
+ menubitmap_s back;
+ menubitmap_s player;
+ menubitmap_s arrows;
+ menubitmap_s left;
+ menubitmap_s right;
+ menutext_s modelname;
+ menutext_s skinname;
+ menutext_s playername;
+ playerInfo_t playerinfo;
+ int nummodels;
+ char modelnames[MAX_PLAYERMODELS][128];
+ int modelpage;
+ int numpages;
+ char modelskin[64];
+ int selectedmodel;
+} playermodel_t;
+
+static playermodel_t s_playermodel;
+
+/*
+=================
+PlayerModel_UpdateGrid
+=================
+*/
+static void PlayerModel_UpdateGrid( void )
+{
+ int i;
+ int j;
+
+ j = s_playermodel.modelpage * MAX_MODELSPERPAGE;
+ for (i=0; i<PLAYERGRID_ROWS*PLAYERGRID_COLS; i++,j++)
+ {
+ if (j < s_playermodel.nummodels)
+ {
+ // model/skin portrait
+ s_playermodel.pics[i].generic.name = s_playermodel.modelnames[j];
+ s_playermodel.picbuttons[i].generic.flags &= ~QMF_INACTIVE;
+ }
+ else
+ {
+ // dead slot
+ s_playermodel.pics[i].generic.name = NULL;
+ s_playermodel.picbuttons[i].generic.flags |= QMF_INACTIVE;
+ }
+
+ s_playermodel.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ s_playermodel.pics[i].shader = 0;
+ s_playermodel.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
+ }
+
+ if (s_playermodel.selectedmodel/MAX_MODELSPERPAGE == s_playermodel.modelpage)
+ {
+ // set selected model
+ i = s_playermodel.selectedmodel % MAX_MODELSPERPAGE;
+
+ s_playermodel.pics[i].generic.flags |= QMF_HIGHLIGHT;
+ s_playermodel.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ }
+
+ if (s_playermodel.numpages > 1)
+ {
+ if (s_playermodel.modelpage > 0)
+ s_playermodel.left.generic.flags &= ~QMF_INACTIVE;
+ else
+ s_playermodel.left.generic.flags |= QMF_INACTIVE;
+
+ if (s_playermodel.modelpage < s_playermodel.numpages-1)
+ s_playermodel.right.generic.flags &= ~QMF_INACTIVE;
+ else
+ s_playermodel.right.generic.flags |= QMF_INACTIVE;
+ }
+ else
+ {
+ // hide left/right markers
+ s_playermodel.left.generic.flags |= QMF_INACTIVE;
+ s_playermodel.right.generic.flags |= QMF_INACTIVE;
+ }
+}
+
+/*
+=================
+PlayerModel_UpdateModel
+=================
+*/
+static void PlayerModel_UpdateModel( void )
+{
+ vec3_t viewangles;
+ vec3_t moveangles;
+
+ memset( &s_playermodel.playerinfo, 0, sizeof(playerInfo_t) );
+
+ viewangles[YAW] = 180 - 30;
+ viewangles[PITCH] = 0;
+ viewangles[ROLL] = 0;
+ VectorClear( moveangles );
+
+ UI_PlayerInfo_SetModel( &s_playermodel.playerinfo, s_playermodel.modelskin );
+ UI_PlayerInfo_SetInfo( &s_playermodel.playerinfo, LEGS_IDLE, TORSO_STAND, viewangles, moveangles, WP_MACHINEGUN, qfalse );
+}
+
+/*
+=================
+PlayerModel_SaveChanges
+=================
+*/
+static void PlayerModel_SaveChanges( void )
+{
+ trap_Cvar_Set( "model", s_playermodel.modelskin );
+ trap_Cvar_Set( "headmodel", s_playermodel.modelskin );
+ trap_Cvar_Set( "team_model", s_playermodel.modelskin );
+ trap_Cvar_Set( "team_headmodel", s_playermodel.modelskin );
+}
+
+/*
+=================
+PlayerModel_MenuEvent
+=================
+*/
+static void PlayerModel_MenuEvent( void* ptr, int event )
+{
+ if (event != QM_ACTIVATED)
+ return;
+
+ switch (((menucommon_s*)ptr)->id)
+ {
+ case ID_PREVPAGE:
+ if (s_playermodel.modelpage > 0)
+ {
+ s_playermodel.modelpage--;
+ PlayerModel_UpdateGrid();
+ }
+ break;
+
+ case ID_NEXTPAGE:
+ if (s_playermodel.modelpage < s_playermodel.numpages-1)
+ {
+ s_playermodel.modelpage++;
+ PlayerModel_UpdateGrid();
+ }
+ break;
+
+ case ID_BACK:
+ PlayerModel_SaveChanges();
+ UI_PopMenu();
+ break;
+ }
+}
+
+/*
+=================
+PlayerModel_MenuKey
+=================
+*/
+static sfxHandle_t PlayerModel_MenuKey( int key )
+{
+ menucommon_s* m;
+ int picnum;
+
+ switch (key)
+ {
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ m = Menu_ItemAtCursor(&s_playermodel.menu);
+ picnum = m->id - ID_PLAYERPIC0;
+ if (picnum >= 0 && picnum <= 15)
+ {
+ if (picnum > 0)
+ {
+ Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor-1);
+ return (menu_move_sound);
+
+ }
+ else if (s_playermodel.modelpage > 0)
+ {
+ s_playermodel.modelpage--;
+ Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor+15);
+ PlayerModel_UpdateGrid();
+ return (menu_move_sound);
+ }
+ else
+ return (menu_buzz_sound);
+ }
+ break;
+
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ m = Menu_ItemAtCursor(&s_playermodel.menu);
+ picnum = m->id - ID_PLAYERPIC0;
+ if (picnum >= 0 && picnum <= 15)
+ {
+ if ((picnum < 15) && (s_playermodel.modelpage*MAX_MODELSPERPAGE + picnum+1 < s_playermodel.nummodels))
+ {
+ Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor+1);
+ return (menu_move_sound);
+ }
+ else if ((picnum == 15) && (s_playermodel.modelpage < s_playermodel.numpages-1))
+ {
+ s_playermodel.modelpage++;
+ Menu_SetCursor(&s_playermodel.menu,s_playermodel.menu.cursor-15);
+ PlayerModel_UpdateGrid();
+ return (menu_move_sound);
+ }
+ else
+ return (menu_buzz_sound);
+ }
+ break;
+
+ case K_MOUSE2:
+ case K_ESCAPE:
+ PlayerModel_SaveChanges();
+ break;
+ }
+
+ return ( Menu_DefaultKey( &s_playermodel.menu, key ) );
+}
+
+/*
+=================
+PlayerModel_PicEvent
+=================
+*/
+static void PlayerModel_PicEvent( void* ptr, int event )
+{
+ int modelnum;
+ int maxlen;
+ char* buffptr;
+ char* pdest;
+ int i;
+
+ if (event != QM_ACTIVATED)
+ return;
+
+ for (i=0; i<PLAYERGRID_ROWS*PLAYERGRID_COLS; i++)
+ {
+ // reset
+ s_playermodel.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ s_playermodel.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
+ }
+
+ // set selected
+ i = ((menucommon_s*)ptr)->id - ID_PLAYERPIC0;
+ s_playermodel.pics[i].generic.flags |= QMF_HIGHLIGHT;
+ s_playermodel.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+
+ // get model and strip icon_
+ modelnum = s_playermodel.modelpage*MAX_MODELSPERPAGE + i;
+ buffptr = s_playermodel.modelnames[modelnum] + strlen("models/players/");
+ pdest = strstr(buffptr,"icon_");
+ if (pdest)
+ {
+ // track the whole model/skin name
+ Q_strncpyz(s_playermodel.modelskin,buffptr,pdest-buffptr+1);
+ strcat(s_playermodel.modelskin,pdest + 5);
+
+ // seperate the model name
+ maxlen = pdest-buffptr;
+ if (maxlen > 16)
+ maxlen = 16;
+ Q_strncpyz( s_playermodel.modelname.string, buffptr, maxlen );
+ Q_strupr( s_playermodel.modelname.string );
+
+ // seperate the skin name
+ maxlen = strlen(pdest+5)+1;
+ if (maxlen > 16)
+ maxlen = 16;
+ Q_strncpyz( s_playermodel.skinname.string, pdest+5, maxlen );
+ Q_strupr( s_playermodel.skinname.string );
+
+ s_playermodel.selectedmodel = modelnum;
+
+ if( trap_MemoryRemaining() > LOW_MEMORY ) {
+ PlayerModel_UpdateModel();
+ }
+ }
+}
+
+/*
+=================
+PlayerModel_DrawPlayer
+=================
+*/
+static void PlayerModel_DrawPlayer( void *self )
+{
+ menubitmap_s* b;
+
+ b = (menubitmap_s*) self;
+
+ if( trap_MemoryRemaining() <= LOW_MEMORY ) {
+ UI_DrawProportionalString( b->generic.x, b->generic.y + b->height / 2, "LOW MEMORY", UI_LEFT, color_red );
+ return;
+ }
+
+ UI_DrawPlayer( b->generic.x, b->generic.y, b->width, b->height, &s_playermodel.playerinfo, uis.realtime/2 );
+}
+
+/*
+=================
+PlayerModel_BuildList
+=================
+*/
+static void PlayerModel_BuildList( void )
+{
+ int numdirs;
+ int numfiles;
+ char dirlist[2048];
+ char filelist[2048];
+ char skinname[MAX_QPATH];
+ char* dirptr;
+ char* fileptr;
+ int i;
+ int j;
+ int dirlen;
+ int filelen;
+ qboolean precache;
+
+ precache = trap_Cvar_VariableValue("com_buildscript");
+
+ s_playermodel.modelpage = 0;
+ s_playermodel.nummodels = 0;
+
+ // iterate directory of all player models
+ numdirs = trap_FS_GetFileList("models/players", "/", dirlist, 2048 );
+ dirptr = dirlist;
+ for (i=0; i<numdirs && s_playermodel.nummodels < MAX_PLAYERMODELS; i++,dirptr+=dirlen+1)
+ {
+ dirlen = strlen(dirptr);
+
+ if (dirlen && dirptr[dirlen-1]=='/') dirptr[dirlen-1]='\0';
+
+ if (!strcmp(dirptr,".") || !strcmp(dirptr,".."))
+ continue;
+
+ // iterate all skin files in directory
+ numfiles = trap_FS_GetFileList( va("models/players/%s",dirptr), "tga", filelist, 2048 );
+ fileptr = filelist;
+ for (j=0; j<numfiles && s_playermodel.nummodels < MAX_PLAYERMODELS;j++,fileptr+=filelen+1)
+ {
+ filelen = strlen(fileptr);
+
+ COM_StripExtension(fileptr,skinname, sizeof(skinname));
+
+ // look for icon_????
+ if (!Q_stricmpn(skinname,"icon_",5))
+ {
+ Com_sprintf( s_playermodel.modelnames[s_playermodel.nummodels++],
+ sizeof( s_playermodel.modelnames[s_playermodel.nummodels] ),
+ "models/players/%s/%s", dirptr, skinname );
+ //if (s_playermodel.nummodels >= MAX_PLAYERMODELS)
+ // return;
+ }
+
+ if( precache ) {
+ trap_S_RegisterSound( va( "sound/player/announce/%s_wins.wav", skinname), qfalse );
+ }
+ }
+ }
+
+ //APSFIXME - Degenerate no models case
+
+ s_playermodel.numpages = s_playermodel.nummodels/MAX_MODELSPERPAGE;
+ if (s_playermodel.nummodels % MAX_MODELSPERPAGE)
+ s_playermodel.numpages++;
+}
+
+/*
+=================
+PlayerModel_SetMenuItems
+=================
+*/
+static void PlayerModel_SetMenuItems( void )
+{
+ int i;
+ int maxlen;
+ char modelskin[64];
+ char* buffptr;
+ char* pdest;
+
+ // name
+ trap_Cvar_VariableStringBuffer( "name", s_playermodel.playername.string, 16 );
+ Q_CleanStr( s_playermodel.playername.string );
+
+ // model
+ trap_Cvar_VariableStringBuffer( "model", s_playermodel.modelskin, 64 );
+
+ // find model in our list
+ for (i=0; i<s_playermodel.nummodels; i++)
+ {
+ // strip icon_
+ buffptr = s_playermodel.modelnames[i] + strlen("models/players/");
+ pdest = strstr(buffptr,"icon_");
+ if (pdest)
+ {
+ Q_strncpyz(modelskin,buffptr,pdest-buffptr+1);
+ strcat(modelskin,pdest + 5);
+ }
+ else
+ continue;
+
+ if (!Q_stricmp( s_playermodel.modelskin, modelskin ))
+ {
+ // found pic, set selection here
+ s_playermodel.selectedmodel = i;
+ s_playermodel.modelpage = i/MAX_MODELSPERPAGE;
+
+ // seperate the model name
+ maxlen = pdest-buffptr;
+ if (maxlen > 16)
+ maxlen = 16;
+ Q_strncpyz( s_playermodel.modelname.string, buffptr, maxlen );
+ Q_strupr( s_playermodel.modelname.string );
+
+ // seperate the skin name
+ maxlen = strlen(pdest+5)+1;
+ if (maxlen > 16)
+ maxlen = 16;
+ Q_strncpyz( s_playermodel.skinname.string, pdest+5, maxlen );
+ Q_strupr( s_playermodel.skinname.string );
+ break;
+ }
+ }
+}
+
+/*
+=================
+PlayerModel_MenuInit
+=================
+*/
+static void PlayerModel_MenuInit( void )
+{
+ int i;
+ int j;
+ int k;
+ int x;
+ int y;
+ static char playername[32];
+ static char modelname[32];
+ static char skinname[32];
+
+ // zero set all our globals
+ memset( &s_playermodel, 0 ,sizeof(playermodel_t) );
+
+ PlayerModel_Cache();
+
+ s_playermodel.menu.key = PlayerModel_MenuKey;
+ s_playermodel.menu.wrapAround = qtrue;
+ s_playermodel.menu.fullscreen = qtrue;
+
+ s_playermodel.banner.generic.type = MTYPE_BTEXT;
+ s_playermodel.banner.generic.x = 320;
+ s_playermodel.banner.generic.y = 16;
+ s_playermodel.banner.string = "PLAYER MODEL";
+ s_playermodel.banner.color = color_white;
+ s_playermodel.banner.style = UI_CENTER;
+
+ s_playermodel.framel.generic.type = MTYPE_BITMAP;
+ s_playermodel.framel.generic.name = MODEL_FRAMEL;
+ s_playermodel.framel.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_playermodel.framel.generic.x = 0;
+ s_playermodel.framel.generic.y = 78;
+ s_playermodel.framel.width = 256;
+ s_playermodel.framel.height = 329;
+
+ s_playermodel.framer.generic.type = MTYPE_BITMAP;
+ s_playermodel.framer.generic.name = MODEL_FRAMER;
+ s_playermodel.framer.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_playermodel.framer.generic.x = 376;
+ s_playermodel.framer.generic.y = 76;
+ s_playermodel.framer.width = 256;
+ s_playermodel.framer.height = 334;
+
+ s_playermodel.ports.generic.type = MTYPE_BITMAP;
+ s_playermodel.ports.generic.name = MODEL_PORTS;
+ s_playermodel.ports.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_playermodel.ports.generic.x = 50;
+ s_playermodel.ports.generic.y = 59;
+ s_playermodel.ports.width = 274;
+ s_playermodel.ports.height = 274;
+
+ y = 59;
+ for (i=0,k=0; i<PLAYERGRID_ROWS; i++)
+ {
+ x = 50;
+ for (j=0; j<PLAYERGRID_COLS; j++,k++)
+ {
+ s_playermodel.pics[k].generic.type = MTYPE_BITMAP;
+ s_playermodel.pics[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_playermodel.pics[k].generic.x = x;
+ s_playermodel.pics[k].generic.y = y;
+ s_playermodel.pics[k].width = 64;
+ s_playermodel.pics[k].height = 64;
+ s_playermodel.pics[k].focuspic = MODEL_SELECTED;
+ s_playermodel.pics[k].focuscolor = colorRed;
+
+ s_playermodel.picbuttons[k].generic.type = MTYPE_BITMAP;
+ s_playermodel.picbuttons[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_NODEFAULTINIT|QMF_PULSEIFFOCUS;
+ s_playermodel.picbuttons[k].generic.id = ID_PLAYERPIC0+k;
+ s_playermodel.picbuttons[k].generic.callback = PlayerModel_PicEvent;
+ s_playermodel.picbuttons[k].generic.x = x - 16;
+ s_playermodel.picbuttons[k].generic.y = y - 16;
+ s_playermodel.picbuttons[k].generic.left = x;
+ s_playermodel.picbuttons[k].generic.top = y;
+ s_playermodel.picbuttons[k].generic.right = x + 64;
+ s_playermodel.picbuttons[k].generic.bottom = y + 64;
+ s_playermodel.picbuttons[k].width = 128;
+ s_playermodel.picbuttons[k].height = 128;
+ s_playermodel.picbuttons[k].focuspic = MODEL_SELECT;
+ s_playermodel.picbuttons[k].focuscolor = colorRed;
+
+ x += 64+6;
+ }
+ y += 64+6;
+ }
+
+ s_playermodel.playername.generic.type = MTYPE_PTEXT;
+ s_playermodel.playername.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
+ s_playermodel.playername.generic.x = 320;
+ s_playermodel.playername.generic.y = 440;
+ s_playermodel.playername.string = playername;
+ s_playermodel.playername.style = UI_CENTER;
+ s_playermodel.playername.color = text_color_normal;
+
+ s_playermodel.modelname.generic.type = MTYPE_PTEXT;
+ s_playermodel.modelname.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
+ s_playermodel.modelname.generic.x = 497;
+ s_playermodel.modelname.generic.y = 54;
+ s_playermodel.modelname.string = modelname;
+ s_playermodel.modelname.style = UI_CENTER;
+ s_playermodel.modelname.color = text_color_normal;
+
+ s_playermodel.skinname.generic.type = MTYPE_PTEXT;
+ s_playermodel.skinname.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
+ s_playermodel.skinname.generic.x = 497;
+ s_playermodel.skinname.generic.y = 394;
+ s_playermodel.skinname.string = skinname;
+ s_playermodel.skinname.style = UI_CENTER;
+ s_playermodel.skinname.color = text_color_normal;
+
+ s_playermodel.player.generic.type = MTYPE_BITMAP;
+ s_playermodel.player.generic.flags = QMF_INACTIVE;
+ s_playermodel.player.generic.ownerdraw = PlayerModel_DrawPlayer;
+ s_playermodel.player.generic.x = 400;
+ s_playermodel.player.generic.y = -40;
+ s_playermodel.player.width = 32*10;
+ s_playermodel.player.height = 56*10;
+
+ s_playermodel.arrows.generic.type = MTYPE_BITMAP;
+ s_playermodel.arrows.generic.name = MODEL_ARROWS;
+ s_playermodel.arrows.generic.flags = QMF_INACTIVE;
+ s_playermodel.arrows.generic.x = 125;
+ s_playermodel.arrows.generic.y = 340;
+ s_playermodel.arrows.width = 128;
+ s_playermodel.arrows.height = 32;
+
+ s_playermodel.left.generic.type = MTYPE_BITMAP;
+ s_playermodel.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_playermodel.left.generic.callback = PlayerModel_MenuEvent;
+ s_playermodel.left.generic.id = ID_PREVPAGE;
+ s_playermodel.left.generic.x = 125;
+ s_playermodel.left.generic.y = 340;
+ s_playermodel.left.width = 64;
+ s_playermodel.left.height = 32;
+ s_playermodel.left.focuspic = MODEL_ARROWSL;
+
+ s_playermodel.right.generic.type = MTYPE_BITMAP;
+ s_playermodel.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_playermodel.right.generic.callback = PlayerModel_MenuEvent;
+ s_playermodel.right.generic.id = ID_NEXTPAGE;
+ s_playermodel.right.generic.x = 125+61;
+ s_playermodel.right.generic.y = 340;
+ s_playermodel.right.width = 64;
+ s_playermodel.right.height = 32;
+ s_playermodel.right.focuspic = MODEL_ARROWSR;
+
+ s_playermodel.back.generic.type = MTYPE_BITMAP;
+ s_playermodel.back.generic.name = MODEL_BACK0;
+ s_playermodel.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_playermodel.back.generic.callback = PlayerModel_MenuEvent;
+ s_playermodel.back.generic.id = ID_BACK;
+ s_playermodel.back.generic.x = 0;
+ s_playermodel.back.generic.y = 480-64;
+ s_playermodel.back.width = 128;
+ s_playermodel.back.height = 64;
+ s_playermodel.back.focuspic = MODEL_BACK1;
+
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.banner );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.framel );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.framer );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.ports );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.playername );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.modelname );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.skinname );
+
+ for (i=0; i<MAX_MODELSPERPAGE; i++)
+ {
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.pics[i] );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.picbuttons[i] );
+ }
+
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.player );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.arrows );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.left );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.right );
+ Menu_AddItem( &s_playermodel.menu, &s_playermodel.back );
+
+ // find all available models
+// PlayerModel_BuildList();
+
+ // set initial states
+ PlayerModel_SetMenuItems();
+
+ // update user interface
+ PlayerModel_UpdateGrid();
+ PlayerModel_UpdateModel();
+}
+
+/*
+=================
+PlayerModel_Cache
+=================
+*/
+void PlayerModel_Cache( void )
+{
+ int i;
+
+ for( i = 0; playermodel_artlist[i]; i++ ) {
+ trap_R_RegisterShaderNoMip( playermodel_artlist[i] );
+ }
+
+ PlayerModel_BuildList();
+ for( i = 0; i < s_playermodel.nummodels; i++ ) {
+ trap_R_RegisterShaderNoMip( s_playermodel.modelnames[i] );
+ }
+}
+
+void UI_PlayerModelMenu(void)
+{
+ PlayerModel_MenuInit();
+
+ UI_PushMenu( &s_playermodel.menu );
+
+ Menu_SetCursorToItem( &s_playermodel.menu, &s_playermodel.pics[s_playermodel.selectedmodel % MAX_MODELSPERPAGE] );
+}
+
+
diff --git a/code/q3_ui/ui_players.c b/code/q3_ui/ui_players.c
new file mode 100644
index 0000000..182b5f0
--- /dev/null
+++ b/code/q3_ui/ui_players.c
@@ -0,0 +1,1249 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+// ui_players.c
+
+#include "ui_local.h"
+
+
+#define UI_TIMER_GESTURE 2300
+#define UI_TIMER_JUMP 1000
+#define UI_TIMER_LAND 130
+#define UI_TIMER_WEAPON_SWITCH 300
+#define UI_TIMER_ATTACK 500
+#define UI_TIMER_MUZZLE_FLASH 20
+#define UI_TIMER_WEAPON_DELAY 250
+
+#define JUMP_HEIGHT 56
+
+#define SWINGSPEED 0.3f
+
+#define SPIN_SPEED 0.9f
+#define COAST_TIME 1000
+
+
+static int dp_realtime;
+static float jumpHeight;
+
+
+/*
+===============
+UI_PlayerInfo_SetWeapon
+===============
+*/
+static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum ) {
+ gitem_t * item;
+ char path[MAX_QPATH];
+
+ pi->currentWeapon = weaponNum;
+tryagain:
+ pi->realWeapon = weaponNum;
+ pi->weaponModel = 0;
+ pi->barrelModel = 0;
+ pi->flashModel = 0;
+
+ if ( weaponNum == WP_NONE ) {
+ return;
+ }
+
+ for ( item = bg_itemlist + 1; item->classname ; item++ ) {
+ if ( item->giType != IT_WEAPON ) {
+ continue;
+ }
+ if ( item->giTag == weaponNum ) {
+ break;
+ }
+ }
+
+ if ( item->classname ) {
+ pi->weaponModel = trap_R_RegisterModel( item->world_model[0] );
+ }
+
+ if( pi->weaponModel == 0 ) {
+ if( weaponNum == WP_MACHINEGUN ) {
+ weaponNum = WP_NONE;
+ goto tryagain;
+ }
+ weaponNum = WP_MACHINEGUN;
+ goto tryagain;
+ }
+
+ if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) {
+ strcpy( path, item->world_model[0] );
+ COM_StripExtension( path, path, sizeof(path) );
+ strcat( path, "_barrel.md3" );
+ pi->barrelModel = trap_R_RegisterModel( path );
+ }
+
+ strcpy( path, item->world_model[0] );
+ COM_StripExtension( path, path, sizeof(path) );
+ strcat( path, "_flash.md3" );
+ pi->flashModel = trap_R_RegisterModel( path );
+
+ switch( weaponNum ) {
+ case WP_GAUNTLET:
+ MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
+ break;
+
+ case WP_MACHINEGUN:
+ MAKERGB( pi->flashDlightColor, 1, 1, 0 );
+ break;
+
+ case WP_SHOTGUN:
+ MAKERGB( pi->flashDlightColor, 1, 1, 0 );
+ break;
+
+ case WP_GRENADE_LAUNCHER:
+ MAKERGB( pi->flashDlightColor, 1, 0.7f, 0.5f );
+ break;
+
+ case WP_ROCKET_LAUNCHER:
+ MAKERGB( pi->flashDlightColor, 1, 0.75f, 0 );
+ break;
+
+ case WP_LIGHTNING:
+ MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
+ break;
+
+ case WP_RAILGUN:
+ MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 );
+ break;
+
+ case WP_PLASMAGUN:
+ MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
+ break;
+
+ case WP_BFG:
+ MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 );
+ break;
+
+ case WP_GRAPPLING_HOOK:
+ MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
+ break;
+
+ default:
+ MAKERGB( pi->flashDlightColor, 1, 1, 1 );
+ break;
+ }
+}
+
+
+/*
+===============
+UI_ForceLegsAnim
+===============
+*/
+static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) {
+ pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
+
+ if ( anim == LEGS_JUMP ) {
+ pi->legsAnimationTimer = UI_TIMER_JUMP;
+ }
+}
+
+
+/*
+===============
+UI_SetLegsAnim
+===============
+*/
+static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) {
+ if ( pi->pendingLegsAnim ) {
+ anim = pi->pendingLegsAnim;
+ pi->pendingLegsAnim = 0;
+ }
+ UI_ForceLegsAnim( pi, anim );
+}
+
+
+/*
+===============
+UI_ForceTorsoAnim
+===============
+*/
+static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) {
+ pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
+
+ if ( anim == TORSO_GESTURE ) {
+ pi->torsoAnimationTimer = UI_TIMER_GESTURE;
+ }
+
+ if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) {
+ pi->torsoAnimationTimer = UI_TIMER_ATTACK;
+ }
+}
+
+
+/*
+===============
+UI_SetTorsoAnim
+===============
+*/
+static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) {
+ if ( pi->pendingTorsoAnim ) {
+ anim = pi->pendingTorsoAnim;
+ pi->pendingTorsoAnim = 0;
+ }
+
+ UI_ForceTorsoAnim( pi, anim );
+}
+
+
+/*
+===============
+UI_TorsoSequencing
+===============
+*/
+static void UI_TorsoSequencing( playerInfo_t *pi ) {
+ int currentAnim;
+
+ currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
+
+ if ( pi->weapon != pi->currentWeapon ) {
+ if ( currentAnim != TORSO_DROP ) {
+ pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
+ UI_ForceTorsoAnim( pi, TORSO_DROP );
+ }
+ }
+
+ if ( pi->torsoAnimationTimer > 0 ) {
+ return;
+ }
+
+ if( currentAnim == TORSO_GESTURE ) {
+ UI_SetTorsoAnim( pi, TORSO_STAND );
+ return;
+ }
+
+ if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) {
+ UI_SetTorsoAnim( pi, TORSO_STAND );
+ return;
+ }
+
+ if ( currentAnim == TORSO_DROP ) {
+ UI_PlayerInfo_SetWeapon( pi, pi->weapon );
+ pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH;
+ UI_ForceTorsoAnim( pi, TORSO_RAISE );
+ return;
+ }
+
+ if ( currentAnim == TORSO_RAISE ) {
+ UI_SetTorsoAnim( pi, TORSO_STAND );
+ return;
+ }
+}
+
+
+/*
+===============
+UI_LegsSequencing
+===============
+*/
+static void UI_LegsSequencing( playerInfo_t *pi ) {
+ int currentAnim;
+
+ currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
+
+ if ( pi->legsAnimationTimer > 0 ) {
+ if ( currentAnim == LEGS_JUMP ) {
+ jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP );
+ }
+ return;
+ }
+
+ if ( currentAnim == LEGS_JUMP ) {
+ UI_ForceLegsAnim( pi, LEGS_LAND );
+ pi->legsAnimationTimer = UI_TIMER_LAND;
+ jumpHeight = 0;
+ return;
+ }
+
+ if ( currentAnim == LEGS_LAND ) {
+ UI_SetLegsAnim( pi, LEGS_IDLE );
+ return;
+ }
+}
+
+
+/*
+======================
+UI_PositionEntityOnTag
+======================
+*/
+static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
+ clipHandle_t parentModel, char *tagName ) {
+ int i;
+ orientation_t lerped;
+
+ // lerp the tag
+ trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
+ 1.0 - parent->backlerp, tagName );
+
+ // FIXME: allow origin offsets along tag?
+ VectorCopy( parent->origin, entity->origin );
+ for ( i = 0 ; i < 3 ; i++ ) {
+ VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
+ }
+
+ // cast away const because of compiler problems
+ MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis );
+ entity->backlerp = parent->backlerp;
+}
+
+
+/*
+======================
+UI_PositionRotatedEntityOnTag
+======================
+*/
+static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent,
+ clipHandle_t parentModel, char *tagName ) {
+ int i;
+ orientation_t lerped;
+ vec3_t tempAxis[3];
+
+ // lerp the tag
+ trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame,
+ 1.0 - parent->backlerp, tagName );
+
+ // FIXME: allow origin offsets along tag?
+ VectorCopy( parent->origin, entity->origin );
+ for ( i = 0 ; i < 3 ; i++ ) {
+ VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin );
+ }
+
+ // cast away const because of compiler problems
+ MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis );
+ MatrixMultiply( lerped.axis, tempAxis, entity->axis );
+}
+
+
+/*
+===============
+UI_SetLerpFrameAnimation
+===============
+*/
+static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
+ animation_t *anim;
+
+ lf->animationNumber = newAnimation;
+ newAnimation &= ~ANIM_TOGGLEBIT;
+
+ if ( newAnimation < 0 || newAnimation >= MAX_ANIMATIONS ) {
+ trap_Error( va("Bad animation number: %i", newAnimation) );
+ }
+
+ anim = &ci->animations[ newAnimation ];
+
+ lf->animation = anim;
+ lf->animationTime = lf->frameTime + anim->initialLerp;
+}
+
+
+/*
+===============
+UI_RunLerpFrame
+===============
+*/
+static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) {
+ int f;
+ animation_t *anim;
+
+ // see if the animation sequence is switching
+ if ( newAnimation != lf->animationNumber || !lf->animation ) {
+ UI_SetLerpFrameAnimation( ci, lf, newAnimation );
+ }
+
+ // if we have passed the current frame, move it to
+ // oldFrame and calculate a new frame
+ if ( dp_realtime >= lf->frameTime ) {
+ lf->oldFrame = lf->frame;
+ lf->oldFrameTime = lf->frameTime;
+
+ // get the next frame based on the animation
+ anim = lf->animation;
+ if ( dp_realtime < lf->animationTime ) {
+ lf->frameTime = lf->animationTime; // initial lerp
+ } else {
+ lf->frameTime = lf->oldFrameTime + anim->frameLerp;
+ }
+ f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp;
+ if ( f >= anim->numFrames ) {
+ f -= anim->numFrames;
+ if ( anim->loopFrames ) {
+ f %= anim->loopFrames;
+ f += anim->numFrames - anim->loopFrames;
+ } else {
+ f = anim->numFrames - 1;
+ // the animation is stuck at the end, so it
+ // can immediately transition to another sequence
+ lf->frameTime = dp_realtime;
+ }
+ }
+ lf->frame = anim->firstFrame + f;
+ if ( dp_realtime > lf->frameTime ) {
+ lf->frameTime = dp_realtime;
+ }
+ }
+
+ if ( lf->frameTime > dp_realtime + 200 ) {
+ lf->frameTime = dp_realtime;
+ }
+
+ if ( lf->oldFrameTime > dp_realtime ) {
+ lf->oldFrameTime = dp_realtime;
+ }
+ // calculate current lerp value
+ if ( lf->frameTime == lf->oldFrameTime ) {
+ lf->backlerp = 0;
+ } else {
+ lf->backlerp = 1.0 - (float)( dp_realtime - lf->oldFrameTime ) / ( lf->frameTime - lf->oldFrameTime );
+ }
+}
+
+
+/*
+===============
+UI_PlayerAnimation
+===============
+*/
+static void UI_PlayerAnimation( playerInfo_t *pi, int *legsOld, int *legs, float *legsBackLerp,
+ int *torsoOld, int *torso, float *torsoBackLerp ) {
+
+ // legs animation
+ pi->legsAnimationTimer -= uis.frametime;
+ if ( pi->legsAnimationTimer < 0 ) {
+ pi->legsAnimationTimer = 0;
+ }
+
+ UI_LegsSequencing( pi );
+
+ if ( pi->legs.yawing && ( pi->legsAnim & ~ANIM_TOGGLEBIT ) == LEGS_IDLE ) {
+ UI_RunLerpFrame( pi, &pi->legs, LEGS_TURN );
+ } else {
+ UI_RunLerpFrame( pi, &pi->legs, pi->legsAnim );
+ }
+ *legsOld = pi->legs.oldFrame;
+ *legs = pi->legs.frame;
+ *legsBackLerp = pi->legs.backlerp;
+
+ // torso animation
+ pi->torsoAnimationTimer -= uis.frametime;
+ if ( pi->torsoAnimationTimer < 0 ) {
+ pi->torsoAnimationTimer = 0;
+ }
+
+ UI_TorsoSequencing( pi );
+
+ UI_RunLerpFrame( pi, &pi->torso, pi->torsoAnim );
+ *torsoOld = pi->torso.oldFrame;
+ *torso = pi->torso.frame;
+ *torsoBackLerp = pi->torso.backlerp;
+}
+
+
+/*
+==================
+UI_SwingAngles
+==================
+*/
+static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance,
+ float speed, float *angle, qboolean *swinging ) {
+ float swing;
+ float move;
+ float scale;
+
+ if ( !*swinging ) {
+ // see if a swing should be started
+ swing = AngleSubtract( *angle, destination );
+ if ( swing > swingTolerance || swing < -swingTolerance ) {
+ *swinging = qtrue;
+ }
+ }
+
+ if ( !*swinging ) {
+ return;
+ }
+
+ // modify the speed depending on the delta
+ // so it doesn't seem so linear
+ swing = AngleSubtract( destination, *angle );
+ scale = fabs( swing );
+ if ( scale < swingTolerance * 0.5 ) {
+ scale = 0.5;
+ } else if ( scale < swingTolerance ) {
+ scale = 1.0;
+ } else {
+ scale = 2.0;
+ }
+
+ // swing towards the destination angle
+ if ( swing >= 0 ) {
+ move = uis.frametime * scale * speed;
+ if ( move >= swing ) {
+ move = swing;
+ *swinging = qfalse;
+ }
+ *angle = AngleMod( *angle + move );
+ } else if ( swing < 0 ) {
+ move = uis.frametime * scale * -speed;
+ if ( move <= swing ) {
+ move = swing;
+ *swinging = qfalse;
+ }
+ *angle = AngleMod( *angle + move );
+ }
+
+ // clamp to no more than tolerance
+ swing = AngleSubtract( destination, *angle );
+ if ( swing > clampTolerance ) {
+ *angle = AngleMod( destination - (clampTolerance - 1) );
+ } else if ( swing < -clampTolerance ) {
+ *angle = AngleMod( destination + (clampTolerance - 1) );
+ }
+}
+
+
+/*
+======================
+UI_MovedirAdjustment
+======================
+*/
+static float UI_MovedirAdjustment( playerInfo_t *pi ) {
+ vec3_t relativeAngles;
+ vec3_t moveVector;
+
+ VectorSubtract( pi->viewAngles, pi->moveAngles, relativeAngles );
+ AngleVectors( relativeAngles, moveVector, NULL, NULL );
+ if ( Q_fabs( moveVector[0] ) < 0.01 ) {
+ moveVector[0] = 0.0;
+ }
+ if ( Q_fabs( moveVector[1] ) < 0.01 ) {
+ moveVector[1] = 0.0;
+ }
+
+ if ( moveVector[1] == 0 && moveVector[0] > 0 ) {
+ return 0;
+ }
+ if ( moveVector[1] < 0 && moveVector[0] > 0 ) {
+ return 22;
+ }
+ if ( moveVector[1] < 0 && moveVector[0] == 0 ) {
+ return 45;
+ }
+ if ( moveVector[1] < 0 && moveVector[0] < 0 ) {
+ return -22;
+ }
+ if ( moveVector[1] == 0 && moveVector[0] < 0 ) {
+ return 0;
+ }
+ if ( moveVector[1] > 0 && moveVector[0] < 0 ) {
+ return 22;
+ }
+ if ( moveVector[1] > 0 && moveVector[0] == 0 ) {
+ return -45;
+ }
+
+ return -22;
+}
+
+
+/*
+===============
+UI_PlayerAngles
+===============
+*/
+static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], vec3_t head[3] ) {
+ vec3_t legsAngles, torsoAngles, headAngles;
+ float dest;
+ float adjust;
+
+ VectorCopy( pi->viewAngles, headAngles );
+ headAngles[YAW] = AngleMod( headAngles[YAW] );
+ VectorClear( legsAngles );
+ VectorClear( torsoAngles );
+
+ // --------- yaw -------------
+
+ // allow yaw to drift a bit
+ if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE
+ || ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) {
+ // if not standing still, always point all in the same direction
+ pi->torso.yawing = qtrue; // always center
+ pi->torso.pitching = qtrue; // always center
+ pi->legs.yawing = qtrue; // always center
+ }
+
+ // adjust legs for movement dir
+ adjust = UI_MovedirAdjustment( pi );
+ legsAngles[YAW] = headAngles[YAW] + adjust;
+ torsoAngles[YAW] = headAngles[YAW] + 0.25 * adjust;
+
+
+ // torso
+ UI_SwingAngles( torsoAngles[YAW], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing );
+ UI_SwingAngles( legsAngles[YAW], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing );
+
+ torsoAngles[YAW] = pi->torso.yawAngle;
+ legsAngles[YAW] = pi->legs.yawAngle;
+
+ // --------- pitch -------------
+
+ // only show a fraction of the pitch angle in the torso
+ if ( headAngles[PITCH] > 180 ) {
+ dest = (-360 + headAngles[PITCH]) * 0.75;
+ } else {
+ dest = headAngles[PITCH] * 0.75;
+ }
+ UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching );
+ torsoAngles[PITCH] = pi->torso.pitchAngle;
+
+ // pull the angles back out of the hierarchial chain
+ AnglesSubtract( headAngles, torsoAngles, headAngles );
+ AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
+ AnglesToAxis( legsAngles, legs );
+ AnglesToAxis( torsoAngles, torso );
+ AnglesToAxis( headAngles, head );
+}
+
+
+/*
+===============
+UI_PlayerFloatSprite
+===============
+*/
+static void UI_PlayerFloatSprite( playerInfo_t *pi, vec3_t origin, qhandle_t shader ) {
+ refEntity_t ent;
+
+ memset( &ent, 0, sizeof( ent ) );
+ VectorCopy( origin, ent.origin );
+ ent.origin[2] += 48;
+ ent.reType = RT_SPRITE;
+ ent.customShader = shader;
+ ent.radius = 10;
+ ent.renderfx = 0;
+ trap_R_AddRefEntityToScene( &ent );
+}
+
+
+/*
+======================
+UI_MachinegunSpinAngle
+======================
+*/
+float UI_MachinegunSpinAngle( playerInfo_t *pi ) {
+ int delta;
+ float angle;
+ float speed;
+ int torsoAnim;
+
+ delta = dp_realtime - pi->barrelTime;
+ if ( pi->barrelSpinning ) {
+ angle = pi->barrelAngle + delta * SPIN_SPEED;
+ } else {
+ if ( delta > COAST_TIME ) {
+ delta = COAST_TIME;
+ }
+
+ speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME );
+ angle = pi->barrelAngle + delta * speed;
+ }
+
+ torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
+ if( torsoAnim == TORSO_ATTACK2 ) {
+ torsoAnim = TORSO_ATTACK;
+ }
+ if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) {
+ pi->barrelTime = dp_realtime;
+ pi->barrelAngle = AngleMod( angle );
+ pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK);
+ }
+
+ return angle;
+}
+
+
+/*
+===============
+UI_DrawPlayer
+===============
+*/
+void UI_DrawPlayer( float x, float y, float w, float h, playerInfo_t *pi, int time ) {
+ refdef_t refdef;
+ refEntity_t legs;
+ refEntity_t torso;
+ refEntity_t head;
+ refEntity_t gun;
+ refEntity_t barrel;
+ refEntity_t flash;
+ vec3_t origin;
+ int renderfx;
+ vec3_t mins = {-16, -16, -24};
+ vec3_t maxs = {16, 16, 32};
+ float len;
+ float xx;
+
+ if ( !pi->legsModel || !pi->torsoModel || !pi->headModel || !pi->animations[0].numFrames ) {
+ return;
+ }
+
+ dp_realtime = time;
+
+ if ( pi->pendingWeapon != -1 && dp_realtime > pi->weaponTimer ) {
+ pi->weapon = pi->pendingWeapon;
+ pi->lastWeapon = pi->pendingWeapon;
+ pi->pendingWeapon = -1;
+ pi->weaponTimer = 0;
+ if( pi->currentWeapon != pi->weapon ) {
+ trap_S_StartLocalSound( weaponChangeSound, CHAN_LOCAL );
+ }
+ }
+
+ UI_AdjustFrom640( &x, &y, &w, &h );
+
+ y -= jumpHeight;
+
+ memset( &refdef, 0, sizeof( refdef ) );
+ memset( &legs, 0, sizeof(legs) );
+ memset( &torso, 0, sizeof(torso) );
+ memset( &head, 0, sizeof(head) );
+
+ refdef.rdflags = RDF_NOWORLDMODEL;
+
+ AxisClear( refdef.viewaxis );
+
+ refdef.x = x;
+ refdef.y = y;
+ refdef.width = w;
+ refdef.height = h;
+
+ refdef.fov_x = (int)((float)refdef.width / 640.0f * 90.0f);
+ xx = refdef.width / tan( refdef.fov_x / 360 * M_PI );
+ refdef.fov_y = atan2( refdef.height, xx );
+ refdef.fov_y *= ( 360 / M_PI );
+
+ // calculate distance so the player nearly fills the box
+ len = 0.7 * ( maxs[2] - mins[2] );
+ origin[0] = len / tan( DEG2RAD(refdef.fov_x) * 0.5 );
+ origin[1] = 0.5 * ( mins[1] + maxs[1] );
+ origin[2] = -0.5 * ( mins[2] + maxs[2] );
+
+ refdef.time = dp_realtime;
+
+ trap_R_ClearScene();
+
+ // get the rotation information
+ UI_PlayerAngles( pi, legs.axis, torso.axis, head.axis );
+
+ // get the animation state (after rotation, to allow feet shuffle)
+ UI_PlayerAnimation( pi, &legs.oldframe, &legs.frame, &legs.backlerp,
+ &torso.oldframe, &torso.frame, &torso.backlerp );
+
+ renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW;
+
+ //
+ // add the legs
+ //
+ legs.hModel = pi->legsModel;
+ legs.customSkin = pi->legsSkin;
+
+ VectorCopy( origin, legs.origin );
+
+ VectorCopy( origin, legs.lightingOrigin );
+ legs.renderfx = renderfx;
+ VectorCopy (legs.origin, legs.oldorigin);
+
+ trap_R_AddRefEntityToScene( &legs );
+
+ if (!legs.hModel) {
+ return;
+ }
+
+ //
+ // add the torso
+ //
+ torso.hModel = pi->torsoModel;
+ if (!torso.hModel) {
+ return;
+ }
+
+ torso.customSkin = pi->torsoSkin;
+
+ VectorCopy( origin, torso.lightingOrigin );
+
+ UI_PositionRotatedEntityOnTag( &torso, &legs, pi->legsModel, "tag_torso");
+
+ torso.renderfx = renderfx;
+
+ trap_R_AddRefEntityToScene( &torso );
+
+ //
+ // add the head
+ //
+ head.hModel = pi->headModel;
+ if (!head.hModel) {
+ return;
+ }
+ head.customSkin = pi->headSkin;
+
+ VectorCopy( origin, head.lightingOrigin );
+
+ UI_PositionRotatedEntityOnTag( &head, &torso, pi->torsoModel, "tag_head");
+
+ head.renderfx = renderfx;
+
+ trap_R_AddRefEntityToScene( &head );
+
+ //
+ // add the gun
+ //
+ if ( pi->currentWeapon != WP_NONE ) {
+ memset( &gun, 0, sizeof(gun) );
+ gun.hModel = pi->weaponModel;
+ VectorCopy( origin, gun.lightingOrigin );
+ UI_PositionEntityOnTag( &gun, &torso, pi->torsoModel, "tag_weapon");
+ gun.renderfx = renderfx;
+ trap_R_AddRefEntityToScene( &gun );
+ }
+
+ //
+ // add the spinning barrel
+ //
+ if ( pi->realWeapon == WP_MACHINEGUN || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
+ vec3_t angles;
+
+ memset( &barrel, 0, sizeof(barrel) );
+ VectorCopy( origin, barrel.lightingOrigin );
+ barrel.renderfx = renderfx;
+
+ barrel.hModel = pi->barrelModel;
+ angles[YAW] = 0;
+ angles[PITCH] = 0;
+ angles[ROLL] = UI_MachinegunSpinAngle( pi );
+ if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) {
+ angles[PITCH] = angles[ROLL];
+ angles[ROLL] = 0;
+ }
+ AnglesToAxis( angles, barrel.axis );
+
+ UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel");
+
+ trap_R_AddRefEntityToScene( &barrel );
+ }
+
+ //
+ // add muzzle flash
+ //
+ if ( dp_realtime <= pi->muzzleFlashTime ) {
+ if ( pi->flashModel ) {
+ memset( &flash, 0, sizeof(flash) );
+ flash.hModel = pi->flashModel;
+ VectorCopy( origin, flash.lightingOrigin );
+ UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash");
+ flash.renderfx = renderfx;
+ trap_R_AddRefEntityToScene( &flash );
+ }
+
+ // make a dlight for the flash
+ if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) {
+ trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0],
+ pi->flashDlightColor[1], pi->flashDlightColor[2] );
+ }
+ }
+
+ //
+ // add the chat icon
+ //
+ if ( pi->chat ) {
+ UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) );
+ }
+
+ //
+ // add an accent light
+ //
+ origin[0] -= 100; // + = behind, - = in front
+ origin[1] += 100; // + = left, - = right
+ origin[2] += 100; // + = above, - = below
+ trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 );
+
+ origin[0] -= 100;
+ origin[1] -= 100;
+ origin[2] -= 100;
+ trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 );
+
+ trap_R_RenderScene( &refdef );
+}
+
+
+/*
+==========================
+UI_RegisterClientSkin
+==========================
+*/
+static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName ) {
+ char filename[MAX_QPATH];
+
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
+ pi->legsSkin = trap_R_RegisterSkin( filename );
+
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
+ pi->torsoSkin = trap_R_RegisterSkin( filename );
+
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/head_%s.skin", modelName, skinName );
+ pi->headSkin = trap_R_RegisterSkin( filename );
+
+ if ( !pi->legsSkin || !pi->torsoSkin || !pi->headSkin ) {
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+
+/*
+======================
+UI_ParseAnimationFile
+======================
+*/
+static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) {
+ char *text_p, *prev;
+ int len;
+ int i;
+ char *token;
+ float fps;
+ int skip;
+ char text[20000];
+ fileHandle_t f;
+
+ memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS );
+
+ // load the file
+ len = trap_FS_FOpenFile( filename, &f, FS_READ );
+ if ( len <= 0 ) {
+ return qfalse;
+ }
+ if ( len >= ( sizeof( text ) - 1 ) ) {
+ Com_Printf( "File %s too long\n", filename );
+ trap_FS_FCloseFile( f );
+ return qfalse;
+ }
+ trap_FS_Read( text, len, f );
+ text[len] = 0;
+ trap_FS_FCloseFile( f );
+
+ // parse the text
+ text_p = text;
+ skip = 0; // quite the compiler warning
+
+ // read optional parameters
+ while ( 1 ) {
+ prev = text_p; // so we can unget
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ if ( !Q_stricmp( token, "footsteps" ) ) {
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ continue;
+ } else if ( !Q_stricmp( token, "headoffset" ) ) {
+ for ( i = 0 ; i < 3 ; i++ ) {
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ }
+ continue;
+ } else if ( !Q_stricmp( token, "sex" ) ) {
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ continue;
+ }
+
+ // if it is a number, start parsing animations
+ if ( token[0] >= '0' && token[0] <= '9' ) {
+ text_p = prev; // unget the token
+ break;
+ }
+
+ Com_Printf( "unknown token '%s' is %s\n", token, filename );
+ }
+
+ // read information for each frame
+ for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) {
+
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ animations[i].firstFrame = atoi( token );
+ // leg only frames are adjusted to not count the upper body only frames
+ if ( i == LEGS_WALKCR ) {
+ skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame;
+ }
+ if ( i >= LEGS_WALKCR ) {
+ animations[i].firstFrame -= skip;
+ }
+
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ animations[i].numFrames = atoi( token );
+
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ animations[i].loopFrames = atoi( token );
+
+ token = COM_Parse( &text_p );
+ if ( !token ) {
+ break;
+ }
+ fps = atof( token );
+ if ( fps == 0 ) {
+ fps = 1;
+ }
+ animations[i].frameLerp = 1000 / fps;
+ animations[i].initialLerp = 1000 / fps;
+ }
+
+ if ( i != MAX_ANIMATIONS ) {
+ Com_Printf( "Error parsing animation file: %s", filename );
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+
+/*
+==========================
+UI_RegisterClientModelname
+==========================
+*/
+qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName ) {
+ char modelName[MAX_QPATH];
+ char skinName[MAX_QPATH];
+ char filename[MAX_QPATH];
+ char *slash;
+
+ pi->torsoModel = 0;
+ pi->headModel = 0;
+
+ if ( !modelSkinName[0] ) {
+ return qfalse;
+ }
+
+ Q_strncpyz( modelName, modelSkinName, sizeof( modelName ) );
+
+ slash = strchr( modelName, '/' );
+ if ( !slash ) {
+ // modelName did not include a skin name
+ Q_strncpyz( skinName, "default", sizeof( skinName ) );
+ } else {
+ Q_strncpyz( skinName, slash + 1, sizeof( skinName ) );
+ // truncate modelName
+ *slash = 0;
+ }
+
+ // load cmodels before models so filecache works
+
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower.md3", modelName );
+ pi->legsModel = trap_R_RegisterModel( filename );
+ if ( !pi->legsModel ) {
+ Com_Printf( "Failed to load model file %s\n", filename );
+ return qfalse;
+ }
+
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper.md3", modelName );
+ pi->torsoModel = trap_R_RegisterModel( filename );
+ if ( !pi->torsoModel ) {
+ Com_Printf( "Failed to load model file %s\n", filename );
+ return qfalse;
+ }
+
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", modelName );
+ pi->headModel = trap_R_RegisterModel( filename );
+ if ( !pi->headModel ) {
+ Com_Printf( "Failed to load model file %s\n", filename );
+ return qfalse;
+ }
+
+ // if any skins failed to load, fall back to default
+ if ( !UI_RegisterClientSkin( pi, modelName, skinName ) ) {
+ if ( !UI_RegisterClientSkin( pi, modelName, "default" ) ) {
+ Com_Printf( "Failed to load skin file: %s : %s\n", modelName, skinName );
+ return qfalse;
+ }
+ }
+
+ // load the animations
+ Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", modelName );
+ if ( !UI_ParseAnimationFile( filename, pi->animations ) ) {
+ Com_Printf( "Failed to load animation file %s\n", filename );
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+
+/*
+===============
+UI_PlayerInfo_SetModel
+===============
+*/
+void UI_PlayerInfo_SetModel( playerInfo_t *pi, const char *model ) {
+ memset( pi, 0, sizeof(*pi) );
+ UI_RegisterClientModelname( pi, model );
+ pi->weapon = WP_MACHINEGUN;
+ pi->currentWeapon = pi->weapon;
+ pi->lastWeapon = pi->weapon;
+ pi->pendingWeapon = -1;
+ pi->weaponTimer = 0;
+ pi->chat = qfalse;
+ pi->newModel = qtrue;
+ UI_PlayerInfo_SetWeapon( pi, pi->weapon );
+}
+
+
+/*
+===============
+UI_PlayerInfo_SetInfo
+===============
+*/
+void UI_PlayerInfo_SetInfo( playerInfo_t *pi, int legsAnim, int torsoAnim, vec3_t viewAngles, vec3_t moveAngles, weapon_t weaponNumber, qboolean chat ) {
+ int currentAnim;
+ weapon_t weaponNum;
+
+ pi->chat = chat;
+
+ // view angles
+ VectorCopy( viewAngles, pi->viewAngles );
+
+ // move angles
+ VectorCopy( moveAngles, pi->moveAngles );
+
+ if ( pi->newModel ) {
+ pi->newModel = qfalse;
+
+ jumpHeight = 0;
+ pi->pendingLegsAnim = 0;
+ UI_ForceLegsAnim( pi, legsAnim );
+ pi->legs.yawAngle = viewAngles[YAW];
+ pi->legs.yawing = qfalse;
+
+ pi->pendingTorsoAnim = 0;
+ UI_ForceTorsoAnim( pi, torsoAnim );
+ pi->torso.yawAngle = viewAngles[YAW];
+ pi->torso.yawing = qfalse;
+
+ if ( weaponNumber != -1 ) {
+ pi->weapon = weaponNumber;
+ pi->currentWeapon = weaponNumber;
+ pi->lastWeapon = weaponNumber;
+ pi->pendingWeapon = -1;
+ pi->weaponTimer = 0;
+ UI_PlayerInfo_SetWeapon( pi, pi->weapon );
+ }
+
+ return;
+ }
+
+ // weapon
+ if ( weaponNumber == -1 ) {
+ pi->pendingWeapon = -1;
+ pi->weaponTimer = 0;
+ }
+ else if ( weaponNumber != WP_NONE ) {
+ pi->pendingWeapon = weaponNumber;
+ pi->weaponTimer = dp_realtime + UI_TIMER_WEAPON_DELAY;
+ }
+ weaponNum = pi->lastWeapon;
+ pi->weapon = weaponNum;
+
+ if ( torsoAnim == BOTH_DEATH1 || legsAnim == BOTH_DEATH1 ) {
+ torsoAnim = legsAnim = BOTH_DEATH1;
+ pi->weapon = pi->currentWeapon = WP_NONE;
+ UI_PlayerInfo_SetWeapon( pi, pi->weapon );
+
+ jumpHeight = 0;
+ pi->pendingLegsAnim = 0;
+ UI_ForceLegsAnim( pi, legsAnim );
+
+ pi->pendingTorsoAnim = 0;
+ UI_ForceTorsoAnim( pi, torsoAnim );
+
+ return;
+ }
+
+ // leg animation
+ currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT;
+ if ( legsAnim != LEGS_JUMP && ( currentAnim == LEGS_JUMP || currentAnim == LEGS_LAND ) ) {
+ pi->pendingLegsAnim = legsAnim;
+ }
+ else if ( legsAnim != currentAnim ) {
+ jumpHeight = 0;
+ pi->pendingLegsAnim = 0;
+ UI_ForceLegsAnim( pi, legsAnim );
+ }
+
+ // torso animation
+ if ( torsoAnim == TORSO_STAND || torsoAnim == TORSO_STAND2 ) {
+ if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
+ torsoAnim = TORSO_STAND2;
+ }
+ else {
+ torsoAnim = TORSO_STAND;
+ }
+ }
+
+ if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) {
+ if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) {
+ torsoAnim = TORSO_ATTACK2;
+ }
+ else {
+ torsoAnim = TORSO_ATTACK;
+ }
+ pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH;
+ //FIXME play firing sound here
+ }
+
+ currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT;
+
+ if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) {
+ pi->pendingTorsoAnim = torsoAnim;
+ }
+ else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) {
+ pi->pendingTorsoAnim = torsoAnim;
+ }
+ else if ( torsoAnim != currentAnim ) {
+ pi->pendingTorsoAnim = 0;
+ UI_ForceTorsoAnim( pi, torsoAnim );
+ }
+}
diff --git a/code/q3_ui/ui_playersettings.c b/code/q3_ui/ui_playersettings.c
new file mode 100644
index 0000000..df4b2d1
--- /dev/null
+++ b/code/q3_ui/ui_playersettings.c
@@ -0,0 +1,513 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_MODEL0 "menu/art/model_0"
+#define ART_MODEL1 "menu/art/model_1"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FX_BASE "menu/art/fx_base"
+#define ART_FX_BLUE "menu/art/fx_blue"
+#define ART_FX_CYAN "menu/art/fx_cyan"
+#define ART_FX_GREEN "menu/art/fx_grn"
+#define ART_FX_RED "menu/art/fx_red"
+#define ART_FX_TEAL "menu/art/fx_teal"
+#define ART_FX_WHITE "menu/art/fx_white"
+#define ART_FX_YELLOW "menu/art/fx_yel"
+
+#define ID_NAME 10
+#define ID_HANDICAP 11
+#define ID_EFFECTS 12
+#define ID_BACK 13
+#define ID_MODEL 14
+
+#define MAX_NAMELENGTH 20
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menubitmap_s player;
+
+ menufield_s name;
+ menulist_s handicap;
+ menulist_s effects;
+
+ menubitmap_s back;
+ menubitmap_s model;
+ menubitmap_s item_null;
+
+ qhandle_t fxBasePic;
+ qhandle_t fxPic[7];
+ playerInfo_t playerinfo;
+ int current_fx;
+ char playerModel[MAX_QPATH];
+} playersettings_t;
+
+static playersettings_t s_playersettings;
+
+static int gamecodetoui[] = {4,2,3,0,5,1,6};
+static int uitogamecode[] = {4,6,2,3,1,5,7};
+
+static const char *handicap_items[] = {
+ "None",
+ "95",
+ "90",
+ "85",
+ "80",
+ "75",
+ "70",
+ "65",
+ "60",
+ "55",
+ "50",
+ "45",
+ "40",
+ "35",
+ "30",
+ "25",
+ "20",
+ "15",
+ "10",
+ "5",
+ NULL
+};
+
+
+/*
+=================
+PlayerSettings_DrawName
+=================
+*/
+static void PlayerSettings_DrawName( void *self ) {
+ menufield_s *f;
+ qboolean focus;
+ int style;
+ char *txt;
+ char c;
+ float *color;
+ int n;
+ int basex, x, y;
+ char name[32];
+
+ f = (menufield_s*)self;
+ basex = f->generic.x;
+ y = f->generic.y;
+ focus = (f->generic.parent->cursor == f->generic.menuPosition);
+
+ style = UI_LEFT|UI_SMALLFONT;
+ color = text_color_normal;
+ if( focus ) {
+ style |= UI_PULSE;
+ color = text_color_highlight;
+ }
+
+ UI_DrawProportionalString( basex, y, "Name", style, color );
+
+ // draw the actual name
+ basex += 64;
+ y += PROP_HEIGHT;
+ txt = f->field.buffer;
+ color = g_color_table[ColorIndex(COLOR_WHITE)];
+ x = basex;
+ while ( (c = *txt) != 0 ) {
+ if ( !focus && Q_IsColorString( txt ) ) {
+ n = ColorIndex( *(txt+1) );
+ if( n == 0 ) {
+ n = 7;
+ }
+ color = g_color_table[n];
+ txt += 2;
+ continue;
+ }
+ UI_DrawChar( x, y, c, style, color );
+ txt++;
+ x += SMALLCHAR_WIDTH;
+ }
+
+ // draw cursor if we have focus
+ if( focus ) {
+ if ( trap_Key_GetOverstrikeMode() ) {
+ c = 11;
+ } else {
+ c = 10;
+ }
+
+ style &= ~UI_PULSE;
+ style |= UI_BLINK;
+
+ UI_DrawChar( basex + f->field.cursor * SMALLCHAR_WIDTH, y, c, style, color_white );
+ }
+
+ // draw at bottom also using proportional font
+ Q_strncpyz( name, f->field.buffer, sizeof(name) );
+ Q_CleanStr( name );
+ UI_DrawProportionalString( 320, 440, name, UI_CENTER|UI_BIGFONT, text_color_normal );
+}
+
+
+/*
+=================
+PlayerSettings_DrawHandicap
+=================
+*/
+static void PlayerSettings_DrawHandicap( void *self ) {
+ menulist_s *item;
+ qboolean focus;
+ int style;
+ float *color;
+
+ item = (menulist_s *)self;
+ focus = (item->generic.parent->cursor == item->generic.menuPosition);
+
+ style = UI_LEFT|UI_SMALLFONT;
+ color = text_color_normal;
+ if( focus ) {
+ style |= UI_PULSE;
+ color = text_color_highlight;
+ }
+
+ UI_DrawProportionalString( item->generic.x, item->generic.y, "Handicap", style, color );
+ UI_DrawProportionalString( item->generic.x + 64, item->generic.y + PROP_HEIGHT, handicap_items[item->curvalue], style, color );
+}
+
+
+/*
+=================
+PlayerSettings_DrawEffects
+=================
+*/
+static void PlayerSettings_DrawEffects( void *self ) {
+ menulist_s *item;
+ qboolean focus;
+ int style;
+ float *color;
+
+ item = (menulist_s *)self;
+ focus = (item->generic.parent->cursor == item->generic.menuPosition);
+
+ style = UI_LEFT|UI_SMALLFONT;
+ color = text_color_normal;
+ if( focus ) {
+ style |= UI_PULSE;
+ color = text_color_highlight;
+ }
+
+ UI_DrawProportionalString( item->generic.x, item->generic.y, "Effects", style, color );
+
+ UI_DrawHandlePic( item->generic.x + 64, item->generic.y + PROP_HEIGHT + 8, 128, 8, s_playersettings.fxBasePic );
+ UI_DrawHandlePic( item->generic.x + 64 + item->curvalue * 16 + 8, item->generic.y + PROP_HEIGHT + 6, 16, 12, s_playersettings.fxPic[item->curvalue] );
+}
+
+
+/*
+=================
+PlayerSettings_DrawPlayer
+=================
+*/
+static void PlayerSettings_DrawPlayer( void *self ) {
+ menubitmap_s *b;
+ vec3_t viewangles;
+ char buf[MAX_QPATH];
+
+ trap_Cvar_VariableStringBuffer( "model", buf, sizeof( buf ) );
+ if ( strcmp( buf, s_playersettings.playerModel ) != 0 ) {
+ UI_PlayerInfo_SetModel( &s_playersettings.playerinfo, buf );
+ strcpy( s_playersettings.playerModel, buf );
+
+ viewangles[YAW] = 180 - 30;
+ viewangles[PITCH] = 0;
+ viewangles[ROLL] = 0;
+ UI_PlayerInfo_SetInfo( &s_playersettings.playerinfo, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse );
+ }
+
+ b = (menubitmap_s*) self;
+ UI_DrawPlayer( b->generic.x, b->generic.y, b->width, b->height, &s_playersettings.playerinfo, uis.realtime/2 );
+}
+
+
+/*
+=================
+PlayerSettings_SaveChanges
+=================
+*/
+static void PlayerSettings_SaveChanges( void ) {
+ // name
+ trap_Cvar_Set( "name", s_playersettings.name.field.buffer );
+
+ // handicap
+ trap_Cvar_SetValue( "handicap", 100 - s_playersettings.handicap.curvalue * 5 );
+
+ // effects color
+ trap_Cvar_SetValue( "color1", uitogamecode[s_playersettings.effects.curvalue] );
+}
+
+
+/*
+=================
+PlayerSettings_MenuKey
+=================
+*/
+static sfxHandle_t PlayerSettings_MenuKey( int key ) {
+ if( key == K_MOUSE2 || key == K_ESCAPE ) {
+ PlayerSettings_SaveChanges();
+ }
+ return Menu_DefaultKey( &s_playersettings.menu, key );
+}
+
+
+/*
+=================
+PlayerSettings_SetMenuItems
+=================
+*/
+static void PlayerSettings_SetMenuItems( void ) {
+ vec3_t viewangles;
+ int c;
+ int h;
+
+ // name
+ Q_strncpyz( s_playersettings.name.field.buffer, UI_Cvar_VariableString("name"), sizeof(s_playersettings.name.field.buffer) );
+
+ // effects color
+ c = trap_Cvar_VariableValue( "color1" ) - 1;
+ if( c < 0 || c > 6 ) {
+ c = 6;
+ }
+ s_playersettings.effects.curvalue = gamecodetoui[c];
+
+ // model/skin
+ memset( &s_playersettings.playerinfo, 0, sizeof(playerInfo_t) );
+
+ viewangles[YAW] = 180 - 30;
+ viewangles[PITCH] = 0;
+ viewangles[ROLL] = 0;
+
+ UI_PlayerInfo_SetModel( &s_playersettings.playerinfo, UI_Cvar_VariableString( "model" ) );
+ UI_PlayerInfo_SetInfo( &s_playersettings.playerinfo, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse );
+
+ // handicap
+ h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") );
+ s_playersettings.handicap.curvalue = 20 - h / 5;
+}
+
+
+/*
+=================
+PlayerSettings_MenuEvent
+=================
+*/
+static void PlayerSettings_MenuEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_HANDICAP:
+ trap_Cvar_Set( "handicap", va( "%i", 100 - 25 * s_playersettings.handicap.curvalue ) );
+ break;
+
+ case ID_MODEL:
+ PlayerSettings_SaveChanges();
+ UI_PlayerModelMenu();
+ break;
+
+ case ID_BACK:
+ PlayerSettings_SaveChanges();
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+=================
+PlayerSettings_MenuInit
+=================
+*/
+static void PlayerSettings_MenuInit( void ) {
+ int y;
+
+ memset(&s_playersettings,0,sizeof(playersettings_t));
+
+ PlayerSettings_Cache();
+
+ s_playersettings.menu.key = PlayerSettings_MenuKey;
+ s_playersettings.menu.wrapAround = qtrue;
+ s_playersettings.menu.fullscreen = qtrue;
+
+ s_playersettings.banner.generic.type = MTYPE_BTEXT;
+ s_playersettings.banner.generic.x = 320;
+ s_playersettings.banner.generic.y = 16;
+ s_playersettings.banner.string = "PLAYER SETTINGS";
+ s_playersettings.banner.color = color_white;
+ s_playersettings.banner.style = UI_CENTER;
+
+ s_playersettings.framel.generic.type = MTYPE_BITMAP;
+ s_playersettings.framel.generic.name = ART_FRAMEL;
+ s_playersettings.framel.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_playersettings.framel.generic.x = 0;
+ s_playersettings.framel.generic.y = 78;
+ s_playersettings.framel.width = 256;
+ s_playersettings.framel.height = 329;
+
+ s_playersettings.framer.generic.type = MTYPE_BITMAP;
+ s_playersettings.framer.generic.name = ART_FRAMER;
+ s_playersettings.framer.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_playersettings.framer.generic.x = 376;
+ s_playersettings.framer.generic.y = 76;
+ s_playersettings.framer.width = 256;
+ s_playersettings.framer.height = 334;
+
+ y = 144;
+ s_playersettings.name.generic.type = MTYPE_FIELD;
+ s_playersettings.name.generic.flags = QMF_NODEFAULTINIT;
+ s_playersettings.name.generic.ownerdraw = PlayerSettings_DrawName;
+ s_playersettings.name.field.widthInChars = MAX_NAMELENGTH;
+ s_playersettings.name.field.maxchars = MAX_NAMELENGTH;
+ s_playersettings.name.generic.x = 192;
+ s_playersettings.name.generic.y = y;
+ s_playersettings.name.generic.left = 192 - 8;
+ s_playersettings.name.generic.top = y - 8;
+ s_playersettings.name.generic.right = 192 + 200;
+ s_playersettings.name.generic.bottom = y + 2 * PROP_HEIGHT;
+
+ y += 3 * PROP_HEIGHT;
+ s_playersettings.handicap.generic.type = MTYPE_SPINCONTROL;
+ s_playersettings.handicap.generic.flags = QMF_NODEFAULTINIT;
+ s_playersettings.handicap.generic.id = ID_HANDICAP;
+ s_playersettings.handicap.generic.ownerdraw = PlayerSettings_DrawHandicap;
+ s_playersettings.handicap.generic.x = 192;
+ s_playersettings.handicap.generic.y = y;
+ s_playersettings.handicap.generic.left = 192 - 8;
+ s_playersettings.handicap.generic.top = y - 8;
+ s_playersettings.handicap.generic.right = 192 + 200;
+ s_playersettings.handicap.generic.bottom = y + 2 * PROP_HEIGHT;
+ s_playersettings.handicap.numitems = 20;
+
+ y += 3 * PROP_HEIGHT;
+ s_playersettings.effects.generic.type = MTYPE_SPINCONTROL;
+ s_playersettings.effects.generic.flags = QMF_NODEFAULTINIT;
+ s_playersettings.effects.generic.id = ID_EFFECTS;
+ s_playersettings.effects.generic.ownerdraw = PlayerSettings_DrawEffects;
+ s_playersettings.effects.generic.x = 192;
+ s_playersettings.effects.generic.y = y;
+ s_playersettings.effects.generic.left = 192 - 8;
+ s_playersettings.effects.generic.top = y - 8;
+ s_playersettings.effects.generic.right = 192 + 200;
+ s_playersettings.effects.generic.bottom = y + 2* PROP_HEIGHT;
+ s_playersettings.effects.numitems = 7;
+
+ s_playersettings.model.generic.type = MTYPE_BITMAP;
+ s_playersettings.model.generic.name = ART_MODEL0;
+ s_playersettings.model.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_playersettings.model.generic.id = ID_MODEL;
+ s_playersettings.model.generic.callback = PlayerSettings_MenuEvent;
+ s_playersettings.model.generic.x = 640;
+ s_playersettings.model.generic.y = 480-64;
+ s_playersettings.model.width = 128;
+ s_playersettings.model.height = 64;
+ s_playersettings.model.focuspic = ART_MODEL1;
+
+ s_playersettings.player.generic.type = MTYPE_BITMAP;
+ s_playersettings.player.generic.flags = QMF_INACTIVE;
+ s_playersettings.player.generic.ownerdraw = PlayerSettings_DrawPlayer;
+ s_playersettings.player.generic.x = 400;
+ s_playersettings.player.generic.y = -40;
+ s_playersettings.player.width = 32*10;
+ s_playersettings.player.height = 56*10;
+
+ s_playersettings.back.generic.type = MTYPE_BITMAP;
+ s_playersettings.back.generic.name = ART_BACK0;
+ s_playersettings.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_playersettings.back.generic.id = ID_BACK;
+ s_playersettings.back.generic.callback = PlayerSettings_MenuEvent;
+ s_playersettings.back.generic.x = 0;
+ s_playersettings.back.generic.y = 480-64;
+ s_playersettings.back.width = 128;
+ s_playersettings.back.height = 64;
+ s_playersettings.back.focuspic = ART_BACK1;
+
+ s_playersettings.item_null.generic.type = MTYPE_BITMAP;
+ s_playersettings.item_null.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT;
+ s_playersettings.item_null.generic.x = 0;
+ s_playersettings.item_null.generic.y = 0;
+ s_playersettings.item_null.width = 640;
+ s_playersettings.item_null.height = 480;
+
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.banner );
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.framel );
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.framer );
+
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.name );
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.handicap );
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.effects );
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.model );
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.back );
+
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.player );
+
+ Menu_AddItem( &s_playersettings.menu, &s_playersettings.item_null );
+
+ PlayerSettings_SetMenuItems();
+}
+
+
+/*
+=================
+PlayerSettings_Cache
+=================
+*/
+void PlayerSettings_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_MODEL0 );
+ trap_R_RegisterShaderNoMip( ART_MODEL1 );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+
+ s_playersettings.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE );
+ s_playersettings.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED );
+ s_playersettings.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW );
+ s_playersettings.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN );
+ s_playersettings.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL );
+ s_playersettings.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE );
+ s_playersettings.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN );
+ s_playersettings.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE );
+}
+
+
+/*
+=================
+UI_PlayerSettingsMenu
+=================
+*/
+void UI_PlayerSettingsMenu( void ) {
+ PlayerSettings_MenuInit();
+ UI_PushMenu( &s_playersettings.menu );
+}
diff --git a/code/q3_ui/ui_preferences.c b/code/q3_ui/ui_preferences.c
new file mode 100644
index 0000000..693e3f4
--- /dev/null
+++ b/code/q3_ui/ui_preferences.c
@@ -0,0 +1,419 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+GAME OPTIONS MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+
+#define PREFERENCES_X_POS 360
+
+#define ID_CROSSHAIR 127
+#define ID_SIMPLEITEMS 128
+#define ID_HIGHQUALITYSKY 129
+#define ID_EJECTINGBRASS 130
+#define ID_WALLMARKS 131
+#define ID_DYNAMICLIGHTS 132
+#define ID_IDENTIFYTARGET 133
+#define ID_SYNCEVERYFRAME 134
+#define ID_FORCEMODEL 135
+#define ID_DRAWTEAMOVERLAY 136
+#define ID_ALLOWDOWNLOAD 137
+#define ID_BACK 138
+
+#define NUM_CROSSHAIRS 10
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menulist_s crosshair;
+ menuradiobutton_s simpleitems;
+ menuradiobutton_s brass;
+ menuradiobutton_s wallmarks;
+ menuradiobutton_s dynamiclights;
+ menuradiobutton_s identifytarget;
+ menuradiobutton_s highqualitysky;
+ menuradiobutton_s synceveryframe;
+ menuradiobutton_s forcemodel;
+ menulist_s drawteamoverlay;
+ menuradiobutton_s allowdownload;
+ menubitmap_s back;
+
+ qhandle_t crosshairShader[NUM_CROSSHAIRS];
+} preferences_t;
+
+static preferences_t s_preferences;
+
+static const char *teamoverlay_names[] =
+{
+ "off",
+ "upper right",
+ "lower right",
+ "lower left",
+ NULL
+};
+
+static void Preferences_SetMenuItems( void ) {
+ s_preferences.crosshair.curvalue = (int)trap_Cvar_VariableValue( "cg_drawCrosshair" ) % NUM_CROSSHAIRS;
+ s_preferences.simpleitems.curvalue = trap_Cvar_VariableValue( "cg_simpleItems" ) != 0;
+ s_preferences.brass.curvalue = trap_Cvar_VariableValue( "cg_brassTime" ) != 0;
+ s_preferences.wallmarks.curvalue = trap_Cvar_VariableValue( "cg_marks" ) != 0;
+ s_preferences.identifytarget.curvalue = trap_Cvar_VariableValue( "cg_drawCrosshairNames" ) != 0;
+ s_preferences.dynamiclights.curvalue = trap_Cvar_VariableValue( "r_dynamiclight" ) != 0;
+ s_preferences.highqualitysky.curvalue = trap_Cvar_VariableValue ( "r_fastsky" ) == 0;
+ s_preferences.synceveryframe.curvalue = trap_Cvar_VariableValue( "r_finish" ) != 0;
+ s_preferences.forcemodel.curvalue = trap_Cvar_VariableValue( "cg_forcemodel" ) != 0;
+ s_preferences.drawteamoverlay.curvalue = Com_Clamp( 0, 3, trap_Cvar_VariableValue( "cg_drawTeamOverlay" ) );
+ s_preferences.allowdownload.curvalue = trap_Cvar_VariableValue( "cl_allowDownload" ) != 0;
+}
+
+
+static void Preferences_Event( void* ptr, int notification ) {
+ if( notification != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_CROSSHAIR:
+ s_preferences.crosshair.curvalue++;
+ if( s_preferences.crosshair.curvalue == NUM_CROSSHAIRS ) {
+ s_preferences.crosshair.curvalue = 0;
+ }
+ trap_Cvar_SetValue( "cg_drawCrosshair", s_preferences.crosshair.curvalue );
+ break;
+
+ case ID_SIMPLEITEMS:
+ trap_Cvar_SetValue( "cg_simpleItems", s_preferences.simpleitems.curvalue );
+ break;
+
+ case ID_HIGHQUALITYSKY:
+ trap_Cvar_SetValue( "r_fastsky", !s_preferences.highqualitysky.curvalue );
+ break;
+
+ case ID_EJECTINGBRASS:
+ if ( s_preferences.brass.curvalue )
+ trap_Cvar_Reset( "cg_brassTime" );
+ else
+ trap_Cvar_SetValue( "cg_brassTime", 0 );
+ break;
+
+ case ID_WALLMARKS:
+ trap_Cvar_SetValue( "cg_marks", s_preferences.wallmarks.curvalue );
+ break;
+
+ case ID_DYNAMICLIGHTS:
+ trap_Cvar_SetValue( "r_dynamiclight", s_preferences.dynamiclights.curvalue );
+ break;
+
+ case ID_IDENTIFYTARGET:
+ trap_Cvar_SetValue( "cg_drawCrosshairNames", s_preferences.identifytarget.curvalue );
+ break;
+
+ case ID_SYNCEVERYFRAME:
+ trap_Cvar_SetValue( "r_finish", s_preferences.synceveryframe.curvalue );
+ break;
+
+ case ID_FORCEMODEL:
+ trap_Cvar_SetValue( "cg_forcemodel", s_preferences.forcemodel.curvalue );
+ break;
+
+ case ID_DRAWTEAMOVERLAY:
+ trap_Cvar_SetValue( "cg_drawTeamOverlay", s_preferences.drawteamoverlay.curvalue );
+ break;
+
+ case ID_ALLOWDOWNLOAD:
+ trap_Cvar_SetValue( "cl_allowDownload", s_preferences.allowdownload.curvalue );
+ trap_Cvar_SetValue( "sv_allowDownload", s_preferences.allowdownload.curvalue );
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+=================
+Crosshair_Draw
+=================
+*/
+static void Crosshair_Draw( void *self ) {
+ menulist_s *s;
+ float *color;
+ int x, y;
+ int style;
+ qboolean focus;
+
+ s = (menulist_s *)self;
+ x = s->generic.x;
+ y = s->generic.y;
+
+ style = UI_SMALLFONT;
+ focus = (s->generic.parent->cursor == s->generic.menuPosition);
+
+ if ( s->generic.flags & QMF_GRAYED )
+ color = text_color_disabled;
+ else if ( focus )
+ {
+ color = text_color_highlight;
+ style |= UI_PULSE;
+ }
+ else if ( s->generic.flags & QMF_BLINK )
+ {
+ color = text_color_highlight;
+ style |= UI_BLINK;
+ }
+ else
+ color = text_color_normal;
+
+ if ( focus )
+ {
+ // draw cursor
+ UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color );
+ UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
+ }
+
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color );
+ if( !s->curvalue ) {
+ return;
+ }
+ UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y - 4, 24, 24, s_preferences.crosshairShader[s->curvalue] );
+}
+
+
+static void Preferences_MenuInit( void ) {
+ int y;
+
+ memset( &s_preferences, 0 ,sizeof(preferences_t) );
+
+ Preferences_Cache();
+
+ s_preferences.menu.wrapAround = qtrue;
+ s_preferences.menu.fullscreen = qtrue;
+
+ s_preferences.banner.generic.type = MTYPE_BTEXT;
+ s_preferences.banner.generic.x = 320;
+ s_preferences.banner.generic.y = 16;
+ s_preferences.banner.string = "GAME OPTIONS";
+ s_preferences.banner.color = color_white;
+ s_preferences.banner.style = UI_CENTER;
+
+ s_preferences.framel.generic.type = MTYPE_BITMAP;
+ s_preferences.framel.generic.name = ART_FRAMEL;
+ s_preferences.framel.generic.flags = QMF_INACTIVE;
+ s_preferences.framel.generic.x = 0;
+ s_preferences.framel.generic.y = 78;
+ s_preferences.framel.width = 256;
+ s_preferences.framel.height = 329;
+
+ s_preferences.framer.generic.type = MTYPE_BITMAP;
+ s_preferences.framer.generic.name = ART_FRAMER;
+ s_preferences.framer.generic.flags = QMF_INACTIVE;
+ s_preferences.framer.generic.x = 376;
+ s_preferences.framer.generic.y = 76;
+ s_preferences.framer.width = 256;
+ s_preferences.framer.height = 334;
+
+ y = 144;
+ s_preferences.crosshair.generic.type = MTYPE_TEXT;
+ s_preferences.crosshair.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT|QMF_NODEFAULTINIT|QMF_OWNERDRAW;
+ s_preferences.crosshair.generic.x = PREFERENCES_X_POS;
+ s_preferences.crosshair.generic.y = y;
+ s_preferences.crosshair.generic.name = "Crosshair:";
+ s_preferences.crosshair.generic.callback = Preferences_Event;
+ s_preferences.crosshair.generic.ownerdraw = Crosshair_Draw;
+ s_preferences.crosshair.generic.id = ID_CROSSHAIR;
+ s_preferences.crosshair.generic.top = y - 4;
+ s_preferences.crosshair.generic.bottom = y + 20;
+ s_preferences.crosshair.generic.left = PREFERENCES_X_POS - ( ( strlen(s_preferences.crosshair.generic.name) + 1 ) * SMALLCHAR_WIDTH );
+ s_preferences.crosshair.generic.right = PREFERENCES_X_POS + 48;
+
+ y += BIGCHAR_HEIGHT+2+4;
+ s_preferences.simpleitems.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.simpleitems.generic.name = "Simple Items:";
+ s_preferences.simpleitems.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.simpleitems.generic.callback = Preferences_Event;
+ s_preferences.simpleitems.generic.id = ID_SIMPLEITEMS;
+ s_preferences.simpleitems.generic.x = PREFERENCES_X_POS;
+ s_preferences.simpleitems.generic.y = y;
+
+ y += BIGCHAR_HEIGHT;
+ s_preferences.wallmarks.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.wallmarks.generic.name = "Marks on Walls:";
+ s_preferences.wallmarks.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.wallmarks.generic.callback = Preferences_Event;
+ s_preferences.wallmarks.generic.id = ID_WALLMARKS;
+ s_preferences.wallmarks.generic.x = PREFERENCES_X_POS;
+ s_preferences.wallmarks.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.brass.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.brass.generic.name = "Ejecting Brass:";
+ s_preferences.brass.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.brass.generic.callback = Preferences_Event;
+ s_preferences.brass.generic.id = ID_EJECTINGBRASS;
+ s_preferences.brass.generic.x = PREFERENCES_X_POS;
+ s_preferences.brass.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.dynamiclights.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.dynamiclights.generic.name = "Dynamic Lights:";
+ s_preferences.dynamiclights.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.dynamiclights.generic.callback = Preferences_Event;
+ s_preferences.dynamiclights.generic.id = ID_DYNAMICLIGHTS;
+ s_preferences.dynamiclights.generic.x = PREFERENCES_X_POS;
+ s_preferences.dynamiclights.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.identifytarget.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.identifytarget.generic.name = "Identify Target:";
+ s_preferences.identifytarget.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.identifytarget.generic.callback = Preferences_Event;
+ s_preferences.identifytarget.generic.id = ID_IDENTIFYTARGET;
+ s_preferences.identifytarget.generic.x = PREFERENCES_X_POS;
+ s_preferences.identifytarget.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.highqualitysky.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.highqualitysky.generic.name = "High Quality Sky:";
+ s_preferences.highqualitysky.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.highqualitysky.generic.callback = Preferences_Event;
+ s_preferences.highqualitysky.generic.id = ID_HIGHQUALITYSKY;
+ s_preferences.highqualitysky.generic.x = PREFERENCES_X_POS;
+ s_preferences.highqualitysky.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.synceveryframe.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.synceveryframe.generic.name = "Sync Every Frame:";
+ s_preferences.synceveryframe.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.synceveryframe.generic.callback = Preferences_Event;
+ s_preferences.synceveryframe.generic.id = ID_SYNCEVERYFRAME;
+ s_preferences.synceveryframe.generic.x = PREFERENCES_X_POS;
+ s_preferences.synceveryframe.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.forcemodel.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.forcemodel.generic.name = "Force Player Models:";
+ s_preferences.forcemodel.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.forcemodel.generic.callback = Preferences_Event;
+ s_preferences.forcemodel.generic.id = ID_FORCEMODEL;
+ s_preferences.forcemodel.generic.x = PREFERENCES_X_POS;
+ s_preferences.forcemodel.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.drawteamoverlay.generic.type = MTYPE_SPINCONTROL;
+ s_preferences.drawteamoverlay.generic.name = "Draw Team Overlay:";
+ s_preferences.drawteamoverlay.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.drawteamoverlay.generic.callback = Preferences_Event;
+ s_preferences.drawteamoverlay.generic.id = ID_DRAWTEAMOVERLAY;
+ s_preferences.drawteamoverlay.generic.x = PREFERENCES_X_POS;
+ s_preferences.drawteamoverlay.generic.y = y;
+ s_preferences.drawteamoverlay.itemnames = teamoverlay_names;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.allowdownload.generic.type = MTYPE_RADIOBUTTON;
+ s_preferences.allowdownload.generic.name = "Automatic Downloading:";
+ s_preferences.allowdownload.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_preferences.allowdownload.generic.callback = Preferences_Event;
+ s_preferences.allowdownload.generic.id = ID_ALLOWDOWNLOAD;
+ s_preferences.allowdownload.generic.x = PREFERENCES_X_POS;
+ s_preferences.allowdownload.generic.y = y;
+
+ y += BIGCHAR_HEIGHT+2;
+ s_preferences.back.generic.type = MTYPE_BITMAP;
+ s_preferences.back.generic.name = ART_BACK0;
+ s_preferences.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_preferences.back.generic.callback = Preferences_Event;
+ s_preferences.back.generic.id = ID_BACK;
+ s_preferences.back.generic.x = 0;
+ s_preferences.back.generic.y = 480-64;
+ s_preferences.back.width = 128;
+ s_preferences.back.height = 64;
+ s_preferences.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &s_preferences.menu, &s_preferences.banner );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.framel );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.framer );
+
+ Menu_AddItem( &s_preferences.menu, &s_preferences.crosshair );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.simpleitems );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.wallmarks );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.brass );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.dynamiclights );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.identifytarget );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.highqualitysky );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.synceveryframe );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.forcemodel );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.drawteamoverlay );
+ Menu_AddItem( &s_preferences.menu, &s_preferences.allowdownload );
+
+ Menu_AddItem( &s_preferences.menu, &s_preferences.back );
+
+ Preferences_SetMenuItems();
+}
+
+
+/*
+===============
+Preferences_Cache
+===============
+*/
+void Preferences_Cache( void ) {
+ int n;
+
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ for( n = 0; n < NUM_CROSSHAIRS; n++ ) {
+ s_preferences.crosshairShader[n] = trap_R_RegisterShaderNoMip( va("gfx/2d/crosshair%c", 'a' + n ) );
+ }
+}
+
+
+/*
+===============
+UI_PreferencesMenu
+===============
+*/
+void UI_PreferencesMenu( void ) {
+ Preferences_MenuInit();
+ UI_PushMenu( &s_preferences.menu );
+}
diff --git a/code/q3_ui/ui_qmenu.c b/code/q3_ui/ui_qmenu.c
new file mode 100644
index 0000000..e955c51
--- /dev/null
+++ b/code/q3_ui/ui_qmenu.c
@@ -0,0 +1,1745 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/**********************************************************************
+ UI_QMENU.C
+
+ Quake's menu framework system.
+**********************************************************************/
+#include "ui_local.h"
+
+sfxHandle_t menu_in_sound;
+sfxHandle_t menu_move_sound;
+sfxHandle_t menu_out_sound;
+sfxHandle_t menu_buzz_sound;
+sfxHandle_t menu_null_sound;
+sfxHandle_t weaponChangeSound;
+
+static qhandle_t sliderBar;
+static qhandle_t sliderButton_0;
+static qhandle_t sliderButton_1;
+
+vec4_t menu_text_color = {1.0f, 1.0f, 1.0f, 1.0f};
+vec4_t menu_dim_color = {0.0f, 0.0f, 0.0f, 0.75f};
+vec4_t color_black = {0.00f, 0.00f, 0.00f, 1.00f};
+vec4_t color_white = {1.00f, 1.00f, 1.00f, 1.00f};
+vec4_t color_yellow = {1.00f, 1.00f, 0.00f, 1.00f};
+vec4_t color_blue = {0.00f, 0.00f, 1.00f, 1.00f};
+vec4_t color_lightOrange = {1.00f, 0.68f, 0.00f, 1.00f };
+vec4_t color_orange = {1.00f, 0.43f, 0.00f, 1.00f};
+vec4_t color_red = {1.00f, 0.00f, 0.00f, 1.00f};
+vec4_t color_dim = {0.00f, 0.00f, 0.00f, 0.25f};
+
+// current color scheme
+vec4_t pulse_color = {1.00f, 1.00f, 1.00f, 1.00f};
+vec4_t text_color_disabled = {0.50f, 0.50f, 0.50f, 1.00f}; // light gray
+vec4_t text_color_normal = {1.00f, 0.43f, 0.00f, 1.00f}; // light orange
+vec4_t text_color_highlight = {1.00f, 1.00f, 0.00f, 1.00f}; // bright yellow
+vec4_t listbar_color = {1.00f, 0.43f, 0.00f, 0.30f}; // transluscent orange
+vec4_t text_color_status = {1.00f, 1.00f, 1.00f, 1.00f}; // bright white
+
+// action widget
+static void Action_Init( menuaction_s *a );
+static void Action_Draw( menuaction_s *a );
+
+// radio button widget
+static void RadioButton_Init( menuradiobutton_s *rb );
+static void RadioButton_Draw( menuradiobutton_s *rb );
+static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key );
+
+// slider widget
+static void Slider_Init( menuslider_s *s );
+static sfxHandle_t Slider_Key( menuslider_s *s, int key );
+static void Slider_Draw( menuslider_s *s );
+
+// spin control widget
+static void SpinControl_Init( menulist_s *s );
+static void SpinControl_Draw( menulist_s *s );
+static sfxHandle_t SpinControl_Key( menulist_s *l, int key );
+
+// text widget
+static void Text_Init( menutext_s *b );
+static void Text_Draw( menutext_s *b );
+
+// scrolllist widget
+static void ScrollList_Init( menulist_s *l );
+sfxHandle_t ScrollList_Key( menulist_s *l, int key );
+
+// proportional text widget
+static void PText_Init( menutext_s *b );
+static void PText_Draw( menutext_s *b );
+
+// proportional banner text widget
+static void BText_Init( menutext_s *b );
+static void BText_Draw( menutext_s *b );
+
+/*
+=================
+Text_Init
+=================
+*/
+static void Text_Init( menutext_s *t )
+{
+ t->generic.flags |= QMF_INACTIVE;
+}
+
+/*
+=================
+Text_Draw
+=================
+*/
+static void Text_Draw( menutext_s *t )
+{
+ int x;
+ int y;
+ char buff[512];
+ float* color;
+
+ x = t->generic.x;
+ y = t->generic.y;
+
+ buff[0] = '\0';
+
+ // possible label
+ if (t->generic.name)
+ strcpy(buff,t->generic.name);
+
+ // possible value
+ if (t->string)
+ strcat(buff,t->string);
+
+ if (t->generic.flags & QMF_GRAYED)
+ color = text_color_disabled;
+ else
+ color = t->color;
+
+ UI_DrawString( x, y, buff, t->style, color );
+}
+
+/*
+=================
+BText_Init
+=================
+*/
+static void BText_Init( menutext_s *t )
+{
+ t->generic.flags |= QMF_INACTIVE;
+}
+
+/*
+=================
+BText_Draw
+=================
+*/
+static void BText_Draw( menutext_s *t )
+{
+ int x;
+ int y;
+ float* color;
+
+ x = t->generic.x;
+ y = t->generic.y;
+
+ if (t->generic.flags & QMF_GRAYED)
+ color = text_color_disabled;
+ else
+ color = t->color;
+
+ UI_DrawBannerString( x, y, t->string, t->style, color );
+}
+
+/*
+=================
+PText_Init
+=================
+*/
+static void PText_Init( menutext_s *t )
+{
+ int x;
+ int y;
+ int w;
+ int h;
+ float sizeScale;
+
+ sizeScale = UI_ProportionalSizeScale( t->style );
+
+ x = t->generic.x;
+ y = t->generic.y;
+ w = UI_ProportionalStringWidth( t->string ) * sizeScale;
+ h = PROP_HEIGHT * sizeScale;
+
+ if( t->generic.flags & QMF_RIGHT_JUSTIFY ) {
+ x -= w;
+ }
+ else if( t->generic.flags & QMF_CENTER_JUSTIFY ) {
+ x -= w / 2;
+ }
+
+ t->generic.left = x - PROP_GAP_WIDTH * sizeScale;
+ t->generic.right = x + w + PROP_GAP_WIDTH * sizeScale;
+ t->generic.top = y;
+ t->generic.bottom = y + h;
+}
+
+/*
+=================
+PText_Draw
+=================
+*/
+static void PText_Draw( menutext_s *t )
+{
+ int x;
+ int y;
+ float * color;
+ int style;
+
+ x = t->generic.x;
+ y = t->generic.y;
+
+ if (t->generic.flags & QMF_GRAYED)
+ color = text_color_disabled;
+ else
+ color = t->color;
+
+ style = t->style;
+ if( t->generic.flags & QMF_PULSEIFFOCUS ) {
+ if( Menu_ItemAtCursor( t->generic.parent ) == t ) {
+ style |= UI_PULSE;
+ }
+ else {
+ style |= UI_INVERSE;
+ }
+ }
+
+ UI_DrawProportionalString( x, y, t->string, style, color );
+}
+
+/*
+=================
+Bitmap_Init
+=================
+*/
+void Bitmap_Init( menubitmap_s *b )
+{
+ int x;
+ int y;
+ int w;
+ int h;
+
+ x = b->generic.x;
+ y = b->generic.y;
+ w = b->width;
+ h = b->height;
+ if( w < 0 ) {
+ w = -w;
+ }
+ if( h < 0 ) {
+ h = -h;
+ }
+
+ if (b->generic.flags & QMF_RIGHT_JUSTIFY)
+ {
+ x = x - w;
+ }
+ else if (b->generic.flags & QMF_CENTER_JUSTIFY)
+ {
+ x = x - w/2;
+ }
+
+ b->generic.left = x;
+ b->generic.right = x + w;
+ b->generic.top = y;
+ b->generic.bottom = y + h;
+
+ b->shader = 0;
+ b->focusshader = 0;
+}
+
+/*
+=================
+Bitmap_Draw
+=================
+*/
+void Bitmap_Draw( menubitmap_s *b )
+{
+ float x;
+ float y;
+ float w;
+ float h;
+ vec4_t tempcolor;
+ float* color;
+
+ x = b->generic.x;
+ y = b->generic.y;
+ w = b->width;
+ h = b->height;
+
+ if (b->generic.flags & QMF_RIGHT_JUSTIFY)
+ {
+ x = x - w;
+ }
+ else if (b->generic.flags & QMF_CENTER_JUSTIFY)
+ {
+ x = x - w/2;
+ }
+
+ // used to refresh shader
+ if (b->generic.name && !b->shader)
+ {
+ b->shader = trap_R_RegisterShaderNoMip( b->generic.name );
+ if (!b->shader && b->errorpic)
+ b->shader = trap_R_RegisterShaderNoMip( b->errorpic );
+ }
+
+ if (b->focuspic && !b->focusshader)
+ b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic );
+
+ if (b->generic.flags & QMF_GRAYED)
+ {
+ if (b->shader)
+ {
+ trap_R_SetColor( colorMdGrey );
+ UI_DrawHandlePic( x, y, w, h, b->shader );
+ trap_R_SetColor( NULL );
+ }
+ }
+ else
+ {
+ if (b->shader)
+ UI_DrawHandlePic( x, y, w, h, b->shader );
+
+ if ( ( (b->generic.flags & QMF_PULSE)
+ || (b->generic.flags & QMF_PULSEIFFOCUS) )
+ && (Menu_ItemAtCursor( b->generic.parent ) == b))
+ {
+ if (b->focuscolor)
+ {
+ tempcolor[0] = b->focuscolor[0];
+ tempcolor[1] = b->focuscolor[1];
+ tempcolor[2] = b->focuscolor[2];
+ color = tempcolor;
+ }
+ else
+ color = pulse_color;
+ color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR);
+
+ trap_R_SetColor( color );
+ UI_DrawHandlePic( x, y, w, h, b->focusshader );
+ trap_R_SetColor( NULL );
+ }
+ else if ((b->generic.flags & QMF_HIGHLIGHT) || ((b->generic.flags & QMF_HIGHLIGHT_IF_FOCUS) && (Menu_ItemAtCursor( b->generic.parent ) == b)))
+ {
+ if (b->focuscolor)
+ {
+ trap_R_SetColor( b->focuscolor );
+ UI_DrawHandlePic( x, y, w, h, b->focusshader );
+ trap_R_SetColor( NULL );
+ }
+ else
+ UI_DrawHandlePic( x, y, w, h, b->focusshader );
+ }
+ }
+}
+
+/*
+=================
+Action_Init
+=================
+*/
+static void Action_Init( menuaction_s *a )
+{
+ int len;
+
+ // calculate bounds
+ if (a->generic.name)
+ len = strlen(a->generic.name);
+ else
+ len = 0;
+
+ // left justify text
+ a->generic.left = a->generic.x;
+ a->generic.right = a->generic.x + len*BIGCHAR_WIDTH;
+ a->generic.top = a->generic.y;
+ a->generic.bottom = a->generic.y + BIGCHAR_HEIGHT;
+}
+
+/*
+=================
+Action_Draw
+=================
+*/
+static void Action_Draw( menuaction_s *a )
+{
+ int x, y;
+ int style;
+ float* color;
+
+ style = 0;
+ color = menu_text_color;
+ if ( a->generic.flags & QMF_GRAYED )
+ {
+ color = text_color_disabled;
+ }
+ else if (( a->generic.flags & QMF_PULSEIFFOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition ))
+ {
+ color = text_color_highlight;
+ style = UI_PULSE;
+ }
+ else if (( a->generic.flags & QMF_HIGHLIGHT_IF_FOCUS ) && ( a->generic.parent->cursor == a->generic.menuPosition ))
+ {
+ color = text_color_highlight;
+ }
+ else if ( a->generic.flags & QMF_BLINK )
+ {
+ style = UI_BLINK;
+ color = text_color_highlight;
+ }
+
+ x = a->generic.x;
+ y = a->generic.y;
+
+ UI_DrawString( x, y, a->generic.name, UI_LEFT|style, color );
+
+ if ( a->generic.parent->cursor == a->generic.menuPosition )
+ {
+ // draw cursor
+ UI_DrawChar( x - BIGCHAR_WIDTH, y, 13, UI_LEFT|UI_BLINK, color);
+ }
+}
+
+/*
+=================
+RadioButton_Init
+=================
+*/
+static void RadioButton_Init( menuradiobutton_s *rb )
+{
+ int len;
+
+ // calculate bounds
+ if (rb->generic.name)
+ len = strlen(rb->generic.name);
+ else
+ len = 0;
+
+ rb->generic.left = rb->generic.x - (len+1)*SMALLCHAR_WIDTH;
+ rb->generic.right = rb->generic.x + 6*SMALLCHAR_WIDTH;
+ rb->generic.top = rb->generic.y;
+ rb->generic.bottom = rb->generic.y + SMALLCHAR_HEIGHT;
+}
+
+/*
+=================
+RadioButton_Key
+=================
+*/
+static sfxHandle_t RadioButton_Key( menuradiobutton_s *rb, int key )
+{
+ switch (key)
+ {
+ case K_MOUSE1:
+ if (!(rb->generic.flags & QMF_HASMOUSEFOCUS))
+ break;
+
+ case K_JOY1:
+ case K_JOY2:
+ case K_JOY3:
+ case K_JOY4:
+ case K_ENTER:
+ case K_KP_ENTER:
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ rb->curvalue = !rb->curvalue;
+ if ( rb->generic.callback )
+ rb->generic.callback( rb, QM_ACTIVATED );
+
+ return (menu_move_sound);
+ }
+
+ // key not handled
+ return 0;
+}
+
+/*
+=================
+RadioButton_Draw
+=================
+*/
+static void RadioButton_Draw( menuradiobutton_s *rb )
+{
+ int x;
+ int y;
+ float *color;
+ int style;
+ qboolean focus;
+
+ x = rb->generic.x;
+ y = rb->generic.y;
+
+ focus = (rb->generic.parent->cursor == rb->generic.menuPosition);
+
+ if ( rb->generic.flags & QMF_GRAYED )
+ {
+ color = text_color_disabled;
+ style = UI_LEFT|UI_SMALLFONT;
+ }
+ else if ( focus )
+ {
+ color = text_color_highlight;
+ style = UI_LEFT|UI_PULSE|UI_SMALLFONT;
+ }
+ else
+ {
+ color = text_color_normal;
+ style = UI_LEFT|UI_SMALLFONT;
+ }
+
+ if ( focus )
+ {
+ // draw cursor
+ UI_FillRect( rb->generic.left, rb->generic.top, rb->generic.right-rb->generic.left+1, rb->generic.bottom-rb->generic.top+1, listbar_color );
+ UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
+ }
+
+ if ( rb->generic.name )
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, rb->generic.name, UI_RIGHT|UI_SMALLFONT, color );
+
+ if ( !rb->curvalue )
+ {
+ UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_off);
+ UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "off", style, color );
+ }
+ else
+ {
+ UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y + 2, 16, 16, uis.rb_on );
+ UI_DrawString( x + SMALLCHAR_WIDTH + 16, y, "on", style, color );
+ }
+}
+
+/*
+=================
+Slider_Init
+=================
+*/
+static void Slider_Init( menuslider_s *s )
+{
+ int len;
+
+ // calculate bounds
+ if (s->generic.name)
+ len = strlen(s->generic.name);
+ else
+ len = 0;
+
+ s->generic.left = s->generic.x - (len+1)*SMALLCHAR_WIDTH;
+ s->generic.right = s->generic.x + (SLIDER_RANGE+2+1)*SMALLCHAR_WIDTH;
+ s->generic.top = s->generic.y;
+ s->generic.bottom = s->generic.y + SMALLCHAR_HEIGHT;
+}
+
+/*
+=================
+Slider_Key
+=================
+*/
+static sfxHandle_t Slider_Key( menuslider_s *s, int key )
+{
+ sfxHandle_t sound;
+ int x;
+ int oldvalue;
+
+ switch (key)
+ {
+ case K_MOUSE1:
+ x = uis.cursorx - s->generic.x - 2*SMALLCHAR_WIDTH;
+ oldvalue = s->curvalue;
+ s->curvalue = (x/(float)(SLIDER_RANGE*SMALLCHAR_WIDTH)) * (s->maxvalue-s->minvalue) + s->minvalue;
+
+ if (s->curvalue < s->minvalue)
+ s->curvalue = s->minvalue;
+ else if (s->curvalue > s->maxvalue)
+ s->curvalue = s->maxvalue;
+ if (s->curvalue != oldvalue)
+ sound = menu_move_sound;
+ else
+ sound = 0;
+ break;
+
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ if (s->curvalue > s->minvalue)
+ {
+ s->curvalue--;
+ sound = menu_move_sound;
+ }
+ else
+ sound = menu_buzz_sound;
+ break;
+
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ if (s->curvalue < s->maxvalue)
+ {
+ s->curvalue++;
+ sound = menu_move_sound;
+ }
+ else
+ sound = menu_buzz_sound;
+ break;
+
+ default:
+ // key not handled
+ sound = 0;
+ break;
+ }
+
+ if ( sound && s->generic.callback )
+ s->generic.callback( s, QM_ACTIVATED );
+
+ return (sound);
+}
+
+#if 1
+/*
+=================
+Slider_Draw
+=================
+*/
+static void Slider_Draw( menuslider_s *s ) {
+ int x;
+ int y;
+ int style;
+ float *color;
+ int button;
+ qboolean focus;
+
+ x = s->generic.x;
+ y = s->generic.y;
+ focus = (s->generic.parent->cursor == s->generic.menuPosition);
+
+ if( s->generic.flags & QMF_GRAYED ) {
+ color = text_color_disabled;
+ style = UI_SMALLFONT;
+ }
+ else if( focus ) {
+ color = text_color_highlight;
+ style = UI_SMALLFONT | UI_PULSE;
+ }
+ else {
+ color = text_color_normal;
+ style = UI_SMALLFONT;
+ }
+
+ // draw label
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color );
+
+ // draw slider
+ UI_SetColor( color );
+ UI_DrawHandlePic( x + SMALLCHAR_WIDTH, y, 96, 16, sliderBar );
+ UI_SetColor( NULL );
+
+ // clamp thumb
+ if( s->maxvalue > s->minvalue ) {
+ s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
+ if( s->range < 0 ) {
+ s->range = 0;
+ }
+ else if( s->range > 1) {
+ s->range = 1;
+ }
+ }
+ else {
+ s->range = 0;
+ }
+
+ // draw thumb
+ if( style & UI_PULSE) {
+ button = sliderButton_1;
+ }
+ else {
+ button = sliderButton_0;
+ }
+
+ UI_DrawHandlePic( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ) - 2, y - 2, 12, 20, button );
+}
+#else
+/*
+=================
+Slider_Draw
+=================
+*/
+static void Slider_Draw( menuslider_s *s )
+{
+ float *color;
+ int style;
+ int i;
+ int x;
+ int y;
+ qboolean focus;
+
+ x = s->generic.x;
+ y = s->generic.y;
+ focus = (s->generic.parent->cursor == s->generic.menuPosition);
+
+ style = UI_SMALLFONT;
+ if ( s->generic.flags & QMF_GRAYED )
+ {
+ color = text_color_disabled;
+ }
+ else if (focus)
+ {
+ color = text_color_highlight;
+ style |= UI_PULSE;
+ }
+ else
+ {
+ color = text_color_normal;
+ }
+
+ if ( focus )
+ {
+ // draw cursor
+ UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color );
+ UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
+ }
+
+ // draw label
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, UI_RIGHT|style, color );
+
+ // draw slider
+ UI_DrawChar( x + SMALLCHAR_WIDTH, y, 128, UI_LEFT|style, color);
+ for ( i = 0; i < SLIDER_RANGE; i++ )
+ UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 129, UI_LEFT|style, color);
+ UI_DrawChar( x + (i+2)*SMALLCHAR_WIDTH, y, 130, UI_LEFT|style, color);
+
+ // clamp thumb
+ if (s->maxvalue > s->minvalue)
+ {
+ s->range = ( s->curvalue - s->minvalue ) / ( float ) ( s->maxvalue - s->minvalue );
+ if ( s->range < 0)
+ s->range = 0;
+ else if ( s->range > 1)
+ s->range = 1;
+ }
+ else
+ s->range = 0;
+
+ // draw thumb
+ if (style & UI_PULSE) {
+ style &= ~UI_PULSE;
+ style |= UI_BLINK;
+ }
+ UI_DrawChar( (int)( x + 2*SMALLCHAR_WIDTH + (SLIDER_RANGE-1)*SMALLCHAR_WIDTH* s->range ), y, 131, UI_LEFT|style, color);
+}
+#endif
+
+/*
+=================
+SpinControl_Init
+=================
+*/
+static void SpinControl_Init( menulist_s *s ) {
+ int len;
+ int l;
+ const char* str;
+
+ if (s->generic.name)
+ len = strlen(s->generic.name) * SMALLCHAR_WIDTH;
+ else
+ len = 0;
+
+ s->generic.left = s->generic.x - SMALLCHAR_WIDTH - len;
+
+ len = s->numitems = 0;
+ while ( (str = s->itemnames[s->numitems]) != 0 )
+ {
+ l = strlen(str);
+ if (l > len)
+ len = l;
+
+ s->numitems++;
+ }
+
+ s->generic.top = s->generic.y;
+ s->generic.right = s->generic.x + (len+1)*SMALLCHAR_WIDTH;
+ s->generic.bottom = s->generic.y + SMALLCHAR_HEIGHT;
+}
+
+/*
+=================
+SpinControl_Key
+=================
+*/
+static sfxHandle_t SpinControl_Key( menulist_s *s, int key )
+{
+ sfxHandle_t sound;
+
+ sound = 0;
+ switch (key)
+ {
+ case K_MOUSE1:
+ s->curvalue++;
+ if (s->curvalue >= s->numitems)
+ s->curvalue = 0;
+ sound = menu_move_sound;
+ break;
+
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ if (s->curvalue > 0)
+ {
+ s->curvalue--;
+ sound = menu_move_sound;
+ }
+ else
+ sound = menu_buzz_sound;
+ break;
+
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ if (s->curvalue < s->numitems-1)
+ {
+ s->curvalue++;
+ sound = menu_move_sound;
+ }
+ else
+ sound = menu_buzz_sound;
+ break;
+ }
+
+ if ( sound && s->generic.callback )
+ s->generic.callback( s, QM_ACTIVATED );
+
+ return (sound);
+}
+
+/*
+=================
+SpinControl_Draw
+=================
+*/
+static void SpinControl_Draw( menulist_s *s )
+{
+ float *color;
+ int x,y;
+ int style;
+ qboolean focus;
+
+ x = s->generic.x;
+ y = s->generic.y;
+
+ style = UI_SMALLFONT;
+ focus = (s->generic.parent->cursor == s->generic.menuPosition);
+
+ if ( s->generic.flags & QMF_GRAYED )
+ color = text_color_disabled;
+ else if ( focus )
+ {
+ color = text_color_highlight;
+ style |= UI_PULSE;
+ }
+ else if ( s->generic.flags & QMF_BLINK )
+ {
+ color = text_color_highlight;
+ style |= UI_BLINK;
+ }
+ else
+ color = text_color_normal;
+
+ if ( focus )
+ {
+ // draw cursor
+ UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color );
+ UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
+ }
+
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color );
+ UI_DrawString( x + SMALLCHAR_WIDTH, y, s->itemnames[s->curvalue], style|UI_LEFT, color );
+}
+
+/*
+=================
+ScrollList_Init
+=================
+*/
+static void ScrollList_Init( menulist_s *l )
+{
+ int w;
+
+ l->oldvalue = 0;
+ l->curvalue = 0;
+ l->top = 0;
+
+ if( !l->columns ) {
+ l->columns = 1;
+ l->seperation = 0;
+ }
+ else if( !l->seperation ) {
+ l->seperation = 3;
+ }
+
+ w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH;
+
+ l->generic.left = l->generic.x;
+ l->generic.top = l->generic.y;
+ l->generic.right = l->generic.x + w;
+ l->generic.bottom = l->generic.y + l->height * SMALLCHAR_HEIGHT;
+
+ if( l->generic.flags & QMF_CENTER_JUSTIFY ) {
+ l->generic.left -= w / 2;
+ l->generic.right -= w / 2;
+ }
+}
+
+/*
+=================
+ScrollList_Key
+=================
+*/
+sfxHandle_t ScrollList_Key( menulist_s *l, int key )
+{
+ int x;
+ int y;
+ int w;
+ int i;
+ int j;
+ int c;
+ int cursorx;
+ int cursory;
+ int column;
+ int index;
+
+ switch (key)
+ {
+ case K_MOUSE1:
+ if (l->generic.flags & QMF_HASMOUSEFOCUS)
+ {
+ // check scroll region
+ x = l->generic.x;
+ y = l->generic.y;
+ w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH;
+ if( l->generic.flags & QMF_CENTER_JUSTIFY ) {
+ x -= w / 2;
+ }
+ if (UI_CursorInRect( x, y, w, l->height*SMALLCHAR_HEIGHT ))
+ {
+ cursorx = (uis.cursorx - x)/SMALLCHAR_WIDTH;
+ column = cursorx / (l->width + l->seperation);
+ cursory = (uis.cursory - y)/SMALLCHAR_HEIGHT;
+ index = column * l->height + cursory;
+ if (l->top + index < l->numitems)
+ {
+ l->oldvalue = l->curvalue;
+ l->curvalue = l->top + index;
+
+ if (l->oldvalue != l->curvalue && l->generic.callback)
+ {
+ l->generic.callback( l, QM_GOTFOCUS );
+ return (menu_move_sound);
+ }
+ }
+ }
+
+ // absorbed, silent sound effect
+ return (menu_null_sound);
+ }
+ break;
+
+ case K_KP_HOME:
+ case K_HOME:
+ l->oldvalue = l->curvalue;
+ l->curvalue = 0;
+ l->top = 0;
+
+ if (l->oldvalue != l->curvalue && l->generic.callback)
+ {
+ l->generic.callback( l, QM_GOTFOCUS );
+ return (menu_move_sound);
+ }
+ return (menu_buzz_sound);
+
+ case K_KP_END:
+ case K_END:
+ l->oldvalue = l->curvalue;
+ l->curvalue = l->numitems-1;
+ if( l->columns > 1 ) {
+ c = (l->curvalue / l->height + 1) * l->height;
+ l->top = c - (l->columns * l->height);
+ }
+ else {
+ l->top = l->curvalue - (l->height - 1);
+ }
+ if (l->top < 0)
+ l->top = 0;
+
+ if (l->oldvalue != l->curvalue && l->generic.callback)
+ {
+ l->generic.callback( l, QM_GOTFOCUS );
+ return (menu_move_sound);
+ }
+ return (menu_buzz_sound);
+
+ case K_PGUP:
+ case K_KP_PGUP:
+ if( l->columns > 1 ) {
+ return menu_null_sound;
+ }
+
+ if (l->curvalue > 0)
+ {
+ l->oldvalue = l->curvalue;
+ l->curvalue -= l->height-1;
+ if (l->curvalue < 0)
+ l->curvalue = 0;
+ l->top = l->curvalue;
+ if (l->top < 0)
+ l->top = 0;
+
+ if (l->generic.callback)
+ l->generic.callback( l, QM_GOTFOCUS );
+
+ return (menu_move_sound);
+ }
+ return (menu_buzz_sound);
+
+ case K_PGDN:
+ case K_KP_PGDN:
+ if( l->columns > 1 ) {
+ return menu_null_sound;
+ }
+
+ if (l->curvalue < l->numitems-1)
+ {
+ l->oldvalue = l->curvalue;
+ l->curvalue += l->height-1;
+ if (l->curvalue > l->numitems-1)
+ l->curvalue = l->numitems-1;
+ l->top = l->curvalue - (l->height-1);
+ if (l->top < 0)
+ l->top = 0;
+
+ if (l->generic.callback)
+ l->generic.callback( l, QM_GOTFOCUS );
+
+ return (menu_move_sound);
+ }
+ return (menu_buzz_sound);
+
+ case K_KP_UPARROW:
+ case K_UPARROW:
+ if( l->curvalue == 0 ) {
+ return menu_buzz_sound;
+ }
+
+ l->oldvalue = l->curvalue;
+ l->curvalue--;
+
+ if( l->curvalue < l->top ) {
+ if( l->columns == 1 ) {
+ l->top--;
+ }
+ else {
+ l->top -= l->height;
+ }
+ }
+
+ if( l->generic.callback ) {
+ l->generic.callback( l, QM_GOTFOCUS );
+ }
+
+ return (menu_move_sound);
+
+ case K_KP_DOWNARROW:
+ case K_DOWNARROW:
+ if( l->curvalue == l->numitems - 1 ) {
+ return menu_buzz_sound;
+ }
+
+ l->oldvalue = l->curvalue;
+ l->curvalue++;
+
+ if( l->curvalue >= l->top + l->columns * l->height ) {
+ if( l->columns == 1 ) {
+ l->top++;
+ }
+ else {
+ l->top += l->height;
+ }
+ }
+
+ if( l->generic.callback ) {
+ l->generic.callback( l, QM_GOTFOCUS );
+ }
+
+ return menu_move_sound;
+
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ if( l->columns == 1 ) {
+ return menu_null_sound;
+ }
+
+ if( l->curvalue < l->height ) {
+ return menu_buzz_sound;
+ }
+
+ l->oldvalue = l->curvalue;
+ l->curvalue -= l->height;
+
+ if( l->curvalue < l->top ) {
+ l->top -= l->height;
+ }
+
+ if( l->generic.callback ) {
+ l->generic.callback( l, QM_GOTFOCUS );
+ }
+
+ return menu_move_sound;
+
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ if( l->columns == 1 ) {
+ return menu_null_sound;
+ }
+
+ c = l->curvalue + l->height;
+
+ if( c >= l->numitems ) {
+ return menu_buzz_sound;
+ }
+
+ l->oldvalue = l->curvalue;
+ l->curvalue = c;
+
+ if( l->curvalue > l->top + l->columns * l->height - 1 ) {
+ l->top += l->height;
+ }
+
+ if( l->generic.callback ) {
+ l->generic.callback( l, QM_GOTFOCUS );
+ }
+
+ return menu_move_sound;
+ }
+
+ // cycle look for ascii key inside list items
+ if ( !Q_isprint( key ) )
+ return (0);
+
+ // force to lower for case insensitive compare
+ if ( Q_isupper( key ) )
+ {
+ key -= 'A' - 'a';
+ }
+
+ // iterate list items
+ for (i=1; i<=l->numitems; i++)
+ {
+ j = (l->curvalue + i) % l->numitems;
+ c = l->itemnames[j][0];
+ if ( Q_isupper( c ) )
+ {
+ c -= 'A' - 'a';
+ }
+
+ if (c == key)
+ {
+ // set current item, mimic windows listbox scroll behavior
+ if (j < l->top)
+ {
+ // behind top most item, set this as new top
+ l->top = j;
+ }
+ else if (j > l->top+l->height-1)
+ {
+ // past end of list box, do page down
+ l->top = (j+1) - l->height;
+ }
+
+ if (l->curvalue != j)
+ {
+ l->oldvalue = l->curvalue;
+ l->curvalue = j;
+ if (l->generic.callback)
+ l->generic.callback( l, QM_GOTFOCUS );
+ return ( menu_move_sound );
+ }
+
+ return (menu_buzz_sound);
+ }
+ }
+
+ return (menu_buzz_sound);
+}
+
+/*
+=================
+ScrollList_Draw
+=================
+*/
+void ScrollList_Draw( menulist_s *l )
+{
+ int x;
+ int u;
+ int y;
+ int i;
+ int base;
+ int column;
+ float* color;
+ qboolean hasfocus;
+ int style;
+
+ hasfocus = (l->generic.parent->cursor == l->generic.menuPosition);
+
+ x = l->generic.x;
+ for( column = 0; column < l->columns; column++ ) {
+ y = l->generic.y;
+ base = l->top + column * l->height;
+ for( i = base; i < base + l->height; i++) {
+ if (i >= l->numitems)
+ break;
+
+ if (i == l->curvalue)
+ {
+ u = x - 2;
+ if( l->generic.flags & QMF_CENTER_JUSTIFY ) {
+ u -= (l->width * SMALLCHAR_WIDTH) / 2 + 1;
+ }
+
+ UI_FillRect(u,y,l->width*SMALLCHAR_WIDTH,SMALLCHAR_HEIGHT+2,listbar_color);
+ color = text_color_highlight;
+
+ if (hasfocus)
+ style = UI_PULSE|UI_LEFT|UI_SMALLFONT;
+ else
+ style = UI_LEFT|UI_SMALLFONT;
+ }
+ else
+ {
+ color = text_color_normal;
+ style = UI_LEFT|UI_SMALLFONT;
+ }
+ if( l->generic.flags & QMF_CENTER_JUSTIFY ) {
+ style |= UI_CENTER;
+ }
+
+ UI_DrawString(
+ x,
+ y,
+ l->itemnames[i],
+ style,
+ color);
+
+ y += SMALLCHAR_HEIGHT;
+ }
+ x += (l->width + l->seperation) * SMALLCHAR_WIDTH;
+ }
+}
+
+/*
+=================
+Menu_AddItem
+=================
+*/
+void Menu_AddItem( menuframework_s *menu, void *item )
+{
+ menucommon_s *itemptr;
+
+ if (menu->nitems >= MAX_MENUITEMS)
+ trap_Error ("Menu_AddItem: excessive items");
+
+ menu->items[menu->nitems] = item;
+ ((menucommon_s*)menu->items[menu->nitems])->parent = menu;
+ ((menucommon_s*)menu->items[menu->nitems])->menuPosition = menu->nitems;
+ ((menucommon_s*)menu->items[menu->nitems])->flags &= ~QMF_HASMOUSEFOCUS;
+
+ // perform any item specific initializations
+ itemptr = (menucommon_s*)item;
+ if (!(itemptr->flags & QMF_NODEFAULTINIT))
+ {
+ switch (itemptr->type)
+ {
+ case MTYPE_ACTION:
+ Action_Init((menuaction_s*)item);
+ break;
+
+ case MTYPE_FIELD:
+ MenuField_Init((menufield_s*)item);
+ break;
+
+ case MTYPE_SPINCONTROL:
+ SpinControl_Init((menulist_s*)item);
+ break;
+
+ case MTYPE_RADIOBUTTON:
+ RadioButton_Init((menuradiobutton_s*)item);
+ break;
+
+ case MTYPE_SLIDER:
+ Slider_Init((menuslider_s*)item);
+ break;
+
+ case MTYPE_BITMAP:
+ Bitmap_Init((menubitmap_s*)item);
+ break;
+
+ case MTYPE_TEXT:
+ Text_Init((menutext_s*)item);
+ break;
+
+ case MTYPE_SCROLLLIST:
+ ScrollList_Init((menulist_s*)item);
+ break;
+
+ case MTYPE_PTEXT:
+ PText_Init((menutext_s*)item);
+ break;
+
+ case MTYPE_BTEXT:
+ BText_Init((menutext_s*)item);
+ break;
+
+ default:
+ trap_Error( va("Menu_Init: unknown type %d", itemptr->type) );
+ }
+ }
+
+ menu->nitems++;
+}
+
+/*
+=================
+Menu_CursorMoved
+=================
+*/
+void Menu_CursorMoved( menuframework_s *m )
+{
+ void (*callback)( void *self, int notification );
+
+ if (m->cursor_prev == m->cursor)
+ return;
+
+ if (m->cursor_prev >= 0 && m->cursor_prev < m->nitems)
+ {
+ callback = ((menucommon_s*)(m->items[m->cursor_prev]))->callback;
+ if (callback)
+ callback(m->items[m->cursor_prev],QM_LOSTFOCUS);
+ }
+
+ if (m->cursor >= 0 && m->cursor < m->nitems)
+ {
+ callback = ((menucommon_s*)(m->items[m->cursor]))->callback;
+ if (callback)
+ callback(m->items[m->cursor],QM_GOTFOCUS);
+ }
+}
+
+/*
+=================
+Menu_SetCursor
+=================
+*/
+void Menu_SetCursor( menuframework_s *m, int cursor )
+{
+ if (((menucommon_s*)(m->items[cursor]))->flags & (QMF_GRAYED|QMF_INACTIVE))
+ {
+ // cursor can't go there
+ return;
+ }
+
+ m->cursor_prev = m->cursor;
+ m->cursor = cursor;
+
+ Menu_CursorMoved( m );
+}
+
+/*
+=================
+Menu_SetCursorToItem
+=================
+*/
+void Menu_SetCursorToItem( menuframework_s *m, void* ptr )
+{
+ int i;
+
+ for (i=0; i<m->nitems; i++)
+ {
+ if (m->items[i] == ptr)
+ {
+ Menu_SetCursor( m, i );
+ return;
+ }
+ }
+}
+
+/*
+** Menu_AdjustCursor
+**
+** This function takes the given menu, the direction, and attempts
+** to adjust the menu's cursor so that it's at the next available
+** slot.
+*/
+void Menu_AdjustCursor( menuframework_s *m, int dir ) {
+ menucommon_s *item = NULL;
+ qboolean wrapped = qfalse;
+
+wrap:
+ while ( m->cursor >= 0 && m->cursor < m->nitems ) {
+ item = ( menucommon_s * ) m->items[m->cursor];
+ if (( item->flags & (QMF_GRAYED|QMF_MOUSEONLY|QMF_INACTIVE) ) ) {
+ m->cursor += dir;
+ }
+ else {
+ break;
+ }
+ }
+
+ if ( dir == 1 ) {
+ if ( m->cursor >= m->nitems ) {
+ if ( m->wrapAround ) {
+ if ( wrapped ) {
+ m->cursor = m->cursor_prev;
+ return;
+ }
+ m->cursor = 0;
+ wrapped = qtrue;
+ goto wrap;
+ }
+ m->cursor = m->cursor_prev;
+ }
+ }
+ else {
+ if ( m->cursor < 0 ) {
+ if ( m->wrapAround ) {
+ if ( wrapped ) {
+ m->cursor = m->cursor_prev;
+ return;
+ }
+ m->cursor = m->nitems - 1;
+ wrapped = qtrue;
+ goto wrap;
+ }
+ m->cursor = m->cursor_prev;
+ }
+ }
+}
+
+/*
+=================
+Menu_Draw
+=================
+*/
+void Menu_Draw( menuframework_s *menu )
+{
+ int i;
+ menucommon_s *itemptr;
+
+ // draw menu
+ for (i=0; i<menu->nitems; i++)
+ {
+ itemptr = (menucommon_s*)menu->items[i];
+
+ if (itemptr->flags & QMF_HIDDEN)
+ continue;
+
+ if (itemptr->ownerdraw)
+ {
+ // total subclassing, owner draws everything
+ itemptr->ownerdraw( itemptr );
+ }
+ else
+ {
+ switch (itemptr->type)
+ {
+ case MTYPE_RADIOBUTTON:
+ RadioButton_Draw( (menuradiobutton_s*)itemptr );
+ break;
+
+ case MTYPE_FIELD:
+ MenuField_Draw( (menufield_s*)itemptr );
+ break;
+
+ case MTYPE_SLIDER:
+ Slider_Draw( (menuslider_s*)itemptr );
+ break;
+
+ case MTYPE_SPINCONTROL:
+ SpinControl_Draw( (menulist_s*)itemptr );
+ break;
+
+ case MTYPE_ACTION:
+ Action_Draw( (menuaction_s*)itemptr );
+ break;
+
+ case MTYPE_BITMAP:
+ Bitmap_Draw( (menubitmap_s*)itemptr );
+ break;
+
+ case MTYPE_TEXT:
+ Text_Draw( (menutext_s*)itemptr );
+ break;
+
+ case MTYPE_SCROLLLIST:
+ ScrollList_Draw( (menulist_s*)itemptr );
+ break;
+
+ case MTYPE_PTEXT:
+ PText_Draw( (menutext_s*)itemptr );
+ break;
+
+ case MTYPE_BTEXT:
+ BText_Draw( (menutext_s*)itemptr );
+ break;
+
+ default:
+ trap_Error( va("Menu_Draw: unknown type %d", itemptr->type) );
+ }
+ }
+#ifndef NDEBUG
+ if( uis.debug ) {
+ int x;
+ int y;
+ int w;
+ int h;
+
+ if( !( itemptr->flags & QMF_INACTIVE ) ) {
+ x = itemptr->left;
+ y = itemptr->top;
+ w = itemptr->right - itemptr->left + 1;
+ h = itemptr->bottom - itemptr->top + 1;
+
+ if (itemptr->flags & QMF_HASMOUSEFOCUS) {
+ UI_DrawRect(x, y, w, h, colorYellow );
+ }
+ else {
+ UI_DrawRect(x, y, w, h, colorWhite );
+ }
+ }
+ }
+#endif
+ }
+
+ itemptr = Menu_ItemAtCursor( menu );
+ if ( itemptr && itemptr->statusbar)
+ itemptr->statusbar( ( void * ) itemptr );
+}
+
+/*
+=================
+Menu_ItemAtCursor
+=================
+*/
+void *Menu_ItemAtCursor( menuframework_s *m )
+{
+ if ( m->cursor < 0 || m->cursor >= m->nitems )
+ return NULL;
+
+ return m->items[m->cursor];
+}
+
+/*
+=================
+Menu_ActivateItem
+=================
+*/
+sfxHandle_t Menu_ActivateItem( menuframework_s *s, menucommon_s* item ) {
+ if ( item->callback ) {
+ item->callback( item, QM_ACTIVATED );
+ if( !( item->flags & QMF_SILENT ) ) {
+ return menu_move_sound;
+ }
+ }
+
+ return 0;
+}
+
+/*
+=================
+Menu_DefaultKey
+=================
+*/
+sfxHandle_t Menu_DefaultKey( menuframework_s *m, int key )
+{
+ sfxHandle_t sound = 0;
+ menucommon_s *item;
+ int cursor_prev;
+
+ // menu system keys
+ switch ( key )
+ {
+ case K_MOUSE2:
+ case K_ESCAPE:
+ UI_PopMenu();
+ return menu_out_sound;
+ }
+
+ if (!m || !m->nitems)
+ return 0;
+
+ // route key stimulus to widget
+ item = Menu_ItemAtCursor( m );
+ if (item && !(item->flags & (QMF_GRAYED|QMF_INACTIVE)))
+ {
+ switch (item->type)
+ {
+ case MTYPE_SPINCONTROL:
+ sound = SpinControl_Key( (menulist_s*)item, key );
+ break;
+
+ case MTYPE_RADIOBUTTON:
+ sound = RadioButton_Key( (menuradiobutton_s*)item, key );
+ break;
+
+ case MTYPE_SLIDER:
+ sound = Slider_Key( (menuslider_s*)item, key );
+ break;
+
+ case MTYPE_SCROLLLIST:
+ sound = ScrollList_Key( (menulist_s*)item, key );
+ break;
+
+ case MTYPE_FIELD:
+ sound = MenuField_Key( (menufield_s*)item, &key );
+ break;
+ }
+
+ if (sound) {
+ // key was handled
+ return sound;
+ }
+ }
+
+ // default handling
+ switch ( key )
+ {
+#ifndef NDEBUG
+ case K_F11:
+ uis.debug ^= 1;
+ break;
+
+ case K_F12:
+ trap_Cmd_ExecuteText(EXEC_APPEND, "screenshot\n");
+ break;
+#endif
+ case K_KP_UPARROW:
+ case K_UPARROW:
+ cursor_prev = m->cursor;
+ m->cursor_prev = m->cursor;
+ m->cursor--;
+ Menu_AdjustCursor( m, -1 );
+ if ( cursor_prev != m->cursor ) {
+ Menu_CursorMoved( m );
+ sound = menu_move_sound;
+ }
+ break;
+
+ case K_TAB:
+ case K_KP_DOWNARROW:
+ case K_DOWNARROW:
+ cursor_prev = m->cursor;
+ m->cursor_prev = m->cursor;
+ m->cursor++;
+ Menu_AdjustCursor( m, 1 );
+ if ( cursor_prev != m->cursor ) {
+ Menu_CursorMoved( m );
+ sound = menu_move_sound;
+ }
+ break;
+
+ case K_MOUSE1:
+ case K_MOUSE3:
+ if (item)
+ if ((item->flags & QMF_HASMOUSEFOCUS) && !(item->flags & (QMF_GRAYED|QMF_INACTIVE)))
+ return (Menu_ActivateItem( m, item ));
+ break;
+
+ case K_JOY1:
+ case K_JOY2:
+ case K_JOY3:
+ case K_JOY4:
+ case K_AUX1:
+ case K_AUX2:
+ case K_AUX3:
+ case K_AUX4:
+ case K_AUX5:
+ case K_AUX6:
+ case K_AUX7:
+ case K_AUX8:
+ case K_AUX9:
+ case K_AUX10:
+ case K_AUX11:
+ case K_AUX12:
+ case K_AUX13:
+ case K_AUX14:
+ case K_AUX15:
+ case K_AUX16:
+ case K_KP_ENTER:
+ case K_ENTER:
+ if (item)
+ if (!(item->flags & (QMF_MOUSEONLY|QMF_GRAYED|QMF_INACTIVE)))
+ return (Menu_ActivateItem( m, item ));
+ break;
+ }
+
+ return sound;
+}
+
+/*
+=================
+Menu_Cache
+=================
+*/
+void Menu_Cache( void )
+{
+ uis.charset = trap_R_RegisterShaderNoMip( "gfx/2d/bigchars" );
+ uis.charsetProp = trap_R_RegisterShaderNoMip( "menu/art/font1_prop.tga" );
+ uis.charsetPropGlow = trap_R_RegisterShaderNoMip( "menu/art/font1_prop_glo.tga" );
+ uis.charsetPropB = trap_R_RegisterShaderNoMip( "menu/art/font2_prop.tga" );
+ uis.cursor = trap_R_RegisterShaderNoMip( "menu/art/3_cursor2" );
+ uis.rb_on = trap_R_RegisterShaderNoMip( "menu/art/switch_on" );
+ uis.rb_off = trap_R_RegisterShaderNoMip( "menu/art/switch_off" );
+
+ uis.whiteShader = trap_R_RegisterShaderNoMip( "white" );
+ if ( uis.glconfig.hardwareType == GLHW_RAGEPRO ) {
+ // the blend effect turns to shit with the normal
+ uis.menuBackShader = trap_R_RegisterShaderNoMip( "menubackRagePro" );
+ } else {
+ uis.menuBackShader = trap_R_RegisterShaderNoMip( "menuback" );
+ }
+ uis.menuBackNoLogoShader = trap_R_RegisterShaderNoMip( "menubacknologo" );
+
+ menu_in_sound = trap_S_RegisterSound( "sound/misc/menu1.wav", qfalse );
+ menu_move_sound = trap_S_RegisterSound( "sound/misc/menu2.wav", qfalse );
+ menu_out_sound = trap_S_RegisterSound( "sound/misc/menu3.wav", qfalse );
+ menu_buzz_sound = trap_S_RegisterSound( "sound/misc/menu4.wav", qfalse );
+ weaponChangeSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse );
+
+ // need a nonzero sound, make an empty sound for this
+ menu_null_sound = -1;
+
+ sliderBar = trap_R_RegisterShaderNoMip( "menu/art/slider2" );
+ sliderButton_0 = trap_R_RegisterShaderNoMip( "menu/art/sliderbutt_0" );
+ sliderButton_1 = trap_R_RegisterShaderNoMip( "menu/art/sliderbutt_1" );
+}
+
diff --git a/code/q3_ui/ui_rankings.c b/code/q3_ui/ui_rankings.c
new file mode 100644
index 0000000..cba8f4b
--- /dev/null
+++ b/code/q3_ui/ui_rankings.c
@@ -0,0 +1,420 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+//
+// ui_rankings.c
+//
+
+#include "ui_local.h"
+
+
+#define RANKINGS_FRAME "menu/art/cut_frame"
+
+#define ID_LOGIN 100
+#define ID_LOGOUT 101
+#define ID_CREATE 102
+#define ID_SPECTATE 103
+#define ID_SETUP 104
+#define ID_LEAVE 105
+
+
+typedef struct
+{
+ menuframework_s menu;
+ menubitmap_s frame;
+ menutext_s login;
+ menutext_s logout;
+ menutext_s create;
+ menutext_s spectate;
+ menutext_s setup;
+ menutext_s leave;
+} rankings_t;
+
+static rankings_t s_rankings;
+
+static menuframework_s s_rankings_menu;
+static menuaction_s s_rankings_login;
+static menuaction_s s_rankings_logout;
+static menuaction_s s_rankings_create;
+static menuaction_s s_rankings_spectate;
+static menuaction_s s_rankings_setup;
+static menuaction_s s_rankings_leave;
+
+
+/*
+===============
+Rankings_DrawText
+===============
+*/
+void Rankings_DrawText( void* self )
+{
+ menufield_s *f;
+ qboolean focus;
+ int style;
+ char *txt;
+ char c;
+ float *color;
+ int basex, x, y;
+
+ f = (menufield_s*)self;
+ basex = f->generic.x;
+ y = f->generic.y + 4;
+ focus = (f->generic.parent->cursor == f->generic.menuPosition);
+
+ style = UI_LEFT|UI_SMALLFONT;
+ color = text_color_normal;
+ if( focus ) {
+ style |= UI_PULSE;
+ color = text_color_highlight;
+ }
+
+ // draw the actual text
+ txt = f->field.buffer;
+ color = g_color_table[ColorIndex(COLOR_WHITE)];
+ x = basex;
+ while ( (c = *txt) != 0 ) {
+ UI_DrawChar( x, y, c, style, color );
+ txt++;
+ x += SMALLCHAR_WIDTH;
+ }
+
+ // draw cursor if we have focus
+ if( focus ) {
+ if ( trap_Key_GetOverstrikeMode() ) {
+ c = 11;
+ } else {
+ c = 10;
+ }
+
+ style &= ~UI_PULSE;
+ style |= UI_BLINK;
+
+ UI_DrawChar( basex + f->field.cursor * SMALLCHAR_WIDTH, y, c, style, color_white );
+ }
+}
+
+/*
+===============
+Rankings_DrawName
+===============
+*/
+void Rankings_DrawName( void* self )
+{
+ menufield_s *f;
+ int length;
+ char* p;
+
+ f = (menufield_s*)self;
+
+ // GRANK_FIXME - enforce valid characters
+ for( p = f->field.buffer; *p != '\0'; p++ )
+ {
+ //if( ispunct(*p) || isspace(*p) )
+ if( !( ( (*p) >= '0' && (*p) <= '9') || Q_isalpha(*p)) )
+ {
+ *p = '\0';
+ }
+ }
+
+ // strip color codes
+ Q_CleanStr( f->field.buffer );
+ length = strlen( f->field.buffer );
+ if( f->field.cursor > length )
+ {
+ f->field.cursor = length;
+ }
+
+ Rankings_DrawText( f );
+}
+
+#if 0 // old version
+/*
+===============
+Rankings_DrawName
+===============
+*/
+void Rankings_DrawName( void* self )
+{
+ menufield_s* f;
+ int length;
+
+ f = (menufield_s*)self;
+
+ // strip color codes
+ Q_CleanStr( f->field.buffer );
+ length = strlen( f->field.buffer );
+ if( f->field.cursor > length )
+ {
+ f->field.cursor = length;
+ }
+
+ // show beginning of long names
+ /*
+ if( Menu_ItemAtCursor( f->generic.parent ) != f )
+ {
+ if( f->field.scroll > 0 )
+ {
+ f->field.cursor = 0;
+ f->field.scroll = 0;
+ }
+ }
+ */
+
+ MenuField_Draw( f );
+}
+#endif
+
+/*
+===============
+Rankings_DrawPassword
+===============
+*/
+void Rankings_DrawPassword( void* self )
+{
+ menufield_s* f;
+ char password[MAX_EDIT_LINE];
+ int length;
+ int i;
+ char* p;
+
+ f = (menufield_s*)self;
+
+ // GRANK_FIXME - enforce valid characters
+ for( p = f->field.buffer; *p != '\0'; p++ )
+ {
+ //if( ispunct(*p) || isspace(*p) )
+ if( !( ( (*p) >= '0' && (*p) <= '9') || Q_isalpha(*p)) )
+ {
+ *p = '\0';
+ }
+ }
+
+ length = strlen( f->field.buffer );
+ if( f->field.cursor > length )
+ {
+ f->field.cursor = length;
+ }
+
+ // save password
+ Q_strncpyz( password, f->field.buffer, sizeof(password) );
+
+ // mask password with *
+ for( i = 0; i < length; i++ )
+ {
+ f->field.buffer[i] = '*';
+ }
+
+ // draw masked password
+ Rankings_DrawText( f );
+ //MenuField_Draw( f );
+
+ // restore password
+ Q_strncpyz( f->field.buffer, password, sizeof(f->field.buffer) );
+}
+
+/*
+===============
+Rankings_MenuEvent
+===============
+*/
+static void Rankings_MenuEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_LOGIN:
+ UI_LoginMenu();
+ break;
+
+ case ID_LOGOUT:
+ // server side masqueraded player logout first
+ trap_CL_UI_RankUserRequestLogout();
+ UI_ForceMenuOff();
+ break;
+
+ case ID_CREATE:
+ UI_SignupMenu();
+ break;
+
+ case ID_SPECTATE:
+ trap_Cmd_ExecuteText( EXEC_APPEND, "cmd rank_spectate\n" );
+ UI_ForceMenuOff();
+ break;
+
+ case ID_SETUP:
+ UI_SetupMenu();
+ break;
+
+ case ID_LEAVE:
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" );
+ UI_ForceMenuOff();
+ break;
+
+ }
+}
+
+
+/*
+===============
+Rankings_MenuInit
+===============
+*/
+void Rankings_MenuInit( void ) {
+ grank_status_t status;
+ int y;
+
+ memset( &s_rankings, 0, sizeof(s_rankings) );
+
+ Rankings_Cache();
+
+ s_rankings.menu.wrapAround = qtrue;
+ s_rankings.menu.fullscreen = qfalse;
+
+ s_rankings.frame.generic.type = MTYPE_BITMAP;
+ s_rankings.frame.generic.flags = QMF_INACTIVE;
+ s_rankings.frame.generic.name = RANKINGS_FRAME;
+ s_rankings.frame.generic.x = 142;
+ s_rankings.frame.generic.y = 118;
+ s_rankings.frame.width = 359;
+ s_rankings.frame.height = 256;
+
+ y = 194;
+
+ s_rankings.login.generic.type = MTYPE_PTEXT;
+ s_rankings.login.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_rankings.login.generic.id = ID_LOGIN;
+ s_rankings.login.generic.callback = Rankings_MenuEvent;
+ s_rankings.login.generic.x = 320;
+ s_rankings.login.generic.y = y;
+ s_rankings.login.string = "LOGIN";
+ s_rankings.login.style = UI_CENTER|UI_SMALLFONT;
+ s_rankings.login.color = colorRed;
+ y += 20;
+
+ s_rankings.logout.generic.type = MTYPE_PTEXT;
+ s_rankings.logout.generic.flags = QMF_HIDDEN|QMF_INACTIVE|QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_rankings.logout.generic.id = ID_LOGOUT;
+ s_rankings.logout.generic.callback = Rankings_MenuEvent;
+ s_rankings.logout.generic.x = 320;
+ s_rankings.logout.generic.y = y;
+ s_rankings.logout.string = "LOGOUT";
+ s_rankings.logout.style = UI_CENTER|UI_SMALLFONT;
+ s_rankings.logout.color = colorRed;
+
+ s_rankings.create.generic.type = MTYPE_PTEXT;
+ s_rankings.create.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_rankings.create.generic.id = ID_CREATE;
+ s_rankings.create.generic.callback = Rankings_MenuEvent;
+ s_rankings.create.generic.x = 320;
+ s_rankings.create.generic.y = y;
+ s_rankings.create.string = "SIGN UP";
+ s_rankings.create.style = UI_CENTER|UI_SMALLFONT;
+ s_rankings.create.color = colorRed;
+ y += 20;
+
+ s_rankings.spectate.generic.type = MTYPE_PTEXT;
+ s_rankings.spectate.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_rankings.spectate.generic.id = ID_SPECTATE;
+ s_rankings.spectate.generic.callback = Rankings_MenuEvent;
+ s_rankings.spectate.generic.x = 320;
+ s_rankings.spectate.generic.y = y;
+ s_rankings.spectate.string = "SPECTATE";
+ s_rankings.spectate.style = UI_CENTER|UI_SMALLFONT;
+ s_rankings.spectate.color = colorRed;
+ y += 20;
+
+ s_rankings.setup.generic.type = MTYPE_PTEXT;
+ s_rankings.setup.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_rankings.setup.generic.id = ID_SETUP;
+ s_rankings.setup.generic.callback = Rankings_MenuEvent;
+ s_rankings.setup.generic.x = 320;
+ s_rankings.setup.generic.y = y;
+ s_rankings.setup.string = "SETUP";
+ s_rankings.setup.style = UI_CENTER|UI_SMALLFONT;
+ s_rankings.setup.color = colorRed;
+ y += 20;
+
+ s_rankings.leave.generic.type = MTYPE_PTEXT;
+ s_rankings.leave.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_rankings.leave.generic.id = ID_LEAVE;
+ s_rankings.leave.generic.callback = Rankings_MenuEvent;
+ s_rankings.leave.generic.x = 320;
+ s_rankings.leave.generic.y = y;
+ s_rankings.leave.string = "LEAVE ARENA";
+ s_rankings.leave.style = UI_CENTER|UI_SMALLFONT;
+ s_rankings.leave.color = colorRed;
+ y += 20;
+
+ status = (grank_status_t)trap_Cvar_VariableValue("client_status");
+ if( (status != QGR_STATUS_NEW) && (status != QGR_STATUS_SPECTATOR) )
+ {
+ s_rankings.login.generic.flags |= QMF_HIDDEN | QMF_INACTIVE;
+ s_rankings.create.generic.flags |= QMF_HIDDEN | QMF_INACTIVE;
+ s_rankings.spectate.generic.flags |= QMF_HIDDEN | QMF_INACTIVE;
+
+ s_rankings.logout.generic.flags &= ~(QMF_HIDDEN | QMF_INACTIVE);
+ }
+
+ if ( (status == QGR_STATUS_VALIDATING) ||
+ (status == QGR_STATUS_PENDING) ||
+ (status == QGR_STATUS_LEAVING) )
+ {
+ s_rankings.login.generic.flags |= QMF_GRAYED;
+ s_rankings.create.generic.flags |= QMF_GRAYED;
+ s_rankings.logout.generic.flags |= QMF_GRAYED;
+ }
+
+ //GRank FIXME -- don't need setup option any more
+ s_rankings.setup.generic.flags |= QMF_HIDDEN | QMF_INACTIVE;
+
+ Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.frame );
+ Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.login );
+ Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.logout );
+ Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.create );
+ Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.spectate );
+ Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.setup );
+ Menu_AddItem( &s_rankings.menu, (void*) &s_rankings.leave );
+}
+
+
+/*
+===============
+Rankings_Cache
+===============
+*/
+void Rankings_Cache( void ) {
+ trap_R_RegisterShaderNoMip( RANKINGS_FRAME );
+}
+
+
+/*
+===============
+UI_RankingsMenu
+===============
+*/
+void UI_RankingsMenu( void ) {
+ Rankings_MenuInit();
+ UI_PushMenu ( &s_rankings.menu );
+}
+
+
diff --git a/code/q3_ui/ui_rankstatus.c b/code/q3_ui/ui_rankstatus.c
new file mode 100644
index 0000000..c34bb0d
--- /dev/null
+++ b/code/q3_ui/ui_rankstatus.c
@@ -0,0 +1,209 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+//
+// ui_rankstatus.c
+//
+
+#include "ui_local.h"
+
+
+#define RANKSTATUS_FRAME "menu/art/cut_frame"
+
+#define ID_MESSAGE 100
+#define ID_OK 101
+
+
+typedef struct
+{
+ menuframework_s menu;
+ menubitmap_s frame;
+ menutext_s message;
+ menutext_s ok;
+} rankstatus_t;
+
+static rankstatus_t s_rankstatus;
+
+static menuframework_s s_rankstatus_menu;
+static menuaction_s s_rankstatus_ok;
+
+static grank_status_t s_status = 0;
+static char* s_rankstatus_message = NULL;
+
+static vec4_t s_rankingstatus_color_prompt = {1.00, 0.43, 0.00, 1.00};
+
+/*
+===============
+RankStatus_MenuEvent
+===============
+*/
+static void RankStatus_MenuEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_OK:
+ UI_PopMenu();
+
+ switch( s_status )
+ {
+ case QGR_STATUS_NO_USER:
+ UI_RankingsMenu();
+ break;
+ case QGR_STATUS_BAD_PASSWORD:
+ UI_RankingsMenu();
+ UI_LoginMenu();
+ break;
+ case QGR_STATUS_USER_EXISTS:
+ UI_RankingsMenu();
+ UI_SignupMenu();
+ break;
+ case QGR_STATUS_NO_MEMBERSHIP:
+ UI_RankingsMenu();
+ break;
+ case QGR_STATUS_TIMEOUT:
+ UI_RankingsMenu();
+ break;
+ case QGR_STATUS_INVALIDUSER:
+ UI_RankingsMenu();
+ break;
+ case QGR_STATUS_ERROR:
+ UI_RankingsMenu();
+ break;
+ default:
+ break;
+ }
+
+ break;
+ }
+}
+
+
+/*
+===============
+RankStatus_MenuInit
+===============
+*/
+void RankStatus_MenuInit( void ) {
+ int y;
+
+ memset( &s_rankstatus, 0, sizeof(s_rankstatus) );
+
+ RankStatus_Cache();
+
+ s_rankstatus.menu.wrapAround = qtrue;
+ s_rankstatus.menu.fullscreen = qfalse;
+
+ s_rankstatus.frame.generic.type = MTYPE_BITMAP;
+ s_rankstatus.frame.generic.flags = QMF_INACTIVE;
+ s_rankstatus.frame.generic.name = RANKSTATUS_FRAME;
+ s_rankstatus.frame.generic.x = 142; //320-233;
+ s_rankstatus.frame.generic.y = 118; //240-166;
+ s_rankstatus.frame.width = 359; //466;
+ s_rankstatus.frame.height = 256; //332;
+
+ y = 214;
+
+ s_rankstatus.message.generic.type = MTYPE_PTEXT;
+ s_rankstatus.message.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
+ s_rankstatus.message.generic.id = ID_MESSAGE;
+ s_rankstatus.message.generic.x = 320;
+ s_rankstatus.message.generic.y = y;
+ s_rankstatus.message.string = s_rankstatus_message;
+ s_rankstatus.message.style = UI_CENTER|UI_SMALLFONT;
+ s_rankstatus.message.color = s_rankingstatus_color_prompt;
+ y += 40;
+
+ s_rankstatus.ok.generic.type = MTYPE_PTEXT;
+ s_rankstatus.ok.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_rankstatus.ok.generic.id = ID_OK;
+ s_rankstatus.ok.generic.callback = RankStatus_MenuEvent;
+ s_rankstatus.ok.generic.x = 320;
+ s_rankstatus.ok.generic.y = y;
+ s_rankstatus.ok.string = "OK";
+ s_rankstatus.ok.style = UI_CENTER|UI_SMALLFONT;
+ s_rankstatus.ok.color = colorRed;
+
+ Menu_AddItem( &s_rankstatus.menu, (void*) &s_rankstatus.frame );
+ Menu_AddItem( &s_rankstatus.menu, (void*) &s_rankstatus.message );
+ Menu_AddItem( &s_rankstatus.menu, (void*) &s_rankstatus.ok );
+}
+
+
+/*
+===============
+RankStatus_Cache
+===============
+*/
+void RankStatus_Cache( void ) {
+ trap_R_RegisterShaderNoMip( RANKSTATUS_FRAME );
+}
+
+
+/*
+===============
+UI_RankStatusMenu
+===============
+*/
+void UI_RankStatusMenu( void ) {
+
+ s_status = (grank_status_t)trap_Cvar_VariableValue("client_status");
+
+ switch( s_status )
+ {
+ case QGR_STATUS_NEW:
+ return;
+ case QGR_STATUS_PENDING:
+ // GRANK_FIXME
+ return;
+ case QGR_STATUS_NO_USER:
+ // GRANK_FIXME - get this when user exists
+ s_rankstatus_message = "Username unavailable";
+ break;
+ case QGR_STATUS_BAD_PASSWORD:
+ s_rankstatus_message = "Invalid password";
+ break;
+ case QGR_STATUS_TIMEOUT:
+ s_rankstatus_message = "Timed out";
+ break;
+ case QGR_STATUS_NO_MEMBERSHIP:
+ s_rankstatus_message = "No membership";
+ break;
+ case QGR_STATUS_INVALIDUSER:
+ s_rankstatus_message = "Validation failed";
+ break;
+ case QGR_STATUS_ERROR:
+ s_rankstatus_message = "Error";
+ break;
+ case QGR_STATUS_SPECTATOR:
+ case QGR_STATUS_ACTIVE:
+ UI_ForceMenuOff();
+ return;
+ default:
+ return;
+ }
+ RankStatus_MenuInit();
+ trap_CL_UI_RankUserReset();
+ UI_PushMenu ( &s_rankstatus.menu );
+}
+
diff --git a/code/q3_ui/ui_removebots.c b/code/q3_ui/ui_removebots.c
new file mode 100644
index 0000000..f1c5239
--- /dev/null
+++ b/code/q3_ui/ui_removebots.c
@@ -0,0 +1,342 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+REMOVE BOTS MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ART_BACKGROUND "menu/art/addbotframe"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_DELETE0 "menu/art/delete_0"
+#define ART_DELETE1 "menu/art/delete_1"
+#define ART_ARROWS "menu/art/arrows_vert_0"
+#define ART_ARROWUP "menu/art/arrows_vert_top"
+#define ART_ARROWDOWN "menu/art/arrows_vert_bot"
+
+#define ID_UP 10
+#define ID_DOWN 11
+#define ID_DELETE 12
+#define ID_BACK 13
+#define ID_BOTNAME0 20
+#define ID_BOTNAME1 21
+#define ID_BOTNAME2 22
+#define ID_BOTNAME3 23
+#define ID_BOTNAME4 24
+#define ID_BOTNAME5 25
+#define ID_BOTNAME6 26
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s background;
+
+ menubitmap_s arrows;
+ menubitmap_s up;
+ menubitmap_s down;
+
+ menutext_s bots[7];
+
+ menubitmap_s delete;
+ menubitmap_s back;
+
+ int numBots;
+ int baseBotNum;
+ int selectedBotNum;
+ char botnames[7][32];
+ int botClientNums[MAX_BOTS];
+} removeBotsMenuInfo_t;
+
+static removeBotsMenuInfo_t removeBotsMenuInfo;
+
+
+/*
+=================
+UI_RemoveBotsMenu_SetBotNames
+=================
+*/
+static void UI_RemoveBotsMenu_SetBotNames( void ) {
+ int n;
+ char info[MAX_INFO_STRING];
+
+ for ( n = 0; (n < 7) && (removeBotsMenuInfo.baseBotNum + n < removeBotsMenuInfo.numBots); n++ ) {
+ trap_GetConfigString( CS_PLAYERS + removeBotsMenuInfo.botClientNums[removeBotsMenuInfo.baseBotNum + n], info, MAX_INFO_STRING );
+ Q_strncpyz( removeBotsMenuInfo.botnames[n], Info_ValueForKey( info, "n" ), sizeof(removeBotsMenuInfo.botnames[n]) );
+ Q_CleanStr( removeBotsMenuInfo.botnames[n] );
+ }
+
+}
+
+
+/*
+=================
+UI_RemoveBotsMenu_DeleteEvent
+=================
+*/
+static void UI_RemoveBotsMenu_DeleteEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va("clientkick %i\n", removeBotsMenuInfo.botClientNums[removeBotsMenuInfo.baseBotNum + removeBotsMenuInfo.selectedBotNum]) );
+}
+
+
+/*
+=================
+UI_RemoveBotsMenu_BotEvent
+=================
+*/
+static void UI_RemoveBotsMenu_BotEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ removeBotsMenuInfo.bots[removeBotsMenuInfo.selectedBotNum].color = color_orange;
+ removeBotsMenuInfo.selectedBotNum = ((menucommon_s*)ptr)->id - ID_BOTNAME0;
+ removeBotsMenuInfo.bots[removeBotsMenuInfo.selectedBotNum].color = color_white;
+}
+
+
+/*
+=================
+UI_RemoveAddBotsMenu_BackEvent
+=================
+*/
+static void UI_RemoveBotsMenu_BackEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+ UI_PopMenu();
+}
+
+
+/*
+=================
+UI_RemoveBotsMenu_UpEvent
+=================
+*/
+static void UI_RemoveBotsMenu_UpEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ if( removeBotsMenuInfo.baseBotNum > 0 ) {
+ removeBotsMenuInfo.baseBotNum--;
+ UI_RemoveBotsMenu_SetBotNames();
+ }
+}
+
+
+/*
+=================
+UI_RemoveBotsMenu_DownEvent
+=================
+*/
+static void UI_RemoveBotsMenu_DownEvent( void* ptr, int event ) {
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ if( removeBotsMenuInfo.baseBotNum + 7 < removeBotsMenuInfo.numBots ) {
+ removeBotsMenuInfo.baseBotNum++;
+ UI_RemoveBotsMenu_SetBotNames();
+ }
+}
+
+
+/*
+=================
+UI_RemoveBotsMenu_GetBots
+=================
+*/
+static void UI_RemoveBotsMenu_GetBots( void ) {
+ int numPlayers;
+ int isBot;
+ int n;
+ char info[MAX_INFO_STRING];
+
+ trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
+ numPlayers = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
+ removeBotsMenuInfo.numBots = 0;
+
+ for( n = 0; n < numPlayers; n++ ) {
+ trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING );
+
+ isBot = atoi( Info_ValueForKey( info, "skill" ) );
+ if( !isBot ) {
+ continue;
+ }
+
+ removeBotsMenuInfo.botClientNums[removeBotsMenuInfo.numBots] = n;
+ removeBotsMenuInfo.numBots++;
+ }
+}
+
+
+/*
+=================
+UI_RemoveBots_Cache
+=================
+*/
+void UI_RemoveBots_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACKGROUND );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_DELETE0 );
+ trap_R_RegisterShaderNoMip( ART_DELETE1 );
+}
+
+
+/*
+=================
+UI_RemoveBotsMenu_Init
+=================
+*/
+static void UI_RemoveBotsMenu_Init( void ) {
+ int n;
+ int count;
+ int y;
+
+ memset( &removeBotsMenuInfo, 0 ,sizeof(removeBotsMenuInfo) );
+ removeBotsMenuInfo.menu.fullscreen = qfalse;
+ removeBotsMenuInfo.menu.wrapAround = qtrue;
+
+ UI_RemoveBots_Cache();
+
+ UI_RemoveBotsMenu_GetBots();
+ UI_RemoveBotsMenu_SetBotNames();
+ count = removeBotsMenuInfo.numBots < 7 ? removeBotsMenuInfo.numBots : 7;
+
+ removeBotsMenuInfo.banner.generic.type = MTYPE_BTEXT;
+ removeBotsMenuInfo.banner.generic.x = 320;
+ removeBotsMenuInfo.banner.generic.y = 16;
+ removeBotsMenuInfo.banner.string = "REMOVE BOTS";
+ removeBotsMenuInfo.banner.color = color_white;
+ removeBotsMenuInfo.banner.style = UI_CENTER;
+
+ removeBotsMenuInfo.background.generic.type = MTYPE_BITMAP;
+ removeBotsMenuInfo.background.generic.name = ART_BACKGROUND;
+ removeBotsMenuInfo.background.generic.flags = QMF_INACTIVE;
+ removeBotsMenuInfo.background.generic.x = 320-233;
+ removeBotsMenuInfo.background.generic.y = 240-166;
+ removeBotsMenuInfo.background.width = 466;
+ removeBotsMenuInfo.background.height = 332;
+
+ removeBotsMenuInfo.arrows.generic.type = MTYPE_BITMAP;
+ removeBotsMenuInfo.arrows.generic.name = ART_ARROWS;
+ removeBotsMenuInfo.arrows.generic.flags = QMF_INACTIVE;
+ removeBotsMenuInfo.arrows.generic.x = 200;
+ removeBotsMenuInfo.arrows.generic.y = 128;
+ removeBotsMenuInfo.arrows.width = 64;
+ removeBotsMenuInfo.arrows.height = 128;
+
+ removeBotsMenuInfo.up.generic.type = MTYPE_BITMAP;
+ removeBotsMenuInfo.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ removeBotsMenuInfo.up.generic.x = 200;
+ removeBotsMenuInfo.up.generic.y = 128;
+ removeBotsMenuInfo.up.generic.id = ID_UP;
+ removeBotsMenuInfo.up.generic.callback = UI_RemoveBotsMenu_UpEvent;
+ removeBotsMenuInfo.up.width = 64;
+ removeBotsMenuInfo.up.height = 64;
+ removeBotsMenuInfo.up.focuspic = ART_ARROWUP;
+
+ removeBotsMenuInfo.down.generic.type = MTYPE_BITMAP;
+ removeBotsMenuInfo.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ removeBotsMenuInfo.down.generic.x = 200;
+ removeBotsMenuInfo.down.generic.y = 128+64;
+ removeBotsMenuInfo.down.generic.id = ID_DOWN;
+ removeBotsMenuInfo.down.generic.callback = UI_RemoveBotsMenu_DownEvent;
+ removeBotsMenuInfo.down.width = 64;
+ removeBotsMenuInfo.down.height = 64;
+ removeBotsMenuInfo.down.focuspic = ART_ARROWDOWN;
+
+ for( n = 0, y = 120; n < count; n++, y += 20 ) {
+ removeBotsMenuInfo.bots[n].generic.type = MTYPE_PTEXT;
+ removeBotsMenuInfo.bots[n].generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ removeBotsMenuInfo.bots[n].generic.id = ID_BOTNAME0 + n;
+ removeBotsMenuInfo.bots[n].generic.x = 320 - 56;
+ removeBotsMenuInfo.bots[n].generic.y = y;
+ removeBotsMenuInfo.bots[n].generic.callback = UI_RemoveBotsMenu_BotEvent;
+ removeBotsMenuInfo.bots[n].string = removeBotsMenuInfo.botnames[n];
+ removeBotsMenuInfo.bots[n].color = color_orange;
+ removeBotsMenuInfo.bots[n].style = UI_LEFT|UI_SMALLFONT;
+ }
+
+ removeBotsMenuInfo.delete.generic.type = MTYPE_BITMAP;
+ removeBotsMenuInfo.delete.generic.name = ART_DELETE0;
+ removeBotsMenuInfo.delete.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ removeBotsMenuInfo.delete.generic.id = ID_DELETE;
+ removeBotsMenuInfo.delete.generic.callback = UI_RemoveBotsMenu_DeleteEvent;
+ removeBotsMenuInfo.delete.generic.x = 320+128-128;
+ removeBotsMenuInfo.delete.generic.y = 256+128-64;
+ removeBotsMenuInfo.delete.width = 128;
+ removeBotsMenuInfo.delete.height = 64;
+ removeBotsMenuInfo.delete.focuspic = ART_DELETE1;
+
+ removeBotsMenuInfo.back.generic.type = MTYPE_BITMAP;
+ removeBotsMenuInfo.back.generic.name = ART_BACK0;
+ removeBotsMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ removeBotsMenuInfo.back.generic.id = ID_BACK;
+ removeBotsMenuInfo.back.generic.callback = UI_RemoveBotsMenu_BackEvent;
+ removeBotsMenuInfo.back.generic.x = 320-128;
+ removeBotsMenuInfo.back.generic.y = 256+128-64;
+ removeBotsMenuInfo.back.width = 128;
+ removeBotsMenuInfo.back.height = 64;
+ removeBotsMenuInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.background );
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.banner );
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.arrows );
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.up );
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.down );
+ for( n = 0; n < count; n++ ) {
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.bots[n] );
+ }
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.delete );
+ Menu_AddItem( &removeBotsMenuInfo.menu, &removeBotsMenuInfo.back );
+
+ removeBotsMenuInfo.baseBotNum = 0;
+ removeBotsMenuInfo.selectedBotNum = 0;
+ removeBotsMenuInfo.bots[0].color = color_white;
+}
+
+
+/*
+=================
+UI_RemoveBotsMenu
+=================
+*/
+void UI_RemoveBotsMenu( void ) {
+ UI_RemoveBotsMenu_Init();
+ UI_PushMenu( &removeBotsMenuInfo.menu );
+}
diff --git a/code/q3_ui/ui_saveconfig.c b/code/q3_ui/ui_saveconfig.c
new file mode 100644
index 0000000..c9f0bc2
--- /dev/null
+++ b/code/q3_ui/ui_saveconfig.c
@@ -0,0 +1,212 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=============================================================================
+
+SAVE CONFIG MENU
+
+=============================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_SAVE0 "menu/art/save_0"
+#define ART_SAVE1 "menu/art/save_1"
+#define ART_BACKGROUND "menu/art/cut_frame"
+
+#define ID_NAME 10
+#define ID_BACK 11
+#define ID_SAVE 12
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s background;
+ menufield_s savename;
+ menubitmap_s back;
+ menubitmap_s save;
+} saveConfig_t;
+
+static saveConfig_t saveConfig;
+
+
+/*
+===============
+UI_SaveConfigMenu_BackEvent
+===============
+*/
+static void UI_SaveConfigMenu_BackEvent( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ UI_PopMenu();
+}
+
+
+/*
+===============
+UI_SaveConfigMenu_SaveEvent
+===============
+*/
+static void UI_SaveConfigMenu_SaveEvent( void *ptr, int event ) {
+ char configname[MAX_QPATH];
+
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ if( !saveConfig.savename.field.buffer[0] ) {
+ return;
+ }
+
+ COM_StripExtension(saveConfig.savename.field.buffer, configname, sizeof(configname));
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "writeconfig %s.cfg\n", configname ) );
+ UI_PopMenu();
+}
+
+
+/*
+===============
+UI_SaveConfigMenu_SavenameDraw
+===============
+*/
+static void UI_SaveConfigMenu_SavenameDraw( void *self ) {
+ menufield_s *f;
+ int style;
+ float *color;
+
+ f = (menufield_s *)self;
+
+ if( f == Menu_ItemAtCursor( &saveConfig.menu ) ) {
+ style = UI_LEFT|UI_PULSE|UI_SMALLFONT;
+ color = text_color_highlight;
+ }
+ else {
+ style = UI_LEFT|UI_SMALLFONT;
+ color = colorRed;
+ }
+
+ UI_DrawProportionalString( 320, 192, "Enter filename:", UI_CENTER|UI_SMALLFONT, color_orange );
+ UI_FillRect( f->generic.x, f->generic.y, f->field.widthInChars*SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, colorBlack );
+ MField_Draw( &f->field, f->generic.x, f->generic.y, style, color );
+}
+
+
+/*
+=================
+UI_SaveConfigMenu_Init
+=================
+*/
+static void UI_SaveConfigMenu_Init( void ) {
+ memset( &saveConfig, 0, sizeof(saveConfig) );
+
+ UI_SaveConfigMenu_Cache();
+ saveConfig.menu.wrapAround = qtrue;
+ saveConfig.menu.fullscreen = qtrue;
+
+ saveConfig.banner.generic.type = MTYPE_BTEXT;
+ saveConfig.banner.generic.x = 320;
+ saveConfig.banner.generic.y = 16;
+ saveConfig.banner.string = "SAVE CONFIG";
+ saveConfig.banner.color = color_white;
+ saveConfig.banner.style = UI_CENTER;
+
+ saveConfig.background.generic.type = MTYPE_BITMAP;
+ saveConfig.background.generic.name = ART_BACKGROUND;
+ saveConfig.background.generic.flags = QMF_INACTIVE;
+ saveConfig.background.generic.x = 142;
+ saveConfig.background.generic.y = 118;
+ saveConfig.background.width = 359;
+ saveConfig.background.height = 256;
+
+ saveConfig.savename.generic.type = MTYPE_FIELD;
+ saveConfig.savename.generic.flags = QMF_NODEFAULTINIT|QMF_UPPERCASE;
+ saveConfig.savename.generic.ownerdraw = UI_SaveConfigMenu_SavenameDraw;
+ saveConfig.savename.field.widthInChars = 20;
+ saveConfig.savename.field.maxchars = 20;
+ saveConfig.savename.generic.x = 240;
+ saveConfig.savename.generic.y = 155+72;
+ saveConfig.savename.generic.left = 240;
+ saveConfig.savename.generic.top = 155+72;
+ saveConfig.savename.generic.right = 233 + 20*SMALLCHAR_WIDTH;
+ saveConfig.savename.generic.bottom = 155+72 + SMALLCHAR_HEIGHT+2;
+
+ saveConfig.back.generic.type = MTYPE_BITMAP;
+ saveConfig.back.generic.name = ART_BACK0;
+ saveConfig.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ saveConfig.back.generic.id = ID_BACK;
+ saveConfig.back.generic.callback = UI_SaveConfigMenu_BackEvent;
+ saveConfig.back.generic.x = 0;
+ saveConfig.back.generic.y = 480-64;
+ saveConfig.back.width = 128;
+ saveConfig.back.height = 64;
+ saveConfig.back.focuspic = ART_BACK1;
+
+ saveConfig.save.generic.type = MTYPE_BITMAP;
+ saveConfig.save.generic.name = ART_SAVE0;
+ saveConfig.save.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ saveConfig.save.generic.id = ID_SAVE;
+ saveConfig.save.generic.callback = UI_SaveConfigMenu_SaveEvent;
+ saveConfig.save.generic.x = 640;
+ saveConfig.save.generic.y = 480-64;
+ saveConfig.save.width = 128;
+ saveConfig.save.height = 64;
+ saveConfig.save.focuspic = ART_SAVE1;
+
+ Menu_AddItem( &saveConfig.menu, &saveConfig.banner );
+ Menu_AddItem( &saveConfig.menu, &saveConfig.background );
+ Menu_AddItem( &saveConfig.menu, &saveConfig.savename );
+ Menu_AddItem( &saveConfig.menu, &saveConfig.back );
+ Menu_AddItem( &saveConfig.menu, &saveConfig.save );
+}
+
+
+/*
+=================
+UI_SaveConfigMenu_Cache
+=================
+*/
+void UI_SaveConfigMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_SAVE0 );
+ trap_R_RegisterShaderNoMip( ART_SAVE1 );
+ trap_R_RegisterShaderNoMip( ART_BACKGROUND );
+}
+
+
+/*
+===============
+UI_SaveConfigMenu
+===============
+*/
+void UI_SaveConfigMenu( void ) {
+ UI_SaveConfigMenu_Init();
+ UI_PushMenu( &saveConfig.menu );
+}
diff --git a/code/q3_ui/ui_serverinfo.c b/code/q3_ui/ui_serverinfo.c
new file mode 100644
index 0000000..8cde1cc
--- /dev/null
+++ b/code/q3_ui/ui_serverinfo.c
@@ -0,0 +1,273 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+#define SERVERINFO_FRAMEL "menu/art/frame2_l"
+#define SERVERINFO_FRAMER "menu/art/frame1_r"
+#define SERVERINFO_BACK0 "menu/art/back_0"
+#define SERVERINFO_BACK1 "menu/art/back_1"
+
+static char* serverinfo_artlist[] =
+{
+ SERVERINFO_FRAMEL,
+ SERVERINFO_FRAMER,
+ SERVERINFO_BACK0,
+ SERVERINFO_BACK1,
+ NULL
+};
+
+#define ID_ADD 100
+#define ID_BACK 101
+
+typedef struct
+{
+ menuframework_s menu;
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menubitmap_s back;
+ menutext_s add;
+ char info[MAX_INFO_STRING];
+ int numlines;
+} serverinfo_t;
+
+static serverinfo_t s_serverinfo;
+
+
+/*
+=================
+Favorites_Add
+
+Add current server to favorites
+=================
+*/
+void Favorites_Add( void )
+{
+ char adrstr[128];
+ char serverbuff[128];
+ int i;
+ int best;
+
+ trap_Cvar_VariableStringBuffer( "cl_currentServerAddress", serverbuff, sizeof(serverbuff) );
+ if (!serverbuff[0])
+ return;
+
+ best = 0;
+ for (i=0; i<MAX_FAVORITESERVERS; i++)
+ {
+ trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, sizeof(adrstr) );
+ if (!Q_stricmp(serverbuff,adrstr))
+ {
+ // already in list
+ return;
+ }
+
+ // use first empty or non-numeric available slot
+ if ((adrstr[0] < '0' || adrstr[0] > '9' ) && !best)
+ best = i+1;
+ }
+
+ if (best)
+ trap_Cvar_Set( va("server%d",best), serverbuff);
+}
+
+
+/*
+=================
+ServerInfo_Event
+=================
+*/
+static void ServerInfo_Event( void* ptr, int event )
+{
+ switch (((menucommon_s*)ptr)->id)
+ {
+ case ID_ADD:
+ if (event != QM_ACTIVATED)
+ break;
+
+ Favorites_Add();
+ UI_PopMenu();
+ break;
+
+ case ID_BACK:
+ if (event != QM_ACTIVATED)
+ break;
+
+ UI_PopMenu();
+ break;
+ }
+}
+
+/*
+=================
+ServerInfo_MenuDraw
+=================
+*/
+static void ServerInfo_MenuDraw( void )
+{
+ const char *s;
+ char key[MAX_INFO_KEY];
+ char value[MAX_INFO_VALUE];
+ int i = 0, y;
+
+ y = SCREEN_HEIGHT/2 - s_serverinfo.numlines*(SMALLCHAR_HEIGHT)/2 - 20;
+ s = s_serverinfo.info;
+ while ( s && i < s_serverinfo.numlines ) {
+ Info_NextPair( &s, key, value );
+ if ( !key[0] ) {
+ break;
+ }
+
+ Q_strcat( key, MAX_INFO_KEY, ":" );
+
+ UI_DrawString(SCREEN_WIDTH*0.50 - 8,y,key,UI_RIGHT|UI_SMALLFONT,color_red);
+ UI_DrawString(SCREEN_WIDTH*0.50 + 8,y,value,UI_LEFT|UI_SMALLFONT,text_color_normal);
+
+ y += SMALLCHAR_HEIGHT;
+ i++;
+ }
+
+ Menu_Draw( &s_serverinfo.menu );
+}
+
+/*
+=================
+ServerInfo_MenuKey
+=================
+*/
+static sfxHandle_t ServerInfo_MenuKey( int key )
+{
+ return ( Menu_DefaultKey( &s_serverinfo.menu, key ) );
+}
+
+/*
+=================
+ServerInfo_Cache
+=================
+*/
+void ServerInfo_Cache( void )
+{
+ int i;
+
+ // touch all our pics
+ for (i=0; ;i++)
+ {
+ if (!serverinfo_artlist[i])
+ break;
+ trap_R_RegisterShaderNoMip(serverinfo_artlist[i]);
+ }
+}
+
+/*
+=================
+UI_ServerInfoMenu
+=================
+*/
+void UI_ServerInfoMenu( void )
+{
+ const char *s;
+ char key[MAX_INFO_KEY];
+ char value[MAX_INFO_VALUE];
+
+ // zero set all our globals
+ memset( &s_serverinfo, 0 ,sizeof(serverinfo_t) );
+
+ ServerInfo_Cache();
+
+ s_serverinfo.menu.draw = ServerInfo_MenuDraw;
+ s_serverinfo.menu.key = ServerInfo_MenuKey;
+ s_serverinfo.menu.wrapAround = qtrue;
+ s_serverinfo.menu.fullscreen = qtrue;
+
+ s_serverinfo.banner.generic.type = MTYPE_BTEXT;
+ s_serverinfo.banner.generic.x = 320;
+ s_serverinfo.banner.generic.y = 16;
+ s_serverinfo.banner.string = "SERVER INFO";
+ s_serverinfo.banner.color = color_white;
+ s_serverinfo.banner.style = UI_CENTER;
+
+ s_serverinfo.framel.generic.type = MTYPE_BITMAP;
+ s_serverinfo.framel.generic.name = SERVERINFO_FRAMEL;
+ s_serverinfo.framel.generic.flags = QMF_INACTIVE;
+ s_serverinfo.framel.generic.x = 0;
+ s_serverinfo.framel.generic.y = 78;
+ s_serverinfo.framel.width = 256;
+ s_serverinfo.framel.height = 329;
+
+ s_serverinfo.framer.generic.type = MTYPE_BITMAP;
+ s_serverinfo.framer.generic.name = SERVERINFO_FRAMER;
+ s_serverinfo.framer.generic.flags = QMF_INACTIVE;
+ s_serverinfo.framer.generic.x = 376;
+ s_serverinfo.framer.generic.y = 76;
+ s_serverinfo.framer.width = 256;
+ s_serverinfo.framer.height = 334;
+
+ s_serverinfo.add.generic.type = MTYPE_PTEXT;
+ s_serverinfo.add.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_serverinfo.add.generic.callback = ServerInfo_Event;
+ s_serverinfo.add.generic.id = ID_ADD;
+ s_serverinfo.add.generic.x = 320;
+ s_serverinfo.add.generic.y = 371;
+ s_serverinfo.add.string = "ADD TO FAVORITES";
+ s_serverinfo.add.style = UI_CENTER|UI_SMALLFONT;
+ s_serverinfo.add.color = color_red;
+ if( trap_Cvar_VariableValue( "sv_running" ) ) {
+ s_serverinfo.add.generic.flags |= QMF_GRAYED;
+ }
+
+ s_serverinfo.back.generic.type = MTYPE_BITMAP;
+ s_serverinfo.back.generic.name = SERVERINFO_BACK0;
+ s_serverinfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_serverinfo.back.generic.callback = ServerInfo_Event;
+ s_serverinfo.back.generic.id = ID_BACK;
+ s_serverinfo.back.generic.x = 0;
+ s_serverinfo.back.generic.y = 480-64;
+ s_serverinfo.back.width = 128;
+ s_serverinfo.back.height = 64;
+ s_serverinfo.back.focuspic = SERVERINFO_BACK1;
+
+ trap_GetConfigString( CS_SERVERINFO, s_serverinfo.info, MAX_INFO_STRING );
+
+ s_serverinfo.numlines = 0;
+ s = s_serverinfo.info;
+ while ( s ) {
+ Info_NextPair( &s, key, value );
+ if ( !key[0] ) {
+ break;
+ }
+ s_serverinfo.numlines++;
+ }
+
+ if (s_serverinfo.numlines > 16)
+ s_serverinfo.numlines = 16;
+
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.banner );
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.framel );
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.framer );
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.add );
+ Menu_AddItem( &s_serverinfo.menu, (void*) &s_serverinfo.back );
+
+ UI_PushMenu( &s_serverinfo.menu );
+}
+
+
diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c
new file mode 100644
index 0000000..6c7cf16
--- /dev/null
+++ b/code/q3_ui/ui_servers2.c
@@ -0,0 +1,1643 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+MULTIPLAYER MENU (SERVER BROWSER)
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define MAX_GLOBALSERVERS 128
+#define MAX_PINGREQUESTS 32
+#define MAX_ADDRESSLENGTH 64
+#define MAX_HOSTNAMELENGTH 22
+#define MAX_MAPNAMELENGTH 16
+#define MAX_LISTBOXITEMS 128
+#define MAX_LOCALSERVERS 128
+#define MAX_STATUSLENGTH 64
+#define MAX_LEAGUELENGTH 28
+#define MAX_LISTBOXWIDTH 68
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_CREATE0 "menu/art/create_0"
+#define ART_CREATE1 "menu/art/create_1"
+#define ART_SPECIFY0 "menu/art/specify_0"
+#define ART_SPECIFY1 "menu/art/specify_1"
+#define ART_REFRESH0 "menu/art/refresh_0"
+#define ART_REFRESH1 "menu/art/refresh_1"
+#define ART_CONNECT0 "menu/art/fight_0"
+#define ART_CONNECT1 "menu/art/fight_1"
+#define ART_ARROWS0 "menu/art/arrows_vert_0"
+#define ART_ARROWS_UP "menu/art/arrows_vert_top"
+#define ART_ARROWS_DOWN "menu/art/arrows_vert_bot"
+#define ART_UNKNOWNMAP "menu/art/unknownmap"
+#define ART_REMOVE0 "menu/art/delete_0"
+#define ART_REMOVE1 "menu/art/delete_1"
+#define ART_PUNKBUSTER "menu/art/pblogo"
+
+#define ID_MASTER 10
+#define ID_GAMETYPE 11
+#define ID_SORTKEY 12
+#define ID_SHOW_FULL 13
+#define ID_SHOW_EMPTY 14
+#define ID_LIST 15
+#define ID_SCROLL_UP 16
+#define ID_SCROLL_DOWN 17
+#define ID_BACK 18
+#define ID_REFRESH 19
+#define ID_SPECIFY 20
+#define ID_CREATE 21
+#define ID_CONNECT 22
+#define ID_REMOVE 23
+#define ID_PUNKBUSTER 24
+
+#define GR_LOGO 30
+#define GR_LETTERS 31
+
+#define UIAS_LOCAL 0
+#define UIAS_GLOBAL1 1
+#define UIAS_GLOBAL2 2
+#define UIAS_GLOBAL3 3
+#define UIAS_GLOBAL4 4
+#define UIAS_GLOBAL5 5
+#define UIAS_FAVORITES 6
+
+#define SORT_HOST 0
+#define SORT_MAP 1
+#define SORT_CLIENTS 2
+#define SORT_GAME 3
+#define SORT_PING 4
+
+#define GAMES_ALL 0
+#define GAMES_FFA 1
+#define GAMES_TEAMPLAY 2
+#define GAMES_TOURNEY 3
+#define GAMES_CTF 4
+
+static const char *master_items[] = {
+ "Local",
+ "Internet1",
+ "Internet2",
+ "Internet3",
+ "Internet4",
+ "Internet5",
+ "Favorites",
+ NULL
+};
+
+static const char *servertype_items[] = {
+ "All",
+ "Free For All",
+ "Team Deathmatch",
+ "Tournament",
+ "Capture the Flag",
+ NULL
+};
+
+static const char *sortkey_items[] = {
+ "Server Name",
+ "Map Name",
+ "Open Player Spots",
+ "Game Type",
+ "Ping Time",
+ NULL
+};
+
+static char* gamenames[] = {
+ "DM ", // deathmatch
+ "1v1", // tournament
+ "SP ", // single player
+ "Team DM", // team deathmatch
+ "CTF", // capture the flag
+ "One Flag CTF", // one flag ctf
+ "OverLoad", // Overload
+ "Harvester", // Harvester
+ "Rocket Arena 3", // Rocket Arena 3
+ "Q3F", // Q3F
+ "Urban Terror", // Urban Terror
+ "OSP", // Orange Smoothie Productions
+ "???", // unknown
+ NULL
+};
+
+static char* netnames[] = {
+ "???",
+ "UDP",
+ "UDP6",
+ NULL
+};
+
+static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files";
+
+const char* punkbuster_items[] = {
+ "Disabled",
+ "Enabled",
+ NULL
+};
+
+const char* punkbuster_msg[] = {
+ "PunkBuster will be",
+ "disabled the next time",
+ "Quake III Arena",
+ "is started.",
+ NULL
+};
+
+typedef struct {
+ char adrstr[MAX_ADDRESSLENGTH];
+ int start;
+} pinglist_t;
+
+typedef struct servernode_s {
+ char adrstr[MAX_ADDRESSLENGTH];
+ char hostname[MAX_HOSTNAMELENGTH+3];
+ char mapname[MAX_MAPNAMELENGTH];
+ int numclients;
+ int maxclients;
+ int pingtime;
+ int gametype;
+ char gamename[12];
+ int nettype;
+ int minPing;
+ int maxPing;
+ qboolean bPB;
+
+} servernode_t;
+
+typedef struct {
+ char buff[MAX_LISTBOXWIDTH];
+ servernode_t* servernode;
+} table_t;
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+
+ menulist_s master;
+ menulist_s gametype;
+ menulist_s sortkey;
+ menuradiobutton_s showfull;
+ menuradiobutton_s showempty;
+
+ menulist_s list;
+ menubitmap_s mappic;
+ menubitmap_s arrows;
+ menubitmap_s up;
+ menubitmap_s down;
+ menutext_s status;
+ menutext_s statusbar;
+
+ menubitmap_s remove;
+ menubitmap_s back;
+ menubitmap_s refresh;
+ menubitmap_s specify;
+ menubitmap_s create;
+ menubitmap_s go;
+
+ pinglist_t pinglist[MAX_PINGREQUESTS];
+ table_t table[MAX_LISTBOXITEMS];
+ char* items[MAX_LISTBOXITEMS];
+ int numqueriedservers;
+ int *numservers;
+ servernode_t *serverlist;
+ int currentping;
+ qboolean refreshservers;
+ int nextpingtime;
+ int maxservers;
+ int refreshtime;
+ char favoriteaddresses[MAX_FAVORITESERVERS][MAX_ADDRESSLENGTH];
+ int numfavoriteaddresses;
+
+ menulist_s punkbuster;
+ menubitmap_s pblogo;
+} arenaservers_t;
+
+static arenaservers_t g_arenaservers;
+
+
+static servernode_t g_globalserverlist[MAX_GLOBALSERVERS];
+static int g_numglobalservers;
+static servernode_t g_localserverlist[MAX_LOCALSERVERS];
+static int g_numlocalservers;
+static servernode_t g_favoriteserverlist[MAX_FAVORITESERVERS];
+static int g_numfavoriteservers;
+static int g_servertype;
+static int g_gametype;
+static int g_sortkey;
+static int g_emptyservers;
+static int g_fullservers;
+
+
+/*
+=================
+ArenaServers_MaxPing
+=================
+*/
+static int ArenaServers_MaxPing( void ) {
+ int maxPing;
+
+ maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" );
+ if( maxPing < 100 ) {
+ maxPing = 100;
+ }
+ return maxPing;
+}
+
+
+/*
+=================
+ArenaServers_Compare
+=================
+*/
+static int QDECL ArenaServers_Compare( const void *arg1, const void *arg2 ) {
+ float f1;
+ float f2;
+ servernode_t* t1;
+ servernode_t* t2;
+
+ t1 = (servernode_t *)arg1;
+ t2 = (servernode_t *)arg2;
+
+ switch( g_sortkey ) {
+ case SORT_HOST:
+ return Q_stricmp( t1->hostname, t2->hostname );
+
+ case SORT_MAP:
+ return Q_stricmp( t1->mapname, t2->mapname );
+
+ case SORT_CLIENTS:
+ f1 = t1->maxclients - t1->numclients;
+ if( f1 < 0 ) {
+ f1 = 0;
+ }
+
+ f2 = t2->maxclients - t2->numclients;
+ if( f2 < 0 ) {
+ f2 = 0;
+ }
+
+ if( f1 < f2 ) {
+ return 1;
+ }
+ if( f1 == f2 ) {
+ return 0;
+ }
+ return -1;
+
+ case SORT_GAME:
+ if( t1->gametype < t2->gametype ) {
+ return -1;
+ }
+ if( t1->gametype == t2->gametype ) {
+ return 0;
+ }
+ return 1;
+
+ case SORT_PING:
+ if( t1->pingtime < t2->pingtime ) {
+ return -1;
+ }
+ if( t1->pingtime > t2->pingtime ) {
+ return 1;
+ }
+ return Q_stricmp( t1->hostname, t2->hostname );
+ }
+
+ return 0;
+}
+
+
+/*
+=================
+ArenaServers_Go
+=================
+*/
+static void ArenaServers_Go( void ) {
+ servernode_t* servernode;
+
+ servernode = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
+ if( servernode ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", servernode->adrstr ) );
+ }
+}
+
+
+/*
+=================
+ArenaServers_UpdatePicture
+=================
+*/
+static void ArenaServers_UpdatePicture( void ) {
+ static char picname[64];
+ servernode_t* servernodeptr;
+
+ if( !g_arenaservers.list.numitems ) {
+ g_arenaservers.mappic.generic.name = NULL;
+ }
+ else {
+ servernodeptr = g_arenaservers.table[g_arenaservers.list.curvalue].servernode;
+ Com_sprintf( picname, sizeof(picname), "levelshots/%s.tga", servernodeptr->mapname );
+ g_arenaservers.mappic.generic.name = picname;
+
+ }
+
+ // force shader update during draw
+ g_arenaservers.mappic.shader = 0;
+}
+
+
+/*
+=================
+ArenaServers_UpdateMenu
+=================
+*/
+static void ArenaServers_UpdateMenu( void ) {
+ int i;
+ int j;
+ int count;
+ char* buff;
+ servernode_t* servernodeptr;
+ table_t* tableptr;
+ char *pingColor;
+
+ if( g_arenaservers.numqueriedservers > 0 ) {
+ // servers found
+ if( g_arenaservers.refreshservers && ( g_arenaservers.currentping <= g_arenaservers.numqueriedservers ) ) {
+ // show progress
+ Com_sprintf( g_arenaservers.status.string, MAX_STATUSLENGTH, "%d of %d Arena Servers.", g_arenaservers.currentping, g_arenaservers.numqueriedservers);
+ g_arenaservers.statusbar.string = "Press SPACE to stop";
+ qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
+ }
+ else {
+ // all servers pinged - enable controls
+ g_arenaservers.master.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.list.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.go.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
+
+ // update status bar
+ if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) {
+ g_arenaservers.statusbar.string = quake3worldMessage;
+ }
+ else {
+ g_arenaservers.statusbar.string = "";
+ }
+
+ }
+ }
+ else {
+ // no servers found
+ if( g_arenaservers.refreshservers ) {
+ strcpy( g_arenaservers.status.string,"Scanning For Servers." );
+ g_arenaservers.statusbar.string = "Press SPACE to stop";
+
+ // disable controls during refresh
+ g_arenaservers.master.generic.flags |= QMF_GRAYED;
+ g_arenaservers.gametype.generic.flags |= QMF_GRAYED;
+ g_arenaservers.sortkey.generic.flags |= QMF_GRAYED;
+ g_arenaservers.showempty.generic.flags |= QMF_GRAYED;
+ g_arenaservers.showfull.generic.flags |= QMF_GRAYED;
+ g_arenaservers.list.generic.flags |= QMF_GRAYED;
+ g_arenaservers.refresh.generic.flags |= QMF_GRAYED;
+ g_arenaservers.go.generic.flags |= QMF_GRAYED;
+ g_arenaservers.punkbuster.generic.flags |= QMF_GRAYED;
+ }
+ else {
+ if( g_arenaservers.numqueriedservers < 0 ) {
+ strcpy(g_arenaservers.status.string,"No Response From Master Server." );
+ }
+ else {
+ strcpy(g_arenaservers.status.string,"No Servers Found." );
+ }
+
+ // update status bar
+ if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) {
+ g_arenaservers.statusbar.string = quake3worldMessage;
+ }
+ else {
+ g_arenaservers.statusbar.string = "";
+ }
+
+ // end of refresh - set control state
+ g_arenaservers.master.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.showfull.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.list.generic.flags |= QMF_GRAYED;
+ g_arenaservers.refresh.generic.flags &= ~QMF_GRAYED;
+ g_arenaservers.go.generic.flags |= QMF_GRAYED;
+ g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED;
+ }
+
+ // zero out list box
+ g_arenaservers.list.numitems = 0;
+ g_arenaservers.list.curvalue = 0;
+ g_arenaservers.list.top = 0;
+
+ // update picture
+ ArenaServers_UpdatePicture();
+ return;
+ }
+
+ // build list box strings - apply culling filters
+ servernodeptr = g_arenaservers.serverlist;
+ count = *g_arenaservers.numservers;
+ for( i = 0, j = 0; i < count; i++, servernodeptr++ ) {
+ tableptr = &g_arenaservers.table[j];
+ tableptr->servernode = servernodeptr;
+ buff = tableptr->buff;
+
+ // can only cull valid results
+ if( !g_emptyservers && !servernodeptr->numclients ) {
+ continue;
+ }
+
+ if( !g_fullservers && ( servernodeptr->numclients == servernodeptr->maxclients ) ) {
+ continue;
+ }
+
+ switch( g_gametype ) {
+ case GAMES_ALL:
+ break;
+
+ case GAMES_FFA:
+ if( servernodeptr->gametype != GT_FFA ) {
+ continue;
+ }
+ break;
+
+ case GAMES_TEAMPLAY:
+ if( servernodeptr->gametype != GT_TEAM ) {
+ continue;
+ }
+ break;
+
+ case GAMES_TOURNEY:
+ if( servernodeptr->gametype != GT_TOURNAMENT ) {
+ continue;
+ }
+ break;
+
+ case GAMES_CTF:
+ if( servernodeptr->gametype != GT_CTF ) {
+ continue;
+ }
+ break;
+ }
+
+ if( servernodeptr->pingtime < servernodeptr->minPing ) {
+ pingColor = S_COLOR_BLUE;
+ }
+ else if( servernodeptr->maxPing && servernodeptr->pingtime > servernodeptr->maxPing ) {
+ pingColor = S_COLOR_BLUE;
+ }
+ else if( servernodeptr->pingtime < 200 ) {
+ pingColor = S_COLOR_GREEN;
+ }
+ else if( servernodeptr->pingtime < 400 ) {
+ pingColor = S_COLOR_YELLOW;
+ }
+ else {
+ pingColor = S_COLOR_RED;
+ }
+
+ Com_sprintf( buff, MAX_LISTBOXWIDTH, "%-20.20s %-12.12s %2d/%2d %-8.8s %3s %s%3d " S_COLOR_YELLOW "%s",
+ servernodeptr->hostname, servernodeptr->mapname, servernodeptr->numclients,
+ servernodeptr->maxclients, servernodeptr->gamename,
+ netnames[servernodeptr->nettype], pingColor, servernodeptr->pingtime, servernodeptr->bPB ? "Yes" : "No" );
+ j++;
+ }
+
+ g_arenaservers.list.numitems = j;
+ g_arenaservers.list.curvalue = 0;
+ g_arenaservers.list.top = 0;
+
+ // update picture
+ ArenaServers_UpdatePicture();
+}
+
+
+/*
+=================
+ArenaServers_Remove
+=================
+*/
+static void ArenaServers_Remove( void )
+{
+ int i;
+ servernode_t* servernodeptr;
+ table_t* tableptr;
+
+ if (!g_arenaservers.list.numitems)
+ return;
+
+ // remove selected item from display list
+ // items are in scattered order due to sort and cull
+ // perform delete on list box contents, resync all lists
+
+ tableptr = &g_arenaservers.table[g_arenaservers.list.curvalue];
+ servernodeptr = tableptr->servernode;
+
+ // find address in master list
+ for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
+ {
+ if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],servernodeptr->adrstr))
+ {
+ // delete address from master list
+ if (i < g_arenaservers.numfavoriteaddresses-1)
+ {
+ // shift items up
+ memcpy( &g_arenaservers.favoriteaddresses[i], &g_arenaservers.favoriteaddresses[i+1], (g_arenaservers.numfavoriteaddresses - i - 1)* MAX_ADDRESSLENGTH );
+ }
+ g_arenaservers.numfavoriteaddresses--;
+ memset( &g_arenaservers.favoriteaddresses[g_arenaservers.numfavoriteaddresses], 0, MAX_ADDRESSLENGTH );
+ break;
+ }
+ }
+
+ // find address in server list
+ for (i=0; i<g_numfavoriteservers; i++)
+ {
+ if (&g_favoriteserverlist[i] == servernodeptr)
+ {
+
+ // delete address from server list
+ if (i < g_numfavoriteservers-1)
+ {
+ // shift items up
+ memcpy( &g_favoriteserverlist[i], &g_favoriteserverlist[i+1], (g_numfavoriteservers - i - 1)*sizeof(servernode_t));
+ }
+ g_numfavoriteservers--;
+ memset( &g_favoriteserverlist[ g_numfavoriteservers ], 0, sizeof(servernode_t));
+ break;
+ }
+ }
+
+ g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
+ g_arenaservers.currentping = g_arenaservers.numfavoriteaddresses;
+}
+
+
+/*
+=================
+ArenaServers_Insert
+=================
+*/
+static void ArenaServers_Insert( char* adrstr, char* info, int pingtime )
+{
+ servernode_t* servernodeptr;
+ char* s;
+ int i;
+
+
+ if ((pingtime >= ArenaServers_MaxPing()) && (g_servertype != UIAS_FAVORITES))
+ {
+ // slow global or local servers do not get entered
+ return;
+ }
+
+ if (*g_arenaservers.numservers >= g_arenaservers.maxservers) {
+ // list full;
+ servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers)-1;
+ } else {
+ // next slot
+ servernodeptr = g_arenaservers.serverlist+(*g_arenaservers.numservers);
+ (*g_arenaservers.numservers)++;
+ }
+
+ Q_strncpyz( servernodeptr->adrstr, adrstr, MAX_ADDRESSLENGTH );
+
+ Q_strncpyz( servernodeptr->hostname, Info_ValueForKey( info, "hostname"), MAX_HOSTNAMELENGTH );
+ Q_CleanStr( servernodeptr->hostname );
+ Q_strupr( servernodeptr->hostname );
+
+ Q_strncpyz( servernodeptr->mapname, Info_ValueForKey( info, "mapname"), MAX_MAPNAMELENGTH );
+ Q_CleanStr( servernodeptr->mapname );
+ Q_strupr( servernodeptr->mapname );
+
+ servernodeptr->numclients = atoi( Info_ValueForKey( info, "clients") );
+ servernodeptr->maxclients = atoi( Info_ValueForKey( info, "sv_maxclients") );
+ servernodeptr->pingtime = pingtime;
+ servernodeptr->minPing = atoi( Info_ValueForKey( info, "minPing") );
+ servernodeptr->maxPing = atoi( Info_ValueForKey( info, "maxPing") );
+ servernodeptr->bPB = atoi( Info_ValueForKey( info, "punkbuster") );
+
+ /*
+ s = Info_ValueForKey( info, "nettype" );
+ for (i=0; ;i++)
+ {
+ if (!netnames[i])
+ {
+ servernodeptr->nettype = 0;
+ break;
+ }
+ else if (!Q_stricmp( netnames[i], s ))
+ {
+ servernodeptr->nettype = i;
+ break;
+ }
+ }
+ */
+ servernodeptr->nettype = atoi(Info_ValueForKey(info, "nettype"));
+
+ s = Info_ValueForKey( info, "game");
+ i = atoi( Info_ValueForKey( info, "gametype") );
+ if( i < 0 ) {
+ i = 0;
+ }
+ else if( i > 11 ) {
+ i = 12;
+ }
+ if( *s ) {
+ servernodeptr->gametype = i;//-1;
+ Q_strncpyz( servernodeptr->gamename, s, sizeof(servernodeptr->gamename) );
+ }
+ else {
+ servernodeptr->gametype = i;
+ Q_strncpyz( servernodeptr->gamename, gamenames[i], sizeof(servernodeptr->gamename) );
+ }
+}
+
+
+/*
+=================
+ArenaServers_InsertFavorites
+
+Insert nonresponsive address book entries into display lists.
+=================
+*/
+void ArenaServers_InsertFavorites( void )
+{
+ int i;
+ int j;
+ char info[MAX_INFO_STRING];
+
+ // resync existing results with new or deleted cvars
+ info[0] = '\0';
+ Info_SetValueForKey( info, "hostname", "No Response" );
+ for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
+ {
+ // find favorite address in refresh list
+ for (j=0; j<g_numfavoriteservers; j++)
+ if (!Q_stricmp(g_arenaservers.favoriteaddresses[i],g_favoriteserverlist[j].adrstr))
+ break;
+
+ if ( j >= g_numfavoriteservers)
+ {
+ // not in list, add it
+ ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() );
+ }
+ }
+}
+
+
+/*
+=================
+ArenaServers_LoadFavorites
+
+Load cvar address book entries into local lists.
+=================
+*/
+void ArenaServers_LoadFavorites( void )
+{
+ int i;
+ int j;
+ int numtempitems;
+ char emptyinfo[MAX_INFO_STRING];
+ char adrstr[MAX_ADDRESSLENGTH];
+ servernode_t templist[MAX_FAVORITESERVERS];
+ qboolean found;
+
+ found = qfalse;
+ emptyinfo[0] = '\0';
+
+ // copy the old
+ memcpy( templist, g_favoriteserverlist, sizeof(servernode_t)*MAX_FAVORITESERVERS );
+ numtempitems = g_numfavoriteservers;
+
+ // clear the current for sync
+ memset( g_favoriteserverlist, 0, sizeof(servernode_t)*MAX_FAVORITESERVERS );
+ g_numfavoriteservers = 0;
+
+ // resync existing results with new or deleted cvars
+ for (i=0; i<MAX_FAVORITESERVERS; i++)
+ {
+ trap_Cvar_VariableStringBuffer( va("server%d",i+1), adrstr, MAX_ADDRESSLENGTH );
+ if (!adrstr[0])
+ continue;
+
+ // quick sanity check to avoid slow domain name resolving
+ // first character must be numeric
+ if (adrstr[0] < '0' || adrstr[0] > '9')
+ continue;
+
+ // favorite server addresses must be maintained outside refresh list
+ // this mimics local and global netadr's stored in client
+ // these can be fetched to fill ping list
+ strcpy( g_arenaservers.favoriteaddresses[g_numfavoriteservers], adrstr );
+
+ // find this server in the old list
+ for (j=0; j<numtempitems; j++)
+ if (!Q_stricmp( templist[j].adrstr, adrstr ))
+ break;
+
+ if (j < numtempitems)
+ {
+ // found server - add exisiting results
+ memcpy( &g_favoriteserverlist[g_numfavoriteservers], &templist[j], sizeof(servernode_t) );
+ found = qtrue;
+ }
+ else
+ {
+ // add new server
+ Q_strncpyz( g_favoriteserverlist[g_numfavoriteservers].adrstr, adrstr, MAX_ADDRESSLENGTH );
+ g_favoriteserverlist[g_numfavoriteservers].pingtime = ArenaServers_MaxPing();
+ }
+
+ g_numfavoriteservers++;
+ }
+
+ g_arenaservers.numfavoriteaddresses = g_numfavoriteservers;
+
+ if (!found)
+ {
+ // no results were found, reset server list
+ // list will be automatically refreshed when selected
+ g_numfavoriteservers = 0;
+ }
+}
+
+
+/*
+=================
+ArenaServers_StopRefresh
+=================
+*/
+static void ArenaServers_StopRefresh( void )
+{
+ if (!g_arenaservers.refreshservers)
+ // not currently refreshing
+ return;
+
+ g_arenaservers.refreshservers = qfalse;
+
+ if (g_servertype == UIAS_FAVORITES)
+ {
+ // nonresponsive favorites must be shown
+ ArenaServers_InsertFavorites();
+ }
+
+ // final tally
+ if (g_arenaservers.numqueriedservers >= 0)
+ {
+ g_arenaservers.currentping = *g_arenaservers.numservers;
+ g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
+ }
+
+ // sort
+ qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
+
+ ArenaServers_UpdateMenu();
+}
+
+
+/*
+=================
+ArenaServers_DoRefresh
+=================
+*/
+static void ArenaServers_DoRefresh( void )
+{
+ int i;
+ int j;
+ int time;
+ int maxPing;
+ char adrstr[MAX_ADDRESSLENGTH];
+ char info[MAX_INFO_STRING];
+
+ if (uis.realtime < g_arenaservers.refreshtime)
+ {
+ if (g_servertype != UIAS_FAVORITES) {
+ if (g_servertype == UIAS_LOCAL) {
+ if (!trap_LAN_GetServerCount(g_servertype)) {
+ return;
+ }
+ }
+ if (trap_LAN_GetServerCount(g_servertype) < 0) {
+ // still waiting for response
+ return;
+ }
+ }
+ }
+
+ if (uis.realtime < g_arenaservers.nextpingtime)
+ {
+ // wait for time trigger
+ return;
+ }
+
+ // trigger at 10Hz intervals
+ g_arenaservers.nextpingtime = uis.realtime + 10;
+
+ // process ping results
+ maxPing = ArenaServers_MaxPing();
+ for (i=0; i<MAX_PINGREQUESTS; i++)
+ {
+ trap_LAN_GetPing( i, adrstr, MAX_ADDRESSLENGTH, &time );
+ if (!adrstr[0])
+ {
+ // ignore empty or pending pings
+ continue;
+ }
+
+ // find ping result in our local list
+ for (j=0; j<MAX_PINGREQUESTS; j++)
+ if (!Q_stricmp( adrstr, g_arenaservers.pinglist[j].adrstr ))
+ break;
+
+ if (j < MAX_PINGREQUESTS)
+ {
+ // found it
+ if (!time)
+ {
+ time = uis.realtime - g_arenaservers.pinglist[j].start;
+ if (time < maxPing)
+ {
+ // still waiting
+ continue;
+ }
+ }
+
+ if (time > maxPing)
+ {
+ // stale it out
+ info[0] = '\0';
+ time = maxPing;
+ }
+ else
+ {
+ trap_LAN_GetPingInfo( i, info, MAX_INFO_STRING );
+ }
+
+ // insert ping results
+ ArenaServers_Insert( adrstr, info, time );
+
+ // clear this query from internal list
+ g_arenaservers.pinglist[j].adrstr[0] = '\0';
+ }
+
+ // clear this query from external list
+ trap_LAN_ClearPing( i );
+ }
+
+ // get results of servers query
+ // counts can increase as servers respond
+ if (g_servertype == UIAS_FAVORITES) {
+ g_arenaservers.numqueriedservers = g_arenaservers.numfavoriteaddresses;
+ } else {
+ g_arenaservers.numqueriedservers = trap_LAN_GetServerCount(g_servertype);
+ }
+
+// if (g_arenaservers.numqueriedservers > g_arenaservers.maxservers)
+// g_arenaservers.numqueriedservers = g_arenaservers.maxservers;
+
+ // send ping requests in reasonable bursts
+ // iterate ping through all found servers
+ for (i=0; i<MAX_PINGREQUESTS && g_arenaservers.currentping < g_arenaservers.numqueriedservers; i++)
+ {
+ if (trap_LAN_GetPingQueueCount() >= MAX_PINGREQUESTS)
+ {
+ // ping queue is full
+ break;
+ }
+
+ // find empty slot
+ for (j=0; j<MAX_PINGREQUESTS; j++)
+ if (!g_arenaservers.pinglist[j].adrstr[0])
+ break;
+
+ if (j >= MAX_PINGREQUESTS)
+ // no empty slots available yet - wait for timeout
+ break;
+
+ // get an address to ping
+
+ if (g_servertype == UIAS_FAVORITES) {
+ strcpy( adrstr, g_arenaservers.favoriteaddresses[g_arenaservers.currentping] );
+ } else {
+ trap_LAN_GetServerAddressString(g_servertype, g_arenaservers.currentping, adrstr, MAX_ADDRESSLENGTH );
+ }
+
+ strcpy( g_arenaservers.pinglist[j].adrstr, adrstr );
+ g_arenaservers.pinglist[j].start = uis.realtime;
+
+ trap_Cmd_ExecuteText( EXEC_NOW, va( "ping %s\n", adrstr ) );
+
+ // advance to next server
+ g_arenaservers.currentping++;
+ }
+
+ if (!trap_LAN_GetPingQueueCount())
+ {
+ // all pings completed
+ ArenaServers_StopRefresh();
+ return;
+ }
+
+ // update the user interface with ping status
+ ArenaServers_UpdateMenu();
+}
+
+
+/*
+=================
+ArenaServers_StartRefresh
+=================
+*/
+static void ArenaServers_StartRefresh( void )
+{
+ int i;
+ char myargs[32], protocol[32];
+
+ memset( g_arenaservers.serverlist, 0, g_arenaservers.maxservers*sizeof(table_t) );
+
+ for (i=0; i<MAX_PINGREQUESTS; i++)
+ {
+ g_arenaservers.pinglist[i].adrstr[0] = '\0';
+ trap_LAN_ClearPing( i );
+ }
+
+ g_arenaservers.refreshservers = qtrue;
+ g_arenaservers.currentping = 0;
+ g_arenaservers.nextpingtime = 0;
+ *g_arenaservers.numservers = 0;
+ g_arenaservers.numqueriedservers = 0;
+
+ // allow max 5 seconds for responses
+ g_arenaservers.refreshtime = uis.realtime + 5000;
+
+ // place menu in zeroed state
+ ArenaServers_UpdateMenu();
+
+ if( g_servertype == UIAS_LOCAL ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" );
+ return;
+ }
+
+ if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) {
+ switch( g_arenaservers.gametype.curvalue ) {
+ default:
+ case GAMES_ALL:
+ myargs[0] = 0;
+ break;
+
+ case GAMES_FFA:
+ strcpy( myargs, " ffa" );
+ break;
+
+ case GAMES_TEAMPLAY:
+ strcpy( myargs, " team" );
+ break;
+
+ case GAMES_TOURNEY:
+ strcpy( myargs, " tourney" );
+ break;
+
+ case GAMES_CTF:
+ strcpy( myargs, " ctf" );
+ break;
+ }
+
+
+ if (g_emptyservers) {
+ strcat(myargs, " empty");
+ }
+
+ if (g_fullservers) {
+ strcat(myargs, " full");
+ }
+
+ protocol[0] = '\0';
+ trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) );
+ if (strlen(protocol)) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - 1, protocol, myargs ));
+ }
+ else {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - 1, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) );
+ }
+ }
+}
+
+
+/*
+=================
+ArenaServers_SaveChanges
+=================
+*/
+void ArenaServers_SaveChanges( void )
+{
+ int i;
+
+ for (i=0; i<g_arenaservers.numfavoriteaddresses; i++)
+ trap_Cvar_Set( va("server%d",i+1), g_arenaservers.favoriteaddresses[i] );
+
+ for (; i<MAX_FAVORITESERVERS; i++)
+ trap_Cvar_Set( va("server%d",i+1), "" );
+}
+
+
+/*
+=================
+ArenaServers_Sort
+=================
+*/
+void ArenaServers_Sort( int type ) {
+ if( g_sortkey == type ) {
+ return;
+ }
+
+ g_sortkey = type;
+ qsort( g_arenaservers.serverlist, *g_arenaservers.numservers, sizeof( servernode_t ), ArenaServers_Compare);
+}
+
+
+/*
+=================
+ArenaServers_SetType
+=================
+*/
+int ArenaServers_SetType( int type )
+{
+ if(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5)
+ {
+ char masterstr[2], cvarname[sizeof("sv_master1")];
+
+ while(type <= UIAS_GLOBAL5)
+ {
+ Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type);
+ trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr));
+ if(*masterstr)
+ break;
+
+ type++;
+ }
+ }
+
+ g_servertype = type;
+
+ switch( type ) {
+ default:
+ case UIAS_LOCAL:
+ g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ g_arenaservers.serverlist = g_localserverlist;
+ g_arenaservers.numservers = &g_numlocalservers;
+ g_arenaservers.maxservers = MAX_LOCALSERVERS;
+ break;
+
+ case UIAS_GLOBAL1:
+ case UIAS_GLOBAL2:
+ case UIAS_GLOBAL3:
+ case UIAS_GLOBAL4:
+ case UIAS_GLOBAL5:
+ g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ g_arenaservers.serverlist = g_globalserverlist;
+ g_arenaservers.numservers = &g_numglobalservers;
+ g_arenaservers.maxservers = MAX_GLOBALSERVERS;
+ break;
+
+ case UIAS_FAVORITES:
+ g_arenaservers.remove.generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
+ g_arenaservers.serverlist = g_favoriteserverlist;
+ g_arenaservers.numservers = &g_numfavoriteservers;
+ g_arenaservers.maxservers = MAX_FAVORITESERVERS;
+ break;
+
+ }
+
+ if( !*g_arenaservers.numservers ) {
+ ArenaServers_StartRefresh();
+ }
+ else {
+ // avoid slow operation, use existing results
+ g_arenaservers.currentping = *g_arenaservers.numservers;
+ g_arenaservers.numqueriedservers = *g_arenaservers.numservers;
+ ArenaServers_UpdateMenu();
+ }
+ strcpy(g_arenaservers.status.string,"hit refresh to update");
+
+ return type;
+}
+
+/*
+=================
+PunkBuster_Confirm
+=================
+*/
+static void Punkbuster_ConfirmEnable( qboolean result ) {
+ if (result)
+ {
+ trap_SetPbClStatus(1);
+ }
+ g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
+}
+
+static void Punkbuster_ConfirmDisable( qboolean result ) {
+ if (result)
+ {
+ trap_SetPbClStatus(0);
+ UI_Message( punkbuster_msg );
+ }
+ g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
+}
+
+/*
+=================
+ArenaServers_Event
+=================
+*/
+static void ArenaServers_Event( void* ptr, int event ) {
+ int id;
+
+ id = ((menucommon_s*)ptr)->id;
+
+ if( event != QM_ACTIVATED && id != ID_LIST ) {
+ return;
+ }
+
+ switch( id ) {
+ case ID_MASTER:
+ g_arenaservers.master.curvalue = ArenaServers_SetType(g_arenaservers.master.curvalue);
+ trap_Cvar_SetValue( "ui_browserMaster", g_arenaservers.master.curvalue);
+ break;
+
+ case ID_GAMETYPE:
+ trap_Cvar_SetValue( "ui_browserGameType", g_arenaservers.gametype.curvalue );
+ g_gametype = g_arenaservers.gametype.curvalue;
+ ArenaServers_UpdateMenu();
+ break;
+
+ case ID_SORTKEY:
+ trap_Cvar_SetValue( "ui_browserSortKey", g_arenaservers.sortkey.curvalue );
+ ArenaServers_Sort( g_arenaservers.sortkey.curvalue );
+ ArenaServers_UpdateMenu();
+ break;
+
+ case ID_SHOW_FULL:
+ trap_Cvar_SetValue( "ui_browserShowFull", g_arenaservers.showfull.curvalue );
+ g_fullservers = g_arenaservers.showfull.curvalue;
+ ArenaServers_UpdateMenu();
+ break;
+
+ case ID_SHOW_EMPTY:
+ trap_Cvar_SetValue( "ui_browserShowEmpty", g_arenaservers.showempty.curvalue );
+ g_emptyservers = g_arenaservers.showempty.curvalue;
+ ArenaServers_UpdateMenu();
+ break;
+
+ case ID_LIST:
+ if( event == QM_GOTFOCUS ) {
+ ArenaServers_UpdatePicture();
+ }
+ break;
+
+ case ID_SCROLL_UP:
+ ScrollList_Key( &g_arenaservers.list, K_UPARROW );
+ break;
+
+ case ID_SCROLL_DOWN:
+ ScrollList_Key( &g_arenaservers.list, K_DOWNARROW );
+ break;
+
+ case ID_BACK:
+ ArenaServers_StopRefresh();
+ ArenaServers_SaveChanges();
+ UI_PopMenu();
+ break;
+
+ case ID_REFRESH:
+ ArenaServers_StartRefresh();
+ break;
+
+ case ID_SPECIFY:
+ UI_SpecifyServerMenu();
+ break;
+
+ case ID_CREATE:
+ UI_StartServerMenu( qtrue );
+ break;
+
+ case ID_CONNECT:
+ ArenaServers_Go();
+ break;
+
+ case ID_REMOVE:
+ ArenaServers_Remove();
+ ArenaServers_UpdateMenu();
+ break;
+
+ case ID_PUNKBUSTER:
+ if (g_arenaservers.punkbuster.curvalue)
+ {
+ UI_ConfirmMenu_Style( "Enable Punkbuster?", UI_CENTER|UI_INVERSE|UI_SMALLFONT, 0, Punkbuster_ConfirmEnable );
+ }
+ else
+ {
+ UI_ConfirmMenu_Style( "Disable Punkbuster?", UI_CENTER|UI_INVERSE|UI_SMALLFONT, 0, Punkbuster_ConfirmDisable );
+ }
+ break;
+ }
+}
+
+
+/*
+=================
+ArenaServers_MenuDraw
+=================
+*/
+static void ArenaServers_MenuDraw( void )
+{
+ if (g_arenaservers.refreshservers)
+ ArenaServers_DoRefresh();
+
+ Menu_Draw( &g_arenaservers.menu );
+}
+
+
+/*
+=================
+ArenaServers_MenuKey
+=================
+*/
+static sfxHandle_t ArenaServers_MenuKey( int key ) {
+ if( key == K_SPACE && g_arenaservers.refreshservers ) {
+ ArenaServers_StopRefresh();
+ return menu_move_sound;
+ }
+
+ if( ( key == K_DEL || key == K_KP_DEL ) && ( g_servertype == UIAS_FAVORITES ) &&
+ ( Menu_ItemAtCursor( &g_arenaservers.menu) == &g_arenaservers.list ) ) {
+ ArenaServers_Remove();
+ ArenaServers_UpdateMenu();
+ return menu_move_sound;
+ }
+
+ if( key == K_MOUSE2 || key == K_ESCAPE ) {
+ ArenaServers_StopRefresh();
+ ArenaServers_SaveChanges();
+ }
+
+
+ return Menu_DefaultKey( &g_arenaservers.menu, key );
+}
+
+
+/*
+=================
+ArenaServers_MenuInit
+=================
+*/
+static void ArenaServers_MenuInit( void ) {
+ int i;
+ int y;
+ int value;
+ static char statusbuffer[MAX_STATUSLENGTH];
+
+ // zero set all our globals
+ memset( &g_arenaservers, 0 ,sizeof(arenaservers_t) );
+
+ ArenaServers_Cache();
+
+ g_arenaservers.menu.fullscreen = qtrue;
+ g_arenaservers.menu.wrapAround = qtrue;
+ g_arenaservers.menu.draw = ArenaServers_MenuDraw;
+ g_arenaservers.menu.key = ArenaServers_MenuKey;
+
+ g_arenaservers.banner.generic.type = MTYPE_BTEXT;
+ g_arenaservers.banner.generic.flags = QMF_CENTER_JUSTIFY;
+ g_arenaservers.banner.generic.x = 320;
+ g_arenaservers.banner.generic.y = 16;
+ g_arenaservers.banner.string = "ARENA SERVERS";
+ g_arenaservers.banner.style = UI_CENTER;
+ g_arenaservers.banner.color = color_white;
+
+ y = 80;
+ g_arenaservers.master.generic.type = MTYPE_SPINCONTROL;
+ g_arenaservers.master.generic.name = "Servers:";
+ g_arenaservers.master.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ g_arenaservers.master.generic.callback = ArenaServers_Event;
+ g_arenaservers.master.generic.id = ID_MASTER;
+ g_arenaservers.master.generic.x = 320;
+ g_arenaservers.master.generic.y = y;
+ g_arenaservers.master.itemnames = master_items;
+
+ y += SMALLCHAR_HEIGHT;
+ g_arenaservers.gametype.generic.type = MTYPE_SPINCONTROL;
+ g_arenaservers.gametype.generic.name = "Game Type:";
+ g_arenaservers.gametype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ g_arenaservers.gametype.generic.callback = ArenaServers_Event;
+ g_arenaservers.gametype.generic.id = ID_GAMETYPE;
+ g_arenaservers.gametype.generic.x = 320;
+ g_arenaservers.gametype.generic.y = y;
+ g_arenaservers.gametype.itemnames = servertype_items;
+
+ y += SMALLCHAR_HEIGHT;
+ g_arenaservers.sortkey.generic.type = MTYPE_SPINCONTROL;
+ g_arenaservers.sortkey.generic.name = "Sort By:";
+ g_arenaservers.sortkey.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ g_arenaservers.sortkey.generic.callback = ArenaServers_Event;
+ g_arenaservers.sortkey.generic.id = ID_SORTKEY;
+ g_arenaservers.sortkey.generic.x = 320;
+ g_arenaservers.sortkey.generic.y = y;
+ g_arenaservers.sortkey.itemnames = sortkey_items;
+
+ y += SMALLCHAR_HEIGHT;
+ g_arenaservers.showfull.generic.type = MTYPE_RADIOBUTTON;
+ g_arenaservers.showfull.generic.name = "Show Full:";
+ g_arenaservers.showfull.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ g_arenaservers.showfull.generic.callback = ArenaServers_Event;
+ g_arenaservers.showfull.generic.id = ID_SHOW_FULL;
+ g_arenaservers.showfull.generic.x = 320;
+ g_arenaservers.showfull.generic.y = y;
+
+ y += SMALLCHAR_HEIGHT;
+ g_arenaservers.showempty.generic.type = MTYPE_RADIOBUTTON;
+ g_arenaservers.showempty.generic.name = "Show Empty:";
+ g_arenaservers.showempty.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ g_arenaservers.showempty.generic.callback = ArenaServers_Event;
+ g_arenaservers.showempty.generic.id = ID_SHOW_EMPTY;
+ g_arenaservers.showempty.generic.x = 320;
+ g_arenaservers.showempty.generic.y = y;
+
+ y += 3 * SMALLCHAR_HEIGHT;
+ g_arenaservers.list.generic.type = MTYPE_SCROLLLIST;
+ g_arenaservers.list.generic.flags = QMF_HIGHLIGHT_IF_FOCUS;
+ g_arenaservers.list.generic.id = ID_LIST;
+ g_arenaservers.list.generic.callback = ArenaServers_Event;
+ g_arenaservers.list.generic.x = 72;
+ g_arenaservers.list.generic.y = y;
+ g_arenaservers.list.width = MAX_LISTBOXWIDTH;
+ g_arenaservers.list.height = 11;
+ g_arenaservers.list.itemnames = (const char **)g_arenaservers.items;
+ for( i = 0; i < MAX_LISTBOXITEMS; i++ ) {
+ g_arenaservers.items[i] = g_arenaservers.table[i].buff;
+ }
+
+ g_arenaservers.mappic.generic.type = MTYPE_BITMAP;
+ g_arenaservers.mappic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ g_arenaservers.mappic.generic.x = 72;
+ g_arenaservers.mappic.generic.y = 80;
+ g_arenaservers.mappic.width = 128;
+ g_arenaservers.mappic.height = 96;
+ g_arenaservers.mappic.errorpic = ART_UNKNOWNMAP;
+
+ g_arenaservers.arrows.generic.type = MTYPE_BITMAP;
+ g_arenaservers.arrows.generic.name = ART_ARROWS0;
+ g_arenaservers.arrows.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ g_arenaservers.arrows.generic.callback = ArenaServers_Event;
+ g_arenaservers.arrows.generic.x = 512+48;
+ g_arenaservers.arrows.generic.y = 240-64+16;
+ g_arenaservers.arrows.width = 64;
+ g_arenaservers.arrows.height = 128;
+
+ g_arenaservers.up.generic.type = MTYPE_BITMAP;
+ g_arenaservers.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ g_arenaservers.up.generic.callback = ArenaServers_Event;
+ g_arenaservers.up.generic.id = ID_SCROLL_UP;
+ g_arenaservers.up.generic.x = 512+48;
+ g_arenaservers.up.generic.y = 240-64+16;
+ g_arenaservers.up.width = 64;
+ g_arenaservers.up.height = 64;
+ g_arenaservers.up.focuspic = ART_ARROWS_UP;
+
+ g_arenaservers.down.generic.type = MTYPE_BITMAP;
+ g_arenaservers.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ g_arenaservers.down.generic.callback = ArenaServers_Event;
+ g_arenaservers.down.generic.id = ID_SCROLL_DOWN;
+ g_arenaservers.down.generic.x = 512+48;
+ g_arenaservers.down.generic.y = 240+16;
+ g_arenaservers.down.width = 64;
+ g_arenaservers.down.height = 64;
+ g_arenaservers.down.focuspic = ART_ARROWS_DOWN;
+
+ y = 376;
+ g_arenaservers.status.generic.type = MTYPE_TEXT;
+ g_arenaservers.status.generic.x = 320;
+ g_arenaservers.status.generic.y = y;
+ g_arenaservers.status.string = statusbuffer;
+ g_arenaservers.status.style = UI_CENTER|UI_SMALLFONT;
+ g_arenaservers.status.color = menu_text_color;
+
+ y += SMALLCHAR_HEIGHT;
+ g_arenaservers.statusbar.generic.type = MTYPE_TEXT;
+ g_arenaservers.statusbar.generic.x = 320;
+ g_arenaservers.statusbar.generic.y = y;
+ g_arenaservers.statusbar.string = "";
+ g_arenaservers.statusbar.style = UI_CENTER|UI_SMALLFONT;
+ g_arenaservers.statusbar.color = text_color_normal;
+
+ g_arenaservers.remove.generic.type = MTYPE_BITMAP;
+ g_arenaservers.remove.generic.name = ART_REMOVE0;
+ g_arenaservers.remove.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ g_arenaservers.remove.generic.callback = ArenaServers_Event;
+ g_arenaservers.remove.generic.id = ID_REMOVE;
+ g_arenaservers.remove.generic.x = 450;
+ g_arenaservers.remove.generic.y = 86;
+ g_arenaservers.remove.width = 96;
+ g_arenaservers.remove.height = 48;
+ g_arenaservers.remove.focuspic = ART_REMOVE1;
+
+ g_arenaservers.back.generic.type = MTYPE_BITMAP;
+ g_arenaservers.back.generic.name = ART_BACK0;
+ g_arenaservers.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ g_arenaservers.back.generic.callback = ArenaServers_Event;
+ g_arenaservers.back.generic.id = ID_BACK;
+ g_arenaservers.back.generic.x = 0;
+ g_arenaservers.back.generic.y = 480-64;
+ g_arenaservers.back.width = 128;
+ g_arenaservers.back.height = 64;
+ g_arenaservers.back.focuspic = ART_BACK1;
+
+ g_arenaservers.specify.generic.type = MTYPE_BITMAP;
+ g_arenaservers.specify.generic.name = ART_SPECIFY0;
+ g_arenaservers.specify.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ g_arenaservers.specify.generic.callback = ArenaServers_Event;
+ g_arenaservers.specify.generic.id = ID_SPECIFY;
+ g_arenaservers.specify.generic.x = 128;
+ g_arenaservers.specify.generic.y = 480-64;
+ g_arenaservers.specify.width = 128;
+ g_arenaservers.specify.height = 64;
+ g_arenaservers.specify.focuspic = ART_SPECIFY1;
+
+ g_arenaservers.refresh.generic.type = MTYPE_BITMAP;
+ g_arenaservers.refresh.generic.name = ART_REFRESH0;
+ g_arenaservers.refresh.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ g_arenaservers.refresh.generic.callback = ArenaServers_Event;
+ g_arenaservers.refresh.generic.id = ID_REFRESH;
+ g_arenaservers.refresh.generic.x = 256;
+ g_arenaservers.refresh.generic.y = 480-64;
+ g_arenaservers.refresh.width = 128;
+ g_arenaservers.refresh.height = 64;
+ g_arenaservers.refresh.focuspic = ART_REFRESH1;
+
+ g_arenaservers.create.generic.type = MTYPE_BITMAP;
+ g_arenaservers.create.generic.name = ART_CREATE0;
+ g_arenaservers.create.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ g_arenaservers.create.generic.callback = ArenaServers_Event;
+ g_arenaservers.create.generic.id = ID_CREATE;
+ g_arenaservers.create.generic.x = 384;
+ g_arenaservers.create.generic.y = 480-64;
+ g_arenaservers.create.width = 128;
+ g_arenaservers.create.height = 64;
+ g_arenaservers.create.focuspic = ART_CREATE1;
+
+ g_arenaservers.go.generic.type = MTYPE_BITMAP;
+ g_arenaservers.go.generic.name = ART_CONNECT0;
+ g_arenaservers.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ g_arenaservers.go.generic.callback = ArenaServers_Event;
+ g_arenaservers.go.generic.id = ID_CONNECT;
+ g_arenaservers.go.generic.x = 640;
+ g_arenaservers.go.generic.y = 480-64;
+ g_arenaservers.go.width = 128;
+ g_arenaservers.go.height = 64;
+ g_arenaservers.go.focuspic = ART_CONNECT1;
+
+ g_arenaservers.punkbuster.generic.type = MTYPE_SPINCONTROL;
+ g_arenaservers.punkbuster.generic.name = "Punkbuster:";
+ g_arenaservers.punkbuster.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ g_arenaservers.punkbuster.generic.callback = ArenaServers_Event;
+ g_arenaservers.punkbuster.generic.id = ID_PUNKBUSTER;
+ g_arenaservers.punkbuster.generic.x = 480+32;
+ g_arenaservers.punkbuster.generic.y = 144;
+ g_arenaservers.punkbuster.itemnames = punkbuster_items;
+
+ g_arenaservers.pblogo.generic.type = MTYPE_BITMAP;
+ g_arenaservers.pblogo.generic.name = ART_PUNKBUSTER;
+ g_arenaservers.pblogo.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ g_arenaservers.pblogo.generic.x = 526;
+ g_arenaservers.pblogo.generic.y = 176;
+ g_arenaservers.pblogo.width = 32;
+ g_arenaservers.pblogo.height = 16;
+ g_arenaservers.pblogo.errorpic = ART_UNKNOWNMAP;
+
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.banner );
+
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.master );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.gametype );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.sortkey );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showfull);
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.showempty );
+
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.mappic );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.list );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.status );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.statusbar );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.arrows );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.up );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.down );
+
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.remove );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.back );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.specify );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.refresh );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.create );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.go );
+
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.punkbuster );
+ Menu_AddItem( &g_arenaservers.menu, (void*) &g_arenaservers.pblogo );
+
+ ArenaServers_LoadFavorites();
+
+ g_servertype = Com_Clamp( 0, 3, ui_browserMaster.integer );
+ // hack to get rid of MPlayer stuff
+ value = g_servertype;
+ if (value >= 1)
+ value--;
+ g_arenaservers.master.curvalue = value;
+
+ g_gametype = Com_Clamp( 0, 4, ui_browserGameType.integer );
+ g_arenaservers.gametype.curvalue = g_gametype;
+
+ g_sortkey = Com_Clamp( 0, 4, ui_browserSortKey.integer );
+ g_arenaservers.sortkey.curvalue = g_sortkey;
+
+ g_fullservers = Com_Clamp( 0, 1, ui_browserShowFull.integer );
+ g_arenaservers.showfull.curvalue = g_fullservers;
+
+ g_emptyservers = Com_Clamp( 0, 1, ui_browserShowEmpty.integer );
+ g_arenaservers.showempty.curvalue = g_emptyservers;
+
+ g_arenaservers.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "cl_punkbuster" ) );
+
+ // force to initial state and refresh
+ g_arenaservers.master.curvalue = g_servertype = ArenaServers_SetType(g_servertype);
+
+ trap_Cvar_Register(NULL, "debug_protocol", "", 0 );
+}
+
+
+/*
+=================
+ArenaServers_Cache
+=================
+*/
+void ArenaServers_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_CREATE0 );
+ trap_R_RegisterShaderNoMip( ART_CREATE1 );
+ trap_R_RegisterShaderNoMip( ART_SPECIFY0 );
+ trap_R_RegisterShaderNoMip( ART_SPECIFY1 );
+ trap_R_RegisterShaderNoMip( ART_REFRESH0 );
+ trap_R_RegisterShaderNoMip( ART_REFRESH1 );
+ trap_R_RegisterShaderNoMip( ART_CONNECT0 );
+ trap_R_RegisterShaderNoMip( ART_CONNECT1 );
+ trap_R_RegisterShaderNoMip( ART_ARROWS0 );
+ trap_R_RegisterShaderNoMip( ART_ARROWS_UP );
+ trap_R_RegisterShaderNoMip( ART_ARROWS_DOWN );
+ trap_R_RegisterShaderNoMip( ART_UNKNOWNMAP );
+ trap_R_RegisterShaderNoMip( ART_PUNKBUSTER );
+}
+
+
+/*
+=================
+UI_ArenaServersMenu
+=================
+*/
+void UI_ArenaServersMenu( void ) {
+ ArenaServers_MenuInit();
+ UI_PushMenu( &g_arenaservers.menu );
+}
diff --git a/code/q3_ui/ui_setup.c b/code/q3_ui/ui_setup.c
new file mode 100644
index 0000000..290d1ce
--- /dev/null
+++ b/code/q3_ui/ui_setup.c
@@ -0,0 +1,327 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+SETUP MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define SETUP_MENU_VERTICAL_SPACING 34
+
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+
+#define ID_CUSTOMIZEPLAYER 10
+#define ID_CUSTOMIZECONTROLS 11
+#define ID_SYSTEMCONFIG 12
+#define ID_GAME 13
+#define ID_CDKEY 14
+#define ID_LOAD 15
+#define ID_SAVE 16
+#define ID_DEFAULTS 17
+#define ID_BACK 18
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menutext_s setupplayer;
+ menutext_s setupcontrols;
+ menutext_s setupsystem;
+ menutext_s game;
+ menutext_s cdkey;
+// menutext_s load;
+// menutext_s save;
+ menutext_s defaults;
+ menubitmap_s back;
+} setupMenuInfo_t;
+
+static setupMenuInfo_t setupMenuInfo;
+
+
+/*
+=================
+Setup_ResetDefaults_Action
+=================
+*/
+static void Setup_ResetDefaults_Action( qboolean result ) {
+ if( !result ) {
+ return;
+ }
+ trap_Cmd_ExecuteText( EXEC_APPEND, "exec default.cfg\n");
+ trap_Cmd_ExecuteText( EXEC_APPEND, "cvar_restart\n");
+ trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" );
+}
+
+
+/*
+=================
+Setup_ResetDefaults_Draw
+=================
+*/
+static void Setup_ResetDefaults_Draw( void ) {
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This will reset *ALL*", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "options to their default values.", UI_CENTER|UI_SMALLFONT, color_yellow );
+}
+
+
+/*
+===============
+UI_SetupMenu_Event
+===============
+*/
+static void UI_SetupMenu_Event( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_CUSTOMIZEPLAYER:
+ UI_PlayerSettingsMenu();
+ break;
+
+ case ID_CUSTOMIZECONTROLS:
+ UI_ControlsMenu();
+ break;
+
+ case ID_SYSTEMCONFIG:
+ UI_GraphicsOptionsMenu();
+ break;
+
+ case ID_GAME:
+ UI_PreferencesMenu();
+ break;
+
+ case ID_CDKEY:
+ UI_CDKeyMenu();
+ break;
+
+// case ID_LOAD:
+// UI_LoadConfigMenu();
+// break;
+
+// case ID_SAVE:
+// UI_SaveConfigMenu();
+// break;
+
+ case ID_DEFAULTS:
+ UI_ConfirmMenu( "SET TO DEFAULTS?", Setup_ResetDefaults_Draw, Setup_ResetDefaults_Action );
+ break;
+
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+UI_SetupMenu_Init
+===============
+*/
+static void UI_SetupMenu_Init( void ) {
+ int y;
+
+ UI_SetupMenu_Cache();
+
+ memset( &setupMenuInfo, 0, sizeof(setupMenuInfo) );
+ setupMenuInfo.menu.wrapAround = qtrue;
+ setupMenuInfo.menu.fullscreen = qtrue;
+
+ setupMenuInfo.banner.generic.type = MTYPE_BTEXT;
+ setupMenuInfo.banner.generic.x = 320;
+ setupMenuInfo.banner.generic.y = 16;
+ setupMenuInfo.banner.string = "SETUP";
+ setupMenuInfo.banner.color = color_white;
+ setupMenuInfo.banner.style = UI_CENTER;
+
+ setupMenuInfo.framel.generic.type = MTYPE_BITMAP;
+ setupMenuInfo.framel.generic.name = ART_FRAMEL;
+ setupMenuInfo.framel.generic.flags = QMF_INACTIVE;
+ setupMenuInfo.framel.generic.x = 0;
+ setupMenuInfo.framel.generic.y = 78;
+ setupMenuInfo.framel.width = 256;
+ setupMenuInfo.framel.height = 329;
+
+ setupMenuInfo.framer.generic.type = MTYPE_BITMAP;
+ setupMenuInfo.framer.generic.name = ART_FRAMER;
+ setupMenuInfo.framer.generic.flags = QMF_INACTIVE;
+ setupMenuInfo.framer.generic.x = 376;
+ setupMenuInfo.framer.generic.y = 76;
+ setupMenuInfo.framer.width = 256;
+ setupMenuInfo.framer.height = 334;
+
+ y = 134;
+ setupMenuInfo.setupplayer.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.setupplayer.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.setupplayer.generic.x = 320;
+ setupMenuInfo.setupplayer.generic.y = y;
+ setupMenuInfo.setupplayer.generic.id = ID_CUSTOMIZEPLAYER;
+ setupMenuInfo.setupplayer.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.setupplayer.string = "PLAYER";
+ setupMenuInfo.setupplayer.color = color_red;
+ setupMenuInfo.setupplayer.style = UI_CENTER;
+
+ y += SETUP_MENU_VERTICAL_SPACING;
+ setupMenuInfo.setupcontrols.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.setupcontrols.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.setupcontrols.generic.x = 320;
+ setupMenuInfo.setupcontrols.generic.y = y;
+ setupMenuInfo.setupcontrols.generic.id = ID_CUSTOMIZECONTROLS;
+ setupMenuInfo.setupcontrols.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.setupcontrols.string = "CONTROLS";
+ setupMenuInfo.setupcontrols.color = color_red;
+ setupMenuInfo.setupcontrols.style = UI_CENTER;
+
+ y += SETUP_MENU_VERTICAL_SPACING;
+ setupMenuInfo.setupsystem.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.setupsystem.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.setupsystem.generic.x = 320;
+ setupMenuInfo.setupsystem.generic.y = y;
+ setupMenuInfo.setupsystem.generic.id = ID_SYSTEMCONFIG;
+ setupMenuInfo.setupsystem.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.setupsystem.string = "SYSTEM";
+ setupMenuInfo.setupsystem.color = color_red;
+ setupMenuInfo.setupsystem.style = UI_CENTER;
+
+ y += SETUP_MENU_VERTICAL_SPACING;
+ setupMenuInfo.game.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.game.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.game.generic.x = 320;
+ setupMenuInfo.game.generic.y = y;
+ setupMenuInfo.game.generic.id = ID_GAME;
+ setupMenuInfo.game.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.game.string = "GAME OPTIONS";
+ setupMenuInfo.game.color = color_red;
+ setupMenuInfo.game.style = UI_CENTER;
+
+ y += SETUP_MENU_VERTICAL_SPACING;
+ setupMenuInfo.cdkey.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.cdkey.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.cdkey.generic.x = 320;
+ setupMenuInfo.cdkey.generic.y = y;
+ setupMenuInfo.cdkey.generic.id = ID_CDKEY;
+ setupMenuInfo.cdkey.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.cdkey.string = "CD Key";
+ setupMenuInfo.cdkey.color = color_red;
+ setupMenuInfo.cdkey.style = UI_CENTER;
+
+ if( !trap_Cvar_VariableValue( "cl_paused" ) ) {
+#if 0
+ y += SETUP_MENU_VERTICAL_SPACING;
+ setupMenuInfo.load.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.load.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.load.generic.x = 320;
+ setupMenuInfo.load.generic.y = y;
+ setupMenuInfo.load.generic.id = ID_LOAD;
+ setupMenuInfo.load.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.load.string = "LOAD";
+ setupMenuInfo.load.color = color_red;
+ setupMenuInfo.load.style = UI_CENTER;
+
+ y += SETUP_MENU_VERTICAL_SPACING;
+ setupMenuInfo.save.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.save.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.save.generic.x = 320;
+ setupMenuInfo.save.generic.y = y;
+ setupMenuInfo.save.generic.id = ID_SAVE;
+ setupMenuInfo.save.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.save.string = "SAVE";
+ setupMenuInfo.save.color = color_red;
+ setupMenuInfo.save.style = UI_CENTER;
+#endif
+
+ y += SETUP_MENU_VERTICAL_SPACING;
+ setupMenuInfo.defaults.generic.type = MTYPE_PTEXT;
+ setupMenuInfo.defaults.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.defaults.generic.x = 320;
+ setupMenuInfo.defaults.generic.y = y;
+ setupMenuInfo.defaults.generic.id = ID_DEFAULTS;
+ setupMenuInfo.defaults.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.defaults.string = "DEFAULTS";
+ setupMenuInfo.defaults.color = color_red;
+ setupMenuInfo.defaults.style = UI_CENTER;
+ }
+
+ setupMenuInfo.back.generic.type = MTYPE_BITMAP;
+ setupMenuInfo.back.generic.name = ART_BACK0;
+ setupMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ setupMenuInfo.back.generic.id = ID_BACK;
+ setupMenuInfo.back.generic.callback = UI_SetupMenu_Event;
+ setupMenuInfo.back.generic.x = 0;
+ setupMenuInfo.back.generic.y = 480-64;
+ setupMenuInfo.back.width = 128;
+ setupMenuInfo.back.height = 64;
+ setupMenuInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.banner );
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.framel );
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.framer );
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.setupplayer );
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.setupcontrols );
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.setupsystem );
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.game );
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.cdkey );
+// Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.load );
+// Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.save );
+ if( !trap_Cvar_VariableValue( "cl_paused" ) ) {
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.defaults );
+ }
+ Menu_AddItem( &setupMenuInfo.menu, &setupMenuInfo.back );
+}
+
+
+/*
+=================
+UI_SetupMenu_Cache
+=================
+*/
+void UI_SetupMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+}
+
+
+/*
+===============
+UI_SetupMenu
+===============
+*/
+void UI_SetupMenu( void ) {
+ UI_SetupMenu_Init();
+ UI_PushMenu( &setupMenuInfo.menu );
+}
diff --git a/code/q3_ui/ui_signup.c b/code/q3_ui/ui_signup.c
new file mode 100644
index 0000000..bdb97ec
--- /dev/null
+++ b/code/q3_ui/ui_signup.c
@@ -0,0 +1,286 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+//
+// ui_signup.c
+//
+
+#include "ui_local.h"
+
+
+#define SIGNUP_FRAME "menu/art/cut_frame"
+
+#define ID_NAME 100
+#define ID_NAME_BOX 101
+#define ID_PASSWORD 102
+#define ID_PASSWORD_BOX 103
+#define ID_AGAIN 104
+#define ID_AGAIN_BOX 105
+#define ID_EMAIL 106
+#define ID_EMAIL_BOX 107
+#define ID_SIGNUP 108
+#define ID_CANCEL 109
+
+
+typedef struct
+{
+ menuframework_s menu;
+ menubitmap_s frame;
+ menutext_s name;
+ menufield_s name_box;
+ menutext_s password;
+ menufield_s password_box;
+ menutext_s again;
+ menufield_s again_box;
+ menutext_s email;
+ menufield_s email_box;
+ menutext_s signup;
+ menutext_s cancel;
+} signup_t;
+
+static signup_t s_signup;
+
+static menuframework_s s_signup_menu;
+static menuaction_s s_signup_signup;
+static menuaction_s s_signup_cancel;
+
+static vec4_t s_signup_color_prompt = {1.00, 0.43, 0.00, 1.00};
+
+/*
+===============
+Signup_MenuEvent
+===============
+*/
+static void Signup_MenuEvent( void* ptr, int event ) {
+ //char cmd[1024];
+
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_SIGNUP:
+ if( strcmp(s_signup.password_box.field.buffer,
+ s_signup.again_box.field.buffer) != 0 )
+ {
+ // GRANK_FIXME - password mismatch
+ break;
+ }
+ // set name
+ //trap_Cvar_Set( "name", s_signup.name_box.field.buffer );
+ /*
+ trap_Cvar_Set( "rank_name", s_signup.name_box.field.buffer );
+ trap_Cvar_Set( "rank_pwd", s_signup.password_box.field.buffer );
+ */
+
+ // create account
+ /*
+ sprintf( cmd, "cmd rank_create \"%s\" \"%s\" \"%s\"\n",
+ s_signup.name_box.field.buffer,
+ s_signup.password_box.field.buffer,
+ s_signup.email_box.field.buffer );
+ trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
+ */
+ trap_CL_UI_RankUserCreate(
+ s_signup.name_box.field.buffer,
+ s_signup.password_box.field.buffer,
+ s_signup.email_box.field.buffer );
+
+ UI_ForceMenuOff();
+ break;
+
+ case ID_CANCEL:
+ UI_PopMenu();
+ break;
+ }
+}
+
+/*
+===============
+Signup_MenuInit
+===============
+*/
+void Signup_MenuInit( void ) {
+ grank_status_t status;
+ int y;
+
+ memset( &s_signup, 0, sizeof(s_signup) );
+
+ Signup_Cache();
+
+ s_signup.menu.wrapAround = qtrue;
+ s_signup.menu.fullscreen = qfalse;
+
+ s_signup.frame.generic.type = MTYPE_BITMAP;
+ s_signup.frame.generic.flags = QMF_INACTIVE;
+ s_signup.frame.generic.name = SIGNUP_FRAME;
+ s_signup.frame.generic.x = 142; //320-233;
+ s_signup.frame.generic.y = 118; //240-166;
+ s_signup.frame.width = 359; //466;
+ s_signup.frame.height = 256; //332;
+
+ y = 194;
+
+ s_signup.name.generic.type = MTYPE_PTEXT;
+ s_signup.name.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE;
+ s_signup.name.generic.id = ID_NAME;
+ s_signup.name.generic.x = 310;
+ s_signup.name.generic.y = y;
+ s_signup.name.string = "NAME";
+ s_signup.name.style = UI_RIGHT|UI_SMALLFONT;
+ s_signup.name.color = s_signup_color_prompt;
+
+ s_signup.name_box.generic.type = MTYPE_FIELD;
+ s_signup.name_box.generic.ownerdraw = Rankings_DrawName;
+ s_signup.name_box.generic.name = "";
+ s_signup.name_box.generic.flags = 0;
+ s_signup.name_box.generic.x = 330;
+ s_signup.name_box.generic.y = y;
+ s_signup.name_box.field.widthInChars = 16;
+ s_signup.name_box.field.maxchars = 16;
+ y += 20;
+
+ s_signup.password.generic.type = MTYPE_PTEXT;
+ s_signup.password.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE;
+ s_signup.password.generic.id = ID_PASSWORD;
+ s_signup.password.generic.x = 310;
+ s_signup.password.generic.y = y;
+ s_signup.password.string = "PASSWORD";
+ s_signup.password.style = UI_RIGHT|UI_SMALLFONT;
+ s_signup.password.color = s_signup_color_prompt;
+
+ s_signup.password_box.generic.type = MTYPE_FIELD;
+ s_signup.password_box.generic.ownerdraw = Rankings_DrawPassword;
+ s_signup.password_box.generic.name = "";
+ s_signup.password_box.generic.flags = 0;
+ s_signup.password_box.generic.x = 330;
+ s_signup.password_box.generic.y = y;
+ s_signup.password_box.field.widthInChars = 16;
+ s_signup.password_box.field.maxchars = 16;
+ y += 20;
+
+ s_signup.again.generic.type = MTYPE_PTEXT;
+ s_signup.again.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE;
+ s_signup.again.generic.id = ID_AGAIN;
+ s_signup.again.generic.x = 310;
+ s_signup.again.generic.y = y;
+ s_signup.again.string = "(AGAIN)";
+ s_signup.again.style = UI_RIGHT|UI_SMALLFONT;
+ s_signup.again.color = s_signup_color_prompt;
+
+ s_signup.again_box.generic.type = MTYPE_FIELD;
+ s_signup.again_box.generic.ownerdraw = Rankings_DrawPassword;
+ s_signup.again_box.generic.name = "";
+ s_signup.again_box.generic.flags = 0;
+ s_signup.again_box.generic.x = 330;
+ s_signup.again_box.generic.y = y;
+ s_signup.again_box.field.widthInChars = 16;
+ s_signup.again_box.field.maxchars = 16;
+ y += 20;
+
+ s_signup.email.generic.type = MTYPE_PTEXT;
+ s_signup.email.generic.flags = QMF_RIGHT_JUSTIFY|QMF_INACTIVE;
+ s_signup.email.generic.id = ID_EMAIL;
+ s_signup.email.generic.x = 310;
+ s_signup.email.generic.y = y;
+ s_signup.email.string = "EMAIL";
+ s_signup.email.style = UI_RIGHT|UI_SMALLFONT;
+ s_signup.email.color = s_signup_color_prompt;
+
+ s_signup.email_box.generic.type = MTYPE_FIELD;
+ s_signup.email_box.generic.ownerdraw = Rankings_DrawText;
+ s_signup.email_box.generic.name = "";
+ s_signup.email_box.generic.flags = 0;
+ s_signup.email_box.generic.x = 330;
+ s_signup.email_box.generic.y = y;
+ s_signup.email_box.field.widthInChars = 16;
+ s_signup.email_box.field.maxchars = MAX_EDIT_LINE;
+ y += 40;
+
+ s_signup.signup.generic.type = MTYPE_PTEXT;
+ s_signup.signup.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_signup.signup.generic.id = ID_SIGNUP;
+ s_signup.signup.generic.callback = Signup_MenuEvent;
+ s_signup.signup.generic.x = 310;
+ s_signup.signup.generic.y = y;
+ s_signup.signup.string = "SIGN UP";
+ s_signup.signup.style = UI_RIGHT|UI_SMALLFONT;
+ s_signup.signup.color = colorRed;
+
+ s_signup.cancel.generic.type = MTYPE_PTEXT;
+ s_signup.cancel.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_signup.cancel.generic.id = ID_CANCEL;
+ s_signup.cancel.generic.callback = Signup_MenuEvent;
+ s_signup.cancel.generic.x = 330;
+ s_signup.cancel.generic.y = y;
+ s_signup.cancel.string = "CANCEL";
+ s_signup.cancel.style = UI_LEFT|UI_SMALLFONT;
+ s_signup.cancel.color = colorRed;
+ y += 20;
+
+ status = (grank_status_t)trap_Cvar_VariableValue("client_status");
+ if( (status != QGR_STATUS_NEW) && (status != QGR_STATUS_SPECTATOR) )
+ {
+ s_signup.name_box.generic.flags |= QMF_INACTIVE;
+ s_signup.password_box.generic.flags |= QMF_INACTIVE;
+ s_signup.again_box.generic.flags |= QMF_INACTIVE;
+ s_signup.email_box.generic.flags |= QMF_INACTIVE;
+ s_signup.signup.generic.flags |= QMF_INACTIVE;
+
+ s_signup.signup.color = colorMdGrey;
+ }
+
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.frame );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.name );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.name_box );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.password );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.password_box );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.again );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.again_box );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.email );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.email_box );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.signup );
+ Menu_AddItem( &s_signup.menu, (void*) &s_signup.cancel );
+}
+
+
+/*
+===============
+Signup_Cache
+===============
+*/
+void Signup_Cache( void ) {
+ trap_R_RegisterShaderNoMip( SIGNUP_FRAME );
+}
+
+
+/*
+===============
+UI_SignupMenu
+===============
+*/
+void UI_SignupMenu( void ) {
+ Signup_MenuInit();
+ UI_PushMenu ( &s_signup.menu );
+}
+
+
diff --git a/code/q3_ui/ui_sound.c b/code/q3_ui/ui_sound.c
new file mode 100644
index 0000000..fcc7f49
--- /dev/null
+++ b/code/q3_ui/ui_sound.c
@@ -0,0 +1,316 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+SOUND OPTIONS MENU
+
+=======================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_FRAMEL "menu/art/frame2_l"
+#define ART_FRAMER "menu/art/frame1_r"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+
+#define ID_GRAPHICS 10
+#define ID_DISPLAY 11
+#define ID_SOUND 12
+#define ID_NETWORK 13
+#define ID_EFFECTSVOLUME 14
+#define ID_MUSICVOLUME 15
+#define ID_QUALITY 16
+//#define ID_A3D 17
+#define ID_BACK 18
+
+
+static const char *quality_items[] = {
+ "Low", "High", NULL
+};
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menutext_s graphics;
+ menutext_s display;
+ menutext_s sound;
+ menutext_s network;
+
+ menuslider_s sfxvolume;
+ menuslider_s musicvolume;
+ menulist_s quality;
+// menuradiobutton_s a3d;
+
+ menubitmap_s back;
+} soundOptionsInfo_t;
+
+static soundOptionsInfo_t soundOptionsInfo;
+
+
+/*
+=================
+UI_SoundOptionsMenu_Event
+=================
+*/
+static void UI_SoundOptionsMenu_Event( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_GRAPHICS:
+ UI_PopMenu();
+ UI_GraphicsOptionsMenu();
+ break;
+
+ case ID_DISPLAY:
+ UI_PopMenu();
+ UI_DisplayOptionsMenu();
+ break;
+
+ case ID_SOUND:
+ break;
+
+ case ID_NETWORK:
+ UI_PopMenu();
+ UI_NetworkOptionsMenu();
+ break;
+
+ case ID_EFFECTSVOLUME:
+ trap_Cvar_SetValue( "s_volume", soundOptionsInfo.sfxvolume.curvalue / 10 );
+ break;
+
+ case ID_MUSICVOLUME:
+ trap_Cvar_SetValue( "s_musicvolume", soundOptionsInfo.musicvolume.curvalue / 10 );
+ break;
+
+ case ID_QUALITY:
+ if( soundOptionsInfo.quality.curvalue ) {
+ trap_Cvar_SetValue( "s_khz", 22 );
+ trap_Cvar_SetValue( "s_compression", 0 );
+ }
+ else {
+ trap_Cvar_SetValue( "s_khz", 11 );
+ trap_Cvar_SetValue( "s_compression", 1 );
+ }
+ UI_ForceMenuOff();
+ trap_Cmd_ExecuteText( EXEC_APPEND, "snd_restart\n" );
+ break;
+/*
+ case ID_A3D:
+ if( soundOptionsInfo.a3d.curvalue ) {
+ trap_Cmd_ExecuteText( EXEC_NOW, "s_enable_a3d\n" );
+ }
+ else {
+ trap_Cmd_ExecuteText( EXEC_NOW, "s_disable_a3d\n" );
+ }
+ soundOptionsInfo.a3d.curvalue = (int)trap_Cvar_VariableValue( "s_usingA3D" );
+ break;
+*/
+ case ID_BACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+UI_SoundOptionsMenu_Init
+===============
+*/
+static void UI_SoundOptionsMenu_Init( void ) {
+ int y;
+
+ memset( &soundOptionsInfo, 0, sizeof(soundOptionsInfo) );
+
+ UI_SoundOptionsMenu_Cache();
+ soundOptionsInfo.menu.wrapAround = qtrue;
+ soundOptionsInfo.menu.fullscreen = qtrue;
+
+ soundOptionsInfo.banner.generic.type = MTYPE_BTEXT;
+ soundOptionsInfo.banner.generic.flags = QMF_CENTER_JUSTIFY;
+ soundOptionsInfo.banner.generic.x = 320;
+ soundOptionsInfo.banner.generic.y = 16;
+ soundOptionsInfo.banner.string = "SYSTEM SETUP";
+ soundOptionsInfo.banner.color = color_white;
+ soundOptionsInfo.banner.style = UI_CENTER;
+
+ soundOptionsInfo.framel.generic.type = MTYPE_BITMAP;
+ soundOptionsInfo.framel.generic.name = ART_FRAMEL;
+ soundOptionsInfo.framel.generic.flags = QMF_INACTIVE;
+ soundOptionsInfo.framel.generic.x = 0;
+ soundOptionsInfo.framel.generic.y = 78;
+ soundOptionsInfo.framel.width = 256;
+ soundOptionsInfo.framel.height = 329;
+
+ soundOptionsInfo.framer.generic.type = MTYPE_BITMAP;
+ soundOptionsInfo.framer.generic.name = ART_FRAMER;
+ soundOptionsInfo.framer.generic.flags = QMF_INACTIVE;
+ soundOptionsInfo.framer.generic.x = 376;
+ soundOptionsInfo.framer.generic.y = 76;
+ soundOptionsInfo.framer.width = 256;
+ soundOptionsInfo.framer.height = 334;
+
+ soundOptionsInfo.graphics.generic.type = MTYPE_PTEXT;
+ soundOptionsInfo.graphics.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ soundOptionsInfo.graphics.generic.id = ID_GRAPHICS;
+ soundOptionsInfo.graphics.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.graphics.generic.x = 216;
+ soundOptionsInfo.graphics.generic.y = 240 - 2 * PROP_HEIGHT;
+ soundOptionsInfo.graphics.string = "GRAPHICS";
+ soundOptionsInfo.graphics.style = UI_RIGHT;
+ soundOptionsInfo.graphics.color = color_red;
+
+ soundOptionsInfo.display.generic.type = MTYPE_PTEXT;
+ soundOptionsInfo.display.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ soundOptionsInfo.display.generic.id = ID_DISPLAY;
+ soundOptionsInfo.display.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.display.generic.x = 216;
+ soundOptionsInfo.display.generic.y = 240 - PROP_HEIGHT;
+ soundOptionsInfo.display.string = "DISPLAY";
+ soundOptionsInfo.display.style = UI_RIGHT;
+ soundOptionsInfo.display.color = color_red;
+
+ soundOptionsInfo.sound.generic.type = MTYPE_PTEXT;
+ soundOptionsInfo.sound.generic.flags = QMF_RIGHT_JUSTIFY;
+ soundOptionsInfo.sound.generic.id = ID_SOUND;
+ soundOptionsInfo.sound.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.sound.generic.x = 216;
+ soundOptionsInfo.sound.generic.y = 240;
+ soundOptionsInfo.sound.string = "SOUND";
+ soundOptionsInfo.sound.style = UI_RIGHT;
+ soundOptionsInfo.sound.color = color_red;
+
+ soundOptionsInfo.network.generic.type = MTYPE_PTEXT;
+ soundOptionsInfo.network.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ soundOptionsInfo.network.generic.id = ID_NETWORK;
+ soundOptionsInfo.network.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.network.generic.x = 216;
+ soundOptionsInfo.network.generic.y = 240 + PROP_HEIGHT;
+ soundOptionsInfo.network.string = "NETWORK";
+ soundOptionsInfo.network.style = UI_RIGHT;
+ soundOptionsInfo.network.color = color_red;
+
+ y = 240 - 1.5 * (BIGCHAR_HEIGHT + 2);
+ soundOptionsInfo.sfxvolume.generic.type = MTYPE_SLIDER;
+ soundOptionsInfo.sfxvolume.generic.name = "Effects Volume:";
+ soundOptionsInfo.sfxvolume.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ soundOptionsInfo.sfxvolume.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.sfxvolume.generic.id = ID_EFFECTSVOLUME;
+ soundOptionsInfo.sfxvolume.generic.x = 400;
+ soundOptionsInfo.sfxvolume.generic.y = y;
+ soundOptionsInfo.sfxvolume.minvalue = 0;
+ soundOptionsInfo.sfxvolume.maxvalue = 10;
+
+ y += BIGCHAR_HEIGHT+2;
+ soundOptionsInfo.musicvolume.generic.type = MTYPE_SLIDER;
+ soundOptionsInfo.musicvolume.generic.name = "Music Volume:";
+ soundOptionsInfo.musicvolume.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ soundOptionsInfo.musicvolume.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.musicvolume.generic.id = ID_MUSICVOLUME;
+ soundOptionsInfo.musicvolume.generic.x = 400;
+ soundOptionsInfo.musicvolume.generic.y = y;
+ soundOptionsInfo.musicvolume.minvalue = 0;
+ soundOptionsInfo.musicvolume.maxvalue = 10;
+
+ y += BIGCHAR_HEIGHT+2;
+ soundOptionsInfo.quality.generic.type = MTYPE_SPINCONTROL;
+ soundOptionsInfo.quality.generic.name = "Sound Quality:";
+ soundOptionsInfo.quality.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ soundOptionsInfo.quality.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.quality.generic.id = ID_QUALITY;
+ soundOptionsInfo.quality.generic.x = 400;
+ soundOptionsInfo.quality.generic.y = y;
+ soundOptionsInfo.quality.itemnames = quality_items;
+/*
+ y += BIGCHAR_HEIGHT+2;
+ soundOptionsInfo.a3d.generic.type = MTYPE_RADIOBUTTON;
+ soundOptionsInfo.a3d.generic.name = "A3D:";
+ soundOptionsInfo.a3d.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ soundOptionsInfo.a3d.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.a3d.generic.id = ID_A3D;
+ soundOptionsInfo.a3d.generic.x = 400;
+ soundOptionsInfo.a3d.generic.y = y;
+*/
+ soundOptionsInfo.back.generic.type = MTYPE_BITMAP;
+ soundOptionsInfo.back.generic.name = ART_BACK0;
+ soundOptionsInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ soundOptionsInfo.back.generic.callback = UI_SoundOptionsMenu_Event;
+ soundOptionsInfo.back.generic.id = ID_BACK;
+ soundOptionsInfo.back.generic.x = 0;
+ soundOptionsInfo.back.generic.y = 480-64;
+ soundOptionsInfo.back.width = 128;
+ soundOptionsInfo.back.height = 64;
+ soundOptionsInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.banner );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.framel );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.framer );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.graphics );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.display );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.sound );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.network );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.sfxvolume );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.musicvolume );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.quality );
+// Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.a3d );
+ Menu_AddItem( &soundOptionsInfo.menu, ( void * ) &soundOptionsInfo.back );
+
+ soundOptionsInfo.sfxvolume.curvalue = trap_Cvar_VariableValue( "s_volume" ) * 10;
+ soundOptionsInfo.musicvolume.curvalue = trap_Cvar_VariableValue( "s_musicvolume" ) * 10;
+ soundOptionsInfo.quality.curvalue = !trap_Cvar_VariableValue( "s_compression" );
+// soundOptionsInfo.a3d.curvalue = (int)trap_Cvar_VariableValue( "s_usingA3D" );
+}
+
+
+/*
+===============
+UI_SoundOptionsMenu_Cache
+===============
+*/
+void UI_SoundOptionsMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAMEL );
+ trap_R_RegisterShaderNoMip( ART_FRAMER );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+}
+
+
+/*
+===============
+UI_SoundOptionsMenu
+===============
+*/
+void UI_SoundOptionsMenu( void ) {
+ UI_SoundOptionsMenu_Init();
+ UI_PushMenu( &soundOptionsInfo.menu );
+ Menu_SetCursorToItem( &soundOptionsInfo.menu, &soundOptionsInfo.sound );
+}
diff --git a/code/q3_ui/ui_sparena.c b/code/q3_ui/ui_sparena.c
new file mode 100644
index 0000000..157670e
--- /dev/null
+++ b/code/q3_ui/ui_sparena.c
@@ -0,0 +1,50 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+void UI_SPArena_Start( const char *arenaInfo ) {
+ char *map;
+ int level;
+ int n;
+ char *txt;
+
+ n = (int)trap_Cvar_VariableValue( "sv_maxclients" );
+ if ( n < 8 ) {
+ trap_Cvar_SetValue( "sv_maxclients", 8 );
+ }
+
+ level = atoi( Info_ValueForKey( arenaInfo, "num" ) );
+ txt = Info_ValueForKey( arenaInfo, "special" );
+ if( txt[0] ) {
+ if( Q_stricmp( txt, "training" ) == 0 ) {
+ level = -4;
+ }
+ else if( Q_stricmp( txt, "final" ) == 0 ) {
+ level = UI_GetNumSPTiers() * ARENAS_PER_TIER;
+ }
+ }
+ trap_Cvar_SetValue( "ui_spSelection", level );
+
+ map = Info_ValueForKey( arenaInfo, "map" );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "spmap %s\n", map ) );
+}
diff --git a/code/q3_ui/ui_specifyleague.c b/code/q3_ui/ui_specifyleague.c
new file mode 100644
index 0000000..a3d88e9
--- /dev/null
+++ b/code/q3_ui/ui_specifyleague.c
@@ -0,0 +1,333 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+/*********************************************************************************
+ SPECIFY SERVER
+*********************************************************************************/
+
+#define MAX_LISTBOXITEMS 128
+#define MAX_LISTBOXWIDTH 40
+#define MAX_LEAGUENAME 80
+
+#define SPECIFYLEAGUE_FRAMEL "menu/art/frame2_l"
+#define SPECIFYLEAGUE_FRAMER "menu/art/frame1_r"
+#define SPECIFYLEAGUE_BACK0 "menu/art/back_0"
+#define SPECIFYLEAGUE_BACK1 "menu/art/back_1"
+#define SPECIFYLEAGUE_ARROWS0 "menu/art/arrows_vert_0"
+#define SPECIFYLEAGUE_UP "menu/art/arrows_vert_top"
+#define SPECIFYLEAGUE_DOWN "menu/art/arrows_vert_bot"
+#define GLOBALRANKINGS_LOGO "menu/art/gr/grlogo"
+#define GLOBALRANKINGS_LETTERS "menu/art/gr/grletters"
+
+#define ID_SPECIFYLEAGUENAME 100
+#define ID_SPECIFYLEAGUELIST 101
+#define ID_SPECIFYLEAGUEUP 102
+#define ID_SPECIFYLEAGUEDOWN 103
+#define ID_SPECIFYLEAGUEBACK 104
+
+static char* specifyleague_artlist[] =
+{
+ SPECIFYLEAGUE_FRAMEL,
+ SPECIFYLEAGUE_FRAMER,
+ SPECIFYLEAGUE_ARROWS0,
+ SPECIFYLEAGUE_UP,
+ SPECIFYLEAGUE_DOWN,
+ SPECIFYLEAGUE_BACK0,
+ SPECIFYLEAGUE_BACK1,
+ GLOBALRANKINGS_LOGO,
+ GLOBALRANKINGS_LETTERS,
+ NULL
+};
+
+static char playername[80];
+
+typedef struct
+{
+ menuframework_s menu;
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menufield_s rankname;
+ menulist_s list;
+ menubitmap_s arrows;
+ menubitmap_s up;
+ menubitmap_s down;
+ menubitmap_s back;
+ menubitmap_s grlogo;
+ menubitmap_s grletters;
+} specifyleague_t;
+
+static specifyleague_t s_specifyleague;
+
+
+typedef struct {
+ char buff[MAX_LISTBOXWIDTH];
+ char leaguename[MAX_LEAGUENAME];
+} table_t;
+
+table_t league_table[MAX_LISTBOXITEMS];
+char *leaguename_items[MAX_LISTBOXITEMS];
+
+
+static void SpecifyLeague_GetList()
+{
+ int count = 0;
+ int i;
+ /* The Player Name has changed. We need to perform another search */
+ Q_strncpyz( playername,
+ s_specifyleague.rankname.field.buffer,
+ sizeof(playername) );
+
+ count = trap_CL_UI_RankGetLeauges( playername );
+
+ for(i = 0; i < count; i++)
+ {
+ char s[MAX_LEAGUENAME];
+ const char *var;
+ var = va( "leaguename%i", i+1 );
+ trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
+ Q_strncpyz(league_table[i].leaguename, s, sizeof(league_table[i].leaguename) );
+ Q_strncpyz(league_table[i].buff, league_table[i].leaguename, sizeof(league_table[i].buff) );
+ }
+
+ s_specifyleague.list.numitems = count;
+}
+
+/*
+=================
+SpecifyLeague_Event
+=================
+*/
+static void SpecifyLeague_Event( void* ptr, int event )
+{
+ int id;
+ id = ((menucommon_s*)ptr)->id;
+ //if( event != QM_ACTIVATED && id != ID_SPECIFYLEAGUELIST ) {
+ // return;
+ //}
+
+ switch (id)
+ {
+ case ID_SPECIFYLEAGUELIST:
+ if( event == QM_GOTFOCUS ) {
+ //ArenaServers_UpdatePicture();
+ }
+ break;
+
+ case ID_SPECIFYLEAGUEUP:
+ if( event == QM_ACTIVATED )
+ ScrollList_Key( &s_specifyleague.list, K_UPARROW );
+ break;
+
+ case ID_SPECIFYLEAGUEDOWN:
+ if( event == QM_ACTIVATED )
+ ScrollList_Key( &s_specifyleague.list, K_DOWNARROW );
+ break;
+
+ case ID_SPECIFYLEAGUENAME:
+ if( (event == QM_LOSTFOCUS) &&
+ (Q_strncmp(playername,
+ s_specifyleague.rankname.field.buffer,
+ strlen(s_specifyleague.rankname.field.buffer)) != 0))
+ {
+ SpecifyLeague_GetList();
+ }
+ break;
+
+ case ID_SPECIFYLEAGUEBACK:
+ if( event == QM_ACTIVATED )
+ {
+ trap_Cvar_Set( "sv_leagueName", league_table[s_specifyleague.list.curvalue].leaguename);
+ UI_PopMenu();
+ }
+ break;
+ }
+}
+
+/*
+=================
+SpecifyLeague_MenuInit
+=================
+*/
+void SpecifyLeague_MenuInit( void )
+{
+ int i;
+ // zero set all our globals
+ memset( &s_specifyleague, 0 ,sizeof(specifyleague_t) );
+
+ SpecifyLeague_Cache();
+
+ s_specifyleague.menu.wrapAround = qtrue;
+ s_specifyleague.menu.fullscreen = qtrue;
+
+ s_specifyleague.banner.generic.type = MTYPE_BTEXT;
+ s_specifyleague.banner.generic.x = 320;
+ s_specifyleague.banner.generic.y = 16;
+ s_specifyleague.banner.string = "CHOOSE LEAGUE";
+ s_specifyleague.banner.color = color_white;
+ s_specifyleague.banner.style = UI_CENTER;
+
+ s_specifyleague.framel.generic.type = MTYPE_BITMAP;
+ s_specifyleague.framel.generic.name = SPECIFYLEAGUE_FRAMEL;
+ s_specifyleague.framel.generic.flags = QMF_INACTIVE;
+ s_specifyleague.framel.generic.x = 0;
+ s_specifyleague.framel.generic.y = 78;
+ s_specifyleague.framel.width = 256;
+ s_specifyleague.framel.height = 334;
+
+ s_specifyleague.framer.generic.type = MTYPE_BITMAP;
+ s_specifyleague.framer.generic.name = SPECIFYLEAGUE_FRAMER;
+ s_specifyleague.framer.generic.flags = QMF_INACTIVE;
+ s_specifyleague.framer.generic.x = 376;
+ s_specifyleague.framer.generic.y = 76;
+ s_specifyleague.framer.width = 256;
+ s_specifyleague.framer.height = 334;
+
+ s_specifyleague.grlogo.generic.type = MTYPE_BITMAP;
+ s_specifyleague.grlogo.generic.name = GLOBALRANKINGS_LOGO;
+ s_specifyleague.grlogo.generic.flags = QMF_INACTIVE;
+ s_specifyleague.grlogo.generic.x = 0;
+ s_specifyleague.grlogo.generic.y = 0;
+ s_specifyleague.grlogo.width = 64;
+ s_specifyleague.grlogo.height = 128;
+
+ s_specifyleague.rankname.generic.type = MTYPE_FIELD;
+ s_specifyleague.rankname.generic.name = "Player Name:";
+ s_specifyleague.rankname.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_specifyleague.rankname.generic.callback = SpecifyLeague_Event;
+ s_specifyleague.rankname.generic.id = ID_SPECIFYLEAGUENAME;
+ s_specifyleague.rankname.generic.x = 226;
+ s_specifyleague.rankname.generic.y = 128;
+ s_specifyleague.rankname.field.widthInChars = 32;
+ s_specifyleague.rankname.field.maxchars = 80;
+
+ s_specifyleague.list.generic.type = MTYPE_SCROLLLIST;
+ s_specifyleague.list.generic.flags = QMF_HIGHLIGHT_IF_FOCUS;
+ s_specifyleague.list.generic.id = ID_SPECIFYLEAGUELIST;
+ s_specifyleague.list.generic.callback = SpecifyLeague_Event;
+ s_specifyleague.list.generic.x = 160;
+ s_specifyleague.list.generic.y = 200;
+ s_specifyleague.list.width = MAX_LISTBOXWIDTH;
+ s_specifyleague.list.height = 8;
+ s_specifyleague.list.itemnames = (const char **)leaguename_items;
+ s_specifyleague.list.numitems = 0;
+ for( i = 0; i < MAX_LISTBOXITEMS; i++ ) {
+ league_table[i].buff[0] = 0;
+ league_table[i].leaguename[0] = 0;
+ leaguename_items[i] = league_table[i].buff;
+ }
+
+ s_specifyleague.arrows.generic.type = MTYPE_BITMAP;
+ s_specifyleague.arrows.generic.name = SPECIFYLEAGUE_ARROWS0;
+ s_specifyleague.arrows.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_specifyleague.arrows.generic.callback = SpecifyLeague_Event;
+ s_specifyleague.arrows.generic.x = 512;
+ s_specifyleague.arrows.generic.y = 240-64+16;
+ s_specifyleague.arrows.width = 64;
+ s_specifyleague.arrows.height = 128;
+
+ s_specifyleague.up.generic.type = MTYPE_BITMAP;
+ s_specifyleague.up.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_specifyleague.up.generic.callback = SpecifyLeague_Event;
+ s_specifyleague.up.generic.id = ID_SPECIFYLEAGUEUP;
+ s_specifyleague.up.generic.x = 512;
+ s_specifyleague.up.generic.y = 240-64+16;
+ s_specifyleague.up.width = 64;
+ s_specifyleague.up.height = 64;
+ s_specifyleague.up.focuspic = SPECIFYLEAGUE_UP;
+
+ s_specifyleague.down.generic.type = MTYPE_BITMAP;
+ s_specifyleague.down.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_MOUSEONLY;
+ s_specifyleague.down.generic.callback = SpecifyLeague_Event;
+ s_specifyleague.down.generic.id = ID_SPECIFYLEAGUEDOWN;
+ s_specifyleague.down.generic.x = 512;
+ s_specifyleague.down.generic.y = 240+16;
+ s_specifyleague.down.width = 64;
+ s_specifyleague.down.height = 64;
+ s_specifyleague.down.focuspic = SPECIFYLEAGUE_DOWN;
+
+ s_specifyleague.back.generic.type = MTYPE_BITMAP;
+ s_specifyleague.back.generic.name = SPECIFYLEAGUE_BACK0;
+ s_specifyleague.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_specifyleague.back.generic.callback = SpecifyLeague_Event;
+ s_specifyleague.back.generic.id = ID_SPECIFYLEAGUEBACK;
+ s_specifyleague.back.generic.x = 0;
+ s_specifyleague.back.generic.y = 480-64;
+ s_specifyleague.back.width = 128;
+ s_specifyleague.back.height = 64;
+ s_specifyleague.back.focuspic = SPECIFYLEAGUE_BACK1;
+
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.banner );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.framel );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.framer );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.grlogo );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.rankname );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.list );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.arrows );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.up );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.down );
+ Menu_AddItem( &s_specifyleague.menu, &s_specifyleague.back );
+
+
+ // initialize any menu variables
+ Q_strncpyz( s_specifyleague.rankname.field.buffer,
+ UI_Cvar_VariableString("name"),
+ sizeof(s_specifyleague.rankname.field.buffer) );
+
+ Q_strncpyz( playername,
+ UI_Cvar_VariableString("name"),
+ sizeof(playername) );
+
+ SpecifyLeague_GetList();
+}
+
+/*
+=================
+SpecifyLeague_Cache
+=================
+*/
+void SpecifyLeague_Cache( void )
+{
+ int i;
+
+ // touch all our pics
+ for (i=0; ;i++)
+ {
+ if (!specifyleague_artlist[i])
+ break;
+ trap_R_RegisterShaderNoMip(specifyleague_artlist[i]);
+ }
+}
+
+/*
+=================
+UI_SpecifyLeagueMenu
+=================
+*/
+void UI_SpecifyLeagueMenu( void )
+{
+ SpecifyLeague_MenuInit();
+ UI_PushMenu( &s_specifyleague.menu );
+}
+
diff --git a/code/q3_ui/ui_specifyserver.c b/code/q3_ui/ui_specifyserver.c
new file mode 100644
index 0000000..784bc7a
--- /dev/null
+++ b/code/q3_ui/ui_specifyserver.c
@@ -0,0 +1,213 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+/*********************************************************************************
+ SPECIFY SERVER
+*********************************************************************************/
+
+#define SPECIFYSERVER_FRAMEL "menu/art/frame2_l"
+#define SPECIFYSERVER_FRAMER "menu/art/frame1_r"
+#define SPECIFYSERVER_BACK0 "menu/art/back_0"
+#define SPECIFYSERVER_BACK1 "menu/art/back_1"
+#define SPECIFYSERVER_FIGHT0 "menu/art/fight_0"
+#define SPECIFYSERVER_FIGHT1 "menu/art/fight_1"
+
+#define ID_SPECIFYSERVERBACK 102
+#define ID_SPECIFYSERVERGO 103
+
+static char* specifyserver_artlist[] =
+{
+ SPECIFYSERVER_FRAMEL,
+ SPECIFYSERVER_FRAMER,
+ SPECIFYSERVER_BACK0,
+ SPECIFYSERVER_BACK1,
+ SPECIFYSERVER_FIGHT0,
+ SPECIFYSERVER_FIGHT1,
+ NULL
+};
+
+typedef struct
+{
+ menuframework_s menu;
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ menufield_s domain;
+ menufield_s port;
+ menubitmap_s go;
+ menubitmap_s back;
+} specifyserver_t;
+
+static specifyserver_t s_specifyserver;
+
+/*
+=================
+SpecifyServer_Event
+=================
+*/
+static void SpecifyServer_Event( void* ptr, int event )
+{
+ char buff[256];
+
+ switch (((menucommon_s*)ptr)->id)
+ {
+ case ID_SPECIFYSERVERGO:
+ if (event != QM_ACTIVATED)
+ break;
+
+ if (s_specifyserver.domain.field.buffer[0])
+ {
+ strcpy(buff,s_specifyserver.domain.field.buffer);
+ if (s_specifyserver.port.field.buffer[0])
+ Com_sprintf( buff+strlen(buff), 128, ":%s", s_specifyserver.port.field.buffer );
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", buff ) );
+ }
+ break;
+
+ case ID_SPECIFYSERVERBACK:
+ if (event != QM_ACTIVATED)
+ break;
+
+ UI_PopMenu();
+ break;
+ }
+}
+
+/*
+=================
+SpecifyServer_MenuInit
+=================
+*/
+void SpecifyServer_MenuInit( void )
+{
+ // zero set all our globals
+ memset( &s_specifyserver, 0 ,sizeof(specifyserver_t) );
+
+ SpecifyServer_Cache();
+
+ s_specifyserver.menu.wrapAround = qtrue;
+ s_specifyserver.menu.fullscreen = qtrue;
+
+ s_specifyserver.banner.generic.type = MTYPE_BTEXT;
+ s_specifyserver.banner.generic.x = 320;
+ s_specifyserver.banner.generic.y = 16;
+ s_specifyserver.banner.string = "SPECIFY SERVER";
+ s_specifyserver.banner.color = color_white;
+ s_specifyserver.banner.style = UI_CENTER;
+
+ s_specifyserver.framel.generic.type = MTYPE_BITMAP;
+ s_specifyserver.framel.generic.name = SPECIFYSERVER_FRAMEL;
+ s_specifyserver.framel.generic.flags = QMF_INACTIVE;
+ s_specifyserver.framel.generic.x = 0;
+ s_specifyserver.framel.generic.y = 78;
+ s_specifyserver.framel.width = 256;
+ s_specifyserver.framel.height = 329;
+
+ s_specifyserver.framer.generic.type = MTYPE_BITMAP;
+ s_specifyserver.framer.generic.name = SPECIFYSERVER_FRAMER;
+ s_specifyserver.framer.generic.flags = QMF_INACTIVE;
+ s_specifyserver.framer.generic.x = 376;
+ s_specifyserver.framer.generic.y = 76;
+ s_specifyserver.framer.width = 256;
+ s_specifyserver.framer.height = 334;
+
+ s_specifyserver.domain.generic.type = MTYPE_FIELD;
+ s_specifyserver.domain.generic.name = "Address:";
+ s_specifyserver.domain.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_specifyserver.domain.generic.x = 206;
+ s_specifyserver.domain.generic.y = 220;
+ s_specifyserver.domain.field.widthInChars = 38;
+ s_specifyserver.domain.field.maxchars = 80;
+
+ s_specifyserver.port.generic.type = MTYPE_FIELD;
+ s_specifyserver.port.generic.name = "Port:";
+ s_specifyserver.port.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT|QMF_NUMBERSONLY;
+ s_specifyserver.port.generic.x = 206;
+ s_specifyserver.port.generic.y = 250;
+ s_specifyserver.port.field.widthInChars = 6;
+ s_specifyserver.port.field.maxchars = 5;
+
+ s_specifyserver.go.generic.type = MTYPE_BITMAP;
+ s_specifyserver.go.generic.name = SPECIFYSERVER_FIGHT0;
+ s_specifyserver.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_specifyserver.go.generic.callback = SpecifyServer_Event;
+ s_specifyserver.go.generic.id = ID_SPECIFYSERVERGO;
+ s_specifyserver.go.generic.x = 640;
+ s_specifyserver.go.generic.y = 480-64;
+ s_specifyserver.go.width = 128;
+ s_specifyserver.go.height = 64;
+ s_specifyserver.go.focuspic = SPECIFYSERVER_FIGHT1;
+
+ s_specifyserver.back.generic.type = MTYPE_BITMAP;
+ s_specifyserver.back.generic.name = SPECIFYSERVER_BACK0;
+ s_specifyserver.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_specifyserver.back.generic.callback = SpecifyServer_Event;
+ s_specifyserver.back.generic.id = ID_SPECIFYSERVERBACK;
+ s_specifyserver.back.generic.x = 0;
+ s_specifyserver.back.generic.y = 480-64;
+ s_specifyserver.back.width = 128;
+ s_specifyserver.back.height = 64;
+ s_specifyserver.back.focuspic = SPECIFYSERVER_BACK1;
+
+ Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.banner );
+ Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.framel );
+ Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.framer );
+ Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.domain );
+ Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.port );
+ Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.go );
+ Menu_AddItem( &s_specifyserver.menu, &s_specifyserver.back );
+
+ Com_sprintf( s_specifyserver.port.field.buffer, 6, "%i", 27960 );
+}
+
+/*
+=================
+SpecifyServer_Cache
+=================
+*/
+void SpecifyServer_Cache( void )
+{
+ int i;
+
+ // touch all our pics
+ for (i=0; ;i++)
+ {
+ if (!specifyserver_artlist[i])
+ break;
+ trap_R_RegisterShaderNoMip(specifyserver_artlist[i]);
+ }
+}
+
+/*
+=================
+UI_SpecifyServerMenu
+=================
+*/
+void UI_SpecifyServerMenu( void )
+{
+ SpecifyServer_MenuInit();
+ UI_PushMenu( &s_specifyserver.menu );
+}
+
diff --git a/code/q3_ui/ui_splevel.c b/code/q3_ui/ui_splevel.c
new file mode 100644
index 0000000..0c3b08f
--- /dev/null
+++ b/code/q3_ui/ui_splevel.c
@@ -0,0 +1,1013 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=============================================================================
+
+SINGLE PLAYER LEVEL SELECT MENU
+
+=============================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_LEVELFRAME_FOCUS "menu/art/maps_select"
+#define ART_LEVELFRAME_SELECTED "menu/art/maps_selected"
+#define ART_ARROW "menu/art/narrow_0"
+#define ART_ARROW_FOCUS "menu/art/narrow_1"
+#define ART_MAP_UNKNOWN "menu/art/unknownmap"
+#define ART_MAP_COMPLETE1 "menu/art/level_complete1"
+#define ART_MAP_COMPLETE2 "menu/art/level_complete2"
+#define ART_MAP_COMPLETE3 "menu/art/level_complete3"
+#define ART_MAP_COMPLETE4 "menu/art/level_complete4"
+#define ART_MAP_COMPLETE5 "menu/art/level_complete5"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+#define ART_FIGHT0 "menu/art/fight_0"
+#define ART_FIGHT1 "menu/art/fight_1"
+#define ART_RESET0 "menu/art/reset_0"
+#define ART_RESET1 "menu/art/reset_1"
+#define ART_CUSTOM0 "menu/art/skirmish_0"
+#define ART_CUSTOM1 "menu/art/skirmish_1"
+
+#define ID_LEFTARROW 10
+#define ID_PICTURE0 11
+#define ID_PICTURE1 12
+#define ID_PICTURE2 13
+#define ID_PICTURE3 14
+#define ID_RIGHTARROW 15
+#define ID_PLAYERPIC 16
+#define ID_AWARD1 17
+#define ID_AWARD2 18
+#define ID_AWARD3 19
+#define ID_AWARD4 20
+#define ID_AWARD5 21
+#define ID_AWARD6 22
+#define ID_BACK 23
+#define ID_RESET 24
+#define ID_CUSTOM 25
+#define ID_NEXT 26
+
+#define PLAYER_Y 314
+#define AWARDS_Y (PLAYER_Y + 26)
+
+
+typedef struct {
+ menuframework_s menu;
+ menutext_s item_banner;
+ menubitmap_s item_leftarrow;
+ menubitmap_s item_maps[4];
+ menubitmap_s item_rightarrow;
+ menubitmap_s item_player;
+ menubitmap_s item_awards[6];
+ menubitmap_s item_back;
+ menubitmap_s item_reset;
+ menubitmap_s item_custom;
+ menubitmap_s item_next;
+ menubitmap_s item_null;
+
+ qboolean reinit;
+
+ const char * selectedArenaInfo;
+ int numMaps;
+ char levelPicNames[4][MAX_QPATH];
+ char levelNames[4][16];
+ int levelScores[4];
+ int levelScoresSkill[4];
+ qhandle_t levelSelectedPic;
+ qhandle_t levelFocusPic;
+ qhandle_t levelCompletePic[5];
+
+ char playerModel[MAX_QPATH];
+ char playerPicName[MAX_QPATH];
+ int awardLevels[6];
+ sfxHandle_t awardSounds[6];
+
+ int numBots;
+ qhandle_t botPics[7];
+ char botNames[7][10];
+} levelMenuInfo_t;
+
+static levelMenuInfo_t levelMenuInfo;
+
+static int selectedArenaSet;
+static int selectedArena;
+static int currentSet;
+static int currentGame;
+static int trainingTier;
+static int finalTier;
+static int minTier;
+static int maxTier;
+
+
+/*
+=================
+PlayerIcon
+=================
+*/
+static void PlayerIcon( const char *modelAndSkin, char *iconName, int iconNameMaxSize ) {
+ char *skin;
+ char model[MAX_QPATH];
+
+ Q_strncpyz( model, modelAndSkin, sizeof(model));
+ skin = Q_strrchr( model, '/' );
+ if ( skin ) {
+ *skin++ = '\0';
+ }
+ else {
+ skin = "default";
+ }
+
+ Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_%s.tga", model, skin );
+
+ if( !trap_R_RegisterShaderNoMip( iconName ) && Q_stricmp( skin, "default" ) != 0 ) {
+ Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_default.tga", model );
+ }
+}
+
+
+/*
+=================
+PlayerIconhandle
+=================
+*/
+static qhandle_t PlayerIconHandle( const char *modelAndSkin ) {
+ char iconName[MAX_QPATH];
+
+ PlayerIcon( modelAndSkin, iconName, sizeof(iconName) );
+ return trap_R_RegisterShaderNoMip( iconName );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_SetBots
+=================
+*/
+static void UI_SPLevelMenu_SetBots( void ) {
+ char *p;
+ char *bot;
+ char *botInfo;
+ char bots[MAX_INFO_STRING];
+
+ levelMenuInfo.numBots = 0;
+ if ( selectedArenaSet > currentSet ) {
+ return;
+ }
+
+ Q_strncpyz( bots, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "bots" ), sizeof(bots) );
+
+ p = &bots[0];
+ while( *p && levelMenuInfo.numBots < 7 ) {
+ //skip spaces
+ while( *p && *p == ' ' ) {
+ p++;
+ }
+ if( !p ) {
+ break;
+ }
+
+ // mark start of bot name
+ bot = p;
+
+ // skip until space of null
+ while( *p && *p != ' ' ) {
+ p++;
+ }
+ if( *p ) {
+ *p++ = 0;
+ }
+
+ botInfo = UI_GetBotInfoByName( bot );
+ if(!botInfo)
+ {
+ botInfo = UI_GetBotInfoByNumber( levelMenuInfo.numBots );
+ }
+
+ if( botInfo ) {
+ levelMenuInfo.botPics[levelMenuInfo.numBots] = PlayerIconHandle( Info_ValueForKey( botInfo, "model" ) );
+ Q_strncpyz( levelMenuInfo.botNames[levelMenuInfo.numBots], Info_ValueForKey( botInfo, "name" ), 10 );
+ }
+ else {
+ levelMenuInfo.botPics[levelMenuInfo.numBots] = 0;
+ Q_strncpyz( levelMenuInfo.botNames[levelMenuInfo.numBots], bot, 10 );
+ }
+ Q_CleanStr( levelMenuInfo.botNames[levelMenuInfo.numBots] );
+ levelMenuInfo.numBots++;
+ }
+}
+
+
+/*
+=================
+UI_SPLevelMenu_SetMenuItems
+=================
+*/
+static void UI_SPLevelMenu_SetMenuArena( int n, int level, const char *arenaInfo ) {
+ char map[MAX_QPATH];
+
+ Q_strncpyz( map, Info_ValueForKey( arenaInfo, "map" ), sizeof(map) );
+
+ Q_strncpyz( levelMenuInfo.levelNames[n], map, sizeof(levelMenuInfo.levelNames[n]) );
+ Q_strupr( levelMenuInfo.levelNames[n] );
+
+ UI_GetBestScore( level, &levelMenuInfo.levelScores[n], &levelMenuInfo.levelScoresSkill[n] );
+ if( levelMenuInfo.levelScores[n] > 8 ) {
+ levelMenuInfo.levelScores[n] = 8;
+ }
+
+ strcpy( levelMenuInfo.levelPicNames[n], va( "levelshots/%s.tga", map ) );
+ if( !trap_R_RegisterShaderNoMip( levelMenuInfo.levelPicNames[n] ) ) {
+ strcpy( levelMenuInfo.levelPicNames[n], ART_MAP_UNKNOWN );
+ }
+ levelMenuInfo.item_maps[n].shader = 0;
+ if ( selectedArenaSet > currentSet ) {
+ levelMenuInfo.item_maps[n].generic.flags |= QMF_GRAYED;
+ }
+ else {
+ levelMenuInfo.item_maps[n].generic.flags &= ~QMF_GRAYED;
+ }
+
+ levelMenuInfo.item_maps[n].generic.flags &= ~QMF_INACTIVE;
+}
+
+static void UI_SPLevelMenu_SetMenuItems( void ) {
+ int n;
+ int level;
+ const char *arenaInfo;
+
+ if ( selectedArenaSet > currentSet ) {
+ selectedArena = -1;
+ }
+ else if ( selectedArena == -1 ) {
+ selectedArena = 0;
+ }
+
+ if( selectedArenaSet == trainingTier || selectedArenaSet == finalTier ) {
+ selectedArena = 0;
+ }
+
+ if( selectedArena != -1 ) {
+ trap_Cvar_SetValue( "ui_spSelection", selectedArenaSet * ARENAS_PER_TIER + selectedArena );
+ }
+
+ if( selectedArenaSet == trainingTier ) {
+ arenaInfo = UI_GetSpecialArenaInfo( "training" );
+ level = atoi( Info_ValueForKey( arenaInfo, "num" ) );
+ UI_SPLevelMenu_SetMenuArena( 0, level, arenaInfo );
+ levelMenuInfo.selectedArenaInfo = arenaInfo;
+
+ levelMenuInfo.item_maps[0].generic.x = 256;
+ Bitmap_Init( &levelMenuInfo.item_maps[0] );
+ levelMenuInfo.item_maps[0].generic.bottom += 32;
+ levelMenuInfo.numMaps = 1;
+
+ levelMenuInfo.item_maps[1].generic.flags |= QMF_INACTIVE;
+ levelMenuInfo.item_maps[2].generic.flags |= QMF_INACTIVE;
+ levelMenuInfo.item_maps[3].generic.flags |= QMF_INACTIVE;
+ levelMenuInfo.levelPicNames[1][0] = 0;
+ levelMenuInfo.levelPicNames[2][0] = 0;
+ levelMenuInfo.levelPicNames[3][0] = 0;
+ levelMenuInfo.item_maps[1].shader = 0;
+ levelMenuInfo.item_maps[2].shader = 0;
+ levelMenuInfo.item_maps[3].shader = 0;
+ }
+ else if( selectedArenaSet == finalTier ) {
+ arenaInfo = UI_GetSpecialArenaInfo( "final" );
+ level = atoi( Info_ValueForKey( arenaInfo, "num" ) );
+ UI_SPLevelMenu_SetMenuArena( 0, level, arenaInfo );
+ levelMenuInfo.selectedArenaInfo = arenaInfo;
+
+ levelMenuInfo.item_maps[0].generic.x = 256;
+ Bitmap_Init( &levelMenuInfo.item_maps[0] );
+ levelMenuInfo.item_maps[0].generic.bottom += 32;
+ levelMenuInfo.numMaps = 1;
+
+ levelMenuInfo.item_maps[1].generic.flags |= QMF_INACTIVE;
+ levelMenuInfo.item_maps[2].generic.flags |= QMF_INACTIVE;
+ levelMenuInfo.item_maps[3].generic.flags |= QMF_INACTIVE;
+ levelMenuInfo.levelPicNames[1][0] = 0;
+ levelMenuInfo.levelPicNames[2][0] = 0;
+ levelMenuInfo.levelPicNames[3][0] = 0;
+ levelMenuInfo.item_maps[1].shader = 0;
+ levelMenuInfo.item_maps[2].shader = 0;
+ levelMenuInfo.item_maps[3].shader = 0;
+ }
+ else {
+ levelMenuInfo.item_maps[0].generic.x = 46;
+ Bitmap_Init( &levelMenuInfo.item_maps[0] );
+ levelMenuInfo.item_maps[0].generic.bottom += 18;
+ levelMenuInfo.numMaps = 4;
+
+ for ( n = 0; n < 4; n++ ) {
+ level = selectedArenaSet * ARENAS_PER_TIER + n;
+ arenaInfo = UI_GetArenaInfoByNumber( level );
+ UI_SPLevelMenu_SetMenuArena( n, level, arenaInfo );
+ }
+
+ if( selectedArena != -1 ) {
+ levelMenuInfo.selectedArenaInfo = UI_GetArenaInfoByNumber( selectedArenaSet * ARENAS_PER_TIER + selectedArena );
+ }
+ }
+
+ // enable/disable arrows when they are valid/invalid
+ if ( selectedArenaSet == minTier ) {
+ levelMenuInfo.item_leftarrow.generic.flags |= ( QMF_INACTIVE | QMF_HIDDEN );
+ }
+ else {
+ levelMenuInfo.item_leftarrow.generic.flags &= ~( QMF_INACTIVE | QMF_HIDDEN );
+ }
+
+ if ( selectedArenaSet == maxTier ) {
+ levelMenuInfo.item_rightarrow.generic.flags |= ( QMF_INACTIVE | QMF_HIDDEN );
+ }
+ else {
+ levelMenuInfo.item_rightarrow.generic.flags &= ~( QMF_INACTIVE | QMF_HIDDEN );
+ }
+
+ UI_SPLevelMenu_SetBots();
+}
+
+
+/*
+=================
+UI_SPLevelMenu_ResetEvent
+=================
+*/
+static void UI_SPLevelMenu_ResetDraw( void ) {
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This resets all of the", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "single player game variables.", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 2, "Do this only if you want to", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 3, "start over from the beginning.", UI_CENTER|UI_SMALLFONT, color_yellow );
+}
+
+static void UI_SPLevelMenu_ResetAction( qboolean result ) {
+ if( !result ) {
+ return;
+ }
+
+ // clear game variables
+ UI_NewGame();
+ trap_Cvar_SetValue( "ui_spSelection", -4 );
+
+ // make the level select menu re-initialize
+ UI_PopMenu();
+ UI_SPLevelMenu();
+}
+
+static void UI_SPLevelMenu_ResetEvent( void* ptr, int event )
+{
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+
+ UI_ConfirmMenu( "RESET GAME?", UI_SPLevelMenu_ResetDraw, UI_SPLevelMenu_ResetAction );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_LevelEvent
+=================
+*/
+static void UI_SPLevelMenu_LevelEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ if ( selectedArenaSet == trainingTier || selectedArenaSet == finalTier ) {
+ return;
+ }
+
+ selectedArena = ((menucommon_s*)ptr)->id - ID_PICTURE0;
+ levelMenuInfo.selectedArenaInfo = UI_GetArenaInfoByNumber( selectedArenaSet * ARENAS_PER_TIER + selectedArena );
+ UI_SPLevelMenu_SetBots();
+
+ trap_Cvar_SetValue( "ui_spSelection", selectedArenaSet * ARENAS_PER_TIER + selectedArena );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_LeftArrowEvent
+=================
+*/
+static void UI_SPLevelMenu_LeftArrowEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ if ( selectedArenaSet == minTier ) {
+ return;
+ }
+
+ selectedArenaSet--;
+ UI_SPLevelMenu_SetMenuItems();
+}
+
+
+/*
+=================
+UI_SPLevelMenu_RightArrowEvent
+=================
+*/
+static void UI_SPLevelMenu_RightArrowEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ if ( selectedArenaSet == maxTier ) {
+ return;
+ }
+
+ selectedArenaSet++;
+ UI_SPLevelMenu_SetMenuItems();
+}
+
+
+/*
+=================
+UI_SPLevelMenu_PlayerEvent
+=================
+*/
+static void UI_SPLevelMenu_PlayerEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ UI_PlayerSettingsMenu();
+}
+
+
+/*
+=================
+UI_SPLevelMenu_AwardEvent
+=================
+*/
+static void UI_SPLevelMenu_AwardEvent( void* ptr, int notification ) {
+ int n;
+
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ n = ((menucommon_s*)ptr)->id - ID_AWARD1;
+ trap_S_StartLocalSound( levelMenuInfo.awardSounds[n], CHAN_ANNOUNCER );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_NextEvent
+=================
+*/
+static void UI_SPLevelMenu_NextEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ if ( selectedArenaSet > currentSet ) {
+ return;
+ }
+
+ if ( selectedArena == -1 ) {
+ selectedArena = 0;
+ }
+
+ UI_SPSkillMenu( levelMenuInfo.selectedArenaInfo );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_BackEvent
+=================
+*/
+static void UI_SPLevelMenu_BackEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ if ( selectedArena == -1 ) {
+ selectedArena = 0;
+ }
+
+ UI_PopMenu();
+}
+
+
+/*
+=================
+UI_SPLevelMenu_CustomEvent
+=================
+*/
+static void UI_SPLevelMenu_CustomEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ UI_StartServerMenu( qfalse );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_MenuDraw
+=================
+*/
+#define LEVEL_DESC_LEFT_MARGIN 332
+
+static void UI_SPLevelMenu_MenuDraw( void ) {
+ int n, i;
+ int x, y;
+ vec4_t color;
+ int level;
+// int fraglimit;
+ int pad;
+ char buf[MAX_INFO_VALUE];
+ char string[64];
+
+ if( levelMenuInfo.reinit ) {
+ UI_PopMenu();
+ UI_SPLevelMenu();
+ return;
+ }
+
+ // draw player name
+ trap_Cvar_VariableStringBuffer( "name", string, 32 );
+ Q_CleanStr( string );
+ UI_DrawProportionalString( 320, PLAYER_Y, string, UI_CENTER|UI_SMALLFONT, color_orange );
+
+ // check for model changes
+ trap_Cvar_VariableStringBuffer( "model", buf, sizeof(buf) );
+ if( Q_stricmp( buf, levelMenuInfo.playerModel ) != 0 ) {
+ Q_strncpyz( levelMenuInfo.playerModel, buf, sizeof(levelMenuInfo.playerModel) );
+ PlayerIcon( levelMenuInfo.playerModel, levelMenuInfo.playerPicName, sizeof(levelMenuInfo.playerPicName) );
+ levelMenuInfo.item_player.shader = 0;
+ }
+
+ // standard menu drawing
+ Menu_Draw( &levelMenuInfo.menu );
+
+ // draw player award levels
+ y = AWARDS_Y;
+ i = 0;
+ for( n = 0; n < 6; n++ ) {
+ level = levelMenuInfo.awardLevels[n];
+ if( level > 0 ) {
+ if( i & 1 ) {
+ x = 224 - (i - 1 ) / 2 * (48 + 16);
+ }
+ else {
+ x = 368 + i / 2 * (48 + 16);
+ }
+ i++;
+
+ if( level == 1 ) {
+ continue;
+ }
+
+ if( level >= 1000000 ) {
+ Com_sprintf( string, sizeof(string), "%im", level / 1000000 );
+ }
+ else if( level >= 1000 ) {
+ Com_sprintf( string, sizeof(string), "%ik", level / 1000 );
+ }
+ else {
+ Com_sprintf( string, sizeof(string), "%i", level );
+ }
+
+ UI_DrawString( x + 24, y + 48, string, UI_CENTER, color_yellow );
+ }
+ }
+
+ UI_DrawProportionalString( 18, 38, va( "Tier %i", selectedArenaSet + 1 ), UI_LEFT|UI_SMALLFONT, color_orange );
+
+ for ( n = 0; n < levelMenuInfo.numMaps; n++ ) {
+ x = levelMenuInfo.item_maps[n].generic.x;
+ y = levelMenuInfo.item_maps[n].generic.y;
+ UI_FillRect( x, y + 96, 128, 18, color_black );
+ }
+
+ if ( selectedArenaSet > currentSet ) {
+ UI_DrawProportionalString( 320, 216, "ACCESS DENIED", UI_CENTER|UI_BIGFONT, color_red );
+ return;
+ }
+
+ // show levelshots for levels of current tier
+ Vector4Copy( color_white, color );
+ color[3] = 0.5+0.5*sin(uis.realtime/PULSE_DIVISOR);
+ for ( n = 0; n < levelMenuInfo.numMaps; n++ ) {
+ x = levelMenuInfo.item_maps[n].generic.x;
+ y = levelMenuInfo.item_maps[n].generic.y;
+
+ UI_DrawString( x + 64, y + 96, levelMenuInfo.levelNames[n], UI_CENTER|UI_SMALLFONT, color_orange );
+
+ if( levelMenuInfo.levelScores[n] == 1 ) {
+ UI_DrawHandlePic( x, y, 128, 96, levelMenuInfo.levelCompletePic[levelMenuInfo.levelScoresSkill[n] - 1] );
+ }
+
+ if ( n == selectedArena ) {
+ if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) {
+ trap_R_SetColor( color );
+ }
+ UI_DrawHandlePic( x-1, y-1, 130, 130 - 14, levelMenuInfo.levelSelectedPic );
+ trap_R_SetColor( NULL );
+ }
+ else if( Menu_ItemAtCursor( &levelMenuInfo.menu ) == &levelMenuInfo.item_maps[n] ) {
+ trap_R_SetColor( color );
+ UI_DrawHandlePic( x-31, y-30, 256, 256-27, levelMenuInfo.levelFocusPic);
+ trap_R_SetColor( NULL );
+ }
+ }
+
+ // show map name and long name of selected level
+ y = 192;
+ Q_strncpyz( buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "map" ), 20 );
+ Q_strupr( buf );
+ Com_sprintf( string, sizeof(string), "%s: %s", buf, Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "longname" ) );
+ UI_DrawProportionalString( 320, y, string, UI_CENTER|UI_SMALLFONT, color_orange );
+
+// fraglimit = atoi( Info_ValueForKey( levelMenuInfo.selectedArenaInfo, "fraglimit" ) );
+// UI_DrawString( 18, 212, va("Frags %i", fraglimit) , UI_LEFT|UI_SMALLFONT, color_orange );
+
+ // draw bot opponents
+ y += 24;
+ pad = (7 - levelMenuInfo.numBots) * (64 + 26) / 2;
+ for( n = 0; n < levelMenuInfo.numBots; n++ ) {
+ x = 18 + pad + (64 + 26) * n;
+ if( levelMenuInfo.botPics[n] ) {
+ UI_DrawHandlePic( x, y, 64, 64, levelMenuInfo.botPics[n]);
+ }
+ else {
+ UI_FillRect( x, y, 64, 64, color_black );
+ UI_DrawProportionalString( x+22, y+18, "?", UI_BIGFONT, color_orange );
+ }
+ UI_DrawString( x, y + 64, levelMenuInfo.botNames[n], UI_SMALLFONT|UI_LEFT, color_orange );
+ }
+}
+
+
+/*
+=================
+UI_SPLevelMenu_Cache
+=================
+*/
+void UI_SPLevelMenu_Cache( void ) {
+ int n;
+
+ trap_R_RegisterShaderNoMip( ART_LEVELFRAME_FOCUS );
+ trap_R_RegisterShaderNoMip( ART_LEVELFRAME_SELECTED );
+ trap_R_RegisterShaderNoMip( ART_ARROW );
+ trap_R_RegisterShaderNoMip( ART_ARROW_FOCUS );
+ trap_R_RegisterShaderNoMip( ART_MAP_UNKNOWN );
+ trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 );
+ trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 );
+ trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 );
+ trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 );
+ trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT0 );
+ trap_R_RegisterShaderNoMip( ART_FIGHT1 );
+ trap_R_RegisterShaderNoMip( ART_RESET0 );
+ trap_R_RegisterShaderNoMip( ART_RESET1 );
+ trap_R_RegisterShaderNoMip( ART_CUSTOM0 );
+ trap_R_RegisterShaderNoMip( ART_CUSTOM1 );
+
+ for( n = 0; n < 6; n++ ) {
+ trap_R_RegisterShaderNoMip( ui_medalPicNames[n] );
+ levelMenuInfo.awardSounds[n] = trap_S_RegisterSound( ui_medalSounds[n], qfalse );
+ }
+
+ levelMenuInfo.levelSelectedPic = trap_R_RegisterShaderNoMip( ART_LEVELFRAME_SELECTED );
+ levelMenuInfo.levelFocusPic = trap_R_RegisterShaderNoMip( ART_LEVELFRAME_FOCUS );
+ levelMenuInfo.levelCompletePic[0] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 );
+ levelMenuInfo.levelCompletePic[1] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 );
+ levelMenuInfo.levelCompletePic[2] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 );
+ levelMenuInfo.levelCompletePic[3] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 );
+ levelMenuInfo.levelCompletePic[4] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_Init
+=================
+*/
+static void UI_SPLevelMenu_Init( void ) {
+ int skill;
+ int n;
+ int x, y;
+ int count;
+ char buf[MAX_QPATH];
+
+ skill = (int)trap_Cvar_VariableValue( "g_spSkill" );
+ if( skill < 1 || skill > 5 ) {
+ trap_Cvar_Set( "g_spSkill", "2" );
+ skill = 2;
+ }
+
+ memset( &levelMenuInfo, 0, sizeof(levelMenuInfo) );
+ levelMenuInfo.menu.fullscreen = qtrue;
+ levelMenuInfo.menu.wrapAround = qtrue;
+ levelMenuInfo.menu.draw = UI_SPLevelMenu_MenuDraw;
+
+ UI_SPLevelMenu_Cache();
+
+ levelMenuInfo.item_banner.generic.type = MTYPE_BTEXT;
+ levelMenuInfo.item_banner.generic.x = 320;
+ levelMenuInfo.item_banner.generic.y = 16;
+ levelMenuInfo.item_banner.string = "CHOOSE LEVEL";
+ levelMenuInfo.item_banner.color = color_red;
+ levelMenuInfo.item_banner.style = UI_CENTER;
+
+ levelMenuInfo.item_leftarrow.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_leftarrow.generic.name = ART_ARROW;
+ levelMenuInfo.item_leftarrow.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ levelMenuInfo.item_leftarrow.generic.x = 18;
+ levelMenuInfo.item_leftarrow.generic.y = 64;
+ levelMenuInfo.item_leftarrow.generic.callback = UI_SPLevelMenu_LeftArrowEvent;
+ levelMenuInfo.item_leftarrow.generic.id = ID_LEFTARROW;
+ levelMenuInfo.item_leftarrow.width = 16;
+ levelMenuInfo.item_leftarrow.height = 114;
+ levelMenuInfo.item_leftarrow.focuspic = ART_ARROW_FOCUS;
+
+ levelMenuInfo.item_maps[0].generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_maps[0].generic.name = levelMenuInfo.levelPicNames[0];
+ levelMenuInfo.item_maps[0].generic.flags = QMF_LEFT_JUSTIFY;
+ levelMenuInfo.item_maps[0].generic.x = 46;
+ levelMenuInfo.item_maps[0].generic.y = 64;
+ levelMenuInfo.item_maps[0].generic.id = ID_PICTURE0;
+ levelMenuInfo.item_maps[0].generic.callback = UI_SPLevelMenu_LevelEvent;
+ levelMenuInfo.item_maps[0].width = 128;
+ levelMenuInfo.item_maps[0].height = 96;
+
+ levelMenuInfo.item_maps[1].generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_maps[1].generic.name = levelMenuInfo.levelPicNames[1];
+ levelMenuInfo.item_maps[1].generic.flags = QMF_LEFT_JUSTIFY;
+ levelMenuInfo.item_maps[1].generic.x = 186;
+ levelMenuInfo.item_maps[1].generic.y = 64;
+ levelMenuInfo.item_maps[1].generic.id = ID_PICTURE1;
+ levelMenuInfo.item_maps[1].generic.callback = UI_SPLevelMenu_LevelEvent;
+ levelMenuInfo.item_maps[1].width = 128;
+ levelMenuInfo.item_maps[1].height = 96;
+
+ levelMenuInfo.item_maps[2].generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_maps[2].generic.name = levelMenuInfo.levelPicNames[2];
+ levelMenuInfo.item_maps[2].generic.flags = QMF_LEFT_JUSTIFY;
+ levelMenuInfo.item_maps[2].generic.x = 326;
+ levelMenuInfo.item_maps[2].generic.y = 64;
+ levelMenuInfo.item_maps[2].generic.id = ID_PICTURE2;
+ levelMenuInfo.item_maps[2].generic.callback = UI_SPLevelMenu_LevelEvent;
+ levelMenuInfo.item_maps[2].width = 128;
+ levelMenuInfo.item_maps[2].height = 96;
+
+ levelMenuInfo.item_maps[3].generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_maps[3].generic.name = levelMenuInfo.levelPicNames[3];
+ levelMenuInfo.item_maps[3].generic.flags = QMF_LEFT_JUSTIFY;
+ levelMenuInfo.item_maps[3].generic.x = 466;
+ levelMenuInfo.item_maps[3].generic.y = 64;
+ levelMenuInfo.item_maps[3].generic.id = ID_PICTURE3;
+ levelMenuInfo.item_maps[3].generic.callback = UI_SPLevelMenu_LevelEvent;
+ levelMenuInfo.item_maps[3].width = 128;
+ levelMenuInfo.item_maps[3].height = 96;
+
+ levelMenuInfo.item_rightarrow.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_rightarrow.generic.name = ART_ARROW;
+ levelMenuInfo.item_rightarrow.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ levelMenuInfo.item_rightarrow.generic.x = 606;
+ levelMenuInfo.item_rightarrow.generic.y = 64;
+ levelMenuInfo.item_rightarrow.generic.callback = UI_SPLevelMenu_RightArrowEvent;
+ levelMenuInfo.item_rightarrow.generic.id = ID_RIGHTARROW;
+ levelMenuInfo.item_rightarrow.width = -16;
+ levelMenuInfo.item_rightarrow.height = 114;
+ levelMenuInfo.item_rightarrow.focuspic = ART_ARROW_FOCUS;
+
+ trap_Cvar_VariableStringBuffer( "model", levelMenuInfo.playerModel, sizeof(levelMenuInfo.playerModel) );
+ PlayerIcon( levelMenuInfo.playerModel, levelMenuInfo.playerPicName, sizeof(levelMenuInfo.playerPicName) );
+ levelMenuInfo.item_player.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_player.generic.name = levelMenuInfo.playerPicName;
+ levelMenuInfo.item_player.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY;
+ levelMenuInfo.item_player.generic.x = 288;
+ levelMenuInfo.item_player.generic.y = AWARDS_Y;
+ levelMenuInfo.item_player.generic.id = ID_PLAYERPIC;
+ levelMenuInfo.item_player.generic.callback = UI_SPLevelMenu_PlayerEvent;
+ levelMenuInfo.item_player.width = 64;
+ levelMenuInfo.item_player.height = 64;
+
+ for( n = 0; n < 6; n++ ) {
+ levelMenuInfo.awardLevels[n] = UI_GetAwardLevel( n );
+ }
+ levelMenuInfo.awardLevels[AWARD_FRAGS] = 100 * (levelMenuInfo.awardLevels[AWARD_FRAGS] / 100);
+
+ y = AWARDS_Y;
+ count = 0;
+ for( n = 0; n < 6; n++ ) {
+ if( levelMenuInfo.awardLevels[n] ) {
+ if( count & 1 ) {
+ x = 224 - (count - 1 ) / 2 * (48 + 16);
+ }
+ else {
+ x = 368 + count / 2 * (48 + 16);
+ }
+
+ levelMenuInfo.item_awards[count].generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_awards[count].generic.name = ui_medalPicNames[n];
+ levelMenuInfo.item_awards[count].generic.flags = QMF_LEFT_JUSTIFY|QMF_SILENT|QMF_MOUSEONLY;
+ levelMenuInfo.item_awards[count].generic.x = x;
+ levelMenuInfo.item_awards[count].generic.y = y;
+ levelMenuInfo.item_awards[count].generic.id = ID_AWARD1 + n;
+ levelMenuInfo.item_awards[count].generic.callback = UI_SPLevelMenu_AwardEvent;
+ levelMenuInfo.item_awards[count].width = 48;
+ levelMenuInfo.item_awards[count].height = 48;
+ count++;
+ }
+ }
+
+ levelMenuInfo.item_back.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_back.generic.name = ART_BACK0;
+ levelMenuInfo.item_back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ levelMenuInfo.item_back.generic.x = 0;
+ levelMenuInfo.item_back.generic.y = 480-64;
+ levelMenuInfo.item_back.generic.callback = UI_SPLevelMenu_BackEvent;
+ levelMenuInfo.item_back.generic.id = ID_BACK;
+ levelMenuInfo.item_back.width = 128;
+ levelMenuInfo.item_back.height = 64;
+ levelMenuInfo.item_back.focuspic = ART_BACK1;
+
+ levelMenuInfo.item_reset.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_reset.generic.name = ART_RESET0;
+ levelMenuInfo.item_reset.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ levelMenuInfo.item_reset.generic.x = 170;
+ levelMenuInfo.item_reset.generic.y = 480-64;
+ levelMenuInfo.item_reset.generic.callback = UI_SPLevelMenu_ResetEvent;
+ levelMenuInfo.item_reset.generic.id = ID_RESET;
+ levelMenuInfo.item_reset.width = 128;
+ levelMenuInfo.item_reset.height = 64;
+ levelMenuInfo.item_reset.focuspic = ART_RESET1;
+
+ levelMenuInfo.item_custom.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_custom.generic.name = ART_CUSTOM0;
+ levelMenuInfo.item_custom.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ levelMenuInfo.item_custom.generic.x = 342;
+ levelMenuInfo.item_custom.generic.y = 480-64;
+ levelMenuInfo.item_custom.generic.callback = UI_SPLevelMenu_CustomEvent;
+ levelMenuInfo.item_custom.generic.id = ID_CUSTOM;
+ levelMenuInfo.item_custom.width = 128;
+ levelMenuInfo.item_custom.height = 64;
+ levelMenuInfo.item_custom.focuspic = ART_CUSTOM1;
+
+ levelMenuInfo.item_next.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_next.generic.name = ART_FIGHT0;
+ levelMenuInfo.item_next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ levelMenuInfo.item_next.generic.x = 640;
+ levelMenuInfo.item_next.generic.y = 480-64;
+ levelMenuInfo.item_next.generic.callback = UI_SPLevelMenu_NextEvent;
+ levelMenuInfo.item_next.generic.id = ID_NEXT;
+ levelMenuInfo.item_next.width = 128;
+ levelMenuInfo.item_next.height = 64;
+ levelMenuInfo.item_next.focuspic = ART_FIGHT1;
+
+ levelMenuInfo.item_null.generic.type = MTYPE_BITMAP;
+ levelMenuInfo.item_null.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT;
+ levelMenuInfo.item_null.generic.x = 0;
+ levelMenuInfo.item_null.generic.y = 0;
+ levelMenuInfo.item_null.width = 640;
+ levelMenuInfo.item_null.height = 480;
+
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_banner );
+
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_leftarrow );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[0] );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[1] );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[2] );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_maps[3] );
+ levelMenuInfo.item_maps[0].generic.bottom += 18;
+ levelMenuInfo.item_maps[1].generic.bottom += 18;
+ levelMenuInfo.item_maps[2].generic.bottom += 18;
+ levelMenuInfo.item_maps[3].generic.bottom += 18;
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_rightarrow );
+
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_player );
+
+ for( n = 0; n < count; n++ ) {
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_awards[n] );
+ }
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_back );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_reset );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_custom );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_next );
+ Menu_AddItem( &levelMenuInfo.menu, &levelMenuInfo.item_null );
+
+ trap_Cvar_VariableStringBuffer( "ui_spSelection", buf, sizeof(buf) );
+ if( *buf ) {
+ n = atoi( buf );
+ selectedArenaSet = n / ARENAS_PER_TIER;
+ selectedArena = n % ARENAS_PER_TIER;
+ }
+ else {
+ selectedArenaSet = currentSet;
+ selectedArena = currentGame;
+ }
+
+ UI_SPLevelMenu_SetMenuItems();
+}
+
+
+/*
+=================
+UI_SPLevelMenu
+=================
+*/
+void UI_SPLevelMenu( void ) {
+ int level;
+ int trainingLevel;
+ const char *arenaInfo;
+
+ trainingTier = -1;
+ arenaInfo = UI_GetSpecialArenaInfo( "training" );
+ if( arenaInfo ) {
+ minTier = trainingTier;
+ trainingLevel = atoi( Info_ValueForKey( arenaInfo, "num" ) );
+ }
+ else {
+ minTier = 0;
+ trainingLevel = -2;
+ }
+
+ finalTier = UI_GetNumSPTiers();
+ arenaInfo = UI_GetSpecialArenaInfo( "final" );
+ if( arenaInfo ) {
+ maxTier = finalTier;
+ }
+ else {
+ maxTier = finalTier - 1;
+ if( maxTier < minTier ) {
+ maxTier = minTier;
+ }
+ }
+
+ level = UI_GetCurrentGame();
+ if ( level == -1 ) {
+ level = UI_GetNumSPArenas() - 1;
+ if( maxTier == finalTier ) {
+ level++;
+ }
+ }
+
+ if( level == trainingLevel ) {
+ currentSet = -1;
+ currentGame = 0;
+ }
+ else {
+ currentSet = level / ARENAS_PER_TIER;
+ currentGame = level % ARENAS_PER_TIER;
+ }
+
+ UI_SPLevelMenu_Init();
+ UI_PushMenu( &levelMenuInfo.menu );
+ Menu_SetCursorToItem( &levelMenuInfo.menu, &levelMenuInfo.item_next );
+}
+
+
+/*
+=================
+UI_SPLevelMenu_f
+=================
+*/
+void UI_SPLevelMenu_f( void ) {
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ uis.menusp = 0;
+ UI_SPLevelMenu();
+}
+
+
+/*
+=================
+UI_SPLevelMenu_ReInit
+=================
+*/
+void UI_SPLevelMenu_ReInit( void ) {
+ levelMenuInfo.reinit = qtrue;
+}
diff --git a/code/q3_ui/ui_sppostgame.c b/code/q3_ui/ui_sppostgame.c
new file mode 100644
index 0000000..3b9c246
--- /dev/null
+++ b/code/q3_ui/ui_sppostgame.c
@@ -0,0 +1,644 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=============================================================================
+
+SINGLE PLAYER POSTGAME MENU
+
+=============================================================================
+*/
+
+#include "ui_local.h"
+
+#define MAX_SCOREBOARD_CLIENTS 8
+
+#define AWARD_PRESENTATION_TIME 2000
+
+#define ART_MENU0 "menu/art/menu_0"
+#define ART_MENU1 "menu/art/menu_1"
+#define ART_REPLAY0 "menu/art/replay_0"
+#define ART_REPLAY1 "menu/art/replay_1"
+#define ART_NEXT0 "menu/art/next_0"
+#define ART_NEXT1 "menu/art/next_1"
+
+#define ID_AGAIN 10
+#define ID_NEXT 11
+#define ID_MENU 12
+
+typedef struct {
+ menuframework_s menu;
+ menubitmap_s item_again;
+ menubitmap_s item_next;
+ menubitmap_s item_menu;
+
+ int phase;
+ int ignoreKeysTime;
+ int starttime;
+ int scoreboardtime;
+ int serverId;
+
+ int clientNums[MAX_SCOREBOARD_CLIENTS];
+ int ranks[MAX_SCOREBOARD_CLIENTS];
+ int scores[MAX_SCOREBOARD_CLIENTS];
+
+ char placeNames[3][64];
+
+ int level;
+ int numClients;
+ int won;
+ int numAwards;
+ int awardsEarned[6];
+ int awardsLevels[6];
+ qboolean playedSound[6];
+ int lastTier;
+ sfxHandle_t winnerSound;
+} postgameMenuInfo_t;
+
+static postgameMenuInfo_t postgameMenuInfo;
+static char arenainfo[MAX_INFO_VALUE];
+
+char *ui_medalNames[] = {"Accuracy", "Impressive", "Excellent", "Gauntlet", "Frags", "Perfect"};
+char *ui_medalPicNames[] = {
+ "menu/medals/medal_accuracy",
+ "menu/medals/medal_impressive",
+ "menu/medals/medal_excellent",
+ "menu/medals/medal_gauntlet",
+ "menu/medals/medal_frags",
+ "menu/medals/medal_victory"
+};
+char *ui_medalSounds[] = {
+ "sound/feedback/accuracy.wav",
+ "sound/feedback/impressive_a.wav",
+ "sound/feedback/excellent_a.wav",
+ "sound/feedback/gauntlet.wav",
+ "sound/feedback/frags.wav",
+ "sound/feedback/perfect.wav"
+};
+
+
+/*
+=================
+UI_SPPostgameMenu_AgainEvent
+=================
+*/
+static void UI_SPPostgameMenu_AgainEvent( void* ptr, int event )
+{
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+ UI_PopMenu();
+ trap_Cmd_ExecuteText( EXEC_APPEND, "map_restart 0\n" );
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_NextEvent
+=================
+*/
+static void UI_SPPostgameMenu_NextEvent( void* ptr, int event ) {
+ int currentSet;
+ int levelSet;
+ int level;
+ int currentLevel;
+ const char *arenaInfo;
+
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+ UI_PopMenu();
+
+ // handle specially if we just won the training map
+ if( postgameMenuInfo.won == 0 ) {
+ level = 0;
+ }
+ else {
+ level = postgameMenuInfo.level + 1;
+ }
+ levelSet = level / ARENAS_PER_TIER;
+
+ currentLevel = UI_GetCurrentGame();
+ if( currentLevel == -1 ) {
+ currentLevel = postgameMenuInfo.level;
+ }
+ currentSet = currentLevel / ARENAS_PER_TIER;
+
+ if( levelSet > currentSet || levelSet == UI_GetNumSPTiers() ) {
+ level = currentLevel;
+ }
+
+ arenaInfo = UI_GetArenaInfoByNumber( level );
+ if ( !arenaInfo ) {
+ return;
+ }
+
+ UI_SPArena_Start( arenaInfo );
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_MenuEvent
+=================
+*/
+static void UI_SPPostgameMenu_MenuEvent( void* ptr, int event )
+{
+ if (event != QM_ACTIVATED) {
+ return;
+ }
+ UI_PopMenu();
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; levelselect\n" );
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_MenuKey
+=================
+*/
+static sfxHandle_t UI_SPPostgameMenu_MenuKey( int key ) {
+ if ( uis.realtime < postgameMenuInfo.ignoreKeysTime ) {
+ return 0;
+ }
+
+ if( postgameMenuInfo.phase == 1 ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, "abort_podium\n" );
+ postgameMenuInfo.phase = 2;
+ postgameMenuInfo.starttime = uis.realtime;
+ postgameMenuInfo.ignoreKeysTime = uis.realtime + 250;
+ return 0;
+ }
+
+ if( postgameMenuInfo.phase == 2 ) {
+ postgameMenuInfo.phase = 3;
+ postgameMenuInfo.starttime = uis.realtime;
+ postgameMenuInfo.ignoreKeysTime = uis.realtime + 250;
+ return 0;
+ }
+
+ if( key == K_ESCAPE || key == K_MOUSE2 ) {
+ return 0;
+ }
+
+ return Menu_DefaultKey( &postgameMenuInfo.menu, key );
+}
+
+
+static int medalLocations[6] = {144, 448, 88, 504, 32, 560};
+
+static void UI_SPPostgameMenu_DrawAwardsMedals( int max ) {
+ int n;
+ int medal;
+ int amount;
+ int x, y;
+ char buf[16];
+
+ for( n = 0; n < max; n++ ) {
+ x = medalLocations[n];
+ y = 64;
+ medal = postgameMenuInfo.awardsEarned[n];
+ amount = postgameMenuInfo.awardsLevels[n];
+
+ UI_DrawNamedPic( x, y, 48, 48, ui_medalPicNames[medal] );
+
+ if( medal == AWARD_ACCURACY ) {
+ Com_sprintf( buf, sizeof(buf), "%i%%", amount );
+ }
+ else {
+ if( amount == 1 ) {
+ continue;
+ }
+ Com_sprintf( buf, sizeof(buf), "%i", amount );
+ }
+
+ UI_DrawString( x + 24, y + 52, buf, UI_CENTER, color_yellow );
+ }
+}
+
+
+static void UI_SPPostgameMenu_DrawAwardsPresentation( int timer ) {
+ int awardNum;
+ int atimer;
+ vec4_t color;
+
+ awardNum = timer / AWARD_PRESENTATION_TIME;
+ atimer = timer % AWARD_PRESENTATION_TIME;
+
+ color[0] = color[1] = color[2] = 1.0f;
+ color[3] = (float)( AWARD_PRESENTATION_TIME - atimer ) / (float)AWARD_PRESENTATION_TIME;
+ UI_DrawProportionalString( 320, 64, ui_medalNames[postgameMenuInfo.awardsEarned[awardNum]], UI_CENTER, color );
+
+ UI_SPPostgameMenu_DrawAwardsMedals( awardNum + 1 );
+
+ if( !postgameMenuInfo.playedSound[awardNum] ) {
+ postgameMenuInfo.playedSound[awardNum] = qtrue;
+ trap_S_StartLocalSound( trap_S_RegisterSound( ui_medalSounds[postgameMenuInfo.awardsEarned[awardNum]], qfalse ), CHAN_ANNOUNCER );
+ }
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_MenuDrawScoreLine
+=================
+*/
+static void UI_SPPostgameMenu_MenuDrawScoreLine( int n, int y ) {
+ int rank;
+ char name[64];
+ char info[MAX_INFO_STRING];
+
+ if( n > (postgameMenuInfo.numClients + 1) ) {
+ n -= (postgameMenuInfo.numClients + 2);
+ }
+
+ if( n >= postgameMenuInfo.numClients ) {
+ return;
+ }
+
+ rank = postgameMenuInfo.ranks[n];
+ if( rank & RANK_TIED_FLAG ) {
+ UI_DrawString( 640 - 31 * SMALLCHAR_WIDTH, y, "(tie)", UI_LEFT|UI_SMALLFONT, color_white );
+ rank &= ~RANK_TIED_FLAG;
+ }
+ trap_GetConfigString( CS_PLAYERS + postgameMenuInfo.clientNums[n], info, MAX_INFO_STRING );
+ Q_strncpyz( name, Info_ValueForKey( info, "n" ), sizeof(name) );
+ Q_CleanStr( name );
+
+ UI_DrawString( 640 - 25 * SMALLCHAR_WIDTH, y, va( "#%i: %-16s %2i", rank + 1, name, postgameMenuInfo.scores[n] ), UI_LEFT|UI_SMALLFONT, color_white );
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_MenuDraw
+=================
+*/
+static void UI_SPPostgameMenu_MenuDraw( void ) {
+ int timer;
+ int serverId;
+ int n;
+ char info[MAX_INFO_STRING];
+
+ trap_GetConfigString( CS_SYSTEMINFO, info, sizeof(info) );
+ serverId = atoi( Info_ValueForKey( info, "sv_serverid" ) );
+ if( serverId != postgameMenuInfo.serverId ) {
+ UI_PopMenu();
+ return;
+ }
+
+ // phase 1
+ if ( postgameMenuInfo.numClients > 2 ) {
+ UI_DrawProportionalString( 510, 480 - 64 - PROP_HEIGHT, postgameMenuInfo.placeNames[2], UI_CENTER, color_white );
+ }
+ UI_DrawProportionalString( 130, 480 - 64 - PROP_HEIGHT, postgameMenuInfo.placeNames[1], UI_CENTER, color_white );
+ UI_DrawProportionalString( 320, 480 - 64 - 2 * PROP_HEIGHT, postgameMenuInfo.placeNames[0], UI_CENTER, color_white );
+
+ if( postgameMenuInfo.phase == 1 ) {
+ timer = uis.realtime - postgameMenuInfo.starttime;
+
+ if( timer >= 1000 && postgameMenuInfo.winnerSound ) {
+ trap_S_StartLocalSound( postgameMenuInfo.winnerSound, CHAN_ANNOUNCER );
+ postgameMenuInfo.winnerSound = 0;
+ }
+
+ if( timer < 5000 ) {
+ return;
+ }
+ postgameMenuInfo.phase = 2;
+ postgameMenuInfo.starttime = uis.realtime;
+ }
+
+ // phase 2
+ if( postgameMenuInfo.phase == 2 ) {
+ timer = uis.realtime - postgameMenuInfo.starttime;
+ if( timer >= ( postgameMenuInfo.numAwards * AWARD_PRESENTATION_TIME ) ) {
+
+ if( timer < 5000 ) {
+ return;
+ }
+
+ postgameMenuInfo.phase = 3;
+ postgameMenuInfo.starttime = uis.realtime;
+ }
+ else {
+ UI_SPPostgameMenu_DrawAwardsPresentation( timer );
+ }
+ }
+
+ // phase 3
+ if( postgameMenuInfo.phase == 3 ) {
+ if( uis.demoversion ) {
+ if( postgameMenuInfo.won == 1 && UI_ShowTierVideo( 8 )) {
+ trap_Cvar_Set( "nextmap", "" );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic demoEnd.RoQ\n" );
+ return;
+ }
+ }
+ else if( postgameMenuInfo.won > -1 && UI_ShowTierVideo( postgameMenuInfo.won + 1 )) {
+ if( postgameMenuInfo.won == postgameMenuInfo.lastTier ) {
+ trap_Cvar_Set( "nextmap", "" );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic end.RoQ\n" );
+ return;
+ }
+
+ trap_Cvar_SetValue( "ui_spSelection", postgameMenuInfo.won * ARENAS_PER_TIER );
+ trap_Cvar_Set( "nextmap", "levelselect" );
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "disconnect; cinematic tier%i.RoQ\n", postgameMenuInfo.won + 1 ) );
+ return;
+ }
+
+ postgameMenuInfo.item_again.generic.flags &= ~QMF_INACTIVE;
+ postgameMenuInfo.item_next.generic.flags &= ~QMF_INACTIVE;
+ postgameMenuInfo.item_menu.generic.flags &= ~QMF_INACTIVE;
+
+ UI_SPPostgameMenu_DrawAwardsMedals( postgameMenuInfo.numAwards );
+
+ Menu_Draw( &postgameMenuInfo.menu );
+ }
+
+ // draw the scoreboard
+ if( !trap_Cvar_VariableValue( "ui_spScoreboard" ) ) {
+ return;
+ }
+
+ timer = uis.realtime - postgameMenuInfo.scoreboardtime;
+ if( postgameMenuInfo.numClients <= 3 ) {
+ n = 0;
+ }
+ else {
+ n = timer / 1500 % (postgameMenuInfo.numClients + 2);
+ }
+ UI_SPPostgameMenu_MenuDrawScoreLine( n, 0 );
+ UI_SPPostgameMenu_MenuDrawScoreLine( n + 1, 0 + SMALLCHAR_HEIGHT );
+ UI_SPPostgameMenu_MenuDrawScoreLine( n + 2, 0 + 2 * SMALLCHAR_HEIGHT );
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_Cache
+=================
+*/
+void UI_SPPostgameMenu_Cache( void ) {
+ int n;
+ qboolean buildscript;
+
+ buildscript = trap_Cvar_VariableValue("com_buildscript");
+
+ trap_R_RegisterShaderNoMip( ART_MENU0 );
+ trap_R_RegisterShaderNoMip( ART_MENU1 );
+ trap_R_RegisterShaderNoMip( ART_REPLAY0 );
+ trap_R_RegisterShaderNoMip( ART_REPLAY1 );
+ trap_R_RegisterShaderNoMip( ART_NEXT0 );
+ trap_R_RegisterShaderNoMip( ART_NEXT1 );
+ for( n = 0; n < 6; n++ ) {
+ trap_R_RegisterShaderNoMip( ui_medalPicNames[n] );
+ trap_S_RegisterSound( ui_medalSounds[n], qfalse );
+ }
+
+ if( buildscript ) {
+ trap_S_RegisterSound( "music/loss.wav", qfalse );
+ trap_S_RegisterSound( "music/win.wav", qfalse );
+ trap_S_RegisterSound( "sound/player/announce/youwin.wav", qfalse );
+ }
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_Init
+=================
+*/
+static void UI_SPPostgameMenu_Init( void ) {
+ postgameMenuInfo.menu.wrapAround = qtrue;
+ postgameMenuInfo.menu.key = UI_SPPostgameMenu_MenuKey;
+ postgameMenuInfo.menu.draw = UI_SPPostgameMenu_MenuDraw;
+ postgameMenuInfo.ignoreKeysTime = uis.realtime + 1500;
+
+ UI_SPPostgameMenu_Cache();
+
+ postgameMenuInfo.item_menu.generic.type = MTYPE_BITMAP;
+ postgameMenuInfo.item_menu.generic.name = ART_MENU0;
+ postgameMenuInfo.item_menu.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE;
+ postgameMenuInfo.item_menu.generic.x = 0;
+ postgameMenuInfo.item_menu.generic.y = 480-64;
+ postgameMenuInfo.item_menu.generic.callback = UI_SPPostgameMenu_MenuEvent;
+ postgameMenuInfo.item_menu.generic.id = ID_MENU;
+ postgameMenuInfo.item_menu.width = 128;
+ postgameMenuInfo.item_menu.height = 64;
+ postgameMenuInfo.item_menu.focuspic = ART_MENU1;
+
+ postgameMenuInfo.item_again.generic.type = MTYPE_BITMAP;
+ postgameMenuInfo.item_again.generic.name = ART_REPLAY0;
+ postgameMenuInfo.item_again.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE;
+ postgameMenuInfo.item_again.generic.x = 320;
+ postgameMenuInfo.item_again.generic.y = 480-64;
+ postgameMenuInfo.item_again.generic.callback = UI_SPPostgameMenu_AgainEvent;
+ postgameMenuInfo.item_again.generic.id = ID_AGAIN;
+ postgameMenuInfo.item_again.width = 128;
+ postgameMenuInfo.item_again.height = 64;
+ postgameMenuInfo.item_again.focuspic = ART_REPLAY1;
+
+ postgameMenuInfo.item_next.generic.type = MTYPE_BITMAP;
+ postgameMenuInfo.item_next.generic.name = ART_NEXT0;
+ postgameMenuInfo.item_next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE;
+ postgameMenuInfo.item_next.generic.x = 640;
+ postgameMenuInfo.item_next.generic.y = 480-64;
+ postgameMenuInfo.item_next.generic.callback = UI_SPPostgameMenu_NextEvent;
+ postgameMenuInfo.item_next.generic.id = ID_NEXT;
+ postgameMenuInfo.item_next.width = 128;
+ postgameMenuInfo.item_next.height = 64;
+ postgameMenuInfo.item_next.focuspic = ART_NEXT1;
+
+ Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_menu );
+ Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_again );
+ Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_next );
+}
+
+
+static void Prepname( int index ) {
+ int len;
+ char name[64];
+ char info[MAX_INFO_STRING];
+
+ trap_GetConfigString( CS_PLAYERS + postgameMenuInfo.clientNums[index], info, MAX_INFO_STRING );
+ Q_strncpyz( name, Info_ValueForKey( info, "n" ), sizeof(name) );
+ Q_CleanStr( name );
+ len = strlen( name );
+
+ while( len && UI_ProportionalStringWidth( name ) > 256 ) {
+ len--;
+ name[len] = 0;
+ }
+
+ Q_strncpyz( postgameMenuInfo.placeNames[index], name, sizeof(postgameMenuInfo.placeNames[index]) );
+}
+
+
+/*
+=================
+UI_SPPostgameMenu_f
+=================
+*/
+void UI_SPPostgameMenu_f( void ) {
+ int playerGameRank;
+ int playerClientNum;
+ int n;
+ int oldFrags, newFrags;
+ const char *arena;
+ int awardValues[6];
+ char map[MAX_QPATH];
+ char info[MAX_INFO_STRING];
+
+ memset( &postgameMenuInfo, 0, sizeof(postgameMenuInfo) );
+
+ trap_GetConfigString( CS_SYSTEMINFO, info, sizeof(info) );
+ postgameMenuInfo.serverId = atoi( Info_ValueForKey( info, "sv_serverid" ) );
+
+ trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
+ Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) );
+ arena = UI_GetArenaInfoByMap( map );
+ if ( !arena ) {
+ return;
+ }
+ Q_strncpyz( arenainfo, arena, sizeof(arenainfo) );
+
+ postgameMenuInfo.level = atoi( Info_ValueForKey( arenainfo, "num" ) );
+
+ postgameMenuInfo.numClients = atoi( UI_Argv( 1 ) );
+ playerClientNum = atoi( UI_Argv( 2 ) );
+ playerGameRank = 8; // in case they ended game as a spectator
+
+ if( postgameMenuInfo.numClients > MAX_SCOREBOARD_CLIENTS ) {
+ postgameMenuInfo.numClients = MAX_SCOREBOARD_CLIENTS;
+ }
+
+ for( n = 0; n < postgameMenuInfo.numClients; n++ ) {
+ postgameMenuInfo.clientNums[n] = atoi( UI_Argv( 8 + n * 3 + 1 ) );
+ postgameMenuInfo.ranks[n] = atoi( UI_Argv( 8 + n * 3 + 2 ) );
+ postgameMenuInfo.scores[n] = atoi( UI_Argv( 8 + n * 3 + 3 ) );
+
+ if( postgameMenuInfo.clientNums[n] == playerClientNum ) {
+ playerGameRank = (postgameMenuInfo.ranks[n] & ~RANK_TIED_FLAG) + 1;
+ }
+ }
+
+ UI_SetBestScore( postgameMenuInfo.level, playerGameRank );
+
+ // process award stats and prepare presentation data
+ awardValues[AWARD_ACCURACY] = atoi( UI_Argv( 3 ) );
+ awardValues[AWARD_IMPRESSIVE] = atoi( UI_Argv( 4 ) );
+ awardValues[AWARD_EXCELLENT] = atoi( UI_Argv( 5 ) );
+ awardValues[AWARD_GAUNTLET] = atoi( UI_Argv( 6 ) );
+ awardValues[AWARD_FRAGS] = atoi( UI_Argv( 7 ) );
+ awardValues[AWARD_PERFECT] = atoi( UI_Argv( 8 ) );
+
+ postgameMenuInfo.numAwards = 0;
+
+ if( awardValues[AWARD_ACCURACY] >= 50 ) {
+ UI_LogAwardData( AWARD_ACCURACY, 1 );
+ postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_ACCURACY;
+ postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_ACCURACY];
+ postgameMenuInfo.numAwards++;
+ }
+
+ if( awardValues[AWARD_IMPRESSIVE] ) {
+ UI_LogAwardData( AWARD_IMPRESSIVE, awardValues[AWARD_IMPRESSIVE] );
+ postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_IMPRESSIVE;
+ postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_IMPRESSIVE];
+ postgameMenuInfo.numAwards++;
+ }
+
+ if( awardValues[AWARD_EXCELLENT] ) {
+ UI_LogAwardData( AWARD_EXCELLENT, awardValues[AWARD_EXCELLENT] );
+ postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_EXCELLENT;
+ postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_EXCELLENT];
+ postgameMenuInfo.numAwards++;
+ }
+
+ if( awardValues[AWARD_GAUNTLET] ) {
+ UI_LogAwardData( AWARD_GAUNTLET, awardValues[AWARD_GAUNTLET] );
+ postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_GAUNTLET;
+ postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_GAUNTLET];
+ postgameMenuInfo.numAwards++;
+ }
+
+ oldFrags = UI_GetAwardLevel( AWARD_FRAGS ) / 100;
+ UI_LogAwardData( AWARD_FRAGS, awardValues[AWARD_FRAGS] );
+ newFrags = UI_GetAwardLevel( AWARD_FRAGS ) / 100;
+ if( newFrags > oldFrags ) {
+ postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_FRAGS;
+ postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = newFrags * 100;
+ postgameMenuInfo.numAwards++;
+ }
+
+ if( awardValues[AWARD_PERFECT] ) {
+ UI_LogAwardData( AWARD_PERFECT, 1 );
+ postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_PERFECT;
+ postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = 1;
+ postgameMenuInfo.numAwards++;
+ }
+
+ if ( playerGameRank == 1 ) {
+ postgameMenuInfo.won = UI_TierCompleted( postgameMenuInfo.level );
+ }
+ else {
+ postgameMenuInfo.won = -1;
+ }
+
+ postgameMenuInfo.starttime = uis.realtime;
+ postgameMenuInfo.scoreboardtime = uis.realtime;
+
+ trap_Key_SetCatcher( KEYCATCH_UI );
+ uis.menusp = 0;
+
+ UI_SPPostgameMenu_Init();
+ UI_PushMenu( &postgameMenuInfo.menu );
+
+ if ( playerGameRank == 1 ) {
+ Menu_SetCursorToItem( &postgameMenuInfo.menu, &postgameMenuInfo.item_next );
+ }
+ else {
+ Menu_SetCursorToItem( &postgameMenuInfo.menu, &postgameMenuInfo.item_again );
+ }
+
+ Prepname( 0 );
+ Prepname( 1 );
+ Prepname( 2 );
+
+ if ( playerGameRank != 1 ) {
+ postgameMenuInfo.winnerSound = trap_S_RegisterSound( va( "sound/player/announce/%s_wins.wav", postgameMenuInfo.placeNames[0] ), qfalse );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "music music/loss\n" );
+ }
+ else {
+ postgameMenuInfo.winnerSound = trap_S_RegisterSound( "sound/player/announce/youwin.wav", qfalse );
+ trap_Cmd_ExecuteText( EXEC_APPEND, "music music/win\n" );
+ }
+
+ postgameMenuInfo.phase = 1;
+
+ postgameMenuInfo.lastTier = UI_GetNumSPTiers();
+ if ( UI_GetSpecialArenaInfo( "final" ) ) {
+ postgameMenuInfo.lastTier++;
+ }
+}
diff --git a/code/q3_ui/ui_spreset.c b/code/q3_ui/ui_spreset.c
new file mode 100644
index 0000000..35deb7c
--- /dev/null
+++ b/code/q3_ui/ui_spreset.c
@@ -0,0 +1,194 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+/*
+=======================================================================
+
+RESET MENU
+
+=======================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_FRAME "menu/art/cut_frame"
+
+#define ID_NO 100
+#define ID_YES 101
+
+typedef struct
+{
+ menuframework_s menu;
+ menutext_s no;
+ menutext_s yes;
+ int slashX;
+} resetMenu_t;
+
+static resetMenu_t s_reset;
+
+
+/*
+=================
+Reset_MenuEvent
+=================
+*/
+void Reset_MenuEvent(void* ptr, int event) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ UI_PopMenu();
+
+ if( ((menucommon_s*)ptr)->id == ID_NO ) {
+ return;
+ }
+
+ // reset the game, pop the level menu and restart it so it updates
+ UI_NewGame();
+ trap_Cvar_SetValue( "ui_spSelection", 0 );
+ UI_PopMenu();
+ UI_SPLevelMenu();
+}
+
+
+/*
+=================
+Reset_MenuKey
+=================
+*/
+static sfxHandle_t Reset_MenuKey( int key ) {
+ switch ( key ) {
+ case K_KP_LEFTARROW:
+ case K_LEFTARROW:
+ case K_KP_RIGHTARROW:
+ case K_RIGHTARROW:
+ key = K_TAB;
+ break;
+
+ case 'n':
+ case 'N':
+ Reset_MenuEvent( &s_reset.no, QM_ACTIVATED );
+ break;
+
+ case 'y':
+ case 'Y':
+ Reset_MenuEvent( &s_reset.yes, QM_ACTIVATED );
+ break;
+ }
+
+ return Menu_DefaultKey( &s_reset.menu, key );
+}
+
+
+/*
+=================
+Reset_MenuDraw
+=================
+*/
+static void Reset_MenuDraw( void ) {
+ UI_DrawNamedPic( 142, 118, 359, 256, ART_FRAME );
+ UI_DrawProportionalString( 320, 194 + 10, "RESET GAME?", UI_CENTER|UI_INVERSE, color_red );
+ UI_DrawProportionalString( s_reset.slashX, 265, "/", UI_LEFT|UI_INVERSE, color_red );
+ Menu_Draw( &s_reset.menu );
+
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 0, "WARNING: This resets all of the", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 1, "single player game variables.", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 2, "Do this only if you want to", UI_CENTER|UI_SMALLFONT, color_yellow );
+ UI_DrawProportionalString( SCREEN_WIDTH/2, 356 + PROP_HEIGHT * 3, "start over from the beginning.", UI_CENTER|UI_SMALLFONT, color_yellow );
+}
+
+
+/*
+=================
+Reset_Cache
+=================
+*/
+void Reset_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAME );
+}
+
+
+/*
+=================
+UI_ResetMenu
+=================
+*/
+void UI_ResetMenu(void) {
+ uiClientState_t cstate;
+ int n1, n2, n3;
+ int l1, l2, l3;
+
+ // zero set all our globals
+ memset( &s_reset, 0, sizeof(s_reset) );
+
+ Reset_Cache();
+
+ n1 = UI_ProportionalStringWidth( "YES/NO" );
+ n2 = UI_ProportionalStringWidth( "YES" ) + PROP_GAP_WIDTH;
+ n3 = UI_ProportionalStringWidth( "/" ) + PROP_GAP_WIDTH;
+ l1 = 320 - ( n1 / 2 );
+ l2 = l1 + n2;
+ l3 = l2 + n3;
+ s_reset.slashX = l2;
+
+ s_reset.menu.draw = Reset_MenuDraw;
+ s_reset.menu.key = Reset_MenuKey;
+ s_reset.menu.wrapAround = qtrue;
+
+ trap_GetClientState( &cstate );
+
+ if ( cstate.connState >= CA_CONNECTED ) {
+ // float on top of running game
+ s_reset.menu.fullscreen = qfalse;
+ }
+ else {
+ // game not running
+ s_reset.menu.fullscreen = qtrue;
+ }
+
+ s_reset.yes.generic.type = MTYPE_PTEXT;
+ s_reset.yes.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_reset.yes.generic.callback = Reset_MenuEvent;
+ s_reset.yes.generic.id = ID_YES;
+ s_reset.yes.generic.x = l1;
+ s_reset.yes.generic.y = 264;
+ s_reset.yes.string = "YES";
+ s_reset.yes.color = color_red;
+ s_reset.yes.style = UI_LEFT;
+
+ s_reset.no.generic.type = MTYPE_PTEXT;
+ s_reset.no.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_reset.no.generic.callback = Reset_MenuEvent;
+ s_reset.no.generic.id = ID_NO;
+ s_reset.no.generic.x = l3;
+ s_reset.no.generic.y = 264;
+ s_reset.no.string = "NO";
+ s_reset.no.color = color_red;
+ s_reset.no.style = UI_LEFT;
+
+ Menu_AddItem( &s_reset.menu, &s_reset.yes );
+ Menu_AddItem( &s_reset.menu, &s_reset.no );
+
+ UI_PushMenu( &s_reset.menu );
+
+ Menu_SetCursorToItem( &s_reset.menu, &s_reset.no );
+}
diff --git a/code/q3_ui/ui_spskill.c b/code/q3_ui/ui_spskill.c
new file mode 100644
index 0000000..7ae70d8
--- /dev/null
+++ b/code/q3_ui/ui_spskill.c
@@ -0,0 +1,329 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=============================================================================
+
+SINGLE PLAYER SKILL MENU
+
+=============================================================================
+*/
+
+#include "ui_local.h"
+
+
+#define ART_FRAME "menu/art/cut_frame"
+#define ART_BACK "menu/art/back_0.tga"
+#define ART_BACK_FOCUS "menu/art/back_1.tga"
+#define ART_FIGHT "menu/art/fight_0"
+#define ART_FIGHT_FOCUS "menu/art/fight_1"
+#define ART_MAP_COMPLETE1 "menu/art/level_complete1"
+#define ART_MAP_COMPLETE2 "menu/art/level_complete2"
+#define ART_MAP_COMPLETE3 "menu/art/level_complete3"
+#define ART_MAP_COMPLETE4 "menu/art/level_complete4"
+#define ART_MAP_COMPLETE5 "menu/art/level_complete5"
+
+#define ID_BABY 10
+#define ID_EASY 11
+#define ID_MEDIUM 12
+#define ID_HARD 13
+#define ID_NIGHTMARE 14
+#define ID_BACK 15
+#define ID_FIGHT 16
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menubitmap_s art_frame;
+ menutext_s art_banner;
+
+ menutext_s item_baby;
+ menutext_s item_easy;
+ menutext_s item_medium;
+ menutext_s item_hard;
+ menutext_s item_nightmare;
+
+ menubitmap_s art_skillPic;
+ menubitmap_s item_back;
+ menubitmap_s item_fight;
+
+ const char *arenaInfo;
+ qhandle_t skillpics[5];
+ sfxHandle_t nightmareSound;
+ sfxHandle_t silenceSound;
+} skillMenuInfo_t;
+
+static skillMenuInfo_t skillMenuInfo;
+
+
+static void SetSkillColor( int skill, vec4_t color ) {
+ switch( skill ) {
+ case 1:
+ skillMenuInfo.item_baby.color = color;
+ break;
+ case 2:
+ skillMenuInfo.item_easy.color = color;
+ break;
+ case 3:
+ skillMenuInfo.item_medium.color = color;
+ break;
+ case 4:
+ skillMenuInfo.item_hard.color = color;
+ break;
+ case 5:
+ skillMenuInfo.item_nightmare.color = color;
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+=================
+UI_SPSkillMenu_SkillEvent
+=================
+*/
+static void UI_SPSkillMenu_SkillEvent( void *ptr, int notification ) {
+ int id;
+ int skill;
+
+ if (notification != QM_ACTIVATED)
+ return;
+
+ SetSkillColor( (int)trap_Cvar_VariableValue( "g_spSkill" ), color_red );
+
+ id = ((menucommon_s*)ptr)->id;
+ skill = id - ID_BABY + 1;
+ trap_Cvar_SetValue( "g_spSkill", skill );
+
+ SetSkillColor( skill, color_white );
+ skillMenuInfo.art_skillPic.shader = skillMenuInfo.skillpics[skill - 1];
+
+ if( id == ID_NIGHTMARE ) {
+ trap_S_StartLocalSound( skillMenuInfo.nightmareSound, CHAN_ANNOUNCER );
+ }
+ else {
+ trap_S_StartLocalSound( skillMenuInfo.silenceSound, CHAN_ANNOUNCER );
+ }
+}
+
+
+/*
+=================
+UI_SPSkillMenu_FightEvent
+=================
+*/
+static void UI_SPSkillMenu_FightEvent( void *ptr, int notification ) {
+ if (notification != QM_ACTIVATED)
+ return;
+
+ UI_SPArena_Start( skillMenuInfo.arenaInfo );
+}
+
+
+/*
+=================
+UI_SPSkillMenu_BackEvent
+=================
+*/
+static void UI_SPSkillMenu_BackEvent( void* ptr, int notification ) {
+ if (notification != QM_ACTIVATED) {
+ return;
+ }
+
+ trap_S_StartLocalSound( skillMenuInfo.silenceSound, CHAN_ANNOUNCER );
+ UI_PopMenu();
+}
+
+
+/*
+=================
+UI_SPSkillMenu_Key
+=================
+*/
+static sfxHandle_t UI_SPSkillMenu_Key( int key ) {
+ if( key == K_MOUSE2 || key == K_ESCAPE ) {
+ trap_S_StartLocalSound( skillMenuInfo.silenceSound, CHAN_ANNOUNCER );
+ }
+ return Menu_DefaultKey( &skillMenuInfo.menu, key );
+}
+
+
+/*
+=================
+UI_SPSkillMenu_Cache
+=================
+*/
+void UI_SPSkillMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAME );
+ trap_R_RegisterShaderNoMip( ART_BACK );
+ trap_R_RegisterShaderNoMip( ART_BACK_FOCUS );
+ trap_R_RegisterShaderNoMip( ART_FIGHT );
+ trap_R_RegisterShaderNoMip( ART_FIGHT_FOCUS );
+ skillMenuInfo.skillpics[0] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE1 );
+ skillMenuInfo.skillpics[1] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE2 );
+ skillMenuInfo.skillpics[2] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE3 );
+ skillMenuInfo.skillpics[3] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE4 );
+ skillMenuInfo.skillpics[4] = trap_R_RegisterShaderNoMip( ART_MAP_COMPLETE5 );
+
+ skillMenuInfo.nightmareSound = trap_S_RegisterSound( "sound/misc/nightmare.wav", qfalse );
+ skillMenuInfo.silenceSound = trap_S_RegisterSound( "sound/misc/silence.wav", qfalse );
+}
+
+
+/*
+=================
+UI_SPSkillMenu_Init
+=================
+*/
+static void UI_SPSkillMenu_Init( void ) {
+ int skill;
+
+ memset( &skillMenuInfo, 0, sizeof(skillMenuInfo) );
+ skillMenuInfo.menu.fullscreen = qtrue;
+ skillMenuInfo.menu.key = UI_SPSkillMenu_Key;
+
+ UI_SPSkillMenu_Cache();
+
+ skillMenuInfo.art_frame.generic.type = MTYPE_BITMAP;
+ skillMenuInfo.art_frame.generic.name = ART_FRAME;
+ skillMenuInfo.art_frame.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ skillMenuInfo.art_frame.generic.x = 142;
+ skillMenuInfo.art_frame.generic.y = 118;
+ skillMenuInfo.art_frame.width = 359;
+ skillMenuInfo.art_frame.height = 256;
+
+ skillMenuInfo.art_banner.generic.type = MTYPE_BTEXT;
+ skillMenuInfo.art_banner.generic.flags = QMF_CENTER_JUSTIFY;
+ skillMenuInfo.art_banner.generic.x = 320;
+ skillMenuInfo.art_banner.generic.y = 16;
+ skillMenuInfo.art_banner.string = "DIFFICULTY";
+ skillMenuInfo.art_banner.color = color_white;
+ skillMenuInfo.art_banner.style = UI_CENTER;
+
+ skillMenuInfo.item_baby.generic.type = MTYPE_PTEXT;
+ skillMenuInfo.item_baby.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ skillMenuInfo.item_baby.generic.x = 320;
+ skillMenuInfo.item_baby.generic.y = 170;
+ skillMenuInfo.item_baby.generic.callback = UI_SPSkillMenu_SkillEvent;
+ skillMenuInfo.item_baby.generic.id = ID_BABY;
+ skillMenuInfo.item_baby.string = "I Can Win";
+ skillMenuInfo.item_baby.color = color_red;
+ skillMenuInfo.item_baby.style = UI_CENTER;
+
+ skillMenuInfo.item_easy.generic.type = MTYPE_PTEXT;
+ skillMenuInfo.item_easy.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ skillMenuInfo.item_easy.generic.x = 320;
+ skillMenuInfo.item_easy.generic.y = 198;
+ skillMenuInfo.item_easy.generic.callback = UI_SPSkillMenu_SkillEvent;
+ skillMenuInfo.item_easy.generic.id = ID_EASY;
+ skillMenuInfo.item_easy.string = "Bring It On";
+ skillMenuInfo.item_easy.color = color_red;
+ skillMenuInfo.item_easy.style = UI_CENTER;
+
+ skillMenuInfo.item_medium.generic.type = MTYPE_PTEXT;
+ skillMenuInfo.item_medium.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ skillMenuInfo.item_medium.generic.x = 320;
+ skillMenuInfo.item_medium.generic.y = 227;
+ skillMenuInfo.item_medium.generic.callback = UI_SPSkillMenu_SkillEvent;
+ skillMenuInfo.item_medium.generic.id = ID_MEDIUM;
+ skillMenuInfo.item_medium.string = "Hurt Me Plenty";
+ skillMenuInfo.item_medium.color = color_red;
+ skillMenuInfo.item_medium.style = UI_CENTER;
+
+ skillMenuInfo.item_hard.generic.type = MTYPE_PTEXT;
+ skillMenuInfo.item_hard.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ skillMenuInfo.item_hard.generic.x = 320;
+ skillMenuInfo.item_hard.generic.y = 255;
+ skillMenuInfo.item_hard.generic.callback = UI_SPSkillMenu_SkillEvent;
+ skillMenuInfo.item_hard.generic.id = ID_HARD;
+ skillMenuInfo.item_hard.string = "Hardcore";
+ skillMenuInfo.item_hard.color = color_red;
+ skillMenuInfo.item_hard.style = UI_CENTER;
+
+ skillMenuInfo.item_nightmare.generic.type = MTYPE_PTEXT;
+ skillMenuInfo.item_nightmare.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ skillMenuInfo.item_nightmare.generic.x = 320;
+ skillMenuInfo.item_nightmare.generic.y = 283;
+ skillMenuInfo.item_nightmare.generic.callback = UI_SPSkillMenu_SkillEvent;
+ skillMenuInfo.item_nightmare.generic.id = ID_NIGHTMARE;
+ skillMenuInfo.item_nightmare.string = "NIGHTMARE!";
+ skillMenuInfo.item_nightmare.color = color_red;
+ skillMenuInfo.item_nightmare.style = UI_CENTER;
+
+ skillMenuInfo.item_back.generic.type = MTYPE_BITMAP;
+ skillMenuInfo.item_back.generic.name = ART_BACK;
+ skillMenuInfo.item_back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ skillMenuInfo.item_back.generic.x = 0;
+ skillMenuInfo.item_back.generic.y = 480-64;
+ skillMenuInfo.item_back.generic.callback = UI_SPSkillMenu_BackEvent;
+ skillMenuInfo.item_back.generic.id = ID_BACK;
+ skillMenuInfo.item_back.width = 128;
+ skillMenuInfo.item_back.height = 64;
+ skillMenuInfo.item_back.focuspic = ART_BACK_FOCUS;
+
+ skillMenuInfo.art_skillPic.generic.type = MTYPE_BITMAP;
+ skillMenuInfo.art_skillPic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ skillMenuInfo.art_skillPic.generic.x = 320-64;
+ skillMenuInfo.art_skillPic.generic.y = 368;
+ skillMenuInfo.art_skillPic.width = 128;
+ skillMenuInfo.art_skillPic.height = 96;
+
+ skillMenuInfo.item_fight.generic.type = MTYPE_BITMAP;
+ skillMenuInfo.item_fight.generic.name = ART_FIGHT;
+ skillMenuInfo.item_fight.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ skillMenuInfo.item_fight.generic.callback = UI_SPSkillMenu_FightEvent;
+ skillMenuInfo.item_fight.generic.id = ID_FIGHT;
+ skillMenuInfo.item_fight.generic.x = 640;
+ skillMenuInfo.item_fight.generic.y = 480-64;
+ skillMenuInfo.item_fight.width = 128;
+ skillMenuInfo.item_fight.height = 64;
+ skillMenuInfo.item_fight.focuspic = ART_FIGHT_FOCUS;
+
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.art_frame );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.art_banner );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_baby );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_easy );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_medium );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_hard );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_nightmare );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.art_skillPic );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_back );
+ Menu_AddItem( &skillMenuInfo.menu, ( void * )&skillMenuInfo.item_fight );
+
+ skill = (int)Com_Clamp( 1, 5, trap_Cvar_VariableValue( "g_spSkill" ) );
+ SetSkillColor( skill, color_white );
+ skillMenuInfo.art_skillPic.shader = skillMenuInfo.skillpics[skill - 1];
+ if( skill == 5 ) {
+ trap_S_StartLocalSound( skillMenuInfo.nightmareSound, CHAN_ANNOUNCER );
+ }
+}
+
+
+void UI_SPSkillMenu( const char *arenaInfo ) {
+ UI_SPSkillMenu_Init();
+ skillMenuInfo.arenaInfo = arenaInfo;
+ UI_PushMenu( &skillMenuInfo.menu );
+ Menu_SetCursorToItem( &skillMenuInfo.menu, &skillMenuInfo.item_fight );
+}
diff --git a/code/q3_ui/ui_startserver.c b/code/q3_ui/ui_startserver.c
new file mode 100644
index 0000000..b249e0f
--- /dev/null
+++ b/code/q3_ui/ui_startserver.c
@@ -0,0 +1,1976 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=============================================================================
+
+START SERVER MENU *****
+
+=============================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define GAMESERVER_BACK0 "menu/art/back_0"
+#define GAMESERVER_BACK1 "menu/art/back_1"
+#define GAMESERVER_NEXT0 "menu/art/next_0"
+#define GAMESERVER_NEXT1 "menu/art/next_1"
+#define GAMESERVER_FRAMEL "menu/art/frame2_l"
+#define GAMESERVER_FRAMER "menu/art/frame1_r"
+#define GAMESERVER_SELECT "menu/art/maps_select"
+#define GAMESERVER_SELECTED "menu/art/maps_selected"
+#define GAMESERVER_FIGHT0 "menu/art/fight_0"
+#define GAMESERVER_FIGHT1 "menu/art/fight_1"
+#define GAMESERVER_UNKNOWNMAP "menu/art/unknownmap"
+#define GAMESERVER_ARROWS "menu/art/gs_arrows_0"
+#define GAMESERVER_ARROWSL "menu/art/gs_arrows_l"
+#define GAMESERVER_ARROWSR "menu/art/gs_arrows_r"
+
+#define MAX_MAPROWS 2
+#define MAX_MAPCOLS 2
+#define MAX_MAPSPERPAGE 4
+
+#define MAX_NAMELENGTH 16
+#define ID_GAMETYPE 10
+#define ID_PICTURES 11 // 12, 13, 14
+#define ID_PREVPAGE 15
+#define ID_NEXTPAGE 16
+#define ID_STARTSERVERBACK 17
+#define ID_STARTSERVERNEXT 18
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menulist_s gametype;
+ menubitmap_s mappics[MAX_MAPSPERPAGE];
+ menubitmap_s mapbuttons[MAX_MAPSPERPAGE];
+ menubitmap_s arrows;
+ menubitmap_s prevpage;
+ menubitmap_s nextpage;
+ menubitmap_s back;
+ menubitmap_s next;
+
+ menutext_s mapname;
+ menubitmap_s item_null;
+
+ qboolean multiplayer;
+ int currentmap;
+ int nummaps;
+ int page;
+ int maxpages;
+ int maplist[MAX_ARENAS];
+} startserver_t;
+
+static startserver_t s_startserver;
+
+static const char *gametype_items[] = {
+ "Free For All",
+ "Team Deathmatch",
+ "Tournament",
+ "Capture the Flag",
+ NULL
+};
+
+static int gametype_remap[] = {GT_FFA, GT_TEAM, GT_TOURNAMENT, GT_CTF};
+static int gametype_remap2[] = {0, 2, 0, 1, 3};
+
+// use ui_servers2.c definition
+extern const char* punkbuster_items[];
+
+static void UI_ServerOptionsMenu( qboolean multiplayer );
+
+
+/*
+=================
+GametypeBits
+=================
+*/
+static int GametypeBits( char *string ) {
+ int bits;
+ char *p;
+ char *token;
+
+ bits = 0;
+ p = string;
+ while( 1 ) {
+ token = COM_ParseExt( &p, qfalse );
+ if( token[0] == 0 ) {
+ break;
+ }
+
+ if( Q_stricmp( token, "ffa" ) == 0 ) {
+ bits |= 1 << GT_FFA;
+ continue;
+ }
+
+ if( Q_stricmp( token, "tourney" ) == 0 ) {
+ bits |= 1 << GT_TOURNAMENT;
+ continue;
+ }
+
+ if( Q_stricmp( token, "single" ) == 0 ) {
+ bits |= 1 << GT_SINGLE_PLAYER;
+ continue;
+ }
+
+ if( Q_stricmp( token, "team" ) == 0 ) {
+ bits |= 1 << GT_TEAM;
+ continue;
+ }
+
+ if( Q_stricmp( token, "ctf" ) == 0 ) {
+ bits |= 1 << GT_CTF;
+ continue;
+ }
+ }
+
+ return bits;
+}
+
+
+/*
+=================
+StartServer_Update
+=================
+*/
+static void StartServer_Update( void ) {
+ int i;
+ int top;
+ static char picname[MAX_MAPSPERPAGE][64];
+ const char *info;
+ char mapname[MAX_NAMELENGTH];
+
+ top = s_startserver.page*MAX_MAPSPERPAGE;
+
+ for (i=0; i<MAX_MAPSPERPAGE; i++)
+ {
+ if (top+i >= s_startserver.nummaps)
+ break;
+
+ info = UI_GetArenaInfoByNumber( s_startserver.maplist[ top + i ]);
+ Q_strncpyz( mapname, Info_ValueForKey( info, "map"), MAX_NAMELENGTH );
+ Q_strupr( mapname );
+
+ Com_sprintf( picname[i], sizeof(picname[i]), "levelshots/%s", mapname );
+
+ s_startserver.mappics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ s_startserver.mappics[i].generic.name = picname[i];
+ s_startserver.mappics[i].shader = 0;
+
+ // reset
+ s_startserver.mapbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
+ s_startserver.mapbuttons[i].generic.flags &= ~QMF_INACTIVE;
+ }
+
+ for (; i<MAX_MAPSPERPAGE; i++)
+ {
+ s_startserver.mappics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ s_startserver.mappics[i].generic.name = NULL;
+ s_startserver.mappics[i].shader = 0;
+
+ // disable
+ s_startserver.mapbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ s_startserver.mapbuttons[i].generic.flags |= QMF_INACTIVE;
+ }
+
+
+ // no servers to start
+ if( !s_startserver.nummaps ) {
+ s_startserver.next.generic.flags |= QMF_INACTIVE;
+
+ // set the map name
+ strcpy( s_startserver.mapname.string, "NO MAPS FOUND" );
+ }
+ else {
+ // set the highlight
+ s_startserver.next.generic.flags &= ~QMF_INACTIVE;
+ i = s_startserver.currentmap - top;
+ if ( i >=0 && i < MAX_MAPSPERPAGE )
+ {
+ s_startserver.mappics[i].generic.flags |= QMF_HIGHLIGHT;
+ s_startserver.mapbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ }
+
+ // set the map name
+ info = UI_GetArenaInfoByNumber( s_startserver.maplist[ s_startserver.currentmap ]);
+ Q_strncpyz( s_startserver.mapname.string, Info_ValueForKey( info, "map" ), MAX_NAMELENGTH);
+ }
+
+ Q_strupr( s_startserver.mapname.string );
+}
+
+
+/*
+=================
+StartServer_MapEvent
+=================
+*/
+static void StartServer_MapEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED) {
+ return;
+ }
+
+ s_startserver.currentmap = (s_startserver.page*MAX_MAPSPERPAGE) + (((menucommon_s*)ptr)->id - ID_PICTURES);
+ StartServer_Update();
+}
+
+
+/*
+=================
+StartServer_GametypeEvent
+=================
+*/
+static void StartServer_GametypeEvent( void* ptr, int event ) {
+ int i;
+ int count;
+ int gamebits;
+ int matchbits;
+ const char *info;
+
+ if( event != QM_ACTIVATED) {
+ return;
+ }
+
+ count = UI_GetNumArenas();
+ s_startserver.nummaps = 0;
+ matchbits = 1 << gametype_remap[s_startserver.gametype.curvalue];
+ if( gametype_remap[s_startserver.gametype.curvalue] == GT_FFA ) {
+ matchbits |= ( 1 << GT_SINGLE_PLAYER );
+ }
+ for( i = 0; i < count; i++ ) {
+ info = UI_GetArenaInfoByNumber( i );
+
+ gamebits = GametypeBits( Info_ValueForKey( info, "type") );
+ if( !( gamebits & matchbits ) ) {
+ continue;
+ }
+
+ s_startserver.maplist[ s_startserver.nummaps ] = i;
+ s_startserver.nummaps++;
+ }
+ s_startserver.maxpages = (s_startserver.nummaps + MAX_MAPSPERPAGE-1)/MAX_MAPSPERPAGE;
+ s_startserver.page = 0;
+ s_startserver.currentmap = 0;
+
+ StartServer_Update();
+}
+
+
+/*
+=================
+StartServer_MenuEvent
+=================
+*/
+static void StartServer_MenuEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_PREVPAGE:
+ if( s_startserver.page > 0 ) {
+ s_startserver.page--;
+ StartServer_Update();
+ }
+ break;
+
+ case ID_NEXTPAGE:
+ if( s_startserver.page < s_startserver.maxpages - 1 ) {
+ s_startserver.page++;
+ StartServer_Update();
+ }
+ break;
+
+ case ID_STARTSERVERNEXT:
+ trap_Cvar_SetValue( "g_gameType", gametype_remap[s_startserver.gametype.curvalue] );
+ UI_ServerOptionsMenu( s_startserver.multiplayer );
+ break;
+
+ case ID_STARTSERVERBACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+/*
+===============
+StartServer_LevelshotDraw
+===============
+*/
+static void StartServer_LevelshotDraw( void *self ) {
+ menubitmap_s *b;
+ int x;
+ int y;
+ int w;
+ int h;
+ int n;
+ const char *info;
+
+ b = (menubitmap_s *)self;
+
+ if( !b->generic.name ) {
+ return;
+ }
+
+ if( b->generic.name && !b->shader ) {
+ b->shader = trap_R_RegisterShaderNoMip( b->generic.name );
+ if( !b->shader && b->errorpic ) {
+ b->shader = trap_R_RegisterShaderNoMip( b->errorpic );
+ }
+ }
+
+ if( b->focuspic && !b->focusshader ) {
+ b->focusshader = trap_R_RegisterShaderNoMip( b->focuspic );
+ }
+
+ x = b->generic.x;
+ y = b->generic.y;
+ w = b->width;
+ h = b->height;
+ if( b->shader ) {
+ UI_DrawHandlePic( x, y, w, h, b->shader );
+ }
+
+ x = b->generic.x;
+ y = b->generic.y + b->height;
+ UI_FillRect( x, y, b->width, 28, colorBlack );
+
+ x += b->width / 2;
+ y += 4;
+ n = s_startserver.page * MAX_MAPSPERPAGE + b->generic.id - ID_PICTURES;
+
+ info = UI_GetArenaInfoByNumber( s_startserver.maplist[ n ]);
+ UI_DrawString( x, y, Info_ValueForKey( info, "map" ), UI_CENTER|UI_SMALLFONT, color_orange );
+
+ x = b->generic.x;
+ y = b->generic.y;
+ w = b->width;
+ h = b->height + 28;
+ if( b->generic.flags & QMF_HIGHLIGHT ) {
+ UI_DrawHandlePic( x, y, w, h, b->focusshader );
+ }
+}
+
+
+/*
+=================
+StartServer_MenuInit
+=================
+*/
+static void StartServer_MenuInit( void ) {
+ int i;
+ int x;
+ int y;
+ static char mapnamebuffer[64];
+
+ // zero set all our globals
+ memset( &s_startserver, 0 ,sizeof(startserver_t) );
+
+ StartServer_Cache();
+
+ s_startserver.menu.wrapAround = qtrue;
+ s_startserver.menu.fullscreen = qtrue;
+
+ s_startserver.banner.generic.type = MTYPE_BTEXT;
+ s_startserver.banner.generic.x = 320;
+ s_startserver.banner.generic.y = 16;
+ s_startserver.banner.string = "GAME SERVER";
+ s_startserver.banner.color = color_white;
+ s_startserver.banner.style = UI_CENTER;
+
+ s_startserver.framel.generic.type = MTYPE_BITMAP;
+ s_startserver.framel.generic.name = GAMESERVER_FRAMEL;
+ s_startserver.framel.generic.flags = QMF_INACTIVE;
+ s_startserver.framel.generic.x = 0;
+ s_startserver.framel.generic.y = 78;
+ s_startserver.framel.width = 256;
+ s_startserver.framel.height = 329;
+
+ s_startserver.framer.generic.type = MTYPE_BITMAP;
+ s_startserver.framer.generic.name = GAMESERVER_FRAMER;
+ s_startserver.framer.generic.flags = QMF_INACTIVE;
+ s_startserver.framer.generic.x = 376;
+ s_startserver.framer.generic.y = 76;
+ s_startserver.framer.width = 256;
+ s_startserver.framer.height = 334;
+
+ s_startserver.gametype.generic.type = MTYPE_SPINCONTROL;
+ s_startserver.gametype.generic.name = "Game Type:";
+ s_startserver.gametype.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_startserver.gametype.generic.callback = StartServer_GametypeEvent;
+ s_startserver.gametype.generic.id = ID_GAMETYPE;
+ s_startserver.gametype.generic.x = 320 - 24;
+ s_startserver.gametype.generic.y = 368;
+ s_startserver.gametype.itemnames = gametype_items;
+
+ for (i=0; i<MAX_MAPSPERPAGE; i++)
+ {
+ x = (i % MAX_MAPCOLS) * (128+8) + 188;
+ y = (i / MAX_MAPROWS) * (128+8) + 96;
+
+ s_startserver.mappics[i].generic.type = MTYPE_BITMAP;
+ s_startserver.mappics[i].generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_startserver.mappics[i].generic.x = x;
+ s_startserver.mappics[i].generic.y = y;
+ s_startserver.mappics[i].generic.id = ID_PICTURES+i;
+ s_startserver.mappics[i].width = 128;
+ s_startserver.mappics[i].height = 96;
+ s_startserver.mappics[i].focuspic = GAMESERVER_SELECTED;
+ s_startserver.mappics[i].errorpic = GAMESERVER_UNKNOWNMAP;
+ s_startserver.mappics[i].generic.ownerdraw = StartServer_LevelshotDraw;
+
+ s_startserver.mapbuttons[i].generic.type = MTYPE_BITMAP;
+ s_startserver.mapbuttons[i].generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_NODEFAULTINIT;
+ s_startserver.mapbuttons[i].generic.id = ID_PICTURES+i;
+ s_startserver.mapbuttons[i].generic.callback = StartServer_MapEvent;
+ s_startserver.mapbuttons[i].generic.x = x - 30;
+ s_startserver.mapbuttons[i].generic.y = y - 32;
+ s_startserver.mapbuttons[i].width = 256;
+ s_startserver.mapbuttons[i].height = 248;
+ s_startserver.mapbuttons[i].generic.left = x;
+ s_startserver.mapbuttons[i].generic.top = y;
+ s_startserver.mapbuttons[i].generic.right = x + 128;
+ s_startserver.mapbuttons[i].generic.bottom = y + 128;
+ s_startserver.mapbuttons[i].focuspic = GAMESERVER_SELECT;
+ }
+
+ s_startserver.arrows.generic.type = MTYPE_BITMAP;
+ s_startserver.arrows.generic.name = GAMESERVER_ARROWS;
+ s_startserver.arrows.generic.flags = QMF_INACTIVE;
+ s_startserver.arrows.generic.x = 260;
+ s_startserver.arrows.generic.y = 400;
+ s_startserver.arrows.width = 128;
+ s_startserver.arrows.height = 32;
+
+ s_startserver.prevpage.generic.type = MTYPE_BITMAP;
+ s_startserver.prevpage.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_startserver.prevpage.generic.callback = StartServer_MenuEvent;
+ s_startserver.prevpage.generic.id = ID_PREVPAGE;
+ s_startserver.prevpage.generic.x = 260;
+ s_startserver.prevpage.generic.y = 400;
+ s_startserver.prevpage.width = 64;
+ s_startserver.prevpage.height = 32;
+ s_startserver.prevpage.focuspic = GAMESERVER_ARROWSL;
+
+ s_startserver.nextpage.generic.type = MTYPE_BITMAP;
+ s_startserver.nextpage.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_startserver.nextpage.generic.callback = StartServer_MenuEvent;
+ s_startserver.nextpage.generic.id = ID_NEXTPAGE;
+ s_startserver.nextpage.generic.x = 321;
+ s_startserver.nextpage.generic.y = 400;
+ s_startserver.nextpage.width = 64;
+ s_startserver.nextpage.height = 32;
+ s_startserver.nextpage.focuspic = GAMESERVER_ARROWSR;
+
+ s_startserver.mapname.generic.type = MTYPE_PTEXT;
+ s_startserver.mapname.generic.flags = QMF_CENTER_JUSTIFY|QMF_INACTIVE;
+ s_startserver.mapname.generic.x = 320;
+ s_startserver.mapname.generic.y = 440;
+ s_startserver.mapname.string = mapnamebuffer;
+ s_startserver.mapname.style = UI_CENTER|UI_BIGFONT;
+ s_startserver.mapname.color = text_color_normal;
+
+ s_startserver.back.generic.type = MTYPE_BITMAP;
+ s_startserver.back.generic.name = GAMESERVER_BACK0;
+ s_startserver.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_startserver.back.generic.callback = StartServer_MenuEvent;
+ s_startserver.back.generic.id = ID_STARTSERVERBACK;
+ s_startserver.back.generic.x = 0;
+ s_startserver.back.generic.y = 480-64;
+ s_startserver.back.width = 128;
+ s_startserver.back.height = 64;
+ s_startserver.back.focuspic = GAMESERVER_BACK1;
+
+ s_startserver.next.generic.type = MTYPE_BITMAP;
+ s_startserver.next.generic.name = GAMESERVER_NEXT0;
+ s_startserver.next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_startserver.next.generic.callback = StartServer_MenuEvent;
+ s_startserver.next.generic.id = ID_STARTSERVERNEXT;
+ s_startserver.next.generic.x = 640;
+ s_startserver.next.generic.y = 480-64;
+ s_startserver.next.width = 128;
+ s_startserver.next.height = 64;
+ s_startserver.next.focuspic = GAMESERVER_NEXT1;
+
+ s_startserver.item_null.generic.type = MTYPE_BITMAP;
+ s_startserver.item_null.generic.flags = QMF_LEFT_JUSTIFY|QMF_MOUSEONLY|QMF_SILENT;
+ s_startserver.item_null.generic.x = 0;
+ s_startserver.item_null.generic.y = 0;
+ s_startserver.item_null.width = 640;
+ s_startserver.item_null.height = 480;
+
+ Menu_AddItem( &s_startserver.menu, &s_startserver.banner );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.framel );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.framer );
+
+ Menu_AddItem( &s_startserver.menu, &s_startserver.gametype );
+ for (i=0; i<MAX_MAPSPERPAGE; i++)
+ {
+ Menu_AddItem( &s_startserver.menu, &s_startserver.mappics[i] );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.mapbuttons[i] );
+ }
+
+ Menu_AddItem( &s_startserver.menu, &s_startserver.arrows );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.prevpage );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.nextpage );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.back );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.next );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.mapname );
+ Menu_AddItem( &s_startserver.menu, &s_startserver.item_null );
+
+ StartServer_GametypeEvent( NULL, QM_ACTIVATED );
+}
+
+
+/*
+=================
+StartServer_Cache
+=================
+*/
+void StartServer_Cache( void )
+{
+ int i;
+ const char *info;
+ qboolean precache;
+ char picname[64];
+ char mapname[ MAX_NAMELENGTH ];
+
+ trap_R_RegisterShaderNoMip( GAMESERVER_BACK0 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_BACK1 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_NEXT0 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_NEXT1 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_FRAMEL );
+ trap_R_RegisterShaderNoMip( GAMESERVER_FRAMER );
+ trap_R_RegisterShaderNoMip( GAMESERVER_SELECT );
+ trap_R_RegisterShaderNoMip( GAMESERVER_SELECTED );
+ trap_R_RegisterShaderNoMip( GAMESERVER_UNKNOWNMAP );
+ trap_R_RegisterShaderNoMip( GAMESERVER_ARROWS );
+ trap_R_RegisterShaderNoMip( GAMESERVER_ARROWSL );
+ trap_R_RegisterShaderNoMip( GAMESERVER_ARROWSR );
+
+ precache = trap_Cvar_VariableValue("com_buildscript");
+
+ if( precache ) {
+ for( i = 0; i < UI_GetNumArenas(); i++ ) {
+ info = UI_GetArenaInfoByNumber( i );
+ Q_strncpyz( mapname, Info_ValueForKey( info, "map"), MAX_NAMELENGTH );
+ Q_strupr( mapname );
+
+ Com_sprintf( picname, sizeof(picname), "levelshots/%s", mapname );
+ trap_R_RegisterShaderNoMip(picname);
+ }
+ }
+}
+
+
+/*
+=================
+UI_StartServerMenu
+=================
+*/
+void UI_StartServerMenu( qboolean multiplayer ) {
+ StartServer_MenuInit();
+ s_startserver.multiplayer = multiplayer;
+ UI_PushMenu( &s_startserver.menu );
+}
+
+
+
+/*
+=============================================================================
+
+SERVER OPTIONS MENU *****
+
+=============================================================================
+*/
+
+#define ID_PLAYER_TYPE 20
+#define ID_MAXCLIENTS 21
+#define ID_DEDICATED 22
+#define ID_GO 23
+#define ID_BACK 24
+
+#define PLAYER_SLOTS 12
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+
+ menubitmap_s mappic;
+ menubitmap_s picframe;
+
+ menulist_s dedicated;
+ menufield_s timelimit;
+ menufield_s fraglimit;
+ menufield_s flaglimit;
+ menuradiobutton_s friendlyfire;
+ menufield_s hostname;
+ menuradiobutton_s pure;
+ menulist_s botSkill;
+
+ menutext_s player0;
+ menulist_s playerType[PLAYER_SLOTS];
+ menutext_s playerName[PLAYER_SLOTS];
+ menulist_s playerTeam[PLAYER_SLOTS];
+
+ menubitmap_s go;
+ menubitmap_s next;
+ menubitmap_s back;
+
+ qboolean multiplayer;
+ int gametype;
+ char mapnamebuffer[32];
+ char playerNameBuffers[PLAYER_SLOTS][16];
+
+ qboolean newBot;
+ int newBotIndex;
+ char newBotName[16];
+
+ menulist_s punkbuster;
+} serveroptions_t;
+
+static serveroptions_t s_serveroptions;
+
+static const char *dedicated_list[] = {
+ "No",
+ "LAN",
+ "Internet",
+ NULL
+};
+
+static const char *playerType_list[] = {
+ "Open",
+ "Bot",
+ "----",
+ NULL
+};
+
+static const char *playerTeam_list[] = {
+ "Blue",
+ "Red",
+ NULL
+};
+
+static const char *botSkill_list[] = {
+ "I Can Win",
+ "Bring It On",
+ "Hurt Me Plenty",
+ "Hardcore",
+ "Nightmare!",
+ NULL
+};
+
+
+/*
+=================
+BotAlreadySelected
+=================
+*/
+static qboolean BotAlreadySelected( const char *checkName ) {
+ int n;
+
+ for( n = 1; n < PLAYER_SLOTS; n++ ) {
+ if( s_serveroptions.playerType[n].curvalue != 1 ) {
+ continue;
+ }
+ if( (s_serveroptions.gametype >= GT_TEAM) &&
+ (s_serveroptions.playerTeam[n].curvalue != s_serveroptions.playerTeam[s_serveroptions.newBotIndex].curvalue ) ) {
+ continue;
+ }
+ if( Q_stricmp( checkName, s_serveroptions.playerNameBuffers[n] ) == 0 ) {
+ return qtrue;
+ }
+ }
+
+ return qfalse;
+}
+
+
+/*
+=================
+ServerOptions_Start
+=================
+*/
+static void ServerOptions_Start( void ) {
+ int timelimit;
+ int fraglimit;
+ int maxclients;
+ int dedicated;
+ int friendlyfire;
+ int flaglimit;
+ int pure;
+ int skill;
+ int n;
+ char buf[64];
+ const char *info;
+
+ timelimit = atoi( s_serveroptions.timelimit.field.buffer );
+ fraglimit = atoi( s_serveroptions.fraglimit.field.buffer );
+ flaglimit = atoi( s_serveroptions.flaglimit.field.buffer );
+ dedicated = s_serveroptions.dedicated.curvalue;
+ friendlyfire = s_serveroptions.friendlyfire.curvalue;
+ pure = s_serveroptions.pure.curvalue;
+ skill = s_serveroptions.botSkill.curvalue + 1;
+
+ //set maxclients
+ for( n = 0, maxclients = 0; n < PLAYER_SLOTS; n++ ) {
+ if( s_serveroptions.playerType[n].curvalue == 2 ) {
+ continue;
+ }
+ if( (s_serveroptions.playerType[n].curvalue == 1) && (s_serveroptions.playerNameBuffers[n][0] == 0) ) {
+ continue;
+ }
+ maxclients++;
+ }
+
+ switch( s_serveroptions.gametype ) {
+ case GT_FFA:
+ default:
+ trap_Cvar_SetValue( "ui_ffa_fraglimit", fraglimit );
+ trap_Cvar_SetValue( "ui_ffa_timelimit", timelimit );
+ break;
+
+ case GT_TOURNAMENT:
+ trap_Cvar_SetValue( "ui_tourney_fraglimit", fraglimit );
+ trap_Cvar_SetValue( "ui_tourney_timelimit", timelimit );
+ break;
+
+ case GT_TEAM:
+ trap_Cvar_SetValue( "ui_team_fraglimit", fraglimit );
+ trap_Cvar_SetValue( "ui_team_timelimit", timelimit );
+ trap_Cvar_SetValue( "ui_team_friendlt", friendlyfire );
+ break;
+
+ case GT_CTF:
+ trap_Cvar_SetValue( "ui_ctf_fraglimit", fraglimit );
+ trap_Cvar_SetValue( "ui_ctf_timelimit", timelimit );
+ trap_Cvar_SetValue( "ui_ctf_friendlt", friendlyfire );
+ break;
+ }
+
+ trap_Cvar_SetValue( "sv_maxclients", Com_Clamp( 0, 12, maxclients ) );
+ trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, dedicated ) );
+ trap_Cvar_SetValue ("timelimit", Com_Clamp( 0, timelimit, timelimit ) );
+ trap_Cvar_SetValue ("fraglimit", Com_Clamp( 0, fraglimit, fraglimit ) );
+ trap_Cvar_SetValue ("capturelimit", Com_Clamp( 0, flaglimit, flaglimit ) );
+ trap_Cvar_SetValue( "g_friendlyfire", friendlyfire );
+ trap_Cvar_SetValue( "sv_pure", pure );
+ trap_Cvar_Set("sv_hostname", s_serveroptions.hostname.field.buffer );
+
+ trap_Cvar_SetValue( "sv_punkbuster", s_serveroptions.punkbuster.curvalue );
+
+ // the wait commands will allow the dedicated to take effect
+ info = UI_GetArenaInfoByNumber( s_startserver.maplist[ s_startserver.currentmap ]);
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", Info_ValueForKey( info, "map" )));
+
+ // add bots
+ trap_Cmd_ExecuteText( EXEC_APPEND, "wait 3\n" );
+ for( n = 1; n < PLAYER_SLOTS; n++ ) {
+ if( s_serveroptions.playerType[n].curvalue != 1 ) {
+ continue;
+ }
+ if( s_serveroptions.playerNameBuffers[n][0] == 0 ) {
+ continue;
+ }
+ if( s_serveroptions.playerNameBuffers[n][0] == '-' ) {
+ continue;
+ }
+ if( s_serveroptions.gametype >= GT_TEAM ) {
+ Com_sprintf( buf, sizeof(buf), "addbot %s %i %s\n", s_serveroptions.playerNameBuffers[n], skill,
+ playerTeam_list[s_serveroptions.playerTeam[n].curvalue] );
+ }
+ else {
+ Com_sprintf( buf, sizeof(buf), "addbot %s %i\n", s_serveroptions.playerNameBuffers[n], skill );
+ }
+ trap_Cmd_ExecuteText( EXEC_APPEND, buf );
+ }
+
+ // set player's team
+ if( dedicated == 0 && s_serveroptions.gametype >= GT_TEAM ) {
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait 5; team %s\n", playerTeam_list[s_serveroptions.playerTeam[0].curvalue] ) );
+ }
+}
+
+
+/*
+=================
+ServerOptions_InitPlayerItems
+=================
+*/
+static void ServerOptions_InitPlayerItems( void ) {
+ int n;
+ int v;
+
+ // init types
+ if( s_serveroptions.multiplayer ) {
+ v = 0; // open
+ }
+ else {
+ v = 1; // bot
+ }
+
+ for( n = 0; n < PLAYER_SLOTS; n++ ) {
+ s_serveroptions.playerType[n].curvalue = v;
+ }
+
+ if( s_serveroptions.multiplayer && (s_serveroptions.gametype < GT_TEAM) ) {
+ for( n = 8; n < PLAYER_SLOTS; n++ ) {
+ s_serveroptions.playerType[n].curvalue = 2;
+ }
+ }
+
+ // if not a dedicated server, first slot is reserved for the human on the server
+ if( s_serveroptions.dedicated.curvalue == 0 ) {
+ // human
+ s_serveroptions.playerType[0].generic.flags |= QMF_INACTIVE;
+ s_serveroptions.playerType[0].curvalue = 0;
+ trap_Cvar_VariableStringBuffer( "name", s_serveroptions.playerNameBuffers[0], sizeof(s_serveroptions.playerNameBuffers[0]) );
+ Q_CleanStr( s_serveroptions.playerNameBuffers[0] );
+ }
+
+ // init teams
+ if( s_serveroptions.gametype >= GT_TEAM ) {
+ for( n = 0; n < (PLAYER_SLOTS / 2); n++ ) {
+ s_serveroptions.playerTeam[n].curvalue = 0;
+ }
+ for( ; n < PLAYER_SLOTS; n++ ) {
+ s_serveroptions.playerTeam[n].curvalue = 1;
+ }
+ }
+ else {
+ for( n = 0; n < PLAYER_SLOTS; n++ ) {
+ s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ }
+ }
+}
+
+
+/*
+=================
+ServerOptions_SetPlayerItems
+=================
+*/
+static void ServerOptions_SetPlayerItems( void ) {
+ int start;
+ int n;
+
+ // types
+// for( n = 0; n < PLAYER_SLOTS; n++ ) {
+// if( (!s_serveroptions.multiplayer) && (n > 0) && (s_serveroptions.playerType[n].curvalue == 0) ) {
+// s_serveroptions.playerType[n].curvalue = 1;
+// }
+// }
+
+ // names
+ if( s_serveroptions.dedicated.curvalue == 0 ) {
+ s_serveroptions.player0.string = "Human";
+ s_serveroptions.playerName[0].generic.flags &= ~QMF_HIDDEN;
+
+ start = 1;
+ }
+ else {
+ s_serveroptions.player0.string = "Open";
+ start = 0;
+ }
+ for( n = start; n < PLAYER_SLOTS; n++ ) {
+ if( s_serveroptions.playerType[n].curvalue == 1 ) {
+ s_serveroptions.playerName[n].generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
+ }
+ else {
+ s_serveroptions.playerName[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ }
+ }
+
+ // teams
+ if( s_serveroptions.gametype < GT_TEAM ) {
+ return;
+ }
+ for( n = start; n < PLAYER_SLOTS; n++ ) {
+ if( s_serveroptions.playerType[n].curvalue == 2 ) {
+ s_serveroptions.playerTeam[n].generic.flags |= (QMF_INACTIVE|QMF_HIDDEN);
+ }
+ else {
+ s_serveroptions.playerTeam[n].generic.flags &= ~(QMF_INACTIVE|QMF_HIDDEN);
+ }
+ }
+}
+
+
+/*
+=================
+ServerOptions_Event
+=================
+*/
+static void ServerOptions_Event( void* ptr, int event ) {
+ switch( ((menucommon_s*)ptr)->id ) {
+
+ //if( event != QM_ACTIVATED && event != QM_LOSTFOCUS) {
+ // return;
+ //}
+ case ID_PLAYER_TYPE:
+ if( event != QM_ACTIVATED ) {
+ break;
+ }
+ ServerOptions_SetPlayerItems();
+ break;
+
+ case ID_MAXCLIENTS:
+ case ID_DEDICATED:
+ ServerOptions_SetPlayerItems();
+ break;
+ case ID_GO:
+ if( event != QM_ACTIVATED ) {
+ break;
+ }
+ ServerOptions_Start();
+ break;
+
+ case ID_STARTSERVERNEXT:
+ if( event != QM_ACTIVATED ) {
+ break;
+ }
+ break;
+ case ID_BACK:
+ if( event != QM_ACTIVATED ) {
+ break;
+ }
+ UI_PopMenu();
+ break;
+ }
+}
+
+
+static void ServerOptions_PlayerNameEvent( void* ptr, int event ) {
+ int n;
+
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ n = ((menutext_s*)ptr)->generic.id;
+ s_serveroptions.newBotIndex = n;
+ UI_BotSelectMenu( s_serveroptions.playerNameBuffers[n] );
+}
+
+
+/*
+=================
+ServerOptions_StatusBar
+=================
+*/
+static void ServerOptions_StatusBar( void* ptr ) {
+ switch( ((menucommon_s*)ptr)->id ) {
+ default:
+ UI_DrawString( 320, 440, "0 = NO LIMIT", UI_CENTER|UI_SMALLFONT, colorWhite );
+ break;
+ }
+}
+
+
+/*
+===============
+ServerOptions_LevelshotDraw
+===============
+*/
+static void ServerOptions_LevelshotDraw( void *self ) {
+ menubitmap_s *b;
+ int x;
+ int y;
+
+ // strange place for this, but it works
+ if( s_serveroptions.newBot ) {
+ Q_strncpyz( s_serveroptions.playerNameBuffers[s_serveroptions.newBotIndex], s_serveroptions.newBotName, 16 );
+ s_serveroptions.newBot = qfalse;
+ }
+
+ b = (menubitmap_s *)self;
+
+ Bitmap_Draw( b );
+
+ x = b->generic.x;
+ y = b->generic.y + b->height;
+ UI_FillRect( x, y, b->width, 40, colorBlack );
+
+ x += b->width / 2;
+ y += 4;
+ UI_DrawString( x, y, s_serveroptions.mapnamebuffer, UI_CENTER|UI_SMALLFONT, color_orange );
+
+ y += SMALLCHAR_HEIGHT;
+ UI_DrawString( x, y, gametype_items[gametype_remap2[s_serveroptions.gametype]], UI_CENTER|UI_SMALLFONT, color_orange );
+}
+
+
+static void ServerOptions_InitBotNames( void ) {
+ int count;
+ int n;
+ const char *arenaInfo;
+ const char *botInfo;
+ char *p;
+ char *bot;
+ char bots[MAX_INFO_STRING];
+
+ if( s_serveroptions.gametype >= GT_TEAM ) {
+ Q_strncpyz( s_serveroptions.playerNameBuffers[1], "grunt", 16 );
+ Q_strncpyz( s_serveroptions.playerNameBuffers[2], "major", 16 );
+ if( s_serveroptions.gametype == GT_TEAM ) {
+ Q_strncpyz( s_serveroptions.playerNameBuffers[3], "visor", 16 );
+ }
+ else {
+ s_serveroptions.playerType[3].curvalue = 2;
+ }
+ s_serveroptions.playerType[4].curvalue = 2;
+ s_serveroptions.playerType[5].curvalue = 2;
+
+ Q_strncpyz( s_serveroptions.playerNameBuffers[6], "sarge", 16 );
+ Q_strncpyz( s_serveroptions.playerNameBuffers[7], "grunt", 16 );
+ Q_strncpyz( s_serveroptions.playerNameBuffers[8], "major", 16 );
+ if( s_serveroptions.gametype == GT_TEAM ) {
+ Q_strncpyz( s_serveroptions.playerNameBuffers[9], "visor", 16 );
+ }
+ else {
+ s_serveroptions.playerType[9].curvalue = 2;
+ }
+ s_serveroptions.playerType[10].curvalue = 2;
+ s_serveroptions.playerType[11].curvalue = 2;
+
+ return;
+ }
+
+ count = 1; // skip the first slot, reserved for a human
+
+ // get info for this map
+ arenaInfo = UI_GetArenaInfoByMap( s_serveroptions.mapnamebuffer );
+
+ // get the bot info - we'll seed with them if any are listed
+ Q_strncpyz( bots, Info_ValueForKey( arenaInfo, "bots" ), sizeof(bots) );
+ p = &bots[0];
+ while( *p && count < PLAYER_SLOTS ) {
+ //skip spaces
+ while( *p && *p == ' ' ) {
+ p++;
+ }
+ if( !p ) {
+ break;
+ }
+
+ // mark start of bot name
+ bot = p;
+
+ // skip until space of null
+ while( *p && *p != ' ' ) {
+ p++;
+ }
+ if( *p ) {
+ *p++ = 0;
+ }
+
+ botInfo = UI_GetBotInfoByName( bot );
+ if( !botInfo )
+ {
+ botInfo = UI_GetBotInfoByNumber( count );
+ }
+ bot = Info_ValueForKey( botInfo, "name" );
+
+ Q_strncpyz( s_serveroptions.playerNameBuffers[count], bot, sizeof(s_serveroptions.playerNameBuffers[count]) );
+ count++;
+ }
+
+ // set the rest of the bot slots to "---"
+ for( n = count; n < PLAYER_SLOTS; n++ ) {
+ strcpy( s_serveroptions.playerNameBuffers[n], "--------" );
+ }
+
+ // pad up to #8 as open slots
+ for( ;count < 8; count++ ) {
+ s_serveroptions.playerType[count].curvalue = 0;
+ }
+
+ // close off the rest by default
+ for( ;count < PLAYER_SLOTS; count++ ) {
+ if( s_serveroptions.playerType[count].curvalue == 1 ) {
+ s_serveroptions.playerType[count].curvalue = 2;
+ }
+ }
+}
+
+
+/*
+=================
+ServerOptions_SetMenuItems
+=================
+*/
+static void ServerOptions_SetMenuItems( void ) {
+ static char picname[64];
+ char mapname[MAX_NAMELENGTH];
+ const char *info;
+
+ switch( s_serveroptions.gametype ) {
+ case GT_FFA:
+ default:
+ Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_fraglimit" ) ) );
+ Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ffa_timelimit" ) ) );
+ break;
+
+ case GT_TOURNAMENT:
+ Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_fraglimit" ) ) );
+ Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_tourney_timelimit" ) ) );
+ break;
+
+ case GT_TEAM:
+ Com_sprintf( s_serveroptions.fraglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_fraglimit" ) ) );
+ Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_team_timelimit" ) ) );
+ s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_team_friendly" ) );
+ break;
+
+ case GT_CTF:
+ Com_sprintf( s_serveroptions.flaglimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 100, trap_Cvar_VariableValue( "ui_ctf_capturelimit" ) ) );
+ Com_sprintf( s_serveroptions.timelimit.field.buffer, 4, "%i", (int)Com_Clamp( 0, 999, trap_Cvar_VariableValue( "ui_ctf_timelimit" ) ) );
+ s_serveroptions.friendlyfire.curvalue = (int)Com_Clamp( 0, 1, trap_Cvar_VariableValue( "ui_ctf_friendly" ) );
+ break;
+ }
+
+ Q_strncpyz( s_serveroptions.hostname.field.buffer, UI_Cvar_VariableString( "sv_hostname" ), sizeof( s_serveroptions.hostname.field.buffer ) );
+ s_serveroptions.pure.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_pure" ) );
+
+ // set the map pic
+ info = UI_GetArenaInfoByNumber( s_startserver.maplist[ s_startserver.currentmap ]);
+ Q_strncpyz( mapname, Info_ValueForKey( info, "map"), MAX_NAMELENGTH );
+ Q_strupr( mapname );
+ Com_sprintf( picname, 64, "levelshots/%s", mapname );
+ s_serveroptions.mappic.generic.name = picname;
+
+ // set the map name
+ strcpy( s_serveroptions.mapnamebuffer, s_startserver.mapname.string );
+ Q_strupr( s_serveroptions.mapnamebuffer );
+
+ // get the player selections initialized
+ ServerOptions_InitPlayerItems();
+ ServerOptions_SetPlayerItems();
+
+ // seed bot names
+ ServerOptions_InitBotNames();
+ ServerOptions_SetPlayerItems();
+}
+
+/*
+=================
+PlayerName_Draw
+=================
+*/
+static void PlayerName_Draw( void *item ) {
+ menutext_s *s;
+ float *color;
+ int x, y;
+ int style;
+ qboolean focus;
+
+ s = (menutext_s *)item;
+
+ x = s->generic.x;
+ y = s->generic.y;
+
+ style = UI_SMALLFONT;
+ focus = (s->generic.parent->cursor == s->generic.menuPosition);
+
+ if ( s->generic.flags & QMF_GRAYED )
+ color = text_color_disabled;
+ else if ( focus )
+ {
+ color = text_color_highlight;
+ style |= UI_PULSE;
+ }
+ else if ( s->generic.flags & QMF_BLINK )
+ {
+ color = text_color_highlight;
+ style |= UI_BLINK;
+ }
+ else
+ color = text_color_normal;
+
+ if ( focus )
+ {
+ // draw cursor
+ UI_FillRect( s->generic.left, s->generic.top, s->generic.right-s->generic.left+1, s->generic.bottom-s->generic.top+1, listbar_color );
+ UI_DrawChar( x, y, 13, UI_CENTER|UI_BLINK|UI_SMALLFONT, color);
+ }
+
+ UI_DrawString( x - SMALLCHAR_WIDTH, y, s->generic.name, style|UI_RIGHT, color );
+ UI_DrawString( x + SMALLCHAR_WIDTH, y, s->string, style|UI_LEFT, color );
+}
+
+
+/*
+=================
+ServerOptions_MenuInit
+=================
+*/
+#define OPTIONS_X 456
+
+static void ServerOptions_MenuInit( qboolean multiplayer ) {
+ int y;
+ int n;
+
+ memset( &s_serveroptions, 0 ,sizeof(serveroptions_t) );
+ s_serveroptions.multiplayer = multiplayer;
+ s_serveroptions.gametype = (int)Com_Clamp( 0, 5, trap_Cvar_VariableValue( "g_gameType" ) );
+ s_serveroptions.punkbuster.curvalue = Com_Clamp( 0, 1, trap_Cvar_VariableValue( "sv_punkbuster" ) );
+
+ ServerOptions_Cache();
+
+ s_serveroptions.menu.wrapAround = qtrue;
+ s_serveroptions.menu.fullscreen = qtrue;
+
+ s_serveroptions.banner.generic.type = MTYPE_BTEXT;
+ s_serveroptions.banner.generic.x = 320;
+ s_serveroptions.banner.generic.y = 16;
+ s_serveroptions.banner.string = "GAME SERVER";
+ s_serveroptions.banner.color = color_white;
+ s_serveroptions.banner.style = UI_CENTER;
+
+ s_serveroptions.mappic.generic.type = MTYPE_BITMAP;
+ s_serveroptions.mappic.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ s_serveroptions.mappic.generic.x = 352;
+ s_serveroptions.mappic.generic.y = 80;
+ s_serveroptions.mappic.width = 160;
+ s_serveroptions.mappic.height = 120;
+ s_serveroptions.mappic.errorpic = GAMESERVER_UNKNOWNMAP;
+ s_serveroptions.mappic.generic.ownerdraw = ServerOptions_LevelshotDraw;
+
+ s_serveroptions.picframe.generic.type = MTYPE_BITMAP;
+ s_serveroptions.picframe.generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE|QMF_HIGHLIGHT;
+ s_serveroptions.picframe.generic.x = 352 - 38;
+ s_serveroptions.picframe.generic.y = 80 - 40;
+ s_serveroptions.picframe.width = 320;
+ s_serveroptions.picframe.height = 320;
+ s_serveroptions.picframe.focuspic = GAMESERVER_SELECT;
+
+ y = 272;
+ if( s_serveroptions.gametype != GT_CTF ) {
+ s_serveroptions.fraglimit.generic.type = MTYPE_FIELD;
+ s_serveroptions.fraglimit.generic.name = "Frag Limit:";
+ s_serveroptions.fraglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.fraglimit.generic.x = OPTIONS_X;
+ s_serveroptions.fraglimit.generic.y = y;
+ s_serveroptions.fraglimit.generic.statusbar = ServerOptions_StatusBar;
+ s_serveroptions.fraglimit.field.widthInChars = 3;
+ s_serveroptions.fraglimit.field.maxchars = 3;
+ }
+ else {
+ s_serveroptions.flaglimit.generic.type = MTYPE_FIELD;
+ s_serveroptions.flaglimit.generic.name = "Capture Limit:";
+ s_serveroptions.flaglimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.flaglimit.generic.x = OPTIONS_X;
+ s_serveroptions.flaglimit.generic.y = y;
+ s_serveroptions.flaglimit.generic.statusbar = ServerOptions_StatusBar;
+ s_serveroptions.flaglimit.field.widthInChars = 3;
+ s_serveroptions.flaglimit.field.maxchars = 3;
+ }
+
+ y += BIGCHAR_HEIGHT+2;
+ s_serveroptions.timelimit.generic.type = MTYPE_FIELD;
+ s_serveroptions.timelimit.generic.name = "Time Limit:";
+ s_serveroptions.timelimit.generic.flags = QMF_NUMBERSONLY|QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.timelimit.generic.x = OPTIONS_X;
+ s_serveroptions.timelimit.generic.y = y;
+ s_serveroptions.timelimit.generic.statusbar = ServerOptions_StatusBar;
+ s_serveroptions.timelimit.field.widthInChars = 3;
+ s_serveroptions.timelimit.field.maxchars = 3;
+
+ if( s_serveroptions.gametype >= GT_TEAM ) {
+ y += BIGCHAR_HEIGHT+2;
+ s_serveroptions.friendlyfire.generic.type = MTYPE_RADIOBUTTON;
+ s_serveroptions.friendlyfire.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.friendlyfire.generic.x = OPTIONS_X;
+ s_serveroptions.friendlyfire.generic.y = y;
+ s_serveroptions.friendlyfire.generic.name = "Friendly Fire:";
+ }
+
+ y += BIGCHAR_HEIGHT+2;
+ s_serveroptions.pure.generic.type = MTYPE_RADIOBUTTON;
+ s_serveroptions.pure.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.pure.generic.x = OPTIONS_X;
+ s_serveroptions.pure.generic.y = y;
+ s_serveroptions.pure.generic.name = "Pure Server:";
+
+ if( s_serveroptions.multiplayer ) {
+ y += BIGCHAR_HEIGHT+2;
+ s_serveroptions.dedicated.generic.type = MTYPE_SPINCONTROL;
+ s_serveroptions.dedicated.generic.id = ID_DEDICATED;
+ s_serveroptions.dedicated.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.dedicated.generic.callback = ServerOptions_Event;
+ s_serveroptions.dedicated.generic.x = OPTIONS_X;
+ s_serveroptions.dedicated.generic.y = y;
+ s_serveroptions.dedicated.generic.name = "Dedicated:";
+ s_serveroptions.dedicated.itemnames = dedicated_list;
+ }
+
+ if( s_serveroptions.multiplayer ) {
+ y += BIGCHAR_HEIGHT+2;
+ s_serveroptions.hostname.generic.type = MTYPE_FIELD;
+ s_serveroptions.hostname.generic.name = "Hostname:";
+ s_serveroptions.hostname.generic.flags = QMF_SMALLFONT;
+ s_serveroptions.hostname.generic.x = OPTIONS_X;
+ s_serveroptions.hostname.generic.y = y;
+ s_serveroptions.hostname.field.widthInChars = 18;
+ s_serveroptions.hostname.field.maxchars = 64;
+ }
+
+ y += BIGCHAR_HEIGHT+2;
+ s_serveroptions.punkbuster.generic.type = MTYPE_SPINCONTROL;
+ s_serveroptions.punkbuster.generic.name = "Punkbuster:";
+ s_serveroptions.punkbuster.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.punkbuster.generic.id = 0;
+ s_serveroptions.punkbuster.generic.x = OPTIONS_X;
+ s_serveroptions.punkbuster.generic.y = y;
+ s_serveroptions.punkbuster.itemnames = punkbuster_items;
+
+ y = 80;
+ s_serveroptions.botSkill.generic.type = MTYPE_SPINCONTROL;
+ s_serveroptions.botSkill.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_serveroptions.botSkill.generic.name = "Bot Skill: ";
+ s_serveroptions.botSkill.generic.x = 32 + (strlen(s_serveroptions.botSkill.generic.name) + 2 ) * SMALLCHAR_WIDTH;
+ s_serveroptions.botSkill.generic.y = y;
+ s_serveroptions.botSkill.itemnames = botSkill_list;
+ s_serveroptions.botSkill.curvalue = 1;
+
+ y += ( 2 * SMALLCHAR_HEIGHT );
+ s_serveroptions.player0.generic.type = MTYPE_TEXT;
+ s_serveroptions.player0.generic.flags = QMF_SMALLFONT;
+ s_serveroptions.player0.generic.x = 32 + SMALLCHAR_WIDTH;
+ s_serveroptions.player0.generic.y = y;
+ s_serveroptions.player0.color = color_orange;
+ s_serveroptions.player0.style = UI_LEFT|UI_SMALLFONT;
+
+ for( n = 0; n < PLAYER_SLOTS; n++ ) {
+ s_serveroptions.playerType[n].generic.type = MTYPE_SPINCONTROL;
+ s_serveroptions.playerType[n].generic.flags = QMF_SMALLFONT;
+ s_serveroptions.playerType[n].generic.id = ID_PLAYER_TYPE;
+ s_serveroptions.playerType[n].generic.callback = ServerOptions_Event;
+ s_serveroptions.playerType[n].generic.x = 32;
+ s_serveroptions.playerType[n].generic.y = y;
+ s_serveroptions.playerType[n].itemnames = playerType_list;
+
+ s_serveroptions.playerName[n].generic.type = MTYPE_TEXT;
+ s_serveroptions.playerName[n].generic.flags = QMF_SMALLFONT;
+ s_serveroptions.playerName[n].generic.x = 96;
+ s_serveroptions.playerName[n].generic.y = y;
+ s_serveroptions.playerName[n].generic.callback = ServerOptions_PlayerNameEvent;
+ s_serveroptions.playerName[n].generic.id = n;
+ s_serveroptions.playerName[n].generic.ownerdraw = PlayerName_Draw;
+ s_serveroptions.playerName[n].color = color_orange;
+ s_serveroptions.playerName[n].style = UI_SMALLFONT;
+ s_serveroptions.playerName[n].string = s_serveroptions.playerNameBuffers[n];
+ s_serveroptions.playerName[n].generic.top = s_serveroptions.playerName[n].generic.y;
+ s_serveroptions.playerName[n].generic.bottom = s_serveroptions.playerName[n].generic.y + SMALLCHAR_HEIGHT;
+ s_serveroptions.playerName[n].generic.left = s_serveroptions.playerName[n].generic.x - SMALLCHAR_HEIGHT/ 2;
+ s_serveroptions.playerName[n].generic.right = s_serveroptions.playerName[n].generic.x + 16 * SMALLCHAR_WIDTH;
+
+ s_serveroptions.playerTeam[n].generic.type = MTYPE_SPINCONTROL;
+ s_serveroptions.playerTeam[n].generic.flags = QMF_SMALLFONT;
+ s_serveroptions.playerTeam[n].generic.x = 240;
+ s_serveroptions.playerTeam[n].generic.y = y;
+ s_serveroptions.playerTeam[n].itemnames = playerTeam_list;
+
+ y += ( SMALLCHAR_HEIGHT + 4 );
+ }
+
+ s_serveroptions.back.generic.type = MTYPE_BITMAP;
+ s_serveroptions.back.generic.name = GAMESERVER_BACK0;
+ s_serveroptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_serveroptions.back.generic.callback = ServerOptions_Event;
+ s_serveroptions.back.generic.id = ID_BACK;
+ s_serveroptions.back.generic.x = 0;
+ s_serveroptions.back.generic.y = 480-64;
+ s_serveroptions.back.width = 128;
+ s_serveroptions.back.height = 64;
+ s_serveroptions.back.focuspic = GAMESERVER_BACK1;
+
+ s_serveroptions.next.generic.type = MTYPE_BITMAP;
+ s_serveroptions.next.generic.name = GAMESERVER_NEXT0;
+ s_serveroptions.next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE|QMF_GRAYED|QMF_HIDDEN;
+ s_serveroptions.next.generic.callback = ServerOptions_Event;
+ s_serveroptions.next.generic.id = ID_STARTSERVERNEXT;
+ s_serveroptions.next.generic.x = 640;
+ s_serveroptions.next.generic.y = 480-64-72;
+ s_serveroptions.next.generic.statusbar = ServerOptions_StatusBar;
+ s_serveroptions.next.width = 128;
+ s_serveroptions.next.height = 64;
+ s_serveroptions.next.focuspic = GAMESERVER_NEXT1;
+
+ s_serveroptions.go.generic.type = MTYPE_BITMAP;
+ s_serveroptions.go.generic.name = GAMESERVER_FIGHT0;
+ s_serveroptions.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_serveroptions.go.generic.callback = ServerOptions_Event;
+ s_serveroptions.go.generic.id = ID_GO;
+ s_serveroptions.go.generic.x = 640;
+ s_serveroptions.go.generic.y = 480-64;
+ s_serveroptions.go.width = 128;
+ s_serveroptions.go.height = 64;
+ s_serveroptions.go.focuspic = GAMESERVER_FIGHT1;
+
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.banner );
+
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.mappic );
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.picframe );
+
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.botSkill );
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.player0 );
+ for( n = 0; n < PLAYER_SLOTS; n++ ) {
+ if( n != 0 ) {
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerType[n] );
+ }
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerName[n] );
+ if( s_serveroptions.gametype >= GT_TEAM ) {
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.playerTeam[n] );
+ }
+ }
+
+ if( s_serveroptions.gametype != GT_CTF ) {
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.fraglimit );
+ }
+ else {
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.flaglimit );
+ }
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.timelimit );
+ if( s_serveroptions.gametype >= GT_TEAM ) {
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.friendlyfire );
+ }
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.pure );
+ if( s_serveroptions.multiplayer ) {
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.dedicated );
+ }
+ if( s_serveroptions.multiplayer ) {
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.hostname );
+ }
+
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.back );
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.next );
+ Menu_AddItem( &s_serveroptions.menu, &s_serveroptions.go );
+
+ Menu_AddItem( &s_serveroptions.menu, (void*) &s_serveroptions.punkbuster );
+
+ ServerOptions_SetMenuItems();
+}
+
+/*
+=================
+ServerOptions_Cache
+=================
+*/
+void ServerOptions_Cache( void ) {
+ trap_R_RegisterShaderNoMip( GAMESERVER_BACK0 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_BACK1 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT0 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_FIGHT1 );
+ trap_R_RegisterShaderNoMip( GAMESERVER_SELECT );
+ trap_R_RegisterShaderNoMip( GAMESERVER_UNKNOWNMAP );
+}
+
+
+/*
+=================
+UI_ServerOptionsMenu
+=================
+*/
+static void UI_ServerOptionsMenu( qboolean multiplayer ) {
+ ServerOptions_MenuInit( multiplayer );
+ UI_PushMenu( &s_serveroptions.menu );
+}
+
+
+
+/*
+=============================================================================
+
+BOT SELECT MENU *****
+
+=============================================================================
+*/
+
+
+#define BOTSELECT_BACK0 "menu/art/back_0"
+#define BOTSELECT_BACK1 "menu/art/back_1"
+#define BOTSELECT_ACCEPT0 "menu/art/accept_0"
+#define BOTSELECT_ACCEPT1 "menu/art/accept_1"
+#define BOTSELECT_SELECT "menu/art/opponents_select"
+#define BOTSELECT_SELECTED "menu/art/opponents_selected"
+#define BOTSELECT_ARROWS "menu/art/gs_arrows_0"
+#define BOTSELECT_ARROWSL "menu/art/gs_arrows_l"
+#define BOTSELECT_ARROWSR "menu/art/gs_arrows_r"
+
+#define PLAYERGRID_COLS 4
+#define PLAYERGRID_ROWS 4
+#define MAX_MODELSPERPAGE (PLAYERGRID_ROWS * PLAYERGRID_COLS)
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+
+ menubitmap_s pics[MAX_MODELSPERPAGE];
+ menubitmap_s picbuttons[MAX_MODELSPERPAGE];
+ menutext_s picnames[MAX_MODELSPERPAGE];
+
+ menubitmap_s arrows;
+ menubitmap_s left;
+ menubitmap_s right;
+
+ menubitmap_s go;
+ menubitmap_s back;
+
+ int numBots;
+ int modelpage;
+ int numpages;
+ int selectedmodel;
+ int sortedBotNums[MAX_BOTS];
+ char boticons[MAX_MODELSPERPAGE][MAX_QPATH];
+ char botnames[MAX_MODELSPERPAGE][16];
+} botSelectInfo_t;
+
+static botSelectInfo_t botSelectInfo;
+
+
+/*
+=================
+UI_BotSelectMenu_SortCompare
+=================
+*/
+static int QDECL UI_BotSelectMenu_SortCompare( const void *arg1, const void *arg2 ) {
+ int num1, num2;
+ const char *info1, *info2;
+ const char *name1, *name2;
+
+ num1 = *(int *)arg1;
+ num2 = *(int *)arg2;
+
+ info1 = UI_GetBotInfoByNumber( num1 );
+ info2 = UI_GetBotInfoByNumber( num2 );
+
+ name1 = Info_ValueForKey( info1, "name" );
+ name2 = Info_ValueForKey( info2, "name" );
+
+ return Q_stricmp( name1, name2 );
+}
+
+
+/*
+=================
+UI_BotSelectMenu_BuildList
+=================
+*/
+static void UI_BotSelectMenu_BuildList( void ) {
+ int n;
+
+ botSelectInfo.modelpage = 0;
+ botSelectInfo.numBots = UI_GetNumBots();
+ botSelectInfo.numpages = botSelectInfo.numBots / MAX_MODELSPERPAGE;
+ if( botSelectInfo.numBots % MAX_MODELSPERPAGE ) {
+ botSelectInfo.numpages++;
+ }
+
+ // initialize the array
+ for( n = 0; n < botSelectInfo.numBots; n++ ) {
+ botSelectInfo.sortedBotNums[n] = n;
+ }
+
+ // now sort it
+ qsort( botSelectInfo.sortedBotNums, botSelectInfo.numBots, sizeof(botSelectInfo.sortedBotNums[0]), UI_BotSelectMenu_SortCompare );
+}
+
+
+/*
+=================
+ServerPlayerIcon
+=================
+*/
+static void ServerPlayerIcon( const char *modelAndSkin, char *iconName, int iconNameMaxSize ) {
+ char *skin;
+ char model[MAX_QPATH];
+
+ Q_strncpyz( model, modelAndSkin, sizeof(model));
+ skin = Q_strrchr( model, '/' );
+ if ( skin ) {
+ *skin++ = '\0';
+ }
+ else {
+ skin = "default";
+ }
+
+ Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_%s.tga", model, skin );
+
+ if( !trap_R_RegisterShaderNoMip( iconName ) && Q_stricmp( skin, "default" ) != 0 ) {
+ Com_sprintf(iconName, iconNameMaxSize, "models/players/%s/icon_default.tga", model );
+ }
+}
+
+
+/*
+=================
+UI_BotSelectMenu_UpdateGrid
+=================
+*/
+static void UI_BotSelectMenu_UpdateGrid( void ) {
+ const char *info;
+ int i;
+ int j;
+
+ j = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
+ for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++, j++) {
+ if( j < botSelectInfo.numBots ) {
+ info = UI_GetBotInfoByNumber( botSelectInfo.sortedBotNums[j] );
+ ServerPlayerIcon( Info_ValueForKey( info, "model" ), botSelectInfo.boticons[i], MAX_QPATH );
+ Q_strncpyz( botSelectInfo.botnames[i], Info_ValueForKey( info, "name" ), 16 );
+ Q_CleanStr( botSelectInfo.botnames[i] );
+ botSelectInfo.pics[i].generic.name = botSelectInfo.boticons[i];
+ if( BotAlreadySelected( botSelectInfo.botnames[i] ) ) {
+ botSelectInfo.picnames[i].color = color_red;
+ }
+ else {
+ botSelectInfo.picnames[i].color = color_orange;
+ }
+ botSelectInfo.picbuttons[i].generic.flags &= ~QMF_INACTIVE;
+ }
+ else {
+ // dead slot
+ botSelectInfo.pics[i].generic.name = NULL;
+ botSelectInfo.picbuttons[i].generic.flags |= QMF_INACTIVE;
+ botSelectInfo.botnames[i][0] = 0;
+ }
+
+ botSelectInfo.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ botSelectInfo.pics[i].shader = 0;
+ botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
+ }
+
+ // set selected model
+ i = botSelectInfo.selectedmodel % MAX_MODELSPERPAGE;
+ botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
+ botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+
+ if( botSelectInfo.numpages > 1 ) {
+ if( botSelectInfo.modelpage > 0 ) {
+ botSelectInfo.left.generic.flags &= ~QMF_INACTIVE;
+ }
+ else {
+ botSelectInfo.left.generic.flags |= QMF_INACTIVE;
+ }
+
+ if( botSelectInfo.modelpage < (botSelectInfo.numpages - 1) ) {
+ botSelectInfo.right.generic.flags &= ~QMF_INACTIVE;
+ }
+ else {
+ botSelectInfo.right.generic.flags |= QMF_INACTIVE;
+ }
+ }
+ else {
+ // hide left/right markers
+ botSelectInfo.left.generic.flags |= QMF_INACTIVE;
+ botSelectInfo.right.generic.flags |= QMF_INACTIVE;
+ }
+}
+
+
+/*
+=================
+UI_BotSelectMenu_Default
+=================
+*/
+static void UI_BotSelectMenu_Default( char *bot ) {
+ const char *botInfo;
+ const char *test;
+ int n;
+ int i;
+
+ for( n = 0; n < botSelectInfo.numBots; n++ ) {
+ botInfo = UI_GetBotInfoByNumber( n );
+ test = Info_ValueForKey( botInfo, "name" );
+ if( Q_stricmp( bot, test ) == 0 ) {
+ break;
+ }
+ }
+ if( n == botSelectInfo.numBots ) {
+ botSelectInfo.selectedmodel = 0;
+ return;
+ }
+
+ for( i = 0; i < botSelectInfo.numBots; i++ ) {
+ if( botSelectInfo.sortedBotNums[i] == n ) {
+ break;
+ }
+ }
+ if( i == botSelectInfo.numBots ) {
+ botSelectInfo.selectedmodel = 0;
+ return;
+ }
+
+ botSelectInfo.selectedmodel = i;
+}
+
+
+/*
+=================
+UI_BotSelectMenu_LeftEvent
+=================
+*/
+static void UI_BotSelectMenu_LeftEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ if( botSelectInfo.modelpage > 0 ) {
+ botSelectInfo.modelpage--;
+ botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
+ UI_BotSelectMenu_UpdateGrid();
+ }
+}
+
+
+/*
+=================
+UI_BotSelectMenu_RightEvent
+=================
+*/
+static void UI_BotSelectMenu_RightEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ if( botSelectInfo.modelpage < botSelectInfo.numpages - 1 ) {
+ botSelectInfo.modelpage++;
+ botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE;
+ UI_BotSelectMenu_UpdateGrid();
+ }
+}
+
+
+/*
+=================
+UI_BotSelectMenu_BotEvent
+=================
+*/
+static void UI_BotSelectMenu_BotEvent( void* ptr, int event ) {
+ int i;
+
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ for( i = 0; i < (PLAYERGRID_ROWS * PLAYERGRID_COLS); i++ ) {
+ botSelectInfo.pics[i].generic.flags &= ~QMF_HIGHLIGHT;
+ botSelectInfo.picbuttons[i].generic.flags |= QMF_PULSEIFFOCUS;
+ }
+
+ // set selected
+ i = ((menucommon_s*)ptr)->id;
+ botSelectInfo.pics[i].generic.flags |= QMF_HIGHLIGHT;
+ botSelectInfo.picbuttons[i].generic.flags &= ~QMF_PULSEIFFOCUS;
+ botSelectInfo.selectedmodel = botSelectInfo.modelpage * MAX_MODELSPERPAGE + i;
+}
+
+
+/*
+=================
+UI_BotSelectMenu_BackEvent
+=================
+*/
+static void UI_BotSelectMenu_BackEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ UI_PopMenu();
+}
+
+
+/*
+=================
+UI_BotSelectMenu_SelectEvent
+=================
+*/
+static void UI_BotSelectMenu_SelectEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ UI_PopMenu();
+
+ s_serveroptions.newBot = qtrue;
+ Q_strncpyz( s_serveroptions.newBotName, botSelectInfo.botnames[botSelectInfo.selectedmodel % MAX_MODELSPERPAGE], 16 );
+}
+
+
+/*
+=================
+UI_BotSelectMenu_Cache
+=================
+*/
+void UI_BotSelectMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( BOTSELECT_BACK0 );
+ trap_R_RegisterShaderNoMip( BOTSELECT_BACK1 );
+ trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT0 );
+ trap_R_RegisterShaderNoMip( BOTSELECT_ACCEPT1 );
+ trap_R_RegisterShaderNoMip( BOTSELECT_SELECT );
+ trap_R_RegisterShaderNoMip( BOTSELECT_SELECTED );
+ trap_R_RegisterShaderNoMip( BOTSELECT_ARROWS );
+ trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSL );
+ trap_R_RegisterShaderNoMip( BOTSELECT_ARROWSR );
+}
+
+
+static void UI_BotSelectMenu_Init( char *bot ) {
+ int i, j, k;
+ int x, y;
+
+ memset( &botSelectInfo, 0 ,sizeof(botSelectInfo) );
+ botSelectInfo.menu.wrapAround = qtrue;
+ botSelectInfo.menu.fullscreen = qtrue;
+
+ UI_BotSelectMenu_Cache();
+
+ botSelectInfo.banner.generic.type = MTYPE_BTEXT;
+ botSelectInfo.banner.generic.x = 320;
+ botSelectInfo.banner.generic.y = 16;
+ botSelectInfo.banner.string = "SELECT BOT";
+ botSelectInfo.banner.color = color_white;
+ botSelectInfo.banner.style = UI_CENTER;
+
+ y = 80;
+ for( i = 0, k = 0; i < PLAYERGRID_ROWS; i++) {
+ x = 180;
+ for( j = 0; j < PLAYERGRID_COLS; j++, k++ ) {
+ botSelectInfo.pics[k].generic.type = MTYPE_BITMAP;
+ botSelectInfo.pics[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_INACTIVE;
+ botSelectInfo.pics[k].generic.x = x;
+ botSelectInfo.pics[k].generic.y = y;
+ botSelectInfo.pics[k].generic.name = botSelectInfo.boticons[k];
+ botSelectInfo.pics[k].width = 64;
+ botSelectInfo.pics[k].height = 64;
+ botSelectInfo.pics[k].focuspic = BOTSELECT_SELECTED;
+ botSelectInfo.pics[k].focuscolor = colorRed;
+
+ botSelectInfo.picbuttons[k].generic.type = MTYPE_BITMAP;
+ botSelectInfo.picbuttons[k].generic.flags = QMF_LEFT_JUSTIFY|QMF_NODEFAULTINIT|QMF_PULSEIFFOCUS;
+ botSelectInfo.picbuttons[k].generic.callback = UI_BotSelectMenu_BotEvent;
+ botSelectInfo.picbuttons[k].generic.id = k;
+ botSelectInfo.picbuttons[k].generic.x = x - 16;
+ botSelectInfo.picbuttons[k].generic.y = y - 16;
+ botSelectInfo.picbuttons[k].generic.left = x;
+ botSelectInfo.picbuttons[k].generic.top = y;
+ botSelectInfo.picbuttons[k].generic.right = x + 64;
+ botSelectInfo.picbuttons[k].generic.bottom = y + 64;
+ botSelectInfo.picbuttons[k].width = 128;
+ botSelectInfo.picbuttons[k].height = 128;
+ botSelectInfo.picbuttons[k].focuspic = BOTSELECT_SELECT;
+ botSelectInfo.picbuttons[k].focuscolor = colorRed;
+
+ botSelectInfo.picnames[k].generic.type = MTYPE_TEXT;
+ botSelectInfo.picnames[k].generic.flags = QMF_SMALLFONT;
+ botSelectInfo.picnames[k].generic.x = x + 32;
+ botSelectInfo.picnames[k].generic.y = y + 64;
+ botSelectInfo.picnames[k].string = botSelectInfo.botnames[k];
+ botSelectInfo.picnames[k].color = color_orange;
+ botSelectInfo.picnames[k].style = UI_CENTER|UI_SMALLFONT;
+
+ x += (64 + 6);
+ }
+ y += (64 + SMALLCHAR_HEIGHT + 6);
+ }
+
+ botSelectInfo.arrows.generic.type = MTYPE_BITMAP;
+ botSelectInfo.arrows.generic.name = BOTSELECT_ARROWS;
+ botSelectInfo.arrows.generic.flags = QMF_INACTIVE;
+ botSelectInfo.arrows.generic.x = 260;
+ botSelectInfo.arrows.generic.y = 440;
+ botSelectInfo.arrows.width = 128;
+ botSelectInfo.arrows.height = 32;
+
+ botSelectInfo.left.generic.type = MTYPE_BITMAP;
+ botSelectInfo.left.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ botSelectInfo.left.generic.callback = UI_BotSelectMenu_LeftEvent;
+ botSelectInfo.left.generic.x = 260;
+ botSelectInfo.left.generic.y = 440;
+ botSelectInfo.left.width = 64;
+ botSelectInfo.left.height = 32;
+ botSelectInfo.left.focuspic = BOTSELECT_ARROWSL;
+
+ botSelectInfo.right.generic.type = MTYPE_BITMAP;
+ botSelectInfo.right.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ botSelectInfo.right.generic.callback = UI_BotSelectMenu_RightEvent;
+ botSelectInfo.right.generic.x = 321;
+ botSelectInfo.right.generic.y = 440;
+ botSelectInfo.right.width = 64;
+ botSelectInfo.right.height = 32;
+ botSelectInfo.right.focuspic = BOTSELECT_ARROWSR;
+
+ botSelectInfo.back.generic.type = MTYPE_BITMAP;
+ botSelectInfo.back.generic.name = BOTSELECT_BACK0;
+ botSelectInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ botSelectInfo.back.generic.callback = UI_BotSelectMenu_BackEvent;
+ botSelectInfo.back.generic.x = 0;
+ botSelectInfo.back.generic.y = 480-64;
+ botSelectInfo.back.width = 128;
+ botSelectInfo.back.height = 64;
+ botSelectInfo.back.focuspic = BOTSELECT_BACK1;
+
+ botSelectInfo.go.generic.type = MTYPE_BITMAP;
+ botSelectInfo.go.generic.name = BOTSELECT_ACCEPT0;
+ botSelectInfo.go.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ botSelectInfo.go.generic.callback = UI_BotSelectMenu_SelectEvent;
+ botSelectInfo.go.generic.x = 640;
+ botSelectInfo.go.generic.y = 480-64;
+ botSelectInfo.go.width = 128;
+ botSelectInfo.go.height = 64;
+ botSelectInfo.go.focuspic = BOTSELECT_ACCEPT1;
+
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.banner );
+ for( i = 0; i < MAX_MODELSPERPAGE; i++ ) {
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.pics[i] );
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.picbuttons[i] );
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.picnames[i] );
+ }
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.arrows );
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.left );
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.right );
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.back );
+ Menu_AddItem( &botSelectInfo.menu, &botSelectInfo.go );
+
+ UI_BotSelectMenu_BuildList();
+ UI_BotSelectMenu_Default( bot );
+ botSelectInfo.modelpage = botSelectInfo.selectedmodel / MAX_MODELSPERPAGE;
+ UI_BotSelectMenu_UpdateGrid();
+}
+
+
+/*
+=================
+UI_BotSelectMenu
+=================
+*/
+void UI_BotSelectMenu( char *bot ) {
+ UI_BotSelectMenu_Init( bot );
+ UI_PushMenu( &botSelectInfo.menu );
+}
diff --git a/code/q3_ui/ui_team.c b/code/q3_ui/ui_team.c
new file mode 100644
index 0000000..e15dde6
--- /dev/null
+++ b/code/q3_ui/ui_team.c
@@ -0,0 +1,200 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+//
+// ui_team.c
+//
+
+#include "ui_local.h"
+
+
+#define TEAMMAIN_FRAME "menu/art/cut_frame"
+
+#define ID_JOINRED 100
+#define ID_JOINBLUE 101
+#define ID_JOINGAME 102
+#define ID_SPECTATE 103
+
+
+typedef struct
+{
+ menuframework_s menu;
+ menubitmap_s frame;
+ menutext_s joinred;
+ menutext_s joinblue;
+ menutext_s joingame;
+ menutext_s spectate;
+} teammain_t;
+
+static teammain_t s_teammain;
+
+/*
+===============
+TeamMain_MenuEvent
+===============
+*/
+static void TeamMain_MenuEvent( void* ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_JOINRED:
+ trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team red\n" );
+ UI_ForceMenuOff();
+ break;
+
+ case ID_JOINBLUE:
+ trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team blue\n" );
+ UI_ForceMenuOff();
+ break;
+
+ case ID_JOINGAME:
+ trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team free\n" );
+ UI_ForceMenuOff();
+ break;
+
+ case ID_SPECTATE:
+ trap_Cmd_ExecuteText( EXEC_APPEND, "cmd team spectator\n" );
+ UI_ForceMenuOff();
+ break;
+ }
+}
+
+
+/*
+===============
+TeamMain_MenuInit
+===============
+*/
+void TeamMain_MenuInit( void ) {
+ int y;
+ int gametype;
+ char info[MAX_INFO_STRING];
+
+ memset( &s_teammain, 0, sizeof(s_teammain) );
+
+ TeamMain_Cache();
+
+ s_teammain.menu.wrapAround = qtrue;
+ s_teammain.menu.fullscreen = qfalse;
+
+ s_teammain.frame.generic.type = MTYPE_BITMAP;
+ s_teammain.frame.generic.flags = QMF_INACTIVE;
+ s_teammain.frame.generic.name = TEAMMAIN_FRAME;
+ s_teammain.frame.generic.x = 142;
+ s_teammain.frame.generic.y = 118;
+ s_teammain.frame.width = 359;
+ s_teammain.frame.height = 256;
+
+ y = 194;
+
+ s_teammain.joinred.generic.type = MTYPE_PTEXT;
+ s_teammain.joinred.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_teammain.joinred.generic.id = ID_JOINRED;
+ s_teammain.joinred.generic.callback = TeamMain_MenuEvent;
+ s_teammain.joinred.generic.x = 320;
+ s_teammain.joinred.generic.y = y;
+ s_teammain.joinred.string = "JOIN RED";
+ s_teammain.joinred.style = UI_CENTER|UI_SMALLFONT;
+ s_teammain.joinred.color = colorRed;
+ y += 20;
+
+ s_teammain.joinblue.generic.type = MTYPE_PTEXT;
+ s_teammain.joinblue.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_teammain.joinblue.generic.id = ID_JOINBLUE;
+ s_teammain.joinblue.generic.callback = TeamMain_MenuEvent;
+ s_teammain.joinblue.generic.x = 320;
+ s_teammain.joinblue.generic.y = y;
+ s_teammain.joinblue.string = "JOIN BLUE";
+ s_teammain.joinblue.style = UI_CENTER|UI_SMALLFONT;
+ s_teammain.joinblue.color = colorRed;
+ y += 20;
+
+ s_teammain.joingame.generic.type = MTYPE_PTEXT;
+ s_teammain.joingame.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_teammain.joingame.generic.id = ID_JOINGAME;
+ s_teammain.joingame.generic.callback = TeamMain_MenuEvent;
+ s_teammain.joingame.generic.x = 320;
+ s_teammain.joingame.generic.y = y;
+ s_teammain.joingame.string = "JOIN GAME";
+ s_teammain.joingame.style = UI_CENTER|UI_SMALLFONT;
+ s_teammain.joingame.color = colorRed;
+ y += 20;
+
+ s_teammain.spectate.generic.type = MTYPE_PTEXT;
+ s_teammain.spectate.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_teammain.spectate.generic.id = ID_SPECTATE;
+ s_teammain.spectate.generic.callback = TeamMain_MenuEvent;
+ s_teammain.spectate.generic.x = 320;
+ s_teammain.spectate.generic.y = y;
+ s_teammain.spectate.string = "SPECTATE";
+ s_teammain.spectate.style = UI_CENTER|UI_SMALLFONT;
+ s_teammain.spectate.color = colorRed;
+ y += 20;
+
+ trap_GetConfigString(CS_SERVERINFO, info, MAX_INFO_STRING);
+ gametype = atoi( Info_ValueForKey( info,"g_gametype" ) );
+
+ // set initial states
+ switch( gametype ) {
+ case GT_SINGLE_PLAYER:
+ case GT_FFA:
+ case GT_TOURNAMENT:
+ s_teammain.joinred.generic.flags |= QMF_GRAYED;
+ s_teammain.joinblue.generic.flags |= QMF_GRAYED;
+ break;
+
+ default:
+ case GT_TEAM:
+ case GT_CTF:
+ s_teammain.joingame.generic.flags |= QMF_GRAYED;
+ break;
+ }
+
+ Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.frame );
+ Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.joinred );
+ Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.joinblue );
+ Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.joingame );
+ Menu_AddItem( &s_teammain.menu, (void*) &s_teammain.spectate );
+}
+
+
+/*
+===============
+TeamMain_Cache
+===============
+*/
+void TeamMain_Cache( void ) {
+ trap_R_RegisterShaderNoMip( TEAMMAIN_FRAME );
+}
+
+
+/*
+===============
+UI_TeamMainMenu
+===============
+*/
+void UI_TeamMainMenu( void ) {
+ TeamMain_MenuInit();
+ UI_PushMenu ( &s_teammain.menu );
+}
diff --git a/code/q3_ui/ui_teamorders.c b/code/q3_ui/ui_teamorders.c
new file mode 100644
index 0000000..d116e01
--- /dev/null
+++ b/code/q3_ui/ui_teamorders.c
@@ -0,0 +1,447 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+/*
+=======================================================================
+
+TEAM ORDERS MENU
+
+=======================================================================
+*/
+
+
+#include "ui_local.h"
+
+
+#define ART_FRAME "menu/art/addbotframe"
+#define ART_BACK0 "menu/art/back_0"
+#define ART_BACK1 "menu/art/back_1"
+
+#define ID_LIST_BOTS 10
+#define ID_LIST_CTF_ORDERS 11
+#define ID_LIST_TEAM_ORDERS 12
+
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s frame;
+
+ menulist_s list;
+
+ menubitmap_s back;
+
+ int gametype;
+ int numBots;
+ int selectedBot;
+ char *bots[9];
+ char botNames[9][16];
+} teamOrdersMenuInfo_t;
+
+static teamOrdersMenuInfo_t teamOrdersMenuInfo;
+
+#define NUM_CTF_ORDERS 7
+static const char *ctfOrders[] = {
+ "I Am the Leader",
+ "Defend the Base",
+ "Follow Me",
+ "Get Enemy Flag",
+ "Camp Here",
+ "Report",
+ "I Relinquish Command",
+ NULL
+};
+static const char *ctfMessages[] = {
+ "i am the leader",
+ "%s defend the base",
+ "%s follow me",
+ "%s get enemy flag",
+ "%s camp here",
+ "%s report",
+ "i stop being the leader",
+ NULL
+};
+
+#define NUM_TEAM_ORDERS 6
+static const char *teamOrders[] = {
+ "I Am the Leader",
+ "Follow Me",
+ "Roam",
+ "Camp Here",
+ "Report",
+ "I Relinquish Command",
+ NULL
+};
+static const char *teamMessages[] = {
+ "i am the leader",
+ "%s follow me",
+ "%s roam",
+ "%s camp here",
+ "%s report",
+ "i stop being the leader",
+ NULL
+};
+
+
+/*
+===============
+UI_TeamOrdersMenu_BackEvent
+===============
+*/
+static void UI_TeamOrdersMenu_BackEvent( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ UI_PopMenu();
+}
+
+
+/*
+===============
+UI_TeamOrdersMenu_SetList
+===============
+*/
+static void UI_TeamOrdersMenu_SetList( int id ) {
+ switch( id ) {
+ default:
+ case ID_LIST_BOTS:
+ teamOrdersMenuInfo.list.generic.id = id;
+ teamOrdersMenuInfo.list.numitems = teamOrdersMenuInfo.numBots;
+ teamOrdersMenuInfo.list.itemnames = (const char **)teamOrdersMenuInfo.bots;
+ break;
+
+ case ID_LIST_CTF_ORDERS:
+ teamOrdersMenuInfo.list.generic.id = id;
+ teamOrdersMenuInfo.list.numitems = NUM_CTF_ORDERS;
+ teamOrdersMenuInfo.list.itemnames = ctfOrders;
+ break;
+
+ case ID_LIST_TEAM_ORDERS:
+ teamOrdersMenuInfo.list.generic.id = id;
+ teamOrdersMenuInfo.list.numitems = NUM_TEAM_ORDERS;
+ teamOrdersMenuInfo.list.itemnames = teamOrders;
+ break;
+ }
+
+ teamOrdersMenuInfo.list.generic.bottom = teamOrdersMenuInfo.list.generic.top + teamOrdersMenuInfo.list.numitems * PROP_HEIGHT;
+}
+
+
+/*
+=================
+UI_TeamOrdersMenu_Key
+=================
+*/
+sfxHandle_t UI_TeamOrdersMenu_Key( int key ) {
+ menulist_s *l;
+ int x;
+ int y;
+ int index;
+
+ l = (menulist_s *)Menu_ItemAtCursor( &teamOrdersMenuInfo.menu );
+ if( l != &teamOrdersMenuInfo.list ) {
+ return Menu_DefaultKey( &teamOrdersMenuInfo.menu, key );
+ }
+
+ switch( key ) {
+ case K_MOUSE1:
+ x = l->generic.left;
+ y = l->generic.top;
+ if( UI_CursorInRect( x, y, l->generic.right - x, l->generic.bottom - y ) ) {
+ index = (uis.cursory - y) / PROP_HEIGHT;
+ l->oldvalue = l->curvalue;
+ l->curvalue = index;
+
+ if( l->generic.callback ) {
+ l->generic.callback( l, QM_ACTIVATED );
+ return menu_move_sound;
+ }
+ }
+ return menu_null_sound;
+
+ case K_KP_UPARROW:
+ case K_UPARROW:
+ l->oldvalue = l->curvalue;
+
+ if( l->curvalue == 0 ) {
+ l->curvalue = l->numitems - 1;
+ }
+ else {
+ l->curvalue--;
+ }
+ return menu_move_sound;
+
+ case K_KP_DOWNARROW:
+ case K_DOWNARROW:
+ l->oldvalue = l->curvalue;
+
+ if( l->curvalue == l->numitems - 1 ) {
+ l->curvalue = 0;;
+ }
+ else {
+ l->curvalue++;
+ }
+ return menu_move_sound;
+ }
+
+ return Menu_DefaultKey( &teamOrdersMenuInfo.menu, key );
+}
+
+
+/*
+=================
+UI_TeamOrdersMenu_ListDraw
+=================
+*/
+static void UI_TeamOrdersMenu_ListDraw( void *self ) {
+ menulist_s *l;
+ int x;
+ int y;
+ int i;
+ float *color;
+ qboolean hasfocus;
+ int style;
+
+ l = (menulist_s *)self;
+
+ hasfocus = (l->generic.parent->cursor == l->generic.menuPosition);
+
+ x = 320;//l->generic.x;
+ y = l->generic.y;
+ for( i = 0; i < l->numitems; i++ ) {
+ style = UI_LEFT|UI_SMALLFONT|UI_CENTER;
+ if( i == l->curvalue ) {
+ color = color_yellow;
+ if( hasfocus ) {
+ style |= UI_PULSE;
+ }
+ }
+ else {
+ color = color_orange;
+ }
+
+ UI_DrawProportionalString( x, y, l->itemnames[i], style, color );
+ y += PROP_HEIGHT;
+ }
+}
+
+
+/*
+===============
+UI_TeamOrdersMenu_ListEvent
+===============
+*/
+static void UI_TeamOrdersMenu_ListEvent( void *ptr, int event ) {
+ int id;
+ int selection;
+ char message[256];
+
+ if (event != QM_ACTIVATED)
+ return;
+
+ id = ((menulist_s *)ptr)->generic.id;
+ selection = ((menulist_s *)ptr)->curvalue;
+
+ if( id == ID_LIST_BOTS ) {
+ teamOrdersMenuInfo.selectedBot = selection;
+ if( teamOrdersMenuInfo.gametype == GT_CTF ) {
+ UI_TeamOrdersMenu_SetList( ID_LIST_CTF_ORDERS );
+ }
+ else {
+ UI_TeamOrdersMenu_SetList( ID_LIST_TEAM_ORDERS );
+ }
+ return;
+ }
+
+ if( id == ID_LIST_CTF_ORDERS ) {
+ Com_sprintf( message, sizeof(message), ctfMessages[selection], teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.selectedBot] );
+ }
+ else {
+ Com_sprintf( message, sizeof(message), teamMessages[selection], teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.selectedBot] );
+ }
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, va( "say_team \"%s\"\n", message ) );
+ UI_PopMenu();
+}
+
+
+/*
+===============
+UI_TeamOrdersMenu_BuildBotList
+===============
+*/
+static void UI_TeamOrdersMenu_BuildBotList( void ) {
+ uiClientState_t cs;
+ int numPlayers;
+ int isBot;
+ int n;
+ char playerTeam = '3';
+ char botTeam;
+ char info[MAX_INFO_STRING];
+
+ for( n = 0; n < 9; n++ ) {
+ teamOrdersMenuInfo.bots[n] = teamOrdersMenuInfo.botNames[n];
+ }
+
+ trap_GetClientState( &cs );
+
+ Q_strncpyz( teamOrdersMenuInfo.botNames[0], "Everyone", 16 );
+ teamOrdersMenuInfo.numBots = 1;
+
+ trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
+ numPlayers = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
+ teamOrdersMenuInfo.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) );
+
+ for( n = 0; n < numPlayers && teamOrdersMenuInfo.numBots < 9; n++ ) {
+ trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING );
+
+ if( n == cs.clientNum ) {
+ playerTeam = *Info_ValueForKey( info, "t" );
+ continue;
+ }
+
+ isBot = atoi( Info_ValueForKey( info, "skill" ) );
+ if( !isBot ) {
+ continue;
+ }
+
+ botTeam = *Info_ValueForKey( info, "t" );
+ if( botTeam != playerTeam ) {
+ continue;
+ }
+
+ Q_strncpyz( teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.numBots], Info_ValueForKey( info, "n" ), 16 );
+ Q_CleanStr( teamOrdersMenuInfo.botNames[teamOrdersMenuInfo.numBots] );
+ teamOrdersMenuInfo.numBots++;
+ }
+}
+
+
+/*
+===============
+UI_TeamOrdersMenu_Init
+===============
+*/
+static void UI_TeamOrdersMenu_Init( void ) {
+ UI_TeamOrdersMenu_Cache();
+
+ memset( &teamOrdersMenuInfo, 0, sizeof(teamOrdersMenuInfo) );
+ teamOrdersMenuInfo.menu.fullscreen = qfalse;
+ teamOrdersMenuInfo.menu.key = UI_TeamOrdersMenu_Key;
+
+ UI_TeamOrdersMenu_BuildBotList();
+
+ teamOrdersMenuInfo.banner.generic.type = MTYPE_BTEXT;
+ teamOrdersMenuInfo.banner.generic.x = 320;
+ teamOrdersMenuInfo.banner.generic.y = 16;
+ teamOrdersMenuInfo.banner.string = "TEAM ORDERS";
+ teamOrdersMenuInfo.banner.color = color_white;
+ teamOrdersMenuInfo.banner.style = UI_CENTER;
+
+ teamOrdersMenuInfo.frame.generic.type = MTYPE_BITMAP;
+ teamOrdersMenuInfo.frame.generic.flags = QMF_INACTIVE;
+ teamOrdersMenuInfo.frame.generic.name = ART_FRAME;
+ teamOrdersMenuInfo.frame.generic.x = 320-233;
+ teamOrdersMenuInfo.frame.generic.y = 240-166;
+ teamOrdersMenuInfo.frame.width = 466;
+ teamOrdersMenuInfo.frame.height = 332;
+
+ teamOrdersMenuInfo.list.generic.type = MTYPE_SCROLLLIST;
+ teamOrdersMenuInfo.list.generic.flags = QMF_PULSEIFFOCUS;
+ teamOrdersMenuInfo.list.generic.ownerdraw = UI_TeamOrdersMenu_ListDraw;
+ teamOrdersMenuInfo.list.generic.callback = UI_TeamOrdersMenu_ListEvent;
+ teamOrdersMenuInfo.list.generic.x = 320-64;
+ teamOrdersMenuInfo.list.generic.y = 120;
+
+ teamOrdersMenuInfo.back.generic.type = MTYPE_BITMAP;
+ teamOrdersMenuInfo.back.generic.name = ART_BACK0;
+ teamOrdersMenuInfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ teamOrdersMenuInfo.back.generic.callback = UI_TeamOrdersMenu_BackEvent;
+ teamOrdersMenuInfo.back.generic.x = 0;
+ teamOrdersMenuInfo.back.generic.y = 480-64;
+ teamOrdersMenuInfo.back.width = 128;
+ teamOrdersMenuInfo.back.height = 64;
+ teamOrdersMenuInfo.back.focuspic = ART_BACK1;
+
+ Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.banner );
+ Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.frame );
+ Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.list );
+ Menu_AddItem( &teamOrdersMenuInfo.menu, &teamOrdersMenuInfo.back );
+
+ teamOrdersMenuInfo.list.generic.left = 220;
+ teamOrdersMenuInfo.list.generic.top = teamOrdersMenuInfo.list.generic.y;
+ teamOrdersMenuInfo.list.generic.right = 420;
+ UI_TeamOrdersMenu_SetList( ID_LIST_BOTS );
+}
+
+
+/*
+=================
+UI_TeamOrdersMenu_Cache
+=================
+*/
+void UI_TeamOrdersMenu_Cache( void ) {
+ trap_R_RegisterShaderNoMip( ART_FRAME );
+ trap_R_RegisterShaderNoMip( ART_BACK0 );
+ trap_R_RegisterShaderNoMip( ART_BACK1 );
+}
+
+
+/*
+===============
+UI_TeamOrdersMenu
+===============
+*/
+void UI_TeamOrdersMenu( void ) {
+ UI_TeamOrdersMenu_Init();
+ UI_PushMenu( &teamOrdersMenuInfo.menu );
+}
+
+
+/*
+===============
+UI_TeamOrdersMenu_f
+===============
+*/
+void UI_TeamOrdersMenu_f( void ) {
+ uiClientState_t cs;
+ char info[MAX_INFO_STRING];
+ int team;
+
+ // make sure it's a team game
+ trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
+ teamOrdersMenuInfo.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) );
+ if( teamOrdersMenuInfo.gametype < GT_TEAM ) {
+ return;
+ }
+
+ // not available to spectators
+ trap_GetClientState( &cs );
+ trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING );
+ team = atoi( Info_ValueForKey( info, "t" ) );
+ if( team == TEAM_SPECTATOR ) {
+ return;
+ }
+
+ UI_TeamOrdersMenu();
+}
diff --git a/code/q3_ui/ui_video.c b/code/q3_ui/ui_video.c
new file mode 100644
index 0000000..160037d
--- /dev/null
+++ b/code/q3_ui/ui_video.c
@@ -0,0 +1,1289 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+//
+#include "ui_local.h"
+
+void GraphicsOptions_MenuInit( void );
+
+/*
+=======================================================================
+
+DRIVER INFORMATION MENU
+
+=======================================================================
+*/
+
+
+#define DRIVERINFO_FRAMEL "menu/art/frame2_l"
+#define DRIVERINFO_FRAMER "menu/art/frame1_r"
+#define DRIVERINFO_BACK0 "menu/art/back_0"
+#define DRIVERINFO_BACK1 "menu/art/back_1"
+
+static char* driverinfo_artlist[] =
+{
+ DRIVERINFO_FRAMEL,
+ DRIVERINFO_FRAMER,
+ DRIVERINFO_BACK0,
+ DRIVERINFO_BACK1,
+ NULL,
+};
+
+#define ID_DRIVERINFOBACK 100
+
+typedef struct
+{
+ menuframework_s menu;
+ menutext_s banner;
+ menubitmap_s back;
+ menubitmap_s framel;
+ menubitmap_s framer;
+ char stringbuff[1024];
+ char* strings[64];
+ int numstrings;
+} driverinfo_t;
+
+static driverinfo_t s_driverinfo;
+
+/*
+=================
+DriverInfo_Event
+=================
+*/
+static void DriverInfo_Event( void* ptr, int event )
+{
+ if (event != QM_ACTIVATED)
+ return;
+
+ switch (((menucommon_s*)ptr)->id)
+ {
+ case ID_DRIVERINFOBACK:
+ UI_PopMenu();
+ break;
+ }
+}
+
+/*
+=================
+DriverInfo_MenuDraw
+=================
+*/
+static void DriverInfo_MenuDraw( void )
+{
+ int i;
+ int y;
+
+ Menu_Draw( &s_driverinfo.menu );
+
+ UI_DrawString( 320, 80, "VENDOR", UI_CENTER|UI_SMALLFONT, color_red );
+ UI_DrawString( 320, 152, "PIXELFORMAT", UI_CENTER|UI_SMALLFONT, color_red );
+ UI_DrawString( 320, 192, "EXTENSIONS", UI_CENTER|UI_SMALLFONT, color_red );
+
+ UI_DrawString( 320, 80+16, uis.glconfig.vendor_string, UI_CENTER|UI_SMALLFONT, text_color_normal );
+ UI_DrawString( 320, 96+16, uis.glconfig.version_string, UI_CENTER|UI_SMALLFONT, text_color_normal );
+ UI_DrawString( 320, 112+16, uis.glconfig.renderer_string, UI_CENTER|UI_SMALLFONT, text_color_normal );
+ UI_DrawString( 320, 152+16, va ("color(%d-bits) Z(%d-bits) stencil(%d-bits)", uis.glconfig.colorBits, uis.glconfig.depthBits, uis.glconfig.stencilBits), UI_CENTER|UI_SMALLFONT, text_color_normal );
+
+ // double column
+ y = 192+16;
+ for (i=0; i<s_driverinfo.numstrings/2; i++) {
+ UI_DrawString( 320-4, y, s_driverinfo.strings[i*2], UI_RIGHT|UI_SMALLFONT, text_color_normal );
+ UI_DrawString( 320+4, y, s_driverinfo.strings[i*2+1], UI_LEFT|UI_SMALLFONT, text_color_normal );
+ y += SMALLCHAR_HEIGHT;
+ }
+
+ if (s_driverinfo.numstrings & 1)
+ UI_DrawString( 320, y, s_driverinfo.strings[s_driverinfo.numstrings-1], UI_CENTER|UI_SMALLFONT, text_color_normal );
+}
+
+/*
+=================
+DriverInfo_Cache
+=================
+*/
+void DriverInfo_Cache( void )
+{
+ int i;
+
+ // touch all our pics
+ for (i=0; ;i++)
+ {
+ if (!driverinfo_artlist[i])
+ break;
+ trap_R_RegisterShaderNoMip(driverinfo_artlist[i]);
+ }
+}
+
+/*
+=================
+UI_DriverInfo_Menu
+=================
+*/
+static void UI_DriverInfo_Menu( void )
+{
+ char* eptr;
+ int i;
+ int len;
+
+ // zero set all our globals
+ memset( &s_driverinfo, 0 ,sizeof(driverinfo_t) );
+
+ DriverInfo_Cache();
+
+ s_driverinfo.menu.fullscreen = qtrue;
+ s_driverinfo.menu.draw = DriverInfo_MenuDraw;
+
+ s_driverinfo.banner.generic.type = MTYPE_BTEXT;
+ s_driverinfo.banner.generic.x = 320;
+ s_driverinfo.banner.generic.y = 16;
+ s_driverinfo.banner.string = "DRIVER INFO";
+ s_driverinfo.banner.color = color_white;
+ s_driverinfo.banner.style = UI_CENTER;
+
+ s_driverinfo.framel.generic.type = MTYPE_BITMAP;
+ s_driverinfo.framel.generic.name = DRIVERINFO_FRAMEL;
+ s_driverinfo.framel.generic.flags = QMF_INACTIVE;
+ s_driverinfo.framel.generic.x = 0;
+ s_driverinfo.framel.generic.y = 78;
+ s_driverinfo.framel.width = 256;
+ s_driverinfo.framel.height = 329;
+
+ s_driverinfo.framer.generic.type = MTYPE_BITMAP;
+ s_driverinfo.framer.generic.name = DRIVERINFO_FRAMER;
+ s_driverinfo.framer.generic.flags = QMF_INACTIVE;
+ s_driverinfo.framer.generic.x = 376;
+ s_driverinfo.framer.generic.y = 76;
+ s_driverinfo.framer.width = 256;
+ s_driverinfo.framer.height = 334;
+
+ s_driverinfo.back.generic.type = MTYPE_BITMAP;
+ s_driverinfo.back.generic.name = DRIVERINFO_BACK0;
+ s_driverinfo.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_driverinfo.back.generic.callback = DriverInfo_Event;
+ s_driverinfo.back.generic.id = ID_DRIVERINFOBACK;
+ s_driverinfo.back.generic.x = 0;
+ s_driverinfo.back.generic.y = 480-64;
+ s_driverinfo.back.width = 128;
+ s_driverinfo.back.height = 64;
+ s_driverinfo.back.focuspic = DRIVERINFO_BACK1;
+
+ // TTimo: overflow with particularly long GL extensions (such as the gf3)
+ // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399
+ // NOTE: could have pushed the size of stringbuff, but the list is already out of the screen
+ // (no matter what your resolution)
+ Q_strncpyz(s_driverinfo.stringbuff, uis.glconfig.extensions_string, 1024);
+
+ // build null terminated extension strings
+ eptr = s_driverinfo.stringbuff;
+ while ( s_driverinfo.numstrings<40 && *eptr )
+ {
+ while ( *eptr && *eptr == ' ' )
+ *eptr++ = '\0';
+
+ // track start of valid string
+ if (*eptr && *eptr != ' ')
+ s_driverinfo.strings[s_driverinfo.numstrings++] = eptr;
+
+ while ( *eptr && *eptr != ' ' )
+ eptr++;
+ }
+
+ // safety length strings for display
+ for (i=0; i<s_driverinfo.numstrings; i++) {
+ len = strlen(s_driverinfo.strings[i]);
+ if (len > 32) {
+ s_driverinfo.strings[i][len-1] = '>';
+ s_driverinfo.strings[i][len] = '\0';
+ }
+ }
+
+ Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.banner );
+ Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.framel );
+ Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.framer );
+ Menu_AddItem( &s_driverinfo.menu, &s_driverinfo.back );
+
+ UI_PushMenu( &s_driverinfo.menu );
+}
+
+/*
+=======================================================================
+
+GRAPHICS OPTIONS MENU
+
+=======================================================================
+*/
+
+#define GRAPHICSOPTIONS_FRAMEL "menu/art/frame2_l"
+#define GRAPHICSOPTIONS_FRAMER "menu/art/frame1_r"
+#define GRAPHICSOPTIONS_BACK0 "menu/art/back_0"
+#define GRAPHICSOPTIONS_BACK1 "menu/art/back_1"
+#define GRAPHICSOPTIONS_ACCEPT0 "menu/art/accept_0"
+#define GRAPHICSOPTIONS_ACCEPT1 "menu/art/accept_1"
+
+#define ID_BACK2 101
+#define ID_FULLSCREEN 102
+#define ID_LIST 103
+#define ID_MODE 104
+#define ID_DRIVERINFO 105
+#define ID_GRAPHICS 106
+#define ID_DISPLAY 107
+#define ID_SOUND 108
+#define ID_NETWORK 109
+#define ID_RATIO 110
+
+typedef struct {
+ menuframework_s menu;
+
+ menutext_s banner;
+ menubitmap_s framel;
+ menubitmap_s framer;
+
+ menutext_s graphics;
+ menutext_s display;
+ menutext_s sound;
+ menutext_s network;
+
+ menulist_s list;
+ menulist_s ratio;
+ menulist_s mode;
+ menulist_s driver;
+ menuslider_s tq;
+ menulist_s fs;
+ menulist_s lighting;
+ menulist_s allow_extensions;
+ menulist_s texturebits;
+ menulist_s colordepth;
+ menulist_s geometry;
+ menulist_s filter;
+ menutext_s driverinfo;
+
+ menubitmap_s apply;
+ menubitmap_s back;
+} graphicsoptions_t;
+
+typedef struct
+{
+ int mode;
+ qboolean fullscreen;
+ int tq;
+ int lighting;
+ int colordepth;
+ int texturebits;
+ int geometry;
+ int filter;
+ int driver;
+ qboolean extensions;
+} InitialVideoOptions_s;
+
+static InitialVideoOptions_s s_ivo;
+static graphicsoptions_t s_graphicsoptions;
+
+static InitialVideoOptions_s s_ivo_templates[] =
+{
+ {
+ 6, qtrue, 3, 0, 2, 2, 2, 1, 0, qtrue
+ },
+ {
+ 4, qtrue, 2, 0, 2, 2, 1, 1, 0, qtrue // JDC: this was tq 3
+ },
+ {
+ 3, qtrue, 2, 0, 0, 0, 1, 0, 0, qtrue
+ },
+ {
+ 2, qtrue, 1, 0, 1, 0, 0, 0, 0, qtrue
+ },
+ {
+ 2, qtrue, 1, 1, 1, 0, 0, 0, 0, qtrue
+ },
+ {
+ 3, qtrue, 1, 0, 0, 0, 1, 0, 0, qtrue
+ }
+};
+
+#define NUM_IVO_TEMPLATES ( sizeof( s_ivo_templates ) / sizeof( s_ivo_templates[0] ) )
+
+static const char *builtinResolutions[ ] =
+{
+ "320x240",
+ "400x300",
+ "512x384",
+ "640x480",
+ "800x600",
+ "960x720",
+ "1024x768",
+ "1152x864",
+ "1280x1024",
+ "1600x1200",
+ "2048x1536",
+ "856x480",
+ NULL
+};
+
+static const char *knownRatios[ ][2] =
+{
+ { "1.25:1", "5:4" },
+ { "1.33:1", "4:3" },
+ { "1.50:1", "3:2" },
+ { "1.56:1", "14:9" },
+ { "1.60:1", "16:10" },
+ { "1.67:1", "5:3" },
+ { "1.78:1", "16:9" },
+ { NULL , NULL }
+};
+
+#define MAX_RESOLUTIONS 32
+
+static const char* ratios[ MAX_RESOLUTIONS ];
+static char ratioBuf[ MAX_RESOLUTIONS ][ 8 ];
+static int ratioToRes[ MAX_RESOLUTIONS ];
+static int resToRatio[ MAX_RESOLUTIONS ];
+
+static char resbuf[ MAX_STRING_CHARS ];
+static const char* detectedResolutions[ MAX_RESOLUTIONS ];
+
+static const char** resolutions = builtinResolutions;
+static qboolean resolutionsDetected = qfalse;
+
+/*
+=================
+GraphicsOptions_FindBuiltinResolution
+=================
+*/
+static int GraphicsOptions_FindBuiltinResolution( int mode )
+{
+ int i;
+
+ if( !resolutionsDetected )
+ return mode;
+
+ if( mode < 0 )
+ return -1;
+
+ for( i = 0; builtinResolutions[ i ]; i++ )
+ {
+ if( !Q_stricmp( builtinResolutions[ i ], detectedResolutions[ mode ] ) )
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+=================
+GraphicsOptions_FindDetectedResolution
+=================
+*/
+static int GraphicsOptions_FindDetectedResolution( int mode )
+{
+ int i;
+
+ if( !resolutionsDetected )
+ return mode;
+
+ if( mode < 0 )
+ return -1;
+
+ for( i = 0; detectedResolutions[ i ]; i++ )
+ {
+ if( !Q_stricmp( builtinResolutions[ mode ], detectedResolutions[ i ] ) )
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+=================
+GraphicsOptions_GetAspectRatios
+=================
+*/
+static void GraphicsOptions_GetAspectRatios( void )
+{
+ int i, r;
+
+ // build ratio list from resolutions
+ for( r = 0; resolutions[r]; r++ )
+ {
+ int w, h;
+ char *x;
+ char str[ sizeof(ratioBuf[0]) ];
+
+ // calculate resolution's aspect ratio
+ x = strchr( resolutions[r], 'x' ) + 1;
+ Q_strncpyz( str, resolutions[r], x-resolutions[r] );
+ w = atoi( str );
+ h = atoi( x );
+ Com_sprintf( str, sizeof(str), "%.2f:1", (float)w / (float)h );
+
+ // add ratio to list if it is new
+ // establish res/ratio relationship
+ for( i = 0; ratioBuf[i][0]; i++ )
+ {
+ if( !Q_stricmp( str, ratioBuf[i] ) )
+ break;
+ }
+ if( !ratioBuf[i][0] )
+ {
+ Q_strncpyz( ratioBuf[i], str, sizeof(ratioBuf[i]) );
+ ratioToRes[i] = r;
+ }
+ resToRatio[r] = i;
+ }
+
+ // prepare itemlist pointer array
+ // rename common ratios ("1.33:1" -> "4:3")
+ for( r = 0; ratioBuf[r][0]; r++ )
+ {
+ for( i = 0; knownRatios[i][0]; i++ )
+ {
+ if( !Q_stricmp( ratioBuf[r], knownRatios[i][0] ) )
+ {
+ Q_strncpyz( ratioBuf[r], knownRatios[i][1], sizeof(ratioBuf[r]) );
+ break;
+ }
+ }
+ ratios[r] = ratioBuf[r];
+ }
+ ratios[r] = NULL;
+}
+
+/*
+=================
+GraphicsOptions_GetInitialVideo
+=================
+*/
+static void GraphicsOptions_GetInitialVideo( void )
+{
+ s_ivo.colordepth = s_graphicsoptions.colordepth.curvalue;
+ s_ivo.driver = s_graphicsoptions.driver.curvalue;
+ s_ivo.mode = s_graphicsoptions.mode.curvalue;
+ s_ivo.fullscreen = s_graphicsoptions.fs.curvalue;
+ s_ivo.extensions = s_graphicsoptions.allow_extensions.curvalue;
+ s_ivo.tq = s_graphicsoptions.tq.curvalue;
+ s_ivo.lighting = s_graphicsoptions.lighting.curvalue;
+ s_ivo.geometry = s_graphicsoptions.geometry.curvalue;
+ s_ivo.filter = s_graphicsoptions.filter.curvalue;
+ s_ivo.texturebits = s_graphicsoptions.texturebits.curvalue;
+}
+
+/*
+=================
+GraphicsOptions_GetResolutions
+=================
+*/
+static void GraphicsOptions_GetResolutions( void )
+{
+ Q_strncpyz(resbuf, UI_Cvar_VariableString("r_availableModes"), sizeof(resbuf));
+ if(*resbuf)
+ {
+ char* s = resbuf;
+ unsigned int i = 0;
+ while( s && i < sizeof(detectedResolutions)/sizeof(detectedResolutions[0])-1)
+ {
+ detectedResolutions[i++] = s;
+ s = strchr(s, ' ');
+ if( s )
+ *s++ = '\0';
+ }
+ detectedResolutions[ i ] = NULL;
+
+ if( i > 0 )
+ {
+ resolutions = detectedResolutions;
+ resolutionsDetected = qtrue;
+ }
+ }
+}
+
+/*
+=================
+GraphicsOptions_CheckConfig
+=================
+*/
+static void GraphicsOptions_CheckConfig( void )
+{
+ int i;
+
+ for ( i = 0; i < NUM_IVO_TEMPLATES-1; i++ )
+ {
+ if ( s_ivo_templates[i].colordepth != s_graphicsoptions.colordepth.curvalue )
+ continue;
+ if ( s_ivo_templates[i].driver != s_graphicsoptions.driver.curvalue )
+ continue;
+ if ( GraphicsOptions_FindDetectedResolution(s_ivo_templates[i].mode) != s_graphicsoptions.mode.curvalue )
+ continue;
+ if ( s_ivo_templates[i].fullscreen != s_graphicsoptions.fs.curvalue )
+ continue;
+ if ( s_ivo_templates[i].tq != s_graphicsoptions.tq.curvalue )
+ continue;
+ if ( s_ivo_templates[i].lighting != s_graphicsoptions.lighting.curvalue )
+ continue;
+ if ( s_ivo_templates[i].geometry != s_graphicsoptions.geometry.curvalue )
+ continue;
+ if ( s_ivo_templates[i].filter != s_graphicsoptions.filter.curvalue )
+ continue;
+// if ( s_ivo_templates[i].texturebits != s_graphicsoptions.texturebits.curvalue )
+// continue;
+ s_graphicsoptions.list.curvalue = i;
+ return;
+ }
+
+ // return 'Custom' ivo template
+ s_graphicsoptions.list.curvalue = NUM_IVO_TEMPLATES - 1;
+}
+
+/*
+=================
+GraphicsOptions_UpdateMenuItems
+=================
+*/
+static void GraphicsOptions_UpdateMenuItems( void )
+{
+ if ( s_graphicsoptions.driver.curvalue == 1 )
+ {
+ s_graphicsoptions.fs.curvalue = 1;
+ s_graphicsoptions.fs.generic.flags |= QMF_GRAYED;
+ s_graphicsoptions.colordepth.curvalue = 1;
+ }
+ else
+ {
+ s_graphicsoptions.fs.generic.flags &= ~QMF_GRAYED;
+ }
+
+ if ( s_graphicsoptions.fs.curvalue == 0 || s_graphicsoptions.driver.curvalue == 1 )
+ {
+ s_graphicsoptions.colordepth.curvalue = 0;
+ s_graphicsoptions.colordepth.generic.flags |= QMF_GRAYED;
+ }
+ else
+ {
+ s_graphicsoptions.colordepth.generic.flags &= ~QMF_GRAYED;
+ }
+
+ if ( s_graphicsoptions.allow_extensions.curvalue == 0 )
+ {
+ if ( s_graphicsoptions.texturebits.curvalue == 0 )
+ {
+ s_graphicsoptions.texturebits.curvalue = 1;
+ }
+ }
+
+ s_graphicsoptions.apply.generic.flags |= QMF_HIDDEN|QMF_INACTIVE;
+
+ if ( s_ivo.mode != s_graphicsoptions.mode.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.fullscreen != s_graphicsoptions.fs.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.extensions != s_graphicsoptions.allow_extensions.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.tq != s_graphicsoptions.tq.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.lighting != s_graphicsoptions.lighting.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.colordepth != s_graphicsoptions.colordepth.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.driver != s_graphicsoptions.driver.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.texturebits != s_graphicsoptions.texturebits.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.geometry != s_graphicsoptions.geometry.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+ if ( s_ivo.filter != s_graphicsoptions.filter.curvalue )
+ {
+ s_graphicsoptions.apply.generic.flags &= ~(QMF_HIDDEN|QMF_INACTIVE);
+ }
+
+ GraphicsOptions_CheckConfig();
+}
+
+/*
+=================
+GraphicsOptions_ApplyChanges
+=================
+*/
+static void GraphicsOptions_ApplyChanges( void *unused, int notification )
+{
+ if (notification != QM_ACTIVATED)
+ return;
+
+ switch ( s_graphicsoptions.texturebits.curvalue )
+ {
+ case 0:
+ trap_Cvar_SetValue( "r_texturebits", 0 );
+ break;
+ case 1:
+ trap_Cvar_SetValue( "r_texturebits", 16 );
+ break;
+ case 2:
+ trap_Cvar_SetValue( "r_texturebits", 32 );
+ break;
+ }
+ trap_Cvar_SetValue( "r_picmip", 3 - s_graphicsoptions.tq.curvalue );
+ trap_Cvar_SetValue( "r_allowExtensions", s_graphicsoptions.allow_extensions.curvalue );
+
+ if( resolutionsDetected )
+ {
+ // search for builtin mode that matches the detected mode
+ int mode;
+ if ( s_graphicsoptions.mode.curvalue == -1
+ || s_graphicsoptions.mode.curvalue >= sizeof(detectedResolutions)/sizeof(detectedResolutions[0]) )
+ s_graphicsoptions.mode.curvalue = 0;
+
+ mode = GraphicsOptions_FindBuiltinResolution( s_graphicsoptions.mode.curvalue );
+ if( mode == -1 )
+ {
+ char w[ 16 ], h[ 16 ];
+ Q_strncpyz( w, detectedResolutions[ s_graphicsoptions.mode.curvalue ], sizeof( w ) );
+ *strchr( w, 'x' ) = 0;
+ Q_strncpyz( h,
+ strchr( detectedResolutions[ s_graphicsoptions.mode.curvalue ], 'x' ) + 1, sizeof( h ) );
+ trap_Cvar_Set( "r_customwidth", w );
+ trap_Cvar_Set( "r_customheight", h );
+ }
+
+ trap_Cvar_SetValue( "r_mode", mode );
+ }
+ else
+ trap_Cvar_SetValue( "r_mode", s_graphicsoptions.mode.curvalue );
+
+ trap_Cvar_SetValue( "r_fullscreen", s_graphicsoptions.fs.curvalue );
+ trap_Cvar_SetValue( "r_colorbits", 0 );
+ trap_Cvar_SetValue( "r_depthbits", 0 );
+ trap_Cvar_SetValue( "r_stencilbits", 0 );
+ trap_Cvar_SetValue( "r_vertexLight", s_graphicsoptions.lighting.curvalue );
+
+ if ( s_graphicsoptions.geometry.curvalue == 2 )
+ {
+ trap_Cvar_SetValue( "r_lodBias", 0 );
+ trap_Cvar_SetValue( "r_subdivisions", 4 );
+ }
+ else if ( s_graphicsoptions.geometry.curvalue == 1 )
+ {
+ trap_Cvar_SetValue( "r_lodBias", 1 );
+ trap_Cvar_SetValue( "r_subdivisions", 12 );
+ }
+ else
+ {
+ trap_Cvar_SetValue( "r_lodBias", 1 );
+ trap_Cvar_SetValue( "r_subdivisions", 20 );
+ }
+
+ if ( s_graphicsoptions.filter.curvalue )
+ {
+ trap_Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_LINEAR" );
+ }
+ else
+ {
+ trap_Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" );
+ }
+
+ trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" );
+}
+
+/*
+=================
+GraphicsOptions_Event
+=================
+*/
+static void GraphicsOptions_Event( void* ptr, int event ) {
+ InitialVideoOptions_s *ivo;
+
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+
+ switch( ((menucommon_s*)ptr)->id ) {
+ case ID_RATIO:
+ s_graphicsoptions.mode.curvalue =
+ ratioToRes[ s_graphicsoptions.ratio.curvalue ];
+ // fall through to apply mode constraints
+
+ case ID_MODE:
+ // clamp 3dfx video modes
+ if ( s_graphicsoptions.driver.curvalue == 1 )
+ {
+ if ( s_graphicsoptions.mode.curvalue < 2 )
+ s_graphicsoptions.mode.curvalue = 2;
+ else if ( s_graphicsoptions.mode.curvalue > 6 )
+ s_graphicsoptions.mode.curvalue = 6;
+ }
+ s_graphicsoptions.ratio.curvalue =
+ resToRatio[ s_graphicsoptions.mode.curvalue ];
+ break;
+
+ case ID_LIST:
+ ivo = &s_ivo_templates[s_graphicsoptions.list.curvalue];
+
+ s_graphicsoptions.mode.curvalue = GraphicsOptions_FindDetectedResolution(ivo->mode);
+ s_graphicsoptions.ratio.curvalue =
+ resToRatio[ s_graphicsoptions.mode.curvalue ];
+ s_graphicsoptions.tq.curvalue = ivo->tq;
+ s_graphicsoptions.lighting.curvalue = ivo->lighting;
+ s_graphicsoptions.colordepth.curvalue = ivo->colordepth;
+ s_graphicsoptions.texturebits.curvalue = ivo->texturebits;
+ s_graphicsoptions.geometry.curvalue = ivo->geometry;
+ s_graphicsoptions.filter.curvalue = ivo->filter;
+ s_graphicsoptions.fs.curvalue = ivo->fullscreen;
+ break;
+
+ case ID_DRIVERINFO:
+ UI_DriverInfo_Menu();
+ break;
+
+ case ID_BACK2:
+ UI_PopMenu();
+ break;
+
+ case ID_GRAPHICS:
+ break;
+
+ case ID_DISPLAY:
+ UI_PopMenu();
+ UI_DisplayOptionsMenu();
+ break;
+
+ case ID_SOUND:
+ UI_PopMenu();
+ UI_SoundOptionsMenu();
+ break;
+
+ case ID_NETWORK:
+ UI_PopMenu();
+ UI_NetworkOptionsMenu();
+ break;
+ }
+}
+
+
+/*
+================
+GraphicsOptions_TQEvent
+================
+*/
+static void GraphicsOptions_TQEvent( void *ptr, int event ) {
+ if( event != QM_ACTIVATED ) {
+ return;
+ }
+ s_graphicsoptions.tq.curvalue = (int)(s_graphicsoptions.tq.curvalue + 0.5);
+}
+
+
+/*
+================
+GraphicsOptions_MenuDraw
+================
+*/
+void GraphicsOptions_MenuDraw (void)
+{
+//APSFIX - rework this
+ GraphicsOptions_UpdateMenuItems();
+
+ Menu_Draw( &s_graphicsoptions.menu );
+}
+
+/*
+=================
+GraphicsOptions_SetMenuItems
+=================
+*/
+static void GraphicsOptions_SetMenuItems( void )
+{
+ s_graphicsoptions.mode.curvalue =
+ GraphicsOptions_FindDetectedResolution( trap_Cvar_VariableValue( "r_mode" ) );
+
+ if ( s_graphicsoptions.mode.curvalue < 0 )
+ {
+ if( resolutionsDetected )
+ {
+ int i;
+ char buf[MAX_STRING_CHARS];
+ trap_Cvar_VariableStringBuffer("r_customwidth", buf, sizeof(buf)-2);
+ buf[strlen(buf)+1] = 0;
+ buf[strlen(buf)] = 'x';
+ trap_Cvar_VariableStringBuffer("r_customheight", buf+strlen(buf), sizeof(buf)-strlen(buf));
+
+ for(i = 0; detectedResolutions[i]; ++i)
+ {
+ if(!Q_stricmp(buf, detectedResolutions[i]))
+ {
+ s_graphicsoptions.mode.curvalue = i;
+ break;
+ }
+ }
+ if ( s_graphicsoptions.mode.curvalue < 0 )
+ s_graphicsoptions.mode.curvalue = 0;
+ }
+ else
+ {
+ s_graphicsoptions.mode.curvalue = 3;
+ }
+ }
+ s_graphicsoptions.ratio.curvalue =
+ resToRatio[ s_graphicsoptions.mode.curvalue ];
+ s_graphicsoptions.fs.curvalue = trap_Cvar_VariableValue("r_fullscreen");
+ s_graphicsoptions.allow_extensions.curvalue = trap_Cvar_VariableValue("r_allowExtensions");
+ s_graphicsoptions.tq.curvalue = 3-trap_Cvar_VariableValue( "r_picmip");
+ if ( s_graphicsoptions.tq.curvalue < 0 )
+ {
+ s_graphicsoptions.tq.curvalue = 0;
+ }
+ else if ( s_graphicsoptions.tq.curvalue > 3 )
+ {
+ s_graphicsoptions.tq.curvalue = 3;
+ }
+
+ s_graphicsoptions.lighting.curvalue = trap_Cvar_VariableValue( "r_vertexLight" ) != 0;
+ switch ( ( int ) trap_Cvar_VariableValue( "r_texturebits" ) )
+ {
+ default:
+ case 0:
+ s_graphicsoptions.texturebits.curvalue = 0;
+ break;
+ case 16:
+ s_graphicsoptions.texturebits.curvalue = 1;
+ break;
+ case 32:
+ s_graphicsoptions.texturebits.curvalue = 2;
+ break;
+ }
+
+ if ( !Q_stricmp( UI_Cvar_VariableString( "r_textureMode" ), "GL_LINEAR_MIPMAP_NEAREST" ) )
+ {
+ s_graphicsoptions.filter.curvalue = 0;
+ }
+ else
+ {
+ s_graphicsoptions.filter.curvalue = 1;
+ }
+
+ if ( trap_Cvar_VariableValue( "r_lodBias" ) > 0 )
+ {
+ if ( trap_Cvar_VariableValue( "r_subdivisions" ) >= 20 )
+ {
+ s_graphicsoptions.geometry.curvalue = 0;
+ }
+ else
+ {
+ s_graphicsoptions.geometry.curvalue = 1;
+ }
+ }
+ else
+ {
+ s_graphicsoptions.geometry.curvalue = 2;
+ }
+
+ switch ( ( int ) trap_Cvar_VariableValue( "r_colorbits" ) )
+ {
+ default:
+ case 0:
+ s_graphicsoptions.colordepth.curvalue = 0;
+ break;
+ case 16:
+ s_graphicsoptions.colordepth.curvalue = 1;
+ break;
+ case 32:
+ s_graphicsoptions.colordepth.curvalue = 2;
+ break;
+ }
+
+ if ( s_graphicsoptions.fs.curvalue == 0 )
+ {
+ s_graphicsoptions.colordepth.curvalue = 0;
+ }
+ if ( s_graphicsoptions.driver.curvalue == 1 )
+ {
+ s_graphicsoptions.colordepth.curvalue = 1;
+ }
+}
+
+/*
+================
+GraphicsOptions_MenuInit
+================
+*/
+void GraphicsOptions_MenuInit( void )
+{
+ static const char *s_driver_names[] =
+ {
+ "Default",
+ "Voodoo",
+ NULL
+ };
+
+ static const char *tq_names[] =
+ {
+ "Default",
+ "16 bit",
+ "32 bit",
+ NULL
+ };
+
+ static const char *s_graphics_options_names[] =
+ {
+ "Very High Quality",
+ "High Quality",
+ "Normal",
+ "Fast",
+ "Fastest",
+ "Custom",
+ NULL
+ };
+
+ static const char *lighting_names[] =
+ {
+ "Lightmap",
+ "Vertex",
+ NULL
+ };
+
+ static const char *colordepth_names[] =
+ {
+ "Default",
+ "16 bit",
+ "32 bit",
+ NULL
+ };
+
+ static const char *filter_names[] =
+ {
+ "Bilinear",
+ "Trilinear",
+ NULL
+ };
+ static const char *quality_names[] =
+ {
+ "Low",
+ "Medium",
+ "High",
+ NULL
+ };
+ static const char *enabled_names[] =
+ {
+ "Off",
+ "On",
+ NULL
+ };
+
+ int y;
+
+ // zero set all our globals
+ memset( &s_graphicsoptions, 0 ,sizeof(graphicsoptions_t) );
+
+ GraphicsOptions_GetResolutions();
+ GraphicsOptions_GetAspectRatios();
+
+ GraphicsOptions_Cache();
+
+ s_graphicsoptions.menu.wrapAround = qtrue;
+ s_graphicsoptions.menu.fullscreen = qtrue;
+ s_graphicsoptions.menu.draw = GraphicsOptions_MenuDraw;
+
+ s_graphicsoptions.banner.generic.type = MTYPE_BTEXT;
+ s_graphicsoptions.banner.generic.x = 320;
+ s_graphicsoptions.banner.generic.y = 16;
+ s_graphicsoptions.banner.string = "SYSTEM SETUP";
+ s_graphicsoptions.banner.color = color_white;
+ s_graphicsoptions.banner.style = UI_CENTER;
+
+ s_graphicsoptions.framel.generic.type = MTYPE_BITMAP;
+ s_graphicsoptions.framel.generic.name = GRAPHICSOPTIONS_FRAMEL;
+ s_graphicsoptions.framel.generic.flags = QMF_INACTIVE;
+ s_graphicsoptions.framel.generic.x = 0;
+ s_graphicsoptions.framel.generic.y = 78;
+ s_graphicsoptions.framel.width = 256;
+ s_graphicsoptions.framel.height = 329;
+
+ s_graphicsoptions.framer.generic.type = MTYPE_BITMAP;
+ s_graphicsoptions.framer.generic.name = GRAPHICSOPTIONS_FRAMER;
+ s_graphicsoptions.framer.generic.flags = QMF_INACTIVE;
+ s_graphicsoptions.framer.generic.x = 376;
+ s_graphicsoptions.framer.generic.y = 76;
+ s_graphicsoptions.framer.width = 256;
+ s_graphicsoptions.framer.height = 334;
+
+ s_graphicsoptions.graphics.generic.type = MTYPE_PTEXT;
+ s_graphicsoptions.graphics.generic.flags = QMF_RIGHT_JUSTIFY;
+ s_graphicsoptions.graphics.generic.id = ID_GRAPHICS;
+ s_graphicsoptions.graphics.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.graphics.generic.x = 216;
+ s_graphicsoptions.graphics.generic.y = 240 - 2 * PROP_HEIGHT;
+ s_graphicsoptions.graphics.string = "GRAPHICS";
+ s_graphicsoptions.graphics.style = UI_RIGHT;
+ s_graphicsoptions.graphics.color = color_red;
+
+ s_graphicsoptions.display.generic.type = MTYPE_PTEXT;
+ s_graphicsoptions.display.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_graphicsoptions.display.generic.id = ID_DISPLAY;
+ s_graphicsoptions.display.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.display.generic.x = 216;
+ s_graphicsoptions.display.generic.y = 240 - PROP_HEIGHT;
+ s_graphicsoptions.display.string = "DISPLAY";
+ s_graphicsoptions.display.style = UI_RIGHT;
+ s_graphicsoptions.display.color = color_red;
+
+ s_graphicsoptions.sound.generic.type = MTYPE_PTEXT;
+ s_graphicsoptions.sound.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_graphicsoptions.sound.generic.id = ID_SOUND;
+ s_graphicsoptions.sound.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.sound.generic.x = 216;
+ s_graphicsoptions.sound.generic.y = 240;
+ s_graphicsoptions.sound.string = "SOUND";
+ s_graphicsoptions.sound.style = UI_RIGHT;
+ s_graphicsoptions.sound.color = color_red;
+
+ s_graphicsoptions.network.generic.type = MTYPE_PTEXT;
+ s_graphicsoptions.network.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_graphicsoptions.network.generic.id = ID_NETWORK;
+ s_graphicsoptions.network.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.network.generic.x = 216;
+ s_graphicsoptions.network.generic.y = 240 + PROP_HEIGHT;
+ s_graphicsoptions.network.string = "NETWORK";
+ s_graphicsoptions.network.style = UI_RIGHT;
+ s_graphicsoptions.network.color = color_red;
+
+ y = 240 - 7 * (BIGCHAR_HEIGHT + 2);
+ s_graphicsoptions.list.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.list.generic.name = "Graphics Settings:";
+ s_graphicsoptions.list.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.list.generic.x = 400;
+ s_graphicsoptions.list.generic.y = y;
+ s_graphicsoptions.list.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.list.generic.id = ID_LIST;
+ s_graphicsoptions.list.itemnames = s_graphics_options_names;
+ y += 2 * ( BIGCHAR_HEIGHT + 2 );
+
+ s_graphicsoptions.driver.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.driver.generic.name = "GL Driver:";
+ s_graphicsoptions.driver.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.driver.generic.x = 400;
+ s_graphicsoptions.driver.generic.y = y;
+ s_graphicsoptions.driver.itemnames = s_driver_names;
+ s_graphicsoptions.driver.curvalue = (uis.glconfig.driverType == GLDRV_VOODOO);
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_allowExtensions"
+ s_graphicsoptions.allow_extensions.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.allow_extensions.generic.name = "GL Extensions:";
+ s_graphicsoptions.allow_extensions.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.allow_extensions.generic.x = 400;
+ s_graphicsoptions.allow_extensions.generic.y = y;
+ s_graphicsoptions.allow_extensions.itemnames = enabled_names;
+ y += BIGCHAR_HEIGHT+2;
+
+ s_graphicsoptions.ratio.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.ratio.generic.name = "Aspect Ratio:";
+ s_graphicsoptions.ratio.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.ratio.generic.x = 400;
+ s_graphicsoptions.ratio.generic.y = y;
+ s_graphicsoptions.ratio.itemnames = ratios;
+ s_graphicsoptions.ratio.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.ratio.generic.id = ID_RATIO;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_mode"
+ s_graphicsoptions.mode.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.mode.generic.name = "Resolution:";
+ s_graphicsoptions.mode.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.mode.generic.x = 400;
+ s_graphicsoptions.mode.generic.y = y;
+ s_graphicsoptions.mode.itemnames = resolutions;
+ s_graphicsoptions.mode.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.mode.generic.id = ID_MODE;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references "r_colorbits"
+ s_graphicsoptions.colordepth.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.colordepth.generic.name = "Color Depth:";
+ s_graphicsoptions.colordepth.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.colordepth.generic.x = 400;
+ s_graphicsoptions.colordepth.generic.y = y;
+ s_graphicsoptions.colordepth.itemnames = colordepth_names;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_fullscreen"
+ s_graphicsoptions.fs.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.fs.generic.name = "Fullscreen:";
+ s_graphicsoptions.fs.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.fs.generic.x = 400;
+ s_graphicsoptions.fs.generic.y = y;
+ s_graphicsoptions.fs.itemnames = enabled_names;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_vertexLight"
+ s_graphicsoptions.lighting.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.lighting.generic.name = "Lighting:";
+ s_graphicsoptions.lighting.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.lighting.generic.x = 400;
+ s_graphicsoptions.lighting.generic.y = y;
+ s_graphicsoptions.lighting.itemnames = lighting_names;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_lodBias" & "subdivisions"
+ s_graphicsoptions.geometry.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.geometry.generic.name = "Geometric Detail:";
+ s_graphicsoptions.geometry.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.geometry.generic.x = 400;
+ s_graphicsoptions.geometry.generic.y = y;
+ s_graphicsoptions.geometry.itemnames = quality_names;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_picmip"
+ s_graphicsoptions.tq.generic.type = MTYPE_SLIDER;
+ s_graphicsoptions.tq.generic.name = "Texture Detail:";
+ s_graphicsoptions.tq.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.tq.generic.x = 400;
+ s_graphicsoptions.tq.generic.y = y;
+ s_graphicsoptions.tq.minvalue = 0;
+ s_graphicsoptions.tq.maxvalue = 3;
+ s_graphicsoptions.tq.generic.callback = GraphicsOptions_TQEvent;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_textureBits"
+ s_graphicsoptions.texturebits.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.texturebits.generic.name = "Texture Quality:";
+ s_graphicsoptions.texturebits.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.texturebits.generic.x = 400;
+ s_graphicsoptions.texturebits.generic.y = y;
+ s_graphicsoptions.texturebits.itemnames = tq_names;
+ y += BIGCHAR_HEIGHT+2;
+
+ // references/modifies "r_textureMode"
+ s_graphicsoptions.filter.generic.type = MTYPE_SPINCONTROL;
+ s_graphicsoptions.filter.generic.name = "Texture Filter:";
+ s_graphicsoptions.filter.generic.flags = QMF_PULSEIFFOCUS|QMF_SMALLFONT;
+ s_graphicsoptions.filter.generic.x = 400;
+ s_graphicsoptions.filter.generic.y = y;
+ s_graphicsoptions.filter.itemnames = filter_names;
+ y += 2*BIGCHAR_HEIGHT;
+
+ s_graphicsoptions.driverinfo.generic.type = MTYPE_PTEXT;
+ s_graphicsoptions.driverinfo.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_graphicsoptions.driverinfo.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.driverinfo.generic.id = ID_DRIVERINFO;
+ s_graphicsoptions.driverinfo.generic.x = 320;
+ s_graphicsoptions.driverinfo.generic.y = y;
+ s_graphicsoptions.driverinfo.string = "Driver Info";
+ s_graphicsoptions.driverinfo.style = UI_CENTER|UI_SMALLFONT;
+ s_graphicsoptions.driverinfo.color = color_red;
+ y += BIGCHAR_HEIGHT+2;
+
+ s_graphicsoptions.back.generic.type = MTYPE_BITMAP;
+ s_graphicsoptions.back.generic.name = GRAPHICSOPTIONS_BACK0;
+ s_graphicsoptions.back.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS;
+ s_graphicsoptions.back.generic.callback = GraphicsOptions_Event;
+ s_graphicsoptions.back.generic.id = ID_BACK2;
+ s_graphicsoptions.back.generic.x = 0;
+ s_graphicsoptions.back.generic.y = 480-64;
+ s_graphicsoptions.back.width = 128;
+ s_graphicsoptions.back.height = 64;
+ s_graphicsoptions.back.focuspic = GRAPHICSOPTIONS_BACK1;
+
+ s_graphicsoptions.apply.generic.type = MTYPE_BITMAP;
+ s_graphicsoptions.apply.generic.name = GRAPHICSOPTIONS_ACCEPT0;
+ s_graphicsoptions.apply.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_HIDDEN|QMF_INACTIVE;
+ s_graphicsoptions.apply.generic.callback = GraphicsOptions_ApplyChanges;
+ s_graphicsoptions.apply.generic.x = 640;
+ s_graphicsoptions.apply.generic.y = 480-64;
+ s_graphicsoptions.apply.width = 128;
+ s_graphicsoptions.apply.height = 64;
+ s_graphicsoptions.apply.focuspic = GRAPHICSOPTIONS_ACCEPT1;
+
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.banner );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.framel );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.framer );
+
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.graphics );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.display );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.sound );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.network );
+
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.list );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.driver );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.allow_extensions );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.ratio );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.mode );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.colordepth );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.fs );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.lighting );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.geometry );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.tq );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.texturebits );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.filter );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.driverinfo );
+
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.back );
+ Menu_AddItem( &s_graphicsoptions.menu, ( void * ) &s_graphicsoptions.apply );
+
+ GraphicsOptions_SetMenuItems();
+ GraphicsOptions_GetInitialVideo();
+
+ if ( uis.glconfig.driverType == GLDRV_ICD &&
+ uis.glconfig.hardwareType == GLHW_3DFX_2D3D )
+ {
+ s_graphicsoptions.driver.generic.flags |= QMF_HIDDEN|QMF_INACTIVE;
+ }
+}
+
+
+/*
+=================
+GraphicsOptions_Cache
+=================
+*/
+void GraphicsOptions_Cache( void ) {
+ trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_FRAMEL );
+ trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_FRAMER );
+ trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_BACK0 );
+ trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_BACK1 );
+ trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_ACCEPT0 );
+ trap_R_RegisterShaderNoMip( GRAPHICSOPTIONS_ACCEPT1 );
+}
+
+
+/*
+=================
+UI_GraphicsOptionsMenu
+=================
+*/
+void UI_GraphicsOptionsMenu( void ) {
+ GraphicsOptions_MenuInit();
+ UI_PushMenu( &s_graphicsoptions.menu );
+ Menu_SetCursorToItem( &s_graphicsoptions.menu, &s_graphicsoptions.graphics );
+}
+