void G_AwardPlayerKilled( edict_t *self, edict_t *inflictor, edict_t *attacker, int mod ) { trace_t trace; score_stats_t *stats; loggedFrag_t *lfrag; if( self->r.svflags & SVF_CORPSE ) return; if( !attacker->r.client ) return; if( !self->r.client ) return; if( attacker == self ) return; if( attacker->s.team == self->s.team && attacker->s.team > TEAM_PLAYERS ) return; if( mod == MOD_ROCKET_W || mod == MOD_ROCKET_S ) { // direct hit attacker->r.client->resp.awardInfo.directrocket_count++; if( attacker->r.client->resp.awardInfo.directrocket_count == DIRECTROCKET_FOR_AWARD ) { attacker->r.client->resp.awardInfo.directrocket_count = 0; attacker->r.client->resp.awardInfo.directrocket_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Direct Rocket Hit!" ); } // Midair if( self->groundentity == NULL && !self->waterlevel ) { // check for height to the ground G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID ); if( trace.fraction == 1.0f ) { attacker->r.client->resp.awardInfo.rl_midair_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Air Rocket!" ); } } } if( mod == MOD_GRENADE_W || mod == MOD_GRENADE_S ) { // direct hit attacker->r.client->resp.awardInfo.directgrenade_count++; if( attacker->r.client->resp.awardInfo.directgrenade_count == DIRECTGRENADE_FOR_AWARD ) { attacker->r.client->resp.awardInfo.directgrenade_count = 0; attacker->r.client->resp.awardInfo.directgrenade_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Direct Grenade Hit!" ); } // Midair if( self->groundentity == NULL && !self->waterlevel ) { // check for height to the ground G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, tv( self->s.origin[0], self->s.origin[1], self->s.origin[2] - 64 ), self, MASK_SOLID ); if( trace.fraction == 1.0f ) { attacker->r.client->resp.awardInfo.gl_midair_award++; G_PlayerAward( attacker, S_COLOR_BLUE "Air Grenade!" ); } } } // Multikill if( game.serverTime - attacker->r.client->resp.awardInfo.multifrag_timer < MULTIKILL_INTERVAL ) attacker->r.client->resp.awardInfo.multifrag_count++; else attacker->r.client->resp.awardInfo.multifrag_count = 1; attacker->r.client->resp.awardInfo.multifrag_timer = game.serverTime; if( attacker->r.client->resp.awardInfo.multifrag_count > 1 ) { char s[MAX_CONFIGSTRING_CHARS]; s[0] = 0; switch( attacker->r.client->resp.awardInfo.multifrag_count ) { case 0: case 1: break; case 2: Q_strncpyz( s, S_COLOR_GREEN "Double Frag!", sizeof( s ) ); break; case 3: Q_strncpyz( s, S_COLOR_GREEN "Triple Frag!", sizeof( s ) ); break; case 4: Q_strncpyz( s, S_COLOR_GREEN "Quadruple Frag!", sizeof( s ) ); break; default: Q_snprintfz( s, sizeof( s ), S_COLOR_GREEN "Extermination! %i in a row!", attacker->r.client->resp.awardInfo.multifrag_count ); break; } G_PlayerAward( attacker, s ); } // Sprees attacker->r.client->resp.awardInfo.frag_count++; if( attacker->r.client->resp.awardInfo.frag_count && ( attacker->r.client->resp.awardInfo.frag_count % 5 == 0 ) ) { char s[MAX_CONFIGSTRING_CHARS]; s[0] = 0; switch( (int)( attacker->r.client->resp.awardInfo.frag_count / 5 ) ) { case 1: Q_strncpyz( s, S_COLOR_YELLOW "On Fire!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is On Fire!\n", attacker->r.client->netname ); break; case 2: Q_strncpyz( s, S_COLOR_YELLOW "Raging!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is Raging!\n", attacker->r.client->netname ); break; case 3: Q_strncpyz( s, S_COLOR_YELLOW "Fraglord!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is the Fraglord!\n", attacker->r.client->netname ); break; case 4: Q_strncpyz( s, S_COLOR_YELLOW "Extermination!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is Exterminating!\n", attacker->r.client->netname ); break; default: Q_strncpyz( s, S_COLOR_YELLOW "God Mode!", sizeof( s ) ); G_PrintMsg( NULL, "%s" S_COLOR_YELLOW " is in God Mode!\n", attacker->r.client->netname ); break; } G_PlayerAward( attacker, s ); } // ch : weapon specific frags if ( G_ModToAmmo( mod ) != AMMO_NONE ) attacker->r.client->level.stats.accuracy_frags[G_ModToAmmo( mod )-AMMO_GUNBLADE]++; if( GS_MatchState() == MATCH_STATE_PLAYTIME /* && !strcmp( "duel", gs.gametypeName ) */) { // ch : frag log stats = &attacker->r.client->level.stats; if( !stats->fragAllocator ) stats->fragAllocator = LinearAllocator( sizeof( loggedFrag_t ), 0, _G_LevelMalloc, _G_LevelFree ); lfrag = LA_Alloc( stats->fragAllocator ); lfrag->mm_attacker = attacker->r.client->mm_session; lfrag->mm_victim = self->r.client->mm_session; lfrag->weapon = G_ModToAmmo( mod ) - AMMO_GUNBLADE; lfrag->time = ( game.serverTime - GS_MatchStartTime() ) / 1000; } }
/* * * - We will consider direct impacts as splash when the player is on the ground and the hit very close to the ground */ int G_Projectile_HitStyle( edict_t *projectile, edict_t *target ) { trace_t trace; vec3_t end; qboolean atGround = qfalse; edict_t *attacker; #define AIRHIT_MINHEIGHT 64 // don't hurt owner for the first second if( target == projectile->r.owner && target != world ) { if( !g_projectile_touch_owner->integer || ( g_projectile_touch_owner->integer && projectile->timeStamp + 1000 > level.time ) ) return PROJECTILE_TOUCH_NOT; } if( !target->takedamage || ISBRUSHMODEL( target->s.modelindex ) ) return PROJECTILE_TOUCH_DIRECTHIT; if( target->waterlevel > 1 ) return PROJECTILE_TOUCH_DIRECTHIT; // water hits are direct but don't count for awards attacker = ( projectile->r.owner && projectile->r.owner->r.client ) ? projectile->r.owner : NULL; // see if the target is at ground or a less than a step of height if( target->groundentity ) atGround = qtrue; else { VectorCopy( target->s.origin, end ); end[2] -= STEPSIZE; G_Trace4D( &trace, target->s.origin, target->r.mins, target->r.maxs, end, target, MASK_DEADSOLID, 0 ); if( ( trace.ent != -1 || trace.startsolid ) && ISWALKABLEPLANE( &trace.plane ) ) atGround = qtrue; } if( atGround ) { // when the player is at ground we will consider a direct hit only when // the hit is 16 units above the feet if( projectile->s.origin[2] <= 16 + target->s.origin[2] + target->r.mins[2] ) return PROJECTILE_TOUCH_DIRECTSPLASH; } else { // it's direct hit, but let's see if it's airhit VectorCopy( target->s.origin, end ); end[2] -= AIRHIT_MINHEIGHT; G_Trace4D( &trace, target->s.origin, target->r.mins, target->r.maxs, end, target, MASK_DEADSOLID, 0 ); if( ( trace.ent != -1 || trace.startsolid ) && ISWALKABLEPLANE( &trace.plane ) ) { // add directhit and airhit to awards counter if( attacker && !GS_IsTeamDamage( &attacker->s, &target->s ) && G_ModToAmmo( projectile->style ) != AMMO_NONE ) { projectile->r.owner->r.client->level.stats.accuracy_hits_direct[G_ModToAmmo( projectile->style )-AMMO_GUNBLADE]++; teamlist[projectile->r.owner->s.team].stats.accuracy_hits_direct[G_ModToAmmo( projectile->style )-AMMO_GUNBLADE]++; projectile->r.owner->r.client->level.stats.accuracy_hits_air[G_ModToAmmo( projectile->style )-AMMO_GUNBLADE]++; teamlist[projectile->r.owner->s.team].stats.accuracy_hits_air[G_ModToAmmo( projectile->style )-AMMO_GUNBLADE]++; } return PROJECTILE_TOUCH_DIRECTAIRHIT; } } // add directhit to awards counter if( attacker && !GS_IsTeamDamage( &attacker->s, &target->s ) && G_ModToAmmo( projectile->style ) != AMMO_NONE ) { projectile->r.owner->r.client->level.stats.accuracy_hits_direct[G_ModToAmmo( projectile->style )-AMMO_GUNBLADE]++; teamlist[projectile->r.owner->s.team].stats.accuracy_hits_direct[G_ModToAmmo( projectile->style )-AMMO_GUNBLADE]++; } return PROJECTILE_TOUCH_DIRECTHIT; #undef AIRHIT_MINHEIGHT }
/* * G_Damage * targ entity that is being damaged * inflictor entity that is causing the damage * attacker entity that caused the inflictor to damage targ * example: targ=enemy, inflictor=rocket, attacker=player * * dir direction of the attack * point point at which the damage is being inflicted * normal normal vector from that point * damage amount of damage being inflicted * knockback force to be applied against targ as a result of the damage * * dflags these flags are used to control how T_Damage works */ void G_Damage( edict_t *targ, edict_t *inflictor, edict_t *attacker, const vec3_t pushdir, const vec3_t dmgdir, const vec3_t point, float damage, float knockback, float stun, int dflags, int mod ) { gclient_t *client; float take; float save; float asave; qboolean statDmg; if( !targ || !targ->takedamage ) return; if( !attacker ) { attacker = world; mod = MOD_TRIGGER_HURT; } meansOfDeath = mod; client = targ->r.client; // Cgg - race mode: players don't interact with one another if( GS_RaceGametype() ) { if( attacker->r.client && targ->r.client && attacker != targ ) return; } // push if( !( dflags & DAMAGE_NO_KNOCKBACK ) ) G_KnockBackPush( targ, attacker, pushdir, knockback, dflags ); // stun if( g_allow_stun->integer && !( dflags & (DAMAGE_NO_STUN|FL_GODMODE) ) && (int)stun > 0 && targ->r.client && targ->r.client->resp.takeStun && !GS_IsTeamDamage( &targ->s, &attacker->s ) && ( targ != attacker ) ) { if( dflags & DAMAGE_STUN_CLAMP ) { if( targ->r.client->ps.pmove.stats[PM_STAT_STUN] < (int)stun ) targ->r.client->ps.pmove.stats[PM_STAT_STUN] = (int)stun; } else targ->r.client->ps.pmove.stats[PM_STAT_STUN] += (int)stun; clamp( targ->r.client->ps.pmove.stats[PM_STAT_STUN], 0, MAX_STUN_TIME ); } // dont count self-damage cause it just adds the same to both stats statDmg = ( attacker != targ ) && ( mod != MOD_TELEFRAG ); // apply handicap on the damage given if( statDmg && attacker->r.client && !GS_Instagib() ) { // handicap is a percentage value if( attacker->r.client->handicap != 0 ) damage *= 1.0 - (attacker->r.client->handicap * 0.01f); } take = damage; save = 0; // check for cases where damage is protected if( !( dflags & DAMAGE_NO_PROTECTION ) ) { // check for godmode if( targ->flags & FL_GODMODE ) { take = 0; save = damage; } // never damage in timeout else if( GS_MatchPaused() ) { take = save = 0; } // ca has self splash damage disabled else if( ( dflags & DAMAGE_RADIUS ) && attacker == targ && !GS_SelfDamage() ) { take = save = 0; } // don't get damage from players in race else if( ( GS_RaceGametype() ) && attacker->r.client && targ->r.client && ( attacker->r.client != targ->r.client ) ) { take = save = 0; } // team damage avoidance else if( GS_IsTeamDamage( &targ->s, &attacker->s ) && !G_Gametype_CanTeamDamage( dflags ) ) { take = save = 0; } // apply warShell powerup protection else if( targ->r.client && targ->r.client->ps.inventory[POWERUP_SHELL] > 0 ) { // warshell offers full protection in instagib if( GS_Instagib() ) { take = 0; save = damage; } else { take = ( damage * 0.25f ); save = damage - take; } // todo : add protection sound } } asave = G_CheckArmor( targ, take, dflags ); take -= asave; //treat cheat/powerup savings the same as armor asave += save; // APPLY THE DAMAGES if( !take && !asave ) return; // do the damage if( take <= 0 ) return; // adding damage given/received to stats if( statDmg && attacker->r.client && !targ->deadflag && targ->movetype != MOVETYPE_PUSH && targ->s.type != ET_CORPSE ) { attacker->r.client->level.stats.total_damage_given += take + asave; teamlist[attacker->s.team].stats.total_damage_given += take + asave; if( GS_IsTeamDamage( &targ->s, &attacker->s ) ) { attacker->r.client->level.stats.total_teamdamage_given += take + asave; teamlist[attacker->s.team].stats.total_teamdamage_given += take + asave; } } G_Gametype_ScoreEvent( attacker->r.client, "dmg", va( "%i %f %i", targ->s.number, damage, attacker->s.number ) ); if( statDmg && client ) { client->level.stats.total_damage_received += take + asave; teamlist[targ->s.team].stats.total_damage_received += take + asave; if( GS_IsTeamDamage( &targ->s, &attacker->s ) ) { client->level.stats.total_teamdamage_received += take + asave; teamlist[targ->s.team].stats.total_teamdamage_received += take + asave; } } // accumulate received damage for snapshot effects { vec3_t dorigin; if( inflictor == world && mod == MOD_FALLING ) // it's fall damage targ->snap.damage_fall += take + save; if( point[0] != 0.0f || point[1] != 0.0f || point[2] != 0.0f ) VectorCopy( point, dorigin ); else VectorSet( dorigin, targ->s.origin[0], targ->s.origin[1], targ->s.origin[2] + targ->viewheight ); G_BlendFrameDamage( targ, take, &targ->snap.damage_taken, dorigin, dmgdir, targ->snap.damage_at, targ->snap.damage_dir ); G_BlendFrameDamage( targ, save, &targ->snap.damage_saved, dorigin, dmgdir, targ->snap.damage_at, targ->snap.damage_dir ); if( targ->r.client ) { if( mod != MOD_FALLING && mod != MOD_TELEFRAG && mod != MOD_SUICIDE ) { if( inflictor == world || attacker == world ) { // for world inflicted damage use always 'frontal' G_ClientAddDamageIndicatorImpact( targ->r.client, take + save, NULL ); } else if( dflags & DAMAGE_RADIUS ) { // for splash hits the direction is from the inflictor origin G_ClientAddDamageIndicatorImpact( targ->r.client, take + save, pushdir ); } else { // for direct hits the direction is the projectile direction G_ClientAddDamageIndicatorImpact( targ->r.client, take + save, dmgdir ); } } } } targ->health = targ->health - take; // add damage done to stats if( !GS_IsTeamDamage( &targ->s, &attacker->s ) && statDmg && G_ModToAmmo( mod ) != AMMO_NONE && client && attacker->r.client ) { attacker->r.client->level.stats.accuracy_hits[G_ModToAmmo( mod )-AMMO_GUNBLADE]++; attacker->r.client->level.stats.accuracy_damage[G_ModToAmmo( mod )-AMMO_GUNBLADE] += damage; teamlist[attacker->s.team].stats.accuracy_hits[G_ModToAmmo( mod )-AMMO_GUNBLADE]++; teamlist[attacker->s.team].stats.accuracy_damage[G_ModToAmmo( mod )-AMMO_GUNBLADE] += damage; G_AwardPlayerHit( targ, attacker, mod ); } // accumulate given damage for hit sounds if( ( take || asave ) && targ != attacker && client && !targ->deadflag ) { if( attacker ) { if( GS_IsTeamDamage( &targ->s, &attacker->s ) ) attacker->snap.damageteam_given += take + asave; // we want to know how good our hit was, so saved also matters else attacker->snap.damage_given += take + asave; } } if( G_IsDead( targ ) ) { if( client ) targ->flags |= FL_NO_KNOCKBACK; G_Killed( targ, inflictor, attacker, HEALTH_TO_INT( take ), point, mod ); } else { G_CallPain( targ, attacker, knockback, take ); } }