/*
==================
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 );
}
Exemple #2
0
/*
==================
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;
}
Exemple #3
0
/*
==================
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 );
}