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