// 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, 0xb708821b /* 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;
	}

	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 + Vec3(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 + Vec3(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(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;

		// 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();
};
