summaryrefslogtreecommitdiff
path: root/src/cgame/cg_view.c
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/cgame/cg_view.c
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/cgame/cg_view.c')
-rw-r--r--src/cgame/cg_view.c563
1 files changed, 357 insertions, 206 deletions
diff --git a/src/cgame/cg_view.c b/src/cgame/cg_view.c
index 428f299..871b095 100644
--- a/src/cgame/cg_view.c
+++ b/src/cgame/cg_view.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,18 +17,16 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
// cg_view.c -- setup all the parameters (position, angle, etc)
// for a 3D rendering
-
#include "cg_local.h"
-
/*
=============================================================================
@@ -181,7 +180,7 @@ static void CG_AddTestModel( void )
return;
}
- // if testing a gun, set the origin reletive to the view origin
+ // if testing a gun, set the origin relative to the view origin
if( cg.testGun )
{
VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
@@ -229,21 +228,7 @@ static void CG_CalcVrect( void )
if( cg.snap->ps.pm_type == PM_INTERMISSION )
size = 100;
else
- {
- // bound normal viewsize
- if( cg_viewsize.integer < 30 )
- {
- trap_Cvar_Set( "cg_viewsize", "30" );
- size = 30;
- }
- else if( cg_viewsize.integer > 100 )
- {
- trap_Cvar_Set( "cg_viewsize","100" );
- size = 100;
- }
- else
- size = cg_viewsize.integer;
- }
+ size = cg_viewsize.integer;
cg.refdef.width = cgs.glconfig.vidWidth * size / 100;
cg.refdef.width &= ~1;
@@ -257,119 +242,282 @@ static void CG_CalcVrect( void )
//==============================================================================
-
/*
===============
CG_OffsetThirdPersonView
===============
*/
-#define FOCUS_DISTANCE 512
-static void CG_OffsetThirdPersonView( void )
+void CG_OffsetThirdPersonView( void )
{
+ int i;
vec3_t forward, right, up;
vec3_t view;
- vec3_t focusAngles;
trace_t trace;
static vec3_t mins = { -8, -8, -8 };
static vec3_t maxs = { 8, 8, 8 };
vec3_t focusPoint;
- float focusDist;
- float forwardScale, sideScale;
vec3_t surfNormal;
-
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBING )
+ int cmdNum;
+ usercmd_t cmd, oldCmd;
+ float range;
+ vec3_t mouseInputAngles;
+ vec3_t rotationAngles;
+ vec3_t axis[ 3 ], rotaxis[ 3 ];
+ float deltaPitch;
+ static float pitch;
+ static vec3_t killerPos = { 0, 0, 0 };
+
+ // If cg_thirdpersonShoulderViewMode == 2, do shoulder view instead
+ // If cg_thirdpersonShoulderViewMode == 1, do shoulder view when chasing
+ // a wallwalker because it's really erratic to watch
+ if( ( cg_thirdPersonShoulderViewMode.integer == 2 ) ||
+ ( ( cg_thirdPersonShoulderViewMode.integer == 1 ) &&
+ ( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) &&
+ ( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) ) )
{
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING )
- VectorSet( surfNormal, 0.0f, 0.0f, -1.0f );
- else
- VectorCopy( cg.predictedPlayerState.grapplePoint, surfNormal );
+ CG_OffsetShoulderView( );
+ return;
}
- else
- VectorSet( surfNormal, 0.0f, 0.0f, 1.0f );
+ BG_GetClientNormal( &cg.predictedPlayerState, surfNormal );
+ // Set the view origin to the class's view height
VectorMA( cg.refdef.vieworg, cg.predictedPlayerState.viewheight, surfNormal, cg.refdef.vieworg );
- VectorCopy( cg.refdefViewAngles, focusAngles );
+ // Set the focus point where the camera will look (at the player's vieworg)
+ VectorCopy( cg.refdef.vieworg, focusPoint );
- // if dead, look at killer
+ // If player is dead, we want the player to be between us and the killer
+ // so pretend that the player was looking at the killer, then place cam behind them.
if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 )
{
- focusAngles[ YAW ] = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ];
- cg.refdefViewAngles[ YAW ] = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ];
+ int killerEntNum = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ];
+
+ // already looking at ourself
+ if( killerEntNum != cg.snap->ps.clientNum )
+ {
+ vec3_t lookDirection;
+ if( cg.wasDeadLastFrame == qfalse || !cg_staticDeathCam.integer )
+ {
+ VectorCopy( cg_entities[ killerEntNum ].lerpOrigin, killerPos );
+ cg.wasDeadLastFrame = qtrue;
+ }
+ VectorSubtract( killerPos, cg.refdef.vieworg, lookDirection );
+ vectoangles( lookDirection, cg.refdefViewAngles );
+ }
}
- //if ( focusAngles[PITCH] > 45 ) {
- // focusAngles[PITCH] = 45; // don't go too far overhead
- //}
- AngleVectors( focusAngles, forward, NULL, NULL );
+ // get and rangecheck cg_thirdPersonRange
+ range = cg_thirdPersonRange.value;
+ if( range > 150.0f ) range = 150.0f;
+ if( range < 30.0f ) range = 30.0f;
+
+ // Calculate the angle of the camera's position around the player.
+ // Unless in demo, PLAYING in third person, or in dead-third-person cam, allow the player
+ // to control camera position offsets using the mouse position.
+ if( cg.demoPlayback ||
+ ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) &&
+ ( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) ) )
+ {
+ // Collect our input values from the mouse.
+ cmdNum = trap_GetCurrentCmdNumber();
+ trap_GetUserCmd( cmdNum, &cmd );
+ trap_GetUserCmd( cmdNum - 1, &oldCmd );
- VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
+ // Prevent pitch from wrapping and clamp it within a [-75, 90] range.
+ // Cgame has no access to ps.delta_angles[] here, so we need to reproduce
+ // it ourselves.
+ deltaPitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
+ if( fabs(deltaPitch) < 200.0f )
+ {
+ pitch += deltaPitch;
+ }
- VectorCopy( cg.refdef.vieworg, view );
+ mouseInputAngles[ PITCH ] = pitch;
+ mouseInputAngles[ YAW ] = -1.0f * SHORT2ANGLE( cmd.angles[ YAW ] ); // yaw is inverted
+ mouseInputAngles[ ROLL ] = 0.0f;
+
+ for( i = 0; i < 3; i++ )
+ mouseInputAngles[ i ] = AngleNormalize180( mouseInputAngles[ i ] );
- VectorMA( view, 12, surfNormal, view );
+ // Set the rotation angles to be the view angles offset by the mouse input
+ // Ignore the original pitch though; it's too jerky otherwise
+ if( !cg_thirdPersonPitchFollow.integer )
+ cg.refdefViewAngles[ PITCH ] = 0.0f;
- //cg.refdefViewAngles[PITCH] *= 0.5;
+ for( i = 0; i < 3; i++ )
+ {
+ rotationAngles[ i ] = AngleNormalize180(cg.refdefViewAngles[ i ]) + mouseInputAngles[ i ];
+ AngleNormalize180( rotationAngles[ i ] );
+ }
- AngleVectors( cg.refdefViewAngles, forward, right, up );
+ // Don't let pitch go too high/too low or the camera flips around and
+ // that's really annoying.
+ // However, when we're not on the floor or ceiling (wallwalk) pitch
+ // may not be pitch, so just let it go.
+ if( surfNormal[ 2 ] > 0.5f || surfNormal[ 2 ] < -0.5f )
+ {
+ if( rotationAngles[ PITCH ] > 85.0f )
+ rotationAngles[ PITCH ] = 85.0f;
+ else if( rotationAngles[ PITCH ] < -85.0f )
+ rotationAngles[ PITCH ] = -85.0f;
+ }
+
+ // Perform the rotations specified by rotationAngles.
+ AnglesToAxis( rotationAngles, axis );
+ if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
+ !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
+ cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
+ AxisCopy( axis, rotaxis );
+
+ // Convert the new axis back to angles.
+ AxisToAngles( rotaxis, rotationAngles );
+ }
+ else
+ {
+ if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 )
+ {
+ // If we're playing the game in third person, the viewangles already
+ // take care of our mouselook, so just use them.
+ for( i = 0; i < 3; i++ )
+ rotationAngles[ i ] = cg.refdefViewAngles[ i ];
+ }
+ else // dead
+ {
+ rotationAngles[ PITCH ] = 20.0f;
+ rotationAngles[ YAW ] = cg.refdefViewAngles[ YAW ];
+ }
+ }
- forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
- sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
- VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
- VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
+ rotationAngles[ YAW ] -= cg_thirdPersonAngle.value;
- // trace a ray from the origin to the viewpoint to make sure the view isn't
- // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
+ // Move the camera range distance back.
+ AngleVectors( rotationAngles, forward, right, up );
+ VectorCopy( cg.refdef.vieworg, view );
+ VectorMA( view, -range, forward, view );
+ // Ensure that the current camera position isn't out of bounds and that there
+ // is nothing between the camera and the player.
if( !cg_cameraMode.integer )
{
+ // Trace a ray from the origin to the viewpoint to make sure the view isn't
+ // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
- if( trace.fraction != 1.0 )
+ if( trace.fraction != 1.0f )
{
VectorCopy( trace.endpos, view );
- view[ 2 ] += ( 1.0 - trace.fraction ) * 32;
- // try another trace to this position, because a tunnel may have the ceiling
- // close enogh that this is poking out
+ view[ 2 ] += ( 1.0f - trace.fraction ) * 32;
+ // Try another trace to this position, because a tunnel may have the ceiling
+ // close enogh that this is poking out.
CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
VectorCopy( trace.endpos, view );
}
}
+ // Set the camera position to what we calculated.
VectorCopy( view, cg.refdef.vieworg );
- // select pitch to look at focus point from vieword
- VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
- focusDist = sqrt( focusPoint[ 0 ] * focusPoint[ 0 ] + focusPoint[ 1 ] * focusPoint[ 1 ] );
- if ( focusDist < 1 ) {
- focusDist = 1; // should never happen
+ // The above checks may have moved the camera such that the existing viewangles
+ // may not still face the player. Recalculate them to do so.
+ // but if we're dead, don't bother because we'd rather see what killed us
+ if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 )
+ {
+ VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
+ vectoangles( focusPoint, cg.refdefViewAngles );
}
- cg.refdefViewAngles[ PITCH ] = -180 / M_PI * atan2( focusPoint[ 2 ], focusDist );
- cg.refdefViewAngles[ YAW ] -= cg_thirdPersonAngle.value;
}
+/*
+===============
+CG_OffsetShoulderView
+
+===============
+*/
+void CG_OffsetShoulderView( void )
+{
+ int i;
+ int cmdNum;
+ usercmd_t cmd, oldCmd;
+ vec3_t rotationAngles;
+ vec3_t axis[ 3 ], rotaxis[ 3 ];
+ float deltaMousePitch;
+ static float mousePitch;
+ vec3_t forward, right, up;
+ classConfig_t* classConfig;
+
+ // Ignore following pitch; it's too jerky otherwise.
+ if( !cg_thirdPersonPitchFollow.integer )
+ cg.refdefViewAngles[ PITCH ] = 0.0f;
+
+ AngleVectors( cg.refdefViewAngles, forward, right, up );
+
+ classConfig = BG_ClassConfig( cg.snap->ps.stats[ STAT_CLASS ] );
+ VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 0 ], forward, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 1 ], right, cg.refdef.vieworg );
+ VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 2 ], up, cg.refdef.vieworg );
+
+ // If someone is playing like this, the rest is already taken care of
+ // so just get the firstperson effects and leave.
+ if( !cg.demoPlayback && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
+ {
+ CG_OffsetFirstPersonView();
+ return;
+ }
+
+ // Get mouse input for camera rotation.
+ cmdNum = trap_GetCurrentCmdNumber();
+ trap_GetUserCmd( cmdNum, &cmd );
+ trap_GetUserCmd( cmdNum - 1, &oldCmd );
+
+ // Prevent pitch from wrapping and clamp it within a [30, -50] range.
+ // Cgame has no access to ps.delta_angles[] here, so we need to reproduce
+ // it ourselves here.
+ deltaMousePitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] );
+ if( fabs(deltaMousePitch) < 200.0f )
+ mousePitch += deltaMousePitch;
+
+ // Handle pitch.
+ rotationAngles[ PITCH ] = mousePitch;
+
+ rotationAngles[ PITCH ] = AngleNormalize180( rotationAngles[ PITCH ] + AngleNormalize180( cg.refdefViewAngles[ PITCH ] ) );
+ if( rotationAngles [ PITCH ] < -90.0f ) rotationAngles [ PITCH ] = -90.0f;
+ if( rotationAngles [ PITCH ] > 90.0f ) rotationAngles [ PITCH ] = 90.0f;
+
+ // Yaw and Roll are much easier.
+ rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ];
+ rotationAngles[ ROLL ] = 0.0f;
+
+ // Perform the rotations.
+ AnglesToAxis( rotationAngles, axis );
+ if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) ||
+ !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse,
+ cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) )
+ AxisCopy( axis, rotaxis );
+
+ AxisToAngles( rotaxis, rotationAngles );
+
+ // Actually set the viewangles.
+ for( i = 0; i < 3; i++ )
+ cg.refdefViewAngles[ i ] = rotationAngles[ i ];
+
+ // Now run the first person stuff so we get various effects added.
+ CG_OffsetFirstPersonView( );
+}
+
// this causes a compiler bug on mac MrC compiler
static void CG_StepOffset( void )
{
float steptime;
- int timeDelta;
+ int timeDelta;
vec3_t normal;
playerState_t *ps = &cg.predictedPlayerState;
- if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING )
- {
- if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING )
- VectorSet( normal, 0.0f, 0.0f, -1.0f );
- else
- VectorCopy( ps->grapplePoint, normal );
- }
- else
- VectorSet( normal, 0.0f, 0.0f, 1.0f );
+ BG_GetClientNormal( ps, normal );
- steptime = BG_FindSteptimeForClass( ps->stats[ STAT_PCLASS ] );
+ steptime = BG_Class( ps->stats[ STAT_CLASS ] )->steptime;
// smooth out stair climbing
timeDelta = cg.time - cg.stepTime;
@@ -378,17 +526,15 @@ static void CG_StepOffset( void )
float stepChange = cg.stepChange
* (steptime - timeDelta) / steptime;
- if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING )
- VectorMA( cg.refdef.vieworg, -stepChange, normal, cg.refdef.vieworg );
- else
- cg.refdef.vieworg[ 2 ] -= stepChange;
+ VectorMA( cg.refdef.vieworg, -stepChange, normal, cg.refdef.vieworg );
}
}
-#define PCLOUD_ROLL_AMPLITUDE 25.0f
-#define PCLOUD_ROLL_FREQUENCY 0.4f
-#define PCLOUD_ZOOM_AMPLITUDE 15
-#define PCLOUD_ZOOM_FREQUENCY 0.7f
+#define PCLOUD_ROLL_AMPLITUDE 25.0f
+#define PCLOUD_ROLL_FREQUENCY 0.4f
+#define PCLOUD_ZOOM_AMPLITUDE 15
+#define PCLOUD_ZOOM_FREQUENCY 0.625f // 2.5s / 4
+#define PCLOUD_DISORIENT_DURATION 2500
/*
@@ -397,7 +543,7 @@ CG_OffsetFirstPersonView
===============
*/
-static void CG_OffsetFirstPersonView( void )
+void CG_OffsetFirstPersonView( void )
{
float *origin;
float *angles;
@@ -409,19 +555,10 @@ static void CG_OffsetFirstPersonView( void )
vec3_t predictedVelocity;
int timeDelta;
float bob2;
- vec3_t normal, baseOrigin;
+ vec3_t normal;
playerState_t *ps = &cg.predictedPlayerState;
- if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING )
- {
- if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING )
- VectorSet( normal, 0.0f, 0.0f, -1.0f );
- else
- VectorCopy( ps->grapplePoint, normal );
- }
- else
- VectorSet( normal, 0.0f, 0.0f, 1.0f );
-
+ BG_GetClientNormal( ps, normal );
if( cg.snap->ps.pm_type == PM_INTERMISSION )
return;
@@ -429,8 +566,6 @@ static void CG_OffsetFirstPersonView( void )
origin = cg.refdef.vieworg;
angles = cg.refdefViewAngles;
- VectorCopy( origin, baseOrigin );
-
// if dead, fix the angle and don't add any kick
if( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 )
{
@@ -441,9 +576,6 @@ static void CG_OffsetFirstPersonView( void )
return;
}
- // add angles based on weapon kick
- VectorAdd( angles, cg.kick_angles, angles );
-
// add angles based on damage kick
if( cg.damageTime )
{
@@ -485,10 +617,10 @@ static void CG_OffsetFirstPersonView( void )
// add angles based on bob
// bob amount is class dependant
- if( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR )
+ if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
bob2 = 0.0f;
else
- bob2 = BG_FindBobForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] );
+ bob2 = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->bob;
#define LEVEL4_FEEDBACK 10.0f
@@ -498,7 +630,8 @@ static void CG_OffsetFirstPersonView( void )
{
if( ps->stats[ STAT_MISC ] > 0 )
{
- float fraction = (float)ps->stats[ STAT_MISC ] / (float)LEVEL4_CHARGE_TIME;
+ float fraction = (float)ps->stats[ STAT_MISC ] /
+ LEVEL4_TRAMPLE_CHARGE_MAX;
if( fraction > 1.0f )
fraction = 1.0f;
@@ -530,26 +663,24 @@ static void CG_OffsetFirstPersonView( void )
#define LEVEL3_FEEDBACK 20.0f
//provide some feedback for pouncing
- if( cg.predictedPlayerState.weapon == WP_ALEVEL3 ||
- cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG )
+ if( ( cg.predictedPlayerState.weapon == WP_ALEVEL3 ||
+ cg.predictedPlayerState.weapon == WP_ALEVEL3_UPG ) &&
+ cg.predictedPlayerState.stats[ STAT_MISC ] > 0 )
{
- if( cg.predictedPlayerState.stats[ STAT_MISC ] > 0 )
- {
- float fraction1, fraction2;
- vec3_t forward;
-
- AngleVectors( angles, forward, NULL, NULL );
- VectorNormalize( forward );
+ float fraction1, fraction2;
+ vec3_t forward;
- fraction1 = (float)( cg.time - cg.weapon2Time ) / (float)LEVEL3_POUNCE_CHARGE_TIME;
+ AngleVectors( angles, forward, NULL, NULL );
+ VectorNormalize( forward );
- if( fraction1 > 1.0f )
- fraction1 = 1.0f;
+ fraction1 = (float)cg.predictedPlayerState.stats[ STAT_MISC ] /
+ LEVEL3_POUNCE_TIME_UPG;
+ if( fraction1 > 1.0f )
+ fraction1 = 1.0f;
- fraction2 = -sin( fraction1 * M_PI / 2 );
+ fraction2 = -sin( fraction1 * M_PI / 2 );
- VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin );
- }
+ VectorMA( origin, LEVEL3_FEEDBACK * fraction2, forward, origin );
}
#define STRUGGLE_DIST 5.0f
@@ -562,7 +693,6 @@ static void CG_OffsetFirstPersonView( void )
usercmd_t cmd;
int cmdNum;
float fFraction, rFraction, uFraction;
- float fFraction2, rFraction2, uFraction2;
cmdNum = trap_GetCurrentCmdNumber();
trap_GetUserCmd( cmdNum, &cmd );
@@ -580,10 +710,6 @@ static void CG_OffsetFirstPersonView( void )
if( uFraction > 1.0f )
uFraction = 1.0f;
- fFraction2 = -sin( fFraction * M_PI / 2 );
- rFraction2 = -sin( rFraction * M_PI / 2 );
- uFraction2 = -sin( uFraction * M_PI / 2 );
-
if( cmd.forwardmove > 0 )
VectorMA( origin, STRUGGLE_DIST * fFraction, forward, origin );
else if( cmd.forwardmove < 0 )
@@ -606,14 +732,21 @@ static void CG_OffsetFirstPersonView( void )
cg.upMoveTime = cg.time;
}
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
+ if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) &&
+ ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) &&
!( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
{
- float fraction = sin( ( (float)cg.time / 1000.0f ) * M_PI * 2 * PCLOUD_ROLL_FREQUENCY );
- float pitchFraction = sin( ( (float)cg.time / 1000.0f ) * M_PI * 5 * PCLOUD_ROLL_FREQUENCY );
-
- fraction *= 1.0f - ( ( cg.time - cg.poisonedTime ) / (float)LEVEL1_PCLOUD_TIME );
- pitchFraction *= 1.0f - ( ( cg.time - cg.poisonedTime ) / (float)LEVEL1_PCLOUD_TIME );
+ float scale, fraction, pitchFraction;
+
+ scale = 1.0f - (float)( cg.time - cg.poisonedTime ) /
+ BG_PlayerPoisonCloudTime( &cg.predictedPlayerState );
+ if( scale < 0.0f )
+ scale = 0.0f;
+
+ fraction = sin( ( cg.time - cg.poisonedTime ) / 500.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) *
+ scale;
+ pitchFraction = sin( ( cg.time - cg.poisonedTime ) / 200.0f * M_PI * PCLOUD_ROLL_FREQUENCY ) *
+ scale;
angles[ ROLL ] += fraction * PCLOUD_ROLL_AMPLITUDE;
angles[ YAW ] += fraction * PCLOUD_ROLL_AMPLITUDE;
@@ -621,17 +754,17 @@ static void CG_OffsetFirstPersonView( void )
}
// this *feels* more realisitic for humans
- if( cg.predictedPlayerState.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS &&
+ ( cg.predictedPlayerState.pm_type == PM_NORMAL ||
+ cg.predictedPlayerState.pm_type == PM_JETPACK ) )
{
angles[PITCH] += cg.bobfracsin * bob2 * 0.5;
// heavy breathing effects //FIXME: sound
- if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < 0 )
+ if( cg.predictedPlayerState.stats[ STAT_STAMINA ] < STAMINA_BREATHING_LEVEL )
{
- float deltaBreath = (float)(
- cg.predictedPlayerState.stats[ STAT_STAMINA ] < 0 ?
- -cg.predictedPlayerState.stats[ STAT_STAMINA ] :
- cg.predictedPlayerState.stats[ STAT_STAMINA ] ) / 200.0;
+ float deltaBreath = ( cg.predictedPlayerState.stats[ STAT_STAMINA ] -
+ STAMINA_BREATHING_LEVEL ) / -250.0;
float deltaAngle = cos( (float)cg.time/150.0 ) * deltaBreath;
deltaAngle += ( deltaAngle < 0 ? -deltaAngle : deltaAngle ) * 0.5;
@@ -643,11 +776,7 @@ static void CG_OffsetFirstPersonView( void )
//===================================
// add view height
- // when wall climbing the viewheight is not straight up
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBING )
- VectorMA( origin, ps->viewheight, normal, origin );
- else
- origin[ 2 ] += cg.predictedPlayerState.viewheight;
+ VectorMA( origin, ps->viewheight, normal, origin );
// smooth out duck height changes
timeDelta = cg.time - cg.duckTime;
@@ -663,12 +792,7 @@ static void CG_OffsetFirstPersonView( void )
if( bob > 6 )
bob = 6;
- // likewise for bob
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBING )
- VectorMA( origin, bob, normal, origin );
- else
- origin[ 2 ] += bob;
-
+ VectorMA( origin, bob, normal, origin );
// add fall height
delta = cg.time - cg.landTime;
@@ -687,10 +811,6 @@ static void CG_OffsetFirstPersonView( void )
// add step offset
CG_StepOffset( );
-
- // add kick offset
-
- VectorAdd (origin, cg.kick_origin, origin);
}
//======================================================================
@@ -702,14 +822,17 @@ CG_CalcFov
Fixed fov at intermissions, otherwise account for fov variable and zooms.
====================
*/
-#define WAVE_AMPLITUDE 1
-#define WAVE_FREQUENCY 0.4
+#define WAVE_AMPLITUDE 1.0f
+#define WAVE_FREQUENCY 0.4f
-#define FOVWARPTIME 400.0
+#define FOVWARPTIME 400.0f
+#define BASE_FOV_Y 73.739792f // atan2( 3, 4 / tan( 90 ) )
+#define MAX_FOV_Y 120.0f
+#define MAX_FOV_WARP_Y 127.5f
static int CG_CalcFov( void )
{
- float x;
+ float y;
float phase;
float v;
int contents;
@@ -719,95 +842,114 @@ static int CG_CalcFov( void )
int inwater;
int attribFov;
usercmd_t cmd;
+ usercmd_t oldcmd;
int cmdNum;
cmdNum = trap_GetCurrentCmdNumber( );
trap_GetUserCmd( cmdNum, &cmd );
+ trap_GetUserCmd( cmdNum - 1, &oldcmd );
+
+ // switch follow modes if necessary: cycle between free -> follow -> third-person follow
+ if( cmd.buttons & BUTTON_USE_HOLDABLE && !( oldcmd.buttons & BUTTON_USE_HOLDABLE ) )
+ {
+ if ( cg.snap->ps.pm_flags & PMF_FOLLOW )
+ {
+ if( !cg.chaseFollow )
+ cg.chaseFollow = qtrue;
+ else
+ {
+ cg.chaseFollow = qfalse;
+ trap_SendClientCommand( "follow\n" );
+ }
+ }
+ else if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
+ trap_SendClientCommand( "follow\n" );
+ }
if( cg.predictedPlayerState.pm_type == PM_INTERMISSION ||
- ( cg.snap->ps.persistant[ PERS_TEAM ] == TEAM_SPECTATOR ) )
+ ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) ||
+ ( cg.renderingThirdPerson ) )
{
- // if in intermission, use a fixed value
- fov_x = 90;
+ // if in intermission or third person, use a fixed value
+ fov_y = BASE_FOV_Y;
}
else
{
// don't lock the fov globally - we need to be able to change it
- attribFov = BG_FindFovForClass( cg.predictedPlayerState.stats[ STAT_PCLASS ] );
- fov_x = attribFov;
+ attribFov = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fov * 0.75f;
+ fov_y = attribFov;
- if ( fov_x < 1 )
- fov_x = 1;
- else if ( fov_x > 160 )
- fov_x = 160;
+ if ( fov_y < 1.0f )
+ fov_y = 1.0f;
+ else if ( fov_y > MAX_FOV_Y )
+ fov_y = MAX_FOV_Y;
if( cg.spawnTime > ( cg.time - FOVWARPTIME ) &&
- BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_PCLASS ], SCA_FOVWARPS ) )
+ BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_FOVWARPS ) )
{
- float temp, temp2;
-
- temp = (float)( cg.time - cg.spawnTime ) / FOVWARPTIME;
- temp2 = ( 170 - fov_x ) * temp;
-
- //Com_Printf( "%f %f\n", temp*100, temp2*100 );
+ float fraction = (float)( cg.time - cg.spawnTime ) / FOVWARPTIME;
- fov_x = 170 - temp2;
+ fov_y = MAX_FOV_WARP_Y - ( ( MAX_FOV_WARP_Y - fov_y ) * fraction );
}
// account for zooms
- zoomFov = BG_FindZoomFovForWeapon( cg.predictedPlayerState.weapon );
- if ( zoomFov < 1 )
- zoomFov = 1;
+ zoomFov = BG_Weapon( cg.predictedPlayerState.weapon )->zoomFov * 0.75f;
+ if ( zoomFov < 1.0f )
+ zoomFov = 1.0f;
else if ( zoomFov > attribFov )
zoomFov = attribFov;
// only do all the zoom stuff if the client CAN zoom
// FIXME: zoom control is currently hard coded to BUTTON_ATTACK2
- if( BG_WeaponCanZoom( cg.predictedPlayerState.weapon ) )
+ if( BG_Weapon( cg.predictedPlayerState.weapon )->canZoom )
{
if ( cg.zoomed )
{
f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
- if ( f > 1.0 )
- fov_x = zoomFov;
+ if ( f > 1.0f )
+ fov_y = zoomFov;
else
- fov_x = fov_x + f * ( zoomFov - fov_x );
+ fov_y = fov_y + f * ( zoomFov - fov_y );
// BUTTON_ATTACK2 isn't held so unzoom next time
if( !( cmd.buttons & BUTTON_ATTACK2 ) )
{
cg.zoomed = qfalse;
- cg.zoomTime = cg.time;
+ cg.zoomTime = MIN( cg.time,
+ cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
}
}
else
{
f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
- if ( f <= 1.0 )
- fov_x = zoomFov + f * ( fov_x - zoomFov );
+ if ( f > 1.0f )
+ fov_y = fov_y;
+ else
+ fov_y = zoomFov + f * ( fov_y - zoomFov );
// BUTTON_ATTACK2 is held so zoom next time
if( cmd.buttons & BUTTON_ATTACK2 )
{
cg.zoomed = qtrue;
- cg.zoomTime = cg.time;
+ cg.zoomTime = MIN( cg.time,
+ cg.time + cg.time - cg.zoomTime - ZOOM_TIME );
}
}
}
}
- x = cg.refdef.width / tan( fov_x / 360 * M_PI );
- fov_y = atan2( cg.refdef.height, x );
- fov_y = fov_y * 360 / M_PI;
+ y = cg.refdef.height / tan( 0.5f * DEG2RAD( fov_y ) );
+ fov_x = atan2( cg.refdef.width, y );
+ fov_x = 2.0f * RAD2DEG( fov_x );
// warp if underwater
contents = CG_PointContents( cg.refdef.vieworg, -1 );
if( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) )
{
- phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
+ phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2.0f;
v = WAVE_AMPLITUDE * sin( phase );
fov_x += v;
fov_y -= v;
@@ -816,13 +958,16 @@ static int CG_CalcFov( void )
else
inwater = qfalse;
- if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
+ if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) &&
+ ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) &&
cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 &&
!( cg.snap->ps.pm_flags & PMF_FOLLOW ) )
{
- phase = cg.time / 1000.0 * PCLOUD_ZOOM_FREQUENCY * M_PI * 2;
- v = PCLOUD_ZOOM_AMPLITUDE * sin( phase );
- v *= 1.0f - ( ( cg.time - cg.poisonedTime ) / (float)LEVEL1_PCLOUD_TIME );
+ float scale = 1.0f - (float)( cg.time - cg.poisonedTime ) /
+ BG_PlayerPoisonCloudTime( &cg.predictedPlayerState );
+
+ phase = ( cg.time - cg.poisonedTime ) / 1000.0f * PCLOUD_ZOOM_FREQUENCY * M_PI * 2.0f;
+ v = PCLOUD_ZOOM_AMPLITUDE * sin( phase ) * scale;
fov_x += v;
fov_y += v;
}
@@ -833,9 +978,9 @@ static int CG_CalcFov( void )
cg.refdef.fov_y = fov_y;
if( !cg.zoomed )
- cg.zoomSensitivity = 1;
+ cg.zoomSensitivity = 1.0f;
else
- cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
+ cg.zoomSensitivity = cg.refdef.fov_y / 75.0f;
return inwater;
}
@@ -941,10 +1086,7 @@ static void CG_smoothWWTransitions( playerState_t *ps, const vec3_t in, vec3_t o
}
//set surfNormal
- if( !( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) )
- VectorCopy( ps->grapplePoint, surfNormal );
- else
- VectorCopy( ceilingNormal, surfNormal );
+ BG_GetClientNormal( ps, surfNormal );
AnglesToAxis( in, inAxis );
@@ -952,7 +1094,6 @@ static void CG_smoothWWTransitions( playerState_t *ps, const vec3_t in, vec3_t o
if( !VectorCompare( surfNormal, cg.lastNormal ) )
{
//if we moving from the ceiling to the floor special case
- //( x product of colinear vectors is undefined)
if( VectorCompare( ceilingNormal, cg.lastNormal ) &&
VectorCompare( refNormal, surfNormal ) )
{
@@ -1091,7 +1232,8 @@ static int CG_CalcViewValues( void )
ps = &cg.predictedPlayerState;
// intermission view
- if( ps->pm_type == PM_INTERMISSION )
+ if( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_FREEZE ||
+ ps->pm_type == PM_SPECTATOR )
{
VectorCopy( ps->origin, cg.refdef.vieworg );
VectorCopy( ps->viewangles, cg.refdefViewAngles );
@@ -1105,18 +1247,22 @@ static int CG_CalcViewValues( void )
cg.xyspeed = sqrt( ps->velocity[ 0 ] * ps->velocity[ 0 ] +
ps->velocity[ 1 ] * ps->velocity[ 1 ] );
+ // the bob velocity should't get too fast to avoid jerking
+ if( cg.xyspeed > 300.0f )
+ cg.xyspeed = 300.0f;
+
VectorCopy( ps->origin, cg.refdef.vieworg );
- if( BG_ClassHasAbility( ps->stats[ STAT_PCLASS ], SCA_WALLCLIMBER ) )
+ if( BG_ClassHasAbility( ps->stats[ STAT_CLASS ], SCA_WALLCLIMBER ) )
CG_smoothWWTransitions( ps, ps->viewangles, cg.refdefViewAngles );
- else if( BG_ClassHasAbility( ps->stats[ STAT_PCLASS ], SCA_WALLJUMPER ) )
+ else if( BG_ClassHasAbility( ps->stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
CG_smoothWJTransitions( ps, ps->viewangles, cg.refdefViewAngles );
else
VectorCopy( ps->viewangles, cg.refdefViewAngles );
//clumsy logic, but it needs to be this way round because the CS propogation
//delay screws things up otherwise
- if( !BG_ClassHasAbility( ps->stats[ STAT_PCLASS ], SCA_WALLJUMPER ) )
+ if( !BG_ClassHasAbility( ps->stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
{
if( !( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING ) )
VectorSet( cg.lastNormal, 0.0f, 0.0f, 1.0f );
@@ -1143,6 +1289,8 @@ static int CG_CalcViewValues( void )
if( CG_IsParticleSystemValid( &cg.poisonCloudPS ) )
CG_DestroyParticleSystem( &cg.poisonCloudPS );
}
+ else
+ cg.wasDeadLastFrame = qfalse;
if( cg.renderingThirdPerson )
{
@@ -1155,7 +1303,7 @@ static int CG_CalcViewValues( void )
CG_OffsetFirstPersonView( );
}
- // position eye reletive to origin
+ // position eye relative to origin
AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
if( cg.hyperspace )
@@ -1260,7 +1408,11 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
CG_PredictPlayerState( );
// decide on third person view
- cg.renderingThirdPerson = cg_thirdPerson.integer || ( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 );
+ cg.renderingThirdPerson = ( cg_thirdPerson.integer || ( cg.snap->ps.stats[ STAT_HEALTH ] <= 0 ) ||
+ ( cg.chaseFollow && cg.snap->ps.pm_flags & PMF_FOLLOW) );
+
+ // update speedometer
+ CG_AddSpeed( );
// build cg.refdef
inwater = CG_CalcViewValues( );
@@ -1335,4 +1487,3 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo
if( cg_stats.integer )
CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
}
-