Counter Strike : Global Offensive Source Code
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.

503 lines
20 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "takedamageinfo.h"
  9. #include "ammodef.h"
  10. #ifdef GAME_DLL
  11. #include "cs_player.h"
  12. #endif
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. ConVar phys_pushscale( "phys_pushscale", "1", FCVAR_REPLICATED );
  16. BEGIN_SIMPLE_DATADESC( CTakeDamageInfo )
  17. DEFINE_FIELD( m_vecDamageForce, FIELD_VECTOR ),
  18. DEFINE_FIELD( m_vecDamagePosition, FIELD_POSITION_VECTOR),
  19. DEFINE_FIELD( m_vecReportedPosition, FIELD_POSITION_VECTOR),
  20. DEFINE_FIELD( m_hInflictor, FIELD_EHANDLE),
  21. DEFINE_FIELD( m_hAttacker, FIELD_EHANDLE),
  22. DEFINE_FIELD( m_hWeapon, FIELD_EHANDLE),
  23. DEFINE_FIELD( m_flDamage, FIELD_FLOAT),
  24. DEFINE_FIELD( m_flMaxDamage, FIELD_FLOAT),
  25. DEFINE_FIELD( m_flBaseDamage, FIELD_FLOAT ),
  26. DEFINE_FIELD( m_bitsDamageType, FIELD_INTEGER),
  27. DEFINE_FIELD( m_iDamageCustom, FIELD_INTEGER),
  28. DEFINE_FIELD( m_iDamageStats, FIELD_INTEGER),
  29. DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER),
  30. DEFINE_FIELD( m_flRadius, FIELD_FLOAT),
  31. DEFINE_FIELD( m_iDamagedOtherPlayers, FIELD_INTEGER),
  32. END_DATADESC()
  33. void CTakeDamageInfo::Init( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, const Vector &reportedPosition, float flDamage, int bitsDamageType, int iCustomDamage, int iObjectsPenetrated )
  34. {
  35. m_hInflictor = pInflictor;
  36. if ( pAttacker )
  37. {
  38. m_hAttacker = pAttacker;
  39. }
  40. else
  41. {
  42. m_hAttacker = pInflictor;
  43. }
  44. m_hWeapon = pWeapon;
  45. m_flDamage = flDamage;
  46. m_flBaseDamage = BASEDAMAGE_NOT_SPECIFIED;
  47. m_bitsDamageType = bitsDamageType;
  48. m_iDamageCustom = iCustomDamage;
  49. m_flMaxDamage = flDamage;
  50. m_vecDamageForce = damageForce;
  51. m_vecDamagePosition = damagePosition;
  52. m_vecReportedPosition = reportedPosition;
  53. m_iAmmoType = -1;
  54. m_flRadius = 0.0f;
  55. m_iDamagedOtherPlayers = 0;
  56. m_iObjectsPenetrated = iObjectsPenetrated;
  57. #ifdef GAME_DLL
  58. m_uiBulletID = CCSPlayer::GetBulletGroup();
  59. #else
  60. m_uiBulletID = 0;
  61. #endif
  62. }
  63. CTakeDamageInfo::CTakeDamageInfo()
  64. {
  65. Init( NULL, NULL, NULL, vec3_origin, vec3_origin, vec3_origin, 0, 0, 0, 0 );
  66. }
  67. CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType, int iKillType, int iObjectsPenetrated )
  68. {
  69. Set( pInflictor, pAttacker, flDamage, bitsDamageType, iKillType, iObjectsPenetrated );
  70. }
  71. CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, float flDamage, int bitsDamageType, int iKillType, int iObjectsPenetrated )
  72. {
  73. Set( pInflictor, pAttacker, pWeapon, flDamage, bitsDamageType, iKillType, iObjectsPenetrated );
  74. }
  75. CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition, int iObjectsPenetrated )
  76. {
  77. Set( pInflictor, pAttacker, damageForce, damagePosition, flDamage, bitsDamageType, iKillType, reportedPosition, iObjectsPenetrated );
  78. }
  79. CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition, int iObjectsPenetrated )
  80. {
  81. Set( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, flDamage, bitsDamageType, iKillType, reportedPosition, iObjectsPenetrated );
  82. }
  83. void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType, int iKillType, int iObjectsPenetrated )
  84. {
  85. Init( pInflictor, pAttacker, NULL, vec3_origin, vec3_origin, vec3_origin, flDamage, bitsDamageType, iKillType, iObjectsPenetrated );
  86. }
  87. void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, float flDamage, int bitsDamageType, int iKillType, int iObjectsPenetrated )
  88. {
  89. Init( pInflictor, pAttacker, pWeapon, vec3_origin, vec3_origin, vec3_origin, flDamage, bitsDamageType, iKillType, iObjectsPenetrated );
  90. }
  91. void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition, int iObjectsPenetrated )
  92. {
  93. Set( pInflictor, pAttacker, NULL, damageForce, damagePosition, flDamage, bitsDamageType, iKillType, reportedPosition, iObjectsPenetrated );
  94. }
  95. void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition, int iObjectsPenetrated )
  96. {
  97. Vector vecReported = vec3_origin;
  98. if ( reportedPosition )
  99. {
  100. vecReported = *reportedPosition;
  101. }
  102. Init( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, vecReported, flDamage, bitsDamageType, iKillType, iObjectsPenetrated );
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Squirrel the damage value away as BaseDamage, which will later be used to
  106. // calculate damage force.
  107. //-----------------------------------------------------------------------------
  108. void CTakeDamageInfo::AdjustPlayerDamageInflictedForSkillLevel()
  109. {
  110. #ifndef CLIENT_DLL
  111. CopyDamageToBaseDamage();
  112. SetDamage( g_pGameRules->AdjustPlayerDamageInflicted(GetDamage()) );
  113. #endif
  114. }
  115. //-----------------------------------------------------------------------------
  116. //-----------------------------------------------------------------------------
  117. void CTakeDamageInfo::AdjustPlayerDamageTakenForSkillLevel()
  118. {
  119. #ifndef CLIENT_DLL
  120. CopyDamageToBaseDamage();
  121. g_pGameRules->AdjustPlayerDamageTaken(this);
  122. #endif
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Purpose: get the name of the ammo that caused damage
  126. // Note: returns the ammo name, or the classname of the object, or the model name in the case of physgun ammo.
  127. //-----------------------------------------------------------------------------
  128. const char *CTakeDamageInfo::GetAmmoName() const
  129. {
  130. const char *pszAmmoType;
  131. if ( m_iAmmoType >= 0 )
  132. {
  133. pszAmmoType = GetAmmoDef()->GetAmmoOfIndex( m_iAmmoType )->pName;
  134. }
  135. // no ammoType, so get the ammo name from the inflictor
  136. else if ( m_hInflictor != NULL )
  137. {
  138. pszAmmoType = m_hInflictor->GetClassname();
  139. // check for physgun ammo. unfortunate that this is in game_shared.
  140. if ( Q_strcmp( pszAmmoType, "prop_physics" ) == 0 )
  141. {
  142. pszAmmoType = STRING( m_hInflictor->GetModelName() );
  143. }
  144. }
  145. else
  146. {
  147. pszAmmoType = "Unknown";
  148. }
  149. return pszAmmoType;
  150. }
  151. // -------------------------------------------------------------------------------------------------- //
  152. // MultiDamage
  153. // Collects multiple small damages into a single damage
  154. // -------------------------------------------------------------------------------------------------- //
  155. BEGIN_SIMPLE_DATADESC_( CMultiDamage, CTakeDamageInfo )
  156. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE),
  157. END_DATADESC()
  158. CMultiDamage g_MultiDamage;
  159. CMultiDamage::CMultiDamage()
  160. {
  161. m_hTarget = NULL;
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose:
  165. //-----------------------------------------------------------------------------
  166. void CMultiDamage::Init( CBaseEntity *pTarget, CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, const Vector &reportedPosition, float flDamage, int bitsDamageType, int iKillType, int iObjectsPenetrated )
  167. {
  168. m_hTarget = pTarget;
  169. BaseClass::Init( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, reportedPosition, flDamage, bitsDamageType, iKillType, iObjectsPenetrated );
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose: Resets the global multi damage accumulator
  173. //-----------------------------------------------------------------------------
  174. void ClearMultiDamage( void )
  175. {
  176. g_MultiDamage.Init( NULL, NULL, NULL, NULL, vec3_origin, vec3_origin, vec3_origin, 0, 0, 0, 0 );
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose: inflicts contents of global multi damage register on gMultiDamage.pEntity
  180. //-----------------------------------------------------------------------------
  181. void ApplyMultiDamage( void )
  182. {
  183. Vector vecSpot1;//where blood comes from
  184. Vector vecDir;//direction blood should go
  185. trace_t tr;
  186. if ( !g_MultiDamage.GetTarget() )
  187. return;
  188. #ifdef GAME_DLL
  189. const CBaseEntity *host = te->GetSuppressHost();
  190. te->SetSuppressHost( NULL );
  191. #endif
  192. g_MultiDamage.GetTarget()->TakeDamage( g_MultiDamage );
  193. #ifdef GAME_DLL
  194. te->SetSuppressHost( (CBaseEntity*)host );
  195. #endif
  196. // Damage is done, clear it out
  197. ClearMultiDamage();
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose: Add damage to the existing multidamage, and apply if it won't fit
  201. //-----------------------------------------------------------------------------
  202. void AddMultiDamage( const CTakeDamageInfo &info, CBaseEntity *pEntity )
  203. {
  204. if ( !pEntity )
  205. return;
  206. if ( pEntity != g_MultiDamage.GetTarget() )
  207. {
  208. ApplyMultiDamage();
  209. g_MultiDamage.Init( pEntity, info.GetInflictor(), info.GetAttacker(), info.GetWeapon(), vec3_origin, vec3_origin, vec3_origin, 0.0, info.GetDamageType(), info.GetDamageCustom(), info.GetObjectsPenetrated() );
  210. g_MultiDamage.SetDamagedOtherPlayers( info.GetDamagedOtherPlayers() );
  211. }
  212. g_MultiDamage.AddDamageType( info.GetDamageType() );
  213. g_MultiDamage.SetDamage( g_MultiDamage.GetDamage() + info.GetDamage() );
  214. g_MultiDamage.SetDamageForce( g_MultiDamage.GetDamageForce() + info.GetDamageForce() );
  215. g_MultiDamage.SetDamagePosition( info.GetDamagePosition() );
  216. g_MultiDamage.SetReportedPosition( info.GetReportedPosition() );
  217. g_MultiDamage.SetMaxDamage( MAX( g_MultiDamage.GetMaxDamage(), info.GetMaxDamage() ) );
  218. g_MultiDamage.SetAmmoType( info.GetAmmoType() );
  219. g_MultiDamage.SetBulletID( info.GetBulletID(), info.GetRecoilIndex() );
  220. bool bHasPhysicsForceDamage = !g_pGameRules->Damage_NoPhysicsForce( info.GetDamageType() );
  221. if ( bHasPhysicsForceDamage && g_MultiDamage.GetDamageType() != DMG_GENERIC )
  222. {
  223. // If you hit this assert, you've called TakeDamage with a damage type that requires a physics damage
  224. // force & position without specifying one or both of them. Decide whether your damage that's causing
  225. // this is something you believe should impart physics force on the receiver. If it is, you need to
  226. // setup the damage force & position inside the CTakeDamageInfo (Utility functions for this are in
  227. // takedamageinfo.cpp. If you think the damage shouldn't cause force (unlikely!) then you can set the
  228. // damage type to DMG_GENERIC, or | DMG_CRUSH if you need to preserve the damage type for purposes of HUD display.
  229. if ( g_MultiDamage.GetDamageForce() == vec3_origin || g_MultiDamage.GetDamagePosition() == vec3_origin )
  230. {
  231. static int warningCount = 0;
  232. if ( ++warningCount < 10 )
  233. {
  234. if ( g_MultiDamage.GetDamageForce() == vec3_origin )
  235. {
  236. Warning( "AddMultiDamage: g_MultiDamage.GetDamageForce() == vec3_origin\n" );
  237. }
  238. if ( g_MultiDamage.GetDamagePosition() == vec3_origin)
  239. {
  240. Warning( "AddMultiDamage: g_MultiDamage.GetDamagePosition() == vec3_origin\n" );
  241. }
  242. }
  243. }
  244. }
  245. }
  246. //============================================================================================================
  247. // Utility functions for physics damage force calculation
  248. //============================================================================================================
  249. //-----------------------------------------------------------------------------
  250. // Purpose: Returns an impulse scale required to push an object.
  251. // Input : flTargetMass - Mass of the target object, in kg
  252. // flDesiredSpeed - Desired speed of the target, in inches/sec.
  253. //-----------------------------------------------------------------------------
  254. float ImpulseScale( float flTargetMass, float flDesiredSpeed )
  255. {
  256. return (flTargetMass * flDesiredSpeed);
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Purpose: Fill out a takedamageinfo with a damage force for an explosive
  260. //-----------------------------------------------------------------------------
  261. void CalculateExplosiveDamageForce( CTakeDamageInfo *info, const Vector &vecDir, const Vector &vecForceOrigin, float flScale )
  262. {
  263. info->SetDamagePosition( vecForceOrigin );
  264. float flClampForce = ImpulseScale( 75, 400 );
  265. // Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage
  266. float flForceScale = info->GetBaseDamage() * ImpulseScale( 75, 4 );
  267. if( flForceScale > flClampForce )
  268. flForceScale = flClampForce;
  269. // Fudge blast forces a little bit, so that each
  270. // victim gets a slightly different trajectory.
  271. // This simulates features that usually vary from
  272. // person-to-person variables such as bodyweight,
  273. // which are all indentical for characters using the same model.
  274. flForceScale *= random->RandomFloat( 0.85, 1.15 );
  275. // Calculate the vector and stuff it into the takedamageinfo
  276. Vector vecForce = vecDir;
  277. VectorNormalize( vecForce );
  278. vecForce *= flForceScale;
  279. vecForce *= phys_pushscale.GetFloat();
  280. vecForce *= flScale;
  281. info->SetDamageForce( vecForce );
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: Fill out a takedamageinfo with a damage force for a bullet impact
  285. //-----------------------------------------------------------------------------
  286. void CalculateBulletDamageForce( CTakeDamageInfo *info, int iBulletType, const Vector &vecBulletDir, const Vector &vecForceOrigin, float flScale )
  287. {
  288. info->SetDamagePosition( vecForceOrigin );
  289. Vector vecForce = vecBulletDir;
  290. VectorNormalize( vecForce );
  291. vecForce *= GetAmmoDef()->DamageForce( iBulletType );
  292. vecForce *= phys_pushscale.GetFloat();
  293. vecForce *= flScale;
  294. info->SetDamageForce( vecForce );
  295. Assert(vecForce!=vec3_origin);
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose: Fill out a takedamageinfo with a damage force for a melee impact
  299. //-----------------------------------------------------------------------------
  300. void CalculateMeleeDamageForce( CTakeDamageInfo *info, const Vector &vecMeleeDir, const Vector &vecForceOrigin, float flScale )
  301. {
  302. info->SetDamagePosition( vecForceOrigin );
  303. // Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage
  304. float flForceScale = info->GetBaseDamage() * ImpulseScale( 75, 4 );
  305. Vector vecForce = vecMeleeDir;
  306. VectorNormalize( vecForce );
  307. vecForce *= flForceScale;
  308. vecForce *= phys_pushscale.GetFloat();
  309. vecForce *= flScale;
  310. info->SetDamageForce( vecForce );
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Purpose: Try and guess the physics force to use.
  314. // This shouldn't be used for any damage where the damage force is unknown.
  315. // i.e. only use it for mapmaker specified damages.
  316. //-----------------------------------------------------------------------------
  317. void GuessDamageForce( CTakeDamageInfo *info, const Vector &vecForceDir, const Vector &vecForceOrigin, float flScale )
  318. {
  319. if ( info->GetDamageType() & DMG_BULLET )
  320. {
  321. CalculateBulletDamageForce( info, GetAmmoDef()->Index("SMG1"), vecForceDir, vecForceOrigin, flScale );
  322. }
  323. else if ( info->GetDamageType() & DMG_BLAST )
  324. {
  325. CalculateExplosiveDamageForce( info, vecForceDir, vecForceOrigin, flScale );
  326. }
  327. else
  328. {
  329. CalculateMeleeDamageForce( info, vecForceDir, vecForceOrigin, flScale );
  330. }
  331. }
  332. // Debug functions for printing out damage types
  333. // This table maps the DMG_* defines to their strings such that
  334. // for DMG_XXX = i << x then table[i] = string for DMG_XXX
  335. static const char * const s_DamageTypeToStrTable[] =
  336. {
  337. "GENERIC",
  338. "CRUSH",
  339. "BULLET",
  340. "SLASH",
  341. "BURN",
  342. "VEHICLE",
  343. "FALL",
  344. "BLAST",
  345. "CLUB",
  346. "SHOCK",
  347. "SONIC",
  348. "ENERGYBEAM",
  349. "PREVENT_PHYSICS_FORCE",
  350. "NEVERGIB",
  351. "ALWAYSGIB",
  352. "DROWN",
  353. "PARALYZE",
  354. "NERVEGAS",
  355. "POISON",
  356. "RADIATION",
  357. "DROWNRECOVER",
  358. "ACID",
  359. "SLOWBURN",
  360. "REMOVENORAGDOLL",
  361. "PHYSGUN",
  362. "PLASMA",
  363. "AIRBOAT",
  364. "DISSOLVE",
  365. "BLAST_SURFACE",
  366. "DIRECT",
  367. "BUCKSHOT"
  368. };
  369. #define DAMAGE_TYPE_STR_TABLE_ENTRIES 31 // number of entries in table above
  370. void CTakeDamageInfo::DebugGetDamageTypeString(unsigned int damageType, char *outbuf, int outbuflength )
  371. {
  372. Assert(outbuflength > 0);
  373. // we need to use snprintf to actually copy out the strings here because that's the only function that returns
  374. // how much text was output
  375. if ( damageType == 0 )
  376. {
  377. int charsWrit = Q_snprintf(outbuf, outbuflength, "%s", s_DamageTypeToStrTable[0]);
  378. outbuflength -= charsWrit;
  379. outbuf += charsWrit; // advance the output pointer (now it sits on the null terminator)
  380. }
  381. // loop through the other entries in the table
  382. for (int i = 0;
  383. outbuflength > 0 && i < (DAMAGE_TYPE_STR_TABLE_ENTRIES - 1);
  384. ++i )
  385. {
  386. if ( damageType & (1 << i) )
  387. {
  388. // this bit was set. Print the corresponding entry from the table
  389. // (the index is +1 because entry 1 in the table corresponds to 1 << 0)
  390. int charsWrit = Q_snprintf(outbuf, outbuflength, "%s ", s_DamageTypeToStrTable[i + 1]);
  391. outbuflength -= charsWrit; // reduce the chars left
  392. outbuf += charsWrit; // advance the output pointer (now it sits on the null terminator)
  393. }
  394. }
  395. }
  396. /*
  397. // instant damage
  398. #define DMG_GENERIC 0 // generic damage was done
  399. #define DMG_CRUSH (1 << 0) // crushed by falling or moving object.
  400. // NOTE: It's assumed crush damage is occurring as a result of physics collision, so no extra physics force is generated by crush damage.
  401. // DON'T use DMG_CRUSH when damaging entities unless it's the result of a physics collision. You probably want DMG_CLUB instead.
  402. #define DMG_BULLET (1 << 1) // shot
  403. #define DMG_SLASH (1 << 2) // cut, clawed, stabbed
  404. #define DMG_BURN (1 << 3) // heat burned
  405. #define DMG_VEHICLE (1 << 4) // hit by a vehicle
  406. #define DMG_FALL (1 << 5) // fell too far
  407. #define DMG_BLAST (1 << 6) // explosive blast damage
  408. #define DMG_CLUB (1 << 7) // crowbar, punch, headbutt
  409. #define DMG_SHOCK (1 << 8) // electric shock
  410. #define DMG_SONIC (1 << 9) // sound pulse shockwave
  411. #define DMG_ENERGYBEAM (1 << 10) // laser or other high energy beam
  412. #define DMG_PREVENT_PHYSICS_FORCE (1 << 11) // Prevent a physics force
  413. #define DMG_NEVERGIB (1 << 12) // with this bit OR'd in, no damage type will be able to gib victims upon death
  414. #define DMG_ALWAYSGIB (1 << 13) // with this bit OR'd in, any damage type can be made to gib victims upon death.
  415. #define DMG_DROWN (1 << 14) // Drowning
  416. #define DMG_PARALYZE (1 << 15) // slows affected creature down
  417. #define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad
  418. #define DMG_POISON (1 << 17) // blood poisoning - heals over time like drowning damage
  419. #define DMG_RADIATION (1 << 18) // radiation exposure
  420. #define DMG_DROWNRECOVER (1 << 19) // drowning recovery
  421. #define DMG_ACID (1 << 20) // toxic chemicals or acid burns
  422. #define DMG_SLOWBURN (1 << 21) // in an oven
  423. #define DMG_REMOVENORAGDOLL (1<<22) // with this bit OR'd in, no ragdoll will be created, and the target will be quietly removed.
  424. // use this to kill an entity that you've already got a server-side ragdoll for
  425. #define DMG_PHYSGUN (1<<23) // Hit by manipulator. Usually doesn't do any damage.
  426. #define DMG_PLASMA (1<<24) // Shot by Cremator
  427. #define DMG_AIRBOAT (1<<25) // Hit by the airboat's gun
  428. #define DMG_DISSOLVE (1<<26) // Dissolving!
  429. #define DMG_BLAST_SURFACE (1<<27) // A blast on the surface of water that cannot harm things underwater
  430. #define DMG_DIRECT (1<<28)
  431. #define DMG_BUCKSHOT (1<<29) // not quite a bullet. Little, rounder, different.
  432. */