/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2002-2021 Q3Rally Team (Per Thormann - q3rally@gmail.com) This file is part of q3rally source code. q3rally source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. q3rally source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with q3rally; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // g_rankings.c -- reports for global rankings system #include "g_local.h" #include "g_rankings.h" /* ================ G_RankRunFrame ================ */ void G_RankRunFrame() { gentity_t* ent; gentity_t* ent2; grank_status_t old_status; grank_status_t status; int time; int i; int j; if( !trap_RankCheckInit() ) { trap_RankBegin( GR_GAMEKEY ); } trap_RankPoll(); if( trap_RankActive() ) { for( i = 0; i < level.maxclients; i++ ) { ent = &(g_entities[i]); if ( !ent->inuse ) continue; if ( ent->client == NULL ) continue; if ( ent->r.svFlags & SVF_BOT) { // no bots in ranked games trap_SendConsoleCommand( EXEC_INSERT, va("kick %s\n", ent->client->pers.netname) ); continue; } old_status = ent->client->client_status; status = trap_RankUserStatus( i ); if( ent->client->client_status != status ) { // inform client of current status // not needed for client side log in trap_SendServerCommand( i, va("rank_status %i\n",status) ); if ( i == 0 ) { int j = 0; } ent->client->client_status = status; } switch( status ) { case QGR_STATUS_NEW: case QGR_STATUS_SPECTATOR: if( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { ent->client->sess.sessionTeam = TEAM_SPECTATOR; ent->client->sess.spectatorState = SPECTATOR_FREE; ClientSpawn( ent ); // make sure by now CS_GRAND rankingsGameID is ready trap_SendServerCommand( i, va("rank_status %i\n",status) ); trap_SendServerCommand( i, "rank_menu\n" ); } break; case QGR_STATUS_NO_USER: case QGR_STATUS_BAD_PASSWORD: case QGR_STATUS_TIMEOUT: case QGR_STATUS_NO_MEMBERSHIP: case QGR_STATUS_INVALIDUSER: case QGR_STATUS_ERROR: if( (ent->r.svFlags & SVF_BOT) == 0 ) { trap_RankUserReset( ent->s.clientNum ); } break; case QGR_STATUS_ACTIVE: if( (ent->client->sess.sessionTeam == TEAM_SPECTATOR) && (g_gametype.integer < GT_TEAM) ) { SetTeam( ent, "free" ); } if( old_status != QGR_STATUS_ACTIVE ) { // player has just become active for( j = 0; j < level.maxclients; j++ ) { ent2 = &(g_entities[j]); if ( !ent2->inuse ) continue; if ( ent2->client == NULL ) continue; if ( ent2->r.svFlags & SVF_BOT) continue; if( (i != j) && (trap_RankUserStatus( j ) == QGR_STATUS_ACTIVE) ) { trap_RankReportInt( i, j, QGR_KEY_PLAYED_WITH, 1, 0 ); } // send current scores so the player's rank will show // up under the crosshair immediately DeathmatchScoreboardMessage( ent2 ); } } break; default: break; } } // don't let ranked games last forever if( ((g_fraglimit.integer == 0) || (g_fraglimit.integer > 100)) && ((g_timelimit.integer == 0) || (g_timelimit.integer > 1000)) ) { trap_Cvar_Set( "timelimit", "1000" ); } } // tell time to clients so they can show current match rating if( level.intermissiontime == 0 ) { for( i = 0; i < level.maxclients; i++ ) { ent = &(g_entities[i]); if( ent->client == NULL ) { continue; } time = (level.time - ent->client->pers.enterTime) / 1000; ent->client->ps.persistant[PERS_MATCH_TIME] = time; } } } /* ================ G_RankFireWeapon ================ */ void G_RankFireWeapon( int self, int weapon ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } if( weapon == WP_GAUNTLET ) { // the gauntlet only "fires" when it actually hits something return; } trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED, 1, 1 ); switch( weapon ) { case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_MACHINEGUN, 1, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_SHOTGUN, 1, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_GRENADE, 1, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_ROCKET, 1, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_LIGHTNING, 1, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_RAILGUN, 1, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_PLASMA, 1, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_BFG, 1, 1 ); break; // STONELANCE /* case WP_GRAPPLING_HOOK: trap_RankReportInt( self, -1, QGR_KEY_SHOT_FIRED_GRAPPLE, 1, 1 ); break; */ // END default: break; } } /* ================ G_RankDamage ================ */ void G_RankDamage( int self, int attacker, int damage, int means_of_death ) { // state information to avoid counting each shotgun pellet as a hit static int last_framenum = -1; static int last_self = -1; static int last_attacker = -1; static int last_means_of_death = MOD_UNKNOWN; qboolean new_hit; int splash; int key_hit; int key_damage; int key_splash; if( level.warmupTime != 0 ) { // no reports during warmup period return; } new_hit = (level.framenum != last_framenum) || (self != last_self) || (attacker != last_attacker) || (means_of_death != last_means_of_death); // update state information last_framenum = level.framenum; last_self = self; last_attacker = attacker; last_means_of_death = means_of_death; // the gauntlet only "fires" when it actually hits something if( (attacker != ENTITYNUM_WORLD) && (attacker != self) && (means_of_death == MOD_GAUNTLET) && (g_entities[attacker].client) ) { trap_RankReportInt( attacker, -1, QGR_KEY_SHOT_FIRED_GAUNTLET, 1, 1 ); } // don't track hazard damage, just deaths switch( means_of_death ) { case MOD_WATER: case MOD_SLIME: case MOD_LAVA: case MOD_CRUSH: case MOD_TELEFRAG: case MOD_FALLING: case MOD_SUICIDE: case MOD_TRIGGER_HURT: return; default: break; } // get splash damage switch( means_of_death ) { case MOD_GRENADE_SPLASH: case MOD_ROCKET_SPLASH: case MOD_PLASMA_SPLASH: case MOD_BFG_SPLASH: splash = damage; break; default: splash = 0; key_splash = -1; break; } // hit, damage, and splash taken switch( means_of_death ) { case MOD_GAUNTLET: key_hit = QGR_KEY_HIT_TAKEN_GAUNTLET; key_damage = QGR_KEY_DAMAGE_TAKEN_GAUNTLET; break; case MOD_MACHINEGUN: key_hit = QGR_KEY_HIT_TAKEN_MACHINEGUN; key_damage = QGR_KEY_DAMAGE_TAKEN_MACHINEGUN; break; case MOD_SHOTGUN: key_hit = QGR_KEY_HIT_TAKEN_SHOTGUN; key_damage = QGR_KEY_DAMAGE_TAKEN_SHOTGUN; break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_GRENADE; key_damage = QGR_KEY_DAMAGE_TAKEN_GRENADE; key_splash = QGR_KEY_SPLASH_TAKEN_GRENADE; break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_ROCKET; key_damage = QGR_KEY_DAMAGE_TAKEN_ROCKET; key_splash = QGR_KEY_SPLASH_TAKEN_ROCKET; break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_PLASMA; key_damage = QGR_KEY_DAMAGE_TAKEN_PLASMA; key_splash = QGR_KEY_SPLASH_TAKEN_PLASMA; break; case MOD_RAILGUN: key_hit = QGR_KEY_HIT_TAKEN_RAILGUN; key_damage = QGR_KEY_DAMAGE_TAKEN_RAILGUN; break; case MOD_LIGHTNING: key_hit = QGR_KEY_HIT_TAKEN_LIGHTNING; key_damage = QGR_KEY_DAMAGE_TAKEN_LIGHTNING; break; case MOD_BFG: case MOD_BFG_SPLASH: key_hit = QGR_KEY_HIT_TAKEN_BFG; key_damage = QGR_KEY_DAMAGE_TAKEN_BFG; key_splash = QGR_KEY_SPLASH_TAKEN_BFG; break; case MOD_GRAPPLE: key_hit = QGR_KEY_HIT_TAKEN_GRAPPLE; key_damage = QGR_KEY_DAMAGE_TAKEN_GRAPPLE; break; default: key_hit = QGR_KEY_HIT_TAKEN_UNKNOWN; key_damage = QGR_KEY_DAMAGE_TAKEN_UNKNOWN; break; } // report general and specific hit taken if( new_hit ) { trap_RankReportInt( self, -1, QGR_KEY_HIT_TAKEN, 1, 1 ); trap_RankReportInt( self, -1, key_hit, 1, 1 ); } // report general and specific damage taken trap_RankReportInt( self, -1, QGR_KEY_DAMAGE_TAKEN, damage, 1 ); trap_RankReportInt( self, -1, key_damage, damage, 1 ); // report general and specific splash taken if( splash != 0 ) { trap_RankReportInt( self, -1, QGR_KEY_SPLASH_TAKEN, splash, 1 ); trap_RankReportInt( self, -1, key_splash, splash, 1 ); } // hit, damage, and splash given if( (attacker != ENTITYNUM_WORLD) && (attacker != self) ) { switch( means_of_death ) { case MOD_GAUNTLET: key_hit = QGR_KEY_HIT_GIVEN_GAUNTLET; key_damage = QGR_KEY_DAMAGE_GIVEN_GAUNTLET; break; case MOD_MACHINEGUN: key_hit = QGR_KEY_HIT_GIVEN_MACHINEGUN; key_damage = QGR_KEY_DAMAGE_GIVEN_MACHINEGUN; break; case MOD_SHOTGUN: key_hit = QGR_KEY_HIT_GIVEN_SHOTGUN; key_damage = QGR_KEY_DAMAGE_GIVEN_SHOTGUN; break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_GRENADE; key_damage = QGR_KEY_DAMAGE_GIVEN_GRENADE; key_splash = QGR_KEY_SPLASH_GIVEN_GRENADE; break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_ROCKET; key_damage = QGR_KEY_DAMAGE_GIVEN_ROCKET; key_splash = QGR_KEY_SPLASH_GIVEN_ROCKET; break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_PLASMA; key_damage = QGR_KEY_DAMAGE_GIVEN_PLASMA; key_splash = QGR_KEY_SPLASH_GIVEN_PLASMA; break; case MOD_RAILGUN: key_hit = QGR_KEY_HIT_GIVEN_RAILGUN; key_damage = QGR_KEY_DAMAGE_GIVEN_RAILGUN; break; case MOD_LIGHTNING: key_hit = QGR_KEY_HIT_GIVEN_LIGHTNING; key_damage = QGR_KEY_DAMAGE_GIVEN_LIGHTNING; break; case MOD_BFG: case MOD_BFG_SPLASH: key_hit = QGR_KEY_HIT_GIVEN_BFG; key_damage = QGR_KEY_DAMAGE_GIVEN_BFG; key_splash = QGR_KEY_SPLASH_GIVEN_BFG; break; case MOD_GRAPPLE: key_hit = QGR_KEY_HIT_GIVEN_GRAPPLE; key_damage = QGR_KEY_DAMAGE_GIVEN_GRAPPLE; break; default: key_hit = QGR_KEY_HIT_GIVEN_UNKNOWN; key_damage = QGR_KEY_DAMAGE_GIVEN_UNKNOWN; break; } // report general and specific hit given // jwu 8/26/00 // had a case where attacker is 245 which is grnadeshooter attacker is // g_entities index not necessarilly clientnum if (g_entities[attacker].client) { if( new_hit ) { trap_RankReportInt( attacker, -1, QGR_KEY_HIT_GIVEN, 1, 1 ); trap_RankReportInt( attacker, -1, key_hit, 1, 1 ); } // report general and specific damage given trap_RankReportInt( attacker, -1, QGR_KEY_DAMAGE_GIVEN, damage, 1 ); trap_RankReportInt( attacker, -1, key_damage, damage, 1 ); // report general and specific splash given if( splash != 0 ) { trap_RankReportInt( attacker, -1, QGR_KEY_SPLASH_GIVEN, splash, 1 ); trap_RankReportInt( attacker, -1, key_splash, splash, 1 ); } } } // friendly fire if( (attacker != self) && OnSameTeam( &(g_entities[self]), &(g_entities[attacker])) && (g_entities[attacker].client) ) { // report teammate hit if( new_hit ) { trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_HIT_TAKEN, 1, 1 ); trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_HIT_GIVEN, 1, 1 ); } // report teammate damage trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_DAMAGE_TAKEN, damage, 1 ); trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_DAMAGE_GIVEN, damage, 1 ); // report teammate splash if( splash != 0 ) { trap_RankReportInt( self, -1, QGR_KEY_TEAMMATE_SPLASH_TAKEN, splash, 1 ); trap_RankReportInt( attacker, -1, QGR_KEY_TEAMMATE_SPLASH_GIVEN, splash, 1 ); } } } /* ================ G_RankPlayerDie ================ */ void G_RankPlayerDie( int self, int attacker, int means_of_death ) { int p1; int p2; if( level.warmupTime != 0 ) { // no reports during warmup period return; } if( attacker == ENTITYNUM_WORLD ) { p1 = self; p2 = -1; trap_RankReportInt( p1, p2, QGR_KEY_HAZARD_DEATH, 1, 1 ); switch( means_of_death ) { case MOD_WATER: trap_RankReportInt( p1, p2, QGR_KEY_WATER, 1, 1 ); break; case MOD_SLIME: trap_RankReportInt( p1, p2, QGR_KEY_SLIME, 1, 1 ); break; case MOD_LAVA: trap_RankReportInt( p1, p2, QGR_KEY_LAVA, 1, 1 ); break; case MOD_CRUSH: trap_RankReportInt( p1, p2, QGR_KEY_CRUSH, 1, 1 ); break; case MOD_TELEFRAG: trap_RankReportInt( p1, p2, QGR_KEY_TELEFRAG, 1, 1 ); break; case MOD_FALLING: trap_RankReportInt( p1, p2, QGR_KEY_FALLING, 1, 1 ); break; case MOD_SUICIDE: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_CMD, 1, 1 ); break; case MOD_TRIGGER_HURT: trap_RankReportInt( p1, p2, QGR_KEY_TRIGGER_HURT, 1, 1 ); break; default: trap_RankReportInt( p1, p2, QGR_KEY_HAZARD_MISC, 1, 1 ); break; } } else if( attacker == self ) { p1 = self; p2 = -1; trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE, 1, 1 ); switch( means_of_death ) { case MOD_GAUNTLET: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GAUNTLET, 1, 1 ); break; case MOD_MACHINEGUN: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_MACHINEGUN, 1, 1 ); break; case MOD_SHOTGUN: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_SHOTGUN, 1, 1 ); break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GRENADE, 1, 1 ); break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_ROCKET, 1, 1 ); break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_PLASMA, 1, 1 ); break; case MOD_RAILGUN: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_RAILGUN, 1, 1 ); break; case MOD_LIGHTNING: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_LIGHTNING, 1, 1 ); break; case MOD_BFG: case MOD_BFG_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_BFG, 1, 1 ); break; case MOD_GRAPPLE: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_GRAPPLE, 1, 1 ); break; default: trap_RankReportInt( p1, p2, QGR_KEY_SUICIDE_UNKNOWN, 1, 1 ); break; } } else { p1 = attacker; p2 = self; trap_RankReportInt( p1, p2, QGR_KEY_FRAG, 1, 1 ); switch( means_of_death ) { case MOD_GAUNTLET: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GAUNTLET, 1, 1 ); break; case MOD_MACHINEGUN: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_MACHINEGUN, 1, 1 ); break; case MOD_SHOTGUN: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_SHOTGUN, 1, 1 ); break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GRENADE, 1, 1 ); break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_ROCKET, 1, 1 ); break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_PLASMA, 1, 1 ); break; case MOD_RAILGUN: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_RAILGUN, 1, 1 ); break; case MOD_LIGHTNING: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_LIGHTNING, 1, 1 ); break; case MOD_BFG: case MOD_BFG_SPLASH: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_BFG, 1, 1 ); break; case MOD_GRAPPLE: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_GRAPPLE, 1, 1 ); break; default: trap_RankReportInt( p1, p2, QGR_KEY_FRAG_UNKNOWN, 1, 1 ); break; } } } /* ================ G_RankWeaponTime ================ */ void G_RankWeaponTime( int self, int weapon ) { gclient_t* client; int time; if( level.warmupTime != 0 ) { // no reports during warmup period return; } client = g_entities[self].client; time = (level.time - client->weapon_change_time) / 1000; client->weapon_change_time = level.time; if( time <= 0 ) { return; } trap_RankReportInt( self, -1, QGR_KEY_TIME, time, 1 ); switch( weapon ) { case WP_GAUNTLET: trap_RankReportInt( self, -1, QGR_KEY_TIME_GAUNTLET, time, 1 ); break; case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_MACHINEGUN, time, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_SHOTGUN, time, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_TIME_GRENADE, time, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_TIME_ROCKET, time, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_TIME_LIGHTNING, time, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_RAILGUN, time, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_TIME_PLASMA, time, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_TIME_BFG, time, 1 ); break; // STONELANCE /* case WP_GRAPPLING_HOOK: trap_RankReportInt( self, -1, QGR_KEY_TIME_GRAPPLE, time, 1 ); break; */ // END default: break; } } /* ================ G_RankPickupWeapon ================ */ void G_RankPickupWeapon( int self, int weapon ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_PICKUP_WEAPON, 1, 1 ); switch( weapon ) { case WP_GAUNTLET: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GAUNTLET, 1, 1 ); break; case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_MACHINEGUN, 1, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_SHOTGUN, 1, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GRENADE, 1, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_ROCKET, 1, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_LIGHTNING, 1, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_RAILGUN, 1, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_PLASMA, 1, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_BFG, 1, 1 ); break; // STONELANCE /* case WP_GRAPPLING_HOOK: trap_RankReportInt( self, -1, QGR_KEY_PICKUP_GRAPPLE, 1, 1 ); break; */ // END default: break; } } /* ================ G_RankPickupAmmo ================ */ void G_RankPickupAmmo( int self, int weapon, int quantity ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_BOXES, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS, quantity, 1 ); switch( weapon ) { case WP_MACHINEGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_BULLETS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_BULLETS, quantity, 1 ); break; case WP_SHOTGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_SHELLS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_SHELLS, quantity, 1 ); break; case WP_GRENADE_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_BOXES_GRENADES, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_GRENADES, quantity, 1 ); break; case WP_ROCKET_LAUNCHER: trap_RankReportInt( self, -1, QGR_KEY_BOXES_ROCKETS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_ROCKETS, quantity, 1 ); break; case WP_LIGHTNING: trap_RankReportInt( self, -1, QGR_KEY_BOXES_LG_AMMO, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_LG_AMMO, quantity, 1 ); break; case WP_RAILGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_SLUGS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_SLUGS, quantity, 1 ); break; case WP_PLASMAGUN: trap_RankReportInt( self, -1, QGR_KEY_BOXES_CELLS, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_CELLS, quantity, 1 ); break; case WP_BFG: trap_RankReportInt( self, -1, QGR_KEY_BOXES_BFG_AMMO, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ROUNDS_BFG_AMMO, quantity, 1 ); break; default: break; } } /* ================ G_RankPickupHealth ================ */ void G_RankPickupHealth( int self, int quantity ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_HEALTH, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_HEALTH_TOTAL, quantity, 1 ); switch( quantity ) { case 5: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_5, 1, 1 ); break; case 25: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_25, 1, 1 ); break; case 50: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_50, 1, 1 ); break; case 100: trap_RankReportInt( self, -1, QGR_KEY_HEALTH_MEGA, 1, 1 ); break; default: break; } } /* ================ G_RankPickupArmor ================ */ void G_RankPickupArmor( int self, int quantity ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_ARMOR, 1, 1 ); trap_RankReportInt( self, -1, QGR_KEY_ARMOR_TOTAL, quantity, 1 ); switch( quantity ) { case 5: trap_RankReportInt( self, -1, QGR_KEY_ARMOR_SHARD, 1, 1 ); break; case 50: trap_RankReportInt( self, -1, QGR_KEY_ARMOR_YELLOW, 1, 1 ); break; case 100: trap_RankReportInt( self, -1, QGR_KEY_ARMOR_RED, 1, 1 ); break; default: break; } } /* ================ G_RankPickupPowerup ================ */ void G_RankPickupPowerup( int self, int powerup ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } // ctf flags are treated as powerups if( (powerup == PW_REDFLAG) || (powerup == PW_BLUEFLAG) ) { trap_RankReportInt( self, -1, QGR_KEY_FLAG_PICKUP, 1, 1 ); return; } trap_RankReportInt( self, -1, QGR_KEY_POWERUP, 1, 1 ); switch( powerup ) { case PW_QUAD: trap_RankReportInt( self, -1, QGR_KEY_QUAD, 1, 1 ); break; case PW_BATTLESUIT: trap_RankReportInt( self, -1, QGR_KEY_SUIT, 1, 1 ); break; case PW_HASTE: trap_RankReportInt( self, -1, QGR_KEY_HASTE, 1, 1 ); break; case PW_INVIS: trap_RankReportInt( self, -1, QGR_KEY_INVIS, 1, 1 ); break; case PW_REGEN: trap_RankReportInt( self, -1, QGR_KEY_REGEN, 1, 1 ); break; case PW_FLIGHT: trap_RankReportInt( self, -1, QGR_KEY_FLIGHT, 1, 1 ); break; default: break; } } /* ================ G_RankPickupHoldable ================ */ void G_RankPickupHoldable( int self, int holdable ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } switch( holdable ) { case HI_MEDKIT: trap_RankReportInt( self, -1, QGR_KEY_MEDKIT, 1, 1 ); break; case HI_TELEPORTER: trap_RankReportInt( self, -1, QGR_KEY_TELEPORTER, 1, 1 ); break; default: break; } } /* ================ G_RankUseHoldable ================ */ void G_RankUseHoldable( int self, int holdable ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } switch( holdable ) { case HI_MEDKIT: trap_RankReportInt( self, -1, QGR_KEY_MEDKIT_USE, 1, 1 ); break; case HI_TELEPORTER: trap_RankReportInt( self, -1, QGR_KEY_TELEPORTER_USE, 1, 1 ); break; default: break; } } /* ================ G_RankReward ================ */ void G_RankReward( int self, int award ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } switch( award ) { case EF_AWARD_IMPRESSIVE: trap_RankReportInt( self, -1, QGR_KEY_IMPRESSIVE, 1, 1 ); break; case EF_AWARD_EXCELLENT: trap_RankReportInt( self, -1, QGR_KEY_EXCELLENT, 1, 1 ); break; default: break; } } /* ================ G_RankCapture ================ */ void G_RankCapture( int self ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportInt( self, -1, QGR_KEY_FLAG_CAPTURE, 1, 1 ); } /* ================ G_RankUserTeamName ================ */ void G_RankUserTeamName( int self, char* team_name ) { if( level.warmupTime != 0 ) { // no reports during warmup period return; } trap_RankReportStr( self, -1, QGR_KEY_TEAM_NAME, team_name ); } /* ================ G_RankClientDisconnect ================ */ void G_RankClientDisconnect( int self ) { gclient_t* client; int time; int match_rating; if( level.warmupTime != 0 ) { // no reports during warmup period return; } // match rating client = g_entities[self].client; time = (level.time - client->pers.enterTime) / 1000; if( time < 60 ) { match_rating = 0; } else { match_rating = client->ps.persistant[PERS_MATCH_RATING] / time; } trap_RankReportInt( self, -1, QGR_KEY_MATCH_RATING, match_rating, 0 ); } /* ================ G_RankGameOver ================ */ void G_RankGameOver( void ) { int i; char str[MAX_INFO_VALUE]; int num; if( level.warmupTime != 0 ) { // no reports during warmup period return; } for( i = 0; i < level.maxclients; i++ ) { if( trap_RankUserStatus( i ) == QGR_STATUS_ACTIVE ) { G_RankClientDisconnect( i ); } } // hostname trap_Cvar_VariableStringBuffer( "sv_hostname", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_HOSTNAME, str ); // map trap_Cvar_VariableStringBuffer( "mapname", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_MAP, str ); // mod trap_Cvar_VariableStringBuffer( "fs_game", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_MOD, str ); // gametype num = trap_Cvar_VariableIntegerValue("g_gametype"); trap_RankReportInt( -1, -1, QGR_KEY_GAMETYPE, num, 0 ); // fraglimit num = trap_Cvar_VariableIntegerValue("fraglimit"); trap_RankReportInt( -1, -1, QGR_KEY_FRAGLIMIT, num, 0 ); // timelimit num = trap_Cvar_VariableIntegerValue("timelimit"); trap_RankReportInt( -1, -1, QGR_KEY_TIMELIMIT, num, 0 ); // maxclients num = trap_Cvar_VariableIntegerValue("sv_maxclients"); trap_RankReportInt( -1, -1, QGR_KEY_MAXCLIENTS, num, 0 ); // maxrate num = trap_Cvar_VariableIntegerValue("sv_maxRate"); trap_RankReportInt( -1, -1, QGR_KEY_MAXRATE, num, 0 ); // minping num = trap_Cvar_VariableIntegerValue("sv_minPing"); trap_RankReportInt( -1, -1, QGR_KEY_MINPING, num, 0 ); // maxping num = trap_Cvar_VariableIntegerValue("sv_maxPing"); trap_RankReportInt( -1, -1, QGR_KEY_MAXPING, num, 0 ); // dedicated num = trap_Cvar_VariableIntegerValue("dedicated"); trap_RankReportInt( -1, -1, QGR_KEY_DEDICATED, num, 0 ); // version trap_Cvar_VariableStringBuffer( "version", str, sizeof(str) ); trap_RankReportStr( -1, -1, QGR_KEY_VERSION, str ); }