/**** LEAPFROG SYSTEM ****

	Thread maps\_leapfrog::leapfrog() on any AI that uses a chain of nodes.
	Make sure to check the NOT_CHAIN check box on each node in the chain to allow chains to merge or loop.
	You need at least two chains for this to look good.
	level.leap_delay_min and max determines how fast they try to advance.
	set script_delay on a node to have the ai use a delay instead of waiting for a leap notify.
	script_delay = 0 will make them run past that node before going to the next one.

*************************/

#include maps\_utility;

main()
{
	//	Set delays to tweak how fast the advance should be.
	level._leap_delay_min = 6;
	level._leap_delay_max = 14;
	level._leap_delay_override = false;

	// there will never be more ai on one leap node then what this is set to.
	level._leapfrog_max_node_ai = 6;

	level._leap_node_array = [];
	level._leapfrog_random_int = randomint( 5 );

	// lower threat threatbias group for when leaping.
	createthreatbiasgroup( "leapfrog" );
	setthreatbiasagainstall( "leapfrog", -200 );

	level thread leapfrog_masterthread();
}

leapfrog_masterthread()
{
	while ( true )
	{
		if ( !level._leap_delay_override )
			wait( randomFloatRange( level._leap_delay_min, level._leap_delay_max ) );
		else
			wait .05;

		level._leap_delay_override = false;

		// used to make ai take the same fork in a path when script_delay is set.
		level._leapfrog_random_int = randomint( 5 );

		node_arr = [];
		high_weight = -1000000;

		if ( !level._leap_node_array.size )
			continue;

		for ( i = 0; i < level._leap_node_array.size; i++ )
		{
			weight = level._leap_node_array[ i ].leap_weight;
			if ( !isdefined( node_arr[ weight ] ) )
				node_arr[ weight ] = [];
			node_arr[ weight ][ node_arr[ weight ].size ] = level._leap_node_array[ i ];
			if ( weight > high_weight )
				high_weight = weight;
		}

		assertEx( isdefined( node_arr[ high_weight ] ), "high_weight is: " + high_weight );
		assertEx( isdefined( high_weight >= 0 ), "high_weight is below zero: " + high_weight );

		node = node_arr[ high_weight ][ randomint( node_arr[ high_weight ].size ) ];

		assert( isdefined( node.target ) );// there should always be a new node or it shouldn't be in the array.

		node_arr = getnodearray( node.target, "targetname" );
		next_node = node_arr[ randomint( node_arr.size ) ];

		// reset future ai count
		if ( isdefined( next_node.leapfrog_ai_count ) )
			next_node.leapfrog_future_ai_count = next_node.leapfrog_ai_count;
		else
			next_node.leapfrog_future_ai_count = 0;

		// increase the weight of all none chosen nodes.
		array_thread( level._leap_node_array, ::increment_leap_weight, node );

		new_weight =  int( node.leap_weight * - .25 );

		if ( isdefined( next_node.leap_weight ) )
			new_weight += next_node.leap_weight;

		add_leap_node( next_node, new_weight );

		node notify( "leapfrog", next_node );
		remove_leap_node( node );
	}
}

increment_leap_weight( node )
{
	if ( self == node )
		return;

	diff_weight = node.leap_weight - self.leap_weight;

	self.leap_weight += ( int( diff_weight * 0.5 ) + 1 );// old .75;
}

leapfrog()
{
	self endon( "death" );
	self endon( "stop_leapfrog" );
	self notify( "stop_going_to_node" );

	// get first node
	node_arr = getnodearray( self.target, "targetname" );
	node = node_arr[ randomint( node_arr.size ) ];

	while ( true )
	{
		if ( node.radius != 0 )
			self.goalradius = node.radius;
		if ( isdefined( node.height ) )
			self.goalheight = node.height;
		self setgoalnode( node );

		old_maxsightdistsqrd = self.maxsightdistsqrd;
		self.maxsightdistsqrd = 350 * 350;
		old_group = self getthreatbiasgroup();
		self setthreatbiasgroup( "leapfrog" );

		self waittill( "goal" );

		// Notify the node and pass the guy. Might be good for something
		node notify( "trigger", self );

		self.maxsightdistsqrd = old_maxsightdistsqrd;
		self setthreatbiasgroup( old_group );

		self thread leapfrog_on_death( node );

		if ( !isdefined( node.target ) )
			break;

		if ( isdefined( node.script_delay ) )
		{
			node script_delay();
			node_arr = getnodearray( node.target, "targetname" );
			next_node = node_arr[ level._leapfrog_random_int % node_arr.size ];
		}
		else
		{
			if ( !add_leap_node( node ) )
				break;

			node waittill( "leapfrog", next_node );

			next_node.leapfrog_future_ai_count++ ;

			max_node_ai = level._leapfrog_max_node_ai;
			if ( isdefined( node.script_noteworthy ) )
				max_node_ai = int( node.script_noteworthy );

			if ( next_node.leapfrog_future_ai_count > max_node_ai )
			{
				level._leap_delay_override = true;
				if ( isdefined( next_node.leap_weight ) )
				{
					next_node.leap_weight += 1;	// make the full node more likely to leap.
				}
				next_node = node;	// stay on old node.
			}
		}

		node = next_node;
	}

	// notify level and pass the guy that reached his final leapfrog node.
	level notify( "leapfrog_completed", self );
}

leapfrog_on_death( node )
{
	node endon( "leapfrog" );

	if ( !isdefined( node.leapfrog_ai_count ) )
		node.leapfrog_ai_count = 0;
	node.leapfrog_ai_count++ ;

	self waittill( "death" );

	node.leapfrog_ai_count -- ;

	if ( isdefined( node.leap_weight ) )
	{
		new_weight = node.leap_weight - 1;
		if ( new_weight < 1 )
			new_weight = 1;
		node.leap_weight = new_weight;
	}

	if ( !node.leapfrog_ai_count )
		remove_leap_node( node );
}

add_leap_node( node, weight )
{
	if ( !isdefined( node.target ) || isdefined( node.script_delay ) )
		return false;

	if ( getdvar( "debug" ) == "1" )
		node thread debug_leap_node();

	if ( !is_in_array( level._leap_node_array, node ) )
	{
		level._leap_node_array = array_add( level._leap_node_array, node );
		node.leap_weight = 0;
	}

	if ( isdefined( weight ) )
		node.leap_weight = weight;
	else
		node.leap_weight += 2;

	return true;
}

remove_leap_node( node )
{
	node.leap_weight = undefined;
	node.leapfrog_ai_count = undefined;

	level._leap_node_array = array_remove( level._leap_node_array, node );
}

/* debug stuff */

debug_leap_node()
{
	if ( isdefined( self.debug_leapnode ) )
		return;
	self.debug_leapnode = true;

	while ( true )
	{
		if ( isdefined( self.leap_weight ) )
			self thread print3Dmessage( self.leap_weight, .5 );
		if ( isdefined( self.leapfrog_ai_count ) )
			self thread print3Dmessage( self.leapfrog_ai_count, 0.5, ( 1, 0, 0 ), ( 0, 0, 128 ), 3 );
		wait .5;
	}
}

print3Dmessage( message, show_time, color, offset, scale )
{
	if ( !isdefined( color ) )
		color = ( 0.5, 1, 0.5 );

	if ( !isdefined( offset ) )
		offset = ( 0, 0, 56 );

	if ( !isdefined( scale ) )
		scale = 6;

	show_time = gettime() + ( show_time * 1000 );
	while ( gettime() < show_time )
	{
		print3d( self.origin + offset, message, color, 1, scale );
		wait( 0.05 );
	}
}
