Files
neo.yt-tremulous/src/cgame/cg_playerstate.c
T
2008-01-05 04:20:33 +00:00

318 lines
8.1 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2000-2006 Tim Angus
This file is part of Tremulous.
Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// 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 changed teams
if( ps->persistant[ PERS_TEAM ] != ops->persistant[ PERS_TEAM ] )
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 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_TEAM ] != TEAM_SPECTATOR )
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;
}
}