//-----note to character makers-----------//
//if forever reason you need to make it so you're character can't 
// change angle/aim please consider setting player.lockaim to true

freeslot("MT_ZTARGET",
"S_ZTAR0","S_ZTAR1","S_ZTAR2","S_ZTAR3",
"SPR_ZTRG"
)

mobjinfo[MT_ZTARGET] = {
	doomednum = -1,
	spawnhealth = 1000,
	spawnstate = S_ZTAR0,
	radius = 24*FRACUNIT,
	height = 32*FRACUNIT,
	speed = 100*FRACUNIT,
	flags = MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_SCENERY
}

//-------- Lock On console commands ---------------//
local lockonbuttonset = 2
COM_AddCommand("lockon_button", function(player, argument)
		
		if lockonbuttonset == nil
			lockonbuttonset = 2
		end
		local choice = lockonbuttonset
		if argument
			if argument == "1" or argument == "one"
				if lockonbuttonset == 1 then CONS_Printf(player, "lockon_button is already on Custom Button 1") return end
				lockonbuttonset = 1
				print("lock on is now set to Custom Button 1")
			elseif argument == "2" or argument == "two"
				if lockonbuttonset == 2 then CONS_Printf(player, "lockon_button is already on Custom Button 2") return end
				lockonbuttonset = 2
				print("lock on is now set to Custom Button 2")
			elseif argument == "3" or argument == "three"
				if lockonbuttonset == 3 then CONS_Printf(player, "lockon_button is already on Custom Button 3") return end
				lockonbuttonset = 3
				print("lock on is now set to Custom Button 3")
			else
				CONS_Printf(player, "sets the lock on to a Custom Button, valid arguments are 1, 2, 3 .Currently ".. choice)
			end
		else
			CONS_Printf(player, "sets the lock on to a Custom Button, valid arguments are 1, 2, 3 .Currently ".. choice)
		end
end, 0)

addHook("PreThinkFrame", function()
		
	for player in players.iterate do
		
		local mbj = player.mo
		
		if player.mo and player.mo.valid and player.locknontarget2 and player.locknontarget2.valid and player.locknontarget2.health then
		local t = player.locknontarget2
		player.locknocheck2 = R_PointToAngle2(mbj.x, mbj.y, t.x, t.y)
		player.returntocenter2 = player.mo.angle
		end

	end
end)

addHook("ThinkFrame", function()
	for player in players.iterate do
	
	if player.toomanylockonmods then
		return end // dont do anything the mod is already added!
	
	if player.mo.valid and player.mo and player.mo.skin == "sms" or
	player.mo.skin == "samus" or player.mo.skin == "basesamus" then
		return // dont lock on if your playing certain characters	
	end
	
	if lockonbuttonset == nil or lockonbuttonset == 2 then // default to BT_CUSTOM2
			player.lockonbutton = (player.cmd.buttons & BT_CUSTOM2)
	elseif lockonbuttonset == 1 then
			player.lockonbutton = (player.cmd.buttons & BT_CUSTOM1)
	elseif lockonbuttonset == 3 then
			player.lockonbutton = (player.cmd.buttons & BT_CUSTOM3)
	end
	
		// grdualy snape back to center
		if player.returntocenter2 != nil and player.mo.angle >= player.returntocenter2-(ANG1/2900) and player.mo.angle <= player.returntocenter2+(ANG1/2900)
		player.movetocenter2 = ANG1
		end
		if player.movetocenter2 and player.movetocenter2 != 0 then
		player.movetocenter2 = $+ANG2
		end
	
		if not player.lockonbutton then ---// BT_CUSTOM2 is default
			player.locktapready2 = true
			player.locktapping = false
		elseif player.locktapready2 then
			player.locktapping = true
			player.locktapready2 = false
		else
			player.locktapping = false
		end -- End new lines
								
		local mbj = player.mo
		
		if player.locktapping then // lock on by a function
			player.movetocenter2 = 0
			player.lockodelay = 1
			player.angeloverflow = 0
			player.angelaim = 0
			player.centeraim = 0
				if player.targetmark2 and player.targetmark2.valid then
				player.targetmark2.state = S_NULL
				end
			if player.locknontarget2 and player.locknontarget2.valid then // remove old target to look for new one
			player.locknontarget2 = false
			else
			player.locknontarget2 = LO_LockOnToEnemies(mbj, player, 3990*mbj.scale)
				if player.locknontarget2 and player.locknontarget2.valid
					player.targetmark2 = P_SpawnMobj(player.locknontarget2.x, player.locknontarget2.y, player.locknontarget2.z, MT_ZTARGET)
					player.targetmark2.target = player.locknontarget2
					player.targetmark2.creator = player
				end
			end
		end
				
		 if player.mo and player.mo.valid and player.locknontarget2 and player.locknontarget2.valid and player.locknontarget2.health
		 and not player.lockaim then // prevent some exploits
			local t = player.locknontarget2
			player.locknonangle = R_PointToAngle2(mbj.x, mbj.y, t.x, t.y)
			player.aiming = R_PointToAngle2(0, 0, R_PointToDist2(mbj.x, mbj.y, t.x, t.y), ((t.z+(t.height/5)) - mbj.z)	)
			if player.aiming >= 518011616 then // limits how far the player will look vertically
				player.aiming = 518011615
			elseif player.aiming <= -518011616 then
				player.aiming = -518011615
			end
				if player.locknocheck2 and player.mo.angle != player.locknocheck2 then
				player.angeloverflow = (player.mo.angle - player.locknocheck2)
				player.targetdistance2 = R_PointToDist2(t.x, t.y, mbj.x, mbj.y)
					if player.angeloverflow > 670240 and player.lockodelay >= 4 and player.centeraim >= 0 then
					player.angelaim = player.angeloverflow-player.movetocenter2
					player.centeraim = 1 // snap to the center when moving camera back twards the target
						if player.angeloverflow > 1050571070 then 
						player.angelaim = 1050571070
						end
					elseif player.angeloverflow < -670240 and player.lockodelay >= 5 and player.centeraim <= 0 then
					player.angelaim = player.angeloverflow+player.movetocenter2
					player.centeraim = -1
						if player.angeloverflow < -1050571070 then 
						player.angelaim = -1050571070
						end
					else
						if player.lockodelay and player.lockodelay <= 6 then 
							player.lockodelay = $+1
						end
						player.angelaim = FixedAngle(0)
						player.movetocenter2 = 0
						if player.centeraim != 0 then
							player.lockodelay = 1
							player.centeraim = 0
						end
					end
	
				end
				if player.pflags&PF_ANALOGMODE then
					player.angelaim = FixedAngle(0) // angle aim not needed for simple controls
				end
				if player.climbing <= 0 then // sets the angle after all calculations
				player.mo.angle = player.locknonangle+player.angelaim 
				player.thinkangle = player.locknonangle+player.angelaim
				end
			player.anglemod = player.angle
			player.locknonreset = true
			player.angeloverflow = 0
			player.angelaim = 0
		elseif player.locknonreset then
			player.aiming = 0
			player.lockodelay = 1
			player.angeloverflow = 0
			player.angelaim = 0
			player.locknonreset = false
			//player.targetmark.state = S_NULL //
		end
		player.angeloverflow = 0
		player.angelaim = 0
		
	end
end)

addHook("MobjThinker", function(mo)
		
	if mo.valid and mo.creator.valid and mo.creator then
		local mbj = mo.creator.mo

		if mo.creator.centeraim == 1 then
			mo.state = S_ZTAR2
		elseif mo.creator.centeraim == -1 then
			mo.state = S_ZTAR3
		else
			mo.state = S_ZTAR1
		end
		if mo.creator.locknontarget2.valid and mo.creator.valid and mbj.valid and (mbj.state == S_PLAY_DEAD or mbj.state == S_PLAY_DRWN) then 
		mo.creator.locknontarget2 = false
		mo.creator.locktapready2 = true
		mo.state = S_NULL
		end
		if mo.valid and mo.target and mo.target.valid  and mo.target.health then // setpostion if target is alive
		local heightadd = (mo.target.height/2)
			P_MoveOrigin(mo, mo.target.x, mo.target.y, mo.target.z+heightadd)
		elseif mo.valid then
			mo.state = S_NULL 
		end
		if mo ~= displayplayer.targetmark2
			mo.flags2 = $ | MF2_DONTDRAW
		end
	elseif mo.valid then
		mo.state = S_NULL 
	end

end, MT_ZTARGET)

states[S_ZTAR0] = {SPR_NULL, 0|FF_FULLBRIGHT|TR_TRANS30, 1, none, 0, 0, S_ZTAR1}
states[S_ZTAR1] = {SPR_ZTRG, 0|FF_FULLBRIGHT|TR_TRANS30, 1, none, 0, 0, S_ZTAR1}
states[S_ZTAR2] = {SPR_ZTRG, 1|FF_FULLBRIGHT|TR_TRANS30, 1, none, 0, 0, S_ZTAR2}
states[S_ZTAR3] = {SPR_ZTRG, 2|FF_FULLBRIGHT|TR_TRANS30, 1, none, 0, 0, S_ZTAR3}

// shameless copy of a lock on script, slightly edited
// ----------------------------------------------
local s_closestdist
local s_closestmo
local s_sourceplayer
local function LockOnFoundObject(pmobj, mo)
	//Prevent targeting Battle's target dummy since we can target players anyways
	if (pcall(function() local v = _G["MT_TARGETDUMMY"] end, "MT_TARGETDUMMY")
	and mo.type == _G["MT_TARGETDUMMY"]) then return end
	
	if (pcall(function() local v = _G["MT_TNTBARREL"] end, "MT_TNTBARREL") // don't target TNT that would be bad
	and mo.type == _G["MT_TNTBARREL"]) then return end
	
	if mo.flags & (MF_NOCLIPTHING|MF_NOCLIP) or mo.flags2 & MF2_FRET
	or not mo.health or mo == pmobj or mo == s_sourceplayer.mo
	or ((twodlevel or pmobj.flags2 & MF2_TWOD) and abs(pmobj.y-mo.y) > pmobj.radius)
	return end

	if mo.player
		if mo.player.spectator
			return
		elseif P_PlayerInPain(mo.player) or mo.player.powers[pw_flashing]
			return
		elseif gametyperules&GTR_FRIENDLY and not CV_FindVar("friendlyfire").value
			return
		elseif G_GametypeHasTeams() and mo.player.ctfteam == s_sourceplayer.ctfteam
			return
		elseif G_TagGametype() and (mo.player.pflags & PF_TAGIT) == (s_sourceplayer.pflags & PF_TAGIT)
			return
		end
	elseif mo.flags & (MF_MONITOR) and (G_RingSlingerGametype() or (CBW_Battle and CBW_Battle.BattleGametype()))
		return
	elseif (!((mo.flags & (MF_ENEMY|MF_BOSS) and (mo.flags & MF_SHOOTABLE))) == !(mo.flags2 & MF2_INVERTAIMABLE)) // allows if it has the flags desired XOR it has the invert aimable flag
		return // not a valid target
	end
	
	// Needs to be closer!
	local adist = P_AproxDistance(P_AproxDistance(pmobj.x-mo.x, pmobj.y-mo.y), pmobj.z-mo.z)
	if(adist > s_closestdist)
		return
	end
	
	local an = R_PointToAngle2(pmobj.x, pmobj.y, mo.x, mo.y) - pmobj.angle
	if((AngleFixed(an) > 45*FRACUNIT) and(AngleFixed(an) < 285*FRACUNIT))	// behind back
		or not(P_CheckSight(pmobj, mo))		// out of sight
		return
	end
	
	s_closestmo = mo
	s_closestdist = adist
end

rawset(_G, "LO_LockOnToEnemies", function(mobj, player, dist)
	s_closestdist = dist+1
	s_closestmo = nil
	s_sourceplayer = player
	// Search the blockmap for targetable objects!
	searchBlockmap("objects", LockOnFoundObject, mobj, mobj.x-dist, mobj.x+dist, mobj.y-dist, mobj.y+dist)
	// Check if it was in range!
	if(s_closestmo and s_closestmo.valid)
		and(P_AproxDistance(P_AproxDistance(mobj.x-s_closestmo.x, mobj.y-s_closestmo.y), mobj.z-s_closestmo.z) > FixedMul(dist, mobj.scale))
		s_closestmo = nil	 // out of range
	end
	return s_closestmo
end)

//-------- Lock On console commands ---------------//
local showdist = true

COM_AddCommand("lockon_dist", function(player, argument)
		local bool = showdist and "on." or "off."
		if argument
			if argument == "1" or argument == "true" or argument == "on"
				if showdist == true then CONS_Printf(player, "lockon_dist is already on.") return end
				showdist = true
				print("being locked on will display distance")
			elseif argument == "0" or argument == "false" or argument == "off"
				if showdist == false then CONS_Printf(player, "lockon_dist is already off.") return end
				showdist = false
				print("distance will not be displayed when locked on")
			else
				CONS_Printf(player, "when locked on displays the distance of the target. Currently ".. bool)
			end
		else
			CONS_Printf(player, "when locked on displays the distance of the target. Currently ".. bool)
		end
end, 0)

hud.add(function(v, player) // basic hud
				if not player.mo
				return end
	local spli
	if (v.height() == 1080) and splitscreen
	spli = 8*FRACUNIT
	else
	spli = 0
	end
		if player.targetmark2 and player.targetmark2.valid then //160
			if player.centeraim == 0 then
			v.drawString(115, 170, "\x82 >LOCKED ON<", V_SNAPTORIGHT|V_ALLOWLOWERCASE, "left")
			elseif player.centeraim > 0 then
			v.drawString(115, 170, "\x87 LOCKED ON>>", V_SNAPTORIGHT|V_ALLOWLOWERCASE, "left")
			elseif player.centeraim < 0 then
			v.drawString(115, 170, "\x87 <<LOCKED ON", V_SNAPTORIGHT|V_ALLOWLOWERCASE, "left")
			end
			
			if player.targetdistance2 and showdist then
			v.drawString(147, 180, "\x82"+(player.targetdistance2/FRACUNIT), V_SNAPTORIGHT|V_ALLOWLOWERCASE, "left")
			end
		end

end)