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.

1558 lines
42 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Ichthyosaur - buh bum... buh bum...
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_basenpc.h"
  9. #include "ai_task.h"
  10. #include "ai_default.h"
  11. #include "ai_schedule.h"
  12. #include "ai_hull.h"
  13. #include "ai_interactions.h"
  14. #include "ai_navigator.h"
  15. #include "ai_motor.h"
  16. #include "activitylist.h"
  17. #include "game.h"
  18. #include "npcevent.h"
  19. #include "player.h"
  20. #include "entitylist.h"
  21. #include "soundenvelope.h"
  22. #include "shake.h"
  23. #include "ndebugoverlay.h"
  24. #include "vstdlib/random.h"
  25. #include "engine/IEngineSound.h"
  26. #include "movevars_shared.h"
  27. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. ConVar sk_ichthyosaur_health( "sk_ichthyosaur_health", "0" );
  30. ConVar sk_ichthyosaur_melee_dmg( "sk_ichthyosaur_melee_dmg", "0" );
  31. #define ICHTHYOSAUR_MODEL "models/ichthyosaur.mdl"
  32. #define ICH_HEIGHT_PREFERENCE 16.0f
  33. #define ICH_DEPTH_PREFERENCE 8.0f
  34. #define ICH_WAYPOINT_DISTANCE 64.0f
  35. #define ICH_AE_BITE 11
  36. #define ICH_AE_BITE_START 12
  37. #define ICH_SWIM_SPEED_WALK 150
  38. #define ICH_SWIM_SPEED_RUN 500
  39. #define ICH_MIN_TURN_SPEED 4.0f
  40. #define ICH_MAX_TURN_SPEED 30.0f
  41. #define ENVELOPE_CONTROLLER (CSoundEnvelopeController::GetController())
  42. #define FEELER_COLLISION 0
  43. #define FEELER_COLLISION_VISUALIZE (FEELER_COLLISION&&0)
  44. enum IchthyosaurMoveType_t
  45. {
  46. ICH_MOVETYPE_SEEK = 0, // Fly through the target without stopping.
  47. ICH_MOVETYPE_ARRIVE // Slow down and stop at target.
  48. };
  49. //
  50. // CNPC_Ichthyosaur
  51. //
  52. class CNPC_Ichthyosaur : public CAI_BaseNPC
  53. {
  54. public:
  55. DECLARE_CLASS( CNPC_Ichthyosaur, CAI_BaseNPC );
  56. DECLARE_DATADESC();
  57. CNPC_Ichthyosaur( void ) {}
  58. int SelectSchedule( void );
  59. int MeleeAttack1Conditions( float flDot, float flDist );
  60. int OnTakeDamage_Alive( const CTakeDamageInfo &info );
  61. int TranslateSchedule( int type );
  62. void Precache( void );
  63. void Spawn( void );
  64. void MoveFlyExecute( CBaseEntity *pTargetEnt, const Vector & vecDir, float flDistance, float flInterval );
  65. void HandleAnimEvent( animevent_t *pEvent );
  66. void PrescheduleThink( void );
  67. bool OverrideMove( float flInterval );
  68. void StartTask( const Task_t *pTask );
  69. void RunTask( const Task_t *pTask );
  70. void TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition );
  71. float GetDefaultNavGoalTolerance();
  72. float MaxYawSpeed( void );
  73. Class_T Classify( void ) { return CLASS_ANTLION; } //FIXME: No classification for various wildlife?
  74. bool FVisible( CBaseEntity *pEntity, int traceMask = MASK_BLOCKLOS, CBaseEntity **ppBlocker = NULL );
  75. private:
  76. bool SteerAvoidObstacles( Vector &Steer, const Vector &Velocity, const Vector &Forward, const Vector &Right, const Vector &Up );
  77. bool Beached( void );
  78. void DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType );
  79. void SteerArrive( Vector &Steer, const Vector &Target );
  80. void SteerSeek( Vector &Steer, const Vector &Target );
  81. void ClampSteer( Vector &SteerAbs, Vector &SteerRel, Vector &forward, Vector &right, Vector &up );
  82. void AddSwimNoise( Vector *velocity );
  83. void Bite( void );
  84. void EnsnareVictim( CBaseEntity *pVictim );
  85. void ReleaseVictim( void );
  86. void DragVictim( float moveDist );
  87. void SetPoses( Vector moveRel, float speed );
  88. //void IchTouch( CBaseEntity *pOther );
  89. float GetGroundSpeed( void );
  90. #if FEELER_COLLISION
  91. Vector DoProbe( const Vector &Probe );
  92. Vector m_LastSteer;
  93. #endif
  94. CBaseEntity *m_pVictim;
  95. static const Vector m_vecAccelerationMax;
  96. static const Vector m_vecAccelerationMin;
  97. Vector m_vecLastMoveTarget;
  98. float m_flNextBiteTime;
  99. float m_flHoldTime;
  100. float m_flSwimSpeed;
  101. float m_flTailYaw;
  102. float m_flTailPitch;
  103. float m_flNextPingTime;
  104. float m_flNextGrowlTime;
  105. bool m_bHasMoveTarget;
  106. bool m_bIgnoreSurface;
  107. //CSoundPatch *m_pSwimSound;
  108. //CSoundPatch *m_pVoiceSound;
  109. DEFINE_CUSTOM_AI;
  110. };
  111. //Acceleration definitions
  112. const Vector CNPC_Ichthyosaur::m_vecAccelerationMax = Vector( 256, 1024, 512 );
  113. const Vector CNPC_Ichthyosaur::m_vecAccelerationMin = Vector( -256, -1024, -512 );
  114. //Data description
  115. BEGIN_DATADESC( CNPC_Ichthyosaur )
  116. // Silence classcheck
  117. // DEFINE_FIELD( m_LastSteer, FIELD_VECTOR ),
  118. DEFINE_FIELD( m_pVictim, FIELD_CLASSPTR ),
  119. DEFINE_FIELD( m_vecLastMoveTarget, FIELD_VECTOR ),
  120. DEFINE_FIELD( m_flNextBiteTime, FIELD_FLOAT ),
  121. DEFINE_FIELD( m_flHoldTime, FIELD_FLOAT ),
  122. DEFINE_FIELD( m_flSwimSpeed, FIELD_FLOAT ),
  123. DEFINE_FIELD( m_flTailYaw, FIELD_FLOAT ),
  124. DEFINE_FIELD( m_flTailPitch, FIELD_FLOAT ),
  125. DEFINE_FIELD( m_flNextPingTime, FIELD_FLOAT ),
  126. DEFINE_FIELD( m_flNextGrowlTime, FIELD_FLOAT ),
  127. DEFINE_FIELD( m_bHasMoveTarget, FIELD_BOOLEAN ),
  128. DEFINE_FIELD( m_bIgnoreSurface, FIELD_BOOLEAN ),
  129. //DEFINE_FUNCTION( IchTouch ),
  130. END_DATADESC()
  131. //Schedules
  132. enum IchSchedules
  133. {
  134. SCHED_ICH_CHASE_ENEMY = LAST_SHARED_SCHEDULE,
  135. SCHED_ICH_PATROL_RUN,
  136. SCHED_ICH_PATROL_WALK,
  137. SCHED_ICH_DROWN_VICTIM,
  138. SCHED_ICH_MELEE_ATTACK1,
  139. SCHED_ICH_THRASH,
  140. };
  141. //Tasks
  142. enum IchTasks
  143. {
  144. TASK_ICH_GET_PATH_TO_RANDOM_NODE = LAST_SHARED_TASK,
  145. TASK_ICH_GET_PATH_TO_DROWN_NODE,
  146. TASK_ICH_THRASH_PATH,
  147. };
  148. //Activities
  149. int ACT_ICH_THRASH;
  150. int ACT_ICH_BITE_HIT;
  151. int ACT_ICH_BITE_MISS;
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void CNPC_Ichthyosaur::InitCustomSchedules( void )
  156. {
  157. INIT_CUSTOM_AI( CNPC_Ichthyosaur );
  158. //Interaction REGISTER_INTERACTION( g_interactionAntlionAttacked );
  159. //Schedules
  160. ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_CHASE_ENEMY );
  161. ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_RUN );
  162. ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_WALK );
  163. ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_DROWN_VICTIM );
  164. ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_MELEE_ATTACK1 );
  165. ADD_CUSTOM_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_THRASH );
  166. //Tasks
  167. ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_GET_PATH_TO_RANDOM_NODE );
  168. ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_GET_PATH_TO_DROWN_NODE );
  169. ADD_CUSTOM_TASK( CNPC_Ichthyosaur, TASK_ICH_THRASH_PATH );
  170. //Conditions ADD_CUSTOM_CONDITION( CNPC_CombineGuard, COND_ANTLIONGRUB_HEARD_SQUEAL );
  171. //Activities
  172. ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_THRASH );
  173. ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_BITE_HIT );
  174. ADD_CUSTOM_ACTIVITY( CNPC_Ichthyosaur, ACT_ICH_BITE_MISS );
  175. AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_CHASE_ENEMY );
  176. AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_RUN );
  177. AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_PATROL_WALK );
  178. AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_DROWN_VICTIM );
  179. AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_MELEE_ATTACK1 );
  180. AI_LOAD_SCHEDULE( CNPC_Ichthyosaur, SCHED_ICH_THRASH );
  181. }
  182. LINK_ENTITY_TO_CLASS( npc_ichthyosaur, CNPC_Ichthyosaur );
  183. IMPLEMENT_CUSTOM_AI( npc_ichthyosaur, CNPC_Ichthyosaur );
  184. //-----------------------------------------------------------------------------
  185. // Purpose:
  186. //-----------------------------------------------------------------------------
  187. void CNPC_Ichthyosaur::Precache( void )
  188. {
  189. PrecacheModel( ICHTHYOSAUR_MODEL );
  190. PrecacheScriptSound( "NPC_Ichthyosaur.Bite" );
  191. PrecacheScriptSound( "NPC_Ichthyosaur.BiteMiss" );
  192. PrecacheScriptSound( "NPC_Ichthyosaur.AttackGrowl" );
  193. BaseClass::Precache();
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose:
  197. //-----------------------------------------------------------------------------
  198. void CNPC_Ichthyosaur::Spawn( void )
  199. {
  200. Precache();
  201. SetModel( ICHTHYOSAUR_MODEL );
  202. SetHullType(HULL_LARGE_CENTERED);
  203. SetHullSizeNormal();
  204. SetDefaultEyeOffset();
  205. SetNavType( NAV_FLY );
  206. m_NPCState = NPC_STATE_NONE;
  207. SetBloodColor( BLOOD_COLOR_RED );
  208. m_iHealth = sk_ichthyosaur_health.GetFloat();
  209. m_iMaxHealth = m_iHealth;
  210. m_flFieldOfView = -0.707; // 270 degrees
  211. SetDistLook( 1024 );
  212. SetSolid( SOLID_BBOX );
  213. AddSolidFlags( FSOLID_NOT_STANDABLE );
  214. SetMoveType( MOVETYPE_STEP );
  215. AddFlag( FL_FLY | FL_STEPMOVEMENT );
  216. m_flGroundSpeed = ICH_SWIM_SPEED_RUN;
  217. m_bIgnoreSurface = false;
  218. m_flSwimSpeed = 0.0f;
  219. m_flTailYaw = 0.0f;
  220. m_flTailPitch = 0.0f;
  221. m_flNextBiteTime = gpGlobals->curtime;
  222. m_flHoldTime = gpGlobals->curtime;
  223. m_flNextPingTime = gpGlobals->curtime;
  224. m_flNextGrowlTime = gpGlobals->curtime;
  225. #if FEELER_COLLISION
  226. Vector forward;
  227. GetVectors( &forward, NULL, NULL );
  228. m_vecCurrentVelocity = forward * m_flGroundSpeed;
  229. #endif
  230. //SetTouch( IchTouch );
  231. CapabilitiesClear();
  232. CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_INNATE_MELEE_ATTACK1 );
  233. NPCInit();
  234. //m_pSwimSound = ENVELOPE_CONTROLLER.SoundCreate( edict(), CHAN_BODY, "xxxCONVERTTOGAMESOUNDS!!!npc/ichthyosaur/ich_amb1wav", ATTN_NORM );
  235. //m_pVoiceSound = ENVELOPE_CONTROLLER.SoundCreate( edict(), CHAN_VOICE, "xxxCONVERTTOGAMESOUNDS!!!npc/ichthyosaur/water_breathwav", ATTN_IDLE );
  236. //ENVELOPE_CONTROLLER.Play( m_pSwimSound, 1.0f, 100 );
  237. //ENVELOPE_CONTROLLER.Play( m_pVoiceSound,1.0f, 100 );
  238. BaseClass::Spawn();
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose:
  242. // Input : *pOther -
  243. //-----------------------------------------------------------------------------
  244. /*
  245. void CNPC_Ichthyosaur::IchTouch( CBaseEntity *pOther )
  246. {
  247. }
  248. */
  249. //-----------------------------------------------------------------------------
  250. // Purpose:
  251. //-----------------------------------------------------------------------------
  252. int CNPC_Ichthyosaur::SelectSchedule( void )
  253. {
  254. if ( m_NPCState == NPC_STATE_COMBAT )
  255. {
  256. if ( m_flHoldTime > gpGlobals->curtime )
  257. return SCHED_ICH_DROWN_VICTIM;
  258. if ( m_flNextBiteTime > gpGlobals->curtime )
  259. return SCHED_PATROL_RUN;
  260. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  261. return SCHED_MELEE_ATTACK1;
  262. return SCHED_CHASE_ENEMY;
  263. }
  264. return BaseClass::SelectSchedule();
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Handles movement towards the last move target.
  268. // Input : flInterval -
  269. //-----------------------------------------------------------------------------
  270. bool CNPC_Ichthyosaur::OverrideMove( float flInterval )
  271. {
  272. m_flGroundSpeed = GetGroundSpeed();
  273. if ( m_bHasMoveTarget )
  274. {
  275. DoMovement( flInterval, m_vecLastMoveTarget, ICH_MOVETYPE_ARRIVE );
  276. }
  277. else
  278. {
  279. DoMovement( flInterval, GetLocalOrigin(), ICH_MOVETYPE_ARRIVE );
  280. }
  281. return true;
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose:
  285. // Input : &probe -
  286. // Output : Vector
  287. //-----------------------------------------------------------------------------
  288. #if FEELER_COLLISION
  289. Vector CNPC_Ichthyosaur::DoProbe( const Vector &probe )
  290. {
  291. trace_t tr;
  292. float fraction = 1.0f;
  293. bool collided = false;
  294. Vector normal = Vector( 0, 0, -1 );
  295. float waterLevel = UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+150 );
  296. waterLevel -= GetAbsOrigin().z;
  297. waterLevel /= 150;
  298. if ( waterLevel < 1.0f )
  299. {
  300. collided = true;
  301. fraction = waterLevel;
  302. }
  303. AI_TraceHull( GetAbsOrigin(), probe, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  304. if ( ( collided == false ) || ( tr.fraction < fraction ) )
  305. {
  306. fraction = tr.fraction;
  307. normal = tr.plane.normal;
  308. }
  309. if ( ( fraction < 1.0f ) && ( GetEnemy() == NULL || tr.u.ent != GetEnemy()->pev ) )
  310. {
  311. #if FEELER_COLLISION_VISUALIZE
  312. NDebugOverlay::Line( GetLocalOrigin(), probe, 255, 0, 0, false, 0.1f );
  313. #endif
  314. Vector probeDir = probe - GetLocalOrigin();
  315. Vector normalToProbeAndWallNormal = probeDir.Cross( normal );
  316. Vector steeringVector = normalToProbeAndWallNormal.Cross( probeDir );
  317. Vector velDir = GetAbsVelocity();
  318. VectorNormalize( velDir );
  319. float steeringForce = m_flGroundSpeed * ( 1.0f - fraction ) * normal.Dot( velDir );
  320. if ( steeringForce < 0.0f )
  321. {
  322. steeringForce = -steeringForce;
  323. }
  324. velDir = steeringVector;
  325. VectorNormalize( velDir );
  326. steeringVector = steeringForce * velDir;
  327. return steeringVector;
  328. }
  329. #if FEELER_COLLISION_VISUALIZE
  330. NDebugOverlay::Line( GetLocalOrigin(), probe, 0, 255, 0, false, 0.1f );
  331. #endif
  332. return Vector( 0.0f, 0.0f, 0.0f );
  333. }
  334. #endif
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Move the victim of a drag along with us
  337. // Input : moveDist - our amount of travel
  338. //-----------------------------------------------------------------------------
  339. #define DRAG_OFFSET 50.0f
  340. void CNPC_Ichthyosaur::DragVictim( float moveDist )
  341. {
  342. Vector mins, maxs;
  343. float width;
  344. mins = WorldAlignMins();
  345. maxs = WorldAlignMaxs();
  346. width = ( maxs.y - mins.y ) * 0.5f;
  347. Vector forward, up;
  348. GetVectors( &forward, NULL, &up );
  349. Vector newPos = GetAbsOrigin() + ( (forward+(up*0.25f)) * ( moveDist + width + DRAG_OFFSET ) );
  350. trace_t tr;
  351. AI_TraceEntity( this, m_pVictim->GetAbsOrigin(), newPos, MASK_NPCSOLID, &tr );
  352. if ( ( tr.fraction == 1.0f ) && ( tr.m_pEnt != this ) )
  353. {
  354. UTIL_SetOrigin( m_pVictim, tr.endpos );
  355. }
  356. else
  357. {
  358. ReleaseVictim();
  359. }
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Purpose: Determines the pose parameters for the bending of the body and tail speed
  363. // Input : moveRel - the dot products for the deviation off of each direction (f,r,u)
  364. // speed - speed of the fish
  365. //-----------------------------------------------------------------------------
  366. void CNPC_Ichthyosaur::SetPoses( Vector moveRel, float speed )
  367. {
  368. float movePerc, moveBase;
  369. //Find out how fast we're moving in our animations boundaries
  370. if ( GetIdealActivity() == ACT_WALK )
  371. {
  372. moveBase = 0.5f;
  373. movePerc = moveBase * ( speed / ICH_SWIM_SPEED_WALK );
  374. }
  375. else
  376. {
  377. moveBase = 1.0f;
  378. movePerc = moveBase * ( speed / ICH_SWIM_SPEED_RUN );
  379. }
  380. Vector tailPosition;
  381. float flSwimSpeed = movePerc;
  382. //Forward deviation
  383. if ( moveRel.x > 0 )
  384. {
  385. flSwimSpeed *= moveBase + (( moveRel.x / m_vecAccelerationMax.x )*moveBase);
  386. }
  387. else if ( moveRel.x < 0 )
  388. {
  389. flSwimSpeed *= moveBase - (( moveRel.x / m_vecAccelerationMin.x )*moveBase);
  390. }
  391. //Vertical deviation
  392. if ( moveRel.z > 0 )
  393. {
  394. tailPosition[PITCH] = -90.0f * ( moveRel.z / m_vecAccelerationMax.z );
  395. }
  396. else if ( moveRel.z < 0 )
  397. {
  398. tailPosition[PITCH] = 90.0f * ( moveRel.z / m_vecAccelerationMin.z );
  399. }
  400. else
  401. {
  402. tailPosition[PITCH] = 0.0f;
  403. }
  404. //Lateral deviation
  405. if ( moveRel.y > 0 )
  406. {
  407. tailPosition[ROLL] = 25 * moveRel.y / m_vecAccelerationMax.y;
  408. tailPosition[YAW] = -1.0f * moveRel.y / m_vecAccelerationMax.y;
  409. }
  410. else if ( moveRel.y < 0 )
  411. {
  412. tailPosition[ROLL] = -25 * moveRel.y / m_vecAccelerationMin.y;
  413. tailPosition[YAW] = moveRel.y / m_vecAccelerationMin.y;
  414. }
  415. else
  416. {
  417. tailPosition[ROLL] = 0.0f;
  418. tailPosition[YAW] = 0.0f;
  419. }
  420. //Clamp
  421. flSwimSpeed = clamp( flSwimSpeed, 0.25f, 1.0f );
  422. tailPosition[YAW] = clamp( tailPosition[YAW], -90.0f, 90.0f );
  423. tailPosition[PITCH] = clamp( tailPosition[PITCH], -90.0f, 90.0f );
  424. //Blend
  425. m_flTailYaw = ( m_flTailYaw * 0.8f ) + ( tailPosition[YAW] * 0.2f );
  426. m_flTailPitch = ( m_flTailPitch * 0.8f ) + ( tailPosition[PITCH] * 0.2f );
  427. m_flSwimSpeed = ( m_flSwimSpeed * 0.8f ) + ( flSwimSpeed * 0.2f );
  428. //Pose the body
  429. SetPoseParameter( 0, m_flSwimSpeed );
  430. SetPoseParameter( 1, m_flTailYaw );
  431. SetPoseParameter( 2, m_flTailPitch );
  432. //FIXME: Until the sequence info is reset properly after SetPoseParameter
  433. if ( ( GetActivity() == ACT_RUN ) || ( GetActivity() == ACT_WALK ) )
  434. {
  435. ResetSequenceInfo();
  436. }
  437. //Face our current velocity
  438. GetMotor()->SetIdealYawAndUpdate( UTIL_AngleMod( CalcIdealYaw( GetAbsOrigin() + GetAbsVelocity() ) ), AI_KEEP_YAW_SPEED );
  439. float pitch = 0.0f;
  440. if ( speed != 0.0f )
  441. {
  442. pitch = -RAD2DEG( asin( GetAbsVelocity().z / speed ) );
  443. }
  444. //FIXME: Framerate dependant
  445. QAngle angles = GetLocalAngles();
  446. angles.x = (angles.x * 0.8f) + (pitch * 0.2f);
  447. angles.z = (angles.z * 0.9f) + (tailPosition[ROLL] * 0.1f);
  448. SetLocalAngles( angles );
  449. }
  450. #define LATERAL_NOISE_MAX 2.0f
  451. #define LATERAL_NOISE_FREQ 1.0f
  452. #define VERTICAL_NOISE_MAX 2.0f
  453. #define VERTICAL_NOISE_FREQ 1.0f
  454. //-----------------------------------------------------------------------------
  455. // Purpose:
  456. // Input : velocity -
  457. //-----------------------------------------------------------------------------
  458. void CNPC_Ichthyosaur::AddSwimNoise( Vector *velocity )
  459. {
  460. Vector right, up;
  461. GetVectors( NULL, &right, &up );
  462. float lNoise, vNoise;
  463. lNoise = LATERAL_NOISE_MAX * sin( gpGlobals->curtime * LATERAL_NOISE_FREQ );
  464. vNoise = VERTICAL_NOISE_MAX * sin( gpGlobals->curtime * VERTICAL_NOISE_FREQ );
  465. (*velocity) += ( right * lNoise ) + ( up * vNoise );
  466. }
  467. //-----------------------------------------------------------------------------
  468. // Purpose:
  469. // Input : flInterval -
  470. // &m_LastMoveTarget -
  471. // eMoveType -
  472. //-----------------------------------------------------------------------------
  473. void CNPC_Ichthyosaur::DoMovement( float flInterval, const Vector &MoveTarget, int eMoveType )
  474. {
  475. // dvs: something is setting this bit, causing us to stop moving and get stuck that way
  476. Forget( bits_MEMORY_TURNING );
  477. Vector Steer, SteerAvoid, SteerRel;
  478. Vector forward, right, up;
  479. //Get our orientation vectors.
  480. GetVectors( &forward, &right, &up);
  481. if ( ( GetActivity() == ACT_MELEE_ATTACK1 ) && ( GetEnemy() != NULL ) )
  482. {
  483. SteerSeek( Steer, GetEnemy()->GetAbsOrigin() );
  484. }
  485. else
  486. {
  487. //If we are approaching our goal, use an arrival steering mechanism.
  488. if ( eMoveType == ICH_MOVETYPE_ARRIVE )
  489. {
  490. SteerArrive( Steer, MoveTarget );
  491. }
  492. else
  493. {
  494. //Otherwise use a seek steering mechanism.
  495. SteerSeek( Steer, MoveTarget );
  496. }
  497. }
  498. #if FEELER_COLLISION
  499. Vector f, u, l, r, d;
  500. float probeLength = GetAbsVelocity().Length();
  501. if ( probeLength < 150 )
  502. probeLength = 150;
  503. if ( probeLength > 500 )
  504. probeLength = 500;
  505. f = DoProbe( GetLocalOrigin() + (probeLength * forward) );
  506. r = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+right)) );
  507. l = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-right)) );
  508. u = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward+up)) );
  509. d = DoProbe( GetLocalOrigin() + (probeLength/3 * (forward-up)) );
  510. SteerAvoid = f+r+l+u+d;
  511. //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+SteerAvoid, 255, 255, 0, false, 0.1f );
  512. if ( SteerAvoid.LengthSqr() )
  513. {
  514. Steer = (SteerAvoid*0.5f);
  515. }
  516. m_vecVelocity = m_vecVelocity + (Steer*0.5f);
  517. VectorNormalize( m_vecVelocity );
  518. SteerRel.x = forward.Dot( m_vecVelocity );
  519. SteerRel.y = right.Dot( m_vecVelocity );
  520. SteerRel.z = up.Dot( m_vecVelocity );
  521. m_vecVelocity *= m_flGroundSpeed;
  522. #else
  523. //See if we need to avoid any obstacles.
  524. if ( SteerAvoidObstacles( SteerAvoid, GetAbsVelocity(), forward, right, up ) )
  525. {
  526. //Take the avoidance vector
  527. Steer = SteerAvoid;
  528. }
  529. //Clamp our ideal steering vector to within our physical limitations.
  530. ClampSteer( Steer, SteerRel, forward, right, up );
  531. ApplyAbsVelocityImpulse( Steer * flInterval );
  532. #endif
  533. Vector vecNewVelocity = GetAbsVelocity();
  534. float flLength = vecNewVelocity.Length();
  535. //Clamp our final speed
  536. if ( flLength > m_flGroundSpeed )
  537. {
  538. vecNewVelocity *= ( m_flGroundSpeed / flLength );
  539. flLength = m_flGroundSpeed;
  540. }
  541. Vector workVelocity = vecNewVelocity;
  542. AddSwimNoise( &workVelocity );
  543. // Pose the fish properly
  544. SetPoses( SteerRel, flLength );
  545. //Drag our victim before moving
  546. if ( m_pVictim != NULL )
  547. {
  548. DragVictim( (workVelocity*flInterval).Length() );
  549. }
  550. //Move along the current velocity vector
  551. if ( WalkMove( workVelocity * flInterval, MASK_NPCSOLID ) == false )
  552. {
  553. //Attempt a half-step
  554. if ( WalkMove( (workVelocity*0.5f) * flInterval, MASK_NPCSOLID) == false )
  555. {
  556. //Restart the velocity
  557. //VectorNormalize( m_vecVelocity );
  558. vecNewVelocity *= 0.5f;
  559. }
  560. else
  561. {
  562. //Cut our velocity in half
  563. vecNewVelocity *= 0.5f;
  564. }
  565. }
  566. SetAbsVelocity( vecNewVelocity );
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose: Gets a steering vector to arrive at a target location with a
  570. // relatively small velocity.
  571. // Input : Steer - Receives the ideal steering vector.
  572. // Target - Target position at which to arrive.
  573. //-----------------------------------------------------------------------------
  574. void CNPC_Ichthyosaur::SteerArrive(Vector &Steer, const Vector &Target)
  575. {
  576. Vector Offset = Target - GetLocalOrigin();
  577. float fTargetDistance = Offset.Length();
  578. float fIdealSpeed = m_flGroundSpeed * (fTargetDistance / ICH_WAYPOINT_DISTANCE);
  579. float fClippedSpeed = MIN( fIdealSpeed, m_flGroundSpeed );
  580. Vector DesiredVelocity( 0, 0, 0 );
  581. if ( fTargetDistance > ICH_WAYPOINT_DISTANCE )
  582. {
  583. DesiredVelocity = (fClippedSpeed / fTargetDistance) * Offset;
  584. }
  585. Steer = DesiredVelocity - GetAbsVelocity();
  586. }
  587. //-----------------------------------------------------------------------------
  588. // Purpose: Gets a steering vector to move towards a target position as quickly
  589. // as possible.
  590. // Input : Steer - Receives the ideal steering vector.
  591. // Target - Target position to seek.
  592. //-----------------------------------------------------------------------------
  593. void CNPC_Ichthyosaur::SteerSeek( Vector &Steer, const Vector &Target )
  594. {
  595. Vector offset = Target - GetLocalOrigin();
  596. VectorNormalize( offset );
  597. Vector DesiredVelocity = m_flGroundSpeed * offset;
  598. Steer = DesiredVelocity - GetAbsVelocity();
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose:
  602. // Input : &Steer -
  603. // Output : Returns true on success, false on failure.
  604. //-----------------------------------------------------------------------------
  605. bool CNPC_Ichthyosaur::SteerAvoidObstacles(Vector &Steer, const Vector &Velocity, const Vector &Forward, const Vector &Right, const Vector &Up)
  606. {
  607. trace_t tr;
  608. bool collided = false;
  609. Vector dir = Velocity;
  610. float speed = VectorNormalize( dir );
  611. //Look ahead one second and avoid whatever is in our way.
  612. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + (dir*speed), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  613. Vector forward;
  614. GetVectors( &forward, NULL, NULL );
  615. //If we're hitting our enemy, just continue on
  616. if ( ( GetEnemy() != NULL ) && ( tr.m_pEnt == GetEnemy() ) )
  617. return false;
  618. if ( tr.fraction < 1.0f )
  619. {
  620. CBaseEntity *pBlocker = tr.m_pEnt;
  621. if ( ( pBlocker != NULL ) && ( pBlocker->MyNPCPointer() != NULL ) )
  622. {
  623. DevMsg( 2, "Avoiding an NPC\n" );
  624. Vector HitOffset = tr.endpos - GetAbsOrigin();
  625. Vector SteerUp = CrossProduct( HitOffset, Velocity );
  626. Steer = CrossProduct( SteerUp, Velocity );
  627. VectorNormalize( Steer );
  628. /*Vector probeDir = tr.endpos - GetAbsOrigin();
  629. Vector normalToProbeAndWallNormal = probeDir.Cross( tr.plane.normal );
  630. Steer = normalToProbeAndWallNormal.Cross( probeDir );
  631. VectorNormalize( Steer );*/
  632. if ( tr.fraction > 0 )
  633. {
  634. Steer = (Steer * Velocity.Length()) / tr.fraction;
  635. //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f );
  636. }
  637. else
  638. {
  639. Steer = (Steer * 1000 * Velocity.Length());
  640. //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f );
  641. }
  642. }
  643. else
  644. {
  645. if ( ( pBlocker != NULL ) && ( pBlocker == GetEnemy() ) )
  646. {
  647. DevMsg( "Avoided collision\n" );
  648. return false;
  649. }
  650. DevMsg( 2, "Avoiding the world\n" );
  651. Vector steeringVector = tr.plane.normal;
  652. if ( tr.fraction == 0.0f )
  653. return false;
  654. Steer = steeringVector * ( Velocity.Length() / tr.fraction );
  655. //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f );
  656. }
  657. //return true;
  658. collided = true;
  659. }
  660. //Try to remain 8 feet above the ground.
  661. AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, -ICH_HEIGHT_PREFERENCE), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  662. if ( tr.fraction < 1.0f )
  663. {
  664. Steer += Vector( 0, 0, m_vecAccelerationMax.z / tr.fraction );
  665. collided = true;
  666. }
  667. //Stay under the surface
  668. if ( m_bIgnoreSurface == false )
  669. {
  670. float waterLevel = ( UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+ICH_DEPTH_PREFERENCE ) - GetAbsOrigin().z ) / ICH_DEPTH_PREFERENCE;
  671. if ( waterLevel < 1.0f )
  672. {
  673. Steer += -Vector( 0, 0, m_vecAccelerationMax.z / waterLevel );
  674. collided = true;
  675. }
  676. }
  677. return collided;
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Purpose: Clamps the desired steering vector based on the limitations of this
  681. // vehicle.
  682. // Input : SteerAbs - The vector indicating our ideal steering vector. Receives
  683. // the clamped steering vector in absolute (x,y,z) coordinates.
  684. // SteerRel - Receives the clamped steering vector in relative (forward, right, up)
  685. // coordinates.
  686. // forward - Our current forward vector.
  687. // right - Our current right vector.
  688. // up - Our current up vector.
  689. //-----------------------------------------------------------------------------
  690. void CNPC_Ichthyosaur::ClampSteer(Vector &SteerAbs, Vector &SteerRel, Vector &forward, Vector &right, Vector &up)
  691. {
  692. float fForwardSteer = DotProduct(SteerAbs, forward);
  693. float fRightSteer = DotProduct(SteerAbs, right);
  694. float fUpSteer = DotProduct(SteerAbs, up);
  695. if (fForwardSteer > 0)
  696. {
  697. fForwardSteer = MIN(fForwardSteer, m_vecAccelerationMax.x);
  698. }
  699. else
  700. {
  701. fForwardSteer = MAX(fForwardSteer, m_vecAccelerationMin.x);
  702. }
  703. if (fRightSteer > 0)
  704. {
  705. fRightSteer = MIN(fRightSteer, m_vecAccelerationMax.y);
  706. }
  707. else
  708. {
  709. fRightSteer = MAX(fRightSteer, m_vecAccelerationMin.y);
  710. }
  711. if (fUpSteer > 0)
  712. {
  713. fUpSteer = MIN(fUpSteer, m_vecAccelerationMax.z);
  714. }
  715. else
  716. {
  717. fUpSteer = MAX(fUpSteer, m_vecAccelerationMin.z);
  718. }
  719. SteerAbs = (fForwardSteer*forward) + (fRightSteer*right) + (fUpSteer*up);
  720. SteerRel.x = fForwardSteer;
  721. SteerRel.y = fRightSteer;
  722. SteerRel.z = fUpSteer;
  723. }
  724. //-----------------------------------------------------------------------------
  725. // Purpose:
  726. // Input : *pTargetEnt -
  727. // vecDir -
  728. // flDistance -
  729. // flInterval -
  730. //-----------------------------------------------------------------------------
  731. void CNPC_Ichthyosaur::MoveFlyExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flDistance, float flInterval )
  732. {
  733. IchthyosaurMoveType_t eMoveType = ( GetNavigator()->CurWaypointIsGoal() ) ? ICH_MOVETYPE_ARRIVE : ICH_MOVETYPE_SEEK;
  734. m_flGroundSpeed = GetGroundSpeed();
  735. Vector moveGoal = GetNavigator()->GetCurWaypointPos();
  736. //See if we can move directly to our goal
  737. if ( ( GetEnemy() != NULL ) && ( GetNavigator()->GetGoalTarget() == (CBaseEntity *) GetEnemy() ) )
  738. {
  739. trace_t tr;
  740. Vector goalPos = GetEnemy()->GetAbsOrigin() + ( GetEnemy()->GetSmoothedVelocity() * 0.5f );
  741. AI_TraceHull( GetAbsOrigin(), goalPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, GetEnemy(), COLLISION_GROUP_NONE, &tr );
  742. if ( tr.fraction == 1.0f )
  743. {
  744. moveGoal = tr.endpos;
  745. }
  746. }
  747. //Move
  748. DoMovement( flInterval, moveGoal, eMoveType );
  749. //Save the info from that run
  750. m_vecLastMoveTarget = moveGoal;
  751. m_bHasMoveTarget = true;
  752. }
  753. //-----------------------------------------------------------------------------
  754. // Purpose:
  755. // Input : *pEntity -
  756. // Output : Returns true on success, false on failure.
  757. //-----------------------------------------------------------------------------
  758. bool CNPC_Ichthyosaur::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
  759. {
  760. // don't look through water
  761. if ( GetWaterLevel() != pEntity->GetWaterLevel() )
  762. return false;
  763. return BaseClass::FVisible( pEntity, traceMask, ppBlocker );
  764. }
  765. //-----------------------------------------------------------------------------
  766. // Purpose: Get our conditions for a melee attack
  767. // Input : flDot -
  768. // flDist -
  769. // Output : int
  770. //-----------------------------------------------------------------------------
  771. int CNPC_Ichthyosaur::MeleeAttack1Conditions( float flDot, float flDist )
  772. {
  773. Vector predictedDir = ( (GetEnemy()->GetAbsOrigin()+(GetEnemy()->GetSmoothedVelocity())) - GetAbsOrigin() );
  774. float flPredictedDist = VectorNormalize( predictedDir );
  775. Vector vBodyDir;
  776. GetVectors( &vBodyDir, NULL, NULL );
  777. float flPredictedDot = DotProduct( predictedDir, vBodyDir );
  778. if ( flPredictedDot < 0.8f )
  779. return COND_NOT_FACING_ATTACK;
  780. if ( ( flPredictedDist > ( GetAbsVelocity().Length() * 0.5f) ) && ( flDist > 128.0f ) )
  781. return COND_TOO_FAR_TO_ATTACK;
  782. return COND_CAN_MELEE_ATTACK1;
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Purpose:
  786. // Input : *pEvent -
  787. //-----------------------------------------------------------------------------
  788. void CNPC_Ichthyosaur::HandleAnimEvent( animevent_t *pEvent )
  789. {
  790. switch ( pEvent->event )
  791. {
  792. case ICH_AE_BITE:
  793. Bite();
  794. break;
  795. case ICH_AE_BITE_START:
  796. {
  797. EmitSound( "NPC_Ichthyosaur.AttackGrowl" );
  798. }
  799. break;
  800. }
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Purpose:
  804. //-----------------------------------------------------------------------------
  805. void CNPC_Ichthyosaur::Bite( void )
  806. {
  807. //Don't allow another bite too soon
  808. if ( m_flNextBiteTime > gpGlobals->curtime )
  809. return;
  810. CBaseEntity *pHurt;
  811. //FIXME: E3 HACK - Always damage bullseyes if we're scripted to hit them
  812. if ( ( GetEnemy() != NULL ) && ( GetEnemy()->Classify() == CLASS_BULLSEYE ) )
  813. {
  814. pHurt = GetEnemy();
  815. }
  816. else
  817. {
  818. pHurt = CheckTraceHullAttack( 108, Vector(-32,-32,-32), Vector(32,32,32), 0, DMG_CLUB );
  819. }
  820. //Hit something
  821. if ( pHurt != NULL )
  822. {
  823. CTakeDamageInfo info( this, this, sk_ichthyosaur_melee_dmg.GetInt(), DMG_CLUB );
  824. if ( pHurt->IsPlayer() )
  825. {
  826. CBasePlayer *pPlayer = ToBasePlayer( pHurt );
  827. if ( pPlayer )
  828. {
  829. if ( ( ( m_flHoldTime < gpGlobals->curtime ) && ( pPlayer->m_iHealth < (pPlayer->m_iMaxHealth*0.5f)) ) || ( pPlayer->GetWaterLevel() < 1 ) )
  830. {
  831. //EnsnareVictim( pHurt );
  832. }
  833. else
  834. {
  835. info.SetDamage( sk_ichthyosaur_melee_dmg.GetInt() * 3 );
  836. }
  837. CalculateMeleeDamageForce( &info, GetAbsVelocity(), pHurt->GetAbsOrigin() );
  838. pHurt->TakeDamage( info );
  839. color32 red = {64, 0, 0, 255};
  840. UTIL_ScreenFade( pPlayer, red, 0.5, 0, FFADE_IN );
  841. //Disorient the player
  842. QAngle angles = pPlayer->GetLocalAngles();
  843. angles.x += random->RandomInt( 60, 25 );
  844. angles.y += random->RandomInt( 60, 25 );
  845. angles.z = 0.0f;
  846. pPlayer->SetLocalAngles( angles );
  847. pPlayer->SnapEyeAngles( angles );
  848. }
  849. }
  850. else
  851. {
  852. CalculateMeleeDamageForce( &info, GetAbsVelocity(), pHurt->GetAbsOrigin() );
  853. pHurt->TakeDamage( info );
  854. }
  855. m_flNextBiteTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 4.0f );
  856. //Bubbles!
  857. UTIL_Bubbles( pHurt->GetAbsOrigin()+Vector(-32.0f,-32.0f,-32.0f), pHurt->GetAbsOrigin()+Vector(32.0f,32.0f,0.0f), random->RandomInt( 16, 32 ) );
  858. // Play a random attack hit sound
  859. EmitSound( "NPC_Ichthyosaur.Bite" );
  860. if ( GetActivity() == ACT_MELEE_ATTACK1 )
  861. {
  862. SetActivity( (Activity) ACT_ICH_BITE_HIT );
  863. }
  864. return;
  865. }
  866. //Play the miss animation and sound
  867. if ( GetActivity() == ACT_MELEE_ATTACK1 )
  868. {
  869. SetActivity( (Activity) ACT_ICH_BITE_MISS );
  870. }
  871. //Miss sound
  872. EmitSound( "NPC_Ichthyosaur.BiteMiss" );
  873. }
  874. //-----------------------------------------------------------------------------
  875. // Purpose:
  876. // Output : Returns true on success, false on failure.
  877. //-----------------------------------------------------------------------------
  878. bool CNPC_Ichthyosaur::Beached( void )
  879. {
  880. trace_t tr;
  881. Vector testPos;
  882. testPos = GetAbsOrigin() - Vector( 0, 0, ICH_DEPTH_PREFERENCE );
  883. AI_TraceHull( GetAbsOrigin(), testPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  884. return ( tr.fraction < 1.0f );
  885. }
  886. //-----------------------------------------------------------------------------
  887. // Purpose:
  888. //-----------------------------------------------------------------------------
  889. void CNPC_Ichthyosaur::PrescheduleThink( void )
  890. {
  891. BaseClass::PrescheduleThink();
  892. //Ambient sounds
  893. /*
  894. if ( random->RandomInt( 0, 20 ) == 10 )
  895. {
  896. if ( random->RandomInt( 0, 1 ) )
  897. {
  898. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pSwimSound, random->RandomFloat( 0.0f, 0.5f ), 1.0f );
  899. }
  900. else
  901. {
  902. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pVoiceSound, random->RandomFloat( 0.0f, 0.5f ), 1.0f );
  903. }
  904. }
  905. */
  906. //Pings
  907. if ( m_flNextPingTime < gpGlobals->curtime )
  908. {
  909. m_flNextPingTime = gpGlobals->curtime + random->RandomFloat( 3.0f, 8.0f );
  910. }
  911. //Growls
  912. if ( ( m_NPCState == NPC_STATE_COMBAT || m_NPCState == NPC_STATE_ALERT ) && ( m_flNextGrowlTime < gpGlobals->curtime ) )
  913. {
  914. m_flNextGrowlTime = gpGlobals->curtime + random->RandomFloat( 2.0f, 6.0f );
  915. }
  916. //Randomly emit bubbles
  917. if ( random->RandomInt( 0, 10 ) == 0 )
  918. {
  919. UTIL_Bubbles( GetAbsOrigin()+(GetHullMins()*0.5f), GetAbsOrigin()+(GetHullMaxs()*0.5f), 1 );
  920. }
  921. //Check our water level
  922. if ( GetWaterLevel() != 3 )
  923. {
  924. if ( GetWaterLevel() < 2 )
  925. {
  926. DevMsg( 2, "Came out of water\n" );
  927. if ( Beached() )
  928. {
  929. SetSchedule( SCHED_ICH_THRASH );
  930. Vector vecNewVelocity = GetAbsVelocity();
  931. vecNewVelocity[2] = 8.0f;
  932. SetAbsVelocity( vecNewVelocity );
  933. }
  934. }
  935. else
  936. {
  937. //TODO: Wake effects
  938. }
  939. }
  940. //If we have a victim, update them
  941. if ( m_pVictim != NULL )
  942. {
  943. //See if it's time to release the victim
  944. if ( m_flHoldTime < gpGlobals->curtime )
  945. {
  946. ReleaseVictim();
  947. return;
  948. }
  949. Bite();
  950. }
  951. }
  952. //-----------------------------------------------------------------------------
  953. // Purpose:
  954. // Input : *pevInflictor -
  955. // *pAttacker -
  956. // flDamage -
  957. // bitsDamageType -
  958. //-----------------------------------------------------------------------------
  959. int CNPC_Ichthyosaur::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  960. {
  961. //Release the player if he's struck us while being held
  962. if ( m_flHoldTime > gpGlobals->curtime )
  963. {
  964. ReleaseVictim();
  965. //Don't give them as much time to flee
  966. m_flNextBiteTime = gpGlobals->curtime + 2.0f;
  967. SetSchedule( SCHED_ICH_THRASH );
  968. }
  969. return BaseClass::OnTakeDamage_Alive( info );
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Purpose:
  973. //-----------------------------------------------------------------------------
  974. void CNPC_Ichthyosaur::EnsnareVictim( CBaseEntity *pVictim )
  975. {
  976. CBaseCombatCharacter* pBCC = (CBaseCombatCharacter *) pVictim;
  977. if ( pBCC && pBCC->DispatchInteraction( g_interactionBarnacleVictimGrab, NULL, this ) )
  978. {
  979. if ( pVictim->IsPlayer() )
  980. {
  981. CBasePlayer *pPlayer = dynamic_cast< CBasePlayer * >((CBaseEntity *) pVictim);
  982. if ( pPlayer )
  983. {
  984. m_flHoldTime = MAX( gpGlobals->curtime+3.0f, pPlayer->PlayerDrownTime() - 2.0f );
  985. }
  986. }
  987. else
  988. {
  989. m_flHoldTime = gpGlobals->curtime + 4.0f;
  990. }
  991. m_pVictim = pVictim;
  992. m_pVictim->AddSolidFlags( FSOLID_NOT_SOLID );
  993. SetSchedule( SCHED_ICH_DROWN_VICTIM );
  994. }
  995. }
  996. //-----------------------------------------------------------------------------
  997. // Purpose:
  998. //-----------------------------------------------------------------------------
  999. void CNPC_Ichthyosaur::ReleaseVictim( void )
  1000. {
  1001. CBaseCombatCharacter *pBCC = (CBaseCombatCharacter *) m_pVictim;
  1002. pBCC->DispatchInteraction( g_interactionBarnacleVictimReleased, NULL, this );
  1003. m_pVictim->RemoveSolidFlags( FSOLID_NOT_SOLID );
  1004. m_pVictim = NULL;
  1005. m_flNextBiteTime = gpGlobals->curtime + 8.0f;
  1006. m_flHoldTime = gpGlobals->curtime - 0.1f;
  1007. }
  1008. //-----------------------------------------------------------------------------
  1009. // Purpose:
  1010. // Output : speed to move at
  1011. //-----------------------------------------------------------------------------
  1012. float CNPC_Ichthyosaur::GetGroundSpeed( void )
  1013. {
  1014. if ( m_flHoldTime > gpGlobals->curtime )
  1015. return ICH_SWIM_SPEED_WALK/2.0f;
  1016. if ( GetIdealActivity() == ACT_WALK )
  1017. return ICH_SWIM_SPEED_WALK;
  1018. if ( GetIdealActivity() == ACT_ICH_THRASH )
  1019. return ICH_SWIM_SPEED_WALK;
  1020. return ICH_SWIM_SPEED_RUN;
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // Purpose:
  1024. // Input : type -
  1025. // Output : int
  1026. //-----------------------------------------------------------------------------
  1027. int CNPC_Ichthyosaur::TranslateSchedule( int type )
  1028. {
  1029. if ( type == SCHED_CHASE_ENEMY ) return SCHED_ICH_CHASE_ENEMY;
  1030. //if ( type == SCHED_IDLE_STAND ) return SCHED_PATROL_WALK;
  1031. if ( type == SCHED_PATROL_RUN ) return SCHED_ICH_PATROL_RUN;
  1032. if ( type == SCHED_PATROL_WALK ) return SCHED_ICH_PATROL_WALK;
  1033. if ( type == SCHED_MELEE_ATTACK1 ) return SCHED_ICH_MELEE_ATTACK1;
  1034. return BaseClass::TranslateSchedule( type );
  1035. }
  1036. //-----------------------------------------------------------------------------
  1037. // Purpose:
  1038. // Input : *pTask -
  1039. //-----------------------------------------------------------------------------
  1040. void CNPC_Ichthyosaur::StartTask( const Task_t *pTask )
  1041. {
  1042. switch ( pTask->iTask )
  1043. {
  1044. case TASK_ICH_THRASH_PATH:
  1045. GetNavigator()->SetMovementActivity( (Activity) ACT_ICH_THRASH );
  1046. TaskComplete();
  1047. break;
  1048. case TASK_ICH_GET_PATH_TO_RANDOM_NODE:
  1049. {
  1050. if ( GetEnemy() == NULL || !GetNavigator()->SetRandomGoal( GetEnemy()->GetLocalOrigin(), pTask->flTaskData ) )
  1051. {
  1052. if (!GetNavigator()->SetRandomGoal( pTask->flTaskData ) )
  1053. {
  1054. TaskFail(FAIL_NO_REACHABLE_NODE);
  1055. return;
  1056. }
  1057. }
  1058. TaskComplete();
  1059. }
  1060. break;
  1061. case TASK_ICH_GET_PATH_TO_DROWN_NODE:
  1062. {
  1063. Vector drownPos = GetLocalOrigin() - Vector( 0, 0, pTask->flTaskData );
  1064. if ( GetNavigator()->SetGoal( drownPos, AIN_CLEAR_TARGET ) == false )
  1065. {
  1066. TaskFail( FAIL_NO_ROUTE );
  1067. return;
  1068. }
  1069. TaskComplete();
  1070. }
  1071. break;
  1072. case TASK_MELEE_ATTACK1:
  1073. m_flPlaybackRate = 1.0f;
  1074. BaseClass::StartTask(pTask);
  1075. break;
  1076. default:
  1077. BaseClass::StartTask(pTask);
  1078. break;
  1079. }
  1080. }
  1081. //-----------------------------------------------------------------------------
  1082. // Purpose:
  1083. // Input : *pTask -
  1084. //-----------------------------------------------------------------------------
  1085. void CNPC_Ichthyosaur::RunTask( const Task_t *pTask )
  1086. {
  1087. switch ( pTask->iTask )
  1088. {
  1089. case TASK_ICH_GET_PATH_TO_RANDOM_NODE:
  1090. return;
  1091. break;
  1092. case TASK_ICH_GET_PATH_TO_DROWN_NODE:
  1093. return;
  1094. break;
  1095. default:
  1096. BaseClass::RunTask(pTask);
  1097. break;
  1098. }
  1099. }
  1100. //-----------------------------------------------------------------------------
  1101. // Purpose:
  1102. // Output : desired yaw speed
  1103. //-----------------------------------------------------------------------------
  1104. float CNPC_Ichthyosaur::MaxYawSpeed( void )
  1105. {
  1106. if ( GetIdealActivity() == ACT_MELEE_ATTACK1 )
  1107. return 16.0f;
  1108. if ( GetIdealActivity() == ACT_ICH_THRASH )
  1109. return 16.0f;
  1110. //Ramp up the yaw speed as we increase our speed
  1111. return ICH_MIN_TURN_SPEED + ( (ICH_MAX_TURN_SPEED-ICH_MIN_TURN_SPEED) * ( fabs(GetAbsVelocity().Length()) / ICH_SWIM_SPEED_RUN ) );
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. // Purpose:
  1115. // Input : *pEnemy -
  1116. // &chasePosition -
  1117. // &tolerance -
  1118. //-----------------------------------------------------------------------------
  1119. void CNPC_Ichthyosaur::TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition )
  1120. {
  1121. Vector offset = pEnemy->EyePosition() - pEnemy->GetAbsOrigin();
  1122. chasePosition += offset;
  1123. }
  1124. float CNPC_Ichthyosaur::GetDefaultNavGoalTolerance()
  1125. {
  1126. return GetHullWidth()*2.0f;
  1127. }
  1128. //-----------------------------------------------------------------------------
  1129. //
  1130. // Schedules
  1131. //
  1132. //-----------------------------------------------------------------------------
  1133. //==================================================
  1134. // SCHED_ICH_CHASE_ENEMY
  1135. //==================================================
  1136. AI_DEFINE_SCHEDULE
  1137. (
  1138. SCHED_ICH_CHASE_ENEMY,
  1139. " Tasks"
  1140. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_ICH_PATROL_WALK"
  1141. " TASK_SET_TOLERANCE_DISTANCE 64"
  1142. " TASK_SET_GOAL GOAL:ENEMY"
  1143. " TASK_GET_PATH_TO_GOAL PATH:TRAVEL"
  1144. " TASK_RUN_PATH 0"
  1145. " TASK_WAIT_FOR_MOVEMENT 0"
  1146. ""
  1147. " Interrupts"
  1148. " COND_NEW_ENEMY"
  1149. " COND_ENEMY_DEAD"
  1150. " COND_ENEMY_UNREACHABLE"
  1151. " COND_CAN_MELEE_ATTACK1"
  1152. " COND_TOO_CLOSE_TO_ATTACK"
  1153. " COND_LOST_ENEMY"
  1154. " COND_TASK_FAILED"
  1155. );
  1156. //==================================================
  1157. // SCHED_ICH_PATROL_RUN
  1158. //==================================================
  1159. AI_DEFINE_SCHEDULE
  1160. (
  1161. SCHED_ICH_PATROL_RUN,
  1162. " Tasks"
  1163. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
  1164. " TASK_SET_TOLERANCE_DISTANCE 64"
  1165. " TASK_SET_ROUTE_SEARCH_TIME 4"
  1166. " TASK_ICH_GET_PATH_TO_RANDOM_NODE 200"
  1167. " TASK_RUN_PATH 0"
  1168. " TASK_WAIT_FOR_MOVEMENT 0"
  1169. ""
  1170. " Interrupts"
  1171. " COND_CAN_MELEE_ATTACK1"
  1172. " COND_GIVE_WAY"
  1173. " COND_NEW_ENEMY"
  1174. " COND_LIGHT_DAMAGE"
  1175. " COND_HEAVY_DAMAGE"
  1176. );
  1177. //==================================================
  1178. // SCHED_ICH_PATROL_WALK
  1179. //==================================================
  1180. AI_DEFINE_SCHEDULE
  1181. (
  1182. SCHED_ICH_PATROL_WALK,
  1183. " Tasks"
  1184. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
  1185. " TASK_SET_TOLERANCE_DISTANCE 64"
  1186. " TASK_SET_ROUTE_SEARCH_TIME 4"
  1187. " TASK_ICH_GET_PATH_TO_RANDOM_NODE 200"
  1188. " TASK_WALK_PATH 0"
  1189. " TASK_WAIT_FOR_MOVEMENT 0"
  1190. ""
  1191. " Interrupts"
  1192. " COND_CAN_MELEE_ATTACK1"
  1193. " COND_GIVE_WAY"
  1194. " COND_NEW_ENEMY"
  1195. " COND_LIGHT_DAMAGE"
  1196. " COND_HEAVY_DAMAGE"
  1197. );
  1198. //==================================================
  1199. // SCHED_ICH_DROWN_VICTIM
  1200. //==================================================
  1201. AI_DEFINE_SCHEDULE
  1202. (
  1203. SCHED_ICH_DROWN_VICTIM,
  1204. " Tasks"
  1205. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
  1206. " TASK_SET_TOLERANCE_DISTANCE 64"
  1207. " TASK_SET_ROUTE_SEARCH_TIME 4"
  1208. " TASK_ICH_GET_PATH_TO_DROWN_NODE 256"
  1209. " TASK_WALK_PATH 0"
  1210. " TASK_WAIT_FOR_MOVEMENT 0"
  1211. ""
  1212. " Interrupts"
  1213. " COND_NEW_ENEMY"
  1214. " COND_LIGHT_DAMAGE"
  1215. " COND_HEAVY_DAMAGE"
  1216. );
  1217. //=========================================================
  1218. // SCHED_ICH_MELEE_ATTACK1
  1219. //=========================================================
  1220. AI_DEFINE_SCHEDULE
  1221. (
  1222. SCHED_ICH_MELEE_ATTACK1,
  1223. " Tasks"
  1224. " TASK_ANNOUNCE_ATTACK 1"
  1225. " TASK_MELEE_ATTACK1 0"
  1226. ""
  1227. " Interrupts"
  1228. " COND_NEW_ENEMY"
  1229. " COND_ENEMY_DEAD"
  1230. " COND_ENEMY_OCCLUDED"
  1231. );
  1232. //==================================================
  1233. // SCHED_ICH_THRASH
  1234. //==================================================
  1235. AI_DEFINE_SCHEDULE
  1236. (
  1237. SCHED_ICH_THRASH,
  1238. " Tasks"
  1239. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COMBAT_FACE"
  1240. " TASK_SET_TOLERANCE_DISTANCE 64"
  1241. " TASK_SET_ROUTE_SEARCH_TIME 4"
  1242. " TASK_ICH_GET_PATH_TO_RANDOM_NODE 64"
  1243. " TASK_ICH_THRASH_PATH 0"
  1244. " TASK_WAIT_FOR_MOVEMENT 0"
  1245. ""
  1246. " Interrupts"
  1247. );