local GUN = GUN_Troops

--[GUN Troops Custom Enemies]--
--Sprites crafted by Metalwario64.
--Lua done by Golden Shine. Assume all comments in the code are made by Golden Shine!

--#============================================================================================================================#
--The main code for the GUN Hunter Robot is here!
--#============================================================================================================================#

--Initial spawn settings?
--addHook("MobjSpawn", function(s)
--	GUN.SetupHunter(s)
--end, MT_GUNHUNTER)

--This becomes your rail number. Thankfully, it's not actually saved in angle variants, but 1 to like...31000 or something.
--addHook("MapThingSpawn", function(s, mapthing)
--	if s and mapthing and (mapthing.options != nil)
//	print("Do this one")
//		GUN.SetupHunter(s, mapthing)
	/*	if (mapthing.options & MTF_AMBUSH)
			s.flags = $|MF_NOGRAVITY
			s.momz = 0
			s.state = S_GUNH_FALL
			priint("\x8a"+"Ambush GUN Hunter  loaded!", 3)
		end
		if mapthing.extrainfo
			priint("\x83"+"GUN Hunter custom health ="+mapthing.extrainfo, 88)
			s.health = max(1, mapthing.extrainfo)
		end
		if mapthing.args
			priint("\x81"+"UDMF GUN Hunter args : "+mapthing.args, 88)
		end
		if mapthing.stringargs
			priint("\x8c"+"UDMF GUN Hunter\x82 STRING\8c args : "+mapthing.stringargs, 88)
		end
		if mapthing.tag
			priint("\x8d"+"GUN Hunter tag : "+mapthing.tag, 88)
		end 
	end
end, MT_GUNHUNTER) */

--Set up the GUN trooper's object Table? This is basically their MobjSpawn function!
GUN.SetupHunter = function(s, SPAWNDEFAULT)
	if s.GUN_HunterTable return end -- Table already exists, cancel!

	local WEAPON = 0
	local SHIELD = false
	local MOVEMENT = 0	
	local AMBUSH = 0
	local HEALTH = 1
	local SEARCHTYPE = 0 
	local HEARING = 500*FRACUNIT
	local VIEWDIST = 2200*FRACUNIT
	local VIEWCONE = ANG10*11
	local EYEGLOW = 0
	local COLOR = SKINCOLOR_GUNHUNTER
	local COLORIZED = false
	local SCALE = s.scale*6/5
	
	local spn = s.spawnpoint --Add spawnpoint parameters if available!
	if spn and spn.valid
	
		if spn.options != nil
		
			--BINARY map options!
			if ((udmf) != true) --Binary map options!
				if (spn.options & MTF_EXTRA) -- Flag 1: BOMBER!
					if (spn.options & MTF_OBJECTSPECIAL) --Combine with SNIPER for ELITE!
						WEAPON = 777 -- print("\x81 ELITE")
						COLOR = SKINCOLOR_GREY
						COLORIZED = true
						EYEGLOW = SKINCOLOR_SUPERGOLD4
						SCALE = $*3/2
					else
						WEAPON = 1 --Bomber!
						COLOR = SKINCOLOR_JET
						EYEGLOW = SKINCOLOR_SKY
					end
				elseif (spn.options & MTF_OBJECTSPECIAL) --Flag 4. SNIPER!
					WEAPON = 2
					COLOR = SKINCOLOR_EMERALD
					EYEGLOW = SKINCOLOR_PURPLE
					VIEWDIST = 5500*FRACUNIT
				end	
				--AMBUSH! If placed in the air, the GUN Hunter is invisible and drops in from above when players approach!
				--If you place ambush mechs on the ground, they'll start off in standing frames and can jump off cliffs to engage foes.
				if (spn.options & MTF_AMBUSH)
					AMBUSH = 9
				end	
				--Give the robot extra health through the parameter flags?
				if spn.extrainfo and (tonumber(spn.extrainfo))
				and not (spn.extrainfo > 16) --There's probably a mistake...ignore it!
					HEALTH = min(16, max(1, spn.extrainfo))
				end	
				
			elseif spn.args != nil --UDMF specific options!

---------------------------------------------------------------------------
				for i = 1,1 --Only doing this so I can use "break"
---------------------------------------------------------------------------	

				if spn.scale
					if spn.scale != FRACUNIT
						-- print("\x82"+"SCALE\x80 ="+spn.scale)
						SCALE = (spn.scale*5)/6
					end
				end
				
				/* -- What the fuck is this shit it crashes the game
				
				--Uhhh, how DOES this work exactly? Are any of these a thing?				
				if spn.xscale and spn.yscale 
					local newscale = (spn.xscale+spn.yscale)/2 
					print("\x82"+"SCALEx\x80 ="+spn.xscale)
					if newscale != FRACUNIT
						SCALE = (newscale*5)/6
					end
				end				
				if spn.scalex and spn.scaley 
					local newscale = (spn.scaley+spn.scalex)/2 
					print("\x81 "+"SCALEy\x80 ="+spn.scalex)
					if newscale != FRACUNIT
						SCALE = (newscale*5)/6
					end
				end	
				*/
				
				--What weapon are wielding?
				if spn.args[0]
				
					if not (spn.args[1] and (spn.args[1] == 3 or spn.args[1] == 2))		
					
						if spn.args[1] == 1 -- or spn.args[1] == "bomber" --BOMBER!!
						
							WEAPON = 1
							COLOR = SKINCOLOR_JET 
							EYEGLOW = SKINCOLOR_SKY
							
						elseif spn.args[1] == 2 -- or spn.args[1] == "sniper" --SNIPER!!
						
							WEAPON = 2
							COLOR = SKINCOLOR_EMERALD
							EYEGLOW = SKINCOLOR_PURPLE
							VIEWDIST = 5500*FRACUNIT --See extra far by default!
						end
					end
--					print("\x83"+"Arg0[WEAPON] = "+spn.args[0])
				end
				
				--Special properties. Have a shield? Be inactive?
				if spn.args[1]
--					print("\x81"+"Arg1[SPECIAL] = "+spn.args[1])
					
					if spn.args[1] == 1  -- == "Shield"
						SHIELD = true
						if (WEAPON == 777) WEAPON = 0 end
						
					elseif spn.args[1] == 2 -- == "Elite"
					
						SHIELD = false
						WEAPON = 777
						COLOR = SKINCOLOR_GREY
						COLORIZED = true							
						EYEGLOW = SKINCOLOR_SUPERGOLD4
						if not spn.args[4] --Auto set health to 4 if nothing is set.
							HEALTH = 4
						end
						SCALE = $*3/2
						if not spn.args[7] and (HEARING == 500*FRACUNIT)
							HEARING = 2000*FRACUNIT --Can't sneak up as easily on elites!
						end
						
					elseif spn.args[1] == 3 -- == "Inactive"
					
						WEAPON = 0
						if not spn.roll and not spn.args[9] --If a a custom color wasn't picked, set this default.
							COLORIZED = true
							COLOR = SKINCOLOR_JET
						end
						EYEGLOW,HEALTH = 0,1
						VIEWCONE,VIEWDIST = 0,1
						AMBUSH,HEARING = 0,1 break --
					end
				end
				
				--Does your robot have the ability to jump, ram, or float?
				if spn.args[2]	
					print("\x84"+"Arg2[MOVEMENT] = "+spn.args[2])
					if spn.args[2] == 2
						MOVEMENT = 2
					else
						MOVEMENT = 1
					end
				end
				
				--Do we drop from above to give our players a jumpscare?
				if spn.args[3]
					AMBUSH = 9
				end
				
				--Want to give your Robot some custom health?
				if tonumber(spn.args[4]) and (spn.args[4] != 1)
					HEALTH = min(64, max(2, spn.args[4])) --Let's call 64 the max health value.
					if (HEALTH >= 2) and not EYEGLOW
						EYEGLOW = SKINCOLOR_RED --Give a red eyeglow by default if mech has alot of health.
					end
				end
				
				--Viewing Range. How far you can see!
				if spn.args[5]
					VIEWDIST = spn.args[5]*FRACUNIT				
				end
				
				--View cone. How broad is your field of vision? 
				--If it's 360, see all around and skip P_CheckSight checks!
				if spn.args[6]
					VIEWCONE = (spn.args[6] >= 360) and 360 or spn.args[6]*ANG1				
				end
				
				--Hearing Range! If set to 1, the mech is deaf!
				if spn.args[7]
					HEARING = (spn.args[7] == 1) and 1 or spn.args[7]*FRACUNIT
				end
				
				--Target players, enemies, both, or do both with a preference for one or the other?
				if spn.args[8]
					print("\x8e"+"Arg8[SEARCHTYPE] = "+spn.args[8])
					if spn.args[8] == 1 or spn.args[8] == 3 or spn.args[8] == 4
						SEARCHTYPE = spn.args[8]
					else
						SEARCHTYPE = 2 --Default to 2.
					end
				end
				
				--Give the GUN Hunter an eye glow?
				if spn.pitch
					if (spn.pitch > #skincolors-1) --Default to red if color is unavailable.
						EYEGLOW = SKINCOLOR_RED
					else
						EYEGLOW = spn.pitch
					end
				end
				
----------------------------------------------------------				
				end --End "for i" loop.
----------------------------------------------------------
				
				--Give your GUN hunter a custom paint job?
				if spn.args[9]
					if not (spn.args[9] > #skincolors-1) --Invalid color!
						COLOR = spn.args[9]
					end
				end
				--Colorize the entire Gun Hunter?
				if spn.roll == 180 or spn.roll == ANGLE_180 or spn.roll == FRACUNIT*180 --If it's 180, then colorize!
					if WEAPON == 777 --Inverted with Elites.
						COLORIZED = false
					else
						COLORIZED = true
					end
				end
			end
		end
				
	elseif not (SPAWNDEFAULT) --Do code for random spawning?
	
		if (leveltime % 6 == 0)
			HEALTH = max($,2)
			EYEGLOW = SKINCOLOR_RED
			if (leveltime % 30 == 0)
				HEALTH = max($,3)
			end
		end
		local RNG = P_RandomRange(1,100)
		if (RNG > 30)
			if (RNG < 65)
				COLOR = SKINCOLOR_JET
				EYEGLOW = SKINCOLOR_SKY
			elseif (RNG < 84)
				COLOR = SKINCOLOR_EMERALD
				EYEGLOW = SKINCOLOR_PURPLE
				WEAPON = 2
			else
				WEAPON = 777
				COLOR = SKINCOLOR_GREY
				COLORIZED = true
				HEALTH = 4
				EYEGLOW = SKINCOLOR_RED
				SCALE = $*2 //3/2
			end
		end
	end
	
	s.GUN_HunterTable = {
	looktimer = 6, --Tics down, then looks for a player.
	searchtype = 0, --Who do we look for? Players, enemies, or both?
	lookdist = VIEWDIST, --From how far away can we see targets?
	distpref = 0, --Preference for distance?
	lostsight = 0, --How many tics we can have lost sight for.
	cone = VIEWCONE, --Vision cone for seeing players.
	hearing = (HEARING != 1) and HEARING or 0, --Hearing range for when vision fails!
	
	awakenanim = 0, --Wake up from inactivity?
	aimanim = 0, --Animation from standing frame to fight!
	recoilanim = 0, --Shooting animation!
	landanim = 0, --Animation when landing!
	deathanim = (HEALTH >= 2) and 59 or 4, --How long death anim lasts on death.
	
	flinch = 0, --Got flinched?
	forceXY = 0, --How hard you've been hit.
	forceZ = 0, --Got hit upwards?
	forceANG = s.angle+ANGLE_180, --Direction you were hit from.
	
	charge = 0, --How long you're charging.
	shotcd = 0, --Firerate cooldown.
	weapon = WEAPON, --0 = Standard laser. 1 = Bomber. 2 = Sniper. 777 = Elite.
	ambush = AMBUSH, --Drop in from above? IF placed on ground, jump off cliff instead!
	shield = (WEAPON != 777) and SHIELD or false, --Shields don't apply as an elite, though!
	eyeglow = EYEGLOW, --Red glare from eye? (Can be other colors)
	starthealth = HEALTH, --How many hits we can take.
	realscale = SCALE,
	lastz = s.z,
	/*
	lasttarget = nil,
	*/
	}
	local yu = s.GUN_HunterTable
	
	if (yu.weapon == 777) --Elite Mechs use no shields!
		yu.shield = false
	end
	if not yu.eyeglow and ((HEALTH >= 2) or (yu.weapon == 777))
		yu.eyeglow = SKINCOLOR_RED
	end
	
	s.GUNally = true
	s.color = COLOR
	s.colorized = (COLORIZED)
	s.health = HEALTH
	s.scale = SCALE --Scale them slightly bigger to satisfy Metalwario's OCD.
	s.destscale = SCALE
	s.spritexscale = FRACUNIT
	s.spriteyscale = FRACUNIT
	
	if AMBUSH
		if not P_IsObjectOnGround(s)
			s.flags = $|MF_NOGRAVITY
			s.state = S_GUNH_FALL
			s.momz = 0
			s.flags2 = $|MF2_DONTDRAW
		else --Grounded ambush bots will jump off nearby cliffs to attack.
			s.state = S_GUNH_STAND
			yu.ambush = 1
		end
	elseif not P_IsObjectOnGround(s) --Go into falling state?
		s.state = S_GUNH_FALL
	end
end

--#============================================================================================================================#
--Main MobjThinker calling most of the functions and performing the actions.
/*
	lostsight = 0, --How many tics we can have lost sight for.
	
	awakenanim = 0, --Wake up from inactivity. Counts up to 13, then stops in standing frame. 4 frames.
	aimanim = 0, --Animation from standing frame to fight! Counts up to 6, then stops in shot frame. Can also count backwards. 3 frames.
	recoilanim = 0, --Shooting recoil animation! Counts up to 6, then stops back in shot frame. 2 frames.
	landanim = 0, --Animation when landing! Counts up to 17, then stops. 4 frames.
	deathanim = (WEAPON == 777) and 95 or 50, --How long death anim lasts on death.
*/
--#============================================================================================================================#

addHook("MobjThinker", function(s)
	if not s.valid return end
	if not s.GUN_HunterTable GUN.SetupHunter(s) end  -- preent("No table for Hunter! "+s)
	local yu = s.GUN_HunterTable
	
	s.scale = yu.realscale
	
	--We're in a flinched state! Ignore everything past the flinch code!
	if yu.flinch
		if (s.state != S_GUNH_LAND) --Use landing frames to signify flinching.
			s.state = S_GUNH_LAND
			s.tics = yu.flinch
		end	
		s.flags2 = $|MF2_FRET
		
		if GUN.HunterDeathAnim(s)
			if not yu.deathanim
				s.state = S_NULL --Vanish!
			else
				GUN.KnockBack(s, 24) --Apply knockback if it exists.
			end return --			
		else --Regular animation?
			yu.flinch = $-1
			
			--We just got done flinching? Reset some values.
			if not yu.flinch 
				if s.target and not yu.shotcd
					yu.aimanim = 8
					s.state = S_GUNH_BATTLE
				else
					s.state = S_GUNH_STAND
				end
				s.flags = $|MF_SHOOTABLE
				s.flags2 = $ & ~MF2_FRET
				s.spriteyoffset = 0
				s.spritexoffset = 0
				s.momx = $/2
				s.momy = $/2
				yu.forceXY = 0
				yu.forceZ = 0
				if not s.health
					s.health = 1
				end				
			else
				if (GUN.KnockBack(s) != 0) --Shake violently when flinching.	
					if (yu.flinch % 2)
						s.spritexoffset = GUN.RNG(FRACUNIT, 7*FRACUNIT, s)
						s.spriteyoffset = GUN.RNG(-FRACUNIT, 3*FRACUNIT, s)	
					else
						s.spritexoffset = $*-1
						s.spriteyoffset = 0
					end
				end
				if (yu.flinch > 10)
					local FRAME = max(H, J-((yu.flinch-10)/2) )
					s.frame = ($ & FF_FRAMEMASK)|FRAME
				end
				if (yu.flinch % 3 == 0) --Flash white for a second!	
					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_GUNOVERLAY)
					ghs.tracer = s
					ghs.frame = FF_TRANS50
					ghs.renderflags = RF_FULLBRIGHT
					ghs.blendmode = AST_ADD
					ghs.fuse = 2
					ghs.colorized = true
					ghs.color = SKINCOLOR_SUPERSILVER1
					ghs.copyframe = true
					GUN.OverLayThink(ghs)
				end return --We can't do anything else for a moment!
			end
		end		
	elseif not (s.flags & MF_SHOOTABLE) --failsafe
		s.flags = $|MF_SHOOTABLE
		s.flags2 = $ & ~MF2_FRET
	end

	if yu.inactive return end --Nope, this robot has no power.
	
	--Spawn the crimson eyeglow?
	if yu.eyeglow
		GUN.CrimsonEye(s)
	end
	
	if (abs(s.momz) > 7*s.scale) and not P_IsObjectOnGround(s)
		GUN.zwind(s, (abs(s.momz) > 14*s.scale) and 4 or 2)
	end
	
	--LANDING behaviour!	
	if not yu.sprung
		if P_IsObjectOnGround(s)
			if not yu.landingcd
				local MOMZ = abs(s.z-yu.lastz) --This always returns a positive value.
				if (MOMZ > 3*s.scale)
				or (s.state == S_GUNH_FALL)
					if (s.state != S_GUNH_LAND)
						GUN.QuickDust(s)
						GUN.SpawnDust(s)
						if (MOMZ > 6*s.scale)
							GUN.MediumDust(s)					
							local POW = max(5*FRACUNIT, min(90*FRACUNIT, MOMZ))
							local DIST = min(2000*FRACUNIT, POW*35)
							local TICS = max(3, min(10, FixedInt(POW/4)))
							GUN.Quake(s, POW, TICS, DIST)
							if (MOMZ > 12*s.scale)
								GUN.WindBlast(s)
							end
						end
						local RVOLUME = min(255, max(140, FixedInt(MOMZ)*40))
						S_StartSoundAtVolume(s, sfx_ghsfx6, RVOLUME)
					end
					GUN.HunterAnim(s, "land", S_GUNH_LAND)
					yu.lastz = s.z --Equal this out, then.
					yu.landingcd = 12
				end
			end
		end
	else
		if yu.sprung == 2 --Springing?
			if not (s.target and (s.state != S_GUNH_BATTLE))
				if P_IsObjectOnGround(s) and not yu.landingcd --Slide across the floor with this animation if we're still on the ground.
					GUN.HunterAnim(s, "land", S_GUNH_LAND)
					GUN.QuickDust(s)
					yu.landingcd = 8
					if not S_SoundPlaying(s, sfx_ghsfx6)
						S_StartSoundAtVolume(s, sfx_ghsfx6, 40)
					end
				else
					GUN.HunterAnim(s, nil, S_GUNH_FALL) --Go into falling animation!
				end
			end
		end
	end
	
--#============================================================================================================================#	
	--NOT flinching or busy, main action code!
--#============================================================================================================================#
	--Doing a transitional animation?
	if yu.anim != nil
		yu.anim = nil
		if yu.awakenanim
			GUN.HunterAnim(s, "awaken", S_GUNH_STAND) --"awaken"
			if not yu.awakenanim and s.target and s.target.valid --Transition right into aiming if you've got a target.
				if not (yu.charge and (yu.charge > 9)) --Get a bit of extra charge!
					yu.charge = 9
				end
				GUN.HunterAnim(s, "aim", S_GUNH_BATTLE)
			end
		elseif yu.landanim
			GUN.HunterAnim(s, "land", S_GUNH_STAND)	--"land"
		elseif yu.recoilanim
			GUN.HunterAnim(s, "shoot", S_GUNH_BATTLE) --"shoot"
		elseif yu.aimanim	
			GUN.HunterAnim(s, "aim", (yu.aimanim < 0) and S_GUNH_STAND or S_GUNH_BATTLE) --"aim"
		end
	end
	
	--Kind of annoying I gotta redo SRB2's momentum code since it apparently has no effect on regular mobjs.
	--Oh well. SRB2's momentum code is simple enough.
	if yu.momx or yu.momy
		if P_IsObjectOnGround(s)
			if P_TryMove(s, s.x+yu.momx, s.y+yu.momy, true)
				local SPEED = abs(yu.momx)+abs(yu.momy)
				if (SPEED > 15*s.scale)
					GUN.QuickDust(s)
				end
			end
			if s.friction != FRACUNIT
				if (abs(yu.momx) > 2*s.scale)
					yu.momx = FixedDiv(FixedMul($, s.friction or 59392), FRACUNIT) --59392
				else
					yu.momx = 0
				end
				if (abs(yu.momy) > 2*s.scale) 
					yu.momy = FixedDiv(FixedMul($, s.friction or 59392), FRACUNIT)
				else
					yu.momy = 0
				end
			end
		else
			yu.momx = 0
			yu.momy = 0
		end
	end
	
	--Looking phase!
	if not (s.target and s.target.valid)
		if not yu.looktimer
		and not ((s.state == S_GUNH_FALL) and not yu.ambush) 
			local t,CLOSEST = GUN.SearchTarget(s, yu.lookdist, yu.searchtype, yu.cone, yu.hearing, yu.distpref)
			--Adjust how often to search depending on how far our last potential target was to save on resources.
			if not (t and t.valid) and CLOSEST != nil
			
				yu.looktimer = 
				(CLOSEST < (yu.lookdist or 2000*FRACUNIT)) and 4 or
				(CLOSEST < (yu.lookdist*2 or 3300*FRACUNIT)) and 8 or
				min((CLOSEST/130/FRACUNIT), 69)	
				
				if yu.cone == 360 --This is less intensive anyway, so do it more often.
					yu.looktimer = max(1, $/3)
				end
				
				if (s.frame & FF_FRAMEMASK) == K and yu.anim == nil --Lazy fix.
					s.frame = $ & ~FF_FRAMEMASK
				end
			else
				if t and t.valid
					s.target = t --You're our target now!
					
					if yu.ambush --Drop from above?
						
						s.flags2 = $ & ~MF2_DONTDRAW
						s.flags = $ & ~MF_NOGRAVITY
						if not yu.charge
							yu.charge = 9
						end
						if P_IsObjectOnGround(s) --Jump off the cliff!
							local TANGLE= GUN.AngleDifference(s.angle, R_PointToAngle2(s.x, s.y, t.x, t.y), 2)
							if (t.z+(150*t.scale) < s.z) and not (TANGLE > ANG10*12)	
								GUN.QuickDust(s)
								GUN.SpawnDust(s, nil, nil, true)
								local ghs = GUN.WindBlast(s)
								ghs.renderflags = $|RF_FLOORSPRITE
								s.z = $+(3*s.scale*P_MobjFlip(s))
								P_SetObjectMomZ(s, 7*s.scale)
								S_StartSoundAtVolume(s, sfx_ghsfx6, 40)
								S_StartSound(s, sfx_brakrl)
								for i = 1,2
									P_TryMove(s, s.x+FixedMul(21*s.scale, cos(s.angle)), s.y+FixedMul(21*s.scale, sin(s.angle)), true)
								end
								P_InstaThrust(s, s.angle, 2*s.scale)
								yu.landanim = -5
								yu.landingcd = 6
								GUN.HunterAnim(s, "land", S_GUNH_FALL)
							else --If they're behind or above you, just aim and shoot instead!
								yu.aimanim = 0
								GUN.HunterAnim(s, "aim", S_GUNH_BATTLE)							
							end
						else --Fall from above!
							s.target = nil
							P_SetObjectMomZ(s, -5*s.scale, true)
							GUN.GradualTurn(s, t, ANGLE_90) --Harsh turn!
							GUN.SpawnDust(s, nil, nil, true)
							
							local ghs = P_SpawnMobjFromMobj(s, s.momx,s.momy,s.momz, MT_EXPLODE)
							ghs.spriteyscale = $*3
							ghs.spritexscale = $*3
							ghs.dispoffset = 3
							GUN.HunterAnim(s, nil, S_GUNH_FALL)
							s.state = S_GUNH_FALL
						end
						yu.ambush = 0
					else
						if (s.state == S_GUNH_INACTIVE)
							S_StartSoundAtVolume(s, sfx_ghsfx1, 144)
							GUN.HunterAnim(s, "awaken", S_GUNH_STAND)
						else
							yu.aimanim = 0
							GUN.HunterAnim(s, "aim", S_GUNH_BATTLE)
						end
					end
					GUN.GradualTurn(s, t)
				end
				yu.looktimer = 8
			end
		end	
		
--#============================================================================================================================#
	else --We've got a confirmed and valid target! GUN HUNTER ATTAAAAAAACK!!!!
--#============================================================================================================================#
		local t = s.target	
		
		if yu.anim == nil --If not in a transitional animation, attack!		
		
			local TURNSPEED = (yu.shotcd) and ANG1*3 or (not P_IsObjectOnGround(s)) and ANG30 or (yu.charge) and ANG20 or ANG1*12
			
			if (s.state == S_GUNH_BATTLE) --We're fighting!
				if (s.frame & FF_FRAMEMASK) < M
					s.frame = ($ & ~FF_FRAMEMASK)|M
				end
				--Gradually face target, a bit slower.
				GUN.GradualTurn(s, t, TURNSPEED)
				if not yu.shotcd
					if not yu.charge --Play charging sound on start.
						S_StartSound(s, sfx_ghsfx4) --4 is the default laser charge.
					end
					yu.charge = $+1
					local CHARGEMAX = (yu.weapon == 777) and 2 or (yu.weapon == 2) and 70 or 14 --Laser needs less charge, while bomb and sniper have more.
					
					--Grenade behaviour.
					if (yu.weapon == 1)
						CHARGEMAX = 50
						local TRANS = (yu.charge < 8) and (10*FRACUNIT-(yu.charge*FRACUNIT)) or (yu.charge < 40) and FF_TRANS30 or FF_TRANS10
						local ghs = GUN.FloorTarget(t, (yu.charge > 40) and SKINCOLOR_RED or 0, 2, max(FRACUNIT+48000, FRACUNIT*7-(yu.charge*7500)),
						(yu.charge > 40) and AST_ADD or nil, TRANS)
						if ghs and ghs.valid
							if (yu.charge < 40)
								ghs.rollangle = leveltime*ANG10
							else
								ghs.z = $+(s.scale*P_MobjFlip(s))
							end
							ghs.momx = t.momx/3
							ghs.momy = t.momy/3
						end
					end
					if (yu.charge >= CHARGEMAX) //and (yu.cone == 360 or P_CheckSight(s,t)) --Wait until they show themselves, then fire immediately!
					
						S_StopSoundByID(s, sfx_ghsfx5)
						S_StopSoundByID(s, sfx_ghsfx4)
						
						yu.charge = 0
						yu.shotcd =  (yu.weapon == 777) and 1 or (yu.weapon == 1) and 50 or 25
						yu.recoilanim = 0
						GUN.HunterAnim(s, "shoot", S_GUNH_BATTLE)	
						GUN.GradualTurn(s, t, ANGLE_45)
						
					--	if yu.weapon == 2
					--		GUN.HunterSniperShot(s, t) --Fire a Sniper Bullet!
						if yu.weapon == 1
							GUN.HunterGrenade(s, t) --Lob a Grenade Shot!
						else
							GUN.HunterLaser(s, t) --Fire the basic laser!
						end
					end
				else
					yu.charge = 0
				end
			elseif not yu.shotcd and P_IsObjectOnGround(s)
				if (s.frame & FF_FRAMEMASK) != M
					yu.aimanim = 0
					GUN.HunterAnim(s, "aim", S_GUNH_BATTLE)	
				end
				GUN.GradualTurn(s, t, TURNSPEED)
			end
		end
		
		--Are we losing the target? Check only every so often to save resources.
		if not yu.looktimer
			local VIEWDIST = (yu.lookdist > 3000*FRACUNIT) and yu.lookdist or 3000*FRACUNIT
			
			if not t.health --They're dead, Jimmy...
				yu.lostsight = FRACUNIT*999
			elseif (FixedHypot(FixedHypot(t.x-s.x, t.y-s.y), t.z-s.z) > VIEWDIST)
				yu.lostsight = $+60 --REALLY far away...! Lose sight quickly.
			elseif not GUN.CanDetect(s,t)
				yu.lostsight = $+12 --Gradually lose target if they're close-by, but not within sight.
			end
			if (yu.lostsight > 100) --If yu.lostsight is above this, undo target.
				s.target = nil
				yu.lostsight = 0
				yu.looktimer = 3
				if (s.state == S_GUNH_BATTLE)
					yu.aimanim = -9
					GUN.HunterAnim(s, "aim", S_GUNH_STAND)
				end
			end
		end
	end
	
	--Timers.
	if yu.lostsight
		yu.lostsight = $-1
	end
	if yu.looktimer
		yu.looktimer = $-1
	end
	if yu.shotcd
		yu.shotcd = $-1
	end
	if yu.sprung
		yu.sprung = $-1
		if (abs(s.momz) > 4*s.scale) and not ((s.state == S_GUNH_BATTLE) and s.target and s.target.valid)
			s.state = S_GUNH_FALL
		end
	end
	if yu.ambush --Dismiss ambush state?
		if (s.state == S_GUNH_BATTLE) 
		or yu.anim or yu.flinch
		or (s.target and s.target.valid)
		or (abs(s.momz) > 5*s.scale)
			s.flags2 = $ & ~MF2_DONTDRAW
			yu.ambush = 0
		end
	end	
	
	--Record our last Z coordinates!
	if yu.landingcd
		yu.landingcd = $-1
	else
		yu.lastz = s.z
	end
end, MT_GUNHUNTER)

--Transition animation? If you don't fill in any, this'll reset animation stats instead.
GUN.HunterAnim = function(s, ANIM, STATE)
	if (STATE) and (s.state != STATE)
		s.state = STATE
	end
	
	local yu, FRAME = s.GUN_HunterTable, A
	
	if ANIM == "awaken" --4 frames. C to E. --(the inactive state itself is B, but not part of the animation!)
	
		yu.awakenanim = max($+1, 1)
		FRAME = (yu.awakenanim < 6) and C or (yu.awakenanim < 12) and D or E
		if (yu.awakenanim > 15) ANIM = 0 end
		
	elseif ANIM == "land" --4 frames. G to J.
	
		yu.landanim = (yu.landanim < 0) and $+1 or max($+1,1)
		FRAME = (yu.landanim < -2) and H or (yu.landanim < 0) and G or (yu.landanim < 3) and G or (yu.landanim < 12) and H or (yu.landanim < 15) and I or J
		if (yu.landanim > 18) or not yu.landanim or (yu.landanim == -1)
			ANIM = 0 
			if not P_IsObjectOnGround(s)
				STATE = S_GUNH_FALL
			end
		end
		
	elseif ANIM == "shoot" --Frames go from N to P.
	
		yu.recoilanim = $+1
		FRAME = (yu.recoilanim < 3) and N or (yu.recoilanim > 7) and P or O
		if (yu.recoilanim > 10) ANIM = 0 end --
		
	elseif ANIM == "aim" --Frames go from L to J. (Or in reverse.)
	
		yu.aimanim = (yu.aimanim < 0) and $+1 or max($+1, 1)
		FRAME = ((yu.aimanim < 5) and (yu.aimanim >= -3)) and K or L
		
		if not yu.aimanim or (yu.aimanim > 11) or (yu.aimanim == -1) ANIM = 0 end --
	end
--	priint("\x8c"+"Hunter ANIM:\x80 "+ANIM+"\x82 State:\x80 "+STATE+"\x84 Frame:\x80 "+R_Frame2Char(FRAME))
	s.frame = (s.frame & ~FF_FRAMEMASK)|FRAME
	
	yu.anim = ANIM --Record what we're currently doing for reference.
	
	if not ANIM
		yu.anim = nil
		if STATE and (s.state != STATE) --Set the state to the finish state all the time.
			s.state = STATE
		end
		yu.awakenanim,yu.recoilanim,yu.aimanim,yu.landanim = 0,0,0,0 --Reset all of these.
	end
end

--Oh no! Our expensive GUN Hunter mech has taken damage! ...seriously, these things must cost millions.
addHook("MobjDamage", function(s, i, so, dmg, dmgtype)
	local yu = s.GUN_HunterTable
	local HEALTH = s.health-1
	
	if dmgtype --Death effects?
		if (dmgtype & DMG_DEATHMASK)
			HEALTH = 0
		elseif (dmgtype == DMG_ELECTRIC)
			GUN.ElectricBody(s, 25)
		end
	end
	if so and so.valid
		if not so.GUNally
			GUN.GradualTurn(s, so, ANGLE_135) --Turn towards your attacker.
			if not (s.target and s.target.valid) --Immediately target whatever just attacked you!
				s.target = so
				if yu and (yu.looktimer > 3)
					yu.looktimer = 3
				end
			end
		end	
		--Take more damage from a powerful foe?
		local TARGETPOWER = GUN.TargetPower(so)
		if (TARGETPOWER > 1)
			HEALTH = $-TARGETPOWER
		end
	end

	--When you take damage, flinching behaviour!!
	if yu and not yu.flinch
		yu.charge = 0 --Charge is undone!
		if yu.shotcd --But expect retaliation shots soon!
			yu.shotcd = $/2
		end
		--Drop your shield if you've got one and you're low on health.
		yu.flinch = 16
		GUN.HunterAnim(s, nil, S_GUNH_LAND)
		GUN.HunterAnim(s, "land", S_GUNH_LAND) --Go into this state to simulate flinching!
		
		if i and i.valid
				
			if (i.type != MT_GUNHUNTER_LASER)  --Get knocked back a bit too?		
				local SPEED = (i.player and i.player.homing) and 70*i.scale or (abs(i.momx)+abs(i.momy)+abs(s.momz/3)) or 1
				if (SPEED > 12*s.scale)
					SPEED = min(83*i.scale, FixedDiv(FixedMul($, i.scale), s.scale*5/3))
					SPEED = FixedDiv(FixedMul($,50),39)
					local ANGLE = (SPEED > 15*s.scale) and R_PointToAngle2(i.x, i.y, i.x+i.momx, i.y+i.momy) or R_PointToAngle2(i.x-i.momx, i.y-i.momy, s.x, s.y)
					local TARGETPOWER = (min(3, GUN.TargetPower(so))+1) or 1	
					
					yu.forceANG = ANGLE
					yu.forceXY = (SPEED*TARGETPOWER)
					if not (abs(i.momz) > 15*i.scale) or (i.player and i.player.homing)
						yu.forceZ = 0
					else
						yu.forceZ = (i.momz/10)*TARGETPOWER
					end
					
					GUN.Quake(s, 
					max(8*FRACUNIT, SPEED), --Power
					min(11, max(2, FixedInt(SPEED/8)) ), --Tics
					max(250*FRACUNIT, SPEED*13)) --Dist
					if s.momz
						s.momz = $/3
					end
					if (HEALTH <= 1)
						GUN.DropShield(s, ANGLE, yu.forceXY+(5*s.scale))
					end
				end
			end
			
			--These guys will give momentum resistance, so jumping into them might open you up to be shot by another!
			--GUN Hunters are manageable enough alone, but can be a serious threat in groups. Pay attention to your surroundings!
			if (i == so) and i.player and (GUN.TargetPower(i) == 0)
				local p = i.player
				
				--If they're not homing attacking, affect their momz!
				if not (p.homing and (HEALTH < 1)) 
				and not (p.yusonictable and p.yusonictable.bouncestate)
				and not (p.xsonictable and (p.xsonictable.snapdive or p.xsonictable.snapdivetrick))
					if (abs(i.momz) > 4*i.scale)
						i.momz = $/2
					else
						P_SetObjectMomZ(i, -3*FRACUNIT, true)
					end
				end
				
				--Thrust them backwards depending on multiple factors.
				local ANGLE = R_PointToAngle2(s.x, s.y, i.x-i.momx, i.y-i.momy)
				local SPEED = max(5*s.scale, min(51*i.scale, p.speed+abs(s.momz/2) ) )
				if (HEALTH >= 1) --Forcefully shoot them back so they can't combo us by staying inside.
					SPEED = (p.homing) and 44*s.scale or max(35*s.scale, FixedDiv(FixedMul($, s.scale*3/2), i.scale))
					if p.homing or p.yusonictable and p.yusonictable.lsahoming
						P_Thrust(i, ANGLE, 132*i.scale)
						if p.yusonictable
							p.yusonictable.nosarun4 = 70
							if not p.powers[pw_super]
								p.yusonictable.nobouncedrag = 28
								p.yusonictable.nbdtarget = s
							end
						end
					end
					P_InstaThrust(i, ANGLE, SPEED)
				elseif not p.homing
					P_Thrust(i, ANGLE, SPEED)
				end
			end
		end
		S_StartSound(s, sfx_ghsfx6)
		if (HEALTH < 2) --Drop your shield if you've (still) got one and you're low on health.
			GUN.DropShield(s)
		end
	end
	if (HEALTH >= 1) --Survive this hit!
		s.health = HEALTH 
		s.flags = $ & ~MF_SHOOTABLE
		return true --Override damage hook.
	elseif (HEALTH <= 0) and s.valid and (s.flags & MF_SHOOTABLE) and not (yu and yu.starthealth < 2)
		P_KillMobj(s, i, so, dmgtype)
	end		
end, MT_GUNHUNTER)

--Argh, Metalwario64's beautiful GUN mech!! Prepare the death animation!
addHook("MobjDeath", function(s, i, so, dmgtype)
	local yu = s.GUN_HunterTable
	GUN.HunterAnim(s, nil, S_GUNH_LAND)
	if yu
		yu.charge = 0
		yu.flinch = yu.deathanim*2
		yu.looktimer = yu.flinch
		yu.lostsight = 0
	end

	--Drop your shield if you've got one.
	if s.GUNshield and s.GUNshield.valid
		local ghs = GUN.CraterSpew(s, ANGLE, RANGE)
		if ghs and ghs.valid
			P_Thrust(ghs, s.angle, -33*s.scale)
			ghs.sprite = SPR_G1SH
			ghs.frame = A
			ghs.rollangle = GUN.RNG(-50,50, s)
		end
		s.GUNshield.state = S_NULL
		s.GUNshield = nil
	end
	--Electricity aftereffects.
	if (dmgtype == DMG_ELECTRIC)
		GUN.ElectricBody(s, 50)
	elseif (dmgtype == DMG_FIRE)
		local ghs = GUN.SA2Explosion(s, FRACUNIT*2, SKINCOLOR_FLAME)
		ghs.frame = $+FRACUNIT*2
		ghs.spritexscale = $+34444
		ghs.spriteyscale = $+34444
	end
	if i and yu and not (yu.starthealth > 1)
		GUN.gibs(s, i)	
	end
	--Impact
	if yu.weapon != 777
		GUN.Quake(s, 50*FRACUNIT, 6, 500*FRACUNIT)
	else
		GUN.Quake(s, 60*FRACUNIT, 15, 1500*FRACUNIT)
		GUN.Flash(s, PAL_NUKE, 3, 1000*FRACUNIT)
	end
end, MT_GUNHUNTER)