#include common_scripts\utility;
#include maps\_utility;

init()
{
	level._effect[ "c4_light_blink" ] = loadfx( "misc/light_c4_blink" );
	level._effect[ "claymore_laser" ] = loadfx( "misc/claymore_laser" );

	for ( i = 0; i < level._players.size; i++ )
	{
		level._players[ i ] thread watchGrenadeUsage();
	}
}

watchGrenadeUsage()
{
	level._c4explodethisframe = false;
	self endon( "death" );
	self.c4array = [];
	self.throwingGrenade = false;

	thread watchC4();
	thread watchC4Detonation();
	thread watchC4AltDetonation();
	thread watchClaymores();
	thread begin_semtex_grenade_tracking();

	for ( ;; )
	{
		self waittill( "grenade_pullback", weaponName );
		self.throwingGrenade = true;

		if ( weaponName == "c4" )
			self beginC4Tracking();
		else if ( weaponName == "smoke_grenade_american" )
			self beginsmokegrenadetracking();
		//else if ( weaponName == "semtex_grenade" )
		//	self beginsemtexgrenadetracking();
		else
			self beginGrenadeTracking();
	}
}

beginsmokegrenadetracking()
{
	self waittill( "grenade_fire", grenade, weaponName );
	if ( !isdefined( level._smokegrenades ) )
		level._smokegrenades = 0;
	if ( level._smokegrenades > 2 && getdvar( "player_sustainAmmo" ) != "0" )
		grenade delete();
	else
		grenade thread smoke_grenade_death();
}

begin_semtex_grenade_tracking()
{
	while( 1 )
	{
		self waittill( "grenade_fire", grenade, weaponName );
		if ( weaponName == "semtex_grenade" )
		{
			thread track_semtex_grenade( grenade );
			grenade thread semtex_sticky_handle( self );
		}
	}
}
	
track_semtex_grenade( grenade )
{
	self.throwingGrenade = false;
	
	if( !isdefined( level._thrown_semtex_grenades ) )
		level._thrown_semtex_grenades = 1;
	else
		level._thrown_semtex_grenades++;
	
	grenade waittill ( "death" );
	
	waittillframeend;
	level._thrown_semtex_grenades--;
}

semtex_sticky_handle( attacker )
{
	self waittill ("missile_stuck", entity );
	
	if( !isdefined( entity ) )
		return;
	
	// just handling vehicles for now. 
	if( entity.code_classname != "script_vehicle" )
		return;
	
	entity.has_semtex_on_it = true;
		
	self waittill ( "explode" );
	
	if( !isdefined( entity ) || !isalive( entity ) )
		return;  // possible it could be dead at this point
	
	if( 
				entity maps\_vehicle::is_godmode() 
		|| 	entity maps\_vehicle::attacker_isonmyteam( attacker ) 
		)
	{
		entity.has_semtex_on_it = undefined;
		return;
	}
		
	entity kill( entity.origin, attacker );
	
}

smoke_grenade_death()
{
	level._smokegrenades++ ;
	wait 50;
	level._smokegrenades -- ;
}

beginGrenadeTracking()
{
	self endon( "death" );

	self waittill( "grenade_fire", grenade, weaponName );
	if ( weaponName == "fraggrenade" )
		grenade thread grenade_earthQuake();

	self.throwingGrenade = false;
}


beginC4Tracking()
{
	self endon( "death" );

	self waittill_any( "grenade_fire", "weapon_change" );
	self.throwingGrenade = false;
}


watchC4()
{
	//maxc4 = 2;

	while ( 1 )
	{
		self waittill( "grenade_fire", c4, weapname );
		if ( weapname == "c4" )
		{
			if ( !self.c4array.size )
				self thread watchC4AltDetonate();
			
			/*if ( self.c4array.size >= maxc4 )
			{
				newarray = [];
				for ( i = 0; i < self.c4array.size; i++ )
				{
					if ( isdefined(self.c4array[i]) )
						newarray[newarray.size] = self.c4array[i];
				}
				self.c4array = newarray;
				for ( i = 0; i < self.c4array.size - maxc4 + 1; i++ )
				{
					self.c4array[i] delete();
				}
				newarray = [];
				for ( i = 0; i < maxc4 - 1; i++ )
				{
					newarray[i] = self.c4array[self.c4array.size - maxc4 + 1 + i];
				}
				self.c4array = newarray;
			}*/
			
			self.c4array[ self.c4array.size ] = c4;
			if ( self.c4array.size > 15 && getdvar( "player_sustainAmmo" ) != "0" )
				self.c4array[ 0 ] delete();
			c4.owner = self;
//			c4 thread maps\mp\gametypes\_shellshock::c4_earthQuake();
			c4 thread c4Damage();
			self thread c4death( c4 );
			c4 thread playC4Effects();
		}
	}
}

c4death( c4 )
{
	// this allows me to delete the first one thrown and reconstruct the array for cheats that enable all the ammo. - Nate
	c4 waittill( "death" );
	self.c4array = array_remove_nokeys( self.c4array, c4 );
}

watchClaymores()
{
	self endon( "spawned_player" );
	self endon( "disconnect" );

	while ( 1 )
	{
		self waittill( "grenade_fire", claymore, weapname );
		if ( weapname == "claymore" || weapname == "claymore_mp" )
		{
			claymore.owner = self;
			claymore thread c4Damage();
			claymore thread claymoreDetonation();
			claymore thread playClaymoreEffects();
			claymore thread claymoreMakeSentient( self.team );
		}
	}
}

claymoreMakeSentient( team )
{
	self endon( "death" );

	wait 1;	// let claymore planting animation finish, and settle

	self MakeEntitySentient( team, true );
	self.attackerAccuracy = 2;
	self.maxVisibleDist = 750;
	self.threatBias = -1000;
}

claymoreDetonation()
{
	self endon( "death" );

	// wait until we settle
	self waittill( "missile_stuck" );

	detonateRadius = 192;// matches MP

	damagearea = spawn( "trigger_radius", self.origin + ( 0, 0, 0 - detonateRadius ), 9, detonateRadius, detonateRadius * 2 );

	self thread deleteOnDeath( damagearea );

	if ( !isdefined( level._claymores ) )
		level._claymores = [];
	level._claymores = array_add( level._claymores, self );

	// limit the number of active claymores
	if ( !is_specialop() && level._claymores.size > 15 )
	{
		level._claymores[ 0 ] delete();
	}

	while ( 1 )
	{
		damagearea waittill( "trigger", ent );

		if ( isdefined( self.owner ) && ent == self.owner )
			continue;

		if ( isplayer( ent ) )
			continue;// no enemy claymores in SP.

		if ( ent damageConeTrace( self.origin, self ) > 0 )
		{
			self playsound( "claymore_activated_SP" );
			wait 0.4;
			if ( isdefined( self.owner ) )
				self detonate( self.owner );
			else
				self detonate( undefined );

			return;
		}
	}
}

deleteOnDeath( ent )
{
	self waittill( "death" );
	// stupid getarraykeys in array_remove reversing the order - nate
	level._claymores = array_remove_nokeys( level._claymores, self );
	wait .05;
	if ( isdefined( ent ) )
		ent delete();
}

watchC4Detonation()
{
	self endon( "death" );
	while ( 1 )
	{
		self waittill( "detonate" );
		weap = self getCurrentWeapon();
		if ( weap == "c4" )
		{
			for ( i = 0; i < self.c4array.size; i++ )
			{
				if ( isdefined( self.c4array[ i ] ) )
					self.c4array[ i ] thread waitAndDetonate( 0.1 );
			}
			self.c4array = [];
		}
	}
}

watchC4AltDetonation()
{
	self endon( "death" );
	self endon( "disconnect" );

	while ( 1 )
	{
		self waittill( "alt_detonate" );
		weap = self getCurrentWeapon();
		if ( weap != "c4" )
		{
			newarray = [];
			for ( i = 0; i < self.c4array.size; i++ )
			{
				c4 = self.c4array[ i ];
				if ( isdefined( self.c4array[ i ] ) )
					c4 thread waitAndDetonate( 0.1 );
			}
			self.c4array = newarray;
			self notify( "detonated" );
		}
	}
}

waitAndDetonate( delay )
{
	self endon( "death" );
	wait delay;

	self detonate();
}


c4Damage()
{
//	self endon( "death" );

	self.health = 100;
	self setcandamage( true );
	self.maxhealth = 100000;
	self.health = self.maxhealth;

	attacker = undefined;

	while ( 1 )
	{
		self waittill( "damage", amount, attacker );

		// don't allow people to destroy C4 on their team if FF is off
//		if ( !friendlyFireCheck(self.owner, attacker) )
//			continue;

		break;
	}

	self playsound( "claymore_activated_SP" );

	if ( level._c4explodethisframe )
		wait .1 + randomfloat( .4 );
	else
		wait .05;

	if ( !isdefined( self ) )
		return;

	level._c4explodethisframe = true;

	thread resetC4ExplodeThisFrame();

	if ( isplayer( attacker ) )
		self detonate( attacker );
	else
		self detonate();
	// won't get here; got death notify.
}

resetC4ExplodeThisFrame()
{
	wait .05;
	level._c4explodethisframe = false;
}

saydamaged( orig, amount )
{
	for ( i = 0; i < 60; i++ )
	{
		print3d( orig, "damaged! " + amount );
		wait .05;
	}
}


playC4Effects()
{
	self endon( "death" );

	self waittill( "missile_stuck" );

	PlayFXOnTag( getfx( "c4_light_blink" ), self, "tag_fx" );
}

playClaymoreEffects()
{
	self endon( "death" );

	self waittill( "missile_stuck" );

	PlayFXOnTag( getfx( "claymore_laser" ), self, "tag_fx" );
}

clearFXOnDeath( fx )
{
	self waittill( "death" );
	fx delete();
}



// these functions are used with scripted weapons (like c4, claymores, artillery)
// returns an array of objects representing damageable entities (including players) within a given sphere.
// each object has the property damageCenter, which represents its center (the location from which it can be damaged).
// each object also has the property entity, which contains the entity that it represents.
// to damage it, call damageEnt() on it.
getDamageableEnts( pos, radius, doLOS, startRadius )
{
	ents = [];

	if ( !isdefined( doLOS ) )
		doLOS = false;

	if ( !isdefined( startRadius ) )
		startRadius = 0;

	// players
	for ( i = 0; i < level._players.size; i++ )
	{
		if ( !isalive( level._players[ i ] ) || level._players[ i ].sessionstate != "playing" )
			continue;

		playerpos = level._players[ i ].origin + ( 0, 0, 32 );
		dist = distance( pos, playerpos );
		if ( dist < radius && ( !doLOS || weaponDamageTracePassed( pos, playerpos, startRadius, undefined ) ) )
		{
			newent = spawnstruct();
			newent.isPlayer = true;
			newent.isADestructable = false;
			newent.entity = level._players[ i ];
			newent.damageCenter = playerpos;
			ents[ ents.size ] = newent;
		}
	}

	// grenades
	grenades = getentarray( "grenade", "classname" );
	for ( i = 0; i < grenades.size; i++ )
	{
		entpos = grenades[ i ].origin;
		dist = distance( pos, entpos );
		if ( dist < radius && ( !doLOS || weaponDamageTracePassed( pos, entpos, startRadius, grenades[ i ] ) ) )
		{
			newent = spawnstruct();
			newent.isPlayer = false;
			newent.isADestructable = false;
			newent.entity = grenades[ i ];
			newent.damageCenter = entpos;
			ents[ ents.size ] = newent;
		}
	}

	destructables = getentarray( "destructable", "targetname" );
	for ( i = 0; i < destructables.size; i++ )
	{
		entpos = destructables[ i ].origin;
		dist = distance( pos, entpos );
		if ( dist < radius && ( !doLOS || weaponDamageTracePassed( pos, entpos, startRadius, destructables[ i ] ) ) )
		{
			newent = spawnstruct();
			newent.isPlayer = false;
			newent.isADestructable = true;
			newent.entity = destructables[ i ];
			newent.damageCenter = entpos;
			ents[ ents.size ] = newent;
		}
	}

	return ents;
}

weaponDamageTracePassed( from, to, startRadius, ignore )
{
	midpos = undefined;

	diff = to - from;
	if ( lengthsquared( diff ) < startRadius * startRadius )
		midpos = to;
	dir = vectornormalize( diff );
	midpos = from + ( dir[ 0 ] * startRadius, dir[ 1 ] * startRadius, dir[ 2 ] * startRadius );

	trace = bullettrace( midpos, to, false, ignore );

	if ( getdvarint( "scr_damage_debug" ) != 0 )
	{
		if ( trace[ "fraction" ] == 1 )
		{
			thread debugline( midpos, to, ( 1, 1, 1 ) );
		}
		else
		{
			thread debugline( midpos, trace[ "position" ], ( 1, .9, .8 ) );
			thread debugline( trace[ "position" ], to, ( 1, .4, .3 ) );
		}
	}

	return( trace[ "fraction" ] == 1 );
}

// eInflictor = the entity that causes the damage (e.g. a claymore)
// eAttacker = the player that is attacking
// iDamage = the amount of damage to do
// sMeansOfDeath = string specifying the method of death (e.g. "MOD_PROJECTILE_SPLASH")
// sWeapon = string specifying the weapon used (e.g. "claymore_mp")
// damagepos = the position damage is coming from
// damagedir = the direction damage is moving in
damageEnt( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, damagepos, damagedir )
{
	if ( self.isPlayer )
	{
		self.damageOrigin = damagepos;
		self.entity thread [[ level._callbackPlayerDamage ]](
			eInflictor,// eInflictor The entity that causes the damage.( e.g. a turret )
			eAttacker,// eAttacker The entity that is attacking.
			iDamage,// iDamage Integer specifying the amount of damage done
			0,// iDFlags Integer specifying flags that are to be applied to the damage
			sMeansOfDeath,// sMeansOfDeath Integer specifying the method of death
			sWeapon,// sWeapon The weapon number of the weapon used to inflict the damage
			damagepos,// vPoint The point the damage is from?
			damagedir,// vDir The direction of the damage
			"none",// sHitLoc The location of the hit
			0// psOffsetTime The time offset for the damage
		 );
	}
	else
	{
		// destructable walls and such can only be damaged in certain ways.
		if ( self.isADestructable && ( sWeapon == "artillery_mp" || sWeapon == "claymore_mp" ) )
			return;

		self.entity notify( "damage", iDamage, eAttacker );
	}
}

debugline( a, b, color )
{
	for ( i = 0; i < 30 * 20; i++ )
	{
		line( a, b, color );
		wait .05;
	}
}


onWeaponDamage( eInflictor, sWeapon, meansOfDeath, damage )
{
	self endon( "death" );

	switch( sWeapon )
	{
		case "concussion_grenade_mp":
			// should match weapon settings in gdt
			radius = 512;
			scale = 1 - ( distance( self.origin, eInflictor.origin ) / radius );

			time = 1 + ( 4 * scale );

			wait( 0.05 );
			self shellShock( "concussion_grenade_mp", time );
		break;
		default:
			// shellshock will only be done if meansofdeath is an appropriate type and if there is enough damage.
//			maps\mp\gametypes\_shellshock::shellshockOnDamage( meansOfDeath, damage );
		break;
	}

}

watchC4AltDetonate()
{
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "detonated" );
	level endon( "game_ended" );

	buttonTime = 0;
	for ( ;; )
	{
		if ( self UseButtonPressed() )
		{
			buttonTime = 0;
			while ( self UseButtonPressed() )
			{
				buttonTime += 0.05;
				wait( 0.05 );
			}

			println( "pressTime1: " + buttonTime );
			if ( buttonTime >= 0.5 )
				continue;

			buttonTime = 0;
			while ( !self UseButtonPressed() && buttonTime < 0.5 )
			{
				buttonTime += 0.05;
				wait( 0.05 );
			}

			println( "delayTime: " + buttonTime );
			if ( buttonTime >= 0.5 )
				continue;

			if ( !self.c4Array.size )
				return;

			self notify( "alt_detonate" );
		}
		wait( 0.05 );
	}
}