/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int killer; int i, j; char *killerName, *obit; float totalDamage = 0.0f; float percentDamage = 0.0f; gentity_t *player; qboolean tk = qfalse; if( self->client->ps.pm_type == PM_DEAD ) return; if( level.intermissiontime ) return; self->client->ps.pm_type = PM_DEAD; self->suicideTime = 0; if( attacker ) { killer = attacker->s.number; if( attacker->client ) { killerName = attacker->client->pers.netname; // ROTAX /* tk = ( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ] == self->client->ps.stats[ STAT_PTEAM ] ); if( attacker != self && attacker->client->ps.stats[ STAT_PTEAM ] == self->client->ps.stats[ STAT_PTEAM ] ) { attacker->client->pers.statscounters.teamkills++; if( attacker->client->pers.teamSelection == PTE_ALIENS ) { level.alienStatsCounters.teamkills++; } else if( attacker->client->pers.teamSelection == PTE_HUMANS ) { level.humanStatsCounters.teamkills++; } } */ tk = ( attacker != self && attacker->client->pers.statscounters.tremball_team == self->client->pers.statscounters.tremball_team ); } 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^7 killed %s^7 by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); //TA: close any menus the client has open G_CloseMenus( self->client->ps.clientNum ); //TA: deactivate all upgrades for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) BG_DeactivateUpgrade( i, self->client->ps.stats ); // broadcast the death event to everyone if( !tk ) { 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 } else if( attacker && attacker->client ) { // tjw: obviously this is a hack and belongs in the client, but // this works as a temporary fix. trap_SendServerCommand( -1, va( "print \"%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n\"", self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] ) ); trap_SendServerCommand( attacker - g_entities, va( "cp \"You killed ^1TEAMMATE^7 %s\"", self->client->pers.netname ) ); G_LogOnlyPrintf("%s^7 was killed by ^1TEAMMATE^7 %s^7 (Did %d damage to %d max)\n", self->client->pers.netname, attacker->client->pers.netname, self->client->tkcredits[ attacker->s.number ], self->client->ps.stats[ STAT_MAX_HEALTH ] ); } self->enemy = attacker; self->client->ps.persistant[ PERS_KILLED ]++; self->client->pers.statscounters.deaths++; if( self->client->pers.teamSelection == PTE_ALIENS ) { level.alienStatsCounters.deaths++; } else if( self->client->pers.teamSelection == PTE_HUMANS ) { level.humanStatsCounters.deaths++; } if( attacker && attacker->client ) { attacker->client->lastkilled_client = self->s.number; if( g_devmapKillerHP.integer && g_cheats.integer ) { trap_SendServerCommand( self-g_entities, va( "print \"Your killer, %s, had %3i HP.\n\"", killerName, attacker->health ) ); } if( attacker == self || attacker->client->pers.statscounters.tremball_team == self->client->pers.statscounters.tremball_team ) // ROTAX { AddScore( attacker, -1 ); // Retribution: transfer value of player from attacker to victim if( g_retribution.integer) { if(attacker!=self){ int max = ALIEN_MAX_KILLS, tk_value = 0; char *type = "evos"; if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { tk_value = BG_ClassCanEvolveFromTo( PCL_ALIEN_LEVEL0, self->client->ps.stats[ STAT_PCLASS ], ALIEN_MAX_KILLS, 0 ); } else { tk_value = BG_GetValueOfEquipment( &self->client->ps ); max = HUMAN_MAX_CREDITS; type = "credits"; } if( attacker->client->ps.persistant[ PERS_CREDIT ] < tk_value ) tk_value = attacker->client->ps.persistant[ PERS_CREDIT ]; if( self->client->ps.persistant[ PERS_CREDIT ]+tk_value > max ) tk_value = max-self->client->ps.persistant[ PERS_CREDIT ]; if( tk_value > 0 ) { // adjust using the retribution cvar (in percent) tk_value = tk_value*g_retribution.integer/100; G_AddCreditToClient( self->client, tk_value, qtrue ); G_AddCreditToClient( attacker->client, -tk_value, qtrue ); trap_SendServerCommand( self->client->ps.clientNum, va( "print \"Received ^3%d %s ^7from %s ^7in retribution.\n\"", tk_value, type, attacker->client->pers.netname ) ); trap_SendServerCommand( attacker->client->ps.clientNum, va( "print \"Transfered ^3%d %s ^7to %s ^7in retribution.\n\"", tk_value, type, self->client->pers.netname ) ); } } } // Normal teamkill penalty else { if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) G_AddCreditToClient( attacker->client, -FREEKILL_ALIEN, qtrue ); else if( attacker->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) G_AddCreditToClient( attacker->client, -FREEKILL_HUMAN, qtrue ); } } else { AddScore( attacker, 1 ); attacker->client->lastKillTime = level.time; attacker->client->pers.statscounters.kills++; if( attacker->client->pers.teamSelection == PTE_ALIENS ) { level.alienStatsCounters.kills++; } else if( attacker->client->pers.teamSelection == PTE_HUMANS ) { level.humanStatsCounters.kills++; } } if( attacker == self ) { attacker->client->pers.statscounters.suicides++; if( attacker->client->pers.teamSelection == PTE_ALIENS ) { level.alienStatsCounters.suicides++; } else if( attacker->client->pers.teamSelection == PTE_HUMANS ) { level.humanStatsCounters.suicides++; } } } else if( attacker->s.eType != ET_BUILDABLE ) AddScore( self, -1 ); //total up all the damage done by every client for( i = 0; i < MAX_CLIENTS; i++ ) totalDamage += (float)self->credits[ i ]; // if players did more than DAMAGE_FRACTION_FOR_KILL increment the stage counters if( !OnSameTeam( self, attacker ) && totalDamage >= ( self->client->ps.stats[ STAT_MAX_HEALTH ] * DAMAGE_FRACTION_FOR_KILL ) ) { if( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { trap_Cvar_Set( "g_alienKills", va( "%d", g_alienKills.integer + 1 ) ); if( g_alienStage.integer < 2 ) { self->client->pers.statscounters.feeds++; level.humanStatsCounters.feeds++; } } else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { trap_Cvar_Set( "g_humanKills", va( "%d", g_humanKills.integer + 1 ) ); if( g_humanStage.integer < 2 ) { self->client->pers.statscounters.feeds++; level.alienStatsCounters.feeds++; } } } if( totalDamage > 0.0f ) { if( self->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { //nice simple happy bouncy human land float classValue = BG_FindValueOfClass( self->client->ps.stats[ STAT_PCLASS ] ); for( i = 0; i < MAX_CLIENTS; i++ ) { player = g_entities + i; if( !player->client ) continue; if( player->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS ) continue; if( !self->credits[ i ] ) continue; percentDamage = (float)self->credits[ i ] / totalDamage; if( percentDamage > 0 && percentDamage < 1) { player->client->pers.statscounters.assists++; level.humanStatsCounters.assists++; } //add credit G_AddCreditToClient( player->client, (int)( classValue * percentDamage ), qtrue ); } } else if( self->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { //horribly complex nasty alien land float humanValue = BG_GetValueOfHuman( &self->client->ps ); int frags; int unclaimedFrags = (int)humanValue; for( i = 0; i < MAX_CLIENTS; i++ ) { player = g_entities + i; if( !player->client ) continue; if( player->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS ) continue; //this client did no damage if( !self->credits[ i ] ) continue; //nothing left to claim if( !unclaimedFrags ) break; percentDamage = (float)self->credits[ i ] / totalDamage; if( percentDamage > 0 && percentDamage < 1) { player->client->pers.statscounters.assists++; level.alienStatsCounters.assists++; } frags = (int)floor( humanValue * percentDamage); if( frags > 0 ) { //add kills G_AddCreditToClient( player->client, frags, qtrue ); //can't revist this account later self->credits[ i ] = 0; //reduce frags left to be claimed unclaimedFrags -= frags; } } //there are frags still to be claimed if( unclaimedFrags ) { //the clients remaining at this point do not //have enough credit to claim even one frag //so simply give the top <unclaimedFrags> clients //a frag each for( i = 0; i < unclaimedFrags; i++ ) { int maximum = 0; int topClient = 0; for( j = 0; j < MAX_CLIENTS; j++ ) { //this client did no damage if( !self->credits[ j ] ) continue; if( self->credits[ j ] > maximum ) { maximum = self->credits[ j ]; topClient = j; } } if( maximum > 0 ) { player = g_entities + topClient; //add kills G_AddCreditToClient( player->client, 1, qtrue ); //can't revist this account again self->credits[ topClient ] = 0; } } } } } ScoreboardMessage( 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 ) ScoreboardMessage( g_entities + i ); } VectorCopy( self->s.origin, self->client->pers.lastDeathLocation ); self->takedamage = qfalse; // can still be gibbed self->s.weapon = WP_NONE; self->r.contents = CONTENTS_CORPSE; self->s.angles[ PITCH ] = 0; self->s.angles[ ROLL ] = 0; self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ]; 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 ) ); { // normal death static int i; if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { switch( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } } else { switch( i ) { case 0: anim = NSPA_DEATH1; break; case 1: anim = NSPA_DEATH2; break; case 2: default: anim = NSPA_DEATH3; break; } } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } // use own entityid if killed by non-client to prevent uint8_t overflow G_AddEvent( self, EV_DEATH1 + i, ( killer < MAX_CLIENTS ) ? killer : self - g_entities ); // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity( self ); }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int meansOfDeath ) { gentity_t *ent; int anim; int killer; int i; const char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; self->suicideTime = 0; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<world>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) ) { // fall back on the number obit = va( "%d", meansOfDeath ); } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf( "Die: %d %d %s: %s" S_COLOR_WHITE " killed %s\n", killer, ( int )( self - g_entities ), obit, killerName, self->client->pers.netname ); // deactivate all upgrades for ( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) { BG_DeactivateUpgrade( i, self->client->ps.stats ); } // broadcast the death event to everyone ent = G_NewTempEntity( 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 if ( attacker && attacker->client ) { if ( ( attacker == self || OnSameTeam( self, attacker ) ) ) { //punish team kills and suicides if ( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { G_AddCreditToClient( attacker->client, -ALIEN_TK_SUICIDE_PENALTY, qtrue ); G_AddCreditsToScore( attacker, -ALIEN_TK_SUICIDE_PENALTY ); } else if ( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { G_AddCreditToClient( attacker->client, -HUMAN_TK_SUICIDE_PENALTY, qtrue ); G_AddCreditsToScore( attacker, -HUMAN_TK_SUICIDE_PENALTY ); } } else if ( g_showKillerHP.integer ) { trap_SendServerCommand( self - g_entities, va( "print_tr %s %s %3i", QQ( N_("Your killer, $1$^7, had $2$ HP.\n") ), Quote( killerName ), attacker->health ) ); } } else if ( attacker->s.eType != ET_BUILDABLE ) { if ( self->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { G_AddCreditsToScore( self, -ALIEN_TK_SUICIDE_PENALTY ); } else if ( self->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { G_AddCreditsToScore( self, -HUMAN_TK_SUICIDE_PENALTY ); } } // give credits for killing this player G_RewardAttackers( self ); ScoreboardMessage( 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.spectatorState == SPECTATOR_NOT ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { ScoreboardMessage( g_entities + i ); } } VectorCopy( self->s.origin, self->client->pers.lastDeathLocation ); self->takedamage = qfalse; // can still be gibbed self->s.weapon = WP_NONE; if ( self->client->noclip ) { self->client->cliprcontents = CONTENTS_CORPSE; } else { self->r.contents = CONTENTS_CORPSE; } self->s.angles[ PITCH ] = 0; self->s.angles[ ROLL ] = 0; self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ]; 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; // clear misc memset( self->client->ps.misc, 0, sizeof( self->client->ps.misc ) ); { static int i; if ( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } } else { switch ( i ) { case 0: anim = NSPA_DEATH1; break; case 1: anim = NSPA_DEATH2; break; case 2: default: anim = NSPA_DEATH3; break; } } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if ( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } // use own entityid if killed by non-client to prevent uint8_t overflow G_AddEvent( self, EV_DEATH1 + i, ( killer < MAX_CLIENTS ) ? killer : self - g_entities ); // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity( self ); self->client->pers.infoChangeTime = level.time; }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent, *ent2; int anim; int killer; int i; char *killerName, *obit; vec3_t dir; if( self->client->ps.pm_type == PM_DEAD ) return; if( level.intermissiontime ) return; self->client->ps.pm_type = PM_DEAD; self->suicideTime = 0; if( attacker ) { killer = attacker->s.number; if( attacker->client ) killerName = attacker->client->pers.netname; else killerName = "<world>"; } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) // fall back on the number obit = va( "%d", meansOfDeath ); else obit = modNames[ meansOfDeath ]; G_LogPrintf( "Die: %d %d %s: %s" S_COLOR_WHITE " killed %s\n", killer, self - g_entities, obit, killerName, self->client->pers.netname ); // deactivate all upgrades for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ ) BG_DeactivateUpgrade( i, self->client->ps.stats ); // kill all player's buildables if they havent spawned yet // this should eliminate build timer hacks for ever dir[0] = dir[1] = 0.0f; dir[2] = 1.0f; for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) { if( ent->s.eType != ET_BUILDABLE ) continue; if( ent == self ) continue; if( ent->spawned ) continue; if( ent->builtBy != self->client->ps.clientNum ) continue; G_Damage( ent, self, attacker, dir, dir, ent->health, 0, MOD_DECONSTRUCT ); } // 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 ) ) && meansOfDeath != MOD_HSPAWN ) { //punish team kills and suicides if( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { G_AddCreditToClient( attacker->client, -ALIEN_TK_SUICIDE_PENALTY, qtrue ); AddScore( attacker, -ALIEN_TK_SUICIDE_PENALTY ); } else if( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { G_AddCreditToClient( attacker->client, -HUMAN_TK_SUICIDE_PENALTY, qtrue ); AddScore( attacker, -HUMAN_TK_SUICIDE_PENALTY ); } } } else if( attacker->s.eType != ET_BUILDABLE ) { if( self->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) AddScore( self, -ALIEN_TK_SUICIDE_PENALTY ); else if( self->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) AddScore( self, -HUMAN_TK_SUICIDE_PENALTY ); } // give credits for killing this player G_RewardAttackers( self ); ScoreboardMessage( 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.spectatorState == SPECTATOR_NOT ) continue; if( client->sess.spectatorClient == self->s.number ) ScoreboardMessage( g_entities + i ); } VectorCopy( self->s.origin, self->client->pers.lastDeathLocation ); self->takedamage = qfalse; // can still be gibbed self->s.weapon = WP_NONE; self->r.contents = CONTENTS_CORPSE; self->s.angles[ PITCH ] = 0; self->s.angles[ ROLL ] = 0; self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ]; if( meansOfDeath != MOD_ALIEN_HATCH ) //don't look at the alien that jumped out of your chest, it screws up the camera 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; // clear misc memset( self->client->ps.misc, 0, sizeof( self->client->ps.misc ) ); { // normal death static int i; if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { switch( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } } else { switch( i ) { case 0: anim = NSPA_DEATH1; break; case 1: anim = NSPA_DEATH2; break; case 2: default: anim = NSPA_DEATH3; break; } } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) ) { self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; } // use own entityid if killed by non-client to prevent uint8_t overflow G_AddEvent( self, EV_DEATH1 + i, ( killer < MAX_CLIENTS ) ? killer : self - g_entities ); // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity( self ); }