/*
	Explosive Gel
	Author: Keith Evans
	Description: replacement for C4 and Semtex
	sticky grenade with timed detonation that can be detonated manually while in the air
	will eventually be merged with the exo_launcher to have a long range charge shot component
*/

#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#include common_scripts\utility;
#include maps\mp\gametypes\_hostmigration;
#include maps\mp\perks\_perkfunctions;

CONST_Mine_DamageRadius = 192;
CONST_Mine_DamageMax = 100;
CONST_Mine_DamageMin = 100;

watchExplosiveGelUsage()
{
	self endon( "spawned_player" );
	self endon( "disconnect" );
	self endon( "death" );
	self endon( "faux_spawn" );

	while ( 1 )
	{
		self waittill( "grenade_fire", grenade, weapname );
		if ( weapname == "explosive_gel_mp")
		{
			if( !IsAlive( self ) )
			{
				grenade delete();
				return;
			}

			thread tryUseExplosiveGel( grenade );
		}
	}	
}

init()
{	
	precacheModel( "weapon_c4" );
	precacheModel( "weapon_c4_bombsquad" );
	
	level.explosiveGelSettings					= SpawnStruct();
	level.explosiveGelSettings.stuckMesh		= "weapon_c4";
	level.explosiveGelSettings.gelBombSquadMesh = "weapon_c4_bombsquad";
	level.explosiveGelSettings.gelExplosionFx = LoadFX( "vfx/explosion/frag_grenade_default" );
	level.explosiveGelSettings.beacon[ "enemy" ]	 = loadfx( "vfx/lights/light_c4_blink" );
	level.explosiveGelSettings.beacon[ "friendly" ]	 = loadfx( "vfx/lights/light_mine_blink_friendly" );
}

tryUseExplosiveGel( grenade ) // self == player
{
	thread LaunchExplosiveGel( grenade );
	
	return true;
}

LaunchExplosiveGel( grenade )
{
	thread watchExplosiveGelAltDetonate( grenade );
	Mine =  self StickExplosiveGel( grenade ) ;
}


watchExplosiveGelAltDetonate( grenade )
{
	self endon( "death" );
	self endon( "disconnect" );
	level endon( "game_ended" );
	self endon( "change_owner" );
	grenade endon( "missile_stuck" );
	grenade endon( "death" );

	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;


			self thread EarlyDetonate( grenade );
		}
		wait( 0.05 );
	}
}

StickExplosiveGel( grenade )
{
	self endon( "earlyNotify" );
	grenade waittill( "missile_stuck" );
	
	Assert( isDefined( self ) );

	tracedown = bulletTrace( grenade.origin, grenade.origin - (0, 0, 4), false, grenade );
	
	traceup = bulletTrace( grenade.origin, grenade.origin + (0, 0, 4), false, grenade );
	
	ForwardAngles = AnglesToForward(grenade.angles);
	
	traceforward = bulletTrace( grenade.origin + (0, 0, 4), (grenade.origin + (ForwardAngles*4)), false, grenade );
	
	trace = undefined;
	IsUp = false;
	IsForward = false;
	
	if(traceforward["surfacetype"] != "none")
	{
		trace = traceforward;
		IsForward = true;
	}
	else if(traceup["surfacetype"] != "none")
	{
		trace = traceup;
		IsUp = true;
	}
	else if(tracedown["surfacetype"] != "none")
		trace = tracedown;
	else
		trace = tracedown;	//fallback

	SpawnOrigin = trace[ "position" ];

	if(SpawnOrigin == traceup[ "position" ] )
	{
		SpawnOrigin+=(0,0,-5);
	}
		
	ExplosiveGel = Spawn( "script_model", SpawnOrigin );
	
	ExplosiveGel.IsUp = IsUp;
	ExplosiveGel.IsForward = IsForward;

	normal = vectornormalize( trace["normal"] );
	angles =  vectortoangles( normal );
	
	angles += ( 90, 0, 0 );

	ExplosiveGel.angles = angles;

	ExplosiveGel SetModel( level.explosiveGelSettings.stuckMesh );
	ExplosiveGel.owner = self;
	ExplosiveGel SetOtherEnt(self);
	ExplosiveGel.killCamOffset = ( 0, 0, 55 );
	ExplosiveGel.killCamEnt = Spawn( "script_model", ExplosiveGel.origin + ExplosiveGel.killCamOffset );
	ExplosiveGel.stunned = false;
	
	ExplosiveGel.weaponname = "explosive_gel_mp";
	
	grenade delete();

	level.mines[level.mines.size] = ExplosiveGel;
				
	ExplosiveGel thread createBombSquadModel( level.explosiveGelSettings.gelBombSquadMesh, "tag_origin", self );
	ExplosiveGel thread mineBeacon();
	ExplosiveGel thread setExplosiveGelTeamHeadIcon( self.team );
	ExplosiveGel thread mineDamageMonitor();
	ExplosiveGel thread ExplosiveGelCountdownDetonation( self );	
	
	return ExplosiveGel;
}

createBombSquadModel( modelName, tagName, owner )
{
	apm_bombSquadModel = spawn( "script_model", (0,0,0) );
	apm_bombSquadModel hide();
	wait ( 0.05 );
	
	apm_bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( owner );
	apm_bombSquadModel setModel( modelName );
	apm_bombSquadModel linkTo( self, tagName, (0,0,0), (0,0,0) );
	apm_bombSquadModel SetContents( 0 );
	
	self waittill ( "death" );

	if ( isDefined(self.trigger) )
		self.trigger delete();
	
	apm_bombSquadModel delete();
}

mineBeacon()  // self == apm_mine
{
	effect["friendly"] = SpawnFx( level.explosiveGelSettings.beacon["friendly"], self getTagOrigin( "tag_fx" ) );
	effect["enemy"] = SpawnFx( level.explosiveGelSettings.beacon["enemy"], self getTagOrigin( "tag_fx" ) );

	self thread mineBeaconTeamUpdater( effect );
	self waittill( "death" );
	
	effect["friendly"] delete();
	effect["enemy"] delete();
}


mineBeaconTeamUpdater( effect, effect_flare )  // self == apm_mine
{
	self endon ( "death" );
	
	ownerTeam = self.owner.team;
	
	// PlayFXOnTag fails if run on the same frame the parent entity was created
	wait ( 0.05 );
	
	TriggerFx( effect["friendly"] );
	TriggerFx( effect["enemy"] );

	for ( ;; )
	{
		effect["friendly"] Hide();
		effect["enemy"] Hide();

		foreach ( player in level.players )
		{
			if ( level.teamBased )
			{
				if ( player.team == ownerTeam )
				{
					effect["friendly"] ShowToPlayer( player );
				}
				else
				{
					effect["enemy"] ShowToPlayer( player );
				}
			}
			else
			{
				if ( player == self.owner )
				{
					effect["friendly"] ShowToPlayer( player );
				}
				else
				{
					effect["enemy"] ShowToPlayer( player );
				}
			}
		}
		
		level waittill_either ( "joined_team", "player_spawned" );
	}
}


setExplosiveGelTeamHeadIcon( team )
{
	self endon( "death" );
	wait .05;
	if ( level.teamBased )
	{
		if(self.IsUp==true || self.IsForward==true)
			self maps\mp\_entityheadicons::setTeamHeadIcon( team, ( 0, 0, 28 ), undefined, true );
		else
			self maps\mp\_entityheadicons::setTeamHeadIcon( team, ( 0, 0, 28 ) );
	}
	else if ( isDefined( self.owner ) )
	{
		if(self.IsUp==true)
			self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (28,0,28) );
		else
			self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (0,0,28) );
	}
}

mineDamageMonitor()  // self == apm_mine
{
	self endon( "mine_triggered" );
	self endon( "mine_selfdestruct" );
	self endon( "death" );

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

	attacker = undefined;

	while ( 1 )
	{
		self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, iDFlags, weapon );
		
		if ( !isPlayer( attacker ) )
			continue;

		// don't allow people to destroy mines on their team if FF is off
		if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
			continue;

		if( IsDefined( weapon ) )
		{
			shortWeapon = maps\mp\_utility::strip_suffix( weapon, "_lefthand" );

			switch( shortWeapon )
			{
			case "smoke_grenade_mp":
			case "smoke_grenade_var_mp":
				continue;
			}
		}
		break;
	}

	self notify( "mine_destroyed" );

	if ( isDefined( type ) && ( isSubStr( type, "MOD_GRENADE" ) || isSubStr( type, "MOD_EXPLOSIVE" ) ) )
		self.wasChained = true;

	if ( isDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
		self.wasDamagedFromBulletPenetration = true;

	self.wasDamaged = true;
	
	if( isPlayer( attacker ) )
	{
		attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "bouncing_betty" );
	}

	if ( level.teamBased )
	{
		// "destroyed_explosive" notify, for challenges
		if ( isdefined( attacker ) && isdefined( attacker.pers[ "team" ] ) && isdefined( self.owner ) && isdefined( self.owner.pers[ "team" ] ) )
		{
			if ( attacker.pers[ "team" ] != self.owner.pers[ "team" ] )
				attacker notify( "destroyed_explosive" );
		}
	}
	else
	{
		// checking isDefined attacker is defensive but it's too late in the project to risk issues by not having it
		if ( isDefined( self.owner ) && isDefined( attacker ) && attacker != self.owner )
			attacker notify( "destroyed_explosive" );		
	}
	
	self thread mineExplode( attacker );
}


mineExplode( attacker )
{
	if ( !IsDefined( self ) || !IsDefined(self.owner) )
		return;
	
	// using a passed in attacker means that the owner wasn't the one who detonated this, this way the correct credit will go to the correct player
	if( !IsDefined( attacker ) )
		attacker = self.owner;
			
	self PlaySound( "null" );
	
	tagOrigin = self GetTagOrigin( "tag_fx" );
	PlayFX( level.explosiveGelSettings.gelExplosionFx, tagOrigin );
	
	wait( 0.05 ); // needed or the effect doesn't play
	if ( !IsDefined( self ) || !IsDefined(self.owner) )
		return;
	
	self Hide();
	
	//cause damage
	self RadiusDamage( self.origin, CONST_Mine_DamageRadius, CONST_Mine_DamageMax, CONST_Mine_DamageMin, attacker, "MOD_EXPLOSIVE");
	
	if ( IsDefined( self.owner ) && IsDefined( level.leaderDialogOnPlayer_func ) )
		self.owner thread [[ level.leaderDialogOnPlayer_func ]]( "mine_destroyed", undefined, undefined, self.origin );

	wait( 0.2 );
	if ( !IsDefined( self ) || !IsDefined(self.owner) )
		return;

	self thread apm_mine_deleteKillCamEnt();
	 
	self notify ( "death" );
	
	
	if(IsDefined(self.pickuptrigger))
		self.pickuptrigger delete();

	self hide();	
}

EarlyDetonate( grenade )
{
	self notify( "earlyNotify" );

	tagOrigin = grenade GetTagOrigin( "tag_fx" );
	PlayFX( level.explosiveGelSettings.gelExplosionFx, tagOrigin );

	grenade Detonate();
}

apm_mine_deleteKillCamEnt()
{
	wait ( 3 );
	self.killCamEnt delete();
	self delete();
		
	level.mines = array_removeUndefined(level.mines);
	
}

ExplosiveGelCountdownDetonation( Owner )
{
	self endon( "mine_destroyed" );
	self endon( "mine_selfdestruct" );
	self endon( "death" );

	wait 3;
	
	self notify( "mine_triggered" );
	
	self thread mineExplode();
}