1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (RTCW MP Source Code).
8
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 // Copyright (C) 1999-2000 Id Software, Inc.
30 //
31 /**********************************************************************
32 UI_ATOMS.C
33
34 User interface building blocks and support functions.
35 **********************************************************************/
36 #include "ui_local.h"
37
38 uiStatic_t uis;
39 qboolean m_entersound; // after a frame, so caching won't disrupt the sound
40
41 void QDECL Com_DPrintf( const char *fmt, ... ) __attribute__ ( ( format ( printf, 1, 2 ) ) );
42
43
44 // JPW NERVE added Com_DPrintf
45 #define MAXPRINTMSG 4096
Com_DPrintf(const char * fmt,...)46 void QDECL Com_DPrintf( const char *fmt, ... ) {
47 va_list argptr;
48 char msg[MAXPRINTMSG];
49 int developer;
50
51 developer = trap_Cvar_VariableValue( "developer" );
52 if ( !developer ) {
53 return;
54 }
55
56 va_start( argptr,fmt );
57 Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
58 va_end( argptr );
59
60 Com_Printf( "%s", msg );
61 }
62 // jpw
63
Com_Error(int level,const char * error,...)64 void QDECL Com_Error( int level, const char *error, ... ) {
65 va_list argptr;
66 char text[1024];
67
68 va_start( argptr, error );
69 Q_vsnprintf( text, sizeof( text ), error, argptr );
70 va_end( argptr );
71
72 trap_Error( text );
73 }
74
Com_Printf(const char * msg,...)75 void QDECL Com_Printf( const char *msg, ... ) {
76 va_list argptr;
77 char text[1024];
78
79 va_start( argptr, msg );
80 Q_vsnprintf( text, sizeof( text ), msg, argptr );
81 va_end( argptr );
82
83 trap_Print( text );
84 }
85
86 /*
87 =================
88 UI_ClampCvar
89 =================
90 */
UI_ClampCvar(float min,float max,float value)91 float UI_ClampCvar( float min, float max, float value ) {
92 if ( value < min ) {
93 return min;
94 }
95 if ( value > max ) {
96 return max;
97 }
98 return value;
99 }
100
101 /*
102 =================
103 UI_StartDemoLoop
104 =================
105 */
UI_StartDemoLoop(void)106 void UI_StartDemoLoop( void ) {
107 trap_Cmd_ExecuteText( EXEC_APPEND, "d1\n" );
108 }
109
110 /*
111 // TTimo: unused
112 static void NeedCDAction( qboolean result ) {
113 if ( !result ) {
114 trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
115 }
116 }
117
118 static void NeedCDKeyAction( qboolean result ) {
119 if ( !result ) {
120 trap_Cmd_ExecuteText( EXEC_APPEND, "quit\n" );
121 }
122 }
123 */
124
UI_Argv(int arg)125 char *UI_Argv( int arg ) {
126 static char buffer[MAX_STRING_CHARS];
127
128 trap_Argv( arg, buffer, sizeof( buffer ) );
129
130 return buffer;
131 }
132
133
UI_Cvar_VariableString(const char * var_name)134 char *UI_Cvar_VariableString( const char *var_name ) {
135 static char buffer[2][MAX_STRING_CHARS];
136 static int toggle;
137
138 toggle ^= 1; // flip-flop to allow two returns without clash
139
140 trap_Cvar_VariableStringBuffer( var_name, buffer[toggle], sizeof( buffer[0] ) );
141
142 return buffer[toggle];
143 }
144
145
146 #ifdef MISSIONPACK
UI_SetBestScores(postGameInfo_t * newInfo,qboolean postGame)147 void UI_SetBestScores( postGameInfo_t *newInfo, qboolean postGame ) {
148 trap_Cvar_Set( "ui_scoreAccuracy", va( "%i%%", newInfo->accuracy ) );
149 trap_Cvar_Set( "ui_scoreImpressives", va( "%i", newInfo->impressives ) );
150 trap_Cvar_Set( "ui_scoreExcellents", va( "%i", newInfo->excellents ) );
151 trap_Cvar_Set( "ui_scoreDefends", va( "%i", newInfo->defends ) );
152 trap_Cvar_Set( "ui_scoreAssists", va( "%i", newInfo->assists ) );
153 trap_Cvar_Set( "ui_scoreGauntlets", va( "%i", newInfo->gauntlets ) );
154 trap_Cvar_Set( "ui_scoreScore", va( "%i", newInfo->score ) );
155 trap_Cvar_Set( "ui_scorePerfect", va( "%i", newInfo->perfects ) );
156 trap_Cvar_Set( "ui_scoreTeam", va( "%i to %i", newInfo->redScore, newInfo->blueScore ) );
157 trap_Cvar_Set( "ui_scoreBase", va( "%i", newInfo->baseScore ) );
158 trap_Cvar_Set( "ui_scoreTimeBonus", va( "%i", newInfo->timeBonus ) );
159 trap_Cvar_Set( "ui_scoreSkillBonus", va( "%i", newInfo->skillBonus ) );
160 trap_Cvar_Set( "ui_scoreShutoutBonus", va( "%i", newInfo->shutoutBonus ) );
161 trap_Cvar_Set( "ui_scoreTime", va( "%02i:%02i", newInfo->time / 60, newInfo->time % 60 ) );
162 trap_Cvar_Set( "ui_scoreCaptures", va( "%i", newInfo->captures ) );
163 if ( postGame ) {
164 trap_Cvar_Set( "ui_scoreAccuracy2", va( "%i%%", newInfo->accuracy ) );
165 trap_Cvar_Set( "ui_scoreImpressives2", va( "%i", newInfo->impressives ) );
166 trap_Cvar_Set( "ui_scoreExcellents2", va( "%i", newInfo->excellents ) );
167 trap_Cvar_Set( "ui_scoreDefends2", va( "%i", newInfo->defends ) );
168 trap_Cvar_Set( "ui_scoreAssists2", va( "%i", newInfo->assists ) );
169 trap_Cvar_Set( "ui_scoreGauntlets2", va( "%i", newInfo->gauntlets ) );
170 trap_Cvar_Set( "ui_scoreScore2", va( "%i", newInfo->score ) );
171 trap_Cvar_Set( "ui_scorePerfect2", va( "%i", newInfo->perfects ) );
172 trap_Cvar_Set( "ui_scoreTeam2", va( "%i to %i", newInfo->redScore, newInfo->blueScore ) );
173 trap_Cvar_Set( "ui_scoreBase2", va( "%i", newInfo->baseScore ) );
174 trap_Cvar_Set( "ui_scoreTimeBonus2", va( "%i", newInfo->timeBonus ) );
175 trap_Cvar_Set( "ui_scoreSkillBonus2", va( "%i", newInfo->skillBonus ) );
176 trap_Cvar_Set( "ui_scoreShutoutBonus2", va( "%i", newInfo->shutoutBonus ) );
177 trap_Cvar_Set( "ui_scoreTime2", va( "%02i:%02i", newInfo->time / 60, newInfo->time % 60 ) );
178 trap_Cvar_Set( "ui_scoreCaptures2", va( "%i", newInfo->captures ) );
179 }
180 }
181 #endif // #ifdef MISSIONPACK
182
UI_LoadBestScores(const char * map,int game)183 void UI_LoadBestScores( const char *map, int game ) {
184 #ifdef MISSIONPACK
185 char fileName[MAX_QPATH];
186 fileHandle_t f;
187 postGameInfo_t newInfo;
188 int protocol, protocolLegacy;
189
190 memset( &newInfo, 0, sizeof( postGameInfo_t ) );
191 Com_sprintf( fileName, MAX_QPATH, "games/%s_%i.game", map, game );
192 if ( trap_FS_FOpenFile( fileName, &f, FS_READ ) >= 0 ) {
193 int size = 0;
194 trap_FS_Read( &size, sizeof( int ), f );
195 if ( size == sizeof( postGameInfo_t ) ) {
196 trap_FS_Read( &newInfo, sizeof( postGameInfo_t ), f );
197 }
198 trap_FS_FCloseFile( f );
199 }
200 UI_SetBestScores( &newInfo, qfalse );
201
202 uiInfo.demoAvailable = qfalse;
203
204 protocolLegacy = trap_Cvar_VariableValue("com_legacyprotocol");
205 protocol = trap_Cvar_VariableValue("com_protocol");
206
207 if(!protocol)
208 protocol = trap_Cvar_VariableValue("protocol");
209 if(protocolLegacy == protocol)
210 protocolLegacy = 0;
211
212 Com_sprintf(fileName, MAX_QPATH, "demos/%s_%d.%s%d", map, game, DEMOEXT, protocol);
213 if(trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0)
214 {
215 uiInfo.demoAvailable = qtrue;
216 trap_FS_FCloseFile( f );
217 }
218 else if(protocolLegacy > 0)
219 {
220 Com_sprintf(fileName, MAX_QPATH, "demos/%s_%d.%s%d", map, game, DEMOEXT, protocolLegacy);
221 if (trap_FS_FOpenFile(fileName, &f, FS_READ) >= 0)
222 {
223 uiInfo.demoAvailable = qtrue;
224 trap_FS_FCloseFile(f);
225 }
226 }
227 #endif // #ifdef MISSIONPACK
228 }
229
230 /*
231 ===============
232 UI_ClearScores
233 ===============
234 */
UI_ClearScores(void)235 void UI_ClearScores(void) {
236 #ifdef MISSIONPACK
237 char gameList[4096];
238 char *gameFile;
239 int i, len, count, size;
240 fileHandle_t f;
241 postGameInfo_t newInfo;
242
243 count = trap_FS_GetFileList( "games", "game", gameList, sizeof( gameList ) );
244
245 size = sizeof( postGameInfo_t );
246 memset( &newInfo, 0, size );
247
248 if ( count > 0 ) {
249 gameFile = gameList;
250 for ( i = 0; i < count; i++ ) {
251 len = strlen( gameFile );
252 if ( trap_FS_FOpenFile( va( "games/%s",gameFile ), &f, FS_WRITE ) >= 0 ) {
253 trap_FS_Write( &size, sizeof( int ), f );
254 trap_FS_Write( &newInfo, size, f );
255 trap_FS_FCloseFile( f );
256 }
257 gameFile += len + 1;
258 }
259 }
260
261 UI_SetBestScores( &newInfo, qfalse );
262 #endif // #ifdef MISSIONPACK
263
264 }
265
266
267
UI_Cache_f(void)268 static void UI_Cache_f(void) {
269 Display_CacheAll();
270 }
271
272 /*
273 =======================
274 UI_CalcPostGameStats
275 =======================
276 */
UI_CalcPostGameStats(void)277 static void UI_CalcPostGameStats(void) {
278 #ifdef MISSIONPACK
279 char map[MAX_QPATH];
280 char fileName[MAX_QPATH];
281 char info[MAX_INFO_STRING];
282 fileHandle_t f;
283 int size, game, time, adjustedTime;
284 postGameInfo_t oldInfo;
285 postGameInfo_t newInfo;
286 qboolean newHigh = qfalse;
287
288 trap_GetConfigString( CS_SERVERINFO, info, sizeof( info ) );
289 Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof( map ) );
290 game = atoi( Info_ValueForKey( info, "g_gametype" ) );
291
292 // compose file name
293 Com_sprintf( fileName, MAX_QPATH, "games/%s_%i.game", map, game );
294 // see if we have one already
295 memset( &oldInfo, 0, sizeof( postGameInfo_t ) );
296 if ( trap_FS_FOpenFile( fileName, &f, FS_READ ) >= 0 ) {
297 // if so load it
298 size = 0;
299 trap_FS_Read( &size, sizeof( int ), f );
300 if ( size == sizeof( postGameInfo_t ) ) {
301 trap_FS_Read( &oldInfo, sizeof( postGameInfo_t ), f );
302 }
303 trap_FS_FCloseFile( f );
304 }
305
306 newInfo.accuracy = atoi( UI_Argv( 3 ) );
307 newInfo.impressives = atoi( UI_Argv( 4 ) );
308 newInfo.excellents = atoi( UI_Argv( 5 ) );
309 newInfo.defends = atoi( UI_Argv( 6 ) );
310 newInfo.assists = atoi( UI_Argv( 7 ) );
311 newInfo.gauntlets = atoi( UI_Argv( 8 ) );
312 newInfo.baseScore = atoi( UI_Argv( 9 ) );
313 newInfo.perfects = atoi( UI_Argv( 10 ) );
314 newInfo.redScore = atoi( UI_Argv( 11 ) );
315 newInfo.blueScore = atoi( UI_Argv( 12 ) );
316 time = atoi( UI_Argv( 13 ) );
317 newInfo.captures = atoi( UI_Argv( 14 ) );
318
319 newInfo.time = ( time - trap_Cvar_VariableValue( "ui_matchStartTime" ) ) / 1000;
320 adjustedTime = uiInfo.mapList[ui_currentMap.integer].timeToBeat[game];
321 if ( newInfo.time < adjustedTime ) {
322 newInfo.timeBonus = ( adjustedTime - newInfo.time ) * 10;
323 } else {
324 newInfo.timeBonus = 0;
325 }
326
327 if ( newInfo.redScore > newInfo.blueScore && newInfo.blueScore <= 0 ) {
328 newInfo.shutoutBonus = 100;
329 } else {
330 newInfo.shutoutBonus = 0;
331 }
332
333 newInfo.skillBonus = trap_Cvar_VariableValue( "g_spSkill" );
334 if ( newInfo.skillBonus <= 0 ) {
335 newInfo.skillBonus = 1;
336 }
337 newInfo.score = newInfo.baseScore + newInfo.shutoutBonus + newInfo.timeBonus;
338 newInfo.score *= newInfo.skillBonus;
339
340 // see if the score is higher for this one
341 newHigh = ( newInfo.redScore > newInfo.blueScore && newInfo.score > oldInfo.score );
342
343 if ( newHigh ) {
344 // if so write out the new one
345 uiInfo.newHighScoreTime = uiInfo.uiDC.realTime + 20000;
346 if ( trap_FS_FOpenFile( fileName, &f, FS_WRITE ) >= 0 ) {
347 size = sizeof( postGameInfo_t );
348 trap_FS_Write( &size, sizeof( int ), f );
349 trap_FS_Write( &newInfo, sizeof( postGameInfo_t ), f );
350 trap_FS_FCloseFile( f );
351 }
352 }
353
354 if ( newInfo.time < oldInfo.time ) {
355 uiInfo.newBestTime = uiInfo.uiDC.realTime + 20000;
356 }
357
358 // put back all the ui overrides
359 trap_Cvar_Set( "capturelimit", UI_Cvar_VariableString( "ui_saveCaptureLimit" ) );
360 trap_Cvar_Set( "fraglimit", UI_Cvar_VariableString( "ui_saveFragLimit" ) );
361 trap_Cvar_Set( "cg_drawTimer", UI_Cvar_VariableString( "ui_drawTimer" ) );
362 trap_Cvar_Set( "g_doWarmup", UI_Cvar_VariableString( "ui_doWarmup" ) );
363 trap_Cvar_Set( "g_Warmup", UI_Cvar_VariableString( "ui_Warmup" ) );
364 trap_Cvar_Set( "sv_pure", UI_Cvar_VariableString( "ui_pure" ) );
365 trap_Cvar_Set( "g_friendlyFire", UI_Cvar_VariableString( "ui_friendlyFire" ) );
366
367 UI_SetBestScores( &newInfo, qtrue );
368 UI_ShowPostGame( newHigh );
369
370 #endif // #ifdef MISSIONPACK
371
372 }
373
374
375 /*
376 =================
377 UI_ConsoleCommand
378 =================
379 */
UI_ConsoleCommand(int realTime)380 qboolean UI_ConsoleCommand( int realTime ) {
381 char *cmd;
382
383 uiInfo.uiDC.frameTime = realTime - uiInfo.uiDC.realTime;
384 uiInfo.uiDC.realTime = realTime;
385
386 cmd = UI_Argv( 0 );
387
388 // ensure minimum menu data is available
389 //Menu_Cache();
390
391 if ( Q_stricmp( cmd, "ui_test" ) == 0 ) {
392 UI_ShowPostGame( qtrue );
393 return qtrue;
394 }
395
396 if ( Q_stricmp( cmd, "ui_report" ) == 0 ) {
397 UI_Report();
398 return qtrue;
399 }
400
401 if ( Q_stricmp( cmd, "ui_load" ) == 0 ) {
402 UI_Load();
403 return qtrue;
404 }
405
406 if ( Q_stricmp( cmd, "remapShader" ) == 0 ) {
407 if ( trap_Argc() == 4 ) {
408 char shader1[MAX_QPATH];
409 char shader2[MAX_QPATH];
410 char shader3[MAX_QPATH];
411 Q_strncpyz( shader1, UI_Argv( 1 ), sizeof( shader1 ) );
412 Q_strncpyz( shader2, UI_Argv( 2 ), sizeof( shader2 ) );
413 Q_strncpyz(shader3, UI_Argv(3), sizeof(shader3));
414
415 trap_R_RemapShader(shader1, shader2, shader3);
416 return qtrue;
417 }
418 }
419
420 if ( Q_stricmp( cmd, "postgame" ) == 0 ) {
421 UI_CalcPostGameStats();
422 return qtrue;
423 }
424
425 if ( Q_stricmp( cmd, "ui_cache" ) == 0 ) {
426 UI_Cache_f();
427 return qtrue;
428 }
429
430 if ( Q_stricmp( cmd, "ui_teamOrders" ) == 0 ) {
431 //UI_TeamOrdersMenu_f();
432 return qtrue;
433 }
434
435
436 if ( Q_stricmp( cmd, "ui_cdkey" ) == 0 ) {
437 //UI_CDKeyMenu_f();
438 return qtrue;
439 }
440
441 return qfalse;
442 }
443
444 /*
445 =================
446 UI_Shutdown
447 =================
448 */
UI_Shutdown(void)449 void UI_Shutdown( void ) {
450 }
451
UI_DrawNamedPic(float x,float y,float width,float height,const char * picname)452 void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
453 qhandle_t hShader;
454
455 hShader = trap_R_RegisterShaderNoMip( picname );
456 UI_AdjustFrom640( &x, &y, &width, &height );
457 trap_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
458 }
459
UI_DrawHandlePic(float x,float y,float w,float h,qhandle_t hShader)460 void UI_DrawHandlePic( float x, float y, float w, float h, qhandle_t hShader ) {
461 float s0;
462 float s1;
463 float t0;
464 float t1;
465
466 if ( w < 0 ) { // flip about vertical
467 w = -w;
468 s0 = 1;
469 s1 = 0;
470 } else {
471 s0 = 0;
472 s1 = 1;
473 }
474
475 if ( h < 0 ) { // flip about horizontal
476 h = -h;
477 t0 = 1;
478 t1 = 0;
479 } else {
480 t0 = 0;
481 t1 = 1;
482 }
483
484 UI_AdjustFrom640( &x, &y, &w, &h );
485 trap_R_DrawStretchPic( x, y, w, h, s0, t0, s1, t1, hShader );
486 }
487
488 /*
489 ================
490 UI_FillRect
491
492 Coordinates are 640*480 virtual values
493 =================
494 */
UI_FillRect(float x,float y,float width,float height,const float * color)495 void UI_FillRect( float x, float y, float width, float height, const float *color ) {
496 trap_R_SetColor( color );
497
498 UI_AdjustFrom640( &x, &y, &width, &height );
499 trap_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
500
501 trap_R_SetColor( NULL );
502 }
503
UI_DrawSides(float x,float y,float w,float h)504 void UI_DrawSides( float x, float y, float w, float h ) {
505 UI_AdjustFrom640( &x, &y, &w, &h );
506 trap_R_DrawStretchPic( x, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
507 trap_R_DrawStretchPic( x + w - 1, y, 1, h, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
508 }
509
UI_DrawTopBottom(float x,float y,float w,float h)510 void UI_DrawTopBottom( float x, float y, float w, float h ) {
511 UI_AdjustFrom640( &x, &y, &w, &h );
512 trap_R_DrawStretchPic( x, y, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
513 trap_R_DrawStretchPic( x, y + h - 1, w, 1, 0, 0, 0, 0, uiInfo.uiDC.whiteShader );
514 }
515 /*
516 ================
517 UI_DrawRect
518
519 Coordinates are 640*480 virtual values
520 =================
521 */
UI_DrawRect(float x,float y,float width,float height,const float * color)522 void UI_DrawRect( float x, float y, float width, float height, const float *color ) {
523 trap_R_SetColor( color );
524
525 UI_DrawTopBottom( x, y, width, height );
526 UI_DrawSides( x, y, width, height );
527
528 trap_R_SetColor( NULL );
529 }
530
UI_SetColor(const float * rgba)531 void UI_SetColor( const float *rgba ) {
532 trap_R_SetColor( rgba );
533 }
534
UI_UpdateScreen(void)535 void UI_UpdateScreen( void ) {
536 trap_UpdateScreen();
537 }
538
539
UI_DrawTextBox(int x,int y,int width,int lines)540 void UI_DrawTextBox( int x, int y, int width, int lines ) {
541 UI_FillRect( x + BIGCHAR_WIDTH / 2, y + BIGCHAR_HEIGHT / 2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorBlack );
542 UI_DrawRect( x + BIGCHAR_WIDTH / 2, y + BIGCHAR_HEIGHT / 2, ( width + 1 ) * BIGCHAR_WIDTH, ( lines + 1 ) * BIGCHAR_HEIGHT, colorWhite );
543 }
544
UI_CursorInRect(int x,int y,int width,int height)545 qboolean UI_CursorInRect( int x, int y, int width, int height ) {
546 if ( uiInfo.uiDC.cursorx < x ||
547 uiInfo.uiDC.cursory < y ||
548 uiInfo.uiDC.cursorx > x + width ||
549 uiInfo.uiDC.cursory > y + height ) {
550 return qfalse;
551 }
552
553 return qtrue;
554 }
555