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 T_Damage (edict_t * targ, edict_t * inflictor, edict_t * attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) { gclient_t *client; char buf[256]; int take, save; int asave, psave; int te_sparks, do_sparks = 0; int damage_type = 0; // used for MOD later int bleeding = 0; // damage causes bleeding int head_success = 0; int instant_dam = 1; float z_rel; int height; float from_top; vec_t dist; float targ_maxs2; //FB 6/1/99 // do this before teamplay check if (!targ->takedamage) return; //FIREBLADE if (teamplay->value && mod != MOD_TELEFRAG) { if (lights_camera_action) return; // AQ2:TNG - JBravo adding UVtime if (ctf->value && targ->client) { if(targ->client->ctf_uvtime > 0) return; if (attacker->client && attacker->client->ctf_uvtime > 0) return; } // AQ2:TNG - JBravo adding FF after rounds if (targ != attacker && targ->client && attacker->client && targ->client->resp.team == attacker->client->resp.team && ((int)(dmflags->value) & (DF_NO_FRIENDLY_FIRE))) { if (team_round_going) return; else if (!ff_afterround->value) return; } // AQ:TNG } //FIREBLADE if (dm_shield->value && targ->client) { if (targ->client->ctf_uvtime > 0) return; if (attacker->client && attacker->client->ctf_uvtime > 0) return; } // damage reduction for shotgun // if far away, reduce it to original action levels if (mod == MOD_M3) { dist = Distance(targ->s.origin, inflictor->s.origin); if (dist > 450.0) damage = damage - 2; } targ_maxs2 = targ->maxs[2]; if (targ_maxs2 == 4) targ_maxs2 = CROUCHING_MAXS2; //FB 6/1/99 height = abs (targ->mins[2]) + targ_maxs2; // locational damage code // base damage is head shot damage, so all the scaling is downwards if (targ->client) { if (!((targ != attacker) && ((deathmatch->value && ((int)dmflags->value & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value) && (attacker && attacker->client && OnSameTeam (targ, attacker) && ((int)dmflags->value & DF_NO_FRIENDLY_FIRE) && (team_round_going && ff_afterround->value)))) { // TNG Stats - Add +1 to hit, make sure that hc and m3 are handles differently if ((attacker->client) && (mod != MOD_M3) && (mod != MOD_HC)) { strcpy(attacker->client->resp.last_damaged_players, targ->client->pers.netname); if (!teamplay->value || team_round_going || stats_afterround->value) { attacker->client->resp.stats_hits[mod]++; attacker->client->resp.stats_shots_h++; } } // TNG Stats END if (mod == MOD_MK23 || mod == MOD_MP5 || mod == MOD_M4 || mod == MOD_SNIPER || mod == MOD_DUAL || mod == MOD_KNIFE || mod == MOD_KNIFE_THROWN) { z_rel = point[2] - targ->s.origin[2]; from_top = targ_maxs2 - z_rel; if (from_top < 0.0) //FB 6/1/99 from_top = 0.0; //Slightly negative values were being handled wrong bleeding = 1; instant_dam = 0; // damage reduction for longer range pistol shots if (mod == MOD_MK23 || mod == MOD_DUAL) { dist = Distance(targ->s.origin, inflictor->s.origin); if (dist > 600.0 && dist < 1400.0) damage = (int) (damage * 2 / 3); else if (dist > 1400.0) damage = (int) (damage * 1 / 2); } //gi.cprintf(targ, PRINT_HIGH, "z_rel is %f\n leg: %f stomach: %f chest: %f\n", z_rel, LEG_DAMAGE, STOMACH_DAMAGE, CHEST_DAMAGE ); //gi.cprintf(targ, PRINT_HIGH, "point[2]: %f targ->s.origin[2]: %f height: %d\n", point[2], targ->s.origin[2], height ); //gi.cprintf(targ, PRINT_HIGH, "abs(trag->min[2]): %d targ_max[2] %d\n", (int)abs(targ->mins[2]), (int)targ_maxs2); //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[2]): %d targ_max[2] %d\n", (int)abs(targ->mins[2]), (int)targ_maxs2); //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[0]): %d targ_max[0] %d\n", (int)abs(targ->mins[0]), (int)targ->maxs[0]); //gi.cprintf(attacker, PRINT_HIGH, "abs(trag->min[1]): %d targ_max[1] %d\n", (int)abs(targ->mins[1]), (int)targ->maxs[1]); if (from_top < 2 * HEAD_HEIGHT) { vec3_t new_point; VerifyHeadShot (point, dir, HEAD_HEIGHT, new_point); VectorSubtract (new_point, targ->s.origin, new_point); //gi.cprintf(attacker, PRINT_HIGH, "z: %d y: %d x: %d\n", (int)(targ_maxs2 - new_point[2]),(int)(new_point[1]) , (int)(new_point[0]) ); if ((targ_maxs2 - new_point[2]) < HEAD_HEIGHT && (abs (new_point[1])) < HEAD_HEIGHT * .8 && (abs (new_point[0])) < HEAD_HEIGHT * .8) { head_success = 1; } } if (head_success) { if (attacker->client) { if (!teamplay->value || team_round_going || stats_afterround->value) { attacker->client->resp.stats_headshot[mod]++; } //AQ2:TNG Slicer Last Damage Location if (INV_AMMO(targ, HELM_NUM)) { attacker->client->resp.last_damaged_part = LOC_KVLR_HELMET; if ((!teamplay->value || team_round_going || stats_afterround->value)) attacker->client->resp.stats_locations[LOC_KVLR_HELMET]++; } else { attacker->client->resp.last_damaged_part = LOC_HDAM; if ((!teamplay->value || team_round_going || stats_afterround->value)) attacker->client->resp.stats_locations[LOC_HDAM]++; } //AQ2:TNG END if (!OnSameTeam (targ, attacker)) attacker->client->resp.hs_streak++; // AQ:TNG Igor[Rock] changing sound dir if (attacker->client->resp.hs_streak == 3) { if (use_rewards->value) { sprintf (buf, "ACCURACY %s!", attacker->client->pers.netname); CenterPrintAll (buf); gi.sound (&g_edicts[0], CHAN_VOICE | CHAN_NO_PHS_ADD, gi.soundindex ("tng/accuracy.wav"), 1.0, ATTN_NONE, 0.0); } attacker->client->resp.hs_streak = 0; } // end of changing sound dir } if (INV_AMMO(targ, HELM_NUM) && mod != MOD_KNIFE && mod != MOD_KNIFE_THROWN && mod != MOD_SNIPER) { if (attacker->client) { gi.cprintf (attacker, PRINT_HIGH, "%s has a Kevlar Helmet - AIM FOR THE BODY!\n", targ->client->pers.netname); gi.cprintf (targ, PRINT_HIGH, "Kevlar Helmet absorbed a part of %s's shot\n", attacker->client->pers.netname); } gi.sound (targ, CHAN_ITEM, gi.soundindex("misc/vest.wav"), 1, ATTN_NORM, 0); damage = (int) (damage / 2); damage_type = LOC_HDAM; bleeding = 0; instant_dam = 1; stopAP = 1; do_sparks = 1; } else if (INV_AMMO(targ, HELM_NUM) && mod == MOD_SNIPER) { if (attacker->client) { gi.cprintf (attacker, PRINT_HIGH, "%s has a Kevlar Helmet, too bad you have AP rounds...\n", targ->client->pers.netname); gi.cprintf (targ, PRINT_HIGH, "Kevlar Helmet absorbed some of %s's AP sniper round\n", attacker->client->pers.netname); } damage = (int) (damage * 0.325); gi.sound (targ, CHAN_VOICE, gi.soundindex("misc/headshot.wav"), 1, ATTN_NORM, 0); damage_type = LOC_HDAM; } else { damage = damage * 1.8 + 1; gi.cprintf (targ, PRINT_HIGH, "Head damage\n"); if (attacker->client) gi.cprintf (attacker, PRINT_HIGH, "You hit %s in the head\n", targ->client->pers.netname); damage_type = LOC_HDAM; if (mod != MOD_KNIFE && mod != MOD_KNIFE_THROWN) gi.sound (targ, CHAN_VOICE, gi.soundindex ("misc/headshot.wav"), 1, ATTN_NORM, 0); //else // gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/glurp.wav"), 1, ATTN_NORM, 0); } } else if (z_rel < LEG_DAMAGE) { damage = damage * .25; gi.cprintf (targ, PRINT_HIGH, "Leg damage\n"); if (attacker->client) { attacker->client->resp.hs_streak = 0; gi.cprintf (attacker, PRINT_HIGH, "You hit %s in the legs\n", targ->client->pers.netname); } damage_type = LOC_LDAM; if (!heroes->value || targ->client->resp.team == 2) { // ESJ Heroes don't run out targ->client->leg_damage = 1; targ->client->leghits++; } //AQ2:TNG Slicer Last Damage Location attacker->client->resp.last_damaged_part = LOC_LDAM; //AQ2:TNG END if (!teamplay->value || team_round_going || stats_afterround->value) attacker->client->resp.stats_locations[LOC_LDAM]++; // TNG Stats } else if (z_rel < STOMACH_DAMAGE) { damage = damage * .4; gi.cprintf (targ, PRINT_HIGH, "Stomach damage\n"); if (attacker->client) { attacker->client->resp.hs_streak = 0; gi.cprintf (attacker, PRINT_HIGH, "You hit %s in the stomach\n", targ->client->pers.netname); } damage_type = LOC_SDAM; //TempFile bloody gibbing if (mod == MOD_SNIPER && sv_gib->value) ThrowGib (targ, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); //AQ2:TNG Slicer Last Damage Location attacker->client->resp.last_damaged_part = LOC_SDAM; //AQ2:TNG END if (!teamplay->value || team_round_going || stats_afterround->value) attacker->client->resp.stats_locations[LOC_SDAM]++; // TNG Stats } else //(z_rel < CHEST_DAMAGE) { if (attacker->client) { attacker->client->resp.hs_streak = 0; } if (INV_AMMO(targ, KEV_NUM) && mod != MOD_KNIFE && mod != MOD_KNIFE_THROWN && mod != MOD_SNIPER) { if (attacker->client) { gi.cprintf (attacker, PRINT_HIGH, "%s has a Kevlar Vest - AIM FOR THE HEAD!\n", targ->client->pers.netname); gi.cprintf (targ, PRINT_HIGH, "Kevlar Vest absorbed most of %s's shot\n", attacker->client->pers.netname); /* if (IsFemale(targ)) gi.cprintf(attacker, PRINT_HIGH, "You bruised %s through her Kevlar Vest\n", targ->client->pers.netname); else gi.cprintf(attacker, PRINT_HIGH, "You bruised %s through his Kevlar Vest\n", targ->client->pers.netname); */ } gi.sound (targ, CHAN_ITEM, gi.soundindex ("misc/vest.wav"), 1, ATTN_NORM, 0); damage = (int) (damage / 10); damage_type = LOC_CDAM; bleeding = 0; instant_dam = 1; stopAP = 1; do_sparks = 1; } else if (INV_AMMO(targ, KEV_NUM) && mod == MOD_SNIPER) { if (attacker->client) { gi.cprintf (attacker, PRINT_HIGH, "%s has a Kevlar Vest, too bad you have AP rounds...\n", targ->client->pers.netname); gi.cprintf (targ, PRINT_HIGH, "Kevlar Vest absorbed some of %s's AP sniper round\n", attacker->client->pers.netname); } damage = damage * .325; damage_type = LOC_CDAM; } else { damage = damage * .65; gi.cprintf (targ, PRINT_HIGH, "Chest damage\n"); if (attacker->client) gi.cprintf (attacker, PRINT_HIGH, "You hit %s in the chest\n", targ->client->pers.netname); damage_type = LOC_CDAM; //TempFile bloody gibbing if (mod == MOD_SNIPER && sv_gib->value) ThrowGib (targ, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } //AQ2:TNG Slicer Last Damage Location if (INV_AMMO(targ, KEV_NUM) && mod != MOD_KNIFE && mod != MOD_KNIFE_THROWN) { attacker->client->resp.last_damaged_part = LOC_KVLR_VEST; if (!teamplay->value || team_round_going || stats_afterround->value) attacker->client->resp.stats_locations[LOC_KVLR_VEST]++; // TNG Stats } else { attacker->client->resp.last_damaged_part = LOC_CDAM; if (!teamplay->value || team_round_going || stats_afterround->value) attacker->client->resp.stats_locations[LOC_CDAM]++; // TNG Stats } //AQ2:TNG END } /*else { // no mod to damage gi.cprintf(targ, PRINT_HIGH, "Head damage\n"); if (attacker->client) gi.cprintf(attacker, PRINT_HIGH, "You hit %s in the head\n", targ->client->pers.netname); damage_type = LOC_HDAM; gi.sound(targ, CHAN_VOICE, gi.soundindex("misc/headshot.wav"), 1, ATTN_NORM, 0); } */ } if (team_round_going && attacker->client && targ != attacker && OnSameTeam (targ, attacker)) { Add_TeamWound (attacker, targ, mod); } } } if (damage_type && !instant_dam) // bullets but not vest hits { vec3_t temp; vec3_t temporig; //vec3_t forward; VectorMA (targ->s.origin, 50, dir, temp); //AngleVectors (attacker->client->v_angle, forward, NULL, NULL); VectorScale (dir, 20, temp); VectorAdd (point, temp, temporig); if (mod != MOD_SNIPER) spray_blood (targ, temporig, dir, damage, mod); else spray_sniper_blood (targ, temporig, dir); } if (mod == MOD_FALLING && !(targ->flags & FL_GODMODE) ) { if (!heroes->value || targ->client->resp.team == 2) { // ESJ no limp for heroes if (targ->client && targ->health > 0) { gi.cprintf (targ, PRINT_HIGH, "Leg damage\n"); targ->client->leg_damage = 1; targ->client->leghits++; // bleeding = 1; for testing } } } if (heroes->value && targ->client && targ->client->resp.team == 1) //ESJ take damage or give points, depending { if (attacker->client) //not fall damage or the like { attacker->client->points += damage; while (attacker->client->points >= 100) { attacker->client->resp.score++; attacker->client->points -= 100; } } damage = 0; } // friendly fire avoidance // if enabled you can't hurt teammates (but you can hurt yourself) // knockback still occurs if (targ != attacker && ((deathmatch->value && ((int)dmflags->value & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) { if (OnSameTeam (targ, attacker)) { if ((int)dmflags->value & DF_NO_FRIENDLY_FIRE && (team_round_going || !ff_afterround->value)) damage = 0; else mod |= MOD_FRIENDLY_FIRE; } } meansOfDeath = mod; locOfDeath = damage_type; // location client = targ->client; if (dflags & DAMAGE_BULLET) te_sparks = TE_BULLET_SPARKS; else te_sparks = TE_SPARKS; VectorNormalize (dir); // bonus damage for suprising a monster // if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) // damage *= 2; if (targ->flags & FL_NO_KNOCKBACK) knockback = 0; // figure momentum add if (!(dflags & DAMAGE_NO_KNOCKBACK)) { if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) { vec3_t kvel, flydir; float mass; if (mod != MOD_FALLING) { VectorCopy (dir, flydir); flydir[2] += 0.4f; } if (targ->mass < 50) mass = 50; else mass = targ->mass; if (targ->client && attacker == targ) VectorScale (flydir, 1600.0 * (float) knockback / mass, kvel); // the rocket jump hack... else VectorScale (flydir, 500.0 * (float) knockback / mass, kvel); // FB //if (mod == MOD_KICK ) //{ // kvel[2] = 0; //} VectorAdd (targ->velocity, kvel, targ->velocity); } } take = damage; save = 0; // check for godmode if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION)) { take = 0; save = damage; SpawnDamage (te_sparks, point, normal, save); } // zucc don't need this stuff, but to remove it need to change how damagefeedback works with colors // check for invincibility if ((client && client->invincible_framenum > level.framenum) && !(dflags & DAMAGE_NO_PROTECTION)) { if (targ->pain_debounce_time < level.time) { gi.sound (targ, CHAN_ITEM, gi.soundindex ("items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } take = 0; save = damage; } psave = CheckPowerArmor (targ, point, normal, take, dflags); take -= psave; asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); take -= asave; //treat cheat/powerup savings the same as armor asave += save; // team damage avoidance if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) return; if ((mod == MOD_M3) || (mod == MOD_HC) || (mod == MOD_HELD_GRENADE) || (mod == MOD_HG_SPLASH) || (mod == MOD_G_SPLASH) || (mod == MOD_BREAKINGGLASS)) { //FB 6/3/99 - shotgun damage report stuff int playernum = targ - g_edicts; playernum--; if (playernum >= 0 && playernum <= game.maxclients - 1) *(took_damage + playernum) = 1; //FB 6/3/99 bleeding = 1; instant_dam = 0; } /* if ( (mod == MOD_M3) || (mod == MOD_HC) ) { instant_dam = 1; remain = take % 2; take = (int)(take/2); // balances out difference in how action and axshun handle damage/bleeding } */ if (ctf->value) CTFCheckHurtCarrier (targ, attacker); // do the damage if (take) { // zucc added check for stopAP, if it hit a vest we want sparks if (((targ->svflags & SVF_MONSTER) || (client)) && !do_sparks) SpawnDamage (TE_BLOOD, point, normal, take); else SpawnDamage (te_sparks, point, normal, take); // all things that have at least some instantaneous damage, i.e. bruising/falling if (instant_dam) targ->health = targ->health - take; if (targ->health <= 0) { if (client && attacker->client) { //Added these here also, if this is the last shot and before shots is from //different attacker, msg's would go to wrong client -M if (!OnSameTeam (attacker, targ)) attacker->client->resp.damage_dealt += damage; client->attacker = attacker; client->attacker_mod = mod; client->attacker_loc = damage_type; } if ((targ->svflags & SVF_MONSTER) || (client)) targ->flags |= FL_NO_KNOCKBACK; Killed (targ, inflictor, attacker, take, point); return; } } if (targ->svflags & SVF_MONSTER) { M_ReactToDamage (targ, attacker); if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) { targ->pain (targ, attacker, knockback, take); // nightmare mode monsters don't go into pain frames often if (skill->value == 3) targ->pain_debounce_time = level.time + 5; } } else if (client) { if (!(targ->flags & FL_GODMODE) && (take)) targ->pain (targ, attacker, knockback, take); } else if (take) { if (targ->pain) targ->pain (targ, attacker, knockback, take); } // 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) { client->damage_parmor += psave; client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; //zucc handle adding bleeding here if (damage_type && bleeding) // one of the hit location weapons { /* zucc add in partial bleeding, changed if ( client->bleeding < 4*damage*BLEED_TIME ) { client->bleeding = 4*damage*BLEED_TIME + client->bleeding/2; } else { client->bleeding += damage*BLEED_TIME*2; } */ client->bleeding += damage * BLEED_TIME; VectorSubtract (point, targ->absmax, targ->client->bleedloc_offset); //VectorSubtract(point, targ->s.origin, client->bleedloc_offset); } else if (bleeding) { /* if ( client->bleeding < damage*BLEED_TIME ) { client->bleeding = damage*BLEED_TIME; //client->bleedcount = 0; } */ client->bleeding += damage * BLEED_TIME; VectorSubtract (point, targ->absmax, targ->client->bleedloc_offset); //VectorSubtract(point, targ->s.origin, client->bleedloc_offset); } if (attacker->client) { if (!OnSameTeam (attacker, targ)) attacker->client->resp.damage_dealt += damage; client->attacker = attacker; client->attacker_mod = mod; client->attacker_loc = damage_type; client->push_timeout = 50; //VectorCopy(dir, client->bleeddir ); //VectorCopy(point, client->bleedpoint ); //VectorCopy(normal, client->bleednormal); } VectorCopy (point, client->damage_from); } }
void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) { gclient_t *client; int take; int save; int asave; int psave; int te_sparks; if (!targ->takedamage) return; // friendly fire avoidance // if enabled you can't hurt teammates (but you can hurt yourself) // knockback still occurs if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) { if (OnSameTeam (targ, attacker)) { if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) damage = 0; else mod |= MOD_FRIENDLY_FIRE; } } meansOfDeath = mod; // easy mode takes half damage if (skill->value == 0 && deathmatch->value == 0 && targ->client) { damage *= 0.5; if (!damage) damage = 1; } client = targ->client; if (dflags & DAMAGE_BULLET) te_sparks = TE_BULLET_SPARKS; else te_sparks = TE_SPARKS; VectorNormalize(dir); // bonus damage for suprising a monster if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) damage *= 2; if (targ->flags & FL_NO_KNOCKBACK) knockback = 0; // figure momentum add if (!(dflags & DAMAGE_NO_KNOCKBACK)) { if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) { vec3_t kvel; float mass; if (targ->mass < 50) mass = 50; else mass = targ->mass; if (targ->client && attacker == targ) VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... else VectorScale (dir, 500.0 * (float)knockback / mass, kvel); VectorAdd (targ->velocity, kvel, targ->velocity); } } take = damage; save = 0; // check for godmode if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) { take = 0; save = damage; SpawnDamage (te_sparks, point, normal, save); } // check for invincibility if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) { if (targ->pain_debounce_time < level.time) { gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } take = 0; save = damage; } psave = CheckPowerArmor (targ, point, normal, take, dflags); take -= psave; asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); take -= asave; //treat cheat/powerup savings the same as armor asave += save; // team damage avoidance if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) return; // do the damage if (take) { if ((targ->svflags & SVF_MONSTER) || (client)) SpawnDamage (TE_BLOOD, point, normal, take); else SpawnDamage (te_sparks, point, normal, take); targ->health = targ->health - take; if (targ->health <= 0) { if ((targ->svflags & SVF_MONSTER) || (client)) targ->flags |= FL_NO_KNOCKBACK; Killed (targ, inflictor, attacker, take, point); return; } } if (targ->svflags & SVF_MONSTER) { M_ReactToDamage (targ, attacker); if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) { targ->pain (targ, attacker, knockback, take); // nightmare mode monsters don't go into pain frames often if (skill->value == 3) targ->pain_debounce_time = level.time + 5; } } else if (client) { if (!(targ->flags & FL_GODMODE) && (take)) targ->pain (targ, attacker, knockback, take); } else if (take) { if (targ->pain) targ->pain (targ, attacker, knockback, take); } // 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) { client->damage_parmor += psave; client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; VectorCopy (point, client->damage_from); } }
void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) { gclient_t *client; int take; int save; int asave; int psave; int te_sparks; int sphere_notified; // PGM if (!targ->takedamage) return; sphere_notified = false; // PGM // friendly fire avoidance // if enabled you can't hurt teammates (but you can hurt yourself) // knockback still occurs if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) { if (OnSameTeam (targ, attacker)) { // PMM - nukes kill everyone if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && (mod != MOD_NUKE)) damage = 0; else mod |= MOD_FRIENDLY_FIRE; } } meansOfDeath = mod; //ROGUE // allow the deathmatch game to change values if (deathmatch->value && gamerules && gamerules->value) { if(DMGame.ChangeDamage) damage = DMGame.ChangeDamage(targ, attacker, damage, mod); if(DMGame.ChangeKnockback) knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod); if(!damage) return; } //ROGUE // easy mode takes half damage if (skill->value == 0 && deathmatch->value == 0 && targ->client) { damage *= 0.5; if (!damage) damage = 1; } client = targ->client; // PMM - defender sphere takes half damage if ((client) && (client->owned_sphere) && (client->owned_sphere->spawnflags == 1)) { damage *= 0.5; if (!damage) damage = 1; } if (dflags & DAMAGE_BULLET) te_sparks = TE_BULLET_SPARKS; else te_sparks = TE_SPARKS; VectorNormalize(dir); // bonus damage for suprising a monster if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) damage *= 2; if (targ->flags & FL_NO_KNOCKBACK) knockback = 0; // figure momentum add if (!(dflags & DAMAGE_NO_KNOCKBACK)) { if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) { vec3_t kvel; float mass; if (targ->mass < 50) mass = 50; else mass = targ->mass; if (targ->client && attacker == targ) VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... else VectorScale (dir, 500.0 * (float)knockback / mass, kvel); VectorAdd (targ->velocity, kvel, targ->velocity); } } take = damage; save = 0; // check for godmode if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) { take = 0; save = damage; SpawnDamage (te_sparks, point, normal, save); } // check for invincibility if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) { if (targ->pain_debounce_time < level.time) { gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } take = 0; save = damage; } // ROGUE // check for monster invincibility if (((targ->svflags & SVF_MONSTER) && targ->monsterinfo.invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) { if (targ->pain_debounce_time < level.time) { gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } take = 0; save = damage; } // ROGUE psave = CheckPowerArmor (targ, point, normal, take, dflags); take -= psave; asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); take -= asave; //treat cheat/powerup savings the same as armor asave += save; // team damage avoidance if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) return; // ROGUE - this option will do damage both to the armor and person. originally for DPU rounds if (dflags & DAMAGE_DESTROY_ARMOR) { if(!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) && !(client && client->invincible_framenum > level.framenum)) { take = damage; } } // ROGUE // do the damage if (take) { //PGM need more blood for chainfist. if(targ->flags & FL_MECHANICAL) { SpawnDamage ( TE_ELECTRIC_SPARKS, point, normal, take); } else if ((targ->svflags & SVF_MONSTER) || (client)) { if(mod == MOD_CHAINFIST) SpawnDamage (TE_MOREBLOOD, point, normal, 255); else SpawnDamage (TE_BLOOD, point, normal, take); } else SpawnDamage (te_sparks, point, normal, take); //PGM targ->health = targ->health - take; //PGM - spheres need to know who to shoot at if(client && client->owned_sphere) { sphere_notified = true; if(client->owned_sphere->pain) client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0); } //PGM if (targ->health <= 0) { if ((targ->svflags & SVF_MONSTER) || (client)) targ->flags |= FL_NO_KNOCKBACK; Killed (targ, inflictor, attacker, take, point); return; } } //PGM - spheres need to know who to shoot at if (!sphere_notified) { if(client && client->owned_sphere) { if(client->owned_sphere->pain) client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0); } } //PGM if (targ->svflags & SVF_MONSTER) { M_ReactToDamage (targ, attacker, inflictor); // PMM - fixme - if anyone else but the medic ever uses AI_MEDIC, check for it here instead // of in the medic's pain function if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) { targ->pain (targ, attacker, knockback, take); // nightmare mode monsters don't go into pain frames often if (skill->value == 3) targ->pain_debounce_time = level.time + 5; } } else if (client) { if (!(targ->flags & FL_GODMODE) && (take)) targ->pain (targ, attacker, knockback, take); } else if (take) { if (targ->pain) targ->pain (targ, attacker, knockback, take); } // 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) { client->damage_parmor += psave; client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; VectorCopy (point, client->damage_from); } }
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; 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); } } }
int T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) { qboolean mod_magic = false; gclient_t *client; int take = 0; int save = 0; int asave = 0; int psave = 0; int rsave = 0; int sasave = 0; int te_sparks; if (!targ->takedamage) return 0; // Can't heal monsters if ((damage < 0) && (targ->svflags & SVF_MONSTER)) { return 0; } if (mod & MOD_MAGIC) { //damage dealt by magic mod_magic = true; mod &= ~MOD_MAGIC; } // friendly fire avoidance // if enabled you can't hurt teammates (but you can hurt yourself) // knockback still occurs // team damage avoidance, if not telefrag if (mod != MOD_TELEFRAG) { if (deathmatch->value && attacker->client && targ->client && (targ != attacker) && (((targ->count == attacker->count) && teams->value) || (game.monsterhunt == 10)) && (damage > 0)) { // Two players on same team damage *= teamdamage->value; } if (coop->value && attacker->client && targ->client && (targ != attacker)) { damage *= teamdamage->value; } } meansOfDeath = mod; client = targ->client; if (dflags & DAMAGE_BULLET) te_sparks = TE_BULLET_SPARKS; else te_sparks = TE_SPARKS; VectorNormalize(dir); if (targ->flags & FL_NO_KNOCKBACK) knockback = 0; // Simulate armor pierce and armor breaker on monsters // This has been disabled, since it applies bonus AFTER all other bonuses, resulting in crazy total bonuses /* if ((targ->svflags & SVF_MONSTER) && (attacker->client) && (!mod_magic)) { if (attacker->client->pers.skill[2] > 0) { if (mod == MOD_BLASTER) { iteminfo_t *winfo = getWornItemInfo(attacker, 0); damage *= 1 + 0.75 * (winfo->arg4 + winfo->arg5 * attacker->client->pers.skill[2]); } else { damage *= 1 + 0.02 * attacker->client->pers.skill[2]; } } if (attacker->client->pers.skill[72] > 0) { damage *= 1 + 0.02 * attacker->client->pers.skill[72]; } }*/ if ((targ->svflags & SVF_MONSTER) && (targ->health > 0)) { float manaburn_mult = 0.0f; float manaleech_mult = 0.0f; if ((targ->radius_dmg) && (targ->monsterinfo.ability) && (damage > 0)) { rsave = 0; if ((targ->monsterinfo.ability & GIEX_MABILITY_RES_EXPL) && ((mod == MOD_GRENADE) || (mod == MOD_G_SPLASH) || (mod == MOD_ROCKET) || (mod == MOD_R_SPLASH) || (mod == MOD_HANDGRENADE) || (mod == MOD_HG_SPLASH))) { rsave += 0.33 * damage; } if ((targ->monsterinfo.ability & GIEX_MABILITY_RES_IMPACT) && ((mod == MOD_MACHINEGUN) || (mod == MOD_CHAINGUN) || (mod == MOD_SHOTGUN) || (mod == MOD_SSHOTGUN))) { rsave += 0.33 * damage; } if ((targ->monsterinfo.ability & GIEX_MABILITY_RES_ENERGY) && ((mod == MOD_BLASTER) || (mod == MOD_HYPERBLASTER) || (mod == MOD_LASERMINE) || (mod == MOD_RAILGUN) || (mod == MOD_BFG_LASER) || (mod == MOD_BFG_BLAST) || (mod == MOD_BFG_EFFECT))) { rsave += 0.33 * damage; } if ((targ->monsterinfo.ability & GIEX_MABILITY_RES_BMAGIC) && ((mod == MOD_PLAGUEBOMB) || (mod == MOD_DRAIN) || (mod == MOD_SPORE))) { rsave += 0.33 * damage; } if ((targ->monsterinfo.ability & GIEX_MABILITY_RES_FMAGIC) && ((mod == MOD_INFERNO) || (mod == MOD_FIREBOLT) || (mod == MOD_FIREBALL) || (mod == MOD_CORPSEEXPLOSION))) { rsave += 0.33 * damage; } if ((targ->monsterinfo.ability & GIEX_MABILITY_RES_LMAGIC) && ((mod == MOD_LIGHTNING) || (mod == MOD_SPARK) || (mod == MOD_BOLT) || (mod == MOD_STORM))) { rsave += 0.33 * damage; } if (rsave > 0) { //Anti-resist int arlvl = getAuraLevel(attacker, 84); if (arlvl > 0) { int slot = getAuraSlot(attacker, 84); if (attacker->client->aura_caster[slot]->client->magic > 0) { attacker->client->aura_caster[slot]->client->magic -= 0.1 * rsave; attacker->client->aura_caster[slot]->client->magregentime = level.time + 1.0; rsave *= 1 - (arlvl * 0.02); } } damage -= rsave; } } if (attacker->client) { if (dflags & DAMAGE_10_MANABURN) // 10% manaburn_mult += 0.1; if (dflags & DAMAGE_25_MANABURN) // 25% manaburn_mult += 0.25; if (dflags & DAMAGE_50_MANABURN) // 50% manaburn_mult += 0.5; if (dflags & DAMAGE_100_MANABURN) // 100% manaburn_mult += 1.0; if (dflags & DAMAGE_200_MANABURN) // 200% manaburn_mult += 2.0; if (dflags & DAMAGE_400_MANABURN) // 400% manaburn_mult += 4.0; if (dflags & DAMAGE_50_MANALEECH) // 50% manaleech_mult += 0.5; if (dflags & DAMAGE_100_MANALEECH) // 100% manaleech_mult += 1.0; if (dflags & DAMAGE_200_MANALEECH) // 200% manaleech_mult += 2.0; if ((manaburn_mult > 0.0) && (attacker->client->magic < attacker->client->max_magic * 4)) { attacker->client->magic += (int) ceil(damage * manaburn_mult); if (attacker->client->magic > attacker->client->max_magic * 4) { attacker->client->magic = attacker->client->max_magic * 4; } gi.WriteByte (svc_muzzleflash); gi.WriteShort (targ-g_edicts); gi.WriteByte (MZ_NUKE4); gi.multicast (targ->s.origin, MULTICAST_PVS); } if (manaleech_mult > 0.0) { if (attacker->client->magic < attacker->client->max_magic * 4) { attacker->client->magic += (int) ceil(damage * manaleech_mult); if (attacker->client->magic > attacker->client->max_magic * 4) { attacker->client->magic = attacker->client->max_magic * 4; } } if (manaburn_mult == 0.0) { gi.WriteByte (svc_muzzleflash); gi.WriteShort (targ-g_edicts); gi.WriteByte (MZ_NUKE4); gi.multicast (targ->s.origin, MULTICAST_PVS); } } } } if (client && (damage > 0) && (targ->health > 0)) { int pre_damage = damage; int nosanc_damage = damage; int pre_magic = targ->client->magic; float res_boost = 0.15 * targ->client->pers.skill[45]; //Resistance boost float manaburn_mult = 0.0f; float manaleech_mult = 0.0f; if (targ->client->magic <= 0) res_boost = 0; //Sanctuary aura if ((getAuraLevel(targ, 65) > 0) && (damage > 0)) { int slot = getAuraSlot(targ, 65); if (targ->client->aura_caster[slot]->client->magic > 0) { float bonus = getMagicBonuses(targ->client->aura_caster[slot], 65); float dmg_mult = 1.0; float cost_mult = 0.0; dmg_mult = (0.004 * targ->client->aura_level[slot] * (0.2 + bonus * 0.8)); if (dmg_mult > 1) dmg_mult = 1.0; cost_mult = 0.002 * targ->client->aura_level[slot]; if (targ->client->aura_level[slot] > 40) { cost_mult += 0.002 * (targ->client->aura_level[slot] - 40); } cost_mult *= ((float) (targ->client->aura_caster[slot]->client->max_magic + 800.0)) / 800.0; if (cost_mult * damage > targ->client->aura_caster[slot]->client->magic) { int dmgsave = (int) targ->client->aura_caster[slot]->client->magic / cost_mult; //gi.dprintf("%d %d %f %d %d\n", damage, dmgsave, cost_mult, targ->client->aura_caster[slot]->client->magic, (int) dmgsave * cost_mult); sasave += dmgsave * dmg_mult; damage -= dmgsave * dmg_mult; targ->client->aura_caster[slot]->client->magic -= dmgsave * cost_mult; } else { targ->client->aura_caster[slot]->client->magic -= cost_mult * damage; sasave += dmg_mult * damage; damage *= 1.0 - dmg_mult; } targ->client->aura_caster[slot]->client->magregentime = level.time + 1.0; gi.sound(targ, CHAN_ITEM, gi.soundindex("giex/magarm1.wav"), 0.5, ATTN_NORM, 0); } } rsave = 0; // Global (Damage resist) if (targ->client->pers.skill[46] > 0) { rsave += damage * 0.01 * targ->client->pers.skill[46]; } // Bullet if ((targ->client->pers.skill[36] > 0) && ((mod == MOD_MACHINEGUN) || (mod == MOD_CHAINGUN))) { rsave += damage * 0.015 * targ->client->pers.skill[36] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } // Pellet else if ((targ->client->pers.skill[37] > 0) && ((mod == MOD_SHOTGUN) || (mod == MOD_SSHOTGUN))) { rsave += damage * 0.015 * targ->client->pers.skill[37] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } // Explosion else if ((targ->client->pers.skill[38] > 0) && ((mod == MOD_GRENADE) || (mod == MOD_G_SPLASH) || (mod == MOD_ROCKET) || (mod == MOD_R_SPLASH) || (mod == MOD_HANDGRENADE) || (mod == MOD_HG_SPLASH))) { rsave += damage * 0.015 * targ->client->pers.skill[38] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } // Energy else if ((targ->client->pers.skill[39] > 0) && ((mod == MOD_BLASTER) || (mod == MOD_HYPERBLASTER) || (mod == MOD_LASERMINE))) { rsave += damage * 0.015 * targ->client->pers.skill[39] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } // High energy else if ((targ->client->pers.skill[40] > 0) && ((mod == MOD_RAILGUN) || (mod == MOD_BFG_LASER) || (mod == MOD_BFG_BLAST) || (mod == MOD_BFG_EFFECT))) { rsave += damage * 0.015 * targ->client->pers.skill[40] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } // World and hit damage else if (targ->client->pers.skill[41] > 0) { if ((mod == MOD_WATER) || (mod == MOD_SLIME) || (mod == MOD_FALLING) || (mod == MOD_LAVA) || (mod == MOD_CRUSH)) { rsave += damage * 0.1 * targ->client->pers.skill[41]; } else if (mod == MOD_HIT) { rsave += damage * 0.015 * targ->client->pers.skill[41] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } } // Blood magic else if ((targ->client->pers.skill[42] > 0) && ((mod == MOD_PLAGUEBOMB) || (mod == MOD_DRAIN) || (mod == MOD_SPORE))) { rsave += damage * 0.015 * targ->client->pers.skill[42] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } // Fire magic else if ((targ->client->pers.skill[43] > 0) && ((mod == MOD_INFERNO) || (mod == MOD_FIREBOLT) || (mod == MOD_FIREBALL) || (mod == MOD_CORPSEEXPLOSION))) { rsave += damage * 0.015 * targ->client->pers.skill[43] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } // Lightning magic else if ((targ->client->pers.skill[69] > 0) && ((mod == MOD_LIGHTNING) || (mod == MOD_SPARK) || (mod == MOD_BOLT))) { rsave += damage * 0.015 * targ->client->pers.skill[69] * res_boost; targ->client->magic -= RESBOOT_MAGUSE_MULT * ceil((pre_damage - damage) * res_boost); } if (rsave > 0) { //Anti-resist int arlvl = getAuraLevel(attacker, 84); if (arlvl > 0) { int slot = getAuraSlot(attacker, 84); if (attacker->client->aura_caster[slot]->client->magic > 0) { attacker->client->aura_caster[slot]->client->magic -= 0.1 * rsave; attacker->client->aura_caster[slot]->client->magregentime = level.time + 1.0; rsave *= 1 - (arlvl * 0.02); } } damage -= rsave; nosanc_damage -= rsave; } if (dflags & DAMAGE_10_MANABURN) // 10% manaburn_mult += 0.1; if (dflags & DAMAGE_25_MANABURN) // 25% manaburn_mult += 0.25; if (dflags & DAMAGE_50_MANABURN) // 50% manaburn_mult += 0.5; if (dflags & DAMAGE_100_MANABURN) // 100% manaburn_mult += 1.0; if (dflags & DAMAGE_200_MANABURN) // 200% manaburn_mult += 2.0; if (dflags & DAMAGE_400_MANABURN) // 400% manaburn_mult += 4.0; if (dflags & DAMAGE_50_MANALEECH) // 50% manaleech_mult += 0.5; if (dflags & DAMAGE_100_MANALEECH) // 100% manaleech_mult += 1.0; if (dflags & DAMAGE_200_MANALEECH) // 200% manaleech_mult += 2.0; if ((attacker->svflags & SVF_MONSTER) && (attacker->monsterinfo.ability & GIEX_MABILITY_MANABURN)) { manaburn_mult += 1.0; } if (manaburn_mult > 0.0) { targ->client->magic -= nosanc_damage * manaburn_mult; if (targ->client->magic < 0) targ->client->magic = 0; if ((attacker->client) && (attacker->client->magic < attacker->client->max_magic * 4)) { attacker->client->magic += nosanc_damage * manaburn_mult; if (attacker->client->magic > attacker->client->max_magic * 4) { attacker->client->magic = attacker->client->max_magic * 4; } } gi.WriteByte (svc_muzzleflash); gi.WriteShort (targ-g_edicts); gi.WriteByte (MZ_NUKE4); gi.multicast (targ->s.origin, MULTICAST_PVS); } if (manaleech_mult > 0.0) { if (attacker->client->magic < attacker->client->max_magic * 4) { attacker->client->magic += (int) ceil(nosanc_damage * manaleech_mult); if (attacker->client->magic > attacker->client->max_magic * 4) { attacker->client->magic = attacker->client->max_magic * 4; } } if (manaburn_mult == 0.0) { gi.WriteByte (svc_muzzleflash); gi.WriteShort (targ-g_edicts); gi.WriteByte (MZ_NUKE4); gi.multicast (targ->s.origin, MULTICAST_PVS); } } if ((pre_magic != targ->client->magic) && (targ->client->magregentime < level.time + 1.0)) { targ->client->magregentime = level.time + 1.0; } if (damage < 0) { damage = 0; } } // figure momentum add if (!(dflags & DAMAGE_NO_KNOCKBACK)) { if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) { vec3_t kvel; float mass; if (targ->mass < 50) mass = 50; else mass = targ->mass; if (targ->client && attacker == targ) VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... else VectorScale (dir, 500.0 * (float)knockback / mass, kvel); VectorAdd (targ->velocity, kvel, targ->velocity); } } take = damage; save = 0; // check for godmode if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) && (damage > 0)) { take = 0; save = damage; SpawnDamage (te_sparks, point, normal, save); } // check for invincibility if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION) && (damage > 0)) { if (targ->pain_debounce_time < level.time) { gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } take = 0; save = damage; } if (take > 0) { if ((targ->client) && (targ->client->damage_time < level.time + 0.5 + take * 0.001)) { targ->client->damage_time = level.time + 0.5 + take * 0.001; } psave = CheckPowerArmor (targ, point, normal, take, dflags); take -= psave; asave = CheckArmor (targ, attacker, point, normal, take, te_sparks, dflags, mod, mod_magic); take -= asave; } // do the damage /* if ((attacker->client) && (targ->svflags & SVF_MONSTER)) { if (take <= 0) { gi.dprintf("NO DAMAGE\n", take); _asm int 3; } }*/ if (rsave > 0) { SpawnDamage (TE_SCREEN_SPARKS, point, normal, rsave); } if (sasave > 0) { SpawnDamage (TE_SHIELD_SPARKS, point, normal, sasave); } if ((take > 0) || ( (take < 0) && (targ->health < 2 * targ->max_health)) ) { targ->health -= take; } if ( (targ->health > 0) && ((take != 0) || (take + asave > 0)) ) { addExp(attacker, targ, take + asave); //Also give exp for killing armor } if (take != 0) { if ((targ->svflags & SVF_MONSTER) || (client)) SpawnDamage (TE_BLOOD, point, normal, take); else SpawnDamage (te_sparks, point, normal, take); if ((attacker->client) && (mod != MOD_TELEFRAG)) /* && (targ->health > 0))*/ { if ((damage > 0) && (attacker->client->pers.skill[34] /*|| (attacker->client->pers.skills.classLevel[4] > 0)*/) && ((targ->health + take) > 0)) { int maxhealth = attacker->max_health * 0.75; maxhealth += 13.5 * pow(attacker->client->pers.skills.classLevel[4], 1.05); if (attacker->health < maxhealth) { float amount = 0; float mult = 0; //mult += 0.001 * pow(attacker->client->pers.skills.classLevel[4], 1.1); // Vampire class level bonus //mult *= 0.4 + 0.6 * ((float) attacker->client->pers.skills.classLevel[4] / (float) attacker->radius_dmg); // Penalty if not pure Vampire mult += 0.01 * attacker->client->pers.skill[34]; //From item powerups if (attacker->client->damage_time > level.time) { mult *= 0.25; } amount = take * mult; if ((mod == MOD_TELEFRAG) || (attacker == targ)) amount = 0; attacker->client->pers.add_health += amount; if (attacker->health > maxhealth) attacker->health = maxhealth; } } } if (targ->health <= 0) { if ((targ->svflags & SVF_MONSTER) || (client)) targ->flags |= FL_NO_KNOCKBACK; Killed (targ, inflictor, attacker, take, point); return take; } } if (take > 0) { if (targ->svflags & SVF_MONSTER) { edict_t *oldenemy = targ->enemy; if (targ->radius_dmg && (targ != attacker) && (attacker->takedamage) && (targ->monsterinfo.ability & GIEX_MABILITY_SHARDARMOR) && targ->monsterinfo.shardtime < (level.time - 0.1)) { vec3_t aim, end; float mult = level.time - targ->monsterinfo.shardtime; if (mult > 1.7) mult = 1.7; mult += 0.2; targ->monsterinfo.shardtime = level.time + 0.2; VectorMA(attacker->s.origin, 0.3, attacker->velocity, end); end[2] += attacker->viewheight; VectorSubtract (end, point, aim); if (targ->monsterinfo.ability & GIEX_MABILITY_DAMAGE) // Damage ability shouldn't affect Shard armor, halve mult mult *= 0.5; monster_fire_blaster (targ, point, aim, (int) ceil((16 + 10 * targ->monsterinfo.skill) * mult), 2200, MZ2_SOLDIER_BLASTER_1, EF_BLASTER); gi.sound(targ, CHAN_AUTO, gi.soundindex("giex/magarm2.wav"), 0.8, ATTN_NORM, 0); } M_ReactToDamage (targ, attacker); if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) { targ->pain_debounce_time = level.time + 2 + 0.1 * targ->monsterinfo.skill; if (oldenemy != NULL) targ->pain (targ, attacker, knockback, take); } } else if (client) { if (!(targ->flags & FL_GODMODE) && (take)) targ->pain (targ, attacker, knockback, take); } else if (take) { if (targ->pain) targ->pain (targ, attacker, knockback, take); } } // 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) { client->damage_parmor += psave; client->damage_armor += (asave + save); client->damage_blood += take; client->damage_knockback += knockback; VectorCopy (point, client->damage_from); } return 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; int max; if (!targ->takedamage) { return; } //in entityplus bots cannot harm other bots (unless it's a telefrag) if ( IsBot( targ ) && attacker && IsBot( attacker ) && mod != MOD_TELEFRAG ) return; //if attacker is shooter which was configured not to harm target, do nothing if ( attacker && strstr(attacker->classname, "shooter_" )) { if ( (attacker->spawnflags & 2) && IsBot( targ ) ) //spawnflags 2 == NO_BOTS return; if ( (attacker->spawnflags & 4) && !IsBot( targ ) ) //spawnflag 4 = NO_HUMANS return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } 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 ( strcmp(targ->classname, "func_breakable") && targ->use && (targ->moverState == MOVER_POS1 || targ->moverState == ROTATOR_POS1) ) { targ->use( targ, inflictor, attacker ); } else { // entity is a func_breakable if ( (targ->spawnflags & 1024) && attacker == level.player ) return; if ( (targ->spawnflags & 2048) && IsBot(attacker) ) return; if ( (targ->spawnflags & 4096) && strstr(attacker->classname, "shooter_") ) return; if ( !strcmp(targ->classname, "func_breakable") ) { targ->health -= damage; if ( targ->health <= 0 ) Break_Breakable(targ, attacker); } } return; } // scale back damage from bots or shooters to humans in single player, based on skill level // "I can win" does 0.05 dmg // "Bring it on" does 0.15 dmg // "Hurt me plenty" does 0.25 dmg // "Hardcore" does 0.35 dmg // "Nightmare does 0.45 dmg if ( attacker && ( IsBot(attacker) || !strcmp(attacker->classname, "shooter_bfg") || !strcmp(attacker->classname, "shooter_grenade") || !strcmp(attacker->classname, "shooter_plasma") || !strcmp(attacker->classname, "shooter_rocket") ) ) { float skill = trap_Cvar_VariableValue( "g_spSkill" ); int orgdmg = damage; if ( attacker->parent && attacker->parent->skill ) skill += attacker->parent->skill; if (skill < 1) skill = 1; //relative skill level should not drop below 1 but is allowed to rise above 5 damage *= ( ( 0.1 * skill ) - 0.05 ); //damage is always rounded down. if ( damage < 1 ) damage = 1; //make sure bot does at least -some- damage //G_Printf("skill: %f -- mp: %f -- orgdmg: %i -- dmg: %i\n", skill, ( 0.1 * skill ) - 0.05, orgdmg, damage); } 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 if ( targ != attacker && OnSameTeam (targ, attacker) ) { if ( !g_friendlyFire.integer ) { return; } } // 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 && 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; } } 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) { if ( g_mutators.integer & MT_INSTAGIB && IsBot( targ ) ) targ->health = -999; else 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 T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) { gclient_t *client; int take; int save; int asave; int psave; int te_sparks; if (!targ->takedamage) return; if(mod == MOD_CRUSH) { //bot's state change if((targ->svflags & SVF_MONSTER) && targ->client) { if((targ->client->zc.waitin_obj == inflictor && targ->client->zc.zcstate) || targ->groundentity == inflictor) { // gi.bprintf(PRINT_HIGH,"MOOOOOOOOOOOOOOOOOOOO\n"); targ->client->zc.zcstate |= STS_W_DONT; } } } // friendly fire avoidance // if enabled you can't hurt teammates (but you can hurt yourself) // knockback still occurs if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) { if (OnSameTeam (targ, attacker)) { if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) damage = 0; else mod |= MOD_FRIENDLY_FIRE; } else if(targ->client && !(targ->svflags & SVF_MONSTER)) { if(attacker->client) targ->client->zc.first_target = attacker; } } meansOfDeath = mod; // easy mode takes half damage if (skill->value == 0 && deathmatch->value == 0 && targ->client) { damage *= 0.5; if (!damage) damage = 1; } client = targ->client; if (dflags & DAMAGE_BULLET) te_sparks = TE_BULLET_SPARKS; else te_sparks = TE_SPARKS; VectorNormalize(dir); // bonus damage for suprising a monster if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) damage *= 2; //ZOID //strength tech damage = CTFApplyStrength(attacker, damage); //ZOID if (targ->flags & FL_NO_KNOCKBACK) knockback = 0; // figure momentum add if (!(dflags & DAMAGE_NO_KNOCKBACK)) { if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) { vec3_t kvel; float mass; if (targ->mass < 50) mass = 50; else mass = targ->mass; if (targ->client && attacker == targ) VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... else VectorScale (dir, 500.0 * (float)knockback / mass, kvel); VectorAdd (targ->velocity, kvel, targ->velocity); } } take = damage; save = 0; // check for godmode if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) ) { take = 0; save = damage; SpawnDamage (te_sparks, point, normal, save); } // check for invincibility if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION)) { if (targ->pain_debounce_time < level.time) { gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect3.wav"), 1, ATTN_NORM, 0); // gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } take = 0; save = damage; } //ZOID //team armor protect if (ctf->value && targ->client && attacker->client && targ->client->resp.ctf_team == attacker->client->resp.ctf_team && targ != attacker && ((int)dmflags->value & DF_ARMOR_PROTECT)) { psave = asave = 0; } else { //ZOID psave = CheckPowerArmor (targ, point, normal, take, dflags); take -= psave; asave = CheckArmor (targ, point, normal, take, te_sparks, dflags); take -= asave; } //treat cheat/powerup savings the same as armor asave += save; //ZOID //resistance tech take = CTFApplyResistance(targ, take); //ZOID // team damage avoidance if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker)) return; //ZOID CTFCheckHurtCarrier(targ, attacker); //ZOID // do the damage if (take) { if ((targ->svflags & SVF_MONSTER) || (client)) { SpawnDamage (TE_BLOOD, point, normal, take); if(client && (targ->svflags & SVF_MONSTER) && attacker) { if(client->zc.battlemode & FIRE_CHIKEN) client->zc.battlemode &= ~FIRE_CHIKEN; if(mod == MOD_RAILGUN || mod == MOD_BFG_LASER || mod == MOD_ROCKET || mod == MOD_BLASTER || mod == MOD_RIPPER || mod == MOD_HYPERBLASTER || mod == MOD_PHALANX) { if(attacker->client && (9 * random() < Bot[client->zc.botindex].param[BOP_REACTION]) && !client->zc.first_target) { if(!OnSameTeam (targ, attacker)) client->zc.first_target = attacker; } } } } else SpawnDamage (te_sparks, point, normal, take); targ->health = targ->health - take; if (targ->health <= 0) { if ((targ->svflags & SVF_MONSTER) || (client)) targ->flags |= FL_NO_KNOCKBACK; Killed (targ, inflictor, attacker, take, point); return; } } if (client) { if (!(targ->flags & FL_GODMODE) && (take)) targ->pain (targ, attacker, knockback, take); } else if (take) { if (targ->pain) targ->pain (targ, attacker, knockback, take); } // 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) { client->damage_parmor += psave; client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; VectorCopy (point, client->damage_from); } }
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; } } }