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.

400 lines
9.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "ai_default.h"
  10. #include "ai_task.h"
  11. #include "ai_schedule.h"
  12. #include "ai_node.h"
  13. #include "ai_hull.h"
  14. #include "ai_hint.h"
  15. #include "ai_memory.h"
  16. #include "ai_route.h"
  17. #include "ai_motor.h"
  18. #include "ai_senses.h"
  19. #include "soundent.h"
  20. #include "game.h"
  21. #include "npcevent.h"
  22. #include "entitylist.h"
  23. #include "activitylist.h"
  24. #include "animation.h"
  25. #include "basecombatweapon.h"
  26. #include "IEffects.h"
  27. #include "vstdlib/random.h"
  28. #include "engine/IEngineSound.h"
  29. #include "ammodef.h"
  30. #include "te.h"
  31. #include "hl1_npc_hornet.h"
  32. int iHornetTrail;
  33. int iHornetPuff;
  34. LINK_ENTITY_TO_CLASS( hornet, CNPC_Hornet );
  35. extern ConVar sk_npc_dmg_hornet;
  36. extern ConVar sk_plr_dmg_hornet;
  37. BEGIN_DATADESC( CNPC_Hornet )
  38. DEFINE_FIELD( m_flStopAttack, FIELD_TIME ),
  39. DEFINE_FIELD( m_iHornetType, FIELD_INTEGER ),
  40. DEFINE_FIELD( m_flFlySpeed, FIELD_FLOAT ),
  41. DEFINE_FIELD( m_flDamage, FIELD_INTEGER ),
  42. DEFINE_FIELD( m_vecEnemyLKP, FIELD_POSITION_VECTOR ),
  43. DEFINE_ENTITYFUNC( DieTouch ),
  44. DEFINE_THINKFUNC( StartDart ),
  45. DEFINE_THINKFUNC( StartTrack ),
  46. DEFINE_ENTITYFUNC( DartTouch ),
  47. DEFINE_ENTITYFUNC( TrackTouch ),
  48. DEFINE_THINKFUNC( TrackTarget ),
  49. END_DATADESC()
  50. //=========================================================
  51. //=========================================================
  52. void CNPC_Hornet::Spawn( void )
  53. {
  54. Precache();
  55. SetMoveType( MOVETYPE_FLY );
  56. SetSolid( SOLID_BBOX );
  57. m_takedamage = DAMAGE_YES;
  58. AddFlag( FL_NPC );
  59. m_iHealth = 1;// weak!
  60. m_bloodColor = DONT_BLEED;
  61. if ( g_pGameRules->IsMultiplayer() )
  62. {
  63. // hornets don't live as long in multiplayer
  64. m_flStopAttack = gpGlobals->curtime + 3.5;
  65. }
  66. else
  67. {
  68. m_flStopAttack = gpGlobals->curtime + 5.0;
  69. }
  70. m_flFieldOfView = 0.9; // +- 25 degrees
  71. if ( random->RandomInt ( 1, 5 ) <= 2 )
  72. {
  73. m_iHornetType = HORNET_TYPE_RED;
  74. m_flFlySpeed = HORNET_RED_SPEED;
  75. }
  76. else
  77. {
  78. m_iHornetType = HORNET_TYPE_ORANGE;
  79. m_flFlySpeed = HORNET_ORANGE_SPEED;
  80. }
  81. SetModel( "models/hornet.mdl" );
  82. UTIL_SetSize( this, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ) );
  83. SetTouch( &CNPC_Hornet::DieTouch );
  84. SetThink( &CNPC_Hornet::StartTrack );
  85. if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_CLIENT) )
  86. {
  87. m_flDamage = sk_plr_dmg_hornet.GetFloat();
  88. }
  89. else
  90. {
  91. // no real owner, or owner isn't a client.
  92. m_flDamage = sk_npc_dmg_hornet.GetFloat();
  93. }
  94. SetNextThink( gpGlobals->curtime + 0.1f );
  95. ResetSequenceInfo();
  96. m_vecEnemyLKP = vec3_origin;
  97. }
  98. void CNPC_Hornet::Precache()
  99. {
  100. PrecacheModel("models/hornet.mdl");
  101. iHornetPuff = PrecacheModel( "sprites/muz1.vmt" );
  102. iHornetTrail = PrecacheModel("sprites/laserbeam.vmt");
  103. PrecacheScriptSound( "Hornet.Die" );
  104. PrecacheScriptSound( "Hornet.Buzz" );
  105. }
  106. //=========================================================
  107. // hornets will never get mad at each other, no matter who the owner is.
  108. //=========================================================
  109. Disposition_t CNPC_Hornet::IRelationType( CBaseEntity *pTarget )
  110. {
  111. if ( pTarget->GetModelIndex() == GetModelIndex() )
  112. {
  113. return D_NU;
  114. }
  115. return BaseClass::IRelationType( pTarget );
  116. }
  117. //=========================================================
  118. // ID's Hornet as their owner
  119. //=========================================================
  120. Class_T CNPC_Hornet::Classify ( void )
  121. {
  122. if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_CLIENT) )
  123. {
  124. return CLASS_PLAYER_BIOWEAPON;
  125. }
  126. return CLASS_ALIEN_BIOWEAPON;
  127. }
  128. //=========================================================
  129. // StartDart - starts a hornet out just flying straight.
  130. //=========================================================
  131. void CNPC_Hornet::StartDart ( void )
  132. {
  133. IgniteTrail();
  134. SetTouch( &CNPC_Hornet::DartTouch );
  135. SetThink( &CBaseEntity::SUB_Remove );
  136. SetNextThink( gpGlobals->curtime + 4 );
  137. }
  138. void CNPC_Hornet::DieTouch ( CBaseEntity *pOther )
  139. {
  140. if ( !pOther || !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
  141. {
  142. return;
  143. }
  144. CPASAttenuationFilter filter( this );
  145. EmitSound( filter, entindex(), "Hornet.Die" );
  146. CTakeDamageInfo info( this, GetOwnerEntity(), m_flDamage, DMG_BULLET );
  147. CalculateBulletDamageForce( &info, GetAmmoDef()->Index("Hornet"), GetAbsVelocity(), GetAbsOrigin() );
  148. pOther->TakeDamage( info );
  149. m_takedamage = DAMAGE_NO;
  150. AddEffects( EF_NODRAW );
  151. AddSolidFlags( FSOLID_NOT_SOLID );// intangible
  152. UTIL_Remove( this );
  153. SetTouch( NULL );
  154. }
  155. //=========================================================
  156. // StartTrack - starts a hornet out tracking its target
  157. //=========================================================
  158. void CNPC_Hornet:: StartTrack ( void )
  159. {
  160. IgniteTrail();
  161. SetTouch( &CNPC_Hornet::TrackTouch );
  162. SetThink( &CNPC_Hornet::TrackTarget );
  163. SetNextThink( gpGlobals->curtime + 0.1f );
  164. }
  165. void TE_BeamFollow( IRecipientFilter& filter, float delay,
  166. int iEntIndex, int modelIndex, int haloIndex, float life, float width, float endWidth,
  167. float fadeLength,float r, float g, float b, float a );
  168. void CNPC_Hornet::IgniteTrail( void )
  169. {
  170. Vector vColor;
  171. if ( m_iHornetType == HORNET_TYPE_RED )
  172. vColor = Vector ( 179, 39, 14 );
  173. else
  174. vColor = Vector ( 255, 128, 0 );
  175. CBroadcastRecipientFilter filter;
  176. TE_BeamFollow( filter, 0.0,
  177. entindex(),
  178. iHornetTrail,
  179. 0,
  180. 1,
  181. 2,
  182. 0.5,
  183. 0.5,
  184. vColor.x,
  185. vColor.y,
  186. vColor.z,
  187. 128 );
  188. }
  189. unsigned int CNPC_Hornet::PhysicsSolidMaskForEntity( void ) const
  190. {
  191. unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
  192. iMask &= ~CONTENTS_MONSTERCLIP;
  193. return iMask;
  194. }
  195. //=========================================================
  196. // Tracking Hornet hit something
  197. //=========================================================
  198. void CNPC_Hornet::TrackTouch ( CBaseEntity *pOther )
  199. {
  200. if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
  201. {
  202. return;
  203. }
  204. if ( pOther == GetOwnerEntity() || pOther->GetModelIndex() == GetModelIndex() )
  205. {// bumped into the guy that shot it.
  206. //SetSolid( SOLID_NOT );
  207. return;
  208. }
  209. int nRelationship = IRelationType( pOther );
  210. if ( (nRelationship == D_FR || nRelationship == D_NU || nRelationship == D_LI) )
  211. {
  212. // hit something we don't want to hurt, so turn around.
  213. Vector vecVel = GetAbsVelocity();
  214. VectorNormalize( vecVel );
  215. vecVel.x *= -1;
  216. vecVel.y *= -1;
  217. SetAbsOrigin( GetAbsOrigin() + vecVel * 4 ); // bounce the hornet off a bit.
  218. SetAbsVelocity( vecVel * m_flFlySpeed );
  219. return;
  220. }
  221. DieTouch( pOther );
  222. }
  223. void CNPC_Hornet::DartTouch( CBaseEntity *pOther )
  224. {
  225. DieTouch( pOther );
  226. }
  227. //=========================================================
  228. // Hornet is flying, gently tracking target
  229. //=========================================================
  230. void CNPC_Hornet::TrackTarget ( void )
  231. {
  232. Vector vecFlightDir;
  233. Vector vecDirToEnemy;
  234. float flDelta;
  235. StudioFrameAdvance( );
  236. if (gpGlobals->curtime > m_flStopAttack)
  237. {
  238. SetTouch( NULL );
  239. SetThink( &CBaseEntity::SUB_Remove );
  240. SetNextThink( gpGlobals->curtime + 0.1f );
  241. return;
  242. }
  243. // UNDONE: The player pointer should come back after returning from another level
  244. if ( GetEnemy() == NULL )
  245. {// enemy is dead.
  246. GetSenses()->Look( 1024 );
  247. SetEnemy( BestEnemy() );
  248. }
  249. if ( GetEnemy() != NULL && FVisible( GetEnemy() ))
  250. {
  251. m_vecEnemyLKP = GetEnemy()->BodyTarget( GetAbsOrigin() );
  252. }
  253. else
  254. {
  255. m_vecEnemyLKP = m_vecEnemyLKP + GetAbsVelocity() * m_flFlySpeed * 0.1;
  256. }
  257. vecDirToEnemy = m_vecEnemyLKP - GetAbsOrigin();
  258. VectorNormalize( vecDirToEnemy );
  259. if ( GetAbsVelocity().Length() < 0.1 )
  260. vecFlightDir = vecDirToEnemy;
  261. else
  262. {
  263. vecFlightDir = GetAbsVelocity();
  264. VectorNormalize( vecFlightDir );
  265. }
  266. SetAbsVelocity( vecFlightDir + vecDirToEnemy );
  267. // measure how far the turn is, the wider the turn, the slow we'll go this time.
  268. flDelta = DotProduct ( vecFlightDir, vecDirToEnemy );
  269. if ( flDelta < 0.5 )
  270. {// hafta turn wide again. play sound
  271. CPASAttenuationFilter filter( this );
  272. EmitSound( filter, entindex(), "Hornet.Buzz" );
  273. }
  274. if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED )
  275. {// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far.
  276. flDelta = 0.25;
  277. }
  278. Vector vecVel = vecFlightDir + vecDirToEnemy;
  279. VectorNormalize( vecVel );
  280. if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_NPC) )
  281. {
  282. // random pattern only applies to hornets fired by monsters, not players.
  283. vecVel.x += random->RandomFloat ( -0.10, 0.10 );// scramble the flight dir a bit.
  284. vecVel.y += random->RandomFloat ( -0.10, 0.10 );
  285. vecVel.z += random->RandomFloat ( -0.10, 0.10 );
  286. }
  287. switch ( m_iHornetType )
  288. {
  289. case HORNET_TYPE_RED:
  290. SetAbsVelocity( vecVel * ( m_flFlySpeed * flDelta ) );// scale the dir by the ( speed * width of turn )
  291. SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) );
  292. break;
  293. default:
  294. Assert( false ); //fall through if release
  295. case HORNET_TYPE_ORANGE:
  296. SetAbsVelocity( vecVel * m_flFlySpeed );// do not have to slow down to turn.
  297. SetNextThink( gpGlobals->curtime + 0.1f );// fixed think time
  298. break;
  299. }
  300. QAngle angNewAngles;
  301. VectorAngles( GetAbsVelocity(), angNewAngles );
  302. SetAbsAngles( angNewAngles );
  303. SetSolid( SOLID_BBOX );
  304. // if hornet is close to the enemy, jet in a straight line for a half second.
  305. // (only in the single player game)
  306. if ( GetEnemy() != NULL && !g_pGameRules->IsMultiplayer() )
  307. {
  308. if ( flDelta >= 0.4 && ( GetAbsOrigin() - m_vecEnemyLKP ).Length() <= 300 )
  309. {
  310. CPVSFilter filter( GetAbsOrigin() );
  311. te->Sprite( filter, 0.0,
  312. &GetAbsOrigin(), // pos
  313. iHornetPuff, // model
  314. 0.2, //size
  315. 128 // brightness
  316. );
  317. CPASAttenuationFilter filter2( this );
  318. EmitSound( filter2, entindex(), "Hornet.Buzz" );
  319. SetAbsVelocity( GetAbsVelocity() * 2 );
  320. SetNextThink( gpGlobals->curtime + 1.0f );
  321. // don't attack again
  322. m_flStopAttack = gpGlobals->curtime;
  323. }
  324. }
  325. }