Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2333 lines
64 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Player for Portal.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "portal_player.h"
  8. #include "globalstate.h"
  9. #include "trains.h"
  10. #include "game.h"
  11. #include "portal_player_shared.h"
  12. #include "predicted_viewmodel.h"
  13. #include "in_buttons.h"
  14. #include "portal_gamerules.h"
  15. #include "weapon_portalgun.h"
  16. #include "portal/weapon_physcannon.h"
  17. #include "KeyValues.h"
  18. #include "team.h"
  19. #include "eventqueue.h"
  20. #include "weapon_portalbase.h"
  21. #include "engine/IEngineSound.h"
  22. #include "ai_basenpc.h"
  23. #include "SoundEmitterSystem/isoundemittersystembase.h"
  24. #include "prop_portal_shared.h"
  25. #include "player_pickup.h" // for player pickup code
  26. #include "vphysics/player_controller.h"
  27. #include "datacache/imdlcache.h"
  28. #include "bone_setup.h"
  29. #include "portal_gamestats.h"
  30. #include "physicsshadowclone.h"
  31. #include "physics_prop_ragdoll.h"
  32. #include "soundenvelope.h"
  33. #include "ai_speech.h" // For expressors, vcd playing
  34. #include "sceneentity.h" // has the VCD precache function
  35. // Max mass the player can lift with +use
  36. #define PORTAL_PLAYER_MAX_LIFT_MASS 85
  37. #define PORTAL_PLAYER_MAX_LIFT_SIZE 128
  38. extern CBaseEntity *g_pLastSpawn;
  39. extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse);
  40. // -------------------------------------------------------------------------------- //
  41. // Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
  42. // -------------------------------------------------------------------------------- //
  43. class CTEPlayerAnimEvent : public CBaseTempEntity
  44. {
  45. public:
  46. DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
  47. DECLARE_SERVERCLASS();
  48. CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
  49. {
  50. }
  51. CNetworkHandle( CBasePlayer, m_hPlayer );
  52. CNetworkVar( int, m_iEvent );
  53. CNetworkVar( int, m_nData );
  54. };
  55. IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
  56. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  57. SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
  58. SendPropInt( SENDINFO( m_nData ), 32 ),
  59. END_SEND_TABLE()
  60. static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
  61. void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
  62. {
  63. CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );
  64. g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
  65. g_TEPlayerAnimEvent.m_iEvent = event;
  66. g_TEPlayerAnimEvent.m_nData = nData;
  67. g_TEPlayerAnimEvent.Create( filter, 0 );
  68. }
  69. //=================================================================================
  70. //
  71. // Ragdoll Entity
  72. //
  73. class CPortalRagdoll : public CBaseAnimatingOverlay, public CDefaultPlayerPickupVPhysics
  74. {
  75. public:
  76. DECLARE_CLASS( CPortalRagdoll, CBaseAnimatingOverlay );
  77. DECLARE_SERVERCLASS();
  78. DECLARE_DATADESC();
  79. CPortalRagdoll()
  80. {
  81. m_hPlayer.Set( NULL );
  82. m_vecRagdollOrigin.Init();
  83. m_vecRagdollVelocity.Init();
  84. }
  85. // Transmit ragdolls to everyone.
  86. virtual int UpdateTransmitState()
  87. {
  88. return SetTransmitState( FL_EDICT_ALWAYS );
  89. }
  90. // In case the client has the player entity, we transmit the player index.
  91. // In case the client doesn't have it, we transmit the player's model index, origin, and angles
  92. // so they can create a ragdoll in the right place.
  93. CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
  94. CNetworkVector( m_vecRagdollVelocity );
  95. CNetworkVector( m_vecRagdollOrigin );
  96. };
  97. LINK_ENTITY_TO_CLASS( portal_ragdoll, CPortalRagdoll );
  98. IMPLEMENT_SERVERCLASS_ST_NOBASE( CPortalRagdoll, DT_PortalRagdoll )
  99. SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ),
  100. SendPropEHandle( SENDINFO( m_hPlayer ) ),
  101. SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
  102. SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ),
  103. SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
  104. SendPropVector( SENDINFO( m_vecRagdollVelocity ) ),
  105. END_SEND_TABLE()
  106. BEGIN_DATADESC( CPortalRagdoll )
  107. DEFINE_FIELD( m_vecRagdollOrigin, FIELD_POSITION_VECTOR ),
  108. DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
  109. DEFINE_FIELD( m_vecRagdollVelocity, FIELD_VECTOR ),
  110. END_DATADESC()
  111. LINK_ENTITY_TO_CLASS( player, CPortal_Player );
  112. IMPLEMENT_SERVERCLASS_ST(CPortal_Player, DT_Portal_Player)
  113. SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
  114. SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
  115. SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ),
  116. SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ),
  117. SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
  118. SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
  119. SendPropExclude( "DT_BaseFlex", "m_viewtarget" ),
  120. SendPropExclude( "DT_BaseFlex", "m_flexWeight" ),
  121. SendPropExclude( "DT_BaseFlex", "m_blinktoggle" ),
  122. // portal_playeranimstate and clientside animation takes care of these on the client
  123. SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
  124. SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
  125. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ),
  126. SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ),
  127. SendPropEHandle( SENDINFO( m_hRagdoll ) ),
  128. SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ),
  129. SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ),
  130. SendPropBool( SENDINFO( m_bHeldObjectOnOppositeSideOfPortal) ),
  131. SendPropEHandle( SENDINFO( m_pHeldObjectPortal ) ),
  132. SendPropBool( SENDINFO( m_bPitchReorientation ) ),
  133. SendPropEHandle( SENDINFO( m_hPortalEnvironment ) ),
  134. SendPropEHandle( SENDINFO( m_hSurroundingLiquidPortal ) ),
  135. SendPropBool( SENDINFO( m_bSuppressingCrosshair ) ),
  136. SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
  137. END_SEND_TABLE()
  138. BEGIN_DATADESC( CPortal_Player )
  139. DEFINE_SOUNDPATCH( m_pWooshSound ),
  140. DEFINE_FIELD( m_bHeldObjectOnOppositeSideOfPortal, FIELD_BOOLEAN ),
  141. DEFINE_FIELD( m_pHeldObjectPortal, FIELD_EHANDLE ),
  142. DEFINE_FIELD( m_bIntersectingPortalPlane, FIELD_BOOLEAN ),
  143. DEFINE_FIELD( m_bStuckOnPortalCollisionObject, FIELD_BOOLEAN ),
  144. DEFINE_FIELD( m_fTimeLastHurt, FIELD_TIME ),
  145. DEFINE_FIELD( m_StatsThisLevel.iNumPortalsPlaced, FIELD_INTEGER ),
  146. DEFINE_FIELD( m_StatsThisLevel.iNumStepsTaken, FIELD_INTEGER ),
  147. DEFINE_FIELD( m_StatsThisLevel.fNumSecondsTaken, FIELD_FLOAT ),
  148. DEFINE_FIELD( m_fTimeLastNumSecondsUpdate, FIELD_TIME ),
  149. DEFINE_FIELD( m_iNumCamerasDetatched, FIELD_INTEGER ),
  150. DEFINE_FIELD( m_bPitchReorientation, FIELD_BOOLEAN ),
  151. DEFINE_FIELD( m_bIsRegenerating, FIELD_BOOLEAN ),
  152. DEFINE_FIELD( m_fNeuroToxinDamageTime, FIELD_TIME ),
  153. DEFINE_FIELD( m_hPortalEnvironment, FIELD_EHANDLE ),
  154. DEFINE_FIELD( m_flExpressionLoopTime, FIELD_TIME ),
  155. DEFINE_FIELD( m_iszExpressionScene, FIELD_STRING ),
  156. DEFINE_FIELD( m_hExpressionSceneEnt, FIELD_EHANDLE ),
  157. DEFINE_FIELD( m_vecTotalBulletForce, FIELD_VECTOR ),
  158. DEFINE_FIELD( m_bSilentDropAndPickup, FIELD_BOOLEAN ),
  159. DEFINE_FIELD( m_hRagdoll, FIELD_EHANDLE ),
  160. DEFINE_FIELD( m_angEyeAngles, FIELD_VECTOR ),
  161. DEFINE_FIELD( m_iPlayerSoundType, FIELD_INTEGER ),
  162. DEFINE_FIELD( m_qPrePortalledViewAngles, FIELD_VECTOR ),
  163. DEFINE_FIELD( m_bFixEyeAnglesFromPortalling, FIELD_BOOLEAN ),
  164. DEFINE_FIELD( m_matLastPortalled, FIELD_VMATRIX_WORLDSPACE ),
  165. DEFINE_FIELD( m_vWorldSpaceCenterHolder, FIELD_POSITION_VECTOR ),
  166. DEFINE_FIELD( m_hSurroundingLiquidPortal, FIELD_EHANDLE ),
  167. DEFINE_FIELD( m_bSuppressingCrosshair, FIELD_BOOLEAN ),
  168. //DEFINE_FIELD ( m_PlayerAnimState, CPortalPlayerAnimState ),
  169. //DEFINE_FIELD ( m_StatsThisLevel, PortalPlayerStatistics_t ),
  170. DEFINE_EMBEDDEDBYREF( m_pExpresser ),
  171. END_DATADESC()
  172. ConVar sv_regeneration_wait_time ("sv_regeneration_wait_time", "1.0", FCVAR_REPLICATED );
  173. const char *g_pszChellModel = "models/player/chell.mdl";
  174. const char *g_pszPlayerModel = g_pszChellModel;
  175. #define MAX_COMBINE_MODELS 4
  176. #define MODEL_CHANGE_INTERVAL 5.0f
  177. #define TEAM_CHANGE_INTERVAL 5.0f
  178. #define PORTALPLAYER_PHYSDAMAGE_SCALE 4.0f
  179. extern ConVar sv_turbophysics;
  180. //----------------------------------------------------
  181. // Player Physics Shadow
  182. //----------------------------------------------------
  183. #define VPHYS_MAX_DISTANCE 2.0
  184. #define VPHYS_MAX_VEL 10
  185. #define VPHYS_MAX_DISTSQR (VPHYS_MAX_DISTANCE*VPHYS_MAX_DISTANCE)
  186. #define VPHYS_MAX_VELSQR (VPHYS_MAX_VEL*VPHYS_MAX_VEL)
  187. extern float IntervalDistance( float x, float x0, float x1 );
  188. //disable 'this' : used in base member initializer list
  189. #pragma warning( disable : 4355 )
  190. CPortal_Player::CPortal_Player()
  191. {
  192. m_PlayerAnimState = CreatePortalPlayerAnimState( this );
  193. CreateExpresser();
  194. UseClientSideAnimation();
  195. m_angEyeAngles.Init();
  196. m_iLastWeaponFireUsercmd = 0;
  197. m_iSpawnInterpCounter = 0;
  198. m_bHeldObjectOnOppositeSideOfPortal = false;
  199. m_pHeldObjectPortal = 0;
  200. m_bIntersectingPortalPlane = false;
  201. m_bPitchReorientation = false;
  202. m_bSilentDropAndPickup = false;
  203. m_iszExpressionScene = NULL_STRING;
  204. m_hExpressionSceneEnt = NULL;
  205. m_flExpressionLoopTime = 0.0f;
  206. m_bSuppressingCrosshair = false;
  207. }
  208. CPortal_Player::~CPortal_Player( void )
  209. {
  210. ClearSceneEvents( NULL, true );
  211. if ( m_PlayerAnimState )
  212. m_PlayerAnimState->Release();
  213. CPortalRagdoll *pRagdoll = dynamic_cast<CPortalRagdoll*>( m_hRagdoll.Get() );
  214. if( pRagdoll )
  215. {
  216. UTIL_Remove( pRagdoll );
  217. }
  218. }
  219. void CPortal_Player::UpdateOnRemove( void )
  220. {
  221. BaseClass::UpdateOnRemove();
  222. }
  223. void CPortal_Player::Precache( void )
  224. {
  225. BaseClass::Precache();
  226. PrecacheScriptSound( "PortalPlayer.EnterPortal" );
  227. PrecacheScriptSound( "PortalPlayer.ExitPortal" );
  228. PrecacheScriptSound( "PortalPlayer.Woosh" );
  229. PrecacheScriptSound( "PortalPlayer.FallRecover" );
  230. PrecacheModel ( "sprites/glow01.vmt" );
  231. //Precache Citizen models
  232. PrecacheModel( g_pszPlayerModel );
  233. PrecacheModel( g_pszChellModel );
  234. PrecacheScriptSound( "NPC_Citizen.die" );
  235. }
  236. void CPortal_Player::CreateSounds()
  237. {
  238. if ( !m_pWooshSound )
  239. {
  240. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  241. CPASAttenuationFilter filter( this );
  242. m_pWooshSound = controller.SoundCreate( filter, entindex(), "PortalPlayer.Woosh" );
  243. controller.Play( m_pWooshSound, 0, 100 );
  244. }
  245. }
  246. void CPortal_Player::StopLoopingSounds()
  247. {
  248. if ( m_pWooshSound )
  249. {
  250. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  251. controller.SoundDestroy( m_pWooshSound );
  252. m_pWooshSound = NULL;
  253. }
  254. BaseClass::StopLoopingSounds();
  255. }
  256. void CPortal_Player::GiveAllItems( void )
  257. {
  258. EquipSuit();
  259. CBasePlayer::GiveAmmo( 255, "Pistol");
  260. CBasePlayer::GiveAmmo( 32, "357" );
  261. CBasePlayer::GiveAmmo( 255, "AR2" );
  262. CBasePlayer::GiveAmmo( 3, "AR2AltFire" );
  263. CBasePlayer::GiveAmmo( 255, "SMG1");
  264. CBasePlayer::GiveAmmo( 3, "smg1_grenade");
  265. CBasePlayer::GiveAmmo( 255, "Buckshot");
  266. CBasePlayer::GiveAmmo( 16, "XBowBolt" );
  267. CBasePlayer::GiveAmmo( 3, "rpg_round");
  268. CBasePlayer::GiveAmmo( 6, "grenade" );
  269. GiveNamedItem( "weapon_crowbar" );
  270. GiveNamedItem( "weapon_physcannon" );
  271. GiveNamedItem( "weapon_pistol" );
  272. GiveNamedItem( "weapon_357" );
  273. GiveNamedItem( "weapon_smg1" );
  274. GiveNamedItem( "weapon_ar2" );
  275. GiveNamedItem( "weapon_shotgun" );
  276. GiveNamedItem( "weapon_crossbow" );
  277. GiveNamedItem( "weapon_rpg" );
  278. GiveNamedItem( "weapon_frag" );
  279. GiveNamedItem( "weapon_bugbait" );
  280. //GiveNamedItem( "weapon_physcannon" );
  281. CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( GiveNamedItem( "weapon_portalgun" ) );
  282. if ( !pPortalGun )
  283. {
  284. pPortalGun = static_cast<CWeaponPortalgun*>( Weapon_OwnsThisType( "weapon_portalgun" ) );
  285. }
  286. if ( pPortalGun )
  287. {
  288. pPortalGun->SetCanFirePortal1();
  289. pPortalGun->SetCanFirePortal2();
  290. }
  291. }
  292. void CPortal_Player::GiveDefaultItems( void )
  293. {
  294. castable_string_t st( "suit_no_sprint" );
  295. GlobalEntity_SetState( st, GLOBAL_OFF );
  296. inputdata_t in;
  297. InputDisableFlashlight( in );
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Purpose: Sets specific defaults.
  301. //-----------------------------------------------------------------------------
  302. void CPortal_Player::Spawn(void)
  303. {
  304. SetPlayerModel();
  305. BaseClass::Spawn();
  306. CreateSounds();
  307. pl.deadflag = false;
  308. RemoveSolidFlags( FSOLID_NOT_SOLID );
  309. RemoveEffects( EF_NODRAW );
  310. StopObserverMode();
  311. GiveDefaultItems();
  312. m_nRenderFX = kRenderNormal;
  313. m_Local.m_iHideHUD = 0;
  314. AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round.
  315. m_impactEnergyScale = PORTALPLAYER_PHYSDAMAGE_SCALE;
  316. RemoveFlag( FL_FROZEN );
  317. m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8;
  318. m_Local.m_bDucked = false;
  319. SetPlayerUnderwater(false);
  320. #ifdef PORTAL_MP
  321. PickTeam();
  322. #endif
  323. }
  324. void CPortal_Player::Activate( void )
  325. {
  326. BaseClass::Activate();
  327. m_fTimeLastNumSecondsUpdate = gpGlobals->curtime;
  328. }
  329. void CPortal_Player::NotifySystemEvent(CBaseEntity *pNotify, notify_system_event_t eventType, const notify_system_event_params_t &params )
  330. {
  331. // On teleport, we send event for tracking fling achievements
  332. if ( eventType == NOTIFY_EVENT_TELEPORT )
  333. {
  334. CProp_Portal *pEnteredPortal = dynamic_cast<CProp_Portal*>( pNotify );
  335. IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_portaled" );
  336. if ( event )
  337. {
  338. event->SetInt( "userid", GetUserID() );
  339. event->SetBool( "portal2", pEnteredPortal->m_bIsPortal2 );
  340. gameeventmanager->FireEvent( event );
  341. }
  342. }
  343. BaseClass::NotifySystemEvent( pNotify, eventType, params );
  344. }
  345. void CPortal_Player::OnRestore( void )
  346. {
  347. BaseClass::OnRestore();
  348. if ( m_pExpresser )
  349. {
  350. m_pExpresser->SetOuter ( this );
  351. }
  352. }
  353. //bool CPortal_Player::StartObserverMode( int mode )
  354. //{
  355. // //Do nothing.
  356. //
  357. // return false;
  358. //}
  359. bool CPortal_Player::ValidatePlayerModel( const char *pModel )
  360. {
  361. if ( !Q_stricmp( g_pszPlayerModel, pModel ) )
  362. {
  363. return true;
  364. }
  365. if ( !Q_stricmp( g_pszChellModel, pModel ) )
  366. {
  367. return true;
  368. }
  369. return false;
  370. }
  371. void CPortal_Player::SetPlayerModel( void )
  372. {
  373. const char *szModelName = NULL;
  374. const char *pszCurrentModelName = modelinfo->GetModelName( GetModel());
  375. szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" );
  376. if ( ValidatePlayerModel( szModelName ) == false )
  377. {
  378. char szReturnString[512];
  379. if ( ValidatePlayerModel( pszCurrentModelName ) == false )
  380. {
  381. pszCurrentModelName = g_pszPlayerModel;
  382. }
  383. Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName );
  384. engine->ClientCommand ( edict(), szReturnString );
  385. szModelName = pszCurrentModelName;
  386. }
  387. int modelIndex = modelinfo->GetModelIndex( szModelName );
  388. if ( modelIndex == -1 )
  389. {
  390. szModelName = g_pszPlayerModel;
  391. char szReturnString[512];
  392. Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName );
  393. engine->ClientCommand ( edict(), szReturnString );
  394. }
  395. SetModel( szModelName );
  396. m_iPlayerSoundType = (int)PLAYER_SOUNDS_CITIZEN;
  397. }
  398. bool CPortal_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex )
  399. {
  400. bool bRet = BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
  401. return bRet;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose:
  405. //-----------------------------------------------------------------------------
  406. void CPortal_Player::UpdateExpression( void )
  407. {
  408. if ( !m_pExpresser )
  409. return;
  410. int iConcept = CONCEPT_CHELL_IDLE;
  411. if ( GetHealth() <= 0 )
  412. {
  413. iConcept = CONCEPT_CHELL_DEAD;
  414. }
  415. GetExpresser()->SetOuter( this );
  416. ClearExpression();
  417. AI_Response response;
  418. bool result = SpeakFindResponse( response, g_pszChellConcepts[iConcept] );
  419. if ( !result )
  420. {
  421. m_flExpressionLoopTime = gpGlobals->curtime + RandomFloat(30,40);
  422. return;
  423. }
  424. char const *szScene = response.GetResponsePtr();
  425. // Ignore updates that choose the same scene
  426. if ( m_iszExpressionScene != NULL_STRING && stricmp( STRING(m_iszExpressionScene), szScene ) == 0 )
  427. return;
  428. if ( m_hExpressionSceneEnt )
  429. {
  430. ClearExpression();
  431. }
  432. m_iszExpressionScene = AllocPooledString( szScene );
  433. float flDuration = InstancedScriptedScene( this, szScene, &m_hExpressionSceneEnt, 0.0, true, NULL );
  434. m_flExpressionLoopTime = gpGlobals->curtime + flDuration;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. //-----------------------------------------------------------------------------
  439. void CPortal_Player::ClearExpression( void )
  440. {
  441. if ( m_hExpressionSceneEnt != NULL )
  442. {
  443. StopScriptedScene( this, m_hExpressionSceneEnt );
  444. }
  445. m_flExpressionLoopTime = gpGlobals->curtime;
  446. }
  447. void CPortal_Player::PreThink( void )
  448. {
  449. QAngle vOldAngles = GetLocalAngles();
  450. QAngle vTempAngles = GetLocalAngles();
  451. vTempAngles = EyeAngles();
  452. if ( vTempAngles[PITCH] > 180.0f )
  453. {
  454. vTempAngles[PITCH] -= 360.0f;
  455. }
  456. SetLocalAngles( vTempAngles );
  457. BaseClass::PreThink();
  458. if( (m_afButtonPressed & IN_JUMP) )
  459. {
  460. Jump();
  461. }
  462. //Reset bullet force accumulator, only lasts one frame
  463. m_vecTotalBulletForce = vec3_origin;
  464. SetLocalAngles( vOldAngles );
  465. }
  466. void CPortal_Player::PostThink( void )
  467. {
  468. BaseClass::PostThink();
  469. // Store the eye angles pitch so the client can compute its animation state correctly.
  470. m_angEyeAngles = EyeAngles();
  471. QAngle angles = GetLocalAngles();
  472. angles[PITCH] = 0;
  473. SetLocalAngles( angles );
  474. // Regenerate heath after 3 seconds
  475. if ( IsAlive() && GetHealth() < GetMaxHealth() )
  476. {
  477. // Color to overlay on the screen while the player is taking damage
  478. color32 hurtScreenOverlay = {64,0,0,64};
  479. if ( gpGlobals->curtime > m_fTimeLastHurt + sv_regeneration_wait_time.GetFloat() )
  480. {
  481. TakeHealth( 1, DMG_GENERIC );
  482. m_bIsRegenerating = true;
  483. if ( GetHealth() >= GetMaxHealth() )
  484. {
  485. m_bIsRegenerating = false;
  486. }
  487. }
  488. else
  489. {
  490. m_bIsRegenerating = false;
  491. UTIL_ScreenFade( this, hurtScreenOverlay, 1.0f, 0.1f, FFADE_IN|FFADE_PURGE );
  492. }
  493. }
  494. UpdatePortalPlaneSounds();
  495. UpdateWooshSounds();
  496. m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
  497. if ( IsAlive() && m_flExpressionLoopTime >= 0 && gpGlobals->curtime > m_flExpressionLoopTime )
  498. {
  499. // Random expressions need to be cleared, because they don't loop. So if we
  500. // pick the same one again, we want to restart it.
  501. ClearExpression();
  502. m_iszExpressionScene = NULL_STRING;
  503. UpdateExpression();
  504. }
  505. UpdateSecondsTaken();
  506. // Try to fix the player if they're stuck
  507. if ( m_bStuckOnPortalCollisionObject )
  508. {
  509. Vector vForward = ((CProp_Portal*)m_hPortalEnvironment.Get())->m_vPrevForward;
  510. Vector vNewPos = GetAbsOrigin() + vForward * gpGlobals->frametime * -1000.0f;
  511. Teleport( &vNewPos, NULL, &vForward );
  512. m_bStuckOnPortalCollisionObject = false;
  513. }
  514. }
  515. void CPortal_Player::PlayerDeathThink(void)
  516. {
  517. float flForward;
  518. SetNextThink( gpGlobals->curtime + 0.1f );
  519. if (GetFlags() & FL_ONGROUND)
  520. {
  521. flForward = GetAbsVelocity().Length() - 20;
  522. if (flForward <= 0)
  523. {
  524. SetAbsVelocity( vec3_origin );
  525. }
  526. else
  527. {
  528. Vector vecNewVelocity = GetAbsVelocity();
  529. VectorNormalize( vecNewVelocity );
  530. vecNewVelocity *= flForward;
  531. SetAbsVelocity( vecNewVelocity );
  532. }
  533. }
  534. if ( HasWeapons() )
  535. {
  536. // we drop the guns here because weapons that have an area effect and can kill their user
  537. // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
  538. // player class sometimes is freed. It's safer to manipulate the weapons once we know
  539. // we aren't calling into any of their code anymore through the player pointer.
  540. PackDeadPlayerItems();
  541. }
  542. if (GetModelIndex() && (!IsSequenceFinished()) && (m_lifeState == LIFE_DYING))
  543. {
  544. StudioFrameAdvance( );
  545. m_iRespawnFrames++;
  546. if ( m_iRespawnFrames < 60 ) // animations should be no longer than this
  547. return;
  548. }
  549. if (m_lifeState == LIFE_DYING)
  550. m_lifeState = LIFE_DEAD;
  551. StopAnimation();
  552. IncrementInterpolationFrame();
  553. m_flPlaybackRate = 0.0;
  554. int fAnyButtonDown = (m_nButtons & ~IN_SCORE);
  555. // Strip out the duck key from this check if it's toggled
  556. if ( (fAnyButtonDown & IN_DUCK) && GetToggledDuckState())
  557. {
  558. fAnyButtonDown &= ~IN_DUCK;
  559. }
  560. // wait for all buttons released
  561. if ( m_lifeState == LIFE_DEAD )
  562. {
  563. if ( fAnyButtonDown || gpGlobals->curtime < m_flDeathTime + DEATH_ANIMATION_TIME )
  564. return;
  565. if ( g_pGameRules->FPlayerCanRespawn( this ) )
  566. {
  567. m_lifeState = LIFE_RESPAWNABLE;
  568. }
  569. return;
  570. }
  571. // if the player has been dead for one second longer than allowed by forcerespawn,
  572. // forcerespawn isn't on. Send the player off to an intermission camera until they
  573. // choose to respawn.
  574. if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->curtime > (m_flDeathTime + DEATH_ANIMATION_TIME) ) && !IsObserver() )
  575. {
  576. // go to dead camera.
  577. StartObserverMode( m_iObserverLastMode );
  578. }
  579. // wait for any button down, or mp_forcerespawn is set and the respawn time is up
  580. if (!fAnyButtonDown
  581. && !( g_pGameRules->IsMultiplayer() && forcerespawn.GetInt() > 0 && (gpGlobals->curtime > (m_flDeathTime + 5))) )
  582. return;
  583. m_nButtons = 0;
  584. m_iRespawnFrames = 0;
  585. //Msg( "Respawn\n");
  586. respawn( this, !IsObserver() );// don't copy a corpse if we're in deathcam.
  587. SetNextThink( TICK_NEVER_THINK );
  588. }
  589. void CPortal_Player::UpdatePortalPlaneSounds( void )
  590. {
  591. CProp_Portal *pPortal = m_hPortalEnvironment;
  592. if ( pPortal && pPortal->m_bActivated )
  593. {
  594. Vector vVelocity;
  595. GetVelocity( &vVelocity, NULL );
  596. if ( !vVelocity.IsZero() )
  597. {
  598. Vector vMin, vMax;
  599. CollisionProp()->WorldSpaceAABB( &vMin, &vMax );
  600. Vector vEarCenter = ( vMax + vMin ) / 2.0f;
  601. Vector vDiagonal = vMax - vMin;
  602. if ( !m_bIntersectingPortalPlane )
  603. {
  604. vDiagonal *= 0.25f;
  605. if ( UTIL_IsBoxIntersectingPortal( vEarCenter, vDiagonal, pPortal ) )
  606. {
  607. m_bIntersectingPortalPlane = true;
  608. CPASAttenuationFilter filter( this );
  609. CSoundParameters params;
  610. if ( GetParametersForSound( "PortalPlayer.EnterPortal", params, NULL ) )
  611. {
  612. EmitSound_t ep( params );
  613. ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
  614. ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
  615. EmitSound( filter, entindex(), ep );
  616. }
  617. }
  618. }
  619. else
  620. {
  621. vDiagonal *= 0.30f;
  622. if ( !UTIL_IsBoxIntersectingPortal( vEarCenter, vDiagonal, pPortal ) )
  623. {
  624. m_bIntersectingPortalPlane = false;
  625. CPASAttenuationFilter filter( this );
  626. CSoundParameters params;
  627. if ( GetParametersForSound( "PortalPlayer.ExitPortal", params, NULL ) )
  628. {
  629. EmitSound_t ep( params );
  630. ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
  631. ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
  632. EmitSound( filter, entindex(), ep );
  633. }
  634. }
  635. }
  636. }
  637. }
  638. else if ( m_bIntersectingPortalPlane )
  639. {
  640. m_bIntersectingPortalPlane = false;
  641. CPASAttenuationFilter filter( this );
  642. CSoundParameters params;
  643. if ( GetParametersForSound( "PortalPlayer.ExitPortal", params, NULL ) )
  644. {
  645. EmitSound_t ep( params );
  646. Vector vVelocity;
  647. GetVelocity( &vVelocity, NULL );
  648. ep.m_nPitch = 80.0f + vVelocity.Length() * 0.03f;
  649. ep.m_flVolume = MIN( 0.3f + vVelocity.Length() * 0.00075f, 1.0f );
  650. EmitSound( filter, entindex(), ep );
  651. }
  652. }
  653. }
  654. void CPortal_Player::UpdateWooshSounds( void )
  655. {
  656. if ( m_pWooshSound )
  657. {
  658. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  659. float fWooshVolume = GetAbsVelocity().Length() - MIN_FLING_SPEED;
  660. if ( fWooshVolume < 0.0f )
  661. {
  662. controller.SoundChangeVolume( m_pWooshSound, 0.0f, 0.1f );
  663. return;
  664. }
  665. fWooshVolume /= 2000.0f;
  666. if ( fWooshVolume > 1.0f )
  667. fWooshVolume = 1.0f;
  668. controller.SoundChangeVolume( m_pWooshSound, fWooshVolume, 0.1f );
  669. // controller.SoundChangePitch( m_pWooshSound, fWooshVolume + 0.5f, 0.1f );
  670. }
  671. }
  672. void CPortal_Player::FireBullets ( const FireBulletsInfo_t &info )
  673. {
  674. NoteWeaponFired();
  675. BaseClass::FireBullets( info );
  676. }
  677. void CPortal_Player::NoteWeaponFired( void )
  678. {
  679. Assert( m_pCurrentCommand );
  680. if( m_pCurrentCommand )
  681. {
  682. m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
  683. }
  684. }
  685. extern ConVar sv_maxunlag;
  686. bool CPortal_Player::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
  687. {
  688. // No need to lag compensate at all if we're not attacking in this command and
  689. // we haven't attacked recently.
  690. if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) )
  691. return false;
  692. // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it.
  693. if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) )
  694. return false;
  695. const Vector &vMyOrigin = GetAbsOrigin();
  696. const Vector &vHisOrigin = pPlayer->GetAbsOrigin();
  697. // get max distance player could have moved within max lag compensation time,
  698. // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value)
  699. float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat();
  700. // If the player is within this distance, lag compensate them in case they're running past us.
  701. if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance )
  702. return true;
  703. // If their origin is not within a 45 degree cone in front of us, no need to lag compensate.
  704. Vector vForward;
  705. AngleVectors( pCmd->viewangles, &vForward );
  706. Vector vDiff = vHisOrigin - vMyOrigin;
  707. VectorNormalize( vDiff );
  708. float flCosAngle = 0.707107f; // 45 degree angle
  709. if ( vForward.Dot( vDiff ) < flCosAngle )
  710. return false;
  711. return true;
  712. }
  713. void CPortal_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
  714. {
  715. m_PlayerAnimState->DoAnimationEvent( event, nData );
  716. TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
  717. }
  718. //-----------------------------------------------------------------------------
  719. // Purpose: Override setup bones so that is uses the render angles from
  720. // the Portal animation state to setup the hitboxes.
  721. //-----------------------------------------------------------------------------
  722. void CPortal_Player::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
  723. {
  724. VPROF_BUDGET( "CBaseAnimating::SetupBones", VPROF_BUDGETGROUP_SERVER_ANIM );
  725. // Set the mdl cache semaphore.
  726. MDLCACHE_CRITICAL_SECTION();
  727. // Get the studio header.
  728. Assert( GetModelPtr() );
  729. CStudioHdr *pStudioHdr = GetModelPtr( );
  730. Vector pos[MAXSTUDIOBONES];
  731. Quaternion q[MAXSTUDIOBONES];
  732. // Adjust hit boxes based on IK driven offset.
  733. Vector adjOrigin = GetAbsOrigin() + Vector( 0, 0, m_flEstIkOffset );
  734. // FIXME: pass this into Studio_BuildMatrices to skip transforms
  735. CBoneBitList boneComputed;
  736. if ( m_pIk )
  737. {
  738. m_iIKCounter++;
  739. m_pIk->Init( pStudioHdr, GetAbsAngles(), adjOrigin, gpGlobals->curtime, m_iIKCounter, boneMask );
  740. GetSkeleton( pStudioHdr, pos, q, boneMask );
  741. m_pIk->UpdateTargets( pos, q, pBoneToWorld, boneComputed );
  742. CalculateIKLocks( gpGlobals->curtime );
  743. m_pIk->SolveDependencies( pos, q, pBoneToWorld, boneComputed );
  744. }
  745. else
  746. {
  747. GetSkeleton( pStudioHdr, pos, q, boneMask );
  748. }
  749. CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );
  750. if ( pParent )
  751. {
  752. // We're doing bone merging, so do special stuff here.
  753. CBoneCache *pParentCache = pParent->GetBoneCache();
  754. if ( pParentCache )
  755. {
  756. BuildMatricesWithBoneMerge(
  757. pStudioHdr,
  758. m_PlayerAnimState->GetRenderAngles(),
  759. adjOrigin,
  760. pos,
  761. q,
  762. pBoneToWorld,
  763. pParent,
  764. pParentCache );
  765. return;
  766. }
  767. }
  768. Studio_BuildMatrices(
  769. pStudioHdr,
  770. m_PlayerAnimState->GetRenderAngles(),
  771. adjOrigin,
  772. pos,
  773. q,
  774. -1,
  775. GetModelScale(), // Scaling
  776. pBoneToWorld,
  777. boneMask );
  778. }
  779. // Set the activity based on an event or current state
  780. void CPortal_Player::SetAnimation( PLAYER_ANIM playerAnim )
  781. {
  782. return;
  783. }
  784. CAI_Expresser *CPortal_Player::CreateExpresser()
  785. {
  786. Assert( !m_pExpresser );
  787. if ( m_pExpresser )
  788. {
  789. delete m_pExpresser;
  790. }
  791. m_pExpresser = new CAI_Expresser(this);
  792. if ( !m_pExpresser)
  793. {
  794. return NULL;
  795. }
  796. m_pExpresser->Connect(this);
  797. return m_pExpresser;
  798. }
  799. //-----------------------------------------------------------------------------
  800. CAI_Expresser *CPortal_Player::GetExpresser()
  801. {
  802. if ( m_pExpresser )
  803. {
  804. m_pExpresser->Connect(this);
  805. }
  806. return m_pExpresser;
  807. }
  808. extern int gEvilImpulse101;
  809. //-----------------------------------------------------------------------------
  810. // Purpose: Player reacts to bumping a weapon.
  811. // Input : pWeapon - the weapon that the player bumped into.
  812. // Output : Returns true if player picked up the weapon
  813. //-----------------------------------------------------------------------------
  814. bool CPortal_Player::BumpWeapon( CBaseCombatWeapon *pWeapon )
  815. {
  816. CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
  817. // Can I have this weapon type?
  818. if ( !IsAllowedToPickupWeapons() )
  819. return false;
  820. if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
  821. {
  822. if ( gEvilImpulse101 )
  823. {
  824. UTIL_Remove( pWeapon );
  825. }
  826. return false;
  827. }
  828. // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows)
  829. if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) )
  830. {
  831. return false;
  832. }
  833. CWeaponPortalgun *pPickupPortalgun = dynamic_cast<CWeaponPortalgun*>( pWeapon );
  834. bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType());
  835. if ( bOwnsWeaponAlready == true )
  836. {
  837. // If we picked up a second portal gun set the bool to alow secondary fire
  838. if ( pPickupPortalgun )
  839. {
  840. CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( Weapon_OwnsThisType( pWeapon->GetClassname() ) );
  841. if ( pPickupPortalgun->CanFirePortal1() )
  842. pPortalGun->SetCanFirePortal1();
  843. if ( pPickupPortalgun->CanFirePortal2() )
  844. pPortalGun->SetCanFirePortal2();
  845. UTIL_Remove( pWeapon );
  846. return true;
  847. }
  848. //If we have room for the ammo, then "take" the weapon too.
  849. if ( Weapon_EquipAmmoOnly( pWeapon ) )
  850. {
  851. pWeapon->CheckRespawn();
  852. UTIL_Remove( pWeapon );
  853. return true;
  854. }
  855. else
  856. {
  857. return false;
  858. }
  859. }
  860. pWeapon->CheckRespawn();
  861. Weapon_Equip( pWeapon );
  862. // If we're holding and object before picking up portalgun, drop it
  863. if ( pPickupPortalgun )
  864. {
  865. ForceDropOfCarriedPhysObjects( GetPlayerHeldEntity( this ) );
  866. }
  867. return true;
  868. }
  869. void CPortal_Player::ShutdownUseEntity( void )
  870. {
  871. ShutdownPickupController( m_hUseEntity );
  872. }
  873. const Vector& CPortal_Player::WorldSpaceCenter( ) const
  874. {
  875. m_vWorldSpaceCenterHolder = GetAbsOrigin();
  876. m_vWorldSpaceCenterHolder.z += ( (IsDucked()) ? (VEC_DUCK_HULL_MAX_SCALED( this ).z) : (VEC_HULL_MAX_SCALED( this ).z) ) * 0.5f;
  877. return m_vWorldSpaceCenterHolder;
  878. }
  879. void CPortal_Player::Teleport( const Vector *newPosition, const QAngle *newAngles, const Vector *newVelocity )
  880. {
  881. Vector oldOrigin = GetLocalOrigin();
  882. QAngle oldAngles = GetLocalAngles();
  883. BaseClass::Teleport( newPosition, newAngles, newVelocity );
  884. m_angEyeAngles = pl.v_angle;
  885. m_PlayerAnimState->Teleport( newPosition, newAngles, this );
  886. }
  887. void CPortal_Player::VPhysicsShadowUpdate( IPhysicsObject *pPhysics )
  888. {
  889. if( m_hPortalEnvironment.Get() == NULL )
  890. return BaseClass::VPhysicsShadowUpdate( pPhysics );
  891. //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
  892. if ( sv_turbophysics.GetBool() )
  893. return;
  894. Vector newPosition;
  895. bool physicsUpdated = m_pPhysicsController->GetShadowPosition( &newPosition, NULL ) > 0 ? true : false;
  896. // UNDONE: If the player is penetrating, but the player's game collisions are not stuck, teleport the physics shadow to the game position
  897. if ( pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING )
  898. {
  899. CUtlVector<CBaseEntity *> list;
  900. PhysGetListOfPenetratingEntities( this, list );
  901. for ( int i = list.Count()-1; i >= 0; --i )
  902. {
  903. // filter out anything that isn't simulated by vphysics
  904. // UNDONE: Filter out motion disabled objects?
  905. if ( list[i]->GetMoveType() == MOVETYPE_VPHYSICS )
  906. {
  907. // I'm currently stuck inside a moving object, so allow vphysics to
  908. // apply velocity to the player in order to separate these objects
  909. m_touchedPhysObject = true;
  910. }
  911. }
  912. }
  913. if ( m_pPhysicsController->IsInContact() || (m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER) )
  914. {
  915. m_touchedPhysObject = true;
  916. }
  917. if ( IsFollowingPhysics() )
  918. {
  919. m_touchedPhysObject = true;
  920. }
  921. if ( GetMoveType() == MOVETYPE_NOCLIP )
  922. {
  923. m_oldOrigin = GetAbsOrigin();
  924. return;
  925. }
  926. if ( phys_timescale.GetFloat() == 0.0f )
  927. {
  928. physicsUpdated = false;
  929. }
  930. if ( !physicsUpdated )
  931. return;
  932. IPhysicsObject *pPhysGround = GetGroundVPhysics();
  933. Vector newVelocity;
  934. pPhysics->GetPosition( &newPosition, 0 );
  935. m_pPhysicsController->GetShadowVelocity( &newVelocity );
  936. Vector tmp = GetAbsOrigin() - newPosition;
  937. if ( !m_touchedPhysObject && !(GetFlags() & FL_ONGROUND) )
  938. {
  939. tmp.z *= 0.5f; // don't care about z delta as much
  940. }
  941. float dist = tmp.LengthSqr();
  942. float deltaV = (newVelocity - GetAbsVelocity()).LengthSqr();
  943. float maxDistErrorSqr = VPHYS_MAX_DISTSQR;
  944. float maxVelErrorSqr = VPHYS_MAX_VELSQR;
  945. if ( IsRideablePhysics(pPhysGround) )
  946. {
  947. maxDistErrorSqr *= 0.25;
  948. maxVelErrorSqr *= 0.25;
  949. }
  950. if ( dist >= maxDistErrorSqr || deltaV >= maxVelErrorSqr || (pPhysGround && !m_touchedPhysObject) )
  951. {
  952. if ( m_touchedPhysObject || pPhysGround )
  953. {
  954. // BUGBUG: Rewrite this code using fixed timestep
  955. if ( deltaV >= maxVelErrorSqr )
  956. {
  957. Vector dir = GetAbsVelocity();
  958. float len = VectorNormalize(dir);
  959. float dot = DotProduct( newVelocity, dir );
  960. if ( dot > len )
  961. {
  962. dot = len;
  963. }
  964. else if ( dot < -len )
  965. {
  966. dot = -len;
  967. }
  968. VectorMA( newVelocity, -dot, dir, newVelocity );
  969. if ( m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER )
  970. {
  971. float val = Lerp( 0.1f, len, dot );
  972. VectorMA( newVelocity, val - len, dir, newVelocity );
  973. }
  974. if ( !IsRideablePhysics(pPhysGround) )
  975. {
  976. if ( !(m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) && IsSimulatingOnAlternateTicks() )
  977. {
  978. newVelocity *= 0.5f;
  979. }
  980. ApplyAbsVelocityImpulse( newVelocity );
  981. }
  982. }
  983. trace_t trace;
  984. UTIL_TraceEntity( this, newPosition, newPosition, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  985. if ( !trace.allsolid && !trace.startsolid )
  986. {
  987. SetAbsOrigin( newPosition );
  988. }
  989. }
  990. else
  991. {
  992. trace_t trace;
  993. Ray_t ray;
  994. ray.Init( GetAbsOrigin(), GetAbsOrigin(), WorldAlignMins(), WorldAlignMaxs() );
  995. CTraceFilterSimple OriginalTraceFilter( this, COLLISION_GROUP_PLAYER_MOVEMENT );
  996. CTraceFilterTranslateClones traceFilter( &OriginalTraceFilter );
  997. UTIL_Portal_TraceRay_With( m_hPortalEnvironment, ray, MASK_PLAYERSOLID, &traceFilter, &trace );
  998. // current position is not ok, fixup
  999. if ( trace.allsolid || trace.startsolid )
  1000. {
  1001. //try again with new position
  1002. ray.Init( newPosition, newPosition, WorldAlignMins(), WorldAlignMaxs() );
  1003. UTIL_Portal_TraceRay_With( m_hPortalEnvironment, ray, MASK_PLAYERSOLID, &traceFilter, &trace );
  1004. if( trace.startsolid == false )
  1005. {
  1006. SetAbsOrigin( newPosition );
  1007. }
  1008. else
  1009. {
  1010. if( !FindClosestPassableSpace( this, newPosition - GetAbsOrigin(), MASK_PLAYERSOLID ) )
  1011. {
  1012. // Try moving the player closer to the center of the portal
  1013. CProp_Portal *pPortal = m_hPortalEnvironment.Get();
  1014. newPosition += ( pPortal->GetAbsOrigin() - WorldSpaceCenter() ) * 0.1f;
  1015. SetAbsOrigin( newPosition );
  1016. DevMsg( "Hurting the player for FindClosestPassableSpaceFailure!" );
  1017. // Deal 1 damage per frame... this will kill a player very fast, but allow for the above correction to fix some cases
  1018. CTakeDamageInfo info( this, this, vec3_origin, vec3_origin, 1, DMG_CRUSH );
  1019. OnTakeDamage( info );
  1020. }
  1021. }
  1022. }
  1023. }
  1024. }
  1025. else
  1026. {
  1027. if ( m_touchedPhysObject )
  1028. {
  1029. // check my position (physics object could have simulated into my position
  1030. // physics is not very far away, check my position
  1031. trace_t trace;
  1032. UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(),
  1033. MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  1034. // is current position ok?
  1035. if ( trace.allsolid || trace.startsolid )
  1036. {
  1037. // stuck????!?!?
  1038. //Msg("Stuck on %s\n", trace.m_pEnt->GetClassname());
  1039. SetAbsOrigin( newPosition );
  1040. UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(),
  1041. MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
  1042. if ( trace.allsolid || trace.startsolid )
  1043. {
  1044. //Msg("Double Stuck\n");
  1045. SetAbsOrigin( m_oldOrigin );
  1046. }
  1047. }
  1048. }
  1049. }
  1050. m_oldOrigin = GetAbsOrigin();
  1051. }
  1052. bool CPortal_Player::UseFoundEntity( CBaseEntity *pUseEntity )
  1053. {
  1054. bool usedSomething = false;
  1055. //!!!UNDONE: traceline here to prevent +USEing buttons through walls
  1056. int caps = pUseEntity->ObjectCaps();
  1057. variant_t emptyVariant;
  1058. if ( m_afButtonPressed & IN_USE )
  1059. {
  1060. // Robin: Don't play sounds for NPCs, because NPCs will allow respond with speech.
  1061. if ( !pUseEntity->MyNPCPointer() )
  1062. {
  1063. EmitSound( "HL2Player.Use" );
  1064. }
  1065. }
  1066. if ( ( (m_nButtons & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) ||
  1067. ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) )
  1068. {
  1069. if ( caps & FCAP_CONTINUOUS_USE )
  1070. m_afPhysicsFlags |= PFLAG_USING;
  1071. pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
  1072. usedSomething = true;
  1073. }
  1074. // UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away
  1075. else if ( (m_afButtonReleased & IN_USE) && (pUseEntity->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use
  1076. {
  1077. pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
  1078. usedSomething = true;
  1079. }
  1080. #if HL2_SINGLE_PRIMARY_WEAPON_MODE
  1081. //Check for weapon pick-up
  1082. if ( m_afButtonPressed & IN_USE )
  1083. {
  1084. CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(pUseEntity);
  1085. if ( ( pWeapon != NULL ) && ( Weapon_CanSwitchTo( pWeapon ) ) )
  1086. {
  1087. //Try to take ammo or swap the weapon
  1088. if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) )
  1089. {
  1090. Weapon_EquipAmmoOnly( pWeapon );
  1091. }
  1092. else
  1093. {
  1094. Weapon_DropSlot( pWeapon->GetSlot() );
  1095. Weapon_Equip( pWeapon );
  1096. }
  1097. usedSomething = true;
  1098. }
  1099. }
  1100. #endif
  1101. return usedSomething;
  1102. }
  1103. //bool CPortal_Player::StartReplayMode( float fDelay, float fDuration, int iEntity )
  1104. //{
  1105. // if ( !BaseClass::StartReplayMode( fDelay, fDuration, 1 ) )
  1106. // return false;
  1107. //
  1108. // CSingleUserRecipientFilter filter( this );
  1109. // filter.MakeReliable();
  1110. //
  1111. // UserMessageBegin( filter, "KillCam" );
  1112. //
  1113. // EHANDLE hPlayer = this;
  1114. //
  1115. // if ( m_hObserverTarget.Get() )
  1116. // {
  1117. // WRITE_EHANDLE( m_hObserverTarget ); // first target
  1118. // WRITE_EHANDLE( hPlayer ); //second target
  1119. // }
  1120. // else
  1121. // {
  1122. // WRITE_EHANDLE( hPlayer ); // first target
  1123. // WRITE_EHANDLE( 0 ); //second target
  1124. // }
  1125. // MessageEnd();
  1126. //
  1127. // return true;
  1128. //}
  1129. //
  1130. //void CPortal_Player::StopReplayMode()
  1131. //{
  1132. // BaseClass::StopReplayMode();
  1133. //
  1134. // CSingleUserRecipientFilter filter( this );
  1135. // filter.MakeReliable();
  1136. //
  1137. // UserMessageBegin( filter, "KillCam" );
  1138. // WRITE_EHANDLE( 0 );
  1139. // WRITE_EHANDLE( 0 );
  1140. // MessageEnd();
  1141. //}
  1142. void CPortal_Player::PlayerUse( void )
  1143. {
  1144. // Was use pressed or released?
  1145. if ( ! ((m_nButtons | m_afButtonPressed | m_afButtonReleased) & IN_USE) )
  1146. return;
  1147. if ( m_afButtonPressed & IN_USE )
  1148. {
  1149. // Currently using a latched entity?
  1150. if ( ClearUseEntity() )
  1151. {
  1152. return;
  1153. }
  1154. else
  1155. {
  1156. if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
  1157. {
  1158. m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
  1159. m_iTrain = TRAIN_NEW|TRAIN_OFF;
  1160. return;
  1161. }
  1162. else
  1163. { // Start controlling the train!
  1164. CBaseEntity *pTrain = GetGroundEntity();
  1165. if ( pTrain && !(m_nButtons & IN_JUMP) && (GetFlags() & FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(this) )
  1166. {
  1167. m_afPhysicsFlags |= PFLAG_DIROVERRIDE;
  1168. m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed());
  1169. m_iTrain |= TRAIN_NEW;
  1170. EmitSound( "HL2Player.TrainUse" );
  1171. return;
  1172. }
  1173. }
  1174. }
  1175. // Tracker 3926: We can't +USE something if we're climbing a ladder
  1176. if ( GetMoveType() == MOVETYPE_LADDER )
  1177. {
  1178. return;
  1179. }
  1180. }
  1181. CBaseEntity *pUseEntity = FindUseEntity();
  1182. bool usedSomething = false;
  1183. // Found an object
  1184. if ( pUseEntity )
  1185. {
  1186. SetHeldObjectOnOppositeSideOfPortal( false );
  1187. // TODO: Removed because we no longer have ghost animatings. May need to rework this code.
  1188. //// If we found a ghost animating then it needs to be held across a portal
  1189. //CGhostAnimating *pGhostAnimating = dynamic_cast<CGhostAnimating*>( pUseEntity );
  1190. //if ( pGhostAnimating )
  1191. //{
  1192. // CProp_Portal *pPortal = NULL;
  1193. // CPortalSimulator *pPortalSimulator = CPortalSimulator::GetSimulatorThatOwnsEntity( pGhostAnimating->GetSourceEntity() );
  1194. // //HACKHACK: This assumes all portal simulators are a member of a prop_portal
  1195. // pPortal = (CProp_Portal *)(((char *)pPortalSimulator) - ((int)&(((CProp_Portal *)0)->m_PortalSimulator)));
  1196. // Assert( (&(pPortal->m_PortalSimulator)) == pPortalSimulator ); //doublechecking the hack
  1197. // if ( pPortal )
  1198. // {
  1199. // SetHeldObjectPortal( pPortal->m_hLinkedPortal );
  1200. // SetHeldObjectOnOppositeSideOfPortal( true );
  1201. // }
  1202. //}
  1203. usedSomething = UseFoundEntity( pUseEntity );
  1204. }
  1205. else
  1206. {
  1207. Vector forward;
  1208. EyeVectors( &forward, NULL, NULL );
  1209. Vector start = EyePosition();
  1210. Ray_t rayPortalTest;
  1211. rayPortalTest.Init( start, start + forward * PLAYER_USE_RADIUS );
  1212. float fMustBeCloserThan = 2.0f;
  1213. CProp_Portal *pPortal = UTIL_Portal_FirstAlongRay( rayPortalTest, fMustBeCloserThan );
  1214. if ( pPortal )
  1215. {
  1216. SetHeldObjectPortal( pPortal );
  1217. pUseEntity = FindUseEntityThroughPortal();
  1218. }
  1219. if ( pUseEntity )
  1220. {
  1221. SetHeldObjectOnOppositeSideOfPortal( true );
  1222. usedSomething = UseFoundEntity( pUseEntity );
  1223. }
  1224. else if ( m_afButtonPressed & IN_USE )
  1225. {
  1226. // Signal that we want to play the deny sound, unless the user is +USEing on a ladder!
  1227. // The sound is emitted in ItemPostFrame, since that occurs after GameMovement::ProcessMove which
  1228. // lets the ladder code unset this flag.
  1229. m_bPlayUseDenySound = true;
  1230. }
  1231. }
  1232. // Debounce the use key
  1233. if ( usedSomething && pUseEntity )
  1234. {
  1235. m_Local.m_nOldButtons |= IN_USE;
  1236. m_afButtonPressed &= ~IN_USE;
  1237. }
  1238. }
  1239. void CPortal_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper)
  1240. {
  1241. if( m_bFixEyeAnglesFromPortalling )
  1242. {
  1243. //the idea here is to handle the notion that the player has portalled, but they sent us an angle update before receiving that message.
  1244. //If we don't handle this here, we end up sending back their old angles which makes them hiccup their angles for a frame
  1245. float fOldAngleDiff = fabs( AngleDistance( ucmd->viewangles.x, m_qPrePortalledViewAngles.x ) );
  1246. fOldAngleDiff += fabs( AngleDistance( ucmd->viewangles.y, m_qPrePortalledViewAngles.y ) );
  1247. fOldAngleDiff += fabs( AngleDistance( ucmd->viewangles.z, m_qPrePortalledViewAngles.z ) );
  1248. float fCurrentAngleDiff = fabs( AngleDistance( ucmd->viewangles.x, pl.v_angle.x ) );
  1249. fCurrentAngleDiff += fabs( AngleDistance( ucmd->viewangles.y, pl.v_angle.y ) );
  1250. fCurrentAngleDiff += fabs( AngleDistance( ucmd->viewangles.z, pl.v_angle.z ) );
  1251. if( fCurrentAngleDiff > fOldAngleDiff )
  1252. ucmd->viewangles = TransformAnglesToWorldSpace( ucmd->viewangles, m_matLastPortalled.As3x4() );
  1253. m_bFixEyeAnglesFromPortalling = false;
  1254. }
  1255. BaseClass::PlayerRunCommand( ucmd, moveHelper );
  1256. }
  1257. bool CPortal_Player::ClientCommand( const CCommand &args )
  1258. {
  1259. if ( FStrEq( args[0], "spectate" ) )
  1260. {
  1261. // do nothing.
  1262. return true;
  1263. }
  1264. return BaseClass::ClientCommand( args );
  1265. }
  1266. void CPortal_Player::CheatImpulseCommands( int iImpulse )
  1267. {
  1268. switch ( iImpulse )
  1269. {
  1270. case 101:
  1271. {
  1272. if( sv_cheats->GetBool() )
  1273. {
  1274. GiveAllItems();
  1275. }
  1276. }
  1277. break;
  1278. default:
  1279. BaseClass::CheatImpulseCommands( iImpulse );
  1280. }
  1281. }
  1282. void CPortal_Player::CreateViewModel( int index /*=0*/ )
  1283. {
  1284. BaseClass::CreateViewModel( index );
  1285. return;
  1286. Assert( index >= 0 && index < MAX_VIEWMODELS );
  1287. if ( GetViewModel( index ) )
  1288. return;
  1289. CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" );
  1290. if ( vm )
  1291. {
  1292. vm->SetAbsOrigin( GetAbsOrigin() );
  1293. vm->SetOwner( this );
  1294. vm->SetIndex( index );
  1295. DispatchSpawn( vm );
  1296. vm->FollowEntity( this, false );
  1297. m_hViewModel.Set( index, vm );
  1298. }
  1299. }
  1300. bool CPortal_Player::BecomeRagdollOnClient( const Vector &force )
  1301. {
  1302. return true;//BaseClass::BecomeRagdollOnClient( force );
  1303. }
  1304. void CPortal_Player::CreateRagdollEntity( const CTakeDamageInfo &info )
  1305. {
  1306. if ( m_hRagdoll )
  1307. {
  1308. UTIL_RemoveImmediate( m_hRagdoll );
  1309. m_hRagdoll = NULL;
  1310. }
  1311. #if PORTAL_HIDE_PLAYER_RAGDOLL
  1312. AddSolidFlags( FSOLID_NOT_SOLID );
  1313. AddEffects( EF_NODRAW | EF_NOSHADOW );
  1314. AddEFlags( EFL_NO_DISSOLVE );
  1315. #endif // PORTAL_HIDE_PLAYER_RAGDOLL
  1316. CBaseEntity *pRagdoll = CreateServerRagdoll( this, m_nForceBone, info, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
  1317. pRagdoll->m_takedamage = DAMAGE_NO;
  1318. m_hRagdoll = pRagdoll;
  1319. /*
  1320. // If we already have a ragdoll destroy it.
  1321. CPortalRagdoll *pRagdoll = dynamic_cast<CPortalRagdoll*>( m_hRagdoll.Get() );
  1322. if( pRagdoll )
  1323. {
  1324. UTIL_Remove( pRagdoll );
  1325. pRagdoll = NULL;
  1326. }
  1327. Assert( pRagdoll == NULL );
  1328. // Create a ragdoll.
  1329. pRagdoll = dynamic_cast<CPortalRagdoll*>( CreateEntityByName( "portal_ragdoll" ) );
  1330. if ( pRagdoll )
  1331. {
  1332. pRagdoll->m_hPlayer = this;
  1333. pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
  1334. pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
  1335. pRagdoll->m_nModelIndex = m_nModelIndex;
  1336. pRagdoll->m_nForceBone = m_nForceBone;
  1337. pRagdoll->CopyAnimationDataFrom( this );
  1338. pRagdoll->SetOwnerEntity( this );
  1339. pRagdoll->m_flAnimTime = gpGlobals->curtime;
  1340. pRagdoll->m_flPlaybackRate = 0.0;
  1341. pRagdoll->SetCycle( 0 );
  1342. pRagdoll->ResetSequence( 0 );
  1343. float fSequenceDuration = SequenceDuration( GetSequence() );
  1344. float fPreviousCycle = clamp(GetCycle()-( 0.1 * ( 1 / fSequenceDuration ) ),0.f,1.f);
  1345. float fCurCycle = GetCycle();
  1346. matrix3x4_t pBoneToWorld[MAXSTUDIOBONES], pBoneToWorldNext[MAXSTUDIOBONES];
  1347. SetupBones( pBoneToWorldNext, BONE_USED_BY_ANYTHING );
  1348. SetCycle( fPreviousCycle );
  1349. SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
  1350. SetCycle( fCurCycle );
  1351. pRagdoll->InitRagdoll( info.GetDamageForce(), m_nForceBone, info.GetDamagePosition(), pBoneToWorld, pBoneToWorldNext, 0.1f, COLLISION_GROUP_INTERACTIVE_DEBRIS, true );
  1352. pRagdoll->SetMoveType( MOVETYPE_VPHYSICS );
  1353. pRagdoll->SetSolid( SOLID_VPHYSICS );
  1354. if ( IsDissolving() )
  1355. {
  1356. pRagdoll->TransferDissolveFrom( this );
  1357. }
  1358. Vector mins, maxs;
  1359. mins = CollisionProp()->OBBMins();
  1360. maxs = CollisionProp()->OBBMaxs();
  1361. pRagdoll->CollisionProp()->SetCollisionBounds( mins, maxs );
  1362. pRagdoll->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  1363. }
  1364. // Turn off the player.
  1365. AddSolidFlags( FSOLID_NOT_SOLID );
  1366. AddEffects( EF_NODRAW | EF_NOSHADOW );
  1367. SetMoveType( MOVETYPE_NONE );
  1368. // Save ragdoll handle.
  1369. m_hRagdoll = pRagdoll;
  1370. */
  1371. }
  1372. void CPortal_Player::Jump( void )
  1373. {
  1374. g_PortalGameStats.Event_PlayerJump( GetAbsOrigin(), GetAbsVelocity() );
  1375. BaseClass::Jump();
  1376. }
  1377. void CPortal_Player::Event_Killed( const CTakeDamageInfo &info )
  1378. {
  1379. //update damage info with our accumulated physics force
  1380. CTakeDamageInfo subinfo = info;
  1381. subinfo.SetDamageForce( m_vecTotalBulletForce );
  1382. // show killer in death cam mode
  1383. // chopped down version of SetObserverTarget without the team check
  1384. //if( info.GetAttacker() )
  1385. //{
  1386. // // set new target
  1387. // m_hObserverTarget.Set( info.GetAttacker() );
  1388. //}
  1389. //else
  1390. // m_hObserverTarget.Set( NULL );
  1391. UpdateExpression();
  1392. // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
  1393. // because we still want to transmit to the clients in our PVS.
  1394. CreateRagdollEntity( info );
  1395. BaseClass::Event_Killed( subinfo );
  1396. #if PORTAL_HIDE_PLAYER_RAGDOLL
  1397. // Fizzle all portals so they don't see the player disappear
  1398. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  1399. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  1400. for( int i = 0; i != iPortalCount; ++i )
  1401. {
  1402. CProp_Portal *pTempPortal = pPortals[i];
  1403. if( pTempPortal && pTempPortal->m_bActivated )
  1404. {
  1405. pTempPortal->Fizzle();
  1406. }
  1407. }
  1408. #endif // PORTAL_HIDE_PLAYER_RAGDOLL
  1409. if ( (info.GetDamageType() & DMG_DISSOLVE) && !(m_hRagdoll.Get()->GetEFlags() & EFL_NO_DISSOLVE) )
  1410. {
  1411. if ( m_hRagdoll )
  1412. {
  1413. m_hRagdoll->GetBaseAnimating()->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
  1414. }
  1415. }
  1416. m_lifeState = LIFE_DYING;
  1417. StopZooming();
  1418. if ( GetObserverTarget() )
  1419. {
  1420. //StartReplayMode( 3, 3, GetObserverTarget()->entindex() );
  1421. //StartObserverMode( OBS_MODE_DEATHCAM );
  1422. }
  1423. }
  1424. int CPortal_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo )
  1425. {
  1426. CTakeDamageInfo inputInfoCopy( inputInfo );
  1427. // If you shoot yourself, make it hurt but push you less
  1428. if ( inputInfoCopy.GetAttacker() == this && inputInfoCopy.GetDamageType() == DMG_BULLET )
  1429. {
  1430. inputInfoCopy.ScaleDamage( 5.0f );
  1431. inputInfoCopy.ScaleDamageForce( 0.05f );
  1432. }
  1433. CBaseEntity *pAttacker = inputInfoCopy.GetAttacker();
  1434. CBaseEntity *pInflictor = inputInfoCopy.GetInflictor();
  1435. bool bIsTurret = false;
  1436. if ( pAttacker && FClassnameIs( pAttacker, "npc_portal_turret_floor" ) )
  1437. bIsTurret = true;
  1438. // Refuse damage from prop_glados_core.
  1439. if ( (pAttacker && FClassnameIs( pAttacker, "prop_glados_core" )) ||
  1440. (pInflictor && FClassnameIs( pInflictor, "prop_glados_core" )) )
  1441. {
  1442. inputInfoCopy.SetDamage(0.0f);
  1443. }
  1444. if ( bIsTurret && ( inputInfoCopy.GetDamageType() & DMG_BULLET ) )
  1445. {
  1446. Vector vLateralForce = inputInfoCopy.GetDamageForce();
  1447. vLateralForce.z = 0.0f;
  1448. // Push if the player is moving against the force direction
  1449. if ( GetAbsVelocity().Dot( vLateralForce ) < 0.0f )
  1450. ApplyAbsVelocityImpulse( vLateralForce );
  1451. }
  1452. else if ( ( inputInfoCopy.GetDamageType() & DMG_CRUSH ) )
  1453. {
  1454. if ( bIsTurret )
  1455. {
  1456. inputInfoCopy.SetDamage( inputInfoCopy.GetDamage() * 0.5f );
  1457. }
  1458. if ( inputInfoCopy.GetDamage() >= 10.0f )
  1459. {
  1460. EmitSound( "PortalPlayer.BonkYelp" );
  1461. }
  1462. }
  1463. else if ( ( inputInfoCopy.GetDamageType() & DMG_SHOCK ) || ( inputInfoCopy.GetDamageType() & DMG_BURN ) )
  1464. {
  1465. EmitSound( "PortalPortal.PainYelp" );
  1466. }
  1467. int ret = BaseClass::OnTakeDamage( inputInfoCopy );
  1468. // Copy the multidamage damage origin over what the base class wrote, because
  1469. // that gets translated correctly though portals.
  1470. m_DmgOrigin = inputInfo.GetDamagePosition();
  1471. if ( GetHealth() < 100 )
  1472. {
  1473. m_fTimeLastHurt = gpGlobals->curtime;
  1474. }
  1475. return ret;
  1476. }
  1477. int CPortal_Player::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  1478. {
  1479. // set damage type sustained
  1480. m_bitsDamageType |= info.GetDamageType();
  1481. if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) )
  1482. return 0;
  1483. CBaseEntity * attacker = info.GetAttacker();
  1484. if ( !attacker )
  1485. return 0;
  1486. Vector vecDir = vec3_origin;
  1487. if ( info.GetInflictor() )
  1488. {
  1489. vecDir = info.GetInflictor()->WorldSpaceCenter() - Vector ( 0, 0, 10 ) - WorldSpaceCenter();
  1490. VectorNormalize( vecDir );
  1491. }
  1492. if ( info.GetInflictor() && (GetMoveType() == MOVETYPE_WALK) &&
  1493. ( !attacker->IsSolidFlagSet(FSOLID_TRIGGER)) )
  1494. {
  1495. Vector force = vecDir;// * -DamageForce( WorldAlignSize(), info.GetBaseDamage() );
  1496. if ( force.z > 250.0f )
  1497. {
  1498. force.z = 250.0f;
  1499. }
  1500. ApplyAbsVelocityImpulse( force );
  1501. }
  1502. // fire global game event
  1503. IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
  1504. if ( event )
  1505. {
  1506. event->SetInt("userid", GetUserID() );
  1507. event->SetInt("health", MAX(0, m_iHealth) );
  1508. event->SetInt("priority", 5 ); // HLTV event priority, not transmitted
  1509. if ( attacker->IsPlayer() )
  1510. {
  1511. CBasePlayer *player = ToBasePlayer( attacker );
  1512. event->SetInt("attacker", player->GetUserID() ); // hurt by other player
  1513. }
  1514. else
  1515. {
  1516. event->SetInt("attacker", 0 ); // hurt by "world"
  1517. }
  1518. gameeventmanager->FireEvent( event );
  1519. }
  1520. // Insert a combat sound so that nearby NPCs hear battle
  1521. if ( attacker->IsNPC() )
  1522. {
  1523. CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 512, 0.5, this );//<<TODO>>//magic number
  1524. }
  1525. return 1;
  1526. }
  1527. void CPortal_Player::ForceDuckThisFrame( void )
  1528. {
  1529. if( m_Local.m_bDucked != true )
  1530. {
  1531. //m_Local.m_bDucking = false;
  1532. m_Local.m_bDucked = true;
  1533. ForceButtons( IN_DUCK );
  1534. AddFlag( FL_DUCKING );
  1535. SetVCollisionState( GetAbsOrigin(), GetAbsVelocity(), VPHYS_CROUCH );
  1536. }
  1537. }
  1538. void CPortal_Player::UnDuck( void )
  1539. {
  1540. if( m_Local.m_bDucked != false )
  1541. {
  1542. m_Local.m_bDucked = false;
  1543. UnforceButtons( IN_DUCK );
  1544. RemoveFlag( FL_DUCKING );
  1545. SetVCollisionState( GetAbsOrigin(), GetAbsVelocity(), VPHYS_WALK );
  1546. }
  1547. }
  1548. //-----------------------------------------------------------------------------
  1549. // Purpose: Overload for portal-- Our player can lift his own mass.
  1550. // Input : *pObject - The object to lift
  1551. // bLimitMassAndSize - check for mass/size limits
  1552. //-----------------------------------------------------------------------------
  1553. void CPortal_Player::PickupObject(CBaseEntity *pObject, bool bLimitMassAndSize )
  1554. {
  1555. // can't pick up what you're standing on
  1556. if ( GetGroundEntity() == pObject )
  1557. return;
  1558. if ( bLimitMassAndSize == true )
  1559. {
  1560. if ( CBasePlayer::CanPickupObject( pObject, PORTAL_PLAYER_MAX_LIFT_MASS, PORTAL_PLAYER_MAX_LIFT_SIZE ) == false )
  1561. return;
  1562. }
  1563. // Can't be picked up if NPCs are on me
  1564. if ( pObject->HasNPCsOnIt() )
  1565. return;
  1566. PlayerPickupObject( this, pObject );
  1567. }
  1568. void CPortal_Player::ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldingThis )
  1569. {
  1570. m_bHeldObjectOnOppositeSideOfPortal = false;
  1571. BaseClass::ForceDropOfCarriedPhysObjects( pOnlyIfHoldingThis );
  1572. }
  1573. void CPortal_Player::IncrementPortalsPlaced( void )
  1574. {
  1575. m_StatsThisLevel.iNumPortalsPlaced++;
  1576. if ( m_iBonusChallenge == PORTAL_CHALLENGE_PORTALS )
  1577. SetBonusProgress( static_cast<int>( m_StatsThisLevel.iNumPortalsPlaced ) );
  1578. }
  1579. void CPortal_Player::IncrementStepsTaken( void )
  1580. {
  1581. m_StatsThisLevel.iNumStepsTaken++;
  1582. if ( m_iBonusChallenge == PORTAL_CHALLENGE_STEPS )
  1583. SetBonusProgress( static_cast<int>( m_StatsThisLevel.iNumStepsTaken ) );
  1584. }
  1585. void CPortal_Player::UpdateSecondsTaken( void )
  1586. {
  1587. float fSecondsSinceLastUpdate = ( gpGlobals->curtime - m_fTimeLastNumSecondsUpdate );
  1588. m_StatsThisLevel.fNumSecondsTaken += fSecondsSinceLastUpdate;
  1589. m_fTimeLastNumSecondsUpdate = gpGlobals->curtime;
  1590. if ( m_iBonusChallenge == PORTAL_CHALLENGE_TIME )
  1591. SetBonusProgress( static_cast<int>( m_StatsThisLevel.fNumSecondsTaken ) );
  1592. if ( m_fNeuroToxinDamageTime > 0.0f )
  1593. {
  1594. float fTimeRemaining = m_fNeuroToxinDamageTime - gpGlobals->curtime;
  1595. if ( fTimeRemaining < 0.0f )
  1596. {
  1597. CTakeDamageInfo info;
  1598. info.SetDamage( gpGlobals->frametime * 50.0f );
  1599. info.SetDamageType( DMG_NERVEGAS );
  1600. TakeDamage( info );
  1601. fTimeRemaining = 0.0f;
  1602. }
  1603. PauseBonusProgress( false );
  1604. SetBonusProgress( static_cast<int>( fTimeRemaining ) );
  1605. }
  1606. }
  1607. void CPortal_Player::ResetThisLevelStats( void )
  1608. {
  1609. m_StatsThisLevel.iNumPortalsPlaced = 0;
  1610. m_StatsThisLevel.iNumStepsTaken = 0;
  1611. m_StatsThisLevel.fNumSecondsTaken = 0.0f;
  1612. if ( m_iBonusChallenge != PORTAL_CHALLENGE_NONE )
  1613. SetBonusProgress( 0 );
  1614. }
  1615. //-----------------------------------------------------------------------------
  1616. // Purpose: Update the area bits variable which is networked down to the client to determine
  1617. // which area portals should be closed based on visibility.
  1618. // Input : *pvs - pvs to be used to determine visibility of the portals
  1619. //-----------------------------------------------------------------------------
  1620. void CPortal_Player::UpdatePortalViewAreaBits( unsigned char *pvs, int pvssize )
  1621. {
  1622. Assert ( pvs );
  1623. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  1624. if( iPortalCount == 0 )
  1625. return;
  1626. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  1627. int *portalArea = (int *)stackalloc( sizeof( int ) * iPortalCount );
  1628. bool *bUsePortalForVis = (bool *)stackalloc( sizeof( bool ) * iPortalCount );
  1629. unsigned char *portalTempBits = (unsigned char *)stackalloc( sizeof( unsigned char ) * 32 * iPortalCount );
  1630. COMPILE_TIME_ASSERT( (sizeof( unsigned char ) * 32) >= sizeof( ((CPlayerLocalData*)0)->m_chAreaBits ) );
  1631. // setup area bits for these portals
  1632. for ( int i = 0; i < iPortalCount; ++i )
  1633. {
  1634. CProp_Portal* pLocalPortal = pPortals[ i ];
  1635. // Make sure this portal is active before adding it's location to the pvs
  1636. if ( pLocalPortal && pLocalPortal->m_bActivated )
  1637. {
  1638. CProp_Portal* pRemotePortal = pLocalPortal->m_hLinkedPortal.Get();
  1639. // Make sure this portal's linked portal is in the PVS before we add what it can see
  1640. if ( pRemotePortal && pRemotePortal->m_bActivated && pRemotePortal->NetworkProp() &&
  1641. pRemotePortal->NetworkProp()->IsInPVS( edict(), pvs, pvssize ) )
  1642. {
  1643. portalArea[ i ] = engine->GetArea( pPortals[ i ]->GetAbsOrigin() );
  1644. if ( portalArea [ i ] >= 0 )
  1645. {
  1646. bUsePortalForVis[ i ] = true;
  1647. }
  1648. engine->GetAreaBits( portalArea[ i ], &portalTempBits[ i * 32 ], sizeof( unsigned char ) * 32 );
  1649. }
  1650. }
  1651. }
  1652. // Use the union of player-view area bits and the portal-view area bits of each portal
  1653. for ( int i = 0; i < m_Local.m_chAreaBits.Count(); i++ )
  1654. {
  1655. for ( int j = 0; j < iPortalCount; ++j )
  1656. {
  1657. // If this portal is active, in PVS and it's location is valid
  1658. if ( bUsePortalForVis[ j ] )
  1659. {
  1660. m_Local.m_chAreaBits.Set( i, m_Local.m_chAreaBits[ i ] | portalTempBits[ (j * 32) + i ] );
  1661. }
  1662. }
  1663. }
  1664. }
  1665. //////////////////////////////////////////////////////////////////////////
  1666. // AddPortalCornersToEnginePVS
  1667. // Subroutine to wrap the adding of portal corners to the PVS which is called once for the setup of each portal.
  1668. // input - pPortal: the portal we are viewing 'out of' which needs it's corners added to the PVS
  1669. //////////////////////////////////////////////////////////////////////////
  1670. void AddPortalCornersToEnginePVS( CProp_Portal* pPortal )
  1671. {
  1672. Assert ( pPortal );
  1673. if ( !pPortal )
  1674. return;
  1675. Vector vForward, vRight, vUp;
  1676. pPortal->GetVectors( &vForward, &vRight, &vUp );
  1677. // Center of the remote portal
  1678. Vector ptOrigin = pPortal->GetAbsOrigin();
  1679. // Distance offsets to the different edges of the portal... Used in the placement checks
  1680. Vector vToTopEdge = vUp * ( PORTAL_HALF_HEIGHT - PORTAL_BUMP_FORGIVENESS );
  1681. Vector vToBottomEdge = -vToTopEdge;
  1682. Vector vToRightEdge = vRight * ( PORTAL_HALF_WIDTH - PORTAL_BUMP_FORGIVENESS );
  1683. Vector vToLeftEdge = -vToRightEdge;
  1684. // Distance to place PVS points away from portal, to avoid being in solid
  1685. Vector vForwardBump = vForward * 1.0f;
  1686. // Add center and edges to the engine PVS
  1687. engine->AddOriginToPVS( ptOrigin + vForwardBump);
  1688. engine->AddOriginToPVS( ptOrigin + vToTopEdge + vToLeftEdge + vForwardBump );
  1689. engine->AddOriginToPVS( ptOrigin + vToTopEdge + vToRightEdge + vForwardBump );
  1690. engine->AddOriginToPVS( ptOrigin + vToBottomEdge + vToLeftEdge + vForwardBump );
  1691. engine->AddOriginToPVS( ptOrigin + vToBottomEdge + vToRightEdge + vForwardBump );
  1692. }
  1693. void PortalSetupVisibility( CBaseEntity *pPlayer, int area, unsigned char *pvs, int pvssize )
  1694. {
  1695. int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
  1696. if( iPortalCount == 0 )
  1697. return;
  1698. CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
  1699. for( int i = 0; i != iPortalCount; ++i )
  1700. {
  1701. CProp_Portal *pPortal = pPortals[i];
  1702. if ( pPortal && pPortal->m_bActivated )
  1703. {
  1704. if ( pPortal->NetworkProp()->IsInPVS( pPlayer->edict(), pvs, pvssize ) )
  1705. {
  1706. if ( engine->CheckAreasConnected( area, pPortal->NetworkProp()->AreaNum() ) )
  1707. {
  1708. CProp_Portal *pLinkedPortal = static_cast<CProp_Portal*>( pPortal->m_hLinkedPortal.Get() );
  1709. if ( pLinkedPortal )
  1710. {
  1711. AddPortalCornersToEnginePVS ( pLinkedPortal );
  1712. }
  1713. }
  1714. }
  1715. }
  1716. }
  1717. }
  1718. void CPortal_Player::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
  1719. {
  1720. BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
  1721. int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
  1722. // At this point the EyePosition has been added as a view origin, but if we are currently stuck
  1723. // in a portal, our EyePosition may return a point in solid. Find the reflected eye position
  1724. // and use that as a vis origin instead.
  1725. if ( m_hPortalEnvironment )
  1726. {
  1727. CProp_Portal *pPortal = NULL, *pRemotePortal = NULL;
  1728. pPortal = m_hPortalEnvironment;
  1729. pRemotePortal = pPortal->m_hLinkedPortal;
  1730. if ( pPortal && pRemotePortal && pPortal->m_bActivated && pRemotePortal->m_bActivated )
  1731. {
  1732. Vector ptPortalCenter = pPortal->GetAbsOrigin();
  1733. Vector vPortalForward;
  1734. pPortal->GetVectors( &vPortalForward, NULL, NULL );
  1735. Vector eyeOrigin = EyePosition();
  1736. Vector vEyeToPortalCenter = ptPortalCenter - eyeOrigin;
  1737. float fPortalDist = vPortalForward.Dot( vEyeToPortalCenter );
  1738. if( fPortalDist > 0.0f ) //eye point is behind portal
  1739. {
  1740. // Move eye origin to it's transformed position on the other side of the portal
  1741. UTIL_Portal_PointTransform( pPortal->MatrixThisToLinked(), eyeOrigin, eyeOrigin );
  1742. // Use this as our view origin (as this is where the client will be displaying from)
  1743. engine->AddOriginToPVS( eyeOrigin );
  1744. if ( !pViewEntity || pViewEntity->IsPlayer() )
  1745. {
  1746. area = engine->GetArea( eyeOrigin );
  1747. }
  1748. }
  1749. }
  1750. }
  1751. PortalSetupVisibility( this, area, pvs, pvssize );
  1752. }
  1753. #ifdef PORTAL_MP
  1754. CBaseEntity* CPortal_Player::EntSelectSpawnPoint( void )
  1755. {
  1756. CBaseEntity *pSpot = NULL;
  1757. CBaseEntity *pLastSpawnPoint = g_pLastSpawn;
  1758. edict_t *player = edict();
  1759. const char *pSpawnpointName = "info_player_start";
  1760. /*if ( HL2MPRules()->IsTeamplay() == true )
  1761. {
  1762. if ( GetTeamNumber() == TEAM_COMBINE )
  1763. {
  1764. pSpawnpointName = "info_player_combine";
  1765. pLastSpawnPoint = g_pLastCombineSpawn;
  1766. }
  1767. else if ( GetTeamNumber() == TEAM_REBELS )
  1768. {
  1769. pSpawnpointName = "info_player_rebel";
  1770. pLastSpawnPoint = g_pLastRebelSpawn;
  1771. }
  1772. if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL )
  1773. {
  1774. pSpawnpointName = "info_player_deathmatch";
  1775. pLastSpawnPoint = g_pLastSpawn;
  1776. }
  1777. }*/
  1778. pSpot = pLastSpawnPoint;
  1779. // Randomize the start spot
  1780. for ( int i = random->RandomInt(1,5); i > 0; i-- )
  1781. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  1782. if ( !pSpot ) // skip over the null point
  1783. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  1784. CBaseEntity *pFirstSpot = pSpot;
  1785. do
  1786. {
  1787. if ( pSpot )
  1788. {
  1789. // check if pSpot is valid
  1790. if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
  1791. {
  1792. if ( pSpot->GetLocalOrigin() == vec3_origin )
  1793. {
  1794. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  1795. continue;
  1796. }
  1797. // if so, go to pSpot
  1798. goto ReturnSpot;
  1799. }
  1800. }
  1801. // increment pSpot
  1802. pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
  1803. } while ( pSpot != pFirstSpot ); // loop if we're not back to the start
  1804. // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there
  1805. if ( pSpot )
  1806. {
  1807. CBaseEntity *ent = NULL;
  1808. for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
  1809. {
  1810. // if ent is a client, kill em (unless they are ourselves)
  1811. if ( ent->IsPlayer() && !(ent->edict() == player) )
  1812. ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) );
  1813. }
  1814. goto ReturnSpot;
  1815. }
  1816. if ( !pSpot )
  1817. {
  1818. pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" );
  1819. if ( pSpot )
  1820. goto ReturnSpot;
  1821. }
  1822. ReturnSpot:
  1823. /*if ( HL2MPRules()->IsTeamplay() == true )
  1824. {
  1825. if ( GetTeamNumber() == TEAM_COMBINE )
  1826. {
  1827. g_pLastCombineSpawn = pSpot;
  1828. }
  1829. else if ( GetTeamNumber() == TEAM_REBELS )
  1830. {
  1831. g_pLastRebelSpawn = pSpot;
  1832. }
  1833. }*/
  1834. g_pLastSpawn = pSpot;
  1835. //m_flSlamProtectTime = gpGlobals->curtime + 0.5;
  1836. return pSpot;
  1837. }
  1838. void CPortal_Player::PickTeam( void )
  1839. {
  1840. //picks lowest or random
  1841. CTeam *pCombine = g_Teams[TEAM_COMBINE];
  1842. CTeam *pRebels = g_Teams[TEAM_REBELS];
  1843. if ( pCombine->GetNumPlayers() > pRebels->GetNumPlayers() )
  1844. {
  1845. ChangeTeam( TEAM_REBELS );
  1846. }
  1847. else if ( pCombine->GetNumPlayers() < pRebels->GetNumPlayers() )
  1848. {
  1849. ChangeTeam( TEAM_COMBINE );
  1850. }
  1851. else
  1852. {
  1853. ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) );
  1854. }
  1855. }
  1856. #endif
  1857. CON_COMMAND( startadmiregloves, "Starts the admire gloves animation." )
  1858. {
  1859. CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
  1860. if( pPlayer == NULL )
  1861. pPlayer = GetPortalPlayer( 1 ); //last ditch effort
  1862. if( pPlayer )
  1863. pPlayer->StartAdmireGlovesAnimation();
  1864. }
  1865. CON_COMMAND( displayportalplayerstats, "Displays current level stats for portals placed, steps taken, and seconds taken." )
  1866. {
  1867. CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
  1868. if( pPlayer == NULL )
  1869. pPlayer = GetPortalPlayer( 1 ); //last ditch effort
  1870. if( pPlayer )
  1871. {
  1872. int iMinutes = static_cast<int>( pPlayer->NumSecondsTaken() / 60.0f );
  1873. int iSeconds = static_cast<int>( pPlayer->NumSecondsTaken() ) % 60;
  1874. CFmtStr msg;
  1875. 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 );
  1876. }
  1877. }
  1878. CON_COMMAND( startneurotoxins, "Starts the nerve gas timer." )
  1879. {
  1880. CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
  1881. if( pPlayer == NULL )
  1882. pPlayer = GetPortalPlayer( 1 ); //last ditch effort
  1883. float fCoundownTime = 180.0f;
  1884. if ( args.ArgC() > 1 )
  1885. fCoundownTime = atof( args[ 1 ] );
  1886. if( pPlayer )
  1887. pPlayer->SetNeuroToxinDamageTime( fCoundownTime );
  1888. }