--╔════════════════════════════╗══════════════════════════════════════════════════════════════════════════════════════════════#
--║(De)Transformation functions║ Also includes context menu thinker and the NiGHTS transformation animation. 
--╚════════════════════════════╝══════════════════════════════════════════════════════════════════════════════════════════════#
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

--Refund it 1 tic later so the game can do it's thing.
local function RingRefund(p,s, RINGS)
	if s.XS_RingReturn == nil
		if RINGS == nil RINGS = max(0, p.rings-50) end 
		if RINGS == 0 return end --
		s.XS_RingReturn = {timer = 1, refund = RINGS}
		p.rings = 0
		p.charflags = $ & ~SF_SUPER
	end
end

--Holding custom3 opens this thing, which can open the menu or start transformations.
XSon.C3context = function(p,s,xs)
	if s.XS_RingReturn or (p.powers[pw_super]==1) or (s.state == S_PLAY_S_XTRANSFORM)
	or (s.XSassault) or xs.FreeFlight and xs.FreeFlight.LockOn or p.powers[pw_carry]
		if type(s.XS_C3CONTEXT)=="table"
			s.XS_C3CONTEXT.NOGO = s.XS_C3CONTEXT.NOGO and $+1 or 1
		else
			return --
		end
	elseif s.XS_C3CONTEXT == nil
		s.XS_C3CONTEXT = {TIMER = 0, RNG = 0, JUMP = 0, SPIN = 0, CUSTOM1 = 0, CUSTOM2 = 0}
	end
	local C3 = s.XS_C3CONTEXT
	if C3.NOGO C3.NOGO = $+1 xs.custom3 = 999<<16 return end --

	xs.jump = 99<<16
	xs.spin = 99<<16
	xs.custom1 = 99<<16
	xs.custom2 = 99<<16
	p.pflags = $|PF_USEDOWN|PF_JUMPDOWN

	C3.ALIVE = 9
	C3.TIMER = $+1
	C3.JUMP = (p.cmd.buttons & BT_JUMP) and min(28, $+1) or 0
	if ((C3.SPIN or 0) <  5) or (not xs.darkphase and not p.powers[pw_super]) or (C3.TIMER%2==0)
		C3.SPIN = (p.cmd.buttons & BT_SPIN) and min(28,$+1) or 0
	end
	local ADD = (netgame==true) and 1 or 4
	if (marathonmode) or ((netgame==true) and (p.jointime < 40)) --Not here.
		C3.CUSTOM1,C3.CUSTOM2 = 0,0
	else
		C3.CUSTOM1 = (p.cmd.buttons & BT_CUSTOM1) and min(28,$+ADD) or 0
		C3.CUSTOM2 = (p.cmd.buttons & BT_CUSTOM2) and min(28,$+ADD) or 0
		if C3.CUSTOM1
			C3.CUSTOM2 = 0
		end
	end
	
	if C3.TIMER == 20 and xs.C3tutorial
		xs.C3tutorial = min($, 28)
	end
	if C3.JUMP or C3.SPIN or C3.CUSTOM1 or C3.CUSTOM2
		local FAKES = XS_FAKEEMERALDS
		if C3.JUMP==1 and not xs.darkphase and not p.powers[pw_super]
		or C3.SPIN==1 and (FAKES==127 or xs.darkphase or xs.supersonic)
		or C3.CUSTOM1==1 or C3.CUSTOM2==1
			S_StartSoundAtVolume(nil, sfx_s25d, 100, p)
		end
		C3.RNG = P_RandomRange(0,14)
		if (C3.JUMP > 12) and emeralds == 127 
		and (p.rings>=100) and not xs.darkphase and not p.powers[pw_super] 
			local MAP = mapheaderinfo[gamemap]
			local SPHERES = MAP.ssspheres or 0
			if (SPHERES > 1) and (gametype == GT_COOP)
				C3.JUMP = 0
				S_StartSound(nil, sfx_lose, p)
			else
				if s.skin != "xsonic"	
					COM_BufInsertText(p, "skin xsonicserious; skin xsonic")		
					R_SetPlayerSkin(p, "xsonic")
					s.skin = "xsonic"
				end	
				XSon.StartTransformation(p,s,xs, true) --Super Sonic!!
			end
		elseif (C3.SPIN > 12) and not p.powers[pw_super] and not xs.supersonic
		or (C3.SPIN > 25) and (xs.supersonic or xs.darkphase or p.powers[pw_super])
			if xs.darkphase --If already transformed, allow detransforming.
				C3.NOGO = 1
				XSon.DeTransform(p,s,xs, "DARK")
				XSVoice(p, s, xs, sfx_xsvoci, true, nil, 130)
				RingRefund(p,s) 
			elseif p.powers[pw_super] 
				C3.NOGO = 1
				RingRefund(p,s, max(p.rings-100, 0))
				XSVoice(p, s, xs, sfx_xsvoct, true)			
			else
				if FAKES == 127 and (p.rings>=50) and not xs.darkphase and not xs.TScooldown
					local MAP = mapheaderinfo[gamemap]
					local SPHERES = MAP.ssspheres or 0
					if (SPHERES > 1) and (gametype == GT_COOP)
						C3.SPIN = 0 --Not in sphere special stages.
						S_StartSound(nil, sfx_lose, p)
					else				
						C3.NOGO = 1
						XSon.ObtainTheNPass(p,s,xs) --Now you die. /DKpunch
					end
				end
			end
		elseif (C3.CUSTOM1 > 24) or (C3.CUSTOM2 > 24)
			XSon.CreateMenu(p,s,xs) return --
		end
	end
	if C3.NOGO
		xs.jump,xs.spin,xs.custom1,xs.custom2,xs.custom3 = 99<<16,99<<16,99<<16,99<<16,99<<16
		s.XS_C3CONTEXT = nil
	end
end

--Bro, there's no such thing as an N-pass!! ...man, there's nothing this dude won't do to disrespect you.
XSon.ObtainTheNPass = function(p,s,xs, SILENT)
	local BOT = ((p.bot) or (SILENT=="silent")) and true or false
	XSon.DeleteAura(p, s, xs, true, true, true)
	XSon.RememberXColor(p,s,xs)
	
	if s.skin != "xsonicserious"	
		if not (p.bot) COM_BufInsertText(p, "skin xsonic; skin xsonicserious") end
		R_SetPlayerSkin(p, "xsonicserious") s.skin = "xsonicserious"
	end
	XSon.SmokeCircle(s, nil, nil, nil, nil, nil, XSon.DarkAura(0))
	if not (BOT)
		for i = 1,6
			XSon.SmokeMist(s, 4, 99*s.scale, nil, nil, nil, XSon.DarkAura(i))
		end	
		XSon.Flash(s, PAL_NUKE, 5, 1110<<16)
		for i = 1,8
			XSon.DarkBody(s, nil, nil, SKINCOLOR_CRIMSON, 2)
		end	
		local ghs = XSon.Overglow(s, 2, SKINCOLOR_PUREBLACK) --SKINCOLOR_MIDNIGHT --SKINCOLOR_XSSUPERBLUR
		if ghs and ghs.valid
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
			ghs.blendmode = AST_TRANSLUCENT
			ghs.fuse,ghs.tics = 7,99
			ghs.sttfade = true
			ghs.tracer = s
		end
	end
	
	
	local OLDDARKNESS = s.XS_darkness or 0
	local MAXRINGS = max(p.rings, 50)
	local RB = s.XS_DarkTable
	if RB 
		RB.heartbeat = nil
		RB.instate = 0 
		if RB.maxrings 
			MAXRINGS = max(50, RB.maxrings) 
		end
		if xs.darkphase and not (BOT)
			XSon.RageHUD(p,s,xs,RB, "double")
			if RB.RAGEHUD
				RB.RAGEHUD.trans,RB.RAGEHUD.direct = 4,1
				RB.RAGEHUD.color = SKINCOLOR_VOLCANIC
			end
		end
	end
	s.eflags = $ & ~MFE_FORCESUPER
	p.powers[pw_super] = 0
	s.XS_darkness,s.XS_darknessgoals,xs.afterdrain = nil --Will be auto-set later if needed.
	xs.TScooldown = 24
	s.XSresist = 18
	
	if not xs.darkphase
		p.rings = max($,50)
		xs.darkphase = 1
		s.XS_darkness = 0
		if not (BOT)
			XSon.Quake(s, 34<<16, 5, 550<<16)
			S_StartSound(s, sfx_xssfpd)
		end
		if (s.sprite2 & FF_SPR2SUPER)
			s.state = S_PLAY_SPRING
		end
	else
		p.rings = min(9999, min($+20, MAXRINGS))
		if xs.darkphase == 1
			xs.darkphase = 2
			s.XS_darkness = max(OLDDARKNESS-100,0)
			if not (BOT)
				XSon.Quake(s, 70<<16, 8, 850<<16)
				S_StartSound(s, sfx_xssfpe)
			end
		else
			s.XS_darkness = max(OLDDARKNESS-150,0)
			xs.darkphase = 3
			if not (BOT)
			
				XSon.Quake(s, 90<<16, 24, 1500<<16)
				
				local SFX = XSon.GibSound(s, sfx_xssfpg) 
				S_StartSound(SFX, sfx_xssfpe) S_StartSound(SFX, sfx_xssfpu)
				S_StartSound(s, sfx_xssfpd)
				if SILENT != "superTA"
					local TAUNT = (leveltime%4==0) and sfx_xsvoc9 or sfx_xsvoca
					if TAUNT==sfx_xsvoc9
						local NAME = p.name or ""
						if (string.find(NAME, "urn")) or (string.find(NAME, "Shine"))
							TAUNT = sfx_xsvoca
						end
					end
					XSVoice(p, s, xs, TAUNT, true, nil, (TAUNT==sfx_xsvoca) and 255 or 158)
				end
			end
			if p.panim == PA_IDLE or (s.state == S_PLAY_EDGE)
				s.state = S_PLAY_FALL
				s.tics,s.anim_duration = 1,1
				P_SetObjectMomZ(s, -3, true)			
			end
		end
		p.xssuperflash = {trans = FF_TRANS90, timer = 18, speed = 1, reversal = 9, COLOR = SKINCOLOR_PUREBLACK}
	end
	if not (p.bot)
		XSon.SetMusic(p,s,xs) --Properly adjust music.
	end
end

--Remember old color.
XSon.RememberXColor = function(p,s,xs, FORCE)
	if not xs.darkphase or (FORCE)
		if (p.skincolor!=SKINCOLOR_DAKUSONIKKU)
			s.XS_PreDarkColor = p.skincolor
		end
		if (s.color != p.skincolor) 
		and ((p.skincolor == SKINCOLOR_DAKUSONIKKU) or (p.skincolor == SKINCOLOR_SONIKKU))
			if not (p.powers[pw_shield] & SH_FIREFLOWER) and not (p.powers[pw_super] > 1)
			and not ((p.powers[pw_invulnerability]) and (mariomode==true))
				s.XS_PreDarkColor = s.color --This might be better...
			end
		end
	end
	if s.XS_PreDarkColor==nil or (s.XS_PreDarkColor==SKINCOLOR_DAKUSONIKKU)
		if (p.skincolor != SKINCOLOR_DAKUSONIKKU)
		and not ( (p.skincolor==SKINCOLOR_SONIKKU) and (xs.SPcolor!=SKINCOLOR_SONIKKU) )
			s.XS_PreDarkColor = p.skincolor
		elseif type(xs.SPcolor)=="number"
			s.XS_PreDarkColor = xs.SPcolor
		end
	end
end

--Revert color from superform.
XSon.RevertXColor = function(p,s,xs, FORCE)
	local COLOR = p.skincolor
	if (s.XS_PreDarkColor!=nil)
		if ((s.color == SKINCOLOR_SONIKKU) or (s.color == SKINCOLOR_DAKUSONIKKU))
		or ((p.skincolor == SKINCOLOR_SONIKKU) or (p.skincolor == SKINCOLOR_DAKUSONIKKU))
			COLOR = s.XS_PreDarkColor
			FORCE = true
			if (p.skincolor == SKINCOLOR_SONIKKU) or (p.skincolor == SKINCOLOR_DAKUSONIKKU)
				p.skincolor = COLOR
			end
		end
		s.XS_PreDarkColor = nil
	end
	if FORCE==nil and not (multiplayer) and xs.SPcolor and (xs.SPcolor!=SKINCOLOR_SONIKKU)
		if ((s.color == SKINCOLOR_SONIKKU) or (s.color == SKINCOLOR_DAKUSONIKKU))
		or ((p.skincolor == SKINCOLOR_SONIKKU) or (p.skincolor == SKINCOLOR_DAKUSONIKKU))	
			COLOR = xs.SPcolor
			FORCE = true
			if (p.skincolor == SKINCOLOR_SONIKKU) or (p.skincolor == SKINCOLOR_DAKUSONIKKU)
				p.skincolor = COLOR
			end			
		end
	end
	if (FORCE)
		local FIREFLOWER = (p.powers[pw_shield] & SH_FIREFLOWER) and SH_FIREFLOWER or 0
		s.color = (FIREFLOWER) and SKINCOLOR_WHITE or COLOR	
	end
end

--Adjust some stuff when we untransform from Super Sonic! Detransform" Untransform"
XSon.DeTransform = function(p,s,xs, DARK)
	local WASSUPER = (p.powers[pw_super] or xs.darkphase)
	local FIREFLOWER = (p.powers[pw_shield] & SH_FIREFLOWER) and SH_FIREFLOWER or 0
	
	if IsXSonic(s)
		if p.powers[pw_shield] --Remove shields after untransfrorming, but not the fire flower.
			p.powers[pw_shield] = FIREFLOWER
			P_SpawnShieldOrb(p)
		end
		if (s.eflags & MFE_FORCESUPER) and not (p.pflags & PF_SPINNING)
			s.state = (s.state == S_PLAY_FALL) and S_PLAY_SPRING or S_PLAY_FALL
		end
		s.XSresist = 12
	end
	if WASSUPER or (DARK=="DARK")
		if s.state == S_PLAY_EDGE or p.panim == PA_IDLE
			s.state = S_PLAY_STND
			if s.tics != -1
				s.tics = max($,200)
			end
		end	
		p.powers[pw_super] = 0
		XSon.RevertXColor(p,s,xs, true)
	end
	if (p.normalspeed == (52<<16)+221) or (p.normalspeed == (104<<16)+221)
		p.normalspeed = skins[s.skin].normalspeed
	end
	if (p.runspeed == (44<<16)+221)
		p.runspeed = skins[s.skin].runspeed	
	end
	XSon.DeleteAura(p, s, xs, true, true, true)
	XSon.Echoes(p,s,xs, "stop")
	p.jumpfactor = skins[s.skin].jumpfactor
	s.eflags = $ & ~MFE_FORCESUPER
	s.spritexoffset,s.spriteyoffset = 0,0
	s.spritexscale,s.spriteyscale = FRACUNIT,FRACUNIT
	s.dispoffset = 0
	s.flags = $ & ~MF_NOSECTOR
	s.flags2 = $ & ~(MF2_DONTDRAW|MF2_SHADOW)
	xs.ZoomLines,s.XS_darkness = nil
	p.viewrollangle = 0
	p.charflags = skins[s.skin].flags
	p.pflags = $ & ~PF_CANCARRY 
	RestoreZipDashCam(p, s, xs)
	XSon.CancelFlight(p,s,xs, true)
	
	if not tonumber(p.rings) or (p.rings < 0) --Fix a rare ring bug.
		p.rings = 0
	end
	
	if leveltime and not s.xsonicspawned 
		for i = sfx_xssfxy,sfx_xssfp8 --Stop like...a billion sounds.
			S_StopSoundByID(s, i)
		end	
		xs.changedcharflags = 10
		p.aiming,s.rollangle,s.spriteyoffset,s.spritexoffset = 0,0,0,0
		if not p.powers[pw_super] and not p.hypermysticsonic
			XSon.DeTransformGFX(p,s,xs) --GFX
			XSon.ExpandingGlow(s, (xs.darkphase) and SKINCOLOR_XSSUPERBLUR or SKINCOLOR_YELLOW, 20, 96, true, (xs.darkphase) and AST_REVERSESUBTRACT or AST_ADD)
		end
		--Give jumping property back if detransformed in midair?
		if not P_IsObjectOnGround(s) and s.health --Always give jumping property back if in the air!
		and not (p.pflags & (PF_STASIS|PF_FULLSTASIS|PF_SLIDING|PF_SPINNING|PF_STARTDASH))
			xs.airborne = 0
			if p.panim != PA_SPRING and p.panim != PA_FALL and p.panim != PA_PAIN
			and not p.powers[pw_carry] and not p.powers[pw_nocontrol] and not xs.poutysonic
				p.pflags = $|PF_JUMPED & ~(PF_THOKKED|PF_STARTJUMP|PF_NOJUMPDAMAGE)
				s.state = S_PLAY_ROLL
			end
		end		
	end
	
	s.XS_DarkTable,s.XSSuperRadius,s.XSsuperroll,s.XS_modScolor = nil
	xs.supersonic,s.XS_darknessgoals,xs.afterdrain,xs.moody = nil
	xs.darkphase = 0
	if xs.ChaosLambent
		xs.ChaosLambent = nil
		S_StopSoundByID(s, sfx_xssfp6) S_StopSoundByID(s, sfx_xssfp1) 		
	end
	XSon.SetMusic(p,s,xs)
	if (p.panim == PA_IDLE or s.tics == -1 or s.anim_duration == -1) and not p.powers[pw_carry]
		s.state = ($==S_PLAY_WAIT) and S_PLAY_STND or S_PLAY_FALL
	end
	if xs.FreeFlight
		s.flags = $ & ~MF_NOGRAVITY
		xs.FreeFlight = nil
	end
	if s.skin == "xsonicserious"
		R_SetPlayerSkin(p, "xsonic")
		s.skin = "xsonic"
	elseif p.exiting and emeralds==127
		local XYDIST = 5000<<16
		searchBlockmap("objects",function(s,m)
			if m and m.valid and (m.type==MT_SIGN) and not (m.XS_darktagged) and not (m.XS_supertagged)
				m.XS_supertagged = true
			end
		end, s, s.x-XYDIST,s.x+XYDIST, s.y-XYDIST, s.y+XYDIST)	
	end
end

local XMUSIC = {"XSG2GF","XSHERO","XSTEN1","XSTEN2","XSTEN3","XSTEN4","XSTEN5","XSDSS","XSDARK","XSSS","XSDONE"}
local TENKMUSIC = {"XSTEN1","XSTEN2","XSTEN3","XSTEN4","XSTEN5"}

--Sets appropriate music. "s" is unused.
XSon.SetMusic = function(p,s,xs, FORCEBATTLE)
	if p == consoleplayer --Finally, undo any active music.
		if (FORCEBATTLE == "next")
			local SETSONG = TENKMUSIC[ min(#TENKMUSIC, XS_TENKAICHIMUSIC+1) ]
			S_ChangeMusic(SETSONG, true, p)
			mapmusname = SETSONG  return --
		end
		local MUSIC = S_MusicName()	
		local MAP = mapheaderinfo[gamemap]
		if MUSIC == "_clear" or MUSIC == "XSDONE"
			if MAP and MAP.musname
				mapmusname = DEFAULTMUSIC
			end return --
		end
		local BATTLEMUSIC = nil
		for i = 1,#TENKMUSIC
			if (MUSIC == TENKMUSIC[i])
				BATTLEMUSIC = TENKMUSIC[i] break --
			end
		end
		if ((BATTLEMUSIC==nil) or (xs.FreeFlight and xs.FreeFlight.LockOn))
		and IsXSonic(nil,p)
			if xs.darkphase or s.XS_FORCEdark and s.XS_FORCEdark.phase!=nil
				if xs.darkphase == 3
					if MUSIC != "XSDSS"
						S_ChangeMusic("XSDSS", true, p, 0, MUSICRATE*5/2, MUSICRATE/12, MUSICRATE/4)
						mapmusname = "XSDSS" 
					end
				elseif MUSIC != "XSDARK"
					S_ChangeMusic("XSDARK", true, p, 0, 0, 0, 2*MUSICRATE)
					mapmusname = "XSDARK" 			
				end 
				return true --
			elseif p.powers[pw_super] or (s.XS_FORCEdark and s.XS_FORCEdark.supersonic==true)
				local RB = xs.FreeFlight
				if RB and RB.LockOn and RB.LockOn.valid
					if BATTLEMUSIC==nil
						local SETSONG = TENKMUSIC[ min(#TENKMUSIC, XS_TENKAICHIMUSIC+1) ]
						S_ChangeMusic(SETSONG, true, p)
						mapmusname = SETSONG
					end
				elseif MUSIC != "XSSS" and not (MAP.levelflags & LF_NOSSMUSIC) 
					S_ChangeMusic("XSSS", true, p)
					mapmusname = "XSSS"
				end 
				return true --
			elseif p.powers[pw_sneakers] or p.powers[pw_invulnerability]
				if p.exiting
					p.powers[pw_sneakers] = 0
					p.powers[pw_invulnerability] = 0 return --
				end
				local INVI = p.powers[pw_invulnerability] or 0
				local FAST = p.powers[pw_sneakers] or 0
				
				if (FAST >= INVI) --Which one to prioritize?
					S_ChangeMusic("XSG2GF", false, p)
				else
					S_ChangeMusic("XSHERO", false, p)
				end return true --
			end
		end
		
		--If we changed music, the function would've ended, so if we got here, set the music back to the default!
		local RESTORE = false
		local MMUS = mapmusname
		for i = 1,#XMUSIC
			if MMUS == XMUSIC[i] RESTORE = true break end --
		end
		local DEFAULTMUSIC = MAP.musname
		if DEFAULTMUSIC
			if MUSIC != "XSDONE" and MUSIC != "_clear"
				local SSMUSIC = (MUSIC=="XSDSS" or MUSIC == "XSDARK" or MUSIC == "XSSS" or (BATTLEMUSIC!=nil))
				S_ChangeMusic(DEFAULTMUSIC, true, p, 0, 0, (SSMUSIC) and 3*MUSICRATE or 0, (SSMUSIC) and 4*MUSICRATE or 0)
			end
			if RESTORE==true
				mapmusname = DEFAULTMUSIC
			end
		else
			COM_BufInsertText(p, "tunes -default")	
		end
		if not p.exiting
			P_RestoreMusic(p)
		end
	end	
end

--Attempt to become a legendary Super Saiyan! 
XSon.StartTransformation = function(p,s,xs, FORCE, TYPE) --If FORCE is 1, it forces Super. If not nil otherwise, it detransforms.
	if (xs.custom3 == 15 or FORCE)
	and ((p.rings >= 100) or p.powers[pw_super] > 2)
	and All7Emeralds(emeralds)
	and not p.exiting and s.skin == "xsonic" and not xs.darkphase
		if (XS_Super(p)==0) or FORCE==1 --We're not super, so transform!
			XSon.Echoes(p,s,xs, "stop")
			XSZipDashCancel(p, s, xs)
			XSSnapDiveReset(p, s, xs, true)
			p.xsringburst,p.xswallrun,s.xsoverglow = nil
			
			XS_QuickDust(s)
			if TYPE != "superTA"
				XSon.TransformGFX(s) --Rotating wind effect
				p.XSscalecam = 30
				p.camerascale = (p.pflags & PF_ANALOGMODE) and 40000 or 55555	
				s.momx,s.momy,p.speed = 0,0,0
				for i = -60,1 --Start making orbs!
					if i > -11
						XSon.TransformGFX2(s, i*188<<16) --Rising orbs
					end
				end				
			end
			s.state = S_PLAY_S_XTRANSFORM
			XSon.DeleteAura(p, s, xs, true, true, true)
			XSon.SuperAura(p, s, xs, nil, false)
			XSon.sparkles(p,s,xs, 39, 6, 3, 4<<16, P_RandomRange(SKINCOLOR_SUPERGOLD1, SKINCOLOR_SUPERGOLD5), AST_ADD)
			XSon.ExpandingGlow(s, SKINCOLOR_YELLOW, 20, 100, true, AST_ADD)	
			
			p.charflags = $|SF_SUPER
			p.powers[pw_nocontrol] = 2
			p.powers[pw_super] = 9
			XSon.Quake(s, 13<<16, 20)
			
			XSSpawnJumpDust(s)
			s.XStransform = 77
			xs.hudboost = 1
			XSon.Flash(s, PAL_WHITE, 3)	
			local ghs = XSon.SonicBoom(p, s, xs, 8)
			if ghs and ghs.valid
				ghs.frame = $ & ~FF_PAPERSPRITE
				ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			end				
			if TYPE != "superTA"
				xs.waitsuperHUD = 14
				P_SetObjectMomZ(s, FRACUNIT/2)
				if p.powers[pw_shield] 
					local FIREFLOWER = (p.powers[pw_shield] & SH_FIREFLOWER) and SH_FIREFLOWER or 0 
					p.powers[pw_shield] = FIREFLOWER
				end	
				p.powers[pw_ignorelatch] = 32769
				XSon.Sound(s, sfx_xssfph)
				XSVoice(p, s, xs, sfx_xsvoc7, true)	
				S_StartSound(s, sfx_xssfph, p)	
			else
				s.color = SKINCOLOR_SUPASONIKKU
			end
			XSon.SetMusic(p,s,xs) return 7 --
		end
	end
end

--Animation for the Super Sonic transformation!
XSon.SuperTransform = function(p, s, xs)
	local FRAME = (s.frame & FF_FRAMEMASK)
	p.cmd.buttons,p.cmd.sidemove,p.cmd.forwardmove = 0,0,0	
	p.powers[pw_ignorelatch] = 32769
	xs.ChaosLambent = nil
	if (FRAME > B)			
		local ghs = XSon.TransformGFX2(s, -488<<16, 54<<16, XSLocalRNG(FRACUNIT-12000, 2<<16,s), nil, XSLocalRNG(s.scale*120, s.scale*-50,s)) --Rising orbs from now on!
		if (leveltime % 2)
			ghs.angle = XSLocalRNG(360,0,s)*ANG1
			ghs.flags2 = MF2_SPLAT
			ghs.renderflags = $|RF_FLOORSPRITE|RF_NOSPLATBILLBOARD
			ghs.spriteyoffset = 10<<16
			ghs.tracer = nil
		else
			ghs.spritexscale = $+XSLocalRNG(0, 3<<16,s)
		end
	end
	if (FRAME <= E)
		s.color = (p.powers[pw_shield] & SH_FIREFLOWER) and SKINCOLOR_WHITE or p.skincolor --Don't be yellow YET!
		if (s.tics % 3 == 0)
			XSSpawnJumpDust(s)
		end
		s.frame = $ & ~FF_FULLBRIGHT
		P_SetObjectMomZ(s, FRACUNIT/2)
		p.camerascale = $-5500 --zoom in...
		if (p.camerascale < 35000)
			p.camerscale = 35000
		end
	else
		s.frame = $|FF_FULLBRIGHT
		P_SetObjectMomZ(s, 2<<16-15000) --Rise a bit!	
		p.XSscalecam = 11

		if (leveltime % 3 == 0)
			local ghs = XS_RingBurstDust(p, s, xs, 400*s.scale)	
			if ghs and ghs.valid
				ghs.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
				ghs.frame = O|FF_TRANS40
				ghs.spritexscale = $+XSLocalRNG(1,22000,s)
				ghs.dispoffset = 8
				if leveltime % 6 == 0
					ghs.colorized = true
					ghs.color = SKINCOLOR_SUPERGOLD5
					ghs.frame = $+FRACUNIT
				end
			end
		end
		
		--SUPER aura!
		if (leveltime % 8 == 0) and (FRAME > E)
		or (FRAME == E) and s.tics == 1
			P_RadiusAttack(s, s, 444*s.scale, DMG_ELECTRIC, false)
			
			local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSUNDERGLOW)
			ghs.tracer,ghs.target = s,s
			ghs.fuse = 10
			ghs.scale = s.scale*2
			ghs.destscale = $*99
			ghs.sprite = SPR_XSAA
			ghs.frame = A|FF_TRANS20
			ghs.color = SKINCOLOR_GOLDENROD
			ghs.blendmode = AST_ADD
			ghs.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.XAura = true
			ghs.spritexscale = (s.spritexscale*10)/9
			
			if FRAME == E
				local ghs = XS_RingBurstShine(p, s, xs)
				if ghs and ghs.valid
					ghs.spritexoffset = 0
					ghs.spriteyscale = $+50000
					ghs.spritexscale = $+50000
				end
				XSon.ExpandingGlow(s, SKINCOLOR_YELLOW, 20, 100, true, AST_ADD)
			end
			XS_RingBurstSparkles(s, nil, SKINCOLOR_GOLDENROD, 800)
			
			for i = 0,2
				local ghs = XS_CircleWave(s, 2, SKINCOLOR_GOLDENROD, 24-i, (FRACUNIT+55000-(i*13500)))
				if ghs and ghs.valid
					ghs.colorized = true
					ghs.color = SKINCOLOR_GOLDENROD
					ghs.scalespeed = (FRACUNIT+45000)/(1+i)
					ghs.sttfade = true
					ghs.z = $+(i*70*s.scale*P_MobjFlip(s))
					if FRAME != E
						ghs.frame = $+(2<<16)+(i<<16)
						if (leveltime % 16 == 0) 
							ghs.z = $+(23*s.scale*P_MobjFlip(s))
						end
					end
				end
			end
		end
		
		local ghs = XSon.SmokeAura(p, s, xs, SKINCOLOR_GOLDENROD) --Show the aura.
		if ghs and ghs.valid
			ghs.scale = (s.scale/5)*FRAME
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60
			ghs.color = SKINCOLOR_GOLDENROD
			ghs.colorized = true
		end		
		if (p.pflags & PF_ANALOGMODE)
			p.camerascale = min(2<<16-7000, $+15000)
		else
			p.camerascale = min(3<<16, $+20000)
		end	
	end	
	--The animation part.
	if (FRAME < E) and (s.tics % 4 == 0) --Advance frame every 4 frames!
	
	or (FRAME >= E) and (s.tics % 2 == 0) --advance frame every 2 frames!
	
		if (FRAME == E)
			P_RadiusAttack(s, s, 500*s.scale, DMG_ELECTRIC, false)
			XSon.Quake(s, 72<<16, 9)
			XS_QuickDust(s, SKINCOLOR_SUPERSILVER1)
			XS_QuickDust(s, SKINCOLOR_SUPERGOLD5, AST_ADD)
			XSon.Flash(s, PAL_MIXUP, 3, 600<<16)
			for i = 5,9,2
				local ghs = XSon.TransformGFX(s, nil, L|FF_TRANS10, 19+i)
				ghs.destscale = 99<<16
				ghs.scalespeed = FRACUNIT/i
				ghs.sttfade = 11
				if i == 9
					ghs.spritexscale,ghs.spriteyscale = $/2,$/2
				end
			end
			
			local ghs = XS_Vortex(s, 36, FRACUNIT+25555, nil, R|FF_TRANS30, SKINCOLOR_GOLD)
			ghs.spritexoffset = 58<<16
			ghs.spriteyoffset = 60<<16
			ghs.renderflags = $|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS
			ghs.flags2 = $|MF2_SPLAT
			ghs.tracer = nil
			ghs.z = $+(3*s.scale*P_MobjFlip(s))
			ghs.scalespeed = FRACUNIT/2
			
			for i = 1,14
				local ghs = XS_CraterSpew(s, XSLocalRNG(0,361-i,s), 14-(i/2))
				if ghs and ghs.valid
					ghs.momx,ghs.momy = $*3,$*3
				end
			end			
		end
		if (FRAME >= O)
			s.frame = ($ & ~FF_FRAMEMASK)|L
			XSon.Quake(s, 23<<16, 10)
		else
			s.frame = $+1
			if (FRAME > D)
				XS_RingBurstSparkles(s, 28, s.color)
				if (s.tics <= 20) and not p.xssuperflash
					p.xssuperflash = {trans = FF_TRANS90, timer = 35, speed = 2, flashtic = 17, reversal = 18, COLOR = SKINCOLOR_SUPERSILVER1}
				end
			end
		end
	end
	if s.tics == 1
		if (xs.FreeFlight!=nil)
			s.flags = $ & ~MF_NOGRAVITY
			xs.FreeFlight = nil
		end
		XSon.TryFlight(p,s,xs, nil, 0, nil, "noSFX")
	end
end

--Call this only if we have s.XS_NiGHTStransform, in a PostThinkFrame
XSon.NiGHTStransform = function(p,s,xs)
	if s.XS_NiGHTStransform==nil s.XS_NiGHTStransform = 0 end
	if (s.skin != "xsonic") --Only this one!
		if (p==consoleplayer)
			COM_BufInsertText(p, "xsonic")
		end
		R_SetPlayerSkin(p, "xsonic")
		s.skin = "xsonic"
	end
	local NUM = max(0, s.XS_NiGHTStransform-1)
	local FRAME = min(L, NUM/4) --Goes from A to L. 12 frames. 5*12 = 60
	s.sprite2 = SPR2_TAL5
	s.frame = ($ & ~FF_FRAMEMASK)|FRAME|FF_FULLBRIGHT
	
	if ((p.powers[pw_nocontrol] or 0) < 3) p.powers[pw_nocontrol] = 3 end
	if (s.tics < 5) s.tics = 5 end
	if (s.anim_duration < 5) s.anim_duration = 5 end
	
	if not (s.XNghost and s.XNghost.valid)
		s.XNghost = P_SpawnGhostMobj(s)
	end
	local TRANS = 9 - (NUM/7) if (NUM < 16) TRANS = 9 end
	local ghs = s.XNghost 
	ghs.color,ghs.colorized = SKINCOLOR_SUPASONIKKU, true
	ghs.blendmode = AST_ADD
	ghs.dispoffset = 148
	ghs.spriteyoffset = s.spriteyoffset-3<<16
	ghs.sprite2 = s.sprite2
	ghs.frame = (s.frame & ~FF_TRANSMASK)| (max(2, TRANS)<<16)
	ghs.tics,ghs.fuse = 9,9
	ghs.z = s.z
	if (NUM > 18) and ghs.destscale != 99<<16
		ghs.destscale = 99<<16
		ghs.scalespeed = $*2/3
	else
		ghs.scale = s.scale+1
	end
	
	if (NUM % 3 == 0) or (NUM < 6)
		local ths = XS_RingBurstSparkles(s, 16, SKINCOLOR_XSSUPERAURA, 41)	
		if ths and ths.valid
			ths.dispoffset = -111
		end
		local ths = XSon.sparkles(p,s,xs, 120, 6, 5, 5<<16, SKINCOLOR_SUPERGOLD4, AST_ADD)			
		if ths and ths.valid
			ths.dispoffset = -136
		end
	end
	if NUM==0
		xs.FreeFlight = nil
		XSon.Sound(s, sfx_xssfe5, 50)
		XSon.Sound(s, sfx_xssfpj, 90)
		XSon.DeTransformGFX(p,s,xs, "ghosts")
		P_FlashPal(p, PAL_RECYCLE, 3)
		XSon.ElecticBody(s, 6, SKINCOLOR_GOLDENROD, FF_HORIZONTALFLIP, AST_TRANSLUCENT) 
		XSon.SquashFlash(s, SKINCOLOR_SILVER, nil, FF_TRANS20, 2<<16)	
		XSon.OrbPillars(s, 11, 350*s.scale, 2, 21<<16, H|FF_TRANS10, FRACUNIT+50000)
		XSon.RadiantLight(s, SKINCOLOR_XSSUPERAURA, 10, nil, S|FF_TRANS30, FRACUNIT-15000)		
	else
		if NUM == 9 
			XSon.Sound(s, sfx_xssfph, 160)
			XSon.DeTransformGFX(p,s,xs, "ghosts")
		elseif NUM == 15 or NUM == 22
			local ghs = P_SpawnGhostMobj(s) --Extra ghost.
			ghs.scale = s.scale*3/2
			ghs.color,ghs.colorized = SKINCOLOR_SUPASONIKKU,true
			ghs.blendmode = AST_ADD
			ghs.dispoffset = 99+NUM
			ghs.tics,ghs.fuse = 12,12
			ghs.spriteyoffset = (s.spriteyoffset)-9<<16
			ghs.destscale = 99<<16	
		elseif NUM == 25
			S_StopSoundByID(s, sfx_xssfpj, 50)
			XSon.Sound(s, sfx_xssfpj, 150)			
			P_NukeEnemies(s,s,0)
			XSon.ExpandingGlow(s, SKINCOLOR_YELLOW, 20, 96, true, AST_ADD)
		elseif NUM == 27		
			XSon.RadiantLight(s, SKINCOLOR_XSSUPERAURA, 17, nil, S|FF_TRANS60, FRACUNIT)
			s.XS_Noldcolor = s.color
			s.color = SKINCOLOR_SUPASONIKKU			
		elseif NUM == 36
			p.xssuperflash = {trans = FF_TRANS90, timer = 35, speed = 2, flashtic = 17, reversal = 18, COLOR = SKINCOLOR_SUPERSILVER1}
			XSon.OrbPillars(s, 11, 350*s.scale, 2, 21<<16, H|FF_TRANS10, FRACUNIT+50000)
		elseif NUM == 44
			XSon.ExpandingGlow(s, SKINCOLOR_YELLOW, 20, 96, true, AST_ADD)
		elseif NUM >= 57
			s.state = S_PLAY_NIGHTS_FLOAT
			if s.XS_Noldcolor != nil
				s.color = s.XS_Noldcolor
			else
				s.color = p.skincolor
			end
			S_StopSoundByID(s, sfx_xssfe5)
			
			s.XNghost,s.XS_Noldcolor,s.XS_NiGHTStransform = nil
			p.drawangle = (p.cmd.angleturn<<16)-ANGLE_90
		end
	end
end

--For mods. Doesn't do X Sonic skin check.
XSon.Hyper = function(p) 
	if not (p.powers[pw_super]) return end
	if p.mrce and mrce and (MRCE_isHyper(p)) --Use MRCE's own check.
	or p.hyper==true or p.hyper==1
	or type(p.hyper)=="table" and p.hyper.isunlocked
	or p.hypermode==1
		return true --
	end
end

--Removes or adds SF_SUPER. Mostly for mod support. Leave nil to auto-detect.
XSon.SFSUPER = function(p,s,xs, ON)
	if ON=="OFF" or ON==nil
		if xs.darkphase
		or not (
		(p.rings < 50)
		or p.powers[pw_shield] and (p.powers[pw_shield]!=SH_FIREFLOWER)
		or p.powers[pw_super] or xs.supertransform
		or p.powers[pw_carry]==CR_NIGHTSMODE
		)
			p.charflags = $ & ~SF_SUPER return false
		end
	end
	if ON=="ON" or ON==nil
		if (p.rings < 50)
		or p.powers[pw_shield] and (p.powers[pw_shield]!=SH_FIREFLOWER) and not (p.powers[pw_shield] & SH_ARMAGEDDON)
		or p.powers[pw_super] or s.XStransform
		or p.powers[pw_carry]==CR_NIGHTSMODE
			if not ((p.pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)) and (p.cmd.buttons & BT_SPIN) and not (p.pflags & PF_USEDOWN))
				p.charflags = $|SF_SUPER return true --
			else
				p.pflags = $|PF_USEDOWN
			end
		end	
	end
end