--Bots for the Hunters gamemode.

--The worst thing that can happen is having only 2 or 3 players in Hunters of completely different skill levels.
--I really feel like this'll make the gamemode easier to learn/practice, and also make otherwise empty games more lively!
/*
COM_AddCommand("AddHuntersBot", function(tp)
if tp
return
end
	if not (SAM.HUNTERS(s))
		if tp
		CONS_Printf(tp, "This isn't the Hunters gamemode.")
		end
		return
	end
	if tp and not (tp.spectate) and tp.lives
		CONS_Printf(tp, "Make sure you're not spectating or dead.")
	end
	-- Create player
	local color = P_RandomRange(1, SKINCOLOR_OGGRAVITYSUIT)
	local p = G_AddPlayer("samus", color, "Samus", BOT_MPAI)
	
	if p --p refers to the bot here, tp is the true player.
	//	p.botleader = tp	
		if p.playerstate != nil
			p.customsuitc1 = 0
			p.customsuitc2 = 0
			p.customsuitc3 = 0
			p.skincolor = color
			p.sam_iamahuntersbot = true
			if p.mo and p.mo.valid
				local s = p.mo
				s.color = color
			end
		end
	end
end)

--Git out.
local function KickHB_(player)
	if server
	SAM_COM_BufInsertText(server, kick "..tostring(#player)..")	
	end
end

--This mess can stay out the main code as far as I'm concerned.
local function HB_Timers(p, yu, s)
	if (p.cmd.buttons & BT_JUMP)
		yu.jump = yu.jump and $+1 or 1
	else
		yu.jump = 0
	end
	if (p.cmd.buttons & BT_SPIN)
		yu.spin = yu.spin and $+1 or 1
	else
		yu.spin = 0
	end
	if (p.cmd.buttons & BT_ATTACK)
		yu.fire = yu.fire and $+1 or 1
	else
		yu.fire = 0
	end
	if (p.cmd.buttons & BT_FIRENORMAL)
		yu.normalfire = yu.normalfire and $+1 or 1
	else
		yu.normalfire = 0
	end	
	if (p.cmd.buttons & BT_CUSTOM1)
		yu.custom1 = yu.custom1 and $+1 or 1
	else
		yu.custom1 = 0
	end
	--Sure! Put timers into the CMD function, that makes sense.	
	if yu.combatdisengage
		if not yu.combat
			yu.combatdisengage = 0
		end
	end
	if (yu.aggressiondrain > 100)
		yu.aggressiondrain = 100
	elseif (yu.aggressiondrain < 1)
		yu.aggressiondrain = 1
	end
	if yu.aggression
		yu.aggression = $-yu.aggressiondrain
		if (yu.aggression < 0)
			yu.aggression = 0
		end
	end	
	if yu.sidetimer
	yu.sidetimer = $-1
		if p.blocked
			yu.sidetimer = $-20
		end
		if yu.sidetimer < 0
		yu.sidetimer = 0
		end
	end
	if yu.boostballreactiontime
		yu.boostballreactiontime = $-1
	end
	if yu.spbscooldown
	yu.spbscooldown = $-1
		if not yu.spbscooldown
		p.autospeedbooster = true
		end
	end
end

--Don't even start the code under these conditions.
local function HB_PreCheck(p)
	if not (p and p.sam_iamahuntersbot) --Make sure it's a Hunters bot.
		return true
	end
	if p.spectator or (p.realmo and not p.mo and p.lives) --No point in spectator bots...
		if p.givemeasecondbot
			p.givemeasecondbot = $-1
		end	
		if server
			if not p.givemeasecondbot
			p.givemeasecondbot = 10
			SAM_COM_BufInsertText(server, "serverchangeteam "..tostring(#p).." 1")
			end
		end
		return true
	end
	if not (p.mo and p.mo.valid) --Cool, now ensure their mobj exists.
		return true
	end
	if not YouAreSamus(p.mo) --Nope. Get outta here.
	or not (SAM.HUNTERS(s))
		print("\x82 Unsuitable conditions present for Hunters bot. Bot has been removed.")
		KickHB_(p)
		return true
	end
end

--We'll need to use this one alot. Let's not clog the code unnecessarily.
local function HB_PathFind(p, s, yu)
	--Remove current pathmobjs first
	if yu.pathmobjs != nil
		local allmobjs = yu.pathmobjs
		for i = 1,#allmobjs
			if allmobjs[i] and allmobjs[i].valid
				allmobjs.fuse = 2
				allmobjs.state = S_NULL
			end
		end
	end	
	yu.pathmobjs = nil	
	yu.pathmobjs = {}
	local mult = 1
	for d = 0,23
		if (d > 7) and (d < 16)
			mult = 2
		elseif (d >= 16)
			mult = 3
		end
		
		local ghs = P_SpawnMobj(
		s.x+FixedMul( (133*s.scale*mult), cos(s.angle+(ANGLE_45*d)) ),
		s.y+FixedMul( (133*s.scale*mult), cos(s.angle+(ANGLE_45*d)) ),
		s.z, MT_GARGOYLE)
		ghs.flags = $|MF_NOCLIPTHING
		ghs.flags2 = $|MF2_SHADOW
		table.insert(yu.pathmobjs, ghs)
	end
end

local function HB_FaceEnemy(s,t)
	s.angle = R_PointToAngle2(s.x, s.y, t.x, t.y)
end
--Determine whether I should shoot, continue charging, or fire a beamcombo!
local function HB_OpenFire(p, s, yu, t)
	if p.sams_shotcd or p.sams_beamcombocharge
	or p.sams_mshotcd --Welp, this ain't gonna work.
	return
	end
	if p.sams_chargetime and (p.sams_chargetime < 42) --Finish charging it.
		return BT_FIRENORMAL
	end
	if (leveltime % (10-yu.difficulty) == 0)
		if not p.sams_chargetime --Not charging? Shall we missile, super missile, or spam some shots? 
			if P_RandomRange(0,3)
			return BT_FIRENORMAL
			end
		end
	end
end
--Setup table. Set up the table. TableSetup. Table Setup. You get the point.
local function HB_MakeTable(p)
	p.sambottable = {
		difficulty = 1, --Difficulty of bot. Tactics, accuracy, and abilities change.
		stress = 0, --Stress raises likelyhood of bot changing tactics.
		aggression = 0, --How likely bot is to attack.
		aggressiondrain = 1, --How fast aggression drains. A lower number means faster drain.
		powerlevel = 1, --How strong the bot thinks it is. Changes it's interaction with players.
		target = nil, --The bot's current target to fire at.
		jump = 0, --Button input timers.
		spin = 0,
		fire = 0,
		firenormal = 0,
		custom1 = 0,
		combat = 0, --The bot is currently in combat.
		combatdisengage = 0
	}
	if p.sam_botpriority == nil p.sambottable = {} end
end 
addHook("BotTiccmd", function(p, cmd) if HB_PreCheck(p) return end if p.sam_bottable == nil HB_MakeTable(p) end
	local s = p.mo --Initial pre-checks out of the way, let's go!
	local prio = p.sam_botpriority
	local yu = p.sam_bottable HB_Timers(p, yu, s) --The rise of Metalwario64's SA-Sonic has turned every table into Yuji Uekawa.	
	
	p.pflags = $ & ~PF_ANALOGMODE & ~PF_FORCESTRAFE & ~PF_DIRECTIONCHAR
	p.autospeedbooster = true
	HB_PathFindSpawn(p, s, yu) --Extremely important.
	
	if (p.pflags & PF_JUMPED) and (s.momz*P_MobjFlip(s) > 0) --Full jumps.
		cmd.buttons = $|BT_JUMP
	end
	--Programming these guys to make them collect tiny missiles and ring ammo pickups would hamper their movement FAR too much.
	--Therefore bots auto-regenerate health and ammo since they're incapable of getting it on their own.
	if (leveltime % (850/yu.difficulty) == 0)
		p.sammissilesmax = $+1
		p.sammissiles = $+1
	elseif (leveltime % 35 == 0)
		if not P_RandomRange(0,20)
			cmd.buttons = $|BT_WEAPONNEXT
		end
	end
	if (yu.difficulty > 3)
		if (p.sam_energy < p.sam_energylimit)
			if yu.difficulty == 9
				s.sam_autotimer = s.sam_autotimer and $+2 or 2
			else
				s.sam_autotimer = s.sam_autotimer and $+1 or 1
			end
			if (s.sam_autotimer > 50)
				p.sam_energy = $+1
				s.sam_autotimer = 0
			end
		end
		if (leveltime % (240/yu.difficulty) == 0)
			p.sammissiles = $+1
		end
	end
	if p.sammorphed
		if p.sambsbl
			if p.sambsblchargefull
				cmd.buttons = $ & ~BT_FIRE
			else
				cmd.buttons = $|BT_FIRE
			end
		end
		if not s.spiderballing 
		and leveltime % 100 == 0
			if P_RandomRange(0,1)
				cmd.buttons = $|BT_USE --Don't just stay morphed for no reason
			end
		end
	else
		if p.samspbs --Speed booster shenanigans. Bots use autospeedbooster rather than custom1 input.
			if not p.sampbschargefull
				if (yu.difficulty < 8) --Randomly stop speedboosting for your enemy to catch up.
					if (leveltime % (280/yu.difficulty) == 0)
						p.autospeedbooster = false
						yu.spbscooldown = 70
					end
				end
			end
		end
	end
	if yu.target and yu.target.valid and yu.target.health and yu.target.player --Engage with your target.
		local t = yu.target
		if yu.combat
				if not (s.sam_lockchosen and s.sam_lockchosen.valid)
					s.sam_lockchosen = t
				end
				local buttonget = HB_OpenFire(p, s, yu, t)
				if buttonget
					cmd.buttons = $|button
				end
				if (FixedHypot(s.x-t.x, s.y-t.y) < 500<<16)				
					if (FixedHypot(s.x-t.x, s.y-t.y) < 120<<16)
						if (p.sams_chargetime > 44) and (yu.difficulty > 5)
						and not P_RandomRange(0,(11-yu.difficulty)) --Spam with bombs.
							cmd.buttons = $|BT_USE
							cmd.forwardmove = 50
						else
							cmd.forwardmove = -50 --Back away from the enemy!
						end
					end
				elseif (FixedHypot(s.x-t.x, s.y-t.y) > 1100<<16)
					cmd.forwardmove = 50
				end
				
				if not yu.sidetimer
					if P_RandomRange(0,1)
					yu.sidechoose = 50
					else
					yu.sidechoose = -50
					end
					yu.sidetimer = P_RandomRange(15,120)
				else
					cmd.sidemove = yu.sidechoose
				end
				
				if (P_IsObjectOnGround(s) and not (p.pflags & PF_JUMPED))
					if not P_RandomRange(0,55) --Jump, Megaman!
						p.pflags = $ & ~PF_JUMPDOWN
						cmd.buttons = $|BT_JUMP
					end
				elseif (p.pflags & PF_JUMPED) --Other action?
				
					if p.sam_spacejump and not p.sam_spacejumped
						if not (s.momz*P_MobjFlip(s) > -2*s.scale) and not P_RandomRange(0,10)
							p.pflags = $ & ~PF_JUMPDOWN
							cmd.buttons = $|BT_JUMP
						end
					end
					
					if p.sam_screwattack 
						if not p.sam_screwattacking
							s.angle = R_PointToAngle2(s.x, s.y, t.x, t.y)
							if not yu.custom1
								cmd.buttons = $|BT_CUSTOM1
							end
						else
							if (p.sam_screwattacking < 26)
								s.angle = R_PointToAngle2(s.x, s.y, t.x+t.momx, t.y+t.momy)
							end
							if (t.z < s.z-s.height)
								cmd.buttons = $|BT_USE
							elseif (t.z > s.z+(s.height*2))
								cmd.buttons = $|BT_JUMP
							end
						end
					end
				end
				if not P_CheckSight(s,t) and yu.combat  --Can't see them? Begin disengaging
				yu.combatdisengage = $+1
					if (yu.combatdisengage > 100)
						yu.combat = 0
					end
				end
		end
	else
		if yu.target
		yu.target = nil
		end
		if (yu.difficulty > 4) and not p.sammorphed
			cmd.buttons = $|BT_FIRENORMAL --Charge Beam beforehand.
		end
	end
end) */