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.

473 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
  4. // events
  5. //
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "basecombatcharacter.h"
  10. #include "ai_basenpc.h"
  11. #include "decals.h"
  12. #include "filters.h"
  13. #include "npc_bullseye.h"
  14. #include "collisionutils.h"
  15. #include "igamesystem.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. class CBullseyeList : public CAutoGameSystem
  19. {
  20. public:
  21. CBullseyeList( char const *name ) : CAutoGameSystem( name )
  22. {
  23. }
  24. virtual void LevelShutdownPostEntity()
  25. {
  26. Clear();
  27. }
  28. void Clear()
  29. {
  30. m_list.Purge();
  31. }
  32. void AddToList( CNPC_Bullseye *pBullseye );
  33. void RemoveFromList( CNPC_Bullseye *pBullseye );
  34. CUtlVector< CNPC_Bullseye * > m_list;
  35. };
  36. void CBullseyeList::AddToList( CNPC_Bullseye *pBullseye )
  37. {
  38. m_list.AddToTail( pBullseye );
  39. }
  40. void CBullseyeList::RemoveFromList( CNPC_Bullseye *pBullseye )
  41. {
  42. int index = m_list.Find( pBullseye );
  43. if ( index != m_list.InvalidIndex() )
  44. {
  45. m_list.FastRemove( index );
  46. }
  47. }
  48. CBullseyeList g_BullseyeList( "CBullseyeList" );
  49. int FindBullseyesInCone( CBaseEntity **pList, int listMax, const Vector &coneOrigin, const Vector &coneAxis, float coneAngleCos, float coneLength )
  50. {
  51. if ( listMax <= 0 )
  52. return 0;
  53. int count = 0;
  54. for ( int i = g_BullseyeList.m_list.Count() - 1; i >= 0; --i )
  55. {
  56. CNPC_Bullseye *pTest = g_BullseyeList.m_list[i];
  57. if ( IsPointInCone( pTest->GetAbsOrigin(), coneOrigin, coneAxis, coneAngleCos, coneLength ) )
  58. {
  59. pList[count] = pTest;
  60. count++;
  61. if ( count >= listMax )
  62. break;
  63. }
  64. }
  65. return count;
  66. }
  67. ConVar sk_bullseye_health( "sk_bullseye_health","0");
  68. BEGIN_DATADESC( CNPC_Bullseye )
  69. DEFINE_FIELD( m_hPainPartner, FIELD_EHANDLE ),
  70. DEFINE_KEYFIELD( m_fAutoaimRadius, FIELD_FLOAT, "autoaimradius" ),
  71. DEFINE_KEYFIELD( m_flFieldOfView, FIELD_FLOAT, "minangle" ),
  72. DEFINE_KEYFIELD( m_flMinDistValidEnemy, FIELD_FLOAT, "mindist" ),
  73. // DEFINE_FIELD( m_bPerfectAccuracy, FIELD_BOOLEAN ), // Don't save
  74. // Function Pointers
  75. DEFINE_THINKFUNC( BullseyeThink ),
  76. DEFINE_INPUTFUNC( FIELD_VOID, "InputTargeted", InputTargeted ),
  77. DEFINE_INPUTFUNC( FIELD_VOID, "InputReleased", InputReleased ),
  78. // Outputs
  79. DEFINE_OUTPUT( m_OnTargeted, "OnTargeted"),
  80. DEFINE_OUTPUT( m_OnReleased, "OnReleased"),
  81. END_DATADESC()
  82. LINK_ENTITY_TO_CLASS( npc_bullseye, CNPC_Bullseye );
  83. //-----------------------------------------------------------------------------
  84. // Purpose: Constructor
  85. //-----------------------------------------------------------------------------
  86. CNPC_Bullseye::CNPC_Bullseye( void )
  87. {
  88. m_takedamage = DAMAGE_YES;
  89. m_iHealth = sk_bullseye_health.GetFloat();
  90. m_hPainPartner = NULL;
  91. g_BullseyeList.AddToList( this );
  92. m_flFieldOfView = 360;
  93. m_flMinDistValidEnemy = 0;
  94. }
  95. CNPC_Bullseye::~CNPC_Bullseye( void )
  96. {
  97. g_BullseyeList.RemoveFromList( this );
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. void CNPC_Bullseye::Precache( void )
  103. {
  104. BaseClass::Precache();
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose:
  108. //-----------------------------------------------------------------------------
  109. void CNPC_Bullseye::Spawn( void )
  110. {
  111. Precache();
  112. // This is a dummy model that is never used!
  113. UTIL_SetSize(this, Vector(-16,-16,-16), Vector(16,16,16));
  114. SetMoveType( MOVETYPE_NONE );
  115. SetBloodColor( BLOOD_COLOR_RED );
  116. ClearEffects();
  117. SetGravity( 0.0 );
  118. m_flFieldOfView = cos( DEG2RAD(m_flFieldOfView) / 2.0 );
  119. //Got blood?
  120. if ( m_spawnflags & SF_BULLSEYE_BLEED )
  121. {
  122. SetBloodColor(BLOOD_COLOR_RED);
  123. }
  124. else
  125. {
  126. SetBloodColor(DONT_BLEED);
  127. }
  128. AddFlag( FL_NPC );
  129. AddEFlags( EFL_NO_DISSOLVE );
  130. SetThink( &CNPC_Bullseye::BullseyeThink );
  131. SetNextThink( gpGlobals->curtime + 0.1f );
  132. SetSolid( SOLID_BBOX );
  133. AddSolidFlags( FSOLID_NOT_STANDABLE );
  134. if( m_spawnflags & SF_BULLSEYE_NONSOLID )
  135. {
  136. AddSolidFlags( FSOLID_NOT_SOLID );
  137. }
  138. if ( m_spawnflags & SF_BULLSEYE_VPHYSICSSHADOW )
  139. {
  140. VPhysicsInitShadow( false, false );
  141. }
  142. if( m_spawnflags & SF_BULLSEYE_NODAMAGE )
  143. {
  144. m_takedamage = DAMAGE_NO;
  145. }
  146. else
  147. {
  148. m_takedamage = DAMAGE_YES;
  149. }
  150. AddEffects( EF_NODRAW );
  151. //Check our water level
  152. PhysicsCheckWater();
  153. CapabilitiesAdd( bits_CAP_SIMPLE_RADIUS_DAMAGE );
  154. m_iMaxHealth = GetHealth();
  155. if( m_fAutoaimRadius > 0.0f )
  156. {
  157. // Make this an aimtarget, since it has some autoaim influence.
  158. AddFlag(FL_AIMTARGET);
  159. }
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose:
  163. //-----------------------------------------------------------------------------
  164. void CNPC_Bullseye::Activate( void )
  165. {
  166. BaseClass::Activate();
  167. if ( m_spawnflags & SF_BULLSEYE_PERFECTACC )
  168. {
  169. m_bPerfectAccuracy = true;
  170. }
  171. else
  172. {
  173. m_bPerfectAccuracy = false;
  174. }
  175. }
  176. //------------------------------------------------------------------------------
  177. // Purpose : Override so doesn't fall to ground when killed
  178. //------------------------------------------------------------------------------
  179. void CNPC_Bullseye::Event_Killed( const CTakeDamageInfo &info )
  180. {
  181. BaseClass::Event_Killed( info );
  182. if( GetParent() )
  183. {
  184. if( GetParent()->ClassMatches("prop_combine_ball") )
  185. {
  186. // If this bullseye is parented to a combine ball, explode the combine ball
  187. // and remove this bullseye.
  188. variant_t emptyVariant;
  189. GetParent()->AcceptInput( "explode", this, this, emptyVariant, 0 );
  190. // Unhook.
  191. SetParent(NULL);
  192. UTIL_Remove(this);
  193. return;
  194. }
  195. }
  196. SetMoveType( MOVETYPE_NONE );
  197. AddSolidFlags( FSOLID_NOT_SOLID );
  198. UTIL_SetSize(this, vec3_origin, vec3_origin );
  199. SetNextThink( gpGlobals->curtime + 0.1f );
  200. SetThink( &CBaseEntity::SUB_Remove );
  201. }
  202. //------------------------------------------------------------------------------
  203. // Purpose : Override base implimentation to let decals pass through
  204. // me onto the surface beneath
  205. // Input :
  206. // Output :
  207. //------------------------------------------------------------------------------
  208. void CNPC_Bullseye::DecalTrace( trace_t *pOldTrace, char const *decalName )
  209. {
  210. int index = decalsystem->GetDecalIndexForName( decalName );
  211. if ( index < 0 )
  212. return;
  213. // Get direction of original trace
  214. Vector vTraceDir = pOldTrace->endpos - pOldTrace->startpos;
  215. VectorNormalize(vTraceDir);
  216. // Create a new trace that passes through me
  217. Vector vStartTrace = pOldTrace->endpos - (1.0 * vTraceDir);
  218. Vector vEndTrace = pOldTrace->endpos + (MAX_TRACE_LENGTH * vTraceDir);
  219. trace_t pNewTrace;
  220. AI_TraceLine(vStartTrace, vEndTrace, MASK_SHOT, this, COLLISION_GROUP_NONE, &pNewTrace);
  221. CBroadcastRecipientFilter filter;
  222. te->Decal( filter, 0.0, &pNewTrace.endpos, &pNewTrace.startpos,
  223. ENTINDEX( pNewTrace.m_pEnt ), pNewTrace.hitbox, index );
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. //-----------------------------------------------------------------------------
  228. void CNPC_Bullseye::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
  229. {
  230. // Get direction of original trace
  231. Vector vTraceDir = pTrace->endpos - pTrace->startpos;
  232. VectorNormalize(vTraceDir);
  233. // Create a new trace that passes through me
  234. Vector vStartTrace = pTrace->endpos - (1.0 * vTraceDir);
  235. Vector vEndTrace = pTrace->endpos + (MAX_TRACE_LENGTH * vTraceDir);
  236. trace_t pNewTrace;
  237. AI_TraceLine(vStartTrace, vEndTrace, MASK_SHOT, this, COLLISION_GROUP_NONE, &pNewTrace);
  238. CBaseEntity *pEntity = pNewTrace.m_pEnt;
  239. // Only do this for BSP model entities
  240. if ( ( pEntity ) && ( pEntity->IsBSPModel() == false ) )
  241. return;
  242. BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName );
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Purpose:
  246. //
  247. //
  248. // Output :
  249. //-----------------------------------------------------------------------------
  250. Class_T CNPC_Bullseye::Classify( void )
  251. {
  252. return CLASS_BULLSEYE;
  253. }
  254. void CNPC_Bullseye::OnRestore( void )
  255. {
  256. if ( m_spawnflags & SF_BULLSEYE_VPHYSICSSHADOW )
  257. {
  258. IPhysicsObject *pObject = VPhysicsGetObject();
  259. if ( pObject == NULL )
  260. {
  261. VPhysicsInitShadow( false, false );
  262. }
  263. }
  264. BaseClass::OnRestore();
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose:
  268. //-----------------------------------------------------------------------------
  269. void CNPC_Bullseye::BullseyeThink( void )
  270. {
  271. ClearCondition( COND_LIGHT_DAMAGE );
  272. ClearCondition( COND_HEAVY_DAMAGE );
  273. }
  274. //-----------------------------------------------------------------------------
  275. //-----------------------------------------------------------------------------
  276. bool CNPC_Bullseye::CanBecomeRagdoll()
  277. {
  278. return false;
  279. }
  280. //-----------------------------------------------------------------------------
  281. //-----------------------------------------------------------------------------
  282. bool CNPC_Bullseye::CanBeAnEnemyOf( CBaseEntity *pEnemy )
  283. {
  284. static const float flFullFov = cos( DEG2RAD(360) / 2.0 );
  285. if ( fabsf( m_flFieldOfView - flFullFov ) > .01 )
  286. {
  287. if ( !FInViewCone( pEnemy ) )
  288. {
  289. return false;
  290. }
  291. }
  292. if ( m_flMinDistValidEnemy > 0 )
  293. {
  294. float distSq = ( GetAbsOrigin().AsVector2D() - pEnemy->GetAbsOrigin().AsVector2D() ).LengthSqr();
  295. if ( distSq < Square( m_flMinDistValidEnemy ) )
  296. {
  297. return false;
  298. }
  299. }
  300. return BaseClass::CanBeAnEnemyOf( pEnemy );
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: Bullseyes should always report light damage if any amount of damage is taken
  304. // Input : fDamage - amount of damage
  305. // bitsDamageType - damage type
  306. //-----------------------------------------------------------------------------
  307. bool CNPC_Bullseye::IsLightDamage( const CTakeDamageInfo &info )
  308. {
  309. return ( info.GetDamage() > 0 );
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose:
  313. // Input : *pAttacker -
  314. // flDamage -
  315. // &vecDir -
  316. // *ptr -
  317. // bitsDamageType -
  318. //-----------------------------------------------------------------------------
  319. void CNPC_Bullseye::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  320. {
  321. //If specified, we must be the enemy of the target
  322. if ( m_spawnflags & SF_BULLSEYE_ENEMYDAMAGEONLY )
  323. {
  324. CAI_BaseNPC *pInstigator = info.GetAttacker()->MyNPCPointer();
  325. if ( pInstigator == NULL )
  326. return;
  327. if ( pInstigator->GetEnemy() != this )
  328. return;
  329. }
  330. //We can bleed if we want to, we can leave decals behind...
  331. if ( ( m_spawnflags & SF_BULLSEYE_BLEED ) && ( m_takedamage == DAMAGE_NO ) )
  332. {
  333. TraceBleed( info.GetDamage(), vecDir, ptr, info.GetDamageType() );
  334. }
  335. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose:
  339. // Input : *pInflictor -
  340. // *pAttacker -
  341. // flDamage -
  342. // bitsDamageType -
  343. // Output : int
  344. //-----------------------------------------------------------------------------
  345. int CNPC_Bullseye::OnTakeDamage( const CTakeDamageInfo &info )
  346. {
  347. SetNextThink( gpGlobals->curtime );
  348. //If specified, we must be the enemy of the target
  349. if ( m_spawnflags & SF_BULLSEYE_ENEMYDAMAGEONLY )
  350. {
  351. CAI_BaseNPC *pInstigator = info.GetAttacker()->MyNPCPointer();
  352. if ( pInstigator == NULL )
  353. return 0;
  354. if ( pInstigator->GetEnemy() != this )
  355. return 0;
  356. }
  357. //If we're a pain proxy, send the damage through
  358. if ( m_hPainPartner != NULL )
  359. {
  360. m_hPainPartner->TakeDamage( info );
  361. //Fire all pain indicators but take no real damage
  362. CTakeDamageInfo subInfo = info;
  363. subInfo.SetDamage( 0 );
  364. return BaseClass::OnTakeDamage( subInfo );
  365. }
  366. return BaseClass::OnTakeDamage( info );
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose:
  370. // Input : *pOther -
  371. //-----------------------------------------------------------------------------
  372. void CNPC_Bullseye::SetPainPartner( CBaseEntity *pOther )
  373. {
  374. m_hPainPartner = pOther;
  375. }
  376. void CNPC_Bullseye::InputTargeted( inputdata_t &inputdata )
  377. {
  378. m_OnTargeted.FireOutput( inputdata.pActivator, inputdata.pCaller, 0 );
  379. }
  380. void CNPC_Bullseye::InputReleased( inputdata_t &inputdata )
  381. {
  382. m_OnReleased.FireOutput( inputdata.pActivator, inputdata.pCaller, 0 );
  383. }