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.

471 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "hl1_ai_basenpc.h"
  10. #include "ai_default.h"
  11. #include "ai_task.h"
  12. #include "ai_schedule.h"
  13. #include "ai_node.h"
  14. #include "ai_hull.h"
  15. #include "ai_hint.h"
  16. #include "ai_memory.h"
  17. #include "ai_route.h"
  18. #include "ai_motor.h"
  19. #include "ai_senses.h"
  20. #include "soundent.h"
  21. #include "game.h"
  22. #include "npcevent.h"
  23. #include "entitylist.h"
  24. #include "activitylist.h"
  25. #include "animation.h"
  26. #include "basecombatweapon.h"
  27. #include "IEffects.h"
  28. #include "vstdlib/random.h"
  29. #include "engine/IEngineSound.h"
  30. #include "ammodef.h"
  31. #include "ai_behavior_follow.h"
  32. #include "ai_navigator.h"
  33. #include "decals.h"
  34. #define ROACH_IDLE 0
  35. #define ROACH_BORED 1
  36. #define ROACH_SCARED_BY_ENT 2
  37. #define ROACH_SCARED_BY_LIGHT 3
  38. #define ROACH_SMELL_FOOD 4
  39. #define ROACH_EAT 5
  40. //=========================================================
  41. // Monster's Anim Events Go Here
  42. //=========================================================
  43. class CNPC_Roach : public CHL1BaseNPC
  44. {
  45. DECLARE_CLASS( CNPC_Roach, CHL1BaseNPC );
  46. public:
  47. void Spawn( void );
  48. void Precache( void );
  49. float MaxYawSpeed( void );
  50. // DECLARE_DATADESC();
  51. void NPCThink ( void );
  52. void PickNewDest ( int iCondition );
  53. void Look ( int iDistance );
  54. void Move ( float flInterval );
  55. Class_T Classify( void ) { return CLASS_INSECT; }
  56. void Touch ( CBaseEntity *pOther );
  57. void Event_Killed( const CTakeDamageInfo &info );
  58. int GetSoundInterests ( void );
  59. void Eat( float flFullDuration );
  60. bool ShouldEat( void );
  61. bool ShouldGib( const CTakeDamageInfo &info ) { return false; }
  62. float m_flLastLightLevel;
  63. float m_flNextSmellTime;
  64. // UNDONE: These don't necessarily need to be save/restored, but if we add more data, it may
  65. bool m_fLightHacked;
  66. int m_iMode;
  67. float m_flHungryTime;
  68. // -----------------------------
  69. };
  70. LINK_ENTITY_TO_CLASS( monster_cockroach, CNPC_Roach );
  71. //BEGIN_DATADESC( CNPC_Roach )
  72. // DEFINE_FUNCTION( RoachTouch ),
  73. //END_DATADESC()
  74. //=========================================================
  75. // Spawn
  76. //=========================================================
  77. void CNPC_Roach::Spawn()
  78. {
  79. Precache( );
  80. SetModel( "models/roach.mdl" );
  81. UTIL_SetSize( this, Vector( -1, -1, 0 ), Vector( 1, 1, 2 ) );
  82. SetSolid( SOLID_BBOX );
  83. AddSolidFlags( FSOLID_NOT_STANDABLE );
  84. SetMoveType( MOVETYPE_STEP );
  85. m_bloodColor = BLOOD_COLOR_YELLOW;
  86. ClearEffects();
  87. m_iHealth = 1;
  88. m_flFieldOfView = 0.5;// indicates the width of this monster's forward view cone ( as a dotproduct result )
  89. m_NPCState = NPC_STATE_NONE;
  90. SetRenderColor( 255, 255, 255, 255 );
  91. NPCInit();
  92. SetActivity ( ACT_IDLE );
  93. SetViewOffset ( Vector ( 0, 0, 1 ) );// position of the eyes relative to monster's origin.
  94. m_takedamage = DAMAGE_YES;
  95. m_fLightHacked = FALSE;
  96. m_flLastLightLevel = -1;
  97. m_iMode = ROACH_IDLE;
  98. m_flNextSmellTime = gpGlobals->curtime;
  99. AddEffects( EF_NOSHADOW );
  100. }
  101. //=========================================================
  102. // Precache - precaches all resources this monster needs
  103. //=========================================================
  104. void CNPC_Roach::Precache()
  105. {
  106. PrecacheModel("models/roach.mdl");
  107. PrecacheScriptSound( "Roach.Walk" );
  108. PrecacheScriptSound( "Roach.Die" );
  109. PrecacheScriptSound( "Roach.Smash" );
  110. }
  111. float CNPC_Roach::MaxYawSpeed( void )
  112. {
  113. return 120.0f;
  114. }
  115. void CNPC_Roach::Eat( float flFullDuration )
  116. {
  117. m_flHungryTime = gpGlobals->curtime + flFullDuration;
  118. }
  119. bool CNPC_Roach::ShouldEat( void )
  120. {
  121. if ( m_flHungryTime > gpGlobals->curtime )
  122. {
  123. return false;
  124. }
  125. return true;
  126. }
  127. //=========================================================
  128. // MonsterThink, overridden for roaches.
  129. //=========================================================
  130. void CNPC_Roach::NPCThink( void )
  131. {
  132. if ( FNullEnt( UTIL_FindClientInPVS( edict() ) ) )
  133. SetNextThink( gpGlobals->curtime + random->RandomFloat( 1.0f , 1.5f ) );
  134. else
  135. SetNextThink( gpGlobals->curtime + 0.1f );// keep monster thinking
  136. float flInterval = gpGlobals->curtime - GetLastThink();
  137. StudioFrameAdvance( ); // animate
  138. if ( !m_fLightHacked )
  139. {
  140. // if light value hasn't been collection for the first time yet,
  141. // suspend the creature for a second so the world finishes spawning, then we'll collect the light level.
  142. SetNextThink( gpGlobals->curtime + 1 );
  143. m_fLightHacked = TRUE;
  144. return;
  145. }
  146. else if ( m_flLastLightLevel < 0 )
  147. {
  148. // collect light level for the first time, now that all of the lightmaps in the roach's area have been calculated.
  149. m_flLastLightLevel = 0;
  150. }
  151. switch ( m_iMode )
  152. {
  153. case ROACH_IDLE:
  154. case ROACH_EAT:
  155. {
  156. // if not moving, sample environment to see if anything scary is around. Do a radius search 'look' at random.
  157. if ( random->RandomInt( 0, 3 ) == 1 )
  158. {
  159. Look( 150 );
  160. if ( HasCondition( COND_SEE_FEAR ) )
  161. {
  162. // if see something scary
  163. //ALERT ( at_aiconsole, "Scared\n" );
  164. Eat( 30 + ( random->RandomInt( 0, 14 ) ) );// roach will ignore food for 30 to 45 seconds
  165. PickNewDest( ROACH_SCARED_BY_ENT );
  166. SetActivity ( ACT_WALK );
  167. }
  168. else if ( random->RandomInt( 0,149 ) == 1 )
  169. {
  170. // if roach doesn't see anything, there's still a chance that it will move. (boredom)
  171. //ALERT ( at_aiconsole, "Bored\n" );
  172. PickNewDest( ROACH_BORED );
  173. SetActivity ( ACT_WALK );
  174. if ( m_iMode == ROACH_EAT )
  175. {
  176. // roach will ignore food for 30 to 45 seconds if it got bored while eating.
  177. Eat( 30 + ( random->RandomInt(0,14) ) );
  178. }
  179. }
  180. }
  181. // don't do this stuff if eating!
  182. if ( m_iMode == ROACH_IDLE )
  183. {
  184. if ( ShouldEat() )
  185. {
  186. GetSenses()->Listen();
  187. }
  188. if ( 0 > m_flLastLightLevel )
  189. {
  190. // someone turned on lights!
  191. //ALERT ( at_console, "Lights!\n" );
  192. PickNewDest( ROACH_SCARED_BY_LIGHT );
  193. SetActivity ( ACT_WALK );
  194. }
  195. else if ( HasCondition( COND_SMELL ) )
  196. {
  197. CSound *pSound = GetLoudestSoundOfType( ALL_SOUNDS );
  198. // roach smells food and is just standing around. Go to food unless food isn't on same z-plane.
  199. if ( pSound && abs( pSound->GetSoundOrigin().z - GetAbsOrigin().z ) <= 3 )
  200. {
  201. PickNewDest( ROACH_SMELL_FOOD );
  202. SetActivity ( ACT_WALK );
  203. }
  204. }
  205. }
  206. break;
  207. }
  208. case ROACH_SCARED_BY_LIGHT:
  209. {
  210. // if roach was scared by light, then stop if we're over a spot at least as dark as where we started!
  211. if ( 0 <= m_flLastLightLevel )
  212. {
  213. SetActivity ( ACT_IDLE );
  214. m_flLastLightLevel = 0;// make this our new light level.
  215. }
  216. break;
  217. }
  218. }
  219. if ( GetActivity() != ACT_IDLE )
  220. {
  221. Move( flInterval );
  222. }
  223. }
  224. void CNPC_Roach::PickNewDest ( int iCondition )
  225. {
  226. Vector vecNewDir;
  227. Vector vecDest;
  228. float flDist;
  229. m_iMode = iCondition;
  230. GetNavigator()->ClearGoal();
  231. if ( m_iMode == ROACH_SMELL_FOOD )
  232. {
  233. // find the food and go there.
  234. CSound *pSound = GetLoudestSoundOfType( ALL_SOUNDS );
  235. if ( pSound )
  236. {
  237. GetNavigator()->SetRandomGoal( 3 - random->RandomInt( 0,5 ) );
  238. return;
  239. }
  240. }
  241. do
  242. {
  243. // picks a random spot, requiring that it be at least 128 units away
  244. // else, the roach will pick a spot too close to itself and run in
  245. // circles. this is a hack but buys me time to work on the real monsters.
  246. vecNewDir.x = random->RandomInt( -1, 1 );
  247. vecNewDir.y = random->RandomInt( -1, 1 );
  248. flDist = 256 + ( random->RandomInt(0,255) );
  249. vecDest = GetAbsOrigin() + vecNewDir * flDist;
  250. } while ( ( vecDest - GetAbsOrigin() ).Length2D() < 128 );
  251. Vector vecLocation;
  252. vecLocation.x = vecDest.x;
  253. vecLocation.y = vecDest.y;
  254. vecLocation.z = GetAbsOrigin().z;
  255. AI_NavGoal_t goal( GOALTYPE_LOCATION, vecLocation, ACT_WALK );
  256. GetNavigator()->SetGoal( goal );
  257. if ( random->RandomInt( 0, 9 ) == 1 )
  258. {
  259. // every once in a while, a roach will play a skitter sound when they decide to run
  260. CPASAttenuationFilter filter( this );
  261. EmitSound( filter, entindex(), "Roach.Walk" );
  262. }
  263. }
  264. //=========================================================
  265. // Look - overriden for the roach, which can virtually see
  266. // 360 degrees.
  267. //=========================================================
  268. void CNPC_Roach::Look ( int iDistance )
  269. {
  270. CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with
  271. // DON'T let visibility information from last frame sit around!
  272. ClearCondition( COND_SEE_HATE | COND_SEE_DISLIKE | COND_SEE_ENEMY | COND_SEE_FEAR );
  273. // don't let monsters outside of the player's PVS act up, or most of the interesting
  274. // things will happen before the player gets there!
  275. if ( FNullEnt( UTIL_FindClientInPVS( edict() ) ) )
  276. {
  277. return;
  278. }
  279. // Does sphere also limit itself to PVS?
  280. // Examine all entities within a reasonable radius
  281. // !!!PERFORMANCE - let's trivially reject the ent list before radius searching!
  282. for ( CEntitySphereQuery sphere( GetAbsOrigin(), iDistance ); ( pSightEnt = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  283. {
  284. // only consider ents that can be damaged. !!!temporarily only considering other monsters and clients
  285. if ( pSightEnt->IsPlayer() || FBitSet ( pSightEnt->GetFlags(), FL_NPC ) )
  286. {
  287. if ( /*FVisible( pSightEnt ) &&*/ !FBitSet( pSightEnt->GetFlags(), FL_NOTARGET ) && pSightEnt->m_iHealth > 0 )
  288. {
  289. // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when
  290. // we see monsters other than the Enemy.
  291. switch ( IRelationType ( pSightEnt ) )
  292. {
  293. case D_FR:
  294. SetCondition( COND_SEE_FEAR );
  295. break;
  296. case D_NU:
  297. break;
  298. default:
  299. Msg ( "%s can't asses %s\n", GetClassname(), pSightEnt->GetClassname() );
  300. break;
  301. }
  302. }
  303. }
  304. }
  305. }
  306. //=========================================================
  307. // roach's move function
  308. //=========================================================
  309. void CNPC_Roach::Move ( float flInterval )
  310. {
  311. float flWaypointDist;
  312. Vector vecApex;
  313. // local move to waypoint.
  314. flWaypointDist = ( GetNavigator()->GetGoalPos() - GetAbsOrigin() ).Length2D();
  315. GetMotor()->SetIdealYawToTargetAndUpdate( GetNavigator()->GetGoalPos() );
  316. float speed = 150 * flInterval;
  317. Vector vToTarget = GetNavigator()->GetGoalPos() - GetAbsOrigin();
  318. vToTarget.NormalizeInPlace();
  319. Vector vMovePos = vToTarget * speed;
  320. if ( random->RandomInt( 0,7 ) == 1 )
  321. {
  322. // randomly change direction
  323. PickNewDest( m_iMode );
  324. }
  325. if( !WalkMove( vMovePos, MASK_NPCSOLID ) )
  326. {
  327. PickNewDest( m_iMode );
  328. }
  329. // if the waypoint is closer than step size, then stop after next step (ok for roach to overshoot)
  330. if ( flWaypointDist <= m_flGroundSpeed * flInterval )
  331. {
  332. // take truncated step and stop
  333. SetActivity ( ACT_IDLE );
  334. m_flLastLightLevel = 0;// this is roach's new comfortable light level
  335. if ( m_iMode == ROACH_SMELL_FOOD )
  336. {
  337. m_iMode = ROACH_EAT;
  338. }
  339. else
  340. {
  341. m_iMode = ROACH_IDLE;
  342. }
  343. }
  344. if ( random->RandomInt( 0,149 ) == 1 && m_iMode != ROACH_SCARED_BY_LIGHT && m_iMode != ROACH_SMELL_FOOD )
  345. {
  346. // random skitter while moving as long as not on a b-line to get out of light or going to food
  347. PickNewDest( FALSE );
  348. }
  349. }
  350. void CNPC_Roach::Touch ( CBaseEntity *pOther )
  351. {
  352. Vector vecSpot;
  353. trace_t tr;
  354. if ( pOther->GetAbsVelocity() == vec3_origin || !pOther->IsPlayer() )
  355. {
  356. return;
  357. }
  358. vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 );//move up a bit, and trace down.
  359. //UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), ignore_monsters, ENT(pev), & tr);
  360. UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ), MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
  361. // This isn't really blood. So you don't have to screen it out based on violence levels (UTIL_ShouldShowBlood())
  362. UTIL_DecalTrace( &tr, "YellowBlood" );
  363. // DMG_GENERIC because we don't want any physics force generated
  364. TakeDamage( CTakeDamageInfo( pOther, pOther, m_iHealth, DMG_GENERIC ) );
  365. }
  366. void CNPC_Roach::Event_Killed( const CTakeDamageInfo &info )
  367. {
  368. RemoveSolidFlags( FSOLID_NOT_SOLID );
  369. CPASAttenuationFilter filter( this );
  370. //random sound
  371. if ( random->RandomInt( 0,4 ) == 1 )
  372. {
  373. EmitSound( filter, entindex(), "Roach.Die" );
  374. }
  375. else
  376. {
  377. EmitSound( filter, entindex(), "Roach.Smash" );
  378. }
  379. CSoundEnt::InsertSound ( SOUND_WORLD, GetAbsOrigin(), 128, 1 );
  380. UTIL_Remove( this );
  381. }
  382. int CNPC_Roach::GetSoundInterests ( void)
  383. {
  384. return SOUND_CARCASS |
  385. SOUND_MEAT;
  386. }