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.

307 lines
7.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A slow-moving, once-human headcrab victim with only melee attacks.
  4. //
  5. // UNDONE: Make head take 100% damage, body take 30% damage.
  6. // UNDONE: Don't flinch every time you get hit.
  7. //
  8. // $NoKeywords: $
  9. //=============================================================================//
  10. #include "cbase.h"
  11. #include "game.h"
  12. #include "ai_default.h"
  13. #include "ai_schedule.h"
  14. #include "ai_hull.h"
  15. #include "ai_route.h"
  16. #include "npcevent.h"
  17. #include "hl1_npc_zombie.h"
  18. #include "gib.h"
  19. //#include "AI_Interactions.h"
  20. #include "ndebugoverlay.h"
  21. #include "vstdlib/random.h"
  22. #include "engine/IEngineSound.h"
  23. ConVar sk_zombie_health( "sk_zombie_health","50");
  24. ConVar sk_zombie_dmg_one_slash( "sk_zombie_dmg_one_slash", "20" );
  25. ConVar sk_zombie_dmg_both_slash( "sk_zombie_dmg_both_slash", "40" );
  26. LINK_ENTITY_TO_CLASS( monster_zombie, CNPC_Zombie );
  27. //=========================================================
  28. // Spawn
  29. //=========================================================
  30. void CNPC_Zombie::Spawn()
  31. {
  32. Precache( );
  33. SetModel( "models/zombie.mdl" );
  34. SetRenderColor( 255, 255, 255, 255 );
  35. SetHullType(HULL_HUMAN);
  36. SetHullSizeNormal();
  37. SetSolid( SOLID_BBOX );
  38. AddSolidFlags( FSOLID_NOT_STANDABLE );
  39. SetMoveType( MOVETYPE_STEP );
  40. m_bloodColor = BLOOD_COLOR_GREEN;
  41. m_iHealth = sk_zombie_health.GetFloat();
  42. //pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
  43. m_flFieldOfView = 0.5;
  44. m_NPCState = NPC_STATE_NONE;
  45. CapabilitiesClear();
  46. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_DOORS_GROUP );
  47. NPCInit();
  48. }
  49. //=========================================================
  50. // Precache - precaches all resources this monster needs
  51. //=========================================================
  52. void CNPC_Zombie::Precache()
  53. {
  54. PrecacheModel( "models/zombie.mdl" );
  55. PrecacheScriptSound( "Zombie.AttackHit" );
  56. PrecacheScriptSound( "Zombie.AttackMiss" );
  57. PrecacheScriptSound( "Zombie.Pain" );
  58. PrecacheScriptSound( "Zombie.Idle" );
  59. PrecacheScriptSound( "Zombie.Alert" );
  60. PrecacheScriptSound( "Zombie.Attack" );
  61. BaseClass::Precache();
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Purpose: Returns this monster's place in the relationship table.
  65. //-----------------------------------------------------------------------------
  66. Class_T CNPC_Zombie::Classify( void )
  67. {
  68. return CLASS_ALIEN_MONSTER;
  69. }
  70. //=========================================================
  71. // HandleAnimEvent - catches the monster-specific messages
  72. // that occur when tagged animation frames are played.
  73. //=========================================================
  74. void CNPC_Zombie::HandleAnimEvent( animevent_t *pEvent )
  75. {
  76. Vector v_forward, v_right;
  77. switch( pEvent->event )
  78. {
  79. case ZOMBIE_AE_ATTACK_RIGHT:
  80. {
  81. // do stuff for this event.
  82. // ALERT( at_console, "Slash right!\n" );
  83. Vector vecMins = GetHullMins();
  84. Vector vecMaxs = GetHullMaxs();
  85. vecMins.z = vecMins.x;
  86. vecMaxs.z = vecMaxs.x;
  87. CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
  88. CPASAttenuationFilter filter( this );
  89. if ( pHurt )
  90. {
  91. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  92. {
  93. pHurt->ViewPunch( QAngle( 5, 0, 18 ) );
  94. GetVectors( &v_forward, &v_right, NULL );
  95. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
  96. }
  97. // Play a random attack hit sound
  98. EmitSound( filter, entindex(), "Zombie.AttackHit" );
  99. }
  100. else // Play a random attack miss sound
  101. EmitSound( filter, entindex(), "Zombie.AttackMiss" );
  102. if ( random->RandomInt( 0, 1 ) )
  103. AttackSound();
  104. }
  105. break;
  106. case ZOMBIE_AE_ATTACK_LEFT:
  107. {
  108. // do stuff for this event.
  109. // ALERT( at_console, "Slash left!\n" );
  110. Vector vecMins = GetHullMins();
  111. Vector vecMaxs = GetHullMaxs();
  112. vecMins.z = vecMins.x;
  113. vecMaxs.z = vecMaxs.x;
  114. CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_one_slash.GetFloat(), DMG_SLASH );
  115. CPASAttenuationFilter filter2( this );
  116. if ( pHurt )
  117. {
  118. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  119. {
  120. pHurt->ViewPunch( QAngle ( 5, 0, -18 ) );
  121. GetVectors( &v_forward, &v_right, NULL );
  122. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
  123. }
  124. EmitSound( filter2, entindex(), "Zombie.AttackHit" );
  125. }
  126. else
  127. {
  128. EmitSound( filter2, entindex(), "Zombie.AttackMiss" );
  129. }
  130. if ( random->RandomInt( 0,1 ) )
  131. AttackSound();
  132. }
  133. break;
  134. case ZOMBIE_AE_ATTACK_BOTH:
  135. {
  136. // do stuff for this event.
  137. Vector vecMins = GetHullMins();
  138. Vector vecMaxs = GetHullMaxs();
  139. vecMins.z = vecMins.x;
  140. vecMaxs.z = vecMaxs.x;
  141. CBaseEntity *pHurt = CheckTraceHullAttack( 70, vecMins, vecMaxs, sk_zombie_dmg_both_slash.GetFloat(), DMG_SLASH );
  142. CPASAttenuationFilter filter3( this );
  143. if ( pHurt )
  144. {
  145. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  146. {
  147. pHurt->ViewPunch( QAngle ( 5, 0, 0 ) );
  148. GetVectors( &v_forward, &v_right, NULL );
  149. pHurt->SetAbsVelocity( pHurt->GetAbsVelocity() - v_right * 100 );
  150. }
  151. EmitSound( filter3, entindex(), "Zombie.AttackHit" );
  152. }
  153. else
  154. EmitSound( filter3, entindex(),"Zombie.AttackMiss" );
  155. if ( random->RandomInt( 0,1 ) )
  156. AttackSound();
  157. }
  158. break;
  159. default:
  160. BaseClass::HandleAnimEvent( pEvent );
  161. break;
  162. }
  163. }
  164. static float DamageForce( const Vector &size, float damage )
  165. {
  166. float force = damage * ((32 * 32 * 72.0) / (size.x * size.y * size.z)) * 5;
  167. if ( force > 1000.0)
  168. {
  169. force = 1000.0;
  170. }
  171. return force;
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Purpose:
  175. // Input : pInflictor -
  176. // pAttacker -
  177. // flDamage -
  178. // bitsDamageType -
  179. // Output : int
  180. //-----------------------------------------------------------------------------
  181. int CNPC_Zombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  182. {
  183. CTakeDamageInfo info = inputInfo;
  184. // Take 30% damage from bullets
  185. if ( info.GetDamageType() == DMG_BULLET )
  186. {
  187. Vector vecDir = GetAbsOrigin() - info.GetInflictor()->WorldSpaceCenter();
  188. VectorNormalize( vecDir );
  189. float flForce = DamageForce( WorldAlignSize(), info.GetDamage() );
  190. SetAbsVelocity( GetAbsVelocity() + vecDir * flForce );
  191. info.ScaleDamage( 0.3f );
  192. }
  193. // HACK HACK -- until we fix this.
  194. if ( IsAlive() )
  195. PainSound( info );
  196. return BaseClass::OnTakeDamage_Alive( info );
  197. }
  198. void CNPC_Zombie::PainSound( const CTakeDamageInfo &info )
  199. {
  200. if ( random->RandomInt(0,5) < 2)
  201. {
  202. CPASAttenuationFilter filter( this );
  203. EmitSound( filter, entindex(), "Zombie.Pain" );
  204. }
  205. }
  206. void CNPC_Zombie::AlertSound( void )
  207. {
  208. CPASAttenuationFilter filter( this );
  209. EmitSound( filter, entindex(), "Zombie.Alert" );
  210. }
  211. void CNPC_Zombie::IdleSound( void )
  212. {
  213. // Play a random idle sound
  214. CPASAttenuationFilter filter( this );
  215. EmitSound( filter, entindex(), "Zombie.Idle" );
  216. }
  217. void CNPC_Zombie::AttackSound( void )
  218. {
  219. // Play a random attack sound
  220. CPASAttenuationFilter filter( this );
  221. EmitSound( filter, entindex(), "Zombie.Attack" );
  222. }
  223. int CNPC_Zombie::MeleeAttack1Conditions ( float flDot, float flDist )
  224. {
  225. if ( flDist > 64)
  226. {
  227. return COND_TOO_FAR_TO_ATTACK;
  228. }
  229. else if (flDot < 0.7)
  230. {
  231. return 0;
  232. }
  233. else if (GetEnemy() == NULL)
  234. {
  235. return 0;
  236. }
  237. return COND_CAN_MELEE_ATTACK1;
  238. }
  239. void CNPC_Zombie::RemoveIgnoredConditions ( void )
  240. {
  241. if ( GetActivity() == ACT_MELEE_ATTACK1 )
  242. {
  243. // Nothing stops an attacking zombie.
  244. ClearCondition( COND_LIGHT_DAMAGE );
  245. ClearCondition( COND_HEAVY_DAMAGE );
  246. }
  247. if (( GetActivity() == ACT_SMALL_FLINCH ) || ( GetActivity() == ACT_BIG_FLINCH ))
  248. {
  249. if (m_flNextFlinch < gpGlobals->curtime)
  250. m_flNextFlinch = gpGlobals->curtime + ZOMBIE_FLINCH_DELAY;
  251. }
  252. BaseClass::RemoveIgnoredConditions();
  253. }