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 asave; int knockback; int max; #ifdef MISSIONPACK vec3_t bouncedir, impactpoint; #endif if (!targ->takedamage) { return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } #ifdef MISSIONPACK if ( targ->client && mod != MOD_JUICED) { if ( targ->client->invulnerabilityTime > level.time) { if ( dir && point ) { G_InvulnerabilityEffect( targ, dir, point, impactpoint, bouncedir ); } return; } } #endif if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER ) { if ( targ->use && targ->moverState == MOVER_POS1 ) { targ->use( targ, inflictor, attacker ); } return; } #ifdef MISSIONPACK if( g_gametype.integer == GT_OBELISK && CheckObeliskAttack( targ, attacker ) ) { return; } #endif // reduce damage by the attacker's handicap value // unless they are rocket jumping if ( attacker->client && attacker != targ ) { max = attacker->client->ps.stats[STAT_MAX_HEALTH]; #ifdef MISSIONPACK if( bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { max /= 2; } #endif damage = damage * max / 100; } client = targ->client; if ( client ) { if ( client->noclip ) { 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; } // 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); // 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 #ifdef MISSIONPACK if ( mod != MOD_JUICED && targ != attacker && !(dflags & DAMAGE_NO_TEAM_PROTECTION) && OnSameTeam (targ, attacker) ) { #else if ( targ != attacker && OnSameTeam (targ, attacker) ) { #endif if ( !g_friendlyFire.integer ) { return; } } #ifdef MISSIONPACK if (mod == MOD_PROXIMITY_MINE) { if (inflictor && inflictor->parent && OnSameTeam(targ, inflictor->parent)) { return; } if (targ == attacker) { return; } } #endif // check for godmode if ( targ->flags & FL_GODMODE ) { return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) { return; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if ( attacker->client && client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE && targ->s.eType != ET_GENERAL) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if ( targ == attacker) { damage *= 0.5; } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // save some from armor asave = CheckArmor (targ, take, dflags); take -= asave; if ( g_debugDamage.integer ) { G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave ); } // 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_armor += asave; 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 #ifdef MISSIONPACK if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF ) { #else if( g_gametype.integer == GT_CTF) { #endif 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 = targ->health - take; if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if ( targ->health <= 0 ) { if ( client ) targ->flags |= FL_NO_KNOCKBACK; if (targ->health < -999) targ->health = -999; targ->enemy = attacker; targ->die (targ, inflictor, attacker, take, mod); return; } else if ( targ->pain ) { targ->pain (targ, attacker, take); } } } /* ============ CanDamage Returns qtrue if the inflictor can directly damage the target. Used for explosions and melee attacks. ============ */ qboolean CanDamage (gentity_t *targ, vec3_t origin) { vec3_t dest; trace_t tr; vec3_t midpoint; // use the midpoint of the bounds instead of the origin, because // bmodels may have their origin is 0,0,0 VectorAdd (targ->r.absmin, targ->r.absmax, midpoint); VectorScale (midpoint, 0.5, midpoint); VectorCopy (midpoint, dest); trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0 || tr.entityNum == targ->s.number) return qtrue; // this should probably check in the plane of projection, // rather than in world coordinate, and also include Z VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; return qfalse; } /* ============ G_RadiusDamage ============ */ qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod) { float points, dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; if ( radius < 1 ) { radius = 1; } for ( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; if (ent == ignore) continue; if (!ent->takedamage) continue; // find the distance from the edge of the bounding box for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if( CanDamage (ent, origin) ) { if( LogAccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract (ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage (ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod); } } return hitClient; }
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 asave; int knockback; int max; vec3_t bouncedir, impactpoint; if (!targ->takedamage) { return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } if ( targ->client && mod != MOD_JUICED) { if ( targ->client->invulnerabilityTime > level.time) { if ( dir && point ) { G_InvulnerabilityEffect( targ, dir, point, impactpoint, bouncedir ); } return; } } //Sago: This was moved up client = targ->client; //Sago: See if the client was sent flying //Check if damage is by somebody who is not a player! if( (!attacker || attacker->s.eType != ET_PLAYER) && client && client->lastSentFlying>-1 && ( mod==MOD_FALLING || mod==MOD_LAVA || mod==MOD_SLIME || mod==MOD_TRIGGER_HURT || mod==MOD_SUICIDE) ) { if( client->lastSentFlyingTime+5000<level.time) { client->lastSentFlying = -1; //More than 5 seconds, not a kill! } else { //G_Printf("LastSentFlying %i\n",client->lastSentFlying); attacker = &g_entities[client->lastSentFlying]; } } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER ) { if ( targ->use && targ->moverState == MOVER_POS1 ) { targ->use( targ, inflictor, attacker ); } return; } if( g_gametype.integer == GT_OBELISK && CheckObeliskAttack( targ, attacker ) ) { return; } // reduce damage by the attacker's handicap value // unless they are rocket jumping if ( attacker->client && attacker != targ ) { max = attacker->client->ps.stats[STAT_MAX_HEALTH]; if( bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { max /= 2; } damage = damage * max / 100; } //Sago: I have moved this up //client = targ->client; if ( client ) { if ( client->noclip ) { 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; } // 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); // 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; } //Remeber the last person to hurt the player if( !g_awardpushing.integer || targ==attacker || OnSameTeam (targ, attacker)) { targ->client->lastSentFlying = -1; } else { /*if ( pm->waterlevel <= 1 ) { if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) { // if getting knocked back, no friction if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) { control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*pm_friction*pml.frametime; } } }*/ targ->client->lastSentFlying = attacker->s.number; targ->client->lastSentFlyingTime = level.time; } } // 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 ( mod != MOD_JUICED && mod != MOD_CRUSH && targ != attacker && !(dflags & DAMAGE_NO_TEAM_PROTECTION) && OnSameTeam (targ, attacker) ) { if ( ( !g_friendlyFire.integer && g_gametype.integer != GT_ELIMINATION && g_gametype.integer != GT_CTF_ELIMINATION ) || ( g_elimination_selfdamage.integer<2 && (g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION) ) ) { return; } } if (mod == MOD_PROXIMITY_MINE) { if (inflictor && inflictor->parent && OnSameTeam(targ, inflictor->parent)) { return; } if (targ == attacker) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } if(targ->client && targ->client->spawnprotected) { if(level.time>targ->client->respawnTime+g_spawnprotect.integer) targ->client->spawnprotected = qfalse; else if( (mod > MOD_UNKNOWN && mod < MOD_WATER) || mod == MOD_TELEFRAG || mod>MOD_TRIGGER_HURT) return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) { return; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if ( attacker->client && client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE && targ->s.eType != ET_GENERAL) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if ( targ == attacker) { damage *= 0.5; } if(targ && targ->client && attacker->client ) damage = catchup_damage(damage, attacker->client->ps.persistant[PERS_SCORE], targ->client->ps.persistant[PERS_SCORE]); if(g_damageModifier.value > 0.01) { damage *= g_damageModifier.value; } if ( damage < 1 ) { damage = 1; } if(targ == attacker && (g_dmflags.integer & DF_NO_SELF_DAMAGE) ) damage = 0; if ((g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS || g_elimination_allgametypes.integer) && g_elimination_selfdamage.integer<1 && ( targ == attacker || mod == MOD_FALLING )) { damage = 0; } //So people can be telefragged! if ((g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) && level.time < level.roundStartTime && ((mod == MOD_LAVA) || (mod == MOD_SLIME)) ) { damage = 1000; } take = damage; //save = 0; // save some from armor asave = CheckArmor (targ, take, dflags); take -= asave; if ( g_debugDamage.integer ) { G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave ); } // 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 if(client->lastSentFlying) { client->ps.persistant[PERS_ATTACKER] = client->lastSentFlying; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_armor += asave; 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 if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF || g_gametype.integer == GT_CTF_ELIMINATION) { 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; } //If vampire is enabled, gain health but not from self or teammate, cannot steal more than targ has if( g_vampire.value>0.0 && (targ != attacker) && take > 0 && !(OnSameTeam(targ, attacker)) && attacker->health > 0 && targ->health > 0 ) { if(take<targ->health) attacker->health += (int)(((float)take)*g_vampire.value); else attacker->health += (int)(((float)targ->health)*g_vampire.value); if(attacker->health>g_vampireMaxHealth.integer) attacker->health = g_vampireMaxHealth.integer; } // do the damage if (take) { targ->health = targ->health - take; if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if ( targ->health <= 0 ) { if ( client ) targ->flags |= FL_NO_KNOCKBACK; if (targ->health < -999) targ->health = -999; targ->enemy = attacker; targ->die (targ, inflictor, attacker, take, mod); return; } else if ( targ->pain ) { targ->pain (targ, attacker, take); } } }
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 asave; int knockback; if ( !targ->takedamage ) { return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued || g_gamestate.integer != GS_PLAYING ) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // 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->aiName ) && !( targ->isProp ) && !targ->scriptName ) { if ( targ->use && targ->moverState == MOVER_POS1 ) { targ->use( targ, inflictor, attacker ); } return; } if ( targ->s.eType == ET_MOVER && targ->aiName && !( targ->isProp ) && !targ->scriptName ) { switch ( mod ) { case MOD_GRENADE: case MOD_GRENADE_SPLASH: case MOD_ROCKET: case MOD_ROCKET_SPLASH: break; default: return; // no damage from other weapons } } else if ( targ->s.eType == ET_EXPLOSIVE ) { // 32 Explosive // 64 Dynamite only if ( ( targ->spawnflags & 32 ) || ( targ->spawnflags & 64 ) ) { switch ( mod ) { case MOD_GRENADE: case MOD_GRENADE_SPLASH: case MOD_ROCKET: case MOD_ROCKET_SPLASH: case MOD_AIRSTRIKE: case MOD_GRENADE_PINEAPPLE: case MOD_MORTAR: case MOD_MORTAR_SPLASH: case MOD_EXPLOSIVE: if ( targ->spawnflags & 64 ) { return; } break; case MOD_DYNAMITE: case MOD_DYNAMITE_SPLASH: break; default: return; } } } client = targ->client; if ( client ) { if ( client->noclip ) { 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; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client && ( g_friendlyFire.integer || !OnSameTeam( targ, attacker ) ) ) { vec3_t kvel; float mass; mass = 200; if ( mod == MOD_LIGHTNING && !( ( level.time + targ->s.number * 50 ) % 400 ) ) { knockback = 60; dir[2] = 0.3; } VectorScale( dir, g_knockback.value * (float)knockback / mass, kvel ); VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity ); if ( targ == attacker && !( mod != MOD_ROCKET && mod != MOD_ROCKET_SPLASH && mod != MOD_GRENADE && mod != MOD_GRENADE_SPLASH && mod != MOD_DYNAMITE ) ) { 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_friendlyFire.integer ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } // RF, warzombie defense position is basically godmode for the time being if ( targ->flags & FL_DEFENSE_GUARD ) { return; } // check for invulnerability // (SA) moved from below so DAMAGE_NO_PROTECTION will still work if ( client && client->ps.powerups[PW_INVULNERABLE] ) { //----(SA) added return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( dflags & DAMAGE_RADIUS ) { return; } damage *= 0.5; } // 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; // save some from armor asave = CheckArmor( targ, take, dflags ); take -= asave; if ( IsHeadShot( targ, qfalse, dir, point, mod ) ) { 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 ( !( targ->client->ps.eFlags & EF_HEADSHOT ) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte( dir ) ); } targ->client->ps.eFlags |= EF_HEADSHOT; } if ( g_debugDamage.integer ) { G_Printf( "client:%i health:%i damage:%i armor:%i\n", targ->s.number, targ->health, take, asave ); } // 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_armor += asave; 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 = targ->health - take; // Ridah, can't gib with bullet weapons (except VENOM) if ( mod != MOD_VENOM && attacker == inflictor && targ->health <= GIB_HEALTH ) { if ( targ->aiCharacter != AICHAR_ZOMBIE ) { // zombie needs to be able to gib so we can kill him (although he doesn't actually GIB, he just dies) targ->health = GIB_HEALTH + 1; } } // JPW NERVE overcome previous chunk of code for making grenades work again if ( ( g_gametype.integer != GT_SINGLE_PLAYER ) && ( take > 190 ) ) { // 190 is greater than 2x mauser headshot, so headshots don't gib targ->health = GIB_HEALTH - 1; } // jpw //G_Printf("health at: %d\n", targ->health); if ( targ->health <= 0 ) { if ( client ) { targ->flags |= FL_NO_KNOCKBACK; // JPW NERVE -- repeated shooting sends to limbo if ( g_gametype.integer >= GT_WOLF ) { if ( ( targ->health < FORCE_LIMBO_HEALTH ) && ( targ->health > GIB_HEALTH ) && ( !( targ->client->ps.pm_flags & PMF_LIMBO ) ) ) { limbo( targ, qtrue ); } } // jpw } if ( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; if ( targ->die ) { // Ridah, mg42 doesn't have die func (FIXME) targ->die( targ, inflictor, attacker, take, mod ); } // if we freed ourselves in death function if ( !targ->inuse ) { return; } // RF, entity scripting if ( targ->s.number >= MAX_CLIENTS && targ->health <= 0 ) { // might have revived itself in death function 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 ); // RF, entity scripting if ( targ->s.number >= MAX_CLIENTS ) { G_Script_ScriptEvent( targ, "pain", va( "%d %d", targ->health, targ->health + take ) ); } } //G_ArmorDamage(targ); //----(SA) moved out to separate routine // 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; } } }
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 asave; int knockback; if ( !targ->takedamage ) { return; } //----(SA) added if ( g_gametype.integer == GT_SINGLE_PLAYER && !targ->aiCharacter && targ->client && targ->client->cameraPortal ) { // get out of damage in sp if in cutscene. return; } //----(SA) end // if (reloading || saveGamePending) { // map transition is happening, don't do anything if ( g_reloading.integer || saveGamePending ) { return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } // RF, track pain for player // This is used by AI to determine how long it has been since their enemy was injured if ( attacker ) { // (SA) whoops, for falling damage there's no attacker if ( targ->client && attacker->client && !( targ->r.svFlags & SVF_CASTAI ) && ( attacker->r.svFlags & SVF_CASTAI ) ) { AICast_RegisterPain( targ->s.number ); } } #ifdef AUTOAIM //If player shoots target if (attacker && !(attacker->r.svFlags& SVF_CASTAI) && (targ->r.svFlags & SVF_CASTAI)) { if (targ->health > 0) //if target is already dead then ignore { g_autoAimEntity = targ; } else { g_autoAimEntity = NULL; } } #endif if ( ( g_gametype.integer == GT_SINGLE_PLAYER ) && !( targ->r.svFlags & SVF_CASTAI ) ) { // the player switch ( mod ) { case MOD_GRENADE: case MOD_GRENADE_SPLASH: case MOD_ROCKET: case MOD_ROCKET_SPLASH: // Rafael - had to change this since the // we added a new lvl of diff if ( g_gameskill.integer == GSKILL_EASY ) { damage *= 0.25; } else if ( g_gameskill.integer == GSKILL_MEDIUM ) { damage *= 0.75; } else if ( g_gameskill.integer == GSKILL_HARD ) { damage *= 0.9; } else { damage *= 0.9; } default: break; } } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER && !( targ->aiName ) && !( targ->isProp ) && !targ->scriptName ) { if ( targ->use && targ->moverState == MOVER_POS1 ) { targ->use( targ, inflictor, attacker ); } return; } if ( targ->s.eType == ET_MOVER && targ->aiName && !( targ->isProp ) && !targ->scriptName ) { switch ( mod ) { case MOD_GRENADE: case MOD_GRENADE_SPLASH: case MOD_ROCKET: case MOD_ROCKET_SPLASH: break; default: return; // no damage from other weapons } } else if ( targ->s.eType == ET_EXPLOSIVE ) { // 32 Explosive // 64 Dynamite only if ( ( targ->spawnflags & 32 ) || ( targ->spawnflags & 64 ) ) { switch ( mod ) { case MOD_GRENADE: case MOD_GRENADE_SPLASH: case MOD_ROCKET: case MOD_ROCKET_SPLASH: case MOD_AIRSTRIKE: case MOD_GRENADE_PINEAPPLE: case MOD_MORTAR: case MOD_MORTAR_SPLASH: case MOD_EXPLOSIVE: if ( targ->spawnflags & 64 ) { return; } break; case MOD_DYNAMITE: case MOD_DYNAMITE_SPLASH: break; default: return; } } } // reduce damage by the attacker's handicap value // unless they are rocket jumping // Ridah, not in single player (skill levels?) // JPW NERVE pulled this from multiplayer too /* if (g_gametype.integer != GT_SINGLE_PLAYER) // done. if ( attacker->client && attacker != targ ) { damage = damage * attacker->client->ps.stats[STAT_MAX_HEALTH] / 100; } */ // jpw // Ridah, Cast AI's don't hurt other Cast AI's as much if ( ( attacker->r.svFlags & SVF_CASTAI ) && ( targ->r.svFlags & SVF_CASTAI ) ) { if ( !AICast_AIDamageOK( AICast_GetCastState( targ->s.number ), AICast_GetCastState( attacker->s.number ) ) ) { return; } damage = (int)( ceil( (float)damage * 0.5 ) ); } // done. client = targ->client; if ( client ) { if ( client->noclip ) { return; } } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize( dir ); } knockback = damage; // if ( knockback > 200 ) // knockback = 200; if ( knockback > 60 ) { // /way/ lessened for SP. keeps dynamite-jumping potential down knockback = 60; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; if ( mod == MOD_LIGHTNING && !( ( level.time + targ->s.number * 50 ) % 400 ) ) { knockback = 60; dir[2] = 0.3; } VectorScale( dir, g_knockback.value * (float)knockback / mass, kvel ); VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity ); if ( targ == attacker && !( mod != MOD_ROCKET && mod != MOD_ROCKET_SPLASH && mod != MOD_GRENADE && mod != MOD_GRENADE_SPLASH && mod != MOD_DYNAMITE ) ) { 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_friendlyFire.integer ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } // RF, warzombie defense position is basically godmode for the time being if ( targ->flags & FL_DEFENSE_GUARD ) { return; } // check for invulnerability // (SA) moved from below so DAMAGE_NO_PROTECTION will still work if ( client && client->ps.powerups[PW_INVULNERABLE] ) { //----(SA) added return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( dflags & DAMAGE_RADIUS ) { return; } damage *= 0.5; } // Ridah, don't play these in single player if ( g_gametype.integer != GT_SINGLE_PLAYER ) { // done. // 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; } } } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // JPW NERVE -- removed from multiplayer -- plays havoc with pfaust & demolition balancing qboolean dynamite = (qboolean)( mod == MOD_DYNAMITE || mod == MOD_DYNAMITE_SPLASH ); if ( targ == attacker ) { if ( !dynamite ) { damage *= 0.5; } } if ( dynamite && targ->aiCharacter == AICHAR_HELGA ) { //helga gets special dynamite damage damage *= 0.5; } } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // save some from armor asave = CheckArmor( targ, take, dflags ); take -= asave; if ( IsHeadShot( targ, attacker, dir, point, mod ) ) { // JPW NERVE -- different headshot behavior in multiplayer if ( g_gametype.integer != GT_SINGLE_PLAYER ) { 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 ( !( targ->client->ps.eFlags & EF_HEADSHOT ) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte( dir ) ); } } // jpw else { // by default, a headshot means damage x2 take *= 2; // RF, allow headshot damage multiplier (helmets, etc) // yes, headshotDamageScale of 0 gives no damage, thats because // the bullet hit the head which is fully protected. take *= targ->headshotDamageScale; // player only code if ( !attacker->aiCharacter ) { // (SA) id reqests one-shot kills for head shots on common humanoids // (SA) except pistols. // first pistol head shot does normal 2x damage and flings hat, second gets kill // if((mod != MOD_LUGER && mod != MOD_COLT ) || (targ->client->ps.eFlags & EF_HEADSHOT)) { // (SA) DM requests removing double shot pistol head shots (3/19) // (SA) removed BG for DM. if ( !( dflags & DAMAGE_PASSTHRU ) ) { // ignore headshot 2x damage and snooper-instant-death if the bullet passed through something. just do reg damage. switch ( targ->aiCharacter ) { case AICHAR_BLACKGUARD: if ( !( targ->client->ps.eFlags & EF_HEADSHOT ) ) { // only obliterate him after he's lost his helmet break; } case AICHAR_SOLDIER: case AICHAR_AMERICAN: case AICHAR_ELITEGUARD: case AICHAR_PARTISAN: case AICHAR_CIVILIAN: take = 200; break; default: break; } } if ( !( targ->client->ps.eFlags & EF_HEADSHOT ) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte( dir ) ); } } } // JPW // shared by both player and ai targ->client->ps.eFlags |= EF_HEADSHOT; } else { // non headshot if ( !( dflags & DAMAGE_PASSTHRU ) ) { // ignore headshot 2x damage and snooper-instant-death if the bullet passed through something. just do reg damage. // snooper kills these types in one shot with any contact if ( ( mod == MOD_SNOOPERSCOPE || mod == MOD_GARAND ) && !( attacker->aiCharacter ) ) { switch ( targ->aiCharacter ) { case AICHAR_SOLDIER: case AICHAR_AMERICAN: case AICHAR_ELITEGUARD: case AICHAR_BLACKGUARD: case AICHAR_PARTISAN: case AICHAR_CIVILIAN: take = 200; break; default: break; } } } } if ( g_debugDamage.integer ) { G_Printf( "client:%i health:%i damage:%i armor:%i\n", targ->s.number, targ->health, take, asave ); } // 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_armor += asave; 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 = targ->health - take; // Ridah, can't gib with bullet weapons (except VENOM) if ( targ->client ) { if ( mod != MOD_VENOM && attacker == inflictor && targ->health <= GIB_HEALTH ) { if ( targ->aiCharacter != AICHAR_ZOMBIE ) { // zombie needs to be able to gib so we can kill him (although he doesn't actually GIB, he just dies) targ->health = GIB_HEALTH + 1; } } } //G_Printf("health at: %d\n", targ->health); if ( targ->health <= 0 ) { if ( client ) { targ->flags |= FL_NO_KNOCKBACK; } if ( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; if ( targ->die ) { // Ridah, mg42 doesn't have die func (FIXME) targ->die( targ, inflictor, attacker, take, mod ); } // if we freed ourselves in death function if ( !targ->inuse ) { return; } // RF, entity scripting if ( targ->s.number >= MAX_CLIENTS && targ->health <= 0 ) { // might have revived itself in death function 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 ); } G_ArmorDamage( targ ); //----(SA) moved out to separate routine // 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; } } }