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.

1313 lines
31 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "ai_default.h"
  10. #include "ai_task.h"
  11. #include "ai_schedule.h"
  12. #include "ai_node.h"
  13. #include "ai_hull.h"
  14. #include "ai_hint.h"
  15. #include "ai_memory.h"
  16. #include "ai_route.h"
  17. #include "ai_motor.h"
  18. #include "soundent.h"
  19. #include "game.h"
  20. #include "npcevent.h"
  21. #include "entitylist.h"
  22. #include "activitylist.h"
  23. #include "animation.h"
  24. #include "basecombatweapon.h"
  25. #include "IEffects.h"
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "ammodef.h"
  29. #include "hl1_ai_basenpc.h"
  30. #include "ai_navigator.h"
  31. #include "decals.h"
  32. #include "effect_dispatch_data.h"
  33. #include "te_effect_dispatch.h"
  34. #include "Sprite.h"
  35. ConVar sk_bigmomma_health_factor( "sk_bigmomma_health_factor", "1" );
  36. ConVar sk_bigmomma_dmg_slash( "sk_bigmomma_dmg_slash", "50" );
  37. ConVar sk_bigmomma_dmg_blast( "sk_bigmomma_dmg_blast", "100" );
  38. ConVar sk_bigmomma_radius_blast( "sk_bigmomma_radius_blast", "250" );
  39. float GetCurrentGravity( void );
  40. //=========================================================
  41. // Monster's Anim Events Go Here
  42. //=========================================================
  43. #define BIG_AE_STEP1 1 // Footstep left
  44. #define BIG_AE_STEP2 2 // Footstep right
  45. #define BIG_AE_STEP3 3 // Footstep back left
  46. #define BIG_AE_STEP4 4 // Footstep back right
  47. #define BIG_AE_SACK 5 // Sack slosh
  48. #define BIG_AE_DEATHSOUND 6 // Death sound
  49. #define BIG_AE_MELEE_ATTACKBR 8 // Leg attack
  50. #define BIG_AE_MELEE_ATTACKBL 9 // Leg attack
  51. #define BIG_AE_MELEE_ATTACK1 10 // Leg attack
  52. #define BIG_AE_MORTAR_ATTACK1 11 // Launch a mortar
  53. #define BIG_AE_LAY_CRAB 12 // Lay a headcrab
  54. #define BIG_AE_JUMP_FORWARD 13 // Jump up and forward
  55. #define BIG_AE_SCREAM 14 // alert sound
  56. #define BIG_AE_PAIN_SOUND 15 // pain sound
  57. #define BIG_AE_ATTACK_SOUND 16 // attack sound
  58. #define BIG_AE_BIRTH_SOUND 17 // birth sound
  59. #define BIG_AE_EARLY_TARGET 50 // Fire target early
  60. enum
  61. {
  62. SCHED_NODE_FAIL = LAST_SHARED_SCHEDULE,
  63. SCHED_BIG_NODE,
  64. };
  65. enum
  66. {
  67. TASK_MOVE_TO_NODE_RANGE = LAST_SHARED_TASK, // Move within node range
  68. TASK_FIND_NODE, // Find my next node
  69. TASK_PLAY_NODE_PRESEQUENCE, // Play node pre-script
  70. TASK_PLAY_NODE_SEQUENCE, // Play node script
  71. TASK_PROCESS_NODE, // Fire targets, etc.
  72. TASK_WAIT_NODE, // Wait at the node
  73. TASK_NODE_DELAY, // Delay walking toward node for a bit. You've failed to get there
  74. TASK_NODE_YAW, // Get the best facing direction for this node
  75. TASK_CHECK_NODE_PROXIMITY,
  76. };
  77. // User defined conditions
  78. //#define bits_COND_NODE_SEQUENCE ( COND_SPECIAL1 ) // pev->netname contains the name of a sequence to play
  79. // Attack distance constants
  80. #define BIG_ATTACKDIST 170
  81. #define BIG_MORTARDIST 800
  82. #define BIG_MAXCHILDREN 6 // Max # of live headcrab children
  83. #define bits_MEMORY_CHILDPAIR (bits_MEMORY_CUSTOM1)
  84. #define bits_MEMORY_ADVANCE_NODE (bits_MEMORY_CUSTOM2)
  85. #define bits_MEMORY_COMPLETED_NODE (bits_MEMORY_CUSTOM3)
  86. #define bits_MEMORY_FIRED_NODE (bits_MEMORY_CUSTOM4)
  87. int gSpitSprite, gSpitDebrisSprite;
  88. Vector VecCheckSplatToss( CBaseEntity *pEntity, const Vector &vecSpot1, Vector vecSpot2, float maxHeight );
  89. void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count );
  90. #define SF_INFOBM_RUN 0x0001
  91. #define SF_INFOBM_WAIT 0x0002
  92. //=========================================================
  93. // Mortar shot entity
  94. //=========================================================
  95. class CBMortar : public CBaseAnimating
  96. {
  97. DECLARE_CLASS( CBMortar, CBaseAnimating );
  98. public:
  99. void Spawn( void );
  100. virtual void Precache();
  101. static CBMortar *Shoot( CBaseEntity *pOwner, Vector vecStart, Vector vecVelocity );
  102. void Touch( CBaseEntity *pOther );
  103. void Animate( void );
  104. float m_flDmgTime;
  105. DECLARE_DATADESC();
  106. int m_maxFrame;
  107. int m_iFrame;
  108. CSprite* pSprite;
  109. };
  110. LINK_ENTITY_TO_CLASS( bmortar, CBMortar );
  111. BEGIN_DATADESC( CBMortar )
  112. DEFINE_FIELD( m_maxFrame, FIELD_INTEGER ),
  113. DEFINE_FIELD( m_flDmgTime, FIELD_FLOAT ),
  114. DEFINE_FUNCTION( Animate ),
  115. DEFINE_FIELD( m_iFrame, FIELD_INTEGER ),
  116. DEFINE_FIELD( pSprite, FIELD_CLASSPTR ),
  117. END_DATADESC()
  118. // AI Nodes for Big Momma
  119. class CInfoBM : public CPointEntity
  120. {
  121. DECLARE_CLASS( CInfoBM, CPointEntity );
  122. public:
  123. void Spawn( void );
  124. bool KeyValue( const char *szKeyName, const char *szValue );
  125. // name in pev->targetname
  126. // next in pev->target
  127. // radius in pev->scale
  128. // health in pev->health
  129. // Reach target in pev->message
  130. // Reach delay in pev->speed
  131. // Reach sequence in pev->netname
  132. DECLARE_DATADESC();
  133. float m_flRadius;
  134. float m_flDelay;
  135. string_t m_iszReachTarget;
  136. string_t m_iszReachSequence;
  137. string_t m_iszPreSequence;
  138. COutputEvent m_OnAnimationEvent;
  139. };
  140. LINK_ENTITY_TO_CLASS( info_bigmomma, CInfoBM );
  141. BEGIN_DATADESC( CInfoBM )
  142. DEFINE_FIELD( m_flRadius, FIELD_FLOAT ),
  143. DEFINE_FIELD( m_flDelay, FIELD_FLOAT ),
  144. DEFINE_KEYFIELD( m_iszReachTarget, FIELD_STRING, "reachtarget" ),
  145. DEFINE_KEYFIELD( m_iszReachSequence, FIELD_STRING, "reachsequence" ),
  146. DEFINE_KEYFIELD( m_iszPreSequence, FIELD_STRING, "presequence" ),
  147. DEFINE_OUTPUT( m_OnAnimationEvent, "OnAnimationEvent" ),
  148. END_DATADESC()
  149. void CInfoBM::Spawn( void )
  150. {
  151. BaseClass::Spawn();
  152. // Msg( "Name %s\n", STRING( GetEntityName() ) );
  153. }
  154. bool CInfoBM::KeyValue( const char *szKeyName, const char *szValue )
  155. {
  156. if (FStrEq( szKeyName, "radius"))
  157. {
  158. m_flRadius = atof( szValue );
  159. return true;
  160. }
  161. else if (FStrEq( szKeyName, "reachdelay" ))
  162. {
  163. m_flDelay = atof( szValue);
  164. return true;
  165. }
  166. else if (FStrEq( szKeyName, "health" ))
  167. {
  168. m_iHealth = atoi( szValue );
  169. return true;
  170. }
  171. return BaseClass::KeyValue(szKeyName, szValue );
  172. }
  173. // UNDONE:
  174. //
  175. #define BIG_CHILDCLASS "monster_babycrab"
  176. class CNPC_BigMomma : public CHL1BaseNPC
  177. {
  178. DECLARE_CLASS( CNPC_BigMomma, CHL1BaseNPC );
  179. public:
  180. void Spawn( void );
  181. void Precache( void );
  182. Class_T Classify( void ) { return CLASS_ALIEN_MONSTER; };
  183. void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  184. int OnTakeDamage( const CTakeDamageInfo &info );
  185. void HandleAnimEvent( animevent_t *pEvent );
  186. void LayHeadcrab( void );
  187. void LaunchMortar( void );
  188. void DeathNotice( CBaseEntity *pevChild );
  189. int MeleeAttack1Conditions( float flDot, float flDist ); // Slash
  190. int MeleeAttack2Conditions( float flDot, float flDist ); // Lay a crab
  191. int RangeAttack1Conditions( float flDot, float flDist ); // Mortar launch
  192. BOOL CanLayCrab( void )
  193. {
  194. if ( m_crabTime < gpGlobals->curtime && m_crabCount < BIG_MAXCHILDREN )
  195. {
  196. // Don't spawn crabs inside each other
  197. Vector mins = GetAbsOrigin() - Vector( 32, 32, 0 );
  198. Vector maxs = GetAbsOrigin() + Vector( 32, 32, 0 );
  199. CBaseEntity *pList[2];
  200. int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_NPC );
  201. for ( int i = 0; i < count; i++ )
  202. {
  203. if ( pList[i] != this ) // Don't hurt yourself!
  204. return COND_NONE;
  205. }
  206. return COND_CAN_MELEE_ATTACK2;
  207. }
  208. return COND_NONE;
  209. }
  210. void Activate ( void );
  211. void NodeReach( void );
  212. void NodeStart( string_t iszNextNode );
  213. bool ShouldGoToNode( void );
  214. const char *GetNodeSequence( void )
  215. {
  216. CInfoBM *pTarget = (CInfoBM*)GetTarget();
  217. if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) )
  218. {
  219. return STRING( pTarget->m_iszReachSequence ); // netname holds node sequence
  220. }
  221. return NULL;
  222. }
  223. const char *GetNodePresequence( void )
  224. {
  225. CInfoBM *pTarget = (CInfoBM *)GetTarget();
  226. if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) )
  227. {
  228. return STRING( pTarget->m_iszPreSequence );
  229. }
  230. return NULL;
  231. }
  232. float GetNodeDelay( void )
  233. {
  234. CInfoBM *pTarget = (CInfoBM *)GetTarget();
  235. if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) )
  236. {
  237. return pTarget->m_flDelay; // Speed holds node delay
  238. }
  239. return 0;
  240. }
  241. float GetNodeRange( void )
  242. {
  243. CInfoBM *pTarget = (CInfoBM *)GetTarget();
  244. if ( pTarget && FClassnameIs( pTarget, "info_bigmomma" ) )
  245. {
  246. return pTarget->m_flRadius; // Scale holds node delay
  247. }
  248. return 1e6;
  249. }
  250. float GetNodeYaw( void )
  251. {
  252. CBaseEntity *pTarget = GetTarget();
  253. if ( pTarget )
  254. {
  255. if ( pTarget->GetAbsAngles().y != 0 )
  256. return pTarget->GetAbsAngles().y;
  257. }
  258. return GetAbsAngles().y;
  259. }
  260. // Restart the crab count on each new level
  261. void OnRestore( void )
  262. {
  263. BaseClass::OnRestore();
  264. m_crabCount = 0;
  265. }
  266. int SelectSchedule( void );
  267. void StartTask( const Task_t *pTask );
  268. void RunTask( const Task_t *pTask );
  269. float MaxYawSpeed( void );
  270. DECLARE_DATADESC();
  271. DEFINE_CUSTOM_AI;
  272. /*
  273. void RunTask( Task_t *pTask );
  274. void StartTask( Task_t *pTask );
  275. Schedule_t *GetSchedule( void );
  276. Schedule_t *GetScheduleOfType( int Type );
  277. void SetYawSpeed( void );
  278. CUSTOM_SCHEDULES;
  279. */
  280. private:
  281. float m_nodeTime;
  282. float m_crabTime;
  283. float m_mortarTime;
  284. float m_painSoundTime;
  285. int m_crabCount;
  286. float m_flDmgTime;
  287. bool m_bDoneWithPath;
  288. string_t m_iszTarget;
  289. string_t m_iszNetName;
  290. float m_flWait;
  291. Vector m_vTossDir;
  292. };
  293. BEGIN_DATADESC( CNPC_BigMomma )
  294. DEFINE_FIELD( m_nodeTime, FIELD_TIME ),
  295. DEFINE_FIELD( m_crabTime, FIELD_TIME ),
  296. DEFINE_FIELD( m_mortarTime, FIELD_TIME ),
  297. DEFINE_FIELD( m_painSoundTime, FIELD_TIME ),
  298. DEFINE_KEYFIELD( m_iszNetName, FIELD_STRING, "netname" ),
  299. DEFINE_FIELD( m_flWait, FIELD_TIME ),
  300. DEFINE_FIELD( m_iszTarget, FIELD_STRING ),
  301. DEFINE_FIELD( m_crabCount, FIELD_INTEGER ),
  302. DEFINE_FIELD( m_flDmgTime, FIELD_TIME ),
  303. DEFINE_FIELD( m_bDoneWithPath, FIELD_BOOLEAN ),
  304. DEFINE_FIELD( m_vTossDir, FIELD_VECTOR ),
  305. END_DATADESC()
  306. LINK_ENTITY_TO_CLASS ( monster_bigmomma, CNPC_BigMomma );
  307. //=========================================================
  308. // Spawn
  309. //=========================================================
  310. void CNPC_BigMomma::Spawn()
  311. {
  312. Precache( );
  313. SetModel( "models/big_mom.mdl" );
  314. UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
  315. Vector vecSurroundingMins( -95, -95, 0 );
  316. Vector vecSurroundingMaxs( 95, 95, 190 );
  317. CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs );
  318. SetNavType( NAV_GROUND );
  319. SetSolid( SOLID_BBOX );
  320. AddSolidFlags( FSOLID_NOT_STANDABLE );
  321. SetMoveType( MOVETYPE_STEP );
  322. m_bloodColor = BLOOD_COLOR_GREEN;
  323. m_iHealth = 150 * sk_bigmomma_health_factor.GetFloat();
  324. SetHullType( HULL_WIDE_HUMAN );
  325. SetHullSizeNormal();
  326. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  327. CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_MELEE_ATTACK2 );
  328. // pev->view_ofs = Vector ( 0, 0, 128 );// position of the eyes relative to monster's origin.
  329. m_flFieldOfView = 0.3;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  330. m_NPCState = NPC_STATE_NONE;
  331. SetRenderColor( 255, 255, 255, 255 );
  332. m_bDoneWithPath = false;
  333. m_nodeTime = 0.0f;
  334. m_iszTarget = m_iszNetName;
  335. NPCInit();
  336. BaseClass::Spawn();
  337. }
  338. //=========================================================
  339. // Precache - precaches all resources this monster needs
  340. //=========================================================
  341. void CNPC_BigMomma::Precache()
  342. {
  343. PrecacheModel("models/big_mom.mdl");
  344. UTIL_PrecacheOther( BIG_CHILDCLASS );
  345. // TEMP: Squid
  346. PrecacheModel("sprites/mommaspit.vmt");// spit projectile.
  347. gSpitSprite = PrecacheModel("sprites/mommaspout.vmt");// client side spittle.
  348. gSpitDebrisSprite = PrecacheModel("sprites/mommablob.vmt" );
  349. PrecacheScriptSound( "BigMomma.Pain" );
  350. PrecacheScriptSound( "BigMomma.Attack" );
  351. PrecacheScriptSound( "BigMomma.AttackHit" );
  352. PrecacheScriptSound( "BigMomma.Alert" );
  353. PrecacheScriptSound( "BigMomma.Birth" );
  354. PrecacheScriptSound( "BigMomma.Sack" );
  355. PrecacheScriptSound( "BigMomma.Die" );
  356. PrecacheScriptSound( "BigMomma.FootstepLeft" );
  357. PrecacheScriptSound( "BigMomma.FootstepRight" );
  358. PrecacheScriptSound( "BigMomma.LayHeadcrab" );
  359. PrecacheScriptSound( "BigMomma.ChildDie" );
  360. PrecacheScriptSound( "BigMomma.LaunchMortar" );
  361. }
  362. //=========================================================
  363. // SetYawSpeed - allows each sequence to have a different
  364. // turn rate associated with it.
  365. //=========================================================
  366. float CNPC_BigMomma::MaxYawSpeed ( void )
  367. {
  368. float ys = 90.0f;
  369. switch ( GetActivity() )
  370. {
  371. case ACT_IDLE:
  372. ys = 100.0f;
  373. break;
  374. default:
  375. ys = 90.0f;
  376. }
  377. return ys;
  378. }
  379. void CNPC_BigMomma::Activate( void )
  380. {
  381. if ( GetTarget() == NULL )
  382. Remember( bits_MEMORY_ADVANCE_NODE ); // Start 'er up
  383. BaseClass::Activate();
  384. }
  385. void CNPC_BigMomma::NodeStart( string_t iszNextNode )
  386. {
  387. m_iszTarget = iszNextNode;
  388. const char *pTargetName = STRING( m_iszTarget );
  389. CBaseEntity *pTarget = NULL;
  390. if ( pTargetName )
  391. pTarget = gEntList.FindEntityByName( NULL, pTargetName );
  392. if ( pTarget == NULL )
  393. {
  394. //Msg( "BM: Finished the path!!\n" );
  395. m_bDoneWithPath = true;
  396. return;
  397. }
  398. SetTarget( pTarget );
  399. }
  400. void CNPC_BigMomma::NodeReach( void )
  401. {
  402. CInfoBM *pTarget = (CInfoBM*)GetTarget();
  403. Forget( bits_MEMORY_ADVANCE_NODE );
  404. if ( !pTarget )
  405. return;
  406. if ( pTarget->m_iHealth >= 1 )
  407. m_iMaxHealth = m_iHealth = pTarget->m_iHealth * sk_bigmomma_health_factor.GetFloat();
  408. if ( !HasMemory( bits_MEMORY_FIRED_NODE ) )
  409. {
  410. if ( pTarget )
  411. {
  412. pTarget->m_OnAnimationEvent.FireOutput( this, this );
  413. }
  414. }
  415. Forget( bits_MEMORY_FIRED_NODE );
  416. m_iszTarget = pTarget->m_target;
  417. if ( pTarget->m_iHealth == 0 )
  418. Remember( bits_MEMORY_ADVANCE_NODE ); // Move on if no health at this node
  419. else
  420. {
  421. GetNavigator()->ClearGoal();
  422. }
  423. }
  424. void CNPC_BigMomma::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  425. {
  426. CTakeDamageInfo dmgInfo = info;
  427. if ( ptr->hitbox <= 9 )
  428. {
  429. // didn't hit the sack?
  430. if ( m_flDmgTime != gpGlobals->curtime || (random->RandomInt( 0, 10 ) < 1) )
  431. {
  432. g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
  433. m_flDmgTime = gpGlobals->curtime;
  434. }
  435. // don't hurt the monster much, but allow bits_COND_LIGHT_DAMAGE to be generated
  436. dmgInfo.SetDamage( 0.1 );
  437. }
  438. else
  439. {
  440. SpawnBlood( ptr->endpos + ptr->plane.normal * 15, vecDir, m_bloodColor, 100 );
  441. if ( gpGlobals->curtime > m_painSoundTime )
  442. {
  443. m_painSoundTime = gpGlobals->curtime + random->RandomInt(1, 3);
  444. EmitSound( "BigMomma.Pain" );
  445. }
  446. }
  447. BaseClass::TraceAttack( dmgInfo, vecDir, ptr, pAccumulator );
  448. }
  449. int CNPC_BigMomma::OnTakeDamage( const CTakeDamageInfo &info )
  450. {
  451. CTakeDamageInfo newInfo = info;
  452. // Don't take any acid damage -- BigMomma's mortar is acid
  453. if ( newInfo.GetDamageType() & DMG_ACID )
  454. {
  455. newInfo.SetDamage( 0 );
  456. }
  457. // never die from damage, just advance to the next node
  458. if ( ( GetHealth() - newInfo.GetDamage() ) < 1 )
  459. {
  460. newInfo.SetDamage( 0 );
  461. Remember( bits_MEMORY_ADVANCE_NODE );
  462. DevMsg( 2, "BM: Finished node health!!!\n" );
  463. }
  464. DevMsg( 2, "BM Health: %f\n", GetHealth() - newInfo.GetDamage() );
  465. return BaseClass::OnTakeDamage( newInfo );
  466. }
  467. bool CNPC_BigMomma::ShouldGoToNode( void )
  468. {
  469. if ( HasMemory( bits_MEMORY_ADVANCE_NODE ) )
  470. {
  471. if ( m_nodeTime < gpGlobals->curtime )
  472. return true;
  473. }
  474. return false;
  475. }
  476. int CNPC_BigMomma::SelectSchedule( void )
  477. {
  478. if ( ShouldGoToNode() )
  479. {
  480. return SCHED_BIG_NODE;
  481. }
  482. return BaseClass::SelectSchedule();
  483. }
  484. void CNPC_BigMomma::StartTask( const Task_t *pTask )
  485. {
  486. switch ( pTask->iTask )
  487. {
  488. case TASK_CHECK_NODE_PROXIMITY:
  489. {
  490. }
  491. break;
  492. case TASK_FIND_NODE:
  493. {
  494. CBaseEntity *pTarget = GetTarget();
  495. if ( !HasMemory( bits_MEMORY_ADVANCE_NODE ) )
  496. {
  497. if ( pTarget )
  498. m_iszTarget = pTarget->m_target;
  499. }
  500. NodeStart( m_iszTarget );
  501. TaskComplete();
  502. //Msg( "BM: Found node %s\n", STRING( m_iszTarget ) );
  503. }
  504. break;
  505. case TASK_NODE_DELAY:
  506. m_nodeTime = gpGlobals->curtime + pTask->flTaskData;
  507. TaskComplete();
  508. //Msg( "BM: FAIL! Delay %.2f\n", pTask->flTaskData );
  509. break;
  510. case TASK_PROCESS_NODE:
  511. //Msg( "BM: Reached node %s\n", STRING( m_iszTarget ) );
  512. NodeReach();
  513. TaskComplete();
  514. break;
  515. case TASK_PLAY_NODE_PRESEQUENCE:
  516. case TASK_PLAY_NODE_SEQUENCE:
  517. {
  518. const char *pSequence = NULL;
  519. int iSequence;
  520. if ( pTask->iTask == TASK_PLAY_NODE_SEQUENCE )
  521. pSequence = GetNodeSequence();
  522. else
  523. pSequence = GetNodePresequence();
  524. //Msg( "BM: Playing node sequence %s\n", pSequence );
  525. if ( pSequence ) //ugh
  526. {
  527. iSequence = LookupSequence( pSequence );
  528. if ( iSequence != -1 )
  529. {
  530. SetIdealActivity( ACT_DO_NOT_DISTURB );
  531. SetSequence( iSequence );
  532. SetCycle( 0.0f );
  533. ResetSequenceInfo();
  534. //Msg( "BM: Sequence %s %f\n", GetNodeSequence(), gpGlobals->curtime );
  535. return;
  536. }
  537. }
  538. TaskComplete();
  539. }
  540. break;
  541. case TASK_NODE_YAW:
  542. GetMotor()->SetIdealYaw( GetNodeYaw() );
  543. TaskComplete();
  544. break;
  545. case TASK_WAIT_NODE:
  546. m_flWait = gpGlobals->curtime + GetNodeDelay();
  547. /*if ( GetTarget() && GetTarget()->GetSpawnFlags() & SF_INFOBM_WAIT )
  548. Msg( "BM: Wait at node %s forever\n", STRING( m_iszTarget) );
  549. else
  550. Msg( "BM: Wait at node %s for %.2f\n", STRING( m_iszTarget ), GetNodeDelay() );*/
  551. break;
  552. case TASK_MOVE_TO_NODE_RANGE:
  553. {
  554. CBaseEntity *pTarget = GetTarget();
  555. if ( !pTarget )
  556. TaskFail( FAIL_NO_TARGET );
  557. else
  558. {
  559. if ( ( pTarget->GetAbsOrigin() - GetAbsOrigin() ).Length() < GetNodeRange() )
  560. TaskComplete();
  561. else
  562. {
  563. Activity act = ACT_WALK;
  564. if ( pTarget->GetSpawnFlags() & SF_INFOBM_RUN )
  565. act = ACT_RUN;
  566. AI_NavGoal_t goal( GOALTYPE_TARGETENT, vec3_origin, act );
  567. if ( !GetNavigator()->SetGoal( goal ) )
  568. {
  569. TaskFail( NO_TASK_FAILURE );
  570. }
  571. }
  572. }
  573. }
  574. //Msg( "BM: Moving to node %s\n", STRING( m_iszTarget ) );
  575. break;
  576. case TASK_MELEE_ATTACK1:
  577. {
  578. // Play an attack sound here
  579. CPASAttenuationFilter filter( this );
  580. EmitSound( filter, entindex(), "BigMomma.Attack" );
  581. BaseClass::StartTask( pTask );
  582. }
  583. break;
  584. default:
  585. BaseClass::StartTask( pTask );
  586. break;
  587. }
  588. }
  589. //=========================================================
  590. // RunTask
  591. //=========================================================
  592. void CNPC_BigMomma::RunTask( const Task_t *pTask )
  593. {
  594. switch ( pTask->iTask )
  595. {
  596. case TASK_CHECK_NODE_PROXIMITY:
  597. {
  598. float distance;
  599. if ( GetTarget() == NULL )
  600. TaskFail( FAIL_NO_TARGET );
  601. else
  602. {
  603. if ( GetNavigator()->IsGoalActive() )
  604. {
  605. distance = ( GetTarget()->GetAbsOrigin() - GetAbsOrigin() ).Length2D();
  606. // Set the appropriate activity based on an overlapping range
  607. // overlap the range to prevent oscillation
  608. if ( distance < GetNodeRange() )
  609. {
  610. //Msg( "BM: Reached node PROXIMITY!!\n" );
  611. TaskComplete();
  612. GetNavigator()->ClearGoal(); // Stop moving
  613. }
  614. }
  615. else
  616. TaskComplete();
  617. }
  618. }
  619. break;
  620. case TASK_WAIT_NODE:
  621. if ( GetTarget() != NULL && (GetTarget()->GetSpawnFlags() & SF_INFOBM_WAIT) )
  622. return;
  623. if ( gpGlobals->curtime > m_flWaitFinished )
  624. TaskComplete();
  625. //Msg( "BM: The WAIT is over!\n" );
  626. break;
  627. case TASK_PLAY_NODE_PRESEQUENCE:
  628. case TASK_PLAY_NODE_SEQUENCE:
  629. if ( IsSequenceFinished() )
  630. {
  631. CBaseEntity *pTarget = NULL;
  632. if ( GetTarget() )
  633. pTarget = gEntList.FindEntityByName( NULL, STRING( GetTarget()->m_target ) );
  634. if ( pTarget )
  635. {
  636. SetActivity( ACT_IDLE );
  637. TaskComplete();
  638. }
  639. }
  640. break;
  641. default:
  642. BaseClass::RunTask( pTask );
  643. break;
  644. }
  645. }
  646. //=========================================================
  647. // HandleAnimEvent - catches the monster-specific messages
  648. // that occur when tagged animation frames are played.
  649. //
  650. // Returns number of events handled, 0 if none.
  651. //=========================================================
  652. void CNPC_BigMomma::HandleAnimEvent( animevent_t *pEvent )
  653. {
  654. CPASAttenuationFilter filter( this );
  655. Vector vecFwd, vecRight, vecUp;
  656. QAngle angles;
  657. angles = GetAbsAngles();
  658. AngleVectors( angles, &vecFwd, &vecRight, &vecUp );
  659. switch( pEvent->event )
  660. {
  661. case BIG_AE_MELEE_ATTACKBR:
  662. case BIG_AE_MELEE_ATTACKBL:
  663. case BIG_AE_MELEE_ATTACK1:
  664. {
  665. Vector center = GetAbsOrigin() + vecFwd * 128;
  666. Vector mins = center - Vector( 64, 64, 0 );
  667. Vector maxs = center + Vector( 64, 64, 64 );
  668. CBaseEntity *pList[8];
  669. int count = UTIL_EntitiesInBox( pList, 8, mins, maxs, FL_NPC | FL_CLIENT );
  670. CBaseEntity *pHurt = NULL;
  671. for ( int i = 0; i < count && !pHurt; i++ )
  672. {
  673. if ( pList[i] != this )
  674. {
  675. if ( pList[i]->GetOwnerEntity() != this )
  676. {
  677. pHurt = pList[i];
  678. }
  679. }
  680. }
  681. if ( pHurt )
  682. {
  683. CTakeDamageInfo info( this, this, 15, DMG_CLUB | DMG_SLASH );
  684. CalculateMeleeDamageForce( &info, (pHurt->GetAbsOrigin() - GetAbsOrigin()), pHurt->GetAbsOrigin() );
  685. pHurt->TakeDamage( info );
  686. QAngle newAngles = angles;
  687. newAngles.x = 15;
  688. if ( pHurt->IsPlayer() )
  689. {
  690. ((CBasePlayer *)pHurt)->SetPunchAngle( newAngles );
  691. }
  692. switch( pEvent->event )
  693. {
  694. case BIG_AE_MELEE_ATTACKBR:
  695. // pHurt->pev->velocity = pHurt->pev->velocity + (vecFwd * 150) + Vector(0,0,250) - (vecRight * 200);
  696. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (vecFwd * 150) + Vector(0,0,250) - (vecRight * 200) );
  697. break;
  698. case BIG_AE_MELEE_ATTACKBL:
  699. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (vecFwd * 150) + Vector(0,0,250) + (vecRight * 200) );
  700. break;
  701. case BIG_AE_MELEE_ATTACK1:
  702. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() + (vecFwd * 220) + Vector(0,0,200) );
  703. break;
  704. }
  705. pHurt->SetGroundEntity( NULL );
  706. EmitSound( filter, entindex(), "BigMomma.AttackHit" );
  707. }
  708. }
  709. break;
  710. case BIG_AE_SCREAM:
  711. EmitSound( filter, entindex(), "BigMomma.Alert" );
  712. break;
  713. case BIG_AE_PAIN_SOUND:
  714. EmitSound( filter, entindex(), "BigMomma.Pain" );
  715. break;
  716. case BIG_AE_ATTACK_SOUND:
  717. EmitSound( filter, entindex(), "BigMomma.Attack" );
  718. break;
  719. case BIG_AE_BIRTH_SOUND:
  720. EmitSound( filter, entindex(), "BigMomma.Birth" );
  721. break;
  722. case BIG_AE_SACK:
  723. if ( RandomInt(0,100) < 30 )
  724. {
  725. EmitSound( filter, entindex(), "BigMomma.Sack" );
  726. }
  727. break;
  728. case BIG_AE_DEATHSOUND:
  729. EmitSound( filter, entindex(), "BigMomma.Die" );
  730. break;
  731. case BIG_AE_STEP1: // Footstep left
  732. case BIG_AE_STEP3: // Footstep back left
  733. EmitSound( filter, entindex(), "BigMomma.FootstepLeft" );
  734. break;
  735. case BIG_AE_STEP4: // Footstep back right
  736. case BIG_AE_STEP2: // Footstep right
  737. EmitSound( filter, entindex(), "BigMomma.FootstepRight" );
  738. break;
  739. case BIG_AE_MORTAR_ATTACK1:
  740. LaunchMortar();
  741. break;
  742. case BIG_AE_LAY_CRAB:
  743. LayHeadcrab();
  744. break;
  745. case BIG_AE_JUMP_FORWARD:
  746. SetGroundEntity( NULL );
  747. SetAbsOrigin(GetAbsOrigin() + Vector ( 0 , 0 , 1) );// take him off ground so engine doesn't instantly reset onground
  748. SetAbsVelocity(vecFwd * 200 + vecUp * 500 );
  749. break;
  750. case BIG_AE_EARLY_TARGET:
  751. {
  752. CInfoBM *pTarget = (CInfoBM*) GetTarget();
  753. if ( pTarget )
  754. {
  755. pTarget->m_OnAnimationEvent.FireOutput( this, this );
  756. }
  757. Remember( bits_MEMORY_FIRED_NODE );
  758. }
  759. break;
  760. default:
  761. BaseClass::HandleAnimEvent( pEvent );
  762. break;
  763. }
  764. }
  765. void CNPC_BigMomma::LayHeadcrab( void )
  766. {
  767. CBaseEntity *pChild = CBaseEntity::Create( BIG_CHILDCLASS, GetAbsOrigin(), GetAbsAngles(), this );
  768. pChild->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
  769. pChild->SetOwnerEntity( this );
  770. // Is this the second crab in a pair?
  771. if ( HasMemory( bits_MEMORY_CHILDPAIR ) )
  772. {
  773. m_crabTime = gpGlobals->curtime + RandomFloat( 5, 10 );
  774. Forget( bits_MEMORY_CHILDPAIR );
  775. }
  776. else
  777. {
  778. m_crabTime = gpGlobals->curtime + RandomFloat( 0.5, 2.5 );
  779. Remember( bits_MEMORY_CHILDPAIR );
  780. }
  781. trace_t tr;
  782. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector(0,0,100), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  783. UTIL_DecalTrace( &tr, "Splash" );
  784. CPASAttenuationFilter filter( this );
  785. EmitSound( filter, entindex(), "BigMomma.LayHeadcrab" );
  786. m_crabCount++;
  787. }
  788. void CNPC_BigMomma::DeathNotice( CBaseEntity *pevChild )
  789. {
  790. if ( m_crabCount > 0 ) // Some babies may cross a transition, but we reset the count then
  791. {
  792. m_crabCount--;
  793. }
  794. if ( IsAlive() )
  795. {
  796. // Make the "my baby's dead" noise!
  797. CPASAttenuationFilter filter( this );
  798. EmitSound( filter, entindex(), "BigMomma.ChildDie" );
  799. }
  800. }
  801. void CNPC_BigMomma::LaunchMortar( void )
  802. {
  803. m_mortarTime = gpGlobals->curtime + RandomFloat( 2, 15 );
  804. Vector startPos = GetAbsOrigin();
  805. startPos.z += 180;
  806. CPASAttenuationFilter filter( this );
  807. EmitSound( filter, entindex(), "BigMomma.LaunchMortar" );
  808. CBMortar *pBomb = CBMortar::Shoot( this, startPos, m_vTossDir );
  809. pBomb->SetGravity( 1.0 );
  810. MortarSpray( startPos, Vector(0,0,10), gSpitSprite, 24 );
  811. }
  812. int CNPC_BigMomma::MeleeAttack1Conditions( float flDot, float flDist )
  813. {
  814. if (flDot >= 0.7)
  815. {
  816. if ( flDist > BIG_ATTACKDIST )
  817. return COND_TOO_FAR_TO_ATTACK;
  818. else
  819. return COND_CAN_MELEE_ATTACK1;
  820. }
  821. else
  822. {
  823. return COND_NOT_FACING_ATTACK;
  824. }
  825. return COND_NONE;
  826. }
  827. // Lay a crab
  828. int CNPC_BigMomma::MeleeAttack2Conditions( float flDot, float flDist )
  829. {
  830. return CanLayCrab();
  831. }
  832. Vector VecCheckSplatToss( CBaseEntity *pEnt, const Vector &vecSpot1, Vector vecSpot2, float maxHeight )
  833. {
  834. trace_t tr;
  835. Vector vecMidPoint;// halfway point between Spot1 and Spot2
  836. Vector vecApex;// highest point
  837. Vector vecScale;
  838. Vector vecGrenadeVel;
  839. Vector vecTemp;
  840. float flGravity = GetCurrentGravity();
  841. // calculate the midpoint and apex of the 'triangle'
  842. vecMidPoint = vecSpot1 + (vecSpot2 - vecSpot1) * 0.5;
  843. UTIL_TraceLine(vecMidPoint, vecMidPoint + Vector(0,0,maxHeight), MASK_SOLID_BRUSHONLY, pEnt, COLLISION_GROUP_NONE, &tr );
  844. vecApex = tr.endpos;
  845. UTIL_TraceLine(vecSpot1, vecApex, MASK_SOLID, pEnt, COLLISION_GROUP_NONE, &tr );
  846. if (tr.fraction != 1.0)
  847. {
  848. // fail!
  849. return vec3_origin;
  850. }
  851. // Don't worry about actually hitting the target, this won't hurt us!
  852. // How high should the grenade travel (subtract 15 so the grenade doesn't hit the ceiling)?
  853. float height = (vecApex.z - vecSpot1.z) - 15;
  854. //HACK HACK
  855. if ( height < 0 )
  856. height *= -1;
  857. // How fast does the grenade need to travel to reach that height given gravity?
  858. float speed = sqrt( 2 * flGravity * height );
  859. // How much time does it take to get there?
  860. float time = speed / flGravity;
  861. vecGrenadeVel = (vecSpot2 - vecSpot1);
  862. vecGrenadeVel.z = 0;
  863. // Travel half the distance to the target in that time (apex is at the midpoint)
  864. vecGrenadeVel = vecGrenadeVel * ( 0.5 / time );
  865. // Speed to offset gravity at the desired height
  866. vecGrenadeVel.z = speed;
  867. return vecGrenadeVel;
  868. }
  869. // Mortar launch
  870. int CNPC_BigMomma::RangeAttack1Conditions( float flDot, float flDist )
  871. {
  872. if ( flDist > BIG_MORTARDIST )
  873. return COND_TOO_FAR_TO_ATTACK;
  874. if ( flDist <= BIG_MORTARDIST && m_mortarTime < gpGlobals->curtime )
  875. {
  876. CBaseEntity *pEnemy = GetEnemy();
  877. if ( pEnemy )
  878. {
  879. Vector startPos = GetAbsOrigin();
  880. startPos.z += 180;
  881. m_vTossDir = VecCheckSplatToss( this, startPos, pEnemy->BodyTarget( GetAbsOrigin() ), random->RandomFloat( 150, 500 ) );
  882. if ( m_vTossDir != vec3_origin )
  883. return COND_CAN_RANGE_ATTACK1;
  884. }
  885. }
  886. return COND_NONE;
  887. }
  888. // ---------------------------------
  889. //
  890. // Mortar
  891. //
  892. // ---------------------------------
  893. void MortarSpray( const Vector &position, const Vector &direction, int spriteModel, int count )
  894. {
  895. CPVSFilter filter( position );
  896. te->SpriteSpray( filter, 0.0, &position, &direction, spriteModel, 200, 80, count );
  897. }
  898. // UNDONE: right now this is pretty much a copy of the squid spit with minor changes to the way it does damage
  899. void CBMortar:: Spawn( void )
  900. {
  901. SetMoveType( MOVETYPE_FLYGRAVITY );
  902. SetClassname( "bmortar" );
  903. SetSolid( SOLID_BBOX );
  904. pSprite = CSprite::SpriteCreate( "sprites/mommaspit.vmt", GetAbsOrigin(), true );
  905. if ( pSprite )
  906. {
  907. pSprite->SetAttachment( this, 0 );
  908. pSprite->m_flSpriteFramerate = 5;
  909. pSprite->m_nRenderMode = kRenderTransAlpha;
  910. pSprite->SetBrightness( 255 );
  911. m_iFrame = 0;
  912. pSprite->SetScale( 2.5f );
  913. }
  914. UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0) );
  915. m_maxFrame = (float)modelinfo->GetModelFrameCount( GetModel() ) - 1;
  916. m_flDmgTime = gpGlobals->curtime + 0.4;
  917. }
  918. void CBMortar::Animate( void )
  919. {
  920. SetNextThink( gpGlobals->curtime + 0.1 );
  921. Vector vVelocity = GetAbsVelocity();
  922. VectorNormalize( vVelocity );
  923. if ( gpGlobals->curtime > m_flDmgTime )
  924. {
  925. m_flDmgTime = gpGlobals->curtime + 0.2;
  926. MortarSpray( GetAbsOrigin() + Vector( 0, 0, 15 ), -vVelocity, gSpitSprite, 3 );
  927. }
  928. if ( m_iFrame++ )
  929. {
  930. if ( m_iFrame > m_maxFrame )
  931. {
  932. m_iFrame = 0;
  933. }
  934. }
  935. }
  936. CBMortar *CBMortar::Shoot( CBaseEntity *pOwner, Vector vecStart, Vector vecVelocity )
  937. {
  938. CBMortar *pSpit = CREATE_ENTITY( CBMortar, "bmortar" );
  939. pSpit->Spawn();
  940. UTIL_SetOrigin( pSpit, vecStart );
  941. pSpit->SetAbsVelocity( vecVelocity );
  942. pSpit->SetOwnerEntity( pOwner );
  943. pSpit->SetThink ( &CBMortar::Animate );
  944. pSpit->SetNextThink( gpGlobals->curtime + 0.1 );
  945. return pSpit;
  946. }
  947. void CBMortar::Precache()
  948. {
  949. BaseClass::Precache();
  950. PrecacheScriptSound( "NPC_BigMomma.SpitTouch1" );
  951. PrecacheScriptSound( "NPC_BigMomma.SpitHit1" );
  952. PrecacheScriptSound( "NPC_BigMomma.SpitHit2" );
  953. }
  954. void CBMortar::Touch( CBaseEntity *pOther )
  955. {
  956. trace_t tr;
  957. int iPitch;
  958. // splat sound
  959. iPitch = random->RandomFloat( 90, 110 );
  960. EmitSound( "NPC_BigMomma.SpitTouch1" );
  961. switch ( random->RandomInt( 0, 1 ) )
  962. {
  963. case 0:
  964. EmitSound( "NPC_BigMomma.SpitHit1" );
  965. break;
  966. case 1:
  967. EmitSound( "NPC_BigMomma.SpitHit2" );
  968. break;
  969. }
  970. if ( pOther->IsBSPModel() )
  971. {
  972. // make a splat on the wall
  973. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 10, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  974. UTIL_DecalTrace( &tr, "Splash" );
  975. }
  976. else
  977. {
  978. tr.endpos = GetAbsOrigin();
  979. Vector vVelocity = GetAbsVelocity();
  980. VectorNormalize( vVelocity );
  981. tr.plane.normal = -1 * vVelocity;
  982. }
  983. // make some flecks
  984. MortarSpray( tr.endpos + Vector( 0, 0, 15 ), tr.plane.normal, gSpitSprite, 24 );
  985. CBaseEntity *pOwner = GetOwnerEntity();
  986. RadiusDamage( CTakeDamageInfo( this, pOwner, sk_bigmomma_dmg_blast.GetFloat(), DMG_ACID ), GetAbsOrigin(), sk_bigmomma_radius_blast.GetFloat(), CLASS_NONE, NULL );
  987. UTIL_Remove( pSprite );
  988. UTIL_Remove( this );
  989. }
  990. AI_BEGIN_CUSTOM_NPC( monster_bigmomma, CNPC_BigMomma )
  991. DECLARE_TASK( TASK_MOVE_TO_NODE_RANGE )
  992. DECLARE_TASK( TASK_FIND_NODE )
  993. DECLARE_TASK( TASK_PLAY_NODE_PRESEQUENCE )
  994. DECLARE_TASK( TASK_PLAY_NODE_SEQUENCE )
  995. DECLARE_TASK( TASK_PROCESS_NODE )
  996. DECLARE_TASK( TASK_WAIT_NODE )
  997. DECLARE_TASK( TASK_NODE_DELAY )
  998. DECLARE_TASK( TASK_NODE_YAW )
  999. DECLARE_TASK( TASK_CHECK_NODE_PROXIMITY )
  1000. //=========================================================
  1001. // > SCHED_BIG_NODE
  1002. //=========================================================
  1003. DEFINE_SCHEDULE
  1004. (
  1005. SCHED_NODE_FAIL,
  1006. " Tasks"
  1007. " TASK_NODE_DELAY 3"
  1008. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1009. " "
  1010. " Interrupts"
  1011. )
  1012. //=========================================================
  1013. // > SCHED_BIG_NODE
  1014. //=========================================================
  1015. DEFINE_SCHEDULE
  1016. (
  1017. SCHED_BIG_NODE,
  1018. " Tasks"
  1019. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_NODE_FAIL"
  1020. " TASK_STOP_MOVING 0"
  1021. " TASK_FIND_NODE 0"
  1022. " TASK_PLAY_NODE_PRESEQUENCE 0"
  1023. " TASK_MOVE_TO_NODE_RANGE 0"
  1024. " TASK_CHECK_NODE_PROXIMITY 0"
  1025. " TASK_STOP_MOVING 0"
  1026. " TASK_NODE_YAW 0"
  1027. " TASK_FACE_IDEAL 0"
  1028. " TASK_WAIT_NODE 0"
  1029. " TASK_PLAY_NODE_SEQUENCE 0"
  1030. " TASK_PROCESS_NODE 0"
  1031. " "
  1032. " Interrupts"
  1033. )
  1034. AI_END_CUSTOM_NPC()