2012-08-04 10:54:37 +00:00
|
|
|
// Copyright (C) 1999-2000 Id Software, Inc.
|
2012-01-22 21:34:33 +00:00
|
|
|
//
|
|
|
|
// 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_CheckAmmo
|
|
|
|
|
|
|
|
If the ammo has gone low enough to generate the warning, play a sound
|
|
|
|
==============
|
|
|
|
*/
|
|
|
|
void CG_CheckAmmo( void ) {
|
|
|
|
int i;
|
|
|
|
int total;
|
|
|
|
int weapons;
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
if ( cg.lowAmmoWarning > 2 )
|
|
|
|
{//a timed message, draws for a specific amount of time
|
|
|
|
if ( cg.lowAmmoWarning > cg.frametime )
|
|
|
|
{
|
|
|
|
cg.lowAmmoWarning -= cg.frametime;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cg.lowAmmoWarning = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
// see about how many seconds of ammo we have remaining
|
|
|
|
weapons = cg.snap->ps.stats[ STAT_WEAPONS ];
|
|
|
|
total = 0;
|
2012-08-04 10:54:37 +00:00
|
|
|
|
|
|
|
//TiM
|
|
|
|
//for ( i = WP_5 ; i < WP_NUM_WEAPONS ; i++ ) {
|
|
|
|
for ( i = WP_1 ; i < WP_NUM_WEAPONS ; i++ ) {
|
2012-01-22 21:34:33 +00:00
|
|
|
if ( ! ( weapons & ( 1 << i ) ) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
switch ( i ) {
|
2012-08-04 10:54:37 +00:00
|
|
|
case WP_10:
|
|
|
|
case WP_8:
|
|
|
|
case WP_1:
|
|
|
|
case WP_6:
|
2012-01-22 21:34:33 +00:00
|
|
|
total += cg.snap->ps.ammo[i] * 1000;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
total += cg.snap->ps.ammo[i] * 200;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ( total >= 5000 ) {
|
|
|
|
cg.lowAmmoWarning = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( total == 0 ) {
|
|
|
|
cg.lowAmmoWarning = 2;
|
|
|
|
} else {
|
|
|
|
cg.lowAmmoWarning = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// play a sound on transitions
|
2012-08-04 10:54:37 +00:00
|
|
|
// RPG-X | Phenix | 13/02/2005
|
|
|
|
/*if ( cg.lowAmmoWarning != previous ) {
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND );
|
2012-08-04 10:54:37 +00:00
|
|
|
}*/
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
CG_DamageFeedback
|
|
|
|
==============
|
|
|
|
*/
|
2012-08-04 10:54:37 +00:00
|
|
|
void CG_DamageFeedback( int yawByte, int pitchByte, int damage, int shielddamage ) {
|
2012-01-22 21:34:33 +00:00
|
|
|
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;
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
|
|
|
|
kick = (damage + shielddamage*0.5) * scale;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
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.1 ) {
|
2012-08-04 10:54:37 +00:00
|
|
|
dist = 0.1;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cg.v_dmg_roll = kick * left;
|
|
|
|
|
|
|
|
cg.v_dmg_pitch = -kick * front;
|
|
|
|
|
|
|
|
if ( front <= 0.1 ) {
|
2012-08-04 10:54:37 +00:00
|
|
|
front = 0.1;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
cg.damageValue = damage * scale;
|
|
|
|
if (cg.damageValue > 10)
|
|
|
|
{
|
|
|
|
cg.damageValue = 1.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cg.damageValue *= 0.1;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
cg.damageShieldValue = shielddamage;
|
|
|
|
if (cg.damageShieldValue > 10)
|
|
|
|
{
|
|
|
|
cg.damageShieldValue = 1.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cg.damageShieldValue *= 0.1;
|
|
|
|
}
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
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_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)]) ) {
|
2012-08-04 10:54:37 +00:00
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
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 );
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
// cg.predictableEvents[ i & (MAX_PREDICTED_EVENTS-1) ] = event;
|
2012-01-22 21:34:33 +00:00
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
// cg.eventSequence++;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
CG_CheckLocalSounds
|
|
|
|
==================
|
|
|
|
*/
|
2012-08-04 10:54:37 +00:00
|
|
|
void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops )
|
|
|
|
{
|
|
|
|
// int highScore;
|
|
|
|
|
|
|
|
// The most important thing to know is if you are doing damage.
|
|
|
|
//RPG-X - TiM
|
|
|
|
/*if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] )
|
|
|
|
{
|
|
|
|
int diffhit, diffshields;
|
|
|
|
|
|
|
|
diffhit = ps->persistant[PERS_HITS] - ops->persistant[PERS_HITS];
|
|
|
|
diffshields = ps->persistant[PERS_SHIELDS] - ops->persistant[PERS_SHIELDS];
|
|
|
|
if (diffshields > diffhit/2)
|
|
|
|
{ // We also hit shields along the way, so consider them "pierced".
|
|
|
|
trap_S_StartLocalSound( cgs.media.shieldPierceSound, CHAN_LOCAL_SOUND );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Shields didn't really stand in our way.
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_S_StartLocalSound( cgs.media.hitSound, CHAN_LOCAL_SOUND );
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
}
|
|
|
|
// The second most important thing to worry about is whether you hurt a friend.
|
|
|
|
else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] )
|
|
|
|
{
|
2012-01-22 21:34:33 +00:00
|
|
|
trap_S_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND );
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
// Finally if all this damage bounced off the shields, indicate this.
|
|
|
|
else if (ps->persistant[PERS_SHIELDS] > ops->persistant[PERS_SHIELDS])
|
|
|
|
{
|
|
|
|
// hit shields and the damage didn't go through
|
|
|
|
trap_S_StartLocalSound( cgs.media.shieldHitSound, CHAN_LOCAL_SOUND );
|
|
|
|
}*/
|
2012-01-22 21:34:33 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CG_CheckDamageDealt(playerState_t *ps, playerState_t *ops)
|
|
|
|
{
|
|
|
|
static int damagetime;
|
|
|
|
static int damageamount;
|
|
|
|
|
|
|
|
if (cg_reportDamage.integer)
|
|
|
|
{
|
|
|
|
if (ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS])
|
|
|
|
{ // We did some damage this frame.
|
|
|
|
if (damagetime+1000 < cg.time)
|
|
|
|
{ // Start a new tally.
|
|
|
|
damageamount = ps->persistant[PERS_HITS] - ops->persistant[PERS_HITS];
|
|
|
|
damagetime = cg.time;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Add to a tally that's already here.
|
|
|
|
damageamount += ps->persistant[PERS_HITS] - ops->persistant[PERS_HITS];
|
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
|
|
|
|
// Report the sum of damage done this second.
|
|
|
|
if (damageamount > 0 && (damagetime+1000 <= cg.time))
|
|
|
|
{
|
|
|
|
Com_Printf("Damage this second: %d\n", damageamount);
|
|
|
|
damageamount = 0;
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
}
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
|
|
|
|
|
2012-01-22 21:34:33 +00:00
|
|
|
/*
|
|
|
|
===============
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// damage events (player is getting wounded)
|
2012-08-04 10:54:37 +00:00
|
|
|
if ( ps->damageEvent != ops->damageEvent && (ps->damageCount || ps->damageShieldCount)) {
|
|
|
|
CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount, ps->damageShieldCount);
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// respawning
|
|
|
|
if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) {
|
|
|
|
CG_Respawn();
|
|
|
|
}
|
|
|
|
|
2012-08-04 10:54:37 +00:00
|
|
|
/* if ( cg.mapRestart ) { //q3 update -not tested yet
|
2012-01-22 21:34:33 +00:00
|
|
|
CG_Respawn();
|
|
|
|
cg.mapRestart = qfalse;
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
*/
|
2012-01-22 21:34:33 +00:00
|
|
|
if ( cg.snap->ps.pm_type != PM_INTERMISSION
|
2012-08-04 10:54:37 +00:00
|
|
|
&& ps->persistant[PERS_TEAM] != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) {
|
2012-01-22 21:34:33 +00:00
|
|
|
CG_CheckLocalSounds( ps, ops );
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for going low on ammo
|
|
|
|
CG_CheckAmmo();
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2012-08-04 10:54:37 +00:00
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
CG_CheckDamageDealt(ps, ops);
|
|
|
|
#endif //_DEBUG
|
2012-01-22 21:34:33 +00:00
|
|
|
}
|
|
|
|
|