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
32 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 "hl1_npc_controller.h"
  25. #include "ai_basenpc_flyer.h"
  26. #include "soundent.h"
  27. #include "game.h"
  28. #include "npcevent.h"
  29. #include "entitylist.h"
  30. #include "activitylist.h"
  31. #include "animation.h"
  32. #include "basecombatweapon.h"
  33. #include "IEffects.h"
  34. #include "vstdlib/random.h"
  35. #include "engine/IEngineSound.h"
  36. #include "ammodef.h"
  37. #include "Sprite.h"
  38. #include "ai_moveprobe.h"
  39. //=========================================================
  40. // Monster's Anim Events Go Here
  41. //=========================================================
  42. #define CONTROLLER_AE_HEAD_OPEN 1
  43. #define CONTROLLER_AE_BALL_SHOOT 2
  44. #define CONTROLLER_AE_SMALL_SHOOT 3
  45. #define CONTROLLER_AE_POWERUP_FULL 4
  46. #define CONTROLLER_AE_POWERUP_HALF 5
  47. #define CONTROLLER_FLINCH_DELAY 2 // at most one flinch every n secs
  48. #define DIST_TO_CHECK 200
  49. ConVar sk_controller_health ( "sk_controller_health", "60" );
  50. ConVar sk_controller_dmgzap ( "sk_controller_dmgzap", "15" );
  51. ConVar sk_controller_speedball ( "sk_controller_speedball", "650" );
  52. ConVar sk_controller_dmgball ( "sk_controller_dmgball", "3" );
  53. int ACT_CONTROLLER_UP;
  54. int ACT_CONTROLLER_DOWN;
  55. int ACT_CONTROLLER_LEFT;
  56. int ACT_CONTROLLER_RIGHT;
  57. int ACT_CONTROLLER_FORWARD;
  58. int ACT_CONTROLLER_BACKWARD;
  59. class CSprite;
  60. class CNPC_Controller;
  61. enum
  62. {
  63. TASK_CONTROLLER_CHASE_ENEMY = LAST_SHARED_TASK,
  64. TASK_CONTROLLER_STRAFE,
  65. TASK_CONTROLLER_TAKECOVER,
  66. TASK_CONTROLLER_FAIL,
  67. };
  68. enum
  69. {
  70. SCHED_CONTROLLER_CHASE_ENEMY = LAST_SHARED_SCHEDULE,
  71. SCHED_CONTROLLER_STRAFE,
  72. SCHED_CONTROLLER_TAKECOVER,
  73. SCHED_CONTROLLER_FAIL,
  74. };
  75. class CControllerNavigator : public CAI_ComponentWithOuter<CNPC_Controller, CAI_Navigator>
  76. {
  77. typedef CAI_ComponentWithOuter<CNPC_Controller, CAI_Navigator> BaseClass;
  78. public:
  79. CControllerNavigator( CNPC_Controller *pOuter )
  80. : BaseClass( pOuter )
  81. {
  82. }
  83. bool ActivityIsLocomotive( Activity activity ) { return true; }
  84. };
  85. class CNPC_Controller : public CAI_BaseFlyingBot
  86. {
  87. public:
  88. DECLARE_CLASS( CNPC_Controller, CAI_BaseFlyingBot );
  89. DEFINE_CUSTOM_AI;
  90. DECLARE_DATADESC();
  91. void Spawn( void );
  92. void Precache( void );
  93. float MaxYawSpeed( void ) { return 120.0f; }
  94. Class_T Classify ( void ) { return CLASS_ALIEN_MILITARY; }
  95. void HandleAnimEvent( animevent_t *pEvent );
  96. void RunAI( void );
  97. int RangeAttack1Conditions ( float flDot, float flDist ); // balls
  98. int RangeAttack2Conditions ( float flDot, float flDist ); // head
  99. int MeleeAttack1Conditions ( float flDot, float flDist ) { return COND_NONE; }
  100. int MeleeAttack2Conditions ( float flDot, float flDist ) { return COND_NONE; }
  101. int TranslateSchedule( int scheduleType );
  102. void StartTask ( const Task_t *pTask );
  103. void RunTask ( const Task_t *pTask );
  104. void Stop( void );
  105. bool OverridePathMove( float flInterval );
  106. bool OverrideMove( float flInterval );
  107. void MoveToTarget( float flInterval, const Vector &vecMoveTarget );
  108. Activity NPC_TranslateActivity( Activity eNewActivity );
  109. void SetActivity ( Activity NewActivity );
  110. bool ShouldAdvanceRoute( float flWaypointDist );
  111. int LookupFloat( );
  112. friend class CControllerNavigator;
  113. CAI_Navigator *CreateNavigator()
  114. {
  115. return new CControllerNavigator( this );
  116. }
  117. bool ShouldGib( const CTakeDamageInfo &info );
  118. bool HasAlienGibs( void ) { return true; }
  119. bool HasHumanGibs( void ) { return false; }
  120. float m_flShootTime;
  121. float m_flShootEnd;
  122. void PainSound( const CTakeDamageInfo &info );
  123. void AlertSound( void );
  124. void IdleSound( void );
  125. void AttackSound( void );
  126. void DeathSound( const CTakeDamageInfo &info );
  127. int OnTakeDamage_Alive( const CTakeDamageInfo &info );
  128. void Event_Killed( const CTakeDamageInfo &info );
  129. CSprite *m_pBall[2]; // hand balls
  130. int m_iBall[2]; // how bright it should be
  131. float m_iBallTime[2]; // when it should be that color
  132. int m_iBallCurrent[2]; // current brightness
  133. Vector m_vecEstVelocity;
  134. Vector m_velocity;
  135. bool m_fInCombat;
  136. void SetSequence( int nSequence );
  137. int IRelationPriority( CBaseEntity *pTarget );
  138. };
  139. class CNPC_ControllerHeadBall : public CAI_BaseNPC
  140. {
  141. public:
  142. DECLARE_CLASS( CNPC_ControllerHeadBall, CAI_BaseNPC );
  143. DECLARE_DATADESC();
  144. void Spawn( void );
  145. void Precache( void );
  146. void EXPORT HuntThink( void );
  147. void EXPORT KillThink( void );
  148. void EXPORT BounceTouch( CBaseEntity *pOther );
  149. void MovetoTarget( Vector vecTarget );
  150. float m_flSpawnTime;
  151. Vector m_vecIdeal;
  152. EHANDLE m_hOwner;
  153. CSprite *m_pSprite;
  154. };
  155. class CNPC_ControllerZapBall : public CAI_BaseNPC
  156. {
  157. public:
  158. DECLARE_CLASS( CNPC_ControllerHeadBall, CAI_BaseNPC );
  159. DECLARE_DATADESC();
  160. void Spawn( void );
  161. void Precache( void );
  162. void EXPORT AnimateThink( void );
  163. void EXPORT ExplodeTouch( CBaseEntity *pOther );
  164. void Kill( void );
  165. EHANDLE m_hOwner;
  166. float m_flSpawnTime;
  167. CSprite *m_pSprite;
  168. };
  169. LINK_ENTITY_TO_CLASS( monster_alien_controller, CNPC_Controller );
  170. BEGIN_DATADESC( CNPC_Controller )
  171. DEFINE_ARRAY( m_pBall, FIELD_CLASSPTR, 2 ),
  172. DEFINE_ARRAY( m_iBall, FIELD_INTEGER, 2 ),
  173. DEFINE_ARRAY( m_iBallTime, FIELD_TIME, 2 ),
  174. DEFINE_ARRAY( m_iBallCurrent, FIELD_INTEGER, 2 ),
  175. DEFINE_FIELD( m_vecEstVelocity, FIELD_VECTOR ),
  176. DEFINE_FIELD( m_velocity, FIELD_VECTOR ),
  177. DEFINE_FIELD( m_fInCombat, FIELD_BOOLEAN ),
  178. DEFINE_FIELD( m_flShootTime, FIELD_TIME ),
  179. DEFINE_FIELD( m_flShootEnd, FIELD_TIME ),
  180. END_DATADESC()
  181. void CNPC_Controller::Spawn()
  182. {
  183. Precache( );
  184. SetModel( "models/controller.mdl" );
  185. UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ));
  186. SetSolid( SOLID_BBOX );
  187. AddSolidFlags( FSOLID_NOT_STANDABLE );
  188. SetMoveType( MOVETYPE_STEP );
  189. SetGravity(0.001);
  190. m_bloodColor = BLOOD_COLOR_GREEN;
  191. m_iHealth = sk_controller_health.GetFloat();
  192. m_flFieldOfView = VIEW_FIELD_FULL;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  193. m_NPCState = NPC_STATE_NONE;
  194. SetRenderColor( 255, 255, 255, 255 );
  195. CapabilitiesClear();
  196. AddFlag( FL_FLY );
  197. SetNavType( NAV_FLY );
  198. CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK2 | bits_CAP_MOVE_SHOOT);
  199. NPCInit();
  200. SetDefaultEyeOffset();
  201. }
  202. //=========================================================
  203. // Precache - precaches all resources this monster needs
  204. //=========================================================
  205. void CNPC_Controller::Precache()
  206. {
  207. PrecacheModel("models/controller.mdl");
  208. PrecacheModel( "sprites/xspark4.vmt");
  209. UTIL_PrecacheOther( "controller_energy_ball" );
  210. UTIL_PrecacheOther( "controller_head_ball" );
  211. PrecacheScriptSound( "Controller.Pain" );
  212. PrecacheScriptSound( "Controller.Alert" );
  213. PrecacheScriptSound( "Controller.Die" );
  214. PrecacheScriptSound( "Controller.Idle" );
  215. PrecacheScriptSound( "Controller.Attack" );
  216. }
  217. //=========================================================
  218. // TakeDamage -
  219. //=========================================================
  220. int CNPC_Controller::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  221. {
  222. PainSound( info );
  223. return BaseClass::OnTakeDamage_Alive( info );
  224. }
  225. bool CNPC_Controller::ShouldGib( const CTakeDamageInfo &info )
  226. {
  227. if ( info.GetDamageType() & DMG_NEVERGIB )
  228. return false;
  229. if ( ( g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) )
  230. return true;
  231. return false;
  232. }
  233. int CNPC_Controller::IRelationPriority( CBaseEntity *pTarget )
  234. {
  235. if ( pTarget->Classify() == CLASS_PLAYER )
  236. {
  237. return BaseClass::IRelationPriority ( pTarget ) + 1;
  238. }
  239. return BaseClass::IRelationPriority( pTarget );
  240. }
  241. void CNPC_Controller::Event_Killed( const CTakeDamageInfo &info )
  242. {
  243. if( ShouldGib(info) )
  244. {
  245. //remove the balls
  246. if (m_pBall[0])
  247. {
  248. UTIL_Remove( m_pBall[0] );
  249. m_pBall[0] = NULL;
  250. }
  251. if (m_pBall[1])
  252. {
  253. UTIL_Remove( m_pBall[1] );
  254. m_pBall[1] = NULL;
  255. }
  256. }
  257. else
  258. {
  259. // fade balls
  260. if (m_pBall[0])
  261. {
  262. m_pBall[0]->FadeAndDie( 2 );
  263. m_pBall[0] = NULL;
  264. }
  265. if (m_pBall[1])
  266. {
  267. m_pBall[1]->FadeAndDie( 2 );
  268. m_pBall[1] = NULL;
  269. }
  270. }
  271. BaseClass::Event_Killed( info );
  272. }
  273. void CNPC_Controller::PainSound( const CTakeDamageInfo &info )
  274. {
  275. if (random->RandomInt(0,5) < 2)
  276. {
  277. CPASAttenuationFilter filter( this );
  278. EmitSound( filter, entindex(), "Controller.Pain" );
  279. }
  280. }
  281. void CNPC_Controller::AlertSound( void )
  282. {
  283. CPASAttenuationFilter filter( this );
  284. EmitSound( filter, entindex(), "Controller.Alert" );
  285. }
  286. void CNPC_Controller::IdleSound( void )
  287. {
  288. CPASAttenuationFilter filter( this );
  289. EmitSound( filter, entindex(), "Controller.Idle" );
  290. }
  291. void CNPC_Controller::AttackSound( void )
  292. {
  293. CPASAttenuationFilter filter( this );
  294. EmitSound( filter, entindex(), "Controller.Attack" );
  295. }
  296. void CNPC_Controller::DeathSound( const CTakeDamageInfo &info )
  297. {
  298. CPASAttenuationFilter filter( this );
  299. EmitSound( filter, entindex(), "Controller.Die" );
  300. }
  301. //=========================================================
  302. // HandleAnimEvent - catches the monster-specific messages
  303. // that occur when tagged animation frames are played.
  304. //=========================================================
  305. void CNPC_Controller::HandleAnimEvent( animevent_t *pEvent )
  306. {
  307. switch( pEvent->event )
  308. {
  309. case CONTROLLER_AE_HEAD_OPEN:
  310. {
  311. Vector vecStart;
  312. QAngle angleGun;
  313. GetAttachment( 0, vecStart, angleGun );
  314. // BUGBUG - attach to attachment point!
  315. CBroadcastRecipientFilter filter;
  316. te->DynamicLight( filter, 0.0, &vecStart, 255, 192, 64, 0, 1 /*radius*/, 0.2, -32 );
  317. m_iBall[0] = 192;
  318. m_iBallTime[0] = gpGlobals->curtime + atoi( pEvent->options ) / 15.0;
  319. m_iBall[1] = 255;
  320. m_iBallTime[1] = gpGlobals->curtime + atoi( pEvent->options ) / 15.0;
  321. }
  322. break;
  323. case CONTROLLER_AE_BALL_SHOOT:
  324. {
  325. Vector vecStart;
  326. QAngle angleGun;
  327. GetAttachment( 1, vecStart, angleGun );
  328. CBroadcastRecipientFilter filter;
  329. te->DynamicLight( filter, 0.0, &vecStart, 255, 192, 64, 0, 1 /*radius*/, 0.1, 32 );
  330. CAI_BaseNPC *pBall = (CAI_BaseNPC*)Create( "controller_head_ball", vecStart, angleGun );
  331. pBall->SetAbsVelocity( Vector(0,0,32) );
  332. pBall->SetEnemy( GetEnemy() );
  333. // DevMsg( 1, "controller shooting head ball\n" );
  334. m_iBall[0] = 0;
  335. m_iBall[1] = 0;
  336. }
  337. break;
  338. case CONTROLLER_AE_SMALL_SHOOT:
  339. {
  340. AttackSound( );
  341. m_flShootTime = gpGlobals->curtime;
  342. m_flShootEnd = m_flShootTime + atoi( pEvent->options ) / 15.0;
  343. }
  344. break;
  345. case CONTROLLER_AE_POWERUP_FULL:
  346. {
  347. m_iBall[0] = 255;
  348. m_iBallTime[0] = gpGlobals->curtime + atoi( pEvent->options ) / 15.0;
  349. m_iBall[1] = 255;
  350. m_iBallTime[1] = gpGlobals->curtime + atoi( pEvent->options ) / 15.0;
  351. }
  352. break;
  353. case CONTROLLER_AE_POWERUP_HALF:
  354. {
  355. m_iBall[0] = 192;
  356. m_iBallTime[0] = gpGlobals->curtime + atoi( pEvent->options ) / 15.0;
  357. m_iBall[1] = 192;
  358. m_iBallTime[1] = gpGlobals->curtime + atoi( pEvent->options ) / 15.0;
  359. }
  360. break;
  361. default:
  362. BaseClass::HandleAnimEvent( pEvent );
  363. break;
  364. }
  365. }
  366. //=========================================================
  367. // AI Schedules Specific to this monster
  368. //=========================================================
  369. AI_BEGIN_CUSTOM_NPC( monster_alien_controller, CNPC_Controller )
  370. //declare our tasks
  371. DECLARE_TASK( TASK_CONTROLLER_CHASE_ENEMY )
  372. DECLARE_TASK( TASK_CONTROLLER_STRAFE )
  373. DECLARE_TASK( TASK_CONTROLLER_TAKECOVER )
  374. DECLARE_TASK( TASK_CONTROLLER_FAIL )
  375. DECLARE_ACTIVITY( ACT_CONTROLLER_UP )
  376. DECLARE_ACTIVITY( ACT_CONTROLLER_DOWN )
  377. DECLARE_ACTIVITY( ACT_CONTROLLER_LEFT )
  378. DECLARE_ACTIVITY( ACT_CONTROLLER_RIGHT )
  379. DECLARE_ACTIVITY( ACT_CONTROLLER_FORWARD )
  380. DECLARE_ACTIVITY( ACT_CONTROLLER_BACKWARD )
  381. //=========================================================
  382. // > SCHED_CONTROLLER_CHASE_ENEMY
  383. //=========================================================
  384. DEFINE_SCHEDULE
  385. (
  386. SCHED_CONTROLLER_CHASE_ENEMY,
  387. " Tasks"
  388. " TASK_GET_PATH_TO_ENEMY 128"
  389. " TASK_WAIT_FOR_MOVEMENT 0"
  390. " "
  391. " Interrupts"
  392. " COND_NEW_ENEMY"
  393. " COND_TASK_FAILED"
  394. )
  395. //=========================================================
  396. // > SCHED_CONTROLLER_STRAFE
  397. //=========================================================
  398. DEFINE_SCHEDULE
  399. (
  400. SCHED_CONTROLLER_STRAFE,
  401. " Tasks"
  402. " TASK_WAIT 0.2"
  403. " TASK_GET_PATH_TO_ENEMY 128"
  404. " TASK_WAIT_FOR_MOVEMENT 0"
  405. " TASK_WAIT 1"
  406. " "
  407. " Interrupts"
  408. " COND_NEW_ENEMY"
  409. )
  410. //=========================================================
  411. // > SCHED_CONTROLLER_TAKECOVER
  412. //=========================================================
  413. DEFINE_SCHEDULE
  414. (
  415. SCHED_CONTROLLER_TAKECOVER,
  416. " Tasks"
  417. " TASK_WAIT 0.2"
  418. " TASK_FIND_COVER_FROM_ENEMY 0"
  419. " TASK_WAIT_FOR_MOVEMENT 0"
  420. " TASK_WAIT 1"
  421. " "
  422. " Interrupts"
  423. " COND_NEW_ENEMY"
  424. )
  425. //=========================================================
  426. // > SCHED_CONTROLLER_FAIL
  427. //=========================================================
  428. DEFINE_SCHEDULE
  429. (
  430. SCHED_CONTROLLER_FAIL,
  431. " Tasks"
  432. " TASK_STOP_MOVING 0"
  433. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  434. " TASK_WAIT 2"
  435. " TASK_WAIT_PVS 0"
  436. )
  437. AI_END_CUSTOM_NPC()
  438. //=========================================================
  439. // StartTask
  440. //=========================================================
  441. void CNPC_Controller::StartTask( const Task_t *pTask )
  442. {
  443. BaseClass::StartTask( pTask );
  444. }
  445. Vector Intersect( Vector vecSrc, Vector vecDst, Vector vecMove, float flSpeed )
  446. {
  447. Vector vecTo = vecDst - vecSrc;
  448. float a = DotProduct( vecMove, vecMove ) - flSpeed * flSpeed;
  449. float b = 0 * DotProduct(vecTo, vecMove); // why does this work?
  450. float c = DotProduct( vecTo, vecTo );
  451. float t;
  452. if (a == 0)
  453. {
  454. t = c / (flSpeed * flSpeed);
  455. }
  456. else
  457. {
  458. t = b * b - 4 * a * c;
  459. t = sqrt( t ) / (2.0 * a);
  460. float t1 = -b +t;
  461. float t2 = -b -t;
  462. if (t1 < 0 || t2 < t1)
  463. t = t2;
  464. else
  465. t = t1;
  466. }
  467. if (t < 0.1)
  468. t = 0.1;
  469. if (t > 10.0)
  470. t = 10.0;
  471. Vector vecHit = vecTo + vecMove * t;
  472. VectorNormalize( vecHit );
  473. return vecHit * flSpeed;
  474. }
  475. int CNPC_Controller::LookupFloat( )
  476. {
  477. if (m_velocity.Length( ) < 32.0)
  478. {
  479. return ACT_CONTROLLER_UP;
  480. }
  481. Vector vecForward, vecRight, vecUp;
  482. AngleVectors( GetAbsAngles(), &vecForward, &vecRight, &vecUp );
  483. float x = DotProduct( vecForward, m_velocity );
  484. float y = DotProduct( vecRight, m_velocity );
  485. float z = DotProduct( vecUp, m_velocity );
  486. if (fabs(x) > fabs(y) && fabs(x) > fabs(z))
  487. {
  488. if (x > 0)
  489. return ACT_CONTROLLER_FORWARD;
  490. else
  491. return ACT_CONTROLLER_BACKWARD;
  492. }
  493. else if (fabs(y) > fabs(z))
  494. {
  495. if (y > 0)
  496. return ACT_CONTROLLER_RIGHT;
  497. else
  498. return ACT_CONTROLLER_LEFT;
  499. }
  500. else
  501. {
  502. if (z > 0)
  503. return ACT_CONTROLLER_UP;
  504. else
  505. return ACT_CONTROLLER_DOWN;
  506. }
  507. }
  508. //=========================================================
  509. // RunTask
  510. //=========================================================
  511. void CNPC_Controller::RunTask ( const Task_t *pTask )
  512. {
  513. if (m_flShootEnd > gpGlobals->curtime)
  514. {
  515. Vector vecHand;
  516. QAngle vecAngle;
  517. GetAttachment( 2, vecHand, vecAngle );
  518. while (m_flShootTime < m_flShootEnd && m_flShootTime < gpGlobals->curtime)
  519. {
  520. Vector vecSrc = vecHand + GetAbsVelocity() * (m_flShootTime - gpGlobals->curtime);
  521. Vector vecDir;
  522. if (GetEnemy() != NULL)
  523. {
  524. if (HasCondition( COND_SEE_ENEMY ))
  525. {
  526. m_vecEstVelocity = m_vecEstVelocity * 0.5 + GetEnemy()->GetAbsVelocity() * 0.5;
  527. }
  528. else
  529. {
  530. m_vecEstVelocity = m_vecEstVelocity * 0.8;
  531. }
  532. vecDir = Intersect( vecSrc, GetEnemy()->BodyTarget( GetAbsOrigin() ), m_vecEstVelocity, sk_controller_speedball.GetFloat() );
  533. float delta = 0.03490; // +-2 degree
  534. vecDir = vecDir + Vector( random->RandomFloat( -delta, delta ), random->RandomFloat( -delta, delta ), random->RandomFloat( -delta, delta ) ) * sk_controller_speedball.GetFloat();
  535. vecSrc = vecSrc + vecDir * (gpGlobals->curtime - m_flShootTime);
  536. CAI_BaseNPC *pBall = (CAI_BaseNPC*)Create( "controller_energy_ball", vecSrc, GetAbsAngles(), this );
  537. pBall->SetAbsVelocity( vecDir );
  538. // DevMsg( 2, "controller shooting energy ball\n" );
  539. }
  540. m_flShootTime += 0.2;
  541. }
  542. if (m_flShootTime > m_flShootEnd)
  543. {
  544. m_iBall[0] = 64;
  545. m_iBallTime[0] = m_flShootEnd;
  546. m_iBall[1] = 64;
  547. m_iBallTime[1] = m_flShootEnd;
  548. m_fInCombat = FALSE;
  549. }
  550. }
  551. switch ( pTask->iTask )
  552. {
  553. case TASK_WAIT_FOR_MOVEMENT:
  554. case TASK_WAIT:
  555. case TASK_WAIT_FACE_ENEMY:
  556. case TASK_WAIT_PVS:
  557. {
  558. if( GetEnemy() )
  559. {
  560. float idealYaw = UTIL_VecToYaw( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() );
  561. GetMotor()->SetIdealYawAndUpdate( idealYaw );
  562. }
  563. if ( IsSequenceFinished() || GetActivity() == ACT_IDLE)
  564. {
  565. m_fInCombat = false;
  566. }
  567. BaseClass::RunTask ( pTask );
  568. if (!m_fInCombat)
  569. {
  570. if( HasCondition( COND_CAN_RANGE_ATTACK1 ))
  571. {
  572. SetActivity( ACT_RANGE_ATTACK1 );
  573. SetCycle( 0 );
  574. ResetSequenceInfo( );
  575. m_fInCombat = true;
  576. }
  577. else if( HasCondition( COND_CAN_RANGE_ATTACK2 ) )
  578. {
  579. SetActivity( ACT_RANGE_ATTACK2 );
  580. SetCycle( 0 );
  581. ResetSequenceInfo( );
  582. m_fInCombat = true;
  583. }
  584. else
  585. {
  586. int iFloatActivity = LookupFloat();
  587. if( IsSequenceFinished() || iFloatActivity != GetActivity() )
  588. {
  589. SetActivity( (Activity)iFloatActivity );
  590. }
  591. }
  592. }
  593. }
  594. break;
  595. default:
  596. BaseClass::RunTask ( pTask );
  597. break;
  598. }
  599. }
  600. void CNPC_Controller::SetSequence( int nSequence )
  601. {
  602. BaseClass::SetSequence( nSequence );
  603. }
  604. //=========================================================
  605. //=========================================================
  606. int CNPC_Controller::TranslateSchedule( int scheduleType )
  607. {
  608. switch ( scheduleType )
  609. {
  610. case SCHED_CHASE_ENEMY:
  611. return SCHED_CONTROLLER_CHASE_ENEMY;
  612. case SCHED_RANGE_ATTACK1:
  613. return SCHED_CONTROLLER_STRAFE;
  614. case SCHED_RANGE_ATTACK2:
  615. case SCHED_MELEE_ATTACK1:
  616. case SCHED_MELEE_ATTACK2:
  617. case SCHED_TAKE_COVER_FROM_ENEMY:
  618. return SCHED_CONTROLLER_TAKECOVER;
  619. case SCHED_FAIL:
  620. return SCHED_CONTROLLER_FAIL;
  621. default:
  622. break;
  623. }
  624. return BaseClass::TranslateSchedule( scheduleType );
  625. }
  626. //=========================================================
  627. // CheckRangeAttack1 - shoot a bigass energy ball out of their head
  628. //=========================================================
  629. int CNPC_Controller::RangeAttack1Conditions ( float flDot, float flDist )
  630. {
  631. if( flDist > 2048 )
  632. {
  633. return COND_TOO_FAR_TO_ATTACK;
  634. }
  635. if( flDist <= 256 )
  636. {
  637. return COND_TOO_CLOSE_TO_ATTACK;
  638. }
  639. // if( flDot <= 0.5 )
  640. // {
  641. // return COND_NOT_FACING_ATTACK;
  642. // }
  643. return COND_CAN_RANGE_ATTACK1;
  644. }
  645. //=========================================================
  646. // CheckRangeAttack1 - head
  647. //=========================================================
  648. int CNPC_Controller::RangeAttack2Conditions ( float flDot, float flDist )
  649. {
  650. if( flDist > 2048 )
  651. {
  652. return COND_TOO_FAR_TO_ATTACK;
  653. }
  654. if( flDist <= 64 )
  655. {
  656. return COND_TOO_CLOSE_TO_ATTACK;
  657. }
  658. // if( flDot <= 0.5 )
  659. // {
  660. // return COND_NOT_FACING_ATTACK;
  661. // }
  662. return COND_CAN_RANGE_ATTACK2;
  663. }
  664. //=========================================================
  665. //=========================================================
  666. Activity CNPC_Controller::NPC_TranslateActivity( Activity eNewActivity )
  667. {
  668. switch ( eNewActivity)
  669. {
  670. case ACT_IDLE:
  671. return (Activity)LookupFloat();
  672. break;
  673. default:
  674. return BaseClass::NPC_TranslateActivity( eNewActivity );
  675. }
  676. }
  677. //=========================================================
  678. // SetActivity -
  679. //=========================================================
  680. void CNPC_Controller::SetActivity ( Activity NewActivity )
  681. {
  682. BaseClass::SetActivity( NewActivity );
  683. m_flGroundSpeed = 100;
  684. }
  685. //=========================================================
  686. // RunAI
  687. //=========================================================
  688. void CNPC_Controller::RunAI( void )
  689. {
  690. BaseClass::RunAI();
  691. Vector vecStart;
  692. QAngle angleGun;
  693. //some kind of hack in hl1 ?
  694. // if ( HasMemory( bits_MEMORY_KILLED ) )
  695. //use this instead
  696. if( !IsAlive() )
  697. return;
  698. for (int i = 0; i < 2; i++)
  699. {
  700. if (m_pBall[i] == NULL)
  701. {
  702. m_pBall[i] = CSprite::SpriteCreate( "sprites/xspark4.vmt", GetAbsOrigin(), TRUE );
  703. m_pBall[i]->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxNoDissipation );
  704. m_pBall[i]->SetAttachment( this, (i + 3) );
  705. m_pBall[i]->SetScale( 1.0 );
  706. }
  707. float t = m_iBallTime[i] - gpGlobals->curtime;
  708. if (t > 0.1)
  709. t = 0.1 / t;
  710. else
  711. t = 1.0;
  712. m_iBallCurrent[i] += (m_iBall[i] - m_iBallCurrent[i]) * t;
  713. m_pBall[i]->SetBrightness( m_iBallCurrent[i] );
  714. GetAttachment( i + 2, vecStart, angleGun );
  715. m_pBall[i]->SetAbsOrigin( vecStart );
  716. CBroadcastRecipientFilter filter;
  717. GetAttachment( i + 3, vecStart, angleGun );
  718. te->DynamicLight( filter, 0.0, &vecStart, 255, 192, 64, 0/*exponent*/, m_iBallCurrent[i] / 8 /*radius*/, 0.5, 0 );
  719. }
  720. }
  721. //=========================================================
  722. // Stop -
  723. //=========================================================
  724. void CNPC_Controller::Stop( void )
  725. {
  726. SetIdealActivity( GetStoppedActivity() );
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose: Handles movement towards the last move target.
  730. // Input : flInterval -
  731. //-----------------------------------------------------------------------------
  732. bool CNPC_Controller::OverridePathMove( float flInterval )
  733. {
  734. CBaseEntity *pMoveTarget = (GetTarget()) ? GetTarget() : GetEnemy();
  735. Vector waypointDir = GetNavigator()->GetCurWaypointPos() - GetLocalOrigin();
  736. float flWaypointDist = waypointDir.Length2D();
  737. VectorNormalize(waypointDir);
  738. // cut corner?
  739. if (flWaypointDist < 128)
  740. {
  741. if (m_flGroundSpeed > 100)
  742. m_flGroundSpeed -= 40;
  743. }
  744. else
  745. {
  746. if (m_flGroundSpeed < 400)
  747. m_flGroundSpeed += 10;
  748. }
  749. m_velocity = m_velocity * 0.8 + m_flGroundSpeed * waypointDir * 0.5;
  750. SetAbsVelocity( m_velocity );
  751. // -----------------------------------------------------------------
  752. // Check route is blocked
  753. // ------------------------------------------------------------------
  754. Vector checkPos = GetLocalOrigin() + (waypointDir * (m_flGroundSpeed * flInterval));
  755. AIMoveTrace_t moveTrace;
  756. GetMoveProbe()->MoveLimit( NAV_FLY, GetLocalOrigin(), checkPos, MASK_NPCSOLID|CONTENTS_WATER,
  757. pMoveTarget, &moveTrace);
  758. if (IsMoveBlocked( moveTrace ))
  759. {
  760. TaskFail(FAIL_NO_ROUTE);
  761. GetNavigator()->ClearGoal();
  762. return true;
  763. }
  764. // ----------------------------------------------
  765. Vector lastPatrolDir = GetNavigator()->GetCurWaypointPos() - GetLocalOrigin();
  766. if ( ProgressFlyPath( flInterval, pMoveTarget, MASK_NPCSOLID, false, 64 ) == AINPP_COMPLETE )
  767. {
  768. {
  769. m_vLastPatrolDir = lastPatrolDir;
  770. VectorNormalize(m_vLastPatrolDir);
  771. }
  772. return true;
  773. }
  774. return false;
  775. }
  776. bool CNPC_Controller::OverrideMove( float flInterval )
  777. {
  778. if (m_flGroundSpeed == 0)
  779. {
  780. m_flGroundSpeed = 100;
  781. }
  782. // ----------------------------------------------
  783. // Select move target
  784. // ----------------------------------------------
  785. CBaseEntity *pMoveTarget = NULL;
  786. if (GetTarget() != NULL )
  787. {
  788. pMoveTarget = GetTarget();
  789. }
  790. else if (GetEnemy() != NULL )
  791. {
  792. pMoveTarget = GetEnemy();
  793. }
  794. // ----------------------------------------------
  795. // Select move target position
  796. // ----------------------------------------------
  797. Vector vMoveTargetPos(0,0,0);
  798. if (GetTarget())
  799. {
  800. vMoveTargetPos = GetTarget()->GetAbsOrigin();
  801. }
  802. else if (GetEnemy() != NULL)
  803. {
  804. vMoveTargetPos = GetEnemy()->GetAbsOrigin();
  805. }
  806. // -----------------------------------------
  807. // See if we can fly there directly
  808. // -----------------------------------------
  809. if (pMoveTarget /*|| HaveInspectTarget()*/)
  810. {
  811. trace_t tr;
  812. if (pMoveTarget)
  813. {
  814. UTIL_TraceEntity( this, GetAbsOrigin(), vMoveTargetPos,
  815. MASK_NPCSOLID_BRUSHONLY, pMoveTarget, GetCollisionGroup(), &tr);
  816. }
  817. else
  818. {
  819. UTIL_TraceEntity( this, GetAbsOrigin(), vMoveTargetPos, MASK_NPCSOLID_BRUSHONLY, &tr);
  820. }
  821. /*
  822. float fTargetDist = (1-tr.fraction)*(GetAbsOrigin() - vMoveTargetPos).Length();
  823. if (fTargetDist > 50)
  824. {
  825. //SetCondition( COND_SCANNER_FLY_BLOCKED );
  826. }
  827. else
  828. {
  829. //SetCondition( COND_SCANNER_FLY_CLEAR );
  830. }
  831. */
  832. }
  833. // -----------------------------------------------------------------
  834. // If I have a route, keep it updated and move toward target
  835. // ------------------------------------------------------------------
  836. if (GetNavigator()->IsGoalActive())
  837. {
  838. if ( OverridePathMove( flInterval ) )
  839. return true;
  840. }
  841. else
  842. {
  843. //do nothing
  844. Stop();
  845. TaskComplete();
  846. }
  847. return true;
  848. }
  849. void CNPC_Controller::MoveToTarget( float flInterval, const Vector &vecMoveTarget )
  850. {
  851. const float myAccel = 300.0;
  852. const float myDecay = 9.0;
  853. //TurnHeadToTarget( flInterval, MoveTarget );
  854. MoveToLocation( flInterval, vecMoveTarget, myAccel, (2 * myAccel), myDecay );
  855. }
  856. //=========================================================
  857. // Controller bouncy ball attack
  858. //=========================================================
  859. LINK_ENTITY_TO_CLASS( controller_head_ball, CNPC_ControllerHeadBall );
  860. BEGIN_DATADESC( CNPC_ControllerHeadBall )
  861. DEFINE_THINKFUNC( HuntThink ),
  862. DEFINE_THINKFUNC( KillThink ),
  863. DEFINE_ENTITYFUNC( BounceTouch ),
  864. DEFINE_FIELD( m_pSprite, FIELD_CLASSPTR ),
  865. DEFINE_FIELD( m_flSpawnTime, FIELD_TIME ),
  866. DEFINE_FIELD( m_vecIdeal, FIELD_VECTOR ),
  867. DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
  868. END_DATADESC()
  869. void CNPC_ControllerHeadBall::Spawn( void )
  870. {
  871. Precache( );
  872. // motor
  873. SetMoveType( MOVETYPE_FLY );
  874. SetSolid( SOLID_BBOX );
  875. SetSize( vec3_origin, vec3_origin );
  876. m_pSprite = CSprite::SpriteCreate( "sprites/xspark4.vmt", GetAbsOrigin(), FALSE );
  877. m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
  878. m_pSprite->SetAttachment( this, 0 );
  879. m_pSprite->SetScale( 2.0 );
  880. UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0) );
  881. UTIL_SetOrigin( this, GetAbsOrigin() );
  882. SetThink( &CNPC_ControllerHeadBall::HuntThink );
  883. SetTouch( &CNPC_ControllerHeadBall::BounceTouch );
  884. // m_vecIdeal = vec3_origin; //(0,0,0)
  885. SetNextThink( gpGlobals->curtime + 0.1 );
  886. m_hOwner = GetOwnerEntity();
  887. m_flSpawnTime = gpGlobals->curtime;
  888. }
  889. void CNPC_ControllerHeadBall::Precache( void )
  890. {
  891. PrecacheModel( "sprites/xspark4.vmt");
  892. }
  893. extern short g_sModelIndexLaser;
  894. void CNPC_ControllerHeadBall::HuntThink( void )
  895. {
  896. SetNextThink( gpGlobals->curtime + 0.1 );
  897. if( !m_pSprite )
  898. {
  899. Assert(0);
  900. return;
  901. }
  902. m_pSprite->SetBrightness( m_pSprite->GetBrightness() - 5, 0.1f );
  903. CBroadcastRecipientFilter filter;
  904. te->DynamicLight( filter, 0.0, &GetAbsOrigin(), 255, 255, 255, 0, m_pSprite->GetBrightness() / 16, 0.2, 0 );
  905. // check world boundaries
  906. if (gpGlobals->curtime - m_flSpawnTime > 5 || m_pSprite->GetBrightness() < 64 /*|| GetEnemy() == NULL || m_hOwner == NULL*/ || !IsInWorld() )
  907. {
  908. SetTouch( NULL );
  909. SetThink( &CNPC_ControllerHeadBall::KillThink );
  910. SetNextThink( gpGlobals->curtime );
  911. return;
  912. }
  913. if( !GetEnemy() )
  914. return;
  915. MovetoTarget( GetEnemy()->GetAbsOrigin() );
  916. if ((GetEnemy()->WorldSpaceCenter() - GetAbsOrigin()).Length() < 64)
  917. {
  918. trace_t tr;
  919. UTIL_TraceLine( GetAbsOrigin(), GetEnemy()->WorldSpaceCenter(), MASK_ALL, this, COLLISION_GROUP_NONE, &tr );
  920. CBaseEntity *pEntity = tr.m_pEnt;
  921. if (pEntity != NULL && pEntity->m_takedamage == DAMAGE_YES)
  922. {
  923. ClearMultiDamage( );
  924. Vector dir = GetAbsVelocity();
  925. VectorNormalize( dir );
  926. CTakeDamageInfo info( this, this, sk_controller_dmgball.GetFloat(), DMG_SHOCK );
  927. CalculateMeleeDamageForce( &info, dir, tr.endpos );
  928. pEntity->DispatchTraceAttack( info, dir, &tr );
  929. ApplyMultiDamage();
  930. int haloindex = 0;
  931. int fadelength = 0;
  932. int amplitude = 0;
  933. const Vector vecEnd = tr.endpos;
  934. te->BeamEntPoint( filter, 0.0, entindex(), NULL, 0, &(tr.m_pEnt->GetAbsOrigin()),
  935. g_sModelIndexLaser, haloindex /* no halo */, 0, 10, 3, 20, 20, fadelength,
  936. amplitude, 255, 255, 255, 255, 10 );
  937. }
  938. UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin(), "Controller.ElectroSound", 0.5, SNDLVL_NORM, 0, 100 );
  939. SetNextAttack( gpGlobals->curtime + 3.0 );
  940. SetThink( &CNPC_ControllerHeadBall::KillThink );
  941. SetNextThink( gpGlobals->curtime + 0.3 );
  942. }
  943. }
  944. void CNPC_ControllerHeadBall::MovetoTarget( Vector vecTarget )
  945. {
  946. // accelerate
  947. float flSpeed = m_vecIdeal.Length();
  948. if (flSpeed == 0)
  949. {
  950. m_vecIdeal = GetAbsVelocity();
  951. flSpeed = m_vecIdeal.Length();
  952. }
  953. if (flSpeed > 400)
  954. {
  955. VectorNormalize( m_vecIdeal );
  956. m_vecIdeal = m_vecIdeal * 400;
  957. }
  958. Vector t = vecTarget - GetAbsOrigin();
  959. VectorNormalize(t);
  960. m_vecIdeal = m_vecIdeal + t * 100;
  961. SetAbsVelocity(m_vecIdeal);
  962. }
  963. void CNPC_ControllerHeadBall::BounceTouch( CBaseEntity *pOther )
  964. {
  965. Vector vecDir = m_vecIdeal;
  966. VectorNormalize( vecDir );
  967. trace_t tr;
  968. tr = CBaseEntity::GetTouchTrace( );
  969. float n = -DotProduct(tr.plane.normal, vecDir);
  970. vecDir = 2.0 * tr.plane.normal * n + vecDir;
  971. m_vecIdeal = vecDir * m_vecIdeal.Length();
  972. }
  973. void CNPC_ControllerHeadBall::KillThink( void )
  974. {
  975. UTIL_Remove( m_pSprite );
  976. UTIL_Remove( this );
  977. }
  978. //=========================================================
  979. // Controller Zap attack
  980. //=========================================================
  981. LINK_ENTITY_TO_CLASS( controller_energy_ball, CNPC_ControllerZapBall );
  982. BEGIN_DATADESC( CNPC_ControllerZapBall )
  983. DEFINE_THINKFUNC( AnimateThink ),
  984. DEFINE_ENTITYFUNC( ExplodeTouch ),
  985. DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
  986. DEFINE_FIELD( m_flSpawnTime, FIELD_TIME ),
  987. DEFINE_FIELD( m_pSprite, FIELD_CLASSPTR ),
  988. END_DATADESC()
  989. void CNPC_ControllerZapBall::Spawn( void )
  990. {
  991. Precache( );
  992. // motor
  993. SetMoveType( MOVETYPE_FLY );
  994. // SetSolid( SOLID_CUSTOM );
  995. SetSolid( SOLID_BBOX );
  996. SetSize( vec3_origin, vec3_origin );
  997. m_pSprite = CSprite::SpriteCreate( "sprites/xspark4.vmt", GetAbsOrigin(), FALSE );
  998. m_pSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNoDissipation );
  999. m_pSprite->SetAttachment( this, 0 );
  1000. m_pSprite->SetScale( 0.5 );
  1001. UTIL_SetSize( this, Vector( 0, 0, 0), Vector(0, 0, 0) );
  1002. UTIL_SetOrigin( this, GetAbsOrigin() );
  1003. SetThink( &CNPC_ControllerZapBall::AnimateThink );
  1004. SetTouch( &CNPC_ControllerZapBall::ExplodeTouch );
  1005. m_hOwner = GetOwnerEntity();
  1006. m_flSpawnTime = gpGlobals->curtime; // keep track of when ball spawned
  1007. SetNextThink( gpGlobals->curtime + 0.1 );
  1008. }
  1009. void CNPC_ControllerZapBall::Precache( void )
  1010. {
  1011. PrecacheModel( "sprites/xspark4.vmt");
  1012. }
  1013. void CNPC_ControllerZapBall::AnimateThink( void )
  1014. {
  1015. SetNextThink( gpGlobals->curtime + 0.1 );
  1016. SetCycle( ((int)GetCycle() + 1) % 11 );
  1017. if (gpGlobals->curtime - m_flSpawnTime > 5 || GetAbsVelocity().Length() < 10)
  1018. {
  1019. SetTouch( NULL );
  1020. Kill();
  1021. }
  1022. }
  1023. void CNPC_ControllerZapBall::ExplodeTouch( CBaseEntity *pOther )
  1024. {
  1025. if (m_takedamage = DAMAGE_YES )
  1026. {
  1027. trace_t tr;
  1028. tr = GetTouchTrace( );
  1029. ClearMultiDamage( );
  1030. Vector vecAttackDir = GetAbsVelocity();
  1031. VectorNormalize( vecAttackDir );
  1032. if (m_hOwner != NULL)
  1033. {
  1034. CTakeDamageInfo info( this, m_hOwner, sk_controller_dmgball.GetFloat(), DMG_ENERGYBEAM );
  1035. CalculateMeleeDamageForce( &info, vecAttackDir, tr.endpos );
  1036. pOther->DispatchTraceAttack( info, vecAttackDir, &tr );
  1037. }
  1038. else
  1039. {
  1040. CTakeDamageInfo info( this, this, sk_controller_dmgball.GetFloat(), DMG_ENERGYBEAM );
  1041. CalculateMeleeDamageForce( &info, vecAttackDir, tr.endpos );
  1042. pOther->DispatchTraceAttack( info, vecAttackDir, &tr );
  1043. }
  1044. ApplyMultiDamage();
  1045. // void UTIL_EmitAmbientSound( CBaseEntity *entity, const Vector &vecOrigin, const char *samp, float vol, soundlevel_t soundlevel, int fFlags, int pitch, float soundtime /*= 0.0f*/ )
  1046. UTIL_EmitAmbientSound( GetSoundSourceIndex(), tr.endpos, "Controller.ElectroSound", 0.3, SNDLVL_NORM, 0, random->RandomInt( 90, 99 ) );
  1047. }
  1048. Kill();
  1049. }
  1050. void CNPC_ControllerZapBall::Kill( void )
  1051. {
  1052. UTIL_Remove( m_pSprite );
  1053. UTIL_Remove( this );
  1054. }