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.

979 lines
28 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Antlion Grub - cannon fodder
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "gib.h"
  9. #include "Sprite.h"
  10. #include "te_effect_dispatch.h"
  11. #include "npc_antliongrub.h"
  12. #include "ai_utils.h"
  13. #include "particle_parse.h"
  14. #include "items.h"
  15. #include "item_dynamic_resupply.h"
  16. #include "npc_vortigaunt_episodic.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. ConVar sk_grubnugget_health_small( "sk_grubnugget_health_small", "1" );
  20. ConVar sk_grubnugget_health_medium( "sk_grubnugget_health_medium", "4" );
  21. ConVar sk_grubnugget_health_large( "sk_grubnugget_health_large", "6" );
  22. ConVar sk_grubnugget_enabled( "sk_grubnugget_enabled", "1" );
  23. #define ANTLIONGRUB_MODEL "models/antlion_grub.mdl"
  24. #define ANTLIONGRUB_SQUASHED_MODEL "models/antlion_grub_squashed.mdl"
  25. #define SF_ANTLIONGRUB_NO_AUTO_PLACEMENT (1<<0)
  26. enum GrubState_e
  27. {
  28. GRUB_STATE_IDLE,
  29. GRUB_STATE_AGITATED,
  30. };
  31. enum
  32. {
  33. NUGGET_NONE,
  34. NUGGET_SMALL = 1,
  35. NUGGET_MEDIUM,
  36. NUGGET_LARGE
  37. };
  38. //
  39. // Grub nugget
  40. //
  41. class CGrubNugget : public CItem
  42. {
  43. public:
  44. DECLARE_CLASS( CGrubNugget, CItem );
  45. virtual void Spawn( void );
  46. virtual void Precache( void );
  47. virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
  48. virtual void Event_Killed( const CTakeDamageInfo &info );
  49. virtual bool VPhysicsIsFlesh( void );
  50. bool MyTouch( CBasePlayer *pPlayer );
  51. void SetDenomination( int nSize ) { Assert( nSize <= NUGGET_LARGE && nSize >= NUGGET_SMALL ); m_nDenomination = nSize; }
  52. DECLARE_DATADESC();
  53. private:
  54. int m_nDenomination; // Denotes size and health amount given
  55. };
  56. BEGIN_DATADESC( CGrubNugget )
  57. DEFINE_FIELD( m_nDenomination, FIELD_INTEGER ),
  58. END_DATADESC()
  59. LINK_ENTITY_TO_CLASS( item_grubnugget, CGrubNugget );
  60. //
  61. // Simple grub
  62. //
  63. class CAntlionGrub : public CBaseAnimating
  64. {
  65. public:
  66. DECLARE_CLASS( CAntlionGrub, CBaseAnimating );
  67. virtual void Activate( void );
  68. virtual void Spawn( void );
  69. virtual void Precache( void );
  70. virtual void UpdateOnRemove( void );
  71. virtual void Event_Killed( const CTakeDamageInfo &info );
  72. virtual int OnTakeDamage( const CTakeDamageInfo &info );
  73. virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
  74. void InputSquash( inputdata_t &data );
  75. void IdleThink( void );
  76. void FlinchThink( void );
  77. void GrubTouch( CBaseEntity *pOther );
  78. DECLARE_DATADESC();
  79. protected:
  80. inline bool InPVS( void );
  81. void SetNextThinkByDistance( void );
  82. int GetNuggetDenomination( void );
  83. void CreateNugget( void );
  84. void MakeIdleSounds( void );
  85. void MakeSquashDecals( const Vector &vecOrigin );
  86. void AttachToSurface( void );
  87. void CreateGlow( void );
  88. void FadeGlow( void );
  89. void Squash( CBaseEntity *pOther, bool bDealDamage, bool bSpawnBlood );
  90. void SpawnSquashedGrub( void );
  91. void InputAgitate( inputdata_t &inputdata );
  92. inline bool ProbeSurface( const Vector &vecTestPos, const Vector &vecDir, Vector *vecResult, Vector *vecNormal );
  93. CHandle<CSprite> m_hGlowSprite;
  94. int m_nGlowSpriteHandle;
  95. float m_flFlinchTime;
  96. float m_flNextIdleSoundTime;
  97. float m_flNextSquealSoundTime;
  98. bool m_bOutsidePVS;
  99. GrubState_e m_State;
  100. COutputEvent m_OnAgitated;
  101. COutputEvent m_OnDeath;
  102. COutputEvent m_OnDeathByPlayer;
  103. };
  104. BEGIN_DATADESC( CAntlionGrub )
  105. DEFINE_FIELD( m_hGlowSprite, FIELD_EHANDLE ),
  106. DEFINE_FIELD( m_flFlinchTime, FIELD_TIME ),
  107. DEFINE_FIELD( m_flNextIdleSoundTime, FIELD_TIME ),
  108. DEFINE_FIELD( m_flNextSquealSoundTime, FIELD_TIME ),
  109. DEFINE_FIELD( m_State, FIELD_INTEGER ),
  110. DEFINE_INPUTFUNC( FIELD_FLOAT, "Agitate", InputAgitate ),
  111. DEFINE_OUTPUT( m_OnAgitated, "OnAgitated" ),
  112. DEFINE_OUTPUT( m_OnDeath, "OnDeath" ),
  113. DEFINE_OUTPUT( m_OnDeathByPlayer, "OnDeathByPlayer" ),
  114. // Functions
  115. DEFINE_ENTITYFUNC( GrubTouch ),
  116. DEFINE_ENTITYFUNC( IdleThink ),
  117. DEFINE_ENTITYFUNC( FlinchThink ),
  118. DEFINE_INPUTFUNC( FIELD_VOID, "Squash", InputSquash ),
  119. END_DATADESC()
  120. LINK_ENTITY_TO_CLASS( npc_antlion_grub, CAntlionGrub );
  121. //-----------------------------------------------------------------------------
  122. // Purpose:
  123. //-----------------------------------------------------------------------------
  124. void CAntlionGrub::CreateGlow( void )
  125. {
  126. // Create the glow sprite
  127. m_hGlowSprite = CSprite::SpriteCreate( "sprites/grubflare1.vmt", GetLocalOrigin(), false );
  128. Assert( m_hGlowSprite );
  129. if ( m_hGlowSprite == NULL )
  130. return;
  131. m_hGlowSprite->TurnOn();
  132. m_hGlowSprite->SetTransparency( kRenderWorldGlow, 156, 169, 121, 164, kRenderFxNoDissipation );
  133. m_hGlowSprite->SetScale( 0.5f );
  134. m_hGlowSprite->SetGlowProxySize( 16.0f );
  135. int nAttachment = LookupAttachment( "glow" );
  136. m_hGlowSprite->SetParent( this, nAttachment );
  137. m_hGlowSprite->SetLocalOrigin( vec3_origin );
  138. // Don't uselessly animate, we're a static sprite!
  139. m_hGlowSprite->SetThink( NULL );
  140. m_hGlowSprite->SetNextThink( TICK_NEVER_THINK );
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose:
  144. //-----------------------------------------------------------------------------
  145. void CAntlionGrub::FadeGlow( void )
  146. {
  147. if ( m_hGlowSprite )
  148. {
  149. m_hGlowSprite->FadeAndDie( 0.25f );
  150. }
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void CAntlionGrub::UpdateOnRemove( void )
  156. {
  157. FadeGlow();
  158. BaseClass::UpdateOnRemove();
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose: Find what size of nugget to spawn
  162. //-----------------------------------------------------------------------------
  163. int CAntlionGrub::GetNuggetDenomination( void )
  164. {
  165. // Find the desired health perc we want to be at
  166. float flDesiredHealthPerc = DynamicResupply_GetDesiredHealthPercentage();
  167. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  168. if ( pPlayer == NULL )
  169. return -1;
  170. // Get the player's current health percentage
  171. float flPlayerHealthPerc = (float) pPlayer->GetHealth() / (float) pPlayer->GetMaxHealth();
  172. // If we're already maxed out, return the small nugget
  173. if ( flPlayerHealthPerc >= flDesiredHealthPerc )
  174. {
  175. return NUGGET_SMALL;
  176. }
  177. // Find where we fall in the desired health's range
  178. float flPercDelta = flPlayerHealthPerc / flDesiredHealthPerc;
  179. // The larger to discrepancy, the higher the chance to move quickly to close it
  180. float flSeed = random->RandomFloat( 0.0f, 1.0f );
  181. float flRandomPerc = Bias( flSeed, (1.0f-flPercDelta) );
  182. int nDenomination;
  183. if ( flRandomPerc < 0.25f )
  184. {
  185. nDenomination = NUGGET_SMALL;
  186. }
  187. else if ( flRandomPerc < 0.625f )
  188. {
  189. nDenomination = NUGGET_MEDIUM;
  190. }
  191. else
  192. {
  193. nDenomination = NUGGET_LARGE;
  194. }
  195. // Msg("Player: %.02f, Desired: %.02f, Seed: %.02f, Perc: %.02f, Result: %d\n", flPlayerHealthPerc, flDesiredHealthPerc, flSeed, flRandomPerc, nDenomination );
  196. return nDenomination;
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Purpose:
  200. //-----------------------------------------------------------------------------
  201. void CAntlionGrub::CreateNugget( void )
  202. {
  203. CGrubNugget *pNugget = (CGrubNugget *) CreateEntityByName( "item_grubnugget" );
  204. if ( pNugget == NULL )
  205. return;
  206. Vector vecOrigin;
  207. Vector vecForward;
  208. GetAttachment( LookupAttachment( "glow" ), vecOrigin, &vecForward );
  209. // Find out what size to make this nugget!
  210. int nDenomination = GetNuggetDenomination();
  211. pNugget->SetDenomination( nDenomination );
  212. pNugget->SetAbsOrigin( vecOrigin );
  213. pNugget->SetAbsAngles( RandomAngle( 0, 360 ) );
  214. DispatchSpawn( pNugget );
  215. IPhysicsObject *pPhys = pNugget->VPhysicsGetObject();
  216. if ( pPhys )
  217. {
  218. Vector vecForward;
  219. GetVectors( &vecForward, NULL, NULL );
  220. Vector vecVelocity = RandomVector( -35.0f, 35.0f ) + ( vecForward * -RandomFloat( 50.0f, 75.0f ) );
  221. AngularImpulse vecAngImpulse = RandomAngularImpulse( -100.0f, 100.0f );
  222. pPhys->AddVelocity( &vecVelocity, &vecAngImpulse );
  223. }
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. // Input : &info -
  228. //-----------------------------------------------------------------------------
  229. void CAntlionGrub::Event_Killed( const CTakeDamageInfo &info )
  230. {
  231. // Fire our output only if the player is the one that killed us
  232. if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
  233. {
  234. m_OnDeathByPlayer.FireOutput( info.GetAttacker(), info.GetAttacker() );
  235. }
  236. m_OnDeath.FireOutput( info.GetAttacker(), info.GetAttacker() );
  237. SendOnKilledGameEvent( info );
  238. // Crush and crowbar damage hurt us more than others
  239. bool bSquashed = ( info.GetDamageType() & (DMG_CRUSH|DMG_CLUB)) ? true : false;
  240. Squash( info.GetAttacker(), false, bSquashed );
  241. m_takedamage = DAMAGE_NO;
  242. if ( sk_grubnugget_enabled.GetBool() )
  243. {
  244. CreateNugget();
  245. }
  246. // Go away
  247. SetThink( &CBaseEntity::SUB_Remove );
  248. SetNextThink( gpGlobals->curtime + 0.1f );
  249. // we deliberately do not call BaseClass::EventKilled
  250. }
  251. //-----------------------------------------------------------------------------
  252. // Purpose:
  253. // Input : &info -
  254. //-----------------------------------------------------------------------------
  255. int CAntlionGrub::OnTakeDamage( const CTakeDamageInfo &info )
  256. {
  257. // Animate a flinch of pain if we're dying
  258. bool bSquashed = ( ( GetEffects() & EF_NODRAW ) != 0 );
  259. if ( bSquashed == false )
  260. {
  261. SetSequence( SelectWeightedSequence( ACT_SMALL_FLINCH ) );
  262. m_flFlinchTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 1.0f );
  263. SetThink( &CAntlionGrub::FlinchThink );
  264. SetNextThink( gpGlobals->curtime + 0.05f );
  265. }
  266. return BaseClass::OnTakeDamage( info );
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: Whether or not we're in the PVS
  270. //-----------------------------------------------------------------------------
  271. inline bool CAntlionGrub::InPVS( void )
  272. {
  273. return ( UTIL_FindClientInPVS( edict() ) != NULL ) || (UTIL_ClientPVSIsExpanded() && UTIL_FindClientInVisibilityPVS( edict() ));
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose:
  277. //-----------------------------------------------------------------------------
  278. void CAntlionGrub::SetNextThinkByDistance( void )
  279. {
  280. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  281. if ( pPlayer == NULL )
  282. {
  283. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.5f, 3.0f ) );
  284. return;
  285. }
  286. float flDistToPlayerSqr = ( GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr();
  287. float scale = RemapValClamped( flDistToPlayerSqr, Square( 400 ), Square( 5000 ), 1.0f, 5.0f );
  288. float time = random->RandomFloat( 1.0f, 3.0f );
  289. SetNextThink( gpGlobals->curtime + ( time * scale ) );
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose:
  293. //-----------------------------------------------------------------------------
  294. void CAntlionGrub::Spawn( void )
  295. {
  296. Precache();
  297. BaseClass::Spawn();
  298. SetModel( ANTLIONGRUB_MODEL );
  299. // FIXME: This is a big perf hit with the number of grubs we're using! - jdw
  300. CreateGlow();
  301. SetSolid( SOLID_BBOX );
  302. SetSolidFlags( FSOLID_TRIGGER );
  303. SetMoveType( MOVETYPE_NONE );
  304. SetCollisionGroup( COLLISION_GROUP_NONE );
  305. AddEffects( EF_NOSHADOW );
  306. CollisionProp()->UseTriggerBounds(true,1);
  307. SetTouch( &CAntlionGrub::GrubTouch );
  308. SetHealth( 1 );
  309. m_takedamage = DAMAGE_YES;
  310. // Stick to the nearest surface
  311. if ( HasSpawnFlags( SF_ANTLIONGRUB_NO_AUTO_PLACEMENT ) == false )
  312. {
  313. AttachToSurface();
  314. }
  315. // At this point, alter our bounds to make sure we're within them
  316. Vector vecMins, vecMaxs;
  317. RotateAABB( EntityToWorldTransform(), CollisionProp()->OBBMins(), CollisionProp()->OBBMaxs(), vecMins, vecMaxs );
  318. UTIL_SetSize( this, vecMins, vecMaxs );
  319. // Start our idle activity
  320. SetSequence( SelectWeightedSequence( ACT_IDLE ) );
  321. SetCycle( random->RandomFloat( 0.0f, 1.0f ) );
  322. ResetSequenceInfo();
  323. m_State = GRUB_STATE_IDLE;
  324. // Reset
  325. m_flFlinchTime = 0.0f;
  326. m_flNextIdleSoundTime = gpGlobals->curtime + random->RandomFloat( 4.0f, 8.0f );
  327. }
  328. //-----------------------------------------------------------------------------
  329. // Purpose:
  330. //-----------------------------------------------------------------------------
  331. void CAntlionGrub::Activate( void )
  332. {
  333. BaseClass::Activate();
  334. // Idly think
  335. SetThink( &CAntlionGrub::IdleThink );
  336. SetNextThinkByDistance();
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose:
  340. // Input : &vecTestPos -
  341. // *vecResult -
  342. // *flDist -
  343. // Output : inline bool
  344. //-----------------------------------------------------------------------------
  345. inline bool CAntlionGrub::ProbeSurface( const Vector &vecTestPos, const Vector &vecDir, Vector *vecResult, Vector *vecNormal )
  346. {
  347. // Trace down to find a surface
  348. trace_t tr;
  349. UTIL_TraceLine( vecTestPos, vecTestPos + (vecDir*256.0f), MASK_NPCSOLID&(~CONTENTS_MONSTER), this, COLLISION_GROUP_NONE, &tr );
  350. if ( vecResult )
  351. {
  352. *vecResult = tr.endpos;
  353. }
  354. if ( vecNormal )
  355. {
  356. *vecNormal = tr.plane.normal;
  357. }
  358. return ( tr.fraction < 1.0f );
  359. }
  360. //-----------------------------------------------------------------------------
  361. // Purpose: Attaches the grub to the surface underneath its abdomen
  362. //-----------------------------------------------------------------------------
  363. void CAntlionGrub::AttachToSurface( void )
  364. {
  365. // Get our downward direction
  366. Vector vecForward, vecRight, vecDown;
  367. GetVectors( &vecForward, &vecRight, &vecDown );
  368. vecDown.Negate();
  369. Vector vecOffset = ( vecDown * -8.0f );
  370. // Middle
  371. Vector vecMid, vecMidNormal;
  372. if ( ProbeSurface( WorldSpaceCenter() + vecOffset, vecDown, &vecMid, &vecMidNormal ) == false )
  373. {
  374. // A grub was left hanging in the air, it must not be near any valid surfaces!
  375. Warning("Antlion grub stranded in space at (%.02f, %.02f, %.02f) : REMOVED\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
  376. UTIL_Remove( this );
  377. return;
  378. }
  379. // Sit at the mid-point
  380. UTIL_SetOrigin( this, vecMid );
  381. Vector vecPivot;
  382. Vector vecPivotNormal;
  383. bool bNegate = true;
  384. // First test our tail (more crucial that it doesn't interpenetrate with the world)
  385. if ( ProbeSurface( WorldSpaceCenter() - ( vecForward * 12.0f ) + vecOffset, vecDown, &vecPivot, &vecPivotNormal ) == false )
  386. {
  387. // If that didn't find a surface, try the head
  388. if ( ProbeSurface( WorldSpaceCenter() + ( vecForward * 12.0f ) + vecOffset, vecDown, &vecPivot, &vecPivotNormal ) == false )
  389. {
  390. // Worst case, just site at the middle
  391. UTIL_SetOrigin( this, vecMid );
  392. QAngle vecAngles;
  393. VectorAngles( vecForward, vecMidNormal, vecAngles );
  394. SetAbsAngles( vecAngles );
  395. return;
  396. }
  397. bNegate = false;
  398. }
  399. // Find the line we'll lay on if these two points are connected by a line
  400. Vector vecLieDir = ( vecPivot - vecMid );
  401. VectorNormalize( vecLieDir );
  402. if ( bNegate )
  403. {
  404. // We need to try and maintain our facing
  405. vecLieDir.Negate();
  406. }
  407. // Use the average of the surface normals to be our "up" direction
  408. Vector vecPseudoUp = ( vecMidNormal + vecPivotNormal ) * 0.5f;
  409. QAngle vecAngles;
  410. VectorAngles( vecLieDir, vecPseudoUp, vecAngles );
  411. SetAbsAngles( vecAngles );
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Purpose:
  415. //-----------------------------------------------------------------------------
  416. void CAntlionGrub::MakeIdleSounds( void )
  417. {
  418. if ( m_State == GRUB_STATE_AGITATED )
  419. {
  420. if ( m_flNextSquealSoundTime < gpGlobals->curtime )
  421. {
  422. EmitSound( "NPC_Antlion_Grub.Stimulated" );
  423. m_flNextSquealSoundTime = gpGlobals->curtime + random->RandomFloat( 1.5f, 3.0f );
  424. m_flNextIdleSoundTime = gpGlobals->curtime + random->RandomFloat( 4.0f, 8.0f );
  425. }
  426. }
  427. else
  428. {
  429. if ( m_flNextIdleSoundTime < gpGlobals->curtime )
  430. {
  431. EmitSound( "NPC_Antlion_Grub.Idle" );
  432. m_flNextIdleSoundTime = gpGlobals->curtime + random->RandomFloat( 8.0f, 12.0f );
  433. }
  434. }
  435. }
  436. #define DEBUG_GRUB_THINK_TIMES 0
  437. #if DEBUG_GRUB_THINK_TIMES
  438. int nFrame = 0;
  439. int nNumThinks = 0;
  440. #endif // DEBUG_GRUB_THINK_TIMES
  441. //-----------------------------------------------------------------------------
  442. // Purpose: Advance our thinks
  443. //-----------------------------------------------------------------------------
  444. void CAntlionGrub::IdleThink( void )
  445. {
  446. #if DEBUG_GRUB_THINK_TIMES
  447. // Test for a new frame
  448. if ( gpGlobals->framecount != nFrame )
  449. {
  450. if ( nNumThinks > 10 )
  451. {
  452. Msg("%d npc_antlion_grubs thinking per frame!\n", nNumThinks );
  453. }
  454. nFrame = gpGlobals->framecount;
  455. nNumThinks = 0;
  456. }
  457. nNumThinks++;
  458. #endif // DEBUG_GRUB_THINK_TIMES
  459. // Check the PVS status
  460. if ( InPVS() == false )
  461. {
  462. // Push out into the future until they're in our PVS
  463. SetNextThinkByDistance();
  464. m_bOutsidePVS = true;
  465. return;
  466. }
  467. // Stagger our sounds if we've just re-entered the PVS
  468. if ( m_bOutsidePVS )
  469. {
  470. m_flNextIdleSoundTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 4.0f );
  471. m_bOutsidePVS = false;
  472. }
  473. // See how close the player is
  474. CBasePlayer *pPlayerEnt = AI_GetSinglePlayer();
  475. float flDistToPlayerSqr = ( GetAbsOrigin() - pPlayerEnt->GetAbsOrigin() ).LengthSqr();
  476. bool bFlinching = ( m_flFlinchTime > gpGlobals->curtime );
  477. // If they're far enough away, just wait to think again
  478. if ( flDistToPlayerSqr > Square( 40*12 ) && bFlinching == false )
  479. {
  480. SetNextThinkByDistance();
  481. return;
  482. }
  483. // At this range, the player agitates us with his presence
  484. bool bPlayerWithinAgitationRange = ( flDistToPlayerSqr <= Square( (6*12) ) );
  485. bool bAgitated = (bPlayerWithinAgitationRange || bFlinching );
  486. // If we're idle and the player has come close enough, get agry
  487. if ( ( m_State == GRUB_STATE_IDLE ) && bAgitated )
  488. {
  489. SetSequence( SelectWeightedSequence( ACT_SMALL_FLINCH ) );
  490. m_State = GRUB_STATE_AGITATED;
  491. }
  492. else if ( IsSequenceFinished() )
  493. {
  494. // See if it's time to choose a new sequence
  495. ResetSequenceInfo();
  496. SetCycle( 0.0f );
  497. // If we're near enough, we want to play an "alert" animation
  498. if ( bAgitated )
  499. {
  500. SetSequence( SelectWeightedSequence( ACT_SMALL_FLINCH ) );
  501. m_State = GRUB_STATE_AGITATED;
  502. }
  503. else
  504. {
  505. // Just idle
  506. SetSequence( SelectWeightedSequence( ACT_IDLE ) );
  507. m_State = GRUB_STATE_IDLE;
  508. }
  509. // Add some variation because we're often in large bunches
  510. SetPlaybackRate( random->RandomFloat( 0.8f, 1.2f ) );
  511. }
  512. // Idle normally
  513. StudioFrameAdvance();
  514. MakeIdleSounds();
  515. SetNextThink( gpGlobals->curtime + 0.1f );
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Purpose:
  519. //-----------------------------------------------------------------------------
  520. void CAntlionGrub::FlinchThink( void )
  521. {
  522. StudioFrameAdvance();
  523. SetNextThink( gpGlobals->curtime + 0.1f );
  524. // See if we're done
  525. if ( m_flFlinchTime < gpGlobals->curtime )
  526. {
  527. SetSequence( SelectWeightedSequence( ACT_IDLE ) );
  528. SetThink( &CAntlionGrub::IdleThink );
  529. }
  530. }
  531. //-----------------------------------------------------------------------------
  532. // Purpose:
  533. //-----------------------------------------------------------------------------
  534. void CAntlionGrub::GrubTouch( CBaseEntity *pOther )
  535. {
  536. // We can be squished by the player, Vort, or flying heavy things.
  537. IPhysicsObject *pPhysOther = pOther->VPhysicsGetObject(); // bool bThrown = ( pTarget->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_WAS_THROWN ) != 0;
  538. if ( pOther->IsPlayer() || FClassnameIs(pOther,"npc_vortigaunt") || ( pPhysOther && (pPhysOther->GetGameFlags() & FVPHYSICS_WAS_THROWN )) )
  539. {
  540. m_OnAgitated.FireOutput( pOther, pOther );
  541. Squash( pOther, true, true );
  542. }
  543. }
  544. //-----------------------------------------------------------------------------
  545. // Purpose:
  546. //-----------------------------------------------------------------------------
  547. void CAntlionGrub::Precache( void )
  548. {
  549. PrecacheModel( ANTLIONGRUB_MODEL );
  550. PrecacheModel( ANTLIONGRUB_SQUASHED_MODEL );
  551. m_nGlowSpriteHandle = PrecacheModel("sprites/grubflare1.vmt");
  552. PrecacheScriptSound( "NPC_Antlion_Grub.Idle" );
  553. PrecacheScriptSound( "NPC_Antlion_Grub.Alert" );
  554. PrecacheScriptSound( "NPC_Antlion_Grub.Stimulated" );
  555. PrecacheScriptSound( "NPC_Antlion_Grub.Die" );
  556. PrecacheScriptSound( "NPC_Antlion_Grub.Squish" );
  557. PrecacheParticleSystem( "GrubSquashBlood" );
  558. PrecacheParticleSystem( "GrubBlood" );
  559. UTIL_PrecacheOther( "item_grubnugget" );
  560. BaseClass::Precache();
  561. }
  562. //-----------------------------------------------------------------------------
  563. // Purpose: Squish the grub!
  564. //-----------------------------------------------------------------------------
  565. void CAntlionGrub::InputSquash( inputdata_t &data )
  566. {
  567. Squash( data.pActivator, true, true );
  568. }
  569. //-----------------------------------------------------------------------------
  570. // Purpose:
  571. //-----------------------------------------------------------------------------
  572. void CAntlionGrub::SpawnSquashedGrub( void )
  573. {
  574. // If we're already invisible, we're done
  575. if ( GetEffects() & EF_NODRAW )
  576. return;
  577. Vector vecUp;
  578. GetVectors( NULL, NULL, &vecUp );
  579. CBaseEntity *pGib = CreateRagGib( ANTLIONGRUB_SQUASHED_MODEL, GetAbsOrigin(), GetAbsAngles(), vecUp * 16.0f );
  580. if ( pGib )
  581. {
  582. pGib->AddEffects( EF_NOSHADOW );
  583. }
  584. }
  585. //-----------------------------------------------------------------------------
  586. // Purpose:
  587. //-----------------------------------------------------------------------------
  588. void CAntlionGrub::MakeSquashDecals( const Vector &vecOrigin )
  589. {
  590. trace_t tr;
  591. Vector vecStart;
  592. Vector vecTraceDir;
  593. GetVectors( NULL, NULL, &vecTraceDir );
  594. vecTraceDir.Negate();
  595. for ( int i = 0 ; i < 8; i++ )
  596. {
  597. vecStart.x = vecOrigin.x + random->RandomFloat( -16.0f, 16.0f );
  598. vecStart.y = vecOrigin.y + random->RandomFloat( -16.0f, 16.0f );
  599. vecStart.z = vecOrigin.z + 4;
  600. UTIL_TraceLine( vecStart, vecStart + ( vecTraceDir * (5*12) ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  601. if ( tr.fraction != 1.0 )
  602. {
  603. UTIL_BloodDecalTrace( &tr, BLOOD_COLOR_YELLOW );
  604. }
  605. }
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose:
  609. //-----------------------------------------------------------------------------
  610. void CAntlionGrub::Squash( CBaseEntity *pOther, bool bDealDamage, bool bSpawnBlood )
  611. {
  612. // If we're already squashed, then don't bother doing it again!
  613. if ( GetEffects() & EF_NODRAW )
  614. return;
  615. SpawnSquashedGrub();
  616. AddEffects( EF_NODRAW );
  617. AddSolidFlags( FSOLID_NOT_SOLID );
  618. // Stop being attached to us
  619. if ( m_hGlowSprite )
  620. {
  621. FadeGlow();
  622. m_hGlowSprite->SetParent( NULL );
  623. }
  624. EmitSound( "NPC_Antlion_Grub.Die" );
  625. EmitSound( "NPC_Antlion_Grub.Squish" );
  626. // if vort stepped on me, maybe he wants to say something
  627. if ( pOther && FClassnameIs( pOther, "npc_vortigaunt" ) )
  628. {
  629. Assert(dynamic_cast<CNPC_Vortigaunt *>(pOther));
  630. static_cast<CNPC_Vortigaunt *>(pOther)->OnSquishedGrub(this);
  631. }
  632. SetTouch( NULL );
  633. //if ( bSpawnBlood )
  634. {
  635. // Temp squash effect
  636. Vector vecForward, vecUp;
  637. AngleVectors( GetAbsAngles(), &vecForward, NULL, &vecUp );
  638. // Start effects at either end of the grub
  639. Vector vecSplortPos = GetAbsOrigin() + vecForward * 14.0f;
  640. DispatchParticleEffect( "GrubSquashBlood", vecSplortPos, GetAbsAngles() );
  641. vecSplortPos = GetAbsOrigin() - vecForward * 16.0f;
  642. Vector vecDir = -vecForward;
  643. QAngle vecAngles;
  644. VectorAngles( vecDir, vecAngles );
  645. DispatchParticleEffect( "GrubSquashBlood", vecSplortPos, vecAngles );
  646. MakeSquashDecals( GetAbsOrigin() + vecForward * 32.0f );
  647. MakeSquashDecals( GetAbsOrigin() - vecForward * 32.0f );
  648. }
  649. // Deal deadly damage to ourself
  650. if ( bDealDamage )
  651. {
  652. CTakeDamageInfo info( pOther, pOther, Vector( 0, 0, -1 ), GetAbsOrigin(), GetHealth()+1, DMG_CRUSH );
  653. TakeDamage( info );
  654. }
  655. }
  656. //-----------------------------------------------------------------------------
  657. // Purpose:
  658. // Input : &info -
  659. // &vecDir -
  660. // *ptr -
  661. //-----------------------------------------------------------------------------
  662. void CAntlionGrub::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
  663. {
  664. QAngle vecAngles;
  665. VectorAngles( -vecDir, vecAngles );
  666. DispatchParticleEffect( "GrubBlood", ptr->endpos, vecAngles );
  667. BaseClass::TraceAttack( info, vecDir, ptr );
  668. }
  669. //-----------------------------------------------------------------------------
  670. // Purpose: Make the grub angry!
  671. //-----------------------------------------------------------------------------
  672. void CAntlionGrub::InputAgitate( inputdata_t &inputdata )
  673. {
  674. SetSequence( SelectWeightedSequence( ACT_SMALL_FLINCH ) );
  675. m_State = GRUB_STATE_AGITATED;
  676. m_flNextSquealSoundTime = gpGlobals->curtime;
  677. m_flFlinchTime = gpGlobals->curtime + inputdata.value.Float();
  678. SetNextThink( gpGlobals->curtime );
  679. }
  680. // =====================================================================
  681. //
  682. // Tasty grub nugget!
  683. //
  684. // =====================================================================
  685. //-----------------------------------------------------------------------------
  686. // Purpose:
  687. //-----------------------------------------------------------------------------
  688. void CGrubNugget::Spawn( void )
  689. {
  690. Precache();
  691. if ( m_nDenomination == NUGGET_LARGE )
  692. {
  693. SetModel( "models/grub_nugget_large.mdl" );
  694. }
  695. else if ( m_nDenomination == NUGGET_MEDIUM )
  696. {
  697. SetModel( "models/grub_nugget_medium.mdl" );
  698. }
  699. else
  700. {
  701. SetModel( "models/grub_nugget_small.mdl" );
  702. }
  703. // We're self-illuminating, so we don't take or give shadows
  704. AddEffects( EF_NOSHADOW|EF_NORECEIVESHADOW );
  705. m_iHealth = 1;
  706. BaseClass::Spawn();
  707. m_takedamage = DAMAGE_YES;
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Purpose:
  711. //-----------------------------------------------------------------------------
  712. void CGrubNugget::Precache( void )
  713. {
  714. PrecacheModel("models/grub_nugget_small.mdl");
  715. PrecacheModel("models/grub_nugget_medium.mdl");
  716. PrecacheModel("models/grub_nugget_large.mdl");
  717. PrecacheScriptSound( "GrubNugget.Touch" );
  718. PrecacheScriptSound( "NPC_Antlion_Grub.Explode" );
  719. PrecacheParticleSystem( "antlion_spit_player" );
  720. }
  721. //-----------------------------------------------------------------------------
  722. // Purpose: Let us be picked up by the gravity gun, regardless of our material
  723. //-----------------------------------------------------------------------------
  724. bool CGrubNugget::VPhysicsIsFlesh( void )
  725. {
  726. return false;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose:
  730. // Input : *pPlayer -
  731. // Output : Returns true on success, false on failure.
  732. //-----------------------------------------------------------------------------
  733. bool CGrubNugget::MyTouch( CBasePlayer *pPlayer )
  734. {
  735. //int nHealthToGive = sk_grubnugget_health.GetFloat() * m_nDenomination;
  736. int nHealthToGive;
  737. switch (m_nDenomination)
  738. {
  739. case NUGGET_SMALL:
  740. nHealthToGive = sk_grubnugget_health_small.GetInt();
  741. break;
  742. case NUGGET_LARGE:
  743. nHealthToGive = sk_grubnugget_health_large.GetInt();
  744. break;
  745. default:
  746. nHealthToGive = sk_grubnugget_health_medium.GetInt();
  747. }
  748. // Attempt to give the player health
  749. if ( pPlayer->TakeHealth( nHealthToGive, DMG_GENERIC ) == 0 )
  750. return false;
  751. CSingleUserRecipientFilter user( pPlayer );
  752. user.MakeReliable();
  753. UserMessageBegin( user, "ItemPickup" );
  754. WRITE_STRING( GetClassname() );
  755. MessageEnd();
  756. CPASAttenuationFilter filter( pPlayer, "GrubNugget.Touch" );
  757. EmitSound( filter, pPlayer->entindex(), "GrubNugget.Touch" );
  758. UTIL_Remove( this );
  759. return true;
  760. }
  761. //-----------------------------------------------------------------------------
  762. // Purpose:
  763. // Input : index -
  764. // *pEvent -
  765. //-----------------------------------------------------------------------------
  766. void CGrubNugget::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  767. {
  768. int damageType;
  769. float damage = CalculateDefaultPhysicsDamage( index, pEvent, 1.0f, true, damageType );
  770. if ( damage > 5.0f )
  771. {
  772. CBaseEntity *pHitEntity = pEvent->pEntities[!index];
  773. if ( pHitEntity == NULL )
  774. {
  775. // hit world
  776. pHitEntity = GetContainingEntity( INDEXENT(0) );
  777. }
  778. Vector damagePos;
  779. pEvent->pInternalData->GetContactPoint( damagePos );
  780. Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
  781. if ( damageForce == vec3_origin )
  782. {
  783. // This can happen if this entity is motion disabled, and can't move.
  784. // Use the velocity of the entity that hit us instead.
  785. damageForce = pEvent->postVelocity[!index] * pEvent->pObjects[!index]->GetMass();
  786. }
  787. // FIXME: this doesn't pass in who is responsible if some other entity "caused" this collision
  788. PhysCallbackDamage( this, CTakeDamageInfo( pHitEntity, pHitEntity, damageForce, damagePos, damage, damageType ), *pEvent, index );
  789. }
  790. BaseClass::VPhysicsCollision( index, pEvent );
  791. }
  792. //-----------------------------------------------------------------------------
  793. // Purpose:
  794. // Input : &info -
  795. //-----------------------------------------------------------------------------
  796. void CGrubNugget::Event_Killed( const CTakeDamageInfo &info )
  797. {
  798. AddEffects( EF_NODRAW );
  799. DispatchParticleEffect( "antlion_spit_player", GetAbsOrigin(), QAngle( -90, 0, 0 ) );
  800. EmitSound( "NPC_Antlion_Grub.Explode" );
  801. BaseClass::Event_Killed( info );
  802. }