summaryrefslogtreecommitdiff
path: root/src/cgame/cg_playerstate.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cgame/cg_playerstate.c')
-rw-r--r--src/cgame/cg_playerstate.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/src/cgame/cg_playerstate.c b/src/cgame/cg_playerstate.c
new file mode 100644
index 0000000..242a3bb
--- /dev/null
+++ b/src/cgame/cg_playerstate.c
@@ -0,0 +1,344 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2009 Darklegion Development
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Tremulous is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Tremulous; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+// cg_playerstate.c -- this file acts on changes in a new playerState_t
+// With normal play, this will be done after local prediction, but when
+// following another player or playing back a demo, it will be checked
+// when the snapshot transitions like all the other entities
+
+
+#include "cg_local.h"
+
+/*
+==============
+CG_DamageFeedback
+==============
+*/
+void CG_DamageFeedback( int yawByte, int pitchByte, int damage )
+{
+ float left, front, up;
+ float kick;
+ int health;
+ float scale;
+ vec3_t dir;
+ vec3_t angles;
+ float dist;
+ float yaw, pitch;
+
+ // show the attacking player's head and name in corner
+ cg.attackerTime = cg.time;
+
+ // the lower on health you are, the greater the view kick will be
+ health = cg.snap->ps.stats[STAT_HEALTH];
+
+ if( health < 40 )
+ scale = 1;
+ else
+ scale = 40.0 / health;
+
+ kick = damage * scale;
+
+ if( kick < 5 )
+ kick = 5;
+
+ if( kick > 10 )
+ kick = 10;
+
+ // if yaw and pitch are both 255, make the damage always centered (falling, etc)
+ if( yawByte == 255 && pitchByte == 255 )
+ {
+ cg.damageX = 0;
+ cg.damageY = 0;
+ cg.v_dmg_roll = 0;
+ cg.v_dmg_pitch = -kick;
+ }
+ else
+ {
+ // positional
+ pitch = pitchByte / 255.0 * 360;
+ yaw = yawByte / 255.0 * 360;
+
+ angles[ PITCH ] = pitch;
+ angles[ YAW ] = yaw;
+ angles[ ROLL ] = 0;
+
+ AngleVectors( angles, dir, NULL, NULL );
+ VectorSubtract( vec3_origin, dir, dir );
+
+ front = DotProduct( dir, cg.refdef.viewaxis[ 0 ] );
+ left = DotProduct( dir, cg.refdef.viewaxis[ 1 ] );
+ up = DotProduct( dir, cg.refdef.viewaxis[ 2 ] );
+
+ dir[ 0 ] = front;
+ dir[ 1 ] = left;
+ dir[ 2 ] = 0;
+ dist = VectorLength( dir );
+
+ if( dist < 0.1f )
+ dist = 0.1f;
+
+ cg.v_dmg_roll = kick * left;
+
+ cg.v_dmg_pitch = -kick * front;
+
+ if( front <= 0.1 )
+ front = 0.1f;
+
+ cg.damageX = -left / front;
+ cg.damageY = up / dist;
+ }
+
+ // clamp the position
+ if( cg.damageX > 1.0 )
+ cg.damageX = 1.0;
+
+ if( cg.damageX < - 1.0 )
+ cg.damageX = -1.0;
+
+ if( cg.damageY > 1.0 )
+ cg.damageY = 1.0;
+
+ if( cg.damageY < - 1.0 )
+ cg.damageY = -1.0;
+
+ // don't let the screen flashes vary as much
+ if( kick > 10 )
+ kick = 10;
+
+ cg.damageValue = kick;
+ cg.v_dmg_time = cg.time + DAMAGE_TIME;
+ cg.damageTime = cg.snap->serverTime;
+}
+
+
+
+
+/*
+================
+CG_Respawn
+
+A respawn happened this snapshot
+================
+*/
+void CG_Respawn( void )
+{
+ // no error decay on player movement
+ cg.thisFrameTeleport = qtrue;
+
+ // display weapons available
+ cg.weaponSelectTime = cg.time;
+
+ // select the weapon the server says we are using
+ cg.weaponSelect = cg.snap->ps.weapon;
+
+ CG_ResetPainBlend( );
+}
+
+/*
+==============
+CG_CheckPlayerstateEvents
+
+==============
+*/
+void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops )
+{
+ int i;
+ int event;
+ centity_t *cent;
+
+ if( ps->externalEvent && ps->externalEvent != ops->externalEvent )
+ {
+ cent = &cg_entities[ ps->clientNum ];
+ cent->currentState.event = ps->externalEvent;
+ cent->currentState.eventParm = ps->externalEventParm;
+ CG_EntityEvent( cent, cent->lerpOrigin );
+ }
+
+ cent = &cg.predictedPlayerEntity; // cg_entities[ ps->clientNum ];
+
+ // go through the predictable events buffer
+ for( i = ps->eventSequence - MAX_PS_EVENTS; i < ps->eventSequence; i++ )
+ {
+ // if we have a new predictable event
+ if( i >= ops->eventSequence ||
+ // or the server told us to play another event instead of a predicted event we already issued
+ // or something the server told us changed our prediction causing a different event
+ ( i > ops->eventSequence - MAX_PS_EVENTS && ps->events[ i & ( MAX_PS_EVENTS - 1 ) ] !=
+ ops->events[ i & ( MAX_PS_EVENTS - 1 ) ] ) )
+ {
+ event = ps->events[ i & ( MAX_PS_EVENTS - 1 ) ];
+
+ cent->currentState.event = event;
+ cent->currentState.eventParm = ps->eventParms[ i & ( MAX_PS_EVENTS - 1 ) ];
+ CG_EntityEvent( cent, cent->lerpOrigin );
+ cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] = event;
+
+ cg.eventSequence++;
+ }
+ }
+}
+
+
+/*
+==================
+CG_CheckChangedPredictableEvents
+==================
+*/
+void CG_CheckChangedPredictableEvents( playerState_t *ps )
+{
+ int i;
+ int event;
+ centity_t *cent;
+
+ cent = &cg.predictedPlayerEntity;
+
+ for( i = ps->eventSequence - MAX_PS_EVENTS; i < ps->eventSequence; i++ )
+ {
+ //
+ if( i >= cg.eventSequence )
+ continue;
+
+ // if this event is not further back in than the maximum predictable events we remember
+ if( i > cg.eventSequence - MAX_PREDICTED_EVENTS )
+ {
+ // if the new playerstate event is different from a previously predicted one
+ if( ps->events[ i & ( MAX_PS_EVENTS - 1 ) ] != cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] )
+ {
+ event = ps->events[ i & ( MAX_PS_EVENTS - 1 ) ];
+ cent->currentState.event = event;
+ cent->currentState.eventParm = ps->eventParms[ i & ( MAX_PS_EVENTS - 1 ) ];
+ CG_EntityEvent( cent, cent->lerpOrigin );
+
+ cg.predictableEvents[ i & ( MAX_PREDICTED_EVENTS - 1 ) ] = event;
+
+ if( cg_showmiss.integer )
+ CG_Printf( "WARNING: changed predicted event\n" );
+ }
+ }
+ }
+}
+
+/*
+==================
+CG_CheckLocalSounds
+==================
+*/
+void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops )
+{
+ int reward;
+
+ // don't play the sounds if the player just spawned
+ if( ps->persistant[ PERS_SPECSTATE ] != ops->persistant[ PERS_SPECSTATE ] )
+ return;
+
+ // health changes of more than -1 should make pain sounds
+ if( ps->stats[ STAT_HEALTH ] < ops->stats[ STAT_HEALTH ] - 1 )
+ {
+ if( ps->stats[ STAT_HEALTH ] > 0 )
+ CG_PainEvent( &cg.predictedPlayerEntity, ps->stats[ STAT_HEALTH ] );
+ }
+
+ if( ( BG_UpgradeIsActive( UP_JETPACK, ps->stats ) ||
+ ( cg.predictedPlayerEntity.jetPackJumpTime + 1000 > cg.time &&
+ cg.predictedPlayerEntity.jetPackJumpTime + 250 < cg.time ) ) &&
+ ps->stats[ STAT_FUEL ] <= JETPACK_FUEL_LOW )
+ {
+ static int last = 0;
+
+ if( last + 740 < cg.time )
+ {
+ trap_S_StartSound( NULL, cg.predictedPlayerState.clientNum, CHAN_AUTO, cgs.media.jetpackLowFuelSound );
+ last = cg.time;
+ }
+ }
+
+ // if we are going into the intermission, don't start any voices
+ if( cg.intermissionStarted )
+ return;
+
+ // reward sounds
+ reward = qfalse;
+}
+
+
+/*
+===============
+CG_TransitionPlayerState
+
+===============
+*/
+void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops )
+{
+ // check for changing follow mode
+ if( ps->clientNum != ops->clientNum )
+ {
+ cg.thisFrameTeleport = qtrue;
+ // make sure we don't get any unwanted transition effects
+ *ops = *ps;
+
+ CG_ResetPainBlend( );
+ }
+
+ // damage events (player is getting wounded)
+ if( ps->damageEvent != ops->damageEvent && ps->damageCount )
+ CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount );
+
+ // respawning
+ if( ps->persistant[ PERS_SPAWN_COUNT ] != ops->persistant[ PERS_SPAWN_COUNT ] )
+ CG_Respawn( );
+
+ if( cg.mapRestart )
+ {
+ CG_Respawn( );
+ cg.mapRestart = qfalse;
+ }
+
+ if( cg.snap->ps.pm_type != PM_INTERMISSION &&
+ ps->persistant[ PERS_SPECSTATE ] == SPECTATOR_NOT )
+ CG_CheckLocalSounds( ps, ops );
+
+ // run events
+ CG_CheckPlayerstateEvents( ps, ops );
+
+ // smooth the ducking viewheight change
+ if( ps->viewheight != ops->viewheight )
+ {
+ cg.duckChange = ps->viewheight - ops->viewheight;
+ cg.duckTime = cg.time;
+ }
+
+ // changed team
+ if( ps->stats[ STAT_TEAM ] != ops->stats[ STAT_TEAM ] )
+ {
+ cg.lastHealthCross = 0;
+ cg.chargeMeterAlpha = 0.0f;
+ }
+
+ if( ps->stats[ STAT_FUEL ] < JETPACK_FUEL_JUMP &&
+ ops->stats[ STAT_FUEL ] >= JETPACK_FUEL_JUMP &&
+ cg.predictedPlayerEntity.jetPackJumpTime + 1000 > cg.time )
+ {
+ trap_S_StartSound( NULL, cg.predictedPlayerState.clientNum, CHAN_AUTO, cgs.media.jetpackNoJumpFuelSound );
+ }
+}
+