void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int knockback; qboolean headShot; qboolean wasAlive; hitRegion_t hr = HR_NUM_HITREGIONS; if (!targ->takedamage) { return; } #ifdef SAVEGAME_SUPPORT if( g_gametype.integer == GT_SINGLE_PLAYER && ( g_reloading.integer || saveGamePending ) ) return; #endif // SAVEGAME_SUPPORT // trap_SendServerCommand( -1, va("print \"%i\n\"\n", targ->health) ); // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued || (g_gamestate.integer != GS_PLAYING && match_warmupDamage.integer == 0)) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // Arnout: invisible entities can't be damaged if( targ->entstate == STATE_INVISIBLE || targ->entstate == STATE_UNDERCONSTRUCTION ) { return; } // xkan, 12/23/2002 - was the bot alive before applying any damage? wasAlive = (targ->health > 0); // Arnout: combatstate if( targ->client && attacker && attacker->client && attacker != targ ) { /*vec_t dist = -1.f; if( targ->client->combatState < COMBATSTATE_HOT ) { vec3_t shotvec; VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, shotvec ); dist = VectorLengthSquared( shotvec ); if( dist < Square(1500.f) && targ->client->combatState == COMBATSTATE_WARM ) targ->client->combatState = COMBATSTATE_HOT; } if( attacker->client->combatState < COMBATSTATE_HOT ) { if( dist < 0.f ) { vec3_t shotvec; VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, shotvec ); dist = VectorLengthSquared( shotvec ); } if( dist > Square(1500.f) ) attacker->client->combatState = COMBATSTATE_WARM; else if( attacker->client->combatState == COMBATSTATE_WARM ) attacker->client->combatState = COMBATSTATE_HOT; }*/ if( g_gamestate.integer == GS_PLAYING ) { if( !OnSameTeam( attacker, targ ) ) { targ->client->combatState |= (1<<COMBATSTATE_DAMAGERECEIVED); attacker->client->combatState |= (1<<COMBATSTATE_DAMAGEDEALT); } } } // JPW NERVE if ((targ->waterlevel >= 3) && (mod == MOD_FLAMETHROWER)) return; // jpw // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER && !(targ->isProp) && !targ->scriptName) { if ( targ->use && targ->moverState == MOVER_POS1 ) { G_UseEntity( targ, inflictor, attacker ); } return; } // TAT 11/22/2002 // In the old code, this check wasn't done for props, so I put that check back in to make props_statue properly work // 4 means destructible if ( targ->s.eType == ET_MOVER && (targ->spawnflags & 4) && !targ->isProp ) { /*switch (mod) { case MOD_GRENADE: case MOD_GRENADE_LAUNCHER: case MOD_ROCKET: case MOD_AIRSTRIKE: case MOD_ARTY: case MOD_GRENADE_PINEAPPLE: case MOD_MAPMORTAR: case MOD_EXPLOSIVE: case MOD_DYNAMITE: case MOD_LANDMINE: case MOD_GPG40: case MOD_M7: case MOD_TELEFRAG: case MOD_PANZERFAUST: case MOD_SATCHEL: break; default: return; // no damage from other weapons }*/ if( !G_WeaponIsExplosive( mod ) ) { return; } // check for team if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } } else if ( targ->s.eType == ET_EXPLOSIVE ) { /*// 32 Explosive // 64 Dynamite only // 256 Airstrike/artillery only // 512 Satchel only if ((targ->spawnflags & 32) || (targ->spawnflags & 64) || (targ->spawnflags & 256) || (targ->spawnflags & 512)) { switch (mod) { case MOD_GRENADE: case MOD_GRENADE_LAUNCHER: case MOD_ROCKET: case MOD_GRENADE_PINEAPPLE: case MOD_MAPMORTAR: case MOD_EXPLOSIVE: case MOD_LANDMINE: case MOD_GPG40: case MOD_M7: if( !(targ->spawnflags & 32) ) return; break; case MOD_SATCHEL: if( !(targ->spawnflags & 512) ) return; break; case MOD_ARTY: case MOD_AIRSTRIKE: if( !(targ->spawnflags & 256) ) return; break; case MOD_DYNAMITE: if( !(targ->spawnflags & 64) ) return; break; default: return; } // check for team if( targ->s.teamNum == inflictor->s.teamNum ) { return; } }*/ if( targ->parent && G_GetWeaponClassForMOD( mod ) == 2 ) { return; } // check for team // if( G_GetWeaponClassForMOD( mod ) != -1 && targ->s.teamNum == inflictor->s.teamNum ) { // return; // } if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } else if ( targ->s.eType == ET_MISSILE && targ->methodOfDeath == MOD_LANDMINE ) { if( targ->s.modelindex2 ) { if( G_WeaponIsExplosive( mod ) ) { mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } if( attacker && attacker->client ) { AddScore( attacker, 1 ); //G_AddExperience( attacker, 1.f ); } G_ExplodeMissile(targ); } } return; } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } client = targ->client; if ( client ) { if ( client->noclip || client->ps.powerups[PW_INVULNERABLE] ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } else if( dflags & DAMAGE_HALF_KNOCKBACK ) { knockback *= 0.5f; } // ydnar: set weapons means less knockback if( client && (client->ps.weapon == WP_MORTAR_SET || client->ps.weapon == WP_MOBILE_MG42_SET) ) knockback *= 0.5; if( targ->client && g_friendlyFire.integer && OnSameTeam(targ, attacker) ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); /*if( mod == MOD_GRENADE || mod == MOD_GRENADE_LAUNCHER || mod == MOD_DYNAMITE || mod == MOD_GPG40 || mod == MOD_M7 || mod == MOD_LANDMINE ) { targ->client->ps.velocity[2] *= 2.f; // gimme air baby! targ->client->ps.groundEntityNum = ENTITYNUM_NONE; // flying high! } else if( mod == MOD_ROCKET ) { targ->client->ps.velocity[2] *= .75f; // but not to the moon please! targ->client->ps.groundEntityNum = ENTITYNUM_NONE; // flying high! }*/ if (targ == attacker && !( mod != MOD_ROCKET && mod != MOD_GRENADE && mod != MOD_GRENADE_LAUNCHER && mod != MOD_DYNAMITE && mod != MOD_GPG40 && mod != MOD_M7 && mod != MOD_LANDMINE )) { targ->client->ps.velocity[2] *= 0.25; } // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team if ( targ != attacker && OnSameTeam (targ, attacker) ) { if ( (g_gamestate.integer != GS_PLAYING && match_warmupDamage.integer == 1)) { return; } else if (!g_friendlyFire.integer) { return; } } } // add to the attacker's hit counter if ( attacker->client && targ != attacker && targ->health > 0 ) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS] -= damage; } else { attacker->client->ps.persistant[PERS_HITS] += damage; } } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // adrenaline junkie! if( targ->client && targ->client->ps.powerups[PW_ADRENALINE] ) { take *= .5f; } // save some from flak jacket if( targ->client && targ->client->sess.skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 4 && targ->client->sess.playerType == PC_ENGINEER ) { if( mod == MOD_GRENADE || mod == MOD_GRENADE_LAUNCHER || mod == MOD_ROCKET || mod == MOD_GRENADE_PINEAPPLE || mod == MOD_MAPMORTAR || mod == MOD_MAPMORTAR_SPLASH || mod == MOD_EXPLOSIVE || mod == MOD_LANDMINE || mod == MOD_GPG40 || mod == MOD_M7 || mod == MOD_SATCHEL || mod == MOD_ARTY || mod == MOD_AIRSTRIKE || mod == MOD_DYNAMITE || mod == MOD_MORTAR || mod == MOD_PANZERFAUST || mod == MOD_MAPMORTAR ) { take -= take * .5f; } } headShot = IsHeadShot(targ, dir, point, mod); if ( headShot ) { if( take * 2 < 50 ) // head shots, all weapons, do minimum 50 points damage take = 50; else take *= 2; // sniper rifles can do full-kill (and knock into limbo) if( dflags & DAMAGE_DISTANCEFALLOFF ) { vec_t dist; vec3_t shotvec; VectorSubtract( point, muzzleTrace, shotvec ); dist = VectorLength( shotvec ); if( dist > 1500.f ) { if( dist > 2500.f ) { take *= 0.2f; } else { float scale = 1.f - 0.2f * (1000.f / (dist - 1000.f)); take *= scale; } } } if( !(targ->client->ps.eFlags & EF_HEADSHOT) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte(dir) ); if( mod != MOD_K43_SCOPE && mod != MOD_GARAND_SCOPE ) { take *= .8f; // helmet gives us some protection } } targ->client->ps.eFlags |= EF_HEADSHOT; // OSP - Record the headshot if(client && attacker && attacker->client #ifndef DEBUG_STATS && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam #endif ) { G_addStatsHeadShot(attacker, mod); } if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Head Shot\n\"\n"); } G_LogRegionHit( attacker, HR_HEAD ); hr = HR_HEAD; } else if ( IsLegShot(targ, dir, point, mod) ) { G_LogRegionHit( attacker, HR_LEGS ); hr = HR_LEGS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Leg Shot\n\"\n"); } } else if ( IsArmShot(targ, attacker, point, mod) ) { G_LogRegionHit( attacker, HR_ARMS ); hr = HR_ARMS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Arm Shot\n\"\n"); } } else if (targ->client && targ->health > 0 && IsHeadShotWeapon( mod ) ) { G_LogRegionHit( attacker, HR_BODY ); hr = HR_BODY; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Body Shot\n\"\n"); } } #ifndef DEBUG_STATS if ( g_debugDamage.integer ) #endif { G_Printf( "client:%i health:%i damage:%i mod:%s\n", targ->s.number, targ->health, take, modNames[mod] ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier // Team_CheckHurtCarrier(targ, attacker); if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if( take ) { targ->health -= take; // Gordon: don't ever gib POWS if( ( targ->health <= 0 ) && ( targ->r.svFlags & SVF_POW ) ) { targ->health = -1; } // Ridah, can't gib with bullet weapons (except VENOM) // Arnout: attacker == inflictor can happen in other cases as well! (movers trying to gib things) //if ( attacker == inflictor && targ->health <= GIB_HEALTH) { if( targ->health <= GIB_HEALTH ) { if( !G_WeaponIsExplosive( mod ) ) { targ->health = GIB_HEALTH + 1; } } // JPW NERVE overcome previous chunk of code for making grenades work again // if ((take > 190)) // 190 is greater than 2x mauser headshot, so headshots don't gib // Arnout: only player entities! messes up ents like func_constructibles and func_explosives otherwise if( ( (targ->s.number < MAX_CLIENTS) && (take > 190) ) && !(targ->r.svFlags & SVF_POW) ) { targ->health = GIB_HEALTH - 1; } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) ) { targ->s.dl_intensity = 255.f * (targ->health / (float)targ->count); // send it to the client } //G_Printf("health at: %d\n", targ->health); if( targ->health <= 0 ) { if( client && !wasAlive ) { targ->flags |= FL_NO_KNOCKBACK; // OSP - special hack to not count attempts for body gibbage if( targ->client->ps.pm_type == PM_DEAD ) { G_addStats(targ, attacker, take, mod); } if( (targ->health < FORCE_LIMBO_HEALTH) && (targ->health > GIB_HEALTH) ) { limbo(targ, qtrue); } // xkan, 1/13/2003 - record the time we died. if (!client->deathTime) client->deathTime = level.time; } else { targ->sound1to2 = hr; targ->sound2to1 = mod; targ->sound2to3 = (dflags & DAMAGE_RADIUS) ? 1 : 0; if( client ) { if( G_GetTeamFromEntity( inflictor ) != G_GetTeamFromEntity( targ ) ) { G_AddKillSkillPoints( attacker, mod, hr, (dflags & DAMAGE_RADIUS) ); } } if( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; targ->deathType = mod; // Ridah, mg42 doesn't have die func (FIXME) if( targ->die ) { // Kill the entity. Note that this funtion can set ->die to another // function pointer, so that next time die is applied to the dead body. targ->die( targ, inflictor, attacker, take, mod ); // OSP - kill stats in player_die function } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) && (targ->spawnflags & 8) ) { return; // reseructable script mover doesn't unlink itself but we don't want a second death script to be called } // if we freed ourselves in death function if (!targ->inuse) return; // RF, entity scripting if (targ->health <= 0) { // might have revived itself in death function if ((targ->s.eType != ET_CONSTRUCTIBLE && targ->s.eType != ET_EXPLOSIVE) || (targ->s.eType == ET_CONSTRUCTIBLE && !targ->desstages)) { // call manually if using desstages G_Script_ScriptEvent( targ, "death", "" ); } } } } else if ( targ->pain ) { if (dir) { // Ridah, had to add this to fix NULL dir crash VectorCopy (dir, targ->rotate); VectorCopy (point, targ->pos3); // this will pass loc of hit } else { VectorClear( targ->rotate ); VectorClear( targ->pos3 ); } targ->pain (targ, attacker, take, point); } else { // OSP - update weapon/dmg stats G_addStats(targ, attacker, take, mod); // OSP } // RF, entity scripting G_Script_ScriptEvent( targ, "pain", va("%d %d", targ->health, targ->health+take) ); // Ridah, this needs to be done last, incase the health is altered in one of the event calls if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } } }
// Dini, Note, this is where damage etc is dealt out! void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int knockback; qboolean headShot; qboolean wasAlive; hitRegion_t hr = HR_NUM_HITREGIONS; if (!targ->takedamage) { return; } // the intermission has already been qualified for, so don't // allow any extra scoring // CHRUKER: b024 - Don't do damage if at warmup and warmupdamage is set to 'None' and the target is a client. if ( level.intermissionQueued || (g_gamestate.integer != GS_PLAYING && targ->client)) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // Arnout: invisible entities can't be damaged if( targ->entstate == STATE_INVISIBLE || targ->entstate == STATE_UNDERCONSTRUCTION ) { return; } // xkan, 12/23/2002 - was the bot alive before applying any damage? wasAlive = (targ->health > 0); // Arnout: combatstate if( targ->client && attacker && attacker->client && attacker != targ ) { if( g_gamestate.integer == GS_PLAYING ) { if( !OnSameTeam( attacker, targ ) ) { targ->client->combatState |= (1<<COMBATSTATE_DAMAGERECEIVED); attacker->client->combatState |= (1<<COMBATSTATE_DAMAGEDEALT); } } } // JPW NERVE if ((targ->waterlevel >= 3) && (mod == MOD_FLAMETHROWER)) return; // jpw // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER && !(targ->isProp) && !targ->scriptName) { if ( targ->use && targ->moverState == MOVER_POS1 ) { G_UseEntity( targ, inflictor, attacker ); } return; } // 4 means destructible if ( targ->s.eType == ET_MOVER && (targ->spawnflags & 4) && !targ->isProp ) { if( !G_WeaponIsExplosive( mod ) ) { return; } // check for team if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } } else if ( targ->s.eType == ET_EXPLOSIVE ) { if( targ->parent && G_GetWeaponClassForMOD( mod ) == 2 ) { return; } if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } else if ( targ->s.eType == ET_MISSILE && targ->methodOfDeath == MOD_LANDMINE ) { if( targ->s.modelindex2 ) { if( G_WeaponIsExplosive( mod ) ) { mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } if( attacker && attacker->client ) { AddScore( attacker, 1 ); } G_ExplodeMissile(targ); } } return; } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } //bani - fix #238 if ( mod == MOD_DYNAMITE ) { if( !( inflictor->etpro_misc_1 & 1 ) ) return; } } client = targ->client; // Dini, Note, Noclip.. if ( client ) { if ( client->noclip || client->ps.powerups[PW_INVULNERABLE] ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } // Only do knockback to yourself if (attacker->client == targ->client) knockback = 1; else knockback = 0; // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; int knock; if (mod == MOD_PANZERFAUST) knock = 500; else knock = 1000; // Dini, Note, Fix this sometime.. VectorScale (dir, knock, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); if (targ == attacker && !( mod != MOD_ROCKET && mod != MOD_GRENADE && mod != MOD_GRENADE_LAUNCHER && mod != MOD_DYNAMITE && mod != MOD_GPG40 && mod != MOD_M7 && mod != MOD_LANDMINE )) { targ->client->ps.velocity[2] *= 0.25; } // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // Dini, disable damage taking for players, aka if they're on a team if (OnAnyTeam (targ, attacker) && !tjg_damage.integer) return; // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team if ( targ != attacker && OnSameTeam (targ, attacker) ) { if ( (g_gamestate.integer != GS_PLAYING)) { return; } else if (!g_friendlyFire.integer) { return; } } } // add to the attacker's hit counter if ( attacker->client && targ != attacker && targ->health > 0 ) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS] -= damage; } else { attacker->client->ps.persistant[PERS_HITS] += damage; } } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; headShot = IsHeadShot(targ, dir, point, mod); if ( headShot ) { if( take * 2 < 50 ) // head shots, all weapons, do minimum 50 points damage take = 50; else take *= 2; // sniper rifles can do full-kill (and knock into limbo) if( dflags & DAMAGE_DISTANCEFALLOFF ) { vec_t dist; vec3_t shotvec; float scale; VectorSubtract( point, muzzleTrace, shotvec ); dist = VectorLength( shotvec ); // zinx - start at 100% at 1500 units (and before), // and go to 20% at 2500 units (and after) // 1500 to 2500 -> 0.0 to 1.0 scale = (dist - 1500.f) / (2500.f - 1500.f); // 0.0 to 1.0 -> 0.0 to 0.8 scale *= 0.8f; // 0.0 to 0.8 -> 1.0 to 0.2 scale = 1.0f - scale; // And, finally, cap it. if (scale > 1.0f) scale = 1.0f; else if (scale < 0.2f) scale = 0.2f; take *= scale; } if( !(targ->client->ps.eFlags & EF_HEADSHOT) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte(dir) ); if( mod != MOD_K43_SCOPE && mod != MOD_GARAND_SCOPE ) { take *= .8f; // helmet gives us some protection } } targ->client->ps.eFlags |= EF_HEADSHOT; // OSP - Record the headshot if(client && attacker && attacker->client #ifndef DEBUG_STATS && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam #endif ) { G_addStatsHeadShot(attacker, mod); } if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Head Shot\n\"\n"); } G_LogRegionHit( attacker, HR_HEAD ); hr = HR_HEAD; } else if ( IsLegShot(targ, dir, point, mod) ) { G_LogRegionHit( attacker, HR_LEGS ); hr = HR_LEGS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Leg Shot\n\"\n"); } } else if ( IsArmShot(targ, attacker, point, mod) ) { G_LogRegionHit( attacker, HR_ARMS ); hr = HR_ARMS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Arm Shot\n\"\n"); } } else if (targ->client && targ->health > 0 && IsHeadShotWeapon( mod ) ) { G_LogRegionHit( attacker, HR_BODY ); hr = HR_BODY; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Body Shot\n\"\n"); } } #ifndef DEBUG_STATS if ( g_debugDamage.integer ) #endif { G_Printf( "client:%i health:%i damage:%i mod:%s\n", targ->s.number, targ->health, take, modNames[mod] ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_blood += take; // Dini, removed for now. //client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier // Team_CheckHurtCarrier(targ, attacker); if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if( take ) { targ->health -= take; // Gordon: don't ever gib POWS if( ( targ->health <= 0 ) && ( targ->r.svFlags & SVF_POW ) ) { targ->health = -1; } // Ridah, can't gib with bullet weapons (except VENOM) // Arnout: attacker == inflictor can happen in other cases as well! (movers trying to gib things) //if ( attacker == inflictor && targ->health <= GIB_HEALTH) { /*if( targ->health <= GIB_HEALTH ) { if( !G_WeaponIsExplosive( mod ) ) { targ->health = GIB_HEALTH + 1; } }*/ // JPW NERVE overcome previous chunk of code for making grenades work again // if ((take > 190)) // 190 is greater than 2x mauser headshot, so headshots don't gib // Arnout: only player entities! messes up ents like func_constructibles and func_explosives otherwise if( ( (targ->s.number < MAX_CLIENTS) && (take > 190) ) && !(targ->r.svFlags & SVF_POW) ) { targ->health = GIB_HEALTH - 1; } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) ) { targ->s.dl_intensity = 255.f * (targ->health / (float)targ->count); // send it to the client } //G_Printf("health at: %d\n", targ->health); if( targ->health <= 0 ) { if( client && !wasAlive ) { targ->flags |= FL_NO_KNOCKBACK; // OSP - special hack to not count attempts for body gibbage if( targ->client->ps.pm_type == PM_DEAD ) { G_addStats(targ, attacker, take, mod); } /*if( (targ->health < FORCE_LIMBO_HEALTH) && (targ->health > GIB_HEALTH) ) { limbo(targ, qtrue); }*/ if (targ->health <= 0 && targ->s.number < MAX_CLIENTS && !(targ->r.svFlags & SVF_POW)) { targ->health = GIB_HEALTH - 1; } // xkan, 1/13/2003 - record the time we died. if (!client->deathTime) client->deathTime = level.time; } else { targ->sound1to2 = hr; targ->sound2to1 = mod; targ->sound2to3 = (dflags & DAMAGE_RADIUS) ? 1 : 0; if( client ) { if( G_GetTeamFromEntity( inflictor ) != G_GetTeamFromEntity( targ ) ) { G_AddKillSkillPoints( attacker, mod, hr, (dflags & DAMAGE_RADIUS) ); } } if( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; targ->deathType = mod; // Ridah, mg42 doesn't have die func (FIXME) if( targ->die ) { // Kill the entity. Note that this funtion can set ->die to another // function pointer, so that next time die is applied to the dead body. targ->die( targ, inflictor, attacker, take, mod ); // OSP - kill stats in player_die function } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) && (targ->spawnflags & 8) ) { return; // reseructable script mover doesn't unlink itself but we don't want a second death script to be called } // if we freed ourselves in death function if (!targ->inuse) return; // RF, entity scripting if ( targ->health <= 0) { // might have revived itself in death function if( ( targ->s.eType != ET_CONSTRUCTIBLE && targ->s.eType != ET_EXPLOSIVE ) || ( targ->s.eType == ET_CONSTRUCTIBLE && !targ->desstages ) ) { // call manually if using desstages G_Script_ScriptEvent( targ, "death", "" ); } } } } else if ( targ->pain ) { if (dir) { // Ridah, had to add this to fix NULL dir crash VectorCopy (dir, targ->rotate); VectorCopy (point, targ->pos3); // this will pass loc of hit } else { VectorClear( targ->rotate ); VectorClear( targ->pos3 ); } targ->pain (targ, attacker, take, point); } else { // OSP - update weapon/dmg stats G_addStats(targ, attacker, take, mod); // OSP } // RF, entity scripting G_Script_ScriptEvent( targ, "pain", va("%d %d", targ->health, targ->health+take) ); // Ridah, this needs to be done last, incase the health is altered in one of the event calls if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } } }