// This script contains functionality to jump or kill stucked bot
this.Name = "CheckStuck";
this.Parent = "LowLevel";
this.AutoAdd = true;
this.KillWhenStuck = true;
this.LogStuckage = true;
this.Interval = 0.6;
this.KillTime = 5.0; // time before /killing
this.TargetTimeout = 10; // time to destroy breakables or kill enemies
this.SidestepTimeout = 3; // time to strafe left to sidestep another player
this.JumpLimit = 1;
this.Debug = false;

this.GetPriority = function()
{
	stuckPos = null;
	stucktime = 0;

	while(1)
	{
		if ( this.JustSpawned ) {
			for (i = 0; i < 3; i += 1) {
				sleep(1.2);
				// player cannot move while standing up after he is revived,
				// reset stuck timer to prevent pathfailed errors immediately after revive
				this.Bot.ResetStuckTime();
			}
			this.JustSpawned = false;
		}

		if ( this.Bot.IsStuck(this.Interval) )
		{
			if (stucktime == 0) {
				jumpAttempts = 0;
				waitTime = 0;
				stuckPos = this.Bot.GetPosition();
			}

			// stuck time gets reset for each new path, so lets accumulate our own
			stucktime += this.Interval;

			if ( this.Debug ) {
				this.Bot.Say("I've been stuck for " + stucktime + " seconds");
			}

			// is the bot blocked by a player?
			nearestPlayer = this.Bot.GetNearest(CAT.PLAYER);
			if ( nearestPlayer && this.Bot.DistanceTo(nearestPlayer) < 65 && this.Bot.InFieldOfView(GetEntPosition(nearestPlayer), 180.0) && stucktime < this.SidestepTimeout)
			{
				if ( this.Debug ) {
					DrawEntityAABB(nearestPlayer, 30.0, COLOR.RED);
					DrawEntityAABB(this.Bot.GetGameEntity(), 30.0, COLOR.BLUE);
				}
				if (GetEntFlags(nearestPlayer, ENTFLAG.PRONE))
				{
					this.Bot.PressButton(BTN.JUMP);

					if ( this.Debug ) {
						this.Bot.Say("jump over proned player");
					}
				}
				else
				{
					this.Bot.HoldButton(BTN.STRAFE_L, 0.5);

					if ( this.Debug ) {
						this.Bot.Say("side stepping player");
					}
				}

				// reset the stuck timer so they don't get a new goal
				this.Bot.ResetStuckTime();
				waitTime = stucktime;
				jumpAttempts = 9;
			}
			else if ( this.Bot.HasTarget() && stucktime < this.TargetTimeout)
			{
				if (jumpAttempts < 9 && GetEntClass(this.Bot.GetTarget())==CLASS.BREAKABLE) {
					//don't jump at breakables
					jumpAttempts = 9;
				}
				this.Bot.ResetStuckTime();
				waitTime = stucktime;
			}
			else if ( stucktime >= this.KillTime + waitTime )
			{
				//if (this.Bot.Name=="testbot"){ return; }

				if ( this.LogStuckage ) {
					// write it to the omni-bot log
					pos = this.Bot.GetPosition();
					yield();
					this.StuckageLog(pos);
				}

				yield();
				if ( this.KillWhenStuck ) {
					this.Bot.ExecCommand("kill");
				}
				stucktime = 0;
			}
			if ( jumpAttempts < this.JumpLimit)
			{
				this.Bot.PressButton(BTN.JUMP);
				jumpAttempts += 1;
			}
		}
		else
		{
			if ( stuckPos && this.Bot.DistanceTo(stuckPos) > 100 ) {
				stuckPos = null;
				stucktime = 0;
			}
		}

		sleep(this.Interval);
	}
};

this.OnSpawn = function()
{
	this.JustSpawned = true;
};

// Note: this will write to ~/omni-bot/et/user. the file will need to be copied to ~/omni-bot/et/scripts
this.StuckageLog = function(pos)
{
	gmfile = File();
	fileName = GetMapName() + "_stuckages.gm";

	if ( !System.FileExists(fileName) ) {
		assert( gmfile.Open( fileName, "text", false ) );
		head = "global Stuckages = {};";
		assert( gmfile.Write( head, System.NewLine ) );
	}
	else {
		assert( gmfile.Open( fileName, "text", false, true ) );
	}

	str = "Stuckages[ tableCount(Stuckages) ] = Vector3("+pos.x +","+pos.y+","+pos.z+");";
	assert( gmfile.Write( str, System.NewLine ) );
	gmfile.Close();
};
