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.

2737 lines
80 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: barnacle - stationary ceiling mounted 'fishing' monster
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "physics_prop_ragdoll.h"
  11. #include "npc_barnacle.h"
  12. #include "npcevent.h"
  13. #include "gib.h"
  14. #include "ai_default.h"
  15. #include "activitylist.h"
  16. #include "hl2_player.h"
  17. #include "vstdlib/random.h"
  18. #include "physics_saverestore.h"
  19. #include "vcollide_parse.h"
  20. #include "vphysics/constraints.h"
  21. #include "studio.h"
  22. #include "bone_setup.h"
  23. #include "iservervehicle.h"
  24. #include "collisionutils.h"
  25. #include "combine_mine.h"
  26. #include "explode.h"
  27. #include "npc_BaseZombie.h"
  28. #include "modelentities.h"
  29. #if HL2_EPISODIC
  30. #include "npc_antlion.h"
  31. #endif
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. float GetCurrentGravity( void );
  35. ConVar sk_barnacle_health( "sk_barnacle_health","0");
  36. static ConVar npc_barnacle_swallow( "npc_barnacle_swallow", "0", 0, "Use prototype swallow code." );
  37. const char *CNPC_Barnacle::m_szGibNames[NUM_BARNACLE_GIBS] =
  38. {
  39. "models/gibs/hgibs.mdl",
  40. "models/gibs/hgibs_scapula.mdl",
  41. "models/gibs/hgibs_rib.mdl",
  42. "models/gibs/hgibs_spine.mdl"
  43. };
  44. //-----------------------------------------------------------------------------
  45. // Private activities.
  46. //-----------------------------------------------------------------------------
  47. int ACT_BARNACLE_SLURP; // Pulling the tongue up with prey on the end
  48. int ACT_BARNACLE_BITE_HUMAN; // Biting the head of a humanoid
  49. int ACT_BARNACLE_BITE_PLAYER; // Biting the head of the player
  50. int ACT_BARNACLE_CHEW_HUMAN; // Slowly swallowing the humanoid
  51. int ACT_BARNACLE_BARF_HUMAN; // Spitting out human legs & gibs
  52. int ACT_BARNACLE_TONGUE_WRAP; // Wrapping the tongue around a target
  53. int ACT_BARNACLE_TASTE_SPIT; // Yuck! Me no like that!
  54. int ACT_BARNACLE_BITE_SMALL_THINGS; // Eats small things
  55. int ACT_BARNACLE_CHEW_SMALL_THINGS; // Chews small things
  56. //-----------------------------------------------------------------------------
  57. // Interactions
  58. //-----------------------------------------------------------------------------
  59. int g_interactionBarnacleVictimDangle = 0;
  60. int g_interactionBarnacleVictimReleased = 0;
  61. int g_interactionBarnacleVictimGrab = 0;
  62. int g_interactionBarnacleVictimBite = 0;
  63. LINK_ENTITY_TO_CLASS( npc_barnacle, CNPC_Barnacle );
  64. // Tongue Spring constants
  65. #define BARNACLE_TONGUE_SPRING_CONSTANT_HANGING 10000
  66. #define BARNACLE_TONGUE_SPRING_CONSTANT_LIFTING 10000
  67. #define BARNACLE_TONGUE_SPRING_CONSTANT_LOWERING 7000
  68. #define BARNACLE_TONGUE_SPRING_DAMPING 20
  69. #define BARNACLE_TONGUE_TIP_MASS 100
  70. #define BARNACLE_TONGUE_MAX_LIFT_MASS 70
  71. #define BARNACLE_BITE_DAMAGE_TO_PLAYER 15
  72. #define BARNACLE_DEAD_TONGUE_ALTITUDE 164
  73. #define BARNACLE_MIN_DEAD_TONGUE_CLEARANCE 78
  74. //=========================================================
  75. // Monster's Anim Events Go Here
  76. //=========================================================
  77. #define BARNACLE_AE_PUKEGIB 2
  78. #define BARNACLE_AE_BITE 3
  79. #define BARNACLE_AE_SPIT 4
  80. int AE_BARNACLE_PUKEGIB;
  81. int AE_BARNACLE_BITE;
  82. int AE_BARNACLE_SPIT;
  83. #if BARNACLE_USE_TONGUE_OFFSET
  84. // Static variable that holds the difference between the player's
  85. // eyepos and the tongue when he is seized -- used for offsetting
  86. // the drawing of the tongue so that it doesn't appear to clip into
  87. // the camera when we recenter the player.
  88. const Vector CNPC_Barnacle::m_svPlayerHeldTipOffset(24,0,-8);
  89. #endif
  90. //-----------------------------------------------------------------------------
  91. // Purpose: Constructor
  92. // Input :
  93. // Output :
  94. //-----------------------------------------------------------------------------
  95. CNPC_Barnacle::CNPC_Barnacle(void)
  96. {
  97. m_flRestUnitsAboveGround = 16.0f;
  98. m_flNextBloodTime = -1.0f;
  99. #ifndef _XBOX
  100. m_nBloodColor = BLOOD_COLOR_YELLOW;
  101. #endif
  102. m_bPlayerWasStanding = false;
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose:
  106. //-----------------------------------------------------------------------------
  107. CNPC_Barnacle::~CNPC_Barnacle( void )
  108. {
  109. // Destroy the ragdoll->tongue tip constraint
  110. if ( m_pConstraint )
  111. {
  112. physenv->DestroyConstraint( m_pConstraint );
  113. m_pConstraint = NULL;
  114. }
  115. }
  116. /*
  117. input LetGo(void) : "Let go of anything I am holding."
  118. output OnGrab(string) : "When I attach my tongue to something"
  119. output OnRelease(string) : "When I let go of something"
  120. */
  121. BEGIN_DATADESC( CNPC_Barnacle )
  122. DEFINE_FIELD( m_flAltitude, FIELD_FLOAT ),
  123. DEFINE_FIELD( m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something.
  124. DEFINE_FIELD( m_bLiftingPrey, FIELD_BOOLEAN ),
  125. DEFINE_FIELD( m_bSwallowingPrey, FIELD_BOOLEAN ),
  126. DEFINE_FIELD( m_flDigestFinish, FIELD_TIME ),
  127. DEFINE_FIELD( m_bPlayedPullSound, FIELD_BOOLEAN ),
  128. DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ),
  129. DEFINE_FIELD( m_flVictimHeight, FIELD_FLOAT ),
  130. DEFINE_FIELD( m_iGrabbedBoneIndex, FIELD_INTEGER ),
  131. DEFINE_FIELD( m_vecRoot, FIELD_POSITION_VECTOR ),
  132. DEFINE_FIELD( m_vecTip, FIELD_POSITION_VECTOR ),
  133. DEFINE_FIELD( m_hTongueRoot, FIELD_EHANDLE ),
  134. DEFINE_FIELD( m_hTongueTip, FIELD_EHANDLE ),
  135. DEFINE_FIELD( m_hRagdoll, FIELD_EHANDLE ),
  136. DEFINE_AUTO_ARRAY( m_pRagdollBones, FIELD_MATRIX3X4_WORLDSPACE ),
  137. DEFINE_PHYSPTR( m_pConstraint ),
  138. DEFINE_KEYFIELD( m_flRestUnitsAboveGround, FIELD_FLOAT, "RestDist" ),
  139. DEFINE_FIELD( m_nSpitAttachment, FIELD_INTEGER ),
  140. DEFINE_FIELD( m_hLastSpitEnemy, FIELD_EHANDLE ),
  141. DEFINE_FIELD( m_nShakeCount, FIELD_INTEGER ),
  142. DEFINE_FIELD( m_flNextBloodTime, FIELD_TIME ),
  143. #ifndef _XBOX
  144. DEFINE_FIELD( m_nBloodColor, FIELD_INTEGER ),
  145. #endif
  146. DEFINE_FIELD( m_vecBloodPos, FIELD_POSITION_VECTOR ),
  147. DEFINE_FIELD( m_flBarnaclePullSpeed, FIELD_FLOAT ),
  148. DEFINE_FIELD( m_flLocalTimer, FIELD_TIME ),
  149. DEFINE_FIELD( m_vLastEnemyPos, FIELD_POSITION_VECTOR ),
  150. DEFINE_FIELD( m_flLastPull, FIELD_FLOAT ),
  151. DEFINE_EMBEDDED( m_StuckTimer ),
  152. DEFINE_INPUTFUNC( FIELD_VOID, "DropTongue", InputDropTongue ),
  153. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDropTongueSpeed", InputSetDropTongueSpeed ),
  154. #ifdef HL2_EPISODIC
  155. DEFINE_INPUTFUNC( FIELD_VOID, "LetGo", InputLetGo ),
  156. DEFINE_OUTPUT( m_OnGrab, "OnGrab" ),
  157. DEFINE_OUTPUT( m_OnRelease, "OnRelease" ),
  158. #endif
  159. // Function pointers
  160. DEFINE_THINKFUNC( BarnacleThink ),
  161. DEFINE_THINKFUNC( WaitTillDead ),
  162. DEFINE_FIELD( m_bSwallowingBomb, FIELD_BOOLEAN ),
  163. END_DATADESC()
  164. IMPLEMENT_SERVERCLASS_ST( CNPC_Barnacle, DT_Barnacle )
  165. SendPropFloat( SENDINFO( m_flAltitude ), 0, SPROP_NOSCALE),
  166. SendPropVector( SENDINFO( m_vecRoot ), 0, SPROP_COORD ),
  167. SendPropVector( SENDINFO( m_vecTip ), 0, SPROP_COORD ),
  168. SendPropVector( SENDINFO( m_vecTipDrawOffset ), 0, SPROP_NOSCALE ),
  169. END_SEND_TABLE()
  170. //=========================================================
  171. // Classify - indicates this monster's place in the
  172. // relationship table.
  173. //=========================================================
  174. Class_T CNPC_Barnacle::Classify ( void )
  175. {
  176. return CLASS_BARNACLE;
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose: Initialize absmin & absmax to the appropriate box
  180. //-----------------------------------------------------------------------------
  181. void CNPC_Barnacle::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
  182. {
  183. // Extend our bounding box downwards the length of the tongue
  184. CollisionProp()->WorldSpaceAABB( pVecWorldMins, pVecWorldMaxs );
  185. // We really care about the tongue tip. The altitude is not really relevant.
  186. VectorMin( *pVecWorldMins, m_vecTip, *pVecWorldMins );
  187. VectorMax( *pVecWorldMaxs, m_vecTip, *pVecWorldMaxs );
  188. // pVecWorldMins->z -= m_flAltitude;
  189. }
  190. //=========================================================
  191. // HandleAnimEvent - catches the monster-specific messages
  192. // that occur when tagged animation frames are played.
  193. //
  194. // Returns number of events handled, 0 if none.
  195. //=========================================================
  196. void CNPC_Barnacle::HandleAnimEvent( animevent_t *pEvent )
  197. {
  198. if ( pEvent->event== AE_BARNACLE_PUKEGIB )
  199. {
  200. CGib::SpawnSpecificGibs( this, 1, 50, 1, "models/gibs/hgibs_rib.mdl");
  201. return;
  202. }
  203. if ( pEvent->event == AE_BARNACLE_BITE )
  204. {
  205. BitePrey();
  206. return;
  207. }
  208. if ( pEvent->event == AE_BARNACLE_SPIT )
  209. {
  210. SpitPrey();
  211. return;
  212. }
  213. BaseClass::HandleAnimEvent( pEvent );
  214. }
  215. //=========================================================
  216. // Spawn
  217. //=========================================================
  218. void CNPC_Barnacle::Spawn()
  219. {
  220. Precache( );
  221. SetModel( "models/barnacle.mdl" );
  222. UTIL_SetSize( this, Vector(-16, -16, -40), Vector(16, 16, 0) );
  223. SetSolid( SOLID_BBOX );
  224. AddSolidFlags( FSOLID_NOT_STANDABLE );
  225. CollisionProp()->SetSurroundingBoundsType( USE_GAME_CODE );
  226. #if HL2_EPISODIC // the episodic barnacle is solid, so it can be sawbladed.
  227. SetMoveType( MOVETYPE_PUSH );
  228. #else
  229. SetMoveType( MOVETYPE_NONE );
  230. #endif
  231. SetBloodColor( BLOOD_COLOR_GREEN );
  232. m_iHealth = sk_barnacle_health.GetFloat();
  233. m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  234. m_NPCState = NPC_STATE_NONE;
  235. m_cGibs = 0;
  236. m_bLiftingPrey = false;
  237. m_bSwallowingPrey = false;
  238. m_bSwallowingBomb = false;
  239. m_flDigestFinish = 0;
  240. m_takedamage = DAMAGE_YES;
  241. m_pConstraint = NULL;
  242. m_nShakeCount = 0;
  243. #if HL2_EPISODIC // the episodic barnacle is solid, so it can be sawbladed.
  244. IPhysicsObject *pPhys = VPhysicsInitShadow( false, false );
  245. if (pPhys)
  246. {
  247. pPhys->SetMass(500);
  248. }
  249. #endif
  250. InitBoneControllers();
  251. InitTonguePosition();
  252. // set eye position
  253. SetDefaultEyeOffset();
  254. // Add some variation because we're often in large bunches
  255. SetActivity( ACT_IDLE );
  256. SetPlaybackRate( random->RandomFloat( 0.8f, 1.2f ) );
  257. SetThink ( &CNPC_Barnacle::BarnacleThink );
  258. SetNextThink( gpGlobals->curtime + 0.5f );
  259. m_flBarnaclePullSpeed = BARNACLE_PULL_SPEED;
  260. //Do not have a shadow
  261. AddEffects( EF_NOSHADOW );
  262. AddFlag( FL_AIMTARGET );
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Sets the tongue's height
  266. //-----------------------------------------------------------------------------
  267. void CNPC_Barnacle::SetAltitude( float flAltitude )
  268. {
  269. if ( HasSpawnFlags( SF_BARNACLE_AMBUSH ) )
  270. return;
  271. m_flAltitude = flAltitude;
  272. }
  273. void CNPC_Barnacle::DropTongue( void )
  274. {
  275. if ( m_hTongueRoot )
  276. return;
  277. m_hTongueRoot = CBarnacleTongueTip::CreateTongueRoot( m_vecRoot, QAngle(90,0,0) );
  278. m_hTongueTip = CBarnacleTongueTip::CreateTongueTip( this, m_hTongueRoot, m_vecTip, QAngle(0,0,0) );
  279. m_nSpitAttachment = LookupAttachment( "StrikeHeadAttach" );
  280. Assert( m_hTongueRoot && m_hTongueTip );
  281. RemoveSpawnFlags( SF_BARNACLE_AMBUSH );
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose:
  285. //-----------------------------------------------------------------------------
  286. void CNPC_Barnacle::Activate( void )
  287. {
  288. BaseClass::Activate();
  289. if ( HasSpawnFlags( SF_BARNACLE_AMBUSH ) )
  290. return;
  291. // Create our tongue tips
  292. if ( !m_hTongueRoot )
  293. {
  294. DropTongue();
  295. }
  296. else if ( GetEnemy() && IsEnemyAPlayer() && !m_pConstraint )
  297. {
  298. IPhysicsObject *pPlayerPhys = GetEnemy()->VPhysicsGetObject();
  299. IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
  300. constraint_fixedparams_t fixed;
  301. fixed.Defaults();
  302. fixed.InitWithCurrentObjectState( pTonguePhys, pPlayerPhys );
  303. fixed.constraint.Defaults();
  304. m_pConstraint = physenv->CreateFixedConstraint( pTonguePhys, pPlayerPhys, NULL, fixed );
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. // Input :
  310. // Output :
  311. //-----------------------------------------------------------------------------
  312. int CNPC_Barnacle::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  313. {
  314. CTakeDamageInfo info = inputInfo;
  315. if ( info.GetDamageType() & DMG_CLUB )
  316. {
  317. info.SetDamage( m_iHealth );
  318. }
  319. if ( GetActivity() == ACT_IDLE )
  320. {
  321. SetActivity( ACT_SMALL_FLINCH );
  322. }
  323. if( hl2_episodic.GetBool() && info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_PLAYER_ALLY_VITAL )
  324. {
  325. if( FClassnameIs( info.GetAttacker(), "npc_alyx" ) )
  326. {
  327. // Alyx does double damage to barnacles, so that she can save the
  328. // player's life in a more timely fashion. (sjb)
  329. info.ScaleDamage( 2.0f );
  330. }
  331. }
  332. DropTongue();
  333. return BaseClass::OnTakeDamage_Alive( info );
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Player has illuminated this NPC with the flashlight
  337. //-----------------------------------------------------------------------------
  338. void CNPC_Barnacle::PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot )
  339. {
  340. // Create a sound to scare friendly allies away from the base on the barnacle
  341. if( IsAlive() )
  342. {
  343. CSoundEnt::InsertSound( SOUND_MOVE_AWAY | SOUND_CONTEXT_ALLIES_ONLY, m_vecTip, 60.0f, FLASHLIGHT_NPC_CHECK_INTERVAL );
  344. }
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose: Initialize tongue position when first spawned
  348. // Input :
  349. // Output :
  350. //-----------------------------------------------------------------------------
  351. void CNPC_Barnacle::InitTonguePosition( void )
  352. {
  353. CBaseEntity *pTouchEnt;
  354. float flLength;
  355. pTouchEnt = TongueTouchEnt( &flLength );
  356. SetAltitude( flLength );
  357. Vector origin;
  358. GetAttachment( "TongueEnd", origin );
  359. float flTongueAdj = origin.z - GetAbsOrigin().z;
  360. m_vecRoot = origin - Vector(0,0,flTongueAdj);
  361. m_vecTip.Set( m_vecRoot.Get() - Vector(0,0,(float)m_flAltitude) );
  362. CollisionProp()->MarkSurroundingBoundsDirty();
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose:
  366. // TODO: The LostPrey(true) at the top of if ( m_hRagdoll ) isnt' quite right:
  367. // it will make the barnacle drop anything that's shot on the way up. This is a
  368. // quick fix for the antlions which crashed otherwise (they have somewhat anomalous
  369. // ragdoll behaivor) but should be revisted.
  370. //-----------------------------------------------------------------------------
  371. void CNPC_Barnacle::BarnacleThink ( void )
  372. {
  373. CBaseEntity *pTouchEnt;
  374. float flLength;
  375. SetNextThink( gpGlobals->curtime + 0.1f );
  376. UpdateTongue();
  377. // AI Disabled, don't do anything?
  378. if ( CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI )
  379. return;
  380. // Do we have an enemy?
  381. if ( m_hRagdoll )
  382. {
  383. if ( m_bLiftingPrey )
  384. {
  385. if ( GetEnemy() )
  386. {
  387. LiftPrey();
  388. }
  389. else
  390. {
  391. LostPrey(true);
  392. }
  393. }
  394. else if ( m_bSwallowingPrey )
  395. {
  396. // Slowly swallowing the ragdoll
  397. SwallowPrey();
  398. }
  399. // Stay bloated as we digest
  400. else if ( m_flDigestFinish )
  401. {
  402. // Still digesting him>
  403. if ( m_flDigestFinish > gpGlobals->curtime )
  404. {
  405. if ( IsActivityFinished() )
  406. {
  407. SetActivity( ACT_IDLE );
  408. }
  409. // bite prey every once in a while
  410. if ( random->RandomInt(0,25) == 0 )
  411. {
  412. EmitSound( "NPC_Barnacle.Digest" );
  413. }
  414. }
  415. else
  416. {
  417. // Finished digesting
  418. #if HL2_EPISODIC
  419. // have to save this off because LostPrey() resets it (and if we take damage before hitting that,
  420. // then the dead thing will go flying)
  421. bool poisoned = m_bSwallowingPoison;
  422. LostPrey( true ); // Remove all evidence
  423. m_flDigestFinish = 0;
  424. if ( poisoned )
  425. { // hurt me
  426. TakeDamage( CTakeDamageInfo( this, this, m_iHealth, DMG_ACID ) );
  427. }
  428. #else
  429. LostPrey( true ); // Remove all evidence
  430. m_flDigestFinish = 0;
  431. #endif
  432. }
  433. }
  434. }
  435. else if ( GetEnemy() )
  436. {
  437. if ( m_bLiftingPrey || m_bSwallowingBomb == true )
  438. {
  439. LiftPrey();
  440. }
  441. // Stay bloated as we digest
  442. else if ( m_flDigestFinish )
  443. {
  444. // Still digesting him
  445. if ( m_flDigestFinish > gpGlobals->curtime )
  446. {
  447. if ( IsActivityFinished() )
  448. {
  449. SetActivity( ACT_IDLE );
  450. }
  451. // bite prey every once in a while
  452. if ( random->RandomInt(0,25) == 0 )
  453. {
  454. EmitSound( "NPC_Barnacle.Digest" );
  455. }
  456. }
  457. else
  458. {
  459. // Finished digesting
  460. #if HL2_EPISODIC
  461. // have to save this off because LostPrey() resets it (and if we take damage before hitting that,
  462. // then the dead thing will go flying)
  463. bool poisoned = m_bSwallowingPoison;
  464. LostPrey( true ); // Remove all evidence
  465. m_flDigestFinish = 0;
  466. if ( poisoned )
  467. { // hurt me
  468. TakeDamage( CTakeDamageInfo( this, this, m_iHealth, DMG_ACID ) );
  469. }
  470. #else
  471. LostPrey( true ); // Remove all evidence
  472. m_flDigestFinish = 0;
  473. #endif
  474. }
  475. }
  476. }
  477. else
  478. {
  479. // Were we lifting prey?
  480. if ( m_bSwallowingPrey || m_bLiftingPrey )
  481. {
  482. // Something removed our prey.
  483. LostPrey( false );
  484. }
  485. // barnacle has no prey right now, so just idle and check to see if anything is touching the tongue.
  486. // If idle and no nearby client, don't think so often
  487. // NOTE: Use the surrounding bounds so that we'll think often event if the tongue
  488. // tip is in the PVS but the body isn't
  489. Vector vecSurroundMins, vecSurroundMaxs;
  490. CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  491. if ( !UTIL_FindClientInPVS( vecSurroundMins, vecSurroundMaxs ) )
  492. {
  493. SetNextThink( gpGlobals->curtime + random->RandomFloat(1,1.5) ); // Stagger a bit to keep barnacles from thinking on the same frame
  494. }
  495. if ( IsActivityFinished() && GetActivity() != ACT_IDLE )
  496. {
  497. // this is done so barnacle will fidget.
  498. // Add some variation because we're often in large bunches
  499. SetActivity( ACT_IDLE );
  500. SetPlaybackRate( random->RandomFloat( 0.8f, 1.2f ) );
  501. }
  502. if ( m_cGibs && random->RandomInt(0,99) == 1 )
  503. {
  504. // cough up a gib.
  505. CGib::SpawnSpecificGibs( this, 1, 50, 1, "models/gibs/hgibs_rib.mdl");
  506. m_cGibs--;
  507. EmitSound( "NPC_Barnacle.Digest" );
  508. }
  509. pTouchEnt = TongueTouchEnt( &flLength );
  510. // If there's something under us, lower the tongue down so we can grab it
  511. if ( m_flAltitude < flLength )
  512. {
  513. float dt = gpGlobals->curtime - GetLastThink();
  514. SetAltitude( m_flAltitude + m_flBarnaclePullSpeed * dt );
  515. }
  516. // NOTE: SetAltitude above will change m_flAltitude, hence the second check
  517. if ( m_flAltitude >= flLength )
  518. {
  519. // If we're already low enough, try to grab.
  520. bool bGrabbedTarget = false;
  521. if ( ( pTouchEnt != NULL ) && ( pTouchEnt != m_hLastSpitEnemy.Get() ) )
  522. {
  523. // tongue is fully extended, and is touching someone.
  524. CBaseCombatCharacter *pBCC = dynamic_cast<CBaseCombatCharacter *>(pTouchEnt);
  525. if( CanPickup( pBCC ) )
  526. {
  527. Vector vecGrabPos = pTouchEnt->EyePosition();
  528. if( !pBCC || pBCC->DispatchInteraction( g_interactionBarnacleVictimGrab, &vecGrabPos, this ) )
  529. {
  530. EmitSound( "NPC_Barnacle.BreakNeck" );
  531. AttachTongueToTarget( pTouchEnt, vecGrabPos );
  532. // Set the local timer to 60 seconds, which starts the lifting phase on
  533. // the upshot of the sine wave which right away makes it more obvious
  534. // that the player is being lifted.
  535. m_flLocalTimer = 60.0f;
  536. m_vLastEnemyPos = pTouchEnt->GetAbsOrigin();
  537. m_flLastPull = 0;
  538. m_StuckTimer.Set(3.0);
  539. bGrabbedTarget = true;
  540. // Set our touch flag so no one else tries to grab us this frame
  541. pTouchEnt->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  542. }
  543. }
  544. }
  545. if ( !bGrabbedTarget )
  546. {
  547. // Restore the hanging spring constant
  548. if ( m_hTongueTip )
  549. {
  550. m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_HANGING );
  551. }
  552. SetAltitude( flLength );
  553. }
  554. }
  555. }
  556. // NDebugOverlay::Box( GetAbsOrigin() - Vector( 0, 0, m_flAltitude ), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,255,255, 0, 0.1 );
  557. StudioFrameAdvance();
  558. DispatchAnimEvents( this );
  559. }
  560. //-----------------------------------------------------------------------------
  561. //-----------------------------------------------------------------------------
  562. bool CNPC_Barnacle::CanPickup( CBaseCombatCharacter *pBCC )
  563. {
  564. // Barnacle can pick this item up because it has already passed the filters
  565. // in TongueTouchEnt. It just isn't an NPC or player and doesn't need further inspection.
  566. if( !pBCC )
  567. return true;
  568. // Don't pickup turrets
  569. if( FClassnameIs( pBCC, "npc_turret_floor" ) )
  570. return false;
  571. // Don't pick up a dead player or NPC
  572. if( !pBCC->IsAlive() )
  573. return false;
  574. if( pBCC->IsPlayer() )
  575. {
  576. CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>(pBCC);
  577. Assert( pPlayer != NULL );
  578. // Don't pick up a player held by another barnacle
  579. if( pPlayer->HasPhysicsFlag(PFLAG_ONBARNACLE) )
  580. return false;
  581. }
  582. else if ( pBCC->IsInAVehicle() )
  583. {
  584. // Don't pluck an NPC from a vehicle.
  585. return false;
  586. }
  587. return true;
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Allows the ragdoll to settle before biting it
  591. //-----------------------------------------------------------------------------
  592. bool CNPC_Barnacle::WaitForRagdollToSettle( float flBiteZOffset )
  593. {
  594. Vector vecVictimPos = GetEnemy()->GetAbsOrigin();
  595. Vector vecCheckPos;
  596. QAngle vecBoneAngles;
  597. m_hRagdoll->GetBonePosition( m_iGrabbedBoneIndex, vecCheckPos, vecBoneAngles );
  598. // Stop sucking while we wait for the ragdoll to settle
  599. SetActivity( ACT_IDLE );
  600. Vector vecVelocity;
  601. AngularImpulse angVel;
  602. float flDelta = 4.0;
  603. // Only bite if the target bone is in the right position.
  604. Vector vecBitePoint = GetAbsOrigin();
  605. vecBitePoint.z -= flBiteZOffset;
  606. //NDebugOverlay::Box( vecBitePoint, -Vector(10,10,10), Vector(10,10,10), 0,255,0, 0, 0.1 );
  607. //NDebugOverlay::Line( vecBitePoint, vecCheckPos, 0, 255, 0, true, 0.1 );
  608. if ( (vecBitePoint.x - vecCheckPos.x) > flDelta || (vecBitePoint.y - vecCheckPos.y) > flDelta )
  609. {
  610. // I can't bite this critter because it's not lined up with me on the X/Y plane. If it is
  611. // as close to my mouth as I can get it, I should drop it.
  612. if( vecBitePoint.z - vecVictimPos.z < 72.0f )
  613. {
  614. // A man-sized target has been pulled up to my mouth, but
  615. // is not aligned for biting. Drop it.
  616. SpitPrey();
  617. }
  618. return false;
  619. }
  620. // Right height?
  621. if ( (vecBitePoint.z - vecCheckPos.z) > flDelta )
  622. {
  623. // Slowly raise / lower the target into the right position
  624. if ( vecBitePoint.z > vecCheckPos.z )
  625. {
  626. // Pull the victim towards the mouth
  627. SetAltitude( m_flAltitude - 1 );
  628. vecVictimPos.z += 1;
  629. }
  630. else
  631. {
  632. // We pulled 'em up too far, so lower them a little
  633. SetAltitude( m_flAltitude + 1 );
  634. vecVictimPos.z -= 1;
  635. }
  636. UTIL_SetOrigin ( GetEnemy(), vecVictimPos );
  637. return false;
  638. }
  639. // Get the velocity of the bone we've grabbed, and only bite when it's not moving much
  640. CStudioHdr *pStudioHdr = m_hRagdoll->GetModelPtr();
  641. mstudiobone_t *pBone = pStudioHdr->pBone( m_iGrabbedBoneIndex );
  642. int iBoneIndex = pBone->physicsbone;
  643. ragdoll_t *pRagdoll = m_hRagdoll->GetRagdoll();
  644. IPhysicsObject *pRagdollPhys = pRagdoll->list[iBoneIndex].pObject;
  645. pRagdollPhys->GetVelocity( &vecVelocity, &angVel );
  646. return ( vecVelocity.LengthSqr() < 20 );
  647. }
  648. //-----------------------------------------------------------------------------
  649. // Allows the physics prop to settle before biting it
  650. //-----------------------------------------------------------------------------
  651. bool CNPC_Barnacle::WaitForPhysicsObjectToSettle( float flBiteZOffset )
  652. {
  653. --m_nShakeCount;
  654. if ( m_nShakeCount & 0x1 )
  655. {
  656. SetAltitude( flBiteZOffset + 15 );
  657. }
  658. else
  659. {
  660. SetAltitude( flBiteZOffset );
  661. }
  662. return ( m_nShakeCount <= 0 );
  663. /*
  664. IPhysicsObject *pPhysicsObject = GetEnemy()->VPhysicsGetObject();
  665. Vector vecVelocity;
  666. AngularImpulse angVel;
  667. pPhysicsObject->GetVelocity( &vecVelocity, &angVel );
  668. return ( vecVelocity.LengthSqr() < 25 );
  669. */
  670. }
  671. //-----------------------------------------------------------------------------
  672. // Purpose: Make a horrific noise before we pull the prey stuck to our tongue up towards our mouth
  673. //-----------------------------------------------------------------------------
  674. void CNPC_Barnacle::PlayLiftingScream( float flBiteZOffset )
  675. {
  676. if ( !m_bPlayedPullSound && m_flAltitude < (flBiteZOffset + 100) )
  677. {
  678. EmitSound( "NPC_Barnacle.Scream" );
  679. m_bPlayedPullSound = true;
  680. }
  681. }
  682. //-----------------------------------------------------------------------------
  683. // Purpose: Lift the prey stuck to our tongue up towards our mouth
  684. //-----------------------------------------------------------------------------
  685. void CNPC_Barnacle::PullEnemyTorwardsMouth( bool bAdjustEnemyOrigin )
  686. {
  687. CBaseEntity *pEnemy = GetEnemy();
  688. if ( pEnemy->IsPlayer() && pEnemy->GetMoveType() == MOVETYPE_NOCLIP )
  689. {
  690. LostPrey( false );
  691. return;
  692. }
  693. // Pull the victim towards the mouth
  694. float dt = gpGlobals->curtime - GetLastThink();
  695. // Assumes constant frame rate :|
  696. m_flLocalTimer += dt;
  697. float flPull = fabs(sin( m_flLocalTimer * 5 ));
  698. flPull *= m_flBarnaclePullSpeed * dt;
  699. SetAltitude( m_flAltitude - flPull );
  700. if ( bAdjustEnemyOrigin )
  701. {
  702. if ( m_flLastPull > 1.0 )
  703. {
  704. if ( (pEnemy->GetAbsOrigin() - m_vLastEnemyPos).LengthSqr() < Square( m_flLastPull - 1.0 ) )
  705. {
  706. if ( m_StuckTimer.Expired() )
  707. {
  708. LostPrey( false );
  709. return;
  710. }
  711. }
  712. else
  713. {
  714. m_StuckTimer.Set(3.0);
  715. }
  716. }
  717. else
  718. m_StuckTimer.Delay(dt);
  719. m_vLastEnemyPos = pEnemy->GetAbsOrigin();
  720. m_flLastPull = flPull;
  721. Vector vecNewPos = m_vLastEnemyPos;
  722. // vecNewPos.z += flPull;
  723. #if 0
  724. // this is an example of one somewhat crude attempt to realign objects so that they are directly underneath
  725. // the barnacle. It introduces unacceptable oscillation.
  726. const float MAX_CENTERING_VELOCITY = 24.0f;
  727. float distToMove = MAX_CENTERING_VELOCITY * dt;
  728. Vector2D vToCenter = GetAbsOrigin().AsVector2D() - GetEnemy()->GetAbsOrigin().AsVector2D();
  729. float distFromCenter = vToCenter.NormalizeInPlace();
  730. Msg("<%.3f,%.3f>\n",vToCenter.x,vToCenter.y);
  731. if ( distFromCenter < distToMove )
  732. {
  733. vecNewPos.x = GetAbsOrigin().x;
  734. vecNewPos.y = GetAbsOrigin().y;
  735. }
  736. else
  737. {
  738. vToCenter *= distToMove;
  739. vecNewPos.x += vToCenter.x;
  740. vecNewPos.y += vToCenter.y;
  741. // GetEnemy()->Teleport( &vecNewPos, NULL, NULL );
  742. }
  743. #endif
  744. // recentering the player under the barnacle was tried in the code
  745. // below, but then disabled for Orange Box ship because the viewmodel
  746. // jitter became unacceptably noisy after other changes to physics
  747. // and client.
  748. #if 0
  749. // this technique is a little noisy and needs to be readdressed.
  750. if (pEnemy->IsPlayer())
  751. {
  752. Vector playerOrigin = GetEnemy()->GetAbsOrigin();
  753. Vector2D vToCenter = GetAbsOrigin().AsVector2D() - playerOrigin.AsVector2D();
  754. float distFromCenter = vToCenter.NormalizeInPlace();
  755. // if we're off by more than a few inches
  756. if ( distFromCenter > 6.0f )
  757. {
  758. // get us there in a second
  759. Vector desiredVelocity;
  760. float distToMove = min(distFromCenter, 24.0f * dt);
  761. desiredVelocity.x = vToCenter.x * distToMove;
  762. desiredVelocity.y = vToCenter.y * distToMove;
  763. desiredVelocity.z = 0;
  764. #if 0 // here is a physical force-based way (too noisy!):
  765. IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
  766. pTonguePhys->ApplyForceCenter(desiredVelocity);
  767. #else
  768. vecNewPos = playerOrigin + desiredVelocity;
  769. // find how far we can actually transport the player
  770. trace_t tr;
  771. UTIL_TraceEntity( pEnemy, playerOrigin, vecNewPos, MASK_PLAYERSOLID, m_hTongueTip.Get(), pEnemy->GetCollisionGroup(), &tr );
  772. pEnemy->Teleport(&tr.endpos, NULL, &desiredVelocity);
  773. #endif
  774. }
  775. }
  776. #endif
  777. // GetEnemy()->Teleport( &vecNewPos, NULL, NULL );
  778. if( pEnemy->GetFlags() & FL_ONGROUND )
  779. {
  780. // Try to fight OnGround
  781. pEnemy->SetGravity( 0 );
  782. pEnemy->RemoveFlag( FL_ONGROUND );
  783. }
  784. }
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Purpose:
  788. //-----------------------------------------------------------------------------
  789. void CNPC_Barnacle::UpdatePlayerConstraint( void )
  790. {
  791. // Check to see if the player's standing/ducking state has changed.
  792. CBasePlayer *pPlayer = static_cast<CBasePlayer*>( GetEnemy() );
  793. bool bStanding = ( ( pPlayer->GetFlags() & FL_DUCKING ) == 0 );
  794. if ( bStanding == m_bPlayerWasStanding )
  795. return;
  796. // if player is on the ladder, disengage him
  797. if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
  798. {
  799. pPlayer->ExitLadder();
  800. }
  801. // Destroy the current constraint.
  802. physenv->DestroyConstraint( m_pConstraint );
  803. m_pConstraint = NULL;
  804. if ( m_hTongueTip )
  805. {
  806. // Create the new constraint for the standing/ducking player physics object.
  807. IPhysicsObject *pPlayerPhys = pPlayer->VPhysicsGetObject();
  808. IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
  809. constraint_fixedparams_t fixed;
  810. fixed.Defaults();
  811. fixed.InitWithCurrentObjectState( pTonguePhys, pPlayerPhys );
  812. fixed.constraint.Defaults();
  813. m_pConstraint = physenv->CreateFixedConstraint( pTonguePhys, pPlayerPhys, NULL, fixed );
  814. }
  815. // Save state for the next check.
  816. m_bPlayerWasStanding = bStanding;
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Purpose: Lift the prey stuck to our tongue up towards our mouth
  820. //-----------------------------------------------------------------------------
  821. void CNPC_Barnacle::LiftPlayer( float flBiteZOffset )
  822. {
  823. // Add an additional height for the player to avoid view clipping
  824. flBiteZOffset += 25.0;
  825. // Play a scream when we're almost within bite range
  826. PlayLiftingScream( flBiteZOffset );
  827. // Update player constraint.
  828. UpdatePlayerConstraint();
  829. // Figure out when the prey has reached our bite range use eye position to avoid
  830. // clipping into the barnacle body
  831. if ( GetAbsOrigin().z - GetEnemy()->EyePosition().z < flBiteZOffset)
  832. {
  833. m_bLiftingPrey = false;
  834. // Start the bite animation. The anim event in it will finish the job.
  835. SetActivity( (Activity)ACT_BARNACLE_BITE_PLAYER );
  836. }
  837. else
  838. {
  839. PullEnemyTorwardsMouth( true );
  840. }
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose: Lift the prey stuck to our tongue up towards our mouth
  844. //-----------------------------------------------------------------------------
  845. void CNPC_Barnacle::LiftNPC( float flBiteZOffset )
  846. {
  847. // Necessary to make the NPCs not do things like talk
  848. GetEnemy()->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  849. // Play a scream when we're almost within bite range
  850. PlayLiftingScream( flBiteZOffset );
  851. // Figure out when the prey has reached our bite range
  852. if ( GetAbsOrigin().z - m_vecTip.Get().z < flBiteZOffset )
  853. {
  854. m_bLiftingPrey = false;
  855. const Vector &vecSize = GetEnemy()->CollisionProp()->OBBSize();
  856. if ( vecSize.z < 40 )
  857. {
  858. // Start the bite animation. The anim event in it will finish the job.
  859. SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
  860. }
  861. else
  862. {
  863. // Start the bite animation. The anim event in it will finish the job.
  864. SetActivity( (Activity)ACT_BARNACLE_BITE_HUMAN );
  865. }
  866. }
  867. else
  868. {
  869. PullEnemyTorwardsMouth( true );
  870. }
  871. }
  872. //-----------------------------------------------------------------------------
  873. // Purpose: Lift the prey stuck to our tongue up towards our mouth
  874. //-----------------------------------------------------------------------------
  875. void CNPC_Barnacle::LiftRagdoll( float flBiteZOffset )
  876. {
  877. // Necessary to make the NPCs not do things like talk
  878. GetEnemy()->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  879. // Play a scream when we're almost within bite range
  880. PlayLiftingScream( flBiteZOffset );
  881. // Figure out when the prey has reached our bite range
  882. if ( GetAbsOrigin().z - m_vecTip.Get().z < flBiteZOffset )
  883. {
  884. // If we've got a ragdoll, wait until the bone is down below the mouth.
  885. if ( !WaitForRagdollToSettle( flBiteZOffset ) )
  886. return;
  887. if ( GetEnemy()->Classify() == CLASS_ZOMBIE )
  888. {
  889. // lifted the prey high enough to see it's a zombie. Spit it out.
  890. if ( hl2_episodic.GetBool() )
  891. {
  892. m_bLiftingPrey = false;
  893. SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
  894. }
  895. else
  896. {
  897. SpitPrey();
  898. }
  899. return;
  900. }
  901. m_bLiftingPrey = false;
  902. const Vector &vecSize = GetEnemy()->CollisionProp()->OBBSize();
  903. if ( vecSize.z < 40 )
  904. {
  905. // Start the bite animation. The anim event in it will finish the job.
  906. SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
  907. }
  908. else
  909. {
  910. // Start the bite animation. The anim event in it will finish the job.
  911. SetActivity( (Activity)ACT_BARNACLE_BITE_HUMAN );
  912. }
  913. }
  914. else
  915. {
  916. // Pull the victim towards the mouth
  917. PullEnemyTorwardsMouth( false );
  918. // Apply forces to the attached ragdoll based upon the animations of the enemy, if the enemy is still alive.
  919. if ( GetEnemy()->IsAlive() )
  920. {
  921. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>( GetEnemy() );
  922. // Get the current bone matrix
  923. /*
  924. Vector pos[MAXSTUDIOBONES];
  925. Quaternion q[MAXSTUDIOBONES];
  926. matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
  927. CalcPoseSingle( pStudioHdr, pos, q, pAnimating->GetSequence(), pAnimating->m_flCycle, pAnimating->GetPoseParameterArray(), BONE_USED_BY_ANYTHING );
  928. Studio_BuildMatrices( pStudioHdr, vec3_angle, vec3_origin, pos, q, -1, pBoneToWorld, BONE_USED_BY_ANYTHING );
  929. // Apply the forces to the ragdoll
  930. RagdollApplyAnimationAsVelocity( *(m_hRagdoll->GetRagdoll()), pBoneToWorld );
  931. */
  932. // Get the current bone matrix
  933. matrix3x4_t pBoneToWorld[MAXSTUDIOBONES];
  934. pAnimating->SetupBones( pBoneToWorld, BONE_USED_BY_ANYTHING );
  935. // Apply the forces to the ragdoll
  936. RagdollApplyAnimationAsVelocity( *(m_hRagdoll->GetRagdoll()), m_pRagdollBones, pBoneToWorld, 0.2 );
  937. // Store off the current bone matrix for next time
  938. pAnimating->SetupBones( m_pRagdollBones, BONE_USED_BY_ANYTHING );
  939. }
  940. }
  941. }
  942. //-----------------------------------------------------------------------------
  943. // Purpose: Lift the prey stuck to our tongue up towards our mouth
  944. //-----------------------------------------------------------------------------
  945. void CNPC_Barnacle::LiftPhysicsObject( float flBiteZOffset )
  946. {
  947. CBaseEntity *pVictim = GetEnemy();
  948. // Bite a little higher up, since the bits point is the tip of the tongue
  949. flBiteZOffset -= 5.0f;
  950. //NDebugOverlay::Box( vecCheckPos, -Vector(10,10,10), Vector(10,10,10), 255,255,255, 0, 0.1 );
  951. // Play a scream when we're almost within bite range
  952. PlayLiftingScream( flBiteZOffset );
  953. // Figure out when the prey has reached our bite range
  954. if ( GetAbsOrigin().z - m_vecTip.Get().z < flBiteZOffset ) // then yes, let's chomp
  955. {
  956. if ( m_hTongueTip )
  957. {
  958. m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_HANGING );
  959. }
  960. // Wait until the physics object stops flailing
  961. if ( !WaitForPhysicsObjectToSettle( flBiteZOffset ) )
  962. return;
  963. // Necessary for good +use interactions
  964. pVictim->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  965. // If we got a physics prop, wait until the thing has settled down
  966. m_bLiftingPrey = false;
  967. if ( hl2_episodic.GetBool() )
  968. {
  969. CBounceBomb *pBounce = dynamic_cast<CBounceBomb *>( pVictim );
  970. if ( pBounce )
  971. {
  972. if ( m_bSwallowingBomb == true )
  973. {
  974. pBounce->ExplodeThink();
  975. return;
  976. }
  977. SetActivity( (Activity)ACT_BARNACLE_BITE_SMALL_THINGS );
  978. }
  979. else
  980. {
  981. // Start the bite animation. The anim event in it will finish the job.
  982. SetActivity( (Activity)ACT_BARNACLE_TASTE_SPIT );
  983. }
  984. }
  985. else
  986. {
  987. // Start the bite animation. The anim event in it will finish the job.
  988. SetActivity( (Activity)ACT_BARNACLE_TASTE_SPIT );
  989. }
  990. #ifdef HL2_EPISODIC
  991. // if the object is a combatclass, send it a chomp interaction in case it wants to respond to that
  992. // in some nonstandard way.
  993. CBaseCombatCharacter *pBCC = dynamic_cast<CBaseCombatCharacter *>(pVictim);
  994. if( pBCC )
  995. {
  996. Vector tipPos = m_vecTip.Get();
  997. pBCC->DispatchInteraction( g_interactionBarnacleVictimBite, &tipPos, this );
  998. }
  999. #endif
  1000. }
  1001. else
  1002. {
  1003. // Necessary for good +use interactions
  1004. pVictim->AddEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  1005. // Pull the victim towards the mouth
  1006. PullEnemyTorwardsMouth( false );
  1007. }
  1008. }
  1009. //-----------------------------------------------------------------------------
  1010. // Purpose: Lift the prey stuck to our tongue up towards our mouth
  1011. //-----------------------------------------------------------------------------
  1012. void CNPC_Barnacle::LiftPrey( void )
  1013. {
  1014. CBaseEntity *pVictim = GetEnemy();
  1015. Assert( pVictim );
  1016. // Drop the prey if it's been obscured by something
  1017. trace_t tr;
  1018. AI_TraceLine( WorldSpaceCenter(), pVictim->WorldSpaceCenter(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  1019. bool bEnemyIsNPC = IsEnemyAnNPC() && !IsEnemyARagdoll();
  1020. if ( ( bEnemyIsNPC && !pVictim->IsAlive() ) || (tr.fraction < 1.0 && tr.m_pEnt != pVictim && tr.m_pEnt != m_hRagdoll) )
  1021. {
  1022. if ( !GetEnemy()->IsPlayer() )
  1023. {
  1024. // ignore the object so we don't get into a loop of trying to pick it up.
  1025. m_hLastSpitEnemy = GetEnemy();
  1026. }
  1027. LostPrey( false );
  1028. return;
  1029. }
  1030. // Height from the barnacle's origin to the point at which it bites
  1031. float flBiteZOffset = 60.0;
  1032. if ( IsEnemyAPlayer() )
  1033. {
  1034. LiftPlayer(flBiteZOffset);
  1035. }
  1036. else if ( IsEnemyARagdoll() )
  1037. {
  1038. LiftRagdoll(flBiteZOffset);
  1039. }
  1040. else if ( bEnemyIsNPC )
  1041. {
  1042. LiftNPC(flBiteZOffset);
  1043. }
  1044. else
  1045. {
  1046. LiftPhysicsObject(flBiteZOffset);
  1047. }
  1048. if ( m_hRagdoll )
  1049. {
  1050. QAngle newAngles( 0, m_hRagdoll->GetAbsAngles()[YAW], 0 );
  1051. Vector centerDelta = m_hRagdoll->WorldSpaceCenter() - GetEnemy()->WorldSpaceCenter();
  1052. Vector newOrigin = GetEnemy()->GetAbsOrigin() + centerDelta;
  1053. GetEnemy()->SetAbsOrigin( newOrigin );
  1054. GetEnemy()->SetAbsAngles( newAngles );
  1055. }
  1056. }
  1057. //-----------------------------------------------------------------------------
  1058. // Purpose: Attach a serverside ragdoll prop for the specified entity to our tongue
  1059. //-----------------------------------------------------------------------------
  1060. CRagdollProp *CNPC_Barnacle::AttachRagdollToTongue( CBaseAnimating *pAnimating )
  1061. {
  1062. // Find his head bone
  1063. m_iGrabbedBoneIndex = -1;
  1064. Vector vecNeckOffset;
  1065. if ( m_hTongueTip )
  1066. {
  1067. vecNeckOffset = (pAnimating->EyePosition() - m_hTongueTip->GetAbsOrigin());
  1068. }
  1069. CStudioHdr *pHdr = pAnimating->GetModelPtr();
  1070. if ( pHdr )
  1071. {
  1072. int set = pAnimating->GetHitboxSet();
  1073. for( int i = 0; i < pHdr->iHitboxCount(set); i++ )
  1074. {
  1075. mstudiobbox_t *pBox = pHdr->pHitbox( i, set );
  1076. if ( !pBox )
  1077. continue;
  1078. if ( pBox->group == HITGROUP_HEAD )
  1079. {
  1080. m_iGrabbedBoneIndex = pBox->bone;
  1081. break;
  1082. }
  1083. }
  1084. }
  1085. // HACK: Until we have correctly assigned hitgroups on our models, lookup the bones
  1086. // for the models that we know are in the barnacle maps.
  1087. //m_iGrabbedBoneIndex = pAnimating->LookupBone( "Bip01 L Foot" );
  1088. if ( m_iGrabbedBoneIndex == -1 )
  1089. {
  1090. // Citizens, Conscripts
  1091. m_iGrabbedBoneIndex = pAnimating->LookupBone( "Bip01 Head" );
  1092. }
  1093. if ( m_iGrabbedBoneIndex == -1 )
  1094. {
  1095. // Metrocops, Combine soldiers
  1096. m_iGrabbedBoneIndex = pAnimating->LookupBone( "ValveBiped.Bip01_Head1" );
  1097. }
  1098. if ( m_iGrabbedBoneIndex == -1 )
  1099. {
  1100. // Vortigaunts
  1101. m_iGrabbedBoneIndex = pAnimating->LookupBone( "ValveBiped.head" );
  1102. }
  1103. if ( m_iGrabbedBoneIndex == -1 )
  1104. {
  1105. // Bullsquids
  1106. m_iGrabbedBoneIndex = pAnimating->LookupBone( "Bullsquid.Head_Bone1" );
  1107. }
  1108. if ( m_iGrabbedBoneIndex == -1 )
  1109. {
  1110. // Just use the first bone
  1111. m_iGrabbedBoneIndex = 0;
  1112. }
  1113. // Move the tip to the bone
  1114. Vector vecBonePos;
  1115. QAngle vecBoneAngles;
  1116. pAnimating->GetBonePosition( m_iGrabbedBoneIndex, vecBonePos, vecBoneAngles );
  1117. if ( m_hTongueTip )
  1118. {
  1119. m_hTongueTip->Teleport( &vecBonePos, NULL, NULL );
  1120. }
  1121. //NDebugOverlay::Box( vecBonePos, -Vector(5,5,5), Vector(5,5,5), 255,255,255, 0, 10.0 );
  1122. // Create the ragdoll attached to tongue
  1123. IPhysicsObject *pTonguePhysObject = m_hTongueTip->VPhysicsGetObject();
  1124. CRagdollProp *pRagdoll = CreateServerRagdollAttached( pAnimating, vec3_origin, -1, COLLISION_GROUP_NONE, pTonguePhysObject, m_hTongueTip, 0, vecBonePos, m_iGrabbedBoneIndex, vec3_origin );
  1125. if ( pRagdoll )
  1126. {
  1127. #if HL2_EPISODIC
  1128. PhysEnableEntityCollisions( this, pAnimating );
  1129. PhysDisableEntityCollisions( this, pRagdoll );
  1130. #endif
  1131. pRagdoll->DisableAutoFade();
  1132. pRagdoll->SetThink( NULL );
  1133. }
  1134. return pRagdoll;
  1135. }
  1136. void CNPC_Barnacle::InputSetDropTongueSpeed( inputdata_t &inputdata )
  1137. {
  1138. m_flBarnaclePullSpeed = inputdata.value.Int();
  1139. }
  1140. void CNPC_Barnacle::InputDropTongue( inputdata_t &inputdata )
  1141. {
  1142. DropTongue();
  1143. }
  1144. //-----------------------------------------------------------------------------
  1145. // Purpose: Grab the specified target with our tongue
  1146. //-----------------------------------------------------------------------------
  1147. void CNPC_Barnacle::AttachTongueToTarget( CBaseEntity *pTouchEnt, Vector vecGrabPos )
  1148. {
  1149. #if HL2_EPISODIC
  1150. m_OnGrab.Set( pTouchEnt, this, this );
  1151. #endif
  1152. // Reset this valricue each time we attach prey. If it needs to be reduced, code below will do so.
  1153. m_flBarnaclePullSpeed = BARNACLE_PULL_SPEED;
  1154. if ( RandomFloat(0,1) > 0.5 )
  1155. {
  1156. EmitSound( "NPC_Barnacle.PullPant" );
  1157. }
  1158. else
  1159. {
  1160. EmitSound( "NPC_Barnacle.TongueStretch" );
  1161. }
  1162. SetActivity( (Activity)ACT_BARNACLE_SLURP );
  1163. // Get the player out of the vehicle he's in.
  1164. if ( pTouchEnt->IsPlayer() )
  1165. {
  1166. CBasePlayer *pPlayer = static_cast<CBasePlayer*>(pTouchEnt);
  1167. if ( pPlayer->IsInAVehicle() )
  1168. {
  1169. pPlayer->LeaveVehicle( pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles() );
  1170. // The player could have warped through the tongue while on a high-speed vehicle.
  1171. // Move him back under the barnacle.
  1172. Vector vecDelta;
  1173. VectorSubtract( pPlayer->GetAbsOrigin(), GetAbsOrigin(), vecDelta );
  1174. vecDelta.z = 0.0f;
  1175. float flDist = VectorNormalize( vecDelta );
  1176. if ( flDist > 20 )
  1177. {
  1178. Vector vecNewPos;
  1179. VectorMA( GetAbsOrigin(), 20, vecDelta, vecNewPos );
  1180. vecNewPos.z = pPlayer->GetAbsOrigin().z;
  1181. pPlayer->SetAbsOrigin( vecNewPos );
  1182. }
  1183. }
  1184. m_bPlayerWasStanding = ( ( pPlayer->GetFlags() & FL_DUCKING ) == 0 );
  1185. }
  1186. SetEnemy( pTouchEnt );
  1187. #if HL2_EPISODIC
  1188. // Disable collision between myself and the obejct I've seized.
  1189. PhysDisableEntityCollisions( this, pTouchEnt );
  1190. #endif
  1191. // teleporting the player in this way is illegitimate -- try it in third person to see the problem
  1192. if ( /* pTouchEnt->IsPlayer() || */ pTouchEnt->MyNPCPointer() )
  1193. {
  1194. Vector origin = GetAbsOrigin();
  1195. origin.z = pTouchEnt->GetAbsOrigin().z;
  1196. CTraceFilterSkipTwoEntities traceFilter( this, pTouchEnt, COLLISION_GROUP_NONE );
  1197. trace_t placementTrace;
  1198. UTIL_TraceHull( origin, origin, pTouchEnt->WorldAlignMins(), pTouchEnt->WorldAlignMaxs(), MASK_NPCSOLID, &traceFilter, &placementTrace );
  1199. if ( placementTrace.startsolid )
  1200. {
  1201. UTIL_TraceHull( origin + Vector(0, 0, 24), origin, pTouchEnt->WorldAlignMins(), pTouchEnt->WorldAlignMaxs(), MASK_NPCSOLID, &traceFilter, &placementTrace );
  1202. if ( !placementTrace.startsolid )
  1203. {
  1204. pTouchEnt->SetAbsOrigin( placementTrace.endpos );
  1205. // pTouchEnt->Teleport( &placementTrace.endpos, NULL, NULL );
  1206. }
  1207. }
  1208. else
  1209. {
  1210. pTouchEnt->SetAbsOrigin( origin );
  1211. // pTouchEnt->Teleport( &origin, NULL, NULL );
  1212. }
  1213. }
  1214. m_nShakeCount = 6;
  1215. m_bLiftingPrey = true;// indicate that we should be lifting prey.
  1216. SetAltitude( (GetAbsOrigin().z - vecGrabPos.z) );
  1217. m_bPlayedPullSound = false;
  1218. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(pTouchEnt);
  1219. if ( IsEnemyAPlayer() || IsEnemyAPhysicsObject() )
  1220. {
  1221. // The player (and phys objects) doesn't ragdoll, so just grab him and pull him up manually
  1222. IPhysicsObject *pPlayerPhys = pTouchEnt->VPhysicsGetObject();
  1223. IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
  1224. Vector vecGrabPos;
  1225. if ( pTouchEnt->IsPlayer() )
  1226. {
  1227. vecGrabPos = pTouchEnt->EyePosition();
  1228. #if BARNACLE_USE_TONGUE_OFFSET
  1229. VectorRotate( m_svPlayerHeldTipOffset, pTouchEnt->EntityToWorldTransform(), m_vecTipDrawOffset.GetForModify() );
  1230. m_vecTipDrawOffset.GetForModify().z = m_svPlayerHeldTipOffset.z;
  1231. #endif
  1232. // pTonguePhys->GetPosition(&vecGrabPos,NULL);
  1233. }
  1234. else
  1235. {
  1236. VectorSubtract( m_vecTip, pTouchEnt->GetAbsOrigin(), vecGrabPos );
  1237. VectorNormalize( vecGrabPos );
  1238. vecGrabPos = physcollision->CollideGetExtent( pPlayerPhys->GetCollide(), pTouchEnt->GetAbsOrigin(), pTouchEnt->GetAbsAngles(), vecGrabPos );
  1239. #if BARNACLE_USE_TONGUE_OFFSET
  1240. m_vecTipDrawOffset.GetForModify().Zero();
  1241. #endif
  1242. }
  1243. m_hTongueTip->Teleport( &vecGrabPos, NULL, NULL );
  1244. float flDist = (vecGrabPos - GetAbsOrigin() ).Length();
  1245. float flTime = flDist / m_flBarnaclePullSpeed;
  1246. // If this object would be pulled in too quickly, change the pull speed.
  1247. if( flTime < BARNACLE_MIN_PULL_TIME )
  1248. {
  1249. m_flBarnaclePullSpeed = flDist / BARNACLE_MIN_PULL_TIME;
  1250. }
  1251. constraint_fixedparams_t fixed;
  1252. fixed.Defaults();
  1253. fixed.InitWithCurrentObjectState( pTonguePhys, pPlayerPhys );
  1254. fixed.constraint.Defaults();
  1255. /*
  1256. You can use this stanza to try to counterplace the constraint on the player's head so he gets hauled sideways to the right place on the barnacle, but it is better to just move the tongue before attachment.
  1257. if ( IsEnemyAPlayer() )
  1258. {
  1259. Vector2D vToCenter = GetAbsOrigin().AsVector2D() - pTouchEnt->EyePosition().AsVector2D();
  1260. fixed.attachedRefXform[0][3] -= vToCenter.x ;
  1261. fixed.attachedRefXform[1][3] -= vToCenter.y ;
  1262. }
  1263. */
  1264. m_pConstraint = physenv->CreateFixedConstraint( pTonguePhys, pPlayerPhys, NULL, fixed );
  1265. // Increase the tongue's spring constant while lifting
  1266. m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LIFTING );
  1267. UpdateTongue();
  1268. return;
  1269. }
  1270. // NPC case...
  1271. pAnimating->InvalidateBoneCache();
  1272. // Make a ragdoll for the guy, and hide him.
  1273. pTouchEnt->AddSolidFlags( FSOLID_NOT_SOLID );
  1274. m_hRagdoll = AttachRagdollToTongue( pAnimating );
  1275. m_hRagdoll->SetDamageEntity( pAnimating );
  1276. // Make it try to blend out of ragdoll on the client on deletion
  1277. // NOTE: This isn't fully implemented, so disable
  1278. //m_hRagdoll->SetUnragdoll( pAnimating );
  1279. // Apply the target's current velocity to each of the ragdoll's bones
  1280. Vector vecVelocity = pAnimating->GetGroundSpeedVelocity() * 0.5;
  1281. ragdoll_t *pRagdoll = m_hRagdoll->GetRagdoll();
  1282. // barnacle might let go if ragdoll is separated - so increase the separation checking a bit
  1283. constraint_groupparams_t params;
  1284. pRagdoll->pGroup->GetErrorParams( &params );
  1285. params.minErrorTicks = MIN( params.minErrorTicks, 5 );
  1286. pRagdoll->pGroup->SetErrorParams( params );
  1287. for ( int i = 0; i < pRagdoll->listCount; i++ )
  1288. {
  1289. pRagdoll->list[i].pObject->AddVelocity( &vecVelocity, NULL );
  1290. }
  1291. if ( npc_barnacle_swallow.GetBool() )
  1292. {
  1293. m_hRagdoll->SetOverlaySequence( ACT_GESTURE_BARNACLE_STRANGLE );
  1294. m_hRagdoll->SetBlendWeight( 1.0f );
  1295. }
  1296. // Now hide the actual enemy
  1297. pTouchEnt->AddEffects( EF_NODRAW );
  1298. // Increase the tongue's spring constant while lifting
  1299. m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LIFTING );
  1300. UpdateTongue();
  1301. // Store off the current bone matrix so we have it next frame
  1302. pAnimating->SetupBones( m_pRagdollBones, BONE_USED_BY_ANYTHING );
  1303. }
  1304. //-----------------------------------------------------------------------------
  1305. // Spit out the prey; add physics force!
  1306. //-----------------------------------------------------------------------------
  1307. void CNPC_Barnacle::SpitPrey()
  1308. {
  1309. if ( GetEnemy() )
  1310. {
  1311. IPhysicsObject *pObject = GetEnemy()->VPhysicsGetObject();
  1312. if (pObject)
  1313. {
  1314. Vector vecPosition, force;
  1315. GetAttachment( m_nSpitAttachment, vecPosition, &force );
  1316. force *= pObject->GetMass() * 50.0f;
  1317. pObject->ApplyForceOffset( force, vec3_origin );
  1318. }
  1319. m_hLastSpitEnemy = GetEnemy();
  1320. }
  1321. LostPrey( false );
  1322. }
  1323. //-----------------------------------------------------------------------------
  1324. // Purpose: Prey is in position, bite them and start swallowing them
  1325. //-----------------------------------------------------------------------------
  1326. void CNPC_Barnacle::BitePrey( void )
  1327. {
  1328. Assert( GetEnemy() );
  1329. CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer();
  1330. #ifdef HL2_EPISODIC
  1331. if ( pVictim == NULL )
  1332. {
  1333. if ( GetEnemy() )
  1334. {
  1335. CBounceBomb *pBounce = dynamic_cast<CBounceBomb *>( GetEnemy() );
  1336. if ( pBounce )
  1337. {
  1338. // Stop the ragdoll moving and start to pull the sucker up into our mouth
  1339. m_bSwallowingPrey = true;
  1340. m_bSwallowingBomb = true;
  1341. IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
  1342. // Stop the tongue's spring getting in the way of swallowing
  1343. m_hTongueTip->m_pSpring->SetSpringConstant( 0 );
  1344. // Switch the tongue tip to shadow and drag it up
  1345. pTonguePhys->SetShadow( 1e4, 1e4, false, false );
  1346. pTonguePhys->UpdateShadow( m_hTongueTip->GetAbsOrigin(), m_hTongueTip->GetAbsAngles(), false, 0 );
  1347. m_hTongueTip->SetMoveType( MOVETYPE_NOCLIP );
  1348. m_hTongueTip->SetAbsVelocity( Vector(0,0,32) );
  1349. SetAltitude( (GetAbsOrigin().z - m_hTongueTip->GetAbsOrigin().z) );
  1350. }
  1351. }
  1352. return;
  1353. }
  1354. #endif
  1355. Assert( pVictim );
  1356. if ( !pVictim )
  1357. {
  1358. return;
  1359. }
  1360. EmitSound( "NPC_Barnacle.FinalBite" );
  1361. m_flVictimHeight = GetEnemy()->WorldAlignSize().z;
  1362. // Kill the victim instantly
  1363. int iDamageType = DMG_SLASH | DMG_ALWAYSGIB;
  1364. int nDamage;
  1365. if ( !pVictim->IsPlayer() )
  1366. {
  1367. iDamageType |= DMG_ALWAYSGIB;
  1368. nDamage = pVictim->m_iHealth;
  1369. }
  1370. else
  1371. {
  1372. nDamage = BARNACLE_BITE_DAMAGE_TO_PLAYER;
  1373. }
  1374. if ( m_hRagdoll )
  1375. {
  1376. // We've got a ragdoll, so prevent this creating another one
  1377. iDamageType |= DMG_REMOVENORAGDOLL;
  1378. m_hRagdoll->SetDamageEntity( NULL );
  1379. }
  1380. #if HL2_EPISODIC
  1381. m_bSwallowingPoison = IsPoisonous(pVictim);
  1382. unsigned int enemyClass = GetEnemy()->Classify();
  1383. #endif
  1384. // DMG_CRUSH because we don't wan't to impart physics forces
  1385. pVictim->TakeDamage( CTakeDamageInfo( this, this, nDamage, iDamageType | DMG_CRUSH ) );
  1386. m_cGibs = 3;
  1387. // In episodic, bite the zombie's headcrab off & drop the body
  1388. #ifdef HL2_EPISODIC
  1389. if ( enemyClass == CLASS_ZOMBIE )
  1390. {
  1391. if ( m_hRagdoll )
  1392. {
  1393. m_hRagdoll->SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, false );
  1394. DetachAttachedRagdoll( m_hRagdoll );
  1395. m_hLastSpitEnemy = m_hRagdoll.Get();
  1396. m_hRagdoll->EmitSound( "NPC_HeadCrab.Die" );
  1397. m_hRagdoll = NULL;
  1398. }
  1399. // Create some blood to hide the vanishing headcrab
  1400. Vector vecBloodPos;
  1401. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecBloodPos );
  1402. UTIL_BloodSpray( vecBloodPos, Vector(0,0,-1), GetEnemy()->BloodColor(), 8, FX_BLOODSPRAY_ALL );
  1403. m_flDigestFinish = gpGlobals->curtime + 10.0;
  1404. return;
  1405. }
  1406. // in episodic, where barnacles can eat antlions, vanish the ragdoll because the gibs will spray everywhere
  1407. // and hide it.
  1408. if ( enemyClass == CLASS_ANTLION )
  1409. {
  1410. #ifndef _XBOX
  1411. m_nBloodColor = pVictim->BloodColor();
  1412. #endif
  1413. m_flNextBloodTime = 0.0f;
  1414. SprayBlood();
  1415. m_flDigestFinish = gpGlobals->curtime + 10.0;
  1416. if (m_hRagdoll)
  1417. {
  1418. UTIL_Remove( m_hRagdoll );
  1419. }
  1420. if ( m_bSwallowingPoison )
  1421. { // hurt me
  1422. TakeDamage( CTakeDamageInfo( this, this, m_iHealth, DMG_ACID ) );
  1423. }
  1424. return;
  1425. }
  1426. #endif
  1427. // Players are never swallowed, nor is anything we don't have a ragdoll for
  1428. if ( !m_hRagdoll || pVictim->IsPlayer() )
  1429. {
  1430. if ( !pVictim->IsPlayer() || pVictim->GetHealth() <= 0 )
  1431. {
  1432. LostPrey( false );
  1433. }
  1434. return;
  1435. }
  1436. // Stop the ragdoll moving and start to pull the sucker up into our mouth
  1437. m_bSwallowingPrey = true;
  1438. IPhysicsObject *pTonguePhys = m_hTongueTip->VPhysicsGetObject();
  1439. // Make it nonsolid to the world so we can pull it through the roof
  1440. PhysDisableEntityCollisions( m_hRagdoll->VPhysicsGetObject(), g_PhysWorldObject );
  1441. // Stop the tongue's spring getting in the way of swallowing
  1442. m_hTongueTip->m_pSpring->SetSpringConstant( 0 );
  1443. // Switch the tongue tip to shadow and drag it up
  1444. pTonguePhys->SetShadow( 1e4, 1e4, false, false );
  1445. pTonguePhys->UpdateShadow( m_hTongueTip->GetAbsOrigin(), m_hTongueTip->GetAbsAngles(), false, 0 );
  1446. m_hTongueTip->SetMoveType( MOVETYPE_NOCLIP );
  1447. m_hTongueTip->SetAbsVelocity( Vector(0,0,32) );
  1448. SetAltitude( (GetAbsOrigin().z - m_hTongueTip->GetAbsOrigin().z) );
  1449. if ( !npc_barnacle_swallow.GetBool() )
  1450. return;
  1451. // Because the victim is dead, remember the blood color
  1452. m_flNextBloodTime = 0.0f;
  1453. // NOTE: This was too confusing to people with the more recognizable blood -- jdw
  1454. #ifndef _XBOX
  1455. m_nBloodColor = pVictim->BloodColor();
  1456. #endif
  1457. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &m_vecBloodPos );
  1458. // m_hRagdoll->SetOverlaySequence( ACT_DIE_BARNACLE_SWALLOW );
  1459. m_hRagdoll->SetBlendWeight( 0.0f );
  1460. SprayBlood();
  1461. }
  1462. //-----------------------------------------------------------------------------
  1463. // Purpose:
  1464. //-----------------------------------------------------------------------------
  1465. void CNPC_Barnacle::SprayBlood()
  1466. {
  1467. if ( gpGlobals->curtime < m_flNextBloodTime )
  1468. return;
  1469. m_flNextBloodTime = gpGlobals->curtime + 0.2f;
  1470. Vector bloodDir = RandomVector( -1.0f, 1.0f );
  1471. bloodDir.z = -fabs( bloodDir.z );
  1472. Vector jitterPos = RandomVector( -8, 8 );
  1473. jitterPos.z = 0.0f;
  1474. #ifndef _XBOX
  1475. UTIL_BloodSpray( m_vecBloodPos + jitterPos, Vector( 0,0,-1),
  1476. m_nBloodColor, RandomInt( 4, 8 ), RandomInt(0,2) == 0 ? FX_BLOODSPRAY_ALL : FX_BLOODSPRAY_CLOUD );
  1477. #else
  1478. UTIL_BloodSpray( m_vecBloodPos + jitterPos, Vector( 0,0,-1),
  1479. BLOOD_COLOR_YELLOW, RandomInt( 4, 8 ), RandomInt(0,2) == 0 ? FX_BLOODSPRAY_ALL : FX_BLOODSPRAY_CLOUD );
  1480. #endif
  1481. }
  1482. //-----------------------------------------------------------------------------
  1483. // Purpose: Slowly swallow the prey whole. Only used on humanoids.
  1484. //-----------------------------------------------------------------------------
  1485. void CNPC_Barnacle::SwallowPrey( void )
  1486. {
  1487. if ( IsActivityFinished() )
  1488. {
  1489. if (GetActivity() == ACT_BARNACLE_BITE_HUMAN )
  1490. {
  1491. SetActivity( (Activity)ACT_BARNACLE_CHEW_HUMAN );
  1492. }
  1493. else
  1494. {
  1495. SetActivity( (Activity)ACT_BARNACLE_CHEW_SMALL_THINGS );
  1496. }
  1497. }
  1498. // Move the body up slowly
  1499. Vector vecSwallowPos = m_hTongueTip->GetAbsOrigin();
  1500. vecSwallowPos.z -= m_flVictimHeight;
  1501. //NDebugOverlay::Box( vecSwallowPos, -Vector(5,5,5), Vector(5,5,5), 255,255,255, 0, 0.1 );
  1502. // bite prey every once in a while
  1503. if ( random->RandomInt(0,25) == 0 )
  1504. {
  1505. EmitSound( "NPC_Barnacle.Digest" );
  1506. }
  1507. // Fully swallowed it?
  1508. float flDistanceToGo = GetAbsOrigin().z - vecSwallowPos.z;
  1509. if ( flDistanceToGo <= 0 )
  1510. {
  1511. // He's dead jim
  1512. m_bSwallowingPrey = false;
  1513. m_hTongueTip->SetAbsVelocity( vec3_origin );
  1514. #if HL2_EPISODIC
  1515. // digest poisonous things for just a moment before being killed by them (it looks wierd if it's instant)
  1516. // Parentheses were probably intended around the ?: part of the expression, but putting them there now
  1517. // would change the behavior which is undesirable, so parentheses were placed around the '+' to suppress
  1518. // compiler warnings.
  1519. m_flDigestFinish = ( gpGlobals->curtime + m_bSwallowingPoison ) ? 0.48f : 10.0f;
  1520. #else
  1521. m_flDigestFinish = gpGlobals->curtime + 10.0;
  1522. #endif
  1523. }
  1524. if ( npc_barnacle_swallow.GetBool() )
  1525. {
  1526. SprayBlood();
  1527. }
  1528. }
  1529. //-----------------------------------------------------------------------------
  1530. // Purpose: Remove the fake ragdoll and bring the actual enemy back in view
  1531. //-----------------------------------------------------------------------------
  1532. void CNPC_Barnacle::RemoveRagdoll( bool bDestroyRagdoll )
  1533. {
  1534. // Destroy the tongue tip constraint
  1535. if ( m_pConstraint )
  1536. {
  1537. physenv->DestroyConstraint( m_pConstraint );
  1538. m_pConstraint = NULL;
  1539. }
  1540. // Remove the ragdoll
  1541. if ( m_hRagdoll )
  1542. {
  1543. // Only destroy the ragdoll if told to. We might be just dropping
  1544. // the ragdoll because the target was killed on the way up.
  1545. m_hRagdoll->SetDamageEntity( NULL );
  1546. if ( npc_barnacle_swallow.GetBool() )
  1547. {
  1548. m_hRagdoll->SetThink( NULL );
  1549. m_hRagdoll->SetBlendWeight( 1.0f );
  1550. }
  1551. DetachAttachedRagdoll( m_hRagdoll );
  1552. if ( bDestroyRagdoll )
  1553. {
  1554. UTIL_Remove( m_hRagdoll );
  1555. }
  1556. m_hRagdoll = NULL;
  1557. // Reduce the spring constant while we lower
  1558. m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LOWERING );
  1559. // Unhide the enemy
  1560. if ( GetEnemy() )
  1561. {
  1562. GetEnemy()->RemoveEffects( EF_NODRAW );
  1563. GetEnemy()->RemoveSolidFlags( FSOLID_NOT_SOLID );
  1564. }
  1565. }
  1566. }
  1567. //-----------------------------------------------------------------------------
  1568. // Purpose: For some reason (he was killed, etc) we lost the prey we were dragging towards our mouth.
  1569. //-----------------------------------------------------------------------------
  1570. void CNPC_Barnacle::LostPrey( bool bRemoveRagdoll )
  1571. {
  1572. #if HL2_EPISODIC
  1573. m_OnRelease.Set( GetEnemy(), this, this );
  1574. #endif
  1575. CBaseEntity * const pEnemy = GetEnemy();
  1576. if ( pEnemy )
  1577. {
  1578. #if HL2_EPISODIC
  1579. PhysEnableEntityCollisions( this, pEnemy );
  1580. #endif
  1581. //No one survives being snatched by a barnacle anymore, so leave
  1582. // this flag set so that their entity gets removed.
  1583. //GetEnemy()->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  1584. CBaseCombatCharacter *pVictim = GetEnemyCombatCharacterPointer();
  1585. if ( pVictim )
  1586. {
  1587. pVictim->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
  1588. pVictim->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  1589. if ( m_hRagdoll )
  1590. {
  1591. QAngle newAngles( 0, m_hRagdoll->GetAbsAngles()[ YAW ], 0 );
  1592. Vector centerDelta = m_hRagdoll->WorldSpaceCenter() - pEnemy->WorldSpaceCenter();
  1593. Vector newOrigin = pEnemy->GetAbsOrigin() + centerDelta;
  1594. pEnemy->SetAbsOrigin( newOrigin );
  1595. pVictim->SetAbsAngles( newAngles );
  1596. }
  1597. pVictim->SetGroundEntity( NULL );
  1598. }
  1599. else if ( IsEnemyAPhysicsObject() )
  1600. {
  1601. // If we're a physics object, then we need to clear this flag
  1602. pEnemy->RemoveEFlags( EFL_IS_BEING_LIFTED_BY_BARNACLE );
  1603. }
  1604. }
  1605. RemoveRagdoll( bRemoveRagdoll );
  1606. m_bLiftingPrey = false;
  1607. m_bSwallowingPrey = false;
  1608. #if HL2_EPISODIC
  1609. m_bSwallowingPoison = false;
  1610. #endif
  1611. SetEnemy( NULL );
  1612. m_vecTipDrawOffset.GetForModify().Zero();
  1613. if ( m_hTongueTip )
  1614. {
  1615. // Remove our tongue's shadow object, in case we just finished swallowing something
  1616. IPhysicsObject *pPhysicsObject = m_hTongueTip->VPhysicsGetObject();
  1617. if ( pPhysicsObject && pPhysicsObject->GetShadowController() )
  1618. {
  1619. Vector vecCenter = WorldSpaceCenter();
  1620. m_hTongueTip->Teleport( &vecCenter, NULL, &vec3_origin );
  1621. // Reduce the spring constant while we lower
  1622. m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_LOWERING );
  1623. // Start colliding with the world again
  1624. pPhysicsObject->RemoveShadowController();
  1625. m_hTongueTip->SetMoveType( MOVETYPE_VPHYSICS );
  1626. pPhysicsObject->EnableMotion( true );
  1627. pPhysicsObject->EnableGravity( true );
  1628. pPhysicsObject->RecheckCollisionFilter();
  1629. }
  1630. }
  1631. }
  1632. //-----------------------------------------------------------------------------
  1633. // The tongue's vphysics updated
  1634. //-----------------------------------------------------------------------------
  1635. void CNPC_Barnacle::OnTongueTipUpdated()
  1636. {
  1637. // Update the tip's position
  1638. const Vector &vecNewTip = m_hTongueTip->GetAbsOrigin();
  1639. if ( vecNewTip != m_vecTip )
  1640. {
  1641. m_vecTip = vecNewTip;
  1642. CollisionProp()->MarkSurroundingBoundsDirty();
  1643. }
  1644. }
  1645. //-----------------------------------------------------------------------------
  1646. // Purpose: Update the positions of the tongue points
  1647. //-----------------------------------------------------------------------------
  1648. void CNPC_Barnacle::UpdateTongue( void )
  1649. {
  1650. if ( m_hTongueTip == NULL )
  1651. return;
  1652. // Set the spring's length to that of the tongue's extension
  1653. // Compute the rest length of the tongue based on the spring.
  1654. // This occurs when mg == kx or x = mg/k
  1655. float flRestStretch = (BARNACLE_TONGUE_TIP_MASS * GetCurrentGravity()) / BARNACLE_TONGUE_SPRING_CONSTANT_HANGING;
  1656. // FIXME: HACK!!!! The code above doesn't quite make the tip end up in the right place.
  1657. // but it should. So, we're gonna hack it the rest of the way.
  1658. flRestStretch += 4;
  1659. m_hTongueTip->m_pSpring->SetSpringLength( m_flAltitude - flRestStretch );
  1660. }
  1661. //-----------------------------------------------------------------------------
  1662. // Purpose:
  1663. //-----------------------------------------------------------------------------
  1664. void CNPC_Barnacle::SpawnDeathGibs( void )
  1665. {
  1666. bool bDroppedAny = false;
  1667. // Drop a random number of gibs
  1668. for ( int i=0; i < ARRAYSIZE(m_szGibNames); i++ )
  1669. {
  1670. if ( random->RandomInt( 0, 1 ) )
  1671. {
  1672. CGib::SpawnSpecificGibs( this, 1, 32, 1, m_szGibNames[i] );
  1673. bDroppedAny = true;
  1674. }
  1675. }
  1676. // Make sure we at least drop something
  1677. if ( bDroppedAny == false )
  1678. {
  1679. CGib::SpawnSpecificGibs( this, 1, 32, 1, m_szGibNames[0] );
  1680. }
  1681. }
  1682. //-----------------------------------------------------------------------------
  1683. // Purpose:
  1684. //-----------------------------------------------------------------------------
  1685. void CNPC_Barnacle::Event_Killed( const CTakeDamageInfo &info )
  1686. {
  1687. m_OnDeath.FireOutput( info.GetAttacker(), this );
  1688. SendOnKilledGameEvent( info );
  1689. AddSolidFlags( FSOLID_NOT_SOLID );
  1690. m_takedamage = DAMAGE_NO;
  1691. m_lifeState = LIFE_DYING;
  1692. // Are we lifting prey?
  1693. if ( GetEnemy() )
  1694. {
  1695. // Cleanup
  1696. LostPrey( false );
  1697. }
  1698. else if ( m_bSwallowingPrey && m_hRagdoll )
  1699. {
  1700. // We're swallowing a body. Make it stick inside us.
  1701. m_hTongueTip->SetAbsVelocity( vec3_origin );
  1702. m_hRagdoll->StopFollowingEntity();
  1703. m_hRagdoll->SetMoveType( MOVETYPE_VPHYSICS );
  1704. m_hRagdoll->SetAbsOrigin( m_hTongueTip->GetAbsOrigin() );
  1705. m_hRagdoll->RemoveSolidFlags( FSOLID_NOT_SOLID );
  1706. m_hRagdoll->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  1707. m_hRagdoll->RecheckCollisionFilter();
  1708. if ( npc_barnacle_swallow.GetBool() )
  1709. {
  1710. m_hRagdoll->SetThink( NULL );
  1711. m_hRagdoll->SetBlendWeight( 1.0f );
  1712. }
  1713. }
  1714. else
  1715. {
  1716. // Destroy the ragdoll->tongue tip constraint
  1717. if ( m_pConstraint )
  1718. {
  1719. physenv->DestroyConstraint( m_pConstraint );
  1720. m_pConstraint = NULL;
  1721. }
  1722. LostPrey( true );
  1723. }
  1724. // Puke gibs unless we're told to be cheap
  1725. bool spawnGibs = ( !HasSpawnFlags( SF_BARNACLE_CHEAP_DEATH ) || random->RandomInt( 0, 1 ) );
  1726. if ( spawnGibs )
  1727. {
  1728. SpawnDeathGibs();
  1729. }
  1730. // Puke blood
  1731. #ifdef _XBOX
  1732. UTIL_BloodSpray( GetAbsOrigin(), Vector(0,0,-1), BLOOD_COLOR_YELLOW, 8, FX_BLOODSPRAY_ALL );
  1733. #else
  1734. UTIL_BloodSpray( GetAbsOrigin(), Vector(0,0,-1), BLOOD_COLOR_RED, 8, FX_BLOODSPRAY_ALL );
  1735. #endif
  1736. // Put blood on the ground if near enough
  1737. trace_t bloodTrace;
  1738. AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 256 ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &bloodTrace);
  1739. if ( bloodTrace.fraction < 1.0f )
  1740. {
  1741. #ifdef _XBOX
  1742. UTIL_BloodDecalTrace( &bloodTrace, BLOOD_COLOR_YELLOW );
  1743. #else
  1744. UTIL_BloodDecalTrace( &bloodTrace, BLOOD_COLOR_RED );
  1745. #endif
  1746. }
  1747. EmitSound( "NPC_Barnacle.Die" );
  1748. SetActivity( ACT_DIESIMPLE );
  1749. StudioFrameAdvance();
  1750. SetNextThink( gpGlobals->curtime + 0.1f );
  1751. SetThink ( &CNPC_Barnacle::WaitTillDead );
  1752. // we deliberately do not call BaseClass::EventKilled
  1753. }
  1754. //-----------------------------------------------------------------------------
  1755. // Purpose:
  1756. //-----------------------------------------------------------------------------
  1757. void CNPC_Barnacle::WaitTillDead ( void )
  1758. {
  1759. SetNextThink( gpGlobals->curtime + 0.1f );
  1760. StudioFrameAdvance();
  1761. DispatchAnimEvents ( this );
  1762. if ( IsActivityFinished() )
  1763. {
  1764. // death anim finished.
  1765. StopAnimation();
  1766. }
  1767. float goalAltitude = BARNACLE_DEAD_TONGUE_ALTITUDE;
  1768. trace_t tr;
  1769. AI_TraceLine( m_vecRoot.Get(), m_vecRoot.Get() - Vector( 0, 0, 256 ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1770. if ( tr.fraction < 1.0 )
  1771. {
  1772. float distToFloor = ( m_vecRoot.Get() - tr.endpos ).Length();
  1773. float clearance = distToFloor - goalAltitude;
  1774. if ( clearance < BARNACLE_MIN_DEAD_TONGUE_CLEARANCE )
  1775. {
  1776. if ( distToFloor - BARNACLE_MIN_DEAD_TONGUE_CLEARANCE > distToFloor * .5 )
  1777. {
  1778. goalAltitude = distToFloor - BARNACLE_MIN_DEAD_TONGUE_CLEARANCE;
  1779. }
  1780. else
  1781. {
  1782. goalAltitude = distToFloor * .5;
  1783. }
  1784. }
  1785. }
  1786. // Keep moving the tongue to its dead position
  1787. // FIXME: This stupid algorithm is necessary because
  1788. // I can't seem to get reproduceable behavior from springs
  1789. bool bTongueInPosition = false;
  1790. float flDist = m_vecRoot.Get().z - m_vecTip.Get().z;
  1791. if ( fabs(flDist - goalAltitude) > 20.0f )
  1792. {
  1793. float flNewAltitude;
  1794. float dt = gpGlobals->curtime - GetLastThink();
  1795. if ( m_flAltitude >= goalAltitude )
  1796. {
  1797. flNewAltitude = MAX( goalAltitude, m_flAltitude - m_flBarnaclePullSpeed * dt );
  1798. }
  1799. else
  1800. {
  1801. flNewAltitude = MIN( goalAltitude, m_flAltitude + m_flBarnaclePullSpeed * dt );
  1802. }
  1803. SetAltitude( flNewAltitude );
  1804. }
  1805. else
  1806. {
  1807. // Wait for settling...
  1808. IPhysicsObject *pTipObject = m_hTongueTip->VPhysicsGetObject();
  1809. Vector vecVelocity;
  1810. AngularImpulse angVel;
  1811. pTipObject->GetVelocity( &vecVelocity, &angVel );
  1812. if ( vecVelocity.LengthSqr() < 1.0f )
  1813. {
  1814. // We may need to have a heavier spring constant until we settle
  1815. // to avoid strange looking rest conditions (when the tongue is really bent from
  1816. // picking up a barrel, it looks strange to switch to the hanging constant)
  1817. m_hTongueTip->m_pSpring->SetSpringConstant( BARNACLE_TONGUE_SPRING_CONSTANT_HANGING );
  1818. if ( fabs(flDist - goalAltitude) > 1.0f )
  1819. {
  1820. float flSign = ( flDist > goalAltitude ) ? -1.0f : 1.0f;
  1821. SetAltitude( m_flAltitude + flSign );
  1822. }
  1823. else if ( vecVelocity.LengthSqr() < 0.01f )
  1824. {
  1825. bTongueInPosition = ( fabs(flDist - goalAltitude) <= 1.0f );
  1826. }
  1827. }
  1828. }
  1829. if ( IsActivityFinished() && bTongueInPosition )
  1830. {
  1831. // Remove our tongue pieces
  1832. UTIL_Remove( m_hTongueTip );
  1833. UTIL_Remove( m_hTongueRoot );
  1834. m_hTongueTip = NULL;
  1835. m_hTongueRoot = NULL;
  1836. SetThink ( NULL );
  1837. m_lifeState = LIFE_DEAD;
  1838. }
  1839. else
  1840. {
  1841. UpdateTongue();
  1842. }
  1843. }
  1844. #if HL2_EPISODIC
  1845. //=========================================================
  1846. // Some creatures are poisonous to barnacles, and the barnacle
  1847. // will die after consuming them. This determines if a given
  1848. // entity is one of those things.
  1849. // todo: could be a bit faster
  1850. //=========================================================
  1851. bool CNPC_Barnacle::IsPoisonous( CBaseEntity *pVictim )
  1852. {
  1853. if (!pVictim)
  1854. return false;
  1855. if ( FClassnameIs(pVictim,"npc_headcrab_poison") )
  1856. return true;
  1857. if ( FClassnameIs(pVictim,"npc_headcrab_black") )
  1858. return true;
  1859. if ( FClassnameIs(pVictim,"npc_antlion") &&
  1860. static_cast<CNPC_Antlion *>(pVictim)->IsWorker()
  1861. )
  1862. return true;
  1863. return false;
  1864. }
  1865. //=========================================================
  1866. // script input to immediately abandon whatever I am lifting
  1867. //=========================================================
  1868. void CNPC_Barnacle::InputLetGo( inputdata_t &inputdata )
  1869. {
  1870. if ( GetEnemy() )
  1871. {
  1872. if ( !GetEnemy()->IsPlayer() )
  1873. {
  1874. // ignore the object so we don't get into a loop of trying to pick it up.
  1875. m_hLastSpitEnemy = GetEnemy();
  1876. }
  1877. LostPrey( false );
  1878. }
  1879. }
  1880. // Barnacle has custom impact damage tables, so it can take grave damage from sawblades.
  1881. static impactentry_t barnacleLinearTable[] =
  1882. {
  1883. { 150*150, 5 },
  1884. { 250*250, 10 },
  1885. { 350*350, 50 },
  1886. { 500*500, 100 },
  1887. { 1000*1000, 500 },
  1888. };
  1889. static impactentry_t barnacleAngularTable[] =
  1890. {
  1891. { 100*100, 35 }, // Sawblade always kills.
  1892. { 200*200, 50 },
  1893. { 250*250, 500 },
  1894. };
  1895. static impactdamagetable_t gBarnacleImpactDamageTable =
  1896. {
  1897. barnacleLinearTable,
  1898. barnacleAngularTable,
  1899. ARRAYSIZE(barnacleLinearTable),
  1900. ARRAYSIZE(barnacleAngularTable),
  1901. 24*24, // minimum linear speed squared
  1902. 360*360, // minimum angular speed squared (360 deg/s to cause spin/slice damage)
  1903. 2, // can't take damage from anything under 2kg
  1904. 5, // anything less than 5kg is "small"
  1905. 5, // never take more than 5 pts of damage from anything under 5kg
  1906. 36*36, // <5kg objects must go faster than 36 in/s to do damage
  1907. VPHYSICS_LARGE_OBJECT_MASS, // large mass in kg
  1908. 4, // large mass scale (anything over 500kg does 4X as much energy to read from damage table)
  1909. 5, // large mass falling scale (emphasize falling/crushing damage over sideways impacts since the stress will kill you anyway)
  1910. 0.0f, // min vel
  1911. };
  1912. const impactdamagetable_t &CNPC_Barnacle::GetPhysicsImpactDamageTable( void )
  1913. {
  1914. return gBarnacleImpactDamageTable;
  1915. }
  1916. #endif
  1917. //=========================================================
  1918. // Precache - precaches all resources this monster needs
  1919. //=========================================================
  1920. void CNPC_Barnacle::Precache()
  1921. {
  1922. PrecacheModel("models/barnacle.mdl");
  1923. // Precache all gibs
  1924. for ( int i=0; i < ARRAYSIZE(m_szGibNames); i++ )
  1925. {
  1926. PrecacheModel( m_szGibNames[i] );
  1927. }
  1928. PrecacheScriptSound( "NPC_Barnacle.Digest" );
  1929. PrecacheScriptSound( "NPC_Barnacle.Scream" );
  1930. PrecacheScriptSound( "NPC_Barnacle.PullPant" );
  1931. PrecacheScriptSound( "NPC_Barnacle.TongueStretch" );
  1932. PrecacheScriptSound( "NPC_Barnacle.FinalBite" );
  1933. PrecacheScriptSound( "NPC_Barnacle.Die" );
  1934. PrecacheScriptSound( "NPC_Barnacle.BreakNeck" );
  1935. PrecacheModel( "models/props_junk/rock001a.mdl" );
  1936. BaseClass::Precache();
  1937. }
  1938. //=========================================================
  1939. // TongueTouchEnt - does a trace along the barnacle's tongue
  1940. // to see if any entity is touching it. Also stores the length
  1941. // of the trace in the int pointer provided.
  1942. //=========================================================
  1943. // enumerate entities that match a set of edict flags into a static array
  1944. class CTongueEntitiesEnum : public IPartitionEnumerator
  1945. {
  1946. public:
  1947. CTongueEntitiesEnum( CBaseEntity **pList, int listMax );
  1948. // This gets called by the enumeration methods with each element
  1949. // that passes the test.
  1950. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity );
  1951. int GetCount() { return m_nCount; }
  1952. bool AddToList( CBaseEntity *pEntity );
  1953. private:
  1954. CBaseEntity **m_pList;
  1955. int m_nListMax;
  1956. int m_nCount;
  1957. };
  1958. CTongueEntitiesEnum::CTongueEntitiesEnum( CBaseEntity **pList, int listMax )
  1959. {
  1960. m_pList = pList;
  1961. m_nListMax = listMax;
  1962. m_nCount = 0;
  1963. }
  1964. bool CTongueEntitiesEnum::AddToList( CBaseEntity *pEntity )
  1965. {
  1966. m_pList[m_nCount] = pEntity;
  1967. ++m_nCount;
  1968. return ( m_nCount < m_nListMax );
  1969. }
  1970. IterationRetval_t CTongueEntitiesEnum::EnumElement( IHandleEntity *pHandleEntity )
  1971. {
  1972. CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  1973. if ( pEntity )
  1974. {
  1975. if ( !AddToList( pEntity ) )
  1976. return ITERATION_STOP;
  1977. }
  1978. return ITERATION_CONTINUE;
  1979. }
  1980. //-----------------------------------------------------------------------------
  1981. // Barnacle must trace against only brushes and its last enemy
  1982. //-----------------------------------------------------------------------------
  1983. class CBarnacleTongueFilter : public CTraceFilterSimple
  1984. {
  1985. DECLARE_CLASS( CBarnacleTongueFilter, CTraceFilterSimple );
  1986. public:
  1987. CBarnacleTongueFilter( CBaseEntity *pLastEnemy, const IHandleEntity *passedict, int collisionGroup ) :
  1988. CTraceFilterSimple( passedict, collisionGroup )
  1989. {
  1990. m_pLastEnemy = pLastEnemy;
  1991. m_pBarnacle = const_cast<CBaseEntity*>( EntityFromEntityHandle( passedict ) );
  1992. }
  1993. virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  1994. {
  1995. if ( pServerEntity == m_pLastEnemy )
  1996. return true;
  1997. #ifdef HL2_EPISODIC
  1998. CBaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  1999. if ( pEntity )
  2000. {
  2001. if ( FStrEq( STRING( pEntity->m_iClassname ), "func_brush" ) )
  2002. {
  2003. CFuncBrush *pFuncBrush = assert_cast<CFuncBrush *>(pEntity);
  2004. if ( pFuncBrush->m_bInvertExclusion )
  2005. {
  2006. if ( pFuncBrush->m_iszExcludedClass == m_pBarnacle->m_iClassname )
  2007. return true;
  2008. else
  2009. return false;
  2010. }
  2011. else
  2012. {
  2013. if ( pFuncBrush->m_iszExcludedClass != m_pBarnacle->m_iClassname )
  2014. return false;
  2015. }
  2016. }
  2017. if ( pEntity->IsBSPModel() == false && pEntity->IsWorld() == false )
  2018. {
  2019. return false;
  2020. }
  2021. }
  2022. #endif
  2023. return BaseClass::ShouldHitEntity( pServerEntity, contentsMask );
  2024. }
  2025. private:
  2026. CBaseEntity *m_pLastEnemy;
  2027. CBaseEntity *m_pBarnacle;
  2028. };
  2029. #define BARNACLE_CHECK_SPACING 12
  2030. CBaseEntity *CNPC_Barnacle::TongueTouchEnt ( float *pflLength )
  2031. {
  2032. trace_t tr;
  2033. float length;
  2034. int iMask = MASK_SOLID_BRUSHONLY;
  2035. #ifdef HL2_EPISODIC
  2036. iMask = MASK_NPCSOLID;
  2037. #endif
  2038. // trace once to hit architecture and see if the tongue needs to change position.
  2039. CBarnacleTongueFilter tongueFilter( m_hLastSpitEnemy, this, COLLISION_GROUP_NONE );
  2040. AI_TraceLine ( GetAbsOrigin(), GetAbsOrigin() - Vector ( 0 , 0 , 2048 ),
  2041. iMask, &tongueFilter, &tr );
  2042. length = fabs( GetAbsOrigin().z - tr.endpos.z );
  2043. // Pull it up a tad
  2044. length = MAX(8, length - m_flRestUnitsAboveGround);
  2045. if ( pflLength )
  2046. {
  2047. *pflLength = length;
  2048. }
  2049. Vector delta = Vector( BARNACLE_CHECK_SPACING, BARNACLE_CHECK_SPACING, 0 );
  2050. Vector mins = GetAbsOrigin() - delta;
  2051. Vector maxs = GetAbsOrigin() + delta;
  2052. maxs.z = GetAbsOrigin().z;
  2053. mins.z -= length;
  2054. CBaseEntity *pList[10];
  2055. CTongueEntitiesEnum tongueEnum( pList, 10 );
  2056. partition->EnumerateElementsInBox( PARTITION_ENGINE_SOLID_EDICTS, mins, maxs, false, &tongueEnum );
  2057. int nCount = tongueEnum.GetCount();
  2058. if ( !nCount )
  2059. return NULL;
  2060. for ( int i = 0; i < nCount; i++ )
  2061. {
  2062. CBaseEntity *pTest = pList[i];
  2063. // Can't lift something that's in the process of being lifted...
  2064. // Necessary for good +use interactions
  2065. if ( pTest->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
  2066. continue;
  2067. // Vehicles can drive so fast that players can warp through the barnacle tongue.
  2068. // Therefore, we have to do a check to ensure that doesn't happen.
  2069. if ( pTest->GetServerVehicle() )
  2070. {
  2071. CBaseEntity *pDriver = pTest->GetServerVehicle()->GetPassenger();
  2072. if ( pDriver )
  2073. {
  2074. Vector vecPrevDriverPos;
  2075. pTest->GetVelocity( &vecPrevDriverPos );
  2076. VectorMA( pDriver->GetAbsOrigin(), -0.1f, vecPrevDriverPos, vecPrevDriverPos );
  2077. Ray_t sweptDriver;
  2078. sweptDriver.Init( vecPrevDriverPos, pDriver->GetAbsOrigin(), pDriver->WorldAlignMins(), pDriver->WorldAlignMaxs() );
  2079. if ( IsBoxIntersectingRay( mins, maxs, sweptDriver ) )
  2080. {
  2081. pTest = pDriver;
  2082. }
  2083. }
  2084. }
  2085. // Deal with physics objects
  2086. if ( pTest->GetMoveType() == MOVETYPE_VPHYSICS )
  2087. {
  2088. IPhysicsObject *pObject = pTest->VPhysicsGetObject();
  2089. if ( pObject && pObject->GetMass() <= BARNACLE_TONGUE_MAX_LIFT_MASS )
  2090. {
  2091. // If this is an item, make sure it's near the tongue before lifting it.
  2092. // Weapons and other items have very large bounding boxes.
  2093. if( pTest->GetSolidFlags() & FSOLID_TRIGGER )
  2094. {
  2095. if( UTIL_DistApprox2D( WorldSpaceCenter(), pTest->WorldSpaceCenter() ) > 16 )
  2096. {
  2097. continue;
  2098. }
  2099. }
  2100. // Allow the barnacles to grab stuff while their tongue is lowering
  2101. #ifdef HL2_EPISODIC
  2102. length = fabs( GetAbsOrigin().z - pTest->WorldSpaceCenter().z );
  2103. // Pull it up a tad
  2104. length = MAX(8, length - m_flRestUnitsAboveGround);
  2105. if ( pflLength )
  2106. {
  2107. *pflLength = length;
  2108. }
  2109. #endif
  2110. return pTest;
  2111. }
  2112. }
  2113. // NPCs + players
  2114. CBaseCombatCharacter *pVictim = ToBaseCombatCharacter( pTest );
  2115. if ( !pVictim )
  2116. continue;
  2117. // only clients and monsters
  2118. if ( pTest != this &&
  2119. IRelationType( pTest ) == D_HT &&
  2120. pVictim->m_lifeState != LIFE_DEAD &&
  2121. pVictim->m_lifeState != LIFE_DYING &&
  2122. !( pVictim->GetFlags() & FL_NOTARGET ) )
  2123. {
  2124. // Allow the barnacles to grab stuff while their tongue is lowering
  2125. #ifdef HL2_EPISODIC
  2126. length = fabs( GetAbsOrigin().z - pTest->WorldSpaceCenter().z );
  2127. // Pull it up a tad
  2128. length = MAX(8, length - m_flRestUnitsAboveGround);
  2129. if ( pflLength )
  2130. {
  2131. *pflLength = length;
  2132. }
  2133. #endif
  2134. return pTest;
  2135. }
  2136. }
  2137. return NULL;
  2138. }
  2139. //===============================================================================================================================
  2140. // BARNACLE TONGUE TIP
  2141. //===============================================================================================================================
  2142. // Crane tip
  2143. LINK_ENTITY_TO_CLASS( npc_barnacle_tongue_tip, CBarnacleTongueTip );
  2144. BEGIN_DATADESC( CBarnacleTongueTip )
  2145. DEFINE_FIELD( m_hBarnacle, FIELD_EHANDLE ),
  2146. DEFINE_PHYSPTR( m_pSpring ),
  2147. END_DATADESC()
  2148. //-----------------------------------------------------------------------------
  2149. // Purpose: To by usable by vphysics, this needs to have a phys model.
  2150. //-----------------------------------------------------------------------------
  2151. void CBarnacleTongueTip::Spawn( void )
  2152. {
  2153. Precache();
  2154. SetModel( "models/props_junk/rock001a.mdl" );
  2155. AddEffects( EF_NODRAW );
  2156. // We don't want this to be solid, because we don't want it to collide with the barnacle.
  2157. SetSolid( SOLID_VPHYSICS );
  2158. AddSolidFlags( FSOLID_NOT_SOLID );
  2159. BaseClass::Spawn();
  2160. m_pSpring = NULL;
  2161. }
  2162. int CBarnacleTongueTip::UpdateTransmitState( void )
  2163. {
  2164. return SetTransmitState( FL_EDICT_PVSCHECK );
  2165. }
  2166. //-----------------------------------------------------------------------------
  2167. // Purpose:
  2168. //-----------------------------------------------------------------------------
  2169. void CBarnacleTongueTip::Precache( void )
  2170. {
  2171. BaseClass::Precache();
  2172. }
  2173. //-----------------------------------------------------------------------------
  2174. // Purpose:
  2175. //-----------------------------------------------------------------------------
  2176. void CBarnacleTongueTip::UpdateOnRemove( )
  2177. {
  2178. if ( m_pSpring )
  2179. {
  2180. physenv->DestroySpring( m_pSpring );
  2181. m_pSpring = NULL;
  2182. }
  2183. BaseClass::UpdateOnRemove();
  2184. }
  2185. //-----------------------------------------------------------------------------
  2186. // If the tip changes, we gotta update the barnacle's notion of his tongue
  2187. //-----------------------------------------------------------------------------
  2188. void CBarnacleTongueTip::VPhysicsUpdate( IPhysicsObject *pPhysics )
  2189. {
  2190. BaseClass::VPhysicsUpdate( pPhysics );
  2191. if ( m_hBarnacle.Get() )
  2192. {
  2193. m_hBarnacle->OnTongueTipUpdated();
  2194. }
  2195. }
  2196. //-----------------------------------------------------------------------------
  2197. // Purpose: Activate/create the spring
  2198. //-----------------------------------------------------------------------------
  2199. bool CBarnacleTongueTip::CreateSpring( CBaseAnimating *pTongueRoot )
  2200. {
  2201. IPhysicsObject *pPhysObject = VPhysicsGetObject();
  2202. IPhysicsObject *pRootPhysObject = pTongueRoot->VPhysicsGetObject();
  2203. Assert( pRootPhysObject );
  2204. Assert( pPhysObject );
  2205. // Root has huge mass, tip has little
  2206. pRootPhysObject->SetMass( VPHYSICS_MAX_MASS );
  2207. pPhysObject->SetMass( BARNACLE_TONGUE_TIP_MASS );
  2208. float damping = 3;
  2209. pPhysObject->SetDamping( &damping, &damping );
  2210. springparams_t spring;
  2211. spring.constant = BARNACLE_TONGUE_SPRING_CONSTANT_HANGING;
  2212. spring.damping = BARNACLE_TONGUE_SPRING_DAMPING;
  2213. spring.naturalLength = (GetAbsOrigin() - pTongueRoot->GetAbsOrigin()).Length();
  2214. spring.relativeDamping = 10;
  2215. spring.startPosition = GetAbsOrigin();
  2216. spring.endPosition = pTongueRoot->GetAbsOrigin();
  2217. spring.useLocalPositions = false;
  2218. m_pSpring = physenv->CreateSpring( pPhysObject, pRootPhysObject, &spring );
  2219. return true;
  2220. }
  2221. //-----------------------------------------------------------------------------
  2222. // Purpose: Create a barnacle tongue tip at the bottom of the tongue
  2223. //-----------------------------------------------------------------------------
  2224. CBarnacleTongueTip *CBarnacleTongueTip::CreateTongueTip( CNPC_Barnacle *pBarnacle, CBaseAnimating *pTongueRoot, const Vector &vecOrigin, const QAngle &vecAngles )
  2225. {
  2226. CBarnacleTongueTip *pTip = (CBarnacleTongueTip *)CBaseEntity::Create( "npc_barnacle_tongue_tip", vecOrigin, vecAngles );
  2227. if ( !pTip )
  2228. return NULL;
  2229. pTip->VPhysicsInitNormal( pTip->GetSolid(), pTip->GetSolidFlags(), false );
  2230. if ( !pTip->CreateSpring( pTongueRoot ) )
  2231. return NULL;
  2232. // Set the backpointer to the barnacle
  2233. pTip->m_hBarnacle = pBarnacle;
  2234. // Don't collide with the world
  2235. IPhysicsObject *pTipPhys = pTip->VPhysicsGetObject();
  2236. // turn off all floating / fluid simulation
  2237. pTipPhys->SetCallbackFlags( pTipPhys->GetCallbackFlags() & (~CALLBACK_DO_FLUID_SIMULATION) );
  2238. return pTip;
  2239. }
  2240. //-----------------------------------------------------------------------------
  2241. // Purpose: Create a barnacle tongue tip at the root (i.e. inside the barnacle)
  2242. //-----------------------------------------------------------------------------
  2243. CBarnacleTongueTip *CBarnacleTongueTip::CreateTongueRoot( const Vector &vecOrigin, const QAngle &vecAngles )
  2244. {
  2245. CBarnacleTongueTip *pTip = (CBarnacleTongueTip *)CBaseEntity::Create( "npc_barnacle_tongue_tip", vecOrigin, vecAngles );
  2246. if ( !pTip )
  2247. return NULL;
  2248. pTip->AddSolidFlags( FSOLID_NOT_SOLID );
  2249. // Disable movement on the root, we'll move this thing manually.
  2250. pTip->VPhysicsInitShadow( false, false );
  2251. pTip->SetMoveType( MOVETYPE_NONE );
  2252. return pTip;
  2253. }
  2254. //-----------------------------------------------------------------------------
  2255. //
  2256. // Schedules
  2257. //
  2258. //-----------------------------------------------------------------------------
  2259. AI_BEGIN_CUSTOM_NPC( npc_barnacle, CNPC_Barnacle )
  2260. // Register our interactions
  2261. DECLARE_INTERACTION( g_interactionBarnacleVictimDangle )
  2262. DECLARE_INTERACTION( g_interactionBarnacleVictimReleased )
  2263. DECLARE_INTERACTION( g_interactionBarnacleVictimGrab )
  2264. DECLARE_INTERACTION( g_interactionBarnacleVictimBite )
  2265. // Conditions
  2266. // Tasks
  2267. // Activities
  2268. DECLARE_ACTIVITY( ACT_BARNACLE_SLURP ) // Pulling the tongue up with prey on the end
  2269. DECLARE_ACTIVITY( ACT_BARNACLE_BITE_HUMAN ) // Biting the head of a humanoid
  2270. DECLARE_ACTIVITY( ACT_BARNACLE_BITE_PLAYER ) // Biting the head of a humanoid
  2271. DECLARE_ACTIVITY( ACT_BARNACLE_CHEW_HUMAN ) // Slowly swallowing the humanoid
  2272. DECLARE_ACTIVITY( ACT_BARNACLE_BARF_HUMAN ) // Spitting out human legs & gibs
  2273. DECLARE_ACTIVITY( ACT_BARNACLE_TONGUE_WRAP ) // Wrapping the tongue around a target
  2274. DECLARE_ACTIVITY( ACT_BARNACLE_TASTE_SPIT ) // Yuck! Me no like that!
  2275. DECLARE_ACTIVITY( ACT_BARNACLE_BITE_SMALL_THINGS ) // Biting small things, like a headcrab
  2276. DECLARE_ACTIVITY( ACT_BARNACLE_CHEW_SMALL_THINGS ) // Chewing small things, like a headcrab
  2277. //Adrian: events go here
  2278. DECLARE_ANIMEVENT( AE_BARNACLE_PUKEGIB )
  2279. DECLARE_ANIMEVENT( AE_BARNACLE_BITE )
  2280. DECLARE_ANIMEVENT( AE_BARNACLE_SPIT )
  2281. // Schedules
  2282. AI_END_CUSTOM_NPC()