Counter Strike : Global Offensive Source Code
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.

1283 lines
30 KiB

  1. // chicken.cpp
  2. // An interactive, shootable chicken
  3. #include "cbase.h"
  4. #include "chicken.h"
  5. #include "cs_player.h"
  6. #include "cs_gamerules.h"
  7. #include "particle_parse.h"
  8. #include "engine/IEngineSound.h"
  9. #include "cs_simple_hostage.h"
  10. #include "cs_player_resource.h"
  11. // NOTE: This has to be the last file included!
  12. #include "tier0/memdbgon.h"
  13. BEGIN_DATADESC( CChicken )
  14. DEFINE_ENTITYFUNC( ChickenTouch ),
  15. DEFINE_THINKFUNC( ChickenThink ),
  16. DEFINE_USEFUNC( ChickenUse ),
  17. END_DATADESC()
  18. IMPLEMENT_SERVERCLASS_ST( CChicken, DT_CChicken )
  19. SendPropBool( SENDINFO( m_jumpedThisFrame ) ),
  20. SendPropEHandle( SENDINFO( m_leader ) ),
  21. END_SEND_TABLE()
  22. LINK_ENTITY_TO_CLASS( chicken, CChicken );
  23. PRECACHE_REGISTER( chicken );
  24. class HostagePathCost;
  25. extern ConVar hostage_debug;
  26. #define CHICKEN_ZOMBIE_SPAWN_DURATION 3.5f
  27. #define CHICKEN_DISTANCE_RUN 200.0f
  28. #define CHICKEN_DISTANCE_WALK 100.0f
  29. //-----------------------------------------------------------------------
  30. CChicken::CChicken()
  31. {
  32. }
  33. //-----------------------------------------------------------------------
  34. CChicken::~CChicken()
  35. {
  36. }
  37. //-----------------------------------------------------------------------
  38. void CChicken::Precache( void )
  39. {
  40. SetModelName( MAKE_STRING( "models/chicken/chicken.mdl" ) ); // prop precache wants this
  41. BaseClass::Precache();
  42. PrecacheModel( "models/chicken/chicken.mdl" );
  43. PrecacheModel( "models/chicken/chicken_zombie.mdl" );
  44. PrecacheModel( "models/chicken/chicken_gone.mdl" );
  45. PrecacheModel( "models/antlers/antlers.mdl" );
  46. PrecacheScriptSound( "Chicken.Idle" );
  47. PrecacheScriptSound( "Chicken.Panic" );
  48. PrecacheScriptSound( "Chicken.Fly" );
  49. PrecacheScriptSound( "Chicken.FlapWings" );
  50. PrecacheScriptSound( "Chicken.Death" );
  51. PrecacheScriptSound( "Chicken.ZombieRez" );
  52. PrecacheParticleSystem( "weapon_confetti_omni" );
  53. PrecacheParticleSystem( "chicken_rez" );
  54. PrecacheParticleSystem( "chicken_gone_crumble_halloween" );
  55. PrecacheParticleSystem( "chicken_gone_zombie" );
  56. PrecacheParticleSystem( "impact_helmet_headshot" );
  57. }
  58. //-----------------------------------------------------------------------
  59. void CChicken::Spawn( void )
  60. {
  61. SetModel( "models/chicken/chicken.mdl" );
  62. BaseClass::Spawn();
  63. SetNextThink( gpGlobals->curtime );
  64. SetThink( &CChicken::ChickenThink );
  65. SetTouch( &CChicken::ChickenTouch );
  66. SetUse( &CChicken::ChickenUse );
  67. SetSolid( SOLID_BBOX );
  68. SetMoveType( MOVETYPE_FLYGRAVITY );
  69. SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
  70. const model_t *pModel = modelinfo->GetModel( GetModelIndex() );
  71. if ( pModel )
  72. {
  73. Vector mins, maxs;
  74. modelinfo->GetModelBounds( pModel, mins, maxs );
  75. mins.z = 0.0f;
  76. SetCollisionBounds( mins, maxs );
  77. }
  78. SetGravity( 1.0 );
  79. SetHealth( 1 );
  80. SetMaxHealth( 1 );
  81. m_takedamage = DAMAGE_YES;
  82. Idle();
  83. m_fleeFrom = NULL;
  84. m_updateTimer.Invalidate();
  85. m_reuseTimer.Invalidate( );
  86. m_moveRateThrottleTimer.Invalidate( );
  87. m_stuckAnchor = GetAbsOrigin();
  88. m_stuckTimer.Start( 1.0f );
  89. m_isOnGround = false;
  90. m_startleTimer.Invalidate();
  91. ListenForGameEvent( "weapon_fire" );
  92. //ListenForGameEvent( "bullet_impact" );
  93. if ( CSGameRules() && CSGameRules()->IsCSGOBirthday() )
  94. {
  95. SetBodygroup( 1, 1 ); // birthday hat
  96. }
  97. m_leader = INVALID_EHANDLE;
  98. m_reuseTimer.Invalidate( );
  99. m_hasBeenUsed = false;
  100. m_jumpedThisFrame = false;
  101. m_path.Invalidate( );
  102. m_repathTimer.Invalidate( );
  103. m_pathFollower.Reset( );
  104. m_pathFollower.SetPath( &m_path );
  105. m_pathFollower.SetImprov( this );
  106. m_lastKnownArea = NULL;
  107. // Need to make sure the hostages are on the ground when they spawn
  108. // Vector GroundPos = DropToGround( this, GetAbsOrigin( ), HOSTAGE_BBOX_VEC_MIN, HOSTAGE_BBOX_VEC_MAX );
  109. // SetAbsOrigin( GroundPos );
  110. m_bInJump = false;
  111. m_flLastJumpTime = 0;
  112. m_inhibitObstacleAvoidanceTimer.Invalidate( );
  113. m_isWaitingForLeader = false;
  114. m_lastLeaderID = 0;
  115. m_flActiveFollowStartTime = 0;
  116. }
  117. //-----------------------------------------------------------------------
  118. void CChicken::ChickenTouch( CBaseEntity *pOther )
  119. {
  120. if ( !CSGameRules() || CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() )
  121. {
  122. Flee( pOther, RandomFloat( 2.0f, 3.0f ) );
  123. }
  124. }
  125. bool CChicken::IsFollowingSomeone( void )
  126. {
  127. return ( m_leader.m_Value != NULL );
  128. }
  129. //-----------------------------------------------------------------------------------------------------
  130. bool CChicken::IsFollowing( const CBaseEntity *entity )
  131. {
  132. return ( m_leader.m_Value == entity );
  133. }
  134. //-----------------------------------------------------------------------------------------------------
  135. bool CChicken::IsOnGround( void ) const
  136. {
  137. return ( GetFlags( ) & FL_ONGROUND );
  138. }
  139. //-----------------------------------------------------------------------------------------------------
  140. /**
  141. * Begin following "leader"
  142. */
  143. void CChicken::Follow( CCSPlayer *leader )
  144. {
  145. m_lastLeaderID = 0;
  146. if ( leader )
  147. {
  148. leader->IncrementNumFollowers( );
  149. m_lastLeaderID = leader->GetUserID( );
  150. m_leader = leader;
  151. }
  152. else
  153. {
  154. m_leader = INVALID_EHANDLE;
  155. }
  156. m_leader = leader;
  157. m_isWaitingForLeader = false;
  158. m_flActiveFollowStartTime = gpGlobals->curtime;
  159. m_moveRateThrottleTimer.Start( 1.0f);
  160. }
  161. //-----------------------------------------------------------------------------------------------------
  162. /**
  163. * Return our leader, or NULL
  164. */
  165. CCSPlayer *CChicken::GetLeader( void ) const
  166. {
  167. return ToCSPlayer( m_leader.m_Value );
  168. }
  169. //-----------------------------------------------------------------------------------------------------
  170. /**
  171. * Invoked when a chicken is "used" by a player
  172. */
  173. void CChicken::ChickenUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  174. {
  175. CCSPlayer *pPlayer = ToCSPlayer( pActivator );
  176. if ( !pPlayer )
  177. return;
  178. // limit use range
  179. float useRange = 1000.0f;
  180. Vector to = pActivator->GetAbsOrigin( ) - GetAbsOrigin( );
  181. if ( to.IsLengthGreaterThan( useRange ) )
  182. {
  183. return;
  184. }
  185. SetChickenStartFollowingPlayer( pPlayer );
  186. }
  187. void CChicken::SetChickenStartFollowingPlayer( CCSPlayer *pPlayer )
  188. {
  189. // throttle how often leader can change
  190. if ( !m_reuseTimer.IsElapsed( ) )
  191. {
  192. return;
  193. }
  194. // if we are already following the player who used us, stop following
  195. if ( IsFollowing( pPlayer ) )
  196. {
  197. Follow( NULL );
  198. Idle( );
  199. EmitSound( "Chicken.Idle" );
  200. }
  201. else
  202. {
  203. // if we're already following a CT, ignore new uses
  204. if ( IsFollowingSomeone( ) )
  205. {
  206. return;
  207. }
  208. // start following
  209. Follow( pPlayer );
  210. EmitSound( "Chicken.FlapWings" );
  211. Jump( 50.0f );
  212. }
  213. m_reuseTimer.Start( 1.0f );
  214. }
  215. //--------------------------------------------------------------------------------------------------------------
  216. /**
  217. * Rotate body to face towards "target"
  218. */
  219. void CChicken::FaceTowards( const Vector &target, float deltaT )
  220. {
  221. Vector to = target - GetFeet( );
  222. to.z = 0.0f;
  223. QAngle desiredAngles;
  224. VectorAngles( to, desiredAngles );
  225. QAngle angles = GetAbsAngles( );
  226. // The animstate system for hostages will smooth out the transition to this direction, so no need to double smooth it here.
  227. angles.y = desiredAngles.y;
  228. SetAbsAngles( angles );
  229. }
  230. int CChicken::OnTakeDamage( const CTakeDamageInfo &info )
  231. {
  232. /*
  233. if ( EconHolidays_IsHolidayActive( kHoliday_Halloween ) )
  234. {
  235. // if we recently turned into a zombie, ignore damage for CHICKEN_ZOMBIE_SPAWN_DURATION seconds.
  236. if ( IsZombie() )
  237. {
  238. if ( (gpGlobals->curtime - m_flWhenZombified) > CHICKEN_ZOMBIE_SPAWN_DURATION )
  239. {
  240. return BaseClass::OnTakeDamage( info );
  241. }
  242. else
  243. {
  244. return 0;
  245. }
  246. }
  247. else
  248. {
  249. if ( m_activity != ACT_GLIDE && m_isOnGround )
  250. {
  251. //when there's no more room in chicken-hell... turn into a chicken zombie!
  252. Zombify();
  253. SetModel( "models/chicken/chicken_zombie.mdl" );
  254. m_activity = ACT_CLIMB_UP;
  255. ResetSequence( LookupSequence( "spawn" ) );
  256. ResetSequenceInfo();
  257. ForceCycle( 0 );
  258. DispatchParticleEffect( "chicken_gone_crumble_halloween", GetAbsOrigin() + Vector( 0, 0, 8 ), QAngle( 0, 0, 0 ) );
  259. CTraceFilterNoNPCsOrPlayer traceFilter( this, COLLISION_GROUP_NONE );
  260. trace_t tr;
  261. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -16 ), MASK_PLAYERSOLID, &traceFilter, &tr );
  262. Vector vecPos, vecVel, vecNormal;
  263. QAngle angNormal;
  264. vecNormal = tr.DidHit() ? tr.plane.normal : Vector( 0, 0, 0 );
  265. //vecNormal.y *= -1;
  266. //vecNormal.z *= -1;
  267. VectorAngles( vecNormal, angNormal );
  268. angNormal.x += 90;
  269. DispatchParticleEffect( "chicken_rez", GetAbsOrigin(), angNormal );
  270. CSoundParameters params;
  271. if ( GetParametersForSound( "Chicken.ZombieRez", params, NULL ) )
  272. {
  273. EmitSound_t ep( params );
  274. ep.m_pOrigin = &GetAbsOrigin();
  275. CBroadcastRecipientFilter filter;
  276. EmitSound( filter, SOUND_FROM_WORLD, ep );
  277. }
  278. SetSolid( SOLID_NONE );
  279. return 0;
  280. }
  281. else
  282. {
  283. return BaseClass::OnTakeDamage( info );
  284. }
  285. }
  286. }
  287. else
  288. */
  289. {
  290. return BaseClass::OnTakeDamage( info );
  291. }
  292. }
  293. //-----------------------------------------------------------------------
  294. void CChicken::Event_Killed( const CTakeDamageInfo &info )
  295. {
  296. EmitSound( "Chicken.Death" );
  297. if ( CSGameRules() && CSGameRules()->IsCSGOBirthday() )
  298. DispatchParticleEffect( "weapon_confetti_omni", GetAbsOrigin(), QAngle( 0, 0, 0 ) );
  299. if ( IsZombie() )
  300. DispatchParticleEffect( "chicken_gone_zombie", GetAbsOrigin() + Vector( 0, 0, 8 ), QAngle( 0, 0, 0 ) );
  301. if ( IsFollowingSomeone( ) )
  302. {
  303. CSingleUserRecipientFilter leaderfilter( ToCSPlayer( m_leader.m_Value ) );
  304. leaderfilter.MakeReliable( );
  305. float flFollowDuration = gpGlobals->curtime - m_flActiveFollowStartTime;
  306. UTIL_ClientPrintFilter( leaderfilter, HUD_PRINTTALK, "#Pet_Killed", CFmtStr( "%.0f", flFollowDuration ).Get() );
  307. }
  308. BaseClass::Event_Killed( info );
  309. }
  310. //-----------------------------------------------------------------------
  311. void CChicken::FireGameEvent( IGameEvent *event )
  312. {
  313. // all the events we care about scare us
  314. if ( m_startleTimer.HasStarted() )
  315. {
  316. // we're already scared
  317. return;
  318. }
  319. if ( event->GetBool( "silenced" ) )
  320. {
  321. // Silenced weapons don't scare chickens
  322. return;
  323. }
  324. CBasePlayer *player = UTIL_PlayerByUserId( event->GetInt( "userid" ) );
  325. if ( player )
  326. {
  327. const float fleeGunfireRange = 1000.0f;
  328. Vector toPlayer = player->GetAbsOrigin() - GetAbsOrigin();
  329. if ( toPlayer.IsLengthLessThan( fleeGunfireRange ) )
  330. {
  331. m_startleTimer.Start( RandomFloat( 0.1f, 0.5f ) );
  332. m_fleeFrom = player;
  333. }
  334. }
  335. }
  336. //-----------------------------------------------------------------------
  337. void CChicken::Idle()
  338. {
  339. // m_leader = NULL;
  340. m_activity = ACT_IDLE;
  341. m_activityTimer.Start( RandomFloat( 0.5, 3.0f ) );
  342. SetSequence( SelectWeightedSequence( m_activity ) );
  343. ResetSequenceInfo();
  344. m_pathFollower.ResetStuck( );
  345. }
  346. //-----------------------------------------------------------------------
  347. void CChicken::Walk()
  348. {
  349. m_activity = ACT_WALK;
  350. m_activityTimer.Start( RandomFloat( 0.5, 3.0f ) );
  351. m_turnRate = RandomFloat( -45.0f, 45.0f );
  352. SetSequence( SelectWeightedSequence( m_activity ) );
  353. ResetSequenceInfo();
  354. }
  355. //-----------------------------------------------------------------------
  356. void CChicken::Flee( CBaseEntity *fleeFrom, float duration )
  357. {
  358. if ( m_activity != ACT_RUN || m_vocalizeTimer.IsElapsed() )
  359. {
  360. // throttle interval between vocalizations
  361. m_vocalizeTimer.Start( RandomFloat( 0.5f, 1.0f ) );
  362. EmitSound( "Chicken.Panic" );
  363. }
  364. m_activity = ACT_RUN;
  365. m_activityTimer.Start( duration );
  366. m_turnRate = 0.0f;
  367. m_fleeFrom = fleeFrom;
  368. SetSequence( SelectWeightedSequence( m_activity ) );
  369. ResetSequenceInfo();
  370. }
  371. //-----------------------------------------------------------------------
  372. void CChicken::Fly()
  373. {
  374. if ( m_activity != ACT_GLIDE || m_vocalizeTimer.IsElapsed() )
  375. {
  376. // throttle interval between vocalizations
  377. m_vocalizeTimer.Start( RandomFloat( 0.5f, 1.0f ) );
  378. EmitSound( "Chicken.Fly" );
  379. }
  380. m_activity = ACT_GLIDE;
  381. m_turnRate = 0.0f;
  382. SetSequence( SelectWeightedSequence( m_activity ) );
  383. ResetSequenceInfo();
  384. }
  385. //-----------------------------------------------------------------------
  386. void CChicken::Land()
  387. {
  388. if ( m_activity != ACT_LAND || m_vocalizeTimer.IsElapsed() )
  389. {
  390. // throttle interval between vocalizations
  391. m_vocalizeTimer.Start( RandomFloat( 0.5f, 1.0f ) );
  392. EmitSound( "Chicken.Idle" );
  393. }
  394. m_activity = ACT_LAND;
  395. m_turnRate = 0.0f;
  396. SetSequence( SelectWeightedSequence( m_activity ) );
  397. ResetSequenceInfo();
  398. }
  399. //-----------------------------------------------------------------------
  400. void CChicken::Update( void )
  401. {
  402. if ( GetLeader( ) )
  403. return;
  404. if ( !m_updateTimer.IsElapsed() )
  405. return;
  406. m_updateTimer.Start( RandomFloat( 0.5f, 1.0f ) );
  407. // find closest visible player
  408. CUtlVector< CBasePlayer * > playerVector;
  409. CollectPlayers( &playerVector, TEAM_ANY, COLLECT_ONLY_LIVING_PLAYERS );
  410. float closeRangeSq = FLT_MAX;
  411. CBasePlayer *close = NULL;
  412. for( int i=0; i<playerVector.Count(); ++i )
  413. {
  414. Vector toPlayer = playerVector[i]->GetAbsOrigin() - GetAbsOrigin();
  415. float rangeSq = toPlayer.LengthSqr();
  416. // Only scare chickens if we're close
  417. if ( rangeSq < closeRangeSq )
  418. {
  419. // Only scare chickens if we're moving fast enough to make noise
  420. Vector vPlayerVelocity;
  421. playerVector[i]->GetVelocity( &vPlayerVelocity, NULL );
  422. if ( vPlayerVelocity.Length() > 126.0f )
  423. {
  424. if ( playerVector[i]->IsLineOfSightClear( this ) )
  425. {
  426. closeRangeSq = rangeSq;
  427. close = playerVector[i];
  428. }
  429. }
  430. }
  431. }
  432. const float tooClose = 200.0f;
  433. if ( close && closeRangeSq < tooClose * tooClose )
  434. {
  435. Flee( close, RandomFloat( 0.5, 1.0f ) );
  436. }
  437. }
  438. //-----------------------------------------------------------------------
  439. float CChicken::AvoidObstacles( void )
  440. {
  441. const float feelerRange = 15.0f;
  442. const Vector &forward = Forward();
  443. Vector left( -forward.y, forward.x, 0.0f );
  444. Vector right( forward.y, -forward.x, 0.0f );
  445. CTraceFilterNoNPCsOrPlayer traceFilter( this, COLLISION_GROUP_NONE );
  446. trace_t resultLeft;
  447. UTIL_TraceLine( WorldSpaceCenter(), WorldSpaceCenter() + feelerRange * ( forward + left ), MASK_PLAYERSOLID, &traceFilter, &resultLeft );
  448. //NDebugOverlay::Line( WorldSpaceCenter(), WorldSpaceCenter() + feelerRange * ( forward + left ), 0, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  449. trace_t resultRight;
  450. UTIL_TraceLine( WorldSpaceCenter(), WorldSpaceCenter() + feelerRange * ( forward + right ), MASK_PLAYERSOLID, &traceFilter, &resultRight );
  451. //NDebugOverlay::Line( WorldSpaceCenter(), WorldSpaceCenter() + feelerRange * ( forward + right ), 255, 0, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  452. const float maxTurnRate = 360.0f;
  453. float turnRate = 0.0f;
  454. if ( resultLeft.DidHit() )
  455. {
  456. if ( resultRight.DidHit() )
  457. {
  458. // both sides hit
  459. if ( resultLeft.fraction < resultRight.fraction )
  460. {
  461. // left hit closer - turn right
  462. turnRate = -maxTurnRate;
  463. }
  464. else
  465. {
  466. // right hit closer - turn left
  467. turnRate = maxTurnRate;
  468. }
  469. }
  470. else
  471. {
  472. // left hit - turn right
  473. turnRate = -maxTurnRate;
  474. }
  475. }
  476. else if ( resultRight.DidHit() )
  477. {
  478. // right hit - turn left
  479. turnRate = maxTurnRate;
  480. }
  481. Vector toAnchor = m_stuckAnchor - GetAbsOrigin();
  482. if ( toAnchor.IsLengthGreaterThan( 50.0f ) )
  483. {
  484. m_stuckAnchor = GetAbsOrigin();
  485. m_stuckTimer.Reset();
  486. }
  487. return turnRate;
  488. }
  489. //-----------------------------------------------------------------------
  490. void CChicken::ResolveCollisions( const Vector &desiredPosition, float deltaT )
  491. {
  492. CTraceFilterNoNPCsOrPlayer traceFilter( this, COLLISION_GROUP_NONE );
  493. trace_t result;
  494. const float stepHeight = WorldAlignSize().z / 2.0f;
  495. // try to do full move
  496. Vector testPosition = desiredPosition;
  497. for( int slideCount=0; slideCount<3; ++slideCount )
  498. {
  499. UTIL_TraceHull( GetAbsOrigin(), testPosition, WorldAlignMins() + Vector( 0, 0, stepHeight ), WorldAlignMaxs(), MASK_PLAYERSOLID, &traceFilter, &result );
  500. if ( !result.DidHit() )
  501. {
  502. break;
  503. }
  504. Vector fullMove = testPosition - GetAbsOrigin();
  505. float blocked = DotProduct( fullMove, result.plane.normal );
  506. testPosition = GetAbsOrigin() + fullMove - blocked * result.plane.normal;
  507. }
  508. Vector resolvedPosition = result.endpos;
  509. // snap to ground (or fall)
  510. UTIL_TraceHull( resolvedPosition + Vector( 0, 0, stepHeight ), resolvedPosition + Vector( 0, 0, -stepHeight ), WorldAlignMins(), WorldAlignMaxs(), MASK_PLAYERSOLID, &traceFilter, &result );
  511. if ( result.DidHit( ) && !IsJumping( ) )
  512. {
  513. // limit slope that can be walked up
  514. const float slopeLimit = 0.7071f;
  515. if ( !result.plane.normal.IsZero() && result.plane.normal.z > slopeLimit )
  516. {
  517. SetAbsOrigin( result.endpos );
  518. }
  519. else
  520. {
  521. SetAbsOrigin( resolvedPosition );
  522. }
  523. if ( !m_isOnGround )
  524. {
  525. Land();
  526. m_isOnGround = true;
  527. m_bInJump = false;
  528. }
  529. }
  530. else
  531. {
  532. SetAbsOrigin( resolvedPosition );
  533. // fall
  534. SetBaseVelocity( GetBaseVelocity() + Vector( 0, 0, GetGravity() * deltaT ) );
  535. m_isOnGround = false;
  536. Fly();
  537. }
  538. }
  539. void CChicken::UpdateFollowing( float deltaT )
  540. {
  541. if ( !IsFollowingSomeone( ) && m_lastLeaderID != 0 )
  542. {
  543. m_lastLeaderID = 0;
  544. }
  545. // if we have a leader, follow him
  546. CCSPlayer *leader = GetLeader( );
  547. if ( leader )
  548. {
  549. // if leader is dead, stop following him
  550. if ( !leader->IsAlive( ) )
  551. {
  552. Follow( NULL );
  553. Idle( );
  554. return;
  555. }
  556. // m_nHostageState = k_EHostageStates_FollowingPlayer;
  557. // if leader has moved, repath
  558. if ( m_path.IsValid( ) )
  559. {
  560. Vector pathError = leader->GetAbsOrigin( ) - m_path.GetEndpoint( );
  561. const float repathRange = 100.0f;
  562. if ( pathError.IsLengthGreaterThan( repathRange ) )
  563. {
  564. m_path.Invalidate( );
  565. }
  566. // m_activity = ACT_WALK;
  567. }
  568. // build a path to our leader
  569. if ( !m_path.IsValid( ) && m_repathTimer.IsElapsed( ) )
  570. {
  571. const float repathInterval = 0.5f;
  572. m_repathTimer.Start( repathInterval );
  573. Vector from = GetAbsOrigin( );
  574. Vector to = leader->GetAbsOrigin( );
  575. HostagePathCost pathCost;
  576. m_path.Compute( from, to, pathCost );
  577. m_pathFollower.Reset( );
  578. }
  579. // if our rescuer is too far away, give up
  580. const float giveUpRange = 2000.0f;
  581. const float maxPathLength = 4000.0f;
  582. Vector toLeader = leader->GetAbsOrigin( ) - GetAbsOrigin( );
  583. if ( toLeader.IsLengthGreaterThan( giveUpRange ) || ( m_path.IsValid( ) && m_path.GetLength( ) > maxPathLength ) )
  584. {
  585. if ( hostage_debug.GetInt( ) < 2 )
  586. {
  587. Idle( );
  588. }
  589. return;
  590. }
  591. // don't crowd the leader
  592. if ( m_isWaitingForLeader )
  593. {
  594. // we are close to our leader and waiting for him to move
  595. const float waitRange = 200.0f;
  596. if ( toLeader.IsLengthGreaterThan( waitRange ) )
  597. {
  598. // leader has moved away - follow him
  599. m_isWaitingForLeader = false;
  600. }
  601. }
  602. if ( !m_isWaitingForLeader )
  603. {
  604. // move along path towards the leader
  605. m_pathFollower.Update( deltaT, m_inhibitObstacleAvoidanceTimer.IsElapsed( ) );
  606. if ( hostage_debug.GetBool( ) )
  607. {
  608. m_pathFollower.Debug( true );
  609. }
  610. float flDist = GetFeet( ).DistTo( ToCSPlayer( m_leader.m_Value )->GetAbsOrigin( ) );
  611. const float minStuckJumpTime = 1.0f;
  612. if ( ( m_pathFollower.GetStuckDuration( ) > minStuckJumpTime ) && ( flDist > CHICKEN_DISTANCE_RUN * 2 ) )
  613. {
  614. Jump( );
  615. }
  616. if ( hostage_debug.GetBool( ) )
  617. {
  618. m_path.Draw( );
  619. }
  620. }
  621. }
  622. }
  623. //-----------------------------------------------------------------------
  624. void CChicken::ChickenThink( void )
  625. {
  626. float deltaT = gpGlobals->frametime;
  627. SetNextThink( gpGlobals->curtime + deltaT );
  628. // if we've been a zombie for less than CHICKEN_ZOMBIE_SPAWN_DURATION seconds
  629. float flZombieDuration = (gpGlobals->curtime - m_flWhenZombified);
  630. if ( IsZombie() && flZombieDuration < CHICKEN_ZOMBIE_SPAWN_DURATION )
  631. {
  632. m_activity = ACT_CLIMB_UP;
  633. SetSequence( LookupSequence( "spawn" ) );
  634. SetCycle( flZombieDuration / CHICKEN_ZOMBIE_SPAWN_DURATION );
  635. m_activityTimer.Start( 0 );
  636. SetSolid( SOLID_NONE );
  637. }
  638. else if ( IsZombie() && (m_flWhenZombified + 1.0f) > (gpGlobals->curtime - CHICKEN_ZOMBIE_SPAWN_DURATION) )
  639. {
  640. SetSolid( SOLID_BBOX );
  641. m_activity = ACT_WALK;
  642. }
  643. // do leader-following behavior, if necessary
  644. UpdateFollowing( deltaT );
  645. Update();
  646. if ( m_startleTimer.HasStarted() && m_startleTimer.IsElapsed() )
  647. {
  648. // we were startled by something and have just noticed it
  649. m_startleTimer.Invalidate();
  650. Flee( m_fleeFrom, RandomFloat( 2.0f, 3.0f ) );
  651. }
  652. if ( IsActivityFinished() && !IsFollowingSomeone() )
  653. {
  654. switch ( ( int ) m_activity )
  655. {
  656. case ACT_IDLE:
  657. if ( RandomInt( 1, 100 ) < 30 )
  658. {
  659. Walk( );
  660. }
  661. break;
  662. case ACT_WALK:
  663. if ( m_activityTimer.IsElapsed( ) )
  664. {
  665. Idle( );
  666. }
  667. break;
  668. case ACT_RUN:
  669. if ( m_activityTimer.IsElapsed( ) )
  670. {
  671. Walk( );
  672. }
  673. break;
  674. case ACT_GLIDE:
  675. Fly( );
  676. break;
  677. case ACT_LAND:
  678. Walk( );
  679. break;
  680. }
  681. }
  682. else// ongoing activity
  683. {
  684. if ( IsFollowingSomeone( ) )
  685. {
  686. if ( m_moveRateThrottleTimer.IsElapsed( ) )
  687. {
  688. float flDist = GetFeet( ).DistTo( ToCSPlayer( m_leader.m_Value )->GetAbsOrigin( ) );
  689. if ( flDist < ( CHICKEN_DISTANCE_WALK * 0.9f ) )
  690. {
  691. if ( m_activity != ACT_IDLE )
  692. Idle( );
  693. }
  694. else if ( flDist > ( CHICKEN_DISTANCE_WALK * 1.1f ) && flDist < ( CHICKEN_DISTANCE_RUN * 0.9f ) )
  695. {
  696. if ( m_activity != ACT_WALK )
  697. Walk( );
  698. }
  699. if ( flDist > ( CHICKEN_DISTANCE_RUN * 1.1f ) )
  700. {
  701. if ( m_activity != ACT_RUN )
  702. Run( );
  703. }
  704. m_moveRateThrottleTimer.Start( RandomFloat( 0.0f, 0.5f ) );
  705. }
  706. }
  707. if ( m_activity == ACT_IDLE || m_activity == ACT_WALK )
  708. {
  709. // sound like a calm chicken
  710. if ( m_vocalizeTimer.IsElapsed() )
  711. {
  712. m_vocalizeTimer.Start( RandomFloat( 1.5f, 4.5f ) );
  713. EmitSound( "Chicken.Idle" );
  714. }
  715. }
  716. // don't walk/run in a straight line - turn a bit
  717. switch ( ( int ) m_activity )
  718. {
  719. case ACT_WALK:
  720. case ACT_RUN:
  721. QAngle angle = GetAbsAngles( );
  722. if ( m_fleeFrom != NULL )
  723. {
  724. Vector fleeVector = GetAbsOrigin( ) - m_fleeFrom->GetAbsOrigin( );
  725. fleeVector.z = 0.0f;
  726. if ( IsZombie( ) )
  727. fleeVector = -fleeVector; //zombie chickens flee TOWARDS players
  728. float fleeRange = fleeVector.NormalizeInPlace( );
  729. const float safeRange = 150.0f;
  730. if ( fleeRange > safeRange )
  731. {
  732. m_fleeFrom = NULL;
  733. }
  734. else
  735. {
  736. m_activityTimer.Reset( );
  737. if ( DotProduct( fleeVector, Forward( ) ) < 0.7071f )
  738. {
  739. // turn away
  740. m_turnRate = 360.0f;
  741. if ( CrossProduct( fleeVector, Forward( ) ).z > 0.0f )
  742. {
  743. m_turnRate = -m_turnRate;
  744. }
  745. }
  746. else
  747. {
  748. m_turnRate = 0.0f;
  749. }
  750. }
  751. float obstacleTurnRate = AvoidObstacles( );
  752. float actualTurnRate = ( obstacleTurnRate == 0.0f ) ? m_turnRate : obstacleTurnRate;
  753. angle.y += actualTurnRate * deltaT;
  754. SetAbsAngles( angle );
  755. }
  756. else if ( IsFollowingSomeone( ) )
  757. {
  758. Vector followVector = {0,0,0};
  759. m_activityTimer.Reset( );
  760. CTraceFilterNoNPCsOrPlayer traceFilter( this, COLLISION_GROUP_NONE );
  761. trace_t tr;
  762. UTIL_TraceLine( GetEyes( ), ToCSPlayer( m_leader.m_Value )->GetAbsOrigin( ), MASK_PLAYERSOLID, &traceFilter, &tr );
  763. if ( tr.fraction == 1.0 ) // chicken can see player so target player
  764. {
  765. followVector = ToCSPlayer( m_leader.m_Value )->GetAbsOrigin( ) - GetAbsOrigin( );
  766. }
  767. else// chicken cannot see player so target next track node
  768. {
  769. followVector = m_vecPathGoal - GetAbsOrigin( );
  770. }
  771. float followRange = followVector.NormalizeInPlace( );
  772. // we use an softer angle threshold at greater distances. This makes the chicken seem more organic.
  773. float flAngleTheshold = RemapValClamped( followRange, 300, 2000, 0.96, 0.707 );
  774. if ( DotProduct( followVector, Forward( ) ) < flAngleTheshold )
  775. {
  776. m_turnRate = 360.0f;
  777. if ( CrossProduct( followVector, Forward( ) ).z > 0.0f )
  778. {
  779. m_turnRate = -m_turnRate;
  780. }
  781. }
  782. else
  783. {
  784. m_turnRate = 0.0f;
  785. }
  786. // If the path goal is above us then don't try to avoid obstacles even if one is present.
  787. // Instead brute force into a wiggle aka jump;
  788. //
  789. float actualTurnRate = m_turnRate;
  790. float obstacleTurnRate = AvoidObstacles( );
  791. if ( DotProduct( followVector, Up( ) ) >= .9f ) // the path goal is overhead
  792. {
  793. Jump( );
  794. }
  795. if ( obstacleTurnRate != 0.0f )
  796. {
  797. actualTurnRate = obstacleTurnRate;
  798. }
  799. angle.y += actualTurnRate * deltaT;
  800. SetAbsAngles( angle );;
  801. }
  802. break;
  803. }
  804. }
  805. Vector desiredPosition;
  806. Vector velocityFromAnimation = GetGroundSpeedVelocity();
  807. desiredPosition = GetAbsOrigin() + velocityFromAnimation * deltaT;
  808. ResolveCollisions( desiredPosition, deltaT );
  809. SetPlaybackRate( 1.0f );
  810. StudioFrameAdvance();
  811. }
  812. const Vector &CChicken::GetCentroid( void ) const
  813. {
  814. static Vector centroid;
  815. centroid = GetFeet( );
  816. centroid.z += HalfHumanHeight;
  817. return centroid;
  818. }
  819. //-----------------------------------------------------------------------------------------------------
  820. /**
  821. * Return position of "feet" - point below centroid of improv at feet level
  822. */
  823. const Vector &CChicken::GetFeet( void ) const
  824. {
  825. static Vector feet;
  826. feet = GetAbsOrigin( );
  827. return feet;
  828. }
  829. //-----------------------------------------------------------------------------------------------------
  830. const Vector &CChicken::GetEyes( void ) const
  831. {
  832. static Vector eyes;
  833. eyes = EyePosition( );
  834. return eyes;
  835. }
  836. //-----------------------------------------------------------------------------------------------------
  837. /**
  838. * Return direction of movement
  839. */
  840. float CChicken::GetMoveAngle( void ) const
  841. {
  842. return GetAbsAngles( ).y;
  843. }
  844. //-----------------------------------------------------------------------------------------------------
  845. CNavArea *CChicken::GetLastKnownArea( void ) const
  846. {
  847. return m_lastKnownArea;
  848. }
  849. //-----------------------------------------------------------------------------------------------------
  850. /**
  851. * Find "simple" ground height, treating current nav area as part of the floo
  852. */
  853. bool CChicken::GetSimpleGroundHeightWithFloor( const Vector &pos, float *height, Vector *normal )
  854. {
  855. if ( TheNavMesh->GetSimpleGroundHeight( pos, height, normal ) )
  856. {
  857. // our current nav area also serves as a ground polygon
  858. if ( m_lastKnownArea && m_lastKnownArea->IsOverlapping( pos ) )
  859. {
  860. *height = MAX( ( *height ), m_lastKnownArea->GetZ( pos ) );
  861. }
  862. return true;
  863. }
  864. return false;
  865. }
  866. //-----------------------------------------------------------------------------------------------------
  867. void CChicken::Crouch( void )
  868. {
  869. // m_isCrouching = true;
  870. }
  871. //-----------------------------------------------------------------------------------------------------
  872. /**
  873. * un-crouch
  874. */
  875. void CChicken::StandUp( void )
  876. {
  877. // m_isCrouching = false;
  878. }
  879. //-----------------------------------------------------------------------------------------------------
  880. bool CChicken::IsCrouching( void ) const
  881. {
  882. return false;
  883. }
  884. //-----------------------------------------------------------------------------------------------------
  885. /**
  886. * Initiate a jump
  887. */
  888. void CChicken::Jump( void )
  889. {
  890. float flJumpPower = 250.0f; /*RandomFloat( 10.0f, 200.0f );*/ /*RemapValClamped( flTimeSinceLastJump, 3.0, 20.0, 200.0f, 30.0f );*/
  891. Jump( flJumpPower );
  892. }
  893. void CChicken::Jump( float flVelocity )
  894. {
  895. // don't jump if the nav disallows it
  896. CNavArea *myArea = GetLastKnownArea( );
  897. if ( myArea && myArea->HasAttributes( NAV_MESH_NO_JUMP ) )
  898. return;
  899. if ( CanJump( ) && IsOnGround( ) )
  900. {
  901. const float minJumpInterval = 0.1f;
  902. m_jumpTimer.Start( minJumpInterval );
  903. m_bInJump = true;
  904. //
  905. // Vector dir;
  906. // AngleVectors( GetAbsAngles( ), &dir, NULL, NULL );
  907. //
  908. // vel += dir * 10;
  909. Vector vel = GetAbsVelocity( );
  910. vel.z += flVelocity;
  911. SetAbsVelocity( vel );
  912. SetSequence( SelectWeightedSequence( ACT_RUN ) );
  913. ResetSequenceInfo( );
  914. m_jumpedThisFrame = true;
  915. m_flLastJumpTime = gpGlobals->curtime;
  916. }
  917. }
  918. //-----------------------------------------------------------------------------------------------------
  919. bool CChicken::IsJumping( void ) const
  920. {
  921. //return m_bInJump;
  922. return !m_jumpTimer.IsElapsed( );
  923. }
  924. bool CChicken::CanJump( void ) const
  925. {
  926. return !IsJumping( ) && ( gpGlobals->curtime - m_flLastJumpTime > 3.0f );
  927. }
  928. //-----------------------------------------------------------------------------------------------------
  929. /**
  930. * Set movement speed to running
  931. */
  932. void CChicken::Run( void )
  933. {
  934. // m_isRunning = true;
  935. m_activity = ACT_RUN;
  936. m_activityTimer.Start( RandomFloat( 0.5, 3.0f ) );
  937. m_turnRate = RandomFloat( -45.0f, 45.0f );
  938. SetSequence( SelectWeightedSequence( m_activity ) );
  939. ResetSequenceInfo( );
  940. }
  941. // -----------------------------------------------------------------------------------------------------
  942. // /**
  943. // * Set movement speed to walking
  944. // */
  945. // void CChicken::Walk( void )
  946. // {
  947. // m_isRunning = false;
  948. // }
  949. //-----------------------------------------------------------------------------------------------------
  950. bool CChicken::IsRunning( void ) const
  951. {
  952. return false;
  953. }
  954. //-----------------------------------------------------------------------------------------------------
  955. /**
  956. * Invoked when a ladder is encountered while following a path
  957. */
  958. void CChicken::StartLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos )
  959. {
  960. }
  961. //-----------------------------------------------------------------------------------------------------
  962. /**
  963. * Traverse given ladder
  964. */
  965. bool CChicken::TraverseLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos, float deltaT )
  966. {
  967. return false;
  968. }
  969. //-----------------------------------------------------------------------------------------------------
  970. bool CChicken::IsUsingLadder( void ) const
  971. {
  972. return false;
  973. }
  974. //-----------------------------------------------------------------------------------------------------
  975. void CChicken::TrackPath( const Vector &pathGoal, float deltaT )
  976. {
  977. m_vecPathGoal = pathGoal;
  978. }
  979. //-----------------------------------------------------------------------------------------------------
  980. /**
  981. * Invoked when an improv reaches its MoveTo goal
  982. */
  983. void CChicken::OnMoveToSuccess( const Vector &goal )
  984. {
  985. }
  986. //-----------------------------------------------------------------------------------------------------
  987. /**
  988. * Invoked when an improv fails to reach a MoveTo goal
  989. */
  990. void CChicken::OnMoveToFailure( const Vector &goal, MoveToFailureType reason )
  991. {
  992. }