1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
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.
11 
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.
16 
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 ===========================================================================
21 */
22 // g_rankings.c -- reports for global rankings system
23 
24 #include "g_local.h"
25 #include "g_rankings.h"
26 
27 /*
28 ================
29 G_RankRunFrame
30 ================
31 */
G_RankRunFrame()32 void G_RankRunFrame()
33 {
34 	gentity_t*		ent;
35 	gentity_t*		ent2;
36 	grank_status_t	old_status;
37 	grank_status_t	status;
38 	int				time;
39 	int				i;
40 	int				j;
41 
42 	if( !trap_RankCheckInit() )
43 	{
44 		trap_RankBegin( GR_GAMEKEY );
45 	}
46 
47 	trap_RankPoll();
48 
49 	if( trap_RankActive() )
50 	{
51 		for( i = 0; i < level.maxclients; i++ )
52 		{
53 			ent = &(g_entities[i]);
54 			if ( !ent->inuse )
55 				continue;
56 			if ( ent->client == NULL )
57 				continue;
58 			if ( ent->r.svFlags & SVF_BOT)
59 			{
60 				// no bots in ranked games
61 				trap_SendConsoleCommand( EXEC_INSERT, va("kick %s\n",
62 					ent->client->pers.netname) );
63 				continue;
64 			}
65 
66 			old_status = ent->client->client_status;
67 			status = trap_RankUserStatus( i );
68 
69 			if( ent->client->client_status != status )
70 			{
71 				// inform client of current status
72 				// not needed for client side log in
73 				trap_SendServerCommand( i, va("rank_status %i\n",status) );
74 				if ( i == 0 )
75 				{
76 					int j = 0;
77 				}
78 				ent->client->client_status = status;
79 			}
80 
81 			switch( status )
82 			{
83 			case QGR_STATUS_NEW:
84 			case QGR_STATUS_SPECTATOR:
85 				if( ent->client->sess.sessionTeam != TEAM_SPECTATOR )
86 				{
87 					ent->client->sess.sessionTeam = TEAM_SPECTATOR;
88 					ent->client->sess.spectatorState = SPECTATOR_FREE;
89 					ClientSpawn( ent );
90 					// make sure by now CS_GRAND rankingsGameID is ready
91 					trap_SendServerCommand( i, va("rank_status %i\n",status) );
92 					trap_SendServerCommand( i, "rank_menu\n" );
93 				}
94 				break;
95 			case QGR_STATUS_NO_USER:
96 			case QGR_STATUS_BAD_PASSWORD:
97 			case QGR_STATUS_TIMEOUT:
98 			case QGR_STATUS_NO_MEMBERSHIP:
99 			case QGR_STATUS_INVALIDUSER:
100 			case QGR_STATUS_ERROR:
101 				if( (ent->r.svFlags & SVF_BOT) == 0 )
102 				{
103 					trap_RankUserReset( ent->s.clientNum );
104 				}
105 				break;
106 			case QGR_STATUS_ACTIVE:
107 				if( (ent->client->sess.sessionTeam == TEAM_SPECTATOR) &&
108 					(g_gametype.integer < GT_TEAM) )
109 				{
110 					SetTeam( ent, "free" );
111 				}
112 
113 				if( old_status != QGR_STATUS_ACTIVE )
114 				{
115 					// player has just become active
116 					for( j = 0; j < level.maxclients; j++ )
117 					{
118 						ent2 = &(g_entities[j]);
119 						if ( !ent2->inuse )
120 							continue;
121 						if ( ent2->client == NULL )
122 							continue;
123 						if ( ent2->r.svFlags & SVF_BOT)
124 							continue;
125 
126 						if( (i != j) && (trap_RankUserStatus( j ) == QGR_STATUS_ACTIVE) )
127 						{
128 							trap_RankReportInt( i, j, QGR_KEY_PLAYED_WITH, 1, 0 );
129 						}
130 
131 						// send current scores so the player's rank will show
132 						// up under the crosshair immediately
133 						DeathmatchScoreboardMessage( ent2 );
134 					}
135 				}
136 				break;
137 			default:
138 				break;
139 			}
140 		}
141 
142 		// don't let ranked games last forever
143 		if( ((g_fraglimit.integer == 0) || (g_fraglimit.integer > 100)) &&
144 			((g_timelimit.integer == 0) || (g_timelimit.integer > 1000)) )
145 		{
146 			trap_Cvar_Set( "timelimit", "1000" );
147 		}
148 	}
149 
150 	// tell time to clients so they can show current match rating
151 	if( level.intermissiontime == 0 )
152 	{
153 		for( i = 0; i < level.maxclients; i++ )
154 		{
155 			ent = &(g_entities[i]);
156 			if( ent->client == NULL )
157 			{
158 				continue;
159 			}
160 
161 			time = (level.time - ent->client->pers.enterTime) / 1000;
162 			ent->client->ps.persistant[PERS_MATCH_TIME] = time;
163 		}
164 	}
165 }
166 
167 /*
168 ================
169 G_RankFireWeapon
170 ================
171 */
G_RankFireWeapon(int self,int weapon)172 void G_RankFireWeapon( int self, int weapon )
173 {
174 	if( level.warmupTime != 0 )
175 	{
176 		// no reports during warmup period
177 		return;
178 	}
179 
180 	if( weapon == WP_GAUNTLET )
181 	{
182 		// the gauntlet only "fires" when it actually hits something
183 		return;
184 	}
185 
186 	trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED, 1, 1 );
187 
188 	switch( weapon )
189 	{
190 	case WP_MACHINEGUN:
191 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_MACHINEGUN, 1, 1 );
192 		break;
193 	case WP_SHOTGUN:
194 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_SHOTGUN, 1, 1 );
195 		break;
196 	case WP_GRENADE_LAUNCHER:
197 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_GRENADE, 1, 1 );
198 		break;
199 	case WP_ROCKET_LAUNCHER:
200 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_ROCKET, 1, 1 );
201 		break;
202 	case WP_LIGHTNING:
203 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_LIGHTNING, 1, 1 );
204 		break;
205 	case WP_RAILGUN:
206 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_RAILGUN, 1, 1 );
207 		break;
208 	case WP_PLASMAGUN:
209 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_PLASMA, 1, 1 );
210 		break;
211 	case WP_BFG:
212 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_BFG, 1, 1 );
213 		break;
214 	case WP_GRAPPLING_HOOK:
215 		trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_GRAPPLE, 1, 1 );
216 		break;
217 	default:
218 		break;
219 	}
220 }
221 
222 /*
223 ================
224 G_RankDamage
225 ================
226 */
G_RankDamage(int self,int attacker,int damage,int means_of_death)227 void G_RankDamage( int self, int attacker, int damage, int means_of_death )
228 {
229 	// state information to avoid counting each shotgun pellet as a hit
230 	static int	last_framenum = -1;
231 	static int	last_self = -1;
232 	static int	last_attacker = -1;
233 	static int	last_means_of_death = MOD_UNKNOWN;
234 
235 	qboolean	new_hit;
236 	int			splash;
237 	int			key_hit;
238 	int			key_damage;
239 	int			key_splash;
240 
241 	if( level.warmupTime != 0 )
242 	{
243 		// no reports during warmup period
244 		return;
245 	}
246 
247 	new_hit = (level.framenum != last_framenum) ||
248 		(self != last_self) ||
249 		(attacker != last_attacker) ||
250 		(means_of_death != last_means_of_death);
251 
252 	// update state information
253 	last_framenum = level.framenum;
254 	last_self = self;
255 	last_attacker = attacker;
256 	last_means_of_death = means_of_death;
257 
258 	// the gauntlet only "fires" when it actually hits something
259 	if( (attacker != ENTITYNUM_WORLD) && (attacker != self) &&
260 		(means_of_death == MOD_GAUNTLET)  &&
261 		(g_entities[attacker].client) )
262 	{
263 		trap_RankReportInt( attacker, -1, QGR_KEY_SHOT_FIRED_GAUNTLET, 1, 1 );
264 	}
265 
266 	// don't track hazard damage, just deaths
267 	switch( means_of_death )
268 	{
269 	case MOD_WATER:
270 	case MOD_SLIME:
271 	case MOD_LAVA:
272 	case MOD_CRUSH:
273 	case MOD_TELEFRAG:
274 	case MOD_FALLING:
275 	case MOD_SUICIDE:
276 	case MOD_TRIGGER_HURT:
277 		return;
278 	default:
279 		break;
280 	}
281 
282 	// get splash damage
283 	switch( means_of_death )
284 	{
285 	case MOD_GRENADE_SPLASH:
286 	case MOD_ROCKET_SPLASH:
287 	case MOD_PLASMA_SPLASH:
288 	case MOD_BFG_SPLASH:
289 		splash = damage;
290 		break;
291 	default:
292 		splash = 0;
293 		key_splash = -1;
294 		break;
295 	}
296 
297 	// hit, damage, and splash taken
298 	switch( means_of_death )
299 	{
300 	case MOD_GAUNTLET:
301 		key_hit = QGR_KEY_HIT_TAKEN_GAUNTLET;
302 		key_damage = QGR_KEY_DAMAGE_TAKEN_GAUNTLET;
303 		break;
304 	case MOD_MACHINEGUN:
305 		key_hit = QGR_KEY_HIT_TAKEN_MACHINEGUN;
306 		key_damage = QGR_KEY_DAMAGE_TAKEN_MACHINEGUN;
307 		break;
308 	case MOD_SHOTGUN:
309 		key_hit = QGR_KEY_HIT_TAKEN_SHOTGUN;
310 		key_damage = QGR_KEY_DAMAGE_TAKEN_SHOTGUN;
311 		break;
312 	case MOD_GRENADE:
313 	case MOD_GRENADE_SPLASH:
314 		key_hit = QGR_KEY_HIT_TAKEN_GRENADE;
315 		key_damage = QGR_KEY_DAMAGE_TAKEN_GRENADE;
316 		key_splash = QGR_KEY_SPLASH_TAKEN_GRENADE;
317 		break;
318 	case MOD_ROCKET:
319 	case MOD_ROCKET_SPLASH:
320 		key_hit = QGR_KEY_HIT_TAKEN_ROCKET;
321 		key_damage = QGR_KEY_DAMAGE_TAKEN_ROCKET;
322 		key_splash = QGR_KEY_SPLASH_TAKEN_ROCKET;
323 		break;
324 	case MOD_PLASMA:
325 	case MOD_PLASMA_SPLASH:
326 		key_hit = QGR_KEY_HIT_TAKEN_PLASMA;
327 		key_damage = QGR_KEY_DAMAGE_TAKEN_PLASMA;
328 		key_splash = QGR_KEY_SPLASH_TAKEN_PLASMA;
329 		break;
330 	case MOD_RAILGUN:
331 		key_hit = QGR_KEY_HIT_TAKEN_RAILGUN;
332 		key_damage = QGR_KEY_DAMAGE_TAKEN_RAILGUN;
333 		break;
334 	case MOD_LIGHTNING:
335 		key_hit = QGR_KEY_HIT_TAKEN_LIGHTNING;
336 		key_damage = QGR_KEY_DAMAGE_TAKEN_LIGHTNING;
337 		break;
338 	case MOD_BFG:
339 	case MOD_BFG_SPLASH:
340 		key_hit = QGR_KEY_HIT_TAKEN_BFG;
341 		key_damage = QGR_KEY_DAMAGE_TAKEN_BFG;
342 		key_splash = QGR_KEY_SPLASH_TAKEN_BFG;
343 		break;
344 	case MOD_GRAPPLE:
345 		key_hit = QGR_KEY_HIT_TAKEN_GRAPPLE;
346 		key_damage = QGR_KEY_DAMAGE_TAKEN_GRAPPLE;
347 		break;
348 	default:
349 		key_hit = QGR_KEY_HIT_TAKEN_UNKNOWN;
350 		key_damage = QGR_KEY_DAMAGE_TAKEN_UNKNOWN;
351 		break;
352 	}
353 
354 	// report general and specific hit taken
355 	if( new_hit )
356 	{
357 		trap_RankReportInt( self, -1, QGR_KEY_HIT_TAKEN, 1, 1 );
358 		trap_RankReportInt( self, -1, key_hit, 1, 1 );
359 	}
360 
361 	// report general and specific damage taken
362 	trap_RankReportInt( self, -1, QGR_KEY_DAMAGE_TAKEN, damage, 1 );
363 	trap_RankReportInt( self, -1, key_damage, damage, 1 );
364 
365 	// report general and specific splash taken
366 	if( splash != 0 )
367 	{
368 		trap_RankReportInt( self, -1, QGR_KEY_SPLASH_TAKEN, splash, 1 );
369 		trap_RankReportInt( self, -1, key_splash, splash, 1 );
370 	}
371 
372 	// hit, damage, and splash given
373 	if( (attacker != ENTITYNUM_WORLD) && (attacker != self) )
374 	{
375 		switch( means_of_death )
376 		{
377 		case MOD_GAUNTLET:
378 			key_hit = QGR_KEY_HIT_GIVEN_GAUNTLET;
379 			key_damage = QGR_KEY_DAMAGE_GIVEN_GAUNTLET;
380 			break;
381 		case MOD_MACHINEGUN:
382 			key_hit = QGR_KEY_HIT_GIVEN_MACHINEGUN;
383 			key_damage = QGR_KEY_DAMAGE_GIVEN_MACHINEGUN;
384 			break;
385 		case MOD_SHOTGUN:
386 			key_hit = QGR_KEY_HIT_GIVEN_SHOTGUN;
387 			key_damage = QGR_KEY_DAMAGE_GIVEN_SHOTGUN;
388 			break;
389 		case MOD_GRENADE:
390 		case MOD_GRENADE_SPLASH:
391 			key_hit = QGR_KEY_HIT_GIVEN_GRENADE;
392 			key_damage = QGR_KEY_DAMAGE_GIVEN_GRENADE;
393 			key_splash = QGR_KEY_SPLASH_GIVEN_GRENADE;
394 			break;
395 		case MOD_ROCKET:
396 		case MOD_ROCKET_SPLASH:
397 			key_hit = QGR_KEY_HIT_GIVEN_ROCKET;
398 			key_damage = QGR_KEY_DAMAGE_GIVEN_ROCKET;
399 			key_splash = QGR_KEY_SPLASH_GIVEN_ROCKET;
400 			break;
401 		case MOD_PLASMA:
402 		case MOD_PLASMA_SPLASH:
403 			key_hit = QGR_KEY_HIT_GIVEN_PLASMA;
404 			key_damage = QGR_KEY_DAMAGE_GIVEN_PLASMA;
405 			key_splash = QGR_KEY_SPLASH_GIVEN_PLASMA;
406 			break;
407 		case MOD_RAILGUN:
408 			key_hit = QGR_KEY_HIT_GIVEN_RAILGUN;
409 			key_damage = QGR_KEY_DAMAGE_GIVEN_RAILGUN;
410 			break;
411 		case MOD_LIGHTNING:
412 			key_hit = QGR_KEY_HIT_GIVEN_LIGHTNING;
413 			key_damage = QGR_KEY_DAMAGE_GIVEN_LIGHTNING;
414 			break;
415 		case MOD_BFG:
416 		case MOD_BFG_SPLASH:
417 			key_hit = QGR_KEY_HIT_GIVEN_BFG;
418 			key_damage = QGR_KEY_DAMAGE_GIVEN_BFG;
419 			key_splash = QGR_KEY_SPLASH_GIVEN_BFG;
420 			break;
421 		case MOD_GRAPPLE:
422 			key_hit = QGR_KEY_HIT_GIVEN_GRAPPLE;
423 			key_damage = QGR_KEY_DAMAGE_GIVEN_GRAPPLE;
424 			break;
425 		default:
426 			key_hit = QGR_KEY_HIT_GIVEN_UNKNOWN;
427 			key_damage = QGR_KEY_DAMAGE_GIVEN_UNKNOWN;
428 			break;
429 		}
430 
431 		// report general and specific hit given
432 		// jwu 8/26/00
433 		// had a case where attacker is 245 which is grnadeshooter attacker is
434 		// g_entities index not necessarilly clientnum
435 		if (g_entities[attacker].client) {
436 			if( new_hit )
437 			{
438 				trap_RankReportInt( attacker, -1, QGR_KEY_HIT_GIVEN, 1, 1 );
439 				trap_RankReportInt( attacker, -1, key_hit, 1, 1 );
440 			}
441 
442 			// report general and specific damage given
443 			trap_RankReportInt( attacker, -1, QGR_KEY_DAMAGE_GIVEN, damage, 1 );
444 			trap_RankReportInt( attacker, -1, key_damage, damage, 1 );
445 
446 			// report general and specific splash given
447 			if( splash != 0 )
448 			{
449 				trap_RankReportInt( attacker, -1, QGR_KEY_SPLASH_GIVEN, splash, 1 );
450 				trap_RankReportInt( attacker, -1, key_splash, splash, 1 );
451 			}
452 		}
453 	}
454 
455 	// friendly fire
456 	if( (attacker != self) &&
457 		OnSameTeam( &(g_entities[self]), &(g_entities[attacker])) &&
458 		(g_entities[attacker].client) )
459 	{
460 		// report teammate hit
461 		if( new_hit )
462 		{
463 			trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_HIT_TAKEN, 1, 1 );
464 			trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_HIT_GIVEN, 1,
465 				1 );
466 		}
467 
468 		// report teammate damage
469 		trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_DAMAGE_TAKEN, damage,
470 			1 );
471 		trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_DAMAGE_GIVEN,
472 			damage, 1 );
473 
474 		// report teammate splash
475 		if( splash != 0 )
476 		{
477 			trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_SPLASH_TAKEN,
478 				splash, 1 );
479 			trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_SPLASH_GIVEN,
480 				splash, 1 );
481 		}
482 	}
483 }
484 
485 /*
486 ================
487 G_RankPlayerDie
488 ================
489 */
G_RankPlayerDie(int self,int attacker,int means_of_death)490 void G_RankPlayerDie( int self, int attacker, int means_of_death )
491 {
492 	int	p1;
493 	int	p2;
494 
495 	if( level.warmupTime != 0 )
496 	{
497 		// no reports during warmup period
498 		return;
499 	}
500 
501 	if( attacker == ENTITYNUM_WORLD )
502 	{
503 		p1 = self;
504 		p2 = -1;
505 
506 		trap_RankReportInt( p1, p2, QGR_KEY_HAZARD_DEATH, 1, 1 );
507 
508 		switch( means_of_death )
509 		{
510 		case MOD_WATER:
511 			trap_RankReportInt( p1, p2, QGR_KEY_WATER, 1, 1 );
512 			break;
513 		case MOD_SLIME:
514 			trap_RankReportInt( p1, p2, QGR_KEY_SLIME, 1, 1 );
515 			break;
516 		case MOD_LAVA:
517 			trap_RankReportInt( p1, p2, QGR_KEY_LAVA, 1, 1 );
518 			break;
519 		case MOD_CRUSH:
520 			trap_RankReportInt( p1, p2, QGR_KEY_CRUSH, 1, 1 );
521 			break;
522 		case MOD_TELEFRAG:
523 			trap_RankReportInt( p1, p2, QGR_KEY_TELEFRAG, 1, 1 );
524 			break;
525 		case MOD_FALLING:
526 			trap_RankReportInt( p1, p2, QGR_KEY_FALLING, 1, 1 );
527 			break;
528 		case MOD_SUICIDE:
529 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_CMD, 1, 1 );
530 			break;
531 		case MOD_TRIGGER_HURT:
532 			trap_RankReportInt( p1, p2, QGR_KEY_TRIGGER_HURT, 1, 1 );
533 			break;
534 		default:
535 			trap_RankReportInt( p1, p2, QGR_KEY_HAZARD_MISC, 1, 1 );
536 			break;
537 		}
538 	}
539 	else if( attacker == self )
540 	{
541 		p1 = self;
542 		p2 = -1;
543 
544 		trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE, 1, 1 );
545 
546 		switch( means_of_death )
547 		{
548 		case MOD_GAUNTLET:
549 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GAUNTLET, 1, 1 );
550 			break;
551 		case MOD_MACHINEGUN:
552 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_MACHINEGUN, 1, 1 );
553 			break;
554 		case MOD_SHOTGUN:
555 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_SHOTGUN, 1, 1 );
556 			break;
557 		case MOD_GRENADE:
558 		case MOD_GRENADE_SPLASH:
559 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GRENADE, 1, 1 );
560 			break;
561 		case MOD_ROCKET:
562 		case MOD_ROCKET_SPLASH:
563 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_ROCKET, 1, 1 );
564 			break;
565 		case MOD_PLASMA:
566 		case MOD_PLASMA_SPLASH:
567 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_PLASMA, 1, 1 );
568 			break;
569 		case MOD_RAILGUN:
570 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_RAILGUN, 1, 1 );
571 			break;
572 		case MOD_LIGHTNING:
573 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_LIGHTNING, 1, 1 );
574 			break;
575 		case MOD_BFG:
576 		case MOD_BFG_SPLASH:
577 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_BFG, 1, 1 );
578 			break;
579 		case MOD_GRAPPLE:
580 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GRAPPLE, 1, 1 );
581 			break;
582 		default:
583 			trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_UNKNOWN, 1, 1 );
584 			break;
585 		}
586 	}
587 	else
588 	{
589 		p1 = attacker;
590 		p2 = self;
591 
592 		trap_RankReportInt( p1, p2, QGR_KEY_FRAG, 1, 1 );
593 
594 		switch( means_of_death )
595 		{
596 		case MOD_GAUNTLET:
597 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GAUNTLET, 1, 1 );
598 			break;
599 		case MOD_MACHINEGUN:
600 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_MACHINEGUN, 1, 1 );
601 			break;
602 		case MOD_SHOTGUN:
603 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_SHOTGUN, 1, 1 );
604 			break;
605 		case MOD_GRENADE:
606 		case MOD_GRENADE_SPLASH:
607 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GRENADE, 1, 1 );
608 			break;
609 		case MOD_ROCKET:
610 		case MOD_ROCKET_SPLASH:
611 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_ROCKET, 1, 1 );
612 			break;
613 		case MOD_PLASMA:
614 		case MOD_PLASMA_SPLASH:
615 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_PLASMA, 1, 1 );
616 			break;
617 		case MOD_RAILGUN:
618 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_RAILGUN, 1, 1 );
619 			break;
620 		case MOD_LIGHTNING:
621 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_LIGHTNING, 1, 1 );
622 			break;
623 		case MOD_BFG:
624 		case MOD_BFG_SPLASH:
625 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_BFG, 1, 1 );
626 			break;
627 		case MOD_GRAPPLE:
628 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GRAPPLE, 1, 1 );
629 			break;
630 		default:
631 			trap_RankReportInt( p1, p2, QGR_KEY_FRAG_UNKNOWN, 1, 1 );
632 			break;
633 		}
634 	}
635 }
636 
637 /*
638 ================
639 G_RankWeaponTime
640 ================
641 */
G_RankWeaponTime(int self,int weapon)642 void G_RankWeaponTime( int self, int weapon )
643 {
644 	gclient_t*	client;
645 	int			time;
646 
647 	if( level.warmupTime != 0 )
648 	{
649 		// no reports during warmup period
650 		return;
651 	}
652 
653 	client = g_entities[self].client;
654 	time = (level.time - client->weapon_change_time) / 1000;
655 	client->weapon_change_time = level.time;
656 
657 	if( time <= 0 )
658 	{
659 		return;
660 	}
661 
662 	trap_RankReportInt( self, -1, QGR_KEY_TIME, time, 1 );
663 
664 	switch( weapon )
665 	{
666 	case WP_GAUNTLET:
667 		trap_RankReportInt( self, -1, QGR_KEY_TIME_GAUNTLET, time, 1 );
668 		break;
669 	case WP_MACHINEGUN:
670 		trap_RankReportInt( self, -1, QGR_KEY_TIME_MACHINEGUN, time, 1 );
671 		break;
672 	case WP_SHOTGUN:
673 		trap_RankReportInt( self, -1, QGR_KEY_TIME_SHOTGUN, time, 1 );
674 		break;
675 	case WP_GRENADE_LAUNCHER:
676 		trap_RankReportInt( self, -1, QGR_KEY_TIME_GRENADE, time, 1 );
677 		break;
678 	case WP_ROCKET_LAUNCHER:
679 		trap_RankReportInt( self, -1, QGR_KEY_TIME_ROCKET, time, 1 );
680 		break;
681 	case WP_LIGHTNING:
682 		trap_RankReportInt( self, -1, QGR_KEY_TIME_LIGHTNING, time, 1 );
683 		break;
684 	case WP_RAILGUN:
685 		trap_RankReportInt( self, -1, QGR_KEY_TIME_RAILGUN, time, 1 );
686 		break;
687 	case WP_PLASMAGUN:
688 		trap_RankReportInt( self, -1, QGR_KEY_TIME_PLASMA, time, 1 );
689 		break;
690 	case WP_BFG:
691 		trap_RankReportInt( self, -1, QGR_KEY_TIME_BFG, time, 1 );
692 		break;
693 	case WP_GRAPPLING_HOOK:
694 		trap_RankReportInt( self, -1, QGR_KEY_TIME_GRAPPLE, time, 1 );
695 		break;
696 	default:
697 		break;
698 	}
699 }
700 
701 /*
702 ================
703 G_RankPickupWeapon
704 ================
705 */
G_RankPickupWeapon(int self,int weapon)706 void G_RankPickupWeapon( int self, int weapon )
707 {
708 	if( level.warmupTime != 0 )
709 	{
710 		// no reports during warmup period
711 		return;
712 	}
713 
714 	trap_RankReportInt( self, -1, QGR_KEY_PICKUP_WEAPON, 1, 1 );
715 	switch( weapon )
716 	{
717 	case WP_GAUNTLET:
718 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GAUNTLET, 1, 1 );
719 		break;
720 	case WP_MACHINEGUN:
721 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_MACHINEGUN, 1, 1 );
722 		break;
723 	case WP_SHOTGUN:
724 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_SHOTGUN, 1, 1 );
725 		break;
726 	case WP_GRENADE_LAUNCHER:
727 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GRENADE, 1, 1 );
728 		break;
729 	case WP_ROCKET_LAUNCHER:
730 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_ROCKET, 1, 1 );
731 		break;
732 	case WP_LIGHTNING:
733 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_LIGHTNING, 1, 1 );
734 		break;
735 	case WP_RAILGUN:
736 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_RAILGUN, 1, 1 );
737 		break;
738 	case WP_PLASMAGUN:
739 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_PLASMA, 1, 1 );
740 		break;
741 	case WP_BFG:
742 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_BFG, 1, 1 );
743 		break;
744 	case WP_GRAPPLING_HOOK:
745 		trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GRAPPLE, 1, 1 );
746 		break;
747 	default:
748 		break;
749 	}
750 }
751 
752 /*
753 ================
754 G_RankPickupAmmo
755 ================
756 */
G_RankPickupAmmo(int self,int weapon,int quantity)757 void G_RankPickupAmmo( int self, int weapon, int quantity )
758 {
759 	if( level.warmupTime != 0 )
760 	{
761 		// no reports during warmup period
762 		return;
763 	}
764 
765 	trap_RankReportInt( self, -1, QGR_KEY_BOXES, 1, 1 );
766 	trap_RankReportInt( self, -1, QGR_KEY_ROUNDS, quantity, 1 );
767 
768 	switch( weapon )
769 	{
770 	case WP_MACHINEGUN:
771 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_BULLETS, 1, 1 );
772 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_BULLETS, quantity, 1 );
773 		break;
774 	case WP_SHOTGUN:
775 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_SHELLS, 1, 1 );
776 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_SHELLS, quantity, 1 );
777 		break;
778 	case WP_GRENADE_LAUNCHER:
779 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_GRENADES, 1, 1 );
780 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_GRENADES, quantity, 1 );
781 		break;
782 	case WP_ROCKET_LAUNCHER:
783 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_ROCKETS, 1, 1 );
784 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_ROCKETS, quantity, 1 );
785 		break;
786 	case WP_LIGHTNING:
787 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_LG_AMMO, 1, 1 );
788 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_LG_AMMO, quantity, 1 );
789 		break;
790 	case WP_RAILGUN:
791 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_SLUGS, 1, 1 );
792 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_SLUGS, quantity, 1 );
793 		break;
794 	case WP_PLASMAGUN:
795 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_CELLS, 1, 1 );
796 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_CELLS, quantity, 1 );
797 		break;
798 	case WP_BFG:
799 		trap_RankReportInt( self, -1, QGR_KEY_BOXES_BFG_AMMO, 1, 1 );
800 		trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_BFG_AMMO, quantity, 1 );
801 		break;
802 	default:
803 		break;
804 	}
805 }
806 
807 /*
808 ================
809 G_RankPickupHealth
810 ================
811 */
G_RankPickupHealth(int self,int quantity)812 void G_RankPickupHealth( int self, int quantity )
813 {
814 	if( level.warmupTime != 0 )
815 	{
816 		// no reports during warmup period
817 		return;
818 	}
819 
820 	trap_RankReportInt( self, -1, QGR_KEY_HEALTH, 1, 1 );
821 	trap_RankReportInt( self, -1, QGR_KEY_HEALTH_TOTAL, quantity, 1 );
822 
823 	switch( quantity )
824 	{
825 	case 5:
826 		trap_RankReportInt( self, -1, QGR_KEY_HEALTH_5, 1, 1 );
827 		break;
828 	case 25:
829 		trap_RankReportInt( self, -1, QGR_KEY_HEALTH_25, 1, 1 );
830 		break;
831 	case 50:
832 		trap_RankReportInt( self, -1, QGR_KEY_HEALTH_50, 1, 1 );
833 		break;
834 	case 100:
835 		trap_RankReportInt( self, -1, QGR_KEY_HEALTH_MEGA, 1, 1 );
836 		break;
837 	default:
838 		break;
839 	}
840 }
841 
842 /*
843 ================
844 G_RankPickupArmor
845 ================
846 */
G_RankPickupArmor(int self,int quantity)847 void G_RankPickupArmor( int self, int quantity )
848 {
849 	if( level.warmupTime != 0 )
850 	{
851 		// no reports during warmup period
852 		return;
853 	}
854 
855 	trap_RankReportInt( self, -1, QGR_KEY_ARMOR, 1, 1 );
856 	trap_RankReportInt( self, -1, QGR_KEY_ARMOR_TOTAL, quantity, 1 );
857 
858 	switch( quantity )
859 	{
860 	case 5:
861 		trap_RankReportInt( self, -1, QGR_KEY_ARMOR_SHARD, 1, 1 );
862 		break;
863 	case 50:
864 		trap_RankReportInt( self, -1, QGR_KEY_ARMOR_YELLOW, 1, 1 );
865 		break;
866 	case 100:
867 		trap_RankReportInt( self, -1, QGR_KEY_ARMOR_RED, 1, 1 );
868 		break;
869 	default:
870 		break;
871 	}
872 }
873 
874 /*
875 ================
876 G_RankPickupPowerup
877 ================
878 */
G_RankPickupPowerup(int self,int powerup)879 void G_RankPickupPowerup( int self, int powerup )
880 {
881 	if( level.warmupTime != 0 )
882 	{
883 		// no reports during warmup period
884 		return;
885 	}
886 
887 	// ctf flags are treated as powerups
888 	if( (powerup == PW_REDFLAG) || (powerup == PW_BLUEFLAG) )
889 	{
890 		trap_RankReportInt( self, -1, QGR_KEY_FLAG_PICKUP, 1, 1 );
891 		return;
892 	}
893 
894 	trap_RankReportInt( self, -1, QGR_KEY_POWERUP, 1, 1 );
895 
896 	switch( powerup )
897 	{
898 	case PW_QUAD:
899 		trap_RankReportInt( self, -1, QGR_KEY_QUAD, 1, 1 );
900 		break;
901 	case PW_BATTLESUIT:
902 		trap_RankReportInt( self, -1, QGR_KEY_SUIT, 1, 1 );
903 		break;
904 	case PW_HASTE:
905 		trap_RankReportInt( self, -1, QGR_KEY_HASTE, 1, 1 );
906 		break;
907 	case PW_INVIS:
908 		trap_RankReportInt( self, -1, QGR_KEY_INVIS, 1, 1 );
909 		break;
910 	case PW_REGEN:
911 		trap_RankReportInt( self, -1, QGR_KEY_REGEN, 1, 1 );
912 		break;
913 	case PW_FLIGHT:
914 		trap_RankReportInt( self, -1, QGR_KEY_FLIGHT, 1, 1 );
915 		break;
916 	default:
917 		break;
918 	}
919 }
920 
921 /*
922 ================
923 G_RankPickupHoldable
924 ================
925 */
G_RankPickupHoldable(int self,int holdable)926 void G_RankPickupHoldable( int self, int holdable )
927 {
928 	if( level.warmupTime != 0 )
929 	{
930 		// no reports during warmup period
931 		return;
932 	}
933 
934 	switch( holdable )
935 	{
936 	case HI_MEDKIT:
937 		trap_RankReportInt( self, -1, QGR_KEY_MEDKIT, 1, 1 );
938 		break;
939 	case HI_TELEPORTER:
940 		trap_RankReportInt( self, -1, QGR_KEY_TELEPORTER, 1, 1 );
941 		break;
942 	default:
943 		break;
944 	}
945 }
946 
947 /*
948 ================
949 G_RankUseHoldable
950 ================
951 */
G_RankUseHoldable(int self,int holdable)952 void G_RankUseHoldable( int self, int holdable )
953 {
954 	if( level.warmupTime != 0 )
955 	{
956 		// no reports during warmup period
957 		return;
958 	}
959 
960 	switch( holdable )
961 	{
962 	case HI_MEDKIT:
963 		trap_RankReportInt( self, -1, QGR_KEY_MEDKIT_USE, 1, 1 );
964 		break;
965 	case HI_TELEPORTER:
966 		trap_RankReportInt( self, -1, QGR_KEY_TELEPORTER_USE, 1, 1 );
967 		break;
968 	default:
969 		break;
970 	}
971 }
972 
973 /*
974 ================
975 G_RankReward
976 ================
977 */
G_RankReward(int self,int award)978 void G_RankReward( int self, int award )
979 {
980 	if( level.warmupTime != 0 )
981 	{
982 		// no reports during warmup period
983 		return;
984 	}
985 
986 	switch( award )
987 	{
988 	case EF_AWARD_IMPRESSIVE:
989 		trap_RankReportInt( self, -1, QGR_KEY_IMPRESSIVE, 1, 1 );
990 		break;
991 	case EF_AWARD_EXCELLENT:
992 		trap_RankReportInt( self, -1, QGR_KEY_EXCELLENT, 1, 1 );
993 		break;
994 	default:
995 		break;
996 	}
997 }
998 
999 /*
1000 ================
1001 G_RankCapture
1002 ================
1003 */
G_RankCapture(int self)1004 void G_RankCapture( int self )
1005 {
1006 	if( level.warmupTime != 0 )
1007 	{
1008 		// no reports during warmup period
1009 		return;
1010 	}
1011 
1012 	trap_RankReportInt( self, -1, QGR_KEY_FLAG_CAPTURE, 1, 1 );
1013 }
1014 
1015 /*
1016 ================
1017 G_RankUserTeamName
1018 ================
1019 */
G_RankUserTeamName(int self,char * team_name)1020 void G_RankUserTeamName( int self, char* team_name )
1021 {
1022 	if( level.warmupTime != 0 )
1023 	{
1024 		// no reports during warmup period
1025 		return;
1026 	}
1027 
1028 	trap_RankReportStr( self, -1, QGR_KEY_TEAM_NAME, team_name );
1029 }
1030 
1031 /*
1032 ================
1033 G_RankClientDisconnect
1034 ================
1035 */
G_RankClientDisconnect(int self)1036 void G_RankClientDisconnect( int self )
1037 {
1038 	gclient_t*	client;
1039 	int			time;
1040 	int			match_rating;
1041 
1042 	if( level.warmupTime != 0 )
1043 	{
1044 		// no reports during warmup period
1045 		return;
1046 	}
1047 
1048 	// match rating
1049 	client = g_entities[self].client;
1050 	time = (level.time - client->pers.enterTime) / 1000;
1051 	if( time < 60 )
1052 	{
1053 		match_rating = 0;
1054 	}
1055 	else
1056 	{
1057 		match_rating = client->ps.persistant[PERS_MATCH_RATING] / time;
1058 	}
1059 	trap_RankReportInt( self, -1, QGR_KEY_MATCH_RATING, match_rating, 0 );
1060 }
1061 
1062 /*
1063 ================
1064 G_RankGameOver
1065 ================
1066 */
G_RankGameOver(void)1067 void G_RankGameOver( void )
1068 {
1069 	int		i;
1070 	char	str[MAX_INFO_VALUE];
1071 	int		num;
1072 
1073 	if( level.warmupTime != 0 )
1074 	{
1075 		// no reports during warmup period
1076 		return;
1077 	}
1078 
1079 	for( i = 0; i < level.maxclients; i++ )
1080 	{
1081 		if( trap_RankUserStatus( i ) == QGR_STATUS_ACTIVE )
1082 		{
1083 			G_RankClientDisconnect( i );
1084 		}
1085 	}
1086 
1087 	// hostname
1088 	trap_Cvar_VariableStringBuffer( "sv_hostname", str, sizeof(str) );
1089 	trap_RankReportStr( -1, -1, QGR_KEY_HOSTNAME, str );
1090 
1091 	// map
1092 	trap_Cvar_VariableStringBuffer( "mapname", str, sizeof(str) );
1093 	trap_RankReportStr( -1, -1, QGR_KEY_MAP, str );
1094 
1095 	// mod
1096 	trap_Cvar_VariableStringBuffer( "fs_game", str, sizeof(str) );
1097 	trap_RankReportStr( -1, -1, QGR_KEY_MOD, str );
1098 
1099 	// gametype
1100 	num = trap_Cvar_VariableIntegerValue("g_gametype");
1101 	trap_RankReportInt( -1, -1, QGR_KEY_GAMETYPE, num, 0 );
1102 
1103 	// fraglimit
1104 	num = trap_Cvar_VariableIntegerValue("fraglimit");
1105 	trap_RankReportInt( -1, -1, QGR_KEY_FRAGLIMIT, num, 0 );
1106 
1107 	// timelimit
1108 	num = trap_Cvar_VariableIntegerValue("timelimit");
1109 	trap_RankReportInt( -1, -1, QGR_KEY_TIMELIMIT, num, 0 );
1110 
1111 	// maxclients
1112 	num = trap_Cvar_VariableIntegerValue("sv_maxclients");
1113 	trap_RankReportInt( -1, -1, QGR_KEY_MAXCLIENTS, num, 0 );
1114 
1115 	// maxrate
1116 	num = trap_Cvar_VariableIntegerValue("sv_maxRate");
1117 	trap_RankReportInt( -1, -1, QGR_KEY_MAXRATE, num, 0 );
1118 
1119 	// minping
1120 	num = trap_Cvar_VariableIntegerValue("sv_minPing");
1121 	trap_RankReportInt( -1, -1, QGR_KEY_MINPING, num, 0 );
1122 
1123 	// maxping
1124 	num = trap_Cvar_VariableIntegerValue("sv_maxPing");
1125 	trap_RankReportInt( -1, -1, QGR_KEY_MAXPING, num, 0 );
1126 
1127 	// dedicated
1128 	num = trap_Cvar_VariableIntegerValue("dedicated");
1129 	trap_RankReportInt( -1, -1, QGR_KEY_DEDICATED, num, 0 );
1130 
1131 	// version
1132 	trap_Cvar_VariableStringBuffer( "version", str, sizeof(str) );
1133 	trap_RankReportStr( -1, -1, QGR_KEY_VERSION, str );
1134 }
1135 
1136