diff options
| author | Paweł Redman <pawel.redman@gmail.com> | 2017-03-22 17:56:34 +0100 | 
|---|---|---|
| committer | Paweł Redman <pawel.redman@gmail.com> | 2017-03-22 17:56:34 +0100 | 
| commit | 6a777afc079c2a8d3af3ecd2145fe8dd50567a39 (patch) | |
| tree | 520f4489cebf8564ef6cb27064ceea45cbc005b3 /src/ui | |
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/ui_atoms.c | 519 | ||||
| -rw-r--r-- | src/ui/ui_gameinfo.c | 333 | ||||
| -rw-r--r-- | src/ui/ui_local.h | 1207 | ||||
| -rw-r--r-- | src/ui/ui_main.c | 6496 | ||||
| -rw-r--r-- | src/ui/ui_players.c | 1369 | ||||
| -rw-r--r-- | src/ui/ui_public.h | 188 | ||||
| -rw-r--r-- | src/ui/ui_shared.c | 6075 | ||||
| -rw-r--r-- | src/ui/ui_shared.h | 454 | ||||
| -rw-r--r-- | src/ui/ui_syscalls.asm | 97 | ||||
| -rw-r--r-- | src/ui/ui_syscalls.c | 387 | 
10 files changed, 17125 insertions, 0 deletions
diff --git a/src/ui/ui_atoms.c b/src/ui/ui_atoms.c new file mode 100644 index 0000000..ed3df50 --- /dev/null +++ b/src/ui/ui_atoms.c @@ -0,0 +1,519 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +/**********************************************************************
 +  UI_ATOMS.C
 +
 +  User interface building blocks and support functions.
 +**********************************************************************/
 +#include "ui_local.h"
 +
 +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);
 +  vsprintf (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);
 +  vsprintf (text, msg, argptr);
 +  va_end (argptr);
 +
 +  trap_Print( va("%s", text) );
 +}
 +
 +qboolean newUI = qfalse;
 +
 +
 +/*
 +=================
 +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" );
 +}
 +
 +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;
 +}
 +
 +
 +
 +void UI_SetBestScores(postGameInfo_t *newInfo, qboolean postGame) {
 +  trap_Cvar_Set("ui_scoreAccuracy",     va("%i%%", newInfo->accuracy));
 +  trap_Cvar_Set("ui_scoreImpressives",  va("%i", newInfo->impressives));
 +  trap_Cvar_Set("ui_scoreExcellents",   va("%i", newInfo->excellents));
 +  trap_Cvar_Set("ui_scoreDefends",       va("%i", newInfo->defends));
 +  trap_Cvar_Set("ui_scoreAssists",       va("%i", newInfo->assists));
 +  trap_Cvar_Set("ui_scoreGauntlets",     va("%i", newInfo->gauntlets));
 +  trap_Cvar_Set("ui_scoreScore",         va("%i", newInfo->score));
 +  trap_Cvar_Set("ui_scorePerfect",       va("%i", newInfo->perfects));
 +  trap_Cvar_Set("ui_scoreTeam",          va("%i to %i", newInfo->redScore, newInfo->blueScore));
 +  trap_Cvar_Set("ui_scoreBase",          va("%i", newInfo->baseScore));
 +  trap_Cvar_Set("ui_scoreTimeBonus",    va("%i", newInfo->timeBonus));
 +  trap_Cvar_Set("ui_scoreSkillBonus",    va("%i", newInfo->skillBonus));
 +  trap_Cvar_Set("ui_scoreShutoutBonus",  va("%i", newInfo->shutoutBonus));
 +  trap_Cvar_Set("ui_scoreTime",          va("%02i:%02i", newInfo->time / 60, newInfo->time % 60));
 +  trap_Cvar_Set("ui_scoreCaptures",    va("%i", newInfo->captures));
 +  if (postGame) {
 +    trap_Cvar_Set("ui_scoreAccuracy2",     va("%i%%", newInfo->accuracy));
 +    trap_Cvar_Set("ui_scoreImpressives2",  va("%i", newInfo->impressives));
 +    trap_Cvar_Set("ui_scoreExcellents2",   va("%i", newInfo->excellents));
 +    trap_Cvar_Set("ui_scoreDefends2",       va("%i", newInfo->defends));
 +    trap_Cvar_Set("ui_scoreAssists2",       va("%i", newInfo->assists));
 +    trap_Cvar_Set("ui_scoreGauntlets2",     va("%i", newInfo->gauntlets));
 +    trap_Cvar_Set("ui_scoreScore2",         va("%i", newInfo->score));
 +    trap_Cvar_Set("ui_scorePerfect2",       va("%i", newInfo->perfects));
 +    trap_Cvar_Set("ui_scoreTeam2",          va("%i to %i", newInfo->redScore, newInfo->blueScore));
 +    trap_Cvar_Set("ui_scoreBase2",          va("%i", newInfo->baseScore));
 +    trap_Cvar_Set("ui_scoreTimeBonus2",    va("%i", newInfo->timeBonus));
 +    trap_Cvar_Set("ui_scoreSkillBonus2",    va("%i", newInfo->skillBonus));
 +    trap_Cvar_Set("ui_scoreShutoutBonus2",  va("%i", newInfo->shutoutBonus));
 +    trap_Cvar_Set("ui_scoreTime2",          va("%02i:%02i", newInfo->time / 60, newInfo->time % 60));
 +    trap_Cvar_Set("ui_scoreCaptures2",    va("%i", newInfo->captures));
 +  }
 +}
 +
 +void UI_LoadBestScores(const char *map, int game) {
 +  char    fileName[MAX_QPATH];
 +  fileHandle_t f;
 +  postGameInfo_t newInfo;
 +  memset(&newInfo, 0, sizeof(postGameInfo_t));
 +  Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game);
 +  if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) {
 +    int size = 0;
 +    trap_FS_Read(&size, sizeof(int), f);
 +    if (size == sizeof(postGameInfo_t)) {
 +      trap_FS_Read(&newInfo, sizeof(postGameInfo_t), f);
 +    }
 +    trap_FS_FCloseFile(f);
 +  }
 +  UI_SetBestScores(&newInfo, qfalse);
 +
 +  Com_sprintf(fileName, MAX_QPATH, "demos/%s_%d.dm_%d", map, game, (int)trap_Cvar_VariableValue("protocol"));
 +  uiInfo.demoAvailable = qfalse;
 +  if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) {
 +    uiInfo.demoAvailable = qtrue;
 +    trap_FS_FCloseFile(f);
 +  }
 +}
 +
 +/*
 +===============
 +UI_ClearScores
 +===============
 +*/
 +void UI_ClearScores( void ) {
 +  char  gameList[4096];
 +  char *gameFile;
 +  int    i, len, count, size;
 +  fileHandle_t f;
 +  postGameInfo_t newInfo;
 +
 +  count = trap_FS_GetFileList( "games", "game", gameList, sizeof(gameList) );
 +
 +  size = sizeof(postGameInfo_t);
 +  memset(&newInfo, 0, size);
 +
 +  if (count > 0) {
 +    gameFile = gameList;
 +    for ( i = 0; i < count; i++ ) {
 +      len = strlen(gameFile);
 +      if (trap_FS_FOpenFile(va("games/%s",gameFile), &f, FS_WRITE) >= 0) {
 +        trap_FS_Write(&size, sizeof(int), f);
 +        trap_FS_Write(&newInfo, size, f);
 +        trap_FS_FCloseFile(f);
 +      }
 +      gameFile += len + 1;
 +    }
 +  }
 +
 +  UI_SetBestScores(&newInfo, qfalse);
 +
 +}
 +
 +
 +
 +static void  UI_Cache_f( void ) {
 +  Display_CacheAll();
 +}
 +
 +/*
 +=======================
 +UI_CalcPostGameStats
 +=======================
 +*/
 +static void UI_CalcPostGameStats( void ) {
 +  char    map[MAX_QPATH];
 +  char    fileName[MAX_QPATH];
 +  char    info[MAX_INFO_STRING];
 +  fileHandle_t f;
 +  int size, game, time, adjustedTime;
 +  postGameInfo_t oldInfo;
 +  postGameInfo_t newInfo;
 +  qboolean newHigh = qfalse;
 +
 +  trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
 +  Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) );
 +  game = atoi(Info_ValueForKey(info, "g_gametype"));
 +
 +  // compose file name
 +  Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game);
 +  // see if we have one already
 +  memset(&oldInfo, 0, sizeof(postGameInfo_t));
 +  if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) {
 +  // if so load it
 +    size = 0;
 +    trap_FS_Read(&size, sizeof(int), f);
 +    if (size == sizeof(postGameInfo_t)) {
 +      trap_FS_Read(&oldInfo, sizeof(postGameInfo_t), f);
 +    }
 +    trap_FS_FCloseFile(f);
 +  }
 +
 +  newInfo.accuracy = atoi(UI_Argv(3));
 +  newInfo.impressives = atoi(UI_Argv(4));
 +  newInfo.excellents = atoi(UI_Argv(5));
 +  newInfo.defends = atoi(UI_Argv(6));
 +  newInfo.assists = atoi(UI_Argv(7));
 +  newInfo.gauntlets = atoi(UI_Argv(8));
 +  newInfo.baseScore = atoi(UI_Argv(9));
 +  newInfo.perfects = atoi(UI_Argv(10));
 +  newInfo.redScore = atoi(UI_Argv(11));
 +  newInfo.blueScore = atoi(UI_Argv(12));
 +  time = atoi(UI_Argv(13));
 +  newInfo.captures = atoi(UI_Argv(14));
 +
 +  newInfo.time = (time - trap_Cvar_VariableValue("ui_matchStartTime")) / 1000;
 +  adjustedTime = uiInfo.mapList[ui_currentMap.integer].timeToBeat[game];
 +  if (newInfo.time < adjustedTime) {
 +    newInfo.timeBonus = (adjustedTime - newInfo.time) * 10;
 +  } else {
 +    newInfo.timeBonus = 0;
 +  }
 +
 +  if (newInfo.redScore > newInfo.blueScore && newInfo.blueScore <= 0) {
 +    newInfo.shutoutBonus = 100;
 +  } else {
 +    newInfo.shutoutBonus = 0;
 +  }
 +
 +  newInfo.skillBonus = trap_Cvar_VariableValue("g_spSkill");
 +  if (newInfo.skillBonus <= 0) {
 +    newInfo.skillBonus = 1;
 +  }
 +  newInfo.score = newInfo.baseScore + newInfo.shutoutBonus + newInfo.timeBonus;
 +  newInfo.score *= newInfo.skillBonus;
 +
 +  // see if the score is higher for this one
 +  newHigh = (newInfo.redScore > newInfo.blueScore && newInfo.score > oldInfo.score);
 +
 +  if  (newHigh) {
 +    // if so write out the new one
 +    uiInfo.newHighScoreTime = uiInfo.uiDC.realTime + 20000;
 +    if (trap_FS_FOpenFile(fileName, &f, FS_WRITE) >= 0) {
 +      size = sizeof(postGameInfo_t);
 +      trap_FS_Write(&size, sizeof(int), f);
 +      trap_FS_Write(&newInfo, sizeof(postGameInfo_t), f);
 +      trap_FS_FCloseFile(f);
 +    }
 +  }
 +
 +  if (newInfo.time < oldInfo.time) {
 +    uiInfo.newBestTime = uiInfo.uiDC.realTime + 20000;
 +  }
 +
 +  // put back all the ui overrides
 +  trap_Cvar_Set("capturelimit", UI_Cvar_VariableString("ui_saveCaptureLimit"));
 +  trap_Cvar_Set("fraglimit", UI_Cvar_VariableString("ui_saveFragLimit"));
 +  trap_Cvar_Set("cg_drawTimer", UI_Cvar_VariableString("ui_drawTimer"));
 +  trap_Cvar_Set("g_doWarmup", UI_Cvar_VariableString("ui_doWarmup"));
 +  trap_Cvar_Set("g_Warmup", UI_Cvar_VariableString("ui_Warmup"));
 +  trap_Cvar_Set("sv_pure", UI_Cvar_VariableString("ui_pure"));
 +  trap_Cvar_Set("g_friendlyFire", UI_Cvar_VariableString("ui_friendlyFire"));
 +
 +  UI_SetBestScores(&newInfo, qtrue);
 +  UI_ShowPostGame(newHigh);
 +
 +
 +}
 +
 +
 +/*
 +=================
 +UI_ConsoleCommand
 +=================
 +*/
 +qboolean UI_ConsoleCommand( int realTime )
 +{
 +  char  *cmd;
 +  char  *arg1;
 +
 +  uiInfo.uiDC.frameTime = realTime - uiInfo.uiDC.realTime;
 +  uiInfo.uiDC.realTime = realTime;
 +
 +  cmd = UI_Argv( 0 );
 +
 +  // ensure minimum menu data is available
 +  //Menu_Cache();
 +
 +  if ( Q_stricmp (cmd, "ui_test") == 0 ) {
 +    UI_ShowPostGame(qtrue);
 +  }
 +
 +  if ( Q_stricmp (cmd, "ui_report") == 0 ) {
 +    UI_Report();
 +    return qtrue;
 +  }
 +
 +  if ( Q_stricmp (cmd, "ui_load") == 0 ) {
 +    UI_Load();
 +    return qtrue;
 +  }
 +
 +  if ( Q_stricmp (cmd, "remapShader") == 0 ) {
 +    if (trap_Argc() == 4) {
 +      char shader1[MAX_QPATH];
 +      char shader2[MAX_QPATH];
 +      Q_strncpyz(shader1, UI_Argv(1), sizeof(shader1));
 +      Q_strncpyz(shader2, UI_Argv(2), sizeof(shader2));
 +      trap_R_RemapShader(shader1, shader2, UI_Argv(3));
 +      return qtrue;
 +    }
 +  }
 +
 +  if ( Q_stricmp (cmd, "postgame") == 0 ) {
 +    UI_CalcPostGameStats();
 +    return qtrue;
 +  }
 +
 +  if ( Q_stricmp (cmd, "ui_cache") == 0 ) {
 +    UI_Cache_f();
 +    return qtrue;
 +  }
 +
 +  if ( Q_stricmp (cmd, "ui_teamOrders") == 0 ) {
 +    //UI_TeamOrdersMenu_f();
 +    return qtrue;
 +  }
 +
 +  if( Q_stricmp ( cmd, "menu" ) == 0 )
 +  {
 +    arg1 = UI_Argv( 1 );
 +
 +    if( Menu_Count( ) > 0 )
 +    {
 +      trap_Key_SetCatcher( KEYCATCH_UI );
 +      Menus_ActivateByName( arg1 );
 +      return qtrue;
 +    }
 +  }
 +
 +  if( Q_stricmp ( cmd, "closemenus" ) == 0 )
 +  {
 +    if( Menu_Count( ) > 0 )
 +    {
 +      trap_Key_SetCatcher( trap_Key_GetCatcher( ) & ~KEYCATCH_UI );
 +      trap_Key_ClearStates( );
 +      trap_Cvar_Set( "cl_paused", "0" );
 +      Menus_CloseAll( );
 +      return qtrue;
 +    }
 +  }
 +
 +  return qfalse;
 +}
 +
 +/*
 +=================
 +UI_Shutdown
 +=================
 +*/
 +void UI_Shutdown( void ) {
 +}
 +
 +/*
 +================
 +UI_AdjustFrom640
 +
 +Adjusted for resolution and screen aspect ratio
 +================
 +*/
 +void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) {
 +  // expect valid pointers
 +#if 0
 +  *x = *x * uiInfo.uiDC.scale + uiInfo.uiDC.bias;
 +  *y *= uiInfo.uiDC.scale;
 +  *w *= uiInfo.uiDC.scale;
 +  *h *= uiInfo.uiDC.scale;
 +#endif
 +
 +  *x *= uiInfo.uiDC.xscale;
 +  *y *= uiInfo.uiDC.yscale;
 +  *w *= uiInfo.uiDC.xscale;
 +  *h *= uiInfo.uiDC.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, uiInfo.uiDC.whiteShader );
 +
 +  trap_R_SetColor( NULL );
 +}
 +
 +void UI_DrawSides(float x, float y, float w, float h) {
 +  UI_AdjustFrom640( &x, &y, &w, &h );
 +  trap_R_DrawStretchPic( x, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +  trap_R_DrawStretchPic( x + w - 1, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +}
 +
 +void UI_DrawTopBottom(float x, float y, float w, float h) {
 +  UI_AdjustFrom640( &x, &y, &w, &h );
 +  trap_R_DrawStretchPic( x, y, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +  trap_R_DrawStretchPic( x, y + h - 1, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +}
 +/*
 +================
 +UI_DrawRect
 +
 +Coordinates are 640*480 virtual values
 +=================
 +*/
 +void UI_DrawRect( float x, float y, float width, float height, const float *color ) {
 +  trap_R_SetColor( color );
 +
 +  UI_DrawTopBottom(x, y, width, height);
 +  UI_DrawSides(x, y, width, height);
 +
 +  trap_R_SetColor( NULL );
 +}
 +
 +void UI_SetColor( const float *rgba ) {
 +  trap_R_SetColor( rgba );
 +}
 +
 +void UI_UpdateScreen( void ) {
 +  trap_UpdateScreen();
 +}
 +
 +
 +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 (uiInfo.uiDC.cursorx < x ||
 +    uiInfo.uiDC.cursory < y ||
 +    uiInfo.uiDC.cursorx > x+width ||
 +    uiInfo.uiDC.cursory > y+height)
 +    return qfalse;
 +
 +  return qtrue;
 +}
 diff --git a/src/ui/ui_gameinfo.c b/src/ui/ui_gameinfo.c new file mode 100644 index 0000000..b60ce0b --- /dev/null +++ b/src/ui/ui_gameinfo.c @@ -0,0 +1,333 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +//
 +// gameinfo.c
 +//
 +
 +#include "ui_local.h"
 +
 +
 +//
 +// arena and bot info
 +//
 +
 +
 +int       ui_numBots;
 +static char   *ui_botInfos[MAX_BOTS];
 +
 +static int    ui_numArenas;
 +static char   *ui_arenaInfos[MAX_ARENAS];
 +
 +/*
 +===============
 +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_MapNameCompare
 +=================
 +*/
 +static int UI_MapNameCompare( const void *a, const void *b )
 +{
 +  mapInfo *A = (mapInfo *)a;
 +  mapInfo *B = (mapInfo *)b;
 +
 +  return Q_stricmp( A->mapName, B->mapName );
 +}
 +
 +/*
 +===============
 +UI_LoadArenas
 +===============
 +*/
 +void UI_LoadArenas( void ) {
 +  int     numdirs;
 +  char    filename[128];
 +  char    dirlist[1024];
 +  char*   dirptr;
 +  int     i, n;
 +  int     dirlen;
 +  char    *type;
 +
 +  ui_numArenas = 0;
 +  uiInfo.mapCount = 0;
 +
 +  // get all arenas from .arena files
 +  numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 );
 +  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( "[skipnotify]%i arenas parsed\n", ui_numArenas ) );
 +  if (UI_OutOfMemory()) {
 +    trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n");
 +  }
 +
 +  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 && strstr( type, "tremulous" ) )
 +      uiInfo.mapList[ uiInfo.mapCount ].typeBits |= ( 1 << 0 );
 +    else
 +      continue; //not a trem map
 +
 +    uiInfo.mapList[uiInfo.mapCount].cinematic = -1;
 +    uiInfo.mapList[uiInfo.mapCount].mapLoadName = String_Alloc(Info_ValueForKey(ui_arenaInfos[n], "map"));
 +    uiInfo.mapList[uiInfo.mapCount].mapName = String_Alloc(Info_ValueForKey(ui_arenaInfos[n], "longname"));
 +    uiInfo.mapList[uiInfo.mapCount].levelShot = -1;
 +    uiInfo.mapList[uiInfo.mapCount].imageName = String_Alloc(va("levelshots/%s", uiInfo.mapList[uiInfo.mapCount].mapLoadName));
 +
 +    uiInfo.mapCount++;
 +    if( uiInfo.mapCount >= MAX_MAPS )
 +      break;
 +  }
 +
 +  qsort( uiInfo.mapList, uiInfo.mapCount, sizeof( mapInfo ), UI_MapNameCompare );
 +}
 +
 +
 +/*
 +===============
 +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 );
 +
 +  COM_Compress(buf);
 +
 +  ui_numBots += UI_ParseInfos( buf, MAX_BOTS - ui_numBots, &ui_botInfos[ui_numBots] );
 +}
 +
 +/*
 +===============
 +UI_LoadBots
 +===============
 +*/
 +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;
 +}
 +
 +int UI_GetNumBots() {
 +  return ui_numBots;
 +}
 +
 +
 +char *UI_GetBotNameByNumber( int num ) {
 +  char *info = UI_GetBotInfoByNumber(num);
 +  if (info) {
 +    return Info_ValueForKey( info, "name" );
 +  }
 +  return "Sarge";
 +}
 +
 +void UI_ServerInfo( void )
 +{
 +  char      info[ MAX_INFO_VALUE ];
 +
 +  info[0] = '\0';
 +  if( trap_GetConfigString( CS_SERVERINFO, info, sizeof( info ) ) )
 +  { 
 +    trap_Cvar_Set( "ui_serverinfo_mapname",
 +      Info_ValueForKey( info, "mapname" ) );
 +    trap_Cvar_Set( "ui_serverinfo_timelimit",
 +      Info_ValueForKey( info, "timelimit" ) );
 +    trap_Cvar_Set( "ui_serverinfo_sd",
 +      Info_ValueForKey( info, "g_suddenDeathTime" ) );
 +    trap_Cvar_Set( "ui_serverinfo_hostname",
 +      Info_ValueForKey( info, "sv_hostname" ) );
 +    trap_Cvar_Set( "ui_serverinfo_maxclients",
 +      Info_ValueForKey( info, "sv_maxclients" ) );
 +    trap_Cvar_Set( "ui_serverinfo_version",
 +      Info_ValueForKey( info, "version" ) );
 +    trap_Cvar_Set( "ui_serverinfo_unlagged",
 +      Info_ValueForKey( info, "g_unlagged" ) );
 +    trap_Cvar_Set( "ui_serverinfo_ff",
 +      Info_ValueForKey( info, "ff" ) );
 +  }
 +}
 diff --git a/src/ui/ui_local.h b/src/ui/ui_local.h new file mode 100644 index 0000000..7f69874 --- /dev/null +++ b/src/ui/ui_local.h @@ -0,0 +1,1207 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +#ifndef __UI_LOCAL_H__
 +#define __UI_LOCAL_H__
 +
 +#include "../qcommon/q_shared.h"
 +#include "../renderer/tr_types.h"
 +#include "ui_public.h"
 +#include "../client/keycodes.h"
 +#include "../game/bg_public.h"
 +#include "ui_shared.h"
 +
 +// global display context
 +
 +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_captureLimit;
 +extern vmCvar_t  ui_fragLimit;
 +extern vmCvar_t  ui_gameType;
 +extern vmCvar_t  ui_netGameType;
 +extern vmCvar_t  ui_actualNetGameType;
 +extern vmCvar_t  ui_joinGameType;
 +extern vmCvar_t  ui_netSource;
 +extern vmCvar_t  ui_serverFilterType;
 +extern vmCvar_t  ui_dedicated;
 +extern vmCvar_t  ui_opponentName;
 +extern vmCvar_t  ui_menuFiles;
 +extern vmCvar_t  ui_currentTier;
 +extern vmCvar_t  ui_currentMap;
 +extern vmCvar_t  ui_currentNetMap;
 +extern vmCvar_t  ui_mapIndex;
 +extern vmCvar_t  ui_currentOpponent;
 +extern vmCvar_t  ui_selectedPlayer;
 +extern vmCvar_t  ui_selectedPlayerName;
 +extern vmCvar_t  ui_lastServerRefresh_0;
 +extern vmCvar_t  ui_lastServerRefresh_1;
 +extern vmCvar_t  ui_lastServerRefresh_2;
 +extern vmCvar_t  ui_lastServerRefresh_3;
 +extern vmCvar_t  ui_singlePlayerActive;
 +extern vmCvar_t  ui_scoreAccuracy;
 +extern vmCvar_t  ui_scoreImpressives;
 +extern vmCvar_t  ui_scoreExcellents;
 +extern vmCvar_t  ui_scoreDefends;
 +extern vmCvar_t  ui_scoreAssists;
 +extern vmCvar_t  ui_scoreGauntlets;
 +extern vmCvar_t  ui_scoreScore;
 +extern vmCvar_t  ui_scorePerfect;
 +extern vmCvar_t  ui_scoreTeam;
 +extern vmCvar_t  ui_scoreBase;
 +extern vmCvar_t  ui_scoreTimeBonus;
 +extern vmCvar_t  ui_scoreSkillBonus;
 +extern vmCvar_t  ui_scoreShutoutBonus;
 +extern vmCvar_t  ui_scoreTime;
 +extern vmCvar_t  ui_smallFont;
 +extern vmCvar_t  ui_bigFont;
 +extern vmCvar_t ui_serverStatusTimeOut;
 +
 +//TA: bank values
 +extern vmCvar_t  ui_bank;
 +
 +
 +//
 +// 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      128
 +
 +#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        0x00000001
 +#define QMF_SMALLFONT      0x00000002
 +#define QMF_LEFT_JUSTIFY    0x00000004
 +#define QMF_CENTER_JUSTIFY    0x00000008
 +#define QMF_RIGHT_JUSTIFY    0x00000010
 +#define QMF_NUMBERSONLY      0x00000020  // edit field is only numbers
 +#define QMF_HIGHLIGHT      0x00000040
 +#define QMF_HIGHLIGHT_IF_FOCUS  0x00000080  // steady focus
 +#define QMF_PULSEIFFOCUS    0x00000100  // pulse if focus
 +#define QMF_HASMOUSEFOCUS    0x00000200
 +#define QMF_NOONOFFTEXT      0x00000400
 +#define QMF_MOUSEONLY      0x00000800  // only mouse input allowed
 +#define QMF_HIDDEN        0x00001000  // skips drawing
 +#define QMF_GRAYED        0x00002000  // grays and disables
 +#define QMF_INACTIVE      0x00004000  // disables any input
 +#define QMF_NODEFAULTINIT    0x00008000  // skip default initialization
 +#define QMF_OWNERDRAW      0x00010000
 +#define QMF_PULSE        0x00020000
 +#define QMF_LOWERCASE      0x00040000  // edit field is all lower case
 +#define QMF_UPPERCASE      0x00080000  // edit field is all upper case
 +#define QMF_SILENT        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 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_main.c
 +//
 +void UI_Report( void );
 +void UI_Load( void );
 +void UI_LoadMenus(const char *menuFile, qboolean reset);
 +void _UI_SetActiveMenu( uiMenuCommand_t menu );
 +int UI_AdjustTimeByGame(int time);
 +void UI_ShowPostGame(qboolean newHigh);
 +void UI_ClearScores( void );
 +void UI_LoadArenas(void);
 +void UI_ServerInfo(void);
 +
 +//
 +// 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 ) );
 +
 +//
 +// 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_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_PLAYER_TOTALANIMATIONS];
 +
 +  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, const char *headmodel, char *teamName );
 +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 , const char *headName, const char *teamName);
 +
 +//
 +// ui_atoms.c
 +//
 +// this is only used in the old ui, the new ui has it's own version
 +typedef struct {
 +  int          frametime;
 +  int          realtime;
 +  int          cursorx;
 +  int          cursory;
 +  glconfig_t   glconfig;
 +  qboolean     debug;
 +  qhandle_t    whiteShader;
 +  qhandle_t    charset;
 +  qhandle_t    charsetProp;
 +  qhandle_t    charsetPropGlow;
 +  qhandle_t    charsetPropB;
 +  qhandle_t    cursor;
 +  qhandle_t    rb_on;
 +  qhandle_t    rb_off;
 +  float        scale;
 +  float        bias;
 +  qboolean     demoversion;
 +  qboolean     firstdraw;
 +} uiStatic_t;
 +
 +
 +// new ui stuff
 +#define UI_NUMFX 7
 +#define MAX_HEADS 64
 +#define MAX_ALIASES 64
 +#define MAX_HEADNAME  32
 +#define MAX_TEAMS 64
 +#define MAX_GAMETYPES 16
 +#define MAX_MAPS 128
 +#define MAX_SPMAPS 16
 +#define PLAYERS_PER_TEAM 5
 +#define MAX_PINGREQUESTS    32
 +#define MAX_ADDRESSLENGTH    64
 +#define MAX_HOSTNAMELENGTH    22
 +#define MAX_MAPNAMELENGTH    16
 +#define MAX_STATUSLENGTH    64
 +#define MAX_LISTBOXWIDTH    59
 +#define UI_FONT_THRESHOLD    0.1
 +#define MAX_DISPLAY_SERVERS    2048
 +#define MAX_SERVERSTATUS_LINES  128
 +#define MAX_SERVERSTATUS_TEXT  1024
 +#define MAX_FOUNDPLAYER_SERVERS  16
 +#define TEAM_MEMBERS 5
 +#define GAMES_ALL      0
 +#define GAMES_FFA      1
 +#define GAMES_TEAMPLAY    2
 +#define GAMES_TOURNEY    3
 +#define GAMES_CTF      4
 +#define MAPS_PER_TIER 3
 +#define MAX_TIERS 16
 +#define MAX_MODS 64
 +#define MAX_DEMOS 256
 +#define MAX_MOVIES 256
 +#define MAX_PLAYERMODELS 256
 +
 +
 +typedef struct {
 +  const char *name;
 +  const char *imageName;
 +  qhandle_t headImage;
 +  const char *base;
 +  qboolean active;
 +  int reference;
 +} characterInfo;
 +
 +typedef struct {
 +  const char *name;
 +  const char *ai;
 +  const char *action;
 +} aliasInfo;
 +
 +typedef struct {
 +  const char *teamName;
 +  const char *imageName;
 +  const char *teamMembers[TEAM_MEMBERS];
 +  qhandle_t teamIcon;
 +  qhandle_t teamIcon_Metal;
 +  qhandle_t teamIcon_Name;
 +  int cinematic;
 +} teamInfo;
 +
 +typedef struct {
 +  const char *gameType;
 +  int gtEnum;
 +} gameTypeInfo;
 +
 +typedef struct {
 +  const char *mapName;
 +  const char *mapLoadName;
 +  const char *imageName;
 +  const char *opponentName;
 +  int teamMembers;
 +  int typeBits;
 +  int cinematic;
 +  int timeToBeat[MAX_GAMETYPES];
 +  qhandle_t levelShot;
 +  qboolean active;
 +} mapInfo;
 +
 +typedef struct {
 +  const char *tierName;
 +  const char *maps[MAPS_PER_TIER];
 +  int gameTypes[MAPS_PER_TIER];
 +  qhandle_t mapHandles[MAPS_PER_TIER];
 +} tierInfo;
 +
 +typedef struct serverFilter_s {
 +  const char *description;
 +  const char *basedir;
 +} serverFilter_t;
 +
 +typedef struct {
 +  char  adrstr[MAX_ADDRESSLENGTH];
 +  int    start;
 +} pinglist_t;
 +
 +
 +typedef struct serverStatus_s {
 +  pinglist_t pingList[MAX_PINGREQUESTS];
 +  int    numqueriedservers;
 +  int    currentping;
 +  int    nextpingtime;
 +  int    maxservers;
 +  int    refreshtime;
 +  int    numServers;
 +  int    sortKey;
 +  int    sortDir;
 +  qboolean sorted;
 +  int    lastCount;
 +  qboolean refreshActive;
 +  int    currentServer;
 +  int    displayServers[MAX_DISPLAY_SERVERS];
 +  int    numDisplayServers;
 +  int    numPlayersOnServers;
 +  int    nextDisplayRefresh;
 +  int    nextSortTime;
 +  qhandle_t currentServerPreview;
 +  int    currentServerCinematic;
 +  int    motdLen;
 +  int    motdWidth;
 +  int    motdPaintX;
 +  int    motdPaintX2;
 +  int    motdOffset;
 +  int    motdTime;
 +  char  motd[MAX_STRING_CHARS];
 +} serverStatus_t;
 +
 +
 +typedef struct {
 +  char    adrstr[MAX_ADDRESSLENGTH];
 +  char    name[MAX_ADDRESSLENGTH];
 +  int      startTime;
 +  int      serverNum;
 +  qboolean  valid;
 +} pendingServer_t;
 +
 +typedef struct {
 +  int num;
 +  pendingServer_t server[MAX_SERVERSTATUSREQUESTS];
 +} pendingServerStatus_t;
 +
 +typedef struct {
 +  char address[MAX_ADDRESSLENGTH];
 +  char *lines[MAX_SERVERSTATUS_LINES][4];
 +  char text[MAX_SERVERSTATUS_TEXT];
 +  char pings[MAX_CLIENTS * 3];
 +  int numLines;
 +} serverStatusInfo_t;
 +
 +typedef struct {
 +  const char *modName;
 +  const char *modDescr;
 +} modInfo_t;
 +
 +//TA: tremulous menus
 +#define MAX_INFOPANE_TEXT     4096
 +#define MAX_INFOPANE_GRAPHICS 16
 +#define MAX_INFOPANES         128
 +
 +typedef enum
 +{
 +  INFOPANE_TOP,
 +  INFOPANE_BOTTOM,
 +  INFOPANE_LEFT,
 +  INFOPANE_RIGHT
 +} tremIPSide_t;
 +
 +typedef struct
 +{
 +  qhandle_t     graphic;
 +
 +  tremIPSide_t  side;
 +  int           offset;
 +
 +  int           width, height;
 +} tremIPGraphic_t;
 +
 +typedef struct
 +{
 +  const char      *name;
 +  char            text[ MAX_INFOPANE_TEXT ];
 +  int             align;
 +
 +  tremIPGraphic_t graphics[ MAX_INFOPANE_GRAPHICS ];
 +  int             numGraphics;
 +} tremInfoPane_t;
 +
 +typedef struct
 +{
 +  const char      *text;
 +  const char      *cmd;
 +  tremInfoPane_t  *infopane;
 +} tremMenuItem_t;
 +//TA: tremulous menus
 +
 +typedef struct {
 +  displayContextDef_t uiDC;
 +  int newHighScoreTime;
 +  int newBestTime;
 +  int showPostGameTime;
 +  qboolean newHighScore;
 +  qboolean demoAvailable;
 +  qboolean soundHighScore;
 +
 +  int characterCount;
 +  int botIndex;
 +  characterInfo characterList[MAX_HEADS];
 +
 +  int aliasCount;
 +  aliasInfo aliasList[MAX_ALIASES];
 +
 +  int teamCount;
 +  teamInfo teamList[MAX_TEAMS];
 +
 +  int numGameTypes;
 +  gameTypeInfo gameTypes[MAX_GAMETYPES];
 +
 +  int numJoinGameTypes;
 +  gameTypeInfo joinGameTypes[MAX_GAMETYPES];
 +
 +  int redBlue;
 +  int playerCount;
 +  int myTeamCount;
 +  int teamIndex;
 +  int playerRefresh;
 +  int playerIndex;
 +  int playerNumber;
 +  int myPlayerIndex;
 +  int ignoreIndex;
 +  qboolean teamLeader;
 +  char playerNames[MAX_CLIENTS][MAX_NAME_LENGTH];
 +  char rawPlayerNames[MAX_CLIENTS][MAX_NAME_LENGTH];
 +  char teamNames[MAX_CLIENTS][MAX_NAME_LENGTH];
 +  char rawTeamNames[MAX_CLIENTS][MAX_NAME_LENGTH];
 +  int clientNums[MAX_CLIENTS];
 +  int teamClientNums[MAX_CLIENTS];
 +  clientList_t ignoreList[MAX_CLIENTS];
 +
 +  int mapCount;
 +  mapInfo mapList[MAX_MAPS];
 +
 +
 +  int tierCount;
 +  tierInfo tierList[MAX_TIERS];
 +
 +  int skillIndex;
 +
 +  modInfo_t modList[MAX_MODS];
 +  int modCount;
 +  int modIndex;
 +
 +  const char *demoList[MAX_DEMOS];
 +  int demoCount;
 +  int demoIndex;
 +
 +  const char *movieList[MAX_MOVIES];
 +  int movieCount;
 +  int movieIndex;
 +  int previewMovie;
 +
 +  tremInfoPane_t  tremInfoPanes[ MAX_INFOPANES ];
 +  int             tremInfoPaneCount;
 +
 +//TA: tremulous menus
 +  tremMenuItem_t  tremTeamList[ 4 ];
 +  int             tremTeamCount;
 +  int             tremTeamIndex;
 +
 +  tremMenuItem_t  tremAlienClassList[ 3 ];
 +  int             tremAlienClassCount;
 +  int             tremAlienClassIndex;
 +
 +  tremMenuItem_t  tremHumanItemList[ 3 ];
 +  int             tremHumanItemCount;
 +  int             tremHumanItemIndex;
 +
 +  tremMenuItem_t  tremHumanArmouryBuyList[ 32 ];
 +  int             tremHumanArmouryBuyCount;
 +  int             tremHumanArmouryBuyIndex;
 +
 +  tremMenuItem_t  tremHumanArmourySellList[ 32 ];
 +  int             tremHumanArmourySellCount;
 +  int             tremHumanArmourySellIndex;
 +
 +  tremMenuItem_t  tremAlienUpgradeList[ 16 ];
 +  int             tremAlienUpgradeCount;
 +  int             tremAlienUpgradeIndex;
 +
 +  tremMenuItem_t  tremAlienBuildList[ 32 ];
 +  int             tremAlienBuildCount;
 +  int             tremAlienBuildIndex;
 +
 +  tremMenuItem_t  tremHumanBuildList[ 32 ];
 +  int             tremHumanBuildCount;
 +  int             tremHumanBuildIndex;
 +//TA: tremulous menus
 +
 +  serverStatus_t serverStatus;
 +
 +  // for the showing the status of a server
 +  char serverStatusAddress[MAX_ADDRESSLENGTH];
 +  serverStatusInfo_t serverStatusInfo;
 +  int nextServerStatusRefresh;
 +
 +  // to retrieve the status of server to find a player
 +  pendingServerStatus_t pendingServerStatus;
 +  char findPlayerName[MAX_STRING_CHARS];
 +  char foundPlayerServerAddresses[MAX_FOUNDPLAYER_SERVERS][MAX_ADDRESSLENGTH];
 +  char foundPlayerServerNames[MAX_FOUNDPLAYER_SERVERS][MAX_ADDRESSLENGTH];
 +  int currentFoundPlayerServer;
 +  int numFoundPlayerServers;
 +  int nextFindPlayerRefresh;
 +
 +  int currentCrosshair;
 +  int startPostGameTime;
 +  sfxHandle_t newHighScoreSound;
 +
 +  int        q3HeadCount;
 +  char      q3HeadNames[MAX_PLAYERMODELS][64];
 +  qhandle_t  q3HeadIcons[MAX_PLAYERMODELS];
 +  int        q3SelectedHead;
 +
 +  int effectsColor;
 +
 +  qboolean inGameLoad;
 +}  uiInfo_t;
 +
 +extern uiInfo_t uiInfo;
 +
 +
 +extern void      UI_Init( void );
 +extern void      UI_Shutdown( void );
 +extern void      UI_KeyEvent( int key );
 +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_DrawTopBottom(float x, float y, float w, float h);
 +extern void     UI_DrawSides(float x, float y, float w, float h);
 +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 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_KeyEvent( int key );
 +extern void      UI_StartDemoLoop( void );
 +extern qboolean    m_entersound;
 +void UI_LoadBestScores(const char *map, int game);
 +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_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs );
 +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_GetServerPing( int source, int n );
 +int        trap_LAN_GetPingQueueCount( void );
 +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 );
 +void      trap_LAN_LoadCachedServers( void );
 +void      trap_LAN_SaveCachedServers( void );
 +void      trap_LAN_MarkServerVisible(int source, int n, qboolean visible);
 +int        trap_LAN_ServerIsVisible( int source, int n);
 +qboolean    trap_LAN_UpdateVisiblePings( int source );
 +int        trap_LAN_AddServer(int source, const char *name, const char *addr);
 +void      trap_LAN_RemoveServer(int source, const char *addr);
 +void      trap_LAN_ResetPings(int n);
 +int        trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen );
 +int        trap_LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 );
 +int        trap_MemoryRemaining( void );
 +void      trap_R_RegisterFont(const char *pFontname, int pointSize, fontInfo_t *font);
 +void      trap_S_StopBackgroundTrack( void );
 +void      trap_S_StartBackgroundTrack( const char *intro, const char *loop);
 +int        trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits);
 +e_status    trap_CIN_StopCinematic(int handle);
 +e_status    trap_CIN_RunCinematic (int handle);
 +void      trap_CIN_DrawCinematic (int handle);
 +void      trap_CIN_SetExtents (int handle, int x, int y, int w, int h);
 +int        trap_RealTime(qtime_t *qtime);
 +void      trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset );
 +
 +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_LoadBots( void );
 +char *UI_GetBotNameByNumber( int num );
 +
 +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 );
 +
 +//
 +// 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 );
 +
 +
 +// new ui
 +
 +#define ASSET_BACKGROUND "uiBackground"
 +
 +// for tracking sp game info in Team Arena
 +typedef struct postGameInfo_s {
 +  int score;
 +  int redScore;
 +  int blueScore;
 +  int perfects;
 +  int accuracy;
 +  int impressives;
 +  int excellents;
 +  int defends;
 +  int assists;
 +  int gauntlets;
 +  int  captures;
 +  int time;
 +  int timeBonus;
 +  int shutoutBonus;
 +  int skillBonus;
 +  int baseScore;
 +} postGameInfo_t;
 +
 +
 +
 +#endif
 diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c new file mode 100644 index 0000000..07afac2 --- /dev/null +++ b/src/ui/ui_main.c @@ -0,0 +1,6496 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +/*
 +=======================================================================
 +
 +USER INTERFACE MAIN
 +
 +=======================================================================
 +*/
 +
 +// use this to get a demo build without an explicit demo build, i.e. to get the demo ui files to build
 +//#define PRE_RELEASE_TADEMO
 +
 +#include "ui_local.h"
 +
 +uiInfo_t uiInfo;
 +
 +static const char *MonthAbbrev[] = {
 +  "Jan","Feb","Mar",
 +  "Apr","May","Jun",
 +  "Jul","Aug","Sep",
 +  "Oct","Nov","Dec"
 +};
 +
 +
 +static const char *skillLevels[] = {
 +  "I Can Win",
 +  "Bring It On",
 +  "Hurt Me Plenty",
 +  "Hardcore",
 +  "Nightmare"
 +};
 +
 +static const int numSkillLevels = sizeof(skillLevels) / sizeof(const char*);
 +
 +
 +static const char *netSources[] = {
 +  "LAN",
 +  "Mplayer",
 +  "Internet",
 +  "Favorites"
 +};
 +static const int numNetSources = sizeof(netSources) / sizeof(const char*);
 +
 +static const serverFilter_t serverFilters[] = {
 +  {"All", "" },
 +  {"Quake 3 Arena", "" },
 +  {"Team Arena", "missionpack" },
 +  {"Rocket Arena", "arena" },
 +  {"Alliance", "alliance20" },
 +  {"Weapons Factory Arena", "wfa" },
 +  {"OSP", "osp" },
 +};
 +
 +static const char *teamArenaGameTypes[] = {
 +  "FFA",
 +  "TOURNAMENT",
 +  "SP",
 +  "TEAM DM",
 +  "CTF",
 +  "1FCTF",
 +  "OVERLOAD",
 +  "HARVESTER",
 +  "TEAMTOURNAMENT"
 +};
 +
 +static int const numTeamArenaGameTypes = sizeof(teamArenaGameTypes) / sizeof(const char*);
 +
 +
 +static const char *teamArenaGameNames[] = {
 +  "Free For All",
 +  "Tournament",
 +  "Single Player",
 +  "Team Deathmatch",
 +  "Capture the Flag",
 +  "One Flag CTF",
 +  "Overload",
 +  "Harvester",
 +  "Team Tournament",
 +};
 +
 +static int const numTeamArenaGameNames = sizeof(teamArenaGameNames) / sizeof(const char*);
 +
 +
 +static const int numServerFilters = sizeof(serverFilters) / sizeof(serverFilter_t);
 +
 +static const char *sortKeys[] = {
 +  "Server Name",
 +  "Map Name",
 +  "Open Player Spots",
 +  "Game Type",
 +  "Ping Time"
 +};
 +static const int numSortKeys = sizeof(sortKeys) / sizeof(const char*);
 +
 +static char* netnames[] = {
 +  "???",
 +  "UDP",
 +  "IPX",
 +  NULL
 +};
 +
 +static int gamecodetoui[] = {4,2,3,0,5,1,6};
 +
 +
 +static void UI_StartServerRefresh(qboolean full);
 +static void UI_StopServerRefresh( void );
 +static void UI_DoServerRefresh( void );
 +static void UI_FeederSelection(float feederID, int index);
 +static void UI_BuildServerDisplayList(qboolean force);
 +static void UI_BuildServerStatus(qboolean force);
 +static void UI_BuildFindPlayerList(qboolean force);
 +static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 );
 +static int UI_MapCountByGameType(qboolean singlePlayer);
 +static int UI_HeadCountByTeam( void );
 +static const char *UI_SelectedMap(int index, int *actual);
 +static const char *UI_SelectedHead(int index, int *actual);
 +static int UI_GetIndexFromSelection(int actual);
 +
 +int ProcessNewUI( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 );
 +
 +/*
 +================
 +vmMain
 +
 +This is the only way control passes into the module.
 +This must be the very first function compiled into the .qvm file
 +================
 +*/
 +vmCvar_t  ui_new;
 +vmCvar_t  ui_debug;
 +vmCvar_t  ui_initialized;
 +vmCvar_t  ui_teamArenaFirstRun;
 +
 +void _UI_Init( qboolean );
 +void _UI_Shutdown( void );
 +void _UI_KeyEvent( int key, qboolean down );
 +void _UI_MouseEvent( int dx, int dy );
 +void _UI_Refresh( int realtime );
 +qboolean _UI_IsFullscreen( void );
 +intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
 +                              int arg4, int arg5, int arg6, int arg7,
 +                              int arg8, int arg9, int arg10, int arg11  ) {
 +  switch ( command ) {
 +    case UI_GETAPIVERSION:
 +      return UI_API_VERSION;
 +
 +    case UI_INIT:
 +      _UI_Init(arg0);
 +      return 0;
 +
 +    case UI_SHUTDOWN:
 +      _UI_Shutdown();
 +      return 0;
 +
 +    case UI_KEY_EVENT:
 +      _UI_KeyEvent( arg0, arg1 );
 +      return 0;
 +
 +    case UI_MOUSE_EVENT:
 +      _UI_MouseEvent( arg0, arg1 );
 +      return 0;
 +
 +    case UI_REFRESH:
 +      _UI_Refresh( arg0 );
 +      return 0;
 +
 +    case UI_IS_FULLSCREEN:
 +      return _UI_IsFullscreen();
 +
 +    case UI_SET_ACTIVE_MENU:
 +      _UI_SetActiveMenu( arg0 );
 +      return 0;
 +
 +    case UI_CONSOLE_COMMAND:
 +      return UI_ConsoleCommand(arg0);
 +
 +    case UI_DRAW_CONNECT_SCREEN:
 +      UI_DrawConnectScreen( arg0 );
 +      return 0;
 +  }
 +
 +  return -1;
 +}
 +
 +
 +
 +void AssetCache( void ) { 
 +  uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR );
 +  uiInfo.uiDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR );
 +  uiInfo.uiDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN );
 +  uiInfo.uiDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP );
 +  uiInfo.uiDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT );
 +  uiInfo.uiDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT );
 +  uiInfo.uiDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB );
 +  uiInfo.uiDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR );
 +  uiInfo.uiDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB );
 +}
 +
 +void _UI_DrawSides(float x, float y, float w, float h, float size) {
 +  UI_AdjustFrom640( &x, &y, &w, &h );
 +  size *= uiInfo.uiDC.xscale;
 +  trap_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +  trap_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +}
 +
 +void _UI_DrawTopBottom(float x, float y, float w, float h, float size) {
 +  UI_AdjustFrom640( &x, &y, &w, &h );
 +  size *= uiInfo.uiDC.yscale;
 +  trap_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +  trap_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
 +}
 +/*
 +================
 +UI_DrawRect
 +
 +Coordinates are 640*480 virtual values
 +=================
 +*/
 +void _UI_DrawRect( float x, float y, float width, float height, float size, const float *color ) {
 +  trap_R_SetColor( color );
 +
 +  _UI_DrawTopBottom(x, y, width, height, size);
 +  _UI_DrawSides(x, y, width, height, size);
 +
 +  trap_R_SetColor( NULL );
 +}
 +
 +
 +
 +
 +int Text_Width(const char *text, float scale, int limit) {
 +  int count,len;
 +  float out;
 +  glyphInfo_t *glyph;
 +  float useScale;
 +  const char *s = text;
 +  fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
 +  if (scale <= ui_smallFont.value) {
 +    font = &uiInfo.uiDC.Assets.smallFont;
 +  } else if (scale >= ui_bigFont.value) {
 +    font = &uiInfo.uiDC.Assets.bigFont;
 +  }
 +  useScale = scale * font->glyphScale;
 +  out = 0;
 +  if (text) {
 +    len = strlen(text);
 +    if (limit > 0 && len > limit) {
 +      len = limit;
 +    }
 +    count = 0;
 +    while (s && *s && count < len) {
 +      if ( Q_IsColorString(s) ) {
 +        s += 2;
 +        continue;
 +      } else {
 +        glyph = &font->glyphs[(int)*s];
 +        out += glyph->xSkip;
 +        s++;
 +        count++;
 +      }
 +    }
 +  }
 +  return out * useScale;
 +}
 +
 +int Text_Height(const char *text, float scale, int limit) {
 +  int len, count;
 +  float max;
 +  glyphInfo_t *glyph;
 +  float useScale;
 +  const char *s = text; // bk001206 - unsigned
 +  fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
 +  if (scale <= ui_smallFont.value) {
 +    font = &uiInfo.uiDC.Assets.smallFont;
 +  } else if (scale >= ui_bigFont.value) {
 +    font = &uiInfo.uiDC.Assets.bigFont;
 +  }
 +  useScale = scale * font->glyphScale;
 +  max = 0;
 +  if (text) {
 +    len = strlen(text);
 +    if (limit > 0 && len > limit) {
 +      len = limit;
 +    }
 +    count = 0;
 +    while (s && *s && count < len) {
 +      if ( Q_IsColorString(s) ) {
 +        s += 2;
 +        continue;
 +      } else {
 +        glyph = &font->glyphs[(int)*s];
 +        if (max < glyph->height) {
 +          max = glyph->height;
 +        }
 +        s++;
 +        count++;
 +      }
 +    }
 +  }
 +  return max * useScale;
 +}
 +
 +void Text_PaintChar(float x, float y, float width, float height, float scale, float s, float t, float s2, float t2, qhandle_t hShader) {
 +  float w, h;
 +  w = width * scale;
 +  h = height * scale;
 +  UI_AdjustFrom640( &x, &y, &w, &h );
 +  trap_R_DrawStretchPic( x, y, w, h, s, t, s2, t2, hShader );
 +}
 +
 +void Text_Paint(float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style) {
 +  int len, count;
 +  vec4_t newColor;
 +  glyphInfo_t *glyph;
 +  float useScale;
 +  fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
 +  if (scale <= ui_smallFont.value) {
 +    font = &uiInfo.uiDC.Assets.smallFont;
 +  } else if (scale >= ui_bigFont.value) {
 +    font = &uiInfo.uiDC.Assets.bigFont;
 +  }
 +  useScale = scale * font->glyphScale;
 +  if (text) {
 +    const char *s = text; // bk001206 - unsigned
 +    trap_R_SetColor( color );
 +    memcpy(&newColor[0], &color[0], sizeof(vec4_t));
 +    len = strlen(text);
 +    if (limit > 0 && len > limit) {
 +      len = limit;
 +    }
 +    count = 0;
 +    while (s && *s && count < len) {
 +      glyph = &font->glyphs[(int)*s];
 +      //int yadj = Assets.textFont.glyphs[text[i]].bottom + Assets.textFont.glyphs[text[i]].top;
 +      //float yadj = scale * (Assets.textFont.glyphs[text[i]].imageHeight - Assets.textFont.glyphs[text[i]].height);
 +      if ( Q_IsColorString( s ) ) {
 +        memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
 +        newColor[3] = color[3];
 +        trap_R_SetColor( newColor );
 +        s += 2;
 +        continue;
 +      } else {
 +        float yadj = useScale * glyph->top;
 +        if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) {
 +          int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
 +          colorBlack[3] = newColor[3];
 +          trap_R_SetColor( colorBlack );
 +          Text_PaintChar(x + ofs, y - yadj + ofs,
 +                            glyph->imageWidth,
 +                            glyph->imageHeight,
 +                            useScale,
 +                            glyph->s,
 +                            glyph->t,
 +                            glyph->s2,
 +                            glyph->t2,
 +                            glyph->glyph);
 +          trap_R_SetColor( newColor );
 +          colorBlack[3] = 1.0;
 +        }
 +        else if( style == ITEM_TEXTSTYLE_NEON )
 +        {
 +          vec4_t glow, outer, inner, white;
 +
 +          glow[ 0 ] = newColor[ 0 ] * 0.5;
 +          glow[ 1 ] = newColor[ 1 ] * 0.5;
 +          glow[ 2 ] = newColor[ 2 ] * 0.5;
 +          glow[ 3 ] = newColor[ 3 ] * 0.2;
 +
 +          outer[ 0 ] = newColor[ 0 ];
 +          outer[ 1 ] = newColor[ 1 ];
 +          outer[ 2 ] = newColor[ 2 ];
 +          outer[ 3 ] = newColor[ 3 ];
 +
 +          inner[ 0 ] = newColor[ 0 ] * 1.5 > 1.0f ? 1.0f : newColor[ 0 ] * 1.5;
 +          inner[ 1 ] = newColor[ 1 ] * 1.5 > 1.0f ? 1.0f : newColor[ 1 ] * 1.5;
 +          inner[ 2 ] = newColor[ 2 ] * 1.5 > 1.0f ? 1.0f : newColor[ 2 ] * 1.5;
 +          inner[ 3 ] = newColor[ 3 ];
 +
 +          white[ 0 ] = white[ 1 ] = white[ 2 ] = white[ 3 ] = 1.0f;
 +
 +          trap_R_SetColor( glow );
 +          Text_PaintChar( x - 1.5, y - yadj - 1.5,
 +                          glyph->imageWidth + 3,
 +                          glyph->imageHeight + 3,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph );
 +
 +          trap_R_SetColor( outer );
 +          Text_PaintChar( x - 1, y - yadj - 1,
 +                          glyph->imageWidth + 2,
 +                          glyph->imageHeight + 2,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph );
 +
 +          trap_R_SetColor( inner );
 +          Text_PaintChar( x - 0.5, y - yadj - 0.5,
 +                          glyph->imageWidth + 1,
 +                          glyph->imageHeight + 1,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph );
 +
 +          trap_R_SetColor( white );
 +        }
 +
 +        Text_PaintChar(x, y - yadj,
 +                          glyph->imageWidth,
 +                          glyph->imageHeight,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph);
 +
 +        x += (glyph->xSkip * useScale) + adjust;
 +        s++;
 +        count++;
 +      }
 +    }
 +    trap_R_SetColor( NULL );
 +  }
 +}
 +
 +void Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) {
 +  int len, count;
 +  vec4_t newColor;
 +  glyphInfo_t *glyph, *glyph2;
 +  float yadj;
 +  float useScale;
 +  fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
 +  if (scale <= ui_smallFont.value) {
 +    font = &uiInfo.uiDC.Assets.smallFont;
 +  } else if (scale >= ui_bigFont.value) {
 +    font = &uiInfo.uiDC.Assets.bigFont;
 +  }
 +  useScale = scale * font->glyphScale;
 +  if (text) {
 +    const char *s = text; // bk001206 - unsigned
 +    trap_R_SetColor( color );
 +    memcpy(&newColor[0], &color[0], sizeof(vec4_t));
 +    len = strlen(text);
 +    if (limit > 0 && len > limit) {
 +      len = limit;
 +    }
 +    count = 0;
 +    glyph2 = &font->glyphs[ (int) cursor]; // bk001206 - possible signed char
 +    while (s && *s && count < len) {
 +      glyph = &font->glyphs[(int)*s];
 +      if ( Q_IsColorString( s ) ) {
 +        memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
 +        newColor[3] = color[3];
 +        trap_R_SetColor( newColor );
 +        s += 2;
 +        continue;
 +      } else {
 +        yadj = useScale * glyph->top;
 +        if (style == ITEM_TEXTSTYLE_SHADOWED || style == ITEM_TEXTSTYLE_SHADOWEDMORE) {
 +          int ofs = style == ITEM_TEXTSTYLE_SHADOWED ? 1 : 2;
 +          colorBlack[3] = newColor[3];
 +          trap_R_SetColor( colorBlack );
 +          Text_PaintChar(x + ofs, y - yadj + ofs,
 +                            glyph->imageWidth,
 +                            glyph->imageHeight,
 +                            useScale,
 +                            glyph->s,
 +                            glyph->t,
 +                            glyph->s2,
 +                            glyph->t2,
 +                            glyph->glyph);
 +          colorBlack[3] = 1.0;
 +          trap_R_SetColor( newColor );
 +        }
 +        else if( style == ITEM_TEXTSTYLE_NEON )
 +        {
 +          vec4_t glow, outer, inner, white;
 +
 +          glow[ 0 ] = newColor[ 0 ] * 0.5;
 +          glow[ 1 ] = newColor[ 1 ] * 0.5;
 +          glow[ 2 ] = newColor[ 2 ] * 0.5;
 +          glow[ 3 ] = newColor[ 3 ] * 0.2;
 +
 +          outer[ 0 ] = newColor[ 0 ];
 +          outer[ 1 ] = newColor[ 1 ];
 +          outer[ 2 ] = newColor[ 2 ];
 +          outer[ 3 ] = newColor[ 3 ];
 +
 +          inner[ 0 ] = newColor[ 0 ] * 1.5 > 1.0f ? 1.0f : newColor[ 0 ] * 1.5;
 +          inner[ 1 ] = newColor[ 1 ] * 1.5 > 1.0f ? 1.0f : newColor[ 1 ] * 1.5;
 +          inner[ 2 ] = newColor[ 2 ] * 1.5 > 1.0f ? 1.0f : newColor[ 2 ] * 1.5;
 +          inner[ 3 ] = newColor[ 3 ];
 +
 +          white[ 0 ] = white[ 1 ] = white[ 2 ] = white[ 3 ] = 1.0f;
 +
 +          trap_R_SetColor( glow );
 +          Text_PaintChar( x - 1.5, y - yadj - 1.5,
 +                          glyph->imageWidth + 3,
 +                          glyph->imageHeight + 3,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph );
 +
 +          trap_R_SetColor( outer );
 +          Text_PaintChar( x - 1, y - yadj - 1,
 +                          glyph->imageWidth + 2,
 +                          glyph->imageHeight + 2,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph );
 +
 +          trap_R_SetColor( inner );
 +          Text_PaintChar( x - 0.5, y - yadj - 0.5,
 +                          glyph->imageWidth + 1,
 +                          glyph->imageHeight + 1,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph );
 +
 +          trap_R_SetColor( white );
 +        }
 +
 +        Text_PaintChar(x, y - yadj,
 +                          glyph->imageWidth,
 +                          glyph->imageHeight,
 +                          useScale,
 +                          glyph->s,
 +                          glyph->t,
 +                          glyph->s2,
 +                          glyph->t2,
 +                          glyph->glyph);
 +
 +        // CG_DrawPic(x, y - yadj, scale * uiDC.Assets.textFont.glyphs[text[i]].imageWidth, scale * uiDC.Assets.textFont.glyphs[text[i]].imageHeight, uiDC.Assets.textFont.glyphs[text[i]].glyph);
 +        yadj = useScale * glyph2->top;
 +        if (count == cursorPos && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) {
 +          Text_PaintChar(x, y - yadj,
 +                            glyph2->imageWidth,
 +                            glyph2->imageHeight,
 +                            useScale,
 +                            glyph2->s,
 +                            glyph2->t,
 +                            glyph2->s2,
 +                            glyph2->t2,
 +                            glyph2->glyph);
 +        }
 +
 +        x += (glyph->xSkip * useScale);
 +        s++;
 +        count++;
 +      }
 +    }
 +    // need to paint cursor at end of text
 +    if (cursorPos == len && !((uiInfo.uiDC.realTime/BLINK_DIVISOR) & 1)) {
 +        yadj = useScale * glyph2->top;
 +        Text_PaintChar(x, y - yadj,
 +                          glyph2->imageWidth,
 +                          glyph2->imageHeight,
 +                          useScale,
 +                          glyph2->s,
 +                          glyph2->t,
 +                          glyph2->s2,
 +                          glyph2->t2,
 +                          glyph2->glyph);
 +
 +    }
 +
 +
 +    trap_R_SetColor( NULL );
 +  }
 +}
 +
 +
 +static void Text_Paint_Limit(float *maxX, float x, float y, float scale, vec4_t color, const char* text, float adjust, int limit) {
 +  int len, count;
 +  vec4_t newColor;
 +  glyphInfo_t *glyph;
 +  if (text) {
 +    const char *s = text; // bk001206 - unsigned
 +    float max = *maxX;
 +    float useScale;
 +    fontInfo_t *font = &uiInfo.uiDC.Assets.textFont;
 +    if (scale <= ui_smallFont.value) {
 +      font = &uiInfo.uiDC.Assets.smallFont;
 +    } else if (scale > ui_bigFont.value) {
 +      font = &uiInfo.uiDC.Assets.bigFont;
 +    }
 +    useScale = scale * font->glyphScale;
 +    trap_R_SetColor( color );
 +    len = strlen(text);
 +    if (limit > 0 && len > limit) {
 +      len = limit;
 +    }
 +    count = 0;
 +    while (s && *s && count < len) {
 +      glyph = &font->glyphs[(int)*s];
 +      if ( Q_IsColorString( s ) ) {
 +        memcpy( newColor, g_color_table[ColorIndex(*(s+1))], sizeof( newColor ) );
 +        newColor[3] = color[3];
 +        trap_R_SetColor( newColor );
 +        s += 2;
 +        continue;
 +      } else {
 +        float yadj = useScale * glyph->top;
 +        if (Text_Width(s, useScale, 1) + x > max) {
 +          *maxX = 0;
 +          break;
 +        }
 +        Text_PaintChar(x, y - yadj,
 +                       glyph->imageWidth,
 +                       glyph->imageHeight,
 +                       useScale,
 +                       glyph->s,
 +                       glyph->t,
 +                       glyph->s2,
 +                       glyph->t2,
 +                       glyph->glyph);
 +        x += (glyph->xSkip * useScale) + adjust;
 +        *maxX = x;
 +        count++;
 +        s++;
 +      }
 +    }
 +    trap_R_SetColor( NULL );
 +  }
 +
 +}
 +
 +
 +void UI_ShowPostGame(qboolean newHigh) {
 +  trap_Cvar_Set ("cg_cameraOrbit", "0");
 +  trap_Cvar_Set("cg_thirdPerson", "0");
 +  trap_Cvar_Set( "sv_killserver", "1" );
 +  uiInfo.soundHighScore = newHigh;
 +  _UI_SetActiveMenu(UIMENU_POSTGAME);
 +}
 +/*
 +=================
 +_UI_Refresh
 +=================
 +*/
 +
 +void UI_DrawCenteredPic(qhandle_t image, int w, int h) {
 +  int x, y;
 +  x = (SCREEN_WIDTH - w) / 2;
 +  y = (SCREEN_HEIGHT - h) / 2;
 +  UI_DrawHandlePic(x, y, w, h, image);
 +}
 +
 +int frameCount = 0;
 +int startTime;
 +
 +#define UI_FPS_FRAMES 4
 +void _UI_Refresh( int realtime )
 +{
 +  static int index;
 +  static int  previousTimes[UI_FPS_FRAMES];
 +
 +  //if ( !( trap_Key_GetCatcher() & KEYCATCH_UI ) ) {
 +  //  return;
 +  //}
 +
 +  uiInfo.uiDC.frameTime = realtime - uiInfo.uiDC.realTime;
 +  uiInfo.uiDC.realTime = realtime;
 +
 +  previousTimes[index % UI_FPS_FRAMES] = uiInfo.uiDC.frameTime;
 +  index++;
 +  if ( index > UI_FPS_FRAMES ) {
 +    int i, total;
 +    // average multiple frames together to smooth changes out a bit
 +    total = 0;
 +    for ( i = 0 ; i < UI_FPS_FRAMES ; i++ ) {
 +      total += previousTimes[i];
 +    }
 +    if ( !total ) {
 +      total = 1;
 +    }
 +    uiInfo.uiDC.FPS = 1000 * UI_FPS_FRAMES / total;
 +  }
 +
 +
 +
 +  UI_UpdateCvars();
 +
 +  if (Menu_Count() > 0) {
 +    // paint all the menus
 +    Menu_PaintAll();
 +    // refresh server browser list
 +    UI_DoServerRefresh();
 +    // refresh server status
 +    UI_BuildServerStatus(qfalse);
 +    // refresh find player list
 +    UI_BuildFindPlayerList(qfalse);
 +  }
 +
 +  // draw cursor
 +  UI_SetColor( NULL );
 +
 +  //TA: don't draw the cursor whilst loading
 +  if( Menu_Count( ) > 0 && !trap_Cvar_VariableValue( "ui_loading" ) )
 +    UI_DrawHandlePic( uiInfo.uiDC.cursorx-16, uiInfo.uiDC.cursory-16, 32, 32, uiInfo.uiDC.Assets.cursor);
 +
 +#ifndef NDEBUG
 +  if (uiInfo.uiDC.debug)
 +  {
 +    // cursor coordinates
 +    //FIXME
 +    //UI_DrawString( 0, 0, va("(%d,%d)",uis.cursorx,uis.cursory), UI_LEFT|UI_SMALLFONT, colorRed );
 +  }
 +#endif
 +
 +}
 +
 +/*
 +=================
 +_UI_Shutdown
 +=================
 +*/
 +void _UI_Shutdown( void ) {
 +  trap_LAN_SaveCachedServers();
 +}
 +
 +char *defaultMenu = NULL;
 +
 +char *GetMenuBuffer(const char *filename) {
 +  int len;
 +  fileHandle_t  f;
 +  static char buf[MAX_MENUFILE];
 +
 +  len = trap_FS_FOpenFile( filename, &f, FS_READ );
 +  if ( !f ) {
 +    trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) );
 +    return defaultMenu;
 +  }
 +  if ( len >= MAX_MENUFILE ) {
 +    trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i", filename, len, MAX_MENUFILE ) );
 +    trap_FS_FCloseFile( f );
 +    return defaultMenu;
 +  }
 +
 +  trap_FS_Read( buf, len, f );
 +  buf[len] = 0;
 +  trap_FS_FCloseFile( f );
 +  //COM_Compress(buf);
 +  return buf;
 +
 +}
 +
 +qboolean Asset_Parse(int handle) {
 +  pc_token_t token;
 +  const char *tempStr;
 +
 +  if (!trap_Parse_ReadToken(handle, &token))
 +    return qfalse;
 +  if (Q_stricmp(token.string, "{") != 0) {
 +    return qfalse;
 +  }
 +
 +  while ( 1 ) {
 +
 +    memset(&token, 0, sizeof(pc_token_t));
 +
 +    if (!trap_Parse_ReadToken(handle, &token))
 +      return qfalse;
 +
 +    if (Q_stricmp(token.string, "}") == 0) {
 +      return qtrue;
 +    }
 +
 +    // font
 +    if (Q_stricmp(token.string, "font") == 0) {
 +      int pointSize;
 +      if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) {
 +        return qfalse;
 +      }
 +      trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.textFont);
 +      uiInfo.uiDC.Assets.fontRegistered = qtrue;
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "smallFont") == 0) {
 +      int pointSize;
 +      if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) {
 +        return qfalse;
 +      }
 +      trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.smallFont);
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "bigFont") == 0) {
 +      int pointSize;
 +      if (!PC_String_Parse(handle, &tempStr) || !PC_Int_Parse(handle,&pointSize)) {
 +        return qfalse;
 +      }
 +      trap_R_RegisterFont(tempStr, pointSize, &uiInfo.uiDC.Assets.bigFont);
 +      continue;
 +    }
 +
 +
 +    // gradientbar
 +    if (Q_stricmp(token.string, "gradientbar") == 0) {
 +      if (!PC_String_Parse(handle, &tempStr)) {
 +        return qfalse;
 +      }
 +      uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip(tempStr);
 +      continue;
 +    }
 +
 +    // enterMenuSound
 +    if (Q_stricmp(token.string, "menuEnterSound") == 0) {
 +      if (!PC_String_Parse(handle, &tempStr)) {
 +        return qfalse;
 +      }
 +      uiInfo.uiDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse );
 +      continue;
 +    }
 +
 +    // exitMenuSound
 +    if (Q_stricmp(token.string, "menuExitSound") == 0) {
 +      if (!PC_String_Parse(handle, &tempStr)) {
 +        return qfalse;
 +      }
 +      uiInfo.uiDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse );
 +      continue;
 +    }
 +
 +    // itemFocusSound
 +    if (Q_stricmp(token.string, "itemFocusSound") == 0) {
 +      if (!PC_String_Parse(handle, &tempStr)) {
 +        return qfalse;
 +      }
 +      uiInfo.uiDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse );
 +      continue;
 +    }
 +
 +    // menuBuzzSound
 +    if (Q_stricmp(token.string, "menuBuzzSound") == 0) {
 +      if (!PC_String_Parse(handle, &tempStr)) {
 +        return qfalse;
 +      }
 +      uiInfo.uiDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse );
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "cursor") == 0) {
 +      if (!PC_String_Parse(handle, &uiInfo.uiDC.Assets.cursorStr)) {
 +        return qfalse;
 +      }
 +      uiInfo.uiDC.Assets.cursor = trap_R_RegisterShaderNoMip( uiInfo.uiDC.Assets.cursorStr);
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "fadeClamp") == 0) {
 +      if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeClamp)) {
 +        return qfalse;
 +      }
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "fadeCycle") == 0) {
 +      if (!PC_Int_Parse(handle, &uiInfo.uiDC.Assets.fadeCycle)) {
 +        return qfalse;
 +      }
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "fadeAmount") == 0) {
 +      if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.fadeAmount)) {
 +        return qfalse;
 +      }
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "shadowX") == 0) {
 +      if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowX)) {
 +        return qfalse;
 +      }
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "shadowY") == 0) {
 +      if (!PC_Float_Parse(handle, &uiInfo.uiDC.Assets.shadowY)) {
 +        return qfalse;
 +      }
 +      continue;
 +    }
 +
 +    if (Q_stricmp(token.string, "shadowColor") == 0) {
 +      if (!PC_Color_Parse(handle, &uiInfo.uiDC.Assets.shadowColor)) {
 +        return qfalse;
 +      }
 +      uiInfo.uiDC.Assets.shadowFadeClamp = uiInfo.uiDC.Assets.shadowColor[3];
 +      continue;
 +    }
 +
 +  }
 +  return qfalse;
 +}
 +
 +void Font_Report( void ) {
 +  int i;
 +  Com_Printf("Font Info\n");
 +  Com_Printf("=========\n");
 +  for ( i = 32; i < 96; i++) {
 +    Com_Printf("Glyph handle %i: %i\n", i, uiInfo.uiDC.Assets.textFont.glyphs[i].glyph);
 +  }
 +}
 +
 +void UI_Report( void ) {
 +  String_Report();
 +  //Font_Report();
 +
 +}
 +
 +void UI_ParseMenu(const char *menuFile) {
 +  int handle;
 +  pc_token_t token;
 +
 +  /*Com_Printf("Parsing menu file:%s\n", menuFile);*/
 +
 +  handle = trap_Parse_LoadSource(menuFile);
 +  if (!handle) {
 +    return;
 +  }
 +
 +  while ( 1 ) {
 +    memset(&token, 0, sizeof(pc_token_t));
 +    if (!trap_Parse_ReadToken( handle, &token )) {
 +      break;
 +    }
 +
 +    //if ( Q_stricmp( token, "{" ) ) {
 +    //  Com_Printf( "Missing { in menu file\n" );
 +    //  break;
 +    //}
 +
 +    //if ( menuCount == MAX_MENUS ) {
 +    //  Com_Printf( "Too many menus!\n" );
 +    //  break;
 +    //}
 +
 +    if ( token.string[0] == '}' ) {
 +      break;
 +    }
 +
 +    if (Q_stricmp(token.string, "assetGlobalDef") == 0) {
 +      if (Asset_Parse(handle)) {
 +        continue;
 +      } else {
 +        break;
 +      }
 +    }
 +
 +    if (Q_stricmp(token.string, "menudef") == 0) {
 +      // start a new menu
 +      Menu_New(handle);
 +    }
 +  }
 +  trap_Parse_FreeSource(handle);
 +}
 +
 +/*
 +===============
 +UI_FindInfoPaneByName
 +===============
 +*/
 +tremInfoPane_t *UI_FindInfoPaneByName( const char *name )
 +{
 +  int i;
 +
 +  for( i = 0; i < uiInfo.tremInfoPaneCount; i++ )
 +  {
 +    if( !Q_stricmp( uiInfo.tremInfoPanes[ i ].name, name ) )
 +      return &uiInfo.tremInfoPanes[ i ];
 +  }
 +
 +  //create a dummy infopane demanding the user write the infopane
 +  uiInfo.tremInfoPanes[ i ].name = String_Alloc( name );
 +  strncpy( uiInfo.tremInfoPanes[ i ].text, "Not implemented.\n\nui/infopanes.def\n", MAX_INFOPANE_TEXT );
 +  Q_strcat( uiInfo.tremInfoPanes[ i ].text, MAX_INFOPANE_TEXT, String_Alloc( name ) );
 +
 +  uiInfo.tremInfoPaneCount++;
 +
 +  return &uiInfo.tremInfoPanes[ i ];
 +}
 +
 +/*
 +===============
 +UI_LoadInfoPane
 +===============
 +*/
 +qboolean UI_LoadInfoPane( int handle )
 +{
 +  pc_token_t  token;
 +  qboolean    valid = qfalse;
 +
 +  while( 1 )
 +  {
 +    memset( &token, 0, sizeof( pc_token_t ) );
 +
 +    if( !trap_Parse_ReadToken( handle, &token ) )
 +      break;
 +
 +    if( !Q_stricmp( token.string, "name" ) )
 +    {
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].name = String_Alloc( token.string );
 +      valid = qtrue;
 +    }
 +    else if( !Q_stricmp( token.string, "graphic" ) )
 +    {
 +      int *graphic;
 +
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      graphic = &uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].numGraphics;
 +
 +      if( !Q_stricmp( token.string, "top" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].side = INFOPANE_TOP;
 +      else if( !Q_stricmp( token.string, "bottom" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].side = INFOPANE_BOTTOM;
 +      else if( !Q_stricmp( token.string, "left" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].side = INFOPANE_LEFT;
 +      else if( !Q_stricmp( token.string, "right" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].side = INFOPANE_RIGHT;
 +      else
 +        break;
 +
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      if( !Q_stricmp( token.string, "center" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].offset = -1;
 +      else
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].offset = token.intvalue;
 +
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].graphic =
 +        trap_R_RegisterShaderNoMip( token.string );
 +
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].width = token.intvalue;
 +
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].graphics[ *graphic ].height = token.intvalue;
 +
 +      //increment graphics
 +      (*graphic)++;
 +
 +      if( *graphic == MAX_INFOPANE_GRAPHICS )
 +        break;
 +    }
 +    else if( !Q_stricmp( token.string, "text" ) )
 +    {
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      Q_strcat( uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].text, MAX_INFOPANE_TEXT, token.string );
 +    }
 +    else if( !Q_stricmp( token.string, "align" ) )
 +    {
 +      memset( &token, 0, sizeof( pc_token_t ) );
 +
 +      if( !trap_Parse_ReadToken( handle, &token ) )
 +        break;
 +
 +      if( !Q_stricmp( token.string, "left" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].align = ITEM_ALIGN_LEFT;
 +      else if( !Q_stricmp( token.string, "right" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].align = ITEM_ALIGN_RIGHT;
 +      else if( !Q_stricmp( token.string, "center" ) )
 +        uiInfo.tremInfoPanes[ uiInfo.tremInfoPaneCount ].align = ITEM_ALIGN_CENTER;
 +    }
 +    else if( token.string[ 0 ] == '}' )
 +    {
 +      //reached the end, break
 +      break;
 +    }
 +    else
 +      break;
 +  }
 +
 +  if( valid )
 +  {
 +    uiInfo.tremInfoPaneCount++;
 +    return qtrue;
 +  }
 +  else
 +  {
 +    return qfalse;
 +  }
 +}
 +
 +/*
 +===============
 +UI_LoadInfoPanes
 +===============
 +*/
 +void UI_LoadInfoPanes( const char *file )
 +{
 +  pc_token_t token;
 +  int handle;
 +  int count;
 +
 +  uiInfo.tremInfoPaneCount = count = 0;
 +
 +  handle = trap_Parse_LoadSource( file );
 +
 +  if( !handle )
 +  {
 +    trap_Error( va( S_COLOR_YELLOW "infopane file not found: %s\n", file ) );
 +    return;
 +  }
 +
 +  while( 1 )
 +  {
 +    if( !trap_Parse_ReadToken( handle, &token ) )
 +      break;
 +
 +    if( token.string[ 0 ] == 0 )
 +      break;
 +
 +    if( token.string[ 0 ] == '{' )
 +    {
 +      if( UI_LoadInfoPane( handle ) )
 +        count++;
 +
 +      if( count == MAX_INFOPANES )
 +        break;
 +    }
 +  }
 +
 +  trap_Parse_FreeSource( handle );
 +}
 +
 +qboolean Load_Menu(int handle) {
 +  pc_token_t token;
 +
 +  if (!trap_Parse_ReadToken(handle, &token))
 +    return qfalse;
 +  if (token.string[0] != '{') {
 +    return qfalse;
 +  }
 +
 +  while ( 1 ) {
 +
 +    if (!trap_Parse_ReadToken(handle, &token))
 +      return qfalse;
 +
 +    if ( token.string[0] == 0 ) {
 +      return qfalse;
 +    }
 +
 +    if ( token.string[0] == '}' ) {
 +      return qtrue;
 +    }
 +
 +    UI_ParseMenu(token.string);
 +  }
 +  return qfalse;
 +}
 +
 +void UI_LoadMenus(const char *menuFile, qboolean reset) {
 +  pc_token_t token;
 +  int handle;
 +  int start;
 +
 +  start = trap_Milliseconds();
 +
 +  handle = trap_Parse_LoadSource( menuFile );
 +  if (!handle) {
 +    trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) );
 +    handle = trap_Parse_LoadSource( "ui/menus.txt" );
 +    if (!handle) {
 +      trap_Error( va( S_COLOR_RED "default menu file not found: ui/menus.txt, unable to continue!\n" ) );
 +    }
 +  }
 +
 +  ui_new.integer = 1;
 +
 +  if (reset) {
 +    Menu_Reset();
 +  }
 +
 +  while ( 1 ) {
 +    if (!trap_Parse_ReadToken(handle, &token))
 +      break;
 +    if( token.string[0] == 0 || token.string[0] == '}') {
 +      break;
 +    }
 +
 +    if ( token.string[0] == '}' ) {
 +      break;
 +    }
 +
 +    if (Q_stricmp(token.string, "loadmenu") == 0) {
 +      if (Load_Menu(handle)) {
 +        continue;
 +      } else {
 +        break;
 +      }
 +    }
 +  }
 +
 +  Com_Printf("UI menu load time = %d milli seconds\n", trap_Milliseconds() - start);
 +
 +  trap_Parse_FreeSource( handle );
 +}
 +
 +void UI_Load( void ) {
 +  char lastName[1024];
 +  menuDef_t *menu = Menu_GetFocused();
 +  char *menuSet = UI_Cvar_VariableString("ui_menuFiles");
 +  if (menu && menu->window.name) {
 +    strcpy(lastName, menu->window.name);
 +  }
 +  if (menuSet == NULL || menuSet[0] == '\0') {
 +    menuSet = "ui/menus.txt";
 +  }
 +
 +  String_Init();
 +
 +/*  UI_ParseGameInfo("gameinfo.txt");
 +  UI_LoadArenas();*/
 +
 +  UI_LoadMenus(menuSet, qtrue);
 +  Menus_CloseAll();
 +  Menus_ActivateByName(lastName);
 +
 +}
 +
 +static const char *handicapValues[] = {"None","95","90","85","80","75","70","65","60","55","50","45","40","35","30","25","20","15","10","5",NULL};
 +
 +static void UI_DrawHandicap(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  int i, h;
 +
 +  h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") );
 +  i = 20 - h / 5;
 +
 +  Text_Paint(rect->x, rect->y, scale, color, handicapValues[i], 0, 0, textStyle);
 +}
 +
 +static void UI_DrawClanName(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_teamName"), 0, 0, textStyle);
 +}
 +
 +
 +static void UI_SetCapFragLimits(qboolean uiVars) {
 +  int cap = 5;
 +  int frag = 10;
 +  if (uiVars) {
 +    trap_Cvar_Set("ui_captureLimit", va("%d", cap));
 +    trap_Cvar_Set("ui_fragLimit", va("%d", frag));
 +  } else {
 +    trap_Cvar_Set("capturelimit", va("%d", cap));
 +    trap_Cvar_Set("fraglimit", va("%d", frag));
 +  }
 +}
 +// ui_gameType assumes gametype 0 is -1 ALL and will not show
 +static void UI_DrawGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_gameType.integer].gameType, 0, 0, textStyle);
 +}
 +
 +static void UI_DrawNetGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  if (ui_netGameType.integer < 0 || ui_netGameType.integer > uiInfo.numGameTypes) {
 +    trap_Cvar_Set("ui_netGameType", "0");
 +    trap_Cvar_Set("ui_actualNetGameType", "0");
 +  }
 +  Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[ui_netGameType.integer].gameType , 0, 0, textStyle);
 +}
 +
 +static void UI_DrawJoinGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  if (ui_joinGameType.integer < 0 || ui_joinGameType.integer > uiInfo.numJoinGameTypes) {
 +    trap_Cvar_Set("ui_joinGameType", "0");
 +  }
 +  Text_Paint(rect->x, rect->y, scale, color, uiInfo.joinGameTypes[ui_joinGameType.integer].gameType , 0, 0, textStyle);
 +}
 +
 +
 +
 +static int UI_TeamIndexFromName(const char *name) {
 +  int i;
 +
 +  if (name && *name) {
 +    for (i = 0; i < uiInfo.teamCount; i++) {
 +      if (Q_stricmp(name, uiInfo.teamList[i].teamName) == 0) {
 +        return i;
 +      }
 +    }
 +  }
 +
 +  return 0;
 +
 +}
 +
 +static void UI_DrawClanLogo(rectDef_t *rect, float scale, vec4_t color) {
 +  int i;
 +  i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +  if (i >= 0 && i < uiInfo.teamCount) {
 +    trap_R_SetColor( color );
 +
 +    if (uiInfo.teamList[i].teamIcon == -1) {
 +      uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
 +      uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName));
 +      uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
 +    }
 +
 +    UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon);
 +    trap_R_SetColor(NULL);
 +  }
 +}
 +
 +static void UI_DrawClanCinematic(rectDef_t *rect, float scale, vec4_t color) {
 +  int i;
 +  i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +  if (i >= 0 && i < uiInfo.teamCount) {
 +
 +    if (uiInfo.teamList[i].cinematic >= -2) {
 +      if (uiInfo.teamList[i].cinematic == -1) {
 +        uiInfo.teamList[i].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.teamList[i].imageName), 0, 0, 0, 0, (CIN_loop | CIN_silent) );
 +      }
 +      if (uiInfo.teamList[i].cinematic >= 0) {
 +        trap_CIN_RunCinematic(uiInfo.teamList[i].cinematic);
 +        trap_CIN_SetExtents(uiInfo.teamList[i].cinematic, rect->x, rect->y, rect->w, rect->h);
 +        trap_CIN_DrawCinematic(uiInfo.teamList[i].cinematic);
 +      } else {
 +          trap_R_SetColor( color );
 +        UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal);
 +        trap_R_SetColor(NULL);
 +        uiInfo.teamList[i].cinematic = -2;
 +      }
 +    } else {
 +      trap_R_SetColor( color );
 +      UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon);
 +      trap_R_SetColor(NULL);
 +    }
 +  }
 +
 +}
 +
 +static void UI_DrawPreviewCinematic(rectDef_t *rect, float scale, vec4_t color) {
 +  if (uiInfo.previewMovie > -2) {
 +    uiInfo.previewMovie = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.movieList[uiInfo.movieIndex]), 0, 0, 0, 0, (CIN_loop | CIN_silent) );
 +    if (uiInfo.previewMovie >= 0) {
 +      trap_CIN_RunCinematic(uiInfo.previewMovie);
 +      trap_CIN_SetExtents(uiInfo.previewMovie, rect->x, rect->y, rect->w, rect->h);
 +      trap_CIN_DrawCinematic(uiInfo.previewMovie);
 +    } else {
 +      uiInfo.previewMovie = -2;
 +    }
 +  }
 +
 +}
 +
 +
 +#define GRAPHIC_BWIDTH  8.0f
 +/*
 +===============
 +UI_DrawInfoPane
 +===============
 +*/
 +static void UI_DrawInfoPane( tremInfoPane_t *pane, rectDef_t *rect, float text_x, float text_y,
 +                             float scale, vec4_t color, int textStyle )
 +{
 +  int       i;
 +  float     maxLeft = 0, maxTop = 0;
 +  float     maxRight = 0, maxBottom = 0;
 +  float     x = rect->x - text_x, y = rect->y - text_y, w, h;
 +  float     xoffset = 0, yoffset = 0;
 +  menuDef_t dummyParent;
 +  itemDef_t textItem;
 +
 +  //iterate through graphics
 +  for( i = 0; i < pane->numGraphics; i++ )
 +  {
 +    float width         = pane->graphics[ i ].width;
 +    float height        = pane->graphics[ i ].height;
 +    qhandle_t graphic = pane->graphics[ i ].graphic;
 +
 +    if( pane->graphics[ i ].side == INFOPANE_TOP || pane->graphics[ i ].side == INFOPANE_BOTTOM )
 +    {
 +      //set horizontal offset of graphic
 +      if( pane->graphics[ i ].offset < 0 )
 +        xoffset = ( rect->w / 2 ) - ( pane->graphics[ i ].width / 2 );
 +      else
 +        xoffset = pane->graphics[ i ].offset + GRAPHIC_BWIDTH;
 +    }
 +    else if( pane->graphics[ i ].side == INFOPANE_LEFT || pane->graphics[ i ].side == INFOPANE_RIGHT )
 +    {
 +      //set vertical offset of graphic
 +      if( pane->graphics[ i ].offset < 0 )
 +        yoffset = ( rect->h / 2 ) - ( pane->graphics[ i ].height / 2 );
 +      else
 +        yoffset = pane->graphics[ i ].offset + GRAPHIC_BWIDTH;
 +    }
 +
 +    if( pane->graphics[ i ].side == INFOPANE_LEFT )
 +    {
 +      //set the horizontal offset of the text
 +      if( pane->graphics[ i ].width > maxLeft )
 +        maxLeft = pane->graphics[ i ].width + GRAPHIC_BWIDTH;
 +
 +      xoffset = GRAPHIC_BWIDTH;
 +    }
 +    else if( pane->graphics[ i ].side == INFOPANE_RIGHT )
 +    {
 +      if( pane->graphics[ i ].width > maxRight )
 +        maxRight = pane->graphics[ i ].width + GRAPHIC_BWIDTH;
 +
 +      xoffset = rect->w - width - GRAPHIC_BWIDTH;
 +    }
 +    else if( pane->graphics[ i ].side == INFOPANE_TOP )
 +    {
 +      //set the vertical offset of the text
 +      if( pane->graphics[ i ].height > maxTop )
 +        maxTop = pane->graphics[ i ].height + GRAPHIC_BWIDTH;
 +
 +      yoffset = GRAPHIC_BWIDTH;
 +    }
 +    else if( pane->graphics[ i ].side == INFOPANE_BOTTOM )
 +    {
 +      if( pane->graphics[ i ].height > maxBottom )
 +        maxBottom = pane->graphics[ i ].height + GRAPHIC_BWIDTH;
 +
 +      yoffset = rect->h - height - GRAPHIC_BWIDTH;
 +    }
 +
 +    //draw the graphic
 +    UI_DrawHandlePic( x + xoffset, y + yoffset, width, height, graphic );
 +  }
 +
 +  //offset the text
 +  x = rect->x + maxLeft;
 +  y = rect->y + maxTop;
 +  w = rect->w - ( maxLeft + maxRight + 16 + ( 2 * text_x ) ); //16 to ensure text within frame
 +  h = rect->h - ( maxTop + maxBottom );
 +
 +  textItem.text = pane->text;
 +
 +  textItem.parent = &dummyParent;
 +  memcpy( textItem.window.foreColor, color, sizeof( vec4_t ) );
 +  textItem.window.flags = 0;
 +
 +  switch( pane->align )
 +  {
 +    case ITEM_ALIGN_LEFT:
 +      textItem.window.rect.x = x;
 +      break;
 +
 +    case ITEM_ALIGN_RIGHT:
 +      textItem.window.rect.x = x + w;
 +      break;
 +
 +    case ITEM_ALIGN_CENTER:
 +      textItem.window.rect.x = x + ( w / 2 );
 +      break;
 +
 +    default:
 +      textItem.window.rect.x = x;
 +      break;
 +  }
 +
 +  textItem.window.rect.y = y;
 +  textItem.window.rect.w = w;
 +  textItem.window.rect.h = h;
 +  textItem.window.borderSize = 0;
 +  textItem.textRect.x = 0;
 +  textItem.textRect.y = 0;
 +  textItem.textRect.w = 0;
 +  textItem.textRect.h = 0;
 +  textItem.textalignment = pane->align;
 +  textItem.textalignx = text_x;
 +  textItem.textaligny = text_y;
 +  textItem.textscale = scale;
 +  textItem.textStyle = textStyle;
 +
 +  textItem.enableCvar = NULL;
 +  textItem.cvarTest = NULL;
 +
 +  //hack to utilise existing autowrap code
 +  Item_Text_AutoWrapped_Paint( &textItem );
 +}
 +
 +
 +static void UI_DrawSkill(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  int i;
 +  i = trap_Cvar_VariableValue( "g_spSkill" );
 +  if (i < 1 || i > numSkillLevels) {
 +    i = 1;
 +  }
 +  Text_Paint(rect->x, rect->y, scale, color, skillLevels[i-1],0, 0, textStyle);
 +}
 +
 +
 +static void UI_DrawTeamName(rectDef_t *rect, float scale, vec4_t color, qboolean blue, int textStyle) {
 +  int i;
 +  i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam"));
 +  if (i >= 0 && i < uiInfo.teamCount) {
 +    Text_Paint(rect->x, rect->y, scale, color, va("%s: %s", (blue) ? "Blue" : "Red", uiInfo.teamList[i].teamName),0, 0, textStyle);
 +  }
 +}
 +
 +static void UI_DrawTeamMember(rectDef_t *rect, float scale, vec4_t color, qboolean blue, int num, int textStyle) {
 +  // 0 - None
 +  // 1 - Human
 +  // 2..NumCharacters - Bot
 +  int value = trap_Cvar_VariableValue(va(blue ? "ui_blueteam%i" : "ui_redteam%i", num));
 +  const char *text;
 +  if (value <= 0) {
 +    text = "Closed";
 +  } else if (value == 1) {
 +    text = "Human";
 +  } else {
 +    value -= 2;
 +
 +    if( value >= UI_GetNumBots( ) )
 +      value = 0;
 +
 +    text = UI_GetBotNameByNumber(value);
 +  }
 +  Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle);
 +}
 +
 +static void UI_DrawMapPreview(rectDef_t *rect, float scale, vec4_t color, qboolean net) {
 +  int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer;
 +  if (map < 0 || map > uiInfo.mapCount) {
 +    if (net) {
 +      ui_currentNetMap.integer = 0;
 +      trap_Cvar_Set("ui_currentNetMap", "0");
 +    } else {
 +      ui_currentMap.integer = 0;
 +      trap_Cvar_Set("ui_currentMap", "0");
 +    }
 +    map = 0;
 +  }
 +
 +  if (uiInfo.mapList[map].levelShot == -1) {
 +    uiInfo.mapList[map].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[map].imageName);
 +  }
 +
 +  if (uiInfo.mapList[map].levelShot > 0) {
 +    UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.mapList[map].levelShot);
 +  } else {
 +    UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("gfx/2d/load_screen"));
 +  }
 +}
 +
 +
 +static void UI_DrawMapTimeToBeat(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  int minutes, seconds, time;
 +  if (ui_currentMap.integer < 0 || ui_currentMap.integer > uiInfo.mapCount) {
 +    ui_currentMap.integer = 0;
 +    trap_Cvar_Set("ui_currentMap", "0");
 +  }
 +
 +  time = uiInfo.mapList[ui_currentMap.integer].timeToBeat[uiInfo.gameTypes[ui_gameType.integer].gtEnum];
 +
 +  minutes = time / 60;
 +  seconds = time % 60;
 +
 +  Text_Paint(rect->x, rect->y, scale, color, va("%02i:%02i", minutes, seconds), 0, 0, textStyle);
 +}
 +
 +
 +
 +static void UI_DrawMapCinematic(rectDef_t *rect, float scale, vec4_t color, qboolean net) {
 +
 +  int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer;
 +  if (map < 0 || map > uiInfo.mapCount) {
 +    if (net) {
 +      ui_currentNetMap.integer = 0;
 +      trap_Cvar_Set("ui_currentNetMap", "0");
 +    } else {
 +      ui_currentMap.integer = 0;
 +      trap_Cvar_Set("ui_currentMap", "0");
 +    }
 +    map = 0;
 +  }
 +
 +  if (uiInfo.mapList[map].cinematic >= -1) {
 +    if (uiInfo.mapList[map].cinematic == -1) {
 +      uiInfo.mapList[map].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[map].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) );
 +    }
 +    if (uiInfo.mapList[map].cinematic >= 0) {
 +      trap_CIN_RunCinematic(uiInfo.mapList[map].cinematic);
 +      trap_CIN_SetExtents(uiInfo.mapList[map].cinematic, rect->x, rect->y, rect->w, rect->h);
 +      trap_CIN_DrawCinematic(uiInfo.mapList[map].cinematic);
 +    } else {
 +      uiInfo.mapList[map].cinematic = -2;
 +    }
 +  } else {
 +    UI_DrawMapPreview(rect, scale, color, net);
 +  }
 +}
 +
 +
 +
 +static qboolean updateModel = qtrue;
 +static qboolean q3Model = qfalse;
 +
 +static void UI_DrawPlayerModel(rectDef_t *rect) {
 +  static playerInfo_t info;
 +  char model[MAX_QPATH];
 +  char team[256];
 +  char head[256];
 +  vec3_t  viewangles;
 +  vec3_t  moveangles;
 +
 +    if (trap_Cvar_VariableValue("ui_Q3Model")) {
 +    strcpy(model, UI_Cvar_VariableString("model"));
 +    strcpy(head, UI_Cvar_VariableString("headmodel"));
 +    if (!q3Model) {
 +      q3Model = qtrue;
 +      updateModel = qtrue;
 +    }
 +    team[0] = '\0';
 +  } else {
 +
 +    strcpy(team, UI_Cvar_VariableString("ui_teamName"));
 +    strcpy(model, UI_Cvar_VariableString("team_model"));
 +    strcpy(head, UI_Cvar_VariableString("team_headmodel"));
 +    if (q3Model) {
 +      q3Model = qfalse;
 +      updateModel = qtrue;
 +    }
 +  }
 +  if (updateModel) {
 +    memset( &info, 0, sizeof(playerInfo_t) );
 +    viewangles[YAW]   = 180 - 10;
 +    viewangles[PITCH] = 0;
 +    viewangles[ROLL]  = 0;
 +    VectorClear( moveangles );
 +    UI_PlayerInfo_SetModel( &info, model, head, team);
 +    UI_PlayerInfo_SetInfo( &info, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse );
 +//    UI_RegisterClientModelname( &info, model, head, team);
 +    updateModel = qfalse;
 +  }
 +
 +  UI_DrawPlayer( rect->x, rect->y, rect->w, rect->h, &info, uiInfo.uiDC.realTime / 2);
 +
 +}
 +
 +static void UI_DrawNetSource(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  if (ui_netSource.integer < 0 || ui_netSource.integer > numNetSources) {
 +    ui_netSource.integer = 0;
 +  }
 +  Text_Paint(rect->x, rect->y, scale, color, va("Source: %s", netSources[ui_netSource.integer]), 0, 0, textStyle);
 +}
 +
 +static void UI_DrawNetMapPreview(rectDef_t *rect, float scale, vec4_t color) {
 +
 +  if (uiInfo.serverStatus.currentServerPreview > 0) {
 +    UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.serverStatus.currentServerPreview);
 +  } else {
 +    UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, trap_R_RegisterShaderNoMip("gfx/2d/load_screen"));
 +  }
 +}
 +
 +static void UI_DrawNetMapCinematic(rectDef_t *rect, float scale, vec4_t color) {
 +  if (ui_currentNetMap.integer < 0 || ui_currentNetMap.integer > uiInfo.mapCount) {
 +    ui_currentNetMap.integer = 0;
 +    trap_Cvar_Set("ui_currentNetMap", "0");
 +  }
 +
 +  if (uiInfo.serverStatus.currentServerCinematic >= 0) {
 +    trap_CIN_RunCinematic(uiInfo.serverStatus.currentServerCinematic);
 +    trap_CIN_SetExtents(uiInfo.serverStatus.currentServerCinematic, rect->x, rect->y, rect->w, rect->h);
 +    trap_CIN_DrawCinematic(uiInfo.serverStatus.currentServerCinematic);
 +  } else {
 +    UI_DrawNetMapPreview(rect, scale, color);
 +  }
 +}
 +
 +
 +
 +static void UI_DrawNetFilter(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) {
 +    ui_serverFilterType.integer = 0;
 +  }
 +  Text_Paint(rect->x, rect->y, scale, color, va("Filter: %s", serverFilters[ui_serverFilterType.integer].description), 0, 0, textStyle);
 +}
 +
 +
 +static void UI_DrawTier(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  int i;
 +  i = trap_Cvar_VariableValue( "ui_currentTier" );
 +  if (i < 0 || i >= uiInfo.tierCount) {
 +    i = 0;
 +  }
 +  Text_Paint(rect->x, rect->y, scale, color, va("Tier: %s", uiInfo.tierList[i].tierName),0, 0, textStyle);
 +}
 +
 +static void UI_DrawTierMap(rectDef_t *rect, int index) {
 +  int i;
 +  i = trap_Cvar_VariableValue( "ui_currentTier" );
 +  if (i < 0 || i >= uiInfo.tierCount) {
 +    i = 0;
 +  }
 +
 +  if (uiInfo.tierList[i].mapHandles[index] == -1) {
 +    uiInfo.tierList[i].mapHandles[index] = trap_R_RegisterShaderNoMip(va("levelshots/%s", uiInfo.tierList[i].maps[index]));
 +  }
 +
 +  UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.tierList[i].mapHandles[index]);
 +}
 +
 +static const char *UI_EnglishMapName(const char *map) {
 +  int i;
 +  for (i = 0; i < uiInfo.mapCount; i++) {
 +    if (Q_stricmp(map, uiInfo.mapList[i].mapLoadName) == 0) {
 +      return uiInfo.mapList[i].mapName;
 +    }
 +  }
 +  return "";
 +}
 +
 +static void UI_DrawTierMapName(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  int i, j;
 +  i = trap_Cvar_VariableValue( "ui_currentTier" );
 +  if (i < 0 || i >= uiInfo.tierCount) {
 +    i = 0;
 +  }
 +  j = trap_Cvar_VariableValue("ui_currentMap");
 +  if (j < 0 || j > MAPS_PER_TIER) {
 +    j = 0;
 +  }
 +
 +  Text_Paint(rect->x, rect->y, scale, color, UI_EnglishMapName(uiInfo.tierList[i].maps[j]), 0, 0, textStyle);
 +}
 +
 +static void UI_DrawTierGameType(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  int i, j;
 +  i = trap_Cvar_VariableValue( "ui_currentTier" );
 +  if (i < 0 || i >= uiInfo.tierCount) {
 +    i = 0;
 +  }
 +  j = trap_Cvar_VariableValue("ui_currentMap");
 +  if (j < 0 || j > MAPS_PER_TIER) {
 +    j = 0;
 +  }
 +
 +  Text_Paint(rect->x, rect->y, scale, color, uiInfo.gameTypes[uiInfo.tierList[i].gameTypes[j]].gameType , 0, 0, textStyle);
 +}
 +
 +
 +static const char *UI_AIFromName(const char *name) {
 +  int j;
 +  for (j = 0; j < uiInfo.aliasCount; j++) {
 +    if (Q_stricmp(uiInfo.aliasList[j].name, name) == 0) {
 +      return uiInfo.aliasList[j].ai;
 +    }
 +  }
 +  return "James";
 +}
 +
 +static qboolean updateOpponentModel = qtrue;
 +static void UI_DrawOpponent(rectDef_t *rect) {
 +  static playerInfo_t info2;
 +  char model[MAX_QPATH];
 +  char headmodel[MAX_QPATH];
 +  char team[256];
 +  vec3_t  viewangles;
 +  vec3_t  moveangles;
 +
 +  if (updateOpponentModel) {
 +
 +    strcpy(model, UI_Cvar_VariableString("ui_opponentModel"));
 +    strcpy(headmodel, UI_Cvar_VariableString("ui_opponentModel"));
 +    team[0] = '\0';
 +
 +    memset( &info2, 0, sizeof(playerInfo_t) );
 +    viewangles[YAW]   = 180 - 10;
 +    viewangles[PITCH] = 0;
 +    viewangles[ROLL]  = 0;
 +    VectorClear( moveangles );
 +    UI_PlayerInfo_SetModel( &info2, model, headmodel, "");
 +    UI_PlayerInfo_SetInfo( &info2, LEGS_IDLE, TORSO_STAND, viewangles, vec3_origin, WP_MACHINEGUN, qfalse );
 +    UI_RegisterClientModelname( &info2, model, headmodel, team);
 +    updateOpponentModel = qfalse;
 +  }
 +
 +  UI_DrawPlayer( rect->x, rect->y, rect->w, rect->h, &info2, uiInfo.uiDC.realTime / 2);
 +
 +}
 +
 +static void UI_NextOpponent( void ) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
 +  int j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +  i++;
 +  if (i >= uiInfo.teamCount) {
 +    i = 0;
 +  }
 +  if (i == j) {
 +    i++;
 +    if ( i >= uiInfo.teamCount) {
 +      i = 0;
 +    }
 +  }
 +  trap_Cvar_Set( "ui_opponentName", uiInfo.teamList[i].teamName );
 +}
 +
 +static void UI_PriorOpponent( void ) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
 +  int j = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +  i--;
 +  if (i < 0) {
 +    i = uiInfo.teamCount - 1;
 +  }
 +  if (i == j) {
 +    i--;
 +    if ( i < 0) {
 +      i = uiInfo.teamCount - 1;
 +    }
 +  }
 +  trap_Cvar_Set( "ui_opponentName", uiInfo.teamList[i].teamName );
 +}
 +
 +static void UI_DrawPlayerLogo(rectDef_t *rect, vec3_t color) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +
 +  if (uiInfo.teamList[i].teamIcon == -1) {
 +    uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
 +    uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName));
 +    uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
 +  }
 +
 +  trap_R_SetColor( color );
 +  UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon );
 +  trap_R_SetColor( NULL );
 +}
 +
 +static void UI_DrawPlayerLogoMetal(rectDef_t *rect, vec3_t color) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +  if (uiInfo.teamList[i].teamIcon == -1) {
 +    uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
 +    uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName));
 +    uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
 +  }
 +
 +  trap_R_SetColor( color );
 +  UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal );
 +  trap_R_SetColor( NULL );
 +}
 +
 +static void UI_DrawPlayerLogoName(rectDef_t *rect, vec3_t color) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +  if (uiInfo.teamList[i].teamIcon == -1) {
 +    uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
 +    uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName));
 +    uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
 +  }
 +
 +  trap_R_SetColor( color );
 +  UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name );
 +  trap_R_SetColor( NULL );
 +}
 +
 +static void UI_DrawOpponentLogo(rectDef_t *rect, vec3_t color) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
 +  if (uiInfo.teamList[i].teamIcon == -1) {
 +    uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
 +    uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName));
 +    uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
 +  }
 +
 +  trap_R_SetColor( color );
 +  UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon );
 +  trap_R_SetColor( NULL );
 +}
 +
 +static void UI_DrawOpponentLogoMetal(rectDef_t *rect, vec3_t color) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
 +  if (uiInfo.teamList[i].teamIcon == -1) {
 +    uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
 +    uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName));
 +    uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
 +  }
 +
 +  trap_R_SetColor( color );
 +  UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal );
 +  trap_R_SetColor( NULL );
 +}
 +
 +static void UI_DrawOpponentLogoName(rectDef_t *rect, vec3_t color) {
 +  int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
 +  if (uiInfo.teamList[i].teamIcon == -1) {
 +    uiInfo.teamList[i].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[i].imageName);
 +    uiInfo.teamList[i].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[i].imageName));
 +    uiInfo.teamList[i].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[i].imageName));
 +  }
 +
 +  trap_R_SetColor( color );
 +  UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Name );
 +  trap_R_SetColor( NULL );
 +}
 +
 +static void UI_DrawAllMapsSelection(rectDef_t *rect, float scale, vec4_t color, int textStyle, qboolean net) {
 +  int map = (net) ? ui_currentNetMap.integer : ui_currentMap.integer;
 +  if (map >= 0 && map < uiInfo.mapCount) {
 +    Text_Paint(rect->x, rect->y, scale, color, uiInfo.mapList[map].mapName, 0, 0, textStyle);
 +  }
 +}
 +
 +static void UI_DrawPlayerListSelection( rectDef_t *rect, float scale,
 +  vec4_t color, int textStyle )
 +{
 +  if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount )
 +  {
 +    Text_Paint(rect->x, rect->y, scale, color,
 +      uiInfo.rawPlayerNames[ uiInfo.playerIndex ],
 +      0, 0, textStyle);
 +  }
 +}
 +
 +static void UI_DrawTeamListSelection( rectDef_t *rect, float scale,
 +  vec4_t color, int textStyle )
 +{
 +  if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount )
 +  {
 +    Text_Paint(rect->x, rect->y, scale, color,
 +      uiInfo.rawTeamNames[ uiInfo.teamIndex ],
 +      0, 0, textStyle);
 +  }
 +}
 +
 +static void UI_DrawOpponentName(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  Text_Paint(rect->x, rect->y, scale, color, UI_Cvar_VariableString("ui_opponentName"), 0, 0, textStyle);
 +}
 +
 +
 +static int UI_OwnerDrawWidth(int ownerDraw, float scale) {
 +  int i, h, value;
 +  const char *text;
 +  const char *s = NULL;
 +
 +  switch( ownerDraw )
 +  {
 +    case UI_HANDICAP:
 +        h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") );
 +        i = 20 - h / 5;
 +        s = handicapValues[i];
 +      break;
 +    case UI_CLANNAME:
 +        s = UI_Cvar_VariableString("ui_teamName");
 +      break;
 +    case UI_GAMETYPE:
 +        s = uiInfo.gameTypes[ui_gameType.integer].gameType;
 +      break;
 +    case UI_SKILL:
 +        i = trap_Cvar_VariableValue( "g_spSkill" );
 +        if (i < 1 || i > numSkillLevels) {
 +          i = 1;
 +        }
 +        s = skillLevels[i-1];
 +      break;
 +    case UI_BLUETEAMNAME:
 +        i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_blueTeam"));
 +        if (i >= 0 && i < uiInfo.teamCount) {
 +          s = va("%s: %s", "Blue", uiInfo.teamList[i].teamName);
 +        }
 +      break;
 +    case UI_REDTEAMNAME:
 +        i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_redTeam"));
 +        if (i >= 0 && i < uiInfo.teamCount) {
 +          s = va("%s: %s", "Red", uiInfo.teamList[i].teamName);
 +        }
 +      break;
 +    case UI_BLUETEAM1:
 +    case UI_BLUETEAM2:
 +    case UI_BLUETEAM3:
 +    case UI_BLUETEAM4:
 +    case UI_BLUETEAM5:
 +      value = trap_Cvar_VariableValue(va("ui_blueteam%i", ownerDraw-UI_BLUETEAM1 + 1));
 +      if (value <= 0) {
 +        text = "Closed";
 +      } else if (value == 1) {
 +        text = "Human";
 +      } else {
 +        value -= 2;
 +        if (value >= uiInfo.aliasCount) {
 +          value = 0;
 +        }
 +        text = uiInfo.aliasList[value].name;
 +      }
 +      s = va("%i. %s", ownerDraw-UI_BLUETEAM1 + 1, text);
 +      break;
 +    case UI_REDTEAM1:
 +    case UI_REDTEAM2:
 +    case UI_REDTEAM3:
 +    case UI_REDTEAM4:
 +    case UI_REDTEAM5:
 +      value = trap_Cvar_VariableValue(va("ui_redteam%i", ownerDraw-UI_REDTEAM1 + 1));
 +      if (value <= 0) {
 +        text = "Closed";
 +      } else if (value == 1) {
 +        text = "Human";
 +      } else {
 +        value -= 2;
 +        if (value >= uiInfo.aliasCount) {
 +          value = 0;
 +        }
 +        text = uiInfo.aliasList[value].name;
 +      }
 +      s = va("%i. %s", ownerDraw-UI_REDTEAM1 + 1, text);
 +      break;
 +    case UI_NETSOURCE:
 +      if (ui_netSource.integer < 0 || ui_netSource.integer > uiInfo.numJoinGameTypes) {
 +        ui_netSource.integer = 0;
 +      }
 +      s = va("Source: %s", netSources[ui_netSource.integer]);
 +      break;
 +    case UI_NETFILTER:
 +      if (ui_serverFilterType.integer < 0 || ui_serverFilterType.integer > numServerFilters) {
 +        ui_serverFilterType.integer = 0;
 +      }
 +      s = va("Filter: %s", serverFilters[ui_serverFilterType.integer].description );
 +      break;
 +    case UI_TIER:
 +      break;
 +    case UI_TIER_MAPNAME:
 +      break;
 +    case UI_TIER_GAMETYPE:
 +      break;
 +    case UI_ALLMAPS_SELECTION:
 +      break;
 +    case UI_PLAYERLIST_SELECTION:
 +      break;
 +    case UI_TEAMLIST_SELECTION:
 +      break;
 +    case UI_OPPONENT_NAME:
 +      break;
 +    case UI_KEYBINDSTATUS:
 +      if (Display_KeyBindPending()) {
 +        s = "Waiting for new key... Press ESCAPE to cancel";
 +      } else {
 +        s = "Press ENTER or CLICK to change, Press BACKSPACE to clear";
 +      }
 +      break;
 +    case UI_SERVERREFRESHDATE:
 +      s = UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer));
 +      break;
 +    default:
 +      break;
 +  }
 +
 +  if (s) {
 +    return Text_Width(s, scale, 0);
 +  }
 +  return 0;
 +}
 +
 +static void UI_DrawBotName(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  int value = uiInfo.botIndex;
 +  const char *text = "";
 +
 +  if( value >= UI_GetNumBots( ) )
 +    value = 0;
 +
 +  text = UI_GetBotNameByNumber( value );
 +
 +  Text_Paint(rect->x, rect->y, scale, color, text, 0, 0, textStyle);
 +}
 +
 +static void UI_DrawBotSkill(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  if (uiInfo.skillIndex >= 0 && uiInfo.skillIndex < numSkillLevels) {
 +    Text_Paint(rect->x, rect->y, scale, color, skillLevels[uiInfo.skillIndex], 0, 0, textStyle);
 +  }
 +}
 +
 +static void UI_DrawRedBlue(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  Text_Paint(rect->x, rect->y, scale, color, (uiInfo.redBlue == 0) ? "Red" : "Blue", 0, 0, textStyle);
 +}
 +
 +/*
 +===============
 +UI_BuildPlayerList
 +===============
 +*/
 +static void UI_BuildPlayerList( void ) {
 +  uiClientState_t cs;
 +  int   n, count, team, team2, playerTeamNumber;
 +  char  info[MAX_INFO_STRING];
 +
 +  trap_GetClientState( &cs );
 +  trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING );
 +  uiInfo.playerNumber = cs.clientNum;
 +  uiInfo.teamLeader = atoi(Info_ValueForKey(info, "tl"));
 +  team = atoi(Info_ValueForKey(info, "t"));
 +  trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
 +  count = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
 +  uiInfo.playerCount = 0;
 +  uiInfo.myTeamCount = 0;
 +  uiInfo.myPlayerIndex = 0;
 +  playerTeamNumber = 0;
 +  for( n = 0; n < count; n++ ) {
 +    trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING );
 +
 +    if (info[0]) {
 +      BG_ClientListParse( &uiInfo.ignoreList[ uiInfo.playerCount ],
 +        Info_ValueForKey( info, "ig" ) );
 +      Q_strncpyz( uiInfo.rawPlayerNames[uiInfo.playerCount],
 +        Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
 +      Q_strncpyz( uiInfo.playerNames[uiInfo.playerCount],
 +        Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
 +      Q_CleanStr( uiInfo.playerNames[uiInfo.playerCount] );
 +      uiInfo.clientNums[uiInfo.playerCount] = n;
 +      if( n == uiInfo.playerNumber )
 +        uiInfo.myPlayerIndex = uiInfo.playerCount;
 +      uiInfo.playerCount++;
 +      team2 = atoi(Info_ValueForKey(info, "t"));
 +      if (team2 == team) {
 +        Q_strncpyz( uiInfo.rawTeamNames[uiInfo.myTeamCount],
 +          Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
 +        Q_strncpyz( uiInfo.teamNames[uiInfo.myTeamCount],
 +          Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH );
 +        Q_CleanStr( uiInfo.teamNames[uiInfo.myTeamCount] );
 +        uiInfo.teamClientNums[uiInfo.myTeamCount] = n;
 +        if (uiInfo.playerNumber == n) {
 +          playerTeamNumber = uiInfo.myTeamCount;
 +        }
 +        uiInfo.myTeamCount++;
 +      }
 +    }
 +  }
 +
 +  if (!uiInfo.teamLeader) {
 +    trap_Cvar_Set("cg_selectedPlayer", va("%d", playerTeamNumber));
 +  }
 +
 +  n = trap_Cvar_VariableValue("cg_selectedPlayer");
 +  if (n < 0 || n > uiInfo.myTeamCount) {
 +    n = 0;
 +  }
 +  if (n < uiInfo.myTeamCount) {
 +    trap_Cvar_Set("cg_selectedPlayerName", uiInfo.teamNames[n]);
 +  }
 +}
 +
 +
 +static void UI_DrawSelectedPlayer(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  char name[ MAX_NAME_LENGTH ];
 +  char *s;
 +
 +  if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) {
 +    uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
 +    UI_BuildPlayerList();
 +  }
 +  if( uiInfo.teamLeader )
 +    s = UI_Cvar_VariableString("cg_selectedPlayerName");
 +  else
 +    s = UI_Cvar_VariableString("name");
 +  Q_strncpyz( name, s, sizeof( name ) );
 +  Text_Paint(rect->x, rect->y, scale, color, name, 0, 0, textStyle);
 +}
 +
 +static void UI_DrawServerRefreshDate(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  if (uiInfo.serverStatus.refreshActive) {
 +    vec4_t lowLight, newColor;
 +    lowLight[0] = 0.8 * color[0];
 +    lowLight[1] = 0.8 * color[1];
 +    lowLight[2] = 0.8 * color[2];
 +    lowLight[3] = 0.8 * color[3];
 +    LerpColor(color,lowLight,newColor,0.5+0.5*sin(uiInfo.uiDC.realTime / PULSE_DIVISOR));
 +    Text_Paint(rect->x, rect->y, scale, newColor, va("Getting info for %d servers (ESC to cancel)", trap_LAN_GetServerCount(ui_netSource.integer)), 0, 0, textStyle);
 +  } else {
 +    char buff[64];
 +    Q_strncpyz(buff, UI_Cvar_VariableString(va("ui_lastServerRefresh_%i", ui_netSource.integer)), 64);
 +    Text_Paint(rect->x, rect->y, scale, color, va("Refresh Time: %s", buff), 0, 0, textStyle);
 +  }
 +}
 +
 +static void UI_DrawServerMOTD(rectDef_t *rect, float scale, vec4_t color) {
 +  if (uiInfo.serverStatus.motdLen) {
 +    float maxX;
 +
 +    if (uiInfo.serverStatus.motdWidth == -1) {
 +      uiInfo.serverStatus.motdWidth = 0;
 +      uiInfo.serverStatus.motdPaintX = rect->x + 1;
 +      uiInfo.serverStatus.motdPaintX2 = -1;
 +    }
 +
 +    if (uiInfo.serverStatus.motdOffset > uiInfo.serverStatus.motdLen) {
 +      uiInfo.serverStatus.motdOffset = 0;
 +      uiInfo.serverStatus.motdPaintX = rect->x + 1;
 +      uiInfo.serverStatus.motdPaintX2 = -1;
 +    }
 +
 +    if (uiInfo.uiDC.realTime > uiInfo.serverStatus.motdTime) {
 +      uiInfo.serverStatus.motdTime = uiInfo.uiDC.realTime + 10;
 +      if (uiInfo.serverStatus.motdPaintX <= rect->x + 2) {
 +        if (uiInfo.serverStatus.motdOffset < uiInfo.serverStatus.motdLen) {
 +          uiInfo.serverStatus.motdPaintX += Text_Width(&uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], scale, 1) - 1;
 +          uiInfo.serverStatus.motdOffset++;
 +        } else {
 +          uiInfo.serverStatus.motdOffset = 0;
 +          if (uiInfo.serverStatus.motdPaintX2 >= 0) {
 +            uiInfo.serverStatus.motdPaintX = uiInfo.serverStatus.motdPaintX2;
 +          } else {
 +            uiInfo.serverStatus.motdPaintX = rect->x + rect->w - 2;
 +          }
 +          uiInfo.serverStatus.motdPaintX2 = -1;
 +        }
 +      } else {
 +        //serverStatus.motdPaintX--;
 +        uiInfo.serverStatus.motdPaintX -= 2;
 +        if (uiInfo.serverStatus.motdPaintX2 >= 0) {
 +          //serverStatus.motdPaintX2--;
 +          uiInfo.serverStatus.motdPaintX2 -= 2;
 +        }
 +      }
 +    }
 +
 +    maxX = rect->x + rect->w - 2;
 +    Text_Paint_Limit(&maxX, uiInfo.serverStatus.motdPaintX, rect->y + rect->h - 3, scale, color, &uiInfo.serverStatus.motd[uiInfo.serverStatus.motdOffset], 0, 0);
 +    if (uiInfo.serverStatus.motdPaintX2 >= 0) {
 +      float maxX2 = rect->x + rect->w - 2;
 +      Text_Paint_Limit(&maxX2, uiInfo.serverStatus.motdPaintX2, rect->y + rect->h - 3, scale, color, uiInfo.serverStatus.motd, 0, uiInfo.serverStatus.motdOffset);
 +    }
 +    if (uiInfo.serverStatus.motdOffset && maxX > 0) {
 +      // if we have an offset ( we are skipping the first part of the string ) and we fit the string
 +      if (uiInfo.serverStatus.motdPaintX2 == -1) {
 +            uiInfo.serverStatus.motdPaintX2 = rect->x + rect->w - 2;
 +      }
 +    } else {
 +      uiInfo.serverStatus.motdPaintX2 = -1;
 +    }
 +
 +  }
 +}
 +
 +static void UI_DrawKeyBindStatus(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +//  int ofs = 0; TTimo: unused
 +  if (Display_KeyBindPending()) {
 +    Text_Paint(rect->x, rect->y, scale, color, "Waiting for new key... Press ESCAPE to cancel", 0, 0, textStyle);
 +  } else {
 +    Text_Paint(rect->x, rect->y, scale, color, "Press ENTER or CLICK to change, Press BACKSPACE to clear", 0, 0, textStyle);
 +  }
 +}
 +
 +static void UI_DrawGLInfo(rectDef_t *rect, float scale, vec4_t color, int textStyle) {
 +  char * eptr;
 +  char buff[1024];
 +  const char *lines[64];
 +  int y, numLines, i;
 +
 +  Text_Paint(rect->x + 2, rect->y, scale, color, va("VENDOR: %s", uiInfo.uiDC.glconfig.vendor_string), 0, 30, textStyle);
 +  Text_Paint(rect->x + 2, rect->y + 15, scale, color, va("VERSION: %s: %s", uiInfo.uiDC.glconfig.version_string,uiInfo.uiDC.glconfig.renderer_string), 0, 30, textStyle);
 +  Text_Paint(rect->x + 2, rect->y + 30, scale, color, va ("PIXELFORMAT: color(%d-bits) Z(%d-bits) stencil(%d-bits)", uiInfo.uiDC.glconfig.colorBits, uiInfo.uiDC.glconfig.depthBits, uiInfo.uiDC.glconfig.stencilBits), 0, 30, textStyle);
 +
 +  // build null terminated extension strings
 +  // TTimo: https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=399
 +  // in TA this was not directly crashing, but displaying a nasty broken shader right in the middle
 +  // brought down the string size to 1024, there's not much that can be shown on the screen anyway
 +  Q_strncpyz(buff, uiInfo.uiDC.glconfig.extensions_string, 1024);
 +  eptr = buff;
 +  y = rect->y + 45;
 +  numLines = 0;
 +  while ( y < rect->y + rect->h && *eptr )
 +  {
 +    while ( *eptr && *eptr == ' ' )
 +      *eptr++ = '\0';
 +
 +    // track start of valid string
 +    if (*eptr && *eptr != ' ') {
 +      lines[numLines++] = eptr;
 +    }
 +
 +    while ( *eptr && *eptr != ' ' )
 +      eptr++;
 +  }
 +
 +  i = 0;
 +  while (i < numLines) {
 +    Text_Paint(rect->x + 2, y, scale, color, lines[i++], 0, 20, textStyle);
 +    if (i < numLines) {
 +      Text_Paint(rect->x + rect->w / 2, y, scale, color, lines[i++], 0, 20, textStyle);
 +    }
 +    y += 10;
 +    if (y > rect->y + rect->h - 11) {
 +      break;
 +    }
 +  }
 +
 +
 +}
 +
 +// FIXME: table drive
 +//
 +static void UI_OwnerDraw( float x, float y, float w, float h,
 +                          float text_x, float text_y, int ownerDraw,
 +                          int ownerDrawFlags, int align, float special,
 +                          float scale, vec4_t color, qhandle_t shader, int textStyle )
 +{
 +  rectDef_t       rect;
 +  tremInfoPane_t  *pane = NULL;
 +
 +  rect.x = x + text_x;
 +  rect.y = y + text_y;
 +  rect.w = w;
 +  rect.h = h;
 +
 +  switch( ownerDraw )
 +  {
 +    case UI_TEAMINFOPANE:
 +      if( ( pane = uiInfo.tremTeamList[ uiInfo.tremTeamIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_ACLASSINFOPANE:
 +      if( ( pane = uiInfo.tremAlienClassList[ uiInfo.tremAlienClassIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_AUPGRADEINFOPANE:
 +      if( ( pane = uiInfo.tremAlienUpgradeList[ uiInfo.tremAlienUpgradeIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_HITEMINFOPANE:
 +      if( ( pane = uiInfo.tremHumanItemList[ uiInfo.tremHumanItemIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_HBUYINFOPANE:
 +      if( ( pane = uiInfo.tremHumanArmouryBuyList[ uiInfo.tremHumanArmouryBuyIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_HSELLINFOPANE:
 +      if( ( pane = uiInfo.tremHumanArmourySellList[ uiInfo.tremHumanArmourySellIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_ABUILDINFOPANE:
 +      if( ( pane = uiInfo.tremAlienBuildList[ uiInfo.tremAlienBuildIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_HBUILDINFOPANE:
 +      if( ( pane = uiInfo.tremHumanBuildList[ uiInfo.tremHumanBuildIndex ].infopane ) )
 +        UI_DrawInfoPane( pane, &rect, text_x, text_y, scale, color, textStyle );
 +      break;
 +
 +    case UI_HANDICAP:
 +      UI_DrawHandicap(&rect, scale, color, textStyle);
 +      break;
 +    case UI_PLAYERMODEL:
 +      UI_DrawPlayerModel(&rect);
 +      break;
 +    case UI_CLANNAME:
 +      UI_DrawClanName(&rect, scale, color, textStyle);
 +      break;
 +    case UI_CLANLOGO:
 +      UI_DrawClanLogo(&rect, scale, color);
 +      break;
 +    case UI_CLANCINEMATIC:
 +      UI_DrawClanCinematic(&rect, scale, color);
 +      break;
 +    case UI_PREVIEWCINEMATIC:
 +      UI_DrawPreviewCinematic(&rect, scale, color);
 +      break;
 +    case UI_GAMETYPE:
 +      UI_DrawGameType(&rect, scale, color, textStyle);
 +      break;
 +    case UI_NETGAMETYPE:
 +      UI_DrawNetGameType(&rect, scale, color, textStyle);
 +      break;
 +    case UI_JOINGAMETYPE:
 +    UI_DrawJoinGameType(&rect, scale, color, textStyle);
 +    break;
 +    case UI_MAPPREVIEW:
 +      UI_DrawMapPreview(&rect, scale, color, qtrue);
 +      break;
 +    case UI_MAP_TIMETOBEAT:
 +      UI_DrawMapTimeToBeat(&rect, scale, color, textStyle);
 +      break;
 +    case UI_MAPCINEMATIC:
 +      UI_DrawMapCinematic(&rect, scale, color, qfalse);
 +      break;
 +    case UI_STARTMAPCINEMATIC:
 +      UI_DrawMapCinematic(&rect, scale, color, qtrue);
 +      break;
 +    case UI_SKILL:
 +      UI_DrawSkill(&rect, scale, color, textStyle);
 +      break;
 +    case UI_BLUETEAMNAME:
 +      UI_DrawTeamName(&rect, scale, color, qtrue, textStyle);
 +      break;
 +    case UI_REDTEAMNAME:
 +      UI_DrawTeamName(&rect, scale, color, qfalse, textStyle);
 +      break;
 +    case UI_BLUETEAM1:
 +    case UI_BLUETEAM2:
 +    case UI_BLUETEAM3:
 +    case UI_BLUETEAM4:
 +    case UI_BLUETEAM5:
 +      UI_DrawTeamMember(&rect, scale, color, qtrue, ownerDraw - UI_BLUETEAM1 + 1, textStyle);
 +      break;
 +    case UI_REDTEAM1:
 +    case UI_REDTEAM2:
 +    case UI_REDTEAM3:
 +    case UI_REDTEAM4:
 +    case UI_REDTEAM5:
 +      UI_DrawTeamMember(&rect, scale, color, qfalse, ownerDraw - UI_REDTEAM1 + 1, textStyle);
 +      break;
 +    case UI_NETSOURCE:
 +      UI_DrawNetSource(&rect, scale, color, textStyle);
 +      break;
 +    case UI_NETMAPPREVIEW:
 +      UI_DrawNetMapPreview(&rect, scale, color);
 +      break;
 +    case UI_NETMAPCINEMATIC:
 +      UI_DrawNetMapCinematic(&rect, scale, color);
 +      break;
 +    case UI_NETFILTER:
 +      UI_DrawNetFilter(&rect, scale, color, textStyle);
 +      break;
 +    case UI_TIER:
 +      UI_DrawTier(&rect, scale, color, textStyle);
 +      break;
 +    case UI_OPPONENTMODEL:
 +      UI_DrawOpponent(&rect);
 +      break;
 +    case UI_TIERMAP1:
 +      UI_DrawTierMap(&rect, 0);
 +      break;
 +    case UI_TIERMAP2:
 +      UI_DrawTierMap(&rect, 1);
 +      break;
 +    case UI_TIERMAP3:
 +      UI_DrawTierMap(&rect, 2);
 +      break;
 +    case UI_PLAYERLOGO:
 +      UI_DrawPlayerLogo(&rect, color);
 +      break;
 +    case UI_PLAYERLOGO_METAL:
 +      UI_DrawPlayerLogoMetal(&rect, color);
 +      break;
 +    case UI_PLAYERLOGO_NAME:
 +      UI_DrawPlayerLogoName(&rect, color);
 +      break;
 +    case UI_OPPONENTLOGO:
 +      UI_DrawOpponentLogo(&rect, color);
 +      break;
 +    case UI_OPPONENTLOGO_METAL:
 +      UI_DrawOpponentLogoMetal(&rect, color);
 +      break;
 +    case UI_OPPONENTLOGO_NAME:
 +      UI_DrawOpponentLogoName(&rect, color);
 +      break;
 +    case UI_TIER_MAPNAME:
 +      UI_DrawTierMapName(&rect, scale, color, textStyle);
 +      break;
 +    case UI_TIER_GAMETYPE:
 +      UI_DrawTierGameType(&rect, scale, color, textStyle);
 +      break;
 +    case UI_ALLMAPS_SELECTION:
 +      UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qtrue);
 +      break;
 +    case UI_MAPS_SELECTION:
 +      UI_DrawAllMapsSelection(&rect, scale, color, textStyle, qfalse);
 +      break;
 +    case UI_PLAYERLIST_SELECTION:
 +      UI_DrawPlayerListSelection(&rect, scale, color, textStyle);
 +      break;
 +    case UI_TEAMLIST_SELECTION:
 +      UI_DrawTeamListSelection(&rect, scale, color, textStyle);
 +      break;
 +    case UI_OPPONENT_NAME:
 +      UI_DrawOpponentName(&rect, scale, color, textStyle);
 +      break;
 +    case UI_BOTNAME:
 +      UI_DrawBotName(&rect, scale, color, textStyle);
 +      break;
 +    case UI_BOTSKILL:
 +      UI_DrawBotSkill(&rect, scale, color, textStyle);
 +      break;
 +    case UI_REDBLUE:
 +      UI_DrawRedBlue(&rect, scale, color, textStyle);
 +      break;
 +    case UI_SELECTEDPLAYER:
 +      UI_DrawSelectedPlayer(&rect, scale, color, textStyle);
 +      break;
 +    case UI_SERVERREFRESHDATE:
 +      UI_DrawServerRefreshDate(&rect, scale, color, textStyle);
 +      break;
 +    case UI_SERVERMOTD:
 +      UI_DrawServerMOTD(&rect, scale, color);
 +      break;
 +    case UI_GLINFO:
 +      UI_DrawGLInfo(&rect,scale, color, textStyle);
 +      break;
 +    case UI_KEYBINDSTATUS:
 +      UI_DrawKeyBindStatus(&rect,scale, color, textStyle);
 +      break;
 +    default:
 +      break;
 +  }
 +
 +}
 +
 +static qboolean UI_OwnerDrawVisible(int flags) {
 +  qboolean vis = qtrue;
 +  uiClientState_t cs;
 +  pTeam_t         team;
 +  char            info[ MAX_INFO_STRING ];
 +
 +  trap_GetClientState( &cs );
 +  trap_GetConfigString( CS_PLAYERS + cs.clientNum, info, MAX_INFO_STRING );
 +  team = atoi( Info_ValueForKey( info, "t" ) );
 +
 +
 +  while (flags) {
 +
 +    if( flags & UI_SHOW_NOTSPECTATING )
 +    {
 +      if( team == PTE_NONE )
 +        vis = qfalse;
 +
 +      flags &= ~UI_SHOW_NOTSPECTATING;
 +    }
 +
 +    if( flags & UI_SHOW_VOTEACTIVE )
 +    {
 +      if( !trap_Cvar_VariableValue( "ui_voteActive" ) )
 +        vis = qfalse;
 +
 +      flags &= ~UI_SHOW_VOTEACTIVE;
 +    }
 +
 +    if( flags & UI_SHOW_CANVOTE )
 +    {
 +      if( trap_Cvar_VariableValue( "ui_voteActive" ) )
 +        vis = qfalse;
 +
 +      flags &= ~UI_SHOW_CANVOTE;
 +    }
 +
 +    if( flags & UI_SHOW_TEAMVOTEACTIVE )
 +    {
 +      if( team == PTE_ALIENS )
 +      {
 +        if( !trap_Cvar_VariableValue( "ui_alienTeamVoteActive" ) )
 +          vis = qfalse;
 +      }
 +      else if( team == PTE_HUMANS )
 +      {
 +        if( !trap_Cvar_VariableValue( "ui_humanTeamVoteActive" ) )
 +          vis = qfalse;
 +      }
 +
 +      flags &= ~UI_SHOW_TEAMVOTEACTIVE;
 +    }
 +
 +    if( flags & UI_SHOW_CANTEAMVOTE )
 +    {
 +      if( team == PTE_ALIENS )
 +      {
 +        if( trap_Cvar_VariableValue( "ui_alienTeamVoteActive" ) )
 +          vis = qfalse;
 +      }
 +      else if( team == PTE_HUMANS )
 +      {
 +        if( trap_Cvar_VariableValue( "ui_humanTeamVoteActive" ) )
 +          vis = qfalse;
 +      }
 +
 +      flags &= ~UI_SHOW_CANTEAMVOTE;
 +    }
 +
 +    if (flags & UI_SHOW_LEADER) {
 +      // these need to show when this client can give orders to a player or a group
 +      if (!uiInfo.teamLeader) {
 +        vis = qfalse;
 +      } else {
 +        // if showing yourself
 +        if (ui_selectedPlayer.integer < uiInfo.myTeamCount && uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber) {
 +          vis = qfalse;
 +        }
 +      }
 +      flags &= ~UI_SHOW_LEADER;
 +    }
 +    if (flags & UI_SHOW_NOTLEADER) {
 +      // these need to show when this client is assigning their own status or they are NOT the leader
 +      if (uiInfo.teamLeader) {
 +        // if not showing yourself
 +        if (!(ui_selectedPlayer.integer < uiInfo.myTeamCount && uiInfo.teamClientNums[ui_selectedPlayer.integer] == uiInfo.playerNumber)) {
 +          vis = qfalse;
 +        }
 +        // these need to show when this client can give orders to a player or a group
 +      }
 +      flags &= ~UI_SHOW_NOTLEADER;
 +    }
 +    if (flags & UI_SHOW_FAVORITESERVERS) {
 +      // this assumes you only put this type of display flag on something showing in the proper context
 +      if (ui_netSource.integer != AS_FAVORITES) {
 +        vis = qfalse;
 +      }
 +      flags &= ~UI_SHOW_FAVORITESERVERS;
 +    }
 +    if (flags & UI_SHOW_NOTFAVORITESERVERS) {
 +      // this assumes you only put this type of display flag on something showing in the proper context
 +      if (ui_netSource.integer == AS_FAVORITES) {
 +        vis = qfalse;
 +      }
 +      flags &= ~UI_SHOW_NOTFAVORITESERVERS;
 +    }
 +    if (flags & UI_SHOW_NEWHIGHSCORE) {
 +      if (uiInfo.newHighScoreTime < uiInfo.uiDC.realTime) {
 +        vis = qfalse;
 +      } else {
 +        if (uiInfo.soundHighScore) {
 +          if (trap_Cvar_VariableValue("sv_killserver") == 0) {
 +            // wait on server to go down before playing sound
 +            trap_S_StartLocalSound(uiInfo.newHighScoreSound, CHAN_ANNOUNCER);
 +            uiInfo.soundHighScore = qfalse;
 +          }
 +        }
 +      }
 +      flags &= ~UI_SHOW_NEWHIGHSCORE;
 +    }
 +    if (flags & UI_SHOW_NEWBESTTIME) {
 +      if (uiInfo.newBestTime < uiInfo.uiDC.realTime) {
 +        vis = qfalse;
 +      }
 +      flags &= ~UI_SHOW_NEWBESTTIME;
 +    }
 +    if (flags & UI_SHOW_DEMOAVAILABLE) {
 +      if (!uiInfo.demoAvailable) {
 +        vis = qfalse;
 +      }
 +      flags &= ~UI_SHOW_DEMOAVAILABLE;
 +    } else {
 +      flags = 0;
 +    }
 +  }
 +  return vis;
 +}
 +
 +static qboolean UI_Handicap_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    int h;
 +    h = Com_Clamp( 5, 100, trap_Cvar_VariableValue("handicap") );
 +    if (key == K_MOUSE2) {
 +      h -= 5;
 +    } else {
 +      h += 5;
 +    }
 +    if (h > 100) {
 +      h = 5;
 +    } else if (h < 0) {
 +      h = 100;
 +    }
 +    trap_Cvar_Set( "handicap", va( "%i", h) );
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_ClanName_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    int i;
 +    i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +    if (uiInfo.teamList[i].cinematic >= 0) {
 +      trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic);
 +      uiInfo.teamList[i].cinematic = -1;
 +    }
 +    if (key == K_MOUSE2) {
 +      i--;
 +    } else {
 +      i++;
 +    }
 +    if (i >= uiInfo.teamCount) {
 +      i = 0;
 +    } else if (i < 0) {
 +      i = uiInfo.teamCount - 1;
 +    }
 +    trap_Cvar_Set( "ui_teamName", uiInfo.teamList[i].teamName);
 +  UI_HeadCountByTeam();
 +  UI_FeederSelection(FEEDER_HEADS, 0);
 +  updateModel = qtrue;
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_GameType_HandleKey(int flags, float *special, int key, qboolean resetMap) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    int oldCount = UI_MapCountByGameType(qtrue);
 +
 +    // hard coded mess here
 +    if (key == K_MOUSE2) {
 +      ui_gameType.integer--;
 +      if (ui_gameType.integer == 2) {
 +        ui_gameType.integer = 1;
 +      } else if (ui_gameType.integer < 2) {
 +        ui_gameType.integer = uiInfo.numGameTypes - 1;
 +      }
 +    } else {
 +      ui_gameType.integer++;
 +      if (ui_gameType.integer >= uiInfo.numGameTypes) {
 +        ui_gameType.integer = 1;
 +      } else if (ui_gameType.integer == 2) {
 +        ui_gameType.integer = 3;
 +      }
 +    }
 +
 +    trap_Cvar_Set("ui_Q3Model", "0");
 +
 +    trap_Cvar_Set("ui_gameType", va("%d", ui_gameType.integer));
 +    UI_SetCapFragLimits(qtrue);
 +    UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);
 +    if (resetMap && oldCount != UI_MapCountByGameType(qtrue)) {
 +      trap_Cvar_Set( "ui_currentMap", "0");
 +      Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, NULL);
 +    }
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_NetGameType_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +
 +    if (key == K_MOUSE2) {
 +      ui_netGameType.integer--;
 +    } else {
 +      ui_netGameType.integer++;
 +    }
 +
 +    if (ui_netGameType.integer < 0) {
 +      ui_netGameType.integer = uiInfo.numGameTypes - 1;
 +    } else if (ui_netGameType.integer >= uiInfo.numGameTypes) {
 +      ui_netGameType.integer = 0;
 +    }
 +
 +    trap_Cvar_Set( "ui_netGameType", va("%d", ui_netGameType.integer));
 +    trap_Cvar_Set( "ui_actualnetGameType", va("%d", uiInfo.gameTypes[ui_netGameType.integer].gtEnum));
 +    trap_Cvar_Set( "ui_currentNetMap", "0");
 +    UI_MapCountByGameType(qfalse);
 +    Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, NULL);
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_JoinGameType_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +
 +    if (key == K_MOUSE2) {
 +      ui_joinGameType.integer--;
 +    } else {
 +      ui_joinGameType.integer++;
 +    }
 +
 +    if (ui_joinGameType.integer < 0) {
 +      ui_joinGameType.integer = uiInfo.numJoinGameTypes - 1;
 +    } else if (ui_joinGameType.integer >= uiInfo.numJoinGameTypes) {
 +      ui_joinGameType.integer = 0;
 +    }
 +
 +    trap_Cvar_Set( "ui_joinGameType", va("%d", ui_joinGameType.integer));
 +    UI_BuildServerDisplayList(qtrue);
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +
 +
 +static qboolean UI_Skill_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    int i = trap_Cvar_VariableValue( "g_spSkill" );
 +
 +    if (key == K_MOUSE2) {
 +      i--;
 +    } else {
 +      i++;
 +    }
 +
 +    if (i < 1) {
 +      i = numSkillLevels;
 +    } else if (i > numSkillLevels) {
 +      i = 1;
 +    }
 +
 +    trap_Cvar_Set("g_spSkill", va("%i", i));
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_TeamName_HandleKey(int flags, float *special, int key, qboolean blue) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    int i;
 +    i = UI_TeamIndexFromName(UI_Cvar_VariableString((blue) ? "ui_blueTeam" : "ui_redTeam"));
 +
 +    if (key == K_MOUSE2) {
 +      i--;
 +    } else {
 +      i++;
 +    }
 +
 +    if (i >= uiInfo.teamCount) {
 +      i = 0;
 +    } else if (i < 0) {
 +      i = uiInfo.teamCount - 1;
 +    }
 +
 +    trap_Cvar_Set( (blue) ? "ui_blueTeam" : "ui_redTeam", uiInfo.teamList[i].teamName);
 +
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_TeamMember_HandleKey(int flags, float *special, int key, qboolean blue, int num) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    // 0 - None
 +    // 1 - Human
 +    // 2..NumCharacters - Bot
 +    char *cvar = va(blue ? "ui_blueteam%i" : "ui_redteam%i", num);
 +    int value = trap_Cvar_VariableValue(cvar);
 +
 +    if (key == K_MOUSE2) {
 +      value--;
 +    } else {
 +      value++;
 +    }
 +
 +    if( value >= UI_GetNumBots( ) + 2 )
 +      value = 0;
 +    else if( value < 0 )
 +      value = UI_GetNumBots( ) + 2 - 1;
 +
 +    trap_Cvar_Set(cvar, va("%i", value));
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_NetSource_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +
 +    if (key == K_MOUSE2) {
 +      ui_netSource.integer--;
 +      if (ui_netSource.integer == AS_MPLAYER)
 +        ui_netSource.integer--;
 +    } else {
 +      ui_netSource.integer++;
 +      if (ui_netSource.integer == AS_MPLAYER)
 +        ui_netSource.integer++;
 +    }
 +
 +    if (ui_netSource.integer >= numNetSources) {
 +      ui_netSource.integer = 0;
 +    } else if (ui_netSource.integer < 0) {
 +      ui_netSource.integer = numNetSources - 1;
 +    }
 +
 +    UI_BuildServerDisplayList(qtrue);
 +    if (ui_netSource.integer != AS_GLOBAL) {
 +      UI_StartServerRefresh(qtrue);
 +    }
 +    trap_Cvar_Set( "ui_netSource", va("%d", ui_netSource.integer));
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_NetFilter_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +
 +    if (key == K_MOUSE2) {
 +      ui_serverFilterType.integer--;
 +    } else {
 +      ui_serverFilterType.integer++;
 +    }
 +
 +    if (ui_serverFilterType.integer >= numServerFilters) {
 +      ui_serverFilterType.integer = 0;
 +    } else if (ui_serverFilterType.integer < 0) {
 +      ui_serverFilterType.integer = numServerFilters - 1;
 +    }
 +    UI_BuildServerDisplayList(qtrue);
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_OpponentName_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    if (key == K_MOUSE2) {
 +      UI_PriorOpponent();
 +    } else {
 +      UI_NextOpponent();
 +    }
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_BotName_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    int value = uiInfo.botIndex;
 +
 +    if (key == K_MOUSE2) {
 +      value--;
 +    } else {
 +      value++;
 +    }
 +
 +
 +    if( value >= UI_GetNumBots( ) + 2 )
 +      value = 0;
 +    else if( value < 0 )
 +      value = UI_GetNumBots( ) + 2 - 1;
 +
 +    uiInfo.botIndex = value;
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_BotSkill_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    if (key == K_MOUSE2) {
 +      uiInfo.skillIndex--;
 +    } else {
 +      uiInfo.skillIndex++;
 +    }
 +    if (uiInfo.skillIndex >= numSkillLevels) {
 +      uiInfo.skillIndex = 0;
 +    } else if (uiInfo.skillIndex < 0) {
 +      uiInfo.skillIndex = numSkillLevels-1;
 +    }
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +static qboolean UI_RedBlue_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    uiInfo.redBlue ^= 1;
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +
 +
 +static qboolean UI_SelectedPlayer_HandleKey(int flags, float *special, int key) {
 +  if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_ENTER || key == K_KP_ENTER) {
 +    int selected;
 +
 +    UI_BuildPlayerList();
 +    if (!uiInfo.teamLeader) {
 +      return qfalse;
 +    }
 +    selected = trap_Cvar_VariableValue("cg_selectedPlayer");
 +
 +    if (key == K_MOUSE2) {
 +      selected--;
 +    } else {
 +      selected++;
 +    }
 +
 +    if (selected > uiInfo.myTeamCount) {
 +      selected = 0;
 +    } else if (selected < 0) {
 +      selected = uiInfo.myTeamCount;
 +    }
 +
 +    if (selected == uiInfo.myTeamCount) {
 +      trap_Cvar_Set( "cg_selectedPlayerName", "Everyone");
 +    } else {
 +      trap_Cvar_Set( "cg_selectedPlayerName", uiInfo.teamNames[selected]);
 +    }
 +    trap_Cvar_Set( "cg_selectedPlayer", va("%d", selected));
 +  }
 +  return qfalse;
 +}
 +
 +
 +static qboolean UI_OwnerDrawHandleKey(int ownerDraw, int flags, float *special, int key) {
 +  switch (ownerDraw) {
 +    case UI_HANDICAP:
 +      return UI_Handicap_HandleKey(flags, special, key);
 +      break;
 +    case UI_CLANNAME:
 +      return UI_ClanName_HandleKey(flags, special, key);
 +      break;
 +    case UI_GAMETYPE:
 +      return UI_GameType_HandleKey(flags, special, key, qtrue);
 +      break;
 +    case UI_NETGAMETYPE:
 +      return UI_NetGameType_HandleKey(flags, special, key);
 +      break;
 +    case UI_JOINGAMETYPE:
 +      return UI_JoinGameType_HandleKey(flags, special, key);
 +      break;
 +    case UI_SKILL:
 +      return UI_Skill_HandleKey(flags, special, key);
 +      break;
 +    case UI_BLUETEAMNAME:
 +      return UI_TeamName_HandleKey(flags, special, key, qtrue);
 +      break;
 +    case UI_REDTEAMNAME:
 +      return UI_TeamName_HandleKey(flags, special, key, qfalse);
 +      break;
 +    case UI_BLUETEAM1:
 +    case UI_BLUETEAM2:
 +    case UI_BLUETEAM3:
 +    case UI_BLUETEAM4:
 +    case UI_BLUETEAM5:
 +      UI_TeamMember_HandleKey(flags, special, key, qtrue, ownerDraw - UI_BLUETEAM1 + 1);
 +      break;
 +    case UI_REDTEAM1:
 +    case UI_REDTEAM2:
 +    case UI_REDTEAM3:
 +    case UI_REDTEAM4:
 +    case UI_REDTEAM5:
 +      UI_TeamMember_HandleKey(flags, special, key, qfalse, ownerDraw - UI_REDTEAM1 + 1);
 +      break;
 +    case UI_NETSOURCE:
 +      UI_NetSource_HandleKey(flags, special, key);
 +      break;
 +    case UI_NETFILTER:
 +      UI_NetFilter_HandleKey(flags, special, key);
 +      break;
 +    case UI_OPPONENT_NAME:
 +      UI_OpponentName_HandleKey(flags, special, key);
 +      break;
 +    case UI_BOTNAME:
 +      return UI_BotName_HandleKey(flags, special, key);
 +      break;
 +    case UI_BOTSKILL:
 +      return UI_BotSkill_HandleKey(flags, special, key);
 +      break;
 +    case UI_REDBLUE:
 +      UI_RedBlue_HandleKey(flags, special, key);
 +      break;
 +    case UI_SELECTEDPLAYER:
 +      UI_SelectedPlayer_HandleKey(flags, special, key);
 +      break;
 +    default:
 +      break;
 +  }
 +
 +  return qfalse;
 +}
 +
 +
 +static float UI_GetValue(int ownerDraw) {
 +  return 0;
 +}
 +
 +/*
 +=================
 +UI_ServersQsortCompare
 +=================
 +*/
 +static int QDECL UI_ServersQsortCompare( const void *arg1, const void *arg2 ) {
 +  return trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey, uiInfo.serverStatus.sortDir, *(int*)arg1, *(int*)arg2);
 +}
 +
 +
 +/*
 +=================
 +UI_ServersSort
 +=================
 +*/
 +void UI_ServersSort(int column, qboolean force) {
 +
 +  if ( !force ) {
 +    if ( uiInfo.serverStatus.sortKey == column ) {
 +      return;
 +    }
 +  }
 +
 +  uiInfo.serverStatus.sortKey = column;
 +  qsort( &uiInfo.serverStatus.displayServers[0], uiInfo.serverStatus.numDisplayServers, sizeof(int), UI_ServersQsortCompare);
 +}
 +
 +
 +/*
 +===============
 +UI_GetCurrentAlienStage
 +===============
 +*/
 +static stage_t UI_GetCurrentAlienStage( void )
 +{
 +  char    buffer[ MAX_TOKEN_CHARS ];
 +  stage_t stage, dummy;
 +
 +  trap_Cvar_VariableStringBuffer( "ui_stages", buffer, sizeof( buffer ) );
 +  sscanf( buffer, "%d %d", (int *)&stage , (int *)&dummy );
 +
 +  return stage;
 +}
 +
 +/*
 +===============
 +UI_GetCurrentHumanStage
 +===============
 +*/
 +static stage_t UI_GetCurrentHumanStage( void )
 +{
 +  char    buffer[ MAX_TOKEN_CHARS ];
 +  stage_t stage, dummy;
 +
 +  trap_Cvar_VariableStringBuffer( "ui_stages", buffer, sizeof( buffer ) );
 +  sscanf( buffer, "%d %d", (int *)&dummy, (int *)&stage );
 +
 +  return stage;
 +}
 +
 +/*
 +===============
 +UI_LoadTremTeams
 +===============
 +*/
 +static void UI_LoadTremTeams( void )
 +{
 +  uiInfo.tremTeamCount = 4;
 +
 +  uiInfo.tremTeamList[ 0 ].text = String_Alloc( "Aliens" );
 +  uiInfo.tremTeamList[ 0 ].cmd = String_Alloc( "cmd team aliens\n" );
 +  uiInfo.tremTeamList[ 0 ].infopane = UI_FindInfoPaneByName( "alienteam" );
 +
 +  uiInfo.tremTeamList[ 1 ].text = String_Alloc( "Humans" );
 +  uiInfo.tremTeamList[ 1 ].cmd = String_Alloc( "cmd team humans\n" );
 +  uiInfo.tremTeamList[ 1 ].infopane = UI_FindInfoPaneByName( "humanteam" );
 +
 +  uiInfo.tremTeamList[ 2 ].text = String_Alloc( "Spectate" );
 +  uiInfo.tremTeamList[ 2 ].cmd = String_Alloc( "cmd team spectate\n" );
 +  uiInfo.tremTeamList[ 2 ].infopane = UI_FindInfoPaneByName( "spectateteam" );
 +
 +  uiInfo.tremTeamList[ 3 ].text = String_Alloc( "Auto select" );
 +  uiInfo.tremTeamList[ 3 ].cmd = String_Alloc( "cmd team auto\n" );
 +  uiInfo.tremTeamList[ 3 ].infopane = UI_FindInfoPaneByName( "autoteam" );
 +}
 +
 +/*
 +===============
 +UI_AddClass
 +===============
 +*/
 +static void UI_AddClass( pClass_t class )
 +{
 +  uiInfo.tremAlienClassList[ uiInfo.tremAlienClassCount ].text =
 +    String_Alloc( BG_FindHumanNameForClassNum( class ) );
 +  uiInfo.tremAlienClassList[ uiInfo.tremAlienClassCount ].cmd =
 +    String_Alloc( va( "cmd class %s\n", BG_FindNameForClassNum( class ) ) );
 +  uiInfo.tremAlienClassList[ uiInfo.tremAlienClassCount ].infopane =
 +    UI_FindInfoPaneByName( va( "%sclass", BG_FindNameForClassNum( class ) ) );
 +
 +  uiInfo.tremAlienClassCount++;
 +}
 +
 +/*
 +===============
 +UI_LoadTremAlienClasses
 +===============
 +*/
 +static void UI_LoadTremAlienClasses( void )
 +{
 +  uiInfo.tremAlienClassCount = 0;
 +
 +  if( BG_ClassIsAllowed( PCL_ALIEN_LEVEL0 ) )
 +    UI_AddClass( PCL_ALIEN_LEVEL0 );
 +
 +  if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0_UPG ) &&
 +      BG_FindStagesForClass( PCL_ALIEN_BUILDER0_UPG, UI_GetCurrentAlienStage( ) ) )
 +    UI_AddClass( PCL_ALIEN_BUILDER0_UPG );
 +  else if( BG_ClassIsAllowed( PCL_ALIEN_BUILDER0 ) )
 +    UI_AddClass( PCL_ALIEN_BUILDER0 );
 +}
 +
 +/*
 +===============
 +UI_AddItem
 +===============
 +*/
 +static void UI_AddItem( weapon_t weapon )
 +{
 +  uiInfo.tremHumanItemList[ uiInfo.tremHumanItemCount ].text =
 +    String_Alloc( BG_FindHumanNameForWeapon( weapon ) );
 +  uiInfo.tremHumanItemList[ uiInfo.tremHumanItemCount ].cmd =
 +    String_Alloc( va( "cmd class %s\n", BG_FindNameForWeapon( weapon ) ) );
 +  uiInfo.tremHumanItemList[ uiInfo.tremHumanItemCount ].infopane =
 +    UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForWeapon( weapon ) ) );
 +
 +  uiInfo.tremHumanItemCount++;
 +}
 +
 +/*
 +===============
 +UI_LoadTremHumanItems
 +===============
 +*/
 +static void UI_LoadTremHumanItems( void )
 +{
 +  uiInfo.tremHumanItemCount = 0;
 +
 +  if( BG_WeaponIsAllowed( WP_MACHINEGUN ) )
 +    UI_AddItem( WP_MACHINEGUN );
 +
 +  if( BG_WeaponIsAllowed( WP_HBUILD2 ) &&
 +      BG_FindStagesForWeapon( WP_HBUILD2, UI_GetCurrentHumanStage( ) ) )
 +    UI_AddItem( WP_HBUILD2 );
 +  else if( BG_WeaponIsAllowed( WP_HBUILD ) )
 +    UI_AddItem( WP_HBUILD );
 +}
 +
 +/*
 +===============
 +UI_ParseCarriageList
 +===============
 +*/
 +static void UI_ParseCarriageList( int *weapons, int *upgrades )
 +{
 +  int  i;
 +  char carriageCvar[ MAX_TOKEN_CHARS ];
 +  char *iterator;
 +  char buffer[ MAX_TOKEN_CHARS ];
 +  char *bufPointer;
 +
 +  trap_Cvar_VariableStringBuffer( "ui_carriage", carriageCvar, sizeof( carriageCvar ) );
 +  iterator = carriageCvar;
 +
 +  if( weapons )
 +    *weapons = 0;
 +
 +  if( upgrades )
 +    *upgrades = 0;
 +
 +  //simple parser to give rise to weapon/upgrade list
 +  while( iterator && iterator[ 0 ] != '$' )
 +  {
 +    bufPointer = buffer;
 +
 +    if( iterator[ 0 ] == 'W' )
 +    {
 +      iterator++;
 +
 +      while( iterator[ 0 ] != ' ' )
 +        *bufPointer++ = *iterator++;
 +
 +      *bufPointer++ = '\n';
 +
 +      i = atoi( buffer );
 +
 +      if( weapons )
 +        *weapons |= ( 1 << i );
 +    }
 +    else if( iterator[ 0 ] == 'U' )
 +    {
 +      iterator++;
 +
 +      while( iterator[ 0 ] != ' ' )
 +        *bufPointer++ = *iterator++;
 +
 +      *bufPointer++ = '\n';
 +
 +      i = atoi( buffer );
 +
 +      if( upgrades )
 +        *upgrades |= ( 1 << i );
 +    }
 +
 +    iterator++;
 +  }
 +}
 +
 +/*
 +===============
 +UI_LoadTremHumanArmouryBuys
 +===============
 +*/
 +static void UI_LoadTremHumanArmouryBuys( void )
 +{
 +  int i, j = 0;
 +  stage_t stage = UI_GetCurrentHumanStage( );
 +  int weapons, upgrades;
 +  int slots = 0;
 +
 +  UI_ParseCarriageList( &weapons, &upgrades );
 +
 +  for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
 +  {
 +    if( weapons & ( 1 << i ) )
 +      slots |= BG_FindSlotsForWeapon( i );
 +  }
 +
 +  for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
 +  {
 +    if( upgrades & ( 1 << i ) )
 +      slots |= BG_FindSlotsForUpgrade( i );
 +  }
 +
 +  uiInfo.tremHumanArmouryBuyCount = 0;
 +
 +  for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
 +  {
 +    if( BG_FindTeamForWeapon( i ) == WUT_HUMANS &&
 +        BG_FindPurchasableForWeapon( i ) &&
 +        BG_FindStagesForWeapon( i, stage ) &&
 +        BG_WeaponIsAllowed( i ) &&
 +        !( BG_FindSlotsForWeapon( i ) & slots ) &&
 +        !( weapons & ( 1 << i ) ) )
 +    {
 +      uiInfo.tremHumanArmouryBuyList[ j ].text =
 +        String_Alloc( BG_FindHumanNameForWeapon( i ) );
 +      uiInfo.tremHumanArmouryBuyList[ j ].cmd =
 +        String_Alloc( va( "cmd buy %s retrigger\n", BG_FindNameForWeapon( i ) ) );
 +      uiInfo.tremHumanArmouryBuyList[ j ].infopane =
 +        UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForWeapon( i ) ) );
 +
 +      j++;
 +
 +      uiInfo.tremHumanArmouryBuyCount++;
 +    }
 +  }
 +
 +  for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
 +  {
 +    if( BG_FindTeamForUpgrade( i ) == WUT_HUMANS &&
 +        BG_FindPurchasableForUpgrade( i ) &&
 +        BG_FindStagesForUpgrade( i, stage ) &&
 +        BG_UpgradeIsAllowed( i ) &&
 +        !( BG_FindSlotsForUpgrade( i ) & slots ) &&
 +        !( upgrades & ( 1 << i ) ) )
 +    {
 +      uiInfo.tremHumanArmouryBuyList[ j ].text =
 +        String_Alloc( BG_FindHumanNameForUpgrade( i ) );
 +      uiInfo.tremHumanArmouryBuyList[ j ].cmd =
 +        String_Alloc( va( "cmd buy %s retrigger\n", BG_FindNameForUpgrade( i ) ) );
 +      uiInfo.tremHumanArmouryBuyList[ j ].infopane =
 +        UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForUpgrade( i ) ) );
 +
 +      j++;
 +
 +      uiInfo.tremHumanArmouryBuyCount++;
 +    }
 +  }
 +}
 +
 +/*
 +===============
 +UI_LoadTremHumanArmourySells
 +===============
 +*/
 +static void UI_LoadTremHumanArmourySells( void )
 +{
 +  int weapons, upgrades;
 +  int i, j = 0;
 +
 +  uiInfo.tremHumanArmourySellCount = 0;
 +  UI_ParseCarriageList( &weapons, &upgrades );
 +
 +  for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
 +  {
 +    if( weapons & ( 1 << i ) )
 +    {
 +      uiInfo.tremHumanArmourySellList[ j ].text = String_Alloc( BG_FindHumanNameForWeapon( i ) );
 +      uiInfo.tremHumanArmourySellList[ j ].cmd =
 +        String_Alloc( va( "cmd sell %s retrigger\n", BG_FindNameForWeapon( i ) ) );
 +      uiInfo.tremHumanArmourySellList[ j ].infopane =
 +        UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForWeapon( i ) ) );
 +
 +      j++;
 +
 +      uiInfo.tremHumanArmourySellCount++;
 +    }
 +  }
 +
 +  for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
 +  {
 +    if( upgrades & ( 1 << i ) )
 +    {
 +      uiInfo.tremHumanArmourySellList[ j ].text = String_Alloc( BG_FindHumanNameForUpgrade( i ) );
 +      uiInfo.tremHumanArmourySellList[ j ].cmd =
 +        String_Alloc( va( "cmd sell %s retrigger\n", BG_FindNameForUpgrade( i ) ) );
 +      uiInfo.tremHumanArmourySellList[ j ].infopane =
 +        UI_FindInfoPaneByName( va( "%sitem", BG_FindNameForUpgrade( i ) ) );
 +
 +      j++;
 +
 +      uiInfo.tremHumanArmourySellCount++;
 +    }
 +  }
 +}
 +
 +/*
 +===============
 +UI_LoadTremAlienUpgrades
 +===============
 +*/
 +static void UI_LoadTremAlienUpgrades( void )
 +{
 +  int     i, j = 0;
 +  int     class, credits;
 +  char    ui_currentClass[ MAX_STRING_CHARS ];
 +  stage_t stage = UI_GetCurrentAlienStage( );
 +
 +  trap_Cvar_VariableStringBuffer( "ui_currentClass", ui_currentClass, MAX_STRING_CHARS );
 +  sscanf( ui_currentClass, "%d %d", &class, &credits );
 +
 +  uiInfo.tremAlienUpgradeCount = 0;
 +
 +  for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
 +  {
 +    if( BG_ClassCanEvolveFromTo( class, i, credits, 0 ) >= 0 &&
 +        BG_FindStagesForClass( i, stage ) &&
 +        BG_ClassIsAllowed( i ) )
 +    {
 +      uiInfo.tremAlienUpgradeList[ j ].text = String_Alloc( BG_FindHumanNameForClassNum( i ) );
 +      uiInfo.tremAlienUpgradeList[ j ].cmd =
 +        String_Alloc( va( "cmd class %s\n", BG_FindNameForClassNum( i ) ) );
 +      uiInfo.tremAlienUpgradeList[ j ].infopane =
 +        UI_FindInfoPaneByName( va( "%sclass", BG_FindNameForClassNum( i ) ) );
 +
 +      j++;
 +
 +      uiInfo.tremAlienUpgradeCount++;
 +    }
 +  }
 +}
 +
 +/*
 +===============
 +UI_LoadTremAlienBuilds
 +===============
 +*/
 +static void UI_LoadTremAlienBuilds( void )
 +{
 +  int     weapons;
 +  int     i, j = 0;
 +  stage_t stage;
 +
 +  UI_ParseCarriageList( &weapons, NULL );
 +  stage = UI_GetCurrentAlienStage( );
 +
 +  uiInfo.tremAlienBuildCount = 0;
 +
 +  for( i = BA_NONE +1; i < BA_NUM_BUILDABLES; i++ )
 +  {
 +    if( BG_FindTeamForBuildable( i ) == BIT_ALIENS &&
 +        BG_FindBuildWeaponForBuildable( i ) & weapons &&
 +        BG_FindStagesForBuildable( i, stage ) &&
 +        BG_BuildableIsAllowed( i ) )
 +    {
 +      uiInfo.tremAlienBuildList[ j ].text =
 +        String_Alloc( BG_FindHumanNameForBuildable( i ) );
 +      uiInfo.tremAlienBuildList[ j ].cmd =
 +        String_Alloc( va( "cmd build %s\n", BG_FindNameForBuildable( i ) ) );
 +      uiInfo.tremAlienBuildList[ j ].infopane =
 +        UI_FindInfoPaneByName( va( "%sbuild", BG_FindNameForBuildable( i ) ) );
 +
 +      j++;
 +
 +      uiInfo.tremAlienBuildCount++;
 +    }
 +  }
 +}
 +
 +/*
 +===============
 +UI_LoadTremHumanBuilds
 +===============
 +*/
 +static void UI_LoadTremHumanBuilds( void )
 +{
 +  int     weapons;
 +  int     i, j = 0;
 +  stage_t stage;
 +
 +  UI_ParseCarriageList( &weapons, NULL );
 +  stage = UI_GetCurrentHumanStage( );
 +
 +  uiInfo.tremHumanBuildCount = 0;
 +
 +  for( i = BA_NONE +1; i < BA_NUM_BUILDABLES; i++ )
 +  {
 +    if( BG_FindTeamForBuildable( i ) == BIT_HUMANS &&
 +        BG_FindBuildWeaponForBuildable( i ) & weapons &&
 +        BG_FindStagesForBuildable( i, stage ) &&
 +        BG_BuildableIsAllowed( i ) )
 +    {
 +      uiInfo.tremHumanBuildList[ j ].text =
 +        String_Alloc( BG_FindHumanNameForBuildable( i ) );
 +      uiInfo.tremHumanBuildList[ j ].cmd =
 +        String_Alloc( va( "cmd build %s\n", BG_FindNameForBuildable( i ) ) );
 +      uiInfo.tremHumanBuildList[ j ].infopane =
 +        UI_FindInfoPaneByName( va( "%sbuild", BG_FindNameForBuildable( i ) ) );
 +
 +      j++;
 +
 +      uiInfo.tremHumanBuildCount++;
 +    }
 +  }
 +}
 +
 +/*
 +===============
 +UI_LoadMods
 +===============
 +*/
 +static void UI_LoadMods( void ) {
 +  int   numdirs;
 +  char  dirlist[2048];
 +  char  *dirptr;
 +  char  *descptr;
 +  int   i;
 +  int   dirlen;
 +
 +  uiInfo.modCount = 0;
 +  numdirs = trap_FS_GetFileList( "$modlist", "", dirlist, sizeof(dirlist) );
 +  dirptr  = dirlist;
 +  for( i = 0; i < numdirs; i++ ) {
 +    dirlen = strlen( dirptr ) + 1;
 +    descptr = dirptr + dirlen;
 +    uiInfo.modList[uiInfo.modCount].modName = String_Alloc(dirptr);
 +    uiInfo.modList[uiInfo.modCount].modDescr = String_Alloc(descptr);
 +    dirptr += dirlen + strlen(descptr) + 1;
 +    uiInfo.modCount++;
 +    if (uiInfo.modCount >= MAX_MODS) {
 +      break;
 +    }
 +  }
 +
 +}
 +
 +
 +/*
 +===============
 +UI_LoadMovies
 +===============
 +*/
 +static void UI_LoadMovies( void ) {
 +  char  movielist[4096];
 +  char  *moviename;
 +  int   i, len;
 +
 +  uiInfo.movieCount = trap_FS_GetFileList( "video", "roq", movielist, 4096 );
 +
 +  if (uiInfo.movieCount) {
 +    if (uiInfo.movieCount > MAX_MOVIES) {
 +      uiInfo.movieCount = MAX_MOVIES;
 +    }
 +    moviename = movielist;
 +    for ( i = 0; i < uiInfo.movieCount; i++ ) {
 +      len = strlen( moviename );
 +      if (!Q_stricmp(moviename +  len - 4,".roq")) {
 +        moviename[len-4] = '\0';
 +      }
 +      Q_strupr(moviename);
 +      uiInfo.movieList[i] = String_Alloc(moviename);
 +      moviename += len + 1;
 +    }
 +  }
 +
 +}
 +
 +
 +
 +/*
 +===============
 +UI_LoadDemos
 +===============
 +*/
 +static void UI_LoadDemos( void ) {
 +  char  demolist[4096];
 +  char demoExt[32];
 +  char  *demoname;
 +  int   i, len;
 +
 +  Com_sprintf(demoExt, sizeof(demoExt), "dm_%d", (int)trap_Cvar_VariableValue("protocol"));
 +
 +  uiInfo.demoCount = trap_FS_GetFileList( "demos", demoExt, demolist, 4096 );
 +
 +  Com_sprintf(demoExt, sizeof(demoExt), ".dm_%d", (int)trap_Cvar_VariableValue("protocol"));
 +
 +  if (uiInfo.demoCount) {
 +    if (uiInfo.demoCount > MAX_DEMOS) {
 +      uiInfo.demoCount = MAX_DEMOS;
 +    }
 +    demoname = demolist;
 +    for ( i = 0; i < uiInfo.demoCount; i++ ) {
 +      len = strlen( demoname );
 +      if (!Q_stricmp(demoname +  len - strlen(demoExt), demoExt)) {
 +        demoname[len-strlen(demoExt)] = '\0';
 +      }
 +      Q_strupr(demoname);
 +      uiInfo.demoList[i] = String_Alloc(demoname);
 +      demoname += len + 1;
 +    }
 +  }
 +
 +}
 +
 +
 +static qboolean UI_SetNextMap(int actual, int index) {
 +  int i;
 +  for (i = actual + 1; i < uiInfo.mapCount; i++) {
 +    if (uiInfo.mapList[i].active) {
 +      Menu_SetFeederSelection(NULL, FEEDER_MAPS, index + 1, "skirmish");
 +      return qtrue;
 +    }
 +  }
 +  return qfalse;
 +}
 +
 +
 +static void UI_StartSkirmish(qboolean next) {
 +  int i, k, g, delay, temp;
 +  float skill;
 +  char buff[MAX_STRING_CHARS];
 +
 +  if (next) {
 +    int actual;
 +    int index = trap_Cvar_VariableValue("ui_mapIndex");
 +    UI_MapCountByGameType(qtrue);
 +    UI_SelectedMap(index, &actual);
 +    if (UI_SetNextMap(actual, index)) {
 +    } else {
 +      UI_GameType_HandleKey(0, NULL, K_MOUSE1, qfalse);
 +      UI_MapCountByGameType(qtrue);
 +      Menu_SetFeederSelection(NULL, FEEDER_MAPS, 0, "skirmish");
 +    }
 +  }
 +
 +  g = uiInfo.gameTypes[ui_gameType.integer].gtEnum;
 +  trap_Cvar_SetValue( "g_gametype", g );
 +  trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName) );
 +  skill = trap_Cvar_VariableValue( "g_spSkill" );
 +  trap_Cvar_Set("ui_scoreMap", uiInfo.mapList[ui_currentMap.integer].mapName);
 +
 +  k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName"));
 +
 +  trap_Cvar_Set("ui_singlePlayerActive", "1");
 +
 +  // set up sp overrides, will be replaced on postgame
 +  temp = trap_Cvar_VariableValue( "capturelimit" );
 +  trap_Cvar_Set("ui_saveCaptureLimit", va("%i", temp));
 +  temp = trap_Cvar_VariableValue( "fraglimit" );
 +  trap_Cvar_Set("ui_saveFragLimit", va("%i", temp));
 +
 +  UI_SetCapFragLimits(qfalse);
 +
 +  temp = trap_Cvar_VariableValue( "cg_drawTimer" );
 +  trap_Cvar_Set("ui_drawTimer", va("%i", temp));
 +  temp = trap_Cvar_VariableValue( "g_doWarmup" );
 +  trap_Cvar_Set("ui_doWarmup", va("%i", temp));
 +  temp = trap_Cvar_VariableValue( "g_friendlyFire" );
 +  trap_Cvar_Set("ui_friendlyFire", va("%i", temp));
 +  temp = trap_Cvar_VariableValue( "sv_maxClients" );
 +  trap_Cvar_Set("ui_maxClients", va("%i", temp));
 +  temp = trap_Cvar_VariableValue( "g_warmup" );
 +  trap_Cvar_Set("ui_Warmup", va("%i", temp));
 +  temp = trap_Cvar_VariableValue( "sv_pure" );
 +  trap_Cvar_Set("ui_pure", va("%i", temp));
 +
 +  trap_Cvar_Set("cg_cameraOrbit", "0");
 +  trap_Cvar_Set("cg_thirdPerson", "0");
 +  trap_Cvar_Set("cg_drawTimer", "1");
 +  trap_Cvar_Set("g_doWarmup", "1");
 +  trap_Cvar_Set("g_warmup", "15");
 +  trap_Cvar_Set("sv_pure", "0");
 +  trap_Cvar_Set("g_friendlyFire", "0");
 +  trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName"));
 +  trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName"));
 +
 +  if (trap_Cvar_VariableValue("ui_recordSPDemo")) {
 +    Com_sprintf(buff, MAX_STRING_CHARS, "%s_%i", uiInfo.mapList[ui_currentMap.integer].mapLoadName, g);
 +    trap_Cvar_Set("ui_recordSPDemoName", buff);
 +  }
 +
 +  delay = 500;
 +
 +  {
 +    temp = uiInfo.mapList[ui_currentMap.integer].teamMembers * 2;
 +    trap_Cvar_Set("sv_maxClients", va("%d", temp));
 +    for (i =0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers; i++) {
 +      Com_sprintf( buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill, "", delay, uiInfo.teamList[k].teamMembers[i]);
 +      trap_Cmd_ExecuteText( EXEC_APPEND, buff );
 +      delay += 500;
 +    }
 +    k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +    for (i =0; i < uiInfo.mapList[ui_currentMap.integer].teamMembers-1; i++) {
 +      Com_sprintf( buff, sizeof(buff), "addbot %s %f %s %i %s\n", UI_AIFromName(uiInfo.teamList[k].teamMembers[i]), skill, "", delay, uiInfo.teamList[k].teamMembers[i]);
 +      trap_Cmd_ExecuteText( EXEC_APPEND, buff );
 +      delay += 500;
 +    }
 +  }
 +}
 +
 +static void UI_Update(const char *name) {
 +  int val = trap_Cvar_VariableValue(name);
 +
 +  if (Q_stricmp(name, "ui_SetName") == 0) {
 +    trap_Cvar_Set( "name", UI_Cvar_VariableString("ui_Name"));
 +  } else if (Q_stricmp(name, "ui_setRate") == 0) {
 +    float rate = trap_Cvar_VariableValue("rate");
 +    if (rate >= 5000) {
 +      trap_Cvar_Set("cl_maxpackets", "30");
 +      trap_Cvar_Set("cl_packetdup", "1");
 +    } else if (rate >= 4000) {
 +      trap_Cvar_Set("cl_maxpackets", "15");
 +      trap_Cvar_Set("cl_packetdup", "2");   // favor less prediction errors when there's packet loss
 +    } else {
 +      trap_Cvar_Set("cl_maxpackets", "15");
 +      trap_Cvar_Set("cl_packetdup", "1");   // favor lower bandwidth
 +    }
 +  } else if (Q_stricmp(name, "ui_GetName") == 0) {
 +    trap_Cvar_Set( "ui_Name", UI_Cvar_VariableString("name"));
 +  } else if (Q_stricmp(name, "r_colorbits") == 0) {
 +    switch (val) {
 +      case 0:
 +        trap_Cvar_SetValue( "r_depthbits", 0 );
 +        trap_Cvar_SetValue( "r_stencilbits", 0 );
 +      break;
 +      case 16:
 +        trap_Cvar_SetValue( "r_depthbits", 16 );
 +        trap_Cvar_SetValue( "r_stencilbits", 0 );
 +      break;
 +      case 32:
 +        trap_Cvar_SetValue( "r_depthbits", 24 );
 +      break;
 +    }
 +  } else if (Q_stricmp(name, "r_lodbias") == 0) {
 +    switch (val) {
 +      case 0:
 +        trap_Cvar_SetValue( "r_subdivisions", 4 );
 +      break;
 +      case 1:
 +        trap_Cvar_SetValue( "r_subdivisions", 12 );
 +      break;
 +      case 2:
 +        trap_Cvar_SetValue( "r_subdivisions", 20 );
 +      break;
 +    }
 +  } else if (Q_stricmp(name, "ui_glCustom") == 0) {
 +    switch (val) {
 +      case 0: // high quality
 +        trap_Cvar_SetValue( "r_fullScreen", 1 );
 +        trap_Cvar_SetValue( "r_subdivisions", 4 );
 +        trap_Cvar_SetValue( "r_vertexlight", 0 );
 +        trap_Cvar_SetValue( "r_lodbias", 0 );
 +        trap_Cvar_SetValue( "r_colorbits", 32 );
 +        trap_Cvar_SetValue( "r_depthbits", 24 );
 +        trap_Cvar_SetValue( "r_picmip", 0 );
 +        trap_Cvar_SetValue( "r_mode", 4 );
 +        trap_Cvar_SetValue( "r_texturebits", 32 );
 +        trap_Cvar_SetValue( "r_fastSky", 0 );
 +        trap_Cvar_SetValue( "r_inGameVideo", 1 );
 +        trap_Cvar_SetValue( "cg_shadows", 1 );
 +        trap_Cvar_SetValue( "cg_brassTime", 2500 );
 +        trap_Cvar_SetValue( "cg_bounceParticles", 1 );
 +        trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
 +      break;
 +      case 1: // normal
 +        trap_Cvar_SetValue( "r_fullScreen", 1 );
 +        trap_Cvar_SetValue( "r_subdivisions", 12 );
 +        trap_Cvar_SetValue( "r_vertexlight", 0 );
 +        trap_Cvar_SetValue( "r_lodbias", 0 );
 +        trap_Cvar_SetValue( "r_colorbits", 0 );
 +        trap_Cvar_SetValue( "r_depthbits", 24 );
 +        trap_Cvar_SetValue( "r_picmip", 1 );
 +        trap_Cvar_SetValue( "r_mode", 3 );
 +        trap_Cvar_SetValue( "r_texturebits", 0 );
 +        trap_Cvar_SetValue( "r_fastSky", 0 );
 +        trap_Cvar_SetValue( "r_inGameVideo", 1 );
 +        trap_Cvar_SetValue( "cg_brassTime", 2500 );
 +        trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
 +        trap_Cvar_SetValue( "cg_shadows", 0 );
 +        trap_Cvar_SetValue( "cg_bounceParticles", 0 );
 +      break;
 +      case 2: // fast
 +        trap_Cvar_SetValue( "r_fullScreen", 1 );
 +        trap_Cvar_SetValue( "r_subdivisions", 8 );
 +        trap_Cvar_SetValue( "r_vertexlight", 0 );
 +        trap_Cvar_SetValue( "r_lodbias", 1 );
 +        trap_Cvar_SetValue( "r_colorbits", 0 );
 +        trap_Cvar_SetValue( "r_depthbits", 0 );
 +        trap_Cvar_SetValue( "r_picmip", 1 );
 +        trap_Cvar_SetValue( "r_mode", 3 );
 +        trap_Cvar_SetValue( "r_texturebits", 0 );
 +        trap_Cvar_SetValue( "cg_shadows", 0 );
 +        trap_Cvar_SetValue( "r_fastSky", 1 );
 +        trap_Cvar_SetValue( "r_inGameVideo", 0 );
 +        trap_Cvar_SetValue( "cg_brassTime", 0 );
 +        trap_Cvar_SetValue( "cg_bounceParticles", 0 );
 +        trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" );
 +      break;
 +      case 3: // fastest
 +        trap_Cvar_SetValue( "r_fullScreen", 1 );
 +        trap_Cvar_SetValue( "r_subdivisions", 20 );
 +        trap_Cvar_SetValue( "r_vertexlight", 1 );
 +        trap_Cvar_SetValue( "r_lodbias", 2 );
 +        trap_Cvar_SetValue( "r_colorbits", 16 );
 +        trap_Cvar_SetValue( "r_depthbits", 16 );
 +        trap_Cvar_SetValue( "r_mode", 3 );
 +        trap_Cvar_SetValue( "r_picmip", 2 );
 +        trap_Cvar_SetValue( "r_texturebits", 16 );
 +        trap_Cvar_SetValue( "cg_shadows", 0 );
 +        trap_Cvar_SetValue( "cg_brassTime", 0 );
 +        trap_Cvar_SetValue( "r_fastSky", 1 );
 +        trap_Cvar_SetValue( "r_inGameVideo", 0 );
 +        trap_Cvar_SetValue( "cg_bounceParticles", 0 );
 +        trap_Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_NEAREST" );
 +      break;
 +    }
 +  } else if (Q_stricmp(name, "ui_mousePitch") == 0) {
 +    if (val == 0) {
 +      trap_Cvar_SetValue( "m_pitch", 0.022f );
 +    } else {
 +      trap_Cvar_SetValue( "m_pitch", -0.022f );
 +    }
 +  }
 +}
 +
 +static void UI_RunMenuScript(char **args) {
 +  const char *name, *name2;
 +  char buff[1024];
 +  const char *cmd;
 +
 +  if (String_Parse(args, &name)) {
 +    if (Q_stricmp(name, "StartServer") == 0) {
 +      int i, clients, oldclients;
 +      float skill;
 +      trap_Cvar_Set("cg_thirdPerson", "0");
 +      trap_Cvar_Set("cg_cameraOrbit", "0");
 +      trap_Cvar_Set("ui_singlePlayerActive", "0");
 +      trap_Cvar_SetValue( "dedicated", Com_Clamp( 0, 2, ui_dedicated.integer ) );
 +      trap_Cvar_SetValue( "g_gametype", Com_Clamp( 0, 8, uiInfo.gameTypes[ui_netGameType.integer].gtEnum ) );
 +      trap_Cvar_Set("g_redTeam", UI_Cvar_VariableString("ui_teamName"));
 +      trap_Cvar_Set("g_blueTeam", UI_Cvar_VariableString("ui_opponentName"));
 +      trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName ) );
 +      skill = trap_Cvar_VariableValue( "g_spSkill" );
 +      // set max clients based on spots
 +      oldclients = trap_Cvar_VariableValue( "sv_maxClients" );
 +      clients = 0;
 +      for (i = 0; i < PLAYERS_PER_TEAM; i++) {
 +        int bot = trap_Cvar_VariableValue( va("ui_blueteam%i", i+1));
 +        if (bot >= 0) {
 +          clients++;
 +        }
 +        bot = trap_Cvar_VariableValue( va("ui_redteam%i", i+1));
 +        if (bot >= 0) {
 +          clients++;
 +        }
 +      }
 +      if (clients == 0) {
 +        clients = 8;
 +      }
 +
 +      if (oldclients > clients) {
 +        clients = oldclients;
 +      }
 +
 +      trap_Cvar_Set("sv_maxClients", va("%d",clients));
 +
 +      for (i = 0; i < PLAYERS_PER_TEAM; i++) {
 +        int bot = trap_Cvar_VariableValue( va("ui_blueteam%i", i+1));
 +        if (bot > 1) {
 +          Com_sprintf( buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot-2), skill);
 +          trap_Cmd_ExecuteText( EXEC_APPEND, buff );
 +        }
 +        bot = trap_Cvar_VariableValue( va("ui_redteam%i", i+1));
 +        if (bot > 1) {
 +          Com_sprintf( buff, sizeof(buff), "addbot %s %f \n", UI_GetBotNameByNumber(bot-2), skill);
 +          trap_Cmd_ExecuteText( EXEC_APPEND, buff );
 +        }
 +      }
 +    } else if (Q_stricmp(name, "updateSPMenu") == 0) {
 +      UI_SetCapFragLimits(qtrue);
 +      UI_MapCountByGameType(qtrue);
 +      ui_mapIndex.integer = UI_GetIndexFromSelection(ui_currentMap.integer);
 +      trap_Cvar_Set("ui_mapIndex", va("%d", ui_mapIndex.integer));
 +      Menu_SetFeederSelection(NULL, FEEDER_MAPS, ui_mapIndex.integer, "skirmish");
 +      UI_GameType_HandleKey(0, NULL, K_MOUSE1, qfalse);
 +      UI_GameType_HandleKey(0, NULL, K_MOUSE2, qfalse);
 +    } else if (Q_stricmp(name, "resetDefaults") == 0) {
 +      trap_Cmd_ExecuteText( EXEC_APPEND, "exec default.cfg\n");
 +      trap_Cmd_ExecuteText( EXEC_APPEND, "cvar_restart\n");
 +      Controls_SetDefaults();
 +      trap_Cvar_Set("com_introPlayed", "1" );
 +      trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart\n" );
 +    } else if (Q_stricmp(name, "loadArenas") == 0) {
 +      UI_LoadArenas();
 +      UI_MapCountByGameType(qfalse);
 +      Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, "createserver");
 +    } else if (Q_stricmp(name, "loadServerInfo") == 0) {
 +      UI_ServerInfo();
 +    } else if (Q_stricmp(name, "saveControls") == 0) {
 +      Controls_SetConfig(qtrue);
 +    } else if (Q_stricmp(name, "loadControls") == 0) {
 +      Controls_GetConfig();
 +    } else if (Q_stricmp(name, "clearError") == 0) {
 +      trap_Cvar_Set("com_errorMessage", "");
 +    } else if (Q_stricmp(name, "loadGameInfo") == 0) {
 +/*      UI_ParseGameInfo("gameinfo.txt");
 +      UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);*/
 +    } else if (Q_stricmp(name, "resetScores") == 0) {
 +      UI_ClearScores();
 +    } else if (Q_stricmp(name, "RefreshServers") == 0) {
 +      UI_StartServerRefresh(qtrue);
 +      UI_BuildServerDisplayList(qtrue);
 +    } else if (Q_stricmp(name, "InitServerList") == 0) {
 +      int time = trap_RealTime( NULL );
 +      int last;
 +      int sortColumn;
 +
 +      // set up default sorting
 +      if(!uiInfo.serverStatus.sorted && Int_Parse(args, &sortColumn))
 +      {
 +        uiInfo.serverStatus.sortKey = sortColumn;
 +	uiInfo.serverStatus.sortDir = 0;
 +      }
 +
 +      // refresh if older than 3 days or if list is empty
 +      last = atoi( UI_Cvar_VariableString( va( "ui_lastServerRefresh_%i_time",
 +        ui_netSource.integer ) ) );
 +      if( trap_LAN_GetServerCount( ui_netSource.integer ) < 1 ||
 +        ( time - last ) > 3600 )
 +      {
 +        UI_StartServerRefresh(qtrue);
 +        UI_BuildServerDisplayList(qtrue);
 +      }
 +    } else if (Q_stricmp(name, "RefreshFilter") == 0) {
 +      UI_StartServerRefresh(qfalse);
 +      UI_BuildServerDisplayList(qtrue);
 +    } else if (Q_stricmp(name, "RunSPDemo") == 0) {
 +      if (uiInfo.demoAvailable) {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va("demo %s_%i\n", uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum));
 +      }
 +    } else if (Q_stricmp(name, "LoadDemos") == 0) {
 +      UI_LoadDemos();
 +    } else if (Q_stricmp(name, "LoadMovies") == 0) {
 +      UI_LoadMovies();
 +    } else if (Q_stricmp(name, "LoadMods") == 0) {
 +      UI_LoadMods();
 +    }
 +
 +//TA: tremulous menus
 +    else if( Q_stricmp( name, "LoadTeams" ) == 0 )
 +      UI_LoadTremTeams( );
 +    else if( Q_stricmp( name, "JoinTeam" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremTeamList[ uiInfo.tremTeamIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "LoadHumanItems" ) == 0 )
 +      UI_LoadTremHumanItems( );
 +    else if( Q_stricmp( name, "SpawnWithHumanItem" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremHumanItemList[ uiInfo.tremHumanItemIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "LoadAlienClasses" ) == 0 )
 +      UI_LoadTremAlienClasses( );
 +    else if( Q_stricmp( name, "SpawnAsAlienClass" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremAlienClassList[ uiInfo.tremAlienClassIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "LoadHumanArmouryBuys" ) == 0 )
 +      UI_LoadTremHumanArmouryBuys( );
 +    else if( Q_stricmp( name, "BuyFromArmoury" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremHumanArmouryBuyList[ uiInfo.tremHumanArmouryBuyIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "LoadHumanArmourySells" ) == 0 )
 +      UI_LoadTremHumanArmourySells( );
 +    else if( Q_stricmp( name, "SellToArmoury" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremHumanArmourySellList[ uiInfo.tremHumanArmourySellIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "LoadAlienUpgrades" ) == 0 )
 +    {
 +      UI_LoadTremAlienUpgrades( );
 +
 +      //disallow the menu if it would be empty
 +      if( uiInfo.tremAlienUpgradeCount <= 0 )
 +        Menus_CloseAll( );
 +    }
 +    else if( Q_stricmp( name, "UpgradeToNewClass" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremAlienUpgradeList[ uiInfo.tremAlienUpgradeIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "LoadAlienBuilds" ) == 0 )
 +      UI_LoadTremAlienBuilds( );
 +    else if( Q_stricmp( name, "BuildAlienBuildable" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremAlienBuildList[ uiInfo.tremAlienBuildIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "LoadHumanBuilds" ) == 0 )
 +      UI_LoadTremHumanBuilds( );
 +    else if( Q_stricmp( name, "BuildHumanBuildable" ) == 0 )
 +    {
 +      if( ( cmd = uiInfo.tremHumanBuildList[ uiInfo.tremHumanBuildIndex ].cmd ) )
 +        trap_Cmd_ExecuteText( EXEC_APPEND, cmd );
 +    }
 +    else if( Q_stricmp( name, "PTRCRestore" ) == 0 )
 +    {
 +      int           len;
 +      char          text[ 16 ];
 +      fileHandle_t  f;
 +      char          command[ 32 ];
 +
 +      // load the file
 +      len = trap_FS_FOpenFile( "ptrc.cfg", &f, FS_READ );
 +
 +      if( len > 0 && ( len < sizeof( text ) - 1 ) )
 +      {
 +        trap_FS_Read( text, len, f );
 +        text[ len ] = 0;
 +        trap_FS_FCloseFile( f );
 +
 +        Com_sprintf( command, 32, "ptrcrestore %s", text );
 +
 +        trap_Cmd_ExecuteText( EXEC_APPEND, command );
 +      }
 +    }
 +//TA: tremulous menus
 +
 +    else if (Q_stricmp(name, "playMovie") == 0) {
 +      if (uiInfo.previewMovie >= 0) {
 +        trap_CIN_StopCinematic(uiInfo.previewMovie);
 +      }
 +      trap_Cmd_ExecuteText( EXEC_APPEND, va("cinematic %s.roq 2\n", uiInfo.movieList[uiInfo.movieIndex]));
 +    } else if (Q_stricmp(name, "RunMod") == 0) {
 +      trap_Cvar_Set( "fs_game", uiInfo.modList[uiInfo.modIndex].modName);
 +      trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" );
 +    } else if (Q_stricmp(name, "RunDemo") == 0) {
 +      trap_Cmd_ExecuteText( EXEC_APPEND, va("demo %s\n", uiInfo.demoList[uiInfo.demoIndex]));
 +    } else if (Q_stricmp(name, "Tremulous") == 0) {
 +      trap_Cvar_Set( "fs_game", "");
 +      trap_Cmd_ExecuteText( EXEC_APPEND, "vid_restart;" );
 +    } else if (Q_stricmp(name, "closeJoin") == 0) {
 +      if (uiInfo.serverStatus.refreshActive) {
 +        UI_StopServerRefresh();
 +        uiInfo.serverStatus.nextDisplayRefresh = 0;
 +        uiInfo.nextServerStatusRefresh = 0;
 +        uiInfo.nextFindPlayerRefresh = 0;
 +        UI_BuildServerDisplayList(qtrue);
 +      } else {
 +        Menus_CloseByName("joinserver");
 +        Menus_OpenByName("main");
 +      }
 +    } else if (Q_stricmp(name, "StopRefresh") == 0) {
 +      UI_StopServerRefresh();
 +      uiInfo.serverStatus.nextDisplayRefresh = 0;
 +      uiInfo.nextServerStatusRefresh = 0;
 +      uiInfo.nextFindPlayerRefresh = 0;
 +    } else if (Q_stricmp(name, "UpdateFilter") == 0) {
 +      if (ui_netSource.integer == AS_LOCAL) {
 +        UI_StartServerRefresh(qtrue);
 +      }
 +      UI_BuildServerDisplayList(qtrue);
 +      UI_FeederSelection(FEEDER_SERVERS, 0);
 +    } else if (Q_stricmp(name, "ServerStatus") == 0) {
 +      trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], uiInfo.serverStatusAddress, sizeof(uiInfo.serverStatusAddress));
 +      UI_BuildServerStatus(qtrue);
 +    } else if (Q_stricmp(name, "FoundPlayerServerStatus") == 0) {
 +      Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer], sizeof(uiInfo.serverStatusAddress));
 +      UI_BuildServerStatus(qtrue);
 +      Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL);
 +    } else if (Q_stricmp(name, "FindPlayer") == 0) {
 +      UI_BuildFindPlayerList(qtrue);
 +      // clear the displayed server status info
 +      uiInfo.serverStatusInfo.numLines = 0;
 +      Menu_SetFeederSelection(NULL, FEEDER_FINDPLAYER, 0, NULL);
 +    } else if (Q_stricmp(name, "JoinServer") == 0) {
 +      trap_Cvar_Set("cg_thirdPerson", "0");
 +      trap_Cvar_Set("cg_cameraOrbit", "0");
 +      trap_Cvar_Set("ui_singlePlayerActive", "0");
 +      if (uiInfo.serverStatus.currentServer >= 0 && uiInfo.serverStatus.currentServer < uiInfo.serverStatus.numDisplayServers) {
 +        trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, 1024);
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", buff ) );
 +      }
 +    } else if (Q_stricmp(name, "FoundPlayerJoinServer") == 0) {
 +      trap_Cvar_Set("ui_singlePlayerActive", "0");
 +      if (uiInfo.currentFoundPlayerServer >= 0 && uiInfo.currentFoundPlayerServer < uiInfo.numFoundPlayerServers) {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "connect %s\n", uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer] ) );
 +      }
 +    } else if (Q_stricmp(name, "Quit") == 0) {
 +      trap_Cvar_Set("ui_singlePlayerActive", "0");
 +      trap_Cmd_ExecuteText( EXEC_NOW, "quit");
 +    } else if (Q_stricmp(name, "Controls") == 0) {
 +      trap_Cvar_Set( "cl_paused", "1" );
 +      trap_Key_SetCatcher( KEYCATCH_UI );
 +      Menus_CloseAll();
 +      Menus_ActivateByName("setup_menu2");
 +    } else if (Q_stricmp(name, "Leave") == 0) {
 +      trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect\n" );
 +      trap_Key_SetCatcher( KEYCATCH_UI );
 +      Menus_CloseAll();
 +      Menus_ActivateByName("main");
 +    } else if (Q_stricmp(name, "ServerSort") == 0) {
 +      int sortColumn;
 +      if (Int_Parse(args, &sortColumn)) {
 +        // if same column we're already sorting on then flip the direction
 +        if (sortColumn == uiInfo.serverStatus.sortKey) {
 +          uiInfo.serverStatus.sortDir = !uiInfo.serverStatus.sortDir;
 +        }
 +        // make sure we sort again
 +        UI_ServersSort(sortColumn, qtrue);
 +        uiInfo.serverStatus.sorted = qtrue;
 +      }
 +    } else if (Q_stricmp(name, "nextSkirmish") == 0) {
 +      UI_StartSkirmish(qtrue);
 +    } else if (Q_stricmp(name, "SkirmishStart") == 0) {
 +      UI_StartSkirmish(qfalse);
 +    } else if (Q_stricmp(name, "closeingame") == 0) {
 +      trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
 +      trap_Key_ClearStates();
 +      trap_Cvar_Set( "cl_paused", "0" );
 +      Menus_CloseAll();
 +    } else if (Q_stricmp(name, "voteMap") == 0) {
 +      if (ui_currentNetMap.integer >=0 && ui_currentNetMap.integer < uiInfo.mapCount) {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote map %s\n",uiInfo.mapList[ui_currentNetMap.integer].mapLoadName) );
 +      }
 +    }
 +    else if( Q_stricmp( name, "voteKick" ) == 0 )
 +    {
 +      if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount )
 +      {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote kick %d\n",
 +              uiInfo.clientNums[ uiInfo.playerIndex ] ) );
 +      }
 +    }
 +    else if( Q_stricmp( name, "voteMute" ) == 0 )
 +    {
 +      if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount )
 +      {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote mute %d\n",
 +              uiInfo.clientNums[ uiInfo.playerIndex ] ) );
 +      }
 +    }
 +    else if( Q_stricmp( name, "voteUnMute" ) == 0 )
 +    {
 +      if( uiInfo.playerIndex >= 0 && uiInfo.playerIndex < uiInfo.playerCount )
 +      {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "callvote unmute %d\n",
 +              uiInfo.clientNums[ uiInfo.playerIndex ] ) );
 +      }
 +    }
 +    else if( Q_stricmp( name, "voteTeamKick" ) == 0 )
 +    {
 +      if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount )
 +      {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote kick %d\n",
 +              uiInfo.teamClientNums[ uiInfo.teamIndex ] ) );
 +      }
 +    }
 +    else if( Q_stricmp( name, "voteTeamDenyBuild" ) == 0 )
 +    {
 +      if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount )
 +      {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote denybuild %d\n",
 +              uiInfo.teamClientNums[ uiInfo.teamIndex ] ) );
 +      }
 +    }
 +    else if( Q_stricmp( name, "voteTeamAllowBuild" ) == 0 )
 +    {
 +      if( uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount )
 +      {
 +        trap_Cmd_ExecuteText( EXEC_APPEND, va( "callteamvote allowbuild %d\n",
 +              uiInfo.teamClientNums[ uiInfo.teamIndex ] ) );
 +      }
 +    }
 +    else if (Q_stricmp(name, "addFavorite") == 0) {
 +      if (ui_netSource.integer != AS_FAVORITES) {
 +        char name[MAX_NAME_LENGTH];
 +        char addr[MAX_NAME_LENGTH];
 +        int res;
 +
 +        trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, MAX_STRING_CHARS);
 +        name[0] = addr[0] = '\0';
 +        Q_strncpyz(name,  Info_ValueForKey(buff, "hostname"), MAX_NAME_LENGTH);
 +        Q_strncpyz(addr,  Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH);
 +        if (strlen(name) > 0 && strlen(addr) > 0) {
 +          res = trap_LAN_AddServer(AS_FAVORITES, name, addr);
 +          if (res == 0) {
 +            // server already in the list
 +            Com_Printf("Favorite already in list\n");
 +          }
 +          else if (res == -1) {
 +            // list full
 +            Com_Printf("Favorite list full\n");
 +          }
 +          else {
 +            // successfully added
 +            Com_Printf("Added favorite server %s\n", addr);
 +          }
 +        }
 +      }
 +    } else if (Q_stricmp(name, "deleteFavorite") == 0) {
 +      if (ui_netSource.integer == AS_FAVORITES) {
 +        char addr[MAX_NAME_LENGTH];
 +        trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.serverStatus.currentServer], buff, MAX_STRING_CHARS);
 +        addr[0] = '\0';
 +        Q_strncpyz(addr,  Info_ValueForKey(buff, "addr"), MAX_NAME_LENGTH);
 +        if (strlen(addr) > 0) {
 +          trap_LAN_RemoveServer(AS_FAVORITES, addr);
 +        }
 +      }
 +    } else if (Q_stricmp(name, "createFavorite") == 0) {
 +      if (ui_netSource.integer == AS_FAVORITES) {
 +        char name[MAX_NAME_LENGTH];
 +        char addr[MAX_NAME_LENGTH];
 +        int res;
 +
 +        name[0] = addr[0] = '\0';
 +        Q_strncpyz(name,  UI_Cvar_VariableString("ui_favoriteName"), MAX_NAME_LENGTH);
 +        Q_strncpyz(addr,  UI_Cvar_VariableString("ui_favoriteAddress"), MAX_NAME_LENGTH);
 +        if (strlen(name) > 0 && strlen(addr) > 0) {
 +          res = trap_LAN_AddServer(AS_FAVORITES, name, addr);
 +          if (res == 0) {
 +            // server already in the list
 +            Com_Printf("Favorite already in list\n");
 +          }
 +          else if (res == -1) {
 +            // list full
 +            Com_Printf("Favorite list full\n");
 +          }
 +          else {
 +            // successfully added
 +            Com_Printf("Added favorite server %s\n", addr);
 +          }
 +        }
 +      }
 +    } else if (Q_stricmp(name, "orders") == 0) {
 +      const char *orders;
 +      if (String_Parse(args, &orders)) {
 +        int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer");
 +        if (selectedPlayer < uiInfo.myTeamCount) {
 +          strcpy(buff, orders);
 +          trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]) );
 +          trap_Cmd_ExecuteText( EXEC_APPEND, "\n" );
 +        } else {
 +          int i;
 +          for (i = 0; i < uiInfo.myTeamCount; i++) {
 +            if (Q_stricmp(UI_Cvar_VariableString("name"), uiInfo.teamNames[i]) == 0) {
 +              continue;
 +            }
 +            strcpy(buff, orders);
 +            trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamNames[i]) );
 +            trap_Cmd_ExecuteText( EXEC_APPEND, "\n" );
 +          }
 +        }
 +        trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
 +        trap_Key_ClearStates();
 +        trap_Cvar_Set( "cl_paused", "0" );
 +        Menus_CloseAll();
 +      }
 +    } else if (Q_stricmp(name, "voiceOrdersTeam") == 0) {
 +      const char *orders;
 +      if (String_Parse(args, &orders)) {
 +        int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer");
 +        if (selectedPlayer == uiInfo.myTeamCount) {
 +          trap_Cmd_ExecuteText( EXEC_APPEND, orders );
 +          trap_Cmd_ExecuteText( EXEC_APPEND, "\n" );
 +        }
 +        trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
 +        trap_Key_ClearStates();
 +        trap_Cvar_Set( "cl_paused", "0" );
 +        Menus_CloseAll();
 +      }
 +    } else if (Q_stricmp(name, "voiceOrders") == 0) {
 +      const char *orders;
 +      if (String_Parse(args, &orders)) {
 +        int selectedPlayer = trap_Cvar_VariableValue("cg_selectedPlayer");
 +        if (selectedPlayer < uiInfo.myTeamCount) {
 +          strcpy(buff, orders);
 +          trap_Cmd_ExecuteText( EXEC_APPEND, va(buff, uiInfo.teamClientNums[selectedPlayer]) );
 +          trap_Cmd_ExecuteText( EXEC_APPEND, "\n" );
 +        }
 +        trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
 +        trap_Key_ClearStates();
 +        trap_Cvar_Set( "cl_paused", "0" );
 +        Menus_CloseAll();
 +      }
 +    } else if (Q_stricmp(name, "glCustom") == 0) {
 +      trap_Cvar_Set("ui_glCustom", "4");
 +    } else if (Q_stricmp(name, "update") == 0) {
 +      if (String_Parse(args, &name2))
 +        UI_Update(name2);
 +    } else if (Q_stricmp(name, "InitIgnoreList") == 0) {
 +      UI_BuildPlayerList();
 +    } else if (Q_stricmp(name, "ToggleIgnore") == 0) {
 +      if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount )
 +      {
 +        if( BG_ClientListTest( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +          uiInfo.clientNums[ uiInfo.ignoreIndex ] ) )
 +        {
 +          BG_ClientListRemove( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] );
 +          trap_Cmd_ExecuteText( EXEC_NOW, va( "unignore %i\n", 
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
 +        }
 +        else
 +        {
 +          BG_ClientListAdd( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] );
 +          trap_Cmd_ExecuteText( EXEC_NOW, va( "ignore %i\n", 
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
 +        }
 +      }
 +    } else if (Q_stricmp(name, "IgnorePlayer") == 0) {
 +      if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount )
 +      {
 +        if( !BG_ClientListTest( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +          uiInfo.clientNums[ uiInfo.ignoreIndex ] ) )
 +        {
 +          BG_ClientListAdd( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] );
 +          trap_Cmd_ExecuteText( EXEC_NOW, va( "ignore %i\n", 
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
 +        }
 +      }
 +    } else if (Q_stricmp(name, "UnIgnorePlayer") == 0) {
 +      if( uiInfo.ignoreIndex >= 0 && uiInfo.ignoreIndex < uiInfo.playerCount )
 +      {
 +        if( BG_ClientListTest( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +          uiInfo.clientNums[ uiInfo.ignoreIndex ] ) )
 +        {
 +          BG_ClientListRemove( &uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] );
 +          trap_Cmd_ExecuteText( EXEC_NOW, va( "unignore %i\n", 
 +            uiInfo.clientNums[ uiInfo.ignoreIndex ] ) );
 +        }
 +      }
 +    } else if (Q_stricmp(name, "setPbClStatus") == 0) {
 +      int stat;
 +      if ( Int_Parse( args, &stat ) )
 +        trap_SetPbClStatus( stat );
 +    }
 +    else {
 +      Com_Printf("unknown UI script %s\n", name);
 +    }
 +  }
 +}
 +
 +static void UI_GetTeamColor(vec4_t *color) {
 +}
 +
 +/*
 +==================
 +UI_MapCountByGameType
 +==================
 +*/
 +static int UI_MapCountByGameType(qboolean singlePlayer) {
 +  int i, c, game;
 +  c = 0;
 +  game = singlePlayer ? uiInfo.gameTypes[ui_gameType.integer].gtEnum : uiInfo.gameTypes[ui_netGameType.integer].gtEnum;
 +
 +  for (i = 0; i < uiInfo.mapCount; i++) {
 +    uiInfo.mapList[i].active = qfalse;
 +    if ( uiInfo.mapList[i].typeBits & (1 << game)) {
 +      if (singlePlayer) {
 +        if (!(uiInfo.mapList[i].typeBits & (1 << 2))) {
 +          continue;
 +        }
 +      }
 +      c++;
 +      uiInfo.mapList[i].active = qtrue;
 +    }
 +  }
 +  return c;
 +}
 +
 +qboolean UI_hasSkinForBase(const char *base, const char *team) {
 +  char  test[1024];
 +
 +  Com_sprintf( test, sizeof( test ), "models/players/%s/%s/lower_default.skin", base, team );
 +
 +  if (trap_FS_FOpenFile(test, NULL, FS_READ)) {
 +    return qtrue;
 +  }
 +  Com_sprintf( test, sizeof( test ), "models/players/characters/%s/%s/lower_default.skin", base, team );
 +
 +  if (trap_FS_FOpenFile(test, NULL, FS_READ)) {
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +/*
 +==================
 +UI_MapCountByTeam
 +==================
 +*/
 +static int UI_HeadCountByTeam( void ) {
 +  static int init = 0;
 +  int i, j, k, c, tIndex;
 +
 +  c = 0;
 +  if (!init) {
 +    for (i = 0; i < uiInfo.characterCount; i++) {
 +      uiInfo.characterList[i].reference = 0;
 +      for (j = 0; j < uiInfo.teamCount; j++) {
 +        if (UI_hasSkinForBase(uiInfo.characterList[i].base, uiInfo.teamList[j].teamName)) {
 +          uiInfo.characterList[i].reference |= (1<<j);
 +        }
 +      }
 +    }
 +    init = 1;
 +  }
 +
 +  tIndex = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +
 +  // do names
 +  for (i = 0; i < uiInfo.characterCount; i++) {
 +    uiInfo.characterList[i].active = qfalse;
 +    for(j = 0; j < TEAM_MEMBERS; j++) {
 +      if (uiInfo.teamList[tIndex].teamMembers[j] != NULL) {
 +        if (uiInfo.characterList[i].reference&(1<<tIndex)) {// && Q_stricmp(uiInfo.teamList[tIndex].teamMembers[j], uiInfo.characterList[i].name)==0) {
 +          uiInfo.characterList[i].active = qtrue;
 +          c++;
 +          break;
 +        }
 +      }
 +    }
 +  }
 +
 +  // and then aliases
 +  for(j = 0; j < TEAM_MEMBERS; j++) {
 +    for(k = 0; k < uiInfo.aliasCount; k++) {
 +      if (uiInfo.aliasList[k].name != NULL) {
 +        if (Q_stricmp(uiInfo.teamList[tIndex].teamMembers[j], uiInfo.aliasList[k].name)==0) {
 +          for (i = 0; i < uiInfo.characterCount; i++) {
 +            if (uiInfo.characterList[i].headImage != -1 && uiInfo.characterList[i].reference&(1<<tIndex) && Q_stricmp(uiInfo.aliasList[k].ai, uiInfo.characterList[i].name)==0) {
 +              if (uiInfo.characterList[i].active == qfalse) {
 +                uiInfo.characterList[i].active = qtrue;
 +                c++;
 +              }
 +              break;
 +            }
 +          }
 +        }
 +      }
 +    }
 +  }
 +  return c;
 +}
 +
 +/*
 +==================
 +UI_InsertServerIntoDisplayList
 +==================
 +*/
 +static void UI_InsertServerIntoDisplayList(int num, int position) {
 +  int i;
 +
 +  if (position < 0 || position > uiInfo.serverStatus.numDisplayServers ) {
 +    return;
 +  }
 +  //
 +  uiInfo.serverStatus.numDisplayServers++;
 +  for (i = uiInfo.serverStatus.numDisplayServers; i > position; i--) {
 +    uiInfo.serverStatus.displayServers[i] = uiInfo.serverStatus.displayServers[i-1];
 +  }
 +  uiInfo.serverStatus.displayServers[position] = num;
 +}
 +
 +/*
 +==================
 +UI_RemoveServerFromDisplayList
 +==================
 +*/
 +static void UI_RemoveServerFromDisplayList(int num) {
 +  int i, j;
 +
 +  for (i = 0; i < uiInfo.serverStatus.numDisplayServers; i++) {
 +    if (uiInfo.serverStatus.displayServers[i] == num) {
 +      uiInfo.serverStatus.numDisplayServers--;
 +      for (j = i; j < uiInfo.serverStatus.numDisplayServers; j++) {
 +        uiInfo.serverStatus.displayServers[j] = uiInfo.serverStatus.displayServers[j+1];
 +      }
 +      return;
 +    }
 +  }
 +}
 +
 +/*
 +==================
 +UI_BinaryServerInsertion
 +==================
 +*/
 +static void UI_BinaryServerInsertion(int num) {
 +  int mid, offset, res, len;
 +
 +  // use binary search to insert server
 +  len = uiInfo.serverStatus.numDisplayServers;
 +  mid = len;
 +  offset = 0;
 +  res = 0;
 +  while(mid > 0) {
 +    mid = len >> 1;
 +    //
 +    res = trap_LAN_CompareServers( ui_netSource.integer, uiInfo.serverStatus.sortKey,
 +          uiInfo.serverStatus.sortDir, num, uiInfo.serverStatus.displayServers[offset+mid]);
 +    // if equal
 +    if (res == 0) {
 +      UI_InsertServerIntoDisplayList(num, offset+mid);
 +      return;
 +    }
 +    // if larger
 +    else if (res == 1) {
 +      offset += mid;
 +      len -= mid;
 +    }
 +    // if smaller
 +    else {
 +      len -= mid;
 +    }
 +  }
 +  if (res == 1) {
 +    offset++;
 +  }
 +  UI_InsertServerIntoDisplayList(num, offset);
 +}
 +
 +/*
 +==================
 +UI_BuildServerDisplayList
 +==================
 +*/
 +static void UI_BuildServerDisplayList(qboolean force) {
 +  int i, count, clients, maxClients, ping, game, len, visible;
 +  char info[MAX_STRING_CHARS];
 +//  qboolean startRefresh = qtrue; TTimo: unused
 +  static int numinvisible;
 +
 +  if (!(force || uiInfo.uiDC.realTime > uiInfo.serverStatus.nextDisplayRefresh)) {
 +    return;
 +  }
 +  // if we shouldn't reset
 +  if ( force == 2 ) {
 +    force = 0;
 +  }
 +
 +  // do motd updates here too
 +  trap_Cvar_VariableStringBuffer( "cl_motdString", uiInfo.serverStatus.motd, sizeof(uiInfo.serverStatus.motd) );
 +  len = strlen(uiInfo.serverStatus.motd);
 +  if (len != uiInfo.serverStatus.motdLen) {
 +    uiInfo.serverStatus.motdLen = len;
 +    uiInfo.serverStatus.motdWidth = -1;
 +  }
 +
 +  if (force) {
 +    numinvisible = 0;
 +    // clear number of displayed servers
 +    uiInfo.serverStatus.numDisplayServers = 0;
 +    uiInfo.serverStatus.numPlayersOnServers = 0;
 +    // set list box index to zero
 +    Menu_SetFeederSelection(NULL, FEEDER_SERVERS, 0, NULL);
 +    // mark all servers as visible so we store ping updates for them
 +    trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue);
 +  }
 +
 +  // get the server count (comes from the master)
 +  count = trap_LAN_GetServerCount(ui_netSource.integer);
 +  if (count == -1 || (ui_netSource.integer == AS_LOCAL && count == 0) ) {
 +    // still waiting on a response from the master
 +    uiInfo.serverStatus.numDisplayServers = 0;
 +    uiInfo.serverStatus.numPlayersOnServers = 0;
 +    uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 500;
 +    return;
 +  }
 +
 +  visible = qfalse;
 +  for (i = 0; i < count; i++) {
 +    // if we already got info for this server
 +    if (!trap_LAN_ServerIsVisible(ui_netSource.integer, i)) {
 +      continue;
 +    }
 +    visible = qtrue;
 +    // get the ping for this server
 +    ping = trap_LAN_GetServerPing(ui_netSource.integer, i);
 +    if (ping > 0 || ui_netSource.integer == AS_FAVORITES) {
 +
 +      trap_LAN_GetServerInfo(ui_netSource.integer, i, info, MAX_STRING_CHARS);
 +
 +      clients = atoi(Info_ValueForKey(info, "clients"));
 +      uiInfo.serverStatus.numPlayersOnServers += clients;
 +
 +      if (ui_browserShowEmpty.integer == 0) {
 +        if (clients == 0) {
 +          trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
 +          continue;
 +        }
 +      }
 +
 +      if (ui_browserShowFull.integer == 0) {
 +        maxClients = atoi(Info_ValueForKey(info, "sv_maxclients"));
 +        if (clients == maxClients) {
 +          trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
 +          continue;
 +        }
 +      }
 +
 +      if (uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum != -1) {
 +        game = atoi(Info_ValueForKey(info, "gametype"));
 +        if (game != uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum) {
 +          trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
 +          continue;
 +        }
 +      }
 +
 +      // make sure we never add a favorite server twice
 +      if (ui_netSource.integer == AS_FAVORITES) {
 +        UI_RemoveServerFromDisplayList(i);
 +      }
 +      // insert the server into the list
 +      UI_BinaryServerInsertion(i);
 +      // done with this server
 +      if (ping > 0) {
 +        trap_LAN_MarkServerVisible(ui_netSource.integer, i, qfalse);
 +        numinvisible++;
 +      }
 +    }
 +  }
 +
 +  uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime;
 +
 +  // if there were no servers visible for ping updates
 +  if (!visible) {
 +//    UI_StopServerRefresh();
 +//    uiInfo.serverStatus.nextDisplayRefresh = 0;
 +  }
 +}
 +
 +typedef struct
 +{
 +  char *name, *altName;
 +} serverStatusCvar_t;
 +
 +serverStatusCvar_t serverStatusCvars[] = {
 +  {"sv_hostname", "Name"},
 +  {"Address", ""},
 +  {"gamename", "Game name"},
 +  {"g_gametype", "Game type"},
 +  {"mapname", "Map"},
 +  {"version", ""},
 +  {"protocol", ""},
 +  {"timelimit", ""},
 +  {"fraglimit", ""},
 +  {NULL, NULL}
 +};
 +
 +/*
 +==================
 +UI_SortServerStatusInfo
 +==================
 +*/
 +static void UI_SortServerStatusInfo( serverStatusInfo_t *info ) {
 +  int i, j, index;
 +  char *tmp1, *tmp2;
 +
 +  // FIXME: if "gamename" == "baseq3" or "missionpack" then
 +  // replace the gametype number by FFA, CTF etc.
 +  //
 +  index = 0;
 +  for (i = 0; serverStatusCvars[i].name; i++) {
 +    for (j = 0; j < info->numLines; j++) {
 +      if ( !info->lines[j][1] || info->lines[j][1][0] ) {
 +        continue;
 +      }
 +      if ( !Q_stricmp(serverStatusCvars[i].name, info->lines[j][0]) ) {
 +        // swap lines
 +        tmp1 = info->lines[index][0];
 +        tmp2 = info->lines[index][3];
 +        info->lines[index][0] = info->lines[j][0];
 +        info->lines[index][3] = info->lines[j][3];
 +        info->lines[j][0] = tmp1;
 +        info->lines[j][3] = tmp2;
 +        //
 +        if ( strlen(serverStatusCvars[i].altName) ) {
 +          info->lines[index][0] = serverStatusCvars[i].altName;
 +        }
 +        index++;
 +      }
 +    }
 +  }
 +}
 +
 +/*
 +==================
 +UI_GetServerStatusInfo
 +==================
 +*/
 +static int UI_GetServerStatusInfo( const char *serverAddress, serverStatusInfo_t *info ) {
 +  char *p, *score, *ping, *name;
 +  int i, len;
 +
 +  if (!info) {
 +    trap_LAN_ServerStatus( serverAddress, NULL, 0);
 +    return qfalse;
 +  }
 +  memset(info, 0, sizeof(*info));
 +  if ( trap_LAN_ServerStatus( serverAddress, info->text, sizeof(info->text)) ) {
 +    Q_strncpyz(info->address, serverAddress, sizeof(info->address));
 +    p = info->text;
 +    info->numLines = 0;
 +    info->lines[info->numLines][0] = "Address";
 +    info->lines[info->numLines][1] = "";
 +    info->lines[info->numLines][2] = "";
 +    info->lines[info->numLines][3] = info->address;
 +    info->numLines++;
 +    // get the cvars
 +    while (p && *p) {
 +      p = strchr(p, '\\');
 +      if (!p) break;
 +      *p++ = '\0';
 +      if (*p == '\\')
 +        break;
 +      info->lines[info->numLines][0] = p;
 +      info->lines[info->numLines][1] = "";
 +      info->lines[info->numLines][2] = "";
 +      p = strchr(p, '\\');
 +      if (!p) break;
 +      *p++ = '\0';
 +      info->lines[info->numLines][3] = p;
 +
 +      info->numLines++;
 +      if (info->numLines >= MAX_SERVERSTATUS_LINES)
 +        break;
 +    }
 +    // get the player list
 +    if (info->numLines < MAX_SERVERSTATUS_LINES-3) {
 +      // empty line
 +      info->lines[info->numLines][0] = "";
 +      info->lines[info->numLines][1] = "";
 +      info->lines[info->numLines][2] = "";
 +      info->lines[info->numLines][3] = "";
 +      info->numLines++;
 +      // header
 +      info->lines[info->numLines][0] = "num";
 +      info->lines[info->numLines][1] = "score";
 +      info->lines[info->numLines][2] = "ping";
 +      info->lines[info->numLines][3] = "name";
 +      info->numLines++;
 +      // parse players
 +      i = 0;
 +      len = 0;
 +      while (p && *p) {
 +        if (*p == '\\')
 +          *p++ = '\0';
 +        if (!p)
 +          break;
 +        score = p;
 +        p = strchr(p, ' ');
 +        if (!p)
 +          break;
 +        *p++ = '\0';
 +        ping = p;
 +        p = strchr(p, ' ');
 +        if (!p)
 +          break;
 +        *p++ = '\0';
 +        name = p;
 +        Com_sprintf(&info->pings[len], sizeof(info->pings)-len, "%d", i);
 +        info->lines[info->numLines][0] = &info->pings[len];
 +        len += strlen(&info->pings[len]) + 1;
 +        info->lines[info->numLines][1] = score;
 +        info->lines[info->numLines][2] = ping;
 +        info->lines[info->numLines][3] = name;
 +        info->numLines++;
 +        if (info->numLines >= MAX_SERVERSTATUS_LINES)
 +          break;
 +        p = strchr(p, '\\');
 +        if (!p)
 +          break;
 +        *p++ = '\0';
 +        //
 +        i++;
 +      }
 +    }
 +    UI_SortServerStatusInfo( info );
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +/*
 +==================
 +stristr
 +==================
 +*/
 +static char *stristr(char *str, char *charset) {
 +  int i;
 +
 +  while(*str) {
 +    for (i = 0; charset[i] && str[i]; i++) {
 +      if (toupper(charset[i]) != toupper(str[i])) break;
 +    }
 +    if (!charset[i]) return str;
 +    str++;
 +  }
 +  return NULL;
 +}
 +
 +/*
 +==================
 +UI_BuildFindPlayerList
 +==================
 +*/
 +static void UI_BuildFindPlayerList(qboolean force) {
 +  static int numFound, numTimeOuts;
 +  int i, j, resend;
 +  serverStatusInfo_t info;
 +  char name[MAX_NAME_LENGTH+2];
 +  char infoString[MAX_STRING_CHARS];
 +
 +  if (!force) {
 +    if (!uiInfo.nextFindPlayerRefresh || uiInfo.nextFindPlayerRefresh > uiInfo.uiDC.realTime) {
 +      return;
 +    }
 +  }
 +  else {
 +    memset(&uiInfo.pendingServerStatus, 0, sizeof(uiInfo.pendingServerStatus));
 +    uiInfo.numFoundPlayerServers = 0;
 +    uiInfo.currentFoundPlayerServer = 0;
 +    trap_Cvar_VariableStringBuffer( "ui_findPlayer", uiInfo.findPlayerName, sizeof(uiInfo.findPlayerName));
 +    Q_CleanStr(uiInfo.findPlayerName);
 +    // should have a string of some length
 +    if (!strlen(uiInfo.findPlayerName)) {
 +      uiInfo.nextFindPlayerRefresh = 0;
 +      return;
 +    }
 +    // set resend time
 +    resend = ui_serverStatusTimeOut.integer / 2 - 10;
 +    if (resend < 50) {
 +      resend = 50;
 +    }
 +    trap_Cvar_Set("cl_serverStatusResendTime", va("%d", resend));
 +    // reset all server status requests
 +    trap_LAN_ServerStatus( NULL, NULL, 0);
 +    //
 +    uiInfo.numFoundPlayerServers = 1;
 +    Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
 +            sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]),
 +              "searching %d...", uiInfo.pendingServerStatus.num);
 +    numFound = 0;
 +    numTimeOuts++;
 +  }
 +  for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
 +    // if this pending server is valid
 +    if (uiInfo.pendingServerStatus.server[i].valid) {
 +      // try to get the server status for this server
 +      if (UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, &info ) ) {
 +        //
 +        numFound++;
 +        // parse through the server status lines
 +        for (j = 0; j < info.numLines; j++) {
 +          // should have ping info
 +          if ( !info.lines[j][2] || !info.lines[j][2][0] ) {
 +            continue;
 +          }
 +          // clean string first
 +          Q_strncpyz(name, info.lines[j][3], sizeof(name));
 +          Q_CleanStr(name);
 +          // if the player name is a substring
 +          if (stristr(name, uiInfo.findPlayerName)) {
 +            // add to found server list if we have space (always leave space for a line with the number found)
 +            if (uiInfo.numFoundPlayerServers < MAX_FOUNDPLAYER_SERVERS-1) {
 +              //
 +              Q_strncpyz(uiInfo.foundPlayerServerAddresses[uiInfo.numFoundPlayerServers-1],
 +                    uiInfo.pendingServerStatus.server[i].adrstr,
 +                      sizeof(uiInfo.foundPlayerServerAddresses[0]));
 +              Q_strncpyz(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
 +                    uiInfo.pendingServerStatus.server[i].name,
 +                      sizeof(uiInfo.foundPlayerServerNames[0]));
 +              uiInfo.numFoundPlayerServers++;
 +            }
 +            else {
 +              // can't add any more so we're done
 +              uiInfo.pendingServerStatus.num = uiInfo.serverStatus.numDisplayServers;
 +            }
 +          }
 +        }
 +        Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
 +                sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]),
 +                  "searching %d/%d...", uiInfo.pendingServerStatus.num, numFound);
 +        // retrieved the server status so reuse this spot
 +        uiInfo.pendingServerStatus.server[i].valid = qfalse;
 +      }
 +    }
 +    // if empty pending slot or timed out
 +    if (!uiInfo.pendingServerStatus.server[i].valid ||
 +      uiInfo.pendingServerStatus.server[i].startTime < uiInfo.uiDC.realTime - ui_serverStatusTimeOut.integer) {
 +      if (uiInfo.pendingServerStatus.server[i].valid) {
 +        numTimeOuts++;
 +      }
 +      // reset server status request for this address
 +      UI_GetServerStatusInfo( uiInfo.pendingServerStatus.server[i].adrstr, NULL );
 +      // reuse pending slot
 +      uiInfo.pendingServerStatus.server[i].valid = qfalse;
 +      // if we didn't try to get the status of all servers in the main browser yet
 +      if (uiInfo.pendingServerStatus.num < uiInfo.serverStatus.numDisplayServers) {
 +        uiInfo.pendingServerStatus.server[i].startTime = uiInfo.uiDC.realTime;
 +        trap_LAN_GetServerAddressString(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num],
 +              uiInfo.pendingServerStatus.server[i].adrstr, sizeof(uiInfo.pendingServerStatus.server[i].adrstr));
 +        trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[uiInfo.pendingServerStatus.num], infoString, sizeof(infoString));
 +        Q_strncpyz(uiInfo.pendingServerStatus.server[i].name, Info_ValueForKey(infoString, "hostname"), sizeof(uiInfo.pendingServerStatus.server[0].name));
 +        uiInfo.pendingServerStatus.server[i].valid = qtrue;
 +        uiInfo.pendingServerStatus.num++;
 +        Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1],
 +                sizeof(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1]),
 +                  "searching %d/%d...", uiInfo.pendingServerStatus.num, numFound);
 +      }
 +    }
 +  }
 +  for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
 +    if (uiInfo.pendingServerStatus.server[i].valid) {
 +      break;
 +    }
 +  }
 +  // if still trying to retrieve server status info
 +  if (i < MAX_SERVERSTATUSREQUESTS) {
 +    uiInfo.nextFindPlayerRefresh = uiInfo.uiDC.realTime + 25;
 +  }
 +  else {
 +    // add a line that shows the number of servers found
 +    if (!uiInfo.numFoundPlayerServers) {
 +      Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerAddresses[0]), "no servers found");
 +    }
 +    else {
 +      Com_sprintf(uiInfo.foundPlayerServerNames[uiInfo.numFoundPlayerServers-1], sizeof(uiInfo.foundPlayerServerAddresses[0]),
 +            "%d server%s found with player %s", uiInfo.numFoundPlayerServers-1,
 +            uiInfo.numFoundPlayerServers == 2 ? "":"s", uiInfo.findPlayerName);
 +    }
 +    uiInfo.nextFindPlayerRefresh = 0;
 +    // show the server status info for the selected server
 +    UI_FeederSelection(FEEDER_FINDPLAYER, uiInfo.currentFoundPlayerServer);
 +  }
 +}
 +
 +/*
 +==================
 +UI_BuildServerStatus
 +==================
 +*/
 +static void UI_BuildServerStatus(qboolean force) {
 +
 +  if (uiInfo.nextFindPlayerRefresh) {
 +    return;
 +  }
 +  if (!force) {
 +    if (!uiInfo.nextServerStatusRefresh || uiInfo.nextServerStatusRefresh > uiInfo.uiDC.realTime) {
 +      return;
 +    }
 +  }
 +  else {
 +    Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL);
 +    uiInfo.serverStatusInfo.numLines = 0;
 +    // reset all server status requests
 +    trap_LAN_ServerStatus( NULL, NULL, 0);
 +  }
 +  if (uiInfo.serverStatus.currentServer < 0 || uiInfo.serverStatus.currentServer > uiInfo.serverStatus.numDisplayServers || uiInfo.serverStatus.numDisplayServers == 0) {
 +    return;
 +  }
 +  if (UI_GetServerStatusInfo( uiInfo.serverStatusAddress, &uiInfo.serverStatusInfo ) ) {
 +    uiInfo.nextServerStatusRefresh = 0;
 +    UI_GetServerStatusInfo( uiInfo.serverStatusAddress, NULL );
 +  }
 +  else {
 +    uiInfo.nextServerStatusRefresh = uiInfo.uiDC.realTime + 500;
 +  }
 +}
 +
 +/*
 +==================
 +UI_FeederCount
 +==================
 +*/
 +static int UI_FeederCount(float feederID) {
 +
 +  if (feederID == FEEDER_HEADS) {
 +    return UI_HeadCountByTeam();
 +  } else if (feederID == FEEDER_Q3HEADS) {
 +    return uiInfo.q3HeadCount;
 +  } else if (feederID == FEEDER_CINEMATICS) {
 +    return uiInfo.movieCount;
 +  } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) {
 +    return UI_MapCountByGameType(feederID == FEEDER_MAPS ? qtrue : qfalse);
 +  } else if (feederID == FEEDER_SERVERS) {
 +    return uiInfo.serverStatus.numDisplayServers;
 +  } else if (feederID == FEEDER_SERVERSTATUS) {
 +    return uiInfo.serverStatusInfo.numLines;
 +  } else if (feederID == FEEDER_FINDPLAYER) {
 +    return uiInfo.numFoundPlayerServers;
 +  } else if (feederID == FEEDER_PLAYER_LIST) {
 +    if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) {
 +      uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
 +      UI_BuildPlayerList();
 +    }
 +    return uiInfo.playerCount;
 +  } else if (feederID == FEEDER_TEAM_LIST) {
 +    if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) {
 +      uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000;
 +      UI_BuildPlayerList();
 +    }
 +    return uiInfo.myTeamCount;
 +  } else if (feederID == FEEDER_IGNORE_LIST) {
 +    return uiInfo.playerCount;
 +  } else if (feederID == FEEDER_MODS) {
 +    return uiInfo.modCount;
 +  } else if (feederID == FEEDER_DEMOS) {
 +    return uiInfo.demoCount;
 +  }
 +
 +//TA: tremulous menus
 +  else if( feederID == FEEDER_TREMTEAMS )
 +    return uiInfo.tremTeamCount;
 +  else if( feederID == FEEDER_TREMHUMANITEMS )
 +    return uiInfo.tremHumanItemCount;
 +  else if( feederID == FEEDER_TREMALIENCLASSES )
 +    return uiInfo.tremAlienClassCount;
 +  else if( feederID == FEEDER_TREMHUMANARMOURYBUY )
 +    return uiInfo.tremHumanArmouryBuyCount;
 +  else if( feederID == FEEDER_TREMHUMANARMOURYSELL )
 +    return uiInfo.tremHumanArmourySellCount;
 +  else if( feederID == FEEDER_TREMALIENUPGRADE )
 +    return uiInfo.tremAlienUpgradeCount;
 +  else if( feederID == FEEDER_TREMALIENBUILD )
 +    return uiInfo.tremAlienBuildCount;
 +  else if( feederID == FEEDER_TREMHUMANBUILD )
 +    return uiInfo.tremHumanBuildCount;
 +//TA: tremulous menus
 +
 +  return 0;
 +}
 +
 +static const char *UI_SelectedMap(int index, int *actual) {
 +  int i, c;
 +  c = 0;
 +  *actual = 0;
 +  for (i = 0; i < uiInfo.mapCount; i++) {
 +    if (uiInfo.mapList[i].active) {
 +      if (c == index) {
 +        *actual = i;
 +        return uiInfo.mapList[i].mapName;
 +      } else {
 +        c++;
 +      }
 +    }
 +  }
 +  return "";
 +}
 +
 +static const char *UI_SelectedHead(int index, int *actual) {
 +  int i, c;
 +  c = 0;
 +  *actual = 0;
 +  for (i = 0; i < uiInfo.characterCount; i++) {
 +    if (uiInfo.characterList[i].active) {
 +      if (c == index) {
 +        *actual = i;
 +        return uiInfo.characterList[i].name;
 +      } else {
 +        c++;
 +      }
 +    }
 +  }
 +  return "";
 +}
 +
 +static int UI_GetIndexFromSelection(int actual) {
 +  int i, c;
 +  c = 0;
 +  for (i = 0; i < uiInfo.mapCount; i++) {
 +    if (uiInfo.mapList[i].active) {
 +      if (i == actual) {
 +        return c;
 +      }
 +        c++;
 +    }
 +  }
 +  return 0;
 +}
 +
 +static void UI_UpdatePendingPings( void ) {
 +  trap_LAN_ResetPings(ui_netSource.integer);
 +  uiInfo.serverStatus.refreshActive = qtrue;
 +  uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
 +
 +}
 +
 +static const char *UI_FeederItemText(float feederID, int index, int column, qhandle_t *handle) {
 +  static char info[MAX_STRING_CHARS];
 +  static char hostname[1024];
 +  static char clientBuff[32];
 +  static int lastColumn = -1;
 +  static int lastTime = 0;
 +  *handle = -1;
 +  if (feederID == FEEDER_HEADS) {
 +    int actual;
 +    return UI_SelectedHead(index, &actual);
 +  } else if (feederID == FEEDER_Q3HEADS) {
 +    if (index >= 0 && index < uiInfo.q3HeadCount) {
 +      return uiInfo.q3HeadNames[index];
 +    }
 +  } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) {
 +    int actual;
 +    return UI_SelectedMap(index, &actual);
 +  } else if (feederID == FEEDER_SERVERS) {
 +    if (index >= 0 && index < uiInfo.serverStatus.numDisplayServers) {
 +      int ping;
 +      if (lastColumn != column || lastTime > uiInfo.uiDC.realTime + 5000) {
 +        trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS);
 +        lastColumn = column;
 +        lastTime = uiInfo.uiDC.realTime;
 +      }
 +
 +      ping = atoi(Info_ValueForKey(info, "ping"));
 +      if (ping == -1) {
 +        // if we ever see a ping that is out of date, do a server refresh
 +        // UI_UpdatePendingPings();
 +      }
 +      switch (column) {
 +        case SORT_HOST :
 +          if (ping <= 0) {
 +            return Info_ValueForKey(info, "addr");
 +          } else {
 +            if ( ui_netSource.integer == AS_LOCAL ) {
 +              Com_sprintf( hostname, sizeof(hostname), "%s [%s]",
 +                      Info_ValueForKey(info, "hostname"),
 +                      netnames[atoi(Info_ValueForKey(info, "nettype"))] );
 +              return hostname;
 +            }
 +            else
 +            {
 +              char *text;
 +
 +              Com_sprintf( hostname, sizeof(hostname), "%s", Info_ValueForKey(info, "hostname"));
 +
 +              // Strip leading whitespace
 +              text = hostname;
 +              while( *text != '\0' && *text == ' ' )
 +                text++;
 +
 +              return text;
 +            }
 +          }
 +        case SORT_MAP :
 +          return Info_ValueForKey(info, "mapname");
 +        case SORT_CLIENTS :
 +          Com_sprintf( clientBuff, sizeof(clientBuff), "%s (%s)", Info_ValueForKey(info, "clients"), Info_ValueForKey(info, "sv_maxclients"));
 +          return clientBuff;
 +        case SORT_PING :
 +          if (ping <= 0) {
 +            return "...";
 +          } else {
 +            return Info_ValueForKey(info, "ping");
 +          }
 +      }
 +    }
 +  } else if (feederID == FEEDER_SERVERSTATUS) {
 +    if ( index >= 0 && index < uiInfo.serverStatusInfo.numLines ) {
 +      if ( column >= 0 && column < 4 ) {
 +        return uiInfo.serverStatusInfo.lines[index][column];
 +      }
 +    }
 +  } else if (feederID == FEEDER_FINDPLAYER) {
 +    if ( index >= 0 && index < uiInfo.numFoundPlayerServers ) {
 +      //return uiInfo.foundPlayerServerAddresses[index];
 +      return uiInfo.foundPlayerServerNames[index];
 +    }
 +  } else if (feederID == FEEDER_PLAYER_LIST) {
 +    if (index >= 0 && index < uiInfo.playerCount) {
 +      return uiInfo.playerNames[index];
 +    }
 +  } else if (feederID == FEEDER_TEAM_LIST) {
 +    if (index >= 0 && index < uiInfo.myTeamCount) {
 +      return uiInfo.teamNames[index];
 +    }
 +  } else if (feederID == FEEDER_IGNORE_LIST) {
 +    if (index >= 0 && index < uiInfo.playerCount) {
 +      switch( column )
 +      {
 +        case 1:
 +          // am I ignoring him
 +          return ( BG_ClientListTest(&uiInfo.ignoreList[ uiInfo.myPlayerIndex ],
 +            uiInfo.clientNums[ index ] ) ) ? "X" : "";
 +        case 2:
 +          // is he ignoring me
 +          return ( BG_ClientListTest( &uiInfo.ignoreList[ index ],
 +            uiInfo.playerNumber ) ) ? "X" : "";
 +        default:
 +          return uiInfo.playerNames[index];
 +      }
 +    }
 +  } else if (feederID == FEEDER_MODS) {
 +    if (index >= 0 && index < uiInfo.modCount) {
 +      if (uiInfo.modList[index].modDescr && *uiInfo.modList[index].modDescr) {
 +        return uiInfo.modList[index].modDescr;
 +      } else {
 +        return uiInfo.modList[index].modName;
 +      }
 +    }
 +  } else if (feederID == FEEDER_CINEMATICS) {
 +    if (index >= 0 && index < uiInfo.movieCount) {
 +      return uiInfo.movieList[index];
 +    }
 +  } else if (feederID == FEEDER_DEMOS) {
 +    if (index >= 0 && index < uiInfo.demoCount) {
 +      return uiInfo.demoList[index];
 +    }
 +  }
 +
 +//TA: tremulous menus
 +  else if( feederID == FEEDER_TREMTEAMS )
 +  {
 +    if( index >= 0 && index < uiInfo.tremTeamCount )
 +      return uiInfo.tremTeamList[ index ].text;
 +  }
 +  else if( feederID == FEEDER_TREMHUMANITEMS )
 +  {
 +    if( index >= 0 && index < uiInfo.tremHumanItemCount )
 +      return uiInfo.tremHumanItemList[ index ].text;
 +  }
 +  else if( feederID == FEEDER_TREMALIENCLASSES )
 +  {
 +    if( index >= 0 && index < uiInfo.tremAlienClassCount )
 +      return uiInfo.tremAlienClassList[ index ].text;
 +  }
 +  else if( feederID == FEEDER_TREMHUMANARMOURYBUY )
 +  {
 +    if( index >= 0 && index < uiInfo.tremHumanArmouryBuyCount )
 +      return uiInfo.tremHumanArmouryBuyList[ index ].text;
 +  }
 +  else if( feederID == FEEDER_TREMHUMANARMOURYSELL )
 +  {
 +    if( index >= 0 && index < uiInfo.tremHumanArmourySellCount )
 +      return uiInfo.tremHumanArmourySellList[ index ].text;
 +  }
 +  else if( feederID == FEEDER_TREMALIENUPGRADE )
 +  {
 +    if( index >= 0 && index < uiInfo.tremAlienUpgradeCount )
 +      return uiInfo.tremAlienUpgradeList[ index ].text;
 +  }
 +  else if( feederID == FEEDER_TREMALIENBUILD )
 +  {
 +    if( index >= 0 && index < uiInfo.tremAlienBuildCount )
 +      return uiInfo.tremAlienBuildList[ index ].text;
 +  }
 +  else if( feederID == FEEDER_TREMHUMANBUILD )
 +  {
 +    if( index >= 0 && index < uiInfo.tremHumanBuildCount )
 +      return uiInfo.tremHumanBuildList[ index ].text;
 +  }
 +//TA: tremulous menus
 +
 +  return "";
 +}
 +
 +
 +static qhandle_t UI_FeederItemImage(float feederID, int index) {
 +  if (feederID == FEEDER_HEADS) {
 +  int actual;
 +  UI_SelectedHead(index, &actual);
 +  index = actual;
 +  if (index >= 0 && index < uiInfo.characterCount) {
 +    if (uiInfo.characterList[index].headImage == -1) {
 +      uiInfo.characterList[index].headImage = trap_R_RegisterShaderNoMip(uiInfo.characterList[index].imageName);
 +    }
 +    return uiInfo.characterList[index].headImage;
 +  }
 +  } else if (feederID == FEEDER_Q3HEADS) {
 +    if (index >= 0 && index < uiInfo.q3HeadCount) {
 +      return uiInfo.q3HeadIcons[index];
 +    }
 +  } else if (feederID == FEEDER_ALLMAPS || feederID == FEEDER_MAPS) {
 +    int actual;
 +    UI_SelectedMap(index, &actual);
 +    index = actual;
 +    if (index >= 0 && index < uiInfo.mapCount) {
 +      if (uiInfo.mapList[index].levelShot == -1) {
 +        uiInfo.mapList[index].levelShot = trap_R_RegisterShaderNoMip(uiInfo.mapList[index].imageName);
 +      }
 +      return uiInfo.mapList[index].levelShot;
 +    }
 +  }
 +  return 0;
 +}
 +
 +static void UI_FeederSelection(float feederID, int index) {
 +  static char info[MAX_STRING_CHARS];
 +  if (feederID == FEEDER_HEADS) {
 +  int actual;
 +  UI_SelectedHead(index, &actual);
 +  index = actual;
 +    if (index >= 0 && index < uiInfo.characterCount) {
 +    trap_Cvar_Set( "team_model", va("%s", uiInfo.characterList[index].base));
 +    trap_Cvar_Set( "team_headmodel", va("*%s", uiInfo.characterList[index].name));
 +    updateModel = qtrue;
 +    }
 +  } else if (feederID == FEEDER_Q3HEADS) {
 +    if (index >= 0 && index < uiInfo.q3HeadCount) {
 +      trap_Cvar_Set( "model", uiInfo.q3HeadNames[index]);
 +      trap_Cvar_Set( "headmodel", uiInfo.q3HeadNames[index]);
 +      updateModel = qtrue;
 +    }
 +  } else if (feederID == FEEDER_MAPS || feederID == FEEDER_ALLMAPS) {
 +    int actual, map;
 +    map = (feederID == FEEDER_ALLMAPS) ? ui_currentNetMap.integer : ui_currentMap.integer;
 +    if (uiInfo.mapList[map].cinematic >= 0) {
 +      trap_CIN_StopCinematic(uiInfo.mapList[map].cinematic);
 +      uiInfo.mapList[map].cinematic = -1;
 +    }
 +    UI_SelectedMap(index, &actual);
 +    trap_Cvar_Set("ui_mapIndex", va("%d", index));
 +    ui_mapIndex.integer = index;
 +
 +    if (feederID == FEEDER_MAPS) {
 +      ui_currentMap.integer = actual;
 +      trap_Cvar_Set("ui_currentMap", va("%d", actual));
 +      uiInfo.mapList[ui_currentMap.integer].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentMap.integer].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) );
 +      UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);
 +      trap_Cvar_Set("ui_opponentModel", uiInfo.mapList[ui_currentMap.integer].opponentName);
 +      updateOpponentModel = qtrue;
 +    } else {
 +      ui_currentNetMap.integer = actual;
 +      trap_Cvar_Set("ui_currentNetMap", va("%d", actual));
 +      uiInfo.mapList[ui_currentNetMap.integer].cinematic = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.mapList[ui_currentNetMap.integer].mapLoadName), 0, 0, 0, 0, (CIN_loop | CIN_silent) );
 +    }
 +
 +  } else if (feederID == FEEDER_SERVERS) {
 +    const char *mapName = NULL;
 +    uiInfo.serverStatus.currentServer = index;
 +    trap_LAN_GetServerInfo(ui_netSource.integer, uiInfo.serverStatus.displayServers[index], info, MAX_STRING_CHARS);
 +    uiInfo.serverStatus.currentServerPreview = trap_R_RegisterShaderNoMip(va("levelshots/%s", Info_ValueForKey(info, "mapname")));
 +    if (uiInfo.serverStatus.currentServerCinematic >= 0) {
 +      trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic);
 +      uiInfo.serverStatus.currentServerCinematic = -1;
 +    }
 +    mapName = Info_ValueForKey(info, "mapname");
 +    if (mapName && *mapName) {
 +      uiInfo.serverStatus.currentServerCinematic = trap_CIN_PlayCinematic(va("%s.roq", mapName), 0, 0, 0, 0, (CIN_loop | CIN_silent) );
 +    }
 +  } else if (feederID == FEEDER_SERVERSTATUS) {
 +    //
 +  } else if (feederID == FEEDER_FINDPLAYER) {
 +    uiInfo.currentFoundPlayerServer = index;
 +    //
 +    if ( index < uiInfo.numFoundPlayerServers-1) {
 +      // build a new server status for this server
 +      Q_strncpyz(uiInfo.serverStatusAddress, uiInfo.foundPlayerServerAddresses[uiInfo.currentFoundPlayerServer], sizeof(uiInfo.serverStatusAddress));
 +      Menu_SetFeederSelection(NULL, FEEDER_SERVERSTATUS, 0, NULL);
 +      UI_BuildServerStatus(qtrue);
 +    }
 +  } else if (feederID == FEEDER_PLAYER_LIST) {
 +    uiInfo.playerIndex = index;
 +  } else if (feederID == FEEDER_TEAM_LIST) {
 +    uiInfo.teamIndex = index;
 +  } else if (feederID == FEEDER_IGNORE_LIST) {
 +    uiInfo.ignoreIndex = index;
 +  } else if (feederID == FEEDER_MODS) {
 +    uiInfo.modIndex = index;
 +  } else if (feederID == FEEDER_CINEMATICS) {
 +    uiInfo.movieIndex = index;
 +    if (uiInfo.previewMovie >= 0) {
 +      trap_CIN_StopCinematic(uiInfo.previewMovie);
 +    }
 +    uiInfo.previewMovie = -1;
 +  } else if (feederID == FEEDER_DEMOS) {
 +    uiInfo.demoIndex = index;
 +  }
 +
 +//TA: tremulous menus
 +  else if( feederID == FEEDER_TREMTEAMS )
 +    uiInfo.tremTeamIndex = index;
 +  else if( feederID == FEEDER_TREMHUMANITEMS )
 +    uiInfo.tremHumanItemIndex = index;
 +  else if( feederID == FEEDER_TREMALIENCLASSES )
 +    uiInfo.tremAlienClassIndex = index;
 +  else if( feederID == FEEDER_TREMHUMANARMOURYBUY )
 +    uiInfo.tremHumanArmouryBuyIndex = index;
 +  else if( feederID == FEEDER_TREMHUMANARMOURYSELL )
 +    uiInfo.tremHumanArmourySellIndex = index;
 +  else if( feederID == FEEDER_TREMALIENUPGRADE )
 +    uiInfo.tremAlienUpgradeIndex = index;
 +  else if( feederID == FEEDER_TREMALIENBUILD )
 +    uiInfo.tremAlienBuildIndex = index;
 +  else if( feederID == FEEDER_TREMHUMANBUILD )
 +    uiInfo.tremHumanBuildIndex = index;
 +//TA: tremulous menus
 +}
 +
 +static void UI_Pause(qboolean b) {
 +  if (b) {
 +    // pause the game and set the ui keycatcher
 +    trap_Cvar_Set( "cl_paused", "1" );
 +    trap_Key_SetCatcher( KEYCATCH_UI );
 +  } else {
 +    // unpause the game and clear the ui keycatcher
 +    trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
 +    trap_Key_ClearStates();
 +    trap_Cvar_Set( "cl_paused", "0" );
 +  }
 +}
 +
 +static int UI_PlayCinematic(const char *name, float x, float y, float w, float h) {
 +  return trap_CIN_PlayCinematic(name, x, y, w, h, (CIN_loop | CIN_silent));
 +}
 +
 +static void UI_StopCinematic(int handle) {
 +  if (handle >= 0) {
 +    trap_CIN_StopCinematic(handle);
 +  } else {
 +    handle = abs(handle);
 +    if (handle == UI_MAPCINEMATIC) {
 +      if (uiInfo.mapList[ui_currentMap.integer].cinematic >= 0) {
 +        trap_CIN_StopCinematic(uiInfo.mapList[ui_currentMap.integer].cinematic);
 +        uiInfo.mapList[ui_currentMap.integer].cinematic = -1;
 +      }
 +    } else if (handle == UI_NETMAPCINEMATIC) {
 +      if (uiInfo.serverStatus.currentServerCinematic >= 0) {
 +        trap_CIN_StopCinematic(uiInfo.serverStatus.currentServerCinematic);
 +        uiInfo.serverStatus.currentServerCinematic = -1;
 +      }
 +    } else if (handle == UI_CLANCINEMATIC) {
 +      int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName"));
 +      if (i >= 0 && i < uiInfo.teamCount) {
 +        if (uiInfo.teamList[i].cinematic >= 0) {
 +          trap_CIN_StopCinematic(uiInfo.teamList[i].cinematic);
 +          uiInfo.teamList[i].cinematic = -1;
 +        }
 +      }
 +    }
 +  }
 +}
 +
 +static void UI_DrawCinematic(int handle, float x, float y, float w, float h) {
 +  trap_CIN_SetExtents(handle, x, y, w, h);
 +  trap_CIN_DrawCinematic(handle);
 +}
 +
 +static void UI_RunCinematicFrame(int handle) {
 +  trap_CIN_RunCinematic(handle);
 +}
 +
 +
 +/*
 +=================
 +UI_Init
 +=================
 +*/
 +void _UI_Init( qboolean inGameLoad ) {
 +  const char *menuSet;
 +  int start;
 +
 +  BG_InitClassOverrides( );
 +  BG_InitAllowedGameElements( );
 +
 +  //uiInfo.inGameLoad = inGameLoad;
 +
 +  UI_RegisterCvars();
 +  UI_InitMemory();
 +
 +  // cache redundant calulations
 +  trap_GetGlconfig( &uiInfo.uiDC.glconfig );
 +
 +  // for 640x480 virtualized screen
 +  uiInfo.uiDC.yscale = uiInfo.uiDC.glconfig.vidHeight * (1.0/480.0);
 +  uiInfo.uiDC.xscale = uiInfo.uiDC.glconfig.vidWidth * (1.0/640.0);
 +  if ( uiInfo.uiDC.glconfig.vidWidth * 480 > uiInfo.uiDC.glconfig.vidHeight * 640 ) {
 +    // wide screen
 +    uiInfo.uiDC.bias = 0.5 * ( uiInfo.uiDC.glconfig.vidWidth - ( uiInfo.uiDC.glconfig.vidHeight * (640.0/480.0) ) );
 +  }
 +  else {
 +    // no wide screen
 +    uiInfo.uiDC.bias = 0;
 +  }
 +
 +
 +  //UI_Load();
 +  uiInfo.uiDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip;
 +  uiInfo.uiDC.setColor = &UI_SetColor;
 +  uiInfo.uiDC.drawHandlePic = &UI_DrawHandlePic;
 +  uiInfo.uiDC.drawStretchPic = &trap_R_DrawStretchPic;
 +  uiInfo.uiDC.drawText = &Text_Paint;
 +  uiInfo.uiDC.textWidth = &Text_Width;
 +  uiInfo.uiDC.textHeight = &Text_Height;
 +  uiInfo.uiDC.registerModel = &trap_R_RegisterModel;
 +  uiInfo.uiDC.modelBounds = &trap_R_ModelBounds;
 +  uiInfo.uiDC.fillRect = &UI_FillRect;
 +  uiInfo.uiDC.drawRect = &_UI_DrawRect;
 +  uiInfo.uiDC.drawSides = &_UI_DrawSides;
 +  uiInfo.uiDC.drawTopBottom = &_UI_DrawTopBottom;
 +  uiInfo.uiDC.clearScene = &trap_R_ClearScene;
 +  uiInfo.uiDC.drawSides = &_UI_DrawSides;
 +  uiInfo.uiDC.addRefEntityToScene = &trap_R_AddRefEntityToScene;
 +  uiInfo.uiDC.renderScene = &trap_R_RenderScene;
 +  uiInfo.uiDC.registerFont = &trap_R_RegisterFont;
 +  uiInfo.uiDC.ownerDrawItem = &UI_OwnerDraw;
 +  uiInfo.uiDC.getValue = &UI_GetValue;
 +  uiInfo.uiDC.ownerDrawVisible = &UI_OwnerDrawVisible;
 +  uiInfo.uiDC.runScript = &UI_RunMenuScript;
 +  uiInfo.uiDC.getTeamColor = &UI_GetTeamColor;
 +  uiInfo.uiDC.setCVar = trap_Cvar_Set;
 +  uiInfo.uiDC.getCVarString = trap_Cvar_VariableStringBuffer;
 +  uiInfo.uiDC.getCVarValue = trap_Cvar_VariableValue;
 +  uiInfo.uiDC.drawTextWithCursor = &Text_PaintWithCursor;
 +  uiInfo.uiDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode;
 +  uiInfo.uiDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode;
 +  uiInfo.uiDC.startLocalSound = &trap_S_StartLocalSound;
 +  uiInfo.uiDC.ownerDrawHandleKey = &UI_OwnerDrawHandleKey;
 +  uiInfo.uiDC.feederCount = &UI_FeederCount;
 +  uiInfo.uiDC.feederItemImage = &UI_FeederItemImage;
 +  uiInfo.uiDC.feederItemText = &UI_FeederItemText;
 +  uiInfo.uiDC.feederSelection = &UI_FeederSelection;
 +  uiInfo.uiDC.setBinding = &trap_Key_SetBinding;
 +  uiInfo.uiDC.getBindingBuf = &trap_Key_GetBindingBuf;
 +  uiInfo.uiDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf;
 +  uiInfo.uiDC.executeText = &trap_Cmd_ExecuteText;
 +  uiInfo.uiDC.Error = &Com_Error;
 +  uiInfo.uiDC.Print = &Com_Printf;
 +  uiInfo.uiDC.Pause = &UI_Pause;
 +  uiInfo.uiDC.ownerDrawWidth = &UI_OwnerDrawWidth;
 +  uiInfo.uiDC.registerSound = &trap_S_RegisterSound;
 +  uiInfo.uiDC.startBackgroundTrack = &trap_S_StartBackgroundTrack;
 +  uiInfo.uiDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack;
 +  uiInfo.uiDC.playCinematic = &UI_PlayCinematic;
 +  uiInfo.uiDC.stopCinematic = &UI_StopCinematic;
 +  uiInfo.uiDC.drawCinematic = &UI_DrawCinematic;
 +  uiInfo.uiDC.runCinematicFrame = &UI_RunCinematicFrame;
 +
 +  Init_Display(&uiInfo.uiDC);
 +
 +  String_Init();
 +
 +  uiInfo.uiDC.whiteShader = trap_R_RegisterShaderNoMip( "white" );
 +
 +  AssetCache();
 +
 +  start = trap_Milliseconds();
 +
 +  uiInfo.teamCount = 0;
 +  uiInfo.characterCount = 0;
 +  uiInfo.aliasCount = 0;
 +
 +/*  UI_ParseTeamInfo("teaminfo.txt");
 +  UI_LoadTeams();
 +  UI_ParseGameInfo("gameinfo.txt");*/
 +
 +  menuSet = UI_Cvar_VariableString("ui_menuFiles");
 +  if (menuSet == NULL || menuSet[0] == '\0') {
 +    menuSet = "ui/menus.txt";
 +  }
 +
 +#if 0
 +  if (uiInfo.inGameLoad) {
 +    UI_LoadMenus("ui/ingame.txt", qtrue);
 +  } else { // bk010222: left this: UI_LoadMenus(menuSet, qtrue);
 +  }
 +#else
 +  UI_LoadMenus(menuSet, qtrue);
 +  UI_LoadMenus("ui/ingame.txt", qfalse);
 +  UI_LoadMenus("ui/tremulous.txt", qfalse);
 +
 +  UI_LoadInfoPanes( "ui/infopanes.def" );
 +
 +  if( uiInfo.uiDC.debug )
 +  {
 +    int i, j;
 +
 +    for( i = 0; i < uiInfo.tremInfoPaneCount; i++ )
 +    {
 +      Com_Printf( "name: %s\n", uiInfo.tremInfoPanes[ i ].name );
 +
 +      Com_Printf( "text: %s\n", uiInfo.tremInfoPanes[ i ].text );
 +
 +      for( j = 0; j < uiInfo.tremInfoPanes[ i ].numGraphics; j++ )
 +        Com_Printf( "graphic %d: %d %d %d %d\n", j, uiInfo.tremInfoPanes[ i ].graphics[ j ].side,
 +                                                    uiInfo.tremInfoPanes[ i ].graphics[ j ].offset,
 +                                                    uiInfo.tremInfoPanes[ i ].graphics[ j ].width,
 +                                                    uiInfo.tremInfoPanes[ i ].graphics[ j ].height );
 +    }
 +  }
 +#endif
 +
 +  Menus_CloseAll();
 +
 +  trap_LAN_LoadCachedServers();
 +  UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum);
 +
 +  // sets defaults for ui temp cvars
 +  uiInfo.effectsColor = gamecodetoui[(int)trap_Cvar_VariableValue("color1")-1];
 +  uiInfo.currentCrosshair = (int)trap_Cvar_VariableValue("cg_drawCrosshair");
 +  trap_Cvar_Set("ui_mousePitch", (trap_Cvar_VariableValue("m_pitch") >= 0) ? "0" : "1");
 +
 +  uiInfo.serverStatus.currentServerCinematic = -1;
 +  uiInfo.previewMovie = -1;
 +
 +  if (trap_Cvar_VariableValue("ui_TeamArenaFirstRun") == 0) {
 +    trap_Cvar_Set("s_volume", "0.8");
 +    trap_Cvar_Set("s_musicvolume", "0.5");
 +    trap_Cvar_Set("ui_TeamArenaFirstRun", "1");
 +  }
 +
 +  trap_Cvar_Register(NULL, "debug_protocol", "", 0 );
 +
 +  trap_Cvar_Set("ui_actualNetGameType", va("%d", ui_netGameType.integer));
 +}
 +
 +
 +/*
 +=================
 +UI_KeyEvent
 +=================
 +*/
 +void _UI_KeyEvent( int key, qboolean down ) {
 +
 +  if (Menu_Count() > 0) {
 +    menuDef_t *menu = Menu_GetFocused();
 +    if (menu) {
 +      if (key == K_ESCAPE && down && !Menus_AnyFullScreenVisible()) {
 +        Menus_CloseAll();
 +      } else {
 +        Menu_HandleKey(menu, key, down );
 +      }
 +    } else {
 +      trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
 +      trap_Key_ClearStates();
 +      trap_Cvar_Set( "cl_paused", "0" );
 +    }
 +  }
 +
 +  //if ((s > 0) && (s != menu_null_sound)) {
 +  //  trap_S_StartLocalSound( s, CHAN_LOCAL_SOUND );
 +  //}
 +}
 +
 +/*
 +=================
 +UI_MouseEvent
 +=================
 +*/
 +void _UI_MouseEvent( int dx, int dy )
 +{
 +  // update mouse screen position
 +  uiInfo.uiDC.cursorx += dx;
 +  if (uiInfo.uiDC.cursorx < 0)
 +    uiInfo.uiDC.cursorx = 0;
 +  else if (uiInfo.uiDC.cursorx > SCREEN_WIDTH)
 +    uiInfo.uiDC.cursorx = SCREEN_WIDTH;
 +
 +  uiInfo.uiDC.cursory += dy;
 +  if (uiInfo.uiDC.cursory < 0)
 +    uiInfo.uiDC.cursory = 0;
 +  else if (uiInfo.uiDC.cursory > SCREEN_HEIGHT)
 +    uiInfo.uiDC.cursory = SCREEN_HEIGHT;
 +
 +  if (Menu_Count() > 0) {
 +    //menuDef_t *menu = Menu_GetFocused();
 +    //Menu_HandleMouseMove(menu, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory);
 +    Display_MouseMove(NULL, uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory);
 +  }
 +
 +}
 +
 +void UI_LoadNonIngame( void ) {
 +  const char *menuSet = UI_Cvar_VariableString("ui_menuFiles");
 +  if (menuSet == NULL || menuSet[0] == '\0') {
 +    menuSet = "ui/menus.txt";
 +  }
 +  UI_LoadMenus(menuSet, qfalse);
 +  uiInfo.inGameLoad = qfalse;
 +}
 +
 +void _UI_SetActiveMenu( uiMenuCommand_t menu ) {
 +  char buf[256];
 +
 +  // this should be the ONLY way the menu system is brought up
 +  // enusure minumum menu data is cached
 +  if (Menu_Count() > 0) {
 +    vec3_t v;
 +    v[0] = v[1] = v[2] = 0;
 +    switch ( menu ) {
 +    case UIMENU_NONE:
 +      trap_Key_SetCatcher( trap_Key_GetCatcher() & ~KEYCATCH_UI );
 +      trap_Key_ClearStates();
 +      trap_Cvar_Set( "cl_paused", "0" );
 +      Menus_CloseAll();
 +
 +      return;
 +    case UIMENU_MAIN:
 +      //trap_Cvar_Set( "sv_killserver", "1" );
 +      trap_Key_SetCatcher( KEYCATCH_UI );
 +      //trap_S_StartLocalSound( trap_S_RegisterSound("sound/misc/menu_background.wav", qfalse) , CHAN_LOCAL_SOUND );
 +      //trap_S_StartBackgroundTrack("sound/misc/menu_background.wav", NULL);
 +      if (uiInfo.inGameLoad) {
 +        UI_LoadNonIngame();
 +      }
 +      Menus_CloseAll();
 +      Menus_ActivateByName("main");
 +      trap_Cvar_Set( "ui_loading", "0" );
 +      trap_Cvar_VariableStringBuffer("com_errorMessage", buf, sizeof(buf));
 +      if (strlen(buf)) {
 +        if (!ui_singlePlayerActive.integer) {
 +          if( trap_Cvar_VariableValue( "com_errorCode" ) == ERR_SERVERDISCONNECT )
 +            Menus_ActivateByName("drop_popmenu");
 +          else
 +            Menus_ActivateByName("error_popmenu");
 +        } else {
 +          trap_Cvar_Set("com_errorMessage", "");
 +        }
 +      }
 +      return;
 +    case UIMENU_TEAM:
 +      trap_Key_SetCatcher( KEYCATCH_UI );
 +      Menus_ActivateByName("team");
 +      return;
 +    case UIMENU_POSTGAME:
 +      //trap_Cvar_Set( "sv_killserver", "1" );
 +      trap_Key_SetCatcher( KEYCATCH_UI );
 +      if (uiInfo.inGameLoad) {
 +        UI_LoadNonIngame();
 +      }
 +      Menus_CloseAll();
 +      Menus_ActivateByName("endofgame");
 +      return;
 +    case UIMENU_INGAME:
 +      trap_Cvar_Set( "cl_paused", "1" );
 +      trap_Key_SetCatcher( KEYCATCH_UI );
 +      UI_BuildPlayerList();
 +      Menus_CloseAll();
 +      Menus_ActivateByName("ingame");
 +      return;
 +    }
 +  }
 +}
 +
 +qboolean _UI_IsFullscreen( void ) {
 +  return Menus_AnyFullScreenVisible();
 +}
 +
 +
 +
 +static connstate_t  lastConnState;
 +static char     lastLoadingText[MAX_INFO_VALUE];
 +
 +static void UI_ReadableSize ( char *buf, int bufsize, int value )
 +{
 +  if (value > 1024*1024*1024 ) { // gigs
 +    Com_sprintf( buf, bufsize, "%d", value / (1024*1024*1024) );
 +    Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d GB",
 +      (value % (1024*1024*1024))*100 / (1024*1024*1024) );
 +  } else if (value > 1024*1024 ) { // megs
 +    Com_sprintf( buf, bufsize, "%d", value / (1024*1024) );
 +    Com_sprintf( buf+strlen(buf), bufsize-strlen(buf), ".%02d MB",
 +      (value % (1024*1024))*100 / (1024*1024) );
 +  } else if (value > 1024 ) { // kilos
 +    Com_sprintf( buf, bufsize, "%d KB", value / 1024 );
 +  } else { // bytes
 +    Com_sprintf( buf, bufsize, "%d bytes", value );
 +  }
 +}
 +
 +// Assumes time is in msec
 +static void UI_PrintTime ( char *buf, int bufsize, int time ) {
 +  time /= 1000;  // change to seconds
 +
 +  if (time > 3600) { // in the hours range
 +    Com_sprintf( buf, bufsize, "%d hr %d min", time / 3600, (time % 3600) / 60 );
 +  } else if (time > 60) { // mins
 +    Com_sprintf( buf, bufsize, "%d min %d sec", time / 60, time % 60 );
 +  } else  { // secs
 +    Com_sprintf( buf, bufsize, "%d sec", time );
 +  }
 +}
 +
 +void Text_PaintCenter(float x, float y, float scale, vec4_t color, const char *text, float adjust) {
 +  int len = Text_Width(text, scale, 0);
 +  Text_Paint(x - len / 2, y, scale, color, text, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE);
 +}
 +
 +void Text_PaintCenter_AutoWrapped(float x, float y, float xmax, float ystep, float scale, vec4_t color, const char *str, float adjust) {
 +  int width;
 +  char *s1,*s2,*s3;
 +  char c_bcp;
 +  char buf[1024];
 +
 +  if (!str || str[0]=='\0')
 +    return;
 +
 +  Q_strncpyz(buf, str, sizeof(buf));
 +  s1 = s2 = s3 = buf;
 +
 +  while (1) {
 +    do {
 +      s3++;
 +    } while (*s3!=' ' && *s3!='\0');
 +    c_bcp = *s3;
 +    *s3 = '\0';
 +    width = Text_Width(s1, scale, 0);
 +    *s3 = c_bcp;
 +    if (width > xmax) {
 +      if (s1==s2)
 +      {
 +        // fuck, don't have a clean cut, we'll overflow
 +        s2 = s3;
 +      }
 +      *s2 = '\0';
 +      Text_PaintCenter(x, y, scale, color, s1, adjust);
 +      y += ystep;
 +      if (c_bcp == '\0')
 +      {
 +        // that was the last word
 +        // we could start a new loop, but that wouldn't be much use
 +        // even if the word is too long, we would overflow it (see above)
 +        // so just print it now if needed
 +        s2++;
 +        if (*s2 != '\0') // if we are printing an overflowing line we have s2 == s3
 +          Text_PaintCenter(x, y, scale, color, s2, adjust);
 +        break;
 +      }
 +      s2++;
 +      s1 = s2;
 +      s3 = s2;
 +    }
 +    else
 +    {
 +      s2 = s3;
 +      if (c_bcp == '\0') // we reached the end
 +      {
 +        Text_PaintCenter(x, y, scale, color, s1, adjust);
 +        break;
 +      }
 +    }
 +  }
 +}
 +
 +
 +static void UI_DisplayDownloadInfo( const char *downloadName, float centerPoint, float yStart, float scale ) {
 +  static char dlText[]  = "Downloading:";
 +  static char etaText[] = "Estimated time left:";
 +  static char xferText[]  = "Transfer rate:";
 +
 +  int downloadSize, downloadCount, downloadTime;
 +  char dlSizeBuf[64], totalSizeBuf[64], xferRateBuf[64], dlTimeBuf[64];
 +  int xferRate;
 +  int leftWidth;
 +  const char *s;
 +
 +  downloadSize = trap_Cvar_VariableValue( "cl_downloadSize" );
 +  downloadCount = trap_Cvar_VariableValue( "cl_downloadCount" );
 +  downloadTime = trap_Cvar_VariableValue( "cl_downloadTime" );
 +
 +  leftWidth = 320;
 +
 +  UI_SetColor(colorWhite);
 +  Text_PaintCenter(centerPoint, yStart + 112, scale, colorWhite, dlText, 0);
 +  Text_PaintCenter(centerPoint, yStart + 192, scale, colorWhite, etaText, 0);
 +  Text_PaintCenter(centerPoint, yStart + 248, scale, colorWhite, xferText, 0);
 +
 +  if (downloadSize > 0) {
 +    s = va( "%s (%d%%)", downloadName, downloadCount * 100 / downloadSize );
 +  } else {
 +    s = downloadName;
 +  }
 +
 +  Text_PaintCenter(centerPoint, yStart+136, scale, colorWhite, s, 0);
 +
 +  UI_ReadableSize( dlSizeBuf,   sizeof dlSizeBuf,   downloadCount );
 +  UI_ReadableSize( totalSizeBuf,  sizeof totalSizeBuf,  downloadSize );
 +
 +  if (downloadCount < 4096 || !downloadTime) {
 +    Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, "estimating", 0);
 +    Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0);
 +  } else {
 +    if ((uiInfo.uiDC.realTime - downloadTime) / 1000) {
 +      xferRate = downloadCount / ((uiInfo.uiDC.realTime - downloadTime) / 1000);
 +    } else {
 +      xferRate = 0;
 +    }
 +    UI_ReadableSize( xferRateBuf, sizeof xferRateBuf, xferRate );
 +
 +    // Extrapolate estimated completion time
 +    if (downloadSize && xferRate) {
 +      int n = downloadSize / xferRate; // estimated time for entire d/l in secs
 +
 +      // We do it in K (/1024) because we'd overflow around 4MB
 +      UI_PrintTime ( dlTimeBuf, sizeof dlTimeBuf,
 +        (n - (((downloadCount/1024) * n) / (downloadSize/1024))) * 1000);
 +
 +      Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, dlTimeBuf, 0);
 +      Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0);
 +    } else {
 +      Text_PaintCenter(leftWidth, yStart+216, scale, colorWhite, "estimating", 0);
 +      if (downloadSize) {
 +        Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s of %s copied)", dlSizeBuf, totalSizeBuf), 0);
 +      } else {
 +        Text_PaintCenter(leftWidth, yStart+160, scale, colorWhite, va("(%s copied)", dlSizeBuf), 0);
 +      }
 +    }
 +
 +    if (xferRate) {
 +      Text_PaintCenter(leftWidth, yStart+272, scale, colorWhite, va("%s/Sec", xferRateBuf), 0);
 +    }
 +  }
 +}
 +
 +/*
 +========================
 +UI_DrawConnectScreen
 +
 +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];
 +  char text[256];
 +  float centerPoint, yStart, scale;
 +
 +  menuDef_t *menu = Menus_FindByName("Connect");
 +
 +
 +  if ( !overlay && menu ) {
 +    Menu_Paint(menu, qtrue);
 +  }
 +
 +  if (!overlay) {
 +    centerPoint = 320;
 +    yStart = 130;
 +    scale = 0.5f;
 +  } else {
 +    centerPoint = 320;
 +    yStart = 32;
 +    scale = 0.6f;
 +    return;
 +  }
 +
 +  // see what information we should display
 +  trap_GetClientState( &cstate );
 +
 +  info[0] = '\0';
 +  if( trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ) ) {
 +    Text_PaintCenter(centerPoint, yStart, scale, colorWhite, va( "Loading %s", Info_ValueForKey( info, "mapname" )), 0);
 +  }
 +
 +  if (!Q_stricmp(cstate.servername,"localhost")) {
 +    Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite, va("Starting up..."), ITEM_TEXTSTYLE_SHADOWEDMORE);
 +  } else {
 +    strcpy(text, va("Connecting to %s", cstate.servername));
 +    Text_PaintCenter(centerPoint, yStart + 48, scale, colorWhite,text , ITEM_TEXTSTYLE_SHADOWEDMORE);
 +  }
 +
 +
 +  // display global MOTD at bottom
 +  Text_PaintCenter(centerPoint, 600, scale, colorWhite, Info_ValueForKey( cstate.updateInfoString, "motd" ), 0);
 +  // print any server info (server full, bad version, etc)
 +  if ( cstate.connState < CA_CONNECTED ) {
 +    Text_PaintCenter_AutoWrapped(centerPoint, yStart + 176, 630, 20, scale, colorWhite, cstate.messageString, 0);
 +  }
 +
 +  if ( lastConnState > cstate.connState ) {
 +    lastLoadingText[0] = '\0';
 +  }
 +  lastConnState = cstate.connState;
 +
 +  switch ( cstate.connState ) {
 +  case CA_CONNECTING:
 +    s = va("Awaiting connection...%i", cstate.connectPacketCount);
 +    break;
 +  case CA_CHALLENGING:
 +    s = va("Awaiting challenge...%i", cstate.connectPacketCount);
 +    break;
 +  case CA_CONNECTED: {
 +    char downloadName[MAX_INFO_VALUE];
 +
 +      trap_Cvar_VariableStringBuffer( "cl_downloadName", downloadName, sizeof(downloadName) );
 +      if (*downloadName) {
 +        UI_DisplayDownloadInfo( downloadName, centerPoint, yStart, scale );
 +        return;
 +      }
 +    }
 +    s = "Awaiting gamestate...";
 +    break;
 +  case CA_LOADING:
 +    return;
 +  case CA_PRIMED:
 +    return;
 +  default:
 +    return;
 +  }
 +
 +
 +  if (Q_stricmp(cstate.servername,"localhost")) {
 +    Text_PaintCenter(centerPoint, yStart + 80, scale, colorWhite, s, 0);
 +  }
 +
 +  // password required / connection rejected information goes here
 +}
 +
 +
 +/*
 +================
 +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_redteam;
 +vmCvar_t  ui_redteam1;
 +vmCvar_t  ui_redteam2;
 +vmCvar_t  ui_redteam3;
 +vmCvar_t  ui_redteam4;
 +vmCvar_t  ui_redteam5;
 +vmCvar_t  ui_blueteam;
 +vmCvar_t  ui_blueteam1;
 +vmCvar_t  ui_blueteam2;
 +vmCvar_t  ui_blueteam3;
 +vmCvar_t  ui_blueteam4;
 +vmCvar_t  ui_blueteam5;
 +vmCvar_t  ui_teamName;
 +vmCvar_t  ui_dedicated;
 +vmCvar_t  ui_gameType;
 +vmCvar_t  ui_netGameType;
 +vmCvar_t  ui_actualNetGameType;
 +vmCvar_t  ui_joinGameType;
 +vmCvar_t  ui_netSource;
 +vmCvar_t  ui_serverFilterType;
 +vmCvar_t  ui_opponentName;
 +vmCvar_t  ui_menuFiles;
 +vmCvar_t  ui_currentTier;
 +vmCvar_t  ui_currentMap;
 +vmCvar_t  ui_currentNetMap;
 +vmCvar_t  ui_mapIndex;
 +vmCvar_t  ui_currentOpponent;
 +vmCvar_t  ui_selectedPlayer;
 +vmCvar_t  ui_selectedPlayerName;
 +vmCvar_t  ui_lastServerRefresh_0;
 +vmCvar_t  ui_lastServerRefresh_1;
 +vmCvar_t  ui_lastServerRefresh_2;
 +vmCvar_t  ui_lastServerRefresh_3;
 +vmCvar_t  ui_lastServerRefresh_0_time;
 +vmCvar_t  ui_lastServerRefresh_1_time;
 +vmCvar_t  ui_lastServerRefresh_2_time;
 +vmCvar_t  ui_lastServerRefresh_3_time;
 +vmCvar_t  ui_singlePlayerActive;
 +vmCvar_t  ui_scoreAccuracy;
 +vmCvar_t  ui_scoreImpressives;
 +vmCvar_t  ui_scoreExcellents;
 +vmCvar_t  ui_scoreCaptures;
 +vmCvar_t  ui_scoreDefends;
 +vmCvar_t  ui_scoreAssists;
 +vmCvar_t  ui_scoreGauntlets;
 +vmCvar_t  ui_scoreScore;
 +vmCvar_t  ui_scorePerfect;
 +vmCvar_t  ui_scoreTeam;
 +vmCvar_t  ui_scoreBase;
 +vmCvar_t  ui_scoreTimeBonus;
 +vmCvar_t  ui_scoreSkillBonus;
 +vmCvar_t  ui_scoreShutoutBonus;
 +vmCvar_t  ui_scoreTime;
 +vmCvar_t  ui_captureLimit;
 +vmCvar_t  ui_fragLimit;
 +vmCvar_t  ui_smallFont;
 +vmCvar_t  ui_bigFont;
 +vmCvar_t  ui_findPlayer;
 +vmCvar_t  ui_Q3Model;
 +vmCvar_t  ui_hudFiles;
 +vmCvar_t  ui_recordSPDemo;
 +vmCvar_t  ui_realCaptureLimit;
 +vmCvar_t  ui_realWarmUp;
 +vmCvar_t  ui_serverStatusTimeOut;
 +
 +//TA: bank values
 +vmCvar_t  ui_bank;
 +vmCvar_t  ui_winner;
 +
 +// bk001129 - made static to avoid aliasing
 +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 | CVAR_ROM },
 +  { &ui_spScores2, "g_spScores2", "", CVAR_ARCHIVE | CVAR_ROM },
 +  { &ui_spScores3, "g_spScores3", "", CVAR_ARCHIVE | CVAR_ROM },
 +  { &ui_spScores4, "g_spScores4", "", CVAR_ARCHIVE | CVAR_ROM },
 +  { &ui_spScores5, "g_spScores5", "", CVAR_ARCHIVE | CVAR_ROM },
 +  { &ui_spAwards, "g_spAwards", "", CVAR_ARCHIVE | CVAR_ROM },
 +  { &ui_spVideos, "g_spVideos", "", CVAR_ARCHIVE | CVAR_ROM },
 +  { &ui_spSkill, "g_spSkill", "2", CVAR_ARCHIVE },
 +
 +  { &ui_spSelection, "ui_spSelection", "", CVAR_ROM },
 +  { &ui_winner, "ui_winner", "", 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_new, "ui_new", "0", CVAR_TEMP },
 +  { &ui_debug, "ui_debug", "0", CVAR_TEMP },
 +  { &ui_initialized, "ui_initialized", "0", CVAR_TEMP },
 +  { &ui_teamName, "ui_teamName", "Pagans", CVAR_ARCHIVE },
 +  { &ui_opponentName, "ui_opponentName", "Stroggs", CVAR_ARCHIVE },
 +  { &ui_redteam, "ui_redteam", "Pagans", CVAR_ARCHIVE },
 +  { &ui_blueteam, "ui_blueteam", "Stroggs", CVAR_ARCHIVE },
 +  { &ui_dedicated, "ui_dedicated", "0", CVAR_ARCHIVE },
 +  { &ui_gameType, "ui_gametype", "3", CVAR_ARCHIVE },
 +  { &ui_joinGameType, "ui_joinGametype", "0", CVAR_ARCHIVE },
 +  { &ui_netGameType, "ui_netGametype", "3", CVAR_ARCHIVE },
 +  { &ui_actualNetGameType, "ui_actualNetGametype", "3", CVAR_ARCHIVE },
 +  { &ui_redteam1, "ui_redteam1", "0", CVAR_ARCHIVE },
 +  { &ui_redteam2, "ui_redteam2", "0", CVAR_ARCHIVE },
 +  { &ui_redteam3, "ui_redteam3", "0", CVAR_ARCHIVE },
 +  { &ui_redteam4, "ui_redteam4", "0", CVAR_ARCHIVE },
 +  { &ui_redteam5, "ui_redteam5", "0", CVAR_ARCHIVE },
 +  { &ui_blueteam1, "ui_blueteam1", "0", CVAR_ARCHIVE },
 +  { &ui_blueteam2, "ui_blueteam2", "0", CVAR_ARCHIVE },
 +  { &ui_blueteam3, "ui_blueteam3", "0", CVAR_ARCHIVE },
 +  { &ui_blueteam4, "ui_blueteam4", "0", CVAR_ARCHIVE },
 +  { &ui_blueteam5, "ui_blueteam5", "0", CVAR_ARCHIVE },
 +  { &ui_netSource, "ui_netSource", "0", CVAR_ARCHIVE },
 +  { &ui_menuFiles, "ui_menuFiles", "ui/menus.txt", CVAR_ARCHIVE },
 +  { &ui_currentTier, "ui_currentTier", "0", CVAR_ARCHIVE },
 +  { &ui_currentMap, "ui_currentMap", "0", CVAR_ARCHIVE },
 +  { &ui_currentNetMap, "ui_currentNetMap", "0", CVAR_ARCHIVE },
 +  { &ui_mapIndex, "ui_mapIndex", "0", CVAR_ARCHIVE },
 +  { &ui_currentOpponent, "ui_currentOpponent", "0", CVAR_ARCHIVE },
 +  { &ui_selectedPlayer, "cg_selectedPlayer", "0", CVAR_ARCHIVE},
 +  { &ui_selectedPlayerName, "cg_selectedPlayerName", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_0, "ui_lastServerRefresh_0", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_1, "ui_lastServerRefresh_1", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_2, "ui_lastServerRefresh_2", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_3, "ui_lastServerRefresh_3", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_0, "ui_lastServerRefresh_0_time", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_1, "ui_lastServerRefresh_1_time", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_2, "ui_lastServerRefresh_2_time", "", CVAR_ARCHIVE},
 +  { &ui_lastServerRefresh_3, "ui_lastServerRefresh_3_time", "", CVAR_ARCHIVE},
 +  { &ui_singlePlayerActive, "ui_singlePlayerActive", "0", 0},
 +  { &ui_scoreAccuracy, "ui_scoreAccuracy", "0", CVAR_ARCHIVE},
 +  { &ui_scoreImpressives, "ui_scoreImpressives", "0", CVAR_ARCHIVE},
 +  { &ui_scoreExcellents, "ui_scoreExcellents", "0", CVAR_ARCHIVE},
 +  { &ui_scoreCaptures, "ui_scoreCaptures", "0", CVAR_ARCHIVE},
 +  { &ui_scoreDefends, "ui_scoreDefends", "0", CVAR_ARCHIVE},
 +  { &ui_scoreAssists, "ui_scoreAssists", "0", CVAR_ARCHIVE},
 +  { &ui_scoreGauntlets, "ui_scoreGauntlets", "0",CVAR_ARCHIVE},
 +  { &ui_scoreScore, "ui_scoreScore", "0", CVAR_ARCHIVE},
 +  { &ui_scorePerfect, "ui_scorePerfect", "0", CVAR_ARCHIVE},
 +  { &ui_scoreTeam, "ui_scoreTeam", "0 to 0", CVAR_ARCHIVE},
 +  { &ui_scoreBase, "ui_scoreBase", "0", CVAR_ARCHIVE},
 +  { &ui_scoreTime, "ui_scoreTime", "00:00", CVAR_ARCHIVE},
 +  { &ui_scoreTimeBonus, "ui_scoreTimeBonus", "0", CVAR_ARCHIVE},
 +  { &ui_scoreSkillBonus, "ui_scoreSkillBonus", "0", CVAR_ARCHIVE},
 +  { &ui_scoreShutoutBonus, "ui_scoreShutoutBonus", "0", CVAR_ARCHIVE},
 +  { &ui_fragLimit, "ui_fragLimit", "10", 0},
 +  { &ui_captureLimit, "ui_captureLimit", "5", 0},
 +  { &ui_smallFont, "ui_smallFont", "0.2", CVAR_ARCHIVE},
 +  { &ui_bigFont, "ui_bigFont", "0.5", CVAR_ARCHIVE},
 +  { &ui_findPlayer, "ui_findPlayer", "Sarge", CVAR_ARCHIVE},
 +  { &ui_Q3Model, "ui_q3model", "0", CVAR_ARCHIVE},
 +  { &ui_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE},
 +  { &ui_recordSPDemo, "ui_recordSPDemo", "0", CVAR_ARCHIVE},
 +  { &ui_teamArenaFirstRun, "ui_teamArenaFirstRun", "0", CVAR_ARCHIVE},
 +  { &ui_realWarmUp, "g_warmup", "20", CVAR_ARCHIVE},
 +  { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART},
 +  { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE},
 +  { &ui_bank, "ui_bank", "0", 0 },
 +};
 +
 +// bk001129 - made static to avoid aliasing
 +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 );
 +  }
 +}
 +
 +
 +/*
 +=================
 +ArenaServers_StopRefresh
 +=================
 +*/
 +static void UI_StopServerRefresh( void )
 +{
 +  int count;
 +
 +  if (!uiInfo.serverStatus.refreshActive) {
 +    // not currently refreshing
 +    return;
 +  }
 +  uiInfo.serverStatus.refreshActive = qfalse;
 +  Com_Printf("%d servers listed in browser with %d players.\n",
 +          uiInfo.serverStatus.numDisplayServers,
 +          uiInfo.serverStatus.numPlayersOnServers);
 +  count = trap_LAN_GetServerCount(ui_netSource.integer);
 +  if (count - uiInfo.serverStatus.numDisplayServers > 0) {
 +    Com_Printf("%d servers not listed due to packet loss or pings higher than %d\n",
 +            count - uiInfo.serverStatus.numDisplayServers,
 +            (int) trap_Cvar_VariableValue("cl_maxPing"));
 +  }
 +
 +}
 +
 +/*
 +=================
 +UI_DoServerRefresh
 +=================
 +*/
 +static void UI_DoServerRefresh( void )
 +{
 +  qboolean wait = qfalse;
 +
 +  if (!uiInfo.serverStatus.refreshActive) {
 +    return;
 +  }
 +  if (ui_netSource.integer != AS_FAVORITES) {
 +    if (ui_netSource.integer == AS_LOCAL) {
 +      if (!trap_LAN_GetServerCount(ui_netSource.integer)) {
 +        wait = qtrue;
 +      }
 +    } else {
 +      if (trap_LAN_GetServerCount(ui_netSource.integer) < 0) {
 +        wait = qtrue;
 +      }
 +    }
 +  }
 +
 +  if (uiInfo.uiDC.realTime < uiInfo.serverStatus.refreshtime) {
 +    if (wait) {
 +      return;
 +    }
 +  }
 +
 +  // if still trying to retrieve pings
 +  if (trap_LAN_UpdateVisiblePings(ui_netSource.integer)) {
 +    uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
 +  } else if (!wait) {
 +    // get the last servers in the list
 +    UI_BuildServerDisplayList(2);
 +    // stop the refresh
 +    UI_StopServerRefresh();
 +  }
 +  //
 +  UI_BuildServerDisplayList(qfalse);
 +}
 +
 +/*
 +=================
 +UI_StartServerRefresh
 +=================
 +*/
 +static void UI_StartServerRefresh(qboolean full)
 +{
 +  int   i;
 +  char  *ptr;
 +  int   time;
 +  qtime_t q;
 +
 +  time = trap_RealTime(&q);
 +  trap_Cvar_Set( va("ui_lastServerRefresh_%i_time", ui_netSource.integer ),
 +                        va( "%i", time ) );
 +  trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer),
 +			va("%s-%i, %i at %i:%02i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min));
 +
 +  if (!full) {
 +    UI_UpdatePendingPings();
 +    return;
 +  }
 +
 +  uiInfo.serverStatus.refreshActive = qtrue;
 +  uiInfo.serverStatus.nextDisplayRefresh = uiInfo.uiDC.realTime + 1000;
 +  // clear number of displayed servers
 +  uiInfo.serverStatus.numDisplayServers = 0;
 +  uiInfo.serverStatus.numPlayersOnServers = 0;
 +  // mark all servers as visible so we store ping updates for them
 +  trap_LAN_MarkServerVisible(ui_netSource.integer, -1, qtrue);
 +  // reset all the pings
 +  trap_LAN_ResetPings(ui_netSource.integer);
 +  //
 +  if( ui_netSource.integer == AS_LOCAL ) {
 +    trap_Cmd_ExecuteText( EXEC_NOW, "localservers\n" );
 +    uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000;
 +    return;
 +  }
 +
 +  uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000;
 +  if( ui_netSource.integer == AS_GLOBAL || ui_netSource.integer == AS_MPLAYER ) {
 +    if( ui_netSource.integer == AS_GLOBAL ) {
 +      i = 0;
 +    }
 +    else {
 +      i = 1;
 +    }
 +
 +    ptr = UI_Cvar_VariableString("debug_protocol");
 +    if (strlen(ptr)) {
 +      trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %s full empty\n", i, ptr));
 +    }
 +    else {
 +      trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %d full empty\n", i, (int)trap_Cvar_VariableValue( "protocol" ) ) );
 +    }
 +  }
 +}
\ No newline at end of file diff --git a/src/ui/ui_players.c b/src/ui/ui_players.c new file mode 100644 index 0000000..9025907 --- /dev/null +++ b/src/ui/ui_players.c @@ -0,0 +1,1369 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +// 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;
 +sfxHandle_t weaponChangeSound;
 +
 +
 +/*
 +===============
 +UI_PlayerInfo_SetWeapon
 +===============
 +*/
 +static void UI_PlayerInfo_SetWeapon( playerInfo_t *pi, weapon_t weaponNum )
 +{
 +  //TA: FIXME: this is probably useless for trem
 +/*  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;
 +  }
 +
 +  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 ) {
 +    strcpy( path, item->world_model[0] );
 +    COM_StripExtension( path, path );
 +    strcat( path, "_barrel.md3" );
 +    pi->barrelModel = trap_R_RegisterModel( path );
 +  }
 +
 +  strcpy( path, item->world_model[0] );
 +  COM_StripExtension( path, 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_TESLAGEN:
 +    MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 );
 +    break;
 +
 +  case WP_RAILGUN:
 +    MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 );
 +    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_PLAYER_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 -= uiInfo.uiDC.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 -= uiInfo.uiDC.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 = uiInfo.uiDC.frameTime * scale * speed;
 +    if ( move >= swing ) {
 +      move = swing;
 +      *swinging = qfalse;
 +    }
 +    *angle = AngleMod( *angle + move );
 +  } else if ( swing < 0 ) {
 +    move = uiInfo.uiDC.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;
 +  }
 +
 +  // this allows the ui to cache the player model on the main menu
 +  if (w == 0 || h == 0) {
 +    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 / (float)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 ) {
 +    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_FileExists
 +==========================
 +*/
 +static qboolean  UI_FileExists(const char *filename) {
 +  int len;
 +
 +  len = trap_FS_FOpenFile( filename, NULL, FS_READ );
 +  if (len>0) {
 +    return qtrue;
 +  }
 +  return qfalse;
 +}
 +
 +/*
 +==========================
 +UI_FindClientHeadFile
 +==========================
 +*/
 +static qboolean  UI_FindClientHeadFile( char *filename, int length, const char *teamName, const char *headModelName, const char *headSkinName, const char *base, const char *ext ) {
 +  char *team, *headsFolder;
 +  int i;
 +
 +  team = "default";
 +
 +  if ( headModelName[0] == '*' ) {
 +    headsFolder = "heads/";
 +    headModelName++;
 +  }
 +  else {
 +    headsFolder = "";
 +  }
 +  while(1) {
 +    for ( i = 0; i < 2; i++ ) {
 +      if ( i == 0 && teamName && *teamName ) {
 +        Com_sprintf( filename, length, "models/players/%s%s/%s/%s%s_%s.%s", headsFolder, headModelName, headSkinName, teamName, base, team, ext );
 +      }
 +      else {
 +        Com_sprintf( filename, length, "models/players/%s%s/%s/%s_%s.%s", headsFolder, headModelName, headSkinName, base, team, ext );
 +      }
 +      if ( UI_FileExists( filename ) ) {
 +        return qtrue;
 +      }
 +      if ( i == 0 && teamName && *teamName ) {
 +        Com_sprintf( filename, length, "models/players/%s%s/%s%s_%s.%s", headsFolder, headModelName, teamName, base, headSkinName, ext );
 +      }
 +      else {
 +        Com_sprintf( filename, length, "models/players/%s%s/%s_%s.%s", headsFolder, headModelName, base, headSkinName, ext );
 +      }
 +      if ( UI_FileExists( filename ) ) {
 +        return qtrue;
 +      }
 +      if ( !teamName || !*teamName ) {
 +        break;
 +      }
 +    }
 +    // if tried the heads folder first
 +    if ( headsFolder[0] ) {
 +      break;
 +    }
 +    headsFolder = "heads/";
 +  }
 +
 +  return qfalse;
 +}
 +
 +/*
 +==========================
 +UI_RegisterClientSkin
 +==========================
 +*/
 +static qboolean  UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, const char *skinName, const char *headModelName, const char *headSkinName , const char *teamName) {
 +  char    filename[MAX_QPATH*2];
 +
 +  if (teamName && *teamName) {
 +    Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/lower_%s.skin", modelName, teamName, skinName );
 +  } else {
 +    Com_sprintf( filename, sizeof( filename ), "models/players/%s/lower_%s.skin", modelName, skinName );
 +  }
 +  pi->legsSkin = trap_R_RegisterSkin( filename );
 +  if (!pi->legsSkin) {
 +    if (teamName && *teamName) {
 +      Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/lower_%s.skin", modelName, teamName, skinName );
 +    } else {
 +      Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/lower_%s.skin", modelName, skinName );
 +    }
 +    pi->legsSkin = trap_R_RegisterSkin( filename );
 +  }
 +
 +  if (teamName && *teamName) {
 +    Com_sprintf( filename, sizeof( filename ), "models/players/%s/%s/upper_%s.skin", modelName, teamName, skinName );
 +  } else {
 +    Com_sprintf( filename, sizeof( filename ), "models/players/%s/upper_%s.skin", modelName, skinName );
 +  }
 +  pi->torsoSkin = trap_R_RegisterSkin( filename );
 +  if (!pi->torsoSkin) {
 +    if (teamName && *teamName) {
 +      Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/%s/upper_%s.skin", modelName, teamName, skinName );
 +    } else {
 +      Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/upper_%s.skin", modelName, skinName );
 +    }
 +    pi->torsoSkin = trap_R_RegisterSkin( filename );
 +  }
 +
 +  if ( UI_FindClientHeadFile( filename, sizeof(filename), teamName, headModelName, headSkinName, "head", "skin" ) ) {
 +    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_PLAYER_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 );
 +
 +  COM_Compress(text);
 +
 +  // 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_PLAYER_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_PLAYER_ANIMATIONS ) {
 +    Com_Printf( "Error parsing animation file: %s", filename );
 +    return qfalse;
 +  }
 +
 +  return qtrue;
 +}
 +
 +/*
 +==========================
 +UI_RegisterClientModelname
 +==========================
 +*/
 +qboolean UI_RegisterClientModelname( playerInfo_t *pi, const char *modelSkinName, const char *headModelSkinName, const char *teamName ) {
 +  char    modelName[MAX_QPATH];
 +  char    skinName[MAX_QPATH];
 +  char    headModelName[MAX_QPATH];
 +  char    headSkinName[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 ) );
 +    *slash = '\0';
 +  }
 +
 +  Q_strncpyz( headModelName, headModelSkinName, sizeof( headModelName ) );
 +  slash = strchr( headModelName, '/' );
 +  if ( !slash ) {
 +    // modelName did not include a skin name
 +    Q_strncpyz( headSkinName, "default", sizeof( skinName ) );
 +  } else {
 +    Q_strncpyz( headSkinName, slash + 1, sizeof( skinName ) );
 +    *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_sprintf( filename, sizeof( filename ), "models/players/characters/%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_sprintf( filename, sizeof( filename ), "models/players/characters/%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;
 +    }
 +  }
 +
 +  if (headModelName && headModelName[0] == '*' ) {
 +    Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", &headModelName[1], &headModelName[1] );
 +  }
 +  else {
 +    Com_sprintf( filename, sizeof( filename ), "models/players/%s/head.md3", headModelName );
 +  }
 +  pi->headModel = trap_R_RegisterModel( filename );
 +  if ( !pi->headModel && headModelName[0] != '*') {
 +    Com_sprintf( filename, sizeof( filename ), "models/players/heads/%s/%s.md3", headModelName, headModelName );
 +    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, headModelName, headSkinName, teamName) ) {
 +    if ( !UI_RegisterClientSkin( pi, modelName, "default", headModelName, "default", teamName ) ) {
 +      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_sprintf( filename, sizeof( filename ), "models/players/characters/%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, const char *headmodel, char *teamName ) {
 +  memset( pi, 0, sizeof(*pi) );
 +  UI_RegisterClientModelname( pi, model, headmodel, teamName );
 +  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 ) {
 +      torsoAnim = TORSO_STAND2;
 +    }
 +    else {
 +      torsoAnim = TORSO_STAND;
 +    }
 +  }
 +
 +  if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) {
 +    if ( weaponNum == WP_NONE ) {
 +      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/src/ui/ui_public.h b/src/ui/ui_public.h new file mode 100644 index 0000000..e46fb36 --- /dev/null +++ b/src/ui/ui_public.h @@ -0,0 +1,188 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +#ifndef __UI_PUBLIC_H__
 +#define __UI_PUBLIC_H__
 +
 +#define UI_API_VERSION  6
 +
 +typedef struct {
 +  connstate_t   connState;
 +  int       connectPacketCount;
 +  int       clientNum;
 +  char      servername[MAX_STRING_CHARS];
 +  char      updateInfoString[MAX_STRING_CHARS];
 +  char      messageString[MAX_STRING_CHARS];
 +} uiClientState_t;
 +
 +typedef enum {
 +  UI_ERROR,
 +  UI_PRINT,
 +  UI_MILLISECONDS,
 +  UI_CVAR_SET,
 +  UI_CVAR_VARIABLEVALUE,
 +  UI_CVAR_VARIABLESTRINGBUFFER,
 +  UI_CVAR_SETVALUE,
 +  UI_CVAR_RESET,
 +  UI_CVAR_CREATE,
 +  UI_CVAR_INFOSTRINGBUFFER,
 +  UI_ARGC,
 +  UI_ARGV,
 +  UI_CMD_EXECUTETEXT,
 +  UI_FS_FOPENFILE,
 +  UI_FS_READ,
 +  UI_FS_WRITE,
 +  UI_FS_FCLOSEFILE,
 +  UI_FS_GETFILELIST,
 +  UI_R_REGISTERMODEL,
 +  UI_R_REGISTERSKIN,
 +  UI_R_REGISTERSHADERNOMIP,
 +  UI_R_CLEARSCENE,
 +  UI_R_ADDREFENTITYTOSCENE,
 +  UI_R_ADDPOLYTOSCENE,
 +  UI_R_ADDLIGHTTOSCENE,
 +  UI_R_RENDERSCENE,
 +  UI_R_SETCOLOR,
 +  UI_R_DRAWSTRETCHPIC,
 +  UI_UPDATESCREEN,
 +  UI_CM_LERPTAG,
 +  UI_CM_LOADMODEL,
 +  UI_S_REGISTERSOUND,
 +  UI_S_STARTLOCALSOUND,
 +  UI_KEY_KEYNUMTOSTRINGBUF,
 +  UI_KEY_GETBINDINGBUF,
 +  UI_KEY_SETBINDING,
 +  UI_KEY_ISDOWN,
 +  UI_KEY_GETOVERSTRIKEMODE,
 +  UI_KEY_SETOVERSTRIKEMODE,
 +  UI_KEY_CLEARSTATES,
 +  UI_KEY_GETCATCHER,
 +  UI_KEY_SETCATCHER,
 +  UI_GETCLIPBOARDDATA,
 +  UI_GETGLCONFIG,
 +  UI_GETCLIENTSTATE,
 +  UI_GETCONFIGSTRING,
 +  UI_LAN_GETPINGQUEUECOUNT,
 +  UI_LAN_CLEARPING,
 +  UI_LAN_GETPING,
 +  UI_LAN_GETPINGINFO,
 +  UI_CVAR_REGISTER,
 +  UI_CVAR_UPDATE,
 +  UI_MEMORY_REMAINING,
 +  UI_R_REGISTERFONT,
 +  UI_R_MODELBOUNDS,
 +  UI_PARSE_ADD_GLOBAL_DEFINE,
 +  UI_PARSE_LOAD_SOURCE,
 +  UI_PARSE_FREE_SOURCE,
 +  UI_PARSE_READ_TOKEN,
 +  UI_PARSE_SOURCE_FILE_AND_LINE,
 +  UI_S_STOPBACKGROUNDTRACK,
 +  UI_S_STARTBACKGROUNDTRACK,
 +  UI_REAL_TIME,
 +  UI_LAN_GETSERVERCOUNT,
 +  UI_LAN_GETSERVERADDRESSSTRING,
 +  UI_LAN_GETSERVERINFO,
 +  UI_LAN_MARKSERVERVISIBLE,
 +  UI_LAN_UPDATEVISIBLEPINGS,
 +  UI_LAN_RESETPINGS,
 +  UI_LAN_LOADCACHEDSERVERS,
 +  UI_LAN_SAVECACHEDSERVERS,
 +  UI_LAN_ADDSERVER,
 +  UI_LAN_REMOVESERVER,
 +  UI_CIN_PLAYCINEMATIC,
 +  UI_CIN_STOPCINEMATIC,
 +  UI_CIN_RUNCINEMATIC,
 +  UI_CIN_DRAWCINEMATIC,
 +  UI_CIN_SETEXTENTS,
 +  UI_R_REMAP_SHADER,
 +  UI_LAN_SERVERSTATUS,
 +  UI_LAN_GETSERVERPING,
 +  UI_LAN_SERVERISVISIBLE,
 +  UI_LAN_COMPARESERVERS,
 +  // 1.32
 +  UI_FS_SEEK,
 +  UI_SET_PBCLSTATUS,
 +
 +  UI_MEMSET = 100,
 +  UI_MEMCPY,
 +  UI_STRNCPY,
 +  UI_SIN,
 +  UI_COS,
 +  UI_ATAN2,
 +  UI_SQRT,
 +  UI_FLOOR,
 +  UI_CEIL
 +} uiImport_t;
 +
 +typedef enum {
 +  UIMENU_NONE,
 +  UIMENU_MAIN,
 +  UIMENU_INGAME,
 +  UIMENU_TEAM,
 +  UIMENU_POSTGAME
 +} uiMenuCommand_t;
 +
 +typedef enum
 +{
 +  SORT_HOST,
 +  SORT_MAP,
 +  SORT_CLIENTS,
 +  SORT_PING
 +} serverSortField_t;
 +
 +typedef enum {
 +  UI_GETAPIVERSION = 0, // system reserved
 +
 +  UI_INIT,
 +//  void  UI_Init( void );
 +
 +  UI_SHUTDOWN,
 +//  void  UI_Shutdown( void );
 +
 +  UI_KEY_EVENT,
 +//  void  UI_KeyEvent( int key );
 +
 +  UI_MOUSE_EVENT,
 +//  void  UI_MouseEvent( int dx, int dy );
 +
 +  UI_REFRESH,
 +//  void  UI_Refresh( int time );
 +
 +  UI_IS_FULLSCREEN,
 +//  qboolean UI_IsFullscreen( void );
 +
 +  UI_SET_ACTIVE_MENU,
 +//  void  UI_SetActiveMenu( uiMenuCommand_t menu );
 +
 +  UI_CONSOLE_COMMAND,
 +//  qboolean UI_ConsoleCommand( int realTime );
 +
 +  UI_DRAW_CONNECT_SCREEN
 +//  void  UI_DrawConnectScreen( qboolean overlay );
 +
 +// if !overlay, the background will be drawn, otherwise it will be
 +// overlayed over whatever the cgame has drawn.
 +// a GetClientState syscall will be made to get the current strings
 +} uiExport_t;
 +
 +#endif
 diff --git a/src/ui/ui_shared.c b/src/ui/ui_shared.c new file mode 100644 index 0000000..2ca88cb --- /dev/null +++ b/src/ui/ui_shared.c @@ -0,0 +1,6075 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2006 Tim Angus + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA +=========================================================================== +*/ + +#include "ui_shared.h" + +#define SCROLL_TIME_START         500 +#define SCROLL_TIME_ADJUST        150 +#define SCROLL_TIME_ADJUSTOFFSET  40 +#define SCROLL_TIME_FLOOR         20 + +typedef struct scrollInfo_s { +  int nextScrollTime; +  int nextAdjustTime; +  int adjustValue; +  int scrollKey; +  float xStart; +  float yStart; +  itemDef_t *item; +  qboolean scrollDir; +} scrollInfo_t; + +static scrollInfo_t scrollInfo; + +//TA: hack to prevent compiler warnings +void voidFunction( void *var ) { return; } +qboolean voidFunction2( itemDef_t *var1, int var2 ) { return qfalse; } + +static void (*captureFunc) (void *p) = voidFunction; +static void *captureData = NULL; +static itemDef_t *itemCapture = NULL;   // item that has the mouse captured ( if any ) + +displayContextDef_t *DC = NULL; + +static qboolean g_waitingForKey = qfalse; +static qboolean g_editingField = qfalse; + +static itemDef_t *g_bindItem = NULL; +static itemDef_t *g_editItem = NULL; + +menuDef_t Menus[MAX_MENUS];      // defined menus +int menuCount = 0;               // how many + +menuDef_t *menuStack[MAX_OPEN_MENUS]; +int openMenuCount = 0; + +static qboolean debugMode = qfalse; + +#define DOUBLE_CLICK_DELAY 300 +static int lastListBoxClickTime = 0; + +void Item_RunScript(itemDef_t *item, const char *s); +void Item_SetupKeywordHash(void); +void Menu_SetupKeywordHash(void); +int BindingIDFromName(const char *name); +qboolean Item_Bind_HandleKey(itemDef_t *item, int key, qboolean down); +itemDef_t *Menu_SetPrevCursorItem(menuDef_t *menu); +itemDef_t *Menu_SetNextCursorItem(menuDef_t *menu); +static qboolean Menu_OverActiveItem(menuDef_t *menu, float x, float y); + +#ifdef CGAME +#define MEM_POOL_SIZE  128 * 1024 +#else +#define MEM_POOL_SIZE  1024 * 1024 +#endif + +//TA: hacked variable name to avoid conflict with new cgame Alloc +static char   UI_memoryPool[MEM_POOL_SIZE]; +static int    allocPoint, outOfMemory; + +/* +=============== +UI_Alloc +=============== +*/ +void *UI_Alloc( int size ) +{ +  char  *p; + +  if( allocPoint + size > MEM_POOL_SIZE ) +  { +    outOfMemory = qtrue; + +    if( DC->Print ) +      DC->Print( "UI_Alloc: Failure. Out of memory!\n" ); +    //DC->trap_Print(S_COLOR_YELLOW"WARNING: UI Out of Memory!\n"); +    return NULL; +  } + +  p = &UI_memoryPool[ allocPoint ]; + +  allocPoint += ( size + 15 ) & ~15; + +  return p; +} + +/* +=============== +UI_InitMemory +=============== +*/ +void UI_InitMemory( void ) +{ +  allocPoint = 0; +  outOfMemory = qfalse; +} + +qboolean UI_OutOfMemory( ) +{ +  return outOfMemory; +} + + + + + +#define HASH_TABLE_SIZE 2048 +/* +================ +return a hash value for the string +================ +*/ +static long hashForString(const char *str) { +  int   i; +  long  hash; +  char  letter; + +  hash = 0; +  i = 0; +  while (str[i] != '\0') { +    letter = tolower(str[i]); +    hash+=(long)(letter)*(i+119); +    i++; +  } +  hash &= (HASH_TABLE_SIZE-1); +  return hash; +} + +typedef struct stringDef_s { +  struct stringDef_s *next; +  const char *str; +} stringDef_t; + +static int strPoolIndex = 0; +static char strPool[STRING_POOL_SIZE]; + +static int strHandleCount = 0; +static stringDef_t *strHandle[HASH_TABLE_SIZE]; + + +const char *String_Alloc(const char *p) { +  int len; +  long hash; +  stringDef_t *str, *last; +  static const char *staticNULL = ""; + +  if (p == NULL) { +    return NULL; +  } + +  if (*p == 0) { +    return staticNULL; +  } + +  hash = hashForString(p); + +  str = strHandle[hash]; +  while (str) { +    if (strcmp(p, str->str) == 0) { +      return str->str; +    } +    str = str->next; +  } + +  len = strlen(p); +  if (len + strPoolIndex + 1 < STRING_POOL_SIZE) { +    int ph = strPoolIndex; +    strcpy(&strPool[strPoolIndex], p); +    strPoolIndex += len + 1; + +    str = strHandle[hash]; +    last = str; +    while (str && str->next) { +      last = str; +      str = str->next; +    } + +    str  = UI_Alloc(sizeof(stringDef_t)); +    str->next = NULL; +    str->str = &strPool[ph]; +    if (last) { +      last->next = str; +    } else { +      strHandle[hash] = str; +    } +    return &strPool[ph]; +  } +  return NULL; +} + +void String_Report( void ) { +  float f; +  Com_Printf("Memory/String Pool Info\n"); +  Com_Printf("----------------\n"); +  f = strPoolIndex; +  f /= STRING_POOL_SIZE; +  f *= 100; +  Com_Printf("String Pool is %.1f%% full, %i bytes out of %i used.\n", f, strPoolIndex, STRING_POOL_SIZE); +  f = allocPoint; +  f /= MEM_POOL_SIZE; +  f *= 100; +  Com_Printf("Memory Pool is %.1f%% full, %i bytes out of %i used.\n", f, allocPoint, MEM_POOL_SIZE); +} + +/* +================= +String_Init +================= +*/ +void String_Init( void ) +{ +  int i; +  for( i = 0; i < HASH_TABLE_SIZE; i++ ) +    strHandle[ i ] = 0; + +  strHandleCount = 0; +  strPoolIndex = 0; +  menuCount = 0; +  openMenuCount = 0; +  UI_InitMemory( ); +  Item_SetupKeywordHash( ); +  Menu_SetupKeywordHash( ); + +  if( DC && DC->getBindingBuf ) +    Controls_GetConfig( ); +} + +/* +================= +PC_SourceWarning +================= +*/ +void PC_SourceWarning(int handle, char *format, ...) { +  int line; +  char filename[128]; +  va_list argptr; +  static char string[4096]; + +  va_start (argptr, format); +  vsprintf (string, format, argptr); +  va_end (argptr); + +  filename[0] = '\0'; +  line = 0; +  trap_Parse_SourceFileAndLine(handle, filename, &line); + +  Com_Printf(S_COLOR_YELLOW "WARNING: %s, line %d: %s\n", filename, line, string); +} + +/* +================= +PC_SourceError +================= +*/ +void PC_SourceError(int handle, char *format, ...) { +  int line; +  char filename[128]; +  va_list argptr; +  static char string[4096]; + +  va_start (argptr, format); +  vsprintf (string, format, argptr); +  va_end (argptr); + +  filename[0] = '\0'; +  line = 0; +  trap_Parse_SourceFileAndLine(handle, filename, &line); + +  Com_Printf(S_COLOR_RED "ERROR: %s, line %d: %s\n", filename, line, string); +} + +/* +================= +LerpColor +================= +*/ +void 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; +  } +} + +/* +================= +Float_Parse +================= +*/ +qboolean Float_Parse(char **p, float *f) { +  char  *token; +  token = COM_ParseExt(p, qfalse); +  if (token && token[0] != 0) { +    *f = atof(token); +    return qtrue; +  } else { +    return qfalse; +  } +} + +/* +================= +PC_Float_Parse +================= +*/ +qboolean PC_Float_Parse(int handle, float *f) { +  pc_token_t token; +  int negative = qfalse; + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; +  if (token.string[0] == '-') { +    if (!trap_Parse_ReadToken(handle, &token)) +      return qfalse; +    negative = qtrue; +  } +  if (token.type != TT_NUMBER) { +    PC_SourceError(handle, "expected float but found %s\n", token.string); +    return qfalse; +  } +  if (negative) +    *f = -token.floatvalue; +  else +    *f = token.floatvalue; +  return qtrue; +} + +/* +================= +Color_Parse +================= +*/ +qboolean Color_Parse(char **p, vec4_t *c) { +  int i; +  float f; + +  for (i = 0; i < 4; i++) { +    if (!Float_Parse(p, &f)) { +      return qfalse; +    } +    (*c)[i] = f; +  } +  return qtrue; +} + +/* +================= +PC_Color_Parse +================= +*/ +qboolean PC_Color_Parse(int handle, vec4_t *c) { +  int i; +  float f; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    (*c)[i] = f; +  } +  return qtrue; +} + +/* +================= +Int_Parse +================= +*/ +qboolean Int_Parse(char **p, int *i) { +  char  *token; +  token = COM_ParseExt(p, qfalse); + +  if (token && token[0] != 0) { +    *i = atoi(token); +    return qtrue; +  } else { +    return qfalse; +  } +} + +/* +================= +PC_Int_Parse +================= +*/ +qboolean PC_Int_Parse(int handle, int *i) { +  pc_token_t token; +  int negative = qfalse; + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; +  if (token.string[0] == '-') { +    if (!trap_Parse_ReadToken(handle, &token)) +      return qfalse; +    negative = qtrue; +  } +  if (token.type != TT_NUMBER) { +    PC_SourceError(handle, "expected integer but found %s\n", token.string); +    return qfalse; +  } +  *i = token.intvalue; +  if (negative) +    *i = - *i; +  return qtrue; +} + +/* +================= +Rect_Parse +================= +*/ +qboolean Rect_Parse(char **p, rectDef_t *r) { +  if (Float_Parse(p, &r->x)) { +    if (Float_Parse(p, &r->y)) { +      if (Float_Parse(p, &r->w)) { +        if (Float_Parse(p, &r->h)) { +          return qtrue; +        } +      } +    } +  } +  return qfalse; +} + +/* +================= +PC_Rect_Parse +================= +*/ +qboolean PC_Rect_Parse(int handle, rectDef_t *r) { +  if (PC_Float_Parse(handle, &r->x)) { +    if (PC_Float_Parse(handle, &r->y)) { +      if (PC_Float_Parse(handle, &r->w)) { +        if (PC_Float_Parse(handle, &r->h)) { +          return qtrue; +        } +      } +    } +  } +  return qfalse; +} + +/* +================= +String_Parse +================= +*/ +qboolean String_Parse(char **p, const char **out) { +  char *token; + +  token = COM_ParseExt(p, qfalse); +  if (token && token[0] != 0) { +    *(out) = String_Alloc(token); +    return qtrue; +  } +  return qfalse; +} + +/* +================= +PC_String_Parse +================= +*/ +qboolean PC_String_Parse(int handle, const char **out) { +  pc_token_t token; + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; + +  *(out) = String_Alloc(token.string); +    return qtrue; +} + +/* +================= +PC_Script_Parse +================= +*/ +qboolean PC_Script_Parse(int handle, const char **out) { +  char script[1024]; +  pc_token_t token; + +  memset(script, 0, sizeof(script)); +  // scripts start with { and have ; separated command lists.. commands are command, arg.. +  // basically we want everything between the { } as it will be interpreted at run time + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; +  if (Q_stricmp(token.string, "{") != 0) { +      return qfalse; +  } + +  while ( 1 ) { +    if (!trap_Parse_ReadToken(handle, &token)) +      return qfalse; + +    if (Q_stricmp(token.string, "}") == 0) { +      *out = String_Alloc(script); +      return qtrue; +    } + +    if (token.string[1] != '\0') { +      Q_strcat(script, 1024, va("\"%s\"", token.string)); +    } else { +      Q_strcat(script, 1024, token.string); +    } +    Q_strcat(script, 1024, " "); +  } +  return qfalse;  // bk001105 - LCC   missing return value +} + +// display, window, menu, item code +// + +/* +================== +Init_Display + +Initializes the display with a structure to all the drawing routines +================== +*/ +void Init_Display( displayContextDef_t *dc ) +{ +  DC = dc; +} + + + +// type and style painting + +void GradientBar_Paint( rectDef_t *rect, vec4_t color ) +{ +  // gradient bar takes two paints +  DC->setColor( color ); +  DC->drawHandlePic( rect->x, rect->y, rect->w, rect->h, DC->Assets.gradientBar ); +  DC->setColor( NULL ); +} + + +/* +================== +Window_Init + +Initializes a window structure ( windowDef_t ) with defaults + +================== +*/ +void Window_Init(Window *w) { +  memset(w, 0, sizeof(windowDef_t)); +  w->borderSize = 1; +  w->foreColor[0] = w->foreColor[1] = w->foreColor[2] = w->foreColor[3] = 1.0; +  w->cinematic = -1; +} + +void Fade(int *flags, float *f, float clamp, int *nextTime, int offsetTime, qboolean bFlags, float fadeAmount) { +  if (*flags & (WINDOW_FADINGOUT | WINDOW_FADINGIN)) { +    if (DC->realTime > *nextTime) { +      *nextTime = DC->realTime + offsetTime; +      if (*flags & WINDOW_FADINGOUT) { +        *f -= fadeAmount; +        if (bFlags && *f <= 0.0) { +          *flags &= ~(WINDOW_FADINGOUT | WINDOW_VISIBLE); +        } +      } else { +        *f += fadeAmount; +        if (*f >= clamp) { +          *f = clamp; +          if (bFlags) { +            *flags &= ~WINDOW_FADINGIN; +          } +        } +      } +    } +  } +} + + + +void Window_Paint(Window *w, float fadeAmount, float fadeClamp, float fadeCycle) { +  //float bordersize = 0; +  vec4_t color; +  rectDef_t fillRect = w->rect; + + +  if (debugMode) { +    color[0] = color[1] = color[2] = color[3] = 1; +    DC->drawRect(w->rect.x, w->rect.y, w->rect.w, w->rect.h, 1, color); +  } + +  if (w == NULL || (w->style == 0 && w->border == 0)) { +    return; +  } + +  if (w->border != 0) { +    fillRect.x += w->borderSize; +    fillRect.y += w->borderSize; +    fillRect.w -= w->borderSize + 1; +    fillRect.h -= w->borderSize + 1; +  } + +  if (w->style == WINDOW_STYLE_FILLED) { +    // box, but possible a shader that needs filled +    if (w->background) { +      Fade(&w->flags, &w->backColor[3], fadeClamp, &w->nextTime, fadeCycle, qtrue, fadeAmount); +      DC->setColor(w->backColor); +      DC->drawHandlePic(fillRect.x, fillRect.y, fillRect.w, fillRect.h, w->background); +      DC->setColor(NULL); +    } else { +      DC->fillRect(fillRect.x, fillRect.y, fillRect.w, fillRect.h, w->backColor); +    } +  } else if (w->style == WINDOW_STYLE_GRADIENT) { +    GradientBar_Paint(&fillRect, w->backColor); +    // gradient bar +  } else if (w->style == WINDOW_STYLE_SHADER) { +    if (w->flags & WINDOW_FORECOLORSET) { +      DC->setColor(w->foreColor); +    } +    DC->drawHandlePic(fillRect.x, fillRect.y, fillRect.w, fillRect.h, w->background); +    DC->setColor(NULL); +  } else if (w->style == WINDOW_STYLE_TEAMCOLOR) { +    if (DC->getTeamColor) { +      DC->getTeamColor(&color); +      DC->fillRect(fillRect.x, fillRect.y, fillRect.w, fillRect.h, color); +    } +  } else if (w->style == WINDOW_STYLE_CINEMATIC) { +    if (w->cinematic == -1) { +      w->cinematic = DC->playCinematic(w->cinematicName, fillRect.x, fillRect.y, fillRect.w, fillRect.h); +      if (w->cinematic == -1) { +        w->cinematic = -2; +      } +    } +    if (w->cinematic >= 0) { +      DC->runCinematicFrame(w->cinematic); +      DC->drawCinematic(w->cinematic, fillRect.x, fillRect.y, fillRect.w, fillRect.h); +    } +  } + +  if (w->border == WINDOW_BORDER_FULL) { +    // full +    // HACK HACK HACK +    if (w->style == WINDOW_STYLE_TEAMCOLOR) { +      if (color[0] > 0) { +        // red +        color[0] = 1; +        color[1] = color[2] = .5; + +      } else { +        color[2] = 1; +        color[0] = color[1] = .5; +      } +      color[3] = 1; +      DC->drawRect(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize, color); +    } else { +      DC->drawRect(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize, w->borderColor); +    } +  } else if (w->border == WINDOW_BORDER_HORZ) { +    // top/bottom +    DC->setColor(w->borderColor); +    DC->drawTopBottom(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize); +    DC->setColor( NULL ); +  } else if (w->border == WINDOW_BORDER_VERT) { +    // left right +    DC->setColor(w->borderColor); +    DC->drawSides(w->rect.x, w->rect.y, w->rect.w, w->rect.h, w->borderSize); +    DC->setColor( NULL ); +  } else if (w->border == WINDOW_BORDER_KCGRADIENT) { +    // this is just two gradient bars along each horz edge +    rectDef_t r = w->rect; +    r.h = w->borderSize; +    GradientBar_Paint(&r, w->borderColor); +    r.y = w->rect.y + w->rect.h - 1; +    GradientBar_Paint(&r, w->borderColor); +  } + +} + + +void Item_SetScreenCoords(itemDef_t *item, float x, float y) { + +  if (item == NULL) { +    return; +  } + +  if (item->window.border != 0) { +    x += item->window.borderSize; +    y += item->window.borderSize; +  } + +  item->window.rect.x = x + item->window.rectClient.x; +  item->window.rect.y = y + item->window.rectClient.y; +  item->window.rect.w = item->window.rectClient.w; +  item->window.rect.h = item->window.rectClient.h; + +  // force the text rects to recompute +  item->textRect.w = 0; +  item->textRect.h = 0; +} + +// FIXME: consolidate this with nearby stuff +void Item_UpdatePosition(itemDef_t *item) { +  float x, y; +  menuDef_t *menu; + +  if (item == NULL || item->parent == NULL) { +    return; +  } + +  menu = item->parent; + +  x = menu->window.rect.x; +  y = menu->window.rect.y; + +  if (menu->window.border != 0) { +    x += menu->window.borderSize; +    y += menu->window.borderSize; +  } + +  Item_SetScreenCoords(item, x, y); + +} + +// menus +void Menu_UpdatePosition(menuDef_t *menu) { +  int i; +  float x, y; + +  if (menu == NULL) { +    return; +  } + +  x = menu->window.rect.x; +  y = menu->window.rect.y; +  if (menu->window.border != 0) { +    x += menu->window.borderSize; +    y += menu->window.borderSize; +  } + +  for (i = 0; i < menu->itemCount; i++) { +    Item_SetScreenCoords(menu->items[i], x, y); +  } +} + +void Menu_PostParse(menuDef_t *menu) { +  if (menu == NULL) { +    return; +  } +  if (menu->fullScreen) { +    menu->window.rect.x = 0; +    menu->window.rect.y = 0; +    menu->window.rect.w = 640; +    menu->window.rect.h = 480; +  } +  Menu_UpdatePosition(menu); +} + +itemDef_t *Menu_ClearFocus(menuDef_t *menu) { +  int i; +  itemDef_t *ret = NULL; + +  if (menu == NULL) { +    return NULL; +  } + +  for (i = 0; i < menu->itemCount; i++) { +    if (menu->items[i]->window.flags & WINDOW_HASFOCUS) { +      ret = menu->items[i]; +    } +    menu->items[i]->window.flags &= ~WINDOW_HASFOCUS; +    if (menu->items[i]->leaveFocus) { +      Item_RunScript(menu->items[i], menu->items[i]->leaveFocus); +    } +  } + +  return ret; +} + +qboolean IsVisible(int flags) { +  return (flags & WINDOW_VISIBLE && !(flags & WINDOW_FADINGOUT)); +} + +qboolean Rect_ContainsPoint(rectDef_t *rect, float x, float y) { +  if (rect) { +    if (x > rect->x && x < rect->x + rect->w && y > rect->y && y < rect->y + rect->h) { +      return qtrue; +    } +  } +  return qfalse; +} + +int Menu_ItemsMatchingGroup(menuDef_t *menu, const char *name) { +  int i; +  int count = 0; +  for (i = 0; i < menu->itemCount; i++) { +    if (Q_stricmp(menu->items[i]->window.name, name) == 0 || (menu->items[i]->window.group && Q_stricmp(menu->items[i]->window.group, name) == 0)) { +      count++; +    } +  } +  return count; +} + +itemDef_t *Menu_GetMatchingItemByNumber(menuDef_t *menu, int index, const char *name) { +  int i; +  int count = 0; +  for (i = 0; i < menu->itemCount; i++) { +    if (Q_stricmp(menu->items[i]->window.name, name) == 0 || (menu->items[i]->window.group && Q_stricmp(menu->items[i]->window.group, name) == 0)) { +      if (count == index) { +        return menu->items[i]; +      } +      count++; +    } +  } +  return NULL; +} + + + +void Script_SetColor(itemDef_t *item, char **args) { +  const char *name; +  int i; +  float f; +  vec4_t *out; +  // expecting type of color to set and 4 args for the color +  if (String_Parse(args, &name)) { +      out = NULL; +      if (Q_stricmp(name, "backcolor") == 0) { +        out = &item->window.backColor; +        item->window.flags |= WINDOW_BACKCOLORSET; +      } else if (Q_stricmp(name, "forecolor") == 0) { +        out = &item->window.foreColor; +        item->window.flags |= WINDOW_FORECOLORSET; +      } else if (Q_stricmp(name, "bordercolor") == 0) { +        out = &item->window.borderColor; +      } + +      if (out) { +        for (i = 0; i < 4; i++) { +          if (!Float_Parse(args, &f)) { +            return; +          } +          (*out)[i] = f; +        } +      } +  } +} + +void Script_SetAsset(itemDef_t *item, char **args) { +  const char *name; +  // expecting name to set asset to +  if (String_Parse(args, &name)) { +    // check for a model +    if (item->type == ITEM_TYPE_MODEL) { +    } +  } +} + +void Script_SetBackground(itemDef_t *item, char **args) { +  const char *name; +  // expecting name to set asset to +  if (String_Parse(args, &name)) { +    item->window.background = DC->registerShaderNoMip(name); +  } +} + + + + +itemDef_t *Menu_FindItemByName(menuDef_t *menu, const char *p) { +  int i; +  if (menu == NULL || p == NULL) { +    return NULL; +  } + +  for (i = 0; i < menu->itemCount; i++) { +    if (Q_stricmp(p, menu->items[i]->window.name) == 0) { +      return menu->items[i]; +    } +  } + +  return NULL; +} + +void Script_SetTeamColor(itemDef_t *item, char **args) { +  if (DC->getTeamColor) { +    int i; +    vec4_t color; +    DC->getTeamColor(&color); +    for (i = 0; i < 4; i++) { +      item->window.backColor[i] = color[i]; +    } +  } +} + +void Script_SetItemColor(itemDef_t *item, char **args) { +  const char *itemname; +  const char *name; +  vec4_t color; +  int i; +  vec4_t *out; +  // expecting type of color to set and 4 args for the color +  if (String_Parse(args, &itemname) && String_Parse(args, &name)) { +    itemDef_t *item2; +    int j; +    int count = Menu_ItemsMatchingGroup(item->parent, itemname); + +    if (!Color_Parse(args, &color)) { +      return; +    } + +    for (j = 0; j < count; j++) { +      item2 = Menu_GetMatchingItemByNumber(item->parent, j, itemname); +      if (item2 != NULL) { +        out = NULL; +        if (Q_stricmp(name, "backcolor") == 0) { +          out = &item2->window.backColor; +        } else if (Q_stricmp(name, "forecolor") == 0) { +          out = &item2->window.foreColor; +          item2->window.flags |= WINDOW_FORECOLORSET; +        } else if (Q_stricmp(name, "bordercolor") == 0) { +          out = &item2->window.borderColor; +        } + +        if (out) { +          for (i = 0; i < 4; i++) { +            (*out)[i] = color[i]; +          } +        } +      } +    } +  } +} + + +void Menu_ShowItemByName(menuDef_t *menu, const char *p, qboolean bShow) { +  itemDef_t *item; +  int i; +  int count = Menu_ItemsMatchingGroup(menu, p); +  for (i = 0; i < count; i++) { +    item = Menu_GetMatchingItemByNumber(menu, i, p); +    if (item != NULL) { +      if (bShow) { +        item->window.flags |= WINDOW_VISIBLE; +      } else { +        item->window.flags &= ~WINDOW_VISIBLE; +        // stop cinematics playing in the window +        if (item->window.cinematic >= 0) { +          DC->stopCinematic(item->window.cinematic); +          item->window.cinematic = -1; +        } +      } +    } +  } +} + +void Menu_FadeItemByName(menuDef_t *menu, const char *p, qboolean fadeOut) { +  itemDef_t *item; +  int i; +  int count = Menu_ItemsMatchingGroup(menu, p); +  for (i = 0; i < count; i++) { +    item = Menu_GetMatchingItemByNumber(menu, i, p); +    if (item != NULL) { +      if (fadeOut) { +        item->window.flags |= (WINDOW_FADINGOUT | WINDOW_VISIBLE); +        item->window.flags &= ~WINDOW_FADINGIN; +      } else { +        item->window.flags |= (WINDOW_VISIBLE | WINDOW_FADINGIN); +        item->window.flags &= ~WINDOW_FADINGOUT; +      } +    } +  } +} + +menuDef_t *Menus_FindByName(const char *p) { +  int i; +  for (i = 0; i < menuCount; i++) { +    if (Q_stricmp(Menus[i].window.name, p) == 0) { +      return &Menus[i]; +    } +  } +  return NULL; +} + +void Menus_ShowByName(const char *p) { +  menuDef_t *menu = Menus_FindByName(p); +  if (menu) { +    Menus_Activate(menu); +  } +} + +void Menus_OpenByName(const char *p) { +  Menus_ActivateByName(p); +} + +static void Menu_RunCloseScript(menuDef_t *menu) { +  if (menu && menu->window.flags & WINDOW_VISIBLE && menu->onClose) { +    itemDef_t item; +    item.parent = menu; +    Item_RunScript(&item, menu->onClose); +  } +} + +void Menus_CloseByName(const char *p) { +  menuDef_t *menu = Menus_FindByName(p); +  if (menu != NULL) { +    Menu_RunCloseScript(menu); +    menu->window.flags &= ~(WINDOW_VISIBLE | WINDOW_HASFOCUS); +  } +} + +void Menus_CloseAll( void ) { +  int i; +  for (i = 0; i < menuCount; i++) { +    Menu_RunCloseScript(&Menus[i]); +    Menus[i].window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE); +  } +} + + +void Script_Show(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    Menu_ShowItemByName(item->parent, name, qtrue); +  } +} + +void Script_Hide(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    Menu_ShowItemByName(item->parent, name, qfalse); +  } +} + +void Script_FadeIn(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    Menu_FadeItemByName(item->parent, name, qfalse); +  } +} + +void Script_FadeOut(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    Menu_FadeItemByName(item->parent, name, qtrue); +  } +} + + + +void Script_Open(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    Menus_OpenByName(name); +  } +} + +void Script_ConditionalOpen(itemDef_t *item, char **args) { +  const char *cvar; +  const char *name1; +  const char *name2; +  float           val; + +  if ( String_Parse(args, &cvar) && String_Parse(args, &name1) && String_Parse(args, &name2) ) { +    val = DC->getCVarValue( cvar ); +    if ( val == 0.f ) { +      Menus_OpenByName(name2); +    } else { +      Menus_OpenByName(name1); +    } +  } +} + +void Script_Close(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    Menus_CloseByName(name); +  } +} + +void Menu_TransitionItemByName(menuDef_t *menu, const char *p, rectDef_t rectFrom, rectDef_t rectTo, int time, float amt) { +  itemDef_t *item; +  int i; +  int count = Menu_ItemsMatchingGroup(menu, p); +  for (i = 0; i < count; i++) { +    item = Menu_GetMatchingItemByNumber(menu, i, p); +    if (item != NULL) { +      item->window.flags |= (WINDOW_INTRANSITION | WINDOW_VISIBLE); +      item->window.offsetTime = time; +      memcpy(&item->window.rectClient, &rectFrom, sizeof(rectDef_t)); +      memcpy(&item->window.rectEffects, &rectTo, sizeof(rectDef_t)); +      item->window.rectEffects2.x = abs(rectTo.x - rectFrom.x) / amt; +      item->window.rectEffects2.y = abs(rectTo.y - rectFrom.y) / amt; +      item->window.rectEffects2.w = abs(rectTo.w - rectFrom.w) / amt; +      item->window.rectEffects2.h = abs(rectTo.h - rectFrom.h) / amt; +      Item_UpdatePosition(item); +    } +  } +} + + +void Script_Transition(itemDef_t *item, char **args) { +  const char *name; +  rectDef_t rectFrom, rectTo; +  int time; +  float amt; + +  if (String_Parse(args, &name)) { +    if ( Rect_Parse(args, &rectFrom) && Rect_Parse(args, &rectTo) && Int_Parse(args, &time) && Float_Parse(args, &amt)) { +      Menu_TransitionItemByName(item->parent, name, rectFrom, rectTo, time, amt); +    } +  } +} + + +void Menu_OrbitItemByName(menuDef_t *menu, const char *p, float x, float y, float cx, float cy, int time) { +  itemDef_t *item; +  int i; +  int count = Menu_ItemsMatchingGroup(menu, p); +  for (i = 0; i < count; i++) { +    item = Menu_GetMatchingItemByNumber(menu, i, p); +    if (item != NULL) { +      item->window.flags |= (WINDOW_ORBITING | WINDOW_VISIBLE); +      item->window.offsetTime = time; +      item->window.rectEffects.x = cx; +      item->window.rectEffects.y = cy; +      item->window.rectClient.x = x; +      item->window.rectClient.y = y; +      Item_UpdatePosition(item); +    } +  } +} + + +void Script_Orbit(itemDef_t *item, char **args) { +  const char *name; +  float cx, cy, x, y; +  int time; + +  if (String_Parse(args, &name)) { +    if ( Float_Parse(args, &x) && Float_Parse(args, &y) && Float_Parse(args, &cx) && Float_Parse(args, &cy) && Int_Parse(args, &time) ) { +      Menu_OrbitItemByName(item->parent, name, x, y, cx, cy, time); +    } +  } +} + + + +void Script_SetFocus(itemDef_t *item, char **args) { +  const char *name; +  itemDef_t *focusItem; + +  if (String_Parse(args, &name)) { +    focusItem = Menu_FindItemByName(item->parent, name); +    if (focusItem && !(focusItem->window.flags & WINDOW_DECORATION) && !(focusItem->window.flags & WINDOW_HASFOCUS)) { +      Menu_ClearFocus(item->parent); +      focusItem->window.flags |= WINDOW_HASFOCUS; +      if (focusItem->onFocus) { +        Item_RunScript(focusItem, focusItem->onFocus); +      } +      if (DC->Assets.itemFocusSound) { +        DC->startLocalSound( DC->Assets.itemFocusSound, CHAN_LOCAL_SOUND ); +      } +    } +  } +} + +void Script_SetPlayerModel(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    DC->setCVar("team_model", name); +  } +} + +void Script_SetPlayerHead(itemDef_t *item, char **args) { +  const char *name; +  if (String_Parse(args, &name)) { +    DC->setCVar("team_headmodel", name); +  } +} + +void Script_SetCvar(itemDef_t *item, char **args) { +  const char *cvar, *val; +  if (String_Parse(args, &cvar) && String_Parse(args, &val)) { +    DC->setCVar(cvar, val); +  } + +} + +void Script_Exec(itemDef_t *item, char **args) { +  const char *val; +  if (String_Parse(args, &val)) { +    DC->executeText(EXEC_APPEND, va("%s ; ", val)); +  } +} + +void Script_Play(itemDef_t *item, char **args) { +  const char *val; +  if (String_Parse(args, &val)) { +    DC->startLocalSound(DC->registerSound(val, qfalse), CHAN_LOCAL_SOUND); +  } +} + +void Script_playLooped(itemDef_t *item, char **args) { +  const char *val; +  if (String_Parse(args, &val)) { +    DC->stopBackgroundTrack(); +    DC->startBackgroundTrack(val, val); +  } +} + + +commandDef_t commandList[] = +{ +  {"fadein", &Script_FadeIn},                   // group/name +  {"fadeout", &Script_FadeOut},                 // group/name +  {"show", &Script_Show},                       // group/name +  {"hide", &Script_Hide},                       // group/name +  {"setcolor", &Script_SetColor},               // works on this +  {"open", &Script_Open},                       // menu +  {"conditionalopen", &Script_ConditionalOpen}, // menu +  {"close", &Script_Close},                     // menu +  {"setasset", &Script_SetAsset},               // works on this +  {"setbackground", &Script_SetBackground},     // works on this +  {"setitemcolor", &Script_SetItemColor},       // group/name +  {"setteamcolor", &Script_SetTeamColor},       // sets this background color to team color +  {"setfocus", &Script_SetFocus},               // sets this background color to team color +  {"setplayermodel", &Script_SetPlayerModel},   // sets this background color to team color +  {"setplayerhead", &Script_SetPlayerHead},     // sets this background color to team color +  {"transition", &Script_Transition},           // group/name +  {"setcvar", &Script_SetCvar},           // group/name +  {"exec", &Script_Exec},           // group/name +  {"play", &Script_Play},           // group/name +  {"playlooped", &Script_playLooped},           // group/name +  {"orbit", &Script_Orbit}                      // group/name +}; + +int scriptCommandCount = sizeof(commandList) / sizeof(commandDef_t); + + +void Item_RunScript(itemDef_t *item, const char *s) { +  char script[1024], *p; +  int i; +  qboolean bRan; +  memset(script, 0, sizeof(script)); +  if (item && s && s[0]) { +    Q_strcat(script, 1024, s); +    p = script; +    while (1) { +      const char *command; +      // expect command then arguments, ; ends command, NULL ends script +      if (!String_Parse(&p, &command)) { +        return; +      } + +      if (command[0] == ';' && command[1] == '\0') { +        continue; +      } + +      bRan = qfalse; +      for (i = 0; i < scriptCommandCount; i++) { +        if (Q_stricmp(command, commandList[i].name) == 0) { +          (commandList[i].handler(item, &p)); +          bRan = qtrue; +          break; +        } +      } +      // not in our auto list, pass to handler +      if (!bRan) { +        DC->runScript(&p); +      } +    } +  } +} + + +qboolean Item_EnableShowViaCvar(itemDef_t *item, int flag) { +  char script[1024], *p; +  memset(script, 0, sizeof(script)); +  if (item && item->enableCvar && *item->enableCvar && item->cvarTest && *item->cvarTest) { +    char buff[1024]; +    DC->getCVarString(item->cvarTest, buff, sizeof(buff)); + +    Q_strcat(script, 1024, item->enableCvar); +    p = script; +    while (1) { +      const char *val; +      // expect value then ; or NULL, NULL ends list +      if (!String_Parse(&p, &val)) { +        return (item->cvarFlags & flag) ? qfalse : qtrue; +      } + +      if (val[0] == ';' && val[1] == '\0') { +        continue; +      } + +      // enable it if any of the values are true +      if (item->cvarFlags & flag) { +        if (Q_stricmp(buff, val) == 0) { +          return qtrue; +        } +      } else { +        // disable it if any of the values are true +        if (Q_stricmp(buff, val) == 0) { +          return qfalse; +        } +      } + +    } +    return (item->cvarFlags & flag) ? qfalse : qtrue; +  } +  return qtrue; +} + + +// will optionaly set focus to this item +qboolean Item_SetFocus(itemDef_t *item, float x, float y) { +  int i; +  itemDef_t *oldFocus; +  sfxHandle_t *sfx = &DC->Assets.itemFocusSound; +  qboolean playSound = qfalse; +  menuDef_t *parent; // bk001206: = (menuDef_t*)item->parent; +  // sanity check, non-null, not a decoration and does not already have the focus +  if (item == NULL || item->window.flags & WINDOW_DECORATION || item->window.flags & WINDOW_HASFOCUS || !(item->window.flags & WINDOW_VISIBLE)) { +    return qfalse; +  } + +  // bk001206 - this can be NULL. +  parent = (menuDef_t*)item->parent; + +  // items can be enabled and disabled based on cvars +  if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { +    return qfalse; +  } + +  if (item->cvarFlags & (CVAR_SHOW | CVAR_HIDE) && !Item_EnableShowViaCvar(item, CVAR_SHOW)) { +    return qfalse; +  } + +  oldFocus = Menu_ClearFocus(item->parent); + +  if (item->type == ITEM_TYPE_TEXT) { +    rectDef_t r; +    r = item->textRect; +    r.y -= r.h; +    if (Rect_ContainsPoint(&r, x, y)) { +      item->window.flags |= WINDOW_HASFOCUS; +      if (item->focusSound) { +        sfx = &item->focusSound; +      } +      playSound = qtrue; +    } else { +      if (oldFocus) { +        oldFocus->window.flags |= WINDOW_HASFOCUS; +        if (oldFocus->onFocus) { +          Item_RunScript(oldFocus, oldFocus->onFocus); +        } +      } +    } +  } else { +      item->window.flags |= WINDOW_HASFOCUS; +    if (item->onFocus) { +      Item_RunScript(item, item->onFocus); +    } +    if (item->focusSound) { +      sfx = &item->focusSound; +    } +    playSound = qtrue; +  } + +  if (playSound && sfx) { +    DC->startLocalSound( *sfx, CHAN_LOCAL_SOUND ); +  } + +  for (i = 0; i < parent->itemCount; i++) { +    if (parent->items[i] == item) { +      parent->cursorItem = i; +      break; +    } +  } + +  return qtrue; +} + +int Item_ListBox_MaxScroll(itemDef_t *item) { +  listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; +  int count = DC->feederCount(item->special); +  int max; + +  if (item->window.flags & WINDOW_HORIZONTAL) { +    max = count - (item->window.rect.w / listPtr->elementWidth) + 1; +  } +  else { +    max = count - (item->window.rect.h / listPtr->elementHeight) + 1; +  } +  if (max < 0) { +    return 0; +  } +  return max; +} + +int Item_ListBox_ThumbPosition(itemDef_t *item) { +  float max, pos, size; +  listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; + +  max = Item_ListBox_MaxScroll(item); +  if (item->window.flags & WINDOW_HORIZONTAL) { +    size = item->window.rect.w - (SCROLLBAR_SIZE * 2) - 2; +    if (max > 0) { +      pos = (size-SCROLLBAR_SIZE) / (float) max; +    } else { +      pos = 0; +    } +    pos *= listPtr->startPos; +    return item->window.rect.x + 1 + SCROLLBAR_SIZE + pos; +  } +  else { +    size = item->window.rect.h - (SCROLLBAR_SIZE * 2) - 2; +    if (max > 0) { +      pos = (size-SCROLLBAR_SIZE) / (float) max; +    } else { +      pos = 0; +    } +    pos *= listPtr->startPos; +    return item->window.rect.y + 1 + SCROLLBAR_SIZE + pos; +  } +} + +int Item_ListBox_ThumbDrawPosition(itemDef_t *item) { +  int min, max; + +  if (itemCapture == item) { +    if (item->window.flags & WINDOW_HORIZONTAL) { +      min = item->window.rect.x + SCROLLBAR_SIZE + 1; +      max = item->window.rect.x + item->window.rect.w - 2*SCROLLBAR_SIZE - 1; +      if (DC->cursorx >= min + SCROLLBAR_SIZE/2 && DC->cursorx <= max + SCROLLBAR_SIZE/2) { +        return DC->cursorx - SCROLLBAR_SIZE/2; +      } +      else { +        return Item_ListBox_ThumbPosition(item); +      } +    } +    else { +      min = item->window.rect.y + SCROLLBAR_SIZE + 1; +      max = item->window.rect.y + item->window.rect.h - 2*SCROLLBAR_SIZE - 1; +      if (DC->cursory >= min + SCROLLBAR_SIZE/2 && DC->cursory <= max + SCROLLBAR_SIZE/2) { +        return DC->cursory - SCROLLBAR_SIZE/2; +      } +      else { +        return Item_ListBox_ThumbPosition(item); +      } +    } +  } +  else { +    return Item_ListBox_ThumbPosition(item); +  } +} + +float Item_Slider_ThumbPosition(itemDef_t *item) { +  float value, range, x; +  editFieldDef_t *editDef = item->typeData; + +  if (item->text) { +    x = item->textRect.x + item->textRect.w + 8; +  } else { +    x = item->window.rect.x; +  } + +  if (editDef == NULL && item->cvar) { +    return x; +  } + +  value = DC->getCVarValue(item->cvar); + +  if (value < editDef->minVal) { +    value = editDef->minVal; +  } else if (value > editDef->maxVal) { +    value = editDef->maxVal; +  } + +  range = editDef->maxVal - editDef->minVal; +  value -= editDef->minVal; +  value /= range; +  //value /= (editDef->maxVal - editDef->minVal); +  value *= SLIDER_WIDTH; +  x += value; +  // vm fuckage +  //x = x + (((float)value / editDef->maxVal) * SLIDER_WIDTH); +  return x; +} + +int Item_Slider_OverSlider(itemDef_t *item, float x, float y) { +  rectDef_t r; + +  r.x = Item_Slider_ThumbPosition(item) - (SLIDER_THUMB_WIDTH / 2); +  r.y = item->window.rect.y - 2; +  r.w = SLIDER_THUMB_WIDTH; +  r.h = SLIDER_THUMB_HEIGHT; + +  if (Rect_ContainsPoint(&r, x, y)) { +    return WINDOW_LB_THUMB; +  } +  return 0; +} + +int Item_ListBox_OverLB(itemDef_t *item, float x, float y) { +  rectDef_t r; +  listBoxDef_t *listPtr; +  int thumbstart; +  int count; + +  count = DC->feederCount(item->special); +  listPtr = (listBoxDef_t*)item->typeData; +  if (item->window.flags & WINDOW_HORIZONTAL) { +    // check if on left arrow +    r.x = item->window.rect.x; +    r.y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; +    r.h = r.w = SCROLLBAR_SIZE; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_LEFTARROW; +    } +    // check if on right arrow +    r.x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_RIGHTARROW; +    } +    // check if on thumb +    thumbstart = Item_ListBox_ThumbPosition(item); +    r.x = thumbstart; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_THUMB; +    } +    r.x = item->window.rect.x + SCROLLBAR_SIZE; +    r.w = thumbstart - r.x; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_PGUP; +    } +    r.x = thumbstart + SCROLLBAR_SIZE; +    r.w = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_PGDN; +    } +  } else { +    r.x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE; +    r.y = item->window.rect.y; +    r.h = r.w = SCROLLBAR_SIZE; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_LEFTARROW; +    } +    r.y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_RIGHTARROW; +    } +    thumbstart = Item_ListBox_ThumbPosition(item); +    r.y = thumbstart; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_THUMB; +    } +    r.y = item->window.rect.y + SCROLLBAR_SIZE; +    r.h = thumbstart - r.y; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_PGUP; +    } +    r.y = thumbstart + SCROLLBAR_SIZE; +    r.h = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE; +    if (Rect_ContainsPoint(&r, x, y)) { +      return WINDOW_LB_PGDN; +    } +  } +  return 0; +} + + +void Item_ListBox_MouseEnter(itemDef_t *item, float x, float y) +{ +  rectDef_t r; +  listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; + +  item->window.flags &= ~(WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN); +  item->window.flags |= Item_ListBox_OverLB(item, x, y); + +  if (item->window.flags & WINDOW_HORIZONTAL) { +    if (!(item->window.flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN))) { +      // check for selection hit as we have exausted buttons and thumb +      if (listPtr->elementStyle == LISTBOX_IMAGE) { +        r.x = item->window.rect.x; +        r.y = item->window.rect.y; +        r.h = item->window.rect.h - SCROLLBAR_SIZE; +        r.w = item->window.rect.w - listPtr->drawPadding; +        if (Rect_ContainsPoint(&r, x, y)) { +          listPtr->cursorPos =  (int)((x - r.x) / listPtr->elementWidth)  + listPtr->startPos; +          if (listPtr->cursorPos >= listPtr->endPos) { +            listPtr->cursorPos = listPtr->endPos; +          } +        } +      } else { +        // text hit.. +      } +    } +  } else if (!(item->window.flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW | WINDOW_LB_THUMB | WINDOW_LB_PGUP | WINDOW_LB_PGDN))) { +    r.x = item->window.rect.x; +    r.y = item->window.rect.y; +    r.w = item->window.rect.w - SCROLLBAR_SIZE; +    r.h = item->window.rect.h - listPtr->drawPadding; +    if (Rect_ContainsPoint(&r, x, y)) { +      listPtr->cursorPos =  (int)((y - 2 - r.y) / listPtr->elementHeight)  + listPtr->startPos; +      if (listPtr->cursorPos > listPtr->endPos) { +        listPtr->cursorPos = listPtr->endPos; +      } +    } +  } +} + +void Item_MouseEnter(itemDef_t *item, float x, float y) { +  rectDef_t r; +  if (item) { +    r = item->textRect; +    r.y -= r.h; +    // in the text rect? + +    // items can be enabled and disabled based on cvars +    if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { +      return; +    } + +    if (item->cvarFlags & (CVAR_SHOW | CVAR_HIDE) && !Item_EnableShowViaCvar(item, CVAR_SHOW)) { +      return; +    } + +    if (Rect_ContainsPoint(&r, x, y)) { +      if (!(item->window.flags & WINDOW_MOUSEOVERTEXT)) { +        Item_RunScript(item, item->mouseEnterText); +        item->window.flags |= WINDOW_MOUSEOVERTEXT; +      } +      if (!(item->window.flags & WINDOW_MOUSEOVER)) { +        Item_RunScript(item, item->mouseEnter); +        item->window.flags |= WINDOW_MOUSEOVER; +      } + +    } else { +      // not in the text rect +      if (item->window.flags & WINDOW_MOUSEOVERTEXT) { +        // if we were +        Item_RunScript(item, item->mouseExitText); +        item->window.flags &= ~WINDOW_MOUSEOVERTEXT; +      } +      if (!(item->window.flags & WINDOW_MOUSEOVER)) { +        Item_RunScript(item, item->mouseEnter); +        item->window.flags |= WINDOW_MOUSEOVER; +      } + +      if (item->type == ITEM_TYPE_LISTBOX) { +        Item_ListBox_MouseEnter(item, x, y); +      } +    } +  } +} + +void Item_MouseLeave(itemDef_t *item) { +  if (item) { +    if (item->window.flags & WINDOW_MOUSEOVERTEXT) { +      Item_RunScript(item, item->mouseExitText); +      item->window.flags &= ~WINDOW_MOUSEOVERTEXT; +    } +    Item_RunScript(item, item->mouseExit); +    item->window.flags &= ~(WINDOW_LB_RIGHTARROW | WINDOW_LB_LEFTARROW); +  } +} + +itemDef_t *Menu_HitTest(menuDef_t *menu, float x, float y) { +  int i; +  for (i = 0; i < menu->itemCount; i++) { +    if (Rect_ContainsPoint(&menu->items[i]->window.rect, x, y)) { +      return menu->items[i]; +    } +  } +  return NULL; +} + +void Item_SetMouseOver(itemDef_t *item, qboolean focus) { +  if (item) { +    if (focus) { +      item->window.flags |= WINDOW_MOUSEOVER; +    } else { +      item->window.flags &= ~WINDOW_MOUSEOVER; +    } +  } +} + + +qboolean Item_OwnerDraw_HandleKey(itemDef_t *item, int key) { +  if (item && DC->ownerDrawHandleKey) { +    return DC->ownerDrawHandleKey(item->window.ownerDraw, item->window.ownerDrawFlags, &item->special, key); +  } +  return qfalse; +} + +qboolean Item_ListBox_HandleKey(itemDef_t *item, int key, qboolean down, qboolean force) { +  listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; +  int count = DC->feederCount(item->special); +  int max, viewmax; + +  if (force || (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && item->window.flags & WINDOW_HASFOCUS)) { +    max = Item_ListBox_MaxScroll(item); +    if (item->window.flags & WINDOW_HORIZONTAL) { +      viewmax = (item->window.rect.w / listPtr->elementWidth); +      if ( key == K_LEFTARROW || key == K_KP_LEFTARROW ) +      { +        if (!listPtr->notselectable) { +          listPtr->cursorPos--; +          if (listPtr->cursorPos < 0) { +            listPtr->cursorPos = 0; +          } +          if (listPtr->cursorPos < listPtr->startPos) { +            listPtr->startPos = listPtr->cursorPos; +          } +          if (listPtr->cursorPos >= listPtr->startPos + viewmax) { +            listPtr->startPos = listPtr->cursorPos - viewmax + 1; +          } +          item->cursorPos = listPtr->cursorPos; +          DC->feederSelection(item->special, item->cursorPos); +        } +        else { +          listPtr->startPos--; +          if (listPtr->startPos < 0) +            listPtr->startPos = 0; +        } +        return qtrue; +      } +      if ( key == K_RIGHTARROW || key == K_KP_RIGHTARROW ) +      { +        if (!listPtr->notselectable) { +          listPtr->cursorPos++; +          if (listPtr->cursorPos < listPtr->startPos) { +            listPtr->startPos = listPtr->cursorPos; +          } +          if (listPtr->cursorPos >= count) { +            listPtr->cursorPos = count-1; +          } +          if (listPtr->cursorPos >= listPtr->startPos + viewmax) { +            listPtr->startPos = listPtr->cursorPos - viewmax + 1; +          } +          item->cursorPos = listPtr->cursorPos; +          DC->feederSelection(item->special, item->cursorPos); +        } +        else { +          listPtr->startPos++; +          if (listPtr->startPos >= count) +            listPtr->startPos = count-1; +        } +        return qtrue; +      } +    } +    else { +      viewmax = (item->window.rect.h / listPtr->elementHeight); +      if ( key == K_UPARROW || key == K_KP_UPARROW ) +      { +        if (!listPtr->notselectable) { +          listPtr->cursorPos--; +          if (listPtr->cursorPos < 0) { +            listPtr->cursorPos = 0; +          } +          if (listPtr->cursorPos < listPtr->startPos) { +            listPtr->startPos = listPtr->cursorPos; +          } +          if (listPtr->cursorPos >= listPtr->startPos + viewmax) { +            listPtr->startPos = listPtr->cursorPos - viewmax + 1; +          } +          item->cursorPos = listPtr->cursorPos; +          DC->feederSelection(item->special, item->cursorPos); +        } +        else { +          listPtr->startPos--; +          if (listPtr->startPos < 0) +            listPtr->startPos = 0; +        } +        return qtrue; +      } +      if ( key == K_DOWNARROW || key == K_KP_DOWNARROW ) +      { +        if (!listPtr->notselectable) { +          listPtr->cursorPos++; +          if (listPtr->cursorPos < listPtr->startPos) { +            listPtr->startPos = listPtr->cursorPos; +          } +          if (listPtr->cursorPos >= count) { +            listPtr->cursorPos = count-1; +          } +          if (listPtr->cursorPos >= listPtr->startPos + viewmax) { +            listPtr->startPos = listPtr->cursorPos - viewmax + 1; +          } +          item->cursorPos = listPtr->cursorPos; +          DC->feederSelection(item->special, item->cursorPos); +        } +        else { +          listPtr->startPos++; +          if (listPtr->startPos > max) +            listPtr->startPos = max; +        } +        return qtrue; +      } +    } +    // mouse hit +    if (key == K_MOUSE1 || key == K_MOUSE2) { +      if (item->window.flags & WINDOW_LB_LEFTARROW) { +        listPtr->startPos--; +        if (listPtr->startPos < 0) { +          listPtr->startPos = 0; +        } +      } else if (item->window.flags & WINDOW_LB_RIGHTARROW) { +        // one down +        listPtr->startPos++; +        if (listPtr->startPos > max) { +          listPtr->startPos = max; +        } +      } else if (item->window.flags & WINDOW_LB_PGUP) { +        // page up +        listPtr->startPos -= viewmax; +        if (listPtr->startPos < 0) { +          listPtr->startPos = 0; +        } +      } else if (item->window.flags & WINDOW_LB_PGDN) { +        // page down +        listPtr->startPos += viewmax; +        if (listPtr->startPos > max) { +          listPtr->startPos = max; +        } +      } else if (item->window.flags & WINDOW_LB_THUMB) { +        // Display_SetCaptureItem(item); +      } else { +        // select an item +        if (DC->realTime < lastListBoxClickTime && listPtr->doubleClick) { +          Item_RunScript(item, listPtr->doubleClick); +        } +        lastListBoxClickTime = DC->realTime + DOUBLE_CLICK_DELAY; +        if (item->cursorPos != listPtr->cursorPos) { +          item->cursorPos = listPtr->cursorPos; +          DC->feederSelection(item->special, item->cursorPos); +        } +      } +      return qtrue; +    } + +    // Scroll wheel +    if (key == K_MWHEELUP) { +      listPtr->startPos--; +      if (listPtr->startPos < 0) { +        listPtr->startPos = 0; +      } +      return qtrue; +    } +    if (key == K_MWHEELDOWN) { +      listPtr->startPos++; +      if (listPtr->startPos > max) { +        listPtr->startPos = max; +      } +      return qtrue; +    } + +    // Invoke the doubleClick handler when enter is pressed +    if( key == K_ENTER ) +    { +      if( listPtr->doubleClick ) +        Item_RunScript( item, listPtr->doubleClick ); + +      return qtrue; +    } + +    if ( key == K_HOME || key == K_KP_HOME) { +      // home +      listPtr->startPos = 0; +      return qtrue; +    } +    if ( key == K_END || key == K_KP_END) { +      // end +      listPtr->startPos = max; +      return qtrue; +    } +    if (key == K_PGUP || key == K_KP_PGUP ) { +      // page up +      if (!listPtr->notselectable) { +        listPtr->cursorPos -= viewmax; +        if (listPtr->cursorPos < 0) { +          listPtr->cursorPos = 0; +        } +        if (listPtr->cursorPos < listPtr->startPos) { +          listPtr->startPos = listPtr->cursorPos; +        } +        if (listPtr->cursorPos >= listPtr->startPos + viewmax) { +          listPtr->startPos = listPtr->cursorPos - viewmax + 1; +        } +        item->cursorPos = listPtr->cursorPos; +        DC->feederSelection(item->special, item->cursorPos); +      } +      else { +        listPtr->startPos -= viewmax; +        if (listPtr->startPos < 0) { +          listPtr->startPos = 0; +        } +      } +      return qtrue; +    } +    if ( key == K_PGDN || key == K_KP_PGDN ) { +      // page down +      if (!listPtr->notselectable) { +        listPtr->cursorPos += viewmax; +        if (listPtr->cursorPos < listPtr->startPos) { +          listPtr->startPos = listPtr->cursorPos; +        } +        if (listPtr->cursorPos >= count) { +          listPtr->cursorPos = count-1; +        } +        if (listPtr->cursorPos >= listPtr->startPos + viewmax) { +          listPtr->startPos = listPtr->cursorPos - viewmax + 1; +        } +        item->cursorPos = listPtr->cursorPos; +        DC->feederSelection(item->special, item->cursorPos); +      } +      else { +        listPtr->startPos += viewmax; +        if (listPtr->startPos > max) { +          listPtr->startPos = max; +        } +      } +      return qtrue; +    } +  } +  return qfalse; +} + +qboolean Item_YesNo_HandleKey(itemDef_t *item, int key) { + +  if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && item->window.flags & WINDOW_HASFOCUS && item->cvar) { +    if (key == K_MOUSE1 || key == K_ENTER || key == K_MOUSE2 || key == K_MOUSE3) { +      DC->setCVar(item->cvar, va("%i", !DC->getCVarValue(item->cvar))); +      return qtrue; +    } +  } + +  return qfalse; + +} + +int Item_Multi_CountSettings(itemDef_t *item) { +  multiDef_t *multiPtr = (multiDef_t*)item->typeData; +  if (multiPtr == NULL) { +    return 0; +  } +  return multiPtr->count; +} + +int Item_Multi_FindCvarByValue(itemDef_t *item) { +  char buff[1024]; +  float value = 0; +  int i; +  multiDef_t *multiPtr = (multiDef_t*)item->typeData; +  if (multiPtr) { +    if (multiPtr->strDef) { +      DC->getCVarString(item->cvar, buff, sizeof(buff)); +    } else { +      value = DC->getCVarValue(item->cvar); +    } +    for (i = 0; i < multiPtr->count; i++) { +      if (multiPtr->strDef) { +        if (Q_stricmp(buff, multiPtr->cvarStr[i]) == 0) { +          return i; +        } +      } else { +        if (multiPtr->cvarValue[i] == value) { +          return i; +        } +      } +    } +  } +  return 0; +} + +const char *Item_Multi_Setting(itemDef_t *item) { +  char buff[1024]; +  float value = 0; +  int i; +  multiDef_t *multiPtr = (multiDef_t*)item->typeData; +  if (multiPtr) { +    if (multiPtr->strDef) { +      DC->getCVarString(item->cvar, buff, sizeof(buff)); +    } else { +      value = DC->getCVarValue(item->cvar); +    } +    for (i = 0; i < multiPtr->count; i++) { +      if (multiPtr->strDef) { +        if (Q_stricmp(buff, multiPtr->cvarStr[i]) == 0) { +          return multiPtr->cvarList[i]; +        } +      } else { +        if (multiPtr->cvarValue[i] == value) { +          return multiPtr->cvarList[i]; +        } +      } +    } +  } +  return ""; +} + +qboolean Item_Multi_HandleKey(itemDef_t *item, int key) { +  multiDef_t *multiPtr = (multiDef_t*)item->typeData; +  if (multiPtr) { +    if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && item->window.flags & WINDOW_HASFOCUS && item->cvar) { +      if (key == K_MOUSE1 || key == K_ENTER || key == K_MOUSE2 || key == K_MOUSE3) { +        int current = Item_Multi_FindCvarByValue(item) + 1; +        int max = Item_Multi_CountSettings(item); +        if ( current < 0 || current >= max ) { +          current = 0; +        } +        if (multiPtr->strDef) { +          DC->setCVar(item->cvar, multiPtr->cvarStr[current]); +        } else { +          float value = multiPtr->cvarValue[current]; +          if (((float)((int) value)) == value) { +            DC->setCVar(item->cvar, va("%i", (int) value )); +          } +          else { +            DC->setCVar(item->cvar, va("%f", value )); +          } +        } +        return qtrue; +      } +    } +  } +  return qfalse; +} + +qboolean Item_TextField_HandleKey(itemDef_t *item, int key) { +  char buff[1024]; +  int len; +  itemDef_t *newItem = NULL; +  editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; + +  if (item->cvar) { + +    memset(buff, 0, sizeof(buff)); +    DC->getCVarString(item->cvar, buff, sizeof(buff)); +    len = strlen(buff); +    if (editPtr->maxChars && len > editPtr->maxChars) { +      len = editPtr->maxChars; +    } +    if ( key & K_CHAR_FLAG ) { +      key &= ~K_CHAR_FLAG; + + +      if (key == 'h' - 'a' + 1 )  { // ctrl-h is backspace +        if ( item->cursorPos > 0 ) { +          memmove( &buff[item->cursorPos - 1], &buff[item->cursorPos], len + 1 - item->cursorPos); +          item->cursorPos--; +          if (item->cursorPos < editPtr->paintOffset) { +            editPtr->paintOffset--; +          } +        } +        DC->setCVar(item->cvar, buff); +          return qtrue; +      } + + +      // +      // ignore any non printable chars +      // +      if ( key < 32 || !item->cvar) { +          return qtrue; +        } + +      if (item->type == ITEM_TYPE_NUMERICFIELD) { +        if (key < '0' || key > '9') { +          return qfalse; +        } +      } + +      if (!DC->getOverstrikeMode()) { +        if (( len == MAX_EDITFIELD - 1 ) || (editPtr->maxChars && len >= editPtr->maxChars)) { +          return qtrue; +        } +        memmove( &buff[item->cursorPos + 1], &buff[item->cursorPos], len + 1 - item->cursorPos ); +      } else { +        if (editPtr->maxChars && item->cursorPos >= editPtr->maxChars) { +          return qtrue; +        } +      } + +      buff[item->cursorPos] = key; + +      DC->setCVar(item->cvar, buff); + +      if (item->cursorPos < len + 1) { +        item->cursorPos++; +        if (editPtr->maxPaintChars && item->cursorPos > editPtr->maxPaintChars) { +          editPtr->paintOffset++; +        } +      } + +    } else { + +      if ( key == K_DEL || key == K_KP_DEL ) { +        if ( item->cursorPos < len ) { +          memmove( buff + item->cursorPos, buff + item->cursorPos + 1, len - item->cursorPos); +          DC->setCVar(item->cvar, buff); +        } +        return qtrue; +      } + +      if ( key == K_RIGHTARROW || key == K_KP_RIGHTARROW ) +      { +        if (editPtr->maxPaintChars && item->cursorPos >= editPtr->maxPaintChars && item->cursorPos < len) { +          item->cursorPos++; +          editPtr->paintOffset++; +          return qtrue; +        } +        if (item->cursorPos < len) { +          item->cursorPos++; +        } +        return qtrue; +      } + +      if ( key == K_LEFTARROW || key == K_KP_LEFTARROW ) +      { +        if ( item->cursorPos > 0 ) { +          item->cursorPos--; +        } +        if (item->cursorPos < editPtr->paintOffset) { +          editPtr->paintOffset--; +        } +        return qtrue; +      } + +      if ( key == K_HOME || key == K_KP_HOME) {// || ( tolower(key) == 'a' && trap_Key_IsDown( K_CTRL ) ) ) { +        item->cursorPos = 0; +        editPtr->paintOffset = 0; +        return qtrue; +      } + +      if ( key == K_END || key == K_KP_END)  {// ( tolower(key) == 'e' && trap_Key_IsDown( K_CTRL ) ) ) { +        item->cursorPos = len; +        if(item->cursorPos > editPtr->maxPaintChars) { +          editPtr->paintOffset = len - editPtr->maxPaintChars; +        } +        return qtrue; +      } + +      if ( key == K_INS || key == K_KP_INS ) { +        DC->setOverstrikeMode(!DC->getOverstrikeMode()); +        return qtrue; +      } +    } + +    if (key == K_TAB || key == K_DOWNARROW || key == K_KP_DOWNARROW) { +      newItem = Menu_SetNextCursorItem(item->parent); +      if (newItem && (newItem->type == ITEM_TYPE_EDITFIELD || newItem->type == ITEM_TYPE_NUMERICFIELD)) { +        g_editItem = newItem; +      } +    } + +    if (key == K_UPARROW || key == K_KP_UPARROW) { +      newItem = Menu_SetPrevCursorItem(item->parent); +      if (newItem && (newItem->type == ITEM_TYPE_EDITFIELD || newItem->type == ITEM_TYPE_NUMERICFIELD)) { +        g_editItem = newItem; +      } +    } + +    if ( key == K_ENTER || key == K_KP_ENTER || key == K_ESCAPE)  { +      return qfalse; +    } + +    return qtrue; +  } +  return qfalse; + +} + +static void Scroll_ListBox_AutoFunc(void *p) { +  scrollInfo_t *si = (scrollInfo_t*)p; +  if (DC->realTime > si->nextScrollTime) { +    // need to scroll which is done by simulating a click to the item +    // this is done a bit sideways as the autoscroll "knows" that the item is a listbox +    // so it calls it directly +    Item_ListBox_HandleKey(si->item, si->scrollKey, qtrue, qfalse); +    si->nextScrollTime = DC->realTime + si->adjustValue; +  } + +  if (DC->realTime > si->nextAdjustTime) { +    si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; +    if (si->adjustValue > SCROLL_TIME_FLOOR) { +      si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET; +    } +  } +} + +static void Scroll_ListBox_ThumbFunc(void *p) { +  scrollInfo_t *si = (scrollInfo_t*)p; +  rectDef_t r; +  int pos, max; + +  listBoxDef_t *listPtr = (listBoxDef_t*)si->item->typeData; +  if (si->item->window.flags & WINDOW_HORIZONTAL) { +    if (DC->cursorx == si->xStart) { +      return; +    } +    r.x = si->item->window.rect.x + SCROLLBAR_SIZE + 1; +    r.y = si->item->window.rect.y + si->item->window.rect.h - SCROLLBAR_SIZE - 1; +    r.h = SCROLLBAR_SIZE; +    r.w = si->item->window.rect.w - (SCROLLBAR_SIZE*2) - 2; +    max = Item_ListBox_MaxScroll(si->item); +    // +    pos = (DC->cursorx - r.x - SCROLLBAR_SIZE/2) * max / (r.w - SCROLLBAR_SIZE); +    if (pos < 0) { +      pos = 0; +    } +    else if (pos > max) { +      pos = max; +    } +    listPtr->startPos = pos; +    si->xStart = DC->cursorx; +  } +  else if (DC->cursory != si->yStart) { + +    r.x = si->item->window.rect.x + si->item->window.rect.w - SCROLLBAR_SIZE - 1; +    r.y = si->item->window.rect.y + SCROLLBAR_SIZE + 1; +    r.h = si->item->window.rect.h - (SCROLLBAR_SIZE*2) - 2; +    r.w = SCROLLBAR_SIZE; +    max = Item_ListBox_MaxScroll(si->item); +    // +    pos = (DC->cursory - r.y - SCROLLBAR_SIZE/2) * max / (r.h - SCROLLBAR_SIZE); +    if (pos < 0) { +      pos = 0; +    } +    else if (pos > max) { +      pos = max; +    } +    listPtr->startPos = pos; +    si->yStart = DC->cursory; +  } + +  if (DC->realTime > si->nextScrollTime) { +    // need to scroll which is done by simulating a click to the item +    // this is done a bit sideways as the autoscroll "knows" that the item is a listbox +    // so it calls it directly +    Item_ListBox_HandleKey(si->item, si->scrollKey, qtrue, qfalse); +    si->nextScrollTime = DC->realTime + si->adjustValue; +  } + +  if (DC->realTime > si->nextAdjustTime) { +    si->nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; +    if (si->adjustValue > SCROLL_TIME_FLOOR) { +      si->adjustValue -= SCROLL_TIME_ADJUSTOFFSET; +    } +  } +} + +static void Scroll_Slider_ThumbFunc(void *p) { +  float x, value, cursorx; +  scrollInfo_t *si = (scrollInfo_t*)p; +  editFieldDef_t *editDef = si->item->typeData; + +  if (si->item->text) { +    x = si->item->textRect.x + si->item->textRect.w + 8; +  } else { +    x = si->item->window.rect.x; +  } + +  cursorx = DC->cursorx; + +  if (cursorx < x) { +    cursorx = x; +  } else if (cursorx > x + SLIDER_WIDTH) { +    cursorx = x + SLIDER_WIDTH; +  } +  value = cursorx - x; +  value /= SLIDER_WIDTH; +  value *= (editDef->maxVal - editDef->minVal); +  value += editDef->minVal; +  DC->setCVar(si->item->cvar, va("%f", value)); +} + +void Item_StartCapture(itemDef_t *item, int key) { +  int flags; +  switch (item->type) { +    case ITEM_TYPE_EDITFIELD: +    case ITEM_TYPE_NUMERICFIELD: + +    case ITEM_TYPE_LISTBOX: +    { +      flags = Item_ListBox_OverLB(item, DC->cursorx, DC->cursory); +      if (flags & (WINDOW_LB_LEFTARROW | WINDOW_LB_RIGHTARROW)) { +        scrollInfo.nextScrollTime = DC->realTime + SCROLL_TIME_START; +        scrollInfo.nextAdjustTime = DC->realTime + SCROLL_TIME_ADJUST; +        scrollInfo.adjustValue = SCROLL_TIME_START; +        scrollInfo.scrollKey = key; +        scrollInfo.scrollDir = (flags & WINDOW_LB_LEFTARROW) ? qtrue : qfalse; +        scrollInfo.item = item; +        captureData = &scrollInfo; +        captureFunc = &Scroll_ListBox_AutoFunc; +        itemCapture = item; +      } else if (flags & WINDOW_LB_THUMB) { +        scrollInfo.scrollKey = key; +        scrollInfo.item = item; +        scrollInfo.xStart = DC->cursorx; +        scrollInfo.yStart = DC->cursory; +        captureData = &scrollInfo; +        captureFunc = &Scroll_ListBox_ThumbFunc; +        itemCapture = item; +      } +      break; +    } +    case ITEM_TYPE_SLIDER: +    { +      flags = Item_Slider_OverSlider(item, DC->cursorx, DC->cursory); +      if (flags & WINDOW_LB_THUMB) { +        scrollInfo.scrollKey = key; +        scrollInfo.item = item; +        scrollInfo.xStart = DC->cursorx; +        scrollInfo.yStart = DC->cursory; +        captureData = &scrollInfo; +        captureFunc = &Scroll_Slider_ThumbFunc; +        itemCapture = item; +      } +      break; +    } +  } +} + +void Item_StopCapture(itemDef_t *item) { + +} + +qboolean Item_Slider_HandleKey(itemDef_t *item, int key, qboolean down) { +  float x, value, width, work; + +  if (item->window.flags & WINDOW_HASFOCUS && item->cvar && Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { +    if (key == K_MOUSE1 || key == K_ENTER || key == K_MOUSE2 || key == K_MOUSE3) { +      editFieldDef_t *editDef = item->typeData; +      if (editDef) { +        rectDef_t testRect; +        width = SLIDER_WIDTH; +        if (item->text) { +          x = item->textRect.x + item->textRect.w + 8; +        } else { +          x = item->window.rect.x; +        } + +        testRect = item->window.rect; +        testRect.x = x; +        value = (float)SLIDER_THUMB_WIDTH / 2; +        testRect.x -= value; +        testRect.w = (SLIDER_WIDTH + (float)SLIDER_THUMB_WIDTH / 2); +        if (Rect_ContainsPoint(&testRect, DC->cursorx, DC->cursory)) { +          work = DC->cursorx - x; +          value = work / width; +          value *= (editDef->maxVal - editDef->minVal); +          // vm fuckage +          // value = (((float)(DC->cursorx - x)/ SLIDER_WIDTH) * (editDef->maxVal - editDef->minVal)); +          value += editDef->minVal; +          DC->setCVar(item->cvar, va("%f", value)); +          return qtrue; +        } +      } +    } +  } +  return qfalse; +} + + +qboolean Item_HandleKey(itemDef_t *item, int key, qboolean down) { + +  if (itemCapture) { +    Item_StopCapture(itemCapture); +    itemCapture = NULL; +    captureFunc = voidFunction; +    captureData = NULL; +  } else { +    // bk001206 - parentheses +    if ( down && ( key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3 ) ) { +      Item_StartCapture(item, key); +    } +  } + +  if (!down) { +    return qfalse; +  } + +  switch (item->type) { +    case ITEM_TYPE_BUTTON: +      return qfalse; +      break; +    case ITEM_TYPE_RADIOBUTTON: +      return qfalse; +      break; +    case ITEM_TYPE_CHECKBOX: +      return qfalse; +      break; +    case ITEM_TYPE_EDITFIELD: +    case ITEM_TYPE_NUMERICFIELD: +      //return Item_TextField_HandleKey(item, key); +      return qfalse; +      break; +    case ITEM_TYPE_COMBO: +      return qfalse; +      break; +    case ITEM_TYPE_LISTBOX: +      return Item_ListBox_HandleKey(item, key, down, qfalse); +      break; +    case ITEM_TYPE_YESNO: +      return Item_YesNo_HandleKey(item, key); +      break; +    case ITEM_TYPE_MULTI: +      return Item_Multi_HandleKey(item, key); +      break; +    case ITEM_TYPE_OWNERDRAW: +      return Item_OwnerDraw_HandleKey(item, key); +      break; +    case ITEM_TYPE_BIND: +      return Item_Bind_HandleKey(item, key, down); +      break; +    case ITEM_TYPE_SLIDER: +      return Item_Slider_HandleKey(item, key, down); +      break; +    //case ITEM_TYPE_IMAGE: +    //  Item_Image_Paint(item); +    //  break; +    default: +      return qfalse; +      break; +  } + +  //return qfalse; +} + +void Item_Action(itemDef_t *item) { +  if (item) { +    Item_RunScript(item, item->action); +  } +} + +itemDef_t *Menu_SetPrevCursorItem(menuDef_t *menu) { +  qboolean wrapped = qfalse; +  int oldCursor = menu->cursorItem; + +  if (menu->cursorItem < 0) { +    menu->cursorItem = menu->itemCount-1; +    wrapped = qtrue; +  } + +  while (menu->cursorItem > -1) { + +    menu->cursorItem--; +    if (menu->cursorItem < 0 && !wrapped) { +      wrapped = qtrue; +      menu->cursorItem = menu->itemCount -1; +    } + +    if (Item_SetFocus(menu->items[menu->cursorItem], DC->cursorx, DC->cursory)) { +      Menu_HandleMouseMove(menu, menu->items[menu->cursorItem]->window.rect.x + 1, menu->items[menu->cursorItem]->window.rect.y + 1); +      return menu->items[menu->cursorItem]; +    } +  } +  menu->cursorItem = oldCursor; +  return NULL; + +} + +itemDef_t *Menu_SetNextCursorItem(menuDef_t *menu) { + +  qboolean wrapped = qfalse; +  int oldCursor = menu->cursorItem; + + +  if (menu->cursorItem == -1) { +    menu->cursorItem = 0; +    wrapped = qtrue; +  } + +  while (menu->cursorItem < menu->itemCount) { + +    menu->cursorItem++; +    if (menu->cursorItem >= menu->itemCount && !wrapped) { +      wrapped = qtrue; +      menu->cursorItem = 0; +    } +    if (Item_SetFocus(menu->items[menu->cursorItem], DC->cursorx, DC->cursory)) { +      Menu_HandleMouseMove(menu, menu->items[menu->cursorItem]->window.rect.x + 1, menu->items[menu->cursorItem]->window.rect.y + 1); +      return menu->items[menu->cursorItem]; +    } + +  } + +  menu->cursorItem = oldCursor; +  return NULL; +} + +static void Window_CloseCinematic(windowDef_t *window) { +  if (window->style == WINDOW_STYLE_CINEMATIC && window->cinematic >= 0) { +    DC->stopCinematic(window->cinematic); +    window->cinematic = -1; +  } +} + +static void Menu_CloseCinematics(menuDef_t *menu) { +  if (menu) { +    int i; +    Window_CloseCinematic(&menu->window); +    for (i = 0; i < menu->itemCount; i++) { +      Window_CloseCinematic(&menu->items[i]->window); +      if (menu->items[i]->type == ITEM_TYPE_OWNERDRAW) { +        DC->stopCinematic(0-menu->items[i]->window.ownerDraw); +      } +    } +  } +} + +static void Display_CloseCinematics( void ) { +  int i; +  for (i = 0; i < menuCount; i++) { +    Menu_CloseCinematics(&Menus[i]); +  } +} + +void  Menus_Activate(menuDef_t *menu) { +  menu->window.flags |= (WINDOW_HASFOCUS | WINDOW_VISIBLE); +  if (menu->onOpen) { +    itemDef_t item; +    item.parent = menu; +    Item_RunScript(&item, menu->onOpen); +  } + +  if (menu->soundName && *menu->soundName) { +//    DC->stopBackgroundTrack();          // you don't want to do this since it will reset s_rawend +    DC->startBackgroundTrack(menu->soundName, menu->soundName); +  } + +  Display_CloseCinematics(); + +} + +int Display_VisibleMenuCount( void ) { +  int i, count; +  count = 0; +  for (i = 0; i < menuCount; i++) { +    if (Menus[i].window.flags & (WINDOW_FORCED | WINDOW_VISIBLE)) { +      count++; +    } +  } +  return count; +} + +void Menus_HandleOOBClick(menuDef_t *menu, int key, qboolean down) { +  if (menu) { +    int i; +    // basically the behaviour we are looking for is if there are windows in the stack.. see if +    // the cursor is within any of them.. if not close them otherwise activate them and pass the +    // key on.. force a mouse move to activate focus and script stuff +    if (down && menu->window.flags & WINDOW_OOB_CLICK) { +      Menu_RunCloseScript(menu); +      menu->window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE); +    } + +    for (i = 0; i < menuCount; i++) { +      if (Menu_OverActiveItem(&Menus[i], DC->cursorx, DC->cursory)) { +        Menu_RunCloseScript(menu); +        menu->window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE); +        Menus_Activate(&Menus[i]); +        Menu_HandleMouseMove(&Menus[i], DC->cursorx, DC->cursory); +        Menu_HandleKey(&Menus[i], key, down); +      } +    } + +    if (Display_VisibleMenuCount() == 0) { +      if (DC->Pause) { +        DC->Pause(qfalse); +      } +    } +    Display_CloseCinematics(); +  } +} + +static rectDef_t *Item_CorrectedTextRect(itemDef_t *item) { +  static rectDef_t rect; +  memset(&rect, 0, sizeof(rectDef_t)); +  if (item) { +    rect = item->textRect; +    if (rect.w) { +      rect.y -= rect.h; +    } +  } +  return ▭ +} + +void Menu_HandleKey(menuDef_t *menu, int key, qboolean down) { +  int i; +  itemDef_t *item = NULL; +  qboolean inHandler = qfalse; + +  if (inHandler) { +    return; +  } + +  inHandler = qtrue; +  if (g_waitingForKey && down) { +    Item_Bind_HandleKey(g_bindItem, key, down); +    inHandler = qfalse; +    return; +  } + +  if (g_editingField && down) { +    if (!Item_TextField_HandleKey(g_editItem, key)) { +      g_editingField = qfalse; +      g_editItem = NULL; +      inHandler = qfalse; +      return; +    } else if (key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3) { +      g_editingField = qfalse; +      g_editItem = NULL; +      Display_MouseMove(NULL, DC->cursorx, DC->cursory); +    } else if (key == K_TAB || key == K_UPARROW || key == K_DOWNARROW) { +      return; +    } +  } + +  if (menu == NULL) { +    inHandler = qfalse; +    return; +  } + +    // see if the mouse is within the window bounds and if so is this a mouse click +  if (down && !(menu->window.flags & WINDOW_POPUP) && !Rect_ContainsPoint(&menu->window.rect, DC->cursorx, DC->cursory)) { +    static qboolean inHandleKey = qfalse; +    // bk001206 - parentheses +    if (!inHandleKey && ( key == K_MOUSE1 || key == K_MOUSE2 || key == K_MOUSE3 ) ) { +      inHandleKey = qtrue; +      Menus_HandleOOBClick(menu, key, down); +      inHandleKey = qfalse; +      inHandler = qfalse; +      return; +    } +  } + +  // get the item with focus +  for (i = 0; i < menu->itemCount; i++) { +    if (menu->items[i]->window.flags & WINDOW_HASFOCUS) { +      item = menu->items[i]; +    } +  } + +  if (item != NULL) { +    if (Item_HandleKey(item, key, down)) { +      Item_Action(item); +      inHandler = qfalse; +      return; +    } +  } + +  if (!down) { +    inHandler = qfalse; +    return; +  } + +  // default handling +  switch ( key ) { + +    case K_F11: +      if (DC->getCVarValue("developer")) { +        debugMode ^= 1; +      } +      break; + +    case K_F12: +      if (DC->getCVarValue("developer")) { +        DC->executeText(EXEC_APPEND, "screenshot\n"); +      } +      break; +    case K_KP_UPARROW: +    case K_UPARROW: +      Menu_SetPrevCursorItem(menu); +      break; + +    case K_ESCAPE: +      if (!g_waitingForKey && menu->onESC) { +        itemDef_t it; +        it.parent = menu; +        Item_RunScript(&it, menu->onESC); +      } +      break; +    case K_TAB: +    case K_KP_DOWNARROW: +    case K_DOWNARROW: +      Menu_SetNextCursorItem(menu); +      break; + +    case K_MOUSE1: +    case K_MOUSE2: +      if (item) { +        if (item->type == ITEM_TYPE_TEXT) { +          if (Rect_ContainsPoint(Item_CorrectedTextRect(item), DC->cursorx, DC->cursory)) { +            Item_Action(item); +          } +        } else if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) { +          if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { +            item->cursorPos = 0; +            g_editingField = qtrue; +            g_editItem = item; +            DC->setOverstrikeMode(qtrue); +          } +        } else { +          if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory)) { +            Item_Action(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: +      break; +    case K_KP_ENTER: +    case K_ENTER: +      if (item) { +        if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD) { +          item->cursorPos = 0; +          g_editingField = qtrue; +          g_editItem = item; +          DC->setOverstrikeMode(qtrue); +        } else { +            Item_Action(item); +        } +      } +      break; +  } +  inHandler = qfalse; +} + +void ToWindowCoords(float *x, float *y, windowDef_t *window) { +  if (window->border != 0) { +    *x += window->borderSize; +    *y += window->borderSize; +  } +  *x += window->rect.x; +  *y += window->rect.y; +} + +void Rect_ToWindowCoords(rectDef_t *rect, windowDef_t *window) { +  ToWindowCoords(&rect->x, &rect->y, window); +} + +void Item_SetTextExtents(itemDef_t *item, int *width, int *height, const char *text) { +  const char *textPtr = (text) ? text : item->text; + +  if (textPtr == NULL ) { +    return; +  } + +  *width = item->textRect.w; +  *height = item->textRect.h; + +  // keeps us from computing the widths and heights more than once +  if (*width == 0 || (item->type == ITEM_TYPE_OWNERDRAW && item->textalignment == ITEM_ALIGN_CENTER)) { +    int originalWidth = DC->textWidth(item->text, item->textscale, 0); + +    if (item->type == ITEM_TYPE_OWNERDRAW && (item->textalignment == ITEM_ALIGN_CENTER || item->textalignment == ITEM_ALIGN_RIGHT)) { +      originalWidth += DC->ownerDrawWidth(item->window.ownerDraw, item->textscale); +    } else if (item->type == ITEM_TYPE_EDITFIELD && item->textalignment == ITEM_ALIGN_CENTER && item->cvar) { +      char buff[256]; +      DC->getCVarString(item->cvar, buff, 256); +      originalWidth += DC->textWidth(buff, item->textscale, 0); +    } + +    *width = DC->textWidth(textPtr, item->textscale, 0); +    *height = DC->textHeight(textPtr, item->textscale, 0); +    item->textRect.w = *width; +    item->textRect.h = *height; +    item->textRect.x = item->textalignx; +    item->textRect.y = item->textaligny; +    if (item->textalignment == ITEM_ALIGN_RIGHT) { +      item->textRect.x = item->textalignx - originalWidth; +    } else if (item->textalignment == ITEM_ALIGN_CENTER) { +      item->textRect.x = item->textalignx - originalWidth / 2; +    } + +    ToWindowCoords(&item->textRect.x, &item->textRect.y, &item->window); +  } +} + +void Item_TextColor(itemDef_t *item, vec4_t *newColor) { +  vec4_t lowLight; +  menuDef_t *parent = (menuDef_t*)item->parent; + +  Fade(&item->window.flags, &item->window.foreColor[3], parent->fadeClamp, &item->window.nextTime, parent->fadeCycle, qtrue, parent->fadeAmount); + +  if (item->window.flags & WINDOW_HASFOCUS) { +/*    lowLight[0] = 0.8 * parent->focusColor[0]; +    lowLight[1] = 0.8 * parent->focusColor[1]; +    lowLight[2] = 0.8 * parent->focusColor[2]; +    lowLight[3] = 0.8 * parent->focusColor[3]; +    LerpColor(parent->focusColor,lowLight,*newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR));*/ +    //TA: +    memcpy(newColor, &parent->focusColor, sizeof(vec4_t)); +  } else if (item->textStyle == ITEM_TEXTSTYLE_BLINK && !((DC->realTime/BLINK_DIVISOR) & 1)) { +    lowLight[0] = 0.8 * item->window.foreColor[0]; +    lowLight[1] = 0.8 * item->window.foreColor[1]; +    lowLight[2] = 0.8 * item->window.foreColor[2]; +    lowLight[3] = 0.8 * item->window.foreColor[3]; +    LerpColor(item->window.foreColor,lowLight,*newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); +  } else { +    memcpy(newColor, &item->window.foreColor, sizeof(vec4_t)); +    // items can be enabled and disabled based on cvars +  } + +  if (item->enableCvar != NULL && *item->enableCvar && item->cvarTest != NULL && *item->cvarTest) { +    if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { +      memcpy(newColor, &parent->disableColor, sizeof(vec4_t)); +    } +  } +} + +int Item_Text_AutoWrapped_Lines( itemDef_t *item ) +{ +  char        text[ 1024 ]; +  const char  *p, *textPtr, *newLinePtr; +  char        buff[ 1024 ]; +  int         len, textWidth, newLine; +  int         lines = 0; + +  textWidth = 0; +  newLinePtr = NULL; + +  if( item->text == NULL ) +  { +    if( item->cvar == NULL ) +      return 0; +    else +    { +      DC->getCVarString( item->cvar, text, sizeof( text ) ); +      textPtr = text; +    } +  } +  else +    textPtr = item->text; + +  if( *textPtr == '\0' ) +    return 0; + +  len = 0; +  buff[ 0 ] = '\0'; +  newLine = 0; +  p = textPtr; + +  while( p ) +  { +    textWidth = DC->textWidth( buff, item->textscale, 0 ); + +    if( *p == ' ' || *p == '\t' || *p == '\n' || *p == '\0' ) +    { +      newLine = len; +      newLinePtr = p + 1; +    } + +    //TA: forceably split lines that are too long (where normal splitage has failed) +    if( textWidth > item->window.rect.w && newLine == 0 && *p != '\n' ) +    { +      newLine = len; +      newLinePtr = p; +    } + +    if( ( newLine && textWidth > item->window.rect.w ) || *p == '\n' || *p == '\0' ) +    { +      if( len ) +        buff[ newLine ] = '\0'; + +      if( !( *p == '\n' && !*( p + 1 ) ) ) +        lines++; + +      if( *p == '\0' ) +        break; + +      // +      p = newLinePtr; +      len = 0; +      newLine = 0; + +      continue; +    } + +    buff[ len++ ] = *p++; +    buff[ len ] = '\0'; +  } + +  return lines; +} + +#define MAX_AUTOWRAP_CACHE  16 +#define MAX_AUTOWRAP_LINES  32 +#define MAX_AUTOWRAP_TEXT   512 + +typedef struct +{ +  //this is used purely for checking for cache hits +  char      text[ MAX_AUTOWRAP_TEXT * MAX_AUTOWRAP_LINES ]; +  rectDef_t rect; +  int       textWidth, textHeight; +  char      lines[ MAX_AUTOWRAP_LINES ][ MAX_AUTOWRAP_TEXT ]; +  int       lineOffsets[ MAX_AUTOWRAP_LINES ][ 2 ]; +  int       numLines; +} autoWrapCache_t; + +static int              cacheIndex = 0; +static autoWrapCache_t  awc[ MAX_AUTOWRAP_CACHE ]; + +static int checkCache( const char *text, rectDef_t *rect, int width, int height ) +{ +  int i; + +  for( i = 0; i < MAX_AUTOWRAP_CACHE; i++ ) +  { +    if( Q_stricmp( text, awc[ i ].text ) ) +      continue; + +    if( rect->x != awc[ i ].rect.x || +        rect->y != awc[ i ].rect.y || +        rect->w != awc[ i ].rect.w || +        rect->h != awc[ i ].rect.h ) +      continue; + +    if( awc[ i ].textWidth != width || awc[ i ].textHeight != height ) +      continue; + +    //this is a match +    return i; +  } + +  //no match - autowrap isn't cached +  return -1; +} + +void Item_Text_AutoWrapped_Paint( itemDef_t *item ) +{ +  char        text[ 1024 ]; +  const char  *p, *textPtr, *newLinePtr; +  char        buff[ 1024 ]; +  char        lastCMod[ 2 ] = { 0, 0 }; +  qboolean    forwardColor = qfalse; +  int         width, height, len, textWidth, newLine, newLineWidth; +  int         skipLines, totalLines, lineNum = 0; +  float       y, totalY, diffY; +  vec4_t      color; +  int         cache, i; + +  textWidth = 0; +  newLinePtr = NULL; + +  if( item->text == NULL ) +  { +    if( item->cvar == NULL ) +      return; +    else +    { +      DC->getCVarString( item->cvar, text, sizeof( text ) ); +      textPtr = text; +    } +  } +  else +    textPtr = item->text; + +  if( *textPtr == '\0' ) +    return; + +  Item_TextColor( item, &color ); +  Item_SetTextExtents( item, &width, &height, textPtr ); + +  //check if this block is cached +  cache = checkCache( textPtr, &item->window.rect, width, height ); +  if( cache >= 0 ) +  { +    lineNum = awc[ cache ].numLines; + +    for( i = 0; i < lineNum; i++ ) +    { +      item->textRect.x = awc[ cache ].lineOffsets[ i ][ 0 ]; +      item->textRect.y = awc[ cache ].lineOffsets[ i ][ 1 ]; + +      DC->drawText( item->textRect.x, item->textRect.y, item->textscale, color, +                    awc[ cache ].lines[ i ], 0, 0, item->textStyle ); +    } +  } +  else +  { +    y = item->textaligny; +    len = 0; +    buff[ 0 ] = '\0'; +    newLine = 0; +    newLineWidth = 0; +    p = textPtr; + +    totalLines = Item_Text_AutoWrapped_Lines( item ); + +    totalY = totalLines * ( height + 5 ); +    diffY = totalY - item->window.rect.h; + +    if( diffY > 0.0f ) +      skipLines = (int)( diffY / ( (float)height + 5.0f ) ); +    else +      skipLines = 0; + +    //set up a cache entry +    strcpy( awc[ cacheIndex ].text, textPtr ); +    awc[ cacheIndex ].rect.x = item->window.rect.x; +    awc[ cacheIndex ].rect.y = item->window.rect.y; +    awc[ cacheIndex ].rect.w = item->window.rect.w; +    awc[ cacheIndex ].rect.h = item->window.rect.h; +    awc[ cacheIndex ].textWidth = width; +    awc[ cacheIndex ].textHeight = height; + +    while( p ) +    { +      textWidth = DC->textWidth( buff, item->textscale, 0 ); + +      if( *p == '^' ) +      { +        lastCMod[ 0 ] = p[ 0 ]; +        lastCMod[ 1 ] = p[ 1 ]; +      } + +      if( *p == ' ' || *p == '\t' || *p == '\n' || *p == '\0' ) +      { +        newLine = len; +        newLinePtr = p+1; +        newLineWidth = textWidth; + +        if( *p == '\n' ) //don't forward colours past deilberate \n's +          lastCMod[ 0 ] = lastCMod[ 1 ] = 0; +        else +          forwardColor = qtrue; +      } + +      //TA: forceably split lines that are too long (where normal splitage has failed) +      if( textWidth > item->window.rect.w && newLine == 0 && *p != '\n' ) +      { +        newLine = len; +        newLinePtr = p; +        newLineWidth = textWidth; + +        forwardColor = qtrue; +      } + +      if( ( newLine && textWidth > item->window.rect.w ) || *p == '\n' || *p == '\0' ) +      { +        if( len ) +        { +          if( item->textalignment == ITEM_ALIGN_LEFT ) +            item->textRect.x = item->textalignx; +          else if( item->textalignment == ITEM_ALIGN_RIGHT ) +            item->textRect.x = item->textalignx - newLineWidth; +          else if( item->textalignment == ITEM_ALIGN_CENTER ) +            item->textRect.x = item->textalignx - newLineWidth / 2; + +          item->textRect.y = y; +          ToWindowCoords( &item->textRect.x, &item->textRect.y, &item->window ); +          // +          buff[ newLine ] = '\0'; + +          if( !skipLines ) +          { +            DC->drawText( item->textRect.x, item->textRect.y, item->textscale, color, buff, 0, 0, item->textStyle ); + +            strcpy( awc[ cacheIndex ].lines[ lineNum ], buff ); +            awc[ cacheIndex ].lineOffsets[ lineNum ][ 0 ] = item->textRect.x; +            awc[ cacheIndex ].lineOffsets[ lineNum ][ 1 ] = item->textRect.y; + +            lineNum++; +          } +        } +        if( *p == '\0' ) +          break; + +        // +        if( !skipLines ) +          y += height + 5; + +        if( skipLines ) +          skipLines--; + +        p = newLinePtr; +        len = 0; +        newLine = 0; +        newLineWidth = 0; + +        if( forwardColor && lastCMod[ 0 ] != 0 ) +        { +          buff[ len++ ] = lastCMod[ 0 ]; +          buff[ len++ ] = lastCMod[ 1 ]; +          buff[ len ] = '\0'; + +          forwardColor = qfalse; +        } + +        continue; +      } + +      buff[ len++ ] = *p++; +      buff[ len ] = '\0'; +    } + +    //mark the end of the lines list +    awc[ cacheIndex ].numLines = lineNum; + +    //increment cacheIndex +    cacheIndex = ( cacheIndex + 1 ) % MAX_AUTOWRAP_CACHE; +  } +} + +void Item_Text_Wrapped_Paint(itemDef_t *item) { +  char text[1024]; +  const char *p, *start, *textPtr; +  char buff[1024]; +  int width, height; +  float x, y; +  vec4_t color; + +  // now paint the text and/or any optional images +  // default to left + +  if (item->text == NULL) { +    if (item->cvar == NULL) { +      return; +    } +    else { +      DC->getCVarString(item->cvar, text, sizeof(text)); +      textPtr = text; +    } +  } +  else { +    textPtr = item->text; +  } +  if (*textPtr == '\0') { +    return; +  } + +  Item_TextColor(item, &color); +  Item_SetTextExtents(item, &width, &height, textPtr); + +  x = item->textRect.x; +  y = item->textRect.y; +  start = textPtr; +  p = strchr(textPtr, '\r'); +  while (p && *p) { +    strncpy(buff, start, p-start+1); +    buff[p-start] = '\0'; +    DC->drawText(x, y, item->textscale, color, buff, 0, 0, item->textStyle); +    y += height + 5; +    start += p - start + 1; +    p = strchr(p+1, '\r'); +  } +  DC->drawText(x, y, item->textscale, color, start, 0, 0, item->textStyle); +} + +void Item_Text_Paint(itemDef_t *item) { +  char text[1024]; +  const char *textPtr; +  int height, width; +  vec4_t color; + +  if (item->window.flags & WINDOW_WRAPPED) { +    Item_Text_Wrapped_Paint(item); +    return; +  } +  if (item->window.flags & WINDOW_AUTOWRAPPED) { +    Item_Text_AutoWrapped_Paint(item); +    return; +  } + +  if (item->text == NULL) { +    if (item->cvar == NULL) { +      return; +    } +    else { +      DC->getCVarString(item->cvar, text, sizeof(text)); +      textPtr = text; +    } +  } +  else { +    textPtr = item->text; +  } + +  // this needs to go here as it sets extents for cvar types as well +  Item_SetTextExtents(item, &width, &height, textPtr); + +  if (*textPtr == '\0') { +    return; +  } + + +  Item_TextColor(item, &color); + +  //FIXME: this is a fucking mess +/* +  adjust = 0; +  if (item->textStyle == ITEM_TEXTSTYLE_OUTLINED || item->textStyle == ITEM_TEXTSTYLE_OUTLINESHADOWED) { +    adjust = 0.5; +  } + +  if (item->textStyle == ITEM_TEXTSTYLE_SHADOWED || item->textStyle == ITEM_TEXTSTYLE_OUTLINESHADOWED) { +    Fade(&item->window.flags, &DC->Assets.shadowColor[3], DC->Assets.fadeClamp, &item->window.nextTime, DC->Assets.fadeCycle, qfalse); +    DC->drawText(item->textRect.x + DC->Assets.shadowX, item->textRect.y + DC->Assets.shadowY, item->textscale, DC->Assets.shadowColor, textPtr, adjust); +  } +*/ + + +//  if (item->textStyle == ITEM_TEXTSTYLE_OUTLINED || item->textStyle == ITEM_TEXTSTYLE_OUTLINESHADOWED) { +//    Fade(&item->window.flags, &item->window.outlineColor[3], DC->Assets.fadeClamp, &item->window.nextTime, DC->Assets.fadeCycle, qfalse); +//    /* +//    Text_Paint(item->textRect.x-1, item->textRect.y-1, item->textscale, item->window.foreColor, textPtr, adjust); +//    Text_Paint(item->textRect.x, item->textRect.y-1, item->textscale, item->window.foreColor, textPtr, adjust); +//    Text_Paint(item->textRect.x+1, item->textRect.y-1, item->textscale, item->window.foreColor, textPtr, adjust); +//    Text_Paint(item->textRect.x-1, item->textRect.y, item->textscale, item->window.foreColor, textPtr, adjust); +//    Text_Paint(item->textRect.x+1, item->textRect.y, item->textscale, item->window.foreColor, textPtr, adjust); +//    Text_Paint(item->textRect.x-1, item->textRect.y+1, item->textscale, item->window.foreColor, textPtr, adjust); +//    Text_Paint(item->textRect.x, item->textRect.y+1, item->textscale, item->window.foreColor, textPtr, adjust); +//    Text_Paint(item->textRect.x+1, item->textRect.y+1, item->textscale, item->window.foreColor, textPtr, adjust); +//    */ +//    DC->drawText(item->textRect.x - 1, item->textRect.y + 1, item->textscale * 1.02, item->window.outlineColor, textPtr, adjust); +//  } + +  DC->drawText(item->textRect.x, item->textRect.y, item->textscale, color, textPtr, 0, 0, item->textStyle); +} + + + +//float     trap_Cvar_VariableValue( const char *var_name ); +//void      trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); + +void Item_TextField_Paint(itemDef_t *item) { +  char buff[1024]; +  vec4_t newColor; +  int offset; +  menuDef_t *parent = (menuDef_t*)item->parent; +  editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; + +  Item_Text_Paint(item); + +  buff[0] = '\0'; + +  if (item->cvar) { +    DC->getCVarString(item->cvar, buff, sizeof(buff)); +  } + +  parent = (menuDef_t*)item->parent; + +  if (item->window.flags & WINDOW_HASFOCUS) { +/*    lowLight[0] = 0.8 * parent->focusColor[0]; +    lowLight[1] = 0.8 * parent->focusColor[1]; +    lowLight[2] = 0.8 * parent->focusColor[2]; +    lowLight[3] = 0.8 * parent->focusColor[3]; +    LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR));*/ +    //TA: +    memcpy(newColor, &parent->focusColor, sizeof(vec4_t)); +  } else { +    memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); +  } + +  offset = (item->text && *item->text) ? 8 : 0; +  if (item->window.flags & WINDOW_HASFOCUS && g_editingField) { +    char cursor = DC->getOverstrikeMode() ? '_' : '|'; +    DC->drawTextWithCursor(item->textRect.x + item->textRect.w + offset, item->textRect.y, item->textscale, newColor, buff + editPtr->paintOffset, item->cursorPos - editPtr->paintOffset , cursor, editPtr->maxPaintChars, item->textStyle); +  } else { +    DC->drawText(item->textRect.x + item->textRect.w + offset, item->textRect.y, item->textscale, newColor, buff + editPtr->paintOffset, 0, editPtr->maxPaintChars, item->textStyle); +  } + +} + +void Item_YesNo_Paint(itemDef_t *item) { +  vec4_t newColor; +  float value; +  menuDef_t *parent = (menuDef_t*)item->parent; + +  value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; + +  if (item->window.flags & WINDOW_HASFOCUS) { +/*    lowLight[0] = 0.8 * parent->focusColor[0]; +    lowLight[1] = 0.8 * parent->focusColor[1]; +    lowLight[2] = 0.8 * parent->focusColor[2]; +    lowLight[3] = 0.8 * parent->focusColor[3]; +    LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR));*/ +    //TA: +    memcpy(newColor, &parent->focusColor, sizeof(vec4_t)); +  } else { +    memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); +  } + +  if (item->text) { +    Item_Text_Paint(item); +    DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, (value != 0) ? "Yes" : "No", 0, 0, item->textStyle); +  } else { +    DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, (value != 0) ? "Yes" : "No", 0, 0, item->textStyle); +  } +} + +void Item_Multi_Paint(itemDef_t *item) { +  vec4_t newColor; +  const char *text = ""; +  menuDef_t *parent = (menuDef_t*)item->parent; + +  if (item->window.flags & WINDOW_HASFOCUS) { +/*    lowLight[0] = 0.8 * parent->focusColor[0]; +    lowLight[1] = 0.8 * parent->focusColor[1]; +    lowLight[2] = 0.8 * parent->focusColor[2]; +    lowLight[3] = 0.8 * parent->focusColor[3]; +    LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR));*/ +    //TA: +    memcpy(newColor, &parent->focusColor, sizeof(vec4_t)); +  } else { +    memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); +  } + +  text = Item_Multi_Setting(item); + +  if (item->text) { +    Item_Text_Paint(item); +    DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle); +  } else { +    DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, text, 0, 0, item->textStyle); +  } +} + + +typedef struct { +  char  *command; +  int   id; +  int   defaultbind1; +  int   defaultbind2; +  int   bind1; +  int   bind2; +} bind_t; + +typedef struct +{ +  char* name; +  float defaultvalue; +  float value; +} configcvar_t; + + +static bind_t g_bindings[] = +{ +  { "+scores",      K_TAB,         -1, -1, -1 }, +  { "+button2",     K_ENTER,       -1, -1, -1 }, +  { "+speed",       K_SHIFT,       -1, -1, -1 }, +  { "boost",        'x',           -1, -1, -1 }, //TA: human sprinting +  { "+forward",     K_UPARROW,     -1, -1, -1 }, +  { "+back",        K_DOWNARROW,   -1, -1, -1 }, +  { "+moveleft",    ',',           -1, -1, -1 }, +  { "+moveright",   '.',           -1, -1, -1 }, +  { "+moveup",      K_SPACE,       -1, -1, -1 }, +  { "+movedown",    'c',           -1, -1, -1 }, +  { "+left",        K_LEFTARROW,   -1, -1, -1 }, +  { "+right",       K_RIGHTARROW,  -1, -1, -1 }, +  { "+strafe",      K_ALT,         -1, -1, -1 }, +  { "+lookup",      K_PGDN,        -1, -1, -1 }, +  { "+lookdown",    K_DEL,         -1, -1, -1 }, +  { "+mlook",       '/',           -1, -1, -1 }, +  { "centerview",   K_END,         -1, -1, -1 }, +  { "+zoom",        -1,            -1, -1, -1 }, +  { "weapon 1",     '1',           -1, -1, -1 }, +  { "weapon 2",     '2',           -1, -1, -1 }, +  { "weapon 3",     '3',           -1, -1, -1 }, +  { "weapon 4",     '4',           -1, -1, -1 }, +  { "weapon 5",     '5',           -1, -1, -1 }, +  { "weapon 6",     '6',           -1, -1, -1 }, +  { "weapon 7",     '7',           -1, -1, -1 }, +  { "weapon 8",     '8',           -1, -1, -1 }, +  { "weapon 9",     '9',           -1, -1, -1 }, +  { "weapon 10",    '0',           -1, -1, -1 }, +  { "weapon 11",    -1,            -1, -1, -1 }, +  { "weapon 12",    -1,            -1, -1, -1 }, +  { "weapon 13",    -1,            -1, -1, -1 }, +  { "+attack",      K_MOUSE1,      -1, -1, -1 }, +  { "+button5",     K_MOUSE2,      -1, -1, -1 }, //TA: secondary attack +  { "reload",       'r',           -1, -1, -1 }, //TA: reload +  { "buy ammo",     'b',           -1, -1, -1 }, //TA: buy ammo +  { "itemact medkit", 'm',         -1, -1, -1 }, //TA: use medkit +  { "+button7",     'q',           -1, -1, -1 }, //TA: buildable use +  { "deconstruct",  'e',           -1, -1, -1 }, //TA: buildable destroy +  { "weapprev",     '[',           -1, -1, -1 }, +  { "weapnext",     ']',           -1, -1, -1 }, +  { "+button3",     K_MOUSE3,      -1, -1, -1 }, +  { "+button4",     K_MOUSE4,      -1, -1, -1 }, +  { "vote yes",     K_F1,          -1, -1, -1 }, +  { "vote no",      K_F2,          -1, -1, -1 }, +  { "teamvote yes", K_F3,          -1, -1, -1 }, +  { "teamvote no",  K_F4,          -1, -1, -1 }, +  { "scoresUp",      K_KP_PGUP,    -1, -1, -1 }, +  { "scoresDown",    K_KP_PGDN,    -1, -1, -1 }, +  // bk001205 - this one below was:  '-1' +  { "messagemode",  -1,            -1, -1, -1 }, +  { "messagemode2", -1,            -1, -1, -1 }, +  { "messagemode3", -1,            -1, -1, -1 }, +  { "messagemode4", -1,            -1, -1, -1 } +}; + + +static const int g_bindCount = sizeof(g_bindings) / sizeof(bind_t); + +/* +================= +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++ ) +  { +    DC->getBindingBuf( j, b, 256 ); +    if ( *b == 0 ) { +      continue; +    } +    if ( !Q_stricmp( b, command ) ) { +      twokeys[count] = j; +      count++; +      if (count == 2) { +        break; +      } +    } +  } +} + +/* +================= +Controls_GetConfig +================= +*/ +void Controls_GetConfig( void ) +{ +  int   i; +  int   twokeys[ 2 ]; + +  // iterate each command, get its numeric binding +  for( i = 0; i < g_bindCount; i++ ) +  { +    Controls_GetKeyAssignment( g_bindings[ i ].command, twokeys ); + +    g_bindings[ i ].bind1 = twokeys[ 0 ]; +    g_bindings[ i ].bind2 = twokeys[ 1 ]; +  } + +  //s_controls.invertmouse.curvalue  = DC->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.05, 0.75, Controls_GetCvarValue( "joy_threshold" ) ); +  //s_controls.freelook.curvalue     = UI_ClampCvar( 0, 1, Controls_GetCvarValue( "cl_freelook" ) ); +} + +/* +================= +Controls_SetConfig +================= +*/ +void Controls_SetConfig(qboolean restart) +{ +  int   i; + +  // iterate each command, get its numeric binding +  for (i=0; i < g_bindCount; i++) +  { + +    if (g_bindings[i].bind1 != -1) +    { +      DC->setBinding( g_bindings[i].bind1, g_bindings[i].command ); + +      if (g_bindings[i].bind2 != -1) +        DC->setBinding( g_bindings[i].bind2, g_bindings[i].command ); +    } +  } + +  //if ( s_controls.invertmouse.curvalue ) +  //  DC->setCVar("m_pitch", va("%f),-fabs( DC->getCVarValue( "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 ); +  DC->executeText(EXEC_APPEND, "in_restart\n"); +  //trap_Cmd_ExecuteText( EXEC_APPEND, "in_restart\n" ); +} + +/* +================= +Controls_SetDefaults +================= +*/ +void Controls_SetDefaults( void ) +{ +  int i; + +  // iterate each command, set its default binding +  for (i=0; i < g_bindCount; i++) +  { +    g_bindings[i].bind1 = g_bindings[i].defaultbind1; +    g_bindings[i].bind2 = g_bindings[i].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" ); +} + +int BindingIDFromName(const char *name) { +  int i; +  for (i=0; i < g_bindCount; i++) +  { +    if (Q_stricmp(name, g_bindings[i].command) == 0) { +      return i; +    } +  } +  return -1; +} + +char g_nameBind1[32]; +char g_nameBind2[32]; + +void BindingFromName(const char *cvar) { +  int i, b1, b2; + +  // iterate each command, set its default binding +  for (i=0; i < g_bindCount; i++) +  { +    if (Q_stricmp(cvar, g_bindings[i].command) == 0) { +      b1 = g_bindings[i].bind1; +      if (b1 == -1) { +        break; +      } +        DC->keynumToStringBuf( b1, g_nameBind1, 32 ); +        Q_strupr(g_nameBind1); + +        b2 = g_bindings[i].bind2; +        if (b2 != -1) +        { +          DC->keynumToStringBuf( b2, g_nameBind2, 32 ); +          Q_strupr(g_nameBind2); +          strcat( g_nameBind1, " or " ); +          strcat( g_nameBind1, g_nameBind2 ); +        } +      return; +    } +  } +  strcpy(g_nameBind1, "???"); +} + +void Item_Slider_Paint(itemDef_t *item) { +  vec4_t newColor; +  float x, y, value; +  menuDef_t *parent = (menuDef_t*)item->parent; + +  value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; + +  if (item->window.flags & WINDOW_HASFOCUS) { +/*    lowLight[0] = 0.8 * parent->focusColor[0]; +    lowLight[1] = 0.8 * parent->focusColor[1]; +    lowLight[2] = 0.8 * parent->focusColor[2]; +    lowLight[3] = 0.8 * parent->focusColor[3]; +    LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR));*/ +    //TA: +    memcpy(newColor, &parent->focusColor, sizeof(vec4_t)); +  } else { +    memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); +  } + +  y = item->window.rect.y; +  if (item->text) { +    Item_Text_Paint(item); +    x = item->textRect.x + item->textRect.w + 8; +  } else { +    x = item->window.rect.x; +  } +  DC->setColor(newColor); +  DC->drawHandlePic( x, y, SLIDER_WIDTH, SLIDER_HEIGHT, DC->Assets.sliderBar ); + +  x = Item_Slider_ThumbPosition(item); +  DC->drawHandlePic( x - (SLIDER_THUMB_WIDTH / 2), y - 2, SLIDER_THUMB_WIDTH, SLIDER_THUMB_HEIGHT, DC->Assets.sliderThumb ); + +} + +void Item_Bind_Paint(itemDef_t *item) { +  vec4_t newColor, lowLight; +  float value; +  int maxChars = 0; +  menuDef_t *parent = (menuDef_t*)item->parent; +  editFieldDef_t *editPtr = (editFieldDef_t*)item->typeData; +  if (editPtr) { +    maxChars = editPtr->maxPaintChars; +  } + +  value = (item->cvar) ? DC->getCVarValue(item->cvar) : 0; + +  if (item->window.flags & WINDOW_HASFOCUS) { +    if (g_bindItem == item) { +      lowLight[0] = 0.8f * 1.0f; +      lowLight[1] = 0.8f * 0.0f; +      lowLight[2] = 0.8f * 0.0f; +      lowLight[3] = 0.8f * 1.0f; +    } else { +      lowLight[0] = 0.8f * parent->focusColor[0]; +      lowLight[1] = 0.8f * parent->focusColor[1]; +      lowLight[2] = 0.8f * parent->focusColor[2]; +      lowLight[3] = 0.8f * parent->focusColor[3]; +    } +    /*LerpColor(parent->focusColor,lowLight,newColor,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR));*/ +    //TA: +    memcpy(newColor, &parent->focusColor, sizeof(vec4_t)); +  } else { +    memcpy(&newColor, &item->window.foreColor, sizeof(vec4_t)); +  } + +  if (item->text) { +    Item_Text_Paint(item); +    BindingFromName(item->cvar); +    DC->drawText(item->textRect.x + item->textRect.w + 8, item->textRect.y, item->textscale, newColor, g_nameBind1, 0, maxChars, item->textStyle); +  } else { +    DC->drawText(item->textRect.x, item->textRect.y, item->textscale, newColor, (value != 0) ? "FIXME" : "FIXME", 0, maxChars, item->textStyle); +  } +} + +qboolean Display_KeyBindPending( void ) { +  return g_waitingForKey; +} + +qboolean Item_Bind_HandleKey(itemDef_t *item, int key, qboolean down) { +  int     id; +  int     i; + +  if (Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) && !g_waitingForKey) +  { +    if (down && (key == K_MOUSE1 || key == K_ENTER)) { +      g_waitingForKey = qtrue; +      g_bindItem = item; +    } +    return qtrue; +  } +  else +  { +    if (!g_waitingForKey || g_bindItem == NULL) { +      return qtrue; +    } + +    if (key & K_CHAR_FLAG) { +      return qtrue; +    } + +    switch (key) +    { +      case K_ESCAPE: +        g_waitingForKey = qfalse; +        return qtrue; + +      case K_BACKSPACE: +        id = BindingIDFromName(item->cvar); +        if (id != -1) { +          g_bindings[id].bind1 = -1; +          g_bindings[id].bind2 = -1; +        } +        Controls_SetConfig(qtrue); +        g_waitingForKey = qfalse; +        g_bindItem = NULL; +        return qtrue; + +      case '`': +        return qtrue; +    } +  } + +  if (key != -1) +  { + +    for (i=0; i < g_bindCount; i++) +    { + +      if (g_bindings[i].bind2 == key) { +        g_bindings[i].bind2 = -1; +      } + +      if (g_bindings[i].bind1 == key) +      { +        g_bindings[i].bind1 = g_bindings[i].bind2; +        g_bindings[i].bind2 = -1; +      } +    } +  } + + +  id = BindingIDFromName(item->cvar); + +  if (id != -1) { +    if (key == -1) { +      if( g_bindings[id].bind1 != -1 ) { +        DC->setBinding( g_bindings[id].bind1, "" ); +        g_bindings[id].bind1 = -1; +      } +      if( g_bindings[id].bind2 != -1 ) { +        DC->setBinding( g_bindings[id].bind2, "" ); +        g_bindings[id].bind2 = -1; +      } +    } +    else if (g_bindings[id].bind1 == -1) { +      g_bindings[id].bind1 = key; +    } +    else if (g_bindings[id].bind1 != key && g_bindings[id].bind2 == -1) { +      g_bindings[id].bind2 = key; +    } +    else { +      DC->setBinding( g_bindings[id].bind1, "" ); +      DC->setBinding( g_bindings[id].bind2, "" ); +      g_bindings[id].bind1 = key; +      g_bindings[id].bind2 = -1; +    } +  } + +  Controls_SetConfig(qtrue); +  g_waitingForKey = qfalse; + +  return qtrue; +} + + + +void AdjustFrom640(float *x, float *y, float *w, float *h) { +  //*x = *x * DC->scale + DC->bias; +  *x *= DC->xscale; +  *y *= DC->yscale; +  *w *= DC->xscale; +  *h *= DC->yscale; +} + +void Item_Model_Paint(itemDef_t *item) { +  float x, y, w, h; +  refdef_t refdef; +  refEntity_t   ent; +  vec3_t      mins, maxs, origin; +  vec3_t      angles; +  modelDef_t *modelPtr = (modelDef_t*)item->typeData; + +  if (modelPtr == NULL) { +    return; +  } + +  // setup the refdef +  memset( &refdef, 0, sizeof( refdef ) ); +  refdef.rdflags = RDF_NOWORLDMODEL; +  AxisClear( refdef.viewaxis ); +  x = item->window.rect.x+1; +  y = item->window.rect.y+1; +  w = item->window.rect.w-2; +  h = item->window.rect.h-2; + +  AdjustFrom640( &x, &y, &w, &h ); + +  refdef.x = x; +  refdef.y = y; +  refdef.width = w; +  refdef.height = h; + +  DC->modelBounds( item->asset, mins, maxs ); + +  origin[2] = -0.5 * ( mins[2] + maxs[2] ); +  origin[1] = 0.5 * ( mins[1] + maxs[1] ); + +  // calculate distance so the model nearly fills the box +  if (qtrue) { +    float len = 0.5 * ( maxs[2] - mins[2] ); +    origin[0] = len / 0.268;  // len / tan( fov/2 ) +    //origin[0] = len / tan(w/2); +  } else { +    origin[0] = item->textscale; +  } +  refdef.fov_x = (modelPtr->fov_x) ? modelPtr->fov_x : w; +  refdef.fov_y = (modelPtr->fov_y) ? modelPtr->fov_y : 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 ); + +  DC->clearScene(); + +  refdef.time = DC->realTime; + +  // add the model + +  memset( &ent, 0, sizeof(ent) ); + +  //adjust = 5.0 * sin( (float)uis.realtime / 500 ); +  //adjust = 360 % (int)((float)uis.realtime / 1000); +  //VectorSet( angles, 0, 0, 1 ); + +  // use item storage to track +  if (modelPtr->rotationSpeed) { +    if (DC->realTime > item->window.nextTime) { +      item->window.nextTime = DC->realTime + modelPtr->rotationSpeed; +      modelPtr->angle = (int)(modelPtr->angle + 1) % 360; +    } +  } +  VectorSet( angles, 0, modelPtr->angle, 0 ); +  AnglesToAxis( angles, ent.axis ); + +  ent.hModel = item->asset; +  VectorCopy( origin, ent.origin ); +  VectorCopy( origin, ent.lightingOrigin ); +  ent.renderfx = RF_LIGHTING_ORIGIN | RF_NOSHADOW; +  VectorCopy( ent.origin, ent.oldorigin ); + +  DC->addRefEntityToScene( &ent ); +  DC->renderScene( &refdef ); + +} + + +void Item_Image_Paint(itemDef_t *item) { +  if (item == NULL) { +    return; +  } +  DC->drawHandlePic(item->window.rect.x+1, item->window.rect.y+1, item->window.rect.w-2, item->window.rect.h-2, item->asset); +} + +void Item_ListBox_Paint(itemDef_t *item) { +  float x, y, size, thumb; +  int i, count; +  qhandle_t image; +  qhandle_t optionalImage; +  listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; + +  // the listbox is horizontal or vertical and has a fixed size scroll bar going either direction +  // elements are enumerated from the DC and either text or image handles are acquired from the DC as well +  // textscale is used to size the text, textalignx and textaligny are used to size image elements +  // there is no clipping available so only the last completely visible item is painted +  count = DC->feederCount(item->special); +  // default is vertical if horizontal flag is not here +  if (item->window.flags & WINDOW_HORIZONTAL) { +    // draw scrollbar in bottom of the window +    // bar +    x = item->window.rect.x + 1; +    y = item->window.rect.y + item->window.rect.h - SCROLLBAR_SIZE - 1; +    DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowLeft); +    x += SCROLLBAR_SIZE - 1; +    size = item->window.rect.w - (SCROLLBAR_SIZE * 2); +    DC->drawHandlePic(x, y, size+1, SCROLLBAR_SIZE, DC->Assets.scrollBar); +    x += size - 1; +    DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowRight); +    // thumb +    thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); +    if (thumb > x - SCROLLBAR_SIZE - 1) { +      thumb = x - SCROLLBAR_SIZE - 1; +    } +    DC->drawHandlePic(thumb, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); +    // +    listPtr->endPos = listPtr->startPos; +    size = item->window.rect.w - 2; +    // items +    // size contains max available space +    if (listPtr->elementStyle == LISTBOX_IMAGE) { +      // fit = 0; +      x = item->window.rect.x + 1; +      y = item->window.rect.y + 1; +      for (i = listPtr->startPos; i < count; i++) { +        // always draw at least one +        // which may overdraw the box if it is too small for the element +        image = DC->feederItemImage(item->special, i); +        if (image) { +          DC->drawHandlePic(x+1, y+1, listPtr->elementWidth - 2, listPtr->elementHeight - 2, image); +        } + +        if (i == item->cursorPos) { +          DC->drawRect(x, y, listPtr->elementWidth-1, listPtr->elementHeight-1, item->window.borderSize, item->window.borderColor); +        } + +        listPtr->endPos++; +        size -= listPtr->elementWidth; +        if (size < listPtr->elementWidth) { +          listPtr->drawPadding = size; //listPtr->elementWidth - size; +          break; +        } +        x += listPtr->elementWidth; +        // fit++; +      } +    } else { +      // +    } +  } else { +    // draw scrollbar to right side of the window +    x = item->window.rect.x + item->window.rect.w - SCROLLBAR_SIZE - 1; +    y = item->window.rect.y + 1; +    DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowUp); +    y += SCROLLBAR_SIZE - 1; + +    listPtr->endPos = listPtr->startPos; +    size = item->window.rect.h - (SCROLLBAR_SIZE * 2); +    DC->drawHandlePic(x, y, SCROLLBAR_SIZE, size+1, DC->Assets.scrollBar); +    y += size - 1; +    DC->drawHandlePic(x, y, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarArrowDown); +    // thumb +    thumb = Item_ListBox_ThumbDrawPosition(item);//Item_ListBox_ThumbPosition(item); +    if (thumb > y - SCROLLBAR_SIZE - 1) { +      thumb = y - SCROLLBAR_SIZE - 1; +    } +    DC->drawHandlePic(x, thumb, SCROLLBAR_SIZE, SCROLLBAR_SIZE, DC->Assets.scrollBarThumb); + +    // adjust size for item painting +    size = item->window.rect.h - 2; +    if (listPtr->elementStyle == LISTBOX_IMAGE) { +      // fit = 0; +      x = item->window.rect.x + 1; +      y = item->window.rect.y + 1; +      for (i = listPtr->startPos; i < count; i++) { +        // always draw at least one +        // which may overdraw the box if it is too small for the element +        image = DC->feederItemImage(item->special, i); +        if (image) { +          DC->drawHandlePic(x+1, y+1, listPtr->elementWidth - 2, listPtr->elementHeight - 2, image); +        } + +        if (i == item->cursorPos) { +          DC->drawRect(x, y, listPtr->elementWidth - 1, listPtr->elementHeight - 1, item->window.borderSize, item->window.borderColor); +        } + +        listPtr->endPos++; +        size -= listPtr->elementWidth; +        if (size < listPtr->elementHeight) { +          listPtr->drawPadding = listPtr->elementHeight - size; +          break; +        } +        y += listPtr->elementHeight; +        // fit++; +      } +    } else { +      x = item->window.rect.x + 1; +      y = item->window.rect.y + 1; +      for (i = listPtr->startPos; i < count; i++) { +        const char *text; +        // always draw at least one +        // which may overdraw the box if it is too small for the element + +        if (listPtr->numColumns > 0) { +          int j; +          for (j = 0; j < listPtr->numColumns; j++) { +            text = DC->feederItemText(item->special, i, j, &optionalImage); +            if (optionalImage >= 0) { +              DC->drawHandlePic(x + 4 + listPtr->columnInfo[j].pos, y - 1 + listPtr->elementHeight / 2, listPtr->columnInfo[j].width, listPtr->columnInfo[j].width, optionalImage); +            } else if (text) { +              //TA: +              int alignOffset = 0.0f, tw; + +              tw = DC->textWidth( text, item->textscale, 0 ); + +              switch( listPtr->columnInfo[ j ].align ) +              { +                case ITEM_ALIGN_LEFT: +                  alignOffset = 0.0f; +                  break; + +                case ITEM_ALIGN_RIGHT: +                  alignOffset = listPtr->columnInfo[ j ].width - tw; +                  break; + +                case ITEM_ALIGN_CENTER: +                  alignOffset = ( listPtr->columnInfo[ j ].width / 2.0f ) - ( tw / 2.0f ); +                  break; + +                default: +                  alignOffset = 0.0f; +              } + +              DC->drawText( x + 4 + listPtr->columnInfo[j].pos + alignOffset, y + listPtr->elementHeight, +                            item->textscale, item->window.foreColor, text, 0, +                            listPtr->columnInfo[j].maxChars, item->textStyle ); +            } +          } +        } else { +          text = DC->feederItemText(item->special, i, 0, &optionalImage); +          if (optionalImage >= 0) { +            //DC->drawHandlePic(x + 4 + listPtr->elementHeight, y, listPtr->columnInfo[j].width, listPtr->columnInfo[j].width, optionalImage); +          } else if (text) { +            DC->drawText(x + 4, y + listPtr->elementHeight, item->textscale, item->window.foreColor, text, 0, 0, item->textStyle); +          } +        } + +        if (i == item->cursorPos) { +          DC->fillRect(x + 2, y + 2, item->window.rect.w - SCROLLBAR_SIZE - 4, listPtr->elementHeight, item->window.outlineColor); +        } + +        listPtr->endPos++; +        size -= listPtr->elementHeight; +        if (size < listPtr->elementHeight) { +          listPtr->drawPadding = listPtr->elementHeight - size; +          break; +        } +        y += listPtr->elementHeight; +        // fit++; +      } +    } +  } + +  //TA: FIXME: hacky fix to off-by-one bug +  listPtr->endPos--; +} + + +void Item_OwnerDraw_Paint(itemDef_t *item) { +  menuDef_t *parent; + +  if (item == NULL) { +    return; +  } +  parent = (menuDef_t*)item->parent; + +  if (DC->ownerDrawItem) { +    vec4_t color, lowLight; +    menuDef_t *parent = (menuDef_t*)item->parent; +    Fade(&item->window.flags, &item->window.foreColor[3], parent->fadeClamp, &item->window.nextTime, parent->fadeCycle, qtrue, parent->fadeAmount); +    memcpy(&color, &item->window.foreColor, sizeof(color)); +    if (item->numColors > 0 && DC->getValue) { +      // if the value is within one of the ranges then set color to that, otherwise leave at default +      int i; +      float f = DC->getValue(item->window.ownerDraw); +      for (i = 0; i < item->numColors; i++) { +        if (f >= item->colorRanges[i].low && f <= item->colorRanges[i].high) { +          memcpy(&color, &item->colorRanges[i].color, sizeof(color)); +          break; +        } +      } +    } + +    if (item->window.flags & WINDOW_HASFOCUS) { +/*      lowLight[0] = 0.8 * parent->focusColor[0]; +      lowLight[1] = 0.8 * parent->focusColor[1]; +      lowLight[2] = 0.8 * parent->focusColor[2]; +      lowLight[3] = 0.8 * parent->focusColor[3]; +      LerpColor(parent->focusColor,lowLight,color,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR));*/ +      //TA: +      memcpy(color, &parent->focusColor, sizeof(vec4_t)); +    } else if (item->textStyle == ITEM_TEXTSTYLE_BLINK && !((DC->realTime/BLINK_DIVISOR) & 1)) { +      lowLight[0] = 0.8 * item->window.foreColor[0]; +      lowLight[1] = 0.8 * item->window.foreColor[1]; +      lowLight[2] = 0.8 * item->window.foreColor[2]; +      lowLight[3] = 0.8 * item->window.foreColor[3]; +      LerpColor(item->window.foreColor,lowLight,color,0.5+0.5*sin(DC->realTime / PULSE_DIVISOR)); +    } + +    if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { +      memcpy(color, parent->disableColor, sizeof(vec4_t)); // bk001207 - FIXME: Com_Memcpy +    } + +    if (item->text) { +      Item_Text_Paint(item); +        if (item->text[0]) { +          // +8 is an offset kludge to properly align owner draw items that have text combined with them +          DC->ownerDrawItem(item->textRect.x + item->textRect.w + 8, item->window.rect.y, item->window.rect.w, item->window.rect.h, 0, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); +        } else { +          DC->ownerDrawItem(item->textRect.x + item->textRect.w, item->window.rect.y, item->window.rect.w, item->window.rect.h, 0, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); +        } +      } else { +      DC->ownerDrawItem(item->window.rect.x, item->window.rect.y, item->window.rect.w, item->window.rect.h, item->textalignx, item->textaligny, item->window.ownerDraw, item->window.ownerDrawFlags, item->alignment, item->special, item->textscale, color, item->window.background, item->textStyle ); +    } +  } +} + + +void Item_Paint(itemDef_t *item) { +  vec4_t red; +  menuDef_t *parent = (menuDef_t*)item->parent; +  red[0] = red[3] = 1; +  red[1] = red[2] = 0; + +  if (item == NULL) { +    return; +  } + +  if (item->window.flags & WINDOW_ORBITING) { +    if (DC->realTime > item->window.nextTime) { +      float rx, ry, a, c, s, w, h; + +      item->window.nextTime = DC->realTime + item->window.offsetTime; +      // translate +      w = item->window.rectClient.w / 2; +      h = item->window.rectClient.h / 2; +      rx = item->window.rectClient.x + w - item->window.rectEffects.x; +      ry = item->window.rectClient.y + h - item->window.rectEffects.y; +      a = 3 * M_PI / 180; +      c = cos(a); +      s = sin(a); +      item->window.rectClient.x = (rx * c - ry * s) + item->window.rectEffects.x - w; +      item->window.rectClient.y = (rx * s + ry * c) + item->window.rectEffects.y - h; +      Item_UpdatePosition(item); + +    } +  } + + +  if (item->window.flags & WINDOW_INTRANSITION) { +    if (DC->realTime > item->window.nextTime) { +      int done = 0; +      item->window.nextTime = DC->realTime + item->window.offsetTime; +      // transition the x,y +      if (item->window.rectClient.x == item->window.rectEffects.x) { +        done++; +      } else { +        if (item->window.rectClient.x < item->window.rectEffects.x) { +          item->window.rectClient.x += item->window.rectEffects2.x; +          if (item->window.rectClient.x > item->window.rectEffects.x) { +            item->window.rectClient.x = item->window.rectEffects.x; +            done++; +          } +        } else { +          item->window.rectClient.x -= item->window.rectEffects2.x; +          if (item->window.rectClient.x < item->window.rectEffects.x) { +            item->window.rectClient.x = item->window.rectEffects.x; +            done++; +          } +        } +      } +      if (item->window.rectClient.y == item->window.rectEffects.y) { +        done++; +      } else { +        if (item->window.rectClient.y < item->window.rectEffects.y) { +          item->window.rectClient.y += item->window.rectEffects2.y; +          if (item->window.rectClient.y > item->window.rectEffects.y) { +            item->window.rectClient.y = item->window.rectEffects.y; +            done++; +          } +        } else { +          item->window.rectClient.y -= item->window.rectEffects2.y; +          if (item->window.rectClient.y < item->window.rectEffects.y) { +            item->window.rectClient.y = item->window.rectEffects.y; +            done++; +          } +        } +      } +      if (item->window.rectClient.w == item->window.rectEffects.w) { +        done++; +      } else { +        if (item->window.rectClient.w < item->window.rectEffects.w) { +          item->window.rectClient.w += item->window.rectEffects2.w; +          if (item->window.rectClient.w > item->window.rectEffects.w) { +            item->window.rectClient.w = item->window.rectEffects.w; +            done++; +          } +        } else { +          item->window.rectClient.w -= item->window.rectEffects2.w; +          if (item->window.rectClient.w < item->window.rectEffects.w) { +            item->window.rectClient.w = item->window.rectEffects.w; +            done++; +          } +        } +      } +      if (item->window.rectClient.h == item->window.rectEffects.h) { +        done++; +      } else { +        if (item->window.rectClient.h < item->window.rectEffects.h) { +          item->window.rectClient.h += item->window.rectEffects2.h; +          if (item->window.rectClient.h > item->window.rectEffects.h) { +            item->window.rectClient.h = item->window.rectEffects.h; +            done++; +          } +        } else { +          item->window.rectClient.h -= item->window.rectEffects2.h; +          if (item->window.rectClient.h < item->window.rectEffects.h) { +            item->window.rectClient.h = item->window.rectEffects.h; +            done++; +          } +        } +      } + +      Item_UpdatePosition(item); + +      if (done == 4) { +        item->window.flags &= ~WINDOW_INTRANSITION; +      } + +    } +  } + +  if (item->window.ownerDrawFlags && DC->ownerDrawVisible) { +    if (!DC->ownerDrawVisible(item->window.ownerDrawFlags)) { +      item->window.flags &= ~WINDOW_VISIBLE; +    } else { +      item->window.flags |= WINDOW_VISIBLE; +    } +  } + +  if (item->cvarFlags & (CVAR_SHOW | CVAR_HIDE)) { +    if (!Item_EnableShowViaCvar(item, CVAR_SHOW)) { +      return; +    } +  } + +  if (item->window.flags & WINDOW_TIMEDVISIBLE) { + +  } + +  if (!(item->window.flags & WINDOW_VISIBLE)) { +    return; +  } + +  // paint the rect first.. +  Window_Paint(&item->window, parent->fadeAmount , parent->fadeClamp, parent->fadeCycle); + +  if (debugMode) { +    vec4_t color; +    rectDef_t *r = Item_CorrectedTextRect(item); +    color[1] = color[3] = 1; +    color[0] = color[2] = 0; +    DC->drawRect(r->x, r->y, r->w, r->h, 1, color); +  } + +  //DC->drawRect(item->window.rect.x, item->window.rect.y, item->window.rect.w, item->window.rect.h, 1, red); + +  switch (item->type) { +    case ITEM_TYPE_OWNERDRAW: +      Item_OwnerDraw_Paint(item); +      break; +    case ITEM_TYPE_TEXT: +    case ITEM_TYPE_BUTTON: +      Item_Text_Paint(item); +      break; +    case ITEM_TYPE_RADIOBUTTON: +      break; +    case ITEM_TYPE_CHECKBOX: +      break; +    case ITEM_TYPE_EDITFIELD: +    case ITEM_TYPE_NUMERICFIELD: +      Item_TextField_Paint(item); +      break; +    case ITEM_TYPE_COMBO: +      break; +    case ITEM_TYPE_LISTBOX: +      Item_ListBox_Paint(item); +      break; +    //case ITEM_TYPE_IMAGE: +    //  Item_Image_Paint(item); +    //  break; +    case ITEM_TYPE_MODEL: +      Item_Model_Paint(item); +      break; +    case ITEM_TYPE_YESNO: +      Item_YesNo_Paint(item); +      break; +    case ITEM_TYPE_MULTI: +      Item_Multi_Paint(item); +      break; +    case ITEM_TYPE_BIND: +      Item_Bind_Paint(item); +      break; +    case ITEM_TYPE_SLIDER: +      Item_Slider_Paint(item); +      break; +    default: +      break; +  } + +} + +void Menu_Init(menuDef_t *menu) { +  memset(menu, 0, sizeof(menuDef_t)); +  menu->cursorItem = -1; +  menu->fadeAmount = DC->Assets.fadeAmount; +  menu->fadeClamp = DC->Assets.fadeClamp; +  menu->fadeCycle = DC->Assets.fadeCycle; +  Window_Init(&menu->window); +} + +itemDef_t *Menu_GetFocusedItem(menuDef_t *menu) { +  int i; +  if (menu) { +    for (i = 0; i < menu->itemCount; i++) { +      if (menu->items[i]->window.flags & WINDOW_HASFOCUS) { +        return menu->items[i]; +      } +    } +  } +  return NULL; +} + +menuDef_t *Menu_GetFocused( void ) { +  int i; +  for (i = 0; i < menuCount; i++) { +    if (Menus[i].window.flags & WINDOW_HASFOCUS && Menus[i].window.flags & WINDOW_VISIBLE) { +      return &Menus[i]; +    } +  } +  return NULL; +} + +void Menu_ScrollFeeder(menuDef_t *menu, int feeder, qboolean down) { +  if (menu) { +    int i; +    for (i = 0; i < menu->itemCount; i++) { +      if (menu->items[i]->special == feeder) { +        Item_ListBox_HandleKey(menu->items[i], (down) ? K_DOWNARROW : K_UPARROW, qtrue, qtrue); +        return; +      } +    } +  } +} + + + +void Menu_SetFeederSelection(menuDef_t *menu, int feeder, int index, const char *name) { +  if (menu == NULL) { +    if (name == NULL) { +      menu = Menu_GetFocused(); +    } else { +      menu = Menus_FindByName(name); +    } +  } + +  if (menu) { +    int i; +    for (i = 0; i < menu->itemCount; i++) { +      if (menu->items[i]->special == feeder) { +        if (index == 0) { +          listBoxDef_t *listPtr = (listBoxDef_t*)menu->items[i]->typeData; +          listPtr->cursorPos = 0; +          listPtr->startPos = 0; +        } +        menu->items[i]->cursorPos = index; +        DC->feederSelection(menu->items[i]->special, menu->items[i]->cursorPos); +        return; +      } +    } +  } +} + +qboolean Menus_AnyFullScreenVisible( void ) { +  int i; +  for (i = 0; i < menuCount; i++) { +    if (Menus[i].window.flags & WINDOW_VISIBLE && Menus[i].fullScreen) { +      return qtrue; +    } +  } +  return qfalse; +} + +menuDef_t *Menus_ActivateByName(const char *p) { +  int i, j; +  menuDef_t *m = NULL; +  menuDef_t *focus = Menu_GetFocused(); + +  for (i = 0; i < menuCount; i++) { +    if (Q_stricmp(Menus[i].window.name, p) == 0) { +      m = &Menus[i]; +      Menus_Activate(m); +      Menu_HandleMouseMove( m, DC->cursorx, DC->cursory ); //TA: force the item under the cursor to focus + +      for( j = 0; j < m->itemCount; j++ ) //TA: reset selection in listboxes when opened +      { +        if( m->items[ j ]->type == ITEM_TYPE_LISTBOX ) +        { +          listBoxDef_t *listPtr = (listBoxDef_t*)m->items[ j ]->typeData; +          m->items[ j ]->cursorPos = 0; +          listPtr->startPos = 0; +          DC->feederSelection( m->items[ j ]->special, 0 ); +        } +      } + +      if (openMenuCount < MAX_OPEN_MENUS && focus != NULL) { +        menuStack[openMenuCount++] = focus; +      } +    } else { +      Menus[i].window.flags &= ~WINDOW_HASFOCUS; +    } +  } +  Display_CloseCinematics(); +  return m; +} + + +void Item_Init(itemDef_t *item) { +  memset(item, 0, sizeof(itemDef_t)); +  item->textscale = 0.55f; +  Window_Init(&item->window); +} + +void Menu_HandleMouseMove(menuDef_t *menu, float x, float y) { +  int i, pass; +  qboolean focusSet = qfalse; + +  itemDef_t *overItem; +  if (menu == NULL) { +    return; +  } + +  if (!(menu->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED))) { +    return; +  } + +  if (itemCapture) { +    //Item_MouseMove(itemCapture, x, y); +    return; +  } + +  if (g_waitingForKey || g_editingField) { +    return; +  } + +  // FIXME: this is the whole issue of focus vs. mouse over.. +  // need a better overall solution as i don't like going through everything twice +  for (pass = 0; pass < 2; pass++) { +    for (i = 0; i < menu->itemCount; i++) { +      // turn off focus each item +      // menu->items[i].window.flags &= ~WINDOW_HASFOCUS; + +      if (!(menu->items[i]->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED))) { +        continue; +      } + +      // items can be enabled and disabled based on cvars +      if (menu->items[i]->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(menu->items[i], CVAR_ENABLE)) { +        continue; +      } + +      if (menu->items[i]->cvarFlags & (CVAR_SHOW | CVAR_HIDE) && !Item_EnableShowViaCvar(menu->items[i], CVAR_SHOW)) { +        continue; +      } + + + +      if (Rect_ContainsPoint(&menu->items[i]->window.rect, x, y)) { +        if (pass == 1) { +          overItem = menu->items[i]; +          if (overItem->type == ITEM_TYPE_TEXT && overItem->text) { +            if (!Rect_ContainsPoint(Item_CorrectedTextRect(overItem), x, y)) { +              continue; +            } +          } +          // if we are over an item +          if (IsVisible(overItem->window.flags)) { +            // different one +            Item_MouseEnter(overItem, x, y); +            // Item_SetMouseOver(overItem, qtrue); + +            // if item is not a decoration see if it can take focus +            if (!focusSet) { +              focusSet = Item_SetFocus(overItem, x, y); +            } +          } +        } +      } else if (menu->items[i]->window.flags & WINDOW_MOUSEOVER) { +          Item_MouseLeave(menu->items[i]); +          Item_SetMouseOver(menu->items[i], qfalse); +      } +    } +  } + +} + +void Menu_Paint(menuDef_t *menu, qboolean forcePaint) { +  int i; + +  if (menu == NULL) { +    return; +  } + +  if (!(menu->window.flags & WINDOW_VISIBLE) &&  !forcePaint) { +    return; +  } + +  if (menu->window.ownerDrawFlags && DC->ownerDrawVisible && !DC->ownerDrawVisible(menu->window.ownerDrawFlags)) { +    return; +  } + +  if (forcePaint) { +    menu->window.flags |= WINDOW_FORCED; +  } + +  // draw the background if necessary +  if (menu->fullScreen) { +    // implies a background shader +    // FIXME: make sure we have a default shader if fullscreen is set with no background +    DC->drawHandlePic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, menu->window.background ); +  } else if (menu->window.background) { +    // this allows a background shader without being full screen +    //UI_DrawHandlePic(menu->window.rect.x, menu->window.rect.y, menu->window.rect.w, menu->window.rect.h, menu->backgroundShader); +  } + +  // paint the background and or border +  Window_Paint(&menu->window, menu->fadeAmount, menu->fadeClamp, menu->fadeCycle ); + +  for (i = 0; i < menu->itemCount; i++) { +    Item_Paint(menu->items[i]); +  } + +  if (debugMode) { +    vec4_t color; +    color[0] = color[2] = color[3] = 1; +    color[1] = 0; +    DC->drawRect(menu->window.rect.x, menu->window.rect.y, menu->window.rect.w, menu->window.rect.h, 1, color); +  } +} + +/* +=============== +Item_ValidateTypeData +=============== +*/ +void Item_ValidateTypeData(itemDef_t *item) { +  if (item->typeData) { +    return; +  } + +  if (item->type == ITEM_TYPE_LISTBOX) { +    item->typeData = UI_Alloc(sizeof(listBoxDef_t)); +    memset(item->typeData, 0, sizeof(listBoxDef_t)); +  } else if (item->type == ITEM_TYPE_EDITFIELD || item->type == ITEM_TYPE_NUMERICFIELD || item->type == ITEM_TYPE_YESNO || item->type == ITEM_TYPE_BIND || item->type == ITEM_TYPE_SLIDER || item->type == ITEM_TYPE_TEXT) { +    item->typeData = UI_Alloc(sizeof(editFieldDef_t)); +    memset(item->typeData, 0, sizeof(editFieldDef_t)); +    if (item->type == ITEM_TYPE_EDITFIELD) { +      if (!((editFieldDef_t *) item->typeData)->maxPaintChars) { +        ((editFieldDef_t *) item->typeData)->maxPaintChars = MAX_EDITFIELD; +      } +    } +  } else if (item->type == ITEM_TYPE_MULTI) { +    item->typeData = UI_Alloc(sizeof(multiDef_t)); +  } else if (item->type == ITEM_TYPE_MODEL) { +    item->typeData = UI_Alloc(sizeof(modelDef_t)); +  } +} + +/* +=============== +Keyword Hash +=============== +*/ + +#define KEYWORDHASH_SIZE  512 + +typedef struct keywordHash_s +{ +  char *keyword; +  qboolean (*func)(itemDef_t *item, int handle); +  struct keywordHash_s *next; +} keywordHash_t; + +int KeywordHash_Key(char *keyword) { +  int register hash, i; + +  hash = 0; +  for (i = 0; keyword[i] != '\0'; i++) { +    if (keyword[i] >= 'A' && keyword[i] <= 'Z') +      hash += (keyword[i] + ('a' - 'A')) * (119 + i); +    else +      hash += keyword[i] * (119 + i); +  } +  hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (KEYWORDHASH_SIZE-1); +  return hash; +} + +void KeywordHash_Add(keywordHash_t *table[], keywordHash_t *key) { +  int hash; + +  hash = KeywordHash_Key(key->keyword); +/* +  if (table[hash]) { +    int collision = qtrue; +  } +*/ +  key->next = table[hash]; +  table[hash] = key; +} + +keywordHash_t *KeywordHash_Find(keywordHash_t *table[], char *keyword) +{ +  keywordHash_t *key; +  int hash; + +  hash = KeywordHash_Key(keyword); +  for (key = table[hash]; key; key = key->next) { +    if (!Q_stricmp(key->keyword, keyword)) +      return key; +  } +  return NULL; +} + +/* +=============== +Item Keyword Parse functions +=============== +*/ + +// name <string> +qboolean ItemParse_name( itemDef_t *item, int handle ) { +  if (!PC_String_Parse(handle, &item->window.name)) { +    return qfalse; +  } +  return qtrue; +} + +// name <string> +qboolean ItemParse_focusSound( itemDef_t *item, int handle ) { +  const char *temp; +  if (!PC_String_Parse(handle, &temp)) { +    return qfalse; +  } +  item->focusSound = DC->registerSound(temp, qfalse); +  return qtrue; +} + + +// text <string> +qboolean ItemParse_text( itemDef_t *item, int handle ) { +  if (!PC_String_Parse(handle, &item->text)) { +    return qfalse; +  } +  return qtrue; +} + +// group <string> +qboolean ItemParse_group( itemDef_t *item, int handle ) { +  if (!PC_String_Parse(handle, &item->window.group)) { +    return qfalse; +  } +  return qtrue; +} + +// asset_model <string> +qboolean ItemParse_asset_model( itemDef_t *item, int handle ) { +  const char *temp; +  modelDef_t *modelPtr; +  Item_ValidateTypeData(item); +  modelPtr = (modelDef_t*)item->typeData; + +  if (!PC_String_Parse(handle, &temp)) { +    return qfalse; +  } +  item->asset = DC->registerModel(temp); +  modelPtr->angle = rand() % 360; +  return qtrue; +} + +// asset_shader <string> +qboolean ItemParse_asset_shader( itemDef_t *item, int handle ) { +  const char *temp; + +  if (!PC_String_Parse(handle, &temp)) { +    return qfalse; +  } +  item->asset = DC->registerShaderNoMip(temp); +  return qtrue; +} + +// model_origin <number> <number> <number> +qboolean ItemParse_model_origin( itemDef_t *item, int handle ) { +  modelDef_t *modelPtr; +  Item_ValidateTypeData(item); +  modelPtr = (modelDef_t*)item->typeData; + +  if (PC_Float_Parse(handle, &modelPtr->origin[0])) { +    if (PC_Float_Parse(handle, &modelPtr->origin[1])) { +      if (PC_Float_Parse(handle, &modelPtr->origin[2])) { +        return qtrue; +      } +    } +  } +  return qfalse; +} + +// model_fovx <number> +qboolean ItemParse_model_fovx( itemDef_t *item, int handle ) { +  modelDef_t *modelPtr; +  Item_ValidateTypeData(item); +  modelPtr = (modelDef_t*)item->typeData; + +  if (!PC_Float_Parse(handle, &modelPtr->fov_x)) { +    return qfalse; +  } +  return qtrue; +} + +// model_fovy <number> +qboolean ItemParse_model_fovy( itemDef_t *item, int handle ) { +  modelDef_t *modelPtr; +  Item_ValidateTypeData(item); +  modelPtr = (modelDef_t*)item->typeData; + +  if (!PC_Float_Parse(handle, &modelPtr->fov_y)) { +    return qfalse; +  } +  return qtrue; +} + +// model_rotation <integer> +qboolean ItemParse_model_rotation( itemDef_t *item, int handle ) { +  modelDef_t *modelPtr; +  Item_ValidateTypeData(item); +  modelPtr = (modelDef_t*)item->typeData; + +  if (!PC_Int_Parse(handle, &modelPtr->rotationSpeed)) { +    return qfalse; +  } +  return qtrue; +} + +// model_angle <integer> +qboolean ItemParse_model_angle( itemDef_t *item, int handle ) { +  modelDef_t *modelPtr; +  Item_ValidateTypeData(item); +  modelPtr = (modelDef_t*)item->typeData; + +  if (!PC_Int_Parse(handle, &modelPtr->angle)) { +    return qfalse; +  } +  return qtrue; +} + +// rect <rectangle> +qboolean ItemParse_rect( itemDef_t *item, int handle ) { +  if (!PC_Rect_Parse(handle, &item->window.rectClient)) { +    return qfalse; +  } +  return qtrue; +} + +// style <integer> +qboolean ItemParse_style( itemDef_t *item, int handle ) { +  if (!PC_Int_Parse(handle, &item->window.style)) { +    return qfalse; +  } +  return qtrue; +} + +// decoration +qboolean ItemParse_decoration( itemDef_t *item, int handle ) { +  item->window.flags |= WINDOW_DECORATION; +  return qtrue; +} + +// notselectable +qboolean ItemParse_notselectable( itemDef_t *item, int handle ) { +  listBoxDef_t *listPtr; +  Item_ValidateTypeData(item); +  listPtr = (listBoxDef_t*)item->typeData; +  if (item->type == ITEM_TYPE_LISTBOX && listPtr) { +    listPtr->notselectable = qtrue; +  } +  return qtrue; +} + +// manually wrapped +qboolean ItemParse_wrapped( itemDef_t *item, int handle ) { +  item->window.flags |= WINDOW_WRAPPED; +  return qtrue; +} + +// auto wrapped +qboolean ItemParse_autowrapped( itemDef_t *item, int handle ) { +  item->window.flags |= WINDOW_AUTOWRAPPED; +  return qtrue; +} + + +// horizontalscroll +qboolean ItemParse_horizontalscroll( itemDef_t *item, int handle ) { +  item->window.flags |= WINDOW_HORIZONTAL; +  return qtrue; +} + +// type <integer> +qboolean ItemParse_type( itemDef_t *item, int handle ) { +  if (!PC_Int_Parse(handle, &item->type)) { +    return qfalse; +  } +  Item_ValidateTypeData(item); +  return qtrue; +} + +// elementwidth, used for listbox image elements +// uses textalignx for storage +qboolean ItemParse_elementwidth( itemDef_t *item, int handle ) { +  listBoxDef_t *listPtr; + +  Item_ValidateTypeData(item); +  listPtr = (listBoxDef_t*)item->typeData; +  if (!PC_Float_Parse(handle, &listPtr->elementWidth)) { +    return qfalse; +  } +  return qtrue; +} + +// elementheight, used for listbox image elements +// uses textaligny for storage +qboolean ItemParse_elementheight( itemDef_t *item, int handle ) { +  listBoxDef_t *listPtr; + +  Item_ValidateTypeData(item); +  listPtr = (listBoxDef_t*)item->typeData; +  if (!PC_Float_Parse(handle, &listPtr->elementHeight)) { +    return qfalse; +  } +  return qtrue; +} + +// feeder <float> +qboolean ItemParse_feeder( itemDef_t *item, int handle ) { +  if (!PC_Float_Parse(handle, &item->special)) { +    return qfalse; +  } +  return qtrue; +} + +// elementtype, used to specify what type of elements a listbox contains +// uses textstyle for storage +qboolean ItemParse_elementtype( itemDef_t *item, int handle ) { +  listBoxDef_t *listPtr; + +  Item_ValidateTypeData(item); +  if (!item->typeData) +    return qfalse; +  listPtr = (listBoxDef_t*)item->typeData; +  if (!PC_Int_Parse(handle, &listPtr->elementStyle)) { +    return qfalse; +  } +  return qtrue; +} + +// columns sets a number of columns and an x pos and width per.. +qboolean ItemParse_columns( itemDef_t *item, int handle ) { +  int num, i; +  listBoxDef_t *listPtr; + +  Item_ValidateTypeData(item); +  if (!item->typeData) +    return qfalse; +  listPtr = (listBoxDef_t*)item->typeData; +  if (PC_Int_Parse(handle, &num)) { +    if (num > MAX_LB_COLUMNS) { +      num = MAX_LB_COLUMNS; +    } +    listPtr->numColumns = num; +    for (i = 0; i < num; i++) { +      int pos, width, maxChars, align; + +      if( PC_Int_Parse( handle, &pos ) && +          PC_Int_Parse( handle, &width ) && +          PC_Int_Parse( handle, &maxChars ) && +          PC_Int_Parse( handle, &align ) ) +      { +        listPtr->columnInfo[i].pos = pos; +        listPtr->columnInfo[i].width = width; +        listPtr->columnInfo[i].maxChars = maxChars; +        listPtr->columnInfo[i].align = align; +      } else { +        return qfalse; +      } +    } +  } else { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_border( itemDef_t *item, int handle ) { +  if (!PC_Int_Parse(handle, &item->window.border)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_bordersize( itemDef_t *item, int handle ) { +  if (!PC_Float_Parse(handle, &item->window.borderSize)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_visible( itemDef_t *item, int handle ) { +  int i; + +  if (!PC_Int_Parse(handle, &i)) { +    return qfalse; +  } +  if (i) { +    item->window.flags |= WINDOW_VISIBLE; +  } +  return qtrue; +} + +qboolean ItemParse_ownerdraw( itemDef_t *item, int handle ) { +  if (!PC_Int_Parse(handle, &item->window.ownerDraw)) { +    return qfalse; +  } +  item->type = ITEM_TYPE_OWNERDRAW; +  return qtrue; +} + +qboolean ItemParse_align( itemDef_t *item, int handle ) { +  if (!PC_Int_Parse(handle, &item->alignment)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_textalign( itemDef_t *item, int handle ) { +  if (!PC_Int_Parse(handle, &item->textalignment)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_textalignx( itemDef_t *item, int handle ) { +  if (!PC_Float_Parse(handle, &item->textalignx)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_textaligny( itemDef_t *item, int handle ) { +  if (!PC_Float_Parse(handle, &item->textaligny)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_textscale( itemDef_t *item, int handle ) { +  if (!PC_Float_Parse(handle, &item->textscale)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_textstyle( itemDef_t *item, int handle ) { +  if (!PC_Int_Parse(handle, &item->textStyle)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_backcolor( itemDef_t *item, int handle ) { +  int i; +  float f; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    item->window.backColor[i]  = f; +  } +  return qtrue; +} + +qboolean ItemParse_forecolor( itemDef_t *item, int handle ) { +  int i; +  float f; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    item->window.foreColor[i]  = f; +    item->window.flags |= WINDOW_FORECOLORSET; +  } +  return qtrue; +} + +qboolean ItemParse_bordercolor( itemDef_t *item, int handle ) { +  int i; +  float f; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    item->window.borderColor[i]  = f; +  } +  return qtrue; +} + +qboolean ItemParse_outlinecolor( itemDef_t *item, int handle ) { +  if (!PC_Color_Parse(handle, &item->window.outlineColor)){ +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_background( itemDef_t *item, int handle ) { +  const char *temp; + +  if (!PC_String_Parse(handle, &temp)) { +    return qfalse; +  } +  item->window.background = DC->registerShaderNoMip(temp); +  return qtrue; +} + +qboolean ItemParse_cinematic( itemDef_t *item, int handle ) { +  if (!PC_String_Parse(handle, &item->window.cinematicName)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_doubleClick( itemDef_t *item, int handle ) { +  listBoxDef_t *listPtr; + +  Item_ValidateTypeData(item); +  if (!item->typeData) { +    return qfalse; +  } + +  listPtr = (listBoxDef_t*)item->typeData; + +  if (!PC_Script_Parse(handle, &listPtr->doubleClick)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_onFocus( itemDef_t *item, int handle ) { +  if (!PC_Script_Parse(handle, &item->onFocus)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_leaveFocus( itemDef_t *item, int handle ) { +  if (!PC_Script_Parse(handle, &item->leaveFocus)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_mouseEnter( itemDef_t *item, int handle ) { +  if (!PC_Script_Parse(handle, &item->mouseEnter)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_mouseExit( itemDef_t *item, int handle ) { +  if (!PC_Script_Parse(handle, &item->mouseExit)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_mouseEnterText( itemDef_t *item, int handle ) { +  if (!PC_Script_Parse(handle, &item->mouseEnterText)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_mouseExitText( itemDef_t *item, int handle ) { +  if (!PC_Script_Parse(handle, &item->mouseExitText)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_action( itemDef_t *item, int handle ) { +  if (!PC_Script_Parse(handle, &item->action)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_special( itemDef_t *item, int handle ) { +  if (!PC_Float_Parse(handle, &item->special)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_cvarTest( itemDef_t *item, int handle ) { +  if (!PC_String_Parse(handle, &item->cvarTest)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean ItemParse_cvar( itemDef_t *item, int handle ) { +  editFieldDef_t *editPtr; + +  Item_ValidateTypeData(item); +  if (!PC_String_Parse(handle, &item->cvar)) { +    return qfalse; +  } +  if (item->typeData) { +    editPtr = (editFieldDef_t*)item->typeData; +    editPtr->minVal = -1; +    editPtr->maxVal = -1; +    editPtr->defVal = -1; +  } +  return qtrue; +} + +qboolean ItemParse_maxChars( itemDef_t *item, int handle ) { +  editFieldDef_t *editPtr; +  int maxChars; + +  Item_ValidateTypeData(item); +  if (!item->typeData) +    return qfalse; + +  if (!PC_Int_Parse(handle, &maxChars)) { +    return qfalse; +  } +  editPtr = (editFieldDef_t*)item->typeData; +  editPtr->maxChars = maxChars; +  return qtrue; +} + +qboolean ItemParse_maxPaintChars( itemDef_t *item, int handle ) { +  editFieldDef_t *editPtr; +  int maxChars; + +  Item_ValidateTypeData(item); +  if (!item->typeData) +    return qfalse; + +  if (!PC_Int_Parse(handle, &maxChars)) { +    return qfalse; +  } +  editPtr = (editFieldDef_t*)item->typeData; +  editPtr->maxPaintChars = maxChars; +  return qtrue; +} + + + +qboolean ItemParse_cvarFloat( itemDef_t *item, int handle ) { +  editFieldDef_t *editPtr; + +  Item_ValidateTypeData(item); +  if (!item->typeData) +    return qfalse; +  editPtr = (editFieldDef_t*)item->typeData; +  if (PC_String_Parse(handle, &item->cvar) && +    PC_Float_Parse(handle, &editPtr->defVal) && +    PC_Float_Parse(handle, &editPtr->minVal) && +    PC_Float_Parse(handle, &editPtr->maxVal)) { +    return qtrue; +  } +  return qfalse; +} + +qboolean ItemParse_cvarStrList( itemDef_t *item, int handle ) { +  pc_token_t token; +  multiDef_t *multiPtr; +  int pass; + +  Item_ValidateTypeData(item); +  if (!item->typeData) +    return qfalse; +  multiPtr = (multiDef_t*)item->typeData; +  multiPtr->count = 0; +  multiPtr->strDef = qtrue; + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; +  if (*token.string != '{') { +    return qfalse; +  } + +  pass = 0; +  while ( 1 ) { +    if (!trap_Parse_ReadToken(handle, &token)) { +      PC_SourceError(handle, "end of file inside menu item\n"); +      return qfalse; +    } + +    if (*token.string == '}') { +      return qtrue; +    } + +    if (*token.string == ',' || *token.string == ';') { +      continue; +    } + +    if (pass == 0) { +      multiPtr->cvarList[multiPtr->count] = String_Alloc(token.string); +      pass = 1; +    } else { +      multiPtr->cvarStr[multiPtr->count] = String_Alloc(token.string); +      pass = 0; +      multiPtr->count++; +      if (multiPtr->count >= MAX_MULTI_CVARS) { +        return qfalse; +      } +    } + +  } +  return qfalse;  // bk001205 - LCC missing return value +} + +qboolean ItemParse_cvarFloatList( itemDef_t *item, int handle ) { +  pc_token_t token; +  multiDef_t *multiPtr; + +  Item_ValidateTypeData(item); +  if (!item->typeData) +    return qfalse; +  multiPtr = (multiDef_t*)item->typeData; +  multiPtr->count = 0; +  multiPtr->strDef = qfalse; + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; +  if (*token.string != '{') { +    return qfalse; +  } + +  while ( 1 ) { +    if (!trap_Parse_ReadToken(handle, &token)) { +      PC_SourceError(handle, "end of file inside menu item\n"); +      return qfalse; +    } + +    if (*token.string == '}') { +      return qtrue; +    } + +    if (*token.string == ',' || *token.string == ';') { +      continue; +    } + +    multiPtr->cvarList[multiPtr->count] = String_Alloc(token.string); +    if (!PC_Float_Parse(handle, &multiPtr->cvarValue[multiPtr->count])) { +      return qfalse; +    } + +    multiPtr->count++; +    if (multiPtr->count >= MAX_MULTI_CVARS) { +      return qfalse; +    } + +  } +  return qfalse;  // bk001205 - LCC missing return value +} + + + +qboolean ItemParse_addColorRange( itemDef_t *item, int handle ) { +  colorRangeDef_t color; + +  if (PC_Float_Parse(handle, &color.low) && +    PC_Float_Parse(handle, &color.high) && +    PC_Color_Parse(handle, &color.color) ) { +    if (item->numColors < MAX_COLOR_RANGES) { +      memcpy(&item->colorRanges[item->numColors], &color, sizeof(color)); +      item->numColors++; +    } +    return qtrue; +  } +  return qfalse; +} + +qboolean ItemParse_ownerdrawFlag( itemDef_t *item, int handle ) { +  int i; +  if (!PC_Int_Parse(handle, &i)) { +    return qfalse; +  } +  item->window.ownerDrawFlags |= i; +  return qtrue; +} + +qboolean ItemParse_enableCvar( itemDef_t *item, int handle ) { +  if (PC_Script_Parse(handle, &item->enableCvar)) { +    item->cvarFlags = CVAR_ENABLE; +    return qtrue; +  } +  return qfalse; +} + +qboolean ItemParse_disableCvar( itemDef_t *item, int handle ) { +  if (PC_Script_Parse(handle, &item->enableCvar)) { +    item->cvarFlags = CVAR_DISABLE; +    return qtrue; +  } +  return qfalse; +} + +qboolean ItemParse_showCvar( itemDef_t *item, int handle ) { +  if (PC_Script_Parse(handle, &item->enableCvar)) { +    item->cvarFlags = CVAR_SHOW; +    return qtrue; +  } +  return qfalse; +} + +qboolean ItemParse_hideCvar( itemDef_t *item, int handle ) { +  if (PC_Script_Parse(handle, &item->enableCvar)) { +    item->cvarFlags = CVAR_HIDE; +    return qtrue; +  } +  return qfalse; +} + + +keywordHash_t itemParseKeywords[] = { +  {"name", ItemParse_name, NULL}, +  {"text", ItemParse_text, NULL}, +  {"group", ItemParse_group, NULL}, +  {"asset_model", ItemParse_asset_model, NULL}, +  {"asset_shader", ItemParse_asset_shader, NULL}, +  {"model_origin", ItemParse_model_origin, NULL}, +  {"model_fovx", ItemParse_model_fovx, NULL}, +  {"model_fovy", ItemParse_model_fovy, NULL}, +  {"model_rotation", ItemParse_model_rotation, NULL}, +  {"model_angle", ItemParse_model_angle, NULL}, +  {"rect", ItemParse_rect, NULL}, +  {"style", ItemParse_style, NULL}, +  {"decoration", ItemParse_decoration, NULL}, +  {"notselectable", ItemParse_notselectable, NULL}, +  {"wrapped", ItemParse_wrapped, NULL}, +  {"autowrapped", ItemParse_autowrapped, NULL}, +  {"horizontalscroll", ItemParse_horizontalscroll, NULL}, +  {"type", ItemParse_type, NULL}, +  {"elementwidth", ItemParse_elementwidth, NULL}, +  {"elementheight", ItemParse_elementheight, NULL}, +  {"feeder", ItemParse_feeder, NULL}, +  {"elementtype", ItemParse_elementtype, NULL}, +  {"columns", ItemParse_columns, NULL}, +  {"border", ItemParse_border, NULL}, +  {"bordersize", ItemParse_bordersize, NULL}, +  {"visible", ItemParse_visible, NULL}, +  {"ownerdraw", ItemParse_ownerdraw, NULL}, +  {"align", ItemParse_align, NULL}, +  {"textalign", ItemParse_textalign, NULL}, +  {"textalignx", ItemParse_textalignx, NULL}, +  {"textaligny", ItemParse_textaligny, NULL}, +  {"textscale", ItemParse_textscale, NULL}, +  {"textstyle", ItemParse_textstyle, NULL}, +  {"backcolor", ItemParse_backcolor, NULL}, +  {"forecolor", ItemParse_forecolor, NULL}, +  {"bordercolor", ItemParse_bordercolor, NULL}, +  {"outlinecolor", ItemParse_outlinecolor, NULL}, +  {"background", ItemParse_background, NULL}, +  {"onFocus", ItemParse_onFocus, NULL}, +  {"leaveFocus", ItemParse_leaveFocus, NULL}, +  {"mouseEnter", ItemParse_mouseEnter, NULL}, +  {"mouseExit", ItemParse_mouseExit, NULL}, +  {"mouseEnterText", ItemParse_mouseEnterText, NULL}, +  {"mouseExitText", ItemParse_mouseExitText, NULL}, +  {"action", ItemParse_action, NULL}, +  {"special", ItemParse_special, NULL}, +  {"cvar", ItemParse_cvar, NULL}, +  {"maxChars", ItemParse_maxChars, NULL}, +  {"maxPaintChars", ItemParse_maxPaintChars, NULL}, +  {"focusSound", ItemParse_focusSound, NULL}, +  {"cvarFloat", ItemParse_cvarFloat, NULL}, +  {"cvarStrList", ItemParse_cvarStrList, NULL}, +  {"cvarFloatList", ItemParse_cvarFloatList, NULL}, +  {"addColorRange", ItemParse_addColorRange, NULL}, +  {"ownerdrawFlag", ItemParse_ownerdrawFlag, NULL}, +  {"enableCvar", ItemParse_enableCvar, NULL}, +  {"cvarTest", ItemParse_cvarTest, NULL}, +  {"disableCvar", ItemParse_disableCvar, NULL}, +  {"showCvar", ItemParse_showCvar, NULL}, +  {"hideCvar", ItemParse_hideCvar, NULL}, +  {"cinematic", ItemParse_cinematic, NULL}, +  {"doubleclick", ItemParse_doubleClick, NULL}, +  {NULL, voidFunction2, NULL} +}; + +keywordHash_t *itemParseKeywordHash[KEYWORDHASH_SIZE]; + +/* +=============== +Item_SetupKeywordHash +=============== +*/ +void Item_SetupKeywordHash( void ) +{ +  int i; + +  memset( itemParseKeywordHash, 0, sizeof( itemParseKeywordHash ) ); + +  for( i = 0; itemParseKeywords[ i ].keyword; i++ ) +    KeywordHash_Add( itemParseKeywordHash, &itemParseKeywords[ i ] ); +} + +/* +=============== +Item_Parse +=============== +*/ +qboolean Item_Parse(int handle, itemDef_t *item) { +  pc_token_t token; +  keywordHash_t *key; + + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; +  if (*token.string != '{') { +    return qfalse; +  } +  while ( 1 ) { +    if (!trap_Parse_ReadToken(handle, &token)) { +      PC_SourceError(handle, "end of file inside menu item\n"); +      return qfalse; +    } + +    if (*token.string == '}') { +      return qtrue; +    } + +    key = KeywordHash_Find(itemParseKeywordHash, token.string); +    if (!key) { +      PC_SourceError(handle, "unknown menu item keyword %s", token.string); +      continue; +    } +    if ( !key->func(item, handle) ) { +      PC_SourceError(handle, "couldn't parse menu item keyword %s", token.string); +      return qfalse; +    } +  } +  return qfalse;  // bk001205 - LCC missing return value +} + + +// Item_InitControls +// init's special control types +void Item_InitControls(itemDef_t *item) { +  if (item == NULL) { +    return; +  } +  if (item->type == ITEM_TYPE_LISTBOX) { +    listBoxDef_t *listPtr = (listBoxDef_t*)item->typeData; +    item->cursorPos = 0; +    if (listPtr) { +      listPtr->cursorPos = 0; +      listPtr->startPos = 0; +      listPtr->endPos = 0; +      listPtr->cursorPos = 0; +    } +  } +} + +/* +=============== +Menu Keyword Parse functions +=============== +*/ + +qboolean MenuParse_font( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_String_Parse(handle, &menu->font)) { +    return qfalse; +  } +  if (!DC->Assets.fontRegistered) { +    DC->registerFont(menu->font, 48, &DC->Assets.textFont); +    DC->Assets.fontRegistered = qtrue; +  } +  return qtrue; +} + +qboolean MenuParse_name( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_String_Parse(handle, &menu->window.name)) { +    return qfalse; +  } +  if (Q_stricmp(menu->window.name, "main") == 0) { +    // default main as having focus +    //menu->window.flags |= WINDOW_HASFOCUS; +  } +  return qtrue; +} + +qboolean MenuParse_fullscreen( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Int_Parse(handle, (int*) &menu->fullScreen)) { // bk001206 - cast qboolean +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_rect( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Rect_Parse(handle, &menu->window.rect)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_style( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Int_Parse(handle, &menu->window.style)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_visible( itemDef_t *item, int handle ) { +  int i; +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_Int_Parse(handle, &i)) { +    return qfalse; +  } +  if (i) { +    menu->window.flags |= WINDOW_VISIBLE; +  } +  return qtrue; +} + +qboolean MenuParse_onOpen( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Script_Parse(handle, &menu->onOpen)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_onClose( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Script_Parse(handle, &menu->onClose)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_onESC( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Script_Parse(handle, &menu->onESC)) { +    return qfalse; +  } +  return qtrue; +} + + + +qboolean MenuParse_border( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Int_Parse(handle, &menu->window.border)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_borderSize( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Float_Parse(handle, &menu->window.borderSize)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_backcolor( itemDef_t *item, int handle ) { +  int i; +  float f; +  menuDef_t *menu = (menuDef_t*)item; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    menu->window.backColor[i]  = f; +  } +  return qtrue; +} + +qboolean MenuParse_forecolor( itemDef_t *item, int handle ) { +  int i; +  float f; +  menuDef_t *menu = (menuDef_t*)item; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    menu->window.foreColor[i]  = f; +    menu->window.flags |= WINDOW_FORECOLORSET; +  } +  return qtrue; +} + +qboolean MenuParse_bordercolor( itemDef_t *item, int handle ) { +  int i; +  float f; +  menuDef_t *menu = (menuDef_t*)item; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    menu->window.borderColor[i]  = f; +  } +  return qtrue; +} + +qboolean MenuParse_focuscolor( itemDef_t *item, int handle ) { +  int i; +  float f; +  menuDef_t *menu = (menuDef_t*)item; + +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    menu->focusColor[i]  = f; +  } +  return qtrue; +} + +qboolean MenuParse_disablecolor( itemDef_t *item, int handle ) { +  int i; +  float f; +  menuDef_t *menu = (menuDef_t*)item; +  for (i = 0; i < 4; i++) { +    if (!PC_Float_Parse(handle, &f)) { +      return qfalse; +    } +    menu->disableColor[i]  = f; +  } +  return qtrue; +} + + +qboolean MenuParse_outlinecolor( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (!PC_Color_Parse(handle, &menu->window.outlineColor)){ +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_background( itemDef_t *item, int handle ) { +  const char *buff; +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_String_Parse(handle, &buff)) { +    return qfalse; +  } +  menu->window.background = DC->registerShaderNoMip(buff); +  return qtrue; +} + +qboolean MenuParse_cinematic( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_String_Parse(handle, &menu->window.cinematicName)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_ownerdrawFlag( itemDef_t *item, int handle ) { +  int i; +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_Int_Parse(handle, &i)) { +    return qfalse; +  } +  menu->window.ownerDrawFlags |= i; +  return qtrue; +} + +qboolean MenuParse_ownerdraw( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_Int_Parse(handle, &menu->window.ownerDraw)) { +    return qfalse; +  } +  return qtrue; +} + + +// decoration +qboolean MenuParse_popup( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  menu->window.flags |= WINDOW_POPUP; +  return qtrue; +} + + +qboolean MenuParse_outOfBounds( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; + +  menu->window.flags |= WINDOW_OOB_CLICK; +  return qtrue; +} + +qboolean MenuParse_soundLoop( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_String_Parse(handle, &menu->soundName)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_fadeClamp( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_Float_Parse(handle, &menu->fadeClamp)) { +    return qfalse; +  } +  return qtrue; +} + +qboolean MenuParse_fadeAmount( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_Float_Parse(handle, &menu->fadeAmount)) { +    return qfalse; +  } +  return qtrue; +} + + +qboolean MenuParse_fadeCycle( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; + +  if (!PC_Int_Parse(handle, &menu->fadeCycle)) { +    return qfalse; +  } +  return qtrue; +} + + +qboolean MenuParse_itemDef( itemDef_t *item, int handle ) { +  menuDef_t *menu = (menuDef_t*)item; +  if (menu->itemCount < MAX_MENUITEMS) { +    menu->items[menu->itemCount] = UI_Alloc(sizeof(itemDef_t)); +    Item_Init(menu->items[menu->itemCount]); +    if (!Item_Parse(handle, menu->items[menu->itemCount])) { +      return qfalse; +    } +    Item_InitControls(menu->items[menu->itemCount]); +    menu->items[menu->itemCount++]->parent = menu; +  } +  return qtrue; +} + +keywordHash_t menuParseKeywords[] = { +  {"font", MenuParse_font, NULL}, +  {"name", MenuParse_name, NULL}, +  {"fullscreen", MenuParse_fullscreen, NULL}, +  {"rect", MenuParse_rect, NULL}, +  {"style", MenuParse_style, NULL}, +  {"visible", MenuParse_visible, NULL}, +  {"onOpen", MenuParse_onOpen, NULL}, +  {"onClose", MenuParse_onClose, NULL}, +  {"onESC", MenuParse_onESC, NULL}, +  {"border", MenuParse_border, NULL}, +  {"borderSize", MenuParse_borderSize, NULL}, +  {"backcolor", MenuParse_backcolor, NULL}, +  {"forecolor", MenuParse_forecolor, NULL}, +  {"bordercolor", MenuParse_bordercolor, NULL}, +  {"focuscolor", MenuParse_focuscolor, NULL}, +  {"disablecolor", MenuParse_disablecolor, NULL}, +  {"outlinecolor", MenuParse_outlinecolor, NULL}, +  {"background", MenuParse_background, NULL}, +  {"ownerdraw", MenuParse_ownerdraw, NULL}, +  {"ownerdrawFlag", MenuParse_ownerdrawFlag, NULL}, +  {"outOfBoundsClick", MenuParse_outOfBounds, NULL}, +  {"soundLoop", MenuParse_soundLoop, NULL}, +  {"itemDef", MenuParse_itemDef, NULL}, +  {"cinematic", MenuParse_cinematic, NULL}, +  {"popup", MenuParse_popup, NULL}, +  {"fadeClamp", MenuParse_fadeClamp, NULL}, +  {"fadeCycle", MenuParse_fadeCycle, NULL}, +  {"fadeAmount", MenuParse_fadeAmount, NULL}, +  {NULL, voidFunction2, NULL} +}; + +keywordHash_t *menuParseKeywordHash[KEYWORDHASH_SIZE]; + +/* +=============== +Menu_SetupKeywordHash +=============== +*/ +void Menu_SetupKeywordHash( void ) +{ +  int i; + +  memset( menuParseKeywordHash, 0, sizeof( menuParseKeywordHash ) ); + +  for(i = 0; menuParseKeywords[ i ].keyword; i++ ) +    KeywordHash_Add( menuParseKeywordHash, &menuParseKeywords[ i ] ); +} + +/* +=============== +Menu_Parse +=============== +*/ +qboolean Menu_Parse(int handle, menuDef_t *menu) { +  pc_token_t token; +  keywordHash_t *key; + +  if (!trap_Parse_ReadToken(handle, &token)) +    return qfalse; +  if (*token.string != '{') { +    return qfalse; +  } + +  while ( 1 ) { + +    memset(&token, 0, sizeof(pc_token_t)); +    if (!trap_Parse_ReadToken(handle, &token)) { +      PC_SourceError(handle, "end of file inside menu\n"); +      return qfalse; +    } + +    if (*token.string == '}') { +      return qtrue; +    } + +    key = KeywordHash_Find(menuParseKeywordHash, token.string); +    if (!key) { +      PC_SourceError(handle, "unknown menu keyword %s", token.string); +      continue; +    } +    if ( !key->func((itemDef_t*)menu, handle) ) { +      PC_SourceError(handle, "couldn't parse menu keyword %s", token.string); +      return qfalse; +    } +  } +  return qfalse;  // bk001205 - LCC missing return value +} + +/* +=============== +Menu_New +=============== +*/ +void Menu_New(int handle) { +  menuDef_t *menu = &Menus[menuCount]; + +  if (menuCount < MAX_MENUS) { +    Menu_Init(menu); +    if (Menu_Parse(handle, menu)) { +      Menu_PostParse(menu); +      menuCount++; +    } +  } +} + +int Menu_Count( void ) { +  return menuCount; +} + +void Menu_PaintAll( void ) { +  int i; +  if (captureFunc) { +    captureFunc(captureData); +  } + +  for (i = 0; i < Menu_Count(); i++) { +    Menu_Paint(&Menus[i], qfalse); +  } + +  if (debugMode) { +    vec4_t v = {1, 1, 1, 1}; +    DC->drawText(5, 25, .5, v, va("fps: %f", DC->FPS), 0, 0, 0); +  } +} + +void Menu_Reset( void ) +{ +  menuCount = 0; +} + +displayContextDef_t *Display_GetContext( void ) { +  return DC; +} + +void *Display_CaptureItem(int x, int y) { +  int i; + +  for (i = 0; i < menuCount; i++) { +    // turn off focus each item +    // menu->items[i].window.flags &= ~WINDOW_HASFOCUS; +    if (Rect_ContainsPoint(&Menus[i].window.rect, x, y)) { +      return &Menus[i]; +    } +  } +  return NULL; +} + + +// FIXME: +qboolean Display_MouseMove(void *p, int x, int y) { +  int i; +  menuDef_t *menu = p; + +  if (menu == NULL) { +    menu = Menu_GetFocused(); +    if (menu) { +      if (menu->window.flags & WINDOW_POPUP) { +        Menu_HandleMouseMove(menu, x, y); +        return qtrue; +      } +    } +    for (i = 0; i < menuCount; i++) { +      Menu_HandleMouseMove(&Menus[i], x, y); +    } +  } else { +    menu->window.rect.x += x; +    menu->window.rect.y += y; +    Menu_UpdatePosition(menu); +  } +  return qtrue; + +} + +int Display_CursorType(int x, int y) { +  int i; +  for (i = 0; i < menuCount; i++) { +    rectDef_t r2; +    r2.x = Menus[i].window.rect.x - 3; +    r2.y = Menus[i].window.rect.y - 3; +    r2.w = r2.h = 7; +    if (Rect_ContainsPoint(&r2, x, y)) { +      return CURSOR_SIZER; +    } +  } +  return CURSOR_ARROW; +} + + +void Display_HandleKey(int key, qboolean down, int x, int y) { +  menuDef_t *menu = Display_CaptureItem(x, y); +  if (menu == NULL) { +    menu = Menu_GetFocused(); +  } +  if (menu) { +    Menu_HandleKey(menu, key, down ); +  } +} + +static void Window_CacheContents(windowDef_t *window) { +  if (window) { +    if (window->cinematicName) { +      int cin = DC->playCinematic(window->cinematicName, 0, 0, 0, 0); +      DC->stopCinematic(cin); +    } +  } +} + + +static void Item_CacheContents(itemDef_t *item) { +  if (item) { +    Window_CacheContents(&item->window); +  } + +} + +static void Menu_CacheContents(menuDef_t *menu) { +  if (menu) { +    int i; +    Window_CacheContents(&menu->window); +    for (i = 0; i < menu->itemCount; i++) { +      Item_CacheContents(menu->items[i]); +    } + +    if (menu->soundName && *menu->soundName) { +      DC->registerSound(menu->soundName, qfalse); +    } +  } + +} + +void Display_CacheAll( void ) { +  int i; +  for (i = 0; i < menuCount; i++) { +    Menu_CacheContents(&Menus[i]); +  } +} + + +static qboolean Menu_OverActiveItem(menuDef_t *menu, float x, float y) { +  if (menu && menu->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED)) { +    if (Rect_ContainsPoint(&menu->window.rect, x, y)) { +      int i; +      for (i = 0; i < menu->itemCount; i++) { +        // turn off focus each item +        // menu->items[i].window.flags &= ~WINDOW_HASFOCUS; + +        if (!(menu->items[i]->window.flags & (WINDOW_VISIBLE | WINDOW_FORCED))) { +          continue; +        } + +        if (menu->items[i]->window.flags & WINDOW_DECORATION) { +          continue; +        } + +        if (Rect_ContainsPoint(&menu->items[i]->window.rect, x, y)) { +          itemDef_t *overItem = menu->items[i]; +          if (overItem->type == ITEM_TYPE_TEXT && overItem->text) { +            if (Rect_ContainsPoint(Item_CorrectedTextRect(overItem), x, y)) { +              return qtrue; +            } else { +              continue; +            } +          } else { +            return qtrue; +          } +        } +      } + +    } +  } +  return qfalse; +} + diff --git a/src/ui/ui_shared.h b/src/ui/ui_shared.h new file mode 100644 index 0000000..c775bcd --- /dev/null +++ b/src/ui/ui_shared.h @@ -0,0 +1,454 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +#ifndef __UI_SHARED_H
 +#define __UI_SHARED_H
 +
 +
 +#include "../qcommon/q_shared.h"
 +#include "../renderer/tr_types.h"
 +#include "../client/keycodes.h"
 +
 +#include "../../ui/menudef.h"
 +
 +#define MAX_MENUNAME 32
 +#define MAX_ITEMTEXT 64
 +#define MAX_ITEMACTION 64
 +#define MAX_MENUDEFFILE 4096
 +#define MAX_MENUFILE 32768
 +#define MAX_MENUS 256
 +#define MAX_MENUITEMS 128
 +#define MAX_COLOR_RANGES 10
 +#define MAX_OPEN_MENUS 16
 +
 +#define WINDOW_MOUSEOVER      0x00000001  // mouse is over it, non exclusive
 +#define WINDOW_HASFOCUS        0x00000002  // has cursor focus, exclusive
 +#define WINDOW_VISIBLE        0x00000004  // is visible
 +#define WINDOW_GREY            0x00000008  // is visible but grey ( non-active )
 +#define WINDOW_DECORATION      0x00000010  // for decoration only, no mouse, keyboard, etc..
 +#define WINDOW_FADINGOUT      0x00000020  // fading out, non-active
 +#define WINDOW_FADINGIN        0x00000040  // fading in
 +#define WINDOW_MOUSEOVERTEXT  0x00000080  // mouse is over it, non exclusive
 +#define WINDOW_INTRANSITION    0x00000100  // window is in transition
 +#define WINDOW_FORECOLORSET    0x00000200  // forecolor was explicitly set ( used to color alpha images or not )
 +#define WINDOW_HORIZONTAL      0x00000400  // for list boxes and sliders, vertical is default this is set of horizontal
 +#define WINDOW_LB_LEFTARROW    0x00000800  // mouse is over left/up arrow
 +#define WINDOW_LB_RIGHTARROW  0x00001000  // mouse is over right/down arrow
 +#define WINDOW_LB_THUMB        0x00002000  // mouse is over thumb
 +#define WINDOW_LB_PGUP        0x00004000  // mouse is over page up
 +#define WINDOW_LB_PGDN        0x00008000  // mouse is over page down
 +#define WINDOW_ORBITING        0x00010000  // item is in orbit
 +#define WINDOW_OOB_CLICK      0x00020000  // close on out of bounds click
 +#define WINDOW_WRAPPED        0x00040000  // manually wrap text
 +#define WINDOW_AUTOWRAPPED      0x00080000  // auto wrap text
 +#define WINDOW_FORCED          0x00100000  // forced open
 +#define WINDOW_POPUP          0x00200000  // popup
 +#define WINDOW_BACKCOLORSET    0x00400000  // backcolor was explicitly set
 +#define WINDOW_TIMEDVISIBLE    0x00800000  // visibility timing ( NOT implemented )
 +
 +
 +// CGAME cursor type bits
 +#define CURSOR_NONE          0x00000001
 +#define CURSOR_ARROW        0x00000002
 +#define CURSOR_SIZER        0x00000004
 +
 +#ifdef CGAME
 +#define STRING_POOL_SIZE 128*1024
 +#else
 +#define STRING_POOL_SIZE 384*1024
 +#endif
 +#define MAX_STRING_HANDLES 4096
 +
 +#define MAX_SCRIPT_ARGS 12
 +#define MAX_EDITFIELD 256
 +
 +#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 ASSET_GRADIENTBAR "ui/assets/gradientbar2.tga"
 +#define ASSET_SCROLLBAR             "ui/assets/scrollbar.tga"
 +#define ASSET_SCROLLBAR_ARROWDOWN   "ui/assets/scrollbar_arrow_dwn_a.tga"
 +#define ASSET_SCROLLBAR_ARROWUP     "ui/assets/scrollbar_arrow_up_a.tga"
 +#define ASSET_SCROLLBAR_ARROWLEFT   "ui/assets/scrollbar_arrow_left.tga"
 +#define ASSET_SCROLLBAR_ARROWRIGHT  "ui/assets/scrollbar_arrow_right.tga"
 +#define ASSET_SCROLL_THUMB          "ui/assets/scrollbar_thumb.tga"
 +#define ASSET_SLIDER_BAR            "ui/assets/slider2.tga"
 +#define ASSET_SLIDER_THUMB          "ui/assets/sliderbutt_1.tga"
 +#define SCROLLBAR_SIZE 16.0
 +#define SLIDER_WIDTH 96.0
 +#define SLIDER_HEIGHT 16.0
 +#define SLIDER_THUMB_WIDTH 12.0
 +#define SLIDER_THUMB_HEIGHT 20.0
 +#define  NUM_CROSSHAIRS      10
 +
 +typedef struct {
 +  const char *command;
 +  const char *args[MAX_SCRIPT_ARGS];
 +} scriptDef_t;
 +
 +
 +typedef struct {
 +  float x;    // horiz position
 +  float y;    // vert position
 +  float w;    // width
 +  float h;    // height;
 +} rectDef_t;
 +
 +typedef rectDef_t Rectangle;
 +
 +// FIXME: do something to separate text vs window stuff
 +typedef struct {
 +  Rectangle rect;                 // client coord rectangle
 +  Rectangle rectClient;           // screen coord rectangle
 +  const char *name;               //
 +  const char *group;              // if it belongs to a group
 +  const char *cinematicName;      // cinematic name
 +  int cinematic;                  // cinematic handle
 +  int style;                      //
 +  int border;                     //
 +  int ownerDraw;                  // ownerDraw style
 +  int ownerDrawFlags;              // show flags for ownerdraw items
 +  float borderSize;               //
 +  int flags;                      // visible, focus, mouseover, cursor
 +  Rectangle rectEffects;          // for various effects
 +  Rectangle rectEffects2;         // for various effects
 +  int offsetTime;                 // time based value for various effects
 +  int nextTime;                   // time next effect should cycle
 +  vec4_t foreColor;               // text color
 +  vec4_t backColor;               // border color
 +  vec4_t borderColor;             // border color
 +  vec4_t outlineColor;            // border color
 +  qhandle_t background;           // background asset
 +} windowDef_t;
 +
 +typedef windowDef_t Window;
 +
 +typedef struct {
 +  vec4_t  color;
 +  float    low;
 +  float    high;
 +} colorRangeDef_t;
 +
 +// FIXME: combine flags into bitfields to save space
 +// FIXME: consolidate all of the common stuff in one structure for menus and items
 +// THINKABOUTME: is there any compelling reason not to have items contain items
 +// and do away with a menu per say.. major issue is not being able to dynamically allocate
 +// and destroy stuff.. Another point to consider is adding an alloc free call for vm's and have
 +// the engine just allocate the pool for it based on a cvar
 +// many of the vars are re-used for different item types, as such they are not always named appropriately
 +// the benefits of c++ in DOOM will greatly help crap like this
 +// FIXME: need to put a type ptr that points to specific type info per type
 +//
 +#define MAX_LB_COLUMNS 16
 +
 +typedef struct columnInfo_s {
 +  int pos;
 +  int width;
 +  int maxChars;
 +  int align;
 +} columnInfo_t;
 +
 +typedef struct listBoxDef_s {
 +  int startPos;
 +  int endPos;
 +  int drawPadding;
 +  int cursorPos;
 +  float elementWidth;
 +  float elementHeight;
 +  int elementStyle;
 +  int numColumns;
 +  columnInfo_t columnInfo[MAX_LB_COLUMNS];
 +  const char *doubleClick;
 +  qboolean notselectable;
 +} listBoxDef_t;
 +
 +typedef struct editFieldDef_s {
 +  float minVal;                  //  edit field limits
 +  float maxVal;                  //
 +  float defVal;                  //
 +  float range;                   //
 +  int maxChars;                  // for edit fields
 +  int maxPaintChars;             // for edit fields
 +  int paintOffset;               //
 +} editFieldDef_t;
 +
 +#define MAX_MULTI_CVARS 32
 +
 +typedef struct multiDef_s {
 +  const char *cvarList[MAX_MULTI_CVARS];
 +  const char *cvarStr[MAX_MULTI_CVARS];
 +  float cvarValue[MAX_MULTI_CVARS];
 +  int count;
 +  qboolean strDef;
 +} multiDef_t;
 +
 +typedef struct modelDef_s {
 +  int angle;
 +  vec3_t origin;
 +  float fov_x;
 +  float fov_y;
 +  int rotationSpeed;
 +} modelDef_t;
 +
 +#define CVAR_ENABLE    0x00000001
 +#define CVAR_DISABLE  0x00000002
 +#define CVAR_SHOW      0x00000004
 +#define CVAR_HIDE      0x00000008
 +
 +typedef struct itemDef_s {
 +  Window window;                 // common positional, border, style, layout info
 +  Rectangle textRect;            // rectangle the text ( if any ) consumes
 +  int type;                      // text, button, radiobutton, checkbox, textfield, listbox, combo
 +  int alignment;                 // left center right
 +  int textalignment;             // ( optional ) alignment for text within rect based on text width
 +  float textalignx;              // ( optional ) text alignment x coord
 +  float textaligny;              // ( optional ) text alignment x coord
 +  float textscale;               // scale percentage from 72pts
 +  int textStyle;                 // ( optional ) style, normal and shadowed are it for now
 +  const char *text;              // display text
 +  void *parent;                  // menu owner
 +  qhandle_t asset;               // handle to asset
 +  const char *mouseEnterText;    // mouse enter script
 +  const char *mouseExitText;     // mouse exit script
 +  const char *mouseEnter;        // mouse enter script
 +  const char *mouseExit;         // mouse exit script
 +  const char *action;            // select script
 +  const char *onFocus;           // select script
 +  const char *leaveFocus;        // select script
 +  const char *cvar;              // associated cvar
 +  const char *cvarTest;          // associated cvar for enable actions
 +  const char *enableCvar;         // enable, disable, show, or hide based on value, this can contain a list
 +  int cvarFlags;                 //  what type of action to take on cvarenables
 +  sfxHandle_t focusSound;
 +  int numColors;                 // number of color ranges
 +  colorRangeDef_t colorRanges[MAX_COLOR_RANGES];
 +  float special;                 // used for feeder id's etc.. diff per type
 +  int cursorPos;                 // cursor position in characters
 +  void *typeData;                 // type specific data ptr's
 +} itemDef_t;
 +
 +typedef struct {
 +  Window window;
 +  const char  *font;                // font
 +  qboolean fullScreen;              // covers entire screen
 +  int itemCount;                    // number of items;
 +  int fontIndex;                    //
 +  int cursorItem;                    // which item as the cursor
 +  int fadeCycle;                    //
 +  float fadeClamp;                  //
 +  float fadeAmount;                  //
 +  const char *onOpen;                // run when the menu is first opened
 +  const char *onClose;              // run when the menu is closed
 +  const char *onESC;                // run when the menu is closed
 +  const char *soundName;            // background loop sound for menu
 +
 +  vec4_t focusColor;                // focus color for items
 +  vec4_t disableColor;              // focus color for items
 +  itemDef_t *items[MAX_MENUITEMS];  // items this menu contains
 +} menuDef_t;
 +
 +typedef struct {
 +  const char *fontStr;
 +  const char *cursorStr;
 +  const char *gradientStr;
 +  fontInfo_t textFont;
 +  fontInfo_t smallFont;
 +  fontInfo_t bigFont;
 +  qhandle_t cursor;
 +  qhandle_t gradientBar;
 +  qhandle_t scrollBarArrowUp;
 +  qhandle_t scrollBarArrowDown;
 +  qhandle_t scrollBarArrowLeft;
 +  qhandle_t scrollBarArrowRight;
 +  qhandle_t scrollBar;
 +  qhandle_t scrollBarThumb;
 +  qhandle_t buttonMiddle;
 +  qhandle_t buttonInside;
 +  qhandle_t solidBox;
 +  qhandle_t sliderBar;
 +  qhandle_t sliderThumb;
 +  sfxHandle_t menuEnterSound;
 +  sfxHandle_t menuExitSound;
 +  sfxHandle_t menuBuzzSound;
 +  sfxHandle_t itemFocusSound;
 +  float fadeClamp;
 +  int fadeCycle;
 +  float fadeAmount;
 +  float shadowX;
 +  float shadowY;
 +  vec4_t shadowColor;
 +  float shadowFadeClamp;
 +  qboolean fontRegistered;
 +
 +} cachedAssets_t;
 +
 +typedef struct {
 +  const char *name;
 +  void (*handler) (itemDef_t *item, char** args);
 +} commandDef_t;
 +
 +typedef struct {
 +  qhandle_t (*registerShaderNoMip) (const char *p);
 +  void (*setColor) (const vec4_t v);
 +  void (*drawHandlePic) (float x, float y, float w, float h, qhandle_t asset);
 +  void (*drawStretchPic) (float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader );
 +  void (*drawText) (float x, float y, float scale, vec4_t color, const char *text, float adjust, int limit, int style );
 +  int (*textWidth) (const char *text, float scale, int limit);
 +  int (*textHeight) (const char *text, float scale, int limit);
 +  qhandle_t (*registerModel) (const char *p);
 +  void (*modelBounds) (qhandle_t model, vec3_t min, vec3_t max);
 +  void (*fillRect) ( float x, float y, float w, float h, const vec4_t color);
 +  void (*drawRect) ( float x, float y, float w, float h, float size, const vec4_t color);
 +  void (*drawSides) (float x, float y, float w, float h, float size);
 +  void (*drawTopBottom) (float x, float y, float w, float h, float size);
 +  void (*clearScene) (void);
 +  void (*addRefEntityToScene) (const refEntity_t *re );
 +  void (*renderScene) ( const refdef_t *fd );
 +  void (*registerFont) (const char *pFontname, int pointSize, fontInfo_t *font);
 +  void (*ownerDrawItem) (float x, float y, float w, float h, float text_x, float text_y, int ownerDraw, int ownerDrawFlags, int align, float special, float scale, vec4_t color, qhandle_t shader, int textStyle);
 +  float (*getValue) (int ownerDraw);
 +  qboolean (*ownerDrawVisible) (int flags);
 +  void (*runScript)(char **p);
 +  void (*getTeamColor)(vec4_t *color);
 +  void (*getCVarString)(const char *cvar, char *buffer, int bufsize);
 +  float (*getCVarValue)(const char *cvar);
 +  void (*setCVar)(const char *cvar, const char *value);
 +  void (*drawTextWithCursor)(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style);
 +  void (*setOverstrikeMode)(qboolean b);
 +  qboolean (*getOverstrikeMode)( void );
 +  void (*startLocalSound)( sfxHandle_t sfx, int channelNum );
 +  qboolean (*ownerDrawHandleKey)(int ownerDraw, int flags, float *special, int key);
 +  int (*feederCount)(float feederID);
 +  const char *(*feederItemText)(float feederID, int index, int column, qhandle_t *handle);
 +  qhandle_t (*feederItemImage)(float feederID, int index);
 +  void (*feederSelection)(float feederID, int index);
 +  void (*keynumToStringBuf)( int keynum, char *buf, int buflen );
 +  void (*getBindingBuf)( int keynum, char *buf, int buflen );
 +  void (*setBinding)( int keynum, const char *binding );
 +  void (*executeText)(int exec_when, const char *text );
 +  void (*Error)(int level, const char *error, ...);
 +  void (*Print)(const char *msg, ...);
 +  void (*Pause)(qboolean b);
 +  int (*ownerDrawWidth)(int ownerDraw, float scale);
 +  sfxHandle_t (*registerSound)(const char *name, qboolean compressed);
 +  void (*startBackgroundTrack)( const char *intro, const char *loop);
 +  void (*stopBackgroundTrack)( void );
 +  int (*playCinematic)(const char *name, float x, float y, float w, float h);
 +  void (*stopCinematic)(int handle);
 +  void (*drawCinematic)(int handle, float x, float y, float w, float h);
 +  void (*runCinematicFrame)(int handle);
 +
 +  float      yscale;
 +  float      xscale;
 +  float      bias;
 +  int        realTime;
 +  int        frameTime;
 +  int        cursorx;
 +  int        cursory;
 +  qboolean  debug;
 +
 +  cachedAssets_t Assets;
 +
 +  glconfig_t glconfig;
 +  qhandle_t  whiteShader;
 +  qhandle_t gradientImage;
 +  qhandle_t cursor;
 +  float FPS;
 +
 +} displayContextDef_t;
 +
 +const char *String_Alloc(const char *p);
 +void String_Init( void );
 +void String_Report( void );
 +void Init_Display(displayContextDef_t *dc);
 +void Display_ExpandMacros(char * buff);
 +void Menu_Init(menuDef_t *menu);
 +void Item_Init(itemDef_t *item);
 +void Menu_PostParse(menuDef_t *menu);
 +menuDef_t *Menu_GetFocused( void );
 +void Menu_HandleKey(menuDef_t *menu, int key, qboolean down);
 +void Menu_HandleMouseMove(menuDef_t *menu, float x, float y);
 +void Menu_ScrollFeeder(menuDef_t *menu, int feeder, qboolean down);
 +qboolean Float_Parse(char **p, float *f);
 +qboolean Color_Parse(char **p, vec4_t *c);
 +qboolean Int_Parse(char **p, int *i);
 +qboolean Rect_Parse(char **p, rectDef_t *r);
 +qboolean String_Parse(char **p, const char **out);
 +qboolean Script_Parse(char **p, const char **out);
 +qboolean PC_Float_Parse(int handle, float *f);
 +qboolean PC_Color_Parse(int handle, vec4_t *c);
 +qboolean PC_Int_Parse(int handle, int *i);
 +qboolean PC_Rect_Parse(int handle, rectDef_t *r);
 +qboolean PC_String_Parse(int handle, const char **out);
 +qboolean PC_Script_Parse(int handle, const char **out);
 +int Menu_Count( void );
 +void Menu_New(int handle);
 +void Menu_PaintAll( void );
 +menuDef_t *Menus_ActivateByName(const char *p);
 +void Menu_Reset( void );
 +qboolean Menus_AnyFullScreenVisible( void );
 +void  Menus_Activate(menuDef_t *menu);
 +
 +displayContextDef_t *Display_GetContext( void );
 +void *Display_CaptureItem(int x, int y);
 +qboolean Display_MouseMove(void *p, int x, int y);
 +int Display_CursorType(int x, int y);
 +qboolean Display_KeyBindPending( void );
 +void Menus_OpenByName(const char *p);
 +menuDef_t *Menus_FindByName(const char *p);
 +void Menus_ShowByName(const char *p);
 +void Menus_CloseByName(const char *p);
 +void Display_HandleKey(int key, qboolean down, int x, int y);
 +void LerpColor(vec4_t a, vec4_t b, vec4_t c, float t);
 +void Menus_CloseAll( void );
 +void Menu_Paint(menuDef_t *menu, qboolean forcePaint);
 +void Menu_SetFeederSelection(menuDef_t *menu, int feeder, int index, const char *name);
 +void Display_CacheAll( void );
 +
 +void *UI_Alloc( int size );
 +void UI_InitMemory( void );
 +qboolean UI_OutOfMemory( void );
 +
 +void Controls_GetConfig( void );
 +void Controls_SetConfig(qboolean restart);
 +void Controls_SetDefaults( void );
 +
 +//for cg_draw.c
 +void Item_Text_AutoWrapped_Paint( itemDef_t *item );
 +
 +int      trap_Parse_AddGlobalDefine( char *define );
 +int      trap_Parse_LoadSource( const char *filename );
 +int      trap_Parse_FreeSource( int handle );
 +int      trap_Parse_ReadToken( int handle, pc_token_t *pc_token );
 +int      trap_Parse_SourceFileAndLine( int handle, char *filename, int *line );
 +
 +void    BindingFromName( const char *cvar );
 +extern char g_nameBind1[ 32 ];
 +extern char g_nameBind2[ 32 ];
 +#endif
 diff --git a/src/ui/ui_syscalls.asm b/src/ui/ui_syscalls.asm new file mode 100644 index 0000000..6ac04e8 --- /dev/null +++ b/src/ui/ui_syscalls.asm @@ -0,0 +1,97 @@ +code
 +
 +equ trap_Error                        -1
 +equ trap_Print                        -2
 +equ trap_Milliseconds                 -3
 +equ trap_Cvar_Set                     -4
 +equ trap_Cvar_VariableValue           -5
 +equ trap_Cvar_VariableStringBuffer    -6
 +equ trap_Cvar_SetValue                -7
 +equ trap_Cvar_Reset                   -8
 +equ trap_Cvar_Create                  -9
 +equ trap_Cvar_InfoStringBuffer        -10
 +equ trap_Argc                         -11
 +equ trap_Argv                         -12
 +equ trap_Cmd_ExecuteText              -13
 +equ trap_FS_FOpenFile                 -14
 +equ trap_FS_Read                      -15
 +equ trap_FS_Write                     -16
 +equ trap_FS_FCloseFile                -17
 +equ trap_FS_GetFileList               -18
 +equ trap_R_RegisterModel              -19
 +equ trap_R_RegisterSkin               -20
 +equ trap_R_RegisterShaderNoMip        -21
 +equ trap_R_ClearScene                 -22
 +equ trap_R_AddRefEntityToScene        -23
 +equ trap_R_AddPolyToScene             -24
 +equ trap_R_AddLightToScene            -25
 +equ trap_R_RenderScene                -26
 +equ trap_R_SetColor                   -27
 +equ trap_R_DrawStretchPic             -28
 +equ trap_UpdateScreen                 -29
 +equ trap_CM_LerpTag                   -30
 +equ trap_CM_LoadModel                 -31
 +equ trap_S_RegisterSound              -32
 +equ trap_S_StartLocalSound            -33
 +equ trap_Key_KeynumToStringBuf        -34
 +equ trap_Key_GetBindingBuf            -35
 +equ trap_Key_SetBinding               -36
 +equ trap_Key_IsDown                   -37
 +equ trap_Key_GetOverstrikeMode        -38
 +equ trap_Key_SetOverstrikeMode        -39
 +equ trap_Key_ClearStates              -40
 +equ trap_Key_GetCatcher               -41
 +equ trap_Key_SetCatcher               -42        
 +equ trap_GetClipboardData             -43
 +equ trap_GetGlconfig                  -44
 +equ trap_GetClientState               -45
 +equ trap_GetConfigString              -46
 +equ trap_LAN_GetPingQueueCount        -47
 +equ trap_LAN_ClearPing                -48
 +equ trap_LAN_GetPing                  -49
 +equ trap_LAN_GetPingInfo              -50
 +equ trap_Cvar_Register                -51
 +equ trap_Cvar_Update                  -52
 +equ trap_MemoryRemaining              -53
 +equ trap_R_RegisterFont               -54
 +equ trap_R_ModelBounds                -55
 +equ trap_Parse_AddGlobalDefine        -56
 +equ trap_Parse_LoadSource             -57
 +equ trap_Parse_FreeSource             -58
 +equ trap_Parse_ReadToken              -59
 +equ trap_Parse_SourceFileAndLine      -60
 +equ trap_S_StopBackgroundTrack        -61
 +equ trap_S_StartBackgroundTrack       -62
 +equ trap_RealTime                     -63
 +equ trap_LAN_GetServerCount           -64
 +equ trap_LAN_GetServerAddressString   -65
 +equ trap_LAN_GetServerInfo            -66
 +equ trap_LAN_MarkServerVisible        -67
 +equ trap_LAN_UpdateVisiblePings       -68
 +equ trap_LAN_ResetPings               -69
 +equ trap_LAN_LoadCachedServers        -70
 +equ trap_LAN_SaveCachedServers        -71
 +equ trap_LAN_AddServer                -72
 +equ trap_LAN_RemoveServer             -73
 +equ trap_CIN_PlayCinematic            -74
 +equ trap_CIN_StopCinematic            -75
 +equ trap_CIN_RunCinematic             -76
 +equ trap_CIN_DrawCinematic            -77
 +equ trap_CIN_SetExtents               -78
 +equ trap_R_RemapShader                -79
 +equ trap_LAN_ServerStatus             -80
 +equ trap_LAN_GetServerPing            -81
 +equ trap_LAN_ServerIsVisible          -82
 +equ trap_LAN_CompareServers           -83
 +equ trap_FS_Seek                      -84
 +equ trap_SetPbClStatus                -85
 +
 +equ memset                            -101
 +equ memcpy                            -102
 +equ strncpy                           -103
 +equ sin                               -104
 +equ cos                               -105
 +equ atan2                             -106
 +equ sqrt                              -107
 +equ floor                             -108
 +equ ceil                              -109
 diff --git a/src/ui/ui_syscalls.c b/src/ui/ui_syscalls.c new file mode 100644 index 0000000..8fc8210 --- /dev/null +++ b/src/ui/ui_syscalls.c @@ -0,0 +1,387 @@ +/*
 +===========================================================================
 +Copyright (C) 1999-2005 Id Software, Inc.
 +Copyright (C) 2000-2006 Tim Angus
 +
 +This file is part of Tremulous.
 +
 +Tremulous is free software; you can redistribute it
 +and/or modify it under the terms of the GNU General Public License as
 +published by the Free Software Foundation; either version 2 of the License,
 +or (at your option) any later version.
 +
 +Tremulous is distributed in the hope that it will be
 +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with Tremulous; if not, write to the Free Software
 +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 +===========================================================================
 +*/
 +
 +#include "ui_local.h"
 +
 +// this file is only included when building a dll
 +// syscalls.asm is included instead when building a qvm
 +
 +static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1;
 +
 +void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) ) {
 +  syscall = syscallptr;
 +}
 +
 +int PASSFLOAT( float x ) {
 +  float  floatTemp;
 +  floatTemp = x;
 +  return *(int *)&floatTemp;
 +}
 +
 +void trap_Print( const char *string ) {
 +  syscall( UI_PRINT, string );
 +}
 +
 +void trap_Error( const char *string ) {
 +  syscall( UI_ERROR, string );
 +}
 +
 +int trap_Milliseconds( void ) {
 +  return syscall( UI_MILLISECONDS );
 +}
 +
 +void trap_Cvar_Register( vmCvar_t *cvar, const char *var_name, const char *value, int flags ) {
 +  syscall( UI_CVAR_REGISTER, cvar, var_name, value, flags );
 +}
 +
 +void trap_Cvar_Update( vmCvar_t *cvar ) {
 +  syscall( UI_CVAR_UPDATE, cvar );
 +}
 +
 +void trap_Cvar_Set( const char *var_name, const char *value ) {
 +  syscall( UI_CVAR_SET, var_name, value );
 +}
 +
 +float trap_Cvar_VariableValue( const char *var_name ) {
 +  int temp;
 +  temp = syscall( UI_CVAR_VARIABLEVALUE, var_name );
 +  return (*(float*)&temp);
 +}
 +
 +void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) {
 +  syscall( UI_CVAR_VARIABLESTRINGBUFFER, var_name, buffer, bufsize );
 +}
 +
 +void trap_Cvar_SetValue( const char *var_name, float value ) {
 +  syscall( UI_CVAR_SETVALUE, var_name, PASSFLOAT( value ) );
 +}
 +
 +void trap_Cvar_Reset( const char *name ) {
 +  syscall( UI_CVAR_RESET, name );
 +}
 +
 +void trap_Cvar_Create( const char *var_name, const char *var_value, int flags ) {
 +  syscall( UI_CVAR_CREATE, var_name, var_value, flags );
 +}
 +
 +void trap_Cvar_InfoStringBuffer( int bit, char *buffer, int bufsize ) {
 +  syscall( UI_CVAR_INFOSTRINGBUFFER, bit, buffer, bufsize );
 +}
 +
 +int trap_Argc( void ) {
 +  return syscall( UI_ARGC );
 +}
 +
 +void trap_Argv( int n, char *buffer, int bufferLength ) {
 +  syscall( UI_ARGV, n, buffer, bufferLength );
 +}
 +
 +void trap_Cmd_ExecuteText( int exec_when, const char *text ) {
 +  syscall( UI_CMD_EXECUTETEXT, exec_when, text );
 +}
 +
 +int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) {
 +  return syscall( UI_FS_FOPENFILE, qpath, f, mode );
 +}
 +
 +void trap_FS_Read( void *buffer, int len, fileHandle_t f ) {
 +  syscall( UI_FS_READ, buffer, len, f );
 +}
 +
 +void trap_FS_Write( const void *buffer, int len, fileHandle_t f ) {
 +  syscall( UI_FS_WRITE, buffer, len, f );
 +}
 +
 +void trap_FS_FCloseFile( fileHandle_t f ) {
 +  syscall( UI_FS_FCLOSEFILE, f );
 +}
 +
 +int trap_FS_GetFileList(  const char *path, const char *extension, char *listbuf, int bufsize ) {
 +  return syscall( UI_FS_GETFILELIST, path, extension, listbuf, bufsize );
 +}
 +
 +int trap_FS_Seek( fileHandle_t f, long offset, int origin ) {
 +    return syscall( UI_FS_SEEK, f, offset, origin );
 +}
 +
 +qhandle_t trap_R_RegisterModel( const char *name ) {
 +  return syscall( UI_R_REGISTERMODEL, name );
 +}
 +
 +qhandle_t trap_R_RegisterSkin( const char *name ) {
 +  return syscall( UI_R_REGISTERSKIN, name );
 +}
 +
 +void trap_R_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) {
 +  syscall( UI_R_REGISTERFONT, fontName, pointSize, font );
 +}
 +
 +qhandle_t trap_R_RegisterShaderNoMip( const char *name ) {
 +  return syscall( UI_R_REGISTERSHADERNOMIP, name );
 +}
 +
 +void trap_R_ClearScene( void ) {
 +  syscall( UI_R_CLEARSCENE );
 +}
 +
 +void trap_R_AddRefEntityToScene( const refEntity_t *re ) {
 +  syscall( UI_R_ADDREFENTITYTOSCENE, re );
 +}
 +
 +void trap_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) {
 +  syscall( UI_R_ADDPOLYTOSCENE, hShader, numVerts, verts );
 +}
 +
 +void trap_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
 +  syscall( UI_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) );
 +}
 +
 +void trap_R_RenderScene( const refdef_t *fd ) {
 +  syscall( UI_R_RENDERSCENE, fd );
 +}
 +
 +void trap_R_SetColor( const float *rgba ) {
 +  syscall( UI_R_SETCOLOR, rgba );
 +}
 +
 +void trap_R_DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, qhandle_t hShader ) {
 +  syscall( UI_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader );
 +}
 +
 +void  trap_R_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) {
 +  syscall( UI_R_MODELBOUNDS, model, mins, maxs );
 +}
 +
 +void trap_UpdateScreen( void ) {
 +  syscall( UI_UPDATESCREEN );
 +}
 +
 +int trap_CM_LerpTag( orientation_t *tag, clipHandle_t mod, int startFrame, int endFrame, float frac, const char *tagName ) {
 +  return syscall( UI_CM_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName );
 +}
 +
 +void trap_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) {
 +  syscall( UI_S_STARTLOCALSOUND, sfx, channelNum );
 +}
 +
 +sfxHandle_t  trap_S_RegisterSound( const char *sample, qboolean compressed ) {
 +  return syscall( UI_S_REGISTERSOUND, sample, compressed );
 +}
 +
 +void trap_Key_KeynumToStringBuf( int keynum, char *buf, int buflen ) {
 +  syscall( UI_KEY_KEYNUMTOSTRINGBUF, keynum, buf, buflen );
 +}
 +
 +void trap_Key_GetBindingBuf( int keynum, char *buf, int buflen ) {
 +  syscall( UI_KEY_GETBINDINGBUF, keynum, buf, buflen );
 +}
 +
 +void trap_Key_SetBinding( int keynum, const char *binding ) {
 +  syscall( UI_KEY_SETBINDING, keynum, binding );
 +}
 +
 +qboolean trap_Key_IsDown( int keynum ) {
 +  return syscall( UI_KEY_ISDOWN, keynum );
 +}
 +
 +qboolean trap_Key_GetOverstrikeMode( void ) {
 +  return syscall( UI_KEY_GETOVERSTRIKEMODE );
 +}
 +
 +void trap_Key_SetOverstrikeMode( qboolean state ) {
 +  syscall( UI_KEY_SETOVERSTRIKEMODE, state );
 +}
 +
 +void trap_Key_ClearStates( void ) {
 +  syscall( UI_KEY_CLEARSTATES );
 +}
 +
 +int trap_Key_GetCatcher( void ) {
 +  return syscall( UI_KEY_GETCATCHER );
 +}
 +
 +void trap_Key_SetCatcher( int catcher ) {
 +  syscall( UI_KEY_SETCATCHER, catcher );
 +}
 +
 +void trap_GetClipboardData( char *buf, int bufsize ) {
 +  syscall( UI_GETCLIPBOARDDATA, buf, bufsize );
 +}
 +
 +void trap_GetClientState( uiClientState_t *state ) {
 +  syscall( UI_GETCLIENTSTATE, state );
 +}
 +
 +void trap_GetGlconfig( glconfig_t *glconfig ) {
 +  syscall( UI_GETGLCONFIG, glconfig );
 +}
 +
 +int trap_GetConfigString( int index, char* buff, int buffsize ) {
 +  return syscall( UI_GETCONFIGSTRING, index, buff, buffsize );
 +}
 +
 +int  trap_LAN_GetServerCount( int source ) {
 +  return syscall( UI_LAN_GETSERVERCOUNT, source );
 +}
 +
 +void trap_LAN_GetServerAddressString( int source, int n, char *buf, int buflen ) {
 +  syscall( UI_LAN_GETSERVERADDRESSSTRING, source, n, buf, buflen );
 +}
 +
 +void trap_LAN_GetServerInfo( int source, int n, char *buf, int buflen ) {
 +  syscall( UI_LAN_GETSERVERINFO, source, n, buf, buflen );
 +}
 +
 +int trap_LAN_GetServerPing( int source, int n ) {
 +  return syscall( UI_LAN_GETSERVERPING, source, n );
 +}
 +
 +int trap_LAN_GetPingQueueCount( void ) {
 +  return syscall( UI_LAN_GETPINGQUEUECOUNT );
 +}
 +
 +int trap_LAN_ServerStatus( const char *serverAddress, char *serverStatus, int maxLen ) {
 +  return syscall( UI_LAN_SERVERSTATUS, serverAddress, serverStatus, maxLen );
 +}
 +
 +void trap_LAN_SaveCachedServers( void ) {
 +  syscall( UI_LAN_SAVECACHEDSERVERS );
 +}
 +
 +void trap_LAN_LoadCachedServers( void ) {
 +  syscall( UI_LAN_LOADCACHEDSERVERS );
 +}
 +
 +void trap_LAN_ResetPings(int n) {
 +  syscall( UI_LAN_RESETPINGS, n );
 +}
 +
 +void trap_LAN_ClearPing( int n ) {
 +  syscall( UI_LAN_CLEARPING, n );
 +}
 +
 +void trap_LAN_GetPing( int n, char *buf, int buflen, int *pingtime ) {
 +  syscall( UI_LAN_GETPING, n, buf, buflen, pingtime );
 +}
 +
 +void trap_LAN_GetPingInfo( int n, char *buf, int buflen ) {
 +  syscall( UI_LAN_GETPINGINFO, n, buf, buflen );
 +}
 +
 +void trap_LAN_MarkServerVisible( int source, int n, qboolean visible ) {
 +  syscall( UI_LAN_MARKSERVERVISIBLE, source, n, visible );
 +}
 +
 +int trap_LAN_ServerIsVisible( int source, int n) {
 +  return syscall( UI_LAN_SERVERISVISIBLE, source, n );
 +}
 +
 +qboolean trap_LAN_UpdateVisiblePings( int source ) {
 +  return syscall( UI_LAN_UPDATEVISIBLEPINGS, source );
 +}
 +
 +int trap_LAN_AddServer(int source, const char *name, const char *addr) {
 +  return syscall( UI_LAN_ADDSERVER, source, name, addr );
 +}
 +
 +void trap_LAN_RemoveServer(int source, const char *addr) {
 +  syscall( UI_LAN_REMOVESERVER, source, addr );
 +}
 +
 +int trap_LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ) {
 +  return syscall( UI_LAN_COMPARESERVERS, source, sortKey, sortDir, s1, s2 );
 +}
 +
 +int trap_MemoryRemaining( void ) {
 +  return syscall( UI_MEMORY_REMAINING );
 +}
 +
 +int trap_Parse_AddGlobalDefine( char *define ) {
 +  return syscall( UI_PARSE_ADD_GLOBAL_DEFINE, define );
 +}
 +
 +int trap_Parse_LoadSource( const char *filename ) {
 +  return syscall( UI_PARSE_LOAD_SOURCE, filename );
 +}
 +
 +int trap_Parse_FreeSource( int handle ) {
 +  return syscall( UI_PARSE_FREE_SOURCE, handle );
 +}
 +
 +int trap_Parse_ReadToken( int handle, pc_token_t *pc_token ) {
 +  return syscall( UI_PARSE_READ_TOKEN, handle, pc_token );
 +}
 +
 +int trap_Parse_SourceFileAndLine( int handle, char *filename, int *line ) {
 +  return syscall( UI_PARSE_SOURCE_FILE_AND_LINE, handle, filename, line );
 +}
 +
 +void trap_S_StopBackgroundTrack( void ) {
 +  syscall( UI_S_STOPBACKGROUNDTRACK );
 +}
 +
 +void trap_S_StartBackgroundTrack( const char *intro, const char *loop) {
 +  syscall( UI_S_STARTBACKGROUNDTRACK, intro, loop );
 +}
 +
 +int trap_RealTime(qtime_t *qtime) {
 +  return syscall( UI_REAL_TIME, qtime );
 +}
 +
 +// this returns a handle.  arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate)
 +int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits) {
 +  return syscall(UI_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits);
 +}
 +
 +// stops playing the cinematic and ends it.  should always return FMV_EOF
 +// cinematics must be stopped in reverse order of when they are started
 +e_status trap_CIN_StopCinematic(int handle) {
 +  return syscall(UI_CIN_STOPCINEMATIC, handle);
 +}
 +
 +
 +// will run a frame of the cinematic but will not draw it.  Will return FMV_EOF if the end of the cinematic has been reached.
 +e_status trap_CIN_RunCinematic (int handle) {
 +  return syscall(UI_CIN_RUNCINEMATIC, handle);
 +}
 +
 +
 +// draws the current frame
 +void trap_CIN_DrawCinematic (int handle) {
 +  syscall(UI_CIN_DRAWCINEMATIC, handle);
 +}
 +
 +
 +// allows you to resize the animation dynamically
 +void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) {
 +  syscall(UI_CIN_SETEXTENTS, handle, x, y, w, h);
 +}
 +
 +
 +void  trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) {
 +  syscall( UI_R_REMAP_SHADER, oldShader, newShader, timeOffset );
 +}
 +
 +void trap_SetPbClStatus( int status ) {
 +    syscall( UI_SET_PBCLSTATUS, status );
 +}
  | 
