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.

413 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_basenpc.h"
  9. #include "ai_senses.h"
  10. #include "ai_squad.h"
  11. #include "grenade_homer.h"
  12. #include "grenade_pathfollower.h"
  13. #include "explode.h"
  14. #include "ndebugoverlay.h"
  15. #include "engine/IEngineSound.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. #define LAUNCHER_REST_TIME 3
  19. //------------------------------------
  20. // Spawnflags
  21. //------------------------------------
  22. #define SF_LAUNCHER_CHECK_LOS (1 << 16)
  23. //=========================================================
  24. // >> CNPC_Launcher
  25. //=========================================================
  26. class CNPC_Launcher : public CAI_BaseNPC
  27. {
  28. DECLARE_CLASS( CNPC_Launcher, CAI_BaseNPC );
  29. public:
  30. int m_nStartOn;
  31. string_t m_sMissileModel;
  32. string_t m_sLaunchSound;
  33. string_t m_sFlySound;
  34. int m_nSmokeTrail;
  35. bool m_bSmokeLaunch;
  36. int m_nLaunchDelay;
  37. float m_flLaunchSpeed;
  38. string_t m_sPathCornerName; // If following a path
  39. float m_flHomingSpeed;
  40. int m_nHomingStrength;
  41. float m_flHomingDelay; // How long before homing starts
  42. float m_flHomingRampUp; // How much time to ramp up to full homing
  43. float m_flHomingDuration; // How long does homing last
  44. float m_flHomingRampDown; // How long to ramp down to no homing
  45. float m_flMissileGravity;
  46. float m_flMinAttackDist;
  47. float m_flMaxAttackDist;
  48. float m_flSpinMagnitude;
  49. float m_flSpinSpeed;
  50. float m_flDamage;
  51. float m_flDamageRadius;
  52. // ----------------
  53. // Outputs
  54. // ----------------
  55. COutputEvent m_OnLaunch; // Triggered when missile is launched.
  56. // ----------------
  57. // Inputs
  58. // ----------------
  59. void InputTurnOn( inputdata_t &inputdata );
  60. void InputTurnOff( inputdata_t &inputdata );
  61. void InputLOSCheckOn( inputdata_t &inputdata );
  62. void InputLOSCheckOff( inputdata_t &inputdata );
  63. void InputSetEnemy( inputdata_t &inputdata );
  64. void InputClearEnemy( inputdata_t &inputdata );
  65. void InputFireOnce( inputdata_t &inputdata );
  66. void LauncherTurnOn(void);
  67. void Precache( void );
  68. void Spawn( void );
  69. Class_T Classify( void );
  70. bool IsValidEnemy(CBaseEntity *pTarget );
  71. void LaunchGrenade(CBaseEntity* pLauncher );
  72. void LauncherThink(void );
  73. bool FInViewCone( CBaseEntity *pEntity );
  74. int DrawDebugTextOverlays(void);
  75. DECLARE_DATADESC();
  76. };
  77. BEGIN_DATADESC( CNPC_Launcher )
  78. // Inputs
  79. DEFINE_KEYFIELD( m_nStartOn, FIELD_INTEGER, "StartOn" ),
  80. DEFINE_KEYFIELD( m_sMissileModel, FIELD_STRING, "MissileModel" ),
  81. DEFINE_KEYFIELD( m_sLaunchSound, FIELD_STRING, "LaunchSound" ),
  82. DEFINE_KEYFIELD( m_sFlySound, FIELD_STRING, "FlySound" ),
  83. DEFINE_KEYFIELD( m_nSmokeTrail, FIELD_INTEGER, "SmokeTrail" ),
  84. DEFINE_KEYFIELD( m_bSmokeLaunch, FIELD_BOOLEAN, "LaunchSmoke" ),
  85. DEFINE_KEYFIELD( m_nLaunchDelay, FIELD_INTEGER, "LaunchDelay" ),
  86. DEFINE_KEYFIELD( m_flLaunchSpeed, FIELD_FLOAT, "LaunchSpeed" ),
  87. DEFINE_KEYFIELD( m_sPathCornerName, FIELD_STRING, "PathCornerName" ),
  88. DEFINE_KEYFIELD( m_flHomingSpeed, FIELD_FLOAT, "HomingSpeed" ),
  89. DEFINE_KEYFIELD( m_nHomingStrength, FIELD_INTEGER, "HomingStrength" ),
  90. DEFINE_KEYFIELD( m_flHomingDelay, FIELD_FLOAT, "HomingDelay" ),
  91. DEFINE_KEYFIELD( m_flHomingRampUp, FIELD_FLOAT, "HomingRampUp" ),
  92. DEFINE_KEYFIELD( m_flHomingDuration, FIELD_FLOAT, "HomingDuration" ),
  93. DEFINE_KEYFIELD( m_flHomingRampDown, FIELD_FLOAT, "HomingRampDown" ),
  94. DEFINE_KEYFIELD( m_flGravity, FIELD_FLOAT, "Gravity" ),
  95. DEFINE_KEYFIELD( m_flMinAttackDist, FIELD_FLOAT, "MinRange" ),
  96. DEFINE_KEYFIELD( m_flMaxAttackDist, FIELD_FLOAT, "MaxRange" ),
  97. DEFINE_KEYFIELD( m_flSpinMagnitude, FIELD_FLOAT, "SpinMagnitude" ),
  98. DEFINE_KEYFIELD( m_flSpinSpeed, FIELD_FLOAT, "SpinSpeed" ),
  99. DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "Damage" ),
  100. DEFINE_KEYFIELD( m_flDamageRadius, FIELD_FLOAT, "DamageRadius" ),
  101. DEFINE_FIELD( m_flMissileGravity, FIELD_FLOAT ),
  102. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOn", InputTurnOn ),
  103. DEFINE_INPUTFUNC( FIELD_VOID, "TurnOff", InputTurnOff ),
  104. DEFINE_INPUTFUNC( FIELD_VOID, "LOSCheckOn", InputLOSCheckOn ),
  105. DEFINE_INPUTFUNC( FIELD_VOID, "LOSCheckOn", InputLOSCheckOn ),
  106. DEFINE_INPUTFUNC( FIELD_VOID, "FireOnce", InputFireOnce ),
  107. DEFINE_INPUTFUNC( FIELD_EHANDLE, "SetEnemyEntity", InputSetEnemy ),
  108. DEFINE_INPUTFUNC( FIELD_VOID, "ClearEnemyEntity", InputClearEnemy ),
  109. DEFINE_OUTPUT( m_OnLaunch, "OnLaunch" ),
  110. // Function Pointers
  111. DEFINE_THINKFUNC( LauncherThink ),
  112. END_DATADESC()
  113. LINK_ENTITY_TO_CLASS( npc_launcher, CNPC_Launcher );
  114. // ===================
  115. // Input Functions
  116. // ===================
  117. void CNPC_Launcher::InputTurnOn( inputdata_t &inputdata )
  118. {
  119. LauncherTurnOn();
  120. }
  121. void CNPC_Launcher::InputTurnOff( inputdata_t &inputdata )
  122. {
  123. SetThink(NULL);
  124. }
  125. void CNPC_Launcher::InputLOSCheckOn( inputdata_t &inputdata )
  126. {
  127. m_spawnflags |= SF_LAUNCHER_CHECK_LOS;
  128. }
  129. void CNPC_Launcher::InputLOSCheckOff( inputdata_t &inputdata )
  130. {
  131. m_spawnflags &= ~SF_LAUNCHER_CHECK_LOS;
  132. }
  133. void CNPC_Launcher::InputSetEnemy( inputdata_t &inputdata )
  134. {
  135. SetEnemy( inputdata.value.Entity().Get() );
  136. }
  137. void CNPC_Launcher::InputClearEnemy( inputdata_t &inputdata )
  138. {
  139. SetEnemy( NULL );
  140. }
  141. void CNPC_Launcher::InputFireOnce( inputdata_t &inputdata )
  142. {
  143. m_flNextAttack = 0;
  144. // If I using path following missiles just launch
  145. if (m_sPathCornerName != NULL_STRING)
  146. {
  147. LaunchGrenade(NULL);
  148. }
  149. // Otherwise only launch if I have an enemy
  150. else
  151. {
  152. LauncherThink();
  153. }
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose:
  157. //-----------------------------------------------------------------------------
  158. void CNPC_Launcher::Precache( void )
  159. {
  160. // This is a dummy model that is never used!
  161. PrecacheModel("models/player.mdl");
  162. PrecacheModel(STRING(m_sMissileModel));
  163. PrecacheScriptSound( STRING(m_sLaunchSound));
  164. PrecacheScriptSound( STRING(m_sFlySound));
  165. UTIL_PrecacheOther( "grenade_homer");
  166. BaseClass::Precache();
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Purpose:
  170. //-----------------------------------------------------------------------------
  171. void CNPC_Launcher::LauncherTurnOn(void)
  172. {
  173. SetThink(&CNPC_Launcher::LauncherThink);
  174. SetNextThink( gpGlobals->curtime );
  175. m_flNextAttack = 0;
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose:
  179. //-----------------------------------------------------------------------------
  180. void CNPC_Launcher::Spawn( void )
  181. {
  182. Precache();
  183. // This is a dummy model that is never used!
  184. SetModel( "models/player.mdl" );
  185. UTIL_SetSize(this, vec3_origin, vec3_origin);
  186. m_takedamage = DAMAGE_NO;
  187. if (m_nHomingStrength > 100)
  188. {
  189. m_nHomingStrength = 100;
  190. Warning("WARNING: NPC_Launcher Homing Strength must be between 0 and 100\n");
  191. }
  192. SetSolid( SOLID_NONE );
  193. SetMoveType( MOVETYPE_NONE );
  194. SetBloodColor( DONT_BLEED );
  195. AddEffects( EF_NODRAW );
  196. AddFlag( FL_NPC );
  197. CapabilitiesAdd( bits_CAP_SQUAD );
  198. InitRelationshipTable();
  199. if (m_nStartOn)
  200. {
  201. LauncherTurnOn();
  202. }
  203. // -------------------------------------------------------
  204. // If I form squads add me to a squad
  205. // -------------------------------------------------------
  206. // @TODO (toml 12-05-02): RECONCILE WITH SAVE/RESTORE
  207. if (!m_pSquad)
  208. {
  209. m_pSquad = g_AI_SquadManager.FindCreateSquad(this, m_SquadName);
  210. }
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. Class_T CNPC_Launcher::Classify( void )
  216. {
  217. return CLASS_NONE;
  218. }
  219. //------------------------------------------------------------------------------
  220. // Purpose:
  221. //------------------------------------------------------------------------------
  222. void CNPC_Launcher::LaunchGrenade( CBaseEntity* pEnemy )
  223. {
  224. // If a path following missile, create a path following missile
  225. if (m_sPathCornerName != NULL_STRING)
  226. {
  227. CGrenadePathfollower *pGrenade = CGrenadePathfollower::CreateGrenadePathfollower( m_sMissileModel, m_sFlySound, GetAbsOrigin(), vec3_angle, edict() );
  228. pGrenade->SetDamage(m_flDamage);
  229. pGrenade->SetDamageRadius(m_flDamageRadius);
  230. pGrenade->Launch(m_flLaunchSpeed,m_sPathCornerName);
  231. }
  232. else
  233. {
  234. Vector vUp;
  235. AngleVectors( GetAbsAngles(), NULL, NULL, &vUp );
  236. Vector vLaunchVelocity = (vUp * m_flLaunchSpeed);
  237. CGrenadeHomer *pGrenade = CGrenadeHomer::CreateGrenadeHomer( m_sMissileModel, m_sFlySound, GetAbsOrigin(), vec3_angle, edict() );
  238. pGrenade->Spawn( );
  239. pGrenade->SetSpin(m_flSpinMagnitude,m_flSpinSpeed);
  240. pGrenade->SetHoming((0.01*m_nHomingStrength),m_flHomingDelay,m_flHomingRampUp,m_flHomingDuration,m_flHomingRampDown);
  241. pGrenade->SetDamage(m_flDamage);
  242. pGrenade->SetDamageRadius(m_flDamageRadius);
  243. pGrenade->Launch(this,pEnemy,vLaunchVelocity,m_flHomingSpeed,GetGravity(),m_nSmokeTrail);
  244. }
  245. CPASAttenuationFilter filter( this, 0.3 );
  246. EmitSound_t ep;
  247. ep.m_nChannel = CHAN_WEAPON;
  248. ep.m_pSoundName = STRING(m_sLaunchSound);
  249. ep.m_SoundLevel = SNDLVL_NORM;
  250. EmitSound( filter, entindex(), ep );
  251. if (m_bSmokeLaunch)
  252. {
  253. UTIL_Smoke(GetAbsOrigin(), random->RandomInt(20,30), random->RandomInt(10,15));
  254. }
  255. m_flNextAttack = gpGlobals->curtime + LAUNCHER_REST_TIME;
  256. }
  257. //------------------------------------------------------------------------------
  258. // Purpose : Launcher sees 360 degrees
  259. //------------------------------------------------------------------------------
  260. bool CNPC_Launcher::FInViewCone( CBaseEntity *pEntity )
  261. {
  262. return true;
  263. }
  264. //------------------------------------------------------------------------------
  265. // Purpose : Override base class to check range and visibility
  266. //------------------------------------------------------------------------------
  267. bool CNPC_Launcher::IsValidEnemy( CBaseEntity *pTarget )
  268. {
  269. // ---------------------------------
  270. // Check range
  271. // ---------------------------------
  272. float flTargetDist = (GetAbsOrigin() - pTarget->GetAbsOrigin()).Length();
  273. if (flTargetDist < m_flMinAttackDist)
  274. {
  275. return false;
  276. }
  277. if (flTargetDist > m_flMaxAttackDist)
  278. {
  279. return false;
  280. }
  281. if (!FBitSet (m_spawnflags, SF_LAUNCHER_CHECK_LOS))
  282. {
  283. return true;
  284. }
  285. // ------------------------------------------------------
  286. // Make sure I can see the target from above my position
  287. // ------------------------------------------------------
  288. trace_t tr;
  289. // Trace from launch position to target position.
  290. // Use position above actual barral based on vertical launch speed
  291. Vector vStartPos = GetAbsOrigin() + Vector(0,0,0.2*m_flLaunchSpeed);
  292. Vector vEndPos = pTarget->GetAbsOrigin();
  293. AI_TraceLine( vStartPos, vEndPos, MASK_SHOT, pTarget, COLLISION_GROUP_NONE, &tr );
  294. if (tr.fraction == 1.0)
  295. {
  296. return true;
  297. }
  298. return false;
  299. }
  300. //------------------------------------------------------------------------------
  301. // Purpose :
  302. //------------------------------------------------------------------------------
  303. void CNPC_Launcher::LauncherThink( void )
  304. {
  305. if (gpGlobals->curtime > m_flNextAttack)
  306. {
  307. // If enemy was set, fire at enemy
  308. if (GetEnemy())
  309. {
  310. LaunchGrenade(GetEnemy());
  311. m_OnLaunch.FireOutput(GetEnemy(), this);
  312. m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay;
  313. }
  314. // Otherwise look for enemy to fire at
  315. else
  316. {
  317. GetSenses()->Look(m_flMaxAttackDist);
  318. CBaseEntity* pBestEnemy = BestEnemy();
  319. if (pBestEnemy)
  320. {
  321. LaunchGrenade(pBestEnemy);
  322. m_OnLaunch.FireOutput(pBestEnemy, this);
  323. m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay;
  324. }
  325. }
  326. }
  327. SetNextThink( gpGlobals->curtime + 0.1f );
  328. }
  329. //-----------------------------------------------------------------------------
  330. // Purpose: Draw any debug text overlays
  331. // Output : Current text offset from the top
  332. //-----------------------------------------------------------------------------
  333. int CNPC_Launcher::DrawDebugTextOverlays(void)
  334. {
  335. int text_offset = BaseClass::DrawDebugTextOverlays();
  336. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  337. {
  338. char tempstr[512];
  339. Q_snprintf(tempstr,sizeof(tempstr),"State: %s", (m_pfnThink) ? "On" : "Off" );
  340. EntityText(text_offset,tempstr,0);
  341. text_offset++;
  342. Q_snprintf(tempstr,sizeof(tempstr),"LOS: %s", (FBitSet (m_spawnflags, SF_LAUNCHER_CHECK_LOS)) ? "On" : "Off" );
  343. EntityText(text_offset,tempstr,0);
  344. text_offset++;
  345. }
  346. return text_offset;
  347. }