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.

724 lines
17 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_senses.h"
  31. // Animation events
  32. #define LEECH_AE_ATTACK 1
  33. #define LEECH_AE_FLOP 2
  34. //#define DEBUG_BEAMS 0
  35. ConVar sk_leech_health( "sk_leech_health", "2" );
  36. ConVar sk_leech_dmg_bite( "sk_leech_dmg_bite", "2" );
  37. // Movement constants
  38. #define LEECH_ACCELERATE 10
  39. #define LEECH_CHECK_DIST 45
  40. #define LEECH_SWIM_SPEED 50
  41. #define LEECH_SWIM_ACCEL 80
  42. #define LEECH_SWIM_DECEL 10
  43. #define LEECH_TURN_RATE 70
  44. #define LEECH_SIZEX 10
  45. #define LEECH_FRAMETIME 0.1
  46. class CNPC_Leech : public CHL1BaseNPC
  47. {
  48. DECLARE_CLASS( CNPC_Leech, CHL1BaseNPC );
  49. public:
  50. DECLARE_DATADESC();
  51. void Spawn( void );
  52. void Precache( void );
  53. static const char *pAlertSounds[];
  54. void SwimThink( void );
  55. void DeadThink( void );
  56. void SwitchLeechState( void );
  57. float ObstacleDistance( CBaseEntity *pTarget );
  58. void UpdateMotion( void );
  59. void RecalculateWaterlevel( void );
  60. void Touch( CBaseEntity *pOther );
  61. Disposition_t IRelationType(CBaseEntity *pTarget);
  62. void HandleAnimEvent( animevent_t *pEvent );
  63. void AttackSound( void );
  64. void AlertSound( void );
  65. void Activate( void );
  66. Class_T Classify( void ) { return CLASS_INSECT; };
  67. void Event_Killed( const CTakeDamageInfo &info );
  68. bool ShouldGib( const CTakeDamageInfo &info );
  69. /* // Base entity functions
  70. void Killed( entvars_t *pevAttacker, int iGib );
  71. int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
  72. */
  73. private:
  74. // UNDONE: Remove unused boid vars, do group behavior
  75. float m_flTurning;// is this boid turning?
  76. bool m_fPathBlocked;// TRUE if there is an obstacle ahead
  77. float m_flAccelerate;
  78. float m_obstacle;
  79. float m_top;
  80. float m_bottom;
  81. float m_height;
  82. float m_waterTime;
  83. float m_sideTime; // Timer to randomly check clearance on sides
  84. float m_zTime;
  85. float m_stateTime;
  86. float m_attackSoundTime;
  87. Vector m_oldOrigin;
  88. };
  89. LINK_ENTITY_TO_CLASS( monster_leech, CNPC_Leech );
  90. BEGIN_DATADESC( CNPC_Leech )
  91. DEFINE_FIELD( m_flTurning, FIELD_FLOAT ),
  92. DEFINE_FIELD( m_fPathBlocked, FIELD_BOOLEAN ),
  93. DEFINE_FIELD( m_flAccelerate, FIELD_FLOAT ),
  94. DEFINE_FIELD( m_obstacle, FIELD_FLOAT ),
  95. DEFINE_FIELD( m_top, FIELD_FLOAT ),
  96. DEFINE_FIELD( m_bottom, FIELD_FLOAT ),
  97. DEFINE_FIELD( m_height, FIELD_FLOAT ),
  98. DEFINE_FIELD( m_waterTime, FIELD_TIME ),
  99. DEFINE_FIELD( m_sideTime, FIELD_TIME ),
  100. DEFINE_FIELD( m_zTime, FIELD_TIME ),
  101. DEFINE_FIELD( m_stateTime, FIELD_TIME ),
  102. DEFINE_FIELD( m_attackSoundTime, FIELD_TIME ),
  103. DEFINE_FIELD( m_oldOrigin, FIELD_VECTOR ),
  104. DEFINE_THINKFUNC( SwimThink ),
  105. DEFINE_THINKFUNC( DeadThink ),
  106. END_DATADESC()
  107. bool CNPC_Leech::ShouldGib( const CTakeDamageInfo &info )
  108. {
  109. return false;
  110. }
  111. void CNPC_Leech::Spawn( void )
  112. {
  113. Precache();
  114. SetModel( "models/leech.mdl" );
  115. SetHullType(HULL_TINY_CENTERED);
  116. SetHullSizeNormal();
  117. UTIL_SetSize( this, Vector(-1,-1,0), Vector(1,1,2));
  118. Vector vecSurroundingMins(-8,-8,0);
  119. Vector vecSurroundingMaxs(8,8,2);
  120. CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs );
  121. // Don't push the minz down too much or the water check will fail because this entity is really point-sized
  122. SetSolid( SOLID_BBOX );
  123. AddSolidFlags( FSOLID_NOT_STANDABLE );
  124. SetMoveType( MOVETYPE_FLY );
  125. AddFlag( FL_SWIM );
  126. m_iHealth = sk_leech_health.GetInt();
  127. m_flFieldOfView = -0.5; // 180 degree FOV
  128. SetDistLook( 750 );
  129. NPCInit();
  130. SetThink( &CNPC_Leech::SwimThink );
  131. SetUse( NULL );
  132. SetTouch( NULL );
  133. SetViewOffset( vec3_origin );
  134. m_flTurning = 0;
  135. m_fPathBlocked = FALSE;
  136. SetActivity( ACT_SWIM );
  137. SetState( NPC_STATE_IDLE );
  138. m_stateTime = gpGlobals->curtime + random->RandomFloat( 1, 5 );
  139. SetRenderColor( 255, 255, 255, 255 );
  140. m_bloodColor = DONT_BLEED;
  141. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  142. }
  143. void CNPC_Leech::Activate( void )
  144. {
  145. RecalculateWaterlevel();
  146. BaseClass::Activate();
  147. }
  148. void CNPC_Leech::DeadThink( void )
  149. {
  150. if ( IsSequenceFinished() )
  151. {
  152. if ( GetActivity() == ACT_DIEFORWARD )
  153. {
  154. SetThink( NULL );
  155. StopAnimation();
  156. return;
  157. }
  158. else if ( GetFlags() & FL_ONGROUND )
  159. {
  160. AddSolidFlags( FSOLID_NOT_SOLID );
  161. SetActivity( ACT_DIEFORWARD );
  162. }
  163. }
  164. StudioFrameAdvance();
  165. SetNextThink( gpGlobals->curtime + 0.1 );
  166. // Apply damage velocity, but keep out of the walls
  167. if ( GetAbsVelocity().x != 0 || GetAbsVelocity().y != 0 )
  168. {
  169. trace_t tr;
  170. // Look 0.5 seconds ahead
  171. UTIL_TraceLine( GetLocalOrigin(), GetLocalOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  172. if (tr.fraction != 1.0)
  173. {
  174. Vector vVelocity = GetAbsVelocity();
  175. vVelocity.x = 0;
  176. vVelocity.y = 0;
  177. SetAbsVelocity( vVelocity );
  178. }
  179. }
  180. }
  181. Disposition_t CNPC_Leech::IRelationType( CBaseEntity *pTarget )
  182. {
  183. if ( pTarget->IsPlayer() )
  184. return D_HT;
  185. return BaseClass::IRelationType( pTarget );
  186. }
  187. void CNPC_Leech::Touch( CBaseEntity *pOther )
  188. {
  189. if ( !pOther->IsPlayer() )
  190. return;
  191. if ( pOther == GetTouchTrace().m_pEnt )
  192. {
  193. if ( pOther->GetAbsVelocity() == vec3_origin )
  194. return;
  195. SetBaseVelocity( pOther->GetAbsVelocity() );
  196. AddFlag( FL_BASEVELOCITY );
  197. }
  198. }
  199. void CNPC_Leech::HandleAnimEvent( animevent_t *pEvent )
  200. {
  201. CBaseEntity *pEnemy = GetEnemy();
  202. switch( pEvent->event )
  203. {
  204. case LEECH_AE_FLOP:
  205. // Play flop sound
  206. break;
  207. case LEECH_AE_ATTACK:
  208. AttackSound();
  209. if ( pEnemy != NULL )
  210. {
  211. Vector dir, face;
  212. AngleVectors( GetAbsAngles(), &face );
  213. face.z = 0;
  214. dir = (pEnemy->GetLocalOrigin() - GetLocalOrigin() );
  215. dir.z = 0;
  216. VectorNormalize( dir );
  217. VectorNormalize( face );
  218. if ( DotProduct(dir, face) > 0.9 ) // Only take damage if the leech is facing the prey
  219. {
  220. CTakeDamageInfo info( this, this, sk_leech_dmg_bite.GetInt(), DMG_SLASH );
  221. CalculateMeleeDamageForce( &info, dir, pEnemy->GetAbsOrigin() );
  222. pEnemy->TakeDamage( info );
  223. }
  224. }
  225. m_stateTime -= 2;
  226. break;
  227. default:
  228. BaseClass::HandleAnimEvent( pEvent );
  229. break;
  230. }
  231. }
  232. void CNPC_Leech::Precache( void )
  233. {
  234. PrecacheModel("models/leech.mdl");
  235. PrecacheScriptSound( "Leech.Attack" );
  236. PrecacheScriptSound( "Leech.Alert" );
  237. }
  238. void CNPC_Leech::AttackSound( void )
  239. {
  240. if ( gpGlobals->curtime > m_attackSoundTime )
  241. {
  242. CPASAttenuationFilter filter( this );
  243. EmitSound(filter, entindex(), "Leech.Attack" );
  244. m_attackSoundTime = gpGlobals->curtime + 0.5;
  245. }
  246. }
  247. void CNPC_Leech::AlertSound( void )
  248. {
  249. CPASAttenuationFilter filter( this );
  250. EmitSound(filter, entindex(), "Leech.Alert" );
  251. }
  252. void CNPC_Leech::SwitchLeechState( void )
  253. {
  254. m_stateTime = gpGlobals->curtime + random->RandomFloat( 3, 6 );
  255. if ( m_NPCState == NPC_STATE_COMBAT )
  256. {
  257. SetEnemy ( NULL );
  258. SetState( NPC_STATE_IDLE );
  259. // We may be up against the player, so redo the side checks
  260. m_sideTime = 0;
  261. }
  262. else
  263. {
  264. GetSenses()->Look( GetSenses()->GetDistLook() );
  265. CBaseEntity *pEnemy = BestEnemy();
  266. if ( pEnemy && pEnemy->GetWaterLevel() != 0 )
  267. {
  268. SetEnemy ( pEnemy );
  269. SetState( NPC_STATE_COMBAT );
  270. m_stateTime = gpGlobals->curtime + random->RandomFloat( 18, 25 );
  271. AlertSound();
  272. }
  273. }
  274. }
  275. void CNPC_Leech::RecalculateWaterlevel( void )
  276. {
  277. // Calculate boundaries
  278. Vector vecTest = GetLocalOrigin() - Vector(0,0,400);
  279. trace_t tr;
  280. UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  281. if ( tr.fraction != 1.0 )
  282. m_bottom = tr.endpos.z + 1;
  283. else
  284. m_bottom = vecTest.z;
  285. m_top = UTIL_WaterLevel( GetLocalOrigin(), GetLocalOrigin().z, GetLocalOrigin().z + 400 ) - 1;
  286. #if DEBUG_BEAMS
  287. NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + Vector( 0, 0, m_bottom ), 0, 255, 0, false, 0.1f );
  288. NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + Vector( 0, 0, m_top ), 0, 255, 255, false, 0.1f );
  289. #endif
  290. // Chop off 20% of the outside range
  291. float newBottom = m_bottom * 0.8 + m_top * 0.2;
  292. m_top = m_bottom * 0.2 + m_top * 0.8;
  293. m_bottom = newBottom;
  294. m_height = random->RandomFloat( m_bottom, m_top );
  295. m_waterTime = gpGlobals->curtime + random->RandomFloat( 5, 7 );
  296. }
  297. void CNPC_Leech::SwimThink( void )
  298. {
  299. trace_t tr;
  300. float flLeftSide;
  301. float flRightSide;
  302. float targetSpeed;
  303. float targetYaw = 0;
  304. CBaseEntity *pTarget;
  305. /*if ( !UTIL_FindClientInPVS( edict() ) )
  306. {
  307. m_flNextThink = gpGlobals->curtime + random->RandomFloat( 1.0f, 1.5f );
  308. SetAbsVelocity( vec3_origin );
  309. return;
  310. }
  311. else*/
  312. SetNextThink( gpGlobals->curtime + 0.1 );
  313. targetSpeed = LEECH_SWIM_SPEED;
  314. if ( m_waterTime < gpGlobals->curtime )
  315. RecalculateWaterlevel();
  316. if ( m_stateTime < gpGlobals->curtime )
  317. SwitchLeechState();
  318. ClearCondition( COND_CAN_MELEE_ATTACK1 );
  319. switch( m_NPCState )
  320. {
  321. case NPC_STATE_COMBAT:
  322. pTarget = GetEnemy();
  323. if ( !pTarget )
  324. SwitchLeechState();
  325. else
  326. {
  327. // Chase the enemy's eyes
  328. m_height = pTarget->GetLocalOrigin().z + pTarget->GetViewOffset().z - 5;
  329. // Clip to viable water area
  330. if ( m_height < m_bottom )
  331. m_height = m_bottom;
  332. else if ( m_height > m_top )
  333. m_height = m_top;
  334. Vector location = pTarget->GetLocalOrigin() - GetLocalOrigin();
  335. location.z += (pTarget->GetViewOffset().z);
  336. if ( location.Length() < 80 )
  337. SetCondition( COND_CAN_MELEE_ATTACK1 );
  338. // Turn towards target ent
  339. targetYaw = UTIL_VecToYaw( location );
  340. QAngle vTestAngle = GetAbsAngles();
  341. targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( GetAbsAngles().y ) );
  342. if ( targetYaw < (-LEECH_TURN_RATE) )
  343. targetYaw = (-LEECH_TURN_RATE);
  344. else if ( targetYaw > (LEECH_TURN_RATE) )
  345. targetYaw = (LEECH_TURN_RATE);
  346. else
  347. targetSpeed *= 2;
  348. }
  349. break;
  350. default:
  351. if ( m_zTime < gpGlobals->curtime )
  352. {
  353. float newHeight = random->RandomFloat( m_bottom, m_top );
  354. m_height = 0.5 * m_height + 0.5 * newHeight;
  355. m_zTime = gpGlobals->curtime + random->RandomFloat( 1, 4 );
  356. }
  357. if ( random->RandomInt( 0, 100 ) < 10 )
  358. targetYaw = random->RandomInt( -30, 30 );
  359. pTarget = NULL;
  360. // oldorigin test
  361. if ( ( GetLocalOrigin() - m_oldOrigin ).Length() < 1 )
  362. {
  363. // If leech didn't move, there must be something blocking it, so try to turn
  364. m_sideTime = 0;
  365. }
  366. break;
  367. }
  368. m_obstacle = ObstacleDistance( pTarget );
  369. m_oldOrigin = GetLocalOrigin();
  370. if ( m_obstacle < 0.1 )
  371. m_obstacle = 0.1;
  372. Vector vForward, vRight;
  373. AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );
  374. // is the way ahead clear?
  375. if ( m_obstacle == 1.0 )
  376. {
  377. // if the leech is turning, stop the trend.
  378. if ( m_flTurning != 0 )
  379. {
  380. m_flTurning = 0;
  381. }
  382. m_fPathBlocked = FALSE;
  383. m_flSpeed = UTIL_Approach( targetSpeed, m_flSpeed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME );
  384. SetAbsVelocity( vForward * m_flSpeed );
  385. }
  386. else
  387. {
  388. m_obstacle = 1.0 / m_obstacle;
  389. // IF we get this far in the function, the leader's path is blocked!
  390. m_fPathBlocked = TRUE;
  391. if ( m_flTurning == 0 )// something in the way and leech is not already turning to avoid
  392. {
  393. Vector vecTest;
  394. // measure clearance on left and right to pick the best dir to turn
  395. vecTest = GetLocalOrigin() + ( vRight * LEECH_SIZEX) + ( vForward * LEECH_CHECK_DIST);
  396. UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  397. flRightSide = tr.fraction;
  398. vecTest = GetLocalOrigin() + ( vRight * -LEECH_SIZEX) + ( vForward * LEECH_CHECK_DIST);
  399. UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  400. flLeftSide = tr.fraction;
  401. // turn left, right or random depending on clearance ratio
  402. float delta = (flRightSide - flLeftSide);
  403. if ( delta > 0.1 || (delta > -0.1 && random->RandomInt( 0,100 ) < 50 ) )
  404. m_flTurning = -LEECH_TURN_RATE;
  405. else
  406. m_flTurning = LEECH_TURN_RATE;
  407. }
  408. m_flSpeed = UTIL_Approach( -(LEECH_SWIM_SPEED*0.5), m_flSpeed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle );
  409. SetAbsVelocity( vForward * m_flSpeed );
  410. }
  411. GetMotor()->SetIdealYaw( m_flTurning + targetYaw );
  412. UpdateMotion();
  413. }
  414. //
  415. // ObstacleDistance - returns normalized distance to obstacle
  416. //
  417. float CNPC_Leech::ObstacleDistance( CBaseEntity *pTarget )
  418. {
  419. trace_t tr;
  420. Vector vecTest;
  421. Vector vForward, vRight;
  422. // use VELOCITY, not angles, not all boids point the direction they are flying
  423. //Vector vecDir = UTIL_VecToAngles( pev->velocity );
  424. QAngle tmp = GetAbsAngles();
  425. tmp.x = -tmp.x;
  426. AngleVectors ( tmp, &vForward, &vRight, NULL );
  427. // check for obstacle ahead
  428. vecTest = GetLocalOrigin() + vForward * LEECH_CHECK_DIST;
  429. UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  430. if ( tr.startsolid )
  431. {
  432. m_flSpeed = -LEECH_SWIM_SPEED * 0.5;
  433. }
  434. if ( tr.fraction != 1.0 )
  435. {
  436. if ( (pTarget == NULL || tr.m_pEnt != pTarget ) )
  437. {
  438. return tr.fraction;
  439. }
  440. else
  441. {
  442. if ( fabs( m_height - GetLocalOrigin().z ) > 10 )
  443. return tr.fraction;
  444. }
  445. }
  446. if ( m_sideTime < gpGlobals->curtime )
  447. {
  448. // extra wide checks
  449. vecTest = GetLocalOrigin() + vRight * LEECH_SIZEX * 2 + vForward * LEECH_CHECK_DIST;
  450. UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  451. if (tr.fraction != 1.0)
  452. return tr.fraction;
  453. vecTest = GetLocalOrigin() - vRight * LEECH_SIZEX * 2 + vForward * LEECH_CHECK_DIST;
  454. UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  455. if (tr.fraction != 1.0)
  456. return tr.fraction;
  457. // Didn't hit either side, so stop testing for another 0.5 - 1 seconds
  458. m_sideTime = gpGlobals->curtime + random->RandomFloat(0.5,1);
  459. }
  460. return 1.0;
  461. }
  462. void CNPC_Leech::UpdateMotion( void )
  463. {
  464. float flapspeed = ( m_flSpeed - m_flAccelerate) / LEECH_ACCELERATE;
  465. m_flAccelerate = m_flAccelerate * 0.8 + m_flSpeed * 0.2;
  466. if (flapspeed < 0)
  467. flapspeed = -flapspeed;
  468. flapspeed += 1.0;
  469. if (flapspeed < 0.5)
  470. flapspeed = 0.5;
  471. if (flapspeed > 1.9)
  472. flapspeed = 1.9;
  473. m_flPlaybackRate = flapspeed;
  474. QAngle vAngularVelocity = GetLocalAngularVelocity();
  475. QAngle vAngles = GetLocalAngles();
  476. if ( !m_fPathBlocked )
  477. vAngularVelocity.y = GetMotor()->GetIdealYaw();
  478. else
  479. vAngularVelocity.y = GetMotor()->GetIdealYaw() * m_obstacle;
  480. if ( vAngularVelocity.y > 150 )
  481. SetIdealActivity( ACT_TURN_LEFT );
  482. else if ( vAngularVelocity.y < -150 )
  483. SetIdealActivity( ACT_TURN_RIGHT );
  484. else
  485. SetIdealActivity( ACT_SWIM );
  486. // lean
  487. float targetPitch, delta;
  488. delta = m_height - GetLocalOrigin().z;
  489. /* if ( delta < -10 )
  490. targetPitch = -30;
  491. else if ( delta > 10 )
  492. targetPitch = 30;
  493. else*/
  494. targetPitch = 0;
  495. vAngles.x = UTIL_Approach( targetPitch, vAngles.x, 60 * LEECH_FRAMETIME );
  496. // bank
  497. vAngularVelocity.z = - ( vAngles.z + (vAngularVelocity.y * 0.25));
  498. if ( m_NPCState == NPC_STATE_COMBAT && HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  499. SetIdealActivity( ACT_MELEE_ATTACK1 );
  500. // Out of water check
  501. if ( !GetWaterLevel() )
  502. {
  503. SetMoveType( MOVETYPE_FLYGRAVITY );
  504. SetIdealActivity( ACT_HOP );
  505. SetAbsVelocity( vec3_origin );
  506. // Animation will intersect the floor if either of these is non-zero
  507. vAngles.z = 0;
  508. vAngles.x = 0;
  509. m_flPlaybackRate = random->RandomFloat( 0.8, 1.2 );
  510. }
  511. else if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
  512. {
  513. SetMoveType( MOVETYPE_FLY );
  514. SetGroundEntity( NULL );
  515. // TODO
  516. RecalculateWaterlevel();
  517. m_waterTime = gpGlobals->curtime + 2; // Recalc again soon, water may be rising
  518. }
  519. if ( GetActivity() != GetIdealActivity() )
  520. {
  521. SetActivity ( GetIdealActivity() );
  522. }
  523. StudioFrameAdvance();
  524. DispatchAnimEvents ( this );
  525. SetLocalAngles( vAngles );
  526. SetLocalAngularVelocity( vAngularVelocity );
  527. Vector vForward, vRight;
  528. AngleVectors( vAngles, &vForward, &vRight, NULL );
  529. #if DEBUG_BEAMS
  530. if ( m_fPathBlocked )
  531. {
  532. float color = m_obstacle * 30;
  533. if ( m_obstacle == 1.0 )
  534. color = 0;
  535. if ( color > 255 )
  536. color = 255;
  537. NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vForward * LEECH_CHECK_DIST, 255, color, color, false, 0.1f );
  538. }
  539. else
  540. NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vForward * LEECH_CHECK_DIST, 255, 255, 0, false, 0.1f );
  541. NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vRight * (vAngularVelocity.y*0.25), 0, 0, 255, false, 0.1f );
  542. #endif
  543. }
  544. void CNPC_Leech::Event_Killed( const CTakeDamageInfo &info )
  545. {
  546. Vector vecSplatDir;
  547. trace_t tr;
  548. //ALERT(at_aiconsole, "Leech: killed\n");
  549. // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
  550. CBaseEntity *pOwner = GetOwnerEntity();
  551. if (pOwner)
  552. pOwner->DeathNotice( this );
  553. // When we hit the ground, play the "death_end" activity
  554. if ( GetWaterLevel() )
  555. {
  556. QAngle qAngles = GetAbsAngles();
  557. QAngle qAngularVel = GetLocalAngularVelocity();
  558. Vector vOrigin = GetLocalOrigin();
  559. qAngles.z = 0;
  560. qAngles.x = 0;
  561. vOrigin.z += 1;
  562. SetAbsVelocity( vec3_origin );
  563. if ( random->RandomInt( 0, 99 ) < 70 )
  564. qAngularVel.y = random->RandomInt( -720, 720 );
  565. SetAbsAngles( qAngles );
  566. SetLocalAngularVelocity( qAngularVel );
  567. SetAbsOrigin( vOrigin );
  568. SetGravity ( 0.02 );
  569. SetGroundEntity( NULL );
  570. SetActivity( ACT_DIESIMPLE );
  571. }
  572. else
  573. SetActivity( ACT_DIEFORWARD );
  574. SetMoveType( MOVETYPE_FLYGRAVITY );
  575. m_takedamage = DAMAGE_NO;
  576. SetThink( &CNPC_Leech::DeadThink );
  577. }