--╔════════════════════════════╗══════════════════════════════════════════════════════════════════════════════════════════════#
--║  Super Budokai Tenkaichi   ║ Because I'm an idiot who does stupid stuff for no reason.
--╚════════════════════════════╝══════════════════════════════════════════════════════════════════════════════════════════════#
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

--Start LockOn" Battle between s and t. Both must be in freeflight first, so activate that first if needed.
XSon.StartLockOn = function(s, t)
	local p,tp = s.player, t.player
	if not (tp) or not (p)
	or t.XS_PVPCD or s.XS_PVPCD	
	or ((s.flags2 & MF2_TWOD) or twodlevel) or s.currentaxis --I'm not bothering on this for 2D, lol, hell no.
	or XS_NOPVP==1
		return  --
	end
	if (tp.bot==1) or (p.bot==1)
		if (XS_NOPVP != 2)
			return --
		end
	end
	
	local xs = p.xsonictable local RB = xs and xs.FreeFlight
	local xss = tp.xsonictable local RBB = xss and xss.FreeFlight
	
	if not (RB and RBB) --One of them isn't flying atm!
		if RB and RB.LockOn == t --Then don't be locked onto them!
			RB.LockOn = nil
		end
		if RBB and RBB.LockOn == s
			RBB.LockOn = nil
		end return false --
	end
	if not (RB.LockOn and RB.LockOn.valid)	
		xs.BeamClashResults = nil
		xs.stopbutton = 8
		RB.PvPtime = nil
		RB.LockOn = t
		RB.DodgeFrames = 20
		RB.camdist = 53
		RB.PvPHEALTH = 10000
		if not S_SoundPlaying(s, sfx_xssfpv)
			S_StartSoundAtVolume(s, sfx_xssfpv, 180)
			S_StartSound(nil, sfx_xssfpv, p)
		end
		XSon.SetMusic(p,s,xs)
	end
	if not (RBB.LockOn and RBB.LockOn.valid)
		xss.BeamClashResults = nil
		xss.stopbutton = 8
		RBB.PvPtime = nil
		RBB.LockOn = s
		RBB.DodgeFrames = 20
		RBB.camdist = 53
		RBB.PvPHEALTH = 10000
		if not S_SoundPlaying(t, sfx_xssfpv)
			S_StartSoundAtVolume(t, sfx_xssfpv, 180)
			S_StartSound(nil, sfx_xssfpv, tp)
		end
		XSon.SetMusic(tp,t,xss)
	end
end

--We're doing psuedo-PvP? "m" is our target mobj. "p" is our own player, NOT the target's!
--Doesn't NECESSARILY need to lead into Tenkaichi, can just be kicking people around at times.
XSon.PsuedoPVP = function(m, p, INIT)
	if not (m.player and m.player.xsonictable and IsXSonic(m)) return end --Must be an X Sonic player.
	
	if not (p.mo and p.mo.valid and p.mo.health)
	or (p.mo.XS_PVPCD) or (m.XS_PVPCD)
		return false --Both parties need PvP enabled and not be in cooldown.
	end	
	
	local xs = p and p.xsonictable
	local tp = m.player 
	local xss = tp and tp.xsonictable
	
	if not (xs and xs.coopPvP==true)
	or not (xss and xss.coopPvP==true)
		if INIT=="tenkaichi" --Popup a message if we can't start because of coopPvP being turned off.
			if m.skin=="xsonic" and p.mo.skin=="xsonic"
			and p.powers[pw_super] and tp.powers[pw_super] 
				p.mo.XS_noPvPmsg = 40
				m.XS_noPvPmsg = 40
			end
		end 
		return false --
	end
	
	local RB,RBB = xs.FreeFlight, xss.FreeFlight
	if RBB and RBB.LockOn and RBB.LockOn.valid and (RBB.LockOn != p.mo)
		return false --They're fighting someone else. Abort!
	end
	if RB and RB.LockOn and RB.LockOn.valid and RB.LockOn==m
		return true --We are already in PvP with them, who cares about the other checks
	end
	
	if tp.XSonicMenu
	or (gametyperules & GTR_TEAMS) and (p.ctfteam == tp.ctfteam or p.skincolor == tp.skincolor) 
		return false -- No can do!
	elseif (gametyperules & (GTR_HIDEFROZEN|GTR_TAG))
		if (tp.pflags & PF_TAGIT) and (p.pflags & PF_TAGIT) --Tag/Hide and Seek teams.
		or not (tp.pflags & PF_TAGIT) and not (p.pflags & PF_TAGIT)
			return false --
		end
	end return true --You're good to go!
end

 --Try to shift into a more useful position . (Commented out because desyncs.)
XSon.PvPMagnet = function(p,s,xs,RB, t)
	local DIST = FixedHypot((s.x+s.momx)-(t.x+t.momx), (s.y+s.momy)-(t.y+t.momy))
	local NZDIST = abs((s.z+s.momz)-(t.z+t.momz))
	if (DIST < 142*s.scale)
	or (NZDIST > 240*s.scale) and (DIST < NZDIST/2) and (DIST < 2000*s.scale)
		if (
		p.panim == PA_WALK
		or p.panim == PA_RUN and not (xs.stopstick and xs.zipdash) and not (xs.zipdash and (p.cmd.forwardmove > 38))
		or p.panim == PA_IDLE and (NZDIST < (DIST/2))
		)
		or RB.haymaker
			if not xs.ChaosLambent and not xs.ChaosSnap and not RB.painstun
			and not RB.RingBurstChase -- and not xs.zipdash
			and not xs.snapdivetrick and not xs.ChaosSnap and not (RB.countered and (RB.countered>9))			
				local TANGLE = R_PointToAngle2(s.x, s.y, t.x, t.y) 
				local PUSH = (p.speed<52*s.scale) and s.scale*5 or (p.speed<90*s.scale) and s.scale*3 or (p.speed < 150*s.scale) and s.scale or s.scale/2
				local HALF = false
				if (NZDIST < 120*s.scale) or (xs.direct > 40) or (p.cmd.forwardmove > 25) and (p.panim <= PA_RUN)
					HALF = true
				end
				if (p.cmd.forwardmove > 0) and (p.cmd.forwardmove+5 >= abs(p.cmd.sidemove))
					RB.PushType = (HALF==true) and 2 or 1
				else
					RB.PushType = (p.panim == PA_IDLE) and 3 or 4
				end
			end
		end
	end
end

--Swaps track.
local function swapSSTtrack(s)
	if XS_TENKAICHIMUSIC != nil
		XS_TENKAICHIMUSIC = ($<4) and min(4,$+1) or 0
	end
end


--Lock On" behaviour for Super Sonic Tenkaichi! FreeFlight table required!
XSon.PVPLockOn = function(p,s,xs,RB,angletype) local t = RB.LockOn
	--Validity checks.
	if not (t and t.valid and t.health and t.player and t.player.xsonictable and t.player.powers[pw_super])
		if RB and (RB.PvPHEALTH!=10000) --That's weird...
			RB.PvPHEALTH = 10000
			RB.PvPtime,RB.LockOn = nil
			s.XS_PVPCD = 50
			XSon.SetMusic(p,s,xs)
			XSon.UpdateSSTWins(p, 0, 0)		
		end
		return false 
	end --Invalid target!
	
	local tp = t.player	local xss = tp.xsonictable local RBB = xss.FreeFlight
	
	--Cancel conditions....
	if not RBB -- and (P_IsObjectOnGround(t) or (tp.pflags & PF_JUMPED|PF_SPINNING)))
	or not RBB.PvPHEALTH
	or ((s.flags2 & MF2_TWOD) or twodlevel or s.currentaxis)
	or (XS_NOPVP==1)
		s.rollangle,t.rollangle = 0,0
		if RB.minPVP
			RB.minPVP = max($-1,0)
		else
			local HEALTH = RB and RB.PvPHEALTH if HEALTH == nil HEALTH = 0 end
			local ENEMYHEALTH = RBB and RBB.PvPHEALTH if ENEMYHEALTH == nil ENEMYHEALTH = 0 end
			if XS_NOPVP==1
				xs.message = {timer = 85, string = "\x82"+"The server\x85 disabled\x82 Super Sonic Tenkaichi."}	
				s.XS_NoFlight = 11
				XSon.CancelFlight(p,s,xs, true)
				s.flags = $ & ~MF_NOGRAVITY	
				XSon.UpdateSSTWins(p, 0, 0, "RESET")
				if xss
					t.XS_NoFlight = 11
					XSon.CancelFlight(tp,t,xss, true)
					t.flags = $ & ~MF_NOGRAVITY				
					xss.message = {timer = 85, string = "\x82"+"The server\x85 disabled\x82 Super Sonic Tenkaichi."}		
				end
			elseif s.XS_PvPgaveup --tp.powers[pw_super] and (not (ENEMYHEALTH <= 0) or 	
				xs.message = {timer = 85, string = "\x82"+tp.name+"\x85 exited FreeFlight, forfeiting the battle."}
				S_StartSound(nil, sfx_ngskid, p)
				if xss
					XSon.UpdateSSTWins(tp, -1, 1)
					xss.message = {timer = 85, string = "You've exited FreeFlight, forfeiting the battle."}
					S_StartSound(nil, sfx_ngskid, tp) 
				end
				if XS_SSTCHAD[97] and (XS_SSTCHAD[97]==tp)
					XS_SSTCHAD[97] = p --You're not worthy of these.
				end				
			else
				if tp and not tp.powers[pw_super]
					xs.message = {timer = 105, string = "\x82"+tp.name+" lost their\x82 superform!\x83 You win by default!"}
					P_GivePlayerRings(p, 10)
					if xss
						P_GivePlayerRings(tp, -10)
						xss.message = {timer = 105, string = "\x85"+"You lost your superform!\x82"+p.name+" wins by default!"}
					end
					
				else
					P_GivePlayerRings(p,10)				
					xs.message = {timer = 105, string = "\x83"+"You've defeated\x82 "+((tp) and tp.name or "your foe")+"\x83"+"!"}
					if tp
						XSon.UpdateSSTWins(tp, 0, 1)
						
						s.XS_banter	= {timer = 28, SFX = (leveltime%3==0) and sfx_xsvopz or XSLocalRNG(sfx_xsvopt,sfx_xsvopv,s), 
						targetplayer = tp, Rvolume = 160, TPvolume = 150}
						
						t.XS_banter	= {timer = 72, SFX = (leveltime%2==0) and sfx_xsvopx or XSLocalRNG(sfx_xsvopw,sfx_xsvopy,t), 
						targetplayer = p, TPvolume = 60}
						
						P_GivePlayerRings(tp, -10)						
					end
					if xss						
						P_GivePlayerRings(tp, -10)
						xss.message = {timer = 105, string = "\x82"+p.name+" won the battle!"}
					end
				end
				XSon.UpdateSSTWins(p, 1, 0)
				S_StartSound(nil, sfx_nxbump, tp) S_StartSound(nil, sfx_nxbump, p)
				S_StartSound(s, sfx_nxdone) S_StartSound(nil, sfx_nghurt, tp)
				t.XS_NoFlight = 11
				if xss
					XSSnapDiveReset(tp, t, xss, true)
					xss.ringburstcharge,xss.snapdive,xss.snapdivetrick = 0,0,0
				end
				if XS_SSTCHAD[97] and (XS_SSTCHAD[97]==tp)
					XS_SSTCHAD[97] = p --Steal their emeralds if they have them.
				end				
				if RBB
					XSon.CancelFlight(tp,t,xss, true)
					t.flags = $ & ~MF_NOGRAVITY
				end
			end
			swapSSTtrack(s) --Cycle through the music tracks.
			t.XS_PVPCD,s.XS_PVPCD = 50,50 --Give it a second before allowing PvP again.
			s.spritexoffset,s.spriteyoffset = 0,0
			t.spritexoffset,t.spriteyoffset = 0,0
			if RB --Restore health.
				RB.PvPHEALTH = 10000
				RB.PvPtime,RB.LockOn = nil
			end
			if RBB
				RBB.PvPHEALTH = 10000
				RBB.LockOn,RBB.PvPtime = nil
			end

			t.XS_PvPgaveup,s.XS_PvPgaveup = nil
			XSon.SetMusic(p,s,xs) XSon.SetMusic(tp,t,xss)			
		end
		return false --
	elseif ((RB.PvPtime or 0) > 18) --Failsafe incase enemy's not locked onto you, but YOU're locked onto them.
		if RB and (RB.LockOn==t) and not (RBB and RBB.LockOn==s) 
		and not (XS_NOPVP == 2)
			if ((RB.minPVP or 0) < 2)
				if not xs.message
					local HEALTH = RB and RB.PvPHEALTH if HEALTH == nil HEALTH = 0 end
					local HEALTH2 = RBB and RBB.PvPHEALTH if HEALTH2 == nil HEALTH2 = 0 end
					if (HEALTH < HEALTH2) or not RBB
						xs.message = {timer = 105, string = "\x82"+tp.name+" won the battle!"}
						s.XS_NoFlight = 11
						XSon.CancelFlight(p,s,xs, true)
						XSon.UpdateSSTWins(p, 0, 1) XSon.UpdateSSTWins(tp, 1, 0)
						if tp and XS_SSTCHAD[97]==p
							XS_SSTCHAD[97] = tp
						end
					else
						xs.message = {timer = 105, string = "\x83"+"You've defeated\x82 "+tp.name+"\x83"+"!"}	
						XSon.UpdateSSTWins(p, 1, 0)
						if tp
							XSon.UpdateSSTWins(tp, 0, 1)				
							if XS_SSTCHAD[97]==tp
								XS_SSTCHAD[97] = p
							end	
						end
					end
				end
				s.XS_PVPCD = 50 s.XS_PvPgaveup = nil XSon.SetMusic(p,s,xs)
			end
			return false --
		end
	end 
	local DIST = FixedHypot(s.x-t.x, s.y-t.y)
	if (DIST > 24000*s.scale)
		S_StartSound(nil, sfx_ngskid, tp) S_StartSound(nil, sfx_ngskid, p)
		xs.message = {timer = 85, string = "\x85"+"Opponent too far away to continue..."}
		if xss
			xss.message = {timer = 85, string = "\x85"+"Opponent too far away to continue..."}
		end
		XSon.UpdateSSTWins(p, 0, 0)
		swapSSTtrack(s) return false --
	end 

	--General Lock-On Behaviour.
	p.pflags = $ & ~PF_CANCARRY
	p.powers[pw_ignorelatch] = 32770
	if xs.zipdash
		if (p.cmd.forwardmove > 30)
			if xs.zipdashminus xs.zipdashminus = max($-3,0) end
		elseif (xs.zipdash > 11)
			xs.zipdashminus = min(60, $+2) --Discourage fleeing.
			if (p.cmd.forwardmove < -5)
				s.momx = FixedDiv(FixedMul($,8),9)
				s.momy = FixedDiv(FixedMul($,8),9)
			end
		end
	end
	
	--Preserve LockOn for now, but don't do any more angle behaviour!
	if angletype == "noangle" or p.xswallrun or (p.awayviewtics > 2) or (s.XSassault)  return end 
	
	local TANGLE = R_PointToAngle2(s.x, s.y, t.x+t.momx, t.y+t.momy)
	if not RB.delaycam
		if angletype == "drawonly" --Don't face opponent or change camera.
			p.drawangle = TANGLE --Only change the player angle to face the enemy, not the camera/input angle.
		else
			s.angle = TANGLE --Point camera towards opponent.	
		end
	end
	if not (p.cmd.buttons & (BT_JUMP|BT_SPIN)) --Vertical auto adjustment?
	and (DIST < 12250*s.scale)
	and (abs(s.momz) < 30*s.scale)
	and not (RB.painstun and (RB.painstun > 2))
	and RB.haymaker==nil
	and not RB.RingBurstChase
	and not RB.rocket 
	and xs.ChaosLambent==nil
	and not xs.zipdash 
	and not xs.snapdivetrick
		if (p.cmd.forwardmove > 3+abs(p.cmd.sidemove))
		and (p.panim == PA_WALK or p.panim == PA_RUN or p.panim == PA_IDLE)
		and (p.speed > 12*s.scale)
			local ZAD = (DIST < 360*s.scale) and s.scale*5
			or (DIST < 850*s.scale+(p.speed*2)) and s.scale*3+(s.scale/6)
			or (DIST < 2000*s.scale+(p.speed*5)) and s.scale*2+(s.scale/3)
			or s.scale*3/2
			if ((XSon.PvPZAdjust(p,s,xs,RB,t, 50*s.scale, ZAD, 1))==true) --Auto adjust height?
				if (abs(s.z-t.z) <= 50*s.scale)
					if (
					(t.z > s.z) and (t.z < s.ceilingz)
					or (t.z < s.z) and (t.z >= s.floorz)
					)
						s.z = t.z
						s.momz = 0
					else
						s.momz = $/3
					end
				end
			end
		elseif (p.panim==PA_IDLE) and (DIST < 1500*s.scale)
		and (abs(s.z-t.z) > (DIST+128<<16)) and not p.cmd.forwardmove
			XSon.PvPZAdjust(p,s,xs,RB,t, 99*s.scale, s.scale*3, 1) --Auto adjust height?
		end
	end
	--Had to do it this convuluted way to make desyncs fuck off.
	if RB.PushType 
		if not xs.ChaosLambent and not xs.ChaosSnap and not RB.painstun
		and not RB.RingBurstChase
		and not xs.snapdivetrick and not (RB.countered and (RB.countered > 9))
		and not xs.snapdivekick
		and not ((s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK4))
			if (RB.PushType <= 2)
				P_Thrust(s, TANGLE , -5*s.scale*RB.PushType)
				P_TryMove(s, s.x+FixedMul(8*s.scale, cos(TANGLE)), s.y+FixedMul(8*s.scale, sin(TANGLE)), true)
			elseif (RB.PushType == 3)
				P_TryMove(s, s.x+FixedMul(-10*s.scale, cos(TANGLE)), s.y+FixedMul(-10*s.scale, sin(TANGLE)), true)
			else
				P_Thrust(s, TANGLE, -5*s.scale)
				P_TryMove(s, s.x+FixedMul(-5*s.scale, cos(TANGLE)), s.y+FixedMul(-5*s.scale, sin(TANGLE)), true)
			end
			TANGLE = R_PointToAngle2(s.x, s.y, t.x+t.momx, t.y+t.momy)
		end
		RB.PushType  = nil
	end
	local KICKCAM = 0
	if xss.snapdivekick or xss.snapdivetrick and (xss.snapdivetrick < 11) and not (RB.countercam and (RB.countercam>2))
		local TD = 0 if s.TenkaichiCam and s.TenkaichiCam.valid TD = s.TenkaichiCam.TenkaichiD or 0 end
		if (TD >= 0) TD = max($, 1) else TD = min($,-1) end	
		KICKCAM = (TD>0) and ANG20*2 or -ANG20*2 --They kicking us? Tilt camera to show them better.
	end
	
	--Keep camera vertically anchored to opponent.
	if (KICKCAM==0) and not (xs.stopstick and xs.zipdash) 
		local ZDIST = (s.z+s.momz)-(t.z+t.momz)
		local ZAIM = (abs(ZDIST)>10<<16) and FixedInt(ZDIST) or 0 
		if ZAIM and (KICKCAM==0) and not (xs.stopstick and xs.zipdash)
			local CDIST = (DIST>100<<16) and FixedInt(DIST) or 100
			ZAIM = $*17*(ANG1/CDIST)
			if ZAIM
				local NANGLE = FixedInt(AngleFixed(ZAIM)) --Gonna try and make this shit actually readable...
				if (t.z >= s.z)	
					if (NANGLE < 292)
						ZAIM = 0
					else
						ZAIM = min($,0)
					end
				else
					if (NANGLE > 66)
						ZAIM = 0
					else
						ZAIM = max($,0)
					end
				end
				p.aiming = -ZAIM
				if (p.aiming < -ANG2*16) --Feels very jarring to go past these limits.
					p.aiming = -ANG2*16
				elseif (p.aiming > ANG2*16)
					p.aiming = ANG2*16
				end	
				p.awayviewaiming = p.aiming
				RB.modifiedaim = true
			end
		end
	end	
	
	if RB.MeteorSmash
		P_SetObjectMomZ(s, 2<<16)
		if s.state == S_PLAY_XKICK1 or s.state == S_PLAY_XKICK2 --Upside down kick prep.
			if (p.aiming < ANG1*5) p.aiming = ANG1*5 end
			s.frame = $|FF_VERTICALFLIP
			s.rollangle = 0
			if s.state == S_PLAY_XKICK1 and (s.tics > 2)
				s.spriteyoffset = -(s.tics*3)<<16
				if not s.XSstretch
					s.spriteyscale = FRACUNIT+6100
					s.XSstretch = {timer = 2}
				end
				s.XS_DidBlur = nil
				XSon.SuperBlur(p, s, xs, SKINCOLOR_SKY)				
				if (RB.MeteorSmash > 24) and (RB.MeteorSmash < 35)
					XSon.zwind(s, 2)
					if (RB.MeteorSmash < 27)
						RB.windlines = 2
					end
				end
			end			
		else
			if (RB.MeteorSmash < 24) and not xs.zipdash
				P_InstaThrust(s, TANGLE, -36*s.scale)
				if (p.aiming > -ANG2*7) p.aiming = -ANG2*7 end
				if (RB.MeteorSmash==20)	
					
				end
			end
			s.frame = $ & ~FF_VERTICALFLIP
			s.rollangle = 0
			if s.state == S_PLAY_XKICK3
				s.state = S_PLAY_XDIVEFLIP2
				xs.frontflip = 13
			end
		end
		p.awayviewaiming = p.aiming
		RB.modifiedaim = true
	end
	
	--Now do a special sideways-Tenkaichi-style camera!	
	local MAXCAM = 54
	local BUFFERMAX = MAXCAM+23	
	local BEAM = (xs.ChaosLambent) and ((xs.ChaosLambent.init) or 1) or 0 
	
	--Special Camera Setup.
	local ghs = s.TenkaichiCam
	if not (ghs and ghs.valid)
		ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
		ghs.flags = MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOSECTOR
		ghs.flags2 = MF2_DONTDRAW
		ghs.camdist = 53
		ghs.angle = s.angle
		ghs.TenkaichiD = (p.cmd.sidemove<0) and -BUFFERMAX or BUFFERMAX
		ghs.lastZAIM = -p.aiming
		s.TenkaichiCam = ghs
	else
		ghs.scale,ghs.radius,ghs.height = s.scale,24*s.scale,18*s.scale --Adjust size!
		P_MoveOrigin(ghs, s.x-s.momx, s.y-s.momy, (s.z-s.momz)+s.height)
		if ghs.TenkaichiD == nil
			ghs.TenkaichiD = ghs.lastTenkaichiD or 0
		end
		ghs.lastTenkaichiD = ghs.TenkaichiD --Last camera shift position?
		ghs.lastZAIM = -p.aiming
		ghs.eflags = s.eflags
		if (s.eflags & MFE_VERTICALFLIP) --We're using MF_NOCLIPHEIGHT, so manually adjust z position!
			if (abs((s.z+s.momz)-s.floorz) <= (s.height*6/5))
				ghs.z = s.z+(s.height/5)
			end
		elseif (abs((s.z+s.momz)-s.ceilingz) < (s.height*6/5))
			ghs.z = s.z+(s.height/5)
		end
		if RB.countercam
			if (RB.countercam < 5)
				ghs.angle =  TANGLE
			end
			if RB.counters and RB.counters.num and (RB.counters.num > 2)
				local VANG = ((RB.counters.num%2)) and -(ANG1*5/2) or (ANG1*5/2)
				p.viewrollangle = RB.countercam*VANG
			end
		end
	end
	p.awayviewmobj = ghs --Use this as the camera.
	p.awayviewtics = 2
	ghs.fuse,ghs.tics = 2,2 --Keep camera alive.
	
	--Set camera's proper position behind us.
	local ANGCAM = s.angle+KICKCAM
	if xs.ChaosLambent and xs.ChaosLambent.struggle or RB.countercam
		ghs.camdist = min(230, $+20)
		if ghs.TenkaichiD and (ghs.TenkaichiD<0) --Different angle!
			ANGCAM = $+(ANG1*72)
		else
			ANGCAM = $-(ANG1*72)
		end	
	elseif RB.explosion and (RB.explosion > 11)
		ghs.camdist = min(215, max(99, $+11))
	elseif RB.RingBurstChase and (RB.RingBurstChase > 16)
		local SIDE = (RB.RBside) and RB.RBside or p.cmd.sidemove
		if (abs(SIDE)>16)
			ANGCAM = $+ ((ANG10/13)* SIDE)
		end
		ghs.camdist = min(245, $+max(10, abs(SIDE)))
	elseif RB.countered and (RB.countered > 9) 
	and ( (xss.snapdivetrick or xss.snapdive or xss.snapdivekick) or (xs.snapdivetrick or xs.snapdive or xs.snapdivekick) )
		ghs.camdist = min(260, $+24)
		p.awayviewaiming,p.aiming = 0,0
	elseif (KICKCAM != 0) and not xs.snapdivekick and not (xs.snapdivetrick and (xs.snapdivetrick < 10))
		ghs.camdist = min(99, max(69, $+3))
		p.awayviewaiming,p.aiming = 0,0
	elseif RB.painstun and (RB.painstun > 4)
	or xs.ChaosLambent and xs.ChaosLambent.ChaosControl and xs.ChaosLambent.firing
		ghs.camdist = min(132, max(91, $+5))
	elseif ( (p.cmd.forwardmove<-9) or (RB.haymaker and (RB.forceanim=="ringburst" or RB.WasRingBursting)) )
	and not (ghs.camdist > 99) and not BEAM 
		local SET = max(1, abs(p.cmd.forwardmove/10))
		ghs.camdist = min(99, $+min(3, SET))
	elseif ghs.camdist != 53
		local MIN = (ghs.camdist>99) and 9 or (ghs.camdist>92) and 5 or (ghs.camdist>81) and 3 or 2
		ghs.camdist = max($-MIN,53)
	end
	local CAMDIST = ( ghs.camdist + ((BEAM) and BEAM/2 or 0) )*s.scale
	if not (RB.countercam and (RB.countercam > 3))
	and not P_TryMove(ghs, ghs.x-FixedMul(CAMDIST*3, cos(ANGCAM)),ghs.y-FixedMul(CAMDIST*3, sin(ANGCAM)), true)
		local DIV = 9
		for i = 1,27 --Set basic camera position! Try a big jump first, and if it didn't work, split the progress into segments.
			if not P_TryMove(ghs, ghs.x-FixedMul(CAMDIST/DIV, cos(ANGCAM)),ghs.y-FixedMul(CAMDIST/DIV, sin(ANGCAM)), true)
				DIV = $+5 if (DIV > 28) break end
			end
		end
	end
	

	--Camera is properly behind us! Now properly set up the sideways position.
	if BEAM
		if xs.ChaosLambent and xs.ChaosLambent.autoadjust!=nil
			if xs.ChaosLambent.firing
				p.drawangle = xs.ChaosLambent.autoadjust 
				s.angle = xs.ChaosLambent.autoadjust
			end
			xs.ChaosLambent.autoadjust = nil
		end	
		if (BEAM < 16)	
			if (RB.AutoAim > 7) 
			or xs.ChaosLambent and (xs.ChaosLambent.struggle or xs.ChaosLambent.struggledone==true)
			or (p.cmd.forwardmove < -32)
				p.drawangle = TANGLE --Autoaim at enemy if you're holding no direction.
				s.angle = TANGLE
			end
			local camD = ghs.TenkaichiD or 0
			if (abs(p.cmd.sidemove) > 20) and not (p.cmd.forwardmove < -40)
			
				local lastD = ghs.lastTenkaichiD or 0	
				local SIDE = min(7, abs(p.cmd.sidemove/6))
				
				if (p.cmd.sidemove < 0)
					ghs.TenkaichiD = min(camD-SIDE, 25)
				else
					ghs.TenkaichiD = max(camD+SIDE, -25)
				end		
			elseif (BEAM < 5)
				ghs.TenkaichiD = (camD < -10) and -BUFFERMAX or BUFFERMAX
			end
		end
	elseif KICKCAM==0
		local camD = ghs.TenkaichiD or 0
		local lastD = ghs.lastTenkaichiD or 0
		if (abs(p.cmd.sidemove) > 7) --Scroll camera according to your movement.
			local ADD = min(3, abs(p.cmd.sidemove/6))
			if (p.cmd.sidemove < 0)
				ghs.TenkaichiD = camD+ADD
			else
				ghs.TenkaichiD = camD-ADD
			end		
		else
			local CAMTURNSPEED = (abs(camD) < 16) and 4 or 2
			local ANGDIFF,PLUS = XS_AngleDifference(R_PointToAngle2(s.x, s.y, t.x, t.y), R_PointToAngle2(s.x+s.momx, s.y+s.momy, t.x+(t.momx*2), t.y+(t.momy*2)), 2)
			if (ANGDIFF > (ANG1/3)) --Follow opponent's movement!		
				ghs.TenkaichiD = camD+(CAMTURNSPEED*PLUS)				
			elseif (lastD != camD) --Shift camera if we're stuck behind the player
				if (lastD < camD)
					ghs.TenkaichiD = max(camD+1, 16)			
				else
					ghs.TenkaichiD = min(camD-1, -16)			
				end		
			end
		end
	end
	ghs.TenkaichiD = min(max(ghs.TenkaichiD or 0, -BUFFERMAX), BUFFERMAX) --set min and max caps
	
	if KICKCAM == 0
		if RB.RingBurstChase and (RB.RBside) --(abs(p.cmd.sidemove) > 6) or 
			ANGCAM = s.angle-((ghs.TenkaichiD > 0) and ANG60 or ANG60*5)
		else
			ANGCAM = s.angle+((ghs.TenkaichiD > 0) and ANG60 or ANG60*5)
		end
	else
		ANGCAM = s.angle+KICKCAM
	end
	local CAMDIST = min(MAXCAM, abs(ghs.TenkaichiD))*s.scale --New camdist value.
	if not (RB.countercam and (RB.countercam > 3))
	and not P_TryMove(ghs, ghs.x-FixedMul(CAMDIST*3, cos(ANGCAM)), ghs.y-FixedMul(CAMDIST*3, sin(ANGCAM)), true) 
		local DIV = 5
		for i = 1,15  --Couldn't do it in 1 go, so performance to the wind!
			if not P_TryMove(ghs, 
				ghs.x-FixedMul(CAMDIST/DIV, cos(ANGCAM)),
				ghs.y-FixedMul(CAMDIST/DIV, sin(ANGCAM)), true
				) 
				DIV = $+4 if (DIV > 28) break end --
			end
		end
	end	
	if not xs.snapdivekick  and not (xs.snapdivetrick and (xs.snapdivetrick < 10)) and not RB.countercam
	or ghs.LastX == nil
		--Where should the camera be pointing?
		if BEAM --Cinematic camera shift!
			local ANG = (BEAM > 38) and max(1, (19-(BEAM-39))*ANG1) or min(ANG1*19, BEAM*ANG1)
			ghs.angle = s.angle+((ghs.TenkaichiD > 0) and -ANG or ANG)
		elseif not RB.delaycam
			ghs.angle = s.angle+KICKCAM
		end	
		if (DIST > 70*s.scale) or ghs.LastX == nil --Save cam coordinates.
			local GHSZ = ghs.z
			if RB.counters and RB.counters.num and (RB.counters.num>3)
				if (abs(ghs.z-s.z) > 350*s.scale)
					ghs.z = (s.z > ghs.z) and $+250*s.scale or $-200*s.scale
				end
				GHSZ = ghs.z+(s.height*4/5)
			end
			ghs.LastX,ghs.LastY,ghs.LastZ,ghs.LastANG = ghs.x, ghs.y, GHSZ, ghs.angle
			if (s.state==S_PLAY_XKICK1) or (s.state==S_PLAY_XKICK2)
				if (abs(s.z-ghs.LastZ) > 245*s.scale)
				and not RB.MeteorSmash
				and not (RB.counters and ((RB.counters.num or 0)>2) ) and not RB.countercam
					if (s.z > ghs.LastZ)
						ghs.LastZ = $+50*s.scale
					else
						ghs.LastZ = $-50*s.scale
					end
				end
			end			
		end
	end
	
	if xs.snapdivekick or xs.snapdivetrick and (xs.snapdivetrick < 10)
		if (DIST < 180*s.scale) and ghs.LastANG!=nil
		and not RB.RingBurstChase and not RB.haymaker and not RB.delaycam and not RB.countercam
			if (abs(s.z-ghs.LastZ) > 245*s.scale)
			and not RB.MeteorSmash
			and not (RB.counters and ((RB.counters.num or 0)>2) ) 
				if (s.z > ghs.LastZ)
					ghs.LastZ = $+50*s.scale
				else
					ghs.LastZ = $-50*s.scale
				end
			end
			P_MoveOrigin(ghs, ghs.LastX, ghs.LastY, ghs.LastZ)
			ghs.angle = ghs.LastANG
		end
	elseif xs.zipdash and (xs.zipdash > 4) and not xs.zipdashslide
	and (RB.RingBurstChase or not (p.cmd.forwardmove < -40)) --Zipdash tackle range
	and not RB.haymaker and not (RB.painstun and (RB.painstun>2))
	and (p.speed > 24<<16) --and not xs.ChaosSnap 
	and not RBB.RingBurstChase
		local TADD = min(250*s.scale, tp.speed+abs(t.momz/2)) --Adjust depending on tackle.
		local TFORWARD = tp.cmd.forwardmove or 0
		local TSIDE = abs(tp.cmd.sidemove) or 0
		if (TFORWARD < 0) --Make it easier to tackle fleeing jerks.
			TADD = FixedDiv(FixedMul($, 100-tp.cmd.forwardmove),100)
		end
		if (TSIDE > 5) --Make it easier to tackle strafers
			TADD = FixedDiv(FixedMul($, 100+abs(TSIDE/4)),100)
		end
		if xss.ChaosLambent --No you don't!!
			TADD = $+(83*s.scale)
			if (xss.ChaosLambent.ChaosControl==true)
				return --Nevermind, yes you do, apparently.
			end
		end
		local ZTADD = TADD		
		if (p.cmd.forwardmove > 16) --Yeah, get them!
			TADD = $+(p.cmd.forwardmove*(s.scale/2))
		end
		if RB.SRAlunge and (RB.SRAlunge < 80)
			TADD = $+38*s.scale
		end
		if (p.cmd.buttons & (BT_JUMP|BT_SPIN))
			if (s.z > t.z+48<<16)
				if (p.cmd.buttons & BT_SPIN)
					ZTADD = $+36*s.scale
				else
					ZTADD = $-36*s.scale
				end
			elseif (s.z < t.z-48<<16)
				if (p.cmd.buttons & BT_JUMP)
					ZTADD = $+36*s.scale
				else
					ZTADD = $-36*s.scale
				end	
			end
		elseif (xs.zipdash > 16) --More range once you were dashing for a bit.
			TADD = $+(8*s.scale)
			ZTADD = $+(24*s.scale)
		end
		if (DIST < 275*s.scale+TADD)
		and (abs((s.z+s.momz)-(t.z+t.momz)) < (320*s.scale)+ZTADD)
			XSon.ZipdashTackle(p,s,xs,RB, t)
		end
	end
end

--Subtract PvP health in the Super Sonic Tenkaichi matches!
XSon.SubtractPvPHealth = function(NUM, RB, RBB)
	local MIN = 0
	if not RBB and (RB.LockOn and RB.LockOn.valid and RB.LockOn.player and RB.LockOn.player.xsonictable)
		RBB = RB.LockOn.player.xsonictable.FreeFlight
	end
	if RBB and (RBB.PvPHEALTH!=nil) and (RBB.PvPHEALTH > RB.PvPHEALTH) and (abs(RB.PvPHEALTH-RBB.PvPHEALTH) > 3350)
		NUM = (abs(RB.PvPHEALTH-RBB.PvPHEALTH) > 4150) and ($*10)/13 or ($*5)/6
	end
	if (RB.PvPHEALTH > (NUM*2/3)) and (RB.PvPHEALTH > 19)
		MIN = 1
		if (NUM > RB.PvPHEALTH) --You would've died from this! Get one last push to win this!
			RB.NoSlamDamage = 70
			RB.noXYcontrol,RB.noZcontrol,RB.SnapOverHeat = 0,0,0
		end
	end	
	RB.PvPHEALTH = max($-NUM, MIN)
end	
		
--The target got hit and cancels what they were doing! (Does not send them flying)
XSon.GetHit = function(tp,t,xss, RBB)
	local SFX = (xss.FourKidsRB) and sfx_xssfpz or sfx_xssfxx
	S_StopSoundByID(t, SFX)
	xss.walljump,xss.walljumpP,xss.forceangle,t.XSautododge = nil
	XSSnapDiveReset(tp, t, xss)
	xss.snapdive,xss.snapdivetrick,xss.snapdivekick = 0,0,0
	if xss.zipdash or xss.zipdashslide
		XSZipDashCancel(tp, t, xss)
	end
	
	S_StopSound(t)	
	local ghs = P_SpawnMobjFromMobj(t,0,0,0, MT_XSTRACKER2)
	if ghs and ghs.valid
		ghs.target,ghs.SnapDiveStretch = t,18
	end
	XSon.SwapStretch(tp, t, xss)
	t.spritexscale,t.spriteyscale = FRACUNIT+20000,FRACUNIT+20000
	t.XSstretch = {timer = 4, Xspeed = 5000, Yspeed = 5000}	
	
	xss.jump,xss.spin,xss.custom3 = 99<<16, 99<<16, 99<<16
	
	tp.pflags = $|PF_JUMPDOWN & ~(PF_THOKKED|PF_JUMPED|PF_SPINNING|PF_CANCARRY)
	RBB.riseanimtimer,RBB.frame = 0,A
	RBB.explosion,RBB.forceanim,RBB.WasRingBursting = nil
	RBB.jump,RBB.spin,RBB.custom2,RBB.custom3 = 99<<16, 99<<16, 99<<16, 99<<16
	xss.ZoomLines = 15
	tp.xswallrun,RBB.forceanim,xss.ChaosLambent,xss.snaptarget = nil
	RBB.painstun = FRACUNIT
end

--Homing Attack followup after a tackle!
XSon.RingBurstChase = function(p,s,xs, RB)

	RB.RingBurstChase = max($-1,0)
	RB.haymaker = nil
	RB.WasRingBursting = 22
	
	local t = RB.LockOn local RBB = t and t.player and t.player.xsonictable and t.player.xsonictable.FreeFlight
	if not RBB or not RB.RingBurstChase
		s.state = S_PLAY_XDIVEFLIP1 --Cancel and animation!
		xs.frontflip = 13
		XSon.Sound(s, sfx_xssfpy, 200)
		RB.CLresist,RB.RBmissed,RB.RingBurstChase,RB.RBdiv,RB.RBside = nil
		local SFX = (xs.FourKidsRB) and sfx_xssfpz or sfx_xssfxx
		S_StopSoundByID(s, SFX)	return --
	elseif xs.zipdash and (xs.zipdash > 8) 
	or RB.painstun and (RB.painstun>1)
	or xs.ChaosLambent --Cancel without animation change.
	or xs.ChaosSnap and xs.ChaosSnap.init==1 and (RB.RingBurstChase < 44) --Sure, let them combo this with Snap at the start!
		XSon.SquashFlash(s)
		XSon.Sound(s, sfx_xssfpr, 150)
		RB.CLresist,RB.RBmissed,RB.RingBurstChase,RB.RBdiv,RB.RBside = nil 
		local SFX = (xs.FourKidsRB) and sfx_xssfpz or sfx_xssfxx
		S_StopSoundByID(s, SFX) return --
	else
		local TOFOE = R_PointToAngle2(s.x, s.y, t.x, t.y)
		xs.zipdashangle = TOFOE
		if not ((s.state >= S_PLAY_XDIVEFLIP1) and (s.state <= S_PLAY_XDIVEFLIP4)) or (RB.RingBurstChase < 42)
			s.state = S_PLAY_ROLL
			s.flags2 = $|MF2_DONTDRAW
			XSRingBurstBall(p, s, xs)
			XSon.RingBurstAfterImages(p,s,xs)
			if (p.speed > 99*s.scale)
				XSZipTrail(s, nil, 1)	
				XS_RingBurstStripes(s, nil, AST_ADD, 11<<16, nil, TOFOE)
			end
		else
			XS_RingBurstSparkles(s, 50, SKINCOLOR_SUPERSKY5)
		end
		XS_RingBurstSparkles(s, 16, nil, 240)
		
		if RB.RBmissed
			if RBB
				RBB.blackbars = nil
			end	
			RB.RingBurstChase = min($,14)
			P_InstaThrust(s, RB.RBmissed, max(70*s.scale, p.speed+28*s.scale))
		else
			local DIST = FixedHypot(FixedHypot(s.x-t.x, s.y-t.y), s.z-t.z)
			if (DIST < (250<<16)+p.speed) and not (RB.RingBurstChase > 42) --Yo, not THAT soon!
				if t.player.xsonictable
					XSZipDashCancel(t.player, t, t.player.xsonictable)			
				end
				XSon.ZipdashTackle(p,s,xs,RB, t, (RB.didfollowup) and 2 or 1)
				if RB.didfollowup and (RB.didfollowup!=64)
					RB.didfollowup = nil
				else
					RB.didfollowup = 64
				end
				if RBB
					RBB.blackbars,RBB.blackbarsshow = nil
				end				
				RB.CLresist,RB.RBmissed,RB.RingBurstChase,RB.RBdiv,RB.RBside = nil
			else	
				if RBB
					RBB.blackbars = 4
					if xss and xss.ChaosSnap and (xss.ChaosSnap.init==2)
						RBB.SnapOverHeat = max( (RBB.SnapOverHeat or 0)+23, 25)
					end
				end
				local ZFLIP = P_MobjFlip(s)
				if (leveltime%2==0)
					local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
					if (abs((s.z+s.momz)-FLOORZ) < 300*s.scale) 
					or ((p.blocked) and (RB.RBside))
						XS_RingBurstDust(p, s, xs, 500*s.scale)	
						if (leveltime%4==0)	
							P_RadiusAttack(s, s, 650<<16, DMG_NUKE, true)
							XSon.Quake(s, max(116<<16, min(50<<16, p.speed)), 4, 1800<<16)
							local SfX = P_SpawnMobjFromMobj(s, s.momx,s.momy,0, MT_THOK)
							SfX.flags2 = $|MF2_DONTDRAW
							SfX.tics = 133
							local SOUND = sfx_rocks2+XSLocalRNG(-1,2,s)
							S_StartSound(SfX, SOUND)
							S_StartSound(t, SOUND)
						end
						local ghs = XS_CraterSpew(s, s.angle+ANGLE_180, 12)	
						if ghs and ghs.valid
							ghs.tics,ghs.fuse = 38,34
						end
					end
				end
				local DIV = RB.RBdiv or 1
				if (RB.RingBurstChase < 9)
					if DIV==4 DIV = 1 end
					P_SetObjectMomZ(s, (6<<16*RB.RingBurstChase))
					s.momx = ((FixedMul(FixedDiv(t.x-s.x, 512<<16), FixedDiv(261<<16, 3<<16/2)))/3)/DIV
					if not (s.flags2 & MF2_TWOD or twodlevel)
						s.momy = ((FixedMul(FixedDiv(t.y-s.y, 512<<16), FixedDiv(261<<16, 3<<16/2)))/3)/DIV
					end
				else
					if DIV==4
						DIV = 1
					elseif DIV==1 and (RB.RingBurstChase > 28)
						DIV = 2
					end
					s.momx = FixedMul(FixedDiv(t.x-s.x, 512<<16), FixedDiv(261<<16, 3<<16/2))/DIV
					if not (s.flags2 & MF2_TWOD or twodlevel)
						s.momy = FixedMul(FixedDiv(t.y-s.y, 512<<16), FixedDiv(261<<16, 3<<16/2))/DIV
					end
					if RB.RBdiv==4 DIV = 4 else DIV = 2 end
					s.momz = FixedMul(FixedDiv(t.z-s.z, 512<<16), FixedDiv(261<<16, 3<<16/DIV))
					if (RB.RingBurstChase > 24) --Smooth in a bit.
						local SDIV = 
						(RB.RingBurstChase>34) and 5 or
						(RB.RingBurstChase>28) and 4 or 
						(RB.RingBurstChase>26) and 3 or 2
						s.momx,s.momy,s.momz = $/SDIV,$/SDIV,$/SDIV
					end
				end
				
				s.target,s.tracer = t,t
				RB.RBdiv = 1
				
				if (RB.RingBurstChase > 16) and not (P_FaceStabCheckMeleeRange(s)) 
					RB.PvPENERGY = max(0, min(10000, $+1))
					if (RB.RingBurstChase > 24) and (RB.RingBurstChase%5==0)
					or (RB.RingBurstChase==48)
						local ghs = XS_RingBurstWave(s)
						if ghs and ghs.valid
							ghs.momx,ghs.momy,ghs.momz = 0,0,0
							if (RB.RingBurstChase < 43)
								ghs.fuse = $*2/3
							end
						end
					end
					if (RB.RingBurstChase > 27)
						local TURNANG = xs.turnang	
						local TSIDE = RB.RBside or 0
						local SIDE = abs(p.cmd.sidemove)
						if (TSIDE)
							if (SIDE < abs(TSIDE)) --Keep this smooth!
							or ((p.cmd.sidemove > 0) and (TSIDE < 0))
							or ((p.cmd.sidemove < 0) and (TSIDE > 0))
								SIDE = abs(TSIDE)
								TURNANG = (p.cmd.angleturn<<16) + (R_PointToAngle2(0, 0, 16<<16, -(TSIDE<<16)))	
							end
						end
						if (SIDE > 6) and TURNANG!=nil --Do some cool swerving!
							local SMOL = (RB.RingBurstChase > 34) and 128<<16 or 111<<16
							P_Thrust(s, TURNANG, -FixedDiv(FixedMul(SMOL, min(50, SIDE*3)), 50) )
							RB.PvPENERGY = max(0, min(10000, $+1))
							if RB.RBside==nil or (SIDE < abs(TSIDE))
								if (SIDE < 10)
									RB.RBside = min(50, max(-50, p.cmd.sidemove*2))
								else
									RB.RBside = p.cmd.sidemove
								end
							end
							RB.RBdiv = (SIDE > 30) and 3 or 2
							if RB.CLresist 
								RB.CLresist = max($-3,0)
							end
						elseif RB.CLresist
							RB.CLresist = max($-1,0)
						end
					elseif RB.CLresist
						RB.CLresist = max($-2,0)
					end
					if not (p.cmd.buttons & (BT_JUMP|BT_SPIN)) --By default, attack from a slight upper arc!
						if (RB.RingBurstChase > 18)
							if RB.didfollowup --Opposite default.
								s.momz = $-54*s.scale*ZFLIP
							else
								s.momz = $+58*s.scale*ZFLIP
							end
							if RB.RBdiv==1 RB.RBdiv = 4 end
						end
					else --Ram them downwards or upwards!
						s.momz = $+( ((p.cmd.buttons & BT_SPIN) and -70*s.scale or 70*s.scale) * ZFLIP )			
						if RB.RBdiv==1 RB.RBdiv = 4 end	
						local UPDOWN = RB.RBupdown or 0
						if (abs(UPDOWN) < 5)
							if (p.cmd.buttons & BT_SPIN)
								RB.RBupdown = min(UPDOWN-1, -abs(UPDOWN))
							elseif (p.cmd.buttons & BT_JUMP) 
								RB.RBupdown = max(UPDOWN+1, abs(UPDOWN))
							end
						end
					end
				elseif RB.CLresist --Can slightly rush through a Chaos Lambence!
					RB.CLresist = max($-2,0)
				end
			end
		end
		return true --
	end
end

--You can followup after a Zipdash Tackle!
XSon.TackleFollowUp = function(p,s,xs,RB, t)

	RB.haymaker.jump = (p.cmd.buttons & BT_JUMP) and $+1 or 0 --Set Controls!
	RB.haymaker.spin = (p.cmd.buttons & BT_SPIN) and $+1 or 0
	RB.haymaker.custom1 = (p.cmd.buttons & BT_CUSTOM1) and $+1 or 0
	RB.haymaker.custom2 = (p.cmd.buttons & BT_CUSTOM2) and $+1 or 0
	RB.haymaker.custom3 = (p.cmd.buttons & BT_CUSTOM3) and $+1 or 0
	RB.haymaker.tossflag = (p.cmd.buttons & BT_TOSSFLAG) and $+1 or 0	
	
	local tp,xss,RBB
	if t and t.valid and t.health
		tp = t.player xss = tp and tp.xsonictable RBB = xss and xss.FreeFlight
	end
	if not RBB or xs.ChaosLambent
		RB.haymaker.timer = max($,99) return false --
	elseif RB.painstun and (RB.painstun>=FRACUNIT) and not RB.RingBurstChase
		return false --
	end

	local DOMOVE = 0
	if (p.cmd.buttons & BT_SPIN) and RB.haymaker.spin==1 and (RB.haymaker.force==2)
	or (p.cmd.buttons & BT_CUSTOM2) and RB.haymaker.custom2==1
	or (p.cmd.buttons & BT_CUSTOM3) and RB.haymaker.custom3==1
	or (p.cmd.buttons & BT_TOSSFLAG) and RB.haymaker.tossflag==1
		DOMOVE = 1
	elseif not (RB.haymaker.force==2) 
		if 	(p.cmd.buttons & BT_SPIN) and RB.haymaker.spin==1
		or (p.cmd.buttons & BT_JUMP) and RB.haymaker.jump==1
		or (p.cmd.buttons & BT_CUSTOM1) and RB.haymaker.custom1==1
			DOMOVE = 2
		end
	end
	if DOMOVE != 0
		RB.custom1 = 0
		p.cmd.buttons = $ & ~(BT_SPIN|BT_WEAPONPREV|BT_WEAPONNEXT)
		if xs.zipdash
			XSZipDashCancel(p, s, xs, true) xs.jump = 1	
		end
		if xss.zipdash
			XSZipDashCancel(tp, t, xss, true)
		end	
		if RBB.painstun
			RBB.painstun = min($,8)
		end
		s.XSautododge,t.XSautododge = nil
		xs.stopstick,xss.stopstick = 0,0
		xs.custom1,RB.custom1 = 999<<16,999<<16
		RB.SnapOverHeat = 0
		
		if DOMOVE == 2
			s.state = S_PLAY_XDIVEFLIP1
			xs.frontflip = 13
			s.anim_duration,s.tics = 1,1
			if RB.WasRingBursting or RB.haymaker and RB.haymaker.force
				XSon.Sound(s, sfx_xssfpb)
				XSon.Sound(t, sfx_xssfpb, 155)
			else
				local ghs = XS_RingBurstShine(p, s, xs)
				if ghs and ghs.valid
					ghs.color,ghs.colorized = SKINCOLOR_SUPERSKY5, true
				end
				S_StopSoundByID(s, sfx_thok) S_StopSoundByID(s, sfx_xssfxz)
				local SFX = (xs.FourKidsRB) and sfx_xssfpz or sfx_xssfxx
				S_StopSoundByID(s, SFX) XSon.Sound(s, SFX)
				S_StopSoundByID(t, SFX) XSon.Sound(t, SFX, 89)
			end
			if RBB  --Give a minor chance to dodge, strong vs spam.
				RBB.SnapOverHeat = min(50, max(RBB.SnapOverHeat or 0, 27))
			end
			RB.RingBurstChase = 50
			RB.forceanim = "ringburst"
			P_Thrust(s, s.angle, 24*s.scale)
			XSon.RingBurstStartGFX(p,s,xs)

		elseif DOMOVE == 1
			if RBB  --Give a decent chance to dodge always!
				RBB.SnapOverHeat = min(30, max(RBB.SnapOverHeat or 0, 8))
			end
			S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx) 
			if (p.cmd.forwardmove < -25)  and not (RB.haymaker.spin==1) --Just move away from the enemy, then.
				XSStartSnapDive(p, s, xs) p.cmd.forwardmove = max($,30)
				if xs.ChaosSnap
					RB.DodgeFrames = max(RB.DodgeFrames or 0,7)
				end
			else
				local XYZ = {x = t.x-(t.momx*5), y = t.y-(t.momy*5), z = t.z-(t.momz*5)} 
				p.cmd.forwardmove = max($,30)
				RB.MeteorSmash = 36
				XSStartSnapDive(p, s, xs) 
				if xs.ChaosSnap --We went into Chaos Snap! Give iframes?
					xs.snaptarget = t
					RB.DodgeFrames = 7
					RB.MeteorSmash = 36
					RB.forcetrail = 2
					RB.BlastCharge.C3 = 99
					RB.BlastCharge.LetGo = 0
					if RBB
						RBB.magicC2 = {timer = RB.MeteorSmash, C2 = 99<<16, RBB = RB}
						xss.stopbutton = 0
						tp.powers[pw_nocontrol] = 0
						RB.countered,RBB.countered = 0,0
						if (tp.cmd.buttons & BT_CUSTOM2)
							RBB.pressedC2 = 0
						end
						RBB.custom2,xss.custom2 = 50,50
					end 
					
					P_MoveOrigin(s, t.x, t.y, t.z)	
				--	XSZipTrail(s, AST_ADD, 1, nil, 17, SKINCOLOR_SKY)
					XSon.SnapDiveProperties(p,s,xs)
					if RBB
						if (tp.cmd.buttons & BT_CUSTOM2)
							RBB.pressedC2 = 0
						end
						RBB.custom2,xss.custom2 = 50,50
						
						XSGhostTrail(p, s, xs, SKINCOLOR_SKY, 8, FF_TRANS20, nil, XYZ, true)							
					end 
					
					S_StopSoundByID(s, sfx_thok) S_StopSoundByID(s, sfx_xssfxz)
					XSon.Sound(s, sfx_xssfe2)
					XSon.Sound(s, sfx_xssfe4)	
				else
					RB.MeteorSmash = 0
				end
			end
		end
		RB.RBupdown,RB.haymaker,RB.painstack,RB.RBmissed,RB.RBdiv,RB.CLresist,RB.RBside = nil
	end
end

--Recoils off the target.
local ZipDashRecoil = function(p,s,xs,RB, MOMANG)
	if xs.zipdash or xs.zipdashslide
		XSZipDashCancel(p, s, xs)
	end xs.zipdash,xs.zipdashslide = 0,0
	if xs.ChaosLambent
		XSon.RemoveLambentHitBoxes(p)
	end	
	if RB.SRAdrain
		RB.PvPENERGY = max(0, min(10000, $+2500))
		RB.SRAdrain = nil
	end
	xs.snaptarget,s.XSautododge = nil
	S_StartSound(s, sfx_xssfpt)

	RB.windlines = 7
	RB.countered = max(RB.countered or 0, 18)
	RB.RBupdown,RB.haymaker,RB.painstack,RB.RBmissed,RB.RBdiv,RB.CLresist,RB.RBside,RB.crossedarms,RB.cancharge,RB.SRAlunge,
	RB.charging,RB.C1stun,RB.C2stun,RB.countered,RB.magicC2,RB.RingBurstChase,RB.quickroll,RB.dontextendagain = nil

	RB.refreshC1 = 18
	RB.DodgeFrames = 0
	RB.noXYcontrol = max(RB.noXYcontrol or 0, 11)
	RB.noZcontrol = 5
	RB.custom2 = 99<<16
	RB.snapCD = max(RB.snapCD or 0, 7)
	s.state = S_PLAY_XDIVEFLIP1
	s.anim_duration,s.tics = $+5,$+5
	xs.frontflip = 0

	local ghs = XSon.ElectricBody(s, 11)
	ghs.frame = $|FF_HORIZONTALFLIP
	
	ghs = XSon.ElectricBody(s, 15, SKINCOLOR_CRIMSON)
	ghs.destscale = 99<<16

	XSon.Quake(s, 104<<16, 10, 2000<<16)
	XSon.Flash(s, PAL_NUKE, 4, 1)
	P_Thrust(s, MOMANG+ANGLE_180, -19*s.scale)
	P_SetObjectMomZ(s, -1<<16)
	if (abs(s.z-s.ceilingz) < 380*s.scale)
		s.momz = $-24<<16			
	end
	if (abs(s.z-s.floorz) < 380*s.scale)
		s.momz = $+18<<16
	end	
	
	local ghs = XS_CircleWave(s, 1, nil, 16, FRACUNIT)
	ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS70	
end

--Returns the X,Y,Z coordinates between "s" and "t"
XSon.MidPoint = function(s, t)
	local XDIFF = s.x-t.x
	local YDIFF = s.y-t.y
	local ZDIFF = s.z-t.z
	return s.x-(XDIFF/2), s.y-(YDIFF/2), s.z-(ZDIFF/2)
end

--Zipdash Tackle, leading into a haymaker
XSon.ZipdashTackle = function(p,s,xs,RB, t, FORCE)
	local tp,xss,RBB
	if t and t.valid
		tp = t.player xss = tp and tp.xsonictable RBB = xss and xss.FreeFlight
	end
	local MOMANG = xs.zipdashangle
	if not xs.zipdash or xs.zipdashangle==nil
		if s.momx or s.momy
			MOMANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
		else
			MOMANG = p.drawangle
		end
	end	
	local CANHURT = true
	if RB.RingBurstChase and (RB.RBmissed!=nil) --They need good timing to dodge this one!	
		return false --
	end
	if not RBB
	or RBB.DodgeFrames or xss.ChaosSnap
	or RBB.painstun and (RBB.painstun > 5) and not (FORCE)
		if not RBB
			return false --Target doesn't exist??? Cancel.
		elseif RB.RingBurstChase 
			local P = ((netgame==true) and (p!=server)) and 2 or 0
			if xss.ChaosSnap and ((xss.ChaosSnap.init or 0) < (7+P)) and RBB.DodgeFrames
				if RB.RBmissed==nil --You missed the target!
					RB.cantsnap,RBB.cantsnap = 18,18
					if RBB.SnapOverHeat and (RBB.SnapOverHeat > 8)
						RBB.SnapOverHeat = max(8, $-22)
					end
					RBB.PvPENERGY = max(0, min(10000, $+350))
					RBB.quickresponse = 50
					RB.RBmissed = MOMANG
					s.momx,s.momy,s.momz = $*2,$*2,$*2
					xs.stopstick,xs.windtrail,xs.Ztrail,xs.ziptrailticket = 4,12,12,11
					RB.refreshC1 = 21
					RB.stripes = 13
					s.state = S_PLAY_XDIVEFLIP1
					xs.frontflip = 13
					S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx) 
					if not S_SoundPlaying(s, sfx_xssfp7)
						S_StartSound(s, sfx_xssfp7)
						S_StartSound(s, sfx_xssfpb)
					end
					if not S_SoundPlaying(t, sfx_xssfp7)
						S_StartSound(t, sfx_xssfp7)
						S_StartSound(s, sfx_xssfpb)
					end	
					return false --miss!
				end
			elseif RB.RBmissed==nil --They need good timing to dodge this one!
				if xss.snapdive or xss.ChaosSnap
					XSSnapDiveReset(tp, t, xss, true)
				end
				RBB.cantsnap,RBB.DodgeFrames = 0,0
			end
		elseif xss.ChaosSnap and RBB.DodgeFrames
			local P = ((netgame==true) and (p!=server)) and 1 or 0
			if ((xss.ChaosSnap.init or 0) < (5+P)) 
				CANHURT = false --They just Chaos Snapped, and will dodge us.
			end
		end
	end
	if RBB.RingBurstChase and (RBB.RBmissed==nil)
		if RB.RingBurstChase and (RB.RBmissed==nil) 
			if (RBB.RingBurstChase > RB.RingBurstChase)
				CANHURT = false --They've been ringbursting first, so they win.
			end
		else
			CANHURT = false --They're ringbursting! We can't beat this
		end
	end	
	
	if not (FORCE) --The Ring Burst always beats a regular zipdash tackle!
	
		--Are we actually heading towards the opponent?
		local TOFOE = R_PointToAngle2(s.x-(s.momx*2), s.y-(s.momy*2), t.x, t.y)
		local ANGLE,PLUS = XS_AngleDifference(MOMANG, TOFOE, 2)
		if (ANGLE > ANG60)
			return false --No, we're not...
		end
		
		if xss.zipdash
			local P = ((netgame==true) and (p!=server)) and 3 or 0
			if not (xss.zipdashslide and (xss.zipdashslide > (1+P))) --We're BOTH trying to tackle? Who will win?
				local TANG = (xss.zipdashangle==nil) and R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy) or xss.zipdashangle
				local SANGLE = XS_AngleDifference(MOMANG, R_PointToAngle2(s.x-s.momx, s.y-s.momy, t.x, t.y), 2)
				local TANGLE = XS_AngleDifference(TANG, R_PointToAngle2(t.x-t.momx, t.y-t.momy, s.x, s.y), 2)
				
				local ANGDIFF = XS_AngleDifference(SANGLE, TANGLE, 2)
				if (ANGDIFF < ANG1*17) --We're both heading directly towards each other! It's clash time!
					RB.refreshC1,RBB.refreshC1 = nil --Set this later if desired.
					ZipDashRecoil(p,s,xs,RB, MOMANG)
					ZipDashRecoil(tp,t,xss,RBB, MOMANG+ANGLE_180) CANHURT = false 
					RB.cantsnap, RBB.cantsnap = 5,5
					RB.custom2, RBB.custom2 = 99<<16, 99<<16
					RB.PvPENERGY = max(0, min(10000, $+350)) --The tension is rising!
					RBB.PvPENERGY = max(0, min(10000, $+350))
					RB.quickresponse,RBB.quickresponse = 28,28
					
					P_InstaThrust(s, MOMANG, -75*s.scale)
					P_InstaThrust(t, MOMANG, 75*s.scale)
					
					XSon.Sound(s, sfx_xssfp2) XSon.Sound(t, sfx_xssfp2)
					local X,Y,Z = XSon.MidPoint(s, t)	
					local ghs = XS_CircleWave(s, 1, SKINCOLOR_KETCHUP) --Impact wave.
					ghs.blendmode = AST_ADD
					ghs.angle = MOMANG+ANGLE_90
					ghs.tracer = nil
					ghs.sprite = SPR_XSG2
					ghs.frame = 31|FF_TRANS50
					P_SetOrigin(ghs, X, Y, Z)
					
					ghs = XS_RingBurstDust(p, s, xs, "manual", s.z) --Crash wave.
					ghs.spritexscale,ghs.spriteyscale = $+1<<16,$+1<<16
					ghs.flags = $|MF_NOGRAVITY|MF_NOCLIPHEIGHT
					ghs.fuse,ghs.tics = $+8,$+8
					P_SetOrigin(ghs, X, Y, Z)
					
					ghs = XSon.SquashFlash(s, SKINCOLOR_RUBY, 19, FF_TRANS10, 4<<16)
					P_SetOrigin(ghs, X, Y, Z)
		
					ghs = XSon.PuffDust(s, nil, nil, nil, 3<<16)
					P_SetOrigin(ghs, X, Y, Z)
					
					ghs = XSon.SmokeCircle(s, 0, 1, nil, 3<<16)
					ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
					P_SetOrigin(ghs, X, Y, Z)
					
					return "clash" --	
				elseif (SANGLE <= TANGLE) --You're doing better than them! You win!
					RBB.DodgeFrames = 0 XSZipDashCancel(tp, t, xss) 
				else--if TANGLE < SANGLE --Your opponent did better!
					RB.DodgeFrames = 0 XSZipDashCancel(p, s, xs)			
					XSon.ZipdashTackle(tp, t,xss,RBB, s) CANHURT = false return --
				end
			end
		end	
	end
	
	if CANHURT==true
		if RBB.explosion --That's some good timing...!
			tp.powers[pw_nocontrol] = 0
			xss.stopstick,xss.stopbutton = 0,0
			RBB.ExplosionCD,RBB.explosion = 0,0
		end	
		RB.cantsnap = max(RB.cantsnap or 0, 2)
		RB.refreshC1,RBB.refreshC1 = nil --Set this later if desired.
		P_MoveOrigin(s, t.x, t.y, t.z)	
		if xss.zipdash or xss.zipdashslide
			XSZipDashCancel(tp, t, xss) xss.zipdash,xss.zipdashslide = 0,0
		end
		tp.drawangle = MOMANG+ANGLE_180
		XSon.GetHit(tp,t,xss, RBB) --Cancel what the other player is doing.
		local P = 0
		if (FORCE)
			P = (FORCE==2) and $+4 or $-18
			if type(RB.RBside)=="number" and (abs(RB.RBside)>22)
				P = $+22
			end
		end
		if not (RB.SRAlunge)
			XSon.SubtractPvPHealth(250+P, RBB, RB)
		end
		local TMOMZ = s.momz
		if (p.cmd.buttons & (BT_SPIN|BT_JUMP))
			TMOMZ = ((p.cmd.buttons & BT_SPIN) and not (p.cmd.buttons & BT_JUMP)) and -28*s.scale
			or ((p.cmd.buttons & BT_JUMP) and not (p.cmd.buttons & BT_SPIN)) and 32*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 -38*s.scale or -24*s.scale
			end
			if (FORCE)
				TMOMZ = $*-3/4
			end
			TMOMZ = $*P_MobjFlip(s)
		end
		t.momz = ($/3)+TMOMZ
		P_InstaThrust(t, MOMANG, 110*s.scale-min(40*s.scale, abs(t.momz/2)) )
	end

	--Now to manipulate us instead.	
	XSon.SwapStretch(p, s, xs)	
	s.spritexscale,t.spriteyscale = FRACUNIT-18000,FRACUNIT+23000
	s.spriteyoffset = 5<<16
	s.XSstretch = {timer = 5, Xspeed = 3200, Yspeed = 4800}	
	
	s.XSkickextend,xs.strongspring,s.XSautododge,t.XSautododge = nil	
	p.viewrollangle,s.rollangle,xs.sprung = 0,0,0
	if xs.zipdash or xs.zipdashslide
		XSZipDashCancel(p, s, xs) xs.zipdash,xs.zipdashslide = 0,0
	end
	
	RB.quickresponse = 0
	RB.cancharge,RB.charging,RB.C1stun,RB.C2stun,RB.countered,RB.pressedC2,RB.magicC2 = nil
	RB.CLresist,RB.RBmissed,RB.RingBurstChase,RB.RBdiv,RB.painstack,RB.quickroll,RB.dontextendagain = nil
	RB.riseanimtimer,RB.rocket,RB.dualpress,RB.CancelMatch = 0,0,0,0
	
	RBB.riseanimtimer,RBB.rocket,RBB.dualpress = 0,0,0
	RBB.crossedarms,RBB.cancharge,RBB.charging,RBB.C1stun,RBB.C2stun,RBB.countered,RBB.magicC2,RBB.MeteorSmash,RBB.counters = nil
	RBB.CLresist,RBB.RBmissed,RBB.RingBurstChase,RBB.RBdiv,RBB.painstack,RBB.quickroll,RBB.dontextendagain = nil
	
	s.state = S_PLAY_FALL s.tics,s.anim_duration = 13,13
	P_TryMove(s, s.x-FixedMul(16*s.scale, cos(MOMANG)), s.y-FixedMul(16*s.scale, sin(MOMANG)), true)
	P_InstaThrust(s, MOMANG, ((FORCE) or RB.WasRingBursting) and -47*s.scale or -32*s.scale)
	xs.spin,xs.jump,xs.custom1,xs.custom3 = 99<<16,99<<16,99<<16,99<<16
	RB.noZcontrol,RB.noXYcontrol = 15,17
	xs.stopstick,xs.stopbutton = 9,2

	if CANHURT==true --This only applies if you hit them!
		local OGMOMZ = s.momz
		
		RB.haymaker = {
		timer = 1, 
		angle = MOMANG,
		jump = (p.cmd.buttons&BT_JUMP) and 99<<16 or 0,
		custom1 = (p.cmd.buttons&BT_CUSTOM1) and 99<<16 or 0,
		spin = (p.cmd.buttons&BT_SPIN) and 99<<16 or 0,
		custom2 = (p.cmd.buttons&BT_CUSTOM2) and 99<<16 or 0,
		custom3 = (p.cmd.buttons&BT_CUSTOM3) and 99<<16 or 0,
		tossflag = (p.cmd.buttons&BT_TOSSFLAG) and 99<<16 or 0,
		force = (FORCE)
		} 
		xs.stopbutton = 0
		xs.forceangle = {angle = MOMANG, nosangle = true, timer = 9} --Forcibly look this way.
		S_StopSoundByID(s, sfx_xssfpl)
		if xs.ChaosLambent
			XSon.RemoveLambentHitBoxes(p)
		end		
		if xss.ChaosLambent
			XSon.RemoveLambentHitBoxes(tp)
		end
		RBB.quickresponse = 0
		RB.windlines,RBB.windlines = 3,7
		RB.CantHitLambence = max(RB.CantHitLambence or 0, 7)
		t.XSautododge = nil
		if RBB.SnapOverHeat
			if (FORCE==1)
				RBB.SnapOverHeat = max(min(40, RBB.SnapOverHeat or 0), 24)
			else
				RBB.SnapOverHeat = $/2
			end
		end
		
		if (FORCE) --Ring Burst Chase Impact!
			XSon.Sound(s, (RB.WasRingBursting) and sfx_xssfpp or sfx_xssfpn)
			XSon.Sound(s, sfx_xssfe3)		
			if (FORCE==2)
				RB.refreshC1 = 12
				xs.ZoomLines = 5
				xs.hudboost = 2
				S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx)
				S_StopSoundByID(s, sfx_xssfe3)
				XSon.Sound(s, sfx_xssfe3)
				XSon.Sound(t, sfx_xssfp9)
				local ghs = XSon.LightningStrike(t, SKINCOLOR_ICY, nil, true)
				ghs.spritexscale,ghs.spriteyscale = $*2,$*2
				RB.WasRingBursting,RBB.WasRingBursting = nil
				RBB.SnapOverHeat = 0			
			else
				xs.stopstick = 0
				xs.ZoomLines = 2
				RB.forceanim = "ringburst"
				RBB.gotRBhit = 3 --Don't do a painsound from this one.
				if RBB.SnapOverHeat
					RBB.SnapOverHeat = max($-9,0) 
				end
			end
			RB.noZcontrol,RB.noXYcontrol = 1,4
			s.momz = -$
			if (abs(s.momz) > 28*s.scale)
				s.momz = ($<0) and -28*s.scale or 28*s.scale
			end
			if (s.momz*P_MobjFlip(s) < 21*s.scale)
				P_SetObjectMomZ(s, 8<<16, true)
			end
			s.state = S_PLAY_XDIVEFLIP1
			s.anim_duration,s.tics = 9,9
			xs.frontflip = 0
			XSZipTrail(s, nil, 1) XSZipTrail(t, nil, 1)
		else
			RB.forceanim = "haymaker"
			xs.ZoomLines = 4
			
			local SFX = P_SpawnMobjFromMobj(t,0,0,0, MT_THOK)
			SFX.fuse,SFX.tics = 19,19 SFX.flags = MF2_DONTDRAW
			S_StartSound(SFX, sfx_xssfpo) S_StartSound(SFX, sfx_xssfxu)	
			
			XSon.Sound(s, sfx_xssfpl)
			P_SetObjectMomZ(s, 3<<16)
			if (abs(s.z-s.ceilingz) < 275*s.scale)
				s.momz = $-9*s.scale		
			end
			if (abs(s.z-s.floorz) < 275*s.scale)
				s.momz = $+7*s.scale
			end
		end
		if (FORCE!=1)
			--Attack shout
			local TABLE = {sfx_xsvopb,sfx_xsvopc,sfx_xsvopb,sfx_xsvope, sfx_xsvocq, sfx_xsvopl}
			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	
			
			--Effects.
			local ghs = XSon.FireBall(t, nil, 70000, FRACUNIT/10, FF_TRANS20)
			ghs.sttspeed,ghs.fuse,ghs.sttfade = 1,18,12
			ghs.tracer,ghs.ditchtracer = t,6
			ghs.momx,ghs.momy,ghs.momz = t.momx/2,t.momy/2,t.momz/2			
		end
		
		for i = 1,2
			local mo = (i==1) and s or t
			local ghs = XS_CircleWave(mo, 1, SKINCOLOR_RUBY-i+1) --Impact Wave!
			ghs.sprite = SPR_XSG2
			ghs.frame = 31|(FF_TRANS60+(i<<16))
			P_Thrust(ghs, MOMANG-ANGLE_90, mo.scale*3)
		end

		XSon.ElectricBody(s, 6, SKINCOLOR_FLAME) --Electric aura on Sonic
		XSon.ElectricBody(t, 11, SKINCOLOR_CRIMSON) 
		XSon.SpikedAura(s, 117000)
		XSon.SpikedAura(t, 96000)
		
		RBB.painflash = 19
		
		XSWindSpawn(t)	
		XSon.Quake(s, 72<<16, 12, 1500<<16)
		XSon.Flash(t, PAL_RECYCLE, 2, 1)
		if RB.SRAlunge
			local BAG = XSon.SandBagShift(BAG, t, s)
			if BAG and BAG.valid
				BAG.tracer = s
				XSon.SandBag(BAG, "attack", nil)
				if s.XSassault --Snap!
					xs.jump,xs.spin,xs.custom1,xs.custom3 = 999<<16,999<<16,999<<16,999<<16
					XSon.AssaultAnim(p,s,xs)	
					RB.haymaker = nil
					
					tp.xssuperflash = {trans = FF_TRANS50, timer = 10, speed = 1, reversal = 5, COLOR = SKINCOLOR_RED}
					XSon.Flash(t, PAL_NUKE, 5, 5)
				end
			end
			if not s.XSassault --Shouldn't happen, but deal SOME damage anyway.
				RB.SRAlunge,RB.SRAdrain = nil
				RB.PvPENERGY = 0
				XSon.SubtractPvPHealth(275, RBB, RB) 
			end
		end
		
		return "tackle" --
		
	else --Got countered!
		if RB.SRAdrain 
			RB.PvPENERGY = max(0, min(10000, $+2000))
			RB.SRAdrain  = nil
		end
		if xss.zipdash
			XSZipDashCancel(tp, t, xss) xss.zipdash,xss.zipdashslide = 0,0
		end	
		if RBB.SnapOverHeat
			RBB.SnapOverHeat = max(0, $-14)
		end
		ZipDashRecoil(p,s,xs,RB, MOMANG)
		XSRemoveDiveHB(xs) XSRemoveDiveHB(xss)
		xs.snaptarget,xss.snaptarget = nil --Remove targets.
		RB.custom2,RB.custom1,xs.custom1 = 99<<16, 99<<16, 99<<16
		RB.snapCD = max(RB.snapCD or 0, 7)
		xs.stopstick = 8
		RB.SnapOverHeat = min(33, max((RB.SnapOverHeat or 0)+16, 16))
		RB.cantsnap = max(RB.cantsnap or 0, 9)
		
		if not RB.painstun
			local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSTRACKER2)
			if ghs and ghs.valid
				ghs.target = s ghs.SteamTrail = 1	
			end		
			RB.stripes = 11
			s.momz = ($/3)-(13*t.scale*P_MobjFlip(t))
			P_Thrust(s, MOMANG, -72*s.scale)
			S_StartSound(s, sfx_xssfpq) S_StartSound(s, sfx_xssfpq)
			S_StartSound(s, sfx_xssfxn)
			S_StartSound(s, sfx_xssfxk) S_StartSound(s, sfx_xssfxl)
			RB.painstun = 7
			RB.painstack,RB.quickroll,RB.rollD = 9, 26, (leveltime%2) and -1 or 1	
		end
		
		t.momz = $*2/3
		t.momx,t.momy = $/3,$/3
		P_InstaThrust(t, MOMANG, 10*s.scale)
		xss.rollcd = 0
		S_StartSound(t, sfx_kc4c)
		
		local ghs = XS_CircleWave(s, 1, nil, 16, FRACUNIT)	
		ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60
		
		if xss.snapdivetrick and (xss.snapdivetrick < 8) --Gotime!			
			XSSnapDiveBounce(tp, s, xss)
			local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and t.ceilingz or t.floorz
			if (abs(t.z-FLOORZ) > 64*t.scale)
				t.z = $-64*t.scale*P_MobjFlip(t)
			end
			if xss.snapdivetrick
				xss.snapdivetrick = max($,9)			
			end
			local ghs = P_SpawnGhostMobj(t) --Transition ghost
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.blendmode = AST_ADD
			ghs.colorized = true
			ghs.fuse,ghs.tics = 8,9
			ghs.destscale = 1
		--	P_SetObjectMomZ(t, -4<<16, true)
			P_Thrust(t, MOMANG, 35*s.scale)
			
			RBB.stripes = 9
		end
		RBB.PvPENERGY = max(0, min(10000, $+50))
		RBB.snapCD = max(RBB.snapCD or 0, 6)
		RBB.DodgeFrames = max(RBB.DodgeFrames or 0, 5)
		RBB.noXYcontrol = max(RBB.noXYcontrol or 0, 3)
		RBB.canforcezip = 40		
		RBB.windlines = 1
		RBB.refreshC1 = 38
		RBB.forcesnap = 25
		RBB.RBupdown,RBB.painstack,RBB.quickroll,RBB.painstun,RBB.rollD,RBB.C2stun,RBB.C1stun,RBB.haymaker = nil
		RBB.custom1 = 0
		tp.cmd.buttons = $ & ~(BT_SPIN|BT_CUSTOM1)
		xss.stopbutton = 0
		tp.powers[pw_nocontrol] = 0
		tp.pflags = $ & ~(PF_STASIS|PF_FULLSTASIS|PF_SPINNING|PF_JUMPED)
		t.angle = MOMANG+ANGLE_180 tp.drawangle = t.angle
		XS_Vortex(t, 11, 2<<16, nil, nil, SKINCOLOR_SKY)	
		
		return "counter" --
	end
end

--Automatically adjust vertical position?
XSon.PvPZAdjust = function(p,s,xs,RB, t, MAXDIFF, ZSPEED, MOM)
	if RB.haymaker or RB.painstun or RB.noZcontrol or xs.snapdivetrick or xs.snapdivekick return end --
	MAXDIFF = (MAXDIFF) or 64*s.scale
	local ZDIFF = abs((s.z+s.momz)-(t.z+t.momz))
	if (ZDIFF > MAXDIFF) 
		ZSPEED = (ZSPEED) or 10*s.scale
		MOM = (MOM) or 2
		local AJ = (ZDIFF < (MAXDIFF*3/2)) and ZSPEED/2 or ZSPEED
		local MAXMOMZ = 32*s.scale
		if (s.z < t.z)
			if (s.z+s.momz+s.height+AJ <= s.ceilingz)
				s.z = $+AJ
			end
			if (s.momz < MAXMOMZ) and (type(MOM)=="number")
				if (s.momz > MAXMOMZ*2/3)
					MOM = $*2
				end
				s.momz = $+AJ/MOM
			end
		else
			if (s.z+s.momz-AJ >= s.floorz)
				s.z = $-AJ
			end
			if (s.momz > -MAXMOMZ) and (type(MOM)=="number")
				if (s.momz < -MAXMOMZ*2/3)
					MOM = $*2
				end
				s.momz = $-AJ/MOM
			end
		end
		return true --
	end
end

local LoserHit = function(p,s,xs,RB, CL)
	XSon.DeTransformGFX(p,s,xs, "sparkles")

	local ghs = XSon.ChargeSpark(s, SKINCOLOR_GOLDENROD, 120000, nil, AST_ADD, 38, 34|FF_TRANS10)
	ghs.destscale = $*7 ghs.scalespeed = $*2
	ghs.tracer,ghs.ditchtracer = s, 16
	XSon.EarthQuake(s, 5, 700*s.scale, 1, 70000, FRACUNIT/5, -ANG30, 32|FF_TRANS50, 53, SKINCOLOR_SUPERGOLD3, AST_ADD)	
	for i = 0,8
		ghs = XSon.SmokeMist(s, 0, 99*s.scale)
		if ghs and ghs.valid
			ghs.spritexscale,ghs.spriteyscale = $+(i*2500),$+(i*2500)
		end
	end
	RB.CantHitLambence = 38 --Can't hit them immediately again.
	RB.painstun = FRACUNIT

	--Loud ass SFX.
	local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
	ghs.tics,ghs.fuse = 220,220 ghs.flags = MF2_DONTDRAW
	if not S_SoundPlaying(s, sfx_xssfpr)
		S_StartSound(s, sfx_xssfpp)
		S_StartSound(s, sfx_xssfpr) 
		S_StartSoundAtVolume(s, sfx_xssfpt, 99)
	end
	S_StartSoundAtVolume(ghs, sfx_xssfxp, 180, p)
	S_StartSoundAtVolume(ghs, (leveltime%2) and sfx_xssfxr or sfx_xssfxt, 180, p)
	local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSTRACKER2) --Smoketrails
	if ghs and ghs.valid
		ghs.target = s ghs.SteamTrail = 1	
	end									
	xs.hudboost = 1
	XSon.ElecticBody(s, 9, SKINCOLOR_FLAME, FF_TRANS10, AST_TRANSLUCENT) 
	XSon.SuperSonicWind(s, SKINCOLOR_GARNET, 57, FF_TRANS10, FRACUNIT+39000)
end

--Remove hitboxes from Chaos Lambent.
XSon.RemoveLambentHitBoxes = function(p, type)
	local CL = p and p.xsonictable and p.xsonictable.ChaosLambent
	if CL and CL.ghstable
		local shine = CL.ghstable
		for _,ghs in pairs(shine)
			if ghs and ghs.valid
				if ghs.clHB and ghs.clHB.valid
					P_RemoveMobj(ghs.clHB)
				end
				if type == "all"
					P_RemoveMobj(ghs)
				end
			end
		end
		CL.noHB,CL.ghstable = 99,nil
	end	
end

--Beam Struggles! Requires a shitton of tables!
XSon.LambentStruggle = function(p,s,xs,RB,CL, tp,t,xss,RBB,CLL) 
	if xss == nil --Manually acquire target info?
		if not (RB and RB.LockOn and RB.LockOn.player)
			if CL CL.struggle = nil end return false --
		end 
		t = RB.LockOn tp = t.player xss = tp.xsonictable
		if not xss and xss.FreeFlight and xss.ChaosLambent
			if CL CL.struggle = nil end return false --
		end
		RBB = xss.FreeFlight CLL = xss.ChaosLambent
	end
	--First, make 100% sure everything is valid!
	if not (s and s.valid and s.health) or not (p and p.rings) --Live, duh.
	or not xs or not (RB and RB.PvPHEALTH) or not CL --xsonictable, FreeFlight table and Chaos Lambent table.	
	or not (t and t.valid and t.health) --We're not actually manipulating the other player much, just making sure they exist.
	or not (tp and tp.rings) or not xss or not (RBB and RBB.PvPHEALTH) or not CLL
		if CL CL.struggle = nil end
		if CLL CLL.struggle = nil end return false --Too bad!
	end
	if CL.struggledone return false end
	if CL.power == nil
		CL.power,CL.powercount,CL.powerpressed = 0,0,0
		if (CL.init < 15) --How did we get here?? Whatever, get to the shooting frames.
			CL.init = 15
		end
	end
	
	local TOANGLE = R_PointToAngle2(s.x, s.y, t.x, t.y)
	local DIST = FixedHypot(s.x-t.x, s.y-t.y)
	if (CL.init > 29) --Loop blasting frames!
		CL.init = 19
	end
	s.momx,s.momy,s.momz = 0,0,0
	xs.forceangle = {angle = TOANGLE, nosangle = true, timer = 9}
	s.angle = TOANGLE
	p.drawangle = TOANGLE --Always face your enemy! FOCUS!
	
	CL.frame = (leveltime%2) and C or B
	s.sprite2 = SPR2_MLEL|FF_SPR2SUPER
	s.tics = max($,4) s.anim_duration = max($,4)

	if not CL.struggle --Initial locking into the struggle!
		local ZDIST = s.z-t.z
		CL.struggle = 1
		CL.init = 18
		s.rollangle = 0
		local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSTRACKER2) --Smoketrails
		if ghs and ghs.valid
			ghs.target = s ghs.SteamTrail = 1	
		end	
		S_StartSound(s, sfx_xssfpr) 
		S_StartSoundAtVolume(nil, sfx_xssfpt, 99, p)
		S_StartSoundAtVolume(nil, sfx_xssfpp, 190, p) 
		XS_RingBurstWave(s, nil, SKINCOLOR_FLAME, FF_FULLBRIGHT|FF_TRANS10)
		p.xssuperflash = {trans = FF_TRANS10, timer = 21, speed = 3, reversephase = true, COLOR = SKINCOLOR_SUPERGOLD2}	
		xs.hudboost = 1
		xs.ZoomLines = 10
		P_FlashPal(p, PAL_WHITE, 3)
		XSon.ElecticBody(s, 5, SKINCOLOR_CRIMSON, FF_HORIZONTALFLIP, AST_TRANSLUCENT) 
		XSon.SuperSonicWind(s, SKINCOLOR_SKY, 37, FF_TRANS10, 2<<16)
		XSon.SquashFlash(s, SKINCOLOR_SUPERSKY5, nil, FF_TRANS10, FRACUNIT+44000)
		XSon.SideSparks(s, SKINCOLOR_RUBY)
		
		P_StartQuake(140<<16, 5)
		if not CLL.struggle --Only one of you can smash the noise button, boys
			local X,Y,Z = XSon.MidPoint(s, t)
			local SFX = P_SpawnMobj(X,Y,Z, MT_THOK)
			SFX.flags2 = MF2_DONTDRAW SFX.tics,SFX.fuse = 299,299 --Loud noises.
			S_StartSound(SFX, sfx_xssfp1)
			S_StartSound(SFX, (leveltime%2) and sfx_xssfxr or sfx_xssfxt)
			S_StartSound(SFX, sfx_bkpoof) 
			S_StartSound(SFX, sfx_brakrx)
			
			XSon.SuperSonicBoomShot(SFX, nil, nil, nil, 60, FF_TRANS10, 3<<16)
			XSon.LightningStrike(SFX, SKINCOLOR_RUBY, SFX) --Lightning strikes the middle-point.
			XSon.LightningStrike(SFX, SKINCOLOR_GOLDENROD, SFX, (Z+2)|FF_TRANS10|FF_HORIZONTALFLIP)	
			XSon.ExpandingGlow(SFX, SKINCOLOR_YELLOW, 23, 95, true, AST_ADD)
			XS_RingBurstWave(SFX)
			XSon.OrbPillars(SFX, 1)
			XSon.SideSparks(SFX, SKINCOLOR_RUBY, nil, FF_TRANS10, 2<<16)
			
			local ghs = XSon.ElecticBody(SFX, 70, SKINCOLOR_SUPERSKY3, FF_TRANS10, AST_ADD) --Large sparks in the middle
			ghs.dispoffset = 99 
			ghs.sttfade = 17
			ghs.spritexscale,ghs.spriteyscale = 4<<16, 4<<16
			ghs.destscale = 99<<16
			for i = 12,44,4
				ghs = XSon.SmokeMist(s, 0, 99*s.scale)
				if ghs and ghs.valid
					ghs.spritexscale = ($*i)/3 
					ghs.spriteyscale = ($*i)/4
					ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
					ghs.fuse,ghs.tics = $+(64-i),$+(64-i)
					P_SetOrigin(ghs, X, Y, Z)
				end
			end
		end	
	else
		if not CLL.struggle and not (CL.struggledone==true) --Now catch up!
			CLL.struggle = CL.struggle
			CLL.power = CL.power
			CLL.powercount = CL.powercount
			CLL.powerpressed = CL.powerpressed
			CLL.struggletime = CL.struggletime
			CLL.init = CL.init
		end	
		if (CL.struggle%4==0)
			xs.ZoomLines = 6
			XSon.ElecticBody(s, 9, (CL.struggle%8==0) and s.color or SKINCOLOR_RUBY, (CL.struggle%8==0) and FF_HORIZONTALFLIP|FF_TRANS10 or FF_TRANS10, AST_TRANSLUCENT)
			XS_RingBurstStripes(s, SKINCOLOR_WHITE, AST_ADD, -5<<16, nil, TOANGLE)
			
			local ghs = P_SpawnGhostMobj(s)
			ghs.scale = s.scale*3/2
			ghs.scalespeed = FRACUNIT/7
			ghs.destscale = 99<<16
			ghs.dispoffset = -99
			ghs.tics,ghs.fuse = 11,9
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.blendmode = AST_ADD
			ghs.colorized = true
			ghs.color = P_RandomRange(SKINCOLOR_SUPERSILVER1, SKINCOLOR_SUPERRED5)
			P_Thrust(ghs, TOANGLE, -XSLocalRNG(12,40,s))
			
			if CLL.init and (CL.init > CLL.init)
				local X,Y,Z = XSon.MidPoint(s, t)	
				
				local ghs = XSon.SmokeCircle(s, 0, 1, nil, XSLocalRNG(2,4,t)<<16)
				ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
				ghs.spritexscale,ghs.spriteyscale = $*10,$*3
				ghs.dispoffset = 145
				ghs.sttspeed = 2
				ghs.fuse = 32
				P_SetOrigin(ghs, X, Y, Z)
				
				ghs = XS_RingBurstDust(p, s, xs, "manual", s.z) --Crash wave.
				ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS20
				ghs.spritexscale,ghs.spriteyscale = $+XSLocalRNG(4,9,s)<<16, $+4<<16
				ghs.scalespeed = $*10/XSLocalRNG(8,12,ghs)
				ghs.dispoffset = 148
				ghs.spriteyoffset = $-32<<16
				ghs.flags = $|MF_NOGRAVITY|MF_NOCLIPHEIGHT
				P_SetOrigin(ghs, X, Y, Z)
				
				if (CL.struggle%8==0)
					
					ghs = XS_CircleWave(s, 1, (CL.struggle%8==0) and SKINCOLOR_GOLDENROD or SKINCOLOR_KETCHUP) 
					ghs.blendmode = AST_ADD
					ghs.angle = TOANGLE+ANGLE_90
					ghs.tracer = nil
					ghs.sprite = SPR_XSG2
					ghs.frame = 31|FF_TRANS10
					ghs.sttfade = 12
					ghs.tics,ghs.fuse = 15,15
					ghs.dispoffset = 149
					ghs.spritexscale,ghs.spriteyscale = $*6,$*6
					ghs.scalespeed = FRACUNIT/XSLocalRNG(5,9,s)
					P_SetOrigin(ghs, X, Y, Z)
					if (CL.struggle%16==0)
						for i = -1,1,2
							XSon.EarthQuake(ghs, (CL.struggle%32==0) and 3 or 2, (850+(100*i))<<16, "tracer", 4<<15, 35033, (-ANG10*5)*i, J, 30, 
							(CL.struggle%48==0) and SKINCOLOR_SUPASONIKKU or (CL.struggle%32==0) and SKINCOLOR_KETCHUP or SKINCOLOR_SKY, AST_ADD)
						end	
					end
					if p==displayplayer
						P_Thrust(ghs, TOANGLE, -48<<16) --Come towards the camera!
					end	
					if (CL.struggle%12==0)
						ghs = XSon.ElectricBody(s, 11, (CL.struggle%24==0) and SKINCOLOR_SUPERSKY5 or SKINCOLOR_CRIMSON)
						ghs.blendmode = AST_TRANSLUCENT
						ghs.frame = ($ & ~FF_TRANSMASK)|((CL.struggle%24==0) and FF_HORIZONTALFLIP or 0)|FF_TRANS10
						ghs.dispoffset = 147
						ghs.tracer = nil
						ghs.spritexscale = 10<<16
						ghs.spriteyscale = 8<<16
						ghs.spriteyoffset = $-16<<16
						ghs.destscale = 99<<16
						ghs.sttfade = 3
						ghs.scalespeed = FRACUNIT/XSLocalRNG(1,4,ghs)
						P_SetOrigin(ghs, X, Y, Z-16<<16)					
						
						if (CL.struggle%24==0)					
							for i = 12,44,4
								ghs = XSon.SmokeMist(s, 0, 99*s.scale)
								if ghs and ghs.valid
									ghs.spritexscale = ($*i)/3
									ghs.spriteyscale = ($*i)/2
									ghs.sttfade = 18
									ghs.dispoffset = 99
									ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS20
									ghs.fuse,ghs.tics = $+(54-i),$+(54-i)
									P_SetOrigin(ghs, X, Y, Z)
								end
							end							
						end
					end
				end
			end
		end
	end
	
	--Try to shift the beam struggle into a good location for better cinematics.
	if (DIST < 3300*s.scale) --Back off!
		P_TryMove(s, s.x-FixedMul(50*s.scale, cos(TOANGLE)), s.y-FixedMul(50*s.scale, sin(TOANGLE)), true)
	end
	
	if CL.powerpressed
		CL.powerpressed = max($-1,0)
	end
	local PLUS = 0
	if CL.struggletime
		if (CL.struggletime > 27) --Each mash counts more the longer the struggle goes on! Don't give up!
			PLUS = (CL.struggletime > 70) and $+5 or (CL.struggletime > 60) and $+3 or (CL.struggletime > 49) and $+2 or  $+1
		end
	end
	--Lastly the beam struggle gameplay part!
	if CL.jump == 1 and not (not CL.struggletime or (CL.struggletime < 5))
		CL.power = $+5+PLUS
		if CL.ChaosControl
			CL.power = $+3 --Gain the upper hand easier if your beam was powered up!
		end
		CL.powercount = CL.powercount and $+1 or 1  --Timer on the HUD for how much you've pressed jump!
		CL.powerpressed = 8
	end
	if CL.spin --Sacrifice everything to win at all costs!!
		if not (not CL.struggletime or (CL.struggletime < 9))
			if RBB and RBB.PvPHEALTH and (RBB.PvPHEALTH > RB.PvPHEALTH)
				RB.PvPHEALTH = max($-6, 1)
				CL.power = $+1
			else
				RB.PvPHEALTH = max($-9, 1)
				if not ((leveltime%3==0) and RBB and RBB.PvPHEALTH and (RB.PvPHEALTH-25000 > RBB.PvPHEALTH))
					CL.power = $+1
				end
			end
			if CL.spin == 1 --Make mashing it a somewhat viable alternative.
				CL.powerpressed = 9
				CL.power = $+1+(PLUS/3)
			end
		end
		if (CL.powercount < (99<<16)) --Timer changes color on pressing spin.
			CL.powercount = $+(99<<16)
		end
	end
	local QUAKE = 56<<16
	XSon.Quake(s, QUAKE, 6, 7700<<16)
	CL.struggleDIV,CL.struggleBEAM = 100,100
	if CLL.power != nil
		local TRUEDIFF = (CL.power-CLL.power)
		local DIFF = abs(TRUEDIFF)
		if (TRUEDIFF >= 20) and (DIFF < 0) --Not good, you're losing!
			local SDIFF = max(1, min(99, DIFF/s.scale) ) 
			CL.struggleDIV = 100-SDIFF
			CL.struggleBEAM = max(50, 100-(SDIFF/3))
			QUAKE = min(140<<16, $+((SDIFF/4)<<16))
		end
		XSon.Quake(s, QUAKE, 6, 999<<16)
	end
	local FF = xs.FreeFlight 
	if FF
		xs.stopstick = 4
		xs.custom3 = 99<<16
		xs.custom2 = 99<<16
		xs.custom1 = 99<<16
		FF.spin = 99<<16
		FF.jump = 99<<16
		FF.noXYcontrol = 8
		FF.noZcontrol = 8
		FF.DodgeFrames = 2
		FF.refreshC1 = 23
		FF.CantHitLambence = 28
		FF.blackbars = 2
		FF.windlines = 2
	end
	p.cmd.sidemove,p.cmd.forwardmove = 0,0
	xs.direct = 0
	CL.struggletime = CL.struggletime and $+1 or 1
	s.XS_DidSuperAura,s.XS_DidBlur = true,true
	
	if (CL.struggletime > 85)
		S_StartSound(nil, sfx_xssfe0) S_StartSound(nil, sfx_xssfxp)
		CL.struggledone,CLL.struggledone = true,true
		CL.struggle,CLL.struggle,CL.struggletime,CLL.struggletime = nil
	
		local WIN,LOSE = t,s --Did we win or lose?
		if ((CL.power or 0) > (CLL.power or 0)) --We win!		
			WIN,LOSE = s,t
		end
		
		xs.BeamClashResults = {timer = 1, mypower = CL.power or 1, foepower = CLL.power or 1, won = (s==WIN) and true or false}
		xss.BeamClashResults = {timer = 1, mypower = CLL.power or 1, foepower = CL.power or 1, won = (t==WIN) and true or false}
		
		if LOSE and LOSE.player
			local p = LOSE.player local xs = p.xsonictable local FF = xs.FreeFlight local CL = xs.ChaosLambent
			LoserHit(p,LOSE,xs,FF,CL)
			XSon.RemoveLambentHitBoxes(p, "all")
			CL.init = 99<<16
			XSon.ChaosLambent(p,LOSE,xs,CL) 
			
			XSon.Flash(LOSE, PAL_NUKE, 8)
			local NOOOO = (leveltime%2) and sfx_xsvocx or sfx_xsvoc5
			XSVoice(p, LOSE, xs, NOOOO, "ForceEcho") ---NOOOOOOOOO!!!
			if xs.voice
				S_StartSound(nil, NOOOO, p)
			end
			
			XSon.Quake(LOSE, 105<<16, 26, 8000<<16)
			xs.hudboost = 1
			xs.ZoomLines = 12
			if FF				
				FF.stripes = 16
				FF.DodgeFrames = 0
				FF.noXYcontrol = 18
				FF.noZcontrol = 18
				
				local POWER = (WIN and WIN.player and WIN.player.xsonictable and WIN.player.xsonictable.ChaosLambent
				and WIN.player.xsonictable.ChaosLambent.ChaosControl==true) and 2402 or 2001
				
				XSon.SubtractPvPHealth(POWER, FF, WIN.player.xsonictable.FreeFlight) --Take a ton of extra damage!
				
				FF.CantHitLambence = 28
				FF.snapCD = 18
				FF.painstack,FF.forcesnap = nil
				FF.painstun = FRACUNIT --Just incase.
				FF.NoSlamDamage = 16
			/*	if LOSE.XS_banter==nil
					local VCLIPS = {sfx_xsvoc9,sfx_xsvoc1,sfx_xsvocj,sfx_xsvopx, sfx_xsvopy}
					LOSE.XS_banter = {timer=60, SFX = VCLIPS[XSLocalRNG(1,#VCLIPS, LOSE)], 
					targetplayer = (WIN) and WIN.player, extra = true}
				end */
			end
			P_InstaThrust(LOSE, LOSE.angle, -120*LOSE.scale)
			p.powers[pw_nocontrol] = 18
			xs.stopstick = 19
			p.xssuperflash = {trans = FF_TRANS10, timer = 24, speed = 3, reversephase = true, COLOR = SKINCOLOR_RED}
			XSon.BurstWave(LOSE, 1, SKINCOLOR_RUBY, AST_ADD, nil, 3<<16, LOSE.angle)
			local ghs = XSon.ChargeSpark(LOSE, SKINCOLOR_FLAME, 179000, nil, AST_ADD, 69, 34|FF_TRANS10)
			ghs.destscale = $*8
			ghs.scalespeed = $*2/3
			XSon.EarthQuake(LOSE, 10, 550*s.scale, 1, 68000, FRACUNIT/5, ANG1*42, 32|FF_TRANS50, 61, SKINCOLOR_WHITE, AST_ADD)
			
			for i = -1,1
				local ghs = XSon.LightningStrike(LOSE, nil, nil, true)
				ghs.spritexoffset = $+((50*i)<<16)
				ghs.spritexscale,ghs.spriteyscale = $*4,$*4
				ghs.fuse,ghs.tics = $+6,$+6
				ghs.z = $+199*LOSE.scale
			end
			for i = 4,36,4
				local ghs = XSon.SmokeMist(LOSE, 0, 99*LOSE.scale)
				if ghs and ghs.valid
					ghs.spritexscale = ($*i)/4 
					ghs.spriteyscale = ($*i)/4
					ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
					ghs.fuse,ghs.tics = $+(54-i),$+(54-i)
				end
			end
			XSon.ChargePower(LOSE, 59, LOSE, nil, nil, SKINCOLOR_CRIMSON)
			XSon.RingBurstStartGFX(p,LOSE,xs)
			if WIN
				P_NukeEnemies(LOSE, WIN, 1500<<16)
			end
		end
		if WIN and WIN.player
			local p = WIN.player local xs = p.xsonictable local FF = xs.FreeFlight local CL = xs.ChaosLambent
			
			XSon.Flash(WIN, PAL_RECYCLE, 6)
			CL.init = 26
			XSon.RemoveLambentHitBoxes(p)
			if FF
				FF.NoSlamDamage = 16
				FF.snapCD = 5
				FF.stripes = 2
				FF.refreshC1 = 8
				FF.CantHitLambence = 28
				FF.DodgeFrames = 18
				FF.forcecrossarms = 9
				
				local VCLIPS = (leveltime%3==0) and {sfx_xsvoce,sfx_xsvoce,sfx_xsvocg,sfx_xsvopt,sfx_xsvoc3} or {sfx_xsvopu,sfx_xsvoct}
				WIN.XS_banter = {timer=(leveltime%3==0) and 9 or 23, SFX = VCLIPS[XSLocalRNG(1,#VCLIPS, WIN)], 
				targetplayer = LOSE and LOSE.player, Rvolume = 255, extra=true, TPvolume = (leveltime%3==0) and 255 or 125}
			end
			XSon.Quake(WIN, 70<<16, 16)
			p.xssuperflash = {trans = FF_TRANS10, timer = 8, speed = 1, reversephase = true, COLOR = SKINCOLOR_SUPERGOLD2}
			XS_RingBurstWave(s)	
			XS_RingBurstShine(p, s, xs)
			
			S_StartSound(nil, sfx_xssfxr, p)
			S_StartSound(nil, sfx_xssfpp, 255, p)
			local SFX = XSon.GibSound(s, sfx_xssfxp, 180, 255)
			S_StartSoundAtVolume(SFX, (leveltime%2) and sfx_xssfxr or sfx_xssfxt, 180, p)
			S_StartSoundAtVolume(SFX, sfx_xssfpt, 130, p)
			S_StartSound(SFX, sfx_xssfpr, p)
			
			if xs.ChaosLambent
				CL.noHB = 99 --You can be done with hitboxes now.
				CL.ghstable = nil
			end			
		end
	end
end

--Chaos Blast is the PvP Counterwave! Can be used out of almost anything, canceling everything the enemy is doing.
XSon.ChaosBlast = function(p,s,xs,RB, t, tp, xss, RBB)
	xs.snapstickx,xs.snapsticky,xs.snapstickz = s.x,s.y,s.z
	XSSnapDiveReset(p, s, xs, true)
	xs.ringburstcharge,xs.snapdive,xs.snapdivetrick = 0,0,0
	if xs.zipdash or xs.zipdashslide
		XSZipDashCancel(p, s, xs)
	end	
	xs.walljump,xs.walljumpP,xs.forceangle,s.XSautododge = nil
	RB.refreshC1,RB.haymaker,RB.RBmissed,RB.RBdiv,RB.CLresist,RB.RBupdown,RB.RBside,RB.RingBurstChase = nil
	RB.shock,RB.painstack,RB.quickroll,RB.painstun,RB.rollD,RB.C2stun,RB.C1stun,xs.snaptarget,RB.forcesnap = nil
	p.viewrollangle,s.rollangle = 0,0
	RB.painflash = 0
	S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx) S_StopSoundByID(s, sfx_xssfxn)
	
	local SFX = XSon.GibSound(s, sfx_xssfxp, 160, 200)
	S_StartSound(SFX, sfx_xssfxs) S_StartSound(nil, sfx_xssfxs, p)
	s.state = S_PLAY_FALL
	s.flags2 = $ & ~MF2_DONTDRAW
	if (p.powers[pw_flashing] > 0)
		p.powers[pw_flashing] = 0
	end
	local Zoff = -((183*8)<<16)
	for i = -4,5 
		local ghs = XSon.TransformGFX2(s, Zoff) --Rising orbs
		if ghs and ghs.valid
			ghs.color,ghs.colorized = SKINCOLOR_KETCHUP, true
			ghs.spritexscale = ($*(5-min(4,abs(i)))) *4/5
		end Zoff = $+(188<<16)
	end
	RB.ExplosionCD = 8
	RB.BlastCharge.C3 = 99<<16
	RB.BlastCharge.LetGo = 0
	RB.noXYcontrol = max(RB.noXYcontrol or 24, 0)
	RB.noZcontrol = max(RB.noZcontrol or 18, 0)
	xs.stopstick,xs.stopbutton = 18,18
	xs.ZoomLines = 18
	RB.snapCD = 24
	
	XSon.SwapStretch(p, s, xs)
	s.spritexscale,s.spriteyscale = FRACUNIT+22500,FRACUNIT-15000
	s.spriteyoffset = -9<<16
	s.XSstretch = {timer = 6, Xspeed = 5000, Yspeed = 2600}
	
	local ghs = XSon.ElectricBody(s, 11)
	ghs.frame = $|FF_HORIZONTALFLIP

	ghs = XSon.ElectricBody(s, 16, SKINCOLOR_NOBLE)
	ghs.destscale = 99<<16

	XSon.BurstWave(s, 1, SKINCOLOR_RUBY, AST_ADD, nil, 3<<16, s.angle)
	XSon.EarthQuake(s, 9, 550*s.scale, 1, 69000, FRACUNIT/5, ANG20*2, 32|FF_TRANS30, 31, SKINCOLOR_WHITE, AST_ADD)

	for i = -1,1
		ghs = XSon.LightningStrike(s, nil, nil, true)
		ghs.spritexoffset = $+((50*i)<<16)
		ghs.spritexscale,ghs.spriteyscale = $*4,$*4
		ghs.fuse,ghs.tics = $+12,$+12
		ghs.z = $+199*s.scale
	end
	for i = 4,36,4
		ghs = XSon.SmokeMist(s, 0, 99*s.scale)
		if ghs and ghs.valid
			ghs.spritexscale = ($*i)/4 
			ghs.spriteyscale = ($*i)/4
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
			ghs.fuse,ghs.tics = $+(54-i),$+(54-i)
		end
	end
	RB.noXYcontrol = max(RB.noXYcontrol or 24, 0)
	RB.noZcontrol = max(RB.noZcontrol or 24, 0)
	RB.explosion = 30 --Go boom.
	RB.lambentCD = 19
	RB.cantsnap,RB.DodgeFrames,RB.CantHitLambence,RB.NoSlamDamage = 32,31,42,31
	RB.countered = 0
	RB.custom2,RB.custom1 = 99<<16,99<<16
	RB.windlines = 1
	
	s.momx,s.momy,s.momz = 0,0,0
	
	local XYDIST,ZDIST = 1999<<16, 999<<16
	searchBlockmap("objects",function(s,m) --Destroy nearby environmental objects.
		if m.valid and (m.flags & (MF_MONITOR|MF_SCENERY|MF_SOLID|MF_PUSHABLE))
		and not (m.flags & (MF_ENEMY|MF_SPRING|MF_BOSS|MF_NOSECTOR|MF_NOCLIPTHING))
		and not m.WaitingXTree and not m.player
			if not (m.z > s.z+ZDIST) and not (s.z > m.z+ZDIST)
				XSon.ScenicCrash(p,s,p.xsonictable, m, 3, nil, 24<<16, "silent")
			end
		end
	end, s, s.x-XYDIST,s.x+XYDIST, s.y-XYDIST, s.y+XYDIST)

	
	local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
	if (abs(s.z-FLOORZ) < 64*s.scale)
		s.z = $+32*s.scale*P_MobjFlip(s)
	end
	
	XSon.Flash(s, PAL_NUKE, 3, 2600<<16)
	p.xssuperflash = {trans = FF_TRANS50, timer = 10, speed = 1, flashtic = 9, reversal = 5, COLOR = SKINCOLOR_SUPERSILVER1}
	tp.xssuperflash = p.xssuperflash
	
	if xss.ChaosLambent and not (xss.ChaosLambent.struggledone) --Easy nullification.
		xss.ChaosLambent.noHB = 35
	end
	local DIST = FixedHypot(FixedHypot(s.x-t.x+t.momx, s.y-t.y+t.momy), s.z-t.z+t.momz) 
	if (DIST < 2750*s.scale) --Huge range for reliability's sake.	
		s.angle = R_PointToAngle2(s.x, s.y, t.x-t.momx, t.y-t.momy)
		p.drawangle = s.angle	

		S_StartSoundAtVolume(SFX, sfx_xssfpt, 140) S_StartSoundAtVolume(nil, sfx_xssfxs, 200, tp)
		S_StartSoundAtVolume(SFX, sfx_xssfp9, 230)
		
		if xss.snapdive or xss.ChaosSnap
			xss.snapstickx,xss.snapsticky,xss.snapstickz = t.x,t.y,t.z 
			XSSnapDiveReset(tp, t, xss, true)
		end	
		if xss.ChaosLambent xss.ChaosLambent = nil end		
		if xss.zipdash XSZipDashCancel(tp, t, xss) end
		if tp.xswallrun tp.xswallrun = nil end	
		S_StopSoundByID(t, sfx_xssfpz) S_StopSoundByID(t, sfx_xssfxx) S_StopSoundByID(t, sfx_xssfxn)	
		tp.viewrollangle,t.rollangle = 0,0
		xss.ringburstcharge,xss.snapdive,xss.snapdivetrick = 0,0,0
		
		if RBB	
			RBB.forcesnap,RBB.shock,RBB.painstack,RBB.painstun,RBB.rollD,RBB.C2stun,RBB.C1stun,xss.snaptarget = nil
			RBB.RBupdown,RBB.haymaker,RBB.RBmissed,RBB.RBdiv,RBB.CLresist,RBB.RBside,RBB.RingBurstChase,RBB.MeteorSmashed = nil
			xss.walljump,xss.walljumpP,xss.forceangle,t.XSautododge = nil
			xss.ringburstcharge,xss.snapdive,xss.snapdivetrick = 0,0,0
			if (RBB.PvPHEALTH > 1) --Can't die from this.
				XSon.SubtractPvPHealth(100, RBB, RB) 
				RBB.PvPHEALTH = max($,1) 
			end	
			XSon.GetHit(tp,t,xss,RBB)
			XSVoice(tp, t, xss, sfx_xsvocj, "ForceEcho")
			RBB.snapCD = 24
			RBB.refreshC1 = 34
			xss.stopbutton,xss.stopstick = 19,21
			xss.ZoomLines = 19
			RBB.noXYcontrol = 29
			RBB.noZcontrol = 19
			RBB.NoSlamDamage = 26
			RBB.windlines = 11
			RBB.quickroll = 12
			RBB.countered = 0
		end
		tp.powers[pw_nocontrol] = 30
		
		local ZDIST = max(1, abs(s.z-t.z)) --GET OFF ME!!
		DIST = max(1, $/(101*s.scale))
		local UPDOWN = (s.z>t.z) and -1 or 1
		local ZLAUNCH = min(46*s.scale, ZDIST/DIST )

		t.momz = ZLAUNCH*UPDOWN 
		P_InstaThrust(t, s.angle, 155*s.scale)
		p.xssuperflash = {trans = FF_TRANS50, timer = 11, speed = 1, flashtic = 9, reversal = 5, COLOR = SKINCOLOR_SUPERSILVER1}
	end
end

--Get ready to try the Super Rush Assault!
XSon.SuperRushAssaultPrep= function(ATTACK, p,s,xs,RB, t, tp, xss, RBB)
	if xs.snapdive or xs.snapdivekick or xs.snapdivetrick or xs.ChaosSnap
		xs.snapstickx,xs.snapsticky,xs.snapstickz = s.x,s.y,s.z
		XSSnapDiveReset(p, s, xs, true)
		xs.ringburstcharge,xs.snapdive,xs.snapdivetrick = 0,0,0
	end
	if xs.zipdash or xs.zipdashslide
		XSZipDashCancel(p, s, xs)	
		xs.zipdash,xs.zipdashslide = 0,0
	end
	s.momx,s.momy,s.momz = 0,0,$/5
	RB.refreshC1,RB.haymaker,xs.snaptarget = nil
	RB.shock,RB.painstack,RB.quickroll,RB.painstun,RB.rollD,RB.forcesnap = nil
	S_StopSoundByID(s, sfx_xssfpz) S_StopSoundByID(s, sfx_xssfxx) S_StopSoundByID(s, sfx_xssfxn)
	
	xs.custom1,xs.custom2,xs.custom3 = 99<<16,99<<16, 99<<16
	RB.jump,RB.custom1,RB.custom2,RB.custom3,RBB.spin = 99<<16,99<<16,99<<16,99<<16,99<<16
	RB.C1stun,RB.C2stun,RB.countered = nil
	
	RB.noXYcontrol,RB.noZcontrol = 3,3
	RB.cantsnap,RB.DodgeFrames = 2,2
	RB.snapCD = 15
	RB.lambentCD = 9
	
	XSVoice(p, s, xs, sfx_xsvopo, "ForceEcho")
	
	local ghs = XSon.ElectricBody(s, 10, SKINCOLOR_SUPASONIKKU)
	ghs.destscale = 99<<16
	
	local Zoff = -((183*8)<<16)
	for i = -2,2
		ghs = XSon.TransformGFX2(s, Zoff) --Rising orbs
		if ghs and ghs.valid
			ghs.color,ghs.colorized = SKINCOLOR_RED, true
			ghs.spritexscale = ($*(5-min(4,abs(i)))) *4/5
		end Zoff = $+(188<<16)
	end	
	for i = 3,5,2
		XSon.EarthQuake(s, i, 50*i*s.scale, 1, 30000+(i*26000), FRACUNIT/(2+i), ANG1*i*12, 32|(FF_TRANS90-i<<16), 3+(i*2),
		(i < 5) and SKINCOLOR_RED or SKINCOLOR_RUBY, AST_ADD)	
	end
	RB.SRAlunge = 90
	S_StartSound(s, sfx_xssfe2)
	
	local SFX = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
	SFX.flags2 = MF2_DONTDRAW SFX.tics = 30 XSon.Sound(SFX, sfx_pumpkn, 255)

	local ths,ghs = XS_QuickDust(s, SKINCOLOR_SALMON, 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, SKINCOLOR_RED, AST_ADD, 1<<16, 13)
	for i = 1,2
		ghs = XSon.ElectricBody(s, 11, (i==2) and SKINCOLOR_RED or SKINCOLOR_XSSUPERAURA)
		if i == 1
			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, SKINCOLOR_KETCHUP, 2)		
	
	XSon.Flash(s, PAL_NUKE, 3, 2650*s.scale)
	XSon.Quake(s, 125<<16, 9, 3500*s.scale)	
	
	if RBB
		S_StartSoundAtVolume(nil, sfx_xssfe2, 100, tp)
		xss.ZoomLines = 9
		RBB.blackbars = 15
		if not RBB.PushType
			RBB.PushType = 4
		end
	end
--	s.angle = R_PointToAngle(s.x, s.y, t.x, t.y)
--	p.drawangle = s.angle
--	P_Thrust(t, s.angle, 50*t.scale)	
end
--Super version of The Dark Rush Assault.
XSon.SuperRushAssault = function(ATTACK, p,s,xs,RB, t, tp, xss, RBB)
	local BAG = RB and RB.BAG
	if not (BAG and BAG.valid) or not RBB or not (ATTACK) return end
	
	RB.cantsnap,RBB.cantsnap = 9,9
	RB.snapCD,RBB.snapCD = 9,9
	RB.DodgeFrames,RBB.DodgeFrames = 9,9
	RB.CantHitLambence,RBB.CantHitLambence = 9,9
	RB.lambentCD,RBB.lambentCD = 9,9
	RB.CancelMatch,RBB.CancelMatch = 0,0
	RB.SRAlunge,RBB.SRAlunge,RB.SRAdrain,RBB.SRAdrain = nil
	RBB.ExplosionCD,RB.ExplosionCD = 20,20
	
	RB.PvPENERGY = 0
	RB.noXYcontrol,RB.noZcontrol = 16,16
	RB.rocketspin,RB.rocketjump = 0,0
	xs.custom1,xs.custom3 = 99<<16,99<<16
	RB.jump,RB.custom1,RB.custom2,RB.custom3,RBB.spin = 99<<16,99<<16,99<<16,99<<16,99<<16
	s.flags = $ & ~MF_NOGRAVITY

	--Now manipulate the target too.
	t.flags = $ & ~MF_NOGRAVITY	
	if ATTACK.type=="groundslam"
		if ATTACK.grounded
			P_MoveOrigin(t, s.x, s.y, s.z)
			t.spriteyoffset = -8<<16
			t.XSstretch = {timer = 1}			
		else
			P_TryMove(t, s.x, s.y, true)
			t.z = s.z
		end
	else --During regular attacks.
		if (BAG.z > t.z)
			if (BAG.z < t.ceilingz)
				t.z = BAG.z
			else
				t.z = t.ceilingz
			end
		elseif not P_IsObjectOnGround(t)
			if (BAG.z > t.floorz)
				t.z = BAG.z
			else
				t.z = t.floorz
			end
		end	
	end
	t.momx,t.momy,s.momz = BAG.momx, BAG.momy, BAG.momz
	
	if ((BAG.justgothit or 0) > 2) --and not BAG.BAGflurry
	
		t.angle = s.angle+ANGLE_180
		tp.drawangle = s.angle+ANGLE_180
		xss.ziptrailticket = 1
		if BAG.justgothit==10
			if (abs(s.z-t.z) > 400*s.scale) --No stupidity!
				if (BAG.momz*P_MobjFlip(s) > 48*s.scale) --Keep some vertical speed caps.
					BAG.momz = $*3/2
				end
				if (t.momz*P_MobjFlip(s) > 48*s.scale)
					t.momz = $*3/2
				end
				P_MoveOrigin(BAG, s.x, s.y, s.z)
				P_MoveOrigin(t, s.x, s.y, s.z)
				local STEP = (ATTACK.type=="groundslam") and 3*s.scale or 45*s.scale
				P_TryMove(BAG, s.x+(s.momx/2)+FixedMul(STEP, cos(s.angle)), s.y+(s.momy/2)+FixedMul(STEP, cos(s.angle)), true)
				P_TryMove(t, s.x+(s.momx/2)+FixedMul(STEP, cos(s.angle)), s.y+(s.momy/2)+FixedMul(STEP, cos(s.angle)), true)
			elseif not ATTACK.type=="groundslam"
				if (s.z+s.momz > t.z)
					if (s.z+s.momz < t.ceilingz)
						t.z = s.z+s.momz
					else
						t.z = t.ceilingz
					end
				elseif not P_IsObjectOnGround(t)
					if (s.z+s.momz > t.floorz)
						t.z = s.z+s.momz
					else
						t.z = t.floorz
					end
				end
			end
		end
		RBB.quickroll = max(RBB.quickroll  or 0, 17)
	else
		tp.viewrollangle = 0
		RBB.quickroll = nil
	end
	if BAG.GIBstretch --Have our enemy mimmick the bag.
		t.spritexscale,t.spriteyscale = min(2<<16, BAG.spritexscale),min(2<<16, BAG.spriteyscale)
		t.spritexoffset = 0
		t.spriteyoffset = min(0, max(-8<<16, BAG.spriteyoffset) )
		t.XSstretch = {timer = 3, Xspeed = BAG.GIBstretch.Xspeed or 5000, Yspeed = BAG.GIBstretch.Yspeed or 5000}
	end
	t.XS_DidSuperAura,t.XS_DidBlur = true,true
	RBB.BAG = BAG
	RBB.crossedarms = 0
	RBB.NoSlamDamage = 17 --We're using the Rush Assault internal calculator.
	RBB.rocketspin,RBB.rocketjump = 0,0
	RBB.noXYcontrol,RBB.noZcontrol = 9,2
	RBB.rollD = (BAG.RollDirection) and BAG.RollDirection or 1
	RBB.painstun = max(RBB.painstun or 0, 17)
	RBB.jump,RBB.custom1,RBB.custom2,RBB.custom3,RBB.spin = 99<<16,99<<16,99<<16,99<<16,99<<16
	xss.custom1,xss.custom2,xss.custom3,xss.spin,xss.jump = 99<<16,99<<16,99<<16,99<<16,99<<16
	RBB.countered,RBB.shocked = nil
	RBB.riseanimtimer,RBB.rocket,RBB.dualpress = 0,0,0	
	RBB.C1stun,RBB.C2stun,RBB.countered,RBB.CLresist,RBB.RBmissed,RBB.RingBurstChase,RBB.RBdiv = nil
	
	tp.cmd.forwardmove,tp.cmd.sidemove = 0,0
	xss.stopstick,xss.stopbutton,xss.direct = 2,2,25
	
	tp.powers[pw_nocontrol] = 3
	if not (splitscreen)
		RBB.blackbars = 3
	end
	return true --
end

--Tracks losses and victories for SST.
XSon.UpdateSSTWins = function(z, WIN, LOSS, RESET) if XS_SSTCHAD == nil return end --
	if (RESET)
		XS_SSTCHAD = {} return --Empty table!
	end	
	local MOSTWINS,BEST = 0,nil
	
	for i = 0,31
		local p = players[i] 
		if not (p and p.valid and p.xsonictable and p.xsonictable.supersonic and p.mo and p.mo.skin=="xsonic")
			XS_SSTCHAD[i] = nil continue --
		end
		if not (XS_SSTCHAD[i])
			XS_SSTCHAD[i] = {player = p, wins = 0, losses = 0, playernum = i}
		end
		if players[i] == z
			if WIN --You can lose wins if you forfeit a match.
				XS_SSTCHAD[i].wins = $+WIN
			end
			if LOSS
				XS_SSTCHAD[i].losses = $+LOSS
			end
		end
		local WINS = XS_SSTCHAD[i].wins or 0
		if WINS and (WINS > MOSTWINS)
			MOSTWINS,BEST = WINS, i --Update!
		end
	end
	if (BEST!=nil)
		XS_SSTCHAD[99] = min(31, max(0,BEST)) --Global best playernum
	end
	if MOSTWINS
		XS_SSTCHAD[98] = MOSTWINS --Global most wins.
		if (MOSTWINS > 4)
			local OLD,HADOLD = XS_SSTCHAD[97]
			if OLD
				HADOLD = OLD
				if not OLD.valid or not OLD.mo or OLD.mo.skin!="xsonic"
				or not OLD.xsonictable or not OLD.xsonictable.coopPvP or not OLD.xsonictable.supersonic
					XS_SSTCHAD[97] = nil
				end
			end	
			local NEW = XS_SSTCHAD[97]
			if not (NEW and NEW.valid and NEW.mo)
				if BEST!=nil and type(BEST)=="number" and players[BEST]
					XS_SSTCHAD[97] = players[BEST]
				elseif z and WIN
					XS_SSTCHAD[97] = z
				end
			end
			local P = XS_SSTCHAD[97]
			if P and P.valid and P.name and (P!=HADOLD)
				print("The 7 Chaos Emeralds now favor\x82 "+P.name+"\x80"+"!")
			end
		end
	end
end

--Spawns Emeralds for the Ultimate Fighter!
XSon.SSTchad = function(p,s,xs)
	if s.XS_gemtable == nil 
		s.XS_gemtable = {} 
		if not S_SoundPlaying(s, sfx_cgot) S_StartSoundAtVolume(s, sfx_cgot, 140) end
		local Zoff = -((183*8)<<16)
		for i = -4,5 
			local ghs = XSon.TransformGFX2(s, Zoff) 
			if ghs and ghs.valid	
				ghs.color,ghs.colorized = P_RandomRange(SKINCOLOR_SUPERSILVER3, SKINCOLOR_SUPERPURPLE5), true
				ghs.spritexscale = ($*(5-min(4,abs(i)))) *4/5
			end Zoff = $+(188<<16)
		end
	end 
	local GEMS = s.XS_gemtable
	
	GEMS[99] = GEMS[99] and $+1 or 1 --timer
	if GEMS[98] == nil GEMS[98] = s.scale end --scale
	local SCALE = max(s.scale, GEMS[98])
	if (GEMS[99] < 25)
		SCALE = $+((6*s.scale)/max(1, (GEMS[99]/2)))
	end
	
	local ANG = (leveltime*ANG2)
	local ZDIST = 30*s.scale
	local XYDIST = 123*SCALE
	local FLAGS2 = 0
	if (s.flags2 & MF2_DONTDRAW) or xs.ChaosLambence and ((xs.ChaosLambence.init or 0)>6)
		FLAGS2 = MF2_DONTDRAW
	elseif (xs.ChaosSnap and ((xs.ChaosSnap.init or 0) < 7))
	or xs.ChaosLambence and not ((xs.ChaosLambence.init or 0) > 6)
		FLAGS2 = MF2_SHADOW
	end
	local GLOWTRANS = (leveltime%3==0) and FF_TRANS60 or FF_TRANS70
	local XYADD = XSLocalRNG(1,4900,s)
	local ghs
	for i = 1,7
		local ghs = GEMS[i]
		if not (ghs and ghs.valid)
			ghs = P_SpawnMobjFromMobj(s, FixedMul(XYDIST, cos(ANG)), FixedMul(XYDIST, sin(ANG)), ZDIST, MT_XSZIPDASHTRAIL)
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.sprite = SPR_XSE9
			ghs.frame = (Q+i)
			GEMS[i] = ghs
		end
		P_MoveOrigin(ghs, s.x+FixedMul(XYDIST, cos(ANG)), s.y+FixedMul(XYDIST, sin(ANG)), s.z+ZDIST)
		ghs.momx,ghs.momy,ghs.momz = s.momx*2/3,s.momy*2/3,s.momz/4
		ghs.spritexscale,ghs.spriteyscale = s.spritexscale,s.spriteyscale
		ghs.spriteyoffset = s.spriteyoffset
		ghs.scale = s.scale
		ghs.tics,ghs.fuse = 3,3
		if (i%3==0) 
			if (GEMS[99] < 70) and (leveltime%4==0)
			or FLAGS2==MF2_SHADOW and not (ghs.flags2 & MF2_SHADOW)
				XSon.sparkles(p,ghs,xs, 199, 2, 3, 4<<16, XSLocalRNG(SKINCOLOR_SUPERSILVER5,SKINCOLOR_SUPERPURPLE5,ghs), AST_ADD)
			end
		end
		ghs.flags2 = ($ & ~(MF2_DONTDRAW|MF2_SHADOW))|FLAGS2
		if (s.eflags & MFE_VERTICALFLIP)
			ghs.eflags = $|MFE_VERTICALFLIP
		else
			ghs.eflags = $ & ~MFE_VERTICALFLIP
		end
		
		local GLOW = ghs.glow
		if not (GLOW and GLOW.valid)
			GLOW = P_SpawnMobjFromMobj(s, FixedMul(XYDIST, cos(ANG)), FixedMul(XYDIST, sin(ANG)), ZDIST, MT_XSZIPDASHTRAIL)
			GLOW.dispoffset = 2
			GLOW.blendmode = AST_ADD
			GLOW.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			GLOW.sprite = SPR_XSE9
			ghs.glow = GLOW
		end
		P_MoveOrigin(GLOW, ghs.x, ghs.y, ghs.z)
		GLOW.momx,GLOW.momy,GLOW.momz = ghs.momx,ghs.momy,ghs.momz
		GLOW.spritexscale,GLOW.spriteyscale = ghs.spritexscale+XYADD,ghs.spriteyscale+XYADD
		GLOW.spriteyoffset = ghs.spriteyoffset
		GLOW.frame = min(Z+6, (ghs.frame+8))|GLOWTRANS
		GLOW.scale = ghs.scale
		GLOW.tics,GLOW.fuse = ghs.tics,ghs.fuse
		GLOW.flags2 = ghs.flags2
		GLOW.eflags = ghs.eflags
		ANG = $+(ANG1*51)
	end
end