/* ============== alarmbox_updateparts ============== */ void alarmbox_updateparts(gentity_t *ent, qboolean matestoo) { gentity_t *t, *mate; qboolean alarming = (ent->s.frame == 1); // update teammates if (matestoo) { for (mate = ent->teammaster; mate; mate = mate->teamchain) { if (mate == ent) { continue; } if (!(mate->active)) { // don't update dead alarm boxes, they stay dead continue; } if (!(ent->active)) { // destroyed, so just turn teammates off mate->s.frame = 0; } else { mate->s.frame = ent->s.frame; } alarmbox_updateparts(mate, qfalse); } } // update lights if (!ent->target) { return; } t = NULL; while ((t = G_FindByTargetname(t, ent->target)) != NULL) { if (t == ent) { G_DPrintf("WARNING: Entity used itself.\n"); } else { // give the dlight the sound if (!Q_stricmp(t->classname, "dlight")) { t->soundLoop = ent->soundLoop; if (alarming) { if (!(t->r.linked)) { G_UseEntity(t, ent, 0); } } else { if (t->r.linked) { G_UseEntity(t, ent, 0); } } } // alarmbox can tell script_trigger about activation // (but don't trigger if dying, only activation) else if (!Q_stricmp(t->classname, "target_script_trigger") && ent->active) { // not dead G_UseEntity(t, ent, 0); } } } }
/*QUAKED target_relay (1 1 0) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM NOKEY_ONLY TAKE_KEY NO_LOCKED_NOISE This doesn't perform any actions except fire its targets. The activator can be forced to be from a certain team. if RANDOM is checked, only one of the targets will be fired, not all of them "key" specifies an item you can be carrying that affects the operation of this relay this key is currently an int (1-16) which matches the id of a key entity (key_key1 = 1, etc) NOKEY_ONLY means "fire only if I do /not/ have the specified key" TAKE_KEY removes the key from the players inventory "lockednoise" specifies a .wav file to play if the relay is used and the player doesn't have the necessary key. By default this sound is "sound/movers/doors/default_door_locked.wav" NO_LOCKED_NOISE specifies that it will be silent if activated without proper key */ void target_relay_use(gentity_t *self, gentity_t *other, gentity_t *activator) { // Nico, silent GCC (void)other; if ((self->spawnflags & 1) && activator && activator->client && activator->client->sess.sessionTeam != TEAM_AXIS) { return; } if ((self->spawnflags & 2) && activator && activator->client && activator->client->sess.sessionTeam != TEAM_ALLIES) { return; } if (self->spawnflags & 4) { gentity_t *ent; ent = G_PickTarget(self->target); if (ent && ent->use) { G_UseEntity(ent, self, activator); } return; } // activator can be NULL if called from script if (activator && self->key && self->key == -1) { // relay permanently locked if (self->soundPos1) { G_Sound(self, self->soundPos1); //----(SA) added } return; } G_UseTargets(self, activator); }
/** * @brief "activator" should be set to the entity that initiated the firing. * Search for (string)targetname in all entities that * match (string)self.target and call their .use function */ void G_UseTargets(gentity_t *ent, gentity_t *activator) { gentity_t *t; int hash; if (!ent) { return; } if (!ent->target) { return; } t = NULL; hash = BG_StringHashValue(ent->target); while ((t = G_FindByTargetnameFast(t, ent->target, hash)) != NULL) { if (t == ent) { G_Printf(S_COLOR_YELLOW "WARNING G_UseTargets: Entity used itself.\n"); } else { if (t->use) { //G_Printf ("ent->classname %s ent->targetname %s t->targetname %s t->s.number %d\n", ent->classname, ent->targetname, t->targetname, t->s.number); t->flags |= (ent->flags & FL_KICKACTIVATE); // If 'ent' was kicked to activate, pass this along to it's targets. // It may become handy to put a "KICKABLE" flag in ents so that it knows whether to pass this along or not // Right now, the only situation where it would be weird would be an invisible_user that is a 'button' near // a rotating door that it triggers. Kick the switch and the door next to it flies open. t->flags |= (ent->flags & FL_SOFTACTIVATE); // likewise for soft activation if (activator && ((Q_stricmp(t->classname, "func_door") == 0) || (Q_stricmp(t->classname, "func_door_rotating") == 0) ) ) { // check door usage rules before allowing any entity to trigger a door open G_TryDoor(t, ent, activator); // (door,other,activator) } else { G_UseEntity(t, ent, activator); } } } if (!ent->inuse) { G_Printf(S_COLOR_YELLOW "WARNING G_UseTargets: entity was removed while using targets\n"); return; } } }
/* QUAKED target_relay (1 1 0) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM NOKEY_ONLY TAKE_KEY NO_LOCKED_NOISE This doesn't perform any actions except fire its targets. The activator can be forced to be from a certain team. if RANDOM is checked, only one of the targets will be fired, not all of them "key" specifies an item you can be carrying that affects the operation of this relay this key is currently an int (1-16) which matches the id of a key entity (key_key1 = 1, etc) NOKEY_ONLY means "fire only if I do /not/ have the specified key" TAKE_KEY removes the key from the players inventory "lockednoise" specifies a .wav file to play if the relay is used and the player doesn't have the necessary key. By default this sound is "sound/movers/doors/default_door_locked.wav" NO_LOCKED_NOISE specifies that it will be silent if activated without proper key */ void target_relay_use(gentity_t *self, gentity_t *other, gentity_t *activator) { if ((self->spawnflags & 1) && activator && activator->client && activator->client->sess.sessionTeam != TEAM_AXIS) { return; } if ((self->spawnflags & 2) && activator && activator->client && activator->client->sess.sessionTeam != TEAM_ALLIES) { return; } if (self->spawnflags & 4) { gentity_t *ent; ent = G_PickTarget(self->target); if (ent && ent->use) { G_UseEntity(ent, self, activator); } return; } if (activator) // activator can be NULL if called from script { if (self->key) { // removed keys //gitem_t *item; if (self->key == -1) // relay permanently locked { if (self->soundPos1) { G_Sound(self, self->soundPos1); } return; } /* if(self->spawnflags & 16) { // take key activator->client->ps.stats[STAT_KEYS] &= ~(1<<item->giTag); // TODO: "took inventory item" sound }*/ } } G_UseTargets(self, activator); }
void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int knockback; qboolean headShot; qboolean wasAlive; hitRegion_t hr = HR_NUM_HITREGIONS; if (!targ->takedamage) { return; } #ifdef SAVEGAME_SUPPORT if( g_gametype.integer == GT_SINGLE_PLAYER && ( g_reloading.integer || saveGamePending ) ) return; #endif // SAVEGAME_SUPPORT // trap_SendServerCommand( -1, va("print \"%i\n\"\n", targ->health) ); // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued || (g_gamestate.integer != GS_PLAYING && match_warmupDamage.integer == 0)) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // Arnout: invisible entities can't be damaged if( targ->entstate == STATE_INVISIBLE || targ->entstate == STATE_UNDERCONSTRUCTION ) { return; } // xkan, 12/23/2002 - was the bot alive before applying any damage? wasAlive = (targ->health > 0); // Arnout: combatstate if( targ->client && attacker && attacker->client && attacker != targ ) { /*vec_t dist = -1.f; if( targ->client->combatState < COMBATSTATE_HOT ) { vec3_t shotvec; VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, shotvec ); dist = VectorLengthSquared( shotvec ); if( dist < Square(1500.f) && targ->client->combatState == COMBATSTATE_WARM ) targ->client->combatState = COMBATSTATE_HOT; } if( attacker->client->combatState < COMBATSTATE_HOT ) { if( dist < 0.f ) { vec3_t shotvec; VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, shotvec ); dist = VectorLengthSquared( shotvec ); } if( dist > Square(1500.f) ) attacker->client->combatState = COMBATSTATE_WARM; else if( attacker->client->combatState == COMBATSTATE_WARM ) attacker->client->combatState = COMBATSTATE_HOT; }*/ if( g_gamestate.integer == GS_PLAYING ) { if( !OnSameTeam( attacker, targ ) ) { targ->client->combatState |= (1<<COMBATSTATE_DAMAGERECEIVED); attacker->client->combatState |= (1<<COMBATSTATE_DAMAGEDEALT); } } } // JPW NERVE if ((targ->waterlevel >= 3) && (mod == MOD_FLAMETHROWER)) return; // jpw // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER && !(targ->isProp) && !targ->scriptName) { if ( targ->use && targ->moverState == MOVER_POS1 ) { G_UseEntity( targ, inflictor, attacker ); } return; } // TAT 11/22/2002 // In the old code, this check wasn't done for props, so I put that check back in to make props_statue properly work // 4 means destructible if ( targ->s.eType == ET_MOVER && (targ->spawnflags & 4) && !targ->isProp ) { /*switch (mod) { case MOD_GRENADE: case MOD_GRENADE_LAUNCHER: case MOD_ROCKET: case MOD_AIRSTRIKE: case MOD_ARTY: case MOD_GRENADE_PINEAPPLE: case MOD_MAPMORTAR: case MOD_EXPLOSIVE: case MOD_DYNAMITE: case MOD_LANDMINE: case MOD_GPG40: case MOD_M7: case MOD_TELEFRAG: case MOD_PANZERFAUST: case MOD_SATCHEL: break; default: return; // no damage from other weapons }*/ if( !G_WeaponIsExplosive( mod ) ) { return; } // check for team if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } } else if ( targ->s.eType == ET_EXPLOSIVE ) { /*// 32 Explosive // 64 Dynamite only // 256 Airstrike/artillery only // 512 Satchel only if ((targ->spawnflags & 32) || (targ->spawnflags & 64) || (targ->spawnflags & 256) || (targ->spawnflags & 512)) { switch (mod) { case MOD_GRENADE: case MOD_GRENADE_LAUNCHER: case MOD_ROCKET: case MOD_GRENADE_PINEAPPLE: case MOD_MAPMORTAR: case MOD_EXPLOSIVE: case MOD_LANDMINE: case MOD_GPG40: case MOD_M7: if( !(targ->spawnflags & 32) ) return; break; case MOD_SATCHEL: if( !(targ->spawnflags & 512) ) return; break; case MOD_ARTY: case MOD_AIRSTRIKE: if( !(targ->spawnflags & 256) ) return; break; case MOD_DYNAMITE: if( !(targ->spawnflags & 64) ) return; break; default: return; } // check for team if( targ->s.teamNum == inflictor->s.teamNum ) { return; } }*/ if( targ->parent && G_GetWeaponClassForMOD( mod ) == 2 ) { return; } // check for team // if( G_GetWeaponClassForMOD( mod ) != -1 && targ->s.teamNum == inflictor->s.teamNum ) { // return; // } if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } else if ( targ->s.eType == ET_MISSILE && targ->methodOfDeath == MOD_LANDMINE ) { if( targ->s.modelindex2 ) { if( G_WeaponIsExplosive( mod ) ) { mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } if( attacker && attacker->client ) { AddScore( attacker, 1 ); //G_AddExperience( attacker, 1.f ); } G_ExplodeMissile(targ); } } return; } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } client = targ->client; if ( client ) { if ( client->noclip || client->ps.powerups[PW_INVULNERABLE] ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } else if( dflags & DAMAGE_HALF_KNOCKBACK ) { knockback *= 0.5f; } // ydnar: set weapons means less knockback if( client && (client->ps.weapon == WP_MORTAR_SET || client->ps.weapon == WP_MOBILE_MG42_SET) ) knockback *= 0.5; if( targ->client && g_friendlyFire.integer && OnSameTeam(targ, attacker) ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); /*if( mod == MOD_GRENADE || mod == MOD_GRENADE_LAUNCHER || mod == MOD_DYNAMITE || mod == MOD_GPG40 || mod == MOD_M7 || mod == MOD_LANDMINE ) { targ->client->ps.velocity[2] *= 2.f; // gimme air baby! targ->client->ps.groundEntityNum = ENTITYNUM_NONE; // flying high! } else if( mod == MOD_ROCKET ) { targ->client->ps.velocity[2] *= .75f; // but not to the moon please! targ->client->ps.groundEntityNum = ENTITYNUM_NONE; // flying high! }*/ if (targ == attacker && !( mod != MOD_ROCKET && mod != MOD_GRENADE && mod != MOD_GRENADE_LAUNCHER && mod != MOD_DYNAMITE && mod != MOD_GPG40 && mod != MOD_M7 && mod != MOD_LANDMINE )) { targ->client->ps.velocity[2] *= 0.25; } // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team if ( targ != attacker && OnSameTeam (targ, attacker) ) { if ( (g_gamestate.integer != GS_PLAYING && match_warmupDamage.integer == 1)) { return; } else if (!g_friendlyFire.integer) { return; } } } // add to the attacker's hit counter if ( attacker->client && targ != attacker && targ->health > 0 ) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS] -= damage; } else { attacker->client->ps.persistant[PERS_HITS] += damage; } } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // adrenaline junkie! if( targ->client && targ->client->ps.powerups[PW_ADRENALINE] ) { take *= .5f; } // save some from flak jacket if( targ->client && targ->client->sess.skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 4 && targ->client->sess.playerType == PC_ENGINEER ) { if( mod == MOD_GRENADE || mod == MOD_GRENADE_LAUNCHER || mod == MOD_ROCKET || mod == MOD_GRENADE_PINEAPPLE || mod == MOD_MAPMORTAR || mod == MOD_MAPMORTAR_SPLASH || mod == MOD_EXPLOSIVE || mod == MOD_LANDMINE || mod == MOD_GPG40 || mod == MOD_M7 || mod == MOD_SATCHEL || mod == MOD_ARTY || mod == MOD_AIRSTRIKE || mod == MOD_DYNAMITE || mod == MOD_MORTAR || mod == MOD_PANZERFAUST || mod == MOD_MAPMORTAR ) { take -= take * .5f; } } headShot = IsHeadShot(targ, dir, point, mod); if ( headShot ) { if( take * 2 < 50 ) // head shots, all weapons, do minimum 50 points damage take = 50; else take *= 2; // sniper rifles can do full-kill (and knock into limbo) if( dflags & DAMAGE_DISTANCEFALLOFF ) { vec_t dist; vec3_t shotvec; VectorSubtract( point, muzzleTrace, shotvec ); dist = VectorLength( shotvec ); if( dist > 1500.f ) { if( dist > 2500.f ) { take *= 0.2f; } else { float scale = 1.f - 0.2f * (1000.f / (dist - 1000.f)); take *= scale; } } } if( !(targ->client->ps.eFlags & EF_HEADSHOT) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte(dir) ); if( mod != MOD_K43_SCOPE && mod != MOD_GARAND_SCOPE ) { take *= .8f; // helmet gives us some protection } } targ->client->ps.eFlags |= EF_HEADSHOT; // OSP - Record the headshot if(client && attacker && attacker->client #ifndef DEBUG_STATS && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam #endif ) { G_addStatsHeadShot(attacker, mod); } if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Head Shot\n\"\n"); } G_LogRegionHit( attacker, HR_HEAD ); hr = HR_HEAD; } else if ( IsLegShot(targ, dir, point, mod) ) { G_LogRegionHit( attacker, HR_LEGS ); hr = HR_LEGS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Leg Shot\n\"\n"); } } else if ( IsArmShot(targ, attacker, point, mod) ) { G_LogRegionHit( attacker, HR_ARMS ); hr = HR_ARMS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Arm Shot\n\"\n"); } } else if (targ->client && targ->health > 0 && IsHeadShotWeapon( mod ) ) { G_LogRegionHit( attacker, HR_BODY ); hr = HR_BODY; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Body Shot\n\"\n"); } } #ifndef DEBUG_STATS if ( g_debugDamage.integer ) #endif { G_Printf( "client:%i health:%i damage:%i mod:%s\n", targ->s.number, targ->health, take, modNames[mod] ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier // Team_CheckHurtCarrier(targ, attacker); if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if( take ) { targ->health -= take; // Gordon: don't ever gib POWS if( ( targ->health <= 0 ) && ( targ->r.svFlags & SVF_POW ) ) { targ->health = -1; } // Ridah, can't gib with bullet weapons (except VENOM) // Arnout: attacker == inflictor can happen in other cases as well! (movers trying to gib things) //if ( attacker == inflictor && targ->health <= GIB_HEALTH) { if( targ->health <= GIB_HEALTH ) { if( !G_WeaponIsExplosive( mod ) ) { targ->health = GIB_HEALTH + 1; } } // JPW NERVE overcome previous chunk of code for making grenades work again // if ((take > 190)) // 190 is greater than 2x mauser headshot, so headshots don't gib // Arnout: only player entities! messes up ents like func_constructibles and func_explosives otherwise if( ( (targ->s.number < MAX_CLIENTS) && (take > 190) ) && !(targ->r.svFlags & SVF_POW) ) { targ->health = GIB_HEALTH - 1; } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) ) { targ->s.dl_intensity = 255.f * (targ->health / (float)targ->count); // send it to the client } //G_Printf("health at: %d\n", targ->health); if( targ->health <= 0 ) { if( client && !wasAlive ) { targ->flags |= FL_NO_KNOCKBACK; // OSP - special hack to not count attempts for body gibbage if( targ->client->ps.pm_type == PM_DEAD ) { G_addStats(targ, attacker, take, mod); } if( (targ->health < FORCE_LIMBO_HEALTH) && (targ->health > GIB_HEALTH) ) { limbo(targ, qtrue); } // xkan, 1/13/2003 - record the time we died. if (!client->deathTime) client->deathTime = level.time; } else { targ->sound1to2 = hr; targ->sound2to1 = mod; targ->sound2to3 = (dflags & DAMAGE_RADIUS) ? 1 : 0; if( client ) { if( G_GetTeamFromEntity( inflictor ) != G_GetTeamFromEntity( targ ) ) { G_AddKillSkillPoints( attacker, mod, hr, (dflags & DAMAGE_RADIUS) ); } } if( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; targ->deathType = mod; // Ridah, mg42 doesn't have die func (FIXME) if( targ->die ) { // Kill the entity. Note that this funtion can set ->die to another // function pointer, so that next time die is applied to the dead body. targ->die( targ, inflictor, attacker, take, mod ); // OSP - kill stats in player_die function } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) && (targ->spawnflags & 8) ) { return; // reseructable script mover doesn't unlink itself but we don't want a second death script to be called } // if we freed ourselves in death function if (!targ->inuse) return; // RF, entity scripting if (targ->health <= 0) { // might have revived itself in death function if ((targ->s.eType != ET_CONSTRUCTIBLE && targ->s.eType != ET_EXPLOSIVE) || (targ->s.eType == ET_CONSTRUCTIBLE && !targ->desstages)) { // call manually if using desstages G_Script_ScriptEvent( targ, "death", "" ); } } } } else if ( targ->pain ) { if (dir) { // Ridah, had to add this to fix NULL dir crash VectorCopy (dir, targ->rotate); VectorCopy (point, targ->pos3); // this will pass loc of hit } else { VectorClear( targ->rotate ); VectorClear( targ->pos3 ); } targ->pain (targ, attacker, take, point); } else { // OSP - update weapon/dmg stats G_addStats(targ, attacker, take, mod); // OSP } // RF, entity scripting G_Script_ScriptEvent( targ, "pain", va("%d %d", targ->health, targ->health+take) ); // Ridah, this needs to be done last, incase the health is altered in one of the event calls if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } } }
void relay_AIScript_AlertEntity(gentity_t *self) { G_UseEntity(self, NULL, NULL); }
/*QUAKED target_relay (1 1 0) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM NOKEY_ONLY TAKE_KEY NO_LOCKED_NOISE This doesn't perform any actions except fire its targets. The activator can be forced to be from a certain team. if RANDOM is checked, only one of the targets will be fired, not all of them "key" specifies an item you can be carrying that affects the operation of this relay this key is currently an int (1-16) which matches the id of a key entity (key_key1 = 1, etc) NOKEY_ONLY means "fire only if I do /not/ have the specified key" TAKE_KEY removes the key from the players inventory "lockednoise" specifies a .wav file to play if the relay is used and the player doesn't have the necessary key. By default this sound is "sound/movers/doors/default_door_locked.wav" NO_LOCKED_NOISE specifies that it will be silent if activated without proper key */ void target_relay_use(gentity_t *self, gentity_t *other, gentity_t *activator) { if ((self->spawnflags & 1) && activator && activator->client && activator->client->sess.sessionTeam != TEAM_AXIS) { return; } if ((self->spawnflags & 2) && activator && activator->client && activator->client->sess.sessionTeam != TEAM_ALLIES) { return; } if (self->spawnflags & 4) { gentity_t *ent; ent = G_PickTarget(self->target); if (ent && ent->use) { G_UseEntity(ent, self, activator); } return; } if (activator) { // activator can be NULL if called from script if (self->key) { // Gordon: removed keys // gitem_t *item; if (self->key == -1) { // relay permanently locked if (self->soundPos1) { G_Sound(self, self->soundPos1); //----(SA) added } return; } /* item = BG_FindItemForKey(self->key, 0); if(item) { if(activator->client->ps.stats[STAT_KEYS] & (1<<item->giTag)) // user has key { if (self->spawnflags & 8 ) { // relay is NOKEY_ONLY and player has key if (self->soundPos1) G_Sound( self, self->soundPos1); //----(SA) added return; } } else // user does not have key { if (!(self->spawnflags & 8) ) { if (self->soundPos1) G_Sound( self, self->soundPos1); //----(SA) added return; } } }*/ /* if(self->spawnflags & 16) { // (SA) take key activator->client->ps.stats[STAT_KEYS] &= ~(1<<item->giTag); // (SA) TODO: "took inventory item" sound }*/ } } G_UseTargets(self, activator); }
void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const vec3_t in_dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int knockback; qboolean wasAlive; hitRegion_t hr = HR_NUM_HITREGIONS; int limbo_health; limbo_health = FORCE_LIMBO_HEALTH; if (!targ->takedamage) { return; } // the intermission has already been qualified for, so don't // allow any extra scoring // CHRUKER: b024 - Don't do damage if at warmup and warmupdamage is set to 'None' and the target is a client. if ( level.intermissionQueued || (cvars::gameState.ivalue != GS_PLAYING && match_warmupDamage.integer == 0 && targ->client)) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // Arnout: invisible entities can't be damaged if( targ->entstate == STATE_INVISIBLE || targ->entstate == STATE_UNDERCONSTRUCTION ) { return; } // xkan, 12/23/2002 - was the bot alive before applying any damage? wasAlive = (targ->health > 0) ? qtrue : qfalse; // Arnout: combatstate if( targ->client && attacker && attacker->client && attacker != targ ) { if( cvars::gameState.ivalue == GS_PLAYING ) { if( !OnSameTeam( attacker, targ ) ) { targ->client->combatState = (combatstate_t)( targ->client->combatState | (1<<COMBATSTATE_DAMAGERECEIVED) ); attacker->client->combatState = (combatstate_t)( attacker->client->combatState | (1<<COMBATSTATE_DAMAGEDEALT) ); } } } // JPW NERVE if ((targ->waterlevel >= 3) && (mod == MOD_FLAMETHROWER)) return; // jpw // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER && !(targ->isProp) && !targ->scriptName) { if ( targ->use && targ->moverState == MOVER_POS1 ) { G_UseEntity( targ, inflictor, attacker ); } return; } // TAT 11/22/2002 // In the old code, this check wasn't done for props, so I put that check back in to make props_statue properly work // 4 means destructible if ( targ->s.eType == ET_MOVER && (targ->spawnflags & 4) && !targ->isProp ) { if( !G_WeaponIsExplosive( (meansOfDeath_t)mod ) ) { return; } // check for team if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } } else if ( targ->s.eType == ET_EXPLOSIVE ) { if( targ->parent && G_GetWeaponClassForMOD( (meansOfDeath_t)mod ) == 2 ) { return; } if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( (meansOfDeath_t)mod ) < targ->constructibleStats.weaponclass ) { return; } } else if ( targ->s.eType == ET_MISSILE && targ->methodOfDeath == MOD_LANDMINE ) { if( targ->s.modelindex2 ) { if( G_WeaponIsExplosive( (meansOfDeath_t)mod ) ) { mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } if( attacker && attacker->client ) { AddScore( attacker, 1 ); } G_ExplodeMissile(targ); } } return; } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( (meansOfDeath_t)mod ) < targ->constructibleStats.weaponclass ) { return; } //bani - fix #238 if ( mod == MOD_DYNAMITE ) { if( !( inflictor->etpro_misc_1 & 1 ) ) return; } } client = targ->client; if ( client ) { if ( client->noclip || ( client->ps.powerups[PW_INVULNERABLE] && !( dflags & DAMAGE_JAY_NO_PROTECTION ))) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } // ugly-ass code but we do this to make in_dir read-only vec3_t dir; if ( !in_dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorCopy( in_dir, dir ); VectorNormalize( dir ); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } else if( dflags & DAMAGE_HALF_KNOCKBACK ) { knockback = int( knockback * 0.5f ); } // ydnar: set weapons means less knockback if( client && (client->ps.weapon == WP_MORTAR_SET || client->ps.weapon == WP_MOBILE_MG42_SET) ) knockback = int( knockback * 0.5f ); if( targ->client && g_friendlyFire.integer && OnSameTeam(targ, attacker) ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); // From NoQuarter, I'm not sure I need this if ( attacker && attacker->client && ( targ->client->ps.groundEntityNum != ENTITYNUM_NONE || G_WeaponIsExplosive((meansOfDeath_t)mod) )){ targ->client->pmext.wasShoved = qtrue; targ->client->pmext.shover = attacker - g_entities; } if (targ == attacker && !( mod != MOD_ROCKET && mod != MOD_GRENADE && mod != MOD_GRENADE_LAUNCHER && mod != MOD_DYNAMITE && mod != MOD_GPG40 && mod != MOD_M7 && mod != MOD_LANDMINE )) { targ->client->ps.velocity[2] *= 0.25; } // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // skip damage if friendly fire is disabled if (!(dflags & DAMAGE_NO_PROTECTION) && targ != attacker && OnSameTeam( targ, attacker ) && !g_friendlyFire.integer) { return; } if (damage < 1) damage = 1; take = damage; save = 0; if ( attacker->client && targ->client && targ != attacker && targ->health > 0 ) { // Jaybird - Hitsounds // vsay "hold your fire" on the first hit of a teammate // only applies if the player has been hurt before // and the match is not in warmup. if( OnSameTeam( targ, attacker )) { if(( !client->lasthurt_mod || client->lasthurt_client != attacker->s.number ) && cvars::gameState.ivalue == GS_PLAYING && ( targ->health - take ) > limbo_health ) { if( client->sess.sessionTeam == TEAM_AXIS ) G_ClientSound( attacker, "sound/chat/axis/26a.wav" ); else G_ClientSound( attacker, "sound/chat/allies/26a.wav" ); } if (mod != MOD_GOOMBA && mod != MOD_POISON_SYRINGE) { g_clientObjects[attacker->s.number].recordHit( AbstractHitVolume::ZONE_BODY, true ); } } else { if (mod != MOD_GOOMBA && mod != MOD_POISON_SYRINGE) { g_clientObjects[attacker->s.number].recordHit( AbstractHitVolume::ZONE_BODY, false ); } } } // adrenaline junkie! if( targ->client && targ->client->ps.powerups[PW_ADRENALINE] ) { take = int( take * 0.5f ); } // save some from flak jacket // Jaybird - engineer class carryover if( targ->client && targ->client->sess.skill[SK_EXPLOSIVES_AND_CONSTRUCTION] >= 4 && ( targ->client->sess.playerType == PC_ENGINEER || ( cvars::bg_skills.ivalue & SBS_ENGI ))) { if( mod == MOD_GRENADE || mod == MOD_GRENADE_LAUNCHER || mod == MOD_ROCKET || mod == MOD_GRENADE_PINEAPPLE || mod == MOD_MAPMORTAR || mod == MOD_MAPMORTAR_SPLASH || mod == MOD_EXPLOSIVE || mod == MOD_LANDMINE || mod == MOD_GPG40 || mod == MOD_M7 || mod == MOD_SATCHEL || mod == MOD_ARTY || mod == MOD_AIRSTRIKE || mod == MOD_DYNAMITE || mod == MOD_MORTAR || mod == MOD_PANZERFAUST || mod == MOD_MAPMORTAR ) { take -= int( take * 0.5f ); } } #ifndef DEBUG_STATS if ( g_debugDamage.integer ) #endif { G_Printf( "client:%i health:%i damage:%i mod:%s\n", targ->s.number, targ->health, take, modNames[mod] ); } if( targ && targ->client && attacker && attacker->client && targ != attacker && targ->health > 0 && OnSameTeam( targ, attacker ) && g_friendlyFire.integer == 2 && IsReflectable( mod )) { int ffDamage; // Percentage based reflect ffDamage = int( take * g_reflectFriendlyFire.value / 100.f ); if( ffDamage <= 0 ) { ffDamage = 0; } attacker->health -= ffDamage; // Give them pain! attacker->client->damage_blood += take; attacker->client->damage_knockback += knockback; // Set the lasthurt stuff so hitsounds do not replay targ->client->lasthurt_mod = mod; targ->client->lasthurt_client = attacker - g_entities; // Kill the player if necessary if( attacker->health <= 0 ) { attacker->deathType = MOD_REFLECTED_FF; attacker->enemy = attacker; if( attacker->die ) { attacker->die( attacker, attacker, attacker, ffDamage, MOD_REFLECTED_FF ); } } } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier // Team_CheckHurtCarrier(targ, attacker); if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; targ->client->lasthurt_time = level.time; } // do the damage if( take ) { targ->health -= take; // Gordon: don't ever gib POWS if( ( targ->health <= 0 ) && ( targ->r.svFlags & SVF_POW ) ) { targ->health = -1; } // Ridah, can't gib with bullet weapons (except VENOM) // Arnout: attacker == inflictor can happen in other cases as well! (movers trying to gib things) //if ( attacker == inflictor && targ->health <= GIB_HEALTH) { if( targ->health <= GIB_HEALTH ) { if( !G_WeaponIsExplosive( (meansOfDeath_t)mod ) ) { targ->health = GIB_HEALTH + 1; } } if( g_damagexp.integer && client && G_GetTeamFromEntity( inflictor ) != G_GetTeamFromEntity( targ )) { // Jaybird - give them some per hit // They get 1 XP per 50 damage, so multiple .02 * take int skill = G_SkillForMOD( mod ); if( skill >= 0 ) G_AddSkillPoints( attacker, (skillType_t)skill, take * .02 ); } // JPW NERVE overcome previous chunk of code for making grenades work again // if ((take > 190)) // 190 is greater than 2x mauser headshot, so headshots don't gib // Arnout: only player entities! messes up ents like func_constructibles and func_explosives otherwise if( ( (targ->s.number < MAX_CLIENTS) && (take > 190) ) && !(targ->r.svFlags & SVF_POW) ) { targ->health = GIB_HEALTH - 1; } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) ) { targ->s.dl_intensity = int( 255.f * (targ->health / (float)targ->count) ); // send it to the client } //G_Printf("health at: %d\n", targ->health); if( targ->health <= 0 ) { if( client && !wasAlive ) { targ->flags |= FL_NO_KNOCKBACK; // OSP - special hack to not count attempts for body gibbage if( targ->client->ps.pm_type == PM_DEAD ) { G_addStats(targ, attacker, take, mod); } if( (targ->health < FORCE_LIMBO_HEALTH) && (targ->health > GIB_HEALTH) ) { limbo(targ, qtrue); } // xkan, 1/13/2003 - record the time we died. if (!client->deathTime) client->deathTime = level.time; //bani - #389 if( targ->health <= GIB_HEALTH ) { GibEntity( targ, 0 ); } } else { targ->sound1to2 = hr; targ->sound2to1 = mod; targ->sound2to3 = (dflags & DAMAGE_RADIUS) ? 1 : 0; if( client ) { if( G_GetTeamFromEntity( inflictor ) != G_GetTeamFromEntity( targ ) ) { G_AddKillSkillPoints( attacker, (meansOfDeath_t)mod, hr, (qboolean)(dflags & DAMAGE_RADIUS) ); } } if( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; targ->deathType = (meansOfDeath_t)mod; // Ridah, mg42 doesn't have die func (FIXME) if( targ->die ) { // Kill the entity. Note that this funtion can set ->die to another // function pointer, so that next time die is applied to the dead body. targ->die( targ, inflictor, attacker, take, mod ); // OSP - kill stats in player_die function } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) && (targ->spawnflags & 8) ) { return; // reseructable script mover doesn't unlink itself but we don't want a second death script to be called } // if we freed ourselves in death function if (!targ->inuse) return; // RF, entity scripting if ( targ->health <= 0) { // might have revived itself in death function if( targ->r.svFlags & SVF_BOT ) { // Removed } else if( ( targ->s.eType != ET_CONSTRUCTIBLE && targ->s.eType != ET_EXPLOSIVE ) || ( targ->s.eType == ET_CONSTRUCTIBLE && !targ->desstages ) ) { // call manually if using desstages G_Script_ScriptEvent( targ, "death", "" ); } } } } else if ( targ->pain ) { if (dir) { // Ridah, had to add this to fix NULL dir crash VectorCopy (dir, targ->rotate); VectorCopy (point, targ->pos3); // this will pass loc of hit } else { VectorClear( targ->rotate ); VectorClear( targ->pos3 ); } targ->pain (targ, attacker, take, point); } else { // OSP - update weapon/dmg stats G_addStats(targ, attacker, take, mod); // OSP } // RF, entity scripting G_Script_ScriptEvent( targ, "pain", va("%d %d", targ->health, targ->health+take) ); // RF, record bot pain if (targ->s.number < level.maxclients) { // notify omni-bot framework Bot_Event_TakeDamage(targ-g_entities, attacker); } // Ridah, this needs to be done last, incase the health is altered in one of the event calls // Jaybird - playdead check if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } // Cheap way to ID inflictor entity as poison smoke. if (inflictor->poisonGasAlarm && mod == MOD_POISON_GAS && targ->health >= 0) G_AddEvent(targ, EV_COUGH, 0); } }
// Dini, Note, this is where damage etc is dealt out! void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int knockback; qboolean headShot; qboolean wasAlive; hitRegion_t hr = HR_NUM_HITREGIONS; if (!targ->takedamage) { return; } // the intermission has already been qualified for, so don't // allow any extra scoring // CHRUKER: b024 - Don't do damage if at warmup and warmupdamage is set to 'None' and the target is a client. if ( level.intermissionQueued || (g_gamestate.integer != GS_PLAYING && targ->client)) { return; } if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // Arnout: invisible entities can't be damaged if( targ->entstate == STATE_INVISIBLE || targ->entstate == STATE_UNDERCONSTRUCTION ) { return; } // xkan, 12/23/2002 - was the bot alive before applying any damage? wasAlive = (targ->health > 0); // Arnout: combatstate if( targ->client && attacker && attacker->client && attacker != targ ) { if( g_gamestate.integer == GS_PLAYING ) { if( !OnSameTeam( attacker, targ ) ) { targ->client->combatState |= (1<<COMBATSTATE_DAMAGERECEIVED); attacker->client->combatState |= (1<<COMBATSTATE_DAMAGEDEALT); } } } // JPW NERVE if ((targ->waterlevel >= 3) && (mod == MOD_FLAMETHROWER)) return; // jpw // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER && !(targ->isProp) && !targ->scriptName) { if ( targ->use && targ->moverState == MOVER_POS1 ) { G_UseEntity( targ, inflictor, attacker ); } return; } // 4 means destructible if ( targ->s.eType == ET_MOVER && (targ->spawnflags & 4) && !targ->isProp ) { if( !G_WeaponIsExplosive( mod ) ) { return; } // check for team if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } } else if ( targ->s.eType == ET_EXPLOSIVE ) { if( targ->parent && G_GetWeaponClassForMOD( mod ) == 2 ) { return; } if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } } else if ( targ->s.eType == ET_MISSILE && targ->methodOfDeath == MOD_LANDMINE ) { if( targ->s.modelindex2 ) { if( G_WeaponIsExplosive( mod ) ) { mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], targ-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } if( attacker && attacker->client ) { AddScore( attacker, 1 ); } G_ExplodeMissile(targ); } } return; } else if ( targ->s.eType == ET_CONSTRUCTIBLE ) { if( G_GetTeamFromEntity( inflictor ) == G_GetTeamFromEntity( targ ) ) { return; } if( G_GetWeaponClassForMOD( mod ) < targ->constructibleStats.weaponclass ) { return; } //bani - fix #238 if ( mod == MOD_DYNAMITE ) { if( !( inflictor->etpro_misc_1 & 1 ) ) return; } } client = targ->client; // Dini, Note, Noclip.. if ( client ) { if ( client->noclip || client->ps.powerups[PW_INVULNERABLE] ) { return; } } // check for godmode if ( targ->flags & FL_GODMODE ) { return; } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } // Only do knockback to yourself if (attacker->client == targ->client) knockback = 1; else knockback = 0; // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; int knock; if (mod == MOD_PANZERFAUST) knock = 500; else knock = 1000; // Dini, Note, Fix this sometime.. VectorScale (dir, knock, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); if (targ == attacker && !( mod != MOD_ROCKET && mod != MOD_GRENADE && mod != MOD_GRENADE_LAUNCHER && mod != MOD_DYNAMITE && mod != MOD_GPG40 && mod != MOD_M7 && mod != MOD_LANDMINE )) { targ->client->ps.velocity[2] *= 0.25; } // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // Dini, disable damage taking for players, aka if they're on a team if (OnAnyTeam (targ, attacker) && !tjg_damage.integer) return; // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team if ( targ != attacker && OnSameTeam (targ, attacker) ) { if ( (g_gamestate.integer != GS_PLAYING)) { return; } else if (!g_friendlyFire.integer) { return; } } } // add to the attacker's hit counter if ( attacker->client && targ != attacker && targ->health > 0 ) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS] -= damage; } else { attacker->client->ps.persistant[PERS_HITS] += damage; } } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; headShot = IsHeadShot(targ, dir, point, mod); if ( headShot ) { if( take * 2 < 50 ) // head shots, all weapons, do minimum 50 points damage take = 50; else take *= 2; // sniper rifles can do full-kill (and knock into limbo) if( dflags & DAMAGE_DISTANCEFALLOFF ) { vec_t dist; vec3_t shotvec; float scale; VectorSubtract( point, muzzleTrace, shotvec ); dist = VectorLength( shotvec ); // zinx - start at 100% at 1500 units (and before), // and go to 20% at 2500 units (and after) // 1500 to 2500 -> 0.0 to 1.0 scale = (dist - 1500.f) / (2500.f - 1500.f); // 0.0 to 1.0 -> 0.0 to 0.8 scale *= 0.8f; // 0.0 to 0.8 -> 1.0 to 0.2 scale = 1.0f - scale; // And, finally, cap it. if (scale > 1.0f) scale = 1.0f; else if (scale < 0.2f) scale = 0.2f; take *= scale; } if( !(targ->client->ps.eFlags & EF_HEADSHOT) ) { // only toss hat on first headshot G_AddEvent( targ, EV_LOSE_HAT, DirToByte(dir) ); if( mod != MOD_K43_SCOPE && mod != MOD_GARAND_SCOPE ) { take *= .8f; // helmet gives us some protection } } targ->client->ps.eFlags |= EF_HEADSHOT; // OSP - Record the headshot if(client && attacker && attacker->client #ifndef DEBUG_STATS && attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam #endif ) { G_addStatsHeadShot(attacker, mod); } if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Head Shot\n\"\n"); } G_LogRegionHit( attacker, HR_HEAD ); hr = HR_HEAD; } else if ( IsLegShot(targ, dir, point, mod) ) { G_LogRegionHit( attacker, HR_LEGS ); hr = HR_LEGS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Leg Shot\n\"\n"); } } else if ( IsArmShot(targ, attacker, point, mod) ) { G_LogRegionHit( attacker, HR_ARMS ); hr = HR_ARMS; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Arm Shot\n\"\n"); } } else if (targ->client && targ->health > 0 && IsHeadShotWeapon( mod ) ) { G_LogRegionHit( attacker, HR_BODY ); hr = HR_BODY; if( g_debugBullets.integer ) { trap_SendServerCommand( attacker-g_entities, "print \"Body Shot\n\"\n"); } } #ifndef DEBUG_STATS if ( g_debugDamage.integer ) #endif { G_Printf( "client:%i health:%i damage:%i mod:%s\n", targ->s.number, targ->health, take, modNames[mod] ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_blood += take; // Dini, removed for now. //client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier // Team_CheckHurtCarrier(targ, attacker); if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if( take ) { targ->health -= take; // Gordon: don't ever gib POWS if( ( targ->health <= 0 ) && ( targ->r.svFlags & SVF_POW ) ) { targ->health = -1; } // Ridah, can't gib with bullet weapons (except VENOM) // Arnout: attacker == inflictor can happen in other cases as well! (movers trying to gib things) //if ( attacker == inflictor && targ->health <= GIB_HEALTH) { /*if( targ->health <= GIB_HEALTH ) { if( !G_WeaponIsExplosive( mod ) ) { targ->health = GIB_HEALTH + 1; } }*/ // JPW NERVE overcome previous chunk of code for making grenades work again // if ((take > 190)) // 190 is greater than 2x mauser headshot, so headshots don't gib // Arnout: only player entities! messes up ents like func_constructibles and func_explosives otherwise if( ( (targ->s.number < MAX_CLIENTS) && (take > 190) ) && !(targ->r.svFlags & SVF_POW) ) { targ->health = GIB_HEALTH - 1; } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) ) { targ->s.dl_intensity = 255.f * (targ->health / (float)targ->count); // send it to the client } //G_Printf("health at: %d\n", targ->health); if( targ->health <= 0 ) { if( client && !wasAlive ) { targ->flags |= FL_NO_KNOCKBACK; // OSP - special hack to not count attempts for body gibbage if( targ->client->ps.pm_type == PM_DEAD ) { G_addStats(targ, attacker, take, mod); } /*if( (targ->health < FORCE_LIMBO_HEALTH) && (targ->health > GIB_HEALTH) ) { limbo(targ, qtrue); }*/ if (targ->health <= 0 && targ->s.number < MAX_CLIENTS && !(targ->r.svFlags & SVF_POW)) { targ->health = GIB_HEALTH - 1; } // xkan, 1/13/2003 - record the time we died. if (!client->deathTime) client->deathTime = level.time; } else { targ->sound1to2 = hr; targ->sound2to1 = mod; targ->sound2to3 = (dflags & DAMAGE_RADIUS) ? 1 : 0; if( client ) { if( G_GetTeamFromEntity( inflictor ) != G_GetTeamFromEntity( targ ) ) { G_AddKillSkillPoints( attacker, mod, hr, (dflags & DAMAGE_RADIUS) ); } } if( targ->health < -999 ) { targ->health = -999; } targ->enemy = attacker; targ->deathType = mod; // Ridah, mg42 doesn't have die func (FIXME) if( targ->die ) { // Kill the entity. Note that this funtion can set ->die to another // function pointer, so that next time die is applied to the dead body. targ->die( targ, inflictor, attacker, take, mod ); // OSP - kill stats in player_die function } if( targ->s.eType == ET_MOVER && !Q_stricmp( targ->classname, "script_mover" ) && (targ->spawnflags & 8) ) { return; // reseructable script mover doesn't unlink itself but we don't want a second death script to be called } // if we freed ourselves in death function if (!targ->inuse) return; // RF, entity scripting if ( targ->health <= 0) { // might have revived itself in death function if( ( targ->s.eType != ET_CONSTRUCTIBLE && targ->s.eType != ET_EXPLOSIVE ) || ( targ->s.eType == ET_CONSTRUCTIBLE && !targ->desstages ) ) { // call manually if using desstages G_Script_ScriptEvent( targ, "death", "" ); } } } } else if ( targ->pain ) { if (dir) { // Ridah, had to add this to fix NULL dir crash VectorCopy (dir, targ->rotate); VectorCopy (point, targ->pos3); // this will pass loc of hit } else { VectorClear( targ->rotate ); VectorClear( targ->pos3 ); } targ->pain (targ, attacker, take, point); } else { // OSP - update weapon/dmg stats G_addStats(targ, attacker, take, mod); // OSP } // RF, entity scripting G_Script_ScriptEvent( targ, "pain", va("%d %d", targ->health, targ->health+take) ); // Ridah, this needs to be done last, incase the health is altered in one of the event calls if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } } }