|
|
//========= 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 ); }
|