Advertisement
Guest User

Code

a guest
May 4th, 2014
47
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 64.99 KB | None | 0 0
  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: combine ball - can be held by the super physcannon and launched
  4. // by the AR2's alt-fire
  5. //
  6. //=============================================================================//
  7.  
  8. #include "cbase.h"
  9. #include "prop_combine_ball.h"
  10. #include "props.h"
  11. #include "explode.h"
  12. #include "saverestore_utlvector.h"
  13. #include "hl2_shareddefs.h"
  14. #include "materialsystem/imaterial.h"
  15. #include "beam_flags.h"
  16. #include "physics_prop_ragdoll.h"
  17. #include "soundent.h"
  18. #include "soundenvelope.h"
  19. #include "te_effect_dispatch.h"
  20. #include "ai_basenpc.h"
  21. #include "npc_bullseye.h"
  22. #include "filters.h"
  23. #include "SpriteTrail.h"
  24. #include "decals.h"
  25. #include "hl2_player.h"
  26. #include "eventqueue.h"
  27. #include "physics_collisionevent.h"
  28. #include "gamestats.h"
  29.  
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include "tier0/memdbgon.h"
  32.  
  33. #define PROP_COMBINE_BALL_MODEL "models/effects/combineball.mdl"
  34. #define PROP_COMBINE_BALL_SPRITE_TRAIL "sprites/combineball_trail_black_1.vmt"
  35.  
  36. #define PROP_COMBINE_BALL_LIFETIME 4.0f // Seconds
  37.  
  38. #define PROP_COMBINE_BALL_HOLD_DISSOLVE_TIME 8.0f
  39.  
  40. #define SF_COMBINE_BALL_BOUNCING_IN_SPAWNER 0x10000
  41.  
  42. #define MAX_COMBINEBALL_RADIUS 12
  43.  
  44. ConVar sk_npc_dmg_combineball( "sk_npc_dmg_combineball","15", FCVAR_REPLICATED);
  45. ConVar sk_combineball_guidefactor( "sk_combineball_guidefactor","0.5", FCVAR_REPLICATED);
  46. ConVar sk_combine_ball_search_radius( "sk_combine_ball_search_radius", "512", FCVAR_REPLICATED);
  47. ConVar sk_combineball_seek_angle( "sk_combineball_seek_angle","15.0", FCVAR_REPLICATED);
  48. ConVar sk_combineball_seek_kill( "sk_combineball_seek_kill","0", FCVAR_REPLICATED);
  49.  
  50. // For our ring explosion
  51. int s_nExplosionTexture = -1;
  52.  
  53. //-----------------------------------------------------------------------------
  54. // Context think
  55. //-----------------------------------------------------------------------------
  56. static const char *s_pWhizThinkContext = "WhizThinkContext";
  57. static const char *s_pHoldDissolveContext = "HoldDissolveContext";
  58. static const char *s_pExplodeTimerContext = "ExplodeTimerContext";
  59. static const char *s_pAnimThinkContext = "AnimThinkContext";
  60. static const char *s_pCaptureContext = "CaptureContext";
  61. static const char *s_pRemoveContext = "RemoveContext";
  62.  
  63. //-----------------------------------------------------------------------------
  64. // Purpose:
  65. // Input : radius -
  66. // Output : CBaseEntity
  67. //-----------------------------------------------------------------------------
  68. CBaseEntity *CreateCombineBall( const Vector &origin, const Vector &velocity, float radius, float mass, float lifetime, CBaseEntity *pOwner )
  69. {
  70. CPropCombineBall *pBall = static_cast<CPropCombineBall*>( CreateEntityByName( "prop_combine_ball" ) );
  71. pBall->SetRadius( radius );
  72.  
  73. pBall->SetAbsOrigin( origin );
  74. pBall->SetOwnerEntity( pOwner );
  75. pBall->SetOriginalOwner( pOwner );
  76.  
  77. pBall->SetAbsVelocity( velocity );
  78. pBall->Spawn();
  79.  
  80. pBall->SetState( CPropCombineBall::STATE_THROWN );
  81. pBall->SetSpeed( velocity.Length() );
  82.  
  83. pBall->EmitSound( "NPC_CombineBall.Launch" );
  84.  
  85. PhysSetGameFlags( pBall->VPhysicsGetObject(), FVPHYSICS_WAS_THROWN );
  86.  
  87. pBall->StartWhizSoundThink();
  88.  
  89. pBall->SetMass( mass );
  90. pBall->StartLifetime( lifetime );
  91. pBall->SetWeaponLaunched( true );
  92.  
  93. return pBall;
  94. }
  95.  
  96. //-----------------------------------------------------------------------------
  97. // Purpose: Allows game to know if the physics object should kill allies or not
  98. //-----------------------------------------------------------------------------
  99. CBasePlayer *CPropCombineBall::HasPhysicsAttacker( float dt )
  100. {
  101. // Must have an owner
  102. if ( GetOwnerEntity() == NULL )
  103. return NULL;
  104.  
  105. // Must be a player
  106. if ( GetOwnerEntity()->IsPlayer() == false )
  107. return NULL;
  108.  
  109. // We don't care about the time passed in
  110. return static_cast<CBasePlayer *>(GetOwnerEntity());
  111. }
  112.  
  113. //-----------------------------------------------------------------------------
  114. // Purpose: Determines whether a physics object is a combine ball or not
  115. // Input : *pObj - Object to test
  116. // Output : Returns true on success, false on failure.
  117. // Notes : This function cannot identify a combine ball that is held by
  118. // the physcannon because any object held by the physcannon is
  119. // COLLISIONGROUP_DEBRIS.
  120. //-----------------------------------------------------------------------------
  121. bool UTIL_IsCombineBall( CBaseEntity *pEntity )
  122. {
  123. // Must be the correct collision group
  124. if ( pEntity->GetCollisionGroup() != HL2COLLISION_GROUP_COMBINE_BALL )
  125. return false;
  126.  
  127. //NOTENOTE: This allows ANY combine ball to pass the test
  128.  
  129. /*
  130. CPropCombineBall *pBall = dynamic_cast<CPropCombineBall *>(pEntity);
  131.  
  132. if ( pBall && pBall->WasWeaponLaunched() )
  133. return false;
  134. */
  135.  
  136. return true;
  137. }
  138.  
  139. //-----------------------------------------------------------------------------
  140. // Purpose: Determines whether a physics object is an AR2 combine ball or not
  141. // Input : *pEntity -
  142. // Output : Returns true on success, false on failure.
  143. //-----------------------------------------------------------------------------
  144. bool UTIL_IsAR2CombineBall( CBaseEntity *pEntity )
  145. {
  146. // Must be the correct collision group
  147. if ( pEntity->GetCollisionGroup() != HL2COLLISION_GROUP_COMBINE_BALL )
  148. return false;
  149.  
  150. CPropCombineBall *pBall = dynamic_cast<CPropCombineBall *>(pEntity);
  151.  
  152. if ( pBall && pBall->WasWeaponLaunched() )
  153. return true;
  154.  
  155. return false;
  156. }
  157.  
  158. //-----------------------------------------------------------------------------
  159. // Purpose: Uses a deeper casting check to determine if pEntity is a combine
  160. // ball. This function exists because the normal (much faster) check
  161. // in UTIL_IsCombineBall() can never identify a combine ball held by
  162. // the physcannon because the physcannon changes the held entity's
  163. // collision group.
  164. // Input : *pEntity - Entity to check
  165. // Output : Returns true on success, false on failure.
  166. //-----------------------------------------------------------------------------
  167. bool UTIL_IsCombineBallDefinite( CBaseEntity *pEntity )
  168. {
  169. CPropCombineBall *pBall = dynamic_cast<CPropCombineBall *>(pEntity);
  170.  
  171. return pBall != NULL;
  172. }
  173.  
  174. //-----------------------------------------------------------------------------
  175. //
  176. // Spawns combine balls
  177. //
  178. //-----------------------------------------------------------------------------
  179. #define SF_SPAWNER_START_DISABLED 0x1000
  180. #define SF_SPAWNER_POWER_SUPPLY 0x2000
  181.  
  182.  
  183.  
  184. //-----------------------------------------------------------------------------
  185. // Implementation of CPropCombineBall
  186. //-----------------------------------------------------------------------------
  187. LINK_ENTITY_TO_CLASS( prop_combine_ball, CPropCombineBall );
  188.  
  189. //-----------------------------------------------------------------------------
  190. // Save/load:
  191. //-----------------------------------------------------------------------------
  192. BEGIN_DATADESC( CPropCombineBall )
  193.  
  194. DEFINE_FIELD( m_flLastBounceTime, FIELD_TIME ),
  195. DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
  196. DEFINE_FIELD( m_nState, FIELD_CHARACTER ),
  197. DEFINE_FIELD( m_pGlowTrail, FIELD_CLASSPTR ),
  198. DEFINE_SOUNDPATCH( m_pHoldingSound ),
  199. DEFINE_FIELD( m_bFiredGrabbedOutput, FIELD_BOOLEAN ),
  200. DEFINE_FIELD( m_bEmit, FIELD_BOOLEAN ),
  201. DEFINE_FIELD( m_bHeld, FIELD_BOOLEAN ),
  202. DEFINE_FIELD( m_bLaunched, FIELD_BOOLEAN ),
  203. DEFINE_FIELD( m_bStruckEntity, FIELD_BOOLEAN ),
  204. DEFINE_FIELD( m_bWeaponLaunched, FIELD_BOOLEAN ),
  205. DEFINE_FIELD( m_bForward, FIELD_BOOLEAN ),
  206. DEFINE_FIELD( m_flSpeed, FIELD_FLOAT ),
  207.  
  208. DEFINE_FIELD( m_flNextDamageTime, FIELD_TIME ),
  209. DEFINE_FIELD( m_flLastCaptureTime, FIELD_TIME ),
  210. DEFINE_FIELD( m_bCaptureInProgress, FIELD_BOOLEAN ),
  211. DEFINE_FIELD( m_nBounceCount, FIELD_INTEGER ),
  212. DEFINE_FIELD( m_nMaxBounces, FIELD_INTEGER ),
  213. DEFINE_FIELD( m_bBounceDie, FIELD_BOOLEAN ),
  214.  
  215.  
  216. DEFINE_FIELD( m_hSpawner, FIELD_EHANDLE ),
  217.  
  218. DEFINE_THINKFUNC( ExplodeThink ),
  219. DEFINE_THINKFUNC( WhizSoundThink ),
  220. DEFINE_THINKFUNC( DieThink ),
  221. DEFINE_THINKFUNC( DissolveThink ),
  222. DEFINE_THINKFUNC( DissolveRampSoundThink ),
  223. DEFINE_THINKFUNC( AnimThink ),
  224. DEFINE_THINKFUNC( CaptureBySpawner ),
  225.  
  226. DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode ),
  227. DEFINE_INPUTFUNC( FIELD_VOID, "FadeAndRespawn", InputFadeAndRespawn ),
  228. DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ),
  229. DEFINE_INPUTFUNC( FIELD_VOID, "Socketed", InputSocketed ),
  230.  
  231. END_DATADESC()
  232.  
  233. IMPLEMENT_SERVERCLASS_ST( CPropCombineBall, DT_PropCombineBall )
  234. SendPropBool( SENDINFO( m_bEmit ) ),
  235. SendPropFloat( SENDINFO( m_flRadius ), 0, SPROP_NOSCALE ),
  236. SendPropBool( SENDINFO( m_bHeld ) ),
  237. SendPropBool( SENDINFO( m_bLaunched ) ),
  238. END_SEND_TABLE()
  239.  
  240. //-----------------------------------------------------------------------------
  241. // Gets at the spawner
  242. //-----------------------------------------------------------------------------
  243. CFuncCombineBallSpawner *CPropCombineBall::GetSpawner()
  244. {
  245. return m_hSpawner;
  246. }
  247.  
  248. //-----------------------------------------------------------------------------
  249. // Precache
  250. //-----------------------------------------------------------------------------
  251. void CPropCombineBall::Precache( void )
  252. {
  253. //NOTENOTE: We don't call into the base class because it chains multiple
  254. // precaches we don't need to incur
  255.  
  256. PrecacheModel( PROP_COMBINE_BALL_MODEL );
  257. PrecacheModel( PROP_COMBINE_BALL_SPRITE_TRAIL );
  258.  
  259. s_nExplosionTexture = PrecacheModel( "sprites/lgtning.vmt" );
  260.  
  261. PrecacheScriptSound( "NPC_CombineBall.Launch" );
  262. PrecacheScriptSound( "NPC_CombineBall.KillImpact" );
  263.  
  264. if ( hl2_episodic.GetBool() )
  265. {
  266. PrecacheScriptSound( "NPC_CombineBall_Episodic.Explosion" );
  267. PrecacheScriptSound( "NPC_CombineBall_Episodic.WhizFlyby" );
  268. PrecacheScriptSound( "NPC_CombineBall_Episodic.Impact" );
  269. }
  270. else
  271. {
  272. PrecacheScriptSound( "NPC_CombineBall.Explosion" );
  273. PrecacheScriptSound( "NPC_CombineBall.WhizFlyby" );
  274. PrecacheScriptSound( "NPC_CombineBall.Impact" );
  275. }
  276.  
  277. PrecacheScriptSound( "NPC_CombineBall.HoldingInPhysCannon" );
  278. }
  279.  
  280.  
  281. //-----------------------------------------------------------------------------
  282. // Spherical vphysics
  283. //-----------------------------------------------------------------------------
  284. bool CPropCombineBall::OverridePropdata()
  285. {
  286. return true;
  287. }
  288.  
  289.  
  290. //-----------------------------------------------------------------------------
  291. // Spherical vphysics
  292. //-----------------------------------------------------------------------------
  293. void CPropCombineBall::SetState( int state )
  294. {
  295. if ( m_nState != state )
  296. {
  297. if ( m_nState == STATE_NOT_THROWN )
  298. {
  299. m_flLastCaptureTime = gpGlobals->curtime;
  300. }
  301.  
  302. m_nState = state;
  303. }
  304. }
  305.  
  306. bool CPropCombineBall::IsInField() const
  307. {
  308. return (m_nState == STATE_NOT_THROWN);
  309. }
  310.  
  311.  
  312. //-----------------------------------------------------------------------------
  313. // Sets the radius
  314. //-----------------------------------------------------------------------------
  315. void CPropCombineBall::SetRadius( float flRadius )
  316. {
  317. m_flRadius = clamp( flRadius, 1, MAX_COMBINEBALL_RADIUS );
  318. }
  319.  
  320. //-----------------------------------------------------------------------------
  321. // Create vphysics
  322. //-----------------------------------------------------------------------------
  323. bool CPropCombineBall::CreateVPhysics()
  324. {
  325. SetSolid( SOLID_BBOX );
  326.  
  327. float flSize = m_flRadius;
  328.  
  329. SetCollisionBounds( Vector(-flSize, -flSize, -flSize), Vector(flSize, flSize, flSize) );
  330. objectparams_t params = g_PhysDefaultObjectParams;
  331. params.pGameData = static_cast<void *>(this);
  332. int nMaterialIndex = physprops->GetSurfaceIndex("metal_bouncy");
  333. IPhysicsObject *pPhysicsObject = physenv->CreateSphereObject( flSize, nMaterialIndex, GetAbsOrigin(), GetAbsAngles(), &params, false );
  334. if ( !pPhysicsObject )
  335. return false;
  336.  
  337. VPhysicsSetObject( pPhysicsObject );
  338. SetMoveType( MOVETYPE_VPHYSICS );
  339. pPhysicsObject->Wake();
  340.  
  341. pPhysicsObject->SetMass( 750.0f );
  342. pPhysicsObject->EnableGravity( false );
  343. pPhysicsObject->EnableDrag( false );
  344.  
  345. float flDamping = 0.0f;
  346. float flAngDamping = 0.5f;
  347. pPhysicsObject->SetDamping( &flDamping, &flAngDamping );
  348. pPhysicsObject->SetInertia( Vector( 1e30, 1e30, 1e30 ) );
  349.  
  350. if( WasFiredByNPC() )
  351. {
  352. // Don't do impact damage. Just touch them and do your dissolve damage and move on.
  353. PhysSetGameFlags( pPhysicsObject, FVPHYSICS_NO_NPC_IMPACT_DMG );
  354. }
  355. else
  356. {
  357. PhysSetGameFlags( pPhysicsObject, FVPHYSICS_DMG_DISSOLVE | FVPHYSICS_HEAVY_OBJECT );
  358. }
  359.  
  360. return true;
  361. }
  362.  
  363.  
  364. //-----------------------------------------------------------------------------
  365. // Spawn:
  366. //-----------------------------------------------------------------------------
  367. void CPropCombineBall::Spawn( void )
  368. {
  369. BaseClass::Spawn();
  370.  
  371. SetModel( PROP_COMBINE_BALL_MODEL );
  372.  
  373. if( ShouldHitPlayer() )
  374. {
  375. // This allows the combine ball to hit the player.
  376. SetCollisionGroup( HL2COLLISION_GROUP_COMBINE_BALL_NPC );
  377. }
  378. else
  379. {
  380. SetCollisionGroup( HL2COLLISION_GROUP_COMBINE_BALL );
  381. }
  382.  
  383. CreateVPhysics();
  384.  
  385. Vector vecAbsVelocity = GetAbsVelocity();
  386. VPhysicsGetObject()->SetVelocity( &vecAbsVelocity, NULL );
  387.  
  388. m_nState = STATE_NOT_THROWN;
  389. m_flLastBounceTime = -1.0f;
  390. m_bFiredGrabbedOutput = false;
  391. m_bForward = true;
  392. m_bCaptureInProgress = false;
  393.  
  394. // No shadow!
  395. AddEffects( EF_NOSHADOW );
  396.  
  397. // Start up the eye trail
  398. m_pGlowTrail = CSpriteTrail::SpriteTrailCreate( PROP_COMBINE_BALL_SPRITE_TRAIL, GetAbsOrigin(), false );
  399.  
  400. if ( m_pGlowTrail != NULL )
  401. {
  402. m_pGlowTrail->FollowEntity( this );
  403. m_pGlowTrail->SetTransparency( kRenderTransAdd, 0, 0, 0, 255, kRenderFxNone );
  404. m_pGlowTrail->SetStartWidth( m_flRadius );
  405. m_pGlowTrail->SetEndWidth( 0 );
  406. m_pGlowTrail->SetLifeTime( 0.1f );
  407. m_pGlowTrail->TurnOff();
  408. }
  409.  
  410. m_bEmit = true;
  411. m_bHeld = false;
  412. m_bLaunched = false;
  413. m_bStruckEntity = false;
  414. m_bWeaponLaunched = false;
  415.  
  416. m_flNextDamageTime = gpGlobals->curtime;
  417. }
  418.  
  419. //-----------------------------------------------------------------------------
  420. // Purpose:
  421. //-----------------------------------------------------------------------------
  422. void CPropCombineBall::StartAnimating( void )
  423. {
  424. // Start our animation cycle. Use the random to avoid everything thinking the same frame
  425. SetContextThink( &CPropCombineBall::AnimThink, gpGlobals->curtime + random->RandomFloat( 0.0f, 0.1f), s_pAnimThinkContext );
  426.  
  427. int nSequence = LookupSequence( "idle" );
  428.  
  429. SetCycle( 0 );
  430. m_flAnimTime = gpGlobals->curtime;
  431. ResetSequence( nSequence );
  432. ResetClientsideFrame();
  433. }
  434.  
  435. //-----------------------------------------------------------------------------
  436. // Purpose:
  437. //-----------------------------------------------------------------------------
  438. void CPropCombineBall::StopAnimating( void )
  439. {
  440. SetContextThink( NULL, gpGlobals->curtime, s_pAnimThinkContext );
  441. }
  442.  
  443. //-----------------------------------------------------------------------------
  444. // Put it into the spawner
  445. //-----------------------------------------------------------------------------
  446. void CPropCombineBall::CaptureBySpawner( )
  447. {
  448. m_bCaptureInProgress = true;
  449. m_bFiredGrabbedOutput = false;
  450.  
  451. // Slow down the ball
  452. Vector vecVelocity;
  453. VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL );
  454. float flSpeed = VectorNormalize( vecVelocity );
  455. if ( flSpeed > 25.0f )
  456. {
  457. vecVelocity *= flSpeed * 0.4f;
  458. VPhysicsGetObject()->SetVelocity( &vecVelocity, NULL );
  459.  
  460. // Slow it down until we can set its velocity ok
  461. SetContextThink( &CPropCombineBall::CaptureBySpawner, gpGlobals->curtime + 0.01f, s_pCaptureContext );
  462. return;
  463. }
  464.  
  465. // Ok, we're captured
  466. SetContextThink( NULL, gpGlobals->curtime, s_pCaptureContext );
  467. ReplaceInSpawner( GetSpawner()->GetBallSpeed() );
  468. m_bCaptureInProgress = false;
  469. }
  470.  
  471. //-----------------------------------------------------------------------------
  472. // Put it into the spawner
  473. //-----------------------------------------------------------------------------
  474. void CPropCombineBall::ReplaceInSpawner( float flSpeed )
  475. {
  476. m_bForward = true;
  477. m_nState = STATE_NOT_THROWN;
  478.  
  479. // Prevent it from exploding
  480. ClearLifetime( );
  481.  
  482. // Stop whiz noises
  483. SetContextThink( NULL, gpGlobals->curtime, s_pWhizThinkContext );
  484.  
  485. // Slam velocity to what the field wants
  486. Vector vecTarget, vecVelocity;
  487. GetSpawner()->GetTargetEndpoint( m_bForward, &vecTarget );
  488. VectorSubtract( vecTarget, GetAbsOrigin(), vecVelocity );
  489. VectorNormalize( vecVelocity );
  490. vecVelocity *= flSpeed;
  491. VPhysicsGetObject()->SetVelocity( &vecVelocity, NULL );
  492.  
  493. // Set our desired speed to the spawner's speed. This will be
  494. // our speed on our first bounce in the field.
  495. SetSpeed( flSpeed );
  496. }
  497.  
  498.  
  499. float CPropCombineBall::LastCaptureTime() const
  500. {
  501. if ( IsInField() || IsBeingCaptured() )
  502. return gpGlobals->curtime;
  503.  
  504. return m_flLastCaptureTime;
  505. }
  506.  
  507. //-----------------------------------------------------------------------------
  508. // Purpose: Starts the lifetime countdown on the ball
  509. // Input : flDuration - number of seconds to live before exploding
  510. //-----------------------------------------------------------------------------
  511. void CPropCombineBall::StartLifetime( float flDuration )
  512. {
  513. SetContextThink( &CPropCombineBall::ExplodeThink, gpGlobals->curtime + flDuration, s_pExplodeTimerContext );
  514. }
  515.  
  516. //-----------------------------------------------------------------------------
  517. // Purpose: Stops the lifetime on the ball from expiring
  518. //-----------------------------------------------------------------------------
  519. void CPropCombineBall::ClearLifetime( void )
  520. {
  521. // Prevent it from exploding
  522. SetContextThink( NULL, gpGlobals->curtime, s_pExplodeTimerContext );
  523. }
  524.  
  525. //-----------------------------------------------------------------------------
  526. // Purpose:
  527. // Input : mass -
  528. //-----------------------------------------------------------------------------
  529. void CPropCombineBall::SetMass( float mass )
  530. {
  531. IPhysicsObject *pObj = VPhysicsGetObject();
  532.  
  533. if ( pObj != NULL )
  534. {
  535. pObj->SetMass( mass );
  536. pObj->SetInertia( Vector( 500, 500, 500 ) );
  537. }
  538. }
  539.  
  540. //-----------------------------------------------------------------------------
  541. // Purpose:
  542. //-----------------------------------------------------------------------------
  543. bool CPropCombineBall::ShouldHitPlayer() const
  544. {
  545. if ( GetOwnerEntity() )
  546. {
  547. CAI_BaseNPC *pNPC = GetOwnerEntity()->MyNPCPointer();
  548. if ( pNPC && !pNPC->IsPlayerAlly() )
  549. {
  550. return true;
  551. }
  552. }
  553. return false;
  554. }
  555.  
  556. //-----------------------------------------------------------------------------
  557. // Purpose:
  558. //-----------------------------------------------------------------------------
  559. void CPropCombineBall::InputKill( inputdata_t &inputdata )
  560. {
  561. // tell owner ( if any ) that we're dead.This is mostly for NPCMaker functionality.
  562. CBaseEntity *pOwner = GetOwnerEntity();
  563. if ( pOwner )
  564. {
  565. pOwner->DeathNotice( this );
  566. SetOwnerEntity( NULL );
  567. }
  568.  
  569. UTIL_Remove( this );
  570.  
  571. NotifySpawnerOfRemoval();
  572. }
  573.  
  574. //-----------------------------------------------------------------------------
  575. // Purpose:
  576. //-----------------------------------------------------------------------------
  577. void CPropCombineBall::InputSocketed( inputdata_t &inputdata )
  578. {
  579. // tell owner ( if any ) that we're dead.This is mostly for NPCMaker functionality.
  580. CBaseEntity *pOwner = GetOwnerEntity();
  581. if ( pOwner )
  582. {
  583. pOwner->DeathNotice( this );
  584. SetOwnerEntity( NULL );
  585. }
  586.  
  587. // if our owner is a player, tell them we were socketed
  588. CHL2_Player *pPlayer = dynamic_cast<CHL2_Player *>( pOwner );
  589. if ( pPlayer )
  590. {
  591. pPlayer->CombineBallSocketed( this );
  592. }
  593.  
  594. UTIL_Remove( this );
  595.  
  596. NotifySpawnerOfRemoval();
  597. }
  598.  
  599. //-----------------------------------------------------------------------------
  600. // Cleanup.
  601. //-----------------------------------------------------------------------------
  602. void CPropCombineBall::UpdateOnRemove()
  603. {
  604. if ( m_pGlowTrail != NULL )
  605. {
  606. UTIL_Remove( m_pGlowTrail );
  607. m_pGlowTrail = NULL;
  608. }
  609.  
  610. //Sigh... this is the only place where I can get a message after the ball is done dissolving.
  611. if ( hl2_episodic.GetBool() )
  612. {
  613. if ( IsDissolving() )
  614. {
  615. if ( GetSpawner() )
  616. {
  617. GetSpawner()->BallGrabbed( this );
  618. NotifySpawnerOfRemoval();
  619. }
  620. }
  621. }
  622.  
  623. BaseClass::UpdateOnRemove();
  624. }
  625.  
  626. //-----------------------------------------------------------------------------
  627. // Purpose:
  628. //-----------------------------------------------------------------------------
  629. void CPropCombineBall::ExplodeThink( void )
  630. {
  631. DoExplosion();
  632. }
  633.  
  634. //-----------------------------------------------------------------------------
  635. // Purpose: Tell the respawner to make a new one
  636. //-----------------------------------------------------------------------------
  637. void CPropCombineBall::NotifySpawnerOfRemoval( void )
  638. {
  639. if ( GetSpawner() )
  640. {
  641. GetSpawner()->RespawnBallPostExplosion();
  642. }
  643. }
  644.  
  645. //-----------------------------------------------------------------------------
  646. // Fade out.
  647. //-----------------------------------------------------------------------------
  648. void CPropCombineBall::DieThink()
  649. {
  650. if ( GetSpawner() )
  651. {
  652. //Let the spawner know we died so it does it's thing
  653. if( hl2_episodic.GetBool() && IsInField() )
  654. {
  655. GetSpawner()->BallGrabbed( this );
  656. }
  657.  
  658. GetSpawner()->RespawnBall( 0.1 );
  659. }
  660.  
  661. UTIL_Remove( this );
  662. }
  663.  
  664.  
  665. //-----------------------------------------------------------------------------
  666. // Fade out.
  667. //-----------------------------------------------------------------------------
  668. void CPropCombineBall::FadeOut( float flDuration )
  669. {
  670. AddSolidFlags( FSOLID_NOT_SOLID );
  671.  
  672. // Start up the eye trail
  673. if ( m_pGlowTrail != NULL )
  674. {
  675. m_pGlowTrail->SetBrightness( 0, flDuration );
  676. }
  677.  
  678. SetThink( &CPropCombineBall::DieThink );
  679. SetNextThink( gpGlobals->curtime + flDuration );
  680. }
  681.  
  682. //-----------------------------------------------------------------------------
  683. // Purpose:
  684. //-----------------------------------------------------------------------------
  685. void CPropCombineBall::StartWhizSoundThink( void )
  686. {
  687. SetContextThink( &CPropCombineBall::WhizSoundThink, gpGlobals->curtime + 2.0f * TICK_INTERVAL, s_pWhizThinkContext );
  688. }
  689.  
  690. //-----------------------------------------------------------------------------
  691. // Danger sounds.
  692. //-----------------------------------------------------------------------------
  693. void CPropCombineBall::WhizSoundThink()
  694. {
  695. Vector vecPosition, vecVelocity;
  696. IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
  697.  
  698. if ( pPhysicsObject == NULL )
  699. {
  700. //NOTENOTE: We should always have been created at this point
  701. Assert( 0 );
  702. SetContextThink( &CPropCombineBall::WhizSoundThink, gpGlobals->curtime + 2.0f * TICK_INTERVAL, s_pWhizThinkContext );
  703. return;
  704. }
  705.  
  706. pPhysicsObject->GetPosition( &vecPosition, NULL );
  707. pPhysicsObject->GetVelocity( &vecVelocity, NULL );
  708.  
  709. if ( gpGlobals->maxClients == 1 )
  710. {
  711. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  712. if ( pPlayer )
  713. {
  714. Vector vecDelta;
  715. VectorSubtract( pPlayer->GetAbsOrigin(), vecPosition, vecDelta );
  716. VectorNormalize( vecDelta );
  717. if ( DotProduct( vecDelta, vecVelocity ) > 0.5f )
  718. {
  719. Vector vecEndPoint;
  720. VectorMA( vecPosition, 2.0f * TICK_INTERVAL, vecVelocity, vecEndPoint );
  721. float flDist = CalcDistanceToLineSegment( pPlayer->GetAbsOrigin(), vecPosition, vecEndPoint );
  722. if ( flDist < 200.0f )
  723. {
  724. CPASAttenuationFilter filter( vecPosition, ATTN_NORM );
  725.  
  726. EmitSound_t ep;
  727. ep.m_nChannel = CHAN_STATIC;
  728. if ( hl2_episodic.GetBool() )
  729. {
  730. ep.m_pSoundName = "NPC_CombineBall_Episodic.WhizFlyby";
  731. }
  732. else
  733. {
  734. ep.m_pSoundName = "NPC_CombineBall.WhizFlyby";
  735. }
  736. ep.m_flVolume = 1.0f;
  737. ep.m_SoundLevel = SNDLVL_NORM;
  738.  
  739. EmitSound( filter, entindex(), ep );
  740.  
  741. SetContextThink( &CPropCombineBall::WhizSoundThink, gpGlobals->curtime + 0.5f, s_pWhizThinkContext );
  742. return;
  743. }
  744. }
  745. }
  746. }
  747.  
  748. SetContextThink( &CPropCombineBall::WhizSoundThink, gpGlobals->curtime + 2.0f * TICK_INTERVAL, s_pWhizThinkContext );
  749. }
  750.  
  751. //-----------------------------------------------------------------------------
  752. // Purpose:
  753. //-----------------------------------------------------------------------------
  754. void CPropCombineBall::SetBallAsLaunched( void )
  755. {
  756. // Give the ball a duration
  757. StartLifetime( PROP_COMBINE_BALL_LIFETIME );
  758.  
  759. m_bHeld = false;
  760. m_bLaunched = true;
  761. SetState( STATE_THROWN );
  762.  
  763. VPhysicsGetObject()->SetMass( 750.0f );
  764. VPhysicsGetObject()->SetInertia( Vector( 1e30, 1e30, 1e30 ) );
  765.  
  766. StopLoopingSounds();
  767. EmitSound( "NPC_CombineBall.Launch" );
  768.  
  769. WhizSoundThink();
  770. }
  771.  
  772. //-----------------------------------------------------------------------------
  773. // Lighten the mass so it's zippy toget to the gun
  774. //-----------------------------------------------------------------------------
  775. void CPropCombineBall::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason )
  776. {
  777. CDefaultPlayerPickupVPhysics::OnPhysGunPickup( pPhysGunUser, reason );
  778.  
  779. if ( m_nMaxBounces == -1 )
  780. {
  781. m_nMaxBounces = 0;
  782. }
  783.  
  784. if ( !m_bFiredGrabbedOutput )
  785. {
  786. if ( GetSpawner() )
  787. {
  788. GetSpawner()->BallGrabbed( this );
  789. }
  790.  
  791. m_bFiredGrabbedOutput = true;
  792. }
  793.  
  794. if ( m_pGlowTrail )
  795. {
  796. m_pGlowTrail->TurnOff();
  797. m_pGlowTrail->SetRenderColor( 0, 0, 0, 0 );
  798. }
  799.  
  800. if ( reason != PUNTED_BY_CANNON )
  801. {
  802. SetState( STATE_HOLDING );
  803. CPASAttenuationFilter filter( GetAbsOrigin(), ATTN_NORM );
  804. filter.MakeReliable();
  805.  
  806. EmitSound_t ep;
  807. ep.m_nChannel = CHAN_STATIC;
  808.  
  809. if( hl2_episodic.GetBool() )
  810. {
  811. ep.m_pSoundName = "NPC_CombineBall_Episodic.HoldingInPhysCannon";
  812. }
  813. else
  814. {
  815. ep.m_pSoundName = "NPC_CombineBall.HoldingInPhysCannon";
  816. }
  817.  
  818. ep.m_flVolume = 1.0f;
  819. ep.m_SoundLevel = SNDLVL_NORM;
  820.  
  821. // Now we own this ball
  822. SetPlayerLaunched( pPhysGunUser );
  823.  
  824. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  825. m_pHoldingSound = controller.SoundCreate( filter, entindex(), ep );
  826. controller.Play( m_pHoldingSound, 1.0f, 100 );
  827.  
  828. // Don't collide with anything we may have to pull the ball through
  829. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  830.  
  831. VPhysicsGetObject()->SetMass( 20.0f );
  832. VPhysicsGetObject()->SetInertia( Vector( 100, 100, 100 ) );
  833.  
  834. // Make it not explode
  835. ClearLifetime( );
  836.  
  837. m_bHeld = true;
  838. m_bLaunched = false;
  839.  
  840. //Let the ball know is not being captured by one of those ball fields anymore.
  841. //
  842. m_bCaptureInProgress = false;
  843.  
  844.  
  845. SetContextThink( &CPropCombineBall::DissolveRampSoundThink, gpGlobals->curtime + GetBallHoldSoundRampTime(), s_pHoldDissolveContext );
  846.  
  847. StartAnimating();
  848. }
  849. else
  850. {
  851. Vector vecVelocity;
  852. VPhysicsGetObject()->GetVelocity( &vecVelocity, NULL );
  853.  
  854. SetSpeed( vecVelocity.Length() );
  855.  
  856. // Set us as being launched by the player
  857. SetPlayerLaunched( pPhysGunUser );
  858.  
  859. SetBallAsLaunched();
  860.  
  861. StopAnimating();
  862. }
  863. }
  864.  
  865. //-----------------------------------------------------------------------------
  866. // Purpose: Reset the ball to be deadly to NPCs after we've picked it up
  867. //-----------------------------------------------------------------------------
  868. void CPropCombineBall::SetPlayerLaunched( CBasePlayer *pOwner )
  869. {
  870. // Now we own this ball
  871. SetOwnerEntity( pOwner );
  872. SetWeaponLaunched( false );
  873.  
  874. if( VPhysicsGetObject() )
  875. {
  876. PhysClearGameFlags( VPhysicsGetObject(), FVPHYSICS_NO_NPC_IMPACT_DMG );
  877. PhysSetGameFlags( VPhysicsGetObject(), FVPHYSICS_DMG_DISSOLVE | FVPHYSICS_HEAVY_OBJECT );
  878. }
  879. }
  880.  
  881. //-----------------------------------------------------------------------------
  882. // Activate death-spin!
  883. //-----------------------------------------------------------------------------
  884. void CPropCombineBall::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason )
  885. {
  886. CDefaultPlayerPickupVPhysics::OnPhysGunDrop( pPhysGunUser, Reason );
  887.  
  888. SetState( STATE_THROWN );
  889. WhizSoundThink();
  890.  
  891. m_bHeld = false;
  892. m_bLaunched = true;
  893.  
  894. // Stop with the dissolving
  895. SetContextThink( NULL, gpGlobals->curtime, s_pHoldDissolveContext );
  896.  
  897. // We're ready to start colliding again.
  898. SetCollisionGroup( HL2COLLISION_GROUP_COMBINE_BALL );
  899.  
  900. if ( m_pGlowTrail )
  901. {
  902. m_pGlowTrail->TurnOn();
  903. m_pGlowTrail->SetRenderColor( 255, 255, 255, 255 );
  904. }
  905.  
  906. // Set our desired speed to be launched at
  907. SetSpeed( 1500.0f );
  908. SetPlayerLaunched( pPhysGunUser );
  909.  
  910. if ( Reason != LAUNCHED_BY_CANNON )
  911. {
  912. // Choose a random direction (forward facing)
  913. Vector vecForward;
  914. pPhysGunUser->GetVectors( &vecForward, NULL, NULL );
  915.  
  916. QAngle shotAng;
  917. VectorAngles( vecForward, shotAng );
  918.  
  919. // Offset by some small cone
  920. shotAng[PITCH] += random->RandomInt( -55, 55 );
  921. shotAng[YAW] += random->RandomInt( -55, 55 );
  922.  
  923. AngleVectors( shotAng, &vecForward, NULL, NULL );
  924.  
  925. vecForward *= GetSpeed();
  926.  
  927. VPhysicsGetObject()->SetVelocity( &vecForward, &vec3_origin );
  928. }
  929. else
  930. {
  931. // This will have the consequence of making it so that the
  932. // ball is launched directly down the crosshair even if the player is moving.
  933. VPhysicsGetObject()->SetVelocity( &vec3_origin, &vec3_origin );
  934. }
  935.  
  936. SetBallAsLaunched();
  937. StopAnimating();
  938. }
  939.  
  940. //------------------------------------------------------------------------------
  941. // Stop looping sounds
  942. //------------------------------------------------------------------------------
  943. void CPropCombineBall::StopLoopingSounds()
  944. {
  945. if ( m_pHoldingSound )
  946. {
  947. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  948. controller.Shutdown( m_pHoldingSound );
  949. controller.SoundDestroy( m_pHoldingSound );
  950. m_pHoldingSound = NULL;
  951. }
  952. }
  953.  
  954.  
  955. //------------------------------------------------------------------------------
  956. // Pow!
  957. //------------------------------------------------------------------------------
  958. void CPropCombineBall::DissolveRampSoundThink( )
  959. {
  960. float dt = GetBallHoldDissolveTime() - GetBallHoldSoundRampTime();
  961. if ( m_pHoldingSound )
  962. {
  963. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  964. controller.SoundChangePitch( m_pHoldingSound, 150, dt );
  965. }
  966. SetContextThink( &CPropCombineBall::DissolveThink, gpGlobals->curtime + dt, s_pHoldDissolveContext );
  967. }
  968.  
  969.  
  970. //------------------------------------------------------------------------------
  971. // Pow!
  972. //------------------------------------------------------------------------------
  973. void CPropCombineBall::DissolveThink( )
  974. {
  975. DoExplosion();
  976. }
  977.  
  978. //-----------------------------------------------------------------------------
  979. //-----------------------------------------------------------------------------
  980. float CPropCombineBall::GetBallHoldDissolveTime()
  981. {
  982. float flDissolveTime = PROP_COMBINE_BALL_HOLD_DISSOLVE_TIME;
  983.  
  984. if( g_pGameRules->IsSkillLevel( 1 ) && hl2_episodic.GetBool() )
  985. {
  986. // Give players more time to handle/aim combine balls on Easy.
  987. flDissolveTime *= 1.5f;
  988. }
  989.  
  990. return flDissolveTime;
  991. }
  992.  
  993. //-----------------------------------------------------------------------------
  994. //-----------------------------------------------------------------------------
  995. float CPropCombineBall::GetBallHoldSoundRampTime()
  996. {
  997. return GetBallHoldDissolveTime() - 1.0f;
  998. }
  999.  
  1000. //------------------------------------------------------------------------------
  1001. // Pow!
  1002. //------------------------------------------------------------------------------
  1003. void CPropCombineBall::DoExplosion( )
  1004. {
  1005. // don't do this twice
  1006. if ( GetMoveType() == MOVETYPE_NONE )
  1007. return;
  1008.  
  1009. if ( PhysIsInCallback() )
  1010. {
  1011. g_PostSimulationQueue.QueueCall( this, &CPropCombineBall::DoExplosion );
  1012. return;
  1013. }
  1014. // Tell the respawner to make a new one
  1015. if ( GetSpawner() )
  1016. {
  1017. GetSpawner()->RespawnBallPostExplosion();
  1018. }
  1019.  
  1020. //Shockring
  1021. CBroadcastRecipientFilter filter2;
  1022.  
  1023. if ( OutOfBounces() == false )
  1024. {
  1025. if ( hl2_episodic.GetBool() )
  1026. {
  1027. EmitSound( "NPC_CombineBall_Episodic.Explosion" );
  1028. }
  1029. else
  1030. {
  1031. EmitSound( "NPC_CombineBall.Explosion" );
  1032. }
  1033.  
  1034. UTIL_ScreenShake( GetAbsOrigin(), 20.0f, 150.0, 1.0, 1250.0f, SHAKE_START );
  1035.  
  1036. CEffectData data;
  1037.  
  1038. data.m_vOrigin = GetAbsOrigin();
  1039.  
  1040. DispatchEffect( "cball_explode", data );
  1041.  
  1042. te->BeamRingPoint( filter2, 0, GetAbsOrigin(), //origin
  1043. m_flRadius, //start radius
  1044. 1024, //end radius
  1045. s_nExplosionTexture, //texture
  1046. 0, //halo index
  1047. 0, //start frame
  1048. 2, //framerate
  1049. 0.2f, //life
  1050. 64, //width
  1051. 0, //spread
  1052. 0, //amplitude
  1053. 255, //r
  1054. 255, //g
  1055. 225, //b
  1056. 32, //a
  1057. 0, //speed
  1058. FBEAM_FADEOUT
  1059. );
  1060.  
  1061. //Shockring
  1062. te->BeamRingPoint( filter2, 0, GetAbsOrigin(), //origin
  1063. m_flRadius, //start radius
  1064. 1024, //end radius
  1065. s_nExplosionTexture, //texture
  1066. 0, //halo index
  1067. 0, //start frame
  1068. 2, //framerate
  1069. 0.5f, //life
  1070. 64, //width
  1071. 0, //spread
  1072. 0, //amplitude
  1073. 255, //r
  1074. 255, //g
  1075. 225, //b
  1076. 64, //a
  1077. 0, //speed
  1078. FBEAM_FADEOUT
  1079. );
  1080. }
  1081. else
  1082. {
  1083. //Shockring
  1084. te->BeamRingPoint( filter2, 0, GetAbsOrigin(), //origin
  1085. 128, //start radius
  1086. 384, //end radius
  1087. s_nExplosionTexture, //texture
  1088. 0, //halo index
  1089. 0, //start frame
  1090. 2, //framerate
  1091. 0.25f, //life
  1092. 48, //width
  1093. 0, //spread
  1094. 0, //amplitude
  1095. 255, //r
  1096. 255, //g
  1097. 225, //b
  1098. 64, //a
  1099. 0, //speed
  1100. FBEAM_FADEOUT
  1101. );
  1102. }
  1103.  
  1104. if( hl2_episodic.GetBool() )
  1105. {
  1106. CSoundEnt::InsertSound( SOUND_COMBAT | SOUND_CONTEXT_EXPLOSION, WorldSpaceCenter(), 180.0f, 0.25, this );
  1107. }
  1108.  
  1109. // Turn us off and wait because we need our trails to finish up properly
  1110. SetAbsVelocity( vec3_origin );
  1111. SetMoveType( MOVETYPE_NONE );
  1112. AddSolidFlags( FSOLID_NOT_SOLID );
  1113.  
  1114. m_bEmit = false;
  1115.  
  1116.  
  1117. if( !m_bStruckEntity && hl2_episodic.GetBool() && GetOwnerEntity() != NULL )
  1118. {
  1119. // Notify the player proxy that this combine ball missed so that it can fire an output.
  1120. CHL2_Player *pPlayer = dynamic_cast<CHL2_Player *>( GetOwnerEntity() );
  1121. if ( pPlayer )
  1122. {
  1123. pPlayer->MissedAR2AltFire();
  1124. }
  1125. }
  1126.  
  1127. SetContextThink( &CPropCombineBall::SUB_Remove, gpGlobals->curtime + 0.5f, s_pRemoveContext );
  1128. StopLoopingSounds();
  1129. }
  1130.  
  1131. //-----------------------------------------------------------------------------
  1132. // Enable/disable
  1133. //-----------------------------------------------------------------------------
  1134. void CPropCombineBall::InputExplode( inputdata_t &inputdata )
  1135. {
  1136. DoExplosion();
  1137. }
  1138.  
  1139. //-----------------------------------------------------------------------------
  1140. // Enable/disable
  1141. //-----------------------------------------------------------------------------
  1142. void CPropCombineBall::InputFadeAndRespawn( inputdata_t &inputdata )
  1143. {
  1144. FadeOut( 0.1f );
  1145. }
  1146.  
  1147. //-----------------------------------------------------------------------------
  1148. // Purpose:
  1149. //-----------------------------------------------------------------------------
  1150. void CPropCombineBall::CollisionEventToTrace( int index, gamevcollisionevent_t *pEvent, trace_t &tr )
  1151. {
  1152. UTIL_ClearTrace( tr );
  1153. pEvent->pInternalData->GetSurfaceNormal( tr.plane.normal );
  1154. pEvent->pInternalData->GetContactPoint( tr.endpos );
  1155. tr.plane.dist = DotProduct( tr.plane.normal, tr.endpos );
  1156. VectorMA( tr.endpos, -1.0f, pEvent->preVelocity[index], tr.startpos );
  1157. tr.m_pEnt = pEvent->pEntities[!index];
  1158. tr.fraction = 0.01f; // spoof!
  1159. }
  1160.  
  1161. //-----------------------------------------------------------------------------
  1162. //-----------------------------------------------------------------------------
  1163. bool CPropCombineBall::DissolveEntity( CBaseEntity *pEntity )
  1164. {
  1165. if( pEntity->IsEFlagSet( EFL_NO_DISSOLVE ) )
  1166. return false;
  1167.  
  1168. #ifdef HL2MP
  1169. if ( pEntity->IsPlayer() )
  1170. {
  1171. m_bStruckEntity = true;
  1172. return false;
  1173. }
  1174. #endif
  1175.  
  1176. if( !pEntity->IsNPC() && !(dynamic_cast<CRagdollProp*>(pEntity)) )
  1177. return false;
  1178.  
  1179. pEntity->GetBaseAnimating()->Dissolve( "", gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
  1180.  
  1181. // Note that we've struck an entity
  1182. m_bStruckEntity = true;
  1183.  
  1184. // Force an NPC to not drop their weapon if dissolved
  1185. // CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
  1186. // if ( pBCC != NULL )
  1187. // {
  1188. // pEntity->AddSpawnFlags( SF_NPC_NO_WEAPON_DROP );
  1189. // }
  1190.  
  1191. return true;
  1192. }
  1193.  
  1194. //-----------------------------------------------------------------------------
  1195. // Purpose:
  1196. //-----------------------------------------------------------------------------
  1197. void CPropCombineBall::OnHitEntity( CBaseEntity *pHitEntity, float flSpeed, int index, gamevcollisionevent_t *pEvent )
  1198. {
  1199. // Detonate on the strider + the bone followers in the strider
  1200. if ( FClassnameIs( pHitEntity, "npc_strider" ) ||
  1201. (pHitEntity->GetOwnerEntity() && FClassnameIs( pHitEntity->GetOwnerEntity(), "npc_strider" )) )
  1202. {
  1203. DoExplosion();
  1204. return;
  1205. }
  1206.  
  1207. CTakeDamageInfo info( this, GetOwnerEntity(), GetAbsVelocity(), GetAbsOrigin(), sk_npc_dmg_combineball.GetFloat(), DMG_DISSOLVE );
  1208.  
  1209. bool bIsDissolving = (pHitEntity->GetFlags() & FL_DISSOLVING) != 0;
  1210. bool bShouldHit = pHitEntity->PassesDamageFilter( info );
  1211.  
  1212. //One more check
  1213. //Combine soldiers are not allowed to hurt their friends with combine balls (they can still shoot and hurt each other with grenades).
  1214. CBaseCombatCharacter *pBCC = pHitEntity->MyCombatCharacterPointer();
  1215.  
  1216. if ( pBCC )
  1217. {
  1218. bShouldHit = pBCC->IRelationType( GetOwnerEntity() ) != D_LI;
  1219. }
  1220.  
  1221. if ( !bIsDissolving && bShouldHit == true )
  1222. {
  1223. if ( pHitEntity->PassesDamageFilter( info ) )
  1224. {
  1225. if( WasFiredByNPC() || m_nMaxBounces == -1 )
  1226. {
  1227. // Since Combine balls fired by NPCs do a metered dose of damage per impact, we have to ignore touches
  1228. // for a little while after we hit someone, or the ball will immediately touch them again and do more
  1229. // damage.
  1230. if( gpGlobals->curtime >= m_flNextDamageTime )
  1231. {
  1232. EmitSound( "NPC_CombineBall.KillImpact" );
  1233.  
  1234. if ( pHitEntity->IsNPC() && pHitEntity->Classify() != CLASS_PLAYER_ALLY_VITAL && hl2_episodic.GetBool() == true )
  1235. {
  1236. if ( pHitEntity->Classify() != CLASS_PLAYER_ALLY || ( pHitEntity->Classify() == CLASS_PLAYER_ALLY && m_bStruckEntity == false ) )
  1237. {
  1238. info.SetDamage( pHitEntity->GetMaxHealth() );
  1239. m_bStruckEntity = true;
  1240. }
  1241. }
  1242. else
  1243. {
  1244. // Ignore touches briefly.
  1245. m_flNextDamageTime = gpGlobals->curtime + 0.1f;
  1246. }
  1247.  
  1248. pHitEntity->TakeDamage( info );
  1249. }
  1250. }
  1251. else
  1252. {
  1253. if ( (m_nState == STATE_THROWN) && (pHitEntity->IsNPC() || dynamic_cast<CRagdollProp*>(pHitEntity) ))
  1254. {
  1255. EmitSound( "NPC_CombineBall.KillImpact" );
  1256. }
  1257. if ( (m_nState != STATE_HOLDING) )
  1258. {
  1259.  
  1260. CBasePlayer *pPlayer = ToBasePlayer( GetOwnerEntity() );
  1261. if ( pPlayer && UTIL_IsAR2CombineBall( this ) && ToBaseCombatCharacter( pHitEntity ) )
  1262. {
  1263. gamestats->Event_WeaponHit( pPlayer, false, "weapon_ar2", info );
  1264. }
  1265.  
  1266. DissolveEntity( pHitEntity );
  1267. if ( pHitEntity->ClassMatches( "npc_hunter" ) )
  1268. {
  1269. DoExplosion();
  1270. return;
  1271. }
  1272. }
  1273. }
  1274. }
  1275. }
  1276.  
  1277. Vector vecFinalVelocity;
  1278. if ( IsInField() )
  1279. {
  1280. // Don't deflect when in a spawner field
  1281. vecFinalVelocity = pEvent->preVelocity[index];
  1282. }
  1283. else
  1284. {
  1285. // Don't slow down when hitting other entities.
  1286. vecFinalVelocity = pEvent->postVelocity[index];
  1287. VectorNormalize( vecFinalVelocity );
  1288. vecFinalVelocity *= GetSpeed();
  1289. }
  1290. PhysCallbackSetVelocity( pEvent->pObjects[index], vecFinalVelocity );
  1291. }
  1292.  
  1293.  
  1294. //-----------------------------------------------------------------------------
  1295. // Purpose:
  1296. //-----------------------------------------------------------------------------
  1297. void CPropCombineBall::DoImpactEffect( const Vector &preVelocity, int index, gamevcollisionevent_t *pEvent )
  1298. {
  1299. // Do that crazy impact effect!
  1300. trace_t tr;
  1301. CollisionEventToTrace( !index, pEvent, tr );
  1302.  
  1303. CBaseEntity *pTraceEntity = pEvent->pEntities[index];
  1304. UTIL_TraceLine( tr.startpos - preVelocity * 2.0f, tr.startpos + preVelocity * 2.0f, MASK_SOLID, pTraceEntity, COLLISION_GROUP_NONE, &tr );
  1305.  
  1306. if ( tr.fraction < 1.0f )
  1307. {
  1308. // See if we hit the sky
  1309. if ( tr.surface.flags & SURF_SKY )
  1310. {
  1311. DoExplosion();
  1312. return;
  1313. }
  1314.  
  1315. // Send the effect over
  1316. CEffectData data;
  1317.  
  1318. data.m_flRadius = 16;
  1319. data.m_vNormal = tr.plane.normal;
  1320. data.m_vOrigin = tr.endpos + tr.plane.normal * 1.0f;
  1321.  
  1322. DispatchEffect( "cball_bounce", data );
  1323. }
  1324.  
  1325. if ( hl2_episodic.GetBool() )
  1326. {
  1327. EmitSound( "NPC_CombineBall_Episodic.Impact" );
  1328. }
  1329. else
  1330. {
  1331. EmitSound( "NPC_CombineBall.Impact" );
  1332. }
  1333. }
  1334.  
  1335. //-----------------------------------------------------------------------------
  1336. // Tells whether this combine ball should consider deflecting towards this entity.
  1337. //-----------------------------------------------------------------------------
  1338. bool CPropCombineBall::IsAttractiveTarget( CBaseEntity *pEntity )
  1339. {
  1340. if ( !pEntity->IsAlive() )
  1341. return false;
  1342.  
  1343. if ( pEntity->GetFlags() & EF_NODRAW )
  1344. return false;
  1345.  
  1346. // Don't guide toward striders
  1347. if ( FClassnameIs( pEntity, "npc_strider" ) )
  1348. return false;
  1349.  
  1350. if( WasFiredByNPC() )
  1351. {
  1352. // Fired by an NPC
  1353. if( !pEntity->IsNPC() && !pEntity->IsPlayer() )
  1354. return false;
  1355.  
  1356. // Don't seek entities of the same class.
  1357. if ( pEntity->m_iClassname == GetOwnerEntity()->m_iClassname )
  1358. return false;
  1359. }
  1360. else
  1361. {
  1362.  
  1363. #ifndef HL2MP
  1364. if ( GetOwnerEntity() )
  1365. {
  1366. // Things we check if this ball has an owner that's not an NPC.
  1367. if( GetOwnerEntity()->IsPlayer() )
  1368. {
  1369. if( pEntity->Classify() == CLASS_PLAYER ||
  1370. pEntity->Classify() == CLASS_PLAYER_ALLY ||
  1371. pEntity->Classify() == CLASS_PLAYER_ALLY_VITAL )
  1372. {
  1373. // Not attracted to other players or allies.
  1374. return false;
  1375. }
  1376. }
  1377. }
  1378.  
  1379. // The default case.
  1380. if ( !pEntity->IsNPC() )
  1381. return false;
  1382.  
  1383. if( pEntity->Classify() == CLASS_BULLSEYE )
  1384. return false;
  1385.  
  1386. #else
  1387. if ( pEntity->IsPlayer() == false )
  1388. return false;
  1389.  
  1390. if ( pEntity == GetOwnerEntity() )
  1391. return false;
  1392.  
  1393. //No tracking teammates in teammode!
  1394. if ( g_pGameRules->IsTeamplay() )
  1395. {
  1396. if ( g_pGameRules->PlayerRelationship( GetOwnerEntity(), pEntity ) == GR_TEAMMATE )
  1397. return false;
  1398. }
  1399. #endif
  1400.  
  1401. // We must be able to hit them
  1402. trace_t tr;
  1403. UTIL_TraceLine( WorldSpaceCenter(), pEntity->BodyTarget( WorldSpaceCenter() ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  1404.  
  1405. if ( tr.fraction < 1.0f && tr.m_pEnt != pEntity )
  1406. return false;
  1407. }
  1408.  
  1409. return true;
  1410. }
  1411.  
  1412. //-----------------------------------------------------------------------------
  1413. // Deflects the ball toward enemies in case of a collision
  1414. //-----------------------------------------------------------------------------
  1415. void CPropCombineBall::DeflectTowardEnemy( float flSpeed, int index, gamevcollisionevent_t *pEvent )
  1416. {
  1417. // Bounce toward a particular enemy; choose one that's closest to my new velocity.
  1418. Vector vecVelDir = pEvent->postVelocity[index];
  1419. VectorNormalize( vecVelDir );
  1420.  
  1421. CBaseEntity *pBestTarget = NULL;
  1422.  
  1423. Vector vecStartPoint;
  1424. pEvent->pInternalData->GetContactPoint( vecStartPoint );
  1425.  
  1426. float flBestDist = MAX_COORD_FLOAT;
  1427.  
  1428. CBaseEntity *list[1024];
  1429.  
  1430. Vector vecDelta;
  1431. float distance, flDot;
  1432.  
  1433. // If we've already hit something, get accurate
  1434. bool bSeekKill = m_bStruckEntity && (WasWeaponLaunched() || sk_combineball_seek_kill.GetInt() );
  1435.  
  1436. if ( bSeekKill )
  1437. {
  1438. int nCount = UTIL_EntitiesInSphere( list, 1024, GetAbsOrigin(), sk_combine_ball_search_radius.GetFloat(), FL_NPC | FL_CLIENT );
  1439.  
  1440. for ( int i = 0; i < nCount; i++ )
  1441. {
  1442. if ( !IsAttractiveTarget( list[i] ) )
  1443. continue;
  1444.  
  1445. VectorSubtract( list[i]->WorldSpaceCenter(), vecStartPoint, vecDelta );
  1446. distance = VectorNormalize( vecDelta );
  1447.  
  1448. if ( distance < flBestDist )
  1449. {
  1450. // Check our direction
  1451. if ( DotProduct( vecDelta, vecVelDir ) > 0.0f )
  1452. {
  1453. pBestTarget = list[i];
  1454. flBestDist = distance;
  1455. }
  1456. }
  1457. }
  1458. }
  1459. else
  1460. {
  1461. float flMaxDot = 0.966f;
  1462. if ( !WasWeaponLaunched() )
  1463. {
  1464. float flMaxDot = sk_combineball_seek_angle.GetFloat();
  1465. float flGuideFactor = sk_combineball_guidefactor.GetFloat();
  1466. for ( int i = m_nBounceCount; --i >= 0; )
  1467. {
  1468. flMaxDot *= flGuideFactor;
  1469. }
  1470. flMaxDot = cos( flMaxDot * M_PI / 180.0f );
  1471.  
  1472. if ( flMaxDot > 1.0f )
  1473. {
  1474. flMaxDot = 1.0f;
  1475. }
  1476. }
  1477.  
  1478. // Otherwise only help out a little
  1479. Vector extents = Vector(256, 256, 256);
  1480. Ray_t ray;
  1481. ray.Init( vecStartPoint, vecStartPoint + 2048 * vecVelDir, -extents, extents );
  1482. int nCount = UTIL_EntitiesAlongRay( list, 1024, ray, FL_NPC | FL_CLIENT );
  1483. for ( int i = 0; i < nCount; i++ )
  1484. {
  1485. if ( !IsAttractiveTarget( list[i] ) )
  1486. continue;
  1487.  
  1488. VectorSubtract( list[i]->WorldSpaceCenter(), vecStartPoint, vecDelta );
  1489. distance = VectorNormalize( vecDelta );
  1490. flDot = DotProduct( vecDelta, vecVelDir );
  1491.  
  1492. if ( flDot > flMaxDot )
  1493. {
  1494. if ( distance < flBestDist )
  1495. {
  1496. pBestTarget = list[i];
  1497. flBestDist = distance;
  1498. }
  1499. }
  1500. }
  1501. }
  1502.  
  1503. if ( pBestTarget )
  1504. {
  1505. Vector vecDelta;
  1506. VectorSubtract( pBestTarget->WorldSpaceCenter(), vecStartPoint, vecDelta );
  1507. VectorNormalize( vecDelta );
  1508. vecDelta *= GetSpeed();
  1509. PhysCallbackSetVelocity( pEvent->pObjects[index], vecDelta );
  1510. }
  1511. }
  1512.  
  1513.  
  1514. //-----------------------------------------------------------------------------
  1515. // Bounce inside the spawner:
  1516. //-----------------------------------------------------------------------------
  1517. void CPropCombineBall::BounceInSpawner( float flSpeed, int index, gamevcollisionevent_t *pEvent )
  1518. {
  1519. GetSpawner()->RegisterReflection( this, m_bForward );
  1520.  
  1521. m_bForward = !m_bForward;
  1522.  
  1523. Vector vecTarget;
  1524. GetSpawner()->GetTargetEndpoint( m_bForward, &vecTarget );
  1525.  
  1526. Vector vecVelocity;
  1527. VectorSubtract( vecTarget, GetAbsOrigin(), vecVelocity );
  1528. VectorNormalize( vecVelocity );
  1529. vecVelocity *= flSpeed;
  1530.  
  1531. PhysCallbackSetVelocity( pEvent->pObjects[index], vecVelocity );
  1532. }
  1533.  
  1534.  
  1535. //-----------------------------------------------------------------------------
  1536. // Purpose:
  1537. //-----------------------------------------------------------------------------
  1538. bool CPropCombineBall::IsHittableEntity( CBaseEntity *pHitEntity )
  1539. {
  1540. if ( pHitEntity->IsWorld() )
  1541. return false;
  1542.  
  1543. if ( pHitEntity->GetMoveType() == MOVETYPE_PUSH )
  1544. {
  1545. if( pHitEntity->GetOwnerEntity() && FClassnameIs(pHitEntity->GetOwnerEntity(), "npc_strider") )
  1546. {
  1547. // The Strider's Bone Followers are MOVETYPE_PUSH, and we want the combine ball to hit these.
  1548. return true;
  1549. }
  1550.  
  1551. // If the entity we hit can take damage, we're good
  1552. if ( pHitEntity->m_takedamage == DAMAGE_YES )
  1553. return true;
  1554.  
  1555. return false;
  1556. }
  1557.  
  1558. return true;
  1559. }
  1560.  
  1561.  
  1562. //-----------------------------------------------------------------------------
  1563. // Purpose:
  1564. //-----------------------------------------------------------------------------
  1565. void CPropCombineBall::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  1566. {
  1567. Vector preVelocity = pEvent->preVelocity[index];
  1568. float flSpeed = VectorNormalize( preVelocity );
  1569.  
  1570. if ( m_nMaxBounces == -1 )
  1571. {
  1572. const surfacedata_t *pHit = physprops->GetSurfaceData( pEvent->surfaceProps[!index] );
  1573.  
  1574. if( pHit->game.material != CHAR_TEX_FLESH || !hl2_episodic.GetBool() )
  1575. {
  1576. CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  1577. if ( pHitEntity && IsHittableEntity( pHitEntity ) )
  1578. {
  1579. OnHitEntity( pHitEntity, flSpeed, index, pEvent );
  1580. }
  1581.  
  1582. // Remove self without affecting the object that was hit. (Unless it was flesh)
  1583. NotifySpawnerOfRemoval();
  1584. PhysCallbackRemove( this->NetworkProp() );
  1585.  
  1586. // disable dissolve damage so we don't kill off the player when he's the one we hit
  1587. PhysClearGameFlags( VPhysicsGetObject(), FVPHYSICS_DMG_DISSOLVE );
  1588. return;
  1589. }
  1590. }
  1591.  
  1592. // Prevents impact sounds, effects, etc. when it's in the field
  1593. if ( !IsInField() )
  1594. {
  1595. BaseClass::VPhysicsCollision( index, pEvent );
  1596. }
  1597.  
  1598. if ( m_nState == STATE_HOLDING )
  1599. return;
  1600.  
  1601. // If we've collided going faster than our desired, then up our desired
  1602. if ( flSpeed > GetSpeed() )
  1603. {
  1604. SetSpeed( flSpeed );
  1605. }
  1606.  
  1607. // Make sure we don't slow down
  1608. Vector vecFinalVelocity = pEvent->postVelocity[index];
  1609. VectorNormalize( vecFinalVelocity );
  1610. vecFinalVelocity *= GetSpeed();
  1611. PhysCallbackSetVelocity( pEvent->pObjects[index], vecFinalVelocity );
  1612.  
  1613. CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  1614. if ( pHitEntity && IsHittableEntity( pHitEntity ) )
  1615. {
  1616. OnHitEntity( pHitEntity, flSpeed, index, pEvent );
  1617. return;
  1618. }
  1619.  
  1620. if ( IsInField() )
  1621. {
  1622. if ( HasSpawnFlags( SF_COMBINE_BALL_BOUNCING_IN_SPAWNER ) && GetSpawner() )
  1623. {
  1624. BounceInSpawner( GetSpeed(), index, pEvent );
  1625. return;
  1626. }
  1627.  
  1628. PhysCallbackSetVelocity( pEvent->pObjects[index], vec3_origin );
  1629.  
  1630. // Delay the fade out so that we don't change our
  1631. // collision rules inside a vphysics callback.
  1632. variant_t emptyVariant;
  1633. g_EventQueue.AddEvent( this, "FadeAndRespawn", 0.01, NULL, NULL );
  1634. return;
  1635. }
  1636.  
  1637. if ( IsBeingCaptured() )
  1638. return;
  1639.  
  1640. // Do that crazy impact effect!
  1641. DoImpactEffect( preVelocity, index, pEvent );
  1642.  
  1643. // Only do the bounce so often
  1644. if ( gpGlobals->curtime - m_flLastBounceTime < 0.25f )
  1645. return;
  1646.  
  1647. // Save off our last bounce time
  1648. m_flLastBounceTime = gpGlobals->curtime;
  1649.  
  1650. // Reset the sound timer
  1651. SetContextThink( &CPropCombineBall::WhizSoundThink, gpGlobals->curtime + 0.01, s_pWhizThinkContext );
  1652.  
  1653. // Deflect towards nearby enemies
  1654. DeflectTowardEnemy( flSpeed, index, pEvent );
  1655.  
  1656. // Once more bounce
  1657. ++m_nBounceCount;
  1658.  
  1659. if ( OutOfBounces() && m_bBounceDie == false )
  1660. {
  1661. StartLifetime( 0.5 );
  1662. //Hack: Stop this from being called by doing this.
  1663. m_bBounceDie = true;
  1664. }
  1665. }
  1666.  
  1667.  
  1668. //-----------------------------------------------------------------------------
  1669. // Purpose:
  1670. //-----------------------------------------------------------------------------
  1671. void CPropCombineBall::AnimThink( void )
  1672. {
  1673. StudioFrameAdvance();
  1674. SetContextThink( &CPropCombineBall::AnimThink, gpGlobals->curtime + 0.1f, s_pAnimThinkContext );
  1675. }
  1676.  
  1677. //-----------------------------------------------------------------------------
  1678. //
  1679. // Implementation of CPropCombineBall
  1680. //
  1681. //-----------------------------------------------------------------------------
  1682. LINK_ENTITY_TO_CLASS( func_combine_ball_spawner, CFuncCombineBallSpawner );
  1683.  
  1684.  
  1685. //-----------------------------------------------------------------------------
  1686. // Save/load:
  1687. //-----------------------------------------------------------------------------
  1688. BEGIN_DATADESC( CFuncCombineBallSpawner )
  1689.  
  1690. DEFINE_KEYFIELD( m_nBallCount, FIELD_INTEGER, "ballcount" ),
  1691. DEFINE_KEYFIELD( m_flMinSpeed, FIELD_FLOAT, "minspeed" ),
  1692. DEFINE_KEYFIELD( m_flMaxSpeed, FIELD_FLOAT, "maxspeed" ),
  1693. DEFINE_KEYFIELD( m_flBallRadius, FIELD_FLOAT, "ballradius" ),
  1694. DEFINE_KEYFIELD( m_flBallRespawnTime, FIELD_FLOAT, "ballrespawntime" ),
  1695. DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
  1696. DEFINE_FIELD( m_nBallsRemainingInField, FIELD_INTEGER ),
  1697. DEFINE_FIELD( m_bEnabled, FIELD_BOOLEAN ),
  1698. DEFINE_UTLVECTOR( m_BallRespawnTime, FIELD_TIME ),
  1699. DEFINE_FIELD( m_flDisableTime, FIELD_TIME ),
  1700.  
  1701. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  1702. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  1703.  
  1704. DEFINE_OUTPUT( m_OnBallGrabbed, "OnBallGrabbed" ),
  1705. DEFINE_OUTPUT( m_OnBallReinserted, "OnBallReinserted" ),
  1706. DEFINE_OUTPUT( m_OnBallHitTopSide, "OnBallHitTopSide" ),
  1707. DEFINE_OUTPUT( m_OnBallHitBottomSide, "OnBallHitBottomSide" ),
  1708. DEFINE_OUTPUT( m_OnLastBallGrabbed, "OnLastBallGrabbed" ),
  1709. DEFINE_OUTPUT( m_OnFirstBallReinserted, "OnFirstBallReinserted" ),
  1710.  
  1711. DEFINE_THINKFUNC( BallThink ),
  1712. DEFINE_ENTITYFUNC( GrabBallTouch ),
  1713.  
  1714. END_DATADESC()
  1715.  
  1716. //-----------------------------------------------------------------------------
  1717. // Purpose: Constructor
  1718. //-----------------------------------------------------------------------------
  1719. CFuncCombineBallSpawner::CFuncCombineBallSpawner()
  1720. {
  1721. m_flBallRespawnTime = 0.0f;
  1722. m_flBallRadius = 20.0f;
  1723. m_flDisableTime = 0.0f;
  1724. m_bShooter = false;
  1725. }
  1726.  
  1727.  
  1728. //-----------------------------------------------------------------------------
  1729. // Spawn a ball
  1730. //-----------------------------------------------------------------------------
  1731. void CFuncCombineBallSpawner::SpawnBall()
  1732. {
  1733. CPropCombineBall *pBall = static_cast<CPropCombineBall*>( CreateEntityByName( "prop_combine_ball" ) );
  1734.  
  1735. float flRadius = m_flBallRadius;
  1736. pBall->SetRadius( flRadius );
  1737.  
  1738. Vector vecAbsOrigin;
  1739. ChoosePointInBox( &vecAbsOrigin );
  1740. Vector zaxis;
  1741. MatrixGetColumn( EntityToWorldTransform(), 2, zaxis );
  1742. VectorMA( vecAbsOrigin, flRadius, zaxis, vecAbsOrigin );
  1743.  
  1744. pBall->SetAbsOrigin( vecAbsOrigin );
  1745. pBall->SetSpawner( this );
  1746.  
  1747. float flSpeed = random->RandomFloat( m_flMinSpeed, m_flMaxSpeed );
  1748.  
  1749. zaxis *= flSpeed;
  1750. pBall->SetAbsVelocity( zaxis );
  1751. if ( HasSpawnFlags( SF_SPAWNER_POWER_SUPPLY ) )
  1752. {
  1753. pBall->AddSpawnFlags( SF_COMBINE_BALL_BOUNCING_IN_SPAWNER );
  1754. }
  1755.  
  1756. pBall->Spawn();
  1757. }
  1758.  
  1759. void CFuncCombineBallSpawner::Precache()
  1760. {
  1761. BaseClass::Precache();
  1762.  
  1763. UTIL_PrecacheOther( "prop_combine_ball" );
  1764. }
  1765.  
  1766. //-----------------------------------------------------------------------------
  1767. // Spawn
  1768. //-----------------------------------------------------------------------------
  1769. void CFuncCombineBallSpawner::Spawn()
  1770. {
  1771. BaseClass::Spawn();
  1772.  
  1773. Precache();
  1774.  
  1775. AddEffects( EF_NODRAW );
  1776. SetModel( STRING( GetModelName() ) );
  1777. SetSolid( SOLID_BSP );
  1778. AddSolidFlags( FSOLID_NOT_SOLID );
  1779. m_nBallsRemainingInField = m_nBallCount;
  1780.  
  1781. float flWidth = CollisionProp()->OBBSize().x;
  1782. float flHeight = CollisionProp()->OBBSize().y;
  1783. m_flRadius = MIN( flWidth, flHeight ) * 0.5f;
  1784. if ( m_flRadius <= 0.0f && m_bShooter == false )
  1785. {
  1786. Warning("Zero dimension func_combine_ball_spawner! Removing...\n");
  1787. UTIL_Remove( this );
  1788. return;
  1789. }
  1790.  
  1791. // Compute a respawn time
  1792. float flDeltaT = 1.0f;
  1793. if ( !( m_flMinSpeed == 0 && m_flMaxSpeed == 0 ) )
  1794. {
  1795. flDeltaT = (CollisionProp()->OBBSize().z - 2 * m_flBallRadius) / ((m_flMinSpeed + m_flMaxSpeed) * 0.5f);
  1796. flDeltaT /= m_nBallCount;
  1797. }
  1798.  
  1799. m_BallRespawnTime.EnsureCapacity( m_nBallCount );
  1800. for ( int i = 0; i < m_nBallCount; ++i )
  1801. {
  1802. RespawnBall( (float)i * flDeltaT );
  1803. }
  1804.  
  1805. m_bEnabled = true;
  1806. if ( HasSpawnFlags( SF_SPAWNER_START_DISABLED ) )
  1807. {
  1808. inputdata_t inputData;
  1809. InputDisable( inputData );
  1810. }
  1811. else
  1812. {
  1813. SetThink( &CFuncCombineBallSpawner::BallThink );
  1814. SetNextThink( gpGlobals->curtime + 0.1f );
  1815. }
  1816. }
  1817.  
  1818.  
  1819. //-----------------------------------------------------------------------------
  1820. // Enable/disable
  1821. //-----------------------------------------------------------------------------
  1822. void CFuncCombineBallSpawner::InputEnable( inputdata_t &inputdata )
  1823. {
  1824. if ( m_bEnabled )
  1825. return;
  1826.  
  1827. m_bEnabled = true;
  1828. m_flDisableTime = 0.0f;
  1829.  
  1830. for ( int i = m_BallRespawnTime.Count(); --i >= 0; )
  1831. {
  1832. m_BallRespawnTime[i] += gpGlobals->curtime;
  1833. }
  1834.  
  1835. SetThink( &CFuncCombineBallSpawner::BallThink );
  1836. SetNextThink( gpGlobals->curtime + 0.1f );
  1837. }
  1838.  
  1839. void CFuncCombineBallSpawner::InputDisable( inputdata_t &inputdata )
  1840. {
  1841. if ( !m_bEnabled )
  1842. return;
  1843.  
  1844. m_flDisableTime = gpGlobals->curtime;
  1845. m_bEnabled = false;
  1846.  
  1847. for ( int i = m_BallRespawnTime.Count(); --i >= 0; )
  1848. {
  1849. m_BallRespawnTime[i] -= gpGlobals->curtime;
  1850. }
  1851.  
  1852. SetThink( NULL );
  1853. }
  1854.  
  1855.  
  1856. //-----------------------------------------------------------------------------
  1857. // Choose a random point inside the cylinder
  1858. //-----------------------------------------------------------------------------
  1859. void CFuncCombineBallSpawner::ChoosePointInBox( Vector *pVecPoint )
  1860. {
  1861. float flXBoundary = ( CollisionProp()->OBBSize().x != 0 ) ? m_flBallRadius / CollisionProp()->OBBSize().x : 0.0f;
  1862. float flYBoundary = ( CollisionProp()->OBBSize().y != 0 ) ? m_flBallRadius / CollisionProp()->OBBSize().y : 0.0f;
  1863. if ( flXBoundary > 0.5f )
  1864. {
  1865. flXBoundary = 0.5f;
  1866. }
  1867. if ( flYBoundary > 0.5f )
  1868. {
  1869. flYBoundary = 0.5f;
  1870. }
  1871.  
  1872. CollisionProp()->RandomPointInBounds(
  1873. Vector( flXBoundary, flYBoundary, 0.0f ), Vector( 1.0f - flXBoundary, 1.0f - flYBoundary, 0.0f ), pVecPoint );
  1874. }
  1875.  
  1876.  
  1877. //-----------------------------------------------------------------------------
  1878. // Choose a random point inside the cylinder
  1879. //-----------------------------------------------------------------------------
  1880. void CFuncCombineBallSpawner::ChoosePointInCylinder( Vector *pVecPoint )
  1881. {
  1882. float flXRange = m_flRadius / CollisionProp()->OBBSize().x;
  1883. float flYRange = m_flRadius / CollisionProp()->OBBSize().y;
  1884.  
  1885. Vector vecEndPoint1, vecEndPoint2;
  1886. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecEndPoint1 );
  1887. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 1.0f ), &vecEndPoint2 );
  1888.  
  1889. // Choose a point inside the cylinder
  1890. float flDistSq;
  1891. do
  1892. {
  1893. CollisionProp()->RandomPointInBounds(
  1894. Vector( 0.5f - flXRange, 0.5f - flYRange, 0.0f ),
  1895. Vector( 0.5f + flXRange, 0.5f + flYRange, 0.0f ),
  1896. pVecPoint );
  1897.  
  1898. flDistSq = CalcDistanceSqrToLine( *pVecPoint, vecEndPoint1, vecEndPoint2 );
  1899.  
  1900. } while ( flDistSq > m_flRadius * m_flRadius );
  1901. }
  1902.  
  1903.  
  1904. //-----------------------------------------------------------------------------
  1905. // Register that a reflection occurred
  1906. //-----------------------------------------------------------------------------
  1907. void CFuncCombineBallSpawner::RegisterReflection( CPropCombineBall *pBall, bool bForward )
  1908. {
  1909. if ( bForward )
  1910. {
  1911. m_OnBallHitTopSide.FireOutput( pBall, this );
  1912. }
  1913. else
  1914. {
  1915. m_OnBallHitBottomSide.FireOutput( pBall, this );
  1916. }
  1917. }
  1918.  
  1919.  
  1920. //-----------------------------------------------------------------------------
  1921. // Choose a random point on the
  1922. //-----------------------------------------------------------------------------
  1923. void CFuncCombineBallSpawner::GetTargetEndpoint( bool bForward, Vector *pVecEndPoint )
  1924. {
  1925. float flZValue = bForward ? 1.0f : 0.0f;
  1926.  
  1927. CollisionProp()->RandomPointInBounds(
  1928. Vector( 0.0f, 0.0f, flZValue ), Vector( 1.0f, 1.0f, flZValue ), pVecEndPoint );
  1929. }
  1930.  
  1931.  
  1932. //-----------------------------------------------------------------------------
  1933. // Fire ball grabbed output
  1934. //-----------------------------------------------------------------------------
  1935. void CFuncCombineBallSpawner::BallGrabbed( CBaseEntity *pCombineBall )
  1936. {
  1937. m_OnBallGrabbed.FireOutput( pCombineBall, this );
  1938. --m_nBallsRemainingInField;
  1939. if ( m_nBallsRemainingInField == 0 )
  1940. {
  1941. m_OnLastBallGrabbed.FireOutput( pCombineBall, this );
  1942. }
  1943.  
  1944. // Wait for another ball to touch this to re-power it up.
  1945. if ( HasSpawnFlags( SF_SPAWNER_POWER_SUPPLY ) )
  1946. {
  1947. AddSolidFlags( FSOLID_TRIGGER );
  1948. SetTouch( &CFuncCombineBallSpawner::GrabBallTouch );
  1949. }
  1950.  
  1951. // Stop the ball thinking in case it was in the middle of being captured (which could re-add incorrectly)
  1952. if ( pCombineBall != NULL )
  1953. {
  1954. pCombineBall->SetContextThink( NULL, gpGlobals->curtime, s_pCaptureContext );
  1955. }
  1956. }
  1957.  
  1958.  
  1959. //-----------------------------------------------------------------------------
  1960. // Fire ball grabbed output
  1961. //-----------------------------------------------------------------------------
  1962. void CFuncCombineBallSpawner::GrabBallTouch( CBaseEntity *pOther )
  1963. {
  1964. // Safety net for two balls hitting this at once
  1965. if ( m_nBallsRemainingInField >= m_nBallCount )
  1966. return;
  1967.  
  1968. if ( pOther->GetCollisionGroup() != HL2COLLISION_GROUP_COMBINE_BALL )
  1969. return;
  1970.  
  1971. CPropCombineBall *pBall = dynamic_cast<CPropCombineBall*>( pOther );
  1972. Assert( pBall );
  1973.  
  1974. // Don't grab AR2 alt-fire
  1975. if ( pBall->WasWeaponLaunched() || !pBall->VPhysicsGetObject() )
  1976. return;
  1977.  
  1978. // Don't grab balls that are already in the field..
  1979. if ( pBall->IsInField() )
  1980. return;
  1981.  
  1982. // Don't grab fading out balls...
  1983. if ( !pBall->IsSolid() )
  1984. return;
  1985.  
  1986. // Don't capture balls that were very recently in the field (breaks punting)
  1987. if ( gpGlobals->curtime - pBall->LastCaptureTime() < 0.5f )
  1988. return;
  1989.  
  1990. // Now we're bouncing in this spawner
  1991. pBall->AddSpawnFlags( SF_COMBINE_BALL_BOUNCING_IN_SPAWNER );
  1992.  
  1993. // Tell the respawner we're no longer its ball
  1994. pBall->NotifySpawnerOfRemoval();
  1995.  
  1996. pBall->SetOwnerEntity( NULL );
  1997. pBall->SetSpawner( this );
  1998. pBall->CaptureBySpawner();
  1999.  
  2000. ++m_nBallsRemainingInField;
  2001.  
  2002. if ( m_nBallsRemainingInField >= m_nBallCount )
  2003. {
  2004. RemoveSolidFlags( FSOLID_TRIGGER );
  2005. SetTouch( NULL );
  2006. }
  2007.  
  2008. m_OnBallReinserted.FireOutput( pBall, this );
  2009. if ( m_nBallsRemainingInField == 1 )
  2010. {
  2011. m_OnFirstBallReinserted.FireOutput( pBall, this );
  2012. }
  2013. }
  2014.  
  2015.  
  2016. //-----------------------------------------------------------------------------
  2017. // Get a speed for the ball to insert
  2018. //-----------------------------------------------------------------------------
  2019. float CFuncCombineBallSpawner::GetBallSpeed( ) const
  2020. {
  2021. return random->RandomFloat( m_flMinSpeed, m_flMaxSpeed );
  2022. }
  2023.  
  2024.  
  2025. //-----------------------------------------------------------------------------
  2026. // Balls call this when they've been removed from the spawner
  2027. //-----------------------------------------------------------------------------
  2028. void CFuncCombineBallSpawner::RespawnBall( float flRespawnTime )
  2029. {
  2030. // Insert the time in sorted order,
  2031. // which by definition means to always insert at the start
  2032. m_BallRespawnTime.AddToTail( gpGlobals->curtime + flRespawnTime - m_flDisableTime );
  2033. }
  2034.  
  2035. //-----------------------------------------------------------------------------
  2036. //
  2037. //-----------------------------------------------------------------------------
  2038. void CFuncCombineBallSpawner::RespawnBallPostExplosion( void )
  2039. {
  2040. if ( m_flBallRespawnTime < 0 )
  2041. return;
  2042.  
  2043. if ( m_flBallRespawnTime == 0.0f )
  2044. {
  2045. m_BallRespawnTime.AddToTail( gpGlobals->curtime + 4.0f - m_flDisableTime );
  2046. }
  2047. else
  2048. {
  2049. m_BallRespawnTime.AddToTail( gpGlobals->curtime + m_flBallRespawnTime - m_flDisableTime );
  2050. }
  2051. }
  2052.  
  2053. //-----------------------------------------------------------------------------
  2054. // Ball think
  2055. //-----------------------------------------------------------------------------
  2056. void CFuncCombineBallSpawner::BallThink()
  2057. {
  2058. for ( int i = m_BallRespawnTime.Count(); --i >= 0; )
  2059. {
  2060. if ( m_BallRespawnTime[i] < gpGlobals->curtime )
  2061. {
  2062. SpawnBall();
  2063. m_BallRespawnTime.FastRemove( i );
  2064. }
  2065. }
  2066.  
  2067. // There are no more to respawn
  2068. SetNextThink( gpGlobals->curtime + 0.1f );
  2069. }
  2070.  
  2071. BEGIN_DATADESC( CPointCombineBallLauncher )
  2072. DEFINE_KEYFIELD( m_flConeDegrees, FIELD_FLOAT, "launchconenoise" ),
  2073. DEFINE_KEYFIELD( m_iszBullseyeName, FIELD_STRING, "bullseyename" ),
  2074. DEFINE_KEYFIELD( m_iBounces, FIELD_INTEGER, "maxballbounces" ),
  2075. DEFINE_INPUTFUNC( FIELD_VOID, "LaunchBall", InputLaunchBall ),
  2076. END_DATADESC()
  2077.  
  2078. #define SF_COMBINE_BALL_LAUNCHER_ATTACH_BULLSEYE 0x00000001
  2079. #define SF_COMBINE_BALL_LAUNCHER_COLLIDE_PLAYER 0x00000002
  2080.  
  2081. LINK_ENTITY_TO_CLASS( point_combine_ball_launcher, CPointCombineBallLauncher );
  2082.  
  2083. CPointCombineBallLauncher::CPointCombineBallLauncher()
  2084. {
  2085. m_bShooter = true;
  2086. m_flConeDegrees = 0.0f;
  2087. m_iBounces = 0;
  2088. }
  2089.  
  2090. void CPointCombineBallLauncher::Spawn( void )
  2091. {
  2092. m_bShooter = true;
  2093.  
  2094. BaseClass::Spawn();
  2095. }
  2096.  
  2097. void CPointCombineBallLauncher::InputLaunchBall ( inputdata_t &inputdata )
  2098. {
  2099. SpawnBall();
  2100. }
  2101.  
  2102. //-----------------------------------------------------------------------------
  2103. // Spawn a ball
  2104. //-----------------------------------------------------------------------------
  2105. void CPointCombineBallLauncher::SpawnBall()
  2106. {
  2107. CPropCombineBall *pBall = static_cast<CPropCombineBall*>( CreateEntityByName( "prop_combine_ball" ) );
  2108.  
  2109. if ( pBall == NULL )
  2110. return;
  2111.  
  2112. float flRadius = m_flBallRadius;
  2113. pBall->SetRadius( flRadius );
  2114.  
  2115. Vector vecAbsOrigin = GetAbsOrigin();
  2116. Vector zaxis;
  2117.  
  2118. pBall->SetAbsOrigin( vecAbsOrigin );
  2119. pBall->SetSpawner( this );
  2120.  
  2121. float flSpeed = random->RandomFloat( m_flMinSpeed, m_flMaxSpeed );
  2122.  
  2123. Vector vDirection;
  2124. QAngle qAngle = GetAbsAngles();
  2125.  
  2126. qAngle = qAngle + QAngle ( random->RandomFloat( -m_flConeDegrees, m_flConeDegrees ), random->RandomFloat( -m_flConeDegrees, m_flConeDegrees ), 0 );
  2127.  
  2128. AngleVectors( qAngle, &vDirection, NULL, NULL );
  2129.  
  2130. vDirection *= flSpeed;
  2131. pBall->SetAbsVelocity( vDirection );
  2132.  
  2133. DispatchSpawn(pBall);
  2134. pBall->Activate();
  2135. pBall->SetState( CPropCombineBall::STATE_LAUNCHED );
  2136. pBall->SetMaxBounces( m_iBounces );
  2137.  
  2138. if ( HasSpawnFlags( SF_COMBINE_BALL_LAUNCHER_COLLIDE_PLAYER ) )
  2139. {
  2140. pBall->SetCollisionGroup( HL2COLLISION_GROUP_COMBINE_BALL_NPC );
  2141. }
  2142.  
  2143. if( GetSpawnFlags() & SF_COMBINE_BALL_LAUNCHER_ATTACH_BULLSEYE )
  2144. {
  2145. CNPC_Bullseye *pBullseye = static_cast<CNPC_Bullseye*>( CreateEntityByName( "npc_bullseye" ) );
  2146.  
  2147. if( pBullseye )
  2148. {
  2149. pBullseye->SetAbsOrigin( pBall->GetAbsOrigin() );
  2150. pBullseye->SetAbsAngles( QAngle( 0, 0, 0 ) );
  2151. pBullseye->KeyValue( "solid", "6" );
  2152. pBullseye->KeyValue( "targetname", STRING(m_iszBullseyeName) );
  2153. pBullseye->Spawn();
  2154.  
  2155. DispatchSpawn(pBullseye);
  2156. pBullseye->Activate();
  2157.  
  2158. pBullseye->SetParent(pBall);
  2159. pBullseye->SetHealth(10);
  2160. }
  2161. }
  2162. }
  2163.  
  2164. // ###################################################################
  2165. // > FilterClass
  2166. // ###################################################################
  2167. class CFilterCombineBall : public CBaseFilter
  2168. {
  2169. DECLARE_CLASS( CFilterCombineBall, CBaseFilter );
  2170. DECLARE_DATADESC();
  2171.  
  2172. public:
  2173. int m_iBallType;
  2174.  
  2175. bool PassesFilterImpl( CBaseEntity *pCaller, CBaseEntity *pEntity )
  2176. {
  2177. CPropCombineBall *pBall = dynamic_cast<CPropCombineBall*>(pEntity );
  2178.  
  2179. if ( pBall )
  2180. {
  2181. //Playtest HACK: If we have an NPC owner then we were shot from an AR2.
  2182. if ( pBall->GetOwnerEntity() && pBall->GetOwnerEntity()->IsNPC() )
  2183. return false;
  2184.  
  2185. return pBall->GetState() == m_iBallType;
  2186. }
  2187.  
  2188. return false;
  2189. }
  2190. };
  2191.  
  2192. LINK_ENTITY_TO_CLASS( filter_combineball_type, CFilterCombineBall );
  2193.  
  2194. BEGIN_DATADESC( CFilterCombineBall )
  2195. // Keyfields
  2196. DEFINE_KEYFIELD( m_iBallType, FIELD_INTEGER, "balltype" ),
  2197. END_DATADESC()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement