/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // /********************************************************************** UI_ATOMS.C User interface building blocks and support functions. **********************************************************************/ #include "ui_local.h" qboolean m_entersound; // after a frame, so caching won't disrupt the sound void QDECL Com_Error( int level, const char *error, ... ) { va_list argptr; char text[1024]; va_start (argptr, error); Q_vsnprintf (text, sizeof(text), error, argptr); va_end (argptr); trap_Error( va("%s", text) ); } void QDECL Com_Printf( const char *msg, ... ) { va_list argptr; char text[1024]; va_start (argptr, msg); Q_vsnprintf (text, sizeof(text), msg, argptr); va_end (argptr); trap_Print( va("%s", text) ); } 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 static void NeedCDAction( qboolean result ) { if ( !result ) { trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" ); } } #endif // MISSIONPACK #ifndef MISSIONPACK 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(void) { char gameList[4096]; char *gameFile; int i, len, count, size; fileHandle_t f; postGameInfo_t newInfo; count = trap_FS_GetFileList( "games", "game", gameList, sizeof(gameList) ); size = sizeof(postGameInfo_t); memset(&newInfo, 0, size); if (count > 0) { gameFile = gameList; for ( i = 0; i < count; i++ ) { len = strlen(gameFile); if (trap_FS_FOpenFile(va("games/%s",gameFile), &f, FS_WRITE) >= 0) { trap_FS_Write(&size, sizeof(int), f); trap_FS_Write(&newInfo, size, f); trap_FS_FCloseFile(f); } gameFile += len + 1; } } UI_SetBestScores(&newInfo, qfalse); } static void UI_Cache_f( void ) { Display_CacheAll(); } /* ======================= UI_CalcPostGameStats ======================= */ static void UI_CalcPostGameStats( void ) { char map[MAX_QPATH]; char fileName[MAX_QPATH]; char info[MAX_INFO_STRING]; fileHandle_t f; int size, game, time, adjustedTime; postGameInfo_t oldInfo; postGameInfo_t newInfo; qboolean newHigh = qfalse; trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) ); Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) ); game = atoi(Info_ValueForKey(info, "g_gametype")); // compose file name Com_sprintf(fileName, MAX_QPATH, "games/%s_%i.game", map, game); // see if we have one already memset(&oldInfo, 0, sizeof(postGameInfo_t)); if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0) { // if so load it size = 0; trap_FS_Read(&size, sizeof(int), f); if (size == sizeof(postGameInfo_t)) { trap_FS_Read(&oldInfo, sizeof(postGameInfo_t), f); } trap_FS_FCloseFile(f); } newInfo.accuracy = atoi(UI_Argv(3)); newInfo.impressives = atoi(UI_Argv(4)); newInfo.excellents = atoi(UI_Argv(5)); newInfo.defends = atoi(UI_Argv(6)); newInfo.assists = atoi(UI_Argv(7)); newInfo.gauntlets = atoi(UI_Argv(8)); newInfo.baseScore = atoi(UI_Argv(9)); newInfo.perfects = atoi(UI_Argv(10)); newInfo.redScore = atoi(UI_Argv(11)); newInfo.blueScore = atoi(UI_Argv(12)); time = atoi(UI_Argv(13)); newInfo.captures = atoi(UI_Argv(14)); newInfo.time = (time - trap_Cvar_VariableValue("ui_matchStartTime")) / 1000; adjustedTime = uiInfo.mapList[ui_currentMap.integer].timeToBeat[game]; if (newInfo.time < adjustedTime) { newInfo.timeBonus = (adjustedTime - newInfo.time) * 10; } else { newInfo.timeBonus = 0; } if (newInfo.redScore > newInfo.blueScore && newInfo.blueScore <= 0) { newInfo.shutoutBonus = 100; } else { newInfo.shutoutBonus = 0; } newInfo.skillBonus = trap_Cvar_VariableValue("g_spSkill"); if (newInfo.skillBonus <= 0) { newInfo.skillBonus = 1; } newInfo.score = newInfo.baseScore + newInfo.shutoutBonus + newInfo.timeBonus; newInfo.score *= newInfo.skillBonus; // see if the score is higher for this one newHigh = (newInfo.redScore > newInfo.blueScore && newInfo.score > oldInfo.score); if (newHigh) { // if so write out the new one uiInfo.newHighScoreTime = uiInfo.uiDC.realTime + 20000; if (trap_FS_FOpenFile(fileName, &f, FS_WRITE) >= 0) { size = sizeof(postGameInfo_t); trap_FS_Write(&size, sizeof(int), f); trap_FS_Write(&newInfo, sizeof(postGameInfo_t), f); trap_FS_FCloseFile(f); } } if (newInfo.time < oldInfo.time) { uiInfo.newBestTime = uiInfo.uiDC.realTime + 20000; } // put back all the ui overrides trap_Cvar_Set("capturelimit", UI_Cvar_VariableString("ui_saveCaptureLimit")); trap_Cvar_Set("fraglimit", UI_Cvar_VariableString("ui_saveFragLimit")); trap_Cvar_Set("cg_drawTimer", UI_Cvar_VariableString("ui_drawTimer")); trap_Cvar_Set("g_doWarmup", UI_Cvar_VariableString("ui_doWarmup")); trap_Cvar_Set("g_Warmup", UI_Cvar_VariableString("ui_Warmup")); trap_Cvar_Set("sv_pure", UI_Cvar_VariableString("ui_pure")); trap_Cvar_Set("g_friendlyFire", UI_Cvar_VariableString("ui_friendlyFire")); UI_SetBestScores(&newInfo, qtrue); UI_ShowPostGame(newHigh); } /* ================= UI_ConsoleCommand ================= */ qboolean UI_ConsoleCommand( int realTime ) { char *cmd; 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]; char shader3[MAX_QPATH]; Q_strncpyz(shader1, UI_Argv(1), sizeof(shader1)); Q_strncpyz(shader2, UI_Argv(2), sizeof(shader2)); Q_strncpyz(shader3, UI_Argv(3), sizeof(shader3)); trap_R_RemapShader(shader1, shader2, shader3); 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, "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; }