--╔════════════════════════════╗══════════════════════════════════════════════════════════════════════════════════════════════#
--║ Super FreeFlight functions ║ Super Sonic's miraculous power of flight! Nothing else compares!
--╚════════════════════════════╝══════════════════════════════════════════════════════════════════════════════════════════════#
local XSon = XSon
local function P_MobjFlip(s) if s and s.valid and ((s.eflags&MFE_VERTICALFLIP) or (s.flags2&MF2_OBJECTFLIP)) return -1 end return 1 end

--Try transitioning into FreeFlight! If CANCEL is specified, it'll cancel if flying conditions aren't met. TryFlight"
XSon.TryFlight = function(p,s,xs, CANCEL, MOMENTUM, MOMZ, NOGFX)
	if (xs.FreeFlight or s.XS_NoFlight) and not CANCEL return true end --Already flying...!	
	
	if NOGFX != "force" and XSon.BadFlightConditions(p,s,xs)
		if CANCEL
			XSon.CancelFlight(p,s,xs, true)
		end return false --
	end
	if CANCEL or p.exiting return true end --Don't make a table in this case.	
--	local SUPER = (XS_Super(p)) or 0
	
	--Okay, we're good to fly!
	if MOMENTUM == 0 --Lose ALL momentum!
		s.momx,s.momy,s.momz = 0,0,(MOMZ) or 0
	elseif MOMENTUM or MOMZ --Lose/Amplify some of your momentum?
		if MOMENTUM and MOMENTUM != 100
			s.momx = FixedDiv(FixedMul($, 100), MOMENTUM)
			s.momy = FixedDiv(FixedMul($, 100), MOMENTUM)
		end
		if MOMZ and MOMZ != 100
			s.momz = FixedDiv(FixedMul($, 100), MOMZ)
		end
	end
	if P_IsObjectOnGround(s) --Rise up a bit if grounded somehow.
		s.z = $+(s.scale*P_MobjFlip(s))
		P_SetObjectMomZ(s, FRACUNIT, true)
	end
	--Okay, now create the table!
	
	xs.FreeFlight = {
	riseanimtimer = 0,
	tics = 6,
	maxtics = 6,
	xymult = 100, 
	zmult = 100,
	landing = 0, --Press against the ground for this long to land!
	startflames = (not (NOGFX)) and 20 or 0,
	noXYcontrol = 0,
	noZcontrol = 0,
	SnapOverHeat = 0,

	recordtap = (xs.direct < 6) and 3 or 0,
	tapped = 0,
	rocket = 0, --Burst mode.
	rocketjump = 0,
	rocketspin = 0,
	jump = xs.jump, --Copy button inputs to new table.
	spin = xs.spin,
	truespin = 0,
	dualpress = 0,
	AutoAim = 0,
	DodgeFrames = 8,
	PvPHEALTH = 10000,
	PvPENERGY = 2850,
	minPVP = 2,
	custom1 = (xs.custom1) or 0,
	custom2 = 99<<16,
	custom3 = 99<<16,
	attack = 99<<16
	}
	xs.FreeFlight.BlastCharge = {C3 = 0, LetGo = 0}
	
	--Get rid of these.
	if xs.zipdashslide
		XSZipDashCancel(p, s, xs)
	end	
	if xs.snapdive or xs.snapdivetrick or xs.ChaosSnap
		XSSnapDiveReset(p, s, xs, true)	
	end
	p.xstauntstance,p.xsringburst = nil,nil
	if xs.ringburstcharge
		xs.ringburstcharge = min($,12)
	end
	s.XS_PvPgaveup,s.XStransform = nil
	if (abs(s.momz) > 10*s.scale)
		s.state = (s.momz*P_MobjFlip(s)>0) and S_PLAY_SPRING or S_PLAY_FALL
	else
		s.state = (p.speed > 50*s.scale) and S_PLAY_RUN or (p.speed) and S_PLAY_WALK or S_PLAY_STND
	end
	xs.jump,xs.spin,xs.custom1,xs.custom3 = 99<<16,99<<16,99<<16,99<<16
	s.eflags = $ & ~(MFE_VERTICALFLIP|MFE_APPLYPMOMZ)
	p.viewrollangle,s.rollangle = 0,0
	XSon.DeleteAura(p, s, xs, true, true, true)
	if not NOGFX or NOGFX == "noSFX"
		if not NOGFX
			XSon.Sound(s, sfx_xssfpj, 170)
			s.spritexscale = FRACUNIT+25000
			s.spriteyscale = FRACUNIT-15000
			s.spriteyoffset = -7<<16
			s.XSstretch = {timer = 5, Xspeed = 3200, Yspeed = 2500}			
		end
		local ghs = P_SpawnGhostMobj(s) --Transition ghost
		ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
		ghs.blendmode = AST_ADD
		ghs.color = SKINCOLOR_WHITE
		ghs.dispoffset = 299
		ghs.colorized = true
		ghs.spriteyscale = 2<<16
		ghs.spritexscale = 6<<16
		ghs.momx,ghs.momy = $+(s.momx/2),$+(s.momy/2)
		ghs.momz = $+(s.momz/5)
		ghs.destscale = 1 ghs.fuse,ghs.tics = 11,22
		ghs.scalespeed = s.scale/9
		
		XSon.DeTransformGFX(p,s,xs, (NOGFX) and "ghosts" or nil)
		XSon.ExpandingGlow(s, SKINCOLOR_YELLOW, 20, 96, true, AST_ADD)
		XSon.OrbPillars(s, 11, 350*s.scale, 2, 21<<16, H|FF_TRANS10, FRACUNIT+50000)
		if not xs.zipdash
			p.xssuperflash = {trans = FF_TRANS30, timer = 6, speed = 1, reversephase = true, COLOR = SKINCOLOR_SUPERGOLD2}
		end
	end
	XSon.FreeFlight(p,s,xs)  --Now run the flight thinker immediately!
end

XSon.DOPAINGRUNT = function(p,s,xs,RB, FORCESFX)
	local SFX = (xs.FourKidsRB) and sfx_xssfpz or sfx_xssfxx
	S_StopSoundByID(s, SFX)	
	local PAINGRUNT = (FORCESFX) or P_RandomRange(sfx_xsvop0,sfx_xsvop4)
	if PAINGRUNT == s.xslastpaingrunt
		PAINGRUNT = ($==sfx_xsvop4) and sfx_xsvop0 or $+1
	end
	RB.painflash = 19
	if not S_SoundPlaying(s, sfx_xsvopr)
	and not S_SoundPlaying(s, sfx_xsvoc5)
	and not S_SoundPlaying(s, sfx_xsvocx)
		XSVoice(p, s, xs, PAINGRUNT, true)
	end
	s.xslastpaingrunt = PAINGRUNT
end

--Super Sonic FreeFlight"! "yu" is the freeflying behaviour table
XSon.FreeFlight = function(p,s,xs) 
	local yu = xs.FreeFlight local RB = yu
	if not RB or XSon.BadFlightConditions(p,s,xs) or p.xsringburst or s.XS_NoFlight
		if not (RB and RB.CantCancel)
			if ((XSon.CancelFlight(p,s,xs))==true) return false end --
		end
	end
	local SUPER = (XS_Super(p)) or 0
	if yu.init == nil --The very start of flight has begun! Change anything initially?
		yu.init = 0
	end
	yu.init = $+1 --How long we've been flying.
	s.XS_wasinFreeFlight = 4
	
	if p.xsringburst return true end --Ring Burst handling is prioritized!
	local ZFLIP = P_MobjFlip(s)
	
	if s.justgotPvPed --Literally just got hit into PvP.
		s.justgotPvPed = $-1
		RB.custom3 = 99<<16
		RB.pressedC3 = 0
		RB.PvPHEALTH = 10000
		if s.justgotPvPed == 0
			if not (RB.painstun)
				RB.painstun = FRACUNIT
			end
			RB.PvPtime,s.justgotPvPed = nil
		end
	else
		local t = RB.LockOn
		if t and t.valid
			if RB.custom3 and not xs.ChaosLambent
				if not RB.CancelMatch
					RB.CancelMatch = 1
				end
				if (p.cmd.buttons & BT_SPIN) and (p.cmd.buttons & BT_CUSTOM3) and not xs.ChaosSnap
				and not (p.cmd.buttons & (BT_JUMP|BT_CUSTOM1|BT_CUSTOM2))
					local CHARGING = (RB.charging) and 1 or 2
					RB.CancelMatch = min(80, $+CHARGING)
					if (RB.CancelMatch > 70) and ((XSon.CancelFlight(p,s,xs))==true) 
						t.XS_PvPgaveup = 3 
						if CHARGING==1 or (s.sprite2 & ~FF_SPR2SUPER) == SPR2_TRNS
							s.state = S_PLAY_FALL if RB RB.charging = nil end
						end
						return false --
					end	
				elseif RB.charging and RB.CancelMatch and (RB.CancelMatch>1)
					RB.CancelMatch = max($-((p.cmd.buttons & BT_JUMP) and 4 or 1),0)
				end
			elseif RB.CancelMatch
				RB.CancelMatch = max($-4, ((p.cmd.buttons & BT_CUSTOM3) and RB.custom3) and 1 or 0)
			end	
		end
		if (RB.custom3 <= 1) and (p.cmd.buttons & BT_CUSTOM3)
			RB.pressedC3 = 8
		elseif RB.pressedC3 
			RB.pressedC3 = $-1 --Manual cancel.
			if not (p.cmd.buttons & BT_CUSTOM3)
			and not xs.ChaosLambent
			and not (t and t.valid)
			and not (RB.painstun) 
			and not (t and t.valid)
			and not ((s.state >= S_PLAY_S_XTRANSFORM) and (s.state <= S_PLAY_XWALLRUN))
				RB.pressedC3 = 0
				local t = RB.LockOn
				if not (t and t.valid) 
					XSon.CancelFlight(p,s,xs) return false --
				end 
			end
		end
	end
	if (s.eflags & MFE_UNDERWATER) --Speed multipliers are...?
		RB.xymult = 135 RB.zmult = 72
	else
		RB.xymult = 100 RB.zmult = 100
	end
	if RB.forcecrossarms
		if xs.ChaosSnap or RB.painstun or RB.haymaker or xs.zipdash or (xs.direct > 25) and (RB.forcecrossarms<=2)
			RB.forcecrossarms = 0
		elseif not xs.ChaosLambent
			if not (xs.direct > 25) and RB.LockOn and RB.LockOn.valid
			and (p.pflags & (PF_ANALOGMODE|PF_DIRECTIONCHAR))
			and RB.LockOn and RB.LockOn.valid
				local t = RB.LockOn
				local DESTANG = R_PointToAngle2(t.x, t.y, s.x, s.y)-ANGLE_45
				p.drawangle = DESTANG+(ANGLE_22h*RB.forcecrossarms)
			end
			if (RB.forcecrossarms > 1)
				RB.forcecrossarms = max($-1,1)
			/*	if (RB.forcecrossarms==1) and not (xs.direct>25) and not RB.didtaunt
					local SFX = (leveltime%2) and sfx_xsvoct or sfx_xsvocs
					XSVoice(p, s, xs, SFX, "ForceEcho")
					RB.didtaunt = true
					if S_SoundPlaying(s, SFX)
						S_StartSound(nil, SFX, p)
					end
				end	 */				
			end
			p.cmd.buttons = $|BT_CUSTOM3
			RB.custom3 = 99
			RB.pressedC3 = 0
		end
	end
	if s.XS_PvPgaveup
		s.XS_PvPgaveup = $-1
	end
	if RB.noXYcontrol
		RB.noXYcontrol = $-1
	end	
	if RB.WasRingBursting
		if RB.painstun or xs.ChaosSnap
			RB.WasRingBursting = 0
		elseif not RB.RingBurstChase and not p.xsringburst
			RB.WasRingBursting = max($-1,0)
		end
	end
	if RB.dontextendagain 
		RB.dontextendagain = max($-1,0)
	end
	if RB.noZcontrol
		RB.noZcontrol = $-1
	end
	if RB.didfollowup --Ring Burst Followup.
		RB.didfollowup = $-1
	end
	if RB.crossedarms
		if not (RB.crossedarms == 1 and (p.cmd.buttons & BT_CUSTOM3) and (p.speed < 70*s.scale))
			RB.crossedarms = $-1
		end
	end
	if RB.SRAlunge
		if xs.ChaosSnap or xs.ChaosLambent or RB.painstun or RB.shock or RB.haymaker or s.XSassault
			if RB.SRAdrain
				if not (RB.painstun or RB.shock or s.XSassault)		
					if xs.ChaosSnap or xs.ChaosLambent
						RB.PvPENERGY = max(0, min(10000, $+3250)) --Get alot of energy refunded for canceling.
					elseif RB.haymaker
						RB.PvPENERGY = max(0, min(10000, $+1250))
					end
				end
				RB.SRAdrain = nil
			end
			RB.SRAlunge = nil
		else
			RB.refreshC1,RB.dualpress = nil,0
			RB.SRAlunge = max($-1,0)
			p.cmd.buttons = $|BT_CUSTOM1
			
			if xs.zipdash
				RB.custom1,xs.custom1,RB.spin = 3,3,3
				if (RB.SRAlunge > 81)
					s.momx,s.momy = $/8,$/8
					xs.zipdash = 1
					xs.delayzip,xs.zipcontinue = 2,nil			
				elseif (xs.zipdash < 4)
					xs.zipdash = $+1
				end
				RB.windlines = 3
				XSon.DarkBody(s, nil, nil, (leveltime%2) and s.color or SKINCOLOR_KETCHUP, 2)
				local RNG = P_RandomRange(6250,19250)
				XSon.SpikedAura(s, 100250+RNG)
				if (xs.zipdash > 4)
					if not RB.SRAdrain
						RB.SRAdrain = true
						RB.PvPENERGY = max(0, min(10000, $-6050))
						S_StartSound(s, sfx_xssfpr)
						S_StartSound(s,sfx_xssfx2)
					end
					xs.zipdashminus = 0
					if p.speed
						if (RB.SRAlunge%4==0)
							local ghs = XS_RingBurstWave(s)
							if ghs and ghs.valid
								ghs.color,ghs.colorized = (RB.SRAlunge%8==0) and s.color or SKINCOLOR_RUBY,true
								ghs.momx,ghs.momy,ghs.momz = 0,0,0
								if (RB.SRAlunge > 70)
									ghs.fuse = $*2/3
								elseif (RB.SRAlunge < 60)
									ghs.fuse = $*4/3
								end
							end
						end
						xs.zipdashadd = max(xs.zipdashadd or 0, 50)
						XSZipTrail(s, AST_ADD, 1, nil, 16, SKINCOLOR_RUBY, 76000, RF_FULLBRIGHT|RF_NOCOLORMAPS)	
						RB.speedlines = 3
					end
				elseif (RB.SRAlunge > 81)
					XSon.ZipDashShiftBack(p,s,xs, 120)
				end
			else
				RB.custom1,xs.custom1 = 1,1
				xs.delayzip,xs.zipcontinue = 2,nil
			end
		end
	else 
		RB.SRAdrain = nil
	end
	if RB.canforcezip
		RB.canforcezip = $-1
	end
	if RB.refreshC1 --Buffer C1&C2 soon? ...fuck it, refresh all buttons.
		if xs.ChaosSnap
		or RB.painstun and (RB.painstun>8) 
		or xs.ChaosLambent and xs.ChaosLambent.firing
			RB.refreshC1 = 0 --Cancel that order...
		else
			if (RB.refreshC1 > 1) 
			or (not RB.haymaker and not RB.RingBurstChase
			and not xs.snapdive and not xs.snapdivekick
			and not (xs.snapdivetrick and (xs.snapdivetrick < 10))
			and not ((s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK3))
			)
				RB.refreshC1 = max($-1,0)
				if RB.refreshC1==1 and xs.stopbutton
					xs.stopbutton = 0
				end
			end
			if not RB.refreshC1 and not RB.SnapOverHeat
				if (p.cmd.buttons & (BT_TOSSFLAG|BT_CUSTOM1|BT_CUSTOM2))
					xs.custom1,RB.custom1 = (xs.zipdash) and 1 or 0, (xs.zipdash) and 1 or 0
					xs.custom2,RB.custom2 = 0,0
				end
				if (p.cmd.buttons & BT_ATTACK)
					RB.attack = 0 
				elseif (p.cmd.buttons & BT_JUMP) and (RB.jump < 12) and (p.cmd.buttons & BT_SPIN) and (RB.spin < 12)
					RB.dualpress = 3 --Doesn't work most of the time.
					RB.spin,RB.truespin,RB.jump = 0,0,0
				end
			end
		end
	end	
	
	RB.jump = (p.cmd.buttons & BT_JUMP) and $+1 or 0
	RB.spin = (p.cmd.buttons & BT_SPIN) and $+1 or 0
	RB.custom1 = ((p.cmd.buttons & BT_CUSTOM1) or RB.SRAlunge) and $+1 or 0
	RB.custom2 = (p.cmd.buttons & (BT_CUSTOM2|BT_TOSSFLAG)) and $+1 or 0
	RB.custom3 = (p.cmd.buttons & BT_CUSTOM3) and $+1 or 0
	RB.attack = (p.cmd.buttons & BT_ATTACK) and $+1 or 0

	if RB.painflash --For the PvP HUD
		RB.painflash = $-1
	end	
	if RB.DodgeFrames --For PvP dodging
		RB.DodgeFrames = max($-1,0)
	end
	if RB.CantHitLambence
		if not (RB.painstun and (RB.painstun>1))
		and not (xs.ChaosLambent and xs.ChaosLambent.firing)
			RB.CantHitLambence = max($-1,0)
		end
	end
	if RB.painstun
		if RB.C1stun == nil --For buffering reasons.
			RB.C1stun = (p.cmd.buttons&BT_CUSTOM1) and 1 or 0
			RB.C2stun = (p.cmd.buttons&BT_CUSTOM2) and 1 or 0
		else
			RB.C1stun = (p.cmd.buttons&BT_CUSTOM1) and $+1 or 0
			RB.C2stun = (p.cmd.buttons&BT_CUSTOM2) and $+1 or 0
		end	
		if not RB.MeteorSmashed and not RB.gotRBhit
			if RB.painstun==FRACUNIT
				if xs.ChaosLambent
					XSon.RemoveLambentHitBoxes(p)
				end
				XSSnapDiveReset(p, s, xs)
				xs.snapdive,xs.snapdivetrick,xs.snapdivekick = 0,0,0
				xs.ChaosLambent,RB.RBupdown,RB.painstack,RB.quickroll,RB.dontextendagain = nil
				XSon.DOPAINGRUNT(p,s,xs,RB)
			elseif not RB.quickroll
				RB.quickresponse = 50
			end
		end
		RB.painstun = max($-1,0)
		RB.WallTransfer = nil
		
		if (RB.painstun <= 2)
			if (p.cmd.buttons & (BT_CUSTOM2|BT_TOSSFLAG)) --Buffering stuff.
				xs.custom2,RB.custom2 = min($,(xs.ChaosSnap) and 2 or 1),min($,(xs.ChaosSnap) and 2 or 1)
			elseif not xs.ChaosSnap or not (p.cmd.buttons & (BT_CUSTOM2|BT_TOSSFLAG)) --(p.cmd.buttons & BT_CUSTOM2) and (RB.C2stun < 11) and 
				xs.custom2,RB.custom2 = 0,0
			end
			p.viewrollangle,s.rollangle = 0,0
			if not (p.cmd.buttons & BT_CUSTOM1)
				xs.custom1,RB.custom1 = 0,0		
			else--if (p.cmd.buttons & BT_CUSTOM1)
				xs.custom1 = min($,(xs.zipdash) and 2 or 1) --(xs.zipdash) and min($, 1) or min($,1)
				RB.custom1 = min($,(xs.zipdash) and 2 or 1) --(xs.zipdash) and 1 or 0
			end			
		end
		if (RB.painstun <= 1) 
			if (xs.direct > 25) and (abs(p.cmd.sidemove) > 9)
				s.state = S_PLAY_WALK
				s.XSsuperroll = (p.cmd.sidemove < 0) and -8 or 8
			else
				s.state = S_PLAY_XZIPBRAKESUPER s.tics = 15
				s.spritexscale,s.spriteyscale = FRACUNIT-12000,FRACUNIT+20000 s.spriteyoffset = -5<<16					
				s.XSstretch = {timer = 5, Xspeed = 3300, Yspeed = 4200, air = true}						
			end	
			local ghs = XSon.Overglow(s, 2, s.color)
			if ghs and ghs.valid
				ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
				ghs.fuse = 7 ghs.tics = 99 ghs.sttfade = true
				ghs.tracer = s
			end
			XSon.sparkles(p,s,xs, 120, 6, 5, 5<<16, SKINCOLOR_SUPERGOLD4, AST_ADD)	
			p.viewrollangle,s.rollangle = 0,0
			RB.painflash = 0
			S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx) S_StopSoundByID(s, sfx_xssfxn)
			
			RB.RBupdown,RB.painstack,RB.quickroll,RB.painstun,RB.rollD,RB.C2stun,RB.C1stun = nil
		else
			if (RB.painstun >= 2) or xs.ChaosSnap
				RB.DodgeFrames = max($,2)
				xs.ZoomLines = 4
				if (RB.painstun>2)
					RB.custom2,RB.custom3 = 99<<16,99<<16				
					if (RB.painstun>4)
						RB.custom1 = 99<<16
						if (RB.painstun > 18)
							s.XS_DidBlur = true
						end
					end
				end							
				if RB.painstun == 18 or RB.painstun == 2
					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSTRACKER2)
					if ghs and ghs.valid
						ghs.target = s ghs.SteamTrail = 1
					end
				end
			end
			if RB.rollD and (XS_CRAPPYPRIORITIES!=1)
				local ROLLD = (RB.rollD < 0) and -1 or 1				
				if RB.quickroll
					s.XS_DidSuperAura,s.XS_DidBlur = true,true
					s.rollangle = (leveltime*(ANGLE_45*ROLLD))
					p.viewrollangle = 0
				else
					if RB.BAG and RB.BAG.valid
						if RB.LockOn and RB.LockOn.XSassault and RB.LockOn.XSassault.grounded
							local P = ANG1*4*((leveltime%2) and 1 or -1)
							s.rollangle = (ANGLE_90*ROLLD)+(P*2)
							p.viewrollangle = P
						elseif RB.BAG.BAGflurry or RB.BAG.GotHyperSmashed
							s.rollangle = $+ ((ANGLE_22h/2)*ROLLD*RB.rollD)
							p.viewrollangle = s.rollangle
						elseif RB.BAG.JustGotHit
							s.rollangle = $+((ANG1*RB.BAG.JustGotHit)*ROLLD*RB.rollD)
							p.viewrollangle = 0
						else
							s.rollangle,p.viewrollangle = 0,0
						end
						RB.quickroll = nil	
					else
						s.rollangle = $+(ANG2*2*ROLLD*abs(RB.rollD))
						p.viewrollangle = s.rollangle
					end
					
				end
			end
			if P_IsObjectOnGround(s)
				s.momz = (abs(s.momz) > 9*s.scale) and -$ or 9*s.scale*ZFLIP
				XSon.sparkles(p,s,xs, 80, 6, 5, 9<<16, SKINCOLOR_FLAME, AST_ADD)
				RB.rollD = (RB.rollD) and -$ or 1
				
				if not RB.quickroll and not (RB.painstack and (RB.painstack > 7))
					s.spritexscale,s.spriteyscale = 2<<16,FRACUNIT-11000
					s.spriteyoffset = -4<<16
					s.XSstretch = {timer = 7, Xspeed = 7700, Yspeed = 1150}
					RB.painstack = RB.painstack and $+1 or 1
					
					local NUKEDIST = 450*s.scale
					if RB.MeteorSmashed						
						s.momx,s.momy = $*4/3,$*4/3
						XSon.Sound(s, sfx_xssfe0, 255)
						XSon.Flash(s, PAL_NUKE, 3, 750*s.scale)
						XSon.Quake(s, 99<<16, 12, 1700*s.scale)
						NUKEDIST = 800*s.scale
						if RB.LockOn and RB.LockOn.valid
							XSon.Flash(RB.LockOn, PAL_RECYCLE, 2, 10)
							XSon.Sound(RB.LockOn, sfx_xssfe0, 165)
							XSon.Quake(RB.LockOn, 99<<16, 6, 10)
							P_NukeEnemies(s, RB.LockOn, NUKEDIST)
						end			
						XSon.EarthQuake(s, 7, 180*s.scale, 1, 58000, FRACUNIT/5, ANG30, 32|FF_TRANS40, 10, SKINCOLOR_WHITE, AST_ADD)
						XSon.EarthQuake(s, 6, 360*s.scale, 1, 93000, FRACUNIT/2, ANG10, 32|FF_TRANS30, 7, SKINCOLOR_SUPASONIKKU, AST_ADD)	
						for i = -1,1
							local ghs = XSon.LightningStrike(s, SKINCOLOR_FLAME, nil, true)
							ghs.spritexoffset = $+((34*i)<<16)
							ghs.spritexscale,ghs.spriteyscale = $*3,$*3
							ghs.z = $+99*s.scale
							
							XSon.DarkBody(s, nil, nil, SKINCOLOR_SUPERGOLD3+i, 2)
						end
						for i = 2,16,2
							local ghs = XSon.SmokeMist(s, 0, 99*s.scale)
							if ghs and ghs.valid
								ghs.spritexscale = ($*i)/2
								ghs.spriteyscale = ($*i)/2
								ghs.spriteyoffset = $-((3*i)<<16)
							end
						end
						XSon.ChargePower(s, 55, s, nil, nil, SKINCOLOR_CRIMSON)
						RB.speedlines,RB.stripes,RB.MeteorSmashed = nil
						RB.windlines,RB.SnapOverHeat = 0,0
						
						XSon.DOPAINGRUNT(p,s,xs,RB, sfx_xsvop4) --OW!!
						RB.PvPHEALTH = max($-237, 0) --Beeg damage!
						if not p.powers[pw_flashing]
							p.powers[pw_flashing] = 17
							s.flags2 = $|MF2_DONTDRAW
						end
					else
						XSon.Quake(s, 99<<16, 6, 1200*s.scale)
						if not RB.NoSlamDamage
							RB.PvPHEALTH = max($-72, 0)
						end
						XSon.Sound(s, sfx_xssfpl, 160)
						XSon.EarthQuake(s, 8, 180*s.scale, 1, 1<<16, FRACUNIT/5, ANG20, 32|FF_TRANS60, 10, SKINCOLOR_XSSUPERBLUR, AST_ADD)
						if RB.LockOn and RB.LockOn.valid
							XSon.Quake(RB.LockOn, 99<<16, 3, 10)
							P_NukeEnemies(s, RB.LockOn, NUKEDIST)
						end							
					end
					local XYDIST = NUKEDIST*2/3 local ZDIST = XYDIST/2					
					searchBlockmap("objects",function(s,m) --Destroy nearby environmental objects.
						if m.valid and (m.flags & (MF_MONITOR|MF_SCENERY|MF_SOLID|MF_PUSHABLE))
						and not (m.flags & (MF_ENEMY|MF_SPRING|MF_BOSS|MF_NOSECTOR|MF_NOCLIPTHING))
						and not m.WaitingXTree and not m.player
							if not (m.z > s.z+ZDIST) and not (s.z > m.z+ZDIST)
								XSon.ScenicCrash(p,s,xs, m, 3, nil, 24<<16)
							end
						end
					end, s, s.x-XYDIST,s.x+XYDIST, s.y-XYDIST, s.y+XYDIST)				
				end
				RB.quickresponse = 0
				RB.quickroll = 26
			else
				if (s.momz*ZFLIP > -7*s.scale) --Auto-adjust trajectory so the fight doesn't get stuck on the ceiling.
					local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
					if (abs((s.z+s.momz)-FLOORZ) < 280*s.scale)
						if (leveltime%3==0) and ((p.speed+abs(s.momz)) > 51*s.scale)
							local ghs = XS_CraterSpew(s, s.angle+ANGLE_180, 12)	
							if ghs and ghs.valid
								ghs.tics,ghs.fuse = 34,32								
							end	
							XS_RingBurstDust(p, s, xs, 350*s.scale)	
							for i = 3,6,3
								ghs = XSon.SmokeMist(s, 0, 99*s.scale)
								if ghs and ghs.valid
									ghs.spritexscale = ($*i)/2
									ghs.spriteyscale = ($*i)/2
									ghs.spriteyoffset = $-24<<16
								end
							end							
						end
					else
						local CEILINGZ = (s.eflags & MFE_VERTICALFLIP) and s.floorz or s.ceilingz
						if (abs((s.z+s.momz)-CEILINGZ) < 350*s.scale)
							P_SetObjectMomZ(s, -3<<16, true)
						end
					end
				end
				if not p.cmd.forwardmove and not p.cmd.sidemove
					p.cmd.forwardmove = -1	
				end
				if not xs.stopbutton and not (RB.quickroll and (RB.quickroll>22))
					XSon.QuickRecover(p,s,xs)
				end
			end
		end	
	else
		if RB.WallTransfer --Currently doing a flight-walljump?
			if xs.zipdash
				XSZipDashCancel(p, s, xs) xs.zipdash = 0 xs.zipdashslide = 0 xs.zipcontinue = nil
			end
			if xs.walljump or RB.truespin == 1 or (not RB.jump and not RB.custom1)
				RB.WallTransfer  = nil
			else
				RB.WallTransfer = $-1
			end
		end
	end
	if RB.explosion
		if (RB.explosion < 16)
			if (p.cmd.buttons & BT_CUSTOM1) 
				RB.custom1 = min($,(xs.zipdash) and 2 or 1)
			end
			if (p.cmd.buttons & BT_CUSTOM2) 
				RB.custom2 = min($,1)
			end
		end
		RB.explosion = max($-1,0)
	end
	if RB.quickresponse --Can start a zipdash quicker.
		if RB.haymaker or RB.RingBurstChase or xs.ChaosLambent or xs.snapdivekick
			RB.quickresponse = 0
		else
			RB.quickresponse = max($-1,0)
		end
	end	
	if p.scoreadd and (leveltime%29==0) --Slowly remove combo bonus when flying. 
		p.scoreadd = max($-1,0)
	end
	if XSon.PVPLockOn(p,s,xs,RB) == false --Not in PvP
	
		s.flags = $|MF_NOGRAVITY
		s.eflags = $ & ~MFE_JUSTHITFLOOR	
		
		if RB.minPVP
			RB.minPVP = max($-1,0)
			p.viewrollangle = 0
		else		
			RB.SnapOverHeat = 0
			RB.noinput,RB.autoinput = nil
			RB.cancharge,RB.charging,RB.counters,RB.blackbars,RB.blackbarsshow,RB.countercam,RB.NoSlamDamage,RB.gotRBhit = nil
			RB.countered,RB.shock,RB.MeteorSmash,RB.MeteorSmashed,RB.pressedC2,RB.CantCancel,RB.ExplosionCD = nil
			if RB.LockOn != nil				
				RB.LockOn,RB.PvPtime,s.XS_PvPgaveup = nil
				p.viewrollangle,p.aiming = 0,0	
				s.XS_PVPCD = 50
				XSon.SetMusic(p,s,xs) 
			end
			RB.PvPHEALTH,RB.PvPENERGY,RB.pressedC2 = 10000,2850,0
		end	
		if p.xswallrun or RB.WallTransfer
		or not RB.painstun and not ((s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK4))
		and not (xs.snapdivekick or xs.snapdivetrick or xs.ChaosSnap or xs.snapdive)
			p.pflags = $|PF_CANCARRY
		else
			p.pflags = $ & ~PF_CANCARRY
		end
	else --We're in PvP.
		if RB.holdcam
			RB.holdcam = $-1
		end
		if RB.gotRBhit
			RB.gotRBhit = $-1
		end		
		if RB.NoSlamDamage --Cant take damage from walls.
			RB.NoSlamDamage = $-1
		end
		if RB.delaycam
			RB.delaycam = max($-1,0)
		end
		if RB.MeteorSmashed 
			RB.MeteorSmashed = $-1
			if (abs(s.momz) > 7*s.scale)
				XSon.zwind(s, 2)
			end
		end	
		if RB.CantCancel
			RB.CantCancel = $-1
		end	
		if RB.auratrans == nil --For the PVP HUD.
			RB.auratransD, RB.auratrans = 1,5
		elseif (RB.auratrans < 2)
			RB.auratransD = 1
		elseif (RB.auratrans > 24)
			RB.auratransD = -1
		end	
		
		RB.auratrans = $+RB.auratransD		
		p.pflags = $ & ~PF_CANCARRY
		if not (s.XSassault or RB.LockOn and RB.LockOn.XSassault) 	
			s.flags = $|MF_NOGRAVITY
			s.eflags = $ & ~MFE_JUSTHITFLOOR
		end
		
		--Used in lock-on battles to stop spam.
		if RB.SnapOverHeat
			if not (xs.ChaosSnap and xs.ChaosSnap.init and (xs.ChaosSnap.init < 8))
				if RB.SnapOverHeat == FRACUNIT
					RB.SnapOverHeat = 0
				else
					RB.SnapOverHeat = max($-1,0)
					if (RB.SnapOverHeat > 37) and xs.ChaosSnap and not xs.ChaosSnap.timer == 1
						local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_EXPLODE)
						ghs.scale = $*2 ghs.colorized = true ghs.color = SKINCOLOR_JET
						local ghs = XSon.Overglow(s, 2, SKINCOLOR_RED)
						S_StartSound(s, sfx_s1ab)
						s.momx = $/2 s.momy = $/2
						if ghs and ghs.valid
							ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
							ghs.fuse = 19 ghs.tics = 99 ghs.sttfade = 22
							ghs.tracer = s ghs.rollangle = s.rollangle
							XSon.OverGlowThinker(ghs)
						end
					end
				end
			end
		end		
		if RB.pressedC2
			RB.pressedC2 = max($-1,0)
		end
		if RB.MeteorSmash
			RB.MeteorSmash = $-1
			if xs.ChaosSnap and xs.ChaosSnap.OverLay
				for i = 1,2
					local OL = xs.ChaosSnap.OverLay[i]
					if OL and OL.valid and OL.skin=="xsonic"
						OL.sprite2 = SPR2_TAL3|FF_SPR2SUPER
						OL.frame = C|FF_TRANS50|FF_VERTICALFLIP			
						OL.angle = p.drawangle
					end
				end
			end
		end	
		if RB.countercam
			RB.countercam = $-1
			if not RB.countercam and not (RB.painstun and (RB.painstun>1))
				p.viewrollangle = 0
			end
		end
		if RB.OnePerKick
			if not (s.state == S_PLAY_XKICK1) and not (s.state == S_PLAY_XKICK2) or not RB.counters
				RB.OnePerKick = nil print("Set to\x85 nil")
			end
		end
		if RB.shock
			RB.shock = max($-1,0)
		end
		if RB.countered
			RB.countered = max($-1,0)
		end		
		if RB.blackbars
			RB.blackbars = $-1
			local SHOW = RB.blackbarsshow or 0
			local ADD = (SHOW < 12) and 2 or 1
			RB.blackbarsshow = max(5, min(19, SHOW+ADD))
		elseif RB.blackbarsshow
			RB.blackbarsshow = max($-((RB.blackbarsshow>13) and 5 or (RB.blackbarsshow>9) and 4 or 2), 0)
		end		
		if RB.counters
			if not RB.counters.timer
				RB.counters = nil
			else
				RB.counters.timer = max($-1,0)		
			end
		end
		if RB.charging --Currently in charge animation.
			if (RB.crossedarms!=1) or not RB.LockOn or xs.zipdash and (xs.zipdash>4) 
			or not (p.cmd.buttons & BT_CUSTOM3) or RB.painstun or RB.haymaker or RB.counters or RB.shock
			or xs.ChaosSnap or xs.ChaosLambent -- or not (RB.cancharge and not p.XS_chargehaxx)
				RB.charging = nil
			else
				s.momx,s.momy = $/2,$/2
			end
		end
		if RB.cancharge --Let foe finish charging for a sec.
			RB.cancharge = max($-1,0)
		end
		RB.AutoAim = ((xs.direct < 22) or (abs(p.cmd.sidemove) < 7) or (p.cmd.forwardmove<-22)) and $+1 or 0
		RB.PvPtime = RB.PvPtime and $+1 or 1	
		if (RB.PvPtime < 15)
			if (RB.PvPtime < 8)
				RB.cantsnap = max(RB.cantsnap or 0, 8)
				RB.SnapCD = max(RB.SnapCD or 0, 8)
				xs.stopbutton = max(xs.stopbutton or 0, 14)
				RB.noXYcontrol = max(RB.noXYcontrol or 0, 14)
				if (RB.PvPtime < 3) --Delete some stuff initially.
					if s.xsoverglow and s.xsoverglow.valid
						s.xsoverglow.state = S_NULL
					end
					if RB.flicker and RB.flicker.valid
						RB.flicker.state = S_NULL 
					end
					XSon.DeleteAura(p, s, xs, true, true, true) 
				end
			end 
			RB.painflash = 0
			RB.NoSlamDamage = max(RB.NoSlamDamage or 0, 4)
			RB.DodgeFrames = max(RB.DodgeFrames or 0, 2)
			RB.PvPHEALTH = 10000 --Keep health maxed.
			RB.PvPENERGY = 2850 --Energy starts here.
			if s.XS_rumble==nil and RB.painstun and RB.LockOn
				s.XS_banter	= {timer = 10, SFX = sfx_xsvocr, targetplayer = tp, TPvolume = 20}
				s.XS_rumble,RB.LockOn.XS_rumble = true,true
			end				
			
		elseif RB.LockOn and RB.LockOn.valid and RB.LockOn.player
		
			local t = RB.LockOn local tp = t.player local xss = tp.xsonictable local RBB = xss and xss.FreeFlight
			if RBB and RB.PvPHEALTH
				local BUTTONS = p.cmd.buttons
				if RB.autoinput BUTTONS = $ & ~(RB.autoinput) end

				if not (BUTTONS & (BT_JUMP|BT_SPIN|BT_CUSTOM3|BT_TOSSFLAG))
				and not (abs(t.z-s.z) < 50*s.scale)
				and not (xs.ChaosLambence or (xs.zipdash==5) or RB.SRAlunge or RB.painstun or RB.explosion or RB.charging)
				and not (xss.ChaosLambence or (xss.zipdash==5) or RBB.SRAlunge or RBB.painstun or RBB.explosion or RBB.charging)
					RB.noinput = RB.noinput and $+1 or 1
				else
					RB.noinput = 0
				end		
				if p.XS_healthhaxx
					tp.XS_healthhaxx = true
					if (RB.PvPHEALTH > 50) and not RB.painstun --Prevent infinite Super Armor.
					and not (xs.ChaosLambent and xs.ChaosLambent.struggle)
					and not ((s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK4))
					and not RB.haymaker and not RB.RingBurstChase and not RB.counters
					and not s.XSassault
						if not ((leveltime%2) and (RB.PvPHEALTH > RBB.PvPHEALTH+5000))
							RB.PvPHEALTH = min(10000, $+1)
						end
					end
				end
				if (p.XS_chargehaxx != true)
					tp.XS_chargehaxx = false
				end
				if s.XSassault --Super Rush Assault!
					XSon.SuperRushAssault(s.XSassault, p,s,xs,RB, t, tp, xss, RBB)	
				elseif (RB.magicC2)
					RB.pressedC2 = 0
					RB.custom2 = 0
					xs.custom2 = 0					
				elseif RB.custom2 == 1 --Counter inputs.
					local MINP = 0
					if RB.pressedC2		
						if not ((RBB.MeteorSmash or 0) > 7)
							if RB.counters 
								local NUM = RB.counters.num or 0
								if (NUM < 10)
									MINP = (NUM > 7) and $+1 or (NUM > 5) and $+2 or (NUM>2) and $+3 or (NUM > 1) and $+4 or $+5
								end
							else
								MINP = $+4
							end
						end
						if (netgame==true) and (p!=server)
							if ((RBB.MeteorSmash or 0) > 7)
								MINP = 1					
							elseif not (RB.counters) or RB.counters and ((RB.counters.num or 0) < 3)
								MINP = $+3
							else
								MINP = $+2
							end
						end
						if (RB.PvPHEALTH < 15000)
							MINP = $+1
						end
						if RB.counters and RB.counters.num and (RB.counters.num > 8)
							local NUM = (RB.counters.num-2)/5
							MINP = max($-NUM, 0)
						end
					end
					if not RB.pressedC2 or (RB.pressedC2 < max(0, min(6, MINP)))
						RB.pressedC2 = 7
					end
				end
				if RB.ExplosionCD
					RB.ExplosionCD = $-1
				end
				if RB.explosion or xs.ChaosLambent and xs.ChaosLambent.ChaosControl==true or RB.SRAlunge
					if RB.explosion==2
						s.angle = R_PointToAngle2(s.x, s.y, t.x-t.momx, t.y-t.momy)
						p.drawangle = R_PointToAngle2(s.x, s.y, t.x-t.momx, t.y-t.momy)
					end
				else
					--Auto energy regeneration. Mostly based on health, catch-up mechanics, and doing cool actions.
					if (RB.PvPENERGY < 10000)
						local ADD = min(14, max(6, 18-(RB.PvPHEALTH/600)) )  --More when lower on health.
						if RB.painstun and (RB.painstun > 1)
							ADD = (ADD<15) and $+2 or $+1 --More when in hitstun
						end
						if xs.zipdash and (xs.zipdash > 3) 
						or xs.ChaosLambent
							ADD = (ADD<11) and $+2 or $+1 --More when doing cool stuff.
						end
						if (RBB.PvPHEALTH > RB.PvPHEALTH+710) --regen energy faster when you're behind.
							ADD = $+min(12, ( (RBB.PvPHEALTH-RB.PvPHEALTH) / 710) ) 
						end
						if p.XS_energyhaxx --Super regen.
							ADD = ($*8)+6
							tp.XS_energyhaxx = true
						end
						RB.PvPENERGY = max(0, min(10000, $+ADD))
						RB.Emaxed = nil
					elseif not (RB.Emaxed)
						RB.Emaxed = true
						S_StartSound(nil, sfx_xssfpu, p)
						XSon.ElectricBody(s)
						for i = 2,30,2
							local ghs = XSon.SmokeMist(s, 0, 99*s.scale)
							if ghs and ghs.valid
								ghs.spritexscale,ghs.spriteyscale = ($*i)/2, ($*i)/2
								ghs.spriteyoffset = $-(i<<16)
							end
						end
						XSon.ChargePower(s, 65, s, nil, nil, SKINCOLOR_YELLOW)
						XSon.ExpandingGlow(s, SKINCOLOR_YELLOW, 20, 100, true, AST_ADD)
					end
					if RB.BlastCharge
						if (RB.BlastCharge.LetGo)
						and (RB.PvPENERGY > 4850) 
						and not (RB.haymaker and (RB.haymaker.timer < 3))
						and not (RB.painstun and (RB.painstun>=FRACUNIT))
						and not (  (RB.PvPENERGY > 9000) and 
						( (xs.zipdash and not xs.zipdashslide) or (p.cmd.buttons & BT_CUSTOM1) )
						)
							local CL = xs.ChaosLambent 
							if not (CL and ( ((CL.init or 0) < 47) or CL.struggledone or CL.struggle or CL.ChaosControl==true) )
							and not (t.XSassault)
							and not RB.ExplosionCD
							and not ((p.cmd.buttons & BT_TOSSFLAG) and not (p.cmd.buttons & (BT_CUSTOM2|BT_CUSTOM1)))
								XSon.ChaosBlast(p,s,xs,RB, t, tp, xss, RBB)
								RB.PvPENERGY = min(4000, max($-5250,0))
								RB.BlastCharge.LetGo = 0
							end
						elseif RB.BlastCharge.C3
							--Taunting increases both party's tensions, but your foe's moreso.
							if RB.crossedarms==1 
							and (p.cmd.buttons & BT_CUSTOM3)
							and not xs.ChaosLambent and not xs.ChaosSnap and not xs.snapdivetrick
								if (RB.PvPENERGY < 9990)
									RB.PvPENERGY = max(0, min(10000, (RB.PvPENERGY<1337) and $+8 or $+6))
								end
								if (RBB.PvPENERGY < 9990) and not (RBB.cancharge and not RBB.crossedarms==1)
									RBB.PvPENERGY = max(0, min(10000, $+9))
								end
								RBB.cancharge = 40 --Basically giving your foe permission to charge.

								if ( p.XS_chargehaxx 
								or RB.cancharge
								or RB.charging
								or RBB.crossedarms==1 and (tp.cmd.buttons & BT_CUSTOM3)
								and not xss.ChaosLambent and not xss.ChaosSnap and not xss.snapdivetrick
								)
								and not ( (RB.PvPENERGY == 10000) and not RB.charging)
									RB.charging = RB.charging and $+1 or 1
									if (RB.PvPENERGY < 10000)
										local CHARGESPEED = 64
										if p.XS_energyhaxx
											CHARGESPEED = 90
										elseif p.XS_chargehaxx and not (RBB.crossedarms==1) and not RB.cancharge
											CHARGESPEED = 50
										end
										RB.PvPENERGY = min(10000, $+CHARGESPEED) --Okay, we're BOTH clearly asking for it! All in!
										if (RB.PvPENERGY >= 10000)
											S_StopSoundByID(s, sfx_xssfpk) 
										elseif not S_SoundPlaying(s, sfx_xssfpk)
											S_StartSound(s, sfx_xssfpk)
										end
									end
									XSon.sparkles(p, s, xs, 65, 10, 11)
									if (leveltime%2==0)
										local RNGCOLOR = XSLocalRNG(SKINCOLOR_SUPERRED2,SKINCOLOR_SUPERPURPLE5,s)
										local RNGX = XSLocalRNG(15,50,t)
										XS_RingBurstSparkles(s, 15, RNGCOLOR)
										local ghs = XSon.TransformGFX2(s, -380<<16) --Rising orbs
										if ghs and ghs.valid
											ghs.frame = $+FF_TRANS20
											ghs.color,ghs.colorized = RNGCOLOR,true
											ghs.spritexscale = ($*50)/RNGX
										end
										if (leveltime%8==0)
											XSon.Quake(s, 25<<16, 5)
											if (RB.PvPENERGY < 10000)
												XSon.Quake(s, 27<<16, 5)
												ghs = XS_RingBurstShine(p, s, xs)
												if ghs and ghs.valid
													ghs.scale = $*4/3
													ghs.spriteyoffset = $+6<<16
													ghs.scalespeed = ($*50)/max(25, RNGX+5)
													ghs.color,ghs.colorized = RNGCOLOR,true
													ghs.frame = $+FF_TRANS10
												end
											else
												XSon.Quake(s, 20<<16, 5)
											end
											ghs = XSon.ElectricBody(s, 9, SKINCOLOR_SKY)
											ghs.frame = ($&~FF_TRANSMASK)|((leveltime%16==0) and FF_HORIZONTALFLIP or 0)
											ghs.destscale = 99<<16
											ghs.dispoffset = 146
											ghs.scalespeed = ($*70)/RNGX
											ghs.blendmode = AST_TRANSLUCENT
											ghs.sttfade = 2
										end
									end
								elseif RB.charging
									RB.charging = nil
								end	
							end
						end
					end
				end
			end
		end
	end
	if RB.autoinput
		RB.autoinput = 0
	end
	if RB.windlines --Force extra huge wind Lines.		
		RB.windlines = $-1
		local MINXY = FixedDiv(FixedMul(max(52*s.scale, p.speed), 4), s.scale)
		if not RB.stripes
			XSon.FlightWindLines(s, 211*s.scale, {
			XY = XSLocalRNG(MINXY/2, MINXY*3,RB.LockOn or s)*s.scale,
			EZ = XSLocalRNG(222,-200,s)*s.scale+(s.momz*4) },
			FF_TRANS10|FF_FULLBRIGHT, 2<<16, 14, SKINCOLOR_RUBY, nil, true)
		end
		XSon.FlightWindLines(s, 600*s.scale, {
		XY = XSLocalRNG(MINXY, MINXY*4,s)*s.scale,
		EZ = XSLocalRNG(-400,460,RB.LockOn or s)*s.scale+(s.momz*8) },
		FF_TRANS10|FF_FULLBRIGHT, 2<<16+XSLocalRNG(2555,124000,s), 15, SKINCOLOR_XSSUPERBLUR, nil, true)
	end	
	if RB.speedlines
		XS_RingBurstStripes(s, nil, AST_ADD, 40<<16)
		RB.speedlines = max($-1,0)
		RB.stripes = 0
	elseif RB.stripes
		RB.stripes = $-1
		if (p.speed > 12<<16) and not RB.painstun and not xs.ChaosSnap
			XS_RingBurstStripes(s, nil, AST_ADD, 40<<16)
			if (RB.stripes%3==0)
				XS_RingBurstDust(p, s, xs, 500*s.scale)	
			end
		end
	end	
	if RB.forcetrail
		RB.forcetrail = $-1
		XSZipTrail(s, nil, 1)
	end	
	--Zipdash specific modifications.
	if xs.zipdash
		xs.jump = RB.jump
		xs.spin = RB.custom1
		xs.custom1 = RB.custom1
		xs.custom3 = 0
		p.xsringburst = nil
	--	if xs.zipdash == 5 not xs.zipdashslide
	--		RB.dualpress = 0
	--	end
		if (xs.zipdash > 4)
			if ((p.cmd.buttons & BT_JUMP) or RB.truespin)
				RB.modifiedaim = true
				p.aiming = (RB.truespin) and -ANG20 or 0
				if (xs.direct < 5) and (abs(s.momz) > 16*s.scale)
					RB.ziprise = RB.ziprise and $+1 or 1 --Transition diagional ascend/descend into a vertical.
					if (RB.ziprise > 5)
						XSZipDashCancel(p, s, xs)
						xs.zipdash = 0 xs.zipdashslide = 0 xs.zipcontinue = nil
						RB.rocketjump = 3 RB.rocketspin = 3
						RB.jump = (p.cmd.buttons & BT_JUMP) and 1 or $ 
						RB.spin = (RB.truespin) and 1 or $
						RB.rocket = (p.cmd.buttons & BT_JUMP) and 1 or -1
						p.aiming = 0
						P_SetObjectMomZ(s, (p.cmd.buttons & BT_JUMP) and 4<<16 or -5<<16, true)
						XSZipTrail(s, nil, 1)
					end
				else
					RB.ziprise = 0
					if not RB.rocket and (RB.truespin > 8) --Shoot down!
					and (s.momz*ZFLIP < -30*s.scale)  and (s.momz*ZFLIP > -45*s.scale) and (p.cmd.forwardmove > 45)
						RB.rocketspin = 2 RB.truespin = 1 RB.spin = 1 RB.rocket = -1
					end
				end	
			end
			if xs.zipdash == 5
				RB.holdcam = 1
				RB.dualpress = 0
				if RB.modifiedaim
					p.aiming = 0 RB.modifiedaim = nil p.viewrollangle = 0
				end
				RB.ziprise = 0
			elseif xs.zipdash == 25 or xs.zipdash == 50 
				if xs.zipdashcamspeed != nil
					RestoreZipDashCam(p, s, xs)
				end
			end
		end
	else --Always change these things for the player!
		if xs.zipdashslide or xs.zipcontinue or RB.ziprise
			XSZipDashCancel(p, s, xs)
			xs.zipdash,xs.zipdashslide = 0,0 
			xs.zipcontinue = nil
			RB.ziprise = 0 
		--	RB.dualpress = 0
		end
		if RB.modifiedaim
			p.aiming = 0
			RB.modifiedaim = nil
		end
		if not p.xswallrun
			if not xs.snapdivetrick and not xs.snapdive
				xs.ziptrailticket = 0
			elseif (xs.snapdivetrick > 12) and (s.spritexscale < FRACUNIT)
				s.spritexscale = min($+2500, FRACUNIT)
				s.spriteyscale = max($-500, FRACUNIT)
			end
			if xs.ChaosSnap and (xs.ChaosSnap.timer > 5) or xs.snapdivetrick and (xs.snapdivetrick < 11) or xs.snapdive
				xs.jump,xs.spin,xs.custom1,xs.custom3 = RB.jump or 0,RB.spin or 0,RB.custom1 or 0,99<<16 --Lock these controls off.
			else
				xs.jump,xs.spin,xs.custom1,xs.custom3 = 99<<16,99<<16,99<<16,99<<16 --Lock these controls off.
			end	
			local FTAP = (p.cmd.forwardmove > 8) and 1 or (p.cmd.forwardmove < -8) and -1 or 0
		
			--Record a tap input? If it's 9, then we tapped 2x!
			if RB.tapped
				RB.tapped = $-1
			end
			if (xs.direct < 10)
				RB.recordtap = 5
				if RB.rocket == 2
					RB.rocket = 0
				end
			end
			if RB.recordtap
				RB.recordtap = $-1
				if (xs.direct >= 34)
					if RB.tapped and (FTAP == RB.FTAP or FTAP == 0)
						RB.tapped = 10
						xs.direct = 50
					else
						RB.tapped = 8
					end
					RB.FTAP = FTAP
					RB.recordtap = 0
				end
			end
		end
	end
	xs.fallbuffer,xs.fallbuffer2,xs.framejump,xs.jumpboost,xs.falltransition = 0,0,0,0,0
	xs.rollcd,xs.dustcd = 2,2
	p.pflags = $|PF_JUMPDOWN & ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_STARTJUMP|PF_STARTDASH)
	
	if RB.rocketjump RB.rocketjump = $-1 end 
	if RB.rocketspin RB.rocketspin = $-1 end
	--Minor rocket burst, not nearly as major as the Ring Burst's.
	local DOROCKET = false
	if RB.jump == 1 or RB.truespin == 1
		if RB.jump == 1
			if RB.rocketjump
				DOROCKET = 1
			else
				RB.rocketjump = 8	
			end
			RB.rocketspin = 0
		elseif RB.truespin == 1
			if RB.rocketspin
				DOROCKET = -1
			else
				RB.rocketspin = 8
			end
			RB.rocketjump = 0
		end
	end
	if DOROCKET != false and not RB.noZcontrol
		XSon.Quake(s, 64<<16, 4)
		if (p.cmd.buttons & BT_JUMP)
			XSon.DeTransformGFX(p,s,xs, "ghosts")
		end
		if xs.zipdash
			local ghs = XSon.RadiantLight(s, nil, 9, nil, S|FF_TRANS40)
			ghs.spriteyoffset = $+14<<16
			ghs.sttfade = true ghs.ditchtracer = nil
		end
		RB.noinput = 0
		RB.rocketjump,RB.rocketspin = 0,0
		S_StartSoundAtVolume(s, sfx_kc5b, 80)
		S_StartSoundAtVolume(s, sfx_kc4d, 70)
		DOROCKET = $*ZFLIP
		RB.rocket = DOROCKET
		if not (RB.LockOn and RB.LockOn.valid)
			RB.dualpress = 0
		end
		local MULT = xs.zipdash and 2 or 1
		if DOROCKET == -1
			local ZMOMMULT1 = FixedDiv(FixedMul(75*s.scale, RB.zmult), 100)
			local ZMOMMULT2 = FixedDiv(FixedMul(22*s.scale*MULT, RB.zmult), 100)
			s.momz = min(0, max(-ZMOMMULT1, $-ZMOMMULT2))
			if not xs.zipdash
				P_SetObjectMomZ(s, -8<<16, true)
			end
		else
			local ZMOMMULT1 = FixedDiv(FixedMul(75*s.scale, RB.zmult), 100)
			local ZMOMMULT2 = FixedDiv(FixedMul(22*s.scale*MULT, RB.zmult), 100)				
			s.momz = min(ZMOMMULT1, $+ZMOMMULT2)
			s.momx = $*2/3 s.momy = $*2/3
		end
		if (xs.direct > 20) and ((abs(p.cmd.sidemove) > 20) or (p.cmd.forwardmove < 0))  --Initial burst of speed!
			local DIRECT = min(50, (abs(p.cmd.sidemove)+p.cmd.forwardmove*-1) )
			P_Thrust(s, xs.turnang, ((p.speed < 77*s.scale) and (RB.init > 12)) and DIRECT*MULT*s.scale/3 or DIRECT*MULT*(s.scale/4))
		end
		s.spritexscale,s.spriteyscale = FRACUNIT-20000,FRACUNIT+35000
		s.spriteyoffset = -7*DOROCKET<<16
		s.XSstretch = {timer = 6, Xspeed = 3500, Yspeed = 5000}	
		
		XSon.zwind(s, 2)
		
		--GFX
		local ghs = XSon.TransformGFX(s)
		ghs.frame = $+2+FF_TRANS20
		ghs.fuse = $/2 ghs.sttfade = true
		ghs.sttspeed = 1
		ghs.rollangle = (DOROCKET == 1) and $+ANGLE_180 or $
		
		XSSpawnDust(s, nil, nil, true)
		
		local ghs = XS_CircleWave(s, 2, SKINCOLOR_SUPERGOLD5, 12, FRACUNIT+15000)
		ghs.frame = $+4<<16

		local ghs = XS_CircleWave(s, 2, nil, 12, FRACUNIT+15000)
		ghs.sprite = SPR_XSG1
		ghs.renderflags = RF_FLOORSPRITE|RF_NOSPLATROLLANGLE|RF_NOSPLATBILLBOARD|RF_ABSOLUTEOFFSETS|RF_NOCOLORMAPS|RF_FULLBRIGHT
		ghs.flags2 = $|MF2_SPLAT
		ghs.momx = s.momx/3
		ghs.momy = s.momy/3
		ghs.tracer = nil
		ghs.spritexoffset = 59<<16
		ghs.spriteyoffset = 56<<16
		ghs.frame = R|FF_TRANS50
		ghs.z = $+(s.momz)+(64*s.scale)
		P_CreateFloorSpriteSlope(ghs)
		ghs.floorspriteslope.o = {x = s.x, y = s.y, z = s.z}
		ghs.floorspriteslope.xydirection = s.angle
		ghs.floorspriteslope.zangle = -ANG20*2				
	end
	if RB.rocket == 2 --Disable horizontal boost mode?
		if (xs.direct < 20) or (p.speed < 18*s.scale) or xs.zipdash
			RB.rocket = 0
		end
	end
	if (p.cmd.buttons & (BT_JUMP|BT_SPIN)) and not xs.ChaosLambent --Adjust flying cam.
		if (p.speed+(abs(s.momz)/2) > 30*s.scale)
			if (xs.direct > 26) or abs(RB.rocket) == 1
				p.XSscalecam = 7 --Adjust flying cam!
				if RB.truespin and xs.zipdash
					p.camerascale = (p.pflags & PF_ANALOGMODE) and FRACUNIT+41000 or (2<<16)+63000
				else
					p.camerascale = (p.pflags & PF_ANALOGMODE) and FRACUNIT+25000 or (2<<16)+25000
				end
				if (xs.direct < 4) 
				and not (xs.snapdive or xs.snapdivetrick or p.xswallrun or xs.zipdash)
					xs.ziptrailticket = 2
					XSZipTrail(s, nil, 1)
				end
			end
		end
	end
	--Horizontal control.
	if (xs.direct > 4) and not xs.zipdash and not p.xswallrun --Thrust forwards?
	and not RB.noXYcontrol
		local trueturn = (XSon.MOMANGLE(s)-xs.turnang) 
		if trueturn != 0 trueturn = abs($/ANG1) end	
		
		if (p.speed < 150*s.scale)
			if (p.speed > 38*s.scale) or ((RB.tapped == 9 or RB.tapped == 10) and (xs.direct > 18))
				local THRUST = (p.speed < 62*s.scale) and s.scale/2
				or (p.speed < 71*s.scale) and s.scale-(s.scale/4)
				or (p.speed < 85*s.scale) and s.scale
				or (p.speed < 97*s.scale) and s.scale*4/3
				or (p.speed < 108*s.scale) and s.scale*3/2
				or s.scale*4/3
				if RB.tapped == 9 or RB.tapped == 10 --Minor burst forwards.
					THRUST = (p.speed < 70*s.scale) and s.scale*33 or (p.speed < 110*s.scale) and s.scale*24 or s.scale*12
					RB.tapped,RB.recordtap = 0,0
					S_StopSoundByID(s, sfx_xssfp5)
					S_StopSoundByID(s, sfx_xssfp4)
					S_StopSoundByID(s, sfx_thok)
					if not S_SoundPlaying(s, sfx_xssfp5)
						XSon.Sound(s, sfx_xssfp5)
						XSon.Sound(s, sfx_thok, 20)
					end
					XSon.Quake(s, 64<<16, 4)
					
					local ghs = XS_CircleWave(s, (yu.speed) and 1 or 2, s.color, 10, (yu.speed) and FRACUNIT+5111 or 2<<16)
					ghs.frame = E|FF_TRANS70
					ghs.spriteyoffset = $-20<<16
					local RNG1 = XSon.RNG(3,10,s)
					XSon.sparkles(p,s,xs, RNG1*3, RNG1, (XSon.RNG(-14,14,s)) or 8, (s.momz*2/3)+7<<16, (xs.darkphase) and SKINCOLOR_LAVENDER or SKINCOLOR_SUPERSKY4, (not xs.darkphase) and AST_ADD or AST_TRANSLUCENT, FF_TRANS20, 32-RNG1)
					
					for i = 32,46,13
						local ghs = XS_RingBurstWave(s, 11, (i==32) and SKINCOLOR_SUPERGOLD2 or SKINCOLOR_SUPERSKY3)
						ghs.ditchtracer = nil
						ghs.sttfade = true
						ghs.GSoffsetX,ghs.GSoffsetY,ghs.GSoffsetZ = i*s.scale,i*s.scale,(i/2)*s.scale
						ghs.spritexscale = (i == 32) and $*2/3 or $/2
						ghs.spriteyscale = ghs.spritexscale
					end
					
					s.spritexscale,s.spriteyscale = FRACUNIT+20000,FRACUNIT-26000 --stretch effect.
					s.spriteyoffset = 7<<16
					s.XSstretch = {timer = 6, Xspeed = 3000, Yspeed = 3200}	
					
					if RB.xymult != 100	
						THRUST = FixedDiv(FixedMul($, RB.xymult), 100)
					end				
					if (p.speed+THRUST <= 50*s.scale)
						P_InstaThrust(s, xs.turnang, 50*s.scale)
					else
						P_Thrust(s, xs.turnang, THRUST)
					end
					xs.hudboost = 2
					RB.rocket = 2
					RB.noinput = 0
					if not (RB.Lockon and RB.LockOn.valid)
						RB.dualpress = 0
					end
					XSon.DeTransformGFX(p,s,xs, "ghosts")
					XS_RingBurstDust(p, s, xs, 200*s.scale)
					XSon.sparkles(p,s,xs, 50, 3, 3, 4<<16, SKINCOLOR_SUPERGOLD4, AST_ADD)
					XS_RingBurstStripes(s, s.color, AST_ADD, -16<<16, nil, p.drawangle)
					XS_MediumDust(s, SKINCOLOR_SUPERGOLD4, AST_ADD, Q|FF_TRANS60)
				else
					if (abs(p.cmd.sidemove) > 12) and (trueturn > 16)
						THRUST = FixedDiv(FixedMul($, 38+abs(p.cmd.sidemove)),50)
					end
					local XYMULT = (RB.xymult) or 100
					if RB.rocket == 2
						XYMULT = $*4/3 --Extra speed!
					end
					if XYMULT != 100
						THRUST = FixedDiv(FixedMul($, RB.xymult), 100)
					end
					P_Thrust(s, xs.turnang, THRUST)				
				end
			end
		end
		if (p.speed > 16*s.scale)
			if (trueturn > 24)
				XSon.FreeFlightVector(s, xs.turnang, max(1, (trueturn/20)), (trueturn > 64) and 3 or 4)
			end		
		elseif not P_IsObjectOnGround(s)
			P_Thrust(s, xs.turnang, (s.scale/40)*xs.direct)
		end		
	else
		if xs.zipdash
			--
		elseif p.xswallrun
			RB.rocket = 0
		elseif p.speed and not RB.noXYcontrol --Even out speed if not pressing anything.
			if (p.speed <= s.scale*2)
				s.momx,s.momy = 0,0
			else
				local BRAKE = max(51, 55-xs.direct)
				s.momx = FixedDiv(FixedMul($,50),BRAKE)
				s.momy = FixedDiv(FixedMul($,50),BRAKE)
			end
		end
		if (p.speed <= 24*s.scale) and not RB.rocket and not (p.XSscalecam and p.camerascale > FRACUNIT) and (yu.init > 9)
		and not p.xswallrun and not xs.ChaosLambent  --Adjust flying cam gradually.
			p.XSscalecam = 7
			p.camerascale = (p.pflags & PF_ANALOGMODE) and FRACUNIT-8000 or FRACUNIT-5000	
		end
	end

	--Vertical control.
	if not ((p.cmd.buttons & BT_JUMP) or RB.truespin or (xs.zipdash and xs.zipdash < 7))
	or (RB.spin >= 99<<16) and (p.cmd.buttons & BT_SPIN)
	or (RB.jump >= 99<<16) and (p.cmd.buttons & BT_JUMP)
	or (abs(s.momz) >= 75*s.scale) --Reached the max Z speed...
		if not p.xswallrun and not RB.noZcontrol
			if (abs(s.momz) > s.scale)
				if xs.zipdash
					s.momz = FixedDiv(FixedMul($, 8), 9)
				elseif (abs(s.momz) > 22*s.scale) or (abs(s.momz) < 2*s.scale)
					s.momz = FixedDiv(FixedMul($, 14), 15) --Divide momz
				else
					s.momz = FixedDiv(FixedMul($, 17), 18)
				end
			elseif s.momz
				s.momz = 0
				S_StopSoundByID(s, sfx_xssfp3) 
				S_StopSoundByID(s, sfx_xssfp4)
				if (p.speed < s.scale)
					S_StopSoundByID(s, sfx_xssfp5)
				end
			end
			if P_IsObjectOnGround(s)
				if not RB.truespin and (s.momz*ZFLIP >= 0)
					s.z = $+(s.scale*ZFLIP)
					P_SetObjectMomZ(s, FRACUNIT+35000, true)
				end
			else
				yu.landing = 0
			end
		end
		if abs(RB.rocket) == 1
			RB.rocket = 0
		end
		
	elseif (abs(s.momz) < 75*s.scale) and not RB.noZcontrol --Rise or fall!
		
		local MOMZSPEED = (abs(s.momz) < 16*s.scale) and FRACUNIT+40000 
		or abs(RB.rocket) == 1 and max(9999, (120000)-(FixedInt(abs(s.momz))*1250) )
		or max(999, (115000)-(FixedInt(abs(s.momz))*2500) )
		local SHIFT = (RB.zmult != 100) and FixedDiv(FixedMul(MOMZSPEED,RB.zmult), 100) or MOMZSPEED
		if xs.zipdash 
			SHIFT = $*5 
			if RB.rocket == 2
				RB.rocket = 0 --Nyoom
			end
		elseif abs(RB.rocket) == 1
			SHIFT = $*4/3
		end
			
		if (p.cmd.buttons & BT_JUMP) --Going up!
			if (s.momz*ZFLIP < 0) --Cancel bad momentum over time!
				if (abs(s.momz) > 25*s.scale) or (abs(s.momz) < 3*s.scale)
					s.momz = FixedDiv(FixedMul($, 100), 120)
				else
					s.momz = FixedDiv(FixedMul($, 100), 111) --Divide momz
				end				
			end
			if RB.jump == 1 --Initial speed shift.
				S_StopSoundByID(s, sfx_xssfp4)
				if not S_SoundPlaying(s, sfx_xssfp3)
					XSon.Sound(s, sfx_xssfp3,180)
				end
				if (yu.init > 8) and (s.momz*ZFLIP < -6*s.scale) and not P_IsObjectOnGround(s)
					P_SetObjectMomZ(s, 6*SHIFT, true)
				end
				if xs.zipdash
					XSon.SonicBoom(p, s, xs, 10)
				end
				RB.spin = 0	
				RB.truespin = 0
			end
			s.momx = FixedDiv(FixedMul($,101),102)
			s.momy = FixedDiv(FixedMul($,101),102)		
			P_SetObjectMomZ(s, SHIFT, true) --rise!
			yu.landing = 0
			
		elseif (p.cmd.buttons & BT_SPIN)
			
			--Prep for landing?
			if P_IsObjectOnGround(s)
				if RB.truespin and not xs.zipdash and not xs.ChaosLambent and not (RB.LockOn and RB.LockOn.valid)
					yu.landing = (xs.direct < 23) and $+4 or $+1
					p.aiming = 0
				end
				if RB.rocket == -1 RB.rocket = 0 end
			else
				local GROUNDHALT = false
				s.momx = FixedDiv(FixedMul($,101),102)
				s.momy = FixedDiv(FixedMul($,101),102)
				yu.landing = 0
				if (s.momz*ZFLIP > 0)
					if (abs(s.momz) > 25*s.scale) or (abs(s.momz) < 3*s.scale)
						s.momz = FixedDiv(FixedMul($, 100), 120)
					else
						s.momz = FixedDiv(FixedMul($, 100), 111) --Divide momz
					end
				elseif xs.zipdash and not p.xswallrun and (s.momz*ZFLIP < 0)
					local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
					if (abs((s.z+s.momz)-FLOORZ) < 16*s.scale)
						s.momz = s.scale/99*ZFLIP
						GROUNDHALT = true
					end	
				end
				if RB.spin == 1 --Initial descending speed shift.
					if RB.truespin
						S_StopSoundByID(s, sfx_xssfp3)
						if not S_SoundPlaying(s, sfx_xssfp4)
							XSon.Sound(s, sfx_xssfp4, 150)
						end
					end
					if (yu.init > 8) and (s.momz*ZFLIP > 6*s.scale) and (GROUNDHALT!=true)
						P_SetObjectMomZ(s, -6*SHIFT, true)
					end
					if xs.zipdash
						XSon.SonicBoom(p, s, xs, 11)	
					end
					RB.jump = 0					
				end
				if RB.truespin and (GROUNDHALT!=true)
					P_SetObjectMomZ(s, -SHIFT, true) --Descend!		
				end
			end
		end
	end
	--GFX!
	--Flight creates dust clouds on nearby floors.
	if (leveltime%6==0) and ((abs(s.momz) > s.scale) or (p.speed > 3*s.scale))
	or (p.speed > 50*s.scale) and (leveltime%2) and not xs.ChaosSnap
		local ghs = XS_RingBurstDust(p, s, xs, p.speed/2 + 125*s.scale)
		if ghs and ghs.valid
			if RB.lastdust
				ghs.fuse = 12
				RB.lastdust = nil
			else
				ghs.blendmode = AST_TRANSLUCENT
				ghs.color = SKINCOLOR_BEIGE
				ghs.colorized = true
				ghs.fuse = 15
				RB.lastdust = true
			end
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS80
			ghs.sttspeed = 3
			ghs.momx = s.momx/-3
			ghs.momy = s.momy/-3
			ghs.spritexscale = FRACUNIT-(XSLocalRNG(8000, 33000, s))
		end
	end
	--Generate wind effects when flying.
	if (p.speed > 48*s.scale)
	or ((xs.zipdash > 5) or p.panim == PA_PAIN) and (p.speed > 30*s.scale)
		if not (xs.direct == 0 and (xs.zipdash < 6))
		and not (p.xswallrun or xs.zipdashslide or xs.ChaosSnap or xs.ChaosLambent or p.xsringburst)
--			XSon.FlightWindLines(s, DIST, OFFSETS, TRANS, CSCALE, FUSE, COLOR, FRAME)
			XSon.FlightWindLines(s, 120*s.scale, nil, (leveltime%2) and FF_TRANS30 or FF_TRANS40, FRACUNIT, 10, SKINCOLOR_SILVER)
			
			local MINXY = FixedDiv(FixedMul(p.speed, 4), s.scale)
			XSon.FlightWindLines(s, 320*s.scale, {
			XY = XSLocalRNG(MINXY, MINXY*3,s)*s.scale,
			EZ = (s.momz<6*s.scale) and XSLocalRNG(-99+s.momz/s.scale,140+s.momz/s.scale,s)*s.scale or XSLocalRNG(-180,124,s)*s.scale },
			(leveltime%2) and FF_TRANS50 or FF_TRANS40, FRACUNIT+XSLocalRNG(0,35000,s), XSLocalRNG(15,22,s), s.color)
		end
	end
	--Super Sonic style flight sparkles and radiance
	if (leveltime%3==0)
		local FLYSPEED = (abs(s.momz)*3/2)+(p.speed*2/3)
		if ((FLYSPEED > 20*s.scale) or RB.startflames) and not xs.ChaosSnap
			local MULT = (FLYSPEED > 70*s.scale) and 3 or (FLYSPEED > 45*s.scale) and 2 or 1
			if MULT == 3 or (leveltime%6==0) or RB.startflames
				local ADD = (p.cmd.buttons & BT_JUMP) and -22<<16 or (p.cmd.buttons & BT_SPIN) and 32<<16 or 2<<16
				local RNG1 = min(7, P_RandomRange(1,3*MULT))
				local COLOR = (leveltime%12==0) and SKINCOLOR_SILVER or XSon.RNG(SKINCOLOR_SUPERGOLD1, SKINCOLOR_SUPERGOLD4, s)
				XSon.sparkles(p,s,xs, RNG1+12, 7, 1, (s.momz/4)+ADD, COLOR, AST_ADD, XSon.RNG(3,RNG1+1,s)<<16, XSon.RNG(18,32,s))
				XS_RingBurstSparkles(s, 22, COLOR, 45)	
			end		
		end
		if (leveltime%6==0) --Glow brightly from far away for other players!
		and ((FLYSPEED > 57*s.scale) or RB.startflames or xs.ChaosLambent or xs.ChaosSnap)
			local ghs = XSon.RadiantLight(s, nil, 7, nil, S|FF_TRANS50)
			ghs.spriteyoffset = $+14<<16
			ghs.sttfade = 3 ghs.ditchtracer = nil
			if xs.ChaosSnap or xs.ChaosLambent
				ghs.color = (xs.ChaosSnap) and SKINCOLOR_SUPERSKY5 or SKINCOLOR_FLAME 
			end
			for z in players.iterate
				if z == displayplayer
					if z == p
						if not xs.zipdashcamspeed
						and not (xs.zipdash and (xs.zipdash < 18))
							ghs.flags2 = MF2_DONTDRAW
						end
					elseif z.mo and z.mo.valid
					and FixedHypot( FixedHypot(z.mo.x-s.x, z.mo.y-s.y), z.mo.z-s.z) < 580*s.scale
						ghs.flags2 = MF2_DONTDRAW
					end
				end
			end	
		end			
	end
	if RB.lambentCD and not xs.ChaosLambent
		RB.lambentCD = $-1
		if xs.zipdash and (xs.zipdash > 5) or RB.painstun and (RB.painstun>2) 
		or RB.haymaker or RB.RingBurstChase or xs.ChaosSnap 
			RB.lambentCD = 0
		end
	end
	if RB.RingBurstChase
		XSon.RingBurstChase(p,s,xs, RB)	
	elseif RB.haymaker		
		RB.haymaker.timer = $+1
		if (RB.haymaker.timer > 26) --Can't follow up anymore!
			local SFX = (xs.FourKidsRB) and sfx_xssfpz or sfx_xssfxx
			S_StopSoundByID(s, SFX)	
			RB.haymaker = nil
			if not RB.refreshC1 and not RB.painstun and not xs.ChaosLambent
				RB.refreshC1 = (xs.stopstick) and 2 or 1
			end
		elseif RB.LockOn and RB.LockOn.valid
			RB.custom1 = 99<<16
			XSon.TackleFollowUp(p,s,xs,RB,RB.LockOn) --Can do a followup!
		end
	end
	if RB.cantsnap --As in, you can't be snapped TO.
		RB.cantsnap = max($-1,0)
	end
	if RB.startflames --Flight startup flames.
		local FF = RB.startflames
		local COLORTABLE = {SKINCOLOR_BONE, SKINCOLOR_SUPERSKY5, SKINCOLOR_SUPERGOLD3, SKINCOLOR_SUPERORANGE5, SKINCOLOR_FLAME, SKINCOLOR_RUBY, SKINCOLOR_SILVER, SKINCOLOR_WHITE, SKINCOLOR_SUPERSKY3, SKINCOLOR_SUPERORANGE4}
		local ghs = XSon.FireBall(s, COLORTABLE[max(10, FF/2)], max(555, FRACUNIT-(FF*1555)), (leveltime%2) and FRACUNIT/11 or FRACUNIT/6, XSLocalRNG(5,9,s)*FF_TRANS10)
		if ghs and ghs.valid
			if (leveltime%2)
				ghs.frame = $|FF_HORIZONTALFLIP
				ghs.spritexscale = $*3/4 ghs.spriteyscale = $*4/3
			else
				ghs.spritexscale = $*4/3 ghs.spriteyscale = $*3/4
				ghs.color = s.color
				P_SetObjectMomZ(ghs, 11<<16, true)
			end
			ghs.momx,ghs.momy,ghs.momz = $+s.momx,$+s.momy,$+s.momz
			P_SetOrigin(ghs, 
			ghs.x+FixedMul(10*FF*s.scale, cos(s.angle+(FF*ANG1*21))),
			ghs.y+FixedMul(10*FF*s.scale, sin(s.angle+(FF*ANG1*21))), 
			ghs.z+(XSLocalRNG(-50,99,s)*s.scale) )
		end
		RB.startflames = $-1
	end
	if RB.forcesnap --Allows snap during painstun.
		RB.forcesnap = $-1
	end
	if RB.snapCD --Absolutely CANNOT snap right now.
		RB.snapCD = max($-1,0)
	end
	if RB.custom2 == 1 and not xs.ChaosSnap  --Try a Chaos Snap.	
	and not (xs.ChaosLambent and xs.ChaosLambent.firing)
		if not (RB.painstun and (RB.painstun > 10) and not RB.forcesnap)
		and not p.xswallrun --Allow Chaos Snap with custom2.
		and not ( (s.state>=S_PLAY_XKICK1) and (s.state<=S_PLAY_XKICK4) and (xs.snapdivekick or xs.snapdive or xs.snapdivetrick) )
		and not (RB.SnapOverHeat > 32) 
		and not RB.snapCD
			local IFRAMES = min(16, max(1, 16-RB.SnapOverHeat))
			if xs.zipdash
				XSZipDashCancel(p, s, xs, true) xs.jump = 1	
			end
			XSStartSnapDive(p, s, xs)
			if xs.ChaosSnap --We went into Chaos Snap! Give iframes?
				RB.DodgeFrames = max($, IFRAMES)
				RB.forcesnap = nil
			end
		end
	end	
	
	local SPRITE2 = (s.sprite2 & ~FF_SPR2SUPER)
	
	--Set proper animation unless these conditions are met! Some are called from a PostThinkFrame later.
	if xs.snapdive or xs.snapdivetrick or xs.snapdivekick 
	or (s.state >= S_PLAY_XSNAPDIVE) and (s.state <= S_PLAY_XKICK4)
	or p.xswallrun 
	or (xs.ChaosLambent and (xs.ChaosLambent.init < 50))
	or (s.state >= S_PLAY_S_XTRANSFORM) and (s.state <= S_PLAY_XZIPBRAKESUPER)
	or P_IsObjectOnGround(s) // and (s.state == S_PLAY_EDGE or s.state == S_PLAY_XZIPSLIDE2 or s.state == S_PLAY_XZIPSLIDE)
	or (p.panim >= PA_ABILITY2)
	or xs.zipdash and (xs.zipdash < 7)
	or SPRITE2 == SPR2_GRND 
	or SPRITE2 == SPR2_PAIN or SPRITE2 == SPR2_STUN or (RB.painstun and RB.painstun != 1)
	or s.state == S_PLAY_PAIN or s.state == S_PLAY_STUN
	or s.state == S_PLAY_GASP or P_PlayerInPain(p)
		if SPRITE2 == SPR2_PAIN or SPRITE2 == SPR2_STUN or RB.painstun or P_PlayerInPain(p)
			if not P_IsObjectOnGround(s)
				s.sprite2 = SPR2_PAIN|FF_SPR2SUPER
				s.frame = $ & ~FF_FRAMEMASK
				if (s.tics < 5) s.tics = 5 end				
			end
			if not RB.painstun or (RB.painstun > FRACUNIT-2)		
				s.spritexscale,s.spriteyscale = FRACUNIT+25000,FRACUNIT-15000
				s.spriteyoffset = 10<<16					
				s.XSstretch = {timer = 8, Xspeed = 2600, Yspeed = 1800}					
				
				XSon.ElecticBody(s, 32, SKINCOLOR_CRIMSON, FF_HORIZONTALFLIP|FF_TRANS10, AST_TRANSLUCENT) 
				
				local ghs = XSon.FlickerLight(s, nil, 9, nil, (leveltime%2) and FF_HORIZONTALFLIP|FF_FULLBRIGHT or FF_FULLBRIGHT, 2<<16+25000)
				ghs.spriteyoffset = $+6<<16
				
				ghs = XSon.SuperSonicWind(s, (leveltime%2) and SKINCOLOR_GARNET or SKINCOLOR_KETCHUP, nil, nil, FRACUNIT-1200)
				ghs.dispoffset = 122
				ghs.scalespeed = 6900
				ghs.sttspeed = 2
				ghs.fuse = $*3/4
				ghs.frame = $+1
				
				ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSTRACKER2)
				if ghs and ghs.valid
					ghs.target = s ghs.SteamTrail = 1	
				end	
				RB.noXYcontrol,RB.noZcontrol = 20,25
				RB.painstun = 30
				if RB.SnapOverHeat
					RB.SnapOverHeat = $/2
				end
			end	
		end
		if not xs.zipdash
			RB.riseanimtimer,RB.rocket = 0,0
			if not (RB.painstun and (RB.painstun>5))
				RB.dualpress = 0
			end
		end
		yu.forceanim = nil
		return --Our animation system is overridden by these!
	end
	--Try out the special beam finisher against SA2's FinalHazard!
	if not (xs.ChaosSnap and (xs.ChaosSnap.timer > 5))
	and not (xs.zipdash and (xs.zipdash > 6) and not xs.zipdashslide)
	and not (xs.snapdivetrick and (xs.snapdivetrick < 13)) 
	and (RB.init > 3)	
		if (RB.attack != 1) and RB.truespin and (p.cmd.buttons & BT_JUMP) 
			if RB.jump == 1 and (RB.truespin < 4)
			or RB.truespin == 1 and (RB.jump < 4)
				if not RB.custom1 and not RB.custom2 and not RB.custom3
					RB.dualpress = (RB.LockOn and RB.LockOn.valid) and 5 or 3
				end
			end
		elseif RB.dualpress or (RB.attack == 1)
			RB.dualpress = max($-1,0)
			if RB.attack != 1 and ( (p.cmd.buttons & BT_CUSTOM1) and RB.custom1 == 1 or RB.custom2 == 1 )
				RB.dualpress = 0
			elseif RB.attack == 1
			or RB.dualpress and not (p.cmd.buttons & BT_JUMP) and not RB.truespin
				if not RB.lambentCD
					xs.ChaosLambent = {frame = A}
					RB.dualpress = 0
					RB.noZcontrol,RB.noXYcontrol = 50,50
					RB.SnapOverHeat = 0
					XSon.ChaosLambent(p,s,xs,xs.ChaosLambent) return true --
				end
			end
		end
	end
	
	--Now let's work animations!
	if RB.shock or RB.explosion
		RB.forceanim = "idle"
		RB.riseanimtimer,RB.frame = 0,A
	elseif (abs(s.momz) > max(p.speed/5, 15*s.scale)) and (p.cmd.buttons & (BT_SPIN|BT_JUMP)) --Use vertical animations?
	and not ( (p.cmd.buttons & BT_CUSTOM3) and (p.speed < (p.runspeed*3/2)) )
		if not yu.riseanimtimer or not yu.forceanim or yu.forceanim == "idle" yu.frame = A end
		yu.riseanimtimer = (xs.zipdash) and 2 or 10
		local RISESPEED = abs(s.momz)+p.speed 
		yu.maxtics = (RISESPEED > 45*s.scale) and 1 or (RISESPEED > 36*s.scale) and 2 or (RISESPEED > 25*s.scale) and 3 or 4
		if (s.momz*ZFLIP<=0)
			yu.forceanim = "descend"
			if s.state != S_PLAY_FALL
				s.state = S_PLAY_FALL
			end
		else
			yu.forceanim = "rise"
			if s.state != S_PLAY_SPRING
				s.state = S_PLAY_SPRING
			end
		end
	else
		if yu.riseanimtimer 
			if yu.riseanimtimer --Stay in rise/descend animation until this runs out.
				yu.riseanimtimer = max($-1,0)
			end		
			if P_IsObjectOnGround(s) or not yu.riseanimtimer
				yu.riseanimtimer = 0 yu.frame = A yu.forceanim = nil
			end
		end
		if not yu.riseanimtimer
			if (p.speed > 5*s.scale) and not (p.cmd.buttons & BT_CUSTOM3)
			or (p.speed >= (p.runspeed*3/2))
				if yu.forceanim yu.forceanim = nil yu.frame = A end
				if (p.speed >= (p.runspeed*3/2))
					if s.state != S_PLAY_RUN
						s.state = S_PLAY_RUN
					end	
				elseif s.state != S_PLAY_WALK
					s.state = S_PLAY_WALK
				end
			else
				local SPEED = abs(s.momz)+p.speed 
				yu.maxtics = (SPEED > 20*s.scale) and 3 or (SPEED > 10*s.scale) and 4 or 5
				if yu.forceanim != "idle"	
					yu.forceanim = "idle" yu.frame = A
				end
				if s.state != S_PLAY_STND
					s.state = S_PLAY_STND
				end
			end
		end
		if RB.haymaker and not (RB.haymaker.force==2)
			yu.forceanim = (RB.haymaker.force) and "ringburst" or "haymaker"
		end
	end
	if yu.forceanim --Apply animation timers and frame limits.
		yu.tics = max($-1,0)
		if not yu.tics
			yu.tics = yu.maxtics
			yu.frame = $+1
		end
		if (yu.frame > B)
			if yu.forceanim == "idle"
				if (yu.frame > D) yu.frame = A end
			else //if yu.forceanim == "rise" or yu.forceanim == "descend"
				yu.frame = A
			end
		end
		XSon.FlightAnim(p,s,xs,yu) --PostThinkFrame does this too, but some effects wanna know sooner which animation we're in.
	end
	return true --
end

--Called from both the PostThinkFrame and the flight thinker!
--Decides what frames to put X Sonic in, overriding whatever SRB2 attempts to do.
XSon.FlightAnim = function(p,s,xs,yu, POST) if not yu.forceanim return end --
	local FRAME, SPRITE2,RB = (yu.frame) or A, SPR2_STND, yu
	if yu.explosion
		local BOOM = yu.explosion
		XS_RingBurstSparkles(s, 28, (BOOM%2) and SKINCOLOR_SUPERGOLD5 or SKINCOLOR_KETCHUP)
		if (BOOM > 11)
			if (BOOM > 25)
				FRAME = A
			else
				FRAME = min(L, A+abs(11-(BOOM/3)) )
			end
			if POST=="POST" --Don't render effects here.
				if (BOOM > 17)
					if (BOOM%3==0)
					
						XSon.EarthQuake(s, (BOOM%9==0) and 7 or (BOOM%6==0) and 6 or 5, (450+(BOOM*6))*s.scale, 1, 
						87000-(BOOM*2500), FRACUNIT/5, (BOOM%6==0) and BOOM*(ANG1*3/4) or -BOOM*(ANG1*3/4), 
						32|((BOOM>26) and FF_TRANS20 or FF_TRANS30), 41-(BOOM/2), SKINCOLOR_XSSUPERBLUR, AST_ADD)
						
						local ghs = XSon.RadiantLight(s, SKINCOLOR_SILVER, 12, nil, nil, 5<<16)					
						if ghs and p==displayplayer and (splitscreen!=true)
							ghs.flags2 = $|MF2_DONTDRAW
						end
						for i = -3,3,1
							ghs = XS_CircleWave(s, 2, SKINCOLOR_FLAME, 20-abs(i), FRACUNIT+55000-abs(i*11500))
							if ghs and ghs.valid
								ghs.color,ghs.colorized = SKINCOLOR_FLAME, true
								ghs.scalespeed = ( (FRACUNIT+27000+(BOOM*5000)) *2 )/(2+abs(i))
								ghs.sttfade = true
								ghs.z = $+(i*128*s.scale*P_MobjFlip(s))
								ghs.frame = $+1<<16
							end
						end					
					end
				end			
				if (BOOM%2==0)					
					if BOOM==14
						XSon.SwapStretch(p, s, xs)
						s.spritexscale,s.spriteyscale = FRACUNIT-10500,FRACUNIT+20000
						s.spriteyoffset = 2<<16
						s.XSstretch = {timer = 7, Xspeed = 1650, Yspeed = 2500}					
					elseif (BOOM > 21)	
						if (BOOM%2==0)
							XSon.ChargePower(s, 28+(BOOM*2), s)
						end						
						if BOOM==22
							local SFX = XSon.GibSound(s, sfx_xssfpn, 255, 200)
							S_StartSound(SFX, sfx_xssfxp)					
						elseif BOOM==26
							XSon.Quake(s, 188<<16, 5, 3600<<16)
							P_RadiusAttack(s, s, 1500*s.scale, DMG_NUKE, false)
							local VOC = (leveltime%4==0) and sfx_xsvopb or (leveltime%3==0) and sfx_xsvoc8
							or (leveltime%2==0) and sfx_xsvoce or sfx_xsvocf
							XSVoice(p, s, xs, VOC, "ForceEcho")
							if S_SoundPlaying(s, VOC)
								S_StartSoundAtVolume(nil, VOC, 125, p)
							end					
						elseif BOOM==30
							XSon.Quake(s, 188<<16, 7, 3600<<16)
							for i = 5,9,2
								local ghs = XSon.TransformGFX(s, nil, L|FF_TRANS10, 19+i)
								ghs.scalespeed,ghs.destscale = FRACUNIT/i,99<<16
								ghs.sttfade = 11
								if i == 9
									ghs.spritexscale,ghs.spriteyscale = $/2,$/2
								end
							end
							local ghs = XS_Vortex(s, 35, FRACUNIT+25555, nil, R|FF_TRANS40, SKINCOLOR_RUBY)
							ghs.spritexoffset,ghs.spriteyoffset = 58<<16,60<<16
							ghs.renderflags = $|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS
							ghs.flags2,ghs.tracer = $|MF2_SPLAT, nil
							ghs.z = $+(3*s.scale*P_MobjFlip(s))
							ghs.scalespeed = 1<<15
						end
					end			
					if (BOOM%4==0)
						if (BOOM%8==0)
							XS_MediumDust(s, SKINCOLOR_SUPERGOLD5, AST_ADD, Q|FF_TRANS50)	
						end
						XS_RingBurstDust(p, s, xs, "manual", s.z-(50*s.scale*P_MobjFlip(s)) )
						XS_QuickDust(s, (BOOM%8==0) and SKINCOLOR_SUPASONIKKU or SKINCOLOR_SUPERGOLD1) 
					end	
				elseif (BOOM > 5)
					XS_QuickDust(s, SKINCOLOR_KETCHUP, AST_ADD)	
				end
			end
		else
			FRAME = (BOOM%8==0) and O or (BOOM%7==0) and O or (BOOM%6==0) and M or (BOOM%5==0) and M
			or (BOOM%4==0) and N or (BOOM%3==0) and N or L 
		end	
		s.sprite2 = SPR2_TRNS
		s.frame = ($ & ~FF_FRAMEMASK)|FRAME	return --	
		
	elseif yu.haymaker and (yu.forceanim=="haymaker" or yu.forceanim=="ringburst")
	
		if (yu.forceanim=="ringburst")
			if not ((s.state >= S_PLAY_XDIVEFLIP1) and (s.state <= S_PLAY_XDIVEFLIP4))
				if RB.RingBurstChase or not ((RB.WasRingBursting or 0) < 17)
					local T = leveltime
					s.sprite2 = SPR2_ROLL|FF_SPR2SUPER --Apply animation.
					s.frame = ($&~FF_FRAMEMASK)|((T%4==0) and D or (T%3==0) and C or (T%2==0) and B or A)
					XSRingBurstBall(p, s, xs) 
				end
			end 
			return --
		else
			if (yu.haymaker.timer < 14)
				FRAME = (yu.haymaker.timer>4) and B or A
			else
				FRAME = (yu.haymaker.timer%2) and D or C
			end
			SPRITE2 = SPR2_TAL1
		end
	elseif yu.shock
		SPRITE2,FRAME = SPR2_MLEL, A	
	end
	if RB.painstun or ((s.sprite2&~FF_SPR2SUPER)==SPR2_PAIN) and not (p.pflags & PF_SLIDING)
		return --	
	end
	if yu.forceanim == "rise" or yu.forceanim == "descend"
		if (p.speed > abs(s.momz)*5/4) and ((xs.direct > 10) or xs.zipdash) and (p.speed > 28*s.scale)
			SPRITE2 = yu.forceanim == "rise" and SPR2_GLID or SPR2_DIVE
		else
			SPRITE2 = yu.forceanim == "rise" and SPR2_SPNG or SPR2_FALL
		end	
	else--if yu.forceanim == "idle" 
		if (p.cmd.buttons & BT_CUSTOM3)
			SPRITE2 = ((p.cmd.buttons & BT_JUMP) and (s.momz*P_MobjFlip(s) > s.scale*3)) and SPR2_FRUN or SPR2_FLT
			if not yu.crossedarms 
				if (yu.init < 18)
					yu.crossedarms = 8
				else
					XSon.CrossArmEffect(p,s,xs, yu)
				end
			elseif yu.crossedarms==1 and yu.charging
				local FRAME = E+(yu.charging/2)
				if (yu.charging%6==0) or yu.charging==4
					local ghs = XS_RingBurstSparkles(s, nil, s.color, 800)
					if (yu.charging%12==0) --Super Saiyan aura.
						local RNGSIZE = XSLocalRNG(40000,90356, ghs or s)
						local RNGFRAME = XSLocalRNG(-2, 2, yu.LockOn or s)
						if (RNGFRAME > 0)
							RNGFRAME = H|(FF_TRANS60-(RNGFRAME<<16))
						else
							RNGFRAME = 32|(FF_TRANS50+(RNGFRAME<<16)) | ((RNGFRAME==0) and FF_HORIZONTALFLIP or 0)
						end
						local RNGSCALESPEED = XSLocalRNG(10033, 30033, yu.LockOn or s)
						
						XSon.EarthQuake(s,(yu.charging%24==0) and 5 or 3,(yu.charging%36==0) and 290*s.scale or 150*s.scale, 1,
						RNGSIZE, RNGSCALESPEED, XSLocalRNG(11,37,s)*ANG1, RNGFRAME, XSLocalRNG(12, 20, s), s.color, AST_ADD)
					else
						if yu.charging == 4
							for i = 1,3
								ghs = XSon.TransformGFX(s) 
								ghs.frame = $+((1+i)<<16)
								ghs.fuse = $+1-i
								ghs.scalespeed = $*i
								ghs.destscale = 99<<16
								ghs.spriteyoffset = $-16<<16
								ghs.spritexscale,ghs.spriteyscale = ($*(3+i))/4,($*(2+i))/3
							end
							local t = yu.LockOn
							if t and t.valid
								local DIST = FixedHypot(FixedHypot(s.x-t.x, s.y-t.y), s.z-t.z) 
								if (DIST < 3660*s.scale)
									P_Thrust(t, R_PointToAngle2(s.x, s.y, t.x, t.y), 36*s.scale)
								end
								if not S_SoundPlaying(s, sfx_xsvocd) and not S_SoundPlaying(t, sfx_xsvocd)
									XSVoice(p, s, xs, sfx_xsvocd, "ForceEcho")
								end
							elseif not S_SoundPlaying(s, sfx_xsvocd)
								XSVoice(p, s, xs, sfx_xsvocd, "ForceEcho")
							end
						end			
						for i = 0,(yu.charging==4) and 2 or 0
							ghs = XS_CircleWave(s, 2, SKINCOLOR_XSSUPERAURA, 17, (FRACUNIT+45000-(i*13500)))
							ghs.color,ghs.colorized = (yu.charging%12==0) and SKINCOLOR_XSSUPERBLUR or SKINCOLOR_XSSUPERAURA,true
							ghs.scalespeed = (FRACUNIT+45000)/(1+i)
							ghs.frame = $+(((yu.charging==4) and 2 or 3)<<16)
							if yu.charging==4 ghs.z = $-((i-1)*144*s.scale) end
						end		
					end
				end
				if (FRAME > O) FRAME = L+((XSon.TimeFrame(H)/2)) end --Loop.
				s.sprite2 = SPR2_TRNS
				s.frame = ($ & ~FF_FRAMEMASK)|min(O, max(F, FRAME))	return --			
			end
		end
	end
	s.sprite2 = SPRITE2|FF_SPR2SUPER --Apply animation.
	s.frame = ($ & ~FF_FRAMEMASK)|FRAME
end

--Returns true if flying conditions are bad.
XSon.BadFlightConditions = function(p,s,xs)
	if not xs.supersonic --Stuff that always cancels flight.
	or (p.pflags & PF_SLIDING)
	or p.powers[pw_carry] or p.gotflag
	or xs.darkphase --DSS can't freefly.
		return true --
	end
	if xs.FreeFlight --Only applies if already flying.
		local yu = xs.FreeFlight local RB = yu
		if RB.landing and (p.cmd.buttons & BT_SPIN) and (xs.direct < 30) and (RB.landing > 8)
			return true --Landing manually.
		end
	else --Only applies if NOT already flying.
		if p.exiting or p.xstauntstance
		or (p.panim == PA_PAIN) and (P_PlayerInPain(p) or (s.state == S_PLAY_STUN))
		or xs.ChaosSnap and (xs.ChaosSnap.init < 5) 
		or xs.snapdivetrick and (xs.snapdivetrick < 8)
		or (p.pflags & (PF_STASIS|PF_FULLSTASIS|PF_JUMPSTASIS))
		or (s.sprite2 == SPR2_CLNG)
		or (s.eflags & MFE_SPRUNG)
		or s.XS_NoFlight
			return true --
		end
	end
	return false --Good flying conditions!
end

--CancelFreeFlight"! Return true if cancel went through. FreeflightCancel"
XSon.CancelFlight = function(p,s,xs, FORCE, FREE, KEEPMOM, TRUERESET)
	local RB = xs.FreeFlight
	if RB and RB.CantCancel return false end --
	if RB or (FORCE)
		local HADFREEFLIGHT = (RB) and true or false
		s.flags = $ & ~MF_NOGRAVITY
		xs.custom3 = 99<<16
		if not p.xswallrun
			p.pflags = $ & ~PF_CANCARRY
		end
		if RB and (RB.LockOn != nil)
			RB.LockOn,RB.PvPtime = nil
			p.aiming = 0
			XSon.SetMusic(p,s,xs) 
		end
		p.viewrollangle = 0
		S_StopSoundByID(s, sfx_xssfp3) S_StopSoundByID(s, sfx_xssfp4) S_StopSoundByID(s, sfx_xssfp5)
		if RB and not TRUERESET
			if (RB.landing and (RB.landing >= 4))
				XSAirGroundState(p, s, xs)
				XS_QuickDust(s)
				s.eflags = $ & ~MFE_JUSTHITFLOOR
				if not xs.zipdash 
					s.state = S_PLAY_XZIPSLIDE
					xs.stupidbugoverride = 32
					s.anim_duration = 7 s.tics = 7
					s.sprite2 = SPR2_SKID|FF_SPR2SUPER
					s.frame = $ & ~FF_FRAMEMASK
					XSon.SwapStretch(p, s, xs)
					s.spriteyscale = FRACUNIT-999 s.spritexscale = FRACUNIT+2900
					xs.zipstretch = 4
					for d = 1,8
						local ghs = P_SpawnMobjFromMobj(s, 0,0,-4<<16, MT_SPINDUST)
						ghs.state = S_SPINDUST2 ghs.tics = $/2 ghs.anim_duration = $/2
						ghs.destscale = 99<<16 ghs.fuse = 25 ghs.flags2 = $|MF2_SHADOW
						P_InstaThrust(ghs, ANGLE_45*d, 3<<16)
					end
				end
				XSon.Sound(s, sfx_xssfx7)
			else
				xs.flightCD = 16
				if not KEEPMOM
					s.momx = FixedDiv(FixedMul($,5),6)
					s.momy = FixedDiv(FixedMul($,5),6)		
					s.momz = $*2/3
				end	
				if FREE
					XSAirGroundState(p, s, xs)
				end
			end
			if RB.modifiedaim
				p.aiming = 0
			end
			if xs.supersonic
				XSon.DeTransformGFX(p,s,xs, "all")
				XSon.Sound(s, sfx_xssfpi)
				local ghs = XSon.RadiantLight(s)
				ghs.blendmode = AST_SUBTRACT
				ghs.dispoffset = 199
				ghs.scalespeed = FRACUNIT/3
				ghs.spriteyoffset = $+3<<16
				ghs.fuse = $-4
				ghs.sttfade = nil	
				P_Thrust(ghs, s.angle, -8*s.scale)	
			end	
			if not P_IsObjectOnGround(s)
				xs.didjumpboost = true
				if FREE and p.rings
					p.pflags = $|PF_JUMPED & ~(PF_THOKKED|PF_SPINNING)
				elseif not p.rings
				and not p.powers[pw_super] and not xs.darkphase and s.skin == "xsonic"
				and not (p.pflags & (PF_SPINNING|PF_THOKKED|PF_SLIDING))
				and (HADFREEFLIGHT==true) and not xs.snapdivetrick 
				and (p.panim >= PA_IDLE) and (p.panim <= PA_FALL)
				and not p.powers[pw_carry]
					local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
					if (abs(s.z+s.momz-FLOORZ) > 600*s.scale)	
						XSZipDashCancel(p, s, xs)
						xs.zipdash,xs.zipdashslide = 0,0
						
						s.state = S_PLAY_PAIN
						s.sprite2 = SPR2_DEAD
						s.frame = $ & ~FF_FRAMEMASK
						p.pflags = $ & ~PF_JUMPED
						P_SetObjectMomZ(s, -8<<16, true)
						xs.ZoomLines = 12
						
						s.XS_comeback = 2
						
						S_StartSound(s, sfx_xssfxj)
						s.spritexscale,s.spriteyscale = FRACUNIT-8000,FRACUNIT+40000
						s.spriteyoffset = 6<<16
						s.XSstretch = {timer = 4, Xspeed = 2222, Yspeed = 5444}
						
						local iv = P_RandomRange(0,1) or -1
						XSDoRoll(p, s, xs, 15*iv<<16, 89*iv<<16, 180, iv, FRACUNIT)
						XSVoice(p, s, xs, sfx_xsvoc5) --OH SHIIIT!
						S_StartSound(s, sfx_xssfxj) 
						
						local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSTRACKER2)
						if ghs and ghs.valid
							ghs.target = s ghs.SteamTrail = 1
						end							
					end
				end
			end
		end
		xs.FreeFlight = nil return true --
	end
end

--Turn around your flying axis, sacrificing some speed across the way. 
XSon.FreeFlightVector = function(s, destANGLE, ADD, TURNSPEED)
	if ((s.flags2 & MF2_TWOD) or twodlevel) or s.currentaxis return end --GTFO.
	
	if ADD == nil ADD = 1 end
	local premomx,premomy = s.momx,s.momy				
	destANGLE = (destANGLE) or XSon.MOMANGLE(s)
	local MOVEANG = R_PointToDist2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	local XADD = P_ReturnThrustX(s, destANGLE, MOVEANG) + (s.player and s.player.cmomx or 0)
	local YADD = P_ReturnThrustY(s, destANGLE, MOVEANG) + (s.player and s.player.cmomy or 0)

	P_InstaThrust(s, R_PointToAngle2(s.x-s.momx,s.y-s.momy, s.x+XADD, s.y+YADD), MOVEANG)	
								
	if TURNSPEED --Higher turnspeed values turns more gradually. 2 is the fastest.	
		if TURNSPEED == 2
			s.momy = $/2+(premomy/2)
			s.momx = $/2+(premomx/2)
		elseif TURNSPEED == 3
			for d = 1,2
				s.momy = $/2+(premomy/2)
				s.momx = $/2+(premomx/2)
			end
		elseif TURNSPEED == 4
			for d = 1,3
				s.momy = $/2+(premomy/2)
				s.momx = $/2+(premomx/2)
			end
		else
			for d = 1,4
				s.momy = $/2+(premomy/2)
				s.momx = $/2+(premomx/2)
			end
		end	
	else
		s.momy = FixedDiv(FixedMul($, 100), 125)+(premomy/5)
		s.momx = FixedDiv(FixedMul($, 100), 125)+(premomx/5)		
	end
	s.momx = FixedDiv(FixedMul($,100),100+ADD)
	s.momy = FixedDiv(FixedMul($,100),100+ADD)		
end