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

LASER_DROP_PITCH_OFFLINE = -10;
LASER_DROP_PITCH_ONLINE = 5;

//TODO: write this for teambased/multi team based and FFA

init()
{
	initLaserFX();
	initLaserSound();
	initLaser();
	initLaserEnts();
	
	level.killstreakFuncs["mp_laser2"] = ::tryUseMPLaser;
	level.mapKillStreak = "mp_laser2";
	level.mapKillstreakPickupString = &"MP_LASER2_MAP_KILLSTREAK_PICKUP";
	level.mapKillStreakDamageFeedbackSound = ::handleDamageFeedbackSound;
	level.killstreak_laser_fxmode = 0;
	level.mapCustomBotKillstreakFunc = ::setupBotsForMapKillstreak;
}

setupBotsForMapKillstreak()
{
	level thread maps\mp\bots\_bots_ks::bot_register_killstreak_func( "mp_laser2", maps\mp\bots\_bots_ks::bot_killstreak_simple_use );
}

initLaserFX()
{
	level.laser_fx["beahm"] = LoadFX( "vfx/muzzleflash/laser_wv_mp_laser" );
	level.laser_fx["beahm_smoke"] = LoadFX( "vfx/muzzleflash/laser_wv_mp_laser_smoke" );
	level.laser_fx["laser_field1"] = LoadFX( "vfx/map/mp_laser2/laser_core_lvl1" );
	level.laser_fx["laser_field1_cheap"] = LoadFX( "vfx/map/mp_laser2/laser_core_lvl1_cheap" );
	level.laser_fx["laser_field2"] = LoadFX( "vfx/map/mp_laser2/laser_core_lvl2" );
	level.laser_fx["laser_field2_cheap"] = LoadFX( "vfx/map/mp_laser2/laser_core_lvl2_cheap" );
	level.laser_fx["laser_field3"] = LoadFX( "vfx/map/mp_laser2/laser_core_lvl3" );
	//level.laser_fx["laser_field1_up"] = LoadFX( "vfx/map/mp_laser2/laser_core_up_lvl1" );
	level.laser_fx["laser_field2_up"] = LoadFX( "vfx/map/mp_laser2/laser_core_up_lvl2" );
	level.laser_fx["laser_field3_up"] = LoadFX( "vfx/map/mp_laser2/laser_core_up_lvl3" );
	level.laser_fx["laser_field1_up_slow"] = LoadFX( "vfx/map/mp_laser2/laser_core_up_slow_lvl1" );
	//level.laser_fx["laser_field1_down"] = LoadFX( "vfx/map/mp_laser2/laser_core_down_lvl1" );
	level.laser_fx["laser_field2_down"] = LoadFX( "vfx/map/mp_laser2/laser_core_down_lvl2" );
	level.laser_fx["laser_field3_down"] = LoadFX( "vfx/map/mp_laser2/laser_core_down_lvl3" );
	level.laser_fx["laser_field1_down_slow"] = LoadFX( "vfx/map/mp_laser2/laser_core_down_slow_lvl1" );
	//level.laser_fx["laser_field2_down_slow"] = LoadFX( "vfx/map/mp_laser2/laser_core_down_slow_lvl2" );
	//level.laser_fx["laser_field3_down_slow"] = LoadFX( "vfx/map/mp_laser2/laser_core_down_slow_lvl3" );
	level.laser_fx["laser_charge1"] = LoadFX( "vfx/map/mp_laser2/laser_energy_fire_lvl1" );
	level.laser_fx["laser_beam_done1"] = LoadFX( "vfx/map/mp_laser2/laser_energy_beam_done_lvl1" );
	level.laser_fx["hatch_light"] =  Loadfx( "vfx/lights/mp_laser2/light_lasercore_glow" );
	level.laser_fx["hatch_light_close"] =  Loadfx( "vfx/lights/mp_laser2/light_lasercore_glow_close" );

	level.laser_fx["laser_steam"] = LoadFX( "vfx/map/mp_laser2/laser_core_steam" );
	level.laser_fx["laser_movement_sparks"] = LoadFX( "vfx/sparks/machinery_scrape_sparks_looping" );
	
}

initLaserSound()
{

	// Dialog.
	game["dialog"]["laser_deactivated"] = "laser_deactivated";
	game["dialog"]["laser_offline"] = "laser_offline";
	game["dialog"]["laser_strength"] = "laser_strength";
}

initLaser()
{
	laser = spawnStruct();
	laser.health =					999999; // keep it from dying anywhere in code
	laser.maxHealth =				1000; // this is the health we'll check
	laser.burstMin =				20;
	laser.burstMax =				120;
	laser.pauseMin =				0.15;
	laser.pauseMax =				0.35;	
	laser.sentryModeOn =			"sentry_manual";	
	laser.sentryModeOff =			"sentry_offline";	
	laser.timeOut =					45.0;	
	laser.spinupTime =				0.05;	
	laser.overheatTime =			8.0;	
	laser.cooldownTime =			0.1;	
	laser.fxTime =					0.3;	
	laser.streakName =				"sky_laser_turret";
	laser.weaponInfo =				"sky_laser_mp";
	laser.useweaponinfo =			"killstreak_laser2_mp";
	laser.modelBase =				"mp_sky_laser_turret_head";
	laser.modelDestroyed =			"mp_sky_laser_turret_head";	
	laser.headIcon =				true;	
	laser.teamSplash =				"used_mp_laser2";	
	laser.shouldSplash =			false;	
	laser.voDestroyed =				"laser_deactivated";
	laser.voOffline =				"laser_offline";
	laser.vopower =					"laser_strength";
	laser.coreShellshock =			"default";
	
	if ( !IsDefined( level.sentrySettings ) )
		level.sentrySettings = [];
	
	level.sentrySettings[ "sky_laser_turret" ] = laser;
	level.killstreakWieldWeapons["mp_laser2_core"]	= "mp_laser2";
}

initLaserEnts()
{
	sentryType = "sky_laser_turret";
	
	PreCacheItem( "mp_laser2_core" );
	PreCacheModel( "lsr_laser_button_01_obj" );
	
	laserEnt = GetEnt( "lasergun", "targetname");
	assertEx( isDefined( laserEnt ), "ent with targetname of lasergun is undefined" );
	laserEnt hide();
	laserEnt laserlightfill();
	laserEnt.FxEnts = laserEnt laser_initFxEnts();
	laserEnt.offSwitch = laserEnt laser_initOffSwitch();
	
	laserEnt.lifter = GetEnt( "laser_animated_prop", "targetname");
	laserEnt.lifter.parts = getentarray( "lsr_animated_parts", "targetname" );
	laserEnt.lifter laserlightfill();
	assertEx( isDefined( laserEnt ), "ent with targetname of laser_lifter is undefined" );
	laserEnt.moveOrgs = laserEnt.lifter laser_initMoveOrgs();
	laserEnt.lifter.animUp = "lsr_laser_turret_up";
	laserEnt.lifter.animDown = "lsr_laser_turret_down";
	laserEnt.lifter.animIdleDown = "lsr_laser_turret_idle_down";
	laserEnt.lifter.animIdleUp = "lsr_laser_turret_idle_up";
	
	laserEnt.GeneratorHat = GetEnt( "generator_hat", "targetname");
	assertEx( isDefined( laserEnt ), "ent with targetname of generator_hat is undefined" );
	laserEnt.GeneratorHat.anim_up = "laser_button_on";
	laserEnt.GeneratorHat.anim_down = "laser_button_off";
	laserEnt.GeneratorHat.anim_idle_up = "laser_button_idle_on";
	laserEnt.GeneratorHat.anim_idle_down = "laser_button_idle_off";
	
	laserEnt.coreDamageTrig = getent( "trig_lasercore_damage", "targetname" );
	assertEx( isDefined( laserEnt.coreDamageTrig ), "ent with targetname of trig_lasercore_damage is undefined" );
	
	laserEnt.coreDeathTrig = getent( "trig_lasercore_death", "targetname" );
	assertEx( isDefined( laserEnt.coreDeathTrig ), "ent with targetname of trig_lasercore_death is undefined" );
	
	laserEnt.firingDamageTrig = getent( "trig_laserfire_damage", "targetname" );
	assertEx( isDefined( laserEnt.firingDamageTrig ), "ent with targetname of trig_laserfire_damage is undefined" );
	
	laserEnt.ownerList = [];
	
	laserEnt.collision = SpawnStruct();

	laserEnt.collision.col_base = GetEnt( "laser_collision_base", "targetname");
	laserEnt.collision.col_head = GetEnt( "laser_collision_head", "targetname");
	//laserEnt.collision.col_gun = GetEnt( "laser_collision_gun", "targetname");
	
	laserEnt.flaps_top = getentarray( "lsr_flap_top", "targetname" );
	laserEnt.attachments = getentarray( "lsr_geo_attach", "targetname" );
	
	laserEnt.lifter LinkGeoToTurret(laserEnt, true);
	
	flaps_bottom = getentarray( "lsr_flap_bottom", "targetname" );
	laserEnt.flaps = array_combine( laserEnt.flaps_top, flaps_bottom);
	foreach( flap in laserEnt.flaps )
	{
		flap.col_base = getent( flap.target, "targetname" );
		if( isDefined( flap.col_base ) )
			flap.col_base.unresolved_collision_kill = true;
		flap.col_T = getent( flap.col_base.target, "targetname" );
		if( isDefined( flap.col_T ) )
			flap.col_T.unresolved_collision_kill = true;
		flap.col_base LinkToSynchronizedParent( flap, "mainFlapBase" );
		flap.col_T LinkToSynchronizedParent( flap, "mainFlap_T" );
	}
	laserEnt.flap_animClose = "lsr_energy_hatch_close";
	laserEnt.flap_animIdleClose = "lsr_energy_hatch_close_idle";
	laserEnt.flap_animOpen = "lsr_energy_hatch_open";
	laserEnt.flap_animIdleOpen = "lsr_energy_hatch_open_idle";
	
	level.sentryGun = laserEnt;
	level.sentryGun laser_initSentry( sentryType );
}
LinkGeoToTurret(laser, ShouldLink)
{
	if(ShouldLink == false)
	{
		laser.collision.col_base unlink();
		laser.collision.col_head unlink();
		//laser.collision.col_gun unlink();
		foreach( flap in laser.flaps_top )
			flap unlink();
		foreach( attachment in laser.attachments )
			attachment unlink();
		foreach( part in laser.lifter.parts )
			part unlink();
	}
	else if(ShouldLink == true)
	{
		laser.collision.col_base linkto(self, "tag_origin");
		laser.collision.col_head linkto(self, "tag_aim_pivot");
		//laser.collision.col_gun linkto(self, "cannon_f");
		foreach( flap in laser.flaps_top )
			flap linktoSynchronizedParent( self );
		foreach( attachment in laser.attachments )
			attachment linktoSynchronizedParent( self );
		foreach( part in laser.lifter.parts )
			part linktoSynchronizedParent( self );
	}
}
laser_initMoveOrgs()
{
	locEnt_top = getStruct( "laser_lifter_top_loc", "targetname" );
	locEnt_bottom = getStruct( "laser_lifter_bottom_loc", "targetname" );
	
	assertEx( isDefined( locEnt_top ), "struct with targetname of laser_lifter_top_loc is undefined" );
	assertEx( isDefined( locEnt_bottom ), "struct with targetname of laser_lifter_bottom_loc is undefined" );
	
	locEnt_dist = locEnt_top.origin - locEnt_bottom.origin;
	
	moveOrgs = [];
	moveOrgs["bottom"] = self.origin;
	moveOrgs["top"] = self.origin + locEnt_dist;
	
	return moveOrgs;
}

laser_initFxEnts()
{
	
	charge_up = undefined;
	
	coreStruct = getstruct( "laser_core_fx_pos", "targetname" );
	
	assertEx( isDefined( coreStruct ), "struct with targetname of laser_core_fx_pos is undefined" );
	
		charge_up = coreStruct spawn_tag_origin();
		charge_up show();
	
	FxEnts = [];
	FxEnts["charge_up"] = charge_up;
	
	return FxEnts;
}

laser_initOffSwitch()
{
	trigger = GetEnt( "laser_use_trig", "targetname" );
	offSwitchEnt = GetEnt( "laser_switch", "targetname" );
	
	assertEx( isDefined( offSwitchEnt ), "ent with targetname of laser_switch is not defined" );
	assertEx( isDefined( trigger ), "ent with targetname of laser_use_trig is not defined" );
	
	offSwitch = [];
	
	switch_obj = spawn( "script_model", offSwitchEnt.origin );
	switch_obj.angles = offSwitchEnt.angles;
	switch_obj setmodel( "lsr_laser_button_01_obj" );
	switch_obj hide();
	
	visuals = [switch_obj];

	use_zone = maps\mp\gametypes\_gameobjects::createUseObject( "none", trigger, visuals, (0,0,64) );
	use_zone maps\mp\gametypes\_gameobjects::allowUse( "none" );
	use_zone maps\mp\gametypes\_gameobjects::setUseTime( 5 );
	use_zone maps\mp\gametypes\_gameobjects::setUseText( &"MP_LASERTURRET_HACKING" );
	use_zone maps\mp\gametypes\_gameobjects::setUseHintText( &"MP_LASERTURRET_HACK" );
	
	use_zone.onBeginUse = ::laser_offSwitch_onBeginUse;
	use_zone.onEndUse = ::laser_offSwitch_onEndUse;
	use_zone.onUse = ::laser_offSwitch_onUsePlantObject;
	use_zone.onCantUse = ::laser_offSwitch_onCantUse;
	use_zone.useWeapon = "search_dstry_bomb_mp";
	
	offSwitch = [];
	offSwitch["switch_obj"] = switch_obj;
	offSwitch["use_zone"] = use_zone;
	
	return offSwitch;
}

laser_offSwitch_onBeginUse( player )
{
	// IPrintLnBold("using switch");
}

laser_offSwitch_onEndUse( team, player, result )
{
	// IPrintLnBold("end using switch");
}

laser_offSwitch_onUsePlantObject( player )
{
	level.sentryGun endon ( "death" );
	level endon ( "game_ended" );
	
//	IPrintLnBold("planting on switch");

	if ( IsDefined( level.sentryGun.owner ) )
	{
		level.sentryGun.owner thread leaderDialogOnPlayer( level.sentrySettings[ level.sentryGun.sentryType ].voDestroyed );
	}
	
	player playSound( "mp_bomb_plant" );
	damage = level.sentrySettings[ "sky_laser_turret" ].maxhealth;
	level.sentryGun notify
		( "damage", 		//"damage"
		damage, 			//damage
		player,				//attacker
		( 0, 0, 0 ), 		//direction_vec
		( 0, 0, 0 ), 		//point
		"MOD_UNKNOWN",		//meansOfDeath
		undefined, 			//modelName
		undefined,			//tagName
		undefined,			//partName
		undefined,			//iDFlags
		"none" );//weapon
}

laser_offSwitch_onCantUse( player )
{
//	IPrintLnBold("switch cant use");
}

laser_initSentry( sentryType ) // self == sentry, turret, sam
{
	self.sentryType = sentryType;

	self setModel( level.sentrySettings[ self.sentryType ].modelBase );
	self.shouldSplash = true; // we only want to splash on the first placement

	self setCanDamage( false );

	self makeTurretInoperable();
	self SetLeftArc( 180 );
	self SetRightArc( 180 );
	self SetTopArc( 80 );
	self SetDefaultDropPitch( LASER_DROP_PITCH_OFFLINE );	// setting this mainly prevents Turret_RestoreDefaultDropPitch() from running
	self.laser_on = false;
	
	self.lifter ScriptModelPlayAnimDeltaMotion( self.lifter.animIdleDown );
	foreach( part in self.lifter.parts )
	{
		part ScriptModelPlayAnimDeltaMotion( self.lifter.animIdleDown );
	}
	
	// needs a kill cam ent
	killCamEnt = Spawn( "script_model", self GetTagOrigin( "tag_laser" ) );
	killCamEnt LinkTo( self );
	self.killCamEnt = killCamEnt;
	self.killCamEnt SetScriptMoverKillCam( "explosive" );
	
	//HINT: this is different than other autosentries since the turret isn't placed it should be solid all the time
	self maps\mp\killstreaks\_autosentry::sentry_makeSolid();
	
	self setTurretModeChangeWait( true );
	self laser_setInactive();
	
	//TODO: don't need to do fakedeath. Do real death but modify maps/_autosentry::sentry_handleDeath function to not delete the turret
	self thread laser_handleDamage();
	self thread laser_handleFakeDeath();
	self thread maps\mp\killstreaks\_autosentry::sentry_beepSounds();
	
	//self makeUsable();
}

laser_handleDamage()
{
	self endon( "death" );
	level endon( "game_ended" );
	
	while ( true )
	{
		self.health = level.sentrySettings[ self.sentryType ].health;
		self.maxHealth = level.sentrySettings[ self.sentryType ].maxHealth;
		self.damageTaken = 0; // how much damage has it taken
	
		self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
		
		// don't allow people to destroy equipment on their team if FF is off
		if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
			continue;

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

		// up the damage for airstrikes, stealth bombs, and bomb sites
		switch( weapon )
		{
		case "artillery_mp":
		case "stealth_bomb_mp":
			damage *= 4;
			break;
		case "bomb_site_mp":
			damage = self.maxHealth;
			break;
		}

		if ( meansOfDeath == "MOD_MELEE" )
			self.damageTaken += self.maxHealth;

		modifiedDamage = damage;
		if ( isPlayer( attacker ) )
		{
			attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "sentry" );

			if ( attacker _hasPerk( "specialty_armorpiercing" ) )
			{
				modifiedDamage = damage * level.armorPiercingMod;			
			}			
		}

		// in case we are shooting from a remote position, like being in the osprey gunner shooting this
		if( IsDefined( attacker.owner ) && IsPlayer( attacker.owner ) )
		{
			attacker.owner maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "sentry" );
		}
		
		if( IsDefined( weapon ) )
		{
			shortWeapon = maps\mp\_utility::strip_suffix( weapon, "_lefthand" );

			switch( shortWeapon )
			{
			case "ac130_105mm_mp":
			case "ac130_40mm_mp":
			case "stinger_mp":
			case "remotemissile_projectile_mp":
				self.largeProjectileDamage = true;
				modifiedDamage = self.maxHealth + 1;
				break;

			case "artillery_mp":
			case "stealth_bomb_mp":
				self.largeProjectileDamage = false;
				modifiedDamage += ( damage * 4 );
				break;

			case "bomb_site_mp":
			case "emp_grenade_mp":
			case "emp_grenade_var_mp":
				self.largeProjectileDamage = false;
				modifiedDamage = self.maxHealth + 1;
				break;
			}
			
			maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
		}

		self.damageTaken += modifiedDamage;		

		if ( self.damageTaken >= self.maxHealth )
		{
			thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );

			if ( isPlayer( attacker ) && (!IsDefined(self.owner) || attacker != self.owner) )
			{
				level thread maps\mp\gametypes\_rank::awardGameEvent( "kill", attacker, weapon, undefined, meansOfDeath );				
			}
		
			if ( IsDefined( self.owner ) )
				self.owner thread leaderDialogOnPlayer( level.sentrySettings[ self.sentryType ].voDestroyed, undefined, undefined, self.origin );
		
			self notify( "fakedeath" );
		}
	}
}

laser_handleFakeDeath()
{
	self endon( "death" );
	level endon( "game_ended" );
	
	for ( ;; )
	{
		self waittill( "fakedeath" );
	
		self laser_setInactive();
		self.ownerList = [];
		self SetSentryOwner( undefined );
		self.samTargetEnt = undefined;
		
		if ( level.teamBased )
		{
			self.team = undefined;
		}
		
		if( IsDefined( self.ownerTrigger ) )
			self.ownerTrigger delete();
	
		self playSound( "sentry_explode" );		
	
		if ( IsDefined( self.inUseBy ) )//TODO: I don't think I'm using this. take it out.
		{
			//playFxOnTag( common_scripts\utility::getFx( "sentry_explode_mp" ), self, "tag_origin" );
			//playFxOnTag( common_scripts\utility::getFx( "sentry_smoke_mp" ), self, "tag_aim" );
			
			self.inUseBy.turret_overheat_bar maps\mp\gametypes\_hud_util::destroyElem();
			self.inUseBy maps\mp\killstreaks\_autosentry::restorePerks();
			self.inUseBy maps\mp\killstreaks\_autosentry::restoreWeapons();				
			
			self notify( "deleting" );
			wait ( 1.0 );
			//StopFXOnTag( common_scripts\utility::getFx( "sentry_explode_mp" ), self, "tag_origin" );
			//StopFXOnTag( common_scripts\utility::getFx( "sentry_smoke_mp" ), self, "tag_aim" );
		}	
		else
		{		
			//playFxOnTag( common_scripts\utility::getFx( "sentry_explode_mp" ), self, "tag_aim" );
			wait ( 1.5 );		
			self playSound( "sentry_explode_smoke" );
			for ( smokeTime = 8; smokeTime > 0; smokeTime -= 0.4 )
			{
				//playFxOnTag( common_scripts\utility::getFx( "sentry_smoke_mp" ), self, "tag_aim" );
				wait ( 0.4 );
			}
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: tryUseMPLaser( <lifeId> )"
"Summary: function called when a player tries to use the mp_laser2 map-based killstreak"
"Module: Entity"
"CallOn: a player"
"MandatoryArg: <lifeId>: "
"Example: level.killstreakFuncs["mp_laser2"] = ::tryUseMPLaser;"
"SPMP: MP"
///ScriptDocEnd
=============
*/
tryUseMPLaser( lifeId, modules )
{
	if ( !self playerCanUseLaser() )
	{
		self iPrintLnBold( &"MP_LASERTURRET_ENEMY" );
		return false;
	}

	if ( self isUsingRemote() )
	{
		return false;
	}
	
	if ( self isAirDenied() )
	{
		return false;
	}
	
	if ( self isEMPed() )
	{
		return false;
	}
	
	if( isDefined( level.sentryGun.locked ) && level.sentryGun.locked == true )
	{
		self iPrintLnBold( &"MP_LASERTURRET_BUSY" );
		return false;
	}
	
	self maps\mp\_matchdata::logKillstreakEvent( "mp_laser2", self.origin );
	level.sentryGun laser_setOwner( self );
	
	mode = level.sentryGun getMode();
	if( ( !isDefined( level.sentryGun.mode ) || level.sentryGun.mode == "off" ) && ( !isDefined( level.sentryGun.moving ) || level.sentryGun.moving == false ) )//TODO: check if we still need to do this .mode hack should be able to just use GetMode()
	{
		self laser_setPlaceSentry( level.sentryGun, level.sentryGun.sentryType );
	}

	return true;
}

playerCanUseLaser()
{
	assertEx( isPlayer( self ), "playerCanUseLaser() called on non-player entity type: " + self.classname );
	assertEx( isDefined( level.sentryGun ), "playerCanUseLaser() called without a laser entity" );
	
	if( !isDefined( level.sentryGun ) )
		return false;
	
	if ( level.teamBased )
	{
		//check to see if other team owns it
		if( isDefined( level.sentryGun.team ) && level.sentryGun.team != self.team )
			return false;
	}
	else
	{
		//check if the laser is already owned by someone... anyone
		foreach( owner in level.sentryGun.ownerList )
		{
			if( (isDefined( owner ) && owner != self ) )
				return false;
		}
	}
	
	return true;	
}

laser_setOwner( owner )
{
	assertEx( IsDefined( owner ), "laser_setOwner() called without owner specified" );
	assertEx( isPlayer( owner ), "laser_setOwner() called on non-player entity type: " + owner.classname );

	self.owner = owner;
	self.ownerList = array_add( self.ownerList, owner );

	self SetSentryOwner( self.owner );
	self SetTurretMinimapVisible( true, "sam_turret" );
	
	if ( level.teamBased )
	{
		self.team = self.owner.team;
		self setTurretTeam( self.team );
	}
	
	self thread laser_handleOwnerDisconnect( owner );
	self thread player_sentry_timeOut( owner );
	
	if (self.ownerList.size > 1)
		self thread playLaserContainmentSwap();
}

laser_handleOwnerDisconnect( owner )
{
	self endon ( "death" );
	self endon ( "fakedeath" );
	level endon ( "game_ended" );
	
	owner common_scripts\utility::waittill_any( "disconnect", "joined_team", "joined_spectators" );
	
	self.ownerList = array_remove( self.ownerList, owner );
	
	if( owner != self.owner )
		self thread stopLaserContainmentSwap();
	
	else if( owner == self.owner )
	{
		next_owner = getNextPlayerInOwnerQueue( self.ownerList );
		if( isDefined( next_owner ) )
		{
			self laser_setOwner( next_owner );
		}
		
		else
		{
			self notify( "fakedeath" );
		}
	}
}

array_removeFirstInQueue( ents, remover )
{
	indexents = [];
	indexent = undefined;
	
	for( i=0; i<ents.size; i++ )
	{
		if( ents[i] == remover )
		{
			indexents[ indexents.size ] = i;
		}
	}
	
	indexent = indexents[ indexents.size - 1 ];
	
	newents = [];
	for( i=0; i<ents.size; i++ )
	{
		if ( i != indexent )
			newents[ newents.size ] = ents[i];
	}

	return newents;
}

getNextPlayerInOwnerQueue( array )
{
	if( !isDefined( array ) )
	{
		return undefined;
	}

	array = array_reverse( array );
	foreach( ent in array )
	{
		if( isDefined( ent ) && isPlayer( ent ) && isAlive( ent ) )
		{
			return ent;
		}
	}
	
	return undefined; //array is defined but empty of players
}

laser_setPlaceSentry( sentryGun, sentryType )
{
	self endon ( "death" );
	self endon ( "disconnect" );

	player = self;
	
	if ( ! player maps\mp\_utility::validateUseStreak() )
	return false;
	
	player.last_sentry = sentryType;
	sentryGun laser_setPlaced( self );		
	return true;
}

laser_setPlaced( player )
{
	//TODO: don't need this since the model is the same //self setModel( level.sentrySettings[ self.sentryType ].modelBase );

	// failsafe check, for some reason this could be manual and setSentryCarried doesn't like that
	if( self GetMode() == "manual" )
	{
		self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
		self.mode = "off";//HACK: checking the mode wasn't consistent so had to add my own var to check against
	}

	self setSentryCarrier( undefined );
	self setCanDamage( true );

	//TODO: do we need to define this? is it false by default? will killstreaks  work correctly without this?
	if( IsDefined( self.owner ) )
		self.owner.isCarrying = false;

	self thread laser_setActive( player );
	self thread playLaserCoreEvent();
	self thread playLaserContainmentStart();
	self playSound( "sentry_gun_plant" );


	self notify ( "placed" );
}

playLaserCoreEvent()
{
	wait 2.0;
	PlayFXOnTag( level.laser_fx[ "laser_steam" ], self.FxEnts["charge_up"], "tag_origin" );
}

StopLaserCoreEvent()
{
	wait 4;
	StopFXOnTag( level.laser_fx[ "laser_steam" ], self.FxEnts["charge_up"], "tag_origin" );
	
	//turn on exploder for off state fx
	//level delayThread( 3.5, common_scripts\_exploder::activate_clientside_exploder, 200);
	level noself_delayCall( 3.5, ::ActivatePersistentClientExploder, 200 );
}

StartLaserLights()
{	
	
	laserLgtEnts = GetScriptableArray( "laser_light", "targetname" );
	foreach ( laserLgtEnt in laserLgtEnts )
	{
		laserLgtEnt SetScriptablePartState("lsr_part_a", "laser_on_a");
	}
	
	laserLgtEnts = GetScriptableArray( "laser_light_b", "targetname" );
	foreach ( laserLgtEnt in laserLgtEnts )
	{
		laserLgtEnt SetScriptablePartState("lsr_part_b", "laser_on_b");
	}
	
	laserLgtEnts = GetScriptableArray( "laser_point_lights", "targetname" );
	foreach ( laserLgtEnt in laserLgtEnts )
	{
		laserLgtEnt SetScriptablePartState("static_part1", "warning");
	}
}

StopLaserLights()
{
	laserLgtEnts = GetScriptableArray( "laser_light", "targetname" );
	foreach ( laserLgtEnt in laserLgtEnts )
	{
		laserLgtEnt SetScriptablePartState("lsr_part_a", "laser_off_a");
	}
	
	laserLgtEnts = GetScriptableArray( "laser_light_b", "targetname" );
	foreach ( laserLgtEnt in laserLgtEnts )
	{
		laserLgtEnt SetScriptablePartState("lsr_part_b", "laser_off_b");
	}
	
	laserLgtEnts = GetScriptableArray( "laser_point_lights", "targetname" );
	foreach ( laserLgtEnt in laserLgtEnts )
	{
		laserLgtEnt SetScriptablePartState("static_part1", "healthy");
	}
}

playLaserContainmentStart()
{
	level.killstreak_laser_fxmode = 1;
	//turn off exploder for off state fx
	stopclientexploder(200);
	PlayFXOnTag( level.laser_fx[ "hatch_light" ], self.FxEnts["charge_up"], "tag_origin" );
	PlayFXOnTag( level.laser_fx[ "hatch_light" ], level.sentryGun.lifter, "tag_origin" );
	wait 5.33;  //time for laser platform to start animating up
	StartLaserLights();
	PlayFXOnTag( level.laser_fx[ "laser_field1_up_slow" ], self.FxEnts["charge_up"], "tag_origin" );
	wait 1.0; //time for laser field up slow effect to play
	PlayFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts["charge_up"], "tag_origin" );
	//turn on exploder for lighting laser cannon
	//level delayThread( 3.5, common_scripts\_exploder::activate_clientside_exploder, 150);
	
}

playLaserContainmentSwap()
{
	if( !isDefined( self.ownerList ) || self.ownerList.size < 1 )
	   return;
	
	level.killstreak_laser_fxmode = self.ownerList.size;
	fxId = level.killstreak_laser_fxmode;
	
	switch( fxId )
	{
		case 0:
			break;
		case 1:
			/*PlayFXOnTag( level.laser_fx[ "laser_field1_up" ], self.FxEnts["charge_up"], "tag_origin" );
			wait 1.0;
			PlayFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts["charge_up"], "tag_origin" );*/
			break;
		case 2:
			StopFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts["charge_up"], "tag_origin" );
			PlayFXOnTag( level.laser_fx[ "laser_field1_cheap" ], self.FxEnts["charge_up"], "tag_origin" );
			PlayFXOnTag( level.laser_fx[ "laser_field2_up" ], self.FxEnts["charge_up"], "tag_origin" );
			wait 1.0;
			
			laserOrngLgtEnts = GetScriptableArray( "laser_light", "targetname" );
			foreach ( laserOrngLgtEnt in laserOrngLgtEnts )
			{
				laserOrngLgtEnt SetScriptablePartState("lsr_part_a", "laser_on_02_a");
			}
			
				
			laserLgtEnts = GetScriptableArray( "laser_light_b", "targetname" );
			foreach ( laserLgtEnt in laserLgtEnts )
			{
				laserLgtEnt SetScriptablePartState("lsr_part_b", "laser_on_02_b");
			}
			
			PlayFXOnTag( level.laser_fx[ "laser_field2" ], self.FxEnts["charge_up"], "tag_origin" );
			break;
		case 3:
			StopFXOnTag( level.laser_fx[ "laser_field2" ], self.FxEnts["charge_up"], "tag_origin" );
			PlayFXOnTag( level.laser_fx[ "laser_field2_cheap" ], self.FxEnts["charge_up"], "tag_origin" );
			PlayFXOnTag( level.laser_fx[ "laser_field3_up" ], self.FxEnts["charge_up"], "tag_origin" );
			wait 1.0;
			
			laserYellowLgtEnts = GetScriptableArray( "laser_light", "targetname" );
			foreach ( laserYellowLgtEnt in laserYellowLgtEnts )
			{
				laserYellowLgtEnt SetScriptablePartState("lsr_part_a", "laser_on_03_a");
			}
			
					
			laserLgtEnts = GetScriptableArray( "laser_light_b", "targetname" );
			foreach ( laserLgtEnt in laserLgtEnts )
			{
				laserLgtEnt SetScriptablePartState("lsr_part_b", "laser_on_03_b");
			}
			
			
			PlayFXOnTag( level.laser_fx[ "laser_field3" ], self.FxEnts["charge_up"], "tag_origin" );
			break;
		default:
			break;
	}
}

stopLaserContainmentSwap()
{
	if( !isDefined( level.killstreak_laser_fxmode ))
	   return;
	
	fxId = level.killstreak_laser_fxmode;
	level.killstreak_laser_fxmode = self.ownerList.size;
	wait 1.0;  //time for next stage of containment to come up.
	switch( fxId )
	{
		case 0:
			break;
		case 1:
			//PlayFXOnTag( level.laser_fx[ "laser_field1_down" ], self.FxEnts["charge_up"], "tag_origin" );
			//StopFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts["charge_up"], "tag_origin" );
			break;
		case 2:
			PlayFXOnTag( level.laser_fx[ "laser_field2_down" ], self.FxEnts[ "charge_up" ], "tag_origin" ); 
			StopFXOnTag( level.laser_fx[ "laser_field2" ], self.FxEnts[ "charge_up" ], "tag_origin" );
			StopFXOnTag( level.laser_fx[ "laser_field1_cheap" ], self.FxEnts[ "charge_up" ], "tag_origin" );
			PlayFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts[ "charge_up" ], "tag_origin" );
			
			laserLgtEnts = GetScriptableArray( "laser_light", "targetname" );
			foreach ( laserLgtEnt in laserLgtEnts )
			{
				laserLgtEnt SetScriptablePartState("lsr_part_a", "laser_on_a");
			}
			
					
			laserLgtEnts = GetScriptableArray( "laser_light_b", "targetname" );
			foreach ( laserLgtEnt in laserLgtEnts )
			{
				laserLgtEnt SetScriptablePartState("lsr_part_b", "laser_on_b");
			}
			
			break;
		case 3:
			PlayFXOnTag( level.laser_fx[ "laser_field3_down" ], self.FxEnts["charge_up"], "tag_origin" );
			StopFXOnTag( level.laser_fx[ "laser_field3" ], self.FxEnts["charge_up"], "tag_origin" );
			StopFXOnTag( level.laser_fx[ "laser_field2_cheap" ], self.FxEnts[ "charge_up" ], "tag_origin" );
			PlayFXOnTag( level.laser_fx[ "laser_field2" ], self.FxEnts[ "charge_up" ], "tag_origin" );
			
			laserLgtEnts = GetScriptableArray( "laser_light", "targetname" );
			foreach ( laserLgtEnt in laserLgtEnts )
			{
				laserLgtEnt SetScriptablePartState("lsr_part_a", "laser_on_02_a");
			}
			
					
			laserLgtEnts = GetScriptableArray( "laser_light_b", "targetname" );
			foreach ( laserLgtEnt in laserLgtEnts )
			{
				laserLgtEnt SetScriptablePartState("lsr_part_b", "laser_on_02_b");
			}
			
			
			break;
		default:
			break;
	}
}
	
stopLaserContainmentEnd()
{
	if( !isDefined( level.killstreak_laser_fxmode ))
	   return;
	
	fxId = level.killstreak_laser_fxmode;
	level.killstreak_laser_fxmode = 0;
	wait 1.6;  //time for laser to start animating down. TODO: use a notetrack for this
	switch( fxId )
	{
		case 0:
			break;
		case 1:
			PlayFXOnTag( level.laser_fx[ "laser_field1_down_slow" ], self.FxEnts["charge_up"], "tag_origin" );
			KillFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts["charge_up"], "tag_origin" );
			StopLaserLights();
			//turn off laser cannon lighting
			//StopClientExploder(150);
			wait 1.5;
			PlayFXOnTag( level.laser_fx[ "hatch_light_close" ], self.FxEnts["charge_up"], "tag_origin" );
			PlayFXOnTag( level.laser_fx[ "hatch_light_close" ], level.sentryGun.lifter, "tag_origin" );
			break;
		/*case 2:
			PlayFXOnTag( level.laser_fx[ "laser_field2_down_slow" ], self.FxEnts[ "charge_up" ], "tag_origin" ); 
			KillFXOnTag( level.laser_fx[ "laser_field2" ], self.FxEnts[ "charge_up" ], "tag_origin" );
			KillFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts["charge_up"], "tag_origin" );
			break;
		case 3:
			PlayFXOnTag( level.laser_fx[ "laser_field3_down_slow" ], self.FxEnts["charge_up"], "tag_origin" );
			KillFXOnTag( level.laser_fx[ "laser_field3" ], self.FxEnts["charge_up"], "tag_origin" );
			KillFXOnTag( level.laser_fx[ "laser_field2" ], self.FxEnts[ "charge_up" ], "tag_origin" );
			KillFXOnTag( level.laser_fx[ "laser_field1" ], self.FxEnts["charge_up"], "tag_origin" );
			break;*/
		default:
			break;
	}
}

laser_setInactive()
{
	self.samTargetEnt = undefined;
	self ClearTargetEntity();
	self setMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
	self.mode = "off";
	
	//self makeUnusable();
	entNum = self GetEntityNumber();

	self maps\mp\killstreaks\_autosentry::removeFromTurretList( entNum );

	if ( level.teamBased )
	{
		self setTeamHeadIcon_large( "none", (0,0,0) );
	}
	else if ( IsDefined( self.owner ) )
	{
		self setTeamHeadIcon_large( "none", (0,0,0) );
	}

	self SetDefaultDropPitch( LASER_DROP_PITCH_OFFLINE );
	level.sentryGun SetTurretMinimapVisible( false );
	self SetTurretMinimapVisible( false );
	self setCanDamage( false );
	
	self laser_coreDamage_deactivated( self.coreDamageTrig );
	self laser_coreDamage_deactivated( self.coreDeathTrig );
	
	if( self.lifter.origin != self.moveOrgs["bottom"] )//these functions shouldn't happen at init
	{
		self laser_usableOffSwitch_off();
		self thread StopLaserCoreEvent();
		self thread stopLaserContainmentEnd();
		self thread laser_handleMoveBottom();
	}
}

laser_handleMoveBottom()
{
	self endon ( "death" );
	self endon ( "fakedeath" );
	level endon ( "game_ended" );
	
	assert( isDefined( self.lifter ) );
	foreach( part in self.lifter.parts )
		assert( isDefined( part ) );
	
	self.locked = true;	//lock while the laser is moving down
	self.moving = true;
	
	wait( 1 );
	
	self LinkGeoToTurret(self, false);
	self.lifter show();
	self.lifter LinkGeoToTurret(self, true);
	self hide();

	
// Xform Down 
    notetracktoaliasarray = [];
    notetracktoaliasarray[ "laser_xform_down_sec1_start" ] 	= "laser_xform_down_sec1_start";
    notetracktoaliasarray[ "laser_xform_down_sec1_end" ] 	= "laser_xform_down_sec1_end";
    notetracktoaliasarray[ "laser_xform_down_sec2_start" ] 	= "laser_xform_down_sec2_start";
    notetracktoaliasarray[ "laser_xform_down_sec2_end" ] 	= "laser_xform_down_sec2_end";
	
	self.lifter ScriptModelPlayAnimDeltaMotion( self.lifter.animDown, "laser_xform_down_sec1_start" );
	foreach( part in self.lifter.parts )
	{
		part ScriptModelPlayAnimDeltaMotion( self.lifter.animDown, "laser_xform_down_sec1_start" );
	}
    self.lifter thread snd_play_on_notetrack( notetracktoaliasarray, "laser_xform_down_sec1_start" );
	
    foreach( flap in self.flaps )
	{
    	if( isDefined( flap.targetname ) && flap.targetname == "lsr_flap_bottom" )//need the collision to match the anim
    		flap ScriptModelPlayAnimDeltaMotion( self.flap_animClose );
    	else
			flap ScriptModelPlayAnim( self.flap_animClose );
	}
	
	self.lifter thread aud_play_laser_move_down( 6 );
	self.lifter thread playMovementSparks(2.5);
	self.lifter delayThread( 6, ::stopMovementSparks );
	wait( 7.8 );//HACK: length on anim
	
	self.moving = false;
	self.locked = false;
}

laser_handleMoveTop()
{
    self endon ( "death" );
    self endon ( "fakedeath" );
    level endon ( "game_ended" );
    
    assert( isDefined( self.lifter ) );
    foreach( part in self.lifter.parts )
    	assert( isDefined( part ) );
    
    self.moving = true;
   
    // Xform Up
    notetracktoaliasarray = [];
    notetracktoaliasarray[ "laser_xform_up_sec1_start" ] 	= "laser_xform_up_sec1_start";
    notetracktoaliasarray[ "laser_xform_up_sec1_end" ] 		= "laser_xform_up_sec1_end";
    notetracktoaliasarray[ "laser_xform_up_sec2_start" ] 	= "laser_xform_up_sec2_start";    
    notetracktoaliasarray[ "laser_xform_up_sec2_end" ] 		= "laser_xform_up_sec2_end";      
    
    self.lifter ScriptModelPlayAnimDeltaMotion( self.lifter.animUp, "laser_xform_up_sec1_start" );
    foreach( part in self.lifter.parts )
    {
    	part ScriptModelPlayAnimDeltaMotion( self.lifter.animUp, "laser_xform_up_sec1_start" );
    }
    self.lifter thread snd_play_on_notetrack( notetracktoaliasarray, "laser_xform_up_sec1_start" );
   
	foreach( flap in self.flaps )
    {
		if( isDefined( flap.targetname ) && flap.targetname == "lsr_flap_bottom" )//need the collision to match the anim
    		flap ScriptModelPlayAnimDeltaMotion( self.flap_animOpen );
    	else
			flap ScriptModelPlayAnim( self.flap_animOpen );
    }
   
    self.lifter thread aud_play_laser_move_up( 5.0 );
    self.lifter thread playMovementSparks( 1.5 );
    self.lifter delaythread( 5.0, ::stopMovementSparks );
    wait( 8 );//HACK: length of anim
    
    self.lifter LinkGeoToTurret(self, false);
    self show();
    self LinkGeoToTurret(self, true);
    self.lifter hide();
    
    waittillframeend;
    
    self.moving = false;
}

aud_play_laser_move_up( delay_time )
{
	thread snd_play_in_space_delayed( "laser_beam_power_up", ( 15,382,902 ), 5.2 );
	thread snd_play_linked( "laser_platform_move_up_start", self );
	thread aud_laser_energy_beam_start();
	thread snd_play_loop_in_space( "laser_platform_move_alarm_lp", ( -1, 355, 945 ),"aud_stop_laser_alarm", 2  );
	wait( delay_time );
	thread snd_play_linked( "laser_platform_move_up_end", self );
	level notify( "aud_stop_laser_alarm" );
}

aud_play_laser_move_down( delay_time )
{
	thread snd_play_in_space_delayed( "laser_beam_power_down", ( 15,382,902 ), 1.25 );
	thread aud_laser_pre_move_down();
	wait(2.5);
	move_down_sound = thread snd_play_linked( "laser_platform_move_down_start", self );
	wait( 3 );
	thread snd_play_linked( "laser_platform_move_down_legs_fold", self );
	move_down_sound scalevolume( 0.0, 0.05 );
	move_down_sound stopsounds();
	level notify( "aud_laser_energy_lp_off" );
	wait( 0.7 );
	thread snd_play_linked( "laser_platform_move_down_end", self );
}

aud_laser_pre_move_down()
{
	wait(1);
	pre_move_down_sound = thread snd_play_linked( "laser_platform_pre_move_down", self );
}

aud_laser_energy_beam_start()
{
	thread snd_play_loop_in_space( "laser_base_pulse_energy_lp", ( -13, 393, 352 ),"aud_laser_energy_lp_off", 2  );
	thread snd_play_loop_in_space( "laser_base_pulse_low_lp", ( -13, 393, 352 ),"aud_laser_energy_lp_off", 2  );	
	thread snd_play_loop_in_space( "laser_base_pulse_motor_lp", ( -13, 393, 352 ),"aud_laser_energy_lp_off", 2  );	
}

playMovementSparks(waitTime)
{
	wait waitTime;
	
	foreach( part in self.parts )
	{
		if( part.model == "mp_sky_laser_turret_lega" )
			PlayFXOnTag(level.laser_fx["laser_movement_sparks"], part, "fx_joint_0");
	}
}

stopMovementSparks()
{
	foreach( part in self.parts )
	{
		if( part.model == "mp_sky_laser_turret_lega" )
			StopFXOnTag(level.laser_fx["laser_movement_sparks"], part, "fx_joint_0");
	}
}
	

player_sentry_timeOut( player )
{
	self endon ( "death" );
	self endon ( "fakedeath" );
	level endon ( "game_ended" );
	
	player endon( "disconnect" );
	player endon( "joined_team" );
	player endon( "joined_spectators" );
	
	lifeSpan = level.sentrySettings[ self.sentryType ].timeOut;
	
	while ( lifeSpan )
	{
		wait ( 1.0 );
		maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
		
		lifeSpan = max( 0, lifeSpan - 1.0 );
	}
	
	if( isDefined( player ) )
	{
		self.ownerList = array_removeFirstInQueue( self.ownerList, player );
		
		if( self.ownerList.size != 0 )
		{
			player thread leaderDialogOnPlayer( level.sentrySettings[ level.sentryGun.sentryType ].vopower );
			self thread stopLaserContainmentSwap();
		}
		else
		{
			player thread leaderDialogOnPlayer( level.sentrySettings[ level.sentryGun.sentryType ].voOffline );
			self notify( "fakedeath" );
		}
	}
}

laser_setActive( player )
{
	foreach ( player in level.players )
	{
		entNum = self GetEntityNumber();
		self maps\mp\killstreaks\_autosentry::addToTurretList( entNum );
	}	

	if( self.shouldSplash )
	{
		level thread maps\mp\_utility::teamPlayerCardSplash( level.sentrySettings[ self.sentryType ].teamSplash, self.owner, self.owner.team );
		self.shouldSplash = false;
	}
	
	self laser_coreDamage_activated( self.coreDamageTrig );
	self laser_coreDamage_activated( self.coreDeathTrig, true );
	self laser_handleMoveTop();
	
	self SetDefaultDropPitch( LASER_DROP_PITCH_ONLINE );
	self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
	self.mode = "on";
	
	self laser_usableOffSwitch_on();
	
	if( level.sentrySettings[ self.sentryType ].headIcon )
	{
		if ( level.teamBased )
		{
			self setTeamHeadIcon_large( self.team, (0,0,400) );
		}
		else
		{
			self setTeamHeadIcon_large( self.team, (0,0,400) );
		}
	}
	
	self thread laser_attackTargets();
	self thread laser_watchDisabled();
}

laser_usableOffSwitch_off()
{
	self.GeneratorHat ScriptModelPlayAnim(self.GeneratorHat.anim_down);
	
	level.sentryGun.offSwitch["use_zone"] maps\mp\gametypes\_gameobjects::disableObject();
	level.sentryGun.offSwitch["switch_obj"] hide();
}

laser_usableOffSwitch_on()
{
	laserOwner = "none";
	if( isDefined( level.sentryGun.owner ) && isDefined( level.sentryGun.owner.team ) )
	{
		laserOwner = level.sentryGun.owner.team;
	}
	self.GeneratorHat ScriptModelPlayAnim(self.GeneratorHat.anim_up);
	
	level.sentryGun.offSwitch["use_zone"].interactTeam = "enemy";
	level.sentryGun.offSwitch["use_zone"] maps\mp\gametypes\_gameobjects::setOwnerTeam( laserOwner );
	
	foreach( player in level.players )
	{
		if( player.team != laserOwner && laserOwner != "none" )
		{
			player.laserOffSwitch_isVisible = true;
			level.sentryGun.offSwitch["switch_obj"] showtoplayer( player );
		}
	}
}

setTeamHeadIcon_large( team, offset ) // "allies", "axis", "all", "none"
{
	if ( !level.teamBased )
		return;

	if ( !isDefined( self.entityHeadIconTeam ) ) 
	{
		self.entityHeadIconTeam = "none";
		self.entityHeadIcon = undefined;
	}

	shader = game["entity_headicon_" + team];	

	self.entityHeadIconTeam = team;
	
	if ( isDefined( offset ) )
		self.entityHeadIconOffset = offset;
	else
		self.entityHeadIconOffset = (0,0,0);

	self notify( "kill_entity_headicon_thread" );

	if ( team == "none" )
	{
		if ( isDefined( self.entityHeadIcon ) )
			self.entityHeadIcon destroy();
		return;
	}
	
	headIcon = newTeamHudElem( team );
	headIcon.archived = true;
	headIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
	headIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
	headIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
	headIcon.alpha = 1;
	headIcon setShader( shader, 50, 50 );
	headIcon setWaypoint( false, false, false, true );
	self.entityHeadIcon = headIcon; 

	self thread maps\mp\_entityheadicons::keepIconPositioned();
	self thread maps\mp\_entityheadicons::destroyHeadIconsOnDeath();
}

setPlayerHeadIcon_large( player, offset ) // "allies", "axis", "all", "none"
{
	if ( level.teamBased )
		return;
	
	if ( !isDefined( self.entityHeadIconTeam ) ) 
	{
		self.entityHeadIconTeam = "none";
		self.entityHeadIcon = undefined;
	}

	self notify( "kill_entity_headicon_thread" );

	if ( !isDefined( player ) )
	{
		if ( isDefined( self.entityHeadIcon ) )
			self.entityHeadIcon destroy();
		return;
	}

	team = player.team;		
	self.entityHeadIconTeam = team;
	
	if ( isDefined( offset ) )
		self.entityHeadIconOffset = offset;
	else
		self.entityHeadIconOffset = (0,0,0);

	shader = game["entity_headicon_" + team];	
	
	headIcon = newClientHudElem( player );
	headIcon.archived = true;
	headIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
	headIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
	headIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
	headIcon.alpha = 1;
	headIcon setShader( shader, 50, 50 );
	headIcon setWaypoint( false, false, false, true );
	self.entityHeadIcon = headIcon; 

	self thread maps\mp\_entityheadicons::keepIconPositioned();
	self thread maps\mp\_entityheadicons::destroyHeadIconsOnDeath();
}

laser_watchDisabled()
{
	self endon ( "death" );
	self endon ( "fakedeath" );
	level endon ( "game_ended" );
	
	self notify ( "laser_watchDisabled" );
	self endon ( "laser_watchDisabled" );//only have one of these threads running at a time

	while( true )
	{
		// this handles any flash or concussion damage
		self waittill( "emp_damage", attacker, duration );//TODO: rename "emp_damage". It is really poorly named. It actually is coming from concussion and frags but not the emp. look at laser_handleDamage or sentry_handleDamage for the real emp damage.

		PlayFXOnTag( common_scripts\utility::getfx( "sentry_explode_mp" ), self, "tag_aim" );

		self SetDefaultDropPitch( LASER_DROP_PITCH_OFFLINE );
		self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
		self.mode = "none";
		
		//TODO: what happens if someone activates it during this time?

		wait( duration );

		self SetDefaultDropPitch( LASER_DROP_PITCH_ONLINE );
		self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
		self.mode = "on";
	}
}

laser_attackTargets() // self == sam
{
	self endon ( "death" );
	self endon ( "fakedeath" );
	level endon ( "game_ended" );
	
	self notify ( "laser_attackTargets" );
	self endon ( "laser_attackTargets" );//only have one of these threads running at a time

	self.samTargetEnt = undefined;
	self.samMissileGroups = [];

	while( true )
	{
		self.samTargetEnt = maps\mp\killstreaks\_autosentry::sam_acquireTarget();
		self laser_fireOnTarget();
		wait( 0.05 );
	}
}

laser_watchLightBeam() // self == sam turret
{
	self endon( "death" );
	level endon( "game_ended" );
	
	//Start the deadly laser beam's FX.
	wait ( 0.5 );
	
	if( !isDefined( self.ownerList ) || self.ownerList.size < 1 )
	   return;
	
	fxId = self.ownerList.size;
	
	
	PlayFXOnTag( level.laser_fx[ "laser_charge1" ], self.FxEnts["charge_up"], "tag_origin" );
	PlayFXOnTag( level.laser_fx[ "beahm" ], self, "tag_laser" );	
	self laser_coreDamage_activated( self.firingDamageTrig );
	fire_start_sfx = snd_play_linked( "wpn_skylaser_fire_startup", self );
	self thread play_loop_sound_on_entity( "wpn_skylaser_beam_lp" );
	
	self LaserOn( "mp_laser2_laser" );
	
	while( IsDefined( self.samTargetEnt ) && IsDefined( self GetTurretTarget( true ) ) && self GetTurretTarget( true ) == self.samTargetEnt )
	{
		wait( 0.05 );
	}
	
	//Stop the deadly laser beam's FX.
	self LaserOff();
	
	StopFXOnTag( level.laser_fx[ "laser_charge1" ], self.FxEnts[ "charge_up" ], "tag_origin" );
	PlayFXOnTag( level.laser_fx[ "laser_beam_done1" ], self.FxEnts[ "charge_up" ], "tag_origin" );
	StopFXOnTag( level.laser_fx[ "beahm" ], self, "tag_laser" );
	PlayFXOnTag( level.laser_fx[ "beahm_smoke" ], self, "tag_laser" );
	self laser_coreDamage_deactivated( self.firingDamageTrig );
	self stop_loop_sound_on_entity( "wpn_skylaser_beam_lp" );
	fire_stop_sfx = snd_play_linked( "wpn_skylaser_beam_stop", self );
	if( isdefined( fire_start_sfx ) )
	{
		fire_start_sfx StopSounds();		
	}
}

laser_fireOnTarget() // self == sam turret
{
	self endon( "death" );
	self endon( "fakedeath" );
	level endon( "game_ended" );
	
	// locked on to target, turn on laser and fire
	if( IsDefined( self.samTargetEnt ) )
	{
		// because we hide the orbital support platform and don't really delete it
		if( isDefined( level.orbitalsupport_planeModel ) && self.samTargetEnt == level.orbitalsupport_planeModel && !IsDefined( level.orbitalsupport_player ) )
		{
			self.samTargetEnt = undefined;
			self ClearTargetEntity();
			return;
		}
		
		self SetTargetEntity( self.samTargetEnt );
		self waittill( "turret_on_target" );
		if( !IsDefined( self.samTargetEnt ) )
			return;
		
		// turn on the laser, also watch for if the target is crashing or leaving
		if( !self.laser_on )
		{
			self thread laser_watchLightBeam();
			self thread maps\mp\killstreaks\_autosentry::sam_watchLaser();
			self thread maps\mp\killstreaks\_autosentry::sam_watchCrashing();
			self thread maps\mp\killstreaks\_autosentry::sam_watchLeaving();
			self thread maps\mp\killstreaks\_autosentry::sam_watchLineOfSight();
		}

		wait( 0.5 );

		if( !IsDefined( self.samTargetEnt ) )
			return;

		// because we hide the orbital support platform and don't really delete it
		if( isDefined( level.orbitalsupport_planeModel ) && self.samTargetEnt == level.orbitalsupport_planeModel && !IsDefined( level.orbitalsupport_player ) )
		{
			self.samTargetEnt = undefined;
			self ClearTargetEntity();
			return;
		}
		
		// need to shoot the turret so it'll show up on the mini-map as it shoots, it will shoot blanks
		self ShootTurret();
		
		//Use a damage radius to deal damage to aerial targets.
		self fireLaserBeam();
	}
}

fireLaserBeam()
{
	self endon( "death" );
	self endon( "fakedeath" );
	level endon( "game_ended" );
	
	trace_start = self GetTagOrigin( "tag_laser" );
	trace_vector = AnglesToForward( self GetTagAngles( "tag_laser" ) );
	trace_end = trace_start + 15000 * trace_vector;
	LaserTraceData = BulletTrace( trace_start, trace_end, true, self );
	
	//HACK: The target is an Orbital Support Platform. Place a RadiusDamage directly on the AC-130's position because the BulletTrace is not hitting the AC-130.
	if ( isDefined( level.orbitalSupport_planemodel ) && self.samTargetEnt == level.orbitalSupport_planemodel && IsDefined( level.orbitalSupport_player ) )
	{
		//TODO: add some VO notifying the Orbital Support Platform user that they are getting shot down by a LAZER!
		RadiusDamage( level.orbitalSupport_planemodel.origin, 512, 100, 100, self.owner, "MOD_EXPLOSIVE", "killstreak_laser2_mp" );
	}
	
	//HACK: the target is an Orbital carepackage
	else if( isDefined( self.samTargetEnt.isPodRocket ) && self.samTargetEnt.isPodRocket )
	{
		//TODO: add some VO notifying the Orbital Support Platform user that they are getting shot down by a LAZER!
		self.samTargetEnt notify( "damage", 1000, self.owner, undefined, undefined, "mod_explosive", undefined, undefined, undefined, undefined, "killstreak_laser2_mp" );//HACK: shouldn't have to call damage notify
	}
	
	else
	{
		RadiusDamage( LaserTraceData[ "position" ], 512, 200, 200, self.owner, "MOD_EXPLOSIVE", "killstreak_laser2_mp" );
	}
}

//Laser Power Core stuff

laser_coreDamage_activated( trig, b_kill )
{
	level endon( "game_ended" );
	
	self thread watchPlayerEnterLaserCore( trig, b_kill );
	trig trigger_on();
}

laser_coreDamage_deactivated( trig )
{
	level endon( "game_ended" );
	
	trig notify( "laser_coreDamage_deactivated" );
	
	trig trigger_off();
}

watchPlayerEnterLaserCore( trig, b_kill )
{
	//self is laser
	
	level endon( "game_ended" );
	trig endon( "laser_coreDamage_deactivated" );
	
	while( true )
	{
		trig waittill( "trigger", player );
		if ( !isPlayer( player ) )
			continue;
		
		if ( !isAlive( player ) )
			continue;
		
		if ( isDefined( player.laserCoreTrigIDs ) && IsDefined( player.laserCoreTrigIDs[ trig.targetname ] ) && player.laserCoreTrigIDs[ trig.targetname ] == 1 )
		{
			continue;
		}
		else
		{
			if ( !IsDefined( player.laserCoreTrigIDs ) )
				player.laserCoreTrigIDs = [];
			
			player.laserCoreTrigIDs[ trig.targetname ] = 1;
			player thread player_laserCoreEffect( trig, b_kill );
			
			// laserCoreEffect may kill the player
			if ( IsAlive( player ) )
			{
				player thread player_watchExitLaserCore( trig );
				player thread player_watchDeath( trig );
			}
			else
			{
				player.laserCoreTrigIDs[ trig.targetname ] = 0;
			}
		}
	}
		
}

player_watchExitLaserCore( trig )
{
	//self is player
	
	level endon( "game_ended" );
	self endon( "player_leftLaserCoreTrigger" + trig.targetname);
	self endon( "stop_watching_trig" );
	
	while( self isTouching( trig ) )
	{
		wait( .05 );
	}
	
	if ( IsDefined( self.laserCoreTrigIDs ) )
	{
		self.laserCoreTrigIDs[ trig.targetname ] = 0;
	}
	self player_resetLaserCoreValues();
	self notify( "player_leftLaserCoreTrigger" + trig.targetname );
}

player_laserCoreEffect( trig, b_kill )
{
	//self is player
	
	level endon( "game_ended" );
	self endon( "player_leftLaserCoreTrigger" + trig.targetname );
	self endon( "stop_watching_trig" );
	
	if( isDefined( b_kill ) && b_kill )
	{
		self _suicide();//kill even if friendly fire
		return;
	}
	
	self.poison = 0;
	shellshockName = level.sentrySettings[ level.sentryGun.sentryType ].coreShellshock;
	
	if( !isDefined( self.usingRemote ) && !isDefined( self.rideVisionSet ) )
	{
		self VisionSetNakedForPlayer( "aftermath", .5 );
		self shellshock( shellshockName, 60);
	}
	else
		self.laserCoreVisualsBlocked = true;
	
	while( 1 )
	{
		self.poison ++;
		
		switch( self.poison )
		{
			case 1:
				self ViewKick( 1, self.origin );
				break;
			case 2:				  
				self ViewKick( 3, self.origin );
				self player_doLaserCoreDamage(15);
				break;
			case 3:
				self ViewKick( 15, self.origin );
				self player_doLaserCoreDamage(15);
				break;
			case 4:
				self ViewKick( 75, self.origin );
				self _suicide( );//kill even if friendly fire
				return;
		}
		wait( 1 );
	}
}

player_watchDeath( trig )
{
	level endon( "game_ended" );
	self endon( "player_leftLaserCoreTrigger" + trig.targetname);
	
	self common_scripts\utility::waittill_any( "disconnect", "joined_team", "joined_spectators", "death" );
	
	self.laserCoreTrigIDs = undefined;
	self player_resetLaserCoreValues();
	self notify( "stop_watching_trig" );
}

player_resetLaserCoreValues()
{
	//self is the player
	if( !isDefined( self.laserCoreVisualsBlocked ) )
	{
		self stopShellShock();
		self thread revertVisionSetForPlayer( .5 );
	}
	self.laserCoreVisualsBlocked = undefined;
}


laserlightfill()
{
	PlayFXOnTag( getFx( "light_laser_fill" ), self, "tag_origin" );
}
	

player_doLaserCoreDamage( iDamage )
{
	if( !isdefined( level.sentryGun.owner ) )
		return;
	
	//TODO: Handle friendly fire
	//TODO: change the weapon to the laser2 weapon and update matchdata.def
	
	self thread [[ level.callbackPlayerDamage ]](
	self,// eInflictor The entity that causes the damage.( e.g. a turret )
	level.sentryGun.owner,// 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
	"MOD_TRIGGER_HURT",// sMeansOfDeath Integer specifying the method of death
	"mp_laser2_core",// sWeapon The weapon number of the weapon used to inflict the damage
	self.origin,// vPoint The point the damage is from?
	( 0,0,0 ) - self.origin,// vDir The direction of the damage
	"none",// sHitLoc The location of the hit
	0// psOffsetTime The time offset for the damage
	);
}

/*
=============
///ScriptDocBegin
"Name: handleDamageFeedbackSound()"
"Summary: "
"Module: Entity"
"CallOn: a player who is doing damage to someone else and should get a damage feedback sound."
"Example: self thread handleDamageFeedbackSound();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
handleDamageFeedbackSound( killer_ent )
{
//	self.shouldloopdamagefeedback = true;
//	self.damagefeedbacktimer = 2;//HACK time of audio looping
//	
//	self PlayLocalSound( "MP_hit_alert" );
//	
//	while ( isDefined( killer_ent.laser_on ) && killer_ent.laser_on )
//	{
//		self PlayLocalSound( "MP_solar_hit_alert" );
//		killer_ent waitForTimeOrNotify( self.damagefeedbacktimer, "laser_off" );
//	}
//	
//	self StopLocalSound( "MP_solar_hit_alert" );
//	self PlayLocalSound( "MP_hit_alert" );
//	self.shouldloopdamagefeedback = undefined;
} 




