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.

430 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: This is the soldier version of the combine, analogous to the HL1 grunt.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_hull.h"
  8. #include "ai_motor.h"
  9. #include "npc_combines.h"
  10. #include "bitstring.h"
  11. #include "engine/IEngineSound.h"
  12. #include "soundent.h"
  13. #include "ndebugoverlay.h"
  14. #include "npcevent.h"
  15. #include "hl2/hl2_player.h"
  16. #include "game.h"
  17. #include "ammodef.h"
  18. #include "explode.h"
  19. #include "ai_memory.h"
  20. #include "Sprite.h"
  21. #include "soundenvelope.h"
  22. #include "weapon_physcannon.h"
  23. #include "hl2_gamerules.h"
  24. #include "gameweaponmanager.h"
  25. #include "vehicle_base.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. ConVar sk_combine_s_health( "sk_combine_s_health","0");
  29. ConVar sk_combine_s_kick( "sk_combine_s_kick","0");
  30. ConVar sk_combine_guard_health( "sk_combine_guard_health", "0");
  31. ConVar sk_combine_guard_kick( "sk_combine_guard_kick", "0");
  32. // Whether or not the combine guard should spawn health on death
  33. ConVar combine_guard_spawn_health( "combine_guard_spawn_health", "1" );
  34. extern ConVar sk_plr_dmg_buckshot;
  35. extern ConVar sk_plr_num_shotgun_pellets;
  36. //Whether or not the combine should spawn health on death
  37. ConVar combine_spawn_health( "combine_spawn_health", "1" );
  38. LINK_ENTITY_TO_CLASS( npc_combine_s, CNPC_CombineS );
  39. #define AE_SOLDIER_BLOCK_PHYSICS 20 // trying to block an incoming physics object
  40. extern Activity ACT_WALK_EASY;
  41. extern Activity ACT_WALK_MARCH;
  42. //-----------------------------------------------------------------------------
  43. // Purpose:
  44. //-----------------------------------------------------------------------------
  45. void CNPC_CombineS::Spawn( void )
  46. {
  47. Precache();
  48. SetModel( STRING( GetModelName() ) );
  49. if( IsElite() )
  50. {
  51. // Stronger, tougher.
  52. SetHealth( sk_combine_guard_health.GetFloat() );
  53. SetMaxHealth( sk_combine_guard_health.GetFloat() );
  54. SetKickDamage( sk_combine_guard_kick.GetFloat() );
  55. }
  56. else
  57. {
  58. SetHealth( sk_combine_s_health.GetFloat() );
  59. SetMaxHealth( sk_combine_s_health.GetFloat() );
  60. SetKickDamage( sk_combine_s_kick.GetFloat() );
  61. }
  62. CapabilitiesAdd( bits_CAP_ANIMATEDFACE );
  63. CapabilitiesAdd( bits_CAP_MOVE_SHOOT );
  64. CapabilitiesAdd( bits_CAP_DOORS_GROUP );
  65. BaseClass::Spawn();
  66. #if HL2_EPISODIC
  67. if (m_iUseMarch && !HasSpawnFlags(SF_NPC_START_EFFICIENT))
  68. {
  69. Msg( "Soldier %s is set to use march anim, but is not an efficient AI. The blended march anim can only be used for dead-ahead walks!\n", GetDebugName() );
  70. }
  71. #endif
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. // Input :
  76. // Output :
  77. //-----------------------------------------------------------------------------
  78. void CNPC_CombineS::Precache()
  79. {
  80. const char *pModelName = STRING( GetModelName() );
  81. if( !Q_stricmp( pModelName, "models/combine_super_soldier.mdl" ) )
  82. {
  83. m_fIsElite = true;
  84. }
  85. else
  86. {
  87. m_fIsElite = false;
  88. }
  89. if( !GetModelName() )
  90. {
  91. SetModelName( MAKE_STRING( "models/combine_soldier.mdl" ) );
  92. }
  93. PrecacheModel( STRING( GetModelName() ) );
  94. UTIL_PrecacheOther( "item_healthvial" );
  95. UTIL_PrecacheOther( "weapon_frag" );
  96. UTIL_PrecacheOther( "item_ammo_ar2_altfire" );
  97. BaseClass::Precache();
  98. }
  99. void CNPC_CombineS::DeathSound( const CTakeDamageInfo &info )
  100. {
  101. // NOTE: The response system deals with this at the moment
  102. if ( GetFlags() & FL_DISSOLVING )
  103. return;
  104. GetSentences()->Speak( "COMBINE_DIE", SENTENCE_PRIORITY_INVALID, SENTENCE_CRITERIA_ALWAYS );
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose: Soldiers use CAN_RANGE_ATTACK2 to indicate whether they can throw
  108. // a grenade. Because they check only every half-second or so, this
  109. // condition must persist until it is updated again by the code
  110. // that determines whether a grenade can be thrown, so prevent the
  111. // base class from clearing it out. (sjb)
  112. //-----------------------------------------------------------------------------
  113. void CNPC_CombineS::ClearAttackConditions( )
  114. {
  115. bool fCanRangeAttack2 = HasCondition( COND_CAN_RANGE_ATTACK2 );
  116. // Call the base class.
  117. BaseClass::ClearAttackConditions();
  118. if( fCanRangeAttack2 )
  119. {
  120. // We don't allow the base class to clear this condition because we
  121. // don't sense for it every frame.
  122. SetCondition( COND_CAN_RANGE_ATTACK2 );
  123. }
  124. }
  125. void CNPC_CombineS::PrescheduleThink( void )
  126. {
  127. /*//FIXME: This doesn't need to be in here, it's all debug info
  128. if( HasCondition( COND_HEAR_PHYSICS_DANGER ) )
  129. {
  130. // Don't react unless we see the item!!
  131. CSound *pSound = NULL;
  132. pSound = GetLoudestSoundOfType( SOUND_PHYSICS_DANGER );
  133. if( pSound )
  134. {
  135. if( FInViewCone( pSound->GetSoundReactOrigin() ) )
  136. {
  137. DevMsg( "OH CRAP!\n" );
  138. NDebugOverlay::Line( EyePosition(), pSound->GetSoundReactOrigin(), 0, 0, 255, false, 2.0f );
  139. }
  140. }
  141. }
  142. */
  143. BaseClass::PrescheduleThink();
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  147. // In the most cases the base implementation should be called first.
  148. //-----------------------------------------------------------------------------
  149. void CNPC_CombineS::BuildScheduleTestBits( void )
  150. {
  151. //Interrupt any schedule with physics danger (as long as I'm not moving or already trying to block)
  152. if ( m_flGroundSpeed == 0.0 && !IsCurSchedule( SCHED_FLINCH_PHYSICS ) )
  153. {
  154. SetCustomInterruptCondition( COND_HEAR_PHYSICS_DANGER );
  155. }
  156. BaseClass::BuildScheduleTestBits();
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. // Input :
  161. // Output :
  162. //-----------------------------------------------------------------------------
  163. int CNPC_CombineS::SelectSchedule ( void )
  164. {
  165. return BaseClass::SelectSchedule();
  166. }
  167. //-----------------------------------------------------------------------------
  168. //-----------------------------------------------------------------------------
  169. float CNPC_CombineS::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDamageInfo &info )
  170. {
  171. switch( iHitGroup )
  172. {
  173. case HITGROUP_HEAD:
  174. {
  175. // Soldiers take double headshot damage
  176. return 2.0f;
  177. }
  178. }
  179. return BaseClass::GetHitgroupDamageMultiplier( iHitGroup, info );
  180. }
  181. //-----------------------------------------------------------------------------
  182. //-----------------------------------------------------------------------------
  183. void CNPC_CombineS::HandleAnimEvent( animevent_t *pEvent )
  184. {
  185. switch( pEvent->event )
  186. {
  187. case AE_SOLDIER_BLOCK_PHYSICS:
  188. DevMsg( "BLOCKING!\n" );
  189. m_fIsBlocking = true;
  190. break;
  191. default:
  192. BaseClass::HandleAnimEvent( pEvent );
  193. break;
  194. }
  195. }
  196. void CNPC_CombineS::OnChangeActivity( Activity eNewActivity )
  197. {
  198. // Any new sequence stops us blocking.
  199. m_fIsBlocking = false;
  200. BaseClass::OnChangeActivity( eNewActivity );
  201. #if HL2_EPISODIC
  202. // Give each trooper a varied look for his march. Done here because if you do it earlier (eg Spawn, StartTask), the
  203. // pose param gets overwritten.
  204. if (m_iUseMarch)
  205. {
  206. SetPoseParameter("casual", RandomFloat());
  207. }
  208. #endif
  209. }
  210. void CNPC_CombineS::OnListened()
  211. {
  212. BaseClass::OnListened();
  213. if ( HasCondition( COND_HEAR_DANGER ) && HasCondition( COND_HEAR_PHYSICS_DANGER ) )
  214. {
  215. if ( HasInterruptCondition( COND_HEAR_DANGER ) )
  216. {
  217. ClearCondition( COND_HEAR_PHYSICS_DANGER );
  218. }
  219. }
  220. // debugging to find missed schedules
  221. #if 0
  222. if ( HasCondition( COND_HEAR_DANGER ) && !HasInterruptCondition( COND_HEAR_DANGER ) )
  223. {
  224. DevMsg("Ignore danger in %s\n", GetCurSchedule()->GetName() );
  225. }
  226. #endif
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Purpose:
  230. // Input : &info -
  231. // Output : Returns true on success, false on failure.
  232. //-----------------------------------------------------------------------------
  233. void CNPC_CombineS::Event_Killed( const CTakeDamageInfo &info )
  234. {
  235. // Don't bother if we've been told not to, or the player has a megaphyscannon
  236. if ( combine_spawn_health.GetBool() == false || PlayerHasMegaPhysCannon() )
  237. {
  238. BaseClass::Event_Killed( info );
  239. return;
  240. }
  241. CBasePlayer *pPlayer = ToBasePlayer( info.GetAttacker() );
  242. if ( !pPlayer )
  243. {
  244. CPropVehicleDriveable *pVehicle = dynamic_cast<CPropVehicleDriveable *>( info.GetAttacker() ) ;
  245. if ( pVehicle && pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer() )
  246. {
  247. pPlayer = assert_cast<CBasePlayer *>( pVehicle->GetDriver() );
  248. }
  249. }
  250. if ( pPlayer != NULL )
  251. {
  252. // Elites drop alt-fire ammo, so long as they weren't killed by dissolving.
  253. if( IsElite() )
  254. {
  255. #ifdef HL2_EPISODIC
  256. if ( HasSpawnFlags( SF_COMBINE_NO_AR2DROP ) == false )
  257. #endif
  258. {
  259. CBaseEntity *pItem = DropItem( "item_ammo_ar2_altfire", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
  260. if ( pItem )
  261. {
  262. IPhysicsObject *pObj = pItem->VPhysicsGetObject();
  263. if ( pObj )
  264. {
  265. Vector vel = RandomVector( -64.0f, 64.0f );
  266. AngularImpulse angImp = RandomAngularImpulse( -300.0f, 300.0f );
  267. vel[2] = 0.0f;
  268. pObj->AddVelocity( &vel, &angImp );
  269. }
  270. if( info.GetDamageType() & DMG_DISSOLVE )
  271. {
  272. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(pItem);
  273. if( pAnimating )
  274. {
  275. pAnimating->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL );
  276. }
  277. }
  278. else
  279. {
  280. WeaponManager_AddManaged( pItem );
  281. }
  282. }
  283. }
  284. }
  285. CHalfLife2 *pHL2GameRules = static_cast<CHalfLife2 *>(g_pGameRules);
  286. // Attempt to drop health
  287. if ( pHL2GameRules->NPC_ShouldDropHealth( pPlayer ) )
  288. {
  289. DropItem( "item_healthvial", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
  290. pHL2GameRules->NPC_DroppedHealth();
  291. }
  292. if ( HasSpawnFlags( SF_COMBINE_NO_GRENADEDROP ) == false )
  293. {
  294. // Attempt to drop a grenade
  295. if ( pHL2GameRules->NPC_ShouldDropGrenade( pPlayer ) )
  296. {
  297. DropItem( "weapon_frag", WorldSpaceCenter()+RandomVector(-4,4), RandomAngle(0,360) );
  298. pHL2GameRules->NPC_DroppedGrenade();
  299. }
  300. }
  301. }
  302. BaseClass::Event_Killed( info );
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose:
  306. // Input : &info -
  307. // Output : Returns true on success, false on failure.
  308. //-----------------------------------------------------------------------------
  309. bool CNPC_CombineS::IsLightDamage( const CTakeDamageInfo &info )
  310. {
  311. return BaseClass::IsLightDamage( info );
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose:
  315. // Input : &info -
  316. // Output : Returns true on success, false on failure.
  317. //-----------------------------------------------------------------------------
  318. bool CNPC_CombineS::IsHeavyDamage( const CTakeDamageInfo &info )
  319. {
  320. // Combine considers AR2 fire to be heavy damage
  321. if ( info.GetAmmoType() == GetAmmoDef()->Index("AR2") )
  322. return true;
  323. // 357 rounds are heavy damage
  324. if ( info.GetAmmoType() == GetAmmoDef()->Index("357") )
  325. return true;
  326. // Shotgun blasts where at least half the pellets hit me are heavy damage
  327. if ( info.GetDamageType() & DMG_BUCKSHOT )
  328. {
  329. int iHalfMax = sk_plr_dmg_buckshot.GetFloat() * sk_plr_num_shotgun_pellets.GetInt() * 0.5;
  330. if ( info.GetDamage() >= iHalfMax )
  331. return true;
  332. }
  333. // Rollermine shocks
  334. if( (info.GetDamageType() & DMG_SHOCK) && hl2_episodic.GetBool() )
  335. {
  336. return true;
  337. }
  338. return BaseClass::IsHeavyDamage( info );
  339. }
  340. #if HL2_EPISODIC
  341. //-----------------------------------------------------------------------------
  342. // Purpose: Translate base class activities into combot activites
  343. //-----------------------------------------------------------------------------
  344. Activity CNPC_CombineS::NPC_TranslateActivity( Activity eNewActivity )
  345. {
  346. // If the special ep2_outland_05 "use march" flag is set, use the more casual marching anim.
  347. if ( m_iUseMarch && eNewActivity == ACT_WALK )
  348. {
  349. eNewActivity = ACT_WALK_MARCH;
  350. }
  351. return BaseClass::NPC_TranslateActivity( eNewActivity );
  352. }
  353. //---------------------------------------------------------
  354. // Save/Restore
  355. //---------------------------------------------------------
  356. BEGIN_DATADESC( CNPC_CombineS )
  357. DEFINE_KEYFIELD( m_iUseMarch, FIELD_INTEGER, "usemarch" ),
  358. END_DATADESC()
  359. #endif