2
===========================================================================
3
Copyright (C) 1999-2005 Id Software, Inc.
5
This file is part of Quake III Arena source code.
7
Quake III Arena source code is free software; you can redistribute it
8
and/or modify it under the terms of the GNU General Public License as
9
published by the Free Software Foundation; either version 2 of the License,
10
or (at your option) any later version.
12
Quake III Arena source code is distributed in the hope that it will be
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with Quake III Arena source code; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
===========================================================================
24
=============================================================================
26
SINGLE PLAYER POSTGAME MENU
28
=============================================================================
33
#define MAX_SCOREBOARD_CLIENTS 8
35
#define AWARD_PRESENTATION_TIME 2000
37
#define ART_MENU0 "menu/art/menu_0"
38
#define ART_MENU1 "menu/art/menu_1"
39
#define ART_REPLAY0 "menu/art/replay_0"
40
#define ART_REPLAY1 "menu/art/replay_1"
41
#define ART_NEXT0 "menu/art/next_0"
42
#define ART_NEXT1 "menu/art/next_1"
50
menubitmap_s item_again;
51
menubitmap_s item_next;
52
menubitmap_s item_menu;
60
int clientNums[MAX_SCOREBOARD_CLIENTS];
61
int ranks[MAX_SCOREBOARD_CLIENTS];
62
int scores[MAX_SCOREBOARD_CLIENTS];
64
char placeNames[3][64];
72
qboolean playedSound[6];
74
sfxHandle_t winnerSound;
77
static postgameMenuInfo_t postgameMenuInfo;
78
static char arenainfo[MAX_INFO_VALUE];
80
char *ui_medalNames[] = {"Accuracy", "Impressive", "Excellent", "Gauntlet", "Frags", "Perfect"};
81
char *ui_medalPicNames[] = {
82
"menu/medals/medal_accuracy",
83
"menu/medals/medal_impressive",
84
"menu/medals/medal_excellent",
85
"menu/medals/medal_gauntlet",
86
"menu/medals/medal_frags",
87
"menu/medals/medal_victory"
89
char *ui_medalSounds[] = {
90
"sound/feedback/accuracy.wav",
91
"sound/feedback/impressive_a.wav",
92
"sound/feedback/excellent_a.wav",
93
"sound/feedback/gauntlet.wav",
94
"sound/feedback/frags.wav",
95
"sound/feedback/perfect.wav"
101
UI_SPPostgameMenu_AgainEvent
104
static void UI_SPPostgameMenu_AgainEvent( void* ptr, int event )
106
if (event != QM_ACTIVATED) {
110
trap_Cmd_ExecuteText( EXEC_APPEND, "map_restart 0\n" );
116
UI_SPPostgameMenu_NextEvent
119
static void UI_SPPostgameMenu_NextEvent( void* ptr, int event ) {
124
const char *arenaInfo;
126
if (event != QM_ACTIVATED) {
131
// handle specially if we just won the training map
132
if( postgameMenuInfo.won == 0 ) {
136
level = postgameMenuInfo.level + 1;
138
levelSet = level / ARENAS_PER_TIER;
140
currentLevel = UI_GetCurrentGame();
141
if( currentLevel == -1 ) {
142
currentLevel = postgameMenuInfo.level;
144
currentSet = currentLevel / ARENAS_PER_TIER;
146
if( levelSet > currentSet || levelSet == UI_GetNumSPTiers() ) {
147
level = currentLevel;
150
arenaInfo = UI_GetArenaInfoByNumber( level );
155
UI_SPArena_Start( arenaInfo );
161
UI_SPPostgameMenu_MenuEvent
164
static void UI_SPPostgameMenu_MenuEvent( void* ptr, int event )
166
if (event != QM_ACTIVATED) {
170
trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; levelselect\n" );
176
UI_SPPostgameMenu_MenuKey
179
static sfxHandle_t UI_SPPostgameMenu_MenuKey( int key ) {
180
if ( uis.realtime < postgameMenuInfo.ignoreKeysTime ) {
184
if( postgameMenuInfo.phase == 1 ) {
185
trap_Cmd_ExecuteText( EXEC_APPEND, "abort_podium\n" );
186
postgameMenuInfo.phase = 2;
187
postgameMenuInfo.starttime = uis.realtime;
188
postgameMenuInfo.ignoreKeysTime = uis.realtime + 250;
192
if( postgameMenuInfo.phase == 2 ) {
193
postgameMenuInfo.phase = 3;
194
postgameMenuInfo.starttime = uis.realtime;
195
postgameMenuInfo.ignoreKeysTime = uis.realtime + 250;
199
if( key == K_ESCAPE || key == K_MOUSE2 ) {
203
return Menu_DefaultKey( &postgameMenuInfo.menu, key );
207
static int medalLocations[6] = {144, 448, 88, 504, 32, 560};
209
static void UI_SPPostgameMenu_DrawAwardsMedals( int max ) {
216
for( n = 0; n < max; n++ ) {
217
x = medalLocations[n];
219
medal = postgameMenuInfo.awardsEarned[n];
220
amount = postgameMenuInfo.awardsLevels[n];
222
UI_DrawNamedPic( x, y, 48, 48, ui_medalPicNames[medal] );
224
if( medal == AWARD_ACCURACY ) {
225
Com_sprintf( buf, sizeof(buf), "%i%%", amount );
231
Com_sprintf( buf, sizeof(buf), "%i", amount );
234
UI_DrawString( x + 24, y + 52, buf, UI_CENTER, color_yellow );
239
static void UI_SPPostgameMenu_DrawAwardsPresentation( int timer ) {
244
awardNum = timer / AWARD_PRESENTATION_TIME;
245
atimer = timer % AWARD_PRESENTATION_TIME;
247
color[0] = color[1] = color[2] = 1.0f;
248
color[3] = (float)( AWARD_PRESENTATION_TIME - atimer ) / (float)AWARD_PRESENTATION_TIME;
249
UI_DrawProportionalString( 320, 64, ui_medalNames[postgameMenuInfo.awardsEarned[awardNum]], UI_CENTER, color );
251
UI_SPPostgameMenu_DrawAwardsMedals( awardNum + 1 );
253
if( !postgameMenuInfo.playedSound[awardNum] ) {
254
postgameMenuInfo.playedSound[awardNum] = qtrue;
255
trap_S_StartLocalSound( trap_S_RegisterSound( ui_medalSounds[postgameMenuInfo.awardsEarned[awardNum]], qfalse ), CHAN_ANNOUNCER );
262
UI_SPPostgameMenu_MenuDrawScoreLine
265
static void UI_SPPostgameMenu_MenuDrawScoreLine( int n, int y ) {
268
char info[MAX_INFO_STRING];
270
if( n > (postgameMenuInfo.numClients + 1) ) {
271
n -= (postgameMenuInfo.numClients + 2);
274
if( n >= postgameMenuInfo.numClients ) {
278
rank = postgameMenuInfo.ranks[n];
279
if( rank & RANK_TIED_FLAG ) {
280
UI_DrawString( 640 - 31 * SMALLCHAR_WIDTH, y, "(tie)", UI_LEFT|UI_SMALLFONT, color_white );
281
rank &= ~RANK_TIED_FLAG;
283
trap_GetConfigString( CS_PLAYERS + postgameMenuInfo.clientNums[n], info, MAX_INFO_STRING );
284
Q_strncpyz( name, Info_ValueForKey( info, "n" ), sizeof(name) );
287
UI_DrawString( 640 - 25 * SMALLCHAR_WIDTH, y, va( "#%i: %-16s %2i", rank + 1, name, postgameMenuInfo.scores[n] ), UI_LEFT|UI_SMALLFONT, color_white );
293
UI_SPPostgameMenu_MenuDraw
296
static void UI_SPPostgameMenu_MenuDraw( void ) {
300
char info[MAX_INFO_STRING];
302
trap_GetConfigString( CS_SYSTEMINFO, info, sizeof(info) );
303
serverId = atoi( Info_ValueForKey( info, "sv_serverid" ) );
304
if( serverId != postgameMenuInfo.serverId ) {
310
if ( postgameMenuInfo.numClients > 2 ) {
311
UI_DrawProportionalString( 510, 480 - 64 - PROP_HEIGHT, postgameMenuInfo.placeNames[2], UI_CENTER, color_white );
313
UI_DrawProportionalString( 130, 480 - 64 - PROP_HEIGHT, postgameMenuInfo.placeNames[1], UI_CENTER, color_white );
314
UI_DrawProportionalString( 320, 480 - 64 - 2 * PROP_HEIGHT, postgameMenuInfo.placeNames[0], UI_CENTER, color_white );
316
if( postgameMenuInfo.phase == 1 ) {
317
timer = uis.realtime - postgameMenuInfo.starttime;
319
if( timer >= 1000 && postgameMenuInfo.winnerSound ) {
320
trap_S_StartLocalSound( postgameMenuInfo.winnerSound, CHAN_ANNOUNCER );
321
postgameMenuInfo.winnerSound = 0;
327
postgameMenuInfo.phase = 2;
328
postgameMenuInfo.starttime = uis.realtime;
332
if( postgameMenuInfo.phase == 2 ) {
333
timer = uis.realtime - postgameMenuInfo.starttime;
334
if( timer >= ( postgameMenuInfo.numAwards * AWARD_PRESENTATION_TIME ) ) {
340
postgameMenuInfo.phase = 3;
341
postgameMenuInfo.starttime = uis.realtime;
344
UI_SPPostgameMenu_DrawAwardsPresentation( timer );
349
if( postgameMenuInfo.phase == 3 ) {
350
if( uis.demoversion ) {
351
if( postgameMenuInfo.won == 1 && UI_ShowTierVideo( 8 )) {
352
trap_Cvar_Set( "nextmap", "" );
353
trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic demoEnd.RoQ\n" );
357
else if( postgameMenuInfo.won > -1 && UI_ShowTierVideo( postgameMenuInfo.won + 1 )) {
358
if( postgameMenuInfo.won == postgameMenuInfo.lastTier ) {
359
trap_Cvar_Set( "nextmap", "" );
360
trap_Cmd_ExecuteText( EXEC_APPEND, "disconnect; cinematic end.RoQ\n" );
364
trap_Cvar_SetValue( "ui_spSelection", postgameMenuInfo.won * ARENAS_PER_TIER );
365
trap_Cvar_Set( "nextmap", "levelselect" );
366
trap_Cmd_ExecuteText( EXEC_APPEND, va( "disconnect; cinematic tier%i.RoQ\n", postgameMenuInfo.won + 1 ) );
370
postgameMenuInfo.item_again.generic.flags &= ~QMF_INACTIVE;
371
postgameMenuInfo.item_next.generic.flags &= ~QMF_INACTIVE;
372
postgameMenuInfo.item_menu.generic.flags &= ~QMF_INACTIVE;
374
UI_SPPostgameMenu_DrawAwardsMedals( postgameMenuInfo.numAwards );
376
Menu_Draw( &postgameMenuInfo.menu );
379
// draw the scoreboard
380
if( !trap_Cvar_VariableValue( "ui_spScoreboard" ) ) {
384
timer = uis.realtime - postgameMenuInfo.scoreboardtime;
385
if( postgameMenuInfo.numClients <= 3 ) {
389
n = timer / 1500 % (postgameMenuInfo.numClients + 2);
391
UI_SPPostgameMenu_MenuDrawScoreLine( n, 0 );
392
UI_SPPostgameMenu_MenuDrawScoreLine( n + 1, 0 + SMALLCHAR_HEIGHT );
393
UI_SPPostgameMenu_MenuDrawScoreLine( n + 2, 0 + 2 * SMALLCHAR_HEIGHT );
399
UI_SPPostgameMenu_Cache
402
void UI_SPPostgameMenu_Cache( void ) {
404
qboolean buildscript;
406
buildscript = trap_Cvar_VariableValue("com_buildscript");
408
trap_R_RegisterShaderNoMip( ART_MENU0 );
409
trap_R_RegisterShaderNoMip( ART_MENU1 );
410
trap_R_RegisterShaderNoMip( ART_REPLAY0 );
411
trap_R_RegisterShaderNoMip( ART_REPLAY1 );
412
trap_R_RegisterShaderNoMip( ART_NEXT0 );
413
trap_R_RegisterShaderNoMip( ART_NEXT1 );
414
for( n = 0; n < 6; n++ ) {
415
trap_R_RegisterShaderNoMip( ui_medalPicNames[n] );
416
trap_S_RegisterSound( ui_medalSounds[n], qfalse );
420
trap_S_RegisterSound( "music/loss.wav", qfalse );
421
trap_S_RegisterSound( "music/win.wav", qfalse );
422
trap_S_RegisterSound( "sound/player/announce/youwin.wav", qfalse );
429
UI_SPPostgameMenu_Init
432
static void UI_SPPostgameMenu_Init( void ) {
433
postgameMenuInfo.menu.wrapAround = qtrue;
434
postgameMenuInfo.menu.key = UI_SPPostgameMenu_MenuKey;
435
postgameMenuInfo.menu.draw = UI_SPPostgameMenu_MenuDraw;
436
postgameMenuInfo.ignoreKeysTime = uis.realtime + 1500;
438
UI_SPPostgameMenu_Cache();
440
postgameMenuInfo.item_menu.generic.type = MTYPE_BITMAP;
441
postgameMenuInfo.item_menu.generic.name = ART_MENU0;
442
postgameMenuInfo.item_menu.generic.flags = QMF_LEFT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE;
443
postgameMenuInfo.item_menu.generic.x = 0;
444
postgameMenuInfo.item_menu.generic.y = 480-64;
445
postgameMenuInfo.item_menu.generic.callback = UI_SPPostgameMenu_MenuEvent;
446
postgameMenuInfo.item_menu.generic.id = ID_MENU;
447
postgameMenuInfo.item_menu.width = 128;
448
postgameMenuInfo.item_menu.height = 64;
449
postgameMenuInfo.item_menu.focuspic = ART_MENU1;
451
postgameMenuInfo.item_again.generic.type = MTYPE_BITMAP;
452
postgameMenuInfo.item_again.generic.name = ART_REPLAY0;
453
postgameMenuInfo.item_again.generic.flags = QMF_CENTER_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE;
454
postgameMenuInfo.item_again.generic.x = 320;
455
postgameMenuInfo.item_again.generic.y = 480-64;
456
postgameMenuInfo.item_again.generic.callback = UI_SPPostgameMenu_AgainEvent;
457
postgameMenuInfo.item_again.generic.id = ID_AGAIN;
458
postgameMenuInfo.item_again.width = 128;
459
postgameMenuInfo.item_again.height = 64;
460
postgameMenuInfo.item_again.focuspic = ART_REPLAY1;
462
postgameMenuInfo.item_next.generic.type = MTYPE_BITMAP;
463
postgameMenuInfo.item_next.generic.name = ART_NEXT0;
464
postgameMenuInfo.item_next.generic.flags = QMF_RIGHT_JUSTIFY|QMF_PULSEIFFOCUS|QMF_INACTIVE;
465
postgameMenuInfo.item_next.generic.x = 640;
466
postgameMenuInfo.item_next.generic.y = 480-64;
467
postgameMenuInfo.item_next.generic.callback = UI_SPPostgameMenu_NextEvent;
468
postgameMenuInfo.item_next.generic.id = ID_NEXT;
469
postgameMenuInfo.item_next.width = 128;
470
postgameMenuInfo.item_next.height = 64;
471
postgameMenuInfo.item_next.focuspic = ART_NEXT1;
473
Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_menu );
474
Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_again );
475
Menu_AddItem( &postgameMenuInfo.menu, ( void * )&postgameMenuInfo.item_next );
479
static void Prepname( int index ) {
482
char info[MAX_INFO_STRING];
484
trap_GetConfigString( CS_PLAYERS + postgameMenuInfo.clientNums[index], info, MAX_INFO_STRING );
485
Q_strncpyz( name, Info_ValueForKey( info, "n" ), sizeof(name) );
487
len = strlen( name );
489
while( len && UI_ProportionalStringWidth( name ) > 256 ) {
494
Q_strncpyz( postgameMenuInfo.placeNames[index], name, sizeof(postgameMenuInfo.placeNames[index]) );
503
void UI_SPPostgameMenu_f( void ) {
507
int oldFrags, newFrags;
511
char info[MAX_INFO_STRING];
513
memset( &postgameMenuInfo, 0, sizeof(postgameMenuInfo) );
515
trap_GetConfigString( CS_SYSTEMINFO, info, sizeof(info) );
516
postgameMenuInfo.serverId = atoi( Info_ValueForKey( info, "sv_serverid" ) );
518
trap_GetConfigString( CS_SERVERINFO, info, sizeof(info) );
519
Q_strncpyz( map, Info_ValueForKey( info, "mapname" ), sizeof(map) );
520
arena = UI_GetArenaInfoByMap( map );
524
Q_strncpyz( arenainfo, arena, sizeof(arenainfo) );
526
postgameMenuInfo.level = atoi( Info_ValueForKey( arenainfo, "num" ) );
528
postgameMenuInfo.numClients = atoi( UI_Argv( 1 ) );
529
playerClientNum = atoi( UI_Argv( 2 ) );
530
playerGameRank = 8; // in case they ended game as a spectator
532
if( postgameMenuInfo.numClients > MAX_SCOREBOARD_CLIENTS ) {
533
postgameMenuInfo.numClients = MAX_SCOREBOARD_CLIENTS;
536
for( n = 0; n < postgameMenuInfo.numClients; n++ ) {
537
postgameMenuInfo.clientNums[n] = atoi( UI_Argv( 8 + n * 3 + 1 ) );
538
postgameMenuInfo.ranks[n] = atoi( UI_Argv( 8 + n * 3 + 2 ) );
539
postgameMenuInfo.scores[n] = atoi( UI_Argv( 8 + n * 3 + 3 ) );
541
if( postgameMenuInfo.clientNums[n] == playerClientNum ) {
542
playerGameRank = (postgameMenuInfo.ranks[n] & ~RANK_TIED_FLAG) + 1;
546
UI_SetBestScore( postgameMenuInfo.level, playerGameRank );
548
// process award stats and prepare presentation data
549
awardValues[AWARD_ACCURACY] = atoi( UI_Argv( 3 ) );
550
awardValues[AWARD_IMPRESSIVE] = atoi( UI_Argv( 4 ) );
551
awardValues[AWARD_EXCELLENT] = atoi( UI_Argv( 5 ) );
552
awardValues[AWARD_GAUNTLET] = atoi( UI_Argv( 6 ) );
553
awardValues[AWARD_FRAGS] = atoi( UI_Argv( 7 ) );
554
awardValues[AWARD_PERFECT] = atoi( UI_Argv( 8 ) );
556
postgameMenuInfo.numAwards = 0;
558
if( awardValues[AWARD_ACCURACY] >= 50 ) {
559
UI_LogAwardData( AWARD_ACCURACY, 1 );
560
postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_ACCURACY;
561
postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_ACCURACY];
562
postgameMenuInfo.numAwards++;
565
if( awardValues[AWARD_IMPRESSIVE] ) {
566
UI_LogAwardData( AWARD_IMPRESSIVE, awardValues[AWARD_IMPRESSIVE] );
567
postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_IMPRESSIVE;
568
postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_IMPRESSIVE];
569
postgameMenuInfo.numAwards++;
572
if( awardValues[AWARD_EXCELLENT] ) {
573
UI_LogAwardData( AWARD_EXCELLENT, awardValues[AWARD_EXCELLENT] );
574
postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_EXCELLENT;
575
postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_EXCELLENT];
576
postgameMenuInfo.numAwards++;
579
if( awardValues[AWARD_GAUNTLET] ) {
580
UI_LogAwardData( AWARD_GAUNTLET, awardValues[AWARD_GAUNTLET] );
581
postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_GAUNTLET;
582
postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = awardValues[AWARD_GAUNTLET];
583
postgameMenuInfo.numAwards++;
586
oldFrags = UI_GetAwardLevel( AWARD_FRAGS ) / 100;
587
UI_LogAwardData( AWARD_FRAGS, awardValues[AWARD_FRAGS] );
588
newFrags = UI_GetAwardLevel( AWARD_FRAGS ) / 100;
589
if( newFrags > oldFrags ) {
590
postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_FRAGS;
591
postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = newFrags * 100;
592
postgameMenuInfo.numAwards++;
595
if( awardValues[AWARD_PERFECT] ) {
596
UI_LogAwardData( AWARD_PERFECT, 1 );
597
postgameMenuInfo.awardsEarned[postgameMenuInfo.numAwards] = AWARD_PERFECT;
598
postgameMenuInfo.awardsLevels[postgameMenuInfo.numAwards] = 1;
599
postgameMenuInfo.numAwards++;
602
if ( playerGameRank == 1 ) {
603
postgameMenuInfo.won = UI_TierCompleted( postgameMenuInfo.level );
606
postgameMenuInfo.won = -1;
609
postgameMenuInfo.starttime = uis.realtime;
610
postgameMenuInfo.scoreboardtime = uis.realtime;
612
trap_Key_SetCatcher( KEYCATCH_UI );
615
UI_SPPostgameMenu_Init();
616
UI_PushMenu( &postgameMenuInfo.menu );
618
if ( playerGameRank == 1 ) {
619
Menu_SetCursorToItem( &postgameMenuInfo.menu, &postgameMenuInfo.item_next );
622
Menu_SetCursorToItem( &postgameMenuInfo.menu, &postgameMenuInfo.item_again );
629
if ( playerGameRank != 1 ) {
630
postgameMenuInfo.winnerSound = trap_S_RegisterSound( va( "sound/player/announce/%s_wins.wav", postgameMenuInfo.placeNames[0] ), qfalse );
631
trap_Cmd_ExecuteText( EXEC_APPEND, "music music/loss\n" );
634
postgameMenuInfo.winnerSound = trap_S_RegisterSound( "sound/player/announce/youwin.wav", qfalse );
635
trap_Cmd_ExecuteText( EXEC_APPEND, "music music/win\n" );
638
postgameMenuInfo.phase = 1;
640
postgameMenuInfo.lastTier = UI_GetNumSPTiers();
641
if ( UI_GetSpecialArenaInfo( "final" ) ) {
642
postgameMenuInfo.lastTier++;