diff options
Diffstat (limited to 'game/client/portal/c_portal_player.cpp')
| -rw-r--r-- | game/client/portal/c_portal_player.cpp | 1653 |
1 files changed, 1653 insertions, 0 deletions
diff --git a/game/client/portal/c_portal_player.cpp b/game/client/portal/c_portal_player.cpp new file mode 100644 index 0000000..59ce85b --- /dev/null +++ b/game/client/portal/c_portal_player.cpp @@ -0,0 +1,1653 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for . +// +//===========================================================================// + +#include "cbase.h" +#include "vcollide_parse.h" +#include "c_portal_player.h" +#include "view.h" +#include "c_basetempentity.h" +#include "takedamageinfo.h" +#include "in_buttons.h" +#include "iviewrender_beams.h" +#include "r_efx.h" +#include "dlight.h" +#include "PortalRender.h" +#include "toolframework/itoolframework.h" +#include "toolframework_client.h" +#include "tier1/KeyValues.h" +#include "ScreenSpaceEffects.h" +#include "portal_shareddefs.h" +#include "ivieweffects.h" // for screenshake +#include "prop_portal_shared.h" + +// NVNT for fov updates +#include "haptics/ihaptics.h" + + +// Don't alias here +#if defined( CPortal_Player ) +#undef CPortal_Player +#endif + + +#define REORIENTATION_RATE 120.0f +#define REORIENTATION_ACCELERATION_RATE 400.0f + +#define ENABLE_PORTAL_EYE_INTERPOLATION_CODE + + +#define DEATH_CC_LOOKUP_FILENAME "materials/correction/cc_death.raw" +#define DEATH_CC_FADE_SPEED 0.05f + + +ConVar cl_reorient_in_air("cl_reorient_in_air", "1", FCVAR_ARCHIVE, "Allows the player to only reorient from being upside down while in the air." ); + + +// -------------------------------------------------------------------------------- // +// Player animation event. Sent to the client when a player fires, jumps, reloads, etc.. +// -------------------------------------------------------------------------------- // + +class C_TEPlayerAnimEvent : public C_BaseTempEntity +{ +public: + DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity ); + DECLARE_CLIENTCLASS(); + + virtual void PostDataUpdate( DataUpdateType_t updateType ) + { + // Create the effect. + C_Portal_Player *pPlayer = dynamic_cast< C_Portal_Player* >( m_hPlayer.Get() ); + if ( pPlayer && !pPlayer->IsDormant() ) + { + pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData ); + } + } + +public: + CNetworkHandle( CBasePlayer, m_hPlayer ); + CNetworkVar( int, m_iEvent ); + CNetworkVar( int, m_nData ); +}; + +IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent ); + +BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent ) +RecvPropEHandle( RECVINFO( m_hPlayer ) ), +RecvPropInt( RECVINFO( m_iEvent ) ), +RecvPropInt( RECVINFO( m_nData ) ) +END_RECV_TABLE() + + +//================================================================================= +// +// Ragdoll Entity +// +class C_PortalRagdoll : public C_BaseFlex +{ +public: + + DECLARE_CLASS( C_PortalRagdoll, C_BaseFlex ); + DECLARE_CLIENTCLASS(); + + C_PortalRagdoll(); + ~C_PortalRagdoll(); + + virtual void OnDataChanged( DataUpdateType_t type ); + + int GetPlayerEntIndex() const; + IRagdoll* GetIRagdoll() const; + + virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ); + +private: + + C_PortalRagdoll( const C_PortalRagdoll & ) {} + + void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity ); + void CreatePortalRagdoll(); + +private: + + EHANDLE m_hPlayer; + CNetworkVector( m_vecRagdollVelocity ); + CNetworkVector( m_vecRagdollOrigin ); + +}; + +IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_PortalRagdoll, DT_PortalRagdoll, CPortalRagdoll ) +RecvPropVector( RECVINFO(m_vecRagdollOrigin) ), +RecvPropEHandle( RECVINFO( m_hPlayer ) ), +RecvPropInt( RECVINFO( m_nModelIndex ) ), +RecvPropInt( RECVINFO(m_nForceBone) ), +RecvPropVector( RECVINFO(m_vecForce) ), +RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ), +END_RECV_TABLE() + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +C_PortalRagdoll::C_PortalRagdoll() +{ + m_hPlayer = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +C_PortalRagdoll::~C_PortalRagdoll() +{ + ( this ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pSourceEntity - +//----------------------------------------------------------------------------- +void C_PortalRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity ) +{ + if ( !pSourceEntity ) + return; + + VarMapping_t *pSrc = pSourceEntity->GetVarMapping(); + VarMapping_t *pDest = GetVarMapping(); + + // Find all the VarMapEntry_t's that represent the same variable. + for ( int i = 0; i < pDest->m_Entries.Count(); i++ ) + { + VarMapEntry_t *pDestEntry = &pDest->m_Entries[i]; + for ( int j=0; j < pSrc->m_Entries.Count(); j++ ) + { + VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j]; + if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(), pDestEntry->watcher->GetDebugName() ) ) + { + pDestEntry->watcher->Copy( pSrcEntry->watcher ); + break; + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Setup vertex weights for drawing +//----------------------------------------------------------------------------- +void C_PortalRagdoll::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ) +{ + // While we're dying, we want to mimic the facial animation of the player. Once they're dead, we just stay as we are. + if ( (m_hPlayer && m_hPlayer->IsAlive()) || !m_hPlayer ) + { + BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ); + } + else if ( m_hPlayer ) + { + m_hPlayer->SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void C_PortalRagdoll::CreatePortalRagdoll() +{ + // First, initialize all our data. If we have the player's entity on our client, + // then we can make ourselves start out exactly where the player is. + C_Portal_Player *pPlayer = dynamic_cast<C_Portal_Player*>( m_hPlayer.Get() ); + + if ( pPlayer && !pPlayer->IsDormant() ) + { + // Move my current model instance to the ragdoll's so decals are preserved. + pPlayer->SnatchModelInstance( this ); + + VarMapping_t *varMap = GetVarMapping(); + + // This is the local player, so set them in a default + // pose and slam their velocity, angles and origin + SetAbsOrigin( /* m_vecRagdollOrigin : */ pPlayer->GetRenderOrigin() ); + SetAbsAngles( pPlayer->GetRenderAngles() ); + SetAbsVelocity( m_vecRagdollVelocity ); + + // Hack! Find a neutral standing pose or use the idle. + int iSeq = LookupSequence( "ragdoll" ); + if ( iSeq == -1 ) + { + Assert( false ); + iSeq = 0; + } + SetSequence( iSeq ); + SetCycle( 0.0 ); + + Interp_Reset( varMap ); + + m_nBody = pPlayer->GetBody(); + SetModelIndex( m_nModelIndex ); + // Make us a ragdoll.. + m_nRenderFX = kRenderFxRagdoll; + + matrix3x4_t boneDelta0[MAXSTUDIOBONES]; + matrix3x4_t boneDelta1[MAXSTUDIOBONES]; + matrix3x4_t currentBones[MAXSTUDIOBONES]; + const float boneDt = 0.05f; + + pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt ); + + InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : IRagdoll* +//----------------------------------------------------------------------------- +IRagdoll* C_PortalRagdoll::GetIRagdoll() const +{ + return m_pRagdoll; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : type - +//----------------------------------------------------------------------------- +void C_PortalRagdoll::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + if ( type == DATA_UPDATE_CREATED ) + { + CreatePortalRagdoll(); + } +} + + +LINK_ENTITY_TO_CLASS( player, C_Portal_Player ); + +IMPLEMENT_CLIENTCLASS_DT(C_Portal_Player, DT_Portal_Player, CPortal_Player) +RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ), +RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ), +RecvPropEHandle( RECVINFO( m_hRagdoll ) ), +RecvPropInt( RECVINFO( m_iSpawnInterpCounter ) ), +RecvPropInt( RECVINFO( m_iPlayerSoundType ) ), +RecvPropBool( RECVINFO( m_bHeldObjectOnOppositeSideOfPortal ) ), +RecvPropEHandle( RECVINFO( m_pHeldObjectPortal ) ), +RecvPropBool( RECVINFO( m_bPitchReorientation ) ), +RecvPropEHandle( RECVINFO( m_hPortalEnvironment ) ), +RecvPropEHandle( RECVINFO( m_hSurroundingLiquidPortal ) ), +RecvPropBool( RECVINFO( m_bSuppressingCrosshair ) ), +END_RECV_TABLE() + + +BEGIN_PREDICTION_DATA( C_Portal_Player ) +END_PREDICTION_DATA() + +#define _WALK_SPEED 150 +#define _NORM_SPEED 190 +#define _SPRINT_SPEED 320 + +static ConVar cl_playermodel( "cl_playermodel", "none", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_SERVER_CAN_EXECUTE, "Default Player Model"); + +extern bool g_bUpsideDown; + +//EHANDLE g_eKillTarget1; +//EHANDLE g_eKillTarget2; + +void SpawnBlood (Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage); + +C_Portal_Player::C_Portal_Player() +: m_iv_angEyeAngles( "C_Portal_Player::m_iv_angEyeAngles" ) +{ + m_PlayerAnimState = CreatePortalPlayerAnimState( this ); + + m_iIDEntIndex = 0; + m_iSpawnInterpCounterCache = 0; + m_flDeathCCWeight = 0.0f; + + m_hRagdoll.Set( NULL ); + m_flStartLookTime = 0.0f; + + m_bHeldObjectOnOppositeSideOfPortal = false; + m_pHeldObjectPortal = 0; + + m_bPitchReorientation = false; + m_fReorientationRate = 0.0f; + + m_angEyeAngles.Init(); + + AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR ); + + m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK; + m_blinkTimer.Invalidate(); + + m_CCDeathHandle = INVALID_CLIENT_CCHANDLE; +} + +C_Portal_Player::~C_Portal_Player( void ) +{ + if ( m_PlayerAnimState ) + { + m_PlayerAnimState->Release(); + } + + g_pColorCorrectionMgr->RemoveColorCorrection( m_CCDeathHandle ); +} + +int C_Portal_Player::GetIDTarget() const +{ + return m_iIDEntIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: Update this client's target entity +//----------------------------------------------------------------------------- +void C_Portal_Player::UpdateIDTarget() +{ + if ( !IsLocalPlayer() ) + return; + + // Clear old target and find a new one + m_iIDEntIndex = 0; + + // don't show IDs in chase spec mode + if ( GetObserverMode() == OBS_MODE_CHASE || + GetObserverMode() == OBS_MODE_DEATHCAM ) + return; + + trace_t tr; + Vector vecStart, vecEnd; + VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd ); + VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart ); + UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + + if ( !tr.startsolid && tr.DidHitNonWorldEntity() ) + { + C_BaseEntity *pEntity = tr.m_pEnt; + + if ( pEntity && (pEntity != this) ) + { + m_iIDEntIndex = pEntity->entindex(); + } + } +} + +void C_Portal_Player::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr ) +{ + Vector vecOrigin = ptr->endpos - vecDir * 4; + + float flDistance = 0.0f; + + if ( info.GetAttacker() ) + { + flDistance = (ptr->endpos - info.GetAttacker()->GetAbsOrigin()).Length(); + } + + if ( m_takedamage ) + { + AddMultiDamage( info, this ); + + int blood = BloodColor(); + + if ( blood != DONT_BLEED ) + { + SpawnBlood( vecOrigin, vecDir, blood, flDistance );// a little surface blood. + TraceBleed( flDistance, vecDir, ptr, info.GetDamageType() ); + } + } +} + +void C_Portal_Player::Initialize( void ) +{ + m_headYawPoseParam = LookupPoseParameter( "head_yaw" ); + GetPoseParameterRange( m_headYawPoseParam, m_headYawMin, m_headYawMax ); + + m_headPitchPoseParam = LookupPoseParameter( "head_pitch" ); + GetPoseParameterRange( m_headPitchPoseParam, m_headPitchMin, m_headPitchMax ); + + CStudioHdr *hdr = GetModelPtr(); + for ( int i = 0; i < hdr->GetNumPoseParameters() ; i++ ) + { + SetPoseParameter( hdr, i, 0.0 ); + } +} + +CStudioHdr *C_Portal_Player::OnNewModel( void ) +{ + CStudioHdr *hdr = BaseClass::OnNewModel(); + + Initialize( ); + + return hdr; +} + +//----------------------------------------------------------------------------- +/** +* Orient head and eyes towards m_lookAt. +*/ +void C_Portal_Player::UpdateLookAt( void ) +{ + // head yaw + if (m_headYawPoseParam < 0 || m_headPitchPoseParam < 0) + return; + + // This is buggy with dt 0, just skip since there is no work to do. + if ( gpGlobals->frametime <= 0.0f ) + return; + + // Player looks at themselves through portals. Pick the portal we're turned towards. + const int iPortalCount = CProp_Portal_Shared::AllPortals.Count(); + CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base(); + float *fPortalDot = (float *)stackalloc( sizeof( float ) * iPortalCount ); + float flLowDot = 1.0f; + int iUsePortal = -1; + + // defaults if no portals are around + Vector vPlayerForward; + GetVectors( &vPlayerForward, NULL, NULL ); + Vector vCurLookTarget = EyePosition(); + + if ( !IsAlive() ) + { + m_viewtarget = EyePosition() + vPlayerForward*10.0f; + return; + } + + bool bNewTarget = false; + if ( UTIL_IntersectEntityExtentsWithPortal( this ) != NULL ) + { + // player is in a portal + vCurLookTarget = EyePosition() + vPlayerForward*10.0f; + } + else if ( pPortals && pPortals[0] ) + { + // Test through any active portals: This may be a shorter distance to the target + for( int i = 0; i != iPortalCount; ++i ) + { + CProp_Portal *pTempPortal = pPortals[i]; + + if( pTempPortal && pTempPortal->m_bActivated && pTempPortal->m_hLinkedPortal.Get() ) + { + Vector vEyeForward, vPortalForward; + EyeVectors( &vEyeForward ); + pTempPortal->GetVectors( &vPortalForward, NULL, NULL ); + fPortalDot[i] = vEyeForward.Dot( vPortalForward ); + if ( fPortalDot[i] < flLowDot ) + { + flLowDot = fPortalDot[i]; + iUsePortal = i; + } + } + } + + if ( iUsePortal >= 0 ) + { + C_Prop_Portal* pPortal = pPortals[iUsePortal]; + if ( pPortal ) + { + vCurLookTarget = pPortal->MatrixThisToLinked()*vCurLookTarget; + if ( vCurLookTarget != m_vLookAtTarget ) + { + bNewTarget = true; + } + } + } + } + else + { + // No other look targets, look straight ahead + vCurLookTarget += vPlayerForward*10.0f; + } + + // Figure out where we want to look in world space. + QAngle desiredAngles; + Vector to = vCurLookTarget - EyePosition(); + VectorAngles( to, desiredAngles ); + QAngle aheadAngles; + VectorAngles( vCurLookTarget, aheadAngles ); + + // Figure out where our body is facing in world space. + QAngle bodyAngles( 0, 0, 0 ); + bodyAngles[YAW] = GetLocalAngles()[YAW]; + + m_flLastBodyYaw = bodyAngles[YAW]; + + // Set the head's yaw. + float desiredYaw = AngleNormalize( desiredAngles[YAW] - bodyAngles[YAW] ); + desiredYaw = clamp( desiredYaw, m_headYawMin, m_headYawMax ); + + float desiredPitch = AngleNormalize( desiredAngles[PITCH] ); + desiredPitch = clamp( desiredPitch, m_headPitchMin, m_headPitchMax ); + + if ( bNewTarget ) + { + m_flStartLookTime = gpGlobals->curtime; + } + + float dt = (gpGlobals->frametime); + float flSpeed = 1.0f - ExponentialDecay( 0.7f, 0.033f, dt ); + + m_flCurrentHeadYaw = m_flCurrentHeadYaw + flSpeed * ( desiredYaw - m_flCurrentHeadYaw ); + m_flCurrentHeadYaw = AngleNormalize( m_flCurrentHeadYaw ); + SetPoseParameter( m_headYawPoseParam, m_flCurrentHeadYaw ); + + m_flCurrentHeadPitch = m_flCurrentHeadPitch + flSpeed * ( desiredPitch - m_flCurrentHeadPitch ); + m_flCurrentHeadPitch = AngleNormalize( m_flCurrentHeadPitch ); + SetPoseParameter( m_headPitchPoseParam, m_flCurrentHeadPitch ); + + // This orients the eyes + m_viewtarget = m_vLookAtTarget = vCurLookTarget; +} + +void C_Portal_Player::ClientThink( void ) +{ + //PortalEyeInterpolation.m_bNeedToUpdateEyePosition = true; + + Vector vForward; + AngleVectors( GetLocalAngles(), &vForward ); + + // Allow sprinting + HandleSpeedChanges(); + + FixTeleportationRoll(); + + //QAngle vAbsAngles = EyeAngles(); + + // Look at the thing that killed you + //if ( !IsAlive() ) + //{ + // C_BaseEntity *pEntity1 = g_eKillTarget1.Get(); + // C_BaseEntity *pEntity2 = g_eKillTarget2.Get(); + + // if ( pEntity2 && pEntity1 ) + // { + // //engine->GetViewAngles( vAbsAngles ); + + // Vector vLook = pEntity1->GetAbsOrigin() - pEntity2->GetAbsOrigin(); + // VectorNormalize( vLook ); + + // QAngle qLook; + // VectorAngles( vLook, qLook ); + + // if ( qLook[PITCH] > 180.0f ) + // { + // qLook[PITCH] -= 360.0f; + // } + + // if ( vAbsAngles[YAW] < 0.0f ) + // { + // vAbsAngles[YAW] += 360.0f; + // } + + // if ( vAbsAngles[PITCH] < qLook[PITCH] ) + // { + // vAbsAngles[PITCH] += gpGlobals->frametime * 120.0f; + // if ( vAbsAngles[PITCH] > qLook[PITCH] ) + // vAbsAngles[PITCH] = qLook[PITCH]; + // } + // else if ( vAbsAngles[PITCH] > qLook[PITCH] ) + // { + // vAbsAngles[PITCH] -= gpGlobals->frametime * 120.0f; + // if ( vAbsAngles[PITCH] < qLook[PITCH] ) + // vAbsAngles[PITCH] = qLook[PITCH]; + // } + + // if ( vAbsAngles[YAW] < qLook[YAW] ) + // { + // vAbsAngles[YAW] += gpGlobals->frametime * 240.0f; + // if ( vAbsAngles[YAW] > qLook[YAW] ) + // vAbsAngles[YAW] = qLook[YAW]; + // } + // else if ( vAbsAngles[YAW] > qLook[YAW] ) + // { + // vAbsAngles[YAW] -= gpGlobals->frametime * 240.0f; + // if ( vAbsAngles[YAW] < qLook[YAW] ) + // vAbsAngles[YAW] = qLook[YAW]; + // } + + // if ( vAbsAngles[YAW] > 180.0f ) + // { + // vAbsAngles[YAW] -= 360.0f; + // } + + // engine->SetViewAngles( vAbsAngles ); + // } + //} + + // If dead, fade in death CC lookup + if ( m_CCDeathHandle != INVALID_CLIENT_CCHANDLE ) + { + if ( m_lifeState != LIFE_ALIVE ) + { + if ( m_flDeathCCWeight < 1.0f ) + { + m_flDeathCCWeight += DEATH_CC_FADE_SPEED; + clamp( m_flDeathCCWeight, 0.0f, 1.0f ); + } + } + else + { + m_flDeathCCWeight = 0.0f; + } + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCDeathHandle, m_flDeathCCWeight ); + } + + UpdateIDTarget(); +} + +void C_Portal_Player::FixTeleportationRoll( void ) +{ + if( IsInAVehicle() ) //HL2 compatibility fix. do absolutely nothing to the view in vehicles + return; + + if( !IsLocalPlayer() ) + return; + + // Normalize roll from odd portal transitions + QAngle vAbsAngles = EyeAngles(); + + + Vector vCurrentForward, vCurrentRight, vCurrentUp; + AngleVectors( vAbsAngles, &vCurrentForward, &vCurrentRight, &vCurrentUp ); + + if ( vAbsAngles[ROLL] == 0.0f ) + { + m_fReorientationRate = 0.0f; + g_bUpsideDown = ( vCurrentUp.z < 0.0f ); + return; + } + + bool bForcePitchReorient = ( vAbsAngles[ROLL] > 175.0f && vCurrentForward.z > 0.99f ); + bool bOnGround = ( GetGroundEntity() != NULL ); + + if ( bForcePitchReorient ) + { + m_fReorientationRate = REORIENTATION_RATE * ( ( bOnGround ) ? ( 2.0f ) : ( 1.0f ) ); + } + else + { + // Don't reorient in air if they don't want to + if ( !cl_reorient_in_air.GetBool() && !bOnGround ) + { + g_bUpsideDown = ( vCurrentUp.z < 0.0f ); + return; + } + } + + if ( vCurrentUp.z < 0.75f ) + { + m_fReorientationRate += gpGlobals->frametime * REORIENTATION_ACCELERATION_RATE; + + // Upright faster if on the ground + float fMaxReorientationRate = REORIENTATION_RATE * ( ( bOnGround ) ? ( 2.0f ) : ( 1.0f ) ); + if ( m_fReorientationRate > fMaxReorientationRate ) + m_fReorientationRate = fMaxReorientationRate; + } + else + { + if ( m_fReorientationRate > REORIENTATION_RATE * 0.5f ) + { + m_fReorientationRate -= gpGlobals->frametime * REORIENTATION_ACCELERATION_RATE; + if ( m_fReorientationRate < REORIENTATION_RATE * 0.5f ) + m_fReorientationRate = REORIENTATION_RATE * 0.5f; + } + else if ( m_fReorientationRate < REORIENTATION_RATE * 0.5f ) + { + m_fReorientationRate += gpGlobals->frametime * REORIENTATION_ACCELERATION_RATE; + if ( m_fReorientationRate > REORIENTATION_RATE * 0.5f ) + m_fReorientationRate = REORIENTATION_RATE * 0.5f; + } + } + + if ( !m_bPitchReorientation && !bForcePitchReorient ) + { + // Randomize which way we roll if we're completely upside down + if ( vAbsAngles[ROLL] == 180.0f && RandomInt( 0, 1 ) == 1 ) + { + vAbsAngles[ROLL] = -180.0f; + } + + if ( vAbsAngles[ROLL] < 0.0f ) + { + vAbsAngles[ROLL] += gpGlobals->frametime * m_fReorientationRate; + if ( vAbsAngles[ROLL] > 0.0f ) + vAbsAngles[ROLL] = 0.0f; + engine->SetViewAngles( vAbsAngles ); + } + else if ( vAbsAngles[ROLL] > 0.0f ) + { + vAbsAngles[ROLL] -= gpGlobals->frametime * m_fReorientationRate; + if ( vAbsAngles[ROLL] < 0.0f ) + vAbsAngles[ROLL] = 0.0f; + engine->SetViewAngles( vAbsAngles ); + m_angEyeAngles = vAbsAngles; + m_iv_angEyeAngles.Reset(); + } + } + else + { + if ( vAbsAngles[ROLL] != 0.0f ) + { + if ( vCurrentUp.z < 0.2f ) + { + float fDegrees = gpGlobals->frametime * m_fReorientationRate; + if ( vCurrentForward.z > 0.0f ) + { + fDegrees = -fDegrees; + } + + // Rotate around the right axis + VMatrix mAxisAngleRot = SetupMatrixAxisRot( vCurrentRight, fDegrees ); + + vCurrentUp = mAxisAngleRot.VMul3x3( vCurrentUp ); + vCurrentForward = mAxisAngleRot.VMul3x3( vCurrentForward ); + + VectorAngles( vCurrentForward, vCurrentUp, vAbsAngles ); + + engine->SetViewAngles( vAbsAngles ); + m_angEyeAngles = vAbsAngles; + m_iv_angEyeAngles.Reset(); + } + else + { + if ( vAbsAngles[ROLL] < 0.0f ) + { + vAbsAngles[ROLL] += gpGlobals->frametime * m_fReorientationRate; + if ( vAbsAngles[ROLL] > 0.0f ) + vAbsAngles[ROLL] = 0.0f; + engine->SetViewAngles( vAbsAngles ); + m_angEyeAngles = vAbsAngles; + m_iv_angEyeAngles.Reset(); + } + else if ( vAbsAngles[ROLL] > 0.0f ) + { + vAbsAngles[ROLL] -= gpGlobals->frametime * m_fReorientationRate; + if ( vAbsAngles[ROLL] < 0.0f ) + vAbsAngles[ROLL] = 0.0f; + engine->SetViewAngles( vAbsAngles ); + m_angEyeAngles = vAbsAngles; + m_iv_angEyeAngles.Reset(); + } + } + } + } + + // Keep track of if we're upside down for look control + vAbsAngles = EyeAngles(); + AngleVectors( vAbsAngles, NULL, NULL, &vCurrentUp ); + + if ( bForcePitchReorient ) + g_bUpsideDown = ( vCurrentUp.z < 0.0f ); + else + g_bUpsideDown = false; +} + +const QAngle& C_Portal_Player::GetRenderAngles() +{ + if ( IsRagdoll() ) + { + return vec3_angle; + } + else + { + return m_PlayerAnimState->GetRenderAngles(); + } +} + +void C_Portal_Player::UpdateClientSideAnimation( void ) +{ + UpdateLookAt(); + + // Update the animation data. It does the local check here so this works when using + // a third-person camera (and we don't have valid player angles). + if ( this == C_Portal_Player::GetLocalPortalPlayer() ) + m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] ); + else + m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] ); + + BaseClass::UpdateClientSideAnimation(); +} + +void C_Portal_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) +{ + m_PlayerAnimState->DoAnimationEvent( event, nData ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int C_Portal_Player::DrawModel( int flags ) +{ + if ( !m_bReadyToDraw ) + return 0; + + if( IsLocalPlayer() ) + { + if ( !C_BasePlayer::ShouldDrawThisPlayer() ) + { + if ( !g_pPortalRender->IsRenderingPortal() ) + return 0; + + if( (g_pPortalRender->GetViewRecursionLevel() == 1) && (m_iForceNoDrawInPortalSurface != -1) ) //CPortalRender::s_iRenderingPortalView ) + return 0; + } + } + + return BaseClass::DrawModel(flags); +} + + + +//----------------------------------------------------------------------------- +// Should this object receive shadows? +//----------------------------------------------------------------------------- +bool C_Portal_Player::ShouldReceiveProjectedTextures( int flags ) +{ + Assert( flags & SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK ); + + if ( IsEffectActive( EF_NODRAW ) ) + return false; + + if( flags & SHADOW_FLAGS_FLASHLIGHT ) + { + return true; + } + + return BaseClass::ShouldReceiveProjectedTextures( flags ); +} + +void C_Portal_Player::DoImpactEffect( trace_t &tr, int nDamageType ) +{ + if ( GetActiveWeapon() ) + { + GetActiveWeapon()->DoImpactEffect( tr, nDamageType ); + return; + } + + BaseClass::DoImpactEffect( tr, nDamageType ); +} + +void C_Portal_Player::PreThink( void ) +{ + QAngle vTempAngles = GetLocalAngles(); + + if ( IsLocalPlayer() ) + { + vTempAngles[PITCH] = EyeAngles()[PITCH]; + } + else + { + vTempAngles[PITCH] = m_angEyeAngles[PITCH]; + } + + if ( vTempAngles[YAW] < 0.0f ) + { + vTempAngles[YAW] += 360.0f; + } + + SetLocalAngles( vTempAngles ); + + BaseClass::PreThink(); + + HandleSpeedChanges(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_Portal_Player::AddEntity( void ) +{ + BaseClass::AddEntity(); + + QAngle vTempAngles = GetLocalAngles(); + vTempAngles[PITCH] = m_angEyeAngles[PITCH]; + + SetLocalAngles( vTempAngles ); + + // Zero out model pitch, blending takes care of all of it. + SetLocalAnglesDim( X_INDEX, 0 ); + + if( this != C_BasePlayer::GetLocalPlayer() ) + { + if ( IsEffectActive( EF_DIMLIGHT ) ) + { + int iAttachment = LookupAttachment( "anim_attachment_RH" ); + + if ( iAttachment < 0 ) + return; + + Vector vecOrigin; + QAngle eyeAngles = m_angEyeAngles; + + GetAttachment( iAttachment, vecOrigin, eyeAngles ); + + Vector vForward; + AngleVectors( eyeAngles, &vForward ); + + trace_t tr; + UTIL_TraceLine( vecOrigin, vecOrigin + (vForward * 200), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + } + } +} + +ShadowType_t C_Portal_Player::ShadowCastType( void ) +{ + // Drawing player shadows looks bad in first person when they get close to walls + // It doesn't make sense to have shadows in the portal view, but not in the main view + // So no shadows for the player + return SHADOWS_NONE; +} + +bool C_Portal_Player::ShouldDraw( void ) +{ + if ( !IsAlive() ) + return false; + + //return true; + + // if( GetTeamNumber() == TEAM_SPECTATOR ) + // return false; + + if( IsLocalPlayer() && IsRagdoll() ) + return true; + + if ( IsRagdoll() ) + return false; + + return true; + + return BaseClass::ShouldDraw(); +} + +const QAngle& C_Portal_Player::EyeAngles() +{ + if ( IsLocalPlayer() && g_nKillCamMode == OBS_MODE_NONE ) + { + return BaseClass::EyeAngles(); + } + else + { + //C_BaseEntity *pEntity1 = g_eKillTarget1.Get(); + //C_BaseEntity *pEntity2 = g_eKillTarget2.Get(); + + //Vector vLook = Vector( 0.0f, 0.0f, 0.0f ); + + //if ( pEntity2 ) + //{ + // vLook = pEntity1->GetAbsOrigin() - pEntity2->GetAbsOrigin(); + // VectorNormalize( vLook ); + //} + //else if ( pEntity1 ) + //{ + // return BaseClass::EyeAngles(); + // //vLook = - pEntity1->GetAbsOrigin(); + //} + + //if ( vLook != Vector( 0.0f, 0.0f, 0.0f ) ) + //{ + // VectorAngles( vLook, m_angEyeAngles ); + //} + + return m_angEyeAngles; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : IRagdoll* +//----------------------------------------------------------------------------- +IRagdoll* C_Portal_Player::GetRepresentativeRagdoll() const +{ + if ( m_hRagdoll.Get() ) + { + C_PortalRagdoll *pRagdoll = static_cast<C_PortalRagdoll*>( m_hRagdoll.Get() ); + if ( !pRagdoll ) + return NULL; + + return pRagdoll->GetIRagdoll(); + } + else + { + return NULL; + } +} + + +void C_Portal_Player::PlayerPortalled( C_Prop_Portal *pEnteredPortal ) +{ + if( pEnteredPortal ) + { + m_bPortalledMessagePending = true; + m_PendingPortalMatrix = pEnteredPortal->MatrixThisToLinked(); + + if( IsLocalPlayer() ) + g_pPortalRender->EnteredPortal( pEnteredPortal ); + } +} + +void C_Portal_Player::OnPreDataChanged( DataUpdateType_t type ) +{ + Assert( m_pPortalEnvironment_LastCalcView == m_hPortalEnvironment.Get() ); + PreDataChanged_Backup.m_hPortalEnvironment = m_hPortalEnvironment; + PreDataChanged_Backup.m_hSurroundingLiquidPortal = m_hSurroundingLiquidPortal; + PreDataChanged_Backup.m_qEyeAngles = m_iv_angEyeAngles.GetCurrent(); + + BaseClass::OnPreDataChanged( type ); +} + +void C_Portal_Player::OnDataChanged( DataUpdateType_t type ) +{ + BaseClass::OnDataChanged( type ); + + if( m_hSurroundingLiquidPortal != PreDataChanged_Backup.m_hSurroundingLiquidPortal ) + { + CLiquidPortal_InnerLiquidEffect *pLiquidEffect = (CLiquidPortal_InnerLiquidEffect *)g_pScreenSpaceEffects->GetScreenSpaceEffect( "LiquidPortal_InnerLiquid" ); + if( pLiquidEffect ) + { + C_Func_LiquidPortal *pSurroundingPortal = m_hSurroundingLiquidPortal.Get(); + if( pSurroundingPortal != NULL ) + { + C_Func_LiquidPortal *pOldSurroundingPortal = PreDataChanged_Backup.m_hSurroundingLiquidPortal.Get(); + if( pOldSurroundingPortal != pSurroundingPortal->m_hLinkedPortal.Get() ) + { + pLiquidEffect->m_pImmersionPortal = pSurroundingPortal; + pLiquidEffect->m_bFadeBackToReality = false; + } + else + { + pLiquidEffect->m_bFadeBackToReality = true; + pLiquidEffect->m_fFadeBackTimeLeft = pLiquidEffect->s_fFadeBackEffectTime; + } + } + else + { + pLiquidEffect->m_pImmersionPortal = NULL; + pLiquidEffect->m_bFadeBackToReality = false; + } + } + } + + DetectAndHandlePortalTeleportation(); + + if ( type == DATA_UPDATE_CREATED ) + { + // Load color correction lookup for the death effect + m_CCDeathHandle = g_pColorCorrectionMgr->AddColorCorrection( DEATH_CC_LOOKUP_FILENAME ); + if ( m_CCDeathHandle != INVALID_CLIENT_CCHANDLE ) + { + g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCDeathHandle, 0.0f ); + } + + SetNextClientThink( CLIENT_THINK_ALWAYS ); + } + + UpdateVisibility(); +} + +//CalcView() gets called between OnPreDataChanged() and OnDataChanged(), and these changes need to be known about in both before CalcView() gets called, and if CalcView() doesn't get called +bool C_Portal_Player::DetectAndHandlePortalTeleportation( void ) +{ + if( m_bPortalledMessagePending ) + { + m_bPortalledMessagePending = false; + + //C_Prop_Portal *pOldPortal = PreDataChanged_Backup.m_hPortalEnvironment.Get(); + //Assert( pOldPortal ); + //if( pOldPortal ) + { + Vector ptNewPosition = GetNetworkOrigin(); + + UTIL_Portal_PointTransform( m_PendingPortalMatrix, PortalEyeInterpolation.m_vEyePosition_Interpolated, PortalEyeInterpolation.m_vEyePosition_Interpolated ); + UTIL_Portal_PointTransform( m_PendingPortalMatrix, PortalEyeInterpolation.m_vEyePosition_Uninterpolated, PortalEyeInterpolation.m_vEyePosition_Uninterpolated ); + + PortalEyeInterpolation.m_bEyePositionIsInterpolating = true; + + UTIL_Portal_AngleTransform( m_PendingPortalMatrix, m_qEyeAngles_LastCalcView, m_angEyeAngles ); + m_angEyeAngles.x = AngleNormalize( m_angEyeAngles.x ); + m_angEyeAngles.y = AngleNormalize( m_angEyeAngles.y ); + m_angEyeAngles.z = AngleNormalize( m_angEyeAngles.z ); + m_iv_angEyeAngles.Reset(); //copies from m_angEyeAngles + + if( engine->IsPlayingDemo() ) + { + pl.v_angle = m_angEyeAngles; + engine->SetViewAngles( pl.v_angle ); + } + + engine->ResetDemoInterpolation(); + if( IsLocalPlayer() ) + { + //DevMsg( "FPT: %.2f %.2f %.2f\n", m_angEyeAngles.x, m_angEyeAngles.y, m_angEyeAngles.z ); + SetLocalAngles( m_angEyeAngles ); + } + + m_PlayerAnimState->Teleport ( &ptNewPosition, &GetNetworkAngles(), this ); + + // Reorient last facing direction to fix pops in view model lag + for ( int i = 0; i < MAX_VIEWMODELS; i++ ) + { + CBaseViewModel *vm = GetViewModel( i ); + if ( !vm ) + continue; + + UTIL_Portal_VectorTransform( m_PendingPortalMatrix, vm->m_vecLastFacing, vm->m_vecLastFacing ); + } + } + m_bPortalledMessagePending = false; + } + + return false; +} + +/*bool C_Portal_Player::ShouldInterpolate( void ) +{ +if( !IsInterpolationEnabled() ) +return false; + +return BaseClass::ShouldInterpolate(); +}*/ + + +void C_Portal_Player::PostDataUpdate( DataUpdateType_t updateType ) +{ + // C_BaseEntity assumes we're networking the entity's angles, so pretend that it + // networked the same value we already have. + SetNetworkAngles( GetLocalAngles() ); + + if ( m_iSpawnInterpCounter != m_iSpawnInterpCounterCache ) + { + MoveToLastReceivedPosition( true ); + ResetLatched(); + m_iSpawnInterpCounterCache = m_iSpawnInterpCounter; + } + + BaseClass::PostDataUpdate( updateType ); +} + +float C_Portal_Player::GetFOV( void ) +{ + //Find our FOV with offset zoom value + float flFOVOffset = C_BasePlayer::GetFOV() + GetZoom(); + + // Clamp FOV in MP + int min_fov = GetMinFOV(); + + // Don't let it go too low + flFOVOffset = MAX( min_fov, flFOVOffset ); + + return flFOVOffset; +} + +//========================================================= +// Autoaim +// set crosshair position to point to enemey +//========================================================= +Vector C_Portal_Player::GetAutoaimVector( float flDelta ) +{ + // Never autoaim a predicted weapon (for now) + Vector forward; + AngleVectors( EyeAngles() + m_Local.m_vecPunchAngle, &forward ); + return forward; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns whether or not we are allowed to sprint now. +//----------------------------------------------------------------------------- +bool C_Portal_Player::CanSprint( void ) +{ + return ( (!m_Local.m_bDucked && !m_Local.m_bDucking) && (GetWaterLevel() != 3) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void C_Portal_Player::StartSprinting( void ) +{ + //if( m_HL2Local.m_flSuitPower < 10 ) + //{ + // // Don't sprint unless there's a reasonable + // // amount of suit power. + // CPASAttenuationFilter filter( this ); + // filter.UsePredictionRules(); + // EmitSound( filter, entindex(), "Player.SprintNoPower" ); + // return; + //} + + CPASAttenuationFilter filter( this ); + filter.UsePredictionRules(); + EmitSound( filter, entindex(), "Player.SprintStart" ); + + SetMaxSpeed( _SPRINT_SPEED ); + m_fIsSprinting = true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void C_Portal_Player::StopSprinting( void ) +{ + SetMaxSpeed( _NORM_SPEED ); + m_fIsSprinting = false; +} + +void C_Portal_Player::HandleSpeedChanges( void ) +{ + int buttonsChanged = m_afButtonPressed | m_afButtonReleased; + + if( buttonsChanged & IN_SPEED ) + { + if ( !(m_afButtonPressed & IN_SPEED) && IsSprinting() ) + { + StopSprinting(); + } + else if ( (m_afButtonPressed & IN_SPEED) && !IsSprinting() ) + { + if ( CanSprint() ) + { + StartSprinting(); + } + else + { + // Reset key, so it will be activated post whatever is suppressing it. + m_nButtons &= ~IN_SPEED; + } + } + } +} + +void C_Portal_Player::ItemPreFrame( void ) +{ + if ( GetFlags() & FL_FROZEN ) + return; + + // Disallow shooting while zooming + if ( m_nButtons & IN_ZOOM ) + { + //FIXME: Held weapons like the grenade get sad when this happens + m_nButtons &= ~(IN_ATTACK|IN_ATTACK2); + } + + BaseClass::ItemPreFrame(); + +} + +void C_Portal_Player::ItemPostFrame( void ) +{ + if ( GetFlags() & FL_FROZEN ) + return; + + BaseClass::ItemPostFrame(); +} + +C_BaseAnimating *C_Portal_Player::BecomeRagdollOnClient() +{ + // Let the C_CSRagdoll entity do this. + // m_builtRagdoll = true; + return NULL; +} + +void C_Portal_Player::UpdatePortalEyeInterpolation( void ) +{ +#ifdef ENABLE_PORTAL_EYE_INTERPOLATION_CODE + //PortalEyeInterpolation.m_bEyePositionIsInterpolating = false; + if( PortalEyeInterpolation.m_bUpdatePosition_FreeMove ) + { + PortalEyeInterpolation.m_bUpdatePosition_FreeMove = false; + + C_Prop_Portal *pOldPortal = PreDataChanged_Backup.m_hPortalEnvironment.Get(); + if( pOldPortal ) + { + UTIL_Portal_PointTransform( pOldPortal->MatrixThisToLinked(), PortalEyeInterpolation.m_vEyePosition_Interpolated, PortalEyeInterpolation.m_vEyePosition_Interpolated ); + //PortalEyeInterpolation.m_vEyePosition_Interpolated = pOldPortal->m_matrixThisToLinked * PortalEyeInterpolation.m_vEyePosition_Interpolated; + + //Vector vForward; + //m_hPortalEnvironment.Get()->GetVectors( &vForward, NULL, NULL ); + + PortalEyeInterpolation.m_vEyePosition_Interpolated = EyeFootPosition(); + + PortalEyeInterpolation.m_bEyePositionIsInterpolating = true; + } + } + + if( IsInAVehicle() ) + PortalEyeInterpolation.m_bEyePositionIsInterpolating = false; + + if( !PortalEyeInterpolation.m_bEyePositionIsInterpolating ) + { + PortalEyeInterpolation.m_vEyePosition_Uninterpolated = EyeFootPosition(); + PortalEyeInterpolation.m_vEyePosition_Interpolated = PortalEyeInterpolation.m_vEyePosition_Uninterpolated; + return; + } + + Vector vThisFrameUninterpolatedPosition = EyeFootPosition(); + + //find offset between this and last frame's uninterpolated movement, and apply this as freebie movement to the interpolated position + PortalEyeInterpolation.m_vEyePosition_Interpolated += (vThisFrameUninterpolatedPosition - PortalEyeInterpolation.m_vEyePosition_Uninterpolated); + PortalEyeInterpolation.m_vEyePosition_Uninterpolated = vThisFrameUninterpolatedPosition; + + Vector vDiff = vThisFrameUninterpolatedPosition - PortalEyeInterpolation.m_vEyePosition_Interpolated; + float fLength = vDiff.Length(); + float fFollowSpeed = gpGlobals->frametime * 100.0f; + const float fMaxDiff = 150.0f; + if( fLength > fMaxDiff ) + { + //camera lagging too far behind, give it a speed boost to bring it within maximum range + fFollowSpeed = fLength - fMaxDiff; + } + else if( fLength < fFollowSpeed ) + { + //final move + PortalEyeInterpolation.m_bEyePositionIsInterpolating = false; + PortalEyeInterpolation.m_vEyePosition_Interpolated = vThisFrameUninterpolatedPosition; + return; + } + + if ( fLength > 0.001f ) + { + vDiff *= (fFollowSpeed/fLength); + PortalEyeInterpolation.m_vEyePosition_Interpolated += vDiff; + } + else + { + PortalEyeInterpolation.m_vEyePosition_Interpolated = vThisFrameUninterpolatedPosition; + } + + + +#else + PortalEyeInterpolation.m_vEyePosition_Interpolated = BaseClass::EyePosition(); +#endif +} + +Vector C_Portal_Player::EyePosition() +{ + return PortalEyeInterpolation.m_vEyePosition_Interpolated; +} + +Vector C_Portal_Player::EyeFootPosition( const QAngle &qEyeAngles ) +{ +#if 0 + static int iPrintCounter = 0; + ++iPrintCounter; + if( iPrintCounter == 50 ) + { + QAngle vAbsAngles = qEyeAngles; + DevMsg( "Eye Angles: %f %f %f\n", vAbsAngles.x, vAbsAngles.y, vAbsAngles.z ); + iPrintCounter = 0; + } +#endif + + //interpolate between feet and normal eye position based on view roll (gets us wall/ceiling & ceiling/ceiling teleportations without an eye position pop) + float fFootInterp = fabs(qEyeAngles[ROLL]) * ((1.0f/180.0f) * 0.75f); //0 when facing straight up, 0.75 when facing straight down + return (BaseClass::EyePosition() - (fFootInterp * m_vecViewOffset)); //TODO: Find a good Up vector for this rolled player and interpolate along actual eye/foot axis +} + +void C_Portal_Player::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov ) +{ + DetectAndHandlePortalTeleportation(); + //if( DetectAndHandlePortalTeleportation() ) + // DevMsg( "Teleported within OnDataChanged\n" ); + + m_iForceNoDrawInPortalSurface = -1; + bool bEyeTransform_Backup = m_bEyePositionIsTransformedByPortal; + m_bEyePositionIsTransformedByPortal = false; //assume it's not transformed until it provably is + UpdatePortalEyeInterpolation(); + + QAngle qEyeAngleBackup = EyeAngles(); + Vector ptEyePositionBackup = EyePosition(); + C_Prop_Portal *pPortalBackup = m_hPortalEnvironment.Get(); + + if ( m_lifeState != LIFE_ALIVE ) + { + if ( g_nKillCamMode != 0 ) + { + return; + } + + Vector origin = EyePosition(); + + C_BaseEntity* pRagdoll = m_hRagdoll.Get(); + + if ( pRagdoll ) + { + origin = pRagdoll->GetAbsOrigin(); +#if !PORTAL_HIDE_PLAYER_RAGDOLL + origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z; // look over ragdoll, not through +#endif //PORTAL_HIDE_PLAYER_RAGDOLL + } + + BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov ); + + eyeOrigin = origin; + + Vector vForward; + AngleVectors( eyeAngles, &vForward ); + + VectorNormalize( vForward ); +#if !PORTAL_HIDE_PLAYER_RAGDOLL + VectorMA( origin, -CHASE_CAM_DISTANCE_MAX, vForward, eyeOrigin ); +#endif //PORTAL_HIDE_PLAYER_RAGDOLL + + Vector WALL_MIN( -WALL_OFFSET, -WALL_OFFSET, -WALL_OFFSET ); + Vector WALL_MAX( WALL_OFFSET, WALL_OFFSET, WALL_OFFSET ); + + trace_t trace; // clip against world + C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace + UTIL_TraceHull( origin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trace ); + C_BaseEntity::PopEnableAbsRecomputations(); + + if (trace.fraction < 1.0) + { + eyeOrigin = trace.endpos; + } + } + else + { + IClientVehicle *pVehicle; + pVehicle = GetVehicle(); + + if ( !pVehicle ) + { + if ( IsObserver() ) + { + CalcObserverView( eyeOrigin, eyeAngles, fov ); + } + else + { + CalcPlayerView( eyeOrigin, eyeAngles, fov ); + if( m_hPortalEnvironment.Get() != NULL ) + { + //time for hax + m_bEyePositionIsTransformedByPortal = bEyeTransform_Backup; + CalcPortalView( eyeOrigin, eyeAngles ); + } + } + } + else + { + CalcVehicleView( pVehicle, eyeOrigin, eyeAngles, zNear, zFar, fov ); + } + } + + m_qEyeAngles_LastCalcView = qEyeAngleBackup; + m_ptEyePosition_LastCalcView = ptEyePositionBackup; + m_pPortalEnvironment_LastCalcView = pPortalBackup; + +#ifdef WIN32 + // NVNT Inform haptics module of fov + if(IsLocalPlayer()) + haptics->UpdatePlayerFOV(fov); +#endif +} + +void C_Portal_Player::SetLocalViewAngles( const QAngle &viewAngles ) +{ + // Nothing + if ( engine->IsPlayingDemo() ) + return; + BaseClass::SetLocalViewAngles( viewAngles ); +} + +void C_Portal_Player::SetViewAngles( const QAngle& ang ) +{ + BaseClass::SetViewAngles( ang ); + + if ( engine->IsPlayingDemo() ) + { + pl.v_angle = ang; + } +} + +void C_Portal_Player::CalcPortalView( Vector &eyeOrigin, QAngle &eyeAngles ) +{ + //although we already ran CalcPlayerView which already did these copies, they also fudge these numbers in ways we don't like, so recopy + VectorCopy( EyePosition(), eyeOrigin ); + VectorCopy( EyeAngles(), eyeAngles ); + + //Re-apply the screenshake (we just stomped it) + vieweffects->ApplyShake( eyeOrigin, eyeAngles, 1.0 ); + + C_Prop_Portal *pPortal = m_hPortalEnvironment.Get(); + assert( pPortal ); + + C_Prop_Portal *pRemotePortal = pPortal->m_hLinkedPortal; + if( !pRemotePortal ) + { + return; //no hacks possible/necessary + } + + Vector ptPortalCenter; + Vector vPortalForward; + + ptPortalCenter = pPortal->GetNetworkOrigin(); + pPortal->GetVectors( &vPortalForward, NULL, NULL ); + float fPortalPlaneDist = vPortalForward.Dot( ptPortalCenter ); + + bool bOverrideSpecialEffects = false; //sometimes to get the best effect we need to kill other effects that are simply for cleanliness + + float fEyeDist = vPortalForward.Dot( eyeOrigin ) - fPortalPlaneDist; + bool bTransformEye = false; + if( fEyeDist < 0.0f ) //eye behind portal + { + if( pPortal->m_PortalSimulator.EntityIsInPortalHole( this ) ) //player standing in portal + { + bTransformEye = true; + } + else if( vPortalForward.z < -0.01f ) //there's a weird case where the player is ducking below a ceiling portal. As they unduck their eye moves beyond the portal before the code detects that they're in the portal hole. + { + Vector ptPlayerOrigin = GetAbsOrigin(); + float fOriginDist = vPortalForward.Dot( ptPlayerOrigin ) - fPortalPlaneDist; + + if( fOriginDist > 0.0f ) + { + float fInvTotalDist = 1.0f / (fOriginDist - fEyeDist); //fEyeDist is negative + Vector ptPlaneIntersection = (eyeOrigin * fOriginDist * fInvTotalDist) - (ptPlayerOrigin * fEyeDist * fInvTotalDist); + Assert( fabs( vPortalForward.Dot( ptPlaneIntersection ) - fPortalPlaneDist ) < 0.01f ); + + Vector vIntersectionTest = ptPlaneIntersection - ptPortalCenter; + + Vector vPortalRight, vPortalUp; + pPortal->GetVectors( NULL, &vPortalRight, &vPortalUp ); + + if( (vIntersectionTest.Dot( vPortalRight ) <= PORTAL_HALF_WIDTH) && + (vIntersectionTest.Dot( vPortalUp ) <= PORTAL_HALF_HEIGHT) ) + { + bTransformEye = true; + } + } + } + } + + if( bTransformEye ) + { + m_bEyePositionIsTransformedByPortal = true; + + //DevMsg( 2, "transforming portal view from <%f %f %f> <%f %f %f>\n", eyeOrigin.x, eyeOrigin.y, eyeOrigin.z, eyeAngles.x, eyeAngles.y, eyeAngles.z ); + + VMatrix matThisToLinked = pPortal->MatrixThisToLinked(); + UTIL_Portal_PointTransform( matThisToLinked, eyeOrigin, eyeOrigin ); + UTIL_Portal_AngleTransform( matThisToLinked, eyeAngles, eyeAngles ); + + //DevMsg( 2, "transforming portal view to <%f %f %f> <%f %f %f>\n", eyeOrigin.x, eyeOrigin.y, eyeOrigin.z, eyeAngles.x, eyeAngles.y, eyeAngles.z ); + + if ( IsToolRecording() ) + { + static EntityTeleportedRecordingState_t state; + + KeyValues *msg = new KeyValues( "entity_teleported" ); + msg->SetPtr( "state", &state ); + state.m_bTeleported = false; + state.m_bViewOverride = true; + state.m_vecTo = eyeOrigin; + state.m_qaTo = eyeAngles; + MatrixInvert( matThisToLinked.As3x4(), state.m_teleportMatrix ); + + // Post a message back to all IToolSystems + Assert( (int)GetToolHandle() != 0 ); + ToolFramework_PostToolMessage( GetToolHandle(), msg ); + + msg->deleteThis(); + } + + bOverrideSpecialEffects = true; + } + else + { + m_bEyePositionIsTransformedByPortal = false; + } + + if( bOverrideSpecialEffects ) + { + m_iForceNoDrawInPortalSurface = ((pRemotePortal->m_bIsPortal2)?(2):(1)); + pRemotePortal->m_fStaticAmount = 0.0f; + } +} + +extern float g_fMaxViewModelLag; +void C_Portal_Player::CalcViewModelView( const Vector& eyeOrigin, const QAngle& eyeAngles) +{ + // HACK: Manually adjusting the eye position that view model looking up and down are similar + // (solves view model "pop" on floor to floor transitions) + Vector vInterpEyeOrigin = eyeOrigin; + + Vector vForward; + Vector vRight; + Vector vUp; + AngleVectors( eyeAngles, &vForward, &vRight, &vUp ); + + if ( vForward.z < 0.0f ) + { + float fT = vForward.z * vForward.z; + vInterpEyeOrigin += vRight * ( fT * 4.7f ) + vForward * ( fT * 5.0f ) + vUp * ( fT * 4.0f ); + } + + if ( UTIL_IntersectEntityExtentsWithPortal( this ) ) + g_fMaxViewModelLag = 0.0f; + else + g_fMaxViewModelLag = 1.5f; + + for ( int i = 0; i < MAX_VIEWMODELS; i++ ) + { + CBaseViewModel *vm = GetViewModel( i ); + if ( !vm ) + continue; + + vm->CalcViewModelView( this, vInterpEyeOrigin, eyeAngles ); + } +} + +bool LocalPlayerIsCloseToPortal( void ) +{ + return C_Portal_Player::GetLocalPlayer()->IsCloseToPortal(); +} + |