// This script contains functionality to allow bots to mount vehicles
this.Name = "MOUNT";
this.Parent = "HighLevel";
this.GetPriorityDelay = 3;		// Delay between calls to GetPriority
this.SkipGetPriorityWhenActive = true;
this.NotDelayed = true;

this.Timeout = 30;
this.AimAdjust = 0; // start time
this.AimAdjustDelay = 1.5;

this.OnSpawn = function()
{
	// don't consider this goal right away, because ET takes a few frame to initialize entities
	// so vehicles will be alive temporarily at the start of the map for a few frames
	if (this.NotDelayed) {
		this.NotDelayed = null;
		this.DelayGetPriority(3);
	}
};

this.GetPriority = function()
{
	Util.GetPriority(this,0x6c166aba /* MOUNT */,0,0, function(goal)
	{
		if (!goal.Vehicle) {
			goal.DisableGoal(true);
			Util.ScriptDebugPrint(goal.GetName(), "disabled", "no vehicle defined!");
			return;
		}

		vehGoal = GetGoal(goal.Vehicle);
		if (!vehGoal) {
			goal.DisableGoal(true);
			Util.ScriptDebugPrint(goal.GetName(), "disabled", "invalid vehicle defined!");
			return;
		}

		vehEnt = vehGoal.GetEntity();
		if (!vehEnt || !GetEntFlags(vehEnt, ENTFLAG.MOUNTABLE)) {
			return;
		}

		if (GetEntFlags(vehEnt, ENTFLAG.DEAD)) {
			if (!Map.InvVehicle || !Map.InvVehicle[ vehEnt ]) {
				return;
			}
		}

		//we don't know if mounted player is friend or enemy
		//mod interface doesn't have method to get mounted player on tank
		//bots would look stupid if they would repeatedly try to mount tank which is already mounted by allied player
		if (GetEntFlags(vehEnt, ENTFLAG.MOUNTED)) {
			return;
		}

		return true;
	});
};

this.Enter = function()
{
	this.MarkInProgress();

	this.VehicleGoal = GetGoal(this.MapGoal.Vehicle);
	this.VehicleEnt = this.VehicleGoal.GetEntity();
	if (!this.VehicleEnt) { this.Finished(); }

	// bad things happen if it's inv and dead and we auto finish the goal
	if (!Map.InvVehicle || !Map.InvVehicle[ this.VehicleEnt ]) {
		this.AddFinishCriteria(this.VehicleEnt,"hasentflag",ENTFLAG.DEAD);
	}

	// ignore the goal as a target in case it's the opposite team mounting it
	this.Bot.IgnoreTarget(this.VehicleEnt, 9999);

	this.AimTime = 0;
};

this.Exit = function()
{
	// stop ignoring it as a target
	if (this.VehicleEnt && GetEntTeam(this.VehicleEnt) && !this.MapGoal.IgnoreTarget) {
		this.Bot.IgnoreTarget(this.VehicleEnt, 0.1);
	}

	if (this.UseTime > GetTime() - 0.1){
		//wait 2 frames because ENTFLAG.MOUNTED is false immediately after mount
		sleep(0.1);
	}
	if (this.Bot && this.Bot.HasEntityFlag(ENTFLAG.MOUNTED)) {
		this.Bot.PressButton(BTN.USE); // dismount
	}
};

this.SetAimMode = function()
{
	mode = this.MapGoal.AimMode;
	if (IsTable(mode)) {
		currentTime = GetTime();
		if(this.AimTime <= currentTime)
		{
			this.AimValue = Util.GetRandomTableValue(mode);
			this.AimTime = currentTime + this.AimAdjustDelay;
		}
		mode = this.AimValue;
	}

	// is it a position?
	if (IsVec3(mode)) {
		this.AimPosition = mode;
		this.UseAimMode = null;
	}
	else
	{
		switch(mode)
		{
			case "random":
			{
				this.UseAimMode = this.AimModes.random360;
			}
			case "opposite":
			{
				this.AimOpposite = true;
				this.UseAimMode = this.AimModes.vehicleVelocity;
			}
			default: //"velocity"
			{
				this.AimOpposite = false;
				this.UseAimMode = this.AimModes.vehicleVelocity;
			}
		}
	}
};

this.Update = function()
{
	botEnt = this.Bot.GetGameEntity();

	if (!GetEntFlags(botEnt, ENTFLAG.MOUNTED))
	{
		if (this.Bot.DistanceTo(this.VehicleEnt) > 200)
		{
			pathSuccess = false;
			offsetCount = 0;

			// are offsets set up for this goal?
			if (this.MapGoal.Offsets) {
				offsetCount = tableCount(this.MapGoal.Offsets);
			}

			// use em if we got em
			if (offsetCount > 0) {
				startIndex = this.MapGoal.StartIndex;

				// randomly select a start index if one isn't defined or invalid
				if (!IsInt(startIndex) || startIndex < 0 || startIndex >= offsetCount) {
					startIndex = RandInt(0, offsetCount - 1);
					if (this.Debug) { print(this.MapGoal.GetName(), "selected random start index of", startIndex); }
				}
				else {
					if (this.Debug) { print(this.MapGoal.GetName(), "user defined start index of", startIndex); }
				}

				// loop through from the start index until we get a good path
				for (i = 0; i < offsetCount; ++i)
				{
					ind = (startIndex + i) % offsetCount;

					// if it's excluded, skip it
					if (this.MapGoal.ExcludeIndexes && !IsNull(Util.GetTableKey(this.MapGoal.ExcludeIndexes, ind))) {
						if (this.Debug) { print(this.Name, "skipping index", ind); }
						continue;
					}

					// and finally check and see if the bot can get there
					offsetPos = GetEntityWorldSpace(this.VehicleEnt, this.MapGoal.Offsets[ ind ]);
					if (this.Goto(offsetPos, 32.0) != EVENT.PATH_FAILED || this.Bot.DistanceTo(this.VehicleEnt) < 200) {
						pathSuccess = true;
						break;
					}
				}
			}
			else if (this.RouteTo(this.VehicleGoal, 128.0) == EVENT.PATH_SUCCESS) {
				pathSuccess = true;
			}

			if(!pathSuccess)
			{
				Util.PathFailed(this);
				this.Finished();
			}
		}

		// bot can't shoot if he mounts mg42 while reloading,
		// because RELOADING entity flag is not cleared (probably bug in the interface)
		while (this.Bot.HasEntityFlag(ENTFLAG.RELOADING)) {
			sleep(0.2);
		}

		timeout = 0;
		while (!GetEntFlags(botEnt, ENTFLAG.MOUNTED))
		{
			if (timeout > 10) {
				this.BlackboardDelay(30);
				Util.ScriptDebugPrint(this.MapGoal.GetName(), "timedout");
				this.Finished();
			}
			vehPos = GetEntPosition(this.VehicleEnt);
			if(!vehPos)
			{
				this.Finished();
			}
			// Still available, so lets go for it.
			this.AddAimRequest(Priority.High, "position", vehPos);

			// Not mounted, lets try to get mounted.
			this.Bot.MoveTowards(vehPos);

			this.Bot.PressButton(BTN.USE);
			this.UseTime = GetTime();

			// if they got there and it's mounted, bail out
			// but only delay it a couple seconds in case its the other
			// team that has it mounted
			if (GetEntFlags(this.VehicleEnt, ENTFLAG.MOUNTED))
			{
				this.BlackboardDelay(2);
				this.Finished();
			}

			sleep(0.5);
			++timeout;
		}
	}

	// a successful mount, so mark in use
	this.MarkInUse();

	mountcounter = 0;

	for(;;)
	{
		this.SetAimMode();

		// Allow the current aim mode to do any processing to change the aimDirection
		if (this.UseAimMode)
		{
			aimFunc = this.UseAimMode;
			this:aimFunc();
		}
		else
		{
			this.AddAimRequest(Priority.Low, "position", this.AimPosition);
		}

		if (GetEntVelocity(this.VehicleEnt).IsZero())
		{
			++mountcounter;

			if ((mountcounter / 20) == this.Timeout)
			{
				this.BlackboardDelay(10);
				this.Finished();
			}
		}
		else
		{
			mountcounter = 0;
		}
		yield();
	}
};

this.AimModes =
{
	// vehicleVelocity makes the bot aim in the direction the vehicle is moving
	vehicleVelocity = function()
	{
		vehicleVelocity = GetEntVelocity(this.VehicleEnt);

		if(!vehicleVelocity || vehicleVelocity.IsZero()) {
			vehicleVelocity = GetEntFacing(this.VehicleEnt);
		}

		if (this.AimOpposite) {
			vehicleVelocity = vehicleVelocity * Vec3(-1,-1,-1);
		}
		this.AddAimRequest(Priority.Low, "facing", vehicleVelocity);
	},

	// random360 will allow the bot to aim in random directions in a 360 degree arc, changing direction at defined intervals.
	random360 = function()
	{
		// Is it time to adjust our aim?
		currentTime = GetTime();
		if(this.AimAdjust <= currentTime)
		{
			rand = RandRange(0.0, 360.0);
			v = Vec3FromSpherical(rand, 0, rand);

			this.AddAimRequest(Priority.Low, "facing", v);

			// Schedule the next aim.
			this.AimAdjust = currentTime + this.AimAdjustDelay;
		}
	},
};
