--#==============================================================================#
--GFX mainly meant for Dark 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

--Edited flames make a surprisingly effective Dark Aura.
XSon.DarkBody = function(s, DIST, ZDIST, COLOR, NUM)
	NUM = (NUM) or 1 --Not intended to go beyond 2.
	DIST = (DIST) or P_RandomRange(-54,54,s)<<16 or (leveltime%2) and -6<<16 or 6<<16
	local MINZ = P_IsObjectOnGround(s) and -38 or -2
	ZDIST = (ZDIST) or P_RandomRange(MINZ,55,s)<<16
	
	local ghs
	for i = 1,NUM
		if i != 1 DIST = -$ end
		ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_XSAUTOFRAME)
		ghs.sprite = SPR_XSG1
		ghs.scale = s.scale/2
		ghs.blendmode = AST_ADD //(leveltime%(1+i)==0) and AST_SUBTRACT or 
		ghs.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS	
		ghs.frame = 29|((leveltime%2==0) and FF_TRANS50 or (leveltime%3==0) and FF_TRANS60 or FF_TRANS40) --29 is "3" here.
		ghs.sttfinalframe = 38 --38 is lowercase c.	
		ghs.sttspeed = 2/i
		ghs.sttfade = 22/i
		ghs.sttXspeed = (DIST>0) and -3<<16 or 3<<16
		ghs.spritexoffset = s.spritexoffset+DIST
		ghs.spriteyoffset = s.spriteyoffset+ZDIST
		ghs.tracer = s
		ghs.GSoffsetX,ghs.GSoffsetY,ghs.GSoffsetZ = 0,0,0
		ghs.tics,ghs.fuse = 99,22/i
		ghs.destscale = 99<<16
		ghs.dispoffset = 99+i
		ghs.spritexscale = max(6535, $+XSon.RNG(-20000,40000,s) )
		ghs.spriteyscale = max(6535, $+XSon.RNG(-20003,40002,s) )
		if COLOR
			ghs.color,ghs.colorized = COLOR,true
		end
	end return ghs --
end

--Electric sparks
XSon.ChargePower = function(s, CHARGE, TRACER, type, subtype, COLOR)
	CHARGE = (CHARGE) and max(10,CHARGE) or 10
	if type == nil
		if (leveltime%3==0)
			type = "rotated"	
			subtype = (leveltime%6) and 60 or 59
		elseif (leveltime%2==0)
			type = "horizontal"
			subtype = 59
		else
			type = "vertical"
			subtype = 60
			CHARGE = $+10
		end
	end
	
	local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_XSAUTOFRAME)
	ghs.sprite = SPR_XSG1
	ghs.blendmode = AST_ADD
	ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	ghs.frame = (subtype)| max(FF_TRANS10, FF_TRANS90-((CHARGE-9)/5)<<16)
	ghs.sttfade = true
	ghs.tracer = TRACER
	ghs.tics,ghs.fuse = 99,(subtype==60) and 7 or 4
--	ghs.scalespeed = FRACUNIT/3
	ghs.destscale = ghs.scale*4/3
	ghs.dispoffset = 105
	
	if COLOR
		ghs.color,ghs.colorized = COLOR,true
	end
	
	--Which one?
	if type == "rotated"
		local RNG = P_RandomRange(-12,348)
		ghs.rollangle = RNG*ANG1
	else
		local RNG = P_RandomRange(-27,27)
		if type == "vertical"
			local RNG2 = P_RandomRange(-82,59)
			ghs.spriteyoffset = RNG2<<16
		else
			ghs.sttXspeed = RNG*1250
		end
		if (RNG>=0)
			ghs.frame = $|FF_HORIZONTALFLIP
			RNG = -$
			if ghs.sttXspeed
				ghs.sttXspeed = -$
			end
		end
		ghs.spritexoffset = RNG<<16
	end
	
	ghs.spritexscale = 50000+(CHARGE*500) --Size?
	ghs.spriteyscale = 50000+(CHARGE*500)
	
	return ghs --
end

--Spawn a cloud of realistic smoke particles. Stolen from SMSreborn.
XSon.SmokeCircle = function(s, NUM, DIST, TRACER, CSCALE, FUSE, COLOR, YOFFSET)
	local dist = (DIST) or 98*s.scale
	
	local NO = NUM
	if NO == nil
		NO = 4
	elseif NO == 0 
		dist = 1			
	end
	
	for i = -NO,NO
		if i == NO and NUM != 0 break end --Break! Topple! Daze!
		local ghs = P_SpawnMobjFromMobj(s, 
		FixedMul(dist+i, cos(s.angle+((ANGLE_180/(NO or ANGLE_180))*i))),
		FixedMul(dist+i, sin(s.angle+((ANGLE_180/(NO or ANGLE_180))*i))),
		s.height/8, MT_XSAUTOFRAME)
		ghs.sprite = SPR_XSG1
		ghs.frame = 39|FF_TRANS30 --39 is lowercase d.
		ghs.sttfinalframe = 48 --48 is lowercase m
		ghs.blendmode = AST_ADD
		
		if COLOR --Ok then?? 
			ghs.color,ghs.colorized = COLOR,true
		end
		if CSCALE
			ghs.spriteyscale = CSCALE
			ghs.spritexscale = CSCALE
		end		
		ghs.fuse = (FUSE) or 38
		
		ghs.sttfade = 20
		ghs.sttspeed = 4
		ghs.sttYspeed = (YOFFSET) or XSon.RNG(0, 2<<16+32500,ghs)
		
		if NUM == 0 --Stop and only create one, returning it to the player.
			if TRACER ghs.tracer = TRACER end	
			ghs.renderflags = $ & ~RF_PAPERSPRITE return ghs --
		elseif TRACER
			ghs.tracer = TRACER
			ghs.GSoffsetX,ghs.GSoffsetY = dist,dist
			ghs.GSoffsetZ = s.height/8
			ghs.GSoffsetang = s.angle+((ANGLE_180/(NO or ANGLE_180))*i)	
		end
	end
end

--Edited version of SmokeCircles for creating mist instead.
XSon.SmokeMist = function(s, NUM, DIST, TRACER, CSCALE, FUSE, COLOR)
	local dist = (DIST) or 99*s.scale
	local NO = NUM
	if NO == nil
		NO = 2
	elseif NO == 0
		dist = 1	
	end
	local XADD,YADD = 55000,-18000
	if CSCALE --It's a table value here.
		XADD,YADD = CSCALE.X,CSCALE.Y
	end
	
	for i = -NO,NO
		if i == NO and NUM != 0 break end 
		local ghs = P_SpawnMobjFromMobj(s, 
		s.momx + FixedMul(dist-i, cos(s.angle+((ANGLE_180/(NO or ANGLE_180))*i))),
		s.momy + FixedMul(dist-i, sin(s.angle+((ANGLE_180/(NO or ANGLE_180))*i))),
		s.height/12, MT_XSAUTOFRAME)
		ghs.sprite = SPR_XSG1
		ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
		ghs.frame = 39|FF_TRANS90 --39 is lowercase d.
		ghs.sttfinalframe = 48 --48 is lowercase m
		ghs.blendmode = AST_ADD
		
		if COLOR --Usually a special color called SKINCOLOR_XDARKMIST
			ghs.color = COLOR
			ghs.colorized = true
		end
		ghs.spritexscale = $+XADD
		ghs.spriteyscale = $-YADD
		ghs.fuse = (FUSE) or 40
		ghs.dispoffset = 131+i
		ghs.sttfade = 32
		ghs.sttspeed = 4
		ghs.sttXspeed = P_RandomRange(-5,5)*s.scale
		ghs.sttYspeed = XSon.RNG(-25000-(i*19),65400+i,ghs)
		
		if NUM == 0 
			if TRACER ghs.tracer = TRACER end	
			ghs.renderflags = $ & ~RF_PAPERSPRITE return ghs --
		elseif TRACER
			ghs.tracer = TRACER
			ghs.GSoffsetX,ghs.GSoffsetY = dist,dist
			ghs.GSoffsetZ = s.height/12
			ghs.GSoffsetang = s.angle+((ANGLE_180/(NO or ANGLE_180))*i)	
		end
	end
end

--Setup of boost aura.
local function AuraSetup(s, PAPER, DIST, ZHEIGHT, SCALE, COLOR, ANGLE, TA)
	local p = s.player
	if ANGLE == nil
		ANGLE = p and p.drawangle or s.angle
	end
	TA = min((TA) and TA+FF_TRANS40 or FF_TRANS70, FF_TRANS80)
	local X,Y = FixedMul(DIST, cos(ANGLE+ANGLE_90)), FixedMul(DIST, sin(ANGLE+ANGLE_90))
	
	local ghs = P_SpawnMobjFromMobj(s, (s.momx/3)+X, (s.momy/3)+Y, ZHEIGHT, MT_XSAUTOFRAME)
	ghs.sprite = SPR_XSG1
	ghs.tics,ghs.fuse = 2,2
	ghs.frame = (leveltime%4==0) and 51|(FF_TRANS20+TA) or (leveltime%3==0) and 52|(FF_TRANS10+TA)
	or (leveltime%2==0) and 53|TA or 54|(FF_TRANS10+TA) --i to l
	if PAPER
		ghs.renderflags = RF_PAPERSPRITE|RF_SEMIBRIGHT|RF_NOCOLORMAPS
		ghs.momx,ghs.momy,ghs.momz = s.momx/2,s.momy/2,-s.momz/6 
	else
		ghs.renderflags = RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_SLOPESPLAT|RF_SEMIBRIGHT|RF_NOCOLORMAPS
		ghs.flags2 = $|MF2_SPLAT
	end
	ghs.sttspeed = 1
	ghs.sttfirstframe,ghs.sttlastframe = 51,54 --Loop!
	ghs.angle = ANGLE
	ghs.spritexscale,ghs.spriteyscale = (SCALE) or FRACUNIT,(SCALE) or FRACUNIT		
	if COLOR
		ghs.color,ghs.colorized = COLOR,true
	end
	
	if p and p == displayplayer and camera and not camera.chase
		ghs.flags2 = $|MF2_DONTDRAW --Don't blind Sonic!
	end	
	return ghs --
end

--Dark boost aura
XSon.SpikedAura = function(s, SCALE)
	local p = s.player local xs = p and p.xsonictable if not xs return end --
	SCALE = (SCALE) or 80777
	
	local ghs 
	local PHASE = XS_Super(p) or 0
	local BLEND = (p.powers[pw_super]) and AST_ADD or (PHASE==3) and AST_REVERSESUBTRACT or AST_TRANSLUCENT
	local COLOR = (PHASE==3) and SKINCOLOR_FLAME or XSon.DarkAura(0)
	local TRANS = (PHASE==1) and FF_TRANS80 or 0
	local ANGLE = p.drawangle
	local MAXZ,MINZ = P_RandomRange(60,74)*s.scale, P_RandomRange(1,-10)*s.scale
	local DIST,ZHEIGHT = 0,0
	local XDIST = 18*s.scale
	local ANGRNG = P_RandomRange(20,48)
	if s.XSBoostAura == nil s.XSBoostAura = {} end
	
	for d = 1,4
		ZHEIGHT = (d==1 or d==3) and MAXZ or MINZ
		DIST = ((d<3) and XDIST or -XDIST)+30*s.scale
		ghs = s.XSBoostAura[d]
		if not (ghs and ghs.valid)
			s.XSBoostAura[d] = AuraSetup(s, false, DIST, ZHEIGHT, SCALE, COLOR, ANGLE, (d==2 or d==4) and TRANS+FF_TRANS20 or TRANS)
			ghs = s.XSBoostAura[d]
			P_CreateFloorSpriteSlope(ghs) --This is performance expensive, so re-use the originals if they exist already
		else --Do maintenance instead.
			local X,Y = FixedMul(DIST, cos(ANGLE+ANGLE_90)),  FixedMul(DIST, sin(ANGLE+ANGLE_90))
			P_MoveOrigin(ghs, s.x+(s.momx/3)+X, s.y+(s.momy/3)+Y, s.z+ZHEIGHT)	
			ghs.tics,ghs.fuse = 2,2
			ghs.spritexscale,ghs.spriteyscale = (SCALE) or FRACUNIT,(SCALE) or FRACUNIT	
			ghs.color,ghs.colorized = (COLOR) or 0,(COLOR!=nil)
			ghs.flags2 = (p==displayplayer and camera and not camera.chase)  and $|MF2_DONTDRAW or $&~MF2_DONTDRAW
		end
		ghs.angle = ANGLE-ANGLE_90
		ghs.blendmode = BLEND
		
		ghs.floorspriteslope.o = {x = s.x, y = s.y, z = s.z+ZHEIGHT}
		ghs.floorspriteslope.zangle = ((d<3) and -ANGRNG or ANGRNG)*ANG1
		ghs.floorspriteslope.xydirection = (d==2 or d==4) and ghs.angle or ghs.angle+ANGLE_180
	end
	
	--Now for the papersprites! Not as intensive, don't need the table.
	local FACERNG = P_RandomRange(7,23)
	for i = -3,3,2
		ghs = AuraSetup(s, true, abs(i)!=1 and 8*i<<16 or 21*i<<16, s.height/4, SCALE, COLOR, ANGLE, (TRANS!=0) and TRANS or nil)
		ghs.angle = $-(FACERNG*ANG1*i)
		ghs.blendmode = BLEND
	end 	
end

--An animated boost wave.
XSon.BurstWave = function(s, STYLE, COLOR, BLEND, FUSE, CSCALE, ANGLE)
	local ghs = P_SpawnMobjFromMobj(s, s.momx, s.momy, 0, MT_XSAUTOFRAME)	
	ghs.sprite = SPR_XSG2
	ghs.frame = 27|FF_TRANS60
	ghs.sttlastframe = 31
	ghs.blendmode = (BLEND) or AST_ADD
	ghs.fuse = (FUSE) or 24
	ghs.sttfade = (FUSE) and FUSE/2 or 14
	if COLOR
		ghs.color,ghs.colorized = COLOR,true
	end
	ghs.destscale = 99<<16
	ghs.scalespeed = FRACUNIT/6
	if STYLE
		if (STYLE > 1) --flat wave
			ghs.renderflags = RF_FLOORSPRITE|RF_NOSPLATROLLANGLE|RF_NOSPLATBILLBOARD
			ghs.flags2 = $|MF2_SPLAT
			ghs.angle = (ANGLE!=nil) and ANGLE or s.angle
		elseif STYLE == 1 --paper wave
			ghs.tracer = s
			ghs.ditchtracer = 2
			ghs.renderflags = $|RF_PAPERSPRITE|RF_SEMIBRIGHT
			ghs.angle = (ANGLE!=nil) and ANGLE or R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)+ANGLE_90
			ghs.spriteyoffset = $+5<<16
		end
	end
	ghs.spritexscale = (CSCALE) or 46725
	ghs.spriteyscale = ghs.spritexscale
	
	return ghs --
end

--Subtle burst GFX when attempting to look for foes to lunge at.
XSon.AttackBurst = function(s, SCALE, COLOR, BLEND, TRANS)
	local p = s.player
	local ANGLE = p and p.drawangle or s.angle
	TRANS = (TRANS) or FF_TRANS10

	local ghs,ths
	for i = -1,1,2
		ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_XSAUTOFRAME)
		ghs.sprite = SPR_XSG1
		ghs.tics,ghs.fuse = 8,ghs.tics
		
		ghs.frame = 55|TRANS|((i!=1) and FF_HORIZONTALFLIP or 0) --frame "t" to "w" (55 to 58)
		ghs.blendmode = (BLEND) or AST_ADD
		ghs.sttspeed = 1
		ghs.sttfirstframe,ghs.sttlastframe = 55,58
		ghs.sttfade = true
		ghs.dispoffset = 139
		
		ghs.destscale = 99<<16
		ghs.sttXspeed = 7<<16+20000
		ghs.spritexoffset = ghs.sttXspeed*2
		ghs.tracer = s
		
		if SCALE
			ghs.spritexscale,ghs.spriteyscale = SCALE,SCALE
			ghs.spriteyoffset = $ + FixedDiv(FixedMul(8<<16, FRACUNIT), SCALE)
		end
		if COLOR
			ghs.color,ghs.colorized = COLOR,true
		end
		if i!=1
			ths = ghs
		end
	end return ths,ghs --
end

--To signify you're fully charged usually, or for impacts.
XSon.ChargeSpark = function(s, COLOR, CSCALE, TRACER, BLEND, FUSE, FRAME)	
	local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_XSAUTOFRAME)
	ghs.fuse = (FUSE) or 16
	ghs.tics = ghs.fuse
	ghs.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS
	ghs.sprite = SPR_XSG2
	ghs.frame = (FRAME) or 34|FF_TRANS20
	ghs.sttspeed = 5
	ghs.sttfade = (ghs.fuse<13) and true or 14
	ghs.scalespeed = 1<<15
	ghs.destscale = 99<<16
	ghs.sttfirstframe = (ghs.frame & FF_FRAMEMASK)+1
	ghs.sttlastframe = ghs.sttfirstframe
	ghs.blendmode = (BLEND) or AST_ADD
	ghs.dispoffset = 139
	if TRACER
		ghs.tracer = TRACER
		ghs.ditchtracer = max(2, ghs.fuse-5)
	end
	if CSCALE
		ghs.spritexscale,ghs.spriteyscale = CSCALE,CSCALE
	end
	if COLOR
		ghs.color,ghs.colorized = COLOR,true
	end
	return ghs --
end

--This one has like five billion options for some reason.
XSon.EarthQuake = function(s, NO, DIST, SPEED, CSCALE, SCALESPEED, ZANGLE, FRAME, FUSE, COLOR, BLEND)
	if NO == nil
		NO = 4 --Technically 8!
	elseif NO == 0
		DIST = 2 --Only spawn 1 to manipulate?
	end	
	CSCALE = (CSCALE) or FRACUNIT
	DIST = (DIST) or FixedDiv(FixedMul(96*s.scale, CSCALE), FRACUNIT)
	BLEND = (BLEND) or AST_ADD
	SPEED = (SPEED) or s.scale*40
	SCALESPEED = (SCALESPEED) or s.scale/10
	ZANGLE = (ZANGLE) and ZANGLE/2 or ANG1*26
	FRAME = (FRAME) or 32|FF_TRANS10 
	FUSE = (FUSE) or 30
	
	--Set color or colortable.
	local COLORTABLE
	if type(COLOR) == "table"
		COLORTABLE = COLOR
	elseif COLOR == "prism"
		COLORTABLE = {SKINCOLOR_GOLDENROD, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_RUBY, SKINCOLOR_BLUEBELL, SKINCOLOR_SKY, SKINCOLOR_SUPASONIKKU, SKINCOLOR_GOLDENROD, SKINCOLOR_ORANGE, SKINCOLOR_RED,SKINCOLOR_RUBY, SKINCOLOR_PURPLE}
	elseif not COLOR
		local p = s.player local xs = p and p.xsonictable
		if xs and xs.supersonic
			if xs.darkphase
				if s.XSassault and s.XSassault.earthslam == 3
					COLORTABLE = {SKINCOLOR_XSSUPERBLUR, SKINCOLOR_XSSUPERAURA, SKINCOLOR_XSSUPERBLUR}
					BLEND = AST_REVERSESUBTRACT
				end
			else
				COLORTABLE = {SKINCOLOR_SUPERGOLD2, SKINCOLOR_SUPERGOLD4, SKINCOLOR_GOLDENROD}
			end
		end
	end
	local ANGLE = s.angle+(leveltime*ANG2)
	local ANGPLUS,ghs = (360/ max(1,NO*2) ) * ANG1
	local RENDERER = CV_FindVar("renderer").value
	
	--Setup done!
	for i = -NO, max(-NO, NO-1)
		ghs = P_SpawnMobjFromMobj(s, FixedMul(DIST, cos(ANGLE)), FixedMul(DIST, sin(ANGLE)), s.scale*4, MT_XSAUTOFRAME)
		ghs.sprite = SPR_XSG2
		ghs.frame = FRAME
		ghs.blendmode = BLEND
		ghs.flags2 = $|MF2_SPLAT
		ghs.scale = $+(SCALESPEED*3)
		if COLORTABLE
			ghs.color,ghs.colorized = COLORTABLE[XSLocalRNG(1, #COLORTABLE, ghs)], true
		elseif type(COLOR)=="number"
			ghs.color,ghs.colorized = COLOR, true
		end
		ghs.fuse = FUSE
		ghs.sttfade = max(ghs.fuse-6, 22)
		ghs.spritexscale,ghs.spriteyscale = CSCALE,CSCALE
		ghs.scalespeed = SCALESPEED
		ghs.destscale = 99<<16
		local TOANG = R_PointToAngle2(s.x, s.y, ghs.x, ghs.y)
		ghs.angle = TOANG
	--	P_Thrust(ghs, TOANG, -SPEED)
		P_SetObjectMomZ(ghs, 4*CSCALE, true)
		
		if (abs(ZANGLE) > 5*ANG1)
			ghs.renderflags = RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_SLOPESPLAT|RF_FULLBRIGHT|RF_NOCOLORMAPS		
			P_CreateFloorSpriteSlope(ghs)
			ghs.floorspriteslope.o = {x = s.x, y = s.y, z = s.z}
			ghs.floorspriteslope.xydirection = TOANG
			ghs.floorspriteslope.zangle = ZANGLE
		else --Don't bother if it's too low!
			ghs.renderflags = RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_FULLBRIGHT|RF_NOCOLORMAPS		
		end
		if SPEED == "tracer"
			ghs.momx,ghs.momy,ghs.momz = s.momx,s.momy, $ + (s.momz*2/3)+(ZANGLE/ANG1*s.scale*2)
			ghs.tracer = s
			ghs.ditchtracer = 8
		else
			ghs.momx,ghs.momy = s.momx/2,s.momy/2
		end
		if NUM == 0
			return ghs --
		elseif (i%2==0)
			if RENDERER == 1 and displayplayer 
				if displayplayer.mo
					ghs.dontdrawforviewmobj = displayplayer.mo
				end
				if s.player and s.player==displayplayer
					ghs.flags2 = $|MF2_DONTDRAW --Nerf for software.
				end
			end			
		end
		ghs.angle = $+ANGLE_90
		ANGLE = $+ANGPLUS --Increase ANGLE.
	end
end