#include maps\mp\_utility;
#include common_scripts\utility;
#include maps\mp\gametypes\_hud_util;

/*
	Remote Turret killstreak: the player will throw this and it will stick to the ground and give a remote view with a small turret
*/

// Killstreak Data
ENTER_MESSAGE_ALPHA = 0.25;
ENTER_MESSAGE_FADE_TIME = 1.5;

CONST_REMOTE_TURRET_SENTRY_BURSTMIN =		20;
CONST_REMOTE_TURRET_SENTRY_BURSTMAX =		120;
CONST_REMOTE_TURRET_SENTRY_PAUSEMIN =		0.15;
CONST_REMOTE_TURRET_SENTRY_PAUSEMAX =		0.35;
CONST_REMOTE_TURRET_SENTRY_SPINUPTIME =		0.05;
CONST_REMOTE_TURRET_SENTRY_OVERHEATTIME =	4.0;
CONST_REMOTE_TURRET_SENTRY_COOLDOWNTIME =	0.1;
CONST_REMOTE_TURRET_SENTRY_FXTIME =			0.3;
CONST_REMOTE_TURRET_SENTRY_HORZ_ARC =		80;
CONST_REMOTE_TURRET_SENTRY_HORZ_ARC_FULL = 	180;
CONST_REMOTE_TURRET_SENTRY_TOP_ARC = 		50;
CONST_REMOTE_TURRET_SENTRY_BOTTOM_ARC = 	30;
CONST_REMOTE_TURRET_BEAM_WEAPINFO =			"remote_energy_turret_mp";
CONST_REMOTE_TURRET_MG_WEAPINFO =			"sentry_minigun_mp";
CONST_REMOTE_TURRET_ROCKET_WEAPINFO = 		"killstreakmahem_mp";

init()
{
	if ( !IsDefined( level.turretType ) )
		level.turretType = [];
	level.turretType[ "mg_turret" ]	= "remote_mg_turret";

	level.killStreakFuncs[ "remote_mg_turret" ] 		= ::tryUseRemoteMGTurret;
	level.killStreakFuncs[ "remote_mg_sentry_turret" ] 	= ::tryUseRemoteMGSentryTurret;
//	level.killstreakWieldWeapons["remote_turret_mp"] = "remote_mg_sentry_turret";
	level.killstreakWieldWeapons["remote_energy_turret_mp"] = "remote_mg_sentry_turret";
	level.killstreakWieldWeapons["sentry_minigun_mp"] = "remote_mg_sentry_turret";
	level.killstreakWieldWeapons["killstreakmahem_mp"] = "remote_mg_sentry_turret";

	if ( !IsDefined( level.turretSettings ) )
		level.turretSettings = [];

	level.turretSettings[ "mg_turret" ] = spawnStruct();
	level.turretSettings[ "mg_turret" ].sentryModeOn =			"sentry";	
	level.turretSettings[ "mg_turret" ].sentryModeOff =			"sentry_offline";	
	level.turretSettings[ "mg_turret" ].timeOut =				60.0;	
	level.turretSettings[ "mg_turret" ].maxHealth =				1000; // this is what we check against for death	
	level.turretSettings[ "mg_turret" ].streakName =			"remote_mg_turret";
	//level.turretSettings[ "mg_turret" ].modelBombSquad =		"mp_remote_turret_bombsquad";	
	level.turretSettings[ "mg_turret" ].teamSplash =			"used_remote_mg_turret";	
	level.turretSettings[ "mg_turret" ].hintEnter =				&"MP_ENTER_REMOTE_TURRET";	
	level.turretSettings[ "mg_turret" ].hintPickUp =			&"MP_HOLD_TO_CARRY";
	level.turretSettings[ "mg_turret" ].hintRipOff = 			&"MP_TURRET_RIP_OFF";
	level.turretSettings[ "mg_turret" ].hintDropTurret = 		&"MP_TURRET_DROP";
	level.turretSettings[ "mg_turret" ].placeString =			&"MP_TURRET_PLACE";	
	level.turretSettings[ "mg_turret" ].cannotPlaceString =		&"MP_TURRET_CANNOT_PLACE";	

	level.turretSettings[ "mg_turret" ].laptopInfo =			"killstreak_remote_turret_mp";

	level._effect[ "sentry_explode_mp" ]	   = LoadFX( "vfx/explosion/remote_sentry_death" );
	level._effect[ "sentry_smoke_mp" ]		   = LoadFX( "vfx/smoke/vehicle_sentrygun_damaged_smoke" );
	level._effect[ "sentry_overheat_mp" ]	   = LoadFX( "vfx/distortion/sentrygun_overheat" );
	level._effect[ "antenna_light_mp" ]		   = LoadFX( "vfx/lights/light_detonator_blink" );
	level._effect[ "sentry_stunned_mp" ]	   = LoadFX( "vfx/sparks/emp_drone_damage" );
	level._effect[ "sentry_laser_flash" ]	   = LoadFX( "vfx/fire/remote_sentry_laser_flash" );
	level._effect[ "sentry_gone" ]			   = LoadFX( "vfx/explosion/remote_sentry_death_smoke" );
	level._effect[ "sentry_rocket_muzzleflash_wv" ]   = LoadFX( "vfx/muzzleflash/rpg_flash_wv" );
	level._effect[ "sentry_rocket_muzzleflash_view" ] = LoadFX( "vfx/muzzleflash/rpg_flash_view" );
	
	game["dialog"][ "ks_sentrygun_destroyed" ] = "ks_sentrygun_destroyed";

/#
	SetDevDvarIfUninitialized( "scr_remote_turret_timeout", 0 );
	SetDevDvarIfUninitialized( "scr_remote_turret_destroy", 0 );
#/
}

tryUseRemoteMGSentryTurret( lifeId, modules )
{
	result = tryUseRemoteTurret( lifeId, "mg_turret", true, modules );

	if( result )
	{
		self maps\mp\_matchdata::logKillstreakEvent( level.turretSettings[ "mg_turret" ].streakName, self.origin );
	}

	// we're done carrying for sure and sometimes this might not get reset
	// this fixes a bug where you could be carrying and have it in a place where it won't plant, get killed, now you can't scroll through killstreaks
	self.isCarrying = false;

	return result; 
}

tryUseRemoteMGTurret( lifeId, modules )
{
	result = tryUseRemoteTurret( lifeId, "mg_turret", false, modules );

	if( result )
	{
		self maps\mp\_matchdata::logKillstreakEvent( level.turretSettings[ "mg_turret" ].streakName, self.origin );
	}

	// we're done carrying for sure and sometimes this might not get reset
	// this fixes a bug where you could be carrying and have it in a place where it won't plant, get killed, now you can't scroll through killstreaks
	self.isCarrying = false;

	return result; 
}

takeKillstreakWeapons( turretType ) // self == owner
{
	self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( level.turretSettings[ turretType ].laptopInfo );
}

tryUseRemoteTurret( lifeId, turretType, isSentry, modules )
{
	if ( IsDefined( self.turret ) )
	{
		self IPrintLnBold( &"KILLSTREAKS_SENTRY_IN_USE" );
		return false;
	}
	
	turret = createTurretForPlayer( turretType, self, isSentry, modules );
	
	if ( isdefined ( level.isHorde ) && level.isHorde && self.killstreakIndexWeapon == 1 )
		self.hordeClassTurret = turret;
	
	self playerAddNotifyCommands();
	
	//	returning from this streak activation seems to strip this?
	//	manually removing and restoring
	self removePerks();
	
	self.carriedTurret = turret;

	self setCarryingTurret( turret, true );
	
	self.carriedTurret = undefined;

	self thread restorePerks();

	// if we failed to place the turret, it will have been deleted at this point
	if ( isDefined( turret ) )
		return true;
	else
		return false;
}

setupHeavyResistanceModel()
{
	if ( self.heavyResistance )
		self HidePart( "TAG_OPTIC_STANDARD" );
	else
		self HidePart( "TAG_OPTIC_RESISTANCE" );
}

setupRippableModel()
{
	if ( !self.rippable )
		self HidePart( "TAG_HANDLES" );
}

setModelTurretBaseOnly()
{
	if ( IsDefined( self.model ) && self.model != "" )
		self ShowAllParts();
	if ( self.energyTurret )
		self SetModel( "npc_sentry_energy_turret_empty_base" );
	else if ( self.rocketTurret )
		self SetModel( "npc_sentry_rocket_turret_empty_base" );
	else
		self SetModel( "npc_sentry_minigun_turret_empty_base" );
	self setupHeavyResistanceModel();
	self setupRippableModel();
}

setModelRemoteTurret()
{
	if ( IsDefined( self.model ) && self.model != "" )
		self ShowAllParts();
	if ( self.energyTurret )
		self SetModel( "npc_sentry_energy_turret_base" );
	else if ( self.rocketTurret )
		self SetModel( "npc_sentry_rocket_turret_base" );
	else
		self SetModel( "npc_sentry_minigun_turret_base" );
	self setupHeavyResistanceModel();
	self setupRippableModel();
}

setModelTurretPlacementGood()
{
	if ( IsDefined( self.model ) && self.model != "" )
		self ShowAllParts();
	if ( self.energyTurret )
		self SetModel( "npc_sentry_energy_turret_base_yellow_obj" );
	else if ( self.rocketTurret )
		self SetModel( "npc_sentry_rocket_turret_base_yellow_obj" );
	else
		self SetModel( "npc_sentry_minigun_turret_base_yellow_obj" );
	self setupHeavyResistanceModel();
	self setupRippableModel();
}

setModelTurretPlacementFailed()
{
	if ( IsDefined( self.model ) && self.model != "" )
		self ShowAllParts();
	if ( self.energyTurret )
		self SetModel( "npc_sentry_energy_turret_base_red_obj" );
	else if ( self.rocketTurret )
		self SetModel( "npc_sentry_rocket_turret_base_red_obj" );
	else
		self SetModel( "npc_sentry_minigun_turret_base_red_obj" );
	self setupHeavyResistanceModel();
	self setupRippableModel();
}

///////////////////////////////////////////////////
// CARRY FUNCTIONS
//////////////////////////////////////////////////

setCarryingTurret( turret, allowCancel ) // self == player
{
	self endon ( "death" );
	self endon ( "disconnect" );
	level endon( "game_ended" );

	if ( !isReallyAlive( self ) )
	{
		turret Delete();
		return;
	}

	turret turret_setCarried( self );

	killstreakWeapon = self getKillstreakWeapon( "remote_mg_sentry_turret" );
	lastWeapon = self GetCurrentPrimaryWeapon();
	if ( !maps\mp\gametypes\_weapons::isValidLastWeapon( lastWeapon ) || lastWeapon == "iw5_underwater_mp" )
		lastWeapon = self getLastWeapon();
	if ( !allowCancel )
	{
		self _giveWeapon( killstreakWeapon, 0 );
		self SwitchToWeapon( killstreakWeapon );
		self.water_last_weapon = killstreakWeapon;
		self _disableWeaponSwitch();
	}

	for ( ;; )
	{
		result = waittill_any_return( "place_turret", "cancel_turret", "force_cancel_placement" );

		if ( result == "cancel_turret" || result == "force_cancel_placement" )
		{
			if ( result == "cancel_turret" && !allowCancel )
				continue;

			turret turret_setCancelled();
			if ( !allowCancel )
			{
				if ( !IsDefined( self.underWater ) )
					self playerSwitchAwayFromHoldingTurret( lastWeapon, killstreakWeapon );
				else
					self.water_last_weapon = lastWeapon;
				self _enableWeaponSwitch();
			}
			return false;
		}

		if ( IsDefined( turret ) )
		{
			if ( !turret.canBePlaced )
				continue;

			turret turret_setPlaced();		
		}
		
		if ( !allowCancel )
		{
			if ( !IsDefined( self.underWater ) )
				self playerSwitchAwayFromHoldingTurret( lastWeapon, killstreakWeapon );
			else
				self.water_last_weapon = lastWeapon;
			self _enableWeaponSwitch();
		}
		return true;
	}
}

playerSwitchAwayFromHoldingTurret( lastWeapon, killstreakWeapon )
{
	self endon ( "death" );
	self endon ( "disconnect" );

	self switch_to_last_weapon( lastWeapon );
	
	while ( self GetCurrentPrimaryWeapon() != lastWeapon )
		waitframe();
	
	self.water_last_weapon = lastWeapon;
	
	self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( killstreakWeapon);
}

setRipOffTurretHead( turret ) // self == player
{
	self endon ( "death" );
	self endon ( "disconnect" );

	assert( isReallyAlive( self ) );
	assert( isPlayer( self ) );

	turret SetCanDamage( false );
	turret SetContents( 0 );
	turret FreeEntitySentient();

	turret.carriedBy = self;
	self.isCarrying = false;

	turret turret_setInactive();
	turret setModelTurretBaseOnly();
	turret notify( "carried" );
	turret notify( "ripped" );
	turret SetTurretMinimapVisible( false );
	turret thread deleteAfterTime( 20 );
	
	turret.remoteEnt makeGloballyUnusableByType();
	
	if ( turret.energyTurret )
		self thread maps\mp\killstreaks\_rippedturret::playerGiveTurretHead( "turretheadenergy_mp" );
	else if ( turret.rocketTurret )
		self thread maps\mp\killstreaks\_rippedturret::playerGiveTurretHead( "turretheadrocket_mp" );
	else
		self thread maps\mp\killstreaks\_rippedturret::playerGiveTurretHead( "turretheadmg_mp" );
	
	turret PlaySound( "sentry_gun_detach" );
}

deleteAfterTime( time )
{
	self endon( "death" );

	level maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( time );

	if( IsDefined( self ) )
	{
		deathSoundsAndFX();
		self delete();
	}
}

deathSoundsAndFX()
{
	org = self GetTagOrigin( "TAG_AIM_PIVOT" );
	PlayFX( getfx( "sentry_gone" ), org );
	playSoundAtPos( org, "sentry_gun_self_destruct" );
}

removePerks()
{
	if ( self _hasPerk( "specialty_explosivebullets" ) )
	{
		self.restorePerk = "specialty_explosivebullets";
		self _unsetPerk( "specialty_explosivebullets" );
	}		
}

restorePerks()
{
	if ( isDefined( self.restorePerk ) )
	{
		self givePerk( self.restorePerk, false );	
		self.restorePerk = undefined;
	}	
}

waitRestorePerks()
{
	self endon( "death" );
	self endon( "disconnect" );
	level endon( "game_ended" );
	wait( 0.05 );
	self restorePerks();
}

turret_quickDeath( data )
{
	self.quick_death = true;
	self notify ( "death" );
}

turret_setPlaced() // self == turret
{
	self setModelRemoteTurret();

	self thread sentry_attackTargets();
//	self thread sentry_guardian();
	self SetSentryCarrier( undefined );
	self SetCanDamage( true );

	self.carriedBy forceUseHintOff();
	self.carriedBy = undefined;
	
	if( IsDefined( self.owner ) )
	{
		self.owner.isCarrying = false;
		self make_entity_sentient_mp( self.owner.team );
	}

	// Moving platforms. 
	data = SpawnStruct();
	data.linkParent = self.placementLinkEntity;
	data.endonString = "carried";
	data.deathOverrideCallback = ::turret_quickDeath;
	self thread maps\mp\_movers::handle_moving_platforms( data );

	// TODO: get sound for this
	self playSound( "sentry_gun_deploy" );

	self thread turret_setActive();
	self notify ( "placed" );
}

turret_setCancelled()
{
	self.carriedBy forceUseHintOff();
	if( IsDefined( self.owner ) )
		self.owner.isCarrying = false;

	self delete();
}

turret_setCarried( carrier ) // self == turret
{
	assert( isPlayer( carrier ) );
	assertEx( carrier == self.owner, "turret_setCarried() specified carrier does not own this turret" );

	self setModelTurretPlacementGood();

	self SetCanDamage( false );
	self SetSentryCarrier( carrier );
	self SetContents( 0 );
	self FreeEntitySentient();
	self Unlink();
	
	self.carriedBy = carrier;
	carrier.isCarrying = true;

	carrier thread updateTurretPlacement( self );

	self thread turret_onCarrierDeath( carrier );
	self thread turret_onCarrierDisconnect( carrier );
	self thread turret_onCarrierChangedTeam( carrier );
	self thread turret_onGameEnded();

	self SetDefaultDropPitch( -89.0 );

	self turret_setInactive();

	self notify ( "carried" );
}

updateTurretPlacement( turret )
{
	self endon ( "death" );
	self endon ( "disconnect" );
	level endon ( "game_ended" );

	turret endon ( "placed" );
	turret endon ( "death" );

	turret.canBePlaced = true;
	turret.placementLinkEntity = undefined;
	
	lastCanPlaceTurret = -1; // force initial update

	for( ;; )
	{
		placement = self canPlayerPlaceSentry( true );

		turret.origin = placement[ "origin" ];
		turret.angles = placement[ "angles" ];
		turret.canBePlaced = self isOnGround() && placement[ "result" ] && ( abs(turret.origin[2]-self.origin[2]) < 10 && !turret isTouchingWater() );

		if ( IsDefined( placement[ "entity" ] ) )
			turret.placementLinkEntity = placement[ "entity" ];
		else 
			turret.placementLinkEntity = undefined;
		
		if ( turret.canBePlaced != lastCanPlaceTurret )
		{
			if ( turret.canBePlaced )
			{
				turret setModelTurretPlacementGood();
				self ForceUseHintOn( level.turretSettings[ turret.turretType ].placeString );
			}
			else
			{
				turret setModelTurretPlacementFailed();
				self ForceUseHintOn( level.turretSettings[ turret.turretType ].cannotPlaceString );
			}
		}

		lastCanPlaceTurret = turret.canBePlaced;		
		wait ( 0.05 );
	}
}

isTouchingWater()
{
	if( !isDefined( level.water_triggers ) )
		return false;
	else
	{
		foreach( trig in level.water_triggers )
		{
			if( self IsTouching( trig ) )
				return true;
		}
	}
	
	return false;
}

turret_onCarrierDeath( carrier ) // self == turret
{
	self endon ( "placed" );
	self endon ( "death" );
	self endon( "ripped" );

	carrier waittill ( "death" );

	if ( self.canBePlaced )
	{
		self turret_setPlaced();
	}
	else
	{
		if ( IsDefined( self.owner ) )
			self.owner.isCarrying = false;
		self delete();
	}
}

turret_onCarrierDisconnect( carrier ) // self == turret
{
	self endon ( "placed" );
	self endon ( "death" );
	self endon( "ripped" );

	carrier waittill ( "disconnect" );

	self delete();
}

turret_onCarrierChangedTeam( carrier ) // self == turret
{
	self endon ( "placed" );
	self endon ( "death" );
	self endon( "ripped" );

	carrier waittill_any( "joined_team", "joined_spectators" );

	self delete();
}

turret_onGameEnded( carrier ) // self == turret
{
	self endon ( "placed" );
	self endon ( "death" );
	self endon( "ripped" );

	level waittill ( "game_ended" );

	self delete();
}

///////////////////////////////////////////////////
// END CARRY FUNCTIONS
//////////////////////////////////////////////////
createTurretForPlayer( turretType, owner, isSentry, modules )
{
	assertEx( IsDefined( owner ), "createTurretForPlayer() called without owner specified" );

	turretWeapon = CONST_REMOTE_TURRET_MG_WEAPINFO;
	if ( array_contains( modules, "sentry_energy_turret" ) )
		turretWeapon = CONST_REMOTE_TURRET_BEAM_WEAPINFO;
		
	turret = SpawnTurret( "misc_turret", owner.origin, turretWeapon );
	turret.angles = owner.angles;
	turret.owner = owner;
	turret.health = level.turretSettings[ turretType ].maxHealth;
	turret.maxHealth = level.turretSettings[ turretType ].maxHealth;
	turret.turretType = turretType;
	turret.stunned = false;
	turret.directHacked = false;
	turret.modules = modules;
	turret.heavyResistance = array_contains( turret.modules, "sentry_heavy_resistance" );
	turret.antiIntrusion = array_contains( turret.modules, "sentry_anti_intrusion" );
	turret.rocketTurret = array_contains( turret.modules, "sentry_rocket_turret" );
	turret.energyTurret = array_contains( turret.modules, "sentry_energy_turret" );
	turret.rippable = array_contains( turret.modules, "sentry_rippable" );
	turret.isSentry = array_contains( turret.modules, "sentry_guardian" );
	turret.is360 = array_contains( turret.modules, "sentry_360" );
	turret.weaponInfo = turretWeapon;
	
	turret setModelRemoteTurret();

	if ( turret.rocketTurret )
	{
		turret TurretFireDisable();
		turret.weaponInfo = CONST_REMOTE_TURRET_ROCKET_WEAPINFO;
	}
	
	if ( turret.rocketTurret || turret.energyTurret )
		turret TurretSetBarrelSpinEnabled( false );

	//turret ThermalDrawEnable();
	turret SetTurretModeChangeWait( true );
	turret turret_setInactive();
	turret SetSentryOwner( owner );
	turret SetTurretMinimapVisible( true, turretType );

	if ( turret.is360 )
	{
		turret SetLeftArc( CONST_REMOTE_TURRET_SENTRY_HORZ_ARC_FULL );
		turret SetRightArc( CONST_REMOTE_TURRET_SENTRY_HORZ_ARC_FULL );
	}
	else
	{
		turret SetLeftArc( CONST_REMOTE_TURRET_SENTRY_HORZ_ARC );
		turret SetRightArc( CONST_REMOTE_TURRET_SENTRY_HORZ_ARC );
	}
	turret SetTopArc( CONST_REMOTE_TURRET_SENTRY_TOP_ARC );
	turret SetBottomArc( CONST_REMOTE_TURRET_SENTRY_BOTTOM_ARC );
	turret SetDefaultDropPitch( -89.0 );	// setting this mainly prevents Turret_RestoreDefaultDropPitch() from running
	
	turret thread turret_handleOwnerDisconnect();
	
	owner.turret = turret;

	turret.damageFade = 1.0;
	turret thread turret_incrementDamageFade();
	turret thread sentry_attackTargets();

	return turret;
}

//turret_createBombSquadModel() // self == turret
//{
//	bombSquadModel = spawn( "script_model", self.origin );
//	bombSquadModel.angles = self.angles;
//	bombSquadModel hide();
//
//	team = undefined;
//	if( level.teambased )
//		team = level.otherTeam[ self.team ];
//
//	bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( team, self.owner );
//	bombSquadModel SetModel( level.turretSettings[ self.turretType ].modelBombSquad );
//	bombSquadModel linkTo( self );
//	bombSquadModel SetContents( 0 );
//
//	self waittill ( "death" );
//
//	bombSquadModel delete();
//}

turret_setActive() // self == turret
{
	self endon( "death" );
	self.owner endon( "disconnect" );

	self MakeUnusable();
	self MakeTurretSolid();

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

	owner = self.owner;

	level.turrets[self GetEntityNumber()] = self;

	// destroy any other remote turrets and put this one in the list
	if( IsDefined( owner.remoteTurretList ) )
	{
		foreach( turret in owner.remoteTurretList )
		{
			turret notify( "death" );
		}
	}
	owner.remoteTurretList = [];
	owner.remoteTurretList[0] = self;

	if ( !IsDefined( self.remoteEnt ) )
	{
		self.remoteEnt = Spawn( "script_model", self.origin + ( 0, 0, 1 ) );
		self.remoteEnt SetModel( "tag_origin" );
		self.remoteEnt.owner = owner;
		self.remoteEnt makeGloballyUsableByType( "killstreakRemote", level.turretSettings[ self.turretType ].hintEnter, owner );
	}
	else
	{
		self.remoteEnt enableGloballyUsableByType();
	}

	// show a message to pick up the turret
	owner.using_remote_turret = false;
	owner.pickup_message_deleted = undefined;
	owner.enter_message_deleted = undefined;
	owner thread watchOwnerMessageOnDeath( self );

	if ( level.teamBased )
	{
		self.team = owner.team;
		self setTurretTeam( owner.team );
		self maps\mp\_entityheadicons::setTeamHeadIcon( self.team, ( 0, 0, 65 ), "tag_origin" );
	}
	else
	{
		self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, ( 0, 0, 65 ), "tag_origin" );
	}

	// spawn a trigger so we know if the owner is within range to pick it up
	self.ownerTrigger = Spawn( "trigger_radius", self.origin + ( 0, 0, 1 ), 0, 32, 64 );
	assert( IsDefined( self.ownerTrigger ) );
	
	self.pickupEnt = Spawn( "script_model", self.origin + ( 0, 0, 1 ) );
	self.pickupEnt SetModel( "tag_origin" );
	self.pickupEnt.owner = owner;
	
	
	owner thread player_handleTurretPickup( self );
	owner thread player_handleTurretHints( self );
	if ( self.rippable )
		owner thread player_handleTurretRippable( self );
	
	self thread watchEnterAndExit();
	self thread turret_handleDeath();
	self thread maps\mp\gametypes\_damage::setEntityDamageCallback( self.maxhealth, undefined, ::onTurretDeath, ::turret_ModifyDamage, true );
	self thread turret_timeOut();
	self thread turret_gameEnd();
	self thread turret_watchEMP();
	if ( !self.heavyResistance )
		self thread turret_watchDisabled();
	if ( self.antiIntrusion )
		self thread turret_createAntiIntrusionKillcamEnt();
	//self thread turret_createBombSquadModel();
	self thread turret_handlePitch();
	self thread turret_handleLaser();

	// Target drones (low priority - the auto aim will attack any other enemy first)	
	if( IsDefined( level.isHorde ) && level.isHorde )
		self thread turret_hordeShootDronesAndTurrets();
	/# self thread turret_watchDestroy(); #/
}


turret_ModifyDamage( attacker, weapon, type, damage )
{
	modifiedDamage = self maps\mp\gametypes\_damage::modifyDamage( attacker, weapon, type, damage );
	
	if( IsDefined(self.owner) && (modifiedDamage > 0) )
	{
		self.owner PlayRumbleOnEntity( "damage_heavy" );
		self.owner thread maps\mp\killstreaks\_aerial_utility::playerShowStreakStaticForDamage();
	}
	
	return modifiedDamage;
}

onTurretDeath( attacker, weapon, meansOfDeath, damage )
{
	self notify( "death", attacker, meansOfDeath, weapon );
	
	// assists for marking autosentry with orbitalsupport buddy.
	if( IsDefined( self.tagMarkedBy ) && (self.tagMarkedBy != attacker) )
		self.tagMarkedBy thread maps\mp\killstreaks\_marking_util::playerProcessTaggedAssist( self );
	
	if( IsDefined( level.isHorde ) && level.isHorde )
	{
		if( !IsPlayer( attacker ) )
			attacker = attacker.owner;
	}
	
	self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, meansOfDeath, damage, "sentry_gun_destroyed", undefined, undefined, false );
	self LaserOff();
	//StopFXOnTag( getfx( "sentry_laser_flash" ), self, "tag_flash" );
}

playerShowTurretOverlay( turret ) // self == player
{
	self endon( "disconnect" );
	self endon( "playerHideTurretOverlay" );
	
	wait 0.5;
	
	omnvarValue = 0;
	if( turret.weaponInfo == CONST_REMOTE_TURRET_MG_WEAPINFO )
	{
		omnvarValue = 1;
	}
	else if( turret.weaponInfo == CONST_REMOTE_TURRET_BEAM_WEAPINFO )
	{
		omnvarValue = 2;
	}
	else if ( turret.weaponInfo == CONST_REMOTE_TURRET_ROCKET_WEAPINFO )
	{
		omnvarValue = 3;
	}

	self SetClientOmnvar( "ui_sentry_ammo_type", omnvarValue );
	self SetClientOmnvar( "ui_sentry_toggle", true );
	self maps\mp\killstreaks\_aerial_utility::playerEnableStreakStatic();
}

playerHideTurretOverlay() // self == player
{
	self notify( "playerHideTurretOverlay" );
	
	self SetClientOmnvar( "ui_sentry_toggle", false );
	self maps\mp\killstreaks\_aerial_utility::playerDisableStreakStatic();
}

playerWaittillWeaponSwitchOver( weapon )
{
	self endon( "weapon_change" );
	
	self waittill( "weapon_switch_started", newWeapon );
	if ( weapon != newWeapon )
		return;
	
	while ( self IsSwitchingWeapon() )
		waitframe();
}

startUsingRemoteTurret( leftArc, rightArc, topArc, bottomArc, isCoop ) // self == turret
{
	if ( !IsDefined( isCoop ) )
		isCoop = false;
	
	owner = self.owner;
	
	if ( !isCoop )
	{
		owner PlayerLinkTo( self.remoteEnt );
		owner PlayerLinkedOffsetEnable();
		owner _giveWeapon( level.turretSettings[ self.turretType ].laptopInfo );
		owner SwitchToWeapon( level.turretSettings[ self.turretType ].laptopInfo );
		owner DisableOffhandWeapons();
		owner DisableOffhandSecondaryWeapons();
		owner playerWaittillWeaponSwitchOver( level.turretSettings[ self.turretType ].laptopInfo );
		newWeapon = owner GetCurrentWeapon();
		
		if ( newWeapon != level.turretSettings[ self.turretType ].laptopInfo )
		{
			owner takeKillstreakWeapons( self.turretType );
			owner EnableOffhandWeapons();
			owner EnableOffhandSecondaryWeapons();
			owner Unlink();
			return false;
		}
	}
	
	self.remoteControlled = true;
	
	self sentry_stopAttackingTargets();

	owner thread playerDoRideKillstreak( self, isCoop );
	owner waittill( "initRideKillstreak_complete", success );
	if ( !success )
		return false;
	
	owner playerSaveAngles();
	
	owner setUsingRemote( self.turretType );

	self notify( "remoteControlledUpdate" );
	self.killCamStartTime = GetTime();

	// with the way we do visionsets we need to wait for the clearRideIntro() is done before we set thermal
	owner thread waitSetThermal( 1.0, self );

	if ( getDvarInt( "camera_thirdPerson" ) )
		owner setThirdPersonDOF( false );

	owner PlayerLinkWeaponViewToDelta( self, "tag_player", 0, leftArc, rightArc, topArc, bottomArc, false );
	owner PlayerLinkedSetViewZNear( false );
	owner PlayerLinkedSetUseBaseAngleForViewClamp( true );
	owner RemoteControlTurret( self );

	if ( IsDefined( self.remoteEnt ) )
		self.remoteEnt disableGloballyUsableByType();
	self turret_clearPickupHints();
	owner thread playerShowTurretOverlay( self );
	if ( self.rocketTurret )
		owner thread playerMonitorRocketTurretFire( self );

	if( owner isJuggernaut() )
		owner.juggernautOverlay.alpha = 0;
	
	self thread playLoopSoundToPlayers( "sentry_gun_remote_view_bg", ( 0, 0, 60 ), [owner] );

	return true;
}

waitSetThermal( delay, remoteTurret )
{
	self endon( "disconnect" );
	remoteTurret endon( "death" );

	wait( delay	);

//	self VisionSetThermalForPlayer( game["thermal_vision"], 1.5 );
//	self ThermalVisionOn();
	self ThermalVisionFOFOverlayOn();
}

stopUsingRemoteTurret( showMessage ) // self == turret
{
	if ( !IsDefined( self.remoteControlled ) || !self.remoteControlled )
		return;
	
	self.remoteControlled = undefined;
	self notify( "remoteControlledUpdate" );

	owner = self.owner;
	
	owner takeKillstreakWeapons( self.turretType );
	owner EnableOffhandWeapons();
	owner EnableOffhandSecondaryWeapons();
	
	self thread sentry_attackTargets();
	
	// take the killstreak weapon
	killstreakWeapon = getKillstreakWeapon( level.turretSettings[ self.turretType ].streakName );
	owner maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( killstreakWeapon );
	
	lastWeapon = owner getLastWeapon();
	if ( IsDefined( owner.underWater ) && owner.underWater )
		lastWeapon = owner maps\mp\_utility::get_water_weapon();
	
	owner switchToWeapon( lastWeapon );
	
	if ( !IsDefined( showMessage ) )
		showMessage = true;

	owner Unlink();
	if ( owner isUsingRemote() )
	{
		owner ThermalVisionFOFOverlayOff();
		owner RemoteControlTurretOff( self );
		if ( owner isUsingRemote() )
			owner clearUsingRemote();

		if ( getDvarInt( "camera_thirdPerson" ) )
			owner setThirdPersonDOF( true );
	}
	
	if ( self turret_isStunned() )
		owner StopShellShock();
	
	if( !self turret_isStunned() && showMessage && ( !IsDefined( owner.using_remote_turret_when_died ) || !owner.using_remote_turret_when_died ) )
		self.remoteEnt enableGloballyUsableByType();

	if( owner isJuggernaut() )
		owner.juggernautOverlay.alpha = 1;
	
	owner playerHideTurretOverlay();
	self notify( "stop soundsentry_gun_remote_view_bg" );
	
	if ( IsDefined( owner.killCamEnt ) )
		owner.killCamEnt delete();
	
	self playerRestoreAngles();

	self notify( "exit" );
}

playerDoRideKillstreak( turret, isCoop )
{
	type = "remote_turret";
	if ( isCoop )
		type = "coop";
	
	result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( type );
	
	if ( !IsDefined( self ) )
		return;
	
	if ( result != "success" || turret turret_isStunned() || IsDefined( turret.dead ) )
	{
		if ( result != "disconnect" || turret turret_isStunned() || IsDefined( turret.dead ) )
		{
			self thread playerRemoteKillstreakShowHud();
			turret stopUsingRemoteTurret( !isCoop );
		}

		self notify( "initRideKillstreak_complete", false );
		return;
	}
	
	self notify( "initRideKillstreak_complete", true );
}

watchOwnerMessageOnDeath( turret ) // self == owner
{
	self endon( "disconnect" );
	turret endon( "death" );
	turret endon( "ripped" );

	// we need to reset the lower message when the owner dies, this way the respawn message shows
	self.using_remote_turret_when_died = false;
	while( true )
	{
		if( IsAlive( self ) )
			self waittill( "death" );

		turret.remoteEnt disableGloballyUsableByType();
		turret turret_clearPickupHints();

		if( self.using_remote_turret )
			self.using_remote_turret_when_died = true;
		else
			self.using_remote_turret_when_died = false;

		self waittill( "spawned_player" );
		
		self.using_remote_turret_when_died = false;
		turret.remoteEnt enableGloballyUsableByType();
	}
}

watchEnterAndExit() // self == turret
{
	self endon( "death" );
	self endon( "carried" );
	level endon( "game_ended" );

	owner = self.owner;
	
	thread watchEnterAndExitInput();
	
	while( true )
	{
		if ( owner player_shouldDisableRemoteEnter( self ) )
		{
			if ( !IsDefined( owner.enter_message_deleted ) || !owner.enter_message_deleted )
			{
				owner.enter_message_deleted = true;
				self.remoteEnt disableGloballyUsableByType();
			}
		}
		else
		{
			if( IsDefined( owner.enter_message_deleted ) && owner.enter_message_deleted )
			{
				self.remoteEnt enableGloballyUsableByType();
				owner.enter_message_deleted = false;
			}
		}

		waitframe();
	}
}

player_shouldDisableRemoteEnter( turret )
{
	currentWeapon = self GetCurrentWeapon();
	return ( turret turret_isStunned() || 
		    self player_isUsingKillstreak( turret ) ||
		    ( IsDefined( self.underWater ) && self.underWater ) || 
		    self.using_remote_turret || 
		    currentWeapon == "none" || 
		    self IsTouching( turret.ownerTrigger ) ||
		    ( self IsLinked() && !self.using_remote_turret ) || 
		    ( IsDefined( self.empGrenaded ) && self.empGrenaded ) 
		   );
}

watchEnterAndExitInput() // self == turret
{
	self endon( "death" );
	self endon( "carried" );
	level endon( "game_ended" );

	owner = self.owner;
	
	topArc = CONST_REMOTE_TURRET_SENTRY_TOP_ARC;
	bottomArc = CONST_REMOTE_TURRET_SENTRY_BOTTOM_ARC;
	leftArc = CONST_REMOTE_TURRET_SENTRY_HORZ_ARC;
	rightArc = CONST_REMOTE_TURRET_SENTRY_HORZ_ARC;
	
	if ( self.is360 )
	{
		leftArc = CONST_REMOTE_TURRET_SENTRY_HORZ_ARC_FULL;
		rightArc = CONST_REMOTE_TURRET_SENTRY_HORZ_ARC_FULL;
	}
	
	while( true )
	{
		self waittillRemoteTurretUsedReturn();

		owner.using_remote_turret = true;
		success = self startUsingRemoteTurret( leftArc, rightArc, topArc, bottomArc );
		
		if ( success )
		{
			wait 2.0;
			
			self waittillRemoteTurretLeaveReturn();
			
			owner.using_remote_turret = false;
			self stopUsingRemoteTurret();
	
			wait 2.0; // these waits are to keep you from going in and out by holding X too long
		}
		else
		{
			owner.using_remote_turret = false;
		}
	}
}

waittillRemoteTurretUsedReturn() // self == turret
{
	owner = self.owner;
	
	while ( true )
	{
		self.remoteEnt waittill( "trigger" );
		
		if ( owner playerCanUseTurret( self ) )
			return;
	}
}

waittillRemoteTurretLeaveReturn() //self == turret
{
	owner = self.owner;
	
	while ( true )
	{
		useHold = 0;
		while ( owner UseButtonPressed() )
		{
			useHold += 0.05;
			if ( useHold > 1 && owner playerCanUseTurret( self ) )
				return;
			waitframe();
		}
		
		waitframe();
	}
}

playerCanUseTurret( turret )
{
	if ( self FragButtonPressed() || IsDefined( self.throwingGrenade ) || self SecondaryOffhandButtonPressed() )
		return false;
	
	if ( self IsUsingTurret() || !self IsOnGround() )
		return false;
	
	if ( IsDefined( turret.ownerTrigger ) && self IsTouching( turret.ownerTrigger ) )
		return false;
	
	if ( IsDefined( self.empGrenaded ) && self.empGrenaded )
		return false;
	
	if( IsDefined( self.isCarrying ) && self.isCarrying )
		return false;

	if( IsDefined( self.isCapturingCrate ) && self.isCapturingCrate )
		return false;

	if( !IsAlive( self ) )
		return false;

	if( !self.using_remote_turret && self isUsingRemote() )
		return false;
	
	// bail out, if the owner is already linked to another entity
	if ( self IsLinked() && !self.using_remote_turret )
		return false;
	
	return true;
}

player_isUsingKillstreak( turret )
{
	currentWeapon = self GetCurrentWeapon();
	return ( self isJuggernaut() || self isUsingRemote() || self isInRemoteTransition() ||
		( isKillstreakWeapon( currentWeapon ) &&
		currentWeapon != "killstreak_remote_turret_mp" &&
		currentWeapon != CONST_REMOTE_TURRET_MG_WEAPINFO && 
		currentWeapon != CONST_REMOTE_TURRET_BEAM_WEAPINFO &&
		currentWeapon != level.turretSettings[ turret.turretType ].laptopInfo && 
		currentWeapon != "none" &&
		currentWeapon != "turretheadmg_mp" && 
		currentWeapon != "turretheadenergy_mp" && 
		currentWeapon != "turretheadrocket_mp" )
	);
}

player_handleTurretHints( turret ) // self == owner( player )
{
	self endon( "disconnect" );
	level endon( "game_ended" );
	turret endon( "death" );
	
	if( IsBot(self) )
		return;
	
	if( !IsDefined( turret.ownerTrigger ) || !IsDefined( turret.pickupEnt ) )
		return;
	
	turret.pickupEnt endon( "death" );

	while( true )
	{
		if( self player_shouldClearTurretPickupHints( turret ) )
		{
			if( !IsDefined( self.pickup_message_deleted ) || !self.pickup_message_deleted )
			{
				self.pickup_message_deleted = true;
				turret turret_clearPickupHints();
			}
		}
		else
		{
			if( IsDefined( self.pickup_message_deleted ) && self.pickup_message_deleted )
			{
				turret thread turret_setPickupHints();
				self.pickup_message_deleted = false;
			}
		}
		
		waitframe();
	}

}

player_shouldClearTurretPickupHints( turret )
{
	currentWeapon = self GetCurrentWeapon();
	return ( turret turret_isStunned() || 
		    self player_isUsingKillstreak( turret ) || 
		    ( IsDefined( self.underWater ) && self.underWater ) || 
		    self.using_remote_turret || 
		    currentWeapon == "none" || 
		    !self IsTouching( turret.ownerTrigger ) ||
		    !isReallyAlive( self ) ||
		    !self IsOnGround() || 
		    IsDefined( turret.carriedBy )
		   );
}

player_handleTurretPickup( turret ) // self == owner( player )
{
	self endon( "disconnect" );
	level endon( "game_ended" );
	turret endon( "death" );

	// defensive bot hack because they keep picking it up right after they place it
	if( IsBot(self) )
		return;
	
	if( !IsDefined( turret.ownerTrigger ) || !IsDefined( turret.pickupEnt ) )
		return;
	
	turret.pickupEnt endon( "death" );

	buttonTime = 0;
	while( true )
	{
		// need to make sure we can't pick up the remote turret while already in a killstreak (like ac130)
		if ( player_isUsingKillstreak( turret ) )
		{
			waitframe();
			continue;
		}

		if( !self IsTouching( turret.ownerTrigger ) )
		{
			waitframe();
			continue;
		}

		if( isReallyAlive( self ) && 
			self IsTouching( turret.ownerTrigger ) && 
			!IsDefined( turret.carriedBy ) &&
			self IsOnGround() )
		{
			
			// entering and exiting the remote turret
			timeUsed = 0;
			while ( self UseButtonPressed() )
			{
				if( !isReallyAlive( self ) )
					break;
				
				if ( !self IsTouching( turret.ownerTrigger ) )
					break;
				
				if ( self FragButtonPressed() || self SecondaryOffhandButtonPressed() || IsDefined( self.throwingGrenade ) )
					break;
				
				if ( self IsLinked() || !self IsOnGround() || self IsUsingTurret() || self isUsingRemote() )
					break;
				
				if ( IsDefined( self.isCarrying ) && self.isCarrying )
					break;
	
				if ( IsDefined( self.isCapturingCrate ) && self.isCapturingCrate )
					break;
				
				if ( IsDefined( self.empGrenaded ) && self.empGrenaded )
					break;
				
				if( IsDefined( self.using_remote_turret ) && self.using_remote_turret )
					break;
				
				if ( IsDefined( self.ball_carried ) )
					break;
	
				timeUsed += 0.05;
				if( timeUsed > 0.75 )
				{
					turret PlaySound( "sentry_gun_pick_up" );
					turret SetMode( level.turretSettings[ turret.turretType ].sentryModeOff );
					turret sentry_stopAttackingTargets();
					self thread setCarryingTurret( turret, false );
					turret turret_clearPickupHints();
					self.remoteTurretList = undefined;
					turret.pickupEnt Delete();
					turret.ownerTrigger delete();
					return;
				}
				waitframe();
			}
		}
		waitframe();
	}
}

player_handleTurretRippable( turret ) // self == owner (player)
{
	self endon( "disconnect" );
	level endon( "game_ended" );
	turret endon( "death" );

	// defensive bot hack because they keep picking it up right after they place it
	if( IsBot(self) )
		return;
	
	if( !IsDefined( turret.ownerTrigger ) || !IsDefined( turret.pickupEnt ) )
		return;
	
	turret.pickupEnt endon( "death" );

	buttonTime = 0;
	while( true )
	{
		// need to make sure we can't pick up the remote turret while already in a killstreak (like ac130)
		if( player_isUsingKillstreak( turret ) )
		{
			wait( 0.05 );
			continue;
		}
		
		if ( self maps\mp\killstreaks\_rippedturret::playerHasTurretHeadWeapon() )
		{
			waitframe();
			continue;
		}

		// since we can pick this back up, if we're not in the trigger clear the message
		if( !self IsTouching( turret.ownerTrigger ) )
		{
			wait( 0.05 );
			continue;
		}

		if( isReallyAlive( self ) && 
			self IsTouching( turret.ownerTrigger ) && 
			!IsDefined( turret.carriedBy ) &&
			self IsOnGround() )
		{
			if ( self UseButtonPressed() )
			{
				if( IsDefined( self.using_remote_turret ) && self.using_remote_turret )
					continue;

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

				if ( buttonTime >= 0.5 )
					continue;

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

				if ( buttonTime >= 0.5 )
					continue;

				if ( !isReallyAlive( self ) )
					continue;

				if( IsDefined( self.using_remote_turret ) && self.using_remote_turret )
					continue;
				
				if ( IsDefined( self.ball_carried ) )
					continue;

				turret SetMode( level.turretSettings[ turret.turretType ].sentryModeOff );
				turret sentry_stopAttackingTargets();
				self thread setRipOffTurretHead( turret );
				turret turret_clearPickupHints();
				self.remoteTurretList = undefined;
				turret.pickupEnt Delete();
				turret.ownerTrigger Delete();
				return;
			}
		}
		wait( 0.05 );
	}
}

turret_blinky_light() // self == turret
{
	self endon( "death" );
	self endon( "carried" );

	while ( true )
	{
		//PlayLoopedFX( getfx( "antenna_light_mp" ), 1.0, self GetTagOrigin( "tag_fx" ) );
		PlayFXOnTag( getfx( "antenna_light_mp" ), self, "tag_fx" );
		wait( 1.0 );
		StopFXOnTag( getfx( "antenna_light_mp" ), self, "tag_fx" );
	}
}

turret_setInactive() // self == turret
{
	self SetMode( level.turretSettings[ self.turretType ].sentryModeOff );
	self sentry_stopAttackingTargets();

	if ( level.teamBased )
		self maps\mp\_entityheadicons::setTeamHeadIcon( "none", ( 0, 0, 0 ) );
	else if ( isDefined( self.owner ) )
		self maps\mp\_entityheadicons::setPlayerHeadIcon( undefined, ( 0, 0, 0 ) );

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

	owner = self.owner;

	level.turrets[self GetEntityNumber()] = undefined;
	
	if ( IsDefined( self.remoteEnt ) )
		self.remoteEnt disableGloballyUsableByType();

	if( IsDefined( owner.using_remote_turret ) && owner.using_remote_turret )
	{
		owner ThermalVisionOff();
		owner ThermalVisionFOFOverlayOff();
		owner RemoteControlTurretOff( self );
		owner unlink();

		lastWeapon = owner getLastWeapon();
		if ( IsDefined( owner.underWater ) && owner.underWater )
			lastWeapon = owner maps\mp\_utility::get_water_weapon();
	
		owner switchToWeapon( lastWeapon );
	
		if ( owner isUsingRemote() )
			owner clearUsingRemote();

		if ( getDvarInt( "camera_thirdPerson" ) )
			owner setThirdPersonDOF( true );

		// just in case the turret is killed before you can get in it, the initRideKillstreak() does _disableUsability() and we unfortunately kill the thread on death
	
		if( IsDefined( owner.disabledUsability ) && owner.disabledUsability )
		{
			// if we go to last stand while using the remote turret, we don't want usability turned back on
			if ( IsDefined( level.isHorde ) && level.isHorde && IsDefined( owner.lastStand ) && owner.lastStand ) 
			{
				// make sure we get rid of the addition to disabledUsability from the call in initRideKillstreak without allowing enable to be called
				if (owner.disabledUsability > 1)
					owner.disabledUsability--;		
			}
			else
				owner _enableUsability();
		}

		owner takeKillstreakWeapons( self.turretType );

		// turn juggernaut overlay back on
		if( owner isJuggernaut() )
			owner.juggernautOverlay.alpha = 1;
	}
}

turret_handleOwnerDisconnect() // self == turret
{
	self endon ( "death" );
	level endon ( "game_ended" );

	self notify ( "turret_handleOwner" );
	self endon ( "turret_handleOwner" );

	self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );

	self notify( "death" );
}

turret_gameEnd()
{
	self endon( "death" );
	level waittill( "game_ended" );
	if ( isDefined( self.owner ) )
	{
		self.owner playerHideTurretOverlay();
	}
}

turret_timeOut() // self == turret
{
	self endon( "death" );
	level endon ( "game_ended" );
	
	if ( IsDefined( self.timeoutStarted ) )
		return;
	
	self.timeoutStarted = true;

	lifeSpan = level.turretSettings[ self.turretType ].timeOut;
	self.owner SetClientOmnvar( "ui_sentry_lifespan", lifeSpan );
	
/#
	if ( GetDvar( "scr_remote_turret_timeout", "0" ) != "0" )
		lifeSpan = GetDvarFloat( "scr_remote_turret_timeout" );
#/
	while ( lifeSpan )
	{
		wait ( 1.0 );
		maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();

		if ( !IsDefined( self.carriedBy ) )
			lifeSpan = max( 0, lifeSpan - 1.0 );
	}

	if ( isDefined( self.owner ) )
	{
		self.owner playerHideTurretOverlay();
	}

	self notify ( "death" );
}

turret_handleDeath() // self == turret
{
	self endon( "carried" );

	entNum = self GetEntityNumber();

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

	self waittill ( "death", attacker, meansOfDeath, weapon );
	
	self ClearTargetEntity();
	self turret_deathSounds( attacker, weapon );
	self.damagecallback = undefined;
	self SetCanDamage( false );
	self SetDamageCallbackOn( false );
	self FreeEntitySentient();
	self LaserOff();
	self.dead = true;

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

	// this handles cases of deletion
	if ( !isDefined( self ) )
		return;

	self turret_clearPickupHints();
	self turret_setInactive();
	self setDefaultDropPitch( 35 );
	self SetSentryOwner( undefined );
	self SetTurretMinimapVisible( false );

	if ( IsDefined( self.remoteEnt ) )
		self.remoteEnt makeGloballyUnusableByType();
	
	owner = self.owner;
	if( IsDefined( owner ) )
	{
		self stopUsingRemoteTurret();

		owner.using_remote_turret = false;
		owner.turret = undefined;
		
		owner restorePerks();
		owner playerRemoveNotifyCommands();
		if( owner GetCurrentWeapon() == "none" )
		{
			lastWeapon = owner getLastWeapon();
			if ( IsDefined( owner.underWater ) && owner.underWater )
				lastWeapon = owner maps\mp\_utility::get_water_weapon();
	
			owner switchToWeapon( lastWeapon );
		}
	}

	// TODO: get sound
	self playSound( "sentry_gun_death_exp" );

	if ( !IsDefined( self.quick_death ) || !self.quick_death )
	{		
		PlayFXOnTag( getFx( "sentry_explode_mp" ), self, "TAG_AIM_PIVOT" );
		wait ( 1.5 );		
		//self playSound( "sentry_explode_smoke" );
		for ( smokeTime = 8; smokeTime > 0; smokeTime -= 0.4 )
		{
			PlayFXOnTag( getFx( "sentry_smoke_mp" ), self, "tag_aim" );
			wait ( 0.4 );
		}
	}
	
	self notify( "deleting" );

	if( IsDefined( self.target_ent ) )
		self.target_ent delete();
	
	if( IsDefined( self.ownerTrigger ) )
		self.ownerTrigger delete();
	
	if ( IsDefined( self.pickupEnt ) )
		self.pickupEnt Delete();
	
	if ( IsDefined( self.remoteEnt ) )
		self.remoteEnt Delete();
	
	if ( IsDefined( self.rocketMuzzleFlashEnt ) )
		self.rocketMuzzleFlashEnt Delete();
	
	deathSoundsAndFX();
	
	self delete();
}

turret_deathSounds( attacker, weapon )
{
	if ( IsDefined( self.owner ) && IsDefined( attacker ) && self.owner != attacker )
		self.owner thread leaderDialogOnPlayer( "ks_sentrygun_destroyed", undefined, undefined, self.origin );
}

turret_incrementDamageFade() // self == turret
{
	self endon( "death" );
	level endon( "game_ended" );

	// this is for the ui
	damaged = false;
	while( true )
	{
		if( self.damageFade < 1.0 )
		{
			//self.owner SetClientOmnvar( "remoteTurretDamageFade", self.damageFade );
			self.damageFade += 0.1;
			damaged = true;
		}
		else
		{
			if( damaged )
			{
				self.damageFade = 1.0;
				//self.owner SetClientOmnvar( "remoteTurretDamageFade", self.damageFade );			
				damaged = false;
			}
		}
		wait( 0.1 );
	}
}

turret_setPickupHints() // self == turret
{
	self notify( "turretClearPickupHints" );
	self endon( "turretClearPickupHints" );

	Assert( IsDefined( self.pickupEnt ) );
	
	self.pickupEnt MakeUsable();
	self.pickupEnt SetHintString( level.turretSettings[ self.turretType ].hintPickUp );
	self.pickupEnt SetCursorHint( "HINT_NOICON" );
	self.pickupEnt SetHintStringVisibleOnlyToOwner( true );
	
	if ( self.rippable )
	{
		while ( true )
		{
			hintStringSet = false;
			
			if ( !hintStringSet && IsDefined( self.owner ) && !(self.owner maps\mp\killstreaks\_rippedturret::playerHasTurretHeadWeapon()) )
			{
				self.pickupEnt SetSecondaryHintString( level.turretSettings[ self.turretType ].hintRipOff );
				hintStringSet = true;
			}
			else if ( hintStringSet )
			{
				self.pickupEnt SetSecondaryHintString( "" );
				hintStringSet = false;
			}
			
			waitframe();
		}
	}
}

turret_clearPickupHints() // self == turret
{
	self notify( "turretClearPickupHints" );
	
	if ( !IsDefined( self.pickupEnt ) )
		return;
	
	self.pickupEnt MakeUnusable();
	self.pickupEnt SetHintString( "" );
	self.pickupEnt SetSecondaryHintString( "" );
	self.pickupEnt SetHintStringVisibleOnlyToOwner( false );
}

/* ============================
	Sentry Logic Functions
   ============================ */
// TODO: if we keep the remote turret we should try to merge this with _autosentry.gsc

sentry_stopAttackingTargets()
{
	self notify( "sentry_stop" );
}
   
sentry_attackTargets( shootFunc )
{
	if ( !self.isSentry )
		return;
	
	self endon( "sentry_stop" );
	self endon( "death" );
	level endon( "game_ended" );
	self notify( "sentry_start" );

	self.momentum = 0;
	self.heatLevel = 0;
	self.overheated = false;
	
	if ( !self.rocketTurret )
		self thread sentry_heatMonitor( CONST_REMOTE_TURRET_MG_WEAPINFO, CONST_REMOTE_TURRET_SENTRY_OVERHEATTIME, CONST_REMOTE_TURRET_SENTRY_COOLDOWNTIME );
	self SetMode( level.turretSettings[ "mg_turret" ].sentryModeOn );
	self.fireReadyTime = GetTime();
	
	for ( ;; )
	{
		self waittill_either( "turretstatechange", "cooled" );

		if ( self isFiringTurret() )
			self thread turret_startShooting( shootFunc );
		else
			self thread turret_stopShooting();
	}
}

turret_startShooting( shootFunc )
{
	if ( self.rocketTurret )
	{
		self thread turret_fireRockets();
	}
	else
	{
		self thread sentry_burstFireStart( shootFunc );
	}
}

turret_stopShooting()
{
	if ( self.rocketTurret )
	{
		self thread turret_stopRockets();
	}
	else
	{
		self sentry_spinDown();
		self thread sentry_burstFireStop();
	}
}

playerMonitorRocketTurretFire( turret ) // self = player
{
	self endon ( "disconnect" );
	level endon ( "game_ended" );
	turret endon ( "death" );
	turret endon ( "sentry_start" );
	turret endon ( "exit" );
		
	turret.fireReadyTime = GetTime();
		
	while ( true )
	{	
		self waittill( "turret_fire" );
		maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
		if( IsDefined( level.hostMigrationTimer ) )
			continue;
		
		if ( GetTime() >= turret.fireReadyTime )
			turret thread turret_fireRocket( false );
	}
}

sentry_targetLockSound()
{
	self endon ( "death" );
	self endon( "sentry_stop" );
	
	self playSound( "sentry_gun_beep" );
	wait ( 0.1 );
	self playSound( "sentry_gun_beep" );
	wait ( 0.1 );
	self playSound( "sentry_gun_beep" );
}

sentry_spinUp()
{
	self endon( "death" );
	self endon( "sentry_stop" );
	
	self thread sentry_targetLockSound();
	
	while ( self.momentum < CONST_REMOTE_TURRET_SENTRY_SPINUPTIME )
	{
		self.momentum += 0.1;
		
		wait ( 0.1 );
	}
}

sentry_spinDown()
{
	self.momentum = 0;
}


sentry_burstFireStart( shootFunc )
{
	self endon( "death" );
	self endon( "sentry_stop" );
	self endon( "stop_shooting" );

	level endon( "game_ended" );

	self sentry_spinUp();

	fireTime = weaponFireTime( CONST_REMOTE_TURRET_MG_WEAPINFO );
	minShots = CONST_REMOTE_TURRET_SENTRY_BURSTMIN;
	maxShots = CONST_REMOTE_TURRET_SENTRY_BURSTMAX;
	minPause = CONST_REMOTE_TURRET_SENTRY_PAUSEMIN;
	maxPause = CONST_REMOTE_TURRET_SENTRY_PAUSEMAX;

	for ( ;; )
	{		
		numShots = randomIntRange( minShots, maxShots + 1 );
		
		for ( i = 0; i < numShots && !self.overheated; i++ )
		{
			if ( IsDefined( shootFunc ) )
				self [[ shootFunc ]]();
			else
				self shootTurret();
			self.heatLevel += fireTime;
			wait ( fireTime );
		}
		
		wait ( randomFloatRange( minPause, maxPause ) );
	}
}

turret_stopRockets()
{
	self notify( "stop_shooting" );
}

turret_fireRockets()
{
	self endon( "death" );
	self endon( "sentry_stop" );
	self endon( "stop_shooting" );

	level endon( "game_ended" );
	
	self.fireReadyTime = GetTime();

	while ( true )
	{
		if ( GetTime() >= self.fireReadyTime )
			self thread turret_fireRocket( true );
		
		waitframe();
	}
}

turret_fireRocket( isAutoTurret )
{
	level endon( "game_ended" );

	start = self GetTagOrigin( "tag_flash" );
	dir = AnglesToForward( self GetTagAngles( "tag_flash" ) );
	end = start + ( dir * 10000 );
	start = start + ( dir * 10 );
	trace = BulletTrace( start, end, true );
	
	entHit = trace["entity"];
	
	hitEnemyPlayer = 0;
	
	if( IsDefined( level.isHorde ) && level.isHorde )
		hitEnemyPlayer = ( IsDefined( entHit ) && IsDefined( entHit.team ) && ( self.team != entHit.team ) );
	else
		hitEnemyPlayer = ( IsDefined( entHit ) && IsPlayer( entHit ) && !IsAlliedSentient(self.owner, entHit) );
	
	if ( !hitEnemyPlayer && isAutoTurret )
		return;
	
	self playRumbleOnEntity( "damage_heavy" );
	rocket = MagicBullet( CONST_REMOTE_TURRET_ROCKET_WEAPINFO, start, end, self.owner );
	
	if ( isAutoTurret )
		reloadTime = 2500;
	else
		reloadTime = 1250;
	
	self.fireReadyTime = GetTime() + reloadTime;
	
	if ( !isAutoTurret )
	{
		PlayFXOnTagForClients( getfx( "sentry_rocket_muzzleflash_view" ), self, "tag_flash", self.owner );
		if ( !IsDefined( self.rocketMuzzleFlashEnt ) )
			self.rocketMuzzleFlashEnt = spawnMuzzleFlashEnt( self, "tag_flash", self.owner );
		PlayFXOnTag( getfx( "sentry_rocket_muzzleflash_wv" ), self.rocketMuzzleFlashEnt, "tag_origin" );
	}
	else
	{
		PlayFXOnTag( getfx( "sentry_rocket_muzzleflash_wv" ), self, "tag_flash" );
	}
}

spawnMuzzleFlashEnt( parent, tagname, hideFromPlayer )
{
	muzzleEnt = Spawn( "script_model", ( 0, 0, 0 ) );
	muzzleEnt SetModel( "tag_origin" );
	muzzleEnt LinkTo( parent, tagname, ( 0, 0, 0 ), ( 0, 0, 0 ) );
	muzzleEnt Hide();
	foreach ( player in level.players )
	{
		if ( player != hideFromPlayer )
			muzzleEnt ShowToPlayer( player );
	}
	thread onPlayerConnectMuzzleFlashEnt( muzzleEnt );
	return muzzleEnt;
}

onPlayerConnectMuzzleFlashEnt( muzzleEnt )
{
	muzzleEnt endon( "death" );
	
	while ( true )
	{
		level waittill( "connected", player );
		
		self thread onPlayerSpawnedMuzzleFlashEnt( muzzleEnt, player );
	}
}

onPlayerSpawnedMuzzleFlashEnt( muzzleEnt, player )
{
	muzzleEnt endon( "death" );
	player endon( "disconnect" );
	
	player waittill( "spawned_player" );
	
	muzzleEnt ShowToPlayer( player );
}

sentry_burstFireStop()
{
	self notify( "stop_shooting" );
}

sentry_heatMonitor( weapInfo, overheatTime, overheatCoolDown )
{
	self endon ( "death" );
	self endon( "sentry_stop" );

	fireTime = weaponFireTime( weapInfo );

	lastHeatLevel = 0;
	lastFxTime = 0;
	
	for ( ;; )
	{
		if ( self.heatLevel != lastHeatLevel )
			wait ( fireTime );
		else
			self.heatLevel = max( 0, self.heatLevel - 0.05 );

		if ( self.heatLevel > overheatTime )
		{
			self.overheated = true;
			self TurretSetBarrelSpinEnabled( false );
			self thread playHeatFX();
			switch( self.turretType )
			{
			case "mg_turret":
				playFxOnTag( getFx( "sentry_smoke_mp" ), self, "tag_aim" );
				//self thread PlaySmokeFX();
				break;
			default:
				break;
			}
			
			while ( self.heatLevel )
			{
				self.heatLevel = max( 0, self.heatLevel - overheatCoolDown );	
				wait ( 0.1 );
			}

			self TurretSetBarrelSpinEnabled( true );
			self.overheated = false;
			self notify( "not_overheated" );
		}

		lastHeatLevel = self.heatLevel;
		wait ( 0.05 );
	}
}

playHeatFX()
{
	self endon( "death" );
	self endon( "sentry_stop" );
	self endon( "not_overheated" );
	level endon ( "game_ended" );
	
	self notify( "playing_heat_fx" );
	self endon( "playing_heat_fx" );
	
	for( ;; )
	{
		playFxOnTag( getFx( "sentry_overheat_mp" ), self, "tag_flash" );
	
		wait( CONST_REMOTE_TURRET_SENTRY_FXTIME );
	}
}

turret_watchEMP()
{
	self endon( "carried" );
	self endon( "death" );
	level endon( "game_ended" );

	self waittill( "emp_damage" );
	
	self notify( "death" );
}

turret_watchDisabled()
{
	self endon( "carried" );
	self endon( "death" );
	level endon( "game_ended" );

	while( true )
	{
		// this handles any flash or concussion damage
		self waittill( "concussed" );
		
		duration = 4;

		PlayFXOnTag( getfx( "sentry_stunned_mp" ), self, "tag_aim" );
		self notify( "stunned" );
		self.stunned = true;

		if ( self.isSentry )
		{
			self SetDefaultDropPitch( 35 );
			self SetMode( level.turretSettings[ self.turretType ].sentryModeOff );
		}
		
		if ( IsDefined( self.remoteControlled ) && self.remoteControlled )
			self stopUsingRemoteTurret();

		maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( duration );
		
		StopFXOnTag( getfx( "sentry_stunned_mp" ), self, "tag_aim" );

		if ( self.isSentry )
		{
			self SetDefaultDropPitch( 0 );
			self SetMode( level.turretSettings[ self.turretType ].sentryModeOn );
		}
		self.stunned = false;
		self notify( "stunnedDone" );
	}
}

turret_isStunned()
{
	return ( IsDefined( self.stunned ) && self.stunned );
}

turret_createAntiIntrusionKillcamEnt()
{
	ent = Spawn( "script_model", self.origin + ( 0, 0, 60 ) );
	self.killCamEnt = ent;
	
	self waittill_any( "death", "carried" );
	
	wait 3;
	
	ent Delete();
}

turret_handleLaser()
{
	self endon( "death" );
	
	if ( !self.isSentry )
		return;
	
	self LaserOn( "mp_sentry_turret" );
	//PlayFXOnTag( getfx( "sentry_laser_flash" ), self, "tag_flash" );
	
	self waittill( "carried" );
	
	self LaserOff();
	//StopFXOnTag( getfx( "sentry_laser_flash" ), self, "tag_flash" );
}

turret_handlePitch()
{
	self endon( "carried" );
	self endon( "death" );
	
	if ( self.isSentry )
	{
		self SetDefaultDropPitch( 0 );
		return;
	}
	else
	{
		self SetDefaultDropPitch( 35 );
	}
	
	while ( true )
	{
		self waittill( "remoteControlledUpdate" );
		
		if ( IsDefined( self.remoteControlled ) && self.remoteControlled )
			self SetDefaultDropPitch( 0 );
		else
			self SetDefaultDropPitch( 35 );
	}
}

// Target the closest drone or turret
// The aiming function will automatic ignore this target if a regular enemy is nearby.
turret_hordeShootDronesAndTurrets() // self == turret
{
	self endon( "death" );
	
	CONST_TURRET_MIDHEIGHT = (0,0,40);
	
	while( true )
	{
		minDistance = 5000000; // A target within 2,236 units
		closestDrone = undefined;
			
		foreach( drone in level.flying_attack_drones )
		{
			dist = DistanceSquared( self.origin, drone.origin );
			if( dist < minDistance )
			{
				if( SightTracePassed( self.origin + CONST_TURRET_MIDHEIGHT, drone.origin, false, undefined ) )
				{
					minDistance = dist;
					closestDrone = drone;
				}
			}
		}
		
		closestSentry = undefined;
		foreach( sentry in level.hordeSentryArray )
		{
			dist = DistanceSquared( self.origin, sentry.origin );
			if( dist < minDistance )
			{
				if( SightTracePassed( self.origin + CONST_TURRET_MIDHEIGHT, sentry.origin + CONST_TURRET_MIDHEIGHT, false, undefined ) )
				{
					minDistance = dist;
					closestSentry = sentry;
				}
			}
		}
		
		// If there's a sentry, that means it's closer
		if( IsDefined( closestSentry ) )
			self SetTargetEntity( closestSentry, CONST_TURRET_MIDHEIGHT); // Don't aim at the origin that's on the ground...aim above it
		else if( IsDefined( closestDrone ) )
			self SetTargetEntity( closestDrone );
		waitframe;
	}
}

playerAddNotifyCommands()
{
	self NotifyOnPlayerCommand( "turret_fire" , "+attack" );
	self NotifyOnPlayerCommand( "turret_fire" , "+attack_akimbo_accessible" );
	self NotifyOnPlayerCommand( "place_turret", "+attack" );
	self NotifyOnPlayerCommand( "place_turret", "+attack_akimbo_accessible" ); // support accessibility control scheme
	if ( !IsBot( self ) )
	{
		self NotifyOnPlayerCommand( "cancel_turret", "weapnext" );
		self NotifyOnPlayerCommand( "cancel_turret", "+actionslot 4" );
		if ( !level.console )
		{
			self NotifyOnPlayerCommand( "cancel_turret", "+actionslot 5" );
			self NotifyOnPlayerCommand( "cancel_turret", "+actionslot 6" );
			self NotifyOnPlayerCommand( "cancel_turret", "+actionslot 7" );
			self NotifyOnPlayerCommand( "cancel_turret", "+actionslot 8" );
		}
	}
}

playerRemoveNotifyCommands()
{
	self NotifyOnPlayerCommandRemove( "turret_fire", "+attack" );
	self NotifyOnPlayerCommandRemove( "turret_fire" , "+attack_akimbo_accessible" );
	self NotifyOnPlayerCommandRemove( "place_turret", "+attack" );
	self NotifyOnPlayerCommandRemove( "place_turret", "+attack_akimbo_accessible" ); // support accessibility control scheme
	if ( !IsBot( self ) )
	{
		self NotifyOnPlayerCommandRemove( "cancel_turret", "+actionslot 4" );
		if ( !level.console )
		{
			self NotifyOnPlayerCommandRemove( "cancel_turret", "weapnext" );
			self NotifyOnPlayerCommandRemove( "cancel_turret", "+actionslot 5" );
			self NotifyOnPlayerCommandRemove( "cancel_turret", "+actionslot 6" );
			self NotifyOnPlayerCommandRemove( "cancel_turret", "+actionslot 7" );
			self NotifyOnPlayerCommandRemove( "cancel_turret", "+actionslot 8" );
		}
	}
}

/#
turret_watchDestroy()
{
	self endon( "death" );
	self endon( "carried" );
	level endon( "game_ended" );
	
	while ( true )
	{
		if ( GetDvarInt( "scr_remote_turret_destroy", 0 ) != 0 )
			break;
		
		waitframe();
	}
	
	SetDvar( "scr_remote_turret_destroy", 0 );
		
	self notify( "death" );
}
#/