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.

736 lines
21 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "BasePropDoor.h"
  8. #include "portal_player.h"
  9. #include "te_effect_dispatch.h"
  10. #include "gameinterface.h"
  11. #include "prop_combine_ball.h"
  12. #include "portal_shareddefs.h"
  13. #include "triggers.h"
  14. #include "collisionutils.h"
  15. #include "cbaseanimatingprojectile.h"
  16. #include "weapon_physcannon.h"
  17. #include "prop_portal_shared.h"
  18. #include "portal_placement.h"
  19. #include "weapon_portalgun_shared.h"
  20. #include "physicsshadowclone.h"
  21. #include "particle_parse.h"
  22. #define BLAST_SPEED_NON_PLAYER 1000.0f
  23. #define BLAST_SPEED 3000.0f
  24. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPortalgun, DT_WeaponPortalgun )
  25. BEGIN_NETWORK_TABLE( CWeaponPortalgun, DT_WeaponPortalgun )
  26. SendPropBool( SENDINFO( m_bCanFirePortal1 ) ),
  27. SendPropBool( SENDINFO( m_bCanFirePortal2 ) ),
  28. SendPropInt( SENDINFO( m_iLastFiredPortal ) ),
  29. SendPropBool( SENDINFO( m_bOpenProngs ) ),
  30. SendPropFloat( SENDINFO( m_fCanPlacePortal1OnThisSurface ) ),
  31. SendPropFloat( SENDINFO( m_fCanPlacePortal2OnThisSurface ) ),
  32. SendPropFloat( SENDINFO( m_fEffectsMaxSize1 ) ), // HACK HACK! Used to make the gun visually change when going through a cleanser!
  33. SendPropFloat( SENDINFO( m_fEffectsMaxSize2 ) ),
  34. SendPropInt( SENDINFO( m_EffectState ) ),
  35. END_NETWORK_TABLE()
  36. BEGIN_DATADESC( CWeaponPortalgun )
  37. DEFINE_KEYFIELD( m_bCanFirePortal1, FIELD_BOOLEAN, "CanFirePortal1" ),
  38. DEFINE_KEYFIELD( m_bCanFirePortal2, FIELD_BOOLEAN, "CanFirePortal2" ),
  39. DEFINE_FIELD( m_iLastFiredPortal, FIELD_INTEGER ),
  40. DEFINE_FIELD( m_bOpenProngs, FIELD_BOOLEAN ),
  41. DEFINE_FIELD( m_fCanPlacePortal1OnThisSurface, FIELD_FLOAT ),
  42. DEFINE_FIELD( m_fCanPlacePortal2OnThisSurface, FIELD_FLOAT ),
  43. DEFINE_FIELD( m_fEffectsMaxSize1, FIELD_FLOAT ),
  44. DEFINE_FIELD( m_fEffectsMaxSize2, FIELD_FLOAT ),
  45. DEFINE_FIELD( m_EffectState, FIELD_INTEGER ),
  46. DEFINE_FIELD( m_iPortalLinkageGroupID, FIELD_CHARACTER ),
  47. DEFINE_INPUTFUNC( FIELD_VOID, "ChargePortal1", InputChargePortal1 ),
  48. DEFINE_INPUTFUNC( FIELD_VOID, "ChargePortal2", InputChargePortal2 ),
  49. DEFINE_INPUTFUNC( FIELD_VOID, "FirePortal1", FirePortal1 ),
  50. DEFINE_INPUTFUNC( FIELD_VOID, "FirePortal2", FirePortal2 ),
  51. DEFINE_INPUTFUNC( FIELD_VECTOR, "FirePortalDirection1", FirePortalDirection1 ),
  52. DEFINE_INPUTFUNC( FIELD_VECTOR, "FirePortalDirection2", FirePortalDirection2 ),
  53. DEFINE_SOUNDPATCH( m_pMiniGravHoldSound ),
  54. DEFINE_OUTPUT ( m_OnFiredPortal1, "OnFiredPortal1" ),
  55. DEFINE_OUTPUT ( m_OnFiredPortal2, "OnFiredPortal2" ),
  56. DEFINE_FUNCTION( Think ),
  57. END_DATADESC()
  58. LINK_ENTITY_TO_CLASS( weapon_portalgun, CWeaponPortalgun );
  59. PRECACHE_WEAPON_REGISTER(weapon_portalgun);
  60. extern ConVar sv_portal_placement_debug;
  61. extern ConVar sv_portal_placement_never_fail;
  62. void CWeaponPortalgun::Spawn( void )
  63. {
  64. Precache();
  65. BaseClass::Spawn();
  66. SetThink( &CWeaponPortalgun::Think );
  67. SetNextThink( gpGlobals->curtime + 0.1 );
  68. if( GameRules()->IsMultiplayer() )
  69. {
  70. CBaseEntity *pOwner = GetOwner();
  71. if( pOwner && pOwner->IsPlayer() )
  72. m_iPortalLinkageGroupID = pOwner->entindex();
  73. Assert( (m_iPortalLinkageGroupID >= 0) && (m_iPortalLinkageGroupID < 256) );
  74. }
  75. }
  76. void CWeaponPortalgun::Activate()
  77. {
  78. BaseClass::Activate();
  79. CreateSounds();
  80. CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  81. if ( pPlayer )
  82. {
  83. CBaseEntity *pHeldObject = GetPlayerHeldEntity( pPlayer );
  84. OpenProngs( ( pHeldObject ) ? ( false ) : ( true ) );
  85. OpenProngs( ( pHeldObject ) ? ( true ) : ( false ) );
  86. if( GameRules()->IsMultiplayer() )
  87. m_iPortalLinkageGroupID = pPlayer->entindex();
  88. Assert( (m_iPortalLinkageGroupID >= 0) && (m_iPortalLinkageGroupID < 256) );
  89. }
  90. // HACK HACK! Used to make the gun visually change when going through a cleanser!
  91. m_fEffectsMaxSize1 = 4.0f;
  92. m_fEffectsMaxSize2 = 4.0f;
  93. }
  94. void CWeaponPortalgun::OnPickedUp( CBaseCombatCharacter *pNewOwner )
  95. {
  96. if( GameRules()->IsMultiplayer() )
  97. {
  98. if( pNewOwner && pNewOwner->IsPlayer() )
  99. m_iPortalLinkageGroupID = pNewOwner->entindex();
  100. Assert( (m_iPortalLinkageGroupID >= 0) && (m_iPortalLinkageGroupID < 256) );
  101. }
  102. BaseClass::OnPickedUp( pNewOwner );
  103. }
  104. void CWeaponPortalgun::CreateSounds()
  105. {
  106. if (!m_pMiniGravHoldSound)
  107. {
  108. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  109. CPASAttenuationFilter filter( this );
  110. m_pMiniGravHoldSound = controller.SoundCreate( filter, entindex(), "Weapon_Portalgun.HoldSound" );
  111. controller.Play( m_pMiniGravHoldSound, 0, 100 );
  112. }
  113. }
  114. void CWeaponPortalgun::StopLoopingSounds()
  115. {
  116. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  117. controller.SoundDestroy( m_pMiniGravHoldSound );
  118. m_pMiniGravHoldSound = NULL;
  119. BaseClass::StopLoopingSounds();
  120. }
  121. void CWeaponPortalgun::DoEffectBlast( bool bPortal2, int iPlacedBy, const Vector &ptStart, const Vector &ptFinalPos, const QAngle &qStartAngles, float fDelay )
  122. {
  123. CEffectData fxData;
  124. fxData.m_vOrigin = ptStart;
  125. fxData.m_vStart = ptFinalPos;
  126. fxData.m_flScale = gpGlobals->curtime + fDelay;
  127. fxData.m_vAngles = qStartAngles;
  128. fxData.m_nColor = ( ( bPortal2 ) ? ( 2 ) : ( 1 ) );
  129. fxData.m_nDamageType = iPlacedBy;
  130. DispatchEffect( "PortalBlast", fxData );
  131. }
  132. //-----------------------------------------------------------------------------
  133. // Purpose: Allows a generic think function before the others are called
  134. // Input : state - which state the turret is currently in
  135. //-----------------------------------------------------------------------------
  136. bool CWeaponPortalgun::PreThink( void )
  137. {
  138. //Animate
  139. StudioFrameAdvance();
  140. //Do not interrupt current think function
  141. return false;
  142. }
  143. void CWeaponPortalgun::Think( void )
  144. {
  145. //Allow descended classes a chance to do something before the think function
  146. if ( PreThink() )
  147. return;
  148. SetNextThink( gpGlobals->curtime + 0.1f );
  149. CPortal_Player *pPlayer = ToPortalPlayer( GetOwner() );
  150. if ( !pPlayer || pPlayer->GetActiveWeapon() != this )
  151. {
  152. m_fCanPlacePortal1OnThisSurface = 1.0f;
  153. m_fCanPlacePortal2OnThisSurface = 1.0f;
  154. return;
  155. }
  156. // Test portal placement
  157. m_fCanPlacePortal1OnThisSurface = ( ( m_bCanFirePortal1 ) ? ( FirePortal( false, 0, 1 ) ) : ( 0.0f ) );
  158. m_fCanPlacePortal2OnThisSurface = ( ( m_bCanFirePortal2 ) ? ( FirePortal( true, 0, 2 ) ) : ( 0.0f ) );
  159. // Draw obtained portal color chips
  160. int iSlot1State = ( ( m_bCanFirePortal1 ) ? ( 0 ) : ( 1 ) ); // FIXME: Portal gun might have only red but not blue;
  161. int iSlot2State = ( ( m_bCanFirePortal2 ) ? ( 0 ) : ( 1 ) );
  162. SetBodygroup( 1, iSlot1State );
  163. SetBodygroup( 2, iSlot2State );
  164. if ( pPlayer->GetViewModel() )
  165. {
  166. pPlayer->GetViewModel()->SetBodygroup( 1, iSlot1State );
  167. pPlayer->GetViewModel()->SetBodygroup( 2, iSlot2State );
  168. }
  169. // HACK HACK! Used to make the gun visually change when going through a cleanser!
  170. if ( m_fEffectsMaxSize1 > 4.0f )
  171. {
  172. m_fEffectsMaxSize1 -= gpGlobals->frametime * 400.0f;
  173. if ( m_fEffectsMaxSize1 < 4.0f )
  174. m_fEffectsMaxSize1 = 4.0f;
  175. }
  176. if ( m_fEffectsMaxSize2 > 4.0f )
  177. {
  178. m_fEffectsMaxSize2 -= gpGlobals->frametime * 400.0f;
  179. if ( m_fEffectsMaxSize2 < 4.0f )
  180. m_fEffectsMaxSize2 = 4.0f;
  181. }
  182. }
  183. void CWeaponPortalgun::OpenProngs( bool bOpenProngs )
  184. {
  185. if ( m_bOpenProngs == bOpenProngs )
  186. {
  187. return;
  188. }
  189. m_bOpenProngs = bOpenProngs;
  190. DoEffect( ( m_bOpenProngs ) ? ( EFFECT_HOLDING ) : ( EFFECT_READY ) );
  191. SendWeaponAnim( ( m_bOpenProngs ) ? ( ACT_VM_PICKUP ) : ( ACT_VM_RELEASE ) );
  192. }
  193. void CWeaponPortalgun::InputChargePortal1( inputdata_t &inputdata )
  194. {
  195. DispatchParticleEffect( "portal_1_charge", PATTACH_POINT_FOLLOW, this, "muzzle" );
  196. }
  197. void CWeaponPortalgun::InputChargePortal2( inputdata_t &inputdata )
  198. {
  199. DispatchParticleEffect( "portal_2_charge", PATTACH_POINT_FOLLOW, this, "muzzle" );
  200. }
  201. void CWeaponPortalgun::FirePortal1( inputdata_t &inputdata )
  202. {
  203. FirePortal( false );
  204. m_iLastFiredPortal = 1;
  205. CBaseCombatCharacter *pOwner = GetOwner();
  206. if( pOwner && pOwner->IsPlayer() )
  207. {
  208. WeaponSound( SINGLE );
  209. }
  210. else
  211. {
  212. WeaponSound( SINGLE_NPC );
  213. }
  214. }
  215. void CWeaponPortalgun::FirePortal2( inputdata_t &inputdata )
  216. {
  217. FirePortal( true );
  218. m_iLastFiredPortal = 2;
  219. CBaseCombatCharacter *pOwner = GetOwner();
  220. if( pOwner && pOwner->IsPlayer() )
  221. {
  222. WeaponSound( WPN_DOUBLE );
  223. }
  224. else
  225. {
  226. WeaponSound( DOUBLE_NPC );
  227. }
  228. }
  229. void CWeaponPortalgun::FirePortalDirection1( inputdata_t &inputdata )
  230. {
  231. Vector vDirection;
  232. inputdata.value.Vector3D( vDirection );
  233. FirePortal( false, &vDirection );
  234. m_iLastFiredPortal = 1;
  235. CBaseCombatCharacter *pOwner = GetOwner();
  236. if( pOwner && pOwner->IsPlayer() )
  237. {
  238. WeaponSound( SINGLE );
  239. }
  240. else
  241. {
  242. WeaponSound( SINGLE_NPC );
  243. }
  244. }
  245. void CWeaponPortalgun::FirePortalDirection2( inputdata_t &inputdata )
  246. {
  247. Vector vDirection;
  248. inputdata.value.Vector3D( vDirection );
  249. FirePortal( true, &vDirection );
  250. m_iLastFiredPortal = 2;
  251. CBaseCombatCharacter *pOwner = GetOwner();
  252. if( pOwner && pOwner->IsPlayer() )
  253. {
  254. WeaponSound( WPN_DOUBLE );
  255. }
  256. else
  257. {
  258. WeaponSound( DOUBLE_NPC );
  259. }
  260. }
  261. float CWeaponPortalgun::TraceFirePortal( bool bPortal2, const Vector &vTraceStart, const Vector &vDirection, trace_t &tr, Vector &vFinalPosition, QAngle &qFinalAngles, int iPlacedBy, bool bTest /*= false*/ )
  262. {
  263. CTraceFilterSimpleClassnameList baseFilter( this, COLLISION_GROUP_NONE );
  264. UTIL_Portal_Trace_Filter( &baseFilter );
  265. CTraceFilterTranslateClones traceFilterPortalShot( &baseFilter );
  266. Ray_t rayEyeArea;
  267. rayEyeArea.Init( vTraceStart + vDirection * 24.0f, vTraceStart + vDirection * -24.0f );
  268. float fMustBeCloserThan = 2.0f;
  269. CProp_Portal *pNearPortal = UTIL_Portal_FirstAlongRay( rayEyeArea, fMustBeCloserThan );
  270. if ( !pNearPortal )
  271. {
  272. // Check for portal near and infront of you
  273. rayEyeArea.Init( vTraceStart + vDirection * -24.0f, vTraceStart + vDirection * 48.0f );
  274. fMustBeCloserThan = 2.0f;
  275. pNearPortal = UTIL_Portal_FirstAlongRay( rayEyeArea, fMustBeCloserThan );
  276. }
  277. if ( pNearPortal && pNearPortal->IsActivedAndLinked() )
  278. {
  279. iPlacedBy = PORTAL_PLACED_BY_PEDESTAL;
  280. Vector vPortalForward;
  281. pNearPortal->GetVectors( &vPortalForward, 0, 0 );
  282. if ( vDirection.Dot( vPortalForward ) < 0.01f )
  283. {
  284. // If shooting out of the world, fizzle
  285. if ( !bTest )
  286. {
  287. CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
  288. pPortal->m_iDelayedFailure = ( ( pNearPortal->m_bIsPortal2 ) ? ( PORTAL_FIZZLE_NEAR_RED ) : ( PORTAL_FIZZLE_NEAR_BLUE ) );
  289. VectorAngles( vPortalForward, pPortal->m_qDelayedAngles );
  290. pPortal->m_vDelayedPosition = pNearPortal->GetAbsOrigin();
  291. vFinalPosition = pPortal->m_vDelayedPosition;
  292. qFinalAngles = pPortal->m_qDelayedAngles;
  293. UTIL_TraceLine( vTraceStart - vDirection * 16.0f, vTraceStart + (vDirection * m_fMaxRange1), MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );
  294. return PORTAL_ANALOG_SUCCESS_NEAR;
  295. }
  296. UTIL_TraceLine( vTraceStart - vDirection * 16.0f, vTraceStart + (vDirection * m_fMaxRange1), MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );
  297. return PORTAL_ANALOG_SUCCESS_OVERLAP_LINKED;
  298. }
  299. }
  300. // Trace to see where the portal hit
  301. UTIL_TraceLine( vTraceStart, vTraceStart + (vDirection * m_fMaxRange1), MASK_SHOT_PORTAL, &traceFilterPortalShot, &tr );
  302. if ( !tr.DidHit() || tr.startsolid )
  303. {
  304. // If it didn't hit anything, fizzle
  305. if ( !bTest )
  306. {
  307. CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
  308. pPortal->m_iDelayedFailure = PORTAL_FIZZLE_NONE;
  309. VectorAngles( -vDirection, pPortal->m_qDelayedAngles );
  310. pPortal->m_vDelayedPosition = tr.endpos;
  311. vFinalPosition = pPortal->m_vDelayedPosition;
  312. qFinalAngles = pPortal->m_qDelayedAngles;
  313. }
  314. return PORTAL_ANALOG_SUCCESS_PASSTHROUGH_SURFACE;
  315. }
  316. // Trace to the surface to see if there's a rotating door in the way
  317. CBaseEntity *list[1024];
  318. Ray_t ray;
  319. ray.Init( vTraceStart, tr.endpos );
  320. int nCount = UTIL_EntitiesAlongRay( list, 1024, ray, 0 );
  321. // Loop through all entities along the ray between the gun and the surface
  322. for ( int i = 0; i < nCount; i++ )
  323. {
  324. // If the entity is a rotating door
  325. if( FClassnameIs( list[i], "prop_door_rotating" ) )
  326. {
  327. // Check more precise door collision
  328. CBasePropDoor *pRotatingDoor = static_cast<CBasePropDoor *>( list[i] );
  329. Ray_t rayDoor;
  330. rayDoor.Init( vTraceStart, vTraceStart + (vDirection * m_fMaxRange1) );
  331. trace_t trDoor;
  332. pRotatingDoor->TestCollision( rayDoor, 0, trDoor );
  333. if ( trDoor.DidHit() )
  334. {
  335. // There's a door in the way
  336. tr = trDoor;
  337. if ( sv_portal_placement_debug.GetBool() )
  338. {
  339. Vector vMin;
  340. Vector vMax;
  341. Vector vZero = Vector( 0.0f, 0.0f, 0.0f );
  342. list[ i ]->GetCollideable()->WorldSpaceSurroundingBounds( &vMin, &vMax );
  343. NDebugOverlay::Box( vZero, vMin, vMax, 0, 255, 0, 128, 0.5f );
  344. }
  345. if ( !bTest )
  346. {
  347. CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
  348. pPortal->m_iDelayedFailure = PORTAL_FIZZLE_CANT_FIT;
  349. VectorAngles( tr.plane.normal, pPortal->m_qDelayedAngles );
  350. pPortal->m_vDelayedPosition = trDoor.endpos;
  351. vFinalPosition = pPortal->m_vDelayedPosition;
  352. qFinalAngles = pPortal->m_qDelayedAngles;
  353. }
  354. return PORTAL_ANALOG_SUCCESS_CANT_FIT;
  355. }
  356. }
  357. else if ( FClassnameIs( list[i], "trigger_portal_cleanser" ) )
  358. {
  359. CBaseTrigger *pTrigger = static_cast<CBaseTrigger*>( list[i] );
  360. if ( pTrigger && !pTrigger->m_bDisabled )
  361. {
  362. Vector vMin;
  363. Vector vMax;
  364. pTrigger->GetCollideable()->WorldSpaceSurroundingBounds( &vMin, &vMax );
  365. IntersectRayWithBox( ray.m_Start, ray.m_Delta, vMin, vMax, 0.0f, &tr );
  366. tr.plane.normal = -vDirection;
  367. if ( !bTest )
  368. {
  369. CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
  370. pPortal->m_iDelayedFailure = PORTAL_FIZZLE_CLEANSER;
  371. VectorAngles( tr.plane.normal, pPortal->m_qDelayedAngles );
  372. pPortal->m_vDelayedPosition = tr.endpos;
  373. vFinalPosition = pPortal->m_vDelayedPosition;
  374. qFinalAngles = pPortal->m_qDelayedAngles;
  375. }
  376. return PORTAL_ANALOG_SUCCESS_CLEANSER;
  377. }
  378. }
  379. }
  380. Vector vUp( 0.0f, 0.0f, 1.0f );
  381. if( ( tr.plane.normal.x > -0.001f && tr.plane.normal.x < 0.001f ) && ( tr.plane.normal.y > -0.001f && tr.plane.normal.y < 0.001f ) )
  382. {
  383. //plane is a level floor/ceiling
  384. vUp = vDirection;
  385. }
  386. // Check that the placement succeed
  387. VectorAngles( tr.plane.normal, vUp, qFinalAngles );
  388. vFinalPosition = tr.endpos;
  389. return VerifyPortalPlacement( CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2 ), vFinalPosition, qFinalAngles, iPlacedBy, bTest );
  390. }
  391. float CWeaponPortalgun::FirePortal( bool bPortal2, Vector *pVector /*= 0*/, bool bTest /*= false*/ )
  392. {
  393. bool bPlayer = false;
  394. Vector vEye;
  395. Vector vDirection;
  396. Vector vTracerOrigin;
  397. CBaseEntity *pOwner = GetOwner();
  398. if ( pOwner && pOwner->IsPlayer() )
  399. {
  400. bPlayer = true;
  401. }
  402. if( bPlayer )
  403. {
  404. CPortal_Player *pPlayer = (CPortal_Player *)pOwner;
  405. if ( !bTest && pPlayer )
  406. {
  407. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY, 0 );
  408. }
  409. Vector forward, right, up;
  410. AngleVectors( pPlayer->EyeAngles(), &forward, &right, &up );
  411. pPlayer->EyeVectors( &vDirection, NULL, NULL );
  412. vEye = pPlayer->EyePosition();
  413. // Check if the players eye is behind the portal they're in and translate it
  414. VMatrix matThisToLinked;
  415. CProp_Portal *pPlayerPortal = pPlayer->m_hPortalEnvironment;
  416. if ( pPlayerPortal )
  417. {
  418. Vector ptPortalCenter;
  419. Vector vPortalForward;
  420. ptPortalCenter = pPlayerPortal->GetAbsOrigin();
  421. pPlayerPortal->GetVectors( &vPortalForward, NULL, NULL );
  422. Vector vEyeToPortalCenter = ptPortalCenter - vEye;
  423. float fPortalDist = vPortalForward.Dot( vEyeToPortalCenter );
  424. if( fPortalDist > 0.0f )
  425. {
  426. // Eye is behind the portal
  427. matThisToLinked = pPlayerPortal->MatrixThisToLinked();
  428. }
  429. else
  430. {
  431. pPlayerPortal = NULL;
  432. }
  433. }
  434. if ( pPlayerPortal )
  435. {
  436. UTIL_Portal_VectorTransform( matThisToLinked, forward, forward );
  437. UTIL_Portal_VectorTransform( matThisToLinked, right, right );
  438. UTIL_Portal_VectorTransform( matThisToLinked, up, up );
  439. UTIL_Portal_VectorTransform( matThisToLinked, vDirection, vDirection );
  440. UTIL_Portal_PointTransform( matThisToLinked, vEye, vEye );
  441. if ( pVector )
  442. {
  443. UTIL_Portal_VectorTransform( matThisToLinked, *pVector, *pVector );
  444. }
  445. }
  446. vTracerOrigin = vEye
  447. + forward * 30.0f
  448. + right * 4.0f
  449. + up * (-5.0f);
  450. }
  451. else
  452. {
  453. // This portalgun is not held by the player-- Fire using the muzzle attachment
  454. Vector vecShootOrigin;
  455. QAngle angShootDir;
  456. GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir );
  457. vEye = vecShootOrigin;
  458. vTracerOrigin = vecShootOrigin;
  459. AngleVectors( angShootDir, &vDirection, NULL, NULL );
  460. }
  461. if ( !bTest )
  462. {
  463. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  464. }
  465. if ( pVector )
  466. {
  467. vDirection = *pVector;
  468. }
  469. Vector vTraceStart = vEye + (vDirection * m_fMinRange1);
  470. Vector vFinalPosition;
  471. QAngle qFinalAngles;
  472. PortalPlacedByType ePlacedBy = ( bPlayer ) ? ( PORTAL_PLACED_BY_PLAYER ) : ( PORTAL_PLACED_BY_PEDESTAL );
  473. trace_t tr;
  474. float fPlacementSuccess = TraceFirePortal( bPortal2, vTraceStart, vDirection, tr, vFinalPosition, qFinalAngles, ePlacedBy, bTest );
  475. if ( sv_portal_placement_never_fail.GetBool() )
  476. {
  477. fPlacementSuccess = 1.0f;
  478. }
  479. if ( !bTest )
  480. {
  481. CProp_Portal *pPortal = CProp_Portal::FindPortal( m_iPortalLinkageGroupID, bPortal2, true );
  482. // If it was a failure, put the effect at exactly where the player shot instead of where the portal bumped to
  483. if ( fPlacementSuccess < 0.5f )
  484. vFinalPosition = tr.endpos;
  485. pPortal->PlacePortal( vFinalPosition, qFinalAngles, fPlacementSuccess, true );
  486. float fDelay = vTracerOrigin.DistTo( tr.endpos ) / ( ( bPlayer ) ? ( BLAST_SPEED ) : ( BLAST_SPEED_NON_PLAYER ) );
  487. QAngle qFireAngles;
  488. VectorAngles( vDirection, qFireAngles );
  489. DoEffectBlast( pPortal->m_bIsPortal2, ePlacedBy, vTracerOrigin, vFinalPosition, qFireAngles, fDelay );
  490. pPortal->SetContextThink( &CProp_Portal::DelayedPlacementThink, gpGlobals->curtime + fDelay, s_pDelayedPlacementContext );
  491. pPortal->m_vDelayedPosition = vFinalPosition;
  492. pPortal->m_hPlacedBy = this;
  493. }
  494. return fPlacementSuccess;
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose:
  498. //-----------------------------------------------------------------------------
  499. void CWeaponPortalgun::StartEffects( void )
  500. {
  501. }
  502. void CWeaponPortalgun::DestroyEffects( void )
  503. {
  504. // Stop everything
  505. StopEffects();
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Purpose: Ready effects
  509. //-----------------------------------------------------------------------------
  510. void CWeaponPortalgun::DoEffectReady( void )
  511. {
  512. if ( m_pMiniGravHoldSound )
  513. {
  514. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  515. controller.SoundChangeVolume( m_pMiniGravHoldSound, 0.0, 0.1 );
  516. }
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Holding effects
  520. //-----------------------------------------------------------------------------
  521. void CWeaponPortalgun::DoEffectHolding( void )
  522. {
  523. if ( m_pMiniGravHoldSound )
  524. {
  525. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  526. controller.SoundChangeVolume( m_pMiniGravHoldSound, 1.0, 0.1 );
  527. }
  528. }
  529. //-----------------------------------------------------------------------------
  530. // Purpose: Shutdown for the weapon when it's holstered
  531. //-----------------------------------------------------------------------------
  532. void CWeaponPortalgun::DoEffectNone( void )
  533. {
  534. if ( m_pMiniGravHoldSound )
  535. {
  536. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  537. controller.SoundChangeVolume( m_pMiniGravHoldSound, 0.0, 0.1 );
  538. }
  539. }
  540. void CC_UpgradePortalGun( void )
  541. {
  542. CPortal_Player *pPlayer = ToPortalPlayer( UTIL_GetCommandClient() );
  543. CWeaponPortalgun *pPortalGun = static_cast<CWeaponPortalgun*>( pPlayer->Weapon_OwnsThisType( "weapon_portalgun" ) );
  544. if ( pPortalGun )
  545. {
  546. pPortalGun->SetCanFirePortal1();
  547. pPortalGun->SetCanFirePortal2();
  548. }
  549. }
  550. static ConCommand upgrade_portal("upgrade_portalgun", CC_UpgradePortalGun, "Equips the player with a single portal portalgun. Use twice for a dual portal portalgun.\n\tArguments: none ", FCVAR_CHEAT);
  551. static void change_portalgun_linkage_id_f( const CCommand &args )
  552. {
  553. if( sv_cheats->GetBool() == false ) //heavy handed version since setting the concommand with FCVAR_CHEATS isn't working like I thought
  554. return;
  555. if( args.ArgC() < 2 )
  556. return;
  557. unsigned char iNewID = (unsigned char)atoi( args[1] );
  558. CPortal_Player *pPlayer = (CPortal_Player *)UTIL_GetCommandClient();
  559. int iWeaponCount = pPlayer->WeaponCount();
  560. for( int i = 0; i != iWeaponCount; ++i )
  561. {
  562. CBaseCombatWeapon *pWeapon = pPlayer->GetWeapon(i);
  563. if( pWeapon == NULL )
  564. continue;
  565. if( dynamic_cast<CWeaponPortalgun *>(pWeapon) != NULL )
  566. {
  567. CWeaponPortalgun *pPortalGun = (CWeaponPortalgun *)pWeapon;
  568. pPortalGun->m_iPortalLinkageGroupID = iNewID;
  569. break;
  570. }
  571. }
  572. }
  573. ConCommand change_portalgun_linkage_id( "change_portalgun_linkage_id", change_portalgun_linkage_id_f, "Changes the portal linkage ID for the portal gun held by the commanding player.", FCVAR_CHEAT );