--╔════════════════════════════╗══════════════════════════════════════════════════════════════════════════════════════════════#
--║     Snap Dive Functions    ║ Includes the kick-follow up and snap dive trick bounce.
--╚════════════════════════════╝══════════════════════════════════════════════════════════════════════════════════════════════#
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

--Be very thorough on where we can snap!
local VerticalSnap = function(s, m, xs)
	local ZLIMIT = (250*s.scale) + min(50*s.scale, abs(s.momz)) + min(50*s.scale, abs(s.momx)+abs(s.momy))
	if xs
		if xs.snapdivekick or (s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK4)
			if not ((s.state == S_PLAY_XKICK1) and (s.tics > 1))
				return --
			end
		end
		if xs.supersonic
			ZLIMIT = $*3/2 --500-ish max for super
			if xs.FreeFlight and xs.FreeFlight.LockOn
				ZLIMIT = $*3 --Really big in SST
			end
		end
		if not m=="skipmobj"
			if not (m and m.valid) and xs.snaptarget
				m = xs.snaptarget
			end
		end
	end
	local mZ = s.z
	local GRAVITY = P_GetMobjGravity(s) or 32678
	s.momz = (abs(GRAVITY)+1)*P_MobjFlip(s)
	
	if m != "skipmobj"
		if m and m.valid
			local ADDZ = 2
			if (m.flags & MF_SPRING) and (m.info.mass) and (m.info.painsound==sfx_spring)
				ADDZ = max(2, FixedDiv(FixedMul(m.height/4, m.spriteyscale), FRACUNIT) )
			else
				ADDZ = max(2, FixedDiv(FixedMul(m.height*4/5, m.spriteyscale), FRACUNIT) )
			end
			for i = 1,2
				mZ = m.z+(ADDZ/i)
				if (abs(s.z-mZ) < ZLIMIT)
					if (mZ < s.ceilingz) and (mZ > s.floorz)
						s.z = mZ return true --
					elseif i == 2 and not (xs and xs.snapstickz!=nil)
						if not (s.eflags & MFE_VERTICALFLIP) and (mZ < s.floorz) --Settle for close enough, I guess.
							s.z = s.floorz+2 return 1 --
						elseif (s.eflags & MFE_VERTICALFLIP) and (mZ > s.ceilingz)
							s.z = s.ceilingz-2  return 1 --
						end
					end	
				end
			end
		end
	end
	if xs and xs.snapstickz !=nil
		mZ = xs.snapstickz
		if (abs(s.z-mZ) < ZLIMIT)
			if (mZ <= s.ceilingz) and (mZ >= s.floorz)
				s.z = mZ return true  --
			else
				if not (s.eflags & MFE_VERTICALFLIP) and (mZ < s.floorz)
					s.z = s.floorz+2 return 2 --
				elseif (s.eflags & MFE_VERTICALFLIP) and (mZ > s.ceilingz)
					s.z = s.ceilingz-2 return 2 --
				end
			end		
		end		
	end-- print("\x85 Couldn't vertically snap!!")
end

--Remove Snap Dive Values.
rawset(_G, "XSSnapDiveReset", function(p, s, xs, extra)
	if xs.snaptarget local t = xs.snaptarget --First, failsafe.
		if t.valid
			s.spritexscale,s.spriteyscale = FRACUNIT,FRACUNIT
			if not xs.supersonic and not (xs.samesnapCD or xs.lastsnaptarget)
				xs.lastsnaptarget, xs.samesnapCD = t, 22
			end
		end
		xs.snaptarget = nil
	end
	if s.xs_TWODy != nil			
		local TWOD = ((s.flags2 & MF2_TWOD) or twodlevel) and true or false
		if TWOD==true
			P_MoveOrigin(s, s.x, s.xs_TWODy, s.z)
		end
		s.xs_TWODy = nil
	end
	xs.snapstickx,xs.snapsticky,xs.snapstickz = nil
	xs.snapdiveangle = p.drawangle
	xs.snapdivedirection = 0 --0 is forwards, 1 is left, 2 is right, 3 is backwards. 
	xs.snapdivekick,xs.snapdiveroll = 0,0
	if xs.ChaosSnap != nil
		XSon.RemoveCSOverlay(xs)
		p.pflags = $ & ~PF_THOKKED
		if xs.FreeFlight and (xs.FreeFlight.SnapOverHeat > 37) and xs.FreeFlight.LockOn and xs.FreeFlight.LockOn.valid
			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,s.momy = $/2,$/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
	
	xs.ChaosSnap = nil --Super Sonic snap dive.
	
	XSRemoveDiveHB(xs) --Snap Dive Hitbox object. If it hits stuff, then Sonic snaps to it!
	xs.snapOLDXY = nil
	
	--Fix a minor bug.
	if (s.sprite2 & ~FF_SPR2SUPER) == SPR2_SKID and P_IsObjectOnGround(s)
		if not xs.zipdash and not xs.zipdashslide and not p.skidtime 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
		end
	end
	
	if extra 
		xs.poutysonic = 0
		if s and s.valid
			XSon.SwapStretch(p, s, xs)
			s.rollangle = 0
		end
		xs.alteredscale = false
		xs.alteredrollangle = true
	end
end)

--Snap dive bounce off an enemy/object! Note! m isn't always necessarily present! SnapDiveAttack"
rawset(_G, "XSSnapDiveBounce", function(p, s, xs, FORCESFX)
	local m = xs.snaptarget
	local SPRING = false
	local SUPER = XS_Super(p)
	
	if xs.zipdash
		XSZipDashCancel(p, s, xs)
	end
	if xs.FreeFlight
		xs.FreeFlight.noZcontrol = 20
	end
	XSRemoveDiveHB(xs)
	xs.jump = 99
	p.pflags = $|PF_JUMPDOWN --Gotta do this part at least...
	s.spritexscale,s.spriteyscale = FRACUNIT,FRACUNIT
	s.rollangle = 0
	xs.ringburstcharge = 0
	XSon.SwapStretch(p, s, xs)
	xs.alteredscale = false
	xs.alteredrollangle = true
	
	local speed = min(45<<16, max(18<<16, abs(FixedInt(xs.lastmomz))) )
	local div = max(6, min(8,10-abs(FixedInt(xs.lastmomz or 1/3))) )
	
	XSSpawnJumpDust(s, speed , div, xs)
	
	if m and m.valid
		if xs.snapOLDXY != nil
			m.spritexscale,m.spriteyscale = xs.snapOLDXY.Xscale, xs.snapOLDXY.Yscale
			m.dispoffset = xs.snapOLDXY.dispoffset
		else
			m.spritexscale,m.spriteyscale = FRACUNIT,FRACUNIT
		end	
		local MAXDIST = 350*s.scale
		if m.player
			if xs.FreeFlight and (xs.FreeFlight.LockOn==m)
				MAXDIST = 725*s.scale
			end
			if not m.XSstretch --Extra measure.
				m.XSstretch = {timer = 3, Xspeed = 1, Yspeed = 1}
			end
		end	
		local DIST = FixedHypot(s.x-m.x, s.y-m.y)
		if (DIST < MAXDIST)		
			VerticalSnap(s, m, xs) --So we don't lose height from the snap.
		end
	end
	if not (m and m.valid and (m.flags & MF_SPRING)) --and not (s.eflags & MFE_SPRUNG)
	
		s.state = S_PLAY_SPRING
		s.momz = abs(xs.lastmomz)
		if (not (SUPER >= 2) or (p.cmd.buttons & BT_JUMP) or not (m and m.valid and (m.flags & (MF_ENEMY|MF_MONITOR|MF_BOSS)) ) )
		and (FORCESFX != true)
			XSon.Sound(s, sfx_xssfx4, 80) --Sound is a bit too loud combined with the new enemy death sounds...
		else
			XSon.Sound(s, sfx_xssfx8, 240)
			local SFX = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
			SFX.flags2 = MF2_DONTDRAW SFX.tics = 99 SFX.fuse = 99
			S_StartSoundAtVolume(SFX, sfx_xssfxs, 120)
		end
		xs.rollangle,xs.snapdive = 0,0
		xs.alteredscale,xs.alteredoffset = true,true
		s.spriteyoffset = -5<<16
		s.spritexscale,s.spriteyscale = FRACUNIT-9000,FRACUNIT+12000
		xs.jumpstretch = 6
		xs.didjumpboost = false
		p.xstauntstance,p.xsringburst = nil,nil
		xs.rollcd = 0
		p.pflags = $|PF_JUMPDOWN|PF_STARTJUMP|PF_JUMPED & ~(PF_THOKKED|PF_SPINNING|PF_STARTDASH)
		if FORCESFX != true
			xs.jumpboost = 5
			XSStartSnapDiveKick(p, s, xs) --Allow kick input
		end
		
		local MOMZ = s.momz*P_MobjFlip(s)
		if xs.uwdebuff
			if (MOMZ < 7*s.scale) --Minimum jumpheight underwater.
				P_SetObjectMomZ(s, 7<<16)	
			end
		else
			if (MOMZ < 10*s.scale) --Minimum jumpheight.
				P_SetObjectMomZ(s, 10<<16)
			else
				local DLIMIT = (xs.supersonic) and 30*s.scale or 23*s.scale
				if (MOMZ < DLIMIT) --Add a bit more, but only to a certain extend!
					local ADDZ = (MOMZ < DLIMIT-6*s.scale) and 95356 or 42500
					if xs.supersonic
						ADDZ = $*3
					elseif xs.darkphase
						ADDZ = (xs.darkphase==2) and $*2 or $*3/2
					end
					P_SetObjectMomZ(s, ADDZ, true)
				end
			end	
		end
		
		if m and m.valid and m.health
		and (FixedHypot(FixedHypot(s.x-m.x, s.y-m.y), s.z-m.z) < 400*s.scale) --Close enough to be reasonably doing something?
			if (m.type == MT_SPIKE) and not p.powers[pw_invulnerability] and not SUPER == 3
				m.momz = 0
				P_DamageMobj(s, m, m, 1, DMG_SPIKE) --What were you thinking exactly...?
				P_DoPlayerPain(p)
			elseif (m.type == MT_TNTBARREL) or (m.type == MT_WALLSPIKE) or (m.type == MT_SPIKE)
				P_KillMobj(m, s, s)
			else
				if (m.flags & MF_MISSILE)
					local ghs = P_SpawnGhostMobj(m)
					ghs.destscale = $*99 ghs.scalespeed = $*2
					m.momz = $-(30<<16*P_MobjFlip(s))
					m.momx = $/2 m.momy = $/2
					XSon.Sound(m, sfx_kc3c)
					m.missileguy = s --Tag the missile so you can't jump off it again.
				end
				if (m.flags & (MF_ENEMY|MF_BOSS))
					local GRUNT = sfx_xsvock+P_RandomRange(0, 3)
					if GRUNT == s.XSlastkickgrunt
						GRUNT = ($==(sfx_xsvock+3)) and sfx_xsvock or $+1
					end
					XSVoice(p, s, xs, GRUNT, true)
					s.XSlastkickgrunt = GRUNT
					if not (m.type == MT_SPRINGSHELL) and not (m.type == MT_YELLOWSHELL)
						P_DamageMobj(m, s, s)
					else
						xs.strongspring = 2 --Spring off them instead of damaging them.
						if not (s.momz*P_MobjFlip(s) > 36*s.scale) --Gain extra height from these.
							P_SetObjectMomZ(s, (m.type == MT_YELLOWSHELL) and 13<<16 or 4<<16, true)
						end
					end
				elseif (m.flags & MF_MONITOR) and m.health and (m.flags & MF_SHOOTABLE)
					m.momz = 0
					P_DamageMobj(m, s, s)
				end
			end
		end
	else
		SPRING = true
		xs.jumpboost = 0
		xs.didjumpboost = true
		if not (m and m.valid and (m.flags & MF_SPRING) and not m.info.damage)
			xs.snapdivetrick = 0
		end
	end
	--Do a fancy sideways flip. 'cause you're Sonic, naturally!
	if (abs(p.cmd.sidemove) > 15) and not (XS_CRAPPYPRIORITIES==1)	
		local momzstabilizer = FixedInt(abs(s.momz))+(SPRING and 35 or 1)
		local sideflip = FixedDiv(FixedMul(p.cmd.sidemove*99000, 46), 46+momzstabilizer) 				
		sideflip = min(max($, -89<<16), 89<<16) --Minmax it so it doesn't go beyond 90 degrees.
		xs.alteredrollangle = false	
		XSDoRoll(p, s, xs, -sideflip, 0, 19, nil, 3<<16)	
	end
	if SUPER
		if (xs.direct > 6)			
			if ((abs(p.cmd.sidemove) > 6) or (p.cmd.forwardmove < 0))
				local DIRECT = max(50, abs(p.cmd.sidemove)+((p.cmd.forwardmove<0) and p.cmd.forwardmove*-1 or 0) )
				P_Thrust(s, xs.turnang, DIRECT*(s.scale/55)*SUPER)
			end	
			if (SUPER >= 2)
				xs.ziptrailticket = 13
				XSZipTrail(s, nil, 1, nil, 24)
				if (p.speed < 105<<16)
					p.speed = 105<<16
				end
			end
		end
		if not xs.darkphase
			if not (s.momz*P_MobjFlip(s) > 20*s.scale) 
				P_SetObjectMomZ(s, SUPER*(FRACUNIT*3/2), true)
			end		
			local ghs = XSon.FlickerLight(s, nil, 3, 0, FF_TRANS30, FRACUNIT*3/2)
			ghs.spriteyoffset = 10<<16
			XSon.sparkles(p,s,xs, 70, 5, 11, abs(s.momz/2), SKINCOLOR_SUPERGOLD3, AST_ADD, FF_HORIZONTALFLIP, nil, FRACUNIT/4)
		end			
	end
	if m and m.valid --Reset their spritexscale
		if (m.momz*P_MobjFlip(s) > s.scale*2)
		and (m.flags & (MF_SOLID|MF_PAIN|MF_SPECIAL|MF_PUSHABLE|MF_MISSILE)) --Wha? Do NOT get carried up with me, sheesh.
			m.momz = -s.scale*P_MobjFlip(s)
		end
	end
	--Finally, remove our target and failsafe coordinates.
	xs.snaptarget,xs.snapOLDXY = nil	
	xs.snapstickx,xs.snapsticky,xs.snapstickz = nil,nil,nil	
end)

--Gradually roll player! Leave speed unchecked for auto-speed calculations.
rawset(_G, "XSDoRoll", function(p, s, xs, start, goal, timer, direction, speed)
	if (XS_CRAPPYPRIORITIES==1)	
		s.rollangle = 0
		xs.rollangle,xs.destroll,xs.rolltimer,xs.rollspeed = 0,0,0,0
		return --
	end	
	if direction == "rng" --Random Direction?
		xs.rolldirection = P_RandomRange(0,1) or -1	
	elseif not direction or direction == "inverse"
		xs.rolldirection = 1
		if start > goal
			xs.rolldirection = -1
		end
		if direction
			xs.rolldirection = $*-1
		end
	elseif direction --Careful, you generally only want this to be -1 or 1.
		xs.rolldirection = direction
	else
		return --idfk
	end
	
	xs.rollangle = FixedAngle(start) --Use FRACUNIT, not angles directly.
	xs.destroll = FixedAngle(goal)
	xs.rolltimer = timer
	s.rollangle = xs.rollangle
	
	if speed --Automatic or manual?
		xs.rollspeed = FixedAngle(speed)
	else
		xs.rollspeed = (abs(xs.destroll)-abs(xs.rollangle))/xs.rolltimer
	end
	
--	CONS_Printf(p,"\x86 Starting Roll = "+((xs.rollangle or 1)/ANG1))
--	CONS_Printf(p,"\x86 Dest Roll = "+((xs.destroll or 1)/ANG1))
--	CONS_Printf(p,"\x86 RollSpeed = "+((xs.rollspeed or 1)/ANG1))
end)

--Quick function to remove the dive hitbox.
rawset(_G, "XSRemoveDiveHB", function(xs) 
	if xs.snapdivehitbox
		if xs.snapdivehitbox.valid xs.snapdivehitbox.state = S_NULL end
		xs.snapdivehitbox = nil
	end
end)

--SnapDiveStart", start the snap dive!
rawset(_G, "XSStartSnapDive", function(p, s, xs, SPAMDIVE, ZIPSPEED) 
	if p.powers[pw_nocontrol] --Don't bother if...
	or p.powers[pw_carry]
	or (xs.snapdivetrick and xs.snapdivetrick < 9) or p.xswallrun or p.xsringburst
	or p.homing or p.gotflag or xs.snapdivewait or (s.state==S_PLAY_DEAD)
		return --
	end
	local SUPER = XS_Super(p)
	local LEAPED = ((xs.justleaped) or (s.XS_JL)) and true or false
	if SUPER==0 and ((xs.zipdashleap or 0) > 12)
		XSon.StopShout(s)
		if ((xs.zipdashleappower or 0) > 16)
			XSVoice(p, s, xs, (leveltime%2) and sfx_xsvop7 or sfx_xsvop5)
		end
	end
	if (xs.zipdash or xs.leapaftermath) and not xs.zipdashleap
		XSZipDashCancel(p, s, xs, true) --Not allowed here.
	end
	XSRemoveDiveHB(xs) 
	S_StopSoundByID(s, sfx_xssfxo)
	
	--Set the Snap Dive flags!
	xs.snapdive = 1
	XSSnapDiveReset(p, s, xs, true)	
	p.pflags = $|PF_THOKKED|PF_NOJUMPDAMAGE|PF_JUMPED & ~(PF_STARTJUMP|PF_SPINNING|PF_STARTDASH|PF_CANCARRY|PF_FORCESTRAFE)
	s.state = S_PLAY_XSNAPDIVE
	xs.didjumpboost = true
	xs.jump = FRACUNIT
	xs.jumpboost,xs.frontflip,xs.dustcd,xs.rollcd = 0,0,0,0
	xs.painrecovery = 0
	s.XS_JL,xs.railpose,xs.damageboost,xs.forceangle = nil
	if xs.walljump or xs.walljumpP
		xs.walljump,xs.walljumpP = nil
		p.aiming = 0	
	end
	if xs.darkphase 
		xs.snapdive = 0
		XSon.DarkRush(p,s,xs, ZIPSPEED) return --Perform this instead.
	end
	
	--Snap Dive Hitbox object. If it hits stuff, then Sonic snaps to it!
	xs.snapdivehitbox = P_SpawnMobjFromMobj(s,s.momx,s.momy,0, MT_XSSNAPDIVE)
	local hb = xs.snapdivehitbox
	hb.flags2 = $|MF2_DONTDRAW
	hb.target,hb.tracer = s,s
	hb.fuse,hb.tics = 2,2
	if SUPER == 3
		local RB = xs.FreeFlight
		if RB
			if RB.LockOn and RB.LockOn.valid
				hb.radius = $*10
				hb.height = $*9 --beeg.
			else
				hb.radius = $*4
				hb.height = $*3
			end
		else
			hb.radius = $*5/3
			hb.height = $*3/2
		end
	elseif xs.uwdebuff and (s.eflags & MFE_UNDERWATER)
		hb.radius = 80*s.scale
		hb.height = 120*s.scale
	end
	
	--Horizontal speed! The dive snap is mostly a redirectional ability.
	if ((s.flags2 & MF2_TWOD) or twodlevel)
		s.momy = 0
		xs.snapdivedirection = 0
		xs.turnang = s.angle
		xs.direct = 0
		xs.stopstick = max(1, xs.stopstick or 0)
		p.cmd.sidemove,p.cmd.forwardmove = 0,0
		if (p.speed < 70*s.scale)
			P_Thrust(s, s.angle, (max(1, SUPER)*6)*s.scale)
		end	
	else
		if (xs.direct > 2)
			--What DIRECTION are we snap-diving towards? If we're not holding any direction, do the backwards animation.
			--xs.snapdivedirection is used for determining which animation to perform.	
			xs.snapdivedirection = 0 --Forwards
			
			if (abs(p.cmd.sidemove) > 28)
				if p.cmd.sidemove < 0
					xs.snapdivedirection = 1 --Left
					xs.snapdiveroll = -ANG30
				else
					xs.snapdivedirection = 2 --Right
					xs.snapdiveroll = ANG30			
				end
			end
			if p.cmd.forwardmove <= -28 --Backwards.
				xs.snapdivedirection = 3
			end
			
			--Now change our momentum. Thrust in half of your input direction, half of your old momentum.
			local newmom = (abs(s.momx)+abs(s.momy) or 1)/2 
			if (SUPER >= 2) or p.powers[pw_sneakers]
				newmom = (abs(s.momx*2/3)+abs(s.momy*2/3) or 1)
				s.momx = $-(s.momx*2/3)
				s.momy = $-(s.momy*2/3)
			else
				s.momx = $/2 s.momy = $/2
			end
			if xs.snapdivedirection != 3
				P_Thrust(s, xs.turnang, newmom)	
			end
		
			--Finally, add some extra speed thrust if applicable!
			local MAXSPEED = ((s.eflags & MFE_UNDERWATER) and 12*s.scale) or 49*s.scale
			local ADDEXTRA = ((s.eflags & MFE_UNDERWATER) and 5) or 8
			if not xs.uwdebuff
			and not xs.snapdivedirection == 3
				if (SUPER >= 2) --busted
					MAXSPEED = $*5/2
					ADDEXTRA = $*5
				elseif p.powers[pw_sneakers]
					MAXSPEED = $*2
					ADDEXTRA = $*5/2
				elseif xs.zipdashleap and xs.zipdash
					MAXSPEED = $*5/4
					ADDEXTRA = $*2
				end
			end
			P_Thrust(s, xs.turnang, ADDEXTRA*s.scale)	
			if (p.speed > MAXSPEED) --Correct speed
				P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), MAXSPEED) 
			end
		end
		if (LEAPED) and (p.speed > 49*s.scale)
			if not xs.diveslowdown xs.diveslowdown = 3 end
			if (p.speed > 56*s.scale)
				P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 56*s.scale) 
			end
		end			
	end

	--This is the angle Sonic'll face for a while.
	
	--Do the forward/backwards animation?
	if not xs.snapdivedirection or xs.snapdivedirection == 3
		s.state = S_PLAY_XDIVEFLIP1
		XSon.SwapStretch(p, s, xs)
		xs.snapdiveangle = p.drawangle
		if xs.snapdivedirection == 3 --Backflip!
			s.spriteyscale = FRACUNIT+5000
			s.spritexscale = FRACUNIT-10000
			xs.divestretch = 5
			xs.diveslowdown = 12
		else
			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
	elseif p.speed --This is the angle Sonic'll face for a while.
		xs.snapdiveangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	end	
	
	--What sound to play?
	if xs.uwdebuff and (s.eflags & MFE_UNDERWATER)
		S_StartSound(s, sfx_s3k7d)
		for d = 1,(P_RandomRange(1,4))
			local bubble = P_SpawnMobj(s.x-P_RandomRange(-35,35)*s.scale, s.y-P_RandomRange(-35,35)*s.scale, s.z+P_RandomRange(-2,20)*s.scale, MT_SMALLBUBBLE)
			if bubble and bubble.valid
				bubble.scale = s.scale bubble.fuse = 99
			end	
		end
		if xs.wasingoo and (s.momz*P_MobjFlip(s) < -s.scale) and SUPER != 3
			P_SetObjectMomZ(s, -99500, true)
		end
	else
		if SUPER != 3 --Specific, specific...
			S_StartSound(s, sfx_thok)
		else
			S_StopSoundByID(s, sfx_thok)
			S_StartSoundAtVolume(s, sfx_thok, 190)
		end
	end

	--Lastly, enhance it with Chaos Snap?
	if SUPER == 3
		xs.snapdivetrick = 0
		local SPEED,MOMZ,ZMOMDIV,XYMOMDIV,TPANGLE,ADD	
		
		--Forwards/sidesnap.	
		if (xs.direct > 7)
			local MAXDIRECT = min(50, (xs.direct*3)/2)
			local DIRECT = (p.cmd.forwardmove<0) and 35 or max(35, MAXDIRECT-(abs(p.cmd.sidemove)/4) )
			SPEED = FixedDiv(FixedMul( max(53*s.scale, p.speed-10*s.scale) , DIRECT), MAXDIRECT)
			if xs.FreeFlight
				if (SPEED <= 85*s.scale) SPEED = $*5/4 end
			elseif not (p.cmd.buttons & BT_SPIN)
				ZMOMDIV = (s.momz*P_MobjFlip(s) <= 0) and 4 or 2
			end
			if (SPEED > 85*s.scale) SPEED = FixedDiv(FixedMul($,8),9) end --Stability's important here, use the zipdash for nutso speed.
			TPANGLE = xs.turnang		
		end	
		--Going upwards?
		if not (xs.direct > 30)
			local FLIP = (p.cmd.buttons & BT_SPIN) and -1 or 1
			MOMZ = (xs.FreeFlight) and 30*s.scale or 20*s.scale
			ADD = (xs.FreeFlight) and 7*s.scale or 5*s.scale
			ZMOMDIV = nil
			
			--Flipped BS.
			if (s.eflags & MFE_VERTICALFLIP)
				if FLIP == -1
					MOMZ = max(25*s.scale, min($, s.momz+3*s.scale))
				else
					MOMZ = max((xs.FreeFlight) and -70*s.scale or -35*s.scale, min(-17*s.scale, s.momz-ADD))
				end
			else
				--The real shit!
				if FLIP == -1
					MOMZ = min(-25*s.scale, s.momz-3*s.scale)
				else
					MOMZ = min((xs.FreeFlight) and 70*s.scale or 35*s.scale, max(17*s.scale, s.momz+ADD)) --Not MEANT for climbing up, but allowed!
				end
			end
			XYMOMDIV = (xs.direct > 20) and 3-FLIP or 4-FLIP
		end
		if xs.FreeFlight
			local RB = xs.FreeFlight
			RB.forceanim = nil
			
			local P,MINOVER = 0, RB.SnapOverHeat or 0
			if RB.LockOn and RB.LockOn.valid
				P = (RB.PvPHEALTH<6000) and 2 or (RB.PvPHEALTH<16000) and 1 or 0
				if MINOVER and RB.painstun and (RB.painstun>2)
					MINOVER = $*2/3
				end
			end
			local IFRAMES = max(1+P, 16-MINOVER)
			RB.DodgeFrames = max(RB.DodgeFrames or 0, IFRAMES)
			
			if RB.LockOn and RB.LockOn.valid
				RB.SnapOverHeat = max($+8-P, 13-P)
				if (RB.SnapOverHeat > 32)
					RB.SnapOverHeat = 57-(P*2)
				end
			end
			if xs.ChaosLambent
				if xs.ChaosLambent.SFX and xs.ChaosLambent.SFX.valid xs.ChaosLambent.SFX.state = S_NULL end
				xs.ChaosLambent = nil
				S_StopSoundByID(s, sfx_xssfpr) S_StopSoundByID(s, sfx_xssfp1) S_StopSoundByID(s, sfx_xssfp6)
			else
				S_StopSoundByID(s, sfx_xssfp3) S_StopSoundByID(s, sfx_xssfp4) S_StopSoundByID(s, sfx_xssfp5)
			end
			RB.painstun = nil
			RB.noXYcontrol,RB.noZcontrol = 12,7
		end		
		--Apply everything, then SNAP!
		xs.ChaosSnap = {
		timer = 13, 
		momz = MOMZ, 
		XYmomdiv = XYMOMDIV, 
		speed = SPEED, 
		angle = TPANGLE,
		Zmomdiv = ZMOMDIV,
		didrise = (SPAMDIVE or (not MOMZ)) and true or false
		}		
		XSon.ChaosSnap(p,s,xs, xs.ChaosSnap)
	else
		--Cut vertical momentum strongly.
		if (s.momz*P_MobjFlip(s) > 8*s.scale)
			s.momz = $/2
		elseif (s.momz*P_MobjFlip(s) > 4*s.scale)
			s.momz = 4*s.scale*P_MobjFlip(s)
		end

		--Mild downwards thrust.
		local fallspeed = FixedDiv(FixedMul(3<<16, 32678), P_GetMobjGravity(s) or 32678)
		if xs.uwdebuff fallspeed = $/5 end
		P_SetObjectMomZ(s, -abs(fallspeed), true)
		
		--Go down immediately after a trick no matter what.
		if xs.snapdivetrick
			local fallspeed = FixedDiv(FixedMul(5<<16, 32678), P_GetMobjGravity(s) or 32678)
			if xs.uwdebuff fallspeed = $/5 end
			
			if (s.momz*P_MobjFlip(s) > abs(fallspeed))
				s.momz = -(abs(fallspeed))*P_MobjFlip(s)
			end
		end
		xs.snapdivetrick = 0
	end
end)

--Swap from a dive to a trick!
rawset(_G, "XSStartSnapDiveTrick", function(p, s, xs) 		
	xs.snapdivetrick  = 1
	if not (s.eflags & MFE_SPRUNG)
		s.state = S_PLAY_XZIPSLIDE
	end
	XSon.SwapStretch(p, s, xs)
	s.spriteyscale = FRACUNIT+5000
	s.spritexscale = FRACUNIT-4500
	xs.zipstretch = 5
	xs.jump = 99
	p.pflags = $|PF_JUMPED|PF_JUMPDOWN & ~(PF_CANCARRY|PF_SPINNING)
	xs.snapdive = 0 
	XSon.Sound(s, sfx_xssfxg)
	XSRemoveDiveHB(xs)
	if (s.eflags & MFE_UNDERWATER) --Cut resistance for a bit.
		s.XuwF = 20<<16
		s.XuwF2 = 15<<16
	end
end)

--Kick the enemy instead of hopping off them! "StartSnapDiveKick"
rawset(_G, "XSStartSnapDiveKick", function(p, s, xs)
	if not (xs.snapdivetrick and (xs.snapdivetrick < 9) and xs.snaptarget and xs.snaptarget.valid)
		return --
	end
	local t = xs.snaptarget
	xs.lastsnaptarget = t
	xs.samesnapCD = (xs.supersonic) and 20 or 35
	if not (t and (t.type == MT_SPIKE))
		if not xs.FreeFlight
			if not ( (p.cmd.buttons & BT_SPIN) and xs.spin and (xs.spin < 10) )
			and not ( (p.cmd.buttons & BT_CUSTOM2) and xs.custom2 and (xs.custom2 < 10) )
			and not ( (p.cmd.buttons & BT_TOSSFLAG) and xs.tossflag and (xs.tossflag < 10) )
				return --
			end
		elseif (t == xs.FreeFlight.LockOn) --Autokick if they're your Tenkaichi LockOn target.
		--
		else 
			if not (
			(p.cmd.buttons & (BT_CUSTOM2|BT_TOSSFLAG|BT_ATTACK)) and (xs.FreeFlight.custom2 <= 1) 
			or (p.cmd.buttons & BT_SPIN) and (xs.FreeFlight.truespin < 3) 
			)
				return --
			end
		end
	end
	
	--Correct target type?		
	if not ((t.flags & MF_SPRING) and not (t.flags & (MF_ENEMY|MF_BOSS)))		
	and not ((t.flags & MF_PUSHABLE) and (t.height > s.height*3)) --Can kick small objects!
	
		local FF = xs.FreeFlight
		if FF
			FF.refreshC1 = nil
			FF.noZcontrol = 30
			FF.noXYcontrol = 38
		end
		
		--Player can be kicked, but only under very specific conditions to not become obnoxious.
		if t.player and t.player.xsonictable
			local tp = t.player local xss = tp.xsonictable
			if tp.XSonicMenu or xs.darkphase or (t.XS_PVPCD) or (s.XS_PVPCD)
				return false --
			else
				local TENKAICHI = nil
				if xs.supersonic and xss and xss.supersonic and t.skin=="xsonic"
					if not (xs.FreeFlight and xs.FreeFlight.LockOn==t)
						TENKAICHI = "tenkaichi"
					end
				end
				if (XSon.PsuedoPVP(t, p, TENKAICHI)==true)
				or IsXSonic(t)
				or (tp.xstauntstance and IsXSonic(t))  --Taunt ME, will you?!
				or (tp.tauntwait and t.state == S_PLAY_TAUNT) 
				or (t.sms_tauntcooldown and t.state == S_SMSTAUNT1) 
				or (t.skin == "adventuresonic" and t.state == S_PLAY_FLY) 
				or (t.skin == "knuckles" and t.state == S_PLAY_WAIT) --No chuckling!
				or ((t.sprite2 & ~FF_SPR2SUPER) == SPR2_HUG_) --No hugging!		
					if (xs.coopPvP!=true) 
					or not (xss and xss.coopPvP==true)
						return --
					end
					--Continue
				else
					return --
				end
			end
			--So it IS a valid player! Stop EVERYTHING they could potentially be doing.
			local CANTAFFECT = ((xss.supersonic or tp.powers[pw_super]) and not xs.supersonic) and true or false
			if tp.hypermysticsonic
				CANTAFFECT = true
			end
			if (CANTAFFECT!=true)
				if RB and RB.MeteorSmash
					s.momx = FixedDiv(FixedMul($,90),91)
					s.momy = FixedDiv(FixedMul($,90),91)
					s.momz = FixedDiv(FixedMul($,69),70)

					t.momx = FixedDiv(FixedMul($,90),91)
					t.momy = FixedDiv(FixedMul($,90),91)
					t.momz = FixedDiv(FixedMul($,69),70)
				else
					t.momx,t.momy,t.momz = s.momx,s.momy,s.momz
				end
				tp.drawangle = p.drawangle+ANGLE_180
				
				xss.walljump,xss.walljumpP,xss.forceangle = nil
				
				xss.jump,xss.spin,xss.custom3 = 99<<16,99<<16,99<<16
				
				XSon.SwapStretch(p, s, xs)	
				s.spritexscale,s.spriteyscale = FRACUNIT+25000,FRACUNIT+10000			
				s.XSstretch = {timer = 5, Xspeed = 4600, Yspeed = 2400}
				
				local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_XSTRACKER2)
				if ghs and ghs.valid
					ghs.SnapDiveStretch = 18 ghs.target = t
				end
				xss.ringburstcd = 16
				tp.pflags = $|PF_JUMPDOWN & ~(PF_THOKKED|PF_JUMPED|PF_SPINNING|PF_CANCARRY)
				if xss.snapdive or xss.ChaosSnap
					XSSnapDiveReset(tp, t, xss, true)
				end
				if xs.ChaosLambent
					XSon.RemoveLambentHitBoxes(p)
					xs.ChaosLambent = nil
				end						
				if xss.ChaosLambent
					XSon.RemoveLambentHitBoxes(tp)
					xss.ChaosLambent = nil
				end		
				if xss.zipdash
					XSZipDashCancel(tp, t, xss)
				end
				if tp.xswallrun
					tp.xswallrun = nil
				end					
			end
			xs.lastsnaptarget = m
			if (CANTAFFECT!=true)
				if xss.FreeFlight
					local RBB = xss.FreeFlight
					RBB.refreshC1,RBB.forceanim = nil
					RBB.jump,RBB.spin,RBB.custom2,RBB.custom3 = 99<<16,99<<16,99<<16,99<<16
					xss.ZoomLines = 15
					RBB.shock = 3
					if xss.ChaosLambent
					and not (xss.ChaosLambent.timer and ((xss.ChaosLambent.timer >= 15) or (xss.ChaosLambent.timer <= 38)))
						xss.ChaosLambent = nil
					end
					if t.state != S_PLAY_XZIPBRAKESUPER --S_PLAY_FALL 
						S_StopSound(t)
						t.state = S_PLAY_XZIPBRAKESUPER 
						
						t.spritexscale,t.spriteyscale = FRACUNIT+30000,FRACUNIT+18000
						t.spriteyoffset = -4<<16
						t.XSstretch = {timer = 4}
						XSon.ExpandingGlow(t, SKINCOLOR_BONE, 16, 97, true, AST_ADD)
					end
					t.sprite2 = SPR2_MLEL|FF_SPR2SUPER
					t.frame = ($ & ~FF_FRAMEMASK)
					
					t.momx,t.momy,t.momz = $/2+(s.momx/3),$/2+(s.momy/3),$/2+(s.momz/3)
					t.drawangle = p.drawangle+ANGLE_180
				else
					t.angle = tp.drawangle
				end
			end
			local SFX = P_SpawnMobjFromMobj(t,0,0,0, MT_THOK)
			SFX.fuse = 19 SFX.tics = 19 SFX.flags = MF2_DONTDRAW
			S_StartSound(SFX, sfx_xssfpo) S_StartSound(SFX, sfx_xssfxu)
		end
		local ANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
		XSon.SwapStretch(p, s, xs)

		for i = 1,2
			if not (P_TryMove(s, s.x-FixedMul((t.radius+3*s.scale)*2, cos(ANGLE)), s.y-FixedMul((t.radius+3*s.scale)*2, sin(ANGLE)), true))
				break --
			end
		end
		
		xs.strongspring,s.XSkickextend = nil
		xs.sprung = 0			
		p.viewrollangle,s.rollangle = 0,0
		s.momx,s.momy,s.momz = t.momx,t.momy,t.momz
		s.state = S_PLAY_XKICK1
		p.pflags = $|PF_JUMPDOWN & ~(PF_CANCARRY|PF_SPINNING)
		P_SetObjectMomZ(s, FRACUNIT/2)
		xs.snapdivetrick = 7
		xs.snapdivekick = 23
		xs.spin = 99<<16 xs.custom1 = 99<<16 xs.jump = 99<<16 xs.custom3 = 99<<16
		XSKick(p, s, xs, t) return true --
	end
end)

--Spawn kicked object from sp. s is source. i is fluff.
local function XSKickEggBox(sp, i, s, SPRITE, FRAME)
	local ghs = P_SpawnMobjFromMobj(sp,0,0,sp.height, MT_EXPLODE)
	ghs.renderflags = $|RF_SEMIBRIGHT
	ghs.destscale = $*99
	ghs.scalespeed = FRACUNIT/7
	
	local gag = P_SpawnMobjFromMobj(sp,0,0,9,MT_THOK)	
	gag.sprite = (SPRITE == nil) and SPR_TVEG or SPRITE
	gag.frame = (FRAME == nil) and C or FRAME
	gag.fuse,gag.tics = 77,99
	gag.flags = MF_NOBLOCKMAP|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
	gag.rollangle = XSLocalRNG(-32,32,s)*ANG1
	if P_IsObjectOnGround(gag)
		gag.z = $+(gag.scale*P_MobjFlip(gag))
	end
	XS_QuickDust(gag)
	
	P_SetObjectMomZ(gag, 10*s.scale)	
	P_InstaThrust(gag, s.angle, 25*s.scale)	
	return gag
end

--XS_KickGFX(s, COLOR, XYOFFSET, ZOFFSET, TRACER, CSCALE, NOFLOOR)
--Currently kicking...! t is the target.
rawset(_G, "XSKick", function(p, s, xs, t)
	if (s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK4) and t and t.valid
		local RB = xs.FreeFlight
		if P_IsObjectOnGround(s) --Don't be grounded!
			s.z = $+(s.scale*P_MobjFlip(s))
		end
		s.eflags = $ & ~(MFE_JUSTHITFLOOR|MFE_APPLYPMOMZ)
		P_SetObjectMomZ(s, FRACUNIT/6) --Anti-grav
		xs.snapdivekick = max($-1, 0)
		
		--Adjust to height of target
		if t and t.valid	
			if (t.z+(t.height/2) <= s.ceilingz) and (t.z+(t.height/2) >= s.floorz)
				xs.snapstickz = t.z+(t.height/2)
				VerticalSnap(s, "skipmobj", xs) 
			end
		end
		local SUPER = XS_Super(p) --Superstate is...?
		if SUPER == 3
			if RB
				if RB.RingBurstChase or RB.haymaker
					RB.CLresist,RB.RBmissed,RB.RingBurstChase,RB.RBdiv,RB.RBside,RB.haymaker = nil
				end
				RB.WasRingBursting = nil
				if RB.painstun
					RB.RBupdown,RB.painstack,RB.quickroll,RB.painstun,RB.rollD,RB.C2stun,RB.C1stun = nil
				end
				xs.stopstick = 0				
				RB.refreshC1 = 19
				RB.DodgeFrames = max($,3)
				RB.cantsnap = 1
				RB.snapCD = 0
				RB.CantHitLambence = max(RB.CantHitLambence or 0, 3)
				RB.noXYcontrol,RB.noZcontrol = 2,2
			end
			if (s.state == S_PLAY_XKICK1 or s.state == S_PLAY_XKICK2)
				local tp = t and t.player
				local xss = tp and tp.xsonictable
				local RBB = xss and xss.FreeFlight
				local ANG = s.angle
				
				if RBB
					RBB.shock = 3
					if RBB.RingBurstChase or RBB.haymaker
						RBB.CLresist,RBB.RBmissed,RBB.RingBurstChase,RBB.RBdiv,RBB.RBside,RBB.haymaker,RBB.CLresist = nil
					end
					RBB.WasRingBursting = nil
					RBB.blackbars = 4
					RBB.snapCD = 0
					RBB.refreshC1 = 0
					RBB.noXYcontrol,RBB.noZcontrol = 2,2	
					if RBB.counters
						RBB.SnapOverHeat = min(RBB.SnapOverHeat or 0, 11)
					end
					RBB.cantsnap = 0
					
					if RBB.countered and (RBB.countered < 14)
						RBB.countered = 14
					end
					if RBB.painstun and (RBB.painstun > 8)
						RBB.painstun = 8 --Minimize painstun!
					end
					t.XS_DidBlur = true --Be the less shiny character.
					tp.powers[pw_nocontrol] = 0
					xss.stopbutton = 0
					xss.stopstick = 0
					if RB and RB.MeteorSmash and (RB.MeteorSmash > 32)
					and (tp.cmd.buttons & (BT_TOSSFLAG|BT_CUSTOM2)) --You can't yet.
						RBB.custom2 = 50
						xs.custom2 = 50
					else
						if RBB.magicC2
							RBB.custom2 = 0
							RBB.pressedC2 = 0
							if (s.state == S_PLAY_XKICK1) and not (s.XSautododge) and s.XSkickextend
								if RBB.magicC2.C2 == 1
									RBB.magicC2.clear = true
									RBB.magicC2.C2 = 2
									RBB.pressedC2 = 7
								end
							end
							tp.cmd.buttons = $ & ~(BT_CUSTOM2|BT_TOSSFLAG)
							
						elseif (RBB.custom2 > 1<<15) and not (tp.cmd.buttons & (BT_TOSSFLAG|BT_CUSTOM2))
							RBB.custom2 = 0
						elseif (RBB.custom2==1) or RBB.pressedC2
							if RBB.counters
								RBB.pressedC2 = 16
							end		
						end						
					end
					
					XSon.DarkBody(s, nil,nil,SKINCOLOR_CRIMSON, 2)
					if not (s.XSstretch) --Very violent shaking to announce your upcoming murder attempt.
						s.spritexscale,s.spriteyscale = FRACUNIT-2,FRACUNIT+2
						s.spritexoffset = XSLocalRNG(22<<16,-22<<16,s)
						s.spriteyoffset = XSLocalRNG(4<<16,-28<<16,t)
						s.XSstretch = {timer = 2, Xspeed = 1, Yspeed = 1}						
					end
					if RB --Flickering.
						local ghs = RB.flicker
						if not (RB.flicker and RB.flicker.valid)
							RB.flicker = XSon.Overglow(s, 2, SKINCOLOR_SUPERSILVER1)
							ghs = RB.flicker
						end
						if ghs and ghs.valid
							ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS20
							ghs.fuse,ghs.tics,ghs.sttfade = 3,3,1		
							if (leveltime%2==0)
								ghs.flags2 = $ & ~MF2_DONTDRAW
								ghs.color,ghs.colorized = (leveltime%4==0) and SKINCOLOR_KETCHUP or SKINCOLOR_CRIMSON, true
								XSon.OverGlowThinker(ghs)
							else
								ghs.flags2 = $|MF2_DONTDRAW
							end
						end	
					end
				end
				if s.tics !=5
					if s.tics==10
						local ghs = P_SpawnGhostMobj(s) --Giant avatar ghosts.
						ghs.fuse,ghs.tics = 6,99 
						ghs.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
						ghs.blendmode = AST_ADD
						ghs.colorized = true
						ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60
						ghs.color = (xs.darkphase) and SKINCOLOR_COBALT or P_RandomRange(SKINCOLOR_SUPERSILVER1, SKINCOLOR_SUPERGOLD5)
						ghs.scale = $*3/2
						ghs.dispoffset = -32
						ghs.scalespeed = (s.tics%2) and FRACUNIT/5 or FRACUNIT/4
						ghs.destscale = 99<<16
						ghs.spriteyoffset = $-8<<16
						P_Thrust(ghs, ANG, -16*s.scale) 
					end
					if s.state == S_PLAY_XKICK1
						XS_RingBurstSparkles(s, nil, (leveltime%3==0) and SKINCOLOR_SUPERGOLD4 or SKINCOLOR_CRIMSON)
					end	
				elseif s.state == S_PLAY_XKICK1
					S_StopSoundByID(s, sfx_xssfxg)		
				end
			end
		end
		
		--If you're at this point, then kick and recoil!
		if s.tics == 1
			if s.state == S_PLAY_XKICK1
			
				XSon.Quake(s, (30+(10*SUPER))<<16, 3)
				XS_RingBurstStripes(s, nil, nil, -10<<16, nil, s.angle)
					
				if (SUPER>=2)
					if s.XSecho
						s.XSecho.rvolume = min(255, $+30)
						s.XSecho.timer = $+2
						s.XSecho2 = s.XSecho
					end
					XSVoice(p,s,xs, sfx_xssfxm, "ForceEcho") --Not a voiceclip, but we can make it echo anyway!
					XSon.GibSound(s, sfx_xssfpp, (not (t.flags & (MF_BOSS|MF_ENEMY))) and 230 or 150)
				else
					S_StartSound(s, sfx_xssfxm)
				end
				if t.player
					local tp = t.player
					local xss = tp.xsonictable
					local TSUPER = XS_Super(tp)
					
					if (TSUPER > SUPER) --You're the one who's gonna feel that...
						
						S_StartSound(s, sfx_xssfpt)
						XSWindSpawn(t)
						XSSnapDiveReset(p, s, xs, true)
						t.rollangle = 0
						t.spritexscale,t.spriteyscale = FRACUNIT+22000,FRACUNIT+22000
						t.XSstretch = {timer = 3, Xspeed = 7400, Yspeed = 7400}	
						P_DoPlayerPain(p, t, t)
						P_InstaThrust(s, s.angle, -55*s.scale)
						P_SetObjectMomZ(s, 8<<16, true)
						xs.snapdive,xs.snapdivekick,xs.snapdivetrick = 0,0,0 return --
						
				 	elseif xss and xss.FreeFlight
						local RBB = tp.xsonictable.FreeFlight
						RBB.refreshC1 = 16	
						if s.XSkickextend==nil
							if not s.XSstretch
								s.spritexscale,s.spriteyscale = FRACUNIT-25000,FRACUNIT+25000
								s.spriteyoffset = 6<<16
								s.XSstretch = {timer = 5, Xspeed = 4300, Yspeed = 5000}	
							end
							if RB
								RB.holdcam = 11
							end
						end	
						if not (s.XSautododge)	
							
							if RBB.countered 
							or RBB.counters 
							or RB and RB.counters
								if s.XSkickextend == nil --Always extend during counters!	
								and not (RB and RB.dontextendagain)
									local NUM = 0
									if RB and RB.counters
										NUM = RB.counters.num or 0
									elseif RBB.counters
										NUM = RBB.counters.num or 0
									end
									if (NUM < 10) and not (RB and RB.MeteorSmash)
										NUM = (NUM) and $/3 or 0 --Be a liiittle bit easier at this stage.
										s.tics,s.anim_duration = $+max(9, (11-NUM)),$+max(9,(11-NUM))
										s.XSkickextend = max(12, 14-NUM)
									else
										s.tics,s.anim_duration = $+9,$+9
										s.XSkickextend = 12
									end
									if RB
										RB.dontextendagain = 24
									end
								end
								local P = ((netgame==true) and (p!=server)) and 1 or 0
								
								if (
								RBB.custom2 and (RBB.custom2 < 10+P) 
								or (RBB.pressedC2)
								or (tp.cmd.buttons & (BT_CUSTOM2|BT_TOSSFLAG)) and not RBB.custom2
								or xss.ChaosSnap and ((xss.ChaosSnap.init or 0) < (6+P))
								)
									s.XSautododge = 24
									s.XSkickextend,t.XSkickextend = nil	
									S_StopSoundByID(s, sfx_xssfe2) S_StopSoundByID(t, sfx_xssfe2)
									
									if RB and RBB.counters
									and RBB.counters.num and (RBB.counters.num > 1)
									
										--Now teleport randomly across the battlefield.
										local RANG = XSLocalRNG(0, 359, t)*ANG1
										local RDIST = XSLocalRNG(200, 9001, s)*s.scale
										
										P_TryMove(s, s.x+FixedMul(RDIST, cos(RANG)), s.y+FixedMul(RDIST, sin(RANG)), true)
										
										local RZDIST = XSLocalRNG(-2250,2250, ghs or s)*s.scale
										
										if (RZDIST)
											local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
											if (abs(s.z-FLOORZ) < RDIST)
												RZDIST = abs(RZDIST) --Can only go up, then.
											else
												local CEILINGZ = (s.eflags & MFE_VERTICALFLIP) and s.floorz or s.ceilingz
												if (abs(s.z-CEILINGZ) < RDIST)
													RZDIST = -abs(RZDIST) --Can only go down.
												end
											end
											if (s.z+RZDIST < s.ceilingz-64*s.scale) and (s.z+RZDIST > s.floorz+16*s.scale)
												s.z = $+RZDIST
											end
										end
										
										local X,Y,Z = XSon.MidPoint(s, t)
										
										local ghs = XSon.FlickerLight(t, SKINCOLOR_SKY)
										ghs.spritexscale = 4<<16
										ghs.spriteyscale = 46000
										RBB.custom2 = 1
										P_SetOrigin(ghs, X, Y, Z)
										
										ghs = XSon.SquashFlash(t)										
										P_SetOrigin(ghs, X, Y, Z)
										
										ghs = XSon.PuffDust(s, nil, nil, nil, XSLocalRNG(4,  6, s)<<16)
										ghs.dispoffset = 149
										P_SetOrigin(ghs, X, Y, Z)
				
										ghs = XSon.ElectricBody(s, 11, (leveltime%3!=0) and SKINCOLOR_SUPERSKY5 or SKINCOLOR_SUPASONIKKU)
										ghs.blendmode = AST_ADD
										ghs.frame = ($ & ~FF_TRANSMASK)|((leveltime%2) and FF_HORIZONTALFLIP or 0)|FF_TRANS20
										ghs.dispoffset = 147
										ghs.tracer = nil
										ghs.spritexscale = 4<<16
										ghs.spriteyscale = 3<<16
										ghs.spriteyoffset = $-18<<16
										ghs.destscale = 99<<16
										ghs.sttfade = 4
										ghs.scalespeed = FRACUNIT/XSLocalRNG(2,4,ghs)
										P_SetOrigin(ghs, X, Y, Z)	
									
										P_MoveOrigin(t, s.x, s.y, s.z)
										S_StartSound(t, sfx_xssfe2)
										
										RB.countercam,RBB.countercam = 6,6
										
										XSGhostTrail(tp, t, xss, SKINCOLOR_SKY, 15, FF_TRANS20, 170000, {
										x=FixedMul(RDIST/4, cos(RANG)), y=FixedMul(RDIST/4, sin(RANG)), z=RZDIST/4, angle=RANG }, true)
									end								
								else
									s.XSkickextend,t.XSkickextend = nil									
								end
							else	
								local CLEAR = false
								local P = ((netgame==true) and (p!=server)) and 2 or 0
								if RBB.magicC2
									if RBB.magicC2.clear==true and not (RBB.magicC2.C2 > 99)
										CLEAR = true
										RBB.magicC2.nomore = true
									end								
								elseif ( 
								RBB.custom2 and (RBB.custom2 < 10+P) 
								or (RBB.pressedC2)
								or (tp.cmd.buttons & (BT_CUSTOM2|BT_TOSSFLAG)) and not RBB.custom2
								or xss.ChaosSnap and ((xss.ChaosSnap.init or 0) < (6+P))
								)
								and not (RBB.SnapOverHeat > 21)
									CLEAR = true
								end
								if CLEAR==true
									if not xss.hudboost
										xss.hudboost = 4
										xss.ZoomLines = 0
									end
									s.XSautododge = 24
									s.XSkickextend,t.XSkickextend = nil
									RBB.pressedC2 = nil
									if RB
										RB.pressedC2 = nil
									end
									if RBB.magicC2
										RBB.magicC2 = nil
										s.state = S_PLAY_XKICK1
									end
								elseif not (RBB.magicC2 and RBB.magicC2.nomore)	
									if not s.XSkickextend 				
										s.tics,s.anim_duration = $+9,$+9
										s.XSkickextend = 12
									else
										s.XSkickextend,t.XSkickextend = nil
									end
								end
							end
						end
					end
				end
				if not (RB and RB.LockOn and RB.LockOn == t)
					local TABLE
					if SUPER --Use a different shout table for Super Sonic.
						TABLE = {sfx_xsvopb,sfx_xsvopc,sfx_xsvopb,sfx_xsvope, sfx_xsvocq, sfx_xsvopl}
					else
						TABLE = {sfx_xsvop5,sfx_xsvop6,sfx_xsvop8,sfx_xsvopd,sfx_xsvopc,sfx_xsvope,sfx_xsvopl, sfx_xsvocq}
					end
					local NUM = P_RandomRange(1, #TABLE)
					local GRUNT = TABLE[NUM]
					if GRUNT == s.XSlastkickgrunt
						GRUNT = (NUM==#TABLE) and TABLE[1] or TABLE[NUM+1]
					end
					XSVoice(p, s, xs, GRUNT, true) --Different shouts for Super Sonic.
					s.XSlastkickgrunt = GRUNT	
				else
					if RB and RB.MeteorSmash
						s.momx = FixedDiv(FixedMul($,90),91)
						s.momy = FixedDiv(FixedMul($,90),91)
						s.momz = FixedDiv(FixedMul($,69),70)

						t.momx = FixedDiv(FixedMul($,90),91)
						t.momy = FixedDiv(FixedMul($,90),91)
						t.momz = FixedDiv(FixedMul($,69),70)
					else
						s.momx,s.momy,s.momz = 0,0,$/2
						t.momx,t.momy,t.momz = 0,0,$/2
					end
				end 
				
			elseif s.state == S_PLAY_XKICK2
			
				local DODGE = false
				local AOE = true
				local BOUNCEANGLE = s.angle-(p.cmd.sidemove*(ANG1/2))
				
				XS_QuickDust(t)	
				XS_RingBurstDust(p,s,xs, 125*s.scale+(SUPER*s.scale*60) )	
				XSon.Quake(s, 65<<16+(SUPER<<16*6), 2)
				t.spritexscale,t.spriteyscale = FRACUNIT,FRACUNIT --Return target to normal.		

				if t.player or (t.sprite == SPR_ROSY) --Can kick taunting X Sonics. And...Amy? 
				
					t.z = $+(99*P_MobjFlip(t))
					
					if t.player and (t.sprite != SPR_ROSY)
						local tp = t.player local xss = tp.xsonictable
						
						if (tp.powers[pw_super] or xss.supersonic) 
						and not (p.powers[pw_super] or xs.supersonic)
						or tp.hypermysticsonic
							t = s --Get your kick redirected back!
						end

						local RBB = xss.FreeFlight
						local ANG = s.angle --(xs.direct>3) and xs.turnang+ANGLE_180 or 
						local P = ((netgame==true) and (p!=server)) and 2 or 0
						--Oops, they countered / dodged it! Get ready to dodge here!
						
				 		if RBB and (t!=s) and (s.XSautododge or t.XSautododge
						or RBB.custom2 and (RBB.custom2 < (14+P))) and not (RBB.SnapOverHeat > (24+P))
						
							s.XSkickextend,t.XSkickextend = nil
							s.XSautododge,t.XSautododge = nil
							RBB.dontextendagain = nil
							
							XSSnapDiveReset(p, s, xs, true) --Stop snap dive entirely!
							XSRemoveDiveHB(xss) --Remove both parties' hitboxes!
							
							XSon.StartLockOn(s,t)
							DODGE = true
							
							tp.cmd.forwardmove = 50
							xss.stopstick,xss.direct = 0,50
							xs.ZoomLines,xss.ZoomLines = 8,0
							xs.hudboost = 2
							RBB.cantsnap = 1
							RBB.magicC2,RBB.MeteorSmash = nil
							RBB.blackbars,RBB.blackbarsshow,RBB.forceanim,RBB.MeteorSmash,RBB.magicC2 = nil
							RBB.noXYcontrol,RBB.noZcontrol = 0,0	
							RBB.CantHitLambence = 9
							RBB.PvPENERGY = max(0, min(10000, $+100)) --Bonus energy!
							RBB.countered = 36
							RBB.forcesnap = 9,9
							
							if RB --YOU are the victim in this case, getting countered!
								RB.magicC2,RB.MeteorSmash,RB.dontextendagain,RB.forceanim = nil
								xs.BeamClashResults,xss.BeamClashResults = nil
								
								local COUNTERNUM = 0
								if RB.counters and RB.counters.num
									COUNTERNUM = RB.counters.num
								end
								if RBB.counters and RBB.counters.num and (RBB.counters.num > COUNTERNUM)
									COUNTERNUM = RBB.counters.num
								end
								RB.counters = {timer = 45, num = COUNTERNUM+1}
								RBB.counters = RB.counters	
								
								if RB.PvPHEALTH and not RB.NoSlamDamage and (RB.PvPHEALTH > 10)
									XSon.SubtractPvPHealth(10, RB, RBB) --Take very minor damage.
									RB.PvPHEALTH = max($,1) --But never die from this nonsense.
								end
								RB.countered = 36
								RB.stripes = 7
								RB.painflash = 19
								RB.blackbars = 12
								RB.snapCD = 13
								RB.forcesnap = 9
								RB.cantsnap = 0
								RB.SnapOverHeat = max(RB.SnapOverHeat or 0, 15)+4
								RB.noXYcontrol,RB.noZcontrol = 9,3
								xs.custom2,RB.custom1,RB.custom2 = 99<<16,99<<16,99<<16
								xs.stopstick = 3
								RB.custom2 = (p.cmd.buttons&BT_CUSTOM2) and $ or 0
								local SFX = XSLocalRNG(sfx_xssfpn, sfx_xssfpt, t)
								S_StartSoundAtVolume(nil, SFX, 145, p)
								S_StartSoundAtVolume(nil, SFX, 145, tp)
								S_StartSound(s, SFX) S_StartSound(t, SFX)
							end
							RBB.forcetrail = 2
							RBB.SnapOverHeat = 0
							XSStartSnapDive(tp, t, xss)
							S_StartSound(nil, sfx_xssfpu, tp)
							S_StartSound(s, sfx_xssfpt)
							P_InstaThrust(s, t.angle, -8*s.scale)
							P_InstaThrust(t, t.angle, 5*s.scale)
							xss.custom2,RBB.custom2 = 99<<16, 99<<16
							RBB.SnapOverHeat = 0
							RBB.custom2 = (tp.cmd.buttons&BT_CUSTOM2) and $ or 0
							RBB.snapCD = 0
							t.momz = $/3
														
							local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
							local CEILINGZ = (s.eflags & MFE_VERTICALFLIP) and s.floorz or s.ceilingz
							if (abs((s.z+s.momz)-FLOORZ) < 360*s.scale) 
								P_SetObjectMomZ(s, 24<<16, true)
								P_SetObjectMomZ(t, 24<<16, true)
							end
							if (abs((s.z+s.momz)-CEILINGZ) < 400*s.scale) 
								P_SetObjectMomZ(s, -28<<16, true)
								P_SetObjectMomZ(t, -28<<16, true)
							end 
							xs.snaptarget = nil
							xss.snaptarget = s 
							
							local ghs = XS_RingBurstDust(tp, t, xss, "manual", t.z) --Crash wave.
							ghs.spritexscale,ghs.spriteyscale = $+(XSLocalRNG(2,4,s)<<16),$+35555
							ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
							ghs.frame = ($ & FF_FRAMEMASK)|FF_TRANS10
							ghs.tracer = nil
							ghs.fuse,ghs.tics = $+8,$+8	
							
							for i = 0,1
								ghs = XS_CircleWave(t, 1, XSLocalRNG( SKINCOLOR_SUPERSKY2,  SKINCOLOR_SUPERTAN5, s)) 
								ghs.blendmode = AST_ADD
								ghs.angle = t.angle+(ANGLE_90*i)
								ghs.spritexscale,ghs.spriteyscale = ($*5/(5+i)),($*5/(5+i))
								ghs.tracer = nil
								ghs.spriteyoffset = $-3<<16
								ghs.sprite = SPR_XSG2
								ghs.fuse,ghs.tics = $-3,$-3
								ghs.scalespeed = $*3/2
								ghs.frame = 31|FF_TRANS30
							end								
							XSon.Flash(s, PAL_NUKE, 2, 0)
							XSon.ElecticBody(s, 12, SKINCOLOR_SUPERGOLD4) 
							P_NukeEnemies(t, t, 1500<<16, DMG_NUKE)
						else		
							xss.painrecovery = 0
							xss.ZoomLines = 15
							XSon.SwapStretch(tp, t, xss)			
							tp.xstauntstance = nil
							tp.aiming = 0
							if SUPER == 3
								local ghs = P_SpawnMobjFromMobj(t,0,0,0, MT_THOK)
								ghs.tics = 220 ghs.fuse = 220 ghs.flags = MF2_DONTDRAW	
								S_StartSoundAtVolume(ghs, sfx_xssfxp, 180)
								S_StartSoundAtVolume(ghs, (leveltime%2) and sfx_xssfxr or sfx_xssfxt, 180)
								local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_XSTRACKER2) --Smoketrails
								if ghs and ghs.valid
									ghs.target = t ghs.SteamTrail = 1	
								end
								if not (RB and RB.MeteorSmash)
									local ghs = XSon.SuperSonicWind(t, SKINCOLOR_AETHER, 30, FF_HORIZONTALFLIP|FF_TRANS10, FRACUNIT+18000)
									if ghs and ghs.valid
										ghs.sttspeed = 3 ghs.sttfade = 26
									end
								end
							end
							if RB or xss.FreeFlight
								RBB = xss.FreeFlight --It might have been set in motion earlier.
								if not RBB
									XSon.TryFlight(tp,t,xss, nil, 101, 200, "force")
									RBB = xss.FreeFlight
									t.justgotPvPed = 2
								end
								s.XSautododge,t.XSautododge = nil
								
								if RB and RB.MeteorSmash --Followup from a Zipdash Tackle in PvP.
									P_InstaThrust(t, ANG, 85*s.scale) 
									P_Thrust(s, ANG, -15*s.scale) 
									t.momz = -68*s.scale*P_MobjFlip(s)
									local ghs = XSon.ChargePower(t, 50, t)
									if ghs and ghs.valid and ((ghs.frame & FF_TRANSMASK)< FF_TRANS80)
										ghs.color,ghs.colorized = SKINCOLOR_CRIMSON,true
										ghs.frame = $+1<<16
									end
									tp.XSscalecam,p.XSscalecam = 18,18
									tp.camerascale,p.camerascale = 3<<16, 3<<16
									if RBB
										RBB.holdcam = 0
									end
									RB.holdcam = 0,0	
									RB.snapCD = 7
									RB.noXYcontrol,RB.noZcontrol = max(RB.noXYcontrol or 0, 5), max(RB.noZcontrol or 0, 2)
									P_NukeEnemies(s, t, 699<<16)
								--	XSon.Sound(s, sfx_xssfe0, 99)	
									xs.forceangle = {nosangle = true, timer = 7, angle = ANG}
									xs.ZoomLines = 3
									XSon.EarthQuake(s, 5+2, 180*s.scale, 1, 6000+(2*26000), FRACUNIT/(7-2), -ANG30, 32|(FF_TRANS60-(2<<16)), 4+(2*3), SKINCOLOR_SUPERGOLD4, AST_ADD)
									XSon.EarthQuake(s, 3+2, 350*s.scale, 1, 30000+(2*26000), FRACUNIT/(4-2), -ANG10, 32|(FF_TRANS90-2<<16), 3+(2*2), SKINCOLOR_RUBY, AST_TRANSLUCENT)
								else						
									local TMOMZ = (p.cmd.buttons & BT_SPIN) and -26*s.scale or 
									((p.cmd.buttons & BT_JUMP) and not (p.cmd.buttons & BT_SPIN)) and 30*s.scale or -3*s.scale
									if (TMOMZ > -20*s.scale) and (abs(t.z-t.ceilingz) < 300*s.scale)
										TMOMZ = (p.cmd.buttons & BT_SPIN) and -36*s.scale or -24*s.scale
									end
									t.momz = $+(TMOMZ*P_MobjFlip(s))
									if not RB
										XSon.TryFlight(p,s,xs, nil, 101, 200, "force")
									end
									if RB
										RB.noXYcontrol,RB.noZcontrol = max(RB.noXYcontrol or 0, 5), max(RB.noZcontrol or 0, 3)
									end
									P_Thrust(t, ANG, 7*s.scale)
								end
								XSon.StartLockOn(s,t)
								if RBB --Apply painstun right away!
									P_InstaThrust(t, ANG, (t==s) and -76*s.scale or 76*s.scale)
									RBB.windlines = 12
									RBB.speedlines = 8									
									RBB.painstun = FRACUNIT
									RBB.snapCD = 9
									P_Thrust(s, ANG, -31*t.scale)  --Now thrust ME backwards.
									if RB and RB.MeteorSmash 
										XSon.SubtractPvPHealth(140, RBB, RB) --Main damage comes from ground impact!
										RBB.MeteorSmashed = 23 --Get ready to be smashed down!
									else
										XSon.SubtractPvPHealth(330, RBB, RB)
										RBB.MeteorSmashed = 0
										RBB.PvPENERGY = max(0, min(10000, $+40))
										P_Thrust(t, ANG, 19*t.scale)
										if RBB.countered --Shoot them further away than usual.
											P_Thrust(t, ANG, 60*t.scale)
											if not tp.powers[pw_flashing]
												tp.powers[pw_flashing] = 24
											end
										end
									end
									if RB
										RB.snapCD = 16
										RB.countered = nil
									end
									RBB.countered = nil
									RBB.cantsnap = 12
								end	
							end
							if not RB and not RBB
							or not xss.darkphase and not tp.powers[pw_super] and not tp.hypermysticsonic
								t.z = $+99*P_MobjFlip(t) --Do knockback.
								P_DoPlayerPain(tp, s, s)	
								P_SetObjectMomZ(t, 12*s.scale)
							end
							if not tp.hypermysticsonic
								if not RBB
									P_InstaThrust(t, ANG, (t==s) and -27*s.scale or 27*s.scale)
								end
								local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_XSTRACKER)
								ghs.target = t
								ghs.SmokeTrail = 17
							end
						end
						
					else					
						P_DamageMobj(t, s, s)
						if t.KickedByBlueBlur --Wow, what a prick, am I right?
							if t.KickedByBlueBlur.valid t.KickedByBlueBlur.state = S_NULL end
							t.KickedByBlueBlur = nil
						end
						local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_XSTRACKER)
						ghs.target = t
						ghs.RosyKick = 75
						ghs.KickANG = s.angle
						ghs.KickXY = 36*s.scale
						ghs.KickZ = 19*s.scale
						if SUPER != 3
							S_StartSound(t, sfx_cdpcm7)
							S_StartSound(ghs, sfx_cdpcm7)
							S_StartSoundAtVolume(t, sfx_kc4b, 70)
						end
						t.KickedByBlueBlur = ghs
						S_StartSound(t, sfx_kc4c)
					end
					
					XSWindSpawn(t)
					local ghs = XS_CircleWave(s, 1, nil, 16, FRACUNIT)	
					ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60				
					if DODGE != true
						if SUPER==3 and ((XSon.Hyper(p))==true)
							XSon.Quake(s, 99<<16, 8, 1000*s.scale)
							P_NukeEnemies(s, s, 2000<<16)
							XSon.Flash(s, PAL_NUKE, 2)
							XSon.GibSound(s, sfx_zoom, 255)
						end
						S_StartSound(t, sfx_kc4c)
					else
						XSWindSpawn(s)
						xs.snapstickx,xs.snapsticky,xs.snapstickz = s.x,s.y,s.z
						XSSnapDiveReset(p, s, xs, true)
						xs.snaptarget,xs.ringburstcharge,xs.snapdive,xs.snapdivetrick = nil,0,0,0
						p.pflags = $|PF_JUMPDOWN|PF_USEDOWN & ~(PF_CANCARRY|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING) return --
					end
					
				elseif (
				(t.flags & MF_PUSHABLE)
				or (t.type == MT_GARGOYLE)
				or (t.type == MT_BIGGARGOYLE) --Turn boring gargoyles into fun interactable ones.
				)
				and not (t.flags & MF_SHOOTABLE) --Can kick small objects!
					
					t.momz = 0
					t.flags = $|MF_PUSHABLE
					P_InstaThrust(t, s.angle, 26*s.scale)
					if t.KickedByBlueBlur --Wow, what a prick, amirite?
						if t.KickedByBlueBlur.valid 
							t.KickedByBlueBlur.state = S_NULL 
						end
						t.KickedByBlueBlur = nil
					end
					local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_XSTRACKER)
					ghs.target = t
					ghs.PushKick = 55
					ghs.KickANG = s.angle
					ghs.KickXY = 32*s.scale
					ghs.KickZ = (not P_IsObjectOnGround(t)) and 7*s.scale or 0 --Shoot up only if they're already airborne or we might break puzzles.
					t.KickedByBlueBlur = ghs
					S_StartSoundAtVolume(ghs, sfx_kc4c, 99)
					S_StopSound(t)
					t.eflags = $ & ~MFE_PUSHED
					
				elseif (t.type == MT_SPIKE) or (t.type == MT_WALLSPIKE)
					if t.health --Can kick spikes!
						t.momz = 0
						P_KillMobj(t, s, s)
						for m in mobjs.iterate(mobjs) --MF_NOBLOCKMAP. Great. Welp, here's my only choice now.
							if (m.type == MT_SPIKE) or (m.type == MT_WALLSPIKE)
								if m.health and ((FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) <= 200*s.scale)
									P_KillMobj(m, s, s)
								end
							end
						end
					end
					
				elseif (t.type == MT_EGGGUARD) and (t.tracer and t.tracer.valid and (t.tracer.type == MT_EGGSHIELD))
				or (t.type == MT_EGGSHIELD) --Kick away shields!
				
					AOE = false	
					local gag = XSKickEggBox(t, i, s, SPR_ESHI, A) --Kick away the shield!
					if gag and gag.valid gag.spriteyoffset = $-40<<16 end
					if (t.type == MT_EGGGUARD)
						t.tracer.state = S_NULL
					else
						t.state = S_NULL
					end	
					
				elseif (t.flags & (MF_ENEMY|MF_PAIN|MF_BOSS|MF_SHOOTABLE|MF_MONITOR)) --Any of these flags?
				
					--Ahh yes, just kick them, it'll work...
					if (
					(XSon.Charger(t) == true) 
					or (t.type == MT_GSNAPPER)
					or (t.type == MT_SPINCUSHION) and (t.flags & MF_PAIN)
					or (t.type == MT_PYREFLY) and (t.state == S_PYREFLY_BURN) and not ((p.powers[pw_shield]) & SH_PROTECTFIRE)
					or (t.type == MT_CRUSHSTACEAN)
					)
					
					and (SUPER == 0) and not p.powers[pw_invulnerability]
						local POOF = false
						if p.powers[pw_shield] and ( (p.powers[pw_shield]) & SH_ARMAGEDDON)
							
						end
						local ANG = s.angle //p.drawangle
						
						XSSnapDiveReset(p, s, xs, true)
						xs.snapdive,xs.snapdivetrick,xs.snapdivekick = 0,0,0
						xs.snaptarget = nil
						s.XSresist = 0
						s.state = S_PLAY_SPRING
						
						local SFX,DMGT = sfx_spkdth, DMG_SPIKE
						if (t.type==MT_CRUSHSTACEAN)
							if p.powers[pw_shield] and ((p.powers[pw_shield]) & SH_ARMAGEDDON)
								POOF = true
							end
							SFX = sfx_xssfx9
							DMGT = 0
							for i = 1,2
								local ST = (i==1) and t.target or t.tracer
								if ST and ST.valid and not ST.player and ST != t
									P_TryMove(ST, s.x-s.momy, s.y-s.momy, true)
									if not (t.state == S_CRUSHSTACEAN_PUNCH2)
										t.state = S_CRUSHSTACEAN_PUNCH2
									end
									local ghs = XS_CircleWave(ST, 1, SKINCOLOR_SUPERGOLD5, 19, FRACUNIT+28111)
									ghs.frame = E|FF_TRANS30
									ghs.spriteyoffset = $-24<<16
									
									local MINSCALE = min(ST.scale, t.scale)
									ST.scale = MINSCALE*9/2
									ST.destscale = MINSCALE
									
									ghs = P_SpawnGhostMobj(ST)
									ghs.angle = ANG+ANGLE_180
									ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
									ghs.scale = ST.scale
									ghs.fuse,ghs.tics = 11,11
									ghs.destscale = MINSCALE
								end
							end
							XS_RingBurstDust(p, s, xs, 500*s.scale)
							XS_RingBurstSparkles(s, nil, SKINCOLOR_FLAME)
							XSon.sparkles(p,s,xs, 38, 5, 5, 2<<16, SKINCOLOR_GOLDENROD, AST_ADD)
							
							P_NukeEnemies(s, s, 0)
							XSon.Quake(s, 90<<16, 9, 750<<16) XSon.Flash(s, PAL_NUKE, 4, 155<<16)
							S_StartSoundAtVolume(s, sfx_doorc2, 240) S_StartSoundAtVolume(nil, sfx_doorc2, 180, p)
							
						elseif (t.type == MT_PYREFLY)
							SFX = sfx_s233
							DMGT = DMG_FIRE
						end
						xs.painrecovery,xs.bonked = 0,0
						S_StartSound(t, SFX) S_StartSound(s, SFX) S_StartSound(nil, SFX, p)
						
						P_DamageMobj(s, t, t, 1, DMGT) --Don't kick these guys or you'll get hurt instead!-
						if not (s.health) s.momz = 14*P_MobjFlip(s)*s.scale return 69 end --
						
						if s.state != S_PLAY_PAIN --incase you were flashing.							
							local FLASH = p.powers[pw_flashing]
							P_DoPlayerPain(p)							
							p.powers[pw_flashing] = max(21, min($, FLASH)) 
						end
						if (t and t.valid and not (XSon.Charger(t) == true) )
							P_InstaThrust(s, ANG, (t.type==MT_CRUSHSTACEAN) and -30*s.scale or -14*s.scale)
						end
						
						p.drawangle = ANG
						AOE = false 
						DODGE = "dumbass" 
						if POOF==true --properly remove foe.
							P_SpawnShieldOrb(p)
							P_KillMobj(t, s, s)
						end
						
					else		
						P_DamageMobj(t, s, s) --Damage enemy you're targeting.
					end
					
				elseif (t.flags & MF_MISSILE) 
					if t.momx or t.momy --Reflect projectile perfectly.
				
						t.momx = $*-1 t.momy = $*-1 t.momz = $*-1
						t.target = s
						t.spritexscale = FRACUNIT+25000
						t.spriteyscale = FRACUNIT+25000
						XS_QuickDust(t)
						XSWindSpawn(t)
						local ths = P_SpawnMobjFromMobj(t, 0,0,0, MT_EXPLODE)
						P_InstaThrust(ths, s.angle, 16*s.scale)
						local ghs = XS_CircleWave(t, 1, s.color, 24, FRACUNIT+20000)
						if ghs and ghs.valid
							ghs.frame = $+FRACUNIT
						end
					end
				elseif (t.type == MT_STARPOST)
					if (t.sprite == SPR_STPT)
						t.state = S_STARPOST_STARTSPIN
					end
				end
				
				XSSnapDiveReset(p, s, xs, true)
				xs.snapdivetrick = 0
				xs.snapdive = 0
				xs.ringburstcharge = 0
				
				if (SUPER >= 2) --Super AoE!
				
					local ZTHRUST = 0	
					
					p.cmd.buttons = $|BT_JUMP	
					XS_KickGFX(s, (xs.darkphase) and SKINCOLOR_PURPLE or SKINCOLOR_SUPASONIKKU, 144*s.scale, 0, nil, FRACUNIT+45000, true, p.drawangle+(ANG1*41))
					if not (RB and RB.MeteorSmash)
						local ghs = P_SpawnMobjFromMobj(s, FixedMul(500*s.scale, cos(p.drawangle)), FixedMul(500*s.scale, sin(p.drawangle)),-350*s.scale, MT_XSSNAPDIVE)
						if ghs and ghs.valid
							ghs.radius = 250*s.scale
							ghs.height = 700*s.scale
							P_Thrust(ghs, p.drawangle, 99)	
							ghs.KillType = "SonicBoom"
							ghs.target = s ghs.tracer = s
							if RB
								if RB.LockOn and RB.LockOn.valid
									ghs.momz = $+(RB.LockOn.momz/3)
									ZTHRUST = $+(RB.LockOn.momz/3)
								end
								if (p.cmd.buttons & BT_JUMP) and not (p.cmd.buttons & BT_SPIN)
									P_SetObjectMomZ(ghs, 20<<16, true)
									ZTHRUST = $+(20*s.scale)
								elseif (p.cmd.buttons & BT_SPIN) and not (p.cmd.buttons & BT_JUMP)
									P_SetObjectMomZ(ghs, -38<<16, true)
									ZTHRUST = $-(38*s.scale)
								end						
							end							
						end	
						if RB and RB.LockOn and RB.LockOn.valid
							RB.noXYcontrol = max(RB.noXYcontrol or 0, 3)
							P_InstaThrust(s, BOUNCEANGLE-(ANG2), -70*s.scale)			
						else
							P_InstaThrust(s, BOUNCEANGLE-(ANG2), (p.cmd.forwardmove > 39) and -20*s.scale or -29*s.scale) --Recoil!
						end
						P_SetObjectMomZ(s, 7*s.scale - ZTHRUST/6)		
						s.XSboomshot = XSon.SuperSonicBoomShot(s, 70*s.scale, ZTHRUST, nil, 31, FF_TRANS10, 10<<16, xs)
						xs.frontflip = 0
					end
					p.pflags = $|PF_JUMPDOWN|PF_USEDOWN & ~(PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING)
					xs.SuperDMG = 2
					xs.hudboost = 3
					return 77 --
				end
				
				if (s.state != S_PLAY_PAIN) and (DODGE != "dumbass")
					P_InstaThrust(s, BOUNCEANGLE-(ANG2), (p.cmd.forwardmove > 39) and -18*s.scale or -26*s.scale) --Recoil!
					P_SetObjectMomZ(s, (p.cmd.buttons & BT_JUMP) and 6*s.scale or 2*s.scale)
				end
				p.pflags = $|PF_JUMPDOWN|PF_USEDOWN & ~(PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING)
				xs.frontflip = 0
				
				--Small AoE in front of you.
				local ghs = XS_KickGFX(s, nil, 40*s.scale, 0)
				if ghs and ghs.valid 
					if AOE == true
						P_RadiusAttack(s, ghs, 125*s.scale)
					end
					searchBlockmap("objects",function(t,m) --Break monitors and spikes too with the AoE.
						if m.valid and m.health 
							if (m.flags & MF_MONITOR) and (m.flags & MF_SHOOTABLE)
								if not (m.z > s.z+220*s.scale)
								and not (m.z+220*s.scale < s.z)
									P_DamageMobj(m, s, s)
								end
							end
						end
					end, s, s.x-112*s.scale,s.x+112*s.scale, s.y-112*s.scale, s.y+112*s.scale)
					return (DODGE == "dumbass") and 69 or 77 					
				end
			end
		end		
		return 2 --
	else
		xs.snapdivekick = 0 --Uhh, that's not right.
	end
end)


--Let X Sonic use his kick ability to destroy eggman boxes! Superforms do so too because why not.
local function XSSmashEggBox(sp, i, s)
	if s and s.valid and IsXSonic(s) and s.player and s.player.xsonictable
	local p = s.player local xs = p.xsonictable
		if (s.state >= S_PLAY_XKICK2) and (s.state <= S_PLAY_XKICK4)
		or (XS_Super(p) == 3)
			local SFX = P_SpawnMobjFromMobj(sp, 0,0,0, MT_THOK)
			SFX.flags2 = MF2_DONTDRAW SFX.fuse = 99 SFX.tics = 99
			local gag = XSKickEggBox(sp, i, s, SPR_TVEG, C)
			
			if (p == displayplayer)
				S_StartSound(SFX, sfx_bowl, p)
				S_StartSound(SFX, sfx_behurt, p)			
			else
				S_StartSound(gag, sfx_bowl)
				S_StartSound(gag, sfx_behurt)
			end	
			sp.health = 0
			if (sp.type == MT_EGGMAN_GOLDBOX)
				sp.flags = $ & ~MF_SHOOTABLE
			else
				sp.flags = $|MF_NOCLIP & ~MF_SOLID
			end
			sp.state = (sp.info.deathstate)+1 return true --
		end
	end
end
addHook("MobjDeath", XSSmashEggBox, MT_EGGMAN_GOLDBOX)
addHook("MobjDeath", XSSmashEggBox, MT_EGGMAN_BOX)

//Ignore these type of collission
local function XShieldNopeS(s, so)
	if so and IsXSonic(so) and so.player and so.player.xsonictable local xs = so.player.xsonictable
		if xs.snapdive or (xs.snapdivetrick and (xs.snapdivetrick < 16)) 
		or (so.state >= S_PLAY_XKICK1) and (so.state <= S_PLAY_XKICK4)
			return true
		end
	end
end
local function XShieldNope(s, so)
	if so and IsXSonic(so) and so.player and so.player.xsonictable local xs = so.player.xsonictable
		if xs.snapdive  or (xs.snapdivetrick and (xs.snapdivetrick < 16)) 
		or (so.state >= S_PLAY_XKICK1) and (so.state <= S_PLAY_XKICK4)
			return false -- False instead of true!!
		end
	end
end
addHook("TouchSpecial", XShieldNopeS,  MT_EGGSHIELD)
addHook("MobjCollide", XShieldNope,  MT_EGGSHIELD)

--Set the Snap Dive Properties. Called from XS-Timers. SnapDiveProperties" SnapDiveTimer"
XSon.SnapDiveProperties = function(p,s,xs)
	if not s.health return end --
	
	xs.snapdive = min($+1, FRACUNIT)
	xs.snapdivetrick = 0
	
	if xs.snapdivehitbox 
		if xs.snapdivehitbox.valid
			xs.snapdivehitbox.fuse = max($,2)
			xs.snapdivehitbox.tics = max($,2) --Keep it alive.
		else
			xs.snapdivehitbox = nil
		end
	end		
	--Got a target? Switch!
	if xs.snaptarget
		local m,delet = xs.snaptarget, true	
		if m.valid
			if xs.lastsnaptarget != m
				local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_XSTRACKER2)
				if ghs and ghs.valid
					ghs.SnapDiveStretch = 18 ghs.target = m
				end
				xs.lastsnaptarget = m
				if (m.flags == MF_SOLID) and not (m.MakeSolidAgain) and not m.player
					local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_XSTRACKER)
					if ghs and ghs.valid 
						ghs.target = m
						ghs.MakeSolidAgain = 11
						m.MakeSolidAgain = ghs
						m.flags = $ & ~MF_SOLID
					end	
				end
			end
			local MAXDIST = (m.player and xs.FreeFlight and (xs.FreeFlight.LockOn==m)) and 900*s.scale or 500*s.scale
			local mX,mY,mZ = m.x,m.y,m.z+(m.height*4/5)
			local DIST = FixedHypot(FixedHypot(s.x-m.x, s.y-m.y), s.z-m.z)
			if (DIST < MAXDIST) and P_TryMove(s, m.x, m.y, true)
				delet = false
				xs.samesnapCD = 26
				xs.snapstickx,xs.snapsticky,xs.snapstickz = mX,mY,mZ
				VerticalSnap(s, m, xs) --Vertically snap too!
				XSStartSnapDiveTrick(p, s, xs)
			elseif (DIST < 99*s.scale)
				VerticalSnap(s, m, xs) 
			end
		end
		if delet	
			xs.snaptarget,xs.snapstickx,xs.snapsticky,xs.snapstickz = nil
		end
		
	--Cancel Conditions
	elseif (xs.snapdive > 18) --Maybe do snap dive multiple times in midair, but with a cooldown?
	or P_IsObjectOnGround(s) 
	or not (p.pflags & PF_THOKKED)
	or xs.zipdash or xs.bonked or p.powers[pw_carry]
	
		if (xs.snapdive > 18) and (p.pflags & PF_JUMPED)
		and not xs.uwdebuff and (XS_Super(p) >= 2 or p.powers[pw_sneakers])
			p.pflags = $ & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
			s.state = S_PLAY_ROLL
		end
		
		xs.snapdive = 0
		xs.snapdiveangle = nil
		xs.snapdivedirection = 0
		xs.snaptarget = nil
		if xs.snapdiveroll
			xs.snapdiveroll = 0
			s.rollangle = 0
		end
		XSRemoveDiveHB(xs) 
		
		--Break floors below you!
		if (s.eflags & MFE_JUSTHITFLOOR) and P_IsObjectOnGround(s)
			local doit = false
			local sectorz = R_PointInSubsector(s.x, s.y).sector 
			for fof in sectorz.ffloors()
				if (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)
				local line = fof.master
				EV_CrumbleChain(fof)
					if (line.flags & ML_EFFECT5)
   						P_LinedefExecute(P_AproxDistance(line.dx, line.dy) >> FRACBITS, s, fof.target)
					end
					doit = true
				end
			end
			if doit
				s.z = $+5*(s.scale*P_MobjFlip(s))
				xs.snapstickx,xs.snapsticky,xs.snapstickz = s.x,s.y,s.z
				s.momx,s.momy = 0,0
				local GRAVITY = P_GetMobjGravity(s) or 32678
				s.momz = (abs(GRAVITY)+1)*P_MobjFlip(s)
				
				XSGhostTrail(p, s, xs)
				xs.snaptarget = nil
				XSStartSnapDiveTrick(p, s, xs)
				if not (s.eflags & MFE_SPRUNG)
					s.state = S_PLAY_XZIPSLIDE
				end
				xs.snapdivetrick = 4
				p.powers[pw_nocontrol] = max($,7)
			end
		end
		
	--Main active part of the Snap Dive.
	
	elseif (xs.snapdive < 14)
		
		--Snap dive animation
		if xs.snapdivedirection and (xs.snapdivedirection < 3) --sideflip!
		
			if s.state != S_PLAY_XSNAPDIVE and (xs.snapdive < 3)
				s.state = S_PLAY_XSNAPDIVE		
			end
			
			local direct = (xs.snapdivedirection == 1 and 1) or -1 --Left or right?
			if xs.snapdiveangle != nil
				if (xs.snapdive < 5)
					p.drawangle = xs.snapdiveangle + (xs.snapdiveroll or 1/2)
				else
					p.drawangle = xs.snapdiveangle + xs.snapdiveroll
				end
			end
			
			if not (XS_CRAPPYPRIORITIES==1)
				s.rollangle = xs.snapdiveroll
			end
			xs.snapdiveroll = $+((ANG1*36)*direct)
			
			if (xs.snapdive >= 13)
				xs.snapdiveroll = 0
				s.rollangle = 0
				p.drawangle = xs.snapdiveangle
			end
			
		else --Forward roll!
		
			if (xs.snapdive < 6)
				if not ((s.state >= S_PLAY_XDIVEFLIP1) and (s.state <= S_PLAY_XDIVEFLIP4))
					s.state = S_PLAY_XDIVEFLIP1
					if xs.snapdivedirection != 3
						xs.frontflip = 13
					end
				end
			end
			--Lock Sonic visually onto this angle.
			if xs.snapdiveangle != nil and (xs.snapdive < 11)
				p.drawangle = xs.snapdiveangle
			end
		end
		
		--Huge acceleration buff.
		if xs.direct
			local div = (xs.uwdebuff) and 4 or 1
			local boost = min( max(FRACUNIT/div, abs(p.speed-(50*(FRACUNIT/div)))), FRACUNIT/div) 
			boost = (FixedDiv(FixedMul(boost, xs.direct), 50)) --Acceleration is stronger/weaker depending on input...
			local momangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
			local turnang =  abs((momangle-xs.turnang)/ANG1) --MATH.
			if turnang > 96 --Deaccelerate hard if you want to during this!
				boost = $*min(5, (turnang/35))
			end
			if (p.speed < 50*s.scale) or (turnang > FixedInt(abs(p.speed)))
				P_Thrust(s, xs.turnang, boost)
			end
		end
		
		--Afterimages when the acceleration buff is active!
		local ghs = P_SpawnGhostMobj(s)
		ghs.blendmode = AST_ADD
		ghs.colorized = true
		ghs.scalespeed = $*3/2
		ghs.destscale = ghs.scalespeed+1
		if (xs.snapdive < 4)
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS70
			ghs.momx = s.momx or 1/-4 
			ghs.momy = s.momy or 1/-4
			ghs.momz = s.momz or 1/-4
		else
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS90
		end
		ghs.rollangle = s.rollangle
		ghs.spritexoffset = s.spritexoffset
		ghs.spriteyoffset = s.spriteyoffset
		
		if XS_Super(p) >= 2 or p.powers[pw_sneakers]
			if (xs.jump and xs.jump < 5) and (xs.snapdive > 4)
				XSStartSnapDive(p, s, xs)
			end
		end
	end
end

--Properties when doinga snap dive trick. Called from XS-TIMERs. SnapDivetrickProperties" SnapDiveTrickTimer"
XSon.SnapDiveTrickProperties = function(p,s,xs) if not s.health return end --
	local SUPER = XS_Super(p)
	
	xs.snapdive,xs.rollcd = 0,0
	xs.snapdivetrick = ((p.powers[pw_carry]) or (P_PlayerInPain(p))) and 30 or min($+1, FRACUNIT)
	
	--Initial part, snap to enemy and SQUISH.
	if (xs.snapdivetrick < 9)

		if not xs.snapdivekick
			if not (s.eflags & MFE_SPRUNG)
				s.state = S_PLAY_XZIPSLIDE
			end
			XSStartSnapDiveKick(p, s, xs) --Allow kick input.
		end
		p.normalspeed = 3
		
		if xs.snaptarget and xs.snaptarget.valid --Repeat this nonsense.
			local m = xs.snaptarget	
			if xs.snapOLDXY == nil or xs.snapdivetrick == 2
				xs.snapOLDXY = {Xscale = m.spritexscale, Yscale = m.spriteyscale, dispoffset = m.dispoffset}
			end
			xs.ringburstcharge = 0
			if xs.snapdivekick
				local KICK = XSKick(p, s, xs, m)
				if (KICK) --Do kick!
					if KICK == 69 return end
					if (xs.snapdivetrick > 5)
						xs.snapdivetrick = 5
					end
				end
			else
				if (m.flags & MF_SPRING) --Do a spring bounce instead.
					if m.info.damage
						xs.snapdivetrick = max($,6) --Get straight to it if it's got any horizontal power.
					else
						xs.snapdivetrick = max($,4)
						if not xs.strongspring
							xs.strongspring = 1
						end
					end				
				else
					if (m.flags & MF_MISSILE) and (m.target == s)
						p.weapondelay = max($,40) --No infinitely jumping off your own projectiles!
					elseif m.player
						if m.player.xsonictable --Give some cooldown before you can do it again!
							m.player.xsonictable.launchcd = 24
						end
					elseif (m.flags == MF_SOLID) and not (m.MakeSolidAgain)
						local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_XSTRACKER)
						if ghs and ghs.valid 
							ghs.target = m
							ghs.MakeSolidAgain = 11
							m.MakeSolidAgain = ghs
							m.flags = $ & ~MF_SOLID
						end	
					end
				end
				if xs.snapOLDXY != nil
					local ADDX = max(0, s.spritexscale-FRACUNIT)
					local ADDY = abs(s.spriteyscale-FRACUNIT)
					m.spritexscale = max(99, xs.snapOLDXY.Xscale + ADDX)
					m.spriteyscale = max(99, xs.snapOLDXY.Yscale - (ADDY*8/9))	
					m.dispoffset = -45
				end
			end
			for i = 1,2
				if m and m.valid and P_TryMove(s, m.x-s.momx, m.y-s.momy, true)
					VerticalSnap(s, m, xs)
					XSGhostTrail(p, s, xs)
				elseif not xs.snapdivekick
					if i == 2
						s.momx,s.momy = $/2,$/2
					end
					if m and m.valid
						local DIST = FixedHypot(FixedHypot(s.x-m.x, s.y-m.y), s.z-m.z)
						if (DIST < 150*s.scale)
							VerticalSnap(s, m, xs)
						end
					end
				end
			end
			if m and m.valid and (m.type == MT_CRUSHSTACEAN) --Just so the stupid claw targets you better.
				local SCALE = max(m.scale, s.scale)
				for i = 1,4
					P_TryMove(s, m.x-FixedMul(46*SCALE, cos(s.angle)), m.y-FixedMul(46*SCALE, sin(s.angle)), true)
				end
			end
			
		--failsafe incase the enemy vanishes suddenly mid-trick.
		elseif xs.snapstickx != nil and xs.snapsticky != nil and xs.snapstickz != nil
		--	CONS_Printf(p, "\x85"+"Failsafe snap. Coords = "+FixedInt(xs.snapstickx)+" "+FixedInt(xs.snapsticky)+" "+FixedInt(xs.snapstickz))
			xs.snaptarget = nil
			local MAKEGHOST = false
			if P_TryMove(s, xs.snapstickx-s.momx, xs.snapsticky-s.momy, true)
				MAKEGHOST = true
				VerticalSnap(s, "skipmobj", xs)
			else
				local DIST = FixedHypot(s.x-xs.snapstickx, s.y-xs.snapsticky)
				if (DIST < 140*s.scale)	and VerticalSnap(s, "skipmobj", xs) == true
					MAKEGHOST = true
				end
			end
			if MAKEGHOST==true
				XSGhostTrail(p, s, xs)
			else
				s.momx,s.momy = $/2,$/2
			end
		else
			xs.snapdivetrick = max($,8) --The hell? No failsafe either? Skip to 8, I guess.
		end
				
		--Trick off the object!
		if xs.snapdivetrick == 8 or (SUPER >= 2) and not (p.cmd.buttons & (BT_JUMP|BT_SPIN|BT_CUSTOM2|BT_ATTACK|BT_TOSSFLAG)) and not xs.FreeFlight
			if not xs.snapdivekick
				XSSnapDiveBounce(p, s, xs)
			end
		elseif (xs.snapdivetrick < 3)
			XSSpawnDust(s, p.speed+30<<16, 5)
			XSSpawnDust(s, p.speed and p.speed/2 or 3<<16, 8)
		end

	else
				
		xs.snapstickx,xs.snapsticky,xs.snapstickz,xs.snaptarget = nil --Values we no longer need.
		xs.snapdiveroll = 0
		xs.snapdivekick = 0
				
		--The end of the trick!
		if (xs.snapdivetrick > 25) or P_IsObjectOnGround(s)

			xs.snapdivetrick = 0
			xs.snapdive = 0
			p.normalspeed = 36<<16 --Back to defaults!
			s.momz = FixedDiv(FixedMul($, 17), 18)
			xs.diveslowdown = 25
			
		else --during the trick!
			
			p.pflags = $ & ~PF_THOKKED
			p.normalspeed = 55<<16 --Temporary speed buff.	
			s.state = S_PLAY_SPRING
			
			if xs.snapdivetrick == 15 and (s.momz*P_MobjFlip(s) > 15*s.scale)
				xs.Ztrail = 48
			end
			--Speeding ring GFX
			if (xs.snapdivetrick > 11) and (xs.snapdivetrick < 16)
				if (s.momz*P_MobjFlip(s) > 5*s.scale)
					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK)
					ghs.spritexoffset = 80<<16
					ghs.spriteyoffset = 52<<16
					ghs.renderflags = RF_SEMIBRIGHT|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS
					ghs.blendmode = AST_ADD
					ghs.fuse = max(2, 18-xs.snapdivetrick)
					ghs.tics = 44
					ghs.sprite = SPR_XSG1
					ghs.frame = C|(min(FF_TRANS90, (xs.snapdivetrick-5)<<16))
					ghs.scale = FixedDiv(FixedMul(s.scale, 200), xs.snapdivetrick*35)
					ghs.scalespeed = $*(ghs.fuse*2/3)
					ghs.destscale = $*99
					if (s.momz*P_MobjFlip(s) > 10*s.scale)
						XSon.zwind(s)
					end
				end
			elseif (xs.snapdivetrick >= 16)
				if SUPER == 3 and (xs.snapdivetrick%3==0) and (xs.direct > 20) and xs.snapdivetrick == 16 --Supersaiyan speed rings!
					local ghs = XS_CircleWave(s, 1, SKINCOLOR_XSSUPERBLUR, 10)
					ghs.sprite2 = SPR_XSG2
					ghs.frame = Q|FF_TRANS40
					ghs.spriteyoffset = $-12<<16	
				end
			end
			
			if xs.snapdivetrick == 25
				if (xs.direct > 2)
					p.drawangle = xs.turnang
				else
					p.drawangle = R_PointToAngle2(s.x, s.y, s.x+s.momy, s.y+s.momy)
				end
			else
				p.drawangle = (xs.snapdiveangle or 0)+(xs.snapdivetrick*ANGLE_45)
			end
			
			--Allow rapid vertical slowdown if not holding jump!
			if not xs.jump and (s.momz*P_MobjFlip(s) > 11*s.scale)
				s.momz = FixedDiv(FixedMul($, 17), 18)
			end
		end
	end
	if (s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK3) and xs.FreeFlight and xs.FreeFlight.LockOn
		local m = xs.snaptarget	
		if (xs.FreeFlight.LockOn == m) and m.valid
			P_TryMove(s, s.x-FixedMul(48*m.scale, cos(s.angle)), s.y-FixedMul(48*m.scale, sin(s.angle)), true)
		end
	end	
end