From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/cgame/cg_view.c | 563 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 357 insertions(+), 206 deletions(-) (limited to 'src/cgame/cg_view.c') 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 + =========================================================================== */ // 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 ); } - -- cgit