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.

1599 lines
38 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Crows. Simple ambient birds that fly away when they hear gunfire or
  4. // when anything gets too close to them.
  5. //
  6. // TODO: landing
  7. // TODO: death
  8. //
  9. // $NoKeywords: $
  10. //=============================================================================//
  11. #include "cbase.h"
  12. #include "game.h"
  13. #include "ai_basenpc.h"
  14. #include "ai_schedule.h"
  15. #include "ai_hull.h"
  16. #include "ai_hint.h"
  17. #include "ai_motor.h"
  18. #include "ai_navigator.h"
  19. #include "hl2_shareddefs.h"
  20. #include "ai_route.h"
  21. #include "npcevent.h"
  22. #include "gib.h"
  23. #include "ai_interactions.h"
  24. #include "ndebugoverlay.h"
  25. #include "soundent.h"
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "movevars_shared.h"
  29. #include "npc_crow.h"
  30. #include "ai_moveprobe.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. //
  34. // Custom activities.
  35. //
  36. static int ACT_CROW_TAKEOFF;
  37. static int ACT_CROW_SOAR;
  38. static int ACT_CROW_LAND;
  39. //
  40. // Animation events.
  41. //
  42. static int AE_CROW_TAKEOFF;
  43. static int AE_CROW_FLY;
  44. static int AE_CROW_HOP;
  45. //
  46. // Skill settings.
  47. //
  48. ConVar sk_crow_health( "sk_crow_health","1");
  49. ConVar sk_crow_melee_dmg( "sk_crow_melee_dmg","0");
  50. LINK_ENTITY_TO_CLASS( npc_crow, CNPC_Crow );
  51. LINK_ENTITY_TO_CLASS( npc_seagull, CNPC_Seagull );
  52. LINK_ENTITY_TO_CLASS( npc_pigeon, CNPC_Pigeon );
  53. BEGIN_DATADESC( CNPC_Crow )
  54. DEFINE_FIELD( m_flGroundIdleMoveTime, FIELD_TIME ),
  55. DEFINE_FIELD( m_bOnJeep, FIELD_BOOLEAN ),
  56. DEFINE_FIELD( m_flEnemyDist, FIELD_FLOAT ),
  57. DEFINE_FIELD( m_nMorale, FIELD_INTEGER ),
  58. DEFINE_FIELD( m_bReachedMoveGoal, FIELD_BOOLEAN ),
  59. DEFINE_FIELD( m_flHopStartZ, FIELD_FLOAT ),
  60. DEFINE_FIELD( m_vDesiredTarget, FIELD_VECTOR ),
  61. DEFINE_FIELD( m_vCurrentTarget, FIELD_VECTOR ),
  62. DEFINE_FIELD( m_flSoarTime, FIELD_TIME ),
  63. DEFINE_FIELD( m_bSoar, FIELD_BOOLEAN ),
  64. DEFINE_FIELD( m_bPlayedLoopingSound, FIELD_BOOLEAN ),
  65. DEFINE_FIELD( m_iBirdType, FIELD_INTEGER ),
  66. DEFINE_FIELD( m_vLastStoredOrigin, FIELD_POSITION_VECTOR ),
  67. DEFINE_FIELD( m_flLastStuckCheck, FIELD_TIME ),
  68. DEFINE_FIELD( m_flDangerSoundTime, FIELD_TIME ),
  69. DEFINE_KEYFIELD( m_bIsDeaf, FIELD_BOOLEAN, "deaf" ),
  70. // Inputs
  71. DEFINE_INPUTFUNC( FIELD_STRING, "FlyAway", InputFlyAway ),
  72. END_DATADESC()
  73. static ConVar birds_debug( "birds_debug", "0" );
  74. //-----------------------------------------------------------------------------
  75. // Purpose:
  76. //-----------------------------------------------------------------------------
  77. void CNPC_Crow::Spawn( void )
  78. {
  79. BaseClass::Spawn();
  80. #ifdef _XBOX
  81. // Always fade the corpse
  82. AddSpawnFlags( SF_NPC_FADE_CORPSE );
  83. #endif // _XBOX
  84. char *szModel = (char *)STRING( GetModelName() );
  85. if (!szModel || !*szModel)
  86. {
  87. szModel = "models/crow.mdl";
  88. SetModelName( AllocPooledString(szModel) );
  89. }
  90. Precache();
  91. SetModel( szModel );
  92. m_iHealth = sk_crow_health.GetFloat();
  93. SetHullType(HULL_TINY);
  94. SetHullSizeNormal();
  95. SetSolid( SOLID_BBOX );
  96. SetMoveType( MOVETYPE_STEP );
  97. m_flFieldOfView = VIEW_FIELD_FULL;
  98. SetViewOffset( Vector(6, 0, 11) ); // Position of the eyes relative to NPC's origin.
  99. m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 0.0f, 5.0f );
  100. SetBloodColor( BLOOD_COLOR_RED );
  101. m_NPCState = NPC_STATE_NONE;
  102. m_nMorale = random->RandomInt( 0, 12 );
  103. SetCollisionGroup( HL2COLLISION_GROUP_CROW );
  104. CapabilitiesClear();
  105. bool bFlying = ( ( m_spawnflags & SF_CROW_FLYING ) != 0 );
  106. SetFlyingState( bFlying ? FlyState_Flying : FlyState_Walking );
  107. // We don't mind zombies so much. They smell good!
  108. AddClassRelationship( CLASS_ZOMBIE, D_NU, 0 );
  109. m_bSoar = false;
  110. m_bOnJeep = false;
  111. m_flSoarTime = gpGlobals->curtime;
  112. NPCInit();
  113. m_iBirdType = BIRDTYPE_CROW;
  114. m_vLastStoredOrigin = vec3_origin;
  115. m_flLastStuckCheck = gpGlobals->curtime;
  116. m_flDangerSoundTime = gpGlobals->curtime;
  117. SetGoalEnt( NULL );
  118. }
  119. //-----------------------------------------------------------------------------
  120. // Purpose: Returns this monster's classification in the relationship table.
  121. //-----------------------------------------------------------------------------
  122. Class_T CNPC_Crow::Classify( void )
  123. {
  124. return( CLASS_EARTH_FAUNA );
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Purpose:
  128. // Input : pEnemy -
  129. //-----------------------------------------------------------------------------
  130. void CNPC_Crow::GatherEnemyConditions( CBaseEntity *pEnemy )
  131. {
  132. m_flEnemyDist = (GetLocalOrigin() - pEnemy->GetLocalOrigin()).Length();
  133. if ( m_flEnemyDist < 512 )
  134. {
  135. SetCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE );
  136. }
  137. if ( m_flEnemyDist < 1024 )
  138. {
  139. SetCondition( COND_CROW_ENEMY_TOO_CLOSE );
  140. }
  141. BaseClass::GatherEnemyConditions(pEnemy);
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. // Input : posSrc -
  146. // Output : Vector
  147. //-----------------------------------------------------------------------------
  148. Vector CNPC_Crow::BodyTarget( const Vector &posSrc, bool bNoisy )
  149. {
  150. Vector vecResult;
  151. vecResult = GetAbsOrigin();
  152. vecResult.z += 6;
  153. return vecResult;
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose:
  157. //-----------------------------------------------------------------------------
  158. void CNPC_Crow::StopLoopingSounds( void )
  159. {
  160. //
  161. // Stop whatever flap sound might be playing.
  162. //
  163. if ( m_bPlayedLoopingSound )
  164. {
  165. StopSound( "NPC_Crow.Flap" );
  166. }
  167. BaseClass::StopLoopingSounds();
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose: Catches the monster-specific messages that occur when tagged
  171. // animation frames are played.
  172. // Input : pEvent -
  173. //-----------------------------------------------------------------------------
  174. void CNPC_Crow::HandleAnimEvent( animevent_t *pEvent )
  175. {
  176. if ( pEvent->event == AE_CROW_TAKEOFF )
  177. {
  178. if ( GetNavigator()->GetPath()->GetCurWaypoint() )
  179. {
  180. Takeoff( GetNavigator()->GetCurWaypointPos() );
  181. }
  182. return;
  183. }
  184. if( pEvent->event == AE_CROW_HOP )
  185. {
  186. SetGroundEntity( NULL );
  187. //
  188. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  189. //
  190. UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
  191. //
  192. // How fast does the crow need to travel to reach the hop goal given gravity?
  193. //
  194. float flHopDistance = ( m_vSavePosition - GetLocalOrigin() ).Length();
  195. float gravity = GetCurrentGravity();
  196. if ( gravity <= 1 )
  197. {
  198. gravity = 1;
  199. }
  200. float height = 0.25 * flHopDistance;
  201. float speed = sqrt( 2 * gravity * height );
  202. float time = speed / gravity;
  203. //
  204. // Scale the sideways velocity to get there at the right time
  205. //
  206. Vector vecJumpDir = m_vSavePosition - GetLocalOrigin();
  207. vecJumpDir = vecJumpDir / time;
  208. //
  209. // Speed to offset gravity at the desired height.
  210. //
  211. vecJumpDir.z = speed;
  212. //
  213. // Don't jump too far/fast.
  214. //
  215. float distance = vecJumpDir.Length();
  216. if ( distance > 650 )
  217. {
  218. vecJumpDir = vecJumpDir * ( 650.0 / distance );
  219. }
  220. m_nMorale -= random->RandomInt( 1, 6 );
  221. if ( m_nMorale <= 0 )
  222. {
  223. m_nMorale = 0;
  224. }
  225. // Play a hop flap sound.
  226. EmitSound( "NPC_Crow.Hop" );
  227. SetAbsVelocity( vecJumpDir );
  228. return;
  229. }
  230. if( pEvent->event == AE_CROW_FLY )
  231. {
  232. //
  233. // Start flying.
  234. //
  235. SetActivity( ACT_FLY );
  236. m_bSoar = false;
  237. m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
  238. return;
  239. }
  240. CAI_BaseNPC::HandleAnimEvent( pEvent );
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Purpose:
  244. // Input : eNewActivity -
  245. //-----------------------------------------------------------------------------
  246. void CNPC_Crow::OnChangeActivity( Activity eNewActivity )
  247. {
  248. // if ( eNewActivity == ACT_FLY )
  249. // {
  250. // m_flGroundSpeed = CROW_AIRSPEED;
  251. // }
  252. //
  253. bool fRandomize = false;
  254. if ( eNewActivity == ACT_FLY )
  255. {
  256. fRandomize = true;
  257. }
  258. BaseClass::OnChangeActivity( eNewActivity );
  259. if ( fRandomize )
  260. {
  261. SetCycle( random->RandomFloat( 0.0, 0.75 ) );
  262. }
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Input handler that makes the crow fly away.
  266. //-----------------------------------------------------------------------------
  267. void CNPC_Crow::InputFlyAway( inputdata_t &inputdata )
  268. {
  269. string_t sTarget = MAKE_STRING( inputdata.value.String() );
  270. if ( sTarget != NULL_STRING )// this npc has a target
  271. {
  272. CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, sTarget );
  273. if ( pEnt )
  274. {
  275. trace_t tr;
  276. AI_TraceLine ( EyePosition(), pEnt->GetAbsOrigin(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  277. if ( tr.fraction != 1.0f )
  278. return;
  279. // Find the npc's initial target entity, stash it
  280. SetGoalEnt( pEnt );
  281. }
  282. }
  283. else
  284. SetGoalEnt( NULL );
  285. SetCondition( COND_CROW_FORCED_FLY );
  286. SetCondition( COND_PROVOKED );
  287. }
  288. void CNPC_Crow::UpdateEfficiency( bool bInPVS )
  289. {
  290. if ( IsFlying() )
  291. {
  292. SetEfficiency( ( GetSleepState() != AISS_AWAKE ) ? AIE_DORMANT : AIE_NORMAL );
  293. SetMoveEfficiency( AIME_NORMAL );
  294. return;
  295. }
  296. BaseClass::UpdateEfficiency( bInPVS );
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Purpose: Implements "deafness"
  300. //-----------------------------------------------------------------------------
  301. bool CNPC_Crow::QueryHearSound( CSound *pSound )
  302. {
  303. if( IsDeaf() )
  304. return false;
  305. return BaseClass::QueryHearSound( pSound );
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose: Handles all flight movement because we don't ever build paths when
  309. // when we are flying.
  310. // Input : flInterval - Seconds to simulate.
  311. //-----------------------------------------------------------------------------
  312. bool CNPC_Crow::OverrideMove( float flInterval )
  313. {
  314. if ( GetNavigator()->GetPath()->CurWaypointNavType() == NAV_FLY && GetNavigator()->GetNavType() != NAV_FLY )
  315. {
  316. SetNavType( NAV_FLY );
  317. }
  318. if ( IsFlying() )
  319. {
  320. if ( GetNavigator()->GetPath()->GetCurWaypoint() )
  321. {
  322. if ( m_flLastStuckCheck <= gpGlobals->curtime )
  323. {
  324. if ( m_vLastStoredOrigin == GetAbsOrigin() )
  325. {
  326. if ( GetAbsVelocity() == vec3_origin )
  327. {
  328. float flDamage = m_iHealth;
  329. CTakeDamageInfo dmgInfo( this, this, flDamage, DMG_GENERIC );
  330. GuessDamageForce( &dmgInfo, vec3_origin - Vector( 0, 0, 0.1 ), GetAbsOrigin() );
  331. TakeDamage( dmgInfo );
  332. return false;
  333. }
  334. else
  335. {
  336. m_vLastStoredOrigin = GetAbsOrigin();
  337. }
  338. }
  339. else
  340. {
  341. m_vLastStoredOrigin = GetAbsOrigin();
  342. }
  343. m_flLastStuckCheck = gpGlobals->curtime + 1.0f;
  344. }
  345. if (m_bReachedMoveGoal )
  346. {
  347. SetIdealActivity( (Activity)ACT_CROW_LAND );
  348. SetFlyingState( FlyState_Landing );
  349. TaskMovementComplete();
  350. }
  351. else
  352. {
  353. SetIdealActivity ( ACT_FLY );
  354. MoveCrowFly( flInterval );
  355. }
  356. }
  357. else if ( !GetTask() || GetTask()->iTask == TASK_WAIT_FOR_MOVEMENT )
  358. {
  359. SetSchedule( SCHED_CROW_IDLE_FLY );
  360. SetFlyingState( FlyState_Flying );
  361. SetIdealActivity ( ACT_FLY );
  362. }
  363. return true;
  364. }
  365. return false;
  366. }
  367. Activity CNPC_Crow::NPC_TranslateActivity( Activity eNewActivity )
  368. {
  369. if ( IsFlying() && eNewActivity == ACT_IDLE )
  370. {
  371. return ACT_FLY;
  372. }
  373. if ( eNewActivity == ACT_FLY )
  374. {
  375. if ( m_flSoarTime < gpGlobals->curtime )
  376. {
  377. //Adrian: This should be revisited.
  378. if ( random->RandomInt( 0, 100 ) <= 50 && m_bSoar == false && GetAbsVelocity().z < 0 )
  379. {
  380. m_bSoar = true;
  381. m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 1, 4 );
  382. }
  383. else
  384. {
  385. m_bSoar = false;
  386. m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
  387. }
  388. }
  389. if ( m_bSoar == true )
  390. {
  391. return (Activity)ACT_CROW_SOAR;
  392. }
  393. else
  394. return ACT_FLY;
  395. }
  396. return BaseClass::NPC_TranslateActivity( eNewActivity );
  397. }
  398. //-----------------------------------------------------------------------------
  399. // Purpose: Handles all flight movement.
  400. // Input : flInterval - Seconds to simulate.
  401. //-----------------------------------------------------------------------------
  402. void CNPC_Crow::MoveCrowFly( float flInterval )
  403. {
  404. //
  405. // Bound interval so we don't get ludicrous motion when debugging
  406. // or when framerate drops catastrophically.
  407. //
  408. if (flInterval > 1.0)
  409. {
  410. flInterval = 1.0;
  411. }
  412. m_flDangerSoundTime = gpGlobals->curtime + 5.0f;
  413. //
  414. // Determine the goal of our movement.
  415. //
  416. Vector vecMoveGoal = GetAbsOrigin();
  417. if ( GetNavigator()->IsGoalActive() )
  418. {
  419. vecMoveGoal = GetNavigator()->GetCurWaypointPos();
  420. if ( GetNavigator()->CurWaypointIsGoal() == false )
  421. {
  422. AI_ProgressFlyPathParams_t params( MASK_NPCSOLID );
  423. params.bTrySimplify = false;
  424. GetNavigator()->ProgressFlyPath( params ); // ignore result, crow handles completion directly
  425. // Fly towards the hint.
  426. if ( GetNavigator()->GetPath()->GetCurWaypoint() )
  427. {
  428. vecMoveGoal = GetNavigator()->GetCurWaypointPos();
  429. }
  430. }
  431. }
  432. else
  433. {
  434. // No movement goal.
  435. vecMoveGoal = GetAbsOrigin();
  436. SetAbsVelocity( vec3_origin );
  437. return;
  438. }
  439. Vector vecMoveDir = ( vecMoveGoal - GetAbsOrigin() );
  440. Vector vForward;
  441. AngleVectors( GetAbsAngles(), &vForward );
  442. //
  443. // Fly towards the movement goal.
  444. //
  445. float flDistance = ( vecMoveGoal - GetAbsOrigin() ).Length();
  446. if ( vecMoveGoal != m_vDesiredTarget )
  447. {
  448. m_vDesiredTarget = vecMoveGoal;
  449. }
  450. else
  451. {
  452. m_vCurrentTarget = ( m_vDesiredTarget - GetAbsOrigin() );
  453. VectorNormalize( m_vCurrentTarget );
  454. }
  455. float flLerpMod = 0.25f;
  456. if ( flDistance <= 256.0f )
  457. {
  458. flLerpMod = 1.0f - ( flDistance / 256.0f );
  459. }
  460. VectorLerp( vForward, m_vCurrentTarget, flLerpMod, vForward );
  461. if ( flDistance < CROW_AIRSPEED * flInterval )
  462. {
  463. if ( GetNavigator()->IsGoalActive() )
  464. {
  465. if ( GetNavigator()->CurWaypointIsGoal() )
  466. {
  467. m_bReachedMoveGoal = true;
  468. }
  469. else
  470. {
  471. GetNavigator()->AdvancePath();
  472. }
  473. }
  474. else
  475. m_bReachedMoveGoal = true;
  476. }
  477. if ( GetHintNode() )
  478. {
  479. AIMoveTrace_t moveTrace;
  480. GetMoveProbe()->MoveLimit( NAV_FLY, GetAbsOrigin(), GetNavigator()->GetCurWaypointPos(), MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace );
  481. //See if it succeeded
  482. if ( IsMoveBlocked( moveTrace.fStatus ) )
  483. {
  484. Vector vNodePos = vecMoveGoal;
  485. GetHintNode()->GetPosition(this, &vNodePos);
  486. GetNavigator()->SetGoal( vNodePos );
  487. }
  488. }
  489. //
  490. // Look to see if we are going to hit anything.
  491. //
  492. VectorNormalize( vForward );
  493. Vector vecDeflect;
  494. if ( Probe( vForward, CROW_AIRSPEED * flInterval, vecDeflect ) )
  495. {
  496. vForward = vecDeflect;
  497. VectorNormalize( vForward );
  498. }
  499. SetAbsVelocity( vForward * CROW_AIRSPEED );
  500. if ( GetAbsVelocity().Length() > 0 && GetNavigator()->CurWaypointIsGoal() && flDistance < CROW_AIRSPEED )
  501. {
  502. SetIdealActivity( (Activity)ACT_CROW_LAND );
  503. }
  504. //Bank and set angles.
  505. Vector vRight;
  506. QAngle vRollAngle;
  507. VectorAngles( vForward, vRollAngle );
  508. vRollAngle.z = 0;
  509. AngleVectors( vRollAngle, NULL, &vRight, NULL );
  510. float flRoll = DotProduct( vRight, vecMoveDir ) * 45;
  511. flRoll = clamp( flRoll, -45, 45 );
  512. vRollAngle[ROLL] = flRoll;
  513. SetAbsAngles( vRollAngle );
  514. }
  515. //-----------------------------------------------------------------------------
  516. // Purpose: Looks ahead to see if we are going to hit something. If we are, a
  517. // recommended avoidance path is returned.
  518. // Input : vecMoveDir -
  519. // flSpeed -
  520. // vecDeflect -
  521. // Output : Returns true if we hit something and need to deflect our course,
  522. // false if all is well.
  523. //-----------------------------------------------------------------------------
  524. bool CNPC_Crow::Probe( const Vector &vecMoveDir, float flSpeed, Vector &vecDeflect )
  525. {
  526. //
  527. // Look 1/2 second ahead.
  528. //
  529. trace_t tr;
  530. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + vecMoveDir * flSpeed, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, HL2COLLISION_GROUP_CROW, &tr );
  531. if ( tr.fraction < 1.0f )
  532. {
  533. //
  534. // If we hit something, deflect flight path parallel to surface hit.
  535. //
  536. Vector vecUp;
  537. CrossProduct( vecMoveDir, tr.plane.normal, vecUp );
  538. CrossProduct( tr.plane.normal, vecUp, vecDeflect );
  539. VectorNormalize( vecDeflect );
  540. return true;
  541. }
  542. vecDeflect = vec3_origin;
  543. return false;
  544. }
  545. //-----------------------------------------------------------------------------
  546. // Purpose: Switches between flying mode and ground mode.
  547. //-----------------------------------------------------------------------------
  548. void CNPC_Crow::SetFlyingState( FlyState_t eState )
  549. {
  550. if ( eState == FlyState_Flying )
  551. {
  552. // Flying
  553. SetGroundEntity( NULL );
  554. AddFlag( FL_FLY );
  555. SetNavType( NAV_FLY );
  556. CapabilitiesRemove( bits_CAP_MOVE_GROUND );
  557. CapabilitiesAdd( bits_CAP_MOVE_FLY );
  558. SetMoveType( MOVETYPE_STEP );
  559. m_vLastStoredOrigin = GetAbsOrigin();
  560. m_flLastStuckCheck = gpGlobals->curtime + 3.0f;
  561. m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
  562. }
  563. else if ( eState == FlyState_Walking )
  564. {
  565. // Walking
  566. QAngle angles = GetAbsAngles();
  567. angles[PITCH] = 0.0f;
  568. angles[ROLL] = 0.0f;
  569. SetAbsAngles( angles );
  570. RemoveFlag( FL_FLY );
  571. SetNavType( NAV_GROUND );
  572. CapabilitiesRemove( bits_CAP_MOVE_FLY );
  573. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  574. SetMoveType( MOVETYPE_STEP );
  575. m_vLastStoredOrigin = vec3_origin;
  576. m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
  577. }
  578. else
  579. {
  580. // Falling
  581. RemoveFlag( FL_FLY );
  582. SetNavType( NAV_GROUND );
  583. CapabilitiesRemove( bits_CAP_MOVE_FLY );
  584. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  585. SetMoveType( MOVETYPE_STEP );
  586. m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 5.0f, 10.0f );
  587. }
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Purpose: Performs a takeoff. Called via an animation event at the moment
  591. // our feet leave the ground.
  592. // Input : pGoalEnt - The entity that we are going to fly toward.
  593. //-----------------------------------------------------------------------------
  594. void CNPC_Crow::Takeoff( const Vector &vGoal )
  595. {
  596. if ( vGoal != vec3_origin )
  597. {
  598. //
  599. // Lift us off ground so engine doesn't instantly reset FL_ONGROUND.
  600. //
  601. UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0 , 0 , 1 ));
  602. //
  603. // Fly straight at the goal entity at our maximum airspeed.
  604. //
  605. Vector vecMoveDir = vGoal - GetAbsOrigin();
  606. VectorNormalize( vecMoveDir );
  607. // FIXME: pitch over time
  608. SetFlyingState( FlyState_Flying );
  609. QAngle angles;
  610. VectorAngles( vecMoveDir, angles );
  611. SetAbsAngles( angles );
  612. SetAbsVelocity( vecMoveDir * CROW_TAKEOFF_SPEED );
  613. }
  614. }
  615. void CNPC_Crow::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  616. {
  617. CTakeDamageInfo newInfo = info;
  618. if ( info.GetDamageType() & DMG_PHYSGUN )
  619. {
  620. Vector puntDir = ( info.GetDamageForce() * 5000.0f );
  621. newInfo.SetDamage( m_iMaxHealth );
  622. PainSound( newInfo );
  623. newInfo.SetDamageForce( puntDir );
  624. }
  625. BaseClass::TraceAttack( newInfo, vecDir, ptr, pAccumulator );
  626. }
  627. void CNPC_Crow::StartTargetHandling( CBaseEntity *pTargetEnt )
  628. {
  629. AI_NavGoal_t goal( GOALTYPE_PATHCORNER, pTargetEnt->GetAbsOrigin(),
  630. ACT_FLY,
  631. AIN_DEF_TOLERANCE, AIN_YAW_TO_DEST);
  632. if ( !GetNavigator()->SetGoal( goal ) )
  633. {
  634. DevWarning( 2, "Can't Create Route!\n" );
  635. }
  636. }
  637. //-----------------------------------------------------------------------------
  638. // Purpose:
  639. // Input : pTask -
  640. //-----------------------------------------------------------------------------
  641. void CNPC_Crow::StartTask( const Task_t *pTask )
  642. {
  643. switch ( pTask->iTask )
  644. {
  645. //
  646. // This task enables us to build a path that requires flight.
  647. //
  648. // case TASK_CROW_PREPARE_TO_FLY:
  649. // {
  650. // SetFlyingState( FlyState_Flying );
  651. // TaskComplete();
  652. // break;
  653. // }
  654. case TASK_CROW_TAKEOFF:
  655. {
  656. if ( random->RandomInt( 1, 4 ) == 1 )
  657. {
  658. AlertSound();
  659. }
  660. FlapSound();
  661. SetIdealActivity( ( Activity )ACT_CROW_TAKEOFF );
  662. break;
  663. }
  664. case TASK_CROW_PICK_EVADE_GOAL:
  665. {
  666. if ( GetEnemy() != NULL )
  667. {
  668. //
  669. // Get our enemy's position in x/y.
  670. //
  671. Vector vecEnemyOrigin = GetEnemy()->GetAbsOrigin();
  672. vecEnemyOrigin.z = GetAbsOrigin().z;
  673. //
  674. // Pick a hop goal a random distance along a vector away from our enemy.
  675. //
  676. m_vSavePosition = GetAbsOrigin() - vecEnemyOrigin;
  677. VectorNormalize( m_vSavePosition );
  678. m_vSavePosition = GetAbsOrigin() + m_vSavePosition * ( 32 + random->RandomInt( 0, 32 ) );
  679. GetMotor()->SetIdealYawToTarget( m_vSavePosition );
  680. TaskComplete();
  681. }
  682. else
  683. {
  684. TaskFail( "No enemy" );
  685. }
  686. break;
  687. }
  688. case TASK_CROW_FALL_TO_GROUND:
  689. {
  690. SetFlyingState( FlyState_Falling );
  691. break;
  692. }
  693. case TASK_FIND_HINTNODE:
  694. {
  695. if ( GetGoalEnt() )
  696. {
  697. TaskComplete();
  698. return;
  699. }
  700. // Overloaded because we search over a greater distance.
  701. if ( !GetHintNode() )
  702. {
  703. SetHintNode(CAI_HintManager::FindHint( this, HINT_CROW_FLYTO_POINT, bits_HINT_NODE_NEAREST | bits_HINT_NODE_USE_GROUP, 10000 ));
  704. }
  705. if ( GetHintNode() )
  706. {
  707. TaskComplete();
  708. }
  709. else
  710. {
  711. TaskFail( FAIL_NO_HINT_NODE );
  712. }
  713. break;
  714. }
  715. case TASK_GET_PATH_TO_HINTNODE:
  716. {
  717. //How did this happen?!
  718. if ( GetGoalEnt() == this )
  719. {
  720. SetGoalEnt( NULL );
  721. }
  722. if ( GetGoalEnt() )
  723. {
  724. SetFlyingState( FlyState_Flying );
  725. StartTargetHandling( GetGoalEnt() );
  726. m_bReachedMoveGoal = false;
  727. TaskComplete();
  728. SetHintNode( NULL );
  729. return;
  730. }
  731. if ( GetHintNode() )
  732. {
  733. Vector vHintPos;
  734. GetHintNode()->GetPosition(this, &vHintPos);
  735. SetNavType( NAV_FLY );
  736. CapabilitiesAdd( bits_CAP_MOVE_FLY );
  737. // @HACKHACK: Force allow triangulation. Too many HL2 maps were relying on this feature WRT fly nodes (toml 8/1/2007)
  738. NPC_STATE state = GetState();
  739. m_NPCState = NPC_STATE_SCRIPT;
  740. bool bFoundPath = GetNavigator()->SetGoal( vHintPos );
  741. m_NPCState = state;
  742. if ( !bFoundPath )
  743. {
  744. GetHintNode()->DisableForSeconds( .3 );
  745. SetHintNode(NULL);
  746. }
  747. CapabilitiesRemove( bits_CAP_MOVE_FLY );
  748. }
  749. if ( GetHintNode() )
  750. {
  751. m_bReachedMoveGoal = false;
  752. TaskComplete();
  753. }
  754. else
  755. {
  756. TaskFail( FAIL_NO_ROUTE );
  757. }
  758. break;
  759. }
  760. //
  761. // We have failed to fly normally. Pick a random "up" direction and fly that way.
  762. //
  763. case TASK_CROW_FLY:
  764. {
  765. float flYaw = UTIL_AngleMod( random->RandomInt( -180, 180 ) );
  766. Vector vecNewVelocity( cos( DEG2RAD( flYaw ) ), sin( DEG2RAD( flYaw ) ), random->RandomFloat( 0.1f, 0.5f ) );
  767. vecNewVelocity *= CROW_AIRSPEED;
  768. SetAbsVelocity( vecNewVelocity );
  769. SetIdealActivity( ACT_FLY );
  770. m_bSoar = false;
  771. m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 );
  772. break;
  773. }
  774. case TASK_CROW_PICK_RANDOM_GOAL:
  775. {
  776. m_vSavePosition = GetLocalOrigin() + Vector( random->RandomFloat( -48.0f, 48.0f ), random->RandomFloat( -48.0f, 48.0f ), 0 );
  777. TaskComplete();
  778. break;
  779. }
  780. case TASK_CROW_HOP:
  781. {
  782. SetIdealActivity( ACT_HOP );
  783. m_flHopStartZ = GetLocalOrigin().z;
  784. break;
  785. }
  786. case TASK_CROW_WAIT_FOR_BARNACLE_KILL:
  787. {
  788. break;
  789. }
  790. default:
  791. {
  792. BaseClass::StartTask( pTask );
  793. }
  794. }
  795. }
  796. //-----------------------------------------------------------------------------
  797. // Purpose:
  798. // Input : pTask -
  799. //-----------------------------------------------------------------------------
  800. void CNPC_Crow::RunTask( const Task_t *pTask )
  801. {
  802. switch ( pTask->iTask )
  803. {
  804. case TASK_CROW_TAKEOFF:
  805. {
  806. if ( GetNavigator()->IsGoalActive() )
  807. {
  808. GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + GetNavigator()->GetCurWaypointPos(), AI_KEEP_YAW_SPEED );
  809. }
  810. else
  811. TaskFail( FAIL_NO_ROUTE );
  812. if ( IsActivityFinished() )
  813. {
  814. TaskComplete();
  815. SetIdealActivity( ACT_FLY );
  816. m_bSoar = false;
  817. m_flSoarTime = gpGlobals->curtime + random->RandomFloat( 2, 5 );
  818. }
  819. break;
  820. }
  821. case TASK_CROW_HOP:
  822. {
  823. if ( IsActivityFinished() )
  824. {
  825. TaskComplete();
  826. SetIdealActivity( ACT_IDLE );
  827. }
  828. if ( ( GetAbsOrigin().z < m_flHopStartZ ) && ( !( GetFlags() & FL_ONGROUND ) ) )
  829. {
  830. //
  831. // We've hopped off of something! See if we're going to fall very far.
  832. //
  833. trace_t tr;
  834. AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -32 ), MASK_SOLID, this, HL2COLLISION_GROUP_CROW, &tr );
  835. if ( tr.fraction == 1.0f )
  836. {
  837. //
  838. // We're falling! Better fly away. SelectSchedule will check ONGROUND and do the right thing.
  839. //
  840. TaskComplete();
  841. }
  842. else
  843. {
  844. //
  845. // We'll be okay. Don't check again unless what we're hopping onto moves
  846. // out from under us.
  847. //
  848. m_flHopStartZ = GetAbsOrigin().z - ( 32 * tr.fraction );
  849. }
  850. }
  851. break;
  852. }
  853. //
  854. // Face the direction we are flying.
  855. //
  856. case TASK_CROW_FLY:
  857. {
  858. GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + GetAbsVelocity(), AI_KEEP_YAW_SPEED );
  859. break;
  860. }
  861. case TASK_CROW_FALL_TO_GROUND:
  862. {
  863. if ( GetFlags() & FL_ONGROUND )
  864. {
  865. SetFlyingState( FlyState_Walking );
  866. TaskComplete();
  867. }
  868. break;
  869. }
  870. case TASK_CROW_WAIT_FOR_BARNACLE_KILL:
  871. {
  872. if ( m_flNextFlinchTime < gpGlobals->curtime )
  873. {
  874. m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 2.0f );
  875. // dvs: TODO: squirm
  876. // dvs: TODO: spawn feathers
  877. EmitSound( "NPC_Crow.Squawk" );
  878. }
  879. break;
  880. }
  881. default:
  882. {
  883. CAI_BaseNPC::RunTask( pTask );
  884. }
  885. }
  886. }
  887. //------------------------------------------------------------------------------
  888. // Purpose: Override to do crow specific gibs.
  889. // Output : Returns true to gib, false to not gib.
  890. //-----------------------------------------------------------------------------
  891. bool CNPC_Crow::CorpseGib( const CTakeDamageInfo &info )
  892. {
  893. EmitSound( "NPC_Crow.Gib" );
  894. // TODO: crow gibs?
  895. //CGib::SpawnSpecificGibs( this, CROW_GIB_COUNT, 300, 400, "models/gibs/crow_gibs.mdl");
  896. return true;
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Don't allow ridiculous forces to be applied to the crow. It only weighs
  900. // 1.5kg, so extreme forces will give it ridiculous velocity.
  901. //-----------------------------------------------------------------------------
  902. #define CROW_RAGDOLL_SPEED_LIMIT 1000.0f // Crow ragdoll speed limit in inches per second.
  903. bool CNPC_Crow::BecomeRagdollOnClient( const Vector &force )
  904. {
  905. Vector newForce = force;
  906. if( VPhysicsGetObject() )
  907. {
  908. float flMass = VPhysicsGetObject()->GetMass();
  909. float speed = VectorNormalize( newForce );
  910. speed = MIN( speed, (CROW_RAGDOLL_SPEED_LIMIT * flMass) );
  911. newForce *= speed;
  912. }
  913. return BaseClass::BecomeRagdollOnClient( newForce );
  914. }
  915. //-----------------------------------------------------------------------------
  916. // Purpose:
  917. //-----------------------------------------------------------------------------
  918. bool CNPC_Crow::FValidateHintType( CAI_Hint *pHint )
  919. {
  920. return( pHint->HintType() == HINT_CROW_FLYTO_POINT );
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Purpose: Returns the activity for the given hint type.
  924. // Input : sHintType -
  925. //-----------------------------------------------------------------------------
  926. Activity CNPC_Crow::GetHintActivity( short sHintType, Activity HintsActivity )
  927. {
  928. if ( sHintType == HINT_CROW_FLYTO_POINT )
  929. {
  930. return ACT_FLY;
  931. }
  932. return BaseClass::GetHintActivity( sHintType, HintsActivity );
  933. }
  934. //-----------------------------------------------------------------------------
  935. // Purpose:
  936. // Input : pevInflictor -
  937. // pevAttacker -
  938. // flDamage -
  939. // bitsDamageType -
  940. //-----------------------------------------------------------------------------
  941. int CNPC_Crow::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  942. {
  943. // TODO: spew a feather or two
  944. return BaseClass::OnTakeDamage_Alive( info );
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose: Returns the best new schedule for this NPC based on current conditions.
  948. //-----------------------------------------------------------------------------
  949. int CNPC_Crow::SelectSchedule( void )
  950. {
  951. if ( HasCondition( COND_CROW_BARNACLED ) )
  952. {
  953. // Caught by a barnacle!
  954. return SCHED_CROW_BARNACLED;
  955. }
  956. //
  957. // If we're flying, just find somewhere to fly to.
  958. //
  959. if ( IsFlying() )
  960. {
  961. return SCHED_CROW_IDLE_FLY;
  962. }
  963. //
  964. // If we were told to fly away via our FlyAway input, do so ASAP.
  965. //
  966. if ( HasCondition( COND_CROW_FORCED_FLY ) )
  967. {
  968. ClearCondition( COND_CROW_FORCED_FLY );
  969. return SCHED_CROW_FLY_AWAY;
  970. }
  971. //
  972. // If we're not flying but we're not on the ground, start flying.
  973. // Maybe we hopped off of something? Don't do this immediately upon
  974. // because we may be falling to the ground on spawn.
  975. //
  976. if ( !( GetFlags() & FL_ONGROUND ) && ( gpGlobals->curtime > 2.0 ) && m_bOnJeep == false )
  977. {
  978. return SCHED_CROW_FLY_AWAY;
  979. }
  980. //
  981. // If we heard a gunshot or have taken damage, fly away.
  982. //
  983. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  984. {
  985. return SCHED_CROW_FLY_AWAY;
  986. }
  987. if ( m_flDangerSoundTime <= gpGlobals->curtime )
  988. {
  989. if ( HasCondition( COND_HEAR_DANGER ) || HasCondition( COND_HEAR_COMBAT ) )
  990. {
  991. m_flDangerSoundTime = gpGlobals->curtime + 10.0f;
  992. return SCHED_CROW_FLY_AWAY;
  993. }
  994. }
  995. //
  996. // If someone we hate is getting WAY too close for comfort, fly away.
  997. //
  998. if ( HasCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE ) )
  999. {
  1000. ClearCondition( COND_CROW_ENEMY_WAY_TOO_CLOSE );
  1001. m_nMorale = 0;
  1002. return SCHED_CROW_FLY_AWAY;
  1003. }
  1004. //
  1005. // If someone we hate is getting a little too close for comfort, avoid them.
  1006. //
  1007. if ( HasCondition( COND_CROW_ENEMY_TOO_CLOSE ) && m_flDangerSoundTime <= gpGlobals->curtime )
  1008. {
  1009. ClearCondition( COND_CROW_ENEMY_TOO_CLOSE );
  1010. if ( m_bOnJeep == true )
  1011. {
  1012. m_nMorale = 0;
  1013. return SCHED_CROW_FLY_AWAY;
  1014. }
  1015. if ( m_flEnemyDist > 400 )
  1016. {
  1017. return SCHED_CROW_WALK_AWAY;
  1018. }
  1019. else if ( m_flEnemyDist > 300 )
  1020. {
  1021. m_nMorale -= 1;
  1022. return SCHED_CROW_RUN_AWAY;
  1023. }
  1024. }
  1025. switch ( m_NPCState )
  1026. {
  1027. case NPC_STATE_IDLE:
  1028. case NPC_STATE_ALERT:
  1029. case NPC_STATE_COMBAT:
  1030. {
  1031. if ( !IsFlying() )
  1032. {
  1033. if ( m_bOnJeep == true )
  1034. return SCHED_IDLE_STAND;
  1035. //
  1036. // If we are hanging out on the ground, see if it is time to pick a new place to walk to.
  1037. //
  1038. if ( gpGlobals->curtime > m_flGroundIdleMoveTime )
  1039. {
  1040. m_flGroundIdleMoveTime = gpGlobals->curtime + random->RandomFloat( 10.0f, 20.0f );
  1041. return SCHED_CROW_IDLE_WALK;
  1042. }
  1043. return SCHED_IDLE_STAND;
  1044. }
  1045. // TODO: need idle flying behaviors!
  1046. }
  1047. }
  1048. return BaseClass::SelectSchedule();
  1049. }
  1050. //-----------------------------------------------------------------------------
  1051. // Purpose:
  1052. //-----------------------------------------------------------------------------
  1053. void CNPC_Crow::Precache( void )
  1054. {
  1055. BaseClass::Precache();
  1056. PrecacheModel( "models/crow.mdl" );
  1057. PrecacheModel( "models/pigeon.mdl" );
  1058. PrecacheModel( "models/seagull.mdl" );
  1059. //Crow
  1060. PrecacheScriptSound( "NPC_Crow.Hop" );
  1061. PrecacheScriptSound( "NPC_Crow.Squawk" );
  1062. PrecacheScriptSound( "NPC_Crow.Gib" );
  1063. PrecacheScriptSound( "NPC_Crow.Idle" );
  1064. PrecacheScriptSound( "NPC_Crow.Alert" );
  1065. PrecacheScriptSound( "NPC_Crow.Die" );
  1066. PrecacheScriptSound( "NPC_Crow.Pain" );
  1067. PrecacheScriptSound( "NPC_Crow.Flap" );
  1068. //Seagull
  1069. PrecacheScriptSound( "NPC_Seagull.Pain" );
  1070. PrecacheScriptSound( "NPC_Seagull.Idle" );
  1071. //Pigeon
  1072. PrecacheScriptSound( "NPC_Pigeon.Idle");
  1073. }
  1074. //-----------------------------------------------------------------------------
  1075. // Purpose: Sounds.
  1076. //-----------------------------------------------------------------------------
  1077. void CNPC_Crow::IdleSound( void )
  1078. {
  1079. if ( m_iBirdType != BIRDTYPE_CROW )
  1080. return;
  1081. EmitSound( "NPC_Crow.Idle" );
  1082. }
  1083. void CNPC_Crow::AlertSound( void )
  1084. {
  1085. if ( m_iBirdType != BIRDTYPE_CROW )
  1086. return;
  1087. EmitSound( "NPC_Crow.Alert" );
  1088. }
  1089. void CNPC_Crow::PainSound( const CTakeDamageInfo &info )
  1090. {
  1091. if ( m_iBirdType != BIRDTYPE_CROW )
  1092. return;
  1093. EmitSound( "NPC_Crow.Pain" );
  1094. }
  1095. void CNPC_Crow::DeathSound( const CTakeDamageInfo &info )
  1096. {
  1097. if ( m_iBirdType != BIRDTYPE_CROW )
  1098. return;
  1099. EmitSound( "NPC_Crow.Die" );
  1100. }
  1101. void CNPC_Crow::FlapSound( void )
  1102. {
  1103. EmitSound( "NPC_Crow.Flap" );
  1104. m_bPlayedLoopingSound = true;
  1105. }
  1106. //-----------------------------------------------------------------------------
  1107. // Purpose: This is a generic function (to be implemented by sub-classes) to
  1108. // handle specific interactions between different types of characters
  1109. // (For example the barnacle grabbing an NPC)
  1110. // Input : Constant for the type of interaction
  1111. // Output : true - if sub-class has a response for the interaction
  1112. // false - if sub-class has no response
  1113. //-----------------------------------------------------------------------------
  1114. bool CNPC_Crow::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt )
  1115. {
  1116. if ( interactionType == g_interactionBarnacleVictimDangle )
  1117. {
  1118. // Die instantly
  1119. return false;
  1120. }
  1121. else if ( interactionType == g_interactionBarnacleVictimGrab )
  1122. {
  1123. if ( GetFlags() & FL_ONGROUND )
  1124. {
  1125. SetGroundEntity( NULL );
  1126. }
  1127. // return ideal grab position
  1128. if (data)
  1129. {
  1130. // FIXME: need a good way to ensure this contract
  1131. *((Vector *)data) = GetAbsOrigin() + Vector( 0, 0, 5 );
  1132. }
  1133. StopLoopingSounds();
  1134. SetThink( NULL );
  1135. return true;
  1136. }
  1137. return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. // Purpose:
  1141. //-----------------------------------------------------------------------------
  1142. int CNPC_Crow::DrawDebugTextOverlays( void )
  1143. {
  1144. int nOffset = BaseClass::DrawDebugTextOverlays();
  1145. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1146. {
  1147. char tempstr[512];
  1148. Q_snprintf( tempstr, sizeof( tempstr ), "morale: %d", m_nMorale );
  1149. EntityText( nOffset, tempstr, 0 );
  1150. nOffset++;
  1151. if ( GetEnemy() != NULL )
  1152. {
  1153. Q_snprintf( tempstr, sizeof( tempstr ), "enemy (dist): %s (%g)", GetEnemy()->GetClassname(), ( double )m_flEnemyDist );
  1154. EntityText( nOffset, tempstr, 0 );
  1155. nOffset++;
  1156. }
  1157. }
  1158. return nOffset;
  1159. }
  1160. //-----------------------------------------------------------------------------
  1161. // Purpose: Determines which sounds the crow cares about.
  1162. //-----------------------------------------------------------------------------
  1163. int CNPC_Crow::GetSoundInterests( void )
  1164. {
  1165. return SOUND_WORLD | SOUND_COMBAT | SOUND_PLAYER | SOUND_DANGER;
  1166. }
  1167. //-----------------------------------------------------------------------------
  1168. //
  1169. // Schedules
  1170. //
  1171. //-----------------------------------------------------------------------------
  1172. AI_BEGIN_CUSTOM_NPC( npc_crow, CNPC_Crow )
  1173. DECLARE_TASK( TASK_CROW_FIND_FLYTO_NODE )
  1174. //DECLARE_TASK( TASK_CROW_PREPARE_TO_FLY )
  1175. DECLARE_TASK( TASK_CROW_TAKEOFF )
  1176. DECLARE_TASK( TASK_CROW_FLY )
  1177. DECLARE_TASK( TASK_CROW_PICK_RANDOM_GOAL )
  1178. DECLARE_TASK( TASK_CROW_HOP )
  1179. DECLARE_TASK( TASK_CROW_PICK_EVADE_GOAL )
  1180. DECLARE_TASK( TASK_CROW_WAIT_FOR_BARNACLE_KILL )
  1181. // experiment
  1182. DECLARE_TASK( TASK_CROW_FALL_TO_GROUND )
  1183. DECLARE_TASK( TASK_CROW_PREPARE_TO_FLY_RANDOM )
  1184. DECLARE_ACTIVITY( ACT_CROW_TAKEOFF )
  1185. DECLARE_ACTIVITY( ACT_CROW_SOAR )
  1186. DECLARE_ACTIVITY( ACT_CROW_LAND )
  1187. DECLARE_ANIMEVENT( AE_CROW_HOP )
  1188. DECLARE_ANIMEVENT( AE_CROW_FLY )
  1189. DECLARE_ANIMEVENT( AE_CROW_TAKEOFF )
  1190. DECLARE_CONDITION( COND_CROW_ENEMY_TOO_CLOSE )
  1191. DECLARE_CONDITION( COND_CROW_ENEMY_WAY_TOO_CLOSE )
  1192. DECLARE_CONDITION( COND_CROW_FORCED_FLY )
  1193. DECLARE_CONDITION( COND_CROW_BARNACLED )
  1194. //=========================================================
  1195. DEFINE_SCHEDULE
  1196. (
  1197. SCHED_CROW_IDLE_WALK,
  1198. " Tasks"
  1199. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_IDLE_STAND"
  1200. " TASK_CROW_PICK_RANDOM_GOAL 0"
  1201. " TASK_GET_PATH_TO_SAVEPOSITION 0"
  1202. " TASK_WALK_PATH 0"
  1203. " TASK_WAIT_FOR_MOVEMENT 0"
  1204. " TASK_WAIT_PVS 0"
  1205. " "
  1206. " Interrupts"
  1207. " COND_CROW_FORCED_FLY"
  1208. " COND_PROVOKED"
  1209. " COND_CROW_ENEMY_TOO_CLOSE"
  1210. " COND_NEW_ENEMY"
  1211. " COND_HEAVY_DAMAGE"
  1212. " COND_LIGHT_DAMAGE"
  1213. " COND_HEAVY_DAMAGE"
  1214. " COND_HEAR_DANGER"
  1215. " COND_HEAR_COMBAT"
  1216. )
  1217. //=========================================================
  1218. DEFINE_SCHEDULE
  1219. (
  1220. SCHED_CROW_WALK_AWAY,
  1221. " Tasks"
  1222. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
  1223. " TASK_CROW_PICK_EVADE_GOAL 0"
  1224. " TASK_GET_PATH_TO_SAVEPOSITION 0"
  1225. " TASK_WALK_PATH 0"
  1226. " TASK_WAIT_FOR_MOVEMENT 0"
  1227. " "
  1228. " Interrupts"
  1229. " COND_CROW_FORCED_FLY"
  1230. " COND_CROW_ENEMY_WAY_TOO_CLOSE"
  1231. " COND_NEW_ENEMY"
  1232. " COND_HEAVY_DAMAGE"
  1233. " COND_LIGHT_DAMAGE"
  1234. " COND_HEAVY_DAMAGE"
  1235. " COND_HEAR_DANGER"
  1236. " COND_HEAR_COMBAT"
  1237. )
  1238. //=========================================================
  1239. DEFINE_SCHEDULE
  1240. (
  1241. SCHED_CROW_RUN_AWAY,
  1242. " Tasks"
  1243. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
  1244. " TASK_CROW_PICK_EVADE_GOAL 0"
  1245. " TASK_GET_PATH_TO_SAVEPOSITION 0"
  1246. " TASK_RUN_PATH 0"
  1247. " TASK_WAIT_FOR_MOVEMENT 0"
  1248. " "
  1249. " Interrupts"
  1250. " COND_CROW_FORCED_FLY"
  1251. " COND_CROW_ENEMY_WAY_TOO_CLOSE"
  1252. " COND_NEW_ENEMY"
  1253. " COND_HEAVY_DAMAGE"
  1254. " COND_LIGHT_DAMAGE"
  1255. " COND_HEAVY_DAMAGE"
  1256. " COND_HEAR_DANGER"
  1257. " COND_HEAR_COMBAT"
  1258. )
  1259. //=========================================================
  1260. DEFINE_SCHEDULE
  1261. (
  1262. SCHED_CROW_HOP_AWAY,
  1263. " Tasks"
  1264. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_AWAY"
  1265. " TASK_STOP_MOVING 0"
  1266. " TASK_CROW_PICK_EVADE_GOAL 0"
  1267. " TASK_FACE_IDEAL 0"
  1268. " TASK_CROW_HOP 0"
  1269. " "
  1270. " Interrupts"
  1271. " COND_CROW_FORCED_FLY"
  1272. " COND_HEAVY_DAMAGE"
  1273. " COND_LIGHT_DAMAGE"
  1274. " COND_HEAVY_DAMAGE"
  1275. " COND_HEAR_DANGER"
  1276. " COND_HEAR_COMBAT"
  1277. )
  1278. //=========================================================
  1279. DEFINE_SCHEDULE
  1280. (
  1281. SCHED_CROW_IDLE_FLY,
  1282. " Tasks"
  1283. " TASK_FIND_HINTNODE 0"
  1284. " TASK_GET_PATH_TO_HINTNODE 0"
  1285. " TASK_WAIT_FOR_MOVEMENT 0"
  1286. " "
  1287. " Interrupts"
  1288. )
  1289. //=========================================================
  1290. DEFINE_SCHEDULE
  1291. (
  1292. SCHED_CROW_FLY_AWAY,
  1293. " Tasks"
  1294. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_FAIL"
  1295. " TASK_STOP_MOVING 0"
  1296. " TASK_FIND_HINTNODE 0"
  1297. " TASK_GET_PATH_TO_HINTNODE 0"
  1298. " TASK_CROW_TAKEOFF 0"
  1299. " TASK_WAIT_FOR_MOVEMENT 0"
  1300. " "
  1301. " Interrupts"
  1302. )
  1303. //=========================================================
  1304. DEFINE_SCHEDULE
  1305. (
  1306. SCHED_CROW_FLY,
  1307. " Tasks"
  1308. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CROW_FLY_FAIL"
  1309. " TASK_STOP_MOVING 0"
  1310. " TASK_CROW_TAKEOFF 0"
  1311. " TASK_CROW_FLY 0"
  1312. " "
  1313. " Interrupts"
  1314. )
  1315. //=========================================================
  1316. DEFINE_SCHEDULE
  1317. (
  1318. SCHED_CROW_FLY_FAIL,
  1319. " Tasks"
  1320. " TASK_CROW_FALL_TO_GROUND 0"
  1321. " TASK_SET_SCHEDULE SCHEDULE:SCHED_CROW_IDLE_WALK"
  1322. " "
  1323. " Interrupts"
  1324. )
  1325. //=========================================================
  1326. // Crow is in the clutches of a barnacle
  1327. DEFINE_SCHEDULE
  1328. (
  1329. SCHED_CROW_BARNACLED,
  1330. " Tasks"
  1331. " TASK_STOP_MOVING 0"
  1332. " TASK_SET_ACTIVITY ACTIVITY:ACT_HOP"
  1333. " TASK_CROW_WAIT_FOR_BARNACLE_KILL 0"
  1334. " Interrupts"
  1335. )
  1336. AI_END_CUSTOM_NPC()