--╔════════════════════════════╗══════════════════════════════════════════════════════════════════════════════════════════════#
--║       Zipdash Functions    ║ Some of these still use rawsets from my old workflow.
--╚════════════════════════════╝══════════════════════════════════════════════════════════════════════════════════════════════#
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
local CVAR = CV_FindVar("cam_speed") if CVAR == nil CVAR = {string = "0.3", value = 1} end

local function DarkZipDashGFX(p,s,xs,SUPER,DARKAURA)
	local DARKAURA2 = XSon.DarkAura(3)
	local ths,ghs = XS_QuickDust(s, DARKAURA2, AST_ADD)
	for i = 1,2 ths = (i==2) and ghs or $
		if ths and ths.valid
			ths.dispoffset = 132
			P_TryMove(ths, ths.x-FixedMul(33*s.scale, cos(s.angle)), ths.y-FixedMul(33*s.scale, sin(s.angle)), true)
		end	
	end
	XSon.sparkles(p,s,xs, 38, 3, 6, 2<<16, (leveltime%2) and DARKAURA2 or DARKAURA, AST_ADD, (4-SUPER)<<16, 13)
	for i = 1,((SUPER>1) and 2 or 1)
		ghs = XSon.ElectricBody(s, 2+(SUPER*3), (i==2) and SKINCOLOR_CRIMSON or SKINCOLOR_XSSUPERAURA)
		if i == 1
			ghs.blendmode = AST_REVERSESUBTRACT
			ghs.destscale = 99<<16
			ghs.frame = $+FF_TRANS50
		else
			ghs.frame = $|FF_HORIZONTALFLIP
		end
	end
	for i = -2,2
		if i == 0 continue end --
		ghs = P_SpawnMobjFromMobj(s,0,0,s.height/4,MT_MINECARTSPARK)
		ghs.fuse = 15
		ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
		ghs.dispoffset = 33
		ghs.scale = s.scale/12
		P_InstaThrust(ghs, s.angle+ANGLE_180+(ANG60*i), 22*s.scale)
		ghs.momz = 5*s.scale*P_MobjFlip(s)
	end		
	XSon.DarkBody(s, nil, nil, DARKAURA, 2)	
	if (SUPER > 1)
		P_RadiusAttack(s, s, (xs.zipdash==1 and 45 or 115)*SUPER<<16, DMG_NUKE, true)
		local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
		if (abs(s.z-FLOORZ) < 111*s.scale)
			for i = 2,SUPER
				ghs = XS_CraterSpew(s, p.drawangle+ANGLE_180, 9+i, true)	
				if ghs and ghs.valid
					ghs.tics,ghs.fuse = 55/i,50/i
				end
			end
		end
	end
end

--Prepping for the Zip Dash! Lean back and go! ZipDashPrep"
XSon.PrepZipDash = function(p, s, xs, SUPER)	
	XSRemoveDiveHB(xs)
	xs.zipdash = 1 --Let's do this shit.
	xs.zipcd = 0
	xs.zipdashslide = 0
	xs.leappenalty,xs.zipcontinue = nil,nil
	xs.zipdashleap,xs.leapaftermath = 0,0
	xs.zipdashleappower = 12
	xs.didjumpboost = true
	xs.jumpboost = 0
	xs.zipdashminus,xs.zipdashadd = 0,0
	xs.leapaftermath = 0
	s.state = (SUPER!=3) and S_PLAY_XZIPSLIDE or S_PLAY_XZIPPREPSUPER
	s.momx,s.momy = $*3/4, $*3/4
	xs.zipspam = (xs.zipspam) and $+9 or 9
	if xs.FreeFlight
		if xs.FreeFlight.refreshC1 
			xs.FreeFlight.refreshC1 = 0
		end
	end
	if not (xs.FreeFlight and (abs(s.z-s.floorz) < 60*s.scale))
		XSon.SwapStretch(p, s, xs)
		s.spritexscale,s.spriteyscale = FRACUNIT-800,FRACUNIT+2500
		xs.zipstretch = 16
		if xs.darkphase --Stomp into the floor aggressively!
			p.powers[pw_strong] = $|STR_UPPER|STR_FLOOR|STR_CEILING|STR_HEAVY
			local SUPER = xs.darkphase
			local DARKAURA = XSon.DarkAura(0)
			if P_IsObjectOnGround(s)
				XSon.Quake(s, max(40<<16, 25*SUPER<<16), 3, 100*SUPER<<16)
				local SFX = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
				SFX.flags2 = MF2_DONTDRAW SFX.tics = 30
				XSon.Sound(SFX, sfx_pumpkn, min(255, SUPER*100))
				
				local sec = R_PointInSubsector(s.x, s.y).sector --Break through bustable floors below!
				for fof in sec.ffloors()
					if fof and (fof.flags & FF_EXISTS) 
					and (fof.flags & (FF_STRONGBUST|FF_BUSTUP))
					and not (s.z-99<<16 >= fof.topheight)
					and not (s.z+99<<16+s.height < fof.bottomheight)
						EV_CrumbleChain(fof)
						if fof.master and (fof.master.flags & ML_EFFECT5)
							P_LinedefExecute(P_AproxDistance(fof.master.dx, fof.master.dy)>>16, s, fof.target)
						end
					end
				end
			end
			DarkZipDashGFX(p,s,xs, SUPER, DARKAURA)
		end
	end
	if xs.darkphase
	or not xs.uwdebuff and not xs.FreeFlight
		xs.zipdashfavor = 0
		xs.framejump = 0
		if P_IsObjectOnGround(s)
			xs.zipdashfavor = 2
			XSon.Sound(s, sfx_xssfx7)
		end
		local ghs
		for d = 1,8
			ghs = P_SpawnMobjFromMobj(s, 0,0,-4<<16, MT_SPINDUST)
			ghs.destscale = 99<<16
			ghs.flags2 = $|MF2_SHADOW
			ghs.fuse = 15
			ghs.color,ghs.colorized = SKINCOLOR_WHITE,true
			ghs.blendmode = AST_ADD
			P_InstaThrust(ghs, ANGLE_45*d, 8*s.scale)
		end
		XSon.ZipdashControls(p,s,xs)
	end
end

--Initial Zip Dash! ZipDashStart" StartZipDash" Called from ZipDashShiftBack
XSon.StartZipDash = function(p, s, xs, ZIPSPEED)
	ZIPSPEED = (ZIPSPEED) or 105 
	
	--Misc stuff to undo
	XSon.GetOutOfTheFloor(p, s)
	xs.bonkeddash,xs.rollcd,xs.justleaped,xs.delayzip = 0,0,0,0
	xs.zipdashangle = s.angle --Set Angle
	s.spritexoffset,s.spriteyoffset = 0,0
	xs.squat,xs.squatcharge = 0,0
	xs.ringburstcd = 2
	S_StopSoundByID(s, sfx_xssfp8)
	if not xs.zipcontinue
		xs.framejump = 0
	end
	
	local SUPER = XS_Super(p)
	local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
	
	--Do another stretch effect.
	if not (xs.FreeFlight and (abs(s.z-FLOORZ) < 360*s.scale) and SUPER==3)
		XSon.SwapStretch(p, s, xs)
		xs.zipstretch2 = 3
		s.rollangle = 0
		s.spritexscale,s.spriteyscale = FRACUNIT-35000,FRACUNIT+5000
	else
		s.spritexscale,s.spriteyscale = FRACUNIT,FRACUNIT
	end
	
	--State and sfx
	s.state = S_PLAY_RUN
	p.pflags = $|PF_JUMPDOWN & ~(PF_STARTJUMP|PF_JUMPED|PF_STARTDASH|PF_SPINNING|PF_THOKKED|PF_NOJUMPDAMAGE)
	S_StopSoundByID(s, skins["xsonic"].soundsid[SKSTHOK])
	s.XSlastzipD,s.XS_zipflip = nil
	
	if p.xboomtable --Delete active Sonic Booms to not clutter the stage if players spam it
		for _,ghs in pairs(p.xboomtable)
			if ghs and ghs.valid P_RemoveMobj(ghs) end
		end
	end
	if SUPER == 3
		XSon.RadiantLight(s, nil, 6, 0, S|FF_TRANS50, FRACUNIT/3)
		XSon.sparkles(p,s,xs, 60, 4, 4, 3<<16, SKINCOLOR_XSSUPERBLUR, (xs.darkphase) and AST_REVERSESUBTRACT or AST_ADD, (leveltime%2) and FF_HORIZONTALFLIP or FF_TRANS10, nil, FRACUNIT/2)
		
		if xs.FreeFlight --Play proper flying sounds.
			local RB = xs.FreeFlight ZIPSPEED = max($, 110)
			S_StopSoundByID(s, sfx_xssfp5) 
			if RB.refreshC1
				RB.refreshC1,RB.C1stun,RB.C2stun = nil
				if (RB.custom2 > 1)
					RB.custom2 = 0
				end
			end
			
			RB.painstack,RB.quickroll,RB.rollD,RB.WasRingBursting,RB.canforcezip = nil
			
			if RB.WallTransfer or RB.painstun
				S_StopSoundByID(s, sfx_xssfp3) S_StopSoundByID(s, sfx_xssfp4)
			else
				XSon.Sound(s, sfx_xssfp5, 164)
				if not (p.cmd.buttons & (BT_SPIN|BT_JUMP))
					S_StopSoundByID(s, sfx_xssfp3) S_StopSoundByID(s, sfx_xssfp4)
					XS_RingBurstStripes(s, SKINCOLOR_WHITE, AST_ADD)
				elseif (p.cmd.buttons & BT_JUMP) or not RB.truespin
					S_StopSoundByID(s, sfx_xssfp4)
					if not S_SoundPlaying(s, sfx_xssfp3)
						XSon.Sound(s, sfx_xssfp3, 155)
					end
				elseif not (p.cmd.buttons & BT_JUMP)
					S_StopSoundByID(s, sfx_xssfp3)
					if not S_SoundPlaying(s, sfx_xssfp4)
						XSon.Sound(s, sfx_xssfp4, 140)
					end		
				end
			end
		end
	elseif xs.uwdebuff and (s.eflags & MFE_UNDERWATER) and SUPER==0
		--Oh dear, utter failure..
		XSZipDashCancel(p, s, xs) xs.zipdash,xs.zipdashslide = 0,0
		P_SetObjectMomZ(s, FRACUNIT)
		p.powers[pw_nocontrol] = max($,5)
		P_InstaThrust(s, xs.turnang, 22*s.scale)
		s.eflags = $ & ~MFE_JUSTHITFLOOR
		xs.poutysonic = 140
		XSon.Sound(s, sfx_s3k7d)
		XSon.Sound(s, sfx_xssfxb)
		XSon.SwapStretch(p, s, xs)
		xs.zipstretch = 5
		s.spritexscale,s.spriteyscale = FRACUNIT-24999,FRACUNIT+34999
		
		local bubble,ANG,DIST,ZDIST = nil, s.angle, 32*s.scale,4*s.scale
		for d = 1,12
			local bubble = P_SpawnMobj(s.x+s.momx+FixedMul(DIST, cos(ANG)), s.y+s.momy+FixedMul(DIST, sin(ANG)), s.z+ZDIST, MT_SMALLBUBBLE)
			if bubble and bubble.valid
				bubble.scale = s.scale bubble.fuse = 99
			end
			ANG = $+ANG60
			if d==6
				DIST = 75*s.scale 
				ZDIST = 20*s.scale
			end
		end	return --
	end
	
	if xs.zipdashcamspeed == nil and (p==consoleplayer) --Register our camera settings.
		xs.zipdashcamspeed = CVAR.string
		COM_BufInsertText(p, "cam_speed 0.03997")
	end
	if (ZIPSPEED > 100)
		--Only on the intial dash to not overload the HUD.
		if not xs.zipcontinue 
			xs.hudboost = 1
		end
		if (p.pflags & PF_ANALOGMODE) and (p.cmd.forwardmove < -36)
			s.angle = p.drawangle --Adjust camera, pls.
		end
		if xs.darkphase
			XSon.Quake(s, max(36<<16, SUPER*25<<16), (SUPER==3) and 12 or SUPER*3, 300*SUPER<<16)
			XSon.Sound(s, sfx_xssfpr, SUPER*50)
			DarkZipDashGFX(p,s,xs, SUPER, XSon.DarkAura(0))
			if xs.zipdashminus
				xs.zipdashminus = max($-5*SUPER, 0)
			end
			if (SUPER > 1) and s.XS_DarkTable
				local RB = s.XS_DarkTable
				if not RB.windlines
					RB.speedlines = SUPER
					RB.windlines = SUPER*2
				end
				if xs.darkphase == 3
					local ghs = XSon.BurstWave(s, 1, SKINCOLOR_XSSUPERBLUR, AST_REVERSESUBTRACT, 18, 85536)
					ghs.sttspeed = 1
					P_TryMove(ghs, s.x+FixedMul(60*s.scale, cos(p.drawangle)), s.y+FixedMul(60*s.scale, sin(p.drawangle)), true)
					P_Thrust(ghs, p.drawangle, 18*s.scale)
				end
			end
		else
			XSon.Quake(s, 34<<16, 3, 250<<16)
		end
		XSon.Sound(s, sfx_xssfx1)
		P_RadiusAttack(s, s, 112*s.scale) --Destroy nearby foes.

		--Create a Sonic Boom!
		p.xboomtable[xs.zipdash] = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK) 
		local ghs = p.xboomtable[xs.zipdash]
		ghs.flags = $|MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY
		ghs.angle = s.angle+ANGLE_90
		ghs.scale = $/4
		ghs.sprite = SPR_XSG1
		ghs.frame = F|FF_TRANS30
		ghs.spriteyoffset = $-2<<16
		ghs.fuse,ghs.tics = 40,41
		ghs.destscale = 99<<16
		ghs.renderflags = RF_PAPERSPRITE
		ghs.blendmode = AST_ADD
		if xs.darkphase
			ghs.color,ghs.colorized = XSon.DarkAura(0),true
		end

		local ths,ghs = XS_QuickDust(s, SKINCOLOR_SUPERORANGE2, AST_ADD, nil, s)
		ths.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
		ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
		
		local bhs = XS_RingBurstStripes(s)
		if bhs and bhs.valid
			bhs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
		end
		if P_IsObjectOnGround(s)
			XS_MediumDust(s, SKINCOLOR_BEIGE)
			if (p.cmd.buttons & BT_JUMP) and (p.cmd.buttons & BT_SPIN) and xs.zipdash 
			and not xs.zipcontinue and not xs.zipdashslide
				xs.zipleapbuffer = 3
			end
		elseif SUPER == 3
			s.momz = $/2
			if (XS_CRAPPYPRIORITIES!=1)
				s.XSsuperroll = XSon.RNG(3,4,s) == 4 and -8 or 8
				s.rollangle = -ANGLE_45*s.XSsuperroll
			end
			s.spritexscale = FRACUNIT+8400
			s.spriteyscale = FRACUNIT+8400
			s.spriteyoffset = 6<<16
			s.XSstretch = {timer = 9, Xspeed = 1000, Yspeed = 1000, air = true}	
		end
		
	else
		local ghs = P_SpawnGhostMobj(s) --Minor dash gfx underwater
		ghs.scale = s.scale
		ghs.destscale = 99<<16
		ghs.colorized = true
		XSon.Sound(s, sfx_thok)
	end
end

--Begin the Zip Dash Leap! StartZipDashLeap"
rawset(_G, "XSStartZipLeap", function(p, s, xs, ZIPSPEED)
	ZIPSPEED = (ZIPSPEED) or 100
	if xs.speednoise and xs.speednoise.valid
		xs.speednoise.state = S_NULL --Delete sound object.
	end
	local CONTINUE = xs.zipcontinue 
	XSSnapDiveReset(p, s, xs, true)
	XSon.SwapStretch(p, s, xs)
	xs.alteredoffset = true
	xs.squat,xs.squatcharge = 0,0
	s.spriteyoffset = -14<<16
	xs.zipdashleapslope = nil
	xs.rollcd = max($,2)
	xs.zipdashpause = 0
	xs.zipcd = 0
	xs.zipcontinue = nil
	xs.jump,xs.spin = 99,99
	xs.snapdivewait = 1
	s.state = S_PLAY_XZIPSLIDE
	xs.framejump = 0
	p.pflags = $|PF_JUMPDOWN
	XSGhostTrail(p, s, xs)
	
	local SUPER = XS_Super(p)
	local SLOPEBOOST = 0 --Calculate slopejump physics if we're on one!
	if s.standingslope and s.standingslope.zdelta and (abs(s.standingslope.zdelta) > 7500)
		local EXTRA = abs(s.standingslope.zdelta)*69
--		CONS_Printf(p,"\x88"+"INITIAL Slope Boost = "+FixedInt(EXTRA))	
		SLOPEBOOST = (s.z < xs.lastz) and $-EXTRA or $+EXTRA
		
		local ANGLE,PLUS = XS_AngleDifference(xs.zipdashangle, s.standingslope.xydirection+ANGLE_180, 2)
		if (ANGLE > ANGLE_90) --Oops...turn that around.
			ANGLE,PLUS = XS_AngleDifference(xs.zipdashangle, s.standingslope.xydirection, 2)
		end
		if (ANGLE > ANG1*12) --Slope effect diminishes slightly when you're not hitting it straight!
			local div = FixedInt(AngleFixed(ANGLE))
			SLOPEBOOST = FixedDiv(FixedMul($,12),div)
--			CONS_Printf(p,"\x84"+"Signifcant Angle Diff! "+div+"\x85 REDUCED slopeboost = "+FixedInt(SLOPEBOOST))
		end
		SLOPEBOOST = min(28<<16, max($, -13<<16)) --Don't lose more than 13, or gain more than 28.
	end
	P_SetObjectMomZ(s, 32<<16)
	
	s.state = S_PLAY_XZIPLEAP
	xs.zipdashleapdist = 0
	xs.bonkeddash = 0
	local PITY = 1
	
	if (SUPER >= 2)
		xs.zipdashleap = (SUPER == 3) and ((xs.darkphase==3) and 34 or 29) or 26
		xs.zipdashleappower = xs.zipdashleap
		P_RadiusAttack(s,s, 133*SUPER<<16, DMG_NUKE, true)
		if xs.darkphase
			XSon.Quake(s, 200<<16, SUPER*5, 0)
			DarkZipDashGFX(p,s,xs, SUPER,XSon.DarkAura(0))
		end
	else
		local bonus = (xs.zipdash*((CONTINUE) and 6 or 5)/7) + ((CONTINUE) and SUPER or 0) + (SUPER*4)
		xs.zipdashleappower = min(25, max(7, 3+bonus)) --Vertical Strength
		xs.zipdashleap = min(25, xs.zipdashleappower+3 + (SUPER*4) ) --Leap timer.
		xs.zipdashleapdist = max(0, 16-(xs.zipdashleappower*3/4)) --Diminishes vertical strenght for horizontal
		
		if not (SUPER) 
			if (xs.zipdashleap > 21) and s.skin == "xsonic"
				local SHOUT = sfx_xsvopf+P_RandomRange(0,2) --Shout from joy into the heavens.
				if SHOUT == s.XSlastleapshout
					SHOUT = ($>=sfx_xsvoph) and sfx_xsvopf or $+1
				end
				XSVoice(p, s, xs, SHOUT, false, nil, 160) 
				s.XSlastleapshout = SHOUT	
			elseif (xs.zipdashleappower < 10)
				PITY = 3
				if (CONTINUE) --Pity this fool.
					PITY = 2
					xs.zipdashleappower = 10
					xs.zipdashleap = max(6, $-2)
					xs.zipdashleapdist = $+1
				end
			end
		end
	end
	if xs.supersonic or p.powers[pw_sneakers] --Apply this BEFORE the slope modifiers!
		s.XS_lastZpower = max(xs.zipdashleappower, 29)
	else
		s.XS_lastZpower = xs.zipdashleappower+(SUPER*2)
	end
	
	--Add slope modifiers.
	if (abs(SLOPEBOOST) >= FRACUNIT)
		SLOPEBOOST = FixedInt(SLOPEBOOST)
		if (SLOPEBOOST < 0)
			xs.zipdashleapdist = $+abs(SLOPEBOOST)
			xs.zipdashleappower = max(1, $-(SLOPEBOOST/5))
		else
			xs.zipdashleappower = $+(SLOPEBOOST)
			xs.zipdashleapdist = max(0, $-SLOPEBOOST)
		end
	end
	
	XSon.SwapStretch(p, s, xs)
	xs.spinstretch = 7
	s.spriteyscale = FRACUNIT+30000
	s.spritexscale = FRACUNIT-35000
	
	xs.hudboost = 1
	xs.ziptrailticket = 1
	xs.didjumpboost,xs.jumpboost = true,0
	xs.walljump,xs.walljumpP = nil
	
	local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK)
	ghs.flags2 = MF2_DONTDRAW
	ghs.xsonic = p
	ghs.tics,ghs.fuse = 99,99	
	if (s.XS_lastZpower < 10)	
		S_StopSoundByID(s, sfx_xssfx1) S_StopSoundByID(s, sfx_xssfx2)
		XSon.Sound(ghs, sfx_xssfxl) XSon.Sound(ghs, sfx_xssfpr)
		XSon.Sound(s, sfx_zoom) XSon.Sound(s, sfx_xssfpo, 159)
	else
		local rvolume = min(255, (xs.zipdashleap*10)+5)	
		XSon.Sound(s, sfx_xssfx2, rvolume)
		if (ghs.xsonic != displayplayer)
			XSon.Sound(ghs, sfx_xssfx3, rvolume)
		else
			XSon.Sound(ghs, sfx_xssfx3, min(1, rvolume-100))
		end		
	end	
	if (PITY != 3) and not (p.bot)
		if xs.zipdashcamspeed == nil and not ((isdedicatedserver) and (p==server)) --Register our camera settings.
			xs.zipdashcamspeed = CVAR.string
			COM_BufInsertText(p, "cam_speed 0.03997")
		end
		XSon.Quake(s, 58<<16, ((xs.zipdashleappower*2/5)/PITY))	
	end
	P_InstaThrust(s, xs.zipdashangle, (ZIPSPEED - xs.zipdashminus) * s.scale) --ZOOM
	p.drawangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	xs.justleaped = 1
	s.XS_JL = 9
	for i = 8,60,16
		XSSpawnDust(s, i<<16, 6)
		XSSpawnJumpDust(s, i<<16, 7, xs)
	end			
end)

--Reset the Zip Dash camera.
rawset(_G, "RestoreZipDashCam", function(p, s, xs)
	if ((isdedicatedserver) and (p==server)) return end --
	local STRING = tostring(xs.zipdashcamspeed)
	xs.zipdashcamspeed = nil
	if (p == consoleplayer)
		if STRING == "0.03997"
		or (CVAR) and CVAR.string == "0.03997" --Something went wrong...reset it properly, damnit!
			if (XS_CAMSPEED!=nil)
				COM_BufInsertText(consoleplayer, "cam_speed "+tostring(XS_CAMSPEED))
			else
				COM_BufInsertText(consoleplayer, "cam_speed 0.3")
			end
		--	print("STRING:"+STRING+" CVAR.STRING:"+CVAR.string+" Restore to: \x85 "+XS_CAMSPEED)
		end
	end
end)

--Cancel the Zip Dash State! CancelZipDash.
rawset(_G, "XSZipDashCancel", function(p, s, xs, SPEEDTRANS, COLLISIONJUMP)	
	if xs.FreeFlight and xs.FreeFlight.SRAlunge and xs.FreeFlight.LockOn and not s.XSassault --and ((xs.zipdash or 0) < 6) ((xs.FreeFlight.SRAlunge or 0) > 79)
		xs.custom1 = max($,1)
		p.cmd.buttons = $|BT_CUSTOM1 return --Can't cancel!
	end
	local OGZIP = (xs.zipdash) or 0
	
	xs.zipdash,xs.zipdashslide,xs.zipdashleap = 0,0,0
	xs.delayzip,xs.zipcd,xs.zipdashleappower = 0,0,12
	xs.zipcontinue = nil
		
	if xs.speednoise and xs.speednoise.valid --Remove noise objects.
		xs.speednoise.state = S_NULL
	end
	if xs.speednoise3 and xs.speednoise3.valid --Leave 2 be.
		xs.speednoise3.state = S_NULL
	end
	if not p.xswallrun
		RestoreZipDashCam(p, s, xs)
		if s and s.valid
			s.spritexscale, s.spriteyscale = FRACUNIT,FRACUNIT
			xs.zipstretch = nil
			p.runspeed = skins[s.skin].runspeed
			S_StopSoundByID(s, sfx_xssfxa)
			if xs.FreeFlight and (abs(s.momz) < 25*s.scale) and not xs.FreeFlight.rocket --Stop some flying sounds.
				S_StopSoundByID(s, sfx_xssfp5) S_StopSoundByID(s, sfx_xssfx1)
			end
		end
	end
		
	if SPEEDTRANS --Adjust speed properly too?	
		xs.leapaftermath = 0			
		--Rail grinding or tornado?
		if p.powers[pw_carry]
			if p.powers[pw_carry] == 3888 --Don't fly out of control too wildly!
				if s.GSrailspeed != nil and abs(s.GSrailspeed) > 80*s.scale
					local TRUESPEED = (xs.supersonic) and max(99, min(143, 90+OGZIP)) or max(78, min(127, 62+OGZIP))
					s.GSrailspeed = TRUESPEED*s.scale* ((s.GSrailspeed<0) and -1 or 1)
				end
			elseif p.powers[pw_carry] == CR_DUSTDEVIL
				if (p.speed > 70*s.scale)
					P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 70*s.scale) 
					p.speed = min($, 70*s.scale)
				end
			end return --
		end
		 --Slow us down instantly just as quick.
		if (p.pflags & PF_SLIDING) or (s.eflags & MFE_SPRUNG)
			if (p.speed > 62*s.scale)
				P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 62*s.scale) 
			end
		elseif COLLISIONJUMP and xs.bonkimmune and not xs.zipdashslide and not xs.zipdashleap
			xs.collisionjump = true
			if (p.speed > 109*s.scale)
				P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 109*s.scale) 
			end	
		else
			if (p.speed > 32*s.scale)
				P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 32*s.scale) 
			end					
		end
		if s.state == S_PLAY_RUN
		or (s.state >= S_PLAY_XZIPBRAKESUPER) and (s.state <= S_PLAY_XZIPSLIDE2)
			XSAirGroundState(p, s, xs) --Reset state.
		end
		if xs.jump == 1 
			if ((p.pflags & PF_JUMPED) or (s.momz*P_MobjFlip(s) > 0)) 
			and not p.powers[pw_carry]
				s.state = S_PLAY_ROLL
				p.pflags = $ & ~(PF_THOKKED|PF_SPINNING|PF_STARTDASH)
				s.z = $+(99*P_MobjFlip(s))
			end
		end
	end
	if (s.sprite2 & ~FF_SPR2SUPER) == SPR2_SKID and P_IsObjectOnGround(s) 
	and not (p.skidtime or xs.snapdive or xs.snapdivetrick)
	and not (s.momz*P_MobjFlip(s) > 0)
		s.state = (p.speed > 35*s.scale) and S_PLAY_RUN or p.speed and S_PLAY_WALK or S_PLAY_STND --Agressive bug fix.
	end
end)

--Sonic Boom visual effects. Function isn't always used, there's alot of variations.
XSon.SonicBoom = function(p, s, xs, xno)
	local no = (xno) or (xs.zipdash) or (xs.zipdashleap)
	if not no return end --Still nope? Okay, the function gives up...!
	if p.xboomtable[no] and p.xboomtable[no].valid
		p.xboomtable[no].state = S_NULL --If it already exists, replace!
	end
	p.xboomtable[no] = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
	local ghs = p.xboomtable[no]
	ghs.flags = $|MF_NOBLOCKMAP|MF_NOCLIPTHING
	ghs.angle = ((not p.speed) and p.drawangle or R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy))+ANGLE_90
	ghs.sprite = SPR_XSG1
	ghs.frame = F|max(FF_TRANS50, min((FF_TRANS90-(no/2)<<16), FF_TRANS90))
	ghs.scale = (s.scale*no)/25
	ghs.fuse = 3+(no*2)
	ghs.tics = 99
	ghs.scalespeed = ($*9)/no 
	ghs.destscale = 99<<16
	ghs.renderflags = RF_PAPERSPRITE
	ghs.blendmode = AST_ADD
	if xs.darkphase
		ghs.color,ghs.colorized = XSon.DarkAura(1), true
	end	
	return ghs --
end

--God DAMNIT SRB2 colission, cut this bullshit out.
XSon.GetOutOfTheFloor = function(p, s)
	if s.standingslope	
		local destZ = P_GetZAt(s.standingslope, s.x, s.y)
		if (s.z < destZ) and not (s.eflags & MFE_VERTICALFLIP)
		or (s.z > destZ) and (s.eflags & MFE_VERTICALFLIP)
			s.z = destZ
			if s.momx or s.momy
				s.momx = FixedDiv(FixedMul($, 90), 91)
				s.momy = FixedDiv(FixedMul($, 90), 91)
			end
			if s.momz==0
				s.momz = -P_MobjFlip(s)
			end
		end
	end
end

--Are we currently above a death pit? (relative to gravity, that is.)
local function XSCheckPit(p, s, xs)
	local SEC,FSEC = s.subsector and s.subsector.sector if not SEC return false end
	if not (s.eflags & MFE_VERTICALFLIP)
		if s.floorrover
			FSEC = s.floorrover
		end
	elseif s.ceilingrover
		FSEC = s.ceilingrover
	end
	
	local PIT = false
	if P_PlayerTouchingSectorSpecial(p, 1, 7) or P_PlayerTouchingSectorSpecial(p, 1, 6)
		PIT = true
	elseif (udmf==true)
		if (FSEC) and FSEC.valid and (FSEC.damagetype) --Use our FOF info?
			if FSEC.damagetype==SD_DEATHPITTILT or FSEC.damagetype==SD_INSTAKILL or FSEC.damagetype==SD_DEATHPITNOTILT
				PIT = true
			end
		end
		if (PIT!=true) and (SEC.damagetype)
			if SEC.damagetype==SD_DEATHPITTILT or SEC.damagetype==SD_INSTAKILL or SEC.damagetype==SD_DEATHPITNOTILT
				PIT = true
			end
		end
	end
	if PIT==true
		if not (s.eflags & MFE_VERTICALFLIP)
			if s.floorz==SEC.floorheight return true end
		else
			if s.ceilingz==SEC.ceilingheight return true end --
		end
	end return false --
end


--Prethinkframe Zipdashcontrols" function
XSon.ZipdashControls = function(p,s,xs)
	if (xs.zipdash <= 7) and not xs.zipcontinue --Manipulate controls. Jumpstasis, basically.
		if (xs.zipdash > 2)
			if (xs.zipdash >= 4)
				if (p.cmd.buttons & BT_JUMP)
					if (xs.direct > 30)
						if not (xs.uwdebuff and (s.eflags & MFE_UNDERWATER)) 
							xs.initwallrun = 2
						end
					end
				end			
				if xs.zipdash == 5
					p.powers[pw_strong] = $|STR_SPIKE|STR_WALL
					if not (p.cmd.forwardmove or p.cmd.sidemove)
						p.cmd.forwardmove = 2
					end
					xs.spin = max($,2)
					if not xs.FreeFlight
						p.cmd.buttons = $|BT_SPIN
					else
						p.cmd.buttons = $|BT_CUSTOM1
					end
				end					
			end				
			if P_IsObjectOnGround(s) and not (p.pflags & PF_JUMPED)
			and ((p.cmd.buttons & BT_SPIN) or (xs.zipdash >= 5))
				p.pflags = $|PF_JUMPDOWN
				if xs.zipdash != 5 
					xs.jump = 0
					p.cmd.buttons = $ & ~BT_JUMP
				end
			end		
		end
	end
	if xs.FreeFlight
		if (xs.zipdash > 5) 
			local t = xs.FreeFlight.LockOn
			if t and t.valid and (p.cmd.buttons & BT_CUSTOM1)
				local RB = xs.FreeFlight
				local DIST = FixedHypot(s.x-t.x, s.y-t.y)
				local SIDE = abs(p.cmd.sidemove)
				
				if (p.cmd.forwardmove > 38) and (SIDE < 9)
				or xs.stopstick
					if not xs.stopstick and (DIST < (400+(p.cmd.forwardmove*3))*s.scale) and (p.speed > 40<<16)
						if (abs((s.z+s.momz)-(t.z+t.momz)) > 240*s.scale)
							xs.stopstick = (SIDE>9) and 24 or 18 --  print("Overrun:\x84 "+xs.stopstick)
							p.cmd.sidemove,p.cmd.forwardmove = 0,0
							xs.zipdashadd = 16
							xs.zipdashminus = 0
							RB.PvPslow = 0
							if not S_SoundPlaying(s, sfx_xssfp8) 
								S_StartSound(s, sfx_xssfp8, t.player)
								S_StartSound(nil, sfx_xssfp8, t.player)
							end
						else --Supersharp turn towards enemy.
							XSon.PvPZAdjust(p,s,xs,RB,t, 50*s.scale, 50*s.scale, "no")
							xs.zipdashangle = R_PointToAngle2(s.x, s.y, t.x+t.momx, t.y+t.momy)			
						end
					end
				else
					if SIDE
					and ( (SIDE*4+5) >= abs(p.cmd.forwardmove) )
					and (DIST < 4000*s.scale)
						local MIN = (DIST < 900*s.scale) and 17 or (DIST < 1400*s.scale) and 14 or 6
						if (p.cmd.forwardmove > -MIN) and (p.cmd.forwardmove < 18+MIN)
							p.cmd.forwardmove = -MIN --Modify controls to avoid annoying spinning stuff.
							xs.direct = (abs(p.cmd.forwardmove)>=SIDE) and abs(p.cmd.forwardmove) or SIDE
							xs.turnang = (p.cmd.angleturn<<16) + (R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
						end
						if RB.SRAlunge
							RB.PvPslow = 0
							xs.zipdashminus = 0
						elseif xs.zipdash and (xs.zipdash > 11)
							RB.PvPslow = max(1, 50-(DIST/(s.scale*80)) ) --4000/80 = 50
							if (DIST < 825*s.scale)
								RB.PvPslow = (DIST < 600*s.scale) and $*2 or $*3/2
							elseif (DIST > 2400*s.scale)
								RB.PvPslow = $*2/3
							end
						end
					end
				end
				--Adjust adjust height relatively fast when zipdashing.
				if not (p.cmd.buttons & (BT_JUMP|BT_SPIN))
				and ((p.cmd.forwardmove > SIDE) or (DIST < 750*s.scale)) 
				and not (p.cmd.forwardmove < 0) and not xs.stopstick
					XSon.PvPZAdjust(p,s,xs,RB,t, 64*s.scale, 10*s.scale, 2)
				end
				if RB.BlastCharge
					local CUSTOM3 = RB.BlastCharge.C3 if CUSTOM3==nil CUSTOM3 = 99 end
					if CUSTOM3 and (CUSTOM3 < 5) and not RB.SRAlunge and (RB.PvPENERGY > 9000)
					and t.player and t.player.xsonictable and t.player.xsonictable.FreeFlight
						XSon.SuperRushAssaultPrep(ATTACK, p,s,xs,RB, t, t.player, t.player.xsonictable, t.player.xsonictable.FreeFlight)
						RB.BlastCharge.C3 = 99<<16
						RB.BlastCharge.LetGo = 0
					end
				end
			end
		end
	else
		if xs.zipdashfavor and not P_IsObjectOnGround(s)
		and (p.panim == PA_RUN or p.panim == PA_ROLL)
		and not (p.pflags & PF_JUMPED)
		and not (s.eflags & (MFE_TOUCHWATER|MFE_SPRUNG|MFE_APPLYPMOMZ))	
		and not ((XSCheckPit(p, s, xs, true))==true) --Don't parkour straight into a pit, you nut
		and not p.powers[pw_carry]
			local SEC = s.subsector and s.subsector.sector
			local YEP,CHECKFAIL = XSon.CanParkour(SEC, p,s,xs)
			if CHECKFAIL == false and YEP == true
				local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
				local FLOORDIST = abs(s.z-FLOORZ)
				if (FLOORDIST < 106*s.scale)
					s.z = FLOORZ
					s.momz = -1
				else
					YEP = false
				end
				if YEP == true
					if s.state != S_PLAY_RUN s.state = S_PLAY_RUN end
					xs.zipdashfavor = 0
					XSon.Sound(s, sfx_xssfxh, 90)
					XSon.Sound(s, sfx_xssfx7)
					s.eflags = $ & ~MFE_JUSTHITFLOOR
					if xs.speednoise2 and xs.speednoise2.valid
						if (xs.speednoise2.tics > 82) --Too soon!
							P_SetOrigin(xs.speednoise2, s.x, s.y, s.z) return false --
						else
							xs.speednoise2.state = S_NULL
						end
					end
					xs.speednoise2 = P_SpawnMobjFromMobj(s,0,0,0, MT_XSZIPDASHTRAIL)
					xs.speednoise2.flags2 = MF2_DONTDRAW xs.speednoise2.tics = 88
					XSon.Sound(xs.speednoise2, sfx_xssfxd, 155)			
				end
				
			end
		end
		if P_IsObjectOnGround(s) --Por favor.
			xs.zipdashfavor = 2
		end
	end
end

--CanSuperLeap"! StartZipDashLeap"! We check if we can begin it here and set Sonic up for it!
XSon.ZipDashLeapOrNot = function(p,s,xs, ZIPSPEED, LEAVEIT)
	ZIPSPEED = (ZIPSPEED) or 110
	
	if xs.FreeFlight return end --
	
	if (s.eflags & MFE_UNDERWATER) and not xs.supersonic
	or not P_IsObjectOnGround(s) and not xs.framejump
	or (ZIPSPEED < 101)
		if not xs.wasingoo --you're cool still if you were in goo
			return --
		end
	end
	if not xs.zipdashslide --Regular leap out of a slide.
	and not (xs.zipdash < 5) --Too early to superleap!
	and not ((xs.direct < 3) and (p.pflags & PF_ANALOGMODE) and (xs.zipdash > 6))
	and not ((p.speed < 55*s.scale) and xs.zipdash > 6) --Too slow for a leap....
		XSStartZipLeap(p, s, xs, ZIPSPEED) return --superleap is a-go! Let's do a massive supersonic leap!
	end
	
	if (xs.zipdash < 7) and (p.cmd.buttons & BT_SPIN) or (xs.zipdash <= 1) or LEAVEIT return false end --
	
	--If we got this far, we did NOT manage to leap! Set some properties!
	xs.spin = 99
	p.pflags = $ & ~(PF_JUMPDOWN|PF_THOKKED)
	xs.rollcd = max($,3)
	XSZipDashCancel(p, s, xs, true)
	s.state = S_PLAY_ROLL	
		
	if not P_IsObjectOnGround(s)
		if xs.framejump
			xs.snapdivewait = 1
			local OLDMOMZ = s.momz
	
			if (OLDMOMZ > s.scale) --Adjust speed depending on slope.
				s.momx = FixedDiv(FixedMul($, 48), 48+(OLDMOMZ/s.scale))
				if not ((s.flags2 & MF2_TWOD) or twodlevel)
					s.momy = FixedDiv(FixedMul($, 48), 48+(OLDMOMZ/s.scale))
				end
			end
			if (OLDMOMZ*P_MobjFlip(s) > 0) --We always work in the player's favor.
				s.momz = $+OLDMOMZ
			end		
			xs.airleverage = p.speed
			xs.framejump = 0
			xs.didjumpboost = false
			xs.jumpboost = 5 
			P_DoJump(p, true) --Do it! Frame jump!
			p.pflags = $|PF_JUMPED & ~(PF_SPINNING|PF_STARTDASH)
		else
			xs.jump = 0
			p.pflags = $|PF_JUMPED
			p.cmd.buttons = $|BT_JUMP
			xs.snapdivewait = 0
		end
	end
end

--In the middle of a Zipdash Leap, flying sky high! LeapBehaviour" LeapTimer"
XSon.ZipDashLeapBehaviour = function(p,s,xs, ZIPSPEED)
	local MOMANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	if ((XSon.Teleport(p,s,xs, xs.zipdashangle)) > 360*s.scale)  --Teleported!
		if p.powers[pw_nocontrol]
		or (s.state==S_PLAY_STND) and (s.tics > 102)
		or s.momx==0 and s.momy==0 and s.momz==0
			xs.zipdashpause = 0 
			XSZipDashCancel(p, s, xs) 
			xs.zipdash,xs.zipdashslide = 0,0 
			s.momx,s.momy,s.momz = 0,0,0
			s.state = S_PLAY_STND return false --
		else
			xs.zipdashangle = MOMANG
		end
		xs.rollcd = 24
	end	
	
	ZIPSPEED = (ZIPSPEED) or 105
	if xs.zipdashleap
		xs.zipdashleap = max($-1, 0)
		s.XS_JL = 3
	end	
	local SPEED = (24+ZIPSPEED+xs.zipdashleapdist+xs.zipdashleap) * s.scale
	local MOMZ = ((xs.zipdashleappower*3/2) + 19 - (xs.zipdashleap*2) - (xs.zipdashleapdist)) * s.scale
	local SUPER = XS_Super(p)
	local ZFLIP = P_MobjFlip(s)
	
	s.momz = max(s.scale, MOMZ)*ZFLIP
	p.drawangle = MOMANG
	xs.bonkframe = nil
	xs.bonkimmune,xs.squat,xs.squatcharge,xs.ringburstcharge = 0,0,0,0
	xs.didjumpboost,xs.jumpboost = true,0
	
	if not xs.bonked
		local BACK,SIDE = 0,0 --Allow a bit of braking and strafing?
		if not ((s.flags2 & MF2_TWOD) or twodlevel) and not xs.FreeFlight
			if (p.cmd.forwardmove < -6)
				BACK = abs(p.cmd.forwardmove)
			elseif (abs(p.cmd.sidemove) > 8)
				SIDE = p.cmd.sidemove*(ANG1/max(3, ((xs.zipdashleap or 2)*2/3)))				
			end		
		end
		local POWER = s.XS_lastZpower or xs.zipdashleappower
		xs.ziptrailticket = 1
		
		if (POWER==nil or (POWER < 10))
		and (xs.zipdashleap < 10) and SUPER==0 and (SPEED > 5*s.scale) and not p.powers[pw_sneakers]
--			print("\x85"+"CRAPPY POWER:\x87"+xs.zipdashleappower+"\x80 Leap:\x82"+xs.zipdashleap)
			SPEED = FixedDiv(FixedMul($, 11), max(11, 19-xs.zipdashleap))
			P_InstaThrust(s, xs.zipdashangle-SIDE, (BACK) and FixedDiv(FixedMul(SPEED, 6),max(6,BACK/7)) or SPEED)
			if (s.momz*ZFLIP > 0)
				s.momz = FixedDiv(FixedMul($, 5), max(5, 13-xs.zipdashleap))
			end
			if (xs.zipdashleap < 3)
				xs.ziptrailticket = 0
			end
		else
--			print("POWER:\x87"+xs.zipdashleappower+"\x80 Leap:\x82"+xs.zipdashleap)
			P_InstaThrust(s, xs.zipdashangle-SIDE, (BACK) and FixedDiv(FixedMul(SPEED, 6),max(6,BACK/7)) or SPEED)
		end
		P_Thrust(s, xs.zipdashangle, 1) 
		xs.zipdashleapdist = max($-1, 0)
		p.pflags = $|PF_JUMPED|PF_THOKKED|PF_JUMPDOWN & ~(PF_SPINNING|PF_NOJUMPDAMAGE)
		if s.state != S_PLAY_XZIPLEAP
			s.state = S_PLAY_XZIPLEAP
			s.tics = xs.zipdashleap+16 s.anim_duration = s.tics
		elseif (s.tics < 3) and (xs.zipdashleap > 1)
			s.tics = 3
		end
		xs.ringburstcd = 0
		
		--Do the same zipdash Sonic Boom effects, but with some tweaks.
		if xs.zipdashleap > 12
			if (xs.zipdashleappower > 18) and (xs.zipdashleap % 2 == 0)
			or xs.zipdashleap > 16
				XSon.SonicBoom(p, s, xs, (xs.zipdashleap-4))
			end
			if BACKPRESS xs.zipdashleap = max($-1,0) end
		else
			if xs.zipdashleap == 6
				RestoreZipDashCam(p, s, xs)
			elseif not xs.zipdashleap and SUPER == 3 and not xs.darkphase
				local RCOLOR = (s.XS_modScolor) and s.XS_modScolor or (leveltime%2) and SKINCOLOR_SUPERGOLD4 or SKINCOLOR_SUPERGOLD3
				XSon.RadiantLight(s, RCOLOR, 8, (p.cmd.sidemove<0) and -ANG10 or ANG10, O|FF_TRANS40, FRACUNIT)
			end	
		end
		if SUPER == 3 and not xs.darkphase
			if (xs.zipdashleap < 6) or (xs.zipdashleap == 17) --Fluctuate the taper a little for accuracy.
			or (xs.zipdashleap > 21) and (xs.zipdashleap < 26)
				XSZipTrail(s, AST_ADD, nil, nil, nil, s.color, FRACUNIT, RF_FULLBRIGHT|RF_NOCOLORMAPS)
			else
				XSZipTrail(s, AST_ADD, 1, nil, 35, s.color, FRACUNIT+4500, RF_FULLBRIGHT|RF_NOCOLORMAPS)
			end
			if xs.zipdashleap == 15 or xs.zipdashleap == 28		
				XSon.RadiantLight(s, SKINCOLOR_XSSUPERAURA, 19, 0, S|FF_TRANS10, (xs.zipdashleap != 28) and 2<<16 or FRACUNIT+24000)
			elseif (xs.zipdashleap%3==0) and (xs.zipdashleap > 3)
				local ghs = XSon.FlickerLight(s, nil, 3, (xs.zipdashleap%6==0) and ANGLE_90 or nil, FF_TRANS10, 2<<16)
				ghs.spriteyoffset = 10<<16
				XSon.sparkles(p,s,xs, 70, 5, 11, abs(s.momz*3/4), SKINCOLOR_XSSUPERBLUR, AST_ADD, FF_HORIZONTALFLIP, nil, FRACUNIT/3)	
			end
			if (p.cmd.buttons & BT_CUSTOM1) and (xs.custom1 == 1 or not xs.zipdashleap)
			and XSon.TryFlight(p,s,xs, nil, 90, 130)
				if xs.FreeFlight
					xs.zipdash = 0 xs.zipdashleap = 0
					xs.FreeFlight.custom1 = 1
					if XSon.TryAirZipDash(p,s,xs) and xs.zipdash
						xs.zipdash = max($,4)
					end
				end					
				return 0 --
			end	
		elseif xs.darkphase
			XSon.DarkBody(s, nil, nil, XSon.DarkAura(1), 2)	
			if (SUPER > 1) and s.XS_DarkTable and not s.XS_DarkTable.windlines and (xs.zipdashleap > 10) 
				s.XS_DarkTable.windlines = 1
			end
		end		
		
		--Cancel out of it with a snap dive? Snap Dive out!
		if (xs.jump == 1) and (p.cmd.buttons & BT_JUMP)
			s.momz = $/11
			S_StopSoundByID(s, sfx_xssfx2)
			XSStartSnapDive(p, s, xs, nil, ZIPSPEED) 
			XSZipDashCancel(p, s, xs, true) --Keep this after the Snap Dive. 
			xs.zipdash,xs.zipdashslide = 0,0
			XSon.sparkles(p, s, xs, 65, 10, 11)
			if xs.spin and (xs.spin < 11)
				xs.ringburstcharge = max($,7)
			end return 1 --
		elseif not xs.zipdashleap --The end of the Zip Dive. Transition into an aftermath roll or just drop!
			XSGhostTrail(p, s, xs)
			if (SUPER >= 2)
				s.momz = $/3
			else
				s.momx,s.momy,s.momz = $/2,$/2,$/5
			end
			XSZipDashCancel(p, s, xs) xs.zipdash,xs.zipdashslide = 0,0
			xs.leapaftermath = 23
			if xs.spin and (xs.spin < 11)
				xs.ringburstcharge = max($,7)
			end	
			if xs.jump xs.jump = 199 end
			if xs.spin xs.spin = 199 end
			p.pflags = $|PF_JUMPED & ~PF_THOKKED
			return 0 --
		end return 3 --
	end	
end

--After the ZipDash Leap is done, you lose the speed and can either drop down quick or go into a floaty roll to re-orient yourself.
XSon.ZipDashLeapAfterMath = function(p,s,xs)
	xs.leapaftermath = max($-1,0)
	
	if P_IsObjectOnGround(s)
	or (s.eflags & MFE_SPRUNG)
	or p.powers[pw_carry] 
	or P_PlayerInPain(p)
	or not s.health 
	or (p.pflags & (PF_SLIDING|PF_STARTDASH))
	or xs.snapdive or xs.snapdivetrick or xs.FreeFlight
		xs.leapaftermath = 0 return --Cancel!
	end
	local CRAPPY = (((s.XS_lastZpower or 0)<10) and not (xs.supersonic or xs.darkphase or p.powers[pw_sneakers])) and true or false
	if CRAPPY==true
		xs.leapaftermath = ($>8) and max($-3,0) or max($-1,0)
	end
	local SLOWDOWN	 = false
	
	if (xs.leapaftermath > 17)	--Slow down at the start before you go into the animation.
		if (p.speed > 32*s.scale) 
			s.momx = FixedDiv(FixedMul($, 30), 31)
			s.momy = FixedDiv(FixedMul($, 30), 31)
		end
		if (s.momz*P_MobjFlip(s) > 9*s.scale)  --Slow down!
			s.momz = FixedDiv(FixedMul($, 11), 12)
			if s.state != S_PLAY_XZIPLEAP and (p.pflags & PF_JUMPED)
				s.state = S_PLAY_XZIPLEAP
			end	
			s.tics = 12 s.anim_duration = 12
		end		
	elseif (xs.jump > 198)
	
		if (xs.leapaftermath >= 17) or (xs.leapaftermath == 13)  or (xs.leapaftermath == 9)
			XSon.Sound(s, sfx_xssfxi, 130-(xs.leapaftermath*5))
			if (xs.leapaftermath == 17)
				XSon.Sound(s, sfx_xssfxo, 200)
				XSon.SwapStretch(p, s, xs)
				xs.zipstretch2 = 5
				s.spriteyscale = FRACUNIT+25000
				s.spritexscale = FRACUNIT-15000
				xs.alteredoffset = true
				s.spriteyoffset = 12<<16					
			end
		end
		
		--Sparkle effects.
		XSon.sparkles(p, s, xs, 50, 1, nil)
		
		--Anti-gravity spin!
		if CRAPPY != true
			local gsd = max(41, 50-xs.leapaftermath)
			
			if (s.momz*P_MobjFlip(s) < 3*s.scale)
				local GRAVITY = P_GetMobjGravity(s)
				if GRAVITY and (GRAVITY*P_MobjFlip(s) < 0)
					--Watch your step now, watch your step, don't FALL!
					local antigrav = (s.momz*P_MobjFlip(s) < s.scale) and abs(GRAVITY)*28/35 or abs(GRAVITY)*6/19
					antigrav = FixedDiv(FixedMul(max(1, $-(xs.leapaftermath*550)), abs(GRAVITY)), GRAVITY) --Have more control over your airspeed.
					if xs.uwdebuff antigrav = $/2 end
					P_SetObjectMomZ(s, abs(antigrav), true)
				end
			end
			s.momx = FixedDiv(FixedMul($, 40-3), gsd-3)
			s.momy = FixedDiv(FixedMul($, 40-3), gsd-3)
			SLOWDOWN = true
		end
		p.normalspeed = 3
		if xs.leapaftermath == 3
			s.state = S_PLAY_XDIVEFLIP1
			xs.frontflip = 13
		elseif xs.leapaftermath > 3
			if s.state != S_PLAY_ROLL
				s.state = S_PLAY_ROLL
			end
			s.tics = 1
		end
			
	else
		if (xs.leapaftermath > 4)
			S_StopSoundByID(s, sfx_xssfxo)
		elseif s.state == S_PLAY_ROLL
			s.state = S_PLAY_XDIVEFLIP1
			xs.frontflip = 13
		end
		xs.leapaftermath = 0
		SLOWDOWN = true
		local ZLIM = (CRAPPY==true) and 8*s.scale or s.scale --Crappy jump can't float, so be less strict.
		if (s.momz*P_MobjFlip(s) > ZLIM) 
			s.momz = ZLIM*P_MobjFlip(s)
		end
	end
	if not xs.leapaftermath
		if (p.pflags & PF_JUMPED) and not P_IsObjectOnGround(s)
			s.XS_vulnerabledrop = true
		end
		if CRAPPY == true or not (p.cmd.buttons & BT_JUMP) and s.state == S_PLAY_XZIPLEAP
			S_StopSoundByID(s, sfx_zoom)
			XSon.Sound(s, sfx_kc44, 36)
			if s.state == S_PLAY_XZIPLEAP
				XSon.SwapStretch(p, s, xs)
				xs.snapdiveangle = p.drawangle			
				s.state = S_PLAY_XDIVEFLIP1
				xs.zipstretch2 = 3
				xs.alteredscale = true
				s.spritexscale = FRACUNIT-4000
				s.spriteyscale = FRACUNIT+2000		
				xs.frontflip = 13
				xs.poutydrop = 8
				xs.alteredoffset = true
				s.spriteyoffset = 8<<16
				s.anim_duration,s.tics = $+2, $+2			
			end
		end
		local XYLIM = ((CRAPPY==true) or p.powers[pw_sneakers] or not (p.cmd.buttons & BT_JUMP) and s.state == S_PLAY_XZIPLEAP) and 44*s.scale or 32*s.scale
		if (p.speed > XYLIM) and not (s.eflags & MFE_SPRUNG)
			P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), XYLIM) 
		end
		if (s.state == S_PLAY_ROLL) and SLOWDOWN and (p.pflags & PF_JUMPED) and not xs.snapdive
			s.state = S_PLAY_FALL
		end
	end
end

--The main zipdash timer is active. ZipdashBehaviour" ZipdashTimer"
XSon.ZipDashTimer = function(p,s,xs, ZIPSPEED)
	ZIPSPEED = (ZIPSPEED) or 105
	xs.zipdashslide = 0
	if xs.zipdashangle == nil xs.zipdashangle = s.angle end
	
	if (xs.zipdash < 12) or xs.zipdashpause --Rapid Sonic Booms
		local no = xs.zipdash
		if (ZIPSPEED > 100)
			if xs.zipdashpause no = 9 end
			if p.xboomtable[no] and p.xboomtable[no].valid --Smaller Sonic Boom!
				p.xboomtable[no].state = S_NULL
			end
			p.xboomtable[no] = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
			local ghs = p.xboomtable[no]
			ghs.flags = $|MF_NOBLOCKMAP|MF_NOCLIPTHING
			ghs.angle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)+ANGLE_90
			ghs.sprite = SPR_XSG1
			ghs.frame = F|min((FF_TRANS40+(no/3)<<16), FF_TRANS90)
			ghs.scale = s.scale/no
			ghs.fuse = 37-(no*2)
			ghs.tics = 38
			ghs.scalespeed = ($*15)/no 
			ghs.destscale = 99<<16
			ghs.renderflags = RF_PAPERSPRITE
			ghs.blendmode = AST_ADD
			ghs.momx,ghs.momy = s.momx/5,s.momy/5
			if xs.darkphase
				ghs.color = XSon.DarkAura(2)
				ghs.colorized = true
			end
		end
		if (s.eflags & MFE_UNDERWATER)
			for d = 1,2
				local bubble = P_SpawnMobj(
				s.x-P_RandomRange(-35,35)*s.scale, 
				s.y-P_RandomRange(-35,35)*s.scale,
				s.z+s.height, MT_SMALLBUBBLE)
				if bubble and bubble.valid
					bubble.scale = s.scale bubble.fuse = 38
					bubble.momx = s.momx/3 bubble.momy = s.momy/3
				end
			end
		end
		if no == 11
			RestoreZipDashCam(p, s, xs)
			if xs.zipcontinue
				xs.zipcontinue = nil
				xs.zipdash = $+4
			end
		end
	end
	local SUPER = (XS_Super(p)) or 0
	if P_IsObjectOnGround(s) or (xs.zipdash == 6) or xs.wasingoo or SUPER == 3		
		if not xs.zipdashpause
			local ADDSPEED = min(xs.zipdash, 24)  --Gradually gain a bit more speed overtime	
			local MINSPEED = (xs.zipdashminus) or 0
			if (xs.zipdash > 48) ADDSPEED = min(xs.zipdash/2, 46) end
			if p.machspeedmode
				ADDSPEED = $+42 --Sure.
			end
			if xs.zipspam and (xs.zipspam > 18)
				MINSPEED = $+(xs.zipspam-13)
			end
			if SUPER --Super Saiyan speed buffs.
				if ADDSPEED
					ADDSPEED = (SUPER==3) and $*2 or (SUPER==2) and $*3/2 or $*5/4
				end
				if MINSPEED
					MINSPEED = (SUPER==3) and $*2/3 or (SUPER==2) and $*5/6 or $*7/8
				end
			end
			--Main Zipdash Speed code.
			P_InstaThrust(s, xs.zipdashangle, (ZIPSPEED + ADDSPEED - MINSPEED) * s.scale + xs.zipdashadd) --ZOOM
			p.drawangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
			if not (p.pflags & PF_ANALOGMODE)
				s.angle = p.drawangle
			end
		end
		
		if (p.speed > 90*s.scale)
			if xs.darkphase == 3
				local RNG = P_RandomRange(6250,19250)
				XSon.SpikedAura(s, 105250+RNG)
				if (xs.zipdash > 49) and (leveltime%2)
					if s.XS_DarkTable and not s.XS_DarkTable.windlines
						s.XS_DarkTable.windlines = 1
					end
				end
			end
			if P_IsObjectOnGround(s) 
				if xs.darkphase and (xs.zipdash > 7)
					if (xs.zipdash < 14)
						XSon.DarkBody(s, nil, nil, SKINCOLOR_CRIMSON, (SUPER==1) and 1 or 2)
					end
					if (xs.zipdash < 9) or SUPER==3 and (xs.zipdash < 11)
						if xs.zipdash % 2 == 0
							local SFX = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSZIPDASHTRAIL)
							SFX.flags = MF2_DONTDRAW SFX.tics = 65 S_StartSound(SFX, sfx_cdfm42)
						end
						for i = -1,1,2
							local ghs = XS_CraterSpew(s, p.drawangle+(ANG1*120*i), 13, true)	
							if ghs and ghs.valid
								ghs.tics,ghs.fuse = 22,20
								ghs.momx = ($*9)+FixedDiv(FixedMul(s.momx, 2),5)
								ghs.momy = ($*9)+FixedDiv(FixedMul(s.momy, 2),5)
							end
						end
					end
				end
				XSSpawnDust(s, nil, nil, true) --Blur REALLY wants more dust, so here's more dust
				if (xs.zipdash != 5) and (xs.zipdash < 12) and not (s.eflags & MFE_TOUCHWATER)
					local ths,ghs = XS_MediumDust(s, SKINCOLOR_BEIGE)
					if ths and ths.valid and ghs and ghs.valid
						ghs.sttspeed,ths.sttspeed = 1,1
						ghs.fuse,ths.fuse = 9,9
						ghs.momx,ths.momx = $+s.momx/3,$+s.momx/3
						ghs.momy,ths.momy = $+s.momy/3,$+s.momy/3
					end
				end
			end
		end
		if (s.state != S_PLAY_RUN)
			if (s.state == S_PLAY_XZIPBRAKESUPER)
				if (xs.zipdash > 6)
					s.state = P_IsObjectOnGround(s) and S_PLAY_XZIPSLIDE or S_PLAY_RUN
				end
			elseif not (SUPER == 3 and not P_IsObjectOnGround(s))
				if (s.state == S_PLAY_DASH or s.state == S_PLAY_WALK or p.panim == PA_IDLE)
				and not xs.zipdashpause and not (p.powers[pw_sneakers] > 2)
					s.state = S_PLAY_RUN
					if SUPER == 3
						s.frame = ($ & ~FF_FRAMEMASK)|((leveltime%2) and B or A)
					else
						s.frame = ($ & ~FF_FRAMEMASK)|(XSon.TimeFrame(D))
					end
					p.panim = PA_RUN				
				end
				if (s.tics > 1) s.tics = 1 end --Max animation speed.
			end
		end
		
		--Fix a bug.
		if xs.wasingoo and (s.momz*P_MobjFlip(s) < 0) and (s.eflags & MFE_TOUCHWATER) s.momz = 0 end
		
		--Make a speeding sound only other players can hear.
		if (xs.zipdash > 14)
			xs.rollcd = max($, 3)
			if not (xs.speednoise and xs.speednoise.valid)
				xs.speednoise = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
				xs.speednoise.xsonic = p
				xs.speednoise.flags = MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT
				xs.speednoise.flags2 = $|MF2_DONTDRAW
			else
				P_SetOrigin(xs.speednoise, s.x, s.y, s.z)
				xs.speednoise.tics = 8
				if (xs.speednoise.xsonic != displayplayer)
				and not S_SoundPlaying(xs.speednoise, sfx_xssfxc)
					XSon.Sound(s, sfx_xssfxc)
				end				
			end
		end
		
		if (s.eflags & MFE_JUSTHITFLOOR) --Zoom further with slightly different effects!
			XSon.Quake(s, 23<<16, 2, 160<<16)
			XSon.SwapStretch(p, s, xs)
			xs.zipstretch2 = 3
			s.state = S_PLAY_RUN
			s.spritexscale,s.spriteyscale = FRACUNIT-19000, FRACUNIT+6000		
			local ghs = XS_RingBurstStripes(s)
			if ghs and ghs.valid
				ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60
			end
		end
		
		--Slope physics do have a mild effect on you!
		if s.standingslope and (p.speed > 70*s.scale)
			local speed = ((s.z-xs.lastz) or 1)/-23 * P_MobjFlip(s)
			speed = min($, 3<<16) --Speed Up cap
			speed = max($, -3<<16) --Slowdown cap
			xs.zipdashadd = min($+speed, 50<<16)
			if (xs.zipdashadd < 0) --And slowdown.
				local detract = FixedInt(abs(xs.zipdashadd or FRACUNIT))
				detract = min($, 5)
				xs.zipdashminus = min($+detract, 15) --Only minor penalties, this kind of move doesn't feel right when slowed too much.
				xs.zipdashadd = 0
			end
		end

	else --if not P_IsObjectOnGround(s) midair stuff
		if not xs.zipdashpause and SUPER != 3
			if ((s.state >= S_PLAY_XZIPBRAKESUPER) and (s.state <= S_PLAY_XZIPSLIDE2))
			or (s.state == S_PLAY_RUN) or (s.state == S_PLAY_ROLL) or (s.state == S_PLAY_WALK) or (p.panim == PA_IDLE)
				if not xs.wasingoo
					XSAirGroundState(p, s, xs) --That ain't right...
				end
			end
			s.momx = FixedDiv(FixedMul($, 240), 241) --Apply slight air drag, but stay in the zipdash!
			s.momy = FixedDiv(FixedMul($, 240), 241)
			if (s.momz*P_MobjFlip(s) < 3*s.scale) and not (P_IsObjectOnGround(s) or p.powers[pw_justlaunched])
				local fallspeed = FixedDiv(FixedMul(33000, 32678), P_GetMobjGravity(s) or 32678)
				if abs(s.momz) < 32*s.scale and not xs.wasingoo --Fall fast like this!
					P_SetObjectMomZ(s, fallspeed, true)
				end
			elseif s.momz and not p.powers[pw_justlaunched] --Equalize upwards momentum.
				s.momz = FixedDiv(FixedMul($, 120), 121)
			end
		end
		p.drawangle = xs.zipdashangle
		if not (p.pflags & PF_ANALOGMODE)
			s.angle = p.drawangle
		end
		if (xs.jump <= 1) and (p.cmd.buttons & BT_JUMP)
			XSZipDashCancel(p, s, xs, true)
			p.pflags = $|PF_JUMPED & ~(PF_JUMPDOWN|PF_THOKKED)
		end
	end
	--Super air animations?
	if SUPER == 3 and not P_IsObjectOnGround(s)
		if (xs.zipdash > 8)
			if ((s.state >= S_PLAY_XZIPBRAKESUPER) and (s.state <= S_PLAY_XZIPSLIDE2))
			or p.panim == PA_RUN or s.state == S_PLAY_ROLL or p.panim == PA_WALK or p.panim == PA_IDLE
				if not xs.wasingoo
					if (abs(s.momz) < 14*s.scale) and (p.speed > 50*s.scale)
						if s.state != S_PLAY_RUN s.state = S_PLAY_RUN end
					else	
						XSAirGroundState(p, s, xs) --That ain't right...
					end
				end
			end
			if not xs.FreeFlight 
				if (xs.direct > 4) and (p.cmd.buttons & BT_CUSTOM1) --Allow instant transition to flight!
				and not (s.eflags & (MFE_JUSTHITFLOOR|MFE_APPLYPMOMZ|MFE_SPRUNG))
					if (xs.jump > 2) and (p.cmd.buttons & BT_JUMP) or xs.spin == 1 and (xs.zipdash > 12)
						if not xs.flightCD and XSon.TryFlight(p,s,xs, nil, 100, 130)
							RestoreZipDashCam(p, s, xs) --
						end
					end
				end
			elseif xs.FreeFlight.PvPslow
				if (p.cmd.buttons & BT_CUSTOM1)
					local SLOW = 110+xs.FreeFlight.PvPslow
					s.momx = FixedDiv(FixedMul($,110), SLOW)
					s.momy = FixedDiv(FixedMul($,110), SLOW)
					s.momz = FixedDiv(FixedMul($,210), 100+SLOW)
				end
				xs.FreeFlight.PvPslow = 0
			end
		else
			if (s.state != S_PLAY_XZIPBRAKESUPER)
				s.state = S_PLAY_RUN			
			end
			if xs.zipdash == 6 and (p.cmd.buttons & (BT_CUSTOM1|BT_JUMP)) and not xs.FreeFlight
				s.airzipWR = 7 --Easy snap to walls for wallrunning.
			end				
		end
		if not (s.momz*P_MobjFlip(s) < 3*s.scale) --Equalize upwards momentum.
			s.momz = FixedDiv(FixedMul($, 120), 121)
		end
	end
	--Spawn ziptrail?
	if (p.speed > 65*s.scale) and not xs.zipdashpause and not ((s.eflags & MFE_UNDERWATER) and (SUPER != 3))
	and not xs.ziptrailticket
		xs.ziptrailticket = 1
	end
end

 --Shift back a bit as you wind up, right before unleashing your superspeed! ShiftBack"
XSon.ZipDashShiftBack = function(p,s,xs, ZIPSPEED)
	local SHIFTBACK = xs.uwdebuff and -4*s.scale or -16*s.scale
	local FF = xs.FreeFlight
	if FF and FF.LockOn
		SHIFTBACK = -28*s.scale
		P_TryMove(s, s.x+FixedMul(SHIFTBACK, cos(s.angle)), s.y+FixedMul(SHIFTBACK, sin(s.angle)), false)
		if FF.quickresponse and ( (xs.zipdash > 1) and (xs.zipdash < 5)) and not FF.SRAlunge
			xs.zipdash = 5
			FF.quickresponse = 1
		end
	elseif P_IsObjectOnGround(s)
		XSSpawnDust(s, SHIFTBACK, 7)
	end
	P_TryMove(s, s.x+FixedMul(SHIFTBACK, cos(s.angle)), s.y+FixedMul(SHIFTBACK, sin(s.angle)), false)
	
	--Readyyyy!!
	if s.momx or s.momy
		s.momx = FixedDiv(FixedMul($, 50), 51)
		s.momy = FixedDiv(FixedMul($, 50), 51)
	end

	S_StopSoundByID(s, sfx_xssfxa)
	p.drawangle = s.angle
	xs.zipcd = 0
	xs.zipdashslide = 0
	xs.framejump = 0
	
	if (s.tics < 3) and (s.tics > 0) s.tics = 3 end --No spine movement here!
	
	if xs.zipdash == 5
		if xs.supersonic and ((XSon.Hyper(p))==true)
			P_NukeEnemies(s, s, 2048<<16)
			xs.ZoomLines = max(xs.ZoomLines or 0, 3)
			XSon.Flash(s, PAL_WHITE, 4, 1050*s.scale)
			S_StartSound(s, sfx_bkpoof)	
		end
		XSon.StartZipDash(p, s, xs, (ZIPSPEED) or 110) return --S.O.N.I.C, GO!!!
	end
	p.cmd.buttons = $ & ~BT_JUMP
	xs.jump = 99<<16
	XSon.AimLine(p,s,xs)
end

--The zipdash slide is currently underway, called from XS-TIMERS.
XSon.ZipDashSlideTimer = function(p,s,xs, ZIPSPEED)
	ZIPSPEED = (ZIPSPEED) or 110
	local SUPER = XS_Super(p)
	if not xs.FreeFlight and P_IsObjectOnGround(s)
		XSSpawnDust(s, p.speed+30<<16, 5)
		XSSpawnDust(s, p.speed or 1/2 or 3<<16, 8)
		if (s.state == S_PLAY_XZIPBRAKESUPER)
			s.state = S_PLAY_XZIPSLIDE
			s.spritexscale = FRACUNIT+8000
			s.spriteyscale = FRACUNIT-13500
			s.XSstretch = {timer = 4, Xspeed = 1700, Yspeed = 3500}			
		end
		if xs.darkphase and (p.speed > 30*s.scale) --Dark Slide
			if (leveltime%3==0)
				if (xs.zipdashslide < 20)
				and (xs.darkphase==3 or not ((s.eflags & MFE_TOUCHWATER) and not (s.eflags & MFE_UNDERWATER)))
					for i = -1,1 --Fling rocks around!
						local ghs = XS_CraterSpew(s, s.angle+(ANG60*i), 9)
						if ghs and ghs.valid
							ghs.momx,ghs.momy = $*5,$*5
							ghs.tics,ghs.fuse = 45,44
						end
					end
				end
				local ths,ghs = XS_QuickDust(s, SKINCOLOR_SUPERORANGE2, AST_ADD, nil, s)
				ths.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
				ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
			elseif (leveltime%2==0)
				if (xs.zipdashslide < 22)
				and (xs.darkphase==3 or not ((s.eflags & MFE_TOUCHWATER) and not (s.eflags & MFE_UNDERWATER)))
					XSon.Quake(s, min(20<<16, max(4<<16, p.speed/4)), 3)
				end
				P_RadiusAttack(s, s, (xs.darkphase*30)<<16, DMG_NUKE, true)		
			end
		end
	elseif SUPER == 3 --Buffer actions out of this.
		if xs.FreeFlight
			s.eflags = $ & ~(MFE_JUSTHITFLOOR|MFE_APPLYPMOMZ)
			local RB = xs.FreeFlight
			if RB.custom2
				RB.custom2 = 0 xs.zipcd = 99
			else
				if (xs.jump and xs.jump < 4) and (p.cmd.buttons & BT_JUMP) and (p.cmd.buttons & BT_SPIN)
					XSZipDashCancel(p, s, xs, true) RB.dualpress = 5 return --
				elseif not (p.cmd.buttons & BT_JUMP) and not (p.cmd.buttons & BT_SPIN)
					RB.spin = 0 RB.truespin = 0 RB.jump = 0 RB.custom1 = 0
				end		
			end
		elseif xs.jump and (xs.jump < 7)
			XSZipDashCancel(p, s, xs, true)
			xs.jump = 1
			XSStartSnapDive(p, s, xs, true) return --
		end
	end
	
	if not xs.zipdashslide
		RestoreZipDashCam(p, s, xs)
		if (ZIPSPEED > 100) or xs.FreeFlight
			local rvolume = min(255, 24+FixedInt( (p.speed*3/2)+abs(s.momz)) )	
			if SUPER == 3
				for i = 32,56,13
					local ghs = XS_RingBurstWave(s, 12, (i==32) and SKINCOLOR_SILVER or SKINCOLOR_XSSUPERBLUR)
					ghs.ditchtracer = nil
					ghs.sttfade = true
					ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS20
					ghs.GSoffsetX,ghs.GSoffsetY,ghs.GSoffsetZ = i*s.scale,i*s.scale,(i/2)*-s.scale
					ghs.spritexscale = (i == 56) and $*2 or $*3/2
					ghs.spriteyscale = ghs.spritexscale
				end
				XSon.Sound(s, sfx_xssfps, rvolume)
				XSon.Quake(s, 70<<16, 2, 800<<16)
			else
				XSon.Sound(s, (xs.darkphase) and sfx_xssfps or sfx_xssfxa, rvolume)
			end
		elseif (s.eflags & MFE_UNDERWATER)
			XSon.Sound(s, sfx_s3k7d)
		end
		if SUPER == 3
			s.spritexscale,s.spriteyscale = FRACUNIT+16800,FRACUNIT-14650
			s.spriteyoffset = -2<<16
			s.XSstretch = {timer = 6, Xspeed = 3200, Yspeed = 3000}	
			XS_RingBurstDust(p, s, xs, 280*s.scale)
			XS_RingBurstStripes(s, SKINCOLOR_SILVER, AST_ADD, FRACUNIT)
		
			if P_IsObjectOnGround(s)
				local ghs = XSon.PuffDust(s, SKINCOLOR_WHITE)
				ghs.spriteyscale = $/3
				ghs.momx,ghs.momy = s.momx/2,s.momy/2
			else
				local ghs = XSon.FlickerLight(s, s.color, 3, ANGLE_45, FF_TRANS20, FRACUNIT+47500)
				ghs.spriteyoffset = 10<<16
			end	
			if xs.FreeFlight
				if (xs.zipdashslide > 14)
					XSZipDashCancel(p, s, xs)
					xs.zipdash = 0 xs.zipdashslide = 0 xs.zipcontinue = nil
					xs.FreeFlight.ziprise = 0 return --
				end
			end
		end
	end
	
	xs.zipdashslide = $+1
	if not (SUPER == 3 and (xs.zipdashslide >= 15))
		if s.momz and (s.momz*P_MobjFlip(s) > 5*s.scale) and (SUPER != 3)
			s.momz = FixedDiv(FixedMul($,63), 64)
			if s.XS_zipflip
				if ((s.state >= S_PLAY_XDIVEFLIP1) and (s.state <= S_PLAY_XDIVEFLIP4))	
					s.rollangle = 0
					local ghs = P_SpawnGhostMobj(s)
					ghs.scale = s.scale
					ghs.blendmode = AST_ADD
					ghs.fuse,ghs.tics = 7,9
					ghs.destscale = 1
					ghs.color,ghs.colorized = XSon.TrailColor(s, xs), true		
				elseif not ((s.state >= S_PLAY_XZIPBRAKESUPER) and (s.state <= S_PLAY_XZIPSLIDE2))
					s.state = ((SUPER == 3) and not P_IsObjectOnGround(s)) and S_PLAY_XZIPBRAKESUPER or S_PLAY_XZIPSLIDE	
				end
			elseif s.state == S_PLAY_XZIPSLIDE or p.panim == PA_RUN or p.panim == PA_SPRING or p.panim == PA_FALL
				if not ((s.state >= S_PLAY_XDIVEFLIP1) and (s.state < S_PLAY_XDIVEFLIP4))
					XSon.GibSound(s, sfx_xssfxl, 170)	
					
					s.state = S_PLAY_XDIVEFLIP1
					S_StartSound(s, sfx_xssfxl)
					s.XS_zipflip = true
					XSon.SwapStretch(p, s, xs)	
					s.spriteyoffset = -5<<16
					s.spritexscale,s.spriteyscale = FRACUNIT-10000,FRACUNIT+24000			
					s.XSstretch = {timer = 5, Xspeed = 2400, Yspeed = 4600}
					
					XSon.sparkles(p, s, xs, 99, 4)
				end
			end
		elseif not ((s.state >= S_PLAY_XZIPBRAKESUPER) and (s.state <= S_PLAY_XZIPSLIDE2))
			s.state = ((SUPER==3) and not P_IsObjectOnGround(s)) and S_PLAY_XZIPBRAKESUPER or S_PLAY_XZIPSLIDE		
		end
		p.panim = PA_DASH
	else
		s.XS_zipflip = nil
		if ((s.state >= S_PLAY_XZIPBRAKESUPER) and (s.state <= S_PLAY_XZIPSLIDE2))
			if not (xs.FreeFlight and (s.eflags & MFE_UNDERWATER))
				s.state = S_PLAY_RUN
			end
		end
	end
	local IFFYMODS = (p.isxmomentum or p.machspeedmode) and true or false
	
	p.drawangle = s.angle+1
	xs.zipdashangle = s.angle --Can adjust angle right now!
	if IFFYMODS==true
		xs.groundfriction,xs.runfriction,s.friction = min($, 59392), min($, 59392),min($,59392)	
	else
		xs.frictionlock = 2
		xs.groundfriction = s.friction
		xs.runfriction,s.friction = FRACUNIT,FRACUNIT
	end
	p.normalspeed = 3
	xs.delayzip = 0
	
	local slowrate = 1
	if SUPER and (p.speed < 64*s.scale) and not (xs.direct<4)
		slowrate = 0
		xs.zipdashslide = $+4
	elseif (xs.direct < 4) or IFFYMODS==true
		slowrate = 7
		xs.zipdashslide = $+3
		if IFFYMODS==true and (xs.zipdashslide > 40)
			XSZipDashCancel(p, s, xs, true) 
			p.pflags = $|PF_JUMPED & ~(PF_JUMPDOWN|PF_THOKKED)
			s.state = S_PLAY_FALL return --
		end
	end
	if p.powers[pw_sneakers] and SUPER!=3
		xs.zipdashslide = $+3
	end
	if (p.speed > 50*s.scale) and not (xs.FreeFlight and (s.eflags & MFE_UNDERWATER))
		slowrate = max(1,$)
		s.momx = FixedDiv(FixedMul($, 22), 22+min(slowrate, 4))
		s.momy = FixedDiv(FixedMul($, 22), 22+min(slowrate, 4))
	else
		if (p.speed < 38*s.scale)
			xs.zipdashslide = $+5
			p.normalspeed = (SUPER==3) and 19<<16 or 36<<16
		end
		if slowrate
			s.momx = FixedDiv(FixedMul($, 50), 50+slowrate)
			s.momy = FixedDiv(FixedMul($, 50), 50+slowrate)
		end
	end

	if (
	xs.custom1
	or (not xs.FreeFlight) and xs.spin	--Sliding Conditions
	)
	and not ((xs.zipspam or 0) > 27)
		S_StopSoundByID(s, sfx_xssfxa)
		xs.zipdashslide = 0
		xs.zipdash = 4
		xs.zipcontinue = true
		xs.zipspam = (xs.zipspam and $+9) or 9
		XSAirGroundState(p, s, xs)
		XSon.SwapStretch(p, s, xs)
		s.spritexscale,s.spriteyscale = FRACUNIT+2500,FRACUNIT-800
		xs.zipstretch = 2
		xs.delayzip = 0
		xs.spin = max($, 2)
		p.cmd.buttons = $|BT_SPIN
		if not P_IsObjectOnGround(s) and (s.momz*P_MobjFlip(s) > -50*s.scale) and SUPER != 3
			if (s.momz*P_MobjFlip(s) > 6*s.scale)
				s.momz = 6*s.scale*P_MobjFlip(s)
			end
			P_SetObjectMomZ(s, -16<<16, true)
		end
	else
		if (xs.zipdashslide > ((SUPER == 3) and 15 or 99))
		or (p.speed < 2*s.scale)
			xs.zipcd = 99 --Time to stop.
			S_StopSoundByID(s, sfx_xssfxa)
		else
			XSon.AimLine(p,s,xs)
		end
	end
	if xs.jump == 1 and not xs.FreeFlight
		XSZipDashCancel(p, s, xs, true) 
		p.pflags = $|PF_JUMPED & ~(PF_JUMPDOWN|PF_THOKKED)
	end
end

--Set all Zip Dash Properties ASAP.
XSon.ZipDashProperties = function(p,s,xs, ZIPSPEED)
	ZIPSPEED = (ZIPSPEED) or 100
	if xs.zipdash == 1 and xs.delayzip and not xs.zipcontinue
		xs.delayzip = $-1
	else
		xs.zipdash = $+1
	end
	if (xs.zipdash > 1) and (ZIPSPEED > 100) 
		p.pflags = $|PF_JUMPDOWN
	end
	p.runspeed = 24<<16+222
	p.pflags = $|PF_JUMPDOWN
	p.charflags = $|SF_NOSKID --No skidding during this.
	xs.changedcharflags = 2
	xs.waszipping = true
	p.powers[pw_noautobrake] = max($, 2) --Buzz off, misguided bandaid.
	
	--Grant bonk immunity? This lets you jump up launch slopes automatically.
	if s.standingslope and abs(s.standingslope.zdelta) > 19999
		if s.z > xs.lastz+(9*s.scale) and not (s.eflags & MFE_VERTICALFLIP)
		or s.z < xs.lastz-(9*s.scale) and (s.eflags & MFE_VERTICALFLIP)
			xs.bonkimmune = 4
		end
	end
	
	if p.speed < (ZIPSPEED/2)*s.scale
	and xs.zipdash > 5  --Keep up the speed or you'll lose the state!
		if not xs.zipdashslide
			xs.zipcd = $+1
		end
	else
		xs.zipcd = 0
	end
	if xs.justleaped
		xs.justleaped = $+1
		if P_IsObjectOnGround(s) or p.powers[pw_carry] or P_PlayerInPain(p)
			xs.justleaped = 0
		end
	end
	
	local FORCEADJUST = 0
	if ((XSon.Teleport(p,s,xs, xs.zipdashangle)) > 260*s.scale)  --Adjust angle incase of teleportation or stop altogether.
		FORCEADJUST = 1
	elseif (s.eflags & MFE_SPRUNG) and P_IsObjectOnGround(s) --Spring/booster
		FORCEADJUST = 2
	elseif p.powers[pw_flashing] and (p.powers[pw_flashing] < 12) 
	and (P_PlayerTouchingSectorSpecial(p, 3, 6) or P_PlayerTouchingSectorSpecial(p, 3, 5)) --Speed Pad
		FORCEADJUST = 3
	end
	if FORCEADJUST != 0
		if FORCEADJUST == 1 
			if p.powers[pw_nocontrol]
			or (s.state==S_PLAY_STND) and (s.tics > 102)
			or s.momx==0 and s.momy==0 and s.momz == 0
				xs.zipdashpause = 0 
				XSon.GetOutOfTheFloor(p, s)
				XSZipDashCancel(p, s, xs) 
				xs.zipdash,xs.zipdashslide = 0,0
				xs.spin,xs.custom1 = 99<<16,99<<16
				s.momx,s.momy,s.momz = 0,0,0 return false --
			end
			xs.rollcd = 16 
			if not (s.eflags & MFE_SPRUNG)
				local ZFLIP = P_MobjFlip(s)
				local FLOORZ = (ZFLIP==-1) and s.ceilingz or s.floorz
				if (abs((s.z+s.momz)-FLOORZ) < 48*s.scale) or P_IsObjectOnGround(s)
					s.z = FLOORZ
					s.momz = -ZFLIP
				end
			end
		end
		xs.zipdashangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)		
	end
	
	local SUPER = XS_Super(p)

	--Pause on enemies for a sec to do a quick attack.
	if xs.zipdashpause and (xs.zipdash >= 5) 
	and P_IsObjectOnGround(s) and not xs.zipdashleap and SUPER!=0
	and xs.zipdashx != nil and xs.zipdashy != nil --Stick to these coordinates!
	and (FORCEADJUST!=1)
		s.eflags = $ & ~MFE_JUSTHITFLOOR
		xs.rollcd = max($, 8)	
		if (FixedHypot(xs.zipdashx-s.x, xs.zipdashy-s.y) <= (p.speed+(350*s.scale)) ) --Sanity distance check!
		and P_TryMove(s, xs.zipdashx, xs.zipdashy, true)
			if not xs.zipdashslide and not xs.FreeFlight	
				s.state = S_PLAY_ROLL
				if (xs.zipdashpause == 5) 
					XSon.SwapStretch(p, s, xs)
					xs.spinstretch = 5
					s.spritexscale, s.spriteyscale = FRACUNIT+30000,FRACUNIT-25000
					xs.alteredoffset = true
					s.spriteyoffset = 5<<16 --Make it look like we're sticking to the ground for 1 frame longer!
				end		
			end
			xs.zipcd = 0 
		
			local ghs = P_SpawnGhostMobj(s)
			ghs.colorized = true ghs.scale = s.scale*2 ghs.destscale = 1
			ghs.blendmode = AST_ADD ghs.spriteyscale = FRACUNIT-15000
			ghs.fuse,ghs.tics = 22,23 ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS90
			P_InstaThrust(ghs, p.drawangle+ANGLE_90, (40-(xs.zipdashpause*12))*s.scale)
		else
			xs.zipdashpause = 0
			XSon.GetOutOfTheFloor(p, s)
		end
	end
	
	if FORCEADJUST==0 and not xs.zipdashslide and not (xs.zipdashleap and SUPER!=3)
		if not xs.zipdashleap
			xs.ringburstcd = 2
		end
		--Gradually turn Sonic, but only a little!
		if (xs.direct > 3) and not ((s.flags2 & MF2_TWOD) or twodlevel)
			local BACK = ((p.cmd.forwardmove < -44) or ((p.cmd.forwardmove < -4) and (abs(p.cmd.sidemove) < 7))) and true or false
			local diffdiv = 17
			local maxang = ANG1+(SUPER*ANG2)
			if p.powers[pw_sneakers] or SUPER == 3 --Turning speed.
				if xs.FreeFlight
					maxang = ANG30
					diffdiv = 4
					if (xs.FreeFlight.LockOn and xs.FreeFlight.LockOn.valid)
						BACK = false
						s.XSlastzipD = nil
						maxang = ANG1*35
					end
				else		
					maxang = ANG20
					diffdiv = 6
				end
			elseif not P_IsObjectOnGround(s)
				diffdiv = 15
				maxang = ANG1*3
			end			
			if (BACK == false or s.XSlastzipD) and xs.zipdashangle != nil
				local DIFF = (xs.zipdashangle-xs.turnang)
				if BACK == true --You're holding directly BACKwards. Uhh...continue whatever you were doing for a bit...??
					if s.XSlastzipD != nil
						if s.XSlastzipD > 0
							xs.zipdashangle = $-min(maxang, s.XSlastzipD)
						else
							xs.zipdashangle = $-max(-maxang, s.XSlastzipD)
						end
						s.XSlastzipD = FixedMul(FixedDiv($, 8),9) --Diminish if you keep holding back
						if s.XSlastzipD < ANG1 s.XSlastzipD = nil end
					end
				else			
					if (DIFF > ANGLE_180)
						DIFF = InvAngle(InvAngle(DIFF)/diffdiv)
 					else
						DIFF = $/diffdiv --Slow AF turning, lessgo
					end
					if DIFF
						if DIFF > 0
							xs.zipdashangle = $-min(maxang, DIFF)
						else
							xs.zipdashangle = $-max(-maxang, DIFF)
						end
						s.XSlastzipD = DIFF
					end
				end
			end
		else
			s.XSlastzipD = nil
		end
	end
	
	--Quit dashing when you're exiting!
	if p.exiting //or (p.awayviewtics and p.awayviewmobj)
		if P_IsObjectOnGround(s) or xs.zipdashslide
			xs.spin = 0
		end
	end
	
	xs.lastsector = R_PointInSubsectorOrNil(s.x, s.y) or xs.lastsector
end

--Project an aiming line to help angle your spindash release better. Fancier version of my initial SMSreborn one.
XSon.AimLine = function(p,s,xs)
	if xs.tutorials=="none"
	or xs.FreeFlight and xs.FreeFlight.LockOn
	or ((s.flags2 & MF2_TWOD) or twodlevel or s.currentaxis)
		return --
	end
	local DIST,ghs = 15*s.scale
	local ZDIST = (s.z-xs.lastz) + ((s.eflags & MFE_VERTICALFLIP) and s.height/2 or s.height/3)
	local DONTDRAW = 0
	if (p!=displayplayer) or (p==displayplayer) and not CV_FindVar("chasecam").value
		DONTDRAW = MF2_DONTDRAW
	end
	if s.XS_aimtable == nil s.XS_aimtable = {} end
	local MIN = max(1, s.scale/69)
	for i = 1,90
		ghs = s.XS_aimtable[i]
		if not (ghs and ghs.valid)
			ghs = P_SpawnMobjFromMobj(s, s.momx+FixedMul(DIST*i,cos(s.angle)), s.momy+FixedMul(DIST*i,sin(s.angle)), ZDIST, MT_XSZIPDASHTRAIL)
			ghs.sprite = SPR_XTK7
			ghs.color = SKINCOLOR_SUPERSILVER1
			ghs.renderflags = $|RF_SEMIBRIGHT
			ghs.dispoffset = -99
			s.XS_aimtable[i] = ghs
		else
			P_MoveOrigin(ghs, (s.x+s.momx)+FixedMul(DIST*i,cos(s.angle)), (s.y+s.momy)+FixedMul(DIST*i,sin(s.angle)), s.z+ZDIST)
		end
		ghs.momx,ghs.momy = s.momx/2,s.momy/2
		ghs.scale = max(50, max(12500, s.scale/2)-(i*MIN))--Avoid sigsevs, thx
		ghs.tics = 2
		ghs.flags2 = ($ & ~MF2_DONTDRAW)|MF2_SHADOW|DONTDRAW
		if (ghs.scale < 55) break end --		
	end
end