summaryrefslogtreecommitdiff
path: root/game/server/portal/portal_player.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/portal/portal_player.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/portal/portal_player.cpp')
-rw-r--r--game/server/portal/portal_player.cpp2333
1 files changed, 2333 insertions, 0 deletions
diff --git a/game/server/portal/portal_player.cpp b/game/server/portal/portal_player.cpp
new file mode 100644
index 0000000..72cc063
--- /dev/null
+++ b/game/server/portal/portal_player.cpp
@@ -0,0 +1,2333 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Player for Portal.
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "portal_player.h"
+#include "globalstate.h"
+#include "trains.h"
+#include "game.h"
+#include "portal_player_shared.h"
+#include "predicted_viewmodel.h"
+#include "in_buttons.h"
+#include "portal_gamerules.h"
+#include "weapon_portalgun.h"
+#include "portal/weapon_physcannon.h"
+#include "KeyValues.h"
+#include "team.h"
+#include "eventqueue.h"
+#include "weapon_portalbase.h"
+#include "engine/IEngineSound.h"
+#include "ai_basenpc.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "prop_portal_shared.h"
+#include "player_pickup.h" // for player pickup code
+#include "vphysics/player_controller.h"
+#include "datacache/imdlcache.h"
+#include "bone_setup.h"
+#include "portal_gamestats.h"
+#include "physicsshadowclone.h"
+#include "physics_prop_ragdoll.h"
+#include "soundenvelope.h"
+#include "ai_speech.h" // For expressors, vcd playing
+#include "sceneentity.h" // has the VCD precache function
+
+// Max mass the player can lift with +use
+#define PORTAL_PLAYER_MAX_LIFT_MASS 85
+#define PORTAL_PLAYER_MAX_LIFT_SIZE 128
+
+extern CBaseEntity *g_pLastSpawn;
+
+extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse);
+
+
+// -------------------------------------------------------------------------------- //
+// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
+// -------------------------------------------------------------------------------- //
+
+class CTEPlayerAnimEvent : public CBaseTempEntity
+{
+public:
+ DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
+ DECLARE_SERVERCLASS();
+
+ CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
+ {
+ }
+
+ CNetworkHandle( CBasePlayer, m_hPlayer );
+ CNetworkVar( int, m_iEvent );
+ CNetworkVar( int, m_nData );
+};
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
+SendPropEHandle( SENDINFO( m_hPlayer ) ),
+SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
+SendPropInt( SENDINFO( m_nData ), 32 ),
+END_SEND_TABLE()
+
+static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
+
+void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
+{
+ CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );
+
+ g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
+ g_TEPlayerAnimEvent.m_iEvent = event;
+ g_TEPlayerAnimEvent.m_nData = nData;
+ g_TEPlayerAnimEvent.Create( filter, 0 );
+}
+
+
+
+//=================================================================================
+//
+// Ragdoll Entity
+//
+class CPortalRagdoll : public CBaseAnimatingOverlay, public CDefaultPlayerPickupVPhysics
+{
+public:
+
+ DECLARE_CLASS( CPortalRagdoll, CBaseAnimatingOverlay );
+ DECLARE_SERVERCLASS();
+ DECLARE_DATADESC();
+
+ CPortalRagdoll()
+ {
+ m_hPlayer.Set( NULL );
+ m_vecRagdollOrigin.Init();
+ m_vecRagdollVelocity.Init();
+ }
+
+ // Transmit ragdolls to everyone.
+ virtual int UpdateTransmitState()
+ {
+ return SetTransmitState( FL_EDICT_ALWAYS );
+ }
+
+ // In case the client has the player entity, we transmit the player index.
+ // In case the client doesn't have it, we transmit the player's model index, origin, and angles
+ // so they can create a ragdoll in the right place.
+ CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
+ CNetworkVector( m_vecRagdollVelocity );
+ CNetworkVector( m_vecRagdollOrigin );
+};
+
+LINK_ENTITY_TO_CLASS( portal_ragdoll, CPortalRagdoll );
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE( CPortalRagdoll, DT_PortalRagdoll )
+SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ),
+SendPropEHandle( SENDINFO( m_hPlayer ) ),
+SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
+SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ),
+SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
+SendPropVector( SENDINFO( m_vecRagdollVelocity ) ),
+END_SEND_TABLE()
+
+
+BEGIN_DATADESC( CPortalRagdoll )
+
+ DEFINE_FIELD( m_vecRagdollOrigin, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_vecRagdollVelocity, FIELD_VECTOR ),
+
+END_DATADESC()
+
+
+
+
+
+LINK_ENTITY_TO_CLASS( player, CPortal_Player );
+
+IMPLEMENT_SERVERCLASS_ST(CPortal_Player, DT_Portal_Player)
+SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
+SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
+SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ),
+SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ),
+SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
+SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
+SendPropExclude( "DT_BaseFlex", "m_viewtarget" ),
+SendPropExclude( "DT_BaseFlex", "m_flexWeight" ),
+SendPropExclude( "DT_BaseFlex", "m_blinktoggle" ),
+
+// portal_playeranimstate and clientside animation takes care of these on the client
+SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
+SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
+
+
+SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ),
+SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ),
+SendPropEHandle( SENDINFO( m_hRagdoll ) ),
+SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ),
+SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ),
+SendPropBool( SENDINFO( m_bHeldObjectOnOppositeSideOfPortal) ),
+SendPropEHandle( SENDINFO( m_pHeldObjectPortal ) ),
+SendPropBool( SENDINFO( m_bPitchReorientation ) ),
+SendPropEHandle( SENDINFO( m_hPortalEnvironment ) ),
+SendPropEHandle( SENDINFO( m_hSurroundingLiquidPortal ) ),
+SendPropBool( SENDINFO( m_bSuppressingCrosshair ) ),
+SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
+
+END_SEND_TABLE()
+
+BEGIN_DATADESC( CPortal_Player )
+
+ DEFINE_SOUNDPATCH( m_pWooshSound ),
+
+ DEFINE_FIELD( m_bHeldObjectOnOppositeSideOfPortal, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_pHeldObjectPortal, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bIntersectingPortalPlane, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bStuckOnPortalCollisionObject, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_fTimeLastHurt, FIELD_TIME ),
+ DEFINE_FIELD( m_StatsThisLevel.iNumPortalsPlaced, FIELD_INTEGER ),
+ DEFINE_FIELD( m_StatsThisLevel.iNumStepsTaken, FIELD_INTEGER ),
+ DEFINE_FIELD( m_StatsThisLevel.fNumSecondsTaken, FIELD_FLOAT ),
+ DEFINE_FIELD( m_fTimeLastNumSecondsUpdate, FIELD_TIME ),
+ DEFINE_FIELD( m_iNumCamerasDetatched, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bPitchReorientation, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bIsRegenerating, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_fNeuroToxinDamageTime, FIELD_TIME ),
+ DEFINE_FIELD( m_hPortalEnvironment, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_flExpressionLoopTime, FIELD_TIME ),
+ DEFINE_FIELD( m_iszExpressionScene, FIELD_STRING ),
+ DEFINE_FIELD( m_hExpressionSceneEnt, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_vecTotalBulletForce, FIELD_VECTOR ),
+ DEFINE_FIELD( m_bSilentDropAndPickup, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hRagdoll, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_angEyeAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_iPlayerSoundType, FIELD_INTEGER ),
+ DEFINE_FIELD( m_qPrePortalledViewAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_bFixEyeAnglesFromPortalling, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_matLastPortalled, FIELD_VMATRIX_WORLDSPACE ),
+ DEFINE_FIELD( m_vWorldSpaceCenterHolder, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( m_hSurroundingLiquidPortal, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bSuppressingCrosshair, FIELD_BOOLEAN ),
+ //DEFINE_FIELD ( m_PlayerAnimState, CPortalPlayerAnimState ),
+ //DEFINE_FIELD ( m_StatsThisLevel, PortalPlayerStatistics_t ),
+
+ DEFINE_EMBEDDEDBYREF( m_pExpresser ),
+
+END_DATADESC()
+
+ConVar sv_regeneration_wait_time ("sv_regeneration_wait_time", "1.0", FCVAR_REPLICATED );
+
+const char *g_pszChellModel = "models/player/chell.mdl";
+const char *g_pszPlayerModel = g_pszChellModel;
+
+
+#define MAX_COMBINE_MODELS 4
+#define MODEL_CHANGE_INTERVAL 5.0f
+#define TEAM_CHANGE_INTERVAL 5.0f
+
+#define PORTALPLAYER_PHYSDAMAGE_SCALE 4.0f
+
+extern ConVar sv_turbophysics;
+
+//----------------------------------------------------
+// Player Physics Shadow
+//----------------------------------------------------
+#define VPHYS_MAX_DISTANCE 2.0
+#define VPHYS_MAX_VEL 10
+#define VPHYS_MAX_DISTSQR (VPHYS_MAX_DISTANCE*VPHYS_MAX_DISTANCE)
+#define VPHYS_MAX_VELSQR (VPHYS_MAX_VEL*VPHYS_MAX_VEL)
+
+
+extern float IntervalDistance( float x, float x0, float x1 );
+
+//disable 'this' : used in base member initializer list
+#pragma warning( disable : 4355 )
+
+CPortal_Player::CPortal_Player()
+{
+
+ m_PlayerAnimState = CreatePortalPlayerAnimState( this );
+ CreateExpresser();
+
+ UseClientSideAnimation();
+
+ m_angEyeAngles.Init();
+
+ m_iLastWeaponFireUsercmd = 0;
+
+ m_iSpawnInterpCounter = 0;
+
+ m_bHeldObjectOnOppositeSideOfPortal = false;
+ m_pHeldObjectPortal = 0;
+
+ m_bIntersectingPortalPlane = false;
+
+ m_bPitchReorientation = false;
+
+ m_bSilentDropAndPickup = false;
+
+ m_iszExpressionScene = NULL_STRING;
+ m_hExpressionSceneEnt = NULL;
+ m_flExpressionLoopTime = 0.0f;
+ m_bSuppressingCrosshair = false;
+}
+
+CPortal_Player::~CPortal_Player( void )
+{
+ ClearSceneEvents( NULL, true );
+
+ if ( m_PlayerAnimState )
+ m_PlayerAnimState->Release();
+
+ CPortalRagdoll *pRagdoll = dynamic_cast<CPortalRagdoll*>( m_hRagdoll.Get() );
+ if( pRagdoll )
+ {
+ UTIL_Remove( pRagdoll );
+ }
+}
+
+void CPortal_Player::UpdateOnRemove( void )
+{
+ BaseClass::UpdateOnRemove();
+}
+
+void CPortal_Player::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "PortalPlayer.EnterPortal" );
+ PrecacheScriptSound( "PortalPlayer.ExitPortal" );
+
+ PrecacheScriptSound( "PortalPlayer.Woosh" );
+ PrecacheScriptSound( "PortalPlayer.FallRecover" );
+
+ PrecacheModel ( "sprites/glow01.vmt" );
+
+ //Precache Citizen models
+ PrecacheModel( g_pszPlayerModel );
+ PrecacheModel( g_pszChellModel );
+
+ PrecacheScriptSound( "NPC_Citizen.die" );
+}
+
+void CPortal_Player::CreateSounds()
+{
+ if ( !m_pWooshSound )
+ {
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ CPASAttenuationFilter filter( this );
+
+ m_pWooshSound = controller.SoundCreate( filter, entindex(), "PortalPlayer.Woosh" );
+ controller.Play( m_pWooshSound, 0, 100 );
+ }
+}
+
+void CPortal_Player::StopLoopingSounds()
+{
+ if ( m_pWooshSound )
+ {
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ controller.SoundDestroy( m_pWooshSound );
+ m_pWooshSound = NULL;
+ }
+
+ BaseClass::StopLoopingSounds();
+}
+
+void CPortal_Player::GiveAllItems( void )
+{
+ EquipSuit();
+
+ CBasePlayer::GiveAmmo( 255, "Pistol");
+ CBasePlayer::GiveAmmo( 32, "357" );
+
+ CBasePlayer::GiveAmmo( 255, "AR2" );
+ CBasePlayer::GiveAmmo( 3, "AR2AltFire" );
+ CBasePlayer::GiveAmmo( 255, "SMG1");
+ CBasePlayer::GiveAmmo( 3, "smg1_grenade");
+
+ CBasePlayer::GiveAmmo( 255, "Buckshot");
+ CBasePlayer::GiveAmmo( 16, "XBowBolt" );
+
+ CBasePlayer::GiveAmmo( 3, "rpg_round");
+ CBasePlayer::GiveAmmo( 6, "grenade" );
+
+ GiveNamedItem( "weapon_crowbar" );
+ GiveNamedItem( "weapon_physcannon" );
+
+ GiveNamedItem( "weapon_pistol" );
+ GiveNamedItem( "weapon_357" );
+
+ GiveNamedItem( "weapon_smg1" );
+ GiveNamedItem( "weapon_ar2" );
+
+ GiveNamedItem( "weapon_shotgun" );
+ GiveNamedItem( "weapon_crossbow" );
+
+ GiveNamedItem( "weapon_rpg" );
+ GiveNamedItem( "weapon_frag" );
+
+ GiveNamedItem( "weapon_bugbait" );
+
+ //GiveNamedItem( "weapon_physcannon" );
+ CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( GiveNamedItem( "weapon_portalgun" ) );
+
+ if ( !pPortalGun )
+ {
+ pPortalGun = static_cast<CWeaponPortalgun*>( Weapon_OwnsThisType( "weapon_portalgun" ) );
+ }
+
+ if ( pPortalGun )
+ {
+ pPortalGun->SetCanFirePortal1();
+ pPortalGun->SetCanFirePortal2();
+ }
+}
+
+void CPortal_Player::GiveDefaultItems( void )
+{
+ castable_string_t st( "suit_no_sprint" );
+ GlobalEntity_SetState( st, GLOBAL_OFF );
+ inputdata_t in;
+ InputDisableFlashlight( in );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets specific defaults.
+//-----------------------------------------------------------------------------
+void CPortal_Player::Spawn(void)
+{
+ SetPlayerModel();
+
+ BaseClass::Spawn();
+
+ CreateSounds();
+
+ pl.deadflag = false;
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+
+ RemoveEffects( EF_NODRAW );
+ StopObserverMode();
+
+ GiveDefaultItems();
+
+ m_nRenderFX = kRenderNormal;
+
+ m_Local.m_iHideHUD = 0;
+
+ AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round.
+
+ m_impactEnergyScale = PORTALPLAYER_PHYSDAMAGE_SCALE;
+
+ RemoveFlag( FL_FROZEN );
+
+ m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8;
+
+ m_Local.m_bDucked = false;
+
+ SetPlayerUnderwater(false);
+
+#ifdef PORTAL_MP
+ PickTeam();
+#endif
+}
+
+void CPortal_Player::Activate( void )
+{
+ BaseClass::Activate();
+ m_fTimeLastNumSecondsUpdate = gpGlobals->curtime;
+}
+
+void CPortal_Player::NotifySystemEvent(CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
+{
+ // On teleport, we send event for tracking fling achievements
+ if ( eventType == NOTIFY_EVENT_TELEPORT )
+ {
+ CProp_Portal *pEnteredPortal = dynamic_cast<CProp_Portal*>( pNotify );
+ IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_portaled" );
+ if ( event )
+ {
+ event->SetInt( "userid", GetUserID() );
+ event->SetBool( "portal2", pEnteredPortal->m_bIsPortal2 );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ BaseClass::NotifySystemEvent( pNotify, eventType, params );
+}
+
+void CPortal_Player::OnRestore( void )
+{
+ BaseClass::OnRestore();
+ if ( m_pExpresser )
+ {
+ m_pExpresser->SetOuter ( this );
+ }
+}
+
+//bool CPortal_Player::StartObserverMode( int mode )
+//{
+// //Do nothing.
+//
+// return false;
+//}
+
+bool CPortal_Player::ValidatePlayerModel( const char *pModel )
+{
+ if ( !Q_stricmp( g_pszPlayerModel, pModel ) )
+ {
+ return true;
+ }
+
+ if ( !Q_stricmp( g_pszChellModel, pModel ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void CPortal_Player::SetPlayerModel( void )
+{
+ const char *szModelName = NULL;
+ const char *pszCurrentModelName = modelinfo->GetModelName( GetModel());
+
+ szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" );
+
+ if ( ValidatePlayerModel( szModelName ) == false )
+ {
+ char szReturnString[512];
+
+ if ( ValidatePlayerModel( pszCurrentModelName ) == false )
+ {
+ pszCurrentModelName = g_pszPlayerModel;
+ }
+
+ Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName );
+ engine->ClientCommand ( edict(), szReturnString );
+
+ szModelName = pszCurrentModelName;
+ }
+
+ int modelIndex = modelinfo->GetModelIndex( szModelName );
+
+ if ( modelIndex == -1 )
+ {
+ szModelName = g_pszPlayerModel;
+
+ char szReturnString[512];
+
+ Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName );
+ engine->ClientCommand ( edict(), szReturnString );
+ }
+
+ SetModel( szModelName );
+ m_iPlayerSoundType = (int)PLAYER_SOUNDS_CITIZEN;
+}
+
+
+bool CPortal_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex )
+{
+ bool bRet = BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
+
+ return bRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPortal_Player::UpdateExpression( void )
+{
+ if ( !m_pExpresser )
+ return;
+
+ int iConcept = CONCEPT_CHELL_IDLE;
+ if ( GetHealth() <= 0 )
+ {
+ iConcept = CONCEPT_CHELL_DEAD;
+ }
+
+ GetExpresser()->SetOuter( this );
+
+ ClearExpression();
+ AI_Response response;
+ bool result = SpeakFindResponse( response, g_pszChellConcepts[iConcept] );
+ if ( !result )
+ {
+ m_flExpressionLoopTime = gpGlobals->curtime + RandomFloat(30,40);
+ return;
+ }
+
+ char const *szScene = response.GetResponsePtr();
+
+ // Ignore updates that choose the same scene
+ if ( m_iszExpressionScene != NULL_STRING && stricmp( STRING(m_iszExpressionScene), szScene ) == 0 )
+ return;
+
+ if ( m_hExpressionSceneEnt )
+ {
+ ClearExpression();
+ }
+
+ m_iszExpressionScene = AllocPooledString( szScene );
+ float flDuration = InstancedScriptedScene( this, szScene, &m_hExpressionSceneEnt, 0.0, true, NULL );
+ m_flExpressionLoopTime = gpGlobals->curtime + flDuration;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPortal_Player::ClearExpression( void )
+{
+ if ( m_hExpressionSceneEnt != NULL )
+ {
+ StopScriptedScene( this, m_hExpressionSceneEnt );
+ }
+ m_flExpressionLoopTime = gpGlobals->curtime;
+}
+
+
+void CPortal_Player::PreThink( void )
+{
+ QAngle vOldAngles = GetLocalAngles();
+ QAngle vTempAngles = GetLocalAngles();
+
+ vTempAngles = EyeAngles();
+
+ if ( vTempAngles[PITCH] > 180.0f )
+ {
+ vTempAngles[PITCH] -= 360.0f;
+ }
+
+ SetLocalAngles( vTempAngles );
+
+ BaseClass::PreThink();
+
+ if( (m_afButtonPressed & IN_JUMP) )
+ {
+ Jump();
+ }
+
+ //Reset bullet force accumulator, only lasts one frame
+ m_vecTotalBulletForce = vec3_origin;
+
+ SetLocalAngles( vOldAngles );
+}
+
+void CPortal_Player::PostThink( void )
+{
+ BaseClass::PostThink();
+
+ // Store the eye angles pitch so the client can compute its animation state correctly.
+ m_angEyeAngles = EyeAngles();
+
+ QAngle angles = GetLocalAngles();
+ angles[PITCH] = 0;
+ SetLocalAngles( angles );
+
+ // Regenerate heath after 3 seconds
+ if ( IsAlive() && GetHealth() < GetMaxHealth() )
+ {
+ // Color to overlay on the screen while the player is taking damage
+ color32 hurtScreenOverlay = {64,0,0,64};
+
+ if ( gpGlobals->curtime > m_fTimeLastHurt + sv_regeneration_wait_time.GetFloat() )
+ {
+ TakeHealth( 1, DMG_GENERIC );
+ m_bIsRegenerating = true;
+
+ if ( GetHealth() >= GetMaxHealth() )
+ {
+ m_bIsRegenerating = false;
+ }
+ }
+ else
+ {
+ m_bIsRegenerating = false;
+ UTIL_ScreenFade( this, hurtScreenOverlay, 1.0f, 0.1f, FFADE_IN|FFADE_PURGE );
+ }
+ }
+
+ UpdatePortalPlaneSounds();
+ UpdateWooshSounds();
+
+ m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
+
+ if ( IsAlive() && m_flExpressionLoopTime >= 0 && gpGlobals->curtime > m_flExpressionLoopTime )
+ {
+ // Random expressions need to be cleared, because they don't loop. So if we
+ // pick the same one again, we want to restart it.
+ ClearExpression();
+ m_iszExpressionScene = NULL_STRING;
+ UpdateExpression();
+ }
+
+ UpdateSecondsTaken();
+
+ // Try to fix the player if they're stuck
+ if ( m_bStuckOnPortalCollisionObject )
+ {
+ Vector vForward = ((CProp_Portal*)m_hPortalEnvironment.Get())->m_vPrevForward;
+ Vector vNewPos = GetAbsOrigin() + vForward * gpGlobals->frametime * -1000.0f;
+ Teleport( &vNewPos, NULL, &vForward );
+ m_bStuckOnPortalCollisionObject = false;
+ }
+}
+
+void CPortal_Player::PlayerDeathThink(void)
+{
+ float flForward;
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if (GetFlags() & FL_ONGROUND)
+ {
+ flForward = GetAbsVelocity().Length() - 20;
+ if (flForward <= 0)
+ {
+ SetAbsVelocity( vec3_origin );
+ }
+ else
+ {
+ Vector vecNewVelocity = GetAbsVelocity();
+ VectorNormalize( vecNewVelocity );
+ vecNewVelocity *= flForward;
+ SetAbsVelocity( vecNewVelocity );
+ }
+ }
+
+ if ( HasWeapons() )
+ {
+ // we drop the guns here because weapons that have an area effect and can kill their user
+ // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
+ // player class sometimes is freed. It's safer to manipulate the weapons once we know
+ // we aren't calling into any of their code anymore through the player pointer.
+ PackDeadPlayerItems();
+ }
+
+ if (GetModelIndex() && (!IsSequenceFinished()) && (m_lifeState == LIFE_DYING))
+ {
+ StudioFrameAdvance( );
+
+ m_iRespawnFrames++;
+ if ( m_iRespawnFrames < 60 ) // animations should be no longer than this
+ return;
+ }
+
+ if (m_lifeState == LIFE_DYING)
+ m_lifeState = LIFE_DEAD;
+
+ StopAnimation();
+
+ IncrementInterpolationFrame();
+ m_flPlaybackRate = 0.0;
+
+ int fAnyButtonDown = (m_nButtons & ~IN_SCORE);
+
+ // Strip out the duck key from this check if it's toggled
+ if ( (fAnyButtonDown & IN_DUCK) && GetToggledDuckState())
+ {
+ fAnyButtonDown &= ~IN_DUCK;
+ }
+
+ // wait for all buttons released
+ if ( m_lifeState == LIFE_DEAD )
+ {
+ if ( fAnyButtonDown || gpGlobals->curtime < m_flDeathTime + DEATH_ANIMATION_TIME )
+ return;
+
+ if ( g_pGameRules->FPlayerCanRespawn( this ) )
+ {
+ m_lifeState = LIFE_RESPAWNABLE;
+ }
+
+ return;
+ }
+
+ // if the player has been dead for one second longer than allowed by forcerespawn,
+ // forcerespawn isn't on. Send the player off to an intermission camera until they
+ // choose to respawn.
+ if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->curtime > (m_flDeathTime + DEATH_ANIMATION_TIME) ) && !IsObserver() )
+ {
+ // go to dead camera.
+ StartObserverMode( m_iObserverLastMode );
+ }
+
+ // wait for any button down, or mp_forcerespawn is set and the respawn time is up
+ if (!fAnyButtonDown
+ && !( g_pGameRules->IsMultiplayer() && forcerespawn.GetInt() > 0 && (gpGlobals->curtime > (m_flDeathTime + 5))) )
+ return;
+
+ m_nButtons = 0;
+ m_iRespawnFrames = 0;
+
+ //Msg( "Respawn\n");
+
+ respawn( this, !IsObserver() );// don't copy a corpse if we're in deathcam.
+ SetNextThink( TICK_NEVER_THINK );
+}
+
+void CPortal_Player::UpdatePortalPlaneSounds( void )
+{
+ CProp_Portal *pPortal = m_hPortalEnvironment;
+ if ( pPortal && pPortal->m_bActivated )
+ {
+ Vector vVelocity;
+ GetVelocity( &vVelocity, NULL );
+
+ if ( !vVelocity.IsZero() )
+ {
+ Vector vMin, vMax;
+ CollisionProp()->WorldSpaceAABB( &vMin, &vMax );
+
+ Vector vEarCenter = ( vMax + vMin ) / 2.0f;
+ Vector vDiagonal = vMax - vMin;
+
+ if ( !m_bIntersectingPortalPlane )
+ {
+ vDiagonal *= 0.25f;
+
+ if ( UTIL_IsBoxIntersectingPortal( vEarCenter, vDiagonal, pPortal ) )
+ {
+ m_bIntersectingPortalPlane = true;
+
+ CPASAttenuationFilter filter( this );
+ CSoundParameters params;
+ if ( GetParametersForSound( "PortalPlayer.EnterPortal", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
+ ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
+
+ EmitSound( filter, entindex(), ep );
+ }
+ }
+ }
+ else
+ {
+ vDiagonal *= 0.30f;
+
+ if ( !UTIL_IsBoxIntersectingPortal( vEarCenter, vDiagonal, pPortal ) )
+ {
+ m_bIntersectingPortalPlane = false;
+
+ CPASAttenuationFilter filter( this );
+ CSoundParameters params;
+ if ( GetParametersForSound( "PortalPlayer.ExitPortal", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
+ ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
+
+ EmitSound( filter, entindex(), ep );
+ }
+ }
+ }
+ }
+ }
+ else if ( m_bIntersectingPortalPlane )
+ {
+ m_bIntersectingPortalPlane = false;
+
+ CPASAttenuationFilter filter( this );
+ CSoundParameters params;
+ if ( GetParametersForSound( "PortalPlayer.ExitPortal", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ Vector vVelocity;
+ GetVelocity( &vVelocity, NULL );
+ ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
+ ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
+
+ EmitSound( filter, entindex(), ep );
+ }
+ }
+}
+
+void CPortal_Player::UpdateWooshSounds( void )
+{
+ if ( m_pWooshSound )
+ {
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ float fWooshVolume = GetAbsVelocity().Length() - MIN_FLING_SPEED;
+
+ if ( fWooshVolume < 0.0f )
+ {
+ controller.SoundChangeVolume( m_pWooshSound, 0.0f, 0.1f );
+ return;
+ }
+
+ fWooshVolume /= 2000.0f;
+ if ( fWooshVolume > 1.0f )
+ fWooshVolume = 1.0f;
+
+ controller.SoundChangeVolume( m_pWooshSound, fWooshVolume, 0.1f );
+ // controller.SoundChangePitch( m_pWooshSound, fWooshVolume + 0.5f, 0.1f );
+ }
+}
+
+void CPortal_Player::FireBullets ( const FireBulletsInfo_t &info )
+{
+ NoteWeaponFired();
+
+ BaseClass::FireBullets( info );
+}
+
+void CPortal_Player::NoteWeaponFired( void )
+{
+ Assert( m_pCurrentCommand );
+ if( m_pCurrentCommand )
+ {
+ m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
+ }
+}
+
+extern ConVar sv_maxunlag;
+
+bool CPortal_Player::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
+{
+ // No need to lag compensate at all if we're not attacking in this command and
+ // we haven't attacked recently.
+ if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) )
+ return false;
+
+ // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it.
+ if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) )
+ return false;
+
+ const Vector &vMyOrigin = GetAbsOrigin();
+ const Vector &vHisOrigin = pPlayer->GetAbsOrigin();
+
+ // get max distance player could have moved within max lag compensation time,
+ // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value)
+ float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat();
+
+ // If the player is within this distance, lag compensate them in case they're running past us.
+ if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance )
+ return true;
+
+ // If their origin is not within a 45 degree cone in front of us, no need to lag compensate.
+ Vector vForward;
+ AngleVectors( pCmd->viewangles, &vForward );
+
+ Vector vDiff = vHisOrigin - vMyOrigin;
+ VectorNormalize( vDiff );
+
+ float flCosAngle = 0.707107f; // 45 degree angle
+ if ( vForward.Dot( vDiff ) < flCosAngle )
+ return false;
+
+ return true;
+}
+
+
+void CPortal_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
+{
+ m_PlayerAnimState->DoAnimationEvent( event, nData );
+ TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override setup bones so that is uses the render angles from
+// the Portal animation state to setup the hitboxes.
+//-----------------------------------------------------------------------------
+void CPortal_Player::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
+{
+ VPROF_BUDGET( "CBaseAnimating::SetupBones", VPROF_BUDGETGROUP_SERVER_ANIM );
+
+ // Set the mdl cache semaphore.
+ MDLCACHE_CRITICAL_SECTION();
+
+ // Get the studio header.
+ Assert( GetModelPtr() );
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+
+ Vector pos[MAXSTUDIOBONES];
+ Quaternion q[MAXSTUDIOBONES];
+
+ // Adjust hit boxes based on IK driven offset.
+ Vector adjOrigin = GetAbsOrigin() + Vector( 0, 0, m_flEstIkOffset );
+
+ // FIXME: pass this into Studio_BuildMatrices to skip transforms
+ CBoneBitList boneComputed;
+ if ( m_pIk )
+ {
+ m_iIKCounter++;
+ m_pIk->Init( pStudioHdr, GetAbsAngles(), adjOrigin, gpGlobals->curtime, m_iIKCounter, boneMask );
+ GetSkeleton( pStudioHdr, pos, q, boneMask );
+
+ m_pIk->UpdateTargets( pos, q, pBoneToWorld, boneComputed );
+ CalculateIKLocks( gpGlobals->curtime );
+ m_pIk->SolveDependencies( pos, q, pBoneToWorld, boneComputed );
+ }
+ else
+ {
+ GetSkeleton( pStudioHdr, pos, q, boneMask );
+ }
+
+ CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );
+ if ( pParent )
+ {
+ // We're doing bone merging, so do special stuff here.
+ CBoneCache *pParentCache = pParent->GetBoneCache();
+ if ( pParentCache )
+ {
+ BuildMatricesWithBoneMerge(
+ pStudioHdr,
+ m_PlayerAnimState->GetRenderAngles(),
+ adjOrigin,
+ pos,
+ q,
+ pBoneToWorld,
+ pParent,
+ pParentCache );
+
+ return;
+ }
+ }
+
+ Studio_BuildMatrices(
+ pStudioHdr,
+ m_PlayerAnimState->GetRenderAngles(),
+ adjOrigin,
+ pos,
+ q,
+ -1,
+ GetModelScale(), // Scaling
+ pBoneToWorld,
+ boneMask );
+}
+
+
+// Set the activity based on an event or current state
+void CPortal_Player::SetAnimation( PLAYER_ANIM playerAnim )
+{
+ return;
+}
+
+CAI_Expresser *CPortal_Player::CreateExpresser()
+{
+ Assert( !m_pExpresser );
+
+ if ( m_pExpresser )
+ {
+ delete m_pExpresser;
+ }
+
+ m_pExpresser = new CAI_Expresser(this);
+ if ( !m_pExpresser)
+ {
+ return NULL;
+ }
+ m_pExpresser->Connect(this);
+
+ return m_pExpresser;
+}
+
+//-----------------------------------------------------------------------------
+
+CAI_Expresser *CPortal_Player::GetExpresser()
+{
+ if ( m_pExpresser )
+ {
+ m_pExpresser->Connect(this);
+ }
+ return m_pExpresser;
+}
+
+
+extern int gEvilImpulse101;
+//-----------------------------------------------------------------------------
+// Purpose: Player reacts to bumping a weapon.
+// Input : pWeapon - the weapon that the player bumped into.
+// Output : Returns true if player picked up the weapon
+//-----------------------------------------------------------------------------
+bool CPortal_Player::BumpWeapon( CBaseCombatWeapon *pWeapon )
+{
+ CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
+
+ // Can I have this weapon type?
+ if ( !IsAllowedToPickupWeapons() )
+ return false;
+
+ if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
+ {
+ if ( gEvilImpulse101 )
+ {
+ UTIL_Remove( pWeapon );
+ }
+ return false;
+ }
+
+ // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows)
+ if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) )
+ {
+ return false;
+ }
+
+ CWeaponPortalgun *pPickupPortalgun = dynamic_cast<CWeaponPortalgun*>( pWeapon );
+
+ bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType());
+
+ if ( bOwnsWeaponAlready == true )
+ {
+ // If we picked up a second portal gun set the bool to alow secondary fire
+ if ( pPickupPortalgun )
+ {
+ CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( Weapon_OwnsThisType( pWeapon->GetClassname() ) );
+
+ if ( pPickupPortalgun->CanFirePortal1() )
+ pPortalGun->SetCanFirePortal1();
+
+ if ( pPickupPortalgun->CanFirePortal2() )
+ pPortalGun->SetCanFirePortal2();
+
+ UTIL_Remove( pWeapon );
+ return true;
+ }
+
+ //If we have room for the ammo, then "take" the weapon too.
+ if ( Weapon_EquipAmmoOnly( pWeapon ) )
+ {
+ pWeapon->CheckRespawn();
+
+ UTIL_Remove( pWeapon );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ pWeapon->CheckRespawn();
+ Weapon_Equip( pWeapon );
+
+ // If we're holding and object before picking up portalgun, drop it
+ if ( pPickupPortalgun )
+ {
+ ForceDropOfCarriedPhysObjects( GetPlayerHeldEntity( this ) );
+ }
+
+ return true;
+}
+
+void CPortal_Player::ShutdownUseEntity( void )
+{
+ ShutdownPickupController( m_hUseEntity );
+}
+
+const Vector& CPortal_Player::WorldSpaceCenter( ) const
+{
+ m_vWorldSpaceCenterHolder = GetAbsOrigin();
+ m_vWorldSpaceCenterHolder.z += ( (IsDucked()) ? (VEC_DUCK_HULL_MAX_SCALED( this ).z) : (VEC_HULL_MAX_SCALED( this ).z) ) * 0.5f;
+ return m_vWorldSpaceCenterHolder;
+}
+
+void CPortal_Player::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
+{
+ Vector oldOrigin = GetLocalOrigin();
+ QAngle oldAngles = GetLocalAngles();
+ BaseClass::Teleport( newPosition, newAngles, newVelocity );
+ m_angEyeAngles = pl.v_angle;
+
+ m_PlayerAnimState->Teleport( newPosition, newAngles, this );
+}
+
+void CPortal_Player::VPhysicsShadowUpdate( IPhysicsObject *pPhysics )
+{
+ if( m_hPortalEnvironment.Get() == NULL )
+ return BaseClass::VPhysicsShadowUpdate( pPhysics );
+
+
+ //below is mostly a cut/paste of existing CBasePlayer::VPhysicsShadowUpdate code with some minor tweaks to avoid getting stuck in stuff when in a portal environment
+ if ( sv_turbophysics.GetBool() )
+ return;
+
+ Vector newPosition;
+
+ bool physicsUpdated = m_pPhysicsController->GetShadowPosition( &newPosition, NULL ) > 0 ? true : false;
+
+ // UNDONE: If the player is penetrating, but the player's game collisions are not stuck, teleport the physics shadow to the game position
+ if ( pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING )
+ {
+ CUtlVector<CBaseEntity *> list;
+ PhysGetListOfPenetratingEntities( this, list );
+ for ( int i = list.Count()-1; i >= 0; --i )
+ {
+ // filter out anything that isn't simulated by vphysics
+ // UNDONE: Filter out motion disabled objects?
+ if ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS )
+ {
+ // I'm currently stuck inside a moving object, so allow vphysics to
+ // apply velocity to the player in order to separate these objects
+ m_touchedPhysObject = true;
+ }
+ }
+ }
+
+ if ( m_pPhysicsController->IsInContact() || (m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER) )
+ {
+ m_touchedPhysObject = true;
+ }
+
+ if ( IsFollowingPhysics() )
+ {
+ m_touchedPhysObject = true;
+ }
+
+ if ( GetMoveType() == MOVETYPE_NOCLIP )
+ {
+ m_oldOrigin = GetAbsOrigin();
+ return;
+ }
+
+ if ( phys_timescale.GetFloat() == 0.0f )
+ {
+ physicsUpdated = false;
+ }
+
+ if ( !physicsUpdated )
+ return;
+
+ IPhysicsObject *pPhysGround = GetGroundVPhysics();
+
+ Vector newVelocity;
+ pPhysics->GetPosition( &newPosition, 0 );
+ m_pPhysicsController->GetShadowVelocity( &newVelocity );
+
+
+
+ Vector tmp = GetAbsOrigin() - newPosition;
+ if ( !m_touchedPhysObject && !(GetFlags() & FL_ONGROUND) )
+ {
+ tmp.z *= 0.5f; // don't care about z delta as much
+ }
+
+ float dist = tmp.LengthSqr();
+ float deltaV = (newVelocity - GetAbsVelocity()).LengthSqr();
+
+ float maxDistErrorSqr = VPHYS_MAX_DISTSQR;
+ float maxVelErrorSqr = VPHYS_MAX_VELSQR;
+ if ( IsRideablePhysics(pPhysGround) )
+ {
+ maxDistErrorSqr *= 0.25;
+ maxVelErrorSqr *= 0.25;
+ }
+
+ if ( dist >= maxDistErrorSqr || deltaV >= maxVelErrorSqr || (pPhysGround && !m_touchedPhysObject) )
+ {
+ if ( m_touchedPhysObject || pPhysGround )
+ {
+ // BUGBUG: Rewrite this code using fixed timestep
+ if ( deltaV >= maxVelErrorSqr )
+ {
+ Vector dir = GetAbsVelocity();
+ float len = VectorNormalize(dir);
+ float dot = DotProduct( newVelocity, dir );
+ if ( dot > len )
+ {
+ dot = len;
+ }
+ else if ( dot < -len )
+ {
+ dot = -len;
+ }
+
+ VectorMA( newVelocity, -dot, dir, newVelocity );
+
+ if ( m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER )
+ {
+ float val = Lerp( 0.1f, len, dot );
+ VectorMA( newVelocity, val - len, dir, newVelocity );
+ }
+
+ if ( !IsRideablePhysics(pPhysGround) )
+ {
+ if ( !(m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) && IsSimulatingOnAlternateTicks() )
+ {
+ newVelocity *= 0.5f;
+ }
+ ApplyAbsVelocityImpulse( newVelocity );
+ }
+ }
+
+ trace_t trace;
+ UTIL_TraceEntity( this, newPosition, newPosition, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+ if ( !trace.allsolid && !trace.startsolid )
+ {
+ SetAbsOrigin( newPosition );
+ }
+ }
+ else
+ {
+ trace_t trace;
+
+ Ray_t ray;
+ ray.Init( GetAbsOrigin(), GetAbsOrigin(), WorldAlignMins(), WorldAlignMaxs() );
+
+ CTraceFilterSimple OriginalTraceFilter( this, COLLISION_GROUP_PLAYER_MOVEMENT );
+ CTraceFilterTranslateClones traceFilter( &OriginalTraceFilter );
+ UTIL_Portal_TraceRay_With( m_hPortalEnvironment, ray, MASK_PLAYERSOLID, &traceFilter, &trace );
+
+ // current position is not ok, fixup
+ if ( trace.allsolid || trace.startsolid )
+ {
+ //try again with new position
+ ray.Init( newPosition, newPosition, WorldAlignMins(), WorldAlignMaxs() );
+ UTIL_Portal_TraceRay_With( m_hPortalEnvironment, ray, MASK_PLAYERSOLID, &traceFilter, &trace );
+
+ if( trace.startsolid == false )
+ {
+ SetAbsOrigin( newPosition );
+ }
+ else
+ {
+ if( !FindClosestPassableSpace( this, newPosition - GetAbsOrigin(), MASK_PLAYERSOLID ) )
+ {
+ // Try moving the player closer to the center of the portal
+ CProp_Portal *pPortal = m_hPortalEnvironment.Get();
+ newPosition += ( pPortal->GetAbsOrigin() - WorldSpaceCenter() ) * 0.1f;
+ SetAbsOrigin( newPosition );
+
+ DevMsg( "Hurting the player for FindClosestPassableSpaceFailure!" );
+
+ // Deal 1 damage per frame... this will kill a player very fast, but allow for the above correction to fix some cases
+ CTakeDamageInfo info( this, this, vec3_origin, vec3_origin, 1, DMG_CRUSH );
+ OnTakeDamage( info );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( m_touchedPhysObject )
+ {
+ // check my position (physics object could have simulated into my position
+ // physics is not very far away, check my position
+ trace_t trace;
+ UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(),
+ MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+
+ // is current position ok?
+ if ( trace.allsolid || trace.startsolid )
+ {
+ // stuck????!?!?
+ //Msg("Stuck on %s\n", trace.m_pEnt->GetClassname());
+ SetAbsOrigin( newPosition );
+ UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(),
+ MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+ if ( trace.allsolid || trace.startsolid )
+ {
+ //Msg("Double Stuck\n");
+ SetAbsOrigin( m_oldOrigin );
+ }
+ }
+ }
+ }
+ m_oldOrigin = GetAbsOrigin();
+}
+
+bool CPortal_Player::UseFoundEntity( CBaseEntity *pUseEntity )
+{
+ bool usedSomething = false;
+
+ //!!!UNDONE: traceline here to prevent +USEing buttons through walls
+ int caps = pUseEntity->ObjectCaps();
+ variant_t emptyVariant;
+
+ if ( m_afButtonPressed & IN_USE )
+ {
+ // Robin: Don't play sounds for NPCs, because NPCs will allow respond with speech.
+ if ( !pUseEntity->MyNPCPointer() )
+ {
+ EmitSound( "HL2Player.Use" );
+ }
+ }
+
+ if ( ( (m_nButtons & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) ||
+ ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) )
+ {
+ if ( caps & FCAP_CONTINUOUS_USE )
+ m_afPhysicsFlags |= PFLAG_USING;
+
+ pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
+
+ usedSomething = true;
+ }
+ // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away
+ else if ( (m_afButtonReleased & IN_USE) && (pUseEntity->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use
+ {
+ pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
+
+ usedSomething = true;
+ }
+
+#if HL2_SINGLE_PRIMARY_WEAPON_MODE
+
+ //Check for weapon pick-up
+ if ( m_afButtonPressed & IN_USE )
+ {
+ CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(pUseEntity);
+
+ if ( ( pWeapon != NULL ) && ( Weapon_CanSwitchTo( pWeapon ) ) )
+ {
+ //Try to take ammo or swap the weapon
+ if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) )
+ {
+ Weapon_EquipAmmoOnly( pWeapon );
+ }
+ else
+ {
+ Weapon_DropSlot( pWeapon->GetSlot() );
+ Weapon_Equip( pWeapon );
+ }
+
+ usedSomething = true;
+ }
+ }
+#endif
+
+ return usedSomething;
+}
+
+//bool CPortal_Player::StartReplayMode( float fDelay, float fDuration, int iEntity )
+//{
+// if ( !BaseClass::StartReplayMode( fDelay, fDuration, 1 ) )
+// return false;
+//
+// CSingleUserRecipientFilter filter( this );
+// filter.MakeReliable();
+//
+// UserMessageBegin( filter, "KillCam" );
+//
+// EHANDLE hPlayer = this;
+//
+// if ( m_hObserverTarget.Get() )
+// {
+// WRITE_EHANDLE( m_hObserverTarget ); // first target
+// WRITE_EHANDLE( hPlayer ); //second target
+// }
+// else
+// {
+// WRITE_EHANDLE( hPlayer ); // first target
+// WRITE_EHANDLE( 0 ); //second target
+// }
+// MessageEnd();
+//
+// return true;
+//}
+//
+//void CPortal_Player::StopReplayMode()
+//{
+// BaseClass::StopReplayMode();
+//
+// CSingleUserRecipientFilter filter( this );
+// filter.MakeReliable();
+//
+// UserMessageBegin( filter, "KillCam" );
+// WRITE_EHANDLE( 0 );
+// WRITE_EHANDLE( 0 );
+// MessageEnd();
+//}
+
+void CPortal_Player::PlayerUse( void )
+{
+ // Was use pressed or released?
+ if ( ! ((m_nButtons | m_afButtonPressed | m_afButtonReleased) & IN_USE) )
+ return;
+
+ if ( m_afButtonPressed & IN_USE )
+ {
+ // Currently using a latched entity?
+ if ( ClearUseEntity() )
+ {
+ return;
+ }
+ else
+ {
+ if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
+ {
+ m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
+ m_iTrain = TRAIN_NEW|TRAIN_OFF;
+ return;
+ }
+ else
+ { // Start controlling the train!
+ CBaseEntity *pTrain = GetGroundEntity();
+ if ( pTrain && !(m_nButtons & IN_JUMP) && (GetFlags() & FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(this) )
+ {
+ m_afPhysicsFlags |= PFLAG_DIROVERRIDE;
+ m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed());
+ m_iTrain |= TRAIN_NEW;
+ EmitSound( "HL2Player.TrainUse" );
+ return;
+ }
+ }
+ }
+
+ // Tracker 3926: We can't +USE something if we're climbing a ladder
+ if ( GetMoveType() == MOVETYPE_LADDER )
+ {
+ return;
+ }
+ }
+
+ CBaseEntity *pUseEntity = FindUseEntity();
+
+ bool usedSomething = false;
+
+ // Found an object
+ if ( pUseEntity )
+ {
+ SetHeldObjectOnOppositeSideOfPortal( false );
+
+ // TODO: Removed because we no longer have ghost animatings. May need to rework this code.
+ //// If we found a ghost animating then it needs to be held across a portal
+ //CGhostAnimating *pGhostAnimating = dynamic_cast<CGhostAnimating*>( pUseEntity );
+ //if ( pGhostAnimating )
+ //{
+ // CProp_Portal *pPortal = NULL;
+
+ // CPortalSimulator *pPortalSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pGhostAnimating->GetSourceEntity() );
+
+ // //HACKHACK: This assumes all portal simulators are a member of a prop_portal
+ // pPortal = (CProp_Portal *)(((char *)pPortalSimulator) - ((int)&(((CProp_Portal *)0)->m_PortalSimulator)));
+ // Assert( (&(pPortal->m_PortalSimulator)) == pPortalSimulator ); //doublechecking the hack
+
+ // if ( pPortal )
+ // {
+ // SetHeldObjectPortal( pPortal->m_hLinkedPortal );
+ // SetHeldObjectOnOppositeSideOfPortal( true );
+ // }
+ //}
+ usedSomething = UseFoundEntity( pUseEntity );
+ }
+ else
+ {
+ Vector forward;
+ EyeVectors( &forward, NULL, NULL );
+ Vector start = EyePosition();
+
+ Ray_t rayPortalTest;
+ rayPortalTest.Init( start, start + forward * PLAYER_USE_RADIUS );
+
+ float fMustBeCloserThan = 2.0f;
+
+ CProp_Portal *pPortal = UTIL_Portal_FirstAlongRay( rayPortalTest, fMustBeCloserThan );
+
+ if ( pPortal )
+ {
+ SetHeldObjectPortal( pPortal );
+ pUseEntity = FindUseEntityThroughPortal();
+ }
+
+ if ( pUseEntity )
+ {
+ SetHeldObjectOnOppositeSideOfPortal( true );
+ usedSomething = UseFoundEntity( pUseEntity );
+ }
+ else if ( m_afButtonPressed & IN_USE )
+ {
+ // Signal that we want to play the deny sound, unless the user is +USEing on a ladder!
+ // The sound is emitted in ItemPostFrame, since that occurs after GameMovement::ProcessMove which
+ // lets the ladder code unset this flag.
+ m_bPlayUseDenySound = true;
+ }
+ }
+
+ // Debounce the use key
+ if ( usedSomething && pUseEntity )
+ {
+ m_Local.m_nOldButtons |= IN_USE;
+ m_afButtonPressed &= ~IN_USE;
+ }
+}
+
+void CPortal_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper)
+{
+ if( m_bFixEyeAnglesFromPortalling )
+ {
+ //the idea here is to handle the notion that the player has portalled, but they sent us an angle update before receiving that message.
+ //If we don't handle this here, we end up sending back their old angles which makes them hiccup their angles for a frame
+ float fOldAngleDiff = fabs( AngleDistance( ucmd->viewangles.x, m_qPrePortalledViewAngles.x ) );
+ fOldAngleDiff += fabs( AngleDistance( ucmd->viewangles.y, m_qPrePortalledViewAngles.y ) );
+ fOldAngleDiff += fabs( AngleDistance( ucmd->viewangles.z, m_qPrePortalledViewAngles.z ) );
+
+ float fCurrentAngleDiff = fabs( AngleDistance( ucmd->viewangles.x, pl.v_angle.x ) );
+ fCurrentAngleDiff += fabs( AngleDistance( ucmd->viewangles.y, pl.v_angle.y ) );
+ fCurrentAngleDiff += fabs( AngleDistance( ucmd->viewangles.z, pl.v_angle.z ) );
+
+ if( fCurrentAngleDiff > fOldAngleDiff )
+ ucmd->viewangles = TransformAnglesToWorldSpace( ucmd->viewangles, m_matLastPortalled.As3x4() );
+
+ m_bFixEyeAnglesFromPortalling = false;
+ }
+
+ BaseClass::PlayerRunCommand( ucmd, moveHelper );
+}
+
+
+bool CPortal_Player::ClientCommand( const CCommand &args )
+{
+ if ( FStrEq( args[0], "spectate" ) )
+ {
+ // do nothing.
+ return true;
+ }
+
+ return BaseClass::ClientCommand( args );
+}
+
+void CPortal_Player::CheatImpulseCommands( int iImpulse )
+{
+ switch ( iImpulse )
+ {
+ case 101:
+ {
+ if( sv_cheats->GetBool() )
+ {
+ GiveAllItems();
+ }
+ }
+ break;
+
+ default:
+ BaseClass::CheatImpulseCommands( iImpulse );
+ }
+}
+
+void CPortal_Player::CreateViewModel( int index /*=0*/ )
+{
+ BaseClass::CreateViewModel( index );
+ return;
+ Assert( index >= 0 && index < MAX_VIEWMODELS );
+
+ if ( GetViewModel( index ) )
+ return;
+
+ CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" );
+ if ( vm )
+ {
+ vm->SetAbsOrigin( GetAbsOrigin() );
+ vm->SetOwner( this );
+ vm->SetIndex( index );
+ DispatchSpawn( vm );
+ vm->FollowEntity( this, false );
+ m_hViewModel.Set( index, vm );
+ }
+}
+
+bool CPortal_Player::BecomeRagdollOnClient( const Vector &force )
+{
+ return true;//BaseClass::BecomeRagdollOnClient( force );
+}
+
+void CPortal_Player::CreateRagdollEntity( const CTakeDamageInfo &info )
+{
+ if ( m_hRagdoll )
+ {
+ UTIL_RemoveImmediate( m_hRagdoll );
+ m_hRagdoll = NULL;
+ }
+
+#if PORTAL_HIDE_PLAYER_RAGDOLL
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ AddEffects( EF_NODRAW | EF_NOSHADOW );
+ AddEFlags( EFL_NO_DISSOLVE );
+#endif // PORTAL_HIDE_PLAYER_RAGDOLL
+ CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
+ pRagdoll->m_takedamage = DAMAGE_NO;
+ m_hRagdoll = pRagdoll;
+
+/*
+ // If we already have a ragdoll destroy it.
+ CPortalRagdoll *pRagdoll = dynamic_cast<CPortalRagdoll*>( m_hRagdoll.Get() );
+ if( pRagdoll )
+ {
+ UTIL_Remove( pRagdoll );
+ pRagdoll = NULL;
+ }
+ Assert( pRagdoll == NULL );
+
+ // Create a ragdoll.
+ pRagdoll = dynamic_cast<CPortalRagdoll*>( CreateEntityByName( "portal_ragdoll" ) );
+ if ( pRagdoll )
+ {
+
+
+ pRagdoll->m_hPlayer = this;
+ pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
+ pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
+ pRagdoll->m_nModelIndex = m_nModelIndex;
+ pRagdoll->m_nForceBone = m_nForceBone;
+ pRagdoll->CopyAnimationDataFrom( this );
+ pRagdoll->SetOwnerEntity( this );
+ pRagdoll->m_flAnimTime = gpGlobals->curtime;
+ pRagdoll->m_flPlaybackRate = 0.0;
+ pRagdoll->SetCycle( 0 );
+ pRagdoll->ResetSequence( 0 );
+
+ float fSequenceDuration = SequenceDuration( GetSequence() );
+ float fPreviousCycle = clamp(GetCycle()-( 0.1 * ( 1 / fSequenceDuration ) ),0.f,1.f);
+ float fCurCycle = GetCycle();
+ matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
+ SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING );
+ SetCycle( fPreviousCycle );
+ SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
+ SetCycle( fCurCycle );
+
+ pRagdoll->InitRagdoll( info.GetDamageForce(), m_nForceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, 0.1f, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
+ pRagdoll->SetMoveType( MOVETYPE_VPHYSICS );
+ pRagdoll->SetSolid( SOLID_VPHYSICS );
+ if ( IsDissolving() )
+ {
+ pRagdoll->TransferDissolveFrom( this );
+ }
+
+ Vector mins, maxs;
+ mins = CollisionProp()->OBBMins();
+ maxs = CollisionProp()->OBBMaxs();
+ pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
+ pRagdoll->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
+ }
+
+ // Turn off the player.
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ AddEffects( EF_NODRAW | EF_NOSHADOW );
+ SetMoveType( MOVETYPE_NONE );
+
+ // Save ragdoll handle.
+ m_hRagdoll = pRagdoll;
+*/
+}
+
+void CPortal_Player::Jump( void )
+{
+ g_PortalGameStats.Event_PlayerJump( GetAbsOrigin(), GetAbsVelocity() );
+ BaseClass::Jump();
+}
+
+void CPortal_Player::Event_Killed( const CTakeDamageInfo &info )
+{
+ //update damage info with our accumulated physics force
+ CTakeDamageInfo subinfo = info;
+ subinfo.SetDamageForce( m_vecTotalBulletForce );
+
+ // show killer in death cam mode
+ // chopped down version of SetObserverTarget without the team check
+ //if( info.GetAttacker() )
+ //{
+ // // set new target
+ // m_hObserverTarget.Set( info.GetAttacker() );
+ //}
+ //else
+ // m_hObserverTarget.Set( NULL );
+
+ UpdateExpression();
+
+ // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
+ // because we still want to transmit to the clients in our PVS.
+ CreateRagdollEntity( info );
+
+ BaseClass::Event_Killed( subinfo );
+
+#if PORTAL_HIDE_PLAYER_RAGDOLL
+ // Fizzle all portals so they don't see the player disappear
+ int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
+ CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
+ for( int i = 0; i != iPortalCount; ++i )
+ {
+ CProp_Portal *pTempPortal = pPortals[i];
+
+ if( pTempPortal && pTempPortal->m_bActivated )
+ {
+ pTempPortal->Fizzle();
+ }
+ }
+#endif // PORTAL_HIDE_PLAYER_RAGDOLL
+
+ if ( (info.GetDamageType() & DMG_DISSOLVE) && !(m_hRagdoll.Get()->GetEFlags() & EFL_NO_DISSOLVE) )
+ {
+ if ( m_hRagdoll )
+ {
+ m_hRagdoll->GetBaseAnimating()->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
+ }
+ }
+
+ m_lifeState = LIFE_DYING;
+ StopZooming();
+
+ if ( GetObserverTarget() )
+ {
+ //StartReplayMode( 3, 3, GetObserverTarget()->entindex() );
+ //StartObserverMode( OBS_MODE_DEATHCAM );
+ }
+}
+
+int CPortal_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo )
+{
+ CTakeDamageInfo inputInfoCopy( inputInfo );
+
+ // If you shoot yourself, make it hurt but push you less
+ if ( inputInfoCopy.GetAttacker() == this && inputInfoCopy.GetDamageType() == DMG_BULLET )
+ {
+ inputInfoCopy.ScaleDamage( 5.0f );
+ inputInfoCopy.ScaleDamageForce( 0.05f );
+ }
+
+ CBaseEntity *pAttacker = inputInfoCopy.GetAttacker();
+ CBaseEntity *pInflictor = inputInfoCopy.GetInflictor();
+
+ bool bIsTurret = false;
+
+ if ( pAttacker && FClassnameIs( pAttacker, "npc_portal_turret_floor" ) )
+ bIsTurret = true;
+
+ // Refuse damage from prop_glados_core.
+ if ( (pAttacker && FClassnameIs( pAttacker, "prop_glados_core" )) ||
+ (pInflictor && FClassnameIs( pInflictor, "prop_glados_core" )) )
+ {
+ inputInfoCopy.SetDamage(0.0f);
+ }
+
+ if ( bIsTurret && ( inputInfoCopy.GetDamageType() & DMG_BULLET ) )
+ {
+ Vector vLateralForce = inputInfoCopy.GetDamageForce();
+ vLateralForce.z = 0.0f;
+
+ // Push if the player is moving against the force direction
+ if ( GetAbsVelocity().Dot( vLateralForce ) < 0.0f )
+ ApplyAbsVelocityImpulse( vLateralForce );
+ }
+ else if ( ( inputInfoCopy.GetDamageType() & DMG_CRUSH ) )
+ {
+ if ( bIsTurret )
+ {
+ inputInfoCopy.SetDamage( inputInfoCopy.GetDamage() * 0.5f );
+ }
+
+ if ( inputInfoCopy.GetDamage() >= 10.0f )
+ {
+ EmitSound( "PortalPlayer.BonkYelp" );
+ }
+ }
+ else if ( ( inputInfoCopy.GetDamageType() & DMG_SHOCK ) || ( inputInfoCopy.GetDamageType() & DMG_BURN ) )
+ {
+ EmitSound( "PortalPortal.PainYelp" );
+ }
+
+ int ret = BaseClass::OnTakeDamage( inputInfoCopy );
+
+ // Copy the multidamage damage origin over what the base class wrote, because
+ // that gets translated correctly though portals.
+ m_DmgOrigin = inputInfo.GetDamagePosition();
+
+ if ( GetHealth() < 100 )
+ {
+ m_fTimeLastHurt = gpGlobals->curtime;
+ }
+
+ return ret;
+}
+
+int CPortal_Player::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ // set damage type sustained
+ m_bitsDamageType |= info.GetDamageType();
+
+ if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) )
+ return 0;
+
+ CBaseEntity * attacker = info.GetAttacker();
+
+ if ( !attacker )
+ return 0;
+
+ Vector vecDir = vec3_origin;
+ if ( info.GetInflictor() )
+ {
+ vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0, 0, 10 ) - WorldSpaceCenter();
+ VectorNormalize( vecDir );
+ }
+
+ if ( info.GetInflictor() && (GetMoveType() == MOVETYPE_WALK) &&
+ ( !attacker->IsSolidFlagSet(FSOLID_TRIGGER)) )
+ {
+ Vector force = vecDir;// * -DamageForce( WorldAlignSize(), info.GetBaseDamage() );
+ if ( force.z > 250.0f )
+ {
+ force.z = 250.0f;
+ }
+ ApplyAbsVelocityImpulse( force );
+ }
+
+ // fire global game event
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
+ if ( event )
+ {
+ event->SetInt("userid", GetUserID() );
+ event->SetInt("health", MAX(0, m_iHealth) );
+ event->SetInt("priority", 5 ); // HLTV event priority, not transmitted
+
+ if ( attacker->IsPlayer() )
+ {
+ CBasePlayer *player = ToBasePlayer( attacker );
+ event->SetInt("attacker", player->GetUserID() ); // hurt by other player
+ }
+ else
+ {
+ event->SetInt("attacker", 0 ); // hurt by "world"
+ }
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ // Insert a combat sound so that nearby NPCs hear battle
+ if ( attacker->IsNPC() )
+ {
+ CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 512, 0.5, this );//<<TODO>>//magic number
+ }
+
+ return 1;
+}
+
+
+void CPortal_Player::ForceDuckThisFrame( void )
+{
+ if( m_Local.m_bDucked != true )
+ {
+ //m_Local.m_bDucking = false;
+ m_Local.m_bDucked = true;
+ ForceButtons( IN_DUCK );
+ AddFlag( FL_DUCKING );
+ SetVCollisionState( GetAbsOrigin(), GetAbsVelocity(), VPHYS_CROUCH );
+ }
+}
+
+void CPortal_Player::UnDuck( void )
+{
+ if( m_Local.m_bDucked != false )
+ {
+ m_Local.m_bDucked = false;
+ UnforceButtons( IN_DUCK );
+ RemoveFlag( FL_DUCKING );
+ SetVCollisionState( GetAbsOrigin(), GetAbsVelocity(), VPHYS_WALK );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Overload for portal-- Our player can lift his own mass.
+// Input : *pObject - The object to lift
+// bLimitMassAndSize - check for mass/size limits
+//-----------------------------------------------------------------------------
+void CPortal_Player::PickupObject(CBaseEntity *pObject, bool bLimitMassAndSize )
+{
+ // can't pick up what you're standing on
+ if ( GetGroundEntity() == pObject )
+ return;
+
+ if ( bLimitMassAndSize == true )
+ {
+ if ( CBasePlayer::CanPickupObject( pObject, PORTAL_PLAYER_MAX_LIFT_MASS, PORTAL_PLAYER_MAX_LIFT_SIZE ) == false )
+ return;
+ }
+
+ // Can't be picked up if NPCs are on me
+ if ( pObject->HasNPCsOnIt() )
+ return;
+
+ PlayerPickupObject( this, pObject );
+}
+
+void CPortal_Player::ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldingThis )
+{
+ m_bHeldObjectOnOppositeSideOfPortal = false;
+ BaseClass::ForceDropOfCarriedPhysObjects( pOnlyIfHoldingThis );
+}
+
+void CPortal_Player::IncrementPortalsPlaced( void )
+{
+ m_StatsThisLevel.iNumPortalsPlaced++;
+
+ if ( m_iBonusChallenge == PORTAL_CHALLENGE_PORTALS )
+ SetBonusProgress( static_cast<int>( m_StatsThisLevel.iNumPortalsPlaced ) );
+}
+
+void CPortal_Player::IncrementStepsTaken( void )
+{
+ m_StatsThisLevel.iNumStepsTaken++;
+
+ if ( m_iBonusChallenge == PORTAL_CHALLENGE_STEPS )
+ SetBonusProgress( static_cast<int>( m_StatsThisLevel.iNumStepsTaken ) );
+}
+
+void CPortal_Player::UpdateSecondsTaken( void )
+{
+ float fSecondsSinceLastUpdate = ( gpGlobals->curtime - m_fTimeLastNumSecondsUpdate );
+ m_StatsThisLevel.fNumSecondsTaken += fSecondsSinceLastUpdate;
+ m_fTimeLastNumSecondsUpdate = gpGlobals->curtime;
+
+ if ( m_iBonusChallenge == PORTAL_CHALLENGE_TIME )
+ SetBonusProgress( static_cast<int>( m_StatsThisLevel.fNumSecondsTaken ) );
+
+ if ( m_fNeuroToxinDamageTime > 0.0f )
+ {
+ float fTimeRemaining = m_fNeuroToxinDamageTime - gpGlobals->curtime;
+
+ if ( fTimeRemaining < 0.0f )
+ {
+ CTakeDamageInfo info;
+ info.SetDamage( gpGlobals->frametime * 50.0f );
+ info.SetDamageType( DMG_NERVEGAS );
+ TakeDamage( info );
+ fTimeRemaining = 0.0f;
+ }
+
+ PauseBonusProgress( false );
+ SetBonusProgress( static_cast<int>( fTimeRemaining ) );
+ }
+}
+
+void CPortal_Player::ResetThisLevelStats( void )
+{
+ m_StatsThisLevel.iNumPortalsPlaced = 0;
+ m_StatsThisLevel.iNumStepsTaken = 0;
+ m_StatsThisLevel.fNumSecondsTaken = 0.0f;
+
+ if ( m_iBonusChallenge != PORTAL_CHALLENGE_NONE )
+ SetBonusProgress( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the area bits variable which is networked down to the client to determine
+// which area portals should be closed based on visibility.
+// Input : *pvs - pvs to be used to determine visibility of the portals
+//-----------------------------------------------------------------------------
+void CPortal_Player::UpdatePortalViewAreaBits( unsigned char *pvs, int pvssize )
+{
+ Assert ( pvs );
+
+ int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
+ if( iPortalCount == 0 )
+ return;
+
+ CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
+ int *portalArea = (int *)stackalloc( sizeof( int ) * iPortalCount );
+ bool *bUsePortalForVis = (bool *)stackalloc( sizeof( bool ) * iPortalCount );
+
+ unsigned char *portalTempBits = (unsigned char *)stackalloc( sizeof( unsigned char ) * 32 * iPortalCount );
+ COMPILE_TIME_ASSERT( (sizeof( unsigned char ) * 32) >= sizeof( ((CPlayerLocalData*)0)->m_chAreaBits ) );
+
+ // setup area bits for these portals
+ for ( int i = 0; i < iPortalCount; ++i )
+ {
+ CProp_Portal* pLocalPortal = pPortals[ i ];
+ // Make sure this portal is active before adding it's location to the pvs
+ if ( pLocalPortal && pLocalPortal->m_bActivated )
+ {
+ CProp_Portal* pRemotePortal = pLocalPortal->m_hLinkedPortal.Get();
+
+ // Make sure this portal's linked portal is in the PVS before we add what it can see
+ if ( pRemotePortal && pRemotePortal->m_bActivated && pRemotePortal->NetworkProp() &&
+ pRemotePortal->NetworkProp()->IsInPVS( edict(), pvs, pvssize ) )
+ {
+ portalArea[ i ] = engine->GetArea( pPortals[ i ]->GetAbsOrigin() );
+
+ if ( portalArea [ i ] >= 0 )
+ {
+ bUsePortalForVis[ i ] = true;
+ }
+
+ engine->GetAreaBits( portalArea[ i ], &portalTempBits[ i * 32 ], sizeof( unsigned char ) * 32 );
+ }
+ }
+ }
+
+ // Use the union of player-view area bits and the portal-view area bits of each portal
+ for ( int i = 0; i < m_Local.m_chAreaBits.Count(); i++ )
+ {
+ for ( int j = 0; j < iPortalCount; ++j )
+ {
+ // If this portal is active, in PVS and it's location is valid
+ if ( bUsePortalForVis[ j ] )
+ {
+ m_Local.m_chAreaBits.Set( i, m_Local.m_chAreaBits[ i ] | portalTempBits[ (j * 32) + i ] );
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// AddPortalCornersToEnginePVS
+// Subroutine to wrap the adding of portal corners to the PVS which is called once for the setup of each portal.
+// input - pPortal: the portal we are viewing 'out of' which needs it's corners added to the PVS
+//////////////////////////////////////////////////////////////////////////
+void AddPortalCornersToEnginePVS( CProp_Portal* pPortal )
+{
+ Assert ( pPortal );
+
+ if ( !pPortal )
+ return;
+
+ Vector vForward, vRight, vUp;
+ pPortal->GetVectors( &vForward, &vRight, &vUp );
+
+ // Center of the remote portal
+ Vector ptOrigin = pPortal->GetAbsOrigin();
+
+ // Distance offsets to the different edges of the portal... Used in the placement checks
+ Vector vToTopEdge = vUp * ( PORTAL_HALF_HEIGHT - PORTAL_BUMP_FORGIVENESS );
+ Vector vToBottomEdge = -vToTopEdge;
+ Vector vToRightEdge = vRight * ( PORTAL_HALF_WIDTH - PORTAL_BUMP_FORGIVENESS );
+ Vector vToLeftEdge = -vToRightEdge;
+
+ // Distance to place PVS points away from portal, to avoid being in solid
+ Vector vForwardBump = vForward * 1.0f;
+
+ // Add center and edges to the engine PVS
+ engine->AddOriginToPVS( ptOrigin + vForwardBump);
+ engine->AddOriginToPVS( ptOrigin + vToTopEdge + vToLeftEdge + vForwardBump );
+ engine->AddOriginToPVS( ptOrigin + vToTopEdge + vToRightEdge + vForwardBump );
+ engine->AddOriginToPVS( ptOrigin + vToBottomEdge + vToLeftEdge + vForwardBump );
+ engine->AddOriginToPVS( ptOrigin + vToBottomEdge + vToRightEdge + vForwardBump );
+}
+
+void PortalSetupVisibility( CBaseEntity *pPlayer, int area, unsigned char *pvs, int pvssize )
+{
+ int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
+ if( iPortalCount == 0 )
+ return;
+
+ CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
+ for( int i = 0; i != iPortalCount; ++i )
+ {
+ CProp_Portal *pPortal = pPortals[i];
+
+ if ( pPortal && pPortal->m_bActivated )
+ {
+ if ( pPortal->NetworkProp()->IsInPVS( pPlayer->edict(), pvs, pvssize ) )
+ {
+ if ( engine->CheckAreasConnected( area, pPortal->NetworkProp()->AreaNum() ) )
+ {
+ CProp_Portal *pLinkedPortal = static_cast<CProp_Portal*>( pPortal->m_hLinkedPortal.Get() );
+ if ( pLinkedPortal )
+ {
+ AddPortalCornersToEnginePVS ( pLinkedPortal );
+ }
+ }
+ }
+ }
+ }
+}
+
+void CPortal_Player::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
+{
+ BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
+
+ int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
+
+ // At this point the EyePosition has been added as a view origin, but if we are currently stuck
+ // in a portal, our EyePosition may return a point in solid. Find the reflected eye position
+ // and use that as a vis origin instead.
+ if ( m_hPortalEnvironment )
+ {
+ CProp_Portal *pPortal = NULL, *pRemotePortal = NULL;
+ pPortal = m_hPortalEnvironment;
+ pRemotePortal = pPortal->m_hLinkedPortal;
+
+ if ( pPortal && pRemotePortal && pPortal->m_bActivated && pRemotePortal->m_bActivated )
+ {
+ Vector ptPortalCenter = pPortal->GetAbsOrigin();
+ Vector vPortalForward;
+ pPortal->GetVectors( &vPortalForward, NULL, NULL );
+
+ Vector eyeOrigin = EyePosition();
+ Vector vEyeToPortalCenter = ptPortalCenter - eyeOrigin;
+
+ float fPortalDist = vPortalForward.Dot( vEyeToPortalCenter );
+ if( fPortalDist > 0.0f ) //eye point is behind portal
+ {
+ // Move eye origin to it's transformed position on the other side of the portal
+ UTIL_Portal_PointTransform( pPortal->MatrixThisToLinked(), eyeOrigin, eyeOrigin );
+
+ // Use this as our view origin (as this is where the client will be displaying from)
+ engine->AddOriginToPVS( eyeOrigin );
+ if ( !pViewEntity || pViewEntity->IsPlayer() )
+ {
+ area = engine->GetArea( eyeOrigin );
+ }
+ }
+ }
+ }
+
+ PortalSetupVisibility( this, area, pvs, pvssize );
+}
+
+
+#ifdef PORTAL_MP
+
+CBaseEntity* CPortal_Player::EntSelectSpawnPoint( void )
+{
+ CBaseEntity *pSpot = NULL;
+ CBaseEntity *pLastSpawnPoint = g_pLastSpawn;
+ edict_t *player = edict();
+ const char *pSpawnpointName = "info_player_start";
+
+ /*if ( HL2MPRules()->IsTeamplay() == true )
+ {
+ if ( GetTeamNumber() == TEAM_COMBINE )
+ {
+ pSpawnpointName = "info_player_combine";
+ pLastSpawnPoint = g_pLastCombineSpawn;
+ }
+ else if ( GetTeamNumber() == TEAM_REBELS )
+ {
+ pSpawnpointName = "info_player_rebel";
+ pLastSpawnPoint = g_pLastRebelSpawn;
+ }
+
+ if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL )
+ {
+ pSpawnpointName = "info_player_deathmatch";
+ pLastSpawnPoint = g_pLastSpawn;
+ }
+ }*/
+
+ pSpot = pLastSpawnPoint;
+ // Randomize the start spot
+ for ( int i = random->RandomInt(1,5); i > 0; i-- )
+ pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
+ if ( !pSpot ) // skip over the null point
+ pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
+
+ CBaseEntity *pFirstSpot = pSpot;
+
+ do
+ {
+ if ( pSpot )
+ {
+ // check if pSpot is valid
+ if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
+ {
+ if ( pSpot->GetLocalOrigin() == vec3_origin )
+ {
+ pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
+ continue;
+ }
+
+ // if so, go to pSpot
+ goto ReturnSpot;
+ }
+ }
+ // increment pSpot
+ pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
+ } while ( pSpot != pFirstSpot ); // loop if we're not back to the start
+
+ // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there
+ if ( pSpot )
+ {
+ CBaseEntity *ent = NULL;
+ for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
+ {
+ // if ent is a client, kill em (unless they are ourselves)
+ if ( ent->IsPlayer() && !(ent->edict() == player) )
+ ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) );
+ }
+ goto ReturnSpot;
+ }
+
+ if ( !pSpot )
+ {
+ pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" );
+
+ if ( pSpot )
+ goto ReturnSpot;
+ }
+
+ReturnSpot:
+
+ /*if ( HL2MPRules()->IsTeamplay() == true )
+ {
+ if ( GetTeamNumber() == TEAM_COMBINE )
+ {
+ g_pLastCombineSpawn = pSpot;
+ }
+ else if ( GetTeamNumber() == TEAM_REBELS )
+ {
+ g_pLastRebelSpawn = pSpot;
+ }
+ }*/
+
+ g_pLastSpawn = pSpot;
+
+ //m_flSlamProtectTime = gpGlobals->curtime + 0.5;
+
+ return pSpot;
+}
+
+void CPortal_Player::PickTeam( void )
+{
+ //picks lowest or random
+ CTeam *pCombine = g_Teams[TEAM_COMBINE];
+ CTeam *pRebels = g_Teams[TEAM_REBELS];
+ if ( pCombine->GetNumPlayers() > pRebels->GetNumPlayers() )
+ {
+ ChangeTeam( TEAM_REBELS );
+ }
+ else if ( pCombine->GetNumPlayers() < pRebels->GetNumPlayers() )
+ {
+ ChangeTeam( TEAM_COMBINE );
+ }
+ else
+ {
+ ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) );
+ }
+}
+
+#endif
+
+CON_COMMAND( startadmiregloves, "Starts the admire gloves animation." )
+{
+ CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
+ if( pPlayer == NULL )
+ pPlayer = GetPortalPlayer( 1 ); //last ditch effort
+
+ if( pPlayer )
+ pPlayer->StartAdmireGlovesAnimation();
+}
+
+CON_COMMAND( displayportalplayerstats, "Displays current level stats for portals placed, steps taken, and seconds taken." )
+{
+ CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
+ if( pPlayer == NULL )
+ pPlayer = GetPortalPlayer( 1 ); //last ditch effort
+
+ if( pPlayer )
+ {
+ int iMinutes = static_cast<int>( pPlayer->NumSecondsTaken() / 60.0f );
+ int iSeconds = static_cast<int>( pPlayer->NumSecondsTaken() ) % 60;
+
+ CFmtStr msg;
+ NDebugOverlay::ScreenText( 0.5f, 0.5f, msg.sprintf( "Portals Placed: %d\nSteps Taken: %d\nTime: %d:%d", pPlayer->NumPortalsPlaced(), pPlayer->NumStepsTaken(), iMinutes, iSeconds ), 255, 255, 255, 150, 5.0f );
+ }
+}
+
+CON_COMMAND( startneurotoxins, "Starts the nerve gas timer." )
+{
+ CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
+ if( pPlayer == NULL )
+ pPlayer = GetPortalPlayer( 1 ); //last ditch effort
+
+ float fCoundownTime = 180.0f;
+
+ if ( args.ArgC() > 1 )
+ fCoundownTime = atof( args[ 1 ] );
+
+ if( pPlayer )
+ pPlayer->SetNeuroToxinDamageTime( fCoundownTime );
+}