2013-04-04 14:52:42 +00:00
// Copyright (C) 1999-2000 Id Software, Inc.
//
# include "g_local.h"
void P_SetTwitchInfo ( gclient_t * client )
{
client - > ps . painTime = level . time ;
client - > ps . painDirection ^ = 1 ;
}
/*
= = = = = = = = = = = = = = =
G_DamageFeedback
Called just before a snapshot is sent to the given player .
Totals up all damage and generates both the player_state_t
damage values to that client for pain blends and kicks , and
global pain sound events for all clients .
= = = = = = = = = = = = = = =
*/
void P_DamageFeedback ( gentity_t * player ) {
gclient_t * client ;
float count ;
vec3_t angles ;
client = player - > client ;
if ( client - > ps . pm_type = = PM_DEAD ) {
return ;
}
// total points of damage shot at the player this frame
count = client - > damage_blood + client - > damage_armor ;
if ( count = = 0 ) {
return ; // didn't take any damage
}
if ( count > 255 ) {
count = 255 ;
}
// send the information to the client
// world damage (falling, slime, etc) uses a special code
// to make the blend blob centered instead of positional
if ( client - > damage_fromWorld ) {
client - > ps . damagePitch = 255 ;
client - > ps . damageYaw = 255 ;
client - > damage_fromWorld = qfalse ;
} else {
vectoangles ( client - > damage_from , angles ) ;
client - > ps . damagePitch = angles [ PITCH ] / 360.0 * 256 ;
client - > ps . damageYaw = angles [ YAW ] / 360.0 * 256 ;
}
// play an apropriate pain sound
if ( ( level . time > player - > pain_debounce_time ) & & ! ( player - > flags & FL_GODMODE ) ) {
// don't do more than two pain sounds a second
if ( level . time - client - > ps . painTime < 500 ) {
return ;
}
P_SetTwitchInfo ( client ) ;
player - > pain_debounce_time = level . time + 700 ;
G_AddEvent ( player , EV_PAIN , player - > health ) ;
client - > ps . damageEvent + + ;
2013-04-04 18:24:26 +00:00
if ( client - > damage_armor & & ! client - > damage_blood )
{
client - > ps . damageType = 1 ; //pure shields
}
else if ( client - > damage_armor )
{
client - > ps . damageType = 2 ; //shields and health
}
else
{
client - > ps . damageType = 0 ; //pure health
}
2013-04-04 14:52:42 +00:00
}
client - > ps . damageCount = count ;
//
// clear totals
//
client - > damage_blood = 0 ;
client - > damage_armor = 0 ;
client - > damage_knockback = 0 ;
}
/*
= = = = = = = = = = = = =
P_WorldEffects
Check for lava / slime contents and drowning
= = = = = = = = = = = = =
*/
void P_WorldEffects ( gentity_t * ent ) {
qboolean envirosuit ;
int waterlevel ;
if ( ent - > client - > noclip ) {
ent - > client - > airOutTime = level . time + 12000 ; // don't need air
return ;
}
waterlevel = ent - > waterlevel ;
envirosuit = ent - > client - > ps . powerups [ PW_BATTLESUIT ] > level . time ;
//
// check for drowning
//
if ( waterlevel = = 3 ) {
// envirosuit give air
if ( envirosuit ) {
ent - > client - > airOutTime = level . time + 10000 ;
}
// if out of air, start drowning
if ( ent - > client - > airOutTime < level . time ) {
// drown!
ent - > client - > airOutTime + = 1000 ;
if ( ent - > health > 0 ) {
// take more damage the longer underwater
ent - > damage + = 2 ;
if ( ent - > damage > 15 )
ent - > damage = 15 ;
// play a gurp sound instead of a normal pain sound
if ( ent - > health < = ent - > damage ) {
G_Sound ( ent , CHAN_VOICE , G_SoundIndex ( /*"*drown.wav"*/ " sound/player/gurp1.wav " ) ) ;
} else if ( rand ( ) & 1 ) {
G_Sound ( ent , CHAN_VOICE , G_SoundIndex ( " sound/player/gurp1.wav " ) ) ;
} else {
G_Sound ( ent , CHAN_VOICE , G_SoundIndex ( " sound/player/gurp2.wav " ) ) ;
}
// don't play a normal pain sound
ent - > pain_debounce_time = level . time + 200 ;
G_Damage ( ent , NULL , NULL , NULL , NULL ,
ent - > damage , DAMAGE_NO_ARMOR , MOD_WATER ) ;
}
}
} else {
ent - > client - > airOutTime = level . time + 12000 ;
ent - > damage = 2 ;
}
//
// check for sizzle damage (move to pmove?)
//
if ( waterlevel & &
( ent - > watertype & ( CONTENTS_LAVA | CONTENTS_SLIME ) ) ) {
if ( ent - > health > 0
& & ent - > pain_debounce_time < = level . time ) {
if ( envirosuit ) {
G_AddEvent ( ent , EV_POWERUP_BATTLESUIT , 0 ) ;
} else {
if ( ent - > watertype & CONTENTS_LAVA ) {
G_Damage ( ent , NULL , NULL , NULL , NULL ,
30 * waterlevel , 0 , MOD_LAVA ) ;
}
if ( ent - > watertype & CONTENTS_SLIME ) {
G_Damage ( ent , NULL , NULL , NULL , NULL ,
10 * waterlevel , 0 , MOD_SLIME ) ;
}
}
}
}
}
//==============================================================
extern void G_ApplyKnockback ( gentity_t * targ , vec3_t newDir , float knockback ) ;
void DoImpact ( gentity_t * self , gentity_t * other , qboolean damageSelf )
{
float magnitude , my_mass ;
vec3_t velocity ;
int cont ;
if ( self - > client )
{
VectorCopy ( self - > client - > ps . velocity , velocity ) ;
my_mass = self - > mass ;
}
else
{
VectorCopy ( self - > s . pos . trDelta , velocity ) ;
if ( self - > s . pos . trType = = TR_GRAVITY )
{
velocity [ 2 ] - = 0.25f * g_gravity . value ;
}
if ( ! self - > mass )
{
my_mass = 1 ;
}
else if ( self - > mass < = 10 )
{
my_mass = 10 ;
}
else
{
my_mass = self - > mass ; ///10;
}
}
magnitude = VectorLength ( velocity ) * my_mass / 10 ;
/*
if ( pointcontents ( self . absmax ) = = CONTENT_WATER ) //FIXME: or other watertypes
magnitude / = 3 ; //water absorbs 2/3 velocity
if ( self . classname = = " barrel " & & self . aflag ) //rolling barrels are made for impacts!
magnitude * = 3 ;
if ( self . frozen > 0 & & magnitude < 300 & & self . flags & FL_ONGROUND & & loser = = world & & self . velocity_z < - 20 & & self . last_onground + 0.3 < time )
magnitude = 300 ;
*/
if ( ! self - > client | | self - > client - > ps . lastOnGround + 300 < level . time | | ( self - > client - > ps . lastOnGround + 100 < level . time & & other - > material > = MAT_GLASS ) )
{
vec3_t dir1 , dir2 ;
float force = 0 , dot ;
if ( other - > material > = MAT_GLASS )
magnitude * = 2 ;
//damage them
if ( magnitude > = 100 & & other - > s . number < ENTITYNUM_WORLD )
{
VectorCopy ( velocity , dir1 ) ;
VectorNormalize ( dir1 ) ;
if ( VectorCompare ( other - > r . currentOrigin , vec3_origin ) )
{ //a brush with no origin
VectorCopy ( dir1 , dir2 ) ;
}
else
{
VectorSubtract ( other - > r . currentOrigin , self - > r . currentOrigin , dir2 ) ;
VectorNormalize ( dir2 ) ;
}
dot = DotProduct ( dir1 , dir2 ) ;
if ( dot > = 0.2 )
{
force = dot ;
}
else
{
force = 0 ;
}
force * = ( magnitude / 50 ) ;
cont = trap_PointContents ( other - > r . absmax , other - > s . number ) ;
if ( ( cont & CONTENTS_WATER ) ) //|| (self.classname=="barrel"&&self.aflag))//FIXME: or other watertypes
{
force / = 3 ; //water absorbs 2/3 velocity
}
/*
if ( self . frozen > 0 & & force > 10 )
force = 10 ;
*/
if ( ( force > = 1 & & other - > s . number ! = 0 ) | | force > = 10 )
{
/*
dprint ( " Damage other ( " ) ;
dprint ( loser . classname ) ;
dprint ( " ): " ) ;
dprint ( ftos ( force ) ) ;
dprint ( " \n " ) ;
*/
if ( other - > r . svFlags & SVF_GLASS_BRUSH )
{
other - > splashRadius = ( float ) ( self - > r . maxs [ 0 ] - self - > r . mins [ 0 ] ) / 4.0f ;
}
if ( other - > takedamage )
{
G_Damage ( other , self , self , velocity , self - > r . currentOrigin , force , DAMAGE_NO_ARMOR , MOD_CRUSH ) ; //FIXME: MOD_IMPACT
}
else
{
G_ApplyKnockback ( other , dir2 , force ) ;
}
}
}
if ( damageSelf & & self - > takedamage )
{
//Now damage me
//FIXME: more lenient falling damage, especially for when driving a vehicle
if ( self - > client & & self - > client - > ps . fd . forceJumpZStart )
{ //we were force-jumping
if ( self - > r . currentOrigin [ 2 ] > = self - > client - > ps . fd . forceJumpZStart )
{ //we landed at same height or higher than we landed
magnitude = 0 ;
}
else
{ //FIXME: take off some of it, at least?
magnitude = ( self - > client - > ps . fd . forceJumpZStart - self - > r . currentOrigin [ 2 ] ) / 3 ;
}
}
//if(self.classname!="monster_mezzoman"&&self.netname!="spider")//Cats always land on their feet
if ( ( magnitude > = 100 + self - > health & & self - > s . number ! = 0 & & self - > s . weapon ! = WP_SABER ) | | ( magnitude > = 700 ) ) //&& self.safe_time < level.time ))//health here is used to simulate structural integrity
{
if ( ( self - > s . weapon = = WP_SABER | | self - > s . number = = 0 ) & & self - > client & & self - > client - > ps . groundEntityNum < ENTITYNUM_NONE & & magnitude < 1000 )
{ //players and jedi take less impact damage
//allow for some lenience on high falls
magnitude / = 2 ;
/*
if ( self . absorb_time > = time ) //crouching on impact absorbs 1/2 the damage
{
magnitude / = 2 ;
}
*/
}
magnitude / = 40 ;
magnitude = magnitude - force / 2 ; //If damage other, subtract half of that damage off of own injury
if ( magnitude > = 1 )
{
//FIXME: Put in a thingtype impact sound function
/*
dprint ( " Damage self ( " ) ;
dprint ( self . classname ) ;
dprint ( " ): " ) ;
dprint ( ftos ( magnitude ) ) ;
dprint ( " \n " ) ;
*/
/*
if ( self . classname = = " player_sheep " & & self . flags & FL_ONGROUND & & self . velocity_z > - 50 )
return ;
*/
G_Damage ( self , NULL , NULL , NULL , self - > r . currentOrigin , magnitude / 2 , DAMAGE_NO_ARMOR , MOD_FALLING ) ; //FIXME: MOD_IMPACT
}
}
}
//FIXME: slow my velocity some?
// NOTENOTE We don't use lastimpact as of yet
// self->lastImpact = level.time;
/*
if ( self . flags & FL_ONGROUND )
self . last_onground = time ;
*/
}
}
/*
= = = = = = = = = = = = = = =
G_SetClientSound
= = = = = = = = = = = = = = =
*/
void G_SetClientSound ( gentity_t * ent ) {
if ( ent - > waterlevel & & ( ent - > watertype & ( CONTENTS_LAVA | CONTENTS_SLIME ) ) ) {
ent - > client - > ps . loopSound = level . snd_fry ;
} else {
ent - > client - > ps . loopSound = 0 ;
}
}
//==============================================================
/*
= = = = = = = = = = = = = =
ClientImpacts
= = = = = = = = = = = = = =
*/
void ClientImpacts ( gentity_t * ent , pmove_t * pm ) {
int i , j ;
trace_t trace ;
gentity_t * other ;
memset ( & trace , 0 , sizeof ( trace ) ) ;
for ( i = 0 ; i < pm - > numtouch ; i + + ) {
for ( j = 0 ; j < i ; j + + ) {
if ( pm - > touchents [ j ] = = pm - > touchents [ i ] ) {
break ;
}
}
if ( j ! = i ) {
continue ; // duplicated
}
other = & g_entities [ pm - > touchents [ i ] ] ;
if ( ( ent - > r . svFlags & SVF_BOT ) & & ( ent - > touch ) ) {
ent - > touch ( ent , other , & trace ) ;
}
if ( ! other - > touch ) {
continue ;
}
other - > touch ( other , ent , & trace ) ;
}
}
/*
= = = = = = = = = = = =
G_TouchTriggers
Find all trigger entities that ent ' s current position touches .
Spectators will only interact with teleporters .
= = = = = = = = = = = =
*/
void G_TouchTriggers ( gentity_t * ent ) {
int i , num ;
int touch [ MAX_GENTITIES ] ;
gentity_t * hit ;
trace_t trace ;
vec3_t mins , maxs ;
static vec3_t range = { 40 , 40 , 52 } ;
if ( ! ent - > client ) {
return ;
}
// dead clients don't activate triggers!
if ( ent - > client - > ps . stats [ STAT_HEALTH ] < = 0 ) {
return ;
}
VectorSubtract ( ent - > client - > ps . origin , range , mins ) ;
VectorAdd ( ent - > client - > ps . origin , range , maxs ) ;
num = trap_EntitiesInBox ( mins , maxs , touch , MAX_GENTITIES ) ;
// can't use ent->r.absmin, because that has a one unit pad
VectorAdd ( ent - > client - > ps . origin , ent - > r . mins , mins ) ;
VectorAdd ( ent - > client - > ps . origin , ent - > r . maxs , maxs ) ;
for ( i = 0 ; i < num ; i + + ) {
hit = & g_entities [ touch [ i ] ] ;
if ( ! hit - > touch & & ! ent - > touch ) {
continue ;
}
if ( ! ( hit - > r . contents & CONTENTS_TRIGGER ) ) {
continue ;
}
// ignore most entities if a spectator
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
if ( hit - > s . eType ! = ET_TELEPORT_TRIGGER & &
// this is ugly but adding a new ET_? type will
// most likely cause network incompatibilities
hit - > touch ! = Touch_DoorTrigger ) {
continue ;
}
}
// use seperate code for determining if an item is picked up
// so you don't have to actually contact its bounding box
if ( hit - > s . eType = = ET_ITEM ) {
if ( ! BG_PlayerTouchesItem ( & ent - > client - > ps , & hit - > s , level . time ) ) {
continue ;
}
} else {
if ( ! trap_EntityContact ( mins , maxs , hit ) ) {
continue ;
}
}
memset ( & trace , 0 , sizeof ( trace ) ) ;
if ( hit - > touch ) {
hit - > touch ( hit , ent , & trace ) ;
}
if ( ( ent - > r . svFlags & SVF_BOT ) & & ( ent - > touch ) ) {
ent - > touch ( ent , hit , & trace ) ;
}
}
// if we didn't touch a jump pad this pmove frame
if ( ent - > client - > ps . jumppad_frame ! = ent - > client - > ps . pmove_framecount ) {
ent - > client - > ps . jumppad_frame = 0 ;
ent - > client - > ps . jumppad_ent = 0 ;
}
}
/*
= = = = = = = = = = = =
G_MoverTouchTriggers
Find all trigger entities that ent ' s current position touches .
Spectators will only interact with teleporters .
= = = = = = = = = = = =
*/
void G_MoverTouchPushTriggers ( gentity_t * ent , vec3_t oldOrg )
{
int i , num ;
float step , stepSize , dist ;
int touch [ MAX_GENTITIES ] ;
gentity_t * hit ;
trace_t trace ;
vec3_t mins , maxs , dir , size , checkSpot ;
const vec3_t range = { 40 , 40 , 52 } ;
// non-moving movers don't hit triggers!
if ( ! VectorLengthSquared ( ent - > s . pos . trDelta ) )
{
return ;
}
VectorSubtract ( ent - > r . mins , ent - > r . maxs , size ) ;
stepSize = VectorLength ( size ) ;
if ( stepSize < 1 )
{
stepSize = 1 ;
}
VectorSubtract ( ent - > r . currentOrigin , oldOrg , dir ) ;
dist = VectorNormalize ( dir ) ;
for ( step = 0 ; step < = dist ; step + = stepSize )
{
VectorMA ( ent - > r . currentOrigin , step , dir , checkSpot ) ;
VectorSubtract ( checkSpot , range , mins ) ;
VectorAdd ( checkSpot , range , maxs ) ;
num = trap_EntitiesInBox ( mins , maxs , touch , MAX_GENTITIES ) ;
// can't use ent->r.absmin, because that has a one unit pad
VectorAdd ( checkSpot , ent - > r . mins , mins ) ;
VectorAdd ( checkSpot , ent - > r . maxs , maxs ) ;
for ( i = 0 ; i < num ; i + + )
{
hit = & g_entities [ touch [ i ] ] ;
if ( hit - > s . eType ! = ET_PUSH_TRIGGER )
{
continue ;
}
if ( hit - > touch = = NULL )
{
continue ;
}
if ( ! ( hit - > r . contents & CONTENTS_TRIGGER ) )
{
continue ;
}
if ( ! trap_EntityContact ( mins , maxs , hit ) )
{
continue ;
}
memset ( & trace , 0 , sizeof ( trace ) ) ;
if ( hit - > touch ! = NULL )
{
hit - > touch ( hit , ent , & trace ) ;
}
}
}
}
/*
= = = = = = = = = = = = = = = = =
SpectatorThink
= = = = = = = = = = = = = = = = =
*/
void SpectatorThink ( gentity_t * ent , usercmd_t * ucmd ) {
pmove_t pm ;
gclient_t * client ;
client = ent - > client ;
if ( client - > sess . spectatorState ! = SPECTATOR_FOLLOW ) {
client - > ps . pm_type = PM_SPECTATOR ;
client - > ps . speed = 400 ; // faster than normal
client - > ps . basespeed = 400 ;
// set up for pmove
memset ( & pm , 0 , sizeof ( pm ) ) ;
pm . ps = & client - > ps ;
pm . cmd = * ucmd ;
pm . tracemask = MASK_PLAYERSOLID & ~ CONTENTS_BODY ; // spectators can fly through bodies
pm . trace = trap_Trace ;
pm . pointcontents = trap_PointContents ;
pm . animations = NULL ;
// perform a pmove
Pmove ( & pm ) ;
// save results of pmove
VectorCopy ( client - > ps . origin , ent - > s . origin ) ;
G_TouchTriggers ( ent ) ;
trap_UnlinkEntity ( ent ) ;
}
client - > oldbuttons = client - > buttons ;
client - > buttons = ucmd - > buttons ;
// attack button cycles through spectators
if ( ( client - > buttons & BUTTON_ATTACK ) & & ! ( client - > oldbuttons & BUTTON_ATTACK ) ) {
Cmd_FollowCycle_f ( ent , 1 ) ;
}
if ( client - > sess . spectatorState = = SPECTATOR_FOLLOW & & ( ucmd - > upmove > 0 ) )
{ //jump now removes you from follow mode
StopFollowing ( ent ) ;
}
}
/*
= = = = = = = = = = = = = = = = =
ClientInactivityTimer
Returns qfalse if the client is dropped
= = = = = = = = = = = = = = = = =
*/
qboolean ClientInactivityTimer ( gclient_t * client ) {
if ( ! g_inactivity . integer ) {
// give everyone some time, so if the operator sets g_inactivity during
// gameplay, everyone isn't kicked
client - > inactivityTime = level . time + 60 * 1000 ;
client - > inactivityWarning = qfalse ;
} else if ( client - > pers . cmd . forwardmove | |
client - > pers . cmd . rightmove | |
client - > pers . cmd . upmove | |
( client - > pers . cmd . buttons & ( BUTTON_ATTACK | BUTTON_ALT_ATTACK ) ) ) {
client - > inactivityTime = level . time + g_inactivity . integer * 1000 ;
client - > inactivityWarning = qfalse ;
} else if ( ! client - > pers . localClient ) {
if ( level . time > client - > inactivityTime ) {
trap_DropClient ( client - level . clients , " Dropped due to inactivity " ) ;
return qfalse ;
}
if ( level . time > client - > inactivityTime - 10000 & & ! client - > inactivityWarning ) {
client - > inactivityWarning = qtrue ;
trap_SendServerCommand ( client - level . clients , " cp \" Ten seconds until inactivity drop! \n \" " ) ;
}
}
return qtrue ;
}
/*
= = = = = = = = = = = = = = = = = =
ClientTimerActions
Actions that happen once a second
= = = = = = = = = = = = = = = = = =
*/
void ClientTimerActions ( gentity_t * ent , int msec ) {
gclient_t * client ;
client = ent - > client ;
client - > timeResidual + = msec ;
while ( client - > timeResidual > = 1000 )
{
client - > timeResidual - = 1000 ;
// count down health when over max
if ( ent - > health > client - > ps . stats [ STAT_MAX_HEALTH ] ) {
ent - > health - - ;
}
// count down armor when over max
if ( client - > ps . stats [ STAT_ARMOR ] > client - > ps . stats [ STAT_MAX_HEALTH ] ) {
client - > ps . stats [ STAT_ARMOR ] - - ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = =
ClientIntermissionThink
= = = = = = = = = = = = = = = = = = = =
*/
void ClientIntermissionThink ( gclient_t * client ) {
client - > ps . eFlags & = ~ EF_TALK ;
client - > ps . eFlags & = ~ EF_FIRING ;
// the level will exit when everyone wants to or after timeouts
// swap and latch button actions
client - > oldbuttons = client - > buttons ;
client - > buttons = client - > pers . cmd . buttons ;
if ( client - > buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client - > oldbuttons ^ client - > buttons ) ) {
// this used to be an ^1 but once a player says ready, it should stick
client - > readyToExit = 1 ;
}
}
/*
= = = = = = = = = = = = = = = =
ClientEvents
Events will be passed on to the clients for presentation ,
but any server game effects are handled here
= = = = = = = = = = = = = = = =
*/
void ClientEvents ( gentity_t * ent , int oldEventSequence ) {
int i ; //, j;
int event ;
gclient_t * client ;
int damage ;
vec3_t dir ;
// vec3_t origin, angles;
// qboolean fired;
// gitem_t *item;
// gentity_t *drop;
client = ent - > client ;
if ( oldEventSequence < client - > ps . eventSequence - MAX_PS_EVENTS ) {
oldEventSequence = client - > ps . eventSequence - MAX_PS_EVENTS ;
}
for ( i = oldEventSequence ; i < client - > ps . eventSequence ; i + + ) {
event = client - > ps . events [ i & ( MAX_PS_EVENTS - 1 ) ] ;
switch ( event ) {
case EV_FALL :
case EV_ROLL :
{
2013-04-04 18:02:27 +00:00
int delta = client - > ps . eventParms [ i & ( MAX_PS_EVENTS - 1 ) ] ;
2013-04-04 14:52:42 +00:00
if ( ent - > client & & ent - > client - > ps . fallingToDeath )
{
break ;
}
if ( ent - > s . eType ! = ET_PLAYER )
{
break ; // not in the player model
}
if ( g_dmflags . integer & DF_NO_FALLING )
{
break ;
}
if ( delta < = 44 )
{
break ;
}
damage = delta * 0.16 ; //good enough for now, I guess
VectorSet ( dir , 0 , 0 , 1 ) ;
ent - > pain_debounce_time = level . time + 200 ; // no normal pain sound
G_Damage ( ent , NULL , NULL , NULL , NULL , damage , DAMAGE_NO_ARMOR , MOD_FALLING ) ;
}
break ;
case EV_FIRE_WEAPON :
FireWeapon ( ent , qfalse ) ;
ent - > client - > dangerTime = level . time ;
2013-04-04 18:01:17 +00:00
ent - > client - > ps . eFlags & = ~ EF_INVULNERABLE ;
ent - > client - > invulnerableTimer = 0 ;
2013-04-04 14:52:42 +00:00
break ;
case EV_ALT_FIRE :
FireWeapon ( ent , qtrue ) ;
ent - > client - > dangerTime = level . time ;
2013-04-04 18:01:17 +00:00
ent - > client - > ps . eFlags & = ~ EF_INVULNERABLE ;
ent - > client - > invulnerableTimer = 0 ;
2013-04-04 14:52:42 +00:00
break ;
case EV_SABER_ATTACK :
ent - > client - > dangerTime = level . time ;
2013-04-04 18:01:17 +00:00
ent - > client - > ps . eFlags & = ~ EF_INVULNERABLE ;
ent - > client - > invulnerableTimer = 0 ;
2013-04-04 14:52:42 +00:00
break ;
//rww - Note that these must be in the same order (ITEM#-wise) as they are in holdable_t
case EV_USE_ITEM1 : //seeker droid
ItemUse_Seeker ( ent ) ;
break ;
case EV_USE_ITEM2 : //shield
ItemUse_Shield ( ent ) ;
break ;
case EV_USE_ITEM3 : //medpack
ItemUse_MedPack ( ent ) ;
break ;
case EV_USE_ITEM4 : //datapad
//G_Printf("Used Datapad\n");
break ;
case EV_USE_ITEM5 : //binoculars
ItemUse_Binoculars ( ent ) ;
break ;
case EV_USE_ITEM6 : //sentry gun
ItemUse_Sentry ( ent ) ;
break ;
default :
break ;
}
}
}
/*
= = = = = = = = = = = = = =
SendPendingPredictableEvents
= = = = = = = = = = = = = =
*/
void SendPendingPredictableEvents ( playerState_t * ps ) {
gentity_t * t ;
int event , seq ;
int extEvent , number ;
// if there are still events pending
if ( ps - > entityEventSequence < ps - > eventSequence ) {
// create a temporary entity for this event which is sent to everyone
// except the client who generated the event
seq = ps - > entityEventSequence & ( MAX_PS_EVENTS - 1 ) ;
event = ps - > events [ seq ] | ( ( ps - > entityEventSequence & 3 ) < < 8 ) ;
// set external event to zero before calling BG_PlayerStateToEntityState
extEvent = ps - > externalEvent ;
ps - > externalEvent = 0 ;
// create temporary entity for event
t = G_TempEntity ( ps - > origin , event ) ;
number = t - > s . number ;
BG_PlayerStateToEntityState ( ps , & t - > s , qtrue ) ;
t - > s . number = number ;
t - > s . eType = ET_EVENTS + event ;
t - > s . eFlags | = EF_PLAYER_EVENT ;
t - > s . otherEntityNum = ps - > clientNum ;
// send to everyone except the client who generated the event
t - > r . svFlags | = SVF_NOTSINGLECLIENT ;
t - > r . singleClient = ps - > clientNum ;
// set back external event
ps - > externalEvent = extEvent ;
}
}
extern int saberOffSound ;
extern int saberOnSound ;
2013-04-04 18:01:17 +00:00
/*
= = = = = = = = = = = = = = = = = =
G_UpdateClientBroadcasts
Determines whether this client should be broadcast to any other clients .
A client is broadcast when another client is using force sight or is
= = = = = = = = = = = = = = = = = =
*/
# define MAX_JEDIMASTER_DISTANCE 2500
# define MAX_JEDIMASTER_FOV 100
# define MAX_SIGHT_DISTANCE 1500
# define MAX_SIGHT_FOV 100
static void G_UpdateForceSightBroadcasts ( gentity_t * self )
{
int i ;
// Any clients with force sight on should see this client
for ( i = 0 ; i < level . numConnectedClients ; i + + )
{
gentity_t * ent = & g_entities [ level . sortedClients [ i ] ] ;
float dist ;
vec3_t angles ;
if ( ent = = self )
{
continue ;
}
// Not using force sight so we shouldnt broadcast to this one
if ( ! ( ent - > client - > ps . fd . forcePowersActive & ( 1 < < FP_SEE ) ) )
{
continue ;
}
VectorSubtract ( self - > client - > ps . origin , ent - > client - > ps . origin , angles ) ;
dist = VectorLengthSquared ( angles ) ;
vectoangles ( angles , angles ) ;
// Too far away then just forget it
if ( dist > MAX_SIGHT_DISTANCE * MAX_SIGHT_DISTANCE )
{
continue ;
}
// If not within the field of view then forget it
if ( ! InFieldOfVision ( ent - > client - > ps . viewangles , MAX_SIGHT_FOV , angles ) )
{
break ;
}
// Turn on the broadcast bit for the master and since there is only one
// master we are done
self - > r . broadcastClients [ ent - > s . clientNum / 32 ] | = ( 1 < < ( ent - > s . clientNum % 32 ) ) ;
break ;
}
}
static void G_UpdateJediMasterBroadcasts ( gentity_t * self )
{
int i ;
// Not jedi master mode then nothing to do
if ( g_gametype . integer ! = GT_JEDIMASTER )
{
return ;
}
// This client isnt the jedi master so it shouldnt broadcast
if ( ! self - > client - > ps . isJediMaster )
{
return ;
}
// Broadcast ourself to all clients within range
for ( i = 0 ; i < level . numConnectedClients ; i + + )
{
gentity_t * ent = & g_entities [ level . sortedClients [ i ] ] ;
float dist ;
vec3_t angles ;
if ( ent = = self )
{
continue ;
}
VectorSubtract ( self - > client - > ps . origin , ent - > client - > ps . origin , angles ) ;
dist = VectorLengthSquared ( angles ) ;
vectoangles ( angles , angles ) ;
// Too far away then just forget it
if ( dist > MAX_JEDIMASTER_DISTANCE * MAX_JEDIMASTER_DISTANCE )
{
continue ;
}
// If not within the field of view then forget it
if ( ! InFieldOfVision ( ent - > client - > ps . viewangles , MAX_JEDIMASTER_FOV , angles ) )
{
continue ;
}
// Turn on the broadcast bit for the master and since there is only one
// master we are done
self - > r . broadcastClients [ ent - > s . clientNum / 32 ] | = ( 1 < < ( ent - > s . clientNum % 32 ) ) ;
}
}
void G_UpdateClientBroadcasts ( gentity_t * self )
{
// Clear all the broadcast bits for this client
memset ( self - > r . broadcastClients , 0 , sizeof ( self - > r . broadcastClients ) ) ;
// The jedi master is broadcast to everyone in range
G_UpdateJediMasterBroadcasts ( self ) ;
// Anyone with force sight on should see this client
G_UpdateForceSightBroadcasts ( self ) ;
}
2013-04-04 14:52:42 +00:00
/*
= = = = = = = = = = = = = =
ClientThink
This will be called once for each client frame , which will
usually be a couple times for each server frame on fast clients .
If " g_synchronousClients 1 " is set , this will be called exactly
once for each server frame , which makes for smooth demo recording .
= = = = = = = = = = = = = =
*/
void ClientThink_real ( gentity_t * ent ) {
gclient_t * client ;
pmove_t pm ;
int oldEventSequence ;
int msec ;
int i ;
usercmd_t * ucmd ;
client = ent - > client ;
// don't think if the client is not yet connected (and thus not yet spawned in)
if ( client - > pers . connected ! = CON_CONNECTED ) {
return ;
}
// mark the time, so the connection sprite can be removed
ucmd = & ent - > client - > pers . cmd ;
// sanity check the command time to prevent speedup cheating
if ( ucmd - > serverTime > level . time + 200 ) {
ucmd - > serverTime = level . time + 200 ;
// G_Printf("serverTime <<<<<\n" );
}
if ( ucmd - > serverTime < level . time - 1000 ) {
ucmd - > serverTime = level . time - 1000 ;
// G_Printf("serverTime >>>>>\n" );
}
msec = ucmd - > serverTime - client - > ps . commandTime ;
// following others may result in bad times, but we still want
// to check for follow toggles
if ( msec < 1 & & client - > sess . spectatorState ! = SPECTATOR_FOLLOW ) {
return ;
}
if ( msec > 200 ) {
msec = 200 ;
}
if ( pmove_msec . integer < 8 ) {
trap_Cvar_Set ( " pmove_msec " , " 8 " ) ;
}
else if ( pmove_msec . integer > 33 ) {
trap_Cvar_Set ( " pmove_msec " , " 33 " ) ;
}
if ( pmove_fixed . integer | | client - > pers . pmoveFixed ) {
ucmd - > serverTime = ( ( ucmd - > serverTime + pmove_msec . integer - 1 ) / pmove_msec . integer ) * pmove_msec . integer ;
//if (ucmd->serverTime - client->ps.commandTime <= 0)
// return;
}
//
// check for exiting intermission
//
if ( level . intermissiontime ) {
ClientIntermissionThink ( client ) ;
return ;
}
// spectators don't do much
if ( client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
if ( client - > sess . spectatorState = = SPECTATOR_SCOREBOARD ) {
return ;
}
SpectatorThink ( ent , ucmd ) ;
return ;
}
2013-04-04 18:01:17 +00:00
if ( ent & & ent - > client & & ( ent - > client - > ps . eFlags & EF_INVULNERABLE ) )
{
if ( ent - > client - > invulnerableTimer < = level . time )
{
ent - > client - > ps . eFlags & = ~ EF_INVULNERABLE ;
}
}
2013-04-04 14:52:42 +00:00
// check for inactivity timer, but never drop the local client of a non-dedicated server
if ( ! ClientInactivityTimer ( client ) ) {
return ;
}
// clear the rewards if time
if ( level . time > client - > rewardTime ) {
client - > ps . eFlags & = ~ ( EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ) ;
}
if ( client - > noclip ) {
client - > ps . pm_type = PM_NOCLIP ;
} else if ( client - > ps . eFlags & EF_DISINTEGRATION ) {
client - > ps . pm_type = PM_NOCLIP ;
} else if ( client - > ps . stats [ STAT_HEALTH ] < = 0 ) {
client - > ps . pm_type = PM_DEAD ;
} else {
if ( client - > ps . forceGripChangeMovetype )
{
client - > ps . pm_type = client - > ps . forceGripChangeMovetype ;
}
else
{
client - > ps . pm_type = PM_NORMAL ;
}
}
client - > ps . gravity = g_gravity . value ;
// set speed
client - > ps . speed = g_speed . value ;
client - > ps . basespeed = g_speed . value ;
if ( ent - > client - > ps . duelInProgress )
{
gentity_t * duelAgainst = & g_entities [ ent - > client - > ps . duelIndex ] ;
//Keep the time updated, so once this duel ends this player can't engage in a duel for another
//10 seconds. This will give other people a chance to engage in duels in case this player wants
//to engage again right after he's done fighting and someone else is waiting.
ent - > client - > ps . fd . privateDuelTime = level . time + 10000 ;
if ( ent - > client - > ps . duelTime < level . time )
{
//Bring out the sabers
if ( ent - > client - > ps . weapon = = WP_SABER & & ent - > client - > ps . saberHolstered & &
ent - > client - > ps . duelTime )
{
if ( ! saberOffSound | | ! saberOnSound )
{
saberOffSound = G_SoundIndex ( " sound/weapons/saber/saberoffquick.wav " ) ;
saberOnSound = G_SoundIndex ( " sound/weapons/saber/saberon.wav " ) ;
}
ent - > client - > ps . saberHolstered = qfalse ;
G_Sound ( ent , CHAN_AUTO , saberOnSound ) ;
G_AddEvent ( ent , EV_PRIVATE_DUEL , 2 ) ;
ent - > client - > ps . duelTime = 0 ;
}
if ( duelAgainst & & duelAgainst - > client & & duelAgainst - > inuse & &
duelAgainst - > client - > ps . weapon = = WP_SABER & & duelAgainst - > client - > ps . saberHolstered & &
duelAgainst - > client - > ps . duelTime )
{
if ( ! saberOffSound | | ! saberOnSound )
{
saberOffSound = G_SoundIndex ( " sound/weapons/saber/saberoffquick.wav " ) ;
saberOnSound = G_SoundIndex ( " sound/weapons/saber/saberon.wav " ) ;
}
duelAgainst - > client - > ps . saberHolstered = qfalse ;
G_Sound ( duelAgainst , CHAN_AUTO , saberOnSound ) ;
G_AddEvent ( duelAgainst , EV_PRIVATE_DUEL , 2 ) ;
duelAgainst - > client - > ps . duelTime = 0 ;
}
}
else
{
client - > ps . speed = 0 ;
client - > ps . basespeed = 0 ;
ucmd - > forwardmove = 0 ;
ucmd - > rightmove = 0 ;
ucmd - > upmove = 0 ;
}
if ( ! duelAgainst | | ! duelAgainst - > client | | ! duelAgainst - > inuse | |
duelAgainst - > client - > ps . duelIndex ! = ent - > s . number )
{
ent - > client - > ps . duelInProgress = 0 ;
G_AddEvent ( ent , EV_PRIVATE_DUEL , 0 ) ;
}
else if ( duelAgainst - > health < 1 | | duelAgainst - > client - > ps . stats [ STAT_HEALTH ] < 1 )
{
ent - > client - > ps . duelInProgress = 0 ;
duelAgainst - > client - > ps . duelInProgress = 0 ;
G_AddEvent ( ent , EV_PRIVATE_DUEL , 0 ) ;
G_AddEvent ( duelAgainst , EV_PRIVATE_DUEL , 0 ) ;
//Winner gets full health.. providing he's still alive
if ( ent - > health > 0 & & ent - > client - > ps . stats [ STAT_HEALTH ] > 0 )
{
2013-04-04 18:01:17 +00:00
if ( ent - > health < ent - > client - > ps . stats [ STAT_MAX_HEALTH ] )
{
ent - > client - > ps . stats [ STAT_HEALTH ] = ent - > health = ent - > client - > ps . stats [ STAT_MAX_HEALTH ] ;
}
if ( g_spawnInvulnerability . integer )
{
ent - > client - > ps . eFlags | = EF_INVULNERABLE ;
ent - > client - > invulnerableTimer = level . time + g_spawnInvulnerability . integer ;
}
2013-04-04 14:52:42 +00:00
}
/*
trap_SendServerCommand ( ent - g_entities , va ( " print \" %s %s \n \" " , ent - > client - > pers . netname , G_GetStripEdString ( " SVINGAME " , " PLDUELWINNER " ) ) ) ;
trap_SendServerCommand ( duelAgainst - g_entities , va ( " print \" %s %s \n \" " , ent - > client - > pers . netname , G_GetStripEdString ( " SVINGAME " , " PLDUELWINNER " ) ) ) ;
*/
//Private duel announcements are now made globally because we only want one duel at a time.
if ( ent - > health > 0 & & ent - > client - > ps . stats [ STAT_HEALTH ] > 0 )
{
2013-04-04 18:01:17 +00:00
trap_SendServerCommand ( - 1 , va ( " cp \" %s %s %s! \n \" " , ent - > client - > pers . netname , G_GetStripEdString ( " SVINGAME " , " PLDUELWINNER " ) , duelAgainst - > client - > pers . netname ) ) ;
2013-04-04 14:52:42 +00:00
}
else
{ //it was a draw, because we both managed to die in the same frame
trap_SendServerCommand ( - 1 , va ( " cp \" %s \n \" " , G_GetStripEdString ( " SVINGAME " , " PLDUELTIE " ) ) ) ;
}
}
else
{
vec3_t vSub ;
float subLen = 0 ;
VectorSubtract ( ent - > client - > ps . origin , duelAgainst - > client - > ps . origin , vSub ) ;
subLen = VectorLength ( vSub ) ;
if ( subLen > = 1024 )
{
ent - > client - > ps . duelInProgress = 0 ;
duelAgainst - > client - > ps . duelInProgress = 0 ;
G_AddEvent ( ent , EV_PRIVATE_DUEL , 0 ) ;
G_AddEvent ( duelAgainst , EV_PRIVATE_DUEL , 0 ) ;
trap_SendServerCommand ( - 1 , va ( " print \" %s \n \" " , G_GetStripEdString ( " SVINGAME " , " PLDUELSTOP " ) ) ) ;
}
}
}
/*
if ( client - > ps . powerups [ PW_HASTE ] ) {
client - > ps . speed * = 1.3 ;
}
*/
if ( client - > ps . usingATST & & ent - > health > 0 )
{ //we have special shot clip boxes as an ATST
ent - > r . contents | = CONTENTS_NOSHOT ;
ATST_ManageDamageBoxes ( ent ) ;
}
else
{
ent - > r . contents & = ~ CONTENTS_NOSHOT ;
client - > damageBoxHandle_Head = 0 ;
client - > damageBoxHandle_RLeg = 0 ;
client - > damageBoxHandle_LLeg = 0 ;
}
//rww - moved this stuff into the pmove code so that it's predicted properly
//BG_AdjustClientSpeed(&client->ps, &client->pers.cmd, level.time);
// set up for pmove
oldEventSequence = client - > ps . eventSequence ;
memset ( & pm , 0 , sizeof ( pm ) ) ;
if ( ent - > flags & FL_FORCE_GESTURE ) {
ent - > flags & = ~ FL_FORCE_GESTURE ;
ent - > client - > pers . cmd . buttons | = BUTTON_GESTURE ;
}
if ( ent - > client & & ent - > client - > ps . fallingToDeath & &
( level . time - FALL_FADE_TIME ) > ent - > client - > ps . fallingToDeath )
{ //die!
player_die ( ent , ent , ent , 100000 , MOD_FALLING ) ;
respawn ( ent ) ;
ent - > client - > ps . fallingToDeath = 0 ;
2013-04-04 18:24:26 +00:00
G_MuteSound ( ent - > s . number , CHAN_VOICE ) ; //stop screaming, because you are dead!
2013-04-04 14:52:42 +00:00
}
if ( ent - > client - > ps . otherKillerTime > level . time & &
ent - > client - > ps . groundEntityNum ! = ENTITYNUM_NONE & &
ent - > client - > ps . otherKillerDebounceTime < level . time )
{
ent - > client - > ps . otherKillerTime = 0 ;
2013-04-04 18:24:26 +00:00
ent - > client - > ps . otherKiller = ENTITYNUM_NONE ;
2013-04-04 14:52:42 +00:00
}
else if ( ent - > client - > ps . otherKillerTime > level . time & &
ent - > client - > ps . groundEntityNum = = ENTITYNUM_NONE )
{
if ( ent - > client - > ps . otherKillerDebounceTime < ( level . time + 100 ) )
{
ent - > client - > ps . otherKillerDebounceTime = level . time + 100 ;
}
}
2013-04-04 18:24:26 +00:00
// WP_ForcePowersUpdate( ent, msec, ucmd); //update any active force powers
// WP_SaberPositionUpdate(ent, ucmd); //check the server-side saber point, do apprioriate server-side actions (effects are cs-only)
2013-04-04 14:52:42 +00:00
if ( ( ent - > client - > pers . cmd . buttons & BUTTON_USE ) & & ent - > client - > ps . useDelay < level . time )
{
TryUse ( ent ) ;
ent - > client - > ps . useDelay = level . time + 100 ;
}
pm . ps = & client - > ps ;
pm . cmd = * ucmd ;
if ( pm . ps - > pm_type = = PM_DEAD ) {
pm . tracemask = MASK_PLAYERSOLID & ~ CONTENTS_BODY ;
}
else if ( ent - > r . svFlags & SVF_BOT ) {
pm . tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP ;
}
else {
pm . tracemask = MASK_PLAYERSOLID ;
}
pm . trace = trap_Trace ;
pm . pointcontents = trap_PointContents ;
pm . debugLevel = g_debugMove . integer ;
pm . noFootsteps = ( g_dmflags . integer & DF_NO_FOOTSTEPS ) > 0 ;
pm . pmove_fixed = pmove_fixed . integer | client - > pers . pmoveFixed ;
pm . pmove_msec = pmove_msec . integer ;
2013-04-04 18:02:27 +00:00
pm . animations = bgGlobalAnimations ; //NULL;
2013-04-04 14:52:42 +00:00
pm . gametype = g_gametype . integer ;
VectorCopy ( client - > ps . origin , client - > oldOrigin ) ;
if ( level . intermissionQueued ! = 0 & & g_singlePlayer . integer ) {
if ( level . time - level . intermissionQueued > = 1000 ) {
pm . cmd . buttons = 0 ;
pm . cmd . forwardmove = 0 ;
pm . cmd . rightmove = 0 ;
pm . cmd . upmove = 0 ;
if ( level . time - level . intermissionQueued > = 2000 & & level . time - level . intermissionQueued < = 2500 ) {
trap_SendConsoleCommand ( EXEC_APPEND , " centerview \n " ) ;
}
ent - > client - > ps . pm_type = PM_SPINTERMISSION ;
}
}
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
{
if ( g_entities [ i ] . inuse & & g_entities [ i ] . client )
{
pm . bgClients [ i ] = & g_entities [ i ] . client - > ps ;
}
}
if ( ent - > client - > ps . saberLockTime > level . time )
{
gentity_t * blockOpp = & g_entities [ ent - > client - > ps . saberLockEnemy ] ;
if ( blockOpp & & blockOpp - > inuse & & blockOpp - > client )
{
vec3_t lockDir , lockAng ;
//VectorClear( ent->client->ps.velocity );
VectorSubtract ( blockOpp - > r . currentOrigin , ent - > r . currentOrigin , lockDir ) ;
//lockAng[YAW] = vectoyaw( defDir );
vectoangles ( lockDir , lockAng ) ;
SetClientViewAngle ( ent , lockAng ) ;
}
if ( ( ent - > client - > buttons & BUTTON_ATTACK ) & & ! ( ent - > client - > oldbuttons & BUTTON_ATTACK ) )
{
ent - > client - > ps . saberLockHits + + ;
}
if ( ent - > client - > ps . saberLockHits > 2 )
{
if ( ! ent - > client - > ps . saberLockAdvance )
{
ent - > client - > ps . saberLockHits - = 3 ;
}
ent - > client - > ps . saberLockAdvance = qtrue ;
}
}
else
{
ent - > client - > ps . saberLockFrame = 0 ;
}
Pmove ( & pm ) ;
2013-04-04 18:02:27 +00:00
switch ( pm . cmd . generic_cmd )
{
case 0 :
break ;
case GENCMD_SABERSWITCH :
Cmd_ToggleSaber_f ( ent ) ;
break ;
case GENCMD_ENGAGE_DUEL :
Cmd_EngageDuel_f ( ent ) ;
break ;
case GENCMD_FORCE_HEAL :
ForceHeal ( ent ) ;
break ;
case GENCMD_FORCE_SPEED :
ForceSpeed ( ent , 0 ) ;
break ;
case GENCMD_FORCE_THROW :
ForceThrow ( ent , qfalse ) ;
break ;
case GENCMD_FORCE_PULL :
ForceThrow ( ent , qtrue ) ;
break ;
case GENCMD_FORCE_DISTRACT :
ForceTelepathy ( ent ) ;
break ;
case GENCMD_FORCE_RAGE :
ForceRage ( ent ) ;
break ;
case GENCMD_FORCE_PROTECT :
ForceProtect ( ent ) ;
break ;
case GENCMD_FORCE_ABSORB :
ForceAbsorb ( ent ) ;
break ;
case GENCMD_FORCE_HEALOTHER :
ForceTeamHeal ( ent ) ;
break ;
case GENCMD_FORCE_FORCEPOWEROTHER :
ForceTeamForceReplenish ( ent ) ;
break ;
case GENCMD_FORCE_SEEING :
ForceSeeing ( ent ) ;
break ;
case GENCMD_USE_SEEKER :
if ( ( ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & ( 1 < < HI_SEEKER ) ) & &
G_ItemUsable ( & ent - > client - > ps , HI_SEEKER ) )
{
ItemUse_Seeker ( ent ) ;
G_AddEvent ( ent , EV_USE_ITEM0 + HI_SEEKER , 0 ) ;
ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & = ~ ( 1 < < HI_SEEKER ) ;
}
break ;
case GENCMD_USE_FIELD :
if ( ( ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & ( 1 < < HI_SHIELD ) ) & &
G_ItemUsable ( & ent - > client - > ps , HI_SHIELD ) )
{
ItemUse_Shield ( ent ) ;
G_AddEvent ( ent , EV_USE_ITEM0 + HI_SHIELD , 0 ) ;
ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & = ~ ( 1 < < HI_SHIELD ) ;
}
break ;
case GENCMD_USE_BACTA :
if ( ( ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & ( 1 < < HI_MEDPAC ) ) & &
G_ItemUsable ( & ent - > client - > ps , HI_MEDPAC ) )
{
ItemUse_MedPack ( ent ) ;
G_AddEvent ( ent , EV_USE_ITEM0 + HI_MEDPAC , 0 ) ;
ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & = ~ ( 1 < < HI_MEDPAC ) ;
}
break ;
case GENCMD_USE_ELECTROBINOCULARS :
if ( ( ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & ( 1 < < HI_BINOCULARS ) ) & &
G_ItemUsable ( & ent - > client - > ps , HI_BINOCULARS ) )
{
ItemUse_Binoculars ( ent ) ;
if ( ent - > client - > ps . zoomMode = = 0 )
{
G_AddEvent ( ent , EV_USE_ITEM0 + HI_BINOCULARS , 1 ) ;
}
else
{
G_AddEvent ( ent , EV_USE_ITEM0 + HI_BINOCULARS , 2 ) ;
}
}
break ;
case GENCMD_ZOOM :
if ( ( ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & ( 1 < < HI_BINOCULARS ) ) & &
G_ItemUsable ( & ent - > client - > ps , HI_BINOCULARS ) )
{
ItemUse_Binoculars ( ent ) ;
if ( ent - > client - > ps . zoomMode = = 0 )
{
G_AddEvent ( ent , EV_USE_ITEM0 + HI_BINOCULARS , 1 ) ;
}
else
{
G_AddEvent ( ent , EV_USE_ITEM0 + HI_BINOCULARS , 2 ) ;
}
}
break ;
case GENCMD_USE_SENTRY :
if ( ( ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & ( 1 < < HI_SENTRY_GUN ) ) & &
G_ItemUsable ( & ent - > client - > ps , HI_SENTRY_GUN ) )
{
ItemUse_Sentry ( ent ) ;
G_AddEvent ( ent , EV_USE_ITEM0 + HI_SENTRY_GUN , 0 ) ;
ent - > client - > ps . stats [ STAT_HOLDABLE_ITEMS ] & = ~ ( 1 < < HI_SENTRY_GUN ) ;
}
break ;
case GENCMD_SABERATTACKCYCLE :
Cmd_SaberAttackCycle_f ( ent ) ;
break ;
default :
break ;
}
2013-04-04 14:52:42 +00:00
// save results of pmove
if ( ent - > client - > ps . eventSequence ! = oldEventSequence ) {
ent - > eventTime = level . time ;
}
if ( g_smoothClients . integer ) {
BG_PlayerStateToEntityStateExtraPolate ( & ent - > client - > ps , & ent - > s , ent - > client - > ps . commandTime , qtrue ) ;
}
else {
BG_PlayerStateToEntityState ( & ent - > client - > ps , & ent - > s , qtrue ) ;
}
SendPendingPredictableEvents ( & ent - > client - > ps ) ;
if ( ! ( ent - > client - > ps . eFlags & EF_FIRING ) ) {
client - > fireHeld = qfalse ; // for grapple
}
// use the snapped origin for linking so it matches client predicted versions
VectorCopy ( ent - > s . pos . trBase , ent - > r . currentOrigin ) ;
VectorCopy ( pm . mins , ent - > r . mins ) ;
VectorCopy ( pm . maxs , ent - > r . maxs ) ;
ent - > waterlevel = pm . waterlevel ;
ent - > watertype = pm . watertype ;
// execute client events
ClientEvents ( ent , oldEventSequence ) ;
if ( pm . useEvent )
{
//TODO: Use
// TryUse( ent );
}
// link entity now, after any personal teleporters have been used
trap_LinkEntity ( ent ) ;
if ( ! ent - > client - > noclip ) {
G_TouchTriggers ( ent ) ;
}
// NOTE: now copy the exact origin over otherwise clients can be snapped into solid
VectorCopy ( ent - > client - > ps . origin , ent - > r . currentOrigin ) ;
//test for solid areas in the AAS file
// BotTestAAS(ent->r.currentOrigin);
// touch other objects
ClientImpacts ( ent , & pm ) ;
// save results of triggers and client events
if ( ent - > client - > ps . eventSequence ! = oldEventSequence ) {
ent - > eventTime = level . time ;
}
// swap and latch button actions
client - > oldbuttons = client - > buttons ;
client - > buttons = ucmd - > buttons ;
client - > latched_buttons | = client - > buttons & ~ client - > oldbuttons ;
// Did we kick someone in our pmove sequence?
if ( client - > ps . forceKickFlip )
{
gentity_t * faceKicked = & g_entities [ client - > ps . forceKickFlip - 1 ] ;
if ( faceKicked & & faceKicked - > client & & ( ! OnSameTeam ( ent , faceKicked ) | | g_friendlyFire . integer ) & &
( ! faceKicked - > client - > ps . duelInProgress | | faceKicked - > client - > ps . duelIndex = = ent - > s . number ) & &
( ! ent - > client - > ps . duelInProgress | | ent - > client - > ps . duelIndex = = faceKicked - > s . number ) )
{
if ( faceKicked & & faceKicked - > client & & faceKicked - > health & & faceKicked - > takedamage )
{ //push them away and do pain
vec3_t oppDir ;
int strength = ( int ) VectorNormalize2 ( client - > ps . velocity , oppDir ) ;
strength * = 0.05 ;
VectorScale ( oppDir , - 1 , oppDir ) ;
G_Damage ( faceKicked , ent , ent , oppDir , client - > ps . origin , strength , DAMAGE_NO_ARMOR , MOD_MELEE ) ;
if ( faceKicked - > health > 0 & &
faceKicked - > client - > ps . stats [ STAT_HEALTH ] > 0 & &
faceKicked - > client - > ps . forceHandExtend ! = HANDEXTEND_KNOCKDOWN )
{
if ( Q_irand ( 1 , 10 ) < = 3 )
{ //only actually knock over sometimes, but always do velocity hit
faceKicked - > client - > ps . forceHandExtend = HANDEXTEND_KNOCKDOWN ;
faceKicked - > client - > ps . forceHandExtendTime = level . time + 1100 ;
faceKicked - > client - > ps . forceDodgeAnim = 0 ; //this toggles between 1 and 0, when it's 1 we should play the get up anim
}
faceKicked - > client - > ps . otherKiller = ent - > s . number ;
faceKicked - > client - > ps . otherKillerTime = level . time + 5000 ;
faceKicked - > client - > ps . otherKillerDebounceTime = level . time + 100 ;
faceKicked - > client - > ps . velocity [ 0 ] = oppDir [ 0 ] * ( strength * 40 ) ;
faceKicked - > client - > ps . velocity [ 1 ] = oppDir [ 1 ] * ( strength * 40 ) ;
faceKicked - > client - > ps . velocity [ 2 ] = 200 ;
}
G_Sound ( faceKicked , CHAN_AUTO , G_SoundIndex ( va ( " sound/weapons/melee/punch%d " , Q_irand ( 1 , 4 ) ) ) ) ;
}
}
client - > ps . forceKickFlip = 0 ;
}
// check for respawning
if ( client - > ps . stats [ STAT_HEALTH ] < = 0 ) {
// wait for the attack button to be pressed
if ( level . time > client - > respawnTime ) {
// forcerespawn is to prevent users from waiting out powerups
if ( g_forcerespawn . integer > 0 & &
( level . time - client - > respawnTime ) > g_forcerespawn . integer * 1000 ) {
respawn ( ent ) ;
return ;
}
// pressing attack or use is the normal respawn method
if ( ucmd - > buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) {
respawn ( ent ) ;
}
}
return ;
}
// perform once-a-second actions
ClientTimerActions ( ent , msec ) ;
2013-04-04 18:01:17 +00:00
G_UpdateClientBroadcasts ( ent ) ;
}
/*
= = = = = = = = = = = = = = = = = =
G_CheckClientTimeouts
Checks whether a client has exceded any timeouts and act accordingly
= = = = = = = = = = = = = = = = = =
*/
void G_CheckClientTimeouts ( gentity_t * ent )
{
// Only timeout supported right now is the timeout to spectator mode
if ( ! g_timeouttospec . integer )
{
return ;
}
// Already a spectator, no need to boot them to spectator
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR )
{
return ;
}
// See how long its been since a command was received by the client and if its
// longer than the timeout to spectator then force this client into spectator mode
if ( level . time - ent - > client - > pers . cmd . serverTime > g_timeouttospec . integer * 1000 )
{
SetTeam ( ent , " spectator " ) ;
}
2013-04-04 14:52:42 +00:00
}
/*
= = = = = = = = = = = = = = = = = =
ClientThink
A new command has arrived from the client
= = = = = = = = = = = = = = = = = =
*/
void ClientThink ( int clientNum ) {
gentity_t * ent ;
ent = g_entities + clientNum ;
trap_GetUsercmd ( clientNum , & ent - > client - > pers . cmd ) ;
// mark the time we got info, so we can display the
// phone jack if they don't get any for a while
ent - > client - > lastCmdTime = level . time ;
if ( ! ( ent - > r . svFlags & SVF_BOT ) & & ! g_synchronousClients . integer ) {
ClientThink_real ( ent ) ;
}
}
void G_RunClient ( gentity_t * ent ) {
if ( ! ( ent - > r . svFlags & SVF_BOT ) & & ! g_synchronousClients . integer ) {
return ;
}
ent - > client - > pers . cmd . serverTime = level . time ;
ClientThink_real ( ent ) ;
}
/*
= = = = = = = = = = = = = = = = = =
SpectatorClientEndFrame
= = = = = = = = = = = = = = = = = =
*/
void SpectatorClientEndFrame ( gentity_t * ent ) {
gclient_t * cl ;
// if we are doing a chase cam or a remote view, grab the latest info
if ( ent - > client - > sess . spectatorState = = SPECTATOR_FOLLOW ) {
int clientNum , flags ;
clientNum = ent - > client - > sess . spectatorClient ;
// team follow1 and team follow2 go to whatever clients are playing
if ( clientNum = = - 1 ) {
clientNum = level . follow1 ;
} else if ( clientNum = = - 2 ) {
clientNum = level . follow2 ;
}
if ( clientNum > = 0 ) {
cl = & level . clients [ clientNum ] ;
if ( cl - > pers . connected = = CON_CONNECTED & & cl - > sess . sessionTeam ! = TEAM_SPECTATOR ) {
flags = ( cl - > ps . eFlags & ~ ( EF_VOTED | EF_TEAMVOTED ) ) | ( ent - > client - > ps . eFlags & ( EF_VOTED | EF_TEAMVOTED ) ) ;
ent - > client - > ps = cl - > ps ;
ent - > client - > ps . pm_flags | = PMF_FOLLOW ;
ent - > client - > ps . eFlags = flags ;
return ;
} else {
// drop them to free spectators unless they are dedicated camera followers
if ( ent - > client - > sess . spectatorClient > = 0 ) {
ent - > client - > sess . spectatorState = SPECTATOR_FREE ;
2013-04-04 18:24:26 +00:00
ClientBegin ( ent - > client - level . clients , qtrue ) ;
2013-04-04 14:52:42 +00:00
}
}
}
}
if ( ent - > client - > sess . spectatorState = = SPECTATOR_SCOREBOARD ) {
ent - > client - > ps . pm_flags | = PMF_SCOREBOARD ;
} else {
ent - > client - > ps . pm_flags & = ~ PMF_SCOREBOARD ;
}
}
/*
= = = = = = = = = = = = = =
ClientEndFrame
Called at the end of each server frame for each connected client
A fast client will have multiple ClientThink for each ClientEdFrame ,
while a slow client may have multiple ClientEndFrame between ClientThink .
= = = = = = = = = = = = = =
*/
void ClientEndFrame ( gentity_t * ent ) {
int i ;
clientPersistant_t * pers ;
if ( ent - > client - > sess . sessionTeam = = TEAM_SPECTATOR ) {
SpectatorClientEndFrame ( ent ) ;
return ;
}
pers = & ent - > client - > pers ;
// turn off any expired powerups
for ( i = 0 ; i < MAX_POWERUPS ; i + + ) {
if ( ent - > client - > ps . powerups [ i ] < level . time ) {
ent - > client - > ps . powerups [ i ] = 0 ;
}
}
// save network bandwidth
#if 0
if ( ! g_synchronousClients - > integer & & ( ent - > client - > ps . pm_type = = PM_NORMAL | | ent - > client - > ps . pm_type = = PM_FLOAT ) ) {
// FIXME: this must change eventually for non-sync demo recording
VectorClear ( ent - > client - > ps . viewangles ) ;
}
# endif
//
// If the end of unit layout is displayed, don't give
// the player any normal movement attributes
//
if ( level . intermissiontime ) {
return ;
}
// burn from lava, etc
P_WorldEffects ( ent ) ;
// apply all the damage taken this frame
P_DamageFeedback ( ent ) ;
// add the EF_CONNECTION flag if we haven't gotten commands recently
if ( level . time - ent - > client - > lastCmdTime > 1000 ) {
ent - > s . eFlags | = EF_CONNECTION ;
} else {
ent - > s . eFlags & = ~ EF_CONNECTION ;
}
ent - > client - > ps . stats [ STAT_HEALTH ] = ent - > health ; // FIXME: get rid of ent->health...
G_SetClientSound ( ent ) ;
// set the latest infor
if ( g_smoothClients . integer ) {
BG_PlayerStateToEntityStateExtraPolate ( & ent - > client - > ps , & ent - > s , ent - > client - > ps . commandTime , qtrue ) ;
}
else {
BG_PlayerStateToEntityState ( & ent - > client - > ps , & ent - > s , qtrue ) ;
}
SendPendingPredictableEvents ( & ent - > client - > ps ) ;
// set the bit for the reachability area the client is currently in
// i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin );
// ent->client->areabits[i >> 3] |= 1 << (i & 7);
}