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.

1167 lines
30 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Cute hound like Alien.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "game.h"
  9. #include "ai_default.h"
  10. #include "ai_schedule.h"
  11. #include "ai_hull.h"
  12. #include "ai_route.h"
  13. #include "ai_hint.h"
  14. #include "ai_navigator.h"
  15. #include "ai_senses.h"
  16. #include "npcevent.h"
  17. #include "animation.h"
  18. #include "hl1_npc_bullsquid.h"
  19. #include "gib.h"
  20. #include "soundent.h"
  21. #include "ndebugoverlay.h"
  22. #include "vstdlib/random.h"
  23. #include "engine/IEngineSound.h"
  24. #include "hl1_grenade_spit.h"
  25. #include "util.h"
  26. #include "shake.h"
  27. #include "movevars_shared.h"
  28. #include "decals.h"
  29. #include "hl2_shareddefs.h"
  30. #include "ammodef.h"
  31. #define SQUID_SPRINT_DIST 256 // how close the squid has to get before starting to sprint and refusing to swerve
  32. ConVar sk_bullsquid_health ( "sk_bullsquid_health", "0" );
  33. ConVar sk_bullsquid_dmg_bite ( "sk_bullsquid_dmg_bite", "0" );
  34. ConVar sk_bullsquid_dmg_whip ( "sk_bullsquid_dmg_whip", "0" );
  35. extern ConVar sk_bullsquid_dmg_spit;
  36. //=========================================================
  37. // monster-specific schedule types
  38. //=========================================================
  39. enum
  40. {
  41. SCHED_SQUID_HURTHOP = LAST_SHARED_SCHEDULE,
  42. SCHED_SQUID_SEECRAB,
  43. SCHED_SQUID_EAT,
  44. SCHED_SQUID_SNIFF_AND_EAT,
  45. SCHED_SQUID_WALLOW,
  46. SCHED_SQUID_CHASE_ENEMY,
  47. };
  48. //=========================================================
  49. // monster-specific tasks
  50. //=========================================================
  51. enum
  52. {
  53. TASK_SQUID_HOPTURN = LAST_SHARED_TASK,
  54. TASK_SQUID_EAT,
  55. };
  56. //-----------------------------------------------------------------------------
  57. // Squid Conditions
  58. //-----------------------------------------------------------------------------
  59. enum
  60. {
  61. COND_SQUID_SMELL_FOOD = LAST_SHARED_CONDITION + 1,
  62. };
  63. //=========================================================
  64. // Monster's Anim Events Go Here
  65. //=========================================================
  66. #define BSQUID_AE_SPIT ( 1 )
  67. #define BSQUID_AE_BITE ( 2 )
  68. #define BSQUID_AE_BLINK ( 3 )
  69. #define BSQUID_AE_TAILWHIP ( 4 )
  70. #define BSQUID_AE_HOP ( 5 )
  71. #define BSQUID_AE_THROW ( 6 )
  72. LINK_ENTITY_TO_CLASS( monster_bullchicken, CNPC_Bullsquid );
  73. int ACT_SQUID_EXCITED;
  74. int ACT_SQUID_EAT;
  75. int ACT_SQUID_DETECT_SCENT;
  76. int ACT_SQUID_INSPECT_FLOOR;
  77. //=========================================================
  78. // Bullsquid's spit projectile
  79. //=========================================================
  80. class CSquidSpit : public CBaseEntity
  81. {
  82. DECLARE_CLASS( CSquidSpit, CBaseEntity );
  83. public:
  84. void Spawn( void );
  85. void Precache( void );
  86. static void Shoot( CBaseEntity *pOwner, Vector vecStart, Vector vecVelocity );
  87. void Touch( CBaseEntity *pOther );
  88. void Animate( void );
  89. int m_nSquidSpitSprite;
  90. DECLARE_DATADESC();
  91. void SetSprite( CBaseEntity *pSprite )
  92. {
  93. m_hSprite = pSprite;
  94. }
  95. CBaseEntity *GetSprite( void )
  96. {
  97. return m_hSprite.Get();
  98. }
  99. private:
  100. EHANDLE m_hSprite;
  101. };
  102. LINK_ENTITY_TO_CLASS( squidspit, CSquidSpit );
  103. BEGIN_DATADESC( CSquidSpit )
  104. DEFINE_FIELD( m_nSquidSpitSprite, FIELD_INTEGER ),
  105. DEFINE_FIELD( m_hSprite, FIELD_EHANDLE ),
  106. END_DATADESC()
  107. void CSquidSpit::Precache( void )
  108. {
  109. m_nSquidSpitSprite = PrecacheModel("sprites/bigspit.vmt");// client side spittle.
  110. PrecacheScriptSound( "NPC_BigMomma.SpitTouch1" );
  111. PrecacheScriptSound( "NPC_BigMomma.SpitHit1" );
  112. PrecacheScriptSound( "NPC_BigMomma.SpitHit2" );
  113. }
  114. void CSquidSpit:: Spawn( void )
  115. {
  116. Precache();
  117. SetMoveType ( MOVETYPE_FLY );
  118. SetClassname( "squidspit" );
  119. SetSolid( SOLID_BBOX );
  120. m_nRenderMode = kRenderTransAlpha;
  121. SetRenderColorA( 255 );
  122. SetModel( "" );
  123. SetSprite( CSprite::SpriteCreate( "sprites/bigspit.vmt", GetAbsOrigin(), true ) );
  124. UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0) );
  125. SetCollisionGroup( HL2COLLISION_GROUP_SPIT );
  126. }
  127. void CSquidSpit::Shoot( CBaseEntity *pOwner, Vector vecStart, Vector vecVelocity )
  128. {
  129. CSquidSpit *pSpit = CREATE_ENTITY( CSquidSpit, "squidspit" );
  130. pSpit->Spawn();
  131. UTIL_SetOrigin( pSpit, vecStart );
  132. pSpit->SetAbsVelocity( vecVelocity );
  133. pSpit->SetOwnerEntity( pOwner );
  134. CSprite *pSprite = (CSprite*)pSpit->GetSprite();
  135. if ( pSprite )
  136. {
  137. pSprite->SetAttachment( pSpit, 0 );
  138. pSprite->SetOwnerEntity( pSpit );
  139. pSprite->SetScale( 0.5 );
  140. pSprite->SetTransparency( pSpit->m_nRenderMode, pSpit->m_clrRender->r, pSpit->m_clrRender->g, pSpit->m_clrRender->b, pSpit->m_clrRender->a, pSpit->m_nRenderFX );
  141. }
  142. CPVSFilter filter( vecStart );
  143. VectorNormalize( vecVelocity );
  144. te->SpriteSpray( filter, 0.0, &vecStart , &vecVelocity, pSpit->m_nSquidSpitSprite, 210, 25, 15 );
  145. }
  146. void CSquidSpit::Touch ( CBaseEntity *pOther )
  147. {
  148. trace_t tr;
  149. int iPitch;
  150. if ( pOther->GetSolidFlags() & FSOLID_TRIGGER )
  151. return;
  152. if ( pOther->GetCollisionGroup() == HL2COLLISION_GROUP_SPIT)
  153. {
  154. return;
  155. }
  156. // splat sound
  157. iPitch = random->RandomFloat( 90, 110 );
  158. EmitSound( "NPC_BigMomma.SpitTouch1" );
  159. switch ( random->RandomInt( 0, 1 ) )
  160. {
  161. case 0:
  162. EmitSound( "NPC_BigMomma.SpitHit1" );
  163. break;
  164. case 1:
  165. EmitSound( "NPC_BigMomma.SpitHit2" );
  166. break;
  167. }
  168. if ( !pOther->m_takedamage )
  169. {
  170. // make a splat on the wall
  171. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 10, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  172. UTIL_DecalTrace(&tr, "BeerSplash" );
  173. // make some flecks
  174. CPVSFilter filter( tr.endpos );
  175. te->SpriteSpray( filter, 0.0, &tr.endpos, &tr.plane.normal, m_nSquidSpitSprite, 30, 8, 5 );
  176. }
  177. else
  178. {
  179. CTakeDamageInfo info( this, this, sk_bullsquid_dmg_spit.GetFloat(), DMG_BULLET );
  180. CalculateBulletDamageForce( &info, GetAmmoDef()->Index("9mmRound"), GetAbsVelocity(), GetAbsOrigin() );
  181. pOther->TakeDamage( info );
  182. }
  183. UTIL_Remove( m_hSprite );
  184. UTIL_Remove( this );
  185. }
  186. BEGIN_DATADESC( CNPC_Bullsquid )
  187. DEFINE_FIELD( m_fCanThreatDisplay, FIELD_BOOLEAN ),
  188. DEFINE_FIELD( m_flLastHurtTime, FIELD_TIME ),
  189. DEFINE_FIELD( m_flNextSpitTime, FIELD_TIME ),
  190. DEFINE_FIELD( m_flHungryTime, FIELD_TIME ),
  191. END_DATADESC()
  192. //=========================================================
  193. // Spawn
  194. //=========================================================
  195. void CNPC_Bullsquid::Spawn()
  196. {
  197. Precache( );
  198. SetModel( "models/bullsquid.mdl");
  199. SetHullType(HULL_WIDE_SHORT);
  200. SetHullSizeNormal();
  201. SetSolid( SOLID_BBOX );
  202. AddSolidFlags( FSOLID_NOT_STANDABLE );
  203. SetMoveType( MOVETYPE_STEP );
  204. m_bloodColor = BLOOD_COLOR_GREEN;
  205. SetRenderColor( 255, 255, 255, 255 );
  206. m_iHealth = sk_bullsquid_health.GetFloat();
  207. m_flFieldOfView = 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  208. m_NPCState = NPC_STATE_NONE;
  209. CapabilitiesClear();
  210. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 );
  211. m_fCanThreatDisplay = TRUE;
  212. m_flNextSpitTime = gpGlobals->curtime;
  213. NPCInit();
  214. m_flDistTooFar = 784;
  215. }
  216. //=========================================================
  217. // Precache - precaches all resources this monster needs
  218. //=========================================================
  219. void CNPC_Bullsquid::Precache()
  220. {
  221. BaseClass::Precache();
  222. PrecacheModel("models/bullsquid.mdl");
  223. PrecacheModel("sprites/bigspit.vmt");// spit projectile.
  224. PrecacheScriptSound( "Bullsquid.Idle" );
  225. PrecacheScriptSound( "Bullsquid.Pain" );
  226. PrecacheScriptSound( "Bullsquid.Alert" );
  227. PrecacheScriptSound( "Bullsquid.Die" );
  228. PrecacheScriptSound( "Bullsquid.Attack" );
  229. PrecacheScriptSound( "Bullsquid.Bite" );
  230. PrecacheScriptSound( "Bullsquid.Growl" );
  231. }
  232. int CNPC_Bullsquid::TranslateSchedule( int scheduleType )
  233. {
  234. switch ( scheduleType )
  235. {
  236. case SCHED_CHASE_ENEMY:
  237. return SCHED_SQUID_CHASE_ENEMY;
  238. break;
  239. }
  240. return BaseClass::TranslateSchedule( scheduleType );
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose: Indicates this monster's place in the relationship table.
  244. // Output :
  245. //-----------------------------------------------------------------------------
  246. Class_T CNPC_Bullsquid::Classify( void )
  247. {
  248. return CLASS_ALIEN_PREDATOR;
  249. }
  250. //=========================================================
  251. // IdleSound
  252. //=========================================================
  253. #define SQUID_ATTN_IDLE (float)1.5
  254. void CNPC_Bullsquid::IdleSound( void )
  255. {
  256. CPASAttenuationFilter filter( this, SQUID_ATTN_IDLE );
  257. EmitSound( filter, entindex(), "Bullsquid.Idle" );
  258. }
  259. //=========================================================
  260. // PainSound
  261. //=========================================================
  262. void CNPC_Bullsquid::PainSound( const CTakeDamageInfo &info )
  263. {
  264. CPASAttenuationFilter filter( this );
  265. EmitSound( filter, entindex(), "Bullsquid.Pain" );
  266. }
  267. //=========================================================
  268. // AlertSound
  269. //=========================================================
  270. void CNPC_Bullsquid::AlertSound( void )
  271. {
  272. CPASAttenuationFilter filter( this );
  273. EmitSound( filter, entindex(), "Bullsquid.Alert" );
  274. }
  275. //=========================================================
  276. // DeathSound
  277. //=========================================================
  278. void CNPC_Bullsquid::DeathSound( const CTakeDamageInfo &info )
  279. {
  280. CPASAttenuationFilter filter( this );
  281. EmitSound( filter, entindex(), "Bullsquid.Die" );
  282. }
  283. //=========================================================
  284. // AttackSound
  285. //=========================================================
  286. void CNPC_Bullsquid::AttackSound( void )
  287. {
  288. CPASAttenuationFilter filter( this );
  289. EmitSound( filter, entindex(), "Bullsquid.Attack" );
  290. }
  291. //=========================================================
  292. // SetYawSpeed - allows each sequence to have a different
  293. // turn rate associated with it.
  294. //=========================================================
  295. float CNPC_Bullsquid::MaxYawSpeed( void )
  296. {
  297. float flYS = 0;
  298. switch ( GetActivity() )
  299. {
  300. case ACT_WALK: flYS = 90; break;
  301. case ACT_RUN: flYS = 90; break;
  302. case ACT_IDLE: flYS = 90; break;
  303. case ACT_RANGE_ATTACK1: flYS = 90; break;
  304. default:
  305. flYS = 90;
  306. break;
  307. }
  308. return flYS;
  309. }
  310. //=========================================================
  311. // HandleAnimEvent - catches the monster-specific messages
  312. // that occur when tagged animation frames are played.
  313. //=========================================================
  314. void CNPC_Bullsquid::HandleAnimEvent( animevent_t *pEvent )
  315. {
  316. switch( pEvent->event )
  317. {
  318. case BSQUID_AE_SPIT:
  319. {
  320. if ( GetEnemy() )
  321. {
  322. Vector vecSpitOffset;
  323. Vector vecSpitDir;
  324. Vector vRight, vUp, vForward;
  325. AngleVectors ( GetAbsAngles(), &vForward, &vRight, &vUp );
  326. // !!!HACKHACK - the spot at which the spit originates (in front of the mouth) was measured in 3ds and hardcoded here.
  327. // we should be able to read the position of bones at runtime for this info.
  328. vecSpitOffset = ( vRight * 8 + vForward * 60 + vUp * 50 );
  329. vecSpitOffset = ( GetAbsOrigin() + vecSpitOffset );
  330. vecSpitDir = ( ( GetEnemy()->BodyTarget( GetAbsOrigin() ) ) - vecSpitOffset );
  331. VectorNormalize( vecSpitDir );
  332. vecSpitDir.x += random->RandomFloat( -0.05, 0.05 );
  333. vecSpitDir.y += random->RandomFloat( -0.05, 0.05 );
  334. vecSpitDir.z += random->RandomFloat( -0.05, 0 );
  335. AttackSound();
  336. CSquidSpit::Shoot( this, vecSpitOffset, vecSpitDir * 900 );
  337. }
  338. }
  339. break;
  340. case BSQUID_AE_BITE:
  341. {
  342. // SOUND HERE!
  343. CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_bite.GetFloat(), DMG_SLASH );
  344. if ( pHurt )
  345. {
  346. Vector forward, up;
  347. AngleVectors( GetAbsAngles(), &forward, NULL, &up );
  348. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - (forward * 100) );
  349. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (up * 100) );
  350. pHurt->SetGroundEntity( NULL );
  351. }
  352. }
  353. break;
  354. case BSQUID_AE_TAILWHIP:
  355. {
  356. CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), sk_bullsquid_dmg_whip.GetFloat(), DMG_SLASH | DMG_ALWAYSGIB );
  357. if ( pHurt )
  358. {
  359. Vector right, up;
  360. AngleVectors( GetAbsAngles(), NULL, &right, &up );
  361. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  362. pHurt->ViewPunch( QAngle( 20, 0, -20 ) );
  363. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (right * 200) );
  364. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (up * 100) );
  365. }
  366. }
  367. break;
  368. case BSQUID_AE_BLINK:
  369. {
  370. // close eye.
  371. m_nSkin = 1;
  372. }
  373. break;
  374. case BSQUID_AE_HOP:
  375. {
  376. float flGravity = GetCurrentGravity();
  377. // throw the squid up into the air on this frame.
  378. if ( GetFlags() & FL_ONGROUND )
  379. {
  380. SetGroundEntity( NULL );
  381. }
  382. // jump into air for 0.8 (24/30) seconds
  383. Vector vecVel = GetAbsVelocity();
  384. vecVel.z += ( 0.625 * flGravity ) * 0.5;
  385. SetAbsVelocity( vecVel );
  386. }
  387. break;
  388. case BSQUID_AE_THROW:
  389. {
  390. // squid throws its prey IF the prey is a client.
  391. CBaseEntity *pHurt = CheckTraceHullAttack( 70, Vector(-16,-16,-16), Vector(16,16,16), 0, 0 );
  392. if ( pHurt )
  393. {
  394. // croonchy bite sound
  395. CPASAttenuationFilter filter( this );
  396. EmitSound( filter, entindex(), "Bullsquid.Bite" );
  397. // screeshake transforms the viewmodel as well as the viewangle. No problems with seeing the ends of the viewmodels.
  398. UTIL_ScreenShake( pHurt->GetAbsOrigin(), 25.0, 1.5, 0.7, 2, SHAKE_START );
  399. if ( pHurt->IsPlayer() )
  400. {
  401. Vector forward, up;
  402. AngleVectors( GetAbsAngles(), &forward, NULL, &up );
  403. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + forward * 300 + up * 300 );
  404. }
  405. }
  406. }
  407. break;
  408. default:
  409. BaseClass::HandleAnimEvent( pEvent );
  410. }
  411. }
  412. int CNPC_Bullsquid::RangeAttack1Conditions( float flDot, float flDist )
  413. {
  414. if ( IsMoving() && flDist >= 512 )
  415. {
  416. // squid will far too far behind if he stops running to spit at this distance from the enemy.
  417. return ( COND_NONE );
  418. }
  419. if ( flDist > 85 && flDist <= 784 && flDot >= 0.5 && gpGlobals->curtime >= m_flNextSpitTime )
  420. {
  421. if ( GetEnemy() != NULL )
  422. {
  423. if ( fabs( GetAbsOrigin().z - GetEnemy()->GetAbsOrigin().z ) > 256 )
  424. {
  425. // don't try to spit at someone up really high or down really low.
  426. return( COND_NONE );
  427. }
  428. }
  429. if ( IsMoving() )
  430. {
  431. // don't spit again for a long time, resume chasing enemy.
  432. m_flNextSpitTime = gpGlobals->curtime + 5;
  433. }
  434. else
  435. {
  436. // not moving, so spit again pretty soon.
  437. m_flNextSpitTime = gpGlobals->curtime + 0.5;
  438. }
  439. return( COND_CAN_RANGE_ATTACK1 );
  440. }
  441. return( COND_NONE );
  442. }
  443. //=========================================================
  444. // MeleeAttack2Conditions - bullsquid is a big guy, so has a longer
  445. // melee range than most monsters. This is the tailwhip attack
  446. //=========================================================
  447. int CNPC_Bullsquid::MeleeAttack1Conditions( float flDot, float flDist )
  448. {
  449. if ( GetEnemy()->m_iHealth <= sk_bullsquid_dmg_whip.GetFloat() && flDist <= 85 && flDot >= 0.7 )
  450. {
  451. return ( COND_CAN_MELEE_ATTACK1 );
  452. }
  453. return( COND_NONE );
  454. }
  455. //=========================================================
  456. // MeleeAttack2Conditions - bullsquid is a big guy, so has a longer
  457. // melee range than most monsters. This is the bite attack.
  458. // this attack will not be performed if the tailwhip attack
  459. // is valid.
  460. //=========================================================
  461. int CNPC_Bullsquid::MeleeAttack2Conditions( float flDot, float flDist )
  462. {
  463. if ( flDist <= 85 && flDot >= 0.7 && !HasCondition( COND_CAN_MELEE_ATTACK1 ) ) // The player & bullsquid can be as much as their bboxes
  464. return ( COND_CAN_MELEE_ATTACK2 );
  465. return( COND_NONE );
  466. }
  467. bool CNPC_Bullsquid::FValidateHintType ( CAI_Hint *pHint )
  468. {
  469. if ( pHint->HintType() == HINT_HL1_WORLD_HUMAN_BLOOD )
  470. return true;
  471. Msg ( "Couldn't validate hint type" );
  472. return false;
  473. }
  474. void CNPC_Bullsquid::RemoveIgnoredConditions( void )
  475. {
  476. if ( m_flHungryTime > gpGlobals->curtime )
  477. ClearCondition( COND_SQUID_SMELL_FOOD );
  478. if ( gpGlobals->curtime - m_flLastHurtTime <= 20 )
  479. {
  480. // haven't been hurt in 20 seconds, so let the squid care about stink.
  481. ClearCondition( COND_SMELL );
  482. }
  483. if ( GetEnemy() != NULL )
  484. {
  485. // ( Unless after a tasty headcrab, yumm ^_^ )
  486. if ( FClassnameIs( GetEnemy(), "monster_headcrab" ) )
  487. ClearCondition( COND_SMELL );
  488. }
  489. }
  490. Disposition_t CNPC_Bullsquid::IRelationType( CBaseEntity *pTarget )
  491. {
  492. if ( gpGlobals->curtime - m_flLastHurtTime < 5 && FClassnameIs ( pTarget, "monster_headcrab" ) )
  493. {
  494. // if squid has been hurt in the last 5 seconds, and is getting relationship for a headcrab,
  495. // tell squid to disregard crab.
  496. return D_NU;
  497. }
  498. return BaseClass::IRelationType ( pTarget );
  499. }
  500. //=========================================================
  501. // TakeDamage - overridden for bullsquid so we can keep track
  502. // of how much time has passed since it was last injured
  503. //=========================================================
  504. int CNPC_Bullsquid::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  505. {
  506. #if 0 //Fix later.
  507. float flDist;
  508. Vector vecApex, vOffset;
  509. // if the squid is running, has an enemy, was hurt by the enemy, hasn't been hurt in the last 3 seconds, and isn't too close to the enemy,
  510. // it will swerve. (whew).
  511. if ( GetEnemy() != NULL && IsMoving() && pevAttacker == GetEnemy() && gpGlobals->curtime - m_flLastHurtTime > 3 )
  512. {
  513. flDist = ( GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ).Length2D();
  514. if ( flDist > SQUID_SPRINT_DIST )
  515. {
  516. AI_Waypoint_t* pRoute = GetNavigator()->GetPath()->Route();
  517. if ( pRoute )
  518. {
  519. flDist = ( GetAbsOrigin() - pRoute[ pRoute->iNodeID ].vecLocation ).Length2D();// reusing flDist.
  520. if ( GetNavigator()->GetPath()->BuildTriangulationRoute( GetAbsOrigin(), pRoute[ pRoute->iNodeID ].vecLocation, flDist * 0.5, GetEnemy(), &vecApex, &vOffset, NAV_GROUND ) )
  521. {
  522. GetNavigator()->PrependWaypoint( vecApex, bits_WP_TO_DETOUR | bits_WP_DONT_SIMPLIFY );
  523. }
  524. }
  525. }
  526. }
  527. #endif
  528. if ( !FClassnameIs ( inputInfo.GetAttacker(), "monster_headcrab" ) )
  529. {
  530. // don't forget about headcrabs if it was a headcrab that hurt the squid.
  531. m_flLastHurtTime = gpGlobals->curtime;
  532. }
  533. return BaseClass::OnTakeDamage_Alive ( inputInfo );
  534. }
  535. //=========================================================
  536. // GetSoundInterests - returns a bit mask indicating which types
  537. // of sounds this monster regards. In the base class implementation,
  538. // monsters care about all sounds, but no scents.
  539. //=========================================================
  540. int CNPC_Bullsquid::GetSoundInterests ( void )
  541. {
  542. return SOUND_WORLD |
  543. SOUND_COMBAT |
  544. SOUND_CARCASS |
  545. SOUND_MEAT |
  546. SOUND_GARBAGE |
  547. SOUND_PLAYER;
  548. }
  549. //=========================================================
  550. // OnListened - monsters dig through the active sound list for
  551. // any sounds that may interest them. (smells, too!)
  552. //=========================================================
  553. void CNPC_Bullsquid::OnListened( void )
  554. {
  555. AISoundIter_t iter;
  556. CSound *pCurrentSound;
  557. static int conditionsToClear[] =
  558. {
  559. COND_SQUID_SMELL_FOOD,
  560. };
  561. ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) );
  562. pCurrentSound = GetSenses()->GetFirstHeardSound( &iter );
  563. while ( pCurrentSound )
  564. {
  565. // the npc cares about this sound, and it's close enough to hear.
  566. int condition = COND_NONE;
  567. if ( !pCurrentSound->FIsSound() )
  568. {
  569. // if not a sound, must be a smell - determine if it's just a scent, or if it's a food scent
  570. if ( pCurrentSound->IsSoundType( SOUND_MEAT | SOUND_CARCASS ) )
  571. {
  572. // the detected scent is a food item
  573. condition = COND_SQUID_SMELL_FOOD;
  574. }
  575. }
  576. if ( condition != COND_NONE )
  577. SetCondition( condition );
  578. pCurrentSound = GetSenses()->GetNextHeardSound( &iter );
  579. }
  580. BaseClass::OnListened();
  581. }
  582. //========================================================
  583. // RunAI - overridden for bullsquid because there are things
  584. // that need to be checked every think.
  585. //========================================================
  586. void CNPC_Bullsquid::RunAI ( void )
  587. {
  588. // first, do base class stuff
  589. BaseClass::RunAI();
  590. if ( m_nSkin != 0 )
  591. {
  592. // close eye if it was open.
  593. m_nSkin = 0;
  594. }
  595. if ( random->RandomInt( 0,39 ) == 0 )
  596. {
  597. m_nSkin = 1;
  598. }
  599. if ( GetEnemy() != NULL && GetActivity() == ACT_RUN )
  600. {
  601. // chasing enemy. Sprint for last bit
  602. if ( (GetAbsOrigin() - GetEnemy()->GetAbsOrigin()).Length2D() < SQUID_SPRINT_DIST )
  603. {
  604. m_flPlaybackRate = 1.25;
  605. }
  606. }
  607. }
  608. //=========================================================
  609. // GetSchedule
  610. //=========================================================
  611. int CNPC_Bullsquid::SelectSchedule( void )
  612. {
  613. switch ( m_NPCState )
  614. {
  615. case NPC_STATE_ALERT:
  616. {
  617. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  618. {
  619. return SCHED_SQUID_HURTHOP;
  620. }
  621. if ( HasCondition( COND_SQUID_SMELL_FOOD ) )
  622. {
  623. CSound *pSound;
  624. pSound = GetBestScent();
  625. if ( pSound && (!FInViewCone ( pSound->GetSoundOrigin() ) || !FVisible ( pSound->GetSoundOrigin() )) )
  626. {
  627. // scent is behind or occluded
  628. return SCHED_SQUID_SNIFF_AND_EAT;
  629. }
  630. // food is right out in the open. Just go get it.
  631. return SCHED_SQUID_EAT;
  632. }
  633. if ( HasCondition( COND_SMELL ) )
  634. {
  635. // there's something stinky.
  636. CSound *pSound;
  637. pSound = GetBestScent();
  638. if ( pSound )
  639. return SCHED_SQUID_WALLOW;
  640. }
  641. break;
  642. }
  643. case NPC_STATE_COMBAT:
  644. {
  645. // dead enemy
  646. if ( HasCondition( COND_ENEMY_DEAD ) )
  647. {
  648. // call base class, all code to handle dead enemies is centralized there.
  649. return BaseClass::SelectSchedule();
  650. }
  651. if ( HasCondition( COND_NEW_ENEMY ) )
  652. {
  653. if ( m_fCanThreatDisplay && IRelationType( GetEnemy() ) == D_HT && FClassnameIs( GetEnemy(), "monster_headcrab" ) )
  654. {
  655. // this means squid sees a headcrab!
  656. m_fCanThreatDisplay = FALSE;// only do the headcrab dance once per lifetime.
  657. return SCHED_SQUID_SEECRAB;
  658. }
  659. else
  660. {
  661. return SCHED_WAKE_ANGRY;
  662. }
  663. }
  664. if ( HasCondition( COND_SQUID_SMELL_FOOD ) )
  665. {
  666. CSound *pSound;
  667. pSound = GetBestScent();
  668. if ( pSound && (!FInViewCone ( pSound->GetSoundOrigin() ) || !FVisible ( pSound->GetSoundOrigin() )) )
  669. {
  670. // scent is behind or occluded
  671. return SCHED_SQUID_SNIFF_AND_EAT;
  672. }
  673. // food is right out in the open. Just go get it.
  674. return SCHED_SQUID_EAT;
  675. }
  676. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  677. {
  678. return SCHED_RANGE_ATTACK1;
  679. }
  680. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  681. {
  682. return SCHED_MELEE_ATTACK1;
  683. }
  684. if ( HasCondition( COND_CAN_MELEE_ATTACK2 ) )
  685. {
  686. return SCHED_MELEE_ATTACK2;
  687. }
  688. return SCHED_CHASE_ENEMY;
  689. break;
  690. }
  691. }
  692. return BaseClass::SelectSchedule();
  693. }
  694. //=========================================================
  695. // FInViewCone - returns true is the passed vector is in
  696. // the caller's forward view cone. The dot product is performed
  697. // in 2d, making the view cone infinitely tall.
  698. //=========================================================
  699. bool CNPC_Bullsquid::FInViewCone ( Vector pOrigin )
  700. {
  701. Vector los = ( pOrigin - GetAbsOrigin() );
  702. // do this in 2D
  703. los.z = 0;
  704. VectorNormalize( los );
  705. Vector facingDir = EyeDirection2D( );
  706. float flDot = DotProduct( los, facingDir );
  707. if ( flDot > m_flFieldOfView )
  708. return true;
  709. return false;
  710. }
  711. //=========================================================
  712. // FVisible - returns true if a line can be traced from
  713. // the caller's eyes to the target vector
  714. //=========================================================
  715. bool CNPC_Bullsquid::FVisible ( Vector vecOrigin )
  716. {
  717. trace_t tr;
  718. Vector vecLookerOrigin;
  719. vecLookerOrigin = EyePosition();//look through the caller's 'eyes'
  720. UTIL_TraceLine(vecLookerOrigin, vecOrigin, MASK_BLOCKLOS, this/*pentIgnore*/, COLLISION_GROUP_NONE, &tr);
  721. if ( tr.fraction != 1.0 )
  722. return false; // Line of sight is not established
  723. else
  724. return true;// line of sight is valid.
  725. }
  726. //=========================================================
  727. // Start task - selects the correct activity and performs
  728. // any necessary calculations to start the next task on the
  729. // schedule. OVERRIDDEN for bullsquid because it needs to
  730. // know explicitly when the last attempt to chase the enemy
  731. // failed, since that impacts its attack choices.
  732. //=========================================================
  733. void CNPC_Bullsquid::StartTask ( const Task_t *pTask )
  734. {
  735. switch ( pTask->iTask )
  736. {
  737. case TASK_MELEE_ATTACK2:
  738. {
  739. CPASAttenuationFilter filter( this );
  740. EmitSound( filter, entindex(), "Bullsquid.Growl" );
  741. BaseClass::StartTask ( pTask );
  742. break;
  743. }
  744. case TASK_SQUID_HOPTURN:
  745. {
  746. SetActivity ( ACT_HOP );
  747. if ( GetEnemy() )
  748. {
  749. Vector vecFacing = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
  750. VectorNormalize( vecFacing );
  751. GetMotor()->SetIdealYaw( vecFacing );
  752. }
  753. break;
  754. }
  755. case TASK_SQUID_EAT:
  756. {
  757. m_flHungryTime = gpGlobals->curtime + pTask->flTaskData;
  758. break;
  759. }
  760. default:
  761. {
  762. BaseClass::StartTask ( pTask );
  763. break;
  764. }
  765. }
  766. }
  767. //=========================================================
  768. // RunTask
  769. //=========================================================
  770. void CNPC_Bullsquid::RunTask ( const Task_t *pTask )
  771. {
  772. switch ( pTask->iTask )
  773. {
  774. case TASK_SQUID_HOPTURN:
  775. {
  776. if ( GetEnemy() )
  777. {
  778. Vector vecFacing = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
  779. VectorNormalize( vecFacing );
  780. GetMotor()->SetIdealYaw( vecFacing );
  781. }
  782. if ( IsSequenceFinished() )
  783. {
  784. TaskComplete();
  785. }
  786. break;
  787. }
  788. default:
  789. {
  790. BaseClass::RunTask( pTask );
  791. break;
  792. }
  793. }
  794. }
  795. //=========================================================
  796. // GetIdealState - Overridden for Bullsquid to deal with
  797. // the feature that makes it lose interest in headcrabs for
  798. // a while if something injures it.
  799. //=========================================================
  800. NPC_STATE CNPC_Bullsquid::SelectIdealState ( void )
  801. {
  802. // If no schedule conditions, the new ideal state is probably the reason we're in here.
  803. switch ( m_NPCState )
  804. {
  805. case NPC_STATE_COMBAT:
  806. /*
  807. COMBAT goes to ALERT upon death of enemy
  808. */
  809. {
  810. if ( GetEnemy() != NULL && ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition ( COND_HEAVY_DAMAGE ) ) && FClassnameIs( GetEnemy(), "monster_headcrab" ) )
  811. {
  812. // if the squid has a headcrab enemy and something hurts it, it's going to forget about the crab for a while.
  813. SetEnemy( NULL );
  814. return NPC_STATE_ALERT;
  815. }
  816. break;
  817. }
  818. }
  819. return BaseClass::SelectIdealState();
  820. }
  821. //------------------------------------------------------------------------------
  822. //
  823. // Schedules
  824. //
  825. //------------------------------------------------------------------------------
  826. AI_BEGIN_CUSTOM_NPC( monster_bullchicken, CNPC_Bullsquid )
  827. DECLARE_TASK ( TASK_SQUID_HOPTURN )
  828. DECLARE_TASK ( TASK_SQUID_EAT )
  829. DECLARE_CONDITION( COND_SQUID_SMELL_FOOD )
  830. DECLARE_ACTIVITY( ACT_SQUID_EXCITED )
  831. DECLARE_ACTIVITY( ACT_SQUID_EAT )
  832. DECLARE_ACTIVITY( ACT_SQUID_DETECT_SCENT )
  833. DECLARE_ACTIVITY( ACT_SQUID_INSPECT_FLOOR )
  834. //=========================================================
  835. // > SCHED_SQUID_HURTHOP
  836. //=========================================================
  837. DEFINE_SCHEDULE
  838. (
  839. SCHED_SQUID_HURTHOP,
  840. " Tasks"
  841. " TASK_STOP_MOVING 0"
  842. " TASK_SOUND_WAKE 0"
  843. " TASK_SQUID_HOPTURN 0"
  844. " TASK_FACE_ENEMY 0"
  845. " "
  846. " Interrupts"
  847. )
  848. //=========================================================
  849. // > SCHED_SQUID_SEECRAB
  850. //=========================================================
  851. DEFINE_SCHEDULE
  852. (
  853. SCHED_SQUID_SEECRAB,
  854. " Tasks"
  855. " TASK_STOP_MOVING 0"
  856. " TASK_SOUND_WAKE 0"
  857. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_EXCITED"
  858. " TASK_FACE_ENEMY 0"
  859. " "
  860. " Interrupts"
  861. " COND_LIGHT_DAMAGE"
  862. " COND_HEAVY_DAMAGE"
  863. )
  864. //=========================================================
  865. // > SCHED_SQUID_EAT
  866. //=========================================================
  867. DEFINE_SCHEDULE
  868. (
  869. SCHED_SQUID_EAT,
  870. " Tasks"
  871. " TASK_STOP_MOVING 0"
  872. " TASK_SQUID_EAT 10"
  873. " TASK_STORE_LASTPOSITION 0"
  874. " TASK_GET_PATH_TO_BESTSCENT 0"
  875. " TASK_WALK_PATH 0"
  876. " TASK_WAIT_FOR_MOVEMENT 0"
  877. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_EAT"
  878. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_EAT"
  879. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_EAT"
  880. " TASK_SQUID_EAT 50"
  881. " TASK_GET_PATH_TO_LASTPOSITION 0"
  882. " TASK_WALK_PATH 0"
  883. " TASK_WAIT_FOR_MOVEMENT 0"
  884. " TASK_CLEAR_LASTPOSITION 0"
  885. " "
  886. " Interrupts"
  887. " COND_LIGHT_DAMAGE"
  888. " COND_HEAVY_DAMAGE"
  889. " COND_NEW_ENEMY"
  890. " COND_SMELL"
  891. )
  892. //=========================================================
  893. // > SCHED_SQUID_SNIFF_AND_EAT
  894. //=========================================================
  895. DEFINE_SCHEDULE
  896. (
  897. SCHED_SQUID_SNIFF_AND_EAT,
  898. " Tasks"
  899. " TASK_STOP_MOVING 0"
  900. " TASK_SQUID_EAT 10"
  901. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_DETECT_SCENT"
  902. " TASK_STORE_LASTPOSITION 0"
  903. " TASK_GET_PATH_TO_BESTSCENT 0"
  904. " TASK_WALK_PATH 0"
  905. " TASK_WAIT_FOR_MOVEMENT 0"
  906. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_EAT"
  907. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_EAT"
  908. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_EAT"
  909. " TASK_SQUID_EAT 50"
  910. " TASK_GET_PATH_TO_LASTPOSITION 0"
  911. " TASK_WALK_PATH 0"
  912. " TASK_WAIT_FOR_MOVEMENT 0"
  913. " TASK_CLEAR_LASTPOSITION 0"
  914. " "
  915. " Interrupts"
  916. " COND_LIGHT_DAMAGE"
  917. " COND_HEAVY_DAMAGE"
  918. " COND_NEW_ENEMY"
  919. " COND_SMELL"
  920. )
  921. //=========================================================
  922. // > SCHED_SQUID_WALLOW
  923. //=========================================================
  924. DEFINE_SCHEDULE
  925. (
  926. SCHED_SQUID_WALLOW,
  927. " Tasks"
  928. " TASK_STOP_MOVING 0"
  929. " TASK_SQUID_EAT 10"
  930. " TASK_STORE_LASTPOSITION 0"
  931. " TASK_GET_PATH_TO_BESTSCENT 0"
  932. " TASK_WALK_PATH 0"
  933. " TASK_WAIT_FOR_MOVEMENT 0"
  934. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_SQUID_INSPECT_FLOOR"
  935. " TASK_SQUID_EAT 50"
  936. " TASK_GET_PATH_TO_LASTPOSITION 0"
  937. " TASK_WALK_PATH 0"
  938. " TASK_WAIT_FOR_MOVEMENT 0"
  939. " TASK_CLEAR_LASTPOSITION 0"
  940. " "
  941. " Interrupts"
  942. " COND_LIGHT_DAMAGE"
  943. " COND_HEAVY_DAMAGE"
  944. " COND_NEW_ENEMY"
  945. )
  946. //=========================================================
  947. // > SCHED_SQUID_CHASE_ENEMY
  948. //=========================================================
  949. DEFINE_SCHEDULE
  950. (
  951. SCHED_SQUID_CHASE_ENEMY,
  952. " Tasks"
  953. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_RANGE_ATTACK1"
  954. " TASK_GET_PATH_TO_ENEMY 0"
  955. " TASK_RUN_PATH 0"
  956. " TASK_WAIT_FOR_MOVEMENT 0"
  957. " "
  958. " Interrupts"
  959. " COND_LIGHT_DAMAGE"
  960. " COND_HEAVY_DAMAGE"
  961. " COND_NEW_ENEMY"
  962. " COND_ENEMY_DEAD"
  963. " COND_SMELL"
  964. " COND_CAN_RANGE_ATTACK1"
  965. " COND_CAN_MELEE_ATTACK1"
  966. " COND_CAN_MELEE_ATTACK2"
  967. " COND_TASK_FAILED"
  968. )
  969. AI_END_CUSTOM_NPC()