cstrike15_src/game/client/portal/c_portal_player.cpp
2025-06-04 03:22:50 +02:00

4213 lines
131 KiB
C++

//===== Copyright (c) 1996-2005, 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 "portal_base2d_shared.h"
#include "input.h"
#include "prediction.h"
#include "choreoevent.h"
#include "model_types.h"
#include "materialsystem/imaterialvar.h"
#include "portal_mp_gamerules.h"
#include "collisionutils.h"
#include "engine/ivdebugoverlay.h"
#include "c_physicsprop.h"
#include "portal2/portal_grabcontroller_shared.h"
#include "igameevents.h"
#include "inetchannelinfo.h"
#include "igamemovement.h"
#include "c_weapon_paintgun.h"
#include "c_weapon_portalgun.h"
#include "cam_thirdperson.h"
#include "vgui_int.h"
#include "dt_utlvector_recv.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// Don't alias here
#if defined( CPortal_Player )
#undef CPortal_Player
#endif
ConVar cl_portal_camera_orientation_max_speed("cl_portal_camera_orientation_max_speed", "375.0f" );
ConVar cl_portal_camera_orientation_rate("cl_portal_camera_orientation_rate", "480.0f" );
ConVar cl_portal_camera_orientation_rate_base("cl_portal_camera_orientation_rate_base", "45.0f" );
ConVar cl_portal_camera_orientation_acceleration_rate("cl_portal_camera_orientation_acceleration_rate", "1000.0f" );
ConVar cl_skip_player_render_in_main_view("cl_skip_player_render_in_main_view", "1", FCVAR_ARCHIVE );
ConVar cl_auto_taunt_pip( "cl_auto_taunt_pip", "1", FCVAR_ARCHIVE );
ConVar sv_zoom_stop_movement_threashold("sv_zoom_stop_movement_threashold", "4.0", FCVAR_REPLICATED, "Move command amount before breaking player out of toggle zoom." );
ConVar sv_zoom_stop_time_threashold("sv_zoom_stop_time_threashold", "5.0", FCVAR_REPLICATED, "Time amount before breaking player out of toggle zoom." );
ConVar cl_taunt_finish_rotate_cam("cl_taunt_finish_rotate_cam", "1", FCVAR_CHEAT);
ConVar cl_taunt_finish_speed("cl_taunt_finish_speed", "0.8f", FCVAR_CHEAT);
ConVar mp_taunt_position_blend_rate( "mp_taunt_position_blend_rate", "4.0", FCVAR_CHEAT );
ConVar mp_bot_fling_trail( "mp_bot_fling_trail", "0", FCVAR_CHEAT, "When bots reach a certain velocity in the air, they will show a trail behind them (0 = off, 1 = on, 2 = fun)" );
ConVar mp_bot_fling_trail_kill_scaler( "mp_bot_fling_trail_kill_scaler", "0.01", FCVAR_CHEAT, "The scaler that determines how close to a portal a player has to be (when flinging towards it) before the trail turns off" );
ConVar player_held_object_keep_out_of_camera("player_held_object_keep_out_of_camera", "1", FCVAR_CHEAT );
ConVar mp_auto_taunt( "mp_auto_taunt", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
ConVar mp_auto_accept_team_taunt( "mp_auto_accept_team_taunt", "1", FCVAR_ARCHIVE );
ConVar cl_fov( "cl_fov", "90", FCVAR_NONE, "Client-side fov control that is global for all splitscreen players on this machine. This gets overriden via splitscreen_config.txt for splitscreen." );
extern ConVar sv_debug_player_use;
extern ConVar mp_should_gib_bots;
extern ConVar player_held_object_use_view_model;
extern ConVar sv_use_trace_duration;
extern void RecieveEntityPortalledMessage( CHandle<C_BaseEntity> hEntity, CHandle<C_Portal_Base2D> hPortal, float fTime, bool bForcedDuck );
extern ConVar player_held_object_collide_with_player;
extern ConVar ss_force_primary_fullscreen;
extern ConVar sv_player_funnel_into_portals;
extern ConVar sv_player_funnel_gimme_dot;
//#define ENABLE_PORTAL_EYE_INTERPOLATION_CODE
extern ConVar sv_gravity;
// -------------------------------------------------------------------------------- //
// 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();
bool ShouldDoAnimationEvent( C_Portal_Player *pPlayer, PlayerAnimEvent_t event )
{
if ( !pPlayer->GetPredictable() )
{
return true;
}
if ( IsCustomPlayerAnimEvent( event ) )
{
return false;
}
if ( event == PLAYERANIMEVENT_JUMP || event == PLAYERANIMEVENT_ATTACK_PRIMARY || event == PLAYERANIMEVENT_ATTACK_SECONDARY )
{
return false;
}
return true;
}
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() )
{
PlayerAnimEvent_t event = (PlayerAnimEvent_t)m_iEvent.Get();
if ( ShouldDoAnimationEvent( pPlayer, event ) )
{
pPlayer->DoAnimationEvent( event, 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();
CopySequenceTransitions(pPlayer);
SetModelIndex( m_nModelIndex );
// Make us a ragdoll..
m_bClientSideRagdoll = true;
matrix3x4a_t boneDelta0[MAXSTUDIOBONES];
matrix3x4a_t boneDelta1[MAXSTUDIOBONES];
matrix3x4a_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();
}
}
BEGIN_RECV_TABLE_NOBASE( C_EntityPortalledNetworkMessage, DT_EntityPortalledNetworkMessage )
RecvPropEHandle( RECVINFO_NAME( m_hEntity, m_hEntity ) ),
RecvPropEHandle( RECVINFO_NAME( m_hPortal, m_hPortal ) ),
RecvPropFloat( RECVINFO_NAME( m_fTime, m_fTime ) ),
RecvPropBool( RECVINFO_NAME( m_bForcedDuck, m_bForcedDuck ) ),
RecvPropInt( RECVINFO_NAME( m_iMessageCount, m_iMessageCount ) ),
END_RECV_TABLE()
// specific to the local player
BEGIN_RECV_TABLE_NOBASE( C_Portal_Player, DT_PortalLocalPlayerExclusive )
RecvPropVectorXY( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ), 0, C_BasePlayer::RecvProxy_LocalOriginXY ),
RecvPropFloat( RECVINFO_NAME( m_vecNetworkOrigin[2], m_vecOrigin[2] ), 0, C_BasePlayer::RecvProxy_LocalOriginZ ),
RecvPropVector( RECVINFO( m_vecViewOffset ) ),
RecvPropQAngles( RECVINFO( m_vecCarriedObjectAngles ) ),
RecvPropVector( RECVINFO( m_vecCarriedObject_CurPosToTargetPos ) ),
RecvPropQAngles( RECVINFO( m_vecCarriedObject_CurAngToTargetAng ) ),
RecvPropUtlVector( RECVINFO_UTLVECTOR( m_EntityPortalledNetworkMessages ), C_Portal_Player::MAX_ENTITY_PORTALLED_NETWORK_MESSAGES, RecvPropDataTable(NULL, 0, 0, &REFERENCE_RECV_TABLE( DT_EntityPortalledNetworkMessage ) ) ),
RecvPropInt( RECVINFO( m_iEntityPortalledNetworkMessageCount ) ),
END_RECV_TABLE()
// all players except the local player
BEGIN_RECV_TABLE_NOBASE( C_Portal_Player, DT_PortalNonLocalPlayerExclusive )
RecvPropVectorXY( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ), 0, C_BasePlayer::RecvProxy_NonLocalCellOriginXY ),
RecvPropFloat( RECVINFO_NAME( m_vecNetworkOrigin[2], m_vecOrigin[2] ), 0, C_BasePlayer::RecvProxy_NonLocalCellOriginZ ),
RecvPropFloat( RECVINFO( m_vecViewOffset[0] ) ),
RecvPropFloat( RECVINFO( m_vecViewOffset[1] ) ),
RecvPropFloat( RECVINFO( m_vecViewOffset[2] ) ),
END_RECV_TABLE()
BEGIN_RECV_TABLE_NOBASE( CPortalPlayerShared, DT_PortalPlayerShared )
RecvPropInt( RECVINFO( m_nPlayerCond ) ),
END_RECV_TABLE()
IMPLEMENT_CLIENTCLASS_DT(C_Portal_Player, DT_Portal_Player, CPortal_Player)
RecvPropDataTable( RECVINFO_DT(m_PortalLocal),0, &REFERENCE_RECV_TABLE(DT_PortalLocal) ),
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 ) ),
RecvPropBool( RECVINFO( m_bPitchReorientation ) ),
RecvPropEHandle( RECVINFO( m_hPortalEnvironment ) ),
RecvPropBool( RECVINFO( m_bIsHoldingSomething ) ),
RecvPropBool( RECVINFO( m_bPingDisabled ) ),
RecvPropBool( RECVINFO( m_bTauntDisabled ) ),
RecvPropBool( RECVINFO( m_bTauntRemoteView ) ),
RecvPropVector( RECVINFO( m_vecRemoteViewOrigin ) ),
RecvPropVector( RECVINFO( m_vecRemoteViewAngles ) ),
RecvPropFloat( RECVINFO( m_fTauntCameraDistance ) ),
RecvPropInt( RECVINFO( m_nTeamTauntState ) ),
RecvPropVector( RECVINFO( m_vTauntPosition ) ),
RecvPropQAngles( RECVINFO( m_vTauntAngles ) ),
RecvPropQAngles( RECVINFO( m_vPreTauntAngles ) ),
RecvPropBool( RECVINFO( m_bTrickFire ) ),
RecvPropEHandle( RECVINFO( m_hTauntPartnerInRange ) ),
RecvPropString( RECVINFO( m_szTauntForce ) ),
RecvPropDataTable( "portallocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_PortalLocalPlayerExclusive) ),
RecvPropDataTable( "portalnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_PortalNonLocalPlayerExclusive) ),
RecvPropBool( RECVINFO( m_bUseVMGrab ) ),
RecvPropBool( RECVINFO( m_bUsingVMGrabState ) ),
RecvPropEHandle( RECVINFO( m_hAttachedObject ) ),
RecvPropEHandle( RECVINFO( m_hHeldObjectPortal ) ),
RecvPropFloat( RECVINFO( m_flMotionBlurAmount ) ),
RecvPropBool( RECVINFO( m_bWantsToSwapGuns ) ),
RecvPropDataTable( RECVINFO_DT( m_Shared ), 0, &REFERENCE_RECV_TABLE( DT_PortalPlayerShared ) ),
RecvPropFloat( RECVINFO( m_flHullHeight ) ),
RecvPropBool( RECVINFO( m_iSpawnCounter ) ),
RecvPropBool( RECVINFO( m_bPotatos ) ),
RecvPropDataTable( RECVINFO_DT( m_StatsThisLevel ), 0, &REFERENCE_RECV_TABLE( DT_PortalPlayerStatistics ) ),
END_RECV_TABLE()
BEGIN_PREDICTION_DATA( C_Portal_Player )
DEFINE_PRED_TYPEDESCRIPTION( m_PortalLocal, C_PortalPlayerLocalData ),
#ifdef PORTAL_PLAYER_PREDICTION
DEFINE_PRED_FIELD( m_nSkin, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE ),
DEFINE_PRED_FIELD( m_nBody, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE ),
DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_flPlaybackRate, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_ARRAY_TOL( m_flEncodedController, FIELD_FLOAT, MAXSTUDIOBONECTRLS, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE, 0.02f ),
DEFINE_PRED_FIELD( m_nNewSequenceParity, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_nResetEventsParity, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
DEFINE_PRED_FIELD( m_hPortalEnvironment, FIELD_EHANDLE, FTYPEDESC_NOERRORCHECK ),
#endif // PORTAL_PLAYER_PREDICTION
// PAINT
DEFINE_PRED_TYPEDESCRIPTION( m_CachedJumpPower, PaintPowerInfo_t ),
DEFINE_PRED_FIELD( m_flCachedJumpPowerTime, FIELD_FLOAT, 0 ),
DEFINE_PRED_FIELD( m_bJumpWasPressedWhenForced, FIELD_BOOLEAN, 0 ),
DEFINE_PRED_FIELD( m_flSpeedDecelerationTime, FIELD_FLOAT, 0 ),
DEFINE_PRED_FIELD( m_flUsePostTeleportationBoxTime, FIELD_FLOAT, 0 ),
DEFINE_PRED_FIELD( m_flHullHeight, FIELD_FLOAT, 0 ),
DEFINE_FIELD( m_fLatestServerTeleport, FIELD_FLOAT ),
DEFINE_FIELD( m_matLatestServerTeleportationInverseMatrix, FIELD_VMATRIX ),
END_PREDICTION_DATA()
LINK_ENTITY_TO_CLASS( player, C_Portal_Player );
#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 ConVar sv_post_teleportation_box_time;
//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_iv_flHullHeight( "C_Portal_Player::m_iv_flHullHeight" ),
m_iv_vecCarriedObject_CurPosToTargetPos_Interpolator( "C_BaseEntity::m_iv_vecCarriedObject_CurPosToTargetPos_Interpolator" ),
m_iv_vecCarriedObject_CurAngToTargetAng_Interpolator( "C_BaseEntity::m_iv_vecCarriedObject_CurAngToTargetAng_Interpolator" ),
m_iv_vEyeOffset( "C_Portal_Player::m_iv_vEyeOffset" ),
m_flMotionBlurAmount( -1.0f ),
m_bIsBendy( false )
{
m_PlayerAnimState = CreatePortalPlayerAnimState( this );
m_iSpawnInterpCounterCache = 0;
m_hRagdoll.Set( NULL );
m_flStartLookTime = 0.0f;
m_bHeldObjectOnOppositeSideOfPortal = false;
m_hHeldObjectPortal = NULL;
m_bPitchReorientation = false;
m_fReorientationRate = 0.0f;
m_flPitchFixup = 0.0f;
m_flUprightRotDist = 0.0f;
m_angEyeAngles.Init();
AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
m_flHullHeight = GetHullHeight();
AddVar( &m_flHullHeight, &m_iv_flHullHeight, LATCH_SIMULATION_VAR );
AddVar( &m_PortalLocal.m_vEyeOffset, &m_iv_vEyeOffset, LATCH_SIMULATION_VAR );
m_iv_vecCarriedObject_CurPosToTargetPos_Interpolator.Setup( &m_vecCarriedObject_CurPosToTargetPos_Interpolated, INTERPOLATE_LINEAR_ONLY );
m_iv_vecCarriedObject_CurAngToTargetAng_Interpolator.Setup( &m_vecCarriedObject_CurAngToTargetAng_Interpolated, INTERPOLATE_LINEAR_ONLY );
m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK;
m_blinkTimer.Invalidate();
m_flUseKeyStartTime = -sv_use_trace_duration.GetFloat() - 1.0f ;
m_nUseKeyEntFoundCommandNum = -1;
m_nUseKeyEntClearCommandNum = -1;
m_nLastRecivedCommandNum = -1;
m_hUseEntToSend = NULL;
m_hUseEntThroughPortal = NULL;
m_flAutoGrabLockOutTime = 0.0f;
m_bForcingDrop = false;
m_bUseVMGrab = false;
m_bUsingVMGrabState = false;
m_bUseWasDown = false;
m_bForceFireNextPortal = false;
m_flImplicitVerticalStepSpeed = 0.0f;
m_flObjectOutOfEyeTransitionDT = 0.0f;
m_vInputVector = vec3_origin;
m_flCachedJumpPowerTime = -FLT_MAX;
m_flUsePostTeleportationBoxTime = 0.0f;
m_flSpeedDecelerationTime = 0.0f;
m_flPredictedJumpTime = 0.f;
m_bJumpWasPressedWhenForced = false;
m_vPrevGroundNormal = vec3_origin;
m_flTimeSinceLastTouchedPower[0] = FLT_MAX;
m_flTimeSinceLastTouchedPower[1] = FLT_MAX;
m_flTimeSinceLastTouchedPower[2] = FLT_MAX;
m_bFaceTauntCameraEndAngles = false;
m_bFinishingTaunt = false;
m_bDoneAirTauntHint = false;
m_bWasTaunting = false;
m_bFlingTrailActive = false;
m_bFlingTrailJustPortalled = false;
m_bFlingTrailPrePortalled = false;
m_fTeamTauntStartTime = 0.0f;
m_nOldTeamTauntState = TEAM_TAUNT_NONE;
m_angTauntPredViewAngles.Init();
m_angTauntEngViewAngles.Init();
m_bGibbed = false;
m_Shared.Init( this );
}
C_Portal_Player::~C_Portal_Player( void )
{
if ( m_PlayerAnimState )
{
m_PlayerAnimState->Release();
}
}
void C_Portal_Player::UpdateOnRemove( void )
{
if( g_pGameRules->IsMultiplayer() )
{
RemoveRemoteSplitScreenViewPlayer( this );
}
// Stop the taunt.
if ( m_bWasTaunting )
{
TurnOffTauntCam_Finish();
}
if ( m_FlingTrailEffect && m_FlingTrailEffect.IsValid() )
{
// stop the effect
m_FlingTrailEffect->StopEmission( false, true, false );
m_FlingTrailEffect = NULL;
}
DestroyPingPointer();
// All conditions should be removed
m_Shared.RemoveAllCond();
if ( m_pHeldEntityClone )
{
GetGrabController().DetachEntity( false );
m_pHeldEntityClone->Release();
m_pHeldEntityClone = NULL;
}
if ( m_pHeldEntityThirdpersonClone )
{
GetGrabController().DetachEntity( false );
m_pHeldEntityThirdpersonClone->Release();
m_pHeldEntityThirdpersonClone = NULL;
}
#if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
m_Inventory.RemoveListener( this );
RemoveClientsideWearables();
#endif
BaseClass::UpdateOnRemove();
}
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 );
}
if( g_pGameRules->IsMultiplayer() )
{
// Tell the player how to use the portalgun
C_WeaponPortalgun *pPortalGun = static_cast<C_WeaponPortalgun *>( Weapon_OwnsThisType( "weapon_portalgun", 0 ) );
if ( pPortalGun )
{
if ( pPortalGun->CanFirePortal2() )
{
IGameEvent *event = gameeventmanager->CreateEvent( "portal_enabled" );
if ( event )
{
event->SetInt( "userid", GetUserID() );
event->SetBool( "leftportal", false );
gameeventmanager->FireEventClientSide( event );
}
}
if ( pPortalGun->CanFirePortal1() )
{
IGameEvent *event = gameeventmanager->CreateEvent( "portal_enabled" );
if ( event )
{
event->SetInt( "userid", GetUserID() );
event->SetBool( "leftportal", true );
gameeventmanager->FireEventClientSide( event );
}
}
}
}
}
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 = CPortal_Base2D_Shared::AllPortals.Count();
CPortal_Base2D **pPortals = CPortal_Base2D_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 )
{
CPortal_Base2D *pTempPortal = pPortals[i];
if( pTempPortal && pTempPortal->IsActive() && 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 )
{
CPortal_Base2D* 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;
}
bool C_Portal_Player::PortalledMessageIsPending() const
{
return m_bPortalledMessagePending;
}
const char *C_Portal_Player::GetVOIPParticleEffectName( void ) const
{
if ( GetTeamNumber() == TEAM_BLUE )
return "coop_robot_talk_blue";
return "coop_robot_talk_orange";
}
CNewParticleEffect *C_Portal_Player::GetVOIPParticleEffect( void )
{
return ParticleProp()->Create( GetVOIPParticleEffectName(), PATTACH_POINT_FOLLOW, "antenna_light" );
}
//-----------------------------------------------------------------------------
// Purpose: Try to steer away from any players and objects we might interpenetrate
//-----------------------------------------------------------------------------
#define PORTAL_AVOID_MAX_RADIUS_SQR 5184.0f // Based on player extents and max buildable extents.
#define PORTAL_OO_AVOID_MAX_RADIUS_SQR 0.00019f
ConVar portal_use_player_avoidance( "portal_use_player_avoidance", "0", FCVAR_REPLICATED );
ConVar portal_max_separation_force ( "portal_max_separation_force", "256", FCVAR_DEVELOPMENTONLY );
extern ConVar cl_forwardspeed;
extern ConVar cl_backspeed;
extern ConVar cl_sidespeed;
void C_Portal_Player::AvoidPlayers( CUserCmd *pCmd )
{
// Don't test if the player doesn't exist or is dead.
if ( IsAlive() == false )
return;
// Up vector.
static Vector vecUp( 0.0f, 0.0f, 1.0f );
Vector vecPlayerCenter = GetAbsOrigin();
Vector vecPlayerMin = GetPlayerMins();
Vector vecPlayerMax = GetPlayerMaxs();
float flZHeight = vecPlayerMax.z - vecPlayerMin.z;
vecPlayerCenter.z += 0.5f * flZHeight;
VectorAdd( vecPlayerMin, vecPlayerCenter, vecPlayerMin );
VectorAdd( vecPlayerMax, vecPlayerCenter, vecPlayerMax );
// Find an intersecting player or object.
int nAvoidPlayerCount = 0;
C_Portal_Player *pAvoidPlayerList[MAX_PLAYERS];
C_Portal_Player *pIntersectPlayer = NULL;
float flAvoidRadius = 0.0f;
Vector vecAvoidCenter, vecAvoidMin, vecAvoidMax;
// for ( int i = 0; i < pTeam->GetNumPlayers(); ++i )
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
// C_TFPlayer *pAvoidPlayer = static_cast< C_TFPlayer * >( pTeam->GetPlayer( i ) );
C_Portal_Player *pAvoidPlayer = static_cast< C_Portal_Player * >( UTIL_PlayerByIndex(i) );
if ( pAvoidPlayer == NULL )
continue;
// Is the avoid player me?
if ( pAvoidPlayer == this )
continue;
// Save as list to check against for objects.
pAvoidPlayerList[nAvoidPlayerCount] = pAvoidPlayer;
++nAvoidPlayerCount;
// Check to see if the avoid player is dormant.
if ( pAvoidPlayer->IsDormant() )
continue;
// Is the avoid player solid?
if ( pAvoidPlayer->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
continue;
Vector t1, t2;
vecAvoidCenter = pAvoidPlayer->GetAbsOrigin();
vecAvoidMin = pAvoidPlayer->GetPlayerMins();
vecAvoidMax = pAvoidPlayer->GetPlayerMaxs();
flZHeight = vecAvoidMax.z - vecAvoidMin.z;
vecAvoidCenter.z += 0.5f * flZHeight;
VectorAdd( vecAvoidMin, vecAvoidCenter, vecAvoidMin );
VectorAdd( vecAvoidMax, vecAvoidCenter, vecAvoidMax );
if ( IsBoxIntersectingBox( vecPlayerMin, vecPlayerMax, vecAvoidMin, vecAvoidMax ) )
{
// Need to avoid this player.
if ( !pIntersectPlayer )
{
pIntersectPlayer = pAvoidPlayer;
break;
}
}
}
// Anything to avoid?
if ( pIntersectPlayer == NULL )
{
// m_Shared.SetSeparation( false );
// m_Shared.SetSeparationVelocity( vec3_origin );
return;
}
// Calculate the push strength and direction.
Vector vecDelta;
// Avoid a player - they have precedence.
VectorSubtract( pIntersectPlayer->WorldSpaceCenter(), vecPlayerCenter, vecDelta );
Vector vRad = pIntersectPlayer->WorldAlignMaxs() - pIntersectPlayer->WorldAlignMins();
vRad.z = 0;
flAvoidRadius = vRad.Length();
float flPushStrength = RemapValClamped( vecDelta.Length(), flAvoidRadius, 0, 0, portal_max_separation_force.GetInt() );
// Check to see if we have enough push strength to make a difference.
if ( flPushStrength < 0.01f )
return;
Vector vecPush;
if ( GetAbsVelocity().Length2DSqr() > 0.1f )
{
Vector vecVelocity = GetAbsVelocity();
vecVelocity.z = 0.0f;
CrossProduct( vecUp, vecVelocity, vecPush );
VectorNormalize( vecPush );
}
else
{
// We are not moving, but we're still intersecting.
QAngle angView = pCmd->viewangles;
angView.x = 0.0f;
AngleVectors( angView, NULL, &vecPush, NULL );
}
// Move away from the other player/object.
Vector vecSeparationVelocity;
if ( vecDelta.Dot( vecPush ) < 0 )
{
vecSeparationVelocity = vecPush * flPushStrength;
}
else
{
vecSeparationVelocity = vecPush * -flPushStrength;
}
// Don't allow the max push speed to be greater than the max player speed.
float flMaxPlayerSpeed = MaxSpeed();
float flCropFraction = 1.33333333f;
if ( ( GetFlags() & FL_DUCKING ) && ( GetGroundEntity() != NULL ) )
{
flMaxPlayerSpeed *= flCropFraction;
}
float flMaxPlayerSpeedSqr = flMaxPlayerSpeed * flMaxPlayerSpeed;
if ( vecSeparationVelocity.LengthSqr() > flMaxPlayerSpeedSqr )
{
vecSeparationVelocity.NormalizeInPlace();
VectorScale( vecSeparationVelocity, flMaxPlayerSpeed, vecSeparationVelocity );
}
QAngle vAngles = pCmd->viewangles;
vAngles.x = 0;
Vector currentdir;
Vector rightdir;
AngleVectors( vAngles, &currentdir, &rightdir, NULL );
Vector vDirection = vecSeparationVelocity;
VectorNormalize( vDirection );
float fwd = currentdir.Dot( vDirection );
float rt = rightdir.Dot( vDirection );
float forward = fwd * flPushStrength;
float side = rt * flPushStrength;
//Msg( "fwd: %f - rt: %f - forward: %f - side: %f\n", fwd, rt, forward, side );
// m_Shared.SetSeparation( true );
// m_Shared.SetSeparationVelocity( vecSeparationVelocity );
pCmd->forwardmove += forward;
pCmd->sidemove += side;
// Clamp the move to within legal limits, preserving direction. This is a little
// complicated because we have different limits for forward, back, and side
//Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
float flForwardScale = 1.0f;
if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) )
{
flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove;
}
else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) )
{
flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove );
}
float flSideScale = 1.0f;
if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) )
{
flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove );
}
float flScale = MIN( flForwardScale, flSideScale );
pCmd->forwardmove *= flScale;
pCmd->sidemove *= flScale;
//Msg( "Pforwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
}
// Remote viewing
extern ConVar cl_enable_remote_splitscreen;
extern ConVar sv_enableholdrotation;
// When set to true, VGui_OnSplitScreenStateChanged will NOT change the current system level.
extern bool g_bSuppressConfigSystemLevelDueToPIPTransitions;
static ConVar cl_suppress_config_system_level_changes_on_pip_transitions( "cl_suppress_config_system_level_changes_on_pip_transitions", "1", FCVAR_DEVELOPMENTONLY );
bool C_Portal_Player::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
{
if ( sv_enableholdrotation.GetBool() && !IsUsingVMGrab() )
{
if( m_bIsHoldingSomething && (pCmd->buttons & IN_ATTACK2) )
{
//stomp view angle changes. When holding right click and holding something, we remap mouse movement to rotate the object instead of our view
pCmd->viewangles = m_vecOldViewAngles;
engine->SetViewAngles( m_vecOldViewAngles );
}
}
// if we are in coop and not split screen, do PIP if the remote_view button is pressed
if ( GameRules()->IsMultiplayer() && !( IsSplitScreenPlayer() || GetSplitScreenPlayers().Count() > 0 ) )
{
bool bOtherPlayerIsTaunting = false;
bool bIsOtherPlayerRemoteViewTaunt = false;
if ( cl_auto_taunt_pip.GetBool() )
{
C_Portal_Player *pOtherPlayer = ToPortalPlayer( UTIL_OtherPlayer( this ) );
if ( pOtherPlayer )
{
bOtherPlayerIsTaunting = pOtherPlayer->m_Shared.InCond( PORTAL_COND_TAUNTING );
bIsOtherPlayerRemoteViewTaunt = pOtherPlayer->IsRemoteViewTaunt();
}
}
bool bRemoteViewPressed = ( pCmd->buttons & IN_REMOTE_VIEW ) != 0;
bool bUsingPIP = m_nTeamTauntState < TEAM_TAUNT_HAS_PARTNER &&
( ( bOtherPlayerIsTaunting && !bIsOtherPlayerRemoteViewTaunt ) || ( !bOtherPlayerIsTaunting && bRemoteViewPressed ) );
// Hack: Suppress changes of the current system level during this transition (see vgui_int.cpp, VGui_OnSplitScreenStateChanged()).
g_bSuppressConfigSystemLevelDueToPIPTransitions = cl_suppress_config_system_level_changes_on_pip_transitions.GetBool();
cl_enable_remote_splitscreen.SetValue( bUsingPIP );
g_bSuppressConfigSystemLevelDueToPIPTransitions = false;
}
static QAngle angMoveAngle( 0.0f, 0.0f, 0.0f );
bool bNoTaunt = true;
if ( IsTaunting() )
{
pCmd->forwardmove = 0.0f;
pCmd->sidemove = 0.0f;
pCmd->upmove = 0.0f;
pCmd->buttons = 0;
pCmd->weaponselect = 0;
VectorCopy( angMoveAngle, pCmd->viewangles );
bNoTaunt = false;
}
else
{
VectorCopy( pCmd->viewangles, angMoveAngle );
}
if ( GetFOV() != GetDefaultFOV() )
{
if ( IsTaunting() )
{
// Pop out of zoom when I'm taunting
pCmd->buttons &= ~IN_ZOOM;
KeyUp( &in_zoom, NULL );
}
else if ( GetVehicle() != NULL )
{
pCmd->buttons &= ~IN_ZOOM;
KeyUp( &in_zoom, NULL );
}
else
{
float fThreshold = sv_zoom_stop_movement_threashold.GetFloat();
if ( gpGlobals->curtime > GetFOVTime() + sv_zoom_stop_time_threashold.GetFloat() &&
( fabsf( pCmd->forwardmove ) > fThreshold || fabsf( pCmd->sidemove ) > fThreshold ) )
{
// Pop out of the zoom if we're moving
pCmd->buttons &= ~IN_ZOOM;
KeyUp( &in_zoom, NULL );
}
}
}
// Bump away from other players
AvoidPlayers( pCmd );
PollForUseEntity( pCmd );
m_bUseWasDown = (pCmd->buttons & IN_USE) != 0;
pCmd->player_held_entity = ( m_hUseEntToSend ) ? ( m_hUseEntToSend->entindex() ) : ( 0 );
pCmd->held_entity_was_grabbed_through_portal = ( m_hUseEntThroughPortal ) ? ( m_hUseEntThroughPortal->entindex() ) : ( 0 );
pCmd->command_acknowledgements_pending = pCmd->command_number - engine->GetLastAcknowledgedCommand();
pCmd->predictedPortalTeleportations = 0;
for( int i = 0; i != m_PredictedPortalTeleportations.Count(); ++i )
{
if( m_PredictedPortalTeleportations[i].iCommandNumber > pCmd->command_number )
break;
++pCmd->predictedPortalTeleportations;
}
if ( m_bForceFireNextPortal )
{
C_WeaponPortalgun *pPortalGun = dynamic_cast< C_WeaponPortalgun* >( GetActiveWeapon() );
if ( pPortalGun )
{
if ( pPortalGun->GetLastFiredPortal() == 1 )
{
pCmd->buttons |= IN_ATTACK2;
}
else
{
pCmd->buttons |= IN_ATTACK;
}
}
if ( pCmd->command_number != 0 )
{
m_bForceFireNextPortal = false;
}
}
BaseClass::CreateMove( flInputSampleTime, pCmd );
return bNoTaunt;
}
// Calls FindUseEntity every tick for a period of time
// I'm REALLY sorry about this. This will clean up quite a bit
// once grab controllers are on the client.
void C_Portal_Player::PollForUseEntity( CUserCmd *pCmd )
{
// Record the last received non-zero command number.
// Non-zero is a sloppy way of telling which CreateMoves came from ExtraMouseSample :/
if ( pCmd->command_number != 0 )
{
m_nLastRecivedCommandNum = pCmd->command_number;
}
else if ( m_nLastRecivedCommandNum == -1 )
{
// If we just constructed, don't run anything below during extra mouse samples
// until we've set m_nLastRecivedCommandNum to the correct command number from the engine
return;
}
// 82077: Polling for the use entity causes the vehicle view to cache at a time
// when the attachments are incorrect... We dont need use in a vehicle
// so this hack prevents that caching from happening and keeps the view smooth
// The only time we have a vehicle is during the ending sequence so this shouldn't be a big issue.
if ( IsX360() && GetVehicle() )
return;
// Get rid of the use ent if we've already sent the last one up to the server
if ( m_nLastRecivedCommandNum == m_nUseKeyEntClearCommandNum )
{
m_hUseEntToSend = NULL;
m_hUseEntThroughPortal = NULL;
m_nUseKeyEntFoundCommandNum = -1;
m_nUseKeyEntClearCommandNum = -1;
}
bool bBasicUse = ( ( pCmd->buttons & IN_USE ) != 0 && m_bUseWasDown == false );
C_BaseEntity *pUseEnt = NULL;
C_Portal_Base2D *pUseThroughPortal = NULL;
PollForUseEntity( bBasicUse, &pUseEnt, &pUseThroughPortal ); // Call the shared poll logic in portal_player_shared
if ( pUseEnt )
{
m_hUseEntToSend = pUseEnt;
m_hUseEntThroughPortal = pUseThroughPortal;
m_flUseKeyStartTime = -sv_use_trace_duration.GetFloat() - 1.0f;
// Record the tick we found this, so we can invalidate the handle after it is sent
m_nUseKeyEntFoundCommandNum = m_nLastRecivedCommandNum;
// If we caught this during 'extra mouse sample' calls, clear on the cmd after next, otherwise clear next command.
m_nUseKeyEntClearCommandNum = m_nUseKeyEntFoundCommandNum + ((pCmd->command_number == 0) ? ( 2 ) : ( 1 ));
}
// If we already found an ent this tick, fake a use key down so the server
// will treat this as a normal +use
if ( m_nLastRecivedCommandNum < m_nUseKeyEntClearCommandNum && m_hUseEntToSend.Get() &&
// HACK: don't do this behaviour through a portal for now (54969)
// This will be really awkward to fix for real with grab controllers living on the server.
m_hUseEntThroughPortal.Get() == NULL )
{
pCmd->buttons |= IN_USE;
}
}
void C_Portal_Player::ClientThink( void )
{
bool bIsMultiplayer = GameRules() && GameRules()->IsMultiplayer();
Vector vForward;
AngleVectors( GetLocalAngles(), &vForward );
if( PortalledMessageIsPending() )
{
m_flUsePostTeleportationBoxTime = sv_post_teleportation_box_time.GetFloat();
m_bPortalledMessagePending = false;
}
if ( m_Local.m_bSlowMovement && m_Local.m_fTBeamEndTime != 0.0f && gpGlobals->curtime > m_Local.m_fTBeamEndTime + 1.0f )
{
m_Local.m_bSlowMovement = false;
SetGravity( 1.0f );
if ( VPhysicsGetObject() )
{
VPhysicsGetObject()->EnableGravity( true );
}
}
// Air taunt lesson events
if ( GetGroundEntity() )
{
if ( m_bDoneAirTauntHint )
{
m_bDoneAirTauntHint = false;
IGameEvent *event = gameeventmanager->CreateEvent( "player_touched_ground" );
if ( event )
{
event->SetInt( "userid", GetUserID() );
gameeventmanager->FireEventClientSide( event );
}
}
if ( bIsMultiplayer && IsLocalPlayer() )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
if( GET_ACTIVE_SPLITSCREEN_SLOT() == GetSplitScreenPlayerSlot() )
{
// Handle team taunts
if ( mp_auto_taunt.GetBool() )
{
engine->ClientCmd( "taunt robotDance" );
}
else if ( mp_auto_accept_team_taunt.GetBool() || GetTeamTauntState() == TEAM_TAUNT_NEED_PARTNER )
{
C_Portal_Player *pPartnerPlayer = HasTauntPartnerInRange();
if ( pPartnerPlayer && pPartnerPlayer->GetGroundEntity() && pPartnerPlayer->GetTauntForceName()[ 0 ] != '\0' &&
( mp_auto_accept_team_taunt.GetBool() || strcmp( pPartnerPlayer->GetTauntForceName(), GetTauntForceName() ) == 0 ) )
{
engine->ClientCmd( "taunt team_accept" );
}
}
}
}
}
else if ( !m_bDoneAirTauntHint && GetAirTime() > 2.0f && PredictedAirTimeEnd() > 2.0f )
{
// We've been flying for a while and aren't headed very downward
m_bDoneAirTauntHint = true;
IGameEvent *event = gameeventmanager->CreateEvent( "player_long_fling" );
if ( event )
{
event->SetInt( "userid", GetUserID() );
gameeventmanager->FireEventClientSide( event );
}
}
// update gun's color
Color color( 255, 255, 255 );
C_WeaponPaintGun *pPaintGun = dynamic_cast< C_WeaponPaintGun* >( GetActiveWeapon() );
if ( pPaintGun && pPaintGun->HasAnyPaintPower() )
{
pPaintGun->ChangeRenderColor();
}
else if ( GetViewModel() )
{
GetViewModel()->SetRenderColor( color.r(), color.g(), color.b() );
}
RANDOM_CEG_TEST_SECRET();
if( IsLocalPlayer() )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
if( GET_ACTIVE_SPLITSCREEN_SLOT() == GetSplitScreenPlayerSlot() )
{
QAngle viewAngles;
engine->GetViewAngles(pl.v_angle);
Reorient( pl.v_angle );
engine->SetViewAngles( pl.v_angle );
}
else
{
Reorient( pl.v_angle );
}
}
else
{
Reorient( pl.v_angle );
}
if( !cl_predict->GetInt() )
{
UpdatePaintedPower();
}
if ( bIsMultiplayer && mp_bot_fling_trail.GetInt() > 0 )
{
if ( !m_bFlingTrailPrePortalled )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
// don't show the trail in first person because it doesn't look good right now
if ( C_BasePlayer::GetLocalPlayer() != this || input->CAM_IsThirdPerson() )
{
float fFlingTrail = GetAbsVelocity().Length() - MIN_FLING_SPEED;
if ( !m_bFlingTrailActive && !m_bFlingTrailPrePortalled && (m_bFlingTrailJustPortalled || fFlingTrail > 0.0f) )
{
if ( m_FlingTrailEffect )
{
// stop the effect
m_FlingTrailEffect->StopEmission( false, false, false );
m_FlingTrailEffect = NULL;
}
if ( !m_FlingTrailEffect || !m_FlingTrailEffect.IsValid() )
{
C_BaseAnimating::PushAllowBoneAccess( true, false, "mpbottrails" );
// create it
if ( mp_bot_fling_trail.GetInt() == 2 )
m_FlingTrailEffect = this->ParticleProp()->Create( "bot_fling_trail_rainbow", PATTACH_POINT_FOLLOW, "forward" );
else
m_FlingTrailEffect = this->ParticleProp()->Create( "bot_fling_trail", PATTACH_POINT_FOLLOW, "forward" );
m_bFlingTrailActive = true;
if ( m_FlingTrailEffect )
{
m_FlingTrailEffect->SetControlPoint( 1, Vector( fFlingTrail, 0, 0 ) );
}
C_BaseAnimating::PopBoneAccess( "mpbottrails" );
}
m_bFlingTrailJustPortalled = false;
}
else if ( fFlingTrail <= 0.0f && m_bFlingTrailActive && m_FlingTrailEffect )
{
// stop the effect
m_FlingTrailEffect->StopEmission( false, false, false );
m_FlingTrailEffect = NULL;
m_bFlingTrailActive = false;
}
}
}
}
if ( IsLocalPlayer() )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
if ( GET_ACTIVE_SPLITSCREEN_SLOT() == GetSplitScreenPlayerSlot() )
{
MoveHeldObjectOutOfPlayerEyes();
if ( bIsMultiplayer && IsTaunting() && IsRemoteViewTaunt() )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
Vector vTargetPos = GetThirdPersonViewPosition();
Vector vecDir = vTargetPos - m_vecRemoteViewOrigin;
float flDist = VectorNormalize( vecDir );
QAngle vecViewAngles;
VectorAngles( vecDir, vecViewAngles );
trace_t trace;
UTIL_TraceLine( vTargetPos, m_vecRemoteViewOrigin, (CONTENTS_SOLID|CONTENTS_MOVEABLE), NULL, COLLISION_GROUP_NONE, &trace );
if ( !trace.startsolid && trace.DidHit() )
{
flDist *= trace.fraction;
}
m_TauntCameraData.m_flPitch = vecViewAngles.x;
m_TauntCameraData.m_flYaw = vecViewAngles.y;
m_TauntCameraData.m_flDist = flDist;
m_TauntCameraData.m_vecHullMin.Init( -1.0f, -1.0f, -1.0f );
m_TauntCameraData.m_vecHullMax.Init( 1.0f, 1.0f, 1.0f );
QAngle vecCameraOffset( vecViewAngles.x, vecViewAngles.y, flDist );
input->CAM_SetCameraThirdData( &m_TauntCameraData, vecCameraOffset );
}
}
}
}
void C_Portal_Player::MoveHeldObjectOutOfPlayerEyes( void )
{
if ( player_held_object_keep_out_of_camera.GetBool() == false )
return;
// Not needed if we're not holding something
if ( m_hAttachedObject.Get() == NULL )
return;
C_BaseAnimating *pAnim = m_hAttachedObject.Get()->GetBaseAnimating();
if ( !pAnim )
return;
if ( IsUsingVMGrab() )
{
pAnim->DisableRenderOriginOverride();
return;
}
// HACK: This level does some odd toggling of vm mode/physics mode during
// a scene where this behavior isn't needed or desired.
if ( V_strcmp( "sp_a1_wakeup", engine->GetLevelNameShort() ) == 0 )
return;
Assert ( player_held_object_collide_with_player.GetBool() == false );
Vector vLook, vRight, vUp;
GetVectors( &vLook, &vRight, &vUp );
Vector vToObject = m_hAttachedObject.Get()->GetAbsOrigin() - EyePosition();
float distSq = vToObject.LengthSqr();
const float flBufferZone = 10.0f; // start moving into safty earlier to avoid penetrating the eyeposition
float rad = m_hAttachedObject.Get()->BoundingRadius() + flBufferZone;
// dt moves between 0 and transition time over time depending on proximity to the eyepos
float dt = m_flObjectOutOfEyeTransitionDT;
const float flTransitionTime = 0.4;
dt += ( distSq < rad*rad ) ? gpGlobals->frametime : -gpGlobals->frametime;
dt = clamp( dt, 0.0f, flTransitionTime );
m_flObjectOutOfEyeTransitionDT = dt;
// Move between our hidden position and our real world position depending on our closeness
float t = RemapVal( dt, 0.0f, flTransitionTime, 0.0f, 1.0f );
Vector out;
Vector vGoalPos = EyePosition() - vUp*rad;// - vLook*rad;
Vector vMid = EyePosition() - vUp*rad - 3.0f*vLook*rad;
Hermite_Spline( vMid, pAnim->GetAbsOrigin(), vGoalPos, t, out );
pAnim->SetRenderOriginOverride( out );
}
Vector C_Portal_Player::GetThirdPersonViewPosition( void )
{
if ( m_nTeamTauntState >= TEAM_TAUNT_HAS_PARTNER )
{
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
C_Portal_Player *pOtherPlayer = ToPortalPlayer( UTIL_PlayerByIndex( i ) );
//If the other player does not exist or if the other player is the local player
if( pOtherPlayer == NULL || pOtherPlayer == this )
continue;
return ( GetRenderOrigin() + GetViewOffset() * 0.75f + pOtherPlayer->GetRenderOrigin() + pOtherPlayer->GetViewOffset() * 0.75f ) * 0.5f;
}
}
Vector vFinalPos = GetRenderOrigin() + GetViewOffset() * 0.75f;
if ( m_Shared.InCond( PORTAL_COND_DROWNING ) && !IsRemoteViewTaunt() )
{
vFinalPos.z = UTIL_FindWaterSurface( vFinalPos, vFinalPos.z - 128.0f, vFinalPos.z + 128.0f ) + 32.0f;
}
return vFinalPos;
}
const Vector& C_Portal_Player::GetRenderOrigin( void )
{
float flHullHeight = ( GetGroundEntity() != NULL ) ? m_flHullHeight : GetStandHullHeight();
m_vRenderOrigin = WorldSpaceCenter();
m_vRenderOrigin -= 0.5f * flHullHeight * m_PortalLocal.m_StickNormal;
float fInterp = 0.0f;
if ( m_nTeamTauntState >= TEAM_TAUNT_HAS_PARTNER )
{
fInterp = mp_taunt_position_blend_rate.GetFloat() * ( gpGlobals->curtime - m_fTeamTauntStartTime );
if ( gpGlobals->curtime - m_fTeamTauntStartTime > 20.0f )
{
if ( !engine->GetNetChannelInfo()->IsTimingOut() )
{
// Fail safe
fInterp = 0.0f;
DevWarning( "Client player has been in the team taunt state for longer than 20 seconds!\n" );
}
}
else
{
fInterp = clamp( fInterp, 0.0, 1.0f );
}
}
else if ( m_fTeamTauntStartTime > 0.0f )
{
fInterp = 1.0f - clamp( mp_taunt_position_blend_rate.GetFloat() * ( gpGlobals->curtime - m_fTeamTauntStartTime ), 0.0, 1.0f );
}
if ( fInterp > 0.0f )
{
Vector vOldTempRenderOrigin = m_vTempRenderOrigin;
m_vTempRenderOrigin = m_vRenderOrigin + ( m_vTauntPosition - GetAbsOrigin() ) * fInterp;
if ( vOldTempRenderOrigin != m_vTempRenderOrigin )
{
MarkRenderHandleDirty();
}
return m_vTempRenderOrigin;
}
else if ( !m_vTempRenderOrigin.IsZero() )
{
m_vTempRenderOrigin.Zero();
MarkRenderHandleDirty();
}
return m_vRenderOrigin;
}
const QAngle& C_Portal_Player::GetRenderAngles()
{
if ( IsRagdoll() )
return vec3_angle;
float fInterp = 0.0f;
if ( m_nTeamTauntState >= TEAM_TAUNT_HAS_PARTNER )
{
fInterp = mp_taunt_position_blend_rate.GetFloat() * ( gpGlobals->curtime - m_fTeamTauntStartTime );
if ( gpGlobals->curtime - m_fTeamTauntStartTime > 20.0f )
{
// Fail safe
if ( !engine->GetNetChannelInfo()->IsTimingOut() )
{
fInterp = 0.0f;
DevWarning( "Client player has been in the team taunt state for longer than 20 seconds!\n" );
}
}
else
{
fInterp = clamp( fInterp, 0.0, 1.0f );
}
}
else if ( m_fTeamTauntStartTime > 0.0f )
{
fInterp = 1.0f - clamp( mp_taunt_position_blend_rate.GetFloat() * ( gpGlobals->curtime - m_fTeamTauntStartTime ), 0.0, 1.0f );
}
if ( fInterp > 0.0f )
{
m_TempRenderAngles[ PITCH ] = ApproachAngle( m_vTauntAngles[ PITCH ], m_PlayerAnimState->GetRenderAngles()[ PITCH ], fInterp * 360.0f );
m_TempRenderAngles[ YAW ] = ApproachAngle( m_vTauntAngles[ YAW ], m_PlayerAnimState->GetRenderAngles()[ YAW ], fInterp * 360.0f );
m_TempRenderAngles[ ROLL ] = ApproachAngle( m_vTauntAngles[ ROLL ], m_PlayerAnimState->GetRenderAngles()[ ROLL ], fInterp * 360.0f );
return m_TempRenderAngles;
}
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 ( C_BasePlayer::IsLocalPlayer( this ) )
{
m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
}
else
{
QAngle qEffectiveAngles;
if( m_iv_angEyeAngles.GetInterpolatedTime( GetEffectiveInterpolationCurTime( gpGlobals->curtime ) ) < m_fLatestServerTeleport )
{
qEffectiveAngles = TransformAnglesToLocalSpace( m_angEyeAngles, m_matLatestServerTeleportationInverseMatrix.As3x4() );
}
else
{
qEffectiveAngles = m_angEyeAngles;
}
m_PlayerAnimState->Update( qEffectiveAngles[YAW], qEffectiveAngles[PITCH] );
}
BaseClass::UpdateClientSideAnimation();
}
void C_Portal_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{
if ( GetPredictable() && IsLocalPlayer( this ) )
{
if ( !prediction->IsFirstTimePredicted() )
return;
}
MDLCACHE_CRITICAL_SECTION();
m_PlayerAnimState->DoAnimationEvent( event, nData );
}
void C_Portal_Player::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
{
switch( event )
{
case AE_WPN_PRIMARYATTACK:
{
if ( IsLocalPlayer() )
{
m_bForceFireNextPortal = true;
}
break;
}
default:
BaseClass::FireEvent( origin, angles,event, options );
}
}
bool Util_PIP_ShouldDrawPlayer( C_Portal_Player* pPortalPlayer )
{
return VGui_IsSplitScreen() && !pPortalPlayer->IsLocalPlayer() && ( pPortalPlayer->m_Shared.InCond( PORTAL_COND_TAUNTING ) ||
pPortalPlayer->m_Shared.InCond( PORTAL_COND_DROWNING ) ||
pPortalPlayer->m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) ||
pPortalPlayer->m_Shared.InCond( PORTAL_COND_DEATH_GIB ) );
}
bool C_Portal_Player::ShouldSkipRenderingViewpointPlayerForThisView( void )
{
if( !cl_skip_player_render_in_main_view.GetBool() )
return false;
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
if ( !ShouldDrawLocalPlayer() && !Util_PIP_ShouldDrawPlayer( this ) )
{
if( g_pPortalRender->GetViewRecursionLevel() == 0 )
{
//never draw if eye is still in the player's head. Always draw if the eye is transformed
if( !m_bEyePositionIsTransformedByPortal )
return true;
}
else if( g_pPortalRender->GetViewRecursionLevel() == 1 )
{
//Always draw if eye is still in the player's head. Draw for all portals except the inverse transform if eye is transformed
if( m_bEyePositionIsTransformedByPortal && (g_pPortalRender->GetCurrentViewEntryPortal() == m_pNoDrawForRecursionLevelOne) )
return true;
}
}
return false;
}
IClientModelRenderable* C_Portal_Player::GetClientModelRenderable()
{
if( (GetSplitScreenViewPlayer() == this) && ShouldSkipRenderingViewpointPlayerForThisView() )
return NULL;
return BaseClass::GetClientModelRenderable();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ConVar cl_draw_player_model("cl_draw_player_model", "1", FCVAR_DEVELOPMENTONLY);
int C_Portal_Player::DrawModel( int flags, const RenderableInstance_t &instance )
{
if ( !m_bReadyToDraw )
return 0;
if ( !cl_draw_player_model.GetBool() )
return 0;
if( (GetSplitScreenViewPlayer() == this) && ShouldSkipRenderingViewpointPlayerForThisView() )
return 0;
if( flags & STUDIO_RENDER )
{
m_nLastFrameDrawn = gpGlobals->framecount;
m_nLastDrawnStudioFlags = flags;
}
return BaseClass::DrawModel( flags, instance );
}
class CAutoInitPlayerSilhoutteMaterials : public CAutoGameSystem
{
public:
CMaterialReference m_Material;
IMaterialVar *m_pTintVariable;
void LevelInitPreEntity()
{
m_Material.Init( "models/player/chell_silhoutte", TEXTURE_GROUP_CLIENT_EFFECTS );
m_pTintVariable = m_Material->FindVar( "$color", NULL, false );
Assert( m_pTintVariable != NULL );
}
};
static CAutoInitPlayerSilhoutteMaterials s_PlayerSilhoutteMaterials;
enum PortalPlayerSkins_t
{
SKIN_RED_CHELL,
SKIN_BLUE_MEL,
SKIN_SILHOUTTE,
SKIN_RED_CHELL_NOHAIRSTRANDS,
SKIN_BLUE_MEL_NOHAIRSTRANDS
};
//-----------------------------------------------------------------------------
// 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( this ) )
{
vTempAngles[PITCH] = EyeAngles()[PITCH];
}
else
{
vTempAngles[PITCH] = m_angEyeAngles[PITCH];
}
if ( vTempAngles[YAW] < 0.0f )
{
vTempAngles[YAW] += 360.0f;
}
SetLocalAngles( vTempAngles );
BaseClass::PreThink();
// Cache the velocity before impact
if( engine->HasPaintmap() )
m_PortalLocal.m_vPreUpdateVelocity = GetAbsVelocity();
// Update the painted power
UpdatePaintedPower();
// Fade the input scale back in if we lost some
UpdateAirInputScaleFadeIn();
// Attempt to resize the hull if there's a pending hull resize
TryToChangeCollisionBounds( m_PortalLocal.m_CachedStandHullMinAttempt,
m_PortalLocal.m_CachedStandHullMaxAttempt,
m_PortalLocal.m_CachedDuckHullMinAttempt,
m_PortalLocal.m_CachedDuckHullMaxAttempt );
FixPortalEnvironmentOwnership();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_Portal_Player::Simulate( void )
{
BaseClass::Simulate();
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( !C_BasePlayer::IsLocalPlayer( this ) )
{
if ( IsEffectActive( EF_DIMLIGHT ) )
{
int iAttachment = LookupAttachment( "anim_attachment_RH" );
if ( iAttachment < 0 )
return true;
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 );
}
}
if( g_pGameRules->IsMultiplayer() && !GetPredictable() && //should have handled the grab controller in prediction, but it's off
(IsUsingVMGrab() || (GetGrabController().GetAttached() != NULL)) ) //already handling a grabbed object, or should be using VM grab
{
ManageHeldObject();
}
return true;
}
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 ( !BaseClass::ShouldDraw() )
return false;
if ( !IsAlive() )
{
if ( m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) )
return true;
else
return false;
}
if ( Util_PIP_ShouldDrawPlayer( this ) )
{
return true;
}
//return true;
// if( GetTeamNumber() == TEAM_SPECTATOR )
// return false;
if( IsLocalPlayer( this ) && IsRagdoll() )
return true;
if ( IsRagdoll() )
return false;
return true;
//return BaseClass::ShouldDraw();
}
bool C_Portal_Player::ShouldSuppressForSplitScreenPlayer( int nSlot )
{
//To properly handle ghost animatings of players through a portal. We MUST draw the player's model in the main view if their eye is transformed by a portal
//That requires that this function return true. C_Portal_PlayerfalseawModel() will sort out the nodraw cases
C_Portal_Player *pSplitscreenPlayer = static_cast< C_Portal_Player* >( GetSplitScreenViewPlayer( nSlot ) );
if ( pSplitscreenPlayer == this )
return false;
return BaseClass::ShouldSuppressForSplitScreenPlayer( nSlot );
}
//-----------------------------------------------------------------------------
// Computes the render mode for this player
//-----------------------------------------------------------------------------
PlayerRenderMode_t C_Portal_Player::GetPlayerRenderMode( int nSlot )
{
// check if local player chases owner of this weapon in first person
C_Portal_Player *pSplitscreenPlayer = static_cast< C_Portal_Player* >( GetSplitScreenViewPlayer( nSlot ) );
if ( !pSplitscreenPlayer )
return PLAYER_RENDER_THIRDPERSON;
if ( !pSplitscreenPlayer->IsLocalPlayer() &&
( pSplitscreenPlayer->m_Shared.InCond( PORTAL_COND_TAUNTING ) ||
pSplitscreenPlayer->m_Shared.InCond( PORTAL_COND_DROWNING ) ||
pSplitscreenPlayer->m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) ||
pSplitscreenPlayer->m_Shared.InCond( PORTAL_COND_DEATH_GIB ) ) )
return PLAYER_RENDER_THIRDPERSON;
return BaseClass::GetPlayerRenderMode( nSlot );
}
void C_Portal_Player::GetRenderBoundsWorldspace( Vector& absMins, Vector& absMaxs )
{
Vector mins, maxs;
GetRenderBounds( mins, maxs );
const Vector& origin = GetRenderOrigin();
VectorAdd( mins, origin, absMins );
VectorAdd( maxs, origin, absMaxs );
}
const QAngle& C_Portal_Player::EyeAngles()
{
static QAngle eyeAngles;
if ( IsLocalPlayer( this ) && g_nKillCamMode == OBS_MODE_NONE )
{
eyeAngles = 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 );
//}
eyeAngles = m_angEyeAngles;
}
Vector vForward;
AngleVectors( eyeAngles, &vForward );
// Convert angles to quaternion
Quaternion qPunch, qEyes;
AngleQuaternion( m_PortalLocal.m_qQuaternionPunch, qPunch );
AngleQuaternion( eyeAngles, qEyes );
// Multiply quaternions to punch ourself in the face
QuaternionMult( qPunch, qEyes, qEyes );
// Convert back into angles
QuaternionAngles( qEyes, eyeAngles );
return eyeAngles;
}
//-----------------------------------------------------------------------------
// 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_Portal_Base2D *pEnteredPortal, float fTime, bool bForcedDuck )
{
//Warning( "C_Portal_Player::PlayerPortalled( %s ) ent:%i slot:%i\n", IsLocalPlayer() ? "local" : "nonlocal", entindex(), engine->GetActiveSplitScreenPlayerSlot() );
#if ( PLAYERPORTALDEBUGSPEW == 1 )
Warning( "C_Portal_Player::PlayerPortalled( %f %f %f %i ) %i\n", fTime, engine->GetLastTimeStamp(), GetTimeBase(), prediction->GetLastAcknowledgedCommandNumber(), m_PredictedPortalTeleportations.Count() );
#endif
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
/*{
QAngle qEngineView;
engine->GetViewAngles( qEngineView );
Warning( "Client player portalled %f %f %f %f\n\t%f %f %f %f %f %f\n", gpGlobals->curtime, XYZ( GetNetworkOrigin() ), XYZ( pl.v_angle ), XYZ( qEngineView ) );
}*/
if ( pEnteredPortal )
{
C_Portal_Base2D *pRemotePortal = pEnteredPortal->m_hLinkedPortal;
m_bPortalledMessagePending = true;
m_PendingPortalMatrix = pEnteredPortal->MatrixThisToLinked();
if( IsLocalPlayer( this ) && pRemotePortal )
{
g_pPortalRender->EnteredPortal( GetSplitScreenPlayerSlot( ), pEnteredPortal );
}
if( !GetPredictable() )
{
//non-predicted case
ApplyUnpredictedPortalTeleportation( pEnteredPortal, fTime, bForcedDuck );
}
else
{
if( m_PredictedPortalTeleportations.Count() == 0 )
{
//surprise teleportation
#if ( PLAYERPORTALDEBUGSPEW == 1 )
Warning( "C_Portal_Player::PlayerPortalled() No predicted teleportations %f %f\n", gpGlobals->curtime, fTime );
#endif
ApplyUnpredictedPortalTeleportation( pEnteredPortal, fTime, bForcedDuck );
}
else
{
PredictedPortalTeleportation_t shouldBeThisTeleport = m_PredictedPortalTeleportations.Head();
if( pEnteredPortal != shouldBeThisTeleport.pEnteredPortal )
{
AssertMsg( false, "predicted teleportation through the wrong portal." ); //we don't have any test cases for this happening. So the logic is accordingly untested.
Warning( "C_Portal_Player::PlayerPortalled() Mismatched head teleportation %f, %f %f\n", gpGlobals->curtime, shouldBeThisTeleport.flTime, fTime );
UnrollPredictedTeleportations( shouldBeThisTeleport.iCommandNumber );
ApplyUnpredictedPortalTeleportation( pEnteredPortal, fTime, bForcedDuck );
}
else
{
#if ( PLAYERPORTALDEBUGSPEW == 1 )
Warning( "C_Portal_Player::PlayerPortalled() Existing teleportation at %f correct, %f %f\n", m_PredictedPortalTeleportations[0].flTime, gpGlobals->curtime, fTime );
#endif
m_PredictedPortalTeleportations.Remove( 0 );
}
}
}
if( pRemotePortal != NULL )
{
m_matLatestServerTeleportationInverseMatrix = pRemotePortal->MatrixThisToLinked();
}
else
{
m_matLatestServerTeleportationInverseMatrix.Identity();
}
}
m_fLatestServerTeleport = fTime;
}
void C_Portal_Player::CheckPlayerAboutToTouchPortal( void )
{
// don't run this code unless we are in MP and are using the robots
if ( !GameRules()->IsMultiplayer() )
return;
int iPortalCount = CPortal_Base2D_Shared::AllPortals.Count();
if( iPortalCount == 0 || m_bFlingTrailPrePortalled )
return;
float fFlingTrail = GetAbsVelocity().Length() - MIN_FLING_SPEED;
// if we aren't going at least fling speed, don't both with the code below
if ( fFlingTrail <= 0 )
return;
Vector vecVelocity = GetAbsVelocity();
Vector vMin, vMax;
CollisionProp()->WorldSpaceAABB( &vMin, &vMax );
Vector ptCenter = ( vMin + vMax ) * 0.5f;
Vector vExtents = ( vMax - vMin ) * 0.5f;
// bloat the player's bounding box check based on the speed and direction that he's travelling
float flScaler = mp_bot_fling_trail_kill_scaler.GetFloat();
for ( int i = 0; i < 3; ++i )
{
if ( vecVelocity[i] >= 0 )
vExtents[i] += vecVelocity[i] * flScaler;
else
vExtents[i] -= vecVelocity[i] * flScaler;
}
CPortal_Base2D **pPortals = CPortal_Base2D_Shared::AllPortals.Base();
for( int i = 0; i != iPortalCount; ++i )
{
CPortal_Base2D *pTempPortal = pPortals[i];
if( pTempPortal->IsActive() &&
(pTempPortal->m_hLinkedPortal.Get() != NULL) &&
UTIL_IsBoxIntersectingPortal( ptCenter, vExtents, pTempPortal ) )
{
Vector vecDirToPortal = ptCenter - pTempPortal->GetAbsOrigin();
VectorNormalize(vecDirToPortal);
Vector vecDirMotion = vecVelocity;
VectorNormalize(vecDirMotion);
float dot = DotProduct( vecDirToPortal, vecDirMotion );
// If the portal is behind our direction of movement, then we probably just came out of it
// IGNORE
if ( dot > 0.0f )
continue;
// if we're flinging and we touched a portal
if ( m_FlingTrailEffect && !m_bFlingTrailPrePortalled && !m_bFlingTrailJustPortalled )
{
// stop the effect linger effect if it exists
m_FlingTrailEffect->SetOwner( NULL );
ParticleProp()->StopEmission( m_FlingTrailEffect, false, true, false );
m_FlingTrailEffect = NULL;
m_bFlingTrailActive = false;
m_bFlingTrailPrePortalled = true;
return;
}
}
}
}
void C_Portal_Player::OnPreDataChanged( DataUpdateType_t type )
{
BaseClass::OnPreDataChanged( type );
m_iOldSpawnCounter = m_iSpawnCounter;
m_Shared.OnPreDataChanged();
m_hPreDataChangedAttachedObject = m_hAttachedObject;
m_bWasAlivePreUpdate = IsAlive();
}
void C_Portal_Player::PreDataUpdate( DataUpdateType_t updateType )
{
PreDataChanged_Backup.m_hPortalEnvironment = m_hPortalEnvironment;
PreDataChanged_Backup.m_qEyeAngles = m_iv_angEyeAngles.GetCurrent();
//PreDataChanged_Backup.m_ptPlayerPosition = GetNetworkOrigin();
PreDataChanged_Backup.m_iEntityPortalledNetworkMessageCount = m_iEntityPortalledNetworkMessageCount;
BaseClass::PreDataUpdate( updateType );
}
void C_Portal_Player::FixPortalEnvironmentOwnership( void )
{
CPortalSimulator *pExistingSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( this );
C_Portal_Base2D *pPortalEnvironment = m_hPortalEnvironment;
CPortalSimulator *pNewSimulator = pPortalEnvironment ? &pPortalEnvironment->m_PortalSimulator : NULL;
if( pExistingSimulator != pNewSimulator )
{
if( pExistingSimulator )
{
pExistingSimulator->ReleaseOwnershipOfEntity( this );
}
if( pNewSimulator )
{
pNewSimulator->TakeOwnershipOfEntity( this );
}
}
}
#if ( PLAYERPORTALDEBUGSPEW == 1 )
ConVar cl_spewplayerpackets( "cl_spewplayerpackets", "0" );
#endif
void C_Portal_Player::OnDataChanged( DataUpdateType_t type )
{
BaseClass::OnDataChanged( type );
FixPortalEnvironmentOwnership();
bool bRespawn = ( m_iOldSpawnCounter != m_iSpawnCounter );
if ( type == DATA_UPDATE_CREATED )
{
if ( IsLocalPlayer() )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
g_ThirdPersonManager.Init();
bRespawn = true;
CPortalMPGameRules *pRules = PortalMPGameRules();
if ( pRules )
{
pRules->LoadMapCompleteData();
}
}
}
if ( bRespawn )
{
ClientPlayerRespawn();
}
if ( g_pGameRules->IsMultiplayer() )
{
if ( m_bWasAlivePreUpdate && !IsAlive() )
{
#if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
RemoveClientsideWearables();
#endif
}
if ( !m_bGibbed && ( m_Shared.InCond( PORTAL_COND_DEATH_GIB ) || m_Shared.InCond( PORTAL_COND_DEATH_CRUSH ) ) /*&& mp_should_gib_bots.GetBool()*/ )
{
m_bGibbed = true;
CUtlReference< CNewParticleEffect > pEffect;
Vector vecOffSet = WorldSpaceCenter() - GetAbsOrigin();
pEffect = this->ParticleProp()->Create( "bot_death_B_gib", PATTACH_POINT_FOLLOW, "damage_mainbody" );
if ( pEffect )
{
pEffect->SetControlPointEntity( 0, this );
pEffect->SetControlPoint( 1, WorldSpaceCenter() );
}
}
}
m_Shared.OnDataChanged();
if ( m_hAttachedObject.Get() != m_hPreDataChangedAttachedObject.Get() && m_hPreDataChangedAttachedObject.Get() != NULL )
{
// We just lost our held object
C_BaseAnimating *pAnim = m_hPreDataChangedAttachedObject.Get()->GetBaseAnimating();
Assert ( pAnim );
if ( pAnim )
{
// Restore render origin to normal in case we modified it
pAnim->DisableRenderOriginOverride();
}
}
// Set held objects to draw in the view model
if ( IsUsingVMGrab() && m_hAttachedObject.Get() && m_hOldAttachedObject == NULL )
{
m_hOldAttachedObject = m_hAttachedObject;
m_hAttachedObject.Get()->UpdateVisibility();
if ( GameRules()->IsMultiplayer() == false )
{
m_hAttachedObject.Get()->RenderWithViewModels( true );
}
}
else if ( ( !IsUsingVMGrab() || m_hAttachedObject.Get() == NULL ) && m_hOldAttachedObject.Get() )
{
if ( GameRules()->IsMultiplayer() == false )
{
m_hOldAttachedObject.Get()->RenderWithViewModels( false );
}
m_hOldAttachedObject.Get()->UpdateVisibility();
m_hOldAttachedObject = NULL;
m_flAutoGrabLockOutTime = gpGlobals->curtime;
}
#if ( PLAYERPORTALDEBUGSPEW == 1 )
if( entindex() == 1 && cl_spewplayerpackets.GetBool() )
{
Msg( "C_Portal_Player::OnDataChanged( %f %f %f %i )\n", GetTimeBase(), gpGlobals->curtime, engine->GetLastTimeStamp(), prediction->GetLastAcknowledgedCommandNumber() );
}
#endif
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
if( GetPredictable() && (m_PredictedPortalTeleportations.Count() != 0) && (m_PredictedPortalTeleportations[0].fDeleteServerTimeStamp != -1.0f) )
{
//just because the server processed the message does not mean it also sent the teleportation temp ent
//Give that temporary entity some slack time to show up.
//We really should encode portal teleportation right into CBaseEntity to further clamp down the processing flow
if( (engine->GetLastTimeStamp() - m_PredictedPortalTeleportations[0].fDeleteServerTimeStamp) > (TICK_INTERVAL * 10) ) //give the server an extra 10 ticks to send out the teleportation message
{
//The server has acknowledged that it processed the command that we predicted this happened on. But we didn't get a teleportation notification. It must not have happened on the server
#if ( PLAYERPORTALDEBUGSPEW == 1 )
Warning( "======================OnDataChanged removing a teleportation that didn't happen!!!! %f %i -=- %f %f %i======================\n", m_PredictedPortalTeleportations[0].flTime, m_PredictedPortalTeleportations[0].iCommandNumber, GetTimeBase(), engine->GetLastTimeStamp(), prediction->GetLastAcknowledgedCommandNumber() );
#endif
UnrollPredictedTeleportations( m_PredictedPortalTeleportations[0].iCommandNumber );
}
}
}
void C_Portal_Player::PostDataUpdate( DataUpdateType_t updateType )
{
BaseClass::PostDataUpdate( updateType );
if ( updateType == DATA_UPDATE_CREATED )
{
SetNextClientThink( CLIENT_THINK_ALWAYS );
if( g_pGameRules->IsMultiplayer() && !IsLocalPlayer() )
{
AddRemoteSplitScreenViewPlayer( this );
}
}
else
{
if( m_iEntityPortalledNetworkMessageCount != PreDataChanged_Backup.m_iEntityPortalledNetworkMessageCount )
{
Assert( IsLocalPlayer() ); //this data should never have been sent down the wire
if( IsLocalPlayer() && !IsSplitScreenPlayer() ) //this buffer is stored in each player entity and sent only to the owner player, therefore we will receive 2 copies in splitscreen. Discard second player's copy
{
uint32 iStopIndex = m_iEntityPortalledNetworkMessageCount%MAX_ENTITY_PORTALLED_NETWORK_MESSAGES;
Assert( m_EntityPortalledNetworkMessages[(m_iEntityPortalledNetworkMessageCount - 1)%MAX_ENTITY_PORTALLED_NETWORK_MESSAGES].m_iMessageCount == (m_iEntityPortalledNetworkMessageCount - 1) );
bool bOverFlowed = m_EntityPortalledNetworkMessages[PreDataChanged_Backup.m_iEntityPortalledNetworkMessageCount%MAX_ENTITY_PORTALLED_NETWORK_MESSAGES].m_iMessageCount != PreDataChanged_Backup.m_iEntityPortalledNetworkMessageCount;
AssertMsg( !bOverFlowed, "Entity teleportation message overflow, increase CPortal_Player::MAX_ENTITY_PORTALLED_NETWORK_MESSAGES" );
uint32 iIterator = (bOverFlowed ? m_iEntityPortalledNetworkMessageCount : //if overflowed, start from oldest entry in the buffer
PreDataChanged_Backup.m_iEntityPortalledNetworkMessageCount) //else, start from the first new entry
% MAX_ENTITY_PORTALLED_NETWORK_MESSAGES;
do
{
C_EntityPortalledNetworkMessage &readFrom = m_EntityPortalledNetworkMessages[iIterator];
RecieveEntityPortalledMessage( readFrom.m_hEntity, readFrom.m_hPortal, readFrom.m_fTime, readFrom.m_bForcedDuck );
iIterator = (iIterator + 1) % MAX_ENTITY_PORTALLED_NETWORK_MESSAGES;
} while( iIterator != iStopIndex );
}
}
}
if ( m_nOldTeamTauntState != m_nTeamTauntState )
{
if ( ( ( m_nOldTeamTauntState == TEAM_TAUNT_NONE || m_nOldTeamTauntState == TEAM_TAUNT_NEED_PARTNER ) &&
( m_nTeamTauntState == TEAM_TAUNT_HAS_PARTNER || m_nTeamTauntState == TEAM_TAUNT_SUCCESS ) ) ||
m_nOldTeamTauntState == TEAM_TAUNT_SUCCESS )
{
m_fTeamTauntStartTime = gpGlobals->curtime;
}
m_nOldTeamTauntState = m_nTeamTauntState;
}
SetNetworkAngles( GetLocalAngles() );
if ( m_iSpawnInterpCounter != m_iSpawnInterpCounterCache )
{
MoveToLastReceivedPosition( true );
ResetLatched();
m_iSpawnInterpCounterCache = m_iSpawnInterpCounter;
}
#if ( PLAYERPORTALDEBUGSPEW == 1 )
if( entindex() == 1 && cl_spewplayerpackets.GetBool() )
{
Msg( "C_Portal_Player::PostDataUpdate( %f %f %f %i )\n", GetTimeBase(), gpGlobals->curtime, engine->GetLastTimeStamp(), prediction->GetLastAcknowledgedCommandNumber() );
}
#endif
if( GetPredictable() && (m_PredictedPortalTeleportations.Count() != 0) && (m_PredictedPortalTeleportations[0].iCommandNumber < prediction->GetLastAcknowledgedCommandNumber()) )
{
int iAcknowledgedCommand = prediction->GetLastAcknowledgedCommandNumber();
for( int i = 0; i != m_PredictedPortalTeleportations.Count(); ++i )
{
//we only mark instead of remove because the EntityPortalled message could still be in the stream, it'll have been processed by the time we get to OnDataChanged()
if( m_PredictedPortalTeleportations[i].iCommandNumber < iAcknowledgedCommand )
{
if( m_PredictedPortalTeleportations[i].fDeleteServerTimeStamp == -1.0f )
{
m_PredictedPortalTeleportations[i].fDeleteServerTimeStamp = engine->GetLastTimeStamp(); //this is the engine update where we should also receive the teleportation message
}
}
else
{
break;
}
}
}
UpdateVisibility();
FixPortalEnvironmentOwnership();
}
float C_Portal_Player::GetFOV( void )
{
//Find our FOV with offset zoom value
float flFOVOffset = C_BasePlayer::GetFOV();
// 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;
}
void C_Portal_Player::ItemPreFrame( void )
{
BaseClass::ItemPreFrame();
ManageHeldObject();
}
// Only runs in MP. Creates a fake held object and uses a clientside grab controller
// to move it so the held object looks responsive under lag conditions.
void C_Portal_Player::ManageHeldObject()
{
Assert ( GameRules()->IsMultiplayer() );
CBaseEntity *pPlayerAttached = m_hAttachedObject.Get();
//cleanup invalid clones.
{
if( (m_pHeldEntityClone != NULL) &&
((pPlayerAttached == NULL) || !IsUsingVMGrab() || (m_pHeldEntityClone->m_hOriginal != pPlayerAttached)) )
{
//cloning wrong entity or don't want a clone
if( GetGrabController().GetAttached() == m_pHeldEntityClone )
{
bool bOldForce = m_bForcingDrop;
m_bForcingDrop = true;
GetGrabController().DetachEntity( false );
m_bForcingDrop = bOldForce;
}
UTIL_Remove( m_pHeldEntityClone );
m_pHeldEntityClone = NULL;
}
if( (m_pHeldEntityThirdpersonClone != NULL) &&
((pPlayerAttached == NULL) || !IsUsingVMGrab() || (m_pHeldEntityThirdpersonClone->m_hOriginal != pPlayerAttached)) )
{
//cloning wrong entity or don't want a clone
if( GetGrabController().GetAttached() == m_pHeldEntityThirdpersonClone )
{
bool bOldForce = m_bForcingDrop;
m_bForcingDrop = true;
GetGrabController().DetachEntity( false );
m_bForcingDrop = bOldForce;
}
UTIL_Remove( m_pHeldEntityThirdpersonClone );
m_pHeldEntityThirdpersonClone = NULL;
}
}
//create clones if necessary
if( pPlayerAttached && IsUsingVMGrab() )
{
if( m_pHeldEntityClone == NULL )
{
m_pHeldEntityClone = new C_PlayerHeldObjectClone;
if ( m_pHeldEntityClone )
{
if( !m_pHeldEntityClone->InitClone( pPlayerAttached, this ) )
{
UTIL_Remove( m_pHeldEntityClone );
m_pHeldEntityClone = NULL;
}
}
}
if( m_pHeldEntityThirdpersonClone == NULL )
{
m_pHeldEntityThirdpersonClone = new C_PlayerHeldObjectClone;
if ( m_pHeldEntityThirdpersonClone )
{
if( !m_pHeldEntityThirdpersonClone->InitClone( pPlayerAttached, this, false, m_pHeldEntityClone ) )
{
UTIL_Remove( m_pHeldEntityThirdpersonClone );
m_pHeldEntityThirdpersonClone = NULL;
}
}
}
}
//ensure correct entity is attached
{
C_BaseEntity *pShouldBeAttached = NULL;
//figure out exactly what should be attached
if( pPlayerAttached )
{
pPlayerAttached->SetPredictionEligible( true ); //open the floodgates of possible predictability.
//Not sure if there's a completely sane way to invert this operation for all possible cross sections of predictables and things you can pick up, so I simply wont.
if( IsUsingVMGrab() && m_pHeldEntityClone )
{
pShouldBeAttached = m_pHeldEntityClone;
}
else
{
pShouldBeAttached = pPlayerAttached;
}
}
//swap it in if necessary
if( GetGrabController().GetAttached() != pShouldBeAttached )
{
//clear out offset history
m_iv_vecCarriedObject_CurPosToTargetPos_Interpolator.ClearHistory();
m_iv_vecCarriedObject_CurAngToTargetAng_Interpolator.ClearHistory();
//release whatever the grab controller is attached to
if( GetGrabController().GetAttached() )
{
if( (GetGrabController().GetAttached() != pPlayerAttached) && (GetGrabController().GetAttached() != m_pHeldEntityClone) )
{
GetGrabController().DetachUnknownEntity(); //the entity it has is not something we gave to it (or our player attached object invalidated under our feet)
}
else
{
bool bOldForce = m_bForcingDrop;
m_bForcingDrop = true;
GetGrabController().DetachEntity( false ); //we know the entity it's holding on to is valid, detach normally
m_bForcingDrop = bOldForce;
}
}
//if something should be attached, attach it now
if( pShouldBeAttached )
{
GetGrabController().SetIgnorePitch( true );
GetGrabController().SetAngleAlignment( 0.866025403784 );
GetGrabController().AttachEntity( this, pShouldBeAttached, pShouldBeAttached->VPhysicsGetObject(), false, vec3_origin, false );
}
}
}
//only adding these on first-predicted frames to keep old results consistent, and ease into new changes
if( pPlayerAttached && (!prediction->InPrediction() || prediction->IsFirstTimePredicted()) )
{
m_iv_vecCarriedObject_CurPosToTargetPos_Interpolator.AddToHead( gpGlobals->curtime, &m_vecCarriedObject_CurPosToTargetPos, true );
m_iv_vecCarriedObject_CurAngToTargetAng_Interpolator.AddToHead( gpGlobals->curtime, &m_vecCarriedObject_CurAngToTargetAng, true );
//need to interpolate these so they clear out old data. No direct access to clearing functions
m_iv_vecCarriedObject_CurPosToTargetPos_Interpolator.Interpolate( gpGlobals->curtime );
m_iv_vecCarriedObject_CurAngToTargetAng_Interpolator.Interpolate( gpGlobals->curtime );
}
//update the grab controller
if ( GetGrabController().GetAttached() )
{
m_iv_vecCarriedObject_CurPosToTargetPos_Interpolator.Interpolate( gpGlobals->curtime );
m_iv_vecCarriedObject_CurAngToTargetAng_Interpolator.Interpolate( gpGlobals->curtime );
GetGrabController().m_attachedAnglesPlayerSpace = m_vecCarriedObjectAngles;
GetGrabController().UpdateObject( this, 12 );
GetGrabController().ClientApproachTarget( this );
}
}
bool C_Portal_Player::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps )
{
if ( pEntity )
{
// Use the union of caps specified by client ents and those networked down from the server
int caps = pEntity->ObjectCaps() | pEntity->GetServerObjectCaps();
if ( caps & (FCAP_IMPULSE_USE|FCAP_CONTINUOUS_USE|FCAP_ONOFF_USE|FCAP_DIRECTIONAL_USE) )
{
if ( (caps & requiredCaps) == requiredCaps )
{
return true;
}
}
}
return false;
}
void C_Portal_Player::CreatePingPointer( Vector vecDestintaion )
{
DestroyPingPointer();
if ( !m_PointLaser || !m_PointLaser.IsValid() )
{
if ( !GetViewModel() )
{
Warning( "Trying to create a ping laser, but we have no view model!" );
return;
}
MDLCACHE_CRITICAL_SECTION();
C_BaseAnimating::PushAllowBoneAccess( false, true, "pingpointer" );
int iAttachment = GetViewModel()->LookupAttachment( "muzzle" );
// we don't have the portalgun
if ( iAttachment == -1 /*!GetActiveWeapon()*/ )
m_PointLaser = ParticleProp()->Create( "robot_point_beam", PATTACH_EYES_FOLLOW, -1, Vector( 0, 0, -18 ) );
else
m_PointLaser = GetViewModel()->ParticleProp()->Create( "robot_point_beam", PATTACH_POINT_FOLLOW, "muzzle" );
if ( m_PointLaser )
{
m_PointLaser->SetDrawOnlyForSplitScreenUser( GetSplitScreenPlayerSlot() );
m_PointLaser->SetControlPoint( 1, vecDestintaion );
int nTeam = GetTeamNumber();
Color color( 255, 255, 255 );
if ( nTeam == TEAM_RED )
color = UTIL_Portal_Color( 2, 0 ); //orange
else
color = UTIL_Portal_Color( 1, 0 ); //blue
Vector vColor;
vColor.x = color.r();
vColor.y = color.g();
vColor.z = color.b();
m_PointLaser->SetControlPoint( 2, vColor );
}
C_BaseAnimating::PopBoneAccess( "pingpointer" );
}
}
CEG_NOINLINE void C_Portal_Player::DestroyPingPointer( void )
{
if ( m_PointLaser )
{
CEG_PROTECT_MEMBER_FUNCTION( C_Portal_Player_DestroyPingPointer )
// stop the effect
m_PointLaser->StopEmission( false, true, false );
m_PointLaser = NULL;
}
}
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_Portal_Base2D *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::EyeFootPosition( const QAngle &qEyeAngles )
{
return EyePosition() - m_vecViewOffset.Length() * m_PortalLocal.m_Up;
}
void C_Portal_Player::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov )
{
HandleTaunting();
m_pNoDrawForRecursionLevelOne = NULL;
m_bEyePositionIsTransformedByPortal = false; //assume it's not transformed until it provably is
UpdatePortalEyeInterpolation();
QAngle qEyeAngleBackup = EyeAngles();
Vector ptEyePositionBackup = EyePosition();
if ( Util_PIP_ShouldDrawPlayer( this ) )
{
eyeAngles = EyeAngles();
eyeAngles[ PITCH ] = -m_vecRemoteViewAngles.x;
eyeAngles[ YAW ] += m_vecRemoteViewAngles.y - 180.0f;
Vector vForward;
AngleVectors( eyeAngles, &vForward );
Vector ptTargetPosition = EyePosition();
CTraceFilterSkipTwoEntities filter( NULL, NULL );
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
C_Portal_Player *pPlayer = ToPortalPlayer( UTIL_PlayerByIndex( i ) );
// Exclude the taunting player and the other player if in team taunt
if( pPlayer == NULL )
continue;
else if( pPlayer == this )
filter.SetPassEntity( this );
else if ( GetTeamTauntState() >= TEAM_TAUNT_HAS_PARTNER )
filter.SetPassEntity2( pPlayer );
}
float flBestFraction = 0.0f;
float flBestYaw = eyeAngles[ YAW ];
int nTestNum = 0;
const int nMaxTests = 15;
const float fJitter = 180.0f / static_cast<float>( nMaxTests );
bool bStartSolid = false;
float fSolidStartDist = MIN( 16.0f, m_fTauntCameraDistance );
while ( flBestFraction < 0.75f && nTestNum < nMaxTests )
{
// Test and modify goals if camera doesn't fit
float fCurrentYaw = eyeAngles[ YAW ] + nTestNum * fJitter * ( ( nTestNum % 2 == 0 ) ? 1.0f : -1.0f );
float TauntCamTargetYaw = GetAbsAngles()[ YAW ] + fCurrentYaw;
Vector vTestForward;
AngleVectors( QAngle( eyeAngles[ PITCH ], TauntCamTargetYaw, 0 ), &vTestForward, NULL, NULL );
trace_t trace;
if ( !bStartSolid )
{
// Start from the center
UTIL_TraceHull( ptTargetPosition, ptTargetPosition + ( vTestForward * m_fTauntCameraDistance ), Vector( -9.f, -9.f, -9.f ), Vector( 9.f, 9.f, 9.f ), MASK_SOLID, &filter, &trace );
if ( trace.startsolid )
{
bStartSolid = true;
}
}
if ( bStartSolid )
{
// Start away from the center
UTIL_TraceHull( ptTargetPosition + ( vTestForward * fSolidStartDist ), ptTargetPosition + ( vTestForward * m_fTauntCameraDistance ), Vector( -9.0f, -9.0f, -9.0f ), Vector( 9.0f, 9.0f, 9.0f ), MASK_SOLID, &filter, &trace );
}
if ( flBestFraction < trace.fraction && !trace.startsolid )
{
flBestFraction = trace.fraction;
flBestYaw = fCurrentYaw;
}
nTestNum++;
}
// setup eye position and angle
eyeAngles[ YAW ] = flBestYaw - 180.0f;
AngleVectors( eyeAngles, &vForward );
eyeOrigin = ptTargetPosition - vForward * m_fTauntCameraDistance * flBestFraction;
fov = GetFOV();
return;
}
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 !defined PORTAL_HIDE_PLAYER_RAGDOLL
origin.z += VEC_DEAD_VIEWHEIGHT.z; // look over ragdoll, not through
#endif //!PORTAL_HIDE_PLAYER_RAGDOLL
}
if ( !GameRules()->IsMultiplayer() )
BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov );
eyeOrigin = origin;
Vector vForward;
AngleVectors( eyeAngles, &vForward );
VectorNormalize( vForward );
#if !defined PORTAL_HIDE_PLAYER_RAGDOLL
VectorMA( origin, -CHASE_CAM_DISTANCE, 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;
}
// in multiplayer we want to make sure we get the screenshakes and stuff
if ( GameRules()->IsMultiplayer() )
CalcPortalView( eyeOrigin, eyeAngles, fov );
}
else
{
IClientVehicle *pVehicle;
pVehicle = GetVehicle();
if ( !pVehicle )
{
if ( IsObserver() )
{
CalcObserverView( eyeOrigin, eyeAngles, fov );
}
else
{
CalcPortalView( eyeOrigin, eyeAngles, fov );
}
}
else
{
CalcVehicleView( pVehicle, eyeOrigin, eyeAngles, zNear, zFar, fov );
}
}
if( !IsLocalPlayer() ) //HACKHACK: This is a quick hammer to fix the roll on nonlocal players
eyeAngles[ROLL] = 0.0f;
}
void C_Portal_Player::CalcPortalView( Vector &eyeOrigin, QAngle &eyeAngles, float &fov )
{
float fEffectiveCurTime = GetEffectiveInterpolationCurTime( gpGlobals->curtime );
if ( !prediction->InPrediction() )
{
// FIXME: Move into prediction
view->DriftPitch();
}
// TrackIR
if ( IsHeadTrackingEnabled() )
{
VectorCopy( EyePosition() + GetEyeOffset(), eyeOrigin );
VectorCopy( EyeAngles() + GetEyeAngleOffset(), eyeAngles );
}
else
{
VectorCopy( EyePosition(), eyeOrigin );
VectorCopy( EyeAngles(), eyeAngles );
}
Vector vRenderOrigin = GetRenderOrigin();
//if discontinuous eye position gets a transform, so do eye angles
bool bEyeDiscontinuity = false;
{
matrix3x4_t matTemp;
if( GetOriginInterpolator().GetDiscontinuityTransform( fEffectiveCurTime, matTemp ) )
{
eyeAngles = TransformAnglesToWorldSpace( eyeAngles, matTemp );
bEyeDiscontinuity = true;
}
}
VectorAdd( eyeAngles, m_Local.m_vecPunchAngle, eyeAngles );
if ( !prediction->InPrediction() )
{
GetViewEffects()->CalcShake();
GetViewEffects()->ApplyShake( eyeOrigin, eyeAngles, 1.0 );
}
if( !prediction->InPrediction() )
{
SmoothViewOnStairs( eyeOrigin );
}
// Apply a smoothing offset to smooth out prediction errors.
Vector vSmoothOffset;
GetPredictionErrorSmoothingVector( vSmoothOffset );
eyeOrigin += vSmoothOffset;
m_bEyePositionIsTransformedByPortal = false;
C_Portal_Base2D *pTransformPortal = NULL;
for( int i = 0; i != CPortal_Base2D_Shared::AllPortals.Count(); ++i )
{
C_Portal_Base2D *pPortal = CPortal_Base2D_Shared::AllPortals[i];
if( !pPortal->IsActivedAndLinked() )
continue;
float fEyeDist = pPortal->m_plane_Origin.normal.Dot( eyeOrigin ) - pPortal->m_plane_Origin.dist;
float fBodyDist = pPortal->m_plane_Origin.normal.Dot( vRenderOrigin ) - pPortal->m_plane_Origin.dist;
if( (fEyeDist < 0.0f) && //eye behind portal
(fBodyDist >= 0.0f) ) //body in front of portal
{
float fOOTotalDist = 1.0f / (fBodyDist - fEyeDist);
Vector vIntersect = (eyeOrigin * (fBodyDist * fOOTotalDist)) - (vRenderOrigin * (fEyeDist * fOOTotalDist));
Vector vCenterToIntersect = vIntersect - pPortal->m_ptOrigin;
if( (fabs(vCenterToIntersect.Dot( pPortal->m_vRight )) > pPortal->GetHalfWidth()) ||
(fabs(vCenterToIntersect.Dot( pPortal->m_vUp )) > pPortal->GetHalfHeight()) )
continue;
pTransformPortal = pPortal;
break;
}
}
if( !pTransformPortal && m_hPortalEnvironment.Get() != NULL )
{
C_Portal_Base2D *pPortal = m_hPortalEnvironment;
if( pPortal->IsActivedAndLinked() )
{
if( GetOriginInterpolator().GetInterpolatedTime( fEffectiveCurTime ) < m_fLatestServerTeleport )
{
pPortal = pPortal->m_hLinkedPortal.Get();
}
float fEyeDist = pPortal->m_plane_Origin.normal.Dot( eyeOrigin ) - pPortal->m_plane_Origin.dist;
if( fEyeDist < 0.0f )
{
pTransformPortal = pPortal;
}
}
}
if( pTransformPortal )
{
m_bEyePositionIsTransformedByPortal = true;
m_pNoDrawForRecursionLevelOne = pTransformPortal->m_hLinkedPortal.Get();
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 );
UTIL_Portal_PointTransform( pTransformPortal->MatrixThisToLinked(), eyeOrigin, eyeOrigin );
UTIL_Portal_AngleTransform( pTransformPortal->MatrixThisToLinked(), 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 );
}
m_flObserverChaseDistance = 0.0;
//if( !engine->IsPaused() && entindex() == 1 )
// Warning( "C_Portal_Player::CalcPortalView(%f) %s %s %f %f %f\n", gpGlobals->curtime, m_bEyePositionIsTransformedByPortal ? "trans" : "normal", bEyeDiscontinuity ? "disc" : "linear", GetOriginInterpolator().GetInterpolatedTime( gpGlobals->curtime ), m_fLatestServerTeleport, GetOriginInterpolator().GetOldestEntry() );
// calc current FOV
fov = GetFOV();
}
void C_Portal_Player::GetToolRecordingState( KeyValues *msg )
{
BaseClass::GetToolRecordingState( msg );
if( m_bToolMode_EyeHasPortalled_LastRecord != m_bEyePositionIsTransformedByPortal )
{
BaseEntityRecordingState_t dummyState;
BaseEntityRecordingState_t *pState = (BaseEntityRecordingState_t *)msg->GetPtr( "baseentity", &dummyState );
pState->m_fEffects |= EF_NOINTERP; //If we interpolate, we'll be traversing an arbitrary line through the level at an undefined speed. That would be bad
}
m_bToolMode_EyeHasPortalled_LastRecord = m_bEyePositionIsTransformedByPortal;
//record if the eye is on the opposite side of the portal from the body
{
CameraRecordingState_t dummyState;
CameraRecordingState_t *pState = (CameraRecordingState_t *)msg->GetPtr( "camera", &dummyState );
pState->m_bPlayerEyeIsPortalled = m_bEyePositionIsTransformedByPortal;
}
}
void C_Portal_Player::SetAnimation( PLAYER_ANIM playerAnim )
{
return;
}
void C_Portal_Player::CalcViewModelView( const Vector& eyeOrigin, const QAngle& eyeAngles)
{
C_Portal_Base2D *pTransformedByPortal = (m_bEyePositionIsTransformedByPortal) ? m_pNoDrawForRecursionLevelOne->m_hLinkedPortal.Get() : NULL;
bool bInvertFacing = GetOriginInterpolator().GetInterpolatedTime( GetEffectiveInterpolationCurTime( gpGlobals->curtime ) ) < m_fLatestServerTeleport;
for ( int i = 0; i < MAX_VIEWMODELS; i++ )
{
CBaseViewModel *vm = GetViewModel( i );
if ( !vm )
continue;
if( bInvertFacing )
{
Vector vTemp;
VectorRotate( vm->m_vecLastFacing, m_matLatestServerTeleportationInverseMatrix.As3x4(), vTemp );
vm->m_vecLastFacing = vTemp;
}
//if our eyes are behind a portal, then we transform the view by the portal when rendering.
//This causes havoc when computing the deltas in view angle changes in the weapon. To fix this we transform the last facing direction by the same matrix
if( pTransformedByPortal )
{
Vector vTemp;
VectorRotate( vm->m_vecLastFacing, pTransformedByPortal->m_matrixThisToLinked.As3x4(), vTemp );
vm->m_vecLastFacing = vTemp;
vm->CalcViewModelView( this, eyeOrigin, eyeAngles );
VectorRotate( vm->m_vecLastFacing, pTransformedByPortal->m_hLinkedPortal.Get()->m_matrixThisToLinked.As3x4(), vTemp );
vm->m_vecLastFacing = vTemp;
}
else
{
vm->CalcViewModelView( this, eyeOrigin, eyeAngles );
}
if( bInvertFacing )
{
Vector vTemp;
VectorIRotate( vm->m_vecLastFacing, m_matLatestServerTeleportationInverseMatrix.As3x4(), vTemp );
vm->m_vecLastFacing = vTemp;
}
}
}
bool LocalPlayerIsCloseToPortal( void )
{
return C_Portal_Player::GetLocalPlayer()->IsCloseToPortal();
}
static ConVar portal_tauntcam_yaw( "portal_tauntcam_yaw", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
static ConVar portal_tauntcam_pitch( "portal_tauntcam_pitch", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
static ConVar portal_tauntcam_speed( "portal_tauntcam_speed", "600", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
static ConVar portal_deathcam_pitch( "portal_deathcam_pitch", "45.f", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
static ConVar portal_deathcam_gib_pitch( "portal_deathcam_gib_pitch", "25.f", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CEG_NOINLINE void C_Portal_Player::TurnOnTauntCam( void )
{
if ( !IsLocalPlayer( this ) )
return;
m_bFinishingTaunt = false;
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
// Save the old view angles.
engine->GetViewAngles( m_angTauntEngViewAngles );
prediction->GetViewAngles( m_angTauntPredViewAngles );
m_bFaceTauntCameraEndAngles = false;
if ( m_bTauntRemoteView )
{
Vector vTargetPos = GetThirdPersonViewPosition();
float flDist = vTargetPos.DistTo( m_vecRemoteViewOrigin );
trace_t trace;
UTIL_TraceLine( vTargetPos, m_vecRemoteViewOrigin, (CONTENTS_SOLID|CONTENTS_MOVEABLE), NULL, COLLISION_GROUP_NONE, &trace );
if ( !trace.startsolid && trace.DidHit() )
{
flDist *= trace.fraction;
}
m_TauntCameraData.m_flPitch = m_vecRemoteViewAngles.x;
m_TauntCameraData.m_flYaw = m_vecRemoteViewAngles.y;
m_TauntCameraData.m_flDist = flDist;
m_TauntCameraData.m_flLag = -1.0f;
m_TauntCameraData.m_vecHullMin.Init( -1.0f, -1.0f, -1.0f );
m_TauntCameraData.m_vecHullMax.Init( 1.0f, 1.0f, 1.0f );
CEG_PROTECT_MEMBER_FUNCTION( C_Portal_Player_TurnOnTauntCam )
QAngle vecCameraOffset( m_vecRemoteViewAngles.x, m_vecRemoteViewAngles.y, flDist );
input->CAM_ToThirdPerson();
ThirdPersonSwitch( true );
input->CAM_SetCameraThirdData( &m_TauntCameraData, vecCameraOffset );
}
else
{
m_flTauntCamTargetDist = m_fTauntCameraDistance;
m_flTauntCamCurrentDist = 0.f;
m_TauntCameraData.m_flPitch = m_vecRemoteViewAngles.x;
m_TauntCameraData.m_flYaw = m_vecRemoteViewAngles.y;
m_TauntCameraData.m_flDist = m_flTauntCamTargetDist;
m_TauntCameraData.m_flLag = 1.0f;
m_TauntCameraData.m_vecHullMin.Init( -9.0f, -9.0f, -9.0f );
m_TauntCameraData.m_vecHullMax.Init( 9.0f, 9.0f, 9.0f );
QAngle angle = EyeAngles();
float pitch;
bool bInterpolateViewToAngle = true;
if ( m_Shared.InCond( PORTAL_COND_DROWNING ) )
{
pitch = portal_deathcam_pitch.GetFloat();
bInterpolateViewToAngle = false;
}
else if ( m_Shared.InCond( PORTAL_COND_DEATH_GIB ) )
{
pitch = portal_deathcam_gib_pitch.GetFloat();
bInterpolateViewToAngle = false;
}
else
{
pitch = angle[PITCH];
}
g_ThirdPersonManager.UseCameraOffsets( true );
g_ThirdPersonManager.SetCameraOffsetAngles( Vector( pitch, angle[YAW], m_flTauntCamCurrentDist ) );
g_ThirdPersonManager.SetDesiredCameraOffset( Vector( pitch, angle[YAW], m_flTauntCamTargetDist ) );
g_ThirdPersonManager.SetOverridingThirdPerson( true );
input->CAM_ToThirdPerson();
ThirdPersonSwitch( true );
m_bTauntInterpolating = true;
m_bTauntInterpolatingAngles = bInterpolateViewToAngle;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_Portal_Player::TurnOffTauntCam( void )
{
if ( !IsLocalPlayer( this ) )
return;
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
CEG_PROTECT_MEMBER_FUNCTION( C_Portal_Player_TurnOffTauntCam )
if ( m_bTauntRemoteView )
{
TurnOffTauntCam_Finish();
}
else
{
// We want to interpolate back into the guy's head.
m_flTauntCamTargetDist = 0.f;
m_TauntCameraData.m_flDist = m_flTauntCamTargetDist;
g_ThirdPersonManager.SetOverridingThirdPerson( false );
m_bTauntInterpolating = true;
if ( ( cl_taunt_finish_rotate_cam.GetInt() != 0 ) && !m_bFaceTauntCameraEndAngles )
{
m_TauntCameraData.m_flPitch = 0;
m_TauntCameraData.m_flYaw = 0;
m_TauntCameraData.m_flLag = -1.0f;
m_bFinishingTaunt = true;
m_bTauntInterpolatingAngles = true;
}
}
}
void C_Portal_Player::TurnOffTauntCam_Finish()
{
if ( !IsLocalPlayer() )
return;
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
Vector vecOffset = g_ThirdPersonManager.GetCameraOffsetAngles();
portal_tauntcam_pitch.SetValue( vecOffset[PITCH] - m_angTauntPredViewAngles[PITCH] );
portal_tauntcam_yaw.SetValue( vecOffset[YAW] - m_angTauntPredViewAngles[YAW] );
QAngle angles;
angles[PITCH] = vecOffset[PITCH];
angles[YAW] = vecOffset[YAW];
angles[DIST] = vecOffset[DIST];
if ( m_bFaceTauntCameraEndAngles )
{
// Reset the old view angles.
engine->SetViewAngles( angles );
prediction->SetViewAngles( angles );
}
g_ThirdPersonManager.SetOverridingThirdPerson( false );
if ( g_ThirdPersonManager.WantToUseGameThirdPerson() == false )
{
input->CAM_ToFirstPerson();
ThirdPersonSwitch( false );
angles = vec3_angle;
}
RANDOM_CEG_TEST_SECRET_PERIOD( 12, 19 )
input->CAM_SetCameraThirdData( NULL, angles );
// Force the feet to line up with the view direction post taunt.
m_PlayerAnimState->m_bForceAimYaw = true;
m_bTauntInterpolating = false;
m_bTauntInterpolatingAngles = false;
if ( GetViewModel() )
{
GetViewModel()->UpdateVisibility();
}
m_bFinishingTaunt = false;
}
void C_Portal_Player::TauntCamInterpolation()
{
C_Portal_Player *pLocalPlayer = C_Portal_Player::GetLocalPortalPlayer();
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
if ( pLocalPlayer && m_bTauntInterpolating )
{
float flSpeed = gpGlobals->frametime * portal_tauntcam_speed.GetFloat();
if ( m_flTauntCamCurrentDist < m_flTauntCamTargetDist )
{
m_flTauntCamCurrentDist += flSpeed;
m_flTauntCamCurrentDist = clamp( m_flTauntCamCurrentDist, m_flTauntCamCurrentDist, m_flTauntCamTargetDist );
}
else if ( m_flTauntCamCurrentDist > m_flTauntCamTargetDist )
{
m_flTauntCamCurrentDist -= flSpeed;
m_flTauntCamCurrentDist = clamp( m_flTauntCamCurrentDist, m_flTauntCamTargetDist, m_flTauntCamCurrentDist );
}
Vector vecOrigin = pLocalPlayer->GetThirdPersonViewPosition();
Vector vecCamOffset = g_ThirdPersonManager.GetCameraOffsetAngles();
CTraceFilterSkipTwoEntities filter( pLocalPlayer, NULL );
if ( pLocalPlayer->GetTeamTauntState() >= TEAM_TAUNT_HAS_PARTNER )
{
for( int i = 1; i <= gpGlobals->maxClients; ++i )
{
C_Portal_Player *pOtherPlayer = ToPortalPlayer( UTIL_PlayerByIndex( i ) );
//If the other player does not exist or if the other player is the local player
if( pOtherPlayer == NULL || pOtherPlayer == pLocalPlayer )
continue;
filter.SetPassEntity2( pOtherPlayer );
break;
}
}
if ( m_bTauntInterpolatingAngles )
{
m_flTauntCamTargetPitch = m_TauntCameraData.m_flPitch;
m_flTauntCamTargetYaw = 0.0f;
float flBestFraction = 0.0f;
float flBestYaw = m_TauntCameraData.m_flYaw;
int nTestNum = 0;
const int nMaxTests = 15;
const float fJitter = 180.0f / static_cast<float>( nMaxTests );
while ( flBestFraction < 0.75f && nTestNum < nMaxTests )
{
// Test and modify goals if camera doesn't fit
float fCurrentYaw = m_TauntCameraData.m_flYaw + nTestNum * fJitter * ( ( nTestNum % 2 == 0 ) ? 1.0f : -1.0f );
m_flTauntCamTargetYaw = GetAbsAngles()[ YAW ] + fCurrentYaw;
Vector vTestForward;
AngleVectors( QAngle( m_flTauntCamTargetPitch, m_flTauntCamTargetYaw, 0 ), &vTestForward, NULL, NULL );
trace_t trace;
UTIL_TraceHull( vecOrigin, vecOrigin - ( vTestForward * m_flTauntCamTargetDist ), Vector( -9.f, -9.f, -9.f ), Vector( 9.f, 9.f, 9.f ), MASK_SOLID, &filter, &trace );
if ( flBestFraction < trace.fraction )
{
flBestFraction = trace.fraction;
flBestYaw = fCurrentYaw;
}
nTestNum++;
}
m_TauntCameraData.m_flYaw = flBestYaw;
float flRotMultiplier = m_bFinishingTaunt ? cl_taunt_finish_speed.GetFloat() : 0.5f;
vecCamOffset[ PITCH ] = ApproachAngle( m_flTauntCamTargetPitch, vecCamOffset[ PITCH ], flSpeed * flRotMultiplier );
vecCamOffset[ YAW ] = ApproachAngle( m_flTauntCamTargetYaw, vecCamOffset[ YAW ], flSpeed * flRotMultiplier );
g_ThirdPersonManager.SetCameraOffsetAngles( vecCamOffset );
if ( fabsf( AngleDiff( m_flTauntCamTargetPitch, vecCamOffset[ PITCH ] ) ) <= 1.0f &&
fabsf( AngleDiff( m_flTauntCamTargetYaw, vecCamOffset[ YAW ] ) ) <= 1.0f )
{
m_bTauntInterpolatingAngles = false;
}
}
Vector vecForward;
AngleVectors( QAngle( vecCamOffset[PITCH], vecCamOffset[YAW], 0 ), &vecForward, NULL, NULL );
trace_t trace;
UTIL_TraceHull( vecOrigin, vecOrigin - ( vecForward * m_flTauntCamCurrentDist ), Vector( -9.f, -9.f, -9.f ), Vector( 9.f, 9.f, 9.f ), MASK_SOLID, &filter, &trace );
if ( trace.fraction < 1.0 )
m_flTauntCamCurrentDist *= trace.fraction;
QAngle angCameraOffset = QAngle( vecCamOffset[PITCH], vecCamOffset[YAW], m_flTauntCamCurrentDist );
input->CAM_SetCameraThirdData( &m_TauntCameraData, angCameraOffset ); // Override camera distance interpolation.
g_ThirdPersonManager.SetDesiredCameraOffset( Vector( m_flTauntCamCurrentDist, 0, 0 ) );
if ( m_flTauntCamCurrentDist == m_flTauntCamTargetDist )
{
if ( m_flTauntCamTargetDist == 0.f )
{
TurnOffTauntCam_Finish();
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_Portal_Player::HandleTaunting( void )
{
C_Portal_Player *pLocalPlayer = C_Portal_Player::GetLocalPortalPlayer();
// Clear the taunt slot.
if ( !m_bWasTaunting && IsTaunting() )
{
m_bWasTaunting = true;
// Handle the camera for the local player.
if ( pLocalPlayer )
{
TurnOnTauntCam();
}
}
if ( m_bWasTaunting && !IsTaunting() )
{
m_bWasTaunting = false;
// Clear the vcd slot.
m_PlayerAnimState->ResetGestureSlot( GESTURE_SLOT_VCD );
// Handle the camera for the local player.
if ( pLocalPlayer )
{
TurnOffTauntCam();
}
}
TauntCamInterpolation();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_Portal_Player::StartSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
{
switch ( event->GetType() )
{
case CChoreoEvent::SEQUENCE:
case CChoreoEvent::GESTURE:
return StartGestureSceneEvent( info, scene, event, actor, pTarget );
default:
return BaseClass::StartSceneEvent( info, scene, event, actor, pTarget );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_Portal_Player::StartGestureSceneEvent( CSceneEventInfo *info, CChoreoScene *scene, CChoreoEvent *event, CChoreoActor *actor, CBaseEntity *pTarget )
{
// Get the (gesture) sequence.
info->m_nSequence = LookupSequence( event->GetParameters() );
if ( info->m_nSequence < 0 )
return false;
// Player the (gesture) sequence.
m_PlayerAnimState->AddVCDSequenceToGestureSlot( GESTURE_SLOT_VCD, info->m_nSequence );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Don't collide with other players, we'll just push away from them
//-----------------------------------------------------------------------------
bool C_Portal_Player::ShouldCollide( int collisionGroup, int contentsMask ) const
{
// Don't hit other players
if ( portal_use_player_avoidance.GetBool() && ( ( collisionGroup == COLLISION_GROUP_PLAYER || collisionGroup == COLLISION_GROUP_PLAYER_MOVEMENT ) ) )
return false;
return BaseClass::ShouldCollide( collisionGroup, contentsMask );
}
void C_Portal_Player::ApplyTransformToInterpolators( const VMatrix &matTransform, float fUpToTime, bool bIsRevertingPreviousTransform, bool bDuckForced )
{
Vector vOriginToCenter = (GetHullMaxs() + GetHullMins()) * 0.5f;
Vector vCenterToOrigin = -vOriginToCenter;
Vector vViewOffset = vec3_origin;
VMatrix matCenterTransform = matTransform, matEyeTransform;
Vector vOldEye = GetViewOffset();
Vector vNewEye = GetViewOffset();
if( bDuckForced )
{
// Going to be standing up
if( bIsRevertingPreviousTransform )
{
vNewEye = VEC_VIEW;
vViewOffset = VEC_VIEW - VEC_DUCK_VIEW;
vOriginToCenter = (GetDuckHullMins() + GetDuckHullMaxs()) * 0.5f;
vCenterToOrigin = -(GetStandHullMins() + GetStandHullMaxs()) * 0.5f;
}
// Going to be crouching
else
{
vNewEye = VEC_DUCK_VIEW;
vViewOffset = VEC_DUCK_VIEW - VEC_VIEW;
vOriginToCenter = (GetStandHullMins() + GetStandHullMaxs()) * 0.5f;
vCenterToOrigin = -(GetDuckHullMins() + GetDuckHullMaxs()) * 0.5f;
}
vOldEye = matTransform.ApplyRotation( vOldEye );
Vector vEyeOffset = vOldEye - vNewEye - vCenterToOrigin;
matEyeTransform = SetupMatrixTranslation(vEyeOffset);
}
else
{
vOldEye -= vOriginToCenter;
vOldEye = matTransform.ApplyRotation( vOldEye );
vOldEye += vOriginToCenter;
Vector vEyeOffset = vOldEye - vNewEye;
matEyeTransform = SetupMatrixTranslation(vEyeOffset);
}
// There's a 1-frame pop in multiplayer with lag when forced to duck. WHAT THE FUCKKKKKKKKKKKKKKK
// Translate origin to center
matCenterTransform = matCenterTransform * SetupMatrixTranslation(vOriginToCenter);
// Translate center to origin
matCenterTransform = SetupMatrixTranslation( vCenterToOrigin ) * matCenterTransform;
VMatrix matViewOffset = SetupMatrixTranslation( vViewOffset );
if( bIsRevertingPreviousTransform )
{
GetOriginInterpolator().RemoveDiscontinuity( fUpToTime, &matCenterTransform.As3x4() );
GetRotationInterpolator().RemoveDiscontinuity( fUpToTime, &matCenterTransform.As3x4() );
m_iv_angEyeAngles.RemoveDiscontinuity( fUpToTime, &matCenterTransform.As3x4() );
m_iv_vecViewOffset.RemoveDiscontinuity( fUpToTime, &matViewOffset.As3x4() );
m_iv_vEyeOffset.RemoveDiscontinuity( fUpToTime, &matEyeTransform.As3x4() );
}
else
{
GetOriginInterpolator().InsertDiscontinuity( matCenterTransform.As3x4(), fUpToTime );
GetRotationInterpolator().InsertDiscontinuity( matCenterTransform.As3x4(), fUpToTime );
m_iv_angEyeAngles.InsertDiscontinuity( matCenterTransform.As3x4(), fUpToTime );
m_iv_vecViewOffset.InsertDiscontinuity( matViewOffset.As3x4(), fUpToTime );
m_iv_vEyeOffset.InsertDiscontinuity( matEyeTransform.As3x4(), fUpToTime );
}
m_PlayerAnimState->TransformYAWs( matCenterTransform.As3x4() );
AddEFlags( EFL_DIRTY_ABSTRANSFORM );
}
ConVar cl_resetportalledplayerinterp( "cl_resetportalledplayerinterp", "0" );
void C_Portal_Player::ApplyUnpredictedPortalTeleportation( const C_Portal_Base2D *pEnteredPortal, float flTeleportationTime, bool bForcedDuck )
{
ApplyTransformToInterpolators( pEnteredPortal->m_matrixThisToLinked, flTeleportationTime, false, bForcedDuck );
//Warning( "Applying teleportation view angle change %d, %f\n", m_PredictedPortalTeleportations.Count(), gpGlobals->curtime );
if( IsLocalPlayer() && (GET_ACTIVE_SPLITSCREEN_SLOT() == GetSplitScreenPlayerSlot()) )
{
//Warning( "C_Portal_Player::ApplyUnpredictedPortalTeleportation() ent:%i slot:%i\n", entindex(), engine->GetActiveSplitScreenPlayerSlot() );
matrix3x4_t matAngleTransformIn, matAngleTransformOut; //temps for angle transformation
{
QAngle qEngineAngles;
engine->GetViewAngles( qEngineAngles );
AngleMatrix( qEngineAngles, matAngleTransformIn );
ConcatTransforms( pEnteredPortal->m_matrixThisToLinked.As3x4(), matAngleTransformIn, matAngleTransformOut );
MatrixAngles( matAngleTransformOut, qEngineAngles );
engine->SetViewAngles( qEngineAngles );
pl.v_angle = qEngineAngles;
}
}
for ( int i = 0; i < MAX_VIEWMODELS; i++ )
{
CBaseViewModel *pViewModel = GetViewModel( i );
if ( !pViewModel )
continue;
pViewModel->m_vecLastFacing = pEnteredPortal->m_matrixThisToLinked.ApplyRotation( pViewModel->m_vecLastFacing );
}
#if ( PLAYERPORTALDEBUGSPEW == 1 )
Warning( "C_Portal_Player::ApplyUnpredictedPortalTeleportation( %f )\n", flTeleportationTime/*gpGlobals->curtime*/ );
#endif
PostTeleportationCameraFixup( pEnteredPortal );
if( IsToolRecording() )
{
KeyValues *msg = new KeyValues( "entity_nointerp" );
// Post a message back to all IToolSystems
Assert( (int)GetToolHandle() != 0 );
ToolFramework_PostToolMessage( GetToolHandle(), msg );
msg->deleteThis();
}
// Use a slightly expanded box to search for stick surfaces as the player leaves the portal.
m_flUsePostTeleportationBoxTime = sv_post_teleportation_box_time.GetFloat();
SetOldPlayerZ( GetNetworkOrigin().z );
}
void C_Portal_Player::ApplyPredictedPortalTeleportation( C_Portal_Base2D *pEnteredPortal, CMoveData *pMove, bool bForcedDuck )
{
if( pEnteredPortal->m_hLinkedPortal.Get() != NULL )
{
m_matLatestServerTeleportationInverseMatrix = pEnteredPortal->m_hLinkedPortal->MatrixThisToLinked();
}
else
{
m_matLatestServerTeleportationInverseMatrix.Identity();
}
m_fLatestServerTeleport = gpGlobals->curtime;
C_PortalGhostRenderable *pGhost = pEnteredPortal->GetGhostRenderableForEntity( this );
if( !pGhost )
{
//high velocity edge case. Entity portalled before it ever created a clone. But will need one for the interpolated origin history
if( C_PortalGhostRenderable::ShouldCloneEntity( this, pEnteredPortal, false ) )
{
pGhost = C_PortalGhostRenderable::CreateGhostRenderable( this, pEnteredPortal );
Assert( !pEnteredPortal->m_hGhostingEntities.IsValidIndex( pEnteredPortal->m_hGhostingEntities.Find( this ) ) );
pEnteredPortal->m_hGhostingEntities.AddToTail( this );
Assert( pEnteredPortal->m_GhostRenderables.IsValidIndex( pEnteredPortal->m_GhostRenderables.Find( pGhost ) ) );
pGhost->PerFrameUpdate();
}
}
if( pGhost )
{
C_PortalGhostRenderable::CreateInversion( pGhost, pEnteredPortal, gpGlobals->curtime );
}
//Warning( "C_Portal_Player::ApplyPredictedPortalTeleportation() ent:%i slot:%i\n", entindex(), engine->GetActiveSplitScreenPlayerSlot() );
ApplyTransformToInterpolators( pEnteredPortal->m_matrixThisToLinked, gpGlobals->curtime, false, bForcedDuck );
// straighten out velocity if going nearly straight up/down out of a floor/ceiling portal
{
const CPortal_Base2D *pOtherPortal = pEnteredPortal->m_hLinkedPortal.Get();
if( sv_player_funnel_into_portals.GetBool() && pOtherPortal )
{
// Make sure this portal is nearly facing straight up/down
const Vector vNormal = pOtherPortal->m_PortalSimulator.GetInternalData().Placement.vForward;
if( (1.f - fabs(vNormal.z)) < 0.001f )
{
const Vector vUp(0.f,0.f,1.f);
const Vector vVel = pMove->m_vecVelocity;
const float flVelDotUp = DotProduct( vVel.Normalized(), vUp );
// We're going mostly straight up/down
if( fabs( flVelDotUp ) > sv_player_funnel_gimme_dot.GetFloat() )
{
// Make us go exactly sraight up/down
pMove->m_vecVelocity = ( DotProduct(vUp, vVel) * vUp );
}
}
}
}
PostTeleportationCameraFixup( pEnteredPortal );
if( prediction->IsFirstTimePredicted() && IsToolRecording() )
{
KeyValues *msg = new KeyValues( "entity_nointerp" );
// Post a message back to all IToolSystems
Assert( (int)GetToolHandle() != 0 );
ToolFramework_PostToolMessage( GetToolHandle(), msg );
msg->deleteThis();
}
// Use a slightly expanded box to search for stick surfaces as the player leaves the portal.
m_flUsePostTeleportationBoxTime = sv_post_teleportation_box_time.GetFloat();
if ( m_bFlingTrailPrePortalled )
{
m_bFlingTrailPrePortalled = false;
m_bFlingTrailJustPortalled = true;
}
SetOldPlayerZ( pMove->GetAbsOrigin().z );
}
void C_Portal_Player::UndoPredictedPortalTeleportation( const C_Portal_Base2D *pEnteredPortal, float fOriginallyAppliedTime, const VMatrix &matUndo, bool bForcedDuck )
{
ApplyTransformToInterpolators( matUndo, fOriginallyAppliedTime, true, bForcedDuck );
// Don't use the expanded box to search for stick surfaces, since the player hasn't teleported yet.
m_flUsePostTeleportationBoxTime = 0.0f;
SetOldPlayerZ( GetNetworkOrigin().z );
}
void C_Portal_Player::UnrollPredictedTeleportations( int iCommandNumber )
{
//roll back changes that aren't automatically restored when rolling back prediction time
//ACTIVE_SPLITSCREEN_PLAYER_GUARD( this );
if( (m_PredictedPortalTeleportations.Count() != 0) && (iCommandNumber <= m_PredictedPortalTeleportations.Tail().iCommandNumber) )
{
matrix3x4_t matAngleTransformIn, matAngleTransformOut; //temps for angle transformation
QAngle qEngineViewAngles;
engine->GetViewAngles( qEngineViewAngles );
//QAngle qVAngles = player->pl.v_angle;
//crap, re-predicting teleportations. This is fine for the CMoveData, but CUserCmd/engine view angles are temporally sensitive.
for( int i = m_PredictedPortalTeleportations.Count(); --i >= 0; )
{
if( iCommandNumber <= m_PredictedPortalTeleportations[i].iCommandNumber )
{
const VMatrix &matTransform = m_PredictedPortalTeleportations[i].matUnroll;
//undo the view transformation this previous (but future) teleportation applied to the view angles.
{
AngleMatrix( qEngineViewAngles, matAngleTransformIn );
ConcatTransforms( matTransform.As3x4(), matAngleTransformIn, matAngleTransformOut );
MatrixAngles( matAngleTransformOut, qEngineViewAngles );
}
/*{
AngleMatrix( qVAngles, matAngleTransformIn );
ConcatTransforms( matTransform.As3x4(), matAngleTransformIn, matAngleTransformOut );
MatrixAngles( matAngleTransformOut, qVAngles );
}*/
UndoPredictedPortalTeleportation( m_PredictedPortalTeleportations[i].pEnteredPortal, m_PredictedPortalTeleportations[i].flTime, matTransform, m_PredictedPortalTeleportations[i].bDuckForced );
// Hack? Squash camera values back to neutral.
m_PortalLocal.m_Up = Vector(0,0,1);
m_PortalLocal.m_qQuaternionPunch.Init(0,0,0);
m_PortalLocal.m_vEyeOffset = vec3_origin;
#if ( PLAYERPORTALDEBUGSPEW == 1 )
Warning( "<--Rolling back predicted teleportation %d, %f %i %i\n", m_PredictedPortalTeleportations.Count(), m_PredictedPortalTeleportations[i].flTime, m_PredictedPortalTeleportations[i].iCommandNumber, iCommandNumber );
#endif
m_PredictedPortalTeleportations.FastRemove( i );
}
else
{
break;
}
}
if( IsLocalPlayer() )
{
engine->SetViewAngles( qEngineViewAngles );
//Warning( "C_Portal_Player::UnrollPredictedTeleportations() ent:%i slot:%i\n", entindex(), engine->GetActiveSplitScreenPlayerSlot() );
}
//player->pl.v_angle = qVAngles;
}
}
void C_Portal_Player::Precache( void )
{
// load the bot textures in co-op
if ( g_pGameRules->IsMultiplayer() )
{
PrecacheParticleSystem( "coop_robot_talk_blue" );
PrecacheParticleSystem( "coop_robot_talk_orange" );
PrecacheParticleSystem( "electrical_arc_01" );
PrecacheParticleSystem( "bot_death_B_gib" );
PrecacheParticleSystem( "bot_fling_trail_rainbow" );
PrecacheParticleSystem( "bot_fling_trail" );
}
}
//////////////////////////////////////////////////////////////////////////
// PAINT SECTION
//////////////////////////////////////////////////////////////////////////
bool C_Portal_Player::RenderLocalScreenSpaceEffect( PortalScreenSpaceEffect effect, IMatRenderContext *pRenderContext, int x, int y, int w, int h )
{
C_Portal_Player *pLocalPlayer = C_Portal_Player::GetLocalPlayer();
if( pLocalPlayer )
{
return pLocalPlayer->RenderScreenSpaceEffect( effect, pRenderContext, x, y, w, h );
}
return false;
}
bool C_Portal_Player::RenderScreenSpaceEffect( PortalScreenSpaceEffect effect, IMatRenderContext *pRenderContext, int x, int y, int w, int h )
{
bool result = false;
switch( effect )
{
case PAINT_SCREEN_SPACE_EFFECT:
result = RenderScreenSpacePaintEffect( pRenderContext );
break;
}
return result;
}
static void SetRenderTargetAndViewPort(ITexture *rt)
{
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->SetRenderTarget(rt);
if ( rt )
{
pRenderContext->Viewport(0,0,rt->GetActualWidth(),rt->GetActualHeight());
}
}
bool C_Portal_Player::ScreenSpacePaintEffectIsActive() const
{
// The reference is valid and the referenced particle system is also valid
return m_PaintScreenSpaceEffect.IsValid() && m_PaintScreenSpaceEffect->IsValid();
}
void C_Portal_Player::SetScreenSpacePaintEffectColors( IMaterialVar* pColor1, IMaterialVar* pColor2 ) const
{
const Color visualColor = MapPowerToVisualColor( m_PortalLocal.m_PaintedPowerType );
Vector vColor1( visualColor.r(), visualColor.g(), visualColor.b() );
Vector vColor2 = vColor1;
for( unsigned i = 0; i < 3; ++i )
{
vColor2[i] = clamp( vColor2[i] - 15.0f, 0, 255 );
}
vColor1.NormalizeInPlace();
vColor2.NormalizeInPlace();
pColor1->SetVecValue( vColor1.x, vColor1.y, vColor1.z );
pColor2->SetVecValue( vColor2.x, vColor2.y, vColor2.z );
}
bool C_Portal_Player::RenderScreenSpacePaintEffect( IMatRenderContext *pRenderContext )
{
if( ScreenSpacePaintEffectIsActive() )
{
pRenderContext->PushRenderTargetAndViewport();
ITexture* pDestRenderTarget = materials->FindTexture( "_rt_SmallFB1", TEXTURE_GROUP_RENDER_TARGET );
SetRenderTargetAndViewPort( pDestRenderTarget );
pRenderContext->ClearColor4ub( 128, 128, 0, 0 );
pRenderContext->ClearBuffers( true, false, false );
RenderableInstance_t instance;
instance.m_nAlpha = 255;
m_PaintScreenSpaceEffect->DrawModel( 1, instance );
if( IsGameConsole() )
{
pRenderContext->CopyRenderTargetToTextureEx( pDestRenderTarget, 0, NULL, NULL );
}
pRenderContext->PopRenderTargetAndViewport();
return true;
}
return false;
}
void C_Portal_Player::InvalidatePaintEffects()
{
// Remove paint screen space effect
if( m_PaintScreenSpaceEffect.IsValid() )
{
m_PaintScreenSpaceEffect->StopEmission();
STEAMWORKS_TESTSECRETALWAYS();
m_PaintScreenSpaceEffect = NULL;
}
// commenting out the 3rd person drop effect
/*
// Remove paint drip effect
if( m_PaintDripEffect.IsValid() )
{
m_PaintDripEffect->StopEmission();
m_PaintDripEffect = NULL;
}
*/
}
void C_Portal_Player::ClientPlayerRespawn()
{
if ( IsLocalPlayer( this ) )
{
m_bGibbed = false;
// Dod called these, not sure why
//MoveToLastReceivedPosition( true );
//ResetLatched();
// Reset the camera.
m_bWasTaunting = false;
if ( IsLocalPlayer( this ) )
{
ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
g_ThirdPersonManager.SetOverridingThirdPerson( false );
if ( g_ThirdPersonManager.WantToUseGameThirdPerson() == false )
{
input->CAM_ToFirstPerson();
ThirdPersonSwitch( false );
input->CAM_SetCameraThirdData( NULL, vec3_angle );
g_ThirdPersonManager.UseCameraOffsets( false );
}
m_bTauntInterpolating = false;
m_bTauntInterpolatingAngles = false;
}
//ResetToneMapping(1.0);
//// Release the duck toggle key (
//{
// ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( this );
// KeyUp( &in_ducktoggle, NULL );
//}
//IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_respawn" );
//if ( event )
//{
// gameeventmanager->FireEventClientSide( event );
//}
RANDOM_CEG_TEST_SECRET();
CPortalMPGameRules *pRules = PortalMPGameRules();
// we want to force the screen to not be split on the credits, but we want to keep the other player connected
if ( pRules && pRules->IsCreditsMap() )
{
engine->ClientCmd( "ss_force_primary_fullscreen 1" );
}
else // otherwise, we don't want this to be the case
{
engine->ClientCmd( "ss_force_primary_fullscreen 0" );
}
}
// clear animation state
m_PlayerAnimState->ClearAnimationState();
#if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
UpdateInventory();
UpdateClientsideWearables();
#endif
}
// Override the default fov so that the fov from the splitscreen_config.txt is the one used.
// FIXME: Need to make other splitscreen-capable games use this method.
// It doesn't currently support user-controllable FOV overrides though like TF2, so only using this for L4D at the moment.
int C_Portal_Player::GetDefaultFOV( void ) const
{
return cl_fov.GetFloat();
}
#if !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
//-----------------------------------------------------------------------------
// Purpose: Request this player's inventories from the steam backend
//-----------------------------------------------------------------------------
void C_Portal_Player::UpdateInventory( void )
{
if ( !g_pGameRules->IsMultiplayer() )
return;
if ( !m_bInventoryReceived )
{
CSteamID steamIDForPlayer;
if ( GetSteamID( &steamIDForPlayer ) )
{
PortalInventoryManager()->SteamRequestInventory( &m_Inventory, steamIDForPlayer, this );
}
// If we have an SOCache, we've got a connection to the GC
bool bInvalid = true;
if ( m_Inventory.GetSOC() )
{
bInvalid = (m_Inventory.GetSOC()->BIsInitialized() == false);
}
m_bInventoryReceived = !bInvalid;
}
}
// Our inventory has changed. Make sure we're wearing the right things.
void C_Portal_Player::InventoryUpdated( CPlayerInventory *pInventory )
{
UpdateClientsideWearables();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool C_Portal_Player::ItemsMatch( CEconItemView *pCurItem, CEconItemView *pNewItem )
{
if ( !pNewItem || !pNewItem->IsValid() )
return false;
// If we already have an item in this slot but is not the same type, nuke it (changed classes)
// We don't need to do this for non-base items because they've already been verified above.
bool bHasNonBase = pNewItem ? pNewItem->GetItemQuality() != AE_NORMAL : false;
if ( bHasNonBase )
{
// If the item isn't the one we're supposed to have, nuke it
if ( pCurItem->GetItemID() != pNewItem->GetItemID() )
{
/*
Msg("Removing %s because its global index (%d) doesn't match the loadout's (%d)\n", p->GetDebugName(),
pCurItem->GetItemID(),
pNewItem->GetItemID() );
*/
return false;
}
}
else
{
if ( pCurItem->GetItemQuality() != AE_NORMAL || (pCurItem->GetItemIndex() != pNewItem->GetItemIndex()) )
{
//Msg("Removing %s because it's not the right type for the class.\n", p->GetDebugName() );
return false;
}
}
return true;
}
void C_Portal_Player::UpdateClientsideWearables( void )
{
if ( !g_pGameRules->IsMultiplayer() )
return;
C_Portal_Player *pHostPlayer = this;
if ( IsSplitScreenPlayer() )
{
pHostPlayer = ToPortalPlayer( C_BasePlayer::GetLocalPlayer( 0 ) );
if ( !pHostPlayer )
{
return;
}
}
// Crap, what do we do about bodygroups? They're server authoritive.
/*
// First, reset all our bodygroups
CStudioHdr studioHdr( GetStudioHdr(), g_pMDLCache );
int iBodyGroup = FindBodygroupByName( &studioHdr, "hat" );
if ( iBodyGroup > -1 )
{
::SetBodygroup( &studioHdr, m_nBody, iBodyGroup, 0 );
}
iBodyGroup = FindBodygroupByName( &studioHdr, "headphones" );
if ( iBodyGroup > -1 )
{
::SetBodygroup( &studioHdr, m_nBody, iBodyGroup, 0 );
}
SetBody( m_nBody );
*/
CSteamID steamIDForPlayer;
pHostPlayer->GetSteamID( &steamIDForPlayer );
int iBot = ( GetTeamNumber() == TEAM_BLUE ) ? P2BOT_ATLAS : P2BOT_PBODY;
// Go through our wearables and make sure we're supposed to be wearing them.
// Need to move backwards because we'll be removing them as we find them.
for ( int wbl = GetNumWearables()-1; wbl >= 0; wbl-- )
{
CEconWearable *pWearable = GetWearable(wbl);
Assert( pWearable );
if ( !pWearable )
continue;
int iLoadoutSlot = pWearable->GetAttributeContainer()->GetItem()->GetStaticData()->GetLoadoutSlot( iBot );
CEconItemView *pItem = PortalInventoryManager()->GetItemInLoadoutForClass( iBot, iLoadoutSlot, &steamIDForPlayer );
if ( !ItemsMatch( pWearable->GetAttributeContainer()->GetItem(), pItem ) || (pWearable->GetTeamNumber() != GetTeamNumber()) )
{
if ( !pWearable->AlwaysAllow() )
{
// We shouldn't have this wearable. Remove it.
RemoveWearable( pWearable );
}
}
}
// Now go through all our loadout slots and find any wearables that we should be wearing.
for ( int i = 0; i < LOADOUT_POSITION_COUNT; i++ )
{
m_EquippedLoadoutItemIndices[i] = LOADOUT_SLOT_USE_BASE_ITEM;
CEconItemView *pItem = pHostPlayer->Inventory()->GetItemInLoadout( iBot, i );
if ( !pItem || !pItem->IsValid() )
continue;
m_EquippedLoadoutItemIndices[i] = pItem->GetItemID();
// See if we're already wearing it.
bool bAlreadyHave = false;
for ( int wbl = 0; wbl < GetNumWearables(); wbl++ )
{
C_EconWearable *pWearable = GetWearable(wbl);
if ( !pWearable )
continue;
if ( ItemsMatch( pWearable->GetAttributeContainer()->GetItem(), pItem ) )
{
bAlreadyHave = true;
break;
}
}
if ( !bAlreadyHave )
{
// Only spawn wearables
if ( !Q_strcmp( pItem->GetStaticData()->GetItemClass(), "wearable_item" ) )
{
CEconWearable *pNewItem = new CEconWearable();
// Initialize with no model, because Spawn() will set it to the item's model afterwards.
if ( pNewItem->InitializeAsClientEntity( NULL, RENDER_GROUP_OPAQUE ) )
{
pNewItem->GetAttributeContainer()->SetItem( pItem );
pNewItem->Spawn();
pNewItem->GiveTo( this );
}
else
{
delete pNewItem;
m_EquippedLoadoutItemIndices[i] = LOADOUT_SLOT_USE_BASE_ITEM;
}
}
}
}
for ( int i = 0; i < GetSplitScreenPlayers().Count(); ++i )
{
C_Portal_Player *pPlayer = static_cast< C_Portal_Player* >( GetSplitScreenPlayers()[ i ].Get() );
if ( pPlayer )
{
pPlayer->UpdateClientsideWearables();
}
}
}
void C_Portal_Player::RemoveClientsideWearables( void )
{
if ( !g_pGameRules->IsMultiplayer() )
return;
// Need to move backwards because we'll be removing them as we find them.
for ( int wbl = GetNumWearables()-1; wbl >= 0; wbl-- )
{
CEconWearable *pWearable = GetWearable(wbl);
if ( !pWearable )
continue;
RemoveWearable( pWearable );
}
for ( int i = 0; i < GetSplitScreenPlayers().Count(); ++i )
{
C_Portal_Player *pPlayer = static_cast< C_Portal_Player* >( GetSplitScreenPlayers()[ i ].Get() );
if ( pPlayer )
{
pPlayer->RemoveClientsideWearables();
}
}
}
#endif //!defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )