void bum_sit_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject) { // self->s.modelindex2 = 0; self->takedamage = DAMAGE_YES; if (DeathByGib(self, inflictor, attacker, damage)) { // gib self->deadflag = DEAD_DEAD; GibEntity( self, inflictor, damage ); return; } if (self->deadflag == DEAD_DEAD) return; self->deadflag = DEAD_DEAD; gi.sound ( self, CHAN_VOICE, gi.soundindex ("actors/bum/death.wav"), 1, 3, 0); if (rand()%100 > 50) self->cast_info.currentmove = &bum_sit_move_death_r; else self->cast_info.currentmove = &bum_sit_move_death_l; }
/* ================== body_die ================== */ void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { if ( self->health > GIB_HEALTH ) { return; } GibEntity( self, 0 ); }
/* ================== body_die ================== */ void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { if ( self->health > GIB_HEALTH ) { return; } GibEntity( self, qfalse ); // add corpse gibbed event G_AddEvent( self, EV_DEATH1, 2 ); }
/* ======================================================================================================================================= BodyDie ======================================================================================================================================= */ void BodyDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath) { if (self->health > GIB_HEALTH) { return; } if (!g_blood.integer) { self->health = GIB_HEALTH + 1; return; } GibEntity(self, 0); }
/* ================== body_die ================== */ void body_die(gentity_t * self, gentity_t * inflictor, gentity_t * attacker, int damage, int meansOfDeath) { if(self->health > GIB_HEALTH) { return; } /* // if(!g_blood.integer) // { self->health = GIB_HEALTH + 1; return; // } */ GibEntity(self, 0); }
/* ================== body_die ================== */ void body_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { if ( self->health > GIB_HEALTH ) { return; } if ( !g_blood.integer ) { self->health = GIB_HEALTH + 1; return; } if ( self->aiCharacter == AICHAR_HEINRICH || self->aiCharacter == AICHAR_HELGA || self->aiCharacter == AICHAR_SUPERSOLDIER || self->aiCharacter == AICHAR_PROTOSOLDIER ) { if ( self->health <= GIB_HEALTH ) { self->health = -1; return; } } GibEntity( self, 0 ); }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i; char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } // check for an almost capture CheckAlmostCapture( self, attacker ); // check for a player that almost brought in cubes CheckAlmostScored( self, attacker ); if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } #ifdef MISSIONPACK if ((self->client->ps.eFlags & EF_TICKING) && self->activator) { self->client->ps.eFlags &= ~EF_TICKING; self->activator->think = G_FreeEntity; self->activator->nextthink = level.time; } #endif self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) ) { obit = "<bad obituary>"; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; if (attacker && attacker->client) { attacker->client->lastkilled_client = self->s.number; if ( attacker == self || OnSameTeam (self, attacker ) ) { AddScore( attacker, self->r.currentOrigin, -1 ); } else { AddScore( attacker, self->r.currentOrigin, 1 ); if( meansOfDeath == MOD_GAUNTLET ) { // play humiliation on player attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; // also play humiliation on target self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD; } // check for two kills in a short amount of time // if this is close enough to the last kill, give a reward sound if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) { // play excellent on player attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } attacker->client->lastKillTime = level.time; } } else { AddScore( self, self->r.currentOrigin, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // if I committed suicide, the flag does not fall, it returns. if (meansOfDeath == MOD_SUICIDE) { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); self->client->ps.powerups[PW_NEUTRALFLAG] = 0; } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); self->client->ps.powerups[PW_REDFLAG] = 0; } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); self->client->ps.powerups[PW_BLUEFLAG] = 0; } } // if client is in a nodrop area, don't drop anything (but return CTF flags!) contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP )) { TossClientItems( self ); } else { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); } } #ifdef MISSIONPACK TossClientPersistantPowerups( self ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( self ); } #endif Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller (self, inflictor, attacker); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700; // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; #ifdef MISSIONPACK if (self->s.eFlags & EF_KAMIKAZE) { Kamikaze_DeathTimer( self ); } #endif } trap_LinkEntity (self); }
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { int contents = 0, i, killer = ENTITYNUM_WORLD; char *killerName = "<world>"; qboolean nogib = qtrue; gitem_t *item = NULL; gentity_t *ent; qboolean killedintank = qfalse; //float timeLived; weapon_t weap = BG_WeaponForMOD( meansOfDeath ); // G_Printf( "player_die\n" ); if(attacker == self) { if(self->client) { self->client->pers.playerStats.suicides++; trap_PbStat ( self - g_entities , "suicide" , va ( "%d %d %d" , self->client->sess.sessionTeam , self->client->sess.playerType , weap ) ) ; } } else if(OnSameTeam( self, attacker )) { G_LogTeamKill( attacker, weap ); } else { G_LogDeath( self, weap ); G_LogKill( attacker, weap ); if( g_gamestate.integer == GS_PLAYING ) { if( attacker->client ) { attacker->client->combatState |= (1<<COMBATSTATE_KILLEDPLAYER); } } } // RF, record this death in AAS system so that bots avoid areas which have high death rates if( !OnSameTeam( self, attacker ) ) { // LC - not needed // BotRecordTeamDeath( self->s.number ); self->isProp = qfalse; // were we teamkilled or not? } else { self->isProp = qtrue; } // if we got killed by a landmine, update our map if( self->client && meansOfDeath == MOD_LANDMINE ) { // if it's an enemy mine, update both teamlists /*int teamNum; mapEntityData_t *mEnt; mapEntityData_Team_t *teamList; teamNum = inflictor->s.teamNum % 4; teamList = self->client->sess.sessionTeam == TEAM_AXIS ? &mapEntityData[0] : &mapEntityData[1]; if((mEnt = G_FindMapEntityData(teamList, inflictor-g_entities)) != NULL) { G_FreeMapEntityData( teamList, mEnt ); } if( teamNum != self->client->sess.sessionTeam ) { teamList = self->client->sess.sessionTeam == TEAM_AXIS ? &mapEntityData[1] : &mapEntityData[0]; if((mEnt = G_FindMapEntityData(teamList, inflictor-g_entities)) != NULL) { G_FreeMapEntityData( teamList, mEnt ); } }*/ mapEntityData_t *mEnt; if((mEnt = G_FindMapEntityData(&mapEntityData[0], inflictor-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[0], mEnt ); } if((mEnt = G_FindMapEntityData(&mapEntityData[1], inflictor-g_entities)) != NULL) { G_FreeMapEntityData( &mapEntityData[1], mEnt ); } } { mapEntityData_t *mEnt; mapEntityData_Team_t *teamList = self->client->sess.sessionTeam == TEAM_AXIS ? &mapEntityData[1] : &mapEntityData[0]; // swapped, cause enemy team mEnt = G_FindMapEntityDataSingleClient( teamList, NULL, self->s.number, -1 ); while( mEnt ) { if( mEnt->type == ME_PLAYER_DISGUISED ) { mapEntityData_t* mEntFree = mEnt; mEnt = G_FindMapEntityDataSingleClient( teamList, mEnt, self->s.number, -1 ); G_FreeMapEntityData( teamList, mEntFree ); } else { mEnt = G_FindMapEntityDataSingleClient( teamList, mEnt, self->s.number, -1 ); } } } if( self->tankLink ) { G_LeaveTank( self, qfalse ); killedintank = qtrue; } if( self->client->ps.pm_type == PM_DEAD || g_gamestate.integer == GS_INTERMISSION ) { return; } // OSP - death stats handled out-of-band of G_Damage for external calls G_addStats(self, attacker, damage, meansOfDeath); // OSP self->client->ps.pm_type = PM_DEAD; G_AddEvent( self, EV_STOPSTREAMINGSOUND, 0); if(attacker) { killer = attacker->s.number; killerName = (attacker->client) ? attacker->client->pers.netname : "<non-client>"; } if(attacker == 0 || killer < 0 || killer >= MAX_CLIENTS) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if(g_gamestate.integer == GS_PLAYING) { char *obit; if(meansOfDeath < 0 || meansOfDeath >= sizeof(modNames) / sizeof(modNames[0])) { obit = "<bad obituary>"; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); } // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; // JPW NERVE -- if player is holding ticking grenade, drop it if ((self->client->ps.grenadeTimeLeft) && (self->s.weapon != WP_DYNAMITE) && (self->s.weapon != WP_LANDMINE) && (self->s.weapon != WP_SATCHEL) && (self->s.weapon != WP_TRIPMINE)) { vec3_t launchvel, launchspot; launchvel[0] = crandom(); launchvel[1] = crandom(); launchvel[2] = random(); VectorScale( launchvel, 160, launchvel ); VectorCopy(self->r.currentOrigin, launchspot); launchspot[2] += 40; { // Gordon: fixes premature grenade explosion, ta bani ;) gentity_t *m = fire_grenade(self, launchspot, launchvel, self->s.weapon); m->damage = 0; } } if (attacker && attacker->client) { if ( attacker == self || OnSameTeam (self, attacker ) ) { // DHM - Nerve :: Complaint lodging if( attacker != self && level.warmupTime <= 0 && g_gamestate.integer == GS_PLAYING) { if( attacker->client->pers.localClient ) { trap_SendServerCommand( self-g_entities, "complaint -4" ); } else { if( meansOfDeath != MOD_CRUSH_CONSTRUCTION && meansOfDeath != MOD_CRUSH_CONSTRUCTIONDEATH && meansOfDeath != MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER ) { if( g_complaintlimit.integer ) { if( !(meansOfDeath == MOD_LANDMINE && g_disableComplaints.integer & TKFL_MINES ) && !((meansOfDeath == MOD_ARTY || meansOfDeath == MOD_AIRSTRIKE) && g_disableComplaints.integer & TKFL_AIRSTRIKE ) && !(meansOfDeath == MOD_MORTAR && g_disableComplaints.integer & TKFL_MORTAR ) ) { trap_SendServerCommand( self-g_entities, va( "complaint %i", attacker->s.number ) ); self->client->pers.complaintClient = attacker->s.clientNum; self->client->pers.complaintEndTime = level.time + 20500; } } } } } // high penalty to offset medic heal /* AddScore( attacker, WOLF_FRIENDLY_PENALTY ); */ if( g_gametype.integer == GT_WOLF_LMS ) { AddKillScore( attacker, WOLF_FRIENDLY_PENALTY ); } } else { //G_AddExperience( attacker, 1 ); // JPW NERVE -- mostly added as conveneience so we can tweak from the #defines all in one place AddScore(attacker, WOLF_FRAG_BONUS); if( g_gametype.integer == GT_WOLF_LMS ) { if( level.firstbloodTeam == -1 ) level.firstbloodTeam = attacker->client->sess.sessionTeam; AddKillScore( attacker, WOLF_FRAG_BONUS ); } attacker->client->lastKillTime = level.time; } } else { AddScore( self, -1 ); if( g_gametype.integer == GT_WOLF_LMS ) AddKillScore( self, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // drop flag regardless if (self->client->ps.powerups[PW_REDFLAG]) { item = BG_FindItem("Red Flag"); if (!item) item = BG_FindItem("Objective"); self->client->ps.powerups[PW_REDFLAG] = 0; } if (self->client->ps.powerups[PW_BLUEFLAG]) { item = BG_FindItem("Blue Flag"); if (!item) item = BG_FindItem("Objective"); self->client->ps.powerups[PW_BLUEFLAG] = 0; } if (item) { vec3_t launchvel = { 0, 0, 0 }; gentity_t *flag = LaunchItem(item, self->r.currentOrigin, launchvel, self->s.number); flag->s.modelindex2 = self->s.otherEntityNum2;// JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here flag->message = self->message; // DHM - Nerve :: also restore item name // Clear out player's temp copies self->s.otherEntityNum2 = 0; self->message = NULL; } // send a fancy "MEDIC!" scream. Sissies, ain' they? if (self->client != NULL) { if( self->health > GIB_HEALTH && meansOfDeath != MOD_SUICIDE && meansOfDeath != MOD_SWITCHTEAM ) { G_AddEvent( self, EV_MEDIC_CALL, 0 ); } } Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for(i=0; i<level.numConnectedClients; i++) { gclient_t *client = &level.clients[level.sortedClients[i]]; if(client->pers.connected != CON_CONNECTED) continue; if(client->sess.sessionTeam != TEAM_SPECTATOR) continue; if(client->sess.spectatorClient == self->s.number) { Cmd_Score_f(g_entities + level.sortedClients[i]); } } self->takedamage = qtrue; // can still be gibbed self->r.contents = CONTENTS_CORPSE; //self->s.angles[2] = 0; self->s.powerups = 0; self->s.loopSound = 0; self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo LookAtKiller( self, inflictor, attacker ); self->client->ps.viewangles[0] = 0; self->client->ps.viewangles[2] = 0; //VectorCopy( self->s.angles, self->client->ps.viewangles ); // trap_UnlinkEntity( self ); self->r.maxs[2] = self->client->ps.crouchMaxZ; //% 0; // ydnar: so bodies don't clip into world self->client->ps.maxs[2] = self->client->ps.crouchMaxZ; //% 0; // ydnar: so bodies don't clip into world trap_LinkEntity( self ); // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.timeCurrent + 800; // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop // FIXME: contents is always 0 here if ( self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) ) { GibEntity( self, killer ); nogib = qfalse; } if(nogib){ // normal death // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH + 1; } // Arnout: re-enable this for flailing /* if( self->client->ps.groundEntityNum == ENTITYNUM_NONE ) { self->client->ps.pm_flags |= PMF_FLAILING; self->client->ps.pm_time = 750; BG_AnimScriptAnimation( &self->client->ps, ANIM_MT_FLAILING, qtrue ); // Face explosion directory { vec3_t angles; vectoangles( self->client->ps.velocity, angles ); self->client->ps.viewangles[YAW] = angles[YAW]; SetClientViewAngle( self, self->client->ps.viewangles ); } } else*/ // DHM - Play death animation, and set pm_time to delay 'fallen' animation //if( G_IsSinglePlayerGame() && self->client->sess.sessionTeam == TEAM_ALLIES ) { // // play "falldown" animation since allies bots won't ever die completely // self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, self->client->pers.character->animModelInfo, ANIM_ET_FALLDOWN, qfalse, qtrue ); // G_StartPlayerAppropriateSound(self, "death"); //} else { self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, self->client->pers.character->animModelInfo, ANIM_ET_DEATH, qfalse, qtrue ); // death animation script already contains sound //} // record the death animation to be used later on by the corpse self->client->torsoDeathAnim = self->client->ps.torsoAnim; self->client->legsDeathAnim = self->client->ps.legsAnim; G_AddEvent( self, EV_DEATH1 + 1, killer ); // the body can still be gibbed self->die = body_die; } if( meansOfDeath == MOD_MACHINEGUN ) { switch( self->client->sess.sessionTeam ) { case TEAM_AXIS: level.axisMG42Counter = level.time; break; case TEAM_ALLIES: level.alliesMG42Counter = level.time; break; } } G_FadeItems( self, MOD_SATCHEL ); CalculateRanks(); if( killedintank /*Gordon: automatically go to limbo from tank*/ ) { limbo( self, qfalse ); // but no corpse } else if ( (meansOfDeath == MOD_SUICIDE && g_gamestate.integer == GS_PLAYING) ) { limbo( self, qtrue ); } else if( g_gametype.integer == GT_WOLF_LMS ) { if( !G_CountTeamMedics( self->client->sess.sessionTeam, qtrue ) ) { limbo( self, qtrue ); } } }
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; // TTimo might be used uninitialized int contents = 0; int killer; int i; const char *killerName, *obit; qboolean nogib = qtrue; gitem_t *item = NULL; // JPW NERVE for flag drop vec3_t launchvel,launchspot; // JPW NERVE gentity_t *flag; // JPW NERVE if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; G_AddEvent( self, EV_STOPSTREAMINGSOUND, 0 ); if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = "<bad obituary>"; } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf( "Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; // JPW NERVE -- if player is holding ticking grenade, drop it if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( ( self->client->ps.grenadeTimeLeft ) && ( self->s.weapon != WP_DYNAMITE ) ) { launchvel[0] = crandom(); launchvel[1] = crandom(); launchvel[2] = random(); VectorScale( launchvel, 160, launchvel ); VectorCopy( self->r.currentOrigin, launchspot ); launchspot[2] += 40; fire_grenade( self, launchspot, launchvel, self->s.weapon ); } } // jpw if ( attacker && attacker->client ) { if ( attacker == self || OnSameTeam( self, attacker ) ) { // DHM - Nerve :: Complaint lodging if ( attacker != self && level.warmupTime <= 0 ) { if ( attacker->client->pers.localClient ) { trap_SendServerCommand( self - g_entities, "complaint -4" ); } else { trap_SendServerCommand( self - g_entities, va( "complaint %i", attacker->s.number ) ); self->client->pers.complaintClient = attacker->s.clientNum; self->client->pers.complaintEndTime = level.time + 20500; } } // dhm // JPW NERVE if ( g_gametype.integer >= GT_WOLF ) { // high penalty to offset medic heal AddScore( attacker, WOLF_FRIENDLY_PENALTY ); } else { // jpw AddScore( attacker, -1 ); } } else { // JPW NERVE -- mostly added as conveneience so we can tweak from the #defines all in one place if ( g_gametype.integer >= GT_WOLF ) { AddScore( attacker, WOLF_FRAG_BONUS ); } else { // jpw AddScore( attacker, 1 ); } attacker->client->lastKillTime = level.time; } } else { AddScore( self, -1 ); } // Add team bonuses Team_FragBonuses( self, inflictor, attacker ); // if client is in a nodrop area, don't drop anything // JPW NERVE new drop behavior if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // only drop here in single player; in multiplayer, drop @ limbo contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } } // drop flag regardless if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( self->client->ps.powerups[PW_REDFLAG] ) { item = BG_FindItem( "Red Flag" ); if ( !item ) { item = BG_FindItem( "Objective" ); } self->client->ps.powerups[PW_REDFLAG] = 0; } if ( self->client->ps.powerups[PW_BLUEFLAG] ) { item = BG_FindItem( "Blue Flag" ); if ( !item ) { item = BG_FindItem( "Objective" ); } self->client->ps.powerups[PW_BLUEFLAG] = 0; } if ( item ) { launchvel[0] = crandom() * 20; launchvel[1] = crandom() * 20; launchvel[2] = 10 + random() * 10; flag = LaunchItem( item,self->r.currentOrigin,launchvel,self->s.number ); flag->s.modelindex2 = self->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here flag->message = self->message; // DHM - Nerve :: also restore item name // Clear out player's temp copies self->s.otherEntityNum2 = 0; self->message = NULL; } // send a fancy "MEDIC!" scream. Sissies, ain' they? if ( self->client != NULL ) { if ( self->health > GIB_HEALTH && meansOfDeath != MOD_SUICIDE ) { if ( self->client->sess.sessionTeam == TEAM_RED ) { if ( random() > 0.5 ) { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic2.wav" ) ); } else { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic3.wav" ) ); } } else { if ( random() > 0.5 ) { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic3.wav" ) ); } else { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic2.wav" ) ); } } } } } // jpw Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->r.contents = CONTENTS_CORPSE; self->s.powerups = 0; // JPW NERVE -- only corpse in SP; in MP, need CONTENTS_BODY so medic can operate if ( g_gametype.integer == GT_SINGLE_PLAYER ) { self->s.weapon = WP_NONE; self->s.angles[0] = 0; } else { self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo } // jpw self->s.angles[2] = 0; LookAtKiller( self, inflictor, attacker ); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; trap_UnlinkEntity( self ); self->r.maxs[2] = 0; self->client->ps.maxs[2] = 0; trap_LinkEntity( self ); // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 800; // remove powerups memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); // never gib in a nodrop if ( self->health <= GIB_HEALTH && !( contents & CONTENTS_NODROP ) ) { GibEntity( self, killer ); nogib = qfalse; } if ( nogib ) { // normal death // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH + 1; } // JPW NERVE for medic self->client->medicHealAmt = 0; // jpw // DHM - Play death animation, and set pm_time to delay 'fallen' animation self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue ); G_AddEvent( self, EV_DEATH1 + 1, killer ); // the body can still be gibbed self->die = body_die; } trap_LinkEntity( self ); if ( g_gametype.integer >= GT_WOLF && meansOfDeath == MOD_SUICIDE ) { limbo( self, qtrue ); } }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i,counter2; char *killerName, *obit; if ( !(self->client) || (self->client->ps.pm_type == PM_DEAD) ) { return; } if ( level.intermissiontime ) { return; } //unlagged - backward reconciliation #2 // make sure the body shows up in the client's current position G_UnTimeShiftClient( self ); //unlagged - backward reconciliation #2 //KK-OAX Here is where we run the streak logic. G_RunStreakLogic( attacker, self ); // check for an almost capture CheckAlmostCapture( self, attacker ); // check for a player that almost brought in cubes CheckAlmostScored( self, attacker ); if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } if ((self->client->ps.eFlags & EF_TICKING) && self->activator) { self->client->ps.eFlags &= ~EF_TICKING; self->activator->think = G_FreeEntity; self->activator->nextthink = level.time; } self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = "<bad obituary>"; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; //Sago: Hmmm... generic? Can I transmit anything I like? Like if it is a team kill? Let's try ent->s.generic1 = OnSameTeam (self, attacker); if( !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime) ) ent->r.svFlags = SVF_BROADCAST; // send to everyone (if not an elimination gametype during active warmup) else ent->r.svFlags = SVF_NOCLIENT; self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; if (attacker && attacker->client) { attacker->client->lastkilled_client = self->s.number; if ( attacker == self || OnSameTeam (self, attacker ) ) { if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime)) if( (g_gametype.integer <GT_TEAM && g_ffa_gt!=1 && self->client->ps.persistant[PERS_SCORE]>0) || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide AddScore( attacker, self->r.currentOrigin, -1 ); } else { if(g_gametype.integer!=GT_LMS) AddScore( attacker, self->r.currentOrigin, 1 ); if( meansOfDeath == MOD_GAUNTLET ) { // Attack gets a challenge complete: if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT)) ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS); // play humiliation on player attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 0, attacker->client->pers.netname, "GAUNTLET" ); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; // also play humiliation on target self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD; } //If neither attacker or taget is bots and not the same if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT) && self!=attacker) { switch(meansOfDeath) { case MOD_GAUNTLET: ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS); break; case MOD_MACHINEGUN: ChallengeMessage(attacker,WEAPON_MACHINEGUN_KILLS); break; case MOD_SHOTGUN: ChallengeMessage(attacker,WEAPON_SHOTGUN_KILLS); break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: ChallengeMessage(attacker,WEAPON_GRANADE_KILLS); break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: ChallengeMessage(attacker,WEAPON_ROCKET_KILLS); break; case MOD_LIGHTNING: ChallengeMessage(attacker,WEAPON_LIGHTNING_KILLS); break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: ChallengeMessage(attacker,WEAPON_PLASMA_KILLS); break; case MOD_RAILGUN: if(g_instantgib.integer) ChallengeMessage(attacker,WEAPON_INSTANT_RAIL_KILLS); else ChallengeMessage(attacker,WEAPON_RAIL_KILLS); break; case MOD_BFG: case MOD_BFG_SPLASH: ChallengeMessage(attacker,WEAPON_BFG_KILLS); break; case MOD_NAIL: ChallengeMessage(attacker,WEAPON_NAILGUN_KILLS); break; case MOD_CHAINGUN: ChallengeMessage(attacker,WEAPON_CHAINGUN_KILLS); break; case MOD_PROXIMITY_MINE: ChallengeMessage(attacker,WEAPON_MINE_KILLS); break; case MOD_GRAPPLE: ChallengeMessage(attacker,WEAPON_GRAPPLE_KILLS); break; case MOD_LAVA: case MOD_SLIME: case MOD_TRIGGER_HURT: case MOD_FALLING: ChallengeMessage(attacker,WEAPON_PUSH_KILLS); break; case MOD_CRUSH: ChallengeMessage(attacker,WEAPON_CRUSH_KILLS); break; case MOD_TELEFRAG: ChallengeMessage(attacker,WEAPON_TELEFRAG_KILLS); break; }; ChallengeMessage(attacker,GENERAL_TOTALKILLS); ChallengeMessage(self,GENERAL_TOTALDEATHS); //Lets count number of powerups: i = 0; counter2 = 0; if(attacker->client->ps.powerups[PW_QUAD]) { ChallengeMessage(attacker,POWERUP_QUAD_KILL); counter2++; } if(self->client->ps.powerups[PW_QUAD]) { ChallengeMessage(attacker,POWERUP_COUNTER_QUAD); i++; } if(attacker->client->ps.powerups[PW_HASTE]) { ChallengeMessage(attacker,POWERUP_SPEED_KILL); counter2++; } if(self->client->ps.powerups[PW_HASTE]) { ChallengeMessage(attacker,POWERUP_COUNTER_SPEED); i++; } if(attacker->client->ps.powerups[PW_INVIS]) { ChallengeMessage(attacker,POWERUP_INVIS_KILL); counter2++; } if(self->client->ps.powerups[PW_INVIS]) { ChallengeMessage(attacker,POWERUP_COUNTER_INVIS); i++; } if(attacker->client->ps.powerups[PW_FLIGHT]) { ChallengeMessage(attacker,POWERUP_FLIGHT_KILL); counter2++; } if(self->client->ps.powerups[PW_FLIGHT]) { ChallengeMessage(attacker,POWERUP_COUNTER_FLIGHT); i++; } if(self->client->ps.powerups[PW_BATTLESUIT]) { ChallengeMessage(attacker,POWERUP_COUNTER_ENVIR); i++; } if(self->client->ps.powerups[PW_REGEN]) { ChallengeMessage(attacker,POWERUP_COUNTER_REGEN); i++; } if(i>1) //The target had more than one powerup ChallengeMessage(attacker,POWERUP_COUNTER_MULTI); if(counter2>1) //The attacker has more than one powerup ChallengeMessage(attacker,POWERUP_MULTI_KILL); } // check for two kills in a short amount of time // if this is close enough to the last kill, give a reward sound if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) { // KK-OAX // Check if Multikills are enabled if( g_altExcellent.integer ) { attacker->client->pers.multiKillCount++; G_checkForMultiKill( attacker ); } // play excellent on player attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 1, attacker->client->pers.netname, "EXCELLENT" ); if(!level.hadBots) //There has not been any bots ChallengeMessage(attacker,AWARD_EXCELLENT); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } else { //KK-OAX Clear multikill count //Must be 1 so the correct number of kills are displayed to the clients. attacker->client->pers.multiKillCount = 1; } attacker->client->lastKillTime = level.time; } } else { if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime)) if(self->client->ps.persistant[PERS_SCORE]>0 || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide AddScore( self, self->r.currentOrigin, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // if I committed suicide, the flag does not fall, it returns. if (meansOfDeath == MOD_SUICIDE) { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); self->client->ps.powerups[PW_NEUTRALFLAG] = 0; } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); self->client->ps.powerups[PW_REDFLAG] = 0; } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); self->client->ps.powerups[PW_BLUEFLAG] = 0; } } TossClientPersistantPowerups( self ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( self ); } // if client is in a nodrop area, don't drop anything (but return CTF flags!) TossClientItems( self ); //#endif Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller (self, inflictor, attacker); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700 +i; if(g_respawntime.integer>0) { for(i=0; self->client->respawnTime > i*g_respawntime.integer*1000;i++); self->client->respawnTime = i*g_respawntime.integer*1000; } //For testing: //G_Printf("Respawntime: %i\n",self->client->respawnTime); //However during warm up, we should respawn quicker! if(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) if(level.time<=level.roundStartTime && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer) self->client->respawnTime = level.time + rand()%800; RespawnTimeMessage(self,self->client->respawnTime); // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; if (self->s.eFlags & EF_KAMIKAZE) { Kamikaze_DeathTimer( self ); } } trap_LinkEntity (self); }
/* ============ AICast_Die ============ */ void AICast_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { int contents; int killer; cast_state_t *cs; qboolean nogib = qtrue; // print debugging message if ( aicast_debug.integer == 2 && attacker->s.number == 0 ) { G_Printf( "killed %s\n", self->aiName ); } cs = AICast_GetCastState( self->s.number ); if ( attacker ) { killer = attacker->s.number; } else { killer = ENTITYNUM_WORLD; } // record the sighting (FIXME: silent weapons shouldn't do this, but the AI should react in some way) if ( attacker->client ) { AICast_UpdateVisibility( self, attacker, qtrue, qtrue ); } // the zombie should show special effect instead of gibbing if ( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime ) { if ( cs->secondDeadTime > 1 ) { // we are already totally dead self->health += damage; // don't drop below gib_health if we weren't already below it return; } /* if (!cs->rebirthTime) { self->health = -999; damage = 999; } else if ( self->health >= GIB_HEALTH ) { // while waiting for rebirth, we only "die" if we drop below gib health return; } */ // always gib self->health = -999; damage = 999; } // Zombies are very fragile against highly explosives if ( self->aiCharacter == AICHAR_ZOMBIE && damage > 20 && inflictor != attacker ) { self->health = -999; damage = 999; } // process the event if ( self->client->ps.pm_type == PM_DEAD ) { // already dead if ( self->health < GIB_HEALTH ) { if ( self->aiCharacter == AICHAR_ZOMBIE ) { // RF, changed this so Zombies always gib now GibEntity( self, killer ); nogib = qfalse; /* // Zombie has special exploding cloud effect if (attacker != inflictor || attacker->s.weapon == WP_VENOM) { GibEntity( self, killer ); nogib = qfalse; } else { // Zombie will decompose upon dying self->client->ps.eFlags |= EF_MONSTER_EFFECT2; self->s.effect2Time = level.time+200; self->health = -1; } */ self->takedamage = qfalse; self->r.contents = 0; cs->secondDeadTime = 2; cs->rebirthTime = 0; cs->revivingTime = 0; } else { body_die( self, inflictor, attacker, damage, meansOfDeath ); return; } } } else { // this is our first death, so set everything up if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; self->enemy = attacker; // drop a weapon? // if client is in a nodrop area, don't drop anything contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } // make sure the client doesn't forget about this entity until it's set to "dead" frame // otherwise it might replay it's death animation if it goes out and into client view self->r.svFlags |= SVF_BROADCAST; self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[1] = self->client->ps.viewangles[1]; self->s.angles[2] = 0; VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; self->client->ps.maxs[2] = self->r.maxs[2]; // remove powerups memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); //cs->rebirthTime = 0; // never gib in a nodrop if ( self->health <= GIB_HEALTH ) { if ( self->aiCharacter == AICHAR_ZOMBIE ) { // RF, changed this so Zombies always gib now GibEntity( self, killer ); nogib = qfalse; /* // Zombie has special exploding cloud effect if (attacker != inflictor || attacker->s.weapon == WP_VENOM) { GibEntity( self, killer ); nogib = qfalse; self->takedamage = qfalse; self->r.contents = 0; cs->secondDeadTime = 2; } else { self->client->ps.eFlags |= EF_MONSTER_EFFECT2; self->s.effect2Time = level.time+200; self->takedamage = qfalse; self->r.contents = 0; self->health = -1; cs->secondDeadTime = 2; } */ } else if ( !( contents & CONTENTS_NODROP ) ) { body_die( self, inflictor, attacker, damage, meansOfDeath ); //GibEntity( self, killer ); nogib = qfalse; } } // if we are a zombie, and lying down during our first death, then we should just die if ( !( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime && cs->rebirthTime ) ) { // set enemy weapon BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse ); if ( attacker->client ) { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, inflictor->s.weapon, qtrue ); } else { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse ); } // set enemy location BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, 0, qfalse ); if ( infront( self, inflictor ) ) { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_INFRONT, qtrue ); } else { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_BEHIND, qtrue ); } // play the animation BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue ); // set this flag so no other anims override us self->client->ps.eFlags |= EF_DEAD; self->s.eFlags |= EF_DEAD; } } if ( nogib ) { // set for rebirth if ( self->aiCharacter == AICHAR_ZOMBIE ) { if ( !cs->secondDeadTime ) { cs->rebirthTime = level.time + 5000 + rand() % 2000; cs->secondDeadTime = qtrue; cs->revivingTime = 0; } else if ( cs->secondDeadTime > 1 ) { cs->rebirthTime = 0; cs->revivingTime = 0; cs->deathTime = level.time; } } else { // the body can still be gibbed self->die = body_die; } } trap_LinkEntity( self ); // mark the time of death cs->deathTime = level.time; // dying ai's can trigger a target if ( !cs->rebirthTime ) { G_UseTargets( self, self ); // really dead now, so call the script AICast_ScriptEvent( cs, "death", "" ); // call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) { cs->deathfunc( self, attacker, damage, meansOfDeath ); //----(SA) added mod } } else { // really dead now, so call the script AICast_ScriptEvent( cs, "fakedeath", "" ); // call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) { cs->deathfunc( self, attacker, damage, meansOfDeath ); //----(SA) added mod } } }
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); } }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i; char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } //if we're in SP mode and player killed a bot, award score for the kill if ( IsBot( self ) ) { if ( self->parent && self->parent->health && attacker->client ) { AddScore( attacker, self->r.currentOrigin, self->parent->health ); self->s.time = level.time; } } if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = "<bad obituary>"; } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone /* ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone */ self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; // Add team bonuses //Team_FragBonuses(self, inflictor, attacker); // if client is in a nodrop area, don't drop anything (but return CTF flags!) contents = trap_PointContents( self->r.currentOrigin, -1 ); TossClientItems( self ); Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller (self, inflictor, attacker); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time if ( !IsBot(self) ) self->client->respawnTime = level.time + 1700; else self->client->respawnTime = level.time + 5000; //keep bot bodies around slightly longer // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity (self); // Fire trigger_death and trigger_frag target entities and the deathtarget for the related target_botspawn G_UseTriggerFragAndDeathEntities ( self, attacker ); if ( self->parent ) G_UseDeathTargets( self->parent, self ); if ( !IsBot( self ) ) G_FadeOut( 1.0 ); }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents = 0; int killer; int i; char *killerName, *obit; qboolean nogib = qtrue; gitem_t *item = NULL; // JPW NERVE for flag drop vec3_t launchvel; // JPW NERVE gentity_t *flag; // JPW NERVE if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } //----(SA) commented out as we have no hook // if (self->client && self->client->hook) // Weapon_HookFree(self->client->hook); self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = "<bad obituary>"; } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf( "Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; if ( attacker && attacker->client ) { if ( attacker == self || OnSameTeam( self, attacker ) ) { AddScore( attacker, -1 ); } else { AddScore( attacker, 1 ); // Ridah, not in single player if ( g_gametype.integer != GT_SINGLE_PLAYER ) { // done. if ( meansOfDeath == MOD_GAUNTLET ) { attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; attacker->client->ps.persistant[PERS_REWARD] = REWARD_GAUNTLET; attacker->client->ps.persistant[PERS_REWARD_COUNT]++; // add the sprite over the player's head // attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT /*| EF_AWARD_GAUNTLET*/ ); //attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; // also play humiliation on target self->client->ps.persistant[PERS_REWARD] = REWARD_GAUNTLET; self->client->ps.persistant[PERS_REWARD_COUNT]++; } // check for two kills in a short amount of time // if this is close enough to the last kill, give a reward sound if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) { attacker->client->ps.persistant[PERS_REWARD_COUNT]++; attacker->client->ps.persistant[PERS_REWARD] = REWARD_EXCELLENT; attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++; // add the sprite over the player's head // attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT /*| EF_AWARD_GAUNTLET*/ ); // attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } // Ridah } // done. attacker->client->lastKillTime = level.time; } } else { AddScore( self, -1 ); } // Add team bonuses Team_FragBonuses( self, inflictor, attacker ); // if client is in a nodrop area, don't drop anything // JPW NERVE new drop behavior if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // only drop here in single player; in multiplayer, drop @ limbo contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } } // drop flag regardless if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( self->client->ps.powerups[PW_REDFLAG] ) { item = BG_FindItem( "Red Flag" ); } if ( self->client->ps.powerups[PW_BLUEFLAG] ) { item = BG_FindItem( "Blue Flag" ); } launchvel[0] = crandom() * 20; launchvel[1] = crandom() * 20; launchvel[2] = 10 + random() * 10; if ( item ) { flag = LaunchItem( item,self->r.currentOrigin,launchvel ); flag->s.modelindex2 = self->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here } } // jpw Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.powerups = 0; // JPW NERVE -- only corpse in SP; in MP, need CONTENTS_BODY so medic can operate if ( g_gametype.integer == GT_SINGLE_PLAYER ) { self->r.contents = CONTENTS_CORPSE; self->s.weapon = WP_NONE; } else { self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo } // jpw self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller( self, inflictor, attacker ); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700; // remove powerups memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); if ( g_gametype.integer == GT_SINGLE_PLAYER ) { trap_SendServerCommand( -1, "mu_play sound/music/l_failed_1.wav 0\n" ); trap_SetConfigstring( CS_MUSIC_QUEUE, "" ); // clear queue so it'll be quiet after hit trap_SendServerCommand( -1, "cp missionfail0" ); } // never gib in a nodrop if ( self->health <= GIB_HEALTH && !( contents & CONTENTS_NODROP ) && g_blood.integer ) { // if(self->client->ps.eFlags & EF_HEADSHOT) // { // GibHead(self, killer); // } // else // gib death // { GibEntity( self, killer ); nogib = qfalse; // } } if ( nogib ) { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH + 1; } // JPW NERVE for medic self->client->medicHealAmt = 0; // jpw self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + 1, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity( self ); if ( g_gametype.integer == GT_SINGLE_PLAYER ) { AICast_ScriptEvent( AICast_GetCastState( self->s.number ), "death", "" ); } }