--#============================================================================================#
--Every function related to Rush Assaults, including the "sandbag" you punch around.
--#============================================================================================#
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

--Restore the target? Damage it? Do both?
local function RestoreTarget(s, RESTORE, DMG, so) if not (s and s.valid) return end --
	so = (so) or s.tracer
	local p = so and so.player local xs = p and p.xsonictable
	S_StopSoundByID(s, sfx_s3kcds)
	if p
		if not p.XSscalecam p.XSscalecam = 2 end
		local DARKNESS = s.BAGdamage
		if type(DARKNESS) == "number"
			XSon.AddDarkness(so, DARKNESS+FRACUNIT)
		elseif type(DMG) == "number"
			XSon.AddDarkness(so, max(1<<16, DMG)+FRACUNIT )		
		else
			XSon.AddDarkness(so, 2<<16)	
		end
		S_StopSoundByID(so, sfx_xssfp1)
	end

	local t = s.target
	if t and t.valid
		if RESTORE
			if not t.player
				P_SetOrigin(t, s.x, s.y, s.z)
				if s.FLAGS != nil  --Restore everything.
					t.sprite,t.frame = s.SPRITE,s.FRAME
					t.flags,t.flags2 = s.FLAGS,s.FLAGS2
					t.scale = s.SCALE
					t.spritexscale,t.spriteyscale = s.SPRITEXSCALE,s.SPRITEYSCALE
					t.spritexoffset,t.spriteyoffset = s.SPRITEXOFFSET,s.SPRITEYOFFSET	
					if t.target and t.target.valid and not t.target.player
						t.target.flags2 = $ & ~MF2_DONTDRAW
					end	
					if t.tracer and t.tracer.valid and not t.tracer.player
						t.tracer.flags2 = $ & ~MF2_DONTDRAW
					end
				end	
				t.angle = s.angle
				t.rollangle = 0
			else
				t.player.XSscalecam = 2
				if t.player.xsonictable
					t.rollangle = 0
					t.player.viewrollangle = 0
					t.player.xsonictable.alteredrollangle = true
				end
			end
			if s.BAGhit != nil
				t.momx,t.momy,t.momz = s.momx,s.momy,s.momz
			end
		end
		if DMG
			if DMG == true --auto determine?
				if s.BAGdamage
					DMG = max(1, FixedInt(s.BAGdamage))
				elseif so and so.XSassault and so.XSassault.damage
					DMG = so.XSassault.damage
				else
					local HITS = s.BAGhit or 1
					DMG = max(1, HITS/4)
				end
				if t.health == 2 and (DMG < 2) and not (t.flags & MF_BOSS)
					DMG = 2 --That'll do.
				end	
			end
			if type(DMG)=="number"
				if t.health 
					if (t.flags & MF_SHOOTABLE)
					or (t.flags2 & MF2_FRET) and (t.flags & (MF_ENEMY|MF_BOSS)) --Fine, force it, then.
						if not t.player
							for d = -1,1,2
								for i = 1,8
									local ghs = P_SpawnMobjFromMobj(t,t.momx,t.momy,t.momz+(5*d)<<16,MT_SPINDUST)
									ghs.scale = $*2
									ghs.state = $+1
									P_Thrust(ghs, t.angle+(ANGLE_45*i)+(ANG10*d), 36<<16)
									ghs.momz = t.momz+(6*d)<<16
								end
							end
							if (DMG > 1) and (t.health > 1)
								t.health = max($-(DMG-1),1)
							end
						else
							local tp = t.player local xss = tp.xsonictable local RBB = xss and xss.FreeFlight
							if RBB
								if so
									P_Thrust(so, so.angle, -50*s.scale)
									P_Thrust(t, so.angle, 30*s.scale)
									t.momz = $/2
									if xs and xs.FreeFlight
										xs.FreeFlight.PvPENERGY = 0
									end
								end
								if RBB.PvPHEALTH
									local PvPDMG = DMG*162
									if (PvPDMG < 425) 
										PvPDMG = min(425, $*2) --C'mon, you can do better than THAT...
									elseif (PvPDMG >= 1175) 
										if (PvPDMG < 1670) --Gradually diminishing returns.
											PvPDMG = max(1175, $*4/5)
										else
											PvPDMG = max(1670, $*3/4)
										end
									end
									XSon.GetHit(tp,t,xss, RBB)
									RBB.PvPENERGY = max(0, min(10000, $+max(100, (PvPDMG/3)) ))
									XSon.SubtractPvPHealth(min(2400, PvPDMG), RBB, (xs) and xs.FreeFlight)
								end
							end
							return --
						end
						t.gotXSassaulted = true
						t.flags2 = $ & ~MF2_FRET
						if (t.health > 1) or t.player or t.facc
							if t.facc and t.facc.health
								t.facc.health = max($-DMG, 3)
							end
							P_DamageMobj(t, s, so, 1)
						else
							P_DamageMobj(t, s, so, DMG_INSTAKILL)
						end
						if t and t.valid
							t.gotXSassaulted = nil
							if not t.player					
								if not t.health
									if not (t.flags & MF_BOSS)
										t.flags2 = $|MF2_DONTDRAW
										t.spriteyscale,t.spritexscale = 1,1
										S_StopSound(t)
									end
									if xs and xs.darkphase and (DMG>8) and not (s.eflags & MFE_UNDERWATER)
										local NAME = p.name or ""
										if not ( (string.find(NAME, "urn")) or (string.find(NAME, "Shine")) )
											local ODDS = max(14, (so.XS_DidStupidLine or 0))
											if (P_RandomRange(0,ODDS)==7)
												so.XS_DidStupidLine = so.XS_DidStupidLine and $+18 or 18
												XSVoice(p, so, xs, sfx_xsvocv, (xs.supersonic) and "ForceEcho" or nil)
											end
										end
									end									
								elseif s.TAXIX != nil and (t.flags & MF_BOSS)
								and (t.type != MT_EGGMOBILE) and (t.type != MT_CYBRAKDEMON) and (t.type != MT_FANG)
									local TAXI = P_SpawnMobjFromMobj(t, 0,0,0, MT_XSTRACKER) --They're alive! Bring them back home, taxi tracker!
									TAXI.EggmanNeedsATaxi = 105					
									TAXI.target,TAXI.tracer = t,t
									TAXI.TAXIX, TAXI.TAXIY, TAXI.TAXIZ = s.TAXIX,s.TAXIY,s.TAXIZ
								end
							end
						end
					end
				end
			end
		end
	end
	if s and s.valid
		s.STARTEDFLUNG = true
		s.fuse = max(s.fuse or 0, 18)
	end
end

--So is the player, s is the bag.
local function CrudeTryMove(so,s, ALLOWTP)
	local X,Y,Z = so.x, so.y, so.z
	local SX,SY = X,Y
	local DONE = false
	local TANG = R_PointToAngle2(s.x, s.y, X, Y)
	local RAD = max(24*so.scale, s.radius)
	for i = 1,2
		if i==1
			if (P_TryMove(so, s.x, s.y, true))
				SX,SY = s.x, s.y
				DONE = true
			end
		end
		if (s.z > so.z)
			if (so.ceilingz > s.z)
				so.z = s.z
			else
				so.z = so.ceilingz
			end
		else
			if (so.floorz < s.z)
				so.z = s.z
			else
				so.z = so.floorz
			end				
		end
		if DONE != true
			if (P_TryMove(so, s.x, s.y, true)) 
				P_TryMove(so, s.x-FixedMul(RAD, cos(TANG)), s.y-FixedMul(RAD, sin(TANG)), true)
				return true
			else
				P_SetOrigin(so, X,Y,Z) DONE = false --Do it again!
			end
		else
			P_TryMove(so, s.x-FixedMul(RAD, cos(TANG)), s.y-FixedMul(RAD, sin(TANG)), true)
			return true --
		end
	end
	if ALLOWTP == true and P_CheckPosition(so, s.x, s.y, s.z) --Only TPs as a last resort.
		P_MoveOrigin(so, s.x, s.y, s.z)
	end
end

--"Pyra, I don't have time for your--!!" ...used for lunging and attack animations. 
local function ImBusyRightNow(p,s,xs, DIST)
	if P_IsObjectOnGround(s) --Never be grounded.
		local ADD = s.scale/2*P_MobjFlip(s) 
		s.z,s.momz = $+ADD,($*P_MobjFlip(s)<1) and ADD or $
	end
	if xs.zipdash or xs.zipdashslide
		XSZipDashCancel(p, s, xs) 
	end
	if (netgame==true) and xs.darkphase and xs.darkphase != 3
		local FLASHING = p.powers[pw_flashing] or 0
		if (FLASHING>=0) and (FLASHING < 3)
			p.powers[pw_flashing] = max($, 2)
			s.flags2 = $ & ~MF2_DONTDRAW
		end
	end
	xs.didjumpboost = true
	xs.squat,xs.skydive,xs.jumpboost,xs.falltransition,xs.framejump,xs.poutysonic,xs.justleaped,xs.leapaftermath = 0,0,0,0,0,0,0,0
	xs.ledgeprotectcd,xs.edgeanimcd,xs.ringburstcd,xs.dustcd,xs.rollcd = 6,6,6,6,6
	p.pflags = $|PF_THOKKED|PF_USEDOWN|PF_JUMPDOWN & ~(PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTJUMP|PF_CANCARRY)
	s.eflags = $ & ~(MFE_JUSTHITFLOOR|MFE_APPLYPMOMZ)
	p.charflags = $|SF_NOSKID
	p.runspeed = 7777<<16 --Just to stop waterrunning without altering charflags.
	p.powers[pw_ignorelatch] = 32769
	p.powers[pw_justlaunched] = 0
end

--Rapidly home into a snappoint rather than the target directly.
local function AssaultHoming(s, t, SPEED, SNAPDIST, DIST, LUNGE)
	if not (t and t.valid)
		s.momx,s.momy,s.momz = 0,0,0 return --
	end
	local p = s.player
	local TZ = t.z+ max(s.scale*8, t.height)/4
	DIST = (DIST) or FixedHypot(FixedHypot(t.x-s.x, t.y-s.y),TZ-s.z)
	
	local TWOD = ((s.flags2 & MF2_TWOD) or twodlevel)
	local MULT = max(1,DIST)
	local TOANGLE = R_PointToAngle2(s.x, s.y, t.x, t.y)
	SNAPDIST = (SNAPDIST) and -abs(SNAPDIST) or -max(24*s.scale, t.radius)
	
	local MX,MY = t.x+FixedMul(SNAPDIST, cos(TOANGLE)), t.y+FixedMul(SNAPDIST, sin(TOANGLE))
	
	if SPEED == nil
		SPEED = s.player and s.player.speed or abs(s.momx)+abs(s.momy)
		SPEED = min(222<<16, max(99<<16, $-9*s.scale))
	end
	
	s.momx = FixedMul(FixedDiv(MX-s.x, MULT), SPEED) --Home in FAST.
	if (TWOD != true)
		s.momy = FixedMul(FixedDiv(MY-s.y, MULT), SPEED)
	end
	if LUNGE
		if not (p.xsonictable and p.xsonictable.forceangle)
			p.drawangle = TOANGLE
		end
		s.momz = FixedMul(FixedDiv(TZ-s.z, MULT), SPEED)
	else
		local JUMP = (p.cmd.buttons&BT_JUMP)
		local SPIN = ((p.cmd.buttons & BT_SPIN) or (s.XSassault and s.XSassault.sprite2==SPR2_TAL2))
		if (s.eflags & MFE_VERTICALFLIP)
			JUMP,SPIN = SPIN,JUMP --Invert.
		end
		if (t.z > s.z+s.height/2)
			s.momz = (SPIN and not (JUMP)) and $/3 or FixedMul(FixedDiv(TZ-s.z, MULT), SPEED)
		elseif (t.z < s.z)
			s.momz = (JUMP) and $/3 or FixedMul(FixedDiv(TZ-s.z, MULT), SPEED)
		end
	end
end

--WHAT HAVE YOU DONE TO THEM?!!?
XSon.DSSlunge = function(p,s,xs, RB, SNAPDIST)
	local t = s.XSAssaultT
	
	--Cancel conditions.
	if not (t and t.valid and t.health) 
		if s.XSlunging or P_IsObjectOnGround(s)
			s.momx,s.momy = $/4,$/4
		end
		s.XSAssaultT,s.XSlunging = nil return --Cancel 0	
	elseif (p.pflags & (PF_SLIDING|PF_FULLSTASIS|PF_STASIS))
	or p.powers[pw_nocontrol] or not xs.darkphase
		if s.XSlunging or P_IsObjectOnGround(s)
			s.momx,s.momy,s.momz = $/6,$/6,$/4
		end
		s.XSAssaultT,s.XSlunging = nil return --Cancel 1
	elseif p.powers[pw_carry] or (s.eflags & (MFE_SPRUNG|MFE_GOOWATER)) or (p.panim == PA_PAIN) or xs.ringburst or p.xswallrun
		s.momx,s.momy = $/3,$/3
		s.XSAssaultT,s.XSlunging = nil return --Cancel 2
	end
	
	local TZ = t.z+ max(s.scale*8, t.height)/4
	local DIST = FixedHypot(FixedHypot(t.x-(s.x+s.momx), t.y-(s.y+s.momy) ),TZ-(s.z+s.momz))
	if (DIST > 4000<<16) -- Wha? We're way too far away
		s.momx,s.momy,s.momz = $/6,$/6,$/4 s.XSAssaultT,s.XSlunging = nil return
	end
	
	--Note that s.XS_DarkTable is not necessarily present!
	if RB
		RB.forcetrail,RB.speedlines,RB.windlines = 0,0,0
		if RB.darkrush and (p.cmd.buttons & (BT_JUMP|BT_SPIN|BT_CUSTOM1))
			RB.darkrush = min(15, max(9,$+3))
		end
		if (p.cmd.buttons & BT_JUMP) and not (p.cmd.buttons & BT_SPIN)
			RB.jumpDR = 9
		end
	end
	

	SNAPDIST = (SNAPDIST) or 128*s.scale --How close you must be before Sonic transitions into an attack	
	ImBusyRightNow(p,s,xs, DIST)
	s.XSresist = 5
	
	if (DIST > SNAPDIST+(p.speed/3)) --You can't run from me!
		
		if s.state != S_PLAY_XZIPLEAP
			s.state = S_PLAY_XZIPLEAP
		end
		--Animation.		
		s.sprite2 = ((xs.darkphase or xs.moody) and SPR2_TAL0 or SPR2_RUN) | (xs.supersonic and FF_SPR2SUPER or 0)
		
		local FRAME = (not (xs.darkphase or xs.supersonic)) and C or (leveltime%2) and B or A
		s.frame = ($ & ~(FF_FRAMEMASK|FF_HORIZONTALFLIP)) | min(B,FRAME)
		s.anim_duration,s.tics = 4,4
		
		xs.spin,xs.custom1,xs.custom3 = 999<<16,999<<16,999<<16
		if not s.XSlunging
			S_StopSoundByID(s, sfx_s238)
		end
		s.XSlunging = 2 --We're lunging!
		AssaultHoming(s, t, max(99<<16, p.speed-9*s.scale), 8*s.scale, DIST, "lunge")
		p.camerascale = (p.pflags & PF_ANALOGMODE) and 28000 or 40000
		p.XSscalecam = 5		
		
		if RB
			RB.noviolence = 0
			RB.darkrushhover = 99<<16
			if s.XSlunging and (p.speed > 8*s.scale) and not (DIST and (DIST < 360*s.scale))
				RB.forcetrail,RB.speedlines,RB.windlines = 1,1,1
			end
		end		
		
	else --Time to SMACK a bitch!
		if (t.type == MT_EGGSHIELD) 
			local TARGET = t.target
			if TARGET and TARGET.valid and (TARGET.type == MT_EGGGUARD) and TARGET.health
				TARGET.flags = $|MF_ENEMY|MF_SHOOTABLE
				XSon.ScenicCrash(p,s,xs,t, 3, "kill")
				t = TARGET
			end	
		end
		
		if RB
			RB.noviolence = 0
			if RB.flashing
				s.flags2 = $ & ~MF2_DONTDRAW
			end
			RB.rushcount,RB.darkrush,RB.flashing,RB.windlines,RB.speedlines = 0,0,0,1,1
		end
		local BAG = XSon.SandBagShift(BAG, t, s)
		if BAG and BAG.valid
			p.camerascale = (p.pflags & PF_ANALOGMODE) and 40000 or 53000
			p.XSscalecam = 5
			
			BAG.tracer = s
			s.XSresist = 7
			XSon.SandBag(BAG, "attack", nil)
			if s.XSassault --Snap!
				S_StopSoundByID(s, sfx_xsvocv)
				xs.jump,xs.spin,xs.custom1,xs.custom3 = 999<<16,999<<16,999<<16,999<<16
			/*	if t and t.valid
					local DIST = 160*s.scale
					local TWOD,TOANGLE = ((s.flags2 & MF2_TWOD) or twodlevel), R_PointToAngle2(s.x-s.momx, s.y-s.momy, t.x, t.y)
					local X,Y = FixedMul(DIST, cos(TOANGLE)), FixedMul(DIST, sin(TOANGLE))
					for i = 1,4
						print("\x80 ========== ATTEMPT TRYMOVE"+i+"===========================")
						if P_TryMove(s, t.x+(X/i), ((TWOD==true) and s.x or t.y+(Y/i)), true) 
							break --
						end
					end 
				end  */
				XSon.AssaultAnim(p,s,xs)
				s.XSresist = 9
			end
		end
		if (netgame!=true)
			P_InstaThrust(s, s.angle, -8*s.scale) 
			P_SetObjectMomZ(s, FRACUNIT)
		end
	end
end

--Play a sound that doesn't care if the original object continues to exists or not. SFX and s must be valid. 
XSon.GibSound = function(s, SFX, RVOLUME, TIME)
	TIME = (TIME) or 199
	RVOLUME = (RVOLUME) or 255
	local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_THOK)
	ghs.flags = MF_NOBLOCKMAP|MF_NOSECTOR|MF_SCENERY|MF_NOCLIPTHING|MF_NOCLIP|MF_NOCLIPHEIGHT
	ghs.flags2 = MF2_DONTDRAW
	ghs.fuse,ghs.tics = TIME,TIME
	S_StartSoundAtVolume(ghs, SFX, RVOLUME) 
	return ghs --
end

--Generic stretch function
XSon.GIBstretch = function(s)
	local yu = s.GIBstretch
	
	if not yu.timer or s.spritexscale == FRACUNIT and s.spriteyscale == FRACUNIT
		s.spritexscale,s.spriteyscale = FRACUNIT,FRACUNIT s.GIBstretch = nil return false --Done!
	end
	yu.timer = max($-1,0)
	
	local XGOAL,YGOAL = yu.XGOAL or FRACUNIT, yu.YGOAL or FRACUNIT --Got a goal, or returning to normal?
	local XPLUS = (yu.XPLUS) or (s.spritexscale > FRACUNIT) and -1 or 1 --Do this manually or automatically?
	local YPLUS = (yu.YPLUS) or (s.spriteyscale > FRACUNIT) and -1 or 1 
	local XSPEED,YSPEED = yu.Xspeed or 5000, yu.Yspeed or 5000 --Got a speed in mind?

	s.spritexscale = (XPLUS<0) and max($-XSPEED, FRACUNIT) or min($+XSPEED, FRACUNIT)
	s.spriteyscale = (XPLUS<0) and max($-YSPEED, FRACUNIT) or min($+YSPEED, FRACUNIT)
end

--Modify the target and mimmick some properties. Can be called initially too.
--s is the bag. t is the enemy. so is the player. (and optional)
XSon.SandBagShift = function(s, t, so) 
	if not (t and t.valid) 
		if s and s.valid and s.BAGhit == nil
			P_RemoveMobj(s)
		end return --Must have a valid target!
	end 
	
	if not (s and s.valid) --If a so has been set, we can spawn the bag if it doesn't exist yet.
		if not (so and so.valid) return end --
		s = P_SpawnMobjFromMobj(t, 0,0,0, MT_XSSANDBAG)
		s.target = t 
		s.tracer = so
		s.scale = t.scale
		s.radius = t.radius
		s.height = t.height
		if (FixedHypot(FixedHypot(s.x-so.x, s.y-so.y), s.z-(so.z+so.height/3))  > 200*so.scale) --Don't you dare...
			P_SetOrigin(s, so.x, so.y, so.z+(so.height/2))
			P_TryMove(s, t.x, t.y)
		end
	end
	local ZFLIP = P_MobjFlip(s)
	local SOFLIP = ZFLIP
	
	if not (s.tracer and s.tracer.valid) --Set up a tracer? Careful to only call this at the beginning.
		if so and so.valid
			s.tracer = so so.XSbag = s
		end
	elseif s.BAGhit
		if not so and s.tracer
			so = s.tracer
		end
	end
	if s.CRASHcanDMG
		s.CRASHcanDMG = $-1
	end
	if s.GotSlammed
		s.GotSlammed = $-1
	end
	if s.XS_BreakThroughItAll
		s.XS_BreakThroughItAll = $-1
	end
	
	local IMPACT = false
	local SST = (so and so.player and (so.player.powers[pw_super])) and 3 or 0
	local PHASE = max(SST, 1)
	
	local HITS = (s.BAGhit) or 1
	local STYLE = "attack"
	local GROUNDED = 0
	
	--Initial setup.
	if s.BAGhit == nil
	
		IMPACT = true
		
		s.Init = 2 --For swapping to the flung thinker later.
		s.FLAMEDEBRIS,s.METALDEBRIS,s.ELECTRICDEBRIS = true,true,true
		s.SMOKINGDEBRIS,s.debrisLVL = 1,1
		s.XSrgtimer = 55
		s.RollDirection = (XSon.RNG(3,4,s) == 4) and 1 or -1 
		P_TryMove(s, t.x, t.y, true)
		if so and (abs(so.z-t.z) > 200*s.scale)
			s.z = so.z+so.height --That's not right...!
		else
			s.z = t.z
		end
		
		if not t.player
			P_TryMove(s, t.x, t.y, true)
			if so and (abs(so.z-t.z) > 200*s.scale)
				s.z = so.z+so.height --That's not right...!
			else
				s.z = t.z
			end		
			--Copy their shit and remember it. Either to mimmick it or restore it later.
			s.TAXIX,s.TAXIY,s.TAXIZ = t.x,t.y,t.z
			
			s.SPRITE,s.FRAME = t.sprite,t.frame
			s.FLAGS,s.FLAGS2 = t.flags,t.flags2
			s.SCALE = t.scale
			s.SPRITEXSCALE,s.SPRITEYSCALE = t.spritexscale,t.spriteyscale
			s.SPRITEXOFFSET,s.SPRITEYOFFSET = t.spritexoffset,t.spriteyoffset
			
			--Mimmick them.
			if ((t.height+t.radius) > 80<<16)
				s.BIGTARGET = (t.height+t.radius)-80<<16
			end	
			s.renderflags,s.blendmode = t.renderflags,t.blendmode
			s.shadowscale = t.shadowscale
			s.roll,s.pitch,s.rollangle = t.roll,t.pitch,t.rollangle
			if t.skin
				s.skin = t.skin
				s.sprite = t.sprite
				s.sprite2 = t.sprite2
				s.frame = t.frame --set early just incase
			end
			s.spritexoffset,s.spriteyoffset = t.spritexoffset,t.spritexoffset 
			s.spritexscale,s.spriteyscale = t.spritexscale,t.spriteyscale 
			s.mirrored = t.mirrored
			s.flags2 = t.flags2 & ~(MF2_DONTDRAW|MF2_SHIELD|MF2_SKULLFLY)
			s.rollangle = t.rollangle
			s.drawonlyforplayer = t.drawonlyforplayer
			s.dontdrawforviewmobj = t.dontdrawforviewmobj
		end	
		
		--Adjust some stuff for the player?
		so = s.tracer
		if so and so.valid
			local p = so.player local xs = p and p.xsonictable 
			if xs 
				if so.XSassault and so.XSassault.type
					STYLE = so.XSassault.type
					if so.XSassault.grounded
						GROUNDED = so.XSassault.grounded
					end
				end
				if xs.darkphase
					PHASE = xs.darkphase
					P_ResetScore(p)
				elseif xs.FreeFlight
					PHASE = 3
					xs.FreeFlight.BAG = s
				end
			end
			
			s.tracer = so so.XSbag = s
			s.OLDMOMX,s.OLDMOMY,s.OLDMOMZ = so.momx,so.momy,so.momz --Remember old momentum values.
			
			s.momx,s.momy,s.momz = so.momx/2,so.momy/2,so.momz/2 --Now shift to the player.
			
			local DIST = max(64*so.scale, so.radius*3)
			local ANGLE = s.angle
			P_TryMove(s, s.momx+so.x+FixedMul(DIST, cos(ANGLE)), s.momy+so.y+FixedMul(DIST, sin(ANGLE)), true)
			s.z = s.momz+(so.z+so.height/8)
		--	P_MoveOrigin(s, s.momx+so.x+FixedMul(DIST, cos(ANGLE)), s.momy+so.y+FixedMul(DIST, sin(ANGLE)), s.momz+so.z+so.height/8)
			
			XSon.Sound(so, sfx_xssfpq, 190)
			XSon.Flash(so, PAL_NUKE, (PHASE==1) and 2 or 3, 340*so.scale*PHASE)
			s.angle = (p and p.drawangle or s.angle)+ANGLE_180
			
			--Do a grunt on the first attack!
			local TABLE = {sfx_xsvop5,sfx_xsvop6,sfx_xsvop8,sfx_xsvop9,sfx_xsvopm,sfx_xsvopa,sfx_xsvopn}
			if (PHASE >= 2) and not (SST)
				table.insert(TABLE, sfx_xsvoc8)
				if PHASE == 3
					table.insert(TABLE, sfx_xsvoch)
				end
			end
			local NUM = P_RandomRange(1, #TABLE)
			local GRUNT = TABLE[NUM]
			if GRUNT == so.XSlastassaultgrunt
				GRUNT = ($==TABLE[#TABLE]) and TABLE[1] or TABLE[NUM+1]
			end
			XSVoice(p, so, xs, GRUNT, true) --Different shouts for Super Sonic.
			so.XSlastassaultgrunt = GRUNT
			if s.XSecho and (s.XSecho.sfx == GRUNT)
				if PHASE == 3
					s.XSecho.timer = $+4
					s.XSecho.rvolume = $+30
				end
				s.XSecho2 = s.XSecho --Move it to second echo slot.
				s.XSecho = nil
			end
		end
		
		--Generic Properties.
		s.radius = max($, t.radius)
		s.height = max($, t.height)
		s.ROLLSPEED = (t.player) and ANGLE_45 or ANGLE_22h
		s.ROLLD = (P_RandomRange(6,7)==6) and 1 or -1	
		s.BAGhit = 1 --Amount of times we've hit the bag.
		XSon.AddDamage(s, (PHASE==3) and 99666 or FRACUNIT) --Updates s.BAGdamage with the amount of damage we've dealt!
		s.BAGexists = 1
		s.JustGotHit = 11		
		s.MOMANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
		s.SPEED,s.MOMZ = abs(s.momx)+abs(s.momy), abs(s.momz)
		s.SFXcanplay = true
		
		if t.player
			s.flags = $ & ~(MF_NOCLIPHEIGHT|MF_NOCLIP)
		end
		
	else --Once active....
		local p = so and so.player local xs = p and p.xsonictable 
		if xs
			SOFLIP = P_MobjFlip(so)
			if so.XSassault and so.XSassault.type
				STYLE = so.XSassault.type
				if so.XSassault.grounded
					GROUNDED = so.XSassault.grounded
				end
				local ATIMER = so.XSassault.timer or 0
				if STYLE == "groundslam"
					if t.player and not P_IsObjectOnGround(t)
						t.z = so.z
					end
				elseif STYLE == "attack" and (ATIMER>1)
				and not ((s.BAGcutcombo or so.XSassault.cantcharge) and (ATIMER>7))
					local TANGLE = R_PointToAngle2(so.x, so.y, s.x+s.momx, s.y+s.momy)
					local DIST = FixedHypot(so.x-s.x, so.y-s.y)
					if (DIST < 480*so.scale) --We're too close!
						if (p.speed > (abs(s.momx)+abs(s.momy))) --Repel the player magnetically so they don't fly past us.
							local PUSH = (DIST/100) //(p.speed<52*so.scale) and so.scale*3 or (p.speed<90*so.scale) and so.scale*2 or (p.speed < 150*so.scale) and so.scale or so.scale/2
							if (netgame!=true) --Desyncs...
								if (p.cmd.forwardmove > 0) and (p.cmd.forwardmove+5 >= abs(p.cmd.sidemove))
									so.momx,so.momy = $*5/6,$*5/6
									PUSH = $*2
									local MOMANG = R_PointToAngle(so.x, so.y, so.x+so.momx, so.y+s.momy)
									P_Thrust(so, MOMANG, PUSH)
									P_TryMove(so, so.x+FixedMul(7*so.scale, cos(MOMANG)), so.y+FixedMul(7*so.scale, sin(MOMANG)), true)
								else
									P_Thrust(so, TANGLE, -PUSH)
									P_TryMove(so, so.x+FixedMul(-4*so.scale, cos(TANGLE)), so.y+FixedMul(-4*so.scale, sin(TANGLE)), true)		
								end
							end
						end
					end
					if (DIST > 140*s.scale) and (ATIMER>11) --Far away? Keep camera on foe!
					and not (abs(p.cmd.sidemove)>27) and not (p.cmd.forwardmove < -20) and not so.XSassault.combolock 
						local ANGLE,PLUS = XS_AngleDifference(TANGLE, p.cmd.angleturn<<16, 2)	
						if (ANGLE >= ANG10)
							local TURNSPEED = min(75, ANGLE/ANG1)*3*PLUS
							if not p.cmd.sidemove and not p.cmd.forwardmove
								TURNSPEED = $*5/2
							end
							if (ANGLE < ANG20)
								TURNSPEED = $/2
							end
							p.cmd.angleturn = $+TURNSPEED
							so.angle = p.cmd.angleturn<<16 --First angleturn, then so.angle!
						end				
					end
				end
			end
			if xs.darkphase
				PHASE = xs.darkphase
				P_ResetScore(p)
			end
			
			if s.GotHyperSmashed
				local MOMANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
				local H = s.GotHyperSmashed
				H.init = $+1
				local INIT = H.init
				if (INIT < 25) and H.timer and not (H.quake == true)
					local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
					local OLDANG,ghs = s.angle			
					if INIT == 1 or INIT == 4
						s.SFXcanplay = true
						XSon.BurstWave(s, 1, DARKAURA, AST_ADD, nil, max(1<<16, 3<<16-(INIT*5000)), MOMANG)
						
						local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK) 
						ghs.angle = MOMANG+ANGLE_90
						ghs.scale = so.scale
						P_InstaThrust(ghs, MOMANG, max(abs(so.momx)+abs(so.momy), 32<<16)+8<<16 )
						ghs.spritexscale,ghs.spriteyscale = 14500,14500
						ghs.sprite = SPR_XSG1
						ghs.frame = F|((INIT==1) and FF_TRANS30 or FF_TRANS40)
						ghs.fuse,ghs.tics = 20,26
						ghs.destscale = 99<<16
						ghs.scalespeed = $*3
						ghs.renderflags,ghs.blendmode = RF_PAPERSPRITE|RF_SEMIBRIGHT, AST_ADD
						if DARKAURA
							ghs.color,ghs.colorized = SKINCOLOR_CRIMSON,true
						end							
					elseif (INIT < 10) or (INIT % 2 == 0)
						ghs = XSon.ChargePower(s, 55-(INIT*2))
						ghs.fuse,ghs.tics,ghs.tracer = 16,16,nil
						ghs.sttfade = 11
						ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
					end
					if so.XSassault
						xs.forceangle = {angle = MOMANG, timer = 7, nosangle = true}
						if (INIT < 20)
							so.momx,so.momy,so.momz = $/2,$/2,$/3
							P_Thrust(so, MOMANG, -so.scale)
							P_SetObjectMomZ(so, 1<<16, true)
						end
					end						
					if (INIT < 3)
						if ( abs(s.momx)+abs(s.momy) < H.speed- 3<<16 )
							P_InstaThrust(s, MOMANG, H.speed)
						end
					end
				end
				if H.timer
					H.timer = $-1
				end
			end				
		end
		if s.momx or s.momy
			if not (s.GotHyperSmashed and not s.GotHyperSmashed.quake)
				s.momx = FixedDiv(FixedMul($, 121), 122)
				s.momy = FixedDiv(FixedMul($, 121), 122)
			end
			s.SPEED = abs(s.momx)+abs(s.momy)
		else
			s.SPEED = 0
		end
		if s.JustGotHit == 10 --Refresh!
			IMPACT = true
		end
		s.MOMZ = abs(s.momz)
		
		local H = s.GotHyperSmashed
		
		if (s.JustGotHit < 10) and (leveltime%2==0)
		or H and not (H.timer and H.quake == true)
			local BOUNCE
			if P_IsObjectOnGround(s) --Groundbounce!
				P_SetObjectMomZ(s, max(8<<16, abs(s.momz)), true) 
				BOUNCE = 2
			elseif not s.EarthSlammed
			and not (H and H.timer and (H.zgain < 0) and not H.quake) --Wallbounce!
				local NEWMOMANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
				local ANGDIFF,PLUS = XS_AngleDifference(s.MOMANG, NEWMOMANG, 2, ANGLE_90)
				if ANGDIFF and (ANGDIFF > ANG30)
					s.GFXANG = s.MOMANG-(ANGDIFF/2*PLUS)
					s.MOMANG = NEWMOMANG
					BOUNCE = 1
				end
			end
			if BOUNCE
				local CRASH,ghs = (s.MOMZ+s.SPEED)*6
				if CRASH
					if s.SFXcanplay
						local ANG = (s.GFXANG!=nil) and s.GFXANG or s.MOMANG
						ghs = XS_CircleWave(s,BOUNCE, (HITS%2) and SKINCOLOR_GREY or SKINCOLOR_CRIMSON, (BOUNCE==1) and 13 or 18, nil, ANG)
						ghs.sprite = SPR_XSG2
						ghs.scalespeed = FRACUNIT/3
						ghs.frame = (HITS%2) and 29|FF_TRANS50 or 30|FF_TRANS40 --XSG230 or XSG1 49 ("n") ?
						ghs.sttfade = (BOUNCE!=1) and 18 or true
						ghs.tracer,ghs.ditchtracer = nil,nil 
						
						if BOUNCE != 1
							ghs.frame = $-2<<16
							ghs.z = $+(s.height/2*ZFLIP)
							ghs.momz = $+(2<<16*ZFLIP)
							
							--Meteor Smash creating a dark quake!
							if H and H.timer and not H.quake
								local QCOLOR = nil
								if p and xs and xs.FreeFlight
									QCOLOR = SKINCOLOR_GOLDENROD
								end
								local BOOST = (H.boost==15) and PHASE or max(1,PHASE-1)
								s.GotHyperSmashed.quake = true
								if H.boost == 15
									XSon.AddDamage(s, 2500+(14000*BOOST))
								--	XSon.EarthQuake(s, NO, DIST, SPEED, CSCALE, SCALESPEED, ZANGLE, FRAME, FUSE, COLOR, BLEND)
									XSon.EarthQuake(s, 5+BOOST, 180*s.scale, 1, 6000+(BOOST*26000), FRACUNIT/(7-BOOST), ANG30, 32|(FF_TRANS60-(BOOST<<16)), 4+(BOOST*3), QCOLOR or SKINCOLOR_RUBY)
									XSon.EarthQuake(s, 3+BOOST, 350*s.scale, 1, 30000+(BOOST*26000), FRACUNIT/(4-BOOST), ANG10, 32|(FF_TRANS90-BOOST<<16), 3+(BOOST*2),
									SKINCOLOR_XSSUPERBLUR, (QCOLOR) and AST_ADD or AST_REVERSESUBTRACT)
									
								else --Generic imperfect smash.
									XSon.AddDamage(s, 750+(5000*BOOST))
									XSon.EarthQuake(s, 7, 140*s.scale, 1, FRACUNIT, FRACUNIT/7, ANGLE_22h, 32|FF_TRANS40, 9, QCOLOR)
								end
								
								s.momx,s.momy = $/5,$/5 --Bounce basically straight up from this.
								if (abs(s.momz) < 120*so.scale)
									if (s.momz*SOFLIP >= 0)
										s.momz = $+6*so.scale
									else
										s.momz = $-12*so.scale
									end
								end
								
								s.JustGotHit = 9
								XSon.Quake(s, (40*BOOST)<<16, 2+(PHASE*3), (990*PHASE)<<16)
								
								s.spriteyoffset = 18<<16
								s.spritexscale = FRACUNIT+155000 --Crazy rebound squash
								s.spriteyscale = FRACUNIT-38000
								s.GIBstretch = {timer = 16, Xspeed = 8000, Yspeed = 4400}

								if p
									if H.boost == 15
									
										local RB = so.XS_DarkTable
										local NUKEDIST = (15+(230*PHASE))*so.scale
										local XYDIST = NUKEDIST*2/3
										local ZDIST = XYDIST/2
										
										searchBlockmap("objects",function(s,m) --Destroy nearby environmental objects.
											if m.valid and (m.flags & (MF_MONITOR|MF_SCENERY|MF_SOLID|MF_PUSHABLE))
											and not (m.flags & (MF_ENEMY|MF_SPRING|MF_BOSS|MF_NOSECTOR|MF_NOCLIPTHING))
											and not m.WaitingXTree and not m.player
												if not (m.z > s.z+ZDIST) and not (s.z > m.z+ZDIST)
													XSon.ScenicCrash(p,s,p.xsonictable, m, PHASE, nil, 24<<16)
												end
											end
										end, s, s.x-XYDIST,s.x+XYDIST, s.y-XYDIST, s.y+XYDIST)

										P_NukeEnemies(s, so, NUKEDIST)
										XSon.Sound(s, sfx_xssfe0, 255)	--YYH impact sound.
										XSon.Sound(so, sfx_xssfe0, 165)	
										if xs.supersonic and H.boost==15 and p.mo --Spawn a tornado
											ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_DUSTDEVIL)
											if ghs and ghs.valid
												ghs.scale = p.mo.scale/8
												ghs.scalespeed = $*3/2
												ghs.destscale = p.mo.scale*4
												ghs.XS_NoCollide = "super"
												if (p.mo.eflags & MFE_VERTICALFLIP)
													ghs.eflags = $|MFE_VERTICALFLIP
												else
													ghs.eflags = $ & ~MFE_VERTICALFLIP
												end
												local TRACK = P_SpawnMobjFromMobj(ghs, 0,0,0, MT_XSTRACKER)
												TRACK.DisappearIn = 50
												TRACK.target = ghs
												TRACK.tornado = ghs
											end
											if xs.FreeFlight XSon.AddDamage(s, 1<<16) end
										end	
										if RB
											RB.windlines = 12
											RB.speedlines = 8 return 77 --						
										end
									else
										P_NukeEnemies(s, so, (15+(40*PHASE))*so.scale)
									end								
									if xs
										if xs.forceangle --Adjust if necessary.
											xs.forceangle.angle = R_PointToAngle2(so.x, so.y, s.x, s.y)
										end
										if BOOST == 3
											xs.ZoomLines = 4
											XSon.Flash(s, PAL_NUKE, 2, 1350*so.scale)
											local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
											for i = -1,1
												ghs = XSon.LightningStrike(s, DARKAURA, nil, true)
												ghs.spritexoffset = $+((34*i)<<16)
												ghs.spritexscale,ghs.spriteyscale = $*3,$*3
											end
										end									
									end
								end					
							else
								XSon.Quake(s, min(80<<16, CRASH/2), 3, CRASH*5)
								if PHASE == 3
									XSon.LightningStrike(s, (SST) and SKINCOLOR_SKY or SKINCOLOR_RUBY, nil, true)	
								end
							end
						else --if BOUNCE == 1, aka wallbounce.
							XSon.Quake(s, min(80<<16, CRASH/2), 3, CRASH*5)
						end
						local NO = (PHASE==3) and 3 or 1
						for i = -NO,NO,2
							ghs = XS_CraterSpew(s, ANG+((i<0) and -ANGLE_90 or ANGLE_90), 10+abs(i), true)	
							if ghs and ghs.valid
								ghs.tics,ghs.fuse = 36,39
							end
						end
						
						local RVOLUME = FixedInt(CRASH)
						local SFXo = XSon.GibSound(s, (HITS%2) and sfx_xssfxm or sfx_xssfx6, max(40, min(255,RVOLUME/2)) )						
						if BOUNCE != 1 --1 is Wallbounce, 2 is floor bounce.
							XSon.Sound(SFXo, sfx_xssfpm, max(90, min(240,RVOLUME)) )
							if PHASE == 3
								XSon.Sound(SFXo, sfx_xssfp9, max(150, min(250,RVOLUME)))
							end
							if so
								XSon.Sound(so, sfx_xssfpm, max(60, min(200,RVOLUME/2)), so.player)
							end
						else 
							if ((RVOLUME/3) > 9)
								S_StartSoundAtVolume(SFXo, sfx_pstop, min(120, RVOLUME/2))
							end
							for i = 1,2
								local SFX = (i==2) and sfx_pumpkn or sfx_xssfxs
								if so
									XSon.Sound(so, SFX, max(65, min(250,RVOLUME/2)) )
								end
								XSon.Sound(SFXo, SFX, max(150, min(255,RVOLUME*3/2)) )
							end
						end
						if not s.CRASHcanDMG and not (GROUNDED > 6) --Deal damage by smashing enemies into floors and walls.
							s.CRASHcanDMG = 5 --minor cooldown.
							local MULT = (BOUNCE==1) and 1 or 3
							local MAX = (25500+(3000*PHASE)) * ((MULT==3) and 2 or 1)
							local DMG = min(MAX, FixedInt(CRASH) * ((70*MULT)+(25*PHASE)) )
							if STYLE == "flurry"
								DMG = $/3
							elseif STYLE == "attack"
								DMG = $*4/3
							end
							XSon.AddDamage(s, DMG)
						end
						s.SFXcanplay = nil
					end
				end
				if not GROUNDED --and not STYLE == "groundslam"
					ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_EXPLODE) --Big poof.
					ghs.color,ghs.colorized = SKINCOLOR_BEIGE, true
					ghs.spriteyscale = FRACUNIT/((leveltime%5==0) and 2 or 1)
					ghs.destscale = $*5
					
					s.ROLLSPEED = FixedDiv(FixedMul($,10),9)
					s.ROLLD = -$
				end
			end
		end
		if s.Init and (s.Init > 3) 
			if not t.player
				P_SetOrigin(t, s.x, s.y, s.z) --Bring original enemy to our location too.
			elseif (GROUNDED) and so
				P_MoveOrigin(t, so.x, so.y, so.z)
			end
		end
	end
	
	local GOTHIT = s.JustGotHit or 0
	local MAXHITS = (PHASE==3) and 22 or (PHASE==2) and 17 or 14
		
	if not t.player
		if t.target and t.target.valid and not t.target.player
			t.target.flags2 = $|MF2_DONTDRAW
		end	
		if t.tracer and t.tracer.valid and not t.tracer.player
			t.tracer.flags2 = $|MF2_DONTDRAW
		end
		t.flags = MF_NOTHINK|MF_NOCLIPTHING|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_NOBLOCKMAP|MF_SCENERY
		t.flags2 = $|MF2_DONTDRAW --You shut up and back off!

		--Update bag to keep it alive and use our remembered properties.
		s.sprite = s.SPRITE
		s.frame = s.FRAME
	else
		s.flags2 = $|MF2_DONTDRAW --print("\x82 t is a player!")		
	end
		
	if s.EarthSlammed and (s.EarthSlammed < 3) and not s.GotSlammed
		s.color = t.color
		s.colorized = t.colorized
	elseif GROUNDED and (GROUNDED < 24)
		s.color = (leveltime%2) and SKINCOLOR_SUPERSILVER1 or t.color
		s.colorized = (leveltime%2) and true or t.colorized	
	else
		local FLASHCOLOR = ((HITS<10) and (GOTHIT>7) or (GOTHIT>8)) and SKINCOLOR_SUPERSILVER1 or SKINCOLOR_GOLDENROD
		--Color to be flashing?
		if HITS <= 2 and not s.GotHyperSmashed
			s.color = (leveltime%2) and FLASHCOLOR or t.color
			s.colorized = (leveltime%2) and true or t.colorized
		elseif (
		(HITS <= (MAXHITS/3)+1)
		or (HITS <= 4)
		or STYLE == "flurry")
		and not s.GotHyperSmashed
			s.renderflags = $ & ~RF_FULLDARK
			s.color = (leveltime%3==0) and t.color or (leveltime%2==0) and FLASHCOLOR or SKINCOLOR_FLAME
			s.colorized = (s.color==t.color) and t.colorized or true
		elseif (HITS < 10)
		or s.GotHyperSmashed
		or so and so.XSassault and ((so.XSassault.timer or 0) < 9)
			s.color = (leveltime%2) and FLASHCOLOR or SKINCOLOR_CRIMSON
			s.colorized = true
		else
			s.renderflags = $|RF_FULLDARK
			s.colorized = t.colorized
		end
		if not GROUNDED
			if (leveltime%2) and not (s.Init and (s.Init > 2) and (s.Init < 17)) and s.STARTEDFLUNG != true
			and not (XS_CRAPPYPRIORITIES==1)
				s.rollangle = $+(s.ROLLSPEED*s.ROLLD)
			end
			if t.player			
				t.player.drawangle = (leveltime*(s.ROLLSPEED*s.ROLLD))
				t.XS_DidBlur = true
			/*	if (s.JustGotHit or 0)>6)
					local ghs = t.XS_flicker
					if not (t.XS_flicker and t.XS_flicker.valid)
						t.XS_flicker = XSon.Overglow(t, 2, SKINCOLOR_WHITE) 
						ghs = t.XS_flicker
					end
					if ghs and ghs.valid
						ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
						ghs.fuse,ghs.tics,ghs.sttfade = 2,2,1
						if s.color and s.colorized==true
							ghs.flags2 = $ & ~MF2_DONTDRAW
							ghs.color,ghs.colorized = s.color, true
							XSon.OverGlowThinker(ghs)
						else
							ghs.flags2 = $|MF2_DONTDRAW
						end
					end	
				end */
			else
				s.angle = $+(s.ROLLSPEED*s.ROLLD)
			end
		end
	end

	--Moment of impact==true
	if IMPACT == true
	
		s.ROLLD = -$
		s.ROLLSPEED = $+(ANG1*5)
		s.SFXcanplay = true
		s.MOMANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
		
		local ghs
		if so and so.player and not (s.EarthSlammed and not s.GotSlammed)
		
			local p = so.player local xs = p and p.xsonictable
			local DARKAURA = (SST) and SKINCOLOR_XSSUPERBLUR or XSon.DarkAura(0)
			local ANG = p.drawangle
			
			if xs 
				if so.XSassault and so.XSassault.type
					STYLE = so.XSassault.type
				end	
				if xs.forceangle and xs.forceangle.angle != nil 
					ANG = xs.forceangle.angle
				end
				if xs.darkphase
					PHASE = xs.darkphase
					if (gametype == GT_COOP) and not t.player
						P_AddPlayerScore(p, (STYLE=="flurry") and 10 or 25)
					end
				elseif t.player and STYLE=="attack"
					if (t.z > so.z)
						if (t.z < so.ceilingz)
							so.z = t.z
						else
							so.z = so.ceilingz
						end
					else
						if (t.z > so.floorz)
							so.z = t.z
						else
							so.z = so.floorz
						end
					end							
				end
				xs.ZoomLines = max(0, PHASE-1)
				if t.player and t.player.xsonictable
					local xss = t.player.xsonictable local RBB = xss and xss.FreeFlight
					if (t.player.speed < 60*s.scale) and not STYLE=="groundslam" and not (STYLE=="flurry")
						s.momx,s.momy,s.momz = $*3/2,$*3/2,$*3/2
						t.momx,t.momy,t.momz = $*3/2,$*3/2,$*3/2
					end
					xss.zoomlines = 15
					XS_RingBurstDust(tp, t, xss, "manual", t.z)
					XSon.FlickerLight(s, SKINCOLOR_RED)
					if RBB
						local ghs= XSon.RadiantLight(s, SKINCOLOR_KETCHUP, 11, nil, S|FF_TRANS30, 1<<16)
						if ghs and ghs.valid
							ghs.ditchtracer = nil
							ghs.scalespeed = $*3
						end
						RBB.windlines = max(RBB.windlines or 0, 5)
						RBB.painflash = 19
						if RBB.stripes
							RBB.speedlines = max(RBB.speedlines or 0, RBB.stripes)
							RBB.stripes = nil
						end
					end
				end
			end	
			
			XS_RingBurstSparkles(s, nil, DARKAURA)
			S_StopSoundByID(so, sfx_xssfpl)
			
			local X,Y = (so.momx/2)+FixedMul(30*so.scale, cos(p.drawangle)), (so.momy/2)+FixedMul(30*so.scale, sin(p.drawangle))
			local GBLEND,GCOLOR = (xs and xs.darkphase) and AST_REVERSESUBTRACT or AST_ADD, SKINCOLOR_SUPERGOLD1
			if (SST)
				GBLEND = AST_ADD
				GCOLOR = (leveltime%2) and SKINCOLOR_FLAME or XSLocalRNG(SKINCOLOR_SUPERGOLD1, SKINCOLOR_SUPERGOLD5,s)
			else
				GCOLOR = (BLEND==AST_ADD) and SKINCOLOR_FLAME or SKINCOLOR_SUPERGOLD1
			end
			
			
			if not GROUNDED and not STYLE=="groundslam"
				--Enemy ghosts.
				local ghs2 = P_SpawnGhostMobj(s) --Yesss!
				ghs2.sprite = s.SPRITE
				ghs2.spritexscale,ghs2.spriteyscale = 75356,75356	
				if ghs2.skin
					ghs2.sprite2 = s.SPRITE2
				end
				ghs2.frame = s.FRAME
				ghs2.scale = s.scale
				ghs2.dispoffset = -99
				ghs2.rollangle = s.rollangle
				ghs2.color = s.color
				ghs2.colorized = s.colorized
				ghs2.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
				ghs2.momz = s.momz/2	
				ghs2.fuse,ghs2.tics = 16,16
				if STYLE == "flurry"
					ghs2.momx,ghs2.momy = s.momx/3,s.momy/3
					local TRANS = (ghs2.frame & FF_TRANSMASK)
					if TRANS
						ghs2.frame = ($ & ~FF_TRANSMASK)|(max(0, TRANS-3<<16))
					end
				else
					ghs2.momx,ghs2.momy = s.momx/2,s.momy/2
				end
				P_SetOrigin(ghs2, so.x+(X*3),so.y+(Y*3), ghs2.z)
			end
				
			if STYLE == "attack"		
				if HITS != 1
					if so and so.skin =="xsonic" and ((HITS or 0)< 8) and so.player 
					and (so.player.cmd.buttons & BT_JUMP) and not (so.player.cmd.buttons & BT_SPIN) and so.player.powers[pw_super] 
						XSon.AddDamage(s, 17200+(5600*PHASE))
					else
						XSon.AddDamage(s, 14750+(5600*PHASE))
					end
				end
				
				XS_RingBurstSparkles(s, nil, DARKAURA)
				XSon.DarkBody(s, nil, nil, DARKAURA, (PHASE==1) and 1 or 2)	
				
				--Sonic Avatar Ghost.
				ghs = P_SpawnGhostMobj(so)
				ghs.blendmode = GBLEND
				ghs.color,ghs.colorized = GCOLOR,true
				ghs.scale = so.scale*5/3
				ghs.scalespeed = ghs.scale/7
				ghs.destscale = so.scale
				ghs.dispoffset = -129
				ghs.fuse,ghs.tics = 9,9
				ghs.rollangle = so.rollangle
				ghs.spriteyoffset = so.spriteyoffset-24<<16
				ghs.angle = ANG
				P_Thrust(ghs, ANG, 36*so.scale)
				P_MoveOrigin(ghs, s.x, s.y, s.z)
					
				 --Generic attack sound
				XSon.Sound(so, sfx_xssfpl, min(255, 70+PHASE*44))

				--Impact Wave!
				ghs = XS_CircleWave(so, 1, (HITS%2) and SKINCOLOR_RUBY or DARKAURA)
				if HITS != 1
					ghs.spritexscale = max(9999, ($*3/4)-P_RandomRange(0,12000))
					ghs.spriteyscale = ghs.spritexscale
				end
				ghs.blendmode = ((SST) or (HITS%2)) and AST_ADD or AST_REVERSESUBTRACT
				ghs.sprite = SPR_XSG2
				local TRANS = (PHASE==1) and FF_TRANS90 or ((PHASE==2) or (s.BAGhit%2)) and FF_TRANS80 or FF_TRANS70
				ghs.frame = 31|TRANS
				P_Thrust(ghs, ghs.angle-ANGLE_90, so.scale*3)
				
				if PHASE != 1
					ghs = XSon.ElectricBody(so, 6, (HITS%2==0) and SKINCOLOR_CRIMSON or DARKAURA) --Electric aura on Sonic
					ghs.blendmode = (HITS%2==0) and AST_TRANSLUCENT or AST_REVERSESUBTRACT
					ghs.frame = $|((HITS%2) and FF_HORIZONTALFLIP or 0)
					if (HITS > 4)
						local RNG = P_RandomRange(6250,19250)
						XSon.SpikedAura(s, 100250+RNG)
						if (HITS > 8) --Enemy begins smoking, then exploding.
							ghs = XSon.FireBall(s, nil, 70000-RNG, FRACUNIT/10, FF_TRANS20)
						else
							ghs = XSon.SmokeCircle(s, 0, 1, s, 64000-RNG)
						end
						ghs.sttspeed,ghs.fuse,ghs.sttfade = 1,18,12
						ghs.tracer,ghs.ditchtracer = s,6
						ghs.momx,ghs.momy,ghs.momz = s.momx/2,s.momy/2,s.momz/2
					end	
					if (HITS > 2)
						ghs = XSon.ElectricBody(s) --Electric aura on enemy.
						ghs.frame = $|((s.BAGhit%2==0) and FF_HORIZONTALFLIP or 0)
					end					
				end
				
			elseif STYLE == "flurry"
			
				S_StopSoundByID(so, sfx_xssfpn) S_StopSoundByID(so, sfx_brakrx) S_StopSoundByID(so, sfx_xssfxs) S_StopSoundByID(so, sfx_xssfpl)
				S_StartSoundAtVolume(so, (HITS%4==0) and sfx_brakrx or (HITS%3==0) and sfx_xssfxs or (HITS%2==0) and sfx_xssfpl or sfx_xssfpn, min(255, 105+PHASE*50))
				XSon.Sound(so, (HITS%3==0) and sfx_xssfp2 or sfx_xssfpt, min(255, 70+PHASE*44))
				
				XSon.AddDamage(s, 5000+(4750*PHASE))
				
				if not s.BAGflurry
					if (SST)
						local SFX = P_SpawnMobjFromMobj(so,0,0,0,MT_THOK)
						SFX.flags2=MF2_DONTDRAW SFX.fuse,SFX.tics = 111,111						
						XSVoice(p, so, xs, sfx_xsvocb, "ForceEcho", nil, 255)
						if S_SoundPlaying(so, sfx_xsvocb)
							S_StartSound(SFX, sfx_xsvocb)
						end
					elseif PHASE!=3
						if PHASE==1
							local SFX = P_SpawnMobjFromMobj(so,0,0,0,MT_THOK)
							SFX.flags2=MF2_DONTDRAW SFX.fuse,SFX.tics = 111,111						
							XSVoice(p, so, xs, sfx_xsvocb, "noecho", nil, 255)
							if S_SoundPlaying(so, sfx_xsvocb)
								S_StartSound(SFX, sfx_xsvocb)
							end
						else
							XSVoice(p, so, xs, sfx_xsvocd, true, nil, 255)					
						end
					else
						XSVoice(p, so, xs, sfx_xsvocc, "noecho", nil, 200)
					end
					if not S_SoundPlaying(so, sfx_xssfp1)
						S_StartSound(so, sfx_xssfp1)
					end
					CrudeTryMove(so,s, false)
					s.BAGflurry = true
				end

				local FSFX = {sfx_xssfpq, sfx_xssfpr, sfx_xssfps} --flurry sounds
				s.LastFSFX = (s.LastFSFX==nil or s.LastFSFX==#FSFX) and 1 or $+1
				XSon.GibSound(so, FSFX[s.LastFSFX], min(255, 185+PHASE*20))		
				
				local ANGRNG = P_RandomRange(15,172)
				local ANGRNG2 = P_RandomRange(15,172)
				if (abs(ANGRNG-ANGRNG2) < 45)
					ANGRNG2 = ($+14)*-1
				end
				
				
				local Xo = FixedMul(37*so.scale, cos(ANG+(ANGRNG*ANG1)))
				local Yo = FixedMul(37*so.scale, sin(ANG+(ANGRNG*ANG1)))
				
				local Xo2 = FixedMul(40*so.scale, cos(ANG+(ANGRNG2*ANG1)))
				local Yo2 = FixedMul(40*so.scale, sin(ANG+(ANGRNG2*ANG1)))
				
				local Zo = ((HITS%2) and P_RandomRange(-11, -1) or P_RandomRange(8,16) ) *so.scale*SOFLIP
				if (HITS%2)
					Xo,Yo = -$,-$
					Xo2,Yo2 = -$,-$
				end
				local TRANS = (xs.darkphase==1) and FF_TRANS90 or FF_TRANS80
				
				for i = 1,48 do --1,45
					for d = -1,1,2 do --Spawn speed smears!
						ghs = P_SpawnMobjFromMobj(s, 
						s.momx+(d!=1 and Xo or Xo2)*i, 
						s.momy+(d!=1 and Yo or Yo2)*i, 
						s.momz+s.height/3 + (d!=1 and Zo or Zo*2)*i, MT_THOK)
						
						ghs.state = S_XSTHOK
						ghs.frame = A|TRANS|FF_FULLBRIGHT
						ghs.flags = $|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_SCENERY
						ghs.blendmode = GBLEND
						ghs.color = GCOLOR
						ghs.scale = FixedDiv(FixedMul(so.scale, 19+((SST) and 4 or 0)),10+i)
						ghs.fuse,ghs.tics = 14,14
						ghs.destscale = 1
						if (ghs.scale < 500)
							break --Too tiny to matter at this point!
						end						
					end
					if i == 1 and xs and ghs 
						local TOANG = R_PointToAngle2(ghs.x, ghs.y, s.x, s.y)
						xs.forceangle = {angle = TOANG, timer = 7, nosangle = true}
						p.drawangle = TOANG
						
						--Flurry impact Wave!
						for d = -1,1,2
							local wave = XS_CircleWave(s, 1, SKINCOLOR_SILVER, 11, (25000+5000*d)-P_RandomRange(d,10000), TOANG)
							wave.sprite = SPR_XSG2
							wave.frame = 31|((2-d)<<16)
							wave.sttfade = true
							wave.scalespeed = FRACUNIT/(3-d)
							wave.dispoffset = d
							wave.angle = $+(ANG1*15*d)
							P_Thrust(wave, TOANG, -so.scale*20*d)
							wave.momz = s.momz- Zo*((d==1) and 1 or 2)
						end
						
						--Sonic afterimages speeding out to the back
						for d = 1,3
							ghs = P_SpawnGhostMobj(so)
							ghs.frame = ($ & ~FF_TRANSMASK)|(FF_TRANS10*d)
							ghs.flags = $|MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING
							ghs.fuse,ghs.tics = 13,99
							P_MoveOrigin(ghs, ghs.x-FixedMul(40*so.scale+so.radius, cos(TOANG)), ghs.y-FixedMul(40*so.scale+so.radius, sin(TOANG)), ghs.z-(Zo*2))
							P_Thrust(ghs, TOANG, -so.scale*16*d)
							ghs.momz = -Zo
							ghs.spritexscale = (($*3*4)/3) / (d+2)
							ghs.spriteyscale = ghs.spritexscale
							ghs.rollangle = so.rollangle
						end
						if ghs2 and ghs2.valid --Spawn a short-lasting copy of the enemy ghost to the middle
							ghs = P_SpawnGhostMobj(ghs2)
							ghs.flags = $|MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING
							ghs.frame = $ & ~FF_TRANSMASK
							ghs.tics = 6
							ghs.rollangle = ghs2.rollangle
							ghs.scale = $*5/4
							ghs.dispoffset = 149
							P_SetOrigin(ghs, so.x,so.y,so.z)
						end						
					end		
				end
				so.flags2 = $|MF2_DONTDRAW
			end
			
			--flurry sounds
			local FSFX = {sfx_xssfpq, sfx_xssfpr, sfx_xssfps}
			s.LastFSFX = (s.LastFSFX==nil or s.LastFSFX==#FSFX) and 1 or $+1
			XSon.GibSound(so, FSFX[s.LastFSFX], min(255, 185+PHASE*20))				
			
			local NFLOORZ = (so.eflags & MFE_VERTICALFLIP) and so.ceilingz or so.floorz
			if STYLE != "flurry" and xs and (abs(so.z-NFLOORZ) < 255*s.scale)
				for i = 1,PHASE
					ghs = XS_RingBurstDust(p, so, xs, 333*so.scale)
					if ghs and ghs.valid
						ghs.spritexscale = $*i
						P_Thrust(ghs, s.MOMANG, (16-(16*i))*so.scale)
					end
				end
				if PHASE != 1
					for i = -1,1,2
						ghs = XS_CraterSpew(so, p.drawangle+(ANG1*120*i), 13, true)	
						if ghs and ghs.valid
							ghs.spritexscale,ghs.spriteyscale = $*3/2,$*3/2
							ghs.tics,ghs.fuse = 22,20
							ghs.momx = ($*9)+FixedDiv(FixedMul(so.momx, 2),6)
							ghs.momy = ($*9)+FixedDiv(FixedMul(so.momy, 2),6)
						end
					end
				end
			end
			if not t.player --Gibs flying from enemy.
				if STYLE == "flurry" 
					if (HITS > 12)
						XSon.gibs(t, so, 1, 111*so.scale, nil, true, nil, false, p.drawangle)
					end
					XSon.gibs(t, so, (xs.darkphase==1) and 1 or 2, 111*so.scale, nil, true, nil, nil, p.drawangle)
				else
					local NUMGIBS = (HITS>6) and 3 or (HITS>3) and 2 or 1
					XSon.gibs(t, so, NUMGIBS, 110*so.scale, nil, true, s)
				end
			elseif t.player.xsonictable 
				local xss = t.player.xsonictable 
				if xss.FreeFlight
					local RBB = xss.FreeFlight
					if type(t.xslastpaingrunt)=="number" S_StopSoundByID(t, t.xslastpaingrunt) end
					XSon.DOPAINGRUNT(tp,t,xss,RBB)
					RBB.windlines = 6
					t.momx,t.momy,t.momz = $*3/2,$*3/2,$*3/2
					
					if so and so.player and so.player.xsonictable
						local FF = so.player.xsonictable
						FF.stripes = 9
						FF.windlines = 6
					end
				end
			end
		end	
	end
	
	local NOPLAYER = s.NoPlayer or 0 
	if not (so and so.XSassault and so.XSbag == s)
		s.NoPlayer = NOPLAYER+1
	end
	if not (NOPLAYER > 200)
		s.tics = max(19, $+1) --Keep it going!
		s.anim_duration = max(19, $+1)
		if s.STARTEDFLUNG != true
			s.fuse = max(19, $+1)
		end
		s.TEMPROLL = max(8, s.fuse-8)
	end
	
	if s.GIBstretch
		XSon.GIBstretch(s)
	end
	local GOTHIT = s.JustGotHit or 0
	if s.GotHyperSmashed and s.GotHyperSmashed.quake and (s.GotHyperSmashed.timer > 6) and (GOTHIT < 3)
		 GOTHIT = (s.GotHyperSmashed.timer/6)
	end
	if GOTHIT --Steam Trail after being hit and countdown.
		if s.GotHyperSmashed
		or STYLE != "flurry" and not (STYLE == "groundslam" and not s.GotSlammed)
			local ghs = P_SpawnMobjFromMobj(s,s.momx/2,s.momy/2,s.momz/2+s.height/3, MT_THOK)
			ghs.state = S_SPINDUST1+P_RandomRange(0,2)
			ghs.color,ghs.colorized = SKINCOLOR_SUPERGOLD5-min(20, GOTHIT*2), true
			ghs.fuse = 38
			ghs.dispoffset = -111
			ghs.frame = ($ & FF_FRAMEMASK)|FF_TRANS60
			ghs.scale = min(s.scale*3, (s.scale*10)/GOTHIT)
			ghs.scalespeed = FRACUNIT/16
			ghs.destscale = 99<<16
			ghs.momx,ghs.momy,ghs.momz = s.momx/3,s.momy/3,s.momz/3
			if (GOTHIT < 9-(PHASE*2))
				ghs.flags2 = $|MF2_SHADOW
			end
		end
		if s.JustGotHit
			s.JustGotHit = $-1 --print("\x86"+"BAG.JustGotHit"+s.JustGotHit)
		end
	end	
	return s --Did it!
end


--so is the player mobj. s is the bag. t is the original enemy.
XSon.ChaseBag = function(so,p,xs, s,t, DOMAGNET)
	local DIRECT = xs.direct or 0
	local TURNANG = (DIRECT>9) and xs.turnang or p.drawangle --Input direction.
	
	if s.EarthSlammed or t and t.EarthSlammed or so and so.XSassault and so.XSassault.nochase
		return 0,1,1,DIRECT,p and p.cmd.angleturn<<16 or 0 
	end	
	local TOANGLE = R_PointToAngle2(so.x, so.y, s.x, s.y)
	
	--Setup magnet points and distance calculations.
	local SNAPDIST = 99*so.scale
	local MX,MY = s.x+FixedMul(SNAPDIST, cos(TOANGLE)), s.y+FixedMul(SNAPDIST, sin(TOANGLE))
	local DIST = FixedHypot(FixedHypot(so.x-MX, so.y-MY), so.z-(s.z+s.height/3)) 
	local STEPSIZE = 99*s.scale --How far we can move each tic.
	local TWOD = ((s.flags2 & MF2_TWOD) or twodlevel)
	local INPUTDIFF,PLUS = nil,1
	
	--Apply sandbag timers!
	if s.JustGotHit
	
		AssaultHoming(so, s, 24*s.scale, 169*so.scale, DIST) --Home into the bag!
		
	else
		s.flags = $ & ~MF_NOGRAVITY
		
		--Magnetize towards your opponent.
		if (DIRECT>4)
			INPUTDIFF,PLUS = XS_AngleDifference(TOANGLE, TURNANG, 2, ANGLE_90)
		/*	if INPUTDIFF == 0
				print("\x83"+"INPUTDIFF:\x8c"+"0"+"\x8a DIST:\x80"+FixedInt(DIST)+"\x80 PLUS:\x8a"+PLUS)
			else
				print("\x83"+"INPUTDIFF:\x8b"+(INPUTDIFF/ANG1)+"\x8a DIST:\x80"+FixedInt(DIST)+"\x80 PLUS:\x8a"+PLUS)
			end */
		end
		if not (so.XSassault and so.XSassault.chase == 0)
			if INPUTDIFF != nil and (INPUTDIFF < ANGLE_45) or (DIST > 33*so.scale)
				if DOMAGNET
					local X = FixedMul(STEPSIZE, cos(TOANGLE))
					local Y = (TWOD!=true) and FixedMul(STEPSIZE, sin(TOANGLE)) or 0
					
					--Approach the opponent passively.
					local soZ = so.z+(so.height/6)
					local NUMSTEPS = max(9, FixedInt(STEPSIZE)/9)
					STEPSIZE = STEPSIZE/NUMSTEPS --Divide the steps.
					local INRANGE = max(max(STEPSIZE, 64*so.scale), 164*so.scale-STEPSIZE)
					
--					print("\x8d"+"[1/"+NUMSTEPS+"]\x80 Magnetize!\x8a STEPSIZE:\x80"+FixedInt(STEPSIZE))
					
					for i = 1,min(1, NUMSTEPS)					
						if (soZ < s.z) --Vertical adjustments.
							if (soZ+STEPSIZE < s.z+(s.height/2))
								if (soZ < so.ceilingz)
									so.z = $+STEPSIZE
								end
							end
						elseif (soZ > s.z+s.height/2) and (soZ > so.floorz) and (soZ-STEPSIZE > s.floorz)
							so.z = $-STEPSIZE
						end
						--Horizontal approach.
						if (DIST > INRANGE) 
							P_TryMove(so, so.x+X, so.y+Y, true)
						end
						--Stop here or magnetize more?
						MX,MY = t.x+FixedMul(SNAPDIST, cos(TOANGLE)), t.y+FixedMul(SNAPDIST, sin(TOANGLE))
						DIST = FixedHypot(FixedHypot(so.x-MX, so.y-MY), so.z-(t.z+t.height/3)) 
						if (DIST <= INRANGE) break end --Close enough, we're done now!					
					end
				end
				AssaultHoming(so, s, 63*s.scale, 16*so.scale, DIST)
				
			else
				AssaultHoming(so, s, 16*s.scale, 32*so.scale, DIST)
			end
		end
	end	return INPUTDIFF,PLUS,DIST,DIRECT,TURNANG --
end

--Treat enemies as repeatably attack-able sandbags, going for complete overkill!
XSon.SandBag = function(s, ATTACK, FINISH) if not s.valid return end --
	if s.STARTEDFLUNG
		s.flags = $ & ~MF_NOGRAVITY XSon.FlungThinker(s) return -- --We're using the Flung Thinker now.
	end
	local so = s.tracer --The heroic player smashing the teeth from your face
	local p = so and so.player 
	local xs = p and p.xsonictable
	local t = s.target --The OG enemy, we're technically just a visual stand-in.
		
	if not xs or s.BAGhit == nil and not (t and t.valid) --It's gone before we even set anything up? Quit out!
		FINISH = "fail"
	elseif FINISH == nil
		if p.powers[pw_carry] 
		or (s.eflags & MFE_SPRUNG) or (s.state == S_PLAY_GASP)
		or (p.pflags & PF_SLIDING) or p.xsringburst or p.gotflag or p.xswallrun
		or (not xs.supersonic and not xs.darkphase)
			FINISH = "fail"
		end
	end
	if FINISH == "fail" --Is it over?
		RestoreTarget(s.XSbag, "restore", true, so or s)
		if s and s.valid and not s.STARTEDFLUNG
			P_RemoveMobj(s) 
		end return 
	end
	
	local SHIFT = (XSon.SandBagShift(s,t)) if not s.valid return end --Manipulate the sandbag.

	if SHIFT == s --The sandbag is still valid? Then continue!
		
		if FINISH --We finishing this once and for all? Determine if the assault continues.
			ATTACK = nil
		elseif ATTACK == nil and so
			if xs.darkphase
			or t.player and xs.FreeFlight and xs.FreeFlight.LockOn == t
				local DIST = FixedHypot(so.x-s.x, so.y-s.y)
				local ZDIST = abs(so.z-s.z)
				local ASSAULT = so.XSassault
				if (ZDIST > 1600<<16) or (DIST > 7500<<16) --Sanity.				
					if ASSAULT and (ASSAULT.timer > 9)
						ASSAULT.timer = 500
					end return --
				elseif ASSAULT and ASSAULT.type == "attack" and (ASSAULT.timer >= 1 and ASSAULT.timer <= 3)
					return --Don't chase in the first 3 tics.
				else
					XSon.ChaseBag(so,p,xs,s,t) return --"ongoing" phase! Keep chasing.
				end
			end
		else
			S_StopSoundByID(s, sfx_xssfpa) if so S_StopSoundByID(so, sfx_xssfpa) end
		end
		
		if ATTACK == nil --No attack given but not ongoing? Then go to the flung thinker.
		
			if so
				if so.XSassault
					local ASSAULT,BAG = so.XSassault,so.XSbag
					if BAG and BAG.valid
						RestoreTarget(BAG, "restore", true, so)
						local HITS = BAG.BAGhit or 0
						if BAG.OLDMOMX != nil --(HITS < 3) and 
							ASSAULT.MOMX,ASSAULT.MOMY,ASSAULT.MOMZ = BAG.OLDMOMX,BAG.OLDMOMY,BAG.OLDMOMZ
						end
					end
					so.XSassault.END = true
					XSon.AssaultAnim(so.player,so,so.player.xsonictable) return false --
				else
					s.STARTEDFLUNG = true
				end
				so.XSbag,s.tracer = nil return false --
			else
				s.STARTEDFLUNG = true	
			end
			
			s.tracer = nil
			if t and t.player
				P_RemoveMobj(s) return false -- print("SandBag:\x85 ATTACK is nil!\x81 Remove!")
			else
--				print("SandBag:\x85 ATTACK is nil! \x82 Do Flung Thinker.")
				s.flags = $ & ~MF_NOGRAVITY
				s.STARTEDFLUNG = true
				if t and t.valid
					s.color = t.color
					s.colorized = t.colorized
				end
				XSon.FlungThinker(s) 
			end
			return false --
			
		else --if we DO have an ATTACK input, then...
		
			local SOFLIP,RB = 1, so and so.XS_DarkTable --RB is optional!		
			if so and so.valid
				SOFLIP = P_MobjFlip(so)
			end
			
			local INPUTDIFF,PLUS,DIST,DIRECT,TURNANG = XSon.ChaseBag(so,p,xs, s,t) --First, chase the bag as usual
			local ASSAULT = so.XSassault
			
			--Now determine what we'll do.
			if ATTACK == "groundslam"
			
				local SLAM = ASSAULT and ASSAULT.slam or 0
				local TOANGLE = R_PointToAngle2(so.x, so.y, s.x, s.y)
				
				XSon.FlickerLight(t)
				P_SetObjectMomZ(s, -53*so.scale)
				CrudeTryMove(so,s, false)
				P_Thrust(so, TOANGLE, 26*so.scale)
				so.momx,so.momy,so.momz = s.momx,s.momy,s.momz
				
				--	P_TryMove(so, s.x, s.y, true)
				s.flags2 = $ & ~(MF2_DONTDRAW|MF2_SHADOW)
				s.BAGcutcombo,s.BAGflurry = nil,nil
				s.spritexscale = max(42000, (abs(s.momx)+abs(s.momy))/14 )
				s.spriteyscale = max( P_RandomRange(70000, 90000), abs(s.momz)/7)

				so.spritexscale,s.spriteyscale = FRACUNIT-20000,FRACUNIT+15500
				so.spritexoffset,so.spriteyoffset = 0,-8<<16
				so.XSstretch = {timer = 5, Xspeed = 4400, Yspeed = 3300}	
				so.flags2 = $ & ~(MF2_DONTDRAW|MF2_SHADOW)
				so.frame = $ & ~(FF_TRANSMASK|FF_HORIZONTALFLIP)
				
				S_StopSoundByID(so, sfx_xsvocb) S_StopSoundByID(so, sfx_xsvocc)
				XSon.Echoes(p,so,xs, "stop")
				S_StartSound(so, sfx_brakrx)
				
				p.camerascale = (p.pflags & PF_ANALOGMODE) and 10000 or 18000
				p.XSscalecam = 6
				
				RB = (xs.FreeFlight) and xs.FreeFlight or so and so.XS_DarkTable
				if RB
					RB.forcetrail,RB.speedlines,RB.windlines = 1,1,6
				end
				local ENDANIM = (s.BAGflurry or (ASSAULT and ASSAULT.type == "flurry")) and 30 or 80
				
				ASSAULT = {
				sprite2 = SPR2_TAL6,
				type = "groundslam",
				slam = SLAM+1,
				timer = 0,
				frame = A,
				impact = D,
				lastframe = F,
				speed = 4, 
				attackCD = ENDANIM,
				endanim = ENDANIM,
				}

				XSon.LightningStrike(so, SKINCOLOR_RUBY, so)
				XSon.Quake(so, 99<<16, 9, 999*so.scale)
				S_StartSound(so, sfx_kc4d)
				XSon.GibSound(so, sfx_xssfpt, 255)
				XSon.Flash(so, PAL_NUKE, 3, 800*so.scale)
				if ENDANIM == 30
					s.BAGhit = 21
					ASSAULT.combolock = ENDANIM
				end
									
			elseif ATTACK == "attack"
			or ATTACK == "flurry"
			or ATTACK == "ForceEndKick" --It's a new attack input!
			
				local HADHYPER = (so.XSassault) and so.XSassault.didhyper or nil
				local HYPERSMASH = (so.XSassault and so.XSassault.hypersmash) and so.XSassault.hypersmash or nil
				local HITS = s.BAGhit or 0
				local SUPER = (xs.darkphase) or 1
				local MAXHITS = (SUPER==3) and 22 or (SUPER==2) and 17 or 14
				local AUTOJUMP = 0
				
				s.momx,s.momy,s.momz = 0,0,1
				if s.target and s.target.player --For simplicity's sake.
					s.target.momx,s.target.momy,s.target.momz = 0,0,1
					if (ATTACK == "attack") and HYPERSMASH==nil
						if (so.z > s.target.z)
							if (s.z < s.target.ceilingz)
								s.target.z = s.z
							else
								s.target.z = s.target.ceilingz
							end
						else
							if (s.z > s.target.floorz)
								s.target.z = s.z
							else
								s.target.z = s.target.floorz
							end
						end
						s.z = s.target.z
					end
				end	
				s.JustGotHit = 10
				
				local TOANGLE = R_PointToAngle2(so.x, so.y, s.x, s.y)
				
				--Calculations
				local PUNCHANGLE,VERTICALPOWER = TOANGLE,0
				if not DIRECT-- or (abs(p.cmd.sidemove) < 6)
					if ASSAULT and ASSAULT.flip
						PUNCHANGLE = (HITS<2) and p.drawangle or p.drawangle+ANG30
					else
						PUNCHANGLE = (HITS<2) and p.drawangle or p.drawangle-ANG30
					end
				else
					PUNCHANGLE = TURNANG
				/*	if (DIRECT > 4)-- and INPUTDIFF == nil --Calculate if we haven't already.
						INPUTDIFF,PLUS = XS_AngleDifference(TOANGLE, TURNANG, 2, ANGLE_90)
						PUNCHANGLE = (INPUTDIFF<ANG60) and TURNANG or $-(ANG60*PLUS)
					end */
				end
				if ATTACK == "flurry" and s.BAGflurry
					VERTICALPOWER = (so.scale/2)*HITS
				elseif ASSAULT and ASSAULT.gotype == 2
				or (p.cmd.buttons & BT_JUMP) and not (p.cmd.buttons & BT_SPIN)
				or ASSAULT and ASSAULT.jump!=nil and (ASSAULT.jump < 6)
				or (DIRECT < 38) and not ((p.cmd.buttons & BT_SPIN) and not (p.cmd.buttons & BT_JUMP))
				or not (p.cmd.buttons & (BT_SPIN|BT_JUMP)) and not (ASSAULT and ASSAULT.jumped)
					VERTICALPOWER = max( (7+(SUPER*6))*so.scale, 38-DIRECT*(so.scale/2) )
				elseif ASSAULT and ASSAULT.gotype == 1 and ((p.cmd.buttons & BT_JUMP) or ASSAULT.jump) and (HITS>2)
					VERTICALPOWER = max( (5+(SUPER*5))*so.scale, 38-DIRECT*(so.scale/2) )
				elseif (p.cmd.buttons & BT_SPIN)
				or ASSAULT and ASSAULT.gotype == 1
					VERTICALPOWER = (p.cmd.forwardmove < 1) and -33*so.scale or -so.scale*5
				end
				--Upwards launching.
				local UPDOWN = -1 --Kick up or punch "down"? (Straight, mostly)
				if ASSAULT
					if ASSAULT.type == "groundslam"
						ASSAULT.timer = 0
					end
					if ASSAULT.gotype == 2
					or (not ((ASSAULT.gotype==1) and (HITS >= 3) and not (p.cmd.buttons & BT_JUMP))) and 
					(
					(p.cmd.buttons & BT_JUMP) and not ((ASSAULT.sprite2 == SPR2_TAL2) and not (p.cmd.buttons & BT_SPIN))
					or ASSAULT.jump and (ASSAULT.jump < 7)
					or not (p.cmd.buttons & (BT_JUMP|BT_SPIN)) and not ASSAULT.jumped
					)
						UPDOWN = 1 --Kick up!	
					elseif ASSAULT.gotype == 1 and ((p.cmd.buttons & BT_JUMP) or ASSAULT.jump)
						UPDOWN = 1
					elseif ASSAULT.gotype == 1
					or ASSAULT.spin and (ASSAULT.spin < 7) and not (ASSAULT.jump and (ASSAULT.jump < 7))
					or (p.cmd.buttons & BT_SPIN) and not (p.cmd.buttons & BT_JUMP)
						VERTICALPOWER = (p.cmd.forwardmove < 1) and -33*so.scale or -so.scale*5				
						UPDOWN = -1
					end
				elseif (p.cmd.buttons & BT_JUMP) and not (p.cmd.buttons & BT_SPIN)
				or not (p.cmd.buttons & (BT_JUMP|BT_SPIN))
					VERTICALPOWER = max( (7+(SUPER*6))*so.scale, 38-DIRECT*(so.scale/2) )
					UPDOWN = 1 
				end
				
				local ANIM = ((UPDOWN==1) or (ATTACK == "ForceEndKick")) and SPR2_TAL2 or SPR2_TAL1 --TAL1 is punch, TAL2 is kick.
				if VERTICALPOWER --VERTPOWER"
					if not ASSAULT
						if (p.cmd.buttons & BT_SPIN) or not (p.cmd.buttons & BT_JUMP)
							ANIM = SPR2_TAL1			
						end
					elseif (p.cmd.buttons & BT_SPIN) and ASSAULT.spin and (ASSAULT.spin < 7)
					and not (ASSAULT.jump and (ASSAULT.jump < 7))
					or ASSAULT.gotype == 1
						ANIM = SPR2_TAL1
					end
					if ANIM == SPR2_TAL1 or (ASSAULT and ASSAULT.gotype == 1)
						VERTICALPOWER = $/2
					end
					if s.target and s.target.player
						VERTICALPOWER = $*5/3
					end
					if not ((ATTACK=="flurry") and s.BAGflurry)
						s.momz = VERTICALPOWER*SOFLIP --Send bag flying vertically!				
					end
				else --Standard, then?
					if not (p.cmd.buttons & BT_JUMP) or ANIM == SPR2_TAL1 or (ASSAULT and ASSAULT.gotype == 1)
						s.momz = $*2/3
					else
						if (s.momz*SOFLIP < s.scale*14)
							s.momz = s.scale*14 
						else
							s.momz = max(14*s.scale*SOFLIP, s.momz/2)
						end
						if s.target and s.target.player and (abs(s.momz) < 60*so.scale)
							s.momz = $*5/3
						end
					end
				end
				P_TryMove(so, s.x, s.y, true)
				if (abs(s.z-so.z) < 444*so.scale)
					if (s.z+s.height/6 > so.z)
						if not (s.z+s.height/6 > so.ceilingz)
							so.z = s.z+s.height/6
						end
					elseif (so.z > s.z+s.height/6)
						if not (s.z+s.height/6 < so.floorz)
							so.z = s.z+s.height/6
						end
					end	
				end
				
				local THRUST = 7*so.scale
				if ATTACK != "flurry" or not s.BAGflurry
					THRUST = (23+((SUPER+min(15,HITS))*6))*so.scale
					THRUST = $ - (VERTICALPOWER and max(0, VERTICALPOWER/4) or 0)
					THRUST = max(7*so.scale, $) --Minimum thrust is 7.
				else
					THRUST = $+(HITS*((so.scale/3)*SUPER))
				end
				if s.target and s.target.player
					THRUST = $*5/3
				end
				P_Thrust(s, PUNCHANGLE, THRUST) --Horizontal launch.		
				xs.forceangle = {angle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), timer = 8, nosangle = true}
				
				--Strike GFX.
				XS_RingBurstSparkles(so, 10+SUPER, (not xs.darkphase) and so.color or XSon.DarkAura(0))
				
				if (HITS < 2) and ATTACK!="flurry" or (HITS >= MAXHITS)
					XSon.LightningStrike(t, (not xs.darkphase) and SKINCOLOR_SUPERSKY4 or SKINCOLOR_RED, t)
					XSon.Quake(so, (30+HITS+(SUPER*10))<<16, 5, 440*SUPER*so.scale)
					if (HITS >= MAXHITS)
						S_StartSoundAtVolume(so, sfx_kc4d, min(255, 55+(80*SUPER)) )
					else
						XSon.GibSound(so, sfx_xssfpt, min(255, 55+SUPER*80))
					end
					XSon.FlickerLight(t)
					XSon.Flash(so, PAL_NUKE, (SUPER==3) and 3 or 2, 250*SUPER*so.scale)
					if (HITS < 2) or s.XS_DarkTable and s.XS_DarkTable.jumpDR
						t.momz = $+(4*so.scale*SOFLIP)
						AUTOJUMP = 6
						p.cmd.buttons = $|BT_JUMP & ~BT_SPIN
						if s.XS_DarkTable and s.XS_DarkTable.jumpDR
							s.XS_DarkTable.jumpDR = 0
						end
					end
				else
					XSon.Quake(so, (30+HITS+(SUPER*10))<<16, 1+SUPER, 333*SUPER*so.scale)
				end
				
				xs.jump,xs.spin = 999<<16,999<<16 --Lock those controls!
				
				if (HITS >= MAXHITS) and (ATTACK != "ForceEndKick")  --Do a finisher on the final attack!
				
					if not t.player
						XSon.gibs(so, t, nil, 70<<16, (SUPER==3) and true or (SUPER==2) and 3 or nil)
					end	
					ASSAULT = {sprite2 = ANIM, finisher = true, timer = 0, speed = 99, frame = B, lastframe = B, endanim = 120, END = true}
					
				else
					--Pay attention, kids! This is how you properly assault your fellow classmates!
					local HADFLIP = (ASSAULT and ASSAULT.flip) and true or false
					local DP = (HITS>1) and 1 or 0
					
					ASSAULT = {
					sprite2 = ANIM,
					timer = 0, --Counts up.
					frame = A, --The frame you're inf. 
					speed = 3, --animation's speed.
					attackCD = (ATTACK=="flurry" or xs.darkphase==3) and 4 or 6, --Time before you may attack again.
					lastframe = (ANIM==SPR2_TAL1) and D or D, --Do not exceed this frame!
					impact = (ANIM==SPR2_TAL1) and B or C, --Point of impact where you send your enemy flying.
					endanim = (ANIM==SPR2_TAL1) and 73 or 76, --Exit your animation after this much time passed.
					flip = ((HITS<2) and (p.cmd.sidemove<-8) or not (HADFLIP)) and true or false --You CAN start with a flip.
					}
					if (HITS > 4)
						if HITS == MAXHITS-1
							ASSAULT.endanim = max(10, $-30)
							ASSAULT.combolock = ASSAULT.endanim					
						elseif s.BAGflurry
							ASSAULT.endanim = max(10, $-14)
						end
					elseif AUTOJUMP
						ASSAULT.autojump = AUTOJUMP
					end
									
					
					local rADD = (ATTACK=="flurry") and 1 or (ANIM == SPR2_TAL2) and ANG1 or (ANG1*3)/2
					
					if ANIM == SPR2_TAL2 or (ATTACK == "ForceEndKick")
						ASSAULT.jumped = true --If you press jump again, you'll exit.
						if s.BIGTARGET
							s.spritexscale = 48000
							s.spriteyscale = P_RandomRange(75635, 115636)
						else
							s.spritexscale = 28000
							s.spriteyscale = P_RandomRange(95635, 145636)
						end
						
						so.spritexscale,s.spriteyscale = FRACUNIT+8200,FRACUNIT-15500
						so.spritexoffset,so.spriteyoffset = 0,-8<<16
						so.XSstretch = {timer = 5, Xspeed = 1600, Yspeed = 3100}	
						
						p.viewrollangle = $/2 + (rADD*((ASSAULT.flip) and SUPER or -SUPER) )
						
						if ATTACK=="flurry" or ATTACK=="ForceEndKick"
							if s.BAGflurry or ATTACK=="ForceEndKick" --Go to last semi-attack in chain if the player ends a flurry with a kick.
								if ATTACK=="ForceEndKick"
									CrudeTryMove(so,s, false)
									ASSAULT.endanim = min($,24)
								end
								S_StopSoundByID(so, sfx_xsvocb) S_StopSoundByID(so, sfx_xsvocc)
								XSon.Echoes(p,so,xs, "stop")
								
								local TABLE = {sfx_xsvocf,sfx_xsvoce,sfx_xsvocf,sfx_xsvocg} --(P_RandomRange(3,3+SUPER)==3) and {sfx_xsvopd,sfx_xsvop6,sfx_xsvop8,sfx_xsvopc,sfx_xsvope}
								local NUM = P_RandomRange(1, #TABLE)
								local GRUNT = TABLE[NUM]
								if GRUNT == so.XSlastattackgrunt
									GRUNT = ($==TABLE[#TABLE]) and TABLE[1] or TABLE[NUM+1]
								end
								XSVoice(p, so, xs, GRUNT, "ForceEcho")
								so.XSlastattackgrunt = GRUNT
								
								ATTACK = "attack"
								ASSAULT.type = "attack" 
								ASSAULT.cantcharge = true
								s.BAGcutcombo = true
								s.BAGhit = max($, MAXHITS-2)
								s.flags2 = $ & ~(MF2_DONTDRAW|MF2_SHADOW)
								so.flags2 = $ & ~(MF2_DONTDRAW|MF2_SHADOW)
								so.frame = $ & ~(FF_TRANSMASK|FF_HORIZONTALFLIP)
								
								S_StartSound(so, sfx_brakrx)
								
								s.momz = (15+(SUPER*3))*so.scale*SOFLIP
								s.momx,s.momy = $*(2+SUPER),$*(2+SUPER)
								p.camerascale = (p.pflags & PF_ANALOGMODE) and 20000 or 33000
								p.XSscalecam = 5
								
								local ghs = XSon.ChargeSpark(s, nil, 2<<16, nil, AST_ADD, 10, 34|FF_TRANS10)
								ghs.dispoffset = -148
								ghs.scalespeed = $*2
								ghs.momx,ghs.momy,ghs.momz = s.momx/6,s.momy/6,s.momz/6	
								
							else
								ATTACK = "attack" --You can prolong it for 1 more frame, then.
							end
						end
						
					else
						if s.BIGTARGET
							s.spritexscale = max(42000, (abs(s.momx)+abs(s.momy))/14 )
							s.spriteyscale = max( P_RandomRange(70000, 90000), abs(s.momz)/7)
						else
							s.spritexscale = max(54000, P_RandomRange(32000, 65636)+max(55000, ((abs(s.momx)+abs(s.momy))/14)))
							s.spriteyscale = P_RandomRange(42000, 55000)+max(38000, abs(s.momz)/7)
						end
						
						so.spritexscale,s.spriteyscale = FRACUNIT+9000,FRACUNIT+4500
						so.spriteyoffset = 4<<16
						so.spritexoffset = (ASSAULT.flip) and 10<<16 or -10<<16
						so.XSstretch = {timer = 5, Xspeed = 2000, Yspeed = 1100}	
						p.viewrollangle = $+ ( (rADD)*((ASSAULT.flip) and SUPER or -SUPER) )
						
					end
					XSon.ChaseBag(so,p,xs, s,t, true)
					xs.forceangle = {angle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), timer = 7, nosangle = true}
					p.drawangle = xs.forceangle.angle
					s.GIBstretch = {timer = 6, Xspeed = 28000, Yspeed = 16000}
					s.fuse = max($,8) s.tics = max($,8)
					
					--Unleash a Hyper Smash!
					if HYPERSMASH --and ATTACK == "attack"
					and not s.BAGflurry and not s.EarthSlammed and not s.BAGcutcombo 
					and (ANIM == SPR2_TAL2 or ANIM == SPR2_TAL1) and ASSAULT
					
					--	CONS_Printf(p, "\x83"+"Hyper Smash "+ATTACK+"!\x80 ANIM:"+spr2names[ANIM]+"\x84 HYPERSMASH:"+HYPERSMASH)
						local SWEETSPOT = (netgame==true and p!=server) and 74 or 71
						local BOOST = (HYPERSMASH<SWEETSPOT) and 15 or 1 --With good timing, you can boost the move's distance.
						local ZGAIN,NEWSPEED = 0,0
						
						XSon.Quake(s, (60+(BOOST*3))<<16, 8+(BOOST/2), (900+(BOOST*35))<<16)
						
						if ANIM == SPR2_TAL2
							ZGAIN = 6+(SUPER*(2+(BOOST/3))) --Min of 17. Max of 29. Can add 12+9
							NEWSPEED = 55+(SUPER*(5+BOOST)) --Min of 60. Max of 115.
							if (s.momz*SOFLIP < ZGAIN*so.scale)
								s.momz = ZGAIN*SOFLIP*so.scale
							end
							if ((abs(s.momx)+abs(s.momy)) < NEWSPEED*so.scale)
								P_InstaThrust(s, p.drawangle, NEWSPEED*so.scale)
							end
							if not s.GotHyperSmashed and P_TryMove(s, so.x, so.y, true)
								s.z = so.z+(so.height/3)
							end
						else
							ZGAIN = -20-(SUPER*(4+(BOOST/3))) --Min of 37. Max of 74.
							NEWSPEED = 38+(SUPER*(16+(BOOST/3))) --Min of 58. Max of 91.
							if (s.momz*SOFLIP > ZGAIN*so.scale)
								s.momz = ZGAIN*SOFLIP*so.scale
							end
							P_InstaThrust(s, p.drawangle, NEWSPEED*so.scale) --Always apply in this case.	
							P_SetObjectMomZ(so, (s.eflags & MFE_UNDERWATER) and 3<<16 or 11<<16)
						end
						s.SFXcanplay = true
						s.BAGhit = HITS+(min(3, 4-SUPER))
						s.GotHyperSmashed = {
						init = 0,
						timer = 60, 
						speed = max(NEWSPEED, abs(s.momx)+abs(s.momy)),
						momz = s.momz,
						boost = BOOST,
						zgain = ZGAIN,
						anim = ANIM
						}
						s.ROLLSPEED = ANG10*5
						if ((s.BAGdamage or 0) < 8<<16)
							XSon.AddDamage(s, 105000+(32000*SUPER))
						else
							XSon.AddDamage(s, 50000+(32000*SUPER))
						end
						
						local ghs = XSon.ChargeSpark(s, nil, 2<<16, nil, AST_ADD, 10, 34|FF_TRANS10)
						ghs.dispoffset = -148
						ghs.scalespeed = $*2
						ghs.momx,ghs.momy,ghs.momz = s.momx/6,s.momy/6,s.momz/6	
						
						so.momx,so.momy,so.momz = $/3,$/3,$/2
						ATTACK = "attack"
						ASSAULT.type = "attack"
						ASSAULT.chase = 5
						ASSAULT.didhyper = 1 --Did a Hyper Smash! Counts up.
						ASSAULT.timer = min($,1)
						ASSAULT.endanim = 43
						ASSAULT.cantcharge = true
						
						if BOOST == 15
							XSon.Flash(so, PAL_NUKE, 3, 999<<16)
							for i = -1,2
								local ghs = XSon.LightningStrike(s, SKINCOLOR_NOBLE)
								if abs(i) == 1
									ghs.spritexoffset = $+((30*i)<<16)
									if not (XS_CRAPPYPRIORITIES==1)
										ghs.rollangle = ANGLE_90*i
									end
								elseif i == 2
									ghs.frame = $|FF_VERTICALFLIP
								end
								ghs.dispoffset = -149
								ghs.momx,ghs.momy,ghs.momz = $+s.momx, $+s.momy, $+s.momz
								ghs.spritexscale,ghs.spriteyscale = $*3,$*3
								ghs.spriteyoffset = $-42<<16
								ghs.fuse,ghs.tics = $+5,$+5
							end	
							if ANIM == SPR2_TAL2	
								XSon.Sound(s, sfx_xssfe0, 225)	--YYH impact sound.
								XSon.Sound(so, sfx_xssfe0, 145)
								local ghs = XSon.LightningStrike(s, SKINCOLOR_NOBLE)
								ghs.tracer = s
								ghs.fuse,ghs.tics = $+5,$+5
								ghs.spritexscale,ghs.spriteyscale = $*3,$*3
							end
							XSon.Sound(so, sfx_xssfe3)
						--	XSon.Sound(s, sfx_xssfe3) XSon.Sound(so, sfx_xssfe2)
							p.viewrollangle = (leveltime%2) and ANG2*13 or -ANG2*13
							RB = (xs.FreeFlight) and xs.FreeFlight or so and so.XS_DarkTable
							if RB
								RB.forcetrail = 6
								RB.windlines  = 6
								RB.speedlines = 6
							end
						else
							p.viewrollangle = (leveltime%2) and ANG2*4 or -ANG2*4
						end	
						if s.BAGPdamage
							if (HYPERSMASH!=1)								
								XSon.Sound(s.tracer or s, sfx_xssfe0, 165)
								XSon.Sound(s.target or s, sfx_xssfe0, 255)
							end
							if (s.momz*SOFLIP < 1) and (s.momz*SOFLIP > -70*so.scale)
								s.momz = $*3
							end
							ASSAULT.stayZ = s.z 							
							s.BAGPhyper = true
						end			
					elseif ATTACK == "attack" and not s.BAGflurry
						s.GotHyperSmashed = nil
						if xs and xs.FreeFlight and not ASSAULT.didhyper
							if s.target and s.target.player
								CrudeTryMove(so, s.target, true)
							else
								CrudeTryMove(so, s, false)
							end
							P_Thrust(so, so.angle, -32*s.scale)
							so.momz = $/2
							so.flags = $ & ~MF_NOGRAVITY					
						end	
					end
				end
				so.XSassault = ASSAULT
				if not s.EarthSlammed and not (ASSAULT and ASSAULT.didhyper)
					RB = (xs.FreeFlight) and xs.FreeFlight or so and so.XS_DarkTable
					if (DIST > 64*s.scale) --Close the gap in an instant!
						if RB
							if ATTACK == "flurry"
								RB.forcetrail,RB.speedlines,RB.windlines = 0,0,1
								so.flags2 = $|MF2_DONTDRAW
							else
								RB.forcetrail,RB.speedlines,RB.windlines = 1,1,6
							end
						end	
						AssaultHoming(so, s, 234*s.scale, 64*so.scale, DIST)
					else --Get closer less aggressively.
						if RB
							RB.forcetrail,RB.speedlines,RB.windlines = 0,2,4
						end					
						AssaultHoming(so, s, 60*s.scale, 63*so.scale, DIST)
					end	
				end
			end
			ASSAULT = so.XSassault
			if ASSAULT and (tostring(ATTACK))
				ASSAULT.didhyper = HADHYPER
				if s.BAGhit --Count up the number of hits.
					s.BAGhit = $+1
				end			
				if ATTACK != "ongoing"
					ASSAULT.type = ATTACK
					if ATTACK == "attack" and ((s.BAGhit == 1) or not s.BAGhit)
						CrudeTryMove(so,s, false)
					end
				end
			end
		end
	end
end

--Cancel with a snapdive jump?
local function SnapDiveJump(p,s,xs, t, ATTACK, SPEED, DIRECT)
	local SUPER = XS_Super(p)
	
	S_StopSoundByID(t, sfx_s3kcds)
	xs.snapstickx,xs.snapsticky,xs.snapstickz, xs.snaptarget = t.x,t.y,s.z,nil	
	ATTACK.END = "bounce"
	XSon.AssaultAnim(p,s,xs, "bounce")
	CrudeTryMove(s,t, false)
	
	XSStartSnapDiveTrick(p, s, xs)
	XSSnapDiveBounce(p, s, xs, true) 
	xs.snapdivetrick = 10
	
	local UW = (s.eflags & MFE_UNDERWATER) and 3 or 1 
	
	P_SetObjectMomZ(s, (10+(SUPER*2))*(s.scale/UW))
	
	if type(SPEED) != "string"
		local ANG = R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy)+(xs.FreeFlight and ANGLE_180 or 0)
		P_InstaThrust(s, ANG, min(60*s.scale/UW, SPEED))
	elseif xs.FreeFlight
		s.momx,s.momy = -$,-$
	end
	if (DIRECT > 3) and not xs.FreeFlight
		local TURNANG = (p.cmd.angleturn<<16) + (R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))	
		local THRUST = s.scale/3 + ((s.scale/7)*SUPER)
		P_Thrust(s, TURNANG, DIRECT*(THRUST/UW))
	end
	local RB = (xs.FreeFlight) and xs.FreeFlight or s and s.XS_DarkTable
	if RB
		RB.speedlines = 2 RB.windlines = 8 RB.forcetrail = 5
	end	
	if SUPER >= 2
		XSon.Quake(s, 85<<16, 7, 400<<16)	
		XSon.EarthQuake(s, 3, 160*s.scale, 1, 116000, s.scale/16, ANG1*34, 32|FF_TRANS40, 7)
		XSon.Sound(s, sfx_xssfps, min(255, 80*SUPER))
	end
	if SUPER == 3
		XSon.GibSound(s, sfx_xssfp9, 255, 131)
		XSon.Flash(s, PAL_NUKE, 2, 600<<16)
		S_StopSoundByID(s, sfx_xssfx4) S_StopSoundByID(s, sfx_xssfx8)			
		local ghs = XSon.LightningStrike(s, SKINCOLOR_COBALT, s, (Z+2)|FF_TRANS10)
		ghs.spritexscale = $*2
		S_StartSound(ghs, sfx_xssfp9)
		ghs = XSon.SquashFlash(s)
		ghs.momz = $+(s.momz/2)
		if xs.FreeFlight
			xs.FreeFlight.PvPENERGY = 0
		end
	else
		XSon.Sound(s, sfx_jump)
	end
	local ZFLIP = P_MobjFlip(s)
	for i = -2,2
		local ghs = XS_RingBurstSparkles(s, nil, SKINCOLOR_RUBY)
		if ghs and ghs.valid
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
			ghs.spritexscale,ghs.spriteyscale = $*3,$*3
			P_Thrust(ghs, s.angle+(ANG60*i), 35*s.scale)
			ghs.momx,ghs.momy = $+(s.momx*3/4),$+(s.momy*3/4)
			ghs.momz = $+(s.momz*2/3)+(5*s.scale*ZFLIP)
		end
	end		
end

--local Groundslam"
local function GroundSlam(p,s,xs,t,ATTACK, SUPER, SPR2SUPER)
	if not (t and t.valid)
	or not (s and s.valid) 
		return --
	end
	
	if ATTACK.grounded == nil ATTACK.grounded = 0 end
	if (t.friction <= 64500)
		t.friction = 64500
	end
	local HEIGHT = max(20*s.scale, t.height)
	local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and abs((t.z+HEIGHT)-s.ceilingz) or abs(t.z-s.floorz)
	local GROUNDED = ATTACK.grounded
	local DIRECT = (abs(p.cmd.forwardmove)>=abs(p.cmd.sidemove)) and abs(p.cmd.forwardmove) or abs(p.cmd.sidemove)
	local TURNANG = p.drawangle
	local THRUSTTIME = 12+(SUPER*5)
	local SPEED = abs(t.momx)+abs(t.momy)
	local MOMANG = R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy)
	local SLOPEBOOST = 0
	local SMASH,SLAM = ATTACK.smashtime or 0, ATTACK.earthslam or 0
	local STT = (xs.FreeFlight) and 3 or 0
	local ZFLIP = P_MobjFlip(s)
	
	if (DIRECT > 4) and not SLAM --Sliding movement.
		TURNANG = (p.cmd.angleturn<<16) + (R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))		
		XSon.FreeFlightVector(t, TURNANG, 1, 2)
		if (ATTACK.grounded < THRUSTTIME)
			if (SPEED < (30+(SUPER*30))*s.scale)
				local THRUST = s.scale / min(GROUNDED+(12-(SUPER*2)), 15)
				P_Thrust(t, TURNANG, min(50, DIRECT*3/2) * THRUST ) 
			end
		end
	end
	if (s.eflags & (MFE_TOUCHLAVA|MFE_GOOWATER))
		ATTACK.END,ATTACK.jump = true,1
		p.cmd.buttons = $|BT_JUMP	
	end
	if ATTACK.noslope 
		ATTACK.noslope = $-1
	end
	if (FLOORZ < 36*s.scale) and (ATTACK.timer > 2) --Smashing them into the ground!
	
		local SEC,SLOPE = (s.subsector) and s.subsector.sector
		if SEC and not SLAM and not ATTACK.noslope --Forcing slope physics since we're not TECHNICALLY touching the ground.
			SLOPE = (s.eflags & MFE_VERTICALFLIP) and SEC.c_slope or SEC.f_slope
			if SLOPE and SLOPE.zdelta and not (SLOPE.flags & SL_NOPHYSICS) and (abs(SLOPE.zdelta) > 750)
				local NUM = (GROUNDED<=3) and 11 or 4
				local OLDSPEED = SPEED
				SLOPEBOOST = max(-s.scale*3, min(s.scale*3, SLOPE.zdelta*10))
				for i = 1,NUM
					local PSPEED = abs(t.momx)+abs(t.momy)
					P_Thrust(t, SLOPE.xydirection, SLOPEBOOST/2)
					if ( abs(t.momx)+abs(t.momy) < PSPEED)
						ATTACK.noslope = 5
						if (t.momz*ZFLIP < 8*s.scale)
							t.momz = 8*s.scale*ZFLIP
						end break --We lost speed, so stop here.
					end
				end
				SPEED = abs(t.momx)+abs(t.momy)
				if (SPEED >= OLDSPEED) and (SPEED > 22*s.scale)
					ATTACK.extendEA = ATTACK.extendEA and $+1 or 1 --Can extend slide if going down slopes.
					if (ATTACK.extendEA < 154)
						ATTACK.endanim = $+1
					end
				end
			end
		end	
		if not GROUNDED --Initial Ground Smash.
			P_RadiusAttack(t, s, 100*SUPER<<16, DMG_NUKE, true)
			XSon.AddDamage(t, 10000+(6500*SUPER))
			XSSpawnDust(t, SPEED+30<<16, 5)
			XSSpawnDust(s, max(SPEED/2, 3<<16), 8)
		end
		local RNG = P_RandomRange(-64,64)
		if not (XS_CRAPPYPRIORITIES==1)
			t.rollangle = ANGLE_90+(RNG*(ANG1/3))
		end
		t.spritexscale = FRACUNIT+(abs(RNG)*600)
		t.spriteyscale = t.spritexscale*2/3
		t.ROLLSPEED = 0
		t.BAGhit = 21 --Amount of times we've hit the bag.
		XSon.AddDamage(t, max(1000, (2000+(SUPER*2000)) - (GROUNDED*250) ) )
		t.flags = $ & ~MF_GRENADEBOUNCE
		t.renderflags = $|RF_FULLBRIGHT		
		
		if not SLAM
			if not S_SoundPlaying(t, sfx_s3kcds)
				S_StartSound(t, sfx_s3kcds)
			end		
			p.camerascale = ((p.pflags & PF_ANALOGMODE) and 10000 or 17000) + min(95000, GROUNDED*15000)
			p.aiming = max(-ANG1*15, ANG2-(GROUNDED*(ANG1/2)))
			local SHAKE = max(ANG1/20, ANG1*8 - GROUNDED*ANG1)
			p.viewrollangle = (leveltime%2) and -SHAKE or SHAKE
			ATTACK.grounded = $+1
			ATTACK.endanim = max($-1,0)
			ATTACK.attackCD = 0				
		end	
		if (ATTACK.grounded < (THRUSTTIME/2)) and SUPER != 1
			if ATTACK.spinangle == nil ATTACK.spinangle = p.drawangle end
			ATTACK.spinangle = $+ ((THRUSTTIME/2)-ATTACK.grounded)* (ANG2*5/2)
			p.drawangle = ATTACK.spinangle
		end
		s.sprite2 = SPR2_SKID|SPR2SUPER --Use this sprite explicitly!
		s.frame = $&~FF_FRAMEMASK
		
		local ghs 
		if (GROUNDED < 6) and not SLAM
			ghs = P_SpawnGhostMobj(t) --Smooshed enemy ghost
			ghs.scale = t.scale
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
			ghs.renderflags = (t.renderflags & ~RF_FULLDARK)|RF_FULLBRIGHT
			ghs.fuse,ghs.tics = 14,14
			ghs.spritexscale = t.spritexscale*4/3
			ghs.spriteyscale = t.spriteyscale/max(2, GROUNDED)
			ghs.destscale = 1
			ghs.scalespeed = t.scale/23
			ghs.dispoffset = -148
			if (GROUNDED%2)
				ghs.momx,ghs.momy = t.momx/2,t.momy/2
			end
		end
		if (SPEED > 6*s.scale) and not SLAM 
			for i = -1,1,2
				ghs = P_SpawnMobjFromMobj(t, t.momx+FixedMul(6*s.scale*i, cos(MOMANG+ANGLE_90)),t.momy+FixedMul(6*s.scale*i, sin(MOMANG+ANGLE_90)),-4*s.scale, MT_SPINDUST)
				ghs.state = S_SPINDUST2 ghs.tics,ghs.anim_duration = $/2,$/2
				ghs.spritexscale = $*3/2
				ghs.color,ghs.colorized = SKINCOLOR_BEIGE,true
				ghs.destscale = 99<<16 ghs.scalespeed = FRACUNIT/6 ghs.fuse = 26	
				P_Thrust(ghs, MOMANG-ANGLE_180+(ANG1*77), 25*s.scale*i)
			end
			if (SPEED > 12*s.scale)	
				ghs = XS_RingBurstDust(p, s, xs, 150*s.scale)
				if (GROUNDED > 7) and ghs and ghs.valid
					local TRANS = (10-FixedInt(SPEED/10))<<16
					if (GROUNDED > 7)
						TRANS = $+min(FF_TRANS40, ((GROUNDED/7)*FF_TRANS10))
					end
					TRANS = max(FF_TRANS40, min(FF_TRANS90, $))
					ghs.frame = ($ & ~FF_TRANSMASK)|TRANS
				end			
				if (GROUNDED < 33)
					XSon.DarkBody(s, nil, nil, (SST) and SKINCOLOR_FLAME or XSon.DarkAura(0), 2)	
					for i = -2,2
						if i == 0 continue end --
						ghs = P_SpawnMobjFromMobj(t,t.momx+FixedMul(8*s.scale*i, cos(MOMANG+ANGLE_90)), 
						t.momy+FixedMul(8*s.scale*i, sin(MOMANG+ANGLE_90)),t.height/4,MT_MINECARTSPARK)
						ghs.fuse = 15
						ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
						ghs.dispoffset = 33
						ghs.scale = s.scale/12
						P_InstaThrust(ghs, MOMANG+ANGLE_180+(ANG60*i), 22*s.scale)
						ghs.momz = 5*s.scale*ZFLIP
					end
					if (GROUNDED > 2)
						P_RadiusAttack(t, s, max(74<<16, min(300<<16, SPEED*SUPER)), DMG_NUKE, false)	
						if (GROUNDED%3==0)
							if not t.target.player
								XSon.gibs(t, s, 2, 110*s.scale, nil, true, t)
							end
							S_StartSound(t, sfx_s3k7e)
							if (GROUNDED < 20) and not ((s.eflags & MFE_TOUCHWATER) and not (s.eflags & MFE_UNDERWATER))
							and (SPEED > 22*s.scale)
								for i = -1,1 --Fling rocks around!
									ghs = XS_CraterSpew(s, MOMANG+(ANG60*i), 9)
									if ghs and ghs.valid
										ghs.momx,ghs.momy = $*5,$*5
										ghs.tics,ghs.fuse = 45,44
									end
								end
							end
							local ths,ghs = XS_QuickDust(t, SKINCOLOR_SUPERORANGE2, AST_ADD, nil, t)
							ths.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
							ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
						elseif (GROUNDED%2==0)
							if (GROUNDED < 26) and not ((s.eflags & MFE_TOUCHWATER) and not (s.eflags & MFE_UNDERWATER))
								XSon.Quake(s, min((16+(SUPER*3))<<16, max(4<<16, SPEED/4)), 3)
							end	
						end	
					end
				end
			end
		end
		
	else --Airborne and going down, then!
	
		local RNG = P_RandomRange(-64,64)
		t.ROLLSPEED = -1
		t.renderflags = $ & ~RF_FULLDARK
		t.spritexscale,t.spriteyscale = FRACUNIT+(RNG*100),FRACUNIT+(RNG*100)
		if not (XS_CRAPPYPRIORITIES==1)
			t.rollangle = RNG*(ANG1/5)
		end
		
		if not SLAM
			t.angle = $+ANGLE_45
			p.aiming = min(ANGLE_45, -ANG30+(ATTACK.timer*ANG1*3)) // (ANG1*59)-(ATTACK.timer*(ANG1)))
			p.camerascale = max(1<<15, 10<<16 -ATTACK.timer*40000)
			s.sprite2 = SPR2_FALL|SPR2SUPER
			s.frame = ($&~FF_FRAMEMASK) | ((ATTACK.timer%2) and B or A)		
			if t.momz
				local DARKAURA = SKINCOLOR_XSSUPERBLUR
				if xs.darkphase
					DARKAURA = XSon.DarkAura(0)
					if xs.darkphase == 3
						ATTACK.custom2 = 999<<16
					end
				end
				for i = 2,6,4
					local ghs = P_SpawnGhostMobj(s)
					ghs.frame = ($ & ~FF_TRANSMASK)|(i<<16)
					ghs.color,ghs.colorized = DARKAURA,true
					ghs.fuse,ghs.tics = 14-i,14-i
					ghs.destscale = 1
					ghs.angle = leveltime*ANGLE_45+(ANG10*i)
					ghs.dispoffset = -149+i
					ghs.momx,ghs.momy = t.momx/4,t.momy/4
					if (STT)
						ghs.spriteyscale = $*3/2
						ghs.blendmode = AST_ADD
					end					
					if i == 6
						ghs.momz = -t.momz/2
						
						local ghs2 = XSon.ElectricBody(ghs, nil, (SST) and SKINCOLOR_KETCHUP or SKINCOLOR_CRIMSON)
						ghs2.dispoffset = 133
						ghs2.frame = (leveltime%3==0) and ($+FF_TRANS20)|FF_HORIZONTALFLIP or $+FF_TRANS20
						ghs2.momz = ghs.momz
					end
				end
			end	
			if ATTACK.jump and (ATTACK.jump < 5) and (p.cmd.buttons & BT_JUMP) and (GROUNDED>2)
				P_Thrust(t, p.cmd.angleturn<<16, 20*s.scale)
				P_SetObjectMomZ(t, -33<<16, true)
				SnapDiveJump(p,s,xs, t, ATTACK, "earthslamjump", DIRECT/2) 
				return false --		
			elseif (abs(t.momz) < 106*s.scale) and not ATTACK.noslope
				P_SetObjectMomZ(t, (abs(t.momz)>40*s.scale) and -s.scale/3 or -s.scale*3, true)
			end	
		elseif (abs(t.momz) < 10*s.scale)
			P_SetObjectMomZ(s, 45000,true)
		end

		ATTACK.extendEA = ATTACK.extendEA and $+1 or 1
		if (ATTACK.extendEA < 50)
			ATTACK.endanim = $+1
		end	
	end

	if SLAM or SMASH
		if (SMASH < 13)
			FLOORZ = max(0, $-30*s.scale)
		end
		local SLAMFRAME = A
		if (FLOORZ >= 36*s.scale) --Not grounded, so float a bit!
			t.momz,s.momz = $/3,$/3
			P_SetObjectMomZ(s, -2<<16, true)
		end
		
		if (SMASH < 0) --Picking enemy up!

			if SMASH == -5
				t.dispoffset = -2
				t.spritexoffset,t.spriteyoffset = 0,0
				if not S_SoundPlaying(s, sfx_kc50)
					S_StartSound(s, sfx_kc50)
				end
				XS_RingBurstDust(p, s, xs, 350*s.scale)
				XSon.SwapStretch(p, s, xs)
				s.spritexscale,s.spriteyscale = FRACUNIT+9000,FRACUNIT-16000
				s.spritexoffset = 4<<16
				s.spriteyoffset = -6<<16
				s.XSstretch = {timer = 5, Xspeed = 2000, Yspeed = 3975}
			end
			SLAMFRAME = G

		else --DETROIDUH SMAAAAAAASHHUH!!
			t.dispoffset = 149
			if SMASH == 0
				if not S_SoundPlaying(s, sfx_kc50)
					S_StartSound(s, sfx_kc50)
				end
				t.spritexoffset,t.spriteyoffset = 0,0
				if (SLAM < 2)
					local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
					local SFLOOR = (s.eflags & MFE_VERTICALFLIP) and s.ceilingz or s.floorz
					local SFLOORZ = abs(s.z-SFLOOR)
					if (SFLOORZ < 45*s.scale) --Snap!
						s.z = SFLOOR 
					end
					if SLAM <= 1
						XSon.sparkles(p,s,xs, 38, 5, 5, 2<<16, DARKAURA, AST_ADD)
						XS_RingBurstSparkles(s, nil, DARKAURA)
					end
				end
				if not s.XSstretch
					XS_RingBurstDust(p, s, xs, 350*s.scale)
					XSon.SwapStretch(p, s, xs)
					s.spritexscale,s.spriteyscale = FRACUNIT+9000,FRACUNIT-16000
					s.spriteyoffset = -6<<16
					s.XSstretch = {timer = 5, Xspeed = 2000, Yspeed = 3975}	
				end
				SMASH = 1+(SLAM*3)
			elseif (SMASH > 7)
				local ghs
				SLAMFRAME = (SMASH < 13) and B or
				(SMASH < 15) and C or 
				(SMASH < 17) and D or 
				(SMASH < 21) and E or
				(SMASH < 29) and F or E 

				if SMASH == 9 --Glowing with power!
				--	ghs = XSon.ElectricBody(s, nil, SKINCOLOR_CRIMSON)
				--	ghs.dispoffset = 133
					
					ghs = XS_RingBurstShine(p, s, xs)
					if ghs and ghs.valid
						ghs.spritexoffset = $+11<<16
						ghs.spriteyoffset = $+29<<16
						ghs.tracer = nil
						ghs.dispoffset = 141
					end
				elseif SMASH == 12 --About to throw!
					p.camerascale = 3<<16
					local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
					
					ghs = XSon.TransformGFX(s, DARKAURA, I|FF_TRANS30)
					ghs.spritexscale,ghs.spriteyscale = $*(max(2, SLAM)),($*max(2,SLAM))/2
					ghs.sttspeed = 1
					ghs.dispoffset = 130
					ghs.fuse,ghs.sttfade = 11,true
					
					s.spritexscale,s.spriteyscale = FRACUNIT+25000,FRACUNIT+40000
					s.spriteyoffset = 4<<16
					s.XSstretch = {timer = 3, Xspeed = 9500, Yspeed = 15000}	
					
				elseif SMASH == 13
					if (DIRECT > 3)
						local MOVE = DIRECT*(s.scale/2)
						for i = 1,4
							P_TryMove(s, s.x+s.momx+FixedMul(MOVE, cos(TURNANG)), s.y+s.momy+FixedMul(MOVE, sin(TURNANG)), false)
						end
					end
				elseif (SMASH>=14) --Throw!
					local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
					t.flags = $ & ~MF_NOGRAVITY
					p.camerascale = (SLAM>=3 and 4 or 3)<<16
					p.XSscalecam = 24
					p.aiming = -ANG20
					
					if SMASH == 14 --Slam Impact!
						local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
						if SLAM != 2
							XSon.BurstWave(s) 
						end
						s.momx,s.momy,t.momy,t.momy = $/4,$/4,$/4,$/4  --Throw them down!
						t.spritexoffset, t.spriteyoffset = 0,0
						t.z = s.z+(s.height/2)
						t.momz = -77*s.scale*ZFLIP
						if (s.momz*ZFLIP < 12*s.scale)
							P_SetObjectMomZ(s, 4<<16, true)
						end
						t.GotSlammed = 99
						t.ROLLSPEED = ANGLE_45
						t.ROLLD = -1
						
						XSon.Sound(s, sfx_xssfpn)
						XS_MediumDust(s, (SLAM==3) and SKINCOLOR_XSSUPERBLUR or DARKAURA, (SLAM==3) and AST_REVERSESUBTRACT or AST_ADD, nil, nil, p.cmd.angleturn<<16) --	XS_MediumDust(s, COLOR, BLENDMODE, FRAME, WALLRUN, ANGLE)
						XS_RingBurstDust(p, s, xs, 500*s.scale)
						if (FLOORZ < 140*s.scale)
							if (SLAM <= 2)
								XSon.PuffDust(t)
								if SLAM <= 1
									XSon.SideSparks(t)
									XSSpawnDust(s, nil, nil, 11)
								end
							end
							P_NukeEnemies(t, s, (300+(200*SLAM))*s.scale)
							S_StartSound(s, sfx_xssfpl)
							XSon.Quake(s, (50+(SLAM*15))<<16, 12+SLAM)
							p.viewrollangle = (SLAM%2==0) and -ANG10 or ANG10
							XSon.AddDamage(t, 22666+(SLAM*5000) )
							for i = -2,SLAM --Fling rocks around!
								XS_CraterSpew(s, (p.cmd.angleturn<<16)+(ANG1*77*i), 12-i)
							end
							XSon.EarthQuake(s, 1+SLAM, ((70+(20*SLAM))*s.scale), (40+SLAM*6)*s.scale, 55536+(15000*SLAM),
							s.scale/(12-SLAM*3), ANG30, 32|FF_TRANS30,  5+(SLAM*10))						
							
							if SLAM == 3
								S_StartSound(s, sfx_kc3b)
							end
							local XYDIST = (80+(310*SLAM))*s.scale
							local ZDIST = XYDIST/3
							searchBlockmap("objects",function(s,m) --Destroy nearby environmental objects.
								if m.valid and (m.flags & (MF_MONITOR|MF_SCENERY|MF_SOLID|MF_PUSHABLE))
								and not (m.flags & (MF_ENEMY|MF_SPRING|MF_BOSS|MF_NOSECTOR|MF_NOCLIPTHING))
								and not m.WaitingXTree and not m.player
									if not (m.z > s.z+ZDIST) and not (s.z > m.z+ZDIST)
										XSon.ScenicCrash(p,s,xs,m, SLAM, nil, (10+(6*SLAM))<<16)
									end
								end
							end, s, s.x-XYDIST,s.x+XYDIST, s.y-XYDIST, s.y+XYDIST)							
						else
							XSon.Sound(t, sfx_xssfpq)
							p.viewrollangle = (SLAM%2==0) and -ANG1*4 or ANG1*4
							XSon.Quake(s, 55<<16, 12)
						end
						ghs = P_SpawnGhostMobj(t) --Enemy ghost.
						ghs.scale = t.scale
						ghs.frame = t.frame & ~FF_TRANSMASK
						ghs.rollangle = t.rollangle+ANGLE_45
						ghs.spritexoffset = -11<<16
						ghs.dispoffset = 149
						ghs.fuse,ghs.tics = 6,6
						ghs.destscale = 99<<16
						if SLAM != 1
							SMASH = $+1
						end

					elseif SMASH == 16 --Second impact point.
						
					/*	ghs = XS_CircleWave(s, 2, DARKAURA, 22)
						ghs.frame = E|FF_TRANS20
						P_SetObjectMomZ(ghs, 9*s.scale, true) */
					--	if SLAM != 3
							XSon.sparkles(p,s,xs, 38, 5, 5, 2<<16, DARKAURA, AST_ADD)
							XSSpawnJumpDust(t)
					--	end

						if (FLOORZ < 140*s.scale)
							P_NukeEnemies(t, s, (200+(250*SLAM))*s.scale)
							XSon.gibs(s, t, 5, 110*s.scale, nil, true, t)
							XSon.GibSound(t, sfx_xssfp9, 255, 99)
							XSon.GibSound(s, sfx_xssfpm, 255, 99)
							XSon.AddDamage(t, 10666+(SLAM*1500))
							S_StopSoundByID(s, sfx_xssfpl)	S_StopSoundByID(s, sfx_xssfpn)
							for i = 0,max(1, SLAM-1)
								XSon.GibSound(t, sfx_xssfpl)
							end
							XSon.EarthQuake(s, SLAM, ((170+(40*SLAM))*s.scale), (44+SLAM*7)*s.scale, 45536+(10000*SLAM), s.scale/(15-SLAM*3), ANG20, 32|FF_TRANS40, 3+(SLAM*10))
							for i = -2,SLAM --Fling rocks around!
								local ghs = XS_CraterSpew(s, (p.cmd.angleturn<<16)+(ANG60*i), 10+i)
								if SLAM>=2 and ghs and ghs.valid
									ghs.FLAMEDEBRIS = true
									if SLAM==3 and abs(i) == 2
										ghs.SMOKINGDEBRIS = 1
										ghs.fuse = $+10
									end
									ghs.spritexscale,ghs.spriteyscale = $*(SLAM+1)/2, $*(SLAM+1)/2
									ghs.z = $+(3*s.scale*ZFLIP)
									ghs.momz = $+(6*s.scale*ZFLIP)
									ghs.momx,ghs.momy = $*(2+SLAM),$*(2+SLAM)									
									P_Thrust(ghs, R_PointToAngle2(ghs.x, ghs.y, ghs.x+ghs.momx, ghs.y+ghs.momy), (SLAM*6)*s.scale) 
								end
							end
							XSon.PuffDust(t, t.color or SKINCOLOR_BEIGE, FF_TRANS50-(SLAM<<16))
							XSon.Quake(s, 75+(SLAM*15)<<16, 8+(SLAM*3))
							if (SLAM > 1)
								XSon.Flash(s, PAL_NUKE, SLAM*2, (400*SLAM)<<16)
								XSon.SmokeCircle(s, (SLAM==3) and 5 or 7, 111*s.scale)
								if SLAM == 3
									XSon.SmokeCircle(s, 8, 500*s.scale, nil, 79500+(30000*SLAM))
									S_StartSound(t, sfx_kc40)
								end	
							end
						else
							XSon.SquashFlash(t)
							XSon.sparkles(p,t,xs, 12, 2, 2, 1, DARKAURA, AST_ADD)
							ATTACK.endanim = min($, 9) --Welp.
						end
						if SLAM != 1
							SMASH = $+1
						end			

					elseif (SMASH%2==0) and (SMASH>=16) --Further effects.

						if (FLOORZ < 210*s.scale)
							if SLAM == 1
								if (SMASH%4==0) or (abs(t.momz)>30*s.scale)
									ghs = XS_RingBurstDust(p, t, xs, 350*s.scale)
									if ghs and ghs.valid
										ghs.frame = $+FF_TRANS30
										ghs.spritexscale = $/max(2, (SMASH-17))	
									end
								end
							end
							if (SMASH < 27)
								local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
								local SIZE = 15+SMASH+(SLAM*13) --52-ish?
								XSon.PuffDust(t, DARKAURA, FF_TRANS60-(SLAM<<16))
								if SLAM != 3
									XSon.EarthQuake(s, 2, ((280+(75*SLAM))*s.scale), (50+SLAM*9)*s.scale, 25536+(12500*SLAM), s.scale/(15-SLAM*2), ANG1*15, 32|FF_TRANS50, 1+(SLAM*8))
								end
								if (SLAM >= 2)
									for i = 1,SLAM
										XSon.SmokeCircle(t, 4+i, (SIZE*s.scale*i*3)/2, nil, 76536+(i*6000), nil, DARKAURA, ((i*2)+(5*SLAM))<<16)
									end
									if (SLAM >= 3) --SKINCOLOR_SALMON+(SMASH-14)
									--	XSon.BurstWave(s)
										XS_MediumDust(t, DARKAURA, AST_ADD, Q|FF_TRANS60, nil, SMASH*ANGLE_22h) --Large dustwaves.
										XSon.SmokeCircle(t, 7, SIZE*s.scale*6, nil, 145655, nil, SKINCOLOR_RUBY, 10<<16)
									end
								end
								
							end
						end						
					end
					if (SMASH < 18)
						local NSMASH = SMASH
						if SLAM == 2 or SLAM == 3
							NSMASH = SMASH-1
						end
						if (FLOORZ < 145*s.scale)
						and (NSMASH == 14 or NSMASH == 16 or SMASH == 0 or NSMASH==0 or SMASH==14)
							local DARKAURA if xs.darkphase DARKAURA = XSon.DarkAura(0) end
							
							ghs = XS_CircleWave(s, 2, SKINCOLOR_XSSUPERBLUR, 10, nil, p.cmd.angleturn<<16)
							ghs.sprite = SPR_XSG2
							ghs.scalespeed = 115356
							ghs.blendmode = (xs.darkphase) and AST_REVERSESUBTRACT or AST_BLENDMODE
							ghs.frame = 29|FF_TRANS30 --XSG230 or XSG1 49 ("n") ?
							ghs.sttfade = true
							ghs.tracer,ghs.ditchtracer = nil,nil 
							ghs.z = s.floorz+(s.height/16*ZFLIP)
							ghs.momz = (3<<16)*ZFLIP
							ghs.spritexscale,ghs.spriteyscale = $*2/3, $*2/3
							
							if (NSMASH == 14) and SLAM >= 2
								local LNO = (SLAM==3) and 3 or 1
								for i = -LNO,LNO
									if i == 0 continue end --
									ghs = XSon.LightningStrike(s, DARKAURA, nil, true)
									ghs.spritexoffset = $+((34*i)<<16)
								end
								ghs = XSon.LightningStrike(t, SKINCOLOR_RUBY, nil, true)
								ghs.scale = $*SLAM
								ghs.dispoffset = 148
								ghs.spritexscale = $*((SLAM-1)*2)
							elseif NSMASH == 16 and SLAM == 2
								XSon.BurstWave(s)
							end
						end
						local SHAKE = (24-SMASH)*ANG1
						p.viewrollangle = (SMASH%2) and -SHAKE or SHAKE
						XSon.DarkBody(s, nil, nil, DARKAURA, 2)	
					/*	for i = -2,2
							if i == 0 continue end --
							ghs = P_SpawnMobjFromMobj(t,t.momx+FixedMul(8*s.scale*i, cos(MOMANG+ANGLE_90)),t.momy+FixedMul(8*s.scale*i, sin(MOMANG+ANGLE_90)),t.height/4,MT_MINECARTSPARK)
							ghs.fuse = 15
							ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
							ghs.dispoffset = 33
							ghs.scale = s.scale/12
							P_InstaThrust(ghs, MOMANG+ANGLE_180+(ANG60*i), 22*s.scale)
							ghs.momz = 5*s.scale*ZFLIP
						end	
						if (FLOORZ < 48*s.scale)
							for i = -1,1,2
								ghs = P_SpawnMobjFromMobj(t, t.momx+FixedMul(6*s.scale*i, cos(MOMANG+ANGLE_90)),t.momy+FixedMul(6*s.scale*i, sin(MOMANG+ANGLE_90)),-4*s.scale, MT_SPINDUST)
								ghs.state = S_SPINDUST2 ghs.tics,ghs.anim_duration = $/2,$/2
								ghs.spritexscale = $*3/2
								ghs.color,ghs.colorized = SKINCOLOR_BEIGE,true
								ghs.destscale = 99<<16 ghs.scalespeed = FRACUNIT/6 ghs.fuse = 26	
								P_Thrust(ghs, MOMANG-ANGLE_180+(ANG1*77), 25*s.scale*i)
							end	
						end */
					end					
				end
			end
			SLAMFRAME = min(F, $) --safety.	
		end
		
		s.sprite2 = SPR2_TAL4|FF_SPR2SUPER
		s.frame = ($ & ~(FF_FRAMEMASK|FF_HORIZONTALFLIP))|SLAMFRAME
		
		if (SMASH < 14) --Pickup part.
			if (s.floorz < s.z-(s.scale*2)) and not (s.eflags & MFE_VERTICALFLIP) 
				s.z = $-(s.scale*2)
			end
			p.aiming = SMASH*(-ANG1/4)
			p.camerascale = FRACUNIT
			p.XSscalecam = 6
			t.flags = $|MF_NOGRAVITY
			t.dispoffset = 149
			t.momz = 0
			t.rollangle = ANGLE_22h
			P_TryMove(t, s.x, s.y)
			
			local SZ = (s.z+s.height) + (SMASH<0 and -31*s.scale or SMASH<7 and 25*s.scale or 36*s.scale)
			t.z = FixedDiv(FixedDiv(SZ, s.spriteyscale), FRACUNIT)
			if t.momx or t.momy
				t.momx,t.momy = FixedDiv(FixedMul($,6),7),FixedDiv(FixedMul($,6),7)
			end
			s.momx,s.momy,s.momz = t.momx,t.momy,t.momz --Copy the bag's momentum.
		else
			local SFLOORZ = (s.eflags & MFE_VERTICALFLIP) and abs(s.z-s.ceilingz) or abs(s.z-s.floorz)
			if (SMASH > 15) and (SFLOORZ > 110*s.scale)
				ATTACK.endanim = max($-3,0)
			else
				ATTACK.endanim = 64-(SLAM*4)
			end
			if (FLOORZ < 140*s.scale) --Use this to portray an impact.
				P_TryMove(t, s.x, s.y)
				if not (XS_CRAPPYPRIORITIES==1)
					t.rollangle = (leveltime*ANGLE_45)
				end
				t.spritexoffset = (12-SMASH)*-149356	
				t.spriteyoffset = (12-SMASH)*109356	
				if SMASH == 20 and SLAM >= 2
					XSon.GibSound(s, sfx_xssfp9, 255, 160)	
				end				
			else --Or do this if we're too far above the floor.
				t.spritexoffset,t.spriteyoffset = $/2,$/2
				if (t.momz*ZFLIP < 80*t.scale) and SMASH == 16 or (SFLOORZ > 300*s.scale)
					P_SetObjectMomZ(t, -80<<16)
				end
			end
			if (SMASH < 21)
				local ZAIM = (36-SMASH)
				p.aiming = (-ZAIM)*ANG1				
			else	
				p.aiming = 0
				if xs.darkphase
					XSon.DarkBody(s, nil, nil, XSon.DarkAura(0), 1)		
				end
			end
			s.momx,s.momy = $/2,$/2
			if (s.momz*ZFLIP < 2*s.scale) --Floaty behaviour after throwing.
				if (s.momz*ZFLIP < 0)
					s.momz = $/3
					P_SetObjectMomZ(s, 1<<15, true)
				end
				P_SetObjectMomZ(s, 1<<15, true)
			end					
		end
		ATTACK.smashtime = $+1
		
	else --Not doing the earthslam
		
		t.angle = p.drawangle+ANGLE_180
		if t.player
			t.player.drawangle = p.drawangle+ANGLE_180
			if not P_IsObjectOnGround(t)
				local TAR = t
				if (s.z > TAR.z)
					if (s.z < TAR.ceilingz)
						TAR.z = s.z
					else
						TAR.z = TAR.ceilingz
					end
				elseif not P_IsObjectOnGround(t.target)
					if (s.z > TAR.floorz)
						TAR.z = s.z
					else
						TAR.z = TAR.floorz
					end	
				end
				t.momz = s.momz*2
			end			
		end
		
		P_TryMove(s, t.x-(t.momx/4), t.y-(t.momy/4), true)	
		if (t.z+HEIGHT <= t.ceilingz) and (t.z+HEIGHT >= t.floorz)
			s.z = t.z+HEIGHT
		end
		if (t.momz*ZFLIP>0) or SLOPEBOOST and not P_IsObjectOnGround(t) --No more bouncing!
			t.momz = (GROUNDED) and -max(18, 50-GROUNDED)*s.scale or -50*s.scale
		end
		s.momx,s.momy,s.momz = t.momx,t.momy,t.momz	--Copy the bag's momentum.
		p.XSscalecam = 6	
	end
	t.renderflags = $ & ~RF_FULLDARK
	
	if GROUNDED --waaaaaa finishers
		local SLAMINPUT = false
		if ATTACK.custom2 and (ATTACK.custom2==1 or SLAM and (ATTACK.custom2 < 11))
		or SLAM and ATTACK.spin and (ATTACK.spin < 11) and (p.cmd.buttons & BT_SPIN)
			if (SLAM < 3) and (SMASH == 0 or (SMASH > 16)) and GROUNDED --and (FLOORZ < 36*s.scale) and (p.cmd.buttons & BT_CUSTOM2)	
				SLAMINPUT = true
			end
		end
		if SLAMINPUT and xs.darkphase == 3
			if not SLAM or (FixedHypot(FixedHypot(t.x-s.x, t.y-s.y), t.z-s.z)< 125*s.scale) --Generous enough range, I think.
				XSon.Sound(s, (SLAM>=2) and sfx_s3k56 or (SLAM==1) and sfx_cdfm17 or sfx_kc44) --sfx_s3k56
				ATTACK.chase = 0
				ATTACK.combolock = 99<<16
				ATTACK.timer = 11
				ATTACK.grounded = 3
				ATTACK.endanim = max($, 60-(SLAM*4))
				ATTACK.earthslam  = SLAM+1 --Can perform up to 3!
				ATTACK.smashtime = (SLAM) and -6 or 0 --Timer
				ATTACK.jump,ATTACK.spin,ATTACK.custom1,ATTACK.custom2 = 999<<16,999<<16,999<<16,999<<16
				
				S_StopSoundByID(t, sfx_s3kcds)
				t.momz = $/3
				t.z = $+(s.scale*16*ZFLIP)
				t.flags = $|MF_NOGRAVITY
				t.EarthSlammed = SLAM+1
				t.SFXcanplay,t.BAGcutcombo = true,nil
				t.CRASHcanDMG,t.GotSlammed = 0,0
				if SLAM == 0
					CrudeTryMove(s,t, false)
				end
			end
		end	
		if ATTACK.earthslam
			if (SMASH > 15) and (FLOORZ < 55*s.scale) --Jump out of the earthslam?
				local JUMPINPUT = (ATTACK.jump and (ATTACK.jump < 18) and (p.cmd.buttons & BT_JUMP)) and true or false
				if JUMPINPUT==true and (SLAM < 3)
					P_Thrust(t, p.cmd.angleturn<<16, 33*s.scale)
					P_SetObjectMomZ(t, -33<<16, true)
					SnapDiveJump(p,s,xs, t, ATTACK, "earthslamjump", DIRECT/2)
					P_SetObjectMomZ(s, 6<<16, true)
					return false --
				elseif JUMPINPUT==true
				or ATTACK.custom1 and (ATTACK.custom1 < 18) and (p.cmd.buttons & BT_CUSTOM1)
					XSon.Echoes(p,s,xs, "stop")
					local ghs = XS_CircleWave(s, 2, SKINCOLOR_SUPERGOLD2)
					ghs.frame = $+FF_TRANS40
					ghs.scalespeed = $*2
					ghs.momx,ghs.momy,ghs.momz = $+s.momx/2,$+s.momy/2,$+s.momz/2
					S_StartSound(ghs, sfx_shattr)
					p.camerascale = (p.pflags & PF_ANALOGMODE) and 30000 or 43000
					p.XSscalecam = 5
					XSon.SandBag(t, nil, "finish")	return false --
				end
			end
		else --if not ATTACK.earthslam
			if ATTACK.spin and (ATTACK.spin < 7) and (p.cmd.buttons & BT_SPIN)
			or SLAMINPUT
				S_StopSoundByID(t, sfx_s3kcds)
				t.BAGcutcombo = true
				p.cmd.buttons = $|BT_JUMP & ~BT_SPIN
				if not xs.FreeFlight
					p.aiming = 0
				end
				local TARGET = t
				XSon.SandBag(t, "ForceEndKick")
				
				ATTACK = s.XSassault t = TARGET
				if ATTACK and t and t.valid
					P_InstaThrust(t, p.drawangle, (90+(SUPER*20))*s.scale)
					P_SetObjectMomZ(t, (15+SUPER)*s.scale)
					
					XSon.ChaseBag(s,p,xs,t,t.target)
					t.ROLLSPEED = ANGLE_45
					ATTACK.combolock = 99<<16
					ATTACK.endanim = 21
					AssaultHoming(s, t, 24*t.scale, 169*s.scale, 99<<16)
					ATTACK.chase = 0
					P_NukeEnemies(t, s, (250+(66*SUPER))*s.scale)
				end
				s.z = $+(s.scale*P_MobjFlip(s))
				P_SetObjectMomZ(s, 80*s.scale)
				s.momx,s.momy = $/2,$/2 return false --
			end	
			if ATTACK.jump and (ATTACK.jump < 7) and (p.cmd.buttons & BT_JUMP)
				SnapDiveJump(p,s,xs, t, ATTACK, SPEED, DIRECT) return false --		
			end
		end
	end	
end

--End the current assault.
local function CancelAssaultAnim(p,s,xs, type, ATTACK, CSPRITE2)
	p.viewrollangle = 0
	S_StopSoundByID(s, sfx_xssfp1) S_StopSoundByID(s, sfx_xssfpa)
	
	if xs.FreeFlight
		xs.frontflip = 0
		s.state = S_PLAY_XDIVEFLIP1
		s.tics,s.anim_duration = $+3,$+3
		P_InstaThrust(s, s.angle, -26*s.scale)
		P_SetObjectMomZ(s, 15<<16)
		XSon.ElectricBody(s, 11, SKINCOLOR_SKY)
		for i = 1,3
			local ghs = XS_CircleWave(s, 1, nil, 10, i<<16)
			if ghs and ghs.valid
				ghs.scalespeed = $*i
				ghs.frame = $+(i<<16)
				ghs.momx,ghs.momy,ghs.momz = s.momx/i,s.momy/i, $+(s.momz/i)
			end
		end
		XSon.sparkles(p, s, xs, 65, 10, 11)
		XS_RingBurstSparkles(s)	
		S_StartSound(s, sfx_xssfe4)
		local FF = xs.FreeFlight
		FF.forcetrail = 4
		FF.speedlines = 4
		FF.noXYcontrol = 17
		FF.snapCD,FF.lambentCD = 11,11
		if FF.LockOn and FF.LockOn.valid
			FF.refreshC1 = 21
			P_Thrust(FF.LockOn, s.angle, 30*s.scale)
		end
	else
		local ZFLIP = P_MobjFlip(s)
		s.flags2 = $ & ~MF2_DONTDRAW
		p.aiming = 0
		if (s.momz*ZFLIP > 11*s.scale) and not (s.eflags & MFE_SPRUNG) and not p.powers[pw_carry]
			s.momz = max(11*s.scale, abs(s.momz)/4)*ZFLIP
		end
		if (p.speed > 77*s.scale)
			P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 77*s.scale)
		end
		if ATTACK.END != "bounce"	
			if CSPRITE2 == SPR2_TAL1 or CSPRITE2 == SPR2_TAL2 --If we're still in an attacking animation...
			
				p.pflags = $|PF_USEDOWN|PF_JUMPDOWN & ~(PF_NOJUMPDAMAGE|PF_STARTJUMP)
				
				if (p.pflags & PF_SPINNING) or (p.powers[pw_carry] == CR_ZOOMTUBE) 
				or (p.powers[pw_carry] == CR_MACESPIN) or p.homing --idk why X Sonic would have p.homing, tho'
					s.state = S_PLAY_ROLL
				elseif P_IsObjectOnGround(s)
					s.state = (p.speed>p.runspeed) and S_PLAY_RUN or p.speed and S_PLAY_WALK or S_PLAY_STND
					p.pflags = $ & ~(PF_JUMPED|PF_THOKKED)
				else
					xs.frontflip = 13
					s.state = S_PLAY_XDIVEFLIP1//(s.momz*ZFLIP>0) and S_PLAY_XDIVEFLIP1 or S_PLAY_FALL
					p.pflags = $|PF_JUMPED & ~(PF_THOKKED|PF_SPINNING)
				end
			end
			if ATTACK and ATTACK.MOMX != nil and xs.darkphase and ATTACK.END != "bounce" --Keep your momentum from before!
				local UW = (s.eflags & MFE_UNDERWATER) and 3 or 1 
				local SPEED = s.XSbag and s.XSbag.SPEED
				if not SPEED
					SPEED = abs(ATTACK.MOMX)+abs(ATTACK.MOMY)
				end
				local DIRECT = (abs(p.cmd.forwardmove)>=abs(p.cmd.sidemove)) and abs(p.cmd.forwardmove) or abs(p.cmd.sidemove)		
				local MOMANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)				
				local SUPER = XS_Super(p)
				
				s.state = S_PLAY_XDIVEFLIP1
				s.momz = (max(7*s.scale/UW, min(14*s.scale/UW, abs(ATTACK.MOMZ/2)))*ZFLIP )
				
				p.pflags = $|PF_JUMPED & ~(PF_THOKKED|PF_JUMPDOWN|PF_NOJUMPDAMAGE|PF_STARTJUMP)
				if type == "nojumphold"
					xs.jump = 99<<16
					p.pflags = $|PF_JUMPDOWN
				else
					xs.jump = (p.cmd.buttons & BT_JUMP) and 1 or 0
					if xs.jump == 1 --Transition into another dark rush.
						xs.forceangle = nil
						XSStartSnapDive(p, s, xs) 
						p.cmd.buttons = $|BT_JUMP & ~(BT_SPIN|BT_CUSTOM1)
						if s.XS_DarkTable and s.XS_DarkTable.darkrush --Gain height!
							local MOMZ = 10+(2*xs.darkphase)
						--	if (s.momz*ZFLIP < MOMZ*s.scale)
								P_SetObjectMomZ(s, MOMZ<<16)
						--	end
						end
					end
				end
				xs.frontflip = ((p.speed>44*s.scale) or (CSPRITE2==SPR2_TAL1)) and 13 or 0
				
				P_InstaThrust(s, MOMANG, min((40+SUPER*10)*s.scale/UW, SPEED)) --Horizontal momentum!
				if (DIRECT > 2)
					local TURNANG = (p.cmd.angleturn<<16) + (R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
					local THRUST = s.scale/4 + ((s.scale/6)*SUPER)
					P_Thrust(s, TURNANG, DIRECT*(THRUST/UW))
					if s.XS_DarkTable
						s.XS_DarkTable.speedlines = 4 s.XS_DarkTable.windlines = 6 s.XS_DarkTable.forcetrail = 2
					end	
				end			
				if xs.forceangle and xs.forceangle.timer
					xs.forceangle.timer = max($,1)
				end
			end	
		end
	end
	
	if s.XSbag
		if s.XSbag.valid
			RestoreTarget(s.XSbag, "restore", true, s)
			s.XSbag.tracer = nil
		end s.XSbag = nil
	end
	xs.spin = 999<<16
	s.XSassault,s.XSAssaultT = nil 
end

--Attack animations! AssaultAnim" AttackAnim". Called from PostThinkFrame.
XSon.AssaultAnim = function(p,s,xs, type)
	local CSPRITE2, ATTACK = (s.sprite2 & ~FF_SPR2SUPER),s.XSassault
	
	--Cancel conditions
	if not ATTACK or ATTACK.END
	or (ATTACK.timer >= ATTACK.endanim) --Extended the animation plenty.
	or p.powers[pw_carry] or xs.ringburst or p.xswallrun
	or (p.panim == PA_PAIN) or (s.state == S_PLAY_GASP)
 	or (s.eflags & MFE_SPRUNG) --or not (s.XSbag and s.XSbag.valid)
	or not xs.darkphase and not xs.supersonic
	or xs.FreeFlight and not (s.XSbag and s.XSbag.valid)
		CancelAssaultAnim(p,s,xs, type, ATTACK, CSPRITE2) return false --Done!
	end
	
	--Adjust player properties.
	if s.state != S_PLAY_FALL s.state = S_PLAY_FALL end
	if s.XS_DarkTable s.XS_DarkTable.noviolence = 0 end
	ImBusyRightNow(p,s,xs) --This lets the code focus on combat.
	
	--Timers
	s.XSresist = 11
	ATTACK.timer = $+1
	if ATTACK.attackCD --How long before we can attack again.
		ATTACK.attackCD = $-1
	end
	if ATTACK.combolock --Can't follow up for this long
		ATTACK.combolock = $-1
		ATTACK.jumppressed,ATTACK.spinpressed = nil
	end
	if ATTACK.didhyper --Counts up.
		ATTACK.didhyper = $+1
	end
	if type == nil and ATTACK.type --Set attack type
		type = ATTACK.type
	end
	local ZFLIP = P_MobjFlip(s)
	local MAXFRAME = D
	if ATTACK.jump == nil --Since controls weren't set yet, set them to max for now!
		if not ATTACK.didhyper
			ATTACK.chase = 19
		end
		ATTACK.charged = 0
		local NEWNO = (type!="flurry" and type!="groundslam") and 999<<16 or 1
		ATTACK.spin = (p.cmd.buttons & BT_SPIN) and NEWNO or 0
		ATTACK.jump = (p.cmd.buttons & BT_JUMP) and NEWNO or 0
		ATTACK.custom1 = (p.cmd.buttons & BT_CUSTOM1) and NEWNO or 0
		ATTACK.custom2 = (p.cmd.buttons & BT_CUSTOM2) and NEWNO or 0		
		
	else
		if (ATTACK.frame >= ATTACK.lastframe)
		
			local ESPRITE2 = (ATTACK.sprite2 & ~FF_SPR2SUPER)
			if (ESPRITE2==SPR2_TAL1 or ESPRITE2==SPR2_TAL2)
			
				if (ATTACK.timer > 4) and (s.momz*ZFLIP < 2*s.scale)
				
					local FLOORZ = (s.eflags & MFE_VERTICALFLIP) and abs((s.z+s.height)-s.ceilingz) or abs(s.z-s.floorz)
					if (FLOORZ < 52*s.scale)
						if (s.eflags & MFE_JUSTHITFLOOR) and not S_SoundPlaying(s, sfx_s3k8d)
							S_StartSound(s, sfx_s3k8d)
						end					
						if (p.speed > 20*s.scale) 
							if ATTACK.type != "groundslam" or ATTACK.earthslam
								if (ATTACK.timer%2==0)
									local ghs = XS_RingBurstDust(p, s, xs, 150*s.scale)
									if ghs and ghs.valid
										ghs.spritexscale = $*2/3
										ghs.frame = $+FRACUNIT*2
									end
								end
								for i = -1,1,2
									local ghs = P_SpawnMobjFromMobj(s, s.momx,s.momy,-4*s.scale, MT_SPINDUST)
									ghs.state = S_SPINDUST2 ghs.tics,ghs.anim_duration = $/2,$/2 
									ghs.color,ghs.colorized = SKINCOLOR_BEIGE,true
									ghs.destscale = 99<<16 ghs.scalespeed = $*2 ghs.fuse = 25
									P_Thrust(ghs, s.angle+(ANGLE_22h*i), -24*s.scale)
								end							
							end
							if not S_SoundPlaying(s, sfx_s3k7e)
								S_StartSound(s, sfx_s3k7e)
							end
						end
						if not ((p.cmd.buttons & BT_JUMP) and (ATTACK.jump < 999<<16))
						and not ((p.cmd.buttons & BT_SPIN) and (ATTACK.spin < 999<<16))
						and not (ATTACK.type == "groundslam" and (p.cmd.buttons & BT_CUSTOM2))
						and not ATTACK.earthslam
							local MIN = (p.speed>20*s.scale) and 1 or 2
							if not (abs(p.cmd.forwardmove) > 10) and not (abs(p.cmd.sidemove) > 10)
								MIN = $+2
							end
							ATTACK.endanim = max($-MIN,0)
						end
					end
				end
				if (ATTACK.timer%3==0) and not ATTACK.earthslam
					if ESPRITE2==SPR2_TAL2
						local SUP = (xs.supersonic or xs.darkphase==3) and 1 or 0
						ATTACK.frame = (ATTACK.frame==(E+SUP)) and D+SUP or E+SUP
					else
						ATTACK.frame = (ATTACK.frame==D) and C or D
					end
				end
				MAXFRAME = ATTACK.frame
			end
		elseif (ATTACK.timer % ATTACK.speed == 0) --Advance to the next frame?
			ATTACK.frame = min(ATTACK.lastframe, $+1) 
		end	
		if ATTACK.chase --Time to keep chasing enemy for.
			ATTACK.chase = $-1
		end	
		ATTACK.jump = (p.cmd.buttons & BT_JUMP) and $+1 or 0 --Kick!
		ATTACK.spin = (p.cmd.buttons & BT_SPIN) and $+1 or 0 --Punch!
		ATTACK.custom1 = (p.cmd.buttons & BT_CUSTOM1) and $+1 or 0 --Dislodge!
		ATTACK.custom2 = (p.cmd.buttons & BT_CUSTOM2) and $+1 or 0 --Stomp!
		if (p.cmd.buttons & BT_JUMP) and ATTACK.jump <= 1
			ATTACK.jumppressed = true
		end
		if ATTACK.spin == 1 and (p.cmd.buttons & BT_SPIN)
			ATTACK.spinpressed = true
		end
	end

	local SUPER = XS_Super(p)
	local SPR2SUPER = (SUPER==3) and FF_SPR2SUPER or 0 --Use super sprite2?
	local FLIP = ((ATTACK.flip==true) and FF_HORIZONTALFLIP or 0)
	if FLIP == FF_HORIZONTALFLIP and p == displayplayer
		local CAMANG = XS_AngleDifference(p.cmd.angleturn<<16, p.drawangle, 2, ANG90)
		if (CAMANG > ANGLE_45)
			FLIP = 0
		end
	end	
	ATTACK.sprite2 = $ & ~FF_SPR2SUPER
	ATTACK.frame = min($, MAXFRAME)
	
	--Set player animation!
	s.sprite2 = (ATTACK.sprite2)|SPR2SUPER --Use this sprite explicitly.
	s.frame = ($&~(FF_FRAMEMASK|FF_HORIZONTALFLIP)) | ATTACK.frame | FLIP 	
	
	--Some more player adjustments....	
	xs.custom1,xs.custom2,xs.custom3,xs.jump,xs.spin = 999<<16,999<<16,999<<16,999<<16, 999<<16
	p.pflags = $|PF_JUMPDOWN|PF_USEDOWN|PF_JUMPED & ~(PF_SPINNING|PF_THOKKED|PF_STARTJUMP|PF_NOJUMPDAMAGE|PF_CANCARRY)
	s.tics,s.anim_duration = 7,7 
	p.aiming = 0
	if p.viewrollangle --Gradually reset rollangle.
		if (xs.ACCE=="both" or xs.ACCE=="noquakes")
			p.viewrollangle = 0 --Or don't display them at all.
		elseif (p.viewrollangle < 0)
			p.viewrollangle = min(0, $+ANG1)
		else
			p.viewrollangle = max(0, $-ANG1)
		end
	end
	if ATTACK.nochase == false
		ATTACK.nochase = true
	end
	if ATTACK.autojump
		ATTACK.autojump = $-1
		if type == "attack" 
			p.cmd.buttons = $|BT_JUMP
			ATTACK.jump = 999<<16
			if P_IsObjectOnGround(s) or (s.momz*ZFLIP < 12*s.scale)
				P_SetObjectMomZ(s, 12<<16, true)
			end
		end
	end
	
	--Interact with the sandbag
	local t = s.XSbag 
	if t and t.valid
		local HITS = (t.BAGhit) or 0 --What's our current status? Send it to the HUD.
		local FLURRIES = max(0,HITS-7)
		local HEALTH = t.target and t.target.health
		local DAMAGE = 1
		if not HEALTH or HEALTH == 1000
			HEALTH = 1
		end
		if t.BAGdamage
			DAMAGE = max(1, FixedInt(t.BAGdamage))
		end
		if t.HUDhit
			t.HUDhit = $-1
		end
		if not t.BAGcutcombo
			ATTACK.hits = HITS
			ATTACK.flurries = FLURRIES
			ATTACK.damage = DAMAGE
			ATTACK.targethealth = HEALTH
			ATTACK.overkill = HEALTH-ATTACK.damage-1
			ATTACK.overkill = ($<0) and abs(ATTACK.overkill) or 0
		end	
		if type == "groundslam" --Stomp into a groundslam.	
			if ((GroundSlam(p,s,xs,t,ATTACK, SUPER, SPR2SUPER))==false)
				return false --
			elseif t and t.EarthSlammed
				return --
			end
		elseif type == "flurry" --Special Flurry Attack GFX
			local NUM = max(0, HITS-((PHASE==3) and 9 or 7))
			local ADD = (NUM>5) and 30000 or (NUM>3) and 23000 or 14000
			p.camerascale = ((p.pflags & PF_ANALOGMODE) and 10000 or 18000)+(ADD*NUM)
			p.XSscalecam = 6
			ATTACK.hypersmash = nil
			t.hypersmashed = nil
			
			if (ATTACK.timer > 6)
				if (ATTACK.timer < 14) and not (ATTACK.timer % 3 == 0) 
					xs.forceangle = {angle = R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy), timer = 7, nosangle = true}	
					s.flags2 = $|MF2_DONTDRAW
				end		
				ATTACK.endanim = max($-1, 0)
			else
				s.flags2 = $|MF2_DONTDRAW	
				if ATTACK.timer == 5
					if not ATTACK.spin  and not ((p.cmd.buttons & BT_JUMP) and (ATTACK.jump<7))
					and not ((p.cmd.buttons & BT_CUSTOM1) and (ATTACK.custom1<7)) and not ((p.cmd.buttons & BT_CUSTOM2) and (ATTACK.custom2<7))
						ATTACK.spin = 1 --Auto attack after a while.
						p.cmd.buttons = $|BT_SPIN					
					end	
					local MAXHITS = (SUPER==3) and 22 or (SUPER==2) and 17 or 14
					if HITS >= MAXHITS-1
						local ghs = XSon.ChargeSpark(t, nil, 1<<16, nil, AST_ADD, 12, 34|FF_TRANS10)
						ghs.scalespeed = $*2
						ghs.spriteyoffset = $+16<<16
					end					
				elseif not s.XSstretch and (ATTACK.timer < 3)				
					XSon.SwapStretch(p, s, xs)
					s.spritexscale,s.spriteyscale = FRACUNIT+9000,FRACUNIT-16000
					s.spriteyoffset = -6<<16
					s.XSstretch = {timer = 4, Xspeed = 2000, Yspeed = 4000}
				end
			end	
			
		else --if type == "attack"
		
			--In your final attack, you can cancel your animation early.
			if (ATTACK.cantcharge or t.BAGcutcombo) and (ATTACK.timer > (11-SUPER)) and not t.EarthSlammed
			and (xs.FreeFlight or not (p.cmd.buttons & (BT_CUSTOM1|BT_CUSTOM2)) )
				if (p.cmd.buttons & BT_JUMP) and (ATTACK.jump < 3) and not (ATTACK.didhyper and (ATTACK.didhyper<8))
					ATTACK.MOMX,ATTACK.MOMY,ATTACK.MOMZ = s.momx/5,s.momy/5,s.momz/2
					CancelAssaultAnim(p,s,xs, "nojumphold", ATTACK) return --
				elseif (p.cmd.buttons & BT_SPIN) and (ATTACK.spin < 5)
					ATTACK.timer = max($, 200)
				end
			end			
			--Camera adjustments?
			if t.JustGotHit and (t.JustGotHit > 3)
				if (t.JustGotHit > 8)
					xs.forceangle = {angle = R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy), timer = 7, nosangle = true}	
				end
				p.camerascale = (p.pflags & PF_ANALOGMODE) and FRACUNIT+41000 or (2<<16)+63000
			else
				p.camerascale = (p.pflags & PF_ANALOGMODE) and 31000 or 46000 // max(45000, min(2<<16, $-29999)) --gradually re-zoom!
			end
			p.XSscalecam = 7
		
			local ZDIST = (s.z+s.momz)-(t.z+t.momz)
			local ZAIM = (abs(ZDIST)>10<<16) and FixedInt(ZDIST) or 0 
			if ZAIM
				local DIST = FixedHypot(s.x-t.x, s.y-t.y)
				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)		
						ZAIM = (NANGLE <= 270) and -ANG30 or min($,0)
					else		
						ZAIM = (NANGLE >= 30) and ANG30 or max($,0)
					end
					p.aiming = -ZAIM
				end
			end
			
			--Charge attacks!
			if ATTACK.charged and ( (p.cmd.buttons & (BT_SPIN|BT_JUMP)) or (ATTACK.charged  > 18))
			and not ATTACK.cantcharge and (SUPER!=1)
				if not (p.cmd.buttons & (BT_SPIN|BT_JUMP))
					if (ATTACK.charged > 32)
						ATTACK.charged = min(79, $+2)  --Auto charge the final bit.
					else
						ATTACK.charged = max($-2,0)
						if ATTACK.chargeSFX
							ATTACK.chargeSFX = $-1
							if not ATTACK.chargeSFX
								S_StopSoundByID(s, sfx_xssfpa)
							end
						end
					end
				end
				local CHARGE = ATTACK.charged
				if not ATTACK.chargeSFX
					XSon.DarkBody(s, nil,nil,nil, 2)
					ATTACK.chargeSFX = 8
					S_StopSoundByID(s, sfx_xssfpa)
					XSon.Sound(s, sfx_xssfpa)
				end
				if (CHARGE > 36) and (ATTACK.sprite2 == SPR2_TAL2 or ATTACK.sprite2 == SPR2_TAL1)	
					local NEWFLIP = FLIP
					if (CHARGE > 44) NEWFLIP = ((not FLIP) and FF_HORIZONTALFLIP or 0) end
					
					s.sprite2 = (SPR2_TAL1)|SPR2SUPER --Use the punching one.
					s.frame = ($&~(FF_FRAMEMASK|FF_HORIZONTALFLIP)) | ((CHARGE<40) and C or (CHARGE<43) and B or A) | NEWFLIP	
					if CHARGE <= 38 and not s.XSstretch
						s.XSstretch = {timer = 4}
						s.spritexscale,s.spriteyscale = FRACUNIT+27000,FRACUNIT+9100
					end
				end
				if (CHARGE < 70)
					local ghs = XSon.ChargePower(s, CHARGE, s)
					if (CHARGE>=32) and (CHARGE < 60)
						if (CHARGE < 45)
							if ghs and ghs.valid and ((ghs.frame & FF_TRANSMASK)< FF_TRANS80)
								ghs.color,ghs.colorized = SKINCOLOR_CRIMSON,true
								ghs.frame = $+1<<16
							end
						end
						
						 --Raise your foe skywards	when charging!
						local TFLOORZ = (s.eflags & MFE_VERTICALFLIP) and abs(t.z-t.ceilingz) or abs(t.z-t.floorz)
						if (TFLOORZ < 220*s.scale)
							local SMOMZ,TMOMZ = s.momz*ZFLIP,t.momz*ZFLIP
							if (TMOMZ <= 0)
								t.momz = $*2/3
							end
							if (SMOMZ <= 0)
								s.momz = $*2/3
							end							
							if CHARGE == 32
							or (CHARGE <= 50) and (CHARGE % 6 == 0)
								for i = -3,-1
									local ghs = XSon.TransformGFX2(t, i*188<<16) 
									if ghs and ghs.valid
										ghs.tracer = t
										ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS70
										ghs.momx,ghs.momy,ghs.momz = $+t.momx,$+t.momy,$+t.momz --Rising orbs
									end
								end		
							end
							local ADD = s.scale*ZFLIP
							ADD = (TMOMZ<4*s.scale) and $*4 or (TMOMZ<9*s.scale) and $*3 or (TMOMZ<16*s.scale) and $*2 or $
							if (TMOMZ < 17*s.scale)
								t.momz = $+ADD
								XS_RingBurstDust(p, t, xs, "manual", TFLOORZ)	
							end
							
							if not (s.eflags & MFE_VERTICALFLIP) and (s.z+s.momz < t.z+t.momz)
							or (s.eflags & MFE_VERTICALFLIP) and (s.z+s.momz > t.z+t.momz)
								if (SMOMZ < 15*s.scale)
									s.momz = $+ADD
									XS_RingBurstDust(p, s, xs, "manual", s.z)
								end
							end
						end
					end						
					if not s.XSstretch
						local SHAKEX,SHAKEY = P_RandomRange(1,CHARGE)*5000,P_RandomRange(1,CHARGE)*5000
						s.XSstretch = {timer = 1}
						s.spritexoffset = (leveltime%2) and -SHAKEX or SHAKEX
						s.spriteyoffset = (leveltime%2) and -SHAKEY or SHAKEY
					end
				else
					local ghs = XSon.ChargePower(s, 26, s)
					if ghs and ghs.valid 
						ghs.color,ghs.colorized = SKINCOLOR_FLAME,true
						ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS80
					end	
				end
				if (CHARGE > 18)
					XSon.DarkBody(s, nil,nil,nil, (CHARGE>36) and 2 or 1)
					if (CHARGE > 50)
						ATTACK.hypersmash = CHARGE --The next attack can now be a Hyper Smash!
						if ATTACK.jumped
							ATTACK.jumped = nil --Can use upwards attacks now.
							ATTACK.jump = (p.cmd.buttons & BT_JUMP)  and $ or 0
							ATTACK.spin = (p.cmd.buttons & BT_SPIN)  and $ or 0					
						end			
						if (CHARGE < 70) 
							local RB = (xs.FreeFlight) and xs.FreeFlight or s.XS_DarkTable
							if RB
								RB.speedlines = 3
								if (CHARGE < 53)
									RB.windlines = 1		
								end
							end
							if CHARGE<53 --Initial sound.
								local ghs = XSon.ChargeSpark(s, SKINCOLOR_NOBLE, 53000+(SUPER*15000), s, AST_ADD, 18, 34|FF_TRANS30)
								ghs.destscale = $*3/2
								XSon.Sound(ghs, sfx_kc4d, 179)
								if (p.cmd.buttons & BT_JUMP)
									ATTACK.jumppressed=true
								elseif (p.cmd.buttons & BT_SPIN)
								or (ATTACK.sprite2!=SPR2_TAL2)
									ATTACK.spinpressed=true
								end								
							end
						end
						local ghs = ATTACK.flicker
						if not (ATTACK.flicker and ATTACK.flicker.valid)
							ATTACK.flicker = XSon.Overglow(s, 2, SKINCOLOR_SUPERSILVER1)
							ghs = ATTACK.flicker
						end
						if ghs and ghs.valid
							local FUSE = (CHARGE>68) and 2 or 3
							ghs.frame = ($ & ~FF_TRANSMASK)|((FUSE==3) and FF_TRANS90 or FF_TRANS40)
							ghs.fuse,ghs.tics,ghs.sttfade = FUSE,FUSE,1		
							if (leveltime%2==0)
								ghs.flags2 = $ & ~MF2_DONTDRAW
								if (CHARGE>68)
									ghs.color,ghs.colorized = SKINCOLOR_ORANGE,true
								else	
									ghs.color = (leveltime%4==0) and SKINCOLOR_SUPERSILVER1 or SKINCOLOR_SUPERPURPLE2
								end
								XSon.OverGlowThinker(ghs)
							else
								ghs.flags2 = $|MF2_DONTDRAW
							end
						end
					end
				end
			else
				ATTACK.chargeSFX = 0
				S_StopSoundByID(s, sfx_xssfpa)
			end
		end
		if not ATTACK.attackCD
		
			--DETERMINE INPUT!
			local GOTYPE = 0
			if (p.cmd.buttons & BT_CUSTOM1) and (ATTACK.custom1 < 7) and not xs.FreeFlight
				GOTYPE = 3 --Cancels move early.
			elseif (p.cmd.buttons & BT_CUSTOM2) and (ATTACK.custom2 < 7) --and SUPER==3
				if (ATTACK.type != "groundslam")
					GOTYPE = 4 --Ground Slam finisher for phase 3
				end
			else
				if t.BAGflurry or type=="flurry" --Cannot charge at this point. Response is instant.
					if (p.cmd.buttons & BT_SPIN) and (ATTACK.spin < 7)
						GOTYPE = 1	--Punch	
					elseif (p.cmd.buttons & BT_JUMP) and (ATTACK.jump < 7)
						GOTYPE = 2  --Upwards kick
					end
				else --Attacks are unleashed when you let go off the button, or when buffer-switched.
					if ATTACK.spinpressed==true and not (p.cmd.buttons & BT_SPIN)
						GOTYPE = 1 --Punch attack. Can be chained into itself.	
					elseif (p.cmd.buttons & BT_SPIN)
						if (p.cmd.buttons & BT_JUMP) and (ATTACK.jump < 7) and not ATTACK.jumped
							p.cmd.buttons = $|BT_JUMP & ~BT_SPIN
							GOTYPE = 2 --Upwards Kick
						else--if (ATTACK.spin < 99<<16)
							ATTACK.charged = min(79, $+2) --Charging up!
						end
					end		
					if (p.cmd.buttons & BT_JUMP)
						if (p.cmd.buttons & BT_SPIN) and (ATTACK.spin < 7) and (ATTACK.jump > 99<<16)
							p.cmd.buttons = $|BT_SPIN & ~BT_JUMP
							GOTYPE = 1 --Straight Punch
						elseif not (p.cmd.buttons & BT_SPIN)
							ATTACK.charged = min(79, $+2)
						end
					elseif ATTACK.jumppressed==true and not ((p.cmd.buttons & BT_SPIN) and GOTYPE!=0)
					and not (GOTYPE==1 and ((type == "flurry") or t.BAGflurry))
					and (ATTACK.hypersmash or not ATTACK.jumped)
						GOTYPE = 2 --Upwards kick. Cancels if performed twice.
					end
				end
			end
			
			if (GOTYPE != 0)
			
				local MAXHITS = (SUPER==3) and 22 or (SUPER==2) and 17 or 14
				
				if (HITS < MAXHITS) and (GOTYPE != 3)

					if GOTYPE==2 and ATTACK.jumped
					
						ATTACK.jump = 999<<16 --Don't cancel because the player is mashing jump!
						
					elseif not t.BAGcutcombo and not ATTACK.combolock
					
						ATTACK.gotype = GOTYPE
						if GOTYPE == 4
							XSon.SandBag(t, "groundslam", nil)
							if t and t.valid and ATTACK and not t.XS_BagGroundSlammed
								t.XS_BagGroundSlammed=true
								if ATTACK.timer and (ATTACK.timer > 4)
									ATTACK.timer = 5
								end
							end
						else
							if ATTACK.type == "groundslam"
								XSon.SandBag(t, "attack", nil)
							else --if not ATTACK.didhyper
								local NEWATTACK = {}
								local CURatk = (ATTACK.type!=nil) and ATTACK.type or "attack"
								local NEWatk = "attack" --Our new attack?
								
								--Setup attack chain table, then pick which one to use.
								local FLURRYTIME = (PHASE==3) and 9 or 7

								if ATTACK.didhyper or t.GotHyperSmashed --What happens when you follow-up on a smash?
									if (HITS < FLURRYTIME)
										t.GotHyperSmashed.timer = 0
										if GOTYPE == 1
											t.BAGhit,HITS = FLURRYTIME,FLURRYTIME --Go straight into a flurry!
										else
											t.BAGhit,HITS = MAXHITS,MAXHITS
											if GOTYPE == 2
												t.momx,t.momy,t.momz = $/3,$/3,$/2
												t.GotHyperSmashed = nil
												XSon.SandBag(t, "ForceEndKick", nil)
												if s.XSassault
													t.momx,t.momy = $/5,$/5 --Go basically straight up.
													if (CrudeTryMove(s,t, false)!= true)
														for i = 1,3
															XSon.ChaseBag(s,p,xs, t,t.target, true)
														end
													end
													s.XSassault.chase = 8
													s.XSassault.endanim = 31
												end return -- 
											end
										end
									end
								end
								for i = 1,MAXHITS
									if i == FLURRYTIME 
										NEWatk = "flurry"
									elseif (i >= MAXHITS-1)
										NEWatk = "attack" 
									end
									NEWATTACK[i] = NEWatk
								end
								local NEXTMOVE = NEWATTACK[max(1, HITS)] --min(#NEWATTACK,
								XSon.SandBag(t, NEXTMOVE, nil) 
							end
						end 
					end
					
				else --The final attack of our chain!
				
					XSon.Echoes(p,s,xs, "stop")
					if GOTYPE == 3
						local ghs = XS_CircleWave(s, 2, SKINCOLOR_SUPERGOLD2)
						ghs.frame = $+FF_TRANS40
						ghs.scalespeed = $*2
						ghs.momx,ghs.momy,ghs.momz = $+s.momx/2,$+s.momy/2,$+s.momz/2
						S_StartSound(ghs, sfx_shattr)
					else
						local TABLE = {sfx_xsvocf,sfx_xsvoce,sfx_xsvocf,sfx_xsvocg}//{sfx_xsvopd,sfx_xsvop6,sfx_xsvop8,sfx_xsvopc,sfx_xsvope}
						local NUM = P_RandomRange(1, #TABLE)
						local GRUNT = TABLE[NUM]
						if GRUNT == s.XSlastattackgrunt
							GRUNT = ($==TABLE[#TABLE]) and TABLE[1] or TABLE[NUM+1]
						end
						XSVoice(p, s, xs, GRUNT, true)
						s.XSlastattackgrunt = GRUNT
					end
					
					S_StopSoundByID(s, sfx_xssfp1)
					p.camerascale = (p.pflags & PF_ANALOGMODE) and 30000 or 43000
					p.XSscalecam = 5
					XSon.SandBag(t, nil, "finish")
				end
			end
		elseif type == "flurry" --Lightning fast attacks when in a flurry!
			local MIN = (SUPER==3) and 3 or (SUPER!=1) and 6 or 7
			ATTACK.attackCD = max(0, min(MIN, $-1))
			if ATTACK.spin and not ATTACK.attackCD
				ATTACK.spin = 0 --Can hold the button if wanted!
			end
		end	
		if xs.FreeFlight
			if ATTACK.stayZ != nil --Go a bit faster to not keep people waiting.
				if (type=="groundslam")
					ATTACK.stayZ = nil
				else
					if t and t.target and (ATTACK.timer < 6)
						p.drawangle = R_PointToAngle2(s.x, s.y, t.target.x, t.target.y)
						s.angle = p.drawangle
					end
					s.z = ATTACK.stayZ
					P_SetObjectMomZ(s, 4<<16)
					if (ATTACK.sprite2!=SPR2_TAL2)
						P_InstaThrust(s, s.angle, -30*s.scale)
						P_Thrust(s, s.angle, 3*s.scale)
					end
					if (ATTACK.timer > 27) and not ATTACK.hypersmash and not t.BAGPhyper and not t.JustGotHit
						ATTACK.timer = $+99
					end
				end
			elseif (type!="groundslam") and (HITS>=14)
				if (type=="flurry") and (ATTACK.timer > 3)
				or (ATTACK.timer > 7) and not ATTACK.hypersmash and not t.BAGPhyper and not t.JustGotHit
					ATTACK.timer = $+99
					if type=="flurry" and t and t.target and t.target.player
						P_Thrust(t.target, s.angle, 160*s.scale)
					end	
				end
			end
		end
	end
end
