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.

1024 lines
25 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 "beam_shared.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 "activitylist.h"
  19. #include "game.h"
  20. #include "npcevent.h"
  21. #include "player.h"
  22. #include "entitylist.h"
  23. #include "soundenvelope.h"
  24. #include "shake.h"
  25. #include "ndebugoverlay.h"
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "hl1_npc_ichthyosaur.h"
  29. ConVar sk_ichthyosaur_health ( "sk_ichthyosaur_health", "0" );
  30. ConVar sk_ichthyosaur_shake ( "sk_ichthyosaur_shake", "0" );
  31. #define ICH_SWIM_SPEED_WALK 150
  32. #define ICH_SWIM_SPEED_RUN 400
  33. #define PROBE_LENGTH 150
  34. enum IchthyosaurMoveType_t
  35. {
  36. ICH_MOVETYPE_SEEK = 0, // Fly through the target without stopping.
  37. ICH_MOVETYPE_ARRIVE // Slow down and stop at target.
  38. };
  39. enum
  40. {
  41. SCHED_SWIM_AROUND = LAST_SHARED_SCHEDULE + 1,
  42. SCHED_SWIM_AGITATED,
  43. SCHED_CIRCLE_ENEMY,
  44. SCHED_TWITCH_DIE,
  45. };
  46. //=========================================================
  47. // monster-specific tasks and states
  48. //=========================================================
  49. enum
  50. {
  51. TASK_ICHTHYOSAUR_CIRCLE_ENEMY = LAST_SHARED_TASK + 1,
  52. TASK_ICHTHYOSAUR_SWIM,
  53. TASK_ICHTHYOSAUR_FLOAT,
  54. };
  55. LINK_ENTITY_TO_CLASS( monster_ichthyosaur, CNPC_Ichthyosaur );
  56. BEGIN_DATADESC( CNPC_Ichthyosaur )
  57. // Function Pointers
  58. DEFINE_ENTITYFUNC( BiteTouch ),
  59. DEFINE_FIELD( m_SaveVelocity, FIELD_VECTOR ),
  60. DEFINE_FIELD( m_idealDist, FIELD_FLOAT ),
  61. DEFINE_FIELD( m_flBlink, FIELD_TIME ),
  62. DEFINE_FIELD( m_flEnemyTouched, FIELD_TIME ),
  63. DEFINE_FIELD( m_bOnAttack, FIELD_BOOLEAN ),
  64. DEFINE_FIELD( m_flMaxSpeed, FIELD_FLOAT ),
  65. DEFINE_FIELD( m_flMinSpeed, FIELD_FLOAT ),
  66. DEFINE_FIELD( m_flMaxDist, FIELD_FLOAT ),
  67. DEFINE_FIELD( m_flNextAlert, FIELD_TIME ),
  68. DEFINE_FIELD( m_flMaxSpeed, FIELD_FLOAT ),
  69. DEFINE_FIELD( m_vecLastMoveTarget, FIELD_VECTOR ),
  70. DEFINE_FIELD( m_bHasMoveTarget, FIELD_BOOLEAN ),
  71. DEFINE_FIELD( m_flFlyingSpeed, FIELD_FLOAT ),
  72. DEFINE_FIELD( m_flLastAttackSound, FIELD_TIME ),
  73. DEFINE_INPUTFUNC( FIELD_VOID, "StartCombat", InputStartCombat ),
  74. DEFINE_INPUTFUNC( FIELD_VOID, "EndCombat", InputEndCombat ),
  75. END_DATADESC()
  76. //=========================================================
  77. // AI Schedules Specific to this monster
  78. //=========================================================
  79. AI_BEGIN_CUSTOM_NPC( monster_ichthyosaur, CNPC_Ichthyosaur )
  80. DECLARE_TASK ( TASK_ICHTHYOSAUR_SWIM )
  81. DECLARE_TASK ( TASK_ICHTHYOSAUR_CIRCLE_ENEMY )
  82. DECLARE_TASK ( TASK_ICHTHYOSAUR_FLOAT )
  83. //=========================================================
  84. // > SCHED_SWIM_AROUND
  85. //=========================================================
  86. DEFINE_SCHEDULE
  87. (
  88. SCHED_SWIM_AROUND,
  89. " Tasks"
  90. " TASK_SET_ACTIVITY ACTIVITY:ACT_GLIDE"
  91. " TASK_ICHTHYOSAUR_SWIM 0.0"
  92. " "
  93. " Interrupts"
  94. " COND_LIGHT_DAMAGE"
  95. " COND_HEAVY_DAMAGE"
  96. " COND_SEE_ENEMY"
  97. " COND_NEW_ENEMY"
  98. " COND_HEAR_PLAYER"
  99. " COND_HEAR_COMBAT"
  100. )
  101. //=========================================================
  102. // > SCHED_SWIM_AGITATED
  103. //=========================================================
  104. DEFINE_SCHEDULE
  105. (
  106. SCHED_SWIM_AGITATED,
  107. " Tasks"
  108. " TASK_STOP_MOVING 0"
  109. " TASK_SET_ACTIVITY ACTIVITY:ACT_SWIM"
  110. " TASK_WAIT 2.0"
  111. " "
  112. )
  113. //=========================================================
  114. // > SCHED_CIRCLE_ENEMY
  115. //=========================================================
  116. DEFINE_SCHEDULE
  117. (
  118. SCHED_CIRCLE_ENEMY,
  119. " Tasks"
  120. " TASK_SET_ACTIVITY ACTIVITY:ACT_GLIDE"
  121. " TASK_ICHTHYOSAUR_CIRCLE_ENEMY 0.0"
  122. " "
  123. " Interrupts"
  124. " COND_LIGHT_DAMAGE"
  125. " COND_HEAVY_DAMAGE"
  126. " COND_NEW_ENEMY"
  127. " COND_CAN_MELEE_ATTACK1"
  128. " COND_CAN_RANGE_ATTACK1"
  129. )
  130. //=========================================================
  131. // > SCHED_TWITCH_DIE
  132. //=========================================================
  133. DEFINE_SCHEDULE
  134. (
  135. SCHED_TWITCH_DIE,
  136. " Tasks"
  137. " TASK_STOP_MOVING 0"
  138. " TASK_SOUND_DIE 0"
  139. " TASK_DIE 0"
  140. " TASK_ICHTHYOSAUR_FLOAT 0"
  141. " "
  142. )
  143. AI_END_CUSTOM_NPC()
  144. //=========================================================
  145. // Precache - precaches all resources this monster needs
  146. //=========================================================
  147. void CNPC_Ichthyosaur::Precache()
  148. {
  149. PrecacheModel("models/icky.mdl");
  150. PrecacheModel("sprites/lgtning.vmt");
  151. PrecacheScriptSound( "Ichthyosaur.Bite" );
  152. PrecacheScriptSound( "Ichthyosaur.Alert" );
  153. PrecacheScriptSound( "Ichthyosaur.Pain" );
  154. PrecacheScriptSound( "Ichthyosaur.Die" );
  155. PrecacheScriptSound( "Ichthyosaur.Idle" );
  156. PrecacheScriptSound( "Ichthyosaur.Attack" );
  157. BaseClass::Precache();
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose:
  161. //-----------------------------------------------------------------------------
  162. void CNPC_Ichthyosaur::Spawn( void )
  163. {
  164. Precache( );
  165. SetModel( "models/icky.mdl");
  166. UTIL_SetSize( this, Vector( -32, -32, -32 ), Vector( 32, 32, 32 ) );
  167. SetHullType(HULL_LARGE_CENTERED);
  168. SetHullSizeNormal();
  169. SetDefaultEyeOffset();
  170. // Use our hitboxes to determine our render bounds
  171. CollisionProp()->SetSurroundingBoundsType( USE_HITBOXES );
  172. SetNavType( NAV_FLY );
  173. m_NPCState = NPC_STATE_NONE;
  174. SetSolid( SOLID_BBOX );
  175. AddSolidFlags( FSOLID_NOT_STANDABLE );
  176. SetMoveType( MOVETYPE_STEP );
  177. AddFlag( FL_FLY | FL_STEPMOVEMENT );
  178. m_flGroundSpeed = ICH_SWIM_SPEED_RUN;
  179. m_bloodColor = BLOOD_COLOR_YELLOW;
  180. m_iHealth = sk_ichthyosaur_health.GetFloat();
  181. m_iMaxHealth = m_iHealth;
  182. m_flFieldOfView = -0.707; // 270 degrees
  183. AddFlag( FL_SWIM );
  184. m_flFlyingSpeed = ICHTHYOSAUR_SPEED;
  185. SetDistLook( 1024 );
  186. SetTouch( &CNPC_Ichthyosaur::BiteTouch );
  187. m_idealDist = 384;
  188. m_flMinSpeed = 80;
  189. m_flMaxSpeed = 400;
  190. m_flMaxDist = 384;
  191. m_flLastAttackSound = gpGlobals->curtime;
  192. Vector vforward;
  193. AngleVectors(GetAbsAngles(), &vforward );
  194. VectorNormalize ( vforward );
  195. SetAbsVelocity( m_flFlyingSpeed * vforward );
  196. m_SaveVelocity = GetAbsVelocity();
  197. CapabilitiesClear();
  198. CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK1 );
  199. m_bOnAttack = false;
  200. NPCInit();
  201. }
  202. //=========================================================
  203. //=========================================================
  204. int CNPC_Ichthyosaur::TranslateSchedule( int scheduleType )
  205. {
  206. switch ( scheduleType )
  207. {
  208. case SCHED_IDLE_WALK:
  209. return SCHED_SWIM_AROUND;
  210. case SCHED_STANDOFF:
  211. return SCHED_CIRCLE_ENEMY;
  212. case SCHED_FAIL:
  213. return SCHED_SWIM_AGITATED;
  214. case SCHED_DIE:
  215. return SCHED_TWITCH_DIE;
  216. case SCHED_CHASE_ENEMY:
  217. if ( m_flLastAttackSound < gpGlobals->curtime )
  218. {
  219. AttackSound();
  220. m_flLastAttackSound = gpGlobals->curtime + random->RandomFloat( 2.0f, 4.0f );
  221. }
  222. break;
  223. }
  224. return BaseClass::TranslateSchedule( scheduleType );
  225. }
  226. int CNPC_Ichthyosaur::SelectSchedule()
  227. {
  228. switch(m_NPCState)
  229. {
  230. case NPC_STATE_IDLE:
  231. m_flFlyingSpeed = 80;
  232. m_flMaxSpeed = 80;
  233. return TranslateSchedule( SCHED_IDLE_WALK );
  234. case NPC_STATE_ALERT:
  235. m_flFlyingSpeed = 150;
  236. m_flMaxSpeed = 150;
  237. return TranslateSchedule( SCHED_IDLE_WALK );
  238. case NPC_STATE_COMBAT:
  239. m_flMaxSpeed = 400;
  240. // eat them
  241. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  242. {
  243. return TranslateSchedule( SCHED_MELEE_ATTACK1 );
  244. }
  245. // chase them down and eat them
  246. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  247. {
  248. return TranslateSchedule( SCHED_CHASE_ENEMY );
  249. }
  250. if ( HasCondition( COND_HEAVY_DAMAGE ) )
  251. {
  252. m_bOnAttack = true;
  253. }
  254. if ( GetHealth() < GetMaxHealth() - 20 )
  255. {
  256. m_bOnAttack = true;
  257. }
  258. return TranslateSchedule( SCHED_STANDOFF );
  259. }
  260. return BaseClass::SelectSchedule();
  261. }
  262. bool CNPC_Ichthyosaur::OverrideMove( float flInterval )
  263. {
  264. if ( m_lifeState == LIFE_ALIVE )
  265. {
  266. if ( m_NPCState != NPC_STATE_SCRIPT)
  267. {
  268. MoveExecute_Alive( flInterval );
  269. }
  270. }
  271. return true;
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Purpose:
  275. //-----------------------------------------------------------------------------
  276. void CNPC_Ichthyosaur::MoveExecute_Alive(float flInterval)
  277. {
  278. Vector vStart = GetAbsOrigin();
  279. Vector vForward, vRight, vUp;
  280. if (GetNavigator()->IsGoalActive())
  281. {
  282. Vector vecDir = ( GetNavigator()->GetPath()->CurWaypointPos() - GetAbsOrigin());
  283. VectorNormalize( vecDir );
  284. m_SaveVelocity = vecDir * m_flFlyingSpeed;
  285. }
  286. // If we're attacking, accelerate to max speed
  287. if (m_bOnAttack && m_flFlyingSpeed < m_flMaxSpeed)
  288. {
  289. m_flFlyingSpeed = MIN( m_flMaxSpeed, m_flFlyingSpeed+40 );
  290. }
  291. if (m_flFlyingSpeed < 180)
  292. {
  293. if (GetIdealActivity() == ACT_SWIM)
  294. SetActivity( ACT_GLIDE );
  295. if (GetIdealActivity() == ACT_GLIDE)
  296. m_flPlaybackRate = m_flFlyingSpeed / 150.0;
  297. }
  298. else
  299. {
  300. if (GetIdealActivity() == ACT_GLIDE)
  301. SetActivity( ACT_SWIM );
  302. if (GetIdealActivity() == ACT_SWIM)
  303. m_flPlaybackRate = m_flFlyingSpeed / 300.0;
  304. }
  305. // Steering
  306. QAngle angSaveAngles;
  307. VectorAngles( m_SaveVelocity, angSaveAngles );
  308. AngleVectors(angSaveAngles, &vForward, &vRight, &vUp);
  309. Vector z;
  310. float frac;
  311. Vector f, u, l, r, d;
  312. f = DoProbe(vStart + (PROBE_LENGTH * vForward) );
  313. r = DoProbe(vStart + ((PROBE_LENGTH/3) * (vForward + vRight)) );
  314. l = DoProbe(vStart + ((PROBE_LENGTH/3) * (vForward - vRight)) );
  315. u = DoProbe(vStart + ((PROBE_LENGTH/3) * (vForward + vUp)) );
  316. d = DoProbe(vStart + ((PROBE_LENGTH/3) * (vForward - vUp)) );
  317. Vector SteeringVector = f+r+l+u+d;
  318. if( ProbeZ( vStart + vForward*50, vUp*50, &frac ) )
  319. {
  320. // reflect off the water surface
  321. m_SaveVelocity.z = -10;
  322. }
  323. m_SaveVelocity += SteeringVector/32;
  324. VectorNormalize( m_SaveVelocity );
  325. angSaveAngles = GetAbsAngles();
  326. AngleVectors( angSaveAngles, &vForward, &vRight, &vUp );
  327. float flDot = DotProduct( vForward, m_SaveVelocity );
  328. if (flDot > 0.5)
  329. m_SaveVelocity = m_SaveVelocity * m_flFlyingSpeed;
  330. else if (flDot > 0)
  331. m_SaveVelocity = m_SaveVelocity * m_flFlyingSpeed * (flDot + 0.5);
  332. else
  333. m_SaveVelocity = m_SaveVelocity * 80;
  334. SetAbsVelocity( m_SaveVelocity );
  335. VectorAngles( m_SaveVelocity, angSaveAngles );
  336. //
  337. // Smooth Pitch
  338. //
  339. if (angSaveAngles.x > 180)
  340. angSaveAngles.x = angSaveAngles.x - 360;
  341. QAngle angAbsAngles = GetAbsAngles();
  342. angAbsAngles.x = clamp( UTIL_Approach(angSaveAngles.x, angAbsAngles.x, 10 ), -60, 60 );
  343. //
  344. // Smooth Yaw and generate Roll
  345. //
  346. float turn = 360;
  347. if (fabs(angSaveAngles.y - angAbsAngles.y) < fabs(turn))
  348. {
  349. turn = angSaveAngles.y - angAbsAngles.y;
  350. }
  351. if (fabs(angSaveAngles.y - angAbsAngles.y + 360) < fabs(turn))
  352. {
  353. turn = angSaveAngles.y - angAbsAngles.y + 360;
  354. }
  355. if (fabs(angSaveAngles.y - angAbsAngles.y - 360) < fabs(turn))
  356. {
  357. turn = angSaveAngles.y - angAbsAngles.y - 360;
  358. }
  359. float speed = m_flFlyingSpeed * 0.4;
  360. if (fabs(turn) > speed)
  361. {
  362. if (turn < 0.0)
  363. {
  364. turn = -speed;
  365. }
  366. else
  367. {
  368. turn = speed;
  369. }
  370. }
  371. angAbsAngles.y += turn;
  372. angAbsAngles.z -= turn;
  373. angAbsAngles.y = fmod((angAbsAngles.y + 360.0), 360.0);
  374. // don't touch bone controller, makes swim animation look funky with all these hard turns.
  375. // static float yaw_adj;
  376. // yaw_adj = yaw_adj * 0.8 + turn;
  377. // SetBoneController( 0, -yaw_adj / 4.0 );
  378. //
  379. // Roll Smoothing
  380. //
  381. turn = 360;
  382. float flTempRoll = angAbsAngles.z;
  383. if (fabs(angSaveAngles.z - angAbsAngles.z) < fabs(turn))
  384. {
  385. turn = angSaveAngles.z - angAbsAngles.z;
  386. }
  387. if (fabs(angSaveAngles.z - angAbsAngles.z + 360) < fabs(turn))
  388. {
  389. turn = angSaveAngles.z - angAbsAngles.z + 360;
  390. }
  391. if (fabs(angSaveAngles.z - angAbsAngles.z - 360) < fabs(turn))
  392. {
  393. turn = angSaveAngles.z - angAbsAngles.z - 360;
  394. }
  395. speed = m_flFlyingSpeed/2 * 0.1;
  396. if (fabs(turn) < speed)
  397. {
  398. flTempRoll += turn;
  399. }
  400. else
  401. {
  402. if (turn < 0.0)
  403. {
  404. flTempRoll -= speed;
  405. }
  406. else
  407. {
  408. flTempRoll += speed;
  409. }
  410. }
  411. angAbsAngles.z = clamp( UTIL_Approach(flTempRoll, angAbsAngles.z, 5 ), -20, 20 );
  412. SetAbsAngles( angAbsAngles );
  413. //Move along the current velocity vector
  414. if ( WalkMove( m_SaveVelocity * flInterval, MASK_NPCSOLID ) == false )
  415. {
  416. //Attempt a half-step
  417. if ( WalkMove( (m_SaveVelocity*0.5f) * flInterval, MASK_NPCSOLID) == false )
  418. {
  419. //Restart the velocity
  420. //VectorNormalize( m_vecVelocity );
  421. m_SaveVelocity *= 0.25f;
  422. }
  423. else
  424. {
  425. //Cut our velocity in half
  426. m_SaveVelocity *= 0.5f;
  427. }
  428. }
  429. SetAbsVelocity( m_SaveVelocity );
  430. }
  431. void CNPC_Ichthyosaur::InputStartCombat( inputdata_t &input )
  432. {
  433. m_bOnAttack = true;
  434. }
  435. void CNPC_Ichthyosaur::InputEndCombat( inputdata_t &input )
  436. {
  437. m_bOnAttack = false;
  438. }
  439. //=========================================================
  440. // Start task - selects the correct activity and performs
  441. // any necessary calculations to start the next task on the
  442. // schedule.
  443. //=========================================================
  444. void CNPC_Ichthyosaur::StartTask(const Task_t *pTask)
  445. {
  446. switch (pTask->iTask)
  447. {
  448. case TASK_ICHTHYOSAUR_CIRCLE_ENEMY:
  449. break;
  450. case TASK_ICHTHYOSAUR_SWIM:
  451. break;
  452. case TASK_SMALL_FLINCH:
  453. if (m_idealDist > 128)
  454. {
  455. m_flMaxDist = 512;
  456. m_idealDist = 512;
  457. }
  458. else
  459. {
  460. m_bOnAttack = true;
  461. }
  462. BaseClass::StartTask(pTask);
  463. break;
  464. case TASK_ICHTHYOSAUR_FLOAT:
  465. m_nSkin = EYE_BASE;
  466. SetSequenceByName( "bellyup" );
  467. break;
  468. default:
  469. BaseClass::StartTask(pTask);
  470. break;
  471. }
  472. }
  473. void CNPC_Ichthyosaur::RunTask(const Task_t *pTask )
  474. {
  475. QAngle angles = GetAbsAngles();
  476. switch ( pTask->iTask )
  477. {
  478. case TASK_ICHTHYOSAUR_CIRCLE_ENEMY:
  479. if (GetEnemy() == NULL )
  480. {
  481. TaskComplete( );
  482. }
  483. else if (FVisible( GetEnemy() ))
  484. {
  485. Vector vecFrom = GetEnemy()->EyePosition( );
  486. Vector vecDelta = GetAbsOrigin() - vecFrom;
  487. VectorNormalize( vecDelta );
  488. Vector vecSwim = CrossProduct( vecDelta, Vector( 0, 0, 1 ) );
  489. VectorNormalize( vecSwim );
  490. if (DotProduct( vecSwim, m_SaveVelocity ) < 0)
  491. {
  492. vecSwim = vecSwim * -1.0;
  493. }
  494. Vector vecPos = vecFrom + vecDelta * m_idealDist + vecSwim * 32;
  495. trace_t tr;
  496. // UTIL_TraceHull( vecFrom, vecPos, ignore_monsters, large_hull, m_hEnemy->edict(), &tr );
  497. UTIL_TraceEntity( this, vecFrom, vecPos, MASK_NPCSOLID, &tr );
  498. if (tr.fraction > 0.5)
  499. {
  500. vecPos = tr.endpos;
  501. }
  502. Vector vecNorm = vecPos - GetAbsOrigin();
  503. VectorNormalize( vecNorm );
  504. m_SaveVelocity = m_SaveVelocity * 0.8 + 0.2 * vecNorm * m_flFlyingSpeed;
  505. if (HasCondition( COND_ENEMY_FACING_ME ) && GetEnemy()->FVisible( this ))
  506. {
  507. m_flNextAlert -= 0.1;
  508. if (m_idealDist < m_flMaxDist)
  509. {
  510. m_idealDist += 4;
  511. }
  512. if (m_flFlyingSpeed > m_flMinSpeed)
  513. {
  514. m_flFlyingSpeed -= 2;
  515. }
  516. else if (m_flFlyingSpeed < m_flMinSpeed)
  517. {
  518. m_flFlyingSpeed += 2;
  519. }
  520. if (m_flMinSpeed < m_flMaxSpeed)
  521. {
  522. m_flMinSpeed += 0.5;
  523. }
  524. }
  525. else
  526. {
  527. m_flNextAlert += 0.1;
  528. if (m_idealDist > 128)
  529. {
  530. m_idealDist -= 4;
  531. }
  532. if (m_flFlyingSpeed < m_flMaxSpeed)
  533. {
  534. m_flFlyingSpeed += 4;
  535. }
  536. }
  537. }
  538. else
  539. {
  540. m_flNextAlert = gpGlobals->curtime + 0.2;
  541. }
  542. if (m_flNextAlert < gpGlobals->curtime)
  543. {
  544. // ALERT( at_console, "AlertSound()\n");
  545. AlertSound( );
  546. m_flNextAlert = gpGlobals->curtime + RandomFloat( 3, 5 );
  547. }
  548. break;
  549. case TASK_ICHTHYOSAUR_SWIM:
  550. if ( IsSequenceFinished() )
  551. {
  552. TaskComplete( );
  553. }
  554. break;
  555. case TASK_DIE:
  556. if ( IsSequenceFinished() )
  557. {
  558. // pev->deadflag = DEAD_DEAD;
  559. TaskComplete( );
  560. }
  561. break;
  562. case TASK_ICHTHYOSAUR_FLOAT:
  563. angles.x = UTIL_ApproachAngle( 0, angles.x, 20 );
  564. SetAbsAngles( angles );
  565. // SetAbsVelocity( GetAbsVelocity() * 0.8 );
  566. // if (pev->waterlevel > 1 && GetAbsVelocity().z < 64)
  567. // {
  568. // pev->velocity.z += 8;
  569. // }
  570. // else
  571. // {
  572. // pev->velocity.z -= 8;
  573. // }
  574. // ALERT( at_console, "%f\n", m_vecAbsVelocity.z );
  575. break;
  576. default:
  577. BaseClass::RunTask( pTask );
  578. break;
  579. }
  580. }
  581. //-----------------------------------------------------------------------------
  582. // Purpose: Get our conditions for a melee attack
  583. // Input : flDot -
  584. // flDist -
  585. // Output : int
  586. //-----------------------------------------------------------------------------
  587. int CNPC_Ichthyosaur::MeleeAttack1Conditions( float flDot, float flDist )
  588. {
  589. // Enemy must be submerged with us
  590. if ( GetEnemy() && GetEnemy()->GetWaterLevel() != GetWaterLevel() )
  591. return COND_NONE;
  592. Vector predictedDir = ( (GetEnemy()->GetAbsOrigin()+(GetEnemy()->GetSmoothedVelocity())) - GetAbsOrigin() );
  593. float flPredictedDist = VectorNormalize( predictedDir );
  594. Vector vBodyDir;
  595. GetVectors( &vBodyDir, NULL, NULL );
  596. float flPredictedDot = DotProduct( predictedDir, vBodyDir );
  597. if ( flPredictedDot < 0.8f )
  598. return COND_NOT_FACING_ATTACK;
  599. if ( ( flPredictedDist > ( GetAbsVelocity().Length() * 0.5f) ) && ( flDist > 128.0f ) )
  600. return COND_TOO_FAR_TO_ATTACK;
  601. return COND_CAN_MELEE_ATTACK1;
  602. }
  603. //=========================================================
  604. // RangeAttack1Conditions
  605. //=========================================================
  606. int CNPC_Ichthyosaur::RangeAttack1Conditions( float flDot, float flDist )
  607. {
  608. CBaseEntity *pEnemy = GetEnemy();
  609. if( pEnemy && pEnemy->GetWaterLevel() != GetWaterLevel() )
  610. {
  611. return COND_NONE;
  612. }
  613. if ( flDot > -0.7 && (m_bOnAttack || ( flDist <= 192 && m_idealDist <= 192)))
  614. {
  615. return COND_CAN_RANGE_ATTACK1;
  616. }
  617. return COND_NONE;
  618. }
  619. void CNPC_Ichthyosaur::BiteTouch( CBaseEntity *pOther )
  620. {
  621. // bite if we hit who we want to eat
  622. if ( pOther == GetEnemy() )
  623. {
  624. m_flEnemyTouched = gpGlobals->curtime + 0.2f;
  625. m_bOnAttack = true;
  626. }
  627. }
  628. #define ICHTHYOSAUR_AE_SHAKE_RIGHT 1
  629. #define ICHTHYOSAUR_AE_SHAKE_LEFT 2
  630. //=========================================================
  631. // HandleAnimEvent - catches the monster-specific messages
  632. // that occur when tagged animation frames are played.
  633. //=========================================================
  634. void CNPC_Ichthyosaur::HandleAnimEvent( animevent_t *pEvent )
  635. {
  636. int bDidAttack = FALSE;
  637. Vector vForward, vRight;
  638. QAngle angles = GetAbsAngles();
  639. AngleVectors( angles, &vForward, &vRight, NULL );
  640. switch( pEvent->event )
  641. {
  642. case ICHTHYOSAUR_AE_SHAKE_RIGHT:
  643. case ICHTHYOSAUR_AE_SHAKE_LEFT:
  644. {
  645. CBaseEntity* hEnemy = GetEnemy();
  646. if (hEnemy != NULL && FVisible( hEnemy ))
  647. {
  648. CBaseEntity *pHurt = GetEnemy();
  649. if ( m_flEnemyTouched > gpGlobals->curtime && (pHurt->BodyTarget( GetAbsOrigin() ) - GetAbsOrigin()).Length() > (32+16+32) )
  650. break;
  651. Vector vecShootOrigin = Weapon_ShootPosition();
  652. Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
  653. if (DotProduct( vecShootDir, vForward ) > 0.707)
  654. {
  655. angles = pHurt->GetAbsAngles();
  656. m_bOnAttack = true;
  657. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - vRight * 300 );
  658. if (pHurt->IsPlayer())
  659. {
  660. angles.x += RandomFloat( -35, 35 );
  661. angles.y += RandomFloat( -90, 90 );
  662. angles.z = 0;
  663. ((CBasePlayer*) pHurt)->SetPunchAngle( angles );
  664. }
  665. CTakeDamageInfo info( this, this, sk_ichthyosaur_shake.GetInt(), DMG_SLASH );
  666. CalculateMeleeDamageForce( &info, vForward, pHurt->GetAbsOrigin() );
  667. pHurt->TakeDamage( info );
  668. }
  669. }
  670. // Do our bite sound
  671. BiteSound();
  672. bDidAttack = TRUE;
  673. }
  674. break;
  675. default:
  676. BaseClass::HandleAnimEvent( pEvent );
  677. break;
  678. }
  679. // make bubbles when he attacks
  680. if (bDidAttack)
  681. {
  682. Vector vecSrc = GetAbsOrigin() + vForward * 32;
  683. UTIL_Bubbles( vecSrc - Vector( 8, 8, 8 ), vecSrc + Vector( 8, 8, 8 ), 16 );
  684. }
  685. }
  686. //=========================================================
  687. // Classify - indicates this monster's place in the
  688. // relationship table.
  689. //=========================================================
  690. Class_T CNPC_Ichthyosaur::Classify ( void )
  691. {
  692. return CLASS_ALIEN_MONSTER;
  693. }
  694. void CNPC_Ichthyosaur::NPCThink ( void )
  695. {
  696. // blink the eye
  697. if (m_flBlink < gpGlobals->curtime)
  698. {
  699. m_nSkin = EYE_CLOSED;
  700. if (m_flBlink + 0.2 < gpGlobals->curtime)
  701. {
  702. m_flBlink = gpGlobals->curtime + random->RandomFloat( 3, 4 );
  703. if (m_bOnAttack)
  704. m_nSkin = EYE_MAD;
  705. else
  706. m_nSkin = EYE_BASE;
  707. }
  708. }
  709. BaseClass::NPCThink( );
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose:
  713. // Output : speed to move at
  714. //-----------------------------------------------------------------------------
  715. float CNPC_Ichthyosaur::GetGroundSpeed( void )
  716. {
  717. if ( GetIdealActivity() == ACT_GLIDE )
  718. return ICH_SWIM_SPEED_WALK;
  719. return ICH_SWIM_SPEED_RUN;
  720. }
  721. Vector CNPC_Ichthyosaur::DoProbe( const Vector &Probe )
  722. {
  723. Vector WallNormal = Vector(0,0,-1); // WATER normal is Straight Down for fish.
  724. float frac = 1.0;
  725. bool bBumpedSomething = false; // = ProbeZ(GetAbsOrigin(), Probe, &frac);
  726. trace_t tr;
  727. UTIL_TraceEntity( this, GetAbsOrigin(), Probe, MASK_NPCSOLID, &tr );
  728. if ( tr.allsolid || tr.fraction < 0.99 )
  729. {
  730. if (tr.fraction < 0.0) tr.fraction = 0.0;
  731. if (tr.fraction > 1.0) tr.fraction = 1.0;
  732. if (tr.fraction < frac)
  733. {
  734. frac = tr.fraction;
  735. bBumpedSomething = true;
  736. WallNormal = tr.plane.normal;
  737. }
  738. }
  739. //NOTENOTE: Debug start
  740. //NDebugOverlay::Line( tr.startpos, tr.endpos, 255.0f*(1.0f-tr.fraction), 255.0f * tr.fraction, 0.0f, true, 0.05f );
  741. //NOTENOTE: Debug end
  742. if (bBumpedSomething && (GetEnemy() == NULL || !tr.m_pEnt || tr.m_pEnt->entindex() != GetEnemy()->entindex()))
  743. {
  744. Vector ProbeDir = Probe - GetAbsOrigin();
  745. Vector NormalToProbeAndWallNormal = CrossProduct(ProbeDir, WallNormal);
  746. Vector SteeringVector = CrossProduct( NormalToProbeAndWallNormal, ProbeDir);
  747. VectorNormalize( WallNormal );
  748. VectorNormalize( m_SaveVelocity );
  749. float SteeringForce = m_flFlyingSpeed * (1-frac) * ( DotProduct( WallNormal, m_SaveVelocity ) );
  750. if (SteeringForce < 0.0)
  751. {
  752. SteeringForce = -SteeringForce;
  753. }
  754. Vector vSteering = SteeringVector;
  755. VectorNormalize( vSteering );
  756. SteeringVector = SteeringForce * vSteering;
  757. //NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + (SteeringVector*4.0f), 0, 255, 255, true, 0.1f );
  758. return SteeringVector;
  759. }
  760. return Vector(0, 0, 0);
  761. }
  762. bool CNPC_Ichthyosaur::ProbeZ( const Vector &position, const Vector &probe, float *pFraction)
  763. {
  764. int iPositionContents = UTIL_PointContents( position );
  765. int iProbeContents = UTIL_PointContents( position );
  766. if( !(iPositionContents & MASK_WATER) )
  767. {
  768. // we're not in the water anymore
  769. *pFraction = 0.0;
  770. return true; // We hit a water boundary because we are where we don't belong.
  771. }
  772. if( iProbeContents == iPositionContents )
  773. {
  774. // The probe is entirely inside the water
  775. *pFraction = 1.0;
  776. return false;
  777. }
  778. Vector ProbeUnit = (probe-position);
  779. VectorNormalize( ProbeUnit );
  780. float ProbeLength = (probe-position).Length();
  781. float maxProbeLength = ProbeLength;
  782. float minProbeLength = 0;
  783. float diff = maxProbeLength - minProbeLength;
  784. while (diff > 1.0)
  785. {
  786. float midProbeLength = minProbeLength + diff/2.0;
  787. Vector midProbeVec = midProbeLength * ProbeUnit;
  788. if (UTIL_PointContents(position+midProbeVec) == iPositionContents)
  789. {
  790. minProbeLength = midProbeLength;
  791. }
  792. else
  793. {
  794. maxProbeLength = midProbeLength;
  795. }
  796. diff = maxProbeLength - minProbeLength;
  797. }
  798. *pFraction = minProbeLength/ProbeLength;
  799. return true;
  800. }
  801. //-----------------------------------------------------------------------------
  802. // Purpose:
  803. // Input : *pEntity -
  804. // Output : Returns true on success, false on failure.
  805. //-----------------------------------------------------------------------------
  806. bool CNPC_Ichthyosaur::FVisible( CBaseEntity *pEntity, int traceMask, CBaseEntity **ppBlocker )
  807. {
  808. // Can't see entities that aren't in water
  809. if ( pEntity->GetWaterLevel() < 1 )
  810. return false;
  811. return BaseClass::FVisible( pEntity, traceMask, ppBlocker );
  812. }
  813. void CNPC_Ichthyosaur::IdleSound( void )
  814. {
  815. CPASAttenuationFilter filter( this );
  816. EmitSound( filter, entindex(), "Ichthyosaur.Idle" );
  817. }
  818. void CNPC_Ichthyosaur::AlertSound( void )
  819. {
  820. CPASAttenuationFilter filter( this );
  821. EmitSound( filter, entindex(), "Ichthyosaur.Alert" );
  822. }
  823. void CNPC_Ichthyosaur::AttackSound( void )
  824. {
  825. CPASAttenuationFilter filter( this );
  826. EmitSound( filter, entindex(), "Ichthyosaur.Attack" );
  827. }
  828. void CNPC_Ichthyosaur::BiteSound( void )
  829. {
  830. CPASAttenuationFilter filter( this );
  831. EmitSound( filter, entindex(), "Ichthyosaur.Bite" );
  832. }
  833. void CNPC_Ichthyosaur::DeathSound( const CTakeDamageInfo &info )
  834. {
  835. CPASAttenuationFilter filter( this );
  836. EmitSound( filter, entindex(), "Ichthyosaur.Die" );
  837. }
  838. void CNPC_Ichthyosaur::PainSound( const CTakeDamageInfo &info )
  839. {
  840. CPASAttenuationFilter filter( this );
  841. EmitSound( filter, entindex(), "Ichthyosaur.Pain" );
  842. }
  843. //-----------------------------------------------------------------------------
  844. void CNPC_Ichthyosaur::GatherEnemyConditions( CBaseEntity *pEnemy )
  845. {
  846. // Do the base class
  847. BaseClass::GatherEnemyConditions( pEnemy );
  848. if ( HasCondition( COND_ENEMY_UNREACHABLE ) == false )
  849. {
  850. if( pEnemy == NULL || pEnemy->GetWaterLevel() != GetWaterLevel() )
  851. {
  852. SetCondition( COND_ENEMY_UNREACHABLE );
  853. }
  854. }
  855. }