277 lines
7.2 KiB
C
277 lines
7.2 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
Copyright (C) 2000-2006 Tim Angus
|
|
|
|
This file is part of Tremulous.
|
|
|
|
Tremulous is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Tremulous is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Tremulous; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "g_local.h"
|
|
|
|
// NULL for everyone
|
|
void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... )
|
|
{
|
|
char msg[ 1024 ];
|
|
va_list argptr;
|
|
char *p;
|
|
|
|
va_start( argptr,fmt );
|
|
|
|
if( vsprintf( msg, fmt, argptr ) > sizeof( msg ) )
|
|
G_Error ( "PrintMsg overrun" );
|
|
|
|
va_end( argptr );
|
|
|
|
// double quotes are bad
|
|
while( ( p = strchr( msg, '"' ) ) != NULL )
|
|
*p = '\'';
|
|
|
|
trap_SendServerCommand( ( ( ent == NULL ) ? -1 : ent-g_entities ), va( "print \"%s\"", msg ) );
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
OnSameTeam
|
|
==============
|
|
*/
|
|
qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 )
|
|
{
|
|
if( !ent1->client || !ent2->client )
|
|
return qfalse;
|
|
|
|
if( ent1->client->pers.teamSelection == ent2->client->pers.teamSelection )
|
|
return qtrue;
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
Team_GetLocation
|
|
|
|
Report a location for the player. Uses placed nearby target_location entities
|
|
============
|
|
*/
|
|
gentity_t *Team_GetLocation( gentity_t *ent )
|
|
{
|
|
gentity_t *eloc, *best;
|
|
float bestlen, len;
|
|
vec3_t origin;
|
|
|
|
best = NULL;
|
|
bestlen = 3.0f * 8192.0f * 8192.0f;
|
|
|
|
VectorCopy( ent->r.currentOrigin, origin );
|
|
|
|
for( eloc = level.locationHead; eloc; eloc = eloc->nextTrain )
|
|
{
|
|
len = ( origin[ 0 ] - eloc->r.currentOrigin[ 0 ] ) * ( origin[ 0 ] - eloc->r.currentOrigin[ 0 ] )
|
|
+ ( origin[ 1 ] - eloc->r.currentOrigin[ 1 ] ) * ( origin[ 1 ] - eloc->r.currentOrigin[ 1 ] )
|
|
+ ( origin[ 2 ] - eloc->r.currentOrigin[ 2 ] ) * ( origin[ 2 ] - eloc->r.currentOrigin[ 2 ] );
|
|
|
|
if( len > bestlen )
|
|
continue;
|
|
|
|
if( !trap_InPVS( origin, eloc->r.currentOrigin ) )
|
|
continue;
|
|
|
|
bestlen = len;
|
|
best = eloc;
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
Team_GetLocationMsg
|
|
|
|
Report a location message for the player. Uses placed nearby target_location entities
|
|
============
|
|
*/
|
|
qboolean Team_GetLocationMsg( gentity_t *ent, char *loc, int loclen )
|
|
{
|
|
gentity_t *best;
|
|
|
|
best = Team_GetLocation( ent );
|
|
|
|
if( !best )
|
|
return qfalse;
|
|
|
|
if( best->count )
|
|
{
|
|
if( best->count < 0 )
|
|
best->count = 0;
|
|
|
|
if( best->count > 7 )
|
|
best->count = 7;
|
|
|
|
Com_sprintf( loc, loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message );
|
|
}
|
|
else
|
|
Com_sprintf( loc, loclen, "%s", best->message );
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int QDECL SortClients( const void *a, const void *b )
|
|
{
|
|
return *(int *)a - *(int *)b;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
TeamplayLocationsMessage
|
|
|
|
Format:
|
|
clientNum location health armor weapon powerups
|
|
|
|
==================
|
|
*/
|
|
void TeamplayInfoMessage( gentity_t *ent )
|
|
{
|
|
char entry[ 1024 ];
|
|
char string[ 8192 ];
|
|
int stringlength;
|
|
int i, j;
|
|
gentity_t *player;
|
|
int cnt;
|
|
int h, a = 0;
|
|
int clients[ TEAM_MAXOVERLAY ];
|
|
|
|
if( ! ent->client->pers.teamInfo )
|
|
return;
|
|
|
|
// figure out what client should be on the display
|
|
// we are limited to 8, but we want to use the top eight players
|
|
// but in client order (so they don't keep changing position on the overlay)
|
|
for( i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++ )
|
|
{
|
|
player = g_entities + level.sortedClients[ i ];
|
|
|
|
if( player->inuse && player->client->sess.sessionTeam ==
|
|
ent->client->sess.sessionTeam )
|
|
clients[ cnt++ ] = level.sortedClients[ i ];
|
|
}
|
|
|
|
// We have the top eight players, sort them by clientNum
|
|
qsort( clients, cnt, sizeof( clients[ 0 ] ), SortClients );
|
|
|
|
// send the latest information on all clients
|
|
string[ 0 ] = 0;
|
|
stringlength = 0;
|
|
|
|
for( i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++)
|
|
{
|
|
player = g_entities + i;
|
|
|
|
if( player->inuse && player->client->sess.sessionTeam ==
|
|
ent->client->sess.sessionTeam )
|
|
{
|
|
h = player->client->ps.stats[ STAT_HEALTH ];
|
|
|
|
if( h < 0 )
|
|
h = 0;
|
|
|
|
Com_sprintf( entry, sizeof( entry ),
|
|
" %i %i %i %i %i %i",
|
|
// level.sortedClients[i], player->client->pers.teamState.location, h, a,
|
|
i, player->client->pers.teamState.location, h, a,
|
|
player->client->ps.weapon, player->s.powerups );
|
|
|
|
j = strlen( entry );
|
|
|
|
if( stringlength + j > sizeof( string ) )
|
|
break;
|
|
|
|
strcpy( string + stringlength, entry );
|
|
stringlength += j;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
trap_SendServerCommand( ent - g_entities, va( "tinfo %i %s", cnt, string ) );
|
|
}
|
|
|
|
void CheckTeamStatus( void )
|
|
{
|
|
int i;
|
|
gentity_t *loc, *ent;
|
|
|
|
if( level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME )
|
|
{
|
|
level.lastTeamLocationTime = level.time;
|
|
|
|
for( i = 0; i < g_maxclients.integer; i++ )
|
|
{
|
|
ent = g_entities + i;
|
|
if( ent->client->pers.connected != CON_CONNECTED )
|
|
continue;
|
|
|
|
if( ent->inuse && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ||
|
|
ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) )
|
|
{
|
|
|
|
loc = Team_GetLocation( ent );
|
|
|
|
if( loc )
|
|
ent->client->pers.teamState.location = loc->health;
|
|
else
|
|
ent->client->pers.teamState.location = 0;
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < g_maxclients.integer; i++ )
|
|
{
|
|
ent = g_entities + i;
|
|
if( ent->client->pers.connected != CON_CONNECTED )
|
|
continue;
|
|
|
|
if( ent->inuse && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ||
|
|
ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) )
|
|
TeamplayInfoMessage( ent );
|
|
}
|
|
}
|
|
|
|
//Warn on unbalanced teams
|
|
if ( g_teamImbalanceWarnings.integer && !level.intermissiontime && level.time - level.lastTeamUnbalancedTime > ( g_teamImbalanceWarnings.integer * 1000 ) && level.numTeamWarnings<3 )
|
|
{
|
|
level.lastTeamUnbalancedTime = level.time;
|
|
if (level.numAlienSpawns > 0 && level.numHumanClients - level.numAlienClients > 2)
|
|
{
|
|
trap_SendServerCommand (-1, "print \"Teams are unbalanced. Humans have more players.\n Humans will keep their points when switching teams.\n\"");
|
|
level.numTeamWarnings++;
|
|
}
|
|
else if (level.numHumanSpawns > 0 && level.numAlienClients - level.numHumanClients > 2)
|
|
{
|
|
trap_SendServerCommand (-1, "print \"Teams are unbalanced. Aliens have more players.\n Aliens will keep their points when switching teams.\n\"");
|
|
level.numTeamWarnings++;
|
|
}
|
|
else
|
|
{
|
|
level.numTeamWarnings = 0;
|
|
}
|
|
}
|
|
}
|