--#==============================================================================#
--GFX mainly meant for Super Sonic.
--#==============================================================================#
local XSon = XSon
local function P_MobjFlip(s) if s and s.valid and ((s.eflags&MFE_VERTICALFLIP) or (s.flags2&MF2_OBJECTFLIP)) return -1 end return 1 end

--Create a transformation wind effect!
XSon.TransformGFX = function(s, COLOR, FRAME)		
	local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_XSAUTOFRAME)	
	ghs.sprite = SPR_XSG1
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = (FRAME) or I|TR_TRANS10
	ghs.blendmode = AST_ADD
	ghs.fuse = 25
	ghs.sttfade = 6
	if (COLOR)
		ghs.color,ghs.colorized = COLOR,true
	end
	ghs.dispoffset = 1
	ghs.sttspeed = 2
	ghs.sttfinalframe = Q
	ghs.tracer = s return ghs --
end

--Large Rising Orbs! Can be heavily modified, for example, turned into outter space.
XSon.TransformGFX2 = function(s, OFFSET, SPEED, SCALE, FRAME, DIST, COLOR)			
	local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_XSAUTOFRAME)	
	ghs.sprite = SPR_XSG1
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = (FRAME) or H|TR_TRANS10
	ghs.blendmode = AST_ADD
	ghs.fuse = 38
	ghs.sttfade = 18
	ghs.spriteyoffset = (OFFSET) or $
	ghs.sttYspeed = (SPEED) or 40<<16
	if (COLOR)
		ghs.color,ghs.colorized = COLOR,true
	end
	if (SCALE)
		ghs.spritexscale,ghs.spriteyscale = SCALE,SCALE
	end
	ghs.dispoffset = -1
	ghs.GSoffsetX = (DIST) or 2*s.scale
	ghs.GSoffsetY = (DIST) or 2*s.scale
	ghs.GSoffsetZ = -40*s.scale
	ghs.tracer = s return ghs --
end

--A light ideally called for flickering!
XSon.FlickerLight = function(s, COLOR, FUSE, ROLL, TRANS, CSCALE)
	local ghs = P_SpawnMobjFromMobj(s,0,0,s.height/2, MT_XSAUTOFRAME)
	ghs.sprite = SPR_XSG4
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.blendmode = AST_ADD
	ghs.frame = P|((TRANS) or FF_TRANS30)
	ghs.destscale = 99<<16
	ghs.tracer = s
	ghs.fuse = (FUSE) or 5
	ghs.sttfade = true
	ghs.dispoffset = 8
	ghs.spritexscale = (CSCALE) or $
	ghs.spriteyscale = (CSCALE) or $
	if ROLL and not (XS_CRAPPYPRIORITIES==1)
		ghs.rollangle = ROLL
	--	ghs.sttrollangle = ROLL
	--	ghs.sttroll = (ROLL>0) and ANG2 or -ANG2
	end
	if COLOR
		ghs.color = COLOR
		ghs.colorized = true
	end	return ghs --
end

--A brilliant radiant light overlaid on top of Super Sonic!
XSon.RadiantLight = function(s, COLOR, FUSE, ROLL, FRAME, CSCALE)
	local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_XSAUTOFRAME)
	ghs.sprite = SPR_XSG4
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.blendmode = AST_ADD
	ghs.frame = (FRAME) or S|FF_TRANS10 --S|FF_TRANS10 is the Brave Shine, O|FF_TRANS10 is SMSreborn's sprite.
	ghs.scalespeed = s.scale/10
	ghs.destscale = 99<<16
	ghs.tracer = s
	ghs.fuse = (FUSE) or 14
	ghs.sttfade = true
	ghs.ditchtracer = max(1,(ghs.fuse-3))
	ghs.dispoffset = 25
	ghs.spritexscale = (CSCALE) or $
	ghs.spriteyscale = (CSCALE) or $
	if (s.XS_modScolor) and s.player and s.player.powers[pw_super] 
		ghs.color = s.XS_modScolor
	else	
		ghs.color = (COLOR) or SKINCOLOR_SUPASONIKKU
	end
	if (ghs.frame & FF_FRAMEMASK) != O
		ghs.colorized = true
		ghs.sttYspeed = -35000
	end
	if ROLL and not (XS_CRAPPYPRIORITIES==1)
		ghs.rollangle = ROLL
	--	ghs.sttrollangle = ROLL
	--	ghs.sttroll = ROLL
	end return ghs --
end

--A massive exploding tornado impact! This is only the visual effect, no hitbox!
XSon.SuperSonicWind = function(s, COLOR, FUSE, TRANS, CSCALE)
	local ghs = P_SpawnMobjFromMobj(s,0,0,s.height/2,MT_XSAUTOFRAME)
	ghs.sprite = SPR_XSG4
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.blendmode = AST_ADD
	ghs.frame = A|((TRANS) or FF_TRANS10)
	ghs.fuse = (FUSE) or 50	
	ghs.sttspeed = (ghs.fuse <= 44) and 4 or 5
	ghs.sttfinalframe = G 
	ghs.scalespeed = s.scale/16
	ghs.destscale = 99<<16
	ghs.sttfade = 7
	ghs.ditchtracer = max(1,(ghs.fuse-3))
	ghs.dispoffset = 2
	ghs.spritexscale = (CSCALE) or FRACUNIT+6000
	ghs.spriteyscale = (CSCALE) or FRACUNIT+6000
	ghs.sttYspeed = -5500
	if COLOR
		ghs.color = COLOR
		ghs.colorized = true
	end return ghs --
end

--Shoot forward a Super Sonic Boom! Effect only.
XSon.SuperSonicBoomShot = function(s, XYTHRUST, ZTHRUST, COLOR, FUSE, TRANS, CSCALE, xs)
	if xs --First, create an actual Sonic Boom effect. Unless you don't want one, then don't define xs.
		for i = 1,3
			local ghs = XSon.SonicBoom(s.player, s, xs, 9+i)
			if ghs and ghs.valid
				ghs.frame = ($ & ~FF_TRANSMASK)|(FF_TRANS40)|((i!=2) and FF_HORIZONTALFLIP or 0)
				ghs.renderflags = RF_PAPERSPRITE|RF_FULLBRIGHT|RF_NOCOLORMAPS
				P_Thrust(ghs, s.angle, 10*s.scale*i)
				if i == 1 
					ghs.renderflags = $ & ~RF_PAPERSPRITE
					ghs.frame = $-(FF_TRANS10*3)
					XS_MediumDust(s, (xs.darkphase) and SKINCOLOR_RUBY or SKINCOLOR_SUPERSKY4, AST_ADD, Q|FF_TRANS40, 48*s.scale, s.angle)	
				else
					ghs.colorized = true
					ghs.color = (xs.darkphase) and SKINCOLOR_RUBY or (i==2) and SKINCOLOR_GOLDENROD or SKINCOLOR_SUPERSKY3
					ghs.spriteyoffset = $+(6<<16*i)
					ghs.scale = $*6/(5+(i*2))
					ghs.scalespeed = ((s.scale/12)*5)/(4+i)
					ghs.fuse = $+5
				end
				ghs.destscale = 9<<16
				if xs.ChaosLambent 
					ghs.spritexscale = $*3/2
					ghs.spriteyscale = $*3/2
					ghs.fuse = min($,15)
					ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
					ghs.sttfade = true
					ghs.spriteyoffset = $-(11<<16)
					ghs.color = SKINCOLOR_SUPERGOLD2+i
					if xs.ChaosLambent.Zangle
						ghs.momz = (xs.ChaosLambent.Zangle > 0) and $+(xs.ChaosLambent.Zangle/3) or $+xs.ChaosLambent.Zangle
					end
				end
				P_SetOrigin(ghs, s.x+FixedMul(16*s.scale*i, cos(s.angle)), s.y+FixedMul(16*s.scale*i, sin(s.angle)), ghs.z)
			end
		end
	end
	if CSCALE == "noboom" return end --
	
	TRANS = (TRANS) or FF_TRANS10
	CSCALE = (SCALE) or 2<<16
	local DIST = FixedInt(19*CSCALE)*s.scale
	local ths,thss,ghs
	for i = -3,3,1
		if i == 0 continue end
		ghs = P_SpawnMobjFromMobj(s,
		s.momx+FixedMul(DIST*i, cos(s.angle-ANGLE_90)),
		s.momy+FixedMul(DIST*i, sin(s.angle-ANGLE_90)),
		s.momz+(s.height/5),MT_XSAUTOFRAME)
		
		ghs.sprite = SPR_XSG4
		ghs.frame = ((i>=1) and Q or R)|TRANS
		ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS|RF_PAPERSPRITE
		ghs.flags = $|MF_NOCLIPTHING|MF_BOUNCE & ~(MF_NOCLIPHEIGHT|MF_NOCLIP)
		ghs.blendmode = AST_ADD
		ghs.fuse = (FUSE) or 35
		ghs.sttfade = min(14, (FUSE/2))
		ghs.color = (COLOR) or (abs(i)==1) and SKINCOLOR_XSSUPERBLUR or SKINCOLOR_SUPERSKY5
		ghs.spritexscale = CSCALE
		ghs.spriteyscale = CSCALE	
		if ZTHRUST
			P_SetObjectMomZ(ghs, ZTHRUST)
		end		
		if XYTHRUST
			if abs(i) != 2
				ghs.angle = s.angle+(ANG20*2*i)
				ghs.scale = $*3/2
				ghs.scalespeed = (4*s.scale)/(ghs.fuse+1)
				ghs.destscale = ghs.scale*8
				P_InstaThrust(ghs, s.angle-(ANG1*i), XYTHRUST)
				local pff
				for d = 2,6,2
					pff = P_SpawnGhostMobj(ghs)
					pff.spritexscale = CSCALE*3/2
					pff.spriteyscale = CSCALE*3/2
					P_SetOrigin(pff, 
					pff.x+FixedMul(XYTHRUST*d, cos(s.angle))+FixedMul(((DIST*3)/d)*i, cos(s.angle-ANGLE_90)),
					pff.y+FixedMul(XYTHRUST*d, sin(s.angle))+FixedMul(((DIST*3)/d)*i, sin(s.angle-ANGLE_90)), pff.z)
					P_InstaThrust(pff, s.angle, (XYTHRUST/2)/d)
					if d != 2
						pff.color = (d==4) and SKINCOLOR_SUPERORANGE5 or SKINCOLOR_FLAME
					end
				--	pff.frame = ($ & ~FF_TRANSMASK)|((11<<16)-(d<<16))
					pff.fuse = max(3, (FUSE/2)-d) pff.tics = 99
					pff.destscale = 99<<16
					pff.momz = ghs.momz
				end
			else
				if abs(i) == 3
					ghs.angle = s.angle+(ANG1*5*i)
					ghs.fuse = 12
					ghs.sttfade = true
					P_InstaThrust(ghs, s.angle-(ANG2*i), XYTHRUST*3/5)
					ghs.scale = $*2
				else
					ghs.angle = s.angle+(ANG1*6*i)
					P_InstaThrust(ghs, s.angle-(ANG1*i), XYTHRUST*3/4)
					ghs.spritexscale = $*2
					ghs.scale = $*3
					ghs.destscale = ghs.scale
					ghs.scalespeed = (2<<16)/ghs.fuse					
				end
			end
		end
		if i==1 ths = ghs elseif i==-1 thss = ghs end
	end return ths,thss
end

--Overlay electricity onto "s"!
XSon.ElecticBody = function(s, FUSE, COLOR, TRANS, BLEND)
	local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_XSAUTOFRAME)
	ghs.sprite = SPR_XSG4
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.blendmode = (BLEND) or AST_ADD
	ghs.frame = H|((TRANS) or FF_TRANS10)
	ghs.fuse = (FUSE) or 6
	ghs.sttspeed = 1
	ghs.sttfirstframe = H
	ghs.sttfinalframe = L
	ghs.dispoffset = 2
	ghs.tracer = s 
	if COLOR
		ghs.color = COLOR
		ghs.colorized = true
	end return ghs --
end

--Strike an area with lightning! Can follow or not.
XSon.LightningStrike = function(s, COLOR, TRACER, ALT)	
	local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSAUTOFRAME)
	if ALT
		ghs.sprite = SPR_XSG1
		ghs.frame = (Z+2)|FF_TRANS10
		ghs.sttfinalframe = Z+3
	else
		ghs.sprite = SPR_XSG4
		ghs.frame = M|FF_TRANS10
		ghs.sttfinalframe = N
	end
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_NOCOLORMAPS|RF_FULLBRIGHT
	ghs.color = (COLOR) or SKINCOLOR_GOLDENROD
	ghs.sttfade = true
	ghs.scale = s.scale*2/3
	ghs.scalespeed = 54460
	ghs.destscale = s.scale*2
	ghs.sttspeed = 3
	ghs.fuse = 7
	ghs.tracer = (TRACER) or nil
	return ghs --
end

--De-transformation effect. Can skip parts of the functions.
XSon.DeTransformGFX = function(p,s,xs, SKIP)
	if SKIP != "ghosts"
		local COLOR = (xs.darkphase) and XSon.DarkAura(0) or 0
		for i = 0,3
			local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_XSOVERGLOW)
			ghs.fuse = (13-i) ghs.tics = 99
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.blendmode = (not xs.darkphase) and AST_ADD or $
			ghs.color = (xs.darkphase) and SKINCOLOR_PASTEL-i or SKINCOLOR_SUPERGOLD4-i
			ghs.colorized = true
			ghs.destscale = 99<<16
			ghs.frame = ($ & ~FF_TRANSMASK)|(FF_TRANS40+(i<<16))
			ghs.scalespeed = FRACUNIT/(12-(i*2))
			ghs.sttYspeed = -2<<16
			ghs.dispoffset = (i<1) and -9 or 99
			ghs.sttfade = 12-i
			ghs.tracer = s
			ghs.copyframe = true
			if not SKIP
				XSon.sparkles(p,s,xs, 30+(i*3), 5, i*2, 3<<16+(i*25500), 
				(xs.darkphase) and SKINCOLOR_BLACK or P_RandomRange(SKINCOLOR_SUPERGOLD1, SKINCOLOR_SUPERGOLD4), ghs.blendmode)
			end
			XSon.OverGlowThinker(ghs)
		end
	end
	if SKIP != "all" --Sonic himself starts glowing!
		local ghs = XSon.Overglow(s, 2, (xs.darkphase) and SKINCOLOR_XSSUPERBLUR or SKINCOLOR_YELLOW)
		if ghs and ghs.valid
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
			ghs.fuse = 28 ghs.tics = 28
			ghs.sttfade = 25
			ghs.tracer = s
			ghs.blendmode = (xs.darkphase) and AST_REVERSESUBTRACT or AST_ADD
			if SKIP == "ghosts" return ghs end --
		end
	end
end

--Spawn a crapton of orbs around yourself. They're thankfully just huge sprites instead of 500 individual objects.
XSon.OrbPillars = function(s, NO, DIST, LAYERS, YSPEED, FRAME, CSCALE, FUSE, COLOR)
	NO = (NO) or 8
	local ANGPLUS = (360/NO)*ANG1
	local DIST = (DIST) or 350*s.scale
	local LAYERS = (LAYERS) or 0
	CSCALE = (CSCALE) or FRACUNIT+20000
	for i = 1,NO,1
		for d = -LAYERS-1,LAYERS-1
			local ghs = P_SpawnMobjFromMobj(s,
			s.momx+FixedMul(DIST, cos(s.angle+(ANGPLUS*i))),
			s.momy+FixedMul(DIST, sin(s.angle+(ANGPLUS*i))),
			s.momz-(DIST/2)+(DIST*d), MT_XSAUTOFRAME)		
			ghs.sprite = SPR_XSG1
			ghs.renderflags = RF_FULLBRIGHT
			ghs.blendmode = AST_ADD
			ghs.frame = (FRAME) or H|FF_TRANS20
			if (NO%2==0) ghs.frame = $|FF_HORIZONTALFLIP end
			
			ghs.fuse = (FUSE) or 18
			if (d>0) ghs.fuse = $*2/3 end
			
			ghs.sttfade = true
			ghs.sttYspeed = (YSPEED) or 12<<16
			ghs.spritexscale,ghs.spriteyscale = CSCALE,CSCALE
			ghs.momx,ghs.momy,ghs.momz = s.momx/2,s.momy/2,s.momz/3
			if COLOR
				ghs.color = COLOR
				ghs.colorized = true
			end	
			if NO == 1 and LAYERS == 0
				return ghs --
			elseif (ghs.frame & FF_FRAMEMASK) == H
				ghs.renderflags = RF_PAPERSPRITE
				ghs.angle = R_PointToAngle2(ghs.x, ghs.y, s.x, s.y)+ANGLE_90
			end
		end
	end
end

--Super Flash, makes the entire screen gradually go white through HUD graphics.
XSon.SuperFlash = function(xs, s)
	if not xs or not xs.timer or not leveltime return "die" end --
	if not xs.speed xs.speed = 1 end
	if not xs.COLOR xs.COLOR = SKINCOLOR_SUPERSILVER1 end
	
	xs.timer = $-1
	if (xs.timer % xs.speed == 0)
		if xs.reversephase
			xs.trans = min($+FRACUNIT, FF_TRANS90)
		else
			xs.trans = max($-FRACUNIT, 0)
		end
	end
	if xs.reversephase != true --Fade from strongest to weakest flash intensity during the reversal phase, rather than weak to strong.
		if xs.timer == xs.reversal
			xs.reversephase = true
		end
		if xs.timer == xs.flashtic
			XSon.Flash(s, PAL_WHITE, 4, 1000<<16)
		end
	end
end

--Little modified crossed arm-cross effect.
XSon.CrossArmEffect = function(p,s,xs, yu)
	s.spritexscale,s.spriteyscale = FRACUNIT-2000,FRACUNIT+7000
	s.XSstretch = {timer = 4, Xspeed = 550, Yspeed = 1900, air = true}
	XSon.Sound(s, sfx_xssfpw, 166)
	if yu 
		yu.crossedarms = 8
	end
	local ghs,ths = XS_MediumDust(s, SKINCOLOR_SILVER, AST_TRANSLUCENT, nil, nil, p.drawangle)
	if ghs and ghs.valid and ths and ths.valid
		for i = -1,1,2
			if i==1 ghs = ths end
			ghs.spritexscale,ghs.spriteyscale = FRACUNIT/5,FRACUNIT/5
			ghs.renderflags = $ & ~RF_PAPERSPRITE
			ghs.sttfade = 99
			ghs.frame = ($ & ~(FF_TRANSMASK|FF_PAPERSPRITE))|FF_TRANS60
			P_InstaThrust(ghs, p.drawangle+ANGLE_90, 4*s.scale*i)
			ghs.momx,ghs.momy,ghs.momz = $+s.momx,$+s.momy,s.momz/2
			P_TryMove(ghs, s.x+FixedMul(15*s.scale, cos(p.drawangle)), s.y+FixedMul(15*s.scale, sin(p.drawangle)), true)
			ghs.z = $+(41*s.scale*P_MobjFlip(s))
		end
	end
end

--FreeFlight Wind Effects. Lots of math, good luck! "SuperWindLines" "SpeedLines"
XSon.FlightWindLines = function(s, DIST, OFFSETS, TRANS, CSCALE, FUSE, COLOR, FRAME, SOUP)
	local MOMANG,TMOMZ = XSon.MOMANGLE(s), abs(s.momz/s.scale)
	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 ZOFFSET = FixedMul(FixedDiv(DIST, 50), P_RandomRange(MINZ,70)) * P_MobjFlip(s) --Vertical position.
	ZOFFSET = $ + ( ((s.momz<-6*s.scale) and s.height/2 or s.height/2)*P_MobjFlip(s) )
	if s.player and not (SOUP) and (s.player != displayplayer) and not splitscreen 
		return --ghs.flags2 = $|MF2_DONTDRAW
	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 + FixedMul( (DIST*i), cos(MOMANG+ANGLE_90)) ,
		YOFFSET + FixedMul( (DIST*i), sin(MOMANG+ANGLE_90)) ,
		ZOFFSET + s.momz, MT_XSAUTOFRAME)
		
		ghs.renderflags = RF_PAPERSPRITE
		ghs.sprite = SPR_XSWL -- 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.eflags = $ & ~MFE_VERTICALFLIP
		ghs.sttfade = (FUSE<13) and true or FUSE
		ghs.angle = MOMANG - ((s.momz<-6*s.scale) and ANG1*2*i or ANG1*5*i) + (i==1 and ANGLE_180 or 0)
		if (TMOMZ < 11)
			ghs.momx,ghs.momy = -s.momx,-s.momy
		end
		if CSCALE
			ghs.spritexscale,ghs.spriteyscale = CSCALE,CSCALE
		end
		if s.player 
			if s.player.xsonictable and (s.player.xsonictable.ChaosLambent or s.player.xsringburst)
				ghs.angle = s.player.drawangle - ((s.momz<-6*s.scale) and ANG1*2*i or ANG1*5*i) + (i==1 and ANGLE_180 or 0)
				ghs.renderflags = RF_PAPERSPRITE|RF_FULLBRIGHT|RF_NOCOLORMAPS
				P_Thrust(ghs, s.player.drawangle, (leveltime%2) and -70*s.scale or -50*s.scale)
			end
		end
	end
end