summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorenneract <trem.redman@gmail.com>2014-12-19 20:38:46 +0100
committerenneract <trem.redman@gmail.com>2014-12-19 20:38:46 +0100
commit3a7015bd646110a378c3b5f0a8638368f2827788 (patch)
tree07cb29caa192f462664b5881b9cbaf7f7502a5a0 /src
parent3afe13c2a292e0940a05634b322300ccd1238541 (diff)
Implement combat statistics.
Diffstat (limited to 'src')
-rw-r--r--src/cgame/cg_event.c8
-rw-r--r--src/game/bg_mod.h137
-rw-r--r--src/game/bg_public.h3
-rw-r--r--src/game/g_admin.c118
-rw-r--r--src/game/g_admin.h1
-rw-r--r--src/game/g_client.c2
-rw-r--r--src/game/g_combat.c120
-rw-r--r--src/game/g_csw.h30
-rw-r--r--src/game/g_local.h26
-rw-r--r--src/game/g_main.c12
-rw-r--r--src/game/g_missile.c4
-rw-r--r--src/game/g_weapon.c96
12 files changed, 447 insertions, 110 deletions
diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c
index e5002cd..4a86235 100644
--- a/src/cgame/cg_event.c
+++ b/src/cgame/cg_event.c
@@ -350,13 +350,13 @@ static void CG_Obituary( entityState_t *ent )
message = "^5found^7";
message2 = "^5's ^5mine";
break;
-
-
- case MOD_FLAMES:
+
+ case MOD_FLAMES:
+ case MOD_LEVEL4_FLAMES:
message = "^5tasted^7";
message2 = "^5's ^5flames";
break;
-
+
case MOD_ABUILDER_CLAW:
message = "^5should leave^7";
message2 = "^5's ^5buildings alone";
diff --git a/src/game/bg_mod.h b/src/game/bg_mod.h
index 6f09037..e6b1e53 100644
--- a/src/game/bg_mod.h
+++ b/src/game/bg_mod.h
@@ -1,68 +1,69 @@
-MOD( MOD_UNKNOWN ),
-MOD( MOD_HDOG ),
-MOD( MOD_SHOTGUN ),
-MOD( MOD_BLASTER ),
-MOD( MOD_PAINSAW ),
-MOD( MOD_MACHINEGUN ),
-MOD( MOD_CHAINGUN ),
-MOD( MOD_PRIFLE ),
-MOD( MOD_MDRIVER ),
-MOD( MOD_LASGUN ),
-MOD( MOD_LIGHTNING ),
-MOD( MOD_LCANNON ),
-MOD( MOD_LCANNON_SPLASH ),
-MOD( MOD_FLAMER ),
-MOD( MOD_FLAMER_SPLASH ),
-MOD( MOD_ROCKETL ),
-MOD( MOD_ROCKETL_SPLASH ),
-MOD( MOD_GRENADE ),
-MOD( MOD_PSAWBLADE ),
-MOD( MOD_MINE ),
-MOD( MOD_FLAMES ),
-MOD( MOD_SPITEFUL_ABCESS ),
-MOD( MOD_WATER ),
-MOD( MOD_SLIME ),
-MOD( MOD_LAVA ),
-MOD( MOD_CRUSH ),
-MOD( MOD_TELEFRAG ),
-MOD( MOD_FALLING ),
-MOD( MOD_SUICIDE ),
-MOD( MOD_TARGET_LASER ),
-MOD( MOD_TRIGGER_HURT ),
-MOD( MOD_ABUILDER_CLAW ),
-MOD( MOD_LEVEL0_BITE ),
-MOD( MOD_LEVEL1_CLAW ),
-MOD( MOD_LEVEL1_PCLOUD ),
-MOD( MOD_LEVEL3_CLAW ),
-MOD( MOD_LEVEL3_POUNCE ),
-MOD( MOD_LEVEL5_POUNCE ),
-MOD( MOD_LEVEL5_PRICKLES ),
-MOD( MOD_LEVEL3_BOUNCEBALL ),
-MOD( MOD_LEVEL2_CLAW ),
-MOD( MOD_LEVEL2_ZAP ),
-MOD( MOD_LEVEL5_CLAW ),
-MOD( MOD_LEVEL5_ZAP ),
-MOD( MOD_LEVEL5_BOUNCEBALL ),
-MOD( MOD_LEVEL2_BOUNCEBALL ),
-MOD( MOD_LEVEL4_CLAW ),
-MOD( MOD_LEVEL4_TRAMPLE ),
-MOD( MOD_LEVEL4_CRUSH ),
-MOD( MOD_SLOWBLOB ),
-MOD( MOD_POISON ),
-MOD( MOD_INFECTION ),
-MOD( MOD_SWARM ),
-MOD( MOD_MD2 ),
-MOD( MOD_HSPAWN ),
-MOD( MOD_TESLAGEN ),
-MOD( MOD_MGTURRET ),
-MOD( MOD_MGTURRET2 ),
-MOD( MOD_REACTOR ),
-MOD( MOD_ASPAWN ),
-MOD( MOD_ATUBE ),
-MOD( MOD_OVERMIND ),
-MOD( MOD_SLAP ),
-MOD( MOD_DECONSTRUCT ),
-MOD( MOD_REPLACE ),
-MOD( MOD_NOCREEP ),
-MOD( MOD_NOBP ),
-MOD( MOD_ABOMB )
+MOD( MOD_UNKNOWN, CSW_UNKNOWN ),
+MOD( MOD_HDOG, CSW_UNKNOWN ),
+MOD( MOD_SHOTGUN, CSW_SHOTGUN ),
+MOD( MOD_BLASTER, CSW_BLASTER ),
+MOD( MOD_PAINSAW, CSW_PAINSAW ),
+MOD( MOD_MACHINEGUN, CSW_MACHINEGUN ),
+MOD( MOD_CHAINGUN, CSW_CHAINGUN ),
+MOD( MOD_PRIFLE, CSW_PRIFLE ),
+MOD( MOD_MDRIVER, CSW_MDRIVER ),
+MOD( MOD_LASGUN, CSW_LASGUN ),
+MOD( MOD_LIGHTNING, CSW_LIGHTNING ),
+MOD( MOD_LCANNON, CSW_LCANNON ),
+MOD( MOD_LCANNON_SPLASH, CSW_LCANNON ),
+MOD( MOD_FLAMER, CSW_FLAMER ),
+MOD( MOD_FLAMER_SPLASH, CSW_FLAMER ),
+MOD( MOD_ROCKETL, CSW_ROCKETL ),
+MOD( MOD_ROCKETL_SPLASH, CSW_ROCKETL ),
+MOD( MOD_GRENADE, CSW_GRENADE ),
+MOD( MOD_PSAWBLADE, CSW_PAINSAW_ALT ),
+MOD( MOD_MINE, CSW_UNKNOWN ),
+MOD( MOD_FLAMES, CSW_UNKNOWN ),
+MOD( MOD_SPITEFUL_ABCESS, CSW_UNKNOWN ),
+MOD( MOD_WATER, CSW_UNKNOWN ),
+MOD( MOD_SLIME, CSW_UNKNOWN ),
+MOD( MOD_LAVA, CSW_UNKNOWN ),
+MOD( MOD_CRUSH, CSW_UNKNOWN ),
+MOD( MOD_TELEFRAG, CSW_UNKNOWN ),
+MOD( MOD_FALLING, CSW_UNKNOWN ),
+MOD( MOD_SUICIDE, CSW_UNKNOWN ),
+MOD( MOD_TARGET_LASER, CSW_UNKNOWN ),
+MOD( MOD_TRIGGER_HURT, CSW_UNKNOWN ),
+MOD( MOD_ABUILDER_CLAW, CSW_ABUILDER ),
+MOD( MOD_LEVEL0_BITE, CSW_UNKNOWN ),
+MOD( MOD_LEVEL1_CLAW, CSW_LEVEL1 ),
+MOD( MOD_LEVEL1_PCLOUD, CSW_UNKNOWN ),
+MOD( MOD_LEVEL3_CLAW, CSW_LEVEL3 ),
+MOD( MOD_LEVEL3_POUNCE, CSW_UNKNOWN ),
+MOD( MOD_LEVEL5_POUNCE, CSW_UNKNOWN ),
+MOD( MOD_LEVEL5_PRICKLES, CSW_LEVEL5_ALT ),
+MOD( MOD_LEVEL3_BOUNCEBALL, CSW_LEVEL3_ALT ),
+MOD( MOD_LEVEL2_CLAW, CSW_LEVEL2 ),
+MOD( MOD_LEVEL2_ZAP, CSW_LEVEL2_ALT ),
+MOD( MOD_LEVEL5_CLAW, CSW_LEVEL5 ),
+MOD( MOD_LEVEL5_ZAP, CSW_UNKNOWN ),
+MOD( MOD_LEVEL5_BOUNCEBALL, CSW_UNKNOWN ),
+MOD( MOD_LEVEL2_BOUNCEBALL, CSW_UNKNOWN ),
+MOD( MOD_LEVEL4_CLAW, CSW_LEVEL4 ),
+MOD( MOD_LEVEL4_TRAMPLE, CSW_UNKNOWN ),
+MOD( MOD_LEVEL4_CRUSH, CSW_UNKNOWN ),
+MOD( MOD_LEVEL4_FLAMES, CSW_LEVEL4_ALT ),
+MOD( MOD_SLOWBLOB, CSW_UNKNOWN ),
+MOD( MOD_POISON, CSW_UNKNOWN ),
+MOD( MOD_INFECTION, CSW_UNKNOWN ),
+MOD( MOD_SWARM, CSW_UNKNOWN ),
+MOD( MOD_MD2, CSW_UNKNOWN ),
+MOD( MOD_HSPAWN, CSW_UNKNOWN ),
+MOD( MOD_TESLAGEN, CSW_UNKNOWN ),
+MOD( MOD_MGTURRET, CSW_UNKNOWN ),
+MOD( MOD_MGTURRET2, CSW_UNKNOWN ),
+MOD( MOD_REACTOR, CSW_UNKNOWN ),
+MOD( MOD_ASPAWN, CSW_UNKNOWN ),
+MOD( MOD_ATUBE, CSW_UNKNOWN ),
+MOD( MOD_OVERMIND, CSW_UNKNOWN ),
+MOD( MOD_SLAP, CSW_UNKNOWN ),
+MOD( MOD_DECONSTRUCT, CSW_UNKNOWN ),
+MOD( MOD_REPLACE, CSW_UNKNOWN ),
+MOD( MOD_NOCREEP, CSW_UNKNOWN ),
+MOD( MOD_NOBP, CSW_UNKNOWN ),
+MOD( MOD_ABOMB, CSW_UNKNOWN )
diff --git a/src/game/bg_public.h b/src/game/bg_public.h
index bbf064e..f96fd66 100644
--- a/src/game/bg_public.h
+++ b/src/game/bg_public.h
@@ -833,7 +833,7 @@ typedef enum
// means of death
typedef enum
{
-#define MOD(x) x
+#define MOD(a,b) a
#include "bg_mod.h"
#undef MOD
} meansOfDeath_t;
@@ -1243,3 +1243,4 @@ typedef struct
const char *name;
} dummyCmd_t;
int cmdcmp( const void *a, const void *b );
+
diff --git a/src/game/g_admin.c b/src/game/g_admin.c
index aa07e6f..36fa6be 100644
--- a/src/game/g_admin.c
+++ b/src/game/g_admin.c
@@ -249,6 +249,10 @@ g_admin_cmd_t g_admin_cmds[ ] =
"move 999 pingers to the spectator team",
""},
+ {"stats", G_admin_stats, qfalse, "stats",
+ "view combat statistics of a player",
+ "[^7name|slot^7]"},
+
{"time", G_admin_time, qtrue, "time",
"show the current local server time",
""},
@@ -4369,6 +4373,119 @@ qboolean G_admin_flag( gentity_t *ent )
return qtrue;
}
+
+
+/*
+=================
+G_admin_stats
+=================
+*/
+qboolean G_admin_stats( gentity_t *ent )
+{
+ gentity_t *targ;
+ int i;
+ qboolean header = qfalse;
+ const static char *cswNames[ ] =
+ {
+#define CSW(a,b) b
+#include "g_csw.h"
+#undef CSW
+ };
+
+ if( trap_Argc( ) > 1 )
+ {
+ char name[ MAX_NAME_LENGTH ];
+ namelog_t *vic;
+
+ trap_Argv( 1, name, sizeof( name ) );
+
+ if( !( vic = G_NamelogFromString( ent, name ) ) ||
+ vic->slot <= -1 )
+ {
+ ADMP( "^3stats: ^7no match\n" );
+ return qfalse;
+ }
+
+ targ = g_entities + vic->slot;
+ }
+ else
+ {
+ if( !ent )
+ {
+ ADMP( "^3stats: ^7console is not a combatant\n" );
+ return qfalse;
+ }
+
+ targ = ent;
+ }
+
+ ADMBP_begin( );
+
+ for( i = CSW_UNKNOWN + 1; i < MAX_COMBAT_STATS_WEAPONS; i++ )
+ {
+ combatStats_t *cs = targ->client->pers.combatStats + i;
+
+ // skip unused weapons
+ if( !cs->total )
+ continue;
+
+ if( !header )
+ {
+ ADMBP( va( "^3stats: ^7combat statistics of %s^7:\n", targ->client->pers.netname ) );
+ ADMBP( va( "^3%*s Dmg Acc FAc BAC FBA^7\n",
+ CSW_MAX_NAME_LEN, "Weapon" ) );
+ header = qtrue;
+ }
+
+ ADMBP( va( "%*s %8d", CSW_MAX_NAME_LEN, cswNames[ i ], cs->total ) );
+
+#define PRINT_ACC(a,b) \
+if( (b) == 0 ) \
+ ADMBP( " ^0n/a" ); \
+else \
+{ \
+ int _t = round( (float)(a)/(b) * 100.0f ); \
+ ADMBP( va( " ^7%3d", _t ) ); \
+}
+
+ PRINT_ACC( cs->enemy,
+ cs->total -
+ cs->friendly -
+ cs->enemy_buildable -
+ cs->friendly_buildable )
+
+ PRINT_ACC( cs->friendly,
+ cs->total -
+ cs->enemy -
+ cs->enemy_buildable -
+ cs->friendly_buildable )
+
+ PRINT_ACC( cs->enemy_buildable,
+ cs->total -
+ cs->enemy -
+ cs->friendly -
+ cs->friendly_buildable )
+
+ PRINT_ACC( cs->friendly_buildable,
+ cs->total -
+ cs->enemy -
+ cs->enemy_buildable -
+ cs->friendly )
+
+#undef PRINT_ACC
+
+ ADMBP( "\n" );
+ }
+
+ if( !header )
+ ADMBP( va( "^3stats: ^7no combat statistics are available for %s^7\n", targ->client->pers.netname ) );
+
+ ADMBP_end( );
+
+ return qtrue;
+}
+
+
/*
================
G_admin_print
@@ -4452,3 +4569,4 @@ void G_admin_cleanup( void )
g_admin_commands = NULL;
BG_DefragmentMemory( );
}
+
diff --git a/src/game/g_admin.h b/src/game/g_admin.h
index 4953cfe..61b012c 100644
--- a/src/game/g_admin.h
+++ b/src/game/g_admin.h
@@ -206,6 +206,7 @@ qboolean G_admin_revert( gentity_t *ent );
qboolean G_admin_flaglist( gentity_t *ent );
qboolean G_admin_flag( gentity_t *ent );
qboolean G_admin_slap( gentity_t *ent );
+qboolean G_admin_stats( gentity_t *ent );
g_admin_level_t *G_admin_find_level_for_score( int score );
void G_admin_add_score( gentity_t *ent, int score );
diff --git a/src/game/g_client.c b/src/game/g_client.c
index 1d50a97..896a7fc 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -1675,6 +1675,8 @@ void ClientDisconnect( int clientNum )
tent->s.clientNum = ent->s.clientNum;
}
+ G_LogCombatStats( ent );
+
G_LogPrintf( "^5ClientDisconnect: ^7%i [%s] (%s) \"%s^7\"\n", clientNum,
ent->client->pers.ip.str, ent->client->pers.guid, ent->client->pers.netname );
diff --git a/src/game/g_combat.c b/src/game/g_combat.c
index e43d036..c8090d4 100644
--- a/src/game/g_combat.c
+++ b/src/game/g_combat.c
@@ -80,7 +80,7 @@ void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker )
// these are just for logging, the client prints its own messages
char *modNames[ ] =
{
-#define MOD(x) #x
+#define MOD(a,b) #a
#include "bg_mod.h"
#undef MOD
};
@@ -1431,7 +1431,9 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
G_InstantRewardAttacker(attacker,targ,take);
targ->credits[ attacker->client->ps.clientNum ] += take;
}
-
+
+ G_CombatStats_HitMOD( attacker, targ, mod, take );
+
if( targ->health <= 0 )
{
if( client )
@@ -1770,3 +1772,117 @@ void G_LogDestruction( gentity_t *self, gentity_t *actor, int mod )
}
}
+
+const static combatStatsWeapon_t modToCsw[ ] =
+{
+#define MOD(a,b) b
+#include "bg_mod.h"
+#undef MOD
+};
+
+const static char *cswStrings[ ] =
+{
+#define CSW(a,b) #a
+#include "g_csw.h"
+#undef CSW
+};
+
+void G_CombatStats_Fire( gentity_t *ent, combatStatsWeapon_t weapon, int damage )
+{
+ combatStats_t *cs;
+
+ if( !ent || !ent->client )
+ return;
+
+ cs = ent->client->pers.combatStats + weapon;
+ cs->total += damage;
+
+ if( g_debugDamage.integer > 3 )
+ Com_Printf( "player %i fired %s, damage: %i\n",
+ ent - g_entities,
+ cswStrings[ weapon ],
+ damage );
+}
+
+void G_CombatStats_FireMOD( gentity_t *ent, meansOfDeath_t mod, int damage )
+{
+ G_CombatStats_Fire( ent, modToCsw[ mod ], damage );
+}
+
+void G_CombatStats_Hit( gentity_t *ent, gentity_t *hit, combatStatsWeapon_t weapon, int damage )
+{
+ combatStats_t *cs;
+ int *stat;
+
+ if( !ent || !ent->client || !hit )
+ return;
+
+ cs = ent->client->pers.combatStats + weapon;
+
+ if( hit->s.eType == ET_BUILDABLE )
+ {
+ if( ent->client->pers.teamSelection == hit->buildableTeam )
+ stat = &cs->friendly_buildable;
+ else
+ stat = &cs->enemy_buildable;
+ }
+ else if( hit->client )
+ {
+ if( ent->client->pers.teamSelection ==
+ hit->client->pers.teamSelection )
+ stat = &cs->friendly;
+ else
+ stat = &cs->enemy;
+ }
+ else
+ return;
+
+ if( g_debugDamage.integer > 3 )
+ Com_Printf( "player %i hit %s %i with %s, damage: %i\n",
+ ent - g_entities,
+ ( stat == &cs->friendly_buildable ) ? "a friendly buildable" :
+ ( stat == &cs->enemy_buildable ) ? "an enemy buildable" :
+ ( stat == &cs->friendly ) ? "a friendly player" :
+ "an enemy player",
+ hit - g_entities,
+ cswStrings[ weapon ],
+ damage );
+
+ (*stat) += damage;
+}
+
+void G_CombatStats_HitMOD( gentity_t *ent, gentity_t *hit, meansOfDeath_t mod, int damage )
+{
+ G_CombatStats_Hit( ent, hit, modToCsw[ mod ], damage );
+}
+
+void G_LogCombatStats( gentity_t *ent )
+{
+ int i;
+ char buffer[ 4096 ], *p = buffer;
+
+ for( i = 0; i < MAX_COMBAT_STATS_WEAPONS; i++ )
+ {
+ combatStats_t *cs = ent->client->pers.combatStats + i;
+
+ // skip unused weapons
+ if( !cs->total )
+ continue;
+
+ Com_sprintf(
+ p, 4096 - ( p - buffer ),
+ " %s %i,%i,%i,%i,%i",
+ cswStrings[ i ],
+ cs->total,
+ cs->enemy,
+ cs->friendly,
+ cs->enemy_buildable,
+ cs->friendly_buildable );
+
+ while( *p ) p++;
+ }
+
+ if( p != buffer )
+ G_LogPrintf( "CombatStats: %i%s\n", ent - g_entities, buffer );
+}
+
diff --git a/src/game/g_csw.h b/src/game/g_csw.h
new file mode 100644
index 0000000..4f3cdf2
--- /dev/null
+++ b/src/game/g_csw.h
@@ -0,0 +1,30 @@
+CSW( CSW_UNKNOWN, NULL ),
+
+CSW( CSW_BLASTER, "Blaster" ),
+CSW( CSW_MACHINEGUN, "Rifle" ),
+CSW( CSW_PAINSAW, "Pain Saw" ),
+CSW( CSW_PAINSAW_ALT, "Pain Saw Blade" ),
+CSW( CSW_SHOTGUN, "Shotgun" ),
+CSW( CSW_LASGUN, "Las Gun" ),
+CSW( CSW_MDRIVER, "Mass Driver" ),
+CSW( CSW_CHAINGUN, "Chaingun" ),
+CSW( CSW_PRIFLE, "Pulse Rifle" ),
+CSW( CSW_FLAMER, "Flame Thrower" ),
+CSW( CSW_LIGHTNING, "Lightning Gun" ),
+CSW( CSW_LCANNON, "Lucifer Cannon" ),
+CSW( CSW_ROCKETL, "Rocket Launcher" ),
+CSW( CSW_GRENADE, "Grenade" ),
+
+CSW( CSW_ABUILDER, "Granger" ),
+CSW( CSW_ABUILDER_ALT, "Granger Spit" ),
+CSW( CSW_LEVEL1, "Basilisk" ),
+CSW( CSW_LEVEL2, "Marauder" ),
+CSW( CSW_LEVEL2_ALT, "Marauder Zap" ),
+CSW( CSW_LEVEL3, "Dragoon" ),
+CSW( CSW_LEVEL3_ALT, "Dragoon Barb" ),
+CSW( CSW_LEVEL4, "Tyrant" ),
+CSW( CSW_LEVEL4_ALT, "Tyrant Flames" ),
+CSW( CSW_LEVEL5, "Hummel" ),
+CSW( CSW_LEVEL5_ALT, "Hummel Prickles" )
+
+#define CSW_MAX_NAME_LEN 15
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 873234b..918a809 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -307,6 +307,25 @@ typedef struct namelog_s
int id;
} namelog_t;
+typedef enum
+{
+#define CSW(a,b) a
+#include "g_csw.h"
+#undef CSW
+ ,
+ MAX_COMBAT_STATS_WEAPONS
+} combatStatsWeapon_t;
+
+typedef struct
+{
+ int total;
+
+ int enemy;
+ int enemy_buildable;
+ int friendly;
+ int friendly_buildable;
+} combatStats_t;
+
// client data that stays across multiple respawns, but is cleared
// on each level change or team change at ClientBegin()
typedef struct
@@ -356,6 +375,8 @@ typedef struct
// keep track of other players' info for tinfo
char cinfo[ MAX_CLIENTS ][ 16 ];
+
+ combatStats_t combatStats[ MAX_COMBAT_STATS_WEAPONS ];
} clientPersistant_t;
#define MAX_UNLAGGED_MARKERS 10
@@ -945,6 +966,11 @@ void G_InitDamageLocations( void );
#define DAMAGE_NO_PROTECTION 0x00000008 // armor, shields, invulnerability, and godmode have no effect
#define DAMAGE_NO_LOCDAMAGE 0x00000010 // do not apply locational damage
+void G_CombatStats_Fire( gentity_t *ent, combatStatsWeapon_t weapon, int damage );
+void G_CombatStats_FireMOD( gentity_t *ent, meansOfDeath_t mod, int damage );
+void G_CombatStats_Hit( gentity_t *ent, gentity_t *hit, combatStatsWeapon_t weapon, int damage );
+void G_CombatStats_HitMOD( gentity_t *ent, gentity_t *hit, meansOfDeath_t mod, int damage );
+
//
// g_missile.c
//
diff --git a/src/game/g_main.c b/src/game/g_main.c
index 5b6b025..fb11594 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -2235,6 +2235,18 @@ void LogExit( const char *string )
}
+ for( i = 0; i < MAX_CLIENTS; i++ )
+ {
+ gentity_t *ent = g_entities + i;
+
+ if( !ent->inuse ||
+ !ent->client ||
+ ent->client->pers.connected == CON_CONNECTING )
+ continue;
+
+ G_LogCombatStats( ent );
+ }
+
for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ )
{
if( !ent->inuse )
diff --git a/src/game/g_missile.c b/src/game/g_missile.c
index ab0156b..8c6e36d 100644
--- a/src/game/g_missile.c
+++ b/src/game/g_missile.c
@@ -734,8 +734,8 @@ gentity_t *FireBreath_fire( gentity_t *self, vec3_t start, vec3_t dir,
bolt->damage = LEVEL4_FIREBREATHDMG;
bolt->splashDamage = 60;
bolt->splashRadius = 250 ;
- bolt->methodOfDeath = MOD_FLAMES;
- bolt->splashMethodOfDeath = MOD_FLAMER_SPLASH;
+ bolt->methodOfDeath = MOD_LEVEL4_FLAMES;
+ bolt->splashMethodOfDeath = MOD_LEVEL4_FLAMES;
bolt->clipmask = MASK_SHOT;
bolt->target_ent = NULL;
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c
index 3bc3871..2a87754 100644
--- a/src/game/g_weapon.c
+++ b/src/game/g_weapon.c
@@ -300,6 +300,8 @@ void meleeAttack( gentity_t *ent, float range, float width, float height,
trace_t tr;
gentity_t *traceEnt;
+ G_CombatStats_FireMOD( ent, mod, damage );
+
G_WideTrace( &tr, ent, range, width, height, &traceEnt );
if( traceEnt == NULL || !traceEnt->takedamage )
return;
@@ -325,6 +327,8 @@ void bulletFire( gentity_t *ent, float spread, int damage, int mod )
gentity_t *tent;
gentity_t *traceEnt;
+ G_CombatStats_FireMOD( ent, mod, damage );
+
r = random( ) * M_PI * 2.0f;
u = sin( r ) * crandom( ) * spread * 16;
r = cos( r ) * crandom( ) * spread * 16;
@@ -424,6 +428,8 @@ void shotgunFire( gentity_t *ent )
{
gentity_t *tent;
+ G_CombatStats_Fire( ent, CSW_SHOTGUN, SHOTGUN_DMG * SHOTGUN_PELLETS );
+
// send shotgun blast
tent = G_TempEntity( muzzle, EV_SHOTGUN );
VectorScale( forward, 4096, tent->s.origin2 );
@@ -450,6 +456,8 @@ void massDriverFire( gentity_t *ent )
gentity_t *tent;
gentity_t *traceEnt;
+ G_CombatStats_Fire( ent, CSW_MDRIVER, MDRIVER_DMG );
+
VectorMA( muzzle, 8192.0f * 16.0f, forward, end );
G_UnlaggedOn( ent, muzzle, 8192.0f * 16.0f );
@@ -545,6 +553,7 @@ BLASTER PISTOL
void blasterFire( gentity_t *ent )
{
+ G_CombatStats_Fire( ent, CSW_BLASTER, BLASTER_DMG );
fire_blaster( ent, muzzle, forward );
}
@@ -558,6 +567,7 @@ PULSE RIFLE
void pulseRifleFire( gentity_t *ent )
{
+ G_CombatStats_Fire( ent, CSW_MDRIVER, PRIFLE_DMG );
fire_pulseRifle( ent, muzzle, forward );
}
@@ -585,19 +595,22 @@ Napalm Charge
===============
*/
void NapalmFire( gentity_t *ent, qboolean secondary )
- {
-
+{
+ int damage;
- NapalmChargeFire( ent, muzzle, forward,
- ent->client->ps.stats[ STAT_MISC ] *
- LCANNON_DAMAGE / LCANNON_CHARGE_TIME_MAX,
- LCANNON_RADIUS, LCANNON_SPEED );
+ damage = ent->client->ps.stats[ STAT_MISC ] *
+ LCANNON_DAMAGE / LCANNON_CHARGE_TIME_MAX;
- NapalmChargeImp( ent, muzzle, forward,
- ent->client->ps.stats[ STAT_MISC ] *
- LCANNON_DAMAGE / LCANNON_CHARGE_TIME_MAX,
- LCANNON_RADIUS, LCANNON_SPEED );
- ent->client->ps.stats[ STAT_MISC ] = 0;
+ G_CombatStats_Fire( ent, CSW_FLAMER, damage );
+
+ NapalmChargeFire( ent, muzzle, forward,
+ damage, LCANNON_RADIUS, LCANNON_SPEED );
+
+ NapalmChargeImp( ent, muzzle, forward,
+ ent->client->ps.stats[ STAT_MISC ] *
+ damage, LCANNON_RADIUS, LCANNON_SPEED );
+
+ ent->client->ps.stats[ STAT_MISC ] = 0;
}
/*
@@ -610,6 +623,8 @@ Normal
{
vec3_t origin;
+ G_CombatStats_Fire( ent, CSW_FLAMER, FLAMER_DMG );
+
// Correct muzzle so that the missile does not start in the ceiling
VectorMA( muzzle, -7.0f, up, origin );
@@ -625,16 +640,9 @@ FireBreath Tyrant
===============
*/
void FireBreath_tyrant( gentity_t *ent )
- {
- vec3_t corr;
- corr[0] = 0.0;
- corr[1] = 0.0;
- corr[2] = 10.0;
-
- FireBreath_fire( ent, muzzle, forward,
- ent->client->ps.stats[ STAT_MISC ] *
- LCANNON_DAMAGE / LCANNON_CHARGE_TIME_MAX,
- LCANNON_RADIUS, LCANNON_SPEED );
+{
+ G_CombatStats_Fire( ent, CSW_LEVEL4_ALT, LEVEL4_FIREBREATHDMG );
+ FireBreath_fire( ent, muzzle, forward, 0, LCANNON_RADIUS, LCANNON_SPEED );
}
/*
@@ -645,13 +653,8 @@ FlameTurret
void FlameTurretFire( gentity_t *ent )
{
- vec3_t corr;
- corr[0] = 0.0;
- corr[1] = 0.0;
- corr[2] = 8.0;
-
- VectorAdd( muzzle, corr, muzzle );
- FlameTurretFireNormal( ent, muzzle, forward );
+ muzzle[2] += 8.0f;
+ FlameTurretFireNormal( ent, muzzle, forward );
}
/*
@@ -662,6 +665,8 @@ GRENADE
void throwGrenade( gentity_t *ent )
{
+ G_CombatStats_Fire( ent, CSW_GRENADE, GRENADE_DAMAGE );
+
launch_grenade( ent, muzzle, forward );
launch_grenade_flames( ent, muzzle, forward );
}
@@ -709,6 +714,8 @@ void lasGunFire( gentity_t *ent )
gentity_t *tent;
gentity_t *traceEnt;
+ G_CombatStats_Fire( ent, CSW_LASGUN, LASGUN_DAMAGE );
+
VectorMA( muzzle, 8192 * 16, forward, end );
G_UnlaggedOn( ent, muzzle, 8192 * 16 );
@@ -760,6 +767,8 @@ void painSawFire( gentity_t *ent )
vec3_t temp;
gentity_t *tent, *traceEnt;
+ G_CombatStats_Fire( ent, CSW_PAINSAW, PAINSAW_DAMAGE );
+
G_WideTrace( &tr, ent, PAINSAW_RANGE, PAINSAW_WIDTH, PAINSAW_HEIGHT,
&traceEnt );
if( !traceEnt || !traceEnt->takedamage )
@@ -792,6 +801,7 @@ PSAW BLADES
*/
void painSawFire2( gentity_t *ent )
{
+ G_CombatStats_Fire( ent, CSW_PAINSAW_ALT, PAINSAW_DAMAGE2 );
launch_saw( ent, muzzle, forward );
}
@@ -806,13 +816,23 @@ LUCIFER CANNON
void LCChargeFire( gentity_t *ent, qboolean secondary )
{
if( secondary && ent->client->ps.stats[ STAT_MISC ] <= 0 )
+ {
+ G_CombatStats_Fire( ent, CSW_LCANNON, LCANNON_SECONDARY_DAMAGE );
fire_luciferCannon( ent, muzzle, forward, LCANNON_SECONDARY_DAMAGE,
LCANNON_SECONDARY_RADIUS, LCANNON_SECONDARY_SPEED );
+ }
else
- fire_luciferCannon( ent, muzzle, forward,
- ent->client->ps.stats[ STAT_MISC ] *
- LCANNON_DAMAGE / LCANNON_CHARGE_TIME_MAX,
- LCANNON_RADIUS, LCANNON_SPEED );
+ {
+ int damage;
+
+ damage = ent->client->ps.stats[ STAT_MISC ] *
+ LCANNON_DAMAGE / LCANNON_CHARGE_TIME_MAX;
+
+ G_CombatStats_Fire( ent, CSW_LCANNON, damage );
+
+ fire_luciferCannon( ent, muzzle, forward, damage,
+ LCANNON_RADIUS, LCANNON_SPEED );
+ }
ent->client->ps.stats[ STAT_MISC ] = 0;
}
@@ -825,6 +845,7 @@ ROCKET LAUNCHER
void rocketLauncherFire( gentity_t *ent )
{
+ G_CombatStats_Fire( ent, CSW_ROCKETL, ROCKETL_DAMAGE );
fire_rocket( ent, muzzle, forward );
}
@@ -879,6 +900,9 @@ void lightningGunFire( gentity_t *ent )
gentity_t *target;
int damage;
+ damage = g_lightningDamage.value / ( 1000.0f / LIGHTNING_REPEAT );
+ G_CombatStats_Fire( ent, CSW_LIGHTNING, damage );
+
VectorMA( muzzle, LIGHTNING_RANGE, forward, end );
G_UnlaggedOn( ent, muzzle, LIGHTNING_RANGE );
@@ -895,7 +919,6 @@ void lightningGunFire( gentity_t *ent )
target = g_entities + tr.entityNum;
- damage = g_lightningDamage.value / ( 1000.0f / LIGHTNING_REPEAT );
if( target->s.eType == ET_PLAYER || target->s.eType == ET_BUILDABLE )
{
@@ -1074,6 +1097,7 @@ void buildFire( gentity_t *ent, dynMenu_t menu )
void slowBlobFire( gentity_t *ent )
{
+ G_CombatStats_Fire( ent, CSW_ABUILDER_ALT, ABUILDER_BLOB_DMG );
fire_slowBlob( ent, muzzle, forward );
}
@@ -1591,6 +1615,8 @@ void areaZapFire( gentity_t *ent )
trace_t tr;
gentity_t *traceEnt;
+ G_CombatStats_Fire( ent, CSW_LEVEL2_ALT, LEVEL2_AREAZAP_DMG );
+
G_WideTrace( &tr, ent, LEVEL2_AREAZAP_RANGE, LEVEL2_AREAZAP_WIDTH, LEVEL2_AREAZAP_WIDTH, &traceEnt );
if( traceEnt == NULL )
@@ -1681,6 +1707,7 @@ qboolean CheckPounceAttack( gentity_t *ent )
void bounceBallFire( gentity_t *ent )
{
+ G_CombatStats_Fire( ent, CSW_LEVEL3_ALT, LEVEL3_BOUNCEBALL_DMG );
fire_bounceBall( ent, muzzle, forward );
}
@@ -1829,6 +1856,9 @@ void Prickles( gentity_t *ent )
float r;
float u;
+
+ G_CombatStats_Fire( ent, CSW_LEVEL5_ALT, LEVEL5_PRICKLES_DMG );
+
r = random( ) * M_PI * 2.0f;
u = sin( r ) * crandom( ) * LEVEL5_PRICKLES_SPREAD * 16;
r = cos( r ) * crandom( ) * LEVEL5_PRICKLES_SPREAD * 16;