예제 #1
0
/*
===============
CG_OffsetShoulderView

===============
*/
void CG_OffsetShoulderView( void )
{
  int            i;
  int            cmdNum;
  usercmd_t      cmd, oldCmd;
  vec3_t         rotationAngles;
  vec3_t         axis[ 3 ], rotaxis[ 3 ];
  float          deltaMousePitch;
  static float   mousePitch;
  vec3_t         forward, right, up;
  classConfig_t* classConfig;

  // Ignore following pitch; it's too jerky otherwise.
  if( !cg_thirdPersonPitchFollow.integer ) 
    cg.refdefViewAngles[ PITCH ] = 0.0f;
    
  AngleVectors( cg.refdefViewAngles, forward, right, up );

  classConfig = BG_ClassConfig( cg.snap->ps.stats[ STAT_CLASS ] );
  VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 0 ], forward, cg.refdef.vieworg );
  VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 1 ], right, cg.refdef.vieworg );
  VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 2 ], up, cg.refdef.vieworg );

  // If someone is playing like this, the rest is already taken care of
  // so just get the firstperson effects and leave.
  if( !cg.demoPlayback && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
  {
    CG_OffsetFirstPersonView();
    return;
  }

  // Get mouse input for camera rotation. 
  cmdNum = trap_GetCurrentCmdNumber();
  trap_GetUserCmd( cmdNum, &cmd );
  trap_GetUserCmd( cmdNum - 1, &oldCmd );

  // Prevent pitch from wrapping and clamp it within a [30, -50] range.
  // Cgame has no access to ps.delta_angles[] here, so we need to reproduce
  // it ourselves here.
  deltaMousePitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
  if( fabs(deltaMousePitch) < 200.0f )
    mousePitch += deltaMousePitch;

  // Handle pitch.
  rotationAngles[ PITCH ] = mousePitch;

  rotationAngles[ PITCH ] = AngleNormalize180( rotationAngles[ PITCH ] + AngleNormalize180( cg.refdefViewAngles[ PITCH ] ) );
  if( rotationAngles [ PITCH ] < -90.0f ) rotationAngles [ PITCH ] = -90.0f;
  if( rotationAngles [ PITCH ] > 90.0f ) rotationAngles [ PITCH ] = 90.0f;

  // Yaw and Roll are much easier.
  rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ];
  rotationAngles[ ROLL ] = 0.0f;

  // Perform the rotations.
  AnglesToAxis( rotationAngles, axis );
  if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
      !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
                      cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
    AxisCopy( axis, rotaxis );

  AxisToAngles( rotaxis, rotationAngles );

  // Actually set the viewangles.
  for( i = 0; i < 3; i++ )
    cg.refdefViewAngles[ i ] = rotationAngles[ i ];

  // Now run the first person stuff so we get various effects added.
  CG_OffsetFirstPersonView( );
}
예제 #2
0
/*
===========
ClientUserInfoChanged

Called from ClientConnect when the player first connects and
directly by the server system when the player updates a userinfo variable.

The game can override any of the settings and call trap_SetUserinfo
if desired.
============
*/
char *ClientUserinfoChanged( int clientNum, qboolean forceName )
{
    gentity_t *ent;
    char      *s;
    char      model[ MAX_QPATH ];
    char      buffer[ MAX_QPATH ];
    char      filename[ MAX_QPATH ];
    char      oldname[ MAX_NAME_LENGTH ];
    char      newname[ MAX_NAME_LENGTH ];
    char      err[ MAX_STRING_CHARS ];
    qboolean  revertName = qfalse;
    gclient_t *client;
    char      userinfo[ MAX_INFO_STRING ];

    ent = g_entities + clientNum;
    client = ent->client;

    trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );

    // check for malformed or illegal info strings
    if( !Info_Validate(userinfo) )
    {
        trap_SendServerCommand( ent - g_entities,
                                "disconnect \"illegal or malformed userinfo\n\"" );
        trap_DropClient( ent - g_entities,
                         "dropped: illegal or malformed userinfo");
        return "Illegal or malformed userinfo";
    }
    // If their userinfo overflowed, tremded is in the process of disconnecting them.
    // If we send our own disconnect, it won't work, so just return to prevent crashes later
    //  in this function. This check must come after the Info_Validate call.
    else if( !userinfo[ 0 ] )
        return "Empty (overflowed) userinfo";

    // stickyspec toggle
    s = Info_ValueForKey( userinfo, "cg_stickySpec" );
    client->pers.stickySpec = atoi( s ) != 0;

    // set name
    Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
    s = Info_ValueForKey( userinfo, "name" );
    G_ClientCleanName( s, newname, sizeof( newname ) );

    if( strcmp( oldname, newname ) )
    {
        if( !forceName && client->pers.namelog->nameChangeTime &&
                level.time - client->pers.namelog->nameChangeTime <=
                g_minNameChangePeriod.value * 1000 )
        {
            trap_SendServerCommand( ent - g_entities, va(
                                        "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"",
                                        g_minNameChangePeriod.integer ) );
            revertName = qtrue;
        }
        else if( !forceName && g_maxNameChanges.integer > 0 &&
                 client->pers.namelog->nameChanges >= g_maxNameChanges.integer  )
        {
            trap_SendServerCommand( ent - g_entities, va(
                                        "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
                                        g_maxNameChanges.integer ) );
            revertName = qtrue;
        }
        else if( !forceName && client->pers.namelog->muted )
        {
            trap_SendServerCommand( ent - g_entities,
                                    "print \"You cannot change your name while you are muted\n\"" );
            revertName = qtrue;
        }
        else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
        {
            trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) );
            revertName = qtrue;
        }

        if( revertName )
        {
            Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer",
                        sizeof( client->pers.netname ) );
            Info_SetValueForKey( userinfo, "name", oldname );
            trap_SetUserinfo( clientNum, userinfo );
        }
        else
        {
            G_CensorString( client->pers.netname, newname,
                            sizeof( client->pers.netname ), ent );
            if( !forceName && client->pers.connected == CON_CONNECTED )
            {
                client->pers.namelog->nameChangeTime = level.time;
                client->pers.namelog->nameChanges++;
            }
            if( *oldname )
            {
                G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n",
                             clientNum, client->pers.ip.str, client->pers.guid,
                             oldname, client->pers.netname,
                             DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
            }
        }
        G_namelog_update_name( client );
    }

    if( client->pers.classSelection == PCL_NONE )
    {
        //This looks hacky and frankly it is. The clientInfo string needs to hold different
        //model details to that of the spawning class or the info change will not be
        //registered and an axis appears instead of the player model. There is zero chance
        //the player can spawn with the battlesuit, hence this choice.
        Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName,
                     BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName );
    }
    else
    {
        Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassConfig( client->pers.classSelection )->modelName,
                     BG_ClassConfig( client->pers.classSelection )->skinName );

        //model segmentation
        Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg",
                     BG_ClassConfig( client->pers.classSelection )->modelName );

        if( G_NonSegModel( filename ) )
            client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL;
        else
            client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL;
    }
    Q_strncpyz( model, buffer, sizeof( model ) );

    // wallwalk follow
    s = Info_ValueForKey( userinfo, "cg_wwFollow" );

    if( atoi( s ) )
        client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW;
    else
        client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW;

    // wallwalk toggle
    s = Info_ValueForKey( userinfo, "cg_wwToggle" );

    if( atoi( s ) )
        client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE;
    else
        client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE;

    // always sprint
    s = Info_ValueForKey( userinfo, "cg_sprintToggle" );

    if( atoi( s ) )
        client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE;
    else
        client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE;

    // fly speed
    s = Info_ValueForKey( userinfo, "cg_flySpeed" );

    if( *s )
        client->pers.flySpeed = atoi( s );
    else
        client->pers.flySpeed = BG_Class( PCL_NONE )->speed;

    // disable blueprint errors
    s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" );

    if( atoi( s ) )
        client->pers.disableBlueprintErrors = qtrue;
    else
        client->pers.disableBlueprintErrors = qfalse;

    // teamInfo
    s = Info_ValueForKey( userinfo, "teamoverlay" );

    if( atoi( s ) != 0 )
    {
        // teamoverlay was enabled so we need an update
        if( client->pers.teamInfo == 0 )
            client->pers.teamInfo = 1;
    }
    else
        client->pers.teamInfo = 0;

    s = Info_ValueForKey( userinfo, "cg_unlagged" );
    if( !s[0] || atoi( s ) != 0 )
        client->pers.useUnlagged = qtrue;
    else
        client->pers.useUnlagged = qfalse;

    Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ),
                sizeof( client->pers.voice ) );

    // send over a subset of the userinfo keys so other clients can
    // print scoreboards, display models, and play custom sounds

    Com_sprintf( userinfo, sizeof( userinfo ),
                 "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s",
                 client->pers.netname, client->pers.teamSelection, model,
                 Com_ClientListString( &client->sess.ignoreList ),
                 client->pers.voice );

    trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );

    /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/

    return NULL;
}
/*
===========
ClientUserInfoChanged

Called from ClientConnect when the player first connects and
directly by the server system when the player updates a userinfo variable.

The game can override any of the settings and call trap_SetUserinfo
if desired.
============
*/
void ClientUserinfoChanged( int clientNum )
{
  gentity_t *ent;
  int       health;
  char      *s;
  char      model[ MAX_QPATH ];
  char      buffer[ MAX_QPATH ];
  char      filename[ MAX_QPATH ];
  char      oldname[ MAX_NAME_LENGTH ];
  char      newname[ MAX_NAME_LENGTH ];
  char      err[ MAX_STRING_CHARS ];
  qboolean  revertName = qfalse;
  gclient_t *client;
  char      c1[ MAX_INFO_STRING ];
  char      c2[ MAX_INFO_STRING ];
  char      userinfo[ MAX_INFO_STRING ];

  ent = g_entities + clientNum;
  client = ent->client;

  trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );

  // check for malformed or illegal info strings
  if( !Info_Validate(userinfo) )
    strcpy( userinfo, "\\name\\badinfo" );

  // set name
  Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
  s = Info_ValueForKey( userinfo, "name" );
  G_ClientCleanName( s, newname, sizeof( newname ) );

  if( strcmp( oldname, newname ) )
  {
    // in case we need to revert and there's no oldname
    if( client->pers.connected != CON_CONNECTED )
        Q_strncpyz( oldname, "UnnamedPlayer", sizeof( oldname ) );

    if( client->pers.nameChangeTime &&
      ( level.time - client->pers.nameChangeTime )
      <= ( g_minNameChangePeriod.value * 1000 ) )
    {
      trap_SendServerCommand( ent - g_entities, va(
        "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"",
         g_minNameChangePeriod.integer ) );
      revertName = qtrue;
    }
    else if( g_maxNameChanges.integer > 0
      && client->pers.nameChanges >= g_maxNameChanges.integer  )
    {
      trap_SendServerCommand( ent - g_entities, va(
        "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
         g_maxNameChanges.integer ) );
      revertName = qtrue;
    }
    else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
    {
      trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) );
      revertName = qtrue;
    }

    if( revertName )
    {
      Q_strncpyz( client->pers.netname, oldname,
        sizeof( client->pers.netname ) );
      Info_SetValueForKey( userinfo, "name", oldname );
      trap_SetUserinfo( clientNum, userinfo );
    }
    else
    {
      Q_strncpyz( client->pers.netname, newname,
        sizeof( client->pers.netname ) );
      if( client->pers.connected == CON_CONNECTED )
      {
        client->pers.nameChangeTime = level.time;
        client->pers.nameChanges++;
      }
    }
  }

  if( client->sess.spectatorState == SPECTATOR_SCOREBOARD )
    Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) );

  if( client->pers.connected == CON_CONNECTED )
  {
    if( strcmp( oldname, client->pers.netname ) )
    {
      trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE
        " renamed to %s\n\"", oldname, client->pers.netname ) );
      G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s\" -> \"%s\"\n", clientNum,
         client->pers.ip, client->pers.guid, oldname, client->pers.netname );
      G_admin_namelog_update( client, qfalse );
    }
  }

  // set max health
  health = atoi( Info_ValueForKey( userinfo, "handicap" ) );
  client->pers.maxHealth = health;

  if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 )
    client->pers.maxHealth = 100;

  if( client->pers.classSelection == PCL_NONE )
  {
    //This looks hacky and frankly it is. The clientInfo string needs to hold different
    //model details to that of the spawning class or the info change will not be
    //registered and an axis appears instead of the player model. There is zero chance
    //the player can spawn with the battlesuit, hence this choice.
    Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName,
                                              BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName );
  }
  else
  {
    Com_sprintf( buffer, MAX_QPATH, "%s/%s",  BG_ClassConfig( client->pers.classSelection )->modelName,
                                              BG_ClassConfig( client->pers.classSelection )->skinName );

    //model segmentation
    Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg",
                 BG_ClassConfig( client->pers.classSelection )->modelName );

    if( G_NonSegModel( filename ) )
      client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL;
    else
      client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL;
  }
  Q_strncpyz( model, buffer, sizeof( model ) );

  // wallwalk follow
  s = Info_ValueForKey( userinfo, "cg_wwFollow" );

  if( atoi( s ) )
    client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW;
  else
    client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW;

  // wallwalk toggle
  s = Info_ValueForKey( userinfo, "cg_wwToggle" );

  if( atoi( s ) )
    client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE;
  else
    client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE;

  // teamInfo
  s = Info_ValueForKey( userinfo, "teamoverlay" );

  if( !*s || atoi( s ) != 0 )
    client->pers.teamInfo = qtrue;
  else
    client->pers.teamInfo = qfalse;

  // colors
  strcpy( c1, Info_ValueForKey( userinfo, "color1" ) );
  strcpy( c2, Info_ValueForKey( userinfo, "color2" ) );

  Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ),
    sizeof( client->pers.voice ) );

  // send over a subset of the userinfo keys so other clients can
  // print scoreboards, display models, and play custom sounds

  Com_sprintf( userinfo, sizeof( userinfo ),
    "n\\%s\\t\\%i\\model\\%s\\c1\\%s\\c2\\%s\\"
    "hc\\%i\\ig\\%16s\\v\\%s",
    client->pers.netname, client->pers.teamSelection, model, c1, c2,
    client->pers.maxHealth, BG_ClientListString( &client->sess.ignoreList ),
    client->pers.voice );

  trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );

  /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/
}
예제 #4
0
/*
==============
CG_Menu
==============
*/
void CG_Menu( int menu, int arg )
{
	const char   *cmd; // command to send
	const char   *longMsg = NULL; // command parameter
	const char   *shortMsg = NULL; // non-modal version of message
	const char   *dialog;
	dialogType_t type = 0; // controls which cg_disable var will switch it off

	switch ( cg.snap->ps.stats[ STAT_TEAM ] )
	{
		case TEAM_ALIENS:
			dialog = "menu tremulous_alien_dialog\n";
			break;

		case TEAM_HUMANS:
			dialog = "menu tremulous_human_dialog\n";
			break;

		default:
			dialog = "menu tremulous_default_dialog\n";
	}

	cmd = dialog;

	switch ( menu )
	{
		case MN_TEAM:
			cmd = "menu tremulous_teamselect\n";
			type = DT_INTERACTIVE;
			break;

		case MN_A_CLASS:
			cmd = "menu tremulous_alienclass\n";
			type = DT_INTERACTIVE;
			break;

		case MN_H_SPAWN:
			cmd = "menu tremulous_humanitem\n";
			type = DT_INTERACTIVE;
			break;

		case MN_A_BUILD:
			cmd = "menu tremulous_alienbuild\n";
			type = DT_INTERACTIVE;
			break;

		case MN_H_BUILD:
			cmd = "menu tremulous_humanbuild\n";
			type = DT_INTERACTIVE;
			break;

		case MN_H_ARMOURY:
			cmd = "menu tremulous_humanarmoury\n";
			type = DT_INTERACTIVE;
			break;

		case MN_H_UNKNOWNITEM:
			shortMsg = "Unknown item";
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_TEAMFULL:
			longMsg = _("The alien team has too many players. Please wait until slots "
			          "become available or join the human team.");
			shortMsg = _("The alien team has too many players");
			type = DT_COMMAND;
			break;

		case MN_H_TEAMFULL:
			longMsg = _("The human team has too many players. Please wait until slots "
			          "become available or join the alien team.");
			shortMsg = _("The human team has too many players");
			type = DT_COMMAND;
			break;

		case MN_A_TEAMLOCKED:
			longMsg = _("The alien team is locked. You cannot join the aliens "
			          "at this time.");
			shortMsg = _("The alien team is locked");
			type = DT_COMMAND;
			break;

		case MN_H_TEAMLOCKED:
			longMsg = _("The human team is locked. You cannot join the humans "
			          "at this time.");
			shortMsg = _("The human team is locked");
			type = DT_COMMAND;
			break;

		case MN_PLAYERLIMIT:
			longMsg = _("The maximum number of playing clients has been reached. "
			          "Please wait until slots become available.");
			shortMsg = _("No free player slots");
			type = DT_COMMAND;
			break;

		case MN_WARMUP:
			longMsg = _("You must wait until the warmup time is finished "
			          "before joining a team. ");
			shortMsg = _("You cannot join a team during warmup.");
			type = DT_COMMAND;
			break;

			//===============================

			// Since cheating commands have no default binds, they will often be done
			// via console. In light of this, perhaps opening a menu is
			// counterintuitive
		case MN_CMD_CHEAT:
			//longMsg   = "This action is considered cheating. It can only be used "
			//            "in cheat mode, which is not enabled on this server.";
			shortMsg = _("Cheats are not enabled on this server");
			type = DT_COMMAND;
			break;

		case MN_CMD_CHEAT_TEAM:
			shortMsg = _("Cheats are not enabled on this server, so "
			           "you may not use this command while on a team");
			type = DT_COMMAND;
			break;

		case MN_CMD_TEAM:
			//longMsg   = "You must be on a team to perform this action. Join the alien"
			//            "or human team and try again.";
			shortMsg = _("Join a team first");
			type = DT_COMMAND;
			break;

		case MN_CMD_SPEC:
			//longMsg   = "You may not perform this action while on a team. Become a "
			//            "spectator before trying again.";
			shortMsg = _("You can only use this command when spectating");
			type = DT_COMMAND;
			break;

		case MN_CMD_ALIEN:
			//longMsg   = "You must be on the alien team to perform this action.";
			shortMsg = _("Must be alien to use this command");
			type = DT_COMMAND;
			break;

		case MN_CMD_HUMAN:
			//longMsg   = "You must be on the human team to perform this action.";
			shortMsg = _("Must be human to use this command");
			type = DT_COMMAND;
			break;

		case MN_CMD_ALIVE:
			//longMsg   = "You must be alive to perform this action.";
			shortMsg = _("Must be alive to use this command");
			type = DT_COMMAND;
			break;

			//===============================

		case MN_B_NOROOM:
			longMsg = _("There is no room to build here. Move until the structure turns "
			          "translucent green, indicating a valid build location.");
			shortMsg = _("There is no room to build here");
			type = DT_BUILD;
			break;

		case MN_B_NORMAL:
			longMsg = _("Cannot build on this surface. The surface is too steep or "
			          "unsuitable for building. Please choose another site for this "
			          "structure.");
			shortMsg = _("Cannot build on this surface");
			type = DT_BUILD;
			break;

		case MN_B_CANNOT:
			longMsg = NULL;
			shortMsg = _("You cannot build that structure");
			type = DT_BUILD;
			break;

			// FIXME: MN_H_ and MN_A_?
		case MN_B_LASTSPAWN:
			longMsg = _("This action would remove your team's last spawn point, "
			          "which often quickly results in a loss. Try building more "
			          "spawns.");
			shortMsg = _("You may not deconstruct the last spawn");
			break;

		case MN_B_SUDDENDEATH:
			longMsg = _("Neither team has prevailed after a certain time and the "
			          "game has entered Sudden Death. During Sudden Death "
			          "building is not allowed.");
			shortMsg = _("Cannot build during Sudden Death");
			type = DT_BUILD;
			break;

		case MN_B_REVOKED:
			longMsg = _("Your teammates have lost faith in your ability to build "
			          "for the team. You will not be allowed to build until your "
			          "team votes to reinstate your building rights.");
			shortMsg = _("Your building rights have been revoked");
			type = DT_BUILD;
			break;

		case MN_B_SURRENDER:
			longMsg = _("Your team has decided to admit defeat and concede the game:"
			          "traitors and cowards are not allowed to build.");
			// too harsh?
			shortMsg = _("Building is denied to traitorous cowards");
			break;

			//===============================

		case MN_H_NOBP:
			if ( cgs.markDeconstruct )
			{
				longMsg = _("There is no power remaining. Free up power by marking "
				          "existing buildable objects.");
			}
			else
			{
				longMsg = _("There is no power remaining. Free up power by deconstructing "
				          "existing buildable objects.");
			}

			shortMsg = _("There is no power remaining");
			type = DT_BUILD;
			break;

		case MN_H_NOTPOWERED:
			longMsg = _("This buildable is not powered. Build a Reactor and/or Repeater "
			          "in order to power it.");
			shortMsg = _("This buildable is not powered");
			type = DT_BUILD;
			break;

		case MN_H_ONEREACTOR:
			longMsg = _("There can only be one Reactor. Deconstruct the existing one if you "
			          "wish to move it.");
			shortMsg = _("There can only be one Reactor");
			type = DT_BUILD;
			break;

		case MN_H_NOPOWERHERE:
			longMsg = _("There is no power here. If available, a Repeater may be used to "
			          "transmit power to this location.");
			shortMsg = _("There is no power here");
			type = DT_BUILD;
			break;

		case MN_H_NODCC:
			longMsg = _("There is no Defense Computer. A Defense Computer is needed to "
			          "build this.");
			shortMsg = _("There is no Defense Computer");
			type = DT_BUILD;
			break;

		case MN_H_RPTPOWERHERE:
			longMsg = _("This area already has power. A Repeater is not required here.");
			shortMsg = _("This area already has power");
			type = DT_BUILD;
			break;

		case MN_H_NOSLOTS:
			longMsg = _("You have no room to carry this. Please sell any conflicting "
			          "upgrades before purchasing this item.");
			shortMsg = _("You have no room to carry this");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_NOFUNDS:
			longMsg = _("Insufficient funds. You do not have enough credits to perform "
			          "this action.");
			shortMsg = _("Insufficient funds");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_ITEMHELD:
			longMsg = _("You already hold this item. It is not possible to carry multiple "
			          "items of the same type.");
			shortMsg = _("You already hold this item");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_NOARMOURYHERE:
			longMsg = _("You must be near a powered Armoury in order to purchase "
			          "weapons, upgrades or ammunition.");
			shortMsg = _("You must be near a powered Armoury");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_NOENERGYAMMOHERE:
			longMsg = _("You must be near a Reactor or a powered Armoury or Repeater "
			          "in order to purchase energy ammunition.");
			shortMsg = _("You must be near a Reactor or a powered Armoury or Repeater");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_NOROOMBSUITON:
			longMsg = _("There is not enough room here to put on a Battle Suit. "
			          "Make sure you have enough head room to climb in.");
			shortMsg = _("Not enough room here to put on a Battle Suit");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_NOROOMBSUITOFF:
			longMsg = _("There is not enough room here to take off your Battle Suit. "
			          "Make sure you have enough head room to climb out.");
			shortMsg = _("Not enough room here to take off your Battle Suit");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_ARMOURYBUILDTIMER:
			longMsg = _("You are not allowed to buy or sell weapons until your "
			          "build timer has expired.");
			shortMsg = _("You can not buy or sell weapons until your build timer "
			           "expires");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_H_DEADTOCLASS:
			shortMsg = _("You must be dead to use the class command");
			type = DT_COMMAND;
			break;

		case MN_H_UNKNOWNSPAWNITEM:
			shortMsg = _("Unknown starting item");
			type = DT_COMMAND;
			break;

			//===============================

		case MN_A_NOCREEP:
			longMsg = _("There is no creep here. You must build near existing Eggs or "
			          "the Overmind. Alien structures will not support themselves.");
			shortMsg = _("There is no creep here");
			type = DT_BUILD;
			break;

		case MN_A_NOOVMND:
			longMsg = _("There is no Overmind. An Overmind must be built to control "
			          "the structure you tried to place.");
			shortMsg = _("There is no Overmind");
			type = DT_BUILD;
			break;

		case MN_A_ONEOVERMIND:
			longMsg = _("There can only be one Overmind. Deconstruct the existing one if you "
			          "wish to move it.");
			shortMsg = _("There can only be one Overmind");
			type = DT_BUILD;
			break;

		case MN_A_NOBP:
			longMsg = _("The Overmind cannot control any more structures. Deconstruct existing "
			          "structures to build more.");
			shortMsg = _("The Overmind cannot control any more structures");
			type = DT_BUILD;
			break;

		case MN_A_NOEROOM:
			longMsg = _("There is no room to evolve here. Move away from walls or other "
			          "nearby objects and try again.");
			shortMsg = _("There is no room to evolve here");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_TOOCLOSE:
			longMsg = _("This location is too close to the enemy to evolve. Move away "
			          "from the enemy's presence and try again.");
			shortMsg = _("This location is too close to the enemy to evolve");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_NOOVMND_EVOLVE:
			longMsg = _("There is no Overmind. An Overmind must be built to allow "
			          "you to upgrade.");
			shortMsg = _("There is no Overmind");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_EVOLVEBUILDTIMER:
			longMsg = _("You cannot evolve until your build timer has expired.");
			shortMsg = _("You cannot evolve until your build timer expires");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_INFEST:
			trap_Cvar_Set( "ui_currentClass",
			               va( "%d %d", cg.snap->ps.stats[ STAT_CLASS ],
			                   cg.snap->ps.persistant[ PERS_CREDIT ] ) );

			cmd = "menu tremulous_alienupgrade\n";
			type = DT_INTERACTIVE;
			break;

		case MN_A_CANTEVOLVE:
			shortMsg = va( _("You cannot evolve into a %s"),
			               BG_ClassConfig( arg )->humanName );
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_EVOLVEWALLWALK:
			shortMsg = _("You cannot evolve while wallwalking");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_UNKNOWNCLASS:
			shortMsg = _("Unknown class");
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_CLASSNOTSPAWN:
			shortMsg = va( _("You cannot spawn as a %s"),
			               BG_ClassConfig( arg )->humanName );
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_CLASSNOTALLOWED:
			shortMsg = va( _("The %s is not allowed"),
			               BG_ClassConfig( arg )->humanName );
			type = DT_ARMOURYEVOLVE;
			break;

		case MN_A_CLASSNOTATSTAGE:
			shortMsg = va( _("The %s is not allowed at Stage %d"),
			               BG_ClassConfig( arg )->humanName,
			               cgs.alienStage + 1 );
			type = DT_ARMOURYEVOLVE;
			break;

		default:
			Com_Printf(_( "cgame: debug: no such menu %d\n"), menu );
	}

	if ( type == DT_ARMOURYEVOLVE && cg_disableUpgradeDialogs.integer )
	{
		return;
	}

	if ( type == DT_BUILD && cg_disableBuildDialogs.integer )
	{
		return;
	}

	if ( type == DT_COMMAND && cg_disableCommandDialogs.integer )
	{
		return;
	}

	if ( cmd != dialog )
	{
		trap_SendConsoleCommand( cmd );
	}
	else if ( longMsg && cg_disableWarningDialogs.integer == 0 )
	{
		trap_Cvar_Set( "ui_dialog", longMsg );
		trap_SendConsoleCommand( cmd );
	}
	else if ( shortMsg && cg_disableWarningDialogs.integer < 2 )
	{
		CG_Printf( "%s\n", shortMsg );
	}
}
예제 #5
0
/*
=============
CG_Obituary
=============
*/
static void CG_Obituary( entityState_t *ent )
{
  int           mod;
  int           target, attacker;
  char          *message;
  char          *message2;
  const char    *targetInfo;
  const char    *attackerInfo;
  char          targetName[ MAX_NAME_LENGTH ];
  char          attackerName[ MAX_NAME_LENGTH ];
  char          className[ 64 ];
  gender_t      gender;
  clientInfo_t  *ci;
  qboolean      teamKill = qfalse;

  target = ent->otherEntityNum;
  attacker = ent->otherEntityNum2;
  mod = ent->eventParm;

  if( target < 0 || target >= MAX_CLIENTS )
    CG_Error( "CG_Obituary: target out of range" );

  ci = &cgs.clientinfo[ target ];
  gender = ci->gender;

  if( attacker < 0 || attacker >= MAX_CLIENTS )
  {
    attacker = ENTITYNUM_WORLD;
    attackerInfo = NULL;
  }
  else
  {
    attackerInfo = CG_ConfigString( CS_PLAYERS + attacker );
    if( ci && cgs.clientinfo[ attacker ].team == ci->team )
      teamKill = qtrue;
  }

  targetInfo = CG_ConfigString( CS_PLAYERS + target );

  if( !targetInfo )
    return;

  Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ));

  message2 = "";

  // check for single client messages

  switch( mod )
  {
    case MOD_FALLING:
      message = "fell fowl to gravity";
      break;
    case MOD_CRUSH:
      message = "was squished";
      break;
    case MOD_WATER:
      message = "forgot to pack a snorkel";
      break;
    case MOD_SLIME:
      message = "melted";
      break;
    case MOD_LAVA:
      message = "did a back flip into the lava";
      break;
    case MOD_TARGET_LASER:
      message = "saw the light";
      break;
    case MOD_TRIGGER_HURT:
      message = "was in the wrong place";
      break;
    case MOD_HSPAWN:
      message = "should have run further";
      break;
    case MOD_ASPAWN:
      message = "shouldn't have trod in the acid";
      break;
    case MOD_MGTURRET:
      message = "was gunned down by a turret";
      break;
    case MOD_TESLAGEN:
      message = "was zapped by a tesla generator";
      break;
    case MOD_ATUBE:
      message = "was melted by an acid tube";
      break;
    case MOD_OVERMIND:
      message = "got too close to the overmind";
      break;
    case MOD_REACTOR:
      message = "got too close to the reactor";
      break;
    case MOD_SLOWBLOB:
      message = "should have visited a medical station";
      break;
    case MOD_SWARM:
      message = "was hunted down by the swarm";
      break;
    case MOD_BUSH://ROTAXfun
      message = "was lost in bush";
      break;
    default:
      message = NULL;
      break;
  }

  if( !message && attacker == target )
  {
    switch( mod )
    {
      case MOD_FLAMER_SPLASH:
        if( gender == GENDER_FEMALE )
          message = "toasted herself";
        else if( gender == GENDER_NEUTER )
          message = "toasted itself";
        else
          message = "toasted himself";
        break;

      case MOD_LCANNON_SPLASH:
        if( gender == GENDER_FEMALE )
          message = "irradiated herself";
        else if( gender == GENDER_NEUTER )
          message = "irradiated itself";
        else
          message = "irradiated himself";
        break;

      case MOD_GRENADE:
        if( gender == GENDER_FEMALE )
          message = "blew herself up";
        else if( gender == GENDER_NEUTER )
          message = "blew itself up";
        else
          message = "blew himself up";
        break;

      case MOD_LEVEL3_BOUNCEBALL:
        if( gender == GENDER_FEMALE )
          message = "sniped herself";
        else if( gender == GENDER_NEUTER )
          message = "sniped itself";
        else
          message = "sniped himself";
        break;

      case MOD_PRIFLE:
        if( gender == GENDER_FEMALE )
          message = "pulse rifled herself";
        else if( gender == GENDER_NEUTER )
          message = "pulse rifled itself";
        else
          message = "pulse rifled himself";
        break;
        
      //ROTAXfun
      case MOD_MINE:
        if( gender == GENDER_FEMALE )
          message = "was betrayed by own mine";
        else if( gender == GENDER_NEUTER )
          message = "it betrayed by own mine";
        else
          message = "was betrayed by own mine";
        break;
      case MOD_FLARE:
        if( gender == GENDER_FEMALE )
          message = "was terminated by own flare";
        else if( gender == GENDER_NEUTER )
          message = "it terminated by own flare";
        else
          message = "was terminated by own flare";
        break;
      case MOD_ABOMB:
        message = "bombed himself up";
        break;

      default:
        if( gender == GENDER_FEMALE )
          message = "killed herself";
        else if( gender == GENDER_NEUTER )
          message = "killed itself";
        else
          message = "killed himself";
        break;
    }
  }

  if( message )
  {
    CG_Printf( "%s" S_COLOR_WHITE " %s\n", targetName, message );
    return;
  }

  // check for double client messages
  if( !attackerInfo )
  {
    attacker = ENTITYNUM_WORLD;
    strcpy( attackerName, "noname" );
  }
  else
  {
    Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ));
    // check for kill messages about the current clientNum
    if( target == cg.snap->ps.clientNum )
      Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) );
  }

  if( attacker != ENTITYNUM_WORLD )
  {
    switch( mod )
    {
      case MOD_PAINSAW:
        message = "was sawn by";
        break;
      case MOD_BLASTER:
        message = "was blasted by";
        break;
      case MOD_MACHINEGUN:
        message = "was machinegunned by";
        break;
      case MOD_CHAINGUN:
        message = "was chaingunned by";
        break;
      case MOD_SHOTGUN:
        message = "was gunned down by";
        break;
      case MOD_PRIFLE:
        message = "was pulse rifled by";
        break;
      case MOD_MDRIVER:
        message = "was mass driven by";
        break;
      case MOD_LASGUN:
        message = "was lasgunned by";
        break;
      case MOD_FLAMER:
        message = "was grilled by";
        message2 = "'s flamer";
        break;
      case MOD_FLAMER_SPLASH:
        message = "was toasted by";
        message2 = "'s flamer";
        break;
      case MOD_LCANNON:
        message = "felt the full force of";
        message2 = "'s lucifer cannon";
        break;
      case MOD_LCANNON_SPLASH:
        message = "was caught in the fallout of";
        message2 = "'s lucifer cannon";
        break;
      case MOD_GRENADE:
        message = "couldn't escape";
        message2 = "'s grenade";
        break;

      case MOD_MINE://ROTACAkfun
        message = "found";
        message2 = "'s mine";
        break;        
      case MOD_FLARE:
        message = "tasted";
        message2 = "'s light";
        break;

      case MOD_ABUILDER_CLAW:
        message = "should leave";
        message2 = "'s buildings alone";
        break;
      case MOD_LEVEL0_BITE:
        message = "was bitten by";
        break;
      case MOD_LEVEL1_CLAW:
        message = "was swiped by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL2_CLAW:
        message = "was clawed by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL2_ZAP:
        message = "was zapped by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL3_CLAW:
        message = "was chomped by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL3_POUNCE:
        message = "was pounced upon by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL3_BOUNCEBALL:
        message = "was sniped by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL4_CLAW:
        message = "was mauled by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL4_TRAMPLE:
        message = "should have gotten out of the way of";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
        message2 = className;
        break;
      case MOD_LEVEL4_CRUSH:
        message = "was crushed under";
        message2 = "'s weight";
        break;

      case MOD_POISON:
        message = "should have used a medkit against";
        message2 = "'s poison";
        break;
      case MOD_LEVEL1_PCLOUD:
        message = "was gassed by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
        message2 = className;
        break;


      case MOD_TELEFRAG:
        message = "tried to invade";
        message2 = "'s personal space";
        break;
        
      case MOD_ABOMB://ROTAXfun
        message = "was bombed by";
        Com_sprintf( className, 64, "'s %s",
            BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
        message2 = className;
        break;
        
      default:
        message = "was killed by";
        break;
    }

    if( message )
    {
      CG_Printf( "%s" S_COLOR_WHITE " %s %s%s" S_COLOR_WHITE "%s\n",
        targetName, message,
        ( teamKill ) ? S_COLOR_RED "TEAMMATE " S_COLOR_WHITE : "",
        attackerName, message2 );
      if( teamKill && attacker == cg.clientNum )
      {
        CG_CenterPrint( va ( "You killed " S_COLOR_RED "TEAMMATE "
          S_COLOR_WHITE "%s", targetName ),
          SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
      }
      return;
    }
  }

  // we don't know what it was
  CG_Printf( "%s" S_COLOR_WHITE " died\n", targetName );
}
예제 #6
0
/*
=============
CG_Obituary
=============
*/
static void CG_Obituary( entityState_t *ent )
{
	int          mod;
	int          target, attacker;
	int          attackerClass = -1;
	const char   *message;
	const char   *targetInfo;
	const char   *attackerInfo;
	char         targetName[ MAX_NAME_LENGTH ];
	char         attackerName[ MAX_NAME_LENGTH ];
	gender_t     gender;
	clientInfo_t *ci;
	qboolean     teamKill = qfalse;

	target = ent->otherEntityNum;
	attacker = ent->otherEntityNum2;
	mod = ent->eventParm;

	if ( target < 0 || target >= MAX_CLIENTS )
	{
		CG_Error( "CG_Obituary: target out of range" );
	}

	ci = &cgs.clientinfo[ target ];
	gender = ci->gender;

	if ( attacker < 0 || attacker >= MAX_CLIENTS )
	{
		attacker = ENTITYNUM_WORLD;
		attackerInfo = NULL;
	}
	else
	{
		attackerInfo = CG_ConfigString( CS_PLAYERS + attacker );

		if ( ci && cgs.clientinfo[ attacker ].team == ci->team )
		{
			teamKill = qtrue;
		}
	}

	targetInfo = CG_ConfigString( CS_PLAYERS + target );

	if ( !targetInfo )
	{
		return;
	}

	Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ) );

	// check for single client messages

	switch ( mod )
	{
		case MOD_FALLING:
			message = G_( "%s ^7fell foul to gravity\n" );
			break;

		case MOD_CRUSH:
			message = G_( "%s ^7was squished\n" );
			break;

		case MOD_WATER:
			message = G_( "%s ^7forgot to pack a snorkel\n" );
			break;

		case MOD_SLIME:
			message = G_( "%s ^7melted\n" );
			break;

		case MOD_LAVA:
			message = G_( "%s ^7did a back flip into the lava\n" );
			break;

		case MOD_TARGET_LASER:
			message = G_( "%s ^7saw the light\n" );
			break;

		case MOD_TRIGGER_HURT:
			message = G_( "%s ^7was in the wrong place\n" );
			break;

		case MOD_HSPAWN:
			message = G_( "%s ^7should have run further\n" );
			break;

		case MOD_ASPAWN:
			message = G_( "%s ^7shouldn't have trod in the acid\n" );
			break;

		case MOD_MGTURRET:
			message = G_( "%s ^7was gunned down by a turret\n" );
			break;

		case MOD_TESLAGEN:
			message = G_( "%s ^7was zapped by a tesla generator\n" );
			break;

		case MOD_ATUBE:
			message = G_( "%s ^7was melted by an acid tube\n" );
			break;

		case MOD_OVERMIND:
			message = G_( "%s ^7got too close to the overmind\n" );
			break;

		case MOD_REACTOR:
			message = G_( "%s ^7got too close to the reactor\n" );
			break;

		case MOD_SLOWBLOB:
			message = G_( "%s ^7should have visited a medical station\n" );
			break;

		case MOD_SWARM:
			message = G_( "%s ^7was hunted down by the swarm\n" );
			break;

		default:
			message = NULL;
			break;
	}

	if ( !message && attacker == target )
	{
		switch ( mod )
		{
			case MOD_FLAMER_SPLASH:
				message = G_( "%s ^7toasted self\n" );
				break;

			case MOD_LCANNON_SPLASH:
				message = G_( "%s ^7irradiated self\n" );
				break;

			case MOD_GRENADE:
				message = G_( "%s ^7blew self up\n" );
				break;

			case MOD_LEVEL3_BOUNCEBALL:
				message = G_( "%s ^7sniped self\n" );
				break;

			case MOD_PRIFLE:
				message = G_( "%s ^7pulse rifled self\n" );
				break;

			default:
				message = G_( "%s ^7killed self\n" );
				break;
		}
	}

	if ( message )
	{
		CG_Printf( message, targetName );
		return;
	}

	// check for double client messages
	if ( !attackerInfo )
	{
		attacker = ENTITYNUM_WORLD;
		strcpy( attackerName, "noname" );
	}
	else
	{
		Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ) );

		// check for kill messages about the current clientNum
		if ( target == cg.snap->ps.clientNum )
		{
			Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) );
		}
	}

	if ( attacker != ENTITYNUM_WORLD )
	{
		switch ( mod )
		{
			case MOD_PAINSAW:
				message = G_( "%s ^7was sawn by %s%s\n" );
				break;

			case MOD_BLASTER:
				message = G_( "%s ^7was blasted by %s%s\n" );
				break;

			case MOD_MACHINEGUN:
				message = G_( "%s ^7was machinegunned by %s%s\n" );
				break;

			case MOD_CHAINGUN:
				message = G_( "%s ^7was chaingunned by %s%s\n" );
				break;

			case MOD_SHOTGUN:
				message = G_( "%s ^7was gunned down by %s%s\n" );
				break;

			case MOD_PRIFLE:
				message = G_( "%s ^7was pulse rifled by %s%s\n" );
				break;

			case MOD_MDRIVER:
				message = G_( "%s ^7was mass driven by %s%s\n" );
				break;

			case MOD_LASGUN:
				message = G_( "%s ^7was lasgunned by %s%s\n" );
				break;

			case MOD_FLAMER:
				message = G_( "%s ^7was grilled by %s%s^7's flamer\n" );
				break;

			case MOD_FLAMER_SPLASH:
				message = G_( "%s ^7was toasted by %s%s^7's flamer\n" );
				break;

			case MOD_LCANNON:
				message = G_( "%s ^7felt the full force of %s%s^7's lucifer cannon\n" );
				break;

			case MOD_LCANNON_SPLASH:
				message = G_( "%s ^7was caught in the fallout of %s%s^7's lucifer cannon\n" );
				break;

			case MOD_GRENADE:
				message = G_( "%s ^7couldn't escape %s%s^7's grenade\n" );
				break;

			case MOD_ABUILDER_CLAW:
				message = G_( "%s ^7should leave %s%s^7's buildings alone\n" );
				break;

			case MOD_LEVEL0_BITE:
				message = G_( "%s ^7was bitten by %s%s\n" );
				break;

			case MOD_LEVEL1_CLAW:
				message = G_( "%s ^7was swiped by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL1;
				break;

			case MOD_LEVEL2_CLAW:
				message = G_( "%s ^7was clawed by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL2;
				break;

			case MOD_LEVEL2_ZAP:
				message = G_( "%s ^7was zapped by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL2;
				break;

			case MOD_LEVEL3_CLAW:
				message = G_( "%s ^7was chomped by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL3;
				break;

			case MOD_LEVEL3_POUNCE:
				message = G_( "%s ^7was pounced upon by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL3;
				break;

			case MOD_LEVEL3_BOUNCEBALL:
				message = G_( "%s ^7was sniped by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL3;
				break;

			case MOD_LEVEL4_CLAW:
				message = G_( "%s ^7was mauled by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL4;
				break;

			case MOD_LEVEL4_TRAMPLE:
				message = G_( "%s ^7should have gotten out of the way of %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL4;
				break;

			case MOD_LEVEL4_CRUSH:
				message = G_( "%s ^7was crushed under %s%s^7's weight\n" );
				break;

			case MOD_POISON:
				message = G_( "%s ^7should have used a medkit against %s%s^7's poison\n" );
				break;

			case MOD_LEVEL1_PCLOUD:
				message = G_( "%s ^7was gassed by %s%s^7's %s\n" );
				attackerClass = PCL_ALIEN_LEVEL1;
				break;

			case MOD_TELEFRAG:
				message = G_( "%s ^7tried to invade %s%s^7's personal space\n" );
				break;

			default:
				message = G_( "%s ^7was killed by %s%s\n" );
				break;
		}

		if ( message )
		{
			// Argument order: victim, "TEAMMATE" (if appropriate), attacker, alien class
			CG_Printf( message,
			           targetName,
			           ( teamKill ) ? _("^1TEAMMATE^7 ") : "",
			           attackerName,
			           ( attackerClass != -1 ) ? BG_ClassConfig( attackerClass )->humanName : NULL );

			if ( teamKill && attacker == cg.clientNum )
			{
				CG_CenterPrint( va( _("You killed ^1TEAMMATE^7 %s"), targetName ),
				                SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
			}

			return;
		}
	}

	// we don't know what it was
	CG_Printf( G_( "%s^7 died\n" ), targetName );
}