//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Player for Portal.
//
//=============================================================================//
# include "cbase.h"
# include "portal_player.h"
# include "globalstate.h"
# include "trains.h"
# include "game.h"
# include "portal_player_shared.h"
# include "predicted_viewmodel.h"
# include "in_buttons.h"
# include "portal_gamerules.h"
# include "weapon_portalgun.h"
# include "portal/weapon_physcannon.h"
# include "KeyValues.h"
# include "team.h"
# include "eventqueue.h"
# include "weapon_portalbase.h"
# include "engine/IEngineSound.h"
# include "ai_basenpc.h"
# include "SoundEmitterSystem/isoundemittersystembase.h"
# include "prop_portal_shared.h"
# include "player_pickup.h" // for player pickup code
# include "vphysics/player_controller.h"
# include "datacache/imdlcache.h"
# include "bone_setup.h"
# include "portal_gamestats.h"
# include "physicsshadowclone.h"
# include "physics_prop_ragdoll.h"
# include "soundenvelope.h"
# include "ai_speech.h" // For expressors, vcd playing
# include "sceneentity.h" // has the VCD precache function
// Max mass the player can lift with +use
# define PORTAL_PLAYER_MAX_LIFT_MASS 85
# define PORTAL_PLAYER_MAX_LIFT_SIZE 128
extern CBaseEntity * g_pLastSpawn ;
extern void respawn ( CBaseEntity * pEdict , bool fCopyCorpse ) ;
// -------------------------------------------------------------------------------- //
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
// -------------------------------------------------------------------------------- //
class CTEPlayerAnimEvent : public CBaseTempEntity
{
public :
DECLARE_CLASS ( CTEPlayerAnimEvent , CBaseTempEntity ) ;
DECLARE_SERVERCLASS ( ) ;
CTEPlayerAnimEvent ( const char * name ) : CBaseTempEntity ( name )
{
}
CNetworkHandle ( CBasePlayer , m_hPlayer ) ;
CNetworkVar ( int , m_iEvent ) ;
CNetworkVar ( int , m_nData ) ;
} ;
IMPLEMENT_SERVERCLASS_ST_NOBASE ( CTEPlayerAnimEvent , DT_TEPlayerAnimEvent )
SendPropEHandle ( SENDINFO ( m_hPlayer ) ) ,
SendPropInt ( SENDINFO ( m_iEvent ) , Q_log2 ( PLAYERANIMEVENT_COUNT ) + 1 , SPROP_UNSIGNED ) ,
SendPropInt ( SENDINFO ( m_nData ) , 32 ) ,
END_SEND_TABLE ( )
static CTEPlayerAnimEvent g_TEPlayerAnimEvent ( " PlayerAnimEvent " ) ;
void TE_PlayerAnimEvent ( CBasePlayer * pPlayer , PlayerAnimEvent_t event , int nData )
{
CPVSFilter filter ( ( const Vector & ) pPlayer - > EyePosition ( ) ) ;
g_TEPlayerAnimEvent . m_hPlayer = pPlayer ;
g_TEPlayerAnimEvent . m_iEvent = event ;
g_TEPlayerAnimEvent . m_nData = nData ;
g_TEPlayerAnimEvent . Create ( filter , 0 ) ;
}
//=================================================================================
//
// Ragdoll Entity
//
class CPortalRagdoll : public CBaseAnimatingOverlay , public CDefaultPlayerPickupVPhysics
{
public :
DECLARE_CLASS ( CPortalRagdoll , CBaseAnimatingOverlay ) ;
DECLARE_SERVERCLASS ( ) ;
DECLARE_DATADESC ( ) ;
CPortalRagdoll ( )
{
m_hPlayer . Set ( NULL ) ;
m_vecRagdollOrigin . Init ( ) ;
m_vecRagdollVelocity . Init ( ) ;
}
// Transmit ragdolls to everyone.
virtual int UpdateTransmitState ( )
{
return SetTransmitState ( FL_EDICT_ALWAYS ) ;
}
// In case the client has the player entity, we transmit the player index.
// In case the client doesn't have it, we transmit the player's model index, origin, and angles
// so they can create a ragdoll in the right place.
CNetworkHandle ( CBaseEntity , m_hPlayer ) ; // networked entity handle
CNetworkVector ( m_vecRagdollVelocity ) ;
CNetworkVector ( m_vecRagdollOrigin ) ;
} ;
LINK_ENTITY_TO_CLASS ( portal_ragdoll , CPortalRagdoll ) ;
IMPLEMENT_SERVERCLASS_ST_NOBASE ( CPortalRagdoll , DT_PortalRagdoll )
SendPropVector ( SENDINFO ( m_vecRagdollOrigin ) , - 1 , SPROP_COORD ) ,
SendPropEHandle ( SENDINFO ( m_hPlayer ) ) ,
SendPropModelIndex ( SENDINFO ( m_nModelIndex ) ) ,
SendPropInt ( SENDINFO ( m_nForceBone ) , 8 , 0 ) ,
SendPropVector ( SENDINFO ( m_vecForce ) , - 1 , SPROP_NOSCALE ) ,
SendPropVector ( SENDINFO ( m_vecRagdollVelocity ) ) ,
END_SEND_TABLE ( )
BEGIN_DATADESC ( CPortalRagdoll )
DEFINE_FIELD ( m_vecRagdollOrigin , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_hPlayer , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_vecRagdollVelocity , FIELD_VECTOR ) ,
END_DATADESC ( )
LINK_ENTITY_TO_CLASS ( player , CPortal_Player ) ;
IMPLEMENT_SERVERCLASS_ST ( CPortal_Player , DT_Portal_Player )
SendPropExclude ( " DT_BaseAnimating " , " m_flPlaybackRate " ) ,
SendPropExclude ( " DT_BaseAnimating " , " m_nSequence " ) ,
SendPropExclude ( " DT_BaseAnimating " , " m_nNewSequenceParity " ) ,
SendPropExclude ( " DT_BaseAnimating " , " m_nResetEventsParity " ) ,
SendPropExclude ( " DT_BaseEntity " , " m_angRotation " ) ,
SendPropExclude ( " DT_BaseAnimatingOverlay " , " overlay_vars " ) ,
SendPropExclude ( " DT_BaseFlex " , " m_viewtarget " ) ,
SendPropExclude ( " DT_BaseFlex " , " m_flexWeight " ) ,
SendPropExclude ( " DT_BaseFlex " , " m_blinktoggle " ) ,
// portal_playeranimstate and clientside animation takes care of these on the client
SendPropExclude ( " DT_ServerAnimationData " , " m_flCycle " ) ,
SendPropExclude ( " DT_AnimTimeMustBeFirst " , " m_flAnimTime " ) ,
SendPropAngle ( SENDINFO_VECTORELEM ( m_angEyeAngles , 0 ) , 11 , SPROP_CHANGES_OFTEN ) ,
SendPropAngle ( SENDINFO_VECTORELEM ( m_angEyeAngles , 1 ) , 11 , SPROP_CHANGES_OFTEN ) ,
SendPropEHandle ( SENDINFO ( m_hRagdoll ) ) ,
SendPropInt ( SENDINFO ( m_iSpawnInterpCounter ) , 4 ) ,
SendPropInt ( SENDINFO ( m_iPlayerSoundType ) , 3 ) ,
SendPropBool ( SENDINFO ( m_bHeldObjectOnOppositeSideOfPortal ) ) ,
SendPropEHandle ( SENDINFO ( m_pHeldObjectPortal ) ) ,
SendPropBool ( SENDINFO ( m_bPitchReorientation ) ) ,
SendPropEHandle ( SENDINFO ( m_hPortalEnvironment ) ) ,
SendPropEHandle ( SENDINFO ( m_hSurroundingLiquidPortal ) ) ,
SendPropBool ( SENDINFO ( m_bSuppressingCrosshair ) ) ,
SendPropExclude ( " DT_BaseAnimating " , " m_flPoseParameter " ) ,
END_SEND_TABLE ( )
BEGIN_DATADESC ( CPortal_Player )
DEFINE_SOUNDPATCH ( m_pWooshSound ) ,
DEFINE_FIELD ( m_bHeldObjectOnOppositeSideOfPortal , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_pHeldObjectPortal , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_bIntersectingPortalPlane , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_bStuckOnPortalCollisionObject , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_fTimeLastHurt , FIELD_TIME ) ,
DEFINE_FIELD ( m_StatsThisLevel . iNumPortalsPlaced , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_StatsThisLevel . iNumStepsTaken , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_StatsThisLevel . fNumSecondsTaken , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_fTimeLastNumSecondsUpdate , FIELD_TIME ) ,
DEFINE_FIELD ( m_iNumCamerasDetatched , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_bPitchReorientation , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_bIsRegenerating , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_fNeuroToxinDamageTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_hPortalEnvironment , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_flExpressionLoopTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_iszExpressionScene , FIELD_STRING ) ,
DEFINE_FIELD ( m_hExpressionSceneEnt , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_vecTotalBulletForce , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_bSilentDropAndPickup , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_hRagdoll , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_angEyeAngles , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_iPlayerSoundType , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_qPrePortalledViewAngles , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_bFixEyeAnglesFromPortalling , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_matLastPortalled , FIELD_VMATRIX_WORLDSPACE ) ,
DEFINE_FIELD ( m_vWorldSpaceCenterHolder , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_hSurroundingLiquidPortal , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_bSuppressingCrosshair , FIELD_BOOLEAN ) ,
//DEFINE_FIELD ( m_PlayerAnimState, CPortalPlayerAnimState ),
//DEFINE_FIELD ( m_StatsThisLevel, PortalPlayerStatistics_t ),
DEFINE_EMBEDDEDBYREF ( m_pExpresser ) ,
END_DATADESC ( )
ConVar sv_regeneration_wait_time ( " sv_regeneration_wait_time " , " 1.0 " , FCVAR_REPLICATED ) ;
const char * g_pszChellModel = " models/player/chell.mdl " ;
const char * g_pszPlayerModel = g_pszChellModel ;
# define MAX_COMBINE_MODELS 4
# define MODEL_CHANGE_INTERVAL 5.0f
# define TEAM_CHANGE_INTERVAL 5.0f
# define PORTALPLAYER_PHYSDAMAGE_SCALE 4.0f
extern ConVar sv_turbophysics ;
//----------------------------------------------------
// Player Physics Shadow
//----------------------------------------------------
# define VPHYS_MAX_DISTANCE 2.0
# define VPHYS_MAX_VEL 10
# define VPHYS_MAX_DISTSQR (VPHYS_MAX_DISTANCE*VPHYS_MAX_DISTANCE)
# define VPHYS_MAX_VELSQR (VPHYS_MAX_VEL*VPHYS_MAX_VEL)
extern float IntervalDistance ( float x , float x0 , float x1 ) ;
//disable 'this' : used in base member initializer list
# pragma warning( disable : 4355 )
CPortal_Player : : CPortal_Player ( )
{
m_PlayerAnimState = CreatePortalPlayerAnimState ( this ) ;
CreateExpresser ( ) ;
UseClientSideAnimation ( ) ;
m_angEyeAngles . Init ( ) ;
m_iLastWeaponFireUsercmd = 0 ;
m_iSpawnInterpCounter = 0 ;
m_bHeldObjectOnOppositeSideOfPortal = false ;
m_pHeldObjectPortal = 0 ;
m_bIntersectingPortalPlane = false ;
m_bPitchReorientation = false ;
m_bSilentDropAndPickup = false ;
m_iszExpressionScene = NULL_STRING ;
m_hExpressionSceneEnt = NULL ;
m_flExpressionLoopTime = 0.0f ;
m_bSuppressingCrosshair = false ;
}
CPortal_Player : : ~ CPortal_Player ( void )
{
ClearSceneEvents ( NULL , true ) ;
if ( m_PlayerAnimState )
m_PlayerAnimState - > Release ( ) ;
CPortalRagdoll * pRagdoll = dynamic_cast < CPortalRagdoll * > ( m_hRagdoll . Get ( ) ) ;
if ( pRagdoll )
{
UTIL_Remove ( pRagdoll ) ;
}
}
void CPortal_Player : : UpdateOnRemove ( void )
{
BaseClass : : UpdateOnRemove ( ) ;
}
void CPortal_Player : : Precache ( void )
{
BaseClass : : Precache ( ) ;
PrecacheScriptSound ( " PortalPlayer.EnterPortal " ) ;
PrecacheScriptSound ( " PortalPlayer.ExitPortal " ) ;
PrecacheScriptSound ( " PortalPlayer.Woosh " ) ;
PrecacheScriptSound ( " PortalPlayer.FallRecover " ) ;
PrecacheModel ( " sprites/glow01.vmt " ) ;
//Precache Citizen models
PrecacheModel ( g_pszPlayerModel ) ;
PrecacheModel ( g_pszChellModel ) ;
PrecacheScriptSound ( " NPC_Citizen.die " ) ;
}
void CPortal_Player : : CreateSounds ( )
{
if ( ! m_pWooshSound )
{
CSoundEnvelopeController & controller = CSoundEnvelopeController : : GetController ( ) ;
CPASAttenuationFilter filter ( this ) ;
m_pWooshSound = controller . SoundCreate ( filter , entindex ( ) , " PortalPlayer.Woosh " ) ;
controller . Play ( m_pWooshSound , 0 , 100 ) ;
}
}
void CPortal_Player : : StopLoopingSounds ( )
{
if ( m_pWooshSound )
{
CSoundEnvelopeController & controller = CSoundEnvelopeController : : GetController ( ) ;
controller . SoundDestroy ( m_pWooshSound ) ;
m_pWooshSound = NULL ;
}
BaseClass : : StopLoopingSounds ( ) ;
}
void CPortal_Player : : GiveAllItems ( void )
{
EquipSuit ( ) ;
CBasePlayer : : GiveAmmo ( 255 , " Pistol " ) ;
CBasePlayer : : GiveAmmo ( 32 , " 357 " ) ;
CBasePlayer : : GiveAmmo ( 255 , " AR2 " ) ;
CBasePlayer : : GiveAmmo ( 3 , " AR2AltFire " ) ;
CBasePlayer : : GiveAmmo ( 255 , " SMG1 " ) ;
CBasePlayer : : GiveAmmo ( 3 , " smg1_grenade " ) ;
CBasePlayer : : GiveAmmo ( 255 , " Buckshot " ) ;
CBasePlayer : : GiveAmmo ( 16 , " XBowBolt " ) ;
CBasePlayer : : GiveAmmo ( 3 , " rpg_round " ) ;
CBasePlayer : : GiveAmmo ( 6 , " grenade " ) ;
GiveNamedItem ( " weapon_crowbar " ) ;
GiveNamedItem ( " weapon_physcannon " ) ;
GiveNamedItem ( " weapon_pistol " ) ;
GiveNamedItem ( " weapon_357 " ) ;
GiveNamedItem ( " weapon_smg1 " ) ;
GiveNamedItem ( " weapon_ar2 " ) ;
GiveNamedItem ( " weapon_shotgun " ) ;
GiveNamedItem ( " weapon_crossbow " ) ;
GiveNamedItem ( " weapon_rpg " ) ;
GiveNamedItem ( " weapon_frag " ) ;
GiveNamedItem ( " weapon_bugbait " ) ;
//GiveNamedItem( "weapon_physcannon" );
CWeaponPortalgun * pPortalGun = static_cast < CWeaponPortalgun * > ( GiveNamedItem ( " weapon_portalgun " ) ) ;
if ( ! pPortalGun )
{
pPortalGun = static_cast < CWeaponPortalgun * > ( Weapon_OwnsThisType ( " weapon_portalgun " ) ) ;
}
if ( pPortalGun )
{
pPortalGun - > SetCanFirePortal1 ( ) ;
pPortalGun - > SetCanFirePortal2 ( ) ;
}
}
void CPortal_Player : : GiveDefaultItems ( void )
{
castable_string_t st ( " suit_no_sprint " ) ;
GlobalEntity_SetState ( st , GLOBAL_OFF ) ;
inputdata_t in ;
InputDisableFlashlight ( in ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets specific defaults.
//-----------------------------------------------------------------------------
void CPortal_Player : : Spawn ( void )
{
SetPlayerModel ( ) ;
BaseClass : : Spawn ( ) ;
CreateSounds ( ) ;
pl . deadflag = false ;
RemoveSolidFlags ( FSOLID_NOT_SOLID ) ;
RemoveEffects ( EF_NODRAW ) ;
StopObserverMode ( ) ;
GiveDefaultItems ( ) ;
m_nRenderFX = kRenderNormal ;
m_Local . m_iHideHUD = 0 ;
AddFlag ( FL_ONGROUND ) ; // set the player on the ground at the start of the round.
m_impactEnergyScale = PORTALPLAYER_PHYSDAMAGE_SCALE ;
RemoveFlag ( FL_FROZEN ) ;
m_iSpawnInterpCounter = ( m_iSpawnInterpCounter + 1 ) % 8 ;
m_Local . m_bDucked = false ;
SetPlayerUnderwater ( false ) ;
# ifdef PORTAL_MP
PickTeam ( ) ;
# endif
}
void CPortal_Player : : Activate ( void )
{
BaseClass : : Activate ( ) ;
m_fTimeLastNumSecondsUpdate = gpGlobals - > curtime ;
}
void CPortal_Player : : NotifySystemEvent ( CBaseEntity * pNotify , notify_system_event_t eventType , const notify_system_event_params_t & params )
{
// On teleport, we send event for tracking fling achievements
if ( eventType = = NOTIFY_EVENT_TELEPORT )
{
CProp_Portal * pEnteredPortal = dynamic_cast < CProp_Portal * > ( pNotify ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " portal_player_portaled " ) ;
if ( event )
{
event - > SetInt ( " userid " , GetUserID ( ) ) ;
event - > SetBool ( " portal2 " , pEnteredPortal - > m_bIsPortal2 ) ;
gameeventmanager - > FireEvent ( event ) ;
}
}
BaseClass : : NotifySystemEvent ( pNotify , eventType , params ) ;
}
void CPortal_Player : : OnRestore ( void )
{
BaseClass : : OnRestore ( ) ;
if ( m_pExpresser )
{
m_pExpresser - > SetOuter ( this ) ;
}
}
//bool CPortal_Player::StartObserverMode( int mode )
//{
// //Do nothing.
//
// return false;
//}
bool CPortal_Player : : ValidatePlayerModel ( const char * pModel )
{
if ( ! Q_stricmp ( g_pszPlayerModel , pModel ) )
{
return true ;
}
if ( ! Q_stricmp ( g_pszChellModel , pModel ) )
{
return true ;
}
return false ;
}
void CPortal_Player : : SetPlayerModel ( void )
{
const char * szModelName = NULL ;
const char * pszCurrentModelName = modelinfo - > GetModelName ( GetModel ( ) ) ;
szModelName = engine - > GetClientConVarValue ( engine - > IndexOfEdict ( edict ( ) ) , " cl_playermodel " ) ;
if ( ValidatePlayerModel ( szModelName ) = = false )
{
char szReturnString [ 512 ] ;
if ( ValidatePlayerModel ( pszCurrentModelName ) = = false )
{
pszCurrentModelName = g_pszPlayerModel ;
}
Q_snprintf ( szReturnString , sizeof ( szReturnString ) , " cl_playermodel %s \n " , pszCurrentModelName ) ;
engine - > ClientCommand ( edict ( ) , szReturnString ) ;
szModelName = pszCurrentModelName ;
}
int modelIndex = modelinfo - > GetModelIndex ( szModelName ) ;
if ( modelIndex = = - 1 )
{
szModelName = g_pszPlayerModel ;
char szReturnString [ 512 ] ;
Q_snprintf ( szReturnString , sizeof ( szReturnString ) , " cl_playermodel %s \n " , szModelName ) ;
engine - > ClientCommand ( edict ( ) , szReturnString ) ;
}
SetModel ( szModelName ) ;
m_iPlayerSoundType = ( int ) PLAYER_SOUNDS_CITIZEN ;
}
bool CPortal_Player : : Weapon_Switch ( CBaseCombatWeapon * pWeapon , int viewmodelindex )
{
bool bRet = BaseClass : : Weapon_Switch ( pWeapon , viewmodelindex ) ;
return bRet ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPortal_Player : : UpdateExpression ( void )
{
if ( ! m_pExpresser )
return ;
int iConcept = CONCEPT_CHELL_IDLE ;
if ( GetHealth ( ) < = 0 )
{
iConcept = CONCEPT_CHELL_DEAD ;
}
GetExpresser ( ) - > SetOuter ( this ) ;
ClearExpression ( ) ;
AI_Response * response = SpeakFindResponse ( g_pszChellConcepts [ iConcept ] ) ;
if ( ! response )
{
m_flExpressionLoopTime = gpGlobals - > curtime + RandomFloat ( 30 , 40 ) ;
return ;
}
char const szScene [ 256 ] ;
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 < 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 \n Steps Taken: %d \n Time: %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 ) ;
}