diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/portal/portal_player.cpp | |
| download | archived-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.cpp | 2333 |
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 ¶ms ) +{ + // 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 ); +} |