--#=================================================#
--Thinkers for X Sonic's effects objects.
--#=================================================#
local XSon = XSon

--GFX object when you need capechase, autoframing, gradual fading, offsets, etc.
XSon.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 (XS_CRAPPYPRIORITIES!=1)
					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

	if not (s.tracer and s.tracer.valid) return end --Will need a tracer from here on!	
	local t = s.tracer
	
	--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) 
		end
		if not s.valid return end --Capechase removes the object sometimes, so always check s.valid afterwards!

		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 --Done!
			end
		end
	end	
	
	--If you set one offset, then set them all, even if they're just 0.
	if s.GSoffsetX != nil
		if s.GSspiralupper
			s.GSoffsetang = s.GSoffsetang and $+s.GSspiralupper or s.GSspiralupper
		end
		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
		
		if t.valid
			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)*P_MobjFlip(t) )
		end
		
		--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) XSon.AutoFrame(s) end, MT_XSAUTOFRAME)

--Putting in a rawset so I can call it in postthinkframe.
XSon.OverGlowThinker = function(s)
	if not (s.valid and s.tracer and s.tracer.valid)
		if s.valid and not (s.fuse) P_RemoveMobj(s) end
		return -- 
	end
	A_CapeChase(s,1,0) if not s.valid return end --Always beware of capechase!	
	
	local t = s.tracer
	if (t.flags & MF2_DONTDRAW) or t.sprite == SPR_NULL
		s.frame = $ & ~FF_FRAMEMASK
		s.flags2 = $|MF2_DONTDRAW return --Don't draw if our tracer isn't visible
	end
	
	if s.XAura == "blur" --If we have this, we're the blur underneath Super Sonic's aura.
		local p = t.player local xs = p and p.xsonictable
		if not (p and p.valid) P_RemoveMobj(s) return end --
		s.skin = "xsonicoverlay" --Always force this skin for the blur overlay.
		s.sprite = SPR_PLAY
		
		local SPRITE2 = (t.sprite2 & ~FF_SPR2SUPER)
		
		if (t.state == S_PLAY_S_XTRANSFORM) 
		or p.xsringburst
		or SPRITE2 == nil --In these cases, turn the sprite off and wait!
		or not ( P_IsValidSprite2(s, SPRITE2) )
			s.frame = $ & ~FF_FRAMEMASK
			s.flags2 = $|MF2_DONTDRAW return --
		else --Everything is valid. Go ahead!
			if (t.eflags & MFE_VERTICALFLIP)
				s.eflags = $|MFE_VERTICALFLIP|MFE_FORCENOSUPER
			else
				s.eflags = ($|MFE_FORCENOSUPER) & ~MFE_VERTICALFLIP
			end
			s.sprite2 = (SPRITE2==SPR2_WALK) and SPR2_TAL6 or SPRITE2
			s.frame = (t.frame & FF_FRAMEMASK) | FF_TRANS60 --((s.dispoffset==-4) and FF_TRANS10 or FF_TRANS60)
			s.spritexoffset = t.spritexoffset
			
			if xs and xs.darkphase --SKINCOLOR_DAKUSONIKKU , SKINCOLOR_XSDARKNESS
				local PHASE = max(1, min(3, xs.darkphase))
				s.color = SKINCOLOR_XSDARKNESS
				s.frame = ($ & ~FF_TRANSMASK)|(10<<16-(PHASE<<16))
				s.blendmode = AST_TRANSLUCENT
			end	
			if not (xs and xs.ChaosSnap and (xs.ChaosSnap.timer > 5)) and not t.XSsuperroll
				if (leveltime%4==0) and not (XS_CRAPPYPRIORITIES==1)
					s.rollangle = t.rollangle  --Be conservative with rollangle copying to prevent memory leaks on SRB2's end.
				end
			end
		end	
	else --if not, we're any old chase object!
		if s.sttfade == nil or s.XAura == "scaled"
			s.spritexscale,s.spriteyscale = t.spritexscale,t.spriteyscale
		end
		s.eflags = t.eflags
		s.spritexoffset,s.spriteyoffset = t.spritexoffset,t.spriteyoffset
		
		if s.copyframe --Copy target's frame?
			local p = t.player local xs = p and p.xsonictable
			if p and s.skin
				s.skin,s.sprite = t.skin,t.sprite
				if s.sprite == SPR_PLAY
					if not (P_IsValidSprite2(s, t.sprite2)) 
						s.frame = $ & ~FF_FRAMEMASK
						s.flags2 = $|MF2_DONTDRAW return --That's bad, abort!
					else
						s.sprite2 = t.sprite2 --Got it! 
					end
				end
			end
			s.frame = ($ & ~FF_FRAMEMASK)|(t.frame & FF_FRAMEMASK)		
		end

		if (leveltime%2) and (XS_CRAPPYPRIORITIES!=1) --Being conservative...
			s.rollangle = t.rollangle
		end
	end	
	s.angle = t.player and t.player.drawangle or s.angle
	s.mirrored = t.mirrored
	
	--Mimmick flip flags always!
	s.frame = (t.frame & FF_HORIZONTALFLIP) and $|FF_HORIZONTALFLIP or $&~FF_HORIZONTALFLIP
	s.frame = (t.frame & FF_VERTICALFLIP) and $|FF_VERTICALFLIP or $&~FF_VERTICALFLIP
	s.flags2 = $ & ~MF2_DONTDRAW
	
	if s.sttfade
		XSon.AutoFrame(s)
	end
end


--Over aura for Super Sonic. It's the blur that makes him look shiny.
--If given sttfade, it'll use both the overglow thinker and the autoframe thinker!
addHook("MobjThinker", function(s)	
	if s.XAura
		if s.XAura == "blur"
			return --
		elseif s.XAura == "MSmash"
			XSon.AutoFrame(s) return --
		end
	end		
	if s.sttfade != nil
		XSon.OverGlowThinker(s)
	end
end, MT_XSOVERGLOW)

--Under aura for Super Sonic. It's the fancy animated part of his aura, rendered below his sprite.
addHook("MobjThinker", function(s)
	if s.XAura == "ringburst"
		if (leveltime % 2) --Do X Aura thinking?
			local FRAME = (s.frame & FF_FRAMEMASK) --Last frame?
			s.frame = $ & ~FF_FRAMEMASK
			
			if not (FRAME >= Z+6) --Do we advance the frame?
				s.frame = $|(min(Z+6, FRAME+1) )
			end	
		end
	end
	--Prefably chase your tracer, but a target's good too.
	if s.tracer and s.tracer.valid 
		A_CapeChase(s,1,0) return --
	elseif s.target and s.target.valid 
		A_CapeChase(s,0,0) return  --
	end
	if not (s.fuse) s.fuse = 8 end
	s.tracer,s.target = nil --Remove if neither are valid.
end, MT_XSUNDERGLOW)


--Ring Burst ball! A CGI-styled spinball that sort of mimmicks Adventure's.
addHook("MobjThinker", function(s) if not s.valid return end --
	local t = s.target 
	if t and t.valid
		A_CapeChase(s,1,0) if not s.valid return end --
		local p = t and t.player local xs = p and p.xsonictable
		if not (xs and IsXSonic(t)) return end --Cape Chase is dangerous....
		
		s.fuse = min($,3)
		s.skin = t.skin
		s.angle = p.drawangle
		if xs.supersonic and not xs.darkphase
			s.color = (t.XS_modScolor) and t.XS_modScolor or t.color
		end
	else
		P_RemoveMobj(s)
	end
end, MT_XSRINGBURSTBALL)
