diff options
author | Tim Angus <tim@ngus.net> | 2001-09-18 00:05:45 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2001-09-18 00:05:45 +0000 |
commit | 74254be06ca766cf7f08ad6b84702a44dd49c282 (patch) | |
tree | 8bd54e422699822fa493812be2824a7360a61b9f /src | |
parent | 0dd20c247521f466587fb32ade318f372dd1a3e8 (diff) |
Switch to TA ui stuff
Diffstat (limited to 'src')
-rw-r--r-- | src/cgame/cg_event.c | 8 | ||||
-rw-r--r-- | src/cgame/cg_servercmds.c | 2 | ||||
-rw-r--r-- | src/game/g_cmds.c | 8 | ||||
-rw-r--r-- | src/ui/keycodes.h | 143 | ||||
-rw-r--r-- | src/ui/ui_atoms.c | 513 | ||||
-rw-r--r-- | src/ui/ui_gameinfo.c | 304 | ||||
-rw-r--r-- | src/ui/ui_main.c | 5929 | ||||
-rw-r--r-- | src/ui/ui_players.c | 1358 | ||||
-rw-r--r-- | src/ui/ui_shared.c | 5753 | ||||
-rw-r--r-- | src/ui/ui_shared.h | 429 | ||||
-rw-r--r-- | src/ui/ui_syscalls.c | 372 |
11 files changed, 14809 insertions, 10 deletions
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c index f3df5871..ea3aca78 100644 --- a/src/cgame/cg_event.c +++ b/src/cgame/cg_event.c @@ -28,10 +28,8 @@ #include "cg_local.h" -#ifdef MISSIONPACK // bk001205 // for the voice chats -#include "../ta_ui/menudef.h" -#endif +#include "../../ui/menudef.h" //========================================================================== @@ -428,8 +426,8 @@ void CG_Menu( int eventParm ) switch( eventParm ) { - case MN_TEAM: trap_SendConsoleCommand( "menu teammenu\n" ); break; - case MN_D_CLASS: trap_SendConsoleCommand( "menu dclassmenu\n" ); break; + case MN_TEAM: trap_SendConsoleCommand( "menu tremulous_team\n" ); break; + case MN_D_CLASS: trap_SendConsoleCommand( "menu tremulous_alienclass\n" ); break; case MN_H_SPAWN: trap_SendConsoleCommand( "menu hsitemmenu\n" ); break; case MN_D_BUILD: trap_SendConsoleCommand( "menu dbuildmenu\n" ); break; case MN_H_BUILD: trap_SendConsoleCommand( "menu hbuildmenu\n" ); break; diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c index 4236cafd..ff419cb9 100644 --- a/src/cgame/cg_servercmds.c +++ b/src/cgame/cg_servercmds.c @@ -30,7 +30,7 @@ #include "cg_local.h" -#include "../ta_ui/menudef.h" +#include "../../ui/menudef.h" typedef struct { const char *order; diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index f2f7ff4d..f77fd7e1 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -27,7 +27,7 @@ #include "g_local.h" -#include "../ta_ui/menudef.h" // for the voice chats +#include "../../ui/menudef.h" // for the voice chats /* ================== @@ -623,11 +623,11 @@ void Cmd_Team_f( gentity_t *ent ) { return; } - if(!Q_stricmp(s, "0")) + if( !Q_stricmp( s, "0" ) || !Q_stricmp( s, "spectate" ) ) ent->client->pers.pteam = PTE_NONE; - else if(!Q_stricmp(s, "1")) + else if( !Q_stricmp( s, "1" ) || !Q_stricmp( s, "aliens" ) ) ent->client->pers.pteam = PTE_DROIDS; - else if(!Q_stricmp(s, "2")) + else if( !Q_stricmp( s, "2" ) || !Q_stricmp( s, "humans" ) ) ent->client->pers.pteam = PTE_HUMANS; if( oldTeam != ent->client->pers.pteam ) diff --git a/src/ui/keycodes.h b/src/ui/keycodes.h new file mode 100644 index 00000000..7a2a695b --- /dev/null +++ b/src/ui/keycodes.h @@ -0,0 +1,143 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +#ifndef __KEYCODES_H__ +#define __KEYCODES_H__ + +// +// these are the key numbers that should be passed to KeyEvent +// + +// normal keys should be passed as lowercased ascii + +typedef enum { + K_TAB = 9, + K_ENTER = 13, + K_ESCAPE = 27, + K_SPACE = 32, + + K_BACKSPACE = 127, + + K_COMMAND = 128, + K_CAPSLOCK, + K_POWER, + K_PAUSE, + + K_UPARROW, + K_DOWNARROW, + K_LEFTARROW, + K_RIGHTARROW, + + K_ALT, + K_CTRL, + K_SHIFT, + K_INS, + K_DEL, + K_PGDN, + K_PGUP, + K_HOME, + K_END, + + K_F1, + K_F2, + K_F3, + K_F4, + K_F5, + K_F6, + K_F7, + K_F8, + K_F9, + K_F10, + K_F11, + K_F12, + K_F13, + K_F14, + K_F15, + + K_KP_HOME, + K_KP_UPARROW, + K_KP_PGUP, + K_KP_LEFTARROW, + K_KP_5, + K_KP_RIGHTARROW, + K_KP_END, + K_KP_DOWNARROW, + K_KP_PGDN, + K_KP_ENTER, + K_KP_INS, + K_KP_DEL, + K_KP_SLASH, + K_KP_MINUS, + K_KP_PLUS, + K_KP_NUMLOCK, + K_KP_STAR, + K_KP_EQUALS, + + K_MOUSE1, + K_MOUSE2, + K_MOUSE3, + K_MOUSE4, + K_MOUSE5, + + K_MWHEELDOWN, + K_MWHEELUP, + + K_JOY1, + K_JOY2, + K_JOY3, + K_JOY4, + K_JOY5, + K_JOY6, + K_JOY7, + K_JOY8, + K_JOY9, + K_JOY10, + K_JOY11, + K_JOY12, + K_JOY13, + K_JOY14, + K_JOY15, + K_JOY16, + K_JOY17, + K_JOY18, + K_JOY19, + K_JOY20, + K_JOY21, + K_JOY22, + K_JOY23, + K_JOY24, + K_JOY25, + K_JOY26, + K_JOY27, + K_JOY28, + K_JOY29, + K_JOY30, + K_JOY31, + K_JOY32, + + K_AUX1, + K_AUX2, + K_AUX3, + K_AUX4, + K_AUX5, + K_AUX6, + K_AUX7, + K_AUX8, + K_AUX9, + K_AUX10, + K_AUX11, + K_AUX12, + K_AUX13, + K_AUX14, + K_AUX15, + K_AUX16, + + K_LAST_KEY // this had better be <256! +} keyNum_t; + + +// The menu code needs to get both key and char events, but +// to avoid duplicating the paths, the char events are just +// distinguished by or'ing in K_CHAR_FLAG (ugly) +#define K_CHAR_FLAG 1024 + +#endif diff --git a/src/ui/ui_atoms.c b/src/ui/ui_atoms.c new file mode 100644 index 00000000..cac94738 --- /dev/null +++ b/src/ui/ui_atoms.c @@ -0,0 +1,513 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +/********************************************************************** + 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 + +// these are here so the functions in q_shared.c can link +#ifndef UI_HARD_LINKED + +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) ); +} + +#endif + +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" ); +} + + +#ifndef MISSIONPACK // bk001206 +static void NeedCDAction( qboolean result ) { + if ( !result ) { + trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); + } +} +#endif // MISSIONPACK + +#ifndef MISSIONPACK // bk001206 +static void NeedCDKeyAction( qboolean result ) { + if ( !result ) { + trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); + } +} +#endif // MISSIONPACK + +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() { + 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() { + Display_CacheAll(); +} + +/* +======================= +UI_CalcPostGameStats +======================= +*/ +static void UI_CalcPostGameStats() { + 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 ) + { + strcpy( arg1, UI_Argv( 1 ) ); + + if( Menu_Count() > 0 ) + { + trap_Key_SetCatcher( KEYCATCH_UI ); + Menus_ActivateByName( arg1 ); + return qtrue; + } + } + + if ( Q_stricmp (cmd, "ui_cdkey") == 0 ) { + //UI_CDKeyMenu_f(); + 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 00000000..91dc800b --- /dev/null +++ b/src/ui/ui_gameinfo.c @@ -0,0 +1,304 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +// +// 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]; + +#ifndef MISSIONPACK // bk001206 +static int ui_numSinglePlayerArenas; +static int ui_numSpecialSinglePlayerArenas; +#endif + +/* +=============== +UI_ParseInfos +=============== +*/ +int UI_ParseInfos( char *buf, int max, char *infos[] ) { + char *token; + int count; + char key[MAX_TOKEN_CHARS]; + char info[MAX_INFO_STRING]; + + count = 0; + + while ( 1 ) { + token = COM_Parse( &buf ); + if ( !token[0] ) { + break; + } + if ( strcmp( token, "{" ) ) { + Com_Printf( "Missing { in info file\n" ); + break; + } + + if ( count == max ) { + Com_Printf( "Max infos exceeded\n" ); + break; + } + + info[0] = '\0'; + while ( 1 ) { + token = COM_ParseExt( &buf, qtrue ); + if ( !token[0] ) { + Com_Printf( "Unexpected end of info file\n" ); + break; + } + if ( !strcmp( token, "}" ) ) { + break; + } + Q_strncpyz( key, token, sizeof( key ) ); + + token = COM_ParseExt( &buf, qfalse ); + if ( !token[0] ) { + strcpy( token, "<NULL>" ); + } + Info_SetValueForKey( info, key, token ); + } + //NOTE: extra space for arena number + infos[count] = UI_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1); + if (infos[count]) { + strcpy(infos[count], info); + count++; + } + } + return count; +} + +/* +=============== +UI_LoadArenasFromFile +=============== +*/ +static void UI_LoadArenasFromFile( char *filename ) { + int len; + fileHandle_t f; + char buf[MAX_ARENAS_TEXT]; + + len = trap_FS_FOpenFile( filename, &f, FS_READ ); + if ( !f ) { + trap_Print( va( S_COLOR_RED "file not found: %s\n", filename ) ); + return; + } + if ( len >= MAX_ARENAS_TEXT ) { + trap_Print( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) ); + trap_FS_FCloseFile( f ); + return; + } + + trap_FS_Read( buf, len, f ); + buf[len] = 0; + trap_FS_FCloseFile( f ); + + ui_numArenas += UI_ParseInfos( buf, MAX_ARENAS - ui_numArenas, &ui_arenaInfos[ui_numArenas] ); +} + +/* +=============== +UI_LoadArenas +=============== +*/ +void UI_LoadArenas( void ) { + int numdirs; + vmCvar_t arenasFile; + char filename[128]; + char dirlist[1024]; + char* dirptr; + int i, n; + int dirlen; + char *type; + + ui_numArenas = 0; + uiInfo.mapCount = 0; + + trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); + if( *arenasFile.string ) { + UI_LoadArenasFromFile(arenasFile.string); + } + else { + UI_LoadArenasFromFile("scripts/arenas.txt"); + } + + // get all arenas from .arena files + numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 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( "%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 + + 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.mapList[uiInfo.mapCount].typeBits = 0; + + type = Info_ValueForKey( ui_arenaInfos[n], "type" ); + // if no type specified, it will be treated as "ffa" + if( *type ) { + if( strstr( type, "ffa" ) ) { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA); + } + if( strstr( type, "tourney" ) ) { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_TOURNAMENT); + } + if( strstr( type, "ctf" ) ) { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_CTF); + } + if( strstr( type, "oneflag" ) ) { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_1FCTF); + } + if( strstr( type, "overload" ) ) { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_OBELISK); + } + if( strstr( type, "harvester" ) ) { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_HARVESTER); + } + } else { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << GT_FFA); + } + + uiInfo.mapCount++; + if (uiInfo.mapCount >= MAX_MAPS) { + break; + } + } +} + + +/* +=============== +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"; +} diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c new file mode 100644 index 00000000..0785fe20 --- /dev/null +++ b/src/ui/ui_main.c @@ -0,0 +1,5929 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +/* +======================================================================= + +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[] = { + "Local", + "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 +}; + +#ifndef MISSIONPACK // bk001206 +static char quake3worldMessage[] = "Visit www.quake3world.com - News, Community, Events, Files"; +#endif + +static int gamecodetoui[] = {4,2,3,0,5,1,6}; +static int uitogamecode[] = {4,6,2,3,1,5,7}; + + +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 void UI_ParseGameInfo(const char *teamFile); +static void UI_ParseTeamInfo(const char *teamFile); +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 ); +int 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; + case UI_HASUNIQUECDKEY: // mod authors need to observe this + return qtrue; // bk010117 - change this to qfalse for mods! + + } + + return -1; +} + + + +void AssetCache() { + int n; + //if (Assets.textFont == NULL) { + //} + //Assets.background = trap_R_RegisterShaderNoMip( ASSET_BACKGROUND ); + //Com_Printf("Menu Size: %i bytes\n", sizeof(Menus)); + uiInfo.uiDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR ); + uiInfo.uiDC.Assets.fxBasePic = trap_R_RegisterShaderNoMip( ART_FX_BASE ); + uiInfo.uiDC.Assets.fxPic[0] = trap_R_RegisterShaderNoMip( ART_FX_RED ); + uiInfo.uiDC.Assets.fxPic[1] = trap_R_RegisterShaderNoMip( ART_FX_YELLOW ); + uiInfo.uiDC.Assets.fxPic[2] = trap_R_RegisterShaderNoMip( ART_FX_GREEN ); + uiInfo.uiDC.Assets.fxPic[3] = trap_R_RegisterShaderNoMip( ART_FX_TEAL ); + uiInfo.uiDC.Assets.fxPic[4] = trap_R_RegisterShaderNoMip( ART_FX_BLUE ); + uiInfo.uiDC.Assets.fxPic[5] = trap_R_RegisterShaderNoMip( ART_FX_CYAN ); + uiInfo.uiDC.Assets.fxPic[6] = trap_R_RegisterShaderNoMip( ART_FX_WHITE ); + 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 ); + + for( n = 0; n < NUM_CROSSHAIRS; n++ ) { + uiInfo.uiDC.Assets.crosshairShader[n] = trap_R_RegisterShaderNoMip( va("gfx/2d/crosshair%c", 'a' + n ) ); + } + + uiInfo.newHighScoreSound = trap_S_RegisterSound("sound/feedback/voc_newhighscore.wav", qfalse); +} + +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; +// TTimo: FIXME: use const unsigned char to avoid getting a warning in linux debug (.so) when using glyph = &font->glyphs[*s]; +// but use const char to build with lcc.. +// const unsigned char *s = text; // bk001206 - unsigned + 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]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + 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; +// TTimo: FIXME +// const unsigned char *s = text; // bk001206 - unsigned + 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]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + 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) { +// TTimo: FIXME +// const unsigned char *s = text; // bk001206 - unsigned + 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]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + //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; + } + 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) { +// TTimo: FIXME +// const unsigned char *s = text; // bk001206 - unsigned + 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]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + //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 { + 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 ); + } + 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) { +// TTimo: FIXME +// const unsigned char *s = text; // bk001206 - unsigned + 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]; // TTimo: FIXME: getting nasty warnings without the cast, hopefully this doesn't break the VM build + 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 ); + if (Menu_Count() > 0) { + 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_PC_ReadToken(handle, &token)) + return qfalse; + if (Q_stricmp(token.string, "{") != 0) { + return qfalse; + } + + while ( 1 ) { + + memset(&token, 0, sizeof(pc_token_t)); + + if (!trap_PC_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() { + 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() { + 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_PC_LoadSource(menuFile); + if (!handle) { + return; + } + + while ( 1 ) { + memset(&token, 0, sizeof(pc_token_t)); + if (!trap_PC_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_PC_FreeSource(handle); +} + +qboolean Load_Menu(int handle) { + pc_token_t token; + + if (!trap_PC_ReadToken(handle, &token)) + return qfalse; + if (token.string[0] != '{') { + return qfalse; + } + + while ( 1 ) { + + if (!trap_PC_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_PC_LoadSource( menuFile ); + if (!handle) { + trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) ); + handle = trap_PC_LoadSource( "ui/menus.txt" ); + if (!handle) { + trap_Error( va( S_COLOR_RED "default menu file not found: ui/menus.txt, unable to continue!\n", menuFile ) ); + } + } + + ui_new.integer = 1; + + if (reset) { + Menu_Reset(); + } + + while ( 1 ) { + if (!trap_PC_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_PC_FreeSource( handle ); +} + +void UI_Load() { + 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(); + +#ifdef PRE_RELEASE_TADEMO + UI_ParseGameInfo("demogameinfo.txt"); +#else + UI_ParseGameInfo("gameinfo.txt"); + UI_LoadArenas(); +#endif + + 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}; +#ifndef MISSIONPACK // bk001206 +static int numHandicaps = sizeof(handicapValues) / sizeof(const char*); +#endif + +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 (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_OBELISK) { + cap = 4; + } else if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_HARVESTER) { + cap = 15; + } + 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; + } + } + +} + + + +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 (ui_actualNetGameType.integer >= GT_TEAM) { + if (value >= uiInfo.characterCount) { + value = 0; + } + text = uiInfo.characterList[value].name; + } else { + 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_DrawEffects(rectDef_t *rect, float scale, vec4_t color) { + UI_DrawHandlePic( rect->x, rect->y - 14, 128, 8, uiInfo.uiDC.Assets.fxBasePic ); + UI_DrawHandlePic( rect->x + uiInfo.effectsColor * 16 + 8, rect->y - 16, 16, 12, uiInfo.uiDC.Assets.fxPic[uiInfo.effectsColor] ); +} + +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("menu/art/unknownmap")); + } +} + + +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 > uiInfo.numGameTypes) { + 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("menu/art/unknownmap")); + } +} + +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); +} + + +#ifndef MISSIONPACK // bk001206 +static const char *UI_OpponentLeaderName() { + int i = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); + return uiInfo.teamList[i].teamMembers[0]; +} +#endif + +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"; +} + +#ifndef MISSIONPACK // bk001206 +static const int UI_AIIndex(const char *name) { + int j; + for (j = 0; j < uiInfo.characterCount; j++) { + if (Q_stricmp(name, uiInfo.characterList[j].name) == 0) { + return j; + } + } + return 0; +} +#endif + +#ifndef MISSIONPACK // bk001206 +static const int UI_AIIndexFromName(const char *name) { + int j; + for (j = 0; j < uiInfo.aliasCount; j++) { + if (Q_stricmp(uiInfo.aliasList[j].name, name) == 0) { + return UI_AIIndex(uiInfo.aliasList[j].ai); + } + } + return 0; +} +#endif + + +#ifndef MISSIONPACK // bk001206 +static const char *UI_OpponentLeaderHead() { + const char *leader = UI_OpponentLeaderName(); + return UI_AIFromName(leader); +} +#endif + +#ifndef MISSIONPACK // bk001206 +static const char *UI_OpponentLeaderModel() { + int i; + const char *head = UI_OpponentLeaderHead(); + for (i = 0; i < uiInfo.characterCount; i++) { + if (Q_stricmp(head, uiInfo.characterList[i].name) == 0) { + return uiInfo.characterList[i].base; + } + } + return "James"; +} +#endif + + +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() { + 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() { + 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_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_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; + int game = trap_Cvar_VariableValue("g_gametype"); + const char *text = ""; + if (game >= GT_TEAM) { + if (value >= uiInfo.characterCount) { + value = 0; + } + text = uiInfo.characterList[value].name; + } else { + 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); +} + +static void UI_DrawCrosshair(rectDef_t *rect, float scale, vec4_t color) { + trap_R_SetColor( color ); + if (uiInfo.currentCrosshair < 0 || uiInfo.currentCrosshair >= NUM_CROSSHAIRS) { + uiInfo.currentCrosshair = 0; + } + UI_DrawHandlePic( rect->x, rect->y - rect->h, rect->w, rect->h, uiInfo.uiDC.Assets.crosshairShader[uiInfo.currentCrosshair]); + trap_R_SetColor( NULL ); +} + +/* +=============== +UI_BuildPlayerList +=============== +*/ +static void UI_BuildPlayerList() { + 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; + playerTeamNumber = 0; + for( n = 0; n < count; n++ ) { + trap_GetConfigString( CS_PLAYERS + n, info, MAX_INFO_STRING ); + + if (info[0]) { + Q_strncpyz( uiInfo.playerNames[uiInfo.playerCount], Info_ValueForKey( info, "n" ), MAX_NAME_LENGTH ); + Q_CleanStr( uiInfo.playerNames[uiInfo.playerCount] ); + uiInfo.playerCount++; + team2 = atoi(Info_ValueForKey(info, "t")); + if (team2 == team) { + 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) { + if (uiInfo.uiDC.realTime > uiInfo.playerRefresh) { + uiInfo.playerRefresh = uiInfo.uiDC.realTime + 3000; + UI_BuildPlayerList(); + } + Text_Paint(rect->x, rect->y, scale, color, (uiInfo.teamLeader) ? UI_Cvar_VariableString("cg_selectedPlayerName") : UI_Cvar_VariableString("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[4096]; + 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 + Q_strncpyz(buff, uiInfo.uiDC.glconfig.extensions_string, 4096); + 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; + + rect.x = x + text_x; + rect.y = y + text_y; + rect.w = w; + rect.h = h; + + switch (ownerDraw) { + case UI_HANDICAP: + UI_DrawHandicap(&rect, scale, color, textStyle); + break; + case UI_EFFECTS: + UI_DrawEffects(&rect, scale, color); + 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_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_CROSSHAIR: + UI_DrawCrosshair(&rect, scale, color); + 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; + + while (flags) { + + if (flags & UI_SHOW_FFA) { + if (trap_Cvar_VariableValue("g_gametype") != GT_FFA) { + vis = qfalse; + } + flags &= ~UI_SHOW_FFA; + } + + if (flags & UI_SHOW_NOTFFA) { + if (trap_Cvar_VariableValue("g_gametype") == GT_FFA) { + vis = qfalse; + } + flags &= ~UI_SHOW_NOTFFA; + } + + 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_ANYTEAMGAME) { + if (uiInfo.gameTypes[ui_gameType.integer].gtEnum <= GT_TEAM ) { + vis = qfalse; + } + flags &= ~UI_SHOW_ANYTEAMGAME; + } + if (flags & UI_SHOW_ANYNONTEAMGAME) { + if (uiInfo.gameTypes[ui_gameType.integer].gtEnum > GT_TEAM ) { + vis = qfalse; + } + flags &= ~UI_SHOW_ANYNONTEAMGAME; + } + if (flags & UI_SHOW_NETANYTEAMGAME) { + if (uiInfo.gameTypes[ui_netGameType.integer].gtEnum <= GT_TEAM ) { + vis = qfalse; + } + flags &= ~UI_SHOW_NETANYTEAMGAME; + } + if (flags & UI_SHOW_NETANYNONTEAMGAME) { + if (uiInfo.gameTypes[ui_netGameType.integer].gtEnum > GT_TEAM ) { + vis = qfalse; + } + flags &= ~UI_SHOW_NETANYNONTEAMGAME; + } + 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_Effects_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.effectsColor--; + } else { + uiInfo.effectsColor++; + } + + if( uiInfo.effectsColor > 6 ) { + uiInfo.effectsColor = 0; + } else if (uiInfo.effectsColor < 0) { + uiInfo.effectsColor = 6; + } + + trap_Cvar_SetValue( "color1", uitogamecode[uiInfo.effectsColor] ); + 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; + } + } + + if (uiInfo.gameTypes[ui_gameType.integer].gtEnum == GT_TOURNAMENT) { + trap_Cvar_Set("ui_Q3Model", "1"); + } else { + 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 (ui_actualNetGameType.integer >= GT_TEAM) { + if (value >= uiInfo.characterCount + 2) { + value = 0; + } else if (value < 0) { + value = uiInfo.characterCount + 2 - 1; + } + } else { + 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--; + } else { + 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 game = trap_Cvar_VariableValue("g_gametype"); + int value = uiInfo.botIndex; + + if (key == K_MOUSE2) { + value--; + } else { + value++; + } + + if (game >= GT_TEAM) { + if (value >= uiInfo.characterCount + 2) { + value = 0; + } else if (value < 0) { + value = uiInfo.characterCount + 2 - 1; + } + } else { + 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_Crosshair_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.currentCrosshair--; + } else { + uiInfo.currentCrosshair++; + } + + if (uiInfo.currentCrosshair >= NUM_CROSSHAIRS) { + uiInfo.currentCrosshair = 0; + } else if (uiInfo.currentCrosshair < 0) { + uiInfo.currentCrosshair = NUM_CROSSHAIRS - 1; + } + trap_Cvar_Set("cg_drawCrosshair", va("%d", uiInfo.currentCrosshair)); + 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_EFFECTS: + return UI_Effects_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_CROSSHAIR: + UI_Crosshair_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); +} + +/* +static void UI_StartSinglePlayer() { + int i,j, k, skill; + char buff[1024]; + i = trap_Cvar_VariableValue( "ui_currentTier" ); + if (i < 0 || i >= tierCount) { + i = 0; + } + j = trap_Cvar_VariableValue("ui_currentMap"); + if (j < 0 || j > MAPS_PER_TIER) { + j = 0; + } + + trap_Cvar_SetValue( "singleplayer", 1 ); + trap_Cvar_SetValue( "g_gametype", Com_Clamp( 0, 7, tierList[i].gameTypes[j] ) ); + trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait ; wait ; map %s\n", tierList[i].maps[j] ) ); + skill = trap_Cvar_VariableValue( "g_spSkill" ); + + if (j == MAPS_PER_TIER-1) { + k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); + Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[0]), skill, "", teamList[k].teamMembers[0]); + } else { + k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_opponentName")); + for (i = 0; i < PLAYERS_PER_TEAM; i++) { + Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[i]), skill, "Blue", teamList[k].teamMembers[i]); + trap_Cmd_ExecuteText( EXEC_APPEND, buff ); + } + + k = UI_TeamIndexFromName(UI_Cvar_VariableString("ui_teamName")); + for (i = 1; i < PLAYERS_PER_TEAM; i++) { + Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %i %s 250 %s\n", UI_AIFromName(teamList[k].teamMembers[i]), skill, "Red", teamList[k].teamMembers[i]); + trap_Cmd_ExecuteText( EXEC_APPEND, buff ); + } + trap_Cmd_ExecuteText( EXEC_APPEND, "wait 5; team Red\n" ); + } + + +} +*/ + +/* +=============== +UI_LoadMods +=============== +*/ +static void UI_LoadMods() { + 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_LoadTeams +=============== +*/ +static void UI_LoadTeams() { + char teamList[4096]; + char *teamName; + int i, len, count; + + count = trap_FS_GetFileList( "", "team", teamList, 4096 ); + + if (count) { + teamName = teamList; + for ( i = 0; i < count; i++ ) { + len = strlen( teamName ); + UI_ParseTeamInfo(teamName); + teamName += len + 1; + } + } + +} + + +/* +=============== +UI_LoadMovies +=============== +*/ +static void UI_LoadMovies() { + 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() { + 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, 0, 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; + + if (g == GT_TOURNAMENT) { + trap_Cvar_Set("sv_maxClients", "2"); + Com_sprintf( buff, sizeof(buff), "wait ; addbot %s %f "", %i \n", uiInfo.mapList[ui_currentMap.integer].opponentName, skill, delay); + trap_Cmd_ExecuteText( EXEC_APPEND, buff ); + } else { + 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, (g == GT_FFA) ? "" : "Blue", 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, (g == GT_FFA) ? "" : "Red", delay, uiInfo.teamList[k].teamMembers[i]); + trap_Cmd_ExecuteText( EXEC_APPEND, buff ); + delay += 500; + } + } + if (g >= GT_TEAM ) { + trap_Cmd_ExecuteText( EXEC_APPEND, "wait 5; team Red\n" ); + } +} + +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_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 ); + 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_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_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]; + + 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) { + if (ui_actualNetGameType.integer >= GT_TEAM) { + Com_sprintf( buff, sizeof(buff), "addbot %s %f %s\n", uiInfo.characterList[bot-2].name, skill, "Blue"); + } else { + 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) { + if (ui_actualNetGameType.integer >= GT_TEAM) { + Com_sprintf( buff, sizeof(buff), "addbot %s %f %s\n", uiInfo.characterList[bot-2].name, skill, "Red"); + } else { + 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, 0, K_MOUSE1, qfalse); + UI_GameType_HandleKey(0, 0, 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, "getCDKey") == 0) { + char out[17]; + trap_GetCDKey(buff, 17); + trap_Cvar_Set("cdkey1", ""); + trap_Cvar_Set("cdkey2", ""); + trap_Cvar_Set("cdkey3", ""); + trap_Cvar_Set("cdkey4", ""); + if (strlen(buff) == CDKEY_LEN) { + Q_strncpyz(out, buff, 5); + trap_Cvar_Set("cdkey1", out); + Q_strncpyz(out, buff + 4, 5); + trap_Cvar_Set("cdkey2", out); + Q_strncpyz(out, buff + 8, 5); + trap_Cvar_Set("cdkey3", out); + Q_strncpyz(out, buff + 12, 5); + trap_Cvar_Set("cdkey4", out); + } + + } else if (Q_stricmp(name, "verifyCDKey") == 0) { + buff[0] = '\0'; + Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey1")); + Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey2")); + Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey3")); + Q_strcat(buff, 1024, UI_Cvar_VariableString("cdkey4")); + trap_Cvar_Set("cdkey", buff); + if (trap_VerifyCDKey(buff, UI_Cvar_VariableString("cdkeychecksum"))) { + trap_Cvar_Set("ui_cdkeyvalid", "CD Key Appears to be valid."); + trap_SetCDKey(buff); + } else { + trap_Cvar_Set("ui_cdkeyvalid", "CD Key does not appear to be valid."); + } + } else if (Q_stricmp(name, "loadArenas") == 0) { + UI_LoadArenas(); + UI_MapCountByGameType(qfalse); + Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, "createserver"); + } 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) { +#ifdef PRE_RELEASE_TADEMO + UI_ParseGameInfo("demogameinfo.txt"); +#else + UI_ParseGameInfo("gameinfo.txt"); +#endif + 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, "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(); + } 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, "Quake3") == 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); + } + } 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 %s\n",uiInfo.playerNames[uiInfo.playerIndex]) ); + } + } else if (Q_stricmp(name, "voteGame") == 0) { + if (ui_netGameType.integer >= 0 && ui_netGameType.integer < uiInfo.numGameTypes) { + trap_Cmd_ExecuteText( EXEC_APPEND, va("callvote g_gametype %i\n",uiInfo.gameTypes[ui_netGameType.integer].gtEnum) ); + } + } else if (Q_stricmp(name, "voteLeader") == 0) { + if (uiInfo.teamIndex >= 0 && uiInfo.teamIndex < uiInfo.myTeamCount) { + trap_Cmd_ExecuteText( EXEC_APPEND, va("callteamvote leader %s\n",uiInfo.teamNames[uiInfo.teamIndex]) ); + } + } else if (Q_stricmp(name, "addBot") == 0) { + if (trap_Cvar_VariableValue("g_gametype") >= GT_TEAM) { + trap_Cmd_ExecuteText( EXEC_APPEND, va("addbot %s %i %s\n", uiInfo.characterList[uiInfo.botIndex].name, uiInfo.skillIndex+1, (uiInfo.redBlue == 0) ? "Red" : "Blue") ); + } else { + trap_Cmd_ExecuteText( EXEC_APPEND, va("addbot %s %i %s\n", UI_GetBotNameByNumber(uiInfo.botIndex), uiInfo.skillIndex+1, (uiInfo.redBlue == 0) ? "Red" : "Blue") ); + } + } 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 { + 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; + if (game == GT_SINGLE_PLAYER) { + game++; + } + if (game == GT_TEAM) { + game = GT_FFA; + } + + 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 << GT_SINGLE_PLAYER))) { + 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, 0, FS_READ)) { + return qtrue; + } + Com_sprintf( test, sizeof( test ), "models/players/characters/%s/%s/lower_default.skin", base, team ); + + if (trap_FS_FOpenFile(test, 0, FS_READ)) { + return qtrue; + } + return qfalse; +} + +/* +================== +UI_MapCountByTeam +================== +*/ +static int UI_HeadCountByTeam() { + 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 == 0) { + strcpy(uiInfo.serverStatus.motd, "Welcome to Team Arena!"); + 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; + } + } + + if (ui_serverFilterType.integer > 0) { + if (Q_stricmp(Info_ValueForKey(info, "game"), serverFilters[ui_serverFilterType.integer].basedir) != 0) { + 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_MODS) { + return uiInfo.modCount; + } else if (feederID == FEEDER_DEMOS) { + return uiInfo.demoCount; + } + 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() { + 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, game; + 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 { + if (atoi(Info_ValueForKey(info, "sv_allowAnonymous")) != 0) { // anonymous server + Com_sprintf( hostname, sizeof(hostname), "(A) %s", + Info_ValueForKey(info, "hostname")); + } else { + Com_sprintf( hostname, sizeof(hostname), "%s", + Info_ValueForKey(info, "hostname")); + } + return hostname; + } + } + 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_GAME : + game = atoi(Info_ValueForKey(info, "gametype")); + if (game >= 0 && game < numTeamArenaGameTypes) { + return teamArenaGameTypes[game]; + } else { + return "Unknown"; + } + 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_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]; + } + } + 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_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; + } +} + +static qboolean Team_Parse(char **p) { + char *token; + const char *tempStr; + int i; + + token = COM_ParseExt(p, qtrue); + + if (token[0] != '{') { + return qfalse; + } + + while ( 1 ) { + + token = COM_ParseExt(p, qtrue); + + if (Q_stricmp(token, "}") == 0) { + return qtrue; + } + + if ( !token || token[0] == 0 ) { + return qfalse; + } + + if (token[0] == '{') { + // seven tokens per line, team name and icon, and 5 team member names + if (!String_Parse(p, &uiInfo.teamList[uiInfo.teamCount].teamName) || !String_Parse(p, &tempStr)) { + return qfalse; + } + + + uiInfo.teamList[uiInfo.teamCount].imageName = tempStr; + uiInfo.teamList[uiInfo.teamCount].teamIcon = trap_R_RegisterShaderNoMip(uiInfo.teamList[uiInfo.teamCount].imageName); + uiInfo.teamList[uiInfo.teamCount].teamIcon_Metal = trap_R_RegisterShaderNoMip(va("%s_metal",uiInfo.teamList[uiInfo.teamCount].imageName)); + uiInfo.teamList[uiInfo.teamCount].teamIcon_Name = trap_R_RegisterShaderNoMip(va("%s_name", uiInfo.teamList[uiInfo.teamCount].imageName)); + + uiInfo.teamList[uiInfo.teamCount].cinematic = -1; + + for (i = 0; i < TEAM_MEMBERS; i++) { + uiInfo.teamList[uiInfo.teamCount].teamMembers[i] = NULL; + if (!String_Parse(p, &uiInfo.teamList[uiInfo.teamCount].teamMembers[i])) { + return qfalse; + } + } + + Com_Printf("Loaded team %s with team icon %s.\n", uiInfo.teamList[uiInfo.teamCount].teamName, tempStr); + if (uiInfo.teamCount < MAX_TEAMS) { + uiInfo.teamCount++; + } else { + Com_Printf("Too many teams, last team replaced!\n"); + } + token = COM_ParseExt(p, qtrue); + if (token[0] != '}') { + return qfalse; + } + } + } + + return qfalse; +} + +static qboolean Character_Parse(char **p) { + char *token; + const char *tempStr; + + token = COM_ParseExt(p, qtrue); + + if (token[0] != '{') { + return qfalse; + } + + + while ( 1 ) { + token = COM_ParseExt(p, qtrue); + + if (Q_stricmp(token, "}") == 0) { + return qtrue; + } + + if ( !token || token[0] == 0 ) { + return qfalse; + } + + if (token[0] == '{') { + // two tokens per line, character name and sex + if (!String_Parse(p, &uiInfo.characterList[uiInfo.characterCount].name) || !String_Parse(p, &tempStr)) { + return qfalse; + } + + uiInfo.characterList[uiInfo.characterCount].headImage = -1; + uiInfo.characterList[uiInfo.characterCount].imageName = String_Alloc(va("models/players/heads/%s/icon_default.tga", uiInfo.characterList[uiInfo.characterCount].name)); + + if (tempStr && (!Q_stricmp(tempStr, "female"))) { + uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("Janet")); + } else if (tempStr && (!Q_stricmp(tempStr, "male"))) { + uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("James")); + } else { + uiInfo.characterList[uiInfo.characterCount].base = String_Alloc(va("%s",tempStr)); + } + + Com_Printf("Loaded %s character %s.\n", uiInfo.characterList[uiInfo.characterCount].base, uiInfo.characterList[uiInfo.characterCount].name); + if (uiInfo.characterCount < MAX_HEADS) { + uiInfo.characterCount++; + } else { + Com_Printf("Too many characters, last character replaced!\n"); + } + + token = COM_ParseExt(p, qtrue); + if (token[0] != '}') { + return qfalse; + } + } + } + + return qfalse; +} + + +static qboolean Alias_Parse(char **p) { + char *token; + + token = COM_ParseExt(p, qtrue); + + if (token[0] != '{') { + return qfalse; + } + + while ( 1 ) { + token = COM_ParseExt(p, qtrue); + + if (Q_stricmp(token, "}") == 0) { + return qtrue; + } + + if ( !token || token[0] == 0 ) { + return qfalse; + } + + if (token[0] == '{') { + // three tokens per line, character name, bot alias, and preferred action a - all purpose, d - defense, o - offense + if (!String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].name) || !String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].ai) || !String_Parse(p, &uiInfo.aliasList[uiInfo.aliasCount].action)) { + return qfalse; + } + + Com_Printf("Loaded character alias %s using character ai %s.\n", uiInfo.aliasList[uiInfo.aliasCount].name, uiInfo.aliasList[uiInfo.aliasCount].ai); + if (uiInfo.aliasCount < MAX_ALIASES) { + uiInfo.aliasCount++; + } else { + Com_Printf("Too many aliases, last alias replaced!\n"); + } + + token = COM_ParseExt(p, qtrue); + if (token[0] != '}') { + return qfalse; + } + } + } + + return qfalse; +} + + + +// mode +// 0 - high level parsing +// 1 - team parsing +// 2 - character parsing +static void UI_ParseTeamInfo(const char *teamFile) { + char *token; + char *p; + char *buff = NULL; + //static int mode = 0; TTimo: unused + + buff = GetMenuBuffer(teamFile); + if (!buff) { + return; + } + + p = buff; + + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if( !token || token[0] == 0 || token[0] == '}') { + break; + } + + if ( Q_stricmp( token, "}" ) == 0 ) { + break; + } + + if (Q_stricmp(token, "teams") == 0) { + + if (Team_Parse(&p)) { + continue; + } else { + break; + } + } + + if (Q_stricmp(token, "characters") == 0) { + Character_Parse(&p); + } + + if (Q_stricmp(token, "aliases") == 0) { + Alias_Parse(&p); + } + + } + +} + + +static qboolean GameType_Parse(char **p, qboolean join) { + char *token; + + token = COM_ParseExt(p, qtrue); + + if (token[0] != '{') { + return qfalse; + } + + if (join) { + uiInfo.numJoinGameTypes = 0; + } else { + uiInfo.numGameTypes = 0; + } + + while ( 1 ) { + token = COM_ParseExt(p, qtrue); + + if (Q_stricmp(token, "}") == 0) { + return qtrue; + } + + if ( !token || token[0] == 0 ) { + return qfalse; + } + + if (token[0] == '{') { + // two tokens per line, character name and sex + if (join) { + if (!String_Parse(p, &uiInfo.joinGameTypes[uiInfo.numJoinGameTypes].gameType) || !Int_Parse(p, &uiInfo.joinGameTypes[uiInfo.numJoinGameTypes].gtEnum)) { + return qfalse; + } + } else { + if (!String_Parse(p, &uiInfo.gameTypes[uiInfo.numGameTypes].gameType) || !Int_Parse(p, &uiInfo.gameTypes[uiInfo.numGameTypes].gtEnum)) { + return qfalse; + } + } + + if (join) { + if (uiInfo.numJoinGameTypes < MAX_GAMETYPES) { + uiInfo.numJoinGameTypes++; + } else { + Com_Printf("Too many net game types, last one replace!\n"); + } + } else { + if (uiInfo.numGameTypes < MAX_GAMETYPES) { + uiInfo.numGameTypes++; + } else { + Com_Printf("Too many game types, last one replace!\n"); + } + } + + token = COM_ParseExt(p, qtrue); + if (token[0] != '}') { + return qfalse; + } + } + } + return qfalse; +} + +static qboolean MapList_Parse(char **p) { + char *token; + + token = COM_ParseExt(p, qtrue); + + if (token[0] != '{') { + return qfalse; + } + + uiInfo.mapCount = 0; + + while ( 1 ) { + token = COM_ParseExt(p, qtrue); + + if (Q_stricmp(token, "}") == 0) { + return qtrue; + } + + if ( !token || token[0] == 0 ) { + return qfalse; + } + + if (token[0] == '{') { + if (!String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].mapName) || !String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].mapLoadName) + ||!Int_Parse(p, &uiInfo.mapList[uiInfo.mapCount].teamMembers) ) { + return qfalse; + } + + if (!String_Parse(p, &uiInfo.mapList[uiInfo.mapCount].opponentName)) { + return qfalse; + } + + uiInfo.mapList[uiInfo.mapCount].typeBits = 0; + + while (1) { + token = COM_ParseExt(p, qtrue); + if (token[0] >= '0' && token[0] <= '9') { + uiInfo.mapList[uiInfo.mapCount].typeBits |= (1 << (token[0] - 0x030)); + if (!Int_Parse(p, &uiInfo.mapList[uiInfo.mapCount].timeToBeat[token[0] - 0x30])) { + return qfalse; + } + } else { + break; + } + } + + //mapList[mapCount].imageName = String_Alloc(va("levelshots/%s", mapList[mapCount].mapLoadName)); + //if (uiInfo.mapCount == 0) { + // only load the first cinematic, selection loads the others + // uiInfo.mapList[uiInfo.mapCount].cinematic = trap_CIN_PlayCinematic(va("%s.roq",uiInfo.mapList[uiInfo.mapCount].mapLoadName), qfalse, qfalse, qtrue, 0, 0, 0, 0); + //} + uiInfo.mapList[uiInfo.mapCount].cinematic = -1; + uiInfo.mapList[uiInfo.mapCount].levelShot = trap_R_RegisterShaderNoMip(va("levelshots/%s_small", uiInfo.mapList[uiInfo.mapCount].mapLoadName)); + + if (uiInfo.mapCount < MAX_MAPS) { + uiInfo.mapCount++; + } else { + Com_Printf("Too many maps, last one replaced!\n"); + } + } + } + return qfalse; +} + +static void UI_ParseGameInfo(const char *teamFile) { + char *token; + char *p; + char *buff = NULL; + //int mode = 0; TTimo: unused + + buff = GetMenuBuffer(teamFile); + if (!buff) { + return; + } + + p = buff; + + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if( !token || token[0] == 0 || token[0] == '}') { + break; + } + + if ( Q_stricmp( token, "}" ) == 0 ) { + break; + } + + if (Q_stricmp(token, "gametypes") == 0) { + + if (GameType_Parse(&p, qfalse)) { + continue; + } else { + break; + } + } + + if (Q_stricmp(token, "joingametypes") == 0) { + + if (GameType_Parse(&p, qtrue)) { + continue; + } else { + break; + } + } + + if (Q_stricmp(token, "maps") == 0) { + // start a new menu + MapList_Parse(&p); + } + + } +} + +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" ); + } +} + +#ifndef MISSIONPACK // bk001206 +static int UI_OwnerDraw_Width(int ownerDraw) { + // bk001205 - LCC missing return value + return 0; +} +#endif + +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); +} + + + +/* +================= +PlayerModel_BuildList +================= +*/ +static void UI_BuildQ3Model_List( void ) +{ + int numdirs; + int numfiles; + char dirlist[2048]; + char filelist[2048]; + char skinname[64]; + char scratch[256]; + char* dirptr; + char* fileptr; + int i; + int j, k, dirty; + int dirlen; + int filelen; + + uiInfo.q3HeadCount = 0; + + // iterate directory of all player models + numdirs = trap_FS_GetFileList("models/players", "/", dirlist, 2048 ); + dirptr = dirlist; + for (i=0; i<numdirs && uiInfo.q3HeadCount < MAX_PLAYERMODELS; i++,dirptr+=dirlen+1) + { + dirlen = strlen(dirptr); + + if (dirlen && dirptr[dirlen-1]=='/') dirptr[dirlen-1]='\0'; + + if (!strcmp(dirptr,".") || !strcmp(dirptr,"..")) + continue; + + // iterate all skin files in directory + numfiles = trap_FS_GetFileList( va("models/players/%s",dirptr), "tga", filelist, 2048 ); + fileptr = filelist; + for (j=0; j<numfiles && uiInfo.q3HeadCount < MAX_PLAYERMODELS;j++,fileptr+=filelen+1) + { + filelen = strlen(fileptr); + + COM_StripExtension(fileptr,skinname); + + // look for icon_???? + if (Q_stricmpn(skinname, "icon_", 5) == 0 && !(Q_stricmp(skinname,"icon_blue") == 0 || Q_stricmp(skinname,"icon_red") == 0)) + { + if (Q_stricmp(skinname, "icon_default") == 0) { + Com_sprintf( scratch, sizeof(scratch), dirptr); + } else { + Com_sprintf( scratch, sizeof(scratch), "%s/%s",dirptr, skinname + 5); + } + dirty = 0; + for(k=0;k<uiInfo.q3HeadCount;k++) { + if (!Q_stricmp(scratch, uiInfo.q3HeadNames[uiInfo.q3HeadCount])) { + dirty = 1; + break; + } + } + if (!dirty) { + Com_sprintf( uiInfo.q3HeadNames[uiInfo.q3HeadCount], sizeof(uiInfo.q3HeadNames[uiInfo.q3HeadCount]), scratch); + uiInfo.q3HeadIcons[uiInfo.q3HeadCount++] = trap_R_RegisterShaderNoMip(va("models/players/%s/%s",dirptr,skinname)); + } + } + + } + } + +} + + + +/* +================= +UI_Init +================= +*/ +void _UI_Init( qboolean inGameLoad ) { + const char *menuSet; + int start; + + //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.cursor = trap_R_RegisterShaderNoMip( "menu/art/3_cursor2" ); + uiInfo.uiDC.whiteShader = trap_R_RegisterShaderNoMip( "white" ); + + AssetCache(); + + start = trap_Milliseconds(); + + uiInfo.teamCount = 0; + uiInfo.characterCount = 0; + uiInfo.aliasCount = 0; + +#ifdef PRE_RELEASE_TADEMO + UI_ParseTeamInfo("demoteaminfo.txt"); + UI_ParseGameInfo("demogameinfo.txt"); +#else + UI_ParseTeamInfo("teaminfo.txt"); + UI_LoadTeams(); + UI_ParseGameInfo("gameinfo.txt"); +#endif + + 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); +#endif + + Menus_CloseAll(); + + trap_LAN_LoadCachedServers(); + UI_LoadBestScores(uiInfo.mapList[ui_currentMap.integer].mapLoadName, uiInfo.gameTypes[ui_gameType.integer].gtEnum); + + UI_BuildQ3Model_List(); + UI_LoadBots(); + + // 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() { + 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_VariableStringBuffer("com_errorMessage", buf, sizeof(buf)); + if (strlen(buf)) { + if (!ui_singlePlayerActive.integer) { + 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_NEED_CD: + // no cd check in TA + //trap_Key_SetCatcher( KEYCATCH_UI ); + //Menus_ActivateByName("needcd"); + //UI_ConfirmMenu( "Insert the CD", NULL, NeedCDAction ); + return; + case UIMENU_BAD_CD_KEY: + // no cd check in TA + //trap_Key_SetCatcher( KEYCATCH_UI ); + //Menus_ActivateByName("badcd"); + //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); + 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"); + //UI_ConfirmMenu( "Bad CD Key", NULL, NeedCDKeyAction ); + 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); +} + + +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); + } + + //UI_DrawProportionalString( 320, 96, "Press Esc to abort", UI_CENTER|UI_SMALLFONT|UI_DROPSHADOW, menu_text_color ); + + // display global MOTD at bottom + Text_PaintCenter(centerPoint, 600, scale, colorWhite, Info_ValueForKey( cstate.updateInfoString, "motd" ), 0); + // print any server info (server full, bad version, etc) + if ( cstate.connState < CA_CONNECTED ) { + Text_PaintCenter(centerPoint, yStart + 176, scale, colorWhite, cstate.messageString, 0); + } + + if ( lastConnState > cstate.connState ) { + lastLoadingText[0] = '\0'; + } + lastConnState = cstate.connState; + + switch ( cstate.connState ) { + case CA_CONNECTING: + s = va("Awaiting connection...%i", cstate.connectPacketCount); + break; + case CA_CHALLENGING: + s = va("Awaiting challenge...%i", cstate.connectPacketCount); + break; + case CA_CONNECTED: { + char downloadName[MAX_INFO_VALUE]; + + 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_cdkeychecked; + +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_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; + + +// 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_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE }, + { &ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE }, + { &ui_browserSortKey, "ui_browserSortKey", "4", CVAR_ARCHIVE }, + { &ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE }, + { &ui_browserShowEmpty, "ui_browserShowEmpty", "1", CVAR_ARCHIVE }, + + { &ui_brassTime, "cg_brassTime", "2500", CVAR_ARCHIVE }, + { &ui_drawCrosshair, "cg_drawCrosshair", "4", CVAR_ARCHIVE }, + { &ui_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE }, + { &ui_marks, "cg_marks", "1", CVAR_ARCHIVE }, + + { &ui_server1, "server1", "", CVAR_ARCHIVE }, + { &ui_server2, "server2", "", CVAR_ARCHIVE }, + { &ui_server3, "server3", "", CVAR_ARCHIVE }, + { &ui_server4, "server4", "", CVAR_ARCHIVE }, + { &ui_server5, "server5", "", CVAR_ARCHIVE }, + { &ui_server6, "server6", "", CVAR_ARCHIVE }, + { &ui_server7, "server7", "", CVAR_ARCHIVE }, + { &ui_server8, "server8", "", CVAR_ARCHIVE }, + { &ui_server9, "server9", "", CVAR_ARCHIVE }, + { &ui_server10, "server10", "", CVAR_ARCHIVE }, + { &ui_server11, "server11", "", CVAR_ARCHIVE }, + { &ui_server12, "server12", "", CVAR_ARCHIVE }, + { &ui_server13, "server13", "", CVAR_ARCHIVE }, + { &ui_server14, "server14", "", CVAR_ARCHIVE }, + { &ui_server15, "server15", "", CVAR_ARCHIVE }, + { &ui_server16, "server16", "", CVAR_ARCHIVE }, + { &ui_cdkeychecked, "ui_cdkeychecked", "0", CVAR_ROM }, + { &ui_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_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.25", CVAR_ARCHIVE}, + { &ui_bigFont, "ui_bigFont", "0.4", 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}, + +}; + +// 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")); + } + +} + +/* +================= +ArenaServers_MaxPing +================= +*/ +#ifndef MISSIONPACK // bk001206 +static int ArenaServers_MaxPing( void ) { + int maxPing; + + maxPing = (int)trap_Cvar_VariableValue( "cl_maxPing" ); + if( maxPing < 100 ) { + maxPing = 100; + } + return maxPing; +} +#endif + +/* +================= +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; + + qtime_t q; + trap_RealTime(&q); + trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), va("%s-%i, %i at %i:%i", 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 00000000..7a0e0bdd --- /dev/null +++ b/src/ui/ui_players.c @@ -0,0 +1,1358 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +// 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 ) { + gitem_t * item; + char path[MAX_QPATH]; + + pi->currentWeapon = weaponNum; +tryagain: + pi->realWeapon = weaponNum; + pi->weaponModel = 0; + pi->barrelModel = 0; + pi->flashModel = 0; + + if ( weaponNum == WP_NONE ) { + return; + } + + for ( item = bg_itemlist + 1; item->classname ; item++ ) { + if ( item->giType != IT_WEAPON ) { + continue; + } + if ( item->giTag == weaponNum ) { + break; + } + } + + if ( item->classname ) { + pi->weaponModel = trap_R_RegisterModel( item->world_model[0] ); + } + + if( pi->weaponModel == 0 ) { + if( weaponNum == WP_MACHINEGUN ) { + weaponNum = WP_NONE; + goto tryagain; + } + weaponNum = WP_MACHINEGUN; + goto tryagain; + } + + if ( weaponNum == WP_MACHINEGUN || weaponNum == WP_GAUNTLET || weaponNum == WP_BFG ) { + strcpy( path, item->world_model[0] ); + COM_StripExtension( path, path ); + 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_LIGHTNING: + MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); + break; + + case WP_RAILGUN: + MAKERGB( pi->flashDlightColor, 1, 0.5f, 0 ); + break; + + case WP_PLASMAGUN: + MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); + break; + + case WP_BFG: + MAKERGB( pi->flashDlightColor, 1, 0.7f, 1 ); + break; + + case WP_GRAPPLING_HOOK: + MAKERGB( pi->flashDlightColor, 0.6f, 0.6f, 1 ); + break; + + default: + MAKERGB( pi->flashDlightColor, 1, 1, 1 ); + break; + } +} + + +/* +=============== +UI_ForceLegsAnim +=============== +*/ +static void UI_ForceLegsAnim( playerInfo_t *pi, int anim ) { + pi->legsAnim = ( ( pi->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; + + if ( anim == LEGS_JUMP ) { + pi->legsAnimationTimer = UI_TIMER_JUMP; + } +} + + +/* +=============== +UI_SetLegsAnim +=============== +*/ +static void UI_SetLegsAnim( playerInfo_t *pi, int anim ) { + if ( pi->pendingLegsAnim ) { + anim = pi->pendingLegsAnim; + pi->pendingLegsAnim = 0; + } + UI_ForceLegsAnim( pi, anim ); +} + + +/* +=============== +UI_ForceTorsoAnim +=============== +*/ +static void UI_ForceTorsoAnim( playerInfo_t *pi, int anim ) { + pi->torsoAnim = ( ( pi->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; + + if ( anim == TORSO_GESTURE ) { + pi->torsoAnimationTimer = UI_TIMER_GESTURE; + } + + if ( anim == TORSO_ATTACK || anim == TORSO_ATTACK2 ) { + pi->torsoAnimationTimer = UI_TIMER_ATTACK; + } +} + + +/* +=============== +UI_SetTorsoAnim +=============== +*/ +static void UI_SetTorsoAnim( playerInfo_t *pi, int anim ) { + if ( pi->pendingTorsoAnim ) { + anim = pi->pendingTorsoAnim; + pi->pendingTorsoAnim = 0; + } + + UI_ForceTorsoAnim( pi, anim ); +} + + +/* +=============== +UI_TorsoSequencing +=============== +*/ +static void UI_TorsoSequencing( playerInfo_t *pi ) { + int currentAnim; + + currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; + + if ( pi->weapon != pi->currentWeapon ) { + if ( currentAnim != TORSO_DROP ) { + pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; + UI_ForceTorsoAnim( pi, TORSO_DROP ); + } + } + + if ( pi->torsoAnimationTimer > 0 ) { + return; + } + + if( currentAnim == TORSO_GESTURE ) { + UI_SetTorsoAnim( pi, TORSO_STAND ); + return; + } + + if( currentAnim == TORSO_ATTACK || currentAnim == TORSO_ATTACK2 ) { + UI_SetTorsoAnim( pi, TORSO_STAND ); + return; + } + + if ( currentAnim == TORSO_DROP ) { + UI_PlayerInfo_SetWeapon( pi, pi->weapon ); + pi->torsoAnimationTimer = UI_TIMER_WEAPON_SWITCH; + UI_ForceTorsoAnim( pi, TORSO_RAISE ); + return; + } + + if ( currentAnim == TORSO_RAISE ) { + UI_SetTorsoAnim( pi, TORSO_STAND ); + return; + } +} + + +/* +=============== +UI_LegsSequencing +=============== +*/ +static void UI_LegsSequencing( playerInfo_t *pi ) { + int currentAnim; + + currentAnim = pi->legsAnim & ~ANIM_TOGGLEBIT; + + if ( pi->legsAnimationTimer > 0 ) { + if ( currentAnim == LEGS_JUMP ) { + jumpHeight = JUMP_HEIGHT * sin( M_PI * ( UI_TIMER_JUMP - pi->legsAnimationTimer ) / UI_TIMER_JUMP ); + } + return; + } + + if ( currentAnim == LEGS_JUMP ) { + UI_ForceLegsAnim( pi, LEGS_LAND ); + pi->legsAnimationTimer = UI_TIMER_LAND; + jumpHeight = 0; + return; + } + + if ( currentAnim == LEGS_LAND ) { + UI_SetLegsAnim( pi, LEGS_IDLE ); + return; + } +} + + +/* +====================== +UI_PositionEntityOnTag +====================== +*/ +static void UI_PositionEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + clipHandle_t parentModel, char *tagName ) { + int i; + orientation_t lerped; + + // lerp the tag + trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, + 1.0 - parent->backlerp, tagName ); + + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->origin ); + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // cast away const because of compiler problems + MatrixMultiply( lerped.axis, ((refEntity_t*)parent)->axis, entity->axis ); + entity->backlerp = parent->backlerp; +} + + +/* +====================== +UI_PositionRotatedEntityOnTag +====================== +*/ +static void UI_PositionRotatedEntityOnTag( refEntity_t *entity, const refEntity_t *parent, + clipHandle_t parentModel, char *tagName ) { + int i; + orientation_t lerped; + vec3_t tempAxis[3]; + + // lerp the tag + trap_CM_LerpTag( &lerped, parentModel, parent->oldframe, parent->frame, + 1.0 - parent->backlerp, tagName ); + + // FIXME: allow origin offsets along tag? + VectorCopy( parent->origin, entity->origin ); + for ( i = 0 ; i < 3 ; i++ ) { + VectorMA( entity->origin, lerped.origin[i], parent->axis[i], entity->origin ); + } + + // cast away const because of compiler problems + MatrixMultiply( entity->axis, ((refEntity_t *)parent)->axis, tempAxis ); + MatrixMultiply( lerped.axis, tempAxis, entity->axis ); +} + + +/* +=============== +UI_SetLerpFrameAnimation +=============== +*/ +static void UI_SetLerpFrameAnimation( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { + animation_t *anim; + + lf->animationNumber = newAnimation; + newAnimation &= ~ANIM_TOGGLEBIT; + + if ( newAnimation < 0 || newAnimation >= MAX_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 || pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { + vec3_t angles; + + memset( &barrel, 0, sizeof(barrel) ); + VectorCopy( origin, barrel.lightingOrigin ); + barrel.renderfx = renderfx; + + barrel.hModel = pi->barrelModel; + angles[YAW] = 0; + angles[PITCH] = 0; + angles[ROLL] = UI_MachinegunSpinAngle( pi ); + if( pi->realWeapon == WP_GAUNTLET || pi->realWeapon == WP_BFG ) { + angles[PITCH] = angles[ROLL]; + angles[ROLL] = 0; + } + AnglesToAxis( angles, barrel.axis ); + + UI_PositionRotatedEntityOnTag( &barrel, &gun, pi->weaponModel, "tag_barrel"); + + trap_R_AddRefEntityToScene( &barrel ); + } + + // + // add muzzle flash + // + if ( dp_realtime <= pi->muzzleFlashTime ) { + if ( pi->flashModel ) { + memset( &flash, 0, sizeof(flash) ); + flash.hModel = pi->flashModel; + VectorCopy( origin, flash.lightingOrigin ); + UI_PositionEntityOnTag( &flash, &gun, pi->weaponModel, "tag_flash"); + flash.renderfx = renderfx; + trap_R_AddRefEntityToScene( &flash ); + } + + // make a dlight for the flash + if ( pi->flashDlightColor[0] || pi->flashDlightColor[1] || pi->flashDlightColor[2] ) { + trap_R_AddLightToScene( flash.origin, 200 + (rand()&31), pi->flashDlightColor[0], + pi->flashDlightColor[1], pi->flashDlightColor[2] ); + } + } + + // + // add the chat icon + // + if ( pi->chat ) { + UI_PlayerFloatSprite( pi, origin, trap_R_RegisterShaderNoMip( "sprites/balloon3" ) ); + } + + // + // add an accent light + // + origin[0] -= 100; // + = behind, - = in front + origin[1] += 100; // + = left, - = right + origin[2] += 100; // + = above, - = below + trap_R_AddLightToScene( origin, 500, 1.0, 1.0, 1.0 ); + + origin[0] -= 100; + origin[1] -= 100; + origin[2] -= 100; + trap_R_AddLightToScene( origin, 500, 1.0, 0.0, 0.0 ); + + trap_R_RenderScene( &refdef ); +} + +/* +========================== +UI_FileExists +========================== +*/ +static qboolean UI_FileExists(const char *filename) { + int len; + + len = trap_FS_FOpenFile( filename, 0, 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 ); + 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 || weaponNum == WP_GAUNTLET ) { + torsoAnim = TORSO_STAND2; + } + else { + torsoAnim = TORSO_STAND; + } + } + + if ( torsoAnim == TORSO_ATTACK || torsoAnim == TORSO_ATTACK2 ) { + if ( weaponNum == WP_NONE || weaponNum == WP_GAUNTLET ) { + torsoAnim = TORSO_ATTACK2; + } + else { + torsoAnim = TORSO_ATTACK; + } + pi->muzzleFlashTime = dp_realtime + UI_TIMER_MUZZLE_FLASH; + //FIXME play firing sound here + } + + currentAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; + + if ( weaponNum != pi->currentWeapon || currentAnim == TORSO_RAISE || currentAnim == TORSO_DROP ) { + pi->pendingTorsoAnim = torsoAnim; + } + else if ( ( currentAnim == TORSO_GESTURE || currentAnim == TORSO_ATTACK ) && ( torsoAnim != currentAnim ) ) { + pi->pendingTorsoAnim = torsoAnim; + } + else if ( torsoAnim != currentAnim ) { + pi->pendingTorsoAnim = 0; + UI_ForceTorsoAnim( pi, torsoAnim ); + } +} diff --git a/src/ui/ui_shared.c b/src/ui/ui_shared.c new file mode 100644 index 00000000..3240ebb8 --- /dev/null +++ b/src/ui/ui_shared.c @@ -0,0 +1,5753 @@ +// +// string allocation/managment + +#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() { + 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() { + 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_PC_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_PC_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_PC_ReadToken(handle, &token)) + return qfalse; + if (token.string[0] == '-') { + if (!trap_PC_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_PC_ReadToken(handle, &token)) + return qfalse; + if (token.string[0] == '-') { + if (!trap_PC_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_PC_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_PC_ReadToken(handle, &token)) + return qfalse; + if (Q_stricmp(token.string, "{") != 0) { + return qfalse; + } + + while ( 1 ) { + if (!trap_PC_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() { + 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_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}, // nenu + {"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; + } + 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; + + //DC->Print("slider handle key\n"); + 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; + //DC->Print("slider x: %f\n", testRect.x); + testRect.w = (SLIDER_WIDTH + (float)SLIDER_THUMB_WIDTH / 2); + //DC->Print("slider w: %f\n", testRect.w); + 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; + } + } + } + } + DC->Print("slider handle key exit\n"); + 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() { + 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() { + 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)); + } 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 && *item->enableCvar && item->cvarTest && *item->cvarTest) { + if (item->cvarFlags & (CVAR_ENABLE | CVAR_DISABLE) && !Item_EnableShowViaCvar(item, CVAR_ENABLE)) { + memcpy(newColor, &parent->disableColor, sizeof(vec4_t)); + } + } +} + +void Item_Text_AutoWrapped_Paint(itemDef_t *item) { + char text[1024]; + const char *p, *textPtr, *newLinePtr; + char buff[1024]; + int width, height, len, textWidth, newLine, newLineWidth; + float y; + vec4_t color; + + 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); + + y = item->textaligny; + len = 0; + buff[0] = '\0'; + newLine = 0; + newLineWidth = 0; + p = textPtr; + while (p) { + if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\0') { + newLine = len; + newLinePtr = p+1; + newLineWidth = textWidth; + } + textWidth = DC->textWidth(buff, item->textscale, 0); + 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'; + DC->drawText(item->textRect.x, item->textRect.y, item->textscale, color, buff, 0, 0, item->textStyle); + } + if (*p == '\0') { + break; + } + // + y += height + 5; + p = newLinePtr; + len = 0; + newLine = 0; + newLineWidth = 0; + continue; + } + buff[len++] = *p++; + buff[len] = '\0'; + } +} + +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, lowLight; + 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)); + } 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, lowLight; + 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)); + } 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, lowLight; + 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)); + } 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}, + {"+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_CTRL, -1, -1, -1}, + {"weapprev", '[', -1, -1, -1}, + {"weapnext", ']', -1, -1, -1}, + {"+button3", K_MOUSE3, -1, -1, -1}, + {"+button4", K_MOUSE4, -1, -1, -1}, + {"prevTeamMember", 'w', -1, -1, -1}, + {"nextTeamMember", 'r', -1, -1, -1}, + {"nextOrder", 't', -1, -1, -1}, + {"confirmOrder", 'y', -1, -1, -1}, + {"denyOrder", 'n', -1, -1, -1}, + {"taskOffense", 'o', -1, -1, -1}, + {"taskDefense", 'd', -1, -1, -1}, + {"taskPatrol", 'p', -1, -1, -1}, + {"taskCamp", 'c', -1, -1, -1}, + {"taskFollow", 'f', -1, -1, -1}, + {"taskRetrieve", 'v', -1, -1, -1}, + {"taskEscort", 'e', -1, -1, -1}, + {"taskOwnFlag", 'i', -1, -1, -1}, + {"taskSuicide", 'k', -1, -1, -1}, + {"tauntKillInsult", K_F1, -1, -1, -1}, + {"tauntPraise", K_F2, -1, -1, -1}, + {"tauntTaunt", K_F3, -1, -1, -1}, + {"tauntDeathInsult", K_F4, -1, -1, -1}, + {"tauntGauntlet", K_F5, -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); + +#ifndef MISSIONPACK // bk001206 +static configcvar_t g_configcvars[] = +{ + {"cl_run", 0, 0}, + {"m_pitch", 0, 0}, + {"cg_autoswitch", 0, 0}, + {"sensitivity", 0, 0}, + {"in_joystick", 0, 0}, + {"joy_threshold", 0, 0}, + {"m_filter", 0, 0}, + {"cl_freelook", 0, 0}, + {NULL, 0, 0} +}; +#endif + +/* +================= +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, lowLight; + 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)); + } 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)); + } 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() { + 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, count, i, thumb; + 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); + } + + size -= listPtr->elementWidth; + if (size < listPtr->elementWidth) { + listPtr->drawPadding = size; //listPtr->elementWidth - size; + break; + } + x += listPtr->elementWidth; + listPtr->endPos++; + // 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) { + DC->drawText(x + 4 + listPtr->columnInfo[j].pos, 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); + } + + size -= listPtr->elementHeight; + if (size < listPtr->elementHeight) { + listPtr->drawPadding = listPtr->elementHeight - size; + break; + } + listPtr->endPos++; + y += listPtr->elementHeight; + // fit++; + } + } + } +} + + +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)); + } 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() { + 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() { + 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; + 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); + 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; + + if (PC_Int_Parse(handle, &pos) && PC_Int_Parse(handle, &width) && PC_Int_Parse(handle, &maxChars)) { + listPtr->columnInfo[i].pos = pos; + listPtr->columnInfo[i].width = width; + listPtr->columnInfo[i].maxChars = maxChars; + } 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_PC_ReadToken(handle, &token)) + return qfalse; + if (*token.string != '{') { + return qfalse; + } + + pass = 0; + while ( 1 ) { + if (!trap_PC_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_PC_ReadToken(handle, &token)) + return qfalse; + if (*token.string != '{') { + return qfalse; + } + + while ( 1 ) { + if (!trap_PC_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_PC_ReadToken(handle, &token)) + return qfalse; + if (*token.string != '{') { + return qfalse; + } + while ( 1 ) { + if (!trap_PC_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_PC_ReadToken(handle, &token)) + return qfalse; + if (*token.string != '{') { + return qfalse; + } + + while ( 1 ) { + + memset(&token, 0, sizeof(pc_token_t)); + if (!trap_PC_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]; + + Com_Printf( "menu_new\n" ); + + if (menuCount < MAX_MENUS) { + Menu_Init(menu); + if (Menu_Parse(handle, menu)) { + Menu_PostParse(menu); + menuCount++; + } + } +} + +int Menu_Count() { + return menuCount; +} + +void Menu_PaintAll() { + 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() { + menuCount = 0; +} + +displayContextDef_t *Display_GetContext() { + return DC; +} + +#ifndef MISSIONPACK // bk001206 +static float captureX; +static float captureY; +#endif + +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() { + 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 00000000..a32c2297 --- /dev/null +++ b/src/ui/ui_shared.h @@ -0,0 +1,429 @@ +#ifndef __UI_SHARED_H +#define __UI_SHARED_H + + +#include "../game/q_shared.h" +#include "../cgame/tr_types.h" +#include "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 64 +#define MAX_MENUITEMS 96 +#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; +} 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; + + // player settings + qhandle_t fxBasePic; + qhandle_t fxPic[7]; + qhandle_t crosshairShader[NUM_CROSSHAIRS]; + +} 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 (*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 (*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)(); + 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 String_Report(); +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 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 Menu_New(int handle); +void Menu_PaintAll(); +menuDef_t *Menus_ActivateByName(const char *p); +void Menu_Reset(); +qboolean Menus_AnyFullScreenVisible(); +void Menus_Activate(menuDef_t *menu); + +displayContextDef_t *Display_GetContext(); +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 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 Menu_Paint(menuDef_t *menu, qboolean forcePaint); +void Menu_SetFeederSelection(menuDef_t *menu, int feeder, int index, const char *name); +void Display_CacheAll(); + +void *UI_Alloc( int size ); +void UI_InitMemory( void ); +qboolean UI_OutOfMemory(); + +void Controls_GetConfig( void ); +void Controls_SetConfig(qboolean restart); +void Controls_SetDefaults( void ); + +int trap_PC_AddGlobalDefine( char *define ); +int trap_PC_LoadSource( const char *filename ); +int trap_PC_FreeSource( int handle ); +int trap_PC_ReadToken( int handle, pc_token_t *pc_token ); +int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ); + +#endif diff --git a/src/ui/ui_syscalls.c b/src/ui/ui_syscalls.c new file mode 100644 index 00000000..8bce0862 --- /dev/null +++ b/src/ui/ui_syscalls.c @@ -0,0 +1,372 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +#include "ui_local.h" + +// this file is only included when building a dll +// syscalls.asm is included instead when building a qvm + +static int (QDECL *syscall)( int arg, ... ) = (int (QDECL *)( int, ...))-1; + +void dllEntry( int (QDECL *syscallptr)( int 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 ); +} + +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() { + syscall( UI_LAN_SAVECACHEDSERVERS ); +} + +void trap_LAN_LoadCachedServers() { + 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 ); +} + +void trap_GetCDKey( char *buf, int buflen ) { + syscall( UI_GET_CDKEY, buf, buflen ); +} + +void trap_SetCDKey( char *buf ) { + syscall( UI_SET_CDKEY, buf ); +} + +int trap_PC_AddGlobalDefine( char *define ) { + return syscall( UI_PC_ADD_GLOBAL_DEFINE, define ); +} + +int trap_PC_LoadSource( const char *filename ) { + return syscall( UI_PC_LOAD_SOURCE, filename ); +} + +int trap_PC_FreeSource( int handle ) { + return syscall( UI_PC_FREE_SOURCE, handle ); +} + +int trap_PC_ReadToken( int handle, pc_token_t *pc_token ) { + return syscall( UI_PC_READ_TOKEN, handle, pc_token ); +} + +int trap_PC_SourceFileAndLine( int handle, char *filename, int *line ) { + return syscall( UI_PC_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 ); +} + +qboolean trap_VerifyCDKey( const char *key, const char *chksum) { + return syscall( UI_VERIFY_CDKEY, key, chksum); +} + +
\ No newline at end of file |