// This script contains functionality to allow bots to avoid grenades or airstrikes
this.Name = "WatchForProjectile";
this.Parent = "HighLevel";
this.AlwaysRecieveEvents = true;
this.Debug = false;

this.LimitToNoEntityFlag(ENTFLAG.INWATER, ENTFLAG.UNDERWATER);

this.AvoidOffsets = { 0, 1, -1, 2, -2, 4, -4, 8, -8 };

this.Initialize = function()
{
	this.WatchForEntityCategory({
		Radius = 400,
		Category = CAT.PROJECTILE,
		RequireLOS = TRACE.VISIBLE,
	});
};

this.Enter = function()
{
	if (!this.AvoidEnt) {
		this.Finished();
	}
	this.AddFinishCriteria(this.AvoidEnt, "deleted");
};

this.Exit = function()
{
	this.Bot.ReleaseButton(BTN.CROUCH, BTN.SPRINT);
	this.AvoidEnt = null;
};

this.Update = function()
{
	botId = this.Bot.GetGameId();
	radius = this.Radius;
	found = false;

	for(;;)
	{
		// get entity position
		avoidPos = GetEntPosition(this.AvoidEnt);
		eyePos = this.Bot.GetEyePosition();
		if (!avoidPos || !eyePos){
			this.Finished();
		}

		// calculate 2D distance
		face = eyePos - avoidPos;
		face = Vec3(face.x, face.y, 0);

		if (face.Length() > radius && found)
		{
			//crouch and wait
			this.Bot.MoveTowards(this.Bot.GetPosition());
			this.AddAimRequest(Priority.Low, "position", avoidPos);
			this.Bot.HoldButton(BTN.CROUCH, 10);
			sleep(0.4);
		}
		else
		{
			face = face.Normalize();
			side = CrossProduct(face, Vec3(0,0,1));
			found = false;

			foreach(offset in this.AvoidOffsets)
			{
				// yield at the top in case of script errors
				yield();

				end = eyePos + face * radius + side * offset * 64;

				tr = TraceLine(eyePos, end, 0, TRACE.SHOT, botId, false);
				if (tr.fraction < 1){
					end = tr.end;
				}

				if (this.Debug) {
					DrawDebugLine(eyePos, end, COLOR.GREEN, 5);
				}

				if (DistanceBetween(avoidPos, end) > radius) {
					found = true;
					break;
				}
			}

			if (!found){
				// failed
				break;
			}

			// sprint to safe spot
			this.Bot.ReleaseButton(BTN.CROUCH);
			this.Bot.HoldButton(BTN.SPRINT, 10);
			this.ReleaseAimRequest();
			this.Goto(end, 64);
		}
	}

	this.Finished();
};

this.Events[EVENT.ENT_ENTER_RADIUS] = function(ent)
{
	// cs: ignore arty for now. The problem is that repeated spam of arty can have an entire team
	// locked down in avoid mode. Especially noticeable on poorly routed maps
	switch(GetEntClass(ent))
	{
		case CLASS.GRENADE:
		{
			radius = 270;
		}
		case CLASS.SMOKEMARKER: //airstrike
		{
			radius = 470;
		}
		default:
		{
			return;
		}
	}

	if (!this.Bot) { return; }

	if (this.Bot.IsAllied(ent)) {
		if(GetEntOwner(ent) == this.Bot.GetGameEntity()) {
			sleep(0.3);
		}
		else if(GetCvar("g_friendlyfire") < 1) {
			return;
		}
		dist = radius/2;
		dowhile (DistanceBetween(this.Bot.GetPosition() + this.Bot.GetVelocity()/10, entPos + Vec3(entVel.x, entVel.y, 0)/10) > dist)
		{
			yield();
			entPos = GetEntPosition(ent);
			if (!entPos || !this.Bot) { return; }
			entVel = GetEntVelocity(ent);
			if (entVel.IsZero()) {
				dist = radius;
			}
		}
	}
	this.Radius = radius;
	this.AvoidEnt = ent;
	this.Priority = 0.95;
};
