#using scripts\codescripts\struct;

#using scripts\shared\callbacks_shared;
#using scripts\shared\challenges_shared;
#using scripts\shared\drown;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#using scripts\shared\weapons\_weapon_utils;

#using scripts\mp\gametypes\_loadout;

    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          

#using scripts\mp\_util;
#using scripts\mp\killstreaks\_counteruav;
#using scripts\mp\killstreaks\_emp;
#using scripts\mp\killstreaks\_killstreaks;
#using scripts\mp\killstreaks\_satellite;
#using scripts\mp\killstreaks\_uav;



                                



#namespace challenges;

function autoexec __init__sytem__() {     system::register("challenges",&__init__,undefined,undefined);    }

function __init__()
{
	callback::on_start_gametype( &start_gametype );
	callback::on_spawned( &on_player_spawn );
	level.heroAbilityActivateNearDeath = &heroAbilityActivateNearDeath;
	level.callbackEndHeroSpecialistEMP = &callbackEndHeroSpecialistEMP;
	level.capturedObjectiveFunction = &capturedObjectiveFunction;
}

function start_gametype()
{
	if ( !isdefined( level.ChallengesCallbacks ) )
	{
		level.ChallengesCallbacks = [];
	}
	
	waittillframeend;
	
	if ( isdefined ( level.scoreEventGameEndCallback ) )
	{
		challenges::registerChallengesCallback( "gameEnd",level.scoreEventGameEndCallback );
	}
	
	if ( canProcessChallenges() )
	{
		challenges::registerChallengesCallback( "playerKilled",&challengeKills );	
		challenges::registerChallengesCallback( "gameEnd",&challengeGameEndMP );
	}

	callback::on_connect( &on_player_connect );
}

function on_player_connect()
{
	initChallengeData();
	self addSpecialistUsedStatOnConnect();
	self thread spawnWatcher();	
	self thread monitorReloads();
	self thread monitorGrenadeFire();
}

function initChallengeData()
{	
	self.pers["bulletStreak"] = 0;
	self.pers["lastBulletKillTime"] = 0;
	self.pers["stickExplosiveKill"] = 0;
	self.pers["carepackagesCalled"] = 0;
	self.pers["challenge_destroyed_air"] = 0;
	self.pers["challenge_destroyed_ground"] = 0;
	self.pers["challenge_anteup_earn"] = 0;
	self.pers["specialistStatAbilityUsage"] = 0;
	self.pers["canSetSpecialistStat"] = self isSpecialistUnlocked();
	self.pers["activeKillstreaks"] = [];
}

function addSpecialistUsedStatOnConnect()
{
	if ( !isdefined(self.pers["challenge_heroweaponkills"]) )
	{
		// intentionally using "used" hero weapon on connect to determine frequency of specialist usage
		// which is the sum of "specialist ability" usage and "specialist weapon" usage.
		heroWeaponName = self GetLoadoutItemRef( 0, "heroWeapon" );
		heroWeapon = GetWeapon( heroWeaponName );
		if ( heroWeapon == level.weaponNone )
		{
			heroAbilityName = self GetHeroAbilityName();
			heroWeapon = GetWeapon( heroAbilityName );
		}
		if ( heroWeapon != level.weaponNone )
		{
			self addweaponstat( heroWeapon, "used", 1 );
		}
		
		self.pers["challenge_heroweaponkills"] = 0;
	}
}

function spawnWatcher()
{
	self endon( "disconnect" );

	self.pers["killNemesis"] = 0;
	self.pers["killsFastMagExt"] = 0;
	self.pers["longshotsPerLife"] = 0;
	self.pers["specialistStatAbilityUsage"] = 0;
	self.challenge_defenderkillcount = 0;
	self.challenge_offenderkillcount = 0;
	self.challenge_offenderProjectileMultiKillcount = 0;
	self.challenge_offenderComlinkKillcount = 0;
	self.challenge_offenderSentryTurretKillCount = 0;
	self.challenge_objectiveDefensiveKillcount = 0;
	self.challenge_objectiveOffensiveKillcount = 0;
	self.challenge_scavengedCount = 0;
	self.challenge_resuppliedNameKills = 0;
	self.challenge_objectiveDefensive = undefined;
	self.challenge_objectiveOffensive = undefined;	
	self.challenge_lastsurvivewithflakfrom = undefined;	
	self.explosiveInfo = [];
	
	for(;;)
	{
		self waittill("spawned_player");
		
		self.weaponKillsThisSpawn = [];
		self.attachmentKillsThisSpawn = [];
		self.challenge_hatchetTossCount = 0;
		self.challenge_hatchetkills = 0;
		self.retreivedBlades = 0;
		self.challenge_combatRobotAttackClientID = [];
		
		self thread watchDoubleJump();
		self thread watchJump();
		self thread watchSwimming();
		self thread watchWallrun();
		self thread watchSlide();
		self thread watchSprint();
		self thread watchScavengeLethal();
		self thread watchWallRunTwoOppositeWallsNoGround();
		self thread watchWeaponChangeComplete();
	}
}

function watchScavengeLethal()
{
	self endon( "death" );
	self endon( "disconnect" );
	
	self.challenge_scavengedCount = 0;
	
	for(;;)
	{
		self waittill( "scavenged_primary_grenade" );
		self.challenge_scavengedCount++;
	}
}

function watchDoublejump()
{
	self endon( "death" );
	self endon( "disconnect" );

	self.challenge_doublejump_begin = 0;
	self.challenge_doublejump_end = 0;
	for(;;)
	{
		ret = util::waittill_any_return( "doublejump_begin", "doublejump_end", "disconnect" );
		switch( ret )
		{
			case "doublejump_begin":
			self.challenge_doublejump_begin = gettime();
			break;
			case "doublejump_end":
			self.challenge_doublejump_end = gettime();
			break;
		}
	}
}

function watchJump()
{
	self endon( "death" );
	self endon( "disconnect" );

	self.challenge_jump_begin = 0;
	self.challenge_jump_end = 0;
	for(;;)
	{
		ret = util::waittill_any_return( "jump_begin", "jump_end", "disconnect" );
		switch( ret )
		{
			case "jump_begin":
			self.challenge_jump_begin = gettime();
			break;
			case "jump_end":
			self.challenge_jump_end = gettime();
			break;
		}
	}
}

function watchSwimming()
{
	self endon( "death" );
	self endon( "disconnect" );

	self.challenge_swimming_begin = 0;
	self.challenge_swimming_end = 0;
	for(;;)
	{
		ret = util::waittill_any_return( "swimming_begin", "swimming_end", "disconnect" );
		switch( ret )
		{
			case "swimming_begin":
			self.challenge_swimming_begin = gettime();
			break;
			case "swimming_end":
			self.challenge_swimming_end = gettime();
			break;
		}
	}
}

function watchWallrun()
{
	self endon( "death" );
	self endon( "disconnect" );

	self.challenge_wallrun_begin = 0;
	self.challenge_wallrun_end = 0;
	for(;;)
	{
		ret = util::waittill_any_return( "wallrun_begin", "wallrun_end", "disconnect" );
		switch( ret )
		{
			case "wallrun_begin":
			self.challenge_wallrun_begin = gettime();
			break;
			case "wallrun_end":
			self.challenge_wallrun_end = gettime();
			break;
		}
	}
}

function watchSlide()
{
	self endon( "death" );
	self endon( "disconnect" );

	self.challenge_slide_begin = 0;
	self.challenge_slide_end = 0;
	for(;;)
	{
		ret = util::waittill_any_return( "slide_begin", "slide_end", "disconnect" );
		switch( ret )
		{
			case "slide_begin":
			self.challenge_slide_begin = gettime();
			break;
			case "slide_end":
			self.challenge_slide_end = gettime();
			break;
		}
	}
}


function watchSprint()
{
	self endon( "death" );
	self endon( "disconnect" );

	self.challenge_sprint_begin = 0;
	self.challenge_sprint_end = 0;
	for(;;)
	{
		ret = util::waittill_any_return( "sprint_begin", "sprint_end", "disconnect" );
		switch( ret )
		{
			case "sprint_begin":
			self.challenge_sprint_begin = gettime();
			break;
			case "sprint_end":
			self.challenge_sprint_end = gettime();
			break;
		}
	}
}



function challengeKills( data )
{
	victim = data.victim;
	attacker = data.attacker;
	time = data.time;
	level.numKills++;
	attacker.lastKilledPlayer			= victim;
	attackerDoubleJumping				= data.attackerDoubleJumping;
	attackerflashbackTime				= data.attackerflashbackTime;
	attackerHeroAbility					= data.attackerHeroAbility;
	attackerHeroAbilityActive			= data.attackerHeroAbilityActive;
	attackerSliding						= data.attackerSliding;
	attackerSpeedburst					= data.attackerSpeedburst;
	attackerTraversing					= data.attackerTraversing;
	attackerVisionPulseActivateTime		= data.attackerVisionPulseActivateTime;
	attackerVisionPulseArray			= data.attackerVisionPulseArray;
	attackerVisionPulseOrigin			= data.attackerVisionPulseOrigin;
	attackerVisionPulseOriginArray		= data.attackerVisionPulseOriginArray;
	attackerWallRunning					= data.attackerWallRunning;
	attackerWasConcussed				= data.attackerWasConcussed;
	attackerWasFlashed					= data.attackerWasFlashed;
	attackerWasHeatWaveStunned			= data.attackerWasHeatWaveStunned;
	attackerWasOnGround					= data.attackerOnGround;
	attackerWasUnderwater				= data.attackerWasUnderwater;
	attackerLastFastReloadTime			= data.attackerLastFastReloadTime;
	lastWeaponBeforeToss				= data.lastWeaponBeforeToss;
	meansOfDeath						= data.sMeansOfDeath;
	ownerWeaponAtLaunch					= data.ownerWeaponAtLaunch;
	victimBedOut						= data.bledOut;
	victimOrigin						= data.victimOrigin;
	victimCombatEfficiencyLastOnTime	= data.victimCombatEfficiencyLastOnTime;
	victimCombatEfficieny				= data.victimCombatEfficieny;
	victimElectrifiedBy					= data.victimElectrifiedBy;
	victimflashbackTime					= data.victimflashbackTime;
	victimHeroAbility					= data.victimHeroAbility;
	victimHeroAbilityActive				= data.victimHeroAbilityActive;
	victimSpeedburst					= data.victimSpeedburst;
	victimSpeedburstLastOnTime			= data.victimSpeedburstLastOnTime;
	victimVisionPulseActivateTime		= data.victimVisionPulseActivateTime;
	victimVisionPulseActivateTime		= data.victimVisionPulseActivateTime;
	victimVisionPulseArray				= data.victimVisionPulseArray;
	victimVisionPulseOrigin				= data.victimVisionPulseOrigin;
	victimVisionPulseOriginArray		= data.victimVisionPulseOriginArray;
	victimAttackersThisSpawn			= data.victimAttackersThisSpawn;
	victimWasDoubleJumping				= data.victimWasDoubleJumping;
	victimWasInSlamState				= data.victimWasInSlamState;
	victimWasLungingWithArmBlades		= data.victimWasLungingWithArmBlades;
	victimWasOnGround					= data.victimOnGround;
	victimWasUnderwater					= data.wasUnderwater;
	victimWasWallRunning				= data.victimWasWallRunning;
	victimLastStunnedBy					= data.victimLastStunnedBy;
	victimActiveProximityGrenades		= data.victim.activeProximityGrenades;
	victimActiveBouncingBetties			= data.victim.activeBouncingBetties;
	attackerLastFlashedBy				= data.attackerLastFlashedBy;
	attackerLastStunnedBy				= data.attackerLastStunnedBy;
	attackerLastStunnedTime				= data.attackerLastStunnedTime;
	attackerWasSliding					= data.attackerWasSliding;
	attackerWasSprinting				= data.attackerWasSprinting;
	wasDefusing							= data.wasDefusing;
	wasPlanting							= data.wasPlanting;
	inflictorOwnerWasSprinting			= data.inflictorOwnerWasSprinting;
	player 								= data.attacker;
	playerOrigin						= data.attackerOrigin;
	weapon 								= data.weapon;
	
	victim_doublejump_begin				= data.victim_doublejump_begin;
	victim_doublejump_end				= data.victim_doublejump_end;
	victim_jump_begin					= data.victim_jump_begin;
	victim_jump_end						= data.victim_jump_end;
	victim_swimming_begin				= data.victim_swimming_begin;
	victim_swimming_end					= data.victim_swimming_end;
	victim_slide_begin					= data.victim_slide_begin;
	victim_slide_end					= data.victim_slide_end;
	victim_wallrun_begin				= data.victim_wallrun_begin;
	victim_wallrun_end					= data.victim_wallrun_end;
	victim_was_drowning					= data.victim_was_drowning;
	
	attacker_doublejump_begin			= data.attacker_doublejump_begin;
	attacker_doublejump_end				= data.attacker_doublejump_end;
	attacker_jump_begin					= data.attacker_jump_begin;
	attacker_jump_end					= data.attacker_jump_end;
	attacker_swimming_begin				= data.attacker_swimming_begin;
	attacker_swimming_end				= data.attacker_swimming_end;
	attacker_slide_begin				= data.attacker_slide_begin;
	attacker_slide_end					= data.attacker_slide_end;
	attacker_wallrun_begin				= data.attacker_wallrun_begin;
	attacker_wallrun_end				= data.attacker_wallrun_end;
	attacker_was_drowning				= data.attacker_was_drowning;
	attacker_sprint_end 				= data.attacker_sprint_end;
	attacker_sprint_begin 				= data.attacker_sprint_begin;
	
	attacker_wallRanTwoOppositeWallsNoGround	= data.attacker_wallRanTwoOppositeWallsNoGround;
	
	inflictorIsCooked = data.inflictorIsCooked;
	inflictorChallenge_hatchetTossCount = data.inflictorChallenge_hatchetTossCount;
	inflictorOwnerWasSprinting = data.inflictorOwnerWasSprinting;
	inflictorPlayerHasEngineerPerk = data.inflictorPlayerHasEngineerPerk;
	
	inflictor =	data.eInflictor; // MAY ALREADY BE REMOVED	


	if ( !isdefined( data.weapon ) )
	{
		return;
	}
	
	if ( !isdefined( player ) || !isplayer( player ) || ( weapon == level.weaponNone ) )
	{
		return;
	}

	// getting the altweapon name is not ideal, but acceptable for BO3's current weapons in MP;
	// TODO: add code-based accessor to parentWeaponName ("Stat Name" in APE)
	weaponClass = util::getWeaponClass( weapon );
	baseWeapon = getBaseWeapon( weapon );
	baseWeaponItemIndex = GetBaseWeaponItemIndex( baseWeapon );
	weaponPurchased = player isItemPurchased( baseWeaponItemIndex );
	victimSupportIndex = victim.team;
	playerSupportIndex = player.team;
	if ( !level.teambased )
	{
		playerSupportIndex = player.entnum;
		victimSupportIndex = victim.entnum;
	}

	if ( meansOfDeath == "MOD_HEAD_SHOT" || meansOfDeath == "MOD_PISTOL_BULLET" || meansOfDeath == "MOD_RIFLE_BULLET" )
	{
		bulletKill = true;
	}
	else
	{
		bulletKill = false;
	}
	
	if ( level.teambased ) 
	{
		if ( player.team == victim.team )
		{
			return;
		}
	}
	else
	{
		if ( player == victim )
		{
			return;
		}
	}
	
	killstreak = killstreaks::get_from_weapon( data.weapon );
	
	if ( !isdefined( killstreak ) )
	{
		player processSpecialistChallenge( "kills" );
		if (  weapon.isheroWeapon == true )
		{
			player processSpecialistChallenge( "kills_weapon" );
			player.heroWeaponKillsThisActivation++;
			player.pers["challenge_heroweaponkills"]++;
			if ( player.pers["challenge_heroweaponkills"] >= 6 )
			{
				player processSpecialistChallenge( "kill_one_game_weapon" );
				player.pers["challenge_heroweaponkills"] = 0;
			}
		}
	}
	
	if ( bulletKill ) 
	{
		if ( weaponPurchased )
		{
			if ( weaponclass == "weapon_sniper" )
			{
				if ( isdefined ( victim.firstTimeDamaged ) && victim.firstTimeDamaged == time )
				{
					player AddPlayerStat( "kill_enemy_one_bullet_sniper", 1 );
					player AddWeaponStat( weapon, "kill_enemy_one_bullet_sniper", 1 );
				}
			}
			else if ( weaponclass == "weapon_cqb" )
			{
				if ( isdefined ( victim.firstTimeDamaged ) && victim.firstTimeDamaged == time )
				{
					player AddPlayerStat( "kill_enemy_one_bullet_shotgun", 1 );
					player AddWeaponStat( weapon, "kill_enemy_one_bullet_shotgun", 1 );
				}
			}
		}
		
		if ( GetDvarInt( "ui_enablePromoTracking", 0 ) && meansOfDeath == "MOD_HEAD_SHOT" )
		{
			util::increment_zmhd_thermometer( 1 );
		}
		
		if ( ( time - data.attacker_swimming_end <= 2000 ) && ( time - data.attacker_doublejump_begin <= 2000 ) )
		{
			player AddPlayerStat( "kill_after_doublejump_out_of_water", 1 );
		}

		if ( attackerWasSliding )
		{
			if ( attacker_doublejump_end == attacker_slide_begin )
			{
				player AddPlayerStat( "kill_while_sliding_from_doublejump", 1 );
			}
		}
		
		if ( ( player IsBonusCardActive( 2, player.class_num ) ) && player IsItemPurchased( GetItemIndexFromRef( "bonuscard_primary_gunfighter_3" ) ) )
		{
			if ( isdefined( weapon.attachments ) && weapon.attachments.size == 6 )
			{
				player AddPlayerStat( "kill_with_gunfighter", 1 );
			}
		}

		//Check for the killstreak 5 challenge completion
		checkkillstreak5( baseWeapon, player );
		
		if ( weapon.isDualWield && weaponPurchased )
		{
			checkDualWield( baseWeapon, player, attacker, time, attackerwassprinting, attacker_sprint_end );
		}
		
		if ( isdefined( weapon.attachments ) && weapon.attachments.size > 0 )
		{
			attachmentName = player GetWeaponOptic( weapon );
	
			if ( isdefined( attachmentName ) && attachmentName != "" && player WeaponHasAttachmentAndUnlocked( weapon, attachmentName ) )
			{
				if ( weapon.attachments.size > 5 && player AllWeaponAttachmentsUnlocked( weapon ) && !isdefined( attacker.tookWeaponFrom[ weapon ] ) )
				{
					player AddPlayerStat( "kill_optic_5_attachments", 1 );
				}
				if ( isdefined( player.attachmentKillsThisSpawn[ attachmentName ] ) )
				{
					player.attachmentKillsThisSpawn[ attachmentName ]++;
					if ( player.attachmentKillsThisSpawn[ attachmentName ] == 5 )
					{
						player AddWeaponStat( weapon, "killstreak_5_attachment", 1 );
					}
				}
				else
				{
					player.attachmentKillsThisSpawn[ attachmentName ] = 1;
				}
				
				if ( weapon_utils::isPistol( weapon.rootweapon ) )
				{
					if ( player weaponHasAttachmentAndUnlocked( weapon, "suppressed", "extbarrel" ) )
					{
						player AddPlayerStat( "kills_pistol_lasersight_suppressor_longbarrel", 1 );
					}
				}
			}
			
			if ( player weaponHasAttachmentAndUnlocked( weapon, "suppressed" ) )
			{
				if ( attacker util::has_hard_wired_perk_purchased_and_equipped() // hardwired
				    && attacker util::has_ghost_perk_purchased_and_equipped() // ghost
				    && attacker util::has_jetquiet_perk_purchased_and_equipped() ) // blast suppressor
				{
					player AddPlayerStat( "kills_suppressor_ghost_hardwired_blastsuppressor", 1 );
				}
			}
			
			if ( player PlayerAds() == 1 )
			{
				if ( isdefined( player.smokeGrenadeTime ) && isdefined( player.smokeGrenadePosition ) )
				{
					if ( player.smokeGrenadeTime + 14000 > time )
					{
						if ( player util::is_looking_at( player.smokeGrenadePosition ) || ( distancesquared( player.origin, player.smokeGrenadePosition ) < 200 * 200  ))
						{
							if ( player weaponHasAttachmentAndUnlocked( weapon, "ir" ) )
							{
								player AddPlayerStat( "kill_with_thermal_and_smoke_ads", 1 );
								player AddWeaponStat( weapon, "kill_thermal_through_smoke", 1 );
							}
						}
					}
				}
			}
				
			if ( weapon.attachments.size > 1 )
			{
				if ( player PlayerAds() == 1 )
				{
					if ( player weaponHasAttachmentAndUnlocked( weapon, "grip", "quickdraw" ) )
					{
						player AddPlayerStat( "kills_ads_quickdraw_and_grip", 1 );
					}
					if ( player weaponHasAttachmentAndUnlocked( weapon, "swayreduc", "stalker" ) )
					{
						player AddPlayerStat( "kills_ads_stock_and_cpu", 1 );
					}
				}
				else
				{
					if ( player weaponHasAttachmentAndUnlocked( weapon, "rf", "steadyaim" ) )
					{
						if ( attacker util::has_fast_hands_perk_purchased_and_equipped() )
						{
							player AddPlayerStat( "kills_hipfire_rapidfire_lasersights_fasthands", 1 );	
						}
					}
				}
				if ( player weaponHasAttachmentAndUnlocked( weapon, "fastreload", "extclip" ) )
				{
					player.pers["killsFastMagExt"]++;
					if ( player.pers["killsFastMagExt"] > 4 ) 
					{
						player AddPlayerStat( "kills_one_life_fastmags_and_extclip", 1 );
						player.pers["killsFastMagExt"] = 0;
					}
				}
			}
			
			if ( weapon.attachments.size > 2 )
			{
				if (  meansOfDeath == "MOD_HEAD_SHOT" ) 
				{
					if ( player weaponHasAttachmentAndUnlocked( weapon, "fmj", "damage", "extbarrel" ) )
					{
						player AddPlayerStat( "headshot_fmj_highcaliber_longbarrel", 1 );
					}
				}
			}
			
			if ( weapon.attachments.size > 4 )
			{
				if ( player weaponHasAttachmentAndUnlocked( weapon, "extclip", "grip", "fastreload", "quickdraw", "stalker" ) )
				{
					player AddPlayerStat( "kills_extclip_grip_fastmag_quickdraw_stock", 1 );
				}
			}
		}
		
		if ( victim_was_drowning && attacker_was_drowning )
		{
			player AddPlayerStat( "dr_lung", 1 );
		}
		
		if ( isdefined( attackerLastFastReloadTime ) && ( time - attackerLastFastReloadTime <= 5000 ) && player WeaponHasAttachmentAndUnlocked( weapon, "fastreload" ) )
		{
			player AddPlayerStat( "kills_after_reload_fastreload", 1 );
		}

		if ( victim.iDFlagsTime == time )
		{
			if ( victim.iDFlags & 8 )
			{
				player AddPlayerStat( "kill_enemy_through_wall", 1 );
				if ( player weaponHasAttachmentAndUnlocked( weapon, "fmj" ) )
				{
					player AddPlayerStat( "kill_enemy_through_wall_with_fmj", 1 );		
				}
			}
		}
	
		if ( attacker_wallRanTwoOppositeWallsNoGround === true )
		{
			player AddPlayerStat( "kill_while_wallrunning_2_walls", 1 );
		}	
		
		// end if ( bulletkill )
	}
	else if ( weapon_utils::isMeleeMOD( meansOfDeath ) && !isdefined( killstreak ) )
	{
		player AddPlayerStat( "melee", 1 );
		if ( weapon_utils::isPunch( weapon ) )
		{
			player AddPlayerStat( "kill_enemy_with_fists", 1 );
		}
		
		//Check for the killstreak 5 challenge completion
		checkkillstreak5( baseWeapon, player );
	}
	else 
	{
		if ( weaponPurchased )
		{
			if ( weapon == player.grenadeTypePrimary )
			{
				if ( player.challenge_scavengedCount > 0 )
				{
					player.challenge_resuppliedNameKills++;
					if ( player.challenge_resuppliedNameKills >= 3 )
					{
						player AddPlayerStat( "kills_3_resupplied_nade_one_life", 1 );
						player.challenge_resuppliedNameKills = 0;
					}
					player.challenge_scavengedCount--;
				}
			}
			if ( isdefined( inflictorIsCooked ) )
			{
				if ( inflictorIsCooked == true && weapon.rootweapon.name != "hatchet" )
				{
					player AddPlayerStat( "kill_with_cooked_grenade", 1 );
				}
			}
	
			if ( victimLastStunnedBy === player )
			{
				if ( weaponclass == "weapon_grenade" )
				{
					player AddPlayerStat( "kill_stun_lethal", 1 );
				}
			}
			
			if ( baseWeapon == level.weaponSpecialCrossbow )
			{				
				if ( weapon.isDualWield ) // determined purchased
				{
					checkDualWield( baseWeapon, player, attacker, time, attackerwassprinting, attacker_sprint_end );
				}
			}
			
			// for "energy" shotgun
			if ( baseWeapon == level.weaponShotgunEnergy )
			{
				if ( isdefined ( victim.firstTimeDamaged ) && victim.firstTimeDamaged == time ) 
				{
					player AddPlayerStat( "kill_enemy_one_bullet_shotgun", 1 );
					player AddWeaponStat( weapon, "kill_enemy_one_bullet_shotgun", 1 );
				}
			}
		}
		
		if ( baseWeapon.forceDamageHitLocation || baseWeapon == level.weaponSpecialCrossbow || baseWeapon == level.weaponShotgunEnergy || baseWeapon == level.weaponSpecialDiscGun || baseWeapon == level.weaponBallisticKnife || baseWeapon == level.weaponLauncherEx41 )
		{
			//Check for the killstreak 5 challenge completion
			checkkillstreak5( baseWeapon, player );
		}
	}
	
	if ( isdefined( attacker.tookWeaponFrom[ weapon ] ) && isdefined( attacker.tookWeaponFrom[ weapon ].previousOwner ) )
	{
		if ( !isdefined( attacker.tookWeaponFrom[ weapon ].previousOwner.team ) || attacker.tookWeaponFrom[ weapon ].previousOwner.team != player.team )
		{
			player AddPlayerStat( "kill_with_pickup", 1 );
		}
	}
	
	awarded_kill_enemy_that_blinded_you = false;
	
	playerHasTacticalMask = loadout::hasTacticalMask( player );
	
	if ( attackerWasFlashed )
	{
		if ( attackerLastFlashedBy === victim && !playerHasTacticalMask )
		{
			player AddPlayerStat( "kill_enemy_that_blinded_you", 1 );		
			awarded_kill_enemy_that_blinded_you = true;
		}
	}
	
	if ( !awarded_kill_enemy_that_blinded_you && isdefined( attackerLastStunnedTime ) && attackerLastStunnedTime + 5000 > time ) 
	{
		if ( attackerLastStunnedBy === victim && !playerHasTacticalMask )
		{
			player AddPlayerStat( "kill_enemy_that_blinded_you", 1 );
			awarded_kill_enemy_that_blinded_you = true;
		}
	}
	
	killedStunnedVictim = false;
	if ( isdefined( victim.lastConcussedBy ) && victim.lastConcussedBy == attacker )
	{
		if ( victim.concussionEndTime > time )
		{
			if ( player util::is_item_purchased( "concussion_grenade" ) )
			{
				player AddPlayerStat( "kill_concussed_enemy", 1 );
			}
			killedStunnedVictim = true;
			player AddWeaponStat( GetWeapon( "concussion_grenade" ), "CombatRecordStat", 1 );
		}
	}
	
	if ( isdefined( victim.lastShockedBy ) && victim.lastShockedBy == attacker )
	{
		if ( victim.shockEndTime > time )
		{
			if ( player util::is_item_purchased( "proximity_grenade" ) )
			{
				player AddPlayerStat( "kill_shocked_enemy", 1 );
			}
			player AddWeaponStat( GetWeapon( "proximity_grenade" ), "CombatRecordStat", 1 );
			killedStunnedVictim = true;
			if ( weapon.rootweapon.name == "bouncingbetty" )
			{
				player AddPlayerStat( "kill_trip_mine_shocked", 1 );
			}
		}
		
	}

	if ( victim util::isFlashbanged() )
	{
		if ( isdefined( victim.lastFlashedBy ) && victim.lastFlashedBy == player )
		{
			killedStunnedVictim = true;
			if ( player util::is_item_purchased( "flash_grenade" ) )
			{
				player AddPlayerStat( "kill_flashed_enemy", 1 );
			}
			player AddWeaponStat( GetWeapon( "flash_grenade" ), "CombatRecordStat", 1 );
		}
	}		

	if ( level.teamBased )
	{
		if ( ( !isDefined( player.pers["kill_every_enemy_with_specialist"] ) ) &&  ( level.playerCount[victim.pers["team"]] > 3 && player.pers["killed_players_with_specialist"].size >= level.playerCount[victim.pers["team"]] ) )
		{
			player AddPlayerStat( "kill_every_enemy", 1 );
			player.pers["kill_every_enemy_with_specialist"] = true;
		}
		
		
		if ( isdefined( victimAttackersThisSpawn ) && IsArray( victimAttackersThisSpawn ) )
		{
			if ( victimAttackersThisSpawn.size > 5 )
			{
				attackerCount = 0;
				foreach( attacking_player in victimAttackersThisSpawn )
				{					
					if ( !isdefined( attacking_player ) )
						continue;
					
					if ( attacking_player == attacker )
						continue;
						
					if ( attacking_player.team != attacker.team )
						continue;
					
					attackerCount++;
				}
				
				if ( attackerCount > 4 )
				{
					// note: "kill_enemy_5_teammates_assists" should be awarded withing on life, not just assist
					player AddPlayerStat( "kill_enemy_5_teammates_assists", 1 );
				}
			}
		}
	}
	
	if ( isdefined( killstreak ) )
	{
		if ( killstreak == "rcbomb" || killstreak == "inventory_rcbomb" ) 
		{
			if ( !victimWasOnGround || victimWasWallRunning )
			{
				player AddPlayerStat( "kill_wallrunner_or_air_with_rcbomb", 1 );
			}
		}
		
		if ( killstreak == "autoturret" || killstreak == "inventory_autoturret" )
	    {
			if ( isdefined( inflictor ) && player util::is_item_purchased( "killstreak_auto_turret" ) )
			{
		    	if ( !isdefined( inflictor.challenge_killcount ) )
		    	{
		    		inflictor.challenge_killcount = 0;
		    	}
		    	
		    	inflictor.challenge_killcount++;
		    	if ( inflictor.challenge_killcount == 5 ) 
		    	{
		    		player AddPlayerStat( "kills_auto_turret_5", 1 );
		    	}
			}
	    }
	}
	
	if ( isdefined( victim.challenge_combatRobotAttackClientID[player.clientid] ) )
    {
		if ( !isdefined( inflictor ) || !isdefined( inflictor.killstreakType ) || !IsString( inflictor.killstreakType ) || ( inflictor.killstreakType != "combat_robot" ) )
		{
			player AddPlayerStat( "kill_enemy_who_damaged_robot", 1 );
		}
    }
	
	if ( player IsBonusCardActive( 8, player.class_num ) && player util::is_item_purchased( "bonuscard_danger_close" ) )
	{
		if ( weaponclass == "weapon_grenade" ) 
		{
			player AddBonusCardStat( 8, "kills", 1, player.class_num );
		}
		if ( weapon.rootweapon.name == "hatchet" && inflictorChallenge_hatchetTossCount <= 2 )
		{
			player.challenge_hatchetkills++;
			if ( player.challenge_hatchetkills == 2 )
			{
				player AddPlayerStat( "kills_first_throw_both_hatchets", 1 );
			}
		}
	}
	
	player trackKillstreakSupportKills( victim );
	
	// PERKS
	if ( !isdefined( killstreak ) )
	{
		if ( attackerWasUnderwater )
		{
			player AddPlayerStat( "kill_while_underwater", 1 );
		}

		if ( player util::has_purchased_perk_equipped( "specialty_jetcharger" ) ) // afterburner
		{
			if ( ( attacker_doublejump_begin > attacker_doublejump_end || attacker_doublejump_end + 3000 > time )
			    || ( attacker_slide_begin > attacker_slide_end || attacker_slide_end + 3000 > time ) )
			{
				player AddPlayerStat( "kills_after_jumping_or_sliding", 1 );	
				
				if ( player util::has_purchased_perk_equipped( "specialty_overcharge" ) )
				{
					{
						player AddPlayerStat( "kill_overclock_afterburner_specialist_weapon_after_thrust", 1 );
					}
				}
			}	
		}
		
		trackedPlayer = false;
		if ( player util::has_purchased_perk_equipped( "specialty_tracker" ) )
		{
			if ( !victim hasPerk ( "specialty_trackerjammer" ) ) // hardwired
			{
				player AddPlayerStat( "kill_detect_tracker", 1 );	
				trackedPlayer = true;
			}
		}
				
		if ( player util::has_purchased_perk_equipped( "specialty_detectnearbyenemies" ) ) // sixth sense
		{
			if ( !victim hasPerk ( "specialty_sixthsensejammer" ) )
			{
				player AddPlayerStat( "kill_enemy_sixth_sense", 1 );	
				
				if ( player util::has_purchased_perk_equipped( "specialty_loudenemies" ) ) // awareness
				{
					if ( !victim hasPerk ( "specialty_quieter" ) ) // dead silence
					{
						player AddPlayerStat( "kill_sixthsense_awareness", 1 );	
					}
				}	
			}
			
			if ( trackedPlayer )
			{
				player AddPlayerStat( "kill_tracker_sixthsense", 1 );	
			}
		}
		
		if ( weapon.isheroWeapon == true || attackerHeroAbilityActive )
		{
			if ( player util::has_purchased_perk_equipped( "specialty_overcharge" ) ) // overclock
			{		
				player AddPlayerStat( "kill_with_specialist_overclock", 1 );			
			}
		}

		if ( player util::has_purchased_perk_equipped( "specialty_gpsjammer" ) ) // ghost
		{
			if ( uav::HasUAV( victimSupportIndex ) )
			{
				player AddPlayerStat( "kill_uav_enemy_with_ghost", 1 );
			}
			
			if ( player util::has_blind_eye_perk_purchased_and_equipped() ) // blindeye
			{
				activeKillstreaks = victim killstreaks::getActiveKillstreaks();
			
				awarded_kill_blindeye_ghost_aircraft = false;
				foreach( activeStreak in activeKillstreaks )
				{
					if ( awarded_kill_blindeye_ghost_aircraft )
						break;
					
					switch( activeStreak.killstreakType ) 
					{
						case "sentinel":
						case "helicopter_comlink":
						case "drone_striked":
						case "uav":
							player AddPlayerStat( "kill_blindeye_ghost_aircraft", 1 );
							awarded_kill_blindeye_ghost_aircraft = true;
							break;
					}
				}
			}
		}
		if ( player util::has_purchased_perk_equipped( "specialty_flakjacket" ) )
		{
			if ( isdefined ( player.challenge_lastsurvivewithflakfrom ) && player.challenge_lastsurvivewithflakfrom == victim )
			{
				player AddPlayerStat( "kill_enemy_survive_flak", 1 );
			}
			
			
			if ( player util::has_tactical_mask_purchased_and_equipped() )
			{
				recentlySurvivedFlak = false;
				if ( isdefined ( player.challenge_lastsurvivewithflaktime ) )
				{
				 	if ( ( player.challenge_lastsurvivewithflaktime + 3000 ) > time )
				 	{
				 		recentlySurvivedFlak = true;
				 	}
				}
				recentlyStunned = false;
				if ( isdefined( player.lastStunnedTime ) )
				{
					if ( player.lastStunnedTime + 2000 > time )
					{
						recentlyStunned = true;
					}
				}
				if ( recentlySurvivedFlak
				    || ( player util::isFlashbanged() )
				    || recentlyStunned )
				{
					player AddPlayerStat( "kill_flak_tac_while_stunned", 1 );
				}
			}
		}
		
		if ( player util::has_hard_wired_perk_purchased_and_equipped() ) // hard wired
		{
			if ( victim counteruav::HasIndexActiveCounterUAV( victimSupportIndex ) || victim emp::hasactiveemp() )
			{
				player AddPlayerStat( "kills_counteruav_emp_hardline", 1 );
			}
		}
		
		if ( player util::has_scavenger_perk_purchased_and_equipped() )
		{
			if ( player.scavenged )
			{
				player AddPlayerStat( "kill_after_resupply", 1 );
				if ( trackedPlayer )
				{
					player AddPlayerStat( "kill_scavenger_tracker_resupply", 1 );
				}
			}
		}		
		
		if ( player util::has_fast_hands_perk_purchased_and_equipped() ) // fasthands
		{
			if ( bulletKill ) 
			{
				if ( attackerWasSprinting || attacker_sprint_end + 3000 > time )
				{
					player AddPlayerStat( "kills_after_sprint_fasthands", 1 );
					if ( player util::has_gung_ho_perk_purchased_and_equipped() ) // gungho
					{
						player AddPlayerStat( "kill_fasthands_gungho_sprint", 1 );
					}
				}
			}
		}
		
		if ( player util::has_hard_wired_perk_purchased_and_equipped() ) // hardwired
		{
			if ( player util::has_cold_blooded_perk_purchased_and_equipped() ) // cold blooded
			{
				player AddPlayerStat( "kill_hardwired_coldblooded", 1 );
			}
		}
		
		
		killedPlayerWithGungHo = false;
		if ( player util::has_gung_ho_perk_purchased_and_equipped() ) // gung ho
		{
			if ( bulletKill ) 
			{
				killedPlayerWithGungHo = true;
				if ( attackerWasSprinting && player PlayerAds() != 1 )
				{
					player AddPlayerStat( "kill_hip_gung_ho", 1 );
				}
			}
			if ( weaponclass == "weapon_grenade" )
			{
				if ( isdefined( inflictorOwnerWasSprinting ) && inflictorOwnerWasSprinting == true )
				{
					killedPlayerWithGungHo = true;
					player AddPlayerStat( "kill_hip_gung_ho", 1 );
				}
			}
		}
		
		if ( player util::has_jetquiet_perk_purchased_and_equipped() ) // blast surpressor
		{
			if ( attackerDoubleJumping || ( attacker_doublejump_end + 3000 > time ) )
			{
				player AddPlayerStat( "kill_blast_doublejump", 1 );
				if ( player util::has_ghost_perk_purchased_and_equipped() ) // ghost
				{
					if ( uav::HasUAV( victimSupportIndex ) )
					{
						player AddPlayerStat( "kill_doublejump_uav_engineer_hardwired", 1 );
					}
				}
			}
		}
		if ( player util::has_awareness_perk_purchased_and_equipped() ) // awareness
		{
			player AddPlayerStat( "kill_awareness", 1 );
		}
	
		if ( killedStunnedVictim ) 
		{
			if ( player util::has_tactical_mask_purchased_and_equipped() )  // tacmask
			{
				player AddPlayerStat( "kill_stunned_tacmask", 1 );
				if ( killedPlayerWithGungHo == true )
				{
					player AddPlayerStat( "kill_sprint_stunned_gungho_tac", 1 );
				}
			}
		}
		
		if ( player util::has_ninja_perk_purchased_and_equipped() ) // dead silence
		{
			player AddPlayerStat( "kill_dead_silence", 1 );
			
			if ( distanceSquared( playerOrigin, victimOrigin ) < 120 * 120 )
			{
				if ( player util::has_awareness_perk_purchased_and_equipped() ) // awareness
				{
					player AddPlayerStat( "kill_close_deadsilence_awareness", 1 );
				}
				
				if ( player util::has_jetquiet_perk_purchased_and_equipped() ) // blast suppressr
				{
					player AddPlayerStat( "kill_close_blast_deadsilence", 1 );
				}
			}
		}

		greedCardsActive = 0;
		if ( player IsBonusCardActive( 5, player.class_num ) && player util::is_item_purchased( "bonuscard_perk_1_greed" ) )
		{
			greedCardsActive++;
		}
		if ( player IsBonusCardActive( 6, player.class_num ) && player util::is_item_purchased( "bonuscard_perk_2_greed" ) )
		{
			greedCardsActive++;
		}
		if ( player IsBonusCardActive( 7, player.class_num ) && player util::is_item_purchased( "bonuscard_perk_3_greed" ) )
		{
			greedCardsActive++;
		}

		if ( greedCardsActive >= 2 )
		{
			player AddPlayerStat( "kill_2_greed_2_perks_each", 1 );
		}

		if ( player BonusCardActiveCount( player.class_num ) >= 2 ) 
		{
			player AddPlayerStat( "kill_2_wildcards", 1 );
		}
		
		
		gunfighterOverkillActive = false;
		if ( player IsBonusCardActive( 4, player.class_num ) && player util::is_item_purchased( "bonuscard_overkill" ) )
		{
			primaryAttachmentsTotal = 0;			
			if ( isdefined( player.primaryLoadoutWeapon ) )
				primaryAttachmentsTotal = player.primaryLoadoutWeapon.attachments.size;

			secondaryAttachmentsTotal = 0;			
			if ( isdefined( player.secondaryLoadoutWeapon ) )
				secondaryAttachmentsTotal = player.secondaryLoadoutWeapon.attachments.size;

			if ( primaryAttachmentsTotal + secondaryAttachmentsTotal >= 5 )
			{
				gunfighterOverkillActive = true;
			}
		}
		
		if ( ( isdefined (player.primaryLoadoutWeapon ) && weapon == player.primaryLoadoutWeapon ) 
			||  ( isdefined (player.primaryLoadoutAltWeapon ) && weapon == player.primaryLoadoutAltWeapon ) )
		{
			if ( player IsBonusCardActive( 0, player.class_num ) && player util::is_item_purchased( "bonuscard_primary_gunfighter" ) )
			{
				player AddBonusCardStat( 0, "kills", 1, player.class_num );
				player AddPlayerStat( "kill_with_loadout_weapon_with_3_attachments", 1 );
			}
			if ( isdefined( player.secondaryWeaponKill ) && player.secondaryWeaponKill == true )
			{
				player.primaryWeaponKill = false;
				player.secondaryWeaponKill = false;
				if ( player IsBonusCardActive( 4, player.class_num ) && player util::is_item_purchased( "bonuscard_overkill" ) )
				{
					player AddBonusCardStat( 4, "kills", 1, player.class_num );
					player AddPlayerStat( "kill_with_both_primary_weapons", 1 );
					if ( gunfighterOverkillActive ) 
					{
						player AddPlayerStat( "kill_overkill_gunfighter_5_attachments", 1 );
					}
				}
			}
			else
			{
				player.primaryWeaponKill = true;
			}
		}
		else if ( ( isdefined( player.secondaryLoadoutWeapon ) && weapon == player.secondaryLoadoutWeapon )
			         || ( isdefined( player.secondaryLoadoutAltWeapon ) && weapon == player.secondaryLoadoutAltWeapon ) )
		{
			if ( player IsBonusCardActive( 3, player.class_num ) && player util::is_item_purchased( "bonuscard_secondary_gunfighter" ) )
			{
				player AddBonusCardStat( 3, "kills", 1, player.class_num );
			}
			if ( isdefined( player.primaryWeaponKill ) && player.primaryWeaponKill == true )
			{
				player.primaryWeaponKill = false;
				player.secondaryWeaponKill = false;
				if ( player IsBonusCardActive( 4, player.class_num ) && player util::is_item_purchased( "bonuscard_overkill" ) )
				{
					player AddBonusCardStat( 4, "kills", 1, player.class_num );
					player AddPlayerStat( "kill_with_both_primary_weapons", 1 );
					if ( gunfighterOverkillActive ) 
					{
						player AddPlayerStat( "kill_overkill_gunfighter_5_attachments", 1 );
					}
				}
			}
			else
			{
				player.secondaryWeaponKill = true;
			}
		}
		
		if ( player util::has_hacker_perk_purchased_and_equipped() && player util::has_hard_wired_perk_purchased_and_equipped() )
		{
			should_award_kill_near_plant_engineer_hardwired = false;
			
			if ( isdefined( victimActiveBouncingBetties ) )
			{
				foreach( bouncingBettyInfo in victimActiveBouncingBetties )
				{
					if ( !isdefined( bouncingBettyInfo ) || !isdefined( bouncingBettyInfo.origin ) )
						continue;
					
					if ( DistanceSquared( bouncingBettyInfo.origin, victimOrigin ) < ( (400) * (400) ) )
					{
						should_award_kill_near_plant_engineer_hardwired = true;
						break;
					}
				}
			}
			
			if ( isdefined( victimActiveProximityGrenades ) && should_award_kill_near_plant_engineer_hardwired == false )
			{
				foreach( proximityGrenadeInfo in victimActiveProximityGrenades )
				{
					if ( !isdefined( proximityGrenadeInfo ) || !isdefined( proximityGrenadeInfo.origin ) )
						continue;
					
					if ( DistanceSquared( proximityGrenadeInfo.origin, victimOrigin ) < ( (400) * (400) ) )
					{
						should_award_kill_near_plant_engineer_hardwired = true;
						break;
					}
				}
			}
			
			if ( should_award_kill_near_plant_engineer_hardwired )
			{
				player AddPlayerStat( "kill_near_plant_engineer_hardwired", 1 );
			}
		}
	}
	else // it was a killstreak
	{	
		if ( weapon.name == "supplydrop" ) 
		{
			if (isdefined( inflictorPlayerHasEngineerPerk ) )
			{
				player AddPlayerStat( "kill_booby_trap_engineer", 1 );	
			}
		}
	}
	
	if ( weapon.isHeroWeapon == true || attackerHeroAbilityActive || isdefined( killstreak ) )
	{
		if ( player util::has_purchased_perk_equipped( "specialty_overcharge" ) // overclock
		    && ( player util::has_purchased_perk_equipped( "specialty_anteup" ) ) ) // anteup
		{					
			player AddPlayerStat( "kill_anteup_overclock_scorestreak_specialist", 1 );					
		}
	}
}

function on_player_spawn()
{
	if ( canProcessChallenges() )
	{
		self fix_challenge_stats_on_spawn();
	}
}

function get_challenge_stat( stat_name )
{
	return self GetDStat( "playerstatslist", stat_name, "challengevalue" );
}

function force_challenge_stat( stat_name, stat_value )
{
	// make the stat value and challenge value identical
	self SetDStat( "playerstatslist", stat_name, "statvalue", stat_value );
	self SetDStat( "playerstatslist", stat_name, "challengevalue", stat_value );
}

function get_challenge_group_stat( group_name, stat_name )
{
	return self GetDStat( "groupstats", group_name, "stats", stat_name, "challengevalue" );
}

function fix_challenge_stats_on_spawn()
{
	// use this method to fix up any challenge inconsistencies at spawn

	player = self;
	
	if ( !isdefined( player ) )
		return;
	
	if ( player.fix_challenge_stats_performed === true )
		return;

	player fix_TU6_weapon_for_diamond( "special_crossbow_for_diamond" );
	player fix_TU6_weapon_for_diamond( "melee_crowbar_for_diamond" );
	player fix_TU6_weapon_for_diamond( "melee_sword_for_diamond" );
	player fix_TU6_ar_garand();
	player fix_TU6_pistol_shotgun();
	player TU7_fix_100_percenter();

	player.fix_challenge_stats_performed = true;
}

function fix_TU6_weapon_for_diamond( stat_name )
{
	player = self;
	
	wepaon_for_diamond = player get_challenge_stat( stat_name );

	if ( wepaon_for_diamond == 1 )
	{
		// valid values for X_for_diamond are 0, 2, and 3; TU5 has an issue for some challenges
		// where only 1 point was awarded if pistol diamond was awarded ( and some challenges where any secondary diamond camo was awwarded )
		// this fixes the issue by setting it to either zero or two
		
		secondary_mastery = player get_challenge_stat( "secondary_mastery" );

		if ( secondary_mastery == 3 )
		{
			player force_challenge_stat( stat_name, 2 );
		}
		else
		{
			player force_challenge_stat( stat_name, 0 );
		}
	}
}

function fix_TU6_ar_garand()
{
	player = self;
	
	group_weapon_assault = player get_challenge_group_stat( "weapon_assault", "challenges" );
	weapons_mastery_assault = player get_challenge_stat( "weapons_mastery_assault" );
	
	// earning weapons mastery on assault rifles prior to TU6 will exhibit an issue of not being able to obtain diamond camo for ar_garand
	// this fixes the issue by awarding ar_garand_for_diamond if weapons_mastery_assault was not obtained

	// 49 obtained from challenge 648 in statsmilestones3.csv
	if ( group_weapon_assault >= 49 && weapons_mastery_assault < 1 )
	{
		player force_challenge_stat( "weapons_mastery_assault", 1 ); // intentionally forced stat here to avoid stat processing

		player AddPlayerStat( "ar_garand_for_diamond", 1 ); // now add stat with stat processing
	}
}

function fix_TU6_pistol_shotgun()
{
	player = self;
	
	group_weapon_pistol = player get_challenge_group_stat( "weapon_pistol", "challenges" );
	secondary_mastery_pistol = player get_challenge_stat( "secondary_mastery_pistol" );

	// see comments in fix_TU6_ar_garand for more related details

	// 21 obtained from challenge 649 in statsmilestones3.csv
	if ( group_weapon_pistol >= 21 && secondary_mastery_pistol < 1 )
	{
		player force_challenge_stat( "secondary_mastery_pistol", 1 ); // intentionally forced stat here to avoid stat processing

		player AddPlayerStat( "pistol_shotgun_for_diamond", 1 ); // now add stat with stat processing
	}
}

function completed_specific_challenge( target_value, challenge_name )
{
	challenge_count = self get_challenge_stat( challenge_name );
	
	return ( challenge_count >= target_value );
}

function tally_completed_challenge( target_value, challenge_name )
{
	return ( ( self completed_specific_challenge( target_value, challenge_name ) ) ? 1 : 0 );
}

function TU7_fix_100_percenter()
{
	self TU7_fix_mastery_perk_2();
}

function TU7_fix_mastery_perk_2()
{
	player = self;
	
	// if mastery_perk_2 is compeleted, then no need to try to fix up
	mastery_perk_2 = player get_challenge_stat( "mastery_perk_2" );
	if ( mastery_perk_2 >= 12 )
		return;
	
	// if earn_scorestreak_anteup has not been completed, no need to try to fix up
	if ( player completed_specific_challenge( 200, "earn_scorestreak_anteup") == false )
		return;

	perk_2_tally = 1; // init to 1 because earn_scorestreak_anteup is already completed
	
	perk_2_tally += player tally_completed_challenge( 100, "destroy_ai_scorestreak_coldblooded" ); //destroy_ai_scorestreak_coldblooded
	perk_2_tally += player tally_completed_challenge( 100, "kills_counteruav_emp_hardline" ); //kills_counteruav_emp_hardline
	perk_2_tally += player tally_completed_challenge( 200, "kill_after_resupply" ); //kill_after_resupply
	perk_2_tally += player tally_completed_challenge( 100, "kills_after_sprint_fasthands" ); //kills_after_sprint_fasthands
	perk_2_tally += player tally_completed_challenge( 200, "kill_detect_tracker" ); //kill_detect_tracker
	//perk_2_tally += player tally_completed_challenge( 200, "earn_scorestreak_anteup" ); //earn_scorestreak_anteup
	perk_2_tally += player tally_completed_challenge( 10, "earn_5_scorestreaks_anteup" ); //earn_5_scorestreaks_anteup
	perk_2_tally += player tally_completed_challenge( 25, "kill_scavenger_tracker_resupply" ); //kill_scavenger_tracker_resupply
	perk_2_tally += player tally_completed_challenge( 25, "kill_hardwired_coldblooded" ); //kill_hardwired_coldblooded
	perk_2_tally += player tally_completed_challenge( 25, "kill_anteup_overclock_scorestreak_specialist" ); //kill_anteup_overclock_scorestreak_specialist
	perk_2_tally += player tally_completed_challenge( 50, "kill_fasthands_gungho_sprint" ); //kill_fasthands_gungho_sprint
	perk_2_tally += player tally_completed_challenge( 50, "kill_tracker_sixthsense" ); //kill_tracker_sixthsense

	if ( mastery_perk_2 < perk_2_tally )
	{
		// award "mastery_perk_2"
		player AddPlayerStat( "mastery_perk_2", 1 );
	}
}

function getBaseWeapon( weapon )
{
	// TODO: need to get weapon stat name available from script (and make sure all stat names are populated properly for weapons)
	base_weapon_param = [[ level.get_base_weapon_param ]]( weapon ); // we have a "root" weapon after this executes
	base_weapon_param_name = str_strip_lh_or_dw( base_weapon_param.name );
	base_weapon_param_name = str_strip_lh_from_crossbow( base_weapon_param_name ); // unique case because of poor naming convention
	return GetWeapon( GetRefFromItemIndex( GetBaseWeaponItemIndex( GetWeapon( base_weapon_param_name ) ) ) );
}

function str_strip_lh_from_crossbow( str )
{
	if ( StrEndsWith( str, "crossbowlh" ) )
	{
		return GetSubStr( str, 0, str.size - 2 );
	}

	return str;
}

function str_strip_lh_or_dw( str )
{
	if ( StrEndsWith( str, "_lh" ) || StrEndsWith( str, "_dw" ) )
	{
		return GetSubStr( str, 0, str.size - 3 );
	}

	return str;
}

function checkKillStreak5( baseWeapon, player )
{
	if ( isdefined( player.weaponKillsThisSpawn[ baseWeapon ] ) )
	{
		player.weaponKillsThisSpawn[ baseWeapon ]++;
		if ( ( player.weaponKillsThisSpawn[ baseWeapon ] ) % 5 == 0 )
		{
			player AddWeaponStat( baseWeapon, "killstreak_5", 1 );
		}
	}
	else
	{
		player.weaponKillsThisSpawn[ baseWeapon ] = 1;
	}
}

function checkDualWield( baseWeapon, player, attacker, time, attackerWasSprinting, attacker_sprint_end )
{
	// note: check weapon.isDualWield and weaponPurchased before calling checkDualWield for challenges.
	if ( attackerWasSprinting || ( attacker_sprint_end + 1000 > time ) )
	{
		if ( attacker util::has_gung_ho_perk_purchased_and_equipped() )
		{
			player AddPlayerStat( "kills_sprinting_dual_wield_and_gung_ho", 1 );
		}
	}
}

function challengeGameEndMP( data )
{
	player = data.player;
	winner = data.winner;
	
	if ( !isdefined( player ) )
		return;
	
	if ( endedEarly( winner ) )
		return;

	if ( level.teambased )
	{
		winnerScore = game["teamScores"][winner];
		loserScore = getLosersTeamScores( winner );
	}
	
	mostKillsLeastDeaths = true;
								
	for ( index = 0; index < level.placement["all"].size; index++ )
	{
		if ( level.placement["all"][index].deaths < player.deaths )
		{
			mostKillsLeastDeaths = false;
		}
		if ( level.placement["all"][index].kills > player.kills )
		{
			mostKillsLeastDeaths = false;
		}
	}
				
	if ( mostKillsLeastDeaths && player.kills > 0 && level.placement["all"].size > 3 )
	{
		if ( level.teambased )
		{
			playerIsWinner = ( player.team === winner );
		}
		else
		{
			// see if in top 3
			playerIsWinner = ( ( level.placement["all"][0] === winner ) || ( level.placement["all"][1] === winner ) || ( level.placement["all"][2] === winner ) );
		}
		
		if ( playerIsWinner )
		{
			player AddPlayerStat( "most_kills_least_deaths", 1 );
		}
	}
}

function killedBaseOffender( objective, weapon )
{
	self endon( "disconnect" );
	self AddPlayerStatWithGameType( "defends", 1 ); // awards the player for being a "defender" as they killed an offender
	
	self.challenge_offenderkillcount++;
	
	if ( !isdefined( self.challenge_objectiveOffensive ) || self.challenge_objectiveOffensive != objective )
	{
		self.challenge_objectiveOffensiveKillcount = 0;
	}
	
	self.challenge_objectiveOffensiveKillcount++;
	self.challenge_ObjectiveOffensive = objective;
	
	killstreak = killstreaks::get_from_weapon( weapon );

	if ( isdefined( killstreak ) ) 
	{
		switch ( killstreak )
		{
			case "planemortar":
			case "inventory_planemortar":
			case "remote_missile":
			case "inventory_remote_missile":
			case "drone_strike":
			case "inventory_drone_strike":
				self.challenge_offenderProjectileMultiKillcount++;
				break;
			case "helicopter_comlink":
			case "inventory_helicopter_comlink":
				self.challenge_offenderComlinkKillcount++;
				break;
			case "combat_robot":
			case "inventory_combat_robot":
				self AddPlayerStat( "kill_attacker_with_robot_or_tank", 1 );
				break;
			case "inventory_autoturret":
			case "autoturret":
				self.challenge_offenderSentryTurretKillCount++;
				self AddPlayerStat( "kill_attacker_with_robot_or_tank", 1 );
				break;
		}
	}

	if ( self.challenge_offenderComlinkKillcount == 2 )
	{
		self AddPlayerStat( "kill_2_attackers_with_comlink", 1 );
	}
	
	if ( self.challenge_objectiveOffensiveKillcount > 4 ) 
	{
		self AddPlayerStatWithGameType( "multikill_5_attackers", 1 );
		self.challenge_objectiveOffensiveKillcount = 0;
	}
	
	if ( self.challenge_offenderSentryTurretKillCount > 2 ) 
	{
		self AddPlayerStat( "multikill_3_attackers_ai_tank", 1 );
		self.challenge_offenderSentryTurretKillCount = 0;
	}

	self util::player_contract_event( "offender_kill" );

	self waitTillTimeoutOrDeath( 4.0 );
	
	if ( self.challenge_offenderkillcount > 1 )
	{
		self AddPlayerStat( "double_kill_attackers", 1 );
	}

	self.challenge_offenderkillcount = 0;
	
	if ( self.challenge_offenderProjectileMultiKillcount >= 2 )
	{
		self AddPlayerStat( "multikill_2_objective_scorestreak_projectile", 1 );
	}

	self.challenge_offenderProjectileMultiKillcount = 0;
}

function killedBaseDefender( objective )
{
	self endon( "disconnect" );
	self AddPlayerStatWithGameType( "offends", 1 ); // awards the player for being an "offender" as they killed a defender
	
	if ( !isdefined( self.challenge_objectiveDefensive ) || self.challenge_objectiveDefensive != objective )
	{
		self.challenge_objectiveDefensiveKillcount = 0;
	}
	
	self.challenge_objectiveDefensiveKillcount++;
	self.challenge_ObjectiveDefensive = objective;


	self.challenge_defenderkillcount++;
	
	self util::player_contract_event( "defender_kill" );

	self waitTillTimeoutOrDeath( 4.0 );
	
	if ( self.challenge_defenderkillcount > 1 )
	{
		self AddPlayerStat( "double_kill_defenders", 1 );
	}
	
	self.challenge_defenderkillcount = 0;
}

function waitTillTimeoutOrDeath( timeout )
{
	self endon( "death" );
	wait( timeout );
}

function killstreak_30_noscorestreaks()
{
	if ( level.gameType == "dm" )
	{
		self AddPlayerStat( "killstreak_30_no_scorestreaks", 1 );
	}
}

function heroAbilityActivateNearDeath()
{
	if ( isdefined( self.heroAbility ) && self.pers["canSetSpecialistStat"] )
	{
		switch( self.heroAbility.name )
		{
			case "gadget_camo":
			case "gadget_armor":
			case "gadget_clone":
			case "gadget_speed_burst":
			case "gadget_vision_pulse":
			case "gadget_flashback":
			case "gadget_heat_wave":
				self thread checkForHeroSurvival();
				break;
		}
	}
}


function checkForHeroSurvival()
{
	self endon ("death");
	self endon ("disconnect");
	
	self util::waittill_any_timeout( 8.0, "challenge_survived_from_death", "disconnect" );
	
	self AddPlayerStat( "death_dodger", 1 );
}


function callbackEndHeroSpecialistEMP()
{
	empOwner = self emp::EnemyEMPOwner();
	if ( isdefined( empOwner ) && IsPlayer( empOwner ) )
    {
    	empOwner AddPlayerStat( "end_enemy_specialist_ability_with_emp", 1 );
    	return;
    }

	if ( isdefined(  self.empStartTime ) && self.empStartTime > ( getTime() - 100 ) )
	{
		if ( isdefined(self.empedBy) && IsPlayer( self.empedBy ) )
		{
			self.empedBy AddPlayerStat( "end_enemy_specialist_ability_with_emp", 1 );
			return;
		}
	}
}



function calledInComlinkChopper()
{
	self.challenge_offenderComlinkKillcount	= 0;
}


function combat_robot_damage( eAttacker, combatRobotOwner )
{
	if ( !isdefined( eAttacker.challenge_combatRobotAttackClientID[combatRobotOwner.clientid]  ) )
	{
		eAttacker.challenge_combatRobotAttackClientID[combatRobotOwner.clientid] = spawnstruct();
	}
}


function trackKillstreakSupportKills( victim )
{
	if ( level.activePlayerEMPs[ self.entNum ] > 0 )
	{
		self AddWeaponStat( GetWeapon( "emp" ), "kills_while_active", 1 );
	}
	
	if ( ( level.activePlayerUAVs[ self.entNum ] > 0 ) && ( !isdefined( level.forceradar ) || level.forceRadar == false ) )
	{
		self AddWeaponStat( GetWeapon( "uav" ), "kills_while_active", 1 );
	}
	
	if ( level.activePlayerSatellites[ self.entNum ] > 0 )
	{
		self AddWeaponStat( GetWeapon( "satellite" ), "kills_while_active", 1 );
	}
	
	if ( level.activePlayerCounterUAVs[ self.entNum ] > 0 )
	{ 
		self AddWeaponStat( GetWeapon( "counteruav" ), "kills_while_active", 1 );
	}
	
	if ( isdefined( victim.lastMicrowavedBy ) && victim.lastMicrowavedBy == self )
	{
		self AddWeaponStat( GetWeapon( "microwave_turret" ), "kills_while_active", 1 );
	}	
}

function monitorReloads()
{
	self endon("disconnect");
	self endon("killMonitorReloads");
	
	while(1)
	{
		self waittill("reload");
		currentWeapon = self getCurrentWeapon();
		if ( currentWeapon == level.weaponNone ) 
		{
			continue;
		}
		
		time = getTime();
		self.lastReloadTime = time;
		
		if ( WeaponHasAttachment( currentWeapon, "fastreload" ) ) // aka fastmags or Fast Mags
		{
			self.lastFastReloadTime = time;
		}
	}
}

function monitorGrenadeFire()
{
	self notify( "grenadeTrackingStart" );
	
	self endon( "grenadeTrackingStart" );
	self endon( "disconnect" );
	
	for (;;)
	{
		self waittill ( "grenade_fire", grenade, weapon );
		
		if ( !isdefined( grenade ) )
		{
			continue;
		}
		
		if ( weapon.rootweapon.name == "hatchet" )
		{
			self.challenge_hatchetTossCount++;
			grenade.challenge_hatchetTossCount = self.challenge_hatchetTossCount;
		}
		if ( self issprinting() )
		{
			grenade.ownerWasSprinting = true;
		}
	}
}

function watchWeaponChangeComplete()
{
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "joined_team" );
	self endon( "joined_spectators" );
			
	while( 1 )
	{
		self.heroWeaponKillsThisActivation = 0;
	
		self waittill( "weapon_change_complete" );
	}
}

function longDistanceKillMP( weapon )
{
	self AddWeaponStat( weapon, "longshot_kill", 1 );
	if ( self weaponHasAttachmentAndUnlocked( weapon, "extbarrel", "suppressed" ) )
	{
		if ( self GetWeaponOptic( weapon ) != "" )
		{
			self addPlayerStat( "long_shot_longbarrel_suppressor_optic", 1 );
		}
	}
}


function capturedObjectiveFunction()
{
	if ( self IsBonusCardActive( 9, self.class_num ) && self util::is_item_purchased( "bonuscard_two_tacticals" ) )
	{
		self AddPlayerStat( "capture_objective_tactician", 1 );
	}
}

function watchWallRunTwoOppositeWallsNoGround()
{
	player = self;
	player endon( "death" );
	player endon( "disconnect" );
	player endon( "joined_team" );
	player endon( "joined_spectators" );
	
	self.wallRanTwoOppositeWallsNoGround = false;

	while ( 1 )
	{
		if ( !player IsWallRunning() )
		{
			self.wallRanTwoOppositeWallsNoGround = false;
			player waittill( "wallrun_begin" );
		}

		ret = player util::waittill_any_return( "jump_begin", "wallrun_end", "disconnect", "joined_team", "joined_spectators" );
		if ( ret == "wallrun_end" )
			continue;
		
		wall_normal = player GetWallRunWallNormal();

		player waittill( "jump_end" );
		
		if ( !player IsWallRunning() )
			continue;
					
		last_wall_normal = wall_normal;
		wall_normal = player GetWallRunWallNormal();
		
		opposite_walls = ( VectorDot( wall_normal, last_wall_normal ) < -0.5 );
		if ( !opposite_walls )
			continue;

		player.wallRanTwoOppositeWallsNoGround = true;

		while ( player IsWallRunning() )
		{
			ret = player util::waittill_any_return( "jump_end", "wallrun_end", "disconnect", "joined_team", "joined_spectators" );
			
			if ( ret == "wallrun_end" )
				break;
		}

		{wait(.05);};
		
		while ( !player IsOnGround() )
		{
			{wait(.05);};
		}
	}
}

function processSpecialistChallenge( statName )
{
	if ( self.pers["canSetSpecialistStat"] )
	{
		self AddSpecialistStat( statName, 1 );
	}
}


function flakjacketProtectedMP( weapon, attacker )
{
	if ( weapon.name == "claymore" )
	{
		self.flakJacketClaymore[ attacker.clientid ] = true;
	}

	self AddPlayerStat( "survive_with_flak", 1 );
	self.challenge_lastsurvivewithflakfrom = attacker;
	self.challenge_lastsurvivewithflaktime = getTime();
}

