View difference between Paste ID: A8E3fEH0 and qYxSshy3
SHOW: | | - or go back to the newest paste.
1
#include maps\mp\_utility;
2
#include maps\mp\gametypes\_hud_util;
3
#include common_scripts\utility;
4
5
6
isSwitchingTeams()
7
{
8
	if ( isDefined( self.switching_teams ) )
9
		return true;
10
11
	return false;
12
}
13
14
15
isTeamSwitchBalanced()
16
{
17
	playerCounts = self maps\mp\gametypes\_teams::CountPlayers();
18
	playerCounts[ self.leaving_team ] -- ;
19
	playerCounts[ self.joining_team ]++ ;
20
21
	return( ( playerCounts[ self.joining_team ] - playerCounts[ self.leaving_team ] ) < 2 );
22
}
23
24
25
isFriendlyFire( victim, attacker )
26
{
27
	if ( !level.teamBased )
28
		return false;
29
	
30
	if ( !isDefined( attacker ) )
31
		return false;
32
	
33
	if ( !isPlayer( attacker ) && !isDefined( attacker.team ) )
34
		return false;
35
	
36
	if ( victim.team != attacker.team )
37
		return false;
38
	
39
	if ( victim == attacker )
40
		return false;
41
	
42
	return true;
43
}
44
45
46
killedSelf( attacker )
47
{
48
	if ( !isPlayer( attacker ) )
49
		return false;
50
51
	if ( attacker != self )
52
		return false;
53
54
	return true;
55
}
56
57
58
isHeadShot( sWeapon, sHitLoc, sMeansOfDeath, attacker )
59
{	
60
	if ( isDefined( attacker ) )
61
	{
62
		if ( attacker.code_classname == "script_vehicle" && isDefined( attacker.owner ) )
63
			return false;
64
		if ( attacker.code_classname == "misc_turret" && isDefined( attacker.owner ) )
65
			return false;
66
		if ( attacker.code_classname == "script_model" && isDefined( attacker.owner ) )
67
			return false;
68
	}
69
	
70
	return( sHitLoc == "head" || sHitLoc == "helmet" ) && sMeansOfDeath != "MOD_MELEE" && sMeansOfDeath != "MOD_IMPACT" && !isMG( sWeapon );
71
}
72
73
74
handleTeamChangeDeath()
75
{
76
	if ( !level.teamBased )
77
		return;
78
79
	// this might be able to happen now, but we should remove instances where it can
80
	assert( self.leaving_team != self.joining_team );
81
82
	if ( self.joining_team == "spectator" || !isTeamSwitchBalanced() )
83
	{
84
		self thread [[ level.onXPEvent ]]( "suicide" );
85
		self incPersStat( "suicides", 1 );
86
		self.suicides = self getPersStat( "suicides" );
87
	}
88
}
89
90
91
handleWorldDeath( attacker, lifeId, sMeansOfDeath, sHitLoc )
92
{
93
	if ( !isDefined( attacker ) )
94
		return;
95
96
	if ( !isDefined( attacker.team ) )
97
	{	
98
		handleSuicideDeath( sMeansOfDeath, sHitLoc );
99
		return;
100
	}
101
	
102
	assert( attacker.team == "axis" || attacker.team == "allies" );
103
104
	if ( level.teamBased && attacker.team != self.team )
105
	{
106
		if ( isDefined( level.onNormalDeath ) && isPlayer( attacker ) && attacker.team != "spectator" )
107
			[[ level.onNormalDeath ]]( self, attacker, lifeId );
108
	}
109
}
110
111
112
handleSuicideDeath( sMeansOfDeath, sHitLoc )
113
{
114
	self SetCardDisplaySlot( self, 7 );
115
	self openMenu( "killedby_card_display" );
116
	
117
	if ( getDvar("g_gametype") == "gg" )
118
	{
119
		self thread maps\mp\gametypes\gg::gotKilled( sMeansOfDeath, true );
120
	}
121
	
122
	if ( getDvar("g_gametype") == "ss" )
123
	{
124
		self thread maps\mp\gametypes\ss::gotKilled( sMeansOfDeath, true );
125
	}
126
	
127
	if ( getDvar("g_gametype") == "oitc" )
128
	{
129
		self thread maps\mp\gametypes\oitc::gotKilled( sMeansOfDeath, true );
130
	}
131
132
	self thread [[ level.onXPEvent ]]( "suicide" );
133
	self incPersStat( "suicides", 1 );
134
	self.suicides = self getPersStat( "suicides" );
135
	
136
	if ( !matchMakingGame() )
137
		self incPlayerStat( "suicides", 1 );
138
139
	scoreSub = maps\mp\gametypes\_tweakables::getTweakableValue( "game", "suicidepointloss" );
140
	maps\mp\gametypes\_gamescore::_setPlayerScore( self, maps\mp\gametypes\_gamescore::_getPlayerScore( self ) - scoreSub );
141
142
	if ( sMeansOfDeath == "MOD_SUICIDE" && sHitLoc == "none" && isDefined( self.throwingGrenade ) )
143
		self.lastGrenadeSuicideTime = gettime();
144
145
	// suicide was caused by too many team kills
146
	if ( isDefined( self.friendlydamage ) )
147
		self iPrintLnBold( &"MP_FRIENDLY_FIRE_WILL_NOT" );
148
}
149
150
151
handleFriendlyFireDeath( attacker )
152
{
153
	attacker SetCardDisplaySlot( self, 8 );
154
	attacker openMenu( "youkilled_card_display" );
155
156
	self SetCardDisplaySlot( attacker, 7 );
157
	self openMenu( "killedby_card_display" );
158
159
	attacker thread [[ level.onXPEvent ]]( "teamkill" );
160
	attacker.pers[ "teamkills" ] += 1.0;
161
162
	attacker.teamkillsThisRound++ ;
163
164
	if ( maps\mp\gametypes\_tweakables::getTweakableValue( "team", "teamkillpointloss" ) )
165
	{
166
		scoreSub = maps\mp\gametypes\_rank::getScoreInfoValue( "kill" );
167
		maps\mp\gametypes\_gamescore::_setPlayerScore( attacker, maps\mp\gametypes\_gamescore::_getPlayerScore( attacker ) - scoreSub );
168
	}
169
170
 	if ( level.maxAllowedTeamkills < 0 )
171
 		return;
172
173
	if ( level.inGracePeriod )
174
	{
175
		teamKillDelay = 1;
176
		attacker.pers["teamkills"] += level.maxAllowedTeamkills;
177
	}
178
	else if ( attacker.pers[ "teamkills" ] > 1 && getTimePassed() < ( (level.gracePeriod * 1000) + 8000 + ( attacker.pers[ "teamkills" ] * 1000 ) ) )
179
	{
180
		teamKillDelay = 1;
181
		attacker.pers["teamkills"] += level.maxAllowedTeamkills;
182
	}
183
	else
184
	{
185
		teamKillDelay = attacker maps\mp\gametypes\_playerlogic::TeamKillDelay();
186
	}
187
188
	if ( teamKillDelay > 0 )
189
	{
190
		attacker.pers["teamKillPunish"] = true;
191
		attacker _suicide();
192
	}
193
}
194
195
196
handleNormalDeath( lifeId, attacker, eInflictor, sWeapon, sMeansOfDeath )
197
{
198
	attacker thread maps\mp\_events::killedPlayer( lifeId, self, sWeapon, sMeansOfDeath );
199
	
200
	if ( getDvar("g_gametype") == "gg" )
201
	{
202
		attacker thread maps\mp\gametypes\gg::killedEnemy( sMeansOfDeath );
203
		self thread maps\mp\gametypes\gg::gotKilled( sMeansOfDeath, false );
204
	}
205
	
206
	if ( getDvar("g_gametype") == "ss" )
207
	{
208
		attacker thread maps\mp\gametypes\ss::killedEnemy( sMeansOfDeath );
209
		self thread maps\mp\gametypes\ss::gotKilled( sMeansOfDeath, false );
210
	}
211
	
212
	if ( getDvar("g_gametype") == "oitc" )
213
	{
214
		self thread maps\mp\gametypes\oitc::gotKilled();
215
	}
216
217
	//if ( attacker.pers["teamkills"] <= level.maxAllowedTeamkills )
218
	//	attacker.pers["teamkills"] = max( attacker.pers["teamkills"] - 1, 0 );
219
220
	attacker SetCardDisplaySlot( self, 8 );
221
	attacker openMenu( "youkilled_card_display" );
222
223
	self SetCardDisplaySlot( attacker, 7 );
224
	self openMenu( "killedby_card_display" );
225
226
	if ( sMeansOfDeath == "MOD_HEAD_SHOT" )
227
	{
228
		attacker incPersStat( "headshots", 1 );
229
		attacker.headshots = attacker getPersStat( "headshots" );
230
		attacker incPlayerStat( "headshots", 1 );
231
232
		if ( isDefined( attacker.lastStand ) )
233
			value = maps\mp\gametypes\_rank::getScoreInfoValue( "kill" ) * 2;
234
		else
235
			value = undefined;
236
237
		attacker playLocalSound( "bullet_impact_headshot_2" );
238
	}
239
	else
240
	{
241
		if ( isDefined( attacker.lastStand ) )
242
			value = maps\mp\gametypes\_rank::getScoreInfoValue( "kill" ) * 2;
243
		else
244
			value = undefined;
245
	}
246
247
	attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", value );
248
249
	attacker incPersStat( "kills", 1 );
250
	attacker.kills = attacker getPersStat( "kills" );
251
	attacker updatePersRatio( "kdRatio", "kills", "deaths" );
252
	attacker maps\mp\gametypes\_persistence::statSetChild( "round", "kills", attacker.kills );
253
	attacker incPlayerStat( "kills", 1 );
254
255
	if ( isFlankKill( self, attacker ) )
256
	{
257
		attacker incPlayerStat( "flankkills", 1 );
258
259
		self incPlayerStat( "flankdeaths", 1 );
260
	}
261
	
262
	lastKillStreak = attacker.pers["cur_kill_streak"];
263
264
	self.pers["copyCatLoadout"] = undefined;
265
266
	if ( self _hasPerk( "specialty_copycat" ) )
267
		self.pers["copyCatLoadout"] = attacker maps\mp\gametypes\_class::cloneLoadout();
268
	
269
	if ( isAlive( attacker ) )
270
	{
271
		// killstreaks only advance from kills earned this life
272
		if ( isDefined( level.killStreakSpecialCaseWeapons[sWeapon] ) ) // this is an optimization
273
		{
274
			switch ( sWeapon )
275
			{
276
				case "ac130_105mm_mp":
277
				case "ac130_40mm_mp":
278
				case "ac130_25mm_mp":
279
					if ( attacker.ac130LifeId == attacker.pers["deaths"] )
280
						attacker.pers["cur_kill_streak"]++;
281
					break;
282
				case "cobra_player_minigun_mp":
283
				case "weapon_cobra_mk19_mp":
284
					if ( attacker.heliRideLifeId == attacker.pers["deaths"] )
285
						attacker.pers["cur_kill_streak"]++;
286
					break;
287
				case "cobra_20mm_mp":
288
				case "artillery_mp":
289
				case "stealth_bomb_mp":
290
				case "remotemissile_projectile_mp":
291
				case "sentry_minigun_mp":
292
				case "harrier_20mm_mp":
293
				case "pavelow_minigun_mp":
294
					if ( isDefined( eInflictor ) && isDefined( eInflictor.lifeId ) )
295
						killstreakLifeId = eInflictor.lifeId;
296
					else
297
						killstreakLifeId = attacker.lifeId;
298
						
299
					if ( killstreakLifeId == attacker.pers["deaths"] )
300
						attacker.pers["cur_kill_streak"]++;
301
					break;
302
				default:
303
					attacker.pers["cur_kill_streak"]++;
304
					break;
305
			}
306
		}
307
		else
308
		{
309
			attacker.pers["cur_kill_streak"]++;
310
		}
311
312
		attacker setPlayerStatIfGreater( "killstreak", attacker.pers["cur_kill_streak"] );
313
314
		if ( attacker.pers["cur_kill_streak"] > attacker getPersStat( "longestStreak" ) )
315
			attacker setPersStat( "longestStreak", attacker.pers["cur_kill_streak"] );
316
	}
317
318
	attacker.pers["cur_death_streak"] = 0;
319
320
	if ( attacker.pers["cur_kill_streak"] > attacker maps\mp\gametypes\_persistence::statGetChild( "round", "killStreak" ) )
321
	{
322
		attacker maps\mp\gametypes\_persistence::statSetChild( "round", "killStreak", attacker.pers["cur_kill_streak"] );
323
	}
324
325
	if ( attacker.pers["cur_kill_streak"] > attacker.kill_streak )
326
	{
327
		attacker maps\mp\gametypes\_persistence::statSet( "killStreak", attacker.pers["cur_kill_streak"] );
328
		attacker.kill_streak = attacker.pers["cur_kill_streak"];
329
	}
330
331
	maps\mp\gametypes\_gamescore::givePlayerScore( "kill", attacker, self );
332
	maps\mp\_skill::processKill( attacker, self );
333
334
	scoreSub = maps\mp\gametypes\_tweakables::getTweakableValue( "game", "deathpointloss" );
335
	maps\mp\gametypes\_gamescore::_setPlayerScore( self, maps\mp\gametypes\_gamescore::_getPlayerScore( self ) - scoreSub );
336
337
	if ( isDefined( level.ac130player ) && level.ac130player == attacker )
338
		level notify( "ai_killed", self );
339
340
	//if ( lastKillStreak != attacker.pers["cur_kill_streak"] )
341
	level notify ( "player_got_killstreak_" + attacker.pers["cur_kill_streak"], attacker );
342
	
343
	if ( isAlive( attacker ) )
344
		attacker thread maps\mp\killstreaks\_killstreaks::checkKillstreakReward( attacker.pers["cur_kill_streak"] );
345
346
	attacker notify ( "killed_enemy" );
347
348
	if ( !level.teamBased )
349
	{
350
		self.attackers = [];
351
		return;
352
	}
353
354
	if ( isDefined( level.onNormalDeath ) && attacker.pers[ "team" ] != "spectator" )
355
		[[ level.onNormalDeath ]]( self, attacker, lifeId );
356
357
	level thread maps\mp\gametypes\_battlechatter_mp::sayLocalSoundDelayed( attacker, "kill", 0.75 );	
358
	
359
	if ( isDefined( self.lastAttackedShieldPlayer ) && isDefined( self.lastAttackedShieldTime ) && self.lastAttackedShieldPlayer != attacker )
360
	{
361
		if ( getTime() - self.lastAttackedShieldTime < 2500 )
362
		{
363
			self.lastAttackedShieldPlayer thread maps\mp\gametypes\_gamescore::processShieldAssist( self );
364
		}
365
		else if ( isAlive( self.lastAttackedShieldPlayer ) && getTime() - self.lastAttackedShieldTime < 5000 )
366
		{
367
			forwardVec = vectorNormalize( anglesToForward( self.angles ) );
368
			shieldVec = vectorNormalize( self.lastAttackedShieldPlayer.origin - self.origin );
369
		
370
			if ( vectorDot( shieldVec, forwardVec ) > 0.925 )
371
				self.lastAttackedShieldPlayer thread maps\mp\gametypes\_gamescore::processShieldAssist( self );
372
		}
373
	}
374
375
	if ( isDefined( self.attackers ) )
376
	{
377
		foreach ( player in self.attackers )
378
		{
379
			if ( !isDefined( player ) )
380
				continue;
381
382
			if ( player == attacker )
383
				continue;
384
385
			player thread maps\mp\gametypes\_gamescore::processAssist( self );
386
		}
387
		self.attackers = [];
388
	}
389
}
390
391
isPlayerWeapon( weaponName )
392
{
393
	if ( weaponClass( weaponName ) == "non-player" )
394
		return false;
395
		
396
	if ( weaponClass( weaponName ) == "turret" )
397
		return false;
398
399
	if ( weaponInventoryType( weaponName ) == "primary" || weaponInventoryType( weaponName ) == "altmode" )
400
		return true;
401
		
402
	return false;
403
}
404
405
Callback_PlayerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration )
406
{
407
	PlayerKilled_internal( eInflictor, attacker, self, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, false );
408
}
409
410
411
QueueShieldForRemoval( shield )
412
{
413
	MY_MAX_SHIELDS_AT_A_TIME = 5;
414
415
	if ( !isDefined( level.shieldTrashArray ) )
416
		level.shieldTrashArray = [];
417
418
	if ( level.shieldTrashArray.size >= MY_MAX_SHIELDS_AT_A_TIME )
419
	{
420
		idxMax = (level.shieldTrashArray.size - 1);
421
		level.shieldTrashArray[0] delete();
422
		for ( idx = 0; idx < idxMax; idx++ )
423
			level.shieldTrashArray[idx] = level.shieldTrashArray[idx + 1];
424
		level.shieldTrashArray[idxMax] = undefined;
425
	}
426
427
	level.shieldTrashArray[level.shieldTrashArray.size] = shield;
428
}
429
430
431
LaunchShield( damage, meansOfDeath )
432
{
433
	shieldModel = "weapon_riot_shield_mp";
434
435
	self DetachShieldModel( shieldModel, "tag_weapon_left" );
436
	self.hasRiotShield = false;
437
	self.hasRiotShieldEquipped = false;
438
}
439
440
441
PlayerKilled_internal( eInflictor, attacker, victim, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, isFauxDeath )
442
{
443
	prof_begin( "PlayerKilled" );
444
	//prof_begin( " PlayerKilled_1" );
445
	
446
	victim endon( "spawned" );
447
	victim notify( "killed_player" );
448
449
	assert( victim.sessionteam != "spectator" );
450
451
	if ( isDefined( attacker ) )
452
		attacker.assistedSuicide = undefined;
453
454
	if ( !isDefined( victim.idFlags ) )
455
	{
456
		if ( sMeansOfDeath == "MOD_SUICIDE" )
457
			victim.idFlags = 0;
458
		else if ( sMeansOfDeath == "MOD_GRENADE" && isSubstr( sWeapon, "frag_grenade" ) && iDamage == 100000 )
459
			victim.idFlags = 0;
460
		else if ( sWeapon == "nuke_mp" )
461
			victim.idFlags = 0;
462
		else if ( level.friendlyfire >= 2)
463
			victim.idFlags = 0;
464
		else
465
			assertEx( 0, "Victims ID flags not set, but means of death was gr or nuke: " + sMeansOfDeath  );
466
	}
467
468
	if ( victim.hasRiotShieldEquipped )
469
		victim LaunchShield( iDamage, sMeansofDeath );
470
		
471
	//victim thread checkForceBleedOut();
472
473
	if ( !isFauxDeath )
474
	{
475
		if ( isDefined( victim.endGame ) )
476
		{
477
			victim VisionSetNakedForPlayer( getDvar( "mapname" ), 2 );
478
		}
479
		else if ( !isDefined( victim.nuked ) )
480
		{
481
			victim VisionSetNakedForPlayer( getDvar( "mapname" ), 0 );
482
			victim ThermalVisionOff();
483
		}
484
	}
485
	else
486
	{
487
		victim.fauxDead = true;
488
		self notify ( "death" );
489
	}
490
491
	if ( game[ "state" ] == "postgame" )
492
	{
493
		//prof_end( " PlayerKilled_1" );
494
		prof_end( "PlayerKilled" );
495
		return;
496
	}
497
498
	// replace params with last stand info
499
	deathTimeOffset = 0;
500
501
	if ( !isPlayer( eInflictor ) && isDefined( eInflictor.primaryWeapon ) )
502
		sPrimaryWeapon = eInflictor.primaryWeapon;
503
	else if ( isDefined( attacker ) && isPlayer( attacker ) && attacker getCurrentPrimaryWeapon() != "none" )
504
		sPrimaryWeapon = attacker getCurrentPrimaryWeapon();
505
	else
506
		sPrimaryWeapon = undefined;
507
508
	if ( isdefined( victim.useLastStandParams ) )
509
	{
510
		victim ensureLastStandParamsValidity();
511
		victim.useLastStandParams = undefined;
512
513
		assert( isdefined( victim.lastStandParams ) );
514
515
		eInflictor = victim.lastStandParams.eInflictor;
516
		attacker = victim.lastStandParams.attacker;
517
		iDamage = victim.lastStandParams.iDamage;
518
		sMeansOfDeath = victim.lastStandParams.sMeansOfDeath;
519
		sWeapon = victim.lastStandParams.sWeapon;
520
		sPrimaryWeapon = victim.lastStandParams.sPrimaryWeapon;
521
		vDir = victim.lastStandParams.vDir;
522
		sHitLoc = victim.lastStandParams.sHitLoc;
523
524
		deathTimeOffset = ( gettime() - victim.lastStandParams.lastStandStartTime ) / 1000;
525
		victim.lastStandParams = undefined;
526
	}
527
	
528
	//prof_end( " PlayerKilled_1" );
529
	//prof_begin( " PlayerKilled_2" );
530
	
531
	//used for endgame perk and assisted suicide.
532
	if ( (!isDefined( attacker ) || attacker.classname == "trigger_hurt" || attacker.classname == "worldspawn" || attacker == victim ) && isDefined( self.attackers )  )
533
	{
534
		bestPlayer = undefined;
535
		
536
		foreach ( player in self.attackers )
537
		{
538
			if ( !isDefined( player ) )
539
				continue;
540
			
541
			if (! isDefined( victim.attackerData[ player.guid ].damage ) )
542
				continue;
543
			
544
			if ( player == victim || (level.teamBased && player.team == victim.team ) )
545
				continue;
546
			
547
			if ( victim.attackerData[ player.guid ].lasttimedamaged + 2500 < getTime() )
548
				continue;			
549
			
550
			if ( victim.attackerData[ player.guid ].damage > 1 && ! isDefined( bestPlayer ) )
551
				bestPlayer = player;
552
			else if ( isDefined( bestPlayer ) && victim.attackerData[ player.guid ].damage > victim.attackerData[ bestPlayer.guid ].damage )
553
				bestPlayer = player;		
554
		}
555
		
556
		if ( isDefined( bestPlayer ) )
557
		{
558
			attacker = bestPlayer;
559
			attacker.assistedSuicide = true;
560
			sWeapon = victim.attackerData[ bestPlayer.guid ].weapon; 
561
			vDir = victim.attackerData[ bestPlayer.guid ].vDir;
562
			sHitLoc = victim.attackerData[ bestPlayer.guid ].sHitLoc;
563
			psOffsetTime = victim.attackerData[ bestPlayer.guid ].psOffsetTime;
564
			sMeansOfDeath = victim.attackerData[ bestPlayer.guid ].sMeansOfDeath;
565
			iDamage = victim.attackerData[ bestPlayer.guid ].damage;
566
			sPrimaryWeapon = victim.attackerData[ bestPlayer.guid ].sPrimaryWeapon;
567
			eInflictor = attacker;
568
		}
569
	}
570
	else
571
	{
572
		if ( isDefined( attacker ) )
573
			attacker.assistedSuicide = undefined;	
574
	}
575
576
	// override MOD
577
	if ( isHeadShot( sWeapon, sHitLoc, sMeansOfDeath, attacker ) )
578
		sMeansOfDeath = "MOD_HEAD_SHOT";
579
	else if ( sMeansOfDeath != "MOD_MELEE" && !isDefined( victim.nuked ) )
580
		victim playDeathSound();
581
	
582
	friendlyFire = isFriendlyFire( victim, attacker );
583
	
584
	if ( isDefined( attacker ) )
585
	{
586
		// override attacker if it's a vehicle	
587
		if ( attacker.code_classname == "script_vehicle" && isDefined( attacker.owner ) )
588
			attacker = attacker.owner;
589
590
		// override attacker if it's a sentry	
591
		if ( attacker.code_classname == "misc_turret" && isDefined( attacker.owner ) )
592
			attacker = attacker.owner;
593
594
		// override attacker if it's a crate	
595
		if ( attacker.code_classname == "script_model" && isDefined( attacker.owner ) )
596
		{
597
			attacker = attacker.owner;
598
			
599
			if ( !isFriendlyFire( victim, attacker ) && attacker != victim )
600
				attacker notify( "crushed_enemy" );
601
		}
602
	}
603
	
604
	//prof_end( " PlayerKilled_2" );
605
	//prof_begin( " PlayerKilled_3" );
606
	
607
	//prof_begin( " PlayerKilled_3_drop" );
608
	// drop weapons from killed player
609
	if ( getDvar("g_gametype") != "oitc" && getDvar("g_gametype") != "gg" && getDvar("g_gametype") != "ss" )
610
	{
611
		victim maps\mp\gametypes\_weapons::dropScavengerForDeath( attacker );	// must be done before dropWeaponForDeath, since we use some weapon information
612
		victim maps\mp\gametypes\_weapons::dropWeaponForDeath( attacker );
613
	}
614
	//prof_end( " PlayerKilled_3_drop" );
615
	
616
	if ( !isFauxDeath )
617
	{
618
		victim.sessionstate = "dead";
619
		victim.statusicon = "hud_status_dead";
620
	}
621
622
	// UTS update aliveCount
623
	victim maps\mp\gametypes\_playerlogic::removeFromAliveCount();
624
	
625
	if ( !isDefined( victim.switching_teams ) )
626
	{
627
		// update our various stats
628
		victim incPersStat( "deaths", 1 );
629
		victim.deaths = victim getPersStat( "deaths" );
630
		victim updatePersRatio( "kdRatio", "kills", "deaths" );
631
		victim maps\mp\gametypes\_persistence::statSetChild( "round", "deaths", victim.deaths );
632
		victim incPlayerStat( "deaths", 1 );
633
	}
634
635
	if ( isDefined( attacker ) )
636
		attacker checkKillSteal( victim );
637
	
638
	// obituary
639
	obituary( victim, attacker, sWeapon, sMeansOfDeath );
640
641
	doKillcam = false;
642
643
	lifeId = getNextLifeId();
644
	
645
	victim logPrintPlayerDeath( lifeId, attacker, iDamage, sMeansOfDeath, sWeapon, sPrimaryWeapon, sHitLoc );
646
	victim maps\mp\_matchdata::logPlayerLife( lifeId );
647
	victim maps\mp\_matchdata::logPlayerDeath( lifeId, attacker, iDamage, sMeansOfDeath, sWeapon, sPrimaryWeapon, sHitLoc );
648
	
649
	if ( (sMeansOfDeath == "MOD_MELEE") )
650
	{
651
		if ( IsSubStr( sWeapon, "riotshield" ) )
652
		{
653
			attacker incPlayerStat( "shieldkills", 1 );
654
			
655
			if ( !matchMakingGame() )
656
				victim incPlayerStat( "shielddeaths", 1 );
657
		}
658
		else
659
			attacker incPlayerStat( "knifekills", 1 );
660
	}
661
	
662
	//prof_end( " PlayerKilled_3" );
663
	//prof_begin( " PlayerKilled_4" );
664
	
665
	if ( victim isSwitchingTeams() )
666
	{
667
		handleTeamChangeDeath();
668
	}
669
	else if ( !isPlayer( attacker ) || (isPlayer( attacker ) && sMeansOfDeath == "MOD_FALLING") )
670
	{
671
		handleWorldDeath( attacker, lifeId, sMeansOfDeath, sHitLoc );
672
	}
673
	else if ( attacker == victim )
674
	{
675
		handleSuicideDeath( sMeansOfDeath, sHitLoc );
676
	}
677
	else if ( friendlyFire )
678
	{
679
		if ( !isDefined( victim.nuked ) )
680
		{
681
			handleFriendlyFireDeath( attacker );
682
		}
683
	}
684
	else
685
	{
686
		if ( sMeansOfDeath == "MOD_GRENADE" && eInflictor == attacker )
687
			addAttacker( victim, attacker, eInflictor, sWeapon, iDamage, (0,0,0), vDir, sHitLoc, psOffsetTime, sMeansOfDeath );
688
689
		doKillcam = true;
690
		handleNormalDeath( lifeId, attacker, eInflictor, sWeapon, sMeansOfDeath );
691
		victim thread maps\mp\gametypes\_missions::playerKilled( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, sPrimaryWeapon, sHitLoc, attacker.modifiers );
692
		
693
		victim.pers["cur_death_streak"]++;
694
		
695
		if ( !getGametypeNumLives() && !matchMakingGame() )
696
			victim setPlayerStatIfGreater( "deathstreak", victim.pers["cur_death_streak"] );
697
	}
698
	
699
	//prof_end( " PlayerKilled_4" );
700
	//prof_begin( " PlayerKilled_5" );
701
	
702
	// clear any per life variables
703
	victim resetPlayerVariables();
704
	victim.lastAttacker = attacker;
705
	victim.lastDeathPos = victim.origin;
706
	victim.deathTime = getTime();
707
	victim.wantSafeSpawn = false;
708
	victim.revived = false;
709
	victim.sameShotDamage = 0;
710
711
	if ( isFauxDeath )
712
	{
713
		doKillcam = false;
714
		deathAnimDuration = (victim PlayerForceDeathAnim( eInflictor, sMeansOfDeath, sWeapon, sHitLoc, vDir ));
715
	}
716
717
	victim.body = victim clonePlayer( deathAnimDuration );
718
719
	if ( isFauxDeath )
720
		victim PlayerHide();
721
722
	if ( victim isOnLadder() || victim isMantling() || !victim isOnGround() || isDefined( victim.nuked ) )
723
		victim.body startRagDoll();
724
725
	if ( !isDefined( victim.switching_teams ) )
726
		thread maps\mp\gametypes\_deathicons::addDeathicon( victim.body, victim, victim.team, 5.0 );
727
728
	thread delayStartRagdoll( victim.body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath );
729
730
	// added
731
	if(sMeansOfDeath == "MOD_HEADSHOT" || sMeansOfDeath == "MOD_HEAD_SHOT" ) // since cod4 it changed
732
	{
733
		attacker.modifiers["headshot"] = true;
734
		attacker thread maps\mp\gametypes\_hud_message::SplashNotifyDelayed( "headshot", maps\mp\gametypes\_rank::getScoreInfoValue( "headshot" ) );	
735
	}
736
737
	// ======
738
	// allow per gametype death handling	
739
	victim thread [[ level.onPlayerKilled ]]( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, lifeId );
740
741
	if ( isPlayer( attacker ) )
742
		attackerNum = attacker getEntityNumber();
743
	else
744
		attackerNum = -1;
745
	killcamentity = victim getKillcamEntity( attacker, eInflictor, sWeapon );
746
	killcamentityindex = -1;
747
	killcamentitystarttime = 0;
748
749
	if ( isDefined( killcamentity ) )
750
	{
751
		killcamentityindex = killcamentity getEntityNumber();// must do this before any waiting lest the entity be deleted
752
		killcamentitystarttime = killcamentity.birthtime;
753
		if ( !isdefined( killcamentitystarttime ) )
754
			killcamentitystarttime = 0;
755
	}
756
757
	 /#
758
	if ( getDvarInt( "scr_forcekillcam" ) != 0 )
759
		doKillcam = true;
760
	#/
761
762
	if ( isDefined( attacker.finalKill ) )
763
		maps\mp\_awards::addAwardWinner( "finalkill", attacker.clientid );
764
	
765
	//prof_end( " PlayerKilled_5" );
766
	//prof_begin( " PlayerKilled_6" );
767
	
768
	if ( isDefined( attacker.finalKill ) && doKillcam && !isDefined( level.nukeDetonated ) )
769
	{
770
		level thread doFinalKillcam( 5.0, victim, attacker, attackerNum, killcamentityindex, killcamentitystarttime, sWeapon, deathTimeOffset, psOffsetTime );
771
772
		if ( !isFauxDeath )
773
			wait ( 1.0 );
774
	}
775
	
776
	if ( isDefined(level.ggended) && level.ggended == true || isDefined(level.ssended) && level.ssended == true )
777
	{
778
		foreach( player in level.players )
779
		{
780
			player notify("killcum");
781
		}
782
		wait 0.1;
783
		level thread doFinalKillcam( 5.0, victim, attacker, attackerNum, killcamentityindex, killcamentitystarttime, sWeapon, deathTimeOffset, psOffsetTime );
784
		
785
		if ( !isFauxDeath )
786
			wait ( 1.0 );
787
	}
788
	
789
	if ( !isFauxDeath )
790
	{
791
		if ( !level.showingFinalKillcam && !level.killcam && doKillcam )
792
		{
793
			if ( victim _hasPerk( "specialty_copycat" ) && isDefined( victim.pers["copyCatLoadout"] ) )
794
			{
795
				victim thread maps\mp\gametypes\_killcam::waitDeathCopyCatButton( attacker );
796
				wait ( 1.0 );
797
			}
798
		}
799
		
800
		// let the player watch themselves die
801
		wait( 0.25 );
802
		victim thread maps\mp\gametypes\_killcam::cancelKillCamOnUse();
803
		wait( 0.25 );
804
		
805
		self.respawnTimerStartTime = gettime() + 1000;
806
		timeUntilSpawn = maps\mp\gametypes\_playerlogic::TimeUntilSpawn( true );
807
		if ( timeUntilSpawn < 1 )
808
			timeUntilSpawn = 1;
809
		victim thread maps\mp\gametypes\_playerlogic::predictAboutToSpawnPlayerOverTime( timeUntilSpawn );
810
		
811
		wait( 1.0 );
812
		victim notify( "death_delay_finished" );
813
	}
814
815
	postDeathDelay = ( getTime() - victim.deathTime ) / 1000;
816
	self.respawnTimerStartTime = gettime();
817
818
	if ( !(isDefined( victim.cancelKillcam) && victim.cancelKillcam) && doKillcam && level.killcam && game[ "state" ] == "playing" && !victim isUsingRemote() && !level.showingFinalKillcam )
819
	{
820
		livesLeft = !( getGametypeNumLives() && !victim.pers[ "lives" ] );
821
		timeUntilSpawn = maps\mp\gametypes\_playerlogic::TimeUntilSpawn( true );
822
		willRespawnImmediately = livesLeft && ( timeUntilSpawn <= 0 );
823
		
824
		if ( !livesLeft )
825
			timeUntilSpawn = -1;
826
827
		victim maps\mp\gametypes\_killcam::killcam( attackerNum, killcamentityindex, killcamentitystarttime, sWeapon, postDeathDelay + deathTimeOffset, psOffsetTime, timeUntilSpawn, maps\mp\gametypes\_gamelogic::timeUntilRoundEnd(), attacker, victim );
828
	}
829
	
830
	//prof_end( " PlayerKilled_6" );
831
	//prof_begin( " PlayerKilled_7" );
832
	
833
	//self openMenu( "killedby_card_hide" );
834
835
	if ( game[ "state" ] != "playing" )
836
	{
837
		if ( !level.showingFinalKillcam )
838
		{
839
			victim.sessionstate = "dead";
840
			victim ClearKillcamState();
841
		}
842
		
843
		//prof_end( " PlayerKilled_7" );
844
		prof_end( "PlayerKilled" );
845
		return;
846
	}
847
848
	// class may be undefined if we have changed teams
849
	if ( isValidClass( victim.class ) )
850
	{
851
		victim thread maps\mp\gametypes\_playerlogic::spawnClient();
852
	}
853
	
854
	//prof_end( " PlayerKilled_7" );
855
	prof_end( "PlayerKilled" );
856
}
857
858
checkForceBleedout()
859
{
860
	if ( level.dieHardMode != 1 )
861
		return false;
862
	
863
	if ( !getGametypeNumLives() )
864
		return false;
865
	
866
	if ( level.livesCount[self.team] > 0 )
867
		return false;
868
	
869
	foreach ( player in level.players )
870
	{
871
		if ( !isAlive( player ) )
872
			continue;
873
			
874
		if ( player.team != self.team )
875
			continue;
876
			
877
		if ( player == self )
878
			continue;
879
		
880
		if ( !player.inLastStand )
881
			return false;
882
	}
883
	
884
	foreach ( player in level.players )
885
	{
886
		if ( !isAlive( player ) )
887
			continue;
888
		
889
		if ( player.team != self.team )
890
			continue;
891
		
892
		if ( player.inLastStand && player != self )
893
			player lastStandBleedOut(false);		
894
	}
895
	
896
	return true;					
897
}
898
899
checkKillSteal( vic )
900
{
901
	if ( matchMakingGame() )
902
		return;
903
	
904
	greatestDamage = 0;
905
	greatestAttacker = undefined;
906
	
907
	if ( isDefined( vic.attackerdata ) && vic.attackerdata.size > 1 )	
908
	{
909
		foreach ( attacker in vic.attackerdata )
910
		{
911
			if ( attacker.damage > greatestDamage )
912
			{
913
				greatestDamage = attacker.damage;
914
				greatestAttacker = attacker.attackerEnt;	
915
			}
916
		}
917
		
918
		if ( isDefined( greatestAttacker ) && greatestAttacker != self )
919
			self incPlayerStat( "killsteals", 1 );
920
	}
921
}
922
923
doFinalKillcam( delay, victim, attacker, attackerNum, killcamentityindex, killcamentitystarttime, sWeapon, deathTimeOffset, psOffsetTime )
924
{
925
	level.showingFinalKillcam = true;
926
927
	level waittill ( "round_end_finished" );
928
929
	if ( !isDefined( victim ) || !isDefined( attacker ) )
930
	{
931
		level.showingFinalKillcam = false;
932
		return;
933
	}
934
935
	postDeathDelay = (( getTime() - victim.deathTime ) / 1000);
936
	
937
	foreach ( player in level.players )
938
	{
939
		player closePopupMenu();
940
		player closeInGameMenu();
941
		player VisionSetNakedForPlayer( getDvar( "mapname" ), 0 );
942
		player.killcamentitylookat = victim getEntityNumber();
943
		
944
		if ( (player != victim || (!isRoundBased() || isLastRound())) && player _hasPerk( "specialty_copycat" ) )
945
			player _unsetPerk( "specialty_copycat" );
946
		
947
		player thread maps\mp\gametypes\_killcam::killcam( attackerNum, killcamentityindex, killcamentitystarttime, sWeapon, postDeathDelay + deathTimeOffset, psOffsetTime, 0, 10000, attacker, victim );
948
	}
949
950
	wait( 0.1 );
951
952
	while ( anyPlayersInKillcam() )
953
		wait( 0.05 );
954
	
955
	level.showingFinalKillcam = false;
956
}
957
958
959
anyPlayersInKillcam()
960
{
961
	foreach ( player in level.players )
962
	{
963
		if ( isDefined( player.killcam ) )
964
			return true;
965
	}
966
	
967
	return false;
968
}
969
970
971
resetPlayerVariables()
972
{
973
	self.killedPlayersCurrent = [];
974
	self.switching_teams = undefined;
975
	self.joining_team = undefined;
976
	self.leaving_team = undefined;
977
978
	self.pers["cur_kill_streak"] = 0;
979
980
	self maps\mp\gametypes\_gameobjects::detachUseModels();// want them detached before we create our corpse
981
}
982
983
984
getKillcamEntity( attacker, eInflictor, sWeapon )
985
{
986
	if ( !isDefined( eInflictor ) )
987
		return undefined;
988
	
989
	if ( eInflictor == attacker )
990
		return undefined;
991
	
992
	if ( isSubStr( sWeapon, "ac130_" ) )
993
		return undefined;
994
995
	if ( sWeapon == "cobra_player_minigun_mp" )
996
		return undefined;
997
	
998
	if ( sWeapon == "artillery_mp" || sWeapon == "stealth_bomb_mp" || sWeapon == "pavelow_minigun_mp" )
999
		return eInflictor.killCamEnt;
1000
	
1001
	if ( isDefined( eInflictor.script_gameobjectname ) && eInflictor.script_gameobjectname == "bombzone" )
1002
		return eInflictor.killCamEnt;
1003
	
1004
	if ( eInflictor.classname == "script_origin" || eInflictor.classname == "script_model" || eInflictor.classname == "script_brushmodel" )
1005
		return undefined; // probably a barrel or a car... code does airstrike cam for these things which looks bad
1006
	
1007
	if ( issubstr( sWeapon, "remotemissile_" ) )
1008
		return undefined;
1009
	if ( issubstr( sWeapon, "ac130_" ) )
1010
		return undefined;
1011
	
1012
	return eInflictor;
1013
}
1014
1015
1016
HitlocDebug( attacker, victim, damage, hitloc, dflags )
1017
{
1018
	colors = [];
1019
	colors[ 0 ] = 2;
1020
	colors[ 1 ] = 3;
1021
	colors[ 2 ] = 5;
1022
	colors[ 3 ] = 7;
1023
1024
	if ( !getdvarint( "scr_hitloc_debug" ) )
1025
		return;
1026
1027
	if ( !isdefined( attacker.hitlocInited ) )
1028
	{
1029
		for ( i = 0; i < 6; i++ )
1030
		{
1031
			attacker setClientDvar( "ui_hitloc_" + i, "" );
1032
		}
1033
		attacker.hitlocInited = true;
1034
	}
1035
1036
	if ( level.splitscreen || !isPLayer( attacker ) )
1037
		return;
1038
1039
	elemcount = 6;
1040
	if ( !isdefined( attacker.damageInfo ) )
1041
	{
1042
		attacker.damageInfo = [];
1043
		for ( i = 0; i < elemcount; i++ )
1044
		{
1045
			attacker.damageInfo[ i ] = spawnstruct();
1046
			attacker.damageInfo[ i ].damage = 0;
1047
			attacker.damageInfo[ i ].hitloc = "";
1048
			attacker.damageInfo[ i ].bp = false;
1049
			attacker.damageInfo[ i ].jugg = false;
1050
			attacker.damageInfo[ i ].colorIndex = 0;
1051
		}
1052
		attacker.damageInfoColorIndex = 0;
1053
		attacker.damageInfoVictim = undefined;
1054
	}
1055
1056
	for ( i = elemcount - 1; i > 0; i -- )
1057
	{
1058
		attacker.damageInfo[ i ].damage = attacker.damageInfo[ i - 1 ].damage;
1059
		attacker.damageInfo[ i ].hitloc = attacker.damageInfo[ i - 1 ].hitloc;
1060
		attacker.damageInfo[ i ].bp = attacker.damageInfo[ i - 1 ].bp;
1061
		attacker.damageInfo[ i ].jugg = attacker.damageInfo[ i - 1 ].jugg;
1062
		attacker.damageInfo[ i ].colorIndex = attacker.damageInfo[ i - 1 ].colorIndex;
1063
	}
1064
	attacker.damageInfo[ 0 ].damage = damage;
1065
	attacker.damageInfo[ 0 ].hitloc = hitloc;
1066
	attacker.damageInfo[ 0 ].bp = ( dflags & level.iDFLAGS_PENETRATION );
1067
	attacker.damageInfo[ 0 ].jugg = victim hasPerk( "specialty_armorvest", true );
1068
	if ( isdefined( attacker.damageInfoVictim ) && ( attacker.damageInfoVictim != victim ) )
1069
	{
1070
		attacker.damageInfoColorIndex++ ;
1071
		if ( attacker.damageInfoColorIndex == colors.size )
1072
			attacker.damageInfoColorIndex = 0;
1073
	}
1074
	attacker.damageInfoVictim = victim;
1075
	attacker.damageInfo[ 0 ].colorIndex = attacker.damageInfoColorIndex;
1076
1077
	for ( i = 0; i < elemcount; i++ )
1078
	{
1079
		color = "^" + colors[ attacker.damageInfo[ i ].colorIndex ];
1080
		if ( attacker.damageInfo[ i ].hitloc != "" )
1081
		{
1082
			val = color + attacker.damageInfo[ i ].hitloc;
1083
			if ( attacker.damageInfo[ i ].bp )
1084
				val += " (BP)";
1085
			if ( attacker.damageInfo[ i ].jugg )
1086
				val += " (Jugg)";
1087
			attacker setClientDvar( "ui_hitloc_" + i, val );
1088
		}
1089
		attacker setClientDvar( "ui_hitloc_damage_" + i, color + attacker.damageInfo[ i ].damage );
1090
	}
1091
}
1092
1093
giveRecentShieldXP()
1094
{
1095
	self endon ( "death" );
1096
	self endon ( "disconnect" );
1097
	
1098
	self notify ( "giveRecentShieldXP" );
1099
	self endon ( "giveRecentShieldXP" );
1100
	
1101
	self.recentShieldXP++;
1102
	
1103
	wait ( 20.0 );
1104
	
1105
	self.recentShieldXP = 0;
1106
}
1107
1108
1109
Callback_PlayerDamage_internal( eInflictor, eAttacker, victim, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime )
1110
{
1111
1112
	if ( getDvar("g_gametype") == "oitc" && sWeapon == "turret_minigun_mp" || getDvar("g_gametype") == "gg" && sWeapon == "turret_minigun_mp" || getDvar("g_gametype") == "ss" && sWeapon == "turret_minigun_mp" )
1113
	{
1114
		iDamage = 0;
1115
	}
1116
	
1117
	if ( !isReallyAlive( victim ) )
1118
		return;
1119
	
1120
	if ( isDefined( eAttacker ) && eAttacker.classname == "script_origin" && isDefined( eAttacker.type ) && eAttacker.type == "soft_landing" )
1121
		return;
1122
	
1123
	if ( isDefined( level.hostMigrationTimer ) )
1124
		return;
1125
	
1126
	if ( sMeansOfDeath == "MOD_FALLING" )
1127
		victim thread emitFallDamage( iDamage );
1128
		
1129
	if ( sMeansOfDeath == "MOD_EXPLOSIVE_BULLET" && iDamage != 1 )
1130
	{
1131
		iDamage *= getDvarFloat( "scr_explBulletMod" );	
1132
		iDamage = int( iDamage );
1133
	}
1134
1135
	if ( isDefined( eAttacker ) && eAttacker.classname == "worldspawn" )
1136
		eAttacker = undefined;
1137
	
1138
	if ( isDefined( eAttacker ) && isDefined( eAttacker.gunner ) )
1139
		eAttacker = eAttacker.gunner;
1140
	
1141
	attackerIsNPC = isDefined( eAttacker ) && !isDefined( eAttacker.gunner ) && (eAttacker.classname == "script_vehicle" || eAttacker.classname == "misc_turret" || eAttacker.classname == "script_model");
1142
	attackerIsHittingTeammate = level.teamBased && isDefined( eAttacker ) && ( victim != eAttacker ) && isDefined( eAttacker.team ) && ( victim.pers[ "team" ] == eAttacker.team );
1143
1144
	stunFraction = 0.0;
1145
1146
	if ( iDFlags & level.iDFLAGS_STUN )
1147
	{
1148
		stunFraction = 0.0;
1149
		//victim StunPlayer( 1.0 );
1150
		iDamage = 0.0;
1151
	}
1152
	else if ( sHitLoc == "shield" )
1153
	{
1154
		if ( attackerIsHittingTeammate && level.friendlyfire == 0 )
1155
			return;
1156
		
1157
		if ( sMeansOfDeath == "MOD_PISTOL_BULLET" || sMeansOfDeath == "MOD_RIFLE_BULLET" || sMeansOfDeath == "MOD_EXPLOSIVE_BULLET" && !attackerIsHittingTeammate )
1158
		{
1159
			if ( isPlayer( eAttacker ) )
1160
			{
1161
				eAttacker.lastAttackedShieldPlayer = victim;
1162
				eAttacker.lastAttackedShieldTime = getTime();
1163
			}
1164
			victim notify ( "shield_blocked" );
1165
1166
			// fix turret + shield challenge exploits
1167
			if ( sWeapon == "turret_minigun_mp" )
1168
				shieldDamage = 25;
1169
			else
1170
				shieldDamage = maps\mp\perks\_perks::cac_modified_damage( victim, eAttacker, iDamage, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc );
1171
						
1172
			victim.shieldDamage += shieldDamage;
1173
1174
			// fix turret + shield challenge exploits
1175
			if ( sWeapon != "turret_minigun_mp" || cointoss() )
1176
				victim.shieldBulletHits++;
1177
1178
			if ( victim.shieldBulletHits >= level.riotShieldXPBullets )
1179
			{
1180
				if ( self.recentShieldXP > 4 )
1181
					xpVal = int( 50 / self.recentShieldXP );
1182
				else
1183
					xpVal = 50;
1184
				
1185
				printLn( xpVal );
1186
				
1187
				victim thread maps\mp\gametypes\_rank::giveRankXP( "shield_damage", xpVal );
1188
				victim thread giveRecentShieldXP();
1189
				
1190
				victim thread maps\mp\gametypes\_missions::genericChallenge( "shield_damage", victim.shieldDamage );
1191
1192
				victim thread maps\mp\gametypes\_missions::genericChallenge( "shield_bullet_hits", victim.shieldBulletHits );
1193
				
1194
				victim.shieldDamage = 0;
1195
				victim.shieldBulletHits = 0;
1196
			}
1197
		}
1198
1199
		if ( iDFlags & level.iDFLAGS_SHIELD_EXPLOSIVE_IMPACT )
1200
		{
1201
			if (  !attackerIsHittingTeammate )
1202
				victim thread maps\mp\gametypes\_missions::genericChallenge( "shield_explosive_hits", 1 );
1203
1204
			sHitLoc = "none";	// code ignores any damage to a "shield" bodypart.
1205
			if ( !(iDFlags & level.iDFLAGS_SHIELD_EXPLOSIVE_IMPACT_HUGE) )
1206
				iDamage *= 0.0;
1207
		}
1208
		else if ( iDFlags & level.iDFLAGS_SHIELD_EXPLOSIVE_SPLASH )
1209
		{
1210
			if ( isDefined( eInflictor ) && isDefined( eInflictor.stuckEnemyEntity ) && eInflictor.stuckEnemyEntity == victim ) //does enough damage to shield carrier to ensure death
1211
				iDamage = 101;
1212
			
1213
			victim thread maps\mp\gametypes\_missions::genericChallenge( "shield_explosive_hits", 1 );
1214
			sHitLoc = "none";	// code ignores any damage to a "shield" bodypart.
1215
		}
1216
		else
1217
		{
1218
			return;
1219
		}
1220
	}
1221
	else if ( (smeansofdeath == "MOD_MELEE") && IsSubStr( sweapon, "riotshield" ) )
1222
	{
1223
		if ( !(attackerIsHittingTeammate && (level.friendlyfire == 0)) )
1224
		{
1225
			stunFraction = 0.0;
1226
			victim StunPlayer( 0.0 );
1227
		}
1228
	}
1229
1230
	if ( !attackerIsHittingTeammate )
1231
		iDamage = maps\mp\perks\_perks::cac_modified_damage( victim, eAttacker, iDamage, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc );
1232
	
1233
	if ( !iDamage )
1234
		return false;
1235
	
1236
	victim.iDFlags = iDFlags;
1237
	victim.iDFlagsTime = getTime();
1238
1239
	if ( game[ "state" ] == "postgame" )
1240
		return;
1241
	if ( victim.sessionteam == "spectator" )
1242
		return;
1243
	if ( isDefined( victim.canDoCombat ) && !victim.canDoCombat )
1244
		return;
1245
	if ( isDefined( eAttacker ) && isPlayer( eAttacker ) && isDefined( eAttacker.canDoCombat ) && !eAttacker.canDoCombat )
1246
		return;
1247
1248
	// handle vehicles/turrets and friendly fire
1249
	if ( attackerIsNPC && attackerIsHittingTeammate )
1250
	{
1251
		if ( sMeansOfDeath == "MOD_CRUSH" )
1252
		{
1253
			victim _suicide();
1254
			return;
1255
		}
1256
		
1257
		if ( !level.friendlyfire )
1258
			return;
1259
	}
1260
1261
	prof_begin( "PlayerDamage flags/tweaks" );
1262
1263
	// Don't do knockback if the damage direction was not specified
1264
	if ( !isDefined( vDir ) )
1265
		iDFlags |= level.iDFLAGS_NO_KNOCKBACK;
1266
1267
	friendly = false;
1268
1269
	if ( ( victim.health == victim.maxhealth && ( !isDefined( victim.lastStand ) || !victim.lastStand )  ) || !isDefined( victim.attackers ) && !isDefined( victim.lastStand )  )
1270
	{
1271
		victim.attackers = [];
1272
		victim.attackerData = [];
1273
	}
1274
1275
	if ( isHeadShot( sWeapon, sHitLoc, sMeansOfDeath, eAttacker ) )
1276
		sMeansOfDeath = "MOD_HEAD_SHOT";
1277
1278
	if ( maps\mp\gametypes\_tweakables::getTweakableValue( "game", "onlyheadshots" ) )
1279
	{
1280
		if ( sMeansOfDeath == "MOD_PISTOL_BULLET" || sMeansOfDeath == "MOD_RIFLE_BULLET" || sMeansOfDeath == "MOD_EXPLOSIVE_BULLET" )
1281
			return;
1282
		else if ( sMeansOfDeath == "MOD_HEAD_SHOT" )
1283
			iDamage = 150;
1284
	}
1285
1286
	// explosive barrel/car detection
1287
	if ( sWeapon == "none" && isDefined( eInflictor ) )
1288
	{
1289
		if ( isDefined( eInflictor.destructible_type ) && isSubStr( eInflictor.destructible_type, "vehicle_" ) )
1290
			sWeapon = "destructible_car";
1291
	}
1292
1293
	prof_end( "PlayerDamage flags/tweaks" );
1294
1295
	// check for completely getting out of the damage
1296
	if ( !(iDFlags & level.iDFLAGS_NO_PROTECTION) )
1297
	{
1298
		// items you own don't damage you in FFA
1299
		if ( !level.teamBased && attackerIsNPC && isDefined( eAttacker.owner ) && eAttacker.owner == victim )
1300
		{
1301
			prof_end( "PlayerDamage player" );
1302
1303
			if ( sMeansOfDeath == "MOD_CRUSH" )
1304
				victim _suicide();
1305
1306
			return;
1307
		}
1308
1309
		if ( ( isSubStr( sMeansOfDeath, "MOD_GRENADE" ) || isSubStr( sMeansOfDeath, "MOD_EXPLOSIVE" ) || isSubStr( sMeansOfDeath, "MOD_PROJECTILE" ) ) && isDefined( eInflictor ) && isDefined( eAttacker ) )
1310
		{
1311
			// protect players from spawnkill grenades
1312
			if ( eInflictor.classname == "grenade" && ( victim.lastSpawnTime + 3500 ) > getTime() && isDefined( victim.lastSpawnPoint ) && distance( eInflictor.origin, victim.lastSpawnPoint.origin ) < 250 )
1313
			{
1314
				prof_end( "PlayerDamage player" );
1315
				return;
1316
			}
1317
1318
			victim.explosiveInfo = [];
1319
			victim.explosiveInfo[ "damageTime" ] = getTime();
1320
			victim.explosiveInfo[ "damageId" ] = eInflictor getEntityNumber();
1321
			victim.explosiveInfo[ "returnToSender" ] = false;
1322
			victim.explosiveInfo[ "counterKill" ] = false;
1323
			victim.explosiveInfo[ "chainKill" ] = false;
1324
			victim.explosiveInfo[ "cookedKill" ] = false;
1325
			victim.explosiveInfo[ "throwbackKill" ] = false;
1326
			victim.explosiveInfo[ "suicideGrenadeKill" ] = false;
1327
			victim.explosiveInfo[ "weapon" ] = sWeapon;
1328
1329
			isFrag = isSubStr( sWeapon, "frag_" );
1330
1331
			if ( eAttacker != victim )
1332
			{
1333
				if ( ( isSubStr( sWeapon, "c4_" ) || isSubStr( sWeapon, "claymore_" ) ) && isDefined( eAttacker ) && isDefined( eInflictor.owner ) )
1334
				{
1335
					victim.explosiveInfo[ "returnToSender" ] = ( eInflictor.owner == victim );
1336
					victim.explosiveInfo[ "counterKill" ] = isDefined( eInflictor.wasDamaged );
1337
					victim.explosiveInfo[ "chainKill" ] = isDefined( eInflictor.wasChained );
1338
					victim.explosiveInfo[ "bulletPenetrationKill" ] = isDefined( eInflictor.wasDamagedFromBulletPenetration );
1339
					victim.explosiveInfo[ "cookedKill" ] = false;
1340
				}
1341
1342
				if ( isDefined( eAttacker.lastGrenadeSuicideTime ) && eAttacker.lastGrenadeSuicideTime >= gettime() - 50 && isFrag )
1343
					victim.explosiveInfo[ "suicideGrenadeKill" ] = true;
1344
			}
1345
1346
			if ( isFrag )
1347
			{
1348
				victim.explosiveInfo[ "cookedKill" ] = isDefined( eInflictor.isCooked );
1349
				victim.explosiveInfo[ "throwbackKill" ] = isDefined( eInflictor.threwBack );
1350
			}
1351
			
1352
			victim.explosiveInfo[ "stickKill" ] = isDefined( eInflictor.isStuck ) && eInflictor.isStuck == "enemy";
1353
			victim.explosiveInfo[ "stickFriendlyKill" ] = isDefined( eInflictor.isStuck ) && eInflictor.isStuck == "friendly";
1354
		}
1355
	
1356
		if ( isPlayer( eAttacker ) )
1357
			eAttacker.pers[ "participation" ]++ ;
1358
1359
		prevHealthRatio = victim.health / victim.maxhealth;
1360
1361
		if ( attackerIsHittingTeammate )
1362
		{
1363
			if ( !matchMakingGame() && isPlayer(eAttacker) )
1364
				eAttacker incPlayerStat( "mostff", 1 );
1365
			
1366
			prof_begin( "PlayerDamage player" );// profs automatically end when the function returns
1367
			if ( level.friendlyfire == 0 || ( !isPlayer(eAttacker) && level.friendlyfire != 1 ) )// no one takes damage
1368
			{
1369
				if ( sWeapon == "artillery_mp" || sWeapon == "stealth_bomb_mp" )
1370
					victim damageShellshockAndRumble( eInflictor, sWeapon, sMeansOfDeath, iDamage, iDFlags, eAttacker );
1371
				return;
1372
			}
1373
			else if ( level.friendlyfire == 1 )// the friendly takes damage
1374
			{
1375
				if ( iDamage < 1 )
1376
					iDamage = 1;
1377
1378
				victim.lastDamageWasFromEnemy = false;
1379
1380
				victim finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction );
1381
			}
1382
			else if ( ( level.friendlyfire == 2 ) && isReallyAlive( eAttacker ) )// only the attacker takes damage
1383
			{
1384
				iDamage = int( iDamage * .5 );
1385
				if ( iDamage < 1 )
1386
					iDamage = 1;
1387
1388
				eAttacker.lastDamageWasFromEnemy = false;
1389
1390
				eAttacker.friendlydamage = true;
1391
				eAttacker finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction );
1392
				eAttacker.friendlydamage = undefined;
1393
			}
1394
			else if ( level.friendlyfire == 3 && isReallyAlive( eAttacker ) )// both friendly and attacker take damage
1395
			{
1396
				iDamage = int( iDamage * .5 );
1397
				if ( iDamage < 1 )
1398
					iDamage = 1;
1399
1400
				victim.lastDamageWasFromEnemy = false;
1401
				eAttacker.lastDamageWasFromEnemy = false;
1402
1403
				victim finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction );
1404
				if ( isReallyAlive( eAttacker ) )// may have died due to friendly fire punishment
1405
				{
1406
					eAttacker.friendlydamage = true;
1407
					eAttacker finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction );
1408
					eAttacker.friendlydamage = undefined;
1409
				}
1410
			}
1411
1412
			friendly = true;
1413
			
1414
		}
1415
		else// not hitting teammate
1416
		{
1417
			prof_begin( "PlayerDamage world" );
1418
1419
			if ( iDamage < 1 )
1420
				iDamage = 1;
1421
1422
			if ( isDefined( eAttacker ) && isPlayer( eAttacker ) )
1423
				addAttacker( victim, eAttacker, eInflictor, sWeapon, iDamage, vPoint, vDir, sHitLoc, psOffsetTime, sMeansOfDeath );
1424
			
1425
			if ( sMeansOfDeath == "MOD_EXPLOSIVE" || sMeansOfDeath == "MOD_GRENADE_SPLASH" && iDamage < victim.health )
1426
				victim notify( "survived_explosion" );
1427
1428
			if ( isdefined( eAttacker ) )
1429
				level.lastLegitimateAttacker = eAttacker;
1430
1431
			if ( isdefined( eAttacker ) && isPlayer( eAttacker ) && isDefined( sWeapon ) )
1432
				eAttacker thread maps\mp\gametypes\_weapons::checkHit( sWeapon, victim );
1433
1434
			if ( isdefined( eAttacker ) && isPlayer( eAttacker ) && isDefined( sWeapon ) && eAttacker != victim )
1435
			{
1436
				eAttacker thread maps\mp\_events::damagedPlayer( self, iDamage, sWeapon );
1437
				victim.attackerPosition = eAttacker.origin;
1438
			}
1439
			else
1440
			{
1441
				victim.attackerPosition = undefined;
1442
			}
1443
1444
			if ( issubstr( sMeansOfDeath, "MOD_GRENADE" ) && isDefined( eInflictor.isCooked ) )
1445
				victim.wasCooked = getTime();
1446
			else
1447
				victim.wasCooked = undefined;
1448
1449
			victim.lastDamageWasFromEnemy = ( isDefined( eAttacker ) && ( eAttacker != victim ) );
1450
1451
			if ( victim.lastDamageWasFromEnemy )
1452
				eAttacker.damagedPlayers[ victim.guid ] = getTime();
1453
1454
			victim finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction );
1455
1456
			if ( isDefined( level.ac130player ) && isDefined( eAttacker ) && ( level.ac130player == eAttacker ) )
1457
				level notify( "ai_pain", victim );
1458
1459
			victim thread maps\mp\gametypes\_missions::playerDamaged( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, sHitLoc );
1460
1461
			prof_end( "PlayerDamage world" );
1462
			
1463
		}
1464
1465
		if ( attackerIsNPC && isDefined( eAttacker.gunner ) )
1466
			damager = eAttacker.gunner;
1467
		else
1468
			damager = eAttacker;
1469
1470
		if ( isDefined( damager) && damager != victim && iDamage > 0 )
1471
		{
1472
			if ( iDFlags & level.iDFLAGS_STUN )
1473
				typeHit = "stun";
1474
			else if ( victim hasPerk( "specialty_armorvest", true ) || (isExplosiveDamage( sMeansOfDeath ) && victim _hasPerk( "_specialty_blastshield" )) )
1475
				typeHit = "hitBodyArmor";
1476
			else if ( victim _hasPerk( "specialty_combathigh") )
1477
				typeHit = "hitEndGame";
1478
			else
1479
				typeHit = "standard";
1480
				
1481
			damager thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback( typeHit );
1482
		}
1483
1484
		victim.hasDoneCombat = true;
1485
	}
1486
1487
	if ( isdefined( eAttacker ) && ( eAttacker != victim ) && !friendly )
1488
		level.useStartSpawns = false;
1489
1490
1491
	//=================
1492
	// Damage Logging
1493
	//=================
1494
1495
	prof_begin( "PlayerDamage log" );
1496
1497
	// why getEntityNumber() for victim and .clientid for attacker?
1498
	if ( getDvarInt( "g_debugDamage" ) )
1499
		println( "client:" + victim getEntityNumber() + " health:" + victim.health + " attacker:" + eAttacker.clientid + " inflictor is player:" + isPlayer( eInflictor ) + " damage:" + iDamage + " hitLoc:" + sHitLoc );
1500
1501
	if ( victim.sessionstate != "dead" )
1502
	{
1503
		lpselfnum = victim getEntityNumber();
1504
		lpselfname = victim.name;
1505
		lpselfteam = victim.pers[ "team" ];
1506
		lpselfGuid = victim.guid;
1507
		lpattackerteam = "";
1508
1509
		if ( isPlayer( eAttacker ) )
1510
		{
1511
			lpattacknum = eAttacker getEntityNumber();
1512
			lpattackGuid = eAttacker.guid;
1513
			lpattackname = eAttacker.name;
1514
			lpattackerteam = eAttacker.pers[ "team" ];
1515
		}
1516
		else
1517
		{
1518
			lpattacknum = -1;
1519
			lpattackGuid = "";
1520
			lpattackname = "";
1521
			lpattackerteam = "world";
1522
		}
1523
1524
		logPrint( "D;" + lpselfGuid + ";" + lpselfnum + ";" + lpselfteam + ";" + lpselfname + ";" + lpattackGuid + ";" + lpattacknum + ";" + lpattackerteam + ";" + lpattackname + ";" + sWeapon + ";" + iDamage + ";" + sMeansOfDeath + ";" + sHitLoc + "\n" );
1525
	}
1526
1527
	HitlocDebug( eAttacker, victim, iDamage, sHitLoc, iDFlags );
1528
1529
	/*if( isDefined( eAttacker ) && eAttacker != victim )
1530
	{
1531
		if ( isPlayer( eAttacker ) )
1532
			eAttacker incPlayerStat( "damagedone", iDamage );
1533
		
1534
		victim incPlayerStat( "damagetaken", iDamage );
1535
	}*/
1536
1537
	prof_end( "PlayerDamage log" );
1538
}
1539
1540
1541
addAttacker( victim, eAttacker, eInflictor, sWeapon, iDamage, vPoint, vDir, sHitLoc, psOffsetTime, sMeansOfDeath )
1542
{
1543
	if ( !isDefined( victim.attackerData ) )
1544
		victim.attackerData = [];
1545
	
1546
	if ( !isDefined( victim.attackerData[ eAttacker.guid ] ) )
1547
	{
1548
		victim.attackers[ eAttacker.guid ] = eAttacker;
1549
		// we keep an array of attackers by their client ID so we can easily tell
1550
		// if they're already one of the existing attackers in the above if().
1551
		// we store in this array data that is useful for other things, like challenges
1552
		victim.attackerData[ eAttacker.guid ] = SpawnStruct();
1553
		victim.attackerData[ eAttacker.guid ].damage = 0;	
1554
		victim.attackerData[ eAttacker.guid ].attackerEnt = eAttacker;
1555
		victim.attackerData[ eAttacker.guid ].firstTimeDamaged = getTime();				
1556
	}
1557
	if ( maps\mp\gametypes\_weapons::isPrimaryWeapon( sWeapon ) && ! maps\mp\gametypes\_weapons::isSideArm( sWeapon ) )
1558
		victim.attackerData[ eAttacker.guid ].isPrimary = true;
1559
	
1560
	victim.attackerData[ eAttacker.guid ].damage += iDamage;
1561
	victim.attackerData[ eAttacker.guid ].weapon = sWeapon;
1562
	victim.attackerData[ eAttacker.guid ].vPoint = vPoint;
1563
	victim.attackerData[ eAttacker.guid ].vDir = vDir;
1564
	victim.attackerData[ eAttacker.guid ].sHitLoc = sHitLoc;
1565
	victim.attackerData[ eAttacker.guid ].psOffsetTime = psOffsetTime;
1566
	victim.attackerData[ eAttacker.guid ].sMeansOfDeath = sMeansOfDeath;
1567
	victim.attackerData[ eAttacker.guid ].attackerEnt = eAttacker;
1568
	victim.attackerData[ eAttacker.guid ].lasttimeDamaged = getTime();
1569
	
1570
	if ( isDefined( eInflictor ) && !isPlayer( eInflictor ) && isDefined( eInflictor.primaryWeapon ) )
1571
		victim.attackerData[ eAttacker.guid ].sPrimaryWeapon = eInflictor.primaryWeapon;
1572
	else if ( isDefined( eAttacker ) && isPlayer( eAttacker ) && eAttacker getCurrentPrimaryWeapon() != "none" )
1573
		victim.attackerData[ eAttacker.guid ].sPrimaryWeapon = eAttacker getCurrentPrimaryWeapon();
1574
	else
1575
		victim.attackerData[ eAttacker.guid ].sPrimaryWeapon = undefined;
1576
}
1577
1578
resetAttackerList()
1579
{
1580
	self endon( "disconnect" );
1581
	self endon( "death" );
1582
	level endon( "game_ended" );
1583
	
1584
	//wait is to offset premature calling in _healthOverlay
1585
	wait( 1.75 ); 
1586
	self.attackers = [];
1587
	self.attackerData = [];
1588
}
1589
1590
1591
Callback_PlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime )
1592
{
1593
	Callback_PlayerDamage_internal( eInflictor, eAttacker, self, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime );
1594
}
1595
1596
1597
finishPlayerDamageWrapper( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction )
1598
{
1599
	if ( (self isUsingRemote() ) && (iDamage >= self.health) && !(iDFlags & level.iDFLAGS_STUN) )
1600
	{
1601
		if ( !isDefined( vDir ) )
1602
			vDir = ( 0,0,0 );
1603
1604
		if ( !isDefined( eAttacker ) && !isDefined( eInflictor ) )
1605
		{
1606
			eAttacker = self;
1607
			eInflictor = eAttacker;
1608
		}
1609
		
1610
		assert( isDefined( eAttacker ) );
1611
		assert( isDefined( eInflictor ) );
1612
1613
		PlayerKilled_internal( eInflictor, eAttacker, self, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, 0, true );
1614
	}
1615
	else
1616
	{
1617
		if ( !self Callback_KillingBlow( eInflictor, eAttacker, iDamage - (iDamage * stunFraction), iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime ) )
1618
			return;
1619
1620
		self finishPlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, stunFraction );
1621
	}
1622
1623
	if ( sMeansOfDeath == "MOD_EXPLOSIVE_BULLET" )
1624
		self shellShock( "damage_mp", getDvarFloat( "scr_csmode" ) );
1625
1626
	self damageShellshockAndRumble( eInflictor, sWeapon, sMeansOfDeath, iDamage, iDFlags, eAttacker );
1627
}
1628
1629
1630
Callback_PlayerLastStand( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration )
1631
{	
1632
		
1633
	lastStandParams = spawnStruct();
1634
	lastStandParams.eInflictor = eInflictor;
1635
	lastStandParams.attacker = attacker;
1636
	lastStandParams.iDamage = iDamage;
1637
	lastStandParams.attackerPosition = attacker.origin;
1638
	if ( attacker == self )
1639
		lastStandParams.sMeansOfDeath = "MOD_SUICIDE";
1640
	else
1641
		lastStandParams.sMeansOfDeath = sMeansOfDeath;
1642
	
1643
	lastStandParams.sWeapon = sWeapon;
1644
	if ( isDefined( attacker ) && isPlayer( attacker ) && attacker getCurrentPrimaryWeapon() != "none" )
1645
		lastStandParams.sPrimaryWeapon = attacker getCurrentPrimaryWeapon();
1646
	else
1647
		lastStandParams.sPrimaryWeapon = undefined;
1648
	lastStandParams.vDir = vDir;
1649
	lastStandParams.sHitLoc = sHitLoc;
1650
	lastStandParams.lastStandStartTime = getTime();
1651
1652
	mayDoLastStand = mayDoLastStand( sWeapon, sMeansOfDeath, sHitLoc );
1653
	
1654
	//if ( mayDoLastStand )
1655
	//	mayDoLastStand = !self checkForceBleedOut();
1656
	
1657
	if ( isDefined( self.endGame ) )
1658
		mayDoLastStand = false;
1659
	
1660
	if ( level.teamBased && isDefined( attacker.team ) && attacker.team == self.team )
1661
		mayDoLastStand = false;
1662
1663
	 /#
1664
	if ( getdvar( "scr_forcelaststand" ) == "1" )
1665
		mayDoLastStand = true;
1666
	#/
1667
	
1668
	if ( !mayDoLastStand )
1669
	{
1670
		self.lastStandParams = lastStandParams;
1671
		self.useLastStandParams = true;
1672
		self _suicide();
1673
		return;
1674
	}
1675
	
1676
	self.inLastStand = true;
1677
1678
	notifyData = spawnStruct();
1679
	if ( self _hasPerk( "specialty_finalstand" ) )
1680
	{
1681
		notifyData.titleText = game[ "strings" ][ "final_stand" ];
1682
		notifyData.iconName = "specialty_finalstand";
1683
	}
1684
	else
1685
	{
1686
		notifyData.titleText = game[ "strings" ][ "last_stand" ];
1687
		notifyData.iconName = "specialty_pistoldeath";
1688
	}
1689
	notifyData.glowColor = ( 1, 0, 0 );
1690
	notifyData.sound = "mp_last_stand";
1691
	notifyData.duration = 2.0;
1692
1693
	self.health = 1;
1694
1695
	self thread maps\mp\gametypes\_hud_message::notifyMessage( notifyData );
1696
1697
	grenadeTypePrimary = "frag_grenade_mp";
1698
1699
	if ( isDefined( level.ac130player ) && isDefined( attacker ) && level.ac130player == attacker )
1700
		level notify( "ai_crawling", self );
1701
	
1702
	if ( self _hasPerk( "specialty_finalstand" ) )
1703
	{
1704
		self.lastStandParams = lastStandParams;
1705
		self.inFinalStand = true;
1706
		
1707
		weaponList = self GetWeaponsListExclusives();
1708
		foreach ( weapon in weaponList )
1709
			self takeWeapon( weapon );
1710
		
1711
		self _disableUsability();
1712
1713
		self thread enableLastStandWeapons();
1714
		self thread lastStandTimer( 20, true );		
1715
	}
1716
	/*
1717
	else if ( self _hasPerk( "specialty_c4death" ) )
1718
	{
1719
		self.lastStandParams = lastStandParams;
1720
1721
		self takeAllWeapons();
1722
		self giveWeapon( "c4Death_mp", 0, false );
1723
		self switchToWeapon( "c4Death_mp" );
1724
		self _disableUsability();
1725
		self.inC4Death = true;
1726
				
1727
		//self thread dieAfterTime( 7 );
1728
		self thread lastStandTimer( 10, false );	
1729
		self thread detonateOnUse();
1730
		//self thread detonateOnDeath();	
1731
	}
1732
	*/
1733
	else if ( level.dieHardMode )
1734
	{	
1735
		self.lastStandParams = lastStandParams;
1736
		self thread enableLastStandWeapons();
1737
		self thread lastStandTimer( 20, false );
1738
		self _disableUsability();
1739
	}
1740
	else // normal last stand
1741
	{
1742
		self.lastStandParams = lastStandParams;
1743
		
1744
		pistolWeapon = undefined;
1745
		
1746
		weaponsList = self GetWeaponsListPrimaries();
1747
		foreach ( weapon in weaponsList )
1748
		{
1749
			if ( maps\mp\gametypes\_weapons::isSideArm( weapon ) )
1750
				pistolWeapon = weapon;			
1751
		}
1752
			
1753
		if ( !isDefined( pistolWeapon ) )
1754
		{
1755
			pistolWeapon = "beretta_mp";
1756
			self _giveWeapon( pistolWeapon );
1757
		}
1758
	
1759
		self giveMaxAmmo( pistolWeapon );
1760
		self DisableWeaponSwitch();
1761
		self _disableUsability();
1762
		
1763
		if ( !self _hasPerk("specialty_laststandoffhand") )
1764
			self DisableOffhandWeapons();
1765
				
1766
		self switchToWeapon( pistolWeapon );
1767
		
1768
		self thread lastStandTimer( 10, false );
1769
	}
1770
}
1771
1772
dieAfterTime( time )
1773
{
1774
	self endon( "death" );
1775
	self endon( "disconnect" );
1776
	self endon( "joined_team" );
1777
	level endon( "game_ended" );
1778
	
1779
	wait ( time );
1780
	self.useLastStandParams = true;
1781
	self _suicide();
1782
}
1783
1784
detonateOnUse()
1785
{
1786
	self endon( "death" );
1787
	self endon( "disconnect" );
1788
	self endon( "joined_team" );
1789
	level endon( "game_ended" );
1790
	
1791
	self waittill( "detonate" );
1792
	self.useLastStandParams = true;
1793
	self c4DeathDetonate();
1794
}
1795
1796
detonateOnDeath()
1797
{
1798
	self endon( "detonate" );
1799
	self endon( "disconnect" );
1800
	self endon( "joined_team" );
1801
	level endon( "game_ended" );
1802
	
1803
	self waittill( "death" );
1804
	self c4DeathDetonate();
1805
}
1806
1807
c4DeathDetonate()
1808
{
1809
	self playSound( "detpack_explo_default" );
1810
	self.c4DeathEffect = playFX( level.c4Death, self.origin );
1811
	RadiusDamage( self.origin, 400, 100, 100, self );
1812
	
1813
	if ( isAlive( self ) )
1814
		self _suicide();
1815
}
1816
1817
enableLastStandWeapons()
1818
{
1819
	self endon( "death" );
1820
	self endon( "disconnect" );
1821
	level endon( "game_ended" );
1822
1823
	self freezeControlsWrapper( true );
1824
	wait .30;
1825
	
1826
	self freezeControlsWrapper( false );
1827
}
1828
1829
lastStandTimer( delay, isFinalStand )
1830
{
1831
	self endon( "death" );
1832
	self endon( "disconnect" );
1833
	self endon( "revive");
1834
	level endon( "game_ended" );
1835
	
1836
	level notify ( "player_last_stand" );
1837
	
1838
	self thread lastStandWaittillDeath();
1839
	
1840
	self.lastStand = true;
1841
	
1842
	if ( !isFinalStand && !level.dieHardMode && ( !isDefined( self.inC4Death ) || !self.inC4Death ) )
1843
	{
1844
		self thread lastStandAllowSuicide();
1845
		self setLowerMessage( "last_stand", &"PLATFORM_COWARDS_WAY_OUT" );
1846
		self thread lastStandKeepOverlay();
1847
	}
1848
	
1849
	if ( level.dieHardMode == 1 && level.dieHardMode != 2 )
1850
	{
1851
		reviveEnt = spawn( "script_model", self.origin );
1852
		reviveEnt setModel( "tag_origin" );
1853
		reviveEnt setCursorHint( "HINT_NOICON" );
1854
		reviveEnt setHintString( &"PLATFORM_REVIVE" );
1855
1856
		reviveEnt reviveSetup( self );
1857
		reviveEnt endon ( "death" );
1858
1859
		reviveIcon = newTeamHudElem( self.team );
1860
		reviveIcon setShader( "waypoint_revive", 8, 8 );
1861
		reviveIcon setWaypoint( true, true );
1862
		reviveIcon SetTargetEnt( self );
1863
		reviveIcon thread destroyOnReviveEntDeath( reviveEnt );
1864
1865
		reviveIcon.color = (0.33, 0.75, 0.24);
1866
		self playDeathSound();
1867
		
1868
		if ( isFinalStand )
1869
		{
1870
			wait( delay );
1871
			
1872
			if ( self.inFinalStand )
1873
				self thread lastStandBleedOut( isFinalStand, reviveEnt );
1874
		}
1875
		
1876
		return;
1877
	}
1878
	else if( level.dieHardMode == 2 )
1879
	{
1880
		self thread lastStandKeepOverlay();
1881
		reviveEnt = spawn( "script_model", self.origin );
1882
		reviveEnt setModel( "tag_origin" );
1883
		reviveEnt setCursorHint( "HINT_NOICON" );
1884
		reviveEnt setHintString( &"PLATFORM_REVIVE" );
1885
1886
		reviveEnt reviveSetup( self );
1887
		reviveEnt endon ( "death" );
1888
1889
		reviveIcon = newTeamHudElem( self.team );
1890
		reviveIcon setShader( "waypoint_revive", 8, 8 );
1891
		reviveIcon setWaypoint( true, true );
1892
		reviveIcon SetTargetEnt( self );
1893
		reviveIcon thread destroyOnReviveEntDeath( reviveEnt );
1894
1895
		reviveIcon.color = (0.33, 0.75, 0.24);
1896
		self playDeathSound();
1897
		
1898
		if ( isFinalStand )
1899
		{
1900
			wait( delay );
1901
			
1902
			if ( self.inFinalStand )
1903
				self thread lastStandBleedOut( isFinalStand, reviveEnt );
1904
		}
1905
		
1906
		wait delay / 3;
1907
		reviveIcon.color = (1.0, 0.64, 0.0);
1908
		
1909
		while ( reviveEnt.inUse )
1910
			wait ( 0.05 );
1911
		
1912
		self playDeathSound();	
1913
		wait delay / 3;
1914
		reviveIcon.color = (1.0, 0.0, 0.0);
1915
1916
		while ( reviveEnt.inUse )
1917
			wait ( 0.05 );
1918
1919
		self playDeathSound();
1920
		wait delay / 3;	
1921
1922
		while ( reviveEnt.inUse )
1923
			wait ( 0.05 );
1924
		
1925
		wait( 0.05 ); 
1926
		self thread lastStandBleedOut( isFinalStand );
1927
		return;
1928
	}
1929
	
1930
	wait( delay );
1931
	self thread lastStandBleedout( isFinalStand );
1932
1933
}
1934
1935
maxHealthOverlay( maxHealth, refresh )
1936
{
1937
	self endon( "stop_maxHealthOverlay" );
1938
	self endon( "revive" );
1939
	self endon( "death" );
1940
	
1941
	for( ;; )
1942
	{
1943
		self.health -= 1;
1944
		self.maxHealth = maxHealth;
1945
		wait( .05 );
1946
		self.maxHealth = 50;
1947
		self.health += 1;
1948
	
1949
		wait ( .50 );
1950
	}	
1951
}
1952
1953
lastStandBleedOut( reviveOnBleedOut, reviveEnt )
1954
{
1955
	if ( reviveOnBleedOut )
1956
	{
1957
		self.lastStand = undefined;
1958
		self.inFinalStand = false;
1959
		self clearLowerMessage( "last_stand" );
1960
		maps\mp\gametypes\_playerlogic::lastStandRespawnPlayer();
1961
		
1962
		if( isDefined( reviveEnt ) )
1963
			reviveEnt Delete();
1964
	}
1965
	else
1966
	{
1967
		self.useLastStandParams = true;
1968
		self.beingRevived = false;
1969
		self _suicide();
1970
	}
1971
}
1972
1973
1974
lastStandAllowSuicide()
1975
{
1976
	self endon( "death" );
1977
	self endon( "disconnect" );
1978
	self endon( "game_ended" );
1979
	self endon( "revive");
1980
1981
	while ( 1 )
1982
	{
1983
		if ( self useButtonPressed() )
1984
		{
1985
			pressStartTime = gettime();
1986
			while ( self useButtonPressed() )
1987
			{
1988
				wait .05;
1989
				if ( gettime() - pressStartTime > 700 )
1990
					break;
1991
			}
1992
			if ( gettime() - pressStartTime > 700 )
1993
				break;
1994
		}
1995
		wait .05;
1996
	}
1997
1998
	self thread lastStandBleedOut( false );
1999
}
2000
2001
lastStandKeepOverlay()
2002
{
2003
	level endon( "game_ended" );
2004
	self endon( "death" );
2005
	self endon( "disconnect" );
2006
	self endon( "revive" );
2007
2008
	// keep the health overlay going by making code think the player is getting damaged
2009
	while ( !level.gameEnded )
2010
	{
2011
		self.health = 2;
2012
		wait .05;
2013
		self.health = 1;
2014
		wait .5;
2015
	}
2016
	
2017
	self.health = self.maxhealth;
2018
}
2019
2020
2021
lastStandWaittillDeath()
2022
{
2023
	self endon( "disconnect" );
2024
	self endon( "revive" );
2025
	level endon( "game_ended" );
2026
	self waittill( "death" );
2027
2028
	self clearLowerMessage( "last_stand" );
2029
	self.lastStand = undefined;
2030
}
2031
2032
2033
mayDoLastStand( sWeapon, sMeansOfDeath, sHitLoc )
2034
{
2035
	if ( sMeansOfDeath == "MOD_TRIGGER_HURT" )
2036
		return false;
2037
	
2038
	if ( sMeansOfDeath != "MOD_PISTOL_BULLET" && sMeansOfDeath != "MOD_RIFLE_BULLET" && sMeansOfDeath != "MOD_FALLING" && sMeansOfDeath != "MOD_EXPLOSIVE_BULLET" )
2039
		return false;
2040
2041
	if ( sMeansOfDeath == "MOD_IMPACT" && sWeapon == "throwingknife_mp" )
2042
		return false;
2043
		
2044
	if ( sMeansOfDeath == "MOD_IMPACT" && ( sWeapon == "m79_mp" || isSubStr(sWeapon, "gl_") ) )
2045
		return false;
2046
2047
	if ( isHeadShot( sWeapon, sHitLoc, sMeansOfDeath ) )
2048
		return false;
2049
	
2050
	if ( self isUsingRemote() )
2051
		return false;
2052
2053
	return true;
2054
}
2055
2056
2057
ensureLastStandParamsValidity()
2058
{
2059
	// attacker may have become undefined if the player that killed me has disconnected
2060
	if ( !isDefined( self.lastStandParams.attacker ) )
2061
		self.lastStandParams.attacker = self;
2062
}
2063
2064
getHitLocHeight( sHitLoc )
2065
{
2066
	switch( sHitLoc )
2067
	{
2068
		case "helmet":
2069
		case "head":
2070
		case "neck":
2071
			return 60;
2072
		case "torso_upper":
2073
		case "right_arm_upper":
2074
		case "left_arm_upper":
2075
		case "right_arm_lower":
2076
		case "left_arm_lower":
2077
		case "right_hand":
2078
		case "left_hand":
2079
		case "gun":
2080
			return 48;
2081
		case "torso_lower":
2082
			return 40;
2083
		case "right_leg_upper":
2084
		case "left_leg_upper":
2085
			return 32;
2086
		case "right_leg_lower":
2087
		case "left_leg_lower":
2088
			return 10;
2089
		case "right_foot":
2090
		case "left_foot":
2091
			return 5;
2092
	}
2093
	return 48;
2094
}
2095
2096
delayStartRagdoll( ent, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath )
2097
{
2098
	if ( isDefined( ent ) )
2099
	{
2100
		deathAnim = ent getCorpseAnim();
2101
		if ( animhasnotetrack( deathAnim, "ignore_ragdoll" ) )
2102
			return;
2103
	}
2104
2105
	wait( 0.2 );
2106
2107
	if ( !isDefined( ent ) )
2108
		return;
2109
2110
	if ( ent isRagDoll() )
2111
		return;
2112
2113
	deathAnim = ent getcorpseanim();
2114
2115
	startFrac = 0.35;
2116
2117
	if ( animhasnotetrack( deathAnim, "start_ragdoll" ) )
2118
	{
2119
		times = getnotetracktimes( deathAnim, "start_ragdoll" );
2120
		if ( isDefined( times ) )
2121
			startFrac = times[ 0 ];
2122
	}
2123
2124
	waitTime = startFrac * getanimlength( deathAnim );
2125
	wait( waitTime );
2126
2127
	if ( isDefined( ent ) )
2128
	{
2129
		ent startragdoll( 1 );
2130
	}
2131
}
2132
2133
2134
getMostKilledBy()
2135
{
2136
	mostKilledBy = "";
2137
	killCount = 0;
2138
2139
	killedByNames = getArrayKeys( self.killedBy );
2140
2141
	for ( index = 0; index < killedByNames.size; index++ )
2142
	{
2143
		killedByName = killedByNames[ index ];
2144
		if ( self.killedBy[ killedByName ] <= killCount )
2145
			continue;
2146
2147
		killCount = self.killedBy[ killedByName ];
2148
		mostKilleBy = killedByName;
2149
	}
2150
2151
	return mostKilledBy;
2152
}
2153
2154
2155
getMostKilled()
2156
{
2157
	mostKilled = "";
2158
	killCount = 0;
2159
2160
	killedNames = getArrayKeys( self.killedPlayers );
2161
2162
	for ( index = 0; index < killedNames.size; index++ )
2163
	{
2164
		killedName = killedNames[ index ];
2165
		if ( self.killedPlayers[ killedName ] <= killCount )
2166
			continue;
2167
2168
		killCount = self.killedPlayers[ killedName ];
2169
		mostKilled = killedName;
2170
	}
2171
2172
	return mostKilled;
2173
}
2174
2175
2176
damageShellshockAndRumble( eInflictor, sWeapon, sMeansOfDeath, iDamage, iDFlags, eAttacker )
2177
{
2178
	self thread maps\mp\gametypes\_weapons::onWeaponDamage( eInflictor, sWeapon, sMeansOfDeath, iDamage, eAttacker );
2179
	self PlayRumbleOnEntity( "damage_heavy" );
2180
}
2181
2182
2183
reviveSetup( owner )
2184
{
2185
	team = owner.team;
2186
	
2187
	self linkTo( owner, "tag_origin" );
2188
2189
	self.owner = owner;
2190
	self.inUse = false;
2191
	self makeUsable();
2192
	self updateUsableByTeam( team );
2193
	self thread trackTeamChanges( team );
2194
	
2195
	self thread reviveTriggerThink( team );
2196
	
2197
	self thread deleteOnReviveOrDeathOrDisconnect();
2198
}
2199
2200
2201
deleteOnReviveOrDeathOrDisconnect()
2202
{
2203
	self endon ( "death" );
2204
	
2205
	self.owner waittill_any ( "death", "disconnect" );
2206
	
2207
	self delete();
2208
}
2209
2210
2211
updateUsableByTeam( team )
2212
{
2213
	foreach (player in level.players)
2214
	{
2215
		if ( team == player.team && player != self.owner )
2216
			self enablePlayerUse( player );	
2217
		else
2218
			self disablePlayerUse( player );	
2219
	}	
2220
}
2221
2222
2223
trackTeamChanges( team )
2224
{
2225
	self endon ( "death" );
2226
	
2227
	while ( true )
2228
	{
2229
		level waittill ( "joined_team" );
2230
		
2231
		self updateUsableByTeam( team );
2232
	}
2233
}
2234
2235
2236
trackLastStandChanges( team )
2237
{
2238
	self endon ( "death" );
2239
	
2240
	while ( true )
2241
	{
2242
		level waittill ( "player_last_stand" );
2243
		
2244
		self updateUsableByTeam( team );
2245
	}
2246
}
2247
2248
2249
reviveTriggerThink( team )
2250
{
2251
	self endon ( "death" );
2252
	level endon ( "game_ended" );
2253
	
2254
	for ( ;; )
2255
	{
2256
		self waittill ( "trigger", player );
2257
		self.owner.beingRevived = true;
2258
2259
		if ( isDefined(player.beingRevived) && player.beingRevived )
2260
		{
2261
			self.owner.beingRevived = false;
2262
			continue;
2263
		}
2264
			
2265
		self makeUnUsable();
2266
		self.owner freezeControlsWrapper( true );
2267
2268
		revived = self useHoldThink( player );
2269
		self.owner.beingRevived = false;
2270
		
2271
		if ( !isAlive( self.owner ) )
2272
		{	
2273
			self delete();
2274
			return;
2275
		}
2276
2277
		self.owner freezeControlsWrapper( false );
2278
			
2279
		if ( revived )
2280
		{
2281
			player thread maps\mp\gametypes\_hud_message::SplashNotifyDelayed( "reviver", 200 );
2282
			player thread maps\mp\gametypes\_rank::giveRankXP( "reviver", 200 );
2283
2284
			self.owner.lastStand = undefined;
2285
			self.owner clearLowerMessage( "last_stand" );
2286
			
2287
			if ( self.owner _hasPerk( "specialty_lightweight" ) )
2288
				self.owner.moveSpeedScaler = 1.07;
2289
			else
2290
				self.owner.moveSpeedScaler = 1;
2291
			
2292
			self.owner.maxHealth = 100;
2293
			
2294
			self.owner maps\mp\gametypes\_weapons::updateMoveSpeedScale( "primary" );
2295
			self.owner maps\mp\gametypes\_playerlogic::lastStandRespawnPlayer();
2296
2297
			self.owner setPerk( "specialty_pistoldeath", true );
2298
			self.owner.beingRevived = false;
2299
			
2300
			self delete();
2301
			return;
2302
		}
2303
			
2304
		self makeUsable();
2305
		self updateUsableByTeam( team );
2306
	}
2307
}
2308
2309
2310
2311
/*
2312
=============
2313
useHoldThink
2314
2315
Claims the use trigger for player and displays a use bar
2316
Returns true if the player sucessfully fills the use bar
2317
=============
2318
*/
2319
useHoldThink( player )
2320
{
2321
	reviveSpot = spawn( "script_origin", self.origin );
2322
	reviveSpot hide();	
2323
	player playerLinkTo( reviveSpot );		
2324
	player PlayerLinkedOffsetEnable();
2325
	
2326
	player _disableWeapon();
2327
	
2328
	self.curProgress = 0;
2329
	self.inUse = true;
2330
	self.useRate = 0;
2331
	self.useTime = 3000;
2332
	
2333
	player thread personalUseBar( self );
2334
	
2335
	result = useHoldThinkLoop( player );
2336
	
2337
	if ( isDefined( player ) && isReallyAlive( player ) )
2338
	{
2339
		player Unlink();
2340
		player _enableWeapon();
2341
	}
2342
2343
	if ( isDefined( result ) && result )
2344
	{
2345
		self.owner thread maps\mp\gametypes\_hud_message::playerCardSplashNotify( "revived", player );
2346
		self.owner.inlaststand = false;
2347
		return true;
2348
	}
2349
	
2350
	self.inUse = false;
2351
	reviveSpot Delete();	
2352
	return false;
2353
}
2354
2355
2356
personalUseBar( object )
2357
{
2358
	useBar = self createPrimaryProgressBar();
2359
	useBarText = self createPrimaryProgressBarText();
2360
	useBarText setText( &"MPUI_REVIVING" );
2361
2362
	objUseBar = object.owner createPrimaryProgressBar();
2363
	objUseBarText = object.owner createPrimaryProgressBarText();
2364
	objUseBarText setText( &"MPUI_BEING_REVIVED" );
2365
2366
	lastRate = -1;
2367
	while ( isReallyAlive( self ) && isDefined( object ) && object.inUse && !level.gameEnded && isDefined( self ) )
2368
	{
2369
		if ( lastRate != object.useRate )
2370
		{
2371
			if( object.curProgress > object.useTime)
2372
				object.curProgress = object.useTime;
2373
				
2374
			useBar updateBar( object.curProgress / object.useTime, (1000 / object.useTime) * object.useRate );
2375
			objUseBar updateBar( object.curProgress / object.useTime, (1000 / object.useTime) * object.useRate );
2376
2377
			if ( !object.useRate )
2378
			{
2379
				useBar hideElem();
2380
				useBarText hideElem();
2381
2382
				objUseBar hideElem();
2383
				objUseBarText hideElem();
2384
			}
2385
			else
2386
			{
2387
				useBar showElem();
2388
				useBarText showElem();
2389
2390
				objUseBar showElem();
2391
				objUseBarText showElem();
2392
			}
2393
		}	
2394
		lastRate = object.useRate;
2395
		wait ( 0.05 );
2396
	}
2397
	
2398
	// when the players disconnect the hudElems are destroyed automatically
2399
	if ( isDefined( useBar ) )
2400
		useBar destroyElem();
2401
	if ( isDefined( useBarText ) )
2402
		useBarText destroyElem();
2403
2404
	if ( isDefined( objUseBar ) )
2405
		objUseBar destroyElem();
2406
	if ( isDefined( objUseBarText ) )
2407
		objUseBarText destroyElem();
2408
}
2409
2410
2411
useHoldThinkLoop( player )
2412
{
2413
	level endon ( "game_ended" );
2414
	self.owner endon( "death" );
2415
	self.owner endon( "disconnect" );
2416
2417
	while( isReallyAlive( player ) && player useButtonPressed() && self.curProgress < self.useTime  )
2418
	{
2419
		self.curProgress += (50 * self.useRate);
2420
		self.useRate = 1; /* * player.objectiveScaler;*/
2421
2422
		if ( self.curProgress >= self.useTime )
2423
		{
2424
			self.inUse = false;
2425
			
2426
			return isReallyAlive( player );
2427
		}
2428
		
2429
		wait 0.05;
2430
	}
2431
	
2432
	return false;
2433
}
2434
2435
2436
Callback_KillingBlow( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime )
2437
{
2438
	if ( isDefined(self.lastDamageWasFromEnemy) && self.lastDamageWasFromEnemy && iDamage >= self.health && isDefined( self.combatHigh ) && self.combatHigh == "specialty_endgame" )
2439
	{
2440
		self setAdrenaline( 0 );
2441
		self _setPerk( "specialty_endgame" );
2442
		return false;
2443
	}
2444
	
2445
	return true;
2446
}
2447
2448
2449
emitFallDamage( iDamage )
2450
{
2451
	PhysicsExplosionSphere( self.origin, 64, 64, 1 );
2452
	
2453
	// get the entities we landed on
2454
	damageEnts = [];
2455
	for ( testAngle = 0; testAngle < 360; testAngle += 30 )
2456
	{
2457
		xOffset = cos( testAngle ) * 16;
2458
		yOffset = sin( testAngle ) * 16;
2459
2460
		traceData = bulletTrace( self.origin + (xOffset, yOffset, 4), self.origin + (xOffset,yOffset,-6), true, self );
2461
		//thread drawLine( self.origin + (xOffset, yOffset, 4), self.origin + (xOffset,yOffset,-6), 10.0 );
2462
		
2463
		if ( isDefined( traceData["entity"] ) && isDefined( traceData["entity"].targetname ) && (traceData["entity"].targetname == "destructible_vehicle" || traceData["entity"].targetname == "destructible_toy") )
2464
			damageEnts[damageEnts.size] = traceData["entity"];
2465
	}
2466
2467
	if ( damageEnts.size )
2468
	{
2469
		damageOwner = spawn( "script_origin", self.origin );
2470
		damageOwner hide();
2471
		damageOwner.type = "soft_landing";
2472
		damageOwner.destructibles = damageEnts;
2473
		radiusDamage( self.origin, 64, 100, 100, damageOwner );
2474
2475
		wait ( 0.1 );	
2476
		damageOwner delete();
2477
	}
2478
}
2479
2480
drawLine( start, end, timeSlice )
2481
{
2482
	drawTime = int(timeSlice * 20);
2483
	for( time = 0; time < drawTime; time++ )
2484
	{
2485
		line( start, end, (1,0,0),false, 1 );
2486
		wait ( 0.05 );
2487
	}
2488
}
2489
2490
isFlankKill( victim, attacker )
2491
{
2492
	victimForward = anglestoforward( victim.angles );
2493
	victimForward = ( victimForward[0], victimForward[1], 0 );
2494
	victimForward = VectorNormalize( victimForward );
2495
2496
	attackDirection = victim.origin - attacker.origin;
2497
	attackDirection = ( attackDirection[0], attackDirection[1], 0 ); 
2498
	attackDirection = VectorNormalize( attackDirection );
2499
2500
	dotProduct = VectorDot( victimForward, attackDirection );
2501
	if ( dotProduct > 0 ) // 0 = cos( 90 ), 180 degree arc total
2502
		return true;
2503
	else
2504
		return false;
2505
}
2506
2507
_obituary( victim, attacker, sWeapon, sMeansOfDeath )
2508
{
2509
	victimTeam = victim.team;
2510
	
2511
	foreach ( player in level.players )
2512
	{
2513
		playerTeam = player.team;
2514
		if ( playerTeam == "spectator" )
2515
			player iPrintLn( &"MP_OBITUARY_NEUTRAL", attacker.name, victim.name );
2516
		else if ( playerTeam == victimTeam )
2517
			player iPrintLn( &"MP_OBITUARY_ENEMY", attacker.name, victim.name );
2518
		else
2519
			player iPrintLn( &"MP_OBITUARY_FRIENDLY", attacker.name, victim.name );
2520
	}
2521
}
2522
2523
2524
logPrintPlayerDeath( lifeId, attacker, iDamage, sMeansOfDeath, sWeapon, sPrimaryWeapon, sHitLoc )
2525
{
2526
	// create a lot of redundant data for the log print
2527
	lpselfnum = self getEntityNumber();
2528
	lpselfname = self.name;
2529
	lpselfteam = self.team;
2530
	lpselfguid = self.guid;
2531
2532
	if ( isPlayer( attacker ) )
2533
	{
2534
		lpattackGuid = attacker.guid;
2535
		lpattackname = attacker.name;
2536
		lpattackerteam = attacker.team;
2537
		lpattacknum = attacker getEntityNumber();
2538
		attackerString = attacker getXuid() + "(" + lpattackname + ")";
2539
	}
2540
	else
2541
	{
2542
		lpattackGuid = "";
2543
		lpattackname = "";
2544
		lpattackerteam = "world";
2545
		lpattacknum = -1;
2546
		attackerString = "none";
2547
	}
2548
2549
	logPrint( "K;" + lpselfguid + ";" + lpselfnum + ";" + lpselfteam + ";" + lpselfname + ";" + lpattackguid + ";" + lpattacknum + ";" + lpattackerteam + ";" + lpattackname + ";" + sWeapon + ";" + iDamage + ";" + sMeansOfDeath + ";" + sHitLoc + "\n" );
2550
}
2551
2552
2553
destroyOnReviveEntDeath( reviveEnt )
2554
{
2555
	reviveEnt waittill ( "death" );
2556
	
2557
	self destroy();
2558
}