// This script sets difficulty settings for the bots
this.Name = "Difficulty";
this.Parent = "LowLevel";
this.Debug = false;
this.AlwaysRecieveEvents = true;

this.AdjustAim = ConfigGet("Difficulty","AdjustAim", true);
this.UseWeaponSpecificTolerances = true;

this.Initialize = function()
{
	this.CurrentDifficulty = ConfigGet("Difficulty","CurrentDifficulty", 4);
	// run it once at start
	this.FirstRun = true;
};

this.GetPriority = function()
{
	yield();
	if (this.FirstRun) {
		this.Priority = 1.0;
	}
	block(0);
};

this.Update = function()
{
	this.FirstRun = false;

	// if a bot profile sets a ProfileSkill, this will run only once per round.
	if (!this.LockSkill && (IsNull(this.Bot.aimskill) || this.CurrentDifficulty != this.Bot.aimskill))
	{
		if (!this.Bot.ProfileSkill)
		{
			this.Bot.aimskill = this.CurrentDifficulty;
		}
		else
		{
			this.Bot.aimskill = this.Bot.ProfileSkill;
			this.LockSkill = true;
		}

		if(this.Bot.aimskill < 0)
		{
			n = tableCount(this.difficulties);
			useDiff = RandInt(0, n-1);
		}
		else
		{
			useDiff = this.Bot.aimskill;
		}

		if (!this.difficulties[useDiff])
		{
			useDiff = 4; // use original default
		}

		difficulty = this.difficulties[useDiff];
		this.Bot.FieldOfView = difficulty.FieldOfView;
		this.Bot.ReactionTime = difficulty.ReactionTime;
		this.Bot.MemorySpan = difficulty.MemorySpan;
		this.Bot.AimPersistance = difficulty.AimPersistance;
		this.Bot.MaxTurnSpeed = difficulty.MaxTurnSpeed;
		this.Bot.AimStiffness = difficulty.AimStiffness;
		this.Bot.AimDamping = difficulty.AimDamping;
		this.Bot.AimTolerance = difficulty.AimTolerance;
		if(this.UseWeaponSpecificTolerances){
			this.Bot.DefaultTolerance = difficulty.AimTolerance;
			this.Bot.WeaponTolerances = difficulty.WeaponTolerances;
		}

		// don't accumulate percentages every difficulty change
		if(!this.OriginalMaxView) { this.OriginalMaxView = this.Bot.MaxViewDistance; }
		dist = this.OriginalMaxView * (difficulty.MaxViewPercent * .01);
		this.Bot.MaxViewDistanceNormal = dist;
		this.Bot.MaxViewDistanceZoom = Clamp(dist*2, 8000, 15000);
		if(!this.Bot.HasEntityFlag(ENTFLAG.ZOOMING)) {
			this.Bot.MaxViewDistance = dist;
		}else{
			this.Bot.MaxViewDistance = this.Bot.MaxViewDistanceZoom;
		}

		if (this.Debug) {
			print("--- " +this.Bot.Name + " Aim Properties ---");
			print("Difficulty: " + useDiff);
			print("FieldOfView: " + this.Bot.FieldOfView);
			print("ReactionTime: " + this.Bot.ReactionTime);
			print("MemorySpan: " + this.Bot.MemorySpan);
			print("AimPersistance: " + this.Bot.AimPersistance);
			print("MaxTurnSpeed: " + this.Bot.MaxTurnSpeed);
			print("AimStiffness: " + this.Bot.AimStiffness);
			print("AimDamping: " + this.Bot.AimDamping);
			print("AimTolerance: " + this.Bot.AimTolerance);
			print("MaxViewDistance: " + this.Bot.MaxViewDistance);
			print("------------ ");
		}

		if (this.AdjustAim) {
			this.AdjustWeaponProperties(this.Bot, difficulty);
		}

		this.SetAimToleranceForWeapon(this.Bot.GetCurrentWeapon());
	}

	this.Finished();
};

this.difficulties =
{
	// Difficulty 0
	{
		FieldOfView = 90.0,
		ReactionTime = 2.0,
		MemorySpan = 1.5,
		AimPersistance = 1.0,
		MaxTurnSpeed = 720.0,
		AimStiffness = 60.0,
		AimDamping = 20.0,
		AimTolerance = 125,
		AimErrorX = 25,
		AimErrorY = 25,
		AimOffsetX = 0,
		AimOffsetY = 0,
		AimOffsetZ = -15,
		MaxViewPercent = 65,
		WeaponTolerances =
		{
			VENOM = 128,
		},
	},
	// Difficulty 1
	{
		FieldOfView = 90.0,
		ReactionTime = 2.0,
		MemorySpan = 1.5,
		AimPersistance = 1.0,
		MaxTurnSpeed = 720.0,
		AimStiffness = 60.0,
		AimDamping = 20.0,
		AimTolerance = 125,
		AimErrorX = 25,
		AimErrorY = 25,
		AimOffsetX = 0,
		AimOffsetY = 0,
		AimOffsetZ = -15,
		MaxViewPercent = 65,
		SnipeDelay = 2.0,
		WeaponTolerances =
		{
			VENOM = 128,
		},
	},
	// Difficulty 2
	{
		FieldOfView = 100.0,
		ReactionTime = 2.0,
		MemorySpan = 2.0,
		AimPersistance = 1.0,
		MaxTurnSpeed = 720.0,
		AimStiffness = 50.0,
		AimDamping = 15.0,
		AimTolerance = 110,
		AimErrorX = 20,
		AimErrorY = 20,
		AimOffsetX = 0,
		AimOffsetY = 0,
		AimOffsetZ = -15,
		MaxViewPercent = 75,
		SnipeDelay = 1.6,
		WeaponTolerances =
		{
			VENOM = 128,
		},
	},
	// Difficulty 3
	{
		FieldOfView = 105.0,
		ReactionTime = 1.0,
		MemorySpan = 2.5,
		AimPersistance = 2.0,
		MaxTurnSpeed = 720.0,
		AimStiffness = 60.0,
		AimDamping = 15.0,
		AimTolerance = 100,
		AimErrorX = 15,
		AimErrorY = 15,
		AimOffsetX = 0,
		AimOffsetY = 0,
		AimOffsetZ = -12,
		MaxViewPercent = 85,
		SnipeDelay = 0.8,
		WeaponTolerances =
		{
			VENOM = 128,
		},
	},
	// Difficulty 4
	{
		FieldOfView = 110.0,
		ReactionTime = 0.75,
		MemorySpan = 3.0,
		AimPersistance = 2.0,
		MaxTurnSpeed = 720.0,
		AimStiffness = 65.0,
		AimDamping = 15.0,
		AimTolerance = 90,
		AimErrorX = 10,
		AimErrorY = 10,
		AimOffsetX = 0,
		AimOffsetY = 0,
		AimOffsetZ = -10,
		MaxViewPercent = 90,
		SnipeDelay = 0.4,
		WeaponTolerances =
		{
			VENOM = 128,
		},
	},
	// Difficulty 5
	{
		FieldOfView = 120.0,
		ReactionTime = 0.5,
		MemorySpan = 3.0,
		AimPersistance = 2.0,
		MaxTurnSpeed = 720.0,
		AimStiffness = 75.0,
		AimDamping = 10.0,
		AimTolerance = 64,
		AimErrorX = 5,
		AimErrorY = 5,
		AimOffsetX = 0,
		AimOffsetY = 0,
		AimOffsetZ = -5,
		MaxViewPercent = 100,
		SnipeDelay = 0.2,
		WeaponTolerances =
		{
			VENOM = 128,
		},
	},
	// Difficulty 6
	{
		FieldOfView = 180.0,
		ReactionTime = 0.0,
		MemorySpan = 4.0,
		AimPersistance = 2.0,
		MaxTurnSpeed = 720.0,
		AimStiffness = 75.0,
		AimDamping = 10.0,
		AimTolerance = 64,
		AimErrorX = 0,
		AimErrorY = 0,
		AimOffsetX = 0,
		AimOffsetY = 0,
		AimOffsetZ = 0,
		MaxViewPercent = 110,
		SnipeDelay = 0.1,
		WeaponTolerances =
		{
			VENOM = 128,
		},
	},
};

this.AdjustWeaponProperties = function(bot, difficulty)
{
	aimError = Vec2(difficulty.AimErrorX, difficulty.AimErrorY);

	foreach (weap in WEAPON)
	{
		w = bot.GetWeapon(weap);
		if (w)
		{
			if(Util.IndexedSniperWeapons[weap]){
				w.PrimaryFire.MaxAimError = aimError/7;
			}else{
				w.PrimaryFire.MaxAimError = aimError;
			}
			w.PrimaryFire.AimOffset = Vec3(difficulty.AimOffsetX, difficulty.AimOffsetY, difficulty.AimOffsetZ + w.PrimaryFire.AimOffsetZ);
		}
	}
};

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

this.Commands["difficulty"] =
{
	Func = function(_params)
	{
		if(!IsNull(_params[0]))
		{
			diff = Clamp(ToInt(_params[0], 4), -1, tableCount(this.difficulties)-1);
			this.CurrentDifficulty = diff;
			if (this.Debug) { print("Difficulty Changed", diff, " for ", this.Bot.Name); }
			this.Priority = 1.0; // run update if the bot is alive
			this.FirstRun = true; // run update after spawn if the bot is dead or waiting for medic
		}
	},
};

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

this.SetAimToleranceForWeapon = function(weaponId)
{
	if (!this.Bot || !this.Bot.WeaponTolerances) { return; }
	weaponName = Util.WeaponName(weaponId);
	tolerance = this.Bot.WeaponTolerances[weaponName];
	if (tolerance) {
		if (this.Debug) {
			this.Bot.Say("changing aim tolerance to " + tolerance + " for " + weaponName);
		}
		this.Bot.AimTolerance = tolerance;
	} else {
		if (this.Debug) {
			this.Bot.Say(format("using default tolerance for %s",weaponName));
		}
		this.Bot.AimTolerance = this.Bot.DefaultTolerance;
	}
};

this.Events[EVENT.WEAPON_CHANGE] = this.SetAimToleranceForWeapon;
