// This script contains functionality to allow bots to ride vehicles
this.Name = "RIDE";
this.Parent = "HighLevel";
this.SkipGetPriorityWhenActive = true;
this.NotDelayed = true;
this.Debug = false;

this.Initialize = function()
{
	this.OnTarget = false;
	this.Timeout = 30;
};

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,0x541798f /* RIDE */);
};

this.Enter = function()
{
	if(!this.MarkInProgress()) { this.Finished(); }

	goal = this.MapGoal;
	if ( !goal.Vehicle ) {
		Util.InvalidGoal(this, "no vehicle defined!");
	}
	this.VehicleGoal = GetGoal(goal.Vehicle);
	if (!this.VehicleGoal) {
		Util.InvalidGoal(this, "invalid vehicle defined!");
	}

	this.VehicleEnt = this.VehicleGoal.GetEntity();

	this.Seat = Util.GetRandomTableValue(this.MapGoal.Seats);

	if(!this.MapGoal.SeatRadius || this.MapGoal.SeatRadius < 60) {
		this.MapGoal.SeatRadius = 60;
	}

	this.DynamicGotoThread = this.ForkThread(this.DynamicGoto);
};

this.Exit = function()
{
	if ( this.DynamicGotoThread )
		{ this.KillThread(this.DynamicGotoThread); }

	if (this.CombatMovementDisabled) {
		this.Bot.DisableCombatMovement = false;
		this.CombatMovementDisabled = false;
	}
	this.OnTarget = false;
};

this.DynamicGoto = function()
{
	x = 0;
	startIndex = 0;
	offsetCount = tableCount(this.MapGoal.BoardPositions);
	offsetPos = null;
	pathSuccess = false;

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

		// at the end of the list? continue looping from 0 if so
		if ( ind > offsetCount - 1 ) {
			ind = 0 + x;
			x += 1;
		}

		// if it's excluded, skip it
		if ( this.MapGoal.ExcludeIndexes && typeId(Util.GetTableKey(this.MapGoal.ExcludeIndexes, ind)) != 0 ) {
			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.BoardPositions[ ind ]);
		if ( this.Goto(offsetPos, 32.0) != EVENT.PATH_FAILED ) {
			this.BoardPositionIndex = ind;
			this.BoardPosition = offsetPos;
			pathSuccess = true;
			break;
		}
		else {
			if ( this.Debug ) {
				print( this.MapGoal.GetName(), "path failed for index", ind);
			}
		}
	}

	if(pathSuccess) {
		// disable combat movement so they can board better
		this.CombatMovementDisabled = true;
		this.Bot.DisableCombatMovement = true;

		this.OnTarget = true;
	}
	else
	{
		Util.PathFailed(this);
		this.Finished();
	}
};

this.Update = function()
{
	while(1)
	{
		ridecounter = 0;

		// update the board position
		newPos = this.GetBoardPosition(this.MapGoal.BoardPositions[ this.BoardPositionIndex ]);

		if( newPos != this.BoardPosition )
		{
			this.Bot.HoldButton(BTN.SPRINT, 1.0);
			if ( this.DynamicGotoThread )
				{ this.KillThread(this.DynamicGotoThread); }
			this.BoardPosition = newPos;
			this.DynamicGotoThread = this.ForkThread(this.DynamicGoto);
		}

		if( this.OnTarget )
		{
			this.RidePosition = this.GetRidePosition(this.Seat);
			timeout = 0;
			boardingfailed = false;

			// Attempt to board the vehicle
			while( !this.Bot.MoveTowards( this.RidePosition ) )
			{
				this.AddAimRequest( Priority.Medium, "position", this.RidePosition );
				this.RidePosition = this.GetRidePosition(this.Seat);
				if ( timeout == 5000 )
				{
					boardingfailed = true;
					break;
				}

				timeout = timeout + 50;

				myPos = this.Bot.GetPosition();
				if(timeout > 200 && myPos.z < this.RidePosition.z)
				{
					this.Bot.HoldButton(BTN.JUMP, 0.1);
				}

				yield();
			}

			if(boardingfailed)
			{
				// Finish the state.
				this.Finished();
			}

			// it's riding, so mark in use
			if(!this.MarkInUse()) { this.Finished(); }

			// Ride cowboy ride
			while(true)
			{
				this.RidePosition = this.GetRidePosition(this.Seat);
				this.Bot.MoveTowards(this.RidePosition);

				switch(this.MapGoal.Stance)
				{
					case "crouch":
					{
						this.Bot.HoldButton(BTN.CROUCH, 0.5);
					}
					case "prone":
					{
						this.Bot.HoldButton(BTN.PRONE, 0.5);
					}
				}

				if ( this.Bot.DistanceTo(this.RidePosition) > this.MapGoal.SeatRadius ) {

					if ( this.Debug ) {
						print( "outside radius: current dist: " + this.Bot.DistanceTo(this.RidePosition) +
							" seat radius " + this.MapGoal.SeatRadius );
					}
					this.Finished();
				}

				this.GetNewVehicleLookPosition();

				if ( GetEntVelocity( this.VehicleEnt ).IsZero() )
				{
					ridecounter += 1;

					if ( ( ridecounter / 20 ) == this.Timeout )
					{
						this.Finished();
					}
				}
				else
				{
					ridecounter = 0;
				}

				yield();
			}

			this.Finished();
		}

		sleep(1);
	}
};

//////////////////////////////////////////////////////////////////////////

this.GetRidePosition = function(seatPos)
{
	vehicle = this.MapGoal.Vehicle;
	if(vehicle && seatPos && !seatPos.IsZero())
	{
		vehGoal = GetGoal(vehicle);
		absPos = GetEntityWorldSpace(vehGoal.GetEntity(),seatPos);
		return absPos;
	}
};

this.GetBoardPosition = function(boardPos)
{
	vehicle = this.MapGoal.Vehicle;
	if(vehicle && boardPos && !boardPos.IsZero())
	{
		vehGoal = GetGoal(vehicle);
		absPos = GetEntityWorldSpace(vehGoal.GetEntity(),boardPos);
		return absPos;
	}
};

this.GetNewVehicleLookPosition = function()
{
	vehicleVelocity = GetEntVelocity(this.VehicleEnt);

	if(vehicleVelocity && !vehicleVelocity.IsZero())
	{
		this.AddAimRequest(Priority.Low, "facing", vehicleVelocity);
	}
	else
	{
		vehFacing = GetEntFacing( this.VehicleEnt );
		this.AddAimRequest(Priority.Low, "facing", vehFacing);
	}
};

