#include common_scripts\utility;
#include animscripts\utility;
#include maps\_utility_code;

/*
=============
///ScriptDocBegin
"Name: set_HudOutline()"
"Summary:  Creates a colored outline around an ent."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <type>: String specifying the type of outline. Use 'friendly', 'enemy', 'objective', or 'neutral' "
"MandatoryArg: <depth_enable>: Bool specifying whether or not the outline should enable depth testing"
"Example: enemy set_HudOutline( "enemy", false );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

set_HudOutline( sType, depth_enable )
{
	num = undefined;
	
	AssertEx( isdefined( sType) && IsString( sType ), "set_HudOutline() needs a string! Use 'friendly', 'enemy', 'objective', or 'neutral' " );
	AssertEx( isdefined( depth_enable ), "set_HudOutline() needs to specify whether or not depth testing should be enabled" );

	sType = ToLower( sType );
	array[ "friendly" ] = 3;
	array[ "enemy" ] = 4;
	array[ "objective" ] = 5;
	array[ "neutral" ] = 0;
	
	AssertEx( IsDefineD( array[ sType ] ), "set_hudOutline() called with invalid type. Use 'friendly', 'enemy', 'objective', or 'neutral' " );
	
	num = array[ sType ];
		
	//0 white 1 red 2 green 3 cyan 4 orange 5 yellow 6 blue
	self HudOutlineEnable( num, depth_enable );			
}

/*
=============
///ScriptDocBegin
"Name: is_coop()"
"Summary: returns true if co-op is in use"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_coop()
{
	if ( IsSplitScreen() || ( GetDvar( "coop" ) == "1" ) )
	{
		AssertEx( IsDefined( level.player2 ), "In co-op mode but level.player2 is undefined. IsSplitScreen=" + IsSplitScreen() + " coop dvar=" + GetDvar( "coop" ) );
		return true;
	}

	return false;
}

/*
=============
///ScriptDocBegin
"Name: is_coop_online()"
"Summary: Returns true if co-op is in use and the game is online."
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_coop_online()
{
	if ( issplitscreen() )
		return false;

	if ( !is_coop() )
		return false;

	return true;
}

/*
=============
///ScriptDocBegin
"Name: is_player_down( <player> )"
"Summary: Returns true if a player is in last stand mode or dead."
"Module: Entity"
"CallOn: A player"
"MandatoryArg: <player>: The player you want to check."
"Example: return is_player_down( level.player );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_player_down( player )
{
	AssertEx( IsDefined( player ) && IsPlayer( player ), "is_player_down() requires a valid player to test." );
	
	if ( player ent_flag_exist( "laststand_downed" ) )
		return player ent_flag( "laststand_downed" );
	
	if ( IsDefined( player.laststand ) )
		return player.laststand;

	return !IsAlive( player );
}

/*
=============
///ScriptDocBegin
"Name: is_player_down_and_out( <player> )"
"Summary: Returns true if a player is in last stand mode AND been knocked into "out" mode... where they have no weapon."
"Module: Entity"
"CallOn: A player"
"MandatoryArg: <player>: "
"Example: bool = is_player_down_and_out( leve.player );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_player_down_and_out( player )
{
	assertex( isdefined( player ) && isplayer( player ), "is_player_down_and_out() requires a valid player to test." );
	
	if ( !isdefined( player.down_part2_proc_ran ) )
		return false;

	return player.down_part2_proc_ran;
}

/*
=============
///ScriptDocBegin
"Name: killing_will_down()"
"Summary: Returns true if damaging the player to death will put them in laststand. Doesn't account for armor."
"Module: Entity"
"CallOn: A player"
"MandatoryArg: <player>: "
"Example: bool = killing_will_down( level.player )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
killing_will_down( player )
{
	AssertEx( IsDefined( player ) && IsPlayer( player ), "Invalid player." );
	
	if ( laststand_enabled() )
	{
		AssertEx( IsDefined( level.laststand_initialized ) && IsDefined( level.laststand_kill_will_down_func ), "Laststand not initialized." );
		
		if ( IsDefined( level.laststand_kill_will_down_func ) )
		{
			return player [[ level.laststand_kill_will_down_func ]]();
		}
	}
	
	return false;
}

/*
=============
///ScriptDocBegin
"Name: is_survival()"
"Summary: returns true if map is in Special Op Survival mode."
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_survival()
{
	return ( is_specialop() && ( GetDvarInt( "so_survival" ) > 0 ) );
}

/*
=============
///ScriptDocBegin
"Name: laststand_enabled()"
"Summary: returns true if the player will enter last stand on death."
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
laststand_enabled()
{
	return IsDefined( level.laststand_type ) && level.laststand_type > 0;
}

/*
=============
///ScriptDocBegin
"Name: is_specialop()"
"Summary: returns true if map is in Special Op mode."
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_specialop()
{
	return GetDvarInt( "specialops" ) >= 1;
}

/*
=============
///ScriptDocBegin
"Name: convert_to_time_string( <timer>, <show_tenths> )"
"Summary: Takes a number and returns a string in Minutes:Seconds format."
"Module: Utility"
"MandatoryArg: <timer>: The number in seconds you want to convert."
"MandatoryArg: <show_tenths>: Option to display up to the tenths place for the seconds portion."
"Example: time_string = convert_to_time_string( time_seconds );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
convert_to_time_string( timer, show_tenths )
{
	string = "";
	if ( timer < 0 )
		string += "-";

	// Before computing the time string round
	// up the tenths place.
	timer = round_float( timer, 1, false );

	timer_clipped = timer * 100;
	timer_clipped = int( timer_clipped );
	timer_clipped = abs( timer_clipped );

	minutes = timer_clipped / 6000;
	minutes = int( minutes );
	string += minutes;

	seconds = timer_clipped / 100;
	seconds = int( seconds );
	seconds -= minutes * 60;
	if ( seconds < 10 )
		string += ":0" + seconds;
	else
		string += ":" + seconds;

	if ( IsDefined( show_tenths ) && show_tenths )
	{
		tenths = timer_clipped;
		tenths -= minutes * 6000;
		tenths -= seconds * 100;
		tenths = int( tenths/10 );
		string += "." + tenths;
	}

	return string;
}


/*
=============
///ScriptDocBegin
"Name: round_float( <value> , <place> , <up> )"
"Summary: Takes a float value and rounds that value according to the specificed place and whether or not up or down is specified."
"Module: Utility"
"MandatoryArg: <value>: The float value to round"
"MandatoryArg: <precision>: The place to round to. Valid params are: 0 for "integer", 1 for "tenths", 2 for "hundredths", 3 for "thousandths" and 4 for "ten_thousandths"."
"OptionalArg: <down>: Boolean value representing whether to round down or up. Defaults to down when undefined."
"Example: float_rounded = round_float( 2.339, 2, false )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

round_float( value, precision, down )
{
	AssertEx( IsDefined( value ), "value must be defined." );
	AssertEx( IsDefined( precision ), "precision must be defined." );
	AssertEx( precision == int( precision ), "precision must be an integer." );
	
	precision = int( precision );
	
	if ( precision < 0 || precision > 4 )
	{
		AssertMsg( "Precision value must be an integer that is >= 0 and <= 4. This was passed in: " + precision );
		return value;
	}
	
	// Start at no decimal offset
	decimal_offset = 1;
	for ( i = 1; i <= precision; i++ )
	{
		decimal_offset *= 10;
	}
	
	value_clipped = value * decimal_offset;
	
	if ( !IsDefined( down ) || down )
	{
		value_clipped = floor( value_clipped );
	}
	else
	{
		value_clipped = ceil( value_clipped );	
	}
	
	value = value_clipped / decimal_offset;
	
	return value;
}

/*
=============
///ScriptDocBegin
"Name: round_millisec_on_sec( <value> , <precision> , <down> )"
"Summary: Takes milliseconds, converts to seconds, rounds to the desired precision and then converts back to milliseconds."
"Module: Utility"
"MandatoryArg: <value>: The time in milliseconds to be rounded."
"MandatoryArg: <precision>: The placeround the value according to seconds. Valid params are: 0 for "integer", 1 for "tenths", 2 for "hundredths", 3 for "thousandths" and 4 for "ten_thousandths"."
"OptionalArg: <down>: Boolean value representing whether to round down or up. Defaults to down when undefined."
"Example: time_mill_rounded = round_millisec_on_sec( time_mil )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

round_millisec_on_sec( value, precision, down )
{
	// Divide by 1000 instead of multiplying by
	// 0.001 because the latter causes excess
	// decimal places due to floating point error - JC
	value_seconds = value / 1000;
	
	value_seconds = round_float( value_seconds, precision, down );
	
	value = value_seconds * 1000;
	
	return int( value );
}

/*
=============
///ScriptDocBegin
"Name: set_vision_set( <visionset> , <transition_time> )"
"Summary: Sets the vision set over time"
"Module: Utility"
"MandatoryArg: <visionset>: Visionset file to use"
"OptionalArg: <transition_time>: Time to transition to the new vision set. Defaults to 1 second."
"Example: set_vision_set( "blackout_darkness", 0.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_vision_set( visionset, transition_time )
{
	if ( init_vision_set( visionset ) )
		return;

	if ( !isdefined( transition_time ) )
		transition_time = 1;
	//iprintlnbold( visionset );
	VisionSetNaked( visionset, transition_time );
	SetDvar( "vision_set_current", visionset );
}

/*
=============
///ScriptDocBegin
"Name: set_vision_set_player( <visionset> , <transition_time> )"
"Summary: Sets the vision set over time for a specific player in coop"
"Module: Utility"
"MandatoryArg: <visionset>: Visionset file to use"
"OptionalArg: <transition_time>: Time to transition to the new vision set. Defaults to 1 second."
"Example: level.player2 set_vision_set_player( "blackout_darkness", 0.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_vision_set_player( visionset, transition_time )
{
	if ( init_vision_set( visionset ) )
		return;

	Assert( IsDefined( self ) );
	Assert( level != self );

	if ( !isdefined( transition_time ) )
		transition_time = 1;
	self VisionSetNakedForPlayer( visionset, transition_time );
}

/*
 =============
///ScriptDocBegin
"Name: sun_light_fade( <startSunColor>, <endSunColor>, <fTime> )"
"Summary: Fade to a given sunlight RGB value over the specified time"
"Module: Utility"
"MandatoryArg: <startSunColor> : Starting RGB values (use whatever the current sun is set to)"
"MandatoryArg: <endSunColor> : Target RGB values the sun colors should change to"
"MandatoryArg: <fTime> : Time in seconds for the fade to occur"
"Example: thread sun_light_fade( (.5,.8,.75), (3.5,3.5,3.5), 2 )"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

sun_light_fade( startSunColor, endSunColor, fTime )
{
	fTime = Int( fTime * 20 );

	// determine difference btwn starting and target sun RGBs
	increment = [];
	for ( i = 0; i < 3; i++ )
		increment[ i ] = ( startSunColor[ i ] - endSunColor[ i ] ) / fTime;

	// change gradually to new sun color over time
    newSunColor = [];
    for ( i = 0; i < fTime; i++ )
    {
    	wait( 0.05 );
    	for ( j = 0; j < 3; j++ )
    		newSunColor[ j ] = startSunColor[ j ] - ( increment[ j ] * i );
		SetSunLight( newSunColor[ 0 ], newSunColor[ 1 ], newSunColor[ 2 ] );
    }
    //set sunlight to new target values to account for rounding off decimal places
    SetSunLight( endSunColor[ 0 ], endSunColor[ 1 ], endSunColor[ 2 ] );
}

/*
 =============
///ScriptDocBegin
"Name: ent_flag_wait( <flagname> )"
"Summary: Waits until the specified flag is set on self. Even handles some default flags for ai such as 'goal' and 'damage'"
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to wait on"
"Example: enemy ent_flag_wait( "goal" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_wait( msg )
{
	AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" );

	while ( IsDefined( self ) && !self.ent_flag[ msg ] )
		self waittill( msg );
}

/*
 =============
///ScriptDocBegin
"Name: ent_flag_wait_vehicle_node( <flagname> )"
"Summary: Waits until the specified flag is set on self. Even handles some default flags for ai such as 'goal' and 'damage'"
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to wait on"
"Example: enemy ent_flag_wait( "goal" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_wait_vehicle_node( msg )
{
	AssertEx( IsDefined( self ) , "Attempt to check a flag on node that is is not defined" );

	while ( IsDefined( self ) && !self.ent_flag[ msg ] )
		self waittill( msg );
}


 /*
 =============
///ScriptDocBegin
"Name: ent_flag_wait_either( <flagname1> , <flagname2> )"
"Summary: Waits until either of the the specified flags are set on self. Even handles some default flags for ai such as 'goal' and 'damage'"
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname1> : name of one flag to wait on"
"MandatoryArg: <flagname2> : name of the other flag to wait on"
"Example: enemy ent_flag_wait( "goal", "damage" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_wait_either( flag1, flag2 )
{
	AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" );

	while ( IsDefined( self ) )
	{
		if ( ent_flag( flag1 ) )
			return;
		if ( ent_flag( flag2 ) )
			return;

		self waittill_either( flag1, flag2 );
	}
}

 /*
 =============
///ScriptDocBegin
"Name: ent_flag_wait_or_timeout( <flagname> , <timer> )"
"Summary: Waits until either the flag gets set on self or the timer elapses. Even handles some default flags for ai such as 'goal' and 'damage'"
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname1: Name of one flag to wait on"
"MandatoryArg: <timer> : Amount of time to wait before continuing regardless of flag."
"Example: ent_flag_wait_or_timeout( "time_to_go", 3 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_wait_or_timeout( flagname, timer )
{
	AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" );

	start_time = GetTime();
	while ( IsDefined( self ) )
	{
		if ( self.ent_flag[ flagname ] )
			break;

		if ( GetTime() >= start_time + timer * 1000 )
			break;

		self ent_wait_for_flag_or_time_elapses( flagname, timer );
	}
}

/*
=============
///ScriptDocBegin
"Name: ent_flag_waitopen( <msg> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
ent_flag_waitopen( msg )
{
	AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" );

	while ( IsDefined( self ) && self.ent_flag[ msg ] )
		self waittill( msg );
}

ent_flag_assert( msg )
{
	AssertEx( !self ent_flag( msg ), "Flag " + msg + " set too soon on entity" );
}


 /*
 =============
///ScriptDocBegin
"Name: ent_flag_waitopen_either( <flagname1> , <flagname2> )"
"Summary: Waits until either of the the specified flags are open on self. Even handles some default flags for ai such as 'goal' and 'damage'"
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname1> : name of one flag to waitopen on"
"MandatoryArg: <flagname2> : name of the other flag to waitopen on"
"Example: enemy ent_flag_waitopen_either( "goal", "damage" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_waitopen_either( flag1, flag2 )
{
	AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" );

	while ( IsDefined( self ) )
	{
		if ( !ent_flag( flag1 ) )
			return;
		if ( !ent_flag( flag2 ) )
			return;

		self waittill_either( flag1, flag2 );
	}
}

 /*
 =============
///ScriptDocBegin
"Name: ent_flag_init( <flagname> )"
"Summary: Initialize a flag to be used. All flags must be initialized before using ent_flag_set or ent_flag_wait.  Some flags for ai are set by default such as 'goal', 'death', and 'damage'"
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to create"
"Example: enemy ent_flag_init( "hq_cleared" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_init( message )
{
	if ( !isDefined( self.ent_flag ) )
	{
		self.ent_flag = [];
		self.ent_flags_lock = [];
	}

	/#
	if ( IsDefined( level.first_frame ) && level.first_frame == -1 )
		AssertEx( !isDefined( self.ent_flag[ message ] ), "Attempt to reinitialize existing message: " + message + " on entity." );
	#/

	self.ent_flag[ message ] = false;
/#
	self.ent_flags_lock[ message ] = false;
#/
}

 /*
 =============
///ScriptDocBegin
"Name: ent_flag_exist( <flagname> )"
"Summary: checks to see if a flag exists"
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to check"
"Example: if( enemy ent_flag_exist( "hq_cleared" ) );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_exist( message )
{
	if ( IsDefined( self.ent_flag ) && IsDefined( self.ent_flag[ message ] ) )
		return true;
	return false;
}

 /*
 =============
///ScriptDocBegin
"Name: ent_flag_set_delayed( <flagname> , <delay> )"
"Summary: Sets the specified flag after waiting the delay time on self, all scripts using ent_flag_wait on self will now continue."
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to set"
"MandatoryArg: <delay> : time to wait before setting the flag"
"Example: entity flag_set_delayed( "hq_cleared", 2.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_set_delayed( message, delay )
{
	self endon( "death" );

	wait( delay );
	self ent_flag_set( message );
}

 /*
 =============
///ScriptDocBegin
"Name: ent_flag_set( <flagname> )"
"Summary: Sets the specified flag on self, all scripts using ent_flag_wait on self will now continue."
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to set"
"Example: enemy ent_flag_set( "hq_cleared" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_set( message )
{
/#
 	AssertEx( IsDefined( self ), "Attempt to set a flag on entity that is not defined" );
	AssertEx( IsDefined( self.ent_flag[ message ] ), "Attempt to set a flag before calling flag_init: " + message + " on entity." );
	Assert( self.ent_flag[ message ] == self.ent_flags_lock[ message ] );
	self.ent_flags_lock[ message ] = true;
#/
	self.ent_flag[ message ] = true;
	self notify( message );
}

 /*
 =============
///ScriptDocBegin
"Name: ent_flag_clear( <flagname> )"
"Summary: Clears the specified flag on self."
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to clear"
"OptionalArg: <remove> : free the flag completely, use this when you want to save a variable after you're never going to use the flag again."
"Example: enemy ent_flag_clear( "hq_cleared" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag_clear( message, remove )
{
/#
 	AssertEx( IsDefined( self ), "Attempt to clear a flag on entity that is not defined" );
	AssertEx( IsDefined( self.ent_flag[ message ] ), "Attempt to set a flag before calling flag_init: " + message + " on entity." );
	Assert( self.ent_flag[ message ] == self.ent_flags_lock[ message ] );
	self.ent_flags_lock[ message ] = false;
#/
	//do this check so we don't unneccessarily send a notify
	if ( 	self.ent_flag[ message ] )
	{
		self.ent_flag[ message ] = false;
		self notify( message );
	}
	
	if ( IsDefined( remove ) && remove )
	{
		self.ent_flag[ message ] = undefined;
/#
		self.ent_flags_lock[ message ] = undefined;
#/
}
}

/*
=============
///ScriptDocBegin
"Name: ent_flag_clear_delayed( <message> , <delay> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
ent_flag_clear_delayed( message, delay )
{
	wait( delay );
	self ent_flag_clear( message );
}

 /*
 =============
///ScriptDocBegin
"Name: ent_flag( <flagname> )"
"Summary: Checks if the flag is set on self. Returns true or false."
"Module: Flag"
"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)"
"MandatoryArg: <flagname> : name of the flag to check"
"Example: enemy ent_flag( "death" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
ent_flag( message )
{
	AssertEx( IsDefined( message ), "Tried to check flag but the flag was not defined." );
	AssertEx( IsDefined( self.ent_flag[ message ] ), "Tried to check flag " + message + " but the flag was not initialized." );

	return self.ent_flag[ message ];
}
 /*
 =============
///ScriptDocBegin
"Name: get_closest_to_player_view( <array>, <player>, <use_eye> )"
"Summary: Returns array member closest to player's view"
"Module: Utility"
"CallOn: Variable"
"MandatoryArg: <array>: The array of items to test against"
"OptionalArg: <player>: Player to test against. If undefined defaults to level.player"
"OptionalArg: <use_eye>: Check from the player's eye point. If undefined uses the player's origin."
"OptionalArg: <min_dot>: min dot"
"Example: enemy get_closest_to_player_view( enemies, level.player2, true );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_to_player_view( array, player, use_eye, min_dot )
{
	if ( !array.size )
		return;

	if ( !isdefined( player ) )
		player = level.player;

	if ( !isdefined( min_dot ) )
		min_dot = -1;

	player_origin = player.origin;
	if ( IsDefined( use_eye ) && use_eye )
		player_origin = player GetEye();

	ent = undefined;

	player_angles = player GetPlayerAngles();
	player_forward = AnglesToForward( player_angles );

	dot = -1;
	foreach ( array_item in array )
	{
		angles = VectorToAngles( array_item.origin - player_origin );
		forward = AnglesToForward( angles );

		newdot = VectorDot( player_forward, forward );
		if ( newdot < dot )
			continue;
		if ( newdot < min_dot )
			continue;
		dot = newdot;
		ent = array_item;
	}
	return ent;
}

/*
 =============
///ScriptDocBegin
"Name: get_closest_index_to_player_view( <array>, <player>, <use_eye> )"
"Summary: Returns array index of item closest to player's view"
"Module: Utility"
"CallOn: Variable"
"MandatoryArg: <array>: The array of items to test against"
"OptionalArg: <player>: Player to test against. If undefined defaults to level.player"
"OptionalArg: <use_eye>: Check from the player's eye point. If undefined uses the player's origin."
"OptionalArg: <return_index>: If true, will return the index within the array rather than the actual array element."
"Example: enemy get_closest_index_to_player_view( enemies, level.player2, true );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_index_to_player_view( array, player, use_eye )
{
	if ( !array.size )
		return;

	if ( !isdefined( player ) )
		player = level.player;

	player_origin = player.origin;
	if ( IsDefined( use_eye ) && use_eye )
		player_origin = player GetEye();

	index = undefined;

	player_angles = player GetPlayerAngles();
	player_forward = AnglesToForward( player_angles );

	dot = -1;
	for ( i = 0; i < array.size; i++ )
	{
		angles = VectorToAngles( array[ i ].origin - player_origin );
		forward = AnglesToForward( angles );

		newdot = VectorDot( player_forward, forward );
		if ( newdot < dot )
			continue;

		dot = newdot;
		index = i;
	}
	return index;
}

flag_trigger_init( message, trigger, continuous )
{
	flag_init( message );

	if ( !isDefined( continuous ) )
		continuous = false;

	Assert( IsSubStr( trigger.classname, "trigger" ) );
	trigger thread _flag_wait_trigger( message, continuous );

	return trigger;
}


flag_triggers_init( message, triggers, all )
{
	flag_init( message );

	if ( !isDefined( all ) )
		all = false;

	for ( index = 0; index < triggers.size; index++ )
	{
		Assert( IsSubStr( triggers[ index ].classname, "trigger" ) );
		triggers[ index ] thread _flag_wait_trigger( message, false );
	}

	return triggers;
}


/*
=============
///ScriptDocBegin
"Name: flag_set_delayed( <flagname> , <delay> )"
"Summary: Sets the specified flag after waiting the delay time, all scripts using flag_wait will now continue."
"Module: Flag"
"CallOn: "
"MandatoryArg: <flagname> : name of the flag to set"
"MandatoryArg: <delay> : time to wait before setting the flag"
"Example: flag_set_delayed( "hq_cleared", 2.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
*/
flag_set_delayed( message, delay )
{
	wait( delay );
	flag_set( message );
}

/*
=============
///ScriptDocBegin
"Name: flag_clear_delayed( <flagname> , <delay> )"
"Summary: Clears the specified flag after waiting the delay time, all scripts using flag_waitopen will now continue."
"Module: Flag"
"CallOn: "
"MandatoryArg: <flagname> : name of the flag to clear"
"MandatoryArg: <delay> : time to wait before clearing the flag"
"Example: flag_clear_delayed( "hq_cleared", 2.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
*/
flag_clear_delayed( message, delay )
{
	wait( delay );
	flag_clear( message );
}

level_end_save()
{
	if ( arcadeMode() )
		return;

	if ( level.MissionFailed )
		return;

	if ( flag( "game_saving" ) )
		return;

	for ( i = 0; i < level.players.size; i++ )
	{
		player = level.players[ i ];
		if ( !isAlive( player ) )
			return;
	}

	flag_set( "game_saving" );

	imagename = "levelshots / autosave / autosave_" + level.script + "end";

	// string not found for AUTOSAVE_AUTOSAVE
	SaveGame( "levelend", &"AUTOSAVE_AUTOSAVE", imagename, true, true );// does not print "Checkpoint Reached"

	flag_clear( "game_saving" );
}

/*
=============
///ScriptDocBegin
"Name: add_extra_autosave_check( <name> , <func> , <msg> )"
"Summary: Adds an extra autosave check that must pass for an autosave to happen"
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <name>: identify the index, of this autosave check ( so you can overwrite it )"
"MandatoryArg: <func>: function should return true for the check to pass"
"MandatoryArg: <msg>: this is what will print to the console when this check fails"
"Example: 		add_extra_autosave_check( "boat_check_trailing" , ::autosave_boat_check_trailing , "trailing too far behind the enemy boat." );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_extra_autosave_check( name, func, msg )
{
	level._extra_autosave_checks[ name ] = [];
	level._extra_autosave_checks[ name ][ "func" ] = func;
	level._extra_autosave_checks[ name ][ "msg" ] = msg;
}

/*
=============
///ScriptDocBegin
"Name: remove_extra_autosave_check( <name> )"
"Summary: removes the extra autosave game condtions added by add_extra_autosave_check"
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <param1>: Name"
"Example: add_extra_autosave_check( "boat_check_trailing" ); "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_extra_autosave_check( name )
{
	level._extra_autosave_checks[ name ] = undefined;
}

/*
=============
///ScriptDocBegin
"Name: autosave_stealth()"
"Summary: autosave the game if it's 'safe' to do so in a stealth level, internally threaded and handles any 'dangerous' situations that we wouldn't want to save at during stealth gameplay"
"Module: Autosave"
"CallOn: "
"Example: autosave_stealth();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
*/
autosave_stealth()
{
	thread autosave_by_name_thread( "autosave_stealth", 8, true );
}

/*
=============
///ScriptDocBegin
"Name: autosave_stealth_silent()"
"Summary: silently autosaves the game if it's 'safe' to do so in a stealth level, internally threaded and handles any 'dangerous' situations that we wouldn't want to save at during stealth gameplay"
"Module: Autosave"
"CallOn: "
"Example: autosave_stealth_silent();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
*/
autosave_stealth_silent()
{
	thread autosave_by_name_thread( "autosave_stealth", 8, true, true );
}

/*
=============
///ScriptDocBegin
"Name: autosave_tactical()"
"Summary: autosave the game if it's 'safe' during tactical gameplay...meaning don't autosave if the player has any current enemies or has thrown a grenade. ( internally threaded )"
"Module: Autosave"
"CallOn: "
"Example: autosave_tactical();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
*/
autosave_tactical()
{
	autosave_tactical_setup();
	thread autosave_tactical_proc();
}

/*
=============
///ScriptDocBegin
"Name: autosave_by_name( <savename> )"
"Summary: autosave the game with the specified save name"
"Module: Autosave"
"CallOn: "
"MandatoryArg: <savename> : name of the save file to create"
"Example: thread autosave_by_name( "building2_cleared" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
*/
autosave_by_name( name )
{
	thread autosave_by_name_thread( name );
}

/*
=============
///ScriptDocBegin
"Name: autosave_by_name_silent( <savename> )"
"Summary: autosave the game with the specified save name without printing a checkpoint msg"
"Module: Autosave"
"CallOn: "
"MandatoryArg: <savename> : name of the save file to create"
"Example: thread autosave_by_name_silent( "building2_cleared" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
*/
autosave_by_name_silent( name )
{
	thread autosave_by_name_thread( name, undefined, undefined, true );
}

autosave_by_name_thread( name, timeout, doStealthChecks, suppress_hint )
{
	if ( !isDefined( level.curAutoSave ) )
		level.curAutoSave = 1;

	imageName = "levelshots/autosave/autosave_" + level.script + level.curAutoSave;
	result = level maps\_autosave::tryAutoSave( level.curAutoSave, "autosave", imagename, timeout, doStealthChecks, suppress_hint );
	if ( IsDefined( result ) && result )
		level.curAutoSave++;
}

/*
=============
///ScriptDocBegin
"Name: autosave_or_timeout( <name> , <timeout> )"
"Summary: Autosaves with the specified name but times out if the time elapses"
"Module: Autosave"
"MandatoryArg: <name>: The name"
"MandatoryArg: <timeout>: The timeout"
"Example: autosave_or_timeout( "thename", 3.5 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
autosave_or_timeout( name, timeout )
{
	thread autosave_by_name_thread( name, timeout );
}

/*
=============
///ScriptDocBegin
"Name: debug_message( <message> , <origin>, <duration>, <entity> )"
"Summary: Prints 3d debug text at the specified location for a duration of time."
"Module: Debug"
"MandatoryArg: <message>: String to print"
"MandatoryArg: <origin>: Location of string ( x, y, z )"
"OptionalArg: <duration>: Time to keep the string on screen. Defaults to 5 seconds."
"OptionalArg: <entity>: Overrides any origin value so that message can print on a moving entity"
"Example: debug_message( "I am the enemy", enemy.origin, 3.0 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
debug_message( message, origin, duration, entity )
{
	if ( !isDefined( duration ) )
		duration = 5;

	if ( IsDefined( entity ) )
	{
		entity endon( "death" );
		origin = entity.origin;
	}


	for ( time = 0; time < ( duration * 20 ); time++ )
	{
		if ( !isdefined( entity ) )
			Print3d( ( origin + ( 0, 0, 45 ) ), message, ( 0.48, 9.4, 0.76 ), 0.85 );
		else
			Print3d( entity.origin, message, ( 0.48, 9.4, 0.76 ), 0.85 );
		wait 0.05;
	}
}

/*
=============
///ScriptDocBegin
"Name: debug_message_ai( <message>, <duration> )"
"Summary: Prints 3d debug text at the ai's origin and follows the ai that passed the function."
"Module: Debug"
"CallOn: An AI"
"MandatoryArg: <message>: String to print"
"OptionalArg: <duration>: Time to keep the string on screen. Defaults to 5 seconds."
"Example: debug_message_ai( "I am the enemy", 3.0 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
debug_message_ai( message, duration )
{
	self notify( "debug_message_ai" );
	self endon( "debug_message_ai" );
	self endon( "death" );

	if ( !isDefined( duration ) )
		duration = 5;

	for ( time = 0; time < ( duration * 20 ); time++ )
	{
		Print3d( ( self.origin + ( 0, 0, 45 ) ), message, ( 0.48, 9.4, 0.76 ), 0.85 );
		wait 0.05;
	}
}

/*
=============
///ScriptDocBegin
"Name: debug_message_clear( <message> , <origin>, <duration>, <extraEndon> )"
"Summary: Prints 3d debug text at the specified location for a duration of time, but can be cleared before the normal time has passed if a notify occurs."
"Module: Debug"
"MandatoryArg: <message>: String to print"
"MandatoryArg: <origin>: Location of string ( x, y, z )"
"OptionalArg: <duration>: Time to keep the string on screen. Defaults to 5 seconds."
"OptionalArg: <extraEndon>: Level notify string that will make this text go away before the time expires."
"Example: debug_message( "I am the enemy", enemy.origin, 3.0, "enemy died" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
debug_message_clear( message, origin, duration, extraEndon )
{
	if ( IsDefined( extraEndon ) )
	{
		level notify( message + extraEndon );
		level endon( message + extraEndon );
	}
	else
	{
		level notify( message );
		level endon( message );
	}

	if ( !isDefined( duration ) )
		duration = 5;

	for ( time = 0; time < ( duration * 20 ); time++ )
	{
		Print3d( ( origin + ( 0, 0, 45 ) ), message, ( 0.48, 9.4, 0.76 ), 0.85 );
		wait 0.05;
	}
}

precache( model )
{
	ent = Spawn( "script_model", ( 0, 0, 0 ) );
	ent.origin = level.player GetOrigin();
	ent SetModel( model );
	ent Delete();
}


closerFunc( dist1, dist2 )
{
	return dist1 >= dist2;
}

fartherFunc( dist1, dist2 )
{
	return dist1 <= dist2;
}

 /*
 =============
///ScriptDocBegin
"Name: getClosestFx( <org> , <fxarray> , <dist> )"
"Summary: Returns the closest fx struct created by createfx in < fxarray > to location < org > "
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"MandatoryArg: <array> : Array of createfx structs to check distance on. These are obtained with getfxarraybyID( <fxid> )"
"OptionalArg: <dist> : Minimum distance to check"
"Example: fxstruct = getClosestFx( hallway_tv, fxarray );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
getClosestFx( org, fxarray, dist )
{
	return compareSizesFx( org, fxarray, dist, ::closerFunc );
}

 /*
 =============
///ScriptDocBegin
"Name: get_closest_point( <origin> , <points> , <maxDist> )"
"Summary: Returns the closest point from array < points > from location < origin > "
"Module: Distance"
"CallOn: "
"MandatoryArg: <origin> : Origin to be closest to."
"MandatoryArg: <points> : Array of points to check distance on"
"OptionalArg: <maxDist> : Maximum distance to check"
"Example: target = getFarthest( level.player.origin, targets );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_point( origin, points, maxDist )
{
	Assert( points.size );

	closestPoint = points[ 0 ];
	dist = Distance( origin, closestPoint );

	for ( index = 0; index < points.size; index++ )
	{
		testDist = Distance( origin, points[ index ] );
		if ( testDist >= dist )
			continue;

		dist = testDist;
		closestPoint = points[ index ];
	}

	if ( !isDefined( maxDist ) || dist <= maxDist )
		return closestPoint;

	return undefined;
}

 /*
 =============
///ScriptDocBegin
"Name: get_farthest_ent( <origin> , <array> )"
"Summary: Returns the farthest entity in <array> from location < origin > "
"Module: Distance"
"CallOn: "
"MandatoryArg: <origin> : Origin to be farthest from."
"MandatoryArg: <array> : Array of entities to choose from"
"Example: dude = get_farthest_ent( level.player.origin, aFriendlies );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_farthest_ent( org, array )
{
	if ( array.size < 1 )
		return;

	dist = Distance( array[ 0 ] GetOrigin(), org );
	ent = array[ 0 ];
	for ( i = 0; i < array.size; i++ )
	{
		newdist = Distance( array[ i ] GetOrigin(), org );
		if ( newdist < dist )
			continue;
		dist = newdist;
		ent = array[ i ];
	}
	return ent;
}


 /*
 =============
///ScriptDocBegin
"Name: get_within_range( <org> , <array> , <dist> )"
"Summary: Returns all elements from the array that are within DIST range to ORG."
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"MandatoryArg: <array> : Array of entities to check distance on"
"OptionalArg: <dist> : Maximum distance to check"
"Example: close_ai = get_within_range( level.player.origin, ai, 500 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_within_range( org, array, dist )
{
	guys = [];
	for ( i = 0; i < array.size; i++ )
	{
		if ( Distance( array[ i ].origin, org ) <= dist )
			guys[ guys.size ] = array[ i ];
	}
	return guys;
}

 /*
 =============
///ScriptDocBegin
"Name: get_outside_range( <org> , <array> , <dist> )"
"Summary: Returns all elements from the array that are outside DIST range to ORG."
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"MandatoryArg: <array> : Array of entities to check distance on"
"OptionalArg: <dist> : Maximum distance to check"
"Example: close_ai = get_outside_range( level.player.origin, ai, 500 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_outside_range( org, array, dist )
{
	guys = [];
	for ( i = 0; i < array.size; i++ )
	{
		if ( Distance( array[ i ].origin, org ) > dist )
			guys[ guys.size ] = array[ i ];
	}
	return guys;
}

 /*
 =============
///ScriptDocBegin
"Name: get_closest_living( <org> , <array> , <dist> )"
"Summary: Returns the closest living entity from the array from the origin"
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"MandatoryArg: <array> : Array of entities to check distance on"
"OptionalArg: <dist> : Maximum distance to check"
"Example: kicker = get_closest_living( node.origin, ai );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_living( org, array, dist )
{
	if ( !isdefined( dist ) )
		dist = 9999999;
	if ( array.size < 1 )
		return;
	ent = undefined;
	for ( i = 0; i < array.size; i++ )
	{
		if ( !isalive( array[ i ] ) )
			continue;
		newdist = Distance( array[ i ].origin, org );
		if ( newdist >= dist )
			continue;
		dist = newdist;
		ent = array[ i ];
	}
	return ent;
}

/*
=============
///ScriptDocBegin
"Name: get_highest_dot( <start> , <end> , <array> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_highest_dot( start, end, array )
{
	if ( !array.size )
		return;

	ent = undefined;

	angles = VectorToAngles( end - start );
	dotforward = AnglesToForward( angles );
	dot = -1;

	foreach ( member in array )
	{
		angles = VectorToAngles( member.origin - start );
		forward = AnglesToForward( angles );

		newdot = VectorDot( dotforward, forward );
		if ( newdot < dot )
			continue;
		dot = newdot;
		ent = member;
	}
	return ent;
}

 /*
 =============
///ScriptDocBegin
"Name: get_closest_index( <org> , <array> , <dist> )"
"Summary: same as getClosest but returns the closest entity's array index instead of the actual entity."
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"MandatoryArg: <array> : Array of entities to check distance on."
"OptionalArg: <dist> : Maximum distance to check"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_index( org, array, dist )
{
	if ( !isdefined( dist ) )
		dist = 9999999;
	if ( array.size < 1 )
		return;
	index = undefined;
	foreach ( i, ent in array )
	{
		newdist = Distance( ent.origin, org );
		if ( newdist >= dist )
			continue;
		dist = newdist;
		index = i;
	}
	return index;
}

/*
=============
///ScriptDocBegin
"Name: get_closest_exclude( <org> , <ents> , <excluders> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_closest_exclude( org, ents, excluders )
{
	if ( !isdefined( ents ) )
		return undefined;

	range = 0;
	if ( IsDefined( excluders ) && excluders.size )
	{
		exclude = [];
		for ( i = 0; i < ents.size; i++ )
			exclude[ i ] = false;

		for ( i = 0; i < ents.size; i++ )
		for ( p = 0; p < excluders.size; p++ )
		if ( ents[ i ] == excluders[ p ] )
			exclude[ i ] = true;

		found_unexcluded = false;
		for ( i = 0; i < ents.size; i++ )
		if ( ( !exclude[ i ] ) && ( IsDefined( ents[ i ] ) ) )
		{
			found_unexcluded = true;
			range = Distance( org, ents[ i ].origin );
			ent = i;
			i = ents.size + 1;
		}

		if ( !found_unexcluded )
			return( undefined );
	}
	else
	{
		for ( i = 0; i < ents.size; i++ )
		if ( IsDefined( ents[ i ] ) )
		{
			range = Distance( org, ents[ 0 ].origin );
			ent = i;
			i = ents.size + 1;
		}
	}

	ent = undefined;

	for ( i = 0; i < ents.size; i++ )
	if ( IsDefined( ents[ i ] ) )
	{
		exclude = false;
		if ( IsDefined( excluders ) )
		{
			for ( p = 0; p < excluders.size; p++ )
			if ( ents[ i ] == excluders[ p ] )
				exclude = true;
		}

		if ( !exclude )
		{
			newrange = Distance( org, ents[ i ].origin );
			if ( newrange <= range )
			{
				range = newrange;
				ent = i;
			}
		}
	}

	if ( IsDefined( ent ) )
		return ents[ ent ];
	else
		return undefined;
}

 /*
 =============
///ScriptDocBegin
"Name: get_closest_player( <org> )"
"Summary: Returns the closest coop player to the specified origin."
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"Example: player = get_closest_player( tank.origin );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_player( org )
{
	if ( level.players.size == 1 )
		return level.player;

	player = getClosest( org, level.players );
	return player;
}

 /*
 =============
///ScriptDocBegin
"Name: get_closest_player_healthy( <org> )"
"Summary: Returns the closest player that is not bleeding out to the specified origin."
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"Example: player = get_closest_player_healthy( tank.origin );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_player_healthy( org )
{
	// JC-Laststand:ToDo - This assumes that all logic has ended once both players have gone down. This "may" be incorrect after last stand works in sp Specops
	if ( level.players.size == 1 )
		return level.player;

	healthyPlayers = get_players_healthy();

	player = getClosest( org, healthyPlayers );

	return player;
}

 /*
 =============
///ScriptDocBegin
"Name: get_players_healthy()"
"Summary: Returns all players not bleeding out."
"Module: Utility"
"Example: players = get_players_healthy();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_players_healthy()
{
	healthy_players = [];
	foreach ( player in level.players )
	{
		if ( is_player_down( player ) )
			continue;
		
		healthy_players[ healthy_players.size ] = player;
	}
	
	return healthy_players;
}

 /*
 =============
///ScriptDocBegin
"Name: get_closest_ai( <org> , <team> )"
"Summary: Returns the closest AI of the specified team to the specified origin."
"Module: Distance"
"CallOn: "
"MandatoryArg: <org> : Origin to be closest to."
"MandatoryArg: <team> : Team to use. Can be "allies", "axis", or "both"."
"Example: friendly = get_closest_ai( level.player.origin, "allies" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_closest_ai( org, team, excluders )
{
	if ( IsDefined( team ) )
		ents = GetAIArray( team );
	else
		ents = GetAIArray();

	if ( ents.size == 0 )
		return undefined;

	if ( IsDefined( excluders ) )
	{
		Assert( excluders.size > 0 );
		ents = array_remove_array( ents, excluders );
	}

	return getClosest( org, ents );
}

/*
=============
///ScriptDocBegin
"Name: get_closest_ai_exclude( <org> , <team> , <excluders> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_closest_ai_exclude( org, team, excluders )
{
	if ( IsDefined( team ) )
		ents = GetAIArray( team );
	else
		ents = GetAIArray();

	if ( ents.size == 0 )
		return undefined;

	return get_closest_exclude( org, ents, excluders );
}

/*
=============
///ScriptDocBegin
"Name: get_progress( <start> , <end> , <org>, <dist> )"
"Summary: Gets the progress of the given org between the start and end points. Supply the distance for optimization."
"Module: Vector"
"CallOn: nothing"
"MandatoryArg: <start>: start point."
"MandatoryArg: <end>: end point."
"MandatoryArg: <org>: the current point of the progression."
"OptionalArg: <dist>: the distance between start and end point (give for optimization, if not given, will calculate distance)."
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_progress( start, end, org, dist )
{
	if (!IsDefined(dist))
		dist = Distance( start, end );		

	dist = max(0.01, dist); // avoid division by zero
	normal = vectorNormalize( end - start );
	vec = org - start;
	progress = vectorDot( vec, normal );
	progress = progress / dist;
	progress = clamp(progress, 0, 1);
	return progress;
}

/*
=============
///ScriptDocBegin
"Name: can_see_origin( <origin>, <test_characters> )"
"Summary: Checks to see if the specified point is within the player's view."
"Module: Sentient"
"CallOn: An AI or player"
"MandatoryArg: <origin>: Origin you wish to see whether the player or AI can see."
"OptionalArg: <test_characters>: Set to determine whether the sight trace should check against characters."
"Example: level.player can_see_origin( entity.origin, true );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
can_see_origin( origin, test_characters )
{
	AssertEx( IsDefined( origin ), "can_see_origin() requires a valid origin to be passed in." );
	AssertEx( IsPlayer( self ) || IsAI( self ), "can_see_origin() can only be called on a player or AI." );

	if ( !isdefined( test_characters ) )
		test_characters = true;

	// if not in FOV, return false
	if ( !self point_in_fov( origin ) )
	{
		return false;
	}

	// if in FOV but sight trace fails, return false
	if ( !SightTracePassed( self GetEye(), origin, test_characters, self ) )
	{
		return false;
	}

	// if in FOV with successful trace, return true
	return true;
}

/*
=============
///ScriptDocBegin
"Name: point_in_fov( <origin> )"
"Summary: Checks to see if the point is within a standard player view FOV."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <origin>: The point to check if it is within view."
"Example: level.player point_in_fov( weapon_respawn.origin );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
point_in_fov( origin )
{
    forward = AnglesToForward( self.angles );
    normalVec = VectorNormalize( origin - self.origin );

    dot = VectorDot( forward, normalVec );
    return dot > 0.766;// fov = 80
}

 /*
 =============
///ScriptDocBegin
"Name: stop_magic_bullet_shield()"
"Summary: Stops magic bullet shield on an AI, setting his health back to a normal value and making him vulnerable to death."
"Module: AI"
"CallOn: AI"
"Example: friendly stop_magic_bullet_shield();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

stop_magic_bullet_shield()
{
	self notify( "stop_magic_bullet_shield" );
	AssertEx( IsDefined( self.magic_bullet_shield ) && self.magic_bullet_shield, "Tried to stop magic bullet shield on a guy without magic bulletshield" );

	if ( IsAI( self ) )
		self.attackeraccuracy = 1;

	self.magic_bullet_shield = undefined;
	self.damageShield = false;

	self notify( "internal_stop_magic_bullet_shield" );
	
	/#
		self.deletable_magic_bullet_shield = undefined;
	#/
}

// For future projects we should distinguish between "death" and "deletion"
// Since we currently do not, bulletshield has to be turned off before deleting characters, or you will get the 2nd assert below
magic_bullet_death_detection()
{
	/#
	self endon( "internal_stop_magic_bullet_shield" );
	export = self.export;
	entnum = self GetEntNum();

	self waittill( "death" );
	if ( IsDefined( self ) )
		AssertEx( 0, "Magic bullet shield guy with export(" + export + ") enuNum(" + entnum + ") died some how." );
	else
		AssertEx( 0, "Magic bullet shield guy with export(" + export + ") enuNum(" + entnum + ") died, most likely deleted from spawning guys." );

	export = export;// extra line so you can get this info in the debugger without a breakpoint.

	#/
}

 /*
 =============
///ScriptDocBegin
"Name: magic_bullet_shield( <no_death_detection> )"
"Summary: Makes an AI invulnerable to death. When he gets shot, he is ignored by enemies for < time > seconds and his health is regenerated."
"Module: AI"
"CallOn: AI"
"OptionalArg: <no_death_detection> : Set this to make the AI not script error on death, like if you want the guy to be deletable."
"Example: friendly thread magic_bullet_shield();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

magic_bullet_shield( no_death_detection )
{
	AssertEx( !IsPlayer( self ), "magic_bullet_shield is only valid for AI. Use EnableInvulnerability, EnableDeathShield or EnableHealthShield for a player" );
	
	if ( IsAI( self ) )// AI only
	{
		AssertEx( IsAlive( self ), "Tried to do magic_bullet_shield on a dead or undefined guy." );
		AssertEx( !self.delayedDeath, "Tried to do magic_bullet_shield on a guy about to die." );
		AssertEx( !isDefined( self.Melee ), "Trying to turn on magic_bullet_shield while melee in progress (might be about to die)." );
	}
	else // for drones
	{
		self.health = 100000;
	}


	self endon( "internal_stop_magic_bullet_shield" );
	AssertEx( !isdefined( self.magic_bullet_shield ), "Can't call magic bullet shield on a character twice. Use make_hero and remove_heroes_from_array so that you don't end up with shielded guys in your logic." );

	if ( IsAI( self ) )// AI only
		self.attackeraccuracy = 0.1;

	/#
	if ( !isdefined( no_death_detection ) )
	{
		thread magic_bullet_death_detection();
	}
	else
	{
		AssertEx( no_death_detection, "no_death_detection must be undefined or true" );
		self.deletable_magic_bullet_shield = true;
	}
	#/

	self notify( "magic_bullet_shield" );
	self.magic_bullet_shield = true;
	self.damageShield = true;
	self.noragdoll = true;
}


/*
=============
///ScriptDocBegin
"Name: disable_long_death(  )"
"Summary: Disables long death on Self"
"Module: Utility"
"CallOn: An enemy AI"
"Example: level.zakhaev disable_long_death()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_long_death()
{
	AssertEx( IsAlive( self ), "Tried to disable long death on a non living thing" );
	self.a.disableLongDeath = true;
}

/*
=============
///ScriptDocBegin
"Name: enable_long_death(  )"
"Summary: Enables long death on Self"
"Module: Utility"
"CallOn: An enemy AI"
"Example: level.zakhaev enable_long_death(0"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_long_death()
{
	AssertEx( IsAlive( self ), "Tried to enable long death on a non living thing" );
	self.a.disableLongDeath = false;
}

/*
=============
///ScriptDocBegin
"Name: enable_blood_pool( <enable_blood_pool> )"
"Summary: enables blood pools on AI deaths( on by default )"
"Module: AI"
"CallOn: An AI"
"Example: ai enable_blood_pool()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_blood_pool()
{
	self.skipBloodPool = undefined;
}

/*
=============
///ScriptDocBegin
"Name: disable_blood_pool( <enable_blood_pool> )"
"Summary: disables blood pools on AI deaths( on by default )"
"Module: AI"
"CallOn: An AI"
"Example: ai disable_blood_pool()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_blood_pool()
{
	self.skipBloodPool = true;
}

/*
=============
///ScriptDocBegin
"Name: deletable_magic_bullet_shield()"
"Summary: A version of magic bullet shield that does not error if the AI dies. Useful for guys that can be deleted but you want them to have aspects of MBS."
"Module: AI"
"CallOn: AI"
"Example: friendly thread magic_bullet_shield();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
deletable_magic_bullet_shield()
{
	magic_bullet_shield( true );
}


/*
=============
///ScriptDocBegin
"Name: get_ignoreme( <get_ignoreme> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_ignoreme()
{
	return self.ignoreme;
}

/*
=============
///ScriptDocBegin
"Name: set_ignoreme( <val> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_ignoreme( val )
{
	AssertEx( IsSentient( self ), "Non ai tried to set ignoreme" );
	self.ignoreme = val;
}

/*
=============
///ScriptDocBegin
"Name: set_ignoreall( <val> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_ignoreall( val )
{
	AssertEx( IsSentient( self ), "Non ai tried to set ignoraell" );
	self.ignoreall = val;
}

/*
=============
///ScriptDocBegin
"Name: set_IgnoreSonicAoE( <val> )"
"Summary: Sets if Sonic AOE can affect an AI, set to true or undefined"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: true or undefined"
"Example: guy set_IgnoreSonicAoE( true );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_IgnoreSonicAoE( val )
{
	AssertEx( IsSentient( self ), "Non ai tried to set IgnoreSonicAoE" );
	self.IgnoreSonicAoE = val;
}

/*
=============
///ScriptDocBegin
"Name: set_favoriteenemy( <enemy> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_favoriteenemy( enemy )
{
	self.favoriteenemy = enemy;
}

/*
=============
///ScriptDocBegin
"Name: get_pacifist( <get_pacifist> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_pacifist()
{
	return self.pacifist;
}

/*
=============
///ScriptDocBegin
"Name: set_pacifist( <val> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_pacifist( val )
{
	AssertEx( IsSentient( self ), "Non ai tried to set pacifist" );
	self.pacifist = val;
}

ignore_me_timer( time )
{
	self notify( "new_ignore_me_timer" );
	self endon( "new_ignore_me_timer" );
	self endon( "death" );

	if ( !isdefined( self.ignore_me_timer_prev_value ) )
		self.ignore_me_timer_prev_value = self.ignoreme;

	ai = GetAIArray( "bad_guys" );

	foreach ( guy in ai )
	{
		if ( !isalive( guy.enemy ) )
			continue;
		if ( guy.enemy != self )
			continue;

		guy ClearEnemy();
	}

	self.ignoreme = true;
	wait( time );

	self.ignoreme = self.ignore_me_timer_prev_value;
	self.ignore_me_timer_prev_value = undefined;
}


 /*
 =============
///ScriptDocBegin
"Name: delete_exploder( <num> )"
"Summary: deletes an exploder and its brushes forever."
"Module: Level"
"CallOn: Level"
"MandatoryArg: <num>: number of the exploder that you want to delete"
"Example: delete_exploder( 3 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

delete_exploder( num )
{
	common_scripts\_exploder::delete_exploder_proc( num );
}

/*
=============
///ScriptDocBegin
"Name: hide_exploder_models( <num> )"
"Summary: Hides the models for an exploder."
"Module: Level"
"CallOn: Level"
"MandatoryArg: <num>: number of the exploder that you want to hide"
"Example: hide_exploder_models( 3 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

hide_exploder_models( num )
{
	common_scripts\_exploder::hide_exploder_models_proc( num );
	}
	
/*
=============
///ScriptDocBegin
"Name: show_exploder_models( <num> )"
"Summary: shows the models for an exploder."
"Module: Level"
"CallOn: Level"
"MandatoryArg: <num>: number of the exploder that you want to hide"
"Example: show_exploder_models( 3 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

show_exploder_models( num )
{
	common_scripts\_exploder::show_exploder_models_proc( num );
				}
	
/*
=============
///ScriptDocBegin
"Name: stop_exploder( <num> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
stop_exploder( num )
{
	common_scripts\_exploder::stop_exploder_proc(num);
	}

/*
=============
///ScriptDocBegin
"Name: get_exploder_array( <msg> )"
"Summary: Return an array of exploders"
"Module: Utility"
"MandatoryArg: <msg>: Exploder num/name"
"Example: exploders = get_exploder_array( "boom" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_exploder_array( msg )
{
	return common_scripts\_exploder::get_exploder_array_proc( msg );
		}
	
flood_spawn( spawners )
{
	maps\_spawner::flood_spawner_scripted( spawners );
}

/*
=============
///ScriptDocBegin
"Name: set_ambient( <track>, <fade> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <track>: the name of the ambient track. This will set the ambient stream, dynamic ambience, mix, reverb, and occlusion."
"OptionalArg: <fade>: the crossfade time to the new ambience."
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_ambient( track, fade ) 
{
	soundscripts\_audio_zone_manager::AZM_start_zone( track, fade );
}

/*
=============
///ScriptDocBegin
"Name: force_crawling_death( <angle> , <num_crawls> , <array> , <nofallanim> )"
"Summary: Force an AI to crawl"
"Module: Utility"
"CallOn: An AI"
"MandatoryArg: <angle>: The angle to crawl"
"MandatoryArg: <num_crawls>: How many times to crawl"
"OptionalArg: <array>: Sets self.a.custom_crawling_death_array"
"OptionalArg: <nofallanim>: Sets self.nofallanim"
"Example: self force_crawling_death( self.angles[ 1 ], 2, level.scr_anim[ "crawl_death_1" ], 1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
force_crawling_death( angle, num_crawls, array, nofallanim )
{
	if ( !isdefined( num_crawls ) )
		num_crawls = 4;

	self thread force_crawling_death_proc( angle, num_crawls, array, nofallanim );
}

#using_animtree( "generic_human" );
override_crawl_death_anims()
{
	if ( IsDefined( self.a.custom_crawling_death_array ) )
	{
		self.a.array[ "crawl" ] = self.a.custom_crawling_death_array[ "crawl" ];
		self.a.array[ "death" ] = self.a.custom_crawling_death_array[ "death" ];
		self.a.crawl_fx_rate = self.a.custom_crawling_death_array[ "blood_fx_rate" ];
		if( isdefined( self.a.custom_crawling_death_array[ "blood_fx" ] ) )
			self.a.crawl_fx = self.a.custom_crawling_death_array[ "blood_fx" ];
	}

	self.a.array[ "stand_2_crawl" ] = [];
	self.a.array[ "stand_2_crawl" ][ 0 ] = %dying_stand_2_crawl_v3;

	if ( IsDefined( self.nofallanim ) )
		self.a.pose = "prone";

	self OrientMode( "face angle", self.a.force_crawl_angle );
	self.a.force_crawl_angle = undefined;
}

force_crawling_death_proc( angle, num_crawls, array, nofallanim )
{
	self.forceLongDeath = true;
	self.a.force_num_crawls = num_crawls;
	self.noragdoll = true;
	self.nofallanim = nofallanim;

	self.a.custom_crawling_death_array = array;
	self.crawlingPainAnimOverrideFunc = ::override_crawl_death_anims;

	self.maxhealth = 100000;
	self.health = 100000;
	self enable_long_death();

	if ( !isdefined( nofallanim ) || nofallanim == false )
		self.a.force_crawl_angle = angle + 181.02;
	else
	{
		self.a.force_crawl_angle = angle;
		self thread animscripts\notetracks::noteTrackPoseCrawl();
	}
}


never_saw_it_coming()
{
	self endon( "death" );
	while ( 1 )
	{
		highJump = self IsHighJumping();
		//Wait for a jump
		if( highJump )
		{
			
			exododge = self waittill_any_return( "exo_dodge", "player_boost_land", "disable_high_jump" );
			if ( !IsDefined( exododge ) || ( exododge == "player_boost_land" || exododge == "disable_high_jump" ) )
				continue;
			if ( !IsDefined( self.never_saw_it_coming ) )
				self.never_saw_it_coming = true;
			self waittill_any( "player_boost_land", "disable_high_jump" );
			waitframe;
			self.never_saw_it_coming = undefined;
		}
		waitframe;
	}
}

check_man_overboard( )
{
	if ( GetDvar( "mapname", "undefined" ) != "sanfran_b" )
		return;
	if ( !IsDefined( level.player.man_overboard ) || !level.player.man_overboard )
		level.player.man_overboard = true;
	
	wait(2.0);
	
	level.player.man_overboard = undefined;
}

monitor_genius_achievement(attacker, type, weapon)
{
  
  if( type != "MOD_GRENADE" )
  {
    attacker.genius_achievement = undefined;
    return;
  }

    
  if ( !isdefined( attacker.genius_achievement ) )
    attacker.genius_achievement = 1;
  else
    attacker.genius_achievement++;
  
  if ( attacker.genius_achievement == 4 )
  	giveachievement_wrapper("SMART_GRENADE_KILL");
  
  wait(0.1);
  attacker.genius_achievement = undefined;

}

start_monitor_escape_artist()
{
	add_global_spawn_function( "axis", ::monitor_escape_artist );
	array_thread( GetAIArray( "axis" ), ::monitor_escape_artist );
	
	level.grenade_id = 0;
	level.player.escape_artist = [];
}

monitor_escape_artist()
{
	while( 1 )
	{
		grenade = undefined;
		self waittill( "grenade_fire", grenade, weaponname );
		grenade.unique_id = level.grenade_id;
		level.grenade_id++;
		grenade.owner = self.unique_id;
		grenade thread escape_artist_distance();
		grenade thread escape_artist();
	}
}

escape_artist_distance()
{
	player = level.player;
	id = self.unique_id;
	while ( IsDefined( self ) )
	{	
		grenadeOffset = player.origin - self.origin;
		// "+ 23" is compensating a bit for the player bounds
		range = GetWeaponExplosionRadius( "fraggrenade" ) + 23;
		
		//range ^2
		rangeSq = squared( range );
		grenadeOffsetSq = LengthSquared(grenadeOffset);
		
		//Player outside range of grenade
		if ( grenadeOffsetSq > rangeSq )
		{
			if ( IsDefined(player.escape_artist[self.unique_id]))
				player.escape_artist[self.unique_id] = undefined;
		}
		//Player in range
		else
		{
			if ( !IsDefined( player.escape_artist[ self.unique_id ] ) )
				if (IsDefined( self.owner ) )
			 	   player.escape_artist[ self.unique_id ] = true;
		}
		waitframe;
		
	}
	if ( IsDefined( player.escape_artist[ id ] ) )
		player.escape_artist[ id ] = undefined;
}

escape_artist()
{
	player = level.player;
	while ( IsDefined( self ) )
	{
		if (IsDefined( player.escape_artist[self.unique_id] ) )
		{
			msg = level.player waittill_any_timeout( 4, "exo_dodge" );
			if ( IsDefined( msg ) && msg == "exo_dodge" )
			{
				self thread check_grenade_dmg();
			}
		}
		else
			waitframe;
	}
}

check_grenade_dmg()
{
	level.player endon( "death" );
	while ( IsDefined( self ) )
	{
		msg = level.player waittill_dmg_timeout( 1, "damage" );
		//If the player takes damage
		if ( IsDefined( msg ) && IsArray( msg ) ) 
		{
			//If the player takes grenade damage
			if ( msg[ 5 ] == "MOD_GRENADE_SPLASH" && !IsDefined( level.player.escape_artist_failure ) )
				//If the grenade damage was one that we were tracking and the player exo_dodged away
				if ( msg[ 2 ].unique_id == self.owner && IsDefined( level.player.escape_artist[ self.unique_id ] ) )
					level.player.escape_artist_failure = true;
		}
		else
			waitframe;
	}
	
	if ( !IsDefined( level.player.escape_artist_failure ) )
		level.player escape_artist_success();
	else
		level.player.escape_artist_failure = undefined;
}

escape_artist_success()
{
	escapee = self GetLocalPlayerProfileData( "ach_escapeArtist" ) + 1;
	if ( escapee == 20 )
		giveachievement_wrapper( "GRENADE_DODGE" );
	self SetLocalPlayerProfileData( "ach_escapeArtist", escapee );
}

waittill_dmg_timeout( timeOut, string1 )
{
	if ( ( !IsDefined( string1 ) || string1 != "death" ) )
			self endon( "death" );

	ent = SpawnStruct();

	if ( isdefined( string1 ) )
		self childthread waittill_string_parms( string1, ent );
	
	ent childthread _timeout( timeOut );
	
	ent waittill( "returned", msg );
	ent notify( "die" );
	return msg;
}

shock_ondeath()
{
	Assert( IsPlayer( self ));

	PreCacheShellShock( "default" );
	self waittill( "death" );

	if ( IsDefined( self.specialDeath ) )
		return;

	if ( GetDvar( "r_texturebits" ) == "16" )
		return;
	self ShellShock( "default", 3 );
}

/*
=============
///ScriptDocBegin
"Name: playerwatch_unresolved_collision()"
"Summary: Teleports the player to the closest pathnode if stuck in moving platform geo for at least a second."
"Module: Player"
"CallOn: Player"
"Example: level.player playerwatch_unresolved_collision()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
playerwatch_unresolved_collision()
{
	self endon( "death" );
	self endon( "stop_unresolved_collision_script" );
	
	self reset_unresolved_collision_handler();
	self childthread playerwatch_unresolved_collision_count();
	
	while( 1 )
	{
		if ( self.unresolved_collision )
		{
			self.unresolved_collision = false;
			if ( self.unresolved_collision_count >= 20 )
			{
				if ( IsDefined( self.handle_unresolved_collision ) )
					self [[self.handle_unresolved_collision]]();
				else 
					self default_unresolved_collision_handler();
			}
		}
		else 
		{
			self reset_unresolved_collision_handler();
		}
	
		wait 0.05;
	}
}

playerwatch_unresolved_collision_count()
{
	while( 1 )
	{
		self waittill( "unresolved_collision" );
		self.unresolved_collision = true;
		self.unresolved_collision_count++;
	}
}

reset_unresolved_collision_handler()
{
	self.unresolved_collision = false;
	self.unresolved_collision_count = 0;
}

default_unresolved_collision_handler()
{
	nodes = GetNodesInRadiusSorted( self.origin, 300, 0, 200, "Path" );
	if ( nodes.size )
	{
		self CancelMantle(); 
		self DontInterpolate();
		self SetOrigin( nodes[0].origin );
		self reset_unresolved_collision_handler();
	}
	else 
	{
		PrintLn( "^3Warning! killing player for unresolved_collision, could not find pathnode." );
		self kill();
	}
}

/*
=============
///ScriptDocBegin
"Name: stop_playerwatch_unresolved_collision()"
"Summary: Stops unresolved collision script for moving platforms."
"Module: Player"
"CallOn: Player"
"Example: level.player stop_playerwatch_unresolved_collision()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

stop_playerwatch_unresolved_collision()
{
	self notify( "stop_unresolved_collision_script" );
	self reset_unresolved_collision_handler();
}

/*
=============
///ScriptDocBegin
"Name: delete_on_death_wait_sound( <ent> , <sounddone> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
delete_on_death_wait_sound( ent, sounddone )
{
	ent endon( "death" );
	self waittill( "death" );
	if ( IsDefined( ent ) )
	{
		if ( ent IsWaitingOnSound() )
			ent waittill( sounddone );

		ent Delete();
	}
}

is_dead_sentient()
{
	return IsSentient( self ) && !isalive( self );
}

 /*
 =============
///ScriptDocBegin
"Name: play_sound_on_tag( <alias> , <tag>, <ends_on_death> )"
"Summary: Play the specified sound alias on a tag of an entity"
"Module: Sound"
"CallOn: An entity"
"MandatoryArg: <alias> : Sound alias to play"
"OptionalArg: <tag> : Tag on the entity to play sound on. If no tag is specified the entities origin will be used."
"OptionalArg: <ends_on_death> : The sound will be cut short if the entity dies. Defaults to false."
"OptionalArg: <op_notify_string> : Optional notify string on sound done."
"Example: vehicle thread play_sound_on_tag( "horn_honk", "tag_engine" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
play_sound_on_tag( alias, tag, ends_on_death, op_notify_string, radio_dialog )
{
	if ( is_dead_sentient() )
		return;

    if ( !SoundExists(alias) )
        return;

	org = Spawn( "script_origin", ( 0, 0, 0 ) );
	org endon( "death" );

	thread delete_on_death_wait_sound( org, "sounddone" );
	if ( IsDefined( tag ) )
		org LinkTo( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
	else
	{
		org.origin = self.origin;
		org.angles = self.angles;
		org LinkTo( self );
	}

	/#
	if ( IsDefined( level.player_radio_emitter ) && ( self == level.player_radio_emitter ) )
		PrintLn( "**dialog alias playing radio: " + alias );
	#/

	org PlaySound( alias, "sounddone" );
	if ( IsDefined( ends_on_death ) )
	{
		AssertEx( ends_on_death, "ends_on_death must be true or undefined" );
		if ( !isdefined( wait_for_sounddone_or_death( org ) ) )
			org StopSounds(); // don't call StopSounds (redundantly) when the sound stopped since this can cut off sounds in SO for the non host
		wait( 0.05 );// stopsounds doesnt work if the org is deleted same frame
	}
	else
	{
		org waittill( "sounddone" );
	}
	if ( IsDefined( op_notify_string ) )
		self notify( op_notify_string );
	org Delete();
}


/*
=============
///ScriptDocBegin
"Name: play_sound_on_tag_endon_death( <alias>, <tag> )"
"Summary: Play the specified sound alias on a tag of an entity but gets cut short if the entity dies"
"Module: Sound"
"CallOn: An entity"
"MandatoryArg: <alias> : Sound alias to play"
"OptionalArg: <tag> : Tag on the entity to play sound on. If no tag is specified the entities origin will be used."
"Example: vehicle thread play_sound_on_tag_endon_death( "horn_honk", "tag_engine" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
play_sound_on_tag_endon_death( alias, tag )
{
	play_sound_on_tag( alias, tag, true );
}

 /*
 =============
///ScriptDocBegin
"Name: play_sound_on_entity( <alias> , <op_notify_string> )"
"Summary: Play the specified sound alias on an entity at it's origin"
"Module: Sound"
"CallOn: An entity"
"MandatoryArg: <alias> : Sound alias to play"
"OptionalArg: <op_notify_string> : Optional notify string on sound done."
"Example: level.player play_sound_on_entity( "breathing_better" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
play_sound_on_entity( alias, op_notify_string )
{
	AssertEx( !isSpawner( self ), "Spawner tried to play a sound" );

	play_sound_on_tag( alias, undefined, undefined, op_notify_string );
}

 /*
 =============
///ScriptDocBegin
"Name: play_loop_sound_on_tag( <alias> , <tag>, bStopSoundOnDeath, bStopSoundOnRemoved )"
"Summary: Play the specified looping sound alias on a tag of an entity"
"Module: Sound"
"CallOn: An entity"
"MandatoryArg: <alias> : Sound alias to loop"
"OptionalArg: <tag> : Tag on the entity to play sound on. If no tag is specified the entities origin will be used."
"OptionalArg: <bStopSoundOnDeath> : Defaults to true. If true, will stop the looping sound when self dies"
"OptionalArg: <bStopSoundOnRemoved> : Defaults to false. If true, will stop the looping sound when self is undefined without dying. Safty measure to avoid infinite looping sound"
"Example: vehicle thread play_loop_sound_on_tag( "engine_belt_run", "tag_engine" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
play_loop_sound_on_tag( alias, tag, bStopSoundOnDeath, bStopSoundOnRemoved )
{
	org = Spawn( "script_origin", ( 0, 0, 0 ) );
	org endon( "death" );
	
	if ( !isdefined( bStopSoundOnDeath ) )
		bStopSoundOnDeath = true;
	if ( bStopSoundOnDeath )
		thread delete_on_death( org );
		
	if ( !isdefined( bStopSoundOnRemoved ) )
		bStopSoundOnRemoved = false;
	if ( bStopSoundOnRemoved )
		thread delete_on_removed( org );
		
	if ( IsDefined( tag ) )
		org LinkTo( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
	else
	{
		org.origin = self.origin;
		org.angles = self.angles;
		org LinkTo( self );
	}
// 	org endon( "death" );
	org PlayLoopSound( alias );
// 	PrintLn( "playing loop sound ", alias, " on entity at origin ", self.origin, " at ORIGIN ", org.origin );
	self waittill( "stop sound" + alias );
	org StopLoopSound( alias );
	org Delete();
}

delete_on_removed( ent )
{
	//self ==> the entity you want to wait to be undefined either by death or removed before deleting the ent
	ent endon( "death" );
	
	while( isdefined( self ) )
		wait 0.05;
	
	//self waittill( "death" );
	if ( IsDefined( ent ) )
		ent Delete();
}

save_friendlies()
{
	ai = GetAIArray( "allies" );
	game_characters = 0;
	for ( i = 0; i < ai.size; i++ )
	{
		if ( IsDefined( ai[ i ].script_friendname ) )
			continue;

// 		attachsize =
// 		PrintLn( "attachSize = ", self GetAttachSize() );

		game[ "character" + game_characters ] = ai[ i ] codescripts\character::save();
		game_characters++;
	}

	game[ "total characters" ] = game_characters;
}

 /*
 =============
///ScriptDocBegin
"Name: spawn_failed( <spawn> )"
"Summary: Checks to see if the spawned AI spawned correctly or had errors. Also waits until all spawn initialization is complete. Returns true or false."
"Module: AI"
"CallOn: "
"MandatoryArg: <spawn> : The actor that just spawned"
"Example: spawn_failed( level.price );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
spawn_failed( spawn )
{
	if ( !isalive( spawn ) )
		return true;
	if ( !isdefined( spawn.finished_spawning ) )
		spawn waittill_either( "finished spawning", "death" );

	if ( IsAlive( spawn ) )
		return false;

	return true;
}

spawn_setcharacter( data )
{
	codescripts\character::precache( data );

	self waittill( "spawned", spawn );
	if ( maps\_utility::spawn_failed( spawn ) )
		return;

	PrintLn( "Size is ", data[ "attach" ].size );
	spawn codescripts\character::new();
	spawn codescripts\character::load( data );
}

key_hint_print( message, binding )
{
	// Note that this will insert only the first bound key for the action
	IPrintLnBold( message, binding[ "key1" ] );
}

view_tag( tag )
{
	self endon( "death" );
	for ( ;; )
	{
		maps\_debug::drawTag( tag );
		wait( 0.05 );
	}
}

 /*
 =============
///ScriptDocBegin
"Name: assign_animtree( <animname> )"
"Summary: Assigns the level.scr_animtree for the given animname to self."
"Module: Anim"
"OptionalArg: <animname> : You can optionally assign the animname for self at this juncture."
"Example: model = assign_animtree( "whatever" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

assign_animtree( animname )
{

/*
=============
///ScriptFieldDocBegin
"Name: .animname"
"Summary: animname"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
	if ( IsDefined( animname ) )
		self.animname = animname;

	AssertEx( IsDefined( level.scr_animtree[ self.animname ] ), "There is no level.scr_animtree for animname " + self.animname );
	self UseAnimTree( level.scr_animtree[ self.animname ] );
}

/*
=============
///ScriptDocBegin
"Name: assign_model( <assign_model> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
assign_model()
{
	AssertEx( IsDefined( level.scr_model[ self.animname ] ), "There is no level.scr_model for animname " + self.animname );

	if ( IsArray( level.scr_model[ self.animname ] ) )
	{
		randIndex = RandomInt( level.scr_model[ self.animname ].size );
		self SetModel( level.scr_model[ self.animname ][ randIndex ] );
	}
	else
		self SetModel( level.scr_model[ self.animname ] );
}

 /*
 =============
///ScriptDocBegin
"Name: spawn_anim_model( <animname>, <origin> )"
"Summary: Spawns a script model and gives it the animtree and model associated with that animname"
"Module: Anim"
"MandatoryArg: <animname> : Name of the animname from this map_anim.gsc."
"OptionalArg: <origin> : Optional origin."
"Example: model = spawn_anim_model( "player_rappel" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

spawn_anim_model( animname, origin, angles )
{
	if ( !IsDefined( origin ) )
		origin = ( 0, 0, 0 );
	
	model = Spawn( "script_model", origin );
	model.animname = animname;
	model assign_animtree();
	model assign_model();
	if ( IsDefined( angles ) )
		model.angles = angles;
	return model;
}

 /*
 =============
///ScriptDocBegin
"Name: trigger_wait( <strName> , <strKey> )"
"Summary: Waits until a trigger with the specified key / value is triggered"
"Module: Trigger"
"CallOn: "
"MandatoryArg: <strName> : Name of the key on this trigger"
"MandatoryArg: <strKey> : Key on the trigger to use, example: "targetname" or "script_noteworthy""
"Example: trigger_wait( "player_in_building1", "targetname" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
trigger_wait( strName, strKey )
{
	eTrigger = GetEnt( strName, strKey );
	if ( !isdefined( eTrigger ) )
	{
		AssertMsg( "trigger not found: " + strName + " key: " + strKey );
		return;
	}
	eTrigger waittill( "trigger", eOther );
	level notify( strName, eOther );
	return eOther;
}
 /*
 =============
///ScriptDocBegin
"Name: trigger_wait_targetname( <strName> )"
"Summary: Waits until a trigger with the specified key / value is triggered"
"Module: Trigger"
"CallOn: "
"MandatoryArg: <strKey> : targetname key to waitfor"
"Example: trigger_wait_targetname( "player_in_building1" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

trigger_wait_targetname( strName )
{
	return trigger_wait( strName, "targetname" );
}

/*
=============
///ScriptDocBegin
"Name: set_flag_on_dead( <spawners> , <strFlag> )"
"Summary: the function will set the flag <strFlag> after the actors from <spawners> have spawned and then died "
"Module: AI"
"CallOn: "
"MandatoryArg: <spawners>: the array of spawners"
"MandatoryArg: <strFlag>: the flag to set"
"Example: set_flag_on_dead( spawners, "base_guys_dead" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_flag_on_dead( spawners, strFlag )
{
	thread set_flag_on_func_wait_proc( spawners, strFlag, ::waittill_dead, "set_flag_on_dead" );
}

/*
=============
///ScriptDocBegin
"Name: set_flag_on_dead_or_dying( <spawners> , <strFlag> )"
"Summary: the function will set the flag <strFlag> after the actors from <spawners> have spawned and then are dead or dying ( long death )."
"Module: AI"
"CallOn: "
"MandatoryArg: <spawners>: the array of spawners"
"MandatoryArg: <strFlag>: the flag to set"
"Example: set_flag_on_dead_or_dying( spawners, "base_guys_dead" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_flag_on_dead_or_dying( spawners, strFlag )
{
	thread set_flag_on_func_wait_proc( spawners, strFlag, ::waittill_dead_or_dying, "set_flag_on_dead_or_dying" );
}

/*
=============
///ScriptDocBegin
"Name: set_flag_on_spawned( <spawners> , <strFlag> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_flag_on_spawned( spawners, strFlag )
{
	thread set_flag_on_func_wait_proc( spawners, strFlag, ::empty_func, "set_flag_on_spawned" );
}

empty_func( var )
{
	return;
}

set_flag_on_spawned_ai_proc( system, internal_flag )
{
	self waittill( "spawned", guy );
	if ( maps\_utility::spawn_failed( guy ) )
		return;

	system.ai[ system.ai.size ] = guy;

	self ent_flag_set( internal_flag );
}

set_flag_on_func_wait_proc( spawners, strFlag, waitfunc, internal_flag )
{
	system = SpawnStruct();
	system.ai = [];

	AssertEx( spawners.size, "spawners is empty" );

	foreach ( key, spawn in spawners )
		spawn ent_flag_init( internal_flag );

	array_thread( spawners, ::set_flag_on_spawned_ai_proc, system, internal_flag );

	foreach ( key, spawn in spawners )
		spawn ent_flag_wait( internal_flag );

	[[ waitfunc ]]( system.ai );
	flag_set( strFlag );
}

 /*
 =============
///ScriptDocBegin
"Name: set_flag_on_trigger( <eTrigger> , <strFlag> )"
"Summary: Calls flag_set to set the specified flag when the trigger is triggered"
"Module: Trigger"
"CallOn: "
"MandatoryArg: <eTrigger> : trigger entity to use"
"MandatoryArg: <strFlag> : name of the flag to set"
"Example: set_flag_on_trigger( trig, "player_is_outside" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_flag_on_trigger( eTrigger, strFlag )
{
	if ( !flag( strFlag ) )
	{
		eTrigger waittill( "trigger", eOther );
		flag_set( strFlag );
		return eOther;
	}
}

 /*
 =============
///ScriptDocBegin
"Name: set_flag_on_targetname_trigger( <flag> )"
"Summary: Sets the specified flag when a trigger with targetname < flag > is triggered."
"Module: Trigger"
"CallOn: "
"MandatoryArg: <flag> : name of the flag to set, and also the targetname of the trigger to use"
"Example:  set_flag_on_targetname_trigger( "player_is_outside" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_flag_on_targetname_trigger( msg )
{
	Assert( IsDefined( level.flag[ msg ] ) );
	if ( flag( msg ) )
		return;

	trigger = GetEnt( msg, "targetname" );
	trigger waittill( "trigger" );
	flag_set( msg );
}

 /*
 =============
///ScriptDocBegin
"Name: is_in_array( <aeCollection> , <eFindee> )"
"Summary: Returns true if < eFindee > is an entity in array < aeCollection > . False if it is not. "
"Module: Array"
"CallOn: "
"MandatoryArg: <aeCollection> : array of entities to search through"
"MandatoryArg: <eFindee> : entity to check if it's in the array"
"Example: qBool = is_in_array( eTargets, vehicle1 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
is_in_array( aeCollection, eFindee )
{
	for ( i = 0; i < aeCollection.size; i++ )
	{
		if ( aeCollection[ i ] == eFindee )
			return( true );
	}

	return( false );
}

 /*
 =============
///ScriptDocBegin
"Name: waittill_dead( <guys> , <num> , <timeoutLength> )"
"Summary: Waits until all the AI in array < guys > are dead."
"Module: AI"
"CallOn: "
"MandatoryArg: <guys> : Array of actors to wait until dead"
"OptionalArg: <num> : Number of guys that must die for this function to continue"
"OptionalArg: <timeoutLength> : Number of seconds before this function times out and continues"
"Example: waittill_dead( GetAIArray( "axis" ) );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
waittill_dead( guys, num, timeoutLength )
{
	// verify the living - ness of the ai
	/#
	allAlive = true;
	foreach ( member in guys )
	{
		if ( IsAlive( member ) )
			continue;
		allAlive = false;
		break;
	}
	AssertEx( allAlive, "Waittill_Dead was called with dead or removed AI in the array, meaning it will never pass." );
	if ( !allAlive )
	{
		newArray = [];
		foreach ( member in guys )
		{
			if ( IsAlive( member ) )
				newArray[ newArray.size ] = member;
		}
		guys = newArray;
	}
	#/

	ent = SpawnStruct();
	if ( IsDefined( timeoutLength ) )
	{
		ent endon( "thread_timed_out" );
		ent thread waittill_dead_timeout( timeoutLength );
	}

	ent.count = guys.size;
	if ( IsDefined( num ) && num < ent.count )
		ent.count = num;
	array_thread( guys, ::waittill_dead_thread, ent );

	while ( ent.count > 0 )
		ent waittill( "waittill_dead guy died" );
}

 /*
 =============
///ScriptDocBegin
"Name: waittill_dead_or_dying( <guys> , <num> , <timeoutLength> )"
"Summary: Similar to waittill_dead(). Waits until all the AI in array < guys > are dead OR dying (long deaths)."
"Module: AI"
"CallOn: "
"MandatoryArg: <guys> : Array of actors to wait until dead or dying"
"OptionalArg: <num> : Number of guys that must die or be dying for this function to continue"
"OptionalArg: <timeoutLength> : Number of seconds before this function times out and continues"
"Example: waittill_dead_or_dying( GetAIArray( "axis" ) );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
waittill_dead_or_dying( guys, num, timeoutLength )
{
	// verify the living - ness and healthy - ness of the ai
	newArray = [];
	foreach ( member in guys )
	{
		if ( IsAlive( member ) && !member.ignoreForFixedNodeSafeCheck )
			newArray[ newArray.size ] = member;
	}
	guys = newArray;

	ent = SpawnStruct();
	if ( IsDefined( timeoutLength ) )
	{
		ent endon( "thread_timed_out" );
		ent thread waittill_dead_timeout( timeoutLength );
	}

	ent.count = guys.size;

	// optional override on count
	if ( IsDefined( num ) && num < ent.count )
		ent.count = num;

	array_thread( guys, ::waittill_dead_or_dying_thread, ent );

	while ( ent.count > 0 )
		ent waittill( "waittill_dead_guy_dead_or_dying" );
}

waittill_dead_thread( ent )
{
	self waittill( "death" );
	ent.count--;
	ent notify( "waittill_dead guy died" );
}

waittill_dead_or_dying_thread( ent )
{
	self waittill_either( "death", "pain_death" );
	ent.count--;
	ent notify( "waittill_dead_guy_dead_or_dying" );
}

waittill_dead_timeout( timeoutLength )
{
	wait( timeoutLength );
	self notify( "thread_timed_out" );
}

waittill_aigroupcleared( aigroup )
{
	while ( level._ai_group[ aigroup ].spawnercount || level._ai_group[ aigroup ].aicount )
		wait( 0.25 );
}

waittill_aigroupcount( aigroup, count )
{
	while ( level._ai_group[ aigroup ].spawnercount + level._ai_group[ aigroup ].aicount > count )
		wait( 0.25 );
}


get_ai_group_count( aigroup )
{
	return( level._ai_group[ aigroup ].spawnercount + level._ai_group[ aigroup ].aicount );
}

get_ai_group_sentient_count( aigroup )
{
	return( level._ai_group[ aigroup ].aicount );
}

get_ai_group_ai( aigroup )
{
	aiSet = [];
	for ( index = 0; index < level._ai_group[ aigroup ].ai.size; index++ )
	{
		if ( !isAlive( level._ai_group[ aigroup ].ai[ index ] ) )
			continue;

		aiSet[ aiSet.size ] = level._ai_group[ aigroup ].ai[ index ];
	}

	return( aiSet );
}

 /*
 =============
///ScriptDocBegin
"Name: waittill_notetrack_or_damage( <notetrack> )"
"Summary: Waits until the entity hits a certain notetrack or is damaged or killed"
"Module: AI"
"CallOn: An AI"
"MandatoryArg: <notetrack> : the notetrack to wait for"
"Example: self waittill_notetrack_or_damage( "bodyfall large" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

waittill_notetrack_or_damage( notetrack )
{
	self endon( "damage" );
	self endon( "death" );
	self waittillmatch( "single anim", notetrack );
}

 /*
 =============
///ScriptDocBegin
"Name: get_living_ai( <name> , <type> )"
"Summary: Returns single spawned ai in the level of <name> and <type>. Error if used on more than one ai with same name and type "
"Module: AI"
"CallOn: "
"MandatoryArg: <name> : the value of the targetname or script_noteworthy of the ai"
"MandatoryArg: <type> : valid types are targetname and script_noteworthy"
"Example: patroller = get_living_ai( "patrol", "script_noteworthy" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_living_ai( name, type )
{
	array = get_living_ai_array( name, type );
	if ( array.size > 1 )
	{
		AssertMsg( "get_living_ai used for more than one living ai of type " + type + " called " + name + "." );
		return undefined;
	}
	return array[ 0 ];
}

 /*
 =============
///ScriptDocBegin
"Name: get_living_ai_array( <name> , <type> )"
"Summary: Returns array of spawned ai in the level of <name> and <type> "
"Module: AI"
"CallOn: "
"MandatoryArg: <name> : the value of the targetname or script_noteworthy of the ai"
"MandatoryArg: <type> : valid types are targetname and script_noteworthy"
"Example: patrollers = get_living_ai_array( "patrol", "script_noteworthy" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_living_ai_array( name, type )
{
	ai = GetAISpeciesArray( "all", "all" );

	array = [];
	foreach ( actor in ai )
	{
		if ( !IsAlive( actor ) )
			continue;

		switch( type )
		{
			case "targetname":{
				if ( IsDefined( actor.targetname ) && actor.targetname == name )
					array[ array.size ] = actor;
			}break;
		 	case "script_noteworthy":{
				if ( IsDefined( actor.script_noteworthy ) && actor.script_noteworthy == name )
					array[ array.size ] = actor;
			}break;
		}
	}
	return array;
}
 /*
 =============
///ScriptDocBegin
"Name: get_vehicle( <name> , <type> )"
"Summary: Returns the vehicle spawned from the SHIFT-V radient menu of <name> and <type> "
"Module: Vehicles"
"CallOn: "
"MandatoryArg: <name> : the value of the targetname or script_noteworthy of the vehicle"
"MandatoryArg: <type> : valid types are targetname and script_noteworthy"
"Example: patrollers = get_vehicle( "heli_1", "script_noteworthy" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_vehicle( name, type )
{
	Assert( IsDefined( name ) );
	Assert( IsDefined( type ) );
	array = get_vehicle_array( name, type );
	if ( !array.size )
		return undefined;

	AssertEx( array.size == 1, "tried to get_vehicle() on vehicles with key-pair: " + name + "," + type );
	return array[ 0 ];
}

 /*
 =============
///ScriptDocBegin
"Name: get_vehicle_array( <name> , <type> )"
"Summary: Returns the vehicles spawned from the SHIFT-V radient menu of <name> and <type> "
"Module: Vehicles"
"CallOn: "
"MandatoryArg: <name> : the value of the targetname or script_noteworthy of the vehicle"
"MandatoryArg: <type> : valid types are targetname and script_noteworthy"
"Example: helis = get_vehicle_array( "tower_helis", "script_noteworthy" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_vehicle_array( name, type )
{
	array = GetEntArray( name, type );
	vehicle = [];

	merge_array = [];

	foreach ( object in array )
	{
		if ( object.code_classname != "script_vehicle" )
			continue;
		merge_array[ 0 ] = object;
		//since vehicles lose their targetname return the last vehicle spawned from the spawner.
		if ( IsSpawner( object ) )
		{
			if ( IsDefined( object.last_spawned_vehicle ) )
			{
				merge_array[ 0 ] = object.last_spawned_vehicle;
				vehicle = array_merge( vehicle, merge_array );// least tricky way for me to do this.
			}
			continue;
		}
		vehicle = array_merge( vehicle, merge_array );// least tricky way for me to do this.
	}
	return vehicle;
}
 /*
 =============
///ScriptDocBegin
"Name: get_living_aispecies( <name> , <type>, <breed> )"
"Summary: Returns single spawned ai in the level of <name> and <type>. Error if used on more than one ai with same name and type "
"Module: AI"
"CallOn: "
"MandatoryArg: <name> : the value of the targetname or script_noteworthy of the ai"
"MandatoryArg: <type> : valid types are targetname and script_noteworthy"
"OptionalArg: <bread> : the breadof spieces, if none is given, defaults to 'all' "
"Example: patroller = get_living_aispecies( "patrol", "script_noteworthy", "dog" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_living_aispecies( name, type, breed )
{
	array = get_living_aispecies_array( name, type, breed );
	if ( array.size > 1 )
	{
		AssertMsg( "get_living_aispecies used for more than one living ai of type " + type + " called " + name + "." );
		return undefined;
	}
	return array[ 0 ];
}

 /*
 =============
///ScriptDocBegin
"Name: get_living_aispecies_array( <name> , <type>, <breed> )"
"Summary: Returns array of spawned ai of any speices in the level of <name>, <type>, and <breed> "
"Module: AI"
"CallOn: "
"MandatoryArg: <name> : the value of the targetname or script_noteworthy of the ai"
"MandatoryArg: <type> : valid types are targetname and script_noteworthy"
"OptionalArg: <bread> : the breadof spieces, if none is given, defaults to 'all' "
"Example: patrollers = get_living_aispecies_array( "patrol", "script_noteworthy", "dog" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_living_aispecies_array( name, type, breed )
{
	if ( !isdefined( breed ) )
		breed = "all";

	ai = GetAISpeciesArray( "allies", breed );
	ai = array_combine( ai, GetAISpeciesArray( "axis", breed ) );

	array = [];
	for ( i = 0; i < ai.size; i++ )
	{
		switch( type )
		{
			case "targetname":{
				if ( IsDefined( ai[ i ].targetname ) && ai[ i ].targetname == name )
					array[ array.size ] = ai[ i ];
			}break;
		 	case "script_noteworthy":{
				if ( IsDefined( ai[ i ].script_noteworthy ) && ai[ i ].script_noteworthy == name )
					array[ array.size ] = ai[ i ];
			}break;
		}
	}
	return array;
}

// Creates an event based on this message if none exists, and sets it to true after the delay.
gather_delay_proc( msg, delay )
{
	if ( IsDefined( level.gather_delay[ msg ] ) )
	{
		if ( level.gather_delay[ msg ] )
		{
			wait( 0.05 );
			if ( IsAlive( self ) )
				self notify( "gather_delay_finished" + msg + delay );
			return;
		}

		level waittill( msg );
		if ( IsAlive( self ) )
			self notify( "gather_delay_finished" + msg + delay );
		return;
	}

	level.gather_delay[ msg ] = false;
	wait( delay );
	level.gather_delay[ msg ] = true;
	level notify( msg );
	if ( IsAlive( self ) )
		self notify( "gat    her_delay_finished" + msg + delay );
}

gather_delay( msg, delay )
{
	thread gather_delay_proc( msg, delay );
	self waittill( "gather_delay_finished" + msg + delay );
}

death_waiter( notifyString )
{
	self waittill( "death" );
	level notify( notifyString );
}

getchar( num )
{
	if ( num == 0 )
		return "0";
	if ( num == 1 )
		return "1";
	if ( num == 2 )
		return "2";
	if ( num == 3 )
		return "3";
	if ( num == 4 )
		return "4";
	if ( num == 5 )
		return "5";
	if ( num == 6 )
		return "6";
	if ( num == 7 )
		return "7";
	if ( num == 8 )
		return "8";
	if ( num == 9 )
		return "9";
}

getlinks_array( array, linkMap )// don't pass stuff through as an array of struct.linkname[] but only linkMap[]
{
	ents = [];
	for ( j = 0; j < array.size; j++ )
	{
		node = array[ j ];
		script_linkname = node.script_linkname;
		if ( !isdefined( script_linkname ) )
			continue;
		if ( !isdefined( linkMap[ script_linkname ] ) )
			continue;
		ents[ ents.size ] = node;
	}
	return ents;
}

// Adds only things that are new to the array.
// Requires the arrays to be of node with script_linkname defined.
/*
=============
///ScriptDocBegin
"Name: array_merge_links( <array1> , <array2> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_merge_links( array1, array2 )
{
	if ( !array1.size )
		return array2;
	if ( !array2.size )
		return array1;

	linkMap = [];

	for ( i = 0; i < array1.size; i++ )
	{
		node = array1[ i ];
		linkMap[ node.script_linkName ] = true;
	}

	for ( i = 0; i < array2.size; i++ )
	{
		node = array2[ i ];
		if ( IsDefined( linkMap[ node.script_linkName ] ) )
			continue;
		linkMap[ node.script_linkName ] = true;
		array1[ array1.size ] = node;
	}

	return array1;
}

 /*
 =============
///ScriptDocBegin
"Name: array_merge( <array1> , <array2> )"
"Summary: Combines the two arrays and returns the resulting array. Adds only things that are new to the array, no duplicates."
"Module: Array"
"CallOn: "
"MandatoryArg: <array1> : first array"
"MandatoryArg: <array2> : second array"
"Example: combinedArray = array_merge( array1, array2 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
array_merge( array1, array2 )// adds only things that are new to the array
{
	if ( array1.size == 0 )
		return array2;
	if ( array2.size == 0 )
		return array1;
	newarray = array1;
	foreach ( array2_ent in array2 )
	{
		foundmatch = false;

		foreach ( array1_ent in array1 )
		{
			if ( array1_ent == array2_ent )
			{
				foundmatch = true;
				break;
			}
		}
		if ( foundmatch )
			continue;
		else
			newarray[ newarray.size ] = array2_ent;
	}
	return newarray;
}

 /*
 =============
///ScriptDocBegin
"Name: array_exclude( <array> , <arrayExclude> )"
"Summary: Returns an array excluding all members of < arrayExclude > "
"Module: Array"
"CallOn: "
"MandatoryArg: <array> : Array containing all items"
"MandatoryArg: <arrayExclude> : Arary containing all items to remove"
"Example: newArray = array_exclude( array1, array2 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
array_exclude( array, arrayExclude )// returns "array" minus all members of arrayExclude
{
	newarray = array;
	for ( i = 0; i < arrayExclude.size; i++ )
	{
		if ( is_in_array( array, arrayExclude[ i ] ) )
			newarray = array_remove( newarray, arrayExclude[ i ] );
	}

	return newarray;
}

/*
=============
///ScriptDocBegin
"Name: array_compare( <array1> , <array2> )"
"Summary: checks whether 2 arrays are equal.  Returns true if they are, false if they are not"
"Module: Array"
"CallOn: "
"MandatoryArg: <array1>: the first array to compare "
"MandatoryArg: <array2>: the second array to compare "
"Example:  if( array_compare( array1, array2 ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_compare( array1, array2 )
{
	if ( array1.size != array2.size )
		return false;

	foreach ( key, member in array1 )
	{
		if ( !isdefined( array2[ key ] ) )
			return false;

		member2 = array2[ key ];

		if ( member2 != member )
			return false;
	}

	return true;
}

/* 
 ============= 
///ScriptDocBegin
	
"Name: getLinkedVehicleNodes()"
"Summary: Returns an array of vehicle nodes that SELF is linked to"
"Module: Utility"
"CallOn: An entity that links to vehicle nodes"
"Example: spawners = heli get_linked_ents()"
"SPMP: both"
///ScriptDocEnd
=============
*/
getLinkedVehicleNodes()
{
	array = [];

	if ( IsDefined( self.script_linkTo ) )
	{
		linknames = get_links();
		foreach ( name in linknames )
		{
			entities = GetVehicleNodeArray( name, "script_linkname" );
			array = array_combine( array, entities );
		}
	}

	return array;
}




 /*
 =============
///ScriptDocBegin
"Name: draw_line( <org1> , <org2> , <r> , <g> , <b> )"
"Summary: Draws a line from < org1 > to < org2 > in the specified color forever"
"Module: Debug"
"CallOn: "
"MandatoryArg: <org1> : starting origin for the line"
"MandatoryArg: <org2> : ending origin for the line"
"MandatoryArg: <r> : red color value( 0 to 1 )"
"MandatoryArg: <g> : green color value( 0 to 1 )"
"MandatoryArg: <b> : blue color value( 0 to 1 )"
"Example: thread draw_line( level.player.origin, vehicle.origin, 1, 0, 0 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
draw_line( org1, org2, r, g, b )
{
	while ( 1 )
	{
		Line( org1, org2, ( r, g, b ), 1 );
		wait .05;
	}

}

 /*
 =============
///ScriptDocBegin
"Name: draw_line_to_ent_for_time( <org1> , <ent> , <r> , <g> , <b> , <timer> )"
"Summary: Draws a line from < org1 > to < ent > origin in the specified color for the specified duration. Updates to the entities origin each frame."
"Module: Debug"
"CallOn: "
"MandatoryArg: <org1> : starting origin for the line"
"MandatoryArg: <ent> : entity to draw line to"
"MandatoryArg: <r> : red color value( 0 to 1 )"
"MandatoryArg: <g> : green color value( 0 to 1 )"
"MandatoryArg: <b> : blue color value( 0 to 1 )"
"MandatoryArg: <timer> : time in seconds the line should last"
"Example: thread draw_line_to_ent_for_time( level.player.origin, vehicle, 1, 0, 0, 10.0 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
draw_line_to_ent_for_time( org1, ent, r, g, b, timer )
{
	timer = GetTime() + ( timer * 1000 );
	while ( GetTime() < timer )
	{
		Line( org1, ent.origin, ( r, g, b ), 1 );
		wait .05;
		if ( !isdefined( ent ) || !isdefined( ent.origin ) )
			return;
	}

}

/*
=============
///ScriptDocBegin
"Name: draw_line_from_ent_for_time( <ent> , <org> , <r> , <g> , <b> , <timer> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
draw_line_from_ent_for_time( ent, org, r, g, b, timer )
{
	draw_line_to_ent_for_time( org, ent, r, g, b, timer );
}

 /*
 =============
///ScriptDocBegin
"Name: draw_line_from_ent_to_ent_for_time( <ent1> , <ent2> , <r> , <g> , <b> , <timer> )"
"Summary: Draws a line from one entity origin to another entity origin in the specified color for the specified duration. Updates to the entities origin each frame."
"Module: Debug"
"CallOn: "
"MandatoryArg: <ent1> : entity to draw line from"
"MandatoryArg: <ent2> : entity to draw line to"
"MandatoryArg: <r> : red color value( 0 to 1 )"
"MandatoryArg: <g> : green color value( 0 to 1 )"
"MandatoryArg: <b> : blue color value( 0 to 1 )"
"MandatoryArg: <timer> : time in seconds the line should last"
"Example: thread draw_line_from_ent_to_ent_for_time( level.player, vehicle, 1, 0, 0, 10.0 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
draw_line_from_ent_to_ent_for_time( ent1, ent2, r, g, b, timer )
{
	ent1 endon( "death" );
	ent2 endon( "death" );

	timer = GetTime() + ( timer * 1000 );
	while ( GetTime() < timer )
	{
		Line( ent1.origin, ent2.origin, ( r, g, b ), 1 );
		wait .05;
	}

}

 /*
 =============
///ScriptDocBegin
"Name: draw_line_from_ent_to_ent_until_notify( <ent1> , <ent2> , <r> , <g> , <b> , <notifyEnt> , <notifyString> )"
"Summary: Draws a line from one entity origin to another entity origin in the specified color until < notifyEnt > is notified < notifyString > . Updates to the entities origin each frame."
"Module: Debug"
"CallOn: "
"MandatoryArg: <ent1> : entity to draw line from"
"MandatoryArg: <ent2> : entity to draw line to"
"MandatoryArg: <r> : red color value( 0 to 1 )"
"MandatoryArg: <g> : green color value( 0 to 1 )"
"MandatoryArg: <b> : blue color value( 0 to 1 )"
"MandatoryArg: <notifyEnt> : entity that waits for the notify"
"MandatoryArg: <notifyString> : notify string that will make the line stop being drawn"
"Example: thread draw_line_from_ent_to_ent_until_notify( level.player, guy, 1, 0, 0, guy, "anim_on_tag_done" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
draw_line_from_ent_to_ent_until_notify( ent1, ent2, r, g, b, notifyEnt, notifyString )
{
	Assert( IsDefined( notifyEnt ) );
	Assert( IsDefined( notifyString ) );

	ent1 endon( "death" );
	ent2 endon( "death" );

	notifyEnt endon( notifyString );

	while ( 1 )
	{
		Line( ent1.origin, ent2.origin, ( r, g, b ), 0.05 );
		wait .05;
	}

}

 /*
 =============
///ScriptDocBegin
"Name: draw_line_until_notify( <org1> , <org2> , <r> , <g> , <b> , <notifyEnt> , <notifyString> )"
"Summary: Draws a line from < org1 > to < org2 > in the specified color until < notifyEnt > is notified < notifyString > "
"Module: Debug"
"CallOn: "
"MandatoryArg: <org1> : starting origin for the line"
"MandatoryArg: <org2> : ending origin for the line"
"MandatoryArg: <r> : red color value( 0 to 1 )"
"MandatoryArg: <g> : green color value( 0 to 1 )"
"MandatoryArg: <b> : blue color value( 0 to 1 )"
"MandatoryArg: <notifyEnt> : entity that waits for the notify"
"MandatoryArg: <notifyString> : notify string that will make the line stop being drawn"
"Example: thread draw_line_until_notify( self.origin, targetLoc, 1, 0, 0, self, "stop_drawing_line" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
draw_line_until_notify( org1, org2, r, g, b, notifyEnt, notifyString )
{
	Assert( IsDefined( notifyEnt ) );
	Assert( IsDefined( notifyString ) );

	notifyEnt endon( notifyString );

	while ( 1 )
	{
		draw_line_for_time( org1, org2, r, g, b, 0.05 );
	}
}

 /*
 =============
///ScriptDocBegin
"Name: draw_line_from_ent_to_vec_for_time( <ent> , <vec> , <length> , <r> , <g> , <b> , <timer> )"
"Summary: Draws a line from < org1 > to < ent > origin in the specified color for the specified duration. Updates to the entities origin each frame."
"Module: Debug"
"CallOn: "
"MandatoryArg: <ent> : entity to draw line to"
"MandatoryArg: <vec> : vector to draw the line at"
"MandatoryArg: <length> : length of the vector line"
"MandatoryArg: <r> : red color value( 0 to 1 )"
"MandatoryArg: <g> : green color value( 0 to 1 )"
"MandatoryArg: <b> : blue color value( 0 to 1 )"
"MandatoryArg: <timer> : time in seconds the line should last"
"Example: thread draw_line_from_ent_to_vec_for_time( vehicle, ( 0, 1, 0 ), 10, 1, 0, 0, 10.0 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
draw_line_from_ent_to_vec_for_time( ent, vec, length, r, g, b, timer )
{
	timer = GetTime() + ( timer * 1000 );
	vec *= length;
	while ( GetTime() < timer )
	{
		Line( ent.origin, ent.origin + vec, ( r, g, b ), 1 );
		wait .05;
		if ( !isdefined( ent ) || !isdefined( ent.origin ) )
			return;
	}
}

 /*
 =============
///ScriptDocBegin
"Name: draw_circle_until_notify( <center> , <radius> , <r> , <g> , <b> , <notifyEnt> , <notifyString> )"
"Summary: Draws a circle at < center > with < radius > in the specified color until < notifyEnt > is notified < notifyString > "
"Module: Debug"
"CallOn: "
"MandatoryArg: <center> : origin center of the circle"
"MandatoryArg: <radius> : radius of the circle to draw"
"MandatoryArg: <r> : red color value( 0 to 1 )"
"MandatoryArg: <g> : green color value( 0 to 1 )"
"MandatoryArg: <b> : blue color value( 0 to 1 )"
"MandatoryArg: <notifyEnt> : entity that waits for the notify"
"MandatoryArg: <notifyString> : notify string that will make the line stop being drawn"
"Example: thread draw_circle_until_notify( self.origin, self.radius, 1, 0, 0, self, "stop_drawing_circle" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
draw_circle_until_notify( center, radius, r, g, b, notifyEnt, notifyString, optionalSides )
{
	if( IsDefined(optionalSides))
		circle_sides = optionalSides;
	else
	circle_sides = 16;

	angleFrac = 360 / circle_sides;

	// Z circle
	circlepoints = [];
	for ( i = 0; i < circle_sides; i++ )
	{
		angle = ( angleFrac * i );
		xAdd = Cos( angle ) * radius;
		yAdd = Sin( angle ) * radius;
		x = center[ 0 ] + xAdd;
		y = center[ 1 ] + yAdd;
		z = center[ 2 ];
		circlepoints[ circlepoints.size ] = ( x, y, z );
	}
	thread draw_circle_lines_until_notify( circlepoints, r, g, b, notifyEnt, notifyString );
	/*
	// X circle
	circlepoints = [];
	for ( i = 0 ; i < circle_sides ; i++ )
	{
		angle = ( angleFrac * i );
		xAdd = Cos( angle ) * radius;
		yAdd = Sin( angle ) * radius;
		x = center[ 0 ];
		y = center[ 1 ] + xAdd;
		z = center[ 2 ] + yAdd;
		circlepoints[ circlepoints.size ] = ( x, y, z );
	}
	thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center );

	// Y circle
	circlepoints = [];
	for ( i = 0 ; i < circle_sides ; i++ )
	{
		angle = ( angleFrac * i );
		xAdd = Cos( angle ) * radius;
		yAdd = Sin( angle ) * radius;
		x = center[ 0 ] + yAdd;
		y = center[ 1 ];
		z = center[ 2 ] + xAdd;
		circlepoints[ circlepoints.size ] = ( x, y, z );
	}
	thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center );
	*/
}

draw_circle_for_time( center, radius, r, g, b, time )
{
	circle_sides = 16;

	angleFrac = 360 / circle_sides;

	// Z circle
	circlepoints = [];
	for ( i = 0; i < circle_sides; i++ )
	{
		angle = ( angleFrac * i );
		xAdd = Cos( angle ) * radius;
		yAdd = Sin( angle ) * radius;
		x = center[ 0 ] + xAdd;
		y = center[ 1 ] + yAdd;
		z = center[ 2 ];
		circlepoints[ circlepoints.size ] = ( x, y, z );
	}
	thread draw_circle_lines_for_time( circlepoints, r, g, b, time );
	/*
	// X circle
	circlepoints = [];
	for ( i = 0 ; i < circle_sides ; i++ )
	{
		angle = ( angleFrac * i );
		xAdd = Cos( angle ) * radius;
		yAdd = Sin( angle ) * radius;
		x = center[ 0 ];
		y = center[ 1 ] + xAdd;
		z = center[ 2 ] + yAdd;
		circlepoints[ circlepoints.size ] = ( x, y, z );
	}
	thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center );

	// Y circle
	circlepoints = [];
	for ( i = 0 ; i < circle_sides ; i++ )
	{
		angle = ( angleFrac * i );
		xAdd = Cos( angle ) * radius;
		yAdd = Sin( angle ) * radius;
		x = center[ 0 ] + yAdd;
		y = center[ 1 ];
		z = center[ 2 ] + xAdd;
		circlepoints[ circlepoints.size ] = ( x, y, z );
	}
	thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center );
	*/
}

draw_circle_lines_for_time( circlepoints, r, g, b, time)
{
	for ( i = 0; i < circlepoints.size; i++ )
	{
		start = circlepoints[ i ];
		if ( i + 1 >= circlepoints.size )
			end = circlepoints[ 0 ];
		else
			end = circlepoints[ i + 1 ];

		thread draw_line_for_time( start, end, r, g, b, time );
	}
}

draw_circle_lines_until_notify( circlepoints, r, g, b, notifyEnt, notifyString )
{
	for ( i = 0; i < circlepoints.size; i++ )
	{
		start = circlepoints[ i ];
		if ( i + 1 >= circlepoints.size )
			end = circlepoints[ 0 ];
		else
			end = circlepoints[ i + 1 ];

		thread draw_line_until_notify( start, end, r, g, b, notifyEnt, notifyString );
	}
}

clear_enemy_passthrough()
{
	self notify( "enemy" );
	self ClearEnemy();
}

 /*
 =============
///ScriptDocBegin
"Name: battlechatter_off( <team> )"
"Summary: Disable battlechatter for the specified team"
"Module: Battlechatter"
"CallOn: "
"MandatoryArg: <team> : team to disable battlechatter on"
"Example: battlechatter_off( "allies" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
battlechatter_off( team )
{
	level notify( "battlechatter_off_thread" );

	maps\_dds::dds_disable( team );	
	
	animscripts\battlechatter::bcs_setup_chatter_toggle_array();

	if ( IsDefined( team ) )
	{
		set_battlechatter_variable( team, false );
		soldiers = GetAIArray( team );
	}
	else
	{
		foreach ( team in anim.teams )
		{
			set_battlechatter_variable( team, false );
		}

		soldiers = GetAIArray();
	}

	if ( !isDefined( anim.chatInitialized ) || !anim.chatInitialized )
		return;

	for ( index = 0; index < soldiers.size; index++ )
		soldiers[ index ].battlechatter = false;

	for ( index = 0; index < soldiers.size; index++ )
	{
		soldier = soldiers[ index ];
		if ( !isalive( soldier ) )
			continue;

		if ( !soldier.chatInitialized )
			continue;

		if ( !soldier.isSpeaking )
			continue;

		soldier wait_until_done_speaking();
	}

	speakDiff = GetTime() - anim.lastTeamSpeakTime[ "allies" ];

	if ( speakDiff < 1500 )
		wait( speakDiff / 1000 );

	if ( IsDefined( team ) )
		level notify( team + " done speaking" );
	else
		level notify( "done speaking" );
}

 /*
 =============
///ScriptDocBegin
"Name: battlechatter_on( <team> )"
"Summary: Enable battlechatter for the specified team"
"Module: Battlechatter"
"CallOn: "
"MandatoryArg: <team> : team to enable battlechatter on"
"Example: battlechatter_on( "allies" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
battlechatter_on( team )
{
	thread battlechatter_on_thread( team );

	maps\_dds::dds_enable( team );
}

battlechatter_on_thread( team )
{
	level endon( "battlechatter_off_thread" );

	animscripts\battlechatter::bcs_setup_chatter_toggle_array();

	while ( !IsDefined( anim.chatInitialized ) )
	{
		wait( 0.05 );
	}

	flag_set( "battlechatter_on_thread_waiting" );

	// buffer time
	wait( 1.5 );

	flag_clear( "battlechatter_on_thread_waiting" );

	if ( IsDefined( team ) )
	{
		set_battlechatter_variable( team, true );
		soldiers = GetAIArray( team );
	}
	else
	{
		foreach ( team in anim.teams )
		{
			set_battlechatter_variable( team, true );
		}
		soldiers = GetAIArray();
	}

	for ( index = 0; index < soldiers.size; index++ )
		soldiers[ index ] set_battlechatter( true );
}

/*
=============
///ScriptDocBegin
"Name: set_battlechatter( <state> )"
"Summary: Turns an AI's battlechatter on/off.  Will be overridden if a spawner's .script_bcdialog is set to 0."
"Module: Battlechatter"
"CallOn: An AI"
"MandatoryArg: <state>: True/false, describes whether battlechatter should be on or off for this AI"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_battlechatter( bEnable )
{
	dds_exclude_this_ai( !bEnable );

	if ( !IsDefined(anim.chatInitialized) || !anim.chatInitialized )
		return;

	if ( self.type == "dog" )
		return;

	if ( bEnable )
	{
		if ( IsDefined( self.script_bcdialog ) && !self.script_bcdialog )
			self.battlechatter = false;
		else
			self.battlechatter = true;
	}
	else
	{
		self.battlechatter = false;

		if ( IsDefined( self.isSpeaking ) && self.isSpeaking )
			self waittill( "done speaking" );
	}
}
/*
=============
///ScriptDocBegin
"Name: set_team_bcvoice( <team>, <newvoice> )"
"Summary: Changes all the battlechatter voices for a certain team.  Useful for switching between loud/stealth chatter."
"Module: Battlechatter"
"CallOn: "
"MandatoryArg: <team>: Allies/Axis/Team3, which team should change voices"
"MandatoryArg: <newvoice>: What voice should they use.  See 'anim.countryIDs' keys for valid voices"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_team_bcvoice( team, newvoice )
{
	if ( !anim.chatInitialized )
		return;
		
	supported_voicetypes = GetArrayKeys( anim.countryIDs );
	in_supported_voicetypes = array_contains( supported_voicetypes, newvoice );
	assertEx( in_supported_voicetypes, "Tried to change ai's voice to " + newvoice + " but that voicetype is not supported!" );
	
	if ( !in_supported_voicetypes )
		return;
		
	allies = GetAIArray( team );	
	foreach( ai in allies )
	{
		ai set_ai_bcvoice( newvoice );
		waitframe(); // watiframe so it doesn't hitch
	}
}

/*
=============
///ScriptDocBegin
"Name: set_ai_bcvoice( <newvoice> )"
"Summary: Changes all the battlechatter voices for a single ai.  Useful for switching between loud/stealth chatter."
"Module: Battlechatter"
"CallOn: an AI"
"MandatoryArg: <newvoice>: What voice should they use.  See 'anim.countryIDs' keys for valid voices"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_ai_bcvoice( newvoice )
{
	if ( !anim.chatInitialized )
		return;
		
	supported_voicetypes = GetArrayKeys( anim.countryIDs );
	in_supported_voicetypes = array_contains( supported_voicetypes, newvoice );
	assertEx( in_supported_voicetypes, "Tried to change ai's voice to " + newvoice + " but that voicetype is not supported!" );
	
	if ( !in_supported_voicetypes )
		return;

	if ( self.type == "dog" )
		return;

	if ( IsDefined( self.isSpeaking ) && self.isSpeaking )
	{
		self waittill( "done speaking" );
		wait( 0.1 );
	}
	
	self animscripts\battlechatter_ai::removeFromSystem();
	waittillframeend;
	self.voice = newvoice;
	self animscripts\battlechatter_ai::addToSystem();
}

/*
=============
///ScriptDocBegin
"Name: flavorbursts_on( <team> )"
"Summary: Gives all the AIs on a team the ability to play flavor burst transmissions.  (Only US allies can use FBTs.)  Note: if the battlechatter system is not working, the flavorbursts will not work even if the AIs have this set on them."
"Module: Battlechatter"
"CallOn: "
"OptionalArg: <team>: which team? Usually, only allies have flavorbursts."
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
flavorbursts_on( team )
{
	thread set_flavorbursts_team_state( true, team );
}

/*
=============
///ScriptDocBegin
"Name: flavorbursts_off( <team> )"
"Summary: Removes the ability to play flavor burst transmissions from all AIs on a team.  (Only US allies can use FBTs.)"
"Module: Battlechatter"
"CallOn: "
"OptionalArg: <team>: which team? Usually, only allies have flavorbursts."
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
flavorbursts_off( team )
{
	thread set_flavorbursts_team_state( false, team );
}

set_flavorbursts_team_state( state, team )
{
	if ( !IsDefined( team ) )
	{
		team = "allies";
	}

	if ( !anim.chatInitialized )
	{
		return;
	}

	// buffer time
	wait( 1.5 );

	level.flavorbursts[ team ] = state;

	guys = [];
	guys = GetAIArray( team );

	array_thread( guys, ::set_flavorbursts, state );
}

/*
=============
///ScriptDocBegin
"Name: set_flavorbursts( <state> )"
"Summary: Turns battlechatter flavor burst transmissions for an AI on or off"
"Module: Entity"
"CallOn: An AI"
"MandatoryArg: <state> "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_flavorbursts( state )
{
	self.flavorbursts = state;
}

/*
=============
///ScriptDocBegin
"Name: friendlyfire_warnings_off()"
"Summary: Disables player-originated friendly fire warnings for all allied AI."
"Module: Entity"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
friendlyfire_warnings_off()
{
	ais = GetAiArray( "allies" );

	foreach( guy in ais )
	{
		if( IsAlive( guy ) )
		{
			guy set_friendlyfire_warnings( false );
		}
	}

	level.friendlyfire_warnings = false;
}

/*
=============
///ScriptDocBegin
"Name: friendlyfire_warnings_on()"
"Summary: Enables player-originated friendly fire warnings for all allied AI."
"Module: Entity"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
friendlyfire_warnings_on()
{
	ais = GetAiArray( "allies" );

	foreach( guy in ais )
	{
		if( IsAlive( guy ) )
		{
			guy set_friendlyfire_warnings( true );
		}
	}

	level.friendlyfire_warnings = true;
}

/*
=============
///ScriptDocBegin
"Name: set_friendlyfire_warnings( <state> )"
"Summary: Turns player-originated friendly fire warnings for an AI on or off"
"Module: Entity"
"CallOn: An AI"
"MandatoryArg: <state> "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_friendlyfire_warnings( state )
{
	if( state )
	{
		self.friendlyfire_warnings_disable = undefined;
	}
	else
	{
		self.friendlyfire_warnings_disable = true;
	}
}


// Newvillers objective management
 /*
	level.currentObjective = "obj1";// disables non obj1 friendly chains if you're using newvillers style friendlychains
	objEvent = get_obj_event( "center_house" );// a trigger with targetname objective_event and a script_deathchain value

	objEvent waittill_objectiveEvent();// this waits until the AI with the event's script_deathchain are dead,
											then waits for trigger from the player. If it targets a friendly chain then it'll
											make the friendlies go to the chain.
 */



/*
"Name: dds_set_player_character_name( <hero_name> )"
"Summary: Sets the player character name in DDS so correct player DDS is played. Default for COD7 is Mason."
"Module: Battlechatter"
"CallOn: Player"
"MandatoryArg: <hero_name> : the player's hero name"
"Example: player dds_set_player_character_name( "hudson" );"
"SPMP: singleplayer"
*/
dds_set_player_character_name( hero_name )
{
	if( !IsPlayer( self ) )
	{
		/#PrintLn( "dds 'dds_set_player_character_name' function was not called on a player. No changes made." );#/
		return;
	}

	switch( hero_name )
	{
		case "mason":
		case "hudson":
		case "reznov":
			level.dds.player_character_name = GetSubStr( hero_name, 0, 3 );
			/#PrintLn( "dds setting player name to '" + level.dds.player_character_name + "'" );#/
			break;
		default:
			/#printLn( "dds: '" + hero_name + "' not a valid player name; setting to 'mason' (mas)" );#/
			level.dds.player_character_name = "mas";
			break;
	}
	self.dds_characterID = level.dds.player_character_name;
}


/*
"Name: dds_exclude_this_ai()"
"Summary: Mark an AI to not be in DDS and to not say any DDS lines."
"Module: Battlechatter"
"CallOn: AI"
"MandatoryArg: "
"Example: us_redshirt dds_exclude_this_ai();"
"SPMP: singleplayer"
*/
dds_exclude_this_ai( bExclude )
{
	if( IsAI( self ) && IsAlive( self ) )
	{
		if (bExclude)
		{
			self.dds_disable = true;
		}
		else
		{
			self.dds_disable = false;
		}
	}
	else
	{
		/#PrintLn( "Tried to mark an entity for DDS removal that was not an AI or not alive." );#/
	}
}


get_obj_origin( msg )
{
	objOrigins = GetEntArray( "objective", "targetname" );
	for ( i = 0; i < objOrigins.size; i++ )
	{
		if ( objOrigins[ i ].script_noteworthy == msg )
			return objOrigins[ i ].origin;
	}
}

get_obj_event( msg )
{
	objEvents = GetEntArray( "objective_event", "targetname" );
	for ( i = 0; i < objEvents.size; i++ )
	{
		if ( objEvents[ i ].script_noteworthy == msg )
			return objEvents[ i ];
	}
}


waittill_objective_event()
{
	waittill_objective_event_proc( true );
}

waittill_objective_event_notrigger()
{
	waittill_objective_event_proc( false );
}

debugorigin()
{
// 	self endon( "killanimscript" );

	self notify( "Debug origin" );
	self endon( "Debug origin" );
	self endon( "death" );
	for ( ;; )
	{
		forward = AnglesToForward( self.angles );
		forwardFar = ( forward * 30 );
		forwardClose = ( forward * 20 );
		right = AnglesToRight( self.angles );
		left = ( right * -10 );
		right = ( right * 10 );
		Line( self.origin, self.origin + forwardFar, ( 0.9, 0.7, 0.6 ), 0.9 );
		Line( self.origin + forwardFar, self.origin + forwardClose + right, ( 0.9, 0.7, 0.6 ), 0.9 );
		Line( self.origin + forwardFar, self.origin + forwardClose + left, ( 0.9, 0.7, 0.6 ), 0.9 );
		wait( 0.05 );
	}
}



/*
=============
///ScriptDocBegin
"Name: get_linked_structs()"
"Summary: Returns an array of entities that SELF is linked to"
"Module: Utility"
"CallOn: An entity that links to other entities"
"Example: spawners = heli get_linked_structs()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_linked_structs()
{
	array = [];

	if ( IsDefined( self.script_linkTo ) )
	{
		linknames = get_links();
		for ( i = 0; i < linknames.size; i++ )
		{
			ent = getstruct( linknames[ i ], "script_linkname" );
			if ( IsDefined( ent ) )
			{
				array[ array.size ] = ent;
			}
		}
	}

	return array;
}

 /*
 =============
///ScriptDocBegin
"Name: get_last_ent_in_chain( <sEntityType> )"
"Summary: Get the last entity/node/vehiclenode in a chain of targeted entities"
"Module: Entity"
"CallOn: Any entity that targets a chain of linked nodes, vehiclenodes or other entities like script_origin"
"MandatoryArg: <sEntityType>: needs to be specified as 'vehiclenode', 'pathnode', 'ent' or 'struct'"
"Example: eLastNode = eVehicle get_last_ent_in_chain( "vehiclenode" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_last_ent_in_chain( sEntityType )
{
	ePathpoint = self;
	while ( IsDefined( ePathpoint.target ) )
	{
		wait( 0.05 );
		if ( IsDefined( ePathpoint.target ) )
		{
			switch( sEntityType )
			{
				case "vehiclenode":
					ePathpoint = GetVehicleNode( ePathpoint.target, "targetname" );
					break;
				case "pathnode":
					ePathpoint = GetNode( ePathpoint.target, "targetname" );
					break;
				case "ent":
					ePathpoint = GetEnt( ePathpoint.target, "targetname" );
					break;
				case "struct":
					ePathpoint = getstruct( ePathpoint.target, "targetname" );
					break;
				default:
					AssertMsg( "sEntityType needs to be 'vehiclenode', 'pathnode', 'ent' or 'struct'" );
			}
		}
		else
			break;
	}
	ePathend = ePathpoint;
	return ePathend;
}


player_seek( timeout )
{
	goalent = Spawn( "script_origin", level.player.origin );
	goalent LinkTo( level.player );
	if ( IsDefined( timeout ) )
		self thread timeout( timeout );
	self SetGoalEntity( goalent );
	if ( !isdefined( self.oldgoalradius ) )
		self.oldgoalradius = self.goalradius;
	self.goalradius = 300;
	self waittill_any( "goal", "timeout" );
	if ( IsDefined( self.oldgoalradius ) )
	{
		self.goalradius = self.oldgoalradius;
		self.oldgoalradius = undefined;
	}
	goalent Delete();
}

timeout( timeout )
{
	self endon( "death" );
	wait( timeout );
	self notify( "timeout" );
}

/*
=============
///ScriptDocBegin
"Name: set_forcegoal( <set_forcegoal> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_forcegoal()
{
	if ( IsDefined( self.set_forcedgoal ) )
		return;

	self.oldfightdist 	 = self.pathenemyfightdist;
	self.oldmaxdist 	 = self.pathenemylookahead;
	self.oldmaxsight 	 = self.maxsightdistsqrd;

	self.pathenemyfightdist = 8;
	self.pathenemylookahead = 8;
	self.maxsightdistsqrd = 1;
	self.set_forcedgoal = true;
}

/*
=============
///ScriptDocBegin
"Name: unset_forcegoal( <unset_forcegoal> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
unset_forcegoal()
{
	if ( !isdefined( self.set_forcedgoal ) )
		return;

	self.pathenemyfightdist = self.oldfightdist;
	self.pathenemylookahead = self.oldmaxdist;
	self.maxsightdistsqrd 	 = self.oldmaxsight;
	self.set_forcedgoal = undefined;
}

/*
=============
///ScriptDocBegin
"Name: array_removeDead_keepkeys( <array> )"
"Summary: Remove dead from an array but keep keys intact"
"Module: Utility"
"CallOn: An array"
"MandatoryArg: <array>: The array "
"Example: array = array_removeDead_keepkeys( <array> );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_removeDead_keepkeys( array )
{
	newArray = [];
	keys = GetArrayKeys( array );
	for ( i = 0; i < keys.size; i++ )
	{
		key = keys[ i ];
		if ( !isalive( array[ key ] ) )
			continue;
		newArray[ key ] = array[ key ];
	}

	return newArray;
}

 /*
 =============
///ScriptDocBegin
"Name: array_removeDead( <array> )"
"Summary: Returns a new array of < array > minus the dead entities"
"Module: Array"
"CallOn: "
"MandatoryArg: <array> : The array to search for dead entities in."
"Example: friendlies = array_removeDead( friendlies );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
array_removeDead( array )
{
	newArray = [];
	foreach ( member in array )
	{
		if ( !isalive( member ) )
			continue;
		newArray[ newArray.size ] = member;
	}

	return newArray;
}

/*
=============
///ScriptDocBegin
"Name: array_removeDead_or_dying( <array> )"
"Summary:  Returns a new array of < array > minus the dead or dying ai"
"Module: Array"
"CallOn: "
"MandatoryArg: <array> : The array to search for dead ai in."
"Example: friendlies = array_removeDead_or_dying( friendlies );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_removeDead_or_dying( array )
{
	newArray = [];
	foreach ( member in array )
	{
		if ( !isalive( member ) )
			continue;
		if ( member doingLongDeath() )
			continue;
		newArray[ newArray.size ] = member;
	}

	return newArray;

}


/*
=============
///ScriptDocBegin
"Name: array_remove_nokeys( <ents> , <remover> )"
"Summary: array_remove used on non keyed arrays doesn't flip the array "
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <ents>: array to remove from"
"MandatoryArg: <remover>: thing to remove from the array"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

array_remove_nokeys( ents, remover )
{
	newents = [];
	for ( i = 0; i < ents.size; i++ )
		if ( ents[ i ] != remover )
			newents[ newents.size ] = ents[ i ];
	return newents;
}

/*
=============
///ScriptDocBegin
"Name: array_remove_index( <array> , <index> )"
"Summary: Removes the element in the array with this index, resulting array order is intact."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_remove_index( array, index )
{
	for ( i = 0; i < array.size - 1; i++ )
	{
		if ( i == index )
		{
			array[ i ] = array[ i + 1 ];
			index++;
		}
	}
	array[ array.size - 1 ] = undefined;
	return array;
}

/*
=============
///ScriptDocBegin
"Name: array_notify( <ents> , <notifier> )"
"Summary: notify this array of entities with <notifier>"
"Module: Array"
"CallOn: array of entities"
"MandatoryArg: <ents>: "
"MandatoryArg: <notifier>: "
"Example: array_notify( enemies, "time_to_dance" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_notify( ents, notifier, match )
{
	foreach ( key, value in ents )
		value notify( notifier, match );
}


// fancy quicker struct array handling, assumes array elements are objects with which an index can be asigned to( IE: can't do 5.struct_array_index )
// also have to be sure that objects can't be a part of another structarray setup as the index position is asigned to the object


struct_arrayspawn()
{
	struct = SpawnStruct();
	struct.array = [];
	struct.lastindex = 0;
	return struct;
}

 /*
structarray_add( struct, object )
{
	struct.array[ struct.lastindex ] = SpawnStruct();
	struct.array[ struct.lastindex ].object = object;
	struct.array[ struct.lastindex ].struct_array_index = struct.lastindex;
	struct.lastindex ++ ;
}
 */
structarray_add( struct, object )
{
	Assert( !isdefined( object.struct_array_index ) );// can't have elements of two structarrays on these. can add that later if it's needed
	struct.array[ struct.lastindex ] = object;
	object.struct_array_index = struct.lastindex;
	struct.lastindex++;
}

 /*
 =============
///ScriptDocBegin
"Name: structarray_remove( <struct> , <object )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1> : "
"OptionalArg: <param2> : "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

structarray_remove( struct, object )
{
	structarray_swaptolast( struct, object );
	struct.array[ struct.lastindex - 1 ] = undefined;
	struct.lastindex--;
}

structarray_remove_index( struct, index )
{
	// if it has a last item, copy it to the index and delete the last
	if ( IsDefined( struct.array[ struct.lastindex - 1 ] ) )
	{
		// overwrite the index to remove with the last in the array
		struct.array[ index ] = struct.array[ struct.lastindex - 1 ];
		struct.array[ index ].struct_array_index = index;

		// remove the last from the array since it's now at position 'index'
		struct.array[ struct.lastindex - 1 ] = undefined;
		struct.lastindex = struct.array.size;
	}
	else
	{
		// is not guaranteed to have a last item, could have been
		//   deleted the same frame, rebuild the array
		struct.array[ index ] = undefined;
		structarray_remove_undefined( struct );
	}
}

structarray_remove_undefined( struct )
{
	// remove undefined ents from array
	newArray = [];
	foreach( object in struct.array )
	{
		if ( !isdefined( object ) )
			continue;
		newArray[ newArray.size ] = object;
	}
	struct.array = newArray;

	// reassign all the index vars
	foreach( i, object in struct.array )
	{
		object.struct_array_index = i;
	}
	struct.lastindex = struct.array.size;
}

structarray_swaptolast( struct, object )
{
	struct structarray_swap( struct.array[ struct.lastindex - 1 ], object );
}

structarray_shuffle( struct, shuffle )
{
	for ( i = 0; i < shuffle; i++ )
		struct structarray_swap( struct.array[ i ], struct.array[ RandomInt( struct.lastindex ) ] );
}

/*
=============
///ScriptDocBegin
"Name: get_use_key( <get_use_key> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_use_key()
{
	if ( level.console )
	 	return " + usereload";
 	else
 		return " + activate";
}

/*
=============
///ScriptDocBegin
"Name: custom_battlechatter( <phrase> )"
"Summary: Call this on an AI to get him to use the battlechatter system to say a specific kind of phrase.  AIs who have battlechatter turned off won't be able to say the phrase.  Returns false if it couldn't do the custom battlechatter for some reason (will output to console or assert with errors, depending on severity)."
"Module: Battlechatter"
"CallOn: An AI"
"MandatoryArg: <phrase>: the string ID for the phrase that the AI will try to say.  Legit phrases are: "order_move_combat", "order_move_noncombat", "order_action_coverme", "inform_reloading""
"Example: level.sarge custom_battlechatter( "move_combat" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
custom_battlechatter( phrase )
{
	return self animscripts\battlechatter_ai::custom_battlechatter_internal( phrase );
}

/*
=============
///ScriptDocBegin
"Name: get_stop_watch( <time> , <othertime> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_stop_watch( time, othertime )
{
	watch = NewHudElem();
	if ( level.console )
	{
		watch.x = 68;
		watch.y = 35;
	}
	else
	{
		watch.x = 58;
		watch.y = 95;
	}

	watch.alignx = "center";
	watch.aligny = "middle";
	watch.horzAlign = "left";
	watch.vertAlign = "middle";
	if ( IsDefined( othertime ) )
		timer = othertime;
	else
		timer = level.explosiveplanttime;
	watch SetClock( timer, time, "hudStopwatch", 64, 64 );// count down for level.explosiveplanttime of 60 seconds, size is 64x64
	return watch;
}

objective_is_active( msg )
{
	active = false;
	// objective must be active for this trigger to hit
	for ( i = 0; i < level.active_objective.size; i++ )
	{
		if ( level.active_objective[ i ] != msg )
			continue;
		active = true;
		break;
	}
	return( active );
}

objective_is_inactive( msg )
{
	inactive = false;
	// objective must be active for this trigger to hit
	for ( i = 0; i < level.inactive_objective.size; i++ )
	{
		if ( level.inactive_objective[ i ] != msg )
			continue;
		inactive = true;
		break;
	}
	return( inactive );
}

set_objective_inactive( msg )
{
	// remove the objective from the active list
	array = [];
	for ( i = 0; i < level.active_objective.size; i++ )
	{
		if ( level.active_objective[ i ] == msg )
			continue;
		array[ array.size ] = level.active_objective[ i ];
	}
	level.active_objective = array;


	// add it to the inactive list
	exists = false;
	for ( i = 0; i < level.inactive_objective.size; i++ )
	{
		if ( level.inactive_objective[ i ] != msg )
			continue;
		exists = true;
	}
	if ( !exists )
		level.inactive_objective[ level.inactive_objective.size ] = msg;

	/#
	// assert that each objective is only on one list
	for ( i = 0; i < level.active_objective.size; i++ )
	{
		for ( p = 0; p < level.inactive_objective.size; p++ )
			AssertEx( level.active_objective[ i ] != level.inactive_objective[ p ], "Objective is both inactive and active" );
	}
	#/
}

set_objective_active( msg )
{
	// remove the objective from the inactive list
	array = [];
	for ( i = 0; i < level.inactive_objective.size; i++ )
	{
		if ( level.inactive_objective[ i ] == msg )
			continue;
		array[ array.size ] = level.inactive_objective[ i ];
	}
	level.inactive_objective = array;

	// add it to the active list
	exists = false;
	for ( i = 0; i < level.active_objective.size; i++ )
	{
		if ( level.active_objective[ i ] != msg )
			continue;
		exists = true;
	}
	if ( !exists )
		level.active_objective[ level.active_objective.size ] = msg;

	/#
	// assert that each objective is only on one list
	for ( i = 0; i < level.active_objective.size; i++ )
	{
		for ( p = 0; p < level.inactive_objective.size; p++ )
			AssertEx( level.active_objective[ i ] != level.inactive_objective[ p ], "Objective is both inactive and active" );
	}
	#/
}

/*
=============
///ScriptDocBegin
"Name: missionFailedWrapper( <missionFailedWrapper> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
missionFailedWrapper()
{
	if ( level.MissionFailed )
		return;

	if ( IsDefined( level.nextmission ) )
		return;// don't fail the mission while the game is on it's way to the next mission.

/*	// will return in the next game
	/#
	if ( IsGodMode( level.player ) )
	{
		PrintLn( GetDvar( "ui_deadquote" ) );
		return;
	}
	#/
*/

	SetSavedDvar( "ammoCounterHide", 1 );

	level.MissionFailed = true;
	flag_set( "missionfailed" );

	if ( arcadeMode() )
		return;

	if ( GetDvar( "failure_disabled" ) == "1" )
		return;

	if ( IsDefined( level.mission_fail_func ) )
	{
		thread [[ level.mission_fail_func ]]();
		return;
	}

	//{NOT_IN_SHIP
	mission_recon( false );	// track failed mission
	//}NOT_IN_SHIP
	MissionFailed();
}

/*
=============
///ScriptDocBegin
"Name: set_mission_failed_override( <func> )"
"Summary: Adds a script function override to be used instead of the code command: MissionFailed()"
"Module: Utility"
"MandatoryArg: <func>: "
"Example: set_mission_failed_override( ::mission_failed )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_mission_failed_override( func )
{
	AssertEx( IsDefined( func ), "function handler not defined in call to add_mission_failed_override()" );
	AssertEx( !IsDefined( level.mission_fail_func ), "mission failed override already exists." );
	
	level.mission_fail_func = func;
}

/*
=============
///ScriptDocBegin
"Name: script_delay( <script_delay> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
script_delay()
{
	if ( IsDefined( self.script_delay ) )
	{
		wait( self.script_delay );
		return true;
	}
	else
	if ( IsDefined( self.script_delay_min ) && IsDefined( self.script_delay_max ) )
	{
		wait( RandomFloatRange( self.script_delay_min, self.script_delay_max ) );
		return true;
	}

	return false;
}


/*
=============
///ScriptDocBegin
"Name: script_wait( <script_wait> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
script_wait()
{
	startTime = GetTime();
	if ( IsDefined( self.script_wait ) )
	{
		wait( self.script_wait );

		if ( IsDefined( self.script_wait_add ) )
			self.script_wait += self.script_wait_add;
	}
	else if ( IsDefined( self.script_wait_min ) && IsDefined( self.script_wait_max ) )
	{
		wait( RandomFloatRange( self.script_wait_min, self.script_wait_max ) );

		if ( IsDefined( self.script_wait_add ) )
		{
			self.script_wait_min += self.script_wait_add;
			self.script_wait_max += self.script_wait_add;
		}
	}

	return( GetTime() - startTime );
}

/*
=============
///ScriptDocBegin
"Name: guy_enter_vehicle( <guy> )"
"Summary: Makes a guy get in a vehicle"
"Module: Utility"
"CallOn: A vehicle"
"MandatoryArg: <guy>: The guy getting in"
"Example: tank guy_enter_vehicle( guy );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
guy_enter_vehicle( guy )
{
	self maps\_vehicle_aianim::guy_enter( guy );
}

/*
=============
///ScriptDocBegin
"Name: guy_runtovehicle_load( <guy> , <vehicle> )"
"Summary: "
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
guy_runtovehicle_load( guy, vehicle )
{
	maps\_vehicle_aianim::guy_runtovehicle( guy, vehicle );
}


/*
=============
///ScriptDocBegin
"Name: get_force_color_guys( <team> , <color> )"
"Summary: gets array of guys on this team with"
"Module: Color"
"CallOn: An entity"
"MandatoryArg: <team>:a team name, either 'axis', 'allies', 'team3', 'neutral', or 'bad_guys' (for all bad guy teams)  "
"MandatoryArg: <color>: color group"
"Example: 	ai = get_force_color_guys( "allies", "c" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

get_force_color_guys( team, color )
{
	ai = GetAIArray( team );
	guys = [];
	for ( i = 0; i < ai.size; i++ )
	{
		guy = ai[ i ];
		if ( !isdefined( guy.script_forcecolor ) )
			continue;

		if ( guy.script_forcecolor != color )
			continue;
		guys[ guys.size ] = guy;
	}

	return guys;
}

get_all_force_color_friendlies()
{
	ai = GetAIArray( "allies" );
	guys = [];
	for ( i = 0; i < ai.size; i++ )
	{
		guy = ai[ i ];
		if ( !isdefined( guy.script_forcecolor ) )
			continue;
		guys[ guys.size ] = guy;
	}

	return guys;
}

/*
=============
///ScriptDocBegin
"Name: get_all_target_ents( <target> )"
"Summary: Returns array of targetted ent, structs, nodes"
"Module: Color"
"OptionalArg: <target>: Optional target override"
"Example: node = guy get_target_ent();"
"SPMP: both"
///ScriptDocEnd
=============
*/
get_all_target_ents( target )
{
	if ( !isdefined( target ) )
		target = self.target;

	AssertEx( IsDefined( target ), "Self had no target!" );
	array = [];

	ents = GetEntArray( target, "targetname" );
	array = array_combine( array, ents );

	ents = GetNodeArray( target, "targetname" );
	array = array_combine( array, ents );

	ents = getstructarray( target, "targetname" );
	array = array_combine( array, ents );

	ents = GetVehicleNodeArray( target, "targetname" );
	array = array_combine( array, ents );

	return array;
}



/*
=============
///ScriptDocBegin
"Name: enable_ai_color( )"
"Summary: Enables color movement system on an AI"
"Module: Color"
"CallOn: An entity"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_ai_color()
{
	if ( IsDefined( self.script_forcecolor ) )
		return;
	if ( !isdefined( self.old_forceColor ) )
		return;

	set_force_color( self.old_forcecolor );
	self.old_forceColor = undefined;
}

/*
=============
///ScriptDocBegin
"Name: enable_ai_color_dontmove( )"
"Summary: Enables color movement system on an AI, but wont cause them to move to that color until another order is given by script or trigger"
"Module: Color"
"CallOn: An entity"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_ai_color_dontmove()
{
	self.dontColorMove = true;
	self enable_ai_color();
}

/*
=============
///ScriptDocBegin
"Name: disable_ai_color( <disable_ai_color> )"
"Summary: "
"Module: Color"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_ai_color()
{
	//make sure we're not setting a color on an AI at the time
	if ( IsDefined( self.new_force_color_being_set ) )
	{
		self endon( "death" );
		//prevents disabling the color while in the process of assigning one
		self waittill( "done_setting_new_color" );
	}

	//remove any safe volume, just to be sage
	self ClearFixedNodeSafeVolume();
	
	//if this AI has no color, we're done
	if ( !isdefined( self.script_forcecolor ) )
		return;

	AssertEx( !isdefined( self.old_forcecolor ), "Tried to disable forcecolor on a guy that somehow had a old_forcecolor already. Investigate!!!" );

	//save off the old color
	self.old_forceColor = self.script_forceColor;

	self maps\_colors::remove_AI_from_colors();


	/#
	update_debug_friendlycolor( self.unique_id );
	#/
}

/*
=============
///ScriptDocBegin
"Name: clear_force_color( <clear_force_color> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
clear_force_color()
{
	disable_ai_color();
}

/*
=============
///ScriptDocBegin
"Name: check_force_color( <_color> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
check_force_color( _color )
{
	color = level.colorCheckList[ ToLower( _color ) ];
	if ( IsDefined( self.script_forcecolor ) && color == self.script_forcecolor )
		return true;
	else
		return false;
}

/*
=============
///ScriptDocBegin
"Name: get_force_color( <get_force_color> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_force_color()
{
	color = self.script_forceColor;
	return color;
}

/*
=============
///ScriptDocBegin
"Name: set_force_color( <_color> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_force_color( _color )
{
	color = self maps\_colors::add_AI_to_colors(_color);

	 /#
	update_debug_friendlycolor( self.unique_id );
	#/
}

/*
=============
///ScriptDocBegin
"Name: issue_color_orders( <color_team>, <team> )"
"Summary: applies a list of color codes to a team, makes them move to the color nodes for that order"
"Module: Level"
"CallOn: Level"
"MandatoryArg: <color_team>: color codes to execute "
"MandatoryArg: <team>: team to issue orders to"
"Example: issue_color_orders("r0 o6", "axis");"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
issue_color_orders( color_team, team )
{
	maps\_colors::issue_color_orders_generic(color_team, team);
}

/*
=============
///ScriptDocBegin
"Name: clear_color_order( <color_team>, <team> )"
"Summary: clears the current codes for a color and a team"
"Module: Level"
"CallOn: Level"
"MandatoryArg: <order_color>: color to remove the order of"
"MandatoryArg: <team>: team to to remove the order from"
"Example: clear_color_order("r", "axis");"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
clear_color_order(order_color, team)
{
  	maps\_colors::clear_color_order_from_team(order_color, team);
}

/*
=============
///ScriptDocBegin
"Name: clear_all_color_orders( <team> )"
"Summary: clears the current codes for all colors of a team"
"Module: Level"
"CallOn: Level"
"MandatoryArg: <team>: team to to remove all orders from"
"Example: clear_all_color_orders("axis");"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
clear_all_color_orders(team)
{
	foreach(color in level.colorList)
		maps\_colors::clear_color_order_from_team(color, team);
}

/*
=============
///ScriptDocBegin
"Name: replace_on_death()"
"Summary: Color friendlies that run this will be replaced when they die"
"Module: Utility"
"CallOn: A friendly"
"Example: red_shirt thread replace_on_death(); // poor guy"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
replace_on_death()
{
	// kill with either:
	// level notify( "kill_color_replacements" );
	// or
	// self notify( "_disable_reinforcement" );

	self thread maps\_colors::colorNode_replace_on_death();
}

/*
=============
///ScriptDocBegin
"Name: disable_replace_on_death()"
"Summary: Disables replace on death"
"Module: Color"
"CallOn: An AI"
"Example: guy disable_replace_on_death();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_replace_on_death()
{
	self.replace_on_death = undefined;
	self notify( "_disable_reinforcement" );
}

stop_replace_on_death()
{
	self notify("_disable_reinforcement");
}
  
stop_all_replace_on_death(team, color)
{
  	self thread maps\_colors::colorNode_stop_replace_on_death_group(team, color);
}

spawn_reinforcement( classname, color, replace, team)
{
	//if no team was provided, assume it's allies
	if(!IsDefined(team))
		team = "allies";
	
	//spawn a guy in to the colors system
	thread maps\_colors::colorNode_spawn_reinforcement( team, classname, color, replace );
}

clear_promotion_order()
{
	maps\_colors::colorNode_clear_promotion_order();
}

set_promotion_order( deadguy, replacer )
{
	maps\_colors::colorNode_set_promotion_order(deadguy, replacer);
}

set_empty_promotion_order( deadguy )
{
	maps\_colors::colorNode_set_empty_promotion_order(deadguy);
}

has_color()
{
	// can lose color during the waittillframeend in left_color_node
	if ( self maps\_colors::get_team() == "axis" )
	{
		return IsDefined( self.script_color_axis ) || IsDefined( self.script_forcecolor );
	}

	return IsDefined( self.script_color_allies ) || IsDefined( self.script_forcecolor );
}

/*
=============
///ScriptDocBegin
"Name: get_color_volume_from_trigger()"
"Summary: Gets the color volume from a trigger that has only one color code"
"Module: Color"
"CallOn: A color trigger"
"Example: volume = trigger get_color_volume_from_trigger();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_color_volume_from_trigger()
{
	return self maps\_colors::get_color_volume_from_trigger_codes();
}

/*
=============
///ScriptDocBegin
"Name: get_color_nodes_from_trigger()"
"Summary: Gets the color nodes from a trigger that has only one color code"
"Module: Color"
"CallOn: A color trigger"
"Example: nodes = trigger get_color_nodes_from_trigger();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_color_nodes_from_trigger()
{
	return self maps\_colors::get_color_nodes_from_trigger_codes();
}







// TODO: Non - hacky rumble.
flashRumbleLoop( duration )
{
	Assert( IsPlayer( self ) );

	goalTime = GetTime() + duration * 1000;

	while ( GetTime() < goalTime )
	{
		self PlayRumbleOnEntity( "damage_heavy" );
		wait( 0.05 );
	}
}

flashMonitorEnableHealthShield( time )
{
	self endon( "death" );
	self endon( "flashed" );

	wait 0.2;

	self EnableHealthShield( false );

	wait time + 2;

	self EnableHealthShield( true );
}

nineBangHandler( origin, percent_distance, percent_angle, attacker, team )
{
	waits = [ 0.8, 0.7, 0.7, 0.6 ];
	banglens = [ 1.0, 0.8, 0.6, 0.6 ];
	foreach( i, banglen in banglens)
	{
		// if it's close enough, angle doesn't matter so much
		frac = ( percent_distance - 0.85 ) / ( 1 - 0.85 );
		if ( frac > percent_angle )
			percent_angle = frac;

		if ( percent_angle < 0.25 )
			percent_angle = 0.25;

		// at 300 or less of the full range of 1000 units, get the full effect
		minamountdist = 0.3;
		if ( percent_distance > 1 - minamountdist )
			percent_distance = 1.0;
		else
			percent_distance = percent_distance / ( 1 - minamountdist );

		if ( team != self.team )
			seconds = percent_distance * percent_angle * 6.0;
		else
			seconds = percent_distance * percent_angle * 3.0;

		if ( seconds < 0.25 )
			continue;

		seconds = banglen * seconds;
		if ( IsDefined( self.maxflashedseconds ) && seconds > self.maxflashedseconds )
 			seconds = self.maxflashedseconds;

		self.flashingTeam = team;
		self notify( "flashed" );
		self.flashendtime = GetTime() + seconds * 1000;// player is flashed if flashDoneTime > GetTime()
		self ShellShock( "flashbang", seconds );
		
		flag_set( "player_flashed" );

		if ( percent_distance * percent_angle > 0.5 )
			self thread flashMonitorEnableHealthShield( seconds );
		wait waits[i];
	}
	thread unflash_flag( 0.05 );
}

flashMonitor()
{
	Assert( IsPlayer( self ) );

	self endon( "death" );

	for ( ;; )
	{
		self waittill( "flashbang", origin, percent_distance, percent_angle, attacker, team );

		if ( "1" == GetDvar( "noflash" ) )
			continue;

		if ( is_player_down( self ) )
			continue;

		if (isdefined(self.threw_ninebang))
		{
			player_range_percent = 1000/1250;	// standard fb radius / 9b radius
			om_player_range_percent = 1.0 - player_range_percent;
			self.threw_ninebang = undefined;
			// percent_distance == 0 at max range, 1 at min_range and less
			if (percent_distance < om_player_range_percent)
				continue;	// outside player's radius so skip it
			percent_distance = (percent_distance-om_player_range_percent) / player_range_percent;	// rescale percent_distance to match standard fb
		}
		// PrintLn( "Flashed by a grenade from team '", team, "'." );

		// if it's close enough, angle doesn't matter so much
		frac = ( percent_distance - 0.85 ) / ( 1 - 0.85 );
		if ( frac > percent_angle )
			percent_angle = frac;

		if ( percent_angle < 0.25 )
			percent_angle = 0.25;

		// at 300 or less of the full range of 1000 units, get the full effect
		minamountdist = 0.3;
		if ( percent_distance > 1 - minamountdist )
			percent_distance = 1.0;
		else
			percent_distance = percent_distance / ( 1 - minamountdist );

		if ( team != self.team )
			seconds = percent_distance * percent_angle * 6.0;
		else
			seconds = percent_distance * percent_angle * 3.0;

		if ( seconds < 0.25 )
			continue;

		if ( IsDefined( self.maxflashedseconds ) && seconds > self.maxflashedseconds )
 			seconds = self.maxflashedseconds;

		self.flashingTeam = team;
		self notify( "flashed" );
		self.flashendtime = GetTime() + seconds * 1000;// player is flashed if flashDoneTime > GetTime()
		self ShellShock( "flashbang", seconds );
		self LightSetOverrideEnableForPlayer( "flashed", 0.1 );
		flag_set( "player_flashed" );
		thread unflash_flag( seconds );
		wait 0.1;
		self LightSetOverrideDisableForPlayer( seconds - 0.1 );

		if ( percent_distance * percent_angle > 0.5 )
			self thread flashMonitorEnableHealthShield( seconds );

		if ( seconds > 2 )
			thread flashRumbleLoop( 0.75 );
		else
			thread flashRumbleLoop( 0.25 );

		// if it's an enemy's flash grenade,
		// flash nearby allies so they can't take out enemies going after the player
		if ( team != "allies" )
			self thread flashNearbyAllies( seconds, team );
	}
}


flashNearbyAllies( baseDuration, team )
{
	Assert( IsPlayer( self ) );

	wait .05;

	allies = GetAIArray( "allies" );
	for ( i = 0; i < allies.size; i++ )
	{
		if ( DistanceSquared( allies[ i ].origin, self.origin ) < 350 * 350 )
		{
			duration = baseDuration + RandomFloatRange( -1000, 1500 );
			if ( duration > 4.5 )
				duration = 4.5;
			else if ( duration < 0.25 )
				continue;

			newendtime = GetTime() + duration * 1000;
			if ( !isdefined( allies[ i ].flashendtime ) || allies[ i ].flashendtime < newendtime )
			{
				allies[ i ].flashingTeam = team;
				allies[ i ] flashBangStart( duration );
			}
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: restartEffect( <restartEffect> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
restartEffect()
{
	self common_scripts\_createfx::restart_fx_looper();
}

/*
=============
///ScriptDocBegin
"Name: pauseExploder( <num> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
pauseExploder( num )
{
	num += "";

	if (isdefined(level.createFXexploders))
	{	// do optimized flavor if available
		exploders = level.createFXexploders[num];
		if (isdefined(exploders))
		{
			foreach (ent in exploders)
			{
				ent pauseEffect();
			}
		}
	}
	else
	{
		foreach ( fx in level.createFXent )
		{
			if ( !isdefined( fx.v[ "exploder" ] ) )
				continue;
	
			if ( fx.v[ "exploder" ] != num )
				continue;
	
			fx pauseEffect();
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: restartExploder( <num> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
restartExploder( num )
{
	num += "";
	if (isdefined(level.createFXexploders))
	{	// do optimized flavor if available
		exploders = level.createFXexploders[num];
		if (isdefined(exploders))
		{
			foreach (ent in exploders)
			{
				ent restartEffect();
			}
		}
	}
	else
	{
		foreach ( fx in level.createFXent )
		{
			if ( !isdefined( fx.v[ "exploder" ] ) )
				continue;
	
			if ( fx.v[ "exploder" ] != num )
				continue;
	
			fx restartEffect();
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: getfxarraybyID( <fxid> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getfxarraybyID( fxid )
{
	array = [];
	if (isdefined(level.createFXbyFXID))
	{
		fxids = level.createFXbyFXID[fxid];
		if (isdefined(fxids))
			array = fxids;
	}
	else
	{
		for ( i = 0; i < level.createFXent.size; i++ )
		{
			if ( level.createFXent[ i ].v[ "fxid" ] == fxid )
				array[ array.size ] = level.createFXent[ i ];
		}
	}
	return array;
}

ignoreAllEnemies( qTrue )
{
	self notify( "ignoreAllEnemies_threaded" );
	self endon( "ignoreAllEnemies_threaded" );

	if ( qTrue )
	{
		// put the ai in a threat bias group that ignores all the other groups so he
		// doesnt get distracted and go into exposed while his goal radius is too small

		self.old_threat_bias_group = self GetThreatBiasGroup();

		num = undefined;
		/#
			num = self GetEntNum();
			PrintLn( "entity: " + num + "ignoreAllEnemies TRUE" );
			PrintLn( "entity: " + num + " threatbiasgroup is " + self.old_threat_bias_group );
		#/

		CreateThreatBiasGroup( "ignore_everybody" );
		/#
			PrintLn( "entity: " + num + "ignoreAllEnemies TRUE" );
			PrintLn( "entity: " + num + " SetThreatBiasGroup( ignore_everybody )" );
		#/
		self SetThreatBiasGroup( "ignore_everybody" );
		teams = [];
		teams[ "axis" ] = "allies";
		teams[ "allies" ] = "axis";

		AssertEx( self.team != "neutral", "Why are you making a guy have team neutral? And also, why is he doing anim_reach?" );
		ai = GetAIArray( teams[ self.team ] );
		groups = [];
		for ( i = 0; i < ai.size; i++ )
			groups[ ai[ i ] GetThreatBiasGroup() ] = true;

		keys = GetArrayKeys( groups );
		for ( i = 0; i < keys.size; i++ )
		{
			/#
				PrintLn( "entity: " + num + "ignoreAllEnemies TRUE" );
				PrintLn( "entity: " + num + " SetThreatBias( " + keys[ i ] + ", ignore_everybody, 0 )" );
			#/
			SetThreatBias( keys[ i ], "ignore_everybody", 0 );
		}

		// should now be impossible for this guy to attack anybody on the other team
	}
	else
	{
		num = undefined;
		AssertEx( IsDefined( self.old_threat_bias_group ), "You can't use ignoreAllEnemies( false ) on an AI that has never ran ignoreAllEnemies( true )" );
		/#
			num = self GetEntNum();
			PrintLn( "entity: " + num + "ignoreAllEnemies FALSE" );
			PrintLn( "entity: " + num + " self.old_threat_bias_group is " + self.old_threat_bias_group );
		#/
		if ( self.old_threat_bias_group != "" )
		{
			/#
				PrintLn( "entity: " + num + "ignoreAllEnemies FALSE" );
				PrintLn( "entity: " + num + " SetThreatBiasGroup( " + self.old_threat_bias_group + " )" );
			#/
			self SetThreatBiasGroup( self.old_threat_bias_group );
		}
		self.old_threat_bias_group = undefined;
	}
}



/*
=============
///ScriptDocBegin
"Name: vehicle_detachfrompath( <vehicle_detachfrompath> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_detachfrompath()
{
	maps\_vehicle_code::vehicle_pathdetach();
}

 /*
 =============
///ScriptDocBegin
"Name: vehicle_resumepath()"
"Summary: will resume to the last path a vehicle was on.  Only used for helicopters, ground vehicles don't ever deviate."
"Module: Vehicles"
"CallOn: An entity"
"Example: helicopter vehicle_resumepath();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

vehicle_resumepath()
{
	thread maps\_vehicle_code::vehicle_resumepathvehicle();
}

 /*
 =============
///ScriptDocBegin
"Name: vehicle_land( <neargoaldist>)"
"Summary: lands a vehicle on the ground, _vehicle scripts take care of offsets and determining where the ground is relative to the origin.  Returns when land is complete"
"Module: Vehicles"
"CallOn: An entity"
"OptionalArg: <neargoaldist>: neargoal distance for notifying near_goal"
"Example: helicopter vehicle_land();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

vehicle_land( neargoaldist )
{
	maps\_vehicle_code::vehicle_landvehicle( neargoaldist );
}

/*
=============
///ScriptDocBegin
"Name: vehicle_liftoff( <height> )"
"Summary: make a vehicle lift of to the specified height. if the vehicle was on a path and went through vehicle_land, this will resume that path"
"Module: Entity"
"CallOn: An entity"
"OptionalArg: <height>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_liftoff( height )
{
	maps\_vehicle_code::vehicle_liftoffvehicle( height );
}

/*
=============
///ScriptDocBegin
"Name: vehicle_dynamicpath( <node> , <bwaitforstart> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_dynamicpath( node, bwaitforstart )
{
	maps\_vehicle::vehicle_paths( node, bwaitforstart );
}

 /*
 =============
///ScriptDocBegin
"Name: groundpos( <origin> )"
"Summary: bullettraces to the ground and returns the position that it hit."
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <origin> : "
"Example: groundposition = helicopter groundpos( helicopter.origin ); "
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

groundpos( origin )
{
	return BulletTrace( origin, ( origin + ( 0, 0, -100000 ) ), 0, self )[ "position" ];
}

change_player_health_packets( num )
{
	Assert( IsPlayer( self ) );

	self.player_health_packets += num;
	self notify( "update_health_packets" );

	if ( self.player_health_packets >= 3 )
		self.player_health_packets = 3;

// 	if( self.player_health_packets <= 0 )
// 		self Kill();
}

/*
=============
///ScriptDocBegin
"Name: getvehiclespawner( <targetname> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getvehiclespawner( targetname )
{
	spawners = getvehiclespawnerarray( targetname );
	Assert( spawners.size == 1 );
	return spawners[ 0 ];
}

/*
=============
///ScriptDocBegin
"Name: getvehiclespawnerarray( <targetname> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getvehiclespawnerarray( targetname )
{
	return maps\_vehicle_code::_getvehiclespawnerarray( targetname );
}


/*
=============
///ScriptDocBegin
"Name: describe_start( <msg> , <func> , <loc_string> , <optional_func>, [transient] )"
"Summary: Use this for Describing an add_start() function, meant for reducing upward dependancy on <LevelName>_starts.gsc "
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <msg>: SEEDOC:Utility\add_start"
"MandatoryArg: <func>: SEEDOC:Utility\add_start"
"OptionalArg: <loc_string>: SEEDOC:Utility\add_start"
"OptionalArg: <optional_func>: SEEDOC:Utility\add_start"
"OptionalArg: <transient_name>: SEEDOC:Utility\add_start"
"OptionalArg: <catchup_function>: SEEDOC:Utility\add_start"
"Example: "
"SPMP: singleplayer"
"NoteLine: with describe_start the order doesn't matter. you are not actually creating the start point order untill you add_start()"
///ScriptDocEnd
=============
*/

describe_start( msg, func, loc_string, optional_func, transient, catchup_function )
{
    add_start_assert();

    if( !IsDefined( level.start_description ) )
        level.start_description = [];
    
    AssertEx( !isdefined( level.start_description[ msg ] ), "You are describing this start point more than once" );
    level.start_description[ msg ] = add_start_construct( msg, func, loc_string, optional_func, [transient], catchup_function );
}

/*
=============
///ScriptDocBegin
"Name: add_start( <msg> , <func> , <loc_string> , <optional_func>, [transient] )"
"Summary: add start with a string"
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <msg>: string to identify the start.  'default' has special meaning, and will cause the <func> not to be called."
"MandatoryArg: <func>: thread to start when this start is initialized.  This Argument is optional when a start is described using describe_start. SEEDOC:Utility\describe_start"
"OptionalArg: <loc_string>: Localizated string to display, this became a requirement when loc_warnings were turned on."
"OptionalArg: <Optional_func>: The main logic function associated with this start point, will run in the order of the start points when a previous function completes."
"OptionalArg: <optional_transients_to_load>: An array of the names of the transient fast files to load at this start point."
"OptionalArg: <catchup_function>: all the catchup functions will run through to this start point"
"Example: 	add_start( "first_hind", ::start_first_hind, &"STARTS_FIRSTHIND" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

add_start( msg, func, loc_string, optional_func, optional_transients_to_load, catchup_function )
{
    add_start_assert();
    
	msg = ToLower( msg );

	if ( IsDefined( optional_transients_to_load ) )
	{
		if ( optional_transients_to_load.size > 2 )
		{
			/#
				AssertMsg( "Start point: " + msg + " trying to load more than 2 transients, only using the first 2 in the array");
			#/
			temp_arr = [];
			temp_arr[0] = optional_transients_to_load[0];
			temp_arr[1] = optional_transients_to_load[1];
			optional_transients_to_load = temp_arr;
		}
		if ( !IsDefined( level.start_transients ) )
		{
			level.start_transients = [];
		}
		foreach ( trans in optional_transients_to_load )
		{
			if ( !array_contains( level.start_transients, trans ) )
				level.start_transients[level.start_transients.size] = trans;
		}
	}
	
	if ( IsDefined( level.start_description ) && IsDefined( level.start_description[ msg ]  ) )
	{
	    /#
    	    assert_msg = "This start is already described. you only need add_start( msg )";
    	    AssertEx( ! IsDefined( func ), assert_msg );
    	    AssertEx( ! IsDefined( loc_string ), assert_msg );
    	    AssertEx( ! IsDefined( optional_func ), assert_msg );
    	    AssertEx( ! IsDefined( optional_transients_to_load ), assert_msg );
	    #/
	    array = level.start_description[ msg ];
	}
	else
	    array = add_start_construct( msg, func, loc_string, optional_func, optional_transients_to_load, catchup_function );
    	
    if( !IsDefined( func ) )
    {
        if ( !IsDefined( level.start_description ) )
            AssertEx( IsDefined( func ), "add_start() called with no descriptions set and no func parameter.." );
        else if( !IsSubStr( msg, "no_game" ) ) 
        {
            if ( !IsDefined( level.start_description[ msg ] ) )
                return; // want to ignore all the other starts. when descriptions are made.
        }
    }

	level.start_functions[ level.start_functions.size ] = array;
	level.start_arrays[ msg ] = array;
}

/*
=============
///ScriptDocBegin
"Name: set_start_transients( <start_name> , [transient_array] )"
"Summary: assigns an array of transients to a start point"
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <start_name>: string to identify the start. "
"MandatoryArg: <transient_array>: An array of the names of the transient fast files to load at this start point."
"Example: 	set_start_transients( "intro", ["levelname_intro_tr"] );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_start_transients( start_name, transients )
{
	if (!IsDefined(level.start_arrays))
		return;
	if (!IsDefined(level.start_arrays[start_name]))
		return;
	
	start_name = ToLower(start_name);
	
	if ( transients.size > 2 )
	{
		/#
			AssertMsg( "Start point: " + start_name + " trying to load more than 2 transients, only using the first 2 in the array");
		#/
		temp_arr = [];
		temp_arr[0] = transients[0];
		temp_arr[1] = transients[1];
		transients = temp_arr;
	}
	if ( !IsDefined( level.start_transients ) )
	{
		level.start_transients = [];
	}
	foreach ( trans in transients )
	{
		if ( !array_contains( level.start_transients, trans ) )
			level.start_transients[level.start_transients.size] = trans;
	}
	
	level.start_arrays[ start_name ][ "transients_to_load" ] = transients;
}

is_no_game_start()
{
    return IsSubStr( level.start_point, "no_game" );
}

add_start_construct( msg, func, loc_string, optional_func, optional_transients_to_load, catchup_function )
{
    /#
	if ( IsDefined( loc_string ) )
	{
		PreCacheString( loc_string );
	}
	#/

	array						= [];
	array[ "name"			  ] = msg;
	array[ "start_func"		  ] = func;
	array[ "start_loc_string" ] = loc_string;
	array[ "logic_func"		  ] = optional_func;
	array[ "transients_to_load" ] = optional_transients_to_load;
	array[ "catchup_function" ] = catchup_function;
    return array;
}

add_start_assert()
{
   	AssertEx( !isdefined( level._loadStarted ), "Can't create starts after _load" );
	if ( !isdefined( level.start_functions ) )
		level.start_functions = [];
}

level_has_start_points()
{
	// account for no_game start point
	return level.start_functions.size > 1;
}

/*
=============
///ScriptDocBegin
"Name: set_default_start( <start> )"
"Summary: Set the default start point by name."
"Module: Utility"
"MandatoryArg: <start>: Which start to use as the default start."
"Example: set_default_start( "ride" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_default_start( start )
{
	level.default_start_override = start;
}

/*
=============
///ScriptDocBegin
"Name: default_start( <func> )"
"Summary: "
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
default_start( func )
{
	level.default_start = func;
}

linetime( start, end, color, timer )
{
	thread linetime_proc( start, end, color, timer );
}


 /*
 =============
///ScriptDocBegin
"Name: within_fov_2d( <start_origin> , <start_angles> , <end_origin> , <fov> )"
"Summary: Returns true if < end_origin > is within the players field of view, otherwise returns false. Compares in 2d only."
"Module: Vector"
"CallOn: "
"MandatoryArg: <start_origin> : starting origin for FOV check( usually the players origin )"
"MandatoryArg: <start_angles> : angles to specify facing direction( usually the players angles )"
"MandatoryArg: <end_origin> : origin to check if it's in the FOV"
"MandatoryArg: <fov> : cosine of the FOV angle to use"
"Example: qBool = within_fov_2d( level.player.origin, level.player.angles, target1.origin, Cos( 45 ) );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
within_fov_2d( start_origin, start_angles, end_origin, fov )
{
	normal	= VectorNormalize( ( end_origin[ 0 ], end_origin[ 1 ], 0 ) - ( start_origin[ 0 ], start_origin[ 1 ], 0 ) );
	forward = AnglesToForward( ( 0, start_angles[ 1 ], 0 ) );
	return VectorDot( forward, normal ) >= fov;
}


 /*
 =============
///ScriptDocBegin
"Name: get_dot( <start_origin> , <start_angles> , <end_origin> )"
"Summary: Returns the dot product."
"Module: Vector"
"CallOn: "
"MandatoryArg: <start_origin> : starting origin for FOV check( usually the players origin )"
"MandatoryArg: <start_angles> : angles to specify facing direction( usually the players angles )"
"MandatoryArg: <end_origin> : origin to check if it's in the FOV"
"Example: fov = get_dot( level.player.origin, level.player.angles, target1.origin );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
get_dot( start_origin, start_angles, end_origin )
{
	normal = VectorNormalize( end_origin - start_origin );
	forward = AnglesToForward( start_angles );
	dot = VectorDot( forward, normal );

	return dot;
}



 /*
 =============
///ScriptDocBegin
"Name: within_fov_of_players( <end_origin> , <fov> )"
"Summary: Returns true if < end_origin > is within the all of the players field of view (single or coop), otherwise returns false. Start origin and start angles are of the individual players in the map (single or coop)"
"Module: Vector"
"CallOn: "
"MandatoryArg: <end_origin> : origin to check if it's in the FOV"
"MandatoryArg: <fov> : cosine of the FOV angle to use"
"Example: qBool = within_fov_of_players( target1.origin, Cos( 45 ) );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
within_fov_of_players( end_origin, fov )
{
	bDestInFOV = undefined;
	for ( i = 0; i < level.players.size; i++ )
	{
		playerEye = level.players[ i ] GetEye();
		bDestInFOV = within_fov( playerEye, level.players[ i ] GetPlayerAngles(), end_origin, fov );
		if ( !bDestInFOV )
			return false;
	}
	return true;
}

/*
=============
///ScriptDocBegin
"Name: wait_for_buffer_time_to_pass( <start_time> , <buffer_time> )"
"Summary: Wait until the current time is equal or greater than the start_time (in ms) + buffer_time (in seconds)"
"Module: Utility"
"MandatoryArg: <start_time>: The GetTime() at the start of the scene."
"MandatoryArg: <buffer_time>: The amount of time you want to insure has passed since start_time."
"Example: wait_for_buffer_time_to_pass( level.last_time_we_checked, 3 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
wait_for_buffer_time_to_pass( last_queue_time, buffer_time )
{
	timer = buffer_time * 1000 - ( GetTime() - last_queue_time );
	timer *= 0.001;
	if ( timer > 0 )
	{
		// 500ms buffer time between radio or dialogue sounds
		wait( timer );
	}
}

bcs_scripted_dialogue_start()
{
	anim.scriptedDialogueStartTime = GetTime();
}

/*
=============
///ScriptDocBegin
"Name: dialogue_queue( <msg> )"
"Summary: Plays a dialogue line with a forced half second delay between lines.  Blocking function.  Plays an anim_single_queue on the guy, with the guy as the actor"
"Module: Utility"
"CallOn: An ai"
"MandatoryArg: <msg>: The dialogue scene, defined as level.scr_sound[ guys.animname ][ "scene" ] "
"Example: level.price dialogue_queue( "nice_find_macgregor" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
dialogue_queue( msg )
{
	squelch_type = GetSndAliasValue( msg, "squelchname");
	if ( self == level || squelch_type != "" )
	{
		// This function is being called globally, so assume radio
		radio_dialogue( msg, undefined, squelch_type );
		return;
	}
	
	bcs_scripted_dialogue_start();

	self maps\_anim::anim_single_queue( self, msg );
}

/*
=============
///ScriptDocBegin
"Name: generic_dialogue_queue( <msg>, <timeout> )"
"Summary: Plays an anim_generic_queue on the guy, with the guy as the actor"
"Module: Utility"
"CallOn: An ai"
"MandatoryArg: <msg>: The dialogue scene, defined as level.scr_sound[ "generic" ][ "scene" ] "
"OptionalArg: <timeout>: optional timeout to forget the radio dialogue if it's been in the queue for longer than this many seconds"
"Example: level.price dialogue_queue( "nice_find_macgregor" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

generic_dialogue_queue( msg, timeout )
{
	bcs_scripted_dialogue_start();

	self maps\_anim::anim_generic_queue( self, msg, undefined, undefined, timeout );
}

/*
=============
///ScriptDocBegin
"Name: radio_dialogue( <msg> )"
"Summary: puts radio dialogue into a queue. Dialogue plays when it gets it's turn in the queue."
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"OptionalArg: <timeout>: optional timeout to forget the radio dialogue if it's been in the queue for longer than this many seconds"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue( msg, timeout, squelch_type )
{
	//this is so we have a universal entity to stack radio dialogue on without worrying about
	//any other scripts dirtying up the stack with functions not related to radio_dialogue
	if ( !isdefined( level.player_radio_emitter ) )
	{
		ent = Spawn( "script_origin", ( 0, 0, 0 ) );
		ent LinkTo( level.player, "", ( 0, 0, 0 ), ( 0, 0, 0 ) );
		level.player_radio_emitter = ent;
	}

	bcs_scripted_dialogue_start();

	// Encapsulated the play with squelch to keep squelches in the right order
	if ( !IsDefined( timeout ) )
	{
		return level.player_radio_emitter function_stack( ::radio_dialogue_play, msg, squelch_type );
	}
	else
	{
		return level.player_radio_emitter function_stack_timeout( timeout, ::radio_dialogue_play, msg, squelch_type );
	}
}

/*
=============
///ScriptDocBegin
"Name: radio_dialogue_play( <msg> )"
"Summary: Play a squelch in, radio line, then try to play a squelch out"
"Module: Utility"
"MandatoryArg: <msg>: The dialogue alias"
"Example: level.player_radio_emitter function_stack( :: radio_dialogue_play, msg );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_play( msg, squelch_type )
{
	if ( !IsDefined( squelch_type ) )
	{
		squelch_type = "none";
	}
	level.player_radio_squelch_out_queued = false;
	if ( squelch_type != "none" && IsDefined(level.scr_radio[ "squelches" ][ squelch_type ]) )
	{
		self play_sound_on_tag( level.scr_radio[ "squelches" ][ squelch_type ][ "on" ], undefined, true );
	}
	
	success = false;
	if( IsDefined( level.scr_radio[ msg ] ) )
	{
		success = self play_sound_on_tag( level.scr_radio[ msg ], undefined, true );
	}
	else
	{
		// If msg isn't in the scr_radio structure, assume it's the alias
		success = self play_sound_on_tag( msg, undefined, true );
	}
	
	// Queue up the squelch out
	if ( squelch_type != "none" && IsDefined(level.scr_radio[ "squelches" ][ squelch_type ]) )
	{
		thread radio_try_squelch_out(squelch_type);
	}

	return success;
}

/*
=============
///ScriptDocBegin
"Name: radio_dialogue_overlap( <msg> )"
"Summary: Play a radio dialogue sound on top of whatever radio dialogue is currently playing (overlapping)."
"Module: Utility"
"MandatoryArg: <msg>: The dialogue alias"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_overlap( msg )
{
	AssertEx( IsDefined( level.scr_radio[ msg ] ), "Tried to play radio dialogue " + msg + " that did not exist! Add it to level.scr_radio" );
	AssertEx( IsDefined( level.player_radio_emitter ), "Tried to overlap dialogue but no radio dialogue was playing, use radio_dialogue." );

	if ( !isdefined( level.player_radio_emitter_overlap ) )
	{
		level.player_radio_emitter_overlap = [];
	}

	org = Spawn( "script_origin", ( 0, 0, 0 ) );
	level.player_radio_emitter_overlap[level.player_radio_emitter_overlap.size] = org;
	org endon( "death" );

	thread delete_on_death_wait_sound( org, "sounddone" );
	org.origin = level.player_radio_emitter.origin;
	org.angles = level.player_radio_emitter.angles;
	org LinkTo( level.player_radio_emitter );

	PrintLn( "**dialog alias playing radio: " + level.scr_radio[ msg ] );
	org PlaySound( level.scr_radio[ msg ], "sounddone" );
	if ( !isdefined( wait_for_sounddone_or_death( org ) ) )
		org StopSounds(); // don't call StopSounds (redundantly) when the sound stopped since this can cut off sounds in SO for the non host
	wait( 0.05 );// stopsounds doesnt work if the org is deleted same frame

	level.player_radio_emitter_overlap = array_remove(level.player_radio_emitter_overlap, org);
	org Delete();
}

/*
=============
///ScriptDocBegin
"Name: radio_dialogue_stop()"
"Summary: Stops any radio dialogue currently playing and Stops any further radio dialogue queued up from playing ( removes those requests from the queue )"
"Module: Utility"
"Example: radio_dialogue_stop();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_stop()
{
	if ( !isdefined( level.player_radio_emitter ) )
		return;
	
	//level.player_radio_emitter function_stack_clear(); ->don't need it - kills the funciton stack already
	level.player_radio_emitter Delete();
}

/*
=============
///ScriptDocBegin
"Name: radio_dialogue_overlap_stop()"
"Summary: Stops any overlapped radio dialogue currently playing"
"Module: Utility"
"Example: radio_dialogue_overlap_stop();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_overlap_stop()
{
	if ( !isdefined( level.player_radio_emitter_overlap ) )
		return;

	Assert( IsArray( level.player_radio_emitter_overlap ));

	foreach (ent in level.player_radio_emitter_overlap )
	{
		if (IsDefined(ent))
		{
			ent StopSounds();
			wait(0.05);
			ent Delete();
		}
	}

	level.player_radio_emitter_overlap = undefined;
}

/*
=============
///ScriptDocBegin
"Name: radio_dialogue_clear_stack()"
"Summary: Stops any further radio dialogue queued up from playing ( removes those requests from the queue )"
"Module: Utility"
"Example: radio_dialogue_clear_stack();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_clear_stack()
{
	if ( !isdefined( level.player_radio_emitter ) )
		return;
	level.player_radio_emitter function_stack_clear();
}


/*
=============
///ScriptDocBegin
"Name: radio_dialogue_remove_from_stack( <msg> )"
"Summary: removes any further radio dialogue queued up ( removes those requests from the queue )"
"Module: Utility"
"Example: radio_dialogue_remove_from_stack( "chaingun_state_ready" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_remove_from_stack( msg )
{
	if ( !isdefined( level.player_radio_emitter ) )
		return;

	if ( !IsDefined( level.player_radio_emitter.function_stack ))
		return;
	
	newstack = [];
	is_remove_performed = false;
	
	orig_size = level.player_radio_emitter.function_stack.size;
	
	for( i = 0; i < orig_size; i++ )
	{
		// check if the first item, and that it isn't already playing
		if ( ( i == 0 ) && IsDefined( level.player_radio_emitter.function_stack[ 0 ].function_stack_func_begun ) && IsDefined( level.player_radio_emitter.function_stack[ 0 ].function_stack_func_begun ))
	    {
			newstack[ newstack.size ] = level.player_radio_emitter.function_stack[ i ];					    	
	    }
		// if the message matches and hasn't started playing yet
		else if ( IsDefined( level.player_radio_emitter.function_stack[ i ].param1 ) && ( level.player_radio_emitter.function_stack[ i ].param1 == msg ))
		{			
			// kill original waits
			level.player_radio_emitter.function_stack[ i ] notify( "death" );
			level.player_radio_emitter.function_stack[ i ] = undefined;
			is_remove_performed = true;
		}
		else
		{
			newstack[ newstack.size ] = level.player_radio_emitter.function_stack[ i ];			
		}
	}
	
	if ( is_remove_performed )
		level.player_radio_emitter.function_stack = newstack;
}




/*
=============
///ScriptDocBegin
"Name: radio_dialogue_interupt( <msg> )"
"Summary: plays a radio dialogue line and oversteps any previous radio dialogue being played (doesn't stop any previous dialogue being played )"
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"Example: radio_dialogue_interupt( "shutup" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_interupt( msg )
{
	AssertEx( IsDefined( level.scr_radio[ msg ] ), "Tried to play radio dialogue " + msg + " that did not exist! Add it to level.scr_radio" );

	if ( !isdefined( level.player_radio_emitter ) )
	{
		ent = Spawn( "script_origin", ( 0, 0, 0 ) );
		ent LinkTo( level.player, "", ( 0, 0, 0 ), ( 0, 0, 0 ) );
		level.player_radio_emitter = ent;
	}

	level.player_radio_emitter play_sound_on_tag( level.scr_radio[ msg ], undefined, true );
}

/*
=============
///ScriptDocBegin
"Name: radio_dialogue_safe( <msg> )"
"Summary: trys to play a radio dialogue line - only plays if there is no other radio dialogue playing"
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"Example: radio_dialogue_interupt( "shutup_please" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_dialogue_safe( msg )
{
	return radio_dialogue( msg, .05 );
}

/*
=============
///ScriptDocBegin
"Name: smart_radio_dialogue( <msg>, <timeout> )"
"Summary: plays a dialogue line; adds it to the systems if it isn't there already."
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"OptionalArg: <timeout>: optional timeout to forget the radio dialogue if it's been in the queue for longer than this many seconds"
"Example: smart_radio_dialogue( "shipg_orb_increasedactivity" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
smart_radio_dialogue( dialogue, timeout )
{
	squelch_type = GetSndAliasValue( dialogue, "squelchname");
	add_to_radio( dialogue );
	// dialogue_queue( dialogue );
	radio_dialogue( dialogue, timeout, squelch_type );
}

/*
=============
///ScriptDocBegin
"Name: smart_radio_dialogue_interrupt( <msg> )"
"Summary: plays a dialogue line; adds it to the systems if it isn't there already."
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"Example: smart_radio_dialogue_interrupt( "shipg_orb_increasedactivity" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
smart_radio_dialogue_interrupt( dialogue )
{
	add_to_radio( dialogue );
	radio_dialogue_stop();
	radio_dialogue_interupt( dialogue );
}

/*
=============
///ScriptDocBegin
"Name: smart_radio_dialogue_overlap( <msg> )"
"Summary: plays a dialogue line; adds it to the systems if it isn't there already."
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"Example: smart_radio_dialogue_overlap( "shipg_orb_increasedactivity" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
smart_radio_dialogue_overlap( dialogue )
{
	add_to_radio( dialogue );
	radio_dialogue_overlap( dialogue );
}

/*
=============
///ScriptDocBegin
"Name: smart_dialogue( <msg> )"
"Summary: plays a dialogue line; adds it to the systems if it isn't there already."
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"Example: smart_dialogue( "shipg_orb_increasedactivity" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
smart_dialogue( dialogue )
{
	self add_to_dialogue( dialogue );
	self dialogue_queue( dialogue );
}

/*
=============
///ScriptDocBegin
"Name: smart_dialogue_generic( <msg> )"
"Summary: plays a dialogue line; adds it to the systems if it isn't there already."
"Module: Utility"
"MandatoryArg: <msg>: the radio dialogue alias to play"
"Example: smart_dialogue_generic( "shipg_orb_increasedactivity" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
smart_dialogue_generic( dialogue )
{
	self add_to_dialogue_generic( dialogue );
	self generic_dialogue_queue( dialogue );
}

/*
=============
///ScriptDocBegin
"Name: radio_try_squelch_out( <time> )"
"Summary: Waits a beat, then trys to play a squelch out if there's been not squelch in during the wait"
"Module: Utility"
"OptionalArg: <time>: Time in seconds to wait for the squelch out, default to 0.01 seconds"
"Example: radio_try_squelch_out(0.4)"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
radio_try_squelch_out( squelch_type, time )
{
	self endon( "death" );
	if( !IsDefined( time ) ) 
	{
		time = 0.1;
	}
	level.player_radio_squelch_out_queued = true;
	wait(time);
	if( IsDefined( level.player_radio_emitter ) && level.player_radio_squelch_out_queued == true )
	{
		level.player_radio_emitter function_stack( ::play_sound_on_tag, level.scr_radio[ "squelches" ][ squelch_type ][ "off" ], undefined, true );
	}
}

/////////////////////// DELETE ME ////////////////////////
radio_dialogue_queue( msg, squelch_type )
{
	radio_dialogue( msg, undefined, squelch_type );
}

// HUD ELEMENT STUFF
hint_create( text, background, backgroundAlpha )
{
	struct = SpawnStruct();
	if ( IsDefined( background ) && background == true )
		struct.bg = NewHudElem();
	struct.elm = NewHudElem();

	struct hint_position_internal( backgroundAlpha );
	// elm.label 		 = struct.text;
	// elm.debugtext 	 = struct.text;
	struct.elm SetText( text );

	return struct;
}

hint_delete()
{
	self notify( "death" );

	if ( IsDefined( self.elm ) )
		self.elm Destroy();
	if ( IsDefined( self.bg ) )
		self.bg Destroy();
}

hint_position_internal( bgAlpha )
{
	if ( level.console )
		self.elm.fontScale = 2;
	else
		self.elm.fontScale = 1.6;

	self.elm.x 			 = 0;
	self.elm.y		 	 = -40;
	self.elm.alignX 	 = "center";
	self.elm.alignY 	 = "bottom";
	self.elm.horzAlign 	 = "center";
	self.elm.vertAlign 	 = "middle";
	self.elm.sort		 = 1;
	self.elm.alpha		 = 0.8;

	if ( !isdefined( self.bg ) )
		return;

	self.bg.x 			 = 0;
	self.bg.y 			 = -40;
	self.bg.alignX 		 = "center";
	self.bg.alignY 		 = "middle";
	self.bg.horzAlign 	 = "center";
	self.bg.vertAlign 	 = "middle";
	self.bg.sort		 = -1;

	if ( level.console )
		self.bg SetShader( "popmenu_bg", 650, 52 );
	else
		self.bg SetShader( "popmenu_bg", 650, 42 );

	if ( !isdefined( bgAlpha ) )
		bgAlpha = 0.5;

	self.bg.alpha = bgAlpha;
}

/*
=============
///ScriptDocBegin
"Name: string( <num> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
string( num )
{
	return( "" + num );
}


/*
=============
///ScriptDocBegin
"Name: is_string_a_number( <string_val> )"
"Summary: Returns whether or not the input string is a number"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <string_val>: A string value to test"
"Example: is_num = is_string_a_number( "1.234" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_string_a_number( string_val )
{
	Assert( IsString( string_val ) );	
	float_val = float( string_val );
	return string( float_val ) == string_val;
}



ignoreEachOther( group1, group2 )
{
	// these threatbias groups ignore each other
	AssertEx( ThreatBiasGroupExists( group1 ), "Tried to make threatbias group " + group1 + " ignore " + group2 + " but " + group1 + " does not exist!" );
	AssertEx( ThreatBiasGroupExists( group2 ), "Tried to make threatbias group " + group2 + " ignore " + group1 + " but " + group2 + " does not exist!" );
	SetIgnoreMeGroup( group1, group2 );
	SetIgnoreMeGroup( group2, group1 );
}

 /*
 =============
///ScriptDocBegin
"Name: add_global_spawn_function( <team> , <func> , <param1> , <param2> , <param3> )"
"Summary: All spawners of this team will run this function on spawn."
"Module: Utility"
"MandatoryArg: <team> : The team of the spawners that will run this function."
"MandatoryArg: <func> : The function to run."
"OptionalArg: <param1> : An optional parameter."
"OptionalArg: <param2> : An optional parameter."
"OptionalArg: <param3> : An optional parameter."
"Example: add_global_spawn_function( "axis", ::do_the_amazing_thing, some_amazing_parameter );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

add_global_spawn_function( team, function, param1, param2, param3 )
{
	AssertEx( IsDefined( level.spawn_funcs ), "Tried to add_global_spawn_function before calling _load" );

	func = [];
	func[ "function" ] = function;
	func[ "param1" ] = param1;
	func[ "param2" ] = param2;
	func[ "param3" ] = param3;

	level.spawn_funcs[ team ][ level.spawn_funcs[ team ].size ] = func;
}

 /*
 =============
///ScriptDocBegin
"Name: remove_global_spawn_function( <team> , <func> )"
"Summary: Remove this function from the global spawn functions for this team."
"Module: Utility"
"MandatoryArg: <team> : The team of the spawners that will no longer run this function."
"MandatoryArg: <func> : The function to remove."
"Example: remove_global_spawn_function( "allies", ::do_the_amazing_thing );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

remove_global_spawn_function( team, function )
{
	AssertEx( IsDefined( level.spawn_funcs ), "Tried to remove_global_spawn_function before calling _load" );

	array = [];
	for ( i = 0; i < level.spawn_funcs[ team ].size; i++ )
	{
		if ( level.spawn_funcs[ team ][ i ][ "function" ] != function )
		{
			array[ array.size ] = level.spawn_funcs[ team ][ i ];
		}
	}

	//assertEx( level.spawn_funcs[ team ].size != array.size, "Tried to remove a function from level.spawn_funcs, but that function didn't exist!" );
	level.spawn_funcs[ team ] = array;
}

 /*
 =============
///ScriptDocBegin
"Name: exists_global_spawn_function( <team> , <func> )"
"Summary: Check if thisfunction from the global spawn functions for this team exists. Returns true or false."
"Module: Utility"
"MandatoryArg: <team> : The team of the spawners that will no longer run this function."
"MandatoryArg: <func> : The function to remove."
"Example: if ( exists_global_spawn_function( "allies", ::do_the_amazing_thing ) )"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

exists_global_spawn_function( team, function )
{
	if ( !isdefined( level.spawn_funcs ) )
		return false;

	for ( i = 0; i < level.spawn_funcs[ team ].size; i++ )
	{
		if ( level.spawn_funcs[ team ][ i ][ "function" ] == function )
			return true;
	}

	return false;
}


 /*
 =============
///ScriptDocBegin
"Name: remove_spawn_function( <func> )"
"Summary: Remove this spawn function from this spawner."
"Module: Utility"
"MandatoryArg: <func1> : The function to remove."
"Example: spawner remove_spawn_function( ::do_the_amazing_thing );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

remove_spawn_function( function )
{
	AssertEx( !isalive( self ), "Tried to remove_spawn_function to a living guy." );
	AssertEx( IsSpawner( self ), "Tried to remove_spawn_function to something that isn't a spawner." );
	AssertEx( IsDefined( self.spawn_functions ), "Tried to remove_spawn_function before calling _load" );

	new_spawn_functions = [];

	foreach ( func_array in self.spawn_functions )
	{
		if ( func_array[ "function" ] == function )
			continue;

		new_spawn_functions[ new_spawn_functions.size ] = func_array;
	}


	self.spawn_functions = new_spawn_functions;
}

 /*
 =============
///ScriptDocBegin
"Name: add_spawn_function( <func> , <param1> , <param2> , <param3>, <param4>, <param5> )"
"Summary: Anything that spawns from this spawner will run this function. Anything."
"Module: Utility"
"MandatoryArg: <func1> : The function to run."
"OptionalArg: <param1> : An optional parameter."
"OptionalArg: <param2> : An optional parameter."
"OptionalArg: <param3> : An optional parameter."
"OptionalArg: <param4> : An optional parameter."
"OptionalArg: <param5> : An optional parameter."
"Example: spawner add_spawn_function( ::do_the_amazing_thing, some_amazing_parameter );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

add_spawn_function( function, param1, param2, param3, param4, param5 )
{
	AssertEx( !isalive( self ), "Tried to add_spawn_function to a living guy." );
	AssertEx( IsSpawner( self ), "Tried to add_spawn_function to something that isn't a spawner." );
	AssertEx( IsDefined( self.spawn_functions ), "Tried to add_spawn_function before calling _load" );

	foreach ( func_array in self.spawn_functions )
	{
		if ( func_array[ "function" ] == function )
			return;
	}

	func = [];
	func[ "function" ] = function;
	func[ "param1" ] = param1;
	func[ "param2" ] = param2;
	func[ "param3" ] = param3;
	func[ "param4" ] = param4;
	func[ "param5" ] = param5;

	self.spawn_functions[ self.spawn_functions.size ] = func;
}

/*
=============
///ScriptDocBegin
"Name: array_delete( <array> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_delete( array )
{
	for ( i = 0; i < array.size; i++ )
	{
		array[ i ] Delete();
	}
}

/*
=============
///ScriptDocBegin
"Name: array_kill( <array> )"
"Summary: "
"Module: Array"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_kill( array )
{
	for ( i = 0; i < array.size; i++ )
	{
		array[ i ] kill();
	}
}
ignore_triggers( timer )
{
	// ignore triggers for awhile so others can trigger the trigger we're in.
	self endon( "death" );
	self.ignoreTriggers = true;
	if ( IsDefined( timer ) )
	{
		wait( timer );
	}
	else
	{
		wait( 0.5 );
	}
	self.ignoreTriggers = false;
}

/*
=============
///ScriptDocBegin
"Name: activate_trigger_with_targetname( <name> )"
"Summary: Notifies a trigger of 'trigger'"
"Module: Trigger"
"MandatoryArg: <name>: string get the entity that has a targetname of <name>"
"Example: activate_trigger_with_targetname( "red_team1" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
activate_trigger_with_targetname( msg )
{
	trigger = GetEnt( msg, "targetname" );
	trigger activate_trigger();
}

/*
=============
///ScriptDocBegin
"Name: activate_trigger_with_noteworthy( <name> )"
"Summary: Notifies a trigger of 'trigger'"
"Module: Trigger"
"MandatoryArg: <name>: string get the entity that has a script_noteworthy of <name>"
"Example: activate_trigger_with_noteworthy( "red_team1" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
activate_trigger_with_noteworthy( msg )
{
	trigger = GetEnt( msg, "script_noteworthy" );
	trigger activate_trigger();
}

/*
=============
///ScriptDocBegin
"Name: disable_trigger_with_targetname( <name> )"
"Summary: calls trigger_off() on a trigger with targetname of <name>"
"Module: Trigger"
"MandatoryArg: <name>: string get the entity that has a targetname of <name>"
"Example: disable_trigger_with_targetname( "red_team1" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_trigger_with_targetname( msg )
{
	trigger = GetEnt( msg, "targetname" );
	trigger trigger_off();
}

/*
=============
///ScriptDocBegin
"Name: disable_trigger_with_noteworthy( <name> )"
"Summary: calls trigger_off() on a trigger with script_noteworthy of <name>"
"Module: Trigger"
"MandatoryArg: <name>: string get the entity that has a script_noteworthy of <name>"
"Example: disable_trigger_with_noteworthy( "red_team1" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_trigger_with_noteworthy( msg )
{
	trigger = GetEnt( msg, "script_noteworthy" );
	trigger trigger_off();
}

/*
=============
///ScriptDocBegin
"Name: enable_trigger_with_targetname( <name> )"
"Summary: calls trigger_on() on a trigger with targetname of <name>"
"Module: Trigger"
"MandatoryArg: <name>: string get the entity that has a targetname of <name>"
"Example: enable_trigger_with_targetname( "red_team1" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_trigger_with_targetname( msg )
{
	trigger = GetEnt( msg, "targetname" );
	trigger trigger_on();
}

/*
=============
///ScriptDocBegin
"Name: enable_trigger_with_noteworthy( <name> )"
"Summary: calls trigger_on() on a trigger with script_noteworthy of <name>"
"Module: Trigger"
"MandatoryArg: <name>: string get the entity that has a script_noteworthy of <name>"
"Example: enable_trigger_with_noteworthy( "red_team1" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_trigger_with_noteworthy( msg )
{
	trigger = GetEnt( msg, "script_noteworthy" );
	trigger trigger_on();
}

/*
=============
///ScriptDocBegin
"Name: is_hero()"
"Summary: check to see if AI is a hero"
"Module: AI"
"Example: level.price ishero()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_hero()
{
	return IsDefined( level.hero_list[ get_ai_number() ] );
}

/*
=============
///ScriptDocBegin
"Name: get_ai_number()"
"Summary: gets an AI's ai number"
"Module: AI"
"Example: num = level.price get_ai_number()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_ai_number()
{
	if ( !isdefined( self.unique_id ) )
	{
		set_ai_number();
	}
	return self.unique_id;
}

/*
=============
///ScriptDocBegin
"Name: set_ai_number( <set_ai_number> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_ai_number()
{
/*
=============
///ScriptFieldDocBegin
"Name: .unique_id"
"Summary: unique_id"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

	self.unique_id = "ai" + level.ai_number;
	level.ai_number++;
}

/*
=============
///ScriptDocBegin
"Name: make_hero()"
"Summary: makes an AI a hero"
"Module: AI"
"Example: level.price make_hero()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
make_hero()
{
	level.hero_list[ self.unique_id ] = true;
}

/*
=============
///ScriptDocBegin
"Name: unmake_hero()"
"Summary: makes an AI not a hero"
"Module: Utility"
"Example: level.price unmake_hero()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
unmake_hero()
{
	level.hero_list[ self.unique_id ] = undefined;
}

/*
=============
///ScriptDocBegin
"Name: get_heroes()"
"Summary: returns an array of hero AI"
"Module: Utility"
"Example: level.heros =  get_heroes()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_heroes()
{
	array = [];
	ai = GetAIArray( "allies" );
	for ( i = 0; i < ai.size; i++ )
	{
		if ( ai[ i ] is_hero() )
			array[ array.size ] = ai[ i ];
	}
	return array;
}

/*
=============
///ScriptDocBegin
"Name: set_team_pacifist( <team> , <val> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_team_pacifist( team, val )
{
	ai = GetAIArray( team );
	for ( i = 0; i < ai.size; i++ )
	{
		ai[ i ].pacifist = val;
	}
}




/*
=============
///ScriptDocBegin
"Name: remove_dead_from_array( <array> )"
"Summary: remove_dead_from_array"
"Module: Utility"
"Example: array = remove_dead_from_array( array )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_dead_from_array( array )
{
	newarray = [];
	foreach ( item in array )
	{
		if ( !isalive( item ) )
			continue;
		newarray[ newarray.size ] = item;
	}
	return newarray;
}

/*
=============
///ScriptDocBegin
"Name: remove_heroes_from_array( <array> )"
"Summary: remove_heroes_from_array"
"Module: Utility"
"Example: array = remove_heroes_from_array( array )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_heroes_from_array( array )
{
	newarray = [];
	for ( i = 0; i < array.size; i++ )
	{
		if ( array[ i ] is_hero() )
			continue;
		newarray[ newarray.size ] = array[ i ];
	}
	return newarray;
}

/*
=============
///ScriptDocBegin
"Name: remove_color_from_array( <array> , <color> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_color_from_array( array, color )
{
	newarray = [];
	for ( i = 0; i < array.size; i++ )
	{
		guy = array[ i ];
		if ( !isdefined( guy.script_forcecolor ) )
			continue;
		if ( guy.script_forcecolor == color )
			continue;
		newarray[ newarray.size ] = guy;
	}
	return newarray;
}

/*
=============
///ScriptDocBegin
"Name: remove_noteworthy_from_array( <array> , <noteworthy> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_noteworthy_from_array( array, noteworthy )
{
	newarray = [];
	for ( i = 0; i < array.size; i++ )
	{
		guy = array[ i ];
		if ( !isdefined( guy.script_noteworthy ) )
			continue;
		if ( guy.script_noteworthy == noteworthy )
			continue;
		newarray[ newarray.size ] = guy;
	}
	return newarray;
}

/*
=============
///ScriptDocBegin
"Name: get_closest_colored_friendly( <color> , <origin> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_closest_colored_friendly( color, origin )
{
	allies = get_force_color_guys( "allies", color );
	allies = remove_heroes_from_array( allies );

	if ( !isdefined( origin ) )
		friendly_origin = level.player.origin;
	else
		friendly_origin = origin;

	return getClosest( friendly_origin, allies );
}

/*
=============
///ScriptDocBegin
"Name: remove_without_classname( <array> , <classname> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_without_classname( array, classname )
{
	newarray = [];
	for ( i = 0; i < array.size; i++ )
	{
		if ( !issubstr( array[ i ].classname, classname ) )
			continue;
		newarray[ newarray.size ] = array[ i ];
	}
	return newarray;
}

/*
=============
///ScriptDocBegin
"Name: remove_without_model( <array> , <model> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_without_model( array, model )
{
	newarray = [];
	for ( i = 0; i < array.size; i++ )
	{
		if ( !issubstr( array[ i ].model, model ) )
			continue;
		newarray[ newarray.size ] = array[ i ];
	}
	return newarray;
}

/*
=============
///ScriptDocBegin
"Name: get_closest_colored_friendly_with_classname( <color> , <classname> , <origin> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_closest_colored_friendly_with_classname( color, classname, origin )
{
	allies = get_force_color_guys( "allies", color );
	allies = remove_heroes_from_array( allies );

	if ( !isdefined( origin ) )
		friendly_origin = level.player.origin;
	else
		friendly_origin = origin;

	allies = remove_without_classname( allies, classname );

	return getClosest( friendly_origin, allies );
}



/*
=============
///ScriptDocBegin
"Name: promote_nearest_friendly( <colorFrom> , <colorTo> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
promote_nearest_friendly( colorFrom, colorTo )
{
	for ( ;; )
	{
		friendly = get_closest_colored_friendly( colorFrom );
		if ( !isalive( friendly ) )
		{
			wait( 1 );
			continue;
		}

		friendly set_force_color( colorTo );
		return;
	}
}

/*
=============
///ScriptDocBegin
"Name: instantly_promote_nearest_friendly( <colorFrom> , <colorTo> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
instantly_promote_nearest_friendly( colorFrom, colorTo )
{
	for ( ;; )
	{
		friendly = get_closest_colored_friendly( colorFrom );
		if ( !isalive( friendly ) )
		{
			AssertEx( 0, "Instant promotion from " + colorFrom + " to " + colorTo + " failed!" );
			return;
		}

		friendly set_force_color( colorTo );
		return;
	}
}

/*
=============
///ScriptDocBegin
"Name: instantly_promote_nearest_friendly_with_classname( <colorFrom> , <colorTo> , <classname> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
instantly_promote_nearest_friendly_with_classname( colorFrom, colorTo, classname )
{
	for ( ;; )
	{
		friendly = get_closest_colored_friendly_with_classname( colorFrom, classname );
		if ( !isalive( friendly ) )
		{
			AssertEx( 0, "Instant promotion from " + colorFrom + " to " + colorTo + " failed!" );
			return;
		}

		friendly set_force_color( colorTo );
		return;
	}
}

/*
=============
///ScriptDocBegin
"Name: promote_nearest_friendly_with_classname( <colorFrom> , <colorTo> , <classname> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
promote_nearest_friendly_with_classname( colorFrom, colorTo, classname )
{
	for ( ;; )
	{
		friendly = get_closest_colored_friendly_with_classname( colorFrom, classname );
		if ( !isalive( friendly ) )
		{
			wait( 1 );
			continue;
		}

		friendly set_force_color( colorTo );
		return;
	}
}

/*
=============
///ScriptDocBegin
"Name: riotshield_lock_orientation( <yaw_angle> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
riotshield_lock_orientation( yaw_angle )
{
	self OrientMode( "face angle", yaw_angle );
	self.lockOrientation = true;
}

/*
=============
///ScriptDocBegin
"Name: riotshield_unlock_orientation( <riotshield_unlock_orientation> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
riotshield_unlock_orientation()
{
	self.lockOrientation = false;
}

/*
=============
///ScriptDocBegin
"Name: instantly_set_color_from_array_with_classname( <array> , <color> , <classname> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
instantly_set_color_from_array_with_classname( array, color, classname )
{
	// the guy is removed from the array so the function can be run on the array again
	foundGuy = false;
	newArray = [];
	for ( i = 0; i < array.size; i++ )
	{
		guy = array[ i ];
		if ( foundGuy || !isSubstr( guy.classname, classname ) )
		{
			newArray[ newArray.size ] = guy;
			continue;
		}

		foundGuy = true;
		guy set_force_color( color );
	}
	return newArray;
}

/*
=============
///ScriptDocBegin
"Name: instantly_set_color_from_array( <array> , <color> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
instantly_set_color_from_array( array, color )
{
	// the guy is removed from the array so the function can be run on the array again
	foundGuy = false;
	newArray = [];
	for ( i = 0; i < array.size; i++ )
	{
		guy = array[ i ];
		if ( foundGuy )
		{
			newArray[ newArray.size ] = guy;
			continue;
		}

		foundGuy = true;
		guy set_force_color( color );
	}
	return newArray;
}

/*
=============
///ScriptDocBegin
"Name: wait_for_script_noteworthy_trigger( <msg> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
wait_for_script_noteworthy_trigger( msg )
{
	wait_for_trigger( msg, "script_noteworthy" );
}

/*
=============
///ScriptDocBegin
"Name: wait_for_targetname_trigger( <msg> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
wait_for_targetname_trigger( msg )
{
	wait_for_trigger( msg, "targetname" );
}

/*
=============
///ScriptDocBegin
"Name: wait_for_flag_or_timeout( <msg> , <timer> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
wait_for_flag_or_timeout( msg, timer )
{
	if ( flag( msg ) )
		return;

	level endon( msg );
	wait timer;
}

/*
=============
///ScriptDocBegin
"Name: wait_for_notify_or_timeout( <msg> , <timer> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
wait_for_notify_or_timeout( msg, timer )
{
	self endon( msg );
	wait timer;
}

/*
=============
///ScriptDocBegin
"Name: wait_for_trigger_or_timeout( <timer> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
wait_for_trigger_or_timeout( timer )
{
	self endon( "trigger" );
	wait timer;
}

/*
=============
///ScriptDocBegin
"Name: wait_for_either_trigger( <msg1> , <msg2> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
wait_for_either_trigger( msg1, msg2 )
{
	ent = SpawnStruct();
	array = [];
	array = array_combine( array, GetEntArray( msg1, "targetname" ) );
	array = array_combine( array, GetEntArray( msg2, "targetname" ) );
	for ( i = 0; i < array.size; i++ )
	{
		ent thread ent_waits_for_trigger( array[ i ] );
	}

	ent waittill( "done" );
}


/*
=============
///ScriptDocBegin
"Name: dronespawn_bodyonly( <spawner> )"
"Summary: Create a drone body, doesn't take damage, die, etc."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
dronespawn_bodyonly( spawner )
{
	drone = maps\_spawner::spawner_dronespawn( spawner );
	Assert( IsDefined( drone ) );

	return drone;
}


/*
=============
///ScriptDocBegin
"Name: dronespawn( <spawner> )"
"Summary: Create a drone, make it behave like AI"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
dronespawn( spawner )
{
	if ( !isdefined( spawner ) )
		spawner = self;
	drone = maps\_spawner::spawner_dronespawn( spawner );
	Assert( IsDefined( drone ) );

	AssertEx( IsDefined( level.drone_spawn_func ), "You need to put maps\_drone_civilian::init(); OR maps\_drone_ai::init(); in your level script! Use the civilian version if your drone is a civilian and the _ai version if it's a friendly or enemy." );
	drone [[ level.drone_spawn_func ]]();

	drone.spawn_funcs = spawner.spawn_functions;
	drone thread maps\_spawner::run_spawn_functions();

	drone.spawner = spawner;
	
	return drone;
}



/*
=============
///ScriptDocBegin
"Name: swap_ai_to_drone( <ai> )"
"Summary: "
"Module: Entity"
"CallOn: "
"MandatoryArg: <An entity that's an AI>: "
"OptionalArg: <param2>: "
"Example: drone = swap_ai_to_drone( ai );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
swap_ai_to_drone( ai )
{
	return maps\_spawner::spawner_swap_ai_to_drone( ai );
}


/*
=============
///ScriptDocBegin
"Name: swap_drone_to_ai( <drone> )"
"Summary: Swaps a drone ent with a AI version"
"Module: Entity"
"CallOn: "
"MandatoryArg: <An entity that's an AI Drone>: "
"OptionalArg: <param2>: "
"Example: ai = swap_drone_to_ai( drone );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
swap_drone_to_ai( drone )
{
	return maps\_spawner::spawner_swap_drone_to_ai( drone );
}

/*
=============
///ScriptDocBegin
"Name: get_trigger_flag( <get_trigger_flag> )"
"Summary: Swaps an AI ent with a drone version"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_trigger_flag()
{
	if ( IsDefined( self.script_flag ) )
	{
		return self.script_flag;
	}

	if ( IsDefined( self.script_noteworthy ) )
	{
		return self.script_noteworthy;
	}

	AssertEx( 0, "Flag trigger at " + self.origin + " has no script_flag set." );
}


set_default_pathenemy_settings()
{
	self.pathenemylookahead = 192;
	self.pathenemyfightdist = 192;
}

/*
=============
///ScriptDocBegin
"Name: cqb_walk( <on_or_off> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
cqb_walk( on_or_off )// ( deprecated )
{
	if ( on_or_off == "on" )
	{
		self enable_cqbwalk();
	}
	else
	{
		Assert( on_or_off == "off" );
		self disable_cqbwalk();
	}
}

/*
=============
///ScriptDocBegin
"Name: enable_cqbwalk( <autoEnabled> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_cqbwalk( autoEnabled )
{
	if ( self.type == "dog" )
		return;
	
	if ( !isdefined( autoEnabled ) )
		self.cqbEnabled = true;

	self.cqbwalking = true;
	self.turnRate = 0.2;
	level thread animscripts\cqb::findCQBPointsOfInterest();

	/#
	self thread animscripts\cqb::CQBDebug();
	#/
}

/*
=============
///ScriptDocBegin
"Name: disable_cqbwalk( <disable_cqbwalk> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_cqbwalk()
{
	if ( self.type == "dog" )
		return;
	
	self.cqbwalking = undefined;
	self.cqbEnabled = undefined;
	self.turnRate = 0.3;
	self.cqb_point_of_interest = undefined;

	/#
	self notify( "end_cqb_debug" );
	#/
}

/*
=============
///ScriptDocBegin
"Name: enable_readystand( )"
"Summary: Set actor to use a ready idle position."
"Module: Entity"
"CallOn: An entity"
"Example: self enable_readystand()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_readystand()
{
	self.bUseReadyIdle = true;
}

/*
=============
///ScriptDocBegin
"Name: disable_readystand( )"
"Summary: Set actor to not use a ready idle position."
"Module: Entity"
"CallOn: An entity"
"Example: self disable_readystand()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_readystand()
{
	self.bUseReadyIdle = undefined;
}

/*
=============
///ScriptDocBegin
"Name: cqb_aim( <the_target> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
cqb_aim( the_target )
{
	if ( !isdefined( the_target ) )
	{
		self.cqb_target = undefined;
	}
	else
	{
		self.cqb_target = the_target;

		if ( !isdefined( the_target.origin ) )
			AssertMsg( "target passed into cqb_aim does not have an origin!" );
	}
}


/*
=============
///ScriptDocBegin
"Name: set_force_cover( <val> )"
"Summary: true to be suppressed, false to turn off
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_force_cover( val )
{
 	AssertEx( !isdefined( val ) || val == false || val == true, "invalid force cover set on guy" );
	AssertEx( IsAlive( self ), "Tried to set force cover on a dead guy" );
	if ( IsDefined( val ) && val )
		self.forceSuppression = true;
	else
		self.forceSuppression = undefined;
}

/*
=============
///ScriptDocBegin
"Name: do_in_order( <func1> , <param1> , <func2> , <param2> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
do_in_order( func1, param1, func2, param2 )
{
	if ( IsDefined( param1 ) )
		[[ func1 ]]( param1 );
	else
		[[ func1 ]]();
	if ( IsDefined( param2 ) )
		[[ func2 ]]( param2 );
	else
		[[ func2 ]]();
}

/*
=============
///ScriptDocBegin
"Name: send_notify( <msg> , <optional_param> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
send_notify( msg, optional_param )
{
	// functionalized so it can be function pointer'd
	if ( IsDefined( optional_param ) )
		self notify( msg, optional_param );
	else
		self notify( msg );
}


/*
=============
///ScriptDocBegin
"Name: waittill_match_or_timeout( <msg> , <match>, <timer> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_match_or_timeout( msg, match, timer )
{
	ent = SpawnStruct();
	ent endon( "complete" );
	ent delayThread( timer, ::send_notify, "complete" );

	self waittillmatch( msg, match );
}


deleteEnt( ent )
{
	// created so entities can be deleted using array_thread
	ent notify( "deleted" );
	ent Delete();
}


/*
=============
///ScriptDocBegin
"Name: first_touch( <ent> )"
"Summary: Returns true the first time this check is run against an entity."
"Module: Utility"
"CallOn: An entity that notifies trigger to a trigger."
"MandatoryArg: <ent>: The ent that caused trigger to be notified "
"Example: if ( first_touch( guy ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
first_touch( ent )
{
	if ( !isdefined( self.touched ) )
		self.touched = [];

	AssertEx( IsDefined( ent ), "Ent is not defined!" );
	AssertEx( IsDefined( ent.unique_id ), "Ent has no unique_id" );

	if ( IsDefined( self.touched[ ent.unique_id ] ) )
		return false;

	self.touched[ ent.unique_id ] = true;
	return true;
}

/*
=============
///ScriptDocBegin
"Name: getanim( <anime> )"
"Summary: Gets the animation referenced by the string for the animname of the caller"
"Module: Anim"
"CallOn: An entity that has an animname."
"MandatoryArg: <anime> The animation scene. "
"Example: animation = guy getanim( "run" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getanim( anime )
{
	AssertEx( IsDefined( self.animname ), "Called getanim on a guy with no animname" );
	AssertEx( IsDefined( level.scr_anim[ self.animname ][ anime ] ), "Called getanim on an inexistent anim" );
	return level.scr_anim[ self.animname ][ anime ];
}

/*
=============
///ScriptDocBegin
"Name: hasanim( <anime> )"
"Summary: Returns whether a guy has the animation referenced by the string for the animname of the caller."
"Module: Anim"
"CallOn: An entity that has an animname."
"MandatoryArg: <anime> The animation scene. "
"Example: if( guy hasanim( "run" ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
hasanim( anime )
{
	AssertEx( IsDefined( self.animname ), "Called getanim on a guy with no animname" );
	return IsDefined( level.scr_anim[ self.animname ][ anime ] );
}

/*
=============
///ScriptDocBegin
"Name: getanim_from_animname( <anime> , <animname> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getanim_from_animname( anime, animname )
{
	AssertEx( IsDefined( animname ), "Must supply an animname" );
	AssertEx( IsDefined( level.scr_anim[ animname ][ anime ] ), "Called getanim on an inexistent anim" );
	return level.scr_anim[ animname ][ anime ];
}

/*
=============
///ScriptDocBegin
"Name: getanim_generic( <anime> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getanim_generic( anime )
{
	AssertEx( IsDefined( level.scr_anim[ "generic" ][ anime ] ), "Called getanim_generic on an inexistent anim" );
	return level.scr_anim[ "generic" ][ anime ];
}

/*
=============
///ScriptDocBegin
"Name: add_hint_string( <name> , <string> , <optionalFunc> )"
"Summary: Adds a hint string that will be displayed with display_hint. The hint runs until the function returns true"
"Module: Hint"
"MandatoryArg: <name>: Name of the hint"
"MandatoryArg: <string>: String to display for the hint"
"MandatoryArg: <optionalFunc>: Draw the string until this function returns true."
// Press^3 [{+actionslot 1}] ^7to disable Night Vision Goggles.
"Example: add_hint_string( "disable_nvg", &"SCRIPT_NIGHTVISION_STOP_USE", maps\_nightvision::should_break_disable_nvg_print );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_hint_string( name, string, optionalFunc )
{
	if ( !isdefined( level.trigger_hint_string ) )
	{
		level.trigger_hint_string = [];
		level.trigger_hint_func = [];
	}

	AssertEx( IsDefined( name ), "Set a name for the hint string. This should be the same as the script_hint on the trigger_hint." );
	AssertEx( IsDefined( string ), "Set a string for the hint string. This is the string you want to appear when the trigger is hit." );
	AssertEx( !isdefined( level.trigger_hint_string[ name ] ), "Tried to redefine hint " + name );

	level.trigger_hint_string[ name ] = string;
	PreCacheString( string );
	if ( IsDefined( optionalFunc ) )
	{
		level.trigger_hint_func[ name ] = optionalFunc;
	}
}

show_hint( struct )
{
	AssertEx( IsDefined( struct.string ), "Need a localized string associated with the hint" );
	thread ShowHintPrint_struct( struct );
}

hide_hint( struct )
{
	struct.timeout = true;
}

fire_radius( origin, radius )
{
	/#
	if ( level.createFX_enabled )
		return;
	#/

	trigger = Spawn( "trigger_radius", origin, 0, radius, 48 );

	for ( ;; )
	{
		trigger waittill( "trigger", other );
		AssertEx( IsPlayer( other ), "Tried to burn a non player in a fire" );
		level.player DoDamage( 5, origin );
	}
}

clearThreatBias( group1, group2 )
{
	SetThreatBias( group1, group2, 0 );
	SetThreatBias( group2, group1, 0 );
}

// use in moderation!
ThrowGrenadeAtPlayerASAP()
{
	animscripts\combat_utility::ThrowGrenadeAtPlayerASAP_combat_utility();
}


/*
=============
///ScriptDocBegin
"Name: array_combine_keys( <array1> , <array2> )"
"Summary: "
"Module: Array"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"MandatoryArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_combine_keys( array1, array2 )// mashes them in. array 2 will overwrite like keys, this works for what I'm using it for - Nate.
{
	if ( !array1.size )
		return array2;
	keys = GetArrayKeys( array2 );
	for ( i = 0; i < keys.size; i++ )
		array1[ keys[ i ] ] = array2[ keys[ i ] ];
	return array1;
}

/*
=============
///ScriptDocBegin
"Name: set_ignoreSuppression( <val> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_ignoreSuppression( val )
{
	self.ignoreSuppression = val;
}

/*
=============
///ScriptDocBegin
"Name: set_goalradius( <radius> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_goalradius( radius )
{
	self.goalradius = radius;
}

try_forever_spawn()
{
	export = self.export;
	for ( ;; )
	{
		AssertEx( IsDefined( self ), "Spawner with export " + export + " was deleted." );
		guy = self DoSpawn();
		if ( spawn_failed( guy ) )
		{
			wait( 1 );
			continue;
		}
		return guy;
	}
}

/*
=============
///ScriptDocBegin
"Name: set_allowdeath( <val> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_allowdeath( val )
{
	self.allowdeath = val;
}

/*
=============
///ScriptDocBegin
"Name: set_run_anim( <anime> , <alwaysRunForward> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_run_anim( anime, alwaysRunForward )
{
	AssertEx( IsDefined( anime ), "Tried to set run anim but didn't specify which animation to ues" );
	AssertEx( IsDefined( self.animname ), "Tried to set run anim on a guy that had no anim name" );
	AssertEx( IsDefined( level.scr_anim[ self.animname ][ anime ] ), "Tried to set run anim but the anim was not defined in the maps _anim file" );

	//this is good for slower run animations like patrol walks
	if ( IsDefined( alwaysRunForward ) )
		self.alwaysRunForward = alwaysRunForward;
	else
		self.alwaysRunForward = true;

	self disable_turnAnims();
	self.run_overrideanim = level.scr_anim[ self.animname ][ anime ];
	self.walk_overrideanim = self.run_overrideanim;
}

/*
=============
///ScriptDocBegin
"Name: set_dog_walk_anim( <set_dog_walk_anim> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_dog_walk_anim()
{
	AssertEx( self.type == "dog" );

	self.a.movement = "walk";
	self.disablearrivals = true;
	self.disableexits = true;
	self.script_nobark = 1;
}


/*
=============
///ScriptDocBegin
"Name: set_combat_stand_animset( <fire>, <aim_straight>, <idle>, <reload> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_combat_stand_animset( fire_anim, aim_straight, idle_anim, reload_anim )
{
	self animscripts\animset::init_animset_custom_stand( fire_anim, aim_straight, idle_anim, reload_anim );
}


/*
=============
///ScriptDocBegin
"Name: set_move_animset( <move_mode>, <move_anim>, <sprint_anim> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_move_animset( move_mode, move_anim, sprint_anim )
{
	AssertEx( IsDefined( anim.archetypes["soldier"][move_mode] ), "Default anim set is not defined" );

	// init with defaults
	animset = self lookupAnimArray( move_mode );

	if ( IsArray( move_anim ) )
	{
		Assert( move_anim.size == 4 );

		animset[ "straight" ] = move_anim[ 0 ];

		animset[ "move_f" ] = move_anim[ 0 ];
		animset[ "move_l" ] = move_anim[ 1 ];
		animset[ "move_r" ] = move_anim[ 2 ];
		animset[ "move_b" ] = move_anim[ 3 ];
	}
	else
	{
		animset[ "straight" ] = move_anim;
		animset[ "move_f" ] = move_anim;
	}

	if ( IsDefined( sprint_anim ) )
		animset[ "sprint" ] = sprint_anim;

	self.customMoveAnimSet[ move_mode ] = animset;
}


/*
=============
///ScriptDocBegin
"Name: set_generic_idle_anim( <anime> )"
"Summary: Sets the override idle anim, needs to be set each time he idles"
"Module: Utility"
"CallOn: An ai"
"MandatoryArg: <anime>: The idle scene "
"Example: guy set_idle_anim( "yay" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_generic_idle_anim( anime )
{
	AssertEx( IsDefined( anime ), "Tried to set generic idle but didn't specify which animation to ues" );
	AssertEx( IsDefined( level.scr_anim[ "generic" ][ anime ] ), "Tried to set generic run anim but the anim was not defined in the maps _anim file" );

	idleAnim = level.scr_anim[ "generic" ][ anime ];

	if ( IsArray( idleAnim ) )
		self.specialIdleAnim = idleAnim;
	else
		self.specialIdleAnim[ 0 ] = idleAnim;
}

/*
=============
///ScriptDocBegin
"Name: set_idle_anim( <anime> )"
"Summary: Sets the override idle anim, needs to be set each time he idles"
"Module: Utility"
"CallOn: An ai"
"MandatoryArg: <anime>: The idle scene "
"Example: guy set_idle_anim( "yay" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_idle_anim( anime )
{
	AssertEx( IsDefined( self.animname ), "No animname!" );
	AssertEx( IsDefined( anime ), "Tried to set idle anim but didn't specify which animation to ues" );
	AssertEx( IsDefined( level.scr_anim[ self.animname ][ anime ] ), "Tried to set generic run anim but the anim was not defined in the maps _anim file" );

	idleAnim = level.scr_anim[ self.animname ][ anime ];

	if ( IsArray( idleAnim ) )
		self.specialIdleAnim = idleAnim;
	else
		self.specialIdleAnim[ 0 ] = idleAnim;
}


/*
=============
///ScriptDocBegin
"Name: clear_generic_idle_anim()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
clear_generic_idle_anim()
{
	self.specialIdleAnim = undefined;
	self notify( "stop_specialidle" );
}


/*
=============
///ScriptDocBegin
"Name: set_generic_run_anim( <anime> , <alwaysRunForward> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_generic_run_anim( anime, alwaysRunForward )
{
	set_generic_run_anim_array( anime, undefined, alwaysRunForward );
}

/*
=============
///ScriptDocBegin
"Name: clear_generic_run_anim()"
"Summary: clear the generic run anims set by set_generic_run_anim "
"Module: Entity"
"CallOn: An entity"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

clear_generic_run_anim()
{
	self notify( "movemode" );
	self enable_turnAnims();
	self.run_overrideanim = undefined;
	self.walk_overrideanim = undefined;
}


/*
=============
///ScriptDocBegin
"Name: set_generic_run_anim_array( <array>, <weights>, <alwaysRunForward> )"
"Summary: "
"Module: AI"
"CallOn: An AI"
"MandatoryArg: <string anime>: index to an array of run cycles (run cycle and twitches) to pass"
"OptionalArg: <string weights>: relative weights of animation variations, if not specified, all are equal"
"OptionalArg: <alwaysRunForward>: bool to always face in the direction of motion or not"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_generic_run_anim_array( anime, weights, alwaysRunForward )
{
	AssertEx( IsDefined( anime ), "Tried to set generic run anim but didn't specify which animation to ues" );
	AssertEx( IsDefined( level.scr_anim[ "generic" ][ anime ] ), "Tried to set generic run anim but the anim was not defined in the maps _anim file" );
	AssertEx( !isdefined(weights) || isdefined(level.scr_anim[ "generic" ][weights]), "weights needs to be a valid entry in level.scr_anim" );
	//weights has to be undefined or an array
	//we saw improper use passing in a 2nd animation as the weights param
	AssertEx( !isdefined(weights) || isarray(level.scr_anim[ "generic" ][weights]), "weights needs to be an array of animation weights (ascending order)" );
	
	//if you dont pass in an array of animations you should not be passing in a weights param
	AssertEx( isarray(level.scr_anim[ "generic" ][anime]) 
		|| !isdefined(weights), "its not valid to pass in a weights param and not an array of anims to run" );
	
	//if you do pass in weights it needs to match the size of the anime array that is passed in
	AssertEx( !isdefined(weights) || (level.scr_anim[ "generic" ][weights].size == level.scr_anim[ "generic" ][anime].size), "the weights array must equal the size of the anims array" );
	
	self notify( "movemode" );

	//this is good for slower run animations like patrol walks
	if ( !isdefined( alwaysRunForward ) || alwaysRunForward )
		self.alwaysRunForward = true;
	else
		self.alwaysRunForward = undefined;

	self disable_turnAnims();
	self.run_overrideanim = level.scr_anim[ "generic" ][ anime ];
	self.walk_overrideanim = self.run_overrideanim;

	if ( IsDefined( weights ) )
	{
		self.run_override_weights = level.scr_anim[ "generic" ][ weights ];
		self.walk_override_weights = self.run_override_weights;
	}
	else
	{
		self.run_override_weights = undefined;
		self.walk_override_weights = undefined;
	}
}

/*
=============
///ScriptDocBegin
"Name: set_run_anim_array( <array>, <weights>, <alwaysRunForward> )"
"Summary: "
"Module: AI"
"CallOn: An AI"
"MandatoryArg: <string anime>: index to an array of run cycles (run cycle and twitches) to pass"
"OptionalArg: <string weights>: relative weights of animation variations, if not specified, all are equal"
"OptionalArg: <alwaysRunForward>: bool to always face in the direction of motion or not"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_run_anim_array( anime, weights, alwaysRunForward )
{
	AssertEx( IsDefined( anime ), "Tried to set generic run anim but didn't specify which animation to ues" );
	AssertEx( IsDefined( self.animname ), "Tried to set run anim on a guy that had no anim name" );
	AssertEx( IsDefined( level.scr_anim[ self.animname ][ anime ] ), "Tried to set run anim but the anim was not defined in the maps _anim file" );
	self notify( "movemode" );

	//this is good for slower run animations like patrol walks
	if ( !isdefined( alwaysRunForward ) || alwaysRunForward )
		self.alwaysRunForward = true;
	else
		self.alwaysRunForward = undefined;

	self disable_turnAnims();
	self.run_overrideanim = level.scr_anim[ self.animname ][ anime ];
	self.walk_overrideanim = self.run_overrideanim;

	if ( IsDefined( weights ) )
	{
		self.run_override_weights = level.scr_anim[ self.animname ][ weights ];
		self.walk_override_weights = self.run_override_weights;
	}
	else
	{
		self.run_override_weights = undefined;
		self.walk_override_weights = undefined;
	}
}


/*
=============
///ScriptDocBegin
"Name: clear_run_anim( <clear_run_anim> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
clear_run_anim()
{
	self notify( "clear_run_anim" );
	self notify( "movemode" );

	if ( self.type == "dog" )
	{
		self.a.movement = "run";
		self.disablearrivals = false;
		self.disableexits = false;
		self.script_nobark = undefined;
		return;
	}

	if ( !isdefined( self.casual_killer ) )
		self enable_turnAnims();

	self.alwaysRunForward = undefined;
	self.run_overrideanim = undefined;
	self.walk_overrideanim = undefined;

	self.run_override_weights = undefined;
	self.walk_override_weights = undefined;
}

debugvar( msg, timer )
{
	SetDvarIfUninitialized( msg, timer );
	return GetDvarFloat( msg );
}

physicsjolt_proximity( outer_radius, inner_radius, force )
{
	// Usage: <entity > thread physicjolt_proximity( 400, 256, ( 0, 0, 0.1 ) );

	self endon( "death" );
	self endon( "stop_physicsjolt" );

	if ( !isdefined( outer_radius ) || !isdefined( inner_radius ) || !isdefined( force ) )
	{
		outer_radius = 400;
		inner_radius = 256;
		force = ( 0, 0, 0.075 );	// no direction on this one.
	}

	fade_distance = outer_radius * outer_radius;

	fade_speed = 3;
	base_force = force;

	while ( true )
	{
		wait 0.1;

		force = base_force;

		if ( self.code_classname == "script_vehicle" )
		{
			speed = self Vehicle_GetSpeed();
			if ( speed < fade_speed )
			{
				scale = speed / fade_speed;
				force = ( base_force* scale );
			}
		}

		dist = DistanceSquared( self.origin, level.player.origin );
		scale = fade_distance / dist;
		if ( scale > 1 )
			scale = 1;
		force = ( force* scale );
		total_force = force[ 0 ] + force[ 1 ] + force[ 2 ];

		if ( total_force > 0.025 )
			PhysicsJitter( self.origin, outer_radius, inner_radius, force[ 2 ], force[ 2 ] * 2.0 );
	}
}

/*
=============
///ScriptDocBegin
"Name: set_goal_entity( <ent> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_goal_entity( ent )
{
	self SetGoalEntity( ent );
}

/*
=============
///ScriptDocBegin
"Name: activate_trigger( <name> , <type> )"
"Summary: Notifies a trigger of 'trigger'"
"Module: Trigger"
"MandatoryArg: <name>: string to get the entity with"
"MandatoryArg: <type>: type that string refers to (ie. targetname, script_noteworthy, target )"
"Example: activate_trigger( "red_team1", "targetname" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
activate_trigger( name, type, triggeringEnt )
{
	if ( !isdefined( name ) )
		self activate_trigger_process( triggeringEnt );
	else
		array_thread( GetEntArray( name, type ), ::activate_trigger_process, triggeringEnt );
}

activate_trigger_process( triggeringEnt )
{
	AssertEx( !isdefined( self.trigger_off ), "Tried to activate trigger that is OFF( either from trigger_off or from flags set on it through shift - G menu" );

	self notify( "trigger", triggeringEnt );
}

 /*
 =============
///ScriptDocBegin
"Name: self_delete()"
"Summary: Just calls the Delete() script command on self. Reason for this is so that we can use array_thread to delete entities"
"Module: Entity"
"CallOn: An entity"
"Example: ai[ 0 ] thread self_delete();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
self_delete()
{
	self Delete();
}

remove_noColor_from_array( ai )
{
	newarray = [];
	for ( i = 0; i < ai.size; i++ )
	{
		guy = ai[ i ];
		if ( guy has_color() )
			newarray[ newarray.size ] = guy;
	}

	return newarray;
}

clear_colors()
{
	clear_team_colors( "axis" );
	clear_team_colors( "allies" );
}

clear_team_colors( team )
{
	level.currentColorForced[ team ][ "r" ] = undefined;
	level.currentColorForced[ team ][ "b" ] = undefined;
	level.currentColorForced[ team ][ "c" ] = undefined;
	level.currentColorForced[ team ][ "y" ] = undefined;
	level.currentColorForced[ team ][ "p" ] = undefined;
	level.currentColorForced[ team ][ "o" ] = undefined;
	level.currentColorForced[ team ][ "g" ] = undefined;
}


/*
=============
///ScriptDocBegin
"Name: get_script_palette( <get_script_palette> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_script_palette()
{
	rgb = [];
	rgb[ "r" ] = ( 1, 0, 0 );
	rgb[ "o" ] = ( 1, 0.5, 0 );
	rgb[ "y" ] = ( 1, 1, 0 );
	rgb[ "g" ] = ( 0, 1, 0 );
	rgb[ "c" ] = ( 0, 1, 1 );
	rgb[ "b" ] = ( 0, 0, 1 );
	rgb[ "p" ] = ( 1, 0, 1 );
	return rgb;
}

 /*
 =============
///ScriptDocBegin
"Name: notify_delay( <notify_string> , <delay> )"
"Summary: Notifies self the string after waiting the specified delay time"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <notify_string> : The string to notify"
"MandatoryArg: <delay> : Time to wait( in seconds ) before sending the notify."
"Example: vehicle notify_delay( "start_to_smoke", 3.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
notify_delay( sNotifyString, fDelay )
{
	Assert( IsDefined( self ) );
	Assert( IsDefined( sNotifyString ) );
	Assert( IsDefined( fDelay ) );

	self endon( "death" );
	if ( fDelay > 0 )
		wait fDelay;
	if ( !isdefined( self ) )
		return;
	self notify( sNotifyString );
}

 /*
 =============
///ScriptDocBegin
"Name: gun_remove()"
"Summary: Removed the gun from the given AI. Often used for scripted sequences where you dont want the AI to carry a weapon."
"Module: AI"
"CallOn: An AI"
"Example: level.price gun_remove();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
gun_remove()
{
	if ( IsAI( self ) )		// regular AI
	{
		self animscripts\shared::placeWeaponOn( self.weapon, "none" );
	}
	else					// Drone
	{
		self detach_attachments_from_weapon_model( self.weapon );
		self Detach( GetWeaponModel( self.weapon ), "tag_weapon_right" );
	}
}


 /*
 =============
///ScriptDocBegin
"Name: gun_recall()"
"Summary: Give the AI his gun back."
"Module: AI"
"CallOn: An AI"
"Example: level.price gun_recall();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
 */
gun_recall()
{
	if ( IsAI( self ) )		// regular AI
	{
		self animscripts\shared::placeWeaponOn( self.weapon, "right" );
	}
	else					// Drone
	{
		self Attach( GetWeaponModel( self.weapon ), "tag_weapon_right" );
		self update_weapon_tag_visibility( self.weapon );		
	}
}



/* 
 ============= 
///ScriptDocBegin
"Name: update_weapon_tag_visibility()"
"Summary: Make sure to hide all the correct tags on a weapon"
"Module: SP"
"CallOn: the object whos weapon needs to be updated"
"MandatoryArg: <weapon> : the weapon we're updating"
"Example: level.sandman update_weapon_tag_visibility( self.primaryweapon )"
"Module: Utility (SHG)"
"SPMP: singleplayer"
///ScriptDocEnd
 ============= 
 */ 
update_weapon_tag_visibility( weapon )
{
	if( isdefined( weapon ) && weapon != "none")
	{
		weapon_and_attachment_models_array = GetWeaponAndAttachmentModels( weapon );
		attachments = array_remove_index( weapon_and_attachment_models_array, 0 );		
		foreach ( attachment in attachments )
		{
			self Attach( attachment[ "worldModel" ], attachment[ "attachTag" ] );
		}		
		
		self HideWeaponTags( weapon );
	}	
}



detach_attachments_from_weapon_model( weapon )
{
	if( isdefined( weapon ) && weapon != "none" )
	{
		weapon_and_attachment_models_array = GetWeaponAndAttachmentModels( weapon );
		attachments = array_remove_index( weapon_and_attachment_models_array, 0 );		
		foreach ( attachment in attachments )
		{
			self Detach( attachment[ "worldModel" ], attachment[ "attachTag" ], false );
		}		
	}	
}




/*
=============
///ScriptDocBegin
"Name: attach_player_current_weapon_to_rig( <rig> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <rig>: the worldbody or worldhands rig you want the gun to attach to"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

attach_player_current_weapon_to_rig( rig )
{
	weapon = level.player GetCurrentWeapon();
	
	weapon_and_attachment_models_array = GetWeaponAndAttachmentModels( weapon );
	
	// item 0 is the gun itself
	weapon_viewmodel = weapon_and_attachment_models_array[0][ "weapon" ];
	
	// items 1 and up are any attachment models that need to be added
	attachments = array_remove_index( weapon_and_attachment_models_array, 0 );
	
	rig Attach( weapon_viewmodel, "TAG_WEAPON_RIGHT", true);

	foreach ( attachment in attachments )
	{
		rig Attach( attachment[ "attachment" ], attachment[ "attachTag" ] );
	}	
	 
	rig HideWeaponTags( weapon );
}

 /*
 =============
///ScriptDocBegin
"Name: place_weapon_on( <weapon>, <location> )"
"Summary: Equip a wepaon on an AI."
"Module: AI"
"CallOn: An AI"
"MandatoryArg: <weapon> : The name of the weapon to equip"
"MandatoryArg: <> : Slot to store the weapon in. 'right', 'left', 'chest', or 'back'."
"Example: level.price place_weapon_on( "at4", "back" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
place_weapon_on( weapon, location )
{
	Assert( IsAI( self ) );
	
	if ( !AIHasWeapon( weapon ) )
		animscripts\init::initWeapon( weapon );
	
	animscripts\shared::placeWeaponOn( weapon, location );
}


 /*
 =============
///ScriptDocBegin
"Name: forceUseWeapon( <newWeapon>, <targetSlot> )"
"Summary: Forces the AI to switch to a specified weapon."
"Module: AI"
"CallOn: An AI"
"MandatoryArg: <newWeapon> : The name of the weapon to use/give"
"MandatoryArg: <target slot> : Slot to store the weapon in. primary, secondary or sidearm."
"Example: level.price forceUseWeapon( "glock", "sidearm" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
forceUseWeapon( newWeapon, targetSlot )
{
	Assert( IsDefined( newWeapon ) );
	Assert( newWeapon != "none" );
	Assert( IsDefined( targetSlot ) );
	AssertEx( ( targetSlot == "primary" ) || ( targetSlot == "secondary" ) || ( targetSlot == "sidearm" ), "Target slot is either primary, secondary or sidearm." );

	// Setup the weaponInfo if it wasn't already done
	if ( !animscripts\init::isWeaponInitialized( newWeapon ) )
		animscripts\init::initWeapon( newWeapon );

	// Figure out whether the current and target weapons are side arms, and which slot to go to
	hasWeapon = ( self.weapon != "none" );
	isCurrentSideArm = usingSidearm();
	isNewSideArm = ( targetSlot == "sidearm" );
	isNewSecondary = ( targetSlot == "secondary" );

	// If we have a weapon and we're not replacing it with one of the same "type", we need to hoslter it first
	if ( hasWeapon && ( isCurrentSideArm != isNewSideArm ) )
	{
		Assert( self.weapon != newWeapon );

		// Based on the current weapon - Hide side arms completely, and holster based on the new target otherwise
		if ( isCurrentSideArm )
			holsterTarget = "none";
		else if ( isNewSecondary )
			holsterTarget = "back";
		else
			holsterTarget = "chest";
		animscripts\shared::placeWeaponOn( self.weapon, holsterTarget );

		// Remember we switched out of that weapon
		self.lastWeapon = self.weapon;
	}
	else
	{
		// We didn't have a weapon before, or we're going to loose the one we had, so reset the lastWeapon.
		self.lastWeapon = newWeapon;
	}

	// Put the new weapon in hand
	animscripts\shared::placeWeaponOn( newWeapon, "right" );

	// Replace the equipped weapon slot of the same type with the new weapon ( could stay the same, too )
	// If the AI was using a secondary, replace that slot instead of primary
	if ( isNewSideArm )
		self.sideArm = newWeapon;
	else if ( isNewSecondary )
		self.secondaryweapon = newWeapon;
	else
		self.primaryweapon = newWeapon;

	// Set our current weapon to the new one
	self.weapon = newWeapon;
	self.bulletsinclip = WeaponClipSize( self.weapon );
	self notify( "weapon_switch_done" );
}


/*
=============
///ScriptDocBegin
"Name: lerp_player_view_to_tag( <player>, <tag> , <lerptime> , <fraction> , <right_arc> , <left_arc> , <top_arc> , <bottom_arc> )"
"Summary: Lerps the player's view to the tag on the entity that calls the function.."
"Module: Utility"
"CallOn: An entity you want to lerp the player's view to."
"MandatoryArg: <player> : Player to do the lerp on"
"MandatoryArg: <tag> : Tag on the entity that you're lerping to."
"MandatoryArg: <lerptime> : Time to lerp over."
"OptionalArg: <fraction> : 0 to 1 amount that the rotation of the player's view should be effected by the entity. If you set it less than 1 then the player's view will not get all the way to the final angle."
"OptionalArg: <arcs> : Various arcs that limit how far the player can change his view."
"Example: car lerp_player_view_to_tag( "tag_windshield", 1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

lerp_player_view_to_tag( player, tag, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc )
{
	lerp_player_view_to_tag_internal( player, tag, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc, false );
}

/*
=============
///ScriptDocBegin
"Name: lerp_player_view_to_tag_and_hit_geo( <player>, <tag> , <lerptime> , <fraction> , <right_arc> , <left_arc> , <top_arc> , <bottom_arc> )"
"Summary: Lerps the player's view to the tag on the entity that calls the function. Geo will block the player."
"Module: Utility"
"CallOn: An entity you want to lerp the player's view to."
"MandatoryArg: <player> : Player to do the lerp on"
"MandatoryArg: <tag> : Tag on the entity that you're lerping to."
"MandatoryArg: <lerptime> : Time to lerp over."
"OptionalArg: <fraction> : 0 to 1 amount that the rotation of the player's view should be effected by the entity. If you set it less than 1 then the player's view will not get all the way to the final angle."
"OptionalArg: <arcs> : Various arcs that limit how far the player can change his view."
"Example: car lerp_player_view_to_tag_and_hit_geo( "tag_windshield", 1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

lerp_player_view_to_tag_and_hit_geo( player, tag, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc )
{
	lerp_player_view_to_tag_internal( player, tag, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc, true );
}

 /*
 =============
///ScriptDocBegin
"Name: lerp_player_view_to_position( <origin> , <angles> , <lerptime> , <fraction> , <right_arc> , <left_arc> , <top_arc> , <bottom_arc>, <hit_geo> )"
"Summary: Lerps the player's view to an origin and angles. See lerp_player_view_to_tag."
"Module: Utility"
"CallOn: A player (optional. If not cvalled on player, will default to level.player)"
"MandatoryArg: <origin> : The origin you're lerping to."
"MandatoryArg: <angles> : The angles you're lerping to."
"MandatoryArg: <lerptime> : Time to lerp over."
"OptionalArg: <fraction> : 0 to 1 amount that the rotation of the player's view should be effected by the destination angles. If you set it less than 1 then the player's view will not get all the way to the final angle."
"OptionalArg: <arcs> : Various arcs that limit how far the player can change his view."
"OptionalArg: <hit_geo> : Sets if the player will hit geo."
"Example: lerp_player_view_to_position( org.origin, org.angles );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

lerp_player_view_to_position( origin, angles, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc, hit_geo, player )
{
	player = get_player_from_self();

	linker = Spawn( "script_origin", ( 0, 0, 0 ) );
	linker.origin = player.origin;
	linker.angles = player GetPlayerAngles();

	if ( IsDefined( hit_geo ) && hit_geo )
	{
		player PlayerLinkTo( linker, "", fraction, right_arc, left_arc, top_arc, bottom_arc, hit_geo );
	}
	else
	if ( IsDefined( right_arc ) )
	{
		player PlayerLinkTo( linker, "", fraction, right_arc, left_arc, top_arc, bottom_arc );
	}
	else
	if ( IsDefined( fraction ) )
	{
		player PlayerLinkTo( linker, "", fraction );
	}
	else
	{
		player PlayerLinkTo( linker );
	}

	linker MoveTo( origin, lerptime, lerptime * 0.25 );
	linker RotateTo( angles, lerptime, lerptime * 0.25 );
	wait( lerptime );
	linker Delete();
}


/*
=============
///ScriptDocBegin
"Name: lerp_player_view_to_tag_oldstyle( <player>, <tag> , <lerptime> , <fraction> , <right_arc> , <left_arc> , <top_arc> , <bottom_arc> )"
"Summary: Lerps the player's view to the tag on the entity that calls the function, using the oldstyle link which moves the player's view when the tag rotates."
"Module: Utility"
"CallOn: An entity you want to lerp the player's view to."
"MandatoryArg: <player> : Which player."
"MandatoryArg: <tag> : Tag on the entity that you're lerping to."
"MandatoryArg: <lerptime> : Time to lerp over."
"OptionalArg: <fraction> : 0 to 1 amount that the rotation of the player's view should be effected by the entity. If you set it less than 1 then the player's view will not get all the way to the final angle."
"OptionalArg: <arcs> : Various arcs that limit how far the player can change his view."
"Example: car lerp_player_view_to_tag_oldstyle( "tag_windshield", 1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
lerp_player_view_to_tag_oldstyle( player, tag, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc )
{
	lerp_player_view_to_tag_oldstyle_internal( player, tag, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc, false );
}

 /*
 =============
///ScriptDocBegin
"Name: lerp_player_view_to_position_oldstyle( <origin> , <angles> , <lerptime> , <fraction> , <right_arc> , <left_arc> , <top_arc> , <bottom_arc>, <hit_geo> )"
"Summary: Lerps the player's view to an origin and angles. See lerp_player_view_to_tag_oldstyle. Oldstyle means that you're going to move to the point where the player's feet would be, rather than directly below the point where the view would be."
"Module: Utility"
"MandatoryArg: <origin> : The origin you're lerping to."
"MandatoryArg: <angles> : The angles you're lerping to."
"MandatoryArg: <lerptime> : Time to lerp over."
"OptionalArg: <fraction> : 0 to 1 amount that the rotation of the player's view should be effected by the destination angles. If you set it less than 1 then the player's view will not get all the way to the final angle."
"OptionalArg: <arcs> : Various arcs that limit how far the player can change his view."
"OptionalArg: <hit_geo> : Sets if the player will hit geo."
"Example: lerp_player_view_to_position_oldstyle( org.origin, org.angles );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

lerp_player_view_to_position_oldstyle( origin, angles, lerptime, fraction, right_arc, left_arc, top_arc, bottom_arc, hit_geo )
{
	player = get_player_from_self();

	linker = Spawn( "script_origin", ( 0, 0, 0 ) );
	linker.origin = player get_player_feet_from_view();
	linker.angles = player GetPlayerAngles();

	if ( IsDefined( hit_geo ) )
	{
		player PlayerLinkToDelta( linker, "", fraction, right_arc, left_arc, top_arc, bottom_arc, hit_geo );
	}
	else
	if ( IsDefined( right_arc ) )
	{
		player PlayerLinkToDelta( linker, "", fraction, right_arc, left_arc, top_arc, bottom_arc );
	}
	else
	if ( IsDefined( fraction ) )
	{
		player PlayerLinkToDelta( linker, "", fraction );
	}
	else
	{
		player PlayerLinkToDelta( linker );
	}

	linker MoveTo( origin, lerptime, lerptime * 0.25 );
	linker RotateTo( angles, lerptime, lerptime * 0.25 );
	wait( lerptime );
	linker Delete();
}

 /*
 =============
///ScriptDocBegin
"Name: player_moves( <dist> )"
"Summary: Returns when the player has moved < dist > distance."
"Module: Utility"
"MandatoryArg: <dist> : The distance the player must move for the function to return."
"Example: player_moves( 500 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

player_moves( dist )
{
	org = level.player.origin;
	for ( ;; )
	{
		if ( Distance( org, level.player.origin ) > dist )
			break;
		wait( 0.05 );
	}
}


 /*
 =============
///ScriptDocBegin
"Name: waittill_either_function( <func1> , <parm1> , <func2> , <parm2> )"
"Summary: Returns when either func1 or func2 have returned."
"Module: Utility"
"MandatoryArg: <func1> : A function pointer to a function that may return at some point."
"MandatoryArg: <func2> : Another function pointer to a function that may return at some point."
"OptionalArg: <parm1> : An optional parameter for func1."
"OptionalArg: <parm2> : An optional parameter for func2."
"Example: player_moves( 500 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */

waittill_either_function( func1, parm1, func2, parm2 )
{
	ent = SpawnStruct();
	thread waittill_either_function_internal( ent, func1, parm1 );
	thread waittill_either_function_internal( ent, func2, parm2 );
	ent waittill( "done" );
}

/*
=============
///ScriptDocBegin
"Name: waittill_msg( <msg> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_msg( msg )
{
	self waittill( msg );
}


 /*
 =============
///ScriptDocBegin
"Name: display_hint( <hint>, [parm1], [parm2], [parm3], [y_offset] )"
"Summary: Displays a hint created with add_hint_string."
"Module: Utility"
"MandatoryArg: <hint> : The hint reference created with add_hint_string."
"OptionalArg: <parm1>: "
"OptionalArg: <parm2>: "
"OptionalArg: <parm3>: "
"OptionalArg: [y_offset]: move the hint lower or higher on the screen"
"Example: display_hint( "huzzah" )"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
display_hint( hint, parm1, parm2, parm3, y_offset )
{
	player = get_player_from_self();

	// hint triggers have an optional function they can boolean off of to determine if the hint will occur
	// such as not doing the NVG hint if the player is using NVGs already
	if ( IsDefined( level.trigger_hint_func[ hint ] ) )
	{
		if ( player [[ level.trigger_hint_func[ hint ] ]]() )
			return;

		player thread HintPrint( level.trigger_hint_string[ hint ], level.trigger_hint_func[ hint ], parm1, parm2, parm3, undefined, undefined, y_offset );
	}
	else
	{
		player thread HintPrint( level.trigger_hint_string[ hint ], undefined, undefined, undefined, undefined, undefined, undefined, y_offset );
	}
}

/*
=============
///ScriptDocBegin
"Name: HintDisplayHandler( <hint>, <timeout>, <param1> )"
"Summary: ONLY use this display hint func if the given add_hint_string <name> is associtated to a precached string that has both a gamepad and PC version (Hold X AND Press X)"
"Module: Utility"
"CallOn: level"
"MandatoryArg: <name> : The hint reference created with add_hint_string."
"OptionalArg: <timeout> : Optional argument, if it exists we use display hint timeout with this timeout. Do not pass in, or set to undefined for the hint to not timeout"
"OptionalArg: <param1> : Optional argument, additional parameters for the break out function."
"OptionalArg: <param2> : Optional argument, additional parameters for the break out function."
"OptionalArg: <param3> : Optional argument, additional parameters for the break out function."
"OptionalArg: <y_offset> : Optional argument, additional parameters for the break out function."
"Example: HintDisplayHandler( "climb_wall" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
HintDisplayHandler(hint, timeout, param1, param2, param3, y_offest)
{
	HintDisplayHandlerSetup(hint);
	
	if(!IsDefined(timeout))
		display_hint(hint, param1, param2, param3, y_offest);
	else
		display_hint_timeout( hint, timeout, param1, param2, param3, y_offest);
}

/*
=============
///ScriptDocBegin
"Name: HintDisplayMintimeHandler( <hint> , <timeout> , <mintime> , <parm1> , <parm2> , <parm3> )"
"Summary: Displays a hint, but will only start checking the break out function after the mintime has been reached"
"Module: Entity"
"CallOn: An Entity"
"MandatoryArg: <hint>: The hint reference created with add_hint_string."
"MandatoryArg: <timeout>: time before it times out"
"MandatoryArg: <mintime>: the minimum amount of time we give the player before getting rid of the hint"
"OptionalArg: <parm1>: "
"OptionalArg: <parm2>: "
"OptionalArg: <parm3>: "
"Example: thread HintDisplayMintimeHandler( "drone_move", 30.0, 10.0 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

HintDisplayMintimeHandler( hint, timeout, mintime, param1, param2, param3 )
{
	player = get_player_from_self();
	AssertEx( IsDefined( level.trigger_hint_func[ hint ] ), "Can't have a hint with a timeout if is has no break function, because hints without break functions display for a set period of time." );

	// hint triggers have an optional function they can boolean off of to determine if the hint will occur
	// such as not doing the NVG hint if the player is using NVGs already
	if ( player [[ level.trigger_hint_func[ hint ] ]]() )
		return;
	
	HintDisplayHandlerSetup(hint);
	
	player thread HintPrint( level.trigger_hint_string[hint], level.trigger_hint_func[ hint ], param1, param2, param3, timeout, mintime );
}

/*
=============
///ScriptDocBegin
"Name: add_control_based_hint_strings( <name> , <defaultGamepadString> , <breakFunc> , <pcString> , <southpawString> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <name>: the name of the hint, will be referenced when calling HintDisplayHandler"
"MandatoryArg: <defaultGamepadString>: The default localized string for the hint, generally the gamepad version if there is more than one version"
"MandatoryArg: <breakFunc>: function that, when returning true, will break out of showing the hint"
"OptionalArg: <pcString>: optional pc version of the hint string, in the form of a localized string"
"OptionalArg: <southpawString>: optional southpaw gamepad version of the hint string, in the form of a localized string"
"Example: add_control_based_hint_strings( "player_input_rappel_hint", &"LAB_RAPPEL_B_HINT_CONTROLLER", maps\lab_code::player_input_rappel_hint_off, &"LAB_RAPPEL_B_HINT_PC", &"LAB_RAPPEL_B_HINT_CONTROLLER_SP" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

add_control_based_hint_strings( name, defaultGamepadString, breakFunc, pcString, southpawString )
{
	if ( !isdefined( level.trigger_hint_string ) )
	{
		level.trigger_hint_string = [];
		level.trigger_hint_func = [];
	}

	AssertEx( IsDefined( name ), "Set a name for the hint string. This should be the same as the script_hint on the trigger_hint." );
	AssertEx( IsDefined( defaultGamepadString ), "Set a string for the hint string. This is the string you want to appear when the trigger is hit." );
	AssertEx( !IsDefined( level.trigger_hint_string[ name ] ), "Tried to redefine hint " + name );
	
	level.trigger_hint_string[ name ] 	= defaultGamepadString;
	level.hint_list[name]["gamepad"] 	= defaultGamepadString;
	level.hint_list[name]["pc"] 		= pcString;
	level.hint_list[name]["southpaw"] 	= southpawString;
	
	PreCacheString( defaultGamepadString );
	
	if(IsDefined(pcString))
		PreCacheString( pcString );
	if(IsDefined(southpawString))
		PreCacheString( southpawString );
	
	if ( IsDefined( breakFunc ) )
	{
		level.trigger_hint_func[ name ] = breakFunc;
	}
	
	//the name of the hint needs to exist, otherwise, assert
	AssertEx( isdefined( level.trigger_hint_string[ name ] ), name + " is not a valid hint string." );
}

/*
=============
///ScriptDocBegin
"Name: HandleTriggerHintInputTypeText(  )"
"Summary: Call this function at the start of your Main function to handle the text that use triggers should display based on user input type (PC vs Gamepad). Note: While using this: instead of calling 'SetHintString' on a use trigger, use  the 'AddHintTrigger' function"
"Module: Utility"
"Example: thread HandleTriggerHintInputTypeText()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
HandleTriggerHintInputTypeText()
{	
	if(!IsDefined(level.hint_triggers))
		level.hint_triggers = [];
	
	while( 1 )
	{
		level.hint_triggers = array_removeUndefined(level.hint_triggers);
		if(IsDefined(level.hint_triggers) && IsDefined(level.player))
		{
			foreach(trigger in level.hint_triggers)
			{
				if ( level.player is_player_gamepad_enabled() )
    				trigger SetHintString( trigger.gp_hint_text );
       			else
    				trigger SetHintString( trigger.pc_hint_text );
			}
		}
		wait(0.1);
	}
}

/*
=============
///ScriptDocBegin
"Name: AddHintTrigger( <trigger>, <gamepadHintText>, <pcHintText> )"
"Summary: Adds the trigger to an array whose hint text strings are updated as input changes between gamepad and PC.  Thread this func"
"Module: Utility"
"CallOn: the trigger that you want to display the hint on"
"MandatoryArg: <gamepadHintText> : The localized string that will be displayed while using gamepad."
"MandatoryArg: <pcHintText> : The localized string that will be displayed while using PC."
"Example: thread AddHintTrigger(self, &EXOCLIMB_CLIMB_HINT, &EXOCLIMB_CLIMB_HINT_PC);"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
AddHintTrigger(gamepadHintText, pcHintText)
{	
	//make sure self is a use trigger, and make sure that the user has passed in the required strings
	AssertEx(IsDefined(gamepadHintText), "gamepadHintText must be defined");
	AssertEx(IsDefined(pcHintText), "pcHintText must be defined");
	
	if(!IsDefined(level.hint_triggers))
	{
		thread HandleTriggerHintInputTypeText();
		level.hint_triggers = [];
	}
	
	trigger_already_exists = false;
	
	//make sure that this trigger isnt already in our array...
	foreach(trig in level.hint_triggers)
	{
		//if it is, just update the already added trigger's hint values with the new ones
		if(self == trig)
		{
			trig.gp_hint_text = gamepadHintText;
			trig.pc_hint_text = pcHintText;
			trigger_already_exists = true;
			break;
		}
	}
	
	//if the trigger hasnt been added already, add it with refs to the different hint text types
	if(!trigger_already_exists)
	{
		self.gp_hint_text = gamepadHintText;
		self.pc_hint_text = pcHintText;
		
		level.hint_triggers = array_add(level.hint_triggers, self);
	}
}

/*
=============
///ScriptDocBegin
"Name: display_hint_timeout( <hint> , <timeout> , [parm1] , [parm2], [parm3], [y_offset] )"
"Summary: Displays a hint created with add_hint_string."
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <hint> : The hint reference created with add_hint_string."
"MandatoryArg: <timeout>: "
"OptionalArg: <parm1>: "
"OptionalArg: <parm2>: "
"OptionalArg: <parm3>: "
"OptionalArg: [y_offset]: move the hint lower or higher on the screen"
"Example: display_hint_timeout( "huzzah",3 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

display_hint_timeout( hint, timeout, parm1, parm2, parm3, y_offset )
{
	player = get_player_from_self();


	AssertEx( IsDefined( level.trigger_hint_func[ hint ] ), "Can't have a hint with a timeout if is has no break function, because hints without break functions display for a set period of time." );

	// hint triggers have an optional function they can boolean off of to determine if the hint will occur
	// such as not doing the NVG hint if the player is using NVGs already
	if ( player [[ level.trigger_hint_func[ hint ] ]]() )
		return;

	player thread HintPrint( level.trigger_hint_string[ hint ], level.trigger_hint_func[ hint ], parm1, parm2, parm3, timeout, undefined, y_offset );
}

display_hint_timeout_mintime( hint, timeout, mintime, parm1, parm2, parm3 )
{
	player = get_player_from_self();
	AssertEx( IsDefined( level.trigger_hint_func[ hint ] ), "Can't have a hint with a timeout if is has no break function, because hints without break functions display for a set period of time." );

	// hint triggers have an optional function they can boolean off of to determine if the hint will occur
	// such as not doing the NVG hint if the player is using NVGs already
	if ( player [[ level.trigger_hint_func[ hint ] ]]() )
		return;

	player thread HintPrint( level.trigger_hint_string[ hint ], level.trigger_hint_func[ hint ], parm1, parm2, parm3, timeout, mintime );
}

 /*
 =============
///ScriptDocBegin
"Name: display_hint_stick( <base_hint>, <pc_suffix>, <gamepad_suffix>, <gamepad_s_suffix>, <gamepad_ps3_suffix>, <gamepad_s_ps3_suffix> )"
"Summary: Displays a hint created with add_hint_string. This function updates the hint based on the player updating their stick configuration. It takes the base_hint and adds a suffix depending on the player's stick configuration."
"Module: Utility"
"MandatoryArg: <base_hint> : The base hint reference created with add_hint_string without the stick configuration suffixes."
"MandatoryArg: <pc_suffix> : The suffix added to base_hint if the PC controls are used."
"MandatoryArg: <gamepad_suffix> : The suffix added to base_hint if default gamepad controls are used."
"MandatoryArg: <gamepad_s_suffix> : The suffix added to base_hint if southpaw gamepad controls are used."
"MandatoryArg: <gamepad_ps3_suffix> : The suffix added to base_hint if default gamepad controls are used with ps3."
"MandatoryArg: <gamepad_s_ps3_suffix> : The suffix added to base_hint if southpaw gamepad controls are used with ps3."
"OptionalArg: <use_southpaw_for_legacy>: the legacy configuration will use the default hint, but if this is true it will show the southpaw hint"
"OptionalArg: <parm1>: "
"OptionalArg: <parm2>: "
"OptionalArg: <parm3>: "
"Example: display_hint_stick( "rappel_movement", "_pc", "_gamepad", "_gamepad_l", "_gamepad_no_glyph", "_gamepad_l_no_glyph", false )"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
display_hint_stick( base_hint, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy, parm1, parm2, parm3 )
{
	if ( !IsDefined( use_southpaw_for_legacy ) )
		use_southpaw_for_legacy = false;
	
	cur_hint_string = hint_stick_get_updated( base_hint, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy );
	thread display_hint( cur_hint_string, parm1, parm2, parm3 );
	thread hint_stick_update( base_hint, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy );
}

 /*
 =============
///ScriptDocBegin
"Name: display_hint_stick_timeout( <base_hint>, <timeout>, <pc_suffix>, <gamepad_suffix>, <gamepad_s_suffix>, <gamepad_ps3_suffix>, <gamepad_s_ps3_suffix> )"
"Summary: Displays a hint created with add_hint_string. This function updates the hint based on the player updating their stick configuration. It takes the base_hint and adds a suffix depending on the player's stick configuration."
"Module: Utility"
"MandatoryArg: <base_hint> : The base hint reference created with add_hint_string without the stick configuration suffixes."
"MandatoryArg: <timeout>: How long to display the hint."
"MandatoryArg: <pc_suffix> : The suffix added to base_hint if the PC controls are used."
"MandatoryArg: <gamepad_suffix> : The suffix added to base_hint if default gamepad controls are used."
"MandatoryArg: <gamepad_s_suffix> : The suffix added to base_hint if southpaw gamepad controls are used."
"MandatoryArg: <gamepad_ps3_suffix> : The suffix added to base_hint if default gamepad controls are used with ps3."
"MandatoryArg: <gamepad_s_ps3_suffix> : The suffix added to base_hint if southpaw gamepad controls are used with ps3."
"OptionalArg: <use_southpaw_for_legacy>: the legacy configuration will use the default hint, but if this is true it will show the southpaw hint"
"OptionalArg: <parm1>: "
"OptionalArg: <parm2>: "
"OptionalArg: <parm3>: "
"Example: display_hint_stick_timeout( "rappel_movement", 30, "_pc", "_gamepad", "_gamepad_l", "_gamepad_no_glyph", "_gamepad_l_no_glyph", false )"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
display_hint_stick_timeout( base_hint, timeout, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy, parm1, parm2, parm3 )
{
	if ( !IsDefined( use_southpaw_for_legacy ) )
		use_southpaw_for_legacy = false;

	cur_hint_string = hint_stick_get_updated( base_hint, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy );
	thread display_hint_timeout( cur_hint_string, timeout, parm1, parm2, parm3 );
	thread hint_stick_update( base_hint, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy );
}

 /*
 =============
///ScriptDocBegin
"Name: display_hint_stick_timeout_mintime( <base_hint>, <timeout>, <mintime>, <pc_suffix>, <gamepad_suffix>, <gamepad_s_suffix>, <gamepad_ps3_suffix>, <gamepad_s_ps3_suffix> )"
"Summary: Displays a hint created with add_hint_string. This function updates the hint based on the player updating their stick configuration. It takes the base_hint and adds a suffix depending on the player's stick configuration."
"Module: Utility"
"MandatoryArg: <base_hint> : The base hint reference created with add_hint_string without the stick configuration suffixes."
"MandatoryArg: <timeout>: How long to display the hint."
"MandatoryArg: <mintime>: Minimum time to display hint."
"MandatoryArg: <pc_suffix> : The suffix added to base_hint if the PC controls are used."
"MandatoryArg: <gamepad_suffix> : The suffix added to base_hint if default gamepad controls are used."
"MandatoryArg: <gamepad_s_suffix> : The suffix added to base_hint if southpaw gamepad controls are used."
"MandatoryArg: <gamepad_ps3_suffix> : The suffix added to base_hint if default gamepad controls are used with ps3."
"MandatoryArg: <gamepad_s_ps3_suffix> : The suffix added to base_hint if southpaw gamepad controls are used with ps3."
"OptionalArg: <use_southpaw_for_legacy>: the legacy configuration will use the default hint, but if this is true it will show the southpaw hint"
"OptionalArg: <parm1>: "
"OptionalArg: <parm2>: "
"OptionalArg: <parm3>: "
"Example: display_hint_stick_timeout_mintime( "rappel_movement", 30, 2, "_pc", "_gamepad", "_gamepad_l", "_gamepad_no_glyph", "_gamepad_l_no_glyph", false )"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
display_hint_stick_timeout_mintime( base_hint, timeout, mintime, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy, parm1, parm2, parm3 )
{
	if ( !IsDefined( use_southpaw_for_legacy ) )
		use_southpaw_for_legacy = false;
	
	cur_hint_string = hint_stick_get_updated( base_hint, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy );
	thread display_hint_timeout_mintime( cur_hint_string, timeout, mintime, parm1, parm2, parm3 );
	thread hint_stick_update( base_hint, pc_suffix, gamepad_suffix, gamepad_s_suffix, gamepad_ps3_suffix, gamepad_s_ps3_suffix, use_southpaw_for_legacy );
}

check_hint_condition( hint, parm1, parm2, parm3 )
{
	AssertEx( IsDefined( level.trigger_hint_func[ hint ] ), "Can't have a hint with a timeout if is has no break function, because hints without break functions display for a set period of time." );
	if ( IsDefined( parm3 ) )
		return [[ level.trigger_hint_func[ hint ]]]( parm1, parm2, parm3 );
	if ( IsDefined( parm2 ) )
		return [[ level.trigger_hint_func[ hint ]]]( parm1, parm2 );
	if ( IsDefined( parm1 ) )
		return [[ level.trigger_hint_func[ hint ]]]( parm1 );
	return [[ level.trigger_hint_func[ hint ]]]();
}

/*
=============
///ScriptDocBegin
"Name: getGenericAnim( <anime> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getGenericAnim( anime )
{
	AssertEx( IsDefined( level.scr_anim[ "generic" ][ anime ] ), "Generic anim " + anime + " was not defined in your _anim file." );
	return level.scr_anim[ "generic" ][ anime ];
}

/*
=============
///ScriptDocBegin
"Name: enable_careful()"
"Summary: Makes an AI not advance into his fixednode safe radius if an enemy enters it."
"Module: AI"
"Example: guy enable_careful()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_careful()
{
	AssertEx( IsAI( self ), "Tried to make an ai careful but it wasn't called on an AI" );
	self.script_careful = true;
}

/*
=============
///ScriptDocBegin
"Name: disable_careful()"
"Summary: Turns off careful mode for this AI."
"Module: AI"
"Example: guy disable_careful()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_careful()
{
	AssertEx( IsAI( self ), "Tried to unmake an ai careful but it wasn't called on an AI" );
	self.script_careful = false;
	self notify( "stop_being_careful" );
}

/*
=============
///ScriptDocBegin
"Name: enable_sprint()"
"Summary: Force an ai to sprint."
"Module: AI"
"Example: guy enable_sprint()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_sprint()
{
	AssertEx( IsAI( self ), "Tried to make an ai sprint but it wasn't called on an AI" );
	self.sprint = true;
}

/*
=============
///ScriptDocBegin
"Name: disable_sprint()"
"Summary: Disable forced sprinting."
"Module: AI"
"Example: guy disable_sprint()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_sprint()
{
	AssertEx( IsAI( self ), "Tried to unmake an ai sprint but it wasn't called on an AI" );
	self.sprint = undefined;
}

/*
=============
///ScriptDocBegin
"Name: disable_bulletwhizbyreaction()"
"Summary: disables the bulletwhizby reactions by ai"
"Module: AI"
"CallOn: An AI"
"Example: level.price disable_bulletwhizbyreaction();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_bulletwhizbyreaction()
{

/*
=============
///ScriptFieldDocBegin
"Name: .disableBulletWhizbyReaction"
"Summary: self explanatory"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

	self.disableBulletWhizbyReaction = true;
}

/*
=============
///ScriptDocBegin
"Name: enable_bulletwhizbyreaction()"
"Summary: renables the bulletwhizby reactions by ai"
"Module: AI"
"CallOn: An AI"
"Example: level.price enable_bulletwhizbyreaction();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_bulletwhizbyreaction()
{
	self.disableBulletWhizbyReaction = undefined;
}

clear_dvar( msg )
{
	SetDvar( msg, "" );
}

/*
=============
///ScriptDocBegin
"Name: set_fixednode_true( <set_fixednode_true> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_fixednode_true()
{
	self.fixednode = true;
}

/*
=============
///ScriptDocBegin
"Name: set_fixednode_false( <set_fixednode_false> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_fixednode_false()
{
	self.fixednode = false;
}


/*
=============
///ScriptDocBegin
"Name: spawn_ai( <bForceSpawn> )"
"Summary: Spawns the AI spawner and returns the alive AI"
"Module: Entity"
"CallOn: An entity"
"OptionalArg: <bForceSpawn>: Set to true to force spawn the AI"
"OptionalArg: <bMagicBulletShield>: Set to true to start magic_bullet_shield immediately"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_ai( bForceSpawn, bMagicBulletShield )
{
	if ( IsDefined( self.script_delay_spawn ) )
	{
		self endon( "death" );
		wait( self.script_delay_spawn );
	}
	spawnedGuy = undefined;

	dontShareEnemyInfo = ( IsDefined( self.script_stealth ) && flag( "_stealth_enabled" ) && !flag( "_stealth_spotted" ) );

	if ( ( IsDefined( self.script_forcespawn ) ) || ( IsDefined( bForceSpawn ) ) )
	{
		if ( !isdefined( self.script_drone ) )
			spawnedGuy = self StalingradSpawn( dontShareEnemyInfo );
		else
			spawnedGuy = dronespawn( self );
	}

	else
	{
		if ( !isdefined( self.script_drone ) )
			spawnedGuy = self DoSpawn( dontShareEnemyInfo );
		else
			spawnedGuy = dronespawn( self );
	}

	// This is to prevent a race condition where the guy could be killed while waiting inside spawn_failed(),
	// and thus die before this function returns and the caller has a chance to call magic_bullet_shield().
	if ( isdefined(bMagicBulletShield) && bMagicBulletShield && IsAlive( spawnedGuy ) )
		spawnedGuy magic_bullet_shield();
	
	if ( !isdefined( self.script_drone ) )
		spawn_failed( spawnedGuy );
	//this assert should still be here - but the functions calling this (pyramid spawner for example ) don't care if
	//the guy is actually spawned or not...seems to be more of an issue with pyramid spawner ( why is trying to spawn guys )
	//and not caring about the consiquences...tracked it down to this function being called twice on the same spawner in the same frame
	//in bog B, maybe further evaluation should be taken of pyramid spawner and why it's trying to do that - but for now - just commenting out
	//the assert and assuming we don't care why we can't spawn 2 guys in the same frame...  -MO
	//assert( IsDefined( spawnedGuy ) );
	
	if( IsDefined( self.script_spawn_once ) )
	    self Delete();
	
	if ( IsDefined( spawnedGuy ))
		spawnedGuy.spawner = self;

	if ( IsDefined( spawnedGuy ) && !IsDefined( spawnedGuy.targetname ) )
	{
		if ( IsDefined( self.targetname ) )
		{
			spawnedGuy.targetname = self.targetname + "_AI";
		}
	}

	return spawnedGuy;
}

 /*
 =============
///ScriptDocBegin
"Name: function_stack( <function>, <param1>, <param2>, <param3>, <param4>, <param5> )"
"Summary: function stack is used to thread off multiple functions one after another an insure that they get called in the order you sent them in (like a FIFO queue or stack). function_stack will wait for the function to finish before continuing the next line of code, but since it internally threads the function off, the function will not end if the parent function which called function_stack() ends.  function_stack is also local to the entity that called it, if you call it on nothing it will use level and all functions sent to the stack will wait on the previous one sent to level.  The same works for entities.  This way you can have 2 AI's that thread off multiple functions but those functions are in individual stacks for each ai"
"Module: Utility"
"CallOn: level or an entity."
"MandatoryArg: <function> : the function to send to the stack"
"OptionalArg: <param1> : An optional parameter for <function>."
"OptionalArg: <param2> : An optional parameter for <function>."
"OptionalArg: <param3> : An optional parameter for <function>."
"OptionalArg: <param4> : An optional parameter for <function>."
"OptionalArg: <param5> : An optional parameter for <function>."
"Example: level thread function_stack(::radio_dialogue, "scoutsniper_mcm_okgo" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
function_stack( func, param1, param2, param3, param4, param5 )
{
	//self might die - but if we endon( "death" ) this function will return without a bool, which is bad.
	//self endon( "death" );

	localentity = SpawnStruct();
	localentity thread function_stack_proc( self, func, param1, param2, param3, param4, param5 );

	return self function_stack_wait_finish( localentity );
}

/*
=============
///ScriptDocBegin
"Name: function_stack_timeout( <timeout> , <func> , <param1> , <param2> , <param3> , <param4> )"
"Summary: function stack is used to thread off multiple functions one after another an insure that they get called in the order you sent them in (like a FIFO queue or stack). function_stack will wait for the function to finish before continuing the next line of code, but since it internally threads the function off, the function will not end if the parent function which called function_stack() ends.  function_stack is also local to the entity that called it, if you call it on nothing it will use level and all functions sent to the stack will wait on the previous one sent to level.  The same works for entities.  This way you can have 2 AI's that thread off multiple functions but those functions are in individual stacks for each ai"
"Module: Utility"
"CallOn: level or an entity."
"MandatoryArg: <timeout> : number of seconds before this function times out of the stack"
"MandatoryArg: <function> : the function to send to the stack"
"OptionalArg: <param1> : An optional parameter for <function>."
"OptionalArg: <param2> : An optional parameter for <function>."
"OptionalArg: <param3> : An optional parameter for <function>."
"OptionalArg: <param4> : An optional parameter for <function>."
"OptionalArg: <param5> : An optional parameter for <function>."
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
function_stack_timeout( timeout, func, param1, param2, param3, param4, param5 )
{
	//self might die - but if we endon( "death" ) this function will return without a bool, which is bad.
	//self endon( "death" );

	localentity = SpawnStruct();
	localentity thread function_stack_proc( self, func, param1, param2, param3, param4, param5 );

	if ( IsDefined( localentity.function_stack_func_begun ) || ( localentity waittill_any_timeout( timeout, "function_stack_func_begun" ) != "timeout" ) )
	{
		return self function_stack_wait_finish( localentity );
	}
	else
	{
		localentity notify( "death" );
		return false;
	}
}

/*
=============
///ScriptDocBegin
"Name: function_stack_clear()"
"Summary: kills the stack of funcitons waiting to run"
"Module: Utility"
"CallOn: level or entity"
"Example: level function_stack_clear();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
function_stack_clear()
{
	newstack = [];
	if ( IsDefined( self.function_stack[ 0 ] ) && IsDefined( self.function_stack[ 0 ].function_stack_func_begun ) )
		newstack[ 0 ] = self.function_stack[ 0 ];

	//clear the stack and stop further processes
	self.function_stack = undefined;
	self notify( "clear_function_stack" );

	//the notify ends a function in the stack process waiting it's turn...it checks to
	//see if there is a function_stack on self to either run the function or just quit
	//in rare cases if you clear the stack and start piling stuff onto a new one, you'll
	//create the new stack before the process has a chance to check if the stack is undefined
	//and then all your functions run at once instead of being actually cleared...this
	//waittillframeend makes sure that doesn't happen
	waittillframeend;

	if ( !newstack.size )
		return;

	if ( !newstack[ 0 ].function_stack_func_begun )
		return;

	self.function_stack = newstack;
}


/*
=============
///ScriptDocBegin
"Name: geo_off( <geo_off> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
geo_off()
{
	if ( IsDefined( self.geo_off ) )
		return;

	self.realorigin = self GetOrigin();
	self MoveTo( self.realorigin + ( 0, 0, -10000 ), .2 );

	self.geo_off = true;
}

/*
=============
///ScriptDocBegin
"Name: geo_on( <geo_on> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
geo_on()
{
	if ( !isDefined( self.geo_off ) )
		return;

	self MoveTo( self.realorigin, .2 );
	self waittill( "movedone" );
	self.geo_off = undefined;
}

/*
=============
///ScriptDocBegin
"Name: disable_exits()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_exits()
{
	self.disableexits = true;
}

/*
=============
///ScriptDocBegin
"Name: enable_exits( <enable_exits> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_exits()
{
	self.disableexits = undefined;
}

/*
=============
///ScriptDocBegin
"Name: disable_turnAnims( <disable_turnAnims> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_turnAnims()
{
/*
=============
///ScriptFieldDocBegin
"Name: .noTurnAnims"
"Summary: don't do turning animations"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

	self.noTurnAnims = true;
}

/*
=============
///ScriptDocBegin
"Name: enable_turnAnims()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_turnAnims()
{
	self.noTurnAnims = undefined;
}

/*
=============
///ScriptDocBegin
"Name: disable_arrivals()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_arrivals()
{
	self.disablearrivals = true;
}

/*
=============
///ScriptDocBegin
"Name: enable_arrivals()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_arrivals()
{
	self endon( "death" );
	// wait until after anim_reach has turned arrivals off
	waittillframeend;
	self.disablearrivals = undefined;
}

 /*
 =============
///ScriptDocBegin
"Name: set_blur( <magnitude>, <transition time> )"
"Summary: calls script command SetBlur( <magnitude>, <transition time> )"
"Module: Utility"
"CallOn: "
"MandatoryArg: <magnitude> : amount of blur to transition to"
"MandatoryArg: <transition time> : time in seconds to transition to desired blur amount"
"Example: set_blur( 8, 3.1 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_blur( magnitude, time )
{
	SetBlur( magnitude, time );
}

 /*
 =============
///ScriptDocBegin
"Name: set_goal_radius( <radius> )"
"Summary: sets .goalradius parameter on ai to <radius>"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <radius> : radius to set ai's .goalradius to"
"Example: guy set_goal_radius( 128 );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_goal_radius( radius )
{
	self.goalradius = radius;
}

 /*
 =============
///ScriptDocBegin
"Name: set_goal_node( <node> )"
"Summary: calls script command SetGoalNode( <node> ), but also sets self.last_set_goalnode to <node>"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <node> : node to send the ai to"
"Example: guy set_goal_node( node );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_goal_node( node )
{
/*
=============
///ScriptFieldDocBegin
"Name: .last_set_goalnode"
"Summary: last_set_goalnode -> set in script and only defined when using set_goal_node instead of setgoalnode"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
	self.last_set_goalnode 	 = node;
	self.last_set_goalpos 	 = undefined;
	self.last_set_goalent 	 = undefined;

	self SetGoalNode( node );
}

 /*
 =============
///ScriptDocBegin
"Name: set_goal_node_targetname( <targetname> )"
"Summary: gets the node with the spedified targetname then calls script command SetGoalNode( <node> ), but also sets self.last_set_goalnode to <node>"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <targetname> : node targetname to send the ai to"
"Example: guy set_goal_node( "first_node" );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_goal_node_targetname( targetname )
{
	Assert( IsDefined( targetname ) );
	node = GetNode( targetname, "targetname" );
	Assert( IsDefined( node ) );

	self set_goal_node( node );
}

 /*
 =============
///ScriptDocBegin
"Name: set_goal_pos( <origin> )"
"Summary: calls script command SetGoalPos( <origin> ), but also sets self.last_set_goalpos to <origin>"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <origin> : origin to send the ai to"
"Example: guy set_goal_pos( vector );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_goal_pos( origin )
{
	self.last_set_goalnode 	 = undefined;

/*
=============
///ScriptFieldDocBegin
"Name: .last_set_goalpos"
"Summary: last_set_goalpos -> set in script and only defined when using set_goal_pos instead of setgoalpos"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
	self.last_set_goalpos 	 = origin;
	self.last_set_goalent 	 = undefined;

	self SetGoalPos( origin );
}

/*
=============
///ScriptDocBegin
"Name: set_goal_ent( <entity> )"
"Summary: calls script command SetGoalPos( <entity>.origin ), but also sets self.last_set_goalent to <origin>"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <entity> : entity with .origin variable to send the ai to"
"Example: guy set_goal_ent( script_origin );"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
set_goal_ent( target )
{
	set_goal_pos( target.origin );
/*
=============
///ScriptFieldDocBegin
"Name: .last_set_goalent"
"Summary: last_set_goalent -> set in script and only defined when using set_goal_ent( entity ) instead of setgoalpos( entity.origin )"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
	self.last_set_goalent 	 = target;
}

/*
=============
///ScriptDocBegin
"Name: objective_complete( <obj> )"
"Summary: Sets an objective to DONE"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <obj>: The objective index"
"Example: objective_complete( 3 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
objective_complete( obj )
{
	//{NOT_IN_SHIP
	objective_recon( obj );
	//}NOT_IN_SHIP
	Objective_State( obj, "done" );
	level notify( "objective_complete" + obj );
}


/*
=============
///ScriptDocBegin
"Name: handsignal( <xanim> , <ender> , <waiter> )"
"Summary: Makes an AI do a handsignal"
"Module: Utility"
"CallOn: An ai"
"MandatoryArg: <xanim>: The string name of the animation. Options are 'moveout', 'moveup', 'stop', 'onme', 'enemy', 'go'."
"OptionalArg: <wait anim done>: if true, wait till animation is over. Defaults to false."
"OptionalArg: <ender>: An optional ender "
"OptionalArg: <waiter>: An optional string to wait for level notify on "
"Example: level.price handsignal( "go" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============

Add this to your _anim script:
	level.scr_anim[ "generic" ][ "signal_onme" ]				= %CQB_stand_wave_on_me;
	level.scr_anim[ "generic" ][ "signal_go" ]					= %CQB_stand_wave_go_v1;
	level.scr_anim[ "generic" ][ "signal_stop" ]				= %CQB_stand_signal_stop;


*/
handsignal( xanim, waitAnimDone, ender, waiter )
{
	returnImmediate = true;
	if ( IsDefined( waitAnimDone ) )
		returnImmediate = !waitAnimDone;

	if ( IsDefined( ender ) )
		level endon( ender );

	if ( IsDefined( waiter ) )
		level waittill( waiter );

	animName = "signal_" + xanim;

	if ( self.a.pose == "crouch" )
		animName = animName + "_crouch";
	else if ( self.script == "cover_right" || ( self.script == "cover_multi" && self.cover.state == "right" ) )
		animName = animName + "_coverR";
	else if ( self isCQBWalking() )
		animName = animName + "_cqb";

	if ( returnImmediate )
		self SetAnimRestart( getGenericAnim( animName ), 1, 0, 1.1 );
	else
		self maps\_anim::anim_generic( self, animName );
}


/*
=============
///ScriptDocBegin
"Name: array_spawn( <spawners>, <bForceSpawn> )"
"Summary: spawns every spawner in the array and then returns an array of the alive AI that were spawned"
"Module: Utility"
"CallOn: An array of spawners"
"MandatoryArg: <spawners>: The spawners"
"OptionalArg: <bForceSpawn>: use stalingradspawn to force spawn the AI"
"OptionalArg: <skipIncorrectNumberAssert>: skips the assert saying that it spawned the incorrect number of guys"
"Example: guys = array_spawn( hooligans );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_spawn( spawners, bForceSpawn, skipIncorrectNumberAssert )
{
	if ( !isdefined( skipIncorrectNumberAssert ) )
		skipIncorrectNumberAssert = 0;
	
	guys = [];
	foreach ( spawner in spawners )
	{
		spawner.count = 1;

		if ( GetSubStr( spawner.classname, 7, 10 ) == "veh" )
		{
			guy = spawner spawn_vehicle();

			/#
			if ( IsDefined( spawner.export ) )
				AssertEx( IsAlive( guy ), "Vehicle with export " + spawner.export + " failed to spawn." );
			else
				AssertEx( IsAlive( guy ), "Vehicle at " + spawner.origin + " failed to spawn." );
			#/
			if ( IsDefined( guy.target ) && !IsDefined( guy.script_moveoverride ) )
				guy thread maps\_vehicle::gopath();
			guys[ guys.size ] = guy;
		}
		else
		{
			guy = spawner spawn_ai( bForceSpawn );

			if ( !skipIncorrectNumberAssert )
				AssertEx( IsAlive( guy ), "Guy with export " + spawner.export + " failed to spawn." );
			guys[ guys.size ] = guy;
		}
	}
	
	if ( !skipIncorrectNumberAssert )
		AssertEx( guys.size == spawners.size, "Didnt spawn correct number of guys" );

	return guys;
}

/*
=============
///ScriptDocBegin
"Name: array_spawn_cg( <spawners>, <bForceSpawn> )"
"Summary: spawns every spawner in the array and then returns an array of the alive AI that were spawned"
"Module: Utility"
"CallOn: An array of spawners"
"MandatoryArg: <spawners>: The spawners"
"OptionalArg: <bForceSpawn>: use stalingradspawn to force spawn the AI"
"OptionalArg: <skipIncorrectNumberAssert>: skips the assert saying that it spawned the incorrect number of guys"
"Example: guys = array_spawn( hooligans );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_spawn_cg( spawners , bForceSpawn , skipIncorrectNumberAssert , spawnDelay)
{
	if( !isdefined( skipIncorrectNumberAssert ) )
	skipIncorrectNumberAssert = 0;
	
	guys = [];
	foreach( spawner in spawners )
	{
		spawner.count = 1;

		if ( GetSubStr( spawner.classname, 7, 10 ) == "veh" )
		{
			guy = spawner spawn_vehicle();

			/#
			if ( IsDefined( spawner.export ) )
				AssertEx( IsAlive( guy ), "Vehicle with export " + spawner.export + " failed to spawn." );
			else
				AssertEx( IsAlive( guy ), "Vehicle at " + spawner.origin + " failed to spawn." );
			#/
			if ( IsDefined( guy.target ) && !IsDefined( guy.script_moveoverride ) )
				guy thread maps\_vehicle::gopath();
			guys[ guys.size ] = guy;
		}
		else
		{		
			guy = spawner spawn_ai( true );
			guys  = array_add( guys  , guy );	
			if( IsDefined( spawnDelay ) )
			   wait spawnDelay;
			else
				waitframe();
		}
	}
	
	if ( !skipIncorrectNumberAssert )
		AssertEx( guys.size == spawners.size, "Didnt spawn correct number of guys" );
	
	return guys;
}

/*
=============
///ScriptDocBegin
"Name: array_spawn_targetname( <targetname> , <forcespawn> , <skipIncorrectNumberAssert> , <deleteDronePool> )"
"Summary: Spawn guys with this targetname"
"Module: Utility"
"MandatoryArg: <targetname>: The targetname "
"OptionalArg: <forcespawn>: force the spawn "
"OptionalArg: <skipIncorrectNumberAssert>: skips the assert saying that it spawned the incorrect number of guys"
"OptionalArg: <deleteDronePool>: Use to save variables when you don't intend to use this drone pool spawner."
"Example: array_spawn_targetname( "badguy_spawner" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

array_spawn_targetname( targetname, forcespawn, skipIncorrectNumberAssert, deleteDronePool )
{
	spawners = GetEntArray( targetname, "targetname" );
	if ( IsDefined( level.spawn_pool_enabled ) )
	{
		struct_spawners = getstructarray( targetname, "targetname" );
		if ( IsDefined( deleteDronePool ) && deleteDronePool )
			deletestructarray_ref( struct_spawners );
		pool_spawners = maps\_spawner::get_pool_spawners_from_structarray( struct_spawners );
		spawners = array_combine( spawners, pool_spawners );
	}
	AssertEx( spawners.size, "Tried to spawn spawners with targetname " + targetname + " but there are no spawners" );
	return array_spawn( spawners, forcespawn, skipIncorrectNumberAssert );
}

/*
=============
///ScriptDocBegin
"Name: array_spawn_targetname_cg( <targetname> , <forcespawn> , <skipIncorrectNumberAssert> , <deleteDronePool> )"
"Summary: Spawn guys with this targetname"
"Module: Utility"
"MandatoryArg: <targetname>: The targetname "
"OptionalArg: <forcespawn>: force the spawn "
"OptionalArg: <skipIncorrectNumberAssert>: (Bool) skips the assert saying that it spawned the incorrect number of guys"
"OptionalArg: <deleteDronePool>: Use to save variables when you don't intend to use this drone pool spawner."
"Example: array_spawn_targetname_cg( "badguy_spawner" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

array_spawn_targetname_cg( targetname, forcespawn, spawnDelay, skipIncorrectNumberAssert, deleteDronePool )
{
	spawners = GetEntArray( targetname, "targetname" );
	if ( IsDefined( level.spawn_pool_enabled ) )
	{
		struct_spawners = getstructarray( targetname, "targetname" );
		if ( IsDefined( deleteDronePool ) && deleteDronePool )
			deletestructarray_ref( struct_spawners );
		pool_spawners = maps\_spawner::get_pool_spawners_from_structarray( struct_spawners );
		spawners = array_combine( spawners, pool_spawners );
	}
	AssertEx( spawners.size, "Tried to spawn spawners with targetname " + targetname + " but there are no spawners" );
	return array_spawn_cg( spawners, forcespawn, skipIncorrectNumberAssert , spawnDelay);
}

/*
=============
///ScriptDocBegin
"Name: array_spawn_noteworthy( <noteworthy> , <forcespawn> , <skipIncorrectNumberAssert> , <deleteDronePool> )"
"Summary: Spawn guys with this noteworthy"
"Module: Utility"
"MandatoryArg: <noteworthy>: The noteworthy "
"OptionalArg: <forcespawn>: force the spawn "
"OptionalArg: <skipIncorrectNumberAssert>: skips the assert saying that it spawned the incorrect number of guys"
"OptionalArg: <deleteDronePool>: Use to save variables when you don't intend to use this drone pool spawner."
"Example: array_spawn_noteworthy( "badguy_spawner" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_spawn_noteworthy( noteworthy, forcespawn, skipIncorrectNumberAssert, deleteDronePool  )
{
	spawners = GetEntArray( noteworthy, "script_noteworthy" );
	if ( IsDefined( level.spawn_pool_enabled ) )
	{
		struct_spawners = getstructarray( noteworthy, "script_noteworthy" );
		if ( IsDefined( deleteDronePool ) && deleteDronePool )
			deletestructarray_ref( struct_spawners );
		pool_spawners = maps\_spawner::get_pool_spawners_from_structarray( struct_spawners );
		spawners = array_combine( spawners, pool_spawners );
	}
	AssertEx( spawners.size, "Tried to spawn spawners with targetname " + noteworthy + " but there are no spawners" );
	return array_spawn( spawners, forcespawn, skipIncorrectNumberAssert );
}


/*
=============
///ScriptDocBegin
"Name: spawn_script_noteworthy( <script_noteworthy> , <bForceSpawn> )"
"Summary: Spawns an AI spawner with a specific script_noteworthy"
"Module: Utility"
"MandatoryArg: <script_noteworthy>: The script_noteworthy of the spawner"
"OptionalArg: <bForceSpawn>: "
"Example: level.price = spawn_script_noteworthy( "friendly_price" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_script_noteworthy( script_noteworthy, bForceSpawn )
{
	spawner = GetEnt( script_noteworthy, "script_noteworthy" );
	AssertEx( IsDefined( spawner ), "Spawner with script_noteworthy " + script_noteworthy + " does not exist." );

	guy = spawner spawn_ai( bForceSpawn );
	return guy;
}

/*
=============
///ScriptDocBegin
"Name: spawn_targetname( <targetname>, <bForcespawn>: )"
"Summary: Spawns an AI spawner with a specific targetname"
"Module: Utility"
"MandatoryArg: <targetname>: The targetname of the spawner"
"OptionalArg: <bForcespawn>: make this spawn with stalingradspawn"
"Example: level.price = targetname( "targetname" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_targetname( targetname, bForceSpawn )
{
	spawner = GetEnt( targetname, "targetname" );
	AssertEx( IsDefined( spawner ), "Spawner with targetname " + targetname + " does not exist." );

	guy = spawner spawn_ai( bForceSpawn );
	return guy;
}

/*
=============
///ScriptDocBegin
"Name: add_dialogue_line( <name> , <msg>, <name_color> )"
"Summary: Prints temp dialogue on the screen in lieu of a sound alias."
"Module: Utility"
"MandatoryArg: <name>: The character."
"MandatoryArg: <msg>: The dialogue."
"OptionalArg: <name_color>: An optional string of a color to give the name (makes different names delivering dialogue easier to follow...colors red, green, yellow, blue, cyan, purple, white, black "
"Example: thread add_dialogue_line( "MacMillan", "Put me down over there on the slope by the mattress." );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_dialogue_line( name, msg, name_color )
{
	if ( GetDvarInt( "loc_warnings", 0 ) )
		return;// I'm not localizing your damn temp dialog lines - Nate.

	if ( !isdefined( level.dialogue_huds ) )
	{
		level.dialogue_huds = [];
	}

	for ( index = 0; ; index++ )
	{
		if ( !isdefined( level.dialogue_huds[ index ] ) )
			break;
	}
	color = "^3";

	if ( IsDefined( name_color ) )
	{
		switch( name_color )
		{
			case "r":
			case "red":
				color = "^1";
				break;
			case "g":
			case "green":
				color = "^2";
				break;
			case "y":
			case "yellow":
				color = "^3";
				break;
			case "b":
			case "blue":
				color = "^4";
				break;
			case "c":
			case "cyan":
				color = "^5";
				break;
			case "p":
			case "purple":
				color = "^6";
				break;
			case "w":
			case "white":
				color = "^7";
				break;
			case "bl":
			case "black":
				color = "^8";
				break;
		}
	}

	level.dialogue_huds[ index ] = true;

	hudelem = maps\_hud_util::createFontString( "default", 1.5 );
	hudelem.location = 0;
	hudelem.alignX = "left";
	hudelem.alignY = "top";
	hudelem.foreground = 1;
	hudelem.sort = 20;

	hudelem.alpha = 0;
	hudelem FadeOverTime( 0.5 );
	hudelem.alpha = 1;
	hudelem.x = 40;
	hudelem.y = 260 + index * 18;
	hudelem.label = " " + color + "< " + name + " > ^7" + msg;
	hudelem.color = ( 1, 1, 1 );

	wait( 2 );
	timer = 2 * 20;
	hudelem FadeOverTime( 6 );
	hudelem.alpha = 0;

	for ( i = 0; i < timer; i++ )
	{
		hudelem.color = ( 1, 1, 0 / ( timer - i ) );
		wait( 0.05 );
	}
	wait( 4 );

	hudelem Destroy();

	level.dialogue_huds[ index ] = undefined;
}

/*
=============
///ScriptDocBegin
"Name: destructible_disable_explosion()"
"Summary: Disables a destructibles ( ie destructible vehicle ) ability to explode. It will catch fire, take window damage etc but not explode."
"Module: Destructibles"
"CallOn: Destructible"
"Example: car thread destructible_disable_explosion();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
destructible_disable_explosion()
{
	self common_scripts\_destructible::disable_explosion();
}

/*
=============
///ScriptDocBegin
"Name: destructible_force_explosion()"
"Summary: Forces a destructible ( ie destructible vehicle ) to explode immediately."
"Module: Destructibles"
"CallOn: Destructible"
"Example: car thread destructible_force_explosion();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
destructible_force_explosion()
{
	self common_scripts\_destructible::force_explosion();
}

set_grenadeammo( count )
{
	self.grenadeammo = count;
}

/*
=============
///ScriptDocBegin
"Name: get_player_feet_from_view( <get_player_feet_from_view> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_player_feet_from_view()
{
	Assert( IsPlayer( self ) );

	tagorigin = self.origin;
	upvec = AnglesToUp( self GetPlayerAngles() );
	height = self GetPlayerViewHeight();

	player_eye = tagorigin + ( 0, 0, height );
	player_eye_fake = tagorigin + ( upvec*height );

	diff_vec = player_eye - player_eye_fake;

	fake_origin = tagorigin + diff_vec;
	return fake_origin;
}


/*
=============
///ScriptDocBegin
"Name: set_baseaccuracy( <val> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_baseaccuracy( val )
{
	self.baseaccuracy = val;
}

/*
=============
///ScriptDocBegin
"Name: set_console_status( <set_console_status> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_console_status()
{
	// WARNING!!!! This code should be kept in-sync with the native version Scr_InitLevelObject() in scr_vm.cpp!
	if ( !isdefined( level.Console ) )
		level.Console = GetDvar( "consoleGame" ) == "true";
	else
		AssertEx( level.Console == ( GetDvar( "consoleGame" ) == "true" ), "Level.console got set incorrectly." );

	if ( !isdefined( level.xenon ) )
		level.xenon = GetDvar( "xenonGame" ) == "true";
	else
		AssertEx( level.xenon == ( GetDvar( "xenonGame" ) == "true" ), "Level.xenon got set incorrectly." );

	if ( !isdefined( level.ps3 ) )
		level.ps3 = GetDvar( "ps3Game" ) == "true";
	else
		AssertEx( level.ps3 == ( GetDvar( "ps3Game" ) == "true" ), "Level.ps3 got set incorrectly." );

	if ( !isdefined( level.wiiu ) )
		level.wiiu = GetDvar( "wiiuGame" ) == "true";
	else
		AssertEx( level.wiiu == ( GetDvar( "wiiuGame" ) == "true" ), "Level.wiiu got set incorrectly." );

	if ( !isdefined( level.pccg ) )
		level.pccg = GetDvar( "pccgGame" ) == "true";
	else
		AssertEx( level.pccg == ( GetDvar( "pccgGame" ) == "true" ), "Level.pccg got set incorrectly." );

	if ( !isdefined( level.xb3 ) )
		level.xb3 = GetDvar( "xb3Game" ) == "true";
	else
		AssertEx( level.xb3 == ( GetDvar( "xb3Game" ) == "true" ), "Level.xb3 got set incorrectly." );

	if ( !isdefined( level.ps4 ) )
		level.ps4 = GetDvar( "ps4Game" ) == "true";
	else
		AssertEx( level.ps4 == ( GetDvar( "ps4Game" ) == "true" ), "Level.ps4 got set incorrectly." );

	if ( !isdefined( level.pc ) )
		level.pc = !level.console && !level.pccg;
	else
		AssertEx( level.pc == (!level.console && !level.pccg), "Level.pc got set incorrectly." );

	if ( !isdefined( level.currentgen ))
		level.currentgen = level.ps3 || level.pccg || level.xenon || level.wiiu;
	else
		AssertEx( level.currentgen == (level.ps3 || level.pccg || level.xenon || level.wiiu), "Level.currentgen got set incorrectly." );

	if ( !isdefined( level.nextgen ))
		level.nextgen = level.pc || level.ps4 || level.xb3;
	else
		AssertEx( level.nextgen == (level.pc || level.ps4 || level.xb3), "Level.nextgen got set incorrectly." );
}

/*
=============
///ScriptDocBegin
"Name: IsGen4Renderer( )"
"Summary: Used to determine if the game is a Gen4 renderer. Gen1 = ps1. These are games that can support more things than a Xbox360"
"Module: Utility"
"CallOn: An entity"
"Example: if ( IsGen4() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_gen4()
{
	AssertEx( isdefined( level.nextgen ), "is_gen4() called before set_console_status() has been run." );

	return level.nextgen;
}

/*
=============
///ScriptDocBegin
"Name: autosave_now( <suppress_print> )"
"Summary: "
"Module: Utility"
"CallOn: Level"
"OptionalArg: <suppress_print>: "
"Example: thread autosave_now();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
autosave_now( suppress_print )
{
	return maps\_autosave::_autosave_game_now( suppress_print );
}

/*
=============
///ScriptDocBegin
"Name: autosave_now_silent()"
"Summary: No checkpoint msg"
"Module: Utility"
"CallOn: Level"
"OptionalArg: <suppress_print>: "
"Example: thread autosave_now_silent();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
autosave_now_silent()
{
	return maps\_autosave::_autosave_game_now( true );
}

/*
=============
///ScriptDocBegin
"Name: set_generic_deathanim( <deathanim> )"
"Summary: Sets a deathanim for a generic guy"
"Module: Utility"
"CallOn: AI"
"MandatoryArg: <deathanim>: The death animation reference"
"Example: guy set_generic_deathanim( "explosive_death" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_generic_deathanim( deathanim )
{
	self.deathanim = getGenericAnim( deathanim );
}

/*
=============
///ScriptDocBegin
"Name: set_deathanim( <deathanim> )"
"Summary: Sets the deathanim on an AI"
"Module: Utility"
"CallOn: AI"
"MandatoryArg: <deathanim>: The death animation reference"
"Example: guy set_deathanim( "death" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_deathanim( deathanim )
{
	self.deathanim = getanim( deathanim );
}

/*
=============
///ScriptDocBegin
"Name: clear_deathanim( <clear_deathanim> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
clear_deathanim()
{
	self.deathanim = undefined;
}

/*
=============
///ScriptDocBegin
"Name: hunted_style_door_open( <soundalias> )"
"Summary: Animates the door/gate/whatever in the style of Hunted's cool price door opening."
"Module: Utility"
"CallOn: A door or gate calls it"
"OptionalArg: <Soundalias>: A soundalias to play "
"Example: door hunted_style_door_open( "door_wood_slow_creaky_open" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
hunted_style_door_open( soundalias )
{
	wait( 1.75 );

	if ( IsDefined( soundalias ) )
		self PlaySound( soundalias );
	else
		self PlaySound( "door_wood_slow_open" );

	self RotateTo( self.angles + ( 0, 70, 0 ), 2, .5, 0 );
	self ConnectPaths();
	self waittill( "rotatedone" );
	self RotateTo( self.angles + ( 0, 40, 0 ), 2, 0, 2 );
}

/*
=============
///ScriptDocBegin
"Name: palm_style_door_open( <soundalias> )"
"Summary: Animates the door/gate/whatever in the style of Hunted's cool price door opening but with the palm instead of door knob."
"Module: Utility"
"CallOn: A door or gate calls it"
"OptionalArg: <Soundalias>: A soundalias to play "
"Example: door palm_style_door_open( "door_wood_slow_creaky_open" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
palm_style_door_open( soundalias )
{
	wait( 1.35 );

	if ( IsDefined( soundalias ) )
		self PlaySound( soundalias );
	else
		self PlaySound( "door_wood_slow_open" );

	self RotateTo( self.angles + ( 0, 70, 0 ), 2, .5, 0 );
	self ConnectPaths();
	self waittill( "rotatedone" );
	self RotateTo( self.angles + ( 0, 40, 0 ), 2, 0, 2 );
}



/*
=============
///ScriptDocBegin
"Name: lerp_fov_overtime( <time> , <destfov> )"
"Summary: lerps from the current cg_fov value to the destfov value linearly over time.  Sleeps for the given time."
"Module: Player"
"CallOn: Level"
"MandatoryArg: <time>: time to lerp"
"OptionalArg: <destfov>: field of view to go to"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

lerp_fov_overtime( time, destfov )
{
	// this used to be implemented in script, and wasn't smooth due to the 20/60 thing.
	// Now using the engine call instead.
	foreach( player in level.players )
		player LerpFOV( destfov, time );
	wait time;
}

/*
=============
///ScriptDocBegin
"Name: lerp_fovscale_overtime( <time> , <destfovscale> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
lerp_fovscale_overtime( time, destfovscale )
{
	basefov = GetDvarFloat( "cg_fovscale" );
	incs = Int( time / .05 );

	incfov = ( destfovscale - basefov ) / incs ;
	currentfov = basefov;
	for ( i = 0; i < incs; i++ )
	{
		currentfov += incfov;
		SetSavedDvar( "cg_fovscale", currentfov );
		wait .05;
	}

	SetSavedDvar( "cg_fovscale", destfovscale );
}

/*
=============
///ScriptDocBegin
"Name: putGunAway()"
"Summary: Puts the AI's weapon away"
"Module: Utility"
"CallOn: An ai"
"Example: level.price putGunAaway();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

putGunAway()
{
	animscripts\shared::placeWeaponOn( self.weapon, "none" );
	self.weapon = "none";
}

/*
=============
///ScriptDocBegin
"Name: apply_fog()"
"Summary: Applies the "start" fog settings for this trigger"
"Module: Utility"
"CallOn: A trigger_fog"
"Example: trigger_fog apply_fog()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
apply_fog()
{
	maps\_art::set_fog_progress( 0 );
}

/*
=============
///ScriptDocBegin
"Name: apply_end_fog()"
"Summary: Applies the "end" fog settings for this trigger"
"Module: Utility"
"CallOn: A trigger_fog"
"Example: trigger_fog apply_end_fog()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
apply_end_fog()
{
	maps\_art::set_fog_progress( 1 );
}


/*
=============
///ScriptDocBegin
"Name: anim_stopanimscripted()"
"Summary: "
"Module: Anim"
"CallOn: An entity"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
anim_stopanimscripted()
{
	self StopAnimScripted();
	self notify( "stop_loop" );// default ender.
	self notify( "single anim", "end" );
	self notify( "looping anim", "end" );
}

/*
=============
///ScriptDocBegin
"Name: disable_pain()"
"Summary: Disables pain on the AI"
"Module: Utility"
"CallOn: An ai"
"Example: level.zakhaev disable_pain();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_pain()
{
	AssertEx( IsAI( self ), "Tried to disable pain on a non ai" );
	self.a.disablePain = true;
	self.allowPain = false;
}

/*
=============
///ScriptDocBegin
"Name: enable_pain()"
"Summary: Enables pain on the AI"
"Module: Utility"
"CallOn: An ai"
"Example: level.zakhaev enable_pain();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_pain()
{
	AssertEx( IsAI( self ), "Tried to enable pain on a non ai" );
	self.a.disablePain = false;
	self.allowPain = true;
}

/*
=============
///ScriptDocBegin
"Name: _delete()"
"Summary: Calls Delete()"
"Module: Code Wrappers"
"CallOn: An entity"
"Example: pointer = ::_delete"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_delete()
{
	self Delete();
}

/*
=============
///ScriptDocBegin
"Name: _kill( <_kill> )"
"Summary: Calls Kill()"
"Module: Code Wrappers"
"CallOn: An entity"
"Example: pointer = ::_kill"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_kill()
{
	self Kill();
}

/*
=============
///ScriptDocBegin
"Name: kill_wrapper( <kill_wrapper> )"
"Summary: Wrapper to safely handle killing entities. Does special checks to ensure stability when killing players in Special Ops. Returns true or false depending on whether it actually killed the player."
"Module: Entity"
"CallOn: An entity"
"Example: level.player kill_wrapper();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
kill_wrapper()
{
	// Only do special checking in special ops for now, and only on players.
	// Players are put into invulnerable states that are unpredictable in Special Ops which can result in
	// attempts to call kill() directly to assert. If the special op has already terminated just exit out. If the
	// player is downed force invulnerability off so the kill will be able to succeed.
	
	if ( isplayer( self ) )
	{
		if ( flag_exist( "special_op_terminated" ) && flag( "special_op_terminated" ) )
		{
			return false;
		}

		if ( is_player_down( self ) )
		{
			self disableinvulnerability();
		}
	}
	
	self EnableDeathShield( false );
	self kill();
	return true;
}

/*
=============
///ScriptDocBegin
"Name: _setentitytarget( <target> )"
"Summary: "
"Module: Code Wrappers"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_setentitytarget( target )
{
	self SetEntityTarget( target );
}

/*
=============
///ScriptDocBegin
"Name: _ClearEntityTarget()"
"Summary: "
"Module: Code Wrappers"
"CallOn: An entity"
"Example: pointer = ::_ClearEntityTarget"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_ClearEntityTarget()
{
	self ClearEntityTarget();
}

/*
=============
///ScriptDocBegin
"Name: _unlink()"
"Summary: "
"Module: Code Wrappers"
"CallOn: An entity"
"Example: pointer = ::_unlink"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_unlink()
{
	self Unlink();
}


/*
=============
///ScriptDocBegin
"Name: disable_oneshotfx_with_noteworthy( <noteworthy> )"
"Summary: Disables _global_fx that have the given noteworthy on them"
"Module: Utility"
"MandatoryArg: <noteworthy>: The script_noteworthy"
"Example: disable_oneshotfx_with_noteworthy( "blackout_spotlight_fx" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_oneshotfx_with_noteworthy( noteworthy )
{
	AssertEx( IsDefined( level._global_fx_ents[ noteworthy ] ), "No _global_fx ents have noteworthy " + noteworthy );
	keys = GetArrayKeys( level._global_fx_ents[ noteworthy ] );
	for ( i = 0; i < keys.size; i++ )
	{
		level._global_fx_ents[ noteworthy ][ keys[ i ] ].looper Delete();
		level._global_fx_ents[ noteworthy ][ keys[ i ] ] = undefined;
	}
}

/*
=============
///ScriptDocBegin
"Name: _setLightIntensity( <val> )"
"Summary: function pointer to code command SetLightIntensity()"
"Module: Code Wrappers"
"CallOn: An entity"
"MandatoryArg: <val>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

_setLightIntensity( val )
{
	self SetLightIntensity( val );
}


/*
=============
///ScriptDocBegin
"Name: _linkto( <targ> , <tag> , <org> , <angles> )"
"Summary: wrapper for linkto
"Module: Code Wrappers"
"CallOn: An entity"
"MandatoryArg: <targ>: "
"OptionalArg: <tag>: "
"OptionalArg: <org>: "
"OptionalArg: <angles>: "
"Example: array_thread( guys, ::_linkto, blackhawk, "tag_detach" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_linkto( targ, tag, org, angles )
{
	if ( IsDefined( angles ) )
	{
		self LinkTo( targ, tag, org, angles );
		return;
	}
	if ( IsDefined( org ) )
	{
		self LinkTo( targ, tag, org );
		return;
	}
	if ( IsDefined( tag ) )
	{
		self LinkTo( targ, tag );
		return;
	}
	self LinkTo( targ );
}

/*
=============
///ScriptDocBegin
"Name: array_wait( <array>, <msg>, <timeout> )"
"Summary: waits for every entry in the <array> to recieve the <msg> notify, die, or timeout"
"Module: Utility"
"MandatoryArg: <array>: the array of entities to wait on"
"MandatoryArg: <msg>: the msg each array entity will wait on"
"OptionalArg: <timeout>: timeout to kill the wait prematurely"
"Example: array_wait( guys, "at the hq" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_wait( array, msg, timeout )
{
	keys = GetArrayKeys( array );
	structs = [];
	for ( i = 0; i < keys.size; i++ )
	{
		key = keys[ i ];

	}

	for ( i = 0; i < keys.size; i++ )
	{
		key = keys[ i ];
		structs[ key ] = SpawnStruct();
		structs[ key ]._array_wait = true;

		structs[ key ] thread array_waitlogic1( array[ key ], msg, timeout );
	}

	for ( i = 0; i < keys.size; i++ )
	{
		key = keys[ i ];
		if ( IsDefined( array[ key ] ) && structs[ key ]._array_wait )
			structs[ key ] waittill( "_array_wait" );
	}
}

/*
=============
///ScriptDocBegin
"Name: die()"
"Summary: The entity does damage to itself of > health value"
"Module: Utility"
"CallOn: An entity"
"Example: enemy die();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
die()
{
	self Kill( ( 0, 0, 0 ) );
}

/*
=============
///ScriptDocBegin
"Name: getmodel( <model> )"
"Summary: Returns the level.scr_model[ model ]"
"Module: Utility"
"MandatoryArg: <model>: The string index into level.scr_model"
"Example: SetModel( getmodel( "zakhaevs arm" ) );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getmodel( str )
{
	AssertEx( IsDefined( level.scr_model[ str ] ), "Tried to getmodel on model " + str + " but level.scr_model[ " + str + " was not defined." );
	return level.scr_model[ str ];
}

/*
=============
///ScriptDocBegin
"Name: isADS()"
"Summary: Returns true if the player is more than 50% ads"
"Module: Utility"
"Example: player_is_ads = isADS();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
isADS()
{
	Assert( IsPlayer( self ) );
	return( self PlayerAds() > 0.5 );
}


/*
=============
///ScriptDocBegin
"Name: waittill_player_lookat( <dot>, <timer>, <dot_only>, <timeout>  )"
"Summary: Waits until the player is looking at this entity."
"Module: Utility"
"CallOn: An AI or entity"
"OptionalArg: <dot>: Overwrite the default dot of .92 "
"OptionalArg: <timer>: Optional parameter to control how long you have to look before it triggers"
"OptionalArg: <dot_only>: Set to false if the player must trace to the entity to pass"
"OptionalArg: <timeout>: Time to pass before this function will timeout and give up"
"OptionalArg: <player>: When specified, is the player you want to detect is doing the looking."
"Example: level.price waittill_player_lookat();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_player_lookat( dot, timer, dot_only, timeout, ignore_ent, player )
{
	if ( !isdefined( player ) )
		player = level.player;

	timeoutEnt = SpawnStruct();
	if ( IsDefined( timeout ) )
		timeoutEnt thread notify_delay( "timeout", timeout );
	timeoutEnt endon( "timeout" );

	if ( !isdefined( dot ) )
		dot = 0.92;

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

	base_time = Int( timer * 20 );
	count = base_time;
	self endon( "death" );
	ai_guy = IsAI( self );
	org = undefined;
	for ( ;; )
	{
		if ( ai_guy )
			org = self GetEye();
		else
			org = self.origin;

		if ( player player_looking_at( org, dot, dot_only, ignore_ent ) )
		{
			count--;
			if ( count <= 0 )
				return true;
		}
		else
		{
			count = base_time;
		}
		wait( 0.05 );
	}
}

/*
=============
///ScriptDocBegin
"Name: waittill_player_lookat_for_time( <timer> , <dot> )"
"Summary: Wait until the player is looking at this entity for x time"
"Module: Utility"
"CallOn: An AI"
"MandatoryArg: <timer>: How long the player must look before the timer passes "
"OptionalArg: <dot>: Optional override dot"
"OptionalArg: <dot_only>: If true, it will only check FOV and not tracepassed"
"Example: self waittill_player_lookat_for_time( 0.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_player_lookat_for_time( timer, dot, dot_only, ignore_ent )
{
	AssertEx( IsDefined( timer ), "Tried to do waittill_player_lookat_for_time with no time parm." );
	waittill_player_lookat( dot, timer, dot_only, undefined, ignore_ent );
}

/*
=============
///ScriptDocBegin
"Name: player_looking_at( <org>, <dot> )"
"Summary: Checks to see if the player can dot and trace to a point"
"Module: Utility"
"MandatoryArg: <org>: The position you're checking if the player is looking at"
"OptionalArg: <dot>: Optional override dot"
"OptionalArg: <dot_only>: If true, it will only check FOV and not tracepassed"
"Example: if ( player_looking_at( org.origin ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_looking_at( start, dot, dot_only, ignore_ent )
{
	if ( !isdefined( dot ) )
		dot = 0.8;
	player = get_player_from_self();

	end = player GetEye();

	angles = VectorToAngles( start - end );
	forward = AnglesToForward( angles );
	player_angles = player GetPlayerAngles();
	player_forward = AnglesToForward( player_angles );

	new_dot = VectorDot( forward, player_forward );
	if ( new_dot < dot )
	{
		return false;
	}

	if ( IsDefined( dot_only ) )
	{
		AssertEx( dot_only, "dot_only must be true or undefined" );
		return true;
	}

	trace = BulletTrace( start, end, false, ignore_ent );
	return trace[ "fraction" ] == 1;
}

/*
=============
///ScriptDocBegin
"Name: either_player_looking_at( <org>, <dot>, <dot_only> )"
"Summary: returns true when either of the two players can trace and/or dot to a point"
"Module: Utility"
"MandatoryArg: <org>: The position you're checking if a player is looking at"
"OptionalArg: <dot>: Optional override dot"
"OptionalArg: <dot_only>: If true, it will only check FOV and not tracepassed"
"Example: if ( either_player_looking_at( org.origin ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
either_player_looking_at( org, dot, dot_only, ignore_ent )
{
	for ( i = 0; i < level.players.size; i++ )
	{
		if ( level.players[ i ] player_looking_at( org, dot, dot_only, ignore_ent ) )
			return true;
	}
	return false;
}


/*
=============
///ScriptDocBegin
"Name: player_can_see_ai( <ai> )"
"Summary: Rough method to test if an AI is in view of the player - only checks three points on the AI (low, mid, high)."
"Module: Utility"
"MandatoryArg: <ai>: The AI whom you're testing if the player can see."
"Example: if( player_can_see_ai( ambushGuy ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_can_see_ai( ai, latency )
{
	currentTime = getTime();

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

	if ( isdefined( ai.playerSeesMeTime ) && ai.playerSeesMeTime + latency >= currentTime )
	{
		assert( isdefined( ai.playerSeesMe ) );
		return ai.playerSeesMe;
	}

	ai.playerSeesMeTime = currentTime;

	if ( !within_fov( level.player.origin, level.player.angles, ai.origin, 0.766 ) )
	{
		ai.playerSeesMe = false;
		return false;
	}

	playerEye = level.player GetEye();

	feetOrigin = ai.origin;
	if ( SightTracePassed( playerEye, feetOrigin, true, level.player, ai ) )
	{
		ai.playerSeesMe = true;
		return true;
	}

	eyeOrigin = ai GetEye();
	if ( SightTracePassed( playerEye, eyeOrigin, true, level.player, ai ) )
	{
		ai.playerSeesMe = true;
		return true;
	}

	midOrigin = ( eyeOrigin + feetOrigin ) * 0.5;
	if ( SightTracePassed( playerEye, midOrigin, true, level.player, ai ) )
	{
		ai.playerSeesMe = true;
		return true;
	}

	ai.playerSeesMe = false;
	return false;
}


/*
=============
///ScriptDocBegin
"Name: players_within_distance( <fDist>, <org> )"
"Summary: Determines if any players are within a distance of a certain point. Returns false if any players are within the distance"
"Module: Utility"
"MandatoryArg: <fDist>: The distance away from the <org>"
"MandatoryArg: <org>: The location in space we are checking distance from"
"Example: if ( players_within_distance( 1024, enemy.origin ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
players_within_distance( fDist, org )
{
	fDistSquared = fDist * fDist;
	for ( i = 0; i < level.players.size; i++ )
	{
		if ( DistanceSquared( org, level.players[ i ].origin ) < fDistSquared )
			return true;
	}
	return false;
}
/*
=============
///ScriptDocBegin
"Name: AI_delete_when_out_of_sight( <aAI_to_delete>, <fDist> )"
"Summary: Deletes all of the AI in the array passed as players reach a certain distance away (and are not looking at them)"
"Module: Utility"
"MandatoryArg: <aAI_to_delete>: The array of AI to delete"
"MandatoryArg: <fDist>: The distance away from the players the AI will be deleted"
"Example: thread AI_delete_when_out_of_sight( aEnemies, 2048 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
AI_delete_when_out_of_sight( aAI_to_delete, fDist )
{
	if ( !isdefined( aAI_to_delete ) )
		return;

	off_screen_dot = 0.75;
	if ( IsSplitScreen() )
	{
		off_screen_dot = 0.65;
	}

	while ( aAI_to_delete.size > 0 )
	{
		wait( 1 );

		for ( i = 0; i < aAI_to_delete.size; i++ )
		{
			/*-----------------------
			KEEP LOOPING AND REMOVE ENEMY IF DEAD/DELETED
			-------------------------*/
			if ( ( !isdefined( aAI_to_delete[ i ] ) ) || ( !isalive( aAI_to_delete[ i ] ) ) )
			{
				aAI_to_delete = array_remove( aAI_to_delete, aAI_to_delete[ i ] );
				continue;
			}
			/*-----------------------
			KEEP LOOPING IF ANY PLAYERS TOO CLOSE OR IS FACING THE AI
			-------------------------*/
			if ( players_within_distance( fDist, aAI_to_delete[ i ].origin ) )
				continue;
			if ( either_player_looking_at( aAI_to_delete[ i ].origin + ( 0, 0, 48 ), off_screen_dot, true ) )
				continue;
			/*-----------------------
			ALL TESTS PASSED, DELETE THE BASTARD
			-------------------------*/
			if ( IsDefined( aAI_to_delete[ i ].magic_bullet_shield ) )
				aAI_to_delete[ i ] stop_magic_bullet_shield();
			aAI_to_delete[ i ] Delete();
			aAI_to_delete = array_remove( aAI_to_delete, aAI_to_delete[ i ] );
		}
	}
}
/*
=============
///ScriptDocBegin
"Name: add_wait( <func> , <parm1> , <parm2> , <parm3> )"
"Summary: Adds a function that you want to wait for completion on. Self of the function will be whatever add_wait is called on. Make sure you call add_wait before any wait, since the functions are stored globally."
"Module: Utility"
"MandatoryArg: <func>: The function."
"OptionalArg: <parm1>: Optional parameter"
"OptionalArg: <parm2>: Optional parameter"
"OptionalArg: <parm3>: Optional parameter"
"Example: add_wait( ::waittill_player_lookat );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_wait( func, parm1, parm2, parm3 )
{
	/#
	thread add_wait_asserter();
	#/

	ent = SpawnStruct();

	ent.caller = self;
	ent.func = func;
	ent.parms = [];
	if ( IsDefined( parm1 ) )
	{
		ent.parms[ ent.parms.size ] = parm1;
	}
	if ( IsDefined( parm2 ) )
	{
		ent.parms[ ent.parms.size ] = parm2;
	}
	if ( IsDefined( parm3 ) )
	{
		ent.parms[ ent.parms.size ] = parm3;
	}

	level.wait_any_func_array[ level.wait_any_func_array.size ] = ent;
}
/*
=============
///ScriptDocBegin
"Name: add_abort( <func> , <parm1> , <parm2> , <parm3> )"
"Summary: Adds a function that when completed aborts both the do_wait and any add_funcs added to the system."
"Module: Utility"
"MandatoryArg: <func>: The function."
"OptionalArg: <parm1>: Optional parameter"
"OptionalArg: <parm2>: Optional parameter"
"OptionalArg: <parm3>: Optional parameter"
"Example: add_abort( ::flag_wait, "shazam" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_abort( func, parm1, parm2, parm3 )
{
	/#
	thread add_wait_asserter();
	#/

	ent = SpawnStruct();

	ent.caller = self;
	ent.func = func;
	ent.parms = [];
	if ( IsDefined( parm1 ) )
	{
		ent.parms[ ent.parms.size ] = parm1;
	}
	if ( IsDefined( parm2 ) )
	{
		ent.parms[ ent.parms.size ] = parm2;
	}
	if ( IsDefined( parm3 ) )
	{
		ent.parms[ ent.parms.size ] = parm3;
	}

	level.abort_wait_any_func_array[ level.abort_wait_any_func_array.size ] = ent;
}

/*
=============
///ScriptDocBegin
"Name: add_func( <func> , <parm1> , <parm2> , <parm3> )"
"Summary: Adds a function that runs after an add_wait/do_wait completes."
"Module: Utility"
"MandatoryArg: <func>: The function."
"OptionalArg: <parm1>: Optional parameter"
"OptionalArg: <parm2>: Optional parameter"
"OptionalArg: <parm3>: Optional parameter"
"Example: add_func( ::waittill_player_lookat );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_func( func, parm1, parm2, parm3, parm4, parm5 )
{
	/#
	thread add_wait_asserter();
	#/

	ent = SpawnStruct();

	ent.caller = self;
	ent.func = func;
	ent.parms = [];
	if ( IsDefined( parm1 ) )
	{
		ent.parms[ ent.parms.size ] = parm1;
	}
	if ( IsDefined( parm2 ) )
	{
		ent.parms[ ent.parms.size ] = parm2;
	}
	if ( IsDefined( parm3 ) )
	{
		ent.parms[ ent.parms.size ] = parm3;
	}
	if ( IsDefined( parm4 ) )
	{
		ent.parms[ ent.parms.size ] = parm4;
	}
	if ( IsDefined( parm5 ) )
	{
		ent.parms[ ent.parms.size ] = parm5;
	}

	level.run_func_after_wait_array[ level.run_func_after_wait_array.size ] = ent;
}

/*
=============
///ScriptDocBegin
"Name: add_call( <func> , <parm1> , <parm2> , <parm3> )"
"Summary: Adds a code function call that runs after an add_wait/do_wait completes."
"Module: Utility"
"MandatoryArg: <func>: The function."
"OptionalArg: <parm1>: Optional parameter"
"OptionalArg: <parm2>: Optional parameter"
"OptionalArg: <parm3>: Optional parameter"
"Example: level.player add_call( ::playerlinktodelta, node );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_call( func, parm1, parm2, parm3, parm4, parm5 )
{
	/#
	thread add_wait_asserter();
	#/

	ent = SpawnStruct();

	ent.caller = self;
	ent.func = func;
	ent.parms = [];
	if ( IsDefined( parm1 ) )
	{
		ent.parms[ ent.parms.size ] = parm1;
	}
	if ( IsDefined( parm2 ) )
	{
		ent.parms[ ent.parms.size ] = parm2;
	}
	if ( IsDefined( parm3 ) )
	{
		ent.parms[ ent.parms.size ] = parm3;
	}
	if ( IsDefined( parm4 ) )
	{
		ent.parms[ ent.parms.size ] = parm4;
	}
	if ( IsDefined( parm5 ) )
	{
		ent.parms[ ent.parms.size ] = parm5;
	}

	level.run_call_after_wait_array[ level.run_call_after_wait_array.size ] = ent;
}

/*
=============
///ScriptDocBegin
"Name: add_noself_call( <func> , <parm1> , <parm2> , <parm3> )"
"Summary: Adds a code function call that runs after an add_wait/do_wait completes."
"Module: Utility"
"MandatoryArg: <func>: The function."
"OptionalArg: <parm1>: Optional parameter"
"OptionalArg: <parm2>: Optional parameter"
"OptionalArg: <parm3>: Optional parameter"
"Example: add_noself_call( ::earthquake, blah, blah, blah );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_noself_call( func, parm1, parm2, parm3, parm4, parm5 )
{
	/#
	thread add_wait_asserter();
	#/

	ent = SpawnStruct();

	ent.func = func;
	ent.parms = [];
	if ( IsDefined( parm1 ) )
	{
		ent.parms[ ent.parms.size ] = parm1;
	}
	if ( IsDefined( parm2 ) )
	{
		ent.parms[ ent.parms.size ] = parm2;
	}
	if ( IsDefined( parm3 ) )
	{
		ent.parms[ ent.parms.size ] = parm3;
	}
	if ( IsDefined( parm4 ) )
	{
		ent.parms[ ent.parms.size ] = parm4;
	}
	if ( IsDefined( parm5 ) )
	{
		ent.parms[ ent.parms.size ] = parm5;
	}

	level.run_noself_call_after_wait_array[ level.run_noself_call_after_wait_array.size ] = ent;
}

/*
=============
///ScriptDocBegin
"Name: add_endon( <endon> )"
"Summary: Adds an endon that will kill a do_wait. Threads can't acquire a parent's endons so this is a way to force an endon in cases where a do_wait could be killed, otherwise it'll cause a thread leak."
"Module: Utility"
"MandatoryArg: <endon>: The endon."
"Example: level.price add_endon( "shazam" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_endon( name )
{
	/#
	thread add_wait_asserter();
	#/

	ent = SpawnStruct();
	ent.caller = self;
	ent.ender = name;

	level.do_wait_endons_array[ level.do_wait_endons_array.size ] = ent;
}

/*
=============
///ScriptDocBegin
"Name: do_wait_any()"
"Summary: Waits until any of functions defined by add_wait complete. Clears the global variable where the functions were being stored."
"Module: Utility"
"Example: do_wait_any();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
do_wait_any()
{
	AssertEx( IsDefined( level.wait_any_func_array ), "Tried to do a do_wait without addings funcs first" );
	AssertEx( level.wait_any_func_array.size > 0, "Tried to do a do_wait without addings funcs first" );
	do_wait( level.wait_any_func_array.size - 1 );
}

/*
=============
///ScriptDocBegin
"Name: do_wait()"
"Summary: Waits until all of the functions defined by add_wait complete. Clears the global variable where the functions were being stored."
"Module: Utility"
"Example: do_wait();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
do_wait( count_to_reach )
{
	if ( !isdefined( count_to_reach ) )
		count_to_reach = 0;

	/#
	level notify( "kill_add_wait_asserter" );
	#/

	AssertEx( IsDefined( level.wait_any_func_array ), "Tried to do a do_wait without addings funcs first" );
	ent = SpawnStruct();
	array 		 = level.wait_any_func_array;
	endons 		 = level.do_wait_endons_array;
	after_array = level.run_func_after_wait_array;
	call_array	 = level.run_call_after_wait_array;
	nscall_array = level.run_noself_call_after_wait_array;
	abort_array = level.abort_wait_any_func_array;

 	level.wait_any_func_array 		 = [];
 	level.run_func_after_wait_array = [];
 	level.do_wait_endons_array 		 = [];
 	level.abort_wait_any_func_array = [];
 	level.run_call_after_wait_array = [];
 	level.run_noself_call_after_wait_array = [];

	ent.count = array.size;

	ent array_levelthread( array, ::waittill_func_ends, endons );
	ent thread do_abort( abort_array );

	ent endon( "any_funcs_aborted" );

	for ( ;; )
	{
		if ( ent.count <= count_to_reach )
			break;
		ent waittill( "func_ended" );
	}
	ent notify( "all_funcs_ended" );

	array_levelthread( after_array, ::exec_func, [] );
	array_levelthread( call_array, ::exec_call );
	array_levelthread( nscall_array, ::exec_call_noself );
}

/*
=============
///ScriptDocBegin
"Name: do_funcs()"
"Summary: Calls a series of add_funcs in order. When one finishes, the next starts. Etc."
"Module: Utility"
"Example: thread do_funcs();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
do_funcs()
{
	/#
	level notify( "kill_add_wait_asserter" );
	#/

	AssertEx( IsDefined( level.wait_any_func_array ), "Tried to do a do_wait without addings funcs first" );
	ent = SpawnStruct();

	AssertEx( !level.wait_any_func_array.size, "Don't use add_wait and do_funcs together." );
	AssertEx( !level.do_wait_endons_array.size, "Don't use add_endon and do_funcs together." );
	AssertEx( !level.run_call_after_wait_array.size, "Don't use add_call and do_funcs together." );
	AssertEx( !level.run_noself_call_after_wait_array.size, "Don't use add_call and do_funcs together." );
	AssertEx( !level.abort_wait_any_func_array.size, "Do_funcs doesn't support add_abort." );

	after_array = level.run_func_after_wait_array;

 	level.run_func_after_wait_array = [];

 	foreach ( func_struct in after_array )
 	{
 		level exec_func( func_struct, [] );
 	}

	ent notify( "all_funcs_ended" );
}

/*
=============
///ScriptDocBegin
"Name: is_default_start()"
"Summary: Returns true if you're playing from the default start"
"Module: Utility"
"Example: if ( is_default_start() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_default_start()
{
    if( isdefined( level.forced_start_catchup ) &&  level.forced_start_catchup == true )
        return false;
    
	if ( IsDefined( level.default_start_override ) && level.default_start_override == level.start_point )
		return true;

	if ( IsDefined( level.default_start ) )
		return level.start_point == "default";

	if ( level_has_start_points() )
		return level.start_point == level.start_functions[ 0 ][ "name" ];

	return level.start_point == "default";
}


/*
=============
///ScriptDocBegin
"Name: force_start_catchup()"
"Summary: used in test map sections of the full level. setting this makes is_default_start() return false. "
"Module: Entity"
"CallOn: An entity"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

force_start_catchup()
{
    level.forced_start_catchup = true;
}

/*
=============
///ScriptDocBegin
"Name: is_first_start()"
"Summary: Returns true if it is the first start point in the list of starts."
"Module: Utility"
"Example: if ( is_first_start() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_first_start()
{
	if ( !level_has_start_points() )
		return true;

	return level.start_point == level.start_functions[ 0 ][ "name" ];
}



/*
=============
///ScriptDocBegin
"Name: is_after_start( <name> )"
"Summary: returns true if the current start point is after name, useful for handling things that have already happened in a start point."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <name>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

is_after_start( name )
{
	hit_current_start = false;

	if( level.start_point == name )
		return false;

	for ( i = 0; i < level.start_functions.size; i++ )
	{
		if ( level.start_functions[ i ][ "name" ] == name )
		{
			hit_current_start = true;
			continue;
		}
		if( level.start_functions[ i ][ "name" ] == level.start_point )
			return hit_current_start;
	}
}

/*
=============
///ScriptDocBegin
"Name: _Earthquake( <scale> , <duration> , <source> , <radius> )"
"Summary: Wrapper for earthquake command"
"Module: Code Wrappers"
"CallOn: Level"
"MandatoryArg: <scale>: "
"MandatoryArg: <duration>: "
"MandatoryArg: <source>: "
"MandatoryArg: <radius>: "
"Example: delayThread( 0.1, ::_Earthquake, 0.4, 1.2, ent.origin, 6000 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_Earthquake( scale, duration, source, radius )
{
	Earthquake( scale, duration, source, radius );
}

/*
=============
///ScriptDocBegin
"Name: waterfx( <endflag> )"
"Summary: Makes AI have trails in water. Can be used on the player as well, so you're not a vampire."
"Module: Utility"
"CallOn: An AI or player"
"OptionalArg: <endflag>: A flag to end on "
"Example: level.price thread waterfx();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waterfx( endflag, soundalias )
{
// currently using these devraw fx:
//	level._effect[ "water_stop" ]						= LoadFX( "fx/misc/parabolic_water_stand" );
//	level._effect[ "water_movement" ]					= LoadFX( "fx/misc/parabolic_water_movement" );

	self endon( "death" );

	play_sound = false;
	if ( IsDefined( soundalias ) )
		play_sound = true;

	if ( IsDefined( endflag ) )
	{
		flag_assert( endflag );
		level endon( endflag );
	}
	for ( ;; )
	{
		wait( RandomFloatRange( 0.15, 0.3 ) );
		start = self.origin + ( 0, 0, 150 );
		end = self.origin - ( 0, 0, 150 );
		trace = BulletTrace( start, end, false, undefined );
		if ( !IsSubStr(trace[ "surfacetype" ], "water") )
			continue;

		fx = "water_movement";
		
		if ( IsPlayer( self ) )
		{
			if ( Distance( self GetVelocity(), ( 0, 0, 0 ) ) < 5 )
			{
				fx = "water_stop";
			}
		}
		else if ( IsDefined( level._effect[ "water_" + self.a.movement ] ) )
		{
			fx = "water_" + self.a.movement;
		}

		water_fx = getfx( fx );
		start = trace[ "position" ];
		//angles = vectortoangles( trace[ "normal" ] );
		angles = (0,self.angles[1],0);
		forward = anglestoforward( angles );
		up = anglestoup( angles );
		PlayFX( water_fx, start, up, forward );
		
		if ( fx != "water_stop" && play_sound )
			thread play_sound_in_space( soundalias, start );
	}
}

/*
=============
///ScriptDocBegin
"Name: playerSnowFootsteps( <endflag> )"
"Summary: Make the player have footstep fx in snow"
"Module: Utility"
"CallOn: player"
"OptionalArg: <endflag>: A flag to end on "
"Example: level.player thread playerSnowFootsteps();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
playerSnowFootsteps( endflag )
{
// currently using these devraw fx:
//	level._effect[ "footstep_snow_small" ]				= LoadFX( "fx/impacts/footstep_snow_small" );
//	level._effect[ "footstep_snow" ]					= LoadFX( "fx/impacts/footstep_snow" );

	if ( IsDefined( endflag ) )
	{
		flag_assert( endflag );
		level endon( endflag );
	}

	for ( ;; )
	{
		wait( RandomFloatRange( 0.25, .5 ) );
		start = self.origin + ( 0, 0, 0 );
		end = self.origin - ( 0, 0, 5 );
		trace = BulletTrace( start, end, false, undefined );
		forward = AnglesToForward( self.angles );
		mydistance = Distance( self GetVelocity(), ( 0, 0, 0 ) );
		if ( IsDefined( self.vehicle ) )
			continue;
		if ( trace[ "surfacetype" ] != "snow" )
			continue;
		if ( mydistance <= 10 )
			continue;
		fx = "snow_movement";
		//iprintlnbold( "Moved" + mydistance );
		if ( Distance( self GetVelocity(), ( 0, 0, 0 ) ) <= 154 )
		{
			PlayFX( getfx( "footstep_snow_small" ), trace[ "position" ], trace[ "normal" ], forward );
		}
		if ( Distance( self GetVelocity(), ( 0, 0, 0 ) ) > 154 )
		{
			PlayFX( getfx( "footstep_snow" ), trace[ "position" ], trace[ "normal" ], forward );
		}
	}
}


/*
=============
///ScriptDocBegin
"Name: mix_up( <sound> )"
"Summary: Used to blend sounds on a script model vehicle. See maps\sniperescape_code::seaknight_sound()"
"Module: Utility"
"CallOn: A sound blend entity"
"OptionalArg: <Sound>: The sound alias to blend, blends with the _off version of the alias. "
"Example: maps\sniperescape_code::seaknight_sound();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
mix_up( sound )
{
	timer = 3 * 20;
	for ( i = 0; i < timer; i++ )
	{
		self SetSoundBlend( sound, sound + "_off", ( timer - i ) / timer );
		wait( 0.05 );
	}
}

/*
=============
///ScriptDocBegin
"Name: mix_down( <sound> )"
"Summary: Used to blend sounds on a script model vehicle. See maps\sniperescape_code::seaknight_sound()"
"Module: Utility"
"CallOn: A sound blend entity"
"OptionalArg: <Sound>: The sound alias to blend, blends with the _off version of the alias. "
"Example: maps\sniperescape_code::seaknight_sound();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
mix_down( sound )
{
	timer = 3 * 20;
	for ( i = 0; i < timer; i++ )
	{
		self SetSoundBlend( sound, sound + "_off", i / timer );
		wait( 0.05 );
	}
}

/*
=============
///ScriptDocBegin
"Name: manual_linkto( <entity> , <offset> )"
"Summary: Sets an entity to the origin of another entity every server frame, for entity types that don't support linkto"
"Module: Utility"
"CallOn: An entity that doesn't support linkto, like soundblend entities."
"MandatoryArg: <entity>: The entity to link to "
"OptionalArg: <offset>: The offset to use "
"Example: flyblend thread manual_linkto( self, (0,0,0) );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
manual_linkto( entity, offset )
{
	entity endon( "death" );
	self endon( "death" );
	// for entities that don't support linkto, like soundblend entities
	if ( !isdefined( offset ) )
	{
		offset = ( 0, 0, 0 );
	}

	for ( ;; )
	{
		self.origin = entity.origin + offset;
		self.angles = entity.angles;
		wait( 0.05 );
	}
}

/*
=============
///ScriptDocBegin
"Name: nextmission()"
"Summary: Sends level to nextmission via _endmission script, one place for handling transitions"
"Module: Code Wrappers"
"CallOn: Level"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

nextmission()
{
	//{NOT_IN_SHIP
	mission_recon();
	//}NOT_IN_SHIP
	maps\_endmission::_nextmission();
}

/*
=============
///ScriptDocBegin
"Name: make_array( <index1> , <index2> , <index3> , <index4> , <index5> )"
"Summary: make an array with the stuff. (up to 5 things) "
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <index1>: "
"OptionalArg: <index2>: "
"OptionalArg: <index3>: "
"OptionalArg: <index4>: "
"OptionalArg: <index5>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
make_array( index1, index2, index3, index4, index5 )
{
	AssertEx( IsDefined( index1 ), "Need to define index 1 at least" );
	array = [];
	array[ array.size ] = index1;
	if ( IsDefined( index2 ) )
	{
		array[ array.size ] = index2;
	}

	if ( IsDefined( index3 ) )
	{
		array[ array.size ] = index3;
	}

	if ( IsDefined( index4 ) )
	{
		array[ array.size ] = index4;
	}

	if ( IsDefined( index5 ) )
	{
		array[ array.size ] = index5;
	}

	return array;
}


/*
=============
///ScriptDocBegin
"Name: fail_on_friendly_fire()"
"Summary: If this is run, the player will fail the mission if he kills a friendly"
"Module: Utility"
"Example: fail_on_friendly_fire();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
fail_on_friendly_fire()
{
	level.failOnFriendlyFire = true;
}

/*
=============
///ScriptDocBegin
"Name: normal_friendly_fire_penalty()"
"Summary: Returns friendly fire penalty to normal"
"Module: Utility"
"Example: normal_friendly_fire_penalty();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
normal_friendly_fire_penalty()
{
	level.failOnFriendlyFire = false;
}

/*
=============
///ScriptDocBegin
"Name: getPlayerClaymores()"
"Summary: Returns the number of claymores the player has"
"Module: Utility"
"Example: count = getPlayerClaymores();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getPlayerClaymores()
{
	Assert( IsPlayer( self ) );

	heldweapons = self GetWeaponsListAll();
	stored_ammo = [];
	for ( i = 0; i < heldweapons.size; i++ )
	{
		weapon = heldweapons[ i ];
		stored_ammo[ weapon ] = self GetWeaponAmmoClip( weapon );
	}


	claymoreCount = 0;
	if ( IsDefined( stored_ammo[ "claymore" ] ) && stored_ammo[ "claymore" ] > 0 )
	{
		claymoreCount = stored_ammo[ "claymore" ];
	}
	return claymoreCount;
}

/*
=============
///ScriptDocBegin
"Name: _wait( <timer> )"
"Summary: wrapper for wait command"
"Module: Code Wrappers"
"CallOn: An entity"
"MandatoryArg: <timer>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_wait( timer )
{
	wait( timer );
}

/*
=============
///ScriptDocBegin
"Name: _waittillmatch( <msg> , <match> )"
"Summary: "
"Module: Code Wrappers"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_waittillmatch( msg, match )
{
	self waittillmatch( msg, match );
}

/*
=============
///ScriptDocBegin
"Name: _setsaveddvar( <var> , <val> )"
"Summary: code wrapper for setsaveddvar"
"Module: Code Wrappers"
"CallOn: Level"
"MandatoryArg: <var>: dvar"
"OptionalArg: <val>: value to set"
"Example: delayThread( 9, ::_setsaveddvar, "g_friendlynamedist", level.friendlynamedist );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_setsaveddvar( var, val )
{
	SetSavedDvar( var, val );
}

/*
=============
///ScriptDocBegin
"Name: lerp_savedDvar( <name> , <value> , <time> )"
"Summary: lerps a saveddvar float setting over time"
"Module: dvar"
"CallOn: "
"MandatoryArg: <name>: the name of the dvar"
"MandatoryArg: <value>: the value to set it to"
"MandatoryArg: <time>: the time to blend to the new value"
"Example: thread lerp_savedDvar( "r_lightGridIntensity", .695, .75 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
lerp_savedDvar( name, value, time )
{
	curr = 	GetDvarFloat( name );

	level notify( name + "_lerp_savedDvar" );
	level endon( name + "_lerp_savedDvar" );

	range = value - curr;

	interval = .05;
	count = Int( time / interval );

	delta = range / count;

	while ( count )
	{
		curr += delta;
		SetSavedDvar( name, curr );

		wait interval;
		count--;
	}

	SetSavedDvar( name, value );
}

/*
=============
///ScriptDocBegin
"Name: lerp_savedDvar_cg_ng( <name> , <ng_value> , <cg_value> , <time> )"
"Summary: lerps a saveddvar float setting over time"
"Module: dvar"
"CallOn: "
"MandatoryArg: <name>: the name of the dvar"
"MandatoryArg: <cg_value>: the value to set it to if on a current-gen platform"
"MandatoryArg: <ng_value>: the value to set it to if on a next-gen platform"
"MandatoryArg: <time>: the time to blend to the new value"
"Example: thread lerp_savedDvar( "r_lightGridIntensity", .695, .75 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
lerp_savedDvar_cg_ng( name, cg_value, ng_value, time )
{
	if( is_gen4() )
	{
		lerp_savedDvar( name, ng_value, time );
	}
	else
	{
		lerp_savedDvar( name, cg_value, time );
	}
}

/*
=============
///ScriptDocBegin
"Name: giveachievement_wrapper( <achievement> )"
"Summary: Gives achievement to all players. Only does so if the player isn't cheating and hasn't cheated."
"Module: Code Wrappers"
"MandatoryArg: <achievement>: "
"Example: giveachievement_wrapper( "RESCUE_ROYCEWICZ" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
giveachievement_wrapper( achievement )
{
	if ( is_demo() )
	{
		return;
	}

	foreach ( player in level.players )
	{
		player GiveAchievement( achievement );
	}
	
	SetSPMatchData( "achievements_completed", achievement, true );
	/#
		println( "ACHIEVEMENT: " + achievement );
	#/
}

/*
=============
///ScriptDocBegin
"Name: player_giveachievement_wrapper( <achievement> )"
"Summary: Gives achievement to a specific player. Only does so if the player isn't cheating and hasn't cheated."
"Module: Code Wrappers"
"CallOn: Player"
"MandatoryArg: <achievement>: "
"Example: player giveachievement_wrapper( "RESCUE_ROYCEWICZ" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_giveachievement_wrapper( achievement )
{
	if ( is_demo() )
	{
		return;
	}

	self GiveAchievement( achievement );

	/#
		println( "ACHIEVEMENT: " + achievement );
	#/
}

/*
=============
///ScriptDocBegin
"Name: add_jav_glow( <optional_glow_delete_flag> )"
"Summary: Adds glow to the Javelin."
"Module: Utility"
"CallOn: A Javelin weapon"
"OptionalArg: <optional_glow_delete_flag>: Flag to disable the glow. "
"Example: jav thread add_jav_glow( "overpass_baddies_flee" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_jav_glow( optional_glow_delete_flag )
{
	jav_glow = Spawn( "script_model", ( 0, 0, 0 ) );
	jav_glow SetContents( 0 );
	jav_glow SetModel( "weapon_javelin_obj" );

	jav_glow.origin = self.origin;
	jav_glow.angles = self.angles;

	self add_wait( ::delete_on_not_defined );
	if ( IsDefined( optional_glow_delete_flag ) )
	{
		flag_assert( optional_glow_delete_flag );
		add_wait( ::flag_wait, optional_glow_delete_flag );
	}

	do_wait_any();

	jav_glow Delete();
}

/*
=============
///ScriptDocBegin
"Name: add_c4_glow( <optional_glow_delete_flag> )"
"Summary: Adds glow to the C4."
"Module: Utility"
"CallOn: A C4 weapon"
"OptionalArg: <optional_glow_delete_flag>: Flag to disable the glow. "
"Example: c4 thread add_c4_glow( "overpass_baddies_flee" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_c4_glow( optional_glow_delete_flag )
{
	c4_glow = Spawn( "script_model", ( 0, 0, 0 ) );
	c4_glow SetContents( 0 );
	c4_glow SetModel( "weapon_c4_obj" );

	c4_glow.origin = self.origin;
	c4_glow.angles = self.angles;

	self add_wait( ::delete_on_not_defined );
	if ( IsDefined( optional_glow_delete_flag ) )
	{
		flag_assert( optional_glow_delete_flag );
		add_wait( ::flag_wait, optional_glow_delete_flag );
	}

	do_wait_any();

	c4_glow Delete();
}

/*
=============
///ScriptDocBegin
"Name: delete_on_not_defined()"
"Summary: Weapons don't seem to notify death when they're picked up."
"Module: Utility"
"CallOn: An entity"
"Example: javelin delete_on_not_defined()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
delete_on_not_defined()
{
	for ( ;; )
	{
		if ( !isdefined( self ) )
			return;
		wait( 0.05 );
	}
}




/*
=============
///ScriptDocBegin
"Name: slowmo_start( <slowmo_start> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_start()
{
}

/*
=============
///ScriptDocBegin
"Name: slowmo_end( <slowmo_end> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_end()
{
}

/*
=============
///ScriptDocBegin
"Name: slowmo_setspeed_slow( <speed> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_setspeed_slow( speed )
{
	level.slowmo.speed_slow = speed;
}

/*
=============
///ScriptDocBegin
"Name: slowmo_setspeed_norm( <speed> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_setspeed_norm( speed )
{
	level.slowmo.speed_norm = speed;
}

/*
=============
///ScriptDocBegin
"Name: slowmo_setlerptime_in( <time> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_setlerptime_in( time )
{
	level.slowmo.lerp_time_in = time;
}

/*
=============
///ScriptDocBegin
"Name: slowmo_setlerptime_out( <time> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_setlerptime_out( time )
{
	level.slowmo.lerp_time_out = time;
}

/*
=============
///ScriptDocBegin
"Name: slowmo_lerp_in( <slowmo_lerp_in> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_lerp_in()
{
	if(IsDefined(level.no_slowmo) && level.no_slowmo)
		return;
	
	SetSlowMotion( level.slowmo.speed_norm, level.slowmo.speed_slow, level.slowmo.lerp_time_in );
	//setslowmotionview( level.slowmo.speed_norm, level.slowmo.speed_slow, level.slowmo.lerp_time_in ); //changed  for slomo breach
}

/*
=============
///ScriptDocBegin
"Name: slowmo_lerp_out( <slowmo_lerp_out> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
slowmo_lerp_out()
{	
	if(IsDefined(level.no_slowmo) && level.no_slowmo)
		return;
	
	setslowmotion( level.slowmo.speed_slow, level.slowmo.speed_norm, level.slowmo.lerp_time_out );
}

/*
=============
///ScriptDocBegin
"Name: add_earthquake( <name> , <mag> , <duration> , <radius> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_earthquake( name, mag, duration, radius )
{
	level.earthquake[ name ][ "magnitude" ] = mag;
	level.earthquake[ name ][ "duration" ] = duration;
	level.earthquake[ name ][ "radius" ] = radius;
}



/*
=============
///ScriptDocBegin
"Name: arcadeMode()"
"Summary: returns true if arcade mode is on"
"Module: Utility"
"CallOn: Level"
"Example: if(arcademode){ dothestuf(); }"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
arcadeMode()
{
	return GetDvar( "arcademode" ) == "1";
}

/*
=============
///ScriptDocBegin
"Name: arcadeMode_stop_timer()"
"Summary: Stops the countdown timer in arcademode, for missions that have non-competitive ending sequences."
"Module: ArcadeMode"
"Example: arcadeMode_stop_timer();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
arcadeMode_stop_timer()
{
 	if ( !isdefined( level.arcadeMode_hud_timer ) )
 		return;

 	level notify( "arcadeMode_remove_timer" );
 	level.arcademode_stoptime = GetTime();

	level.arcadeMode_hud_timer Destroy();
	level.arcadeMode_hud_timer = undefined;
}

/*
=============
///ScriptDocBegin
"Name: MusicPlayWrapper( <song> , <timescale> , <overrideCheat> )"
"Summary: wrapper for music play for arcade mode stuff."
"Module: Code Wrappers"
"CallOn: Level"
"MandatoryArg: <song>: song to play"
"OptionalArg: <timescale>: timescale param to pass to musicplay"
"OptionalArg: <overrideCheat>: overrideCheat param to pass to musicplay"
"Example: MusicPlayWrapper( "credits_bog_victory" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

MusicPlayWrapper( song, timescale, overrideCheat )
{
	/#
	if( GetDvarInt( "music_enable" ) == 0 )
	{
		return;
	}
	#/
	AssertEx( isdefined( level._audio ), "Cannot play music before _load::main " );
	level._audio.last_song = song;

	if ( !isdefined( timescale ) )
		timescale = true;
	if ( !isdefined( overrideCheat ) )
		overrideCheat = false;

	MusicStop( 0 );
	MusicPlay( song, 0, 1.0, true, overrideCheat );
	
}

/*
=============
///ScriptDocBegin
"Name: music_loop( <name> , <time>, <fade_time> )"
"Summary: stops any previously playing music, and plays a track of music that loops."
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: the name of the music track"
"MandatoryArg: <time>: The amount of time to wait between loops. If > 10, will be ignored."
"OptionalArg: <fade_time>: optional fade time IN SECONDS to fade out any previously playing music...if undefined, previously playing music will end immediately"
"Example: music_loop( 'ICBM_tension_music', 112 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
music_loop( name, time, fade_time, timescale, overrideCheat )
{
	/#
	if( GetDvarInt( "music_enable" ) == 0 )
	{
		return;
	}
	#/
	thread music_loop_internal( name, time, fade_time, timescale, overrideCheat );
}

/*
=============
///ScriptDocBegin
"Name: music_loop_stealth( <name> , <length>, <fade_time> )"
"Summary: stops any previously playing music, and plays a track of music that loops.  The music will automatically pause on stealth_spotted and resume on stealth_hidden."
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: the name of the music track"
"MandatoryArg: <length>: the length of the music track IN SECONDS (if short, the song will end and loop early )"
"OptionalArg: <fade_time>: optional fade time IN SECONDS to fade out any previously playing music...if undefined, previously playing music will end immediately"
"Example: music_loop_stealth( 'ICBM_tension_music', 112 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
music_loop_stealth( name, length, fade_time, timescale, overrideCheat )
{
	/#
	if( GetDvarInt( "music_enable" ) == 0 )
	{
		return;
	}
	#/
	thread music_loop_internal( name, length, fade_time, timescale, overrideCheat, true );
}

/*
=============
///ScriptDocBegin
"Name: music_play( <name> , <fade_time> )"
"Summary: stops any previously playing music, and plays a new piece of music."
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: the name of the music track"
"OptionalArg: <fade_time>: optional fade time IN SECONDS to fade out any previously playing music...if undefined, previously playing music will end immediately"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
music_play( name, fade_time, timescale, overrideCheat )
{
	if ( IsDefined( fade_time ) && fade_time > 0 )
	{
		thread music_play_internal_stop_with_fade_then_call( name, fade_time, timescale, overrideCheat );
		return;
	}	
	music_stop();

	MusicPlayWrapper( name, timescale, overrideCheat );
}

/*
=============
///ScriptDocBegin
"Name: music_crossfade( name, crossfade_time, timescale, overrideCheat  )"
"Summary: Crossfades already playing music into the specified music"
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <name>: the name of the music track"
"MandatoryArg: <crossfade_time>: The amount of time to crossfade into the new song"
"OptionalArg: "
"Example: music_crossfade( "my_awesome_tune", 10 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

music_crossfade( name, crossfade_time, timescale, overrideCheat )
{
	/#
	if( GetDvarInt( "music_enable" ) == 0 )
	{
		return;
	}
	#/
	AssertEx( isdefined( level._audio ), "Cannot play music before _load::main " );
	//AssertEx( isdefined( level._audio.last_song ), "No previous music detected. Either there is none, or previously played music was not played with music_play() or MusicPlayWrapper()" );	
	
	if ( !isdefined( timescale ) )
		timescale = true;
	if ( !isdefined( overrideCheat ) )
		overrideCheat = false;
		
	if (isdefined (level._audio.last_song))
	{
	MusicStop( crossfade_time, level._audio.last_song, overrideCheat );
	}
	else
	{
		//let us know
		iprintln("^3WARNING!  script music_crossfade(): No previous song was played - no previous song to crossfade from - not fading out anything");
	}	
	
	level._audio.last_song = name;
	
	musicplay( name, crossfade_time, timescale, false, overrideCheat );
	
	level endon( "stop_music" );	
	
	wait( crossfade_time );
	level notify( "done_crossfading" );	
}

/*
=============
///ScriptDocBegin
"Name: music_stop( <fade_time> )"
"Summary: stops any currently playing music, including loops"
"Module: Entity"
"CallOn: An entity"
"OptionalArg: <fade_time>: the time IN SECONDS to stop the currently playing music"
"Example: music_stop( 3 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
music_stop( fade_time )
{
	if ( !isdefined( fade_time ) || fade_time <= 0 )
		MusicStop();
	else
		MusicStop( fade_time );
	level notify( "stop_music" );
}

/*
=============
///ScriptDocBegin
"Name: player_is_near_live_grenade()"
"Summary: Returns true if player(s) is near a live grenade"
"Module: Utility"
"CallOn: Level"
"Example: if( player_is_near_live_grenade() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_is_near_live_grenade()
{
	grenades = GetEntArray( "grenade", "classname" );
	for ( i = 0; i < grenades.size; i++ )
	{
		grenade = grenades[ i ];
		if ( grenade.model == "weapon_claymore" )
			continue;

		for ( playerIndex = 0; playerIndex < level.players.size; playerIndex++ )
		{
			player = level.players[ playerIndex ];
			if ( DistanceSquared( grenade.origin, player.origin ) < ( 275 * 275 ) )// grenade radius is 256
			{
				/# maps\_autosave::AutoSavePrint( "autosave failed: live grenade too close to player" ); #/
				return true;
			}
		}
	}
	return false;
}

/*
=============
///ScriptDocBegin
"Name: player_died_recently()"
"Summary: check the player_died_recently dvar"
"Module: Utility"
"CallOn: Level"
"Example: if(player_died_recently())"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_died_recently()
{
	return GetDvarInt( "player_died_recently", "0" ) > 0;
}

 /*
 =============
///ScriptDocBegin
"Name: all_players_istouching( eVolume )"
"Summary: Returns true/false if all coop players are currently touching eVolume. Does IsTouching() code function on all coop players (or just single player if no coop enabled)."
"Module: Player"
"MandatoryArg: <eVolume>: The volume you are checking to see that the players are touching."
"Example: while ( coop_players_touching( eRoomVolume ) )....<do something>"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
all_players_istouching( eVolume )
{
	AssertEx( IsDefined( eVolume ), "eVolume parameter not defined" );
	foreach ( player in level.players )
	{
		if ( !player IsTouching( eVolume ) )
			return false;
	}
	return true;
}

 /*
 =============
///ScriptDocBegin
"Name: any_players_istouching( eVolume )"
"Summary: Returns true/false if any coop players are currently touching eVolume. Does IsTouching() code function on all coop players (or just single player if no coop enabled)."
"Module: Player"
"MandatoryArg: <eVolume>: The volume you are checking to see that the players are touching."
"Example: while ( coop_players_touching( eRoomVolume ) )....<do something>"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
any_players_istouching( eVolume )
{
	AssertEx( IsDefined( eVolume ), "eVolume parameter not defined" );
	foreach ( player in level.players )
	{
		if ( player IsTouching( eVolume ) )
			return true;
	}
	return false;
}
 
/*
=============
///ScriptDocBegin
"Name: getDifficulty( <getDifficulty> )"
"Summary: Returns level difficulty in "easy","medium", "hard", or "fu" format."
"Module: Utility"
"CallOn: Level"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getDifficulty()
{
	Assert( IsDefined( level.gameskill ) );
	if ( level.gameskill < 1 )
		return "easy";
	if ( level.gameskill < 2 )
		return "medium";
	if ( level.gameskill < 3 )
		return "hard";
	return "fu";
}

/*
 =============
///ScriptDocBegin
"Name: getAveragePlayerOrigin()"
"Summary: Returns the average player origin of level.players array."
"Module: Utility"
"CallOn: "
"Example: org = getAveragePlayerOrigin();"
"SPMP: singleplayer"
///ScriptDocEnd
 =============
 */
getAveragePlayerOrigin()
{
	averageOrigin_x = 0;
	averageOrigin_y = 0;
	averageOrigin_z = 0;
	foreach ( player in level.players )
	{
		averageOrigin_x += player.origin[ 0 ];
		averageOrigin_y += player.origin[ 1 ];
		averageOrigin_z += player.origin[ 2 ];
	}
	averageOrigin_x = averageOrigin_x / level.players.size;
	averageOrigin_y = averageOrigin_y / level.players.size;
	averageOrigin_z = averageOrigin_z / level.players.size;
	return( averageOrigin_x, averageOrigin_y, averageOrigin_z );
}

/*
=============
///ScriptDocBegin
"Name: get_average_origin( <array> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_average_origin( array )
{
	origin = ( 0, 0, 0 );
	foreach ( member in array )
		origin += member.origin;


	return origin *  ( 1.0 / array.size ) ;
}



generic_damage_think()
{
	self.damage_functions = [];
	self endon ( "entitydeleted" );
	self endon ( "stop_generic_damage_think" );
	for ( ;; )
	{
		self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName );
		foreach ( func in self.damage_functions )
		{
			thread [[ func ]]( damage, attacker, direction_vec, point, type, modelName, tagName );
		}
	}
}


/*
=============
///ScriptDocBegin
"Name: add_damage_function( <func> )"
"Summary: Adds a damage function to an AI or Vehicle. It is called when the AI or Vehicle takes damage. The AI function must have all 7 of the required parameters."
"Module: Spawner"
"CallOn: An AI"
"MandatoryArg: <func > The function to be run when the AI takes damage. "
"Example: ai add_damage_function( ::got_hurt );"
"SPMP: singleplayer"
"NoteLine: thread [[ func ]]( damage, attacker, direction_vec, point, type, modelName, tagName );"
///ScriptDocEnd
=============
*/
add_damage_function( func )
{
	self.damage_functions[ self.damage_functions.size ] = func;
}

/*
=============
///ScriptDocBegin
"Name: remove_damage_function( <func> )"
"Summary: Removes a damage function that was added with add_damage_function."
"Module: Spawner"
"CallOn: An AI"
"MandatoryArg: <func > The function to be removed. "
"Example: ai remove_damage_function( ::got_hurt );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_damage_function( damage_func )
{
	new_array = [];
	foreach ( func in self.damage_functions )
	{
		if ( func == damage_func )
			continue;
		new_array[ new_array.size ] = func;
	}
	self.damage_functions = new_array;
}


/*
=============
///ScriptDocBegin
"Name: giveXp( <type>, <value> )"
"Summary: Gives a player XP either by predefined <type> or customized <value>, single player XP gain feature must be enabled to use this."
"Module: Spawner"
"CallOn: A Player"
"MandatoryArg: <type> String name of type of XP given. Does not need to be an existing type, can be any label for the XP reward."
"OptionalArg: <value> Int value of XP given. If not defined, type's XP reward will be awarded. If type does not exist, default 'kill' type XP will be awarded."
"Example: level.player giveXp( "assist" ); level.player2 giveXp( "pwn", 10000 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
giveXp( type, value )
{
	AssertEx( IsDefined( level.xp_enable ) && level.xp_enable, "giveXp() called when xp is not enabled." );
	AssertEx( IsDefined( level.xp_give_func ), "giveXp() called when level.xp_give_func not defined." );
	
	if ( IsDefined( level.xp_enable ) && level.xp_enable && IsDefined( level.xp_give_func ) )
	{
		self [[ level.xp_give_func ]]( type, value );
	}
}


/*
=============
///ScriptDocBegin
"Name: playLocalSoundWrapper( <soundalias> )"
"Summary: Calls playLocalSound on self. This function was made so it can be used in array_thread"
"Module: Player"
"CallOn: A Player"
"MandatoryArg: <soundalias> Soundalias to played."
"Example: level.player playLocalSoundWrapper( "mission_complete_sound" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
playLocalSoundWrapper( alias )
{
	Assert( IsDefined( alias ) );
	self PlayLocalSound( alias );
}


/*
=============
///ScriptDocBegin
"Name: enablePlayerWeapons( <bool> )"
"Summary: enables or disables weapons on single all players (singleplayer or coop)"
"Module: Player"
"MandatoryArg: <bool> True or false to enable or disable weapons"
"Example: enablePlayerWeapons( false );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enablePlayerWeapons( bool )
{
	AssertEx( IsDefined( bool ), "Need to pass either 'true' or 'false' to enable/disable weapons" );
	if ( level.players.size < 1 )
		return;
	foreach ( player in level.players )
	{
		if ( bool == true )
			player EnableWeapons();
		else
			player DisableWeapons();
	}
}

/*
=============
///ScriptDocBegin
"Name: teleport_players( aNodes )"
"Summary: Teleports coop players to an array of nodes"
"Module: Player"
"MandatoryArg: <aNodes> An array of exactly 2 nodes (to account for the possibility of a second player)"
"Example: teleport_players( aPlayerNodes, true );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
teleport_players( aNodes )
{
	//->not supporting coop in SP anymore
	//assertEx( aNodes.size == 2, "An array of exactly 2 nodes is required to teleport 2 coop players" );

	player1node = undefined;
	player2node = undefined;
	eNode = undefined;
	foreach ( node in aNodes )
	{
		if ( ( IsDefined( node.script_noteworthy ) ) && ( node.script_noteworthy == "player1" ) )
			player1node = node;
		else if ( ( IsDefined( node.script_noteworthy ) ) && ( node.script_noteworthy == "player2" ) )
			player2node = node;
		else
		{
			if ( !isdefined( player1node ) )
				player1node = node;
			if ( !isdefined( player2node ) )
				player2node = node;
		}
	}
	foreach ( player in level.players )
	{
		if ( player == level.player )
			eNode = player1node;
		else if ( player == level.player2 )
			eNode = player2node;
		player SetOrigin( eNode.origin );
		player SetPlayerAngles( eNode.angles );
	}
}

/*
=============
///ScriptDocBegin
"Name: teleport_player( <object> )"
"Summary: move level.player to the origin and angles of the specified object"
"Module: Player"
"CallOn: A Player"
"MandatoryArg: <param1>: "
"Example: 	teleport_player( getstruct("start_surprized_parachute_moment","script_noteworthy" ) );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

teleport_player( object )
{
	level.player SetOrigin( object.origin );
	if ( IsDefined( object.angles ) )
    	level.player SetPlayerAngles( object.angles );
}


/*
=============
///ScriptDocBegin
"Name: translate_local()"
"Summary: Translates an entity in space"
"Module: Utility"
"CallOn: A spawnstruct"
"MandatoryArg: <.entity>: Member variable on spawnstruct that sets the entity that will be moved"
"OptionalArg: <.entities>: Member variable on spawnstruct that sets an array of entities to be moved"
"OptionalArg: <.forward>: Member variable on spawnstruct that determines how far forward the entity will move"
"OptionalArg: <.right>: Member variable on spawnstruct that determines how far right the entity will move"
"OptionalArg: <.up>: Member variable on spawnstruct that determines how far up the entity will move"
"OptionalArg: <.yaw>: Member variable on spawnstruct that determines how much to rotate the yaw"
"OptionalArg: <.pitch>: Member variable on spawnstruct that determines how much to rotate the pitch"
"OptionalArg: <.roll>: Member variable on spawnstruct that determines how much to rotate the roll"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
translate_local()
{
	entities = [];
	if ( IsDefined( self.entities ) )
		entities = self.entities;
	if ( IsDefined( self.entity ) )
	entities[ entities.size ] = self.entity;

	AssertEx( entities.size > 0, "Tried to do translate_local without any entities" );
	array_levelthread( entities, ::translate_local_on_ent );
}


/*
=============
///ScriptDocBegin
"Name: open_up_fov( <time> , <player_rig> , <tag> , <arcRight> , <arcLeft> , <arcTop> , <arcBottom> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
open_up_fov( time, player_rig, tag, arcRight, arcLeft, arcTop, arcBottom )
{
	level.player endon( "stop_opening_fov" );
	wait( time );
	level.player PlayerLinkToDelta( player_rig, tag, 1, arcRight, arcLeft, arcTop, arcBottom, true );
}



/*
=============
///ScriptDocBegin
"Name: get_ai_touching_volume( sTeam, species )"
"Summary: Returns an array of all ai touching a specified info_volume"
"Module: Utility"
"CallOn: An info_volume"
"OptionalArg: <sTeam>: 'axis' or 'allies'. Left blank the function will return an array containing all ai of all teams."
"OptionalArg: <species>: 'human', 'dog', 'civilians'. Left blank the function will return an array containing all ai of all species."
"Example: aEnemies = eRoomVolume get_ai_touching_volume( team )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_ai_touching_volume( sTeam, species, bGetDrones )
{
	if ( !isdefined( sTeam ) )
		sTeam = "all";

	if ( !isdefined( species ) )
		species = "all";

	aTeam = GetAISpeciesArray( sTeam, species );

	aGuysTouchingVolume = [];
	foreach ( guy in aTeam )
	{
		AssertEx( IsAlive( guy ), "Got ai array yet got a dead guy!" );
		if ( guy IsTouching( self ) )
			aGuysTouchingVolume[ aGuysTouchingVolume.size ] = guy;
	}

	return aGuysTouchingVolume;
}

/*
=============
///ScriptDocBegin
"Name: get_drones_touching_volume( sTeam )"
"Summary: Returns an array of all ai touching a specified info_volume"
"Module: Utility"
"CallOn: An info_volume"
"OptionalArg: <sTeam>: 'axis', 'allies' or 'neutral. Left blank the function will return an array containing all ai of all teams."
"Example: aDrones = eRoomVolume get_drones_touching_volume( "axis" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_drones_touching_volume( sTeam )
{
	if ( !isdefined( sTeam ) )
		sTeam = "all";

	aDrones = [];
	if ( sTeam == "all" )
	{
		aDrones = array_merge( level.drones[ "allies" ].array, level.drones[ "axis" ].array );
		aDrones = array_merge( aDrones, level.drones[ "neutral" ].array );
	}
	else
		aDrones = level.drones[ sTeam ].array;

	aDronesToReturn = [];
	foreach ( drone in aDrones )
	{
		if ( !isdefined( drone ) )
			continue;
		if ( drone IsTouching( self ) )
			aDronesToReturn[ aDronesToReturn.size ] = drone;
	}
	return aDronesToReturn;
}


/*
=============
///ScriptDocBegin
"Name: get_drones_with_targetname( sTargetname )"
"Summary: Returns an array of all live drones with the specified targetname"
"Module: Utility"
"MandatoryArg: <"MandatoryArg>: Targetname of the drones you want."
"Example: aDrones = get_drones_with_targetname( bunker_drones )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_drones_with_targetname( sTargetname )
{
	aDrones = array_merge( level.drones[ "allies" ].array, level.drones[ "axis" ].array );
	aDrones = array_merge( aDrones, level.drones[ "neutral" ].array );
	aDronesToReturn = [];
	foreach ( drone in aDrones )
	{
		if ( !isdefined( drone ) )
			continue;
		if ( ( IsDefined( drone.targetname ) ) && ( drone.targetname == sTargetname ) )
			aDronesToReturn[ aDronesToReturn.size ] = drone;
	}
	return aDronesToReturn;
}

/*
=============
///ScriptDocBegin
"Name: get_other_player( <player> )"
"Summary: Gets the other player in Coop."
"Module: Utility"
"MandatoryArg: <player>: A player. The function will return the other player in coop"
"Example: other_player = get_other_player( player );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_other_player( player )
{
	Assert( is_coop() );
	Assert( isdefined( player ) && isplayer( player ) );

	foreach ( other_player in level.players )
	{
		if ( player == other_player )
			continue;
		return other_player;
	}

	AssertMsg( "get_other_player() tried to get other player but there is no other player." );
}

/*
=============
///ScriptDocBegin
"Name: set_count( <count> )"
"Summary: Set the count of a spawner"
"Module: Utility"
"CallOn: A spawner"
"MandatoryArg: <Count>: The count to set to"
"Example: spawner set_count( 3 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_count( count )
{
	/#
	AssertEx( IsDefined( self ), "Spawner wasn't defined!" );
	AssertEx( !isalive( self ), "Spawner was alive!" );
	#/
	self.count = count;
}/*
=============
///ScriptDocBegin
"Name: follow_path( <node>, <require_player_dist>, <arrived_at_node_func> )"
"Summary: Set's the AI on a path of nodes, script_structs, or script_origins.
Internally uses maps\_spawner::go_to_node which has the following attributes:
- if the node has .script_requires_player set, then the AI will stop at that node until the player catches up.
- if the node has .script_delay set, then the AI will stop at that node for the amount of delay"
- the function will return once all nodes are traversed, and will notify self 'path_end_reached'.
- follow_path stops maps\_spawner::go_to_node from exploding the AI's goal_radius after the last node is reached."
"Module: Utility"
"CallOn: AI"
"MandatoryArg: <node>: a node, script_struct, or script_origin that starts the path"
"OptionalArg: <require_player_dist>: The distance the player must be to the AI before the AI will move on from a node with .script_requires_player set, defaults to 300."
"OptionalArg: <arrived_at_node_func>: Optional function to run every time the AI reaches his goal."
"OptionalArg: <start_moving_func>: Optional function to run every time the AI leaves his goal."
"Example: other_player = get_other_player( player );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
follow_path( node, require_player_dist, arrived_at_node_func, start_moving_func )
{
	self notify( "_utility::follow_path" );
	self endon( "_utility::follow_path" );
	self endon( "death" );

	goal_type = undefined;
	//only nodes and structs dont have classnames - ents do
	if ( !isdefined( node.classname ) )
	{
		//only structs don't have types, nodes do
		if ( !isdefined( node.type ) )
			goal_type = "struct";
		else
			goal_type = "node";
	}
	else
		goal_type = "entity";

	if ( !isdefined( require_player_dist ) )
		require_player_dist = 300;

	//set this so the radius doesn't explode.
	oldforcegoal = self.script_forcegoal;
	self.script_forcegoal = 1;

	self maps\_spawner::go_to_node( node, goal_type, arrived_at_node_func, require_player_dist, start_moving_func );

	self.script_forcegoal = oldforcegoal;
}

/*
=============
///ScriptDocBegin
"Name: enable_dynamic_run_speed( <pushdist> , <sprintdist> , <stopdist> , <jogdist> , <group> , <dontChangeMovePlaybackRate> )"
"Summary: Turns on dynamic speed system for an AI, where the AI will run faster or slower based on average position of players.
make sure to call maps\_dynamic_run_speed::main(); after maps\_load::main(). maps\_dynamic_run_speed::main() takes an optional parameter
which is an array of dialogue lines the for the AI to say when they are stopped and want the player to catch up"
"Module: AI"
"CallOn: AI"
"OptionalArg: <pushdist>: The max distance from the player to the AI that the AI will go from a jog/walk to a run, at twice this distance the ai will stop. DEFAULTS to 250."
"OptionalArg: <sprintdist>: The min distance from the player to the AI where the AI will start sprinting. DEFAULTS to 125."
"Example: level.price enable_dynamic_run_speed();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_dynamic_run_speed( pushdist, sprintdist, stopdist, jogdist, group, dontChangeMovePlaybackRate )
{
	AssertEx( IsDefined( level.scr_anim[ "generic" ][ "DRS_sprint" ] ), " - -- -- -- -- -- -- add this line: 'maps\_dynamic_run_speed::main();' AFTER maps\\\_load::main(); -- -- -- -- -- -- - " );
	if ( !isdefined( pushdist ) )
		pushdist = 250;

	if ( !isdefined( sprintdist ) )
		sprintdist = 100;

	if ( !isdefined( stopdist ) )
		stopdist = pushdist * 2;

	if ( !isdefined( jogdist ) )
		jogdist = pushdist * 1.25;
	if ( !isdefined( dontChangeMovePlaybackRate ) )
		dontChangeMovePlaybackRate = false;

	self.dontChangeMovePlaybackRate = dontChangeMovePlaybackRate;

	self thread dynamic_run_speed_proc( pushdist, sprintdist, stopdist, jogdist, group );
}

/*
=============
///ScriptDocBegin
"Name: disable_dynamic_run_speed()"
"Summary: Stops Dynamic run speed logic on an AI."
"Module: AI"
"CallOn: AI"
"OptionalArg: <pushdist>: The max distance from the player to the AI that the AI will go from a jog/walk to a run, at twice this distance the ai will stop. DEFAULTS to 250."
"OptionalArg: <sprintdist>: The min distance from the player to the AI where the AI will start sprinting. DEFAULTS to 125."
"Example: level.price disable_dynamic_run_speed();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_dynamic_run_speed()
{
	self notify( "stop_dynamic_run_speed" );
}

/*
=============
///ScriptDocBegin
"Name: player_seek_enable()"
"Summary: Makes an enemy AI seek out the closest player.  Returns when he gets close to the player."
"Module: AI"
"CallOn: AI"
"Example: enemy thread player_seek_enable();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_seek_enable()
{
	self endon( "death" );
	self endon( "stop_player_seek" );
	g_radius = 1200;
	if ( self has_shotgun() )
		g_radius = 250;

	newGoalRadius = Distance( self.origin, level.player.origin );
	for ( ;; )
	{
		wait 2;
		self.goalradius = newGoalRadius;
		player = get_closest_player( self.origin );
		self SetGoalEntity( player );
		newGoalRadius -= 175;
		if ( newGoalRadius < g_radius )
		{
			newGoalRadius = g_radius;
			return;
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: player_seek_disable()"
"Summary: Makes an enemy AI stop seeking out the closest player. Disables behavior caused by funtion player_seek_enable()"
"Module: AI"
"CallOn: AI"
"Example: enemy thread player_seek_disable();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_seek_disable()
{
	self notify( "stop_player_seek" );
}

/*
=============
///ScriptDocBegin
"Name: waittill_entity_in_range_or_timeout( <entity>, <range>, <timeout> )"
"Summary: waits until a given entity is within range of the calling entity or timeout time passes"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <entity>: entity whose origin is checked to see if it is within range of the calling entity "
"OptionalArg: <range>: distance in units to check"
"OptionalArg: <timeout>: time in seconds to wait defaults to 5"
"Example: level.player waittill_entity_in_range_or_timeout( level.enemyHeli, 2048, 10 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_entity_in_range_or_timeout( entity, range, timeout )
{
	self endon( "death" );
	entity endon( "death" );
	if ( !isdefined( timeout ) )
		timeout = 5;
	timeout_time = GetTime() + ( timeout * 1000 );
	while ( IsDefined( entity ) )
	{
		if ( Distance( entity.origin, self.origin ) <= range )
			break;
		if ( GetTime() > timeout_time )
			break;
		wait .1;
	}
}

/*
=============
///ScriptDocBegin
"Name: waittill_entity_in_range( <entity>, <range> )"
"Summary: waits until a given entity is within range of the calling entity"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <entity>: entity whose origin is checked to see if it is within range of the calling entity "
"OptionalArg: <range>: distance in units to check"
"Example: level.player waittill_entity_in_range( level.enemyHeli, 2048 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_entity_in_range( entity, range )
{
	self endon( "death" );
	entity endon( "death" );
	while ( IsDefined( entity ) )
	{
		if ( Distance( entity.origin, self.origin ) <= range )
			break;
		wait .1;
	}
}
/*
=============
///ScriptDocBegin
"Name: waittill_entity_out_of_range( <entity>, <range> )"
"Summary: waits until a given entity is out of range of the calling entity"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <entity>: entity whose origin is checked"
"OptionalArg: <range>: distance in units to check"
"Example: level.player waittill_entity_out_of_range( level.enemyHeli, 2048 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_entity_out_of_range( entity, range )
{
	self endon( "death" );
	entity endon( "death" );
	while ( IsDefined( entity ) )
	{
		if ( Distance( entity.origin, self.origin ) > range )
			break;
		wait .1;
	}
}

/*
=============
///ScriptDocBegin
"Name: has_shotgun()"
"Summary: returns true/false if AI has a shotgun"
"Module: AI"
"CallOn: AI"
"Example: self has_shotgun()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
has_shotgun()
{
	self endon( "death" );
	if ( !isdefined( self.weapon ) )
		return false;

//	shotguns = [ "maul", "fp6", "uts16", "mts55" ];
//	foreach ( shotgun in shotguns )
//	{
//		if ( IsSubStr( self.weapon, shotgun ) )
//			return true;
//	}
	
	if ( WeaponClass( self.weapon ) == "spread" )
		return true;
	
		return false;
}

/*
=============
///ScriptDocBegin
"Name: isPrimaryWeapon( weaponName )"
"Summary: Returns if the weapon is a primary weapon."
"Example: isPrimaryWeapon( weaponName )"
///ScriptDocEnd
=============
*/

isPrimaryWeapon( weapName )
{
	if ( weapName == "none" )
		return false;
		
	if ( weaponInventoryType( weapName ) != "primary" )
		return false;

	switch ( weaponClass( weapName ) )
	{
		case "rifle":
		case "smg":
		case "mg":
		case "spread":
		case "pistol":
		case "rocketlauncher":
		case "sniper":
			return true;

		default:
			return false;
	}	
}

/*
=============
///ScriptDocBegin
"Name: player_has_thermal()"
"Summary: returns true/false if player has a weapon with a thermal scope"
"Module: Player"
"CallOn: Player"
"Example: if ( level.player player_has_thermal() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_has_thermal()
{
	weapons = self GetWeaponsListAll();
	if ( !isdefined( weapons ) )
		return false;
	foreach ( weapon in weapons )
	{
		if ( IsSubStr( weapon, "thermal" ) )
			return true;
	}
	return false;
}

/*
=============
///ScriptDocBegin
"Name: waittill_true_goal( <origin> , <radius> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_true_goal( origin, radius )
{
	self endon( "death" );

	if ( !isdefined( radius ) )
		radius = self.goalradius;

	while ( 1 )
	{
		self waittill( "goal" );
		if ( Distance( self.origin, origin ) < radius + 10 )// give a little leeway
			break;
	}
}

/*
=============
///ScriptDocBegin
"Name: player_speed_percent( <percent> , <time> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_speed_percent( percent, time )
{
	currspeed = Int( GetDvar( "g_speed" ) );
	if ( !isdefined( level.player.g_speed ) )
		level.player.g_speed = currspeed;

	goalspeed = Int( level.player.g_speed * percent * .01 );

	level.player player_speed_set( goalspeed, time );
}

/*
=============
///ScriptDocBegin
"Name: blend_movespeedscale_percent( <percent> , <time> )"
"Summary: blends the player's speed percentage using setMoveSpeedScale over time"
"Module: Player"
"CallOn: player"
"MandatoryArg: <percent>: percentage > 0 to scale to"
"OptionalArg: <time>: time in seconds to lerp over if none - set speed instantly"
"Example: level.player blend_movespeedscale_percent( 1.2, 3 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
blend_movespeedscale_percent( percent, time )
{
	player = self;
	if ( !isplayer( player ) )
		player = level.player;

	if ( !isdefined( player.movespeedscale ) )
		player.movespeedscale = 1.0;

	goalscale = percent * .01;

	player blend_movespeedscale( goalscale, time );
}

/*
=============
///ScriptDocBegin
"Name: player_speed_set( <speed> , <time> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_speed_set( speed, time )
{
	currspeed = Int( GetDvar( "g_speed" ) );
	if ( !isdefined( level.player.g_speed ) )
		level.player.g_speed = currspeed;

	get_func = ::g_speed_get_func;
	set_func = ::g_speed_set_func;
	level.player thread player_speed_proc( speed, time, get_func, set_func, "player_speed_set" );
}

/*
=============
///ScriptDocBegin
"Name: player_bob_scale_set( <bobScale> , <time> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_bob_scale_set( scale, time )
{
	get_func = ::g_bob_scale_get_func;
	set_func = ::g_bob_scale_set_func;
	level.player thread player_speed_proc( scale, time, get_func, set_func, "player_bob_scale_set" );
}

/*
=============
///ScriptDocBegin
"Name: blend_movespeedscale( <scale> , <time> )"
"Summary: blends the player's speed scaler using setMoveSpeedScale over time"
"Module: Player"
"CallOn: player"
"MandatoryArg: <scale>: scale > 0 to scale to"
"OptionalArg: <time>: time in seconds to lerp over if none - set speed instantly"
"Example: level.player blend_movespeedscale( 1.2, 3 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
blend_movespeedscale( scale, time )
{
	player = self;
	if ( !isplayer( player ) )
		player = level.player;

	if ( !isdefined( player.movespeedscale ) )
		player.movespeedscale = 1.0;

	get_func = ::movespeed_get_func;
	set_func = ::movespeed_set_func;
	player thread player_speed_proc( scale, time, get_func, set_func, "blend_movespeedscale" );
}

player_speed_proc( speed, time, get_func, set_func, ender )
{
	self notify( ender );
	self endon( ender );

	currspeed = [[ get_func ]]();
	goalspeed = speed;

	if ( IsDefined( time ) )
	{
		range = goalspeed - currspeed;
		interval = .05;
		numcycles = time / interval;
		fraction = range / numcycles;

		while ( abs( goalspeed - currspeed ) > abs( fraction * 1.1 ) )
		{
			currspeed += fraction;
			[[ set_func ]]( currspeed );
			wait interval;
		}
	}

	[[ set_func ]]( goalspeed );
}

/*
=============
///ScriptDocBegin
"Name: player_speed_default( <time> )"
"Summary: blends the level.player's speed back to default using the dvar g_speed over time"
"Module: Player"
"CallOn: "
"OptionalArg: <time>: the time to blend over, if none - then set speed instantly"
"Example: player_speed_default( 2 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_speed_default( time )
{
	if ( !isdefined( level.player.g_speed ) )
		return;

	level.player player_speed_set( level.player.g_speed, time );

	waittillframeend;
	level.player.g_speed = undefined;
}

/*
=============
///ScriptDocBegin
"Name: blend_movespeedscale_default( <time> )"
"Summary: blends the player's speed scale back to default using setmovespeedscale over time"
"Module: Player"
"CallOn: player"
"OptionalArg: <time>: the time to blend over, if none - then set speed instantly"
"Example: level.player blend_movespeedscale_default( 2 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
blend_movespeedscale_default( time )
{
	player = self;
	if ( !isplayer( player ) )
		player = level.player;

	if ( !isdefined( player.movespeedscale ) )
		return;

	player blend_movespeedscale( 1.0, time );

	waittillframeend;
	player.movespeedscale = undefined;
}

/*
=============
///ScriptDocBegin
"Name: teleport_ent( <ent> )"
"Summary: Teleport self to an ent"
"Module: Utility"
"CallOn: An entity you want to teleport"
"MandatoryArg: <ent>: The entity to teleport to"
"Example: level.player teleport_ent( player_start_ent );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
teleport_ent( ent )
{
	if ( IsPlayer( self ) )
	{
		self SetOrigin( ent.origin );
		self SetPlayerAngles( ent.angles );
	}
	else
	{
		self ForceTeleport( ent.origin, ent.angles );
	}
}


/*
=============
///ScriptDocBegin
"Name: teleport_to_ent_tag( <ent> , <tag> )"
"Summary: teleport self to the tag location and angles of an entity"
"Module: Utility"
"CallOn: An entity you want to teleport"
"MandatoryArg: <ent>: ent to teleport to"
"MandatoryArg: <tag>: tag on ent to teleport to"
"Example: self teleport_to_ent_tag( ent, tag );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

teleport_to_ent_tag( ent, tag )
{
    position = ent GetTagOrigin( tag );
    angles = ent GetTagAngles( tag );
    self DontInterpolate();
    
 	if ( IsPlayer( self ) )
	{
		self SetOrigin( position );
		self SetPlayerAngles( angles );
	}
	else if( IsAI( self ) )
	{
		self ForceTeleport( position, angles );
	}   
	else
	{
	    self.origin = position;
	    self.angles = angles;
	}
}


/*
=============
///ScriptDocBegin
"Name: teleport_ai( <eNode> )"
"Summary: Force teleport of an AI to a given node and set its goalpos and goalnode to the node"
"Module: Utility"
"CallOn: An AI you want to teleport"
"MandatoryArg: <eNode>: The node to teleport to"
"Example: level.price teleport_ai( eNode );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
teleport_ai( eNode )
{
	AssertEx( IsAI( self ), "Function teleport_ai can only be called on an AI entity" );
	AssertEx( IsDefined( eNode ), "Need to pass a node entity to function teleport_ai" );
	self ForceTeleport( eNode.origin, eNode.angles );
	self SetGoalPos( self.origin );
	self SetGoalNode( eNode );
}

/*
=============
///ScriptDocBegin
"Name: move_all_fx( <vec> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
move_all_fx( vec )
{
	foreach ( fx in level.createFXent )
	{
		fx.v[ "origin" ] += vec;
	}
}

/*
=============
///ScriptDocBegin
"Name: IsSliding( <IsSliding> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
IsSliding()
{
	return IsDefined( self.slideModel );
}

/*
=============
///ScriptDocBegin
"Name: BeginSliding( <velocity> , <allowedAcceleration> , <dampening> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
BeginSliding( velocity, allowedAcceleration, dampening )
{
	Assert( IsPlayer( self ) );
	player = self;


	player thread play_sound_on_entity( "foot_slide_plr_start" );
	if( SoundExists( "foot_slide_plr_loop" ) )
		player thread play_loop_sound_on_tag( "foot_slide_plr_loop" );

	override_link_method = IsDefined( level.custom_linkto_slide );

	if ( !isDefined( velocity ) )
		velocity = player GetVelocity() + ( 0, 0, -10 );
	if ( !isDefined( allowedAcceleration ) )
		allowedAcceleration = 10;
	if ( !isDefined( dampening ) )
	{
		if( isDefined( level.slide_dampening ))
			dampening = level.slide_dampening;
		else
			dampening = .035;
	}
		
	Assert( !isDefined( player.slideModel ) );
	// in the future, slideModel will actually be a script_model of legs
	slideModel = Spawn( "script_origin", player.origin );
	slideModel.angles = player.angles;
	player.slideModel = slideModel;

	slideModel MoveSlide( ( 0, 0, 15 ), 15, velocity );

	if ( override_link_method )
	{
		player PlayerLinkToBlend( slideModel, undefined, 1 );
	}
	else
	{
		player PlayerLinkTo( slideModel );
	}

	player DisableWeapons();
	player AllowProne( false );
	player AllowCrouch( true );
	player AllowStand( false );

	player thread DoSlide( slideModel, allowedAcceleration, dampening );
}

/*
=============
///ScriptDocBegin
"Name: EndSliding( <EndSliding> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
EndSliding()
{
	Assert( IsPlayer( self ) );
	player = self;

	Assert( IsDefined( player.slideModel ) );

	player notify( "stop sound" + "foot_slide_plr_loop" );
	player thread play_sound_on_entity( "foot_slide_plr_end" );
	player Unlink();
	player SetVelocity( player.slidemodel.slideVelocity );
 	player.slideModel Delete();
 	player EnableWeapons();
	player AllowProne( true );
	player AllowCrouch( true );
	player AllowStand( true );

	player notify( "stop_sliding" );
}


/*
=============
///ScriptDocBegin
"Name: spawn_vehicle( <spawn_vehicle> )"
"Summary: Self is a vehicle spawner"
"Module: Vehicles"
LevelOn: A vehicle spawner"
"Example: vehicle = spawner spawn_vehicle();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_vehicle()
{
	/#
	// dev comments so we can catch the local variable in the debugger
	vehicle = maps\_vehicle::vehicle_spawn( self );
	return vehicle;
	#/

	// to conform with the style that AI are spawned;
	return maps\_vehicle::vehicle_spawn( self );
}

/*
=============
///ScriptDocBegin
"Name: getEntWithFlag( <flag> )"
"Summary: Returns an entity that has the flag"
"Module: Utility"
"MandatoryArg: <flag>: The flag "
"Example: flagEnt = getEntWithFlag( "give_c4_obj" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getEntWithFlag( flag )
{
	trigger_classes = maps\_trigger::get_load_trigger_classes();

	triggers = [];

	foreach ( class, _ in trigger_classes )
	{
		if ( !IsSubStr( class, "flag" ) )
			continue;
		other_triggers = GetEntArray( class, "classname" );
		triggers = array_combine( triggers, other_triggers );
	}

	trigger_funcs = maps\_trigger::get_load_trigger_funcs();

	foreach ( func, _ in trigger_funcs )
	{
		if ( !IsSubStr( func, "flag" ) )
			continue;
		other_triggers = GetEntArray( func, "targetname" );
		triggers = array_combine( triggers, other_triggers );
	}

	found_trigger = undefined;
	/#
	foreach ( trigger in triggers )
	{
		AssertEx( IsDefined( trigger.script_flag ), "Flag trigger at " + trigger.origin + " has no script_flag" );

		if ( trigger.script_flag == flag )
		{
			AssertEx( !isdefined( found_trigger ), "Did getEntWithFlag on flag " + flag + " but found multiple entities with that flag" );
			found_trigger = trigger;
		}
	}

	//AssertEx( IsDefined( found_trigger ), "Tried to find entity with flag " + flag + " but found none" );
	//if ( 1 ) return found_trigger;
	#/

	foreach ( trigger in triggers )
	{
		if ( trigger.script_flag == flag )
		{
			return trigger;
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: getEntArrayWithFlag( <flag> )"
"Summary: Returns an array of entities that have the flag"
"Module: Utility"
"MandatoryArg: <flag>: The flag "
"Example: flagEnt = getEntArrayWithFlag( "give_c4_obj" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getEntArrayWithFlag( flag )
{
	trigger_classes = maps\_trigger::get_load_trigger_classes();

	triggers = [];

	foreach ( class, _ in trigger_classes )
	{
		if ( !IsSubStr( class, "flag" ) )
			continue;
		other_triggers = GetEntArray( class, "classname" );
		triggers = array_combine( triggers, other_triggers );
	}

	trigger_funcs = maps\_trigger::get_load_trigger_funcs();

	foreach ( func, _ in trigger_funcs )
	{
		if ( !IsSubStr( func, "flag" ) )
			continue;
		other_triggers = GetEntArray( func, "targetname" );
		triggers = array_combine( triggers, other_triggers );
	}

	found_triggers = [];
	/#
	foreach ( trigger in triggers )
	{
		AssertEx( IsDefined( trigger.script_flag ), "Flag trigger at " + trigger.origin + " has no script_flag" );

		if ( trigger.script_flag == flag )
		{
			found_triggers[ found_triggers.size ] = trigger;
		}
	}

	AssertEx( found_triggers.size, "Tried to find entity with flag " + flag + " but found none" );
	if ( 1 ) return found_triggers;
	#/

	foreach ( trigger in triggers )
	{
		if ( trigger.script_flag == flag )
		{
			found_triggers[ found_triggers.size ] = trigger;
		}
	}
	return found_triggers;
}

/*
=============
///ScriptDocBegin
"Name: set_z( <vec> , <z> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_z( vec, z )
{
	return( vec[ 0 ], vec[ 1 ], z );
}

add_z( vec, zPlus )
{
	return ( vec[ 0 ], vec[ 1 ], vec[ 2 ] + zPlus );
}

/*
=============
///ScriptDocBegin
"Name: set_y( <vec> , <y> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_y( vec, y )
{
	return( vec[ 0 ], y, vec[ 2 ] );
}

/*
=============
///ScriptDocBegin
"Name: set_x( <vec> , <x> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_x( vec, x )
{
	return( x, vec[ 1 ], vec[ 2 ] );
}

/*
=============
///ScriptDocBegin
"Name: player_using_missile()"
"Summary: detect whether player has a missile weapon equipped as his current weapon (AT4, RPG, Stinger)"
"Module: Utility"
"CallOn: Player"
"Example: if ( player_using_missile() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_using_missile()
{
	weapon = self GetCurrentWeapon();

	if ( !isdefined( weapon ) )
		return false;

	if ( IsSubStr( ToLower( weapon ), "rpg" ) )
		return true;

	if ( IsSubStr( ToLower( weapon ), "stinger" ) )
		return true;

	if ( IsSubStr( ToLower( weapon ), "at4" ) )
		return true;

	if ( IsSubStr( ToLower( weapon ), "javelin" ) )
		return true;

	return false;
}

/*
=============
///ScriptDocBegin
"Name: doingLongDeath( <longDeath> )"
"Summary: Returns if an AI is doing long death"
"Module: Utility"
"CallOn: An AI"
"Example: if ( guy longDeath() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
doingLongDeath()
{
	Assert( IsAI( self ) );
	return IsDefined( self.a.doingLongDeath );
}

/*
=============
///ScriptDocBegin
"Name: get_rumble_ent( <rumble> )"
"Summary: Create an entity to do rumble on. Set .intensity to change the rumble intensity or use blend commands below."
"Module: Rumble"
"OptionalArg: <rumble>: The rumble to use, if not specified steady_rumble is used. "
"Example: ent = get_rumble_ent();"
"NoteLine: WARNING: The rumble you pass in must have a fade range of 1000, otherwise blending will be wrong. "
"NoteLine: don't forget to add PreCacheRumble( "steady_rumble" ) or whatever rumble you decide to use to your script. "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_rumble_ent( rumble, starting_rumble_val )
{
	if ( is_coop() )
		PrintLn( "^3Warning! Using get_rumble_ent will cause the same rumbles to apply to all of the coop players!" );

	player = get_player_from_self();
	if ( !IsDefined( rumble ) )
		rumble = "steady_rumble";
	ent = Spawn( "script_origin", player GetEye() );
	if(!IsDefined(starting_rumble_val) || !IsNumber(starting_rumble_val))
	ent.intensity = 1;
	else
		ent.intensity = starting_rumble_val;
	ent thread update_rumble_intensity( player, rumble );
	return ent;
}

set_rumble_intensity( intensity )
{
	AssertEx( intensity >= 0 && intensity <= 1, "Intensity must be between 0 and 1" );
	self.intensity = intensity;
}

/*
=============
///ScriptDocBegin
"Name: rumble_ramp_on( <time> )"
"Summary: Ramp rumble to 1 on entity"
"Module: Rumble"
"CallOn: A rumble entity created with get_rumble_ent()"
"MandatoryArg: <time>: Time in seconds over which to ramp "
"Example: rumble_ent rumble_ramp_on( 2 )";"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
rumble_ramp_on( time )
{
	thread rumble_ramp_to( 1, time );
}

/*
=============
///ScriptDocBegin
"Name: rumble_ramp_off( <time> )"
"Summary: Ramp rumble to 0 on entity"
"Module: Rumble"
"CallOn: A rumble entity created with get_rumble_ent()"
"MandatoryArg: <time>: Time in seconds over which to ramp "
"Example: rumble_ent rumble_ramp_off( 2 )";"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
rumble_ramp_off( time )
{
	thread rumble_ramp_to( 0, time );
}

/*
=============
///ScriptDocBegin
"Name: rumble_ramp_to( <dest> , <time> )"
"Summary: Ramp rumble to dest on entity"
"Module: Rumble"
"CallOn: A rumble entity created with get_rumble_ent()"
"MandatoryArg: <dest>: Value to blend rumble to "
"MandatoryArg: <time>: Time in seconds over which to ramp "
"Example: rumble_ent rumble_ramp_to( 1, 2 )";"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
rumble_ramp_to( dest, time )
{
	self notify( "new_ramp" );
	self endon( "new_ramp" );
	self endon( "death" );

	frames = time * 20;
	dif = dest - self.intensity;
	slice = dif / frames;

	for ( i = 0; i < frames; i++ )
	{
		self.intensity += slice;
		wait( 0.05 );
	}
	self.intensity = dest;
}

/*
=============
///ScriptDocBegin
"Name: get_player_from_self()"
"Summary: Returns a player, and if self is a player then it returns that player."
"Module: Utility"
"Example: player = get_player_from_self();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_player_from_self()
{
	if ( IsDefined( self ) )
	{
		if ( !is_in_array( level.players, self ) )
			return level.player;
		else
			return self;
	}
	else
		return level.player;
}

/*
=============
///ScriptDocBegin
"Name: get_player_gameskill( <get_player_gameskill> )"
"Summary: Returns the gameskill for a player based on the dvar."
"Module: Entity"
"CallOn: A player"
"Example: player get_player_gameskill()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_player_gameskill()
{
	AssertEx( IsPlayer( self ), "get_player_gameskill() can only be called on a player." );
	return Int( self GetPlayerSetting( "gameskill" ) );
}

/*
=============
///ScriptDocBegin
"Name: glow( <model> )"
"Summary: Makes a model glow with"
"Module: Utility"
"CallOn: An entity that should glow"
"OptionalArg: <Model>: A glow model to use. If not specified then _obj is appended to the current model, which is how it should normally work anyway. "
"Example: keyboard glow();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
glow( model )
{
	if ( IsDefined( self.non_glow_model ) )
		return;

	self.non_glow_model = self.model;

	if ( !isdefined( model ) )
		model = self.model + "_obj";
	self SetModel( model );
}

/*
=============
///ScriptDocBegin
"Name: stopGlow( <model> )"
"Summary: Stops a model from glowing."
"Module: Utility"
"CallOn: An entity that is glowing"
"Example: keyboard stopGlow();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
stopGlow( model )
{
	if ( !isdefined( self.non_glow_model ) )
		return;

	self SetModel( self.non_glow_model );
	self.non_glow_model = undefined;
}

/*
=============
///ScriptDocBegin
"Name: array_delete_evenly( <array> , <delete_size>, <set_size> )"
"Summary: Evenly remove entries from an array. For example delete 3 out of every 4 entries in an array."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <array>: The array, order of the array determines the order stuff is removed."
"MandatoryArg: <delete_size>: The ratio of entries to delete."
"MandatoryArg: <set_size>: The size of each set."
"Example: array = array_delete_evenly( array, 1, 3 ); // Delete 1 out of every 3 entries in the array"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_delete_evenly( array, delete_size, set_size )
{
	AssertEx( delete_size > 0, "Save size must be at least 1" );
	AssertEx( set_size > 0, "Removal size must be at least 1" );
	AssertEx( delete_size < set_size, "Save size must be less than removal size" );
	removal = [];
	delete_size = set_size - delete_size;
	foreach ( entry in array )
	{
		removal[ removal.size ] = entry;
		if ( removal.size == set_size )
		{
			// remove 2 of every 3 entrys
			removal = array_randomize( removal );
			for ( i = delete_size; i < removal.size; i++ )
			{
				removal[ i ] Delete();
			}
			removal = [];
		}
	}

	new_array = [];
	foreach ( entry in array )
	{
		if ( !isdefined( entry ) )
			continue;
		new_array[ new_array.size ] = entry;
	}

	return new_array;
}

/*
=============
///ScriptDocBegin
"Name: waittill_in_range( <origin> , <range> )"
"Summary: Wait until and entity is within range or a given origin."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <origin>: wait until entity is within range of this position."
"MandatoryArg: <range>: wait until entity is within this distance to the given position."
"OptionalArg: <resolution>: time between poling, if unspecified uses half a second."
"Example: 	level.player waittill_in_range( obj_origin, 600 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_in_range( origin, range, resolution )
{
    if( !isdefined( resolution )  )
        resolution = 0.5;
	self endon( "death" );
	while ( IsDefined( self ) )
	{
		if ( Distance( origin, self.origin ) <= range )
			break;
		wait resolution;
	}
}


/*
=============
///ScriptDocBegin
"Name: add_trace_fx( <name> )"
"Summary: Adds FX that play off a trace from a tag. See _climb.gsc for examples."
"Module: Utility"
"MandatoryArg: <name>: Name the tracefx."
"Example: add_trace_fx( "ice_pick" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_trace_fx( name )
{
	ent = SpawnStruct();
	ent thread add_trace_fx_proc( name );
	return ent;
}

/*
=============
///ScriptDocBegin
"Name: traceFX_on_tag( <fx_name> , <tag> , <trace_depth> )"
"Summary: Plays a traceFX on the specified tag."
"Module: Utility"
"CallOn: An entity with a tag."
"MandatoryArg: <fx_name>: The name of the tracefx, created with add_trace_fx."
"MandatoryArg: <tag>: The tag to play the fx on."
"MandatoryArg: <trace_depth>: How far to trace to find a surface."
"Example: car traceFX_on_tag( "player_ice_pick", "tag_origin", 10 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
traceFX_on_tag( fx_name, tag, trace_depth )
{
	origin = self GetTagOrigin( tag );
	angles = self GetTagAngles( tag );
	traceFx( fx_name, origin, angles, trace_depth );
}


/*
=============
///ScriptDocBegin
"Name: traceFx( <fx_name> , <origin> , <angles> , <trace_depth> )"
"Summary: Trace from org/ang and then play an fx where it hits"
"Module: Utility"
"MandatoryArg: <fx_name>: The TraceFX name, added with add_trace_fx. "
"MandatoryArg: <origin>: The origin to trace from. "
"MandatoryArg: <angles>: The angles to trace from. "
"MandatoryArg: <trace_depth>: The depth to trace for a surface. "
"Example: traceFx( fx_name, origin, angles, trace_depth );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
traceFx( fx_name, origin, angles, trace_depth )
{
	// plays an effect based off surface type then deletes the tag that called it
	AssertEx( IsDefined( level.trace_fx[ fx_name ] ), "No level.trace_fx with name " + fx_name );
	AssertEx( IsDefined( level.trace_fx[ fx_name ][ "default" ] ), "No default fx defined for " + fx_name );


	forward = AnglesToForward( angles );
	trace = BulletTrace( origin, origin + forward * trace_depth, false, undefined );

	if ( trace[ "fraction" ] >= 1 )
	{
		/#
		if ( GetDvarInt( "debug_tracefx" ) )
			Line( origin, origin + forward * trace_depth, ( 1, 0, 0 ), 1, 0, 500 );
		#/
		// didn't hit anything
		return;
	}

	surface = trace[ "surfacetype" ];
	if ( !isdefined( level.trace_fx[ fx_name ][ surface ] ) )
		surface = "default";

	fx_info = level.trace_fx[ fx_name ][ surface ];

	if ( IsDefined( fx_info[ "fx" ] ) )
	{
		PlayFX( fx_info[ "fx" ], trace[ "position" ], trace[ "normal" ] );
	}

	if ( IsDefined( fx_info[ "fx_array" ] ) )
	{
		foreach ( fx in fx_info[ "fx_array" ] )
		{
			PlayFX( fx, trace[ "position" ], trace[ "normal" ] );
		}
	}

	if ( IsDefined( fx_info[ "sound" ] ) )
	{
		level thread play_sound_in_space( fx_info[ "sound" ], trace[ "position" ] );
	}

	if ( IsDefined( fx_info[ "rumble" ] ) )
	{
		player = get_player_from_self();
		player PlayRumbleOnEntity( fx_info[ "rumble" ] );
	}
}


/*
=============
///ScriptDocBegin
"Name: disable_surprise()"
"Summary: Disables AI surprise"
"Module: AI"
"CallOn: An AI"
"Example: guy disable_surprise();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_surprise()
{
	self.newEnemyReactionDistSq = 0;
}

/*
=============
///ScriptDocBegin
"Name: enable_surprise()"
"Summary: reenables AI surprise"
"Module: AI"
"CallOn: An AI"
"Example: guy enable_surprise();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_surprise()
{
	self.newEnemyReactionDistSq = squared( 512 );
}

/*
=============
///ScriptDocBegin
"Name: enable_heat_behavior( <shoot_while_moving> )"
"Summary: Enables heat behavior"
"Module: AI"
"CallOn: An AI"
"OptionalArg: <shoot_while_moving>: do regular AI shoot behavior while moving. Defaults to false"
"Example: guy enable_heat_behavior();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_heat_behavior( shoot_while_moving )
{
	self.heat = true;
	self.no_pistol_switch = true;
	self.useCombatScriptAtCover = true;

	if ( !isdefined( shoot_while_moving ) || !shoot_while_moving )
	{
		self.dontshootwhilemoving = true;
		self.maxfaceenemydist = 64;
		self.pathenemylookahead = 2048;
		self disable_surprise();
	}

	self.specialReloadAnimFunc = animscripts\animset::heat_reload_anim;

	self.customMoveAnimSet[ "run" ] = self lookupAnimArray( "heat_run" );
}


/*
=============
///ScriptDocBegin
"Name: disable_heat_behavior()"
"Summary: Disables heat behavior"
"Module: AI"
"CallOn: An AI"
"Example: guy disable_heat_behavior();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_heat_behavior()
{
	self.heat = undefined;
	self.no_pistol_switch = undefined;
	self.dontshootwhilemoving = undefined;
	self.useCombatScriptAtCover = false;
	self.maxfaceenemydist = 512;
	self.specialReloadAnimFunc = undefined;

	self.customMoveAnimSet = undefined;
}


/*
=============
///ScriptDocBegin
"Name: getVehicleArray()"
"Summary: Returns Vehicle_GetArray();"
"Module: Utility"
"Example: vehicles = getVehicleArray()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getVehicleArray()
{
	return Vehicle_GetArray();
}

/*
=============
///ScriptDocBegin
"Name: hint( string, timeOut )"
"Summary: Creates a simple white text string at the center of the screen;"
"Module: Utility"
"Example: thread hint( &"OILRIG_HINT_C4_SWITCH" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
hint( string, timeOut, zoffset )
{
	if ( !isdefined( zoffset ) )
		zoffset = 0;

	hintfade = 0.5;

	level endon( "clearing_hints" );

	if ( IsDefined( level.hintElement ) )
		level.hintElement maps\_hud_util::destroyElem();

	level.hintElement = maps\_hud_util::createFontString( "default", 1.5 );
	level.hintElement maps\_hud_util::setPoint( "MIDDLE", undefined, 0, 30 + zoffset );
	level.hintElement.color = ( 1, 1, 1 );
	level.hintElement SetText( string );
	level.hintElement.alpha = 0;
	level.hintElement FadeOverTime( 0.5 );
	level.hintElement.alpha = 1;
	wait( 0.5 );
	level.hintElement endon( "death" );

	if ( IsDefined( timeOut ) )
		wait( timeOut );
	else
		return;

	level.hintElement FadeOverTime( hintfade );
	level.hintElement.alpha = 0;
	wait( hintfade );

	level.hintElement maps\_hud_util::destroyElem();
}


/*
=============
///ScriptDocBegin
"Name: hint_fade()"
"Summary: Fades out a text hint created with the hint() function"
"Module: Utility"
"Example: thread hint( &"OILRIG_HINT_C4_SWITCH" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
hint_fade()
{
	hintfade = 1;
	if ( IsDefined( level.hintElement ) )
	{
		level notify( "clearing_hints" );
		level.hintElement FadeOverTime( hintfade );
		level.hintElement.alpha = 0;
		wait( hintfade );
	}
}

/*
=============
///ScriptDocBegin
"Name: kill_deathflag( <theFlag>, <time> )"
"Summary: Kill everything associated with a deathflag"
"Module: Utility"
"MandatoryArg: <theFlag>: The flag to kill on "
"OptionalArg: <time>: random amount of time to wait before death "
"OptionalArg: <bNotVisible>: boolean - if set, only kill ai outside the player's view "
"Example: kill_deathflag( "tower_cleared" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
kill_deathflag( theFlag, time, bNotVisible )
{
	//assertex( IsDefined( level.flag[ theFlag ] ), "Tried to kill_deathFlag on a flag " + theFlag + " which does not exist." );
	if ( !IsDefined( level.flag[ theFlag ] ) )
		return;

	if ( !IsDefined( time ) )
		time = 0;

	foreach ( deathTypes in level.deathFlags[ theFlag ] )
	{
		foreach ( element in deathTypes )
		{
			if ( IsAlive( element ) )
			{
				element thread kill_deathflag_proc( time, bNotVisible );
			}
			else
			{
				element Delete();
			}
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: get_player_view_controller( <model> , <tag>, <originoffset>, <turret> )"
"Summary: Returns a turret that you can link the player to. Link the player to tag_aim."
"Module: Utility"
"MandatoryArg: <model>: The model to link to."
"MandatoryArg: <tag>: The tag to link to."
"OptionalArg: <originoffset>: Optional offset from the tag."
"OptionalArg: <turret>: Optional turret to use."
"Example: player_view_controller = get_player_view_controller( level.player_heli, "tag_guy2" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_player_view_controller( model, tag, originoffset, turret )
{
	if ( !IsDefined( turret ) )
	{
		turret = "player_view_controller";
	}

	if ( !isdefined( originoffset ) )
		originoffset = ( 0, 0, 0 );
	origin = model GetTagOrigin( tag );
	player_view_controller = SpawnTurret( "misc_turret", origin, turret );
	player_view_controller.angles = model GetTagAngles( tag );
	player_view_controller SetModel( "tag_turret" );
	player_view_controller LinkTo( model, tag, originoffset, ( 0, 0, 0 ) );
	player_view_controller MakeUnusable();
	player_view_controller Hide();
	player_view_controller SetMode( "manual" );
	/*
	player_view_controller thread maps\_debug::drawTagForever( "tag_origin" );
	player_view_controller thread maps\_debug::drawTagForever( "TAG_player", (1,0,0) );
	player_view_controller thread maps\_debug::drawTagForever( "TAG_Weapon", (0,1,0) );
	player_view_controller thread maps\_debug::drawTagForever( "tag_flash", (1,0,1) );
	model thread maps\_debug::drawTagForever( tag );
	player_view_controller thread maps\_debug::drawTagForever( "TAG_aim", (0,0,1) );
	*/
	return player_view_controller;
}


/*
=============
///ScriptDocBegin
"Name: create_blend( <func> , <var1> , <var2> , <var3> )"
"Summary: Creates a spawnstruct that blends values over time, using a function you create (see blend_dof)"
"Module: Utility"
"MandatoryArg: <func>: The function that will blend the values "
"OptionalArg: <var1>: Optional var to use."
"OptionalArg: <var2>: Optional var to use."
"OptionalArg: <var3>: Optional var to use."
"Example: blend = create_blend( ::blend_dof, start, end );     blend.time = 14;"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
create_blend( func, var1, var2, var3 )
{
	ent = SpawnStruct();
	ent childthread process_blend( func, self, var1, var2, var3 );
	return ent;
}

/*
=============
///ScriptDocBegin
"Name: set_unstorable_weapon( <weapon> , <unstorable> )"
"Summary: Prevents a weapon type from being stored/restored"
"Module: Player"
"CallOn: A Player"
"MandatoryArg: <weapon>: the weapon whose type will not be stored"
"OptionalArg: <unstorable>: if this weapon is unstorable, default true"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

set_unstorable_weapon( weapon, unstorable )
{
	assert( IsDefined( weapon ) );

	if ( IsDefined( self.unstorable_weapons ) )
		self.unstorable_weapons = [];

	if ( !IsDefined( unstorable ) || unstorable )
		self.unstorable_weapons[ weapon ] = true;
	else
		self.unstorable_weapons[ weapon ] = undefined;
	
}

is_unstorable_weapon( weapon )
{
	assert( IsDefined( weapon ) );
	
	if ( !IsDefined( self.unstorable_weapons ) )
		return false;
	
	return IsDefined( self.unstorable_weapons[ weapon ] );
}

/*
=============
///ScriptDocBegin
"Name: store_players_weapons( <scene> )"
"Summary: Store the player's inventory to a variable that you can recall later"
"Module: Player"
"CallOn: A player"
"MandatoryArg: <scene>: String to store the inventory to."
"Example: level.player store_players_weapons( "c4_scene" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
store_players_weapons( scene )
{
	if ( !isdefined( self.stored_weapons ) )
	{
		self.stored_weapons = [];
	}
	if ( !IsDefined( self.unstorable_weapons ) )
    {
    	self.unstorable_weapons = [];
    }

	array = [];
	weapons = self GetWeaponsListAll();
	current_weapon = self GetCurrentWeapon();
	
	foreach ( weapon in weapons )
	{
		if ( IsDefined( self.unstorable_weapons[ weapon ] ) )
			continue;
			
		array[ weapon ] = [];
		array[ weapon ][ "clip_left" ] = self GetWeaponAmmoClip( weapon, "left" );
		array[ weapon ][ "clip_right" ] = self GetWeaponAmmoClip( weapon, "right" );
		array[ weapon ][ "stock" ] = self GetWeaponAmmoStock( weapon );
	}

	if ( !isdefined( scene ) )
		scene = "default";

	self.stored_weapons[ scene ] = [];
	if ( IsDefined( self.unstorable_weapons[ current_weapon ] ) )
	{
		primary_weapons = self GetWeaponsListPrimaries();
		foreach( weapon in primary_weapons )
		{
			if ( !IsDefined( self.unstorable_weapons[ weapon ] ) )
			{
				current_weapon = weapon;
				break;
			}
		}
	}
	self.stored_weapons[ scene ][ "current_weapon" ] = current_weapon;
	self.stored_weapons[ scene ][ "inventory" ] = array;
}

/*
=============
///ScriptDocBegin
"Name: restore_players_weapons( <scene> )"
"Summary: Restore the player's inventory from a variable that you stored earlier."
"Module: Player"
"MandatoryArg: <scene>: String to store the inventory to."
"Example: level.player restore_players_weapons( "c4_scene" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
restore_players_weapons( scene )
{
	if ( !isdefined( scene ) )
		scene = "default";

	if ( !isdefined( self.stored_weapons ) || !isdefined( self.stored_weapons[ scene ] ) )
	{
		PrintLn( "^3Warning! Tried to restore weapons for scene " + scene + " but they weren't stored" );
		return;
	}

	self TakeAllWeapons();
	foreach ( weapon, array in self.stored_weapons[ scene ][ "inventory" ] )
	{
		// We cannot give "alt-fire" weapons
		if ( WeaponInventoryType( weapon ) != "altmode" )
		{
			self GiveWeapon( weapon );
		}

		self SetWeaponAmmoClip( weapon, array[ "clip_left" ], "left" );
		self SetWeaponAmmoClip( weapon, array[ "clip_right" ], "right" );
		self SetWeaponAmmoStock( weapon, array[ "stock" ] );
	}

	current_weapon = self.stored_weapons[ scene ][ "current_weapon" ];
	if ( current_weapon != "none" )
		self SwitchToWeapon( current_weapon );
}

get_storable_weapons_list_all()
{
	weapons = self GetWeaponsListAll();
	
	if ( IsDefined( self.unstorable_weapons ) )
	{
		foreach( weapon in weapons )
		{
			if ( IsDefined( self.unstorable_weapons[ weapon ] ) )
			    weapons = array_remove( weapons, weapon );
		}
	}
		
	return weapons;
}

get_storable_weapons_list_primaries()
{
	primary_weapons = self GetWeaponsListPrimaries();
	
	if ( IsDefined( self.unstorable_weapons ) )
	{
		foreach( weapon in primary_weapons )
		{
			if ( IsDefined( self.unstorable_weapons[ weapon ] ) )
			    primary_weapons = array_remove( primary_weapons, weapon );
		}
	}
		
	return primary_weapons;
}

get_storable_current_weapon_primary()
{
	primary_weapon = self GetCurrentPrimaryWeapon();	
	
	if ( IsDefined( self.unstorable_weapons ) && IsDefined( self.unstorable_weapons[ primary_weapon ] ) )
		primary_weapon = self get_first_storable_weapon();
	
	return primary_weapon;
}

get_storable_current_weapon()
{
	current_weapon = self GetCurrentWeapon();
	
	if ( IsDefined( self.unstorable_weapons ) && IsDefined( self.unstorable_weapons[ current_weapon ] ) )
		current_weapon = self get_first_storable_weapon();

	return current_weapon;
		
}

get_first_storable_weapon()
{	
	primaries = self get_storable_weapons_list_primaries();
	
	if ( primaries.size > 0 )
		weapon = primaries[0];
	else
		weapon = "none";	

	return weapon;	
}

/*
=============
///ScriptDocBegin
"Name: hide_entity()"
"Summary: Used to hide large groups of ents when you are unsure of the exact classname. Will use appropriate functions for turning off triggers, connecting paths in brushmodels, etc. if appropriate."
"Module: Utility"
"CallOn: An entity"
"Example: array_thread( group_of_misc_entities,::hide_entity );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
hide_entity()
{

	switch( self.code_classname )
	{
		case "light_spot":
		case "script_vehicle":
		case "script_model":
			self Hide();
			break;
		case "script_brushmodel":
			self Hide();
			self NotSolid();
			if ( self.spawnflags & 1 )
				self ConnectPaths();
			break;
		case "trigger_radius":
		case "trigger_multiple":
		case "trigger_use":
		case "trigger_use_touch":
		case "trigger_multiple_flag_set":
		case "trigger_multiple_breachIcon":
		case "trigger_multiple_flag_lookat":
		case "trigger_multiple_flag_looking":
			self trigger_off();
			break;
		default:
			AssertMsg( "Unable to hide entity at " + self.origin + ". Need to define a method for handling entities of classname " + self.code_classname );
	}
}

/*
=============
///ScriptDocBegin
"Name: show_entity()"
"Summary: Used to show large groups of hidden ents when you are unsure of the exact classname. Will use appropriate functions for turning off triggers, disconnecting paths in brushmodels, etc. if appropriate."
"Module: Utility"
"CallOn: An entity"
"Example: array_thread( group_of_misc_entities,::show_entity );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
show_entity()
{
	switch( self.code_classname )
	{
		case "light_spot":
		case"script_vehicle":
		case"script_model":
			self Show();
			break;
		case "script_brushmodel":
			self Show();
			self Solid();
			if ( self.spawnflags & 1 )
				self DisconnectPaths();
			break;
		case "trigger_radius":
		case "trigger_multiple":
		case "trigger_use":
		case "trigger_use_touch":
		case "trigger_multiple_flag_set":
		case "trigger_multiple_breachIcon":
		case "trigger_multiple_flag_lookat":
		case "trigger_multiple_flag_looking":
			self trigger_on();
			break;
		default:
			AssertMsg( "Unable to show entity at " + self.origin + ". Need to define a method for handling entities of classname " + self.code_classname );
	}
}
/*
=============
///ScriptDocBegin
"Name: _rotateyaw( <yaw_angle> , <time> , <time> , <time> )"
"Summary: Calls RotateYaw"
"Module: Code Wrappers"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_rotateyaw( yaw_angle, time, acc_time, dec_time )
{
	if ( IsDefined( dec_time ) )
		self RotateYaw( yaw_angle, time, acc_time, dec_time );
	else
	if ( IsDefined( acc_time ) )
		self RotateYaw( yaw_angle, time, acc_time );
	else
		self RotateYaw( yaw_angle, time );
}

/*
=============
///ScriptDocBegin
"Name: set_moveplaybackrate( <rate>, <time>, <also_change_transitionrate> )"
"Summary: set an AI's .moveplaybackrate over time"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <rate>: the speed of the playbackrate ( default rate is 1 )"
"OptionalArg: <time>: time to lerp the rate in - if no time then set instantly"
"OptionalArg: <also_change_transitionrate>: boolean. if true, it also changes the transition rate. Default false."
"Example: level.price set_moveplaybackrate( 1.1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_moveplaybackrate( rate, time, also_change_transitionrate )
{
	self notify( "set_moveplaybackrate" );
	self endon( "set_moveplaybackrate" );

	// [2014/08/15 tfong] For the next project, make this default TRUE
	if ( IsDefined( also_change_transitionrate ) && also_change_transitionrate )
		self thread set_movetransitionrate( rate, time );

	if ( !IsDefined( self.moveplaybackrate_orig ))
		self.moveplaybackrate_orig = self.moveplaybackrate;
	
	if ( IsDefined( time ) )
	{
		range = rate - self.moveplaybackrate;
		interval = .05;
		numcycles = time / interval;
		fraction = range / numcycles;

		while ( abs( rate - self.moveplaybackrate ) > abs( fraction * 1.1 ) )
		{
			self.moveplaybackrate += fraction;
			wait interval;
		}
	}

	self.moveplaybackrate = rate;
}

/*
=============
///ScriptDocBegin
"Name: restore_moveplaybackrate( <time>, <also_change_transitionrate> )"
"Summary: restores an AI's .moveplaybackrate over time, it was previously saved in .moveplaybackrate_orig"
"Module: AI"
"CallOn: AI"
"OptionalArg: <time>: time to lerp the rate in - if no time then set instantly"
"OptionalArg: <also_change_transitionrate>: boolean. if true, it also changes the transition rate. Default false."
"Example: level.price restore_moveplaybackrate( 1.1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
restore_moveplaybackrate( time, also_change_transitionrate )
{
	self notify( "set_moveplaybackrate" );
	self endon( "set_moveplaybackrate" );
		
	// [2014/08/15 tfong] For the next project, make this default TRUE
	if ( IsDefined( also_change_transitionrate ) && also_change_transitionrate )
	{
		// thread off the transition rate
		self thread restore_movetransitionrate( time );
	}
	
	self set_moveplaybackrate( self.moveplaybackrate_orig, time, false );

	self.moveplaybackrate_orig = undefined;
}


/*
=============
///ScriptDocBegin
"Name: set_movetransitionrate( <rate>, <time> )"
"Summary: set an AI's .movetransitionrate over time"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <rate>: the speed of the movetransitionrate ( default rate is 1 )"
"OptionalArg: <time>: time to lerp the rate in - if no time then set instantly"
"Example: level.price set_movetransitionrate( 1.1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_movetransitionrate( rate, time )
{
	self notify( "set_moveplaybackrate" );
	self endon( "set_moveplaybackrate" );
	
	if ( !IsDefined( self.movetransitionrate_orig ))
		self.movetransitionrate_orig = self.movetransitionrate;
	
	if ( IsDefined( time ) )
	{
		range = rate - self.movetransitionrate;
		interval = .05;
		numcycles = time / interval;
		fraction = range / numcycles;

		while ( abs( rate - self.movetransitionrate ) > abs( fraction * 1.1 ) )
		{
			self.movetransitionrate += fraction;
			wait interval;
		}
	}

	self.movetransitionrate = rate;
}


/*
=============
///ScriptDocBegin
"Name: restore_movetransitionrate( <rate>, <time> )"
"Summary: restores an AI's .movetransitionrate over time, it was previously saved in .movetransitionrate_orig"
"Module: AI"
"CallOn: AI"
"MandatoryArg: <rate>: the speed of the playbackrate ( default rate is 1 )"
"OptionalArg: <time>: time to lerp the rate in - if no time then set instantly"
"Example: level.price restore_movetransitionrate( 1.1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
restore_movetransitionrate( time )
{
	self notify( "set_moveplaybackrate" );
	self endon( "set_moveplaybackrate" );
		
	set_movetransitionrate( self.movetransitionrate_orig, time );

	self.movetransitionrate_orig = undefined;
}

/*
=============
///ScriptDocBegin
"Name: array_spawn_function( <array> , <func>, param1, param2, param3, param4 )"
"Summary: Adds a spawn function to an array of spawners"
"Module: Utility"
"MandatoryArg: <array>: Array of spawners"
"MandatoryArg: <func>: Function to run on the guy when he spawns"
"OptionalArg: <param1> : An optional parameter."
"OptionalArg: <param2> : An optional parameter."
"OptionalArg: <param3> : An optional parameter."
"OptionalArg: <param4> : An optional parameter."
"Example: array_spawn_function( spawners, ::do_it, do_it_parameter );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_spawn_function( array, func, param1, param2, param3, param4 )
{
	AssertEx( IsDefined( array ), "That isn't an array!" );
	AssertEx( IsArray( array ), "That isn't an array!" );
	AssertEx( array.size, "That array is empty!" );
	foreach ( spawner in array )
	{
		AssertEx( IsSpawner( spawner ), "This isn't a spawner!" );
		spawner thread add_spawn_function( func, param1, param2, param3, param4 );
	}
}

/*
=============
///ScriptDocBegin
"Name: array_spawn_function_targetname( <key> , <func>, param1, param2, param3, param4 )"
"Summary: Gets spawners with the specified targetname and adds a spawn function to them"
"Module: Utility"
"MandatoryArg: <key>: targetname of spawners"
"MandatoryArg: <func>: Function to run on the guys when they spawn"
"OptionalArg: <param1> : An optional parameter."
"OptionalArg: <param2> : An optional parameter."
"OptionalArg: <param3> : An optional parameter."
"OptionalArg: <param4> : An optional parameter."
"Example: array_spawn_function_targetname( "specialguy", ::do_it );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_spawn_function_targetname( key, func, param1, param2, param3, param4 )
{
	array = GetEntArray( key, "targetname" );
	array_spawn_function( array, func, param1, param2, param3, param4 );
}


/*
=============
///ScriptDocBegin
"Name: array_spawn_function_noteworthy( <key> , <func>, param1, param2, param3, param4 )"
"Summary: Gets spawners with the specified noteworthy and adds a spawn function to them"
"Module: Utility"
"MandatoryArg: <key>: noteworthy of spawners"
"MandatoryArg: <func>: Function to run on the guys when they spawn"
"OptionalArg: <param1> : An optional parameter."
"OptionalArg: <param2> : An optional parameter."
"OptionalArg: <param3> : An optional parameter."
"OptionalArg: <param4> : An optional parameter."
"Example: array_spawn_function_noteworthy( "specialguy", ::do_it );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_spawn_function_noteworthy( key, func, param1, param2, param3, param4 )
{
	array = GetEntArray( key, "script_noteworthy" );
	array_spawn_function( array, func, param1, param2, param3, param4 );
}




/*
=============
///ScriptDocBegin
"Name: enable_dontevershoot( <enable_dontevershoot> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_dontevershoot()
{

/*
=============
///ScriptFieldDocBegin
"Name: .dontEverShoot"
"Summary: dontEverShoot"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

	self.dontEverShoot = true;
}

/*
=============
///ScriptDocBegin
"Name: disable_dontevershoot( <disable_dontevershoot> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_dontevershoot()
{
	self.dontEverShoot = undefined;
}

/*
=============
///ScriptDocBegin
"Name: create_sunflare_setting( <sunflare name> )"
"Summary: Stores sunflare settings that get executed automatically with trigger_multiple_sunflare"
"Module: Utility"
"MandatoryArg: <sunflare name>: Name, must be unique to other sunflare names. "
"Example: ent = create_sunflare_setting( "shipg_bottom" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
create_sunflare_setting( sunflare_name )
{
	if ( !isdefined( level.sunflare_settings ) )
		level.sunflare_settings = [];
	ent = SpawnStruct();
	ent.name = sunflare_name;

	level.sunflare_settings[ sunflare_name ] = ent;
	return ent;
}

/*
=============
///ScriptDocBegin
"Name: create_vision_set_fog( <fogset> )"
"Summary: Stores fog settings that get executed automatically with trigger_multiple_visionset"
"Module: Utility"
"MandatoryArg: <fogset>: Name, must be the same as the vision set it works with. "
"Example: ent = create_vision_set_fog( "gulag" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
create_vision_set_fog( fogset )
{
	if ( !isdefined( level.vision_set_fog ) )
		level.vision_set_fog = [];
	ent = SpawnStruct();
	ent.name = fogset;

	
/*  // Use HDRColorIntensity/HDRSunColorIntensity existence to determine the space of the color values.
 
    // Special init for variables that may not exist on every set of fog yet -- add variable defaults here to avoid IsDefined checks everywhere later on	
	ent.HDRColorIntensity 			= 1;
	ent.HDRSunColorIntensity 		= 1;
*/
	ent.skyFogIntensity				= 0;
	ent.skyFogMinAngle 				= 0;
	ent.skyFogMaxAngle 				= 0;
	ent.heightFogEnabled			= 0;
	ent.heightFogBaseHeight			= 0;
	ent.heightFogHalfPlaneDistance	= 1000;

	level.vision_set_fog[ ToLower(fogset) ] = ent;
	return ent;
}

/*
=============
///ScriptDocBegin
"Name: get_vision_set_fog( <fogset> )"
"Summary: Get vision set fog defined by create_vision_set_fog( fogset ). Returns Undefined if doesn't exist."
"Module: Utility"
"MandatoryArg: <fogset>: Unique vision set name"
"Example: ent = get_vision_set_fog( "contingency_bridge" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_vision_set_fog( fogset )
{
	if ( !isdefined( level.vision_set_fog ) )
		level.vision_set_fog = [];

	ent = level.vision_set_fog[ ToLower(fogset) ];
	if ( using_hdr_fog() && isdefined( ent ) && isdefined( ent.HDROverride ) )
		ent = level.vision_set_fog[ ToLower(ent.HDROverride) ];

	//assertex( IsDefined( ent ), "visiont set fog: " + fogset + "does not exist, use create_vision_set_fog( " + fogset + " ) in your level_fog.gsc." );
	return ent;
}


/*
=============
///ScriptDocBegin
"Name: create_fog( <fogset> )"
"Summary: Stores fog settings that get executed automatically with trigger_multiple_fog"
"Module: Utility"
"MandatoryArg: <fogset>: Unique fog setting name"
"Example: ent = create_fog( "contingency_bridge" );"
"NoteLine: Here are some values you might set on the ent:"
"NoteLine: ent.startDist"
"NoteLine: ent.halfwayDist"
"NoteLine: ent.red"
"NoteLine: ent.green"
"NoteLine: ent.blue"
"NoteLine: ent.maxOpacity"
"NoteLine: ent.sunRed"
"NoteLine: ent.sunGreen"
"NoteLine: ent.sunBlue"
"NoteLine: ent.sunDir"
"NoteLine: ent.sunBeginFadeAngle"
"NoteLine: ent.sunEndFadeAngle"
"NoteLine: ent.normalFogScale"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

create_fog( fogset )
{
	if ( !isdefined( level.fog_set ) )
		level.fog_set = [];
	ent = SpawnStruct();
	ent.name = fogset;

	level.fog_set[ ToLower(fogset) ] = ent;
	return ent;
}

/*
=============
///ScriptDocBegin
"Name: get_fog( <fogset> )"
"Summary: Get fog defined by create_fog( fogset ). Returns Undefined if doesn't exist."
"Module: Utility"
"MandatoryArg: <fogset>: Unique fog setting name"
"Example: ent = get_fog( "contingency_fog_forest" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_fog( fogset )
{
	if ( !isdefined( level.fog_set ) )
		level.fog_set = [];

	ent = level.fog_set[ ToLower(fogset) ];
	//assertex( IsDefined( ent ), "fog set: " + fogset + "does not exist, use create_fog( " + fogset + " ) in your level_fog.gsc." );
	return ent;
}


init_self_fog_transition()
{
	if ( !IsDefined( self.fog_transition_ent ) )
	{
		self.fog_transition_ent = SpawnStruct();
		self.fog_transition_ent.fogset = "";
		self.fog_transition_ent.time = 0;
	}
}


using_hdr_fog()
{
	if ( !IsDefined( level.console ) )
		set_console_status();
	AssertEx( IsDefined( level.console ) && IsDefined( level.xb3 ) && IsDefined( level.ps4 ), "Expected platform defines to be complete." );

	return is_gen4();
}


/*
=============
///ScriptDocBegin
"Name: fog_set_changes( <fog_set> , <transition_time> )"
"Summary: Switches to a fog set over a transition time."
"Module: Utility"
"Call on: The Player"
"MandatoryArg: <fog_set>: The fog set name, defined by create_fog( name )"
"MandatoryArg: <transition_time>: How long to transition for"
"Example: level.player fog_set_changes( "contingency_fog_forest", 10 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
fog_set_changes( fog_set, transition_time )
{
	if ( !isPlayer( self ) )
		maps\_art::init_fog_transition();
	else
		init_self_fog_transition();
	
	if ( !isdefined( level.fog_set ) )
		level.fog_set = [];

	ent = level.fog_set[ ToLower(fog_set) ];
	if ( !isdefined( ent ) )
	{
		AssertEx( IsDefined( level.vision_set_fog ), "Fog set:" + fog_set + " does not exist, use create_fog( " + fog_set + " ) or create_vision_set_fog( " + fog_set + " ); in your /createart/level_fog.gsc" );
		ent = level.vision_set_fog[ ToLower(fog_set) ];
	}

	if ( isdefined(ent) && isdefined( ent.HDROverride ) && using_hdr_fog() )
	{
		if ( isdefined( level.fog_set[ ToLower(ent.HDROverride) ] ) )
			ent = level.fog_set[ ToLower(ent.HDROverride) ];
		else if ( isdefined( level.vision_set_fog ) )
			ent = level.vision_set_fog[ ToLower(ent.HDROverride) ];
	}

	AssertEx( IsDefined( ent ), "Fog set:" + fog_set + " does not exist, use create_fog( " + fog_set + " ) or create_vision_set_fog( " + fog_set + " ); in your /createart/level_fog.gsc" );

	//if ( !isdefined( ent ) )
	//	return;

	// transition time override
	if ( !isdefined( transition_time ) )
		transition_time = ent.transitiontime;
	AssertEx( IsDefined( transition_time ), "Fog set: " + fog_set + " does not have a transition_time defined and a time was not specified in the function call." );
/$
	if(  GetDvarInt( "scr_art_tweak") != 0 )
	{
		translateEntTosliders(ent );
		//pending some mechanism to handle transition while you're editing, just set to zero for now since it leaves less room for ugly.
		transition_time = 0;
	}
$/
	if ( !isPlayer( self ) )
	{
		set_fog_to_ent_values( ent, transition_time  );

		level.fog_transition_ent.fogset = fog_set;
		level.fog_transition_ent.time = transition_time;
	}
	else
	{
		// this the same fog set we're already doing?
		if ( fog_set != "" && self.fog_transition_ent.fogset == fog_set && self.fog_transition_ent.time == transition_time )
			return;

		self set_fog_to_ent_values( ent, transition_time  );

		self.fog_transition_ent.fogset = fog_set;
		self.fog_transition_ent.time = transition_time;
	}
}
/$
translateEntTosliders(ent)
{
	ConvertLegacyFog( ent );
	SetDevDvar( "scr_fog_exp_halfplane",			ent.halfwayDist );
	SetDevDvar( "scr_fog_nearplane",				ent.startDist);
	SetDevDvar( "scr_fog_color", 					( ent.red, ent.green, ent.blue ) );
	SetDevDvar( "scr_fog_color_intensity",			ent.HDRColorIntensity );
	SetDevDvar( "scr_fog_max_opacity", 				ent.maxOpacity );
	SetDevDvar( "scr_skyFogIntensity", 				ent.skyFogIntensity );
	SetDevDvar( "scr_skyFogMinAngle", 				ent.skyFogMinAngle );
	SetDevDvar( "scr_skyFogMaxAngle", 				ent.skyFogMaxAngle );
	SetDevDvar( "scr_heightFogEnabled",				ent.heightFogEnabled );
	SetDevDvar( "scr_heightFogBaseHeight",			ent.heightFogBaseHeight );
	SetDevDvar( "scr_heightFogHalfPlaneDistance",	ent.heightFogHalfPlaneDistance );

	if ( IsDefined( ent.sunFogEnabled ) && ent.sunFogEnabled)
	{
		SetDevDvar( "scr_sunFogEnabled", 1 );
		SetDevDvar( "scr_sunFogColor", (ent.sunRed, ent.sunGreen,ent.sunBlue) );
		SetDevDvar( "scr_sunFogColorIntensity",	ent.HDRSunColorIntensity );
		SetDevDvar( "scr_sunFogDir", ent.sunDir );
		SetDevDvar( "scr_sunFogBeginFadeAngle", ent.sunBeginFadeAngle );
		SetDevDvar( "scr_sunFogEndFadeAngle", ent.sunEndFadeAngle );
		SetDevDvar( "scr_sunFogScale", ent.normalFogScale );
	}
	else
	{
		SetDevDvar( "scr_sunFogEnabled", 0 );
	}

	if ( IsDefined( ent.atmosFogEnabled ) )
	{
		AssertEx( IsDefined( ent.atmosFogSunFogColor ) );
		AssertEx( IsDefined( ent.atmosFogHazeColor ) );
		AssertEx( IsDefined( ent.atmosFogHazeStrength ) );
		AssertEx( IsDefined( ent.atmosFogHazeSpread ) );
		AssertEx( IsDefined( ent.atmosFogExtinctionStrength ) );
		AssertEx( IsDefined( ent.atmosFogInScatterStrength ) );
		AssertEx( IsDefined( ent.atmosFogHalfPlaneDistance ) );
		AssertEx( IsDefined( ent.atmosFogStartDistance ) );
		AssertEx( IsDefined( ent.atmosFogDistanceScale ) );
		AssertEx( IsDefined( ent.atmosFogSkyDistance ) );		
		AssertEx( IsDefined( ent.atmosFogSkyAngularFalloffEnabled ) );	
		AssertEx( IsDefined( ent.atmosFogSkyFalloffStartAngle ) );
		AssertEx( IsDefined( ent.atmosFogSkyFalloffAngleRange ) );
		AssertEx( IsDefined( ent.atmosFogSunDirection ) );
		AssertEx( IsDefined( ent.atmosFogHeightFogEnabled ) );
		AssertEx( IsDefined( ent.atmosFogHeightFogBaseHeight ) );
		AssertEx( IsDefined( ent.atmosFogHeightFogHalfPlaneDistance ) );

		SetDevDvar( "scr_atmosFogEnabled",						ent.atmosFogEnabled );
		SetDevDvar( "scr_atmosFogSunFogColor",					ent.atmosFogSunFogColor );
		SetDevDvar( "scr_atmosFogHazeColor",					ent.atmosFogHazeColor );
		SetDevDvar( "scr_atmosFogHazeStrength",					ent.atmosFogHazeStrength );
		SetDevDvar( "scr_atmosFogHazeSpread",					ent.atmosFogHazeSpread );
		SetDevDvar( "scr_atmosFogExtinctionStrength",			ent.atmosFogExtinctionStrength );
		SetDevDvar( "scr_atmosFogInScatterStrength",			ent.atmosFogInScatterStrength );
		SetDevDvar( "scr_atmosFogHalfPlaneDistance",			ent.atmosFogHalfPlaneDistance );
		SetDevDvar( "scr_atmosFogStartDistance",				ent.atmosFogStartDistance );
		SetDevDvar( "scr_atmosFogDistanceScale",				ent.atmosFogDistanceScale );
		SetDevDvar( "scr_atmosFogSkyDistance",					int( ent.atmosFogSkyDistance ) );
		SetDevDvar( "scr_atmosFogSkyAngularFalloffEnabled",		ent.atmosFogSkyAngularFalloffEnabled );
		SetDevDvar( "scr_atmosFogSkyFalloffStartAngle",			ent.atmosFogSkyFalloffStartAngle );
		SetDevDvar( "scr_atmosFogSkyFalloffAngleRange",			ent.atmosFogSkyFalloffAngleRange );
		SetDevDvar( "scr_atmosFogSunDirection",					ent.atmosFogSunDirection );
		SetDevDvar( "scr_atmosFogHeightFogEnabled",				ent.atmosFogHeightFogEnabled );
		SetDevDvar( "scr_atmosFogHeightFogBaseHeight",			ent.atmosFogHeightFogBaseHeight );
		SetDevDvar( "scr_atmosFogHeightFogHalfPlaneDistance",	ent.atmosFogHeightFogHalfPlaneDistance );
	}
	else
	{
		SetDevDvar( "scr_atmosFogEnabled",	false );
	}
}
$/

/*
=============
///ScriptDocBegin
"Name: vision_set_fog_changes( <vision_set> , <transition_time> )"
"Summary: Switches to a vision set over a transition time and changes fog, safely overwriting other calls to the same function. "
"Module: Utility"
"Call on: The Player"
"MandatoryArg: <vision_set>: The vision set file"
"MandatoryArg: <transition_time>: How long to transition for"
"Example: level.player vision_set_fog_changes( "gulag_flying", 10 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vision_set_fog_changes( vision_set, transition_time )
{
	do_fog = vision_set_changes( vision_set, transition_time );
	if ( do_fog )
	{
		if ( level.currentgen && IsDefined( get_vision_set_fog( vision_set + "_cg" ) ) )
		    fog_set_changes( vision_set + "_cg", transition_time );
		else if ( IsDefined( get_vision_set_fog( vision_set ) ) )
			fog_set_changes( vision_set, transition_time );
		else
			ClearFog( transition_time );
	}
}

init_self_visionset()
{
	if ( !IsDefined( self.vision_set_transition_ent ) )
	{
		self.vision_set_transition_ent = SpawnStruct();
		self.vision_set_transition_ent.vision_set = "";
		self.vision_set_transition_ent.time = 0;
	}
}

/*
=============
///ScriptDocBegin
"Name: vision_set_changes( <vision_set> , <transition_time> )"
"Summary: Switches to a vision set over a transition time, safely overwriting other calls to the same function. "
"Notes: Client side triggers are disabled if the vision_set is not empty and enabled if it is empty"
"Module: Utility"
"MandatoryArg: <vision_set>: The vision set file"
"MandatoryArg: <transition_time>: How long to transition for"
"Example: vision_set_changes( "gulag_flying", 10 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vision_set_changes( vision_set, transition_time )
{
	if ( !IsPlayer( self ) )
	{
		vision_set_initd = true;
		if ( !isdefined( level.vision_set_transition_ent ) )
		{
			level.vision_set_transition_ent = SpawnStruct();
			level.vision_set_transition_ent.vision_set = "";
			level.vision_set_transition_ent.time = 0;
			vision_set_initd = false;
		}

		// this the same vision set we're already doing?
		if ( vision_set != "" && level.vision_set_transition_ent.vision_set == vision_set && level.vision_set_transition_ent.time == transition_time )
		{
			return false;// no fog
		}

		level.vision_set_transition_ent.vision_set = vision_set;
		level.vision_set_transition_ent.time = transition_time;

		if( vision_set_initd && ( GetDvarInt( "scr_art_tweak") != 0 ) )
		{
/#
			transition_time = 0;
			VisionSetNaked( vision_set, transition_time );
			level.player openpopupmenu("dev_vision_exec");
			wait( 0.05 );
			level.player closepopupmenu();
	#/
		}
		else
		{
			VisionSetNaked( vision_set, transition_time );
		}

		//iprintlnbold( vision_set );
		level.lvl_visionset = vision_set;
		SetDvar( "vision_set_current", vision_set );
	}
	else
	{
		self init_self_visionset();

		// this the same vision set we're already doing?
		if ( vision_set != "" && self.vision_set_transition_ent.vision_set == vision_set && self.vision_set_transition_ent.time == transition_time )
			return false;// no fog

		self.vision_set_transition_ent.vision_set = vision_set;
		self.vision_set_transition_ent.time = transition_time;

		self VisionSetNakedForPlayer( vision_set, transition_time );
	}

	return true;// do fog
}


/*
=============
///ScriptDocBegin
"Name: enable_teamflashbangImmunity( <enable_teamflashbangImmunity> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_teamflashbangImmunity()
{
	self thread enable_teamflashbangImmunity_proc();
}

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

	while ( 1 )
	{
		self.teamFlashbangImmunity = true;
		wait .05;
	}
}

/*
=============
///ScriptDocBegin
"Name: disable_teamflashbangImmunity( <disable_teamflashbangImmunity> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_teamflashbangImmunity()
{
	self.teamFlashbangImmunity = undefined;
}

/*
=============
///ScriptDocBegin
"Name: _radiusdamage( <origin> , <range> , <maxdamage> , <mindamage> , <attacker> )"
"Summary: Calls RadiusDamage"
"Module: Code Wrappers"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: pointer = ::RadiusDamage"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
_radiusdamage( origin, range, maxdamage, mindamage, attacker )
{
	if ( !isdefined( attacker ) )
		RadiusDamage( origin, range, maxdamage, mindamage );
	else
		RadiusDamage( origin, range, maxdamage, mindamage, attacker );
}


/*
=============
///ScriptDocBegin
"Name: mask_destructibles_in_volumes( <volumes> )"
"Summary: Masks destructibles in an array of volumes so they dont use entities. Must be done before _load.""
"Module: Utility"
"MandatoryArg: <volumes>: Array of volumes "
"Example: mask_destructibles_in_volumes( vols );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
mask_destructibles_in_volumes( volumes )
{
	destructible_toy = GetEntArray( "destructible_toy", "targetname" );
	destructible_vehicles = GetEntArray( "destructible_vehicle", "targetname" );
	
	combined_destructibles = array_combine( destructible_toy, destructible_vehicles );
	
	foreach ( volume in volumes )
	{
		volume.destructibles = [];
	}

	foreach ( toy in combined_destructibles )
	{
		foreach ( volume in volumes )
		{
			if ( !volume IsTouching( toy ) )
				continue;

			volume put_toy_in_volume( toy );
			break;
		}
	}
}

InteractiveKeypairs()
{
	interactiveKeypairs = [];
	interactiveKeypairs[0] = [ "interactive_birds", "targetname" ];
	interactiveKeypairs[1] = [ "interactive_vulture", "targetname" ];
	interactiveKeypairs[2] = [ "interactive_fish", "script_noteworthy" ];
	return interactiveKeypairs;
}

/*
=============
///ScriptDocBegin
"Name: mask_interactives_in_volumes( <volumes> )"
"Summary: Masks interactives in an array of volumes so they dont use entities. Can be used before or after _load but only after level_precache::main().  Does not affect sccript_structs."
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <volumes>: Array of volumes."
"Example: mask_interactives_in_volumes( vols );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

mask_interactives_in_volumes( volumes )
{
	// Note: This function only works for interactives of types that have a ::saveToStructFn defined, and that are listed in InteractiveKeypairs().
	interactiveKeypairs = InteractiveKeypairs();
	combinedInteractives = [];
	foreach ( interactiveKeypair in interactiveKeypairs ) {
		moreInteractives = GetEntArray( interactiveKeypair[0], interactiveKeypair[1] );
		combinedInteractives = array_combine( combinedInteractives, moreInteractives );
	}
	foreach ( ent in combinedInteractives )
	{
		/# 
		if ( IsDefined( ent.script_noteworthy ) ) ent_type = ent.script_noteworthy;
		else ent_type = ent.targetname;
		AssertEx( IsDefined( ent.interactive_type ), ent_type + " entity does not have an interactive_type keypair." );
		AssertEx( IsDefined( level._interactive ), ent_type + " entity in map, but level._interactive is not defined." );
		AssertEx( IsDefined( level._interactive[ ent.interactive_type ] ), ( ent_type + " entity in map, but level._interactive[" + ent.interactive_type + "] is not defined." ) );
		#/
		if ( !isDefined( level._interactive[ ent.interactive_type ].saveToStructFn ) )
			continue;
		foreach ( volume in volumes ) {
			if ( !volume IsTouching( ent ) )
				continue;
			if ( !IsDefined( volume.interactives ) )
				volume.interactives = [];
			volume.interactives[ volume.interactives.size ] = ent [[ level._interactive[ ent.interactive_type ].saveToStructFn ]]();
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: activate_interactives_in_volume()"
"Summary: Activate interactives in a masked volume.  Also activates interactives that have been placed as script_structs in Radiant."
"Module: Utility"
"CallOn: A volume, usually a trigger_multiple_fx_volume"
"Example: vol activate_interactives_in_volume();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

activate_interactives_in_volume()
{
/* I implemented support for script_structs in addition to script_models.  However, they make things more complicated and 
 * Nate tells me that as long as ents are deleted before _load they don't matter anyway.  So here is the script, commented out.
 * 
	// Activate interactives that are currently script_structs, then those that are stored in self.interactives.
	interactiveKeypairs = InteractiveKeypairs();
	combinedInteractives = [];
	foreach ( interactiveKeypair in interactiveKeypairs ) {
		moreInteractives = getstructarray( interactiveKeypair[0], interactiveKeypair[1] );	// nb This only works after _load
		combinedInteractives = array_combine( combinedInteractives, moreInteractives );
	}
	tempEnt = Spawn( "script_model", (0,0,0) );
	foreach ( interactive in combinedInteractives )
	{
		tempEnt.origin = interactive.origin;
		if ( !self IsTouching( tempEnt ) ) continue;
		if ( !IsDefined( interactive.model ) ) interactive.model = interactive.script_modelname;
		if ( !IsDefined( interactive.angles ) ) interactive.angles = (0,0,0);
		interactive [[ level._interactive[ interactive.interactive_type ].loadFromStructFn ]]();	
		interactive = undefined;
	}
	tempEnt Delete();
*/
	if ( !isdefined( self.interactives ) )
		return;

	foreach ( stored_int in self.interactives )
	{
		stored_int [[ level._interactive[ stored_int.interactive_type ].loadFromStructFn ]]();
	}
	self.interactives = undefined;
}

/*
=============
///ScriptDocBegin
"Name: delete_interactives_in_volumes( <volumes> )"
"Summary: Deletes interactives within an array of volumes.  Only affects ents, not script_structs."
"Module: Utility"
"MandatoryArg: <volumes>: Array of volumes "
"Example: delete_interactives_in_volumes( vols );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

delete_interactives_in_volumes( volumes )
{
	mask_interactives_in_volumes( volumes );
	foreach ( volume in volumes )
	{
		volume.interactives = undefined;
	}
}

/*
=============
///ScriptDocBegin
"Name: mask_exploders_in_volume( <volumes> )"
"Summary: Masks exploders in an array of volumes so they dont use entities. Must be done before _load.""
"Module: Utility"
"MandatoryArg: <volumes>: Array of volumes "
"Example: mask_exploders_in_volume( vols );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

mask_exploders_in_volume( volumes )
{
    if( GetDvar( "createfx" ) != "" )
        return;
	// Hide exploder models.
	ents = GetEntArray( "script_brushmodel", "classname" );
	smodels = GetEntArray( "script_model", "classname" );
	for ( i = 0; i < smodels.size; i++ )
		ents[ ents.size ] = smodels[ i ];

    foreach( volume in volumes )
    	foreach ( ent in ents )
    	{
    		if ( IsDefined( ent.script_prefab_exploder ) )
    			ent.script_exploder = ent.script_prefab_exploder;
    
    		if ( !IsDefined( ent.script_exploder ) )
    		    continue;
            
            if ( !IsDefined( ent.model ) )
                continue;
                
            if ( ent.code_classname != "script_model" )
                continue;
            
            if ( !ent IsTouching( volume ) )
                continue;
                
            ent.masked_exploder = true;
    	}
}


/*
=============
///ScriptDocBegin
"Name: activate_exploders_in_volume()"
"Summary: Activates masked exploders in a volume"
"Module: Utility"
"CallOn: A masking volume"
"Example: volume activate_exploders_in_volume();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

activate_exploders_in_volume()
{
    test_org = Spawn( "script_origin", ( 0, 0, 0 ) );
    
	foreach ( EntFx in level.createfxent )
	{
	    if ( !IsDefined( EntFx.v[ "masked_exploder" ] ) )
	        continue;
        test_org.origin = EntFx.v[ "origin" ];
        test_org.angles = EntFx.v[ "angles" ];

        if( !test_org IsTouching( self ) )
            continue;
        
	    model_name = EntFx.v[ "masked_exploder" ];
	    spawnflags = EntFx.v[ "masked_exploder_spawnflags" ];
	    disconnect_paths = EntFX.v[ "masked_exploder_script_disconnectpaths" ];
	    new_ent = Spawn( "script_model", ( 0, 0, 0 ), spawnflags );
	    new_ent SetModel( model_name );
	    new_ent.origin = EntFx.v[ "origin" ];
	    new_ent.angles = EntFx.v[ "angles" ];
	    Entfx.v[ "masked_exploder" ]  = undefined;
	    Entfx.v[ "masked_exploder_spawnflags" ]  = undefined;
	    Entfx.v[ "masked_exploder_script_disconnectpaths" ]  = undefined;
	    new_ent.disconnect_paths = disconnect_paths;
	    new_ent.script_exploder = EntFx.v[ "exploder" ];
	    common_scripts\_exploder::setup_individual_exploder( new_ent );
	    EntFX.model = new_ent;
	}
	test_org delete();
}



/*
=============
///ScriptDocBegin
"Name: precache_destructible( <destructible_type> )"
"Summary: precaches a destructible. Useful for when you want to spawn a destructible at runtime."
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <destructible_type>: destructible to precache "
"Example:  toy precache_destructible( toy.destructible_type );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
precache_destructible( destructible_type )
{
    infoIndex = common_scripts\_destructible::destructible_getInfoIndex( destructible_type );

    // if it's already been created dont create it again
    if ( infoIndex != -1 )
        return;
        
    if ( !IsDefined( level.destructible_functions ) )
    	level.destructible_functions = [];
    
        
	struct = SpawnStruct();
	struct.destructibleInfo = self common_scripts\_destructible::destructible_getType( destructible_type );
	struct thread common_scripts\_destructible::precache_destructibles();
	struct thread common_scripts\_destructible::add_destructible_fx();
}

/*
=============
///ScriptDocBegin
"Name: delete_destructibles_in_volumes( <volumes> )"
"Summary: deletes destructibles in an array of volumes so they dont use entities."
"Module: Utility"
"MandatoryArg: <volumes>: Array of volumes "
"Example: delete_destructibles_in_volumes( vols );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

delete_destructibles_in_volumes( volumes, dodelayed )
{
	foreach ( volume in volumes )
		volume.destructibles = [];
	names = [ "destructible_toy", "destructible_vehicle" ];
	incs = 0;
	
	if ( !IsDefined( dodelayed ) )
		dodelayed = false;
	foreach( name in names )
	{
		destructible_toy = GetEntArray( name, "targetname" );
		foreach ( toy in destructible_toy )
		{
			foreach ( volume in volumes )
			{
				if( dodelayed )
				{
					incs++;
					incs %= 5;
					if( incs == 1 )
						wait 0.05;
				}
				if ( !volume IsTouching( toy ) )
					continue;
				toy delete();
				break;
			}
		}
	}
	


	
}

/*
=============
///ScriptDocBegin
"Name: delete_exploders_in_volumes( <volumes> )"
"Summary: Delete exploders in volumes so they don't use entities."
"Module: Utility"
"MandatoryArg: <volumes>: Array of volumes to delete entities in"
"Example: delete_exploders_in_volumes( volumes );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

delete_exploders_in_volumes( volumes, dodelayed  )
{
	ents = GetEntArray( "script_brushmodel", "classname" );
	smodels = GetEntArray( "script_model", "classname" );
	for ( i = 0; i < smodels.size; i++ )
		ents[ ents.size ] = smodels[ i ];
		
    delete_ents = [];
    
    test_org = Spawn( "script_origin", ( 0, 0, 0 ) );

	incs = 0;
	if ( !IsDefined( dodelayed ) )
		dodelayed = false;


    foreach( volume in volumes )
    	foreach ( ent in ents )
    	{
    		if ( !IsDefined( ent.script_exploder ) )
    		    continue;
            test_org.origin = ent GetOrigin();
            if( !volume IsTouching( test_org ) )
                continue;
            delete_ents[ delete_ents.size ]  = ent;
    	}
    	
	array_delete( delete_ents );
    
    test_org Delete();
}
/*
=============
///ScriptDocBegin
"Name: activate_destructibles_in_volume()"
"Summary: Activate destructibles in a masked volume."
"Module: Utility"
"CallOn: A mask volume"
"Example: vol activate_destructibles_in_volume();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
activate_destructibles_in_volume()
{
	if ( !isdefined( self.destructibles ) )
		return;

	foreach ( ent in self.destructibles )
	{
		toy = Spawn( "script_model", ( 0, 0, 0 ) );

		toy SetModel( ent.toy_model );
		toy.origin = ent.origin;
		toy.angles = ent.angles;
		toy.script_noteworthy = ent.script_noteworthy;
		toy.targetname = ent.targetname;
		toy.target = ent.target;
		toy.script_linkto = ent.script_linkto;
		toy.destructible_type = ent.destructible_type;
		toy.script_noflip = ent.script_noflip;

		toy common_scripts\_destructible::setup_destructibles( true );// true meaning don't precache
	}

	self.destructibles = [];
}

/*
=============
///ScriptDocBegin
"Name: setFlashbangImmunity( <immune> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
setFlashbangImmunity( immune )
{
	self.flashBangImmunity = immune;
}


/*
=============
///ScriptDocBegin
"Name: flashBangGetTimeLeftSec()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"Example: duration = flashBangGetTimeLeftSec();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
flashBangGetTimeLeftSec()
{
	Assert( IsDefined( self ) );
	Assert( IsDefined( self.flashEndTime ) );

	// duration is the flash time left
	durationMs = self.flashEndTime - GetTime();
	if ( durationMs < 0 )
		return 0;

	// return the duration in seconds
	return( durationMs * 0.001 );
}


/*
=============
///ScriptDocBegin
"Name: flashBangIsActive()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"Example: active = flashBangIsActive();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
flashBangIsActive()
{
	return( flashBangGetTimeLeftSec() > 0 );
}


/*
=============
///ScriptDocBegin
"Name: flashBangStart( <duration> )"
"Summary: I'm guessing it makes a guy get flash banged, maybe somebody could FILL IN THE FREAKING DOCS"
"Module: Entity"
"CallOn: An entity"
"Example: actor flashBangStart( 4.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
flashBangStart( duration )
{
	Assert( IsDefined( self ) );
	Assert( IsDefined( duration ) );
/*
=============
///ScriptFieldDocBegin
"Name: flashBangImmunity"
"Summary: flashBangImmunity"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
	if ( IsDefined( self.flashBangImmunity ) && self.flashbangImmunity )
		return;

	// If we're alreayd flashing, update the time to the one that would last the longest
	newFlashEndTime = GetTime() + ( duration * 1000.0 );

	if ( IsDefined( self.flashendtime ) )
		self.flashEndTime = max( self.flashendtime, newFlashEndTime );
	else
		self.flashendtime = newFlashEndTime;

	self notify( "flashed" );
	self SetFlashBanged( true );
}

/*
=============
///ScriptDocBegin
"Name: waittill_volume_dead()"
"Summary: Waits for all the guys in a volume to die."
"Module: Utility"
"CallOn: A volume"
"Example: volume waittill_volume_dead()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_volume_dead()
{
	for ( ;; )
	{
		ai = GetAISpeciesArray( "axis", "all" );

		found_guy = false;
		foreach ( guy in ai )
		{
			if ( !isalive( guy ) )
				continue;
			if ( guy IsTouching( self ) )
			{
				found_guy = true;
				break;
			}

			// check all the time, 4 guys per frame
			wait( 0.0125 );
		}
		if ( !found_guy )
		{
			// do one more complete check
			aHostiles = self get_ai_touching_volume( "axis" );
			if ( !aHostiles.size )
				break;
		}
		wait( 0.05 );

		//aHostiles = self get_ai_touching_volume( "axis" );
		//if ( !aHostiles.size )
		//	break;
		//wait(
		//waittill_dead( aHostiles );
	}
}

/*
=============
///ScriptDocBegin
"Name: waittill_volume_dead_or_dying()"
"Summary: Waits for all the guys in a volume to die or do long death."
"Module: Utility"
"CallOn: A volume"
"Example: volume waittill_volume_dead_or_dying()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_volume_dead_or_dying()
{
	ever_found_guy = false;
	for ( ;; )
	{
		ai = GetAISpeciesArray( "axis", "all" );

		found_guy = false;
		foreach ( guy in ai )
		{
			if ( !isalive( guy ) )
				continue;
			if ( guy IsTouching( self ) )
			{
				if ( guy doingLongDeath() )
					continue;

				found_guy = true;
				ever_found_guy = true;
				break;
			}

			// check all the time, 4 guys per frame
			wait( 0.0125 );
		}
		if ( !found_guy )
		{
			// do one more complete check
			aHostiles = self get_ai_touching_volume( "axis" );
			if ( !aHostiles.size )
			{
				break;
			}
			else
			{
				ever_found_guy = true;
			}
		}
		wait( 0.05 );

		//aHostiles = self get_ai_touching_volume( "axis" );
		//if ( !aHostiles.size )
		//	break;
		//wait(
		//waittill_dead( aHostiles );
	}
	return ever_found_guy;
}

/*
=============
///ScriptDocBegin
"Name: waittill_volume_dead_then_set_flag( <sFlag> )"
"Summary: Read the function name."
"Module: Utility"
"CallOn: A volume"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: volume waittill_volume_dead_then_set_flag( "some_flag" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_volume_dead_then_set_flag( sFlag )
{
	self waittill_volume_dead();
	flag_set( sFlag );
}

/*
=============
///ScriptDocBegin
"Name: waittill_targetname_volume_dead_then_set_flag( <targetname> , <msg> )"
"Summary: Read the function name."
"Module: Utility"
"MandatoryArg: <targetname>: The targetname "
"OptionalArg: <msg>: The flag "
"Example: waittill_targetname_volume_dead_then_set_flag( "some_targetname", "some_flag" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
waittill_targetname_volume_dead_then_set_flag( targetname, msg )
{
	volume = GetEnt( targetname, "targetname" );
	AssertEx( IsDefined( volume ), "No volume for targetname " + targetname );
	volume waittill_volume_dead_then_set_flag( msg );
}

/*
=============
///ScriptDocBegin
"Name: player_can_be_shot()"
"Summary: The player gets correct attackeraccuracy again."
"Module: Utility"
"Example: player_can_be_shot();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_can_be_shot()
{
	level.player ent_flag_clear( "player_zero_attacker_accuracy" );
	level.player.IgnoreRandomBulletDamage = false;
	level.player maps\_gameskill::update_player_attacker_accuracy();
}

/*
=============
///ScriptDocBegin
"Name: player_cant_be_shot()"
"Summary: The player gets 0 attackeraccuracy and won't get his attackeraccuracy overwritten by _gameskill."
"Module: Utility"
"Example: player_cant_be_shot();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_cant_be_shot()
{
	level.player ent_flag_set( "player_zero_attacker_accuracy" );
	level.player.attackeraccuracy = 0;
	level.player.IgnoreRandomBulletDamage = true;
}

/*
=============
///ScriptDocBegin
"Name: set_player_attacker_accuracy( <val> )"
"Summary: Sets the player's attacker accuracy correctly so _gameskill won't overwrite it"
"Module: Utility"
"CallOn: A player"
"MandatoryArg: <val>: The attacker accuracy"
"Example: level.player set_player_attacker_accuracy( 0.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_player_attacker_accuracy( val )
{
	// sets player attackeraccuracy
	player = get_player_from_self();
	player.gs.player_attacker_accuracy = val;
	player maps\_gameskill::update_player_attacker_accuracy();
}

/*
=============
///ScriptDocBegin
"Name: array_index_by_parameters( <array> )"
"Summary: Indexes and array by its script_parameter values and returns the array"
"Module: Utility"
"MandatoryArg: <array>: The array of entities that all have script_parameters "
"Example: orgs = array_index_by_parameters( friendly_escape_orgs );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_index_by_parameters( old_array )
{
	array = [];
	foreach ( item in old_array )
	{
		array[ item.script_parameters ] = item;
	}
	return array;
}

/*
=============
///ScriptDocBegin
"Name: array_index_by_classname( <array> )"
"Summary: Indexes and array by its classname values and returns the array"
"Module: Utility"
"MandatoryArg: <array>: The array of entities that all have classnames "
"Example: orgs = array_index_by_classname( friendly_escape_orgs );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_index_by_classname( old_array )
{
	array = [];
	foreach ( item in old_array )
	{
		array[ item.classname ] = item;
	}
	return array;
}

/*
=============
///ScriptDocBegin
"Name: array_index_by_script_index( <array> )"
"Summary: Indexes and array by its script_index values and returns the array"
"Module: Utility"
"MandatoryArg: <array>: The array of entities that all have script_indexs "
"Example: orgs = array_index_by_script_index( friendly_escape_orgs );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
array_index_by_script_index( array )
{
	// sort ents by order of script_index
	newarray = [];
	foreach ( ent in array )
	{
		index = ent.script_index;
		if ( IsDefined( index ) )
		{
			AssertEx( !isdefined( newarray[ index ] ), "Multiple ents had the same script_index of " + index );

			newarray[ index ] = ent;
		}
	}

	return newarray;
}



/*
=============
///ScriptDocBegin
"Name: add_target_pivot()"
"Summary: Adds a .pivot to an entity, from the entity's .target."
"Module: Utility"
"CallOn: An entity that is going to rotate around a pivot point."
"OptionalArg: <ent>: Optional override ent"
"Example: evac_rock add_target_pivot();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_target_pivot( ent )
{
	if ( IsDefined( ent ) )
	{
		self.pivot = ent;
	}
	else
	{
		AssertEx( IsDefined( self.target ), "Tried to add pivot to an entity but it has no target." );
		self.pivot = GetEnt( self.target, "targetname" );
		AssertEx( IsDefined( self.pivot ), "Tried to add pivot but there was no pivot entity. Must be a script mover, like a script_origin not script_struct." );
	}

	self LinkTo( self.pivot );
}





/*
=============
///ScriptDocBegin
"Name: flashBangStop()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"Example: actor flashBangStop();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
flashBangStop()
{
	self.flashendtime = undefined;
	self SetFlashBanged( false );
}

/*
=============
///ScriptDocBegin
"Name: getent_or_struct( param1, param2 )"
"Summary: "
"Module: Entity"
"MandatoryArg: <param1>: first param of a key/value pair, such as 'self.target'"
"MandatoryArg: <param2>: second param of a key/value pair, such as 'targetname'"
"Example: nextNode = getent_or_struct( self.target, "targetname" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getent_or_struct( param1, param2 )
{
	ent = GetEnt( param1, param2 );
	if ( IsDefined( ent ) )
		return ent;
	return getstruct( param1, param2 );
}


grenade_earthQuake()
{
	// grenades die when they explode so we wait until the end of the frame before killing the thread.
	// not doing so would kill the thread before the fx could happen.
	self thread endOnDeath();
	self endon( "end_explode" );

	self waittill( "explode", position );

	dirt_on_screen_from_position( position );
}

endOnDeath()
{
	self waittill( "death" );
	waittillframeend;
	self notify( "end_explode" );
}

/*
=============
///ScriptDocBegin
"Name: dirt_on_screen_from_position( <position> )"
"Summary: Self explanatory"
"Module: Utility"
"MandatoryArg: <position>: origin of explosion"
"Example: dirt_on_screen_from_position( from_on_high );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
dirt_on_screen_from_position( position )
{
	PlayRumbleOnPosition( "grenade_rumble", position );
	Earthquake( 0.3, 0.5, position, 400 );

	foreach ( player in level.players )
	{
		if ( Distance( position, player.origin ) > 600 )
			continue;

		if ( player DamageConeTrace( position ) )
			player thread dirtEffect( position );
	}
}

/*
=============
///ScriptDocBegin
"Name: player_rides_shotgun_in_humvee( <right> , <left> , <up> , <down> )"
"Summary: Makes the player pop into the humvee's shotgun seat. Returns the org used to dismount the player"
"Module: Utility"
"CallOn: The humvee"
"OptionalArg: <directions> FOV limits for the player "
"Example: org = humvee player_rides_shotgun_in_humvee( 45,45,45,45 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_rides_shotgun_in_humvee( right, left, up, down )
{
	return self player_rides_in_humvee( "shotgun", level.player, right, left, up, down );
}

player_rides_in_humvee( seat, rider, right, left, up, down )
{
	if ( !isdefined( rider ) )
		rider = level.player;
	assert( isplayer( rider ) );

	rider AllowCrouch( false );
	rider AllowProne( false );
	rider DisableWeapons();

	org = spawn_tag_origin();
	org LinkTo( self, "tag_passenger", player_rides_in_humvee_offset( seat ), ( 0, 0, 0 ) );
	org.player_dismount = spawn_tag_origin();
	org.player_dismount LinkTo( self, "tag_body", player_rides_humvee_offset_dismount( seat ), ( 0, 0, 0 ) );

	if ( !isdefined( right ) )
		right = 90;
	if ( !isdefined( left ) )
		left = 90;
	if ( !isdefined( up ) )
		up = 40;
	if ( !isdefined( down ) )
		down = 40;

	rider DisableWeapons();
	rider PlayerLinkTo( org, "tag_origin", 0.8, right, left, up, down );
	rider.humvee_org = org;

	return org;
}

player_rides_in_humvee_offset( seat )
{
	switch( seat )
	{
		case "shotgun" :	return ( -5, 10, -34 );
		case "backleft" :	return ( -45, 45, -34 );
		case "backright" :	return ( -45, 5, -34 );
	}

	assertmsg( "No offset for " + seat + " was available in player_rides_in_humvee_offset()" );
}

player_rides_humvee_offset_dismount( seat )
{
	switch( seat )
	{
		case "shotgun" :	return ( -8, -90, -12.6 );
		case "backleft" :	return ( -58, 85, -12.6 );
		case "backright" :	return ( -58, -95, -12.6 );
	}

	assertmsg( "No offset for " + seat + " was available in player_rides_humvee_offset_dismount()" );
}

player_leaves_humvee( skip_offset )
{
	if ( !isdefined( skip_offset ) )
		skip_offset = false;

	// Backwards compatibility calling on the org itself rather than the player.
	org = self;
	rider = level.player;
	if ( isplayer( self ) )
	{
		rider = self;
		org = rider.humvee_org;
	}

	org Unlink();
	if ( !skip_offset )
	{
		move_time = 0.6;
		org MoveTo( org.player_dismount.origin, move_time, move_time * 0.5, move_time * 0.5 );
		wait( move_time );
	}

	rider Unlink();
	rider EnableWeapons();
	rider AllowCrouch( true );
	rider AllowProne( true );
	rider.humvee_org = undefined;

	org.player_dismount Delete();
	org Delete();
}

/*
=============
///ScriptDocBegin
"Name: dirtEffect( <position> )"
"Summary: plays a grenade dirt effect on the screen"
"Module: Player"
"CallOn: player"
"MandatoryArg: <position>: the position of the event that causes the effect"
"Example: level.player dirtEffect( fx.origin );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
dirtEffect( position, attacker )
{
	sides = screen_effect_sides( position );

	foreach ( type, _ in sides )
	{
		self thread maps\_gameskill::grenade_dirt_on_screen( type );
	}
}

bloodsplateffect( position )
{
	if ( !IsDefined( self.damageAttacker ) )
	{
		return;
	}

	sides = screen_effect_sides( self.damageAttacker.origin );

	foreach ( type, _ in sides )
	{
		self thread maps\_gameskill::blood_splat_on_screen( type );
	}
}

screen_effect_sides( position )
{
	forwardVec = VectorNormalize( AnglesToForward( self.angles ) );
	rightVec = VectorNormalize( AnglesToRight( self.angles ) );
	entVec = VectorNormalize( position - self.origin );

	fDot = VectorDot( entVec, forwardVec );
	rDot = VectorDot( entVec, rightVec );

	sides = [];
	curWeapon = self GetCurrentWeapon();
	if ( fDot > 0 && fDot > 0.5 && WeaponType( curWeapon ) != "riotshield" )
	{
		sides[ "bottom" ] = true;
	}

	if ( abs( fDot ) < 0.866 )
	{
		if ( rDot > 0 )
		{
			sides[ "right" ] = true;
		}
		else
		{
			sides[ "left" ] = true;
		}
	}

	return sides;
}

/*
=============
///ScriptDocBegin
"Name: pathrandompercent_set( <value> )"
"Summary: sets self.pathrandompercent to the value"
"Module: AI"
"CallOn: An AI"
"MandatoryArg: <value>: the value of self.pathrandompercent"
"Example: level.price pathrandompercent_set( 0 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
pathrandompercent_set( value )
{
	if ( !isdefined( self.old_pathrandompercent ) )
		self.old_pathrandompercent = self.pathrandompercent;
	self.pathrandompercent = value;
}

/*
=============
///ScriptDocBegin
"Name: pathrandompercent_zero()"
"Summary: sets self.pathrandompercent to 0"
"Module: AI"
"CallOn: An AI"
"Example: level.price pathrandompercent_zero();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
pathrandompercent_zero()
{
	if ( IsDefined( self.old_pathrandompercent ) )
		return;

	self.old_pathrandompercent = self.pathrandompercent;
	self.pathrandompercent = 0;
}

/*
=============
///ScriptDocBegin
"Name: pathrandompercent_reset()"
"Summary: resets self.pathrandompercent ( see pathrandompercent_zero )"
"Module: AI"
"CallOn: An AI"
"Example: level.price pathrandompercent_reset();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
pathrandompercent_reset()
{
	Assert( IsDefined( self.old_pathrandompercent ) );

	self.pathrandompercent = self.old_pathrandompercent;
	self.old_pathrandompercent = undefined;
}

/*
=============
///ScriptDocBegin
"Name: walkdist_zero()"
"Summary: sets self.walkdist and self.old_walkDistFacingMotion to 0"
"Module: AI"
"CallOn: An AI"
"Example: level.price walkdist_zero();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
walkdist_zero()
{
	if ( IsDefined( self.old_walkDistFacingMotion ) )
		return;

	self.old_walkDist 				 = self.walkDist;
	self.old_walkDistFacingMotion 	 = self.walkDistFacingMotion;

	self.walkdist 				 = 0;
	self.walkDistFacingMotion 	 = 0;
}



/*
=============
///ScriptDocBegin
"Name: walkdist_force_walk()"
"Summary: sets self.walkdist and self.old_walkDistFacingMotion to a high enough number so it forces walking and supresses running"
"Module: AI"
"CallOn: An AI"
"Example: level.price walkdist_force_walk();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
walkdist_force_walk()
{
	if ( !IsDefined( self.old_walkDistFacingMotion ) )
	{
		self.old_walkDist 				 = self.walkDist;
		self.old_walkDistFacingMotion 	 = self.walkDistFacingMotion;
	}

	self.walkdist 				 = 999999999;
	self.walkDistFacingMotion 	 = 999999999;
}



/*
=============
///ScriptDocBegin
"Name: is_walkdist_override()"
"Summary: checks to see if self.walkdist and self.old_walkDistFacingMotion is altered"
"Module: AI"
"CallOn: An AI"
"Example: level.price is_walkdist_override();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_walkdist_override()
{
	return ( IsDefined( self.old_walkDistFacingMotion ) || IsDefined( self.old_walkDist ) );
}


/*
=============
///ScriptDocBegin
"Name: walkdist_reset()"
"Summary: resets self.walkdist and self.old_walkDistFacingMotion ( see walkdist_zero )"
"Module: AI"
"CallOn: An AI"
"Example: level.price walkdist_reset();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
walkdist_reset()
{
	Assert( IsDefined( self.old_walkDist ) );
	Assert( IsDefined( self.old_walkDistFacingMotion ) );

	self.walkdist 				 = self.old_walkDist;
	self.walkDistFacingMotion 	 = self.old_walkDistFacingMotion;

	self.old_walkDist 				 = undefined;
	self.old_walkDistFacingMotion 	 = undefined;
}

/*
=============
///ScriptDocBegin
"Name: enable_ignorerandombulletdamage_drone()"
"Summary: effectively has the same effect as .IgnoreRandomBulletDamage on AI, but for drones"
"Module: Entity"
"CallOn: An a drone"
"Example: guy enable_ignorerandombulletdamage_drone()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_ignorerandombulletdamage_drone()
{
	//threading internally so script doesn't stop here...most our enable/disable utility code doesn't stop script.
	self thread ignorerandombulletdamage_drone_proc();
}

ignorerandombulletdamage_drone_proc()
{
	AssertEx( !IsSentient( self ), "AI tried to run enable_ignorerandombulletdamage_drone" );

	self endon( "disable_ignorerandombulletdamage_drone" );
	self endon( "death" );

	//just an easy variable we can check for debugging - doesn't actually do anything in code.
	self.IgnoreRandomBulletDamage = true;
	self.fakehealth = self.health;
	self.health = 1000000;

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

		if ( !isplayer( attacker ) && IsSentient( attacker ) )
		{
			if ( IsDefined( attacker.enemy ) && attacker.enemy != self )
				continue;
		}

		self.fakehealth -= damage;

		if ( self.fakehealth <= 0 )
			break;
	}

	self Kill();
}

/*
=============
///ScriptDocBegin
"Name: set_brakes( <num> )"
"Summary: Sets the brakes on the vehicle"
"Module: Utility"
"CallOn: A vehicle"
"MandatoryArg: <num>: The brakes, 0 to 1. "
"Example: vehicle set_brakes( 0.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_brakes( num )
{
	self.veh_brake = num;
}


/*
=============
///ScriptDocBegin
"Name: disable_ignorerandombulletdamage_drone()"
"Summary: stops ignoring random bullet damage"
"Module: Entity"
"CallOn: An a drone"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_ignorerandombulletdamage_drone()
{
	if ( !isalive( self ) )
		return;
	if ( !isdefined( self.IgnoreRandomBulletDamage ) )
		return;

	self notify( "disable_ignorerandombulletdamage_drone" );
	self.IgnoreRandomBulletDamage = undefined;
	self.health = self.fakehealth;
}

timeOutEnt( timeOut )
{
    ent = SpawnStruct();
    ent delayThread( timeOut, ::send_notify, "timeout" );
    return ent;
}



/*
=============
///ScriptDocBegin
"Name: delayThread( <delay>, <function>, <arg1>, <arg2>, <arg3>, <arg4>, <arg5>, <arg6> )"
"Summary: Delaythread is cool! It saves you from having to write extra script for once off commands. Note you don?t have to thread it off. Delaythread is that smart!"
"Module: Utility"
"MandatoryArg: <delay> : The delay before the function occurs"
"MandatoryArg: <delay> : The function to run."
"OptionalArg: <arg1> : parameter 1 to pass to the process"
"OptionalArg: <arg2> : parameter 2 to pass to the process"
"OptionalArg: <arg3> : parameter 3 to pass to the process"
"OptionalArg: <arg4> : parameter 4 to pass to the process"
"OptionalArg: <arg5> : parameter 5 to pass to the process"
"OptionalArg: <arg6> : parameter 6 to pass to the process"
"Example: delayThread( 3, ::flag_set, "player_can_rappel" );
"SPMP: both"
///ScriptDocEnd
=============
*/
delayThread( timer, func, param1, param2, param3, param4, param5, param6 )
{
	// to thread it off
	thread delayThread_proc( func, timer, param1, param2, param3, param4, param5, param6 );
}

delayChildThread( timer, func, param1, param2, param3, param4, param5, param6 )
{
	// to thread it off
	childthread delaychildThread_proc( func, timer, param1, param2, param3, param4, param5, param6 );
}

/*
=============
///ScriptDocBegin
"Name: flagWaitThread( <flag>, <function>, <arg1>, <arg2>, <arg3>, <arg4>, <arg5>, <arg6> )"
"Summary: flagWaitThread waits for a flag set to run a function. Note if you want an extra wait after the flag send an array -- flag = [ "flag_name", 2 ]"
"Module: Utility"
"MandatoryArg: <flg> : flag to wait for before the function occurs -- send as an array to add a wait -- [0] = flag, [1] = wait time"
"MandatoryArg: <func> : The function to run."
"OptionalArg: <arg1> : parameter 1 to pass to the process"
"OptionalArg: <arg2> : parameter 2 to pass to the process"
"OptionalArg: <arg3> : parameter 3 to pass to the process"
"OptionalArg: <arg4> : parameter 4 to pass to the process"
"OptionalArg: <arg5> : parameter 5 to pass to the process"
"OptionalArg: <arg6> : parameter 6 to pass to the process"
"Example: level.ally flagWaitThread( "flag_begin_move", ::follow_path, goal_node );
"SPMP: both"
///ScriptDocEnd
=============
*/
flagWaitThread( flag, func, param1, param2, param3, param4, param5, param6 )
{
	// notify flag_stop_flagWaitThread to stop thread ex: level.ally notify( "flag_begin_move_stop_flagWaitThread" )
	self endon( "death" );
	
	// init flag array.. [0] = the flag, [1] = an aditional optional timed wait after the flag
	if( !IsArray( flag ) )
		flag = [ flag, 0 ];
	
	// to thread it off
	self thread flagWaitThread_proc( func, flag, param1, param2, param3, param4, param5, param6 );
}

/*
=============
///ScriptDocBegin
"Name: waittillThread( <note>, <function>, <arg1>, <arg2>, <arg3>, <arg4>, <arg5>, <arg6> )"
"Summary: waittillThread waits for a notify to run a function. Note if you want an extra wait after the notify send an array -- note = [ "notify_name", 2 ]"
"Module: Utility"
"MandatoryArg: <note> : notify to wait for before the function occurs -- send as an array to add a wait -- [0] = notify, [1] = wait time"
"MandatoryArg: <func> : The function to run."
"OptionalArg: <arg1> : parameter 1 to pass to the process"
"OptionalArg: <arg2> : parameter 2 to pass to the process"
"OptionalArg: <arg3> : parameter 3 to pass to the process"
"OptionalArg: <arg4> : parameter 4 to pass to the process"
"OptionalArg: <arg5> : parameter 5 to pass to the process"
"OptionalArg: <arg6> : parameter 6 to pass to the process"
"Example: level.ally waittillThread( "entered_building", ::enable_cqbwalk );
"SPMP: both"
///ScriptDocEnd
=============
*/
waittillThread( note, func, param1, param2, param3, param4, param5, param6 )
{
	// notify note_stop_waittillThread to stop thread ex: level.ally notify( "entered_building_stop_waittillThread" )
	self endon( "death" );
	
	// init note array.. [0] = the notify, [1] = an aditional optional timed wait after the notify
	if( !IsArray( note ) )
		note = [note, 0 ];
	
	// to thread it off
	self thread waittillThread_proc( func, note, param1, param2, param3, param4, param5, param6 );
}


/*
=============
///ScriptDocBegin
"Name: enable_danger_react( <duration_seconds> )"
"Summary: if nearby teammate is shot, makes AI stop at current position, find nearby cover if available, and fight."
"Module: AI"
"CallOn: An AI"
"MandatoryArg: <duration_seconds>: how long to stop and fight"
"Example: self enable_danger_react( 10 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_danger_react( duration )
{
	duration *= 1000;

	Assert( IsAI( self ) );
	self.doDangerReact = true;
	self.dangerReactDuration = duration;
	self.neverSprintForVariation = undefined;
}


/*
=============
///ScriptDocBegin
"Name: disable_danger_react()"
"Summary: disable danger reaction. See enable_danger_react"
"Module: AI"
"CallOn: An AI"
"Example: self disable_danger_react();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
disable_danger_react()
{
	Assert( IsAI( self ) );
	self.doDangerReact = false;
	self.neverSprintForVariation = true;
}


/*
=============
///ScriptDocBegin
"Name: set_group_advance_to_enemy_parameters( <interval> <group size> )"
"Summary: sets how often and group size for AI advancing directly toward their enemy"
"Module: AI"
"CallOn: level"
"MandatoryArg: <interval millisec>: how often"
"MandatoryArg: <group size>: how many"
"Example: self set_group_advance_to_enemy_parameters( 30000, 5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_group_advance_to_enemy_parameters( interval, group_size )
{
	level.advanceToEnemyInterval = interval;		// how often AI will try to run directly to their enemy if the enemy is not visible
	level.advanceToEnemyGroupMax = group_size;		// group size for AI running to their enemy
}


/*
=============
///ScriptDocBegin
"Name: reset_group_advance_to_enemy_timer( <team> )"
"Summary: reset the advance-to-enemy time for a team. Useful when player advances to a new section of a level"
"Module: AI"
"CallOn: level"
"MandatoryArg: <team>: team to reset the timer for"
"Example: self reset_group_advance_to_enemy_timer( "team3" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
reset_group_advance_to_enemy_timer( team )
{
	Assert( IsDefined( level.lastAdvanceToEnemyTime[ team ] ) );
	level.lastAdvanceToEnemyTime[ team ] = GetTime();
}

/*
=============
///ScriptDocBegin
"Name: set_custom_gameskill_func( <func> )"
"Summary: Sets a custom func to run at the end of _gameskill::setskill(), so you can change difficulty settings."
"Module: Utility"
"MandatoryArg: <func>: The function to call after _gameskill::setskill()"
"Example: set_custom_gameskill_func( maps\_gameskill::solo_player_in_coop_gameskill_settings );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_custom_gameskill_func( func )
{
	Assert( IsDefined( func ) );
	level.custom_gameskill_func = func;

	// reset so it takes effect
	thread maps\_gameskill::resetSkill();
}

/*
=============
///ScriptDocBegin
"Name: clear_custom_gameskill_func()"
"Summary: Sets a custom func to run at the end of _gameskill::setskill(), so you can change difficulty settings."
"Module: Utility"
"MandatoryArg: <func>: The function to call after _gameskill::setskill()"
"Example: set_custom_gameskill_func( maps\_gameskill::solo_player_in_coop_gameskill_settings );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
clear_custom_gameskill_func()
{
	level.custom_gameskill_func = undefined;

	// reset so it takes effect
	thread maps\_gameskill::resetSkill();
}

/*
=============
///ScriptDocBegin
"Name: set_wind( <weight>, <rate>, <variance> )"
"Summary: sets the blend weight,rate on the strong wind animation for animated trees. defaults at 1 and .4."
"Module: Level"
"CallOn: The Level"
"MandatoryArg: <weight>: the weight of the animation"
"MandatoryArg: <rate>: the weight of the animation"
"OptionalArg: <variance>: How much random speed is added to the rate for each loop of the animation. "
"Example: set_wind( 100,6 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_wind( weight, rate, variance )
{
	Assert( IsDefined( weight ) );
	Assert( IsDefined( rate ) );
	maps\_animatedmodels::init_wind_if_uninitialized();
	if ( IsDefined( variance ) )
		level.wind.variance = variance;
	level.wind.rate = rate;
	level.wind.weight = weight;
	level notify( "windchange", "strong" );
}

/*
=============
///ScriptDocBegin
"Name: string_is_single_digit_integer( <str> )"
"Summary: Tests whether a string is a single digit integer. Helpful for situations like parsing strings for indexes, etc."
"Module: Utility"
"CallOn: the level"
"MandatoryArg: <str>: the string you want to test."
"Example: bIsInt = string_is_single_digit_integer( "1" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
string_is_single_digit_integer( str )
{
	if ( str.size > 1 )
	{
		return false;
	}

	arr = [];
	arr[ "0" ] = true;
	arr[ "1" ] = true;
	arr[ "2" ] = true;
	arr[ "3" ] = true;
	arr[ "4" ] = true;
	arr[ "5" ] = true;
	arr[ "6" ] = true;
	arr[ "7" ] = true;
	arr[ "8" ] = true;
	arr[ "9" ] = true;

	if ( IsDefined( arr[ str ] ) )
	{
		return true;
	}

	return false;
}


set_battlechatter_variable( team, val )
{
	// used internally
	level.battlechatter[ team ] = val;
	update_battlechatter_hud();
}

/*
=============
///ScriptDocBegin
"Name: objective_clearAdditionalPositions( <objective_number> )"
"Summary: Clears the positions for this objective"
"Module: Utility"
"MandatoryArg: <objective_number>: The index for the objective"
"Example: objective_clearAdditionalPositions( objective_index );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
objective_clearAdditionalPositions( objective_number )
{
	// can have up to 8 positions, so clear them all
	for ( i = 0; i < 8; i++ )
	{
		objective_additionalposition( objective_number, i, (0,0,0) );
	}
}


/*
=============
///ScriptDocBegin
"Name: get_minutes_and_seconds( <milliseconds> )"
"Summary: Returns how many minutes and seconds are in the milliseconds provided. Returns array with index "minutes" and index "seconds"."
"Module: Utility"
"CallOn: the level"
"MandatoryArg: <milliseconds>: Number of milliseconds to check"
"Example: time = get_minutes_and_seconds( GetTime() ); minutes = time[ "minutes" ]; seconds = time[ "seconds" ];"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_minutes_and_seconds( milliseconds )
{
	time = [];
	time[ "minutes" ] = 0;
	time[ "seconds" ] = Int( milliseconds / 1000 );

	while ( time[ "seconds" ] >= 60 )
	{
		time[ "minutes" ]++;
		time[ "seconds" ] -= 60;
	}
	if ( time[ "seconds" ] < 10 )
		time[ "seconds" ] = "0" + time[ "seconds" ];

	return time;
}

/*
=============
///ScriptDocBegin
"Name: player_has_weapon( <weap> )"
"Summary: Returns true if the player has the wepaon."
"Module: Utility"
"MandatoryArg: <weap>: The weapon"
"Example: if ( player_has_weapon( "riotshield" ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_has_weapon( weap )
{
	weaponList = level.player GetWeaponsListPrimaries();
	foreach ( weapon in weaponList )
	{
		if ( weapon == weap )
			return true;
	}
	return false;
}

/*
=============
///ScriptDocBegin
"Name: obj( <msg> )"
"Summary: A simple script macro for translating objective strings into indices"
"Module: Utility"
"MandatoryArg: <msg>: The string to refer to the objective"
"Example: Objective_Add( obj( "exfil" ), "current", &"ROADKILL_OBJECTIVE_REPORT", struct.origin );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
obj( msg )
{
	if ( !isdefined( level.obj_array ) )
	{
		level.obj_array = [];
	}

	if ( !isdefined( level.obj_array[ msg ] ) )
	{
		level.obj_array[ msg ] = level.obj_array.size + 1; // cause objs start at 1 not 0
	}

	return level.obj_array[ msg ];
}


/*
=============
///ScriptDocBegin
"Name: obj_exists( <msg> )"
"Summary: Checks to see if an objective id has been generated for the passed string."
"Module: Utility"
"MandatoryArg: <msg>: The string to refer to the objective"
"Example: if ( obj_exists( "hug_makarov ) ) {}"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

obj_exists( msg )
{
	return IsDefined( level.obj_array ) && IsDefined( level.obj_array[ msg ] );
}

/*
=============
///ScriptDocBegin
"Name: player_mount_vehicle( <vehicle> )"
"Summary: Makes player drive the specified vehicle"
"Module: Player"
"CallOn: A player"
"MandatoryArg: <vehicle>: the vehicle for the player to ride"
"Example: level.player player_mount_vehicle( snowmobile );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_mount_vehicle( vehicle )
{
	assert( isplayer( self ) );
	self MountVehicle( vehicle );
	self.drivingVehicle = vehicle;
}

/*
=============
///ScriptDocBegin
"Name: player_dismount_vehicle()"
"Summary: Makes player stop driving the vehicle"
"Module: Player"
"CallOn: A player"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
player_dismount_vehicle()
{
	assert( isplayer( self ) );
	self DismountVehicle();
	self.drivingVehicle = undefined;
}


/*
=============
///ScriptDocBegin
"Name: graph_position( <v> , <min_x> , <min_y> , <max_x> , <max_y> )"
"Summary: Input graph parameters and variable, returns the result."
"Module: Utility"
"MandatoryArg: <v>: The variable to plot on the graph"
"MandatoryArg: <min_x>: The first x value on the graph"
"MandatoryArg: <min_y>: The first y value on the graph"
"MandatoryArg: <max_x>: The last x value on the graph"
"MandatoryArg: <max_y>: The last y value on the graph"
"Example: x = graph_position( val, 0, 0, 1, 1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
graph_position( v, min_x, min_y, max_x, max_y )
{
	rise = max_y - min_y;
	run = max_x - min_x;
	assertex( run != 0, "max and min x must be different, or you havent defined any graph space." );
	slope = rise / run;

	v -= max_x;

	v = slope * v;

	v += max_y;

	return v;
}

/*
=============
"Name: enable_achievement_harder_they_fall()"
"Summary: enables this AI's death to count towards the achievement THE HARDER THEY FALL"
"Module: AI"
"CallOn: An AI"
"Example: repeller enable_achievement_harder_they_fall()"
"SPMP: singleplayer"
=============
*/
enable_achievement_harder_they_fall()
{
	self.rappeller = true;
}

/*
=============
"Name: disable_achievement_harder_they_fall()"
"Summary: disables this AI's death to count towards the achievement THE HARDER THEY FALL"
"Module: AI"
"CallOn: An AI"
"Example: repeller disable_achievement_harder_they_fall()"
"SPMP: singleplayer"
=============
*/
disable_achievement_harder_they_fall()
{
	self.rappeller = undefined;
}

enable_achievement_harder_they_fall_guy( guy )
{
	guy enable_achievement_harder_they_fall();
}

disable_achievement_harder_they_fall_guy( guy )
{
	guy disable_achievement_harder_they_fall();
}

/*
=============
///ScriptDocBegin
"Name: musicLength( <alias> )"
"Summary: Returns the length of a music alias, from main/sound/soundlength.csv, which is auto generated on convert."
"Module: Utility"
"MandatoryArg: <alias>: The music alias"
"Example: time = musicLength( "gulag_ending_music" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
musicLength( alias )
{
	time = TableLookup( "sound/soundlength.csv", 0, alias, 1 );

	if ( !isdefined( time ) || ( time == "" ) )
	{
		assertmsg( "No time stored in sound/soundlength.csv for " + alias + "." );
		return -1;
	}

	time = int( time );
	assertex( time > 0, "Music alias " + alias + " had zero time." );
	time *= 0.001;
	return time;
}

/*
=============
///ScriptDocBegin
"Name: is_command_bound( <cmd> )"
"Summary: Returns the amount of keys bound to the given command."
"Module: Utility"
"MandatoryArg: <cmd>: The command to check if it is bound, ie: "+attack", "+toggleads_throw""
"Example: if ( is_command_bound( "+toggleads_throw" ) )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_command_bound( cmd )
{
	binding = GetKeyBinding( cmd );
	return binding[ "count" ];
}


/*
=============
///ScriptDocBegin
"Name: linear_interpolate( <percentage> , <min_value> , <max_value> )"
"Summary: Calculates the linear interpolation result based on the current percentage and min and max values. Typically uses a value of 0 to 1 for the percentage, but this is not required."
"Module: Utility"
"MandatoryArg: <percentage>: Generally a value from 0 to 1. 0 will return min, 1 will return max, anything in between will return ( percentage * ( max - min ) )."
"MandatoryArg: <min_value>: Minimum value to return"
"MandatoryArg: <max_value>: Maximum value to return"
"Example: move_dist = linear_interpolate( time_passed / time_total, 0, 1000 )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
linear_interpolate( percentage, min_value, max_value )
{
	assert( isdefined( percentage ) );
	assert( isdefined( min_value ) );
	assert( isdefined( max_value ) );

	max_adjustment = max_value - min_value;
	real_adjustment = percentage * max_adjustment;
	final_value = min_value + real_adjustment;

	return final_value;
}

/*
=============
///ScriptDocBegin
"Name: define_loadout( <levelname> )"
"Summary: for use in test map situation where we want to borrow another levels loadout"
"Module: Utility"
"MandatoryArg: <levelname>: name of level loadout"
"Example: define_loadout( "london" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

define_loadout( levelname )
{
    level.loadout = levelname;
}

/*
=============
///ScriptDocBegin
"Name: template_level( <levelname> )"
"Summary: Used as a generic template override when a test level needs to borrow it's root levels name"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <levelname>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

template_level( levelname )
{
	define_loadout( levelname );
	level.template_script = levelname;
}

/*
=============
///ScriptDocBegin
"Name: template_so_level( <levelname> )"
"Summary: Used as a generic template override for SO Mission level where it's root levels name is needed"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <levelname>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

template_so_level( levelname )
{
	// audio script expects base level name
	level.audio_stringtable_mapname = levelname;
}

/*
=============
///ScriptDocBegin
"Name: fx_volume_pause_noteworthy( <noteworthy> )"
"Summary: Pauses the effects in a trigger_multiple_fx_volume with the specified script_noteworthy key"
"Module: Utility"
"CallOn: An entity"
"MandatoryArg: <noteworthy>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

fx_volume_pause_noteworthy( noteworthy, dodelayed )
{
    thread fx_volume_pause_noteworthy_thread( noteworthy, dodelayed );
}

fx_volume_pause_noteworthy_thread( noteworthy, dodelayed )
{
    volume = GetEnt( noteworthy, "script_noteworthy" );
    volume notify ( "new_volume_command" );
    volume endon ( "new_volume_command" );
    wait 0.05;
    fx_volume_pause( volume, dodelayed );
    
}

fx_volume_pause( volume, dodelayed )
{
    Assert( IsDefined( volume ) );
    volume.fx_paused = true;
    if ( !IsDefined( dodelayed ) )
    	dodelayed = false;
    	
    if( dodelayed )
	    array_thread_mod_delayed( volume.fx, ::pauseeffect );
    else
	    array_thread( volume.fx, ::pauseeffect );
}

array_thread_mod_delayed( array, threadname, mod )
{
	inc = 0;
	if ( !IsDefined( mod ) )
		mod = 5;	
		
	send_array = [];
	foreach( object in array )
	{
		send_array[ send_array.size ] = object;
		inc ++;
		inc %= mod;
		if( mod == 0 )
		{
			array_thread( send_array, threadname );
			wait 0.05;
			send_array = [];
		}
	}
}

/*
=============
///ScriptDocBegin
"Name: fx_volume_restart_noteworthy( <noteworthy> )"
"Summary: used for restarting all the createFX effects contained in a trigger_multiple_fx_volume"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <noteworthy>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

fx_volume_restart_noteworthy( noteworthy )
{
    thread fx_volume_restart_noteworthy_thread( noteworthy );
}

fx_volume_restart_noteworthy_thread( noteworthy )
{
    volume = GetEnt( noteworthy, "script_noteworthy" );
    volume notify ( "new_volume_command" );
    volume endon ( "new_volume_command" );
    
    wait 0.05;
    if( ! IsDefined( volume.fx_paused ) )
        return;
    volume.fx_paused = undefined;
    
    fx_volume_restart( volume );
}

fx_volume_restart( volume )
{
    Assert( IsDefined( volume ) );
    array_thread( volume.fx, ::restartEffect );
}


flag_count_increment( msg )
{
    AssertEx( flag_exist( msg ), "Attempt to increment flag before calling flag_init: " + msg );
    if ( !IsDefined( level.flag_count ) )
        level.flag_count = [];
    if ( ! IsDefined( level.flag_count[ msg ] ) )
        level.flag_count[ msg ]   = 1;
    else
        level.flag_count[ msg ]++;
}

flag_count_decrement( msg )
{
    AssertEx( flag_exist( msg ) , "Attempt to decrement flag before calling flag_init: " + msg );
    AssertEx( IsDefined( level.flag_count ) && IsDefined( level.flag_count[ msg ] ), "Can't decrement a flag that's never been set/incremented." );
    level.flag_count[ msg ]--;
    level.flag_count[ msg ]  = int( max( 0, level.flag_count[ msg ] ) );
    if ( level.flag_count[ msg ] )
        return;
    flag_set( msg );
}

flag_count_set( msg, count )
{
    AssertEx( count, "don't support setting to Zero" );
    AssertEx( flag_exist( msg ) , "Attempt to set flag count before calling flag_init: " + msg );
    level.flag_count[ msg ]   = count;
}


/*
=============
///ScriptDocBegin
"Name: add_cleanup_ent( <ent> , <groupname> )"
"Summary: Adds an entity to be cleaned up SEEDOC:Utility\cleanup_ents"
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <ent>: entity to be deleted by cleanup_ents() "
"MandatoryArg: <groupname>: name used by cleanup_ents to delete"
"Example: add_cleanup_ent( guy, "guys_on_train" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

add_cleanup_ent( ent, groupname )
{
    Assert( IsDefined( ent ) );
    Assert( IsDefined( groupname ) );
    
    if ( !IsDefined( level.cleanup_ents ) )
        level.cleanup_ents = [];
        
    if ( !IsDefined( level.cleanup_ents[ groupname ] ) )
        level.cleanup_ents[ groupname ]   = [];
    /#
    if ( array_contains( level.cleanup_ents[ groupname ] , ent ) )
        AssertMsg( "add_cleanup_ent - this ent already added" );
    #/   
    level.cleanup_ents[ groupname ][ level.cleanup_ents[ groupname ].size ]   = ent;
}

/*
=============
///ScriptDocBegin
"Name: cleanup_ents( <groupname> )"
"Summary: deletes ents specified by add_cleanup_ent"
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <groupname>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

cleanup_ents( groupname ) 
{
    Assert( IsDefined(  level.cleanup_ents ) );
    Assert( IsDefined(  level.cleanup_ents[ groupname ]  ) );
    
    array = level.cleanup_ents[ groupname ];
    array = array_removeUndefined( array );
    array_delete( array );
    
    level.cleanup_ents[ groupname ] = undefined;
}

/*
=============
///ScriptDocBegin
"Name: cleanup_ents_removing_bullet_shield( <groupname> )"
"Summary: deletes ents specified by add_cleanup_ent also removes the bulletshield"
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <groupname>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

cleanup_ents_removing_bullet_shield( groupname ) 
{
    if( ! IsDefined(  level.cleanup_ents ) ) 
    	return;
    if( ! IsDefined(  level.cleanup_ents[ groupname ]  ) )
    	return;
    
    array = level.cleanup_ents[ groupname ];
    array = array_removeUndefined( array );

    foreach( obj in array ) 
    {
    	if ( ! IsAI( obj ) )
    		continue;
		if ( !IsAlive( obj ) )
			continue;
		if ( ! IsDefined( obj.magic_bullet_shield ) )
			continue;
		if( !obj.magic_bullet_shield )
			continue;	
		obj stop_magic_bullet_shield();
    }

    array_delete( array );
    
    level.cleanup_ents[ groupname ] = undefined;
}

/*
=============
///ScriptDocBegin
"Name: add_trigger_function( <function> )"
"Summary: adds a function to be called when a trigger is triggered"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <function>: function to add"
"Example: scn_london_subway_truck_trans add_trigger_function( ::scn_london_subway_truck_trans );"
"SPMP: singleplayer"
"NoteLine: function is called with trigger as self and first param as the triggering entity"
///ScriptDocEnd
=============
*/

add_trigger_function( function )
{
    if( !isdefined( self.trigger_functions ) )
        self thread add_trigger_func_thread();
    self.trigger_functions[ self.trigger_functions.size ] = function;
}

/*
=============
///ScriptDocBegin
"Name: getallweapons( <getallweapons> )"
"Summary: gets all the entities containing classname 'weapon_'.. expensive"
"Module: Entity"
"CallOn: An entity"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

getallweapons()
{
    array = [];
    entities = GetEntArray();
    foreach( ent in entities )
    {
        if ( !IsDefined( ent.classname ) )
            continue;
        if ( IsSubStr( ent.classname, "weapon_" ) )
            array[array.size] = ent;
    }
    return array;
}

radio_add( dialog )
{
     level.scr_radio[dialog ] = dialog;
}



/*
=============
///ScriptDocBegin
"Name: move_with_rate( <origin> , <angles> , <moverate> )"
"Summary: move and rotate an entity to a position with a rate"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <origin>: "
"MandatoryArg: <angles>: "
"MandatoryArg: <moverate>: rate in Units Per Second"
"Example: dummy move_with_rate( tank.origin, tank.angles, 280 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

move_with_rate( origin, angles, moverate )
{
	Assert( IsDefined( origin ) );
	Assert( IsDefined( angles ) );
	Assert( IsDefined( moverate ) );
	
	self notify( "newmove" );
	self endon( "newmove" );

	if ( !isdefined( moverate ) )
		moverate = 200;

	dist = Distance( self.origin, origin );
	movetime = dist / moverate;
	movevec = VectorNormalize( origin - self.origin );

	self MoveTo( origin, movetime, 0, 0 );
	self RotateTo( angles, movetime, 0, 0 );
	wait movetime;

	if ( !isdefined( self ) )
		return;
	self.velocity = movevec * ( dist / movetime );
}

/*
=============
///ScriptDocBegin
"Name: flag_on_death( <msg> )"
"Summary: sets this flag when self dies "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <msg>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

flag_on_death( msg )
{
	level endon( msg );
	self waittill( "death" );
	flag_set( msg );
}

/*
=============
///ScriptDocBegin
"Name: enable_damagefeedback_hud()"
"Summary: Enable damage feedback that draws a crosshair when player hits NPC."
"Module: Utility"
"Example: enable_damagefeedback_hud();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

enable_damagefeedback_hud()
{
	level.damagefeedbackhud = true;
}

/*
=============
///ScriptDocBegin
"Name: disable_damagefeedback_hud()"
"Summary: Disable damage feedback that draws a crosshair when player hits NPC."
"Module: Utility"
"Example: disable_damagefeedback_hud();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

disable_damagefeedback_hud()
{
	level.damagefeedbackhud = false;
}

/*
=============
///ScriptDocBegin
"Name: is_damagefeedback_hud_enabled()"
"Summary: Return boolean - if damage feed hud back is enabled or not."
"Module: Utility"
"Example: if ( is_damagefeedback_hud_enabled() ){ //blah }"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

is_damagefeedback_hud_enabled()
{
	return ( isdefined( level.damagefeedbackhud ) && level.damagefeedbackhud );
}

/*
=============
///ScriptDocBegin
"Name: enable_damagefeedback_snd()"
"Summary: Enable damage feedback that plays a sound when the entity is hit."
"Module: Utility"
"Example: enable_damagefeedback_snd();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

enable_damagefeedback_snd()
{
	level.damagefeedbacksnd = true;
}

/*
=============
///ScriptDocBegin
"Name: disable_damagefeedback_snd()"
"Summary: Disable damage feedback that plays a sound when the entity is hit."
"Module: Utility"
"Example: disable_damagefeedback_snd();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

disable_damagefeedback_snd()
{
	level.damagefeedbacksnd = false;
}

/*
=============
///ScriptDocBegin
"Name: is_damagefeedback_snd_enabled()"
"Summary: Return boolean - if damage feed back snd is enabled or not."
"Module: Utility"
"Example: if ( is_damagefeedback_snd_enabled() ){ //blah }"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

is_damagefeedback_snd_enabled()
{
	return ( isdefined( level.damagefeedbacksnd ) && level.damagefeedbacksnd );
}

/*
=============
///ScriptDocBegin
"Name: add_damageFeedback()"
"Summary: Add damage feedback monitor on this entity. Call enable_damagefeedback_hud/snd() to enable globally"
"Module: Entity"
"CallOn: An entity"
"Example: chopper add_damagefeedback();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_damagefeedback()
{
	assert( isdefined( self ) );
	self maps\_damagefeedback::monitorDamage();
}

/*
=============
///ScriptDocBegin
"Name: remove_damagefeedback()"
"Summary: Remove damage feedback monitor on this entity."
"Module: Entity"
"CallOn: An entity"
"Example: chopper remove_damagefeedback();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_damagefeedback()
{
	assert( isdefined( self ) );
	self maps\_damagefeedback::stopMonitorDamage();
}

/*
=============
///ScriptDocBegin
"Name: is_demo()"
"Summary: Determines if the level is set for demo or not
"Module: Utility"
"Example: if ( is_demo() )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
is_demo()
{
	if ( GetDvar( "e3demo" ) == "1" )
	{
		return true;
	}

	return false;
}

/*
=============
///ScriptDocBegin
"Name: deletestructarray( <value> , <key> , <delay> )"
"Summary: Deletes a array of structs ( Used for structs placed in the Level Editor )"
"Module: Utility"
"MandatoryArg: <value>: "
"MandatoryArg: <key>: "
"OptionalArg:  <delay> : delay between deleting structs. Use when you are freeing up many structs"
"Example: deletestructarray( "my_struct", "targetname" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

deletestructarray( value, key, delay )
{
	structs = getstructarray( value, key );
	deletestructarray_ref( structs, delay );
}

/*
=============
///ScriptDocBegin
"Name: deletestruct_ref( <struct> )"
"Summary: Delete a struct via reference ( Used for a struct in the Level Editor )"
"Module: Utility"
"MandatoryArg: <struct>: "
"Example: deletestruct_ref( struct )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

deletestruct_ref( struct )
{
	if ( !IsDefined( struct ) )
		return;

	value = struct.script_linkname;
	
	if ( IsDefined( value ) && 
		 IsDefined( level.struct_class_names[ "script_linkname" ] ) &&
		 IsDefined( level.struct_class_names[ "script_linkname" ][ value ] ) )
	{
		foreach( i, _struct in level.struct_class_names[ "script_linkname" ][ value ] )
			if ( IsDefined( _struct ) && struct == _struct )
				level.struct_class_names[ "script_linkname" ][ value ][ i ] = undefined;
				
		if ( level.struct_class_names[ "script_linkname" ][ value ].size == 0 )
			level.struct_class_names[ "script_linkname" ][ value ] = undefined;
	}
	
	value = struct.script_noteworthy;
	
	if ( IsDefined( value ) && 
		 IsDefined( level.struct_class_names[ "script_noteworthy" ] ) &&
		 IsDefined( level.struct_class_names[ "script_noteworthy" ][ value ] ) )
	{
		foreach( i, _struct in level.struct_class_names[ "script_noteworthy" ][ value ] )
			if ( IsDefined( _struct ) && struct == _struct )
				level.struct_class_names[ "script_noteworthy" ][ value ][ i ] = undefined;
				
		if ( level.struct_class_names[ "script_noteworthy" ][ value ].size == 0 )
			level.struct_class_names[ "script_noteworthy" ][ value ] = undefined;
	}
	
	value = struct.target;
	
	if ( IsDefined( value ) && 
		 IsDefined( level.struct_class_names[ "target" ] ) &&
		 IsDefined( level.struct_class_names[ "target" ][ value ] ) )
	{
		foreach( i, _struct in level.struct_class_names[ "target" ][ value ] )
			if ( IsDefined( _struct ) && struct == _struct )
				level.struct_class_names[ "target" ][ value ][ i ] = undefined;
				
		if ( level.struct_class_names[ "target" ][ value ].size == 0 )
			level.struct_class_names[ "target" ][ value ] = undefined;
	}
	
	value = struct.targetname;
	
	if ( IsDefined( value ) && 
		 IsDefined( level.struct_class_names[ "targetname" ] ) &&
		 IsDefined( level.struct_class_names[ "targetname" ][ value ] ) )
	{
		foreach( i, _struct in level.struct_class_names[ "targetname" ][ value ] )
			if ( IsDefined( _struct ) && struct == _struct )
				level.struct_class_names[ "targetname" ][ value ][ i ] = undefined;
				
		if ( level.struct_class_names[ "targetname" ][ value ].size == 0 )
			level.struct_class_names[ "targetname" ][ value ] = undefined;
	}
	
	// You Should Remove level.struct on level start if you use this!!
	
	if ( IsDefined( level.struct ) )
		foreach ( i, _struct in level.struct )
			if ( struct == _struct )
				level.struct[ i ] = undefined;
}

/*
=============
///ScriptDocBegin
"Name: deletestructarray_ref( <structs> , <delay> )"
"Summary: Delete an array of structs via references ( Used for a struct placed in the Level Editor )"
"Module: Utility"
"MandatoryArg: <structs>: "
"OptionalArg:  <delay> : delay between deleting structs. Use when you are freeing up many structs"
"Example: deletestructarray_ref( structs )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

deletestructarray_ref( structs, delay )
{
	if ( !IsDefined( structs ) || !IsArray( structs ) || structs.size == 0 )
		return;
		
	delay = ter_op( IsDefined( delay ), delay, 0 );
	delay = ter_op( delay > 0, delay, 0 );
	
	if ( delay > 0 )
	{
		foreach ( struct in structs )
		{
			deletestruct_ref( struct );
			wait delay;
		}
	}
	else
		foreach ( struct in structs )
			deletestruct_ref( struct );
}

/*
=============
///ScriptDocBegin
"Name: getstruct_delete( <value> , <key> )"
"Summary: Just like getstruct, but deletes the struct from global level struct list"
"Module: Utility"
"MandatoryArg: <value>: "
"MandatoryArg: <key>: "
"Example: struct = getstruct_delete( "my_struct", "targetname" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

getstruct_delete( value, key )
{
	struct = getstruct( value, key );
	deletestruct_ref( struct );
	return struct;
}

/*
=============
///ScriptDocBegin
"Name: getstructarray_delete( <value> , <key> , <delay> )"
"Summary: Just like getstructarray, but deletes the structs from the global level struct list"
"Module: Utility"
"MandatoryArg: <value>: "
"MandatoryArg: <key>: "
"OptionalArg:  <delay> : delay between deleting structs. Use when you are freeing up many structs"
"Example: structs = getstructarray_delete( "my_struct", "targetname" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

getstructarray_delete( value, key, delay )
{
	structs = getstructarray( value, key );
	deletestructarray_ref( structs, delay );
	return structs;
}

/*
=============
///ScriptDocBegin
"Name: getent_or_struct_or_node( <value> , <key> )"
"Summary: Returns a ent or struct or node or vehicle node"
"Module: Utility"
"MandatoryArg: <value>: "
"MandatoryArg: <key>: "
"Example: ent = getent_or_struct_or_node( "my_ent", "targetname" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

getent_or_struct_or_node( value, key )
{
    Assert( IsDefined( value ) );
    Assert( IsDefined( key ) );
    
    ent = getent_or_struct( value, key );
    
    if ( !IsDefined( ent ) )
    	ent = GetNode( value, key );
    if ( !IsDefined( ent ) )
    	ent = GetVehicleNode( value, key );
    return ent;
}


/*
=============
///ScriptDocBegin
"Name: setEntityHeadIcon( <shader>, <width>, <height>, <offset_vector>, <reference_point_func> )"
"Summary: Places head icon on an entity. The head icon changes size to the observer and is using newclienthudelem(). Called on the entity."
"Module: Utility"
"MandatoryArg: <shader>: Icon shader, must be precached to be used."
"MandatoryArg: <width>: Widthof icon."
"MandatoryArg: <height>: Height of icon."
"OptionalArg: <offset_vector>: Offset vector from the origin of the entity."
"OptionalArg: <reference_point_func>: Function that returns dynamic reference vector."
"Example: AI setEntityHeadIcon( "delta_airsupport", ( 0, 0, 20 ), level.getAiEyePos_func )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

setEntityHeadIcon( icon_shader, icon_width, icon_height, offset, reference_point_func )
{
	// self is entity
	assertex( isdefined( icon_shader ), "setEntityHeadIcon() requires icon shader passed in." );
	
	if ( isDefined( offset ) )
		self.entityHeadIconOffset = offset;
	else
		self.entityHeadIconOffset = (0,0,0);
	
	if( isDefined( reference_point_func ) )
		self.entityHeadIconReferenceFunc = reference_point_func;

	self notify( "new_head_icon" );

	headIcon = newhudelem();
	headIcon.archived = true;
	headIcon.alpha = .8;
	headIcon setShader( icon_shader, icon_width, icon_height );
	headIcon setWaypoint( false, false, false, true );
	self.entityHeadIcon = headIcon;
	self updateEntityHeadIconOrigin();
	
	self thread updateEntityHeadIcon();
	self thread destroyEntityHeadIconOnDeath();
}

/*
=============
///ScriptDocBegin
"Name: removeEntityHeadIcon()"
"Summary: Removes head icon on an entity that setEntityHeadIcon() set earlier. Called on the entity."
"Module: Utility"
"Example: AI removeEntityHeadIcon()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
removeEntityHeadIcon()
{
	// self is entity, self.entityHeadIcon is the HudElem
	
	if ( !isdefined( self.entityHeadIcon ) )
		return;
	
	self.entityHeadIcon destroy();
}

updateEntityHeadIcon()
{
	// self is entity, self.entityHeadIcon is the HudElem
	
	self endon( "new_head_icon" );
	self endon( "death" );
	
	pos = self.origin;
	while(1)
	{
		if ( pos != self.origin ) 
		{
			self updateEntityHeadIconOrigin();
			pos = self.origin;
		}
		wait .05;
	}
}

updateEntityHeadIconOrigin()
{
	// self is entity, self.entityHeadIcon is the HudElem
	if( isDefined( self.entityHeadIconReferenceFunc ) )
	{
		entityHeadIconReference = self [[ self.entityHeadIconReferenceFunc ]]();
		
		if( isdefined( entityHeadIconReference ) )
		{	
			self.entityHeadIcon.x = self.entityHeadIconOffset[0] + entityHeadIconReference[0];
			self.entityHeadIcon.y = self.entityHeadIconOffset[1] + entityHeadIconReference[1];
			self.entityHeadIcon.z = self.entityHeadIconOffset[2] + entityHeadIconReference[2];	

			return;
		}
	}

	self.entityHeadIcon.x = self.origin[0] + self.entityHeadIconOffset[0];
	self.entityHeadIcon.y = self.origin[1] + self.entityHeadIconOffset[1];
	self.entityHeadIcon.z = self.origin[2] + self.entityHeadIconOffset[2];
}

destroyEntityHeadIconOnDeath()
{
	// self is entity, self.entityHeadIcon is the HudElem
	
	self endon( "new_head_icon" );
	self waittill( "death" );

	if( !isDefined(self.entityHeadIcon) )
		return;

	self.entityHeadIcon destroy();
}

/*
=============
///ScriptDocBegin
"Name: WorldToLocalCoords( <world_vec> )"
"Summary: Transform the given world coordinate point into a local coordinate point"
"CallOn: An entity (or anything with .origin and .angles)"
"MandatoryArg: <world_vec>: "
"the vector to transform, defined in world corodinates"
"Example: link_offset = thing_being_linked_to WorldToLocalCoords(final_pos)"
"Module: Utility"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
WorldToLocalCoords(world_vec)
{
	// note: this is named as it is by analogy with the engine call LocalToWorldCoords().
	// If performance becomes a problem, we might want to convert this to an engine call.
	
	pos_to_vec_world = world_vec - self.origin;
	
	return (        VectorDot( pos_to_vec_world, AnglesToForward( self.angles ) ),
	         -1.0 * VectorDot( pos_to_vec_world, AnglesToRight( self.angles ) ),
	                VectorDot( pos_to_vec_world, AnglesToUp( self.angles ) )
	       );
}


//---------------------------------------------------------
// Audio Section
//---------------------------------------------------------
/*
=============
///ScriptDocBegin
"Name: intro_screen_create( <string1> , <string2> , <string3> , <string4> , <string5> )"
"Summary: sets Localized strings to be displayed in the intro screen"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <string1>: Title"
"MandatoryArg: <string2>: Date"
"MandatoryArg: <string3>: Who"
"MandatoryArg: <string4>: Squad Name"
"OptionalArg: <string5>: Where"
"Example: intro_screen_create( &"TANKCOMMANDER_INTROSCREEN_LINE_1", &"TANKCOMMANDER_INTROSCREEN_LINE_2", &"TANKCOMMANDER_INTROSCREEN_LINE_3", &"TANKCOMMANDER_INTROSCREEN_LINE_4" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

intro_screen_create( string1, string2, string3, string4, string5 )
{
	AssertEx( !IsDefined( level.introScreen ), "intro_screen_create called Twice, Make up your mind!" );
	
	level.introScreen = SpawnStruct();
	
	level.introScreen.completed_delay = 3;
	level.introScreen.fade_out_time	  = 1.5;
	level.introScreen.fade_in_time	  = undefined;
	
	if ( IsDefined( string4 ) )
		level.introScreen.lines = [ string1, string2, string3, string4 ];
	else
	level.introScreen.lines = [ string1, string2, string3 ];
	noself_array_call( level.introScreen.lines, ::PreCacheString );
}

/*
=============
///ScriptDocBegin
"Name: intro_screen_custom_func( <function> )"
"Summary: overrides _introscreen::introscreen_default()"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <function>: "
"Example: intro_screen_custom_func( ::london_intro );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

intro_screen_custom_func( function )
{
	AssertEx( IsDefined( level.introScreen ), "must call intro_screen_custom_func after intro_screen_create" );
	level.introScreen.CustomFunc = function;
}

/*
=============
///ScriptDocBegin
"Name: intro_screen_custom_timing( <completed_delay> , <fade_out_time> , <fade_in_time> )"
"Summary: Adjust timings in the default introscreen, try this first before overiding the function."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <completed_delay>: "
"OptionalArg: <fade_out_time>: "
"OptionalArg: <fade_in_time>: "
"Example: intro_screen_custom_timing( 3, 3.5 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

intro_screen_custom_timing( completed_delay, fade_out_time, fade_in_time )
{
	AssertEx( IsDefined( level.introScreen ), "must call intro_screen_custom_timing after Intro_Screen_Create" );
	level.introScreen.completed_delay = completed_delay;
	level.introScreen.fade_out_time	  = fade_out_time;
	level.introScreen.fade_in_time	  = fade_in_time;
}


/*
=============
///ScriptDocBegin
"Name: set_npc_anims( archetypeName, run_anim, walk_anim, idle_anim_array, arrive_anim_array, exit_anim_array, run_turn_anim_array )"
"Summary: Creates a new archetype and assigns the specified anims. All anims are optional"
"Module: Utility"
"CallOn: Npc"
"MandatoryArg: <archetypeName>: name of the archetype"
"OptionalArg: <run_anim>: animation for run"
"OptionalArg: <walk_anim>: animation for walk"
"OptionalArg: <idle_anim_array>: array of animation for idle"
"OptionalArg: <arrive_anim_array>: array of animations for arrivals"
"OptionalArg: <exit_anim_array>: array of animations for exits"
"OptionalArg: <arrivals_exits_trans_types>: trans types to assosicate the arrivals and exits with"
"OptionalArg: <run_turn_anim_array>: array of animations for run_turns"
"OptionalArg: <walk_turn_anim_array>: array of animations for walk_turns - defaults to cqb run turns"
"OptionalArg: <stairs_anim_array>: array of animations for walk_turns - defaults to cqb run turns"
"Example: self set_npc_anims( "president", %rec_president_run, %rec_president_run, %rec_president_crouch_idle, arrive_anim_array, exit_anim_array, arrivals_exits_trans_types );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

set_npc_anims( archetypeName, run_anim, walk_anim, idle_anim_array, arrive_anim_array, exit_anim_array, arrivals_exits_trans_types ,run_turn_anim_array, walk_turn_anim_array, stairs_anim_array )
{
	
	Assert( IsAI( self ) );
	Assert( IsDefined( archetypeName ) );
	
//	[2014/09/02 tfong] this assert is very useful in catching archetypes that are overridden, but there are preexisting utility functions such as set_urgent_walk() that would like to set the archetype
//  TODO: Fix the real way .... next project?    Its safer to just revert to prior behaviour
//	AssertEx(!IsDefined(anim.archetypes[archetypeName]),"Overwriting existing archetype " + archetypeName);
	
	if( IsDefined( run_anim ) )
		self.run_overrideanim  = run_anim;
	if( IsDefined( walk_anim ) )
		self.walk_overrideanim = walk_anim;
	if( IsDefined( idle_anim_array ) )
		self.specialIdleAnim = idle_anim_array;
	
	self.animArchetype = archetypeName;
	archetype = [];
	
	// arrivals and exits
	if( IsDefined( arrive_anim_array ) && IsDefined( exit_anim_array ) )
	{
		AssertEx( IsDefined(arrivals_exits_trans_types) && IsArray(arrivals_exits_trans_types), "Must define array of transition types to associate with" );
		
		Assert( IsDefined( arrive_anim_array[1] ) );
		Assert( IsDefined( arrive_anim_array[2] ) );
		Assert( IsDefined( arrive_anim_array[3] ) );
		Assert( IsDefined( arrive_anim_array[4] ) );
		Assert( IsDefined( arrive_anim_array[6] ) );
		Assert( IsDefined( arrive_anim_array[7] ) );
		Assert( IsDefined( arrive_anim_array[8] ) );
		Assert( IsDefined( arrive_anim_array[9] ) );
		
		Assert( IsDefined( exit_anim_array[1] ) );
		Assert( IsDefined( exit_anim_array[2] ) );
		Assert( IsDefined( exit_anim_array[3] ) );
		Assert( IsDefined( exit_anim_array[4] ) );
		Assert( IsDefined( exit_anim_array[6] ) );
		Assert( IsDefined( exit_anim_array[7] ) );
		Assert( IsDefined( exit_anim_array[8] ) );
		Assert( IsDefined( exit_anim_array[9] ) );
		
		arrive_animset = [];
		foreach(transType in arrivals_exits_trans_types)
			arrive_animset[ transType ] = arrive_anim_array;
		archetype[ "cover_trans" ] = arrive_animset;
		
		exit_animset = [];
		foreach(transType in arrivals_exits_trans_types)
			exit_animset[ transType ] = exit_anim_array;
		archetype[ "cover_exit" ] = exit_animset;
	}
	else if( IsDefined( arrive_anim_array ) || IsDefined( exit_anim_array ) )
	{
		AssertMsg( "Did you specify one and not the other ? (arrivals/exits) " );
	}
	
	// run turn & walk turn
	if( IsDefined( run_turn_anim_array ) )
	{
		Assert( IsDefined( run_turn_anim_array[0] ) );
		Assert( IsDefined( run_turn_anim_array[1] ) );
		Assert( IsDefined( run_turn_anim_array[2] ) );
		Assert( IsDefined( run_turn_anim_array[3] ) );
		Assert( IsDefined( run_turn_anim_array[5] ) );
		Assert( IsDefined( run_turn_anim_array[6] ) );
		Assert( IsDefined( run_turn_anim_array[7] ) );
		Assert( IsDefined( run_turn_anim_array[8] ) );
		
		if( IsDefined( walk_turn_anim_array ) )
		{
			Assert( IsDefined( walk_turn_anim_array[0] ) );
			Assert( IsDefined( walk_turn_anim_array[1] ) );
			Assert( IsDefined( walk_turn_anim_array[2] ) );
			Assert( IsDefined( walk_turn_anim_array[3] ) );
			Assert( IsDefined( walk_turn_anim_array[5] ) );
			Assert( IsDefined( walk_turn_anim_array[6] ) );
			Assert( IsDefined( walk_turn_anim_array[7] ) );
			Assert( IsDefined( walk_turn_anim_array[8] ) );
		}
		
		archetype[ "run_turn" ] = run_turn_anim_array;
		archetype[ "walk_turn" ] = walk_turn_anim_array;
		self.noTurnAnims = undefined;
	}
	else if( IsDefined( walk_turn_anim_array ))
	{
		AssertEx("Must specify run turns.");
	}
	else
	{
		self.noTurnAnims = true;
	}
	
	if( IsDefined( stairs_anim_array ) )
	{
		animset = [];
		
		animset[ "stairs_up" ] 		 = stairs_anim_array[ "stairs_up" ];
		animset[ "stairs_down" ] 	 = stairs_anim_array[ "stairs_down" ];
		animset[ "stairs_up_in" ] 	 = stairs_anim_array[ "stairs_up_in" ];
		animset[ "stairs_down_in" ]  = stairs_anim_array[ "stairs_down_in" ];
		animset[ "stairs_up_out" ] 	 = stairs_anim_array[ "stairs_up_out" ];
		animset[ "stairs_down_out" ] = stairs_anim_array[ "stairs_down_out" ];
		
		// single stair anim set for walk and run
		archetype["walk"] = animset;
		archetype["run"] = animset;
		
		self.run_overrideanim_hasStairAnimArray = true;
	}
	else
	{
		self.run_overrideanim_hasStairAnimArray = undefined;
	}
	
	anim.archetypes[archetypeName] = archetype;
	animscripts\init_move_transitions::initTransDistAndAnglesForArchetype( archetypeName );	
}

/*
=============
///ScriptDocBegin
"Name: clear_npc_anims( archetypeName )"
"Summary: Clears the work of set_npc_anims()"
"Module: Utility"
"CallOn: Npc"
"MandatoryArg: <archetypeName>: name of the archetype"
"Example: clear_npc_anims( "president" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

clear_npc_anims( archetypeName )
{
	self.animArchetype = undefined;
	anim.archetypes[archetypeName] = undefined;
	self.run_overrideanim  = undefined;
	self.run_overrideanim_hasStairAnimArray = undefined;
	self.walk_overrideanim = undefined;
	self.specialIdleAnim = undefined;
}

/*
=============
///ScriptDocBegin
"Name: register_archetype( name, anims )"
"Summary: Registers a new set of actor animations"
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: name of the archetype"
"MandatoryArg: <anims>: array of animation arrays"
"OptionalArg: <calc_split_times>: Bool that determines whether transition anim angles and dists are calculated"
"Example: register_archetype( "deep_water", animations )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

register_archetype( name, anims, calc_split_times )
{
	animscripts\animset::RegisterArchetype( name, anims, calc_split_times );
}

/*
=============
///ScriptDocBegin
"Name: archetype_exists( name )"
"Summary: Returns whether an archetype already exists"
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: name of the archetype"
"Example: archetype_exists( "deep_water" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

archetype_exists( name )
{
	return animscripts\animset::ArchetypeExists( name );
}

/*
=============
///ScriptDocBegin
"Name: set_archetype( name )"
"Summary: Sets an actor to use an anim archetype"
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: name of the archetype"
"Example: set_archetype( "deep_water" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

set_archetype( name )
{
	assert( animscripts\animset::ArchetypeExists( name ) );
	self.animArchetype = name;
	self notify( "move_loop_restart" );

	if ( name == "creepwalk" )
		self.sharpTurnLookaheadDist = 72;
}

/*
=============
///ScriptDocBegin
"Name: clear_archetype()"
"Summary: Sets an actor to use the default archetype"
"Module: Utility"
"CallOn: "
"Example: clear_archetype()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

clear_archetype()
{
	if ( IsDefined( self.animArchetype ) && self.animArchetype == "creepwalk" )
		self.sharpTurnLookaheadDist = 30;

	self.animArchetype = undefined;
	self notify( "move_loop_restart" );
}


/*
=============
///ScriptDocBegin
"Name: shot_endangers_any_player( <start>, <end> )"
"Summary: Returns true if a bullet shot from start to end would pass too close to any player.  Does no traces, only angle checks."
"Module: Utility"
"MandatoryArg: <start>: the starting point of the hypothetical bullet"
"MandatoryArg: <end>: the end point.  The hypothetical bullet will not stop at this point, however."
"Example: if ( ! trace_endangers_any_player( start, end ) ) MagicBullet("weapon", start, end );"
///ScriptDocEnd
=============
*/
shot_endangers_any_player(start, end)
{
	foreach(player in level.players)
	{
		if(player shot_endangers_player(start, end))
		{
			return true;
		}
	}
	return false;
}

/*
=============
///ScriptDocBegin
"Name: shot_endangers_player( <start>, <end> )"
"Summary: Returns true if a bullet shot from start to end would pass too close to self.  Does no traces, only angle checks."
"Module: Utility"
"CallOn: player (or anything with an origin)"
"MandatoryArg: <start>: the starting point of the hypothetical bullet"
"MandatoryArg: <end>: the end point.  The hypothetical bullet will not stop at this point, however."
"Example: if ( ! level.player trace_endangers_player( start, end ) ) MagicBullet("weapon", start, end );"
///ScriptDocEnd
=============
*/
TRACE_ENDANGERS_PLAYER_RADIUS = 60;  // really 41, but give some buffer for if the player moves
shot_endangers_player(start, end)
{
	// return true if the trace is within TRACE_ENDANGERS_PLAYER_EXTRA_ANGLE degrees of hitting a sphere centered on
	// the player's center with radius TRACE_ENDANGERS_PLAYER_RADIUS
	player_center = self GetPointInBounds(0, 0, 0);
	
	to_player = player_center - start;
	range_to_player = Length(to_player);
	min_angle = ASin(Clamp(TRACE_ENDANGERS_PLAYER_RADIUS / range_to_player, 0, 1));
	if(VectorDot(VectorNormalize(to_player), VectorNormalize(end - start)) > Cos(min_angle))
	{
		// /# thread draw_line_for_time(start, end, 0, 1, 0, .5); #/
		return true;
	}
	// /# thread draw_line_for_time(start, end, 1, 0, 0, .05); #/
	return false;
}

/*
=============
///ScriptDocBegin
"Name: transient_load( <name> )"
"Summary: Loads the given transient file. The transient needs to have used transient_init()"
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: name of the transient file"
"Example: transient_load( "las_vegas_transient_hotel_tr" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
transient_load( name )
{
	LoadTransient( name );
	while ( !IsTransientLoaded( name ) )
	{
		wait( 0.1 );
	}
	
	flag_set( name + "_loaded" );
}

/*
=============
///ScriptDocBegin
"Name: transient_unload( <name> )"
"Summary: Unloads the given transient file. The transient needs to have used transient_init()"
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: name of the transient file"
"Example: transient_unload( "las_vegas_transient_hotel_tr" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
transient_unload( name )
{
	UnloadTransient( name );
	while ( IsTransientLoaded( name ) )
	{
		wait( 0.1 );
	}

	flag_clear( name + "_loaded" );
}

/*
=============
///ScriptDocBegin
"Name: transient_init( <name> )"
"Summary: Initializes the given transient file ot a flag. The flag is name + '_loaded'"
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: name of the transient file"
"Example: transient_unload( "las_vegas_transient_hotel_tr" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
transient_init( name )
{
	AssertEx( !IsDefined( level._loadStarted ), "transient_init() must be set before _load::main()" );
	flag_init( name + "_loaded" );
}

/*
=============
///ScriptDocBegin
"Name: transient_switch( <prev>, <next> )"
"Summary: Switches from the previous transient to the next.
"Module: Utility"
"CallOn: "
"MandatoryArg: <prev>: name of the previous transient file"
"MandatoryArg: <next>: name of the next transient file"
"Example: transient_unload( "las_vegas_transient_hotel_tr", "las_vegas_transient_strip_tr" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
transient_switch( prev, next )
{
	if ( flag( prev + "_loaded" ) )
	{
		transient_unload( prev );
	}
	
	if ( !flag( next + "_loaded" ) )
	{
		transient_load( next );
	}
}

/*
=============
///ScriptDocBegin
"Name: transient_unloadall_and_load( <name> )"
"Summary: Unloads all transient files, then loads the given transient file."
"Module: Utility"
"CallOn: "
"MandatoryArg: <name>: name of the transient file"
"Example: transient_unload( "las_vegas_transient_hotel_tr", "las_vegas_transient_strip_tr" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
transient_unloadall_and_load( name )
{
	UnloadAllTransients();
	transient_load( name );
}




/*
=============
///ScriptDocBegin
"Name: deep_array_call( <ents> , <process> , <args> )"
"Summary: see, array_call. dives deep into the array calling out deep_array_call.
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <ents>: "
"MandatoryArg: <process>: "
"MandatoryArg: <args>: "
"Example: "
"SPMP: singleplayer"
"NoteLine: This is intended as a fix for override_array_call being duped all over, I'm not sure I agree with all the isdefined checks"
///ScriptDocEnd
=============
*/

deep_array_call( ents, process, args )
{
	Assert( IsDefined( ents ) );
	Assert( IsDefined( process ) );
	
	if ( !IsDefined( args ) )
	{
		foreach ( ent in ents )
			if ( IsDefined( ent ) )
				if ( IsArray( ent ) )
					deep_array_call( ent, process );
				else
					ent call [[ process ]]();
		return;
	}
	
	Assert( IsArray( args ) );
				
	switch( args.size )
	{
		case 0:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_call( ent, process, args );
					else
						ent call [[ process ]]();
			break;
		case 1:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_call( ent, process, args );
					else
						ent call [[ process ]]( args[ 0 ] );
			break;
		case 2:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_call( ent, process, args );
					else
						ent call [[ process ]]( args[ 0 ], args[ 1 ] );
			break;
		case 3:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_call( ent, process, args );
					else
						ent call [[ process ]]( args[ 0 ], args[ 1 ], args[ 2 ] );
			break;
		case 4:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_call( ent, process, args );
					else
						ent call [[ process ]]( args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ] );
			break;
		case 5:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_call( ent, process, args );
					else
						ent call [[ process ]]( args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ] );
			break;
	}
	return;
}


/*
=============
///ScriptDocBegin
"Name: deep_array_thread( <ents> , <process> , <args> )"
"Summary: similar to array thread, but will walk through the array."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <ents>: "
"MandatoryArg: <process>: "
"MandatoryArg: <args>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

deep_array_thread( ents, process, args )
{
	Assert( IsDefined( ents ) );
	Assert( IsDefined( process ) );
	
	if ( !IsDefined( args ) )
	{
		foreach ( ent in ents )
			if ( IsDefined( ent ) )
				if ( IsArray( ent ) )
					deep_array_thread( ent, process, args );
				else
					ent thread [[ process ]]();
		return;
	}
	
	Assert( IsArray( args ) );
				
	switch( args.size )
	{
		case 0:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_thread( ent, process, args );
					else
						ent thread [[ process ]]();
			break;
		case 1:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_thread( ent, process, args );
					else
						ent thread [[ process ]]( args[ 0 ] );
			break;
		case 2:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_thread( ent, process, args );
					else
						ent thread [[ process ]]( args[ 0 ], args[ 1 ] );
			break;
		case 3:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_thread( ent, process, args );
					else
						ent thread [[ process ]]( args[ 0 ], args[ 1 ], args[ 2 ] );
			break;
		case 4:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_thread( ent, process, args );
					else
						ent thread [[ process ]]( args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ] );
			break;
		case 5:
			foreach ( ent in ents )
				if ( IsDefined( ent ) )
					if ( IsArray( ent ) )
						deep_array_thread( ent, process, args );
					else
						ent thread [[ process ]]( args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ] );
			break;
	}
	return;
}


setdvar_cg_ng( dvar_name, current_gen_val, next_gen_val )
{
	if ( !IsDefined( level.console ) )
		set_console_status();
	AssertEx( IsDefined( level.console ) && IsDefined( level.xb3 ) && IsDefined( level.ps4 ), "Expected platform defines to be complete." );

	if ( is_gen4() )
		setdvar( dvar_name, next_gen_val );
	else
		setdvar( dvar_name, current_gen_val );
}

setsaveddvar_cg_ng( dvar_name, current_gen_val, next_gen_val )
{
	if ( !IsDefined( level.console ) )
		set_console_status();
	AssertEx( IsDefined( level.console ) && IsDefined( level.xb3 ) && IsDefined( level.ps4 ), "Expected platform defines to be complete." );

	if ( is_gen4() )
		setsaveddvar( dvar_name, next_gen_val );
	else
		setsaveddvar( dvar_name, current_gen_val );
}

follow_path_and_animate( start_node, require_player_dist )
{
	self endon( "death" );
	self endon( "stop_path" );

	self notify( "stop_going_to_node" );
	self notify( "follow_path" );
	self endon( "follow_path" );
	
	wait 0.1;

	node = start_node;

	getfunc = undefined;
	gotofunc = undefined;

	if ( !IsDefined( require_player_dist ) )
		require_player_dist = 300;

	//only nodes dont have classnames - ents do

	self.current_follow_path = node;
	
	node script_delay();
	
	while ( IsDefined( node ) )
	{
		self.current_follow_path = node;
		
		if ( IsDefined( node.lookahead ) )
		{
			break;
		}
		if ( IsDefined( level.struct_class_names[ "targetname" ][ node.targetname ] ) )
		{
			gotofunc = ::follow_path_animate_set_struct;
		}
		else if ( IsDefined( node.classname ) )
		{
			gotofunc = ::follow_path_animate_set_ent;
		}
		else
		{
			gotofunc = ::follow_path_animate_set_node;
		}
		
		if ( IsDefined( node.radius ) && node.radius != 0 )
			self.goalradius = node.radius;
		if ( self.goalradius < 16 )
			self.goalradius = 16;
		if ( IsDefined( node.height ) && node.height != 0 )
			self.goalheight = node.height;

		original_goalradius = self.goalradius;

		self childthread [[ gotofunc ]]( node );

		if ( isdefined( node.animation ) )
			node waittill( node.animation );
		else
			while ( 1 )
			{
				self waittill( "goal" );
				if ( Distance( node.origin, self.origin ) < ( original_goalradius + 10 ) || self.team != "allies" )
					break;
			}

		node notify( "trigger", self );

		if ( isdefined( node.script_flag_set ) )
			flag_set( node.script_flag_set );
		
		// parameters
		if ( isdefined( node.script_parameters ) )
		{
			words = strtok( node.script_parameters, " " );
			for ( i=0 ; i<words.size ; i++ )
			{
				if ( isdefined( level.custom_followpath_parameter_func ) )
			    {
					self [[ level.custom_followpath_parameter_func ]]( words[i], node );
			    }
				if ( self.type == "dog" )
				{
				}
				else
				{
					switch ( words[i] )
					{
						case "enable_cqb":
							self enable_cqbwalk();
							break;
						case "disable_cqb":
							self disable_cqbwalk();
							break;			
						case "deleteme":
							self delete();
							return;								
					}
				}
			}
		}
		
		// waiting for player
		if ( !IsDefined( node.script_requires_player ) && require_player_dist > 0 && self.team == "allies" )
		{
			while ( IsAlive( level.player ) )
			{
				if ( self follow_path_wait_for_player( node, require_player_dist ) )
					break;
				if ( IsDefined( node.animation ) )
				{
					self.goalradius = original_goalradius;
					self SetGoalPos( self.origin );
				}
				wait 0.05;
			}
		}	
		
		if ( !IsDefined( node.target ) )
			break;

		if ( isdefined( node.script_flag_wait ) )
			flag_wait( node.script_flag_wait );
		
		node script_delay();

		node = node get_target_ent();
	}

	self notify( "path_end_reached" );
}

follow_path_wait_for_player( node, dist )
{
	if ( Distance( level.player.origin, node.origin ) < Distance( self.origin, node.origin ) )
		return true;

	vec = undefined;
	//is the player ahead of us?
	vec = AnglesToForward( self.angles );
	vec2 = VectorNormalize( ( level.player.origin - self.origin ) );

	if ( IsDefined( node.target ) )
	{
		temp = get_target_ent( node.target );
		vec = VectorNormalize( temp.origin - node.origin );
	}
	else if ( IsDefined( node.angles ) )
		vec = AnglesToForward( node.angles );
	else
		vec = AnglesToForward( self.angles );

	//i just created a vector which is in the direction i want to
	//go, lets see if the player is closer to our goal than we are				
	if ( VectorDot( vec, vec2 ) > 0 )
		return true;

	//ok so that just checked if he was a mile away but more towards the target
	//than us...but we dont want him to be right on top of us before we start moving
	//so lets also do a distance check to see if he's close behind

	if ( Distance( level.player.origin, self.origin ) < dist )
		return true;

	return false;
}

follow_path_animate_set_node( node )
{
	self notify( "follow_path_new_goal" );
	if ( IsDefined( node.animation ) )
	{
		node maps\_anim::anim_generic_reach( self, node.animation );
		self notify( "starting_anim", node.animation );
		if ( isdefined( node.script_parameters ) && issubstr( node.script_parameters, "gravity" ) )
			node maps\_anim::anim_generic_gravity( self, node.animation );
		else
		node maps\_anim::anim_generic_run( self, node.animation );
		self setGoalPos( self.origin );
	}
	else
	{
		self set_goal_node( node );
	}
}

follow_path_animate_set_ent( ent )
{
	self notify( "follow_path_new_goal" );
	if ( IsDefined( ent.animation ) )
	{
		ent maps\_anim::anim_generic_reach( self, ent.animation );
		self notify( "starting_anim", ent.animation );
		if ( isdefined( ent.script_parameters ) && issubstr( ent.script_parameters, "gravity" ) )
			ent maps\_anim::anim_generic_gravity( self, ent.animation );
		else
		ent maps\_anim::anim_generic_run( self, ent.animation );
		self setGoalPos( self.origin );
	}
	else
	{
		self set_goal_ent( ent );
	}
}

follow_path_animate_set_struct( struct )
{
	self notify( "follow_path_new_goal" );
	if ( IsDefined( struct.animation ) )
	{
		struct maps\_anim::anim_generic_reach( self, struct.animation );
		self notify( "starting_anim", struct.animation );
		self disable_exits();
		if ( isdefined( struct.script_parameters ) && issubstr( struct.script_parameters, "gravity" ) )
			struct maps\_anim::anim_generic_gravity( self, struct.animation );
		else
		struct maps\_anim::anim_generic_run( self, struct.animation );
		self delayThread( 0.05, ::enable_exits );
		self setGoalPos( self.origin );
	}
	else
	{
		self set_goal_pos( struct.origin );
	}
}


/*
=============
///ScriptDocBegin
"Name: post_load_precache( <function> )"
"Summary: used for one line additions before _load::main of script where you want some precaching and then something to happen after _load::main"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <function>: "
"Example: post_load_precache( ::helicopter_attack_system )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

post_load_precache( function )
{
	if ( !IsDefined( level.post_load_funcs ) )
		level.post_load_funcs = [];
	level.post_load_funcs = array_add( level.post_load_funcs, function );
}

/*
=============
///ScriptDocBegin
"Name: game_is_current_gen()"
"Summary: returns true if the running game is current gen, false if next gen or PC"
"Module: Utility"
"Example: if ( game_is_current_gen() ) ..."
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

game_is_current_gen()
{
	//level.ps3 level.ps4 level.xb3 level.xenon
	if( level.xenon )
		return true;
	if( level.ps3 )
		return true;
	
	return false;
}

//workaround function for ( IWSIX-44267 ), Creates ugly pop when loading the savegame.
LerpFov_Saved( fov_dest, time )
{
	thread LerpFov_Saved_thread( fov_dest, time );
}

LerpFov_Saved_thread( fov_dest, time )
{
	self notify ( "new_lerp_Fov_Saved" );
	self endon ( "new_lerp_Fov_Saved" );
	self LerpFOV( fov_dest, time );
	wait time;
	SetSavedDvar( "cg_fov", fov_dest );
}

getDvarFloatDefault( dvarName, defaultValue)
{
	value=getDvar(dvarName);
	if (value!= "")
	{
		return float(value);
	}	
	return defaultValue;
}	

getDvarIntDefault( dvarName, defaultValue)
{
	value=getDvar(dvarName);
	if (value!= "")
	{
		return int(value);
	}	
	return defaultValue;
}	

/* 
=============
///ScriptDocBegin
"Name: ui_action_slot_force_active_on( <slot> )"
"Summary: This will activate the dpad icon for the action slot you are using. It will stay active until you call the 'off' version"
"Module: HUD"
"CallOn: none"
"MandatoryArg: <slot> Which of the four action slots are being forced on"
"Example: ui_action_slot_force_active_on( 2 )"
///ScriptDocEnd
============
 */
ui_action_slot_force_active_on( slot )
{
	dvarName = "ui_actionslot_" + slot + "_forceActive";
	SetDvar( dvarName, "on" );
}

/* 
=============
///ScriptDocBegin
"Name: ui_action_slot_force_active_off( <slot> )"
"Summary: This will DE-activate the dpad icon for the action slot you are using."
"Module: HUD"
"CallOn: none"
"MandatoryArg: <slot> Which of the four action slots are being forced off"
"Example: ui_action_slot_force_active_off( 2 )"
///ScriptDocEnd
============
 */
ui_action_slot_force_active_off( slot )
{
	dvarName = "ui_actionslot_" + slot + "_forceActive";
	SetDvar( dvarName, "turn_off" );
}


/* 
=============
///ScriptDocBegin
"Name: ui_action_slot_force_active_one_time( <slot> )"
"Summary: This will activate the dpad icon for an action slot ONE TIME and then respond to normal dpad requests"
"Module: HUD"
"CallOn: none"
"MandatoryArg: <slot> Which of the four action slots are being forced told to activate"
"Example: ui_action_slot_force_active_off( 2 )"
///ScriptDocEnd
============
 */
ui_action_slot_force_active_one_time( slot )
{
	dvarName = "ui_actionslot_" + slot + "_forceActive";
	SetDvar( dvarName, "onetime" );
}


HasTag( model, tag )
{
	partCount = GetNumParts( model );
	for ( i = 0; i < partCount; i++ )
	{
		if( toLower( GetPartName( model, i)) == toLower( tag ))
			return true;
	}
	return false;
}

/* 
=============
///ScriptDocBegin
"Name: stylized_center_text( <strings>, <time>, [glowcolor], [type speed],  )"
"Summary: Center text that matches the Chyron"
"Module: HUD"
"CallOn: none"
"MandatoryArg: <strings> string or an array of strings for each line"
"MandatoryArg: <time> duration that the centered text will be on screen for"
"OptionalArg: [glowcolor] RGB values for the glow outline of the text"
"OptionalArg: [type speed] It uses SetPulseFX, so the smaller the speed the quicker it prints"
"Example: stylized_center_text( &"INTROSCREEN_TIMEBEFORE", 4, ( 0.3, 0.6, 0.3 ) )"
///ScriptDocEnd
============
 */
stylized_center_text( strings, time, glowcolor, type_speed )
{
	if ( !IsArray( strings ) )
		strings = [ strings ];
	
	x = 320;
	y = 200;

	huds = [];
	foreach ( i, str in strings )
	{
		temp_huds = maps\_introscreen::stylized_line( str, time, x, y + ( i * 20 ), "center", glowcolor, type_speed );
		huds = array_combine( temp_huds, huds );
	}
	
	wait( time );
	maps\_introscreen::stylized_fadeout( huds, x, y, strings.size );
}

/*
 =============
///ScriptDocBegin
"Name: center_screen_text( lines )"
"Summary: Center screen text for level locations and info."
"Module: HUD"
"CallOn: none"
"MandatoryArg: <strings> string or an array of strings for each line"
"Example: center_screen_text( &"INTROSCREEN_TIMEBEFORE" )"
///ScriptDocEnd
============
 */
center_screen_text( lines )
{
	thread maps\_introscreen::center_screen_lines( lines );
}

/*
=============
///ScriptDocBegin
"Name: enable_s1_motionset( <onOff> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"Example: level.gideon enable_s1_motionset( true )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_s1_motionset( enabled )
{	
	if(!s1_motionset_avaliable())
	{
		Print("S1 motionset unavaliable.");
		return;
	}
	
	if ( IsDefined(self.mech) && self.mech )
		return;
	
	if(	!level.nextgen)
	{
		Print("S1 motionset features are only enabled for NG, and should never be called for CG levels.");
		return;
	}
	
	if(IsDefined(enabled) && enabled)
	{
		if(!isdefined(self.animArchetype) || self.animArchetype == "soldier")
		{
			self.animArchetype = "s1_soldier";
		}
	}
	else
	{
		if(!isdefined(self.animArchetype) || self.animArchetype == "s1_soldier")
		{
			self.animArchetype = "soldier";
		}
	}
}

s1_motionset_avaliable()
{
	//if(!IsDefined(anim.S1_motionset_enabled) || !anim.S1_motionset_enabled )
		//return false;
	if(level.nextgen)
	{
		// [nextgen-begin]
		return true;
		// [nextgen-end]
	}
	return false;
}

/*
=============
///ScriptDocBegin
"Name: ai_ignore_everything( )"
"Summary: Disables most normal behavior for an AI to allow them to temporarily do something canned/specific. Previous state restores via ai_unignore_everything()"
"Module: AI"
"CallOn: An actor"
"Example: enemyGuy ai_ignore_everything();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
ai_ignore_everything() // self = AI
{        
    if( IsDefined( self.script_drone ) )
		return;
    
    // Looks like unignore_everything wasn't called, clear first
    if ( IsDefined( self._ignore_settings_old ) )
        self ai_unignore_everything();
    
    self._ignore_settings_old = [];
    
    self.disableplayeradsloscheck 		= ai_save_ignore_setting( self.disableplayeradsloscheck, "disableplayeradsloscheck", true );
    self.ignoreall                  	= ai_save_ignore_setting( self.ignoreall, "ignoreall", true );        
    self.ignoreme                  		= ai_save_ignore_setting( self.ignoreme, "ignoreme", true );    
    self.grenadeAwareness          		= ai_save_ignore_setting( self.grenadeAwareness, "grenadeawareness", 0 );
	self.badplaceawareness				= ai_save_ignore_setting( self.badplaceawareness, "badplaceawareness", 0 );
    self.ignoreexplosionevents      	= ai_save_ignore_setting( self.ignoreexplosionevents, "ignoreexplosionevents", true );
    self.ignorerandombulletdamage 		= ai_save_ignore_setting( self.ignorerandombulletdamage, "ignorerandombulletdamage", true );
    self.ignoresuppression          	= ai_save_ignore_setting( self.ignoresuppression, "ignoresuppression", true );
    self.dontavoidplayer          		= ai_save_ignore_setting( self.dontavoidplayer, "dontavoidplayer", true );
    self.newEnemyReactionDistSq      	= ai_save_ignore_setting( self.newEnemyReactionDistSq, "newEnemyReactionDistSq", 0 );
    self.disableBulletWhizbyReaction 	= ai_save_ignore_setting( self.disableBulletWhizbyReaction, "disableBulletWhizbyReaction", true );
    self.disableFriendlyFireReaction 	= ai_save_ignore_setting( self.disableFriendlyFireReaction, "disableFriendlyFireReaction", true );
    self.dontMelee                   	= ai_save_ignore_setting( self.dontMelee, "dontMelee", true );
    self.flashBangImmunity           	= ai_save_ignore_setting( self.flashBangImmunity, "flashBangImmunity", true );
    self.doDangerReact             		= ai_save_ignore_setting( self.doDangerReact, "doDangerReact", false );    
    self.neverSprintForVariation 		= ai_save_ignore_setting( self.neverSprintForVariation, "neverSprintForVariation", true );
    self.a.disablePain 					= ai_save_ignore_setting( self.a.disablePain, "a.disablePain", true );
    self.allowPain       				= ai_save_ignore_setting( self.allowPain, "allowPain", false );
	self.fixednode 						= ai_save_ignore_setting( self.fixednode, "fixedNode", 1 );
	self.script_forcegoal 				= ai_save_ignore_setting( self.script_forcegoal, "script_forcegoal", 1 );
	self.goalradius 					= ai_save_ignore_setting( self.goalradius, "goalradius", 5 );

	self disable_ai_color(); // automatically keeps its old state
}

/*
=============
///ScriptDocBegin
"Name: ai_unignore_everything( <dont_restore_old> )"
"Summary: Reverts an actor to is previous settings before ai_ignore_everything()"
"Module: AI"
"CallOn: An actor"
"OptionalArg: <dont_restore_old>: If true it will throw away the previously saved values and just use defaults"
"Example: enemyGuy ai_unignore_everything();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
ai_unignore_everything( dont_restore_old ) // self = AI
{
    if( IsDefined( self.script_drone ) )
   		return;
        
    // Don't restore old settings
    if ( IsDefined( dont_restore_old ) && dont_restore_old )
        if ( IsDefined( self._ignore_settings_old ) )
            self._ignore_settings_old = undefined;
            
    self.disableplayeradsloscheck 		= ai_restore_ignore_setting( "disableplayeradsloscheck", false );
    self.ignoreall                  	= ai_restore_ignore_setting( "ignoreall", false );        
    self.ignoreme                  		= ai_restore_ignore_setting( "ignoreme", false );    
    self.grenadeAwareness          		= ai_restore_ignore_setting( "grenadeawareness", 1 );
    self.badplaceAwareness          	= ai_restore_ignore_setting( "badplaceawareness", 1 );
    self.ignoreexplosionevents      	= ai_restore_ignore_setting( "ignoreexplosionevents", false );
    self.ignorerandombulletdamage 		= ai_restore_ignore_setting( "ignorerandombulletdamage", false );
    self.ignoresuppression          	= ai_restore_ignore_setting( "ignoresuppression", false );
    self.dontavoidplayer          		= ai_restore_ignore_setting( "dontavoidplayer", false );
    self.newEnemyReactionDistSq      	= ai_restore_ignore_setting( "newEnemyReactionDistSq", 262144 );
    self.disableBulletWhizbyReaction 	= ai_restore_ignore_setting( "disableBulletWhizbyReaction", undefined );
    self.disableFriendlyFireReaction 	= ai_restore_ignore_setting( "disableFriendlyFireReaction", undefined );
    self.dontMelee                  	= ai_restore_ignore_setting( "dontMelee", undefined );
    self.flashBangImmunity           	= ai_restore_ignore_setting( "flashBangImmunity", undefined );
    self.doDangerReact             		= ai_restore_ignore_setting( "doDangerReact", true );
    self.neverSprintForVariation		= ai_restore_ignore_setting( "neverSprintForVariation", undefined );
    self.a.disablePain 					= ai_restore_ignore_setting( "a.disablePain", false );
    self.allowPain       				= ai_restore_ignore_setting( "allowPain", true );
	self.fixednode 						= ai_restore_ignore_setting( "fixedNode", 0 );
	self.script_forcegoal 				= ai_restore_ignore_setting( "script_forcegoal", 0 );
	self.goalradius 					= ai_restore_ignore_setting( "goalradius", 100 );
	
	self enable_ai_color(); // automatically keeps its old state
	            
    self._ignore_settings_old = undefined;
}

/*
=============
///ScriptDocBegin
"Name: attach_player_current_weapon_to_anim_tag( <amim_tag> )"
"Summary: Attach the players current weapon view model to an animation tag. Includes attachments."
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <amim_tag>: the anim tag you want to attach the player's gun to."
"Example: anim_rig attach_player_current_weapon_to_anim_tag( "tag_weapon" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/

attach_player_current_weapon_to_anim_tag( anim_tag )
{
	weapon = level.player GetCurrentWeapon();
	
	weapon_and_attachment_models_array = GetWeaponAndAttachmentModels( weapon );
	
	// item 0 is the gun itself
	weapon_viewmodel = weapon_and_attachment_models_array[0][ "weapon" ];
	
	// items 1 and up are any attachment models that need to be added
	attachments = array_remove_index( weapon_and_attachment_models_array, 0 );
	
	self Attach( weapon_viewmodel, anim_tag, true);

	foreach ( attachment in attachments )
	{
		self Attach( attachment[ "attachment" ], attachment[ "attachTag" ] );
	}	
	 
	self HideWeaponTags( weapon );
}
/*
=============
// Player Allow
=============
*/
playerAllowAlternateMelee( enable, type )
{
	self _playerAllow( "altmelee", enable, type, ::_allowAlternateMelee, false );
}

_allowAlternateMelee( allow )
{
	if ( allow )
		self EnableAlternateMelee();
	else
		self DisableAlternateMelee();
}

playerAllowWeaponPickup( enable, type )
{
	self _playerAllow( "weaponPickup", enable, type, ::_allowWeaponPickup, false );	
}

_allowWeaponPickup( allow )
{
	if ( allow )
		self EnableWeaponPickup();
	else
		self DisableWeaponPickup();
}

_playerAllow(ability, enable, type, enableFunc, funcIsBuiltin)
{
	if ( !IsDefined(self.playerDisableAbilityTypes) )
		self.playerDisableAbilityTypes = [];
	
	if ( !IsDefined(self.playerDisableAbilityTypes[ability]) )
	{
		self.playerDisableAbilityTypes[ability] = [];
	}
	
	if ( !IsDefined( type ) )
		type = "default";
	
	if ( enable )
	{
		self.playerDisableAbilityTypes[ability] = array_remove(self.playerDisableAbilityTypes[ability], type);
		if ( !self.playerDisableAbilityTypes[ability].size )
		{
			if ( !IsDefined( funcIsBuiltin ) || funcIsBuiltin )
				self call [[enableFunc]] ( true );
			else
				self [[enableFunc]] ( true );
		}
	}
	else
	{
		if ( !IsDefined( array_find( self.playerDisableAbilityTypes[ability], type ) ) )
			self.playerDisableAbilityTypes[ability] = array_add(self.playerDisableAbilityTypes[ability], type);
		
		if ( !IsDefined( funcIsBuiltin ) || funcIsBuiltin )
				self call [[enableFunc]] ( false );
			else
				self [[enableFunc]] ( false );
	}
}


/*
=============
///ScriptDocBegin
"Name: pretend_to_be_dead()"
"Summary: Sets various things for an AI so he appears to be dead despite still playing a scripted anim"
"Module: Utility"
"CallOn: an AI"
"Example: guy pretend_to_be_dead()"
"SPMP: both"
///ScriptDocEnd
=============
*/
pretend_to_be_dead()
{
	if(!IsAlive(self))
		return;
	
	self.pretending_to_be_dead = true;
	self SetThreatDetection( "disable" );
	self DisableAimAssist();
	self.ignoreme = true;
	self.IgnoreSonicAoE = true;
}

/*
=============
///ScriptDocBegin
"Name: tff_sync_setup()"
"Summary: Initializes and sets up transient sync related script. Call this in the level's main function"
"Module: Utility"
"CallOn: "
"Example: tff_sync_setup()"
"SPMP: SP"
///ScriptDocEnd
=============
*/
tff_sync_setup()
{
	PreCacheShader( "loading_animation" );
	flag_init( "tff_sync_complete" );
	_tff_sync_triggers();
}

/*
=============
///ScriptDocBegin
"Name: tff_sync( delay )"
"Summary: Performs a transient sync while displaying text on screen to inform player that a loading moment is occurring"
"Module: Utility"
"CallOn: "
"OptionalArg: <delay>: If defined will wait this time in seconds before performing transient sync"
"Example: tff_sync( 1 )"
"SPMP: SP"
///ScriptDocEnd
=============
*/
tff_sync(delay)
{
	if ( IsDefined( delay ) )
		wait( delay );
	
	if ( AreTransientsBusy() )
	{
		flag_clear( "tff_sync_complete" );
	
		SyncTransients();
		while ( AreTransientsBusy() )
			wait( 0.05 );
		
		flag_set( "tff_sync_complete" );
	}
}

/*
=============
///ScriptDocBegin
"Name: tff_sync_notetrack( guy, delay )"
"Summary: Wrapper function used for notetrack assignments to do transient sync.  ONLY use as a notetrack callback!"
"Module: Utility"
"CallOn: "
"OptionalArg: <delay>: If defined will wait this time in seconds before performing transient sync"
"Example: addNotetrack_customFunction( anim_name, notetrack, ::tff_sync_notetrack", anime_scene )
"SPMP: SP"
///ScriptDocEnd
=============
*/
tff_sync_notetrack( guy, delay )
{
	tff_sync( delay );
}

logBreadcrumbDataSP()
{
	level.player endon( "death" );
	
	while( 1 )
	{
		savedData = GetSPCheckpointData();
		lifeId = savedData[ 4 ];	// "deaths_total"
		levelTime = GetTime();
		
		RecordBreadCrumbDataForPlayerSP( level.player, lifeId, levelTime );
		wait 2;		// must match #define MATCH_SAMPLE_SECS in code
	}	
}
