2012-08-04 10:54:37 +00:00
// Copyright (C) 1999-2000 Id Software, Inc.
//
2013-07-06 16:19:28 +00:00
# include "g_client.h"
2012-08-04 10:54:37 +00:00
# include "g_local.h"
# include "g_groups.h"
2013-05-16 21:54:40 +00:00
# include "g_main.h"
2013-05-16 22:42:09 +00:00
# include "g_cmds.h"
2013-05-24 21:08:55 +00:00
# include "g_main.h"
2013-07-08 19:00:23 +00:00
# include "g_spawn.h"
2013-10-12 22:50:21 +00:00
# include "g_items.h"
2012-08-04 10:54:37 +00:00
reconData_t g_reconData [ MAX_RECON_NAMES ] ; //!< recon data for a limited ammount of clients
2013-07-06 16:19:28 +00:00
int32_t g_reconNum ;
2012-08-04 10:54:37 +00:00
// g_client.c -- client functions that don't happen every frame
2012-01-22 21:34:33 +00:00
2012-11-15 23:58:56 +00:00
void G_Client_StoreClientInitialStatus ( gentity_t * ent ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
//! players mins
static vec3_t playerMins = { - 12 , - 12 , - 24 } ; //RPG-X : TiM - {-15, -15, -24}
//! players maxs
static vec3_t playerMaxs = { 12 , 12 , 32 } ; // {15, 15, 32}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
clInitStatus_t clientInitialStatus [ MAX_CLIENTS ] ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
//TiM: For easier transport setup
/**
* Function that makes transport setup easier
* \ author Ubergames - TiM
2012-01-22 21:34:33 +00:00
*/
2013-07-06 16:19:28 +00:00
void G_InitTransport ( int32_t clientNum , vec3_t origin , vec3_t angles ) {
gentity_t * tent = NULL ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
TransDat [ clientNum ] . beamTime = level . time + 8000 ;
g_entities [ clientNum ] . client - > ps . powerups [ PW_BEAM_OUT ] = level . time + 8000 ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
//Transfer stored data to active beamer
2013-07-06 16:19:28 +00:00
VectorCopy ( origin , TransDat [ clientNum ] . currentCoord . origin ) ;
VectorCopy ( angles , TransDat [ clientNum ] . currentCoord . angles ) ;
2012-08-04 10:54:37 +00:00
tent = G_TempEntity ( g_entities [ clientNum ] . client - > ps . origin , EV_PLAYER_TRANSPORT_OUT ) ;
tent - > s . clientNum = clientNum ;
}
2012-01-22 21:34:33 +00:00
2012-11-15 05:41:12 +00:00
/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) INITIAL
- - - - - DESCRIPTION - - - - -
2012-01-22 21:34:33 +00:00
potential spawning position for deathmatch games .
2012-11-15 05:41:12 +00:00
- - - - - SPAWNFLAGS - - - - -
2012-11-27 02:04:09 +00:00
1 : INITIAL - Preferred spawn for the first spawn of a client when entering a match .
2012-11-15 05:41:12 +00:00
- - - - - KEYS - - - - -
" target " - entities with matching targetname will be fired if someone spawns here .
" nobots " - if 1 will prevent bots from using this spot .
" nohumans " - if 1 will prevent non - bots from using this spot .
2012-01-22 21:34:33 +00:00
*/
2012-08-04 10:54:37 +00:00
/**
* Spawn function for deathmatch spawnpoint
*/
2012-01-22 21:34:33 +00:00
void SP_info_player_deathmatch ( gentity_t * ent ) {
2013-07-06 16:19:28 +00:00
int32_t i = 0 ;
2012-01-22 21:34:33 +00:00
2013-07-20 23:34:09 +00:00
ent - > type = ENT_INFO_PLAYER_START ;
2013-07-06 16:19:28 +00:00
if ( strcmp ( ent - > classname , " info_player_deathmatch " ) ! = 0 ) {
2013-07-21 13:24:57 +00:00
ent - > classname = " info_player_deathmatch " ;
2012-11-15 22:43:56 +00:00
}
2012-01-22 21:34:33 +00:00
G_SpawnInt ( " nobots " , " 0 " , & i ) ;
2013-07-06 16:19:28 +00:00
if ( i ! = 0 ) {
2012-01-22 21:34:33 +00:00
ent - > flags | = FL_NO_BOTS ;
}
G_SpawnInt ( " nohumans " , " 0 " , & i ) ;
2013-07-06 16:19:28 +00:00
if ( i ! = 0 ) {
2012-01-22 21:34:33 +00:00
ent - > flags | = FL_NO_HUMANS ;
}
2012-08-04 10:54:37 +00:00
trap_LinkEntity ( ent ) ;
2012-01-22 21:34:33 +00:00
}
/*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
2012-11-15 05:41:12 +00:00
- - - - - DESCRIPTION - - - - -
The intermission will be viewed from this point .
It is also used to spawn spectators .
Target an info_notnull or similar for the view direction .
- - - - - SPAWNFLAGS - - - - -
none
- - - - - KEYS - - - - -
none
2012-01-22 21:34:33 +00:00
*/
2012-08-04 10:54:37 +00:00
/**
* Spawn function for intermission entity .
*/
2013-07-20 23:34:09 +00:00
void SP_info_player_intermission ( gentity_t * ent ) {
ent - > type = ENT_INFO_PLAYER_INTERMISSION ;
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
/**
2013-07-06 16:19:28 +00:00
* Determine whether spot would telefrag .
*
* \ param spot Spot to check .
* \ return Whether this spot would telefrag .
*/
static qboolean G_Client_SpotWouldTelefrag ( gentity_t * spot ) {
int32_t i = 0 ;
int32_t num = 0 ;
2012-01-22 21:34:33 +00:00
int touch [ MAX_GENTITIES ] ;
2013-07-06 16:19:28 +00:00
gentity_t * hit = NULL ;
vec3_t mins ;
vec3_t maxs ;
memset ( touch , 0 , sizeof ( touch ) ) ;
2012-01-22 21:34:33 +00:00
VectorAdd ( spot - > s . origin , playerMins , mins ) ;
VectorAdd ( spot - > s . origin , playerMaxs , maxs ) ;
num = trap_EntitiesInBox ( mins , maxs , touch , MAX_GENTITIES ) ;
2013-07-06 16:19:28 +00:00
for ( ; i < num ; i + + ) {
2012-01-22 21:34:33 +00:00
hit = & g_entities [ touch [ i ] ] ;
2012-08-04 10:54:37 +00:00
if ( hit & & hit - > client & & hit - > client - > ps . stats [ STAT_HEALTH ] > 0 ) {
return qtrue ;
}
if ( hit & & hit - > s . eType = = ET_USEABLE & & hit - > s . modelindex = = HI_SHIELD ) { //hit a portable force field
2012-01-22 21:34:33 +00:00
return qtrue ;
}
2012-08-04 10:54:37 +00:00
2012-01-22 21:34:33 +00:00
}
return qfalse ;
}
/*
= = = = = = = = = = = = = = = =
SelectNearestDeathmatchSpawnPoint
Find the spot that we DON ' T want to use
= = = = = = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
# define MAX_SPAWN_POINTS 256
/**
* Find the spot that we DON ' T want to use
*/
2013-07-06 16:19:28 +00:00
static gentity_t * SelectNearestDeathmatchSpawnPoint ( vec3_t from ) {
gentity_t * spot = NULL ;
2012-01-22 21:34:33 +00:00
vec3_t delta ;
2013-07-06 16:19:28 +00:00
double dist = 0.0 ;
double nearestDist = 999999.0 ;
gentity_t * nearestSpot = NULL ;
2012-01-22 21:34:33 +00:00
while ( ( spot = G_Find ( spot , FOFS ( classname ) , " info_player_deathmatch " ) ) ! = NULL ) {
VectorSubtract ( spot - > s . origin , from , delta ) ;
dist = VectorLength ( delta ) ;
if ( dist < nearestDist ) {
nearestDist = dist ;
nearestSpot = spot ;
}
}
return nearestSpot ;
}
/*
= = = = = = = = = = = = = = = =
SelectRandomDeathmatchSpawnPoint
go to a random point that doesn ' t telefrag
= = = = = = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
# define MAX_SPAWN_POINTS 256
/**
* go to a random point that doesn ' t telefrag
*/
2013-07-06 16:19:28 +00:00
static gentity_t * SelectRandomDeathmatchSpawnPoint ( void ) {
gentity_t * spot = NULL ;
int32_t count = 0 ;
int32_t selection = 0 ;
gentity_t * spots [ MAX_SPAWN_POINTS ] ;
2012-01-22 21:34:33 +00:00
2013-07-06 16:19:28 +00:00
memset ( spots , 0 , sizeof ( spots ) ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
while ( ( spot = G_Find ( spot , FOFS ( classname ) , " info_player_deathmatch " ) ) ! = NULL ) {
2012-11-15 23:58:56 +00:00
if ( G_Client_SpotWouldTelefrag ( spot ) ) {
2012-01-22 21:34:33 +00:00
continue ;
}
2012-08-04 10:54:37 +00:00
spots [ count ] = spot ;
2012-01-22 21:34:33 +00:00
count + + ;
}
2013-07-06 16:19:28 +00:00
if ( count = = 0 ) { // no spots that won't telefrag
2012-01-22 21:34:33 +00:00
return G_Find ( NULL , FOFS ( classname ) , " info_player_deathmatch " ) ;
}
selection = rand ( ) % count ;
return spots [ selection ] ;
}
2013-05-21 19:08:36 +00:00
gentity_t * G_Client_SelectSpawnPoint ( vec3_t avoidPoint , vec3_t origin , vec3_t angles ) {
2013-07-06 16:19:28 +00:00
gentity_t * spot = NULL ;
gentity_t * nearestSpot = SelectNearestDeathmatchSpawnPoint ( avoidPoint ) ;
2012-01-22 21:34:33 +00:00
spot = SelectRandomDeathmatchSpawnPoint ( ) ;
if ( spot = = nearestSpot ) {
// roll again if it would be real close to point of death
spot = SelectRandomDeathmatchSpawnPoint ( ) ;
if ( spot = = nearestSpot ) {
// last try
spot = SelectRandomDeathmatchSpawnPoint ( ) ;
2012-08-04 10:54:37 +00:00
}
2012-01-22 21:34:33 +00:00
}
// find a single player start spot
2013-07-06 16:19:28 +00:00
if ( spot = = NULL ) {
2012-01-22 21:34:33 +00:00
G_Error ( " Couldn't find a spawn point " ) ;
2012-08-04 10:54:37 +00:00
return spot ;
2012-01-22 21:34:33 +00:00
}
VectorCopy ( spot - > s . origin , origin ) ;
origin [ 2 ] + = 9 ;
VectorCopy ( spot - > s . angles , angles ) ;
return spot ;
}
/*
= = = = = = = = = = =
SelectInitialSpawnPoint
Try to find a spawn point marked ' initial ' , otherwise
use normal spawn selection .
= = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
/**
* Try to find a spawn point marked ' initial ' , otherwise
* use normal spawn selection .
*/
2013-07-06 16:19:28 +00:00
static gentity_t * SelectInitialSpawnPoint ( vec3_t origin , vec3_t angles ) {
gentity_t * spot = NULL ;
2012-01-22 21:34:33 +00:00
spot = NULL ;
2012-08-04 10:54:37 +00:00
while ( ( spot = G_Find ( spot , FOFS ( classname ) , " info_player_deathmatch " ) ) ! = NULL ) {
if ( spot - > spawnflags & 1 ) {
2012-01-22 21:34:33 +00:00
break ;
2012-08-04 10:54:37 +00:00
}
2012-01-22 21:34:33 +00:00
}
2013-07-06 16:19:28 +00:00
if ( spot = = NULL | | G_Client_SpotWouldTelefrag ( spot ) ) {
2012-11-15 23:58:56 +00:00
return G_Client_SelectSpawnPoint ( vec3_origin , origin , angles ) ;
2012-08-04 10:54:37 +00:00
}
2012-01-22 21:34:33 +00:00
VectorCopy ( spot - > s . origin , origin ) ;
origin [ 2 ] + = 9 ;
VectorCopy ( spot - > s . angles , angles ) ;
return spot ;
}
/*
= = = = = = = = = = =
SelectSpectatorSpawnPoint
= = = = = = = = = = = =
*/
2013-07-06 16:19:28 +00:00
static gentity_t * SelectSpectatorSpawnPoint ( vec3_t origin , vec3_t angles ) {
2012-01-22 21:34:33 +00:00
FindIntermissionPoint ( ) ;
VectorCopy ( level . intermission_origin , origin ) ;
VectorCopy ( level . intermission_angle , angles ) ;
return NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
BODYQUE
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2013-07-06 16:19:28 +00:00
static int32_t bodyFadeSound = 0 ;
2012-08-04 10:54:37 +00:00
2012-01-22 21:34:33 +00:00
/*
= = = = = = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_InitBodyQue
2012-01-22 21:34:33 +00:00
= = = = = = = = = = = = = = =
*/
2012-11-15 23:58:56 +00:00
void G_Client_InitBodyQue ( void ) {
2013-07-06 16:19:28 +00:00
int32_t i = 0 ;
gentity_t * ent = NULL ;
2012-01-22 21:34:33 +00:00
level . bodyQueIndex = 0 ;
2013-07-06 16:19:28 +00:00
for ( ; i < BODY_QUEUE_SIZE ; i + + ) {
2012-01-22 21:34:33 +00:00
ent = G_Spawn ( ) ;
2013-07-06 16:19:28 +00:00
if ( ent = = NULL ) {
// TODO print error?
return ;
}
2012-01-22 21:34:33 +00:00
ent - > classname = " bodyque " ;
ent - > neverFree = qtrue ;
level . bodyQue [ i ] = ent ;
}
2012-08-04 10:54:37 +00:00
if ( bodyFadeSound = = 0 )
{ // Initialize this sound.
bodyFadeSound = G_SoundIndex ( " sound/enemies/borg/walkthroughfield.wav " ) ;
}
2012-01-22 21:34:33 +00:00
}
/*
= = = = = = = = = = = = =
2012-08-04 10:54:37 +00:00
BodyRezOut
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
After sitting around for five seconds , fade out .
2012-01-22 21:34:33 +00:00
= = = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
/**
* After sitting around for five seconds , fade out .
*/
2013-07-06 16:19:28 +00:00
static void BodyRezOut ( gentity_t * ent ) {
2012-08-04 10:54:37 +00:00
if ( level . time - ent - > timestamp > = 7500 ) {
2012-01-22 21:34:33 +00:00
// the body ques are never actually freed, they are just unlinked
trap_UnlinkEntity ( ent ) ;
ent - > physicsObject = qfalse ;
2012-08-04 10:54:37 +00:00
return ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
ent - > nextthink = level . time + 2500 ;
ent - > s . time = level . time + 2500 ;
G_AddEvent ( ent , EV_GENERAL_SOUND , bodyFadeSound ) ;
2012-01-22 21:34:33 +00:00
}
/*
= = = = = = = = = = = = =
CopyToBodyQue
A player is respawning , so make an entity that looks
just like the existing corpse to leave behind .
= = = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
/**
* A player is respawning , so make an entity that looks
* just like the existing corpse to leave behind .
*/
2012-11-11 17:33:50 +00:00
static void CopyToBodyQue ( gentity_t * ent ) {
2013-07-06 16:19:28 +00:00
gentity_t * body = NULL ;
int32_t contents = 0 ;
entityState_t * eState = NULL ;
2012-01-22 21:34:33 +00:00
trap_UnlinkEntity ( ent ) ;
// if client is in a nodrop area, don't leave the body
contents = trap_PointContents ( ent - > s . origin , - 1 ) ;
2013-07-06 16:19:28 +00:00
if ( ( contents & CONTENTS_NODROP ) ! = 0 ) {
2012-08-04 10:54:37 +00:00
ent - > s . eFlags & = ~ EF_NODRAW ; // Just in case we died from a bottomless pit, reset EF_NODRAW
2012-01-22 21:34:33 +00:00
return ;
}
// grab a body que and cycle to the next one
body = level . bodyQue [ level . bodyQueIndex ] ;
level . bodyQueIndex = ( level . bodyQueIndex + 1 ) % BODY_QUEUE_SIZE ;
2012-08-04 10:54:37 +00:00
trap_UnlinkEntity ( body ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
eState = & ent - > s ;
eState - > eFlags = EF_DEAD ; // clear EF_TALK, etc
eState - > powerups = 0 ; // clear powerups
eState - > loopSound = 0 ; // clear lava burning
eState - > number = body - g_entities ;
2012-01-22 21:34:33 +00:00
body - > timestamp = level . time ;
body - > physicsObject = qtrue ;
body - > physicsBounce = 0 ; // don't bounce
2012-08-04 10:54:37 +00:00
if ( eState - > groundEntityNum = = ENTITYNUM_NONE ) {
eState - > pos . trType = TR_GRAVITY ;
eState - > pos . trTime = level . time ;
VectorCopy ( ent - > client - > ps . velocity , eState - > pos . trDelta ) ;
2012-01-22 21:34:33 +00:00
} else {
2012-08-04 10:54:37 +00:00
eState - > pos . trType = TR_STATIONARY ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
eState - > event = 0 ;
2012-01-22 21:34:33 +00:00
// change the animation to the last-frame only, so the sequence
// doesn't repeat anew for the body
2012-08-04 10:54:37 +00:00
switch ( eState - > legsAnim & ~ ANIM_TOGGLEBIT ) {
2012-01-22 21:34:33 +00:00
case BOTH_DEATH1 :
case BOTH_DEAD1 :
2012-08-04 10:54:37 +00:00
eState - > torsoAnim = eState - > legsAnim = BOTH_DEAD1 ;
2012-01-22 21:34:33 +00:00
break ;
case BOTH_DEATH2 :
case BOTH_DEAD2 :
2012-08-04 10:54:37 +00:00
eState - > torsoAnim = eState - > legsAnim = BOTH_DEAD2 ;
2012-01-22 21:34:33 +00:00
break ;
default :
2012-08-04 10:54:37 +00:00
eState - > torsoAnim = eState - > legsAnim = BOTH_DEAD1 ; //DEAD3
2012-01-22 21:34:33 +00:00
break ;
}
body - > r . svFlags = ent - > r . svFlags ;
VectorCopy ( ent - > r . mins , body - > r . mins ) ;
VectorCopy ( ent - > r . maxs , body - > r . maxs ) ;
VectorCopy ( ent - > r . absmin , body - > r . absmin ) ;
VectorCopy ( ent - > r . absmax , body - > r . absmax ) ;
body - > clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP ;
body - > r . contents = CONTENTS_CORPSE ;
2012-08-04 10:54:37 +00:00
body - > r . ownerNum = ent - > r . ownerNum ;
2012-01-22 21:34:33 +00:00
body - > nextthink = level . time + 5000 ;
2012-08-04 10:54:37 +00:00
body - > think = BodyRezOut ;
2012-01-22 21:34:33 +00:00
body - > die = body_die ;
2012-08-04 10:54:37 +00:00
// if there shouldn't be a body, don't show one.
2013-07-06 16:19:28 +00:00
if ( ent - > client ! = NULL & &
2012-08-04 10:54:37 +00:00
( ( level . time - ent - > client - > ps . powerups [ PW_DISINTEGRATE ] ) < 10000 | |
( level . time - ent - > client - > ps . powerups [ PW_EXPLODE ] ) < 10000 ) )
{
eState - > eFlags | = EF_NODRAW ;
}
2012-01-22 21:34:33 +00:00
// don't take more damage if already gibbed
2012-08-04 10:54:37 +00:00
//RPG-X: RedTechie - Check for medicrevive
if ( rpg_medicsrevive . integer = = 0 ) {
if ( ent - > health < = GIB_HEALTH ) {
body - > takedamage = qfalse ;
} else {
body - > takedamage = qtrue ;
}
2013-07-06 16:19:28 +00:00
} else {
2012-01-22 21:34:33 +00:00
body - > takedamage = qfalse ;
}
2012-08-04 10:54:37 +00:00
VectorCopy ( eState - > pos . trBase , body - > r . currentOrigin ) ;
2012-01-22 21:34:33 +00:00
trap_LinkEntity ( body ) ;
}
//======================================================================
/*
= = = = = = = = = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_SetViewAngle
2012-01-22 21:34:33 +00:00
= = = = = = = = = = = = = = = = = =
*/
2012-11-15 23:58:56 +00:00
void G_Client_SetViewAngle ( gentity_t * ent , vec3_t angle ) {
2012-01-22 21:34:33 +00:00
int i ;
// set the delta angle
for ( i = 0 ; i < 3 ; i + + ) {
int cmdAngle ;
cmdAngle = ANGLE2SHORT ( angle [ i ] ) ;
ent - > client - > ps . delta_angles [ i ] = cmdAngle - ent - > client - > pers . cmd . angles [ i ] ;
}
VectorCopy ( angle , ent - > s . angles ) ;
VectorCopy ( ent - > s . angles , ent - > client - > ps . viewangles ) ;
}
/*
= = = = = = = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_Respawn
2012-01-22 21:34:33 +00:00
= = = = = = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
extern char * ClassNameForValue ( pclass_t pClass ) ;
2012-11-15 23:58:56 +00:00
void G_Client_Respawn ( gentity_t * ent ) {
2013-07-06 16:19:28 +00:00
qboolean borg = qfalse ;
gentity_t * tent = NULL ;
playerState_t * ps = NULL ;
2012-01-22 21:34:33 +00:00
CopyToBodyQue ( ent ) ;
2012-08-04 10:54:37 +00:00
2012-11-15 23:58:56 +00:00
G_Client_Spawn ( ent , 0 , qfalse ) ; //RPG-X: RedTechie - Modifyed
2012-08-04 10:54:37 +00:00
ps = & ent - > client - > ps ;
// add a teleportation effect
2013-07-06 16:19:28 +00:00
if ( borg ) {
2012-08-04 10:54:37 +00:00
tent = G_TempEntity ( ps - > origin , EV_BORG_TELEPORT ) ;
2013-07-06 16:19:28 +00:00
} else {
2012-08-04 10:54:37 +00:00
tent = G_TempEntity ( ps - > origin , EV_PLAYER_TRANSPORT_IN ) ;
ps - > powerups [ PW_QUAD ] = level . time + 4000 ;
}
tent - > s . clientNum = ent - > s . clientNum ;
2012-01-22 21:34:33 +00:00
}
2013-07-06 16:19:28 +00:00
/**
* Get number of clients in team .
*
* \ param ignoreClientNum Client to ignore .
* \ param team Team .
* \ reutrn Number of clients in team .
*/
static team_t G_Client_TeamCount ( int ignoreClientNum , int team ) {
2012-01-22 21:34:33 +00:00
int i ;
int count = 0 ;
for ( i = 0 ; i < level . maxclients ; i + + ) {
if ( i = = ignoreClientNum ) {
continue ;
}
if ( level . clients [ i ] . pers . connected = = CON_DISCONNECTED ) {
continue ;
}
if ( level . clients [ i ] . sess . sessionTeam = = team ) {
count + + ;
}
}
2012-11-11 17:33:50 +00:00
return ( team_t ) count ;
2012-01-22 21:34:33 +00:00
}
2012-11-15 23:58:56 +00:00
team_t G_Client_PickTeam ( int ignoreClientNum ) {
2012-01-22 21:34:33 +00:00
int counts [ TEAM_NUM_TEAMS ] ;
2012-11-15 23:58:56 +00:00
counts [ TEAM_BLUE ] = G_Client_TeamCount ( ignoreClientNum , TEAM_BLUE ) ;
counts [ TEAM_RED ] = G_Client_TeamCount ( ignoreClientNum , TEAM_RED ) ;
2012-01-22 21:34:33 +00:00
if ( counts [ TEAM_BLUE ] > counts [ TEAM_RED ] ) {
return TEAM_RED ;
}
if ( counts [ TEAM_RED ] > counts [ TEAM_BLUE ] ) {
return TEAM_BLUE ;
}
// equal team count, so join the team with the lowest score
if ( level . teamScores [ TEAM_BLUE ] > level . teamScores [ TEAM_RED ] ) {
return TEAM_RED ;
}
2012-08-04 10:54:37 +00:00
if ( level . teamScores [ TEAM_BLUE ] < level . teamScores [ TEAM_RED ] ) {
return TEAM_BLUE ;
}
2012-11-11 17:33:50 +00:00
return ( team_t ) irandom ( TEAM_RED , TEAM_BLUE ) ;
2012-01-22 21:34:33 +00:00
}
/*
= = = = = = = = = = =
ForceClientSkin
Forces a client ' s skin ( for teamplay )
2012-08-04 10:54:37 +00:00
HEAVILY modified for the RPG - X
Player Model system : P
2012-01-22 21:34:33 +00:00
= = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
/**
* Forces a client ' s skin ( for teamplay )
* HEAVILY modified for the RPG - X
* Player Model system
*/
2013-07-06 16:19:28 +00:00
static void ForceClientSkin ( char * model , const char * skin ) {
char * p = NULL ;
char * q = NULL ;
2012-08-04 10:54:37 +00:00
//we expect model to equal 'char/model/skin'
p = strchr ( model , ' / ' ) ;
//if no slashes at all
2013-07-06 16:19:28 +00:00
if ( p = = NULL | | p [ 0 ] = = 0 | | p [ 1 ] = = 0 ) {
2012-08-04 10:54:37 +00:00
//input everything
2013-07-06 16:19:28 +00:00
strncat ( model , " / " , MAX_QPATH ) ;
strncat ( model , " main " , MAX_QPATH ) ;
strncat ( model , " / " , MAX_QPATH ) ;
strncat ( model , skin , MAX_QPATH ) ;
} else { //ie we got a slash (which should be the first of two
2012-08-04 10:54:37 +00:00
p + + ;
q = strchr ( p , ' / ' ) ; //okay, we should get another one if one was already found
2013-07-06 16:19:28 +00:00
if ( q = = NULL | | q [ 0 ] = = 0 | | q [ 1 ] = = 0 )
2012-08-04 10:54:37 +00:00
{ //no slashes were found?? >.<
//okay, let's assume they specified the .model file, no skin
//so just add the skin to the end :P
2013-07-06 16:19:28 +00:00
strncat ( model , " / " , MAX_QPATH ) ;
strncat ( model , skin , MAX_QPATH ) ;
} else {
2012-08-04 10:54:37 +00:00
q + + ;
* q = ' \0 ' ;
2013-07-06 16:19:28 +00:00
strncat ( model , skin , MAX_QPATH ) ;
2012-08-04 10:54:37 +00:00
}
2012-01-22 21:34:33 +00:00
}
}
/*
= = = = = = = = = = =
ClientCheckName
= = = = = = = = = = = =
*/
2013-07-06 16:19:28 +00:00
static void ClientCleanName ( const char * in , char * out , int outSize ) {
int32_t len = 0 ;
int32_t colorlessLen = 0 ;
char ch = 0 ;
char * p = NULL ;
int32_t spaces = 0 ;
2012-08-04 10:54:37 +00:00
//save room for trailing null byte
outSize - - ;
p = out ;
* p = 0 ;
spaces = 0 ;
while ( 1 ) {
ch = * in + + ;
2013-07-06 16:19:28 +00:00
if ( ch = = 0 ) {
2012-08-04 10:54:37 +00:00
break ;
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// don't allow leading spaces
if ( ! * p & & ch = = ' ' ) {
continue ;
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// check colors
if ( ch = = Q_COLOR_ESCAPE ) {
// solo trailing carat is not a color prefix
if ( ! * in ) {
break ;
}
// don't allow black in a name, period
if ( ColorIndex ( * in ) = = 0 ) {
in + + ;
2012-01-22 21:34:33 +00:00
continue ;
}
2012-08-04 10:54:37 +00:00
// make sure room in dest for both chars
if ( len > outSize - 2 ) {
break ;
}
* out + + = ch ;
* out + + = * in + + ;
len + = 2 ;
continue ;
}
// don't allow too many consecutive spaces
if ( ch = = ' ' ) {
spaces + + ;
if ( spaces > 3 ) {
continue ;
2012-01-22 21:34:33 +00:00
}
}
2012-08-04 10:54:37 +00:00
else {
2012-01-22 21:34:33 +00:00
spaces = 0 ;
}
2012-08-04 10:54:37 +00:00
if ( len > outSize - 1 ) {
break ;
}
* out + + = ch ;
colorlessLen + + ;
len + + ;
}
* out = 0 ;
2012-01-22 21:34:33 +00:00
// don't allow empty names
2012-08-04 10:54:37 +00:00
if ( * p = = 0 | | colorlessLen = = 0 )
{
Q_strncpyz ( p , " RedShirt " , outSize ) ;
}
2012-01-22 21:34:33 +00:00
}
/*
= = = = = = = = = = =
2012-08-04 10:54:37 +00:00
legalSkin
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
Compare a list of races with an incoming race name .
Used to decide if in a CTF game where a race is specified for a given team if a skin is actually already legal .
= = = = = = = = = = =
2012-01-22 21:34:33 +00:00
*/
2012-08-04 10:54:37 +00:00
/**
* Compare a list of races with an incoming race name .
* Used to decide if in a CTF game where a race is specified for a given team if a skin is actually already legal .
*/
2013-07-06 16:19:28 +00:00
static qboolean legalSkin ( const char * race_list , const char * race )
2012-08-04 10:54:37 +00:00
{
char current_race_name [ 125 ] ;
const char * s = race_list ;
const char * max_place = race_list + strlen ( race_list ) ;
2013-07-06 16:19:28 +00:00
const char * marker = NULL ;
2012-08-04 10:54:37 +00:00
memset ( current_race_name , 0 , sizeof ( current_race_name ) ) ;
// look through the list till it's empty
while ( s < max_place )
{
marker = s ;
// figure out from where we are where the next ',' or 0 is
while ( * s ! = ' , ' & & * s ! = 0 )
{
s + + ;
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// copy just that name
2013-07-06 16:19:28 +00:00
strncpy ( current_race_name , marker , ( s - marker ) + 1 ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// avoid the comma or increment us past the end of the string so we fail the main while loop
s + + ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// compare and see if this race is the same as the one we want
2013-07-06 16:19:28 +00:00
if ( Q_stricmp ( current_race_name , race ) = = 0 )
2012-08-04 10:54:37 +00:00
{
return qtrue ;
2012-01-22 21:34:33 +00:00
}
}
2012-08-04 10:54:37 +00:00
return qfalse ;
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
/*
= = = = = = = = = = =
randomSkin
given a race name , go find all the skins that use it , and randomly select one
= = = = = = = = = = =
*/
/**
* given a race name , go find all the skins that use it , and randomly select one
*/
2013-07-06 16:19:28 +00:00
static void randomSkin ( const char * race , char * model , int32_t current_team , int32_t clientNum )
2012-08-04 10:54:37 +00:00
{
2013-07-06 16:19:28 +00:00
char * * skinsForRace = NULL ;
int32_t howManySkins = 0 ;
int32_t i = 0 ;
int32_t x = 0 ;
int32_t temp = 0 ;
int32_t skin_count_check = 0 ;
char * * skinNamesAlreadyUsed = NULL ;
int32_t current_skin_count = 0 ;
gentity_t * ent = NULL ;
char * userinfo = NULL ;
char temp_model [ MAX_QPATH ] ;
memset ( temp_model , 0 , sizeof ( temp_model ) ) ;
skinsForRace = malloc ( MAX_SKINS_FOR_RACE * 128 * sizeof ( char ) ) ;
if ( skinsForRace = = NULL ) {
2012-08-04 10:54:37 +00:00
G_Error ( " Was unable to allocate %i bytes. \n " , MAX_SKINS_FOR_RACE * 128 * sizeof ( char ) ) ;
return ;
2012-01-22 21:34:33 +00:00
}
2013-07-06 16:19:28 +00:00
skinNamesAlreadyUsed = malloc ( 16 * 128 * sizeof ( char ) ) ;
if ( skinNamesAlreadyUsed = = NULL ) {
2012-08-04 10:54:37 +00:00
G_Error ( " Was unable to allocate %i bytes. \n " , 16 * 128 * sizeof ( char ) ) ;
return ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
memset ( skinsForRace , 0 , MAX_SKINS_FOR_RACE * 128 * sizeof ( char ) ) ;
memset ( skinNamesAlreadyUsed , 0 , 16 * 128 * sizeof ( char ) ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// first up, check to see if we want to select a skin from someone that's already playing on this guys team
skin_count_check = g_random_skin_limit . integer ;
2013-07-06 16:19:28 +00:00
if ( skin_count_check ! = 0 ) {
2012-08-04 10:54:37 +00:00
// sanity check the skins to compare against count
2013-07-06 16:19:28 +00:00
if ( skin_count_check > 16 ) {
2012-08-04 10:54:37 +00:00
skin_count_check = 16 ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
// now construct an array of the names already used
2013-07-06 16:19:28 +00:00
for ( ; i < g_maxclients . integer ; i + + ) {
2012-08-04 10:54:37 +00:00
// did we find enough skins to grab a random one from yet?
if ( current_skin_count = = skin_count_check )
{
break ;
}
ent = g_entities + i ;
2013-07-07 22:14:54 +00:00
if ( ent = = NULL | | ! ent - > inuse | | i = = clientNum ) {
2012-08-04 10:54:37 +00:00
continue ;
2013-07-06 16:19:28 +00:00
}
2012-08-04 10:54:37 +00:00
// no, so look at the next one, and see if it's in the list we are constructing
// same team?
2013-07-07 22:14:54 +00:00
if ( ent - > client ! = NULL & & ent - > client - > sess . sessionTeam = = current_team ) {
userinfo = malloc ( MAX_INFO_STRING * sizeof ( char ) ) ;
if ( userinfo = = NULL ) {
2012-08-04 10:54:37 +00:00
G_Error ( " Was unable to allocate %i bytes. \n " , MAX_INFO_STRING * sizeof ( char ) ) ;
return ;
}
// so what's this clients model then?
trap_GetUserinfo ( i , userinfo , MAX_INFO_STRING * sizeof ( char ) ) ;
Q_strncpyz ( temp_model , Info_ValueForKey ( userinfo , " model " ) , sizeof ( temp_model ) ) ;
free ( userinfo ) ;
// check the name
2013-07-07 22:14:54 +00:00
for ( x = 0 ; x < current_skin_count ; x + + ) {
2012-08-04 10:54:37 +00:00
// are we the same?
2013-07-07 22:14:54 +00:00
if ( Q_stricmp ( skinNamesAlreadyUsed [ x ] , temp_model ) = = 0 ) {
2012-08-04 10:54:37 +00:00
// yeah - ok we already got this one
break ;
}
}
// ok, did we match anything?
2013-07-07 22:14:54 +00:00
if ( x = = current_skin_count ) {
2012-08-04 10:54:37 +00:00
// no - better add this name in
Q_strncpyz ( skinNamesAlreadyUsed [ current_skin_count ] , temp_model , sizeof ( skinNamesAlreadyUsed [ current_skin_count ] ) ) ;
current_skin_count + + ;
}
}
}
// ok, array constructed. Did we get enough?
2013-07-07 22:14:54 +00:00
if ( current_skin_count > = skin_count_check ) {
2012-08-04 10:54:37 +00:00
// yeah, we did - so select a skin from one of these then
temp = rand ( ) % current_skin_count ;
Q_strncpyz ( model , skinNamesAlreadyUsed [ temp ] , MAX_QPATH ) ;
ForceClientSkin ( model , " " ) ;
free ( skinNamesAlreadyUsed ) ;
free ( skinsForRace ) ;
return ;
}
}
// search through each and every skin we can find
2013-07-07 22:14:54 +00:00
for ( i = 0 ; i < group_count & & howManySkins < MAX_SKINS_FOR_RACE ; i + + ) {
2012-08-04 10:54:37 +00:00
// if this models race list contains the race we want, then add it to the list
2013-07-07 22:14:54 +00:00
if ( legalSkin ( group_list [ i ] . text , race ) ) {
2012-08-04 10:54:37 +00:00
Q_strncpyz ( skinsForRace [ howManySkins + + ] , group_list [ i ] . name , 128 ) ;
}
}
// set model to a random one
2013-07-07 22:14:54 +00:00
if ( howManySkins > 0 ) {
2012-08-04 10:54:37 +00:00
temp = rand ( ) % howManySkins ;
Q_strncpyz ( model , skinsForRace [ temp ] , MAX_QPATH ) ;
2013-07-07 22:14:54 +00:00
} else {
2012-08-04 10:54:37 +00:00
model [ 0 ] = 0 ;
}
free ( skinsForRace ) ;
free ( skinNamesAlreadyUsed ) ;
}
/*
= = = = = = = = = = =
getNewSkin
Go away and actually get a random new skin based on a group name
= = = = = = = = = = = =
*/
/**
* Go away and actually get a random new skin based on a group name
*/
2013-07-07 22:14:54 +00:00
static qboolean getNewSkin ( const char * group , char * model , const char * color , const gclient_t * client , int32_t clientNum )
2012-08-04 10:54:37 +00:00
{
2013-07-07 22:14:54 +00:00
char * temp_string = NULL ;
2012-08-04 10:54:37 +00:00
// go away and get what ever races this skin is attached to.
// remove blue or red name
ForceClientSkin ( model , " " ) ;
temp_string = G_searchGroupList ( model ) ;
// are any of the races legal for this team race?
2013-07-07 22:14:54 +00:00
if ( legalSkin ( temp_string , group ) ) {
2012-08-04 10:54:37 +00:00
ForceClientSkin ( model , color ) ;
return qfalse ;
}
//if we got this far, then we need to reset the skin to something appropriate
randomSkin ( group , model , client - > sess . sessionTeam , clientNum ) ;
return qtrue ;
}
/*
= = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_UserinfoChanged
2012-08-04 10:54:37 +00:00
= = = = = = = = = = = =
2012-01-22 21:34:33 +00:00
*/
2012-08-04 10:54:37 +00:00
/**
2012-11-15 23:58:56 +00:00
* Called from G_Client_Connect when the player first connects and
2012-08-04 10:54:37 +00:00
* 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 .
*/
2013-07-07 22:14:54 +00:00
void G_Client_UserinfoChanged ( int32_t clientNum ) {
gentity_t * ent = NULL ;
int32_t i = 0 ;
int32_t modelOffset = 0 ;
char * s = NULL ;
2013-05-24 21:22:14 +00:00
char model [ MAX_QPATH ] ;
char oldname [ MAX_STRING_CHARS ] ;
char userinfo [ MAX_INFO_STRING ] ;
char age [ MAX_NAME_LENGTH ] ;
char race [ MAX_NAME_LENGTH ] ;
char sHeight [ 10 ] ;
char sWeight [ 10 ] ;
2013-07-07 22:14:54 +00:00
double weight ;
double height ;
gclient_t * client = NULL ;
qboolean reset ;
qboolean changeName = qtrue ; //TiM : For the name filter
2013-05-24 21:22:14 +00:00
clientPersistant_t * pers = NULL ;
clientSession_t * sess = NULL ;
2012-08-04 10:54:37 +00:00
ent = g_entities + clientNum ;
2013-07-07 22:14:54 +00:00
if ( ent = = NULL ) {
return ;
}
memset ( model , 0 , sizeof ( model ) ) ;
memset ( oldname , 0 , sizeof ( oldname ) ) ;
memset ( userinfo , 0 , sizeof ( userinfo ) ) ;
memset ( age , 0 , sizeof ( age ) ) ;
memset ( race , 0 , sizeof ( race ) ) ;
memset ( sHeight , 0 , sizeof ( sHeight ) ) ;
memset ( sWeight , 0 , sizeof ( sWeight ) ) ;
2012-08-04 10:54:37 +00:00
client = ent - > client ;
pers = & client - > pers ;
sess = & client - > sess ;
//TiM - Exit if this user has had their info clamped
2013-07-07 22:14:54 +00:00
if ( ( ent - > flags & FL_CLAMPED ) ! = 0 ) {
2012-08-04 10:54:37 +00:00
return ;
2013-07-07 22:14:54 +00:00
}
2012-08-04 10:54:37 +00:00
trap_GetUserinfo ( clientNum , userinfo , sizeof ( userinfo ) ) ;
// check for malformed or illegal info strings
if ( ! Info_Validate ( userinfo ) ) {
strcpy ( userinfo , " \\ name \\ badinfo " ) ;
}
// check for local client
s = Info_ValueForKey ( userinfo , " ip " ) ;
2013-07-07 22:14:54 +00:00
if ( strcmp ( s , " localhost " ) = = 0 ) {
2012-08-04 10:54:37 +00:00
pers - > localClient = qtrue ;
}
// check the item prediction
s = Info_ValueForKey ( userinfo , " cg_predictItems " ) ;
2013-07-07 22:14:54 +00:00
if ( atoi ( s ) = = 0 ) {
2012-08-04 10:54:37 +00:00
pers - > predictItemPickup = qfalse ;
2012-01-22 21:34:33 +00:00
} else {
2012-08-04 10:54:37 +00:00
pers - > predictItemPickup = qtrue ;
}
// set name
//TiM: Filter for if a player is already on this server with that name.
s = Info_ValueForKey ( userinfo , " name " ) ;
2013-07-07 22:14:54 +00:00
if ( ( rpg_uniqueNames . integer ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
2012-08-04 10:54:37 +00:00
char newName [ 36 ] ;
char activeName [ 36 ] ;
2013-07-07 22:14:54 +00:00
memset ( newName , 0 , sizeof ( newName ) ) ;
memset ( activeName , 0 , sizeof ( activeName ) ) ;
2012-08-04 10:54:37 +00:00
ClientCleanName ( s , newName , sizeof ( newName ) ) ;
Q_CleanStr ( newName ) ;
//loop thru all the clients, and see if we have one that has the same name as our proposed one
for ( i = 0 ; i < level . numConnectedClients ; i + + ) {
Q_strncpyz ( activeName , g_entities [ i ] . client - > pers . netname , sizeof ( activeName ) ) ;
Q_CleanStr ( activeName ) ;
2013-07-07 22:14:54 +00:00
if ( ( g_entities [ i ] . client - > ps . clientNum ! = client - > ps . clientNum )
& & ( Q_stricmp ( newName , activeName ) = = 0 ) ) {
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( ent - g_entities , " print \" Unable to change name. A player already has that name on this server. \n \" " ) ;
changeName = qfalse ;
break ;
}
}
}
if ( changeName ) {
Q_strncpyz ( oldname , pers - > netname , sizeof ( oldname ) ) ;
ClientCleanName ( s , pers - > netname , sizeof ( pers - > netname ) ) ;
if ( sess - > sessionTeam = = TEAM_SPECTATOR ) {
if ( sess - > spectatorState = = SPECTATOR_SCOREBOARD ) {
Q_strncpyz ( pers - > netname , " scoreboard " , sizeof ( pers - > netname ) ) ;
}
}
if ( pers - > connected = = CON_CONNECTED ) {
2013-07-07 22:14:54 +00:00
if ( strcmp ( oldname , pers - > netname ) ! = 0 ) {
if ( ( ! levelExiting ) & & ( level . intermissiontime = = 0 ) ) {
//no need to do this during level changes
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( - 1 , va ( " print \" %s " S_COLOR_WHITE " renamed to %s \n \" " , oldname , pers - > netname ) ) ;
}
}
}
}
pers - > pms_height = atof ( Info_ValueForKey ( userinfo , " height " ) ) ;
2013-05-24 21:22:14 +00:00
if ( pers - > pms_height = = 0 ) {
2012-08-04 10:54:37 +00:00
pers - > pms_height = 1.0f ;
2013-05-24 21:22:14 +00:00
}
2012-08-04 10:54:37 +00:00
pers - > maxHealth = atoi ( Info_ValueForKey ( userinfo , " handicap " ) ) ;
if ( pers - > maxHealth < 1 | | pers - > maxHealth > 100 ) {
pers - > maxHealth = 100 ;
}
//if you have a class, ignores handicap and 100 limit, sorry
client - > ps . stats [ STAT_MAX_HEALTH ] = pers - > maxHealth ;
Q_strncpyz ( model , Info_ValueForKey ( userinfo , " model " ) , sizeof ( model ) ) ;
// team
2013-07-07 22:14:54 +00:00
switch ( sess - > sessionTeam ) {
case TEAM_RED :
{
2012-08-04 10:54:37 +00:00
// decide if we are going to have to reset a skin cos it's not applicable to a race selected
2013-07-07 22:14:54 +00:00
if ( ( g_gametype . integer < GT_TEAM ) | | ( Q_stricmp ( " " , g_team_group_red . string ) = = 0 ) ) {
2012-08-04 10:54:37 +00:00
ForceClientSkin ( model , " red " ) ;
break ;
2013-07-07 22:14:54 +00:00
} else {
// at this point, we are playing CTF and there IS a race specified for this game
2012-08-04 10:54:37 +00:00
reset = getNewSkin ( g_team_group_red . string , model , " red " , client , clientNum ) ;
// did we get a model name back?
2013-07-07 22:14:54 +00:00
if ( model [ 0 ] = = 0 ) {
2012-08-04 10:54:37 +00:00
// no - this almost certainly means we had a bogus race is the g_team_group_team cvar
// so reset it to starfleet and try it again
Com_Printf ( " WARNING! - Red Group %s is unknown - resetting Red Group to Allow Any Group \n " , g_team_group_red . string ) ;
trap_Cvar_Set ( " g_team_group_red " , " " ) ;
trap_Cvar_Register ( & g_team_group_red , " g_team_group_red " ,
" " , CVAR_LATCH ) ;
// Since we are allow any group now, just get his normal model and carry on
Q_strncpyz ( model , Info_ValueForKey ( userinfo , " model " ) , sizeof ( model ) ) ;
ForceClientSkin ( model , " red " ) ;
reset = qfalse ;
}
2013-07-07 22:14:54 +00:00
if ( reset ) {
if ( ! levelExiting ) {
//no need to do this during level changes
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( - 1 , va ( " print \" In-appropriate skin selected for %s on team %s \n Skin selection overridden from skin %s to skin %s \n \" " ,
pers - > netname , g_team_group_red . string , Info_ValueForKey ( userinfo , " model " ) , model ) ) ;
}
ForceClientSkin ( model , " red " ) ;
// change the value in out local copy, then update it on the server
Info_SetValueForKey ( userinfo , " model " , model ) ;
trap_SetUserinfo ( clientNum , userinfo ) ;
}
break ;
}
2013-07-07 22:14:54 +00:00
}
case TEAM_BLUE :
{
2012-08-04 10:54:37 +00:00
// decide if we are going to have to reset a skin cos it's not applicable to a race selected
2013-07-07 22:14:54 +00:00
if ( ( g_gametype . integer < GT_TEAM ) | | ( Q_stricmp ( " " , g_team_group_blue . string ) = = 0 ) ) {
2012-08-04 10:54:37 +00:00
ForceClientSkin ( model , " blue " ) ;
break ;
2013-07-07 22:14:54 +00:00
} else {
// at this point, we are playing CTF and there IS a race specified for this game
2012-08-04 10:54:37 +00:00
// go away and get what ever races this skin is attached to.
reset = getNewSkin ( g_team_group_blue . string , model , " blue " , client , clientNum ) ;
// did we get a model name back?
2013-07-07 22:14:54 +00:00
if ( model [ 0 ] = = 0 ) {
2012-08-04 10:54:37 +00:00
// no - this almost certainly means we had a bogus race is the g_team_group_team cvar
// so reset it to klingon and try it again
Com_Printf ( " WARNING! - Blue Group %s is unknown - resetting Blue Group to Allow Any Group \n " , g_team_group_blue . string ) ;
trap_Cvar_Set ( " g_team_group_blue " , " " ) ;
trap_Cvar_Register ( & g_team_group_blue , " g_team_group_blue " ,
" " , CVAR_LATCH ) ;
// Since we are allow any group now, just get his normal model and carry on
Q_strncpyz ( model , Info_ValueForKey ( userinfo , " model " ) , sizeof ( model ) ) ;
ForceClientSkin ( model , " blue " ) ;
reset = qfalse ;
}
2013-07-07 22:14:54 +00:00
if ( reset ) {
if ( ! levelExiting ) {
//no need to do this during level changes
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( - 1 , va ( " print \" In-appropriate skin selected for %s on team %s \n Skin selection overridden from skin %s to skin %s \n \" " ,
pers - > netname , g_team_group_blue . string , Info_ValueForKey ( userinfo , " model " ) , model ) ) ;
}
ForceClientSkin ( model , " blue " ) ;
// change the value in out local copy, then update it on the server
Info_SetValueForKey ( userinfo , " model " , model ) ;
trap_SetUserinfo ( clientNum , userinfo ) ;
}
break ;
}
2012-01-22 21:34:33 +00:00
}
2013-07-07 22:14:54 +00:00
default :
break ;
2012-01-22 21:34:33 +00:00
}
2013-07-07 22:14:54 +00:00
if ( ( g_gametype . integer > = GT_TEAM ) & & ( sess - > sessionTeam = = TEAM_SPECTATOR ) ) {
// don't ever use a default skin in teamplay, it would just waste memory
ForceClientSkin ( model , " red " ) ;
2012-08-04 10:54:37 +00:00
}
2013-07-07 22:14:54 +00:00
if ( ( rpg_rpg . integer ! = 0 ) & & ( rpg_forceclasscolor . integer ! = 0 ) & & ( g_gametype . integer < GT_TEAM ) ) {
2012-08-04 10:54:37 +00:00
ForceClientSkin ( model , g_classData [ sess - > sessionClass ] . modelSkin ) ;
}
//TiM : For when an admin chooses not to see admin messages
//Marcin : and check for privacy mode - 24/12/2008
s = Info_ValueForKey ( userinfo , " noAdminChat " ) ;
if ( atoi ( s ) > 0 ) {
client - > noAdminChat = qtrue ;
2013-07-07 22:14:54 +00:00
} else {
2012-08-04 10:54:37 +00:00
client - > noAdminChat = qfalse ;
}
2012-01-22 21:34:33 +00:00
// teamInfo
s = Info_ValueForKey ( userinfo , " teamoverlay " ) ;
if ( ! * s | | atoi ( s ) ! = 0 ) {
2012-08-04 10:54:37 +00:00
pers - > teamInfo = qtrue ;
2012-01-22 21:34:33 +00:00
} else {
2012-08-04 10:54:37 +00:00
pers - > teamInfo = qfalse ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
//PMS system - lock down the values
s = Info_ValueForKey ( userinfo , " height " ) ;
height = atof ( s ) ;
2013-07-07 22:14:54 +00:00
if ( height > ( float ) rpg_maxHeight . value ) {
2012-08-04 10:54:37 +00:00
Q_strncpyz ( sHeight , rpg_maxHeight . string , sizeof ( sHeight ) ) ;
2013-07-07 22:14:54 +00:00
} else if ( height < ( float ) rpg_minHeight . value ) {
2012-08-04 10:54:37 +00:00
Q_strncpyz ( sHeight , rpg_minHeight . string , sizeof ( sHeight ) ) ;
2013-07-07 22:14:54 +00:00
} else {
2012-08-04 10:54:37 +00:00
Q_strncpyz ( sHeight , s , sizeof ( sHeight ) ) ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
//TiM - needed for height offset
pers - > pms_height = atof ( sHeight ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
//PMS system - lock down the values
s = Info_ValueForKey ( userinfo , " weight " ) ;
weight = atof ( s ) ;
2013-07-07 22:14:54 +00:00
if ( weight > ( float ) rpg_maxWeight . value ) {
2012-08-04 10:54:37 +00:00
Q_strncpyz ( sWeight , rpg_maxWeight . string , sizeof ( sWeight ) ) ;
2013-07-07 22:14:54 +00:00
} else if ( weight < ( float ) rpg_minWeight . value ) {
2012-08-04 10:54:37 +00:00
Q_strncpyz ( sWeight , rpg_minWeight . string , sizeof ( sWeight ) ) ;
2013-07-07 22:14:54 +00:00
} else {
2012-08-04 10:54:37 +00:00
Q_strncpyz ( sWeight , s , sizeof ( sWeight ) ) ;
}
s = Info_ValueForKey ( userinfo , " age " ) ;
Q_strncpyz ( age , s , sizeof ( age ) ) ;
s = Info_ValueForKey ( userinfo , " race " ) ;
Q_strncpyz ( race , s , sizeof ( race ) ) ;
s = Info_ValueForKey ( userinfo , " modelOffset " ) ;
modelOffset = atoi ( s ) ;
// send over a subset of the userinfo keys so other clients can
// print scoreboards, display models, and play custom sounds
//FIXME: In future, we'll lock down these PMS values so we can't have overloaded transmission data
2013-07-07 22:14:54 +00:00
if ( ( ent - > r . svFlags & SVF_BOT ) ! = 0 ) {
2012-08-04 10:54:37 +00:00
s = va ( " n \\ %s \\ t \\ %i \\ p \\ %i \\ model \\ %s \\ hc \\ %i \\ w \\ %i \\ l \\ %i \\ skill \\ %s \\ age \\ 25 \\ height \\ %s \\ weight \\ %s \\ race \\ Bot \\ of \\ %i \\ admin \\ 0 " ,
pers - > netname , sess - > sessionTeam , sess - > sessionClass , model ,
pers - > maxHealth , sess - > wins , sess - > losses ,
Info_ValueForKey ( userinfo , " skill " ) ,
sHeight , sWeight , modelOffset ) ;
} else {
s = va ( " n \\ %s \\ t \\ %i \\ p \\ %i \\ model \\ %s \\ hc \\ %i \\ w \\ %i \\ l \\ %i \\ age \\ %s \\ height \\ %s \\ weight \\ %s \\ race \\ %s \\ of \\ %i \\ admin \\ %i " ,
pers - > netname , sess - > sessionTeam , sess - > sessionClass , model ,
pers - > maxHealth , sess - > wins , sess - > losses , age ,
sHeight , sWeight , race , modelOffset , ( ( int ) IsAdmin ( g_entities + clientNum ) ) ) ;
2012-01-22 21:34:33 +00:00
}
trap_SetConfigstring ( CS_PLAYERS + clientNum , s ) ;
2012-11-11 17:33:50 +00:00
G_LogPrintf ( " ClientUserinfoChanged: %i %s \n " , clientNum , s ) ; // no ip logging here as string might get to long
2012-01-22 21:34:33 +00:00
}
/*
= = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_Connect
2012-01-22 21:34:33 +00:00
= = = = = = = = = = = =
*/
2012-08-04 10:54:37 +00:00
/**
* Called when a player begins connecting to the server .
* Called again for every map change or tournement restart .
*
* The session information will be valid after exit .
*
* Return NULL if the client should be allowed , otherwise return
* a string with the reason for denial .
*
* Otherwise , the client will be sent the current gamestate
2012-11-15 23:58:56 +00:00
* and will eventually get to G_Client_Begin .
2012-08-04 10:54:37 +00:00
*
* firstTime will be qtrue the very first time a client connects
* to the server machine , but qfalse on map changes and tournement
* restarts .
*/
2013-07-08 17:40:30 +00:00
char * G_Client_Connect ( int32_t clientNum , qboolean firstTime , qboolean isBot ) {
char * value = NULL ;
char * newClass = NULL ;
char * newRank = NULL ;
char ip [ 64 ] ; //TiM : Saved the IP data for player recon feature
2012-01-22 21:34:33 +00:00
char userinfo [ MAX_INFO_STRING ] ;
2013-07-08 17:40:30 +00:00
gclient_t * client = NULL ;
gentity_t * ent = NULL ;
2012-08-04 10:54:37 +00:00
vmCvar_t mapname ;
vmCvar_t sv_hostname ;
2013-07-08 17:40:30 +00:00
ent = & g_entities [ clientNum ] ;
if ( ent = = NULL ) {
return " Critical Error: Client entity was NULL " ;
}
memset ( ip , 0 , sizeof ( ip ) ) ;
memset ( userinfo , 0 , sizeof ( userinfo ) ) ;
2012-08-04 10:54:37 +00:00
trap_Cvar_Register ( & mapname , " mapname " , " " , CVAR_SERVERINFO | CVAR_ROM ) ;
trap_Cvar_Register ( & sv_hostname , " sv_hostname " , " " , CVAR_SERVERINFO | CVAR_ROM ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
trap_GetUserinfo ( clientNum , userinfo , sizeof ( userinfo ) ) ;
// check to see if they are on the banned IP list
value = Info_ValueForKey ( userinfo , " ip " ) ;
Q_strncpyz ( ip , value , sizeof ( ip ) ) ;
if ( G_FilterPacket ( value ) | | CheckID ( Info_ValueForKey ( userinfo , " sv_securityCode " ) ) ) {
return " Banned from this server " ;
2012-11-11 17:33:50 +00:00
}
2012-08-04 10:54:37 +00:00
2012-11-11 17:33:50 +00:00
// check for a password
if ( ! isBot )
{
value = Info_ValueForKey ( userinfo , " password " ) ;
2013-07-08 17:40:30 +00:00
if ( g_password . string [ 0 ] & & Q_stricmp ( g_password . string , " none " ) ! = 0 & & strcmp ( g_password . string , value ) ! = 0 )
2012-08-04 10:54:37 +00:00
{
2012-11-11 17:33:50 +00:00
return " Invalid password " ;
2012-08-04 10:54:37 +00:00
}
2012-11-11 17:33:50 +00:00
}
2012-08-04 10:54:37 +00:00
//TiM: If need be, chack to see if any other players have the current name...
//evil impersonators and the likes
2013-07-08 17:40:30 +00:00
if ( rpg_uniqueNames . integer ! = 0 & & ! isBot ) {
2012-08-04 10:54:37 +00:00
char name [ 36 ] ;
char oldName [ 36 ] ;
2013-08-13 19:45:20 +00:00
int32_t i = 0 ;
2013-07-08 17:40:30 +00:00
memset ( name , 0 , sizeof ( name ) ) ;
memset ( oldName , 0 , sizeof ( oldName ) ) ;
2012-08-04 10:54:37 +00:00
//get the name
value = Info_ValueForKey ( userinfo , " name " ) ;
//Clean the data
ClientCleanName ( value , name , sizeof ( name ) ) ;
//Now, do a compare with all clients in the server
for ( i = 0 ; i < MAX_CLIENTS ; i + + ) {
2013-07-08 17:40:30 +00:00
if ( ( g_entities [ i ] . client = = NULL ) | | ( g_entities [ i ] . client - > pers . connected ! = CON_CONNECTED ) ) {
2012-08-04 10:54:37 +00:00
continue ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
if ( g_entities [ i ] . client - > pers . netname [ 0 ] ) {
//local copy the string and work on that, else we risk wrecking other people's names
Q_strncpyz ( oldName , g_entities [ i ] . client - > pers . netname , sizeof ( oldName ) ) ;
2013-07-08 17:40:30 +00:00
if ( ( Q_stricmp ( Q_CleanStr ( name ) , Q_CleanStr ( oldName ) ) = = 0 ) & & ! isBot ) {
2012-08-04 10:54:37 +00:00
return " There is already a user with that name. " ;
}
}
}
}
// they can connect
ent - > client = level . clients + clientNum ;
client = ent - > client ;
memset ( client , 0 , sizeof ( * client ) ) ;
client - > pers . connected = CON_CONNECTING ;
// read or initialize the session data
if ( firstTime | | level . newSession ) {
G_InitSessionData ( client , userinfo ) ;
}
G_ReadSessionData ( client ) ;
if ( isBot ) {
ent - > r . svFlags | = SVF_BOT ;
ent - > inuse = qtrue ;
if ( ! G_BotConnect ( clientNum , ! firstTime ) ) {
return " BotConnectfailed " ;
}
}
// get and distribute relevent paramters
G_LogPrintf ( " ClientConnect: %i (%s) \n " , clientNum , g_entities [ clientNum ] . client - > pers . ip ) ;
if ( rpg_rpg . integer ! = 0 /*&& firstTime*/ )
{
//TiM: Code for automatic class + rank switching
//========================================================
if ( isBot ) {
client - > sess . sessionClass = 0 ;
client - > ps . persistant [ PERS_SCORE ] = 1 ;
2013-07-08 17:40:30 +00:00
} else {
2013-08-13 19:45:20 +00:00
int32_t tmpScore = 0 ;
2013-08-13 19:48:06 +00:00
int32_t i = 0 ;
2013-08-14 19:39:07 +00:00
qboolean changeRank = qfalse ;
2013-08-13 19:45:20 +00:00
2012-08-04 10:54:37 +00:00
newClass = Info_ValueForKey ( userinfo , " ui_playerClass " ) ;
newRank = Info_ValueForKey ( userinfo , " ui_playerRank " ) ;
//Com_Printf( S_COLOR_RED "Data: %s %s\n", newClass, newRank );
2013-07-08 17:40:30 +00:00
if ( newClass [ 0 ] ! = 0 ) {
2012-08-04 10:54:37 +00:00
client - > sess . sessionClass = ValueNameForClass ( newClass ) ; //TiM: BOOYEAH! :)
//if class doesn't exist, default to 0
2013-07-08 17:40:30 +00:00
if ( client - > sess . sessionClass < 0 ) {
2012-08-04 10:54:37 +00:00
client - > sess . sessionClass = 0 ;
2013-07-08 17:40:30 +00:00
}
} else {
2012-08-04 10:54:37 +00:00
client - > sess . sessionClass = 0 ;
}
2013-07-08 17:40:30 +00:00
for ( i = 0 ; i < MAX_RANKS ; i + + ) {
if ( rpg_startingRank . string [ 0 ] = = 0 & & newRank [ 0 ] ! = 0 ) {
if ( Q_stricmp ( newRank , g_rankNames [ i ] . consoleName ) = = 0 ) {
tmpScore = i ; //1 << i;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
if ( rpg_changeRanks . integer ! = 0 ) {
2012-08-04 10:54:37 +00:00
changeRank = qtrue ;
}
2013-07-08 17:40:30 +00:00
break ;
}
} else {
if ( ( rpg_startingRank . string [ 0 ] ! = 0 ) & & ( Q_stricmp ( g_rankNames [ i ] . consoleName , rpg_startingRank . string ) = = 0 ) ) {
tmpScore = i ; // 1 << i;
changeRank = qtrue ;
break ;
2012-08-04 10:54:37 +00:00
}
}
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
if ( changeRank ) {
ent - > client - > UpdateScore = qtrue ;
SetScore ( ent , tmpScore ) ;
2012-08-04 10:54:37 +00:00
}
}
//========================================================
}
2012-11-15 23:58:56 +00:00
G_Client_UserinfoChanged ( clientNum ) ;
2012-08-04 10:54:37 +00:00
//RPG-X: Save the ip for later - has to be down here, since it gets flushed in the above function
Q_strncpyz ( ent - > client - > pers . ip , ip , sizeof ( ent - > client - > pers . ip ) ) ;
// don't do the "xxx connected" messages if they were caried over from previous level
if ( firstTime )
{
if ( ! levelExiting )
{ //no need to do this during level changes
2013-07-08 17:40:30 +00:00
qboolean nameFound = qfalse ;
2012-08-04 10:54:37 +00:00
//Check to see if this player already connected on this server
2013-07-08 17:40:30 +00:00
if ( ( rpg_renamedPlayers . integer ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
2013-08-13 19:50:19 +00:00
int32_t i = 0 ;
2012-08-04 10:54:37 +00:00
for ( i = 0 ; i < MAX_RECON_NAMES ; i + + ) {
2013-07-08 17:40:30 +00:00
if ( g_reconData [ i ] . previousName [ 0 ] = = 0 ) {
2012-08-04 10:54:37 +00:00
continue ;
}
2013-07-08 17:40:30 +00:00
if ( ( Q_stricmp ( client - > pers . ip , g_reconData [ i ] . ipAddress ) = = 0 )
2013-07-08 18:21:20 +00:00
& & ( Q_stricmp ( client - > pers . netname , g_reconData [ i ] . previousName ) ! = 0 ) ) {
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( - 1 , va ( " print \" %s " S_COLOR_WHITE " (With the previous name of %s " S_COLOR_WHITE " ) connected \n \" " , client - > pers . netname , g_reconData [ i ] . previousName ) ) ;
nameFound = qtrue ;
break ;
}
}
}
if ( ! nameFound ) {
trap_SendServerCommand ( - 1 , va ( " print \" %s " S_COLOR_WHITE " connected \n \" " , client - > pers . netname ) ) ;
}
//RPG-X | Phenix | 07/04/2005
client - > AdminFailed = 0 ;
ent - > n00bCount = 0 ;
client - > LoggedAsDeveloper = qfalse ;
}
}
2013-07-08 17:40:30 +00:00
if ( ( g_gametype . integer > = GT_TEAM ) & & ( client - > sess . sessionTeam ! = TEAM_SPECTATOR ) ) {
2012-08-04 10:54:37 +00:00
BroadcastTeamChange ( client , - 1 ) ;
}
// count current clients and rank for scoreboard
2012-11-15 23:58:56 +00:00
//G_Client_CalculateRanks( qfalse );
2012-08-04 10:54:37 +00:00
//RPG-X: J2J - Reset Variables
DragDat [ clientNum ] . AdminId = - 1 ;
DragDat [ clientNum ] . distance = 0 ;
g_entities [ clientNum ] . client - > noclip = qfalse ;
return NULL ;
}
extern holoData_t holoData ;
//! Think function for temporal entity that transmits the holodeck date to the client
2013-07-08 17:40:30 +00:00
static void holoTent_think ( gentity_t * ent ) {
if ( ent - > count = = 0 ) {
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( ent - g_entities , va ( " holo_data %i " , holoData . numProgs ) ) ;
ent - > count = 1 ;
ent - > health = 0 ;
ent - > nextthink = level . time + 250 ;
return ;
}
if ( ent - > health = = holoData . numProgs ) {
ent - > count + + ;
ent - > health = 0 ;
}
switch ( ent - > count ) {
case 1 : // name
trap_SendServerCommand ( ent - g_entities , va ( " holo_data \" n%i \\ %s \\ \" " , ent - > health , holoData . name [ ent - > health ] ) ) ;
break ;
case 2 : // desc1
trap_SendServerCommand ( ent - g_entities , va ( " holo_data \" da%i \\ %s \\ \" " , ent - > health , holoData . desc1 [ ent - > health ] ) ) ;
break ;
case 3 : // desc2
trap_SendServerCommand ( ent - g_entities , va ( " holo_data \" db%i \\ %s \\ \" " , ent - > health , holoData . desc2 [ ent - > health ] ) ) ;
break ;
case 4 : // image
trap_SendServerCommand ( ent - g_entities , va ( " holo_data \" i%i \\ %s \\ \" " , ent - > health , holoData . image [ ent - > health ] ) ) ;
break ;
}
ent - > health + + ;
if ( ent - > count > 4 ) {
G_PrintfClient ( ent , " Received data of %i holodeck programs. \n " , holoData . numProgs ) ;
G_FreeEntity ( ent ) ;
return ;
}
ent - > nextthink = level . time + 250 ;
}
//! Create a temporal entity that sends over the holodata to the client
2013-07-08 17:40:30 +00:00
static void G_SendHoloData ( int32_t clientNum ) {
2012-08-04 10:54:37 +00:00
gentity_t * holoTent ;
holoTent = G_Spawn ( ) ;
2013-07-21 13:24:57 +00:00
holoTent - > classname = " holoTent " ;
2012-08-04 10:54:37 +00:00
holoTent - > target_ent = g_entities + clientNum ;
holoTent - > think = holoTent_think ;
holoTent - > nextthink = level . time + 2500 ;
}
//! Think function for temporal entity that transmits the server change data and map change data for transporter UI
2013-07-08 17:40:30 +00:00
static void transTent_think ( gentity_t * ent ) {
char temp [ MAX_STRING_CHARS ] ;
int32_t i ;
2012-08-04 10:54:37 +00:00
memset ( temp , 0 , sizeof ( temp ) ) ;
2013-02-25 11:15:33 +00:00
for ( i = 0 ; i < level . srvChangeData . count ; i + + ) {
2013-07-08 17:40:30 +00:00
if ( temp [ 0 ] = = 0 ) {
2013-02-25 11:15:33 +00:00
Com_sprintf ( temp , sizeof ( temp ) , " ui_trdata d%i \\ %s \\ " , i , level . srvChangeData . name [ i ] ) ;
2013-07-08 17:40:30 +00:00
} else {
2013-02-25 11:15:33 +00:00
Com_sprintf ( temp , sizeof ( temp ) , " %sd%i \\ %s \\ " , temp , i , level . srvChangeData . name [ i ] ) ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
}
2013-02-25 11:15:33 +00:00
trap_SendServerCommand ( ent - > target_ent - g_entities , temp ) ;
2012-08-04 10:54:37 +00:00
G_FreeEntity ( ent ) ;
}
//! creates an entity that transmits the server change data to the client
2013-07-08 17:40:30 +00:00
static void G_SendTransData ( int32_t clientNum ) {
2012-08-04 10:54:37 +00:00
gentity_t * transTent ;
transTent = G_Spawn ( ) ;
2013-07-21 13:24:57 +00:00
transTent - > classname = " transTent " ;
2012-08-04 10:54:37 +00:00
transTent - > target_ent = g_entities + clientNum ;
transTent - > think = transTent_think ;
transTent - > nextthink = level . time + 500 ;
}
/*
= = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_Begin
2012-08-04 10:54:37 +00:00
= = = = = = = = = = = =
*/
/**
* called when a client has finished connecting , and is ready
* to be placed into the level . This will happen every level load ,
* and on transition between teams , but doesn ' t happen on respawns
*/
2013-07-08 17:40:30 +00:00
void G_Client_Begin ( int32_t clientNum , qboolean careAboutWarmup , qboolean isBot , qboolean first ) {
gentity_t * ent = NULL ;
gclient_t * client = NULL ;
gentity_t * tent = NULL ;
gentity_t * selfdestruct = NULL ;
int32_t flags = 0 ;
2012-08-04 10:54:37 +00:00
qboolean alreadyIn = qfalse ;
2013-07-08 17:40:30 +00:00
int32_t score = 0 ;
2012-08-04 10:54:37 +00:00
ent = g_entities + clientNum ;
2013-07-08 17:40:30 +00:00
if ( ent = = NULL ) {
return ;
}
2012-08-04 10:54:37 +00:00
if ( ent - > botDelayBegin ) {
G_QueueBotBegin ( clientNum ) ;
ent - > botDelayBegin = qfalse ;
return ;
}
client = level . clients + clientNum ;
if ( ent - > r . linked ) {
trap_UnlinkEntity ( ent ) ;
}
G_InitGentity ( ent ) ;
ent - > touch = 0 ;
ent - > pain = 0 ;
ent - > client = client ;
2013-07-08 17:40:30 +00:00
if ( client - > pers . connected = = CON_CONNECTED ) {
2012-08-04 10:54:37 +00:00
alreadyIn = qtrue ;
}
client - > pers . connected = CON_CONNECTED ;
client - > pers . enterTime = level . time ;
client - > pers . teamState . state = TEAM_BEGIN ;
// save eflags around this, because changing teams will
// cause this to happen with a valid entity, and we
// want to make sure the teleport bit is set right
// so the viewpoint doesn't interpolate through the
// world to the new position
//TiM... I think this is why my damn RANK SYSTEM ENHANCEMENT HAS BEEN BUGGING OUT!!@!@!!
//ARRRGRGRGRGRGRGRGRGRGRGRGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!!! D:<
flags = client - > ps . eFlags ;
score = client - > ps . persistant [ PERS_SCORE ] ;
memset ( & client - > ps , 0 , sizeof ( client - > ps ) ) ;
client - > ps . eFlags = flags ;
client - > UpdateScore = qtrue ;
SetScore ( ent , score ) ;
// locate ent at a spawn point
2012-11-15 23:58:56 +00:00
G_Client_Spawn ( ent , 0 , qfalse ) ; //RPG-X: RedTechie - Modifyed
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
if ( client - > sess . sessionTeam ! = TEAM_SPECTATOR ) {
// Don't use transporter FX for spectators or those watching the holodoors.
2012-08-04 10:54:37 +00:00
// send event
ent - > client - > ps . powerups [ PW_QUAD ] = level . time + 4000 ;
tent = G_TempEntity ( ent - > client - > ps . origin , EV_PLAYER_TRANSPORT_IN ) ;
tent - > s . clientNum = ent - > s . clientNum ;
}
G_LogPrintf ( " ClientBegin: %i (%s) \n " , clientNum , g_entities [ clientNum ] . client - > pers . ip ) ;
// count current clients and rank for scoreboard
2012-11-15 23:58:56 +00:00
G_Client_CalculateRanks ( qfalse ) ;
2012-08-04 10:54:37 +00:00
//TiM - This appears to be a flaw in Raven's design
//When a client connects, or if they enter admin or medics class
//ensure the relevant health data is sent to them, or else they'll
//see anomalies when scanning players
2013-07-08 17:40:30 +00:00
if ( ( client - > sess . sessionTeam = = TEAM_SPECTATOR ) | |
( g_classData [ client - > sess . sessionClass ] . isMedical ) | |
( g_classData [ client - > sess . sessionClass ] . isAdmin ) )
2012-08-04 10:54:37 +00:00
{
2013-07-08 17:40:30 +00:00
int32_t i = 0 ;
2012-08-04 10:54:37 +00:00
char entry [ 16 ] ;
char command [ 1024 ] ;
2013-07-08 17:40:30 +00:00
int32_t numPlayers = 0 ;
2012-08-04 10:54:37 +00:00
gentity_t * player ;
2013-07-08 17:40:30 +00:00
int32_t len = 0 ;
int32_t cmdLen = 0 ;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
memset ( entry , 0 , sizeof ( entry ) ) ;
memset ( command , 0 , sizeof ( command ) ) ;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
for ( ; i < g_maxclients . integer ; i + + )
2012-08-04 10:54:37 +00:00
{
player = g_entities + i ;
2013-07-08 17:40:30 +00:00
if ( ( player = = NULL ) | | ( player = = ent ) | | ! player - > inuse ) {
2012-08-04 10:54:37 +00:00
continue ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
Com_sprintf ( entry , sizeof ( entry ) , " %i %i " , i , player - > health > = 0 ? player - > health : 0 ) ;
len = strlen ( entry ) ;
2013-07-08 17:40:30 +00:00
if ( cmdLen + len > sizeof ( command ) ) {
2012-08-04 10:54:37 +00:00
break ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
strcpy ( command + cmdLen , entry ) ;
cmdLen + = len ;
numPlayers + + ;
}
2013-07-08 17:40:30 +00:00
if ( numPlayers > 0 ) {
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( clientNum , va ( " hinfo %i%s " , numPlayers , command ) ) ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
}
//RPG-X: RedTechie - But we dont care about warmup!
2013-07-08 17:40:30 +00:00
if ( careAboutWarmup ) {
if ( level . restarted | | g_restarted . integer ! = 0 ) {
2012-08-04 10:54:37 +00:00
trap_Cvar_Set ( " g_restarted " , " 0 " ) ;
level . restarted = qfalse ;
}
}
//RPG-X | Phenix | 21/11/2004
//BOOKMARK FOR INIT
if ( ! alreadyIn ) {
// RPG-X | Phenix | 06/04/2005
ent - > client - > n00bTime = - 1 ;
}
ent - > client - > fraggerTime = - 1 ;
// kef -- should reset all of our awards-related stuff
G_ClearClientLog ( clientNum ) ;
//TiM - if our user's security key was default, transmit the received IP bak to
//the client and get it to encode it into our new key
//Scooter's filter list
2013-07-08 17:40:30 +00:00
if ( Q_stricmp ( ent - > client - > pers . ip , " localhost " ) ! = 0 //localhost
& & Q_strncmp ( ent - > client - > pers . ip , " 10. " , 3 ) ! = 0 //class A
& & Q_strncmp ( ent - > client - > pers . ip , " 172.16. " , 7 ) ! = 0 //class B
& & Q_strncmp ( ent - > client - > pers . ip , " 192.168. " , 8 ) ! = 0 //class C
& & Q_strncmp ( ent - > client - > pers . ip , " 127. " , 4 ) ! = 0 //loopback
& & Q_strncmp ( ent - > client - > pers . ip , " 169.254. " , 8 ) ! = 0 //link-local
2012-08-04 10:54:37 +00:00
)
{
2013-07-08 17:40:30 +00:00
char userInfo [ MAX_TOKEN_CHARS ] ;
uint64_t securityID = 0 ;
memset ( userInfo , 0 , sizeof ( userInfo ) ) ;
2012-08-04 10:54:37 +00:00
trap_GetUserinfo ( clientNum , userInfo , sizeof ( userInfo ) ) ;
2013-07-08 17:40:30 +00:00
if ( userInfo [ 0 ] = = 0 ) {
2012-08-04 10:54:37 +00:00
return ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
securityID = ( uint64_t ) atoul ( Info_ValueForKey ( userInfo , " sv_securityCode " ) ) ;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
if ( ( securityID < = 0 ) | | ( securityID > = 0xffffffff ) ) {
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( clientNum , va ( " configID %s " , ent - > client - > pers . ip ) ) ;
}
}
// send srv change data to ui
if ( ! isBot & & first ) {
2013-07-08 17:40:30 +00:00
if ( level . srvChangeData . count > 0 ) {
2012-08-04 10:54:37 +00:00
G_SendTransData ( clientNum ) ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// send holo data to ui
if ( ! isBot & & first ) {
2013-07-08 17:40:30 +00:00
if ( holoData . numProgs ) {
2012-08-04 10:54:37 +00:00
G_SendHoloData ( clientNum ) ;
2013-07-08 17:40:30 +00:00
}
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
//RPG-X: Marcin: show the server motd - 15/12/2008
if ( ! isBot & & first ) {
trap_SendServerCommand ( ent - > s . number , " motd " ) ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
if ( ! isBot ) {
2013-07-08 17:40:30 +00:00
qboolean last = qfalse ;
int32_t len = 0 ;
fileHandle_t file = 0 ;
char * p = NULL ;
char * q = NULL ;
2013-04-12 23:34:37 +00:00
char buf [ 16000 ] ; // TODO move to heap ?
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
len = trap_FS_FOpenFile ( rpg_motdFile . string , & file , FS_READ ) ;
2013-07-08 17:40:30 +00:00
if ( file = = 0 | | len = = 0 ) {
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( ent - > s . number , va ( " motd_line \" ^1%s not found or empty^7 \" " , rpg_motdFile . string ) ) ;
return ;
}
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
memset ( buf , 0 , sizeof ( buf ) ) ;
2012-08-04 10:54:37 +00:00
trap_FS_Read ( buf , len , file ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
p = & buf [ 0 ] ;
q = p ;
buf [ len ] = ' \0 ' ;
while ( ! last ) {
p = q ;
while ( * q ! = ' \n ' ) {
if ( ! * q ) {
last = qtrue ;
}
if ( ( * q = = ' ' ) & & ( EndWord ( q ) - p ) > 78 ) {
break ;
}
q + + ;
}
* q = ' \0 ' ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( ent - > s . number , va ( " motd_line \" %s \" " , p ) ) ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
q + + ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
}
2013-02-09 23:31:04 +00:00
//we may currently be selfdestructing, so send stuff for that case
selfdestruct = G_Find ( NULL , FOFS ( classname ) , " target_selfdestruct " ) ;
2013-07-08 17:40:30 +00:00
if ( ( selfdestruct ! = NULL ) & & ( ( selfdestruct - > spawnflags & 1 ) ! = 0 ) ) {
2013-02-09 23:31:04 +00:00
trap_SendServerCommand ( ent - > s . number , va ( " selfdestructupdate %i " , selfdestruct - > damage - level . time ) ) ;
2013-07-08 17:40:30 +00:00
} else {
2013-02-09 23:31:04 +00:00
trap_SendServerCommand ( ent - > s . number , va ( " selfdestructupdate %i " , - 1 ) ) ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
}
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
/**
* Get weapons for a class .
*
* \ param client The client .
* \ param pclass Class to get weapons for .
* \ author PHENIX1
*/
static void G_Client_WeaponsForClass ( gclient_t * client , pclass_t pclass )
2012-08-04 10:54:37 +00:00
{
2013-07-08 17:40:30 +00:00
int32_t i = WP_1 ;
int32_t Bits = 0 ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
Bits = ( 1 < < WP_1 ) ;
Bits | = g_classData [ pclass ] . weaponsFlags ;
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
for ( ; i < MAX_WEAPONS ; i + + ) {
2012-08-04 10:54:37 +00:00
//if we want no weapons and aren't an admin, skip this particular weapon
2013-07-08 17:40:30 +00:00
if ( ( rpg_noweapons . integer ! = 0 ) & & ! g_classData [ pclass ] . isAdmin ) {
if ( ( i > = WP_5 ) & & ( i < = WP_10 ) ) {
2012-08-04 10:54:37 +00:00
continue ;
}
}
2013-07-08 17:40:30 +00:00
if ( ( Bits & ( 1 < < i ) ) ! = 0 ) {
2012-08-04 10:54:37 +00:00
client - > ps . stats [ STAT_WEAPONS ] | = ( 1 < < i ) ;
client - > ps . ammo [ i ] = Min_Weapon ( i ) ;
}
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
}
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
/**
* Get holdable items for a class .
*
* \ param client The client .
* \ param pclass Class for which to get holdables .
*/
static void G_Client_HoldablesForClass ( gclient_t * client , pclass_t pclass )
2012-08-04 10:54:37 +00:00
{
2013-07-08 17:40:30 +00:00
if ( g_classData [ pclass ] . isMarine ) {
2012-08-04 10:54:37 +00:00
client - > ps . stats [ STAT_HOLDABLE_ITEM ] = BG_FindItemForHoldable ( HI_TRANSPORTER ) - bg_itemlist ;
2013-07-08 17:40:30 +00:00
} else if ( g_classData [ pclass ] . isAdmin ) {
2012-08-04 10:54:37 +00:00
client - > ps . stats [ STAT_HOLDABLE_ITEM ] = BG_FindItemForHoldable ( HI_SHIELD ) - bg_itemlist ;
2013-07-08 17:40:30 +00:00
}
2012-01-22 21:34:33 +00:00
}
2013-07-08 17:40:30 +00:00
void G_Client_StoreClientInitialStatus ( gentity_t * ent )
2012-08-04 10:54:37 +00:00
{
2013-07-08 17:40:30 +00:00
char userinfo [ MAX_INFO_STRING ] ;
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
if ( clientInitialStatus [ ent - > s . number ] . initialized ) {
//already set
2012-08-04 10:54:37 +00:00
return ;
}
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
//don't store their data if they're just a spectator
2012-08-04 10:54:37 +00:00
return ;
}
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
memset ( userinfo , 0 , sizeof ( userinfo ) ) ;
2012-08-04 10:54:37 +00:00
trap_GetUserinfo ( ent - > s . number , userinfo , sizeof ( userinfo ) ) ;
Q_strncpyz ( clientInitialStatus [ ent - > s . number ] . model , Info_ValueForKey ( userinfo , " model " ) , sizeof ( clientInitialStatus [ ent - > s . number ] . model ) ) ;
clientInitialStatus [ ent - > s . number ] . pClass = ent - > client - > sess . sessionClass ;
clientInitialStatus [ ent - > s . number ] . team = ent - > client - > sess . sessionTeam ;
clientInitialStatus [ ent - > s . number ] . initialized = qtrue ;
ent - > client - > classChangeDebounceTime = 0 ;
}
2012-01-22 21:34:33 +00:00
/*
= = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_Spawn
2012-01-22 21:34:33 +00:00
Called every time a client is placed fresh in the world :
2012-11-15 23:58:56 +00:00
after the first G_Client_Begin , and after each respawn
2012-01-22 21:34:33 +00:00
Initializes all non - persistant parts of playerState
2012-08-04 10:54:37 +00:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Modifyed By : RedTechie
And also by Marcin - 30 / 12 / 2008
2012-01-22 21:34:33 +00:00
= = = = = = = = = = = =
*/
2013-07-08 17:40:30 +00:00
void G_Client_Spawn ( gentity_t * ent , int32_t rpgx_spawn , qboolean fromDeath ) {
int32_t index = 0 ;
int32_t i = 0 ;
int32_t persistant [ MAX_PERSISTANT ] ;
int32_t flags = 0 ;
int32_t savedPing = 0 ;
int32_t cCDT = 0 ;
int32_t clientNum = 0 ;
vec3_t spawn_origin ;
vec3_t spawn_angles ;
gclient_t * client = NULL ;
2012-01-22 21:34:33 +00:00
clientPersistant_t saved ;
clientSession_t savedSess ;
2013-07-08 17:40:30 +00:00
gentity_t * spawnPoint = NULL ;
pclass_t pClass = 0 ;
pclass_t oClass = 0 ;
if ( ent = = NULL ) {
return ;
}
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
memset ( persistant , 0 , sizeof ( persistant ) ) ;
2012-01-22 21:34:33 +00:00
index = ent - g_entities ;
client = ent - > client ;
2012-08-04 10:54:37 +00:00
clientNum = ent - > client - > ps . clientNum ;
2012-01-22 21:34:33 +00:00
// find a spawn point
// do it before setting health back up, so farthest
// ranging doesn't count this client
2012-08-04 10:54:37 +00:00
if ( rpgx_spawn ! = 1 ) { //RPG-X: RedTechie - Make sure the spawn is regular spawn or spawn at current position (rpgx_spawn = current possition)
if ( client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
2013-07-08 17:40:30 +00:00
spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin , spawn_angles ) ;
2012-08-04 10:54:37 +00:00
} else {
do {
// the first spawn should be at a good looking spot
if ( ! client - > pers . initialSpawn & & client - > pers . localClient ) {
client - > pers . initialSpawn = qtrue ;
spawnPoint = SelectInitialSpawnPoint ( spawn_origin , spawn_angles ) ;
} else {
// don't spawn near existing origin if possible
2013-07-08 17:40:30 +00:00
spawnPoint = G_Client_SelectSpawnPoint ( client - > ps . origin , spawn_origin , spawn_angles ) ;
2012-08-04 10:54:37 +00:00
}
// Tim needs to prevent bots from spawning at the initial point
// on q3dm0...
2013-07-08 17:40:30 +00:00
if ( ( ( spawnPoint - > flags & FL_NO_BOTS ) ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) ! = 0 ) ) {
2012-08-04 10:54:37 +00:00
continue ; // try again
}
// just to be symetric, we have a nohumans option...
2013-07-08 17:40:30 +00:00
if ( ( ( spawnPoint - > flags & FL_NO_HUMANS ) ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
2012-08-04 10:54:37 +00:00
continue ; // try again
}
break ;
} while ( 1 ) ;
2012-01-22 21:34:33 +00:00
}
2013-07-08 17:40:30 +00:00
} //RPG-X: RedTechie - End rpgx_spawn check
2012-01-22 21:34:33 +00:00
client - > pers . teamState . state = TEAM_ACTIVE ;
// toggle the teleport bit so the client knows to not lerp
2012-08-04 10:54:37 +00:00
if ( rpgx_spawn ! = 1 ) {
flags = ent - > client - > ps . eFlags & EF_TELEPORT_BIT ;
flags ^ = EF_TELEPORT_BIT ;
}
2012-01-22 21:34:33 +00:00
// clear everything but the persistant data
saved = client - > pers ;
savedSess = client - > sess ;
savedPing = client - > ps . ping ;
for ( i = 0 ; i < MAX_PERSISTANT ; i + + ) {
persistant [ i ] = client - > ps . persistant [ i ] ;
}
2012-08-04 10:54:37 +00:00
//okay, this is hacky, but we need to keep track of this, even if uninitialized first time you spawn, it will be stomped anyway
//RPG-X: RedTechie - Damn thing screwed my function up
if ( rpgx_spawn ! = 1 ) {
2013-07-08 17:40:30 +00:00
if ( client - > classChangeDebounceTime ) {
2012-08-04 10:54:37 +00:00
cCDT = client - > classChangeDebounceTime ;
}
memset ( client , 0 , sizeof ( * client ) ) ;
client - > classChangeDebounceTime = cCDT ;
}
//
2012-01-22 21:34:33 +00:00
client - > pers = saved ;
client - > sess = savedSess ;
client - > ps . ping = savedPing ;
for ( i = 0 ; i < MAX_PERSISTANT ; i + + ) {
client - > ps . persistant [ i ] = persistant [ i ] ;
}
2012-08-04 10:54:37 +00:00
// increment the spawncount so the client will detect the respawn
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-08-04 10:54:37 +00:00
client - > ps . persistant [ PERS_SPAWN_COUNT ] + + ;
client - > airOutTime = level . time + 12000 ;
}
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
if ( client - > sess . sessionTeam ! = TEAM_SPECTATOR ) {
2012-08-04 10:54:37 +00:00
client - > sess . sessionTeam = TEAM_FREE ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
client - > ps . persistant [ PERS_TEAM ] = client - > sess . sessionTeam ;
2012-01-22 21:34:33 +00:00
// clear entity values
client - > ps . stats [ STAT_MAX_HEALTH ] = client - > pers . maxHealth ;
2012-11-11 17:33:50 +00:00
client - > ps . eFlags = flags ;
2012-08-04 10:54:37 +00:00
client - > streakCount = 0 ;
ent - > client - > ps . pm_type = PM_NORMAL ;
2012-01-22 21:34:33 +00:00
ent - > s . groundEntityNum = ENTITYNUM_NONE ;
ent - > client = & level . clients [ index ] ;
ent - > takedamage = qtrue ;
ent - > inuse = qtrue ;
ent - > classname = " player " ;
2012-08-04 10:54:37 +00:00
2012-01-22 21:34:33 +00:00
ent - > r . contents = CONTENTS_BODY ;
2012-08-04 10:54:37 +00:00
2012-01-22 21:34:33 +00:00
ent - > clipmask = MASK_PLAYERSOLID ;
2012-11-15 23:58:56 +00:00
ent - > die = G_Client_Die ;
2012-01-22 21:34:33 +00:00
ent - > waterlevel = 0 ;
ent - > watertype = 0 ;
ent - > flags = 0 ;
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-08-04 10:54:37 +00:00
VectorCopy ( playerMins , ent - > r . mins ) ;
VectorCopy ( playerMaxs , ent - > r . maxs ) ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
client - > ps . clientNum = index ;
2012-01-22 21:34:33 +00:00
// health will count down towards max_health
2012-08-04 10:54:37 +00:00
ent - > health = client - > ps . stats [ STAT_HEALTH ] = client - > ps . stats [ STAT_MAX_HEALTH ] * 1.25 ;
2013-04-12 22:50:57 +00:00
oClass = client - > sess . sessionClass ;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
if ( oClass ! = client - > sess . sessionClass ) {
//need to send the class change
2012-11-15 23:58:56 +00:00
G_Client_UserinfoChanged ( client - > ps . clientNum ) ;
2012-08-04 10:54:37 +00:00
}
client - > ps . persistant [ PERS_CLASS ] = client - > sess . sessionClass ;
pClass = client - > sess . sessionClass ;
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
if ( pClass ! = 0 ) {
//no health boost on spawn for playerclasses
2012-08-04 10:54:37 +00:00
ent - > health = client - > ps . stats [ STAT_HEALTH ] = client - > ps . stats [ STAT_MAX_HEALTH ] = client - > pers . maxHealth ;
}
2013-07-08 17:40:30 +00:00
if ( ! fromDeath | | ( rpg_dropOnDeath . integer = = 0 ) | | ( rpg_allowWeaponDrop . integer = = 0 ) ) {
2012-11-15 23:58:56 +00:00
G_Client_WeaponsForClass ( client , pClass ) ;
2012-08-04 10:54:37 +00:00
} else { // Marcin: just a hand
2012-11-15 23:58:56 +00:00
G_Client_WeaponsForClass ( client , 0 ) ;
2012-08-04 10:54:37 +00:00
}
2012-11-15 23:58:56 +00:00
G_Client_HoldablesForClass ( client , pClass ) ;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-08-04 10:54:37 +00:00
G_SetOrigin ( ent , spawn_origin ) ;
VectorCopy ( spawn_origin , client - > ps . origin ) ;
}
2012-01-22 21:34:33 +00:00
// the respawned flag will be cleared after the attack and jump keys come up
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-08-04 10:54:37 +00:00
client - > ps . pm_flags | = PMF_RESPAWNED ;
}
2012-01-22 21:34:33 +00:00
trap_GetUsercmd ( client - level . clients , & ent - > client - > pers . cmd ) ;
2012-08-04 10:54:37 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-11-15 23:58:56 +00:00
G_Client_SetViewAngle ( ent , spawn_angles ) ;
2012-08-04 10:54:37 +00:00
}
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-11-11 17:33:50 +00:00
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
2012-08-04 10:54:37 +00:00
} else {
G_MoveBox ( ent ) ;
trap_LinkEntity ( ent ) ;
// force the base weapon up
client - > ps . weapon = WP_1 ; //TiM: WP_5
client - > ps . weaponstate = WEAPON_READY ;
}
}
2012-01-22 21:34:33 +00:00
// don't allow full run speed for a bit
client - > ps . pm_flags | = PMF_TIME_KNOCKBACK ;
client - > ps . pm_time = 100 ;
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-08-04 10:54:37 +00:00
client - > respawnTime = level . time ;
}
2012-01-22 21:34:33 +00:00
client - > inactivityTime = level . time + g_inactivity . integer * 1000 ;
client - > latched_buttons = 0 ;
// set default animations
2012-08-04 10:54:37 +00:00
if ( rpgx_spawn ! = 1 ) {
client - > ps . stats [ TORSOANIM ] = BOTH_STAND1 ;
client - > ps . stats [ LEGSANIM ] = BOTH_STAND1 ;
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
if ( level . intermissiontime ) {
MoveClientToIntermission ( ent ) ;
} else {
// fire the targets of the spawn point
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-08-04 10:54:37 +00:00
G_UseTargets ( spawnPoint , ent ) ;
}
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
// select the highest weapon number available, after any
// spawn given items have fired
client - > ps . weapon = 1 ;
2012-01-22 21:34:33 +00:00
2012-08-04 10:54:37 +00:00
//TiM - Always default to the null hand
client - > ps . weapon = WP_1 ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
2012-01-22 21:34:33 +00:00
// run a client frame to drop exactly to the floor,
// initialize animations and other things
client - > ps . commandTime = level . time - 100 ;
ent - > client - > pers . cmd . serverTime = level . time ;
ClientThink ( ent - g_entities ) ;
2012-08-04 10:54:37 +00:00
// positively link the client, even if the command times are weird
if ( ent - > client - > sess . sessionTeam ! = TEAM_SPECTATOR ) {
BG_PlayerStateToEntityState ( & client - > ps , & ent - > s , qtrue ) ;
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 1 ) {
2012-08-04 10:54:37 +00:00
VectorCopy ( ent - > client - > ps . origin , ent - > r . currentOrigin ) ;
}
trap_LinkEntity ( ent ) ;
}
2012-01-22 21:34:33 +00:00
// run the presend to set anything else
ClientEndFrame ( ent ) ;
// clear entity state values
BG_PlayerStateToEntityState ( & client - > ps , & ent - > s , qtrue ) ;
2012-08-04 10:54:37 +00:00
//start-up messages
//FIXME: externalize all this text!
//FIXME: make the gametype titles be graphics!
//FIXME: make it do this on a map_restart also
2013-07-08 17:40:30 +00:00
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
//spectators just get the title of the game
switch ( g_gametype . integer ) {
2012-08-04 10:54:37 +00:00
case GT_FFA : // free for all
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_TOURNAMENT : // one on one tournament
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_SINGLE_PLAYER : // single player tournament
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_TEAM : // team deathmatch
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_CTF : // capture the flag
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
}
2013-07-08 17:40:30 +00:00
} else {
if ( ! clientInitialStatus [ ent - > s . number ] . initialized ) {
//first time coming in
switch ( g_gametype . integer ) {
2012-08-04 10:54:37 +00:00
case GT_FFA : // free for all
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_TOURNAMENT : // one on one tournament
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_SINGLE_PLAYER : // single player tournament
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_TEAM : // team deathmatch
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
case GT_CTF : // capture the flag
trap_SendServerCommand ( ent - g_entities , va ( " cp \" %s \" " , rpg_welcomemessage . string ) ) ;
break ;
}
}
}
2013-07-08 17:40:30 +00:00
if ( rpgx_spawn ! = 0 ) {
if ( ( client - > sess . sessionTeam = = TEAM_SPECTATOR ) | |
2012-08-04 10:54:37 +00:00
g_classData [ client - > sess . sessionClass ] . isMedical | |
2013-07-08 17:40:30 +00:00
g_classData [ client - > sess . sessionClass ] . isAdmin ) {
int32_t l = 0 ;
int32_t numPlayers = 0 ;
int32_t len = 0 ;
int32_t cmdLen = 0 ;
2012-08-04 10:54:37 +00:00
char entry [ 16 ] ;
char command [ 1024 ] ;
2013-07-08 17:40:30 +00:00
gentity_t * player = NULL ;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
memset ( entry , 0 , sizeof ( entry ) ) ;
memset ( command , 0 , sizeof ( entry ) ) ;
2012-08-04 10:54:37 +00:00
2013-07-08 17:40:30 +00:00
for ( ; l < g_maxclients . integer ; l + + )
2012-08-04 10:54:37 +00:00
{
player = g_entities + l ;
2013-07-08 17:40:30 +00:00
if ( ( player = = NULL ) | | ( player = = ent ) | | ! player - > inuse ) {
2012-08-04 10:54:37 +00:00
continue ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
Com_sprintf ( entry , sizeof ( entry ) , " %i %i " , l , player - > health > = 0 ? player - > health : 0 ) ;
len = strlen ( entry ) ;
2013-07-08 17:40:30 +00:00
if ( cmdLen + len > sizeof ( command ) ) {
2012-08-04 10:54:37 +00:00
break ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
strcpy ( command + cmdLen , entry ) ;
cmdLen + = len ;
numPlayers + + ;
}
2013-07-08 17:40:30 +00:00
if ( numPlayers > 0 ) {
2012-08-04 10:54:37 +00:00
trap_SendServerCommand ( clientNum , va ( " hinfo %i%s " , numPlayers , command ) ) ;
2013-07-08 17:40:30 +00:00
}
2012-08-04 10:54:37 +00:00
}
}
//store intial client values
//FIXME: when purposely change teams, this gets confused?
2012-11-15 23:58:56 +00:00
G_Client_StoreClientInitialStatus ( ent ) ;
2012-08-04 10:54:37 +00:00
//RPG-X: Marcin: stuff was here previously - 22/12/2008
2012-01-22 21:34:33 +00:00
}
/*
= = = = = = = = = = =
2012-11-15 23:58:56 +00:00
G_Client_Disconnect
2012-01-22 21:34:33 +00:00
Called when a player drops from the server .
Will not be called between levels .
This should NOT be called directly by any game logic ,
call trap_DropClient ( ) , which will call this and do
server system housekeeping .
= = = = = = = = = = = =
*/
2013-07-08 17:40:30 +00:00
void G_Client_Disconnect ( int32_t clientNum ) {
gentity_t * ent = NULL ;
gentity_t * tent = NULL ;
int32_t i = 0 ;
2012-01-22 21:34:33 +00:00
2013-08-25 21:19:30 +00:00
ent = & g_entities [ clientNum ] ;
if ( ( ent = = NULL ) | | ( ent - > client = = NULL ) ) {
2012-01-22 21:34:33 +00:00
return ;
}
// stop any following clients
for ( i = 0 ; i < level . maxclients ; i + + ) {
if ( level . clients [ i ] . sess . sessionTeam = = TEAM_SPECTATOR
& & level . clients [ i ] . sess . spectatorState = = SPECTATOR_FOLLOW
& & level . clients [ i ] . sess . spectatorClient = = clientNum ) {
StopFollowing ( & g_entities [ i ] ) ;
}
}
2012-08-04 10:54:37 +00:00
//RPG-X: J2J - Stop any dragging.
DragDat [ clientNum ] . AdminId = - 1 ;
DragDat [ clientNum ] . distance = 0 ;
g_entities [ clientNum ] . client - > noclip = qfalse ;
//TiM: Log the player's IP and name. If they reconnect again, it'll announce their deceipt >:)
2013-07-08 17:40:30 +00:00
if ( ( rpg_renamedPlayers . integer ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
int32_t l = 0 ;
qboolean foundName = qfalse ;
2012-08-04 10:54:37 +00:00
//Do a chek to see if this player has disconnected b4. else we'll be wasting a slot.
2013-07-08 17:40:30 +00:00
for ( ; l < MAX_RECON_NAMES ; l + + ) {
if ( g_reconData [ l ] . ipAddress [ 0 ] = = 0 ) {
2012-08-04 10:54:37 +00:00
continue ;
}
2013-07-08 17:40:30 +00:00
if ( Q_stricmp ( ent - > client - > pers . ip , g_reconData [ l ] . ipAddress ) = = 0 ) {
2012-08-04 10:54:37 +00:00
foundName = qtrue ;
break ;
}
}
if ( foundName ) {
2013-08-25 21:19:30 +00:00
memset ( & g_reconData [ l ] , 0 , sizeof ( g_reconData [ l ] ) ) ;
2012-08-04 10:54:37 +00:00
//IP Address
2013-08-25 21:19:30 +00:00
Q_strncpyz ( g_reconData [ l ] . ipAddress , ent - > client - > pers . ip , sizeof ( g_reconData [ l ] . ipAddress ) ) ;
2012-08-04 10:54:37 +00:00
//Player Name
2013-08-25 21:19:30 +00:00
Q_strncpyz ( g_reconData [ l ] . previousName , ent - > client - > pers . netname , sizeof ( g_reconData [ l ] . previousName ) ) ;
2013-07-08 17:40:30 +00:00
} else {
2012-08-04 10:54:37 +00:00
memset ( & g_reconData [ g_reconNum ] , 0 , sizeof ( g_reconData [ g_reconNum ] ) ) ;
//IP Address
Q_strncpyz ( g_reconData [ g_reconNum ] . ipAddress , ent - > client - > pers . ip , sizeof ( g_reconData [ g_reconNum ] . ipAddress ) ) ;
//Player Name
Q_strncpyz ( g_reconData [ g_reconNum ] . previousName , ent - > client - > pers . netname , sizeof ( g_reconData [ g_reconNum ] . previousName ) ) ;
g_reconNum + + ;
//cap reconNum just in case.
if ( g_reconNum > = MAX_RECON_NAMES ) {
g_reconNum = 0 ;
}
}
}
2012-01-22 21:34:33 +00:00
// send effect if they were completely connected
2013-07-08 17:40:30 +00:00
if ( ( ent - > client - > pers . connected = = CON_CONNECTED )
& & ( ent - > client - > sess . sessionTeam ! = TEAM_SPECTATOR ) ) {
2012-08-04 10:54:37 +00:00
vec3_t org ;
VectorCopy ( ent - > client - > ps . origin , org ) ;
org [ 2 ] + = ( ent - > client - > ps . viewheight > > 1 ) ;
tent = G_TempEntity ( org , EV_PLAYER_TELEPORT_OUT ) ;
2012-01-22 21:34:33 +00:00
tent - > s . clientNum = ent - > s . clientNum ;
// They don't get to take powerups with them!
// Especially important for stuff like CTF flags
2012-08-04 10:54:37 +00:00
TossClientItems ( ent , qtrue ) ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
G_LogPrintf ( " ClientDisconnect: %i (%s) \n " , clientNum , g_entities [ clientNum ] . client - > pers . ip ) ;
2012-01-22 21:34:33 +00:00
// if we are playing in tourney mode and losing, give a win to the other player
2013-07-08 17:40:30 +00:00
if ( ( g_gametype . integer = = GT_TOURNAMENT ) & & ( level . intermissiontime = = 0 )
& & ( level . warmupTime = = 0 ) & & ( level . sortedClients [ 1 ] = = clientNum ) ) {
2012-01-22 21:34:33 +00:00
level . clients [ level . sortedClients [ 0 ] ] . sess . wins + + ;
2012-11-15 23:58:56 +00:00
G_Client_UserinfoChanged ( level . sortedClients [ 0 ] ) ;
2012-01-22 21:34:33 +00:00
}
2013-07-08 17:40:30 +00:00
if ( ( g_gametype . integer = = GT_TOURNAMENT ) & & ( ent - > client - > sess . sessionTeam = = TEAM_FREE ) & & ( level . intermissiontime ! = 0 ) ) {
2013-08-25 21:24:25 +00:00
trap_SendConsoleCommand ( EXEC_APPEND , " map_restart 0 \n " ) ;
2012-01-22 21:34:33 +00:00
level . restarted = qtrue ;
level . intermissiontime = 0 ;
}
trap_UnlinkEntity ( ent ) ;
2012-08-04 10:54:37 +00:00
memset ( ent , 0 , sizeof ( ent ) ) ;
2012-01-22 21:34:33 +00:00
ent - > s . modelindex = 0 ;
ent - > inuse = qfalse ;
ent - > classname = " disconnected " ;
ent - > client - > pers . connected = CON_DISCONNECTED ;
ent - > client - > ps . persistant [ PERS_TEAM ] = TEAM_FREE ;
2012-08-04 10:54:37 +00:00
ent - > client - > ps . persistant [ PERS_CLASS ] = 0 ; //PC_NOCLASS;
2012-01-22 21:34:33 +00:00
ent - > client - > sess . sessionTeam = TEAM_FREE ;
2012-08-04 10:54:37 +00:00
ent - > client - > sess . sessionClass = 0 ; //PC_NOCLASS;
2012-01-22 21:34:33 +00:00
trap_SetConfigstring ( CS_PLAYERS + clientNum , " " ) ;
2012-11-15 23:58:56 +00:00
G_Client_CalculateRanks ( qfalse ) ;
2012-01-22 21:34:33 +00:00
2013-07-08 17:40:30 +00:00
if ( ( ent - > r . svFlags & SVF_BOT ) ! = 0 ) {
2012-08-04 10:54:37 +00:00
BotAIShutdownClient ( clientNum ) ;
2012-01-22 21:34:33 +00:00
}
2012-08-04 10:54:37 +00:00
// kef -- if this guy contributed to any of our kills/deaths/weapons logs, clean 'em out
G_ClearClientLog ( clientNum ) ;
//also remove any initial data
clientInitialStatus [ clientNum ] . initialized = qfalse ;
}
2012-01-22 21:34:33 +00:00
2012-11-11 21:41:22 +00:00
/*
= = = = = = = = = = = = = = = =
IsAdmin
RPG - X | Phenix | 21 / 11 / 2004
= = = = = = = = = = = = = = = =
*/
/**
* Checks if player is an admin .
* \ param ent the player
*
* \ author Ubergames - Phenix
* \ date 21 / 11 / 2004
*/
qboolean IsAdmin ( gentity_t * ent )
{
2013-07-08 18:27:33 +00:00
if ( ent = = NULL ) {
2012-11-11 21:41:22 +00:00
return qfalse ;
2013-07-08 18:27:33 +00:00
}
2012-11-11 21:41:22 +00:00
2013-07-08 18:27:33 +00:00
if ( ent - > client = = NULL ) {
2012-11-11 21:41:22 +00:00
return qfalse ;
2013-07-08 18:27:33 +00:00
}
2012-11-11 21:41:22 +00:00
if ( ( g_classData [ ent - > client - > sess . sessionClass ] . isAdmin ) | |
( ent - > client - > LoggedAsAdmin = = qtrue ) | |
2013-07-08 18:27:33 +00:00
( ent - > client - > LoggedAsDeveloper = = qtrue ) ) {
2012-11-11 21:41:22 +00:00
return qtrue ;
} else {
return qfalse ;
}
}
2012-11-15 23:02:59 +00:00
/*
= = = = = = = = = = =
G_Client_GetLocation
Report a location for the player . Uses placed nearby target_location entities
= = = = = = = = = = = =
*/
2013-07-08 18:42:21 +00:00
static gentity_t * G_Client_GetLocation ( gentity_t * ent ) {
gentity_t * eloc = NULL ;
gentity_t * best = NULL ;
double bestlen = 0.0 ;
double len = 0.0 ;
2012-11-15 23:02:59 +00:00
vec3_t origin ;
bestlen = 3 * 8192.0 * 8192.0 ;
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 ;
}
/*
= = = = = = = = = = =
G_Client_GetLocationMsg
Report a location for the player . Uses placed nearby target_location entities
= = = = = = = = = = = =
*/
2013-07-08 18:42:21 +00:00
qboolean G_Client_GetLocationMsg ( gentity_t * ent , char * loc , int32_t loclen )
2012-11-15 23:02:59 +00:00
{
2013-07-08 18:42:21 +00:00
gentity_t * best = NULL ;
2012-11-15 23:02:59 +00:00
best = G_Client_GetLocation ( ent ) ;
2013-07-08 18:42:21 +00:00
if ( best = = NULL ) {
2012-11-15 23:02:59 +00:00
return qfalse ;
2013-07-08 18:42:21 +00:00
}
2012-11-15 23:02:59 +00:00
2013-07-08 18:42:21 +00:00
if ( best - > count ! = 0 ) {
2012-11-15 23:02:59 +00:00
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 ) ;
2013-07-08 18:42:21 +00:00
} else {
2012-11-15 23:02:59 +00:00
Com_sprintf ( loc , loclen , " %s " , best - > message ) ;
2013-07-08 18:42:21 +00:00
}
2012-11-15 23:02:59 +00:00
return qtrue ;
}
/*
= = = = = = = = = = = = = = = = = =
G_Client_CheckHealthInfoMessage
Sends Health Changes to proper clients
Format :
clientNum health
= = = = = = = = = = = = = = = = = =
*/
2013-07-08 18:42:21 +00:00
static void G_Client_CheckHealthInfoMessage ( void ) {
2012-11-15 23:02:59 +00:00
char entry [ 1024 ] ;
char string [ 1400 ] ;
2013-07-08 18:42:21 +00:00
int32_t stringlength = 0 ;
int32_t i = 0 ;
int32_t j = 0 ;
int32_t t = 0 ;
int32_t sendToCnt = 0 ;
int32_t cnt = 0 ;
int32_t sentCnt = 0 ;
int32_t h = 0 ;
int32_t clients [ MAX_CLIENTS ] ;
int32_t sendToClients [ MAX_CLIENTS ] ;
gentity_t * player = NULL ;
gentity_t * ent = NULL ;
memset ( entry , 0 , sizeof ( entry ) ) ;
memset ( string , 0 , sizeof ( string ) ) ;
memset ( clients , 0 , sizeof ( clients ) ) ;
memset ( sendToClients , 0 , sizeof ( sendToClients ) ) ;
2012-11-15 23:02:59 +00:00
//only send this to medics or spectators or adminz
2013-07-08 18:42:21 +00:00
for ( ; i < g_maxclients . integer ; i + + ) {
2012-11-15 23:02:59 +00:00
2013-07-08 18:42:21 +00:00
if ( ( level . clients [ i ] . pers . connected = = CON_CONNECTED ) & & ( level . clients [ i ] . ps . stats [ STAT_HEALTH ] > 0 ) & & //make sure they've actually spawned in already
( ( level . clients [ i ] . sess . sessionTeam = = TEAM_SPECTATOR ) | | ( g_classData [ level . clients [ i ] . sess . sessionClass ] . isMedical ) | | ( g_classData [ level . clients [ i ] . sess . sessionClass ] . isAdmin ) ) ) {
2012-11-15 23:02:59 +00:00
sendToClients [ sendToCnt + + ] = i ;
}
}
2013-07-08 18:42:21 +00:00
if ( sendToCnt = = 0 ) {
//no-one to send to
2012-11-15 23:02:59 +00:00
return ;
}
//only send those clients whose health has changed this cycle
//NB: there's a prob with client 0 in here....
2013-07-08 18:42:21 +00:00
for ( i = 0 , cnt = 0 ; i < g_maxclients . integer ; i + + ) {
2012-11-15 23:02:59 +00:00
player = g_entities + i ;
2013-07-08 18:42:21 +00:00
if ( ( player ! = NULL ) & & player - > inuse & & ( player - > old_health ! = player - > health ) & & ( ( player - > health > 0 ) | | ( player - > old_health > 0 ) ) ) {
2012-11-15 23:02:59 +00:00
clients [ cnt + + ] = i ;
player - > old_health = player - > health ;
}
}
2013-07-08 18:42:21 +00:00
if ( cnt = = 0 ) {
//no-one relevant changed health
2012-11-15 23:02:59 +00:00
return ;
}
2013-07-08 18:42:21 +00:00
for ( t = 0 ; t < sendToCnt ; t + + ) {
2012-11-15 23:02:59 +00:00
ent = g_entities + sendToClients [ t ] ;
sentCnt = 0 ;
// send the latest information on all clients
string [ 0 ] = 0 ;
stringlength = 0 ;
2013-07-08 18:42:21 +00:00
for ( i = 0 ; i < cnt ; i + + ) {
2012-11-15 23:02:59 +00:00
player = g_entities + clients [ i ] ;
2013-07-08 18:42:21 +00:00
if ( ent = = player ) { //don't send the ent his own health
2012-11-15 23:02:59 +00:00
continue ;
}
//send this one
sentCnt + + ;
h = player - > health ;
if ( h < 0 ) h = 0 ;
Com_sprintf ( entry , sizeof ( entry ) , " %i %i " , clients [ i ] , h ) ;
j = strlen ( entry ) ;
if ( stringlength + j > sizeof ( string ) )
break ;
strcpy ( string + stringlength , entry ) ;
stringlength + = j ;
}
2013-07-08 18:42:21 +00:00
if ( sentCnt ! = 0 ) {
2012-11-15 23:02:59 +00:00
trap_SendServerCommand ( sendToClients [ t ] , va ( " hinfo %i%s " , sentCnt , string ) ) ;
}
}
}
2013-07-08 18:42:21 +00:00
/**
* Send client location information .
*
* \ param ent The client .
*/
static void G_Client_LocationsMessage ( gentity_t * ent ) {
2012-11-15 23:02:59 +00:00
char entry [ 1024 ] ;
char string [ 1400 ] ;
2013-07-08 18:42:21 +00:00
int32_t stringlength = 0 ;
int32_t i = 0 ;
int32_t j = 0 ;
int32_t cnt = 0 ;
gentity_t * player = NULL ;
2012-11-15 23:02:59 +00:00
//don't bother sending during intermission?
2013-07-08 18:42:21 +00:00
if ( level . intermissiontime ! = 0 ) {
2012-11-15 23:02:59 +00:00
return ;
2013-07-08 18:42:21 +00:00
}
memset ( entry , 0 , sizeof ( entry ) ) ;
memset ( string , 0 , sizeof ( string ) ) ;
2012-11-15 23:02:59 +00:00
// 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)
2013-07-08 18:42:21 +00:00
for ( ; i < g_maxclients . integer & & cnt < TEAM_MAXOVERLAY ; i + + ) {
2012-11-15 23:02:59 +00:00
player = g_entities + level . sortedClients [ i ] ;
2013-07-08 18:42:21 +00:00
if ( ( player ! = NULL ) & & ( player - > inuse ) & & ( player - > client - > sess . sessionTeam = = ent - > client - > sess . sessionTeam ) ) {
// TODO huh? remove?
2012-11-15 23:02:59 +00:00
}
}
// send the latest information on all clients
for ( i = 0 , cnt = 0 ; i < g_maxclients . integer & & cnt < TEAM_MAXOVERLAY ; i + + ) {
player = g_entities + i ;
//RPG-X | Phenix | 05/03/2005
2013-07-08 18:42:21 +00:00
if ( ( player ! = NULL ) & & player - > inuse ) {
2012-11-15 23:02:59 +00:00
//to counter for the fact we could pwn the server doing this, remove all superfluous data
Com_sprintf ( entry , sizeof ( entry ) , " %i %i " , i , player - > client - > pers . teamState . location ) ;
j = strlen ( entry ) ;
2013-07-08 18:42:21 +00:00
if ( stringlength + j > sizeof ( string ) ) {
2012-11-15 23:02:59 +00:00
break ;
2013-07-08 18:42:21 +00:00
}
2012-11-15 23:02:59 +00:00
strcpy ( string + stringlength , entry ) ;
stringlength + = j ;
cnt + + ;
}
}
trap_SendServerCommand ( ent - g_entities , va ( " tinfo %i%s " , cnt , string ) ) ;
}
2013-07-08 18:47:39 +00:00
//TiM - Modified to work with RPG-X
void G_Client_CheckClientStatus ( void ) {
gentity_t * loc = NULL ;
gentity_t * ent = NULL ;
if ( level . time - level . lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME ) {
2013-08-13 19:45:20 +00:00
int32_t i = 0 ;
2013-07-08 18:47:39 +00:00
level . lastTeamLocationTime = level . time ;
for ( ; i < g_maxclients . integer ; i + + ) {
ent = g_entities + i ;
if ( ( ent ! = NULL ) & & ( ent - > inuse ) ) {
loc = G_Client_GetLocation ( ent ) ;
if ( loc ! = NULL ) {
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 ! = NULL ) & & ( ent - > inuse ) ) {
G_Client_LocationsMessage ( ent ) ;
}
}
G_Client_CheckHealthInfoMessage ( ) ;
}
}
2013-07-08 18:53:00 +00:00
/*
= = = = = = = = = = = =
G_Client_AddScore
Adds score to both the client and his team
= = = = = = = = = = = =
*/
void G_Client_AddScore ( gentity_t * ent , int score ) {
if ( ! ent )
{
return ;
}
if ( ! ent - > client ) {
return ;
}
if ( ! ent - > client - > UpdateScore )
{
return ;
}
ent - > client - > ps . persistant [ PERS_SCORE ] + = score ;
//don't add score to team score during elimination
if ( g_gametype . integer = = GT_TEAM )
{ //this isn't capture score
level . teamScores [ ent - > client - > ps . persistant [ PERS_TEAM ] ] + = score ;
}
G_Client_CalculateRanks ( qfalse ) ;
//RPG-X: RedTechie - Lets enable score updating without this scores will not be updated
ent - > client - > UpdateScore = qfalse ;
}
/*
= = = = = = = = = = = = =
SortRanks
= = = = = = = = = = = = =
*/
static int QDECL SortRanks ( const void * a , const void * b ) {
gclient_t * ca , * cb ;
ca = & level . clients [ * ( int * ) a ] ;
cb = & level . clients [ * ( int * ) b ] ;
// sort special clients last
if ( ca - > sess . spectatorState = = SPECTATOR_SCOREBOARD | | ca - > sess . spectatorClient < 0 ) {
return 1 ;
}
if ( cb - > sess . spectatorState = = SPECTATOR_SCOREBOARD | | cb - > sess . spectatorClient < 0 ) {
return - 1 ;
}
// then connecting clients
if ( ca - > pers . connected = = CON_CONNECTING ) {
return 1 ;
}
if ( cb - > pers . connected = = CON_CONNECTING ) {
return - 1 ;
}
// then spectators
if ( ca - > sess . sessionTeam = = TEAM_SPECTATOR & & cb - > sess . sessionTeam = = TEAM_SPECTATOR ) {
if ( ca - > sess . spectatorTime < cb - > sess . spectatorTime ) {
return - 1 ;
}
if ( ca - > sess . spectatorTime > cb - > sess . spectatorTime ) {
return 1 ;
}
return 0 ;
}
if ( ca - > sess . sessionTeam = = TEAM_SPECTATOR ) {
return 1 ;
}
if ( cb - > sess . sessionTeam = = TEAM_SPECTATOR ) {
return - 1 ;
}
// then sort by score & number of times killed
if ( ca - > ps . persistant [ PERS_SCORE ]
> cb - > ps . persistant [ PERS_SCORE ] ) {
return - 1 ;
}
if ( ( ca - > ps . persistant [ PERS_SCORE ] = = cb - > ps . persistant [ PERS_SCORE ] ) & &
( ca - > ps . persistant [ PERS_KILLED ] < cb - > ps . persistant [ PERS_KILLED ] ) )
{ return - 1 ; }
if ( ca - > ps . persistant [ PERS_SCORE ]
< cb - > ps . persistant [ PERS_SCORE ] ) {
return 1 ;
}
if ( ( ca - > ps . persistant [ PERS_SCORE ] = = cb - > ps . persistant [ PERS_SCORE ] ) & &
( ca - > ps . persistant [ PERS_KILLED ] > cb - > ps . persistant [ PERS_KILLED ] ) )
{ return 1 ; }
return 0 ;
}
/*
= = = = = = = = = = = =
G_Client_CalculateRanks
Recalculates the score ranks of all players
This will be called on every client connect , begin , disconnect , death ,
and team change .
FIXME : for elimination , the last man standing must be ranked first
= = = = = = = = = = = =
*/
void G_Client_CalculateRanks ( qboolean fromExit ) {
2013-08-13 19:45:20 +00:00
int32_t i = 0 ;
2013-07-08 18:53:00 +00:00
gclient_t * cl ;
level . follow1 = - 1 ;
level . follow2 = - 1 ;
level . numConnectedClients = 0 ;
level . numNonSpectatorClients = 0 ;
level . numPlayingClients = 0 ;
level . numVotingClients = 0 ; // don't count bots
2013-08-13 19:45:20 +00:00
for ( ; i < level . maxclients ; i + + ) {
2013-07-08 18:53:00 +00:00
if ( level . clients [ i ] . pers . connected ! = CON_DISCONNECTED ) {
level . sortedClients [ level . numConnectedClients ] = i ;
level . numConnectedClients + + ;
if ( level . clients [ i ] . sess . sessionTeam ! = TEAM_SPECTATOR ) {
level . numNonSpectatorClients + + ;
// decide if this should be auto-followed
if ( level . clients [ i ] . pers . connected = = CON_CONNECTED ) {
level . numPlayingClients + + ;
if ( ( g_entities [ i ] . r . svFlags & SVF_BOT ) = = 0 ) {
level . numVotingClients + + ;
}
if ( level . follow1 = = - 1 ) {
level . follow1 = i ;
} else if ( level . follow2 = = - 1 ) {
level . follow2 = i ;
}
}
}
}
}
qsort ( level . sortedClients , ( size_t ) level . numConnectedClients ,
sizeof ( level . sortedClients [ 0 ] ) , SortRanks ) ;
// set the rank value for all clients that are connected and not spectators
if ( g_gametype . integer > = GT_TEAM ) {
// in team games, rank is just the order of the teams, 0=red, 1=blue, 2=tied
for ( i = 0 ; i < level . numConnectedClients ; i + + ) {
cl = & level . clients [ level . sortedClients [ i ] ] ;
if ( level . teamScores [ TEAM_RED ] = = level . teamScores [ TEAM_BLUE ] ) {
cl - > ps . persistant [ PERS_RANK ] = 2 ;
} else if ( level . teamScores [ TEAM_RED ] > level . teamScores [ TEAM_BLUE ] ) {
cl - > ps . persistant [ PERS_RANK ] = 0 ;
} else {
cl - > ps . persistant [ PERS_RANK ] = 1 ;
}
}
} else {
2013-08-13 19:45:20 +00:00
int32_t rank = - 1 ;
int32_t score = 0 ;
2013-07-08 18:53:00 +00:00
for ( i = 0 ; i < level . numPlayingClients ; i + + ) {
2013-08-13 19:45:20 +00:00
int32_t newScore = 0 ;
2013-07-08 18:53:00 +00:00
cl = & level . clients [ level . sortedClients [ i ] ] ;
newScore = cl - > ps . persistant [ PERS_SCORE ] ;
if ( i = = 0 | | newScore ! = score ) {
rank = i ;
// assume we aren't tied until the next client is checked
level . clients [ level . sortedClients [ i ] ] . ps . persistant [ PERS_RANK ] = rank ;
} else {
// we are tied with the previous client
level . clients [ level . sortedClients [ i - 1 ] ] . ps . persistant [ PERS_RANK ] = rank | RANK_TIED_FLAG ;
level . clients [ level . sortedClients [ i ] ] . ps . persistant [ PERS_RANK ] = rank | RANK_TIED_FLAG ;
}
score = newScore ;
if ( g_gametype . integer = = GT_SINGLE_PLAYER & & level . numPlayingClients = = 1 ) {
level . clients [ level . sortedClients [ i ] ] . ps . persistant [ PERS_RANK ] = rank | RANK_TIED_FLAG ;
}
}
}
// set the CS_SCORES1/2 configstrings, which will be visible to everyone
if ( g_gametype . integer > = GT_TEAM ) {
trap_SetConfigstring ( CS_SCORES1 , va ( " %i " , level . teamScores [ TEAM_RED ] ) ) ;
trap_SetConfigstring ( CS_SCORES2 , va ( " %i " , level . teamScores [ TEAM_BLUE ] ) ) ;
} else {
if ( level . numConnectedClients = = 0 ) {
trap_SetConfigstring ( CS_SCORES1 , va ( " %i " , SCORE_NOT_PRESENT ) ) ;
trap_SetConfigstring ( CS_SCORES2 , va ( " %i " , SCORE_NOT_PRESENT ) ) ;
} else if ( level . numConnectedClients = = 1 ) {
trap_SetConfigstring ( CS_SCORES1 , va ( " %i " , level . clients [ level . sortedClients [ 0 ] ] . ps . persistant [ PERS_SCORE ] ) ) ;
trap_SetConfigstring ( CS_SCORES2 , va ( " %i " , SCORE_NOT_PRESENT ) ) ;
} else {
trap_SetConfigstring ( CS_SCORES1 , va ( " %i " , level . clients [ level . sortedClients [ 0 ] ] . ps . persistant [ PERS_SCORE ] ) ) ;
trap_SetConfigstring ( CS_SCORES2 , va ( " %i " , level . clients [ level . sortedClients [ 1 ] ] . ps . persistant [ PERS_SCORE ] ) ) ;
}
}
// if we are at the intermission, send the new info to everyone
if ( level . intermissiontime ! = 0 ) {
SendScoreboardMessageToAllClients ( ) ;
}
}
2013-08-13 19:56:47 +00:00
void G_Client_UpdateSoundZones ( void ) {
2013-07-21 13:24:57 +00:00
int32_t i = 0 ;
int32_t b = 0 ;
int32_t entlist [ MAX_GENTITIES ] ;
int32_t zones [ MAX_CLIENTS ] ;
int32_t count = 0 ;
char supdate [ MAX_STRING_CHARS ] ;
memset ( & zones , 0 , sizeof ( zones ) ) ;
memset ( & supdate , 0 , sizeof ( supdate ) ) ;
2013-07-08 18:47:39 +00:00
2013-07-21 13:24:57 +00:00
for ( ; i < MAX_GENTITIES ; i + + ) {
if ( ( g_entities [ i ] . type = = ENT_TARGET_ZONE ) & & ( g_entities [ i ] . count = = 3 ) ) {
memset ( & entlist , 0 , sizeof ( entlist ) ) ;
count = trap_EntitiesInBox ( g_entities [ i ] . r . mins , g_entities [ i ] . r . maxs , & entlist , MAX_GENTITIES ) ;
2012-11-15 23:02:59 +00:00
2013-07-21 13:24:57 +00:00
for ( b = 0 ; b < count ; b + + ) {
if ( g_entities [ entlist [ b ] ] . client ! = NULL ) {
zones [ g_entities [ entlist [ b ] ] . client - > ps . clientNum ] = g_entities [ i ] . s . number ;
}
}
}
}
2012-11-11 21:41:22 +00:00
2013-07-21 13:24:57 +00:00
for ( i = 0 ; i < MAX_CLIENTS ; i + + ) {
if ( strlen ( supdate ) ! = 0 ) {
Com_sprintf ( supdate , sizeof ( supdate ) , " %s \\ c%d \\ %d " , supdate , i , zones [ i ] ) ;
} else {
Com_sprintf ( supdate , sizeof ( supdate ) , " cd% \\ %d " , i , zones [ i ] ) ;
}
}
2012-11-15 23:58:56 +00:00
2013-07-21 13:24:57 +00:00
for ( i = 0 ; i < MAX_GENTITIES ; i + + ) {
if ( g_entities [ i ] . client ! = NULL ) {
trap_SendServerCommand ( i , va ( " slup %s " , supdate ) ) ;
}
}
}