--#==============================================================================
--Our HUD hook for menus, flashes, and little trinkets
--#==============================================================================
local XSon = XSon

--Seperates a number or string into a table of individual characters.
local function GSSeperateChars(NUM)
	local TABLE,OK = {},tostring(NUM)
	local CHARS = string.len(OK) if not CHARS return TABLE end --
	for i = 1,CHARS
		TABLE[i] = string.sub(OK, i, i)
	end return TABLE --
end

--Show results of the last Beam Clash!
local function BeamClashResults(v, MYPOWER, FOEPOWER, TIMER, WON, p)
	if (p!=displayplayer) return end
	local YOFF,TRANS = 0,0
	if (TIMER < 16)
		if (TIMER%2) return end
		TRANS = (8-(TIMER/2))<<16
	elseif (TIMER > 38)
		TRANS = abs(38-TIMER)<<16
		TRANS = min(FF_TRANS90, max($, FF_TRANS10))
		YOFF = 38-TIMER
	end
	MYPOWER,FOEPOWER = max(1, MYPOWER/5),max(1, FOEPOWER/5)
	if (MYPOWER==FOEPOWER) --Results show simplified power scales, so adjust "equal" scores to avoid confusion.
		if WON==true
			MYPOWER= $+1
		else
			FOEPOWER = $+1
		end
	end
	local X,Y = 26,99+YOFF
	local SCALE = (WON==true) and 33000 or 24000
	local CMB,CM = v.getColormap(nil, SKINCOLOR_PUREBLACK)
	local TABLE,PATCH = GSSeperateChars(MYPOWER)
	if TABLE != nil
		CM = v.getColormap(nil, (WON==true) and SKINCOLOR_SUPASONIKKU or SKINCOLOR_GREY)
		for TEXT = 1, #TABLE
			if type(TABLE[TEXT])=="string"
				PATCH = v.cachePatch(("XSDSNU"+TABLE[TEXT]))
				v.drawScaled((X+2)<<16,(Y+2)<<16, SCALE, PATCH, TRANS, CMB) --The number's shadow.
				v.drawScaled(X<<16,Y<<16, SCALE, PATCH, TRANS, CM)
				X = $+27
			end
		end
	end
	
	TABLE = GSSeperateChars(FOEPOWER)
	if TABLE != nil
		SCALE = (WON!=true) and 33000 or 24000
		X,Y = 186,40+YOFF	
		CM = v.getColormap(nil, (WON==false) and SKINCOLOR_SUPASONIKKU or SKINCOLOR_GREY)
		for TEXT = 1, #TABLE 
			if type(TABLE[TEXT])=="string"
				PATCH = v.cachePatch(("XSDSNU"+TABLE[TEXT]))
				v.drawScaled((X+2)<<16,(Y+2)<<16, SCALE, PATCH, TRANS, CMB)
				v.drawScaled(X<<16,Y<<16, SCALE, PATCH, TRANS, CM)
				X = $+27
			end
		end
	end
end

--Always fun to build up a big counter clash number.
local function CounterClashDisplay(v, p, RB) if (p!=displayplayer) return end
	local TIMER,NUM = RB.timer or 0, min(9999, max(1, RB.num or 0))
	if (NUM <= 1) return end --Don't display the first counter.
	
	local XSHAKE,YSHAKE = 0,0
	if (NUM > 4) or (TIMER > 36)
		XSHAKE = (leveltime%2) and 1 or -1
		YSHAKE = (leveltime%4==0) and 2 or (leveltime%3==0) and 1 or (leveltime%2==0) and -1 or 0
	end
	local SCALE,TRANS = 24000+min(12000, NUM*620), 0
	local X,Y = 60+XSHAKE,35+YSHAKE
	
	if (TIMER > 37)
		if (TIMER%2) return end
		local DIFF = abs(37-TIMER)
		X = $+DIFF
		Y = $-(DIFF*2)
		TRANS = min(FF_TRANS90, DIFF<<16)
		SCALE = $+(DIFF*999)
	end
	
	local TABLE,PATCH = GSSeperateChars(NUM)
	if TABLE != nil
		local CMB,CM = v.getColormap(nil, SKINCOLOR_PUREBLACK)
		if (NUM > 5) 
			if (NUM >= 10) 
				local TEXT = (NUM>999) and "Someone press C3 already!" 
				or (NUM>899) and "You can stop now..."
				or (NUM>799) and "uh..."
				or (NUM>699) and "..."
				or (NUM>599) and "wtf..."
				or (NUM>499) and "*speechless*"
				or (NUM>399) and "Are you a machine?"
				or (NUM>299) and "How're you still going?"
				or (NUM>199) and "Holy crap..."	
				or (NUM> 99) and "what."			
				or (NUM>=50) and "AMAAAAAZING!!!!!"
				or (NUM>=40) and "OUTSTANDING!!!!"
				or (NUM>=30) and "AWESOME!!!"
				or (NUM>=20) and "GREAT!!" 
				or "GOOD!"
				v.drawString(X+48, Y-12, ((leveltime%2) and "\x82" or "\x85")+TEXT, V_ALLOWLOWERCASE, "thin-center")	
			end
			CM = v.getColormap(nil, (leveltime%2) and SKINCOLOR_SUPASONIKKU or SKINCOLOR_CRIMSON)
		else
			CM = v.getColormap(nil, 
			(NUM>4) and SKINCOLOR_FLAME or (NUM>3) and SKINCOLOR_SUPERORANGE5 
			or (NUM>2) and SKINCOLOR_SUPERSKY2 or SKINCOLOR_WHITE )
		end
		for TEXT = 1, #TABLE
			if type(TABLE[TEXT])=="string"
				PATCH = v.cachePatch(("XSDSNU"+TABLE[TEXT]))
				v.drawScaled((X+2)<<16,(Y+2)<<16, SCALE, PATCH, TRANS, CMB) --The number's shadow.
				v.drawScaled(X<<16,Y<<16, SCALE, PATCH, TRANS, CM)
				X = $+28
			end
		end
	end
end

--When two Falcon Punches collide.
local function BeamClashHUD(v, p,s,xs,CL, tp,t,xss,CLL)
	if (splitscreen==true and p==players[1]) or (p!=displayplayer) return end --

	local X,Y = 25,99	
	local FLAGS = V_SNAPTOLEFT
	local XSHAKE = (leveltime%2) and 1 or -1
	local YSHAKE = (leveltime%4==0) and 2 or (leveltime%3==0) and 1 or (leveltime%2==0) and -1 or 0	
	local XOFF,YOFF = XSHAKE,YSHAKE
	
	--Draw the power text.
	if CL.powerpressed
		XOFF = $*((CL.powerpressed>5) and 4 or (CL.powerpressed>3) and 3 or 2)
		YOFF = $*((CL.powerpressed>5) and 4 or (CL.powerpressed>3) and 3 or 2) + CL.powerpressed
	end	
	local MYPOWER,FOEPOWER,CM = 1,1
	if CL.power
		MYPOWER = max(1, CL.power/5)
	end
	if CLL.power
		FOEPOWER = max(1, CLL.power/5)
	end	
	local SCALE = 33000
	local CMB = v.getColormap(nil, SKINCOLOR_PUREBLACK)
	if (leveltime%2==0) and (p.cmd.buttons & BT_SPIN) --Trying your best!
		CMB = v.getColormap(nil, (leveltime%4==0) and SKINCOLOR_MINT or SKINCOLOR_EMERALD)
		SCALE = $+1250
	end
	if (MYPOWER > FOEPOWER-8) --About equal or winning!
		CM = v.getColormap(nil, (leveltime%2) and SKINCOLOR_ORANGE or SKINCOLOR_SUPASONIKKU)
	else --Losing!!
		CM = v.getColormap(nil, (leveltime%2) and SKINCOLOR_SUPERGOLD5 or SKINCOLOR_CRIMSON)
		SCALE = $-min(9000, abs(FOEPOWER-MYPOWER)*320)
	end
	
	local TABLE = GSSeperateChars(MYPOWER)
	if TABLE != nil
		if (#TABLE > 1)
			X = $-((#TABLE)*7)
		end	
		for TEXT = 1, #TABLE --Use X Sonic Font for the numbers.
			if type(TABLE[TEXT])=="string"
				local PATCH = v.cachePatch(("XSDSNU"+TABLE[TEXT]))
				v.drawScaled((X+XOFF+2)<<16,(Y+YOFF+2)<<16, SCALE, PATCH, 0, CMB)
				v.drawScaled((X+XOFF)<<16,(Y+YOFF)<<16, SCALE, PATCH, 0, CM)
				X = $+28
			end
		end
	end
	
	--Now for the enemy!
	X,Y = 186,40
	SCALE = 33000
	XOFF,YOFF = XSHAKE,YSHAKE
	if CLL.powerpressed
		XOFF = $*((CLL.powerpressed>5) and 4 or (CLL.powerpressed>3) and 3 or 2)
		YOFF = $*((CLL.powerpressed>5) and 4 or (CLL.powerpressed>3) and 3 or 2) + CLL.powerpressed
	end
	CMB = v.getColormap(nil, SKINCOLOR_PUREBLACK)
	if (leveltime%2==0) and (tp.cmd.buttons & BT_SPIN) --Trying your best!
		CMB = v.getColormap(nil, (leveltime%4==0) and SKINCOLOR_MINT or SKINCOLOR_EMERALD)
		SCALE = $+1250
	end
	if (FOEPOWER > MYPOWER-8)
		CM = v.getColormap(nil, (leveltime%2) and SKINCOLOR_ORANGE or SKINCOLOR_SUPASONIKKU)
	else
		CM = v.getColormap(nil, (leveltime%2) and SKINCOLOR_RUBY or SKINCOLOR_CRIMSON)
		SCALE = $-min(9000, abs(MYPOWER-FOEPOWER)*320)
	end
	TABLE = GSSeperateChars(FOEPOWER)
	if TABLE != nil
		if (#TABLE > 1)
			X = $-((#TABLE)*7)
		end	
		for TEXT = 1, #TABLE 
			if type(TABLE[TEXT])=="string"
				local PATCH = v.cachePatch(("XSDSNU"+TABLE[TEXT]))
				v.drawScaled((X+XOFF+2)<<16,(Y+YOFF+2)<<16, SCALE, PATCH, 0, CMB)
				v.drawScaled((X+XOFF)<<16,(Y+YOFF)<<16, SCALE, PATCH, 0, CM)
				X = $+28
			end
		end
	end

	--Now draw the buttons. A mashable JUMP button, and a hold-SPIN button for energy consumption.
	X,Y = 144+XSHAKE,141+YSHAKE
	SCALE = 34000
	if CL.powerpressed == 8 and (p.cmd.buttons & BT_JUMP)
		SCALE = 45000
		X,Y = $-3,$-2
		CM = v.getColormap(nil, SKINCOLOR_SUPERGOLD5)
	elseif (leveltime%3==0)
		SCALE = 29000
		X,Y = $+3,$+3
		CM = v.getColormap(nil, SKINCOLOR_FLAME)
	else
		CM = v.getColormap(nil, SKINCOLOR_CRIMSON)
	end
	v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT1"), FLAGS, CM)
	
	X,Y = 190,146
	if (p.cmd.buttons & BT_SPIN)
		CM = v.getColormap(nil, (leveltime%2) and SKINCOLOR_MINT or SKINCOLOR_EMERALD)
		v.drawScaled(X<<16, Y<<16, 25500, v.cachePatch("XSBUTT2"), FLAGS, CM)
		if not (leveltime%3==0)
			CM = (leveltime%2) and "\x82" or "\x85"
			v.drawString(X+5, Y+7, CM+"Draining health", V_ALLOWLOWERCASE, "small")	
			v.drawString(X+5, Y+15, CM+" for power!!", V_ALLOWLOWERCASE, "small")
		end
	else
		v.drawScaled(X<<16, Y<<16, 27500, v.cachePatch("XSBUTT2"), FLAGS|FF_TRANS30, v.getColormap(nil, SKINCOLOR_GREY))
	end
/*	v.drawString(225, 191, 
	"\x88"+"CL.struggle: \x82"+tostring(CL.struggle)+"\x88 CL.init: \x82"+tostring(CL.init),
	V_ALLOWLOWERCASE, "small")
	v.drawString(225, 200, 
	"\x89"+"CLL.struggle: \x82"+tostring(CLL.struggle)+"\x89 CLL.init: \x82"+tostring(CLL.init),
	V_ALLOWLOWERCASE, "small") */
end

--Button tutor behaviour
--s.XS_tutor = {b1 = "2", ex1 = "3", text1="Zip Dash", timelimit=120}
XSon.ButtonTutor = function(p,s,xs, TUT)
	if TUT.init == nil TUT.init = 0 end if TUT.timelimit == nil TUT.timelimit = 61 end
	TUT.init = $+1

	if (TUT.init > TUT.timelimit)
		s.XS_tutor,s.XS_TutTimer = nil return --
	elseif not TUT.constant
		s.XS_TutTimer = s.XS_TutTimer and $+1 or 1
		if TUT.dontshow
			TUT.dontshow = $-1
		end	
		local MAXTIME = max(3, TUT.timelimit-70)
		if (TUT.init > MAXTIME)
			if (leveltime%2)
				TUT.dontshow = 1
			end
			if (TUT.init > (TUT.timelimit-70))
				TUT.forcetrans = TUT.forcetrans and $+1 or 1 
			end
		end
	end
end

--Display buttons neatly for the Ring Burst.
local function XS_ShowButtons(v, s, TUT)
	if TUT.dontshow return end --
	local TIME = s.XS_TutTimer or 0
	local BUTTONS,TEXT,EX = {},{},{}
	local X,Y,XOFF,YOFF,TRANS = 100,128,0,0,0
	
	BUTTONS[1] = TUT.b1 if TUT.b2 BUTTONS[2] = TUT.b2 end if TUT.b3 BUTTONS[3] = TUT.b3 end
	TEXT[1],TEXT[2],TEXT[3] = TUT.text1,TUT.text2,TUT.text3
	EX[1],EX[2],EX[3] = TUT.ex1,TUT.ex2,TUT.ex3	
	
	local BSCALE = 20500
	if TUT.forcetrans
		TRANS = max(0, min(TUT.forcetrans/6,9))<<16
		if (TUT.forcetrans>36)
			YOFF = $+(36+TUT.forcetrans)
		end
	else
		if TIME != "constant" and (TIME < 14)
			YOFF = $+(28-(TIME*2))
			if (TIME < 7)
				TRANS = max(0, 7-TIME)<<16
				BSCALE = 13500-(1000*TIME)
			end
		end
		if TUT.shake
			XOFF = $+((leveltime%2) and 1 or -1)
			YOFF = $+((leveltime%4==0) and 2 or (leveltime%3==0) and 1 or (leveltime%2==0) and -1 or 0)		
		end		
	end

	local COLOR = ((s.color<6) or s.color == SKINCOLOR_SUPERSILVER1) and SKINCOLOR_SONIKKU or s.color
	local CM = v.getColormap(nil, COLOR)
	local OGCM = CM
	local FLAGS = V_SNAPTOBOTTOM|TRANS|V_PERPLAYER
	--1:JUMP  2:SPIN  3:C1  4:C2  5:C3  6:TOSSFLAG
	for i = 1,#BUTTONS
		local KEY = tostring(BUTTONS[i]) if KEY == nil continue end --
		local EXTRA = EX[i]
		if type(EXTRA) == "string" and EXTRA !="none" --Extra/alt button?
			if EXTRA == "1"
				CM = v.getColormap(nil, SKINCOLOR_SALMON)
			elseif EXTRA == "2"
				CM = v.getColormap(nil, SKINCOLOR_EMERALD)
			elseif EXTRA == "3"
				CM = v.getColormap(nil, SKINCOLOR_GOLDENROD)			
			else
				CM = OGCM
			end			
			v.drawScaled((X+XOFF-18)<<16, (Y+YOFF-1)<<16, BSCALE-2000, v.cachePatch("XSBUTT"+EXTRA), FLAGS, CM)			
		end
		if KEY == "1"
			CM = v.getColormap(nil, SKINCOLOR_SALMON)
		elseif KEY == "2"
			CM = v.getColormap(nil, SKINCOLOR_EMERALD)
		elseif KEY == "3"
			CM = v.getColormap(nil, SKINCOLOR_GOLDENROD)			
		else
			CM = OGCM
		end		
		v.drawScaled((X+XOFF)<<16, (Y+YOFF)<<16, BSCALE, v.cachePatch(("XSBUTT"+KEY)), FLAGS, CM)
		if type(TEXT[i]) == "string" and TEXT[i]!="none"
			v.drawString((X+XOFF)+24, (Y+YOFF)+7, TEXT[i], FLAGS|V_ALLOWLOWERCASE, "thin")	
		end
		YOFF = $+20
	end
end

--This Planet Egg...this is the one from THEIR world!
--BTW, scoreboard HUD is in XS-EGG. "scores"
local function XS_AtPlanetEgg(v,p,s,xs, EGG, t)
	local INIT = EGG.init or 0
	local TIMER = EGG.timer or 0
	local TRANS,FLAGS = 0, V_SNAPTOTOP|V_SNAPTOLEFT|V_PERPLAYER
	if (TIMER < 9)
		TRANS = max(9-TIMER, $)
	elseif (INIT < 30)
		TRANS = max((30-INIT)/3, $)
	end

	local X,Y, XO,YO = 127,142, 0,0
	if (TRANS < 10)
		local TCOLOR = (t.CanOffer) and "\x80" or "\x85" --Display the text.
		YO = $+(TRANS*2)
		if (INIT > 45) and TCOLOR == "\x80"
			if (INIT%46==0)
				TCOLOR = "\x81"
				XO,YO = $-3,$-2	
			elseif (INIT%47==0)
				TCOLOR = "\x8f"
				XO,YO = $+1,$+2
			end
		end
		local PTRANS = (not t.CanOffer) and FF_TRANS90 or TRANS<<16
		v.drawScaled((X+XO)<<16, (Y+YO)<<16, 41000, v.cachePatch("XSBUTT5"), PTRANS, v.getColormap(nil, XSon.DarkAura(0)))
		
		if t.sacrifice
			v.drawString(160+XO, (Y+YO)-20,  "\x8f"+"Sacrifice Required:", max(3<<16, TRANS<<16)|V_ALLOWLOWERCASE, "thin-center")
			v.drawString(160+XO, (Y+YO)-9,  TCOLOR+t.sacrifice, max(2<<16, TRANS<<16)|V_ALLOWLOWERCASE, "thin-center")
		end
	end
	
	--Screen Fuzzing?
/*	local PATCH,COLOR = "XSSCFUZZ", SKINCOLOR_GREY
	local vHEIGHT,SCALE = v.height() or 0, FRACUNIT
	X,Y = 0,0
	
	if vHEIGHT == 1080 --1080p.
		Y = -40
		SCALE = 82033	
	elseif (vHEIGHT == 1200) or (vHEIGHT == 800) or (vHEIGHT == 400) or (vHEIGHT == 200) --Green resolutions.
		SCALE = FRACUNIT
		Y = 0
	end	
	if (leveltime%2==0)
		X = 320
		FLAGS = ($ & ~V_SNAPTOLEFT)|V_SNAPTORIGHT|V_FLIP
	end
	if (INIT > 20) 
		local FTRANS  = max(4, 14-(INIT/5))
		if (FTRANS < 10)
			FTRANS  = FTRANS <<16
			v.drawScaled((X)<<16, (13+Yo)<<16, SCALE, v.cachePatch(PATCH), FLAGS|FTRANS, v.getColormap(nil, COLOR))	
		end
	end */
end

--You've obtained a Power Ring! Do an animation, or just display it normally?
local function XS_PowerRing(v,p,s,xs)
	local Xo,Yo,SCALE,TRANS = 0,0,FRACUNIT/8,FF_TRANS10
	if p.xsringburst
		if xs.PowerRing and (xs.PowerRing < 60) and (leveltime%2)
			return --Flicker!
		end
		local ADD = (leveltime%4==0) and 4 or (leveltime%3==0) and 3 or (leveltime%2==0) and 2 or 1
		Xo = $-(ADD) --Compensate for the scaling
		Yo = $-(ADD) 
		SCALE = $+(ADD*900)
	elseif xs.FirstPWring and not ((xs.FirstPWring < 60) and (xs.FirstPWring%2==0))
		local ZO = (xs.FirstPWring>268) and xs.FirstPWring-267 or 0
		local ZTRANS = (xs.FirstPWring>274) and FF_TRANS60 or (xs.FirstPWring>270) and FF_TRANS30 or 0
		v.drawString(160, 163+ZO, "You got a\x82 Power Ring!", V_SNAPTOBOTTOM|ZTRANS|V_PERPLAYER, "small-center")
		v.drawString(160, 171+ZO, "It gives one Ring Burst\x82 infinite\x80 energy!", V_SNAPTOBOTTOM|ZTRANS|V_PERPLAYER, "small-center")		
	end
	
	if s.XS_pwringget
		if (s.XS_pwringget < 0) --Fade from the screen gracefully.
			TRANS = (10-abs(s.XS_pwringget))<<16
		else
			Xo = $+(s.XS_pwringget*-11) --Come in from the left.
			Yo = $+(s.XS_pwringget*7) --Come in from below.
			SCALE = $+(s.XS_pwringget*1500)
			TRANS = min(FF_TRANS60, s.XS_pwringget<<16)
		end
	end
	local FLAGS = V_SNAPTOTOP|V_SNAPTORIGHT|V_PERPLAYER
	if xs.HUDpref!=true
		Xo = $-182
		Yo = $+163
		FLAGS = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_PERPLAYER
	elseif s.XShudpopin and not s.noXSHUDthisstage
		Yo = $-s.XShudpopin
		TRANS = min(FF_TRANS90, max($, s.XShudpopin<<16) )
	end
	v.drawScaled((258+Xo)<<16, (13+Yo)<<16, SCALE, v.cachePatch("XSPOWERR"), FLAGS|TRANS|V_ADD|V_PERPLAYER)	
end

--Seeing red.
local function DSS_RageHUD(v, RAGE)
	if splitscreen and players[1] return end --
	local STYLE = (RAGE.type=="veins") and "veins" or "bloodlust"
	local XSHAKE = (leveltime%2) and 1 or -1
	local YSHAKE = (leveltime%4==0) and 2 or (leveltime%3==0) and 1 or (leveltime%2==0) and -1 or 0	
	local SCALE = FRACUNIT
	local TRANS = min(FF_TRANS90, RAGE.trans<<16)
	local COLOR = v.getColormap(nil, RAGE.color)
	
	if STYLE == "bloodlust"
		local PATCH = v.cachePatch("XSDSSRGO")
		if RAGE.double
			TRANS = min(FF_TRANS60, $)
		end
		v.drawScaled((-1+XSHAKE)<<16, (-2+YSHAKE)<<16, SCALE, PATCH, V_SNAPTOTOP|V_SNAPTOLEFT|TRANS, COLOR)
		v.drawScaled((321+XSHAKE)<<16, (-2+YSHAKE)<<16, SCALE, PATCH, V_SNAPTOTOP|V_SNAPTORIGHT|TRANS|V_FLIP, COLOR)
		if RAGE.double
			COLOR = v.getColormap(nil, SKINCOLOR_MIDNIGHT)
			v.drawScaled((-10+XSHAKE)<<16, (-10+YSHAKE*5)<<16, SCALE*3/2, PATCH, V_SNAPTOLEFT|FF_TRANS80, COLOR)
			v.drawScaled((331+XSHAKE)<<16, (-10+YSHAKE*5)<<16, SCALE*3/2, PATCH, V_FLIP|V_SNAPTORIGHT|FF_TRANS80, COLOR)			
		end
	else
		v.drawScaled(XSHAKE<<16, (-2+YSHAKE)<<16, SCALE, v.cachePatch("XSDSSRGT"), V_SNAPTOTOP|TRANS, COLOR)
		v.drawScaled((-1+XSHAKE)<<16, (-2+YSHAKE)<<16, SCALE, v.cachePatch("XSDSSRGL"), V_SNAPTOLEFT|TRANS, COLOR)
		v.drawScaled((273+XSHAKE)<<16, (-2+YSHAKE)<<16, SCALE, v.cachePatch("XSDSSRGR"), V_SNAPTORIGHT|TRANS, COLOR)
		v.drawScaled((-1+XSHAKE)<<16, (166+YSHAKE)<<16, SCALE, v.cachePatch("XSDSSRGB"), V_SNAPTOBOTTOM|TRANS, COLOR)
	end
end

--Button inputs for FreeFlight
local function FreeFlightPrompts(v, p,s,xs, RB)
	if RB.painstun and (RB.painstun>8) --In pain, can't display buttons rn!
	or xs.ChaosLambent and (xs.ChaosLambent.ChaosControl==true or xs.ChaosLambent.struggle)
		return --
	end
	local XOFF,YOFF,SCALE,TRANS = 0,0,FRACUNIT/5,0
	local FLAGS = V_PERPLAYER|V_SNAPTOBOTTOM|V_SNAPTOLEFT
	local INIT = RB.init or 0
	if (INIT < 18)
		XOFF = -18+INIT
		TRANS = max($, min(FF_TRANS90, (9-INIT)<<16))
	end
	local X,Y = 1+XOFF,151+YOFF --Starting coordinates. 110
	if (xs.HUDpref!=true) or s.noXSHUDthisstage
		local vHEIGHT = v.height() or 0
		Y = ((vHEIGHT==1080) and 41 or 59)+YOFF --Adjust for vanilla HUD!
		X = $+10+(XOFF) --More XOFF!
	elseif RB.LockOn and RB.LockOn.valid
		Y = $-((v.height()==1080) and 46 or 64) --Move up a bit to not get in the way of the Life Icons during Tenkaichi.
	end
	local OGY = Y
	local XADD,YADD,textY = 20, 12, 5
	local FIRING = (xs.ChaosLambent and xs.ChaosLambent.firing)
	
	if FIRING != true
		local OTRANS = TRANS
		if xs.ChaosLambent and ((xs.ChaosLambent.init or 0) < 24)
			--Aim Up button
			v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT1"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_ROSY))
			v.drawString(X+XADD, Y+textY, "\x82"+"Aim Up", FLAGS|TRANS, "small")	
			
			Y = OGY+YADD
			
			--Aim Down button
			v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT2"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_MINT))
			v.drawString(X+XADD, Y+textY, "\x82"+"Aim Down", FLAGS|TRANS, "small")	
			
			Y = OGY+YADD*2
			
			--Chaos Snap can still be used at this stage!
			v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT4"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_PURPLE))
			v.drawString(X+(XADD-4), Y+textY-1, "/", FLAGS|TRANS, "thin")
			v.drawScaled((X+XADD)<<16, Y<<16, SCALE, v.cachePatch("XSBUTT6"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_PURPLE))
			local TTEXT = (RB.LockOn and RB.LockOn.valid) and "Dodge/Kick" or "Chaos Snap"
			v.drawString(X+(XADD+18), Y+textY, "\x88"+TTEXT, FLAGS|TRANS, "small")	
			
		else
			if xs.snapdivetrick and (xs.snapdivetrick < 12) --Sonic Boom Kick button!
				if not (RB.LockOn and RB.LockOn.valid)
					Y = OGY+YADD
					v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT4"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_AETHER))
					v.drawString(X+(XADD-4), Y+textY-1, "/", FLAGS|TRANS, "thin")
					v.drawScaled((X+XADD)<<16, Y<<16, SCALE, v.cachePatch("XSBUTT2"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_AETHER))
					v.drawString(X+(XADD+18), Y+textY, ((leveltime%2) and "\x80" or "\x88")+"Sonic Boom Kick!", FLAGS|TRANS, "small")
				end
				return --
			end
			local TCM = SKINCOLOR_GREY
			if RB.LockOn and RB.LockOn.valid
				if xs.ChaosLambent and (xs.ChaosLambent.firing or xs.ChaosLambent.struggle)
				or RB.RingBurstChase and (RB.RingBurstChase > 9)
				or RB.explosion and (RB.explosion > 9)
				or RB.crossedarms and (RB.crossedarms>6) and (p.cmd.buttons & BT_CUSTOM3) and not RB.painstun
				or RB.SRAlunge and (RB.SRAlunge>9)
					TCM = nil
				end
			end
			
			if xs.ChaosSnap and (not xs.ChaosSnap.timer or (xs.ChaosSnap.timer > 6))
				TRANS = max($, FF_TRANS70)			
			elseif xs.zipdash and not xs.zipdashslide
				TRANS = max(OTRANS, FF_TRANS60)
			end
			
			if TCM != nil
				--Zip Dash button
				v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT3"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_GOLDENROD))
				if RB.LockOn and RB.LockOn.valid
					v.drawString(X+XADD, Y+textY, "\x82"+"Zipdash Tackle", FLAGS|TRANS, "small")	
				else
					v.drawString(X+XADD, Y+textY, "\x82"+"Zip Dash", FLAGS|TRANS, "small")	
				end
			end
			--Chaos Snap button!
			TRANS = OTRANS
			Y = OGY+YADD
			if xs.ChaosSnap and (xs.ChaosSnap.timer == nil or (xs.ChaosSnap.timer > 6))
				TRANS = max($, FF_TRANS70)		
				OTRANS = TRANS
			end
			v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT4"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_PURPLE))
			v.drawString(X+(XADD-4), Y+textY-1, "/", FLAGS|TRANS, "thin")
			v.drawScaled((X+XADD)<<16, Y<<16, SCALE, v.cachePatch("XSBUTT6"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_PURPLE))
			if (RB.LockOn and RB.LockOn.valid)
				v.drawString(X+(XADD+18), Y+textY, "\x88"+"Dodge/Kick", FLAGS|TRANS, "small")
			else
				v.drawString(X+(XADD+18), Y+textY, "\x88"+"Chaos Snap", FLAGS|TRANS, "small")
			end
			
			if xs.zipdash and not xs.zipdashslide
				TRANS = max($, FF_TRANS60)	
			end
			
			Y = OGY+(YADD*2)
			
			if TCM != nil --Chaos Lambent button!
				v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT1"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_RED))
				v.drawString(X+(XADD-4), Y+textY-2, "+", FLAGS|TRANS, "thin")	
				v.drawScaled((X+XADD)<<16, Y<<16, SCALE, v.cachePatch("XSBUTT2"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_FOREST))
				
				v.drawString(X+(XADD+18), Y+textY, "\x87"+"Chaos Lambent", FLAGS|TRANS, "small")
				if not (RB.LockOn and RB.LockOn.valid)
					local RTRANS = (max(2<<16, TRANS))|V_ADD --Ring cost.
					v.drawScaled((X-9+XADD*5)<<16, (Y+textY+4)<<16, 9000, v.cachePatch("RINGA0"), FLAGS|RTRANS )
					v.drawString(X-6+(XADD*5), Y+textY, "x5", FLAGS|RTRANS|V_ALLOWLOWERCASE, "small")
				end
			end
			
			--Cancel Flight!
			TRANS = OTRANS
			Y = OGY+(YADD*3)
			
			if TCM != nil --Chaos Lambent button!
				if RB.LockOn and RB.LockOn.valid
					if (RB.PvPENERGY>4850)
						if (RB.PvPENERGY>9850) and xs.ChaosLambent and ((xs.ChaosLambent.init or 0) < 17)
							local TC = (leveltime%3==0) and "\x84" or (leveltime%2==0) and "\x80" or "\x88"
							v.drawString(X+XADD, Y+textY, TC+"Chaos Control!", FLAGS|TRANS, "small")
							TCM = SKINCOLOR_BLUE
						else
							v.drawString(X+XADD, Y+textY, "\x80"+"Chaos Blast(Tap)", FLAGS|TRANS, "small")
							if (RB.PvPENERGY==10000) and not RB.charging
								v.drawString(X+XADD-3, Y+textY+7, "\x86"+"/ Taunt (Hold)", FLAGS|TRANS, "small")
							else
								local TC = (not RB.charging) and "\x80" or (leveltime%2) and "\x8a" or "\x8b"
								v.drawString(X+XADD-3, Y+textY+7, "\x80"+"/ "+TC+"Charge(Hold)", FLAGS|TRANS, "small")
							end
							TCM = SKINCOLOR_SILVER --Chaos BLAST!!
						end
					elseif p.XS_chargehaxx or RB.cancharge or RB.charging
						local TC = (not RB.charging) and "\x80" or (leveltime%2) and "\x8a" or "\x8b"
						v.drawString(X+XADD, Y+textY, TC+"Charge(Hold)", FLAGS|TRANS, "small")
					else
						v.drawString(X+XADD, Y+textY, "\x86"+"Taunt", FLAGS|TRANS, "small")
					end
				else
					v.drawString(X+XADD, Y+textY, "\x86"+"Toggle FreeFlight", FLAGS|TRANS, "small")
				end
				--Now draw the button
				v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT5"), FLAGS|TRANS, v.getColormap(nil, TCM))
			end
		end
	else
		if xs.ChaosLambent and xs.ChaosLambent.init
			TRANS = max($, min(FF_TRANS90, ((xs.ChaosLambent.init-10)/2)<<16))
		end
		v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT1"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_ROSY))
		v.drawString(X+XADD, Y+textY, "\x82"+"Aim Up", FLAGS|TRANS, "small")	
			
		Y = OGY+YADD
		
		v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSBUTT2"), FLAGS|TRANS, v.getColormap(nil, SKINCOLOR_MINT))
		v.drawString(X+XADD, Y+textY, "\x82"+"Aim Down", FLAGS|TRANS, "small")	
	end
end


--Overkill/Damage text and button prompts.
local function AssaultHUD(v, p,s,xs, ASSAULT, BAG)	
	local OVERKILL = ASSAULT.overkill or ASSAULT.damage if OVERKILL == nil return end --
	local SST = (xs.FreeFlight) and true or false
	local TEXTPATCH = v.cachePatch(((ASSAULT.overkill) and not (SST)) and "XSDSSOK" or "XSDSSDM")
	local X,Y = 146,160
	local XSHAKE = (leveltime%2) and 1 or -1
	local YSHAKE = (leveltime%4==0) and 2 or (leveltime%3==0) and 1 or (leveltime%2==0) and -1 or 0
	local SHOUT = 0
	
	local OKSCALE = FRACUNIT/7
	local CM,CM2,CM3 = SKINCOLOR_SUPERSILVER1, SKINCOLOR_PUREBLACK, (ASSAULT.overkill) and SKINCOLOR_GOLDENROD or SKINCOLOR_SUPERSILVER1
	if ASSAULT.overkill and (leveltime%2==0)
		if (leveltime%4==0)
			CM3 = SKINCOLOR_ICY
		end
		if (SST)
			CM = (leveltime%4==0) and SKINCOLOR_FLAME or SKINCOLOR_RED
		else
			CM = (OVERKILL>5) and SKINCOLOR_MIDNIGHT or (OVERKILL>4) and SKINCOLOR_RUBY or (OVERKILL>1) and SKINCOLOR_PURPLE or SKINCOLOR_SUPERGOLD2
		end
		if (OVERKILL>5)
			CM2 = SKINCOLOR_NOBLE
		end
	end
	CM,CM2,CM3 = v.getColormap(nil, CM), v.getColormap(nil, CM2), v.getColormap(nil, CM3)
	
	local MULT = max(1, min(3, OVERKILL-1))
	XSHAKE = ($==0) and -1 or $*MULT
	YSHAKE = ($==2) and 2+MULT or $*MULT
	X,Y = $+XSHAKE, $+YSHAKE --Add shake to the coordinates.
	SHOUT = max(0, OVERKILL-5)
	local HS = (BAG and BAG.HUDhit) and BAG.HUDhit*2500 or 0
	
	--First, draw the enemy sprite!
	if not (leveltime%3==0) and not xs.FreeFlight --Flicker a little!
		local SPRITE = sprnames[BAG.sprite]
		if SPRITE --Render enemy sprite...? I dunno why I'm  doing that, tbh.
			local PATCH = (v.patchExists(SPRITE+"A1")) and SPRITE+"A1" or (v.patchExists(SPRITE+"A0")) and SPRITE+"A0" or nil
			if PATCH
				local TRANS = (OVERKILL) and (OVERKILL*2/3)*FF_TRANS10 or 0
				local COLOR = (BAG.target and BAG.target.valid and BAG.target.color) and v.getColormap(nil, BAG.target.color) or nil
				v.drawScaled((115+XSHAKE)<<16, (168+YSHAKE)<<16-(HS*11), (FRACUNIT/6)+HS, v.cachePatch(PATCH), V_PERPLAYER|TRANS, COLOR)
			end
		end
	end	
	
	v.drawScaled((X-20)<<16, (Y-2)<<16, OKSCALE, TEXTPATCH, V_PERPLAYER, CM2) --The overkill/damage text.
	v.drawScaled((X-21)<<16, (Y-3)<<16, OKSCALE, TEXTPATCH, V_PERPLAYER, CM)
	if (SST)
		if BAG and BAG.BAGPdamage
			OVERKILL = min(240, max(4, BAG.BAGPdamage/3675))
		elseif OVERKILL
			OVERKILL = min(240, $*18)
		end
	end
	local TABLE,PATCH = GSSeperateChars(OVERKILL) --,STAGE
	if TABLE != nil
		local EX,PATCH = (HS) and X+25 or X+33, nil
		OKSCALE = $*6/5
		local OGY = Y
		Y = (HS) and $-4 or $
		for TEXT = 1,#TABLE --Use X Sonic Font for the numbers.
			if TABLE[TEXT]==nil continue end
			PATCH = "XSDSNU"+TABLE[TEXT]
			for d = 0,1
				v.drawScaled((EX-d)<<16-(HS*3),(Y-9-d)<<16-(HS*16), OKSCALE+HS,
				v.cachePatch(PATCH), V_PERPLAYER, (d!=0 and leveltime%3==0) and CM3 or (d==0) and CM2 or CM)		
			end
			EX = $+10 --Increase X coordinates for the next letter, adding "EXADD" to it.
		end
		if ASSAULT.overkill
			EX = (HS) and $+6 or $
			PATCH = v.cachePatch("XSDSSEX")
			for i = 1, min((SST) and 1 or 25, OVERKILL) --Spam exclamation marks
				for d = 0,1
					v.drawScaled( (EX+4-d)<<16, (Y-4-d)<<16-(1<<15)-(HS*11), OKSCALE+HS, PATCH, V_PERPLAYER, (d==0) and CM2 or CM)	
				end
				EX = $+((HS) and 6 or 4)
			end
		end
		Y = OGY
	end
	if xs.tutorials == "all" and ASSAULT
		local ADD = (BAG and BAG.HUDhit) and min(9, BAG.HUDhit*2)<<16 or 0
		local SCALE,TRANS = 21000,ADD
		local FLAGS = V_PERPLAYER|V_SNAPTOBOTTOM
		local CHARGE = 0
		if not (BAG and BAG.JustGotHit)
			XSHAKE,YSHAKE = 0,0 --Test, see if it makes things more readable while keeping the style.
		end		
		XSHAKE = $-((HS) and 3 or 0)
		Y = 164+YSHAKE-((HS) and 3 or 0)
		
		CM3 = (leveltime%3==0) and CM or (leveltime%2==0) and CM2 or v.getColormap(nil, SKINCOLOR_CRIMSON)
		
		if ASSAULT.type != "groundslam"
			local CM4 = v.getColormap(nil, SKINCOLOR_FOREST)
			if ASSAULT.charged and (ASSAULT.charged > 47) and xs.darkphase != 1 and not ASSAULT.cantcharge
				CHARGE = (ASSAULT.charged < 70) and 2 or 1
				CM,CM2,CM3 = v.getColormap(nil, SKINCOLOR_ICY),v.getColormap(nil, SKINCOLOR_ICY),v.getColormap(nil, SKINCOLOR_ICY)
				CM4 = CM3
			else
				if BAG and BAG.JustGotHit
					CM4 = CM3
				end
			end
			if not ASSAULT.combolock and BAG and not BAG.BAGcutcombo	
			--	local OGTRANS = TRANS
			--	local OGCM4 = CM4
				for i = 2,1,-1 --Show upper and straight buttons!
					if i != 2
						if ASSAULT.type == "flurry" continue end --Upper!
						TRANS = min(FF_TRANS90, $+((ASSAULT.jumped) and FF_TRANS80 or 0))	
						if CM4!=CM3 CM4 = v.getColormap(nil, SKINCOLOR_RUBY) end
						X = 204+((CHARGE!=2) and XSHAKE or 0)
						v.drawScaled(X<<16-HS*9, (Y<<16)-HS*11, SCALE+HS, v.cachePatch("XSBUTT1"), FLAGS|TRANS, CM4)
					else --Straight!		
						X = 100+((CHARGE!=2) and XSHAKE or 0)
						v.drawScaled(X<<16-HS*9, (Y<<16)-HS*11, SCALE+HS, v.cachePatch("XSBUTT2"), FLAGS|TRANS, CM4)
					end
				end
			
				X = 130+XSHAKE
				if CHARGE==2
					v.drawString(X, Y+7, "\x88"+"Hyper"+"\n"+"Smash!!", FLAGS, "small")
					v.drawString(X+41, Y+7, "\x88"+"Hyper"+"\n"+"Smash!!", FLAGS, "small") --Hyper Meteor!
				elseif CHARGE==1
					v.drawString(X, Y+8, "\x8a"+"Smash!", FLAGS, "small")
					v.drawString(X+41, Y+8, "\x8a"+"Smash!", FLAGS, "small") --Meteor!				
				else
					v.drawString(X, Y+9, ((ADD) and "\x80" or "\x8b")+"Straight", FLAGS|ADD, "small")	
					if ASSAULT.type == "attack"
						v.drawString(X+41, Y+9, ((ADD) and "\x80" or "\x87")+"Upper", FLAGS|TRANS, "small")
					end
				end
			end
		end
		
		FLAGS = $|V_SNAPTOLEFT
		X,Y = 1, 147-((modeattacking) and 45 or 1)
		for i = 3,4
			if i == 3
				v.drawScaled(X<<16, (Y-8)<<16, SCALE, v.cachePatch("XSBUTT3"), FLAGS, v.getColormap(nil, SKINCOLOR_GREY))			
				v.drawString(X+25, Y, "\x86"+"Cancel", FLAGS|min(ADD, 2<<16), "small")
			else
				if ASSAULT.type == "groundslam"
					if s.player and s.player.xsonictable and s.player.xsonictable.darkphase==3
						v.drawScaled((X+XSHAKE)<<16, (Y-8)<<16, SCALE+4000, v.cachePatch("XSBUTT4"), FLAGS, v.getColormap(nil, SKINCOLOR_CRIMSON))	
						v.drawString(X+30+XSHAKE, Y+3+YSHAKE, "\x8f"+"EarthShaker", FLAGS, "small")
					else
						v.drawScaled(X<<16, (Y-8)<<16, SCALE, v.cachePatch("XSBUTT4"), FLAGS, v.getColormap(nil, SKINCOLOR_RUST))	
						v.drawString(X+25, Y, "\x87"+"Punt Kick", FLAGS, "small")					
					end
				else
					v.drawScaled(X<<16, (Y-8)<<16, SCALE, v.cachePatch("XSBUTT4"), FLAGS, v.getColormap(nil, SKINCOLOR_NOBLE))	
					v.drawString(X+25, Y, "\x89"+"GroundSlam", FLAGS, "small")
				end
			end
			X = $+1
			Y = $+17
		end
	end	
end

--Custom 3 context HUD.
local function C3_Context(v, p,s,xs, C3)
	local X,Y,YOFF = 115,120,0
	local TRANS = max(10-C3.TIMER, 1)
	local ALIVE = C3.ALIVE or 0
	if (ALIVE < 8)
		TRANS = max($, 10-C3.ALIVE)
	end
	local FLAGS, CM = V_PERPLAYER|V_SNAPTOBOTTOM|(min(8, TRANS)<<16)
	local CM = v.getColormap(nil, SKINCOLOR_GOLDENROD)
	
	local BUTTON = C3.JUMP
	local BFLAGS,TRANSMASK = FLAGS, (FLAGS & FF_TRANSMASK) 
	for i = 1,3 --Buttons, progress bar, and text.
		if i == 1
			if p.powers[pw_super] or xs.darkphase
				YOFF = $+24 continue --
			end
		elseif i == 2 
			if p.powers[pw_super] or xs.darkphase --Revert!
				CM = v.getColormap(nil, SKINCOLOR_CORNFLOWER)
			end
			BUTTON = C3.SPIN 
		elseif i == 3 
			if (marathonmode) continue end --Not in Time-Attack.
			BUTTON = max(C3.CUSTOM1, C3.CUSTOM2)
		end
		local BSCALE = 27500
		if BUTTON and not (ALIVE < 8)
			if not (i==1 and (emeralds!=127 or (p.rings<100)))
			and not (i==2 and (XS_FAKEEMERALDS!=127 or (p.rings<50)) and not xs.darkphase and not p.powers[pw_super])
				BFLAGS = (FLAGS & ~FF_TRANSMASK)|V_PERPLAYER
				
				local BTO = (i==3) and BUTTON*4 or BUTTON*8
				if i==2 and xs.supersonic --debug test
					BTO,BUTTON = $/2,$/2
				end
				v.drawFill(X, Y+YOFF, 2+BTO, 15, 100|FLAGS) --Underside.
				v.drawFill(X, Y+YOFF+6, 2+BTO, 9, 101|FLAGS)
				v.drawFill(X, Y+YOFF+10, 2+BTO, 5, 102|FLAGS)
				
				v.drawFill(X, Y+YOFF, 2+BTO, 2, 96|FLAGS) --Above?
				v.drawFill(X, Y+YOFF+13, 2+BTO, 2, 107|FLAGS)
				v.drawFill(X, Y+YOFF, 2, 13, 96|FLAGS)
				if (BUTTON < 12)
					v.drawScaled(((X-2)+BTO)<<16, (Y+YOFF+29-C3.RNG)<<16, 40055, v.cachePatch("SSPKA0"), (FLAGS+FF_TRANS10)|V_ADD|V_PERPLAYER)
					v.drawScaled(((X+2)+BTO)<<16, (Y+YOFF+20+C3.RNG)<<16, 55355, v.cachePatch("SSPKA0"), (FLAGS+FF_TRANS10)|V_ADD|V_PERPLAYER)
				end
			end
		else
			BSCALE = 24000
			BFLAGS = (FLAGS & ~FF_TRANSMASK)|(max(TRANSMASK, FF_TRANS10))|V_PERPLAYER
		end

		--Bar setup done, now buttons & text!
		if i == 1 --Super
			local TEMPTRANS = (BFLAGS & FF_TRANSMASK)
			local SUPTRANS = max(TEMPTRANS, 1<<16)
			if (p.rings<100) or emeralds != 127
				v.drawScaled( (X-22)<<16, (Y-6+YOFF)<<16, BSCALE, v.cachePatch("XSBUTT1"), 
				(BFLAGS&~FF_TRANSMASK) | (max(TEMPTRANS, FF_TRANS50)) , v.getColormap(nil, SKINCOLOR_FLAME))
				
				v.drawScaled((X+12)<<16,(Y-4+YOFF)<<16, BSCALE/3, v.cachePatch("XSCMSUP2"), 
				(BFLAGS & ~FF_TRANSMASK) | max(TEMPTRANS, FF_TRANS60), v.getColormap(nil, SKINCOLOR_ORANGE))				
				SUPTRANS = max($, 8<<16)
			else
				v.drawScaled((X-22)<<16,(Y-6+YOFF)<<16,BSCALE, v.cachePatch("XSBUTT1"), BFLAGS, v.getColormap(nil, SKINCOLOR_FLAME)) --Button!
				v.drawScaled((X+12)<<16,(Y-2+YOFF)<<16,BSCALE/3,v.cachePatch("XSCMSUP2"),(BFLAGS&~FF_TRANSMASK)|max(TEMPTRANS, FF_TRANS10),v.getColormap(nil, SKINCOLOR_VOLCANIC))--Shadow!
				if s
					local PATCH = (leveltime%2) and "NSPKA0" or "NSPKB0"
					local RNGX = XSLocalRNG(8, 84, s) --Imperfect RNG works in our favor this time
					local RNGY = XSLocalRNG(9, 20, s)
					v.drawScaled((X+RNGX)<<16, (Y+RNGY+YOFF)<<16, 1<<16, v.cachePatch(PATCH), SUPFLAGS)
				end
			end
			local SUPFLAGS = (BFLAGS & ~FF_TRANSMASK) | SUPTRANS | V_ADD | V_PERPLAYER
			v.drawScaled((X+11)<<16, (Y-2+YOFF)<<16, (BSCALE/3)*2, v.cachePatch("XSCMSUPE"), SUPFLAGS)
			
			--Ring Requirement.
			if emeralds == 127
				v.drawScaled((X+101)<<16, (Y+12+YOFF)<<16, 20000, v.cachePatch("RINGA0"), SUPFLAGS)
				v.drawString(X+109, Y+YOFF+4, "x 100", SUPFLAGS, "thin")	
			end
				
		elseif i == 2 --DSS or REVERT.	
			local TEMPTRANS = (BFLAGS & FF_TRANSMASK)
			local DX = (X-5)<<16
			local DY = (Y-9+YOFF)<<16	
			if xs.darkphase or p.powers[pw_super] --Simple revert option!
				v.drawScaled((X-22)<<16, (Y-8+YOFF)<<16, BSCALE, v.cachePatch("XSBUTT2"), BFLAGS, v.getColormap(nil, SKINCOLOR_EMERALD))
				for f = 1,0,-1 --Revert!
					local DCM = v.getColormap(nil, (f==1) and SKINCOLOR_PUREBLACK or SKINCOLOR_SUPERSKY2)
					v.drawScaled(DX+((f+13)<<16), DY+((f-2)<<16), BSCALE/3, v.cachePatch("XSCMBASE"), BFLAGS, DCM)
				end
			else
				local XSHAKE,YSHAKE = 0,0
				local DSTR = v.cachePatch("XSCMDSS2")
				local DCM = v.getColormap(nil, SKINCOLOR_PUREBLACK)
				local SHADFLAGS = (BFLAGS & ~FF_TRANSMASK) | (max(TEMPTRANS, 3<<16))
				
				if XS_FAKEEMERALDS==127 and (p.rings>=50) --You sure about this?
					v.drawScaled((X-22)<<16, (Y-6+YOFF)<<16, BSCALE, v.cachePatch("XSBUTT2"), BFLAGS, v.getColormap(nil, SKINCOLOR_FOREST))
					if (leveltime%16==0)
						DCM = v.getColormap(nil, SKINCOLOR_CRIMSON)
						XSHAKE = (leveltime%32==0) and 1 or -1
						YSHAKE = (leveltime%64==0) and 2 or (leveltime%48==0) and 1 or (leveltime%32==0) and -1 or -2
						XSHAKE,YSHAKE = $<<16, $<<16				
						if v.renderer() != "software" 
							local SCALEX = (BSCALE/3)+abs(XSHAKE*48500)
							local SCALEY = max(4000, (BSCALE/3)+YSHAKE*6900) 						
							v.drawStretched(DX-(XSHAKE*5), DY-(YSHAKE*2),SCALEX, SCALEY, v.cachePatch("XSCMDSS2"), (SHADFLAGS&~FF_TRANSMASK)|FF_TRANS30, DCM)
						else --Software once again sucks.
							v.drawScaled(DX-(XSHAKE*5)-(6<<16), DY-(5<<16)-(YSHAKE*2), (BSCALE*2)/5, DSTR, (SHADFLAGS&~FF_TRANSMASK)|FF_TRANS30, DCM)
						end
					end					
					local CFLAGS = (BFLAGS & ~FF_TRANSMASK) | (max(TEMPTRANS, 7<<16)) //| ((not XSHAKE) and V_ADD or 0)
					v.drawScaled(DX+(XSHAKE*2), DY+YSHAKE, BSCALE/3, DSTR, SHADFLAGS, DCM) --Distortion text, now underneath
					v.drawScaled(DX, DY, BSCALE/3, v.cachePatch("XSCMDSS1"), BFLAGS, DCM) --Text
					v.drawScaled(DX, DY, (BSCALE/3)*2, v.cachePatch("XSCMDSS3"), CFLAGS) --Mist overlay

				else --You aren't ready for this
				
					v.drawScaled((X-22)<<16, (Y-6+YOFF)<<16, BSCALE, v.cachePatch("XSBUTT2"), (BFLAGS&~FF_TRANSMASK)|(max(TEMPTRANS, FF_TRANS50)), v.getColormap(nil, SKINCOLOR_FOREST)) 
					local DFLAGS = BFLAGS
					if XS_FAKEEMERALDS != 127 --Completely obscured.
						DFLAGS = (BFLAGS & ~FF_TRANSMASK)| (max(FF_TRANS80, TEMPTRANS))
						SHADFLAGS = ($ & ~FF_TRANSMASK)| (max(FF_TRANS60, TEMPTRANS))
					elseif (p.rings < 50) --Slightly obscured.
						DFLAGS = (BFLAGS & ~FF_TRANSMASK)| (max(FF_TRANS50, TEMPTRANS))
						SHADFLAGS = ($ & ~FF_TRANSMASK)| (max(FF_TRANS40, TEMPTRANS))
					end
					local CFLAGS = (BFLAGS & ~FF_TRANSMASK) | (max(TEMPTRANS, 4<<16))| V_ADD
					v.drawScaled(DX-XSHAKE, DY-YSHAKE, BSCALE/3, v.cachePatch("XSCMDSS1"), DFLAGS, DCM) --Text
					if (leveltime%32==0)
						DCM = v.getColormap(nil, SKINCOLOR_MIDNIGHT)
						XSHAKE = (leveltime%64==0) and 1 or -1
						YSHAKE = (leveltime%128==0) and 2 or (leveltime%96==0) and 1 or (leveltime%64==0) and -1 or -2
						XSHAKE,YSHAKE = $<<16, $<<16
						if v.renderer() != "software" 
							local SCALEX = (BSCALE/3)+abs(XSHAKE*11500)
							local SCALEY = max(5000, (BSCALE/3)+YSHAKE*4000) 					
							v.drawStretched(DX-(XSHAKE*4), DY-YSHAKE, SCALEX, SCALEY, DSTR, (SHADFLAGS&~FF_TRANSMASK)|FF_TRANS30, DCM)	
						else --Software once again sucks, part 2.
							v.drawScaled(DX-(XSHAKE*4)-(4<<16), DY-YSHAKE-(2<<16), (BSCALE*4)/11, DSTR, (SHADFLAGS&~FF_TRANSMASK)|FF_TRANS40, DCM)
						end																	
					end										
					v.drawScaled(DX+XSHAKE, DY+YSHAKE, BSCALE/3, v.cachePatch("XSCMDSS2"), SHADFLAGS, DCM) --Distortion text
					v.drawScaled(DX, DY, (BSCALE/3)*2, v.cachePatch("XSCMDSS3"), CFLAGS) --Mist overlay
				end
				if XS_FAKEEMERALDS == 127 --Ring Requirements.
					local RFLAGS = (BFLAGS&~FF_TRANSMASK)
					if (p.rings < 50)
						RFLAGS = $|(max(TEMPTRANS, FF_TRANS80))
					else
						RFLAGS = $|V_ADD|(max(TEMPTRANS, FF_TRANS10))
					end				
					v.drawScaled((X+101)<<16, (Y+12+YOFF)<<16, 20000, v.cachePatch("RINGA0"), RFLAGS)
					v.drawString(X+109, Y+YOFF+4, "x"+" 50", RFLAGS, "thin")		
				end
			end
		elseif i == 3 --Menu
			v.drawScaled((X+12)<<16, (Y+4+YOFF)<<16, BSCALE*3/4, v.cachePatch("XSNAMTAG"), BFLAGS)
			for f = 1,0,-1
				v.drawScaled((X+51+f)<<16, (Y+2+f+YOFF)<<16, BSCALE/3, v.cachePatch("XSCMMEN"), BFLAGS, 
				(f==1) and v.getColormap(nil, SKINCOLOR_PUREBLACK) or v.getColormap(nil, SKINCOLOR_SUPERSKY5))
			end
			CM = v.getColormap(nil, SKINCOLOR_GOLDENROD)
			v.drawScaled((X-32)<<16, (Y-9+YOFF)<<16, BSCALE-2000, v.cachePatch("XSBUTT4"), BFLAGS, CM) --Extra button for menu.(C2)
			v.drawScaled((X-22)<<16, (Y-6+YOFF)<<16, BSCALE, v.cachePatch("XSBUTT3"), BFLAGS, CM)
		end			
		YOFF = $+24
	end
end

--Render portraits for Super Sonic PvP!
local function PvPPortrait(v, p,s,xs, RB, OPPONENT)
	local X,Y = 1,154
	local X2,Y2 = 0,0 --Shake coordinates. Only apply to portrait, not background or spike.
	local VFLAGS = V_SNAPTOBOTTOM|V_SNAPTOLEFT
	local COLOR = SKINCOLOR_ICY --Background color
	local SPIKECOLOR = SKINCOLOR_PUREBLACK 
	local SCALE = FRACUNIT/8
	local HEALTH = (RB.PvPHEALTH!=nil) and RB.PvPHEALTH or 10000 --10000 is full PvP health.
	local TRANS,FLIP,SPIKEADD,LAMBENTFLASH,PAINFLASH = 0,0,0,0,0
	
	if OPPONENT != nil --This is the opponent, so change some flags!
		X,Y = 319,5
		FLIP = V_FLIP
		VFLAGS = V_SNAPTORIGHT|V_SNAPTOTOP
	elseif (xs.HUDpref!=true) and (p==consoleplayer) and hud.enabled("lives")
		Y = $-11
	end
	if (HEALTH < 9500) --Change background color depending on player health.
		local CTABLE = {SKINCOLOR_VOLCANIC, SKINCOLOR_CRIMSON, SKINCOLOR_PEPPER, SKINCOLOR_ORANGE, SKINCOLOR_APRICOT,
		SKINCOLOR_GOLDENROD, SKINCOLOR_LEMON, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_ICY}
		COLOR = CTABLE[min(#CTABLE, (HEALTH/1000)+1)] --Change background color depending on player health.
	end
	
	local FACE = "XSPVPF1"
	if xs.ChaosLambent
		local INIT = xs.ChaosLambent.init or 0
		FACE = "XSPVPF3"
		if xs.ChaosLambent.firing
			X2 = (leveltime%3==0) and -1 or (leveltime%2==0) and 1 or 0
			Y2 = (leveltime%2) and 1 or -1
			COLOR = (leveltime%2) and SKINCOLOR_SUPERSILVER1 or SKINCOLOR_GOLD
			SPIKECOLOR = (leveltime%2) and SKINCOLOR_GOLDENROD or SKINCOLOR_FLAME
			SPIKEADD = V_ADD|FF_TRANS10
		elseif (INIT < 14)
		
			local CTABLE = {SKINCOLOR_BLACK, SKINCOLOR_VOLCANIC, SKINCOLOR_CRIMSON, SKINCOLOR_RED, SKINCOLOR_COPPER,SKINCOLOR_ORANGE, 
			SKINCOLOR_APRICOT, SKINCOLOR_TOPAZ, SKINCOLOR_GOLDENROD, SKINCOLOR_SUPERGOLD3, SKINCOLOR_SUPERGOLD2, SKINCOLOR_SUPERGOLD1, SKINCOLOR_SUPERSILVER1}
			
			SPIKECOLOR = CTABLE[INIT]
			LAMBENTFLASH = max(1, min(9,INIT-3))<<16
			if (INIT > 4)
				COLOR = SPIKECOLOR
				SPIKEADD = V_ADD|FF_TRANS10
			end
		end
		if xs.ChaosLambent.struggle and xs.FreeFlight
			local t = xs.FreeFlight.LockOn
			local tp = t and t.player
			local xss = tp and tp.xsonictable
			local CLL = xss and xss.ChaosLambent
			if CLL
				BeamClashHUD(v, p,s,xs,xs.ChaosLambent, tp,t,xss,CLL)
			end
		end
	else
		if p.powers[pw_flashing] and (s.flags2 & MF2_DONTDRAW)
			return --
		end
		if RB.painstun
			FACE = "XSPVPF2"
			if RB.painflash and (RB.painstun > 2) and RB.painstun != FRACUNIT
				PAINFLASH = max(1, (19-RB.painflash)/2)<<16
				SPIKECOLOR = (RB.painflash%2) and SKINCOLOR_VOLCANIC or SKINCOLOR_MIDNIGHT
				SPIKEADD = 0
				if (RB.painflash > 9)
					X2 = (leveltime%3==0) and -1 or (leveltime%2==0) and 1 or 0 
					Y2 = (RB.painflash%2) and -1 or 1
				/*	if (leveltime%2)
						COLOR = (HEALTH<2500) and SKINCOLOR_BLACK or (HEALTH<5000) and SKINCOLOR_MIDNIGHT or SKINCOLOR_RED
					else
						COLOR = (HEALTH<2500) and SKINCOLOR_RED or (HEALTH<5000) and SKINCOLOR_FLAME or SKINCOLOR_BLACK
					end */ 				
				end
			end
		else
			if RB.DodgeFrames and ((RB.DodgeFrames > 2) or xs.ChaosSnap) --Show when the opponent is intangible.
				if (RB.DodgeFrames > 9)
					TRANS = (RB.DodgeFrames>13) and FF_TRANS40 or (RB.DodgeFrames>11) and FF_TRANS60 or FF_TRANS80
				else
					TRANS = RB.DodgeFrames<<16 --Make portrait transparent when the player has i-frames.
				end
				if (RB.DodgeFrames > 2)
					COLOR = SKINCOLOR_AETHER
				end
				SPIKECOLOR = SKINCOLOR_SLATE
			end
		end	
	end
	
	--Colored background
	v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSPVPBG1"), VFLAGS|FLIP|TRANS, v.getColormap(nil, COLOR))
	
	--Super Sonic Portrait
	if (SPIKECOLOR == SKINCOLOR_PUREBLACK) or (FACE != "XSPVPF1")
		v.drawScaled((X+X2)<<16, (Y+Y2)<<16, SCALE, v.cachePatch(FACE), VFLAGS|FLIP|TRANS)
	else
		v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch(FACE), VFLAGS|FLIP|FF_TRANS80) --Disappearing act.
		v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSPVPF1O"), VFLAGS|FLIP|min(FF_TRANS90, TRANS+FF_TRANS20), v.getColormap(nil, COLOR))
	end
	if LAMBENTFLASH
		v.drawScaled((X+X2)<<16, (Y+Y2)<<16, SCALE, v.cachePatch("XSPVPF3O"), VFLAGS|FLIP|LAMBENTFLASH, v.getColormap(nil, SKINCOLOR_SUPERGOLD3))
	elseif PAINFLASH
		v.drawScaled((X+X2)<<16, (Y+Y2)<<16, SCALE, v.cachePatch("XSPVPF2O"), VFLAGS|FLIP|PAINFLASH, v.getColormap(nil, SKINCOLOR_RED))
	end
	
	--Portrait aura
	if RB.auratrans != nil	
		local MIN = (FACE=="XSPVPF1") and 3<<16 or 0
		local AURATRANS = max(MIN, ((RB.auratrans/3)+1)<<16)+TRANS
		v.drawScaled((X+X2)<<16, (Y+Y2)<<16, SCALE, v.cachePatch(FACE+"B"), VFLAGS|FLIP|V_ADD|(min(FF_TRANS90, AURATRANS)) )
	end
	
	--Spiky Sonic X-style thing
	v.drawScaled(X<<16, Y<<16, SCALE, v.cachePatch("XSPVPBG2"), VFLAGS|FLIP|TRANS|SPIKEADD, v.getColormap(nil, SPIKECOLOR))	
	if not p.XS_chargehaxx and RB.cancharge
		local PATCH = v.cachePatch(
		( "XSG1" + R_Frame2Char( O+XSon.TimeFrame(2) ) + "0" ) --Goes from I to Q. We only use O to Q
		)
		local AX = X+X2 + ((OPPONENT) and -49 or 56) 
		local AY = Y+Y2 + ((OPPONENT) and 7 or 26)
		for i = 2,4,2
			v.drawScaled(AX<<16, AY<<16, SCALE/i, PATCH, V_ADD|VFLAGS|FLIP|min(FF_TRANS90, TRANS+2<<16) )
		end
	end
end

--Autocorrects X offsets since I develop HUD primarily for 1080p
local function hudX(v, FRAC)
	local WIDTH,HEIGHT = v.width(),v.height()
	if HEIGHT == 1080 or WIDTH == 1152 return 0 end --Standard 1080p or that random render that conveniently works.
	if FRAC == nil FRAC = 1 end

	if WIDTH == 1024
		return 18*FRAC --440x900	
	elseif WIDTH == 1440
		return 9*FRAC --1440x900
	elseif WIDTH == 1680
		return 23*FRAC --1680x1050		
	else
		if WIDTH == 800
			return -6*FRAC --800x600
		elseif WIDTH == 1600 and HEIGHT == 900 
			return -12*FRAC --1600x900	
		elseif WIDTH == 1366
			return -34*FRAC --1366x768
		elseif HEIGHT == 720
			return -20*FRAC --1280x720
		end
		return 29*FRAC --All other resolutions
	end
end

--The menu card and background, all for decoration!
--XSHUDM0 is the base card. XSHUDMB is the back of the card.
--XSHUDM1-4 are the card images for the front, XSHUDF is a colorable frame overlay for glowing effects.
--All card graphics are 501 pixels wide, 703 pixels tall.
--Scaled down to 20%, they'll render out as 100x wide and 140x tall.
local function ShowCard(v, p,s,xs, MENU)

	--Card coordinates and scale.
	local X,Y = 60<<16, 32<<16  --Frame X and Y coordinates.
	local FRAMESCALE = 13071 --It's FRACUNIT/5, exactly 20%.
	
	local IX,IY = X,Y --Image X and Y coordinates.
	local XSCALE,YSCALE = FRAMESCALE,FRAMESCALE --For the image itself!
	
	--Decide which side of the card, and which image to show!
	local CARD,TRANSC,TRANSI = "front",0,0 --Translucency values
	local BOOT,EXIT = MENU.BOOT,MENU.EXIT
	
	if BOOT or EXIT
		local XADD, YADD = (-MENU.EXIT*12)<<16, MENU.EXIT*2<<16
		if (BOOT > 7)
			YADD = $+((BOOT-7)*10<<16)
		end
		X,Y = $+XADD, $+YADD
		
		if BOOT --The menu open animation is playing! The value tics down from 25 to 0.	
			IX,IY = X+(18<<16), Y+(8<<16) --Might as well update them here.
			TRANSC = min(FF_TRANS90, (BOOT*2/3)<<16) --Fade in just a little as well.	
		end
		if EXIT
			TRANSC = min(FF_TRANS90, $+EXIT*FF_TRANS20)	
		end
	end
		
	--Display the card's frame!
	v.drawScaled(X,Y, FRAMESCALE, v.cachePatch("XSHUDM0"), V_SNAPTOLEFT|TRANSC)
	
	if MENU.IMAGEFADE --What's our inner image's translucency?
		TRANSI = (MENU.IMAGEFADE*3/2)<<16
	end
	
	--The outter frame flashes when you change an option.
	if MENU.FLASH
		local GTRANS = (MENU.FLASH>5) and 13-MENU.FLASH or 10-MENU.FLASH
		GTRANS = max(0, min(9, $))<<16
		v.drawScaled(X,Y, FRAMESCALE, v.cachePatch("XSHUDMF"), V_ADD|V_SNAPTOLEFT|GTRANS, v.getColormap(nil, SKINCOLOR_SUPERSILVER1))
	end	
	
	--Display the inner image? Not if translucency is over 90%, ofc.
	if (TRANSI <= FF_TRANS90) --and (BOOT<9)
		if EXIT
			TRANSI = max(FF_TRANS50,$)
			IX,IY = X,Y
			XSCALE,YSCALE = FRAMESCALE,FRAMESCALE	
		elseif MENU.SPINNING != nil --The image inside the card is spinning! Loops from 8 to -7 until it stops at 5.
			local SPIN = abs(MENU.SPINNING)
			
			XSCALE = (SPIN==5) and FRAMESCALE
			or (SPIN==4 or SPIN==6) and FRAMESCALE*2/3
			or (SPIN==3 or SPIN==7) and FRAMESCALE/2
			or (SPIN==2 or MENU.SPINNING==8) and FRAMESCALE/3
			or FRAMESCALE/7	
			
			IX = (SPIN==4 or SPIN==6) and 16<<16
			or (SPIN==3 or SPIN==7) and 25<<16
			or (SPIN==2 or SPIN==8) and 33<<16
			or 44<<16
			if abs(MENU.SPINNING) == 5
				IX = 0 --For some reason I had to specificy this HERE.
			end
			
			if BOOT --Scale into the picture on boot!
				XSCALE = max(1, $-BOOT*975)
				YSCALE = max(1, $-BOOT*1135)
				IX = $+(4*BOOT<<16)
				IY = $+(2*BOOT<<16)
			end
			IX = $+X 
			
			CARD = (MENU.SPINNING<0) and "back" or "front" --Show front or back of the card?
		end	
		local IFLAGS = ((TRANSI>FF_TRANS10) and (CARD != "back")) and V_SNAPTOLEFT|V_ADD or V_SNAPTOLEFT
		local IMAGE = v.cachePatch( ((CARD=="back") and "XSHUDMB" or MENU.IMAGE) ) --Show the back or front?
		v.drawStretched(IX, IY, XSCALE, YSCALE, IMAGE, IFLAGS|TRANSI)
	end	
	
	--Lastly, a glowing overlay for the outter frame!
/*	if not MENU.BOOT
		local GTRANS = (max(6, MENU.OVERLAY)<<16)+TRANSI 
		if (GTRANS <= FF_TRANS90) --MENU.OVERLAY goes from 14 to 3, then 3 to 14 again in a loop.
			for i = 1,3
				v.drawScaled(X,Y, FRAMESCALE, v.cachePatch("XSHUDMF"), V_ADD|V_SNAPTOLEFT|(GTRANS<<16), v.getColormap(nil, SKINCOLOR_SUPERSILVER1))
				SCALE = $+2200
				X,Y = $-65000, $+65000
				GTRANS = min(FF_TRANS90, $+FF_TRANS20)
			end
		end
	end */	
	
	--Sonic image popin when you select colors!
	local POPIN = MENU.POPIN
	if POPIN != nil 
	
	--	v.drawString(222, 14, "\x84 POPIN: "+(-POPIN)+" xs.SPcolor:"+xs.SPcolor, FF_TRANS20, "thin-center")
	
		local FLIP = 24<<16
		local STRETCH = MENU.POPINSTRETCH or 0
		local PXSCALE = 25000
		local PYSCALE = PXSCALE-(STRETCH*1600)
		local PFLAGS = 0
		local VFLIP = 0
		
		local type = "XSHUDP4"
		if xs.darkphase or p.powers[pw_super]
			if xs.darkphase
				type = "XSHUDP2" --The only one using only a single graphic
			else
				if (POPIN < 0)
					type = "XSHUDP3"
					PFLAGS = $|V_FLIP
					VFLIP = V_FLIP
					X = $+66<<16
				else
					type = "XSHUDP1"
				end
				FLIP = -$
			end
		elseif POPIN != 0 or MENU.SELECT != 2
			type = "XSHUDP3" --Running pose. Uses 4 when he comes to a stop normally.
		end

		--Adjust coordinates!
		X = $+ hudX(v,1<<16) - 24<<16
		Y = $+ (40+(STRETCH*5))<<16
		if (type=="XSHUDP1") --slide pose
			PXSCALE,PYSCALE = $-4500,$-4500
			X = $- (9+(POPIN*FLIP*6))<<16
			Y = $- 10<<16
		elseif (type=="XSHUDP2") --Jump Pose
			X = $- 6<<16
			Y = $- 11<<16
		end
		if (POPIN != 0) --Change stretch and mobile coordinates!
			if (POPIN*FLIP < 0) --Going to the right!
				X = $-(POPIN*FLIP*2)
				PFLAGS = $+min(FF_TRANS70, (abs(POPIN/2)<<16))
			else --Coming from the left!
				X = $-POPIN*FLIP		
			end
			if (type=="XSHUDP2") --darkphase jump pose
				PXSCALE = $-(MENU.POPIN*1250)
				PYSCALE = $-(MENU.POPIN*1250)
				Y = $+(POPIN*11)<<16
			else
				PXSCALE = $+abs(MENU.POPIN*900)
				PYSCALE = $-abs(MENU.POPIN*600)			
			end
		end
		local CM = v.getColormap(nil, xs.SPcolor or 4)
		local PATCH = v.cachePatch(type)
		v.drawStretched(X, Y, PXSCALE, PYSCALE, PATCH, PFLAGS, CM) --Now draw Sonic!
		
		--Add blur after effects?
		if not MENU.EXIT and POPIN!=0
			if type=="XSHUDP1"
				v.drawStretched(X, Y, PXSCALE+2500, PYSCALE,  v.cachePatch("XSHUDP0"), VFLIP|V_ADD|FF_TRANS20, CM)
			elseif (POPIN < 0)
				if VFLIP != 0
					for i = -1,-4,-1
						v.drawStretched(X+((POPIN*-9*i)+64)<<16, Y, PXSCALE-(1200*i), PYSCALE+(750*i), PATCH, VFLIP|V_ADD|((10+i*2)<<16), CM)
					end
				else
					for i = 1,2
						v.drawStretched(X+(POPIN*7*i)<<16, Y, PXSCALE+(1500*i), PYSCALE-(500*i), PATCH, V_ADD|((5+i*2)<<16), CM)
					end					
				end
			end
		end
	end
end

--Display the X Sonic Menu!
local function XSonicMenu(v, p,s,xs, MENU)

	ShowCard(v, p,s,xs, MENU) --Display the menu card and other such fluff.
	
	local BOOT = MENU.BOOT
	local FADE = MENU.FADE and min(FF_TRANS90, (MENU.FADE/3)<<16) or 0 --If the menu's currently fading in, add translucency.
	
	local SELECT = MENU.SELECT or 0 --Which options did we select?
	local rX = hudX(v)*2
	local X,Y = 210+rX,40-(BOOT*2)+(MENU.EXIT*5) --Where to place the text.
	
	local YSPACING = 15 --How far to space out vertical lines.
	
	--Text for the menu selections.
	local TEXT = {
	[1] = "Custom HUD",
	[2] = "SP Color",
	[3] = "Sonic Voice", --"Disable BlueBlur"
	[4] = "Tutorials",
	[5] = "Ringburst SFX",
	[6] = "Coop PvP",
	[7] = "Accessibility"
	}
	if MENU.TIMEATTACK==true
		TEXT[(#TEXT+1)] = "Super TimeAttack"
	end
	if MENU.ADMIN==true
		TEXT[(#TEXT+1)] = "Stupid Memoryleak Fix"
		if ((#TEXT)>8)
			Y = $-5
		end
	end	
	if TEXT[SELECT] --Highlight the selected text as yellow.
		TEXT[SELECT] = "\x82"+$
	end
	
	--What each option is currently set to.
	local OPTIONS = {
	[1] = (xs.HUDpref==true) and "\x83"+"On" or "\x85"+"Off",
	[2] = (tonumber(xs.SPcolor)) and "\x80"+R_GetNameByColor(xs.SPcolor)+" ("+xs.SPcolor+")" or "\x86"+"Auto",
	[3] = (xs.voice) and "\x83"+"On" or "\x85"+"Off",
	[4] = (xs.tutorials=="all") and "\x83"+"all" or (xs.tutorials=="mild") and "\x8b"+"aimline-only" or "\x85"+"none",
	[5] = (xs.FourKidsRB) and "\x87"+"4Kids" or "\x8c"+"Original",
	[6] = (xs.coopPvP) and "\x83"+"On" or "\x85"+"Off",
	[7] = (xs.ACCE) and "\x81"+xs.ACCE or "\x83"+"Normal"
	}
	if MENU.TIMEATTACK==true
		OPTIONS[(#OPTIONS+1)] = 
		(xs.SuperTimeAttack=="AllEmeralds") and "\x88"+"Emeralds"
		or (xs.SuperTimeAttack=="Super") and "\x82"+"Super"
		or not (xs.SuperTimeAttack) and "\x85"+"Off"
		or "\x89"+xs.SuperTimeAttack
	end
	if MENU.ADMIN==true
		OPTIONS[(#OPTIONS+1)] = (XS_CRAPPYPRIORITIES==1) and "\x81"+"On" or "\x83"+"Off"
	end	
	
	--Now do all the rendering nonsense.
	for i = 1, (MENU.MAXNUM) do
		if type(TEXT[i]) != "string"
			Y = $+YSPACING continue --Don't show this option!
		end
		local OPTION = OPTIONS[i] or "???"
		local LENGTH = string.len(OPTION)+string.len(TEXT[i])
		if i==SELECT
			v.drawString(X+LENGTH-MENU.SHIFT, Y, TEXT[i]+"\x80 : "+OPTION, FADE|V_SNAPTORIGHT, "thin-right")
		else
			v.drawString(X+LENGTH, Y, TEXT[i]+"\x80 : "+OPTION, min(FF_TRANS90, FADE+FF_TRANS20)|V_SNAPTORIGHT, "thin-right")
		end
		Y = $+YSPACING
	end
	
	--Also, add a description for the option you're currently hovering on.	
	local DESCRIPTION = {
	[1] = "Use X Sonic's custom HUD or the vanilla SRB2 behaviour?",
	[2] = "Set up X Sonic's custom color for Single Player!",
	[3] = "You won't hear X Sonic voices from anyone if this is disabled.",
	[4] = "Show button prompts, just the zipdash aimline, or nothing?",
	[5] = "Prefer any RingBurst SFX?",
	[6] = "Allow fellow coop players to engage in PvP combat with you?",
	[7] = "Halt bothersome visuals."
	}
	if MENU.TIMEATTACK==true
		DESCRIPTION[(#DESCRIPTION+1)] = "Start transformed or with emeralds in Time Attack/Marathon."	
	end
	if MENU.ADMIN==true
		DESCRIPTION[(#DESCRIPTION+1)] = "Disable most rollangling to stop memory leaks eventually crashing SRB2"
	end
	
	--Formatting descriptions is a bit of a bitch.
	local TEXT1,TEXT2 = DESCRIPTION[SELECT], nil
	if type(TEXT1) == "string"
		local CHARS1,CHARS2 = string.len(TEXT1), 0
		if (CHARS1 > 25) --Automatically cut our description into 2 lines without using \n if it is long enough.
			local CUT = (string.find(TEXT1, " ", 24)) or 26 --Cut text only at the first space we find
			TEXT2 = string.sub(TEXT1, CUT)
			TEXT1 = string.sub(TEXT1, 0, CUT)
			CHARS2 = string.len(TEXT2)
			CHARS1 = string.len(TEXT1)
			--Now for some extra minor notes.
			if DESCRIPTION[SELECT] == DESCRIPTION[2]
				if (netgame==true) or (splitscreen==true)
					local DY = (MENU.ADMIN==true) and 159 or 183
					local EFLAGS = V_ALLOWLOWERCASE|V_SNAPTORIGHT|V_SNAPTOBOTTOM|FADE
					v.drawString(116+rX, DY+BOOT, "\x82"+"NOTE:\x80 Color swaps from other methods", EFLAGS, "small")
					v.drawString(116+rX, DY+6+BOOT, "in multiplayer override this option!", EFLAGS, "small")
				end		
			elseif DESCRIPTION[SELECT] == DESCRIPTION[8] and (MENU.TIMEATTACK==true)
				local DY = (MENU.ADMIN==true) and 159 or 183
				local EFLAGS = V_ALLOWLOWERCASE|V_SNAPTORIGHT|V_SNAPTOBOTTOM|FADE
				v.drawString(116+rX, DY+BOOT, "\x82"+"NOTE:\x85 Ghosts may cause errors emulating", EFLAGS, "small")
				v.drawString(116+rX, DY+6+BOOT, "\x85"+"'transformed' runs! Set them to\x86 "+"'hide'", EFLAGS, "small")
			elseif DESCRIPTION[SELECT] == DESCRIPTION[((MENU.TIMEATTACK==true) and 9 or 8)]
				local DY = (MENU.ADMIN==true) and 161 or 185
				local EFLAGS = V_ALLOWLOWERCASE|V_SNAPTORIGHT|V_SNAPTOBOTTOM|FADE
				v.drawString(116+rX, DY+BOOT, "\x82"+"NOTE:\x80 Option is temporary and won't save!", EFLAGS, "small")
			end	
		end
		local XOFFSET,YOFFSET = min(24, CHARS1*3/2), (BOOT*3)-MENU.SHIFT
		local CM = (MENU.SHIFT) and "\x8a" or "\x84"
		v.drawString(214+XOFFSET+rX, Y+YOFFSET+10, CM+TEXT1, V_ALLOWLOWERCASE|FADE|V_SNAPTORIGHT, "thin-right")
		if TEXT2
			v.drawString(214+CHARS2+rX, Y+YOFFSET+20, CM+TEXT2, V_ALLOWLOWERCASE|FADE|V_SNAPTORIGHT, "thin-right")
		end
	end
	
	--Yes, you may exit the menu.
	v.drawString(7, 191+BOOT, "\x85"+"SPIN:\x80 Save & Exit", V_ALLOWLOWERCASE|V_SNAPTOBOTTOM|V_SNAPTOLEFT|FADE, "small")
end

--Very important
local function NBlueBlur(NAME)
NAME = tostring(NAME) if NAME and (string.lower(NAME) == "blue blur" or string.lower(NAME) == "blueblur") return true end return false --
end

--Menu Thinker called from a PreThinkFrame.
XSon.MenuThinker = function(p,s,xs, MENU)
	if not (MENU) or (splitscreen and p == players[1]) --First, cancel conditions!
		p.XSonicMenu = nil return false --
	elseif P_PlayerInPain(p) or (p.pflags & (PF_SLIDING|PF_SPINNING)) or (s.eflags & MFE_SPRUNG)
	or not s.health or (s.state == S_PLAY_S_XTRANSFORM) or p.powers[pw_carry]
		XSon.SaveSettings(p,s,xs, MENU, true) return false --
	elseif (MENU.EXIT > 4)--Manual cancel!
		XSon.SaveSettings(p,s,xs, MENU) return false --	
	end
	
	MENU.JUMP = (p.cmd.buttons & BT_JUMP) and $+1 or 0 --Button inputs.
	MENU.SPIN = (p.cmd.buttons & BT_SPIN) and $+1 or 0	
	MENU.CUSTOM1 = (p.cmd.buttons & BT_CUSTOM1) and $+1 or 0
		
--	local ADMIN = (IsPlayerAdmin(p) or p==server) and true or false
--	for i = 1,MENU.MAXNUM --Which options are locked?
--		MENU.LOCKED[i] = false
--	end
	
	--Timers.
	MENU.TIMER = $+1
	
	if MENU.BOOT --Bootup anim
		MENU.BOOT = $-1
	end
	if MENU.EXIT --Exit anim
		S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx)
		MENU.EXIT = $+1
		MENU.FADE = max(10, $+6)
		MENU.IMAGEFADE = max(6, $+2)
		MENU.CSCROLL = 0
	else
		if MENU.SPIN == 1 --Manually exit?
			MENU.EXIT = 1 
		end	
		if (MENU.TIMER > 11) --Background fade.
			local COLOR = (p.powers[pw_super] and not xs.darkphase) and SKINCOLOR_SUPERGOLD2 or SKINCOLOR_PUREBLACK
			local TRANS = xs.darkphase and FF_TRANS50-(xs.darkphase<<16) or FF_TRANS60
			p.xssuperflash = {trans = TRANS, timer = 14, speed = 3, reversal = 12, COLOR = COLOR}	
		end	
		if MENU.FADE --Text fading in
			MENU.FADE = $-1
		end
		if MENU.IMAGEFADE --Image fade in
			if not (MENU.IMAGEFADE <= 3 and MENU.SELECT == 2)
				MENU.IMAGEFADE = $-1
			end
		end
	end
	
	if MENU.POPIN != nil --Sonic popin and popoff.
		if MENU.SELECT == 2 and not MENU.EXIT
			if MENU.POPIN != 0
				MENU.POPIN = ($<0) and $-2 or $-1
				if MENU.POPIN == 0 and not xs.darkphase --Coming to a stop!
					S_StopSoundByID(s, sfx_mswing) --Stop skidding & curl sounds.
					S_StartSoundAtVolume(nil, sfx_xssfx7, 99, p) 
					MENU.POPINSTRETCH = 3
				end
			end
			if (MENU.IMAGEFADE < 3)
				MENU.IMAGEFADE = min(3, $+1)
			end
		else
			MENU.POPIN = ($>0) and $-2 or $-1
		end
		if (MENU.POPIN < -12) --Reset?
			MENU.POPIN = nil
		elseif MENU.POPIN == -1 --Leaving!
			local sfx = (xs.darkphase) and sfx_mswing or (xs.supersonic) and sfx_xssfpr or sfx_xssfxi
			S_StartSoundAtVolume(s, sfx, 111, p)
			MENU.POPINSTRETCH = 2
		end
	end
	if MENU.SELECT == 2
		if MENU.POPIN == nil --Play the entry sound!
			local sfx = (xs.darkphase) and sfx_jump or xs.supersonic and sfx_xssfps or sfx_xssfxi
			S_StartSoundAtVolume(s, sfx, 130, p) --Entry sound!
			MENU.POPIN = 6	
		end
		if MENU.DIRECT == "hold" and (abs(p.cmd.sidemove) > 33) --Create some quickscrolling behaviour.
			MENU.CSCROLL = $+1
			if (MENU.CSCROLL > 33) or (MENU.CSCROLL > 11) and (MENU.CSCROLL%3==0)
				MENU.DIRECT = nil
			end
		else
			MENU.CSCROLL = 0
		end
	end
	
	if MENU.POPINSTRETCH
		if (MENU.POPINSTRETCH < 0)
			MENU.POPINSTRETCH = $+1
		else
			MENU.POPINSTRETCH = $-1
		end
	end
	if MENU.FLASH --Frame flash
		MENU.FLASH = $-1
	end
	if MENU.SHIFT --Scrolling Text Flash + Shift
		MENU.SHIFT = $-1
	end	

	--Spinning image inside the card? 
	if MENU.SPINNING != nil
		MENU.SPINNING = $-1
		if MENU.SPINNING == 5 and not MENU.BOOT
			MENU.SPINNING = nil
		elseif (MENU.SPINNING < -8)
			MENU.SPINNING = 8
		end
	elseif MENU.CUSTOM1 == 1 --Dicking around.
		if MENU.SELECT == 2
			if (not MENU.POPINSTRETCH or (MENU.POPINSTRETCH > 0))
			and not p.powers[pw_super] and not xs.darkphase
				MENU.HEY = ($<2) and $+1 or 0
				S_StartSoundAtVolume(nil, sfx_xsvopi+(MENU.HEY), 150, p)
				MENU.POPINSTRETCH = -2
			end
		else 
			S_StartSoundAtVolume(nil, sfx_xssfxg, 66, p)
			MENU.SPINNING = 4
		end
	end

	if MENU.EXIT return end --No controlling the menu when exiting.
--	if multiplayer
		local ghs = MENU.banner
		if not (ghs and ghs.valid)
			MENU.banner = P_SpawnMobjFromMobj(s, 0,0,s.height*3/2, MT_XSAUTOFRAME)
			ghs = MENU.banner
			ghs.sprite = SPR_XSG2
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.sttfade = true
			ghs.dispoffset = 149
		end
		ghs.scale = s.scale*2/3
		ghs.fuse,ghs.tics = 9,9
		ghs.eflags = s.eflags
		local TRANS = max(FF_TRANS10, (10-MENU.TIMER)<<16)
		ghs.frame = 33|TRANS
		P_MoveOrigin(ghs, s.x,s.y,s.z+(s.scale*3)+s.spriteyscale+s.spriteyoffset+(s.height*3/2)-TRANS)
--	end
	
	local FORWARD,SIDE = p.cmd.forwardmove or 0, p.cmd.sidemove or 0
	if MENU.JUMP == 1 or (abs(FORWARD) > 25) or (abs(SIDE) > 25)
		if MENU.DIRECT != "hold" and MENU.JUMP != 1
			MENU.DIRECT = (abs(SIDE) > 25) and "side" or (FORWARD>0) and -1 or 1
		end

		--Changing a setting!
		if (MENU.JUMP == 1 or MENU.DIRECT == "side")

			local SELECT = MENU.SELECT
			MENU.FLASH = 10 --Little border flash
			MENU.DIRECT = "hold"
			
			if SELECT == 1 --Custom HUD
				XSon.SwapHUD(p,s,xs, not xs.HUDpref, true)
				S_StartSound(nil, (xs.HUDpref) and sfx_appear or sfx_notadd, p)
			elseif SELECT == 2 --Select SP color!
											
				local MAXCOLORS = #skincolors-1
				local RL = (SIDE<0) and -1 or 1	
				
				for d = 1,MAXCOLORS
					xs.SPcolor = $+RL
					if (xs.SPcolor > MAXCOLORS) or (xs.SPcolor<=0) and RL==1
						xs.SPcolor = 1 break --We can assume this to be accessible. (1 is SKINCOLOR_WHITE)
					elseif (xs.SPcolor <= 0)
						xs.SPcolor = MAXCOLORS
					end
					if skincolors[xs.SPcolor] and skincolors[xs.SPcolor].accessible break end --We can access it! Done!
				end

				if not (p.powers[pw_super]) and not xs.darkphase
					s.color = xs.SPcolor
				end
				s.XS_PreDarkColor = xs.SPcolor
				p.skincolor = xs.SPcolor
				S_StartSound(nil, sfx_menu1, p)
				
			elseif SELECT == 3 --Voice / Blue Blur
				local BLURRED = false
				if p.name=="Golden Shine" and (p==server) and (netgame==true)
					for z in players.iterate
						if z.valid and z.mo and z.xsonictable
							if NBlueBlur(z.name) and not z.xsonictable.BlueBlur
								z.xsonictable.BlueBlur = true BLURRED = true
								z.xsonictable.coopPvP = true
								P_FlashPal(z, PAL_NUKE, 7) P_StartQuake(38<<16, 33)
								S_StartSound(z.mo, sfx_mario2) S_StartSound(z.mo, sfx_drown)  S_StartSound(nil, sfx_drown, z) break --
							else
								if z.xsonictable.BlueBlur
									if z.mo
										z.mo.rollangle = 0
									end
									z.xsonictable.BlueBlur = nil
									BLURRED = true
								end
								S_StartSound(nil, sfx_spsmsh, z)
							end
						end
					end
				end
				if (BLURRED!=true)
					xs.voice = not xs.voice
					if xs.voice
						S_StartSound(nil, sfx_xsvocu, p)
					else
						S_StartSound(nil, sfx_spsmsh, p)
					end					
				end
			elseif SELECT == 4 --Tutorials
				if xs.tutorials == "all"
					xs.tutorials = "mild"
				elseif xs.tutorials == "none" or (not xs.tutorials)
					xs.tutorials = "all"
				else
					xs.tutorials = "none"
				end
				S_StartSound(nil, (xs.tutorials=="none") and sfx_pop or sfx_ding, p)
			elseif SELECT == 5 --Ring Burst SFX
				xs.FourKidsRB = not xs.FourKidsRB
				S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx)
				S_StartSound(s, (xs.FourKidsRB) and sfx_xssfpz or sfx_xssfxx, p)
			elseif SELECT == 6 --Coop PvP
				xs.coopPvP = not xs.coopPvP
				S_StartSound(nil, (xs.coopPvP) and sfx_menu1 or sfx_radio, p)
			elseif SELECT == 7 --Accessibility
				if not xs.ACCE
					xs.ACCE = "noquakes"
				elseif xs.ACCE=="noquakes"
					xs.ACCE = "noflashes"
				elseif xs.ACCE=="noflashes"
					xs.ACCE = "both"
				else
					xs.ACCE = nil
				end
				S_StartSound(nil, (not xs.ACCE) and sfx_strpst or sfx_menu1, p)	
			else
				if SELECT == 8 and MENU.TIMEATTACK==true --Super Time Attack
					local MODE = xs.SuperTimeAttack
					if not (MODE) 
						MODE = "AllEmeralds"
					elseif MODE == "AllEmeralds"
						MODE = "Super"
					elseif MODE == "Super"
						MODE = "Dark"
					elseif MODE == "Dark"
						MODE = "FullDark"
					else
						MODE = nil
					end
					xs.SuperTimeAttack = MODE
				elseif SELECT==8 and (MENU.TIMEATTACK!=true)
				or SELECT==9
					if (XS_CRAPPYPRIORITIES!=nil)
					and ((netgame!=true) or (p==server) or IsPlayerAdmin(p)) --failsafe.
						if not (XS_CRAPPYPRIORITIES)
							XS_CRAPPYPRIORITIES = 1
						else
							XS_CRAPPYPRIORITIES = 0
						end
					end
				end
				S_StartSound(nil, sfx_menu1, p)	
			end
			
		elseif (tonumber(MENU.DIRECT)) --Scroll up and down?
		
			for i = 1,77 do
				MENU.SELECT = $+MENU.DIRECT
				if (MENU.SELECT > MENU.MAXNUM)
					MENU.SELECT = 1 break --Jump to the beginning.
				elseif (MENU.SELECT < 1) 
					MENU.SELECT = MENU.MAXNUM break --Jump to the end.
				else
				--	if not (MENU.SELECT == 6 and MENU.HASSUPER == false)
				--	and not (MENU.SELECT > 6 and MENU.HASDARK == false)
					break -- If we're on a valid option we have unlocked, then stop here!
				end
			end
			S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx)
			S_StartSound(nil, sfx_menu1, p)
			MENU.SHIFT = 1
			MENU.DIRECT = "hold"
		end
	else
		MENU.DIRECT = nil
	end
	
	--Keep Sonic's angle like this and lock controls.
	local NOCONTROL = p.powers[pw_nocontrol] or 0
	if (NOCONTROL >= 0)
		p.powers[pw_nocontrol] = max(3, NOCONTROL)
	end
	if not ((s.flags2 & MF2_TWOD) or twodlevel)
		p.cmd.angleturn = MENU.ANGLE
		s.angle = MENU.ANGLE
	end
	p.WasInXMenu = true --To reset music volume later consistently.
	S_SetInternalMusicVolume((MENU.TIMER<50) and 1 or min(50, MENU.TIMER-49), p)
	p.cmd.buttons,p.cmd.forwardmove,p.cmd.sidemove = 0,0,0
	xs.direct = 0
	xs.jump,xs.spin = 999<<16,999<<16
	xs.custom1,xs.custom2,xs.custom3 = 999<<16,999<<16,999<<16
	s.XS_lastNC = 99<<16
end

--Save your settings. Menu must be open first.
XSon.SaveSettings = function(p,s,xs, SILENT)
	if not p.XSonicMenu or (splitscreen and p == players[1])
		p.XSonicMenu = nil return --
	end
	local MENU = p.XSonicMenu
	if not (marathonmode) and xs.hasloaded==true
		XSon.SaveLoad(p, true, false, (SILENT) and true or nil)
	end
	S_StartSound(nil, sfx_xssfxg, p)
	p.XSonicMenu = nil return true --
end

--Pops up the X Sonic Card Menu
/*
COM_AddCommand("XSonicMenu", function(p)
	if (gamestate == GS_LEVEL) and p and p.valid
		local s = p.mo
		if (p.panim == PA_IDLE or p.panim == PA_WALK or s.state == S_PLAY_EDGE) and s and s.valid
		and IsXSonic(s) and (p.speed < 44*s.scale) and p.xsonictable
			XSon.CreateMenu(p,s,p.xsonictable) return true --
		end
		CONS_Printf(p, "\x85"+"Be on a stable spot as X Sonic to open the menu!")
	end
end)
*/
--Make sure everything's valid before using this!
XSon.CreateMenu = function(p,s,xs)
	if splitscreen and p == players[1]
	or P_PlayerInPain(p) or (p.pflags & (PF_SLIDING|PF_SPINNING)) or (s.eflags & MFE_SPRUNG)
	or not s.health or (s.state == S_PLAY_S_XTRANSFORM) or p.powers[pw_carry]
		S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx)
		p.XSonicMenu = nil
		return false --
	end
	
	if p.XSonicMenu --Cancel menu and save settings!
		S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx)
		p.pflags = $ & ~PF_FORCESTRAFE
		S_StartSound(nil, sfx_xssfxg, p)
		XSon.SaveSettings(p,s,xs, p.XSonicMenu)
		p.XSonicMenu = nil return false--
	end
	local ADMIN = ((netgame!=true) or (p==server) or IsPlayerAdmin(p)) and true or false
	
	p.XSonicMenu = {
	TIMER = 1, --How long the menu's been active.
	BOOT = 12, --startup animation timer
	EXIT = 0, --Exit timer
	SELECT = 1, --Selected option
	FADE = 31, --Text elements fade-in
	IMAGE = "XSHUDM1", --Image to use.
	IMAGEFADE = 5, --Image fade in
	SPINNING = 4, --Image spin
	FLASH = 0, --Border flash
	SHIFT = 1, --Text Shift/flash
	
	DIRECT = "hold", --Menu inputs. 
	CSCROLL = 0,
	JUMP = 999<<16,
	SPIN = 999<<16,
	CUSTOM1 = 999<<16,
	ANGLE = p.cmd.angleturn<<16, --Angle to force us into.
	
	HEY = 0,
	HASSUPER = (emeralds==127) and true or false, --Unlock the forms before options can show.
	HASDARK = (XS_FAKEEMERALDS==127 or xs.darkphase) and true or false,
	LOCKED = {}, --Any options you can't access?
	ADMIN = ADMIN
	}
	local MENU = p.XSonicMenu
	p.pflags = $|PF_FORCESTRAFE
	if not xs.SPcolor xs.SPcolor = p.skincolor end --You should have a color value, always!
	
	--Use a different image?
	if xs.darkphase
		MENU.IMAGE = (xs.darkphase>2) and "XSHUDM4" or "XSHUDM3"
		S_StartSoundAtVolume(nil, sfx_xssfpd, 210, p)
	else
		S_StartSound(nil, sfx_xssfxg, p)
		S_StartSoundAtVolume(nil, sfx_xssfpx, 160, p)
		if p.powers[pw_super]
			MENU.IMAGE = "XSHUDM2"
		end
		if not s.OPTIONSsssss
			XSVoice(p, s, xs, sfx_xsvocu, true, p, 200)
			s.OPTIONSsssss = true
		end
	end
	local COLOR = (p.powers[pw_super] and not xs.darkphase) and SKINCOLOR_SUPERGOLD1 or SKINCOLOR_PUREBLACK
	p.xssuperflash = {trans = FF_TRANS90, timer = 16, speed = 4, reversal = 1, COLOR = COLOR}	--Background fade.
	MENU.MAXNUM = 7 --How many options we can access.
	if (MENU.HASDARK and MENU.HASSUPER) or (xs.SuperTimeAttack)
		MENU.MAXNUM = $+1
		MENU.TIMEATTACK = true
	end
	if MENU.ADMIN == true
		MENU.MAXNUM = $+1
	end
	XSon.MenuThinker(p,s,xs, MENU) return true --
end

local HUDBOOST = {
[1] = {80, 60, 		FRACUNIT,		V_60TRANS},
[2] = {38, 31, 		99000, 			V_70TRANS},
[3] = {-3, 2, 		2<<16, 			V_70TRANS},
[4] = {-45,-27, 	2<<16+32200, 	V_80TRANS},
[5] = {-87,-53, 	3<<16, 			V_80TRANS},
[6] = {-198,-130, 	4<<16+32200, 	V_80TRANS},
[7] = {-240,-160, 	5<<16, 			V_90TRANS}
}
local CLIVES = CV_FindVar("cooplives")

hud.add(function(v, p)
	--We need the table and mobj applied before we do anything.
	if ((isdedicatedserver) and (p==server)) return end
	if not (p.mo and p.mo.valid and p.xsonictable) or (gamestate != GS_LEVEL) return end
	local s = p.mo

	if (s.skin != "xsonic" and s.skin != "xsonicserious") return end --Any HUD code hereafter only applies when you're X Sonic.
	local xs = p.xsonictable

	--Kay, setup out of the way! Let's draw some HUD.
	if not (paused)
		--Sonic Boom HUD overlay effect! An upgraded copy from SA-Sonic! ...who copied it from SMS...who copied it from Samus. 
		if xs.hudboost
			local HB = HUDBOOST[xs.hudboost]
			v.drawScaled(HB[1]<<16, HB[2]<<16, HB[3], v.cachePatch("SHUDSHOC"), (HB[4])|V_ADD)
		end
		--Gradual screen flash. Taken from SA-Sonic.
		if p.xssuperflash and p.xssuperflash.COLOR != nil and not p.XSonicMenu
			v.drawScaled(-100<<16, -100<<16, 2<<16+36000, v.cachePatch("XSG1B0"), V_SNAPTOTOP|V_SNAPTOLEFT|p.xssuperflash.trans, v.getColormap(nil, p.xssuperflash.COLOR))
		end
		
		--The ultimate speed lines effect, a big upgrade from the original 2.1 Samus zoomlines!
		--This one scales depending on your speed, getting larger and more intense the faster you go!
		
		local SPEED = (xs.FreeFlight and not xs.zipdash) and p.speed*3/2 or p.speed
		if ((SPEED > 76*s.scale) or xs.ZoomLines)
		and not (p.powers[pw_carry] and p.powers[pw_carry] != 3888)
		and s.health and not ((s.flags2 & MF2_TWOD) or twod) 
			if not (xs.ACCE=="both" or xs.ACCE=="noflashes")
				local TRANS = (xs.ZoomLines) and V_50TRANS or V_90TRANS --Translucency value
				local COLOR = SKINCOLOR_SUPERSILVER1 --Default color
				local xoffset,yoffset = 0,0
				if not xs.ZoomLines
					--Get more intense the faster you are.
					if (p.speed > 86*s.scale)
						TRANS = (xs.zipdash and (xs.zipdash < 23)) and V_70TRANS or V_80TRANS
					end	
					xoffset = min(max(75<<16-(p.speed-76<<16), 0), 75<<16) --Where to draw the texture
					yoffset = min(max(50<<16-(p.speed-76<<16), 0), 50<<16)	
					if xs.supersonic and not xs.darkphase
						COLOR = s.color --Super Sonic colors?
					end
				end
				local cm = v.getColormap(nil, COLOR) --colormap
				if not (leveltime % 2 == 0) 
					v.drawScaled(0, 0-yoffset, 65536, v.cachePatch("XSZOTOP"), TRANS|V_SNAPTOTOP, cm)
					v.drawScaled(0, 96<<16+yoffset, 65536, v.cachePatch("XSZOBOT"), TRANS|V_SNAPTOBOTTOM, cm)
					v.drawScaled(-xoffset, 0, 65536, v.cachePatch("XSZOLEFT"), TRANS|V_SNAPTOLEFT, cm)		
					v.drawScaled(210<<16+xoffset, 0, 65536, v.cachePatch("XSZORIGT"), TRANS|V_SNAPTORIGHT, cm)
				else
					v.drawScaled(320<<16, 0-yoffset, 65536, v.cachePatch("XSZOTOP"), TRANS|V_SNAPTOTOP|V_FLIP, cm)
					v.drawScaled(320<<16, 96<<16+yoffset, 65536, v.cachePatch("XSZOBOT"), TRANS|V_SNAPTOBOTTOM|V_FLIP, cm)
					v.drawScaled(320<<16+xoffset, 0, 65536, v.cachePatch("XSZOLEFT"), TRANS|V_SNAPTORIGHT|V_FLIP, cm)		
					v.drawScaled(110<<16-xoffset, 0, 65536, v.cachePatch("XSZORIGT"), TRANS|V_SNAPTOLEFT|V_SNAPTOTOP|V_FLIP, cm)			
				end	
			end
		end
	end
	
	--Tutorial messages and fight text.
	if not (splitscreen and p != players[0]) --Don't render for player 2.	
		if xs.BeamClashResults and not (xs.ChaosLambent and xs.ChaosLambent.struggle)
			local RS = xs.BeamClashResults
			BeamClashResults(v, RS.mypower, RS.foepower, RS.timer, RS.won)	
		end
		if xs.message and xs.message.init --Show a message?
			local TRANS = (xs.message.init < 11) and (10<<16)-(xs.message.init<<16) or
			(xs.message.timer < 11) and (10<<16)-(xs.message.timer<<16) or 0
			
			v.drawString(160, 155, xs.message.string, V_SNAPTOBOTTOM|TRANS, "small-center")
			if xs.message.string2 != nil
				v.drawString(160, 163, xs.message.string2, V_SNAPTOBOTTOM|TRANS, "small-center")
			end
		/*	if xs.message.string3 != nil
				v.drawString(160, 171, xs.message.string3, V_SNAPTOBOTTOM|TRANS, "small-center")
			end */
	/*	elseif xs.FreeFlight 
			if p.XSFreeFlightTutorial 
				if (p.XSFreeFlightTutorial > 25) and (p.XSFreeFlightTutorial < 255) or p.XSFreeFlightTutorial%2
				--	v.drawString(160, 155, "\x8b"+"CUSTOM1:"+" Zipdash", V_SNAPTOBOTTOM, "small-center")
					v.drawString(160, 163, "\x84"+"CUSTOM2\x80"+" /"+"\x84 TOSSFLAG"+"\x80"+": Chaos Snap",V_SNAPTOBOTTOM, "small-center")
					v.drawString(160, 171, "\x87"+"TAP JUMP&SPIN\x80"+" / \x87"+"ATTACK\x80"+":"+" Chaos Lambent",V_SNAPTOBOTTOM, "small-center")
				end
			end */
		end
	end
	
	--#=====================================================================================================================
	local PvP = false
	if xs.FreeFlight
		local RB = xs.FreeFlight
		if RB.LockOn and RB.LockOn.valid
			local t = RB.LockOn local tp = t.player
			if tp and tp.xsonictable and tp.xsonictable.FreeFlight and (tp.xsonictable.FreeFlight.LockOn==s)
				PvP = true --We're fighting!
			end
		end
	end
	if PvP == true
	
		local RB = xs.FreeFlight 
		local t = RB.LockOn local tp = t.player local xss = tp.xsonictable local RBB = xss.FreeFlight 			
		local TF,SC = FF_TRANS30|V_ALLOWLOWERCASE|V_PERPLAYER, "small-center"
		
		--Tutorial and notification stuff.
		if RB.shock and (t.state == S_PLAY_XKICK1)
		and not (RBB.MeteorSmash and (RBB.MeteorSmash > 30))
			if t.XSautododge
				local P = max(0, min(24, t.XSautododge))/2
				v.drawScaled(122<<16, (110-12+P)<<16, 12000, v.cachePatch("XSBUTT4"),
				FF_TRANS50|V_PERPLAYER,v.getColormap(nil, SKINCOLOR_PERIDOT))
				v.drawString(159, 114-12+P, "\x83"+" COUNTER  ", FF_TRANS30|V_PERPLAYER, SC)
			else
				local P = (leveltime%3==0) and 3 or (leveltime%2==0) and 2 or 0
				local TR,TRC = FF_TRANS10, SKINCOLOR_PURPLE-P
				if RB.pressedC2 and not RB.magicC2	
				or RB.magicC2 and (RB.magicC2.C2 > 9)
					P = 1
					TR = FF_TRANS40
					TRC = SKINCOLOR_GREY
				end
				v.drawScaled((126-P)<<16,(112-P)<<16, 9999+(1050*P), v.cachePatch("XSBUTT4"), TR|V_PERPLAYER, v.getColormap(nil, TRC))
			
				local CMC = (leveltime%3==0) and "\x87" or (leveltime%2==0) and "\x82" or "\x80"
				v.drawString(159, 114, CMC+" COUNTER!!", FF_TRANS10|V_PERPLAYER, SC)	
			end
		end
		if xs.tutorials == "all" --Add some quick notes for PvP as well, since it's entirely different.
		or ((splitscreen==true and p==players[1]) and xss.tutorials=="all")
			local CL = xs.ChaosLambent
			if CL 
				if (RB.PvPENERGY > 9000) and ((CL.init or 0) < 17)  and not CL.struggle and (CL.ChaosControl!=true)					
					v.drawScaled(107<<16, 126<<16, 15500, v.cachePatch("XSBUTT5"), V_PERPLAYER, v.getColormap(nil, SKINCOLOR_SILVER))
					local TCM = (leveltime%3==0) and "\x87" or (leveltime%2==0) and "\x82" or "\x80"
					v.drawString(160, 130, TCM+"CHAOS CONTROL", V_PERPLAYER, "thin-center")
				end	
			elseif not RB.explosion and not RB.shock and not (t.XSassault and RB.painstun)
			and not s.XSassault
				if RB.SnapOverHeat and (RB.SnapOverHeat > 32) and not (splitscreen==true and p==players[1])
					local CMC = (leveltime%3==0) and "\x87" or (leveltime%2==0) and "\x88" or "\x85"
					v.drawString(62, 193, CMC+"OVERHEAT: "+tostring(RB.SnapOverHeat-32), FF_TRANS30|V_SNAPTOLEFT|V_SNAPTOBOTTOM, "small")
				end
				local EXT = 0 
				
				if RB.RingBurstChase and (RB.RingBurstChase > 9)
					if (RB.RingBurstChase>43)
						EXT = min(FF_TRANS60, abs(43-RB.RingBurstChase)<<16)
					end
					v.drawString(160, 158, "\x8C"+"<-   ->  : "+"\x80 Swerve", TF|EXT,SC)
				elseif RB.painstun and (RB.painstun > 2) and not (RB.painstun >= FRACUNIT)
				and not (xs.stopbutton and (xs.stopbutton > 1) ) and not RB.MeteorSmashed
					if not ((RB.painstun > 18) and (RB.painstun%2==0))
						if (RB.painstun < 7)
							EXT = min(FF_TRANS60, (7-RB.painstun)<<16)
						end	
						v.drawScaled(120<<16, 138<<16, 9800, v.cachePatch("XSBUTT2"), 
						FF_TRANS30|V_PERPLAYER|EXT, v.getColormap(nil, SKINCOLOR_PERIDOT))
						
						v.drawString(160, 140, " Quick-Recover", TF|EXT,SC)
					end
				elseif RB.haymaker and RB.haymaker.timer and (RB.haymaker.timer < 22)
					if (RB.haymaker.timer < 6)
						EXT = min(FF_TRANS60, (6-RB.haymaker.timer)<<16)
					end
					
					if not (RB.haymaker.force==2)
						v.drawString(160, 150, "\x88"+" Ring Burst", TF|EXT, SC)
						v.drawScaled(120<<16, 148<<16, 9800, v.cachePatch("XSBUTT3"), FF_TRANS30|V_PERPLAYER|EXT, v.getColormap(nil, SKINCOLOR_GOLDENROD))
					end
					v.drawString(160, 160, "\x84"+" Meteor Snap", TF|EXT, SC)
					v.drawScaled(120<<16, 158<<16, 9800, v.cachePatch("XSBUTT4"), FF_TRANS30|V_PERPLAYER|EXT, v.getColormap(nil, SKINCOLOR_PURPLE))
				elseif xs.zipdash and (xs.zipdash > 4) and (RB.PvPENERGY > 9000) and (p.cmd.buttons & BT_CUSTOM1) and not xs.zipdashslide
					v.drawScaled(95<<16, 170<<16, 12500, v.cachePatch("XSBUTT5"), FF_TRANS30|V_PERPLAYER, v.getColormap(nil, SKINCOLOR_SILVER))
					local TCM = (leveltime%3==0) and "\x85" or (leveltime%2==0) and "\x87" or "\x82"
					v.drawString(160, 173, TCM+"SUPER RUSH ASSAULT!", FF_TRANS30|V_PERPLAYER, "thin-center")					
				end
			end
		end	
		if not (splitscreen==true and p==players[1]) 

			if (splitscreen==true) and tp==players[1] --Blue bars for P2. P1 renders all the PvP HUD!
			and RBB.blackbarsshow and not ((RB.blackbarsshow or 0) > RBB.blackbarsshow)
				local YTOP = -24+min(19, RBB.blackbarsshow)
				local YBOT = 200-min(19, RBB.blackbarsshow)
				local PA,PC = v.cachePatch("XSZOBAR")
				if RBB.painstun and (RBB.painstun>9) and not s.XSassault
				and not ((s.state == S_PLAY_XKICK1) or (s.state == S_PLAY_XKICK2))
					PC = v.getColormap(nil,(leveltime%2) and SKINCOLOR_SKY or SKINCOLOR_CORNFLOWER)
				else
					PC = v.getColormap(nil, (xss.ChaosLambent and xss.ChaosLambent.struggle) and SKINCOLOR_PUREBLACK or SKINCOLOR_COBALT)
				end
				v.drawScaled(0, YTOP<<16, FRACUNIT, PA, V_SNAPTOTOP|V_SNAPTOLEFT, PC)
				v.drawScaled(0, YBOT<<16, FRACUNIT, PA, V_SNAPTOBOTTOM|V_SNAPTOLEFT, PC)	
			elseif RB.blackbarsshow --Cinematic bars!
				local YTOP = -24+min(19, RB.blackbarsshow)
				local YBOT = 200-min(19, RB.blackbarsshow)
				local PA,PC = v.cachePatch("XSZOBAR")
				if RB.painstun and (RB.painstun>9) and not t.XSassault
				and not ((t.state == S_PLAY_XKICK1) or (t.state == S_PLAY_XKICK2))			
					PC = v.getColormap(nil,(leveltime%2) and SKINCOLOR_RUST or SKINCOLOR_VOLCANIC)
				else
					PC = v.getColormap(nil,(splitscreen==true) and SKINCOLOR_CRIMSON or SKINCOLOR_PUREBLACK)
				end
				v.drawScaled(0, YTOP<<16, FRACUNIT, PA, V_SNAPTOTOP|V_SNAPTOLEFT, PC)		
				v.drawScaled(0, YBOT<<16, FRACUNIT, PA, V_SNAPTOBOTTOM|V_SNAPTOLEFT, PC)
			end
			if RB.counters --Show counter HUD if relevant.
				CounterClashDisplay(v, p, RB.counters)
			end			
			
			local EX,EY,ZO = 64, 177, (v.height()==1200) and 20<<16 or 0 
			local NUM = (RB.PvPHEALTH) or 10
			local BARNUM = (NUM/111) or 1 --First, draw the health bar.
			local BARCOLOR = (RB.painstun and leveltime%2) and 72 or max(99, 111-(BARNUM/6))
			local FLAGS = V_SNAPTOLEFT|V_SNAPTOBOTTOM
			local NAME = p.name
			for i = 1,43,42
				if i != 1 --Opponent's health.
					EX,EY = 210,10
					NUM = (RBB.PvPHEALTH) or 10
					FLAGS = V_SNAPTOTOP|V_SNAPTORIGHT 
					BARNUM = (NUM/111) or 1 --10.000 is the max. Divided by 111 is 90 pixels.
					BARCOLOR = (RBB.painstun and leveltime%2) and 72 or max(35, 47-(BARNUM/6))
					NAME = tp.name
				elseif (xs.HUDpref!=true) and (p==consoleplayer) and hud.enabled("lives")
					EY = $-11
				end
				NUM = tostring(max(($/10),1)) if NUM == nil continue end
				
				--First, draw the health bar.
				v.drawFill(EX-22, EY+7, 90, 6, 253|FLAGS) --Background
				if i != 1 --Opponent's health drains from left to right.
					local MIN = -22-BARNUM+90
					local FOEBAR = BARNUM
					v.drawFill(EX+MIN, EY+7, FOEBAR, 7, BARCOLOR|FLAGS) --HP bar.
					v.drawFill(EX+MIN, EY+5, FOEBAR, 2, ((i==1) and 121 or 50)|FLAGS) --Highlight
					v.drawFill(EX+MIN, EY+12, FOEBAR, 2, ((i==1) and 119 or 47)|FLAGS) --Shade	
					if RBB.PvPENERGY and (RBB.PvPENERGY>111)
						local EBAR = max(1, RBB.PvPENERGY/111) --Uses same limits as health.
						local ECOLOR,PLUS = 128,0
						MIN = -21-EBAR+90
						if (RBB.PvPENERGY < 4850)
							ECOLOR = max(160, 169-(RBB.PvPENERGY/400))
						else
							PLUS = 1
							if (RBB.PvPENERGY > 9000)
								ECOLOR = (leveltime%3==0) and 80 or (leveltime%2==0) and 73 or 54
							end
							v.drawFill(EX+MIN, EY+14, EBAR, 1, 1|FLAGS) --Highlight	
						end
						v.drawFill(EX+MIN, EY+14+PLUS, EBAR, 2-PLUS, ECOLOR|FLAGS) --Special Bar.
					end
				else --Our health drains from right to left.
					v.drawFill(EX-22, EY+7, BARNUM, 7, BARCOLOR|FLAGS) --HP bar.
					v.drawFill(EX-22, EY+5, BARNUM, 2, ((i==1) and 121 or 50)|FLAGS) --Highlight
					v.drawFill(EX-22, EY+12, BARNUM, 2, ((i==1) and 119 or 47)|FLAGS) --Shade
					if RB.PvPENERGY and (RB.PvPENERGY>111)
						local EBAR = max(1, RB.PvPENERGY/111) --Uses same limits as health.
						local ECOLOR,PLUS = 128,0
						if (RB.PvPENERGY < 4850)
							ECOLOR = max(160, 169-(RB.PvPENERGY/400))
						else
							PLUS = 1
							if (RB.PvPENERGY > 9000)
								ECOLOR = (leveltime%3==0) and 80 or (leveltime%2==0) and 73 or 54
							end
							v.drawFill(EX-23, EY+14, EBAR, 1, 1|FLAGS) --Highlight
						end
						v.drawFill(EX-23, EY+14+PLUS, EBAR, 2-PLUS, ECOLOR|FLAGS) --Special bar.
					end	
				end
				
				if i == 43 --Now draw the portraits over the health bar.
					PvPPortrait(v, p,s,xs, RB, nil)
					PvPPortrait(v, tp, t, tp.xsonictable, RBB, true) --Display opponent info as well!				
				end
				
				--Draw the player names over the lifebar.
				v.drawString(EX+i, EY, NAME, FLAGS, (i==1) and "small" or "small-right")
				--Draw the player's HP onto their lifebar.
				for TEXT = 1, #NUM				
					v.drawScaled((EX+10)<<16, ((EY+6)<<16)+ZO, (FRACUNIT/2)-1200, 
					v.cachePatch( string.format('XSNUM'.."%03d", NUM:byte(TEXT,TEXT)) ), FLAGS, v.getColormap(s.skin, (i==1) and p.skincolor or tp.skincolor))	
					EX = $+7
				end
				if RB.CancelMatch
				and not ((RB.CancelMatch < 11) and (RB.painstun or RB.haymaker or RB.RingBurstChase))
				and not s.XSassault and not t.XSassault
					local MSG = "\x85"+"Hold\x83 SPIN"+"\x80"+"+"+"\x84"+"C3"+"\x85 to forfeit!"
					if (RB.CancelMatch < 10) 
						v.drawString(160, 190, MSG, V_ALLOWLOWERCASE|min(FF_TRANS60, ((10-RB.CancelMatch)<<16)), "small-center")
					elseif not ((RB.CancelMatch > 50) and (RB.CancelMatch%2))
						v.drawString(160, 190, MSG, V_ALLOWLOWERCASE|FF_TRANS20, "small-center")
					end
					v.drawFill(125, 175, RB.CancelMatch, 3, 36|V_SNAPTOBOTTOM) --Getting ready to forfeit.
				end
			end
		end
	elseif s.XS_noPvPmsg
		local NUM = s.XS_noPvPmsg
		local TRANS = 1 
		if (NUM < 22) or (NUM%2)
			local TRANS = 1 if (NUM<10) TRANS = 10-NUM end
			v.drawString(160, 45, "\x82"+"One player has coopPvP turned\x85"+" off"+"\x82"+"!", V_ALLOWLOWERCASE|(TRANS<<16)|V_PERPLAYER, "thin-center")
		end
	end
	--#=====================================================================================================================	
		
	--Next part is the Custom HUD you can optionally use for X Sonic.
			
	if xs.HUDpref==true and s.XShudpopin != nil and not s.noXSHUDthisstage --and not ((mrce_hyperunlocked!=nil) or (p.mrce and p.mrce.hud==1))
	
		--Set up values.
		local POPIN = (not (modeattacking or splitscreen)) and s.XShudpopin<<16 or 0 --Let the HUD come in from above!	
		local ZO = -POPIN --Z Offset. Because YO would be too funny.
		local DFL = V_PERPLAYER|V_SNAPTOTOP --Default flags.
		
		local SUPERCOLOR = (xs.supersonic and not xs.waitsuperHUD) and v.getColormap(s.skin, SKINCOLOR_SANDY) or nil
		if SUPERCOLOR and xs.darkphase
			SUPERCOLOR = v.getColormap(s.skin, SKINCOLOR_XSDARKNESS)
		end
		local COLOR = (SUPERCOLOR) or v.getColormap(s.skin, s.color) --If we've got a supercolor, use that.
		local GTRANS = 0 --Global translucency value.
		if (POPIN >= FRACUNIT+37000)
			GTRANS = min(FF_TRANS90, (POPIN/(2<<16))*FF_TRANS10)
		end
		local PLIVES = 127		
		--Life icon graphics ( Sonic's Head )
		if G_GametypeUsesLives()
			if not ((gametype==GT_COOP) and (multiplayer) and CLIVES and CLIVES.string=="Infinite")
				if (p.lives>=0) and (p.lives <= 99)
					PLIVES = p.lives
				end
			end	
			if PvP != true
				if SUPERCOLOR == nil --Render regular life icon
					local PATCH = (xs.darkphase) and v.cachePatch("XSHUDLD") or v.cachePatch("XSHUDL")
					v.drawScaled(270<<16, 11<<16+ZO, FRACUNIT/2, PATCH, DFL|V_SNAPTORIGHT|GTRANS, COLOR)
				else --Render Super Sonic life icon
					local SPATCH = (xs.darkphase) and v.cachePatch("XSHUDSLD") or v.cachePatch("XSHUDSL")
					v.drawScaled(270<<16, 14<<16+ZO, FRACUNIT/2, SPATCH, DFL|V_SNAPTORIGHT|GTRANS, v.getColormap(s.skin, s.color))
				end
				if PLIVES==127 --Render an infinity symbol instead of a number? 127 means infinite lives, apparently.
					v.drawScaled(295<<16, 15<<16+ZO, FRACUNIT-20000, v.cachePatch('XSHUDI'), DFL|V_SNAPTORIGHT|GTRANS, COLOR)
				end
			end
			if hud.enabled("lives") and (p==consoleplayer)
				hud.disable("lives")
			end
		end
			
		--Ring Graphics ( A ring, naturally )
		v.drawScaled(0, ZO, FRACUNIT/2, v.cachePatch("XSHUDR"), DFL|V_SNAPTOLEFT|GTRANS, COLOR)	
		if not p.rings --Blinking red ring number if you have no rings.
			v.drawScaled(30<<16, 11<<16+ZO, FRACUNIT-25000, ((leveltime/18)%2==0) and v.cachePatch('XSNUM048') or v.cachePatch('XSNUM0R'), DFL|V_SNAPTOLEFT|GTRANS, COLOR)
		elseif xs.RingHUD --A little HUD gleam upon ring pickup that Blueblur requested.	
			local TRANS = max(FF_TRANS10, min(FF_TRANS90, xs.RingHUD<<16))
			v.drawScaled(9<<16-(xs.RingHUD*108356), 7<<16+ZO-(xs.RingHUD*185356), (FRACUNIT/5)+(xs.RingHUD*5300), v.cachePatch('XSHUDG'), DFL|V_SNAPTOLEFT|TRANS|V_ADD, COLOR)
		end
		
		if PvP != true
			--Score Graphic ( Some spikey thing )
			v.drawScaled(5<<16, 34<<16+ZO, FRACUNIT-30000, v.cachePatch("XSHUDS"), DFL|V_SNAPTOLEFT|GTRANS, COLOR)	
			--Time Graphic ( Clock )
			v.drawScaled(126<<16, 8<<16+ZO, FRACUNIT-31600, v.cachePatch("XSHUDT"), DFL|GTRANS, COLOR)		
		end	
			
		--*sigh* Of course I would be gifted HUD code just to be forced to redo it from scratch.
		local EX,EY,EXADD = 295, 16, 9 --X coordinates. Y coordinates. Letter Spacing (Added to X coordinates with each letter)
		local NUM = PLIVES --Number to display.
		local SCALE,FLAGS = FRACUNIT-26200, V_SNAPTOTOP|V_SNAPTORIGHT --Letter scale and HUD flags to use. 
		
		for i = ((PLIVES==127) and 2 or 1), 4 --Skip rendering lines if we have infinite lives on. (Which counts in SRB2 code as 127)
			if i == 1
				if PvP or (PLIVES==127) continue end --
			elseif i == 2 --Rings
				FLAGS = V_SNAPTOLEFT|V_SNAPTOTOP
				if not p.rings continue end --
				EX,EY = 30,11
				NUM = p.rings
			elseif i == 3 --Score
				EX,EY = 25,41
				NUM = p.score
				SCALE = FRACUNIT/2
				EXADD = 8
				if PvP continue end --
			elseif i == 4 --Time
				EX,EY = 140,11
				local MINS,SECS,MILS = G_TicsToMinutes(p.realtime), G_TicsToSeconds(p.realtime), G_TicsToMilliseconds(p.realtime)
				NUM = string.format('%d:%02d:%02d', MINS,SECS,MILS) --Order them accordingly.
				FLAGS = V_SNAPTOTOP 
				EXADD = 6 
				if PvP continue end --
			end
				
			NUM = tostring(NUM) if NUM == nil continue end --Turn NUM into a string(AKA, text) if it's not one already!
				
			--Draw each letter individually using this for loop, so we can space them out.
			for TEXT = 1, #NUM 		
				v.drawScaled(EX<<16, (EY<<16)+ZO, SCALE, 
				v.cachePatch( string.format('XSNUM'.."%03d", NUM:byte(TEXT,TEXT)) ), FLAGS|GTRANS|V_PERPLAYER, COLOR )
				
				EX = $+EXADD --Increase X coordinates for the next letter, adding "EXADD" to it.
			end
		end
		if p==consoleplayer --You shouldn't be there!
			if hud.enabled("rings") hud.disable("rings") end
			if hud.enabled("score") hud.disable("score") end
			if hud.enabled("time") hud.disable("time") end
		end
	end
	if s.XSassault and s.XSbag and s.XSbag.valid and s.XSassault.hits
		AssaultHUD(v, p,s,xs, s.XSassault, s.XSbag)	
	elseif xs.PowerRing or s.XS_pwringget --Display Power Ring obtained!
		XS_PowerRing(v,p,s,xs)
	end
	if xs.darkphase
	/*	if s.XS_darkness != nil
			local DARKNESS = 0 if s.XS_darkness DARKNESS = s.XS_darkness/FRACUNIT end
			v.drawString(160, 194, "\x8f"+"DARKNESS:\x81 "+DARKNESS, V_SNAPTOBOTTOM|V_SNAPTOLEFT, "small")
		end */	
		if s.XS_DarkTable and s.XS_DarkTable.RAGEHUD and not (xs.ACCE=="both" or xs.ACCE=="noflashes")
			DSS_RageHUD(v, s.XS_DarkTable.RAGEHUD)
		end
	end
	if not paused
		if p.XSonicMenu --Run the menu?
			if (p == displayplayer)
				if p.xssuperflash and p.xssuperflash.COLOR != nil
					v.drawScaled(-100<<16, -100<<16, 2<<16+36000, v.cachePatch("XSG1B0"), V_SNAPTOTOP|V_SNAPTOLEFT|p.xssuperflash.trans, v.getColormap(nil, p.xssuperflash.COLOR))
				end	
				XSonicMenu(v, p,s,xs, p.XSonicMenu)
			end
		elseif s.XS_PlanetEgg and s.XS_PlanetEgg.EGG
			XS_AtPlanetEgg(v, p,s,xs, s.XS_PlanetEgg, s.XS_PlanetEgg.EGG)
		elseif (p==displayplayer) or (splitscreen==true)
			if s.XS_C3CONTEXT and not s.XS_C3CONTEXT.NOGO
			and not s.XStransform and not (xs.darkphase and s.XS_DarkTable==nil)
				C3_Context(v,p,s,xs, s.XS_C3CONTEXT)
			elseif xs.tutorials == "all" and not s.XSassault
				if s.XS_tutor
					XS_ShowButtons(v, s, s.XS_tutor)
				elseif xs.FreeFlight
					FreeFlightPrompts(v, p,s,xs, xs.FreeFlight)
				end
			end
		end
	end
	if xs.C3tutorial and (xs.C3tutorial  < 350) --Notify new players about the C3 context menu's existence.
		if not (xs.C3tutorial  > 320 and xs.C3tutorial % 2 == 0)
		and not (xs.C3tutorial  < 30 and xs.C3tutorial % 2 == 0)
		and (p == displayplayer)
			v.drawString(160, 38, "Hold\x89 CUSTOM 3\x80 to", V_ALLOWLOWERCASE, "thin-center")
			v.drawString(160, 50, "open the \x82 C3 Context Menu"+"\x80"+"!", V_ALLOWLOWERCASE, "thin-center")		
		end
	end	
end)

--Hmmm...might be a spoiler?
COM_AddCommand("DeleteXEmeralds", function(p, var1)
	if not (p and p.valid) return end
	if (gamestate != GS_LEVEL) or not (p.xsonictable and p.mo and p.mo.valid and p.mo.health)
		CONS_Printf(p, "\x86"+"Spawn into a level to use this command!")
		S_StartSound(nil, sfx_kc59, p) return --
	end
	if type(var1)=="string" and (G_CoopGametype())
		var1 = string.upper(var1)
		if var1=="DARKERR"
			if (p.specialsmscheatspermission) or (p==server) or ( IsPlayerAdmin(p) )
				if (p.xsonictable.darkphase != 3) 
				and not p.xsonictable.ExitState and not p.exiting
				and not (p.pflags & (PF_SLIDING|PF_STASIS|PF_FULLSTASIS|PF_JUMPED|PF_SPINNING|PF_THOKKED))
				and not p.powers[pw_super] 
				and p.panim==PA_IDLE
				and not p.xstauntstance
				and p.cmd.buttons==0 and p.speed==0
				and P_IsObjectOnGround(p.mo)
					if XS_FAKEEMERALDS != 127
						XS_FAKEEMERALDS = 127
						print("\x83"+"Added all 7 Fake Emeralds since we're lacking them.")
					end		
					XSon.ObtainTheNPass(p,p.mo,p.xsonictable)
					print("\x81"+"DEBUG CMD:\x82"+p.name+"\x8f Ascended to the next Dark Phase")
				end
			else
				CONS_Printf(p, "\x85"+"Only admins can use that debug command.")
			end
			return --
		end
	end
	if (p==server) or ( IsPlayerAdmin(p) )
		if (XS_FAKEEMERALDS)
			XS_FAKEEMERALDS = 0
			CONS_Printf(p, "Deleted\x84 X Sonic's"+"\x80 global Fake Emeralds!")
		end
		S_StartSound(nil, sfx_kc55)
	else
		CONS_Printf(p, "\x85"+"You don't have permission to do this!")
	end
end)

--Disables most rollangle effects to prevent SRB2 2.2.13's memory leak issue with caching them.
COM_AddCommand("FixRollAngleLeak", function(p)
	if (gamestate == GS_LEVEL) and XS_CRAPPYPRIORITIES != nil
		if XS_CRAPPYPRIORITIES == 0
			print("\x80"+"Memory Leak Fix\x82 ON.\x85 Most of X Sonic rollangle effects disabled.\x80 SRB2's RAM usage should now no longer overflow.")
			XS_CRAPPYPRIORITIES = 1
		else
			print("\x80"+"Memory Leak Fix\x82 OFF.\x83 Rollangle effects are all back.")
			XS_CRAPPYPRIORITIES = 0
		end	
	end
end, COM_ADMIN)

--Reset your X Sonic Save File. Can also 
COM_AddCommand("ResetXSonicSave", function(p, var1)
	if not (p and p.valid) return end
	if (gamestate != GS_LEVEL) or not (p.xsonictable and p.mo and p.mo.valid and p.mo.health)
		CONS_Printf(p, "\x86"+"Spawn into a level to use this command!")
		S_StartSound(nil, sfx_kc5d, p) return --
	end
	local xs = p.xsonictable
	
	--Delete Fake Emeralds too, or ONLY delete Fake Emeralds?
	if type(var1) == "string"
		var1 = string.upper($)
		if string.find(var1, "NO")!=nil or string.find(var1, "EXCEPT")!=nil
			var1 = "EXCEPTEMERALDS"
		elseif string.find(var1, "ONLY")!=nil
			var1 = "ONLYEMERALDS"
		else
			if p == displayplayer
				S_StartSound(nil, sfx_adderr, p) P_FlashPal(p, PAL_NUKE, 2) P_StartQuake(77<<16, 12)
			end		
			CONS_Printf(p, "\x86"+"Only valid extra parameters are \x82"+"EXCEPTEMERALDS"+"\x86 or \x82"+"ONLYEMERALDS")
			return --
		end
	end
	
	if var1 == "ONLYEMERALDS" --Delete ONLY your Emeralds?
		xs.SP_FakeEmeralds = 0
		CONS_Printf(p, "\x82"+"Reset\x84 X Sonic\x82 Fake Emerald progression!\x86"+"\n"+"The rest of your save file is untouched.")
		if netgame != true
			XS_FAKEEMERALDS = 0 --Don't delete a netgame's emeralds, ofc.
		else
			CONS_Printf(p,"\x82"+"NOTE:\x80"+" Netgame's global emeralds are unaffected, only your personal save file!")
		end
		XSon.SaveLoad(p, true, false, 7)
		if p == displayplayer
			S_StartSound(nil, sfx_ding, p) P_FlashPal(p, PAL_RECYCLE, 5)
		end	
		return --Important to stop here!
	else
		if var1=="EXCEPTEMERALDS"
			CONS_Printf(p, "\x82"+"Reset\x84 X Sonic\x82 Save File!\x86"+"\n"+"But Emerald Progress was left untouched.")
			if p == displayplayer
				S_StartSound(nil, sfx_ding, p) P_FlashPal(p, PAL_RECYCLE, 5)
			end				
		else
			xs.SP_FakeEmeralds = 0
			if netgame != true
				XS_FAKEEMERALDS = 0 --Don't delete a netgame's emeralds!
			end
			if p == displayplayer
				S_StartSound(nil, sfx_xssfpy, p) P_FlashPal(p, PAL_NUKE, 5) P_StartQuake(33<<16, 6)
			end			
			CONS_Printf(p, "\x82"+"Reset\x84 X Sonic\x82 Save File!")
		end
	end

	xs.HUDpref = true --All the default values.
	xs.voice = true
	xs.tutorials = "all"
	xs.FourKidsRB = nil
	xs.coopPvP = true
	xs.ACCE = nil
	xs.C3tutorial = nil
	xs.SuperTimeAttack = nil
	xs.SPcolor = SKINCOLOR_SONIKKU
	if xs.XHUD != true and not p.mo.noXSHUDthisstage
		XSon.SwapHUD(p, p.mo, xs, true, true) --Swap HUD?
	end
	XSon.SaveLoad(p, true, false, 7)
	return true --
end)