diff options
Diffstat (limited to 'src/ui')
| -rw-r--r-- | src/ui/menudef.h | 362 | ||||
| -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 | 6500 | ||||
| -rw-r--r-- | src/ui/ui_players.c | 1369 | ||||
| -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 | 98 | ||||
| -rw-r--r-- | src/ui/ui_syscalls.c | 387 | 
10 files changed, 17304 insertions, 0 deletions
diff --git a/src/ui/menudef.h b/src/ui/menudef.h new file mode 100644 index 0000000..d4ed0b2 --- /dev/null +++ b/src/ui/menudef.h @@ -0,0 +1,362 @@ + +#define ITEM_TYPE_TEXT 0                  // simple text +#define ITEM_TYPE_BUTTON 1                // button, basically text with a border  +#define ITEM_TYPE_RADIOBUTTON 2           // toggle button, may be grouped  +#define ITEM_TYPE_CHECKBOX 3              // check box +#define ITEM_TYPE_EDITFIELD 4             // editable text, associated with a cvar +#define ITEM_TYPE_COMBO 5                 // drop down list +#define ITEM_TYPE_LISTBOX 6               // scrollable list   +#define ITEM_TYPE_MODEL 7                 // model +#define ITEM_TYPE_OWNERDRAW 8             // owner draw, name specs what it is +#define ITEM_TYPE_NUMERICFIELD 9          // editable text, associated with a cvar +#define ITEM_TYPE_SLIDER 10               // mouse speed, volume, etc. +#define ITEM_TYPE_YESNO 11                // yes no cvar setting +#define ITEM_TYPE_MULTI 12                // multiple list setting, enumerated +#define ITEM_TYPE_BIND 13                 // multiple list setting, enumerated +     +#define ITEM_ALIGN_LEFT 0                 // left alignment +#define ITEM_ALIGN_CENTER 1               // center alignment +#define ITEM_ALIGN_RIGHT 2                // right alignment + +#define ITEM_TEXTSTYLE_NORMAL 0           // normal text +#define ITEM_TEXTSTYLE_BLINK 1            // fast blinking +#define ITEM_TEXTSTYLE_PULSE 2            // slow pulsing +#define ITEM_TEXTSTYLE_SHADOWED 3         // drop shadow ( need a color for this ) +#define ITEM_TEXTSTYLE_OUTLINED 4         // drop shadow ( need a color for this ) +#define ITEM_TEXTSTYLE_OUTLINESHADOWED 5  // drop shadow ( need a color for this ) +#define ITEM_TEXTSTYLE_SHADOWEDMORE 6         // drop shadow ( need a color for this ) +#define ITEM_TEXTSTYLE_NEON     7         // drop shadow ( need a color for this ) +                           +#define WINDOW_BORDER_NONE 0              // no border +#define WINDOW_BORDER_FULL 1              // full border based on border color ( single pixel ) +#define WINDOW_BORDER_HORZ 2              // horizontal borders only +#define WINDOW_BORDER_VERT 3              // vertical borders only  +#define WINDOW_BORDER_KCGRADIENT 4        // horizontal border using the gradient bars +   +#define WINDOW_STYLE_EMPTY 0              // no background +#define WINDOW_STYLE_FILLED 1             // filled with background color +#define WINDOW_STYLE_GRADIENT 2           // gradient bar based on background color  +#define WINDOW_STYLE_SHADER   3           // gradient bar based on background color  +#define WINDOW_STYLE_TEAMCOLOR 4          // team color +#define WINDOW_STYLE_CINEMATIC 5          // cinematic + +#define MENU_TRUE 1                       // uh.. true +#define MENU_FALSE 0                      // and false + +#define HUD_VERTICAL        0x00 +#define HUD_HORIZONTAL        0x01 + +// list box element types +#define LISTBOX_TEXT  0x00 +#define LISTBOX_IMAGE 0x01 + +// list feeders +#define FEEDER_HEADS                    0x00      // model heads +#define FEEDER_MAPS                     0x01      // text maps based on game type +#define FEEDER_SERVERS                  0x02      // servers +#define FEEDER_CLANS                    0x03      // clan names +#define FEEDER_ALLMAPS                  0x04      // all maps available, in graphic format +#define FEEDER_ALIENTEAM_LIST           0x05      // red team members +#define FEEDER_HUMANTEAM_LIST           0x06      // blue team members +#define FEEDER_PLAYER_LIST              0x07      // players +#define FEEDER_TEAM_LIST                0x08      // team members for team voting +#define FEEDER_MODS                     0x09      // team members for team voting +#define FEEDER_DEMOS                    0x0a      // team members for team voting +#define FEEDER_SCOREBOARD               0x0b      // team members for team voting +#define FEEDER_Q3HEADS                  0x0c      // model heads +#define FEEDER_SERVERSTATUS             0x0d      // server status +#define FEEDER_FINDPLAYER               0x0e      // find player +#define FEEDER_CINEMATICS               0x0f      // cinematics + +//TA: tremulous menus +#define FEEDER_TREMTEAMS                0x10      //teams +#define FEEDER_TREMALIENCLASSES         0x11      //alien classes +#define FEEDER_TREMHUMANITEMS           0x12      //human items +#define FEEDER_TREMHUMANARMOURYBUY      0x13      //human buy +#define FEEDER_TREMHUMANARMOURYSELL     0x14      //human sell +#define FEEDER_TREMALIENUPGRADE         0x15      //alien upgrade +#define FEEDER_TREMALIENBUILD           0x16      //alien buildables +#define FEEDER_TREMHUMANBUILD           0x17      //human buildables +//TA: tremulous menus +#define FEEDER_IGNORE_LIST              0x18      //ignored players + +// display flags +#define CG_SHOW_BLUE_TEAM_HAS_REDFLAG     0x00000001 +#define CG_SHOW_RED_TEAM_HAS_BLUEFLAG     0x00000002 +#define CG_SHOW_ANYTEAMGAME               0x00000004 +#define CG_SHOW_HARVESTER                 0x00000008 +#define CG_SHOW_ONEFLAG                   0x00000010 +#define CG_SHOW_CTF                       0x00000020 +#define CG_SHOW_OBELISK                   0x00000040 +#define CG_SHOW_HEALTHCRITICAL            0x00000080 +#define CG_SHOW_SINGLEPLAYER              0x00000100 +#define CG_SHOW_TOURNAMENT                0x00000200 +#define CG_SHOW_DURINGINCOMINGVOICE       0x00000400 +#define CG_SHOW_IF_PLAYER_HAS_FLAG        0x00000800 +#define CG_SHOW_LANPLAYONLY               0x00001000 +#define CG_SHOW_MINED                     0x00002000 +#define CG_SHOW_HEALTHOK                  0x00004000 +#define CG_SHOW_TEAMINFO                  0x00008000 +#define CG_SHOW_NOTEAMINFO                0x00010000 +#define CG_SHOW_OTHERTEAMHASFLAG          0x00020000 +#define CG_SHOW_YOURTEAMHASENEMYFLAG      0x00040000 +#define CG_SHOW_ANYNONTEAMGAME            0x00080000 +#define CG_SHOW_2DONLY                    0x10000000 + + +#define UI_SHOW_LEADER                    0x00000001 +#define UI_SHOW_NOTLEADER                 0x00000002 +#define UI_SHOW_FAVORITESERVERS           0x00000004 +#define UI_SHOW_ANYNONTEAMGAME            0x00000008 +#define UI_SHOW_ANYTEAMGAME               0x00000010 +#define UI_SHOW_NEWHIGHSCORE              0x00000020 +#define UI_SHOW_DEMOAVAILABLE             0x00000040 +#define UI_SHOW_NEWBESTTIME               0x00000080 +#define UI_SHOW_FFA                       0x00000100 +#define UI_SHOW_NOTFFA                    0x00000200 +#define UI_SHOW_NETANYNONTEAMGAME         0x00000400 +#define UI_SHOW_NETANYTEAMGAME            0x00000800 +#define UI_SHOW_NOTFAVORITESERVERS        0x00001000 + +#define UI_SHOW_VOTEACTIVE                0x00002000 +#define UI_SHOW_CANVOTE                   0x00004000 +#define UI_SHOW_TEAMVOTEACTIVE            0x00008000 +#define UI_SHOW_CANTEAMVOTE               0x00010000 + +#define UI_SHOW_NOTSPECTATING             0x00020000 + +// owner draw types +// ideally these should be done outside of this file but +// this makes it much easier for the macro expansion to  +// convert them for the designers ( from the .menu files ) +#define CG_OWNERDRAW_BASE           1 +#define CG_PLAYER_ARMOR_ICON        1               +#define CG_PLAYER_ARMOR_VALUE       2 +#define CG_PLAYER_HEAD              3 +#define CG_PLAYER_HEALTH            4 +#define CG_PLAYER_HEALTH_BAR        92 +#define CG_PLAYER_HEALTH_CROSS      99 +#define CG_PLAYER_AMMO_ICON         5 +#define CG_PLAYER_AMMO_VALUE        6 +#define CG_PLAYER_CLIPS_VALUE       70 +#define CG_PLAYER_BUILD_TIMER       115 +#define CG_PLAYER_CREDITS_VALUE     71 +#define CG_PLAYER_BANK_VALUE        72 +#define CG_PLAYER_CREDITS_VALUE_NOPAD     106 +#define CG_PLAYER_BANK_VALUE_NOPAD        107 +#define CG_PLAYER_STAMINA           73 +#define CG_PLAYER_STAMINA_1         93 +#define CG_PLAYER_STAMINA_2         94 +#define CG_PLAYER_STAMINA_3         95 +#define CG_PLAYER_STAMINA_4         96 +#define CG_PLAYER_STAMINA_BOLT      97 +#define CG_PLAYER_BOOST_BOLT        112 +#define CG_PLAYER_CLIPS_RING        98 +#define CG_PLAYER_BUILD_TIMER_RING  113 +#define CG_PLAYER_SELECT            74 +#define CG_PLAYER_SELECTTEXT        75 +#define CG_PLAYER_WEAPONICON        111 +#define CG_PLAYER_WALLCLIMBING      103 +#define CG_PLAYER_BOOSTED           104 +#define CG_PLAYER_POISON_BARBS      105 +#define CG_PLAYER_ALIEN_SENSE       108 +#define CG_PLAYER_HUMAN_SCANNER     109 +#define CG_PLAYER_USABLE_BUILDABLE  110 +#define CG_SELECTEDPLAYER_HEAD      7 +#define CG_SELECTEDPLAYER_NAME      8 +#define CG_SELECTEDPLAYER_LOCATION  9 +#define CG_SELECTEDPLAYER_STATUS    10 +#define CG_SELECTEDPLAYER_WEAPON    11 +#define CG_SELECTEDPLAYER_POWERUP   12 + +#define CG_FLAGCARRIER_HEAD         13 +#define CG_FLAGCARRIER_NAME         14 +#define CG_FLAGCARRIER_LOCATION     15 +#define CG_FLAGCARRIER_STATUS       16 +#define CG_FLAGCARRIER_WEAPON       17 +#define CG_FLAGCARRIER_POWERUP      18 + +#define CG_PLAYER_ITEM              19 +#define CG_PLAYER_SCORE             20 + +#define CG_BLUE_FLAGHEAD            21 +#define CG_BLUE_FLAGSTATUS          22 +#define CG_BLUE_FLAGNAME            23 +#define CG_RED_FLAGHEAD             24 +#define CG_RED_FLAGSTATUS           25 +#define CG_RED_FLAGNAME             26 + +#define CG_BLUE_SCORE               27 +#define CG_RED_SCORE                28 +#define CG_RED_NAME                 29 +#define CG_BLUE_NAME                30 +#define CG_HARVESTER_SKULLS         31  // only shows in harvester +#define CG_ONEFLAG_STATUS           32  // only shows in one flag +#define CG_PLAYER_LOCATION          33 +#define CG_TEAM_COLOR               34 +#define CG_CTF_POWERUP              35 +                                         +#define CG_AREA_POWERUP             36 +#define CG_AREA_LAGOMETER           37  // painted with old system +#define CG_PLAYER_HASFLAG           38             +#define CG_GAME_TYPE                39  // not done + +#define CG_SELECTEDPLAYER_ARMOR     40       +#define CG_SELECTEDPLAYER_HEALTH    41 +#define CG_PLAYER_STATUS 42 +#define CG_FRAGGED_MSG 43               // painted with old system +#define CG_PROXMINED_MSG 44             // painted with old system +#define CG_AREA_FPSINFO 45              // painted with old system +#define CG_GAME_STATUS 49 +#define CG_KILLER 50 +#define CG_PLAYER_ARMOR_ICON2D 51               +#define CG_PLAYER_AMMO_ICON2D 52 +#define CG_ACCURACY 53 +#define CG_ASSISTS 54 +#define CG_DEFEND 55 +#define CG_EXCELLENT 56 +#define CG_IMPRESSIVE 57 +#define CG_PERFECT 58 +#define CG_GAUNTLET 59 +#define CG_SPECTATORS 60 +#define CG_TEAMINFO 61 +#define CG_VOICE_HEAD 62 +#define CG_VOICE_NAME 63 +#define CG_PLAYER_HASFLAG2D 64             +#define CG_HARVESTER_SKULLS2D 65          // only shows in harvester +#define CG_CAPFRAGLIMIT 66    +#define CG_1STPLACE 67 +#define CG_2NDPLACE 68 +#define CG_CAPTURES 69 + +//TA: loading screen +#define CG_LOAD_LEVELSHOT         76 +#define CG_LOAD_MEDIA             77 +#define CG_LOAD_MEDIA_LABEL       78 +#define CG_LOAD_BUILDABLES        79 +#define CG_LOAD_BUILDABLES_LABEL  80 +#define CG_LOAD_CHARMODEL         81 +#define CG_LOAD_CHARMODEL_LABEL   82 +#define CG_LOAD_OVERALL           83 +#define CG_LOAD_LEVELNAME         84 +#define CG_LOAD_MOTD              85 +#define CG_LOAD_HOSTNAME          86 + +#define CG_FPS                    87 +#define CG_FPS_FIXED              100 +#define CG_TIMER                  88 +#define CG_TIMER_MINS             101 +#define CG_TIMER_SECS             102 +#define CG_SNAPSHOT               89 +#define CG_LAGOMETER              90 +#define CG_PLAYER_CROSSHAIRNAMES  114 +#define CG_STAGE_REPORT_TEXT      116 +#define CG_DEMO_PLAYBACK          117 +#define CG_DEMO_RECORDING         118 + +#define CG_CONSOLE                91 +#define CG_TUTORIAL               119 +#define CG_CLOCK                  120 + + + +#define UI_OWNERDRAW_BASE 200 +#define UI_HANDICAP 200 +#define UI_PLAYERMODEL 202 +#define UI_CLANNAME 203 +#define UI_CLANLOGO 204 +#define UI_GAMETYPE 205 +#define UI_MAPPREVIEW 206 +#define UI_SKILL 207 +#define UI_BLUETEAMNAME 208 +#define UI_REDTEAMNAME 209 +#define UI_BLUETEAM1 210 +#define UI_BLUETEAM2 211 +#define UI_BLUETEAM3 212 +#define UI_BLUETEAM4 213 +#define UI_BLUETEAM5 214 +#define UI_REDTEAM1 215 +#define UI_REDTEAM2 216 +#define UI_REDTEAM3 217 +#define UI_REDTEAM4 218 +#define UI_REDTEAM5 219 +#define UI_NETSOURCE 220 +#define UI_NETMAPPREVIEW 221 +#define UI_NETFILTER 222 +#define UI_TIER 223 +#define UI_OPPONENTMODEL 224 +#define UI_TIERMAP1 225 +#define UI_TIERMAP2 226 +#define UI_TIERMAP3 227 +#define UI_PLAYERLOGO 228 +#define UI_OPPONENTLOGO 229 +#define UI_PLAYERLOGO_METAL 230 +#define UI_OPPONENTLOGO_METAL 231 +#define UI_PLAYERLOGO_NAME 232 +#define UI_OPPONENTLOGO_NAME 233 +#define UI_TIER_MAPNAME 234 +#define UI_TIER_GAMETYPE 235 +#define UI_ALLMAPS_SELECTION 236 +#define UI_OPPONENT_NAME 237 +#define UI_VOTE_KICK 238 +#define UI_BOTNAME 239 +#define UI_BOTSKILL 240 +#define UI_REDBLUE 241 +#define UI_SELECTEDPLAYER 243 +#define UI_MAPCINEMATIC 244 +#define UI_NETGAMETYPE 245 +#define UI_NETMAPCINEMATIC 246 +#define UI_SERVERREFRESHDATE 247 +#define UI_SERVERMOTD 248 +#define UI_GLINFO  249 +#define UI_KEYBINDSTATUS      250 +#define UI_CLANCINEMATIC      251 +#define UI_MAP_TIMETOBEAT     252 +#define UI_JOINGAMETYPE       253 +#define UI_PREVIEWCINEMATIC   254 +#define UI_STARTMAPCINEMATIC  255 +#define UI_MAPS_SELECTION     256 + +//TA: +//#define UI_DIALOG             257 +#define UI_TEAMINFOPANE       258 +#define UI_ACLASSINFOPANE     259 +#define UI_AUPGRADEINFOPANE   260 +#define UI_HITEMINFOPANE      261 +#define UI_HBUYINFOPANE       262 +#define UI_HSELLINFOPANE      263 +#define UI_ABUILDINFOPANE     264 +#define UI_HBUILDINFOPANE     265 + +#define UI_PLAYERLIST_SELECTION 266 +#define UI_TEAMLIST_SELECTION 267 + +#define VOICECHAT_GETFLAG     "getflag"       // command someone to get the flag +#define VOICECHAT_OFFENSE     "offense"       // command someone to go on offense +#define VOICECHAT_DEFEND      "defend"        // command someone to go on defense +#define VOICECHAT_DEFENDFLAG    "defendflag"      // command someone to defend the flag +#define VOICECHAT_PATROL      "patrol"        // command someone to go on patrol (roam) +#define VOICECHAT_CAMP        "camp"          // command someone to camp (we don't have sounds for this one) +#define VOICECHAT_FOLLOWME      "followme"        // command someone to follow you +#define VOICECHAT_RETURNFLAG    "returnflag"      // command someone to return our flag +#define VOICECHAT_FOLLOWFLAGCARRIER "followflagcarrier"   // command someone to follow the flag carrier +#define VOICECHAT_YES       "yes"         // yes, affirmative, etc. +#define VOICECHAT_NO        "no"          // no, negative, etc. +#define VOICECHAT_ONGETFLAG     "ongetflag"       // I'm getting the flag +#define VOICECHAT_ONOFFENSE     "onoffense"       // I'm on offense +#define VOICECHAT_ONDEFENSE     "ondefense"       // I'm on defense +#define VOICECHAT_ONPATROL      "onpatrol"        // I'm on patrol (roaming) +#define VOICECHAT_ONCAMPING     "oncamp"        // I'm camping somewhere +#define VOICECHAT_ONFOLLOW      "onfollow"        // I'm following +#define VOICECHAT_ONFOLLOWCARRIER "onfollowcarrier"   // I'm following the flag carrier +#define VOICECHAT_ONRETURNFLAG    "onreturnflag"      // I'm returning our flag +#define VOICECHAT_INPOSITION    "inposition"      // I'm in position +#define VOICECHAT_IHAVEFLAG     "ihaveflag"       // I have the flag +#define VOICECHAT_BASEATTACK    "baseattack"      // the base is under attack +#define VOICECHAT_ENEMYHASFLAG    "enemyhasflag"      // the enemy has our flag (CTF) +#define VOICECHAT_STARTLEADER   "startleader"     // I'm the leader +#define VOICECHAT_STOPLEADER    "stopleader"      // I resign leadership +#define VOICECHAT_TRASH       "trash"         // lots of trash talk +#define VOICECHAT_WHOISLEADER   "whoisleader"     // who is the team leader +#define VOICECHAT_WANTONDEFENSE   "wantondefense"     // I want to be on defense +#define VOICECHAT_WANTONOFFENSE   "wantonoffense"     // I want to be on offense diff --git a/src/ui/ui_atoms.c b/src/ui/ui_atoms.c new file mode 100644 index 0000000..64efa91 --- /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..43639e5 --- /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..7afdf65 --- /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..b193f4f --- /dev/null +++ b/src/ui/ui_main.c @@ -0,0 +1,6500 @@ +/* +=========================================================================== +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" ) ) ); +    } +  } +} + diff --git a/src/ui/ui_players.c b/src/ui/ui_players.c new file mode 100644 index 0000000..5dbfdd3 --- /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_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..e55cc7f --- /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..1b236fe --- /dev/null +++ b/src/ui/ui_syscalls.asm @@ -0,0 +1,98 @@ +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..528658e --- /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 ); +}  | 
