//========= 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( 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( GiveNamedItem( "weapon_portalgun" ) ); if ( !pPortalGun ) { pPortalGun = static_cast( 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( 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 = SpeakFindResponse( g_pszChellConcepts[iConcept] ); if ( !response ) { m_flExpressionLoopTime = gpGlobals->curtime + RandomFloat(30,40); return; } char szScene[256] = { 0 }; response->GetResponse( szScene, sizeof(szScene) ); // 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 *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( 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( 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 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(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( 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( m_hRagdoll.Get() ); if( pRagdoll ) { UTIL_Remove( pRagdoll ); pRagdoll = NULL; } Assert( pRagdoll == NULL ); // Create a ragdoll. pRagdoll = dynamic_cast( 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 );//<>//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( m_StatsThisLevel.iNumPortalsPlaced ) ); } void CPortal_Player::IncrementStepsTaken( void ) { m_StatsThisLevel.iNumStepsTaken++; if ( m_iBonusChallenge == PORTAL_CHALLENGE_STEPS ) SetBonusProgress( static_cast( 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( 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( 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( 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( pPlayer->NumSecondsTaken() / 60.0f ); int iSeconds = static_cast( 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 ); }