// 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()
{
	if(!this.MarkInProgress()) { this.Finished(); }

	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()
{
	if (this.Bot.HasEntityFlag(ENTFLAG.MOUNTED)) {
		this.Bot.PressButton(BTN.USE); // dismount
	}

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

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 += 1 )
				{
					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);

			// 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 += 1;
		}
	}

	// a successful mount, so mark in use
	if(!this.MarkInUse()) { this.Finished(); }

	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 += 1;

			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 * Vector3(-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;
		}
	},
};
