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.

1012 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
  4. // events
  5. //
  6. // $Workfile: $
  7. // $Date: $
  8. //
  9. //-----------------------------------------------------------------------------
  10. // $Log: $
  11. //
  12. // $NoKeywords: $
  13. //=============================================================================//
  14. #include "cbase.h"
  15. #include "ai_default.h"
  16. #include "ai_task.h"
  17. #include "ai_schedule.h"
  18. #include "ai_node.h"
  19. #include "ai_hull.h"
  20. #include "ai_hint.h"
  21. #include "ai_memory.h"
  22. #include "ai_route.h"
  23. #include "ai_motor.h"
  24. #include "ai_senses.h"
  25. #include "soundent.h"
  26. #include "game.h"
  27. #include "npcevent.h"
  28. #include "entitylist.h"
  29. #include "activitylist.h"
  30. #include "animation.h"
  31. #include "basecombatweapon.h"
  32. #include "IEffects.h"
  33. #include "vstdlib/random.h"
  34. #include "engine/IEngineSound.h"
  35. #include "ammodef.h"
  36. #include "hl1_ai_basenpc.h"
  37. #include "studio.h" //hitbox parsing
  38. #include "collisionutils.h" //ComputeSeparatingPlane
  39. #include "physics_bone_follower.h" //For BoneFollowerManager
  40. #define ACT_T_IDLE 1010
  41. Activity ACT_1010;
  42. Activity ACT_1011;
  43. Activity ACT_1012;
  44. Activity ACT_1013;
  45. #define ACT_T_TAP 1020
  46. Activity ACT_1020;
  47. Activity ACT_1021;
  48. Activity ACT_1022;
  49. Activity ACT_1023;
  50. #define ACT_T_STRIKE 1030
  51. Activity ACT_1030;
  52. Activity ACT_1031;
  53. Activity ACT_1032;
  54. Activity ACT_1033;
  55. #define ACT_T_REARIDLE 1040
  56. Activity ACT_1040;
  57. Activity ACT_1041;
  58. Activity ACT_1042;
  59. Activity ACT_1043;
  60. Activity ACT_1044;
  61. class CNPC_Tentacle : public CHL1BaseNPC
  62. {
  63. DECLARE_CLASS( CNPC_Tentacle, CHL1BaseNPC );
  64. public:
  65. CNPC_Tentacle();
  66. void Spawn( );
  67. void Precache( );
  68. bool KeyValue( const char *szKeyName, const char *szValue );
  69. bool QueryHearSound( CSound *pSound ) { return true; } // Tentacle isn't picky.
  70. int Level( float dz );
  71. int MyLevel( void );
  72. float MyHeight( void );
  73. // Don't allow the tentacle to go across transitions!!!
  74. virtual int ObjectCaps( void ) { return CAI_BaseNPC::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  75. void Start ( void );
  76. void Cycle ( void );
  77. void HitTouch( CBaseEntity *pOther );
  78. void HandleAnimEvent( animevent_t *pEvent );
  79. float HearingSensitivity( void ) { return 2.0; };
  80. virtual int OnTakeDamage( const CTakeDamageInfo &info );
  81. bool CreateVPhysics( void );
  82. void UpdateOnRemove( void );
  83. float m_flInitialYaw;
  84. int m_iGoalAnim;
  85. int m_iLevel;
  86. int m_iDir;
  87. float m_flFramerateAdj;
  88. float m_flSoundYaw;
  89. int m_iSoundLevel;
  90. float m_flSoundTime;
  91. float m_flSoundRadius;
  92. int m_iHitDmg;
  93. float m_flHitTime;
  94. float m_flTapRadius;
  95. float m_flNextSong;
  96. static int g_fFlySound;
  97. static int g_fSquirmSound;
  98. float m_flMaxYaw;
  99. int m_iTapSound;
  100. Vector m_vecPrevSound;
  101. float m_flPrevSoundTime;
  102. float MaxYawSpeed( void ) { return 18.0f; }
  103. bool HeardAnything( void );
  104. Class_T Classify ( void );
  105. CBoneFollowerManager m_BoneFollowerManager;
  106. DECLARE_DATADESC();
  107. DEFINE_CUSTOM_AI;
  108. };
  109. // Crane bones that have physics followers
  110. static const char *pTentacleFollowerBoneNames[] =
  111. {
  112. "Bone08",
  113. "Bone09"
  114. };
  115. int CNPC_Tentacle::g_fFlySound;
  116. int CNPC_Tentacle::g_fSquirmSound;
  117. LINK_ENTITY_TO_CLASS( monster_tentacle, CNPC_Tentacle );
  118. // stike sounds
  119. #define TE_NONE -1
  120. #define TE_SILO 0
  121. #define TE_DIRT 1
  122. #define TE_WATER 2
  123. // animation sequence aliases
  124. typedef enum
  125. {
  126. TENTACLE_ANIM_Pit_Idle,
  127. TENTACLE_ANIM_rise_to_Temp1,
  128. TENTACLE_ANIM_Temp1_to_Floor,
  129. TENTACLE_ANIM_Floor_Idle,
  130. TENTACLE_ANIM_Floor_Fidget_Pissed,
  131. TENTACLE_ANIM_Floor_Fidget_SmallRise,
  132. TENTACLE_ANIM_Floor_Fidget_Wave,
  133. TENTACLE_ANIM_Floor_Strike,
  134. TENTACLE_ANIM_Floor_Tap,
  135. TENTACLE_ANIM_Floor_Rotate,
  136. TENTACLE_ANIM_Floor_Rear,
  137. TENTACLE_ANIM_Floor_Rear_Idle,
  138. TENTACLE_ANIM_Floor_to_Lev1,
  139. TENTACLE_ANIM_Lev1_Idle,
  140. TENTACLE_ANIM_Lev1_Fidget_Claw,
  141. TENTACLE_ANIM_Lev1_Fidget_Shake,
  142. TENTACLE_ANIM_Lev1_Fidget_Snap,
  143. TENTACLE_ANIM_Lev1_Strike,
  144. TENTACLE_ANIM_Lev1_Tap,
  145. TENTACLE_ANIM_Lev1_Rotate,
  146. TENTACLE_ANIM_Lev1_Rear,
  147. TENTACLE_ANIM_Lev1_Rear_Idle,
  148. TENTACLE_ANIM_Lev1_to_Lev2,
  149. TENTACLE_ANIM_Lev2_Idle,
  150. TENTACLE_ANIM_Lev2_Fidget_Shake,
  151. TENTACLE_ANIM_Lev2_Fidget_Swing,
  152. TENTACLE_ANIM_Lev2_Fidget_Tut,
  153. TENTACLE_ANIM_Lev2_Strike,
  154. TENTACLE_ANIM_Lev2_Tap,
  155. TENTACLE_ANIM_Lev2_Rotate,
  156. TENTACLE_ANIM_Lev2_Rear,
  157. TENTACLE_ANIM_Lev2_Rear_Idle,
  158. TENTACLE_ANIM_Lev2_to_Lev3,
  159. TENTACLE_ANIM_Lev3_Idle,
  160. TENTACLE_ANIM_Lev3_Fidget_Shake,
  161. TENTACLE_ANIM_Lev3_Fidget_Side,
  162. TENTACLE_ANIM_Lev3_Fidget_Swipe,
  163. TENTACLE_ANIM_Lev3_Strike,
  164. TENTACLE_ANIM_Lev3_Tap,
  165. TENTACLE_ANIM_Lev3_Rotate,
  166. TENTACLE_ANIM_Lev3_Rear,
  167. TENTACLE_ANIM_Lev3_Rear_Idle,
  168. TENTACLE_ANIM_Lev1_Door_reach,
  169. TENTACLE_ANIM_Lev3_to_Engine,
  170. TENTACLE_ANIM_Engine_Idle,
  171. TENTACLE_ANIM_Engine_Sway,
  172. TENTACLE_ANIM_Engine_Swat,
  173. TENTACLE_ANIM_Engine_Bob,
  174. TENTACLE_ANIM_Engine_Death1,
  175. TENTACLE_ANIM_Engine_Death2,
  176. TENTACLE_ANIM_Engine_Death3,
  177. TENTACLE_ANIM_none
  178. } TENTACLE_ANIM;
  179. BEGIN_DATADESC( CNPC_Tentacle )
  180. DEFINE_FIELD( m_flInitialYaw, FIELD_FLOAT ),
  181. DEFINE_FIELD( m_iGoalAnim, FIELD_INTEGER ),
  182. DEFINE_FIELD( m_iLevel, FIELD_INTEGER ),
  183. DEFINE_FIELD( m_iDir, FIELD_INTEGER ),
  184. DEFINE_FIELD( m_flFramerateAdj, FIELD_FLOAT ),
  185. DEFINE_FIELD( m_flSoundYaw, FIELD_FLOAT ),
  186. DEFINE_FIELD( m_iSoundLevel, FIELD_INTEGER ),
  187. DEFINE_FIELD( m_flSoundTime, FIELD_TIME ),
  188. DEFINE_FIELD( m_flSoundRadius, FIELD_FLOAT ),
  189. DEFINE_FIELD( m_iHitDmg, FIELD_INTEGER ),
  190. DEFINE_FIELD( m_flHitTime, FIELD_TIME ),
  191. DEFINE_FIELD( m_flTapRadius, FIELD_FLOAT ),
  192. DEFINE_FIELD( m_flNextSong, FIELD_TIME ),
  193. DEFINE_FIELD( m_iTapSound, FIELD_INTEGER ),
  194. DEFINE_FIELD( m_flMaxYaw, FIELD_FLOAT ),
  195. DEFINE_FIELD( m_vecPrevSound, FIELD_POSITION_VECTOR ),
  196. DEFINE_FIELD( m_flPrevSoundTime, FIELD_TIME ),
  197. DEFINE_EMBEDDED( m_BoneFollowerManager ),
  198. DEFINE_THINKFUNC( Start ),
  199. DEFINE_THINKFUNC( Cycle ),
  200. DEFINE_ENTITYFUNC( HitTouch ),
  201. END_DATADESC()
  202. Class_T CNPC_Tentacle::Classify ( void )
  203. {
  204. return CLASS_ALIEN_MONSTER;
  205. }
  206. CNPC_Tentacle::CNPC_Tentacle()
  207. {
  208. m_flMaxYaw = 65;
  209. m_iTapSound = 0;
  210. }
  211. bool CNPC_Tentacle::KeyValue( const char *szKeyName, const char *szValue )
  212. {
  213. if ( FStrEq( szKeyName, "sweeparc") )
  214. {
  215. m_flMaxYaw = atof( szValue ) / 2.0;
  216. return true;
  217. }
  218. else if (FStrEq( szKeyName, "sound"))
  219. {
  220. m_iTapSound = atoi( szValue );
  221. return true;
  222. }
  223. else
  224. return BaseClass::KeyValue( szKeyName, szValue );
  225. return false;
  226. }
  227. //
  228. // Tentacle Spawn
  229. //
  230. void CNPC_Tentacle::Spawn( )
  231. {
  232. Precache( );
  233. SetSolid( SOLID_BBOX );
  234. //Necessary for TestCollision to be called for hitbox ray hits
  235. AddSolidFlags( FSOLID_CUSTOMRAYTEST );
  236. SetMoveType( MOVETYPE_NONE );
  237. ClearEffects();
  238. m_iHealth = 75;
  239. SetSequence( 0 );
  240. SetModel( "models/tentacle2.mdl" );
  241. UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
  242. // Use our hitboxes to determine our render bounds
  243. CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
  244. m_takedamage = DAMAGE_AIM;
  245. AddFlag( FL_NPC );
  246. m_bloodColor = BLOOD_COLOR_GREEN;
  247. ResetSequenceInfo( );
  248. m_iDir = 1;
  249. SetThink( &CNPC_Tentacle::Start );
  250. SetNextThink( gpGlobals->curtime + 0.2 );
  251. SetTouch( &CNPC_Tentacle::HitTouch );
  252. m_flInitialYaw = GetAbsAngles().y;
  253. GetMotor()->SetIdealYawAndUpdate( m_flInitialYaw );
  254. g_fFlySound = FALSE;
  255. g_fSquirmSound = FALSE;
  256. m_iHitDmg = 200;
  257. if (m_flMaxYaw <= 0)
  258. m_flMaxYaw = 65;
  259. m_NPCState = NPC_STATE_IDLE;
  260. UTIL_SetOrigin( this, GetAbsOrigin() );
  261. CreateVPhysics();
  262. AddEffects( EF_NOSHADOW );
  263. }
  264. void CNPC_Tentacle::UpdateOnRemove( void )
  265. {
  266. m_BoneFollowerManager.DestroyBoneFollowers();
  267. BaseClass::UpdateOnRemove();
  268. }
  269. void CNPC_Tentacle::Precache( )
  270. {
  271. PrecacheModel("models/tentacle2.mdl");
  272. PrecacheScriptSound( "Tentacle.Flies" );
  273. PrecacheScriptSound( "Tentacle.Squirm" );
  274. PrecacheScriptSound( "Tentacle.Sing" );
  275. PrecacheScriptSound( "Tentacle.HitDirt" );
  276. PrecacheScriptSound( "Tentacle.Swing" );
  277. PrecacheScriptSound( "Tentacle.Search" );
  278. PrecacheScriptSound( "Tentacle.Roar" );
  279. PrecacheScriptSound( "Tentacle.Alert" );
  280. BaseClass::Precache();
  281. }
  282. int CNPC_Tentacle::Level( float dz )
  283. {
  284. if (dz < 216)
  285. return 0;
  286. if (dz < 408)
  287. return 1;
  288. if (dz < 600)
  289. return 2;
  290. return 3;
  291. }
  292. float CNPC_Tentacle::MyHeight( )
  293. {
  294. switch ( MyLevel( ) )
  295. {
  296. case 1:
  297. return 256;
  298. case 2:
  299. return 448;
  300. case 3:
  301. return 640;
  302. }
  303. return 0;
  304. }
  305. int CNPC_Tentacle::MyLevel( )
  306. {
  307. switch( GetSequence() )
  308. {
  309. case TENTACLE_ANIM_Pit_Idle:
  310. return -1;
  311. case TENTACLE_ANIM_rise_to_Temp1:
  312. case TENTACLE_ANIM_Temp1_to_Floor:
  313. case TENTACLE_ANIM_Floor_to_Lev1:
  314. return 0;
  315. case TENTACLE_ANIM_Floor_Idle:
  316. case TENTACLE_ANIM_Floor_Fidget_Pissed:
  317. case TENTACLE_ANIM_Floor_Fidget_SmallRise:
  318. case TENTACLE_ANIM_Floor_Fidget_Wave:
  319. case TENTACLE_ANIM_Floor_Strike:
  320. case TENTACLE_ANIM_Floor_Tap:
  321. case TENTACLE_ANIM_Floor_Rotate:
  322. case TENTACLE_ANIM_Floor_Rear:
  323. case TENTACLE_ANIM_Floor_Rear_Idle:
  324. return 0;
  325. case TENTACLE_ANIM_Lev1_Idle:
  326. case TENTACLE_ANIM_Lev1_Fidget_Claw:
  327. case TENTACLE_ANIM_Lev1_Fidget_Shake:
  328. case TENTACLE_ANIM_Lev1_Fidget_Snap:
  329. case TENTACLE_ANIM_Lev1_Strike:
  330. case TENTACLE_ANIM_Lev1_Tap:
  331. case TENTACLE_ANIM_Lev1_Rotate:
  332. case TENTACLE_ANIM_Lev1_Rear:
  333. case TENTACLE_ANIM_Lev1_Rear_Idle:
  334. return 1;
  335. case TENTACLE_ANIM_Lev1_to_Lev2:
  336. return 1;
  337. case TENTACLE_ANIM_Lev2_Idle:
  338. case TENTACLE_ANIM_Lev2_Fidget_Shake:
  339. case TENTACLE_ANIM_Lev2_Fidget_Swing:
  340. case TENTACLE_ANIM_Lev2_Fidget_Tut:
  341. case TENTACLE_ANIM_Lev2_Strike:
  342. case TENTACLE_ANIM_Lev2_Tap:
  343. case TENTACLE_ANIM_Lev2_Rotate:
  344. case TENTACLE_ANIM_Lev2_Rear:
  345. case TENTACLE_ANIM_Lev2_Rear_Idle:
  346. return 2;
  347. case TENTACLE_ANIM_Lev2_to_Lev3:
  348. return 2;
  349. case TENTACLE_ANIM_Lev3_Idle:
  350. case TENTACLE_ANIM_Lev3_Fidget_Shake:
  351. case TENTACLE_ANIM_Lev3_Fidget_Side:
  352. case TENTACLE_ANIM_Lev3_Fidget_Swipe:
  353. case TENTACLE_ANIM_Lev3_Strike:
  354. case TENTACLE_ANIM_Lev3_Tap:
  355. case TENTACLE_ANIM_Lev3_Rotate:
  356. case TENTACLE_ANIM_Lev3_Rear:
  357. case TENTACLE_ANIM_Lev3_Rear_Idle:
  358. return 3;
  359. case TENTACLE_ANIM_Lev1_Door_reach:
  360. return -1;
  361. }
  362. return -1;
  363. }
  364. void CNPC_Tentacle::Start( void )
  365. {
  366. SetThink( &CNPC_Tentacle::Cycle );
  367. CPASAttenuationFilter filter( this );
  368. if ( !g_fFlySound )
  369. {
  370. EmitSound( filter, entindex(), "Tentacle.Flies" );
  371. g_fFlySound = TRUE;
  372. }
  373. else if ( !g_fSquirmSound )
  374. {
  375. EmitSound( filter, entindex(), "Tentacle.Squirm" );
  376. g_fSquirmSound = TRUE;
  377. }
  378. SetNextThink( gpGlobals->curtime + 0.1 );
  379. }
  380. bool CNPC_Tentacle::HeardAnything( void )
  381. {
  382. if ( HasCondition( COND_HEAR_DANGER ) || // I remove a bunch of sounds from here on purpose. Talk to me if you're changing this!(sjb)
  383. HasCondition( COND_HEAR_COMBAT ) ||
  384. HasCondition( COND_HEAR_WORLD ) ||
  385. HasCondition( COND_HEAR_PLAYER ) )
  386. return true;
  387. return false;
  388. }
  389. void CNPC_Tentacle::Cycle( void )
  390. {
  391. //NDebugOverlay::Cross3D( EarPosition(), 32, 255, 0, 0, false, 0.1 );
  392. // ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState );
  393. SetNextThink( gpGlobals->curtime + 0.1 );
  394. // ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health );
  395. if ( m_NPCState == NPC_STATE_SCRIPT || GetIdealState() == NPC_STATE_SCRIPT)
  396. {
  397. SetAbsAngles( QAngle( GetAbsAngles().x, m_flInitialYaw, GetAbsAngles().z ) );
  398. GetMotor()->SetIdealYaw( m_flInitialYaw );
  399. RemoveIgnoredConditions();
  400. NPCThink( );
  401. m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
  402. return;
  403. }
  404. StudioFrameAdvance();
  405. DispatchAnimEvents( this );
  406. GetMotor()->UpdateYaw( MaxYawSpeed() );
  407. CSound *pSound = NULL;
  408. GetSenses()->Listen();
  409. m_BoneFollowerManager.UpdateBoneFollowers(this);
  410. // Listen will set this if there's something in my sound list
  411. if ( HeardAnything() )
  412. pSound = GetSenses()->GetClosestSound( false, (SOUND_DANGER|SOUND_COMBAT|SOUND_WORLD|SOUND_PLAYER) );
  413. else
  414. pSound = NULL;
  415. if ( pSound )
  416. {
  417. //NDebugOverlay::Line( EarPosition(), pSound->GetSoundOrigin(), 0, 255, 0, false, 0.2 );
  418. Vector vecDir;
  419. if ( gpGlobals->curtime - m_flPrevSoundTime < 0.5 )
  420. {
  421. float dt = gpGlobals->curtime - m_flPrevSoundTime;
  422. vecDir = pSound->GetSoundOrigin() + (pSound->GetSoundOrigin() - m_vecPrevSound) / dt - GetAbsOrigin();
  423. }
  424. else
  425. {
  426. vecDir = pSound->GetSoundOrigin() - GetAbsOrigin();
  427. }
  428. m_flPrevSoundTime = gpGlobals->curtime;
  429. m_vecPrevSound = pSound->GetSoundOrigin();
  430. m_flSoundYaw = VecToYaw ( vecDir ) - m_flInitialYaw;
  431. m_iSoundLevel = Level( vecDir.z );
  432. if (m_flSoundYaw < -180)
  433. m_flSoundYaw += 360;
  434. if (m_flSoundYaw > 180)
  435. m_flSoundYaw -= 360;
  436. // ALERT( at_console, "sound %d %.0f\n", m_iSoundLevel, m_flSoundYaw );
  437. if (m_flSoundTime < gpGlobals->curtime)
  438. {
  439. // play "I hear new something" sound
  440. UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Alert", 1.0, SNDLVL_GUNFIRE, 0, 100);
  441. }
  442. m_flSoundTime = gpGlobals->curtime + random->RandomFloat( 5.0, 10.0 );
  443. }
  444. // clip ideal_yaw
  445. float dy = m_flSoundYaw;
  446. switch( GetSequence() )
  447. {
  448. case TENTACLE_ANIM_Floor_Rear:
  449. case TENTACLE_ANIM_Floor_Rear_Idle:
  450. case TENTACLE_ANIM_Lev1_Rear:
  451. case TENTACLE_ANIM_Lev1_Rear_Idle:
  452. case TENTACLE_ANIM_Lev2_Rear:
  453. case TENTACLE_ANIM_Lev2_Rear_Idle:
  454. case TENTACLE_ANIM_Lev3_Rear:
  455. case TENTACLE_ANIM_Lev3_Rear_Idle:
  456. if (dy < 0 && dy > -m_flMaxYaw)
  457. dy = -m_flMaxYaw;
  458. if (dy > 0 && dy < m_flMaxYaw)
  459. dy = m_flMaxYaw;
  460. break;
  461. default:
  462. if (dy < -m_flMaxYaw)
  463. dy = -m_flMaxYaw;
  464. if (dy > m_flMaxYaw)
  465. dy = m_flMaxYaw;
  466. }
  467. GetMotor()->SetIdealYaw( m_flInitialYaw + dy );
  468. if ( IsSequenceFinished() )
  469. {
  470. // ALERT( at_console, "%s done %d %d\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim );
  471. if ( m_iHealth <= 1)
  472. {
  473. m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
  474. if ( GetSequence() == TENTACLE_ANIM_Pit_Idle)
  475. {
  476. m_iHealth = 75;
  477. }
  478. }
  479. else if ( m_flSoundTime > gpGlobals->curtime )
  480. {
  481. if (m_flSoundYaw >= -(m_flMaxYaw + 30) && m_flSoundYaw <= (m_flMaxYaw + 30))
  482. {
  483. // strike
  484. switch ( m_iSoundLevel )
  485. {
  486. case 0:
  487. m_iGoalAnim = SelectWeightedSequence ( ACT_1030 );
  488. break;
  489. case 1:
  490. m_iGoalAnim = SelectWeightedSequence ( ACT_1031 );
  491. break;
  492. case 2:
  493. m_iGoalAnim = SelectWeightedSequence ( ACT_1032 );
  494. break;
  495. case 3:
  496. m_iGoalAnim = SelectWeightedSequence ( ACT_1033 );
  497. break;
  498. }
  499. }
  500. else if (m_flSoundYaw >= -m_flMaxYaw * 2 && m_flSoundYaw <= m_flMaxYaw * 2)
  501. {
  502. // tap
  503. switch ( m_iSoundLevel )
  504. {
  505. case 0:
  506. m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
  507. break;
  508. case 1:
  509. m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
  510. break;
  511. case 2:
  512. m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
  513. break;
  514. case 3:
  515. m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
  516. break;
  517. }
  518. }
  519. else
  520. {
  521. // go into rear idle
  522. switch ( m_iSoundLevel )
  523. {
  524. case 0:
  525. m_iGoalAnim = SelectWeightedSequence ( ACT_1040 );
  526. break;
  527. case 1:
  528. m_iGoalAnim = SelectWeightedSequence ( ACT_1041 );
  529. break;
  530. case 2:
  531. m_iGoalAnim = SelectWeightedSequence ( ACT_1042 );
  532. break;
  533. case 3:
  534. m_iGoalAnim = SelectWeightedSequence ( ACT_1043 );
  535. break;
  536. case 4:
  537. m_iGoalAnim = SelectWeightedSequence ( ACT_1044 );
  538. break;
  539. }
  540. }
  541. }
  542. else if ( GetSequence() == TENTACLE_ANIM_Pit_Idle)
  543. {
  544. // stay in pit until hear noise
  545. m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
  546. }
  547. else if ( GetSequence() == m_iGoalAnim)
  548. {
  549. if ( MyLevel() >= 0 && gpGlobals->curtime < m_flSoundTime)
  550. {
  551. if ( random->RandomInt(0,9) < m_flSoundTime - gpGlobals->curtime )
  552. {
  553. // continue stike
  554. switch ( m_iSoundLevel )
  555. {
  556. case 0:
  557. m_iGoalAnim = SelectWeightedSequence ( ACT_1030 );
  558. break;
  559. case 1:
  560. m_iGoalAnim = SelectWeightedSequence ( ACT_1031 );
  561. break;
  562. case 2:
  563. m_iGoalAnim = SelectWeightedSequence ( ACT_1032 );
  564. break;
  565. case 3:
  566. m_iGoalAnim = SelectWeightedSequence ( ACT_1033 );
  567. break;
  568. }
  569. }
  570. else
  571. {
  572. // tap
  573. switch ( m_iSoundLevel )
  574. {
  575. case 0:
  576. m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
  577. break;
  578. case 1:
  579. m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
  580. break;
  581. case 2:
  582. m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
  583. break;
  584. case 3:
  585. m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
  586. break;
  587. }
  588. }
  589. }
  590. else if ( MyLevel( ) < 0 )
  591. {
  592. m_iGoalAnim = SelectWeightedSequence( ACT_1010 );
  593. }
  594. else
  595. {
  596. if (m_flNextSong < gpGlobals->curtime)
  597. {
  598. // play "I hear new something" sound
  599. CPASAttenuationFilter filter( this );
  600. EmitSound( filter, entindex(), "Tentacle.Sing" );
  601. m_flNextSong = gpGlobals->curtime + random->RandomFloat( 10, 20 );
  602. }
  603. if (random->RandomInt(0,15) == 0)
  604. {
  605. // idle on new level
  606. switch ( random->RandomInt( 0, 3 ) )
  607. {
  608. case 0:
  609. m_iGoalAnim = SelectWeightedSequence ( ACT_1010 );
  610. break;
  611. case 1:
  612. m_iGoalAnim = SelectWeightedSequence ( ACT_1011 );
  613. break;
  614. case 2:
  615. m_iGoalAnim = SelectWeightedSequence ( ACT_1012 );
  616. break;
  617. case 3:
  618. m_iGoalAnim = SelectWeightedSequence ( ACT_1013 );
  619. break;
  620. }
  621. }
  622. else if ( random->RandomInt( 0, 3 ) == 0 )
  623. {
  624. // tap
  625. switch ( MyLevel() )
  626. {
  627. case 0:
  628. m_iGoalAnim = SelectWeightedSequence ( ACT_1020 );
  629. break;
  630. case 1:
  631. m_iGoalAnim = SelectWeightedSequence ( ACT_1021 );
  632. break;
  633. case 2:
  634. m_iGoalAnim = SelectWeightedSequence ( ACT_1022 );
  635. break;
  636. case 3:
  637. m_iGoalAnim = SelectWeightedSequence ( ACT_1023 );
  638. break;
  639. }
  640. }
  641. else
  642. {
  643. // idle
  644. switch ( MyLevel() )
  645. {
  646. case 0:
  647. m_iGoalAnim = SelectWeightedSequence ( ACT_1010 );
  648. break;
  649. case 1:
  650. m_iGoalAnim = SelectWeightedSequence ( ACT_1011 );
  651. break;
  652. case 2:
  653. m_iGoalAnim = SelectWeightedSequence ( ACT_1012 );
  654. break;
  655. case 3:
  656. m_iGoalAnim = SelectWeightedSequence ( ACT_1013 );
  657. break;
  658. }
  659. }
  660. }
  661. if (m_flSoundYaw < 0)
  662. m_flSoundYaw += random->RandomFloat( 2, 8 );
  663. else
  664. m_flSoundYaw -= random->RandomFloat( 2, 8 );
  665. }
  666. SetSequence( FindTransitionSequence( GetSequence(), m_iGoalAnim, &m_iDir ) );
  667. if (m_iDir > 0)
  668. {
  669. SetCycle( 0 );
  670. }
  671. else
  672. {
  673. m_iDir = -1; // just to safe
  674. SetCycle( 1.0f );
  675. }
  676. ResetSequenceInfo( );
  677. m_flFramerateAdj = random->RandomFloat( -0.2, 0.2 );
  678. m_flPlaybackRate = m_iDir * 1.0 + m_flFramerateAdj;
  679. switch( GetSequence() )
  680. {
  681. case TENTACLE_ANIM_Floor_Tap:
  682. case TENTACLE_ANIM_Lev1_Tap:
  683. case TENTACLE_ANIM_Lev2_Tap:
  684. case TENTACLE_ANIM_Lev3_Tap:
  685. {
  686. Vector vecSrc, v_forward;
  687. AngleVectors( GetAbsAngles(), &v_forward );
  688. trace_t tr1, tr2;
  689. vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() - 4);
  690. UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 );
  691. vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() + 8);
  692. UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 );
  693. // ALERT( at_console, "%f %f\n", tr1.flFraction * 512, tr2.flFraction * 512 );
  694. m_flTapRadius = SetPoseParameter( 0, random->RandomFloat( tr1.fraction * 512, tr2.fraction * 512 ) );
  695. }
  696. break;
  697. default:
  698. m_flTapRadius = 336; // 400 - 64
  699. break;
  700. }
  701. SetViewOffset( Vector( 0, 0, MyHeight() ) );
  702. // ALERT( at_console, "seq %d\n", pev->sequence );
  703. }
  704. if (m_flPrevSoundTime + 2.0 > gpGlobals->curtime)
  705. {
  706. // 1.5 normal speed if hears sounds
  707. m_flPlaybackRate = m_iDir * 1.5 + m_flFramerateAdj;
  708. }
  709. else if (m_flPrevSoundTime + 5.0 > gpGlobals->curtime)
  710. {
  711. // slowdown to normal
  712. m_flPlaybackRate = m_iDir + m_iDir * (5 - (gpGlobals->curtime - m_flPrevSoundTime)) / 2 + m_flFramerateAdj;
  713. }
  714. }
  715. void CNPC_Tentacle::HandleAnimEvent( animevent_t *pEvent )
  716. {
  717. switch( pEvent->event )
  718. {
  719. case 1: // bang
  720. {
  721. Vector vecSrc;
  722. QAngle angAngles;
  723. GetAttachment( "0", vecSrc, angAngles );
  724. // Vector vecSrc = GetAbsOrigin() + m_flTapRadius * Vector( cos( GetAbsAngles().y * (3.14192653 / 180.0) ), sin( GetAbsAngles().y * (M_PI / 180.0) ), 0.0 );
  725. // vecSrc.z += MyHeight( );
  726. switch( m_iTapSound )
  727. {
  728. case TE_SILO:
  729. UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitSilo", 1.0, SNDLVL_GUNFIRE, 0, 100);
  730. break;
  731. case TE_NONE:
  732. break;
  733. case TE_DIRT:
  734. UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitDirt", 1.0, SNDLVL_GUNFIRE, 0, 100);
  735. break;
  736. case TE_WATER:
  737. UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitWater", 1.0, SNDLVL_GUNFIRE, 0, 100);
  738. break;
  739. }
  740. }
  741. break;
  742. case 3: // start killing swing
  743. m_iHitDmg = 200;
  744. break;
  745. case 4: // end killing swing
  746. m_iHitDmg = 25;
  747. break;
  748. case 5: // just "whoosh" sound
  749. break;
  750. case 2: // tap scrape
  751. case 6: // light tap
  752. {
  753. Vector vecSrc = GetAbsOrigin() + m_flTapRadius * Vector( cos( GetAbsAngles().y * (M_PI / 180.0) ), sin( GetAbsAngles().y * (M_PI / 180.0) ), 0.0 );
  754. vecSrc.z += MyHeight( );
  755. float flVol = random->RandomFloat( 0.3, 0.5 );
  756. switch( m_iTapSound )
  757. {
  758. case TE_SILO:
  759. UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitSilo", flVol, SNDLVL_GUNFIRE, 0, 100);
  760. break;
  761. case TE_NONE:
  762. break;
  763. case TE_DIRT:
  764. UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitDirt", flVol, SNDLVL_GUNFIRE, 0, 100);
  765. break;
  766. case TE_WATER:
  767. UTIL_EmitAmbientSound( GetSoundSourceIndex(), vecSrc, "Tentacle.HitWater", flVol, SNDLVL_GUNFIRE, 0, 100);
  768. break;
  769. }
  770. }
  771. break;
  772. case 7: // roar
  773. UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Roar", 1.0, SNDLVL_GUNFIRE, 0, 100);
  774. break;
  775. case 8: // search
  776. UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Search", 1.0, SNDLVL_GUNFIRE, 0, 100);
  777. break;
  778. case 9: // swing
  779. UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Swing", 1.0, SNDLVL_GUNFIRE, 0, 100);
  780. break;
  781. default:
  782. BaseClass::HandleAnimEvent( pEvent );
  783. }
  784. }
  785. void CNPC_Tentacle::HitTouch( CBaseEntity *pOther )
  786. {
  787. if (m_flHitTime > gpGlobals->curtime)
  788. return;
  789. // only look at the ones where the player hit me
  790. if( pOther == NULL || pOther->GetModelIndex() == GetModelIndex() || ( pOther->GetSolidFlags() & FSOLID_TRIGGER ) )
  791. return;
  792. //Right now the BoneFollower will always be hit in box 0, and
  793. //will pass that to us. Make *any* touch by the physics objects a kill
  794. //as the ragdoll only covers the top portion of the tentacle.
  795. if ( pOther->m_takedamage )
  796. {
  797. CTakeDamageInfo info( this, this, m_iHitDmg, DMG_CLUB );
  798. Vector vDamageForce = pOther->GetAbsOrigin() - GetAbsOrigin();
  799. VectorNormalize( vDamageForce );
  800. CalculateMeleeDamageForce( &info, vDamageForce, pOther->GetAbsOrigin() );
  801. pOther->TakeDamage( info );
  802. m_flHitTime = gpGlobals->curtime + 0.5;
  803. }
  804. }
  805. int CNPC_Tentacle::OnTakeDamage( const CTakeDamageInfo &info )
  806. {
  807. CTakeDamageInfo i = info;
  808. //Don't allow the tentacle to die. Instead set health to 1, so we can do our own death and rebirth
  809. if( (int)i.GetDamage() >= m_iHealth )
  810. {
  811. i.SetDamage( 0.0f );
  812. m_iHealth = 1;
  813. }
  814. return BaseClass::OnTakeDamage( i );
  815. }
  816. bool CNPC_Tentacle::CreateVPhysics( void )
  817. {
  818. BaseClass::CreateVPhysics();
  819. IPhysicsObject *pPhysics = VPhysicsGetObject();
  820. if( pPhysics )
  821. {
  822. unsigned short flags = pPhysics->GetCallbackFlags();
  823. flags |= CALLBACK_GLOBAL_TOUCH;
  824. pPhysics->SetCallbackFlags( flags );
  825. }
  826. m_BoneFollowerManager.InitBoneFollowers( this, ARRAYSIZE(pTentacleFollowerBoneNames), pTentacleFollowerBoneNames );
  827. return true;
  828. }
  829. //------------------------------------------------------------------------------
  830. //
  831. // Schedules
  832. //
  833. //------------------------------------------------------------------------------
  834. AI_BEGIN_CUSTOM_NPC( monster_tentacle, CNPC_Tentacle )
  835. DECLARE_ACTIVITY( ACT_1010 )
  836. DECLARE_ACTIVITY( ACT_1011 )
  837. DECLARE_ACTIVITY( ACT_1012 )
  838. DECLARE_ACTIVITY( ACT_1013 )
  839. DECLARE_ACTIVITY( ACT_1020 )
  840. DECLARE_ACTIVITY( ACT_1021 )
  841. DECLARE_ACTIVITY( ACT_1022 )
  842. DECLARE_ACTIVITY( ACT_1023 )
  843. DECLARE_ACTIVITY( ACT_1030 )
  844. DECLARE_ACTIVITY( ACT_1031 )
  845. DECLARE_ACTIVITY( ACT_1032 )
  846. DECLARE_ACTIVITY( ACT_1033 )
  847. DECLARE_ACTIVITY( ACT_1040 )
  848. DECLARE_ACTIVITY( ACT_1041 )
  849. DECLARE_ACTIVITY( ACT_1042 )
  850. DECLARE_ACTIVITY( ACT_1043 )
  851. DECLARE_ACTIVITY( ACT_1044 )
  852. AI_END_CUSTOM_NPC()