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