2019-02-15 23:49:31 +00:00
// Copyright (C) 1999-2000 Id Software, Inc.
//
# include "g_client.h"
# include "g_local.h"
# include "g_groups.h"
# include "g_main.h"
# include "g_cmds.h"
# include "g_main.h"
# include "g_spawn.h"
# include "g_items.h"
# include "g_lua.h"
# include "g_logger.h"
# include "g_syscalls.h"
# include "ai_common.h"
// predeclare ai main functions
extern int32_t AI_main_BotAISetupClient ( int32_t client , bot_settings_t * settings ) ;
extern int32_t AI_main_BotAIShutdownClient ( int32_t client ) ;
enum g_clientLimits_e {
MAX_SPAWN_POINTS = 256
} ;
reconData_t g_reconData [ MAX_RECON_NAMES ] ; //!< recon data for a limited ammount of clients
int32_t g_reconNum ;
// g_client.c -- client functions that don't happen every frame
void G_Client_StoreClientInitialStatus ( gentity_t * ent ) ;
//! 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}
clInitStatus_t clientInitialStatus [ MAX_CLIENTS ] ;
//TiM: For easier transport setup
/**
* Function that makes transport setup easier
* \ author Ubergames - TiM
*/
void G_InitTransport ( int32_t clientNum , vec3_t origin , vec3_t angles ) {
gentity_t * tent = NULL ;
TransDat [ clientNum ] . beamTime = level . time + 8000 ;
g_entities [ clientNum ] . client - > ps . powerups [ PW_BEAM_OUT ] = level . time + 8000 ;
//Transfer stored data to active beamer
VectorCopy ( origin , TransDat [ clientNum ] . currentCoord . origin ) ;
VectorCopy ( angles , TransDat [ clientNum ] . currentCoord . angles ) ;
tent = G_TempEntity ( g_entities [ clientNum ] . client - > ps . origin , EV_PLAYER_TRANSPORT_OUT ) ;
tent - > s . clientNum = clientNum ;
}
/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) INITIAL
- - - - - DESCRIPTION - - - - -
potential spawning position for deathmatch games .
- - - - - SPAWNFLAGS - - - - -
1 : INITIAL - Preferred spawn for the first spawn of a client when entering a match .
- - - - - 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 .
*/
/**
* Spawn function for deathmatch spawnpoint
*/
void SP_info_player_deathmatch ( gentity_t * ent ) {
int32_t i = 0 ;
ent - > type = EntityType : : ENT_INFO_PLAYER_START ;
if ( strcmp ( ent - > classname , " info_player_deathmatch " ) ! = 0 ) {
ent - > classname = " info_player_deathmatch " ;
}
G_SpawnInt ( " nobots " , " 0 " , & i ) ;
if ( i ! = 0 ) {
ent - > flags | = FL_NO_BOTS ;
}
G_SpawnInt ( " nohumans " , " 0 " , & i ) ;
if ( i ! = 0 ) {
ent - > flags | = FL_NO_HUMANS ;
}
trap_LinkEntity ( ent ) ;
}
/*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
- - - - - 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
*/
/**
* Spawn function for intermission entity .
*/
void SP_info_player_intermission ( gentity_t * ent ) {
ent - > type = EntityType : : ENT_INFO_PLAYER_INTERMISSION ;
}
/**
* 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 ;
int touch [ MAX_GENTITIES ] ;
gentity_t * hit = NULL ;
vec3_t mins ;
vec3_t maxs ;
memset ( touch , 0 , sizeof ( touch ) ) ;
VectorAdd ( spot - > s . origin , playerMins , mins ) ;
VectorAdd ( spot - > s . origin , playerMaxs , maxs ) ;
num = trap_EntitiesInBox ( mins , maxs , touch , MAX_GENTITIES ) ;
for ( ; i < num ; i + + ) {
hit = & g_entities [ touch [ i ] ] ;
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
return qtrue ;
}
}
return qfalse ;
}
/*
= = = = = = = = = = = = = = = =
SelectNearestDeathmatchSpawnPoint
Find the spot that we DON ' T want to use
= = = = = = = = = = = = = = = =
*/
/**
* Find the spot that we DON ' T want to use
*/
static gentity_t * SelectNearestDeathmatchSpawnPoint ( vec3_t from ) {
gentity_t * spot = NULL ;
vec3_t delta ;
double dist = 0.0 ;
double nearestDist = 999999.0 ;
gentity_t * nearestSpot = NULL ;
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
= = = = = = = = = = = = = = = =
*/
/**
* go to a random point that doesn ' t telefrag
*/
static gentity_t * SelectRandomDeathmatchSpawnPoint ( void ) {
gentity_t * spot = NULL ;
int32_t count = 0 ;
int32_t selection = 0 ;
gentity_t * spots [ MAX_SPAWN_POINTS ] ;
memset ( spots , 0 , sizeof ( spots ) ) ;
while ( ( spot = G_Find ( spot , FOFS ( classname ) , " info_player_deathmatch " ) ) ! = NULL ) {
if ( G_Client_SpotWouldTelefrag ( spot ) ) {
continue ;
}
spots [ count ] = spot ;
count + + ;
}
if ( count = = 0 ) { // no spots that won't telefrag
return G_Find ( NULL , FOFS ( classname ) , " info_player_deathmatch " ) ;
}
selection = rand ( ) % count ;
return spots [ selection ] ;
}
gentity_t * G_Client_SelectSpawnPoint ( vec3_t avoidPoint , vec3_t origin , vec3_t angles ) {
gentity_t * spot = NULL ;
gentity_t * nearestSpot = SelectNearestDeathmatchSpawnPoint ( avoidPoint ) ;
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 ( ) ;
}
}
// find a single player start spot
if ( spot = = NULL ) {
G_Error ( " Couldn't find a spawn point " ) ;
return spot ;
}
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 .
= = = = = = = = = = = =
*/
/**
* Try to find a spawn point marked ' initial ' , otherwise
* use normal spawn selection .
*/
static gentity_t * SelectInitialSpawnPoint ( vec3_t origin , vec3_t angles ) {
gentity_t * spot = NULL ;
spot = NULL ;
while ( ( spot = G_Find ( spot , FOFS ( classname ) , " info_player_deathmatch " ) ) ! = NULL ) {
if ( spot - > spawnflags & 1 ) {
break ;
}
}
if ( spot = = NULL | | G_Client_SpotWouldTelefrag ( spot ) ) {
return G_Client_SelectSpawnPoint ( vec3_origin , origin , angles ) ;
}
VectorCopy ( spot - > s . origin , origin ) ;
origin [ 2 ] + = 9 ;
VectorCopy ( spot - > s . angles , angles ) ;
return spot ;
}
/*
= = = = = = = = = = =
SelectSpectatorSpawnPoint
= = = = = = = = = = = =
*/
static gentity_t * SelectSpectatorSpawnPoint ( vec3_t origin , vec3_t angles ) {
FindIntermissionPoint ( ) ;
VectorCopy ( level . intermission_origin , origin ) ;
VectorCopy ( level . intermission_angle , angles ) ;
return NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
BODYQUE
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
static int32_t bodyFadeSound = 0 ;
/*
= = = = = = = = = = = = = = =
G_Client_InitBodyQue
= = = = = = = = = = = = = = =
*/
void G_Client_InitBodyQue ( void ) {
int32_t i = 0 ;
gentity_t * ent = NULL ;
level . bodyQueIndex = 0 ;
for ( ; i < BODY_QUEUE_SIZE ; i + + ) {
ent = G_Spawn ( ) ;
if ( ent = = NULL ) {
// TODO print error?
return ;
}
ent - > classname = " bodyque " ;
ent - > neverFree = qtrue ;
level . bodyQue [ i ] = ent ;
}
if ( bodyFadeSound = = 0 )
{ // Initialize this sound.
bodyFadeSound = G_SoundIndex ( " sound/enemies/borg/walkthroughfield.wav " ) ;
}
}
/*
= = = = = = = = = = = = =
BodyRezOut
After sitting around for five seconds , fade out .
= = = = = = = = = = = = =
*/
/**
* After sitting around for five seconds , fade out .
*/
static void BodyRezOut ( gentity_t * ent ) {
if ( level . time - ent - > timestamp > = 7500 ) {
// the body ques are never actually freed, they are just unlinked
trap_UnlinkEntity ( ent ) ;
ent - > physicsObject = qfalse ;
return ;
}
ent - > nextthink = level . time + 2500 ;
ent - > s . time = level . time + 2500 ;
G_AddEvent ( ent , EV_GENERAL_SOUND , bodyFadeSound ) ;
}
/*
= = = = = = = = = = = = =
CopyToBodyQue
A player is respawning , so make an entity that looks
just like the existing corpse to leave behind .
= = = = = = = = = = = = =
*/
/**
* A player is respawning , so make an entity that looks
* just like the existing corpse to leave behind .
*/
static void CopyToBodyQue ( gentity_t * ent ) {
gentity_t * body = NULL ;
int32_t contents = 0 ;
entityState_t * eState = NULL ;
trap_UnlinkEntity ( ent ) ;
// if client is in a nodrop area, don't leave the body
contents = trap_PointContents ( ent - > s . origin , - 1 ) ;
if ( ( contents & CONTENTS_NODROP ) ! = 0 ) {
ent - > s . eFlags & = ~ EF_NODRAW ; // Just in case we died from a bottomless pit, reset EF_NODRAW
return ;
}
// grab a body que and cycle to the next one
body = level . bodyQue [ level . bodyQueIndex ] ;
level . bodyQueIndex = ( level . bodyQueIndex + 1 ) % BODY_QUEUE_SIZE ;
trap_UnlinkEntity ( body ) ;
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 ;
body - > timestamp = level . time ;
body - > physicsObject = qtrue ;
body - > physicsBounce = 0 ; // don't bounce
if ( eState - > groundEntityNum = = ENTITYNUM_NONE ) {
eState - > pos . trType = TR_GRAVITY ;
eState - > pos . trTime = level . time ;
VectorCopy ( ent - > client - > ps . velocity , eState - > pos . trDelta ) ;
}
else {
eState - > pos . trType = TR_STATIONARY ;
}
eState - > event = 0 ;
// change the animation to the last-frame only, so the sequence
// doesn't repeat anew for the body
switch ( eState - > legsAnim & ~ ANIM_TOGGLEBIT ) {
case BOTH_DEATH1 :
case BOTH_DEAD1 :
eState - > torsoAnim = eState - > legsAnim = BOTH_DEAD1 ;
break ;
case BOTH_DEATH2 :
case BOTH_DEAD2 :
eState - > torsoAnim = eState - > legsAnim = BOTH_DEAD2 ;
break ;
default :
eState - > torsoAnim = eState - > legsAnim = BOTH_DEAD1 ; //DEAD3
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 ;
body - > r . ownerNum = ent - > r . ownerNum ;
body - > nextthink = level . time + 5000 ;
body - > think = BodyRezOut ;
body - > die = body_die ;
// if there shouldn't be a body, don't show one.
if ( ent - > client ! = NULL & &
( ( level . time - ent - > client - > ps . powerups [ PW_DISINTEGRATE ] ) < 10000 | |
( level . time - ent - > client - > ps . powerups [ PW_EXPLODE ] ) < 10000 ) )
{
eState - > eFlags | = EF_NODRAW ;
}
// don't take more damage if already gibbed
//RPG-X: RedTechie - Check for medicrevive
if ( rpg_medicsrevive . integer = = 0 ) {
if ( ent - > health < = GIB_HEALTH ) {
body - > takedamage = qfalse ;
}
else {
body - > takedamage = qtrue ;
}
}
else {
body - > takedamage = qfalse ;
}
VectorCopy ( eState - > pos . trBase , body - > r . currentOrigin ) ;
trap_LinkEntity ( body ) ;
}
//======================================================================
/*
= = = = = = = = = = = = = = = = = =
G_Client_SetViewAngle
= = = = = = = = = = = = = = = = = =
*/
void G_Client_SetViewAngle ( gentity_t * ent , vec3_t angle ) {
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 ) ;
}
/*
= = = = = = = = = = = = = = = =
G_Client_Respawn
= = = = = = = = = = = = = = = =
*/
void G_Client_Respawn ( gentity_t * ent ) {
qboolean borg = qfalse ;
gentity_t * tent = NULL ;
playerState_t * ps = NULL ;
CopyToBodyQue ( ent ) ;
G_Client_Spawn ( ent , 0 , qfalse ) ; //RPG-X: RedTechie - Modifyed
ps = & ent - > client - > ps ;
// add a teleportation effect
if ( borg ) {
tent = G_TempEntity ( ps - > origin , EV_BORG_TELEPORT ) ;
}
else {
tent = G_TempEntity ( ps - > origin , EV_PLAYER_TRANSPORT_IN ) ;
ps - > powerups [ PW_QUAD ] = level . time + 4000 ;
}
tent - > s . clientNum = ent - > s . clientNum ;
}
/**
* 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 ) {
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 + + ;
}
}
return ( team_t ) count ;
}
team_t G_Client_PickTeam ( int ignoreClientNum ) {
int counts [ TEAM_NUM_TEAMS ] ;
counts [ TEAM_BLUE ] = G_Client_TeamCount ( ignoreClientNum , TEAM_BLUE ) ;
counts [ TEAM_RED ] = G_Client_TeamCount ( ignoreClientNum , TEAM_RED ) ;
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 ;
}
if ( level . teamScores [ TEAM_BLUE ] < level . teamScores [ TEAM_RED ] ) {
return TEAM_BLUE ;
}
return ( team_t ) irandom ( TEAM_RED , TEAM_BLUE ) ;
}
/*
= = = = = = = = = = =
ForceClientSkin
Forces a client ' s skin ( for teamplay )
HEAVILY modified for the RPG - X
Player Model system : P
= = = = = = = = = = =
*/
/**
* Forces a client ' s skin ( for teamplay )
* HEAVILY modified for the RPG - X
* Player Model system
*/
static void ForceClientSkin ( char * model , const char * skin ) {
char * p = NULL ;
char * q = NULL ;
//we expect model to equal 'char/model/skin'
p = strchr ( model , ' / ' ) ;
//if no slashes at all
if ( p = = NULL | | p [ 0 ] = = 0 | | p [ 1 ] = = 0 ) {
//input everything
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
p + + ;
q = strchr ( p , ' / ' ) ; //okay, we should get another one if one was already found
if ( q = = NULL | | q [ 0 ] = = 0 | | q [ 1 ] = = 0 )
{ //no slashes were found?? >.<
//okay, let's assume they specified the .model file, no skin
//so just add the skin to the end :P
strncat ( model , " / " , MAX_QPATH ) ;
strncat ( model , skin , MAX_QPATH ) ;
}
else {
q + + ;
* q = ' \0 ' ;
strncat ( model , skin , MAX_QPATH ) ;
}
}
}
/*
= = = = = = = = = = =
ClientCheckName
= = = = = = = = = = = =
*/
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 ;
//save room for trailing null byte
outSize - - ;
p = out ;
* p = 0 ;
spaces = 0 ;
while ( 1 ) {
ch = * in + + ;
if ( ch = = 0 ) {
break ;
}
// don't allow leading spaces
if ( ! * p & & ch = = ' ' ) {
continue ;
}
// 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 + + ;
continue ;
}
// 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 ;
}
}
else {
spaces = 0 ;
}
if ( len > outSize - 1 ) {
break ;
}
* out + + = ch ;
colorlessLen + + ;
len + + ;
}
* out = 0 ;
// don't allow empty names
if ( * p = = 0 | | colorlessLen = = 0 )
{
Q_strncpyz ( p , " RedShirt " , outSize ) ;
}
}
/*
= = = = = = = = = = =
legalSkin
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 .
= = = = = = = = = = =
*/
/**
* 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 .
*/
static qboolean legalSkin ( const char * race_list , const char * race )
{
char current_race_name [ 125 ] ;
const char * s = race_list ;
const char * max_place = race_list + strlen ( race_list ) ;
const char * marker = NULL ;
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 + + ;
}
// copy just that name
strncpy ( current_race_name , marker , ( s - marker ) + 1 ) ;
// avoid the comma or increment us past the end of the string so we fail the main while loop
s + + ;
// compare and see if this race is the same as the one we want
if ( Q_stricmp ( current_race_name , race ) = = 0 )
{
return qtrue ;
}
}
return qfalse ;
}
/*
= = = = = = = = = = =
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
*/
static void randomSkin ( const char * race , char * model , int32_t current_team , int32_t clientNum )
{
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 = ( char * * ) malloc ( MAX_SKINS_FOR_RACE * 128 * sizeof ( char ) ) ;
if ( skinsForRace = = NULL ) {
G_Error ( " Was unable to allocate %i bytes. \n " , MAX_SKINS_FOR_RACE * 128 * sizeof ( char ) ) ;
return ;
}
skinNamesAlreadyUsed = ( char * * ) malloc ( 16 * 128 * sizeof ( char ) ) ;
if ( skinNamesAlreadyUsed = = NULL ) {
G_Error ( " Was unable to allocate %i bytes. \n " , 16 * 128 * sizeof ( char ) ) ;
return ;
}
memset ( skinsForRace , 0 , MAX_SKINS_FOR_RACE * 128 * sizeof ( char ) ) ;
memset ( skinNamesAlreadyUsed , 0 , 16 * 128 * sizeof ( char ) ) ;
// 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 ;
if ( skin_count_check ! = 0 ) {
// sanity check the skins to compare against count
if ( skin_count_check > 16 ) {
skin_count_check = 16 ;
}
// now construct an array of the names already used
for ( ; i < g_maxclients . integer ; i + + ) {
// did we find enough skins to grab a random one from yet?
if ( current_skin_count = = skin_count_check )
{
break ;
}
ent = g_entities + i ;
if ( ent = = NULL | | ! ent - > inuse | | i = = clientNum ) {
continue ;
}
// no, so look at the next one, and see if it's in the list we are constructing
// same team?
if ( ent - > client ! = NULL & & ent - > client - > sess . sessionTeam = = current_team ) {
userinfo = ( char * ) malloc ( MAX_INFO_STRING * sizeof ( char ) ) ;
if ( userinfo = = NULL ) {
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
for ( x = 0 ; x < current_skin_count ; x + + ) {
// are we the same?
if ( Q_stricmp ( skinNamesAlreadyUsed [ x ] , temp_model ) = = 0 ) {
// yeah - ok we already got this one
break ;
}
}
// ok, did we match anything?
if ( x = = current_skin_count ) {
// 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?
if ( current_skin_count > = skin_count_check ) {
// 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
for ( i = 0 ; i < group_count & & howManySkins < MAX_SKINS_FOR_RACE ; i + + ) {
// if this models race list contains the race we want, then add it to the list
if ( legalSkin ( group_list [ i ] . text , race ) ) {
Q_strncpyz ( skinsForRace [ howManySkins + + ] , group_list [ i ] . name , 128 ) ;
}
}
// set model to a random one
if ( howManySkins > 0 ) {
temp = rand ( ) % howManySkins ;
Q_strncpyz ( model , skinsForRace [ temp ] , MAX_QPATH ) ;
}
else {
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
*/
static qboolean getNewSkin ( const char * group , char * model , const char * color , const gclient_t * client , int32_t clientNum )
{
char * temp_string = NULL ;
// 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?
if ( legalSkin ( temp_string , group ) ) {
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 ;
}
/*
= = = = = = = = = = =
G_Client_UserinfoChanged
= = = = = = = = = = = =
*/
/**
* Called from G_Client_Connect when the player first connects and
* directly by the server system when the player updates a userinfo variable .
*
* The game can override any of the settings and call trap_SetUserinfo
* if desired .
*/
void G_Client_UserinfoChanged ( int32_t clientNum ) {
gentity_t * ent = NULL ;
int32_t i = 0 ;
int32_t modelOffset = 0 ;
char * s = NULL ;
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 ] ;
double weight ;
double height ;
gclient_t * client = NULL ;
qboolean reset ;
qboolean changeName = qtrue ; //TiM : For the name filter
clientPersistant_t * pers = NULL ;
clientSession_t * sess = NULL ;
ent = g_entities + clientNum ;
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 ) ) ;
client = ent - > client ;
pers = & client - > pers ;
sess = & client - > sess ;
//TiM - Exit if this user has had their info clamped
if ( ( ent - > flags & FL_CLAMPED ) ! = 0 ) {
return ;
}
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 " ) ;
if ( strcmp ( s , " localhost " ) = = 0 ) {
pers - > localClient = qtrue ;
}
// check the item prediction
s = Info_ValueForKey ( userinfo , " cg_predictItems " ) ;
if ( atoi ( s ) = = 0 ) {
pers - > predictItemPickup = qfalse ;
}
else {
pers - > predictItemPickup = qtrue ;
}
// set name
//TiM: Filter for if a player is already on this server with that name.
s = Info_ValueForKey ( userinfo , " name " ) ;
if ( ( rpg_uniqueNames . integer ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
char newName [ 36 ] ;
char activeName [ 36 ] ;
memset ( newName , 0 , sizeof ( newName ) ) ;
memset ( activeName , 0 , sizeof ( activeName ) ) ;
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 ) ;
if ( ( g_entities [ i ] . client - > ps . clientNum ! = client - > ps . clientNum )
& & ( Q_stricmp ( newName , activeName ) = = 0 ) ) {
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 ) {
if ( strcmp ( oldname , pers - > netname ) ! = 0 ) {
if ( ( ! levelExiting ) & & ( level . intermissiontime = = 0 ) ) {
//no need to do this during level changes
trap_SendServerCommand ( - 1 , va ( " print \" %s " S_COLOR_WHITE " renamed to %s \n \" " , oldname , pers - > netname ) ) ;
}
}
}
}
pers - > pms_height = atof ( Info_ValueForKey ( userinfo , " height " ) ) ;
if ( pers - > pms_height = = 0 ) {
pers - > pms_height = 1.0f ;
}
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
switch ( sess - > sessionTeam ) {
case TEAM_RED :
{
// decide if we are going to have to reset a skin cos it's not applicable to a race selected
if ( ( g_gametype . integer < GT_TEAM ) | | ( Q_stricmp ( " " , g_team_group_red . string ) = = 0 ) ) {
ForceClientSkin ( model , " red " ) ;
break ;
}
else {
// at this point, we are playing CTF and there IS a race specified for this game
reset = getNewSkin ( g_team_group_red . string , model , " red " , client , clientNum ) ;
// did we get a model name back?
if ( model [ 0 ] = = 0 ) {
// 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 ;
}
if ( reset ) {
if ( ! levelExiting ) {
//no need to do this during level changes
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 ;
}
}
case TEAM_BLUE :
{
// decide if we are going to have to reset a skin cos it's not applicable to a race selected
if ( ( g_gametype . integer < GT_TEAM ) | | ( Q_stricmp ( " " , g_team_group_blue . string ) = = 0 ) ) {
ForceClientSkin ( model , " blue " ) ;
break ;
}
else {
// at this point, we are playing CTF and there IS a race specified for this game
// 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?
if ( model [ 0 ] = = 0 ) {
// 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 ;
}
if ( reset ) {
if ( ! levelExiting ) {
//no need to do this during level changes
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 ;
}
}
default :
break ;
}
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 " ) ;
}
if ( ( rpg_rpg . integer ! = 0 ) & & ( rpg_forceclasscolor . integer ! = 0 ) & & ( g_gametype . integer < GT_TEAM ) ) {
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 ;
}
else {
client - > noAdminChat = qfalse ;
}
// teamInfo
s = Info_ValueForKey ( userinfo , " teamoverlay " ) ;
if ( ! * s | | atoi ( s ) ! = 0 ) {
pers - > teamInfo = qtrue ;
}
else {
pers - > teamInfo = qfalse ;
}
//PMS system - lock down the values
s = Info_ValueForKey ( userinfo , " height " ) ;
height = atof ( s ) ;
if ( height > ( float ) rpg_maxHeight . value ) {
Q_strncpyz ( sHeight , rpg_maxHeight . string , sizeof ( sHeight ) ) ;
}
else if ( height < ( float ) rpg_minHeight . value ) {
Q_strncpyz ( sHeight , rpg_minHeight . string , sizeof ( sHeight ) ) ;
}
else {
Q_strncpyz ( sHeight , s , sizeof ( sHeight ) ) ;
}
//TiM - needed for height offset
pers - > pms_height = atof ( sHeight ) ;
//PMS system - lock down the values
s = Info_ValueForKey ( userinfo , " weight " ) ;
weight = atof ( s ) ;
if ( weight > ( float ) rpg_maxWeight . value ) {
Q_strncpyz ( sWeight , rpg_maxWeight . string , sizeof ( sWeight ) ) ;
}
else if ( weight < ( float ) rpg_minWeight . value ) {
Q_strncpyz ( sWeight , rpg_minWeight . string , sizeof ( sWeight ) ) ;
}
else {
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
if ( ( ent - > r . svFlags & SVF_BOT ) ! = 0 ) {
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 ) G_Client_IsAdmin ( g_entities + clientNum ) ) ) ;
}
trap_SetConfigstring ( CS_PLAYERS + clientNum , s ) ;
G_LogPrintf ( " ClientUserinfoChanged: %i %s \n " , clientNum , s ) ; // no ip logging here as string might get to long
}
/*
= = = = = = = = = = =
G_Client_Connect
= = = = = = = = = = = =
*/
/**
* 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
* and will eventually get to G_Client_Begin .
*
* firstTime will be qtrue the very first time a client connects
* to the server machine , but qfalse on map changes and tournement
* restarts .
*/
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
char userinfo [ MAX_INFO_STRING ] ;
gclient_t * client = NULL ;
gentity_t * ent = NULL ;
vmCvar_t mapname ;
vmCvar_t sv_hostname ;
ent = & g_entities [ clientNum ] ;
if ( ent = = NULL ) {
return " Critical Error: Client entity was NULL " ;
}
memset ( ip , 0 , sizeof ( ip ) ) ;
memset ( userinfo , 0 , sizeof ( userinfo ) ) ;
trap_Cvar_Register ( & mapname , " mapname " , " " , CVAR_SERVERINFO | CVAR_ROM ) ;
trap_Cvar_Register ( & sv_hostname , " sv_hostname " , " " , CVAR_SERVERINFO | CVAR_ROM ) ;
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 " ;
}
// check for a password
if ( ! isBot )
{
value = Info_ValueForKey ( userinfo , " password " ) ;
if ( g_password . string [ 0 ] & & Q_stricmp ( g_password . string , " none " ) ! = 0 & & strcmp ( g_password . string , value ) ! = 0 )
{
return " Invalid password " ;
}
}
//TiM: If need be, chack to see if any other players have the current name...
//evil impersonators and the likes
if ( rpg_uniqueNames . integer ! = 0 & & ! isBot ) {
char name [ 36 ] ;
char oldName [ 36 ] ;
int32_t i = 0 ;
memset ( name , 0 , sizeof ( name ) ) ;
memset ( oldName , 0 , sizeof ( oldName ) ) ;
//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 + + ) {
if ( ( g_entities [ i ] . client = = NULL ) | | ( g_entities [ i ] . client - > pers . connected ! = CON_CONNECTED ) ) {
continue ;
}
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 ) ) ;
if ( ( Q_stricmp ( Q_CleanStr ( name ) , Q_CleanStr ( oldName ) ) = = 0 ) & & ! isBot ) {
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 , qboolean ( ! 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 ;
}
else {
int32_t tmpScore = 0 ;
int32_t i = 0 ;
qboolean changeRank = qfalse ;
newClass = Info_ValueForKey ( userinfo , " ui_playerClass " ) ;
newRank = Info_ValueForKey ( userinfo , " ui_playerRank " ) ;
//Com_Printf( S_COLOR_RED "Data: %s %s\n", newClass, newRank );
if ( newClass [ 0 ] ! = 0 ) {
client - > sess . sessionClass = ValueNameForClass ( newClass ) ; //TiM: BOOYEAH! :)
//if class doesn't exist, default to 0
if ( client - > sess . sessionClass < 0 ) {
client - > sess . sessionClass = 0 ;
}
}
else {
client - > sess . sessionClass = 0 ;
}
for ( i = 0 ; i < MAX_RANKS ; i + + ) {
if ( rpg_startingRank . string [ 0 ] = = 0 & & newRank [ 0 ] ! = 0 ) {
if ( Q_stricmp ( newRank , g_rankNames [ i ] . consoleName . data ( ) ) = = 0 ) {
tmpScore = i ; //1 << i;
if ( rpg_changeRanks . integer ! = 0 ) {
changeRank = qtrue ;
}
break ;
}
}
else {
if ( ( rpg_startingRank . string [ 0 ] ! = 0 ) & & ( Q_stricmp ( g_rankNames [ i ] . consoleName . data ( ) , rpg_startingRank . string ) = = 0 ) ) {
tmpScore = i ; // 1 << i;
changeRank = qtrue ;
break ;
}
}
}
if ( changeRank ) {
ent - > client - > UpdateScore = qtrue ;
G_Client_SetScore ( ent , tmpScore ) ;
}
}
//========================================================
}
G_Client_UserinfoChanged ( clientNum ) ;
//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
qboolean nameFound = qfalse ;
//Check to see if this player already connected on this server
if ( ( rpg_renamedPlayers . integer ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
int32_t i = 0 ;
for ( i = 0 ; i < MAX_RECON_NAMES ; i + + ) {
if ( g_reconData [ i ] . previousName [ 0 ] = = 0 ) {
continue ;
}
if ( ( Q_stricmp ( client - > pers . ip , g_reconData [ i ] . ipAddress ) = = 0 )
& & ( Q_stricmp ( client - > pers . netname , g_reconData [ i ] . previousName ) ! = 0 ) ) {
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 ;
}
}
if ( ( g_gametype . integer > = GT_TEAM ) & & ( client - > sess . sessionTeam ! = TEAM_SPECTATOR ) ) {
BroadcastTeamChange ( client , - 1 ) ;
}
// count current clients and rank for scoreboard
//G_Client_CalculateRanks( qfalse );
//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
static void holoTent_think ( gentity_t * ent ) {
if ( ent - > count = = 0 ) {
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
static void G_SendHoloData ( int32_t clientNum ) {
gentity_t * holoTent ;
holoTent = G_Spawn ( ) ;
holoTent - > classname = " holoTent " ;
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
static void transTent_think ( gentity_t * ent ) {
std : : string message = " ui_trdata " ;
for ( auto i = 0U ; i < 6 & & i < level . srvChangeData . size ( ) ; i + + )
{
message + = std : : to_string ( i ) + R " ( \ ) " + level . srvChangeData [ i ] . m_name + R " ( \ ) " ;
}
trap_SendServerCommand ( ent - > target_ent - g_entities , message . data ( ) ) ;
G_FreeEntity ( ent ) ;
}
//! creates an entity that transmits the server change data to the client
static void G_SendTransData ( int32_t clientNum ) {
gentity_t * transTent ;
transTent = G_Spawn ( ) ;
transTent - > classname = " transTent " ;
transTent - > target_ent = g_entities + clientNum ;
transTent - > think = transTent_think ;
transTent - > nextthink = level . time + 500 ;
}
/*
= = = = = = = = = = =
G_Client_Begin
= = = = = = = = = = = =
*/
/**
* 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
*/
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 ;
qboolean alreadyIn = qfalse ;
int32_t score = 0 ;
ent = g_entities + clientNum ;
if ( ent = = NULL ) {
return ;
}
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 ;
if ( client - > pers . connected = = CON_CONNECTED ) {
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 ;
G_Client_SetScore ( ent , score ) ;
// locate ent at a spawn point
G_Client_Spawn ( ent , 0 , qfalse ) ; //RPG-X: RedTechie - Modifyed
if ( client - > sess . sessionTeam ! = TEAM_SPECTATOR ) {
// Don't use transporter FX for spectators or those watching the holodoors.
// 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
G_Client_CalculateRanks ( qfalse ) ;
//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
if ( ( client - > sess . sessionTeam = = TEAM_SPECTATOR ) | |
( g_classData [ client - > sess . sessionClass ] . isMedical ) | |
( g_classData [ client - > sess . sessionClass ] . isAdmin ) )
{
int32_t i = 0 ;
char entry [ 16 ] ;
char command [ 1024 ] ;
int32_t numPlayers = 0 ;
gentity_t * player ;
int32_t len = 0 ;
int32_t cmdLen = 0 ;
memset ( entry , 0 , sizeof ( entry ) ) ;
memset ( command , 0 , sizeof ( command ) ) ;
for ( ; i < g_maxclients . integer ; i + + )
{
player = g_entities + i ;
if ( ( player = = NULL ) | | ( player = = ent ) | | ! player - > inuse ) {
continue ;
}
Com_sprintf ( entry , sizeof ( entry ) , " %i %i " , i , player - > health > = 0 ? player - > health : 0 ) ;
len = strlen ( entry ) ;
if ( cmdLen + len > sizeof ( command ) ) {
break ;
}
strcpy ( command + cmdLen , entry ) ;
cmdLen + = len ;
numPlayers + + ;
}
if ( numPlayers > 0 ) {
trap_SendServerCommand ( clientNum , va ( " hinfo %i%s " , numPlayers , command ) ) ;
}
}
//RPG-X: RedTechie - But we dont care about warmup!
if ( careAboutWarmup ) {
if ( level . restarted | | g_restarted . integer ! = 0 ) {
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
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
)
{
char userInfo [ MAX_TOKEN_CHARS ] ;
uint64_t securityID = 0 ;
memset ( userInfo , 0 , sizeof ( userInfo ) ) ;
trap_GetUserinfo ( clientNum , userInfo , sizeof ( userInfo ) ) ;
if ( userInfo [ 0 ] = = 0 ) {
return ;
}
securityID = ( uint64_t ) atoul ( Info_ValueForKey ( userInfo , " sv_securityCode " ) ) ;
if ( ( securityID < = 0 ) | | ( securityID > = 0xffffffff ) ) {
trap_SendServerCommand ( clientNum , va ( " configID %s " , ent - > client - > pers . ip ) ) ;
}
}
// send srv change data to ui
if ( ! isBot & & first ) {
if ( ! level . srvChangeData . empty ( ) ) {
G_SendTransData ( clientNum ) ;
}
}
// send holo data to ui
if ( ! isBot & & first ) {
if ( holoData . numProgs ) {
G_SendHoloData ( clientNum ) ;
}
}
//RPG-X: Marcin: show the server motd - 15/12/2008
if ( ! isBot & & first ) {
trap_SendServerCommand ( ent - > s . number , " motd " ) ;
}
if ( ! isBot ) {
qboolean last = qfalse ;
int32_t len = 0 ;
fileHandle_t file = 0 ;
char * p = NULL ;
char * q = NULL ;
char buf [ 16000 ] ; // TODO move to heap ?
len = trap_FS_FOpenFile ( rpg_motdFile . string , & file , FS_READ ) ;
if ( file = = 0 | | len = = 0 ) {
trap_SendServerCommand ( ent - > s . number , va ( " motd_line \" ^1%s not found or empty^7 \" " , rpg_motdFile . string ) ) ;
return ;
}
memset ( buf , 0 , sizeof ( buf ) ) ;
trap_FS_Read ( buf , len , file ) ;
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 ' ;
trap_SendServerCommand ( ent - > s . number , va ( " motd_line \" %s \" " , p ) ) ;
q + + ;
}
}
//we may currently be selfdestructing, so send stuff for that case
selfdestruct = G_Find ( NULL , FOFS ( classname ) , " target_selfdestruct " ) ;
if ( ( selfdestruct ! = NULL ) & & ( ( selfdestruct - > spawnflags & 1 ) ! = 0 ) ) {
trap_SendServerCommand ( ent - > s . number , va ( " selfdestructupdate %i " , selfdestruct - > damage - level . time ) ) ;
}
else {
trap_SendServerCommand ( ent - > s . number , va ( " selfdestructupdate %i " , - 1 ) ) ;
}
}
/**
* 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 )
{
int32_t i = WP_1 ;
int32_t Bits = 0 ;
Bits = ( 1 < < WP_1 ) ;
Bits | = g_classData [ pclass ] . weaponsFlags ;
for ( ; i < MAX_WEAPONS ; i + + ) {
//if we want no weapons and aren't an admin, skip this particular weapon
if ( ( rpg_noweapons . integer ! = 0 ) & & ! g_classData [ pclass ] . isAdmin ) {
if ( ( i > = WP_5 ) & & ( i < = WP_10 ) ) {
continue ;
}
}
if ( ( Bits & ( 1 < < i ) ) ! = 0 ) {
client - > ps . stats [ STAT_WEAPONS ] | = ( 1 < < i ) ;
client - > ps . ammo [ i ] = Min_Weapon ( i ) ;
}
}
}
/**
* 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 )
{
if ( g_classData [ pclass ] . isMarine ) {
client - > ps . stats [ STAT_HOLDABLE_ITEM ] = BG_FindItemForHoldable ( HI_TRANSPORTER ) - bg_itemlist ;
}
else if ( g_classData [ pclass ] . isAdmin ) {
client - > ps . stats [ STAT_HOLDABLE_ITEM ] = BG_FindItemForHoldable ( HI_SHIELD ) - bg_itemlist ;
}
}
void G_Client_StoreClientInitialStatus ( gentity_t * ent )
{
char userinfo [ MAX_INFO_STRING ] ;
if ( clientInitialStatus [ ent - > s . number ] . initialized ) {
//already set
return ;
}
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
//don't store their data if they're just a spectator
return ;
}
memset ( userinfo , 0 , sizeof ( userinfo ) ) ;
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 ;
}
/*
= = = = = = = = = = =
G_Client_Spawn
Called every time a client is placed fresh in the world :
after the first G_Client_Begin , and after each respawn
Initializes all non - persistant parts of playerState
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Modifyed By : RedTechie
And also by Marcin - 30 / 12 / 2008
= = = = = = = = = = = =
*/
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 ;
clientPersistant_t saved ;
clientSession_t savedSess ;
gentity_t * spawnPoint = NULL ;
pclass_t pClass = 0 ;
pclass_t oClass = 0 ;
if ( ent = = NULL ) {
return ;
}
memset ( persistant , 0 , sizeof ( persistant ) ) ;
index = ent - g_entities ;
client = ent - > client ;
clientNum = ent - > client - > ps . clientNum ;
// find a spawn point
// do it before setting health back up, so farthest
// ranging doesn't count this client
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 ) {
spawnPoint = SelectSpectatorSpawnPoint ( spawn_origin , spawn_angles ) ;
}
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
spawnPoint = G_Client_SelectSpawnPoint ( client - > ps . origin , spawn_origin , spawn_angles ) ;
}
// Tim needs to prevent bots from spawning at the initial point
// on q3dm0...
if ( ( ( spawnPoint - > flags & FL_NO_BOTS ) ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) ! = 0 ) ) {
continue ; // try again
}
// just to be symetric, we have a nohumans option...
if ( ( ( spawnPoint - > flags & FL_NO_HUMANS ) ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
continue ; // try again
}
break ;
} while ( 1 ) ;
}
} //RPG-X: RedTechie - End rpgx_spawn check
client - > pers . teamState . state = TEAM_ACTIVE ;
// toggle the teleport bit so the client knows to not lerp
if ( rpgx_spawn ! = 1 ) {
flags = ent - > client - > ps . eFlags & EF_TELEPORT_BIT ;
flags ^ = EF_TELEPORT_BIT ;
}
// 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 ] ;
}
//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 ) {
if ( client - > classChangeDebounceTime ) {
cCDT = client - > classChangeDebounceTime ;
}
memset ( client , 0 , sizeof ( * client ) ) ;
client - > classChangeDebounceTime = cCDT ;
}
//
client - > pers = saved ;
client - > sess = savedSess ;
client - > ps . ping = savedPing ;
for ( i = 0 ; i < MAX_PERSISTANT ; i + + ) {
client - > ps . persistant [ i ] = persistant [ i ] ;
}
// increment the spawncount so the client will detect the respawn
if ( rpgx_spawn ! = 1 ) {
client - > ps . persistant [ PERS_SPAWN_COUNT ] + + ;
client - > airOutTime = level . time + 12000 ;
}
if ( client - > sess . sessionTeam ! = TEAM_SPECTATOR ) {
client - > sess . sessionTeam = TEAM_FREE ;
}
client - > ps . persistant [ PERS_TEAM ] = client - > sess . sessionTeam ;
// clear entity values
client - > ps . stats [ STAT_MAX_HEALTH ] = client - > pers . maxHealth ;
client - > ps . eFlags = flags ;
client - > streakCount = 0 ;
ent - > client - > ps . pm_type = PM_NORMAL ;
ent - > s . groundEntityNum = ENTITYNUM_NONE ;
ent - > client = & level . clients [ index ] ;
ent - > takedamage = qtrue ;
ent - > inuse = qtrue ;
ent - > classname = " player " ;
ent - > r . contents = CONTENTS_BODY ;
ent - > clipmask = MASK_PLAYERSOLID ;
ent - > die = G_Client_Die ;
ent - > waterlevel = 0 ;
ent - > watertype = 0 ;
ent - > flags = 0 ;
if ( rpgx_spawn ! = 1 ) {
VectorCopy ( playerMins , ent - > r . mins ) ;
VectorCopy ( playerMaxs , ent - > r . maxs ) ;
}
client - > ps . clientNum = index ;
// health will count down towards max_health
ent - > health = client - > ps . stats [ STAT_HEALTH ] = client - > ps . stats [ STAT_MAX_HEALTH ] * 1.25 ;
oClass = client - > sess . sessionClass ;
if ( oClass ! = client - > sess . sessionClass ) {
//need to send the class change
G_Client_UserinfoChanged ( client - > ps . clientNum ) ;
}
client - > ps . persistant [ PERS_CLASS ] = client - > sess . sessionClass ;
pClass = client - > sess . sessionClass ;
if ( pClass ! = 0 ) {
//no health boost on spawn for playerclasses
ent - > health = client - > ps . stats [ STAT_HEALTH ] = client - > ps . stats [ STAT_MAX_HEALTH ] = client - > pers . maxHealth ;
}
if ( ! fromDeath | | ( rpg_dropOnDeath . integer = = 0 ) | | ( rpg_allowWeaponDrop . integer = = 0 ) ) {
G_Client_WeaponsForClass ( client , pClass ) ;
}
else { // Marcin: just a hand
G_Client_WeaponsForClass ( client , 0 ) ;
}
G_Client_HoldablesForClass ( client , pClass ) ;
if ( rpgx_spawn ! = 1 ) {
G_SetOrigin ( ent , spawn_origin ) ;
VectorCopy ( spawn_origin , client - > ps . origin ) ;
}
// the respawned flag will be cleared after the attack and jump keys come up
if ( rpgx_spawn ! = 1 ) {
client - > ps . pm_flags | = PMF_RESPAWNED ;
}
trap_GetUsercmd ( client - level . clients , & ent - > client - > pers . cmd ) ;
if ( rpgx_spawn ! = 1 ) {
G_Client_SetViewAngle ( ent , spawn_angles ) ;
}
if ( rpgx_spawn ! = 1 ) {
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
}
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 ;
}
}
// don't allow full run speed for a bit
client - > ps . pm_flags | = PMF_TIME_KNOCKBACK ;
client - > ps . pm_time = 100 ;
if ( rpgx_spawn ! = 1 ) {
client - > respawnTime = level . time ;
}
client - > inactivityTime = level . time + g_inactivity . integer * 1000 ;
client - > latched_buttons = 0 ;
// set default animations
if ( rpgx_spawn ! = 1 ) {
client - > ps . stats [ TORSOANIM ] = BOTH_STAND1 ;
client - > ps . stats [ LEGSANIM ] = BOTH_STAND1 ;
}
if ( level . intermissiontime ) {
MoveClientToIntermission ( ent ) ;
}
else {
// fire the targets of the spawn point
if ( rpgx_spawn ! = 1 ) {
G_UseTargets ( spawnPoint , ent ) ;
}
// select the highest weapon number available, after any
// spawn given items have fired
client - > ps . weapon = 1 ;
//TiM - Always default to the null hand
client - > ps . weapon = WP_1 ;
}
// 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 ) ;
// 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 ) ;
if ( rpgx_spawn ! = 1 ) {
VectorCopy ( ent - > client - > ps . origin , ent - > r . currentOrigin ) ;
}
trap_LinkEntity ( ent ) ;
}
// run the presend to set anything else
ClientEndFrame ( ent ) ;
// clear entity state values
BG_PlayerStateToEntityState ( & client - > ps , & ent - > s , qtrue ) ;
//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
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
//spectators just get the title of the game
switch ( g_gametype . integer ) {
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 ;
}
}
else {
if ( ! clientInitialStatus [ ent - > s . number ] . initialized ) {
//first time coming in
switch ( g_gametype . integer ) {
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 ;
}
}
}
if ( rpgx_spawn ! = 0 ) {
if ( ( client - > sess . sessionTeam = = TEAM_SPECTATOR ) | |
g_classData [ client - > sess . sessionClass ] . isMedical | |
g_classData [ client - > sess . sessionClass ] . isAdmin ) {
int32_t l = 0 ;
int32_t numPlayers = 0 ;
int32_t len = 0 ;
int32_t cmdLen = 0 ;
char entry [ 16 ] ;
char command [ 1024 ] ;
gentity_t * player = NULL ;
memset ( entry , 0 , sizeof ( entry ) ) ;
memset ( command , 0 , sizeof ( entry ) ) ;
for ( ; l < g_maxclients . integer ; l + + )
{
player = g_entities + l ;
if ( ( player = = NULL ) | | ( player = = ent ) | | ! player - > inuse ) {
continue ;
}
Com_sprintf ( entry , sizeof ( entry ) , " %i %i " , l , player - > health > = 0 ? player - > health : 0 ) ;
len = strlen ( entry ) ;
if ( cmdLen + len > sizeof ( command ) ) {
break ;
}
strcpy ( command + cmdLen , entry ) ;
cmdLen + = len ;
numPlayers + + ;
}
if ( numPlayers > 0 ) {
trap_SendServerCommand ( clientNum , va ( " hinfo %i%s " , numPlayers , command ) ) ;
}
}
}
//store intial client values
//FIXME: when purposely change teams, this gets confused?
G_Client_StoreClientInitialStatus ( ent ) ;
//RPG-X: Marcin: stuff was here previously - 22/12/2008
}
/*
= = = = = = = = = = =
G_Client_Disconnect
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 .
= = = = = = = = = = = =
*/
void G_Client_Disconnect ( int32_t clientNum ) {
gentity_t * ent = NULL ;
gentity_t * tent = NULL ;
int32_t i = 0 ;
ent = & g_entities [ clientNum ] ;
if ( ( ent = = NULL ) | | ( ent - > client = = NULL ) ) {
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 ] ) ;
}
}
//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 >:)
if ( ( rpg_renamedPlayers . integer ! = 0 ) & & ( ( ent - > r . svFlags & SVF_BOT ) = = 0 ) ) {
int32_t l = 0 ;
qboolean foundName = qfalse ;
//Do a chek to see if this player has disconnected b4. else we'll be wasting a slot.
for ( ; l < MAX_RECON_NAMES ; l + + ) {
if ( g_reconData [ l ] . ipAddress [ 0 ] = = 0 ) {
continue ;
}
if ( Q_stricmp ( ent - > client - > pers . ip , g_reconData [ l ] . ipAddress ) = = 0 ) {
foundName = qtrue ;
break ;
}
}
if ( foundName ) {
memset ( & g_reconData [ l ] , 0 , sizeof ( g_reconData [ l ] ) ) ;
//IP Address
Q_strncpyz ( g_reconData [ l ] . ipAddress , ent - > client - > pers . ip , sizeof ( g_reconData [ l ] . ipAddress ) ) ;
//Player Name
Q_strncpyz ( g_reconData [ l ] . previousName , ent - > client - > pers . netname , sizeof ( g_reconData [ l ] . previousName ) ) ;
}
else {
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 ;
}
}
}
// send effect if they were completely connected
if ( ( ent - > client - > pers . connected = = CON_CONNECTED )
& & ( ent - > client - > sess . sessionTeam ! = TEAM_SPECTATOR ) ) {
vec3_t org ;
VectorCopy ( ent - > client - > ps . origin , org ) ;
org [ 2 ] + = ( ent - > client - > ps . viewheight > > 1 ) ;
tent = G_TempEntity ( org , EV_PLAYER_TELEPORT_OUT ) ;
tent - > s . clientNum = ent - > s . clientNum ;
// They don't get to take powerups with them!
// Especially important for stuff like CTF flags
G_Client_TossClientItems ( ent , qtrue ) ;
}
G_LogPrintf ( " ClientDisconnect: %i (%s) \n " , clientNum , g_entities [ clientNum ] . client - > pers . ip ) ;
// if we are playing in tourney mode and losing, give a win to the other player
if ( ( g_gametype . integer = = GT_TOURNAMENT ) & & ( level . intermissiontime = = 0 )
& & ( level . warmupTime = = 0 ) & & ( level . sortedClients [ 1 ] = = clientNum ) ) {
level . clients [ level . sortedClients [ 0 ] ] . sess . wins + + ;
G_Client_UserinfoChanged ( level . sortedClients [ 0 ] ) ;
}
if ( ( g_gametype . integer = = GT_TOURNAMENT ) & & ( ent - > client - > sess . sessionTeam = = TEAM_FREE ) & & ( level . intermissiontime ! = 0 ) ) {
trap_SendConsoleCommand ( EXEC_APPEND , " map_restart 0 \n " ) ;
level . restarted = qtrue ;
level . intermissiontime = 0 ;
}
trap_UnlinkEntity ( ent ) ;
memset ( ent , 0 , sizeof ( ent ) ) ;
ent - > s . modelindex = 0 ;
ent - > inuse = qfalse ;
ent - > classname = " disconnected " ;
ent - > client - > pers . connected = CON_DISCONNECTED ;
ent - > client - > ps . persistant [ PERS_TEAM ] = TEAM_FREE ;
ent - > client - > ps . persistant [ PERS_CLASS ] = 0 ; //PC_NOCLASS;
ent - > client - > sess . sessionTeam = TEAM_FREE ;
ent - > client - > sess . sessionClass = 0 ; //PC_NOCLASS;
trap_SetConfigstring ( CS_PLAYERS + clientNum , " " ) ;
G_Client_CalculateRanks ( qfalse ) ;
if ( ( ent - > r . svFlags & SVF_BOT ) ! = 0 ) {
AI_main_BotAIShutdownClient ( clientNum ) ;
}
// 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 ;
}
/*
= = = = = = = = = = = = = = = =
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 G_Client_IsAdmin ( gentity_t * ent )
{
if ( ent = = NULL ) {
return qfalse ;
}
if ( ent - > client = = NULL ) {
return qfalse ;
}
if ( ( g_classData [ ent - > client - > sess . sessionClass ] . isAdmin ) | |
( ent - > client - > LoggedAsAdmin = = qtrue ) | |
( ent - > client - > LoggedAsDeveloper = = qtrue ) ) {
return qtrue ;
}
else {
return qfalse ;
}
}
/*
= = = = = = = = = = =
G_Client_GetLocation
Report a location for the player . Uses placed nearby target_location entities
= = = = = = = = = = = =
*/
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 ;
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
= = = = = = = = = = = =
*/
qboolean G_Client_GetLocationMsg ( gentity_t * ent , char * loc , int32_t loclen )
{
gentity_t * best = NULL ;
best = G_Client_GetLocation ( ent ) ;
if ( best = = NULL ) {
return qfalse ;
}
if ( best - > count ! = 0 ) {
if ( best - > count < 0 )
best - > count = 0 ;
if ( best - > count > 7 )
best - > count = 7 ;
Com_sprintf ( loc , loclen , " %c%c%s " S_COLOR_WHITE , Q_COLOR_ESCAPE , best - > count + ' 0 ' , best - > message ) ;
}
else {
Com_sprintf ( loc , loclen , " %s " , best - > message ) ;
}
return qtrue ;
}
/*
= = = = = = = = = = = = = = = = = =
G_Client_CheckHealthInfoMessage
Sends Health Changes to proper clients
Format :
clientNum health
= = = = = = = = = = = = = = = = = =
*/
static void G_Client_CheckHealthInfoMessage ( void ) {
char entry [ 1024 ] ;
char string [ 1400 ] ;
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 ) ) ;
//only send this to medics or spectators or adminz
for ( ; i < g_maxclients . integer ; i + + ) {
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 ) ) ) {
sendToClients [ sendToCnt + + ] = i ;
}
}
if ( sendToCnt = = 0 ) {
//no-one to send to
return ;
}
//only send those clients whose health has changed this cycle
//NB: there's a prob with client 0 in here....
for ( i = 0 , cnt = 0 ; i < g_maxclients . integer ; i + + ) {
player = g_entities + i ;
if ( ( player ! = NULL ) & & player - > inuse & & ( player - > old_health ! = player - > health ) & & ( ( player - > health > 0 ) | | ( player - > old_health > 0 ) ) ) {
clients [ cnt + + ] = i ;
player - > old_health = player - > health ;
}
}
if ( cnt = = 0 ) {
//no-one relevant changed health
return ;
}
for ( t = 0 ; t < sendToCnt ; t + + ) {
ent = g_entities + sendToClients [ t ] ;
sentCnt = 0 ;
// send the latest information on all clients
string [ 0 ] = 0 ;
stringlength = 0 ;
for ( i = 0 ; i < cnt ; i + + ) {
player = g_entities + clients [ i ] ;
if ( ent = = player ) { //don't send the ent his own health
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 ;
}
if ( sentCnt ! = 0 ) {
trap_SendServerCommand ( sendToClients [ t ] , va ( " hinfo %i%s " , sentCnt , string ) ) ;
}
}
}
/**
* Send client location information .
*
* \ param ent The client .
*/
static void G_Client_LocationsMessage ( gentity_t * ent ) {
char entry [ 1024 ] ;
char string [ 1400 ] ;
int32_t stringlength = 0 ;
int32_t i = 0 ;
int32_t j = 0 ;
int32_t cnt = 0 ;
gentity_t * player = NULL ;
//don't bother sending during intermission?
if ( level . intermissiontime ! = 0 ) {
return ;
}
memset ( entry , 0 , sizeof ( entry ) ) ;
memset ( string , 0 , sizeof ( string ) ) ;
// figure out what client should be on the display
// we are limited to 8, but we want to use the top eight players
// but in client order (so they don't keep changing position on the overlay)
for ( ; i < g_maxclients . integer & & cnt < TEAM_MAXOVERLAY ; i + + ) {
player = g_entities + level . sortedClients [ i ] ;
if ( ( player ! = NULL ) & & ( player - > inuse ) & & ( player - > client - > sess . sessionTeam = = ent - > client - > sess . sessionTeam ) ) {
// TODO huh? remove?
}
}
// 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
if ( ( player ! = NULL ) & & player - > inuse ) {
//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 ) ;
if ( stringlength + j > sizeof ( string ) ) {
break ;
}
strcpy ( string + stringlength , entry ) ;
stringlength + = j ;
cnt + + ;
}
}
trap_SendServerCommand ( ent - g_entities , va ( " tinfo %i%s " , cnt , string ) ) ;
}
//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 ) {
int32_t i = 0 ;
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 ( ) ;
}
}
/*
= = = = = = = = = = = =
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 ) {
int32_t i = 0 ;
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
for ( ; i < level . maxclients ; i + + ) {
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 {
int32_t rank = - 1 ;
int32_t score = 0 ;
for ( i = 0 ; i < level . numPlayingClients ; i + + ) {
int32_t newScore = 0 ;
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 ( ) ;
}
}
void G_Client_UpdateSoundZones ( void ) {
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 ) ) ;
for ( ; i < MAX_GENTITIES ; i + + ) {
if ( ( g_entities [ i ] . type = = EntityType : : 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 , ( int32_t * ) & entlist , MAX_GENTITIES ) ;
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 ;
}
}
}
}
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 ) , " c%d \\ %d " , i , zones [ i ] ) ;
}
}
for ( i = 0 ; i < MAX_GENTITIES ; i + + ) {
if ( g_entities [ i ] . client ! = NULL ) {
trap_SendServerCommand ( i , va ( " slup %s " , supdate ) ) ;
}
}
}
void G_Client_SetScore ( gentity_t * ent , int32_t score ) {
G_Assert ( ent , ( void ) 0 ) ;
G_Assert ( ent - > client , ( void ) 0 ) ;
if ( ! ent - > client - > UpdateScore ) {
return ;
}
ent - > client - > ps . persistant [ PERS_SCORE ] = score ;
G_Client_CalculateRanks ( qfalse ) ;
// TiM: send the current scoring to all clients
SendScoreboardMessageToAllClients ( ) ;
//RPG-X: RedTechie - Lets enable score updating without this scores will not be updated
ent - > client - > UpdateScore = qfalse ;
}
void G_Client_TossClientItems ( gentity_t * self , qboolean dis_con ) {
double angle = 0 ;
int32_t i = 0 ;
int32_t times = 0 ;
gentity_t * drop = NULL ;
playerState_t * ps = NULL ;
gitem_t * item = NULL ;
G_Assert ( self , ( void ) 0 ) ;
G_Assert ( self - > client , ( void ) 0 ) ;
ps = & self - > client - > ps ;
if ( ( self - > flags & FL_CLOAK ) ! = 0 ) {
// remove the invisible powerup if the player is cloaked.
//RPG-X: RedTechie - Also remove ghost
ps - > powerups [ PW_GHOST ] = level . time ;
ps - > powerups [ PW_INVIS ] = level . time ;
}
if ( ( self - > flags & FL_FLY ) ! = 0 ) {
// remove the flying powerup if the player is flying.
ps - > powerups [ PW_FLIGHT ] = level . time ;
}
//RPG-X | Phenix | 8/8/2004
if ( ( self - > flags & FL_EVOSUIT ) ! = 0 ) {
// remove the evosuit powerup
ps - > powerups [ PW_EVOSUIT ] = level . time ;
}
// drop all the powerups if not in teamplay
if ( g_gametype . integer ! = GT_TEAM ) {
angle = 45 ;
for ( i = 1 ; i < PW_NUM_POWERUPS ; i + + ) {
if ( ps - > powerups [ i ] > level . time ) {
item = BG_FindItemForPowerup ( powerup_t ( i ) ) ;
if ( item = = NULL ) {
continue ;
}
drop = Drop_Item ( self , item , angle ) ;
// decide how many seconds it has left
drop - > count = ( ps - > powerups [ i ] - level . time ) / 1000 ;
if ( drop - > count < 1 ) {
drop - > count = 1 ;
}
angle + = 45 ;
}
}
}
// RPG-X | Marcin | 30/12/2008
// ...
if ( rpg_allowWeaponDrop . integer = = 0 | | rpg_dropOnDeath . integer = = 0 | | dis_con ) {
return ;
}
// Drop ALL weapons in inventory
for ( i = 0 ; i < WP_NUM_WEAPONS ; + + i ) {
// these weapons should not be tossed (hand and null)
if ( Max_Weapons [ i ] = = NULL ) {
continue ;
}
//RPG-X | GSIO01 | 08/05/2009: let's make sure we only drop weapons the player has
item = NULL ;
if ( ps - > ammo [ i ] ) {
times = ps - > ammo [ i ] ;
item = BG_FindItemForWeapon ( ( weapon_t ) i ) ;
while ( times - - > 0 ) { // the 'goes towards' operator :p
Drop_Item ( self , item , 0 ) ;
}
}
}
// then remove weapons
for ( i = 0 ; i < WP_NUM_WEAPONS ; + + i ) {
ps - > stats [ STAT_WEAPONS ] & = ~ i ;
ps - > ammo [ i ] = 0 ;
}
}
void body_die ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int32_t damage , int32_t meansOfDeath ) {
int32_t contents = 0 ;
G_Assert ( self , ( void ) 0 ) ;
contents = trap_PointContents ( self - > r . currentOrigin , - 1 ) ;
if ( rpg_medicsrevive . integer = = 1 & & ! ( contents & CONTENTS_NODROP ) & & ( meansOfDeath ! = MOD_TRIGGER_HURT ) ) {
if ( self - > health > GIB_HEALTH_IMPOSSIBLE ) {
return ;
}
}
else {
if ( self - > health > GIB_HEALTH ) {
return ;
}
}
G_Combat_GibEntity ( self , 0 ) ;
}
char * G_Client_ClassNameForValue ( pclass_t pClass ) {
static char buffer [ MAX_QPATH ] ;
char * ptr = NULL ;
trap_Cvar_VariableStringBuffer ( va ( " rpg_%sPass " , g_classData [ pClass ] . consoleName ) , buffer , sizeof ( buffer ) ) ;
ptr = buffer ;
return ptr ;
}
// these are just for logging, the client prints its own messages
char * modNames [ MOD_MAX ] = {
" MOD_UNKNOWN " ,
" MOD_WATER " ,
" MOD_SLIME " ,
" MOD_LAVA " ,
" MOD_CRUSH " ,
" MOD_TELEFRAG " ,
" MOD_FALLING " ,
" MOD_SUICIDE " ,
" MOD_TARGET_LASER " ,
" MOD_TRIGGER_HURT " ,
// Trek weapons
" MOD_PHASER " ,
" MOD_PHASER_ALT " ,
" MOD_CRIFLE " ,
" MOD_CRIFLE_SPLASH " ,
" MOD_CRIFLE_ALT " ,
" MOD_CRIFLE_ALT_SPLASH " ,
" MOD_IMOD " ,
" MOD_IMOD_ALT " ,
" MOD_SCAVENGER " ,
" MOD_SCAVENGER_ALT " ,
" MOD_SCAVENGER_ALT_SPLASH " ,
" MOD_STASIS " ,
" MOD_STASIS_ALT " ,
" MOD_GRENADE " ,
" MOD_GRENADE_ALT " ,
" MOD_GRENADE_SPLASH " ,
" MOD_GRENADE_ALT_SPLASH " ,
" MOD_TETRYON " ,
" MOD_TETRYON_ALT " ,
" MOD_DREADNOUGHT " ,
" MOD_DREADNOUGHT_ALT " ,
" MOD_QUANTUM " ,
" MOD_QUANTUM_SPLASH " ,
" MOD_QUANTUM_ALT " ,
" MOD_QUANTUM_ALT_SPLASH " ,
" MOD_DETPACK " ,
" MOD_SEEKER "
//expansion pack
" MOD_KNOCKOUT " ,
" MOD_ASSIMILATE " ,
" MOD_BORG " ,
" MOD_BORG_ALT " ,
" MOD_RESPAWN " ,
" MOD_EXPLOSION " ,
} ; //must be kept up to date with bg_public, meansOfDeath_t
/*
= = = = = = = = = = = = = = = = = =
G_Client_Die
Heavly Modifyed By : RedTechie
RPG - X : Marcin : a little bit modified - 30 / 12 / 2008
= = = = = = = = = = = = = = = = = =
*/
void G_Client_Die ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int32_t damage , int32_t meansOfDeath ) {
//---------------------
//RPG-X: RedTechie - Check to see if medics revive people and not respawn if true use my fake death insead :)
//---------------------
int32_t contents = 0 ;
G_Assert ( self , ( void ) 0 ) ;
G_Assert ( self - > client , ( void ) 0 ) ;
//RPG-X: RedTechie - Make sure there not getting killed by a trigger kill or the medics wont be able to heal them
contents = trap_PointContents ( self - > r . currentOrigin , - 1 ) ;
if ( rpg_medicsrevive . integer = = 1 & & ! ( contents & CONTENTS_NODROP ) & & ( meansOfDeath ! = MOD_TRIGGER_HURT ) ) {
char * classname = NULL ;
char * killerName = NULL ;
char * obit = NULL ;
int32_t anim = 0 ;
int32_t killer = 0 ;
int32_t i = 0 ;
gentity_t * detpack = NULL ;
gentity_t * ent = NULL ;
playerState_t * ps = & self - > client - > ps ;
//RPG-X: RedTechie - Blow up a detpack if some one placed it and died
classname = BG_FindClassnameForHoldable ( HI_DETPACK ) ;
if ( classname ! = NULL ) {
while ( ( detpack = G_Find ( detpack , FOFS ( classname ) , classname ) ) ! = NULL ) {
if ( detpack - > parent = = self ) {
detpack - > think = DetonateDetpack ; // Detonate next think.
detpack - > nextthink = level . time ;
}
}
}
//RPG-X: Redtechie - Do some score keeping witch we commented out and log
if ( attacker ! = NULL ) {
killer = attacker - > s . number ;
if ( attacker - > client ! = NULL ) {
killerName = attacker - > client - > pers . netname ;
}
else {
killerName = " <non-client> " ;
}
}
else {
killer = ENTITYNUM_WORLD ;
killerName = " <world> " ;
}
if ( killer < 0 | | killer > = MAX_CLIENTS ) {
killer = ENTITYNUM_WORLD ;
killerName = " <world> " ;
}
if ( meansOfDeath < 0 | | meansOfDeath > = sizeof ( modNames ) / sizeof ( modNames [ 0 ] ) ) {
obit = " <bad obituary> " ;
}
else {
obit = modNames [ meansOfDeath ] ;
}
G_LogPrintf ( " Kill: %i %i %i: %s killed %s by %s \n " , killer , self - > s . number , meansOfDeath , killerName , self - > client - > pers . netname , obit ) ;
G_LogWeaponKill ( killer , meansOfDeath ) ;
G_LogWeaponDeath ( self - > s . number , self - > s . weapon ) ;
if ( attacker ! = NULL & & attacker - > client ! = NULL & & attacker - > inuse ) {
G_LogWeaponFrag ( killer , self - > s . number ) ;
}
if ( meansOfDeath ! = MOD_RESPAWN & & meansOfDeath ! = MOD_CUSTOM_DIE ) {
// broadcast the death event to everyone
ent = G_TempEntity ( self - > r . currentOrigin , EV_OBITUARY ) ;
ent - > s . eventParm = meansOfDeath ;
ent - > s . otherEntityNum = self - > s . number ;
ent - > s . otherEntityNum2 = killer ;
ent - > r . svFlags = SVF_BROADCAST ; // send to everyone
}
self - > enemy = attacker ;
ps - > persistant [ PERS_KILLED ] + + ;
if ( self = = attacker ) {
self - > client - > pers . teamState . suicides + + ;
}
else {
//RPG-X | Phenix | 06/04/2005
// N00b Protection, you kill two people and puff your auto n00b!
if ( attacker ! = NULL ) {
if ( attacker - > client ! = NULL ) {
if ( G_Client_IsAdmin ( attacker ) = = qfalse ) {
attacker - > n00bCount + + ;
attacker - > client - > fraggerTime = level . time + ( rpg_fraggerSpawnDelay . integer * 1000 ) ;
if ( rpg_kickAfterXkills . integer < 1 ) {
trap_SendServerCommand ( attacker - g_entities , va ( " print \" ^7Server: You have been caught n00bing, you have been temporary put in the n00b class. \n \" " ) ) ;
}
else {
trap_SendServerCommand ( attacker - g_entities , va ( " print \" ^7Server: You have been caught n00bing, %i more times and you will be kicked. \n \" " , ( rpg_kickAfterXkills . integer - attacker - > n00bCount ) ) ) ;
}
if ( ( attacker - > n00bCount > = rpg_kickAfterXkills . integer ) & & ( rpg_kickAfterXkills . integer ! = 0 ) ) {
trap_DropClient ( attacker - > s . number , " Kicked: Do Not N00b! " ) ;
}
else {
for ( i = 0 ; g_classData [ i ] . consoleName [ 0 ] & & i < MAX_CLASSES ; i + + ) {
if ( g_classData [ i ] . isn00b ) {
char conName [ 64 ] ;
trap_Cvar_VariableStringBuffer ( va ( " rpg_%sPass " , conName ) , conName , sizeof ( conName ) ) ;
Q_strncpyz ( attacker - > client - > origClass , G_Client_ClassNameForValue ( attacker - > client - > sess . sessionClass ) , sizeof ( attacker - > client - > origClass ) ) ;
attacker - > client - > n00bTime = level . time + 10000 ;
SetClass ( attacker , conName , NULL , qfalse ) ;
break ;
}
}
}
}
}
}
}
//RPG-X: RedTechie no noclip
if ( self - > client - > noclip ) {
self - > client - > noclip = qfalse ;
}
//RPG-X: RedTechie - Toss items
//RPG-X: Marcin - not when respawning - 30/12/2008
if ( meansOfDeath ! = MOD_RESPAWN ) {
G_Client_TossClientItems ( self , qfalse ) ;
}
ps - > pm_type = PM_DEAD ;
self - > takedamage = qfalse ;
ps - > weapon = WP_0 ;
ps - > weaponstate = WEAPON_READY ;
self - > r . contents = CONTENTS_CORPSE ;
//-TiM
self - > s . loopSound = 0 ;
self - > r . maxs [ 2 ] = - 8 ;
//RPG-X: RedTechie - Wait....forever
self - > client - > respawnTime = level . time + 1000000000 ;
//Play death sound
//RPG-X: RedTechie - No pain sound when they change class
if ( meansOfDeath ! = MOD_RESPAWN ) {
G_AddEvent ( self , irandom ( EV_DEATH1 , EV_DEATH3 ) , killer ) ;
//if we died from falling, add a nice "splat' sound lol
if ( meansOfDeath = = MOD_FALLING ) {
G_AddEvent ( self , EV_SPLAT , killer ) ;
}
}
//RPG-X : Model system - Death animations now based on vector hit
if ( meansOfDeath = = MOD_FALLING ) {
anim = BOTH_FALLDEATH1LAND ;
}
else if ( self - > waterlevel = = 3 ) {
anim = BOTH_FLOAT2 ;
}
else {
if ( meansOfDeath = = MOD_PHASER | | meansOfDeath = = MOD_PHASER_ALT ) {
if ( ( self - > client - > lasthurt_location & LOCATION_FRONT ) ! = 0 ) {
anim = BOTH_DEATHBACKWARD1 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_BACK ) ! = 0 ) {
anim = BOTH_DEATHFORWARD2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_LEFT ) ! = 0 ) {
anim = BOTH_DEATH2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_RIGHT ) ! = 0 ) {
anim = BOTH_DEATH2 ;
}
else {
anim = BOTH_DEATH1 ;
}
}
else {
if ( ( self - > client - > lasthurt_location & LOCATION_FRONT ) ! = 0 ) {
anim = BOTH_DEATHBACKWARD2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_BACK ) ! = 0 ) {
anim = BOTH_DEATHFORWARD1 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_LEFT ) ! = 0 ) {
anim = BOTH_DEATHFORWARD2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_RIGHT ) ! = 0 ) {
anim = BOTH_DEATHFORWARD2 ;
}
else {
anim = BOTH_DEATH1 ;
}
}
}
//TiM
ps - > stats [ LEGSANIM ] = ( ( ps - > stats [ LEGSANIM ] & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim ;
ps - > stats [ TORSOANIM ] = ( ( ps - > stats [ TORSOANIM ] & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim ;
trap_LinkEntity ( self ) ;
BG_PlayerStateToEntityState ( & self - > client - > ps , & self - > s , qtrue ) ;
G_Client_UserinfoChanged ( self - > s . clientNum ) ;
ClientEndFrame ( self ) ;
G_Client_StoreClientInitialStatus ( self ) ;
//---------------------
//RPG-X: RedTechie - If it dose equal 0 use regular die
//---------------------
}
else {
char * killerName = NULL ;
char * obit = NULL ;
char * classname = NULL ;
int anim = 0 ;
int killer = 0 ;
int i = 0 ;
int BottomlessPitDeath = 0 ;
static int deathNum ;
gentity_t * ent = NULL ;
gentity_t * detpack = NULL ;
playerState_t * ps = & self - > client - > ps ;
if ( ps - > pm_type = = PM_DEAD ) {
return ;
}
if ( level . intermissiontime ! = 0 ) {
return ;
}
//RPG-X: RedTechie - Trying to make sure player dies when there health is 1 without medics revive turned on
//RPG-X | Phenix | 05/04/2005 - Read learn that "=" sets where "==" is an if statement!!!
if ( self - > health = = 1 ) {
self - > health = 0 ;
}
ps - > pm_type = PM_DEAD ;
//need to copy health here because pm_type was getting reset to PM_NORMAL if ClientThink_real was called before the STAT_HEALTH was updated
ps - > stats [ STAT_HEALTH ] = self - > health ;
// check if we are in a NODROP Zone and died from a TRIGGER HURT
// if so, we assume that this resulted from a fall to a "bottomless pit" and
// treat it differently...
//
// Any problems with other places in the code?
//
BottomlessPitDeath = 0 ; // initialize
contents = trap_PointContents ( self - > r . currentOrigin , - 1 ) ;
if ( ( contents & CONTENTS_NODROP ) ! = 0 & & ( meansOfDeath = = MOD_TRIGGER_HURT ) ) {
BottomlessPitDeath = 1 ;
}
// similarly, if El Corpso here has already dropped a detpack, blow it up
classname = BG_FindClassnameForHoldable ( HI_DETPACK ) ;
if ( classname ) {
while ( ( detpack = G_Find ( detpack , FOFS ( classname ) , classname ) ) ! = NULL ) {
if ( detpack - > parent = = self ) {
detpack - > think = DetonateDetpack ; // Detonate next think.
detpack - > nextthink = level . time ;
}
}
}
if ( attacker ! = NULL ) {
killer = attacker - > s . number ;
if ( attacker - > client ! = NULL ) {
killerName = attacker - > client - > pers . netname ;
}
else {
killerName = " <non-client> " ;
}
}
else {
killer = ENTITYNUM_WORLD ;
killerName = " <world> " ;
}
if ( killer < 0 | | killer > = MAX_CLIENTS ) {
killer = ENTITYNUM_WORLD ;
killerName = " <world> " ;
}
if ( meansOfDeath < 0 | | meansOfDeath > = sizeof ( modNames ) / sizeof ( modNames [ 0 ] ) ) {
obit = " <bad obituary> " ;
}
else {
obit = modNames [ meansOfDeath ] ;
}
G_LogPrintf ( " Kill: %i %i %i: %s killed %s by %s \n " , killer , self - > s . number , meansOfDeath , killerName , self - > client - > pers . netname , obit ) ;
G_LogWeaponKill ( killer , meansOfDeath ) ;
G_LogWeaponDeath ( self - > s . number , self - > s . weapon ) ;
if ( attacker ! = NULL & & attacker - > client ! = NULL & & attacker - > inuse ) {
G_LogWeaponFrag ( killer , self - > s . number ) ;
}
if ( meansOfDeath ! = MOD_RESPAWN ) {
// broadcast the death event to everyone
ent = G_TempEntity ( self - > r . currentOrigin , EV_OBITUARY ) ;
ent - > s . eventParm = meansOfDeath ;
ent - > s . otherEntityNum = self - > s . number ;
ent - > s . otherEntityNum2 = killer ;
ent - > r . svFlags = SVF_BROADCAST ; // send to everyone
}
self - > enemy = attacker ;
ps - > persistant [ PERS_KILLED ] + + ;
if ( self = = attacker ) {
self - > client - > pers . teamState . suicides + + ;
}
//RPG-X: Redtechie - No awards or score calculations
////////////////////////////////////////////////////////////////////////
if ( attacker ! = NULL & & attacker - > client ! = NULL ) {
if ( attacker = = self ) {
if ( meansOfDeath ! = MOD_RESPAWN ) { //just changing class
G_Client_AddScore ( attacker , - 1 ) ;
}
}
else {
attacker - > client - > pers . teamState . frags + + ;
G_Client_AddScore ( attacker , 1 ) ;
// Check to see if the player is on a streak.
attacker - > client - > streakCount + + ;
attacker - > client - > lastKillTime = level . time ;
}
}
else {
if ( meansOfDeath ! = MOD_RESPAWN ) { //not just changing class
G_Client_AddScore ( self , - 1 ) ;
}
}
////////////////////////////////////////////////////////////////////////
//RPG-X: Redtechie - agian no need
// Add team bonuses
//Team_FragBonuses(self, inflictor, attacker);
// if client is in a nodrop area, don't drop anything (but return CTF flags!)
if ( ( contents & CONTENTS_NODROP ) = = 0 & & meansOfDeath ! = MOD_SUICIDE & & meansOfDeath ! = MOD_RESPAWN ) { //action hero doesn't drop stuff
//don't drop stuff in specialty mode
if ( meansOfDeath ! = MOD_RESPAWN ) {
G_Client_TossClientItems ( self , qfalse ) ;
}
}
DeathmatchScoreboardMessage ( self ) ; // show scores
// send updated scores to any clients that are following this one,
// or they would get stale scoreboards
for ( i = 0 ; i < level . maxclients ; i + + ) {
gclient_t * client = NULL ;
client = & level . clients [ i ] ;
if ( client = = NULL ) {
continue ;
}
if ( client - > pers . connected ! = CON_CONNECTED ) {
continue ;
}
if ( client - > sess . sessionTeam ! = TEAM_SPECTATOR ) {
continue ;
}
if ( client - > sess . spectatorClient = = self - > s . number ) {
DeathmatchScoreboardMessage ( g_entities + i ) ;
}
}
self - > takedamage = qtrue ; // can still be gibbed
self - > s . weapon = WP_0 ;
self - > s . powerups = 0 ;
self - > r . contents = CONTENTS_CORPSE ;
self - > s . loopSound = 0 ;
self - > r . maxs [ 2 ] = - 8 ;
// don't allow respawn until the death anim is done
// g_forcerespawn may force spawning at some later time
self - > client - > respawnTime = level . time + 1700 ;
// We always want to see the body for special animations, so make sure not to gib right away:
if ( meansOfDeath = = MOD_CRIFLE_ALT | |
meansOfDeath = = MOD_DETPACK | |
meansOfDeath = = MOD_QUANTUM_ALT | |
meansOfDeath = = MOD_DREADNOUGHT_ALT | |
meansOfDeath = = MOD_PHASER_ALT ) //RPG-X: RedTechie - Added phaser alt disnt
{
self - > health = 0 ;
}
//RPG-X : Model system - Death animations now based on vector hit
if ( meansOfDeath = = MOD_FALLING ) {
anim = BOTH_FALLDEATH1LAND ;
}
else if ( self - > waterlevel = = 3 ) {
anim = BOTH_FLOAT2 ;
}
else {
if ( meansOfDeath = = MOD_PHASER | | meansOfDeath = = MOD_PHASER_ALT ) {
if ( ( self - > client - > lasthurt_location & LOCATION_FRONT ) ! = 0 ) {
anim = BOTH_DEATHBACKWARD1 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_BACK ) ! = 0 ) {
anim = BOTH_DEATHFORWARD2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_LEFT ) ! = 0 ) {
anim = BOTH_DEATH2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_RIGHT ) ! = 0 ) {
anim = BOTH_DEATH2 ;
}
else {
anim = BOTH_DEATH1 ;
}
}
else {
if ( ( self - > client - > lasthurt_location & LOCATION_FRONT ) ! = 0 ) {
anim = BOTH_DEATHBACKWARD2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_BACK ) ! = 0 ) {
anim = BOTH_DEATHFORWARD1 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_LEFT ) ! = 0 ) {
anim = BOTH_DEATHFORWARD2 ;
}
else if ( ( self - > client - > lasthurt_location & LOCATION_RIGHT ) ! = 0 ) {
anim = BOTH_DEATHFORWARD2 ;
}
else {
anim = BOTH_DEATH1 ;
}
}
}
ps - > stats [ LEGSANIM ] = ( ( ps - > stats [ LEGSANIM ] & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim ;
ps - > stats [ TORSOANIM ] = ( ( ps - > stats [ TORSOANIM ] & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim ;
if ( ( BottomlessPitDeath = = 1 ) & & ( killer = = ENTITYNUM_WORLD ) ) {
//G_AddEvent( self, EV_FALL_FAR, killer ); ?? Need to play falling SF now, or
// use designer trigger??
//FIXME: need *some* kind of death anim!
}
else {
// normal death
switch ( meansOfDeath ) {
case MOD_PHASER_ALT : //RPG-X: RedTechie - Added better effect for alt phaser
if ( rpg_phaserdisintegrates . integer = = 1 ) { //RPG-X: RedTechie - Check to see if we want this
G_AddEvent ( self , EV_DISINTEGRATION , killer ) ;
ps - > powerups [ PW_DISINTEGRATE ] = level . time + 100000 ;
VectorClear ( ps - > velocity ) ;
self - > takedamage = qfalse ;
self - > r . contents = 0 ;
}
break ;
case MOD_CRIFLE_ALT :
break ;
case MOD_QUANTUM_ALT :
G_AddEvent ( self , EV_DISINTEGRATION2 , killer ) ;
ps - > powerups [ PW_EXPLODE ] = level . time + 100000 ;
VectorClear ( ps - > velocity ) ;
self - > takedamage = qfalse ;
self - > r . contents = 0 ;
break ;
case MOD_SCAVENGER_ALT :
case MOD_SCAVENGER_ALT_SPLASH :
case MOD_GRENADE :
case MOD_GRENADE_ALT :
case MOD_GRENADE_SPLASH :
case MOD_GRENADE_ALT_SPLASH :
case MOD_QUANTUM :
case MOD_QUANTUM_SPLASH :
case MOD_QUANTUM_ALT_SPLASH :
case MOD_DETPACK :
G_AddEvent ( self , EV_EXPLODESHELL , killer ) ;
ps - > powerups [ PW_EXPLODE ] = level . time + 100000 ;
VectorClear ( ps - > velocity ) ;
self - > takedamage = qfalse ;
self - > r . contents = 0 ;
break ;
case MOD_DREADNOUGHT :
case MOD_DREADNOUGHT_ALT :
G_AddEvent ( self , EV_ARCWELD_DISINT , killer ) ;
ps - > powerups [ PW_ARCWELD_DISINT ] = level . time + 100000 ;
VectorClear ( ps - > velocity ) ;
self - > takedamage = qfalse ;
self - > r . contents = 0 ;
break ;
case MOD_FALLING :
G_AddEvent ( self , EV_SPLAT , killer ) ;
break ;
default :
G_AddEvent ( self , irandom ( EV_DEATH1 , EV_DEATH3 ) , killer ) ;
break ;
}
// the body can still be gibbed
self - > die = body_die ;
}
// globally cycle through the different death animations
deathNum = ( deathNum + 1 ) % 3 ;
trap_LinkEntity ( self ) ;
} //RPG-X: RedTechie - End of my if statment for medics revive check
} //RPG-X: RedTechie - End of void
qboolean G_Client_IsBorg ( gentity_t * ent ) {
if ( ent = = NULL ) {
return qfalse ;
}
if ( ent - > client = = NULL ) {
return qfalse ;
}
if ( g_classData [ ent - > client - > sess . sessionClass ] . isBorg ) {
return qtrue ;
}
else {
return qfalse ;
}
}