--The good old classic AutoFrame. Also a debris thinker and some GFX functions that accompany these. 
--All effects here use the SW79 spriteslot for SAMUS.
local SAM = MetroidVanguard

--More flexible RNG function to SMSreborn's local RNG version.
--The big advantage to using this over P_RandomRange is that it doesn't update the RNG seed in netgames.
--s is optional, but recommended if you want RNG that actually seems random.
--Never call the same number ranges with the same object reference in the same tic, you'll get the same result.
SAM.RNG = function(a, b, s) --Can swap s with a.
	if type(s) != "userdata"
		if type(a)!="userdata" or type(s)!="number"
			print("\x85"+"RNG was input badly!\x80 Input = \x8a a:"+a+" b:"+b+" s:"+s) return 1 --
		end s,a = a,s --Swap them out, I guess.
	end
	if not netgame return P_RandomRange(a,b) end --Zero reason to use this function outside of netgames.
	
	local XYZ = (emeralds) or 77
	local TIME = (leveltime) or 1
	local JTIME = (TIME/3)
	local ANG = (gamemap) or 2>>16
	local EXTRA = (globallevelskynum) or 99
	if s
		XYZ = $+(s.x-s.y+s.z)
		EXTRA = $+(s.momx-s.frame+s.type-(s.floorz or 0))
		ANG = $+(s.angle>>15)
		JTIME = $+(s.player and s.player.jointime+19 or 9)
	end
	--Starting RNG seed
	local samRNGseed = (TIME%4==0) and 15248646 or (TIME%3==0) and JTIME<<16 or (TIME%2==0) and 2347239441 or 316574858 
	samRNGseed = $*(gamemap+TIME%128) - XYZ+ANG+EXTRA+JTIME
	samRNGseed = ($) and $/2 or 1623724541+JTIME

	local RESULT = (samRNGseed*36585539>>4) & 65535
	return (RESULT*(b-a+1)>>FRACBITS)+a
end
--Note to self about "A & B" calculations, normally used for flags!
--"A" is irrelevant to the actual equation. It's actually just a limiter.
--If "A" is lower than "B", your result's automatically 0. Otherwise, A is irrelevant, and you need focus only on "B"
--Calculate how many bits fit into B, THAT'll be your result. Bits are multiplications of 2, by the way. 
--1 bit = 1
--2 bits = 2
--3 bits = 4
--4 bits = 8
--5 bits = 16
--6 bits = 32, etc.
--A "FRACUNIT" is 16 bits, AKA 65536.
--While "FRACBITS" is 5 bits. AKA 16.

--GFX object when you need capechase, autoframing, gradual fading, offsets, etc.
SAM.AutoFrame = function(s) if not s.valid return end --...duh, be valid.
	--Apply autoframe behaviour? Basically an adjustable FF_ANIMATE that can loop within the same sprite.
	if s.sttspeed
		if s.sttspeed == 1 or (leveltime % (s.sttspeed) == 0) --Animation speed. If sttspeed is 2, we'd advance frames every 2 tics.
			if (s.frame & FF_FRAMEMASK) == s.sttfinalframe --Never go past this frame!
				if s.sttfirstframe != nil --Unless we want it to loop?
					s.frame = ($ & ~FF_FRAMEMASK)|s.sttfirstframe
				end
			else		
				--If you don't specify a frameloop, make sure you don't exceed the amount of available frames!
				if s.sttlastframe != nil and ((s.frame & FF_FRAMEMASK) >= s.sttlastframe) 
					s.frame = ($ & ~FF_FRAMEMASK)|(s.sttfirstframe or 0)
				else
					s.frame = $+1
				end
			end
		end
	end
	
	--Fade if sttfade exists.
	if s.sttfade
		local FADE = (s.sttfade == true) and 10 or s.sttfade
		if (s.fuse > FADE)
			FADE = nil
		elseif (FADE != 10) and (leveltime % 2)
			FADE = nil --Fade slower if it's not 10...
		end
		if FADE --Fade confirmed. Go!
			local trans = (s.frame & FF_TRANSMASK)
			s.frame = ($ & ~FF_TRANSMASK)|min(FF_TRANS90, (trans+FRACUNIT))
			if s.sttroll --Slow rollspeed too.
				s.sttroll = FixedDiv(FixedMul(s.sttroll, 20),21)
			end
		end
		--Automatically roll at any given speed?
		if s.sttroll
			if (s.renderflags & RF_FLOORSPRITE)
				s.angle =  $+s.sttroll
			else
				s.sttrollangle = s.sttrollangle and $+s.sttroll or s.sttroll
				if (leveltime%2) or not s.rollangle
					s.rollangle = s.sttrollangle
				end
			end
		end	
	end
	
	--Scroll graphic?
	if s.sttXspeed
		s.spritexoffset = $+s.sttXspeed	
	end
	if s.sttYspeed
		if s.floorspriteslope --Raise the tilt of slopesprites instead!
			local EX,EY,EZ = s.floorspriteslope.o.x, s.floorspriteslope.o.y, s.floorspriteslope.o.z
			s.floorspriteslope.o = {x = EX, y = EY, z = EZ+s.sttYspeed}
		else
			s.spriteyoffset = $+s.sttYspeed
		end
	end
	
	local t = s.tracer if not (t and t.valid) return end --Will need a tracer from here on!	
	
	--Apply capechase and potentially also offsets if given a tracer!
	if (s.renderflags & RF_FLOORSPRITE) 
		P_SetOrigin(s, t.x, t.y, t.z) --No capechase for floorsprites! I don't want the object's angle to change!
	else
		local PREVANGLE = s.angle
		if s.ditchtracer != 1 
			A_CapeChase(s, 1, 0) 
			if not s.valid return end --Capechase removes the object sometimes, so always check s.valid afterwards!
		end
		if s.ditchtracer --Ditch the capechase after any couple of frames?
			s.ditchtracer = $-1 --This is a number value, NOT true or false!
			if s.ditchtracer == 0
				if s.GSoffsetangFACE != nil --Face target one last time! 
					local EXT = (tonumber(s.GSoffsetangFACE)) or s.angle
					P_MoveOrigin(s, s.x+FixedMul(s.GSoffsetX, cos(s.GSoffsetang+EXT)), s.y+FixedMul(s.GSoffsetY, sin(s.GSoffsetang+EXT)), s.z)
					s.angle = R_PointToAngle2(s.x, s.y, t.x, t.y) + ((s.renderflags & RF_PAPERSPRITE) and ANGLE_90 or 0)
				else
					s.angle = PREVANGLE --Undo capechase's angle copying.
				end 
				s.tracer = nil return --We're done now!
			end
		end
	end	
	
	--If you set one offset, then set them all, even if they're just 0.
	if s.GSoffsetX != nil
		local ANGLE = (s.GSoffsetang != nil) and s.GSoffsetang or t.angle --Force a specific angle, or use the tracer's angle?	
		s.GSoffsetwait = (s.GSoffsetwait) and 1 or 2
		local ZFLIP = 1 if t and t.valid ZFLIP = P_MobjFlip(t) end
		P_MoveOrigin(s,
		s.x+ FixedMul(s.GSoffsetX/s.GSoffsetwait, cos(ANGLE)),
		s.y+ FixedMul(s.GSoffsetY/s.GSoffsetwait, sin(ANGLE)),
		s.z+ (s.GSoffsetZ or 0)*ZFLIP )
		
		--Adjust offsets overtime? 110 would INCREASE distance by 10% each frame, 80 would DECREASE distance by 20%.
		if s.GSshiftoffsetXY and (s.GSoffsetX or s.GSoffsetY)
			s.GSoffsetX = FixedDiv(FixedMul($, s.GSshiftoffsetXY), 100)
			s.GSoffsetY = FixedDiv(FixedMul($, s.GSshiftoffsetXY), 100)
		end
		if s.GSshiftoffsetZ and s.GSoffsetZ
			s.GSoffsetZ = FixedDiv(FixedMul($, s.GSshiftoffsetZ), 100)
		end
		
		--Face tracer all the time? If set to "false" or "0", the obejct will face it's tracer without adjusting offsets!
		if s.GSoffsetangFACE != nil 
			s.angle = t.angle + ((s.renderflags & RF_PAPERSPRITE) and ANGLE_90 or 0)
			if s.GSoffsetangFACE --Rotate around tracer at this speed?
				s.GSoffsetang = $+s.GSoffsetangFACE
			end 
			return --Don't need to do further papersprite checks.
		end
	end
	--Properly angle papersprites with tracers.
	if (s.renderflags & RF_PAPERSPRITE)
		local EXTRA = (s.GSpaperang) or 0
		s.angle = t.angle+ANGLE_90+EXTRA
	end
end
addHook("MobjThinker",  function(s) SAM.AutoFrame(s) end, MT_SAMAUTOFRAME)

--Speed Booster Wind Effects. Lots of math, good luck! "SuperWindLines" "SpeedLines"
SAM.SpeedBoosterWindLines = function(s, DIST, OFFSETS, TRANS, CSCALE, FUSE, COLOR, FRAME)

	local MOMANG,TMOMZ = s.angle, abs(s.momz/s.scale) //R_PointToAngle(s.x,s.y,s.x+s.momy,s.y+s.momy)
	local MINZ = (abs(s.z+s.momz-s.floorz) > 77*s.scale) and -35 or 1
	DIST = ((DIST) or 150*s.scale) + (TMOMZ*2*s.scale)
	DIST = FixedDiv(FixedMul(DIST, 100), P_RandomRange(100,400))
	FUSE = (FUSE) or 10	
	
	local ZFLIP = 1 if s and s.valid ZFLIP = P_MobjFlip(s) end
	local ZOFFSET = FixedMul(FixedDiv(DIST, 50), P_RandomRange(MINZ,70)) * ZFLIP --Vertical position.
	ZOFFSET = $ + ( ((s.momz<-6*s.scale) and s.height/2 or s.height/2)*ZFLIP )
	if not splitscreen and s.player and displayplayer
		if s.player.samspbschargefull
			if s.player == displayplayer and camera and camera.chase == true
			or (s.player != displayplayer)
				return	--
			end
		end
	end
	
	if FRAME == nil --By default, make the frames momentum based.
		FRAME = ((TMOMZ > 29) and H or (TMOMZ > 5) and G or F) | ((TRANS) or FF_TRANS30) | ((s.momz > 0) and FF_VERTICALFLIP or 0)
	end	
	local XTRAX,XTRAY = 0,0
	if (OFFSETS) --An optional table to mess with offsets. If you use it, both XY and EZ must be valid.
		XTRAX,XTRAY,ZOFFSET = OFFSETS.XY, OFFSETS.XY, $+OFFSETS.EZ 
	end
	local XOFFSET = FixedMul(XTRAX+(TMOMZ*2*s.scale), cos(MOMANG)) - (s.momx*4/3) --Final XY coordinate adjustments.
	local YOFFSET = FixedMul(XTRAY+(TMOMZ*2*s.scale), sin(MOMANG)) - (s.momy*4/3)
	
	for i = -1,1,2
		local ghs = P_SpawnMobjFromMobj(s,
		XOFFSET + s.momx + FixedMul( (DIST*i), cos(MOMANG+ANGLE_90)) + FixedMul(DIST*2 + 250*s.scale, cos(MOMANG)),
		YOFFSET + s.momy + FixedMul( (DIST*i), sin(MOMANG+ANGLE_90)) + FixedMul(DIST*2 + 250*s.scale, sin(MOMANG)),
		ZOFFSET + s.momz, MT_SAMAUTOFRAME)
		
		ghs.renderflags = RF_PAPERSPRITE
		ghs.sprite = SPR_SW79 -- 1 is left, -1 is on the right!
		ghs.frame = FRAME | (i==1 and FF_HORIZONTALFLIP or 0) 
		ghs.color = (COLOR) or SKINCOLOR_SILVER
		ghs.fuse = FUSE
		ghs.sttfade = (FUSE<13) and true or FUSE
		if (TMOMZ < 11)
			ghs.momx,ghs.momy = -s.momx,-s.momy
		end
		if CSCALE
			ghs.spritexscale,ghs.spriteyscale = CSCALE,CSCALE
		end
		ghs.angle = MOMANG - ((s.momz<-6*s.scale) and ANG1*2*i or ANG1*5*i) + (i==1 and ANGLE_180 or 0)
		if s.player
			ghs.renderflags = RF_PAPERSPRITE|RF_FULLBRIGHT|RF_NOCOLORMAPS
			P_Thrust(ghs, s.angle, (leveltime%2) and -50*s.scale or -28*s.scale)
		end
	end
end