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.

331 lines
9.1 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. // ghost.cpp
  3. // A spooky halloween ghost bot
  4. // Michael Booth, October 2011
  5. #include "cbase.h"
  6. #include "tf_player.h"
  7. #include "tf_gamerules.h"
  8. #include "tf_team.h"
  9. #include "tf_projectile_arrow.h"
  10. #include "tf_weapon_grenade_pipebomb.h"
  11. #include "nav_mesh/tf_nav_area.h"
  12. #include "ghost.h"
  13. #include "NextBot/Path/NextBotChasePath.h"
  14. #include "econ_wearable.h"
  15. #include "team_control_point_master.h"
  16. #include "particle_parse.h"
  17. #include "CRagdollMagnet.h"
  18. #include "NextBot/Behavior/BehaviorMoveTo.h"
  19. void CC_GhostSpawn( const CCommand& args )
  20. {
  21. MDLCACHE_CRITICAL_SECTION();
  22. CBaseEntity *entity = CreateEntityByName( "ghost" );
  23. if ( entity )
  24. {
  25. entity->Precache();
  26. DispatchSpawn( entity );
  27. // Now attempt to drop into the world
  28. CBasePlayer* pPlayer = UTIL_GetCommandClient();
  29. trace_t tr;
  30. Vector forward;
  31. pPlayer->EyeVectors( &forward );
  32. UTIL_TraceLine(pPlayer->EyePosition(),
  33. pPlayer->EyePosition() + forward * MAX_TRACE_LENGTH,MASK_SOLID,
  34. pPlayer, COLLISION_GROUP_NONE, &tr );
  35. if ( tr.fraction != 1.0 )
  36. {
  37. // Raise the end position a little up off the floor, place the npc and drop him down
  38. tr.endpos.z += 12;
  39. entity->Teleport( &tr.endpos, NULL, NULL );
  40. }
  41. }
  42. }
  43. static ConCommand ghost_spawn( "ghost_spawn", CC_GhostSpawn, "Spawns a Ghost where the player is looking.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  44. //-----------------------------------------------------------------------------------------------------
  45. CGhost *SpawnGhost( const Vector &spot, const QAngle &angles, float lifetime )
  46. {
  47. CGhost *ghost = (CGhost *)CreateEntityByName( "ghost" );
  48. if ( ghost )
  49. {
  50. DispatchSpawn( ghost );
  51. ghost->SetAbsOrigin( spot );
  52. ghost->SetLocalAngles( angles );
  53. ghost->SetLifetime( lifetime );
  54. return ghost;
  55. }
  56. return NULL;
  57. }
  58. //-----------------------------------------------------------------------------------------------------
  59. // The Ghost
  60. //-----------------------------------------------------------------------------------------------------
  61. LINK_ENTITY_TO_CLASS( ghost, CGhost );
  62. //-----------------------------------------------------------------------------------------------------
  63. CGhost::CGhost()
  64. {
  65. ALLOCATE_INTENTION_INTERFACE( CGhost );
  66. m_locomotor = new CGhostLocomotion( this );
  67. m_eyeOffset = vec3_origin;
  68. m_lifetime = 10.0f;
  69. }
  70. //-----------------------------------------------------------------------------------------------------
  71. CGhost::~CGhost()
  72. {
  73. DEALLOCATE_INTENTION_INTERFACE;
  74. if ( m_locomotor )
  75. delete m_locomotor;
  76. }
  77. //-----------------------------------------------------------------------------------------------------
  78. void CGhost::PrecacheGhost()
  79. {
  80. PrecacheModel( "models/props_halloween/ghost_no_hat.mdl" );
  81. PrecacheParticleSystem( "ghost_appearation" );
  82. PrecacheScriptSound( "Halloween.GhostMoan" );
  83. PrecacheScriptSound( "Halloween.GhostBoo" );
  84. PrecacheScriptSound( "Halloween.Haunted" );
  85. }
  86. //-----------------------------------------------------------------------------------------------------
  87. void CGhost::Precache()
  88. {
  89. BaseClass::Precache();
  90. // always allow late precaching, so we don't pay the cost of the
  91. // Halloween Ghost for the entire year
  92. bool bAllowPrecache = CBaseEntity::IsPrecacheAllowed();
  93. CBaseEntity::SetAllowPrecache( true );
  94. PrecacheGhost();
  95. CBaseEntity::SetAllowPrecache( bAllowPrecache );
  96. }
  97. //-----------------------------------------------------------------------------------------------------
  98. void CGhost::Spawn( void )
  99. {
  100. Precache();
  101. BaseClass::Spawn();
  102. SetCollisionGroup( COLLISION_GROUP_NONE );
  103. SetSolid( SOLID_NONE );
  104. AddSolidFlags( FSOLID_NOT_SOLID );
  105. SetModel( "models/props_halloween/ghost_no_hat.mdl" );
  106. }
  107. //---------------------------------------------------------------------------------------------
  108. bool CGhost::ShouldCollide( int collisionGroup, int contentsMask ) const
  109. {
  110. return false;
  111. //return BaseClass::ShouldCollide( collisionGroup, contentsMask );
  112. }
  113. //---------------------------------------------------------------------------------------------
  114. //---------------------------------------------------------------------------------------------
  115. class CGhostBehavior : public Action< CGhost >
  116. {
  117. public:
  118. //---------------------------------------------------------------------------------------------
  119. virtual ActionResult< CGhost > OnStart( CGhost *me, Action< CGhost > *priorAction )
  120. {
  121. m_lifeTimer.Start();
  122. m_stuckAnchor = me->GetAbsOrigin();
  123. m_stuckTimer.Start( 1.0f );
  124. me->GetVectors( &m_forward, NULL, NULL );
  125. DispatchParticleEffect( "ghost_appearation", me->WorldSpaceCenter(), me->GetAbsAngles() );
  126. return Continue();
  127. }
  128. //---------------------------------------------------------------------------------------------
  129. virtual ActionResult< CGhost > Update( CGhost *me, float interval )
  130. {
  131. if ( m_lifeTimer.IsGreaterThen( me->GetLifetime() ) || m_stuckTimer.IsElapsed() )
  132. {
  133. DispatchParticleEffect( "ghost_appearation", me->WorldSpaceCenter(), me->GetAbsAngles() );
  134. me->EmitSound( "Halloween.Haunted" );
  135. UTIL_Remove( me );
  136. return Done();
  137. }
  138. if ( m_moanTimer.IsElapsed() )
  139. {
  140. me->EmitSound( "Halloween.GhostMoan" );
  141. m_moanTimer.Start( RandomFloat( 5.0f, 7.0f ) );
  142. }
  143. DriftAroundAndAvoidObstacles( me );
  144. ScareNearbyPlayers( me );
  145. return Continue();
  146. }
  147. //---------------------------------------------------------------------------------------------
  148. void DriftAroundAndAvoidObstacles( CGhost *me )
  149. {
  150. const float feelerRange = 150.0f;
  151. Vector left( -m_forward.y, m_forward.x, 0.0f );
  152. Vector right( m_forward.y, -m_forward.x, 0.0f );
  153. CTraceFilterNoNPCsOrPlayer traceFilter( me, COLLISION_GROUP_NONE );
  154. trace_t resultLeft;
  155. UTIL_TraceLine( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + left ), MASK_PLAYERSOLID, &traceFilter, &resultLeft );
  156. //NDebugOverlay::Line( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + left ), 0, 0, 255, true, interval );
  157. trace_t resultRight;
  158. UTIL_TraceLine( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + right ), MASK_PLAYERSOLID, &traceFilter, &resultRight );
  159. //NDebugOverlay::Line( me->WorldSpaceCenter(), me->WorldSpaceCenter() + feelerRange * ( m_forward + right ), 255, 0, 0, true, interval );
  160. const float turnRate = 0.2f;
  161. if ( resultLeft.DidHit() )
  162. {
  163. if ( resultRight.DidHit() )
  164. {
  165. // both sides hit
  166. if ( resultLeft.fraction < resultRight.fraction )
  167. {
  168. // left hit closer - turn right
  169. m_forward += turnRate * right;
  170. }
  171. else
  172. {
  173. // right hit closer - turn left
  174. m_forward += turnRate * left;
  175. }
  176. }
  177. else
  178. {
  179. // left hit - turn right
  180. m_forward += turnRate * right;
  181. }
  182. }
  183. else if ( resultRight.DidHit() )
  184. {
  185. // right hit - turn left
  186. m_forward += turnRate * left;
  187. }
  188. m_forward.NormalizeInPlace();
  189. Vector goal = 100.0f * m_forward + me->GetAbsOrigin();
  190. me->GetLocomotionInterface()->Approach( goal );
  191. me->GetLocomotionInterface()->FaceTowards( goal );
  192. me->GetLocomotionInterface()->Run();
  193. if ( me->IsRangeGreaterThan( m_stuckAnchor, 50.0f ) )
  194. {
  195. m_stuckAnchor = me->GetAbsOrigin();
  196. m_stuckTimer.Reset();
  197. }
  198. }
  199. //---------------------------------------------------------------------------------------------
  200. void ScareNearbyPlayers( CGhost *me )
  201. {
  202. if ( m_scareTimer.IsElapsed() )
  203. {
  204. m_scareTimer.Start( 1.0f );
  205. CUtlVector< CTFPlayer * > playerVector;
  206. CollectPlayers( &playerVector, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS );
  207. CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS );
  208. for( int i=0; i<playerVector.Count(); ++i )
  209. {
  210. CTFPlayer *victim = playerVector[i];
  211. if ( victim && !victim->HasPurgatoryBuff() )
  212. {
  213. if ( me->IsRangeLessThan( victim, GHOST_SCARE_RADIUS ) )
  214. {
  215. if ( me->IsLineOfSightClear( victim ) )
  216. {
  217. // scare them!
  218. const float scareTime = 2.0f;
  219. const float speedReduction = 0.0f;
  220. // "stun by trigger" results in the Halloween "yikes" effects
  221. int stunFlags = TF_STUN_LOSER_STATE | TF_STUN_BY_TRIGGER;
  222. victim->m_Shared.StunPlayer( scareTime, speedReduction, stunFlags, NULL );
  223. }
  224. }
  225. }
  226. }
  227. }
  228. }
  229. virtual const char *GetName( void ) const { return "Behavior"; } // return name of this action
  230. private:
  231. IntervalTimer m_lifeTimer;
  232. CountdownTimer m_moanTimer;
  233. CountdownTimer m_scareTimer;
  234. Vector m_forward;
  235. Vector m_stuckAnchor;
  236. CountdownTimer m_stuckTimer;
  237. };
  238. IMPLEMENT_INTENTION_INTERFACE( CGhost, CGhostBehavior );
  239. //---------------------------------------------------------------------------------------------
  240. //---------------------------------------------------------------------------------------------
  241. // Get maximum running speed
  242. float CGhostLocomotion::GetRunSpeed( void ) const
  243. {
  244. return 90.0f;
  245. }
  246. //---------------------------------------------------------------------------------------------
  247. // Return maximum acceleration of locomotor
  248. float CGhostLocomotion::GetMaxAcceleration( void ) const
  249. {
  250. return 500.0f;
  251. }
  252. //---------------------------------------------------------------------------------------------
  253. // Return maximum deceleration of locomotor
  254. float CGhostLocomotion::GetMaxDeceleration( void ) const
  255. {
  256. return 500.0f;
  257. }