// This script is the paththrough version of goal_callartillery

this.Name = "Artillery_PT";
this.Parent = "LowLevel";       // PathThrough states must be low level or they will not be used.
this.Debug = false;
this.DebugLos = false;

this.OnPathThrough = function( data )
{
	if ( !data ) {
		return false;
	}
	if ( this.IsActive() ) {
		return false;
	}

	if ( this.Bot.GetClass() != CLASS.FIELDOPS ) {
		sleep( 1 ); // give time to move off the wp
		return false;
	}

	if ( !this.Bot.IsWeaponCharged( WEAPON.BINOCULARS ) ) {
		sleep( 1 );
		return false;
	}

	// cs: only allow it every 5 seconds to handle bad spots.
	time = GetTime();
	if ( this.Bot.ptArtyTime && time - this.Bot.ptArtyTime < 5 ) {
		return false;
	}

	this.Bot.ptArtyTime = time;

	// cache info about this pt spot
	if ( !Map.PTArtyInfo ) {
		Map.PTArtyInfo = {};
	}
	if ( !Map.PTArtyInfo[ data ] ) {
		Map.PTArtyInfo[ data ] = {};
	}
	if ( !Map.PTArtyInfo[ data ].WpInfo ) {
		Map.PTArtyInfo[ data ].WpInfo = Util.WpNameInfo( data );

		// cs: no wp info, create an empty table rather than re-checking every time.
		if ( !Map.PTArtyInfo[ data ].WpInfo ) {
			Map.PTArtyInfo[ data ].WpInfo = {};
		}
	}
	if ( !Map.PTArtyInfo[ data ].TraceSuccess ) {
		Map.PTArtyInfo[ data ].TraceSuccess = {};
	}

	// cache successful traces in the form of a list of map goals
	this.Data = data;
	if ( this.FindTarget() == false ) {
		sleep( 1 ); // give time to move off the wp
		return false;
	}

	return true;
};

this.FindTarget = function()
{
	team = this.Bot.GetTeam();

	// cache the goal list
	if ( !Map.ArtyGoals ) {
		Map.ArtyGoals = {};
		GetGoals( Map.ArtyGoals, 0, "Artillery_S.*" );
	}

	if ( !Map.PTArtyInfo[ this.Data ].Cached ) {
		this.CacheTraceResults();
	}

	temp = {};
	i = 0;
	foreach( id and mg in Map.PTArtyInfo[ this.Data ].TraceSuccess )
	{
		if ( !mg.IsAvailable( team ) ) {
			continue;
		}

		temp[ i ] = mg;
		i += 1;
	}

	tc = tableCount( temp );
	if ( tc > 0 ) {
		x = RandInt( 0, tc - 1 );
	} else {
		return false;
	}

	this.TargetGoalName = temp[ x ].GetName();
	this.TargetGoalPosition = temp[ x ].GetPosition();

	return true;
};

this.CacheTraceResults = function()
{
	foreach( id and mg in Map.ArtyGoals )
	{
		pos = mg.GetPosition();

		if ( !Map.PTArtyInfo[ this.Data ].WpInfo || !Map.PTArtyInfo[ this.Data ].WpInfo.facing ) {
			if ( !this.Bot.InFieldOfView( pos, 180 ) ) {
				continue;
			}

			if ( !this.Bot.HasLineOfSightTo( pos ) ) {
				continue;
			}
		}
		else { // evaluate from the goto wp
			tr = TraceLine( Map.PTArtyInfo[ this.Data ].WpInfo.position + Vector3( 0,0,42 ), pos, null, TRACE.SHOT, this.Bot.GetGameId(), false );
			if ( tr.fraction < 1 ) {
				if ( this.DebugLos ) {
					DrawDebugLine( Map.PTArtyInfo[ this.Data ].WpInfo.position + Vector3( 0,0,42 ), pos, COLOR.RED, 30 );
				}
				continue;
			}
		}

		// cache any successful ones
		Map.PTArtyInfo[ this.Data ].TraceSuccess[ tableCount( Map.PTArtyInfo[ this.Data ].TraceSuccess ) ] = mg;
	}

	// cs: we can only cache ones with a valid wp name as data otherwise 'data' most likely is not unique
	// really need to pass the wp guid to the callback for indexing
	Map.PTArtyInfo[ this.Data ].Cached = !(!Map.PTArtyInfo[ this.Data ].WpInfo.facing);
};

this.Exit = function()
{
	this.TargetGoalName = null;
	this.TargetGoalPosition = null;
};

this.Update = function()
{
	if ( Map.PTArtyInfo[ this.Data ].WpInfo && Map.PTArtyInfo[ this.Data ].WpInfo.position ) {
		if ( this.Goto( Map.PTArtyInfo[ this.Data ].WpInfo.position ) == EVENT.PATH_FAILED ) {
			this.Finished();
		}
	}

	if ( !this.Bot.HasLineOfSightTo( this.TargetGoalPosition ) ) {
		if ( this.DebugLos ) {
			this.Bot.Say( "aborting pt arty, no los" );
		}
		sleep(1);
		this.Finished();
	}

	if ( this.Debug ) {
		this.Bot.Say( "CallArtillery_PT: I'm targeting ", this.TargetGoalName );
	}

	// call the artillery
	this.AddAimRequest( Priority.High, "position", this.TargetGoalPosition );
	this.AddWeaponRequest( Priority.High, WEAPON.BINOCULARS );
	this.BlockForWeaponChange( WEAPON.BINOCULARS );

	timeout = 21;
	this.Bot.HoldButton( BTN.AIM, 1.1 ); // 20 fps
	while ( timeout > 0 && this.Bot.IsWeaponCharged( WEAPON.BINOCULARS ) )
	{
		timeout -= 1;

		// keep trying every 3 frames
		if ( timeout % 3 == 0 ) {
			this.Bot.PressButton(BTN.ATTACK1);
		}

		if ( Map.PTArtyInfo[ this.Data ].WpInfo && Map.PTArtyInfo[ this.Data ].WpInfo.position ) {
			this.Bot.MoveTowards( Map.PTArtyInfo[ this.Data ].WpInfo.position );
		} else {
			this.Goto( this.Bot.GetPosition() );
		}

		yield();
	}

	// cs: Log the position if they still have a charge?

	this.Bot.ReleaseButton( BTN.AIM );

	// Finish the state.
	this.Finished();
};

