--=============================FUNCTIONS=======================================
--Rawset functions and SOC compatible functions are setup here. At 2.1's launch,
--I didn't realize how redundant SOC would become, and I worried about Lua's
--instability on netgames. So where possible, I used SOC actions.

--This hook contains general functions not exclusive to HMS or the walljump, and that 
--aren't purely for the sake of creating visuals either.
--=============================================================================

--SMS Lightning Homing Bolt, with a large recoilbounce!
/*
rawset(_G, "SMS_HomingBolt", function(s, p, yu)		
	if not (yu.target and yu.target.valid)
	or (s.eflags & MFE_UNDERWATER)
	or yu.preptimer and (FixedHypot(FixedHypot(s.x-yu.target.x, s.y-yu.target.y), s.z-yu.target.z) > 999*s.scale) --Too far!
	or p.powers[pw_carry]
		S_StartSound(s, sfx_dmpain)
		s.SMS_HomingBolt = nil --It went wrong, abort!
		return false --
	elseif not yu.launchtimer --It's over!
		s.SMS_HomingBolt = nil 
		return false --
	end
				
	local t = yu.target 
	p.powers[pw_noautobrake] = 2
	p.powers[pw_ignorelatch] = 32769
	s.SMShomingspeed = yu.homespeed --Keep this up!
	s.SMShomingangle = yu.homeangle
	
	--========================================
	--START!!
	--Initial landing effect!
	--========================================
	
	if yu.start	
		if not yu.minspeed
			if (p.speed > 36*s.scale)
				yu.minspeed = p.speed
			elseif (yu.homespeed > 36*s.scale)
				yu.minspeed = yu.homespeed
			else
				yu.minspeed = 36*s.scale
			end
		end
		s.angle = s.SMShomingangle
		p.drawangle = s.SMShomingangle
		
		P_SetObjectMomZ(s, FRACUNIT/22)
		s.spritexscale = FRACUNIT+12500
		s.spriteyscale = FRACUNIT-24000
		s.SMS_squashtable = {timer = 5, Xspeed = 3000, Yspeed = 3500}
		s.SMS_wallwait = 4
		SMS_WallJumpDelete(s, true)
		
		SMS_ElectricRecoil(s)
		for i = 1,2
			local ghs = SMS_GroundWindBlast(s)
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS|((i == 1) and RF_FLOORSPRITE or 0)
			ghs.colorized = true
			ghs.color = SKINCOLOR_EMERALD				
			if i == 1
				ghs.frame = ($ & ~FF_FRAMEMASK)|TR_TRANS40
				ghs.blendmode = AST_ADD
			else
				ghs.spriteyoffset = $-24*FRACUNIT
			end
		end
		
		if not SMS_Graphix(s)
			A_SMSSTOMPQUAKE(s, SMSLocalRNG(100, 300,s), SMSLocalRNG(6,11,s), SMSLocalRNG(40,90,s)*FRACUNIT, 0, leveltime*ANG1*45, 0, 30, 0, MT_HMSWAVEAURA)
			A_SMSSTOMPQUAKE(s, 64, 12)
			A_SMSSTOMPSMOKE(s, 24, MT_SPINDUST)
			for i = 0,6
				HMS_GroundSparks(s, p, i)
			end
		else
			for i = 0,2
				HMS_GroundSparks(s, p, i)
			end
		end
		
		if yu.preptimer and (abs(yu.bounce) >= 21*s.scale)
			yu.preptimer = max($, 3)
			local ghs = SMS_SmartSplat(s, 135)
			if ghs and ghs.valid
				A_SMSquake(s, 70*FRACUNIT, 5, 1050)
				ghs.sprite = SPR_SMX1
				ghs.frame = T|TR_TRANS10
				ghs.scale = s.scale*3
				if SMS_Graphix(s) == 0
					SMS_RisingFlames(s)
					for i = 1,4
						SMS_CraterSpew(s, s.angle+ANGLE_180, 17)
					end	
				end
			else
				A_SMSquake(s, 40*FRACUNIT, 6, 550)
			end
		else
			A_SMSquake(s, 40*FRACUNIT, 6, 550)
		end		
		yu.start = false
	end

	--Lightning all the time!
	for d = 1,3
		local ghs = HMS_GroundSparks(s, p, d)
		if ghs and ghs.valid
			P_SetObjectMomZ(ghs, s.momz+FRACUNIT*3, true)
			P_Thrust(ghs, s.angle, (p.speed/3)+8*s.scale)
		end
	end

	--Initial prep for bounce! Doesn't necessarily last any time at all!
	if yu.preptimer
	
		s.SMS_wallwait = 4
		s.HayMakerPunch = yu.preptimer
		P_SetObjectMomZ(s, FRACUNIT/22)
		p.pflags = $|PF_JUMPDOWN|PF_USEDOWN
		
		t.spriteyscale = s.spriteyscale
		t.spritexscale = s.spritexscale
		
		--Try Moving!
		if P_TryMove(s, t.x+t.momx-s.momx, t.y+t.momy-s.momy, true)
			s.z = t.z+t.height
		else --Failure to move there?
			P_TryMove(s, s.x-s.momx, s.y-s.momy, true) --Try undoing your momentum issues at least?
		end
		yu.preptimer = max($-1,0)
		
	elseif yu.launchtimer --Launching!
	
		if (yu.launchtimer == 12) --Initial launch!
		
			A_SMSSTOMPSMOKE(s, 24, MT_SPINDUST)
			SMS_BoostAura(s, p)
			
			t.spriteyscale = FRACUNIT
			t.spritexscale = FRACUNIT
			if t.health and not (t.flags & MF2_FRET)
				P_DamageMobj(t, s, s, DMG_ELECTRIC)
			end
			P_RadiusAttack(s, s, 99*s.scale, DMG_ELECTRIC, false) --Minor AoE!
			
			--Lightning bounce leap incoming!
			if (abs(yu.bounce) < 21*s.scale) 
				yu.launchtimer = 0 --Aww.
				p.sms_jumphold = 850
				p.smsmeleebutton = 850
				p.pflags = $|PF_JUMPDOWN|PF_JUMPED|PF_NOJUMPDAMAGE & ~PF_THOKKED
				P_SetObjectMomZ(s, 10*s.scale) --GO!
				SMS_HomingTrickJump(s, p)
				return false --
			else
				if (yu.launchtimer == 12)
					P_SetObjectMomZ(s, abs(yu.bounce)) --GO!
					s.angle = yu.homeangle
					p.drawangle = yu.homeangle	
					if (p.speed < yu.minspeed/4)
						P_Thrust(s, s.angle, yu.minspeed/4)
					end
				end
				
				s.spritexscale = FRACUNIT-30000
				s.spriteyscale = FRACUNIT+30000
				s.SMS_squashtable = {timer = 4, Xspeed = 13000, Yspeed = 13000, air = true}
				
				local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_HMSLIGHTNING2)
				ghs.spriteyoffset = -80*FRACUNIT
				ghs.blendmode = AST_ADD
				ghs.rollangle = ANGLE_180
				ghs.color = SKINCOLOR_SMS4R	
				ghs.spriteyscale = FRACUNIT-20000
				ghs.fuse = 14
				
				local ANGLE = s.angle
				if p.smsrtable and (p.smsrtable.turnang != nil and p.smsrtable.direct > 20)
					ANGLE = p.smsrtable.turnang
				end
				P_InstaThrust(s, ANGLE, s.SMShomingspeed)
			end
		end
		if (yu.launchtimer > 3)	
			if (s.momz*P_MobjFlip(s) > s.scale*3)
				s.state = S_PLAY_SPRING
				p.panim = PA_SPRING
				p.pflags = $|PF_THOKKED|PF_JUMPED|PF_NOJUMPDAMAGE|PF_JUMPDOWN
				p.sms_jumphold = FRACUNIT
				p.hometrickanim = false
				p.homingtrickcharge = 0
				if p.smsrtable and p.smsrtable.turnang != nil and (p.smsrtable.direct > 5)
					if (p.speed < yu.minspeed)
						local ANGSHIFT = (SMS_AngleDifference(yu.homeangle, p.smsrtable.turnang, 2)) or 1
						ANGSHIFT = max($/(ANG1*8), 1)
						print(ANGSHIFT)
						P_Thrust(s, p.smsrtable.turnang, p.smsrtable.direct*((FRACUNIT/ANGSHIFT)/2))
					end
				end
			else
				s.state = S_PLAY_FALL
			end
			if (p.panim == PA_SPRING)
			
				if (yu.launchtimer == 4)
					p.drawangle = yu.homeangle	
				else
					p.drawangle = leveltime*ANGLE_45
				end
				for i = 1,4 --Zoom!
					if (s.z+s.height+(yu.bounce/4) > s.ceilingz)
					or (s.z+(yu.bounce/4) < s.floorz)
						break
					end
					s.z = $+(abs(yu.bounce/4)*P_MobjFlip(s))
					if i == 1
						yu.addmult = $+1
					end
				end	
				local ghs = SMS_ElectricRecoil(s)
				if ghs and ghs.valid
					ghs.renderflags = $|RF_FLOORSPRITE & ~RF_PAPERSPRITE
					ghs.flags2 = $|MF2_SPLAT
					ghs.momz = s.momz/2
				end
			end
			local DIV = (p.speed > 77*s.scale) and 56 or (p.speed > 45*s.scale) and 52 or 51 
			s.momx = FixedDiv(FixedMul($, 50), DIV)
			s.momy = FixedDiv(FixedMul($, 50), DIV)
			if yu.launchtimer == 4 and (s.momz*P_MobjFlip(s) > 12*s.scale)
				s.momz = FixedDiv(FixedMul($, 90), 91+yu.addmult)
			end
		elseif (yu.launchtimer <= 1)
			p.pflags = $ & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
		end	
		if (yu.launchtimer % 2)
			local ghs = SMSRSpawnGhost(s, 6)
			ghs.color = SKINCOLOR_SMSROVERGLOWRED
		else
			SMSRSpawnGhost(s, 14)
		end
		yu.launchtimer = max($-1,0)
	end
	return true --
end) */

--Would we call this player an admin...?
rawset(_G, "SMS_Admin", function(p)
	if (p == players[0] and not netgame) --You're not just an admin, you're the entire server!
	or (p == server)
		return 3 --
	elseif IsPlayerAdmin(p) or (p == admin) --Yeah, that's an admin. The latter is the old check.
		return 2 --
	else
		local maxplayers = 0
		for z in players.iterate
			maxplayers = $+1
		end
		if maxplayers == 1 --Uhh, you're the ONLY player, so you kind of count?
			return 1 --
		end
	end
	return 0 --
end)

--Ignore fireflower and just give the actual shield value.
rawset(_G, "SMSSHIELD", function(p, SH1, SH2)
	local SHIELD = p.powers[pw_shield] & ~SH_FIREFLOWER
	if SH1
		if SHIELD == SH1 or SHIELD == SH2 --Is it either of these shields? If so, return true.
			return true --
		else
			return false --
		end
	end
	return SHIELD --
end)

--Set SMS' colors to be a shiny set of 5, or just normal SRB2 colors.
rawset(_G, "SMS_Colors", function(s, p)
		if p.hypermysticsonic
			if (p.hypermysticsonic > 30) --Wait a tiny bit...
				if (p.transformstatecolors >= 26)
					p.transformstatecolors = 0
					s.color = SKINCOLOR_SUPERGOLD3
				end
				p.transformstatecolors = $+1
			
				if (s.color < SKINCOLOR_SUPERGOLD1) or (s.color > SKINCOLOR_SUPERGOLD5)
					s.color = SKINCOLOR_SUPERGOLD2 --Forced color.
				end
				
				if (p.transformstatecolors == 3)
					s.color = SKINCOLOR_SUPERGOLD4
				elseif (p.transformstatecolors == 6)
					s.color = SKINCOLOR_SUPERGOLD5
				elseif (p.transformstatecolors == 9)
					s.color = SKINCOLOR_SUPERGOLD4
				elseif (p.transformstatecolors == 12)
					s.color = SKINCOLOR_SUPERGOLD3
				elseif (p.transformstatecolors == 16)
					s.color = SKINCOLOR_SUPERGOLD2
				elseif (p.transformstatecolors == 21)
					s.color = SKINCOLOR_SUPERGOLD1
				elseif (p.transformstatecolors == 23)
					s.color = SKINCOLOR_SUPERGOLD2
				end
			end

		elseif not G_GametypeHasTeams()

			if p.smscoatc5 == nil
				A_setSMSspecialcolors(p, (not p.nosmscolorspls or p.sms_transformstate) and 1 or 0)
			end

			if (p.sms_transformstate or p.shinyglosscoat) --Shiny boy!
			and not (p.powers[pw_shield] & SH_FIREFLOWER) --White boy!
			and not ((mariomode) and p.powers[pw_invulnerability]) --Rainbow boy!
			
				--If this is on, you're forced to be a regular color.
				if p.nosmscolorspls and not p.sms_transformstate
					if (p.skincolor != SKINCOLOR_COBALT) and (p.shinyglosscoat != 0)
						A_setSMSspecialcolors(p, 0)
					end
				else		
					p.transformstatecolors = (p.transformstatecolors < 52) and $+1 or 0
			
					if (p.transformstatecolors < 6)
						if s.color != p.smscoatc3
							s.color = p.smscoatc3
						end
					elseif (p.transformstatecolors < 12)
						if s.color != p.smscoatc2
							s.color = p.smscoatc2
						end
					elseif (p.transformstatecolors < 18)
						if s.color != p.smscoatc1
							s.color = p.smscoatc1
						end
					elseif (p.transformstatecolors < 27)
						if s.color != p.smscoatc2
							s.color = p.smscoatc2
						end
					elseif (p.transformstatecolors < 33)
						if s.color != p.smscoatc3
							s.color = p.smscoatc3
						end
					elseif (p.transformstatecolors < 44)
						if s.color != p.smscoatc4
							s.color = p.smscoatc4
						end
					elseif (p.transformstatecolors < 47)
						if s.color != p.smscoatc5
							s.color = p.smscoatc5
						end
					else --if (p.transformstatecolors < 53)
						if s.color != p.smscoatc4
							s.color = p.smscoatc4
						end
					end
				end
			end	
		end
end)

--SMS Custom Shield Behaviour.
rawset(_G, "SMS_Shields", function(s, p)
local SHIELD = SMSSHIELD(p)
	if SHIELD
		if SHIELD == SH_WHIRLWIND
			p.jumpfactor = 87250 //Get slightly less than double jumpheight.
			
			if s.whirljumpgfx
				s.whirljumpgfx = $-1
			end
				
			if (p.pflags & PF_JUMPED)
			and (p.cmd.buttons & BT_JUMP)
			and not p.sms_dodging and not p.footsweepanim
			and not (s.eflags & MFE_SPRUNG)
				if not s.whirljumpgfx 
				and (s.momz*P_MobjFlip(s) > 8*s.scale)
					if (s.z-s.momz < s.floorz+(s.height/2))
						A_SMSSTOMPSMOKE(s, 11, MT_SPINDUST)
						local ghs = P_SpawnGhostMobj(s)
						ghs.spriteyscale = $*3/2
						ghs.spritexscale = $*2/3
						ghs.destscale = ghs.scalespeed+1								
					end
					SMS_WindJumpGFX(s)				
				end
				s.whirljumpgfx = 2
    		end
			return SHIELD
				
    	else
			if p.jumpfactor >= 72535
				p.jumpfactor = (p.powers[pw_sneakers]) and 72535 or skins[s.skin].jumpfactor
			end
			if SHIELD == SH_ARMAGEDDON		
				if (p.spinhold == 2) and (p.cmd.buttons & BT_CUSTOM2)
				and (p.cmd.buttons & BT_SPIN)
					if not P_IsObjectOnGround(s) //(p.pflags & PF_JUMPED) or p.sms_stompanim or p.hometrickanim
						P_BlackOw(p)
					end
				end
				return SHIELD
			elseif SMSSHIELD(p, SH_ATTRACT, SH_THUNDERCOIN)
				if p.sms_attracttl
    			    p.sms_attracttl = $-1
					if (p.sms_attracttl <= 1)
						p.powers[pw_shield] = SH_PITY
						P_SpawnShieldOrb(p)
						S_StartSound(s, sfx_shield)
						P_FlashPal(p, PAL_MIXUP, 3)
						P_NukeEnemies(s, s, 0)
						p.sms_attracttl = nil
					end
				else
    			   	 p.sms_attracttl = G_GametypeHasSpectators() and 390 or 725 //Roughly 10 seconds for matches, otherwise 20
				end
				return SHIELD --
			end
		end
	end
	if p.sms_attracttl --Remove attraction timer?
		if not SMSSHIELD(p,SH_ATTRACT, SH_THUNDERCOIN)
			p.sms_attracttl = nil
		end
    end
end)

--Armored Crash. Hit an enemy with a moving footsweep or close-by airboost for this to happen.
rawset(_G, "SMS_ArmoredCrash", function(s, p, i)
	if (s.state == S_SMS_AIRBOOST) and (p.sms_didairboost > 1)
		if not (i and i.valid)
			return false --
		elseif (i.flags & (MF_ENEMY|MF_BOSS)) 
			if (i.type == MT_DETON)
			or (i.flags & MF_PAIN)
				return false --
			end
		end
	elseif i and i.valid 
		if not (i.flags & (MF_PAIN|MF_BOSS|MF_ENEMY))
		or (i.flags & (MF_MISSILE|MF_FIRE|MF_SCENERY|MF_SPECIAL))
			if not (i.type == MT_BOMBSPHERE)
			and not (i.type == MT_DETON)
				return false --
			end
		end
	else
		if P_PlayerTouchingSectorSpecial(p, 1, 2) 
			return false --
		end
	end
	--Okay, CRASH into them!
				if (p.speed > 8*s.scale)
					s.momx = $/-9
					s.momy = $/-9
				else
					P_InstaThrust(s, (i) and R_PointToAngle2(s.x, s.y, i.x, i.y) or s.angle, -8*s.scale)
				end
				
				p.footsweepanim = false
				p.footsweep01 = 0
				p.footsweep02 = 0
				p.footsweep03 = 0
				
				SMS_WallJumpGFX(s)
				
				if SMS_Graphix(s) != 2
					if not (i and i.valid)
						P_SpawnMobjFromMobj(s, 0,0,0,MT_SMSGROUNDBOOST)
						SMS_ElectricRecoil(s)
						SMS_WindBlast(s)
						SMS_LightClash(s, SKINCOLOR_LAVENDER, s)					
					else
						P_SpawnMobjFromMobj(i, 0,0,0,MT_SMSGROUNDBOOST)
						SMS_ElectricRecoil(i)
						SMS_WindBlast(i)
						SMS_LightClash(i, SKINCOLOR_LAVENDER, i)	
					end
				end
				
				local ghs = SMS_LightFlash(s, SKINCOLOR_RED)
				ghs.tracer = s
				ghs.spriteyoffset = -13*FRACUNIT
				
				SMS_LightningStrike(s, SKINCOLOR_RED, R|FF_TRANS10)				
				
				P_SetObjectMomZ(s, 8*FRACUNIT, true)
				A_SMSquake(s, 55*FRACUNIT, 4, 800)
				s.state = S_PLAY_FALL
				p.sms_boostlevel = 0
				s.sms_boostloss = 12
				p.pflags = $ & ~PF_JUMPED & ~PF_NOJUMPDAMAGE
				s.SMS_locked = true
			--	p.smsatomicdivefail = 80 --stun timer.
				S_StartSound(s, sfx_kc31)
				P_NukeEnemies(s, s, 0)
		
				s.SMS_ControlAlter = {
				timer = (i and i.valid) and 39 or 8,
				maxforwardmove = 28,
				minforwardmove = -28,
				maxsidemove = 28,
				minsidemove = -28,
				removebuttons = 0,
				freecondition = 2}
				
				s.spritexscale = FRACUNIT-900
				s.spriteyscale = FRACUNIT+11000
				s.spriteyoffset = -11*FRACUNIT
				s.SMS_squashtable = {timer = 9, Xspeed = 40, Yspeed = 2400, air = true}	
				
				for d = 0,15
					P_SpawnParaloop(s.x, s.y, s.z+FixedMul(20*FRACUNIT, s.scale), 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
				end
				if i and i.health and (i.flags & MF_SHOOTABLE)
					if (i.type == MT_GSNAPPER) or (i.type == MT_CRUSHCLAW) --Trade blows with these dudes.
						P_DamageMobj(i) 
						return
					else
						P_DamageMobj(i)
					end
				end
				p.powers[pw_flashing] = max($, $+16)
				return true --
end)

--What can you do while idle? Powershare? Taunt? Sit on your butt?
rawset(_G, "SMS_IdleBehaviour", function(s, p)

			--Make a little "plop" on the ground as you lie down.
			if (s.state == S_PLAY_STND)
				if (s.tics == 1)
				and not p.hypermysticsonic
				and not (p.cmd.buttons & (BT_CUSTOM1|BT_CUSTOM3))
					s.state = S_PLAY_WAIT
					s.smsaurablastivate = nil
					s.smsaurablastivatecd = 0
					s.SMS_tossready = 0
					if not s.SMS_squashtable
						s.spritexscale = FRACUNIT-2300
						s.spriteyscale = FRACUNIT+4100
						s.spritexoffset = -3*FRACUNIT
						s.spriteyoffset = 3*FRACUNIT
						s.SMS_squashtable = {timer = 2, Xspeed = 1500, Yspeed = 2900}
						if P_IsObjectOnGround(s)
							for i = 1,8
								local ghs = P_SpawnMobjFromMobj(s, 
								FixedMul(-12*s.scale, cos(s.angle-ANGLE_90)) + FixedMul(4*s.scale, cos(s.angle+(ANGLE_45*i))),
								FixedMul(-12*s.scale, sin(s.angle-ANGLE_90)) + FixedMul(4*s.scale, sin(s.angle+(ANGLE_45*i))),
								0, MT_THOK)
								ghs.fuse = 14 ghs.tics = 2
								if (s.eflags & MFE_UNDERWATER)
									ghs.state = S_SPINDUST_BUBBLE1
								else
									ghs.colorized = true
									ghs.color = SKINCOLOR_BEIGE
									ghs.state = S_SPINDUST2
								end
								ghs.flags2 = $|MF2_SHADOW
								P_Thrust(ghs, s.angle+(ANGLE_45*i), 3*s.scale)
							end
							S_StartSoundAtVolume(s, sfx_s1b6, 16)
							S_StartSoundAtVolume(s, sfx_kc50, 3)
						end
					end	
					return 1 --
				end
			end
			
			--Wanna throw some dudes around? 
			--Just part of my efforts to make SRB2's COOP feel more actually cooperative and interactive.
			--It's lame if it's just a race, and SMS is way faster than other characters, so share the fun a bit!
			if (p.cmd.buttons & BT_TOSSFLAG)
			or (p.smsmeleebutton > 12)
			or (p.cmd.buttons & BT_WEAPONPREV) and not p.hypermysticsonic
				if not (SMS_Locked(s, p, true, true) and not p.exiting)
				and not (p.pflags & (PF_SPINNING|PF_STARTDASH|PF_JUMPED))
				and not ((s.state >= S_SMSTOSSING) and (s.state <= S_SMSHUGEFLAMETOSS))
				and not (p.footsweep01 > 6)
				and not p.sms_dodging --No dodging AT all
					p.camerascale =  FRACUNIT*5/6
					s.bhlcamtimer = 2
					s.state = S_SMSTOSSREADY			
					if p.hypermysticsonic
						s.hmsoframe = F --Standing on the ground.
					else
						p.pflags = $|PF_JUMPDOWN
					end
					if (p.cmd.buttons & BT_TOSSFLAG) or (p.cmd.buttons & BT_WEAPONPREV) or (leveltime % 3 == 0)
						s.SMS_tossready = s.SMS_tossready and $+1 or 1
					end
					
					--Create a shield?
					if p.transformdown == 1
					and p.powers[pw_shield] and (p.rings >= 10)
					and not (p.smspickup and p.smspickup.valid)
					and (p.cmd.buttons & BT_CUSTOM3)
					and (gametype == GT_COOP)
					and not SMS_Locked(s,p,true)
						local SHIELD = p.powers[pw_shield] & ~(SH_FIREFLOWER|SH_PINK) or 0
						if SHIELD --Shield is...?					
							local BOX = (SHIELD == SH_PITY) and MT_PITY_BOX
							or (SHIELD == (SH_WHIRLWIND)) and MT_WHIRLWIND_BOX
							or (SHIELD == (SH_ELEMENTAL)) and MT_ELEMENTAL_BOX
							or (SHIELD == (SH_ARMAGEDDON)) and MT_ARMAGEDDON_BOX
							or (SHIELD == (SH_ATTRACT)) and MT_ATTRACT_BOX
							or (SHIELD == (SH_FLAMEAURA)) and MT_FLAMEAURA_BOX
							or (SHIELD == (SH_BUBBLEWRAP)) and MT_BUBBLEWRAP_BOX
							or (SHIELD == (SH_THUNDERCOIN)) and MT_THUNDERCOIN_BOX
							or MT_RING_BOX --Unable to use it!
							if (SHIELD >= SH_FORCE) and (SHIELD < 512)
								BOX = (SHIELD > SH_FORCE) and MT_FORCE_BOX or MT_PITY_BOX
							end
							if BOX == MT_RING_BOX
								CONS_Printf(p, "Don't recognize the current shield!")
								return --
							end
							local m = P_SpawnMobjFromMobj(s,
							s.momx+FixedMul(48*s.scale, cos(s.angle)),
							s.momy+FixedMul(48*s.scale, sin(s.angle)),
							s.momz+(s.height+8*s.scale), BOX)
							local ghs = m
							if m and m.valid
								p.smspickup = nil
								s.smpktarget = m
								p.sms_tossflaghold = 0
								p.cmd.buttons = $|BT_TOSSFLAG
								s.momx = 0
								s.momy = 0
								m.momz = s.scale*P_MobjFlip(s)
								S_StartSound(s, sfx_antiri)
								S_StartSound(ghs, sfx_antiri)
								S_StartSound(nil, sfx_antiri, p)
								S_StartSound(ghs, sfx_monton)
								S_StartSound(s, sfx_itemup)
								if not SMS_Graphix(s)
									SMS_FollowCircle(s, SKINCOLOR_SMS2R)
									SMS_FollowCircle(s, SKINCOLOR_WHITE, 2)
									SMS_LensFlare(s)
									SMS_DiffusionChargeSparkles(s,p, SKINCOLOR_BLUEBELL)	
								end
								P_GivePlayerRings(p, -10)
								local ths = SMS_SparkleFlash(ghs)
								ths.color = SKINCOLOR_EMERALD
								ths.dispoffset = 2
								SMS_FollowCircle(ghs, SKINCOLOR_EMERALD, 3)
								if BOX != MT_RING_BOX
									p.powers[pw_shield] = max($-SHIELD, 0)
									P_SpawnShieldOrb(p)
								end
								return 7 --
							end
						end
						
					--YAHH!! *whoosh whoosh* ....HAHAH! ...an easy way to do a flip jump, I suppose!
					elseif (p.cmd.buttons & BT_JUMP) and not p.hypermysticsonic and (p.sms_jumphold < 12)
					
						local JUMPHEIGHT = FixedDiv(FixedMul(685555, 32678), P_GetMobjGravity(s) or 32678)
						JUMPHEIGHT = $*5/4
						s.z = $+(s.scale*P_MobjFlip(s))
						
						local ghs = SMS_GroundWindBlast(s)
						ghs.renderflags = RF_FLOORSPRITE
						ghs.z = $-s.momz
						ghs = SMS_GroundWindBlast(s)
						ghs.renderflags = RF_FLOORSPRITE
						ghs.spriteyscale = $/3
						ghs.spritexscale = $/3
						ghs.fuse = 11
						ghs.sttspeed = 2
						ghs.tracer = s
						ghs.ditchtracer = 1
						
						SMS_WindJumpGFX(s)
						S_StartSound(s, sfx_smsfx3)
						
						s.spritexscale = FRACUNIT-23000
						s.spriteyscale = FRACUNIT+35000
						s.spriteyoffset = 11*FRACUNIT
						s.SMS_squashtable = {timer = 7, Xspeed = 8500, Yspeed = 9570, air = true}	
						if (s.eflags & MFE_UNDERWATER)
							P_SetObjectMomZ(s, ((JUMPHEIGHT/2)+(s.scale/2)*-1))
						else
							P_SetObjectMomZ(s, JUMPHEIGHT*-1)
						end
						s.SMS_tossready = 0
						s.state = S_SMS_ROLLT1
						p.lasthomingtrick = 2
						p.hometrickanim = true
						p.homingtrickcharge = 0
						p.pflags = $|PF_NOJUMPDAMAGE|PF_JUMPED & ~(PF_SPINNING|PF_STARTDASH|PF_STARTJUMP)
						P_InstaThrust(s, s.angle, -8*s.scale)
						return 8 --
					end
				end
			end
			
			s.SMS_tossready = (s.state == S_SMSTOSSREADY) and s.SMS_tossready or 0	
			
			if not p.hypermysticsonic
				--Do a Smash Bros-inspired taunt?
				if (p.cmd.buttons & BT_CUSTOM3) and G_GametypeHasSpectators() 
				or (p.cmd.buttons & BT_TOSSFLAG) and (p.cmd.buttons & BT_CUSTOM2)
			
				or (p.cmd.buttons & BT_WEAPONPREV) and (p.cmd.buttons & BT_WEAPONNEXT)
				and (p.weaponprevhold < 5) and (p.sms_weaponnexthold < 5)
			
					if (not netgame or not s.sms_tauntcooldown)
					and not (p.pflags & (PF_SPINNING|PF_JUMPED|PF_STARTDASH))
					and not (SMS_Locked(s, p, true, true) and not (p.exiting or p.realtime < 0))
						if G_GametypeHasSpectators() --Risking it all here.
							p.powers[pw_nocontrol] = max($,61)
						else
							p.powers[pw_nocontrol] = max($,2)
						end
						s.state = S_SMSTAUNT1
						s.sms_tauntcooldown = 190
						s.SMS_locked = true
						s.smsaurablastivate = nil
						s.smsaurablastivatecd = 0
						p.aiming = 0
						if not (s.SMS_squashtable and s.SMS_squashtable.timer > 2)
							s.spritexscale = FRACUNIT-11100
							s.spriteyscale = FRACUNIT+14100
							s.SMS_squashtable = {timer = 4, Xspeed = 3200, Yspeed = 3990}
						end					
						local tauntrng = SMSLocalRNG(0,3,s)
						if (s.eflags & MFE_UNDERWATER) --Trash talking underwater, good job.
							S_StartSound(s, sfx_splash)
							S_StartSound(s, sfx_s3k7d)
							P_SpawnMobjFromMobj(s, 10*s.scale, 6*s.scale, 14*s.scale, MT_SMALLBUBBLE)
							P_SpawnMobjFromMobj(s, -10*s.scale, -6*s.scale, 29, MT_SMALLBUBBLE)
							P_SpawnMobjFromMobj(s, 14*s.scale, 5*s.scale, 23*s.scale, MT_MEDIUMBUBBLE)
							P_SpawnMobjFromMobj(s, 5*s.scale, -11*s.scale, 15*s.scale, MT_MEDIUMBUBBLE)
						else
							S_StartSound(s, sfx_victr1+tauntrng)
							S_StartSoundAtVolume(s, sfx_s1b8, 56)
						end
						return 3 --
					end
				end
			end

			--Create a Power Share aura, inspired by 06's super scene before the Solaris fight!
			if (p.cmd.buttons & BT_CUSTOM3) 
			or p.dobotpowershare and p.bot 
					
				if (gametype == GT_COOP)
				and not (p.powersharerestrict == 2 or (p.powersharerestrict == 1 and not All7Emeralds(emeralds)))
					if (not s.bhlcamtimer or s.bhlcamtimer == 2)
					and (p.camerascale < 2*FRACUNIT)
						p.camerascale = FRACUNIT*3/2
						s.bhlcamtimer = 3
					end
					s.smsglowticket = 2
					P_SpawnMobj(s.x+90*s.scale, s.y+90*s.scale, s.z+32*s.scale, MT_POWERSHARE)
					P_SpawnMobj(s.x, s.y+100*s.scale, s.z+32*s.scale, MT_POWERSHARE)
					P_SpawnMobj(s.x+100*s.scale, s.y, s.z+32*s.scale, MT_POWERSHARE)
					P_SpawnMobj(s.x-90*s.scale, s.y-90*s.scale, s.z+32*s.scale, MT_POWERSHARE)
					P_SpawnMobj(s.x, s.y-100*s.scale, s.z+32*s.scale, MT_POWERSHARE)
					P_SpawnMobj(s.x-100*s.scale, s.y, s.z+32*s.scale, MT_POWERSHARE)
					
					if p.hypermysticsonic and (leveltime % 6 == 0)
						local ghs = SMS_ElectricBody(s, 7, SKINCOLOR_SKY)
						ghs.spriteyscale = $+(FRACUNIT/3)
					end
					if not s.sms_auradelay
						local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSPOWERAURA)
						if ghs and ghs.valid 
							ghs.tracer = s
							ghs.dispoffset = 10
							if p.hypermysticsonic
								ghs.scale = $*5/3
							end
							if (p == displayplayer) and not CV_FindVar("chasecam").value
								ghs.flags2 = $|MF2_DONTDRAW
							end
						end
						s.sms_auradelay = 16
						if not s.smsaurablastivate --Initial burst.
							S_StartSound(s, sfx_s3ka0)
							s.smsaurablastivatecd = 29
							s.smsaurablastivate = true
							local ghs = P_SpawnMobjFromMobj(s, 0,0, -25*s.scale, MT_BURSTWAVE)
							if s.color == SKINCOLOR_CRIMSON
								ghs.color = SKINCOLOR_RED
							elseif not p.hypermysticsonic and SMSisyellow(s)
								ghs.color = SKINCOLOR_BONE
							else
								ghs.color = s.color
							end
							if SMS_Graphix(s) == 0
								if p.hypermysticsonic
									for i = -3,0
										SMS_FlameWalls(s, FRACUNIT-27500, 77*FRACUNIT, 256*i*FRACUNIT, A|FF_TRANS60, SKINCOLOR_MYSTICAL2-i, 32-(i*6), s)
									end
									local ghs = SMS_FlatWave(s, SKINCOLOR_SMS4R, 34, 3, 0)
									ghs.sttspeed = nil
									ghs.frame = H|FF_FULLBRIGHT
									ghs.sttfade = 20
									ghs.renderflags = RF_FULLBRIGHT|RF_FLOORSPRITE|RF_NOCOLORMAPS|RF_ABSOLUTEOFFSETS
									ghs.spritexoffset = 111*FRACUNIT
									ghs.spriteyoffset = 108*FRACUNIT
									SMS_LensFlare(s, SKINCOLOR_EMERALD, ANG1*9, 24, FRACUNIT/2)
								else
									HMS_AuraStorm(s, FRACUNIT-12250, SKINCOLOR_WHITE, 11, G|TR_TRANS70)
																	
									ghs = SMS_FlatWave(s, SKINCOLOR_WHITE, 9, 15, 2)
									ghs.frame = $+FF_TRANS20
								end							
							end								
							local ghs = SMS_LightFlash(s, SKINCOLOR_WHITE, FRACUNIT-20000, 6, Z+3|TR_TRANS30)
							ghs.fuse = (p.hypermysticsonic) and 16 or 10
						end
					elseif (p.panim == PA_IDLE) and not p.hypermysticsonic
						s.state = S_PLAY_STND --Stop laying on the floor, you lazy ass!
						s.tics = 99 s.anim_duration = 99
					end					
				end
				return 4 --
				
			elseif s.smsaurablastivate and not s.smsaurablastivatecd
			
				if not (p.cmd.buttons & BT_CUSTOM3)
				or not (p.speed < 4*s.scale)
				or not P_IsObjectOnGround(s)
					s.smsaurablastivate = nil
				end		
				return 5 --Power share stats cancel.
			end
end)

--The oldest Lightdash I've made. Pretty crappy, but oh well, I don't feel like redoing this AGAIN.
rawset(_G, "SMS_LightDash", function(s, p)
	if not G_PlatformGametype()
	or not s.health	
		p.lightdashenable = 0 --INSTANTLY disables Lightdash entirely!
		p.smsrdash = 0
		p.lightdashstart = 0
		s.smsldcheck = 0
		s.smsrdash = 0		
		return --
	elseif p.smsrdash or p.lightdashstart
		s.SMS_locked = true
		if (p.cmd.buttons & BT_SPIN) and (p.spinhold <= 2) //Failsafe so you can exit lightdash at will.
			p.smsrdash = 0
			p.lightdashstart = 0
			p.pflags = $ & ~PF_SPINNING & ~PF_JUMPED
			s.momx = $/2
			s.momy = $/2
			P_SetObjectMomZ(s, FRACUNIT*4)
			S_StartSound(s, sfx_s1a2)
			s.state = S_PLAY_FALL
			s.smsldcheck = 0
			s.smsrdash = 0
			return --
		end
	end
	
	--Set up the ring finder object!
	if not s.smsldcheck
	and not p.lightdashstart
	and not (SMSSHIELD(p) == SH_ATTRACT or SMSSHIELD(p) == SH_THUNDERCOIN)
	and (p.panim != PA_PAIN)
	and not p.hypermysticsonic
	and not s.smspkuret --Pickup takes priority.
	and not p.powers[pw_carry]
	and not p.sms_comethaymaker
	and not p.sms_intoship
	and not p.knucklewaving
	
			local ANGLE = ((p.pflags & PF_ANALOGMODE) and (p.speed > 4*s.scale)) and R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) or s.angle
			local ghs = P_SpawnMobj(s.x+FixedMul(158*s.scale,cos(ANGLE)), s.y+FixedMul(158*s.scale,sin(ANGLE)), s.z-160*s.scale, MT_SMSECHECK)
			if ghs and ghs.valid
				ghs.justcheckin = true
				ghs.target = s
				ghs.height = 320*FRACUNIT
				ghs.radius = 190*FRACUNIT
			end
			if ((p.cmd.buttons & BT_CUSTOM1) and p.sms_c1 == 1 and not p.smsldtf)
			or ((p.cmd.buttons & BT_TOSSFLAG) and not (p.sms_tossflaghold > 1)) //waaaaaaa
				s.smsldcheck = 2
				local ghs = P_SpawnMobj(s.x+FixedMul(158*s.scale,cos(ANGLE)), s.y+FixedMul(158*s.scale,sin(ANGLE)), s.z-160*s.scale, MT_SMSECHECK)
				if ghs and ghs.valid
					ghs.target = s
					ghs.height = 320*FRACUNIT
					ghs.radius = 190*FRACUNIT
				end
			end
	end
	
	--Do we have a ring target?
				if s.smsldcheck and s.smsrdash
				and s.target and s.target.valid and s.target.health
				local t = s.target
				
						if P_CheckSight(s, t) and t == s.smsrdash
						and ( (FixedHypot(FixedHypot(t.x-s.x, t.y-s.y), t.z-s.z)) < 500*s.scale)
						
							s.momz = 0
							s.state = S_SMS_LIGHTDASH
							p.powers[pw_flashing] = 0
							p.sms_stompanim = 0
							p.pflags = $ & ~PF_JUMPED
							p.sms_afterstompthok = false
							
							if not (abs(s.momz) > 50*s.scale)
								if s.lastldrx != nil and s.lastldry != nil
								and not (p.lightdashstart <= 1)
									s.angle = R_PointToAngle2(s.lastldrx, s.lastldry, t.x, t.y)
									s.lastldrx = t.x //Look in the same direction as the ring-lines, not relative to your own angle.
									s.lastldry = t.y
									if (t.z < t.floorz+60*s.scale)
										s.z = t.z
										s.momz = 0
										P_TryMove(s, t.x, t.y)
									end
								else
									s.lastldrx = t.x
									s.lastldry = t.y
									s.angle = R_PointToAngle2(s.x, s.y, t.x, t.y)
								end 
							end
							if (p.pflags & (PF_DIRECTIONCHAR|PF_ANALOGMODE))
								s.forcesmsangletimer = 2
								s.forcesmsangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
							end
							s.smsldcheck = 3 //Extend lightdash!
							if (p.lightdashstart < 6) or (p.lightdashstart % 2 == 0)
								A_SMSSTOMPSMOKE(s, SMSLocalRNG(7, 24,s), MT_IVSP)
							end
					/*	elseif not (t and t.valid and t.health)
							local ghs = P_SpawnMobj(s.x+FixedMul(158*s.scale,cos(s.angle)), s.y+FixedMul(158*s.scale,sin(s.angle)), s.z-310*s.scale, MT_SMSECHECK)
							if ghs and ghs.valid
								ghs.target = s
								ghs.height = 620*FRACUNIT
								ghs.radius = 190*FRACUNIT
							end */
						end	
						if s.state == S_SMS_LIGHTDASH
							local ghs = P_SpawnGhostMobj(s)
							ghs.rollangle = s.rollangle
							p.smsrdash = 5
							p.lightdashstart = $+1
							p.pflags = $ & ~(PF_SPINNING|PF_STARTDASH)
							return true --
						end
				end
			
				if p.smsrdash or p.lightdashstart //p.smsrdash and s.smsrdash are different! The former's a timer, the latter a target failsafe.
					if P_PlayerInPain(p)
					or p.powers[pw_carry] --INSTANT CANCEL
					or (p.smspickup and p.smspickup == s.target)
						p.smsrdash = 0
						p.lightdashstart,s.smsrdash = 0,0
						s.smsldcheck = 0
						p.pflags = $ & ~(PF_JUMPED|PF_THOKKED|PF_NOJUMPDAMAGE)
						s.lastldrx,s.lastldry = 0,0
						return false --
					
					elseif not s.smsldcheck --Ran out of rings? Stop lightdashing!
				
						p.smsrdash = 0
						p.lightdashstart,s.smsrdash = 0,0
						s.lastldrx,s.lastldry = 0,0			
					
						if P_IsObjectOnGround(s)
							s.state = S_PLAY_RUN --Go straight to running.
							p.pflags = $|PF_JUMPED & ~(PF_SPINNING|PF_STARTJUMP)
						else				
							if (s.z < s.floorz+60*s.scale) and not (s.eflags & MFE_VERTICALFLIP)
								s.z = s.floorz
								P_SetObjectMomZ(s, -FRACUNIT)
							elseif (s.momz >= ((FRACUNIT*27)*P_MobjFlip(s))) //You can fly high, sure! Is cool, dog.						
								SMS_SideFlip(s, p)
								P_SetObjectMomZ(s, FRACUNIT*26) //...but let's at least make it manageable, instead of smashing player heads into the skybox.
							end				
							s.momx,s.momy = $*2/3,$*2/3 --Lose a tiny bit of speed.		
							
							if (s.state != S_SMS_SIDET) and not P_IsObjectOnGround(s)
								s.state = S_PLAY_ROLL
								p.pflags = $|PF_JUMPED & ~(PF_SPINNING|PF_STARTJUMP)
							end
						end
						return false --
					end
				end	
end)

--Ragdoll when flinging from foes or out of minecarts!
rawset(_G, "SMS_RagDoll", function(s, QUICKSTART)
	if QUICKSTART and not s.SMS_CrashRoll
		s.SMS_CrashRoll = {
		timer = 27,
		GFX = (tonumber(QUICKSTART)) or 1,
		extratime = 12
		}
	end

	local p = s.player
	local yu = s.SMS_CrashRoll

	--Convert state.
	if (s.eflags & MFE_SPRUNG)
		s.state = S_PLAY_PAIN
		p.panim = PA_PAIN
	end
	--Cancel conditions.
	if not yu.timer
	or (p.panim != PA_PAIN and yu.init != nil)
	or P_IsObjectOnGround(s) and yu.extratime != 12  and not (s.momz*P_MobjFlip(s) > 0)
	or p.powers[pw_carry]
	or (s.eflags & MFE_SPRUNG)
	or p.hypermysticsonic
	or not s.health
		
		--Any landing effects?
		if yu.timer and not p.powers[pw_carry] and not p.hypermysticsonic and s.health
			if (p.speed > s.scale) and (p.pflags & (PF_ANALOGMODE|PF_DIRECTIONCHAR))
				p.drawangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
			end
			if p.sms_autoboosthold and not (p.cmd.buttons & BT_CUSTOM1)
				p.sms_autoboosthold = 0
			end
			if P_IsObjectOnGround(s)
				for i = 1,8
					local ghs = P_SpawnMobjFromMobj(s, 
					FixedMul(8*s.scale, cos(s.angle+(ANGLE_45*i))),
					FixedMul(8*s.scale, sin(s.angle+(ANGLE_45*i))),
					0, MT_THOK)
					ghs.fuse = 14 ghs.tics = 2
					if (s.eflags & MFE_UNDERWATER)
						ghs.state = S_SPINDUST_BUBBLE1
					else
						ghs.colorized = true
						ghs.color = SKINCOLOR_BEIGE
						ghs.state = S_SPINDUST2
					end
					P_Thrust(ghs, s.angle+(ANGLE_45*i), 3*s.scale)
					ghs.spriteyscale = $+45555
					ghs.spritexscale = ghs.spriteyscale
				end	
				S_StartSoundAtVolume(s, sfx_s1b6, 190)
				S_StartSoundAtVolume(s, sfx_kc50, 88)
				S_StartSoundAtVolume(s, sfx_corkh, 20)	
				if (p.speed < 36*s.scale) and not (p.pflags & PF_SPINNING)
					s.sms_juststomped = 6
				elseif not s.SMS_squashtable
					s.spritexscale = FRACUNIT+5100
					s.spriteyscale = FRACUNIT-9100
					s.SMS_squashtable = {timer = 3, Xspeed = 2200, Yspeed = 3500, ground = true}			
				end
			end			
		end
		if yu.rollangle != nil
			s.rollangle = 0
			s.SMS_WallBounce = nil
		end
		s.SMS_CrashRoll = nil		
		
	else --Setup!
	
		if yu.init == nil --Initial setup effects.
			s.SMS_WallBounce = nil
			yu.init = 1
			yu.rollspeed = min(ANG1*77, max(ANG1*12, FixedAngle( ((abs(s.momx)+abs(s.momy)) or 3)/3)) )
			yu.spinspeed = max(ANGLE_22h/2, FixedDiv(FixedMul(yu.rollspeed, 100), SMSLocalRNG(70,125,s)) )
			yu.rollangle = 0
			yu.faceangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
			
			--Now for SMS stuff...
			s.airboostroll = 0
			if (p.cmd.sidemove < -9)
				s.hmsrotateright = nil
			elseif (p.cmd.sidemove > 9)
				s.hmsrotateright = true
			elseif s.hmsrotateright
				s.hmsrotateright = nil
			else
				s.hmsrotateright = true
			end
			if (p.sms_boostlevel >= 65)
				s.sms_boostloss = 12
				p.sms_boostlevel = 32
			end
			s.SMS_locked = true
			s.SMS_MineCarting = nil
			s.sms_painr = 0
			p.sms_advfall = 0
			p.sms_stompanim = 0
			p.sms_afterstompthok = false		
			p.pflags = $ & ~(PF_THOKKED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH|PF_STARTJUMP|PF_JUMPED)
			s.state = S_PLAY_PAIN
			p.panim = PA_PAIN
			
			--if not specified as 0 or false, you'll always have extra ragdoll time!
			if yu.extratime == nil
				yu.extratime = 1
			elseif yu.extratime == 12
				if P_IsObjectOnGround(s)
					s.z = $+(24*s.scale*P_MobjFlip(s))
					P_SetObjectMomZ(s, 6*FRACUNIT, true)
				end
			end
		end

		--Starting GFX.
		local GFXLVL = (yu.GFX) or 9
		if yu.init == 1		
			if (GFXLVL > 1) --Basic harsh impact gfx.
				A_SMSquake(s, 65*FRACUNIT, 4, 500)
				P_NukeEnemies(s, s, 0)
				SMS_WindBlast(s)
				if (GFXLVL > 2) --Bright impact flash
					local ghs = SMS_ElectricRecoil(s)
					ghs.tracer = s	
					ghs.color = SKINCOLOR_SUPERSKY5
					ghs.colorized = true
					ghs.frame = $+FF_TRANS10
					if SMS_Graphix(s) != 2
						ghs = SMS_LightFlash(s, SKINCOLOR_MYSTICAL3)
						ghs.tracer = s
						ghs.frame = $+FF_TRANS20
						ghs.spriteyoffset = -13*FRACUNIT
						
						SMS_LightClash(s, SKINCOLOR_SMS5R, s)
					end				
					if GFXLVL > 3 --Starry explosion + sfx
						S_StartSound(s, sfx_kc31)
						for d = 0,15
							P_SpawnParaloop(s.x, s.y, s.z+20*s.scale, 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
						end
					end
				end
			end	
		elseif (yu.init > 4) and (yu.init < 14) and (GFXLVL > 1)
			if (p.speed > 4*s.scale) or abs(s.momz) > 4*s.scale 
				local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK) --Make a smoke trail?
				ghs.state = S_SPINDUST1+SMSLocalRNG(0,2,s)
				ghs.colorized = true
				ghs.color = (SKINCOLOR_SUPERGOLD5+5)-(yu.init*2)
				ghs.fuse = 32
				ghs.frame = ($ & FF_FRAMEMASK)|TR_TRANS60
				ghs.scale = (s.scale*yu.init)/5
				ghs.scalespeed = $/2
				ghs.destscale = $*99
				if (yu.init < 9)
					ghs.flags2 = $|MF2_SHADOW
				end
			end
		end
		yu.init = $+1
		
		--And the actual behaviour starts!
		if yu.timer == 1 and yu.extratime
			yu.extratime = $+1
			if (yu.extratime > 400) --Failsafe kicks in after roughly 12 seconds.
				yu.timer = 0
			end
			s.momx = FixedDiv(FixedMul($,210),211)
			s.momy = FixedDiv(FixedMul($,210),211)			
		else
			yu.timer = $-1
			if (yu.timer < 11) --Start slowing down at the tail end.
				s.momx = FixedDiv(FixedMul($,50),51)
				s.momy = FixedDiv(FixedMul($,50),51)
			end	
		end
		
		--Roll in the direction of HMSrotate.
		local DIRECT = (s.hmsrotateright == true) and 1 or -1
		yu.rollangle = $+(yu.rollspeed*DIRECT)
		yu.faceangle = $+(yu.spinspeed*DIRECT)
		
		s.rollangle = yu.rollangle
		p.drawangle = yu.faceangle
		
		if yu.rollspeed > ANG2*3 --Gradually slow rollspeeds to some level.
			yu.rollspeed = FixedDiv(FixedMul($, 100), 101)
		end
		if yu.spinspeed > ANG2*5
			yu.spinspeed = FixedDiv(FixedMul($, 100), 101)
		end	
		if p.smsrtable and p.smsrtable.direct --Influence flung direction?
			local XS = p.smsrtable
			P_Thrust(s, yu.faceangle, (s.scale/-88)*XS.direct)
			P_Thrust(s, XS.turnang, (s.scale/88)*XS.direct)
		end

		s.SMS_wallwait = 3
		if s.SMS_WallBounce and (yu.init % 3 == 0)
			s.SMS_WallBounce = $-1
			if not s.SMS_WallBounce
				s.SMS_WallBounce = nil
			end
		end
		if not (s.SMS_ControlAlter and s.SMS_ControlAlter.timer > 1)
			s.SMS_ControlAlter = {
			timer = 2,
			removebuttons = (BT_CUSTOM1|BT_CUSTOM2|BT_CUSTOM3|BT_SPIN|BT_TOSSFLAG|BT_WEAPONPREV|BT_WEAPONNEXT),
			freecondition = 3}
		end
		return s --
	end
end)
--Find enemies, then start a homing attack if possible! Used in conjuction with the actual CA_HOMINGTHOK behaviour!
--ForceHomingAttack"
rawset(_G, "SMS_TryForcedHomingAttack", function(s, p)
	if SMS_Locked(s, p)
	or (p.charability != CA_HOMINGTHOK) --Active 99% of the time, but still.
	or P_IsObjectOnGround(s)
	or p.homing
		return --
	end	
	local t --Our target, if we find one!
	
	if p.powers[pw_sneakers] or (p.sms_boostlevel >= 65) --Further reaching homing attack! 
	or (s.state == S_SMS_AIRBOOST) or ((p.pflags & PF_THOKKED) and not (p.cmd.buttons & BT_SPIN))
		t = P_LookForEnemies(p, true, true) --Extended range search first!
		if not (t and t.valid) --Found nothing.
			t = nil
		else
			if (t.flags & (MF_SPRING|MF_MONITOR)) and not (t.flags & (MF_ENEMY|MF_BOSS))
				t = nil --No extended range on these. Unless they're also enemies somehow.
			elseif not (p.powers[pw_sneakers] or (p.sms_boostlevel >= 65)) --You only get to home upwards in boostmode!
				if (s.eflags & MFE_VERTICALFLIP)
					if (t.z < s.z-48*s.scale)
						t = nil
					end
				elseif (t.z > (s.z+abs(s.momz)+s.height+48*s.scale)) --But...we DO have a little leniency, unlike vanilla!
					t = nil
				end
			end
		end
	end
	--Regular range.I do this because the "extra range" version has a 30 degree aiming limit. (For...some reason) 
	--It thus might occasionally miss something the regular version wouldn't.
	if not (t and t.valid and t.health) 
		t = P_LookForEnemies(p, true)
		if not (t and t.valid and t.health) --Still haven't found a valid, living object?
			return -- Then give up.
		end
	end	
	
	if (t.flags & (MF_SHOOTABLE|MF_SPRING))
	or (t.flags2 & MF2_INVERTAIMABLE)
	or ((t.type == MT_METALSONIC_BATTLE) and (t.state != S_METALSONIC_VECTOR))
		--None of these objects!
		if ((t.type >= MT_RING) and (t.type <= MT_EMBLEM)) --The hell, don't home into collectibles?
		or (t.type == MT_EGGMAN_BOX) or (t.type == MT_EGGMAN_GOLDBOX) --Why are these even home-able?
		or (t.type == MT_BIGMINE)
		or (t.type == MT_FAKEMOBILE)
		or (t.type == MT_EGGSHIELD)
			return --
		end
		--Can we see it, and is it in front of SMS?
		if not P_CheckSight(s, t)
			return --
		end	
		--Make sure they're in FRONT of me. 
		local TARGETANGLE = R_PointToAngle2(s.x, s.y, t.x, t.y)
		if SMS_AngleDifference( TARGETANGLE , s.angle, 2) > ANG1*76
			if p.smsrtable and (p.smsrtable.direct > 35) --Second try, but now with input angle in consideration.
				if SMS_AngleDifference(TARGETANGLE, SMS_PlayerAngle(s,p)-s.angle, 2) > ANG20*4
					return --
				end
			else
				return --
			end
		end	
		--SUCCESS! Set up le stuff.
		
		s.rollangle = 0 
		s.smstrickroll = false
		s.airboostroll = 0
		p.sms_advfall = 0
		s.retmhtsfx = 0
		
		s.SMS_squashtable = nil
		s.spritexscale = FRACUNIT
		s.spriteyscale = FRACUNIT
		s.spriteyoffset = 0
		
		p.charflags = $ & ~SF_MULTIABILITY
		p.actionspd = skins["sms"].actionspd
		
		if p.powers[pw_sneakers]
		or (p.sms_boostlevel > 64)
		or (s.state == S_SMS_AIRBOOST)
			if SUBVERSION != 11
				p.powers[pw_strong] = $|STR_SPRING
			end
			P_TryMove(s, s.x-s.momx, s.y-s.momy, true)	--So we don't overshoot next frame.	
		end
		

		p.sms_wallstat = 0 --Remove walljumping data, homing attacks take priority.
		s.SMS_wallwait = (p.cmd.buttons & BT_SPIN) and 8 or 5
		SMS_WallJumpDelete(s, true)			
		s.SMS_lastwallANG = nil
		s.SMS_lastwallNUM = nil
		s.SMS_WJpenalty = 0
		
		if s.SMShomingspeed == nil								
			s.SMShomingspeed = 24*s.scale
			s.SMShomingangle = s.angle	
		end
		s.tracer = t --We home into our tracer instead of target, apparently?
		if not (s.target and s.target.valid)
			s.target = t --Ehh, sure, why not, even if it does nothing.
		end
		if (p.pflags & PF_THOKKED) --Play thok sound again manually, since the game won't.
			S_StartSound(s, sfx_thok)
		end
		s.angle = R_PointToAngle2(s.x, s.y, t.x, t.y)
		p.drawangle = s.angle
		p.cmd.forwardmove = 0
		p.cmd.sidemove = 0
		p.homing = 105 --With this and a tracer, now the default homing should take over.
		p.pflags = $|PF_JUMPED|PF_JUMPDOWN & ~(PF_SPINNING|PF_STARTDASH|PF_NOJUMPDAMAGE|PF_STARTJUMP)
		s.SMS_locked = true --No other actions this frame.
		if (s.state != S_PLAY_ROLL) --SMS' thokanim will kick in 1 frame later!
			s.state = S_PLAY_ROLL
		end	
		return t --Return the target for shits and giggles.
	end
end)

--Return max difference between sector floorheight and an object's?
rawset(_G, "SMS_MobjSecHeightDiff", function(s, line)
	local secT = (s.subsector) and s.subsector.sector --Object sector.
	if not line.backsector --There IS no other sector.
	or not secT --What? We're not in a sector???
		return FRACUNIT*4004+404 --
	end
	local secL = (secT != line.backsector) and line.backsector or line.frontsector
	if (s.eflags & MFE_VERTICALFLIP)
		return abs(s.z-secL.ceilingheight) --
	else
		return abs(s.z-secL.floorheight) --
	end
	return 0 --
end)

--Your wall is a skybox wall. Ignore! Leniency must be filled in.
rawset(_G, "SMS_SkyBoxWall", function(s, line, leniency)
	local secT = (s.subsector) and s.subsector.sector --Object sector.
	local EXTRA = leniency
	if EXTRA == nil EXTRA = 0 end

	if line and secT
		local lineside = ((secT != line.backsector) and line.backsector) and line.backside or line.frontside --lineside facing player.
		local secL = lineside.sector	
		if line.special == 41 --Horizon wall effect.
			return true --
		end
		if (secL.floorheight == secL.ceilingheight)
		and (secL.ceilingpic == "F_SKY1" or not secL.ceilingpic)
		and secL.floorheight != nil --Rare glitch prevention.
		and not lineside.toptexture
			if not ( s.z <= (secL.floorheight+((s.height)+s.momz+leniency)) ) --Only cancel if we're ABOVE the skywall.
				return true --It's a skybox wall.
			end
		end
	end
	return false --
end)

--Uses leveltime to return a desired number going up each game frame, then loops back to 0.
rawset(_G, "SMS_TimeFrame", function(no, STARTAHEAD)
	local EXTRA = (STARTAHEAD) or 0
	local result = 0 --STARTAHEAD means you can start at for example frame W if needed.
	for i = no-1,0,-1
		if (leveltime % max(i,1)) == 0
			result = $+i
			break
		end
	end
	return result+EXTRA
end)

--Return a player-preferred angle! Prioritizes input angle, then tries momentum angle, then lastly uses player facing direction.
rawset(_G, "SMS_PlayerAngle", function(s,p, NOINPUT)
	if not NOINPUT
		if ( (abs(p.cmd.sidemove) > 4) or (abs(p.cmd.forwardmove) > 4) )
			return (p.cmd.angleturn<<16)+(R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
		end
	end
	if (s.momx or s.momy)
		return R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) --
	end
	return s.angle --
end)

--How big is the difference from one angle to another? This function returns the answer!
rawset(_G, "SMS_AngleDifference", function(ANG_1, ANG_2, Rformat, maxANG, divide)
	local diff = (ANG_1-ANG_2)
	if not diff return 0,1 end --The result's 0. ...that was easy.	
	
	diff = (diff > ANGLE_180) and InvAngle(InvAngle(diff)/((divide) or 1)) or diff/((divide) or 1)	
	
	--This function NEVER gives negative results. 
	--ANGLE_270 or -ANGLE_90 for example would return as ANGLE_90, with PLUS simply becoming -1.
	local PLUS = (diff < 0) and -1 or 1
	if diff < 0 diff = InvAngle(diff) end
	
	if maxANG --Return a maximum value?
		diff = min(maxANG, diff)
	end
	if Rformat != 2
		if Rformat == 1 --Turns the result into FRACUNITs. From 0 to 180*FRACUNIT
			diff = AngleFixed(diff)
		elseif Rformat == 0 --Turns result into regular numbers. From 0 to 180.
			diff = (diff >= ANG1) and diff/ANG1 or 0
		end
	end
	return diff, PLUS
end)

--What wall-busting powers will we set for SMS?
rawset(_G, "SMS_WallBustPower", function(s,p)
	if SUBVERSION == 11 --Barely supported anymore.
		p.charflags = $|SF_CANBUSTWALLS
		if (p.sms_stompanim or p.sms_afterstompthok)
			p.charflags = $|SF_STOMPDAMAGE
		elseif (p.charflags & SF_STOMPDAMAGE)
			p.charflags = $ & ~SF_STOMPDAMAGE
		end
		return false
	end
	
	if p.hypermysticsonic --ALL the power, ALL the time!
		p.powers[pw_strong] = STR_BUST|STR_ATTACK|STR_HEAVY|STR_SPIKE|STR_GUARD 
		p.charflags = $|SF_CANBUSTWALLS
		return true --
	else
		p.powers[pw_strong] = 0 --Fine, context sensitivity it is...
		p.charflags = $ & ~SF_CANBUSTWALLS
	end
	
	--Destroy frontal walls.
	if (p.pflags & PF_SPINNING) and not (p.pflags & PF_JUMPED)
	or p.footsweepanim 
	or p.sms_didairboost
	or p.sms_ringburstfly == 2
	or (p.speed > 60*s.scale) and (p.powers[pw_sneakers] or (p.sms_boostlevel >= 65))
		p.powers[pw_strong] = $|STR_SPIKE|STR_WALL
	/*	local ANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
		local sectorz = R_PointInSubsector( --SRB2 is fickle asf...
		s.x+s.momx+FixedMul(2*s.scale, sin(ANGLE)),
		s.y+s.momy+FixedMul(2*s.scale, sin(ANGLE))).sector
		if sectorz and sectorz.ffloors()
			for fof in sectorz.ffloors()
				if (fof.flags & FF_EXISTS)
				and ((fof.flags & FF_STRONGBUST) or (fof.flags & FF_BUSTUP))
				and not (s.z+s.momz > fof.topheight)
				and not (s.z+s.height+s.momz < fof.bottomheight)
					EV_CrumbleChain(sectorz, fof)
				end
			end
		end */	
	end
	
	--Destroy stuff from below.
	if p.sms_stompanim
	or p.sms_afterstompthok
	or (p.smsdivetimer > 17)
		p.powers[pw_strong] = $|STR_HEAVY|STR_FLOOR
	end
	if p.sms_ringburstfly == 1
	or s.SMS_WallJumpTable and (s.SMS_WallJumpTable.straightjump == 777) and (s.SMS_WallJumpTable.timer > 8) 
		p.powers[pw_strong] = $|STR_UPPER|STR_WALL|STR_CEILING
	end
	
	if p.homing and (p.powers[pw_sneakers] or (p.sms_boostlevel > 64))
--	or s.SMS_HomingBolt		
		p.powers[pw_strong] = $|STR_SPRING
	end
	return true --
end)

--Enter the hometrickanim state to do a homing trick!
rawset(_G, "SMS_HomingTrick", function(s,p)
	if P_IsObjectOnGround(s) and not (s.momz*P_MobjFlip(s) > 0) return end --
		if s.SMS_WallJumpTable or p.homing
			SMS_WallJumpDelete(s, true)
			s.SMS_WJpenalty = 0
			s.SMS_lastwallANG = nil
			s.SMS_lastwallNUM = nil
			s.SMS_wallwait = 8
		end
		
		if not p.lddiscount and p.lasthomingtrick != 1
			s.state = S_SMS_FLIPT1
			p.lasthomingtrick = 1			
		elseif p.lddiscount == 1 and p.lasthomingtrick != 2
			s.state = S_SMS_ROLLT1
			p.lasthomingtrick = 2			
		elseif p.lasthomingtrick != 3
			s.state = S_SMS_PREHOMET1
			p.lasthomingtrick = 3
			s.sms_spintrickspeed = 27
		else
			s.state = S_SMS_FLIPT1
			p.lasthomingtrick = 1
		end
		if p.lasthomingtrick == 1
			s.spriteyoffset = -8*FRACUNIT
			s.spritexscale = FRACUNIT-16500
			s.spriteyscale = FRACUNIT+14000
			s.SMS_squashtable = {timer = 5, Xspeed = 5200, Yspeed = 5000, air = true}	
		end
		p.hometrickanim = true
		p.pflags = $|PF_JUMPED|PF_NOJUMPDAMAGE & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH 
		p.homingtrickcharge = 0
end)

//Retrieve the game's graphics value.
rawset(_G, "SMS_Graphix", function(s)
	for z in players.iterate
		if z.valid and z.smsgraphix != nil
			return z.smsgraphix --You're done! Returns 2 for shit graphics, 1 for mid-graphics.
		end
	end
	return 0 --Return 0 otherwise, AKA full graphics.
end)

--Retrieve current gore(robot gibs) level.
rawset(_G, "SMS_Gore", function(s, addnum)
	for z in players.iterate
		if z.valid and z.srgorelvl != nil
			if addnum --Decrease/Increase the gorelvl?
				z.srgorelvl = max($+addnum, 1)
			end
			return z.srgorelvl --You're done! Anything over 200 is usually ALOT of gibs...
		end
	end
	return 300 --Return 300, no gore.
end)

//Returns appropriate HMS or SMS haymaker state. Put 1 in num to return HAYMAKER2, put in 0 for HAYMAKER1.
rawset(_G, "SMSHayMakerState", function (p, num)
	if p.hypermysticsonic
		return S_HMSCOMETHAYMAKER1+num
	else
		return S_SMSCOMETHAYMAKER1+num
	end
end)

--Function to kill stuff! Var1 is distance. Var2 is inactive.
--Var3 is the instant kill's source. Var4 is a custom damage amount.
--Never spam this function! It'll lag like hell on bigger levels.
function A_SMSkillstuff(s, var1, var2, var3, var4, var5)
	if not (s.valid and var1) return false end --
	local DAMAGE = (tonumber(var5) and (var5 >= 4)) and var5 or 4
	for m in mobjs.iterate(mobjs)
		if (m.flags & (MF_ENEMY|MF_BOSS)) --Enemy or Boss?
			if var4 and m.health == 1 continue end

			if FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z) <= var1
			and m.health and (m.info.mass != 2006)
			
				if var4 --Regular damage!
					if not (m.z < s.z-150*s.scale*P_MobjFlip(s) )
					and not (m.flags2 & MF2_FRET)
						m.health = max($-1,1)
					end
				elseif var5 --Custom Damage? Currently only works with 4 or higher.
					if not (m.flags2 & MF2_FRET)
						m.health = max($-DAMAGE, 1) --DamageMobj will finish the job!
					end
				else --Murder.
					P_KillMobj(m, s, (var3) or s) --Var3 is given source.
				end
			--	if var2 == 777 break end --End search early if we find even 1 mobj.
			end
		end
	end
end

//Dedicated functions to start and end Mystic Super state. AKA SMS' custom powershare transformation!
function A_STARTMYSTICSUPER(s, var1)
	if not s.player
	or (s.skin == "samus")
	or (s.skin == "basesamus") --Don't mess with Samus.
	or s.player.sms_intoship
		return --
	end
	local p = s.player
	
	if p.isclassic or p.isclassic == nil  --For Rumia's mod. Remove or it'll bug later.
		p.isclassic = false
		p.isclassicsms = true
	end

	if not var1 --Perform the transform animation?
		SMS_SkinSwap(s, p) --Reset ALL values!
		p.pflags = $|PF_THOKKED & ~(PF_STARTDASH|PF_SPINNING)
		p.powers[pw_super] = 0 --Turn off regular super if you're super.
		p.powers[pw_nocontrol] = 24
		p.powers[pw_invulnerability] = 7777
		p.powers[pw_sneakers] = min($,1)
		p.powers[pw_extralife] = min($,1)
		p.premysticscolor = s.color
		p.mysticsuper = 1
		s.momx,s.momy,s.momz = 0,0,0 --Stop movmenet
		SMS_StopStick(s)
		s.SMS_locked = true --Important frame.
		s.state = S_PLAY_SUPER_TRANS1
		p.sms_transformstate = 0 --Literally can't have this.
		p.sms_sontransform = 0 --Can't do both at the same time anyway.
		S_StartSound(nil, sfx_supert)
	end
	if (skins[s.skin].normalspeed) == 36*FRACUNIT and s.skin != "sonic"
		p.normalspeed = 59*FRACUNIT
	elseif (skins[s.skin].normalspeed < 108*FRACUNIT) --Speed up!
		p.normalspeed = min(108*FRACUNIT, (skins[s.skin].normalspeed)*12/7)
	end
	
	s.lightspeedpermission = nil
	
	--Get lightspeed attack with custom3?
/*	if ((p.charability == CA_THOK) or (p.charability == CA_HOMINGTHOK) or s.skin == "shadow") --Special permission for classic Shadow.
	and not (s.skin == "adventuresonic" or s.skin == "adventureshadow") --They can already do this naturally!
		s.lightspeedpermission = true		
	end */
	
	--Gain the multiability?
	if not (p.charflags & SF_MULTIABILITY)
		if not (p.charability == (CA_TELEKINESIS|CA_HOMINGTHOK|CA_TWINSPIN))
			p.charflags = $|SF_MULTIABILITY
		end
	end
	
	--Gain ability to run on water while Mystic Super! Also, remove superflag for now.
	p.charflags = $|SF_RUNONWATER & ~SF_SUPER
	p.jumpfactor = (skins[s.skin].jumpfactor)*69/51 --Boost jumpheight!
	p.myssskin = s.skin --the skin you were before transforming, to prevent later issues.
	for i = 1,2
		P_RemoveShield(p)
	end
	
	p.mysscolor = 15 --Default MysticSuper Color!
	if skins[s.skin].supercolor
	and tonumber(skins[s.skin].supercolor) != nil
		for i = 0,40,5
			if skins[s.skin].supercolor == SKINCOLOR_SUPERSILVER1+i
				p.mysscolor = i break --Got the character's preferred SuperColor!
			end
		end
	end
	return true --Success!
end

function A_ENDMYSTICSUPER(s, NOSKINRESET)
	if not s.player return end --
	local p = s.player
	
	if p.isclassicsms --restore classic physics if they apply
		p.isclassic = nil
		p.isclassicsms = nil
	end
	if s.health
		s.state = P_IsObjectOnGround(s) and S_PLAY_RUN or S_PLAY_SPRING
	end
	if p.smsgraphix != 2
		for d = 0,15
			P_SpawnParaloop(s.x, s.y, s.z+(20*s.scale), 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
		end
		for i = -3,0
			SMS_FlameWalls(s, nil, 57*FRACUNIT, ((256*i)+46)*FRACUNIT, A, SKINCOLOR_SMS3R, 39)
		end		
	end
	
	--Reset skin values unless specified not to!
	if not NOSKINRESET
		if p.premysticscolor	
			s.color = p.premysticscolor
			if not (p.premysticscolor >= SKINCOLOR_SUPERSILVER1)
				p.skincolor = p.premysticscolor
			else
				p.skincolor = SKINCOLOR_YELLOW
			end
		end
		p.normalspeed = skins[s.skin].normalspeed --Take it all back!
		p.accelstart = skins[s.skin].accelstart
		p.acceleration = skins[s.skin].acceleration
		p.thrustfactor = skins[s.skin].thrustfactor
		p.jumpfactor = skins[s.skin].jumpfactor
		p.charflags = skins[s.skin].flags
		p.actionspd = skins[s.skin].actionspd
		p.mindash = skins[s.skin].mindash
		p.maxdash = skins[s.skin].maxdash
	end
	
		s.SMS_locked = true
		s.lightspeedpermission = nil
		p.myssskin = nil
		P_SpawnShieldOrb(p)
		
		if P_IsObjectOnGround(s) --Stop for a second.
			if s.health
				s.momx,s.momy,s.momz = 0,0,0
				SMS_StopStick(s)
				p.pflags = $ & ~(PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_THOKKED)
				s.state = S_PLAY_STND
			end
			p.powers[pw_nocontrol] = max($,16)
			S_StopSound(s)
		elseif s.health --If in midair, slow down just a bit.
			s.momx,s.momy,s.momz = $/2,$/2,$/2
		end
		P_FlashPal(p, PAL_MIXUP, 6)
		s.tkteleportup = nil
		s.myssswim = nil
		s.twinrefreshonce = nil
		s.myssbacd = nil
		s.tkteleport = nil
		s.metalcharge = nil
		s.metalafterimage = nil
		S_StartSound(s, sfx_s3k66)
		p.mysticsuper = 0

		if p.hypermysticsonic or p.wasHMS or (p.jumpfactor == 133469) --huh...???
			HMS_UntransformedStats(s, p) 
			p.hypermysticsonic = 0
		end
end

//Var1: Standstill untransform or not | Var2: No State Change | Var3: NoSound or Flash
function A_SMSTOSONIC(s, var1, var2, var3)
	if not (s.valid and s.player) return end --
	local p = s.player
				if p.hypermysticsonic
					HMS_UntransformGFX(s)
					HMS_UntransformedStats(s, p) --
				end
				p.camerascale = s.scale*5/7
				s.bhlcamtimer = 2
				
				if not var3
					if leveltime
						S_StartSound(s, sfx_s3k66)
						P_FlashPal(p, PAL_MIXUP, 4)
						if not SMS_Graphix(s)
							SMS_LightFlash(s, SKINCOLOR_WHITE)	
							for i = -2,2
								HMS_AuraStorm(s, nil, SKINCOLOR_SUPERSKY5, 30, nil, 2, 127*FRACUNIT*i)
							end
						end
					end
					local ghs = P_SpawnGhostMobj(s)
					ghs.destscale = $*99
					ghs.scalespeed = $*2
					ghs.fuse = 18 ghs.tics = 99
					ghs.colorized = true
					ghs.color = SKINCOLOR_SMSROVERGLOW
					ghs.blendmode = AST_ADD
					if SMS_Graphix(s) != 2
						SMS_LightningTrail(s, s)
						for d = 0,15
							P_SpawnParaloop(s.x, s.y, s.z+FixedMul(20*FRACUNIT, s.scale), 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
						end
					end
				end
				
				if p.presmsskin
					R_SetPlayerSkin(p, p.presmsskin)
					s.skin = p.presmsskin
					COM_BufInsertText(p, "skin "+p.presmsskin)
				else
					R_SetPlayerSkin(p, "sonic")
					s.skin = "sonic"
					COM_BufInsertText(p, "skin sonic") --Sometimes SRB2 refuses otherwise.
				end
				

				p.sms_transformstate = 0
				SMS_SkinSwap(s, p) --Always do this AFTER transformstate is 0!
				s.AmSuperMysticSonic = false
				
				if not (p.powers[pw_shield] & SH_FIREFLOWER)
				and not ((mariomode) and p.powers[pw_invulnerability])
					if p.sms_originalcolor != nil
						s.color = p.sms_originalcolor
						p.skincolor = p.sms_originalcolor
					else
						s.color = SKINCOLOR_BLUE
						p.skincolor = SKINCOLOR_BLUE
					end
				else
					s.color = SKINCOLOR_WHITE
				end			
				if leveltim
					if not var2 and s.health
						if (s.state != S_PLAY_DEAD) and (p.panim != PA_PAIN)
							s.state = S_PLAY_FALL
							p.pflags = $ & ~PF_JUMPED
						end
						SMS_StopStick(s)
					end		
					if (s.state != S_PLAY_DEAD) and (p.panim != PA_PAIN)	
						if var1 //Untransform on the spot.
							p.pflags = $ & ~PF_SPINNING & ~PF_STARTDASH
							if not s.battlemodsms
								P_RadiusAttack(s, s, 150*s.scale)
							end
							p.powers[pw_nocontrol] = 8
							s.momx = 0
							s.momy = 0
						else
							p.powers[pw_nocontrol] = 2
							s.momx = $/2
							s.momy = $/2
						end
					end
				end
				if p.sms_originalcolor != nil
					p.skincolor = p.sms_originalcolor
				end				
				if G_GametypeHasTeams() and p.ctfteam --Adjust to team colors!
					p.sms_originalcolor = (p.ctfteam == 1) and SKINCOLOR_RED or SKINCOLOR_BLUE
					s.color = (p.ctfteam == 1) and SKINCOLOR_RED or SKINCOLOR_BLUE
					p.skincolor = (p.ctfteam == 1) and SKINCOLOR_RED or SKINCOLOR_BLUE						
				end
				return true --
end

--Lazy.
local function SMS_MCheck(s, m)
	if m.valid and m.health and (m != s)
		return true
	else
		return false
	end
end

--SMS' version of FindTarget! Mostly unused nowadays except for BlackHole and a few other cases. 
--Blockmap searches were too buggy, and iterating through the whole mobjthinker was lag hell, so I moved towards colission detection.
function A_SMSFindTarget(s, var1, var2, var3)
	if not s.player return end
	
	local looklimit = FixedMul(var1, s.scale) --Scale given range by your scale.
	local p = s.player
	
	local FoundStuff = nil --Our return value at the end.
	
	if (var2 == 9) --Look for Rings to lightdash?
	
		searchBlockmap("objects",function (s,m)
			if SMS_MCheck(s, m)
			and ((m.type == MT_RING) or (m.type == MT_COIN) or (m.type == MT_BLUESPHERE))
				local lookrange = FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
				if (lookrange <= looklimit)
				and not (m.z > s.z+(var1))
				and not (m.z < s.z-(var1))
					s.target = m
					looklimit = lookrange
					FoundStuff = m
				end
			end
		end,s, s.x-looklimit,s.x+looklimit, s.y-looklimit,s.y+looklimit)
		
	elseif (var2 == 7) --Searcher for HMS' obscure God Breaker attack.
	
		searchBlockmap("objects",function (s,m)		
			if SMS_MCheck(s, m)
			and not m.hmscombod			
				if (m.flags & MF_BOSS)
				or (m.type == MT_CYBRAKDEMON)
				or (m.player and G_GametypeHasSpectators() and not m.player.hypermysticsonic)
				or (m.flags & MF_ENEMY)
					local lookrange = FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
					if (lookrange <= looklimit)
					and not (m.z > s.z+(var1/2))
					and not (m.z < s.z-(var1/2))
					and P_CheckSight(s, m)
						s.target = m
						looklimit = lookrange
						FoundStuff = m
						if not var2
							return FoundStuff
						end
					end
				end
			end
	   end,s, s.x-looklimit,s.x+looklimit, s.y-looklimit,s.y+looklimit)
	   
	elseif (var2 == 25) --Auto-target for your charged flame toss ability.
	
		searchBlockmap("objects",function (s,m)
			if not SMS_MCheck(s, m)
			or m.theghoul 
				return nil
			end
			
			if (m.flags & (MF_BOSS|MF_ENEMY))
			
			or m.player and (s.battlemodsms or not G_PlatformGametype())
			and not m.player.powers[pw_invulnerability]
			and not m.player.powers[pw_flashing]
			and not (p.ctfteam and (m.player.ctfteam == p.ctfteam))

					local lookrange = FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
					if (lookrange <= looklimit)
					and not (m.z > s.z+(var1/2))
					and not (m.z < s.z-(var1/2))
					and P_CheckSight(s, m)
						if not s.dfclosest 
						or ( s.dfclosest and ( (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) < s.dfclosest) )
							s.dfclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
							s.target = m
							looklimit = lookrange
							FoundStuff = m
							if m.player and not var3
								m.player.dfflame = 16
							end
						end
					end
			end
	   end,s, s.x-looklimit,s.x+looklimit, s.y-looklimit,s.y+looklimit)
	   
	elseif (var2 == 15) and not s.smspickupCD --Alternate method for finding objects to pick up for SMS.
	
		searchBlockmap("objects",function (s,m)
			if not m or s != m
			or (p.smspickup == m)
				return nil
			end		
			if (m.flags & MF_PUSHABLE) and (m.flags & MF_SOLID)
			or (m.type == MT_GARGOYLE)
			or (m.flags & MF_MONITOR) and m.health

				local lookrange = FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
				if (lookrange <= looklimit)
				and not (m.z > s.z+s.height)
				and not (m.z+m.height+95355 < s.z)
				and P_CheckSight(s, m)
					if (m.flags & MF_MONITOR)
						if m.smspickupflags
							m.flags = m.smspickupflags
							m.smspickupflags = nil
						end
						m.smspickupflags = m.flags
						m.pickedupmonitor = true
						m.flags = $|MF_NOGRAVITY|MF_NOCLIPTHING & ~MF_MONITOR & ~MF_SHOOTABLE
						p.smspickup = m
						m.smspickedup = true
						p.sms_tossflaghold = 5
						FoundStuff = m
						return true
					else
						if not ( (m.type == MT_GARGOYLE) and ((m.subsector.sector.floorpic == "DSTRACK2") or (m.subsector.sector.floorpic == "DSTRACK1")) )
							if m.smspickupflags
								m.flags = m.smspickupflags
								m.smspickupflags = nil
							end
							m.smspickupflags = m.flags
							p.smspickup = m
							m.smspickedup = true
							m.flags = $|MF_NOGRAVITY|MF_NOCLIPTHING|MF_NOCLIPHEIGHT & ~MF_PUSHABLE & ~MF_SOLID
							p.sms_tossflaghold = 5
							FoundStuff = m
							return true
						end
					end
				end
			end
	   end,s, s.x-looklimit,s.x+looklimit, s.y-looklimit,s.y+looklimit)
	   
	else --Generic method for finding enemies to home into.
	
		searchBlockmap("objects",function (s,m)
			if (m and (m != s) and ((m.flags & (MF_ENEMY|MF_BOSS)) or (m.type == MT_EGGGUARD) or ((var2 and var2 == 20) and G_GametypeHasSpectators() and m.player) or ((m.flags & MF_MONITOR and var2 and var2 == 20))  or (((m.type == MT_CYBRAKDEMON) or (m.type == MT_MINUS)) and var2 and var2 == 1)) 
			and m.health and (m.flags & MF_SHOOTABLE or (((m.type == MT_CYBRAKDEMON) or (m.type == MT_MINUS) or (m.type == MT_EGGGUARD)) and var2 and var2 == 1)) and not (m.type == MT_DETON) and not m.fusrodah and not (m.flags2 & MF2_FRET)) then
			
				if (var2 and var2 == 20 and G_GametypeHasSpectators())
					var1 = var1/2
				end
				local lookrange = FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
				if (lookrange <= looklimit)
				and not (m.z > s.z+(var1/2))
				and not (m.z < s.z-(var1/2))
				and P_CheckSight(s, m)
					s.target = m
					looklimit = lookrange
					FoundStuff = m
					if not var2
						return FoundStuff
					end
				end
			end
		end,s, s.x-looklimit,s.x+looklimit, s.y-looklimit,s.y+looklimit)
	end
	return FoundStuff --Did we find anything suitable? Will return a mobj if so.
end

--Hurt hostile players when touched, otherwise disappear.
addHook("TouchSpecial", function(s, v)
	if not (v and v.valid and v.player)
	or G_GametypeHasSpectators()
	or not (s.target and s.target.player)
		return --
	end
	local t = s.target
	local p = v.player
	
	if SMS_MCheck(t, v)
	and not p.powers[pw_flashing]
	and not p.powers[pw_super] 
	and not (SMSSHIELD(p) == SH_ELEMENTAL)
	and not (p.ctfteam and p.ctfteam == t.player.ctfteam) --Don't hurt yourself or teammates
		if t.player.hypermysticsonic
			P_DamageMobj(v, s, t)
			return true
		else
			return true
		end
	end
end, MT_SMSSPINFIRE)

--SMS spinfire! With an actual painbox.
addHook("MobjThinker", function(s)
	if not s.valid return end
	if (gametype == GT_COOP) and s.target 
		if (leveltime % 4 == 0) and s.target.valid --Oh. No wonder this shit can lag, what the hell?
			P_RadiusAttack(s, s.target, 25*FRACUNIT)
		end
		s.flags = $ & ~MF_SPECIAL
	end
	--Immediately disappear always.
	if P_CheckDeathPitCollide(s)
	or (s.scale < FRACUNIT/4)
	or (s.eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))
	or s.extspd
		s.state = S_NULL
		return --
	end
	if s.growtimer
		s.growtimer = $-1
		if (s.growtimer >= 29) --Just started growing.
			if not s.extrfl
				if (s.flame1)
					P_InstaThrust(s, s.angle + ANGLE_90, 10*s.scale)
				else
					P_InstaThrust(s, s.angle + ANGLE_90, -10*s.scale)
				end
			end
			if (s.growtimer == 29)
				local ghs = P_SpawnGhostMobj(s)
				ghs.blendmode = AST_ADD
				ghs.destscale = $*99
				ghs.fuse = 8 ghs.tics = 9
				A_SetScale(s, s.scale+200000, 1)
			end
		elseif (s.growtimer < 26) --Below 26?
			if P_IsObjectOnGround(s) --Instantly stop on the ground.
				s.momx = 0
				s.momy = 0
			end
			if (s.growtimer == 4) --Spawn rest flame when we're done!
				A_SetScale(s, FRACUNIT/11, 1)
				if s.spawnhmsrestflame
					s.spawnhmsrestflame2 = P_SpawnMobjFromMobj(s, 0,0,0, MT_HMSFLAMEREST)
					if s.target and (s.spawnhmsrestflame2 and s.spawnhmsrestflame2.valid) and s.target.valid
					local t = s.target
					s.spawnhmsrestflame2.target = t
						if t.player and t.player.hypermysticsonic
							s.spawnhmsrestflame2.scale = t.scale*9/P_RandomRange(3,9)
						else
							s.spawnhmsrestflame2.scale = t.scale
						end
					end
				end
			end
		elseif (s.growtimer > 26) --Inbetween?
			if (s.growtimer == 28)
				if not s.extrfl
					local ghs = P_SpawnGhostMobj(s)
					ghs.blendmode = AST_ADD
					ghs.fuse = 8 ghs.tics = 9
					if s.flame1
						P_InstaThrust(s, s.angle+ANGLE_90, 7*s.scale)
					else
						P_InstaThrust(s, s.angle+ANGLE_90, -7*s.scale)
					end
				end
			elseif (s.growtimer == 27)
				if not s.extrfl
					if s.flame1
						P_InstaThrust(s, s.angle+ANGLE_90, 3*s.scale)
					else
						P_InstaThrust(s, s.angle+ANGLE_90, -3*s.scale)
					end
				end
			elseif (s.growtimer == 26)
				P_SetObjectMomZ(s, 9*FRACUNIT)
			end
		end
	end		
end, MT_SMSSPINFIRE)

//Replacement for DeathPitCollide. If var1 is set, it'll returns false if you're HMS.
function A_HMSCheckDeathPit(s, var1)
	if not (s and s.valid and s.player)
	or var1 and s.player.hypermysticsonic
		return false --
	end
	local p = s.player
	
	if P_PlayerTouchingSectorSpecial(p, 1, 7)
	or P_PlayerTouchingSectorSpecial(p, 1, 6)
	or P_PlayerTouchingSectorSpecial(p, 1, 8)
		if s.subsector and s.subsector.sector
		local SEC = s.subsector.sector
			if (s.eflags & MFE_VERTICALFLIP) --Flipped check.
				if s.ceilingz == SEC.ceilingheight and (P_IsObjectOnGround(s) or (abs(s.z-s.ceilingz) < 51*s.scale))
					return true --
				end
			elseif (s.floorz == SEC.floorheight) and (P_IsObjectOnGround(s) or (abs(s.z-s.floorz) < 4*s.scale))
				return true --
			end
		else --Can't obtain info? Assume you're on a death pit!
			return true --
		end
	end
	return false --Not on a death pit.
end

--SMS' own modified firetrail. Usually used for boostspeed and HMS shenanigans
function A_SMSSpinFire(s, var1, var2, var3, var4)
if s.player and s.player.smsgraphix == 2
or not s.health
or (s.eflags & MFE_TOUCHWATER)
	return
end
		local f0 = P_SpawnMobjFromMobj(s, P_RandomRange(-7, 7)*FRACUNIT, P_RandomRange(-7, 7)*FRACUNIT, FRACUNIT, var1)
		local f1 = P_SpawnMobjFromMobj(s, P_RandomRange(-7, 7)*FRACUNIT, P_RandomRange(-7, 7)*FRACUNIT, FRACUNIT, var1)
		if not (f0 and f1 and f0.valid and f1.valid)
			return --Welp, this went terribly wrong.
		end
		if (s.eflags & MFE_VERTICALFLIP)
			f0.flags2 = $|MF2_OBJECTFLIP
			f1.flags2 = $|MF2_OBJECTFLIP
		end
			
		f0.fuse = TICRATE*3
		f0.angle = s.angle+ANG2+ANG2
		f0.scale = FixedMul(var2, s.scale)
		f0.growtimer = 30
						
			
		f1.flame1 = true
		f1.fuse = TICRATE*3
		f1.angle = s.angle-(ANG1*4)
		f1.scale = FixedMul(var2, s.scale)
		f1.growtimer = 30

		--Resting flame?
		if (var2 == 65357)
			f0.spawnhmsrestflame = true
			f1.spawnhmsrestflame = true
		end
		--What type?
		if var3
			f0.extrfl = true
			f1.extspd = var3		
		else
			f0.extspd = 0
			f1.extspd = 0
		end
		--Target spawner or something else?
		if var4
			f0.target = var4
			f1.target = var4
		else
			f0.target = s
			f1.target = s
		end				
end

function A_AtomDiveAim(p)
	if not p.mo
	or A_HMSCheckDeathPit(p.mo)
	or p.smsatomicdivefail
	or p.mo.SMSDELAYRENDER
	or SMS_Graphix(p.mo) == 2 and (leveltime % 2)
		p.mo.SMSDELAYRENDER = nil
		return --
	end
	local s = p.mo
	
	local DIST = 14
	local COLOR = p.smsdivetimer and SKINCOLOR_RED or SKINCOLOR_BLUE
	local SCALE = s.scale/6
	local TICS = 1
	if s.SMSLASTANGLE == nil
		s.SMSLASTANGLE = s.angle
	elseif (s.SMSLASTANGLE-(p.cmd.angleturn<<16) == 0)
		s.SMSDELAYRENDER = true
		TICS = 2 --Skip spawning more next frame and render them for 2 frames instead.
	end
	
	SMS_StopStick(s) --Stop regular controls.
	
	local a = s
	--Flipped and non-flipped versions, to optimize calculations a bit for SRB2.
	if (DIST > 5000) 
		--That's enough!
	elseif not (s.eflags & MFE_VERTICALFLIP)
		while P_CheckPosition(a, a.x+DIST*cos(s.angle), a.y+DIST*sin(s.angle), a.z+(DIST*-32678))
		and not (a.z < a.floorz+16*s.scale)
			a = P_SpawnMobj(a.x+DIST*cos(s.angle), a.y+DIST*sin(s.angle), a.z+(DIST*-32678), MT_THOK)
			a.tics = TICS
			a.scale = SCALE
			a.color = COLOR	
			DIST = $+7
			SCALE = $+600
		end
	else
		while P_CheckPosition(a, a.x+DIST*cos(s.angle), a.y+DIST*sin(s.angle), a.z+(DIST*32678))
		and not (a.z > a.ceilingz-16*s.scale)
			a = P_SpawnMobj(a.x+DIST*cos(s.angle), a.y+DIST*sin(s.angle), a.z+(DIST*32678), MT_THOK)
			a.tics = TICS
			a.scale = SCALE 
			a.color = COLOR	
			DIST = $+7
			SCALE = $+600
		end	
	end
	--We're at the end! Spawn reticule.
	if not p.atomdivetargetspin
		local SEC = (a.subsector) and a.subsector.sector
		local SETZ = a.z
		local SETXY = 0
		if SEC and SEC.ffloors()
			if (s.eflags & MFE_VERTICALFLIP)
				for fof in SEC.ffloors()
					if (fof.flags & FF_EXISTS) and (fof.flags & (FF_BLOCKPLAYER|FF_STRONGBUST|FF_BUSTUP|FF_CUTSPRITES))
					and (SETZ > fof.bottomheight)
						SETXY = abs(fof.topheight-a.z)*2
						SETZ = fof.bottomheight
					end
				end
			else
				for fof in SEC.ffloors()
					if (fof.flags & FF_EXISTS) and (fof.flags & (FF_BLOCKPLAYER|FF_STRONGBUST|FF_BUSTUP|FF_CUTSPRITES))
					and (SETZ < fof.topheight)
						SETXY = abs(fof.topheight-a.z)*2
						SETZ = fof.topheight
					end
				end			
			end
		end	
		
		a = P_SpawnMobj(a.x-FixedMul(24*s.scale+SETXY, cos(s.angle)), a.y-FixedMul(24*s.scale+SETXY, sin(s.angle)), SETZ, MT_ATOMDIVETARGET)
		a.scale = s.scale
		a.spriteyscale = $*2
		a.spritexscale = $*2
		a.target = s
		if (s.eflags & MFE_VERTICALFLIP) --If we're flipped, so are you!
			a.z = $-88*s.scale
			a.flags2 = $|MF2_OBJECTFLIP
		end
		
		if p.smsdivetimer
			local DIST = FixedHypot(FixedHypot(s.x-a.x, s.y-a.y), s.z-a.z)
			if DIST > 800*s.scale
				if (DIST > 1500*s.scale)
					a.spriteyscale = $*2
					a.spritexscale = $*2
				else
					a.spriteyscale = $*3/2
					a.spritexscale = $*3/2	
				end
			end
			a.dispoffset = 30
			a.state = S_CYBRAKDEMONTARGETRETICULE14
			p.atomdivetargetspin = 14
			return true --
		end
	end
	s.SMSLASTANGLE = s.angle
	return false --
end
 
//Simple function to check if SMS is some form of yellow.
rawset(_G, "SMSisyellow", function(s)
	if (s.color >= SKINCOLOR_SMS1R) and (s.color <= SKINCOLOR_MYSTICAL5)
	or (s.color >= SKINCOLOR_SUPERGOLD1) and (s.color <= SKINCOLOR_SUPERGOLD5)
	or (s.color >= SKINCOLOR_GOLD) and (s.color <= SKINCOLOR_YELLOW)
	or not s.color --We'll count "no" color as yellow too.
		return true --
	end
	return false --
end) 

--Shortcut to crumble FOFs below us, with generous AoE.
rawset(_G, "SMS_AtomBust", function (s)
	local sectorz = R_PointInSubsector(s.x, s.y).sector
	if not (sectorz and sectorz.ffloors())
		return
	end
	for fof in sectorz.ffloors()
		if (fof.flags & FF_EXISTS) and ((fof.flags & FF_STRONGBUST) or (fof.flags & FF_BUSTUP))
		and not (s.z-99*FRACUNIT >= fof.topheight)
		and not (s.z+99*FRACUNIT+s.height < fof.bottomheight)
			EV_CrumbleChain(sectorz, fof)
		end
	end
end)

--Alter player controls for a while. Only works because it's activated from a PreThinkFrame.
--It either completely overrides controls, removes certain inputs, or limits the amount you can push in a certain direction.
--Tabel is s.SMS_ControlAlter"
rawset(_G, "SMS_AlterControls", function(s, p, yu)
	if not yu.timer
		if yu.removebuttons
			if (yu.removebuttons & BT_CUSTOM1)
				p.sms_c1 = FRACUNIT
				p.sms_readyairboost = 0
				if not P_IsObjectOnGround(s) and (s.state == S_PLAY_FALL)
					p.sms_didairboost = 1
				end
			end
			if (yu.removebuttons & BT_CUSTOM2)
				p.smsmeleebutton = FRACUNIT
				p.smsmeleebutton2 = FRACUNIT
			end
			if (yu.removebuttons & BT_CUSTOM3)
				p.transformdown = FRACUNIT
			end
			if (yu.removebuttons & BT_JUMP)
				p.sms_jumphold = FRACUNIT
				p.pflags = $|PF_JUMPDOWN
			end
			if (yu.removebuttons & BT_USE)
				p.spinhold = 850
				p.pflags = $|PF_USEDOWN
			end	
		end
		s.SMS_ControlAlter = nil
		return --
	end
	
	yu.timer = $-1
		
	 --Entirely different stick altering code for ANALOG ("simple") Controls.
	if yu.analogangle != nil and (p.pflags & PF_ANALOGMODE)
	and not (yu.sidemove == 0 and yu.forwardmove == 0)
	or yu.forceanalog
		
			if p.cmd.forwardmove or p.cmd.sidemove
				if yu.limiter == nil yu.limiter = 100 end --Set default values.
				if yu.diff == nil yu.diff = 30 end 
			
				local ANGLE = yu.analogangle
				local TURNANG = (p.cmd.angleturn<<16)+(R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
				local ANGLESHIFT = 0
				local diff = (ANGLE-TURNANG) --Forbidden Angle vs Input Angle	
			
				diff = (diff > ANGLE_180) and InvAngle(InvAngle(diff)/2) or diff/2
				if (diff > 0)
					ANGSHIFT = AngleFixed(diff)
				else
					ANGSHIFT = abs(AngleFixed(diff)-360*FRACUNIT)
				end
				if (ANGSHIFT > 3*FRACUNIT)
					local LIMITER = max(yu.limiter, FixedDiv( FixedMul(FixedInt(ANGSHIFT), yu.limiter), yu.diff) )		
					if p.cmd.forwardmove
						p.cmd.forwardmove = (FixedDiv(p.cmd.forwardmove*2, yu.limiter) or 1)/2
					end			
					if p.cmd.sidemove
						p.cmd.sidemove = (FixedDiv(p.cmd.sidemove*2, yu.limiter) or 1)/2
					end
				end
			end
			
	else --Change sidemove and forwardmove!
		
			--Alter forward controls
			if yu.forwardmove != nil --Set forward/backward input directly.
				p.cmd.forwardmove = yu.forwardmove
			else --How hard are we allowed to press the stick forward/backwards?
				if yu.minforwardmove != nil
					p.cmd.forwardmove = max($, yu.minforwardmove)
				end
				if yu.maxforwardmove != nil
					p.cmd.forwardmove = min($, yu.maxforwardmove)
				end
			end	
			
			--Alter sidemove controls
			if yu.sidemove != nil --Set sideways input directly.
				p.cmd.sidemove = yu.sidemove
			else --How hard are we allowed to press the stick sideways?
				if yu.minsidemove != nil
					p.cmd.sidemove = max($, yu.minsidemove)
				end
				if yu.maxsidemove != nil
					p.cmd.sidemove = min($, yu.maxsidemove)
				end
			end
	end
		
		--Lock aiming and turning angles?
		if yu.turnangle != nil --On it's own, this is basically just strafing.
			s.angle = yu.turnangle
			p.drawangle = yu.turnangle
		end
		if yu.aiming != nil
			p.aiming = yu.aiming
		end
		
		--Remove any button inputs...?
		if yu.removebuttons != nil
			if yu.removebuttons == 0
				p.cmd.buttons = 0  --Force NO button inputs
			else		
				p.cmd.buttons = $ & ~yu.removebuttons --Remove specific buttons.
			end
		end
		
		--Free Conditions!
		if yu.freecondition != nil
			if yu.freecondition == 1 --Perform common actions or land!
				if p.sms_stompanim or p.sms_afterstompthok or p.homing or p.sms_didairboost
				or (p.pflags & PF_THOKKED) and (p.panim == PA_ROLL) and (p.pflags & PF_JUMPED)
				or P_IsObjectOnGround(s) and not (s.momz*P_MobjFlip(s) > 0)
					s.SMS_ControlAlter = nil
				end
			elseif yu.freecondition == 2 --Hit the floor!
				if P_IsObjectOnGround(s) and not (s.momz*P_MobjFlip(s) > 0)
					s.SMS_ControlAlter = nil
				end
			elseif yu.freecondition == 3 --Go into a jump or common action!
				if (p.pflags & (PF_JUMPED|PF_THOKKED|PF_STARTDASH|PF_SPINNING))
				or p.sms_stompanim or p.sms_afterstompthok or p.homing or p.sms_didairboost
					s.SMS_ControlAlter = nil
				end			
			end
		end
		
		--Removes all absolute stick values when timer cuts down, but keeps restricting buttons/maxmove.
		if yu.freeabsolutemove
			yu.freeabsolutemove = max($-1,0)
			if not yu.freeabsolutemove
				yu.sidemove = nil
				yu.forwardmove = nil
				yu.turnangle = nil
				yu.aimangle = nil
			end
		end
		--Frees up button inputs only when timer reaches 0.
		if yu.freebuttons 
			yu.freebuttons = max($-1,0)
			if not yu.freebuttons
				yu.removebuttons = nil
			end
		end	
		return true --
end)

--Quickly put in a basic table to stop stick presses.
rawset(_G, "SMS_StopStick", function(s)
	if not (s.SMS_ControlAlter and (s.SMS_ControlAlter.timer > 1))
		s.SMS_ControlAlter = {
		timer = 2,
		forwardmove = 0,
		sidemove = 0}
	end
end)

--Atom Diving! Aim in midair, and then fire off using your momentum for an impact AoE!
--This is the active landing/firing part, setup is in LUA_SUPERMOVES. "Atomdive"

rawset(_G, "SMSatomdiving", function(s, p)

	p.powers[pw_noautobrake] = max($,2)
	s.smsboostimmune = 2
	
	if not p.hypermysticsonic
	
		s.smshmcheck = 0
		s.cmphclosest = nil
		s.cmphtarget = nil	

		p.sms_jumphold = 99
		s.sms_jumpbuffer = 0
		s.bufferframejump = false
		s.smsframejump = 0
		p.pflags = $|PF_JUMPDOWN|PF_USEDOWN
		
		s.SMS_ControlAlter = {
		timer = 3,
		forwardmove = 0,
		sidemove = 0,
		removebuttons = (BT_CUSTOM1|BT_CUSTOM3|BT_TOSSFLAG|BT_ATTACK)
		}
	end

	--We're hitting a floor! Start an atom dive landing!
	if (s.eflags & MFE_JUSTHITFLOOR) or P_IsObjectOnGround(s)
			local LEVEL = 1
			//Low level landing. Accomplishes nothing but a bit of smoke.
			
			if (((p.smsdivetimer < 18) or ((G_GametypeHasSpectators() or s.battlemodsms) and not p.rings)) )
			and not ((s.eflags & MFE_UNDERWATER) and not p.hypermysticsonic)
				
				P_InstaThrust(s, s.angle, 2*FRACUNIT)
				s.hmselcharge = 3
				S_StartSound(s, sfx_s3k77)
				A_SMSSTOMPSMOKE(s, 3, MT_EXPLODE)
				A_SMSSTOMPQUAKE(s, 45, 14, FRACUNIT, 0, 0, 0, 0)
				A_SMSSTOMPQUAKE(s, 65, 18, 2*FRACUNIT, 0, ANG20, 0, 0, SKINCOLOR_SUPERSILVER4)				

			--Only Hyper Mystic Sonic can perform this one!
			//Ultimate landing, a nuclear explosion!
			elseif (p.smsdivetimer >= 57)
			and p.hypermysticsonic
					local ghs = SMS_SmartSplat(s, 180)
					if ghs and ghs.valid
						ghs.sprite = SPR_SMX1
						ghs.frame = T|TR_TRANS10
						ghs.height = 0
						ghs.radius = 0						
						ghs.scale = s.scale*6
					end			
					p.cmd.buttons = $ & ~BT_CUSTOM1
					p.speed = 550*FRACUNIT
					s.boostpass = nil --Gonna be a busy frame. Disable funny effects that you won't see anyway.
					s.hmgodticket = nil
					s.hmgodmticket = nil
					s.NoSMSGlow = true
					s.hudboost = 1
					s.hmselcharge = 70
					s.hmspowerbombcam = 105
					P_FlashPal(p, PAL_NUKE, 10)
					LEVEL = 4
					
					S_StartSound(s, sfx_spld)
					S_StartSound(s, sfx_nuexp)
					P_InstaThrust(s, s.angle, 2*FRACUNIT)
					
					if not SMS_Graphix(s) --You sure? I'm not holding back.
						A_SMSSTOMPQUAKE(s, 95, 4, 70*FRACUNIT, 0, ANG30, 0, 19, SKINCOLOR_SUPERSILVER4)
						A_SMSSTOMPQUAKE(s, 75, 1, 32*FRACUNIT, 0, (ANG10*5), 0, 22)
						
						ghs = SMS_FlatWave(s, SKINCOLOR_RED, 78, 3, 0)
						ghs.sttspeed = nil
						ghs.frame = H|FF_FULLBRIGHT
						ghs.sttfade = 20
						ghs.z = $+(48*s.scale*P_MobjFlip(s))
						ghs.scalespeed = $+(FRACUNIT/2)
						ghs.renderflags = RF_FULLBRIGHT|RF_FLOORSPRITE|RF_NOCOLORMAPS|RF_ABSOLUTEOFFSETS
						ghs.spritexoffset = 111*FRACUNIT
						ghs.spriteyoffset = 108*FRACUNIT
						ghs.destscale = FRACUNIT*999
					
						ghs = P_SpawnMobjFromMobj(s, FixedMul(8*s.scale, cos(s.angle)),FixedMul(8*s.scale, sin(s.angle)),0, MT_TNTDUST)
						ghs.fuse = 50
						ghs.spriteyscale = $*3
						ghs.spritexscale = $*3
						ghs.scalespeed = FRACUNIT/3
						ghs.destscale = $*50	
						ghs.dispoffset = -30
						
						--Huge light!
						ghs = SMS_LightFlash(s, SKINCOLOR_MYSTICAL1, FRACUNIT*2, 8, Z+8|FF_TRANS10|FF_REVERSESUBTRACT)	
						ghs.fuse = 29
						ghs.tracer = s
						ghs.spritexscale = $+38000
						ghs.spriteyoffset = $-8*FRACUNIT
						ghs.dispoffset = 10
						
						local THROWANGLE
						local NO = 7
						if not netgame
							THROWANGLE = s.angle+ANGLE_180
							NO = (CV_FindVar("renderer").value == 2) and 22 or 10
						end
						for i = 1,NO
							ghs = SMS_CraterSpew(s, THROWANGLE, 17)
							if ghs and ghs.valid
								ghs.spritexscale = $+SMSLocalRNG(6*FRACUNIT, FRACUNIT*2+1, ghs)
								ghs.spriteyscale = $+SMSLocalRNG(FRACUNIT*2, 6*FRACUNIT, ghs)
								ghs.momz = $+( (SMSLocalRNG(0,55,ghs)*s.scale) *P_MobjFlip(s))
								ghs.smsrgtimer = 121
								P_Thrust(ghs, R_PointToAngle2(ghs.x, ghs.y, ghs.x+ghs.momx, ghs.y+ghs.momy), SMSLocalRNG(25,129,ghs)*s.scale)
							end
						end	
					elseif s.HMS_lastNB and s.HMS_lastNB.valid  --Stop last powerbomb from spawning more particles if graphics aren't maxed.
						s.HMS_lastNB.state = S_NULL
					end
					if netgame and s.HMS_lastNB and s.HMS_lastNB.valid 
						s.HMS_lastNB.state = S_NULL
					end
					s.HMS_lastNB = P_SpawnPlayerMissile(s, MT_HMSPOWERBOMB, MF2_SUPERFIRE)
					
					A_SMSSTOMPQUAKE(s, 85, 1, 16*FRACUNIT, 0, ANG10, 0, 22, SKINCOLOR_RED)
					A_SMSSTOMPQUAKE(s, 105, 3, 78*FRACUNIT, 0, ANG30*2, 0, 19, SKINCOLOR_SILVER)				
						
					if G_GametypeHasSpectators()
						P_Telekinesis(p, 100*FRACUNIT, 2800*FRACUNIT)
					end
					
			//High level landing!		
			elseif (p.smsdivetimer >= 37)	
			    s.boostpass = true

				if not (s.eflags & MFE_UNDERWATER and not p.hypermysticsonic)
					P_FlashPal(p, 4, 4)
					s.hmselcharge = 40
					if (G_GametypeHasSpectators() or s.battlemodsms)				
						P_GivePlayerRings(p, -2) --ring drain
					end
					if not p.hypermysticsonic
						s.hmspowerbombcam = 45
					end
					local ghs = SMS_SmartSplat(s, 80)
					if ghs and ghs.valid
						ghs.sprite = SPR_SMX1
						ghs.frame = T|TR_TRANS20
						ghs.scale = s.scale
						ghs.scalespeed = $*2
						ghs.destscale = s.scale*3
					end	
					ghs = SMS_LightFlash(s, SKINCOLOR_SMS3R, FRACUNIT, nil, Z+3|TR_TRANS10)
					ghs.tracer = s
					ghs.fuse = 14
					ghs.scalespeed = FRACUNIT/4	
					
					if SMS_Graphix(s) == 0						
						ghs = P_SpawnMobjFromMobj(s, 0,0,-20*s.scale, MT_HMSWAVEAURA)
						ghs.dustwave = 1
						ghs.dustwavet = 8
						ghs.sprite = SPR_SMMK
						ghs.frame = 3
						ghs.target = s
						ghs.fuse = 32
						ghs.scale = s.scale*2
						ghs.destscale = $*4
						
						ghs = SMS_FlatWave(s, SKINCOLOR_SMS5R, 55, 2, 0)
						ghs.sttspeed = nil
						ghs.frame = H|FF_FULLBRIGHT
						ghs.sttfade = 20
						ghs.spritexscale = $/2
						ghs.spriteyscale = $/2
						ghs.z = $+(38*s.scale*P_MobjFlip(s))
						ghs.renderflags = RF_FULLBRIGHT|RF_FLOORSPRITE|RF_NOCOLORMAPS|RF_ABSOLUTEOFFSETS
						ghs.destscale = 999*FRACUNIT
						ghs.spritexoffset = 111*FRACUNIT
						ghs.spriteyoffset = 108*FRACUNIT					
								
						ghs = SMS_ElectricBody(s, 5, SKINCOLOR_SUPERSKY5)
						ghs.spriteyscale = FRACUNIT+42000
						ghs.spritexscale = FRACUNIT+45000
						
						A_SMSSTOMPQUAKE(s, 85, 6, 16*FRACUNIT, 0, ANG10, 0, 22)
						A_SMSSTOMPQUAKE(s, 165, 12, 40*FRACUNIT, FRACUNIT*2, ANG30, 0, 19, SKINCOLOR_SUPERSILVER4)
						A_SMSSTOMPQUAKE(s, 115, 7, 25*FRACUNIT, 0, 0, 0, 14)	
						
						for i = 0,5
							HMS_GroundSparks(s, p, i)
						end
					end
					
					LEVEL = 3
					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_BURSTWAVE)
					ghs.blendmode = AST_ADD ghs.mirrored = true
					
					P_SpawnPlayerMissile(s, MT_SMSPOWERBOMB, MF2_SUPERFIRE)
					
					A_SMSSTOMPQUAKE(s, 45, 5, 5*FRACUNIT, 0, 0, 0, 7, SKINCOLOR_SUPERRED4)
					A_SMSSTOMPQUAKE(s, 125, 7, 30*FRACUNIT, 0, ANG20, 0, 14)
				
					A_SMSSTOMPQUAKE(s, 55, 5, 7*FRACUNIT, 0, ANG30, 0, 7, SKINCOLOR_RED)
					A_SMSSTOMPQUAKE(s, 75, 6, 12*FRACUNIT, 0, (ANG10*5), 0, 22)
					A_SMSSTOMPQUAKE(s, 145, 12, 35*FRACUNIT, FRACUNIT*2, (ANG30*2), 0, 19, SKINCOLOR_SILVER)
				
	    			local ghs = P_SpawnMobj(s.x, s.y, s.z, MT_ATOMDIVEPOP)
	    			ghs.tracer = s
				
					S_StartSound(s, sfx_spld)
					S_StartSound(s, sfx_pwbmb)
					
					if G_GametypeHasSpectators()
						P_InstaThrust(s, s.angle, -8*FRACUNIT)
					else
						P_InstaThrust(s, s.angle, 2*FRACUNIT)
					end
				end
					
			//Mid level landing!
			elseif (p.smsdivetimer >= 18 and p.smsdivetimer < 39)

				if not (s.eflags & MFE_UNDERWATER and not p.hypermysticsonic)
				
					if (G_GametypeHasSpectators() or s.battlemodsms)				
						P_GivePlayerRings(p, -1) --ring drain
					end
					local ghs 
					
					if SMS_Graphix(s) != 2
					/*	ghs = SMS_SmartSplat(s, 64)
						if ghs and ghs.valid
							ghs.sprite = SPR_SMX1
							ghs.frame = S|TR_TRANS20
							ghs.scale = s.scale/2
							ghs.scalespeed = $*2
							ghs.destscale = s.scale*3/2
						end */

						ghs = SMS_FlatWave(s, SKINCOLOR_CRIMSON, 30, 5, 0)
						ghs.sttspeed = nil
						ghs.frame = H|FF_FULLBRIGHT
						ghs.sttfade = 20
						ghs.renderflags = RF_FULLBRIGHT|RF_FLOORSPRITE|RF_NOCOLORMAPS|RF_ABSOLUTEOFFSETS
						ghs.spritexoffset = 111*FRACUNIT
						ghs.spriteyoffset = 108*FRACUNIT					
					
						SMS_ElectricBody(s, 24)
					
						ghs = SMS_LightFlash(s, SKINCOLOR_SMS5R)
						ghs.tracer = s
						ghs.fuse = 17
						ghs.scalespeed = FRACUNIT/3
						ghs.spriteyoffset = $-10*FRACUNIT
						
						for i = 0,4
							HMS_GroundSparks(s, p, i)
						end						
				
						A_SMSSTOMPQUAKE(s, 55, 7, 5*FRACUNIT, 0, ANG30, 0, 13, SKINCOLOR_FLAME)
						A_SMSSTOMPQUAKE(s, 75, 9, 16*FRACUNIT, 0, (ANG30*3), 0, 20, SKINCOLOR_SUPERRED1)
						A_SMSSTOMPQUAKE(s, 95, 10, 30*FRACUNIT, 0, (ANG20*2), 0, 22, SKINCOLOR_SUPERRED5)						
					end
					
					s.hmselcharge = 18
					P_InstaThrust(s, s.angle, 2*FRACUNIT)
				
	    			local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_ATOMDIVEPOP)
	    			ghs.tracer = s			
					
					LEVEL = 2
					S_StartSound(s, sfx_brakrx)
					P_FlashPal(p, 4, 2)
					S_StartSound(s, sfx_s3kcfs)
				
					A_SMSSTOMPQUAKE(s, 45, 7, 5*FRACUNIT, 0, 0, 0, 13, SKINCOLOR_RED)
					A_SMSSTOMPQUAKE(s, 85, 9, 16*FRACUNIT, 0, ANG10, 0, 20, SKINCOLOR_SUPERRED5)
					A_SMSSTOMPQUAKE(s, 105, 10, 30*FRACUNIT, 0, ANG20, 0, 22, SKINCOLOR_SUPERRED1)
				
				
					for i = 1,4
						P_SpawnMobjFromMobj(s, 
						FixedMul(70*s.scale, cos(s.angle+(ANGLE_90*i))), 
						FixedMul(70*s.scale, sin(s.angle+(ANGLE_90*i))),
						0, MT_SMSDIVEBOMB)

						P_SpawnMobjFromMobj(s, 
						FixedMul(250*s.scale, cos(s.angle+(ANGLE_90*i))), 
						FixedMul(250*s.scale, sin(s.angle+(ANGLE_90*i))),
						0, MT_SMSDIVEBOMB)	
					
						if i != 4
							P_SpawnMobjFromMobj(s, 
							FixedMul(45*s.scale, cos(s.angle+(ANG1*114))), 
							FixedMul(45*s.scale, sin(s.angle+(ANG1*114))),
							0, MT_SMSDIVEBOMB)						
						end
					end

					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMSDIVEBOMB)
					if ghs and ghs.valid
						ghs.target = s
						P_NukeEnemies(ghs, s, 577*FRACUNIT)
					end

					A_SMSSTOMPSMOKE(s, 4, MT_BOSSEXPLODE, 80*FRACUNIT)
					A_SMSSTOMPSMOKE(s, 5, MT_EXPLODE, 40*FRACUNIT)
					A_SMSSTOMPSMOKE(s, 6, MT_BOSSEXPLODE)

					A_SMSkillstuff(s, 777*FRACUNIT, 500*FRACUNIT, s, true)
				end			
			end
			
			--Activate these always on landing.
			p.powers[pw_nocontrol] = max($,2)
			s.SMS_locked = true --Crucial frame.
			p.spinhold = 850
			p.smsmeleebutton = 850
			p.sms_flamedelay = max($,3)
			
			SMS_AtomBust(s)
			if not p.hypermysticsonic
			
				s.state = S_SMS_ATOMLAND1
				p.sms_boostlevel = 0
				SMS_WallJumpDelete(s, true)
				s.SMS_WJpenalty = 0	
				
				if LEVEL == 1
					s.sms_juststomped = 6
					p.powers[pw_nocontrol] = 2
					s.tics = 2 s.anim_duration = 2
				else
					s.SMS_ControlAlter = {
					timer = 10,
					turnangle = s.angle,
					sidemove = 0,
					forwardmove = 0,
					removebuttons = (BT_JUMP|BT_SPIN|BT_CUSTOM1|BT_CUSTOM2|BT_CUSTOM3|BT_TOSSFLAG|BT_ATTACK)
					}	
					s.spritexscale = FRACUNIT+19500
					s.spriteyscale = FRACUNIT-37000	
					s.SMS_squashtable = {timer = 22, Xspeed = 2500, Yspeed = 3900, ground = true}						
				end
			else
				s.state = S_HMS_ATOMLAND
			end
			if (p.speed > 4*s.scale) --failsafe.
				s.momx = 0 s.momy = 0
			end
			s.momz = 0
			p.smsdivetimer = 0
				
	else --We're in the air! Currently doing an atom dive!
	
		local mult = ((p.powers[pw_sneakers]) and 14) or 10 --Speed up!
		
		if (p.panim == PA_ROLL) or p.hypermysticsonic --Be in the right animation, though...

			--Midpoint diving speed!	
			if (p.smsdivetimer >= 18 and p.smsdivetimer < 39)
				P_RadiusAttack(s, s, 12*FRACUNIT*mult)
				
				local ghs = P_SpawnGhostMobj(s)
				ghs.destscale = ghs.scalespeed+1
				ghs.scalespeed = $*2
				
				if (leveltime % 2)
					P_InstaThrust(s, s.angle, 10*FRACUNIT*mult)
				end
				s.momz = (-5*FRACUNIT*mult)*P_MobjFlip(s)
				
			--Extreme diving momentum!	
			elseif (p.smsdivetimer >= 37)
			and not (p.smsdivetimer >= 57 and p.hypermysticsonic)
			    SMS_StompGFX(s, p, true, true)
			    s.boostpass = true
				
				P_RadiusAttack(s, s, 16*FRACUNIT*mult)
				P_SpawnGhostMobj(s)
				
				local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_SMSGROUNDBOOST)
				ghs.tracer = s 
				
				if (leveltime % 2)
					P_InstaThrust(s, s.angle, 15*FRACUNIT*mult)
				end
				s.momz = ((-75*FRACUNIT*mult)/10)*(P_MobjFlip(s))				
			end
			
			--GFX when picking up damage-inducing speed!
			if (p.smsdivetimer == 16)
					
				P_SpawnMobjFromMobj(s, 0,0,0, MT_SMSGROUNDBOOST)
				P_RadiusAttack(s, s, 10*FRACUNIT*mult)
				for i = 1,2
					S_StartSound(s, sfx_smach)
				end
				local ghs = P_SpawnGhostMobj(s)
				ghs.destscale = $*99
				ghs.blendmode = AST_ADD
				ghs.fuse = 15 ghs.tics = 14	
				
			--GFX when breaching the boiling point!
			elseif (p.smsdivetimer == 36)
			
				local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_RADIALBOOST)
				ghs.target = s
				P_NukeEnemies(s, s, 15*FRACUNIT*mult)
				S_StartSound(s, sfx_s3k83)
			
			--Hyper Mystic Sonic incoming at max speed! Nowhere left to run.
			
			elseif (p.smsdivetimer > 56)
			and p.hypermysticsonic
				SMS_StompGFX(s, p, true)
				s.boostpass = true			
				
				local COLORTABLE = {SKINCOLOR_SMS1R, SKINCOLOR_SMS3R, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_FLAME,
				SKINCOLOR_CRIMSON, SKINCOLOR_MINT, SKINCOLOR_WHITE,  SKINCOLOR_RED,  SKINCOLOR_SMS4R, SKINCOLOR_SMS3R, SKINCOLOR_SMS2R}
				if p.smsdivetimer % 8 == 0 and SMS_Graphix(s) == 0
					HMS_BoltStorm(s, COLORTABLE[SMS_TimeFrame(9, 1)], 12, SMSLocalRNG(1,37, s)*ANG1, SMSLocalRNG(50,350, s))	
					for d = -1,1
						for i = -1,1
							if abs(d) == 1 and abs(i) == 1
							and not (d == 0 and i == 0)
								continue --
							end
							local ghs = HMS_BigBangVortex(s, 13, FRACUNIT, ANG1*69*((d) or 1)*((i) or 1), H|TR_TRANS10, COLORTABLE[SMS_TimeFrame(11+d+i, 1)])	
							ghs.tracer = s
							ghs.GSoffsetZ = (s.height/3)+(150*s.scale*d)
							if i != 0
								ghs.GSoffsetX = 150*s.scale
								ghs.GSoffsetY = 150*s.scale						
								ghs.GSoffsetang = s.angle+(ANGLE_90*i)
								ghs.frame = (i == 1) and $|FF_HORIZONTALFLIP or $
							else
								ghs.GSoffsetX = 0
								ghs.GSoffsetY = 0							
							end
							ghs.frame = (d == 1) and $|FF_VERTICALFLIP or $
						end
					end
				end
				
				if (leveltime % 2) --Player horizontal speed!
					P_InstaThrust(s, s.angle, 240*FRACUNIT)
				end
				P_SetObjectMomZ(s, -120*FRACUNIT, false) --Player falling speed!

				if (p.smsdivetimer == 57)
					S_StartSound(s, sfx_s3k83)
					S_StartSound(s, sfx_beeyow) //For that extra intensity.
					if SMS_Graphix(s) != 2
						SMS_AngledLightning(s, SKINCOLOR_RED, 23, 4)
						local ghs = SMS_LightFlash(s, SKINCOLOR_WHITE, FRACUNIT*2, 5, Z+3|TR_TRANS10)	
						ghs.tracer = s
						
						HMS_BoltStorm(s, SKINCOLOR_SMS5R, 14, nil, 150)	
					end
					A_SMSquake(s, 99*FRACUNIT, 8, 901)
				end
			end
			if mult != 10
				p.smsdivetimer = $+1
			end
		end
		if not p.hypermysticsonic
			s.SMS_locked = true
		end
	end
end)

--Landing in the Atom Dive can also be used to charge up a powerful dropdash!
rawset(_G, "SMSDropDash", function (s, p)
	if (G_GametypeHasSpectators() or s.battlemodsms) and p.rings
		P_GivePlayerRings(p, -1) --ring drain
	end
	--Trick the game a bit...
	p.powers[pw_noautobrake] = max($,2)
	p.speed = 250*FRACUNIT
	p.cmd.forwardmove = 50
	p.sms_spinuc = 2
	
	--What kind of boost?
	if p.hypermysticsonic
	or (p.powers[pw_sneakers] and G_PlatformGametype() and not s.battlemodsms) //Overdrive Dropdash!
		
		if p.hypermysticsonic
			P_InstaThrust(s, s.angle, 350*FRACUNIT)
			A_SMSquake(s, 104*FRACUNIT, 12, 1500)
			P_NukeEnemies(s, s, 500*FRACUNIT)
			P_FlashPal(p, PAL_NUKE, 7)
		else
			P_InstaThrust(s, s.angle, 260*FRACUNIT)
			A_SMSquake(s, 90*FRACUNIT, 4, 1500)
			P_NukeEnemies(s, s, 110*FRACUNIT) --Not really meant as an AoE.
			P_FlashPal(p, PAL_NUKE, 6)
		end
		S_StartSound(s, sfx_kc3c)
		S_StartSound(s, sfx_s3k5f)
		S_StartSound(s, sfx_bkpoof)
		s.smschargedbd = 11
		p.sms_boostlevel = 70
	//	s.hypermysticdash = 105 Hmm, maybe not?
		A_SMSSTOMPQUAKE(s, 125, 10, 25*FRACUNIT, 0, ANG20, 0, 20)
		A_SMSSTOMPQUAKE(s, 165, 14, 35*FRACUNIT, FRACUNIT*2, ANG30, 0, 20, SKINCOLOR_SUPERSILVER4)
			
	elseif (p.smsdivetimer >= 38) or (p.sms_boostlevel >= 45) //Instant max-dropdash if boostleveling
			
		P_InstaThrust(s, s.angle, 206*FRACUNIT)
		S_StartSound(s, sfx_kc3c)
		S_StartSound(s, sfx_s3k5f)
		P_FlashPal(p, PAL_MIXUP, 3)
		P_NukeEnemies(s, s, 70*FRACUNIT)
		s.smschargedbd = 11
		A_SMSquake(s, 74*FRACUNIT, 11, 1400)
		p.sms_boostlevel = 70 //Automatically go into boostmode from this one.	
			
	elseif (p.smsdivetimer > 16)
			
		P_InstaThrust(s, s.angle, 145*FRACUNIT)
		S_StartSound(s, sfx_kc3c)
		p.sms_boostlevel = $+35
		A_SMSquake(s, 54*FRACUNIT, 7, 1000)
			
	elseif (p.smsdivetimer < 17)	
			
		P_InstaThrust(s, s.angle, 106*FRACUNIT)
		p.sms_boostlevel = $+24
		A_SMSquake(s, 24*FRACUNIT, 4, 500)
			
	end
	
	local ghs
	--Drop Dash effects.
	for i = 0,1
			if i == 0 and (p.smsdivetimer < 17) continue end
			ghs = SMS_LightFlash(s, SKINCOLOR_SMS2R+(i*3), FRACUNIT-17000+(i*9999), 6, Z+3|(TR_TRANS30-i*FRACUNIT))
			ghs.fuse = 9
			ghs.tracer = s
			ghs.renderflags = $|RF_PAPERSPRITE
			ghs.angle = s.angle+ANGLE_90
			ghs.spriteyoffset = $-20*FRACUNIT
			ghs.spriteyscale = $-10000
			if i != 0
				ghs.ditchtracer = 1
				ghs.spritexscale = $+30000
			else
				ghs.dispoffset = -40
			end
	end
	
	ghs = SMS_WallJumpGFX(s, true)	
	if ghs and ghs.valid
		ghs.color = SKINCOLOR_MYSTICAL3
	end
	s.forcewarp1 = 0
	s.forcewarp2 = 0
			
	p.smsdivetimer = 0
	p.smsdivefloat = 0
	p.smsdivedelay = 0
	p.smsatomicdivefail = 0
	s.smsdropdash = false
	
	p.sms_autoboosthold = 2
	s.smsdropdash = false
	p.pflags = $|PF_SPINNING & ~PF_STARTDASH & ~PF_THOKKED & ~PF_JUMPED
	s.state = S_PLAY_ROLL
	S_StartSound(s, sfx_cdfm35)	
	SMS_DustLayers(s)
	
	if p.hypermysticsonic
		s.spritexscale = FRACUNIT+22500
		s.spriteyscale = FRACUNIT-25000
		s.SMS_squashtable = {timer = 4, ground = true}
	else
		s.spritexscale = FRACUNIT-18500
		s.spriteyscale = FRACUNIT+43000
		s.SMS_squashtable = {timer = 20, Xspeed = 3800, Yspeed = 7500, ground = true}	
	end
	
	local ghs = P_SpawnGhostMobj(s)
	ghs.blendmode = AST_ADD
			
	ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMSGROUNDBOOST)
	ghs.tracer = s
	ghs.flags = MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOGRAVITY
	ghs.momx = s.momx/3 --Important note, apparently this causes a crash if it collides with a wall. Gotta mess with flags.
	ghs.momy = s.momy/3
	ghs.color = SKINCOLOR_SUPERSILVER5
			
	p.smsfullcharge = 0
			
	ghs = P_SpawnMobjFromMobj(s,0,0,-20*FRACUNIT, MT_HMSWAVEAURA)
	ghs.dustwave = 1
	ghs.sprite = SPR_SMMK
	ghs.frame = 3
	ghs.target = s
	ghs.fuse = 15
	ghs.scale = s.scale
	ghs.destscale = $*2
			
	for smsbdzone = 0,05
		P_SpawnParaloop(s.x, s.y, s.z+FixedMul(15*FRACUNIT, s.scale), FixedMul(84*FRACUNIT, s.scale), 8, MT_NIGHTSPARKLE, smsbdzone*ANGLE_22h, S_SMSTSPARK1, true)
	end
end)

--Puts SMS in the trickflip animation.
rawset(_G, "SMS_SideFlip", function(s, p, SMSblastjump)
		if (p.cmd.sidemove < -9)
			s.hmsrotateright = nil
		elseif (p.cmd.sidemove > 9)
			s.hmsrotateright = true
		elseif s.hmsrotateright
			s.hmsrotateright = nil
		else
			s.hmsrotateright = true
		end
		
		if SMSblastjump --Lets you double-jump in legacy mode if present, and adds effect.
			A_SMSSTOMPSMOKE(s, 6, MT_SPARK, nil, (s.momz*4/3))
			if p.smsblastboost //SMSblast SMS didn't actually have this, but it's what I intended back then with the white shield.
				P_SetObjectMomZ(s, 12*FRACUNIT, false)
				S_StartSound(s, sfx_nxbump)
			end
		end
				
		SMSRSpawnGhost(s, 14)
		s.state = S_SMS_SIDET
		
		p.hometrickanim = true
		s.hmscchange = true
		p.homingtrickcharge = 0
		s.smstrickroll = true
		s.smstrickrollang = 0
		
		p.pflags = $ & ~PF_JUMPED
		s.airboostroll = 0
		s.rollangle = 0
		p.charability = CA_THOK	
end)

--Roll during various trick. Usually 16*FRACUNIT speed with drawangle changing too.
rawset(_G, "SMS_TrickRoll", function (s, p, rollspeed, drawang)

		p.pflags = $|PF_JUMPED & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
	
	--	if s.smstrickrollang == nil
	--		s.smstrickrollang = 0
	--	end
		
		local MULT = s.hmsrotateright and 1 or -1
		s.rollangle = $-FixedAngle(rollspeed*MULT)
		
		if (s.tics < 19) and (s.tics > 5)
			if drawang
				p.drawangle = s.smstrickrollang-FixedAngle(45*FRACUNIT*MULT)
			end
			s.smstrickrollang = $-FixedAngle(45*FRACUNIT*MULT)
		end
		
		if not (s.SMS_WallJumpTable and s.SMS_WallJumpTable.sideflip == 2)
			if s.hmscchange and p.smsg2gfast and p.powers[pw_sneakers]
				s.hmscchange = true
				if not p.MSsettings
					p.viewrollangle = s.rollangle --Change this too.
				end
			end
			if (s.tics <= 2) --Aaaand stop!
				s.rollangle = 0
				s.smstrickrollang = 0
				s.smstrickroll = false
				if s.tics == 1 and (s.momz*P_MobjFlip(s) > 19*s.scale)
					s.state = S_SMS_ROLLT5 --Change trick depending on upwards momentum.
				end
			end			
		end
	
		--Allow player to thok out of the move.
		if (p.cmd.buttons & BT_JUMP) and (p.sms_jumphold == 1)
		and not p.powers[pw_carry]
			s.state = S_PLAY_ROLL
			p.pflags = $ & ~PF_JUMPDOWN --Removing PF_JUMPDOWN lets Sonic instantly thok again.
			p.hometrickanim = false
			return false --
		end
		return true --
end)

--Play as base Sonic in NiGHTS, but use the SMS transformation for boostpowers!
rawset(_G, "SMSNiGHTS", function (s, p)
		s.ssxaticket = false
		
		--Undo NiGHTS rollangle?
		if s.nrollanga
			if s.rollangle
				if not ((s.state >= S_PLAY_NIGHTS_STUN) and (s.state <= S_PLAY_NIGHTS_ATTACK))
					s.rollangle = 0
				end
			end
			s.fboostgfx = 0
			s.nrollanga = false
		end
		if p.powers[pw_flashing] and (s.state == S_PLAY_NIGHTS_STUN or s.state == S_PLAY_NIGHTS_PULL)
			if p.drillmeter and (p.drillmeter >= 3)
				p.drillmeter = $-3 //As a downside for your boost, you lose drill meter when you get hurt 
			else
				p.drillmeter = 0
			end
		end
		
		if (p.cmd.buttons & BT_CUSTOM1) and not (p.sms_tapnothold > 1)
		and not s.fboostgfx and (s.state >= S_PLAY_NIGHTS_STAND and s.state <= S_PLAY_NIGHTS_DRILL)
			if (p.drillmeter and (p.drillmeter > 200)) 
			s.fboost = P_SpawnMobj(s.x+s.momx, s.y+s.momy, s.z+s.momz, MT_NIGHTSBUMPER)
			s.fboost.sprite = SPR_NULL
			s.fboost.scale = $*3/2
				if (p.cmd.sidemove < 0)
					s.fboost.threshold = $-185 //Seems about right
				end
			s.fboost.nsonicb = s
			s.fboost.fuse = 1
			s.fboostgfx = 30
			S_StartSound(s, sfx_zoom)
			S_StartSound(s, sfx_bkpoof)
			A_SMSquake(s, 90*FRACUNIT, 12, 1100)
			P_FlashPal(p, PAL_MIXUP, 3)
			p.drillmeter = $-200
				for d = 0,15
					P_SpawnParaloop(s.x, s.y, s.z+FixedMul(20*FRACUNIT, s.scale), 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
				end
			else
				S_StartSound(s, sfx_cdfm70)
				s.flags2 = $|MF2_DONTDRAW
			end
		end
		
		if s.fboostgfx
			p.camerascale = FRACUNIT*3/2 //Zoom the camera out.
			s.bhlcamtimer = 70
			if (s.fboostgfx > 19)
				if not p.powers[pw_nights_helper]		
					p.powers[pw_nights_helper] = 105 
				end
				s.state = S_SMS_AIRBOOST
				local ghs = P_SpawnMobjFromMobj(s, 0,0, ((20*FRACUNIT)*(P_MobjFlip(s))), MT_SMSAIRBOOST)
				ghs.color = s.color
				ghs.scale = $+P_RandomRange(5000, 50000) 
				ghs.state = $+P_RandomRange(0,2)
				ghs.blendmode = AST_ADD
				s.renderflags = $|RF_FULLBRIGHT
			else
				if (s.fboostgfx == 19)
				p.bumpertime = 0
				A_SMSquake(s, 36*FRACUNIT, 16, 400)
				S_StartSound(s, sfx_s3k66)
					for d = 0,15 
					P_SpawnParaloop(s.x+s.momx, s.y+s.momy, s.z+s.momz+s.height*2, 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
					end
				end
				
				s.renderflags = $ & ~RF_FULLBRIGHT
				local ghs = P_SpawnMobjFromMobj(s, 0,0, ((20*FRACUNIT)*(P_MobjFlip(s))), MT_SMSAIRBOOST)
				ghs.color = SKINCOLOR_SKY
				ghs.state = $+P_RandomRange(0,2)
				ghs.blendmode = AST_ADD
			end
			s.fboostgfx = $-1
			P_SpawnGhostMobj(s)
		end
		
		if (s.state == S_PLAY_STND) or (s.state == S_PLAY_NIGHTS_STAND)
			s.state = S_PLAY_NIGHTS_FLOAT
		end
		if s.nrollangle == nil
			s.nrollangle = FixedAngle(39*FRACUNIT)
		else
			s.nrollangle = $+FixedAngle(39*FRACUNIT)
		end

end)

--Light Speed Attack, the LSA! Yes, SMS had this ages ago even in 2.1.
rawset(_G, "SMS_LSA", function(s, p)
			if not (p.mysticsuper and p.mysticsuper < 30)
			and not (p.lightspeedchain and p.lightspeedchain > 2)
			and not p.sms_intoship
				local ghs = P_SpawnMobj(s.x+FixedMul(170*FRACUNIT,cos(s.angle)), s.y+FixedMul(170*FRACUNIT,sin(s.angle)), s.z-550*FRACUNIT, MT_SMSECHECK)
				if ghs and ghs.valid
					ghs.lsasms = true
					ghs.target = s
					ghs.height = 1100*FRACUNIT
					ghs.radius = 960*FRACUNIT
				end		
				p.lightspeedchain = 2
				p.mslightspeedattack = 1
				p.pflags = $|PF_JUMPED
				if not (s.state == S_PLAY_ROLL)
					s.state = S_PLAY_ROLL
				end
			end
			
			if not p.lightspeedchain
				if not (s.target and s.target.valid)
				or not (s.target.health)
				or not (s.target.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR))
					p.mslightspeedattack = 0
				--	return false -- 
				end
			end
			
		if p.mslightspeedattack	
			local ghs = P_SpawnGhostMobj(s)
			ghs.colorized = true
			ghs.blendmode = AST_ADD
			ghs.fuse = 8 ghs.tics = 9
			ghs.destscale = ghs.scalespeed+1
				
			if s.target and s.target.valid and s.target.health // and (s.lsatarget and (s.target == s.lsatarget))
			and (s.target.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR))
			and not (s.target.flags2 & MF2_FRET)
			and (FixedHypot(s.target.x-s.x, s.target.y-s.y) < 1400*FRACUNIT)
			and P_CheckSight(s, s.target)
				p.lightspeedchain = 2
				p.lsdeadjump = 0
				p.pflags = $|PF_JUMPED
				s.lsaclosest = nil
				s.lsatarget = nil
				s.angle = (R_PointToAngle2(s.x, s.y, s.target.x, s.target.y))
				s.momx = FixedMul(FixedDiv(s.target.x-s.x, 512*FRACUNIT), FixedDiv(261*FRACUNIT, 3*FRACUNIT/2))
				s.momy = FixedMul(FixedDiv(s.target.y-s.y, 512*FRACUNIT), FixedDiv(261*FRACUNIT, 3*FRACUNIT/2))
				P_SpawnSpinMobj(p, (p.spinitem or MT_THOK))
				
					if p.mslightspeedattack == 1
						P_SetObjectMomZ(s, 32*FRACUNIT)
					elseif p.mslightspeedattack == 2
						P_SetObjectMomZ(s, 17*FRACUNIT)
					elseif p.mslightspeedattack == 3
						P_SetObjectMomZ(s, 3*FRACUNIT)
					else
						s.momz = FixedMul(FixedDiv(s.target.z-s.z, 512*FRACUNIT), FixedDiv(261*FRACUNIT, 3*FRACUNIT/2))
					end
					
					if (s.target.type == MT_SPRINGSHELL) //Oh, these bastards! Use P_Radius so we don't just recoil from their springs
					or (s.target.type == MT_YELLOWSHELL)
						P_RadiusAttack(s, s, 70*s.scale)
					else
						P_RadiusAttack(s, s, 30*s.scale)
					end
					
					//Attaaack!
					if (p.speed > 24*s.scale)
					or (abs(s.momz) > 16*s.scale)
						P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSAIRBOOST)
					end
			else
				if p.lsdeadjump != 2
					p.lsdeadjump = 1
				end
			end
		end
		
		if p.mslightspeedattack	
			if (p.lsdeadjump == 1)
				if not (s.target and s.target.valid)
				or not s.target.health
				or (s.target.flags2 & MF2_FRET)
					s.lsaclosest = nil
					s.lsatarget = nil
					p.lightspeedchain = 4
					p.lsdeadjump = 2
					p.pflags = $|PF_JUMPED
					P_SetObjectMomZ(s, 14*FRACUNIT)
					local ghs = P_SpawnMobj(s.x+FixedMul(170*FRACUNIT,cos(s.angle)), s.y+FixedMul(170*FRACUNIT,sin(s.angle)), s.z-550*FRACUNIT, MT_SMSECHECK)
					if ghs and ghs.valid
						ghs.lsasms = true
						ghs.target = s
						ghs.height = 1100*FRACUNIT
						ghs.radius = 960*FRACUNIT
					end
				end
			end
		end
		
		if p.mslightspeedattack
			// This party can't go on forever! Here's some cases where it's gotta stop!
			
			if (p.pflags & (PF_GLIDING|PF_STASIS|PF_SPINNING))
			and not (p.powers[pw_carry])
				p.mslightspeedattack = 0
		--		return false --
			end

			if (s.target and (s.target.flags2 & MF2_FRET))
			or (p.mslightspeedattack > 30)
				p.mslightspeedattack = 0
				p.pflags = $|PF_JUMPED
				P_SetObjectMomZ(s, 14*FRACUNIT)
			--	return false --
			end
			if not (p.panim == PA_ROLL) --We're still going, so zoooooom
				P_SetMobjStateNF(s, S_PLAY_ROLL)
				p.pflags = $|PF_JUMPED				
			end	
		end
end)

--Shitty version of P_RandomRange to save on netcode strain. "s" is the mobj calling it and must be valid.
--Don't use the exact same check within the same frame, the seed likely won't have changed.
rawset(_G, "SMSLocalRNG", function(a, b, s)
	local smsRNGseed = (leveltime % 3 == 0) and 15228646 or (leveltime % 2 == 0) and 2327299421 or 313512858
	
	smsRNGseed = $*(max(gamemap+leveltime,1) % 128) - (s.x+s.y+s.z) + (s.momx-s.frame+s.type) + FixedInt(s.angle) + (s.player and s.player.jointime*3 or 6)
	smsRNGseed = ($) and $/2 or 1626124580
	local final = ((smsRNGseed*36548569) >> 4) & (FRACUNIT-1)	
	return ((final * (b-a+1)) >> FRACBITS) + a
end)

--Quick function for spawning debris impacted by momentum. "s" does not have to be a player.
rawset(_G, "SMSdebris", function (s, v, total, sprite, frame)
	local speed = abs(s.momx)+abs(s.momy) 
	local angle
	if speed
		angle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	else
		angle = s.angle
	end
	local rng1 = P_RandomRange(-60,60)
	local rng2 = P_RandomRange(-60,60)	
	--Now spawn debris!
	for d = 0,total	
		local debris = P_SpawnMobjFromMobj(v, rng1*v.scale, rng2*v.scale, v.height/(d or 1), MT_THOK)	
		debris.fuse = 74
		debris.tics = 75
		debris.rollangle = (ANG1*5)*rng1
		debris.flags = MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
		P_InstaThrust(debris, angle+(((rng2 or 1)/2)*ANG1), speed+(8*s.scale))
		P_SetObjectMomZ(debris, P_RandomRange(-4,8)*s.scale, false)
		debris.scale = v.scale*3/2
		debris.sprite = sprite
		debris.frame = frame
	end
end)

--Dodgeboost" or buffer a spincharge/footsweep after a dodge.
rawset(_G, "SMS_dodgefollowup", function(s, p)

			if (s.sdodgelvl and s.sdodgelvl > 2)
				return false --Can only do it on first and second dodge!
			end
			
			local ghs	
			if (p.cmd.buttons & BT_SPIN) and not (p.cmd.forwardmove > 10) --Buffer into a spindash charge.
			
				p.pflags = $|PF_STARTDASH|PF_SPINNING|PF_USEDOWN & ~PF_THOKKED
				s.state = S_PLAY_ROLL
				s.momx = 0
				s.momy = 0
				p.smsmeleebutton = 850
				s.sms_juststomped = 5
				return true --
				
			elseif ((p.cmd.buttons & BT_CUSTOM1) and not (p.sms_c1 >= FRACUNIT)
			or ((p.cmd.buttons & BT_CUSTOM2) and (p.sms_autoboosthold or p.sms_boostlevel > 64 or p.powers[pw_sneakers])) )
			
			and (abs(p.cmd.forwardmove) > 5 or abs(p.cmd.sidemove) > 5)
			
			and not (s.eflags & (MFE_UNDERWATER|MFE_SPRUNG))
			
				p.speed = 140*FRACUNIT //Trick SRB2 for 1 frame
				s.smsboostimmune = 7
				p.sms_c1 = FRACUNIT
				p.sms_tapnothold = 30
				p.sms_dodging = 0
				p.sms_dodgetap = 30
				p.smsmeleebutton = 850
				s.sms_juststomped = 5
				local skipboost = false
				if p.powers[pw_sneakers] and (G_PlatformGametype() and not s.battlemodsms)
					p.sms_boostlevel = 65
					skipboost = true
				else
					p.sms_boostlevel = max($+29, 34)
				end

				ghs = P_SpawnGhostMobj(s)
				ghs.destscale = $*4
				ghs.rollangle = s.rollangle
				ghs.tics = 12
				
				local angle = (p.cmd.angleturn<<16)+(R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
				local speed = (p.cmd.forwardmove > 35) and 65*FRACUNIT or 55*FRACUNIT
					if (p.sms_boostlevel >= 65)
						speed = $+141*FRACUNIT
						if (p.speed > speed)
							P_InstaThrust(s, angle, p.speed)
						else
							P_InstaThrust(s, angle, speed/2)
						end
						if skipboost != true
							S_StartSound(s, sfx_smach)
							A_SMSquake(s, 64*FRACUNIT, 4, 900)
							if (p.cmd.forwardmove > 35)
								s.hudboost = 1
							end
							
							ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_RADIALBOOST)
							ghs.target = s
							ghs.blendmode = AST_ADD	
						end
					else
						P_Thrust(s, angle, speed)
					end
				
				ghs = SMS_MediumDustGFX(s, SKINCOLOR_WHITE, AST_ADD)
				
				if SMS_Graphix(s) != 2
					ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_HMSNOVAWAVE)
					ghs.color = s.color
					ghs.followmenoz = s
					ghs.tics = 1
					ghs.destscale = $/3

					ghs = SMS_WallJumpGFX(s, true, true)
					ghs.blendmode = AST_ADD
					ghs.color = SKINCOLOR_SMS3R 
					ghs.colorized = true
					P_TryMove(ghs, s.x+(s.momx*5), s.y+(s.momy*5), true)
					
					SMS_DustLayers(s, nil, s.angle)
				end
			
				S_StartSound(s, sfx_kc3c)
				S_StartSound(s, sfx_kc5b)			
				S_StartSound(s, sfx_s3k7e)
				
				local ghs = SMS_FollowCircle(s, s.color, 2)
				ghs.scalespeed = $*3 ghs.fuse = $/3
				for i = 1,2
					ghs = HMS_BigBangVortex(s, 13, nil, nil, T|(i*FRACUNIT), s.color)
					ghs.tracer = s
					if i == 2
						ghs.ditchtracer = 1
						ghs.color = SKINCOLOR_MYSTICAL3
						ghs.momx = s.momx/2
						ghs.momy = s.momy/2
					end
				end		
				if G_GametypeHasSpectators() and p.rings
					P_GivePlayerRings(p, -1) --This costs rings in match.
				end
				return true --
					
			elseif (p.cmd.buttons & (BT_CUSTOM3|BT_TOSSFLAG|BT_WEAPONPREV|BT_WEAPONNEXT)) // and not (p.pflags & PF_ANALOGMODE)
			and not s.sdodgeoh
			and not (s.eflags & MFE_UNDERWATER)
			and not (p.sms_c1 >= FRACUNIT)
			
				p.sweepspeed = 85*FRACUNIT
				p.footsweepanim = true
				p.sweepangle = s.angle
				p.footsweep01 = 0
				p.footsweep02 = 20
				A_SMSquake(s, 9*FRACUNIT, 2, 750, true)
				p.footsweep03 = 0
				p.smsmeleebutton = 9000
				s.state = S_SMS_FOOTSWEEP1
				P_SpawnGhostMobj(s)
				S_StartSound(s, sfx_s1c0)
				S_StartSound(s, sfx_sswip)
				S_StartSound(s, sfx_s3k64)
				SMS_DustLayers(s, nil, s.angle)
				
				ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_HMSNOVAWAVE)
				ghs.color = s.color
				ghs.followmenoz = s
				ghs.tics = 1
				ghs.destscale = $/3				
				
				if G_GametypeHasSpectators() and not (p.mxscap > 1)
					P_GivePlayerRings(p, -1)
				end
				return true --
			end
end)

--SMS can pick up items that are considered pushable objects, players, and sometimes even enemies!
rawset(_G, "SMSpickup", function (s, p)

	if not (p.smspickup and p.smspickup.valid)
		p.smspickup = nil
		
	else
	
		local t = p.smspickup
	
		t.eflags = s.eflags --Adjust to match our needs.
		t.rollangle = s.rollangle
	
		if (t.type == MT_PLAYER)
			t.state = S_PLAY_PAIN
			if t.player
			t.player.powers[pw_nocontrol] = 2
				if (t.player.cmd.buttons & BT_JUMP) or p.sms_intoship or t.player.sms_intoship
					s.whoopsnope = true
					P_DoJump(t.player, true)
					t.player.pflags = $|PF_JUMPED|PF_JUMPDOWN
					t.state = S_PLAY_ROLL
					if t.smspickupflags
						t.flags = t.smspickupflags
						t.smspickupflags = nil
					end
					t.SMS_lemmego = 2
				end
			end
		end
		if P_PlayerInPain(p) and s.skin == "sms" and not p.hypermysticsonic --Always ragdoll when hit holding stuff.
			SMS_RagDoll(s, 2)
		end
			
		--Now, do we drop or throw the item? WE might be forced to drop it.
		if (s.skin != "sms")
		or not t.health
		or not s.health
		or P_PlayerInPain(p)
		or (leveltime == 1) 
		or p.exiting
		or s.whoopsnope
		or ((p.cmd.buttons & BT_TOSSFLAG) and not (p.sms_tossflaghold > 1))
		or ( ((p.cmd.buttons & BT_WEAPONPREV) and not (p.weaponprevhold > 1)) and not p.hypermysticsonic)
		or ( ((p.cmd.buttons & BT_WEAPONNEXT) and (p.sms_weaponnexthold == 1)) and not p.hypermysticsonic)
		or (p.cmd.buttons & BT_ATTACK) and not (p.sms_flamebuttonhold > 1)
		or (p.cmd.buttons & BT_FIRENORMAL) and not p.sms_nflamehold
			
			s.whoopsnope = nil
				
			--Now then! Let's THROW them!
			if not t.SMS_lemmego
				
					P_InstaThrust(t, s.angle, 7*FRACUNIT)
					P_SetObjectMomZ(t, 5*FRACUNIT)
					t.smspickedup = nil
					if not s.homingbounce
						t.rollangle = 0
					end
					
						if ((p.cmd.buttons & BT_TOSSFLAG) and not (p.sms_tossflaghold > 1))
						or ( ((p.cmd.buttons & BT_WEAPONPREV) and not p.weaponprevhold) and not p.hypermysticsonic)
						or ( ((p.cmd.buttons & BT_WEAPONNEXT) and (p.sms_weaponnexthold == 1)) and not p.hypermysticsonic)
						or ((p.cmd.buttons & BT_FIRENORMAL) or (p.cmd.buttons & BT_ATTACK))
						
							if (p.speed < 5*s.scale)
								p.powers[pw_nocontrol] = $+7
							end
							P_InstaThrust(s, s.angle, -7*s.scale)
							p.sms_flamedelay = 28
							p.sms_nflamehold = 10
							if p.sms_ringburstfly
								s.momx = $/4
								s.momy = $/4
								s.momz = $/4
								p.sms_ringburstfly = 0
								p.sms_ringburstdrain = 0
							end
							s.hms_jumphold = 10
							p.sms_flamebuttonhold = 11
							p.hmsthrownnorise = true
							p.pflags = $ & ~PF_JUMPED
							s.spritexscale = FRACUNIT-14000
							s.spriteyscale = FRACUNIT+14000
							s.SMS_squashtable = {timer = 3}	
						
							local ghs
						
							if p.hypermysticsonic --HMS throw!
							and ((p.cmd.buttons & BT_ATTACK) or (p.cmd.buttons & BT_FIRENORMAL)) 
							and s.skin == "sms"
							
								s.state = S_HMSCOMETHAYMAKER2
								SMS_ElectricRecoil(s)
								SMS_Embers(s, s, 0)	
								SMS_BoostAura(s, p)
								SMS_WindBlast(s)
								SMS_DustLayers(t, nil, s.angle)
							
								s.hms_readyriseboost = 0
								p.novafire = 0
								p.powers[pw_nocontrol] = 11
								P_FlashPal(p, PAL_NUKE, 3)
								A_SMSquake(s, 80*FRACUNIT, 5, 1)
							
								ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSTHROWTRACKER)
								ghs.hmsthrow = 1
								ghs.hmsthrowaim = p.aiming
								ghs.hmsthrowang = s.angle
								ghs.hmsthrowog = p
								ghs.target = t
							
								S_StartSound(s, sfx_beflap)
								S_StartSound(s, sfx_s3k83)
								SMS_DustLayers(s, nil, s.angle)
							
							elseif not p.hypermysticsonic --SMS throw!
							and ((p.cmd.buttons & BT_ATTACK) or (p.cmd.buttons & BT_FIRENORMAL))
							and s.skin == "sms"
							
								s.state = S_SMSHUGEFLAMETOSS
								SMS_DustLayers(t, nil, s.angle)
							
								p.sms_flamebuttonhold = 10
								s.tics = $+3
								p.powers[pw_nocontrol] = 11
							
								ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSTHROWTRACKER)
								ghs.smsthrow = 1
								ghs.smsthrowaim = p.aiming
								ghs.smsthrowang = s.angle
								ghs.smsthrowog = p
								ghs.target = t
							
								P_SetObjectMomZ(s, 5*FRACUNIT)
								P_Thrust(s, s.angle, -9*FRACUNIT)
								S_StartSound(s, sfx_wepfir)
							
								if (p.cmd.buttons & BT_ATTACK) and (P_LookForEnemies(p, false, true)) //Use fire to do a homing throw!
									ghs.smsthrowguy = P_LookForEnemies(p)
								end
								
							--Weak throw!
							elseif not p.hypermysticsonic
								s.state = S_PLAY_GASP //Other skins can throw too, but not the strong throw since they'll have no sprites for it.
							end
							
							if t and t.valid
								if ghs and ghs.valid and ghs.smsthrowguy and ghs.smsthrowguy.valid
									s.forcesmsangletimer = 10
									s.forcesmsangle = R_PointToAngle2(s.x, s.y, ghs.smsthrowguy.x, ghs.smsthrowguy.y)
								else
									s.forcesmsangletimer = 10
									s.forcesmsangle = s.angle							
								end
							end
							
						end
						s.smspickupCD = 3 --Can't pick up stuff for a sec.
						if (t.type == MT_GARGOYLE) and t.smspickupflags != nil
							t.flags = t.smspickupflags|MF_PUSHABLE
							t.smspickupflags = nil
						elseif not t.pickedupmonitor and t.smspickupflags != nil
							t.flags = t.smspickupflags
							t.smspickupflags = nil
						end
						if t.pickedupmonitor
							local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSTHROWTRACKER)
							ghs.throwntracker = true
							ghs.ogsms = s
							ghs.target = t
							if not t.smspickupflags
								t.smspickupflags = MF_SOLID|MF_MONITOR|MF_SHOOTABLE
								t.flags = $|MF_NOCLIPTHING & ~(MF_MONITOR|MF_SHOOTABLE|MF_NOGRAVITY)
							end
						end
						
						if t and t.valid and (t.type == MT_PLAYER)
						t.state = S_PLAY_PAIN
							if t.player
								t.player.powers[pw_carry] = 0
								t.player.powers[pw_ignorelatch] = 32770
								t.player.powers[pw_noautobrake] = 2
								t.player.powers[pw_nocontrol] = 22
							end
						end

						p.smspickup = nil
						p.sms_tossflaghold = 5
						p.weaponprevhold = 5		
				return true --
			else --Nothing valid, we're done, I guess?
				p.smspickup = nil
				return false --
			end
		end
	end
		
		--This is the info given back to us by our detector object. It becomes our grabbed object!
		
		if s.smpktarget
			if s.smpktarget.valid and s.smpktarget.health --Great! We have just picked up the item!
				local zm = s.smpktarget
				local t = zm --Because zm?? Huh?
				
				zm.ogsms = nil --Remove potential values you shouldn't have.
				zm.rollhdecide = nil
				zm.cmphttimer = nil
				zm.SMS_lemmego = nil
				zm.ibethrownbysmshelp = nil
				
				if (zm.flags & MF_NOCLIPTHING) --Hyper specific situation...
				and zm.smspickupflags and not (zm.smspickupflags & MF_NOCLIPTHING)
					zm.flags = zm.smspickupflags
					zm.smspickupflags = nil
				end
				if zm.cttt --What, how?? Delete previously existing trackers.
					if zm.cttt.valid
						zm.cttt.state = S_NULL
					end
					zm.cttt = nil
				end
				
				if not p.smspickup
				S_StartSound(s, sfx_cdfm17)
				S_StartSound(s, sfx_s251)
			
				local ghs = P_SpawnMobjFromMobj(zm, 0,0,0, MT_HMSNOVAWAVE)
				ghs.scale = zm.scale/12
				ghs.tics = 2
				ghs.destscale = ghs.scalespeed+1
				ghs.color = SKINCOLOR_BLUE
				ghs = P_SpawnMobjFromMobj(zm,0,0,zm.height/2, MT_HMSNOVAWAVE)
				ghs.scale = zm.scale/12
				ghs.tics = 2
				ghs.destscale = ghs.scalespeed+1
				ghs.color = SKINCOLOR_BLUE

				if not s.SMS_squashtable
				and not p.hypermysticsonic
					s.spritexscale = FRACUNIT+14000
					s.spriteyscale = FRACUNIT-14000
					s.SMS_squashtable = {timer = 3}	
				end
				
				ghs = SMS_WallJumpGFX(zm, true)
				if ghs and ghs.valid
					ghs.spriteyscale = FRACUNIT/3
					ghs.spritexscale = FRACUNIT/3
				end
				if not s.homingbounce
					zm.rollangle = 0
				end
			
					if (zm.flags & MF_MONITOR)
						zm.smspickupflags = MF_MONITOR|MF_SHOOTABLE|MF_SOLID
						zm.pickedupmonitor = true
						zm.flags = $|MF_NOCLIPTHING & ~(MF_MONITOR|MF_SHOOTABLE)
						
						p.smspickup = zm
						
						zm.smspickedup = true
						p.sms_tossflaghold = 5
					elseif not ( (zm.type == MT_GARGOYLE) and ((zm.subsector.sector.floorpic == "DSTRACK2") or (zm.subsector.sector.floorpic == "DSTRACK1")) )
						if zm.smspickupflags
							zm.flags = zm.smspickupflags
							zm.smspickupflags = nil
						end
						p.smspickup = zm
						zm.smspickupflags = zm.flags
						zm.smspickedup = true
						zm.flags = $|MF_NOCLIPTHING|MF_NOCLIPHEIGHT & ~(MF_PUSHABLE|MF_SOLID)
						p.sms_tossflaghold = 5
					end
				end
			end
			s.smpktarget = nil

		--We're holding nothing. Spawn the item pickup detector!
		elseif (gametype == GT_COOP) and (s.skin == "sms") and s.health
		and not (p.pflags & (PF_SPINNING|PF_STARTDASH)) //Throw monitors! ..and other stuff, like players and pushables!
		and not s.smspickupCD
		
		local ANGLE = ((p.pflags & PF_ANALOGMODE) and (p.speed > 4*s.scale)) and R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) or s.angle
		
			if ((p.cmd.buttons & BT_TOSSFLAG) and not (p.sms_tossflaghold > 1) --Alot of buttons work here.
			or ((p.cmd.buttons & BT_WEAPONPREV) and not (p.weaponprevhold > 1))
			or (p.cmd.buttons & BT_ATTACK) and not (p.sms_flamebuttonhold > 1)
			or (p.cmd.buttons & BT_FIRENORMAL) and not p.sms_nflamehold)
				if not (p.smspickup and p.smspickup.valid)
				and not p.exiting
				and not p.powers[pw_nocontrol]
				local ghs
					if p.hypermysticsonic
					ghs = P_SpawnMobj(
					s.x+FixedMul(119*FRACUNIT,cos(ANGLE)),
					s.y+FixedMul(119*FRACUNIT,sin(ANGLE)),
					s.z-60*FRACUNIT, MT_SMSECHECK)
						if ghs and ghs.valid
							ghs.smsepickup = true
							ghs.target = s
							ghs.radius = 120*FRACUNIT
							ghs.height = 120*FRACUNIT
						end
						
					else
					
					ghs = P_SpawnMobj(
					s.x+FixedMul(49*FRACUNIT,cos(ANGLE)),
					s.y+FixedMul(49*FRACUNIT,sin(ANGLE)),
					s.z-60*FRACUNIT, MT_SMSECHECK)
						if ghs and ghs.valid
							ghs.smsepickup = true
							ghs.target = s
							ghs.radius = 50*FRACUNIT
							ghs.height = 120*FRACUNIT
						end
					end
				end
					
			else --Only detects if can pick something up.
				local ghs
				if p.hypermysticsonic --Telekinetic pickup range.
					ghs = P_SpawnMobj(s.x+FixedMul(119*FRACUNIT,cos(ANGLE)), s.y+FixedMul(119*FRACUNIT,sin(ANGLE)), s.z-60*FRACUNIT, MT_SMSECHECK)
					if ghs and ghs.valid
						ghs.radius = 120*FRACUNIT
					end
				else
					ghs = P_SpawnMobj(s.x+FixedMul(49*FRACUNIT,cos(ANGLE)), s.y+FixedMul(49*FRACUNIT,sin(ANGLE)), s.z-60*FRACUNIT, MT_SMSECHECK)
					if ghs and ghs.valid
						ghs.radius = 50*FRACUNIT
					end
				end
				if ghs and ghs.valid
					ghs.justcheckin = true
					ghs.smsepickup = true
					ghs.target = s
					ghs.height = 120*FRACUNIT
				end
			end
		end
end)

--Charge up a blinddash on the spot
rawset(_G, "SMS_MovingCharge", function(s, p)
	//Let's allow SMS a moving spindash charge!
	if P_IsObjectOnGround(s)	
		if (p.cmd.buttons & BT_SPIN)
		
		s.sms_doubleblinddash = s.sms_doubleblinddash and $+1 or 1
		
			local METHOD = 0
			
			if (s.sms_doubleblinddash > 2) --Needed for proper conversion.
			or (p.cmd.forwardmove < -40)
				METHOD = 1
			elseif not p.spinhold
				if (abs(p.cmd.forwardmove) < 4) and (abs(p.cmd.sidemove) < 4) 
				and (p.pflags & PF_SPINNING) and not (p.cmd.buttons & BT_CUSTOM2)
				and not p.sms_dodging and not p.footsweepanim
					METHOD = 2
				end
			end
			if METHOD
				if METHOD == 2
				or ((p.cmd.forwardmove < 0) and not (p.pflags & PF_ANALOGMODE)) 
				or ((p.pflags & PF_ANALOGMODE) and (abs(p.cmd.forwardmove) < 5) and (abs(p.cmd.sidemove) < 5))	
					s.haltthespam  = s.haltthespam  and min($+8,28) or 8
					if (s.haltthespam > 23) and s.sms_fullcharge --You're spamming...
						if (p.sms_boostlevel > 64)
							s.sms_boostloss = 12
						end
						p.sms_boostlevel = 0
						s.haltthespam = 14
						s.momx = $/5
						s.momy = $/5
						s.sms_fullcharge = 0
						if p.dashspeed > 36*FRACUNIT
							p.dashspeed = 36*FRACUNIT
						end
						return false --
					end
					if p.dashspeed < p.mindash
						p.dashspeed = p.mindash
						if (p.dashspeed < p.speed)
							p.dashspeed = min(p.speed, p.maxdash) 
						end
					end
					p.pflags = $|PF_STARTDASH
					S_StartSoundAtVolume(s, sfx_cdfm17, 88)
					local ghs = SMS_WallJumpGFX(s, true)
					ghs.frame = ($ & ~FF_TRANSMASK)|TR_TRANS60
					ghs.spriteyscale = FRACUNIT/3
					ghs.spritexscale = FRACUNIT/3
					
					if p.cmomx or p.cmomy
						s.momx = 0
						s.momy = 0
						p.cmomy = 0
						p.cmomx = 0
					elseif (p.speed > 4*s.scale+30035)
						P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 4*s.scale+30035)
					end
					return true --
				end
			end
		else
			s.sms_doubleblinddash = 0
		end
	end
	return false --
end)

--Do a homing trick jump! Trick off the enemy! If forcejump is on, just do a frontflip without momentum.
rawset(_G, "SMS_HomingTrickJump", function(s, p, forcejump)

	if forcejump --Simply trick roll flip!
		s.state = S_SMS_ROLLT1
		p.lasthomingtrick = 2		
	else --Regular homing attack!
		--Gain back some homing attack momentum?
		local KeepMomentum = false
		
		if s.forcesmstrick
			s.forcesmstrick = false
		elseif (p.cmd.buttons & BT_CUSTOM1)
		or p.autoc1mom == 1
			KeepMomentum = true
		elseif p.autoc1mom == 2
			if p.smsrtable and p.smsrtable.direct
				if (p.smsrtable.direct > 20)
					KeepMomentum = true
				end
			else --Old school, I guess?
				if (p.cmd.forwardmove > 25) and not (p.pflags & PF_ANALOGMODE) 
				or (p.pflags & PF_ANALOGMODE) and (abs(p.cmd.sidemove) > 8) or (abs(p.cmd.forwardmove) > 8)
					KeepMomentum = true
				end
			end
		end
		if KeepMomentum --Now calculate angles and how hard we've pressed control sticks.
			local speed = min(36*s.scale, max(s.SMShomingspeed or 24*s.scale, 3*s.scale))
			local ANGLE = s.angle --We should always be facing the enemy in theory.
			local mult = 50
			
			if s.SMShomingangle != nil ANGLE = s.SMShomingangle end --but to be sure...
			
			if p.smsrtable and p.smsrtable.turnang != nil and (p.smsrtable.direct > 3)
				local yu = p.smsrtable
				local shift = (ANGLE-yu.turnang)	
				
				if (abs(shift/ANG1) > 110) and (p.cmd.forwardmove < -38) 
					speed = 4*s.scale --Player is clearly trying to brake...!
					shift = 4
				else --No braking? Then calculations time!
					if (shift > ANGLE_180) --Alow a fairly big degree of control.
						shift = InvAngle(InvAngle(shift)/4)
 					elseif shift
						shift = $/4
					end
					if shift --Shift isn't 0? (At which point nothing would happen anyway.)
						if (shift > 0)
							ANGLE = $-min((ANG20*6), shift)
							shift = AngleFixed(shift) --turn it into a readable number. Angle > Fixed means FRACUNIT, btw.
						elseif shift
							ANGLE = $-max(-(ANG20*6), shift)
							shift = abs(AngleFixed(shift)-360*FRACUNIT)
						end
					end
					if (yu.direct < 39) --Input determines momentum.
						mult = max(yu.direct, 10)
					end	
					if (s.eflags & MFE_UNDERWATER) --Less momentum underwater.
						mult = max($-10, 10)
					end		
				end
				speed = max($-(shift/4), 3*s.scale) --Speed is diminished by harsher input angles.
			end
			P_InstaThrust(s, ANGLE, FixedDiv(FixedMul(speed,mult), 50) ) --Final speed and angle!
		end
	end
	
	if not s.SMS_HomingBolt
		s.SMShomingangle = nil --Reset these.
		s.SMShomingspeed = nil
	end
	s.forcesmstrick = false
	
	if forcejump return end --
	
		--No tricks underwater.
		if (s.eflags & MFE_UNDERWATER) and not p.powers[pw_carry]
			p.pflags = $|PF_JUMPED &~ PF_NOJUMPDAMAGE & ~PF_THOKKED
			if s.state == S_PLAY_SPRING or s.state == S_PLAY_FALL
				s.state = S_PLAY_ROLL //No trickjumping underwater.
			end
			return --
		end
		
			local rng = P_RandomRange(0, 3)
			
			if not rng
			and not (p.lasthomingtrick == 1) --Backflip from a handstand frame!
				s.state = S_SMS_FLIPT1
				p.lasthomingtrick = 1			
				
			elseif (rng == 1) --Frontflip ofrwards!
			and not (p.lasthomingtrick == 2)
			
				s.state = S_SMS_ROLLT1
				p.lasthomingtrick = 2
				
			elseif (rng == 2) --Do a roll to the side using rollangle shenanigans.
			and not (p.lasthomingtrick == 4)
			
				s.state = S_SMS_SIDET
				s.rollangle = 0
				p.lasthomingtrick = 4
				s.smstrickroll = true
				s.smstrickrollang = 0
				if s.hmsrotateright --Alternate rolling right and left each time.
					s.hmsrotateright = false
				else
					s.hmsrotateright = true
				end
					
			elseif not (p.lasthomingtrick == 3) 
				s.state = S_SMS_PREHOMET1
				p.lasthomingtrick = 3
				s.sms_spintrickspeed = 27
				
			else --Emergency backup?
			
				s.state = S_SMS_FLIPT1
				p.lasthomingtrick = 1
			end
			if p.lasthomingtrick == 1 --Different table for handstand flip trick!
				s.spriteyoffset = -8*FRACUNIT
				s.spritexscale = FRACUNIT-16500
				s.spriteyscale = FRACUNIT+14000
				s.SMS_squashtable = {timer = 5, Xspeed = 5200, Yspeed = 5000, air = true}
			else
				s.spritexscale = FRACUNIT+11500
				s.spriteyscale = FRACUNIT-400
				s.spriteyoffset = 11*FRACUNIT
				s.SMS_squashtable = {timer = 9, Xspeed = 3500, Yspeed = 40, air = true}			
			end
			
			p.hometrickanim = true
			p.pflags = $|PF_JUMPED & ~PF_THOKKED
			p.homingtrickcharge = 0
			
			if (s.scale >= 2*FRACUNIT) and not forcejump --Big stomp!
				S_StartSound(s, sfx_brakrx)
				A_SMSSTOMPSMOKE(s, 18, MT_EXPLODE)
				A_SMSquake(s, 24*FRACUNIT, 6, 2200)
				P_RadiusAttack(s, s, 230*FRACUNIT, 0, false)
			end
end)

--Behaviour for tracking punched people. Used by MT_CMPHTRACKER" in LUA_SUPERMOVES.
--The punchedtracker"!
rawset(_G, "SMS_PunchTracker", function(s, t)
		if not (t and t.valid) --No target..??
		or t.trackerspawn and t.trackerspawn.valid --Another tracker already got them, let them handle it.
		and (t.trackerspawn != s) and (t.trackerspawn.type == MT_CMPHTRACKER)
			s.state = S_NULL return --
		end
		
		--Need to do Setup?
		if s.InitTrack == nil
			s.InitTrack = true
			if s.fusrodahfuse == nil		
				s.fusrodahfuse = 1	
			end			
			s.scale = t.scale  --Mimmick tracked object properties.
			s.radius = t.radius
			s.height = t.height
			s.flags = $|MF_NOCLIPTHING & ~MF_NOCLIP 
			
			s.Killer = s --Who's Hajime's killer?
			if s.tracer and s.tracer.player and s.tracer.skin == "sms"
				s.Killer = s.tracer
			end
			if (t.flags & MF_BOSS)
				if ((t.type >= MT_EGGMOBILE) and (t.type <= MT_EGGMOBILE4))
				or (t.type == MT_CYBRAKDEMON)
				or (t.type == MT_BLACKEGGMAN)
					S_StartSound(t, sfx_eggahh) --This man lives to be flung.
				end
			end
		end
		
		--Amount of time foe can be flung for before emergency protocols.
		if s.fusrodahfuse
			s.fusrodahfuse = $+1		
			--Failsafes if target has no momentum. s.angle is the punch angle and won't change.
			if ( ((leveltime % 2 == 0) or s.fusrodahfuse < 6) and (t.type == MT_METALSONIC_BATTLE))
			or not (t.type == MT_METALSONIC_BATTLE)
				if (abs(t.momx+t.momy) < 10*FRACUNIT)
					P_InstaThrust(t, s.angle, 125*FRACUNIT)
				elseif not s.fusrodahwallexplode
					P_InstaThrust(t, s.angle, 125*FRACUNIT)
				end
			 end
			if (s.fusrodahfuse < 8) --Rise like crazy initially!
				s.momz = $+115550*(P_MobjFlip(t))						
			elseif (s.fusrodahfuse > 220) --Time limit on how long enemies can be send flying before failsafe kicks in.
				s.fusrodahexplode = true --Explode after 7 seconds regardless of collission.
			else
				s.momz = $-55355*(P_MobjFlip(t))			
			end
			if (s.fusrodahfuse > 4) and (s.fusrodahfuse < 14) --Initial take-off smoketrail!
				local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_THOK) --Kind of feels like Smash Bros launching.
				ghs.state = S_SPINDUST1+P_RandomRange(0,2)
				ghs.colorized = true
				ghs.color = (SKINCOLOR_SUPERGOLD5+5)-(s.fusrodahfuse*2)
				ghs.fuse = 32
				ghs.frame = ($ & FF_FRAMEMASK)|TR_TRANS60
				ghs.scale = (t.scale*s.fusrodahfuse)/5
				ghs.scalespeed = $/2
				ghs.destscale = $*99
				if (s.fusrodahfuse < 9)
					ghs.flags2 = $|MF2_SHADOW
				end
			end			
		end
			
		P_SetOrigin(s, t.x, t.y, t.z) --The tracker copies the target's position and momentum for collisions!
			
		--Must have SOME amount of speed.
		if not s.momx and not s.momy
			P_InstaThrust(t, s.angle, 10*FRACUNIT)
			P_InstaThrust(s, s.angle, 10*FRACUNIT)
		else
			s.momx = t.momx
			s.momy = t.momy
			s.momz = t.momz
		end
			
		SMSWindLines(t, nil, true) --Spawn windlines around enemy.
			
		--Some strange enemies need special manipulation.(Dragon bombers, for example) Use this behaviour for them.
		if t.fixedbounceded
			t.flags = $|MF_BOUNCE|MF_SCENERY|MF_GRENADEBOUNCE
			t.retfus = t.retfus and $-1 or 77
		end
			
		--Decide spinangle, speed, and direction.
		if s.spindirection == nil
			if P_RandomRange(0,1)
				s.spindirection = 1
			else
				s.spindirection = -1
			end
		end
		
		s.SpinSpeed = s.SpinSpeed and max($-1, 40) or 99 --Gradually slow down spinning overtime!
		s.SpinRangle = s.SpinRangle and $+FixedAngle((s.SpinSpeed/2*s.spindirection)*FRACUNIT) or FixedAngle((s.SpinSpeed/2*s.spindirection)*FRACUNIT)
			
		t.rollangle = s.SpinRangle
		t.angle = s.SpinRangle
		t.flags = $|MF_NOCLIPTHING & ~MF_FLOAT & ~MF_NOGRAVITY & ~MF_SLIDEME --Flung target can't hit solid objects!
			
		--Keep flung target in current animation.
		if t.anim_duration t.anim_duration = $+1 end
		if t.tics t.tics = $+1 end
			
		--Don't let these enemies have targets or they'll misbehave.
		if (t.type == MT_GSNAPPER) or (t.type == MT_DRAGONBOMBER)
			t.target = nil
		end
		local method = 0
		
		if (t.eflags & MFE_JUSTHITFLOOR) 
			method = 1 --Hit floor! 			
		elseif s.fusrodahwallexplode
			method = 2 --Hit Wall!		
		elseif (t.retfus != nil and t.retfus <= 1)	
		or (t.type == MT_METALSONIC_BATTLE) and (abs(t.momx) < 10*t.scale) and (abs(t.momy) < 10*t.scale)
			s.fusrodahexplode = true --Special enemy ran out of fling juice
			method = 3 
		elseif s.fusrodahexplode 
			method = 4 --Lua-called force explode.
		end
		
		--Did we get an explosion method? Then finish it up!
		
		if method or s.fusrodahexplode --Got a method of explosion?
		
			if (t.type == MT_PTERABYTE) or (t.type == MT_PYREFLY) or (t.type == MT_DRAGONBOMBER)	
				t.flags = $ & ~MF_NOCLIPTHING & ~MF_SCENERY --Have your flags back.
			end
			
			--Create ground crater!
			if method != 2
			or P_IsObjectOnGround(t)
			or P_IsObjectOnGround(s)
				local ghs = SMS_SmartSplat(t, 62)
				if ghs and ghs.valid
					ghs.sprite = SPR_SMX1
					ghs.frame = S|TR_TRANS30
					ghs.scale = $*2
				end
				for i = 1,5
					SMS_CraterSpew(s, s.angle+ANGLE_180, 17)
				end
			end
			
			--Create wall crater!		
			if s.fusrodahwallexplode and s.fusrodahwallexplode.frontside
				if SMS_Graphix(s) != 2
				and not SMS_SkyBoxWall(s, s.fusrodahwallexplode, -35*s.scale)
				and not (SMS_MobjSecHeightDiff(s, s.fusrodahwallexplode) < 35*s.scale)
					SMS_WallCrater(s,t,s.fusrodahwallexplode)
					if SMS_Graphix(s) == 0
						for i = 1,5
							SMS_CraterSpew(s, s.angle+ANGLE_180, 9)
						end
					end
				end
				local SfX = P_SpawnMobjFromMobj(t, 0,0,0, MT_THOK) 
				SfX.flags2 = $|MF2_DONTDRAW
				SfX.tics = 133
				S_StartSound(SfX, sfx_smsfx6)
			end

			--Grounded wind blast!
			SMS_GroundWindBlast(s)
				
			--Wind Blast, but slightly customized!
			if method != 1
				for i = 1,2		
					local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSAUTOFRAME)
					
					ghs.sprite = SPR_SMX1
					ghs.frame = A|(i*FRACUNIT)
					ghs.fuse = 11+(6*i)
					if i == 2
						ghs.rollangle = ANGLE_45
					else
						ghs.scalespeed = $*3
					end
					ghs.sttspeed = 3+i
					ghs.destscale = $*99
					ghs.sttfinalframe = D 
					ghs.sttfade = true
				end	
			end		
				
			--Where there's smoke...
			local ghs = P_SpawnMobjFromMobj(t, 0, 0, 0, MT_TNTDUST)
			if ghs and ghs.valid
				P_SetObjectMomZ(ghs, FRACUNIT*3)
				ghs.scale = $/8
				ghs.frame = ($ & FF_FRAMEMASK)|TR_TRANS80
				ghs.destscale = FRACUNIT*99
				ghs.tics = $+6
				ghs.fuse = 75
			end
			
			if SMS_Graphix(s) != 2	
			
				--Rising crater flames.
				SMS_RisingFlames(s)			
			
				--A more detailed explosion!
				SMS_SA2Explosion(s)

				--Subtle papersprite flames that turn into embers.
				SMS_Embers(s, t, 0)	
			end
			
			--The original shockwave + sound. Only used for HMS now. If fusrodah = 2, HMS was the puncher.
			if (t.fusrodah >= 2) and method != 1			
				local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_BURSTWAVE)	
				if ghs and ghs.valid
					ghs.scale = 3*t.scale
					ghs.blendmode = AST_ADD
					ghs.color = SKINCOLOR_CRIMSON
					ghs.renderflags = RF_SEMIBRIGHT|RF_PAPERSPRITE
					ghs.angle = s.angle+ANGLE_90
						
					local SfX = P_SpawnMobjFromMobj(t, 0,0,0, MT_THOK) --Sound object.
					SfX.flags2 = $|MF2_DONTDRAW
					SfX.tics = 99
					S_StartSound(SfX, sfx_brakrx)					
				end				
			end
			--Kill target and remove tracker.
			if t and t.valid
				t.cmphtracker = nil --Remove tracker from their mobj.
				t.rollangle = 0 --Reset rollangle
				
				--Spawn an exploding inflated ghost version of yourself. Not on bosses, tho.
				if not (t.flags & MF_BOSS)
					SMS_ExplodingMobj(t)	
				else
					S_StopSoundByID(t, sfx_eggahh)
				end
				--Nearby players can feel the impact!
				for z in players.iterate
					if z.mo
					local DIST = FixedHypot(z.mo.x-t.x, z.mo.y-t.y)
						if DIST < 500*FRACUNIT
							P_FlashPal(z, PAL_NUKE, 2)
							A_SMSquake(z.mo, 60*FRACUNIT, 10, 1)
						elseif DIST < 2000*FRACUNIT
							A_SMSquake(z.mo, 20*FRACUNIT, 7, 1)							
						end
					end
				end
				if s.fusrodahwallexplode
					local NUM = ((s.fusrodahfuse or 1) > 14) and 2 or 1 --Explode louder if you're further away.
					for i = 1,NUM
						local SfX = P_SpawnMobjFromMobj(t, 0,0,0, MT_THOK) 
						SfX.flags2 = $|MF2_DONTDRAW
						SfX.tics = 99
						S_StartSound(SfX, sfx_smsfx1)
					end		
				end				
				if (t.health > 1)
					t.health = 1
				end
				
				P_KillMobj(t, s, s.Killer) --DIE.
				
				if t and t.valid
					t.fusrodah = nil --If you haven't disintegrated entirely, remove this.
				end
				if not (s.SMS_SpecialKill and s.SMS_SpecialKill.valid)
					s.state = S_NULL return --
				else
					
				end
			end					
		end
end)

--Assign a tracker to the guy we punched! "v" (the punched mobj) must be valid beforehand.
rawset(_G, "SMS_AssignPunchTracker", function(s, v, p)
local t = v
local TARGETPLAYER = p and p.mo or s.target
		
		if v.player
			if not G_GametypeHasSpectators() --Forget this nonsense.
			or TARGETPLAYER and TARGETPLAYER == v
			or s == v --Not yourself!
				return --
			end
			--Either be a direct hit or be an AoE from HMS.
			if not (s.player and s.player.hypermysticsonic)
			and not (TARGETPLAYER and TARGETPLAYER.player and TARGETPLAYER.player.hypermysticsonic)
				return --
			end
			
			local tp = v.player
			
			if v.health
			and not tp.cmphdodge
			and not tp.sms_dodging
			and not (TARGETPLAYER and TARGETPLAYER.player and TARGETPLAYER.player.ctfteam and (tp.ctfteam == TARGETPLAYER.player.ctfteam))
			and not (s.player and s.player.ctfteam and (tp.ctfteam == s.player.ctfteam))			
			and not (tp.panim == PA_PAIN)
				if (TARGETPLAYER and TARGETPLAYER.player and TARGETPLAYER.player.hypermysticsonic)
				or (s.player and s.player.hypermysticsonic)			
					if tp.powers[pw_invulnerability]
						tp.powers[pw_invulnerability] = 0  //What invincibility?
						P_RestoreMusic(tp)
					end
					P_RemoveShield(tp)
					tp.losstime = 2000
					P_FlashPal(tp, PAL_MIXUP, 5)
					if not s.player
						P_DamageMobj(v, s, TARGETPLAYER)
					else
						P_DamageMobj(v, s)
					end
					P_SpawnMobjFromMobj(v, 0, 0, 0, MT_SMSAIRBOOST)
					P_InstaThrust(v, s.angle, 125*FRACUNIT)
				else
					if not (tp.powers[pw_shield] == 513)
						P_RemoveShield(tp)
					end
					P_FlashPal(tp, PAL_MIXUP, 4)
					P_DamageMobj(v, s, TARGETPLAYER)
					P_SpawnMobjFromMobj(v, 0, 0, 0, MT_BURSTWAVE)
					P_RemoveShield(tp)
					if v.health //I COULD remove instathrust and stop dead people, but this is more hilarious.
						P_SetObjectMomZ(v, 12*FRACUNIT)
						v.state = S_PLAY_PAIN
					end
					P_InstaThrust(v, s.angle, 125*FRACUNIT)
				end
			end
		//If Battlemod Tails dodges the haymaker, his sentries do too!
		elseif v.followdist and v.target and v.target.player and v.target.player.cmphdodge
		
			return false --
			
		elseif (v.type == MT_SUSPICIOUSFACESTABBERSTATUE)
		
			if v.health and not (v.state == S_SUSPICIOUSFACESTABBERSTATUE_BURST2)	
				v.state = S_SUSPICIOUSFACESTABBERSTATUE_BURST2
				v.tics = 1
				v.fusrodah = 2
				return false --
			end
			
		elseif (v.type == MT_EGGROBO1)
		
				v.state = S_EGGROBO1_PISSED
				v.flags = MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
				local ghs = P_SpawnMobjFromMobj(v, 0,0,0, MT_CYBRAKDEMON_VILE_EXPLOSION)
				ghs.momx = v.momx
				ghs.momy = v.momy
				ghs.momz = v.momz
				S_StartSound(TARGETPLAYER, sfx_bsnipe)
				S_StartSound(v, sfx_brakrx)
				P_Thrust(v, R_PointToAngle2(s.x, s.y, v.x, v.y), 90*FRACUNIT)
				v.momz = 40*FRACUNIT
				v.fuse = 105
				v.fusrodah = 2
				P_SpawnGhostMobj(v)
				return false --
				
		elseif (v.type == MT_EGGSHIELD)
		
			if not v.health
				return --
			end
			
			if (v.target and v.target.valid)
			
			local t = v.target
			
			t.flags = $|MF_SHOOTABLE|MF_ENEMY
			P_SetObjectMomZ(t, 12*FRACUNIT)
			P_InstaThrust(t, s.angle, 125*s.scale)
			P_SpawnMobjFromMobj(t, 0,0,0, MT_BURSTWAVE)
			
			local ghs = P_SpawnMobjFromMobj(t,0,0,0, MT_BURSTWAVE)
			ghs.momx = t.momx/2
			ghs.momy = t.momy/2
			ghs.momz = t.momz/2
			
			t.fusrodah = 1
			t.trackerspawn = P_SpawnMobjFromMobj(t,0,0,0, MT_CMPHTRACKER)
			t.trackerspawn.angle = s.angle
			t.trackerspawn.tracer = TARGETPLAYER
				if TARGETPLAYER and TARGETPLAYER.valid
					if not S_SoundPlaying(TARGETPLAYER, sfx_nerf) --1 at a time?
						S_StartSound(TARGETPLAYER, sfx_nerf)
					end
				elseif s.player
					if not S_SoundPlaying(s, sfx_nerf)
						S_StartSound(s, sfx_nerf)
					end				
				end
				if t.trackerspawn
				t.trackerspawn.target = t
					if (TARGETPLAYER and TARGETPLAYER.player.hypermysticsonic)
						t.fusrodah = 2
					end
				end
			end
			P_KillMobj(v, s, TARGETPLAYER)
			return false --
			
		elseif (v.type == MT_EGGGUARD) --Your shield does not exist.
		
			if v.tracer and v.tracer.health and (v.tracer.type == MT_EGGSHIELD)	
				v.tracer.state = S_NULL
				v.flags = $|MF_SHOOTABLE|MF_ENEMY
			end
			
		elseif (v.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER) --Destroy electricity barrier.
		
			if s.z == s.floorz
				s.z = s.z+TARGETPLAYER.height/2
			end
			P_SetObjectMomZ(v.target, 12*FRACUNIT)
			P_InstaThrust(v.target, s.angle, 125*s.scale)
			P_SpawnMobjFromMobj(v.target, 0,0,0, MT_BURSTWAVE)
			v.target.fusrodah = 1
			v.target.trackerspawn = P_SpawnMobjFromMobj(v, 0,0,0, MT_CMPHTRACKER)
			v.target.trackerspawn.angle = s.angle
			v.target.trackerspawn.tracer = TARGETPLAYER
			v.target.trackerspawn.target = v.target
			P_KillMobj(v)
			return false --
		end	
		
		if (v.flags & (MF_ENEMY|MF_BOSS))
		or (v.type == MT_METALSONIC_BATTLE)
		or (v.type == MT_EGGGUARD)
		or (v.type == MT_KOOPA)
		or (v.type == MT_TURRET)
		or ((v.type == MT_CMPHTRACKER) and v.dummyhb)
		
			//Special treatment for special types.
			if (v.type == MT_TURRET)
			
				v.flags = $|MF_ENEMY|MF_SHOOTABLE & ~MF_NOCLIPTHING & ~MF_NOCLIP & ~MF_NOGRAVITY
		
			elseif (v.type == MT_PTERABYTE) or (v.type == MT_PYREFLY) or (v.type == MT_DRAGONBOMBER)
			
				v.flags = $|MF_SCENERY|MF_BOUNCE|MF_GRENADEBOUNCE
				v.fixedbounceded = true
			
			elseif (v.type == MT_METALSONIC_BATTLE)
			
 				v.flags = $|MF_BOSS|MF_SHOOTABLE & ~MF_NOCLIPTHING
			
			elseif (v.type == MT_EGGGUARD) and v.tracer and v.tracer.health and (v.tracer.type == MT_EGGSHIELD)
			
				v.tracer.state = S_NULL
				v.flags = $|MF_SHOOTABLE|MF_ENEMY
				
			elseif (v.type == MT_CMPHTRACKER) 
			
				if v.target and v.target.valid and ((v.target.type == MT_METALSONIC_BATTLE) or (v.target.type == MT_TURRET))
					v.target.flags = $|MF_BOSS|MF_SHOOTABLE & ~MF_NOCLIPTHING
				end		
			end	
			
			--Explosion!
			local ghs = P_SpawnMobjFromMobj(v, FixedMul(8*v.scale, cos(v.angle)),FixedMul(8*v.scale, sin(v.angle)), 0, MT_BURSTWAVE)
			ghs.renderflags = RF_SEMIBRIGHT|RF_PAPERSPRITE
			ghs.angle = s.angle+ANGLE_90	
			
			--Now adjust our angle appropriately.
			if s.tracer and s.tracer.valid and s.tracer.player and not s.player
				s.angle = R_PointToAngle2(t.x, t.y, s.tracer.x, s.tracer.y)
			end
			
			--Wind Blast!
			for i = 1,2		
				local ghs = P_SpawnMobjFromMobj(s, 
				FixedMul((15*i)*t.scale, cos(s.angle)),
				FixedMul((15*i)*t.scale, sin(s.angle)),
				0, MT_SMSAUTOFRAME)
		
				ghs.sprite = SPR_SMX1
				ghs.frame = A|(i*FRACUNIT)
				ghs.fuse = 11+(6*i)
				if i == 2
					ghs.rollangle = ANGLE_45
				else
					ghs.scalespeed = $*3
				end
				ghs.sttspeed = 3+i
				ghs.destscale = $*99
				ghs.sttfinalframe = D 
				ghs.sttfade = true
			end	
			
			--Fling the target!
			P_SetObjectMomZ(v, 12*FRACUNIT)
			P_InstaThrust(v, s.angle, 125*FRACUNIT)			
			
			local ghs = P_SpawnMobjFromMobj(v, FixedMul(3*v.scale, cos(v.angle)),FixedMul(3*v.scale, sin(v.angle)),0, MT_BURSTWAVE)
			P_InstaThrust(ghs, s.angle, 5*FRACUNIT)
			ghs.momz = v.momz/3
			ghs.renderflags = RF_SEMIBRIGHT|RF_PAPERSPRITE
			ghs.angle = s.angle+ANGLE_90
			
			v.fusrodah = 1
			v.trackerspawn = P_SpawnMobjFromMobj(v, 0,0,0, MT_CMPHTRACKER)
			v.trackerspawn.angle = s.angle
			v.trackerspawn.tracer = TARGETPLAYER
			v.trackerspawn.target = v

			if TARGETPLAYER and TARGETPLAYER.valid
				if TARGETPLAYER.player and TARGETPLAYER.player.hypermysticsonic
					v.fusrodah = 2
				elseif not S_SoundPlaying(TARGETPLAYER, sfx_nerf)
					S_StartSound(TARGETPLAYER, sfx_nerf)
				end				
			elseif s.player
				if not S_SoundPlaying(s, sfx_nerf)
					S_StartSound(s, sfx_nerf)
				end
				if s.player.hypermysticsonic
					v.fusrodah = 2
				elseif not S_SoundPlaying(s, sfx_nerf)
					S_StartSound(s, sfx_nerf)
				end
			end
		end	
end)

--The exact moment of raw impact for the Comet Haymaker Punch! Make sure s.target is valid beforehand!
rawset(_G, "SMS_HayMakerImpact", function(s, p)

		local t = s.target
		
		--Horizontal snap
		if not p.hypermysticsonic 
		and t.radius != nil
		and ( FixedHypot(s.x-t.x, s.y-t.y) >= max((t.radius/2), 3*s.scale) ) --Is there a need?
			local ang = R_PointToAngle2(s.x, s.y, t.x, t.y)
			P_TryMove(s, t.x-FixedMul(t.radius/2, cos(ang)), t.y-FixedMul(t.radius/2, sin(ang)), true)
		end
		
		--Vertical snap
		local plus = (P_IsObjectOnGround(t) and 1) or (t.height/2)+1
		
		if not (s.eflags & MFE_VERTICALFLIP) 
		and (t.z > t.floorz) and (FixedHypot(s.z, t.z+plus) < 180*FRACUNIT)
		and not (t.z > s.ceilingz)
		and not (t.z < s.floorz)
			s.z = t.z+(plus-1)
		end
		
		--Set state and punching properties
		s.state = SMSHayMakerState(p, 1)
		p.sms_comethaymaker = 1
		s.cmphlanded = 14
		s.eflags = $ & ~MFE_JUSTHITFLOOR
		p.sweepangle = R_PointToAngle2(s.x, s.y, t.x, t.y) --Used to lock player angle.
		p.sms_flamedelay = max($, 11) --Can't shoot for a little bit!
		A_SMSquake(s, 44*FRACUNIT, 11, 4400)
		P_FlashPal(p, PAL_MIXUP, 2)
		
		if not P_IsObjectOnGround(s) --raise into the air a bit to regain control after punching
			P_SetObjectMomZ(s, FRACUNIT*2)
		end
		
		--HMS exclusive stuff.
		if p.hypermysticsonic
			P_SpawnMobjFromMobj(s, FixedMul(6*s.scale, cos(s.angle)),FixedMul(6*s.scale, sin(s.angle)),0, MT_BURSTLIGHT)
			--Since it's spammable, cycle through effects.
			local rng = P_RandomRange(0,3)
			if not rng
				S_StartSound(s, sfx_rail1)
				S_StartSound(s, sfx_rail1)
				local ghs = P_SpawnMobjFromMobj(s, FixedMul(6*s.scale, cos(s.angle)),FixedMul(6*s.scale, sin(s.angle)),0, MT_RADIALBOOST)
				ghs.target = s
			elseif (rng == 1)
				S_StartSound(s, sfx_bedie1)
				P_SpawnMobjFromMobj(s, FixedMul(6*s.scale, cos(s.angle)),FixedMul(6*s.scale, sin(s.angle)),0, MT_SMSAIRBOOST)
			elseif (rng == 2)
				S_StartSound(s, sfx_s3k4e)
				local ghs = P_SpawnMobjFromMobj(s, FixedMul(6*s.scale, cos(s.angle)),FixedMul(6*s.scale, sin(s.angle)),0, MT_BURSTLIGHT)
				ghs.blendmode = AST_ADD
			else
				S_StartSound(s, sfx_s3k83)
				P_SpawnMobjFromMobj(s, FixedMul(6*s.scale, cos(s.angle)),FixedMul(6*s.scale, sin(s.angle)),0, MT_SMSGROUNDBOOST)
			end
			if not (twodlevel or (s.flags2 & MF2_TWOD))
				if p.smsrotatespikeball3 == 2
					p.smsrotatespikeball3 = 1
				else
					p.smsrotatespikeball3 = 2
					s.frame = $|FF_HORIZONTALFLIP
				end
			end
		end
		
		s.HayMakerPunch = 27
		
		--Did we get em? Lessgo!
		if not t.fusrodah
		
			SMS_AssignPunchTracker(s, t, p) --Assign a tracker to the guy getting punched.
			
				if t.valid and t.fusrodah --We did it!
					--Explosion!
					local ghs = P_SpawnMobjFromMobj(s, FixedMul(24*s.scale, cos(s.angle)), FixedMul(24*s.scale, sin(s.angle)), 0, MT_BURSTWAVE)
					ghs.renderflags = RF_SEMIBRIGHT|RF_PAPERSPRITE
					ghs.angle = R_PointToAngle2(s.x, s.y, s.target.x, s.target.y)+ANGLE_90	
					
					--Wind Blast!
					SMS_WindBlast(s)		
				end

				--Shockwaves as visual reference for the AoE.
				if SMS_Graphix(s) != 2
					SMS_GoldWaves(s,p)
				end

				if SMS_Graphix(s) == 0
					--Dust trail behind you if you're close to the ground!
					SMS_DustLayers(s, true)

					--Electric Recoil
					SMS_ElectricRecoil(s)
				
					--Create a skid mark on the floor
					local ghs = SMS_SmartSplat(s, 29, t, s.radius*4)
					if ghs and ghs.valid
						ghs.sprite = SPR_SMX2
						ghs.frame = (Z+3)|TR_TRANS10
						ghs.blendmode = AST_ADD
						--Second glowing overlay to give the illusion of a red-hot skid mark turning white.
						local ghs = SMS_SmartSplat(s, 12, t, s.radius*4)
						if ghs and ghs.valid
							ghs.sprite = SPR_SMX2
							ghs.frame = (Z+3)
							ghs.colorized = true
							ghs.color = SKINCOLOR_CRIMSON
							ghs.blendmode = AST_ADD
						end				
						--Flames coming from the skid mark.
						SMS_RisingCraterFlames(s)			
					end
				end
		end
		
		--No AoE hitbox in match. Players found it hard to predict
		if p.hypermysticsonic
		or not (G_GametypeHasSpectators() or s.battlemodsms) 
			P_SpawnPlayerMissile(s, MT_CMPHRADIUS)
		end
		
		--Arm swing smear! Using the autoframe object to save freeslot resources and hassle.
		SMS_PunchSmear(s,p)
		
		--Play a loud-ass punching sound.
		for i = 1,2
			local ghs = P_SpawnMobjFromMobj(s, 0,0,0,MT_THOK)
			ghs.tics = 199 ghs.flags2 = MF2_DONTDRAW
			S_StartSoundAtVolume(ghs, sfx_smsfx5, 188)
		end		
end)

--Comet Haymaker Chase code! Determine the best moment of impact, then SLAM!
rawset(_G, "SMS_HayMakerChase", function(s, p)
	if not (s.target and s.target.valid) --Why are we even here
		s.target = nil
		return false
	end
	
	local t = s.target

		p.powers[pw_noautobrake] = max($, 5)
		p.powers[pw_nocontrol] = max($, 2) --Automated from now on! Let Sonic do his thing.
		s.smspickupCD = 3
		s.eflags = $ & ~MFE_JUSTHITFLOOR
		p.charflags = $ & ~SF_RUNONWATER & ~SF_NOSKID
		p.skidtime = 0
		p.homing = 0
		SMS_StopStick(s)
		
			--Animation safeguard (Usually MFE_JUSTHITFLOOR annoyances...)
			if not ((s.state >= S_SMSCOMETHAYMAKER1) and (s.state <= S_HMSCOMETHAYMAKER2))
				s.state = SMSHayMakerState(p, 0)
			end
			if s.HayMakerRoll
				s.HayMakerRoll = min(0, $+ANG1)
				s.rollangle = s.HayMakerRoll
			end
			
			
			s.tics = $+1
			s.anim_duration = $+1
			
			if not p.hypermysticsonic
			and (p.sms_comethaymaker > 4)
				if (p.sms_comethaymaker % 7 == 0)
					local ghs = SMS_SparkleFlash(s, 18*s.scale, 1)
					ghs.colorized = true
					ghs.color = s.color	
				end
				SMSWindLines(s, p, true)
				--Create a smooth motion.
				s.spriteyoffset = min(10*FRACUNIT, (25-p.sms_comethaymaker)*32000)
				s.spritexscale = max(FRACUNIT-15000, FRACUNIT-((24-p.sms_comethaymaker)*398) )
				s.spriteyscale = min(FRACUNIT+24000, FRACUNIT+((24-p.sms_comethaymaker)*699) )			
				s.SMS_squashtable = {timer = 1, Xspeed = 1, Yspeed = 1}	
				
				if s.HayMakerGFXTable == nil 
					s.HayMakerGFXTable = {}
				end --Exist specifically during this function.
				s.HayMakerGFXTable[p.sms_comethaymaker] = SMS_WallJumpGFX(s, true)
				local ghs = s.HayMakerGFXTable[p.sms_comethaymaker]
				ghs.spriteyscale = FRACUNIT/min(6, max(2, (25-p.sms_comethaymaker)))
				ghs.spritexscale = FRACUNIT/min(6, max(2, (25-p.sms_comethaymaker)))
				ghs.spriteyoffset = $+(s.spriteyoffset)+4*FRACUNIT
				ghs.momx = s.momx/4
				ghs.momy = s.momy/4
			--	ghs.momz = s.momz/-4
				ghs.blendmode = AST_ADD
				ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60			
			end
			
			--Circumstances...
			if (t.type == MT_MINUS)
				if not t.fusrodah
					P_SetObjectMomZ(t, 1*FRACUNIT)
					t.flags = $|MF_SHOOTABLE|MF_ENEMY
					t.state = S_MINUS_AERIAL1
				end			
			elseif G_GametypeHasSpectators() or s.battlemodsms
				p.sms_boostdrain = $+1 --Drain rings while punching too.
			end
			
			--Undo directionchar's BS.
			if (p.pflags & (PF_DIRECTIONCHAR|PF_ANALOGMODE))
				s.forcesmsangletimer = 9
				s.forcesmsangle = R_PointToAngle2(s.x, s.y, t.x, t.y)
				p.drawangle = s.forcesmsangle
			end	
			
			if (t.player and (t.player.cmphdodge or t.player.sms_dodging) ) --Target is dodging us, don't proceed!
			and not p.hypermysticsonic
				return
			end
			
			--Chasing!
			local cspeed = p.hypermysticsonic and 512*FRACUNIT or 450*FRACUNIT
			
			--Aim not where they currently are, but where their face will be when you swing your fist into it.
			s.angle = R_PointToAngle2(s.x, s.y, t.x+t.momx, t.y+t.momy)
			s.momx = FixedMul(FixedDiv((t.x+t.momx)-s.x, cspeed), FixedDiv(132*FRACUNIT, 3*FRACUNIT/2))
			s.momy = FixedMul(FixedDiv((t.y+t.momy)-s.y, cspeed), FixedDiv(132*FRACUNIT, 3*FRACUNIT/2))
			s.momz = FixedMul(FixedDiv((t.z+t.momz)-s.z, cspeed), FixedDiv(132*FRACUNIT, 3*FRACUNIT/2))	
			
			 --Give players a chance to dodge the punch!
			if G_GametypeHasSpectators() and not p.hypermysticsonic
				for z in players.iterate
					if z.mo and z.mo.valid and (s != z.mo)
						if (z.mo == t) --It's already our target, so valid by default!
							
						or (FixedHypot(FixedHypot(z.mo.x-s.x, z.mo.y-s.y), z.mo.z-s.z) <= 585*FRACUNIT)
						and not (z.mo.z > s.z+(375*FRACUNIT)) --Close enough!
						and not (z.mo.z < s.z-(375*FRACUNIT))
							
							if not (z.cmphwarning and z.cmphwarning > 1)
							and not z.cmphdodge
							and not (z.ctfteam and (z.ctfteam == p.ctfteam)) --Don't hit teammates!
			
							z.smpawncher = p --The one who punches is I
							z.cmphwarning = 3
							
								if z.powers[pw_tailsfly] --Undo Tails' flight.
									z.mo.state = S_PLAY_FALL
									z.powers[pw_tailsfly] = 0
									z.fly1 = 0
									z.secondjump = 0
								end				
							end
						end
					end
				end
			end
			
			--If it's not a dodging player, and we're either close-by or the timer ran out, then PUNCH!				
			if ((p.hypermysticsonic and P_FaceStabCheckMeleeRange(s)) or P_CheckMeleeRange(s) and not t.player)
			or t.fusrodah
			or not t.health //Don't attack dead people.
			or not p.sms_comethaymaker --Timer's out!
			or (s.state == SMSHayMakerState(p, 1))
			
				if s.HayMakerGFXTable and not p.hypermysticsonic
					for i = 36,4,-1
						if s.HayMakerGFXTable[no] and s.HayMakerGFXTable[no].valid
							s.HayMakerGFXTable[no].state = S_NULL
						end
					end
				end
				s.HayMakerGFXTable = nil
				p.pflags = $|PF_JUMPDOWN|PF_USEDOWN
				
				if p.hypermysticsonic
					s.SMS_ControlAlter = {
					timer = 3,
					turnangle = R_PointToAngle2(s.x, s.y, t.x, t.y),
					removebuttons = (BT_SPIN|BT_CUSTOM3), --Can instantly punch again!
					freecondition = 3,
					}
				else
					s.SMS_ControlAlter = {
					timer = 9,
					turnangle = R_PointToAngle2(s.x, s.y, t.x, t.y),
					freeabsolutemove = 6,
					removebuttons = (BT_SPIN|BT_CUSTOM2|BT_CUSTOM3),
					freecondition = 3,
					}
				end
				
				S_StopSoundByID(s, sfx_smsfx8)
				s.SMS_lastwallANG = nil
				s.SMS_lastwallNUM = nil
				s.SMS_WJpenalty = 0
				
				s.rollangle = 0
				s.HayMakerRoll = -2
				SMS_HayMakerImpact(s, p)
				
				s.spritexscale = FRACUNIT*2
				s.spriteyscale = FRACUNIT-7100
				s.spriteyoffset = 5*FRACUNIT
				s.SMS_squashtable = {timer = 4, Xspeed = 27500, Yspeed = 3000}				
				
				return true --We made it to punch-land, good job!
			end
end)

--Are we correctly meeting Haymaker inputs and conditions?
rawset(_G, "SMS_HayMakerInput", function(s, p)
	if not (s.smshmcheck or (p.cmd.buttons & BT_CUSTOM2)) --Already ready for target!
		return false --
	end
	if (p.sms_boostlevel >= 64)
	or p.hypermysticsonic
	or (p.sms_ringburst and not p.sms_ringburstfly) --You need some speed going for this attack.
		if (p.smsmeleebutton < 2)
		or (p.smsmeleebutton < 5) and not p.hypermysticsonic --Tap button!
			if P_IsObjectOnGround(s)
			or p.smseasyhaymaker
			or (p.cmd.forwardmove < -15) and ((abs(p.cmd.sidemove) < 10) or (p.pflags & PF_ANALOGMODE)) --Hold backwards then punch for haymaker!
				if p.hypermysticsonic
					if not p.smsdivetimer
					and not SMS_Locked(s, p)
						return true --
					end
				elseif not p.sms_dodging
				and not p.smsdivetimer
				and not p.transformdown
				and not (p.cmd.buttons & BT_TOSSFLAG)
				and not (p.pflags & PF_SLIDING)
				and not (s.SMS_BDboostloss and (p.pflags & PF_STARTDASH))
				and not SMS_Locked(s, p)
					if (s.state != S_SMSCOMETHAYMAKER1)
					and (s.state != S_SMSCOMETHAYMAKER2)
						return true --
					end
				end
			end
		end
	end
	return false --
end)

--Float in Midair with the Atom Dive?
rawset(_G, "SMS_AtomDiveFloat", function(s, p, ADloop)				
	--You manually let go? You can move right away, then.
	local HoldControls = false	
	if p.hypermysticsonic
		if (p.cmd.buttons & BT_WEAPONPREV)
			HoldControls = true
		end
	elseif (p.cmd.buttons & (BT_TOSSFLAG|BT_WEAPONPREV|BT_WEAPONNEXT))
		HoldControls = true
	end
			
	if not s.atomdiveheld 
		if HoldControls 
			if not p.smsdivefloat --Time ran out!
				p.smsatomicdivefail = 120
				p.sms_boostlevel = 0
				s.sms_boostloss = 0
				s.atomdiveheld = 0
				s.state = S_PLAY_FALL
				s.SMSLASTANGLE = nil
				s.SMSDELAYRENDER = nil
				p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED
				p.powers[pw_flashing] = max($, 16)
				S_StartSound(s, sfx_ngskid)
							
				s.spritexscale = FRACUNIT-900
				s.spriteyscale = FRACUNIT+11000
				s.spriteyoffset = -11*FRACUNIT
				s.SMS_squashtable = {timer = 9, Xspeed = 40, Yspeed = 2400, air = true}	
				return false --
			end
		else --You manually let go? Then you can move right away
			s.state = S_PLAY_FALL
			p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED
			p.smsdivefloat = 0
			p.sms_boostlevel = 0 --You wasted this, though...
			s.SMSLASTANGLE = nil
			s.SMSDELAYRENDER = nil	
			s.spritexscale = FRACUNIT-3900
			s.spriteyscale = FRACUNIT+31000
			s.spriteyoffset = -11*FRACUNIT
			s.SMS_squashtable = {timer = 9, Xspeed = 400, Yspeed = 6400, air = true}
			return false --
		end
	end
	
	if (ADloop == true) return end
	
	p.camerascale = FRACUNIT*3/2
	s.bhlcamtimer = 2	
	
	if p.smsdivefloat and not p.stompanim
	
		//Okay! You can continue floating, but only for so long!
		//When ready, tap spin or fire to divebomb!
		
		if HoldControls or (s.atomdiveheld and not HoldControls)
	
			if not s.hmstorvictim --Keep yourself still unless you're in a tornado.
				s.momx = 0
				s.momy = 0
			end
			
			if (p.pflags & PF_DIRECTIONCHAR) --Lock player angle as if in strafe mode!
				s.forcesmsangletimer = 2
				s.forcesmsangle = s.angle
			end
			
			--Slowly lose floating powers...
			if p.smsdivefloat > 50
				P_SetObjectMomZ(s, -1*FRACUNIT/19)
			elseif (p.smsdivefloat > 22)
				P_SetObjectMomZ(s, -2*FRACUNIT/5)
			else
				P_SetObjectMomZ(s, -3*FRACUNIT/4)
			end
			
			--Launch the atom dive!
			local method = 0
			
			if (p.cmd.buttons & BT_JUMP) and not (p.sms_jumphold > 2)
			or (p.cmd.buttons & (BT_ATTACK|BT_FIRENORMAL))
			or (p.cmd.buttons & BT_SPIN) and not (p.spinhold > 1)
				method = 1
			elseif s.atomdiveheld and not HoldControls
				method = 2 --autofire method.
			end
			
			if method
				s.state = S_PLAY_ROLL
				p.smsdivefloat = 0
				s.smsdropdash = false
				p.spinhold = 2
				p.smsdivetimer = 1
				s.atomdiveheld = 0
				s.SMSDELAYRENDER = nil
				s.SMSLASTANGLE = nil
				
				local ghs = P_SpawnGhostMobj(s)
				ghs.destscale = $*99
				ghs.blendmode = AST_ADD

		
				s.spritexscale = FRACUNIT-32500
				s.spriteyscale = FRACUNIT*2
				s.spriteyoffset = -17*FRACUNIT
				s.SMS_squashtable = {timer = 22, Xspeed = 7000, Yspeed = 12100, air = true}
				
				if not ( (G_PlatformGametype() and not s.battlemodsms) )
				and not p.hypermysticsonic
				
				p.smsdivedelay = 8 --Slower slightly nerfed version for competitive gametypes.
				S_StartSound(s, sfx_s3k72)
				s.momx = 0 s.momy =0
				P_SetObjectMomZ(s, 0, false)
				P_SpawnMobjFromMobj(s, 0,0,0, MT_BURSTLIGHT)
				
				else --Regular version for coop.
				
				P_InstaThrust(s, s.angle, 50*s.scale)
				s.momz = (-25*FRACUNIT)*(P_MobjFlip(s))
				S_StartSound(s, sfx_beflap)
				
				end
				if method == 2 --Auto dropdash from a quicklaunch.
					s.smsdropdash = true
					S_StartSound(s, sfx_s3kabb)
					S_StartSound(s, sfx_cdfm23)
							/*	--Automatically dropdash if you're too close to the ground.
					if (s.eflags & MFE_VERTICALFLIP)
						if (s.z > s.ceilingz-170*FRACUNIT)
							s.smsdropdash = true
							S_StartSound(s, sfx_s3kabb)
							S_StartSound(s, sfx_cdfm23)
						end
					else
						if (s.z < s.floorz+150*FRACUNIT)
							s.smsdropdash = true
							S_StartSound(s, sfx_s3kabb)
							S_StartSound(s, sfx_cdfm23)
						end
					end */
				end
				s.atomdiveheld = 0
			else
				SMS_AtomDiveFloat(s, p, true)
			end
			return true --
		end	
	end
	return false --
end)

--You got atomdive properties, so do your transition!
rawset(_G, "SMS_AtomDiveTransition", function(s, p)	
	p.atomdiveaim = true
	--Enter stunned state on landing.
	if p.smsatomicdivefail
		if P_IsObjectOnGround(s) or (s.eflags & MFE_JUSTHITFLOOR)
			if not (s.momz*P_MobjFlip(s) > 0)			
				s.state = S_SMS_ATOMLAND1
				p.pflags = $|PF_JUMPDOWN
				p.powers[pw_nocontrol] = max($, 2)		
				return false --
			end
		end	
	end
	if p.smsdivedelay
		p.smsdivedelay = $-1
	end
	if p.atomdivetargetspin
		p.atomdivetargetspin = $-1
	end	
				
	--Shoot out aiming reticule and point camera down!
	A_AtomDiveAim(p)
	p.aiming = (P_MobjFlip(s)*-4850*FRACUNIT)	
	p.hmsdash = 0
	--Still in floating phase!
	if (s.state == S_SMS_ATOMDIVE)
		SMS_AtomDiveFloat(s, p)
		return false --
	end
	
	//Going into the diving phase itself! Anything left to do?
	if p.smsdivetimer
	and not A_HMSCheckDeathPit(s) //Let's make sure NONE of this works....
				
		s.smsboostimmune = 1
		p.pflags = $|PF_JUMPDOWN
		s.SMS_locked = true
		
		if not s.smsdropdash //Ready up for a dropdash instead!
		and (p.cmd.buttons & BT_SPIN) and not (p.spinhold > 1)
			s.smsdropdash = true
			S_StartSound(s, sfx_s3kabb)
			S_StartSound(s, sfx_cdfm23)
			
			local ghs = P_SpawnMobj(s.x, s.y, s.z, MT_HMSNOVAWAVE)
		 	ghs.scale = s.scale/12
		 	ghs.tics = 3
		 	ghs.destscale = ghs.scalespeed+1
				
		elseif s.smsdropdash and not P_IsObjectOnGround(s)
				
			s.ringburstticket = true
			if (p.cmd.buttons & BT_JUMP) and (p.sms_jumphold <= 1)
			or (p.cmd.buttons & BT_CUSTOM2) and not (p.smsmeleebutton > 1)  //Nope, nvm, cancel the dropdash!
				s.smsdropdash = false
				S_StartSound(s, sfx_s222)
				S_StartSound(s, sfx_s251)
				
				local ghs = P_SpawnMobj(s.x, s.y, s.z, MT_HMSNOVAWAVE)
		 		ghs.scale = s.scale/12
		 		ghs.tics = 3
				s.ringburstticket = false
			end
		end
		
		if (p.pflags & PF_DIRECTIONCHAR)
			p.drawangle = s.angle
		end
		if (p.smsdivetimer == 3) and (p.sms_boostlevel >= 65) --Boost to the next level!
			p.smsdivetimer = $+9
		end

		//Small delay before firing off in match!

		if p.smsdivedelay
			s.momx = 0 s.momy = 0 s.momz = 0
			if (p.smsdivedelay == 1)
				p.smsdivedelay = 0
				p.smsdivetimer = 2
				P_InstaThrust(s, s.angle, 50*s.scale)
				s.momz = (-25*FRACUNIT)*(P_MobjFlip(s))
				S_StartSound(s, sfx_beflap)
			end
			
		elseif (p.smsdivetimer < 18)
		and ((p.panim == PA_ROLL) or p.hypermysticsonic)
		and not (s.eflags & MFE_JUSTHITFLOOR)
		and not P_IsObjectOnGround(s)
		
		P_InstaThrust(s, s.angle, 50*s.scale)
		s.momz = (-25*s.scale)*P_MobjFlip(s)			
		
			if (p.powers[pw_sneakers] or p.hypermysticsonic) //Got runshoes? Advance STRAIGHT to the 2nd level!
			and not (G_GametypeHasSpectators() and not p.hypermysticsonic)
				p.smsdivetimer = 24
			end
		end
	end	
end)

--Input for the Atomic Dive, both SMS and HMS compatible.
rawset(_G, "SMS_AtomDiveInput", function(s, p)	
	local GOODSOFAR = false
	
	if p.hypermysticsonic --HMS conditions
		if (p.cmd.buttons & BT_WEAPONPREV)
		or s.atomdiveheld
			if (abs(p.speed) < s.scale/3)
			and not (p.cmd.buttons & BT_WEAPONNEXT)	
				if ((p.pflags & PF_JUMPED) or (s.eflags & MFE_SPRUNG))
				and (abs(s.momz) < 3*s.scale)
					GOODSOFAR = true
				end
			end
		end
	else --SMS version
		if (p.cmd.buttons & BT_TOSSFLAG) --Method 1
		and not (p.sms_tossflaghold > 1)
		
		or (p.cmd.buttons & BT_WEAPONPREV) --Method 2
		and not (p.weaponprevhold > 1)
		
		or (p.cmd.buttons & BT_WEAPONNEXT) --Method 3
		and not (p.sms_weaponnexthold > 1)
		
		or s.atomdiveheld --Method 4(Not an input, technically)
		
			if (p.pflags & PF_JUMPED)
			or (s.state == S_PLAY_SPRING)
			or p.hometrickanim 
			or p.sms_ringburstfly
			
			or (s.state == S_PLAY_FALL) --Or VERY situationally the falling state.
			and not SMS_Locked(s, p, true, true)
			and not p.hometrickanim
			and not ((p.pflags & PF_JUMPED)
			and not (p.pflags & PF_NOJUMPDAMAGE))
			and not p.sms_dodging --At ALL.
			and not p.smsatomicdivefail
			and not p.smsblastboost
			
				if not (s.eflags & MFE_UNDERWATER)
				and not (p.pflags & (PF_THOKKED|PF_STASIS))
				and not (s.state == S_SMS_ATOMDIVE)
				and not (p.sms_stompanim or p.smsdivefloat or p.smsatomicdivefail)	
					GOODSOFAR = true
				end
			end
		end
	end
	if GOODSOFAR == true --Now conditions that apply to both forms.	
		if not ((p.pflags & PF_SPINNING) and not (p.pflags & PF_JUMPED))
		and not P_IsObjectOnGround(s)
		and not SMS_Locked(s, p)		
		and not (p.pflags & PF_SLIDING)
		and not (s.eflags & MFE_GOOWATER)
		and not p.smsblastboost
		and not p.sms_sontransform
			return (p.hypermysticsonic) and 2 or 1
		end
	end
	return 0 --
end)

--The action coming from the Atom Dive input, putting SMS or HMS in a launch-ready
rawset(_G, "SMS_AtomDiveStart", function(s, p)
	--Actions to do for both forms.
	s.hmgodticket = true
	s.smslastspeedw = nil
	s.wthudtime = false
	s.SMS_locked = true
	s.smsdropdash = false
	
	if p.hypermysticsonic --HMS
		p.camerascale = FRACUNIT*3/2
		s.bhlcamtimer = 2
		s.atomdiveheld = 0
					
		if (p.cmd.buttons & (BT_SPIN|BT_ATTACK))
		and not (p.sms_comethaymaker or (p.cmd.buttons & BT_JUMP))
			p.smsdivefloat = 70
			s.state = S_PLAY_RUN
			p.spinhold = 2
			p.pflags = $ & ~(PF_JUMPED|PF_STARTJUMP)
			p.smsdivetimer = 1
			s.atomdiveheld = 0
			p.smsdivefloat = 0
			P_SpawnGhostMobj(s)
			P_InstaThrust(s, s.angle, 52*s.scale)
			P_SetObjectMomZ(s, -26*FRACUNIT, false)
			S_StartSound(s, sfx_beflap)
		end 
		return 2
		
	else --SMS		
			
			local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_HMSNOVAWAVE)
			ghs.scale = s.scale/7
			ghs.tics = 2
			ghs.destscale = ghs.scalespeed+1
			ghs.color = s.color
					
			ghs = P_SpawnMobjFromMobj(s, 0, 0, s.height, MT_HMSNOVAWAVE)
			ghs.scale = s.scale/11
			ghs.tics = 2
			ghs.destscale = ghs.scalespeed+1
			ghs.color = s.color
					
			if not p.smsdivefloat
				SMS_WallJumpDelete(s, true)
				s.atomdiveheld = 6
			end
			p.smsdivefloat = 70					
			S_StartSound(s, sfx_s3k64)
			S_StartSound(s, sfx_s3ka6)
					
			s.state = S_SMS_ATOMDIVE
			p.pflags = $|PF_THOKKED & ~(PF_JUMPED|PF_SPINNING|PF_STARTDASH|PF_STARTJUMP)
			s.momx = 0
			s.momy = 0
			s.momz = 0
					
			--Instantly stop a ringburst.
			if p.sms_ringburstfly
				local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_RADIALBOOST)
				ghs.target = s
				ghs.color = SKINCOLOR_FLAME
						
				S_StopSoundByID(s, sfx_smsrbt)
				if s.smssoundobj9 and s.smssoundobj9.valid
					s.smssoundobj9.state = S_NULL 
					s.smssoundobj9 = nil
				end
				A_SMSquake(s, 62*FRACUNIT, 4, 100)
				S_StartSound(s, sfx_wbreak)
						
				p.sms_ringburst = 0
				p.sms_ringburstready = 0
				p.sms_ringburstfly = 0
				p.sms_boostlevel = 95
				if p.rings --Balancing.
					P_GivePlayerRings(p, -1)
				end
			end
		return 1
	end
end)

--Are we correctly meeting dodge inputs and conditions?
rawset(_G, "SMS_DodgeInput", function(s, p)
	if (p.cmd.buttons & BT_CUSTOM2)
	and ((abs(p.cmd.sidemove) > 20) or (abs(p.cmd.forwardmove) > 20)) --Hold direction!
	and (p.sms_dodgetap < 5)
		
		if (p.smsmeleebutton > 849)
		or (p.smsmeleebutton > 8) and (p.speed >= 3*s.scale)
			return false --
		end
	
		if P_IsObjectOnGround(s)
		or (p.powers[pw_carry] == CR_ROPEHANG)
		or s.stompnegatedinput
			
			if not p.hypermysticsonic --Mr invincible don't need to dodge.
				if not p.sms_stompanim
				and (s.stompnegatedinput or not (s.eflags & (MFE_JUSTHITFLOOR|MFE_SPRUNG)))
				and not p.sms_comethaymaker
				and not (s.cmphlanded and s.cmphlanded > 9)
				and not (p.footsweep02 or p.footsweep03 or (p.footsweep01 and (p.footsweep01 < 13)))
				and not (p.pflags & (PF_STASIS|PF_SLIDING))
				and not (p.powers[pw_carry] and p.powers[pw_carry] != CR_ROPEHANG)
				and not p.smsblastboost
				and not p.gotflag	
					if not ( (abs(p.cmd.forwardmove) > 8) and ((s.flags2 & MF2_TWOD) or twodlevel) )
						return true --Do it!
					end
				end
			end
		end
	end
	return false --
end)

--Pull out SMS' swift dodge! Dodges laser sectors, projectiles, enemies, shields, haymakers, practically everything!
rawset(_G, "SMS_SwiftDodge", function(s, p)			
			if not (s.eflags & MFE_UNDERWATER)
				if s.sdodgecd
					if s.sdodgelvl
						s.sdodgelvl = $+1
					end
				else
					s.sdodgelvl = 1
				end
				s.sdodgecd = 24
				if s.sms_superspin 
				and (p.sms_boostlevel < 40)
					p.sms_boostlevel = min($+8, 40) --Literally had to program the exploit back in...you're welcome.
				end				
			else
				s.sdodgelvl = 1			
			end
			
			s.sms_runspinboost = 0
			p.sms_spinuc = 0
			s.smsboostimmune = 15 --Can't lose boostgauge for 15 tics.
			p.sms_dodging = 15 --12 of these are invincibility frames, the rest are cooldown and animation related.
			p.sms_tapnothold = 500
			s.dodgespinslide = false
			s.forcedod = false
			A_SMSquake(s, 5*FRACUNIT, 2, 200, true)
			p.footsweep01 = 0
			p.sms_comethaymaker = 0
			p.pflags = $ & ~(PF_SPINNING|PF_STARTDASH)
			
			if (p.powers[pw_carry] == CR_ROPEHANG)
				p.powers[pw_carry] = CR_NONE --Dodge off ropes!
				s.z = $+60*FRACUNIT
			end
			if G_GametypeHasSpectators() and not (p.mxscap > 1) and p.rings
				P_GivePlayerRings(p, -1) //Allow players to still use this at 0 rings for clutch life-saving dodges
			end
			
			local debuff = 0
			
			if not (s.eflags & MFE_UNDERWATER)
				P_SetObjectMomZ(s, FRACUNIT*8) --Rise!
				S_StartSound(s, sfx_sdodge)
				P_SpawnMobjFromMobj(s,
				FixedMul(-4*FRACUNIT, cos(s.angle)),
				FixedMul(-4*FRACUNIT, sin(s.angle)),
				0, MT_EXPLODE)
			else
				debuff = 35
				P_SetObjectMomZ(s, FRACUNIT*3)
				S_StartSound(s, sfx_s3k42)
				P_SpawnMobjFromMobj(s, 0,0,0, MT_MEDIUMBUBBLE)
				P_SpawnMobjFromMobj(s, -20*s.scale,-20*s.scale,8*s.scale, MT_SMALLBUBBLE)
				P_SpawnMobjFromMobj(s, 20*s.scale, 20*s.scale, 4*s.scale, MT_SMALLBUBBLE)
				if (p.cmd.buttons & BT_JUMP) and s.smsframejump
				and (p.sms_jumphold < 16) --Buffer into a dodgeleap.(in LUA_SMS1)
					s.bufferframejump = true
					s.smsframejump = $+1
				end
			end
			
			local TurnAng = (p.cmd.angleturn<<16)+(R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
			
			--2D Version?
			if (s.flags2 & MF2_TWOD or twodlevel)
				if (abs(p.cmd.sidemove) > 20)
					s.state = S_SMS_SIDEDODGE1
					s.hmsrotateright = (p.cmd.sidemove > 0) and true or false
					P_InstaThrust(s, s.angle, (76-debuff)*FRACUNIT)
				end
			--3D version!	
			elseif (abs(p.cmd.sidemove) > 20) --Dodge laterally!		
				s.state = S_SMS_SIDEDODGE1
				P_InstaThrust(s, TurnAng, (64-debuff)*FRACUNIT)
				s.sms_sideroll = (p.cmd.sidemove > 0) and -1 or 1 --Dodge left or right!
			elseif (p.cmd.forwardmove > 20) //Dodge forwards!
				if (p.sms_boostlevel >= 65) --This one has a boost version.
					s.state = S_SMS_BOOST02
					p.smsboostpanim = true
					P_InstaThrust(s, TurnAng, (226-debuff)*FRACUNIT)
				else
					s.state = S_PLAY_RUN
					P_InstaThrust(s, TurnAng, (76-debuff)*FRACUNIT)			
				end
			elseif (p.cmd.forwardmove < -20) //Dodge Backwards!
				s.state = S_SMS_SIDEDODGE2
				P_InstaThrust(s, TurnAng, (69-debuff)*FRACUNIT)
			end
			if not debuff
				SMSWindLines(s, nil, true)
				if (s.sdodgelvl > 2)
					SMS_MediumDustGFX(s)
				else
					SMS_QuickDustGFX(s)	
				end
			end
			SMS_Dodging(s, p) --Go ahead!
			return true --
end)

--SMS is currently dodging!
rawset(_G, "SMS_Dodging", function(s, p)
	--Cancel conditions!
	if (p.panim == PA_SPRING)
	or (p.panim == PA_FALL)
	or p.powers[pw_carry]
	or p.hypermysticsonic
	or p.sms_comethaymaker
	or p.cmphdodge
	or p.sms_didairboost
	or not p.sms_dodging
		p.sms_dodging = 0
		if not s.airboostroll
		and not p.sms_comethaymaker
			s.rollangle = 0
		end
		return false --
	end
	s.smsboostimmune = s.smsboostimmune and max($,3) or 3
		if p.sms_dodging == 15
			p.smsmeleebutton = 850
			p.pflags = $|PF_JUMPDOWN|PF_USEDOWN
				if s.sdodgelvl and not (s.eflags & MFE_UNDERWATER)
					if s.sdodgelvl == 2
						S_StartSound(s, sfx_s236)
						SMS_QuickDustGFX(s)
					--	P_SpawnXYZMissile(s, s, MT_EXPLODE, s.x, s.y, s.z)
					elseif (s.sdodgelvl > 2)
						s.sdodgecd = 0
							if (p.pflags & PF_ANALOGMODE)
								s.sliderollang = p.drawangle
							else
								s.sliderollang = s.angle
							end
						s.sdodgeoh = 30
						p.sms_flamedelay = 30
						p.smsatomdivestun = s.angle
						S_StartSound(s, sfx_cdfm51)
						S_StartSound(s, sfx_s3k47)
						S_StartSound(s, sfx_s25b)
						
					//	S_StartSound(s, sfx_kc3b) Unused sound
					
					--	for d = 0,7
					--		P_SpawnParaloop(s.x, s.y, s.z, 30*FRACUNIT, 4, MT_NIGHTSPARKLE, d*(ANG20*2), S_XPLD1, true)
					--	end
					--	P_SpawnXYZMissile(s, s, MT_EXPLODE, s.x, s.y, s.z)
						s.momx = $*6/5
						s.momy = $*6/5
						SMS_MediumDustGFX(s)
						s.momz = $*2 
					end
				end
				if (p.pflags & PF_DIRECTIONCHAR)
					p.drawangle = s.angle
				end
		elseif p.sms_dodging == 14
				if s.state == S_PLAY_EDGE
				or (s.state == S_PLAY_WALK)
				or (s.state == S_SMS_SIDEDODGE1)
					s.state = S_SMS_SIDEDODGE1
				elseif not (s.state == S_SMS_SIDEDODGE2) and not (p.sms_boostlevel >= 65)
					s.state = S_PLAY_RUN
				end
				p.pflags = $|PF_JUMPDOWN
		elseif p.sms_dodging == 13
				if (s.state == S_PLAY_WALK)
				or (s.state == S_PLAY_RUN)
				or ((s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04))
					if not P_IsObjectOnGround(s)
						s.state = S_PLAY_ROLL
					end
				end		
		elseif (p.sms_dodging < 4) --Final frames! What will you do?
		or P_IsObjectOnGround(s)
		
			local FailedFSkip = false
				if (p.sms_dodging > 1) --Snap to the floor for an early boost!
					if (s.eflags & MFE_VERTICALFLIP)
						if (abs(s.z-s.ceilingz) < 100*s.scale)
						and (SMS_dodgefollowup(s, p))
							s.z = s.ceilingz
							p.sms_dodging = 0
							FailedFSkip = true
							if (s.momz < 0)
								s.momz = $*-1
							end
						end
					else
						if (s.z < (s.floorz+60*s.scale+s.momz))
						and SMS_dodgefollowup(s, p)
							s.z = s.floorz
							p.sms_dodging = 0
							FailedFSkip = true
							if (s.momz > 0)
								s.momz = $*-1
							end
						end
					end
				end
				if not FailedFSkip
					p.pflags = $ & ~PF_THOKKED
					if (p.cmd.buttons & BT_CUSTOM2) and P_IsObjectOnGround(s) and not (p.cmd.buttons & BT_CUSTOM1) and not s.sdodgeoh
					and ((p.speed > 3*FRACUNIT) and (p.speed < 40*FRACUNIT))
						s.momx = $/2 s.momy = $/2 //Makes rapid-dodging easier for the button mashers.
					end
					if not P_IsObjectOnGround(s)
						if not p.powers[pw_carry]
							p.pflags = $|PF_JUMPED
						end
						if (p.pflags & PF_DIRECTIONCHAR)
							p.drawangle = s.angle
						end
					else
						SMS_dodgefollowup(s, p)	--We're grounded! Other Dodge follow-up potential then!
					end
				end
		end
		
		--Still dodging! Dodging 1 is a special value, btw.	
		
		if (p.sms_dodging > 2)
			p.pflags = $|PF_THOKKED
			p.drawangle = s.angle
			if (p.sms_dodging > 3)
				if s.forcedod and s.forcedodx
				s.momx = s.forcedodx
				s.momy = s.forcedody
				s.forcedod = false
					if (p.speed > 40*s.scale) and not (p.sms_boostlevel >= 65)
					or (p.speed > 70*s.scale)
						s.momx = $/2
						s.momy = $/2
					end
				end
				s.forcedodx = s.momx 
				s.forcedody = s.momy
			end
			if (p.sms_dodging > 10)
			local ghscolor = SMSLocalRNG(SKINCOLOR_SUPERRED1, SKINCOLOR_SUPERPERIDOT5,s)
			local ghs
				for d = 1,3
					if (d % 2 == 0)
						ghs = P_SpawnMobj(s.x-s.momx/2, s.y-s.momy/2, s.z-s.momz/2, MT_THOK)
						ghs.scale = $*10/9
						ghs.color = ghscolor
						ghs.momx = s.momx/d
						ghs.momy = s.momy/d
						ghs.momz = s.momz/d
						ghs.tics = 4
						ghs.destscale = ghs.scalespeed+1
					else
						ghs = P_SpawnGhostMobj(s)
						ghs.color = ghscolor
						ghs.tics = 3
						ghs.rollangle = s.rollangle
						ghs.momx = s.momx/((d)*-1)
						ghs.momy = s.momy/((d)*-1)
						ghs.momz = s.momz/((d)*-1)
					end
				end
			end
			if (p.cmd.buttons & BT_ATTACK) //Allow an immediate diffusion flame
				p.sms_flamebuttonhold = 3500
			end
			if (s.eflags & MFE_JUSTHITFLOOR) and p.sms_dodging != 15 and not (s.momz*P_MobjFlip(s) > 0)
				p.sms_dodging = 2
				
				if not s.sdodgeoh --Allow dodge followups here.
					SMS_dodgefollowup(s, p)
				end
			end
		end
		return true --
end)

--Are we correctly meeting footsweep inputs?
rawset(_G, "SMS_FootSweepInput", function(s, p)
	--First footsweep!
	if (p.sms_dodging > 6)
	or P_PlayerInPain(p)
		return --
	end
	if not (p.footsweep01 or p.footsweep02 or p.footsweep03)	
		if not (p.cmd.buttons & (BT_CUSTOM1|BT_TOSSFLAG))
		and not (p.sms_boostlevel >= 64)
		and not (s.sms_juststomped and s.sms_juststomped > 3)
		and not p.sms_ringburst
		and not p.smsdivetimer
		and not (p.pflags & (PF_SPINNING|PF_STASIS))
		and (p.panim != PA_ROLL)
		and (s.state != S_SMSCOMETHAYMAKER2)
		and not s.sdodgeoh
		and not p.smsblastboost
		
			if (p.pflags & PF_SLIDING)
				p.sweepspeed = 25*s.scale
			else
				p.sweepspeed = p.speed+FixedMul(p.cmomx, p.cmomy)
			end
			
			p.sweepangle = s.angle
			p.smsmeleebutton = $+4
			p.smsmeleebutton2 = 9000
			p.footsweepanim = true
			p.footsweep01 = 20
			p.footsweep02 = 0
			p.footsweep03 = 0
			p.pflags = $ & ~(PF_FULLSTASIS|PF_STASIS|PF_JUMPSTASIS|PF_SPINNING)
			s.state = S_SMS_FOOTSWEEP1
			if (s.eflags & MFE_UNDERWATER)
				S_StartSound(s, sfx_bubbl5)
				S_StartSound(s, sfx_s3k7d)
			else
				S_StartSound(s, sfx_sswip)
				s.spritexscale = FRACUNIT+9000
				s.spriteyscale = FRACUNIT-12000
				s.spritexoffset = 3*FRACUNIT
				s.spriteyoffset = 7*FRACUNIT
				s.SMS_squashtable = {timer = 5, Xspeed = (9000/4)-1, Yspeed = (12000/4)-1}	
				
				local ghs = SMS_TwirlGFX(s)
				ghs.spriteyoffset = $+5*FRACUNIT
				ghs.spriteyscale = $-23000
				ghs.spritexscale = $+9000
				ghs.frame = $+(FRACUNIT*2)
			end
			return true --
		end
		return --
		
	--Next footsweep combo?
	
	elseif not (p.pflags & (PF_SPINNING|PF_SLIDING))
	and not (G_GametypeHasSpectators() and ((p.rings < 1) and not (p.mxscap > 1)))
	and not (s.eflags & (MFE_UNDERWATER|MFE_SPRUNG))
	and not p.footsweep03
	
	
		if (p.footsweep01 or p.sms_dodging)
		and not p.footsweep02
	
			if abs(p.cmd.forwardmove) > 6
				p.sweepspeed = 50*s.scale
			end
			p.footsweepanim = true
			p.pflags = $ & ~(PF_FULLSTASIS|PF_STASIS|PF_JUMPSTASIS|PF_SPINNING)
			p.sweepangle = s.angle
			p.footsweep01 = 0
			p.footsweep02 = 20
			p.footsweep03 = 0
		
			A_SMSquake(s, 9*FRACUNIT, 2, 750, true)
			p.smsmeleebutton = 9000
		
			s.spritexscale = FRACUNIT+30000
			s.spriteyscale = FRACUNIT-4000
			s.spriteyoffset = 2*FRACUNIT
			s.SMS_squashtable = {timer = 4, Xspeed = 13000, Yspeed = 1700}	
		
			s.state = S_SMS_FOOTSWEEP1
		
			SMSRSpawnGhost(s, 10, true)
			S_StartSound(s, sfx_sswip)
			S_StartSound(s, sfx_s3k64)
			if G_GametypeHasSpectators() and not (p.mxscap > 1)
				P_GivePlayerRings(p, -1)
			end
			return true --
		
		elseif p.footsweep02 --Now do the Final Footsweep!
		and not (G_GametypeHasSpectators() and ((p.rings < 2) and not (p.mxscap > 1)))
	
			p.pflags = $ & ~(PF_FULLSTASIS|PF_STASIS|PF_JUMPSTASIS|PF_SPINNING)
			p.footsweepanim = true
			A_SMSquake(s, 9*FRACUNIT, 18, 750, true)
		
			if abs(p.cmd.forwardmove) > 6
				p.sweepspeed = 50*s.scale
			end
		
			p.sweepangle = s.angle
			p.footsweep01 = 0
			p.footsweep02 = 0
			p.footsweep03 = 21
				
			s.spritexscale = FRACUNIT*3
			s.spriteyscale = FRACUNIT-15000
			s.spriteyoffset = 6*FRACUNIT
			s.SMS_squashtable = {timer = 7, Xspeed = 33000, Yspeed = 3500}
			s.state = S_SMS_FOOTSWEEP1
			
			if SMS_Graphix(s) != 2
			local ghs
				for i = 2,8,2
					if i == 2 
						ghs = SMS_FlatWave(s, SKINCOLOR_MYSTICAL1, 30, nil, 0)
						ghs.frame = I|FF_TRANS10
					else
						if i == 6 continue end -- skip 6
						ghs = SMS_FlatWave(s, SKINCOLOR_SILVER, 13+(i*2), 2, 0)
						ghs.frame = Q|((i/3)*FRACUNIT)
						ghs.spritexscale = $/i
						ghs.spriteyscale = $/i
						ghs.spritexoffset = 127*FRACUNIT
						ghs.spriteyoffset = 129*FRACUNIT						
					end
					ghs.sttspeed = nil
					ghs.sttfade = 9
					ghs.tracer = s
					ghs.renderflags = RF_SEMIBRIGHT|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS|RF_NOSPLATBILLBOARD
					ghs.destscale = 999*FRACUNIT
					ghs.GSoffsetX = 16*s.scale/i
					ghs.GSoffsetY = 16*s.scale/i
					ghs.GSoffsetZ = (-25+(i*7))*s.scale
					ghs.GSoffsetang = s.angle
					ghs.sttroll = ANG1*-(37+(i*5))
				end
		
				local ghs = SMS_GroundWindBlast(s)
				ghs.colorized = true
				ghs.color = s.color
		
				if (abs(s.z-s.floorz) < 128*s.scale)
					if (p.sweepspeed >= 40*s.scale)
						SMS_DustLayers(s, true)
					end
					A_SMSSTOMPQUAKE(s, 45, 14, FRACUNIT, 0, 0, 0, 0, SKINCOLOR_RED)
					A_SMSSTOMPQUAKE(s, 65, 18, 2*FRACUNIT, 0, ANG20, 0, 0, SKINCOLOR_SUPERSILVER4)
				end		
				SMSRSpawnGhost(s, 10, true)
			end
			
			S_StartSound(s, sfx_sswip)
			S_StartSound(s, sfx_s3k82)
			S_StartSound(s, sfx_mswing)
		
			if G_GametypeHasSpectators() and not (p.mxscap > 1)
				P_GivePlayerRings(p, -1)
			end
			return true --
		end
	end
	return false
end)

--Reflect projectile with the third footsweep hit! 
rawset(_G, "SMS_FootSweepReflect", function(s, m)
	if not (m.valid and ((m.flags & MF_MISSILE) or (m.type == MT_ENERGYBALL)) )
		return 
	end
	local v = m
	if (m.target != s) 
	and (m.momx or m.momy or m.momz)
	and not (m.flags & (MF_ENEMY|MF_BOSS))
	and not (m.target and m.target.player and m.target.player.hypermysticsonic) --Can't reflect HMS attacks.
		if (m.flags2 & (MF2_RAILRING|MF2_DEBRIS)) or (m.flags & MF_NOCLIP) --Already destroyed missile.
		or (m.type == MT_DETON) --pls don't crash the game.
		or (m.type == MT_LASER)
		or (m.type == MT_SHOCKWAVE)
			return --
		end
		
		S_StartSound(s, sfx_kc68)
		S_StartSound(s, sfx_kc4c)
		
		--Turn it around!
		if m.target and m.target.valid --Aim at original target?
			local SPEED = abs(m.momy)+abs(m.momx)
			P_InstaThrust(m, R_PointToAngle2(m.x, m.y, m.target.x, m.target.y), SPEED*2)
			v.momx = (v.flags & MF_NOGRAVITY) and $*3 or $*2
			v.momy = (v.flags & MF_NOGRAVITY) and $*3 or $*2
		else	
			v.momx = (v.flags & MF_NOGRAVITY) and $*-3 or $*-2
			v.momy = (v.flags & MF_NOGRAVITY) and $*-3 or $*-2
		end
		v.momz = (v.flags & MF_NOGRAVITY) and $*-2 or $*-1
		local NEWANGLE = R_PointToAngle2(v.x, v.y, v.x+v.momx, v.y+v.momy)		
		v.angle = NEWANGLE
		
		local ghs
		local COLORTABLE = {SKINCOLOR_SMS1R, SKINCOLOR_SMS3R, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_MYSTICAL3, SKINCOLOR_MINT, SKINCOLOR_SMSROVERGLOW}
		--Effects.
		for i = 1,2
			ghs = SMS_FollowCircle(m, COLORTABLE[(SMSLocalRNG(1, 8, m)+i)], 1+(i*2))
			ghs.tracer = nil
			ghs.fuse = $+7
			if i != 1
				ghs.tracer = nil
				local RNG = SMSLocalRNG(7,11,s)
				ghs.momx = m.momx/RNG
				ghs.momy = m.momy/RNG
				ghs.momz = m.momz/RNG
			end
		end

		for i = 1,3 --Ghost trails of the projectile!
			local ghs = P_SpawnGhostMobj(m)
			ghs.fuse = 28-(i*2) ghs.tics = 99
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			if i != 1
				ghs.colorized = true
				ghs.color = (SKINCOLOR_SMSROVERGLOWRED+1-i)
				ghs.frame = ($ & ~FF_TRANSMASK)|(i*2*FRACUNIT)
				ghs.momx = m.momx/i
				ghs.momy = m.momy/i
				ghs.momz = m.momz/i
				ghs.scalespeed = m.scale/ghs.fuse
				ghs.destscale = ghs.scalespeed+1
				ghs.blendmode = AST_ADD
			else
				ghs.destscale = $*99
			end
		end
		
		local ghs = SMS_LightFlash(m, SKINCOLOR_SMS2R, FRACUNIT/3, 2)
		ghs.tracer = m
		ghs.fuse = 24
		
		SMS_QuickDustGFX(m, SKINCOLOR_SMS5R, AST_ADD)
		
		--You're MY missile now!
		v.target = s
		return m --
	end
end)

--Are we meeting the input for the transformation input into SMS? (HMS transformation is in LUA_HMS_FUNCTIONS)
--Note this one is called from a PreThinkFrame hook to modify controls.
rawset(_G, "SMS_TransformInput", function(s, p)
	if not (p.cmd.buttons & BT_CUSTOM3) --Button requirements.
	or (p.spinhold > 1)
	or (p.cmd.forwardmove < -5)
		return --
	end
	--Ring requirements.
	if not  (((p.rings > 29) or (s.smsvsboss and (p.rings >= s.smsvsboss))) or (p.sms_sontransform >= 69))
	or not (A_SMScompatibleskin(s))
		return --
	end
	if (p.panim == PA_IDLE)	
	and (p.speed < s.scale)
	and P_IsObjectOnGround(s)
		
		if not p.mysticsuper // and not p.powers[pw_super] 
		and not p.powers[pw_carry]
		and not G_GametypeHasSpectators() //SMS isn't a blatant advantage in these modes.
		and not p.exiting
		and not (p.pflags & (PF_JUMPED|PF_THOKKED|PF_SLIDING))
		and not s.battlemodsms
		and s.health
			p.cmd.forwardmove = 0
			p.cmd.sidemove = 0
			SMSRglow(s)
			s.tnospina = true
			if (p.cmd.buttons & BT_SPIN)
				S_StopSoundByID(s, skins[s.skin].soundsid[SKSSPNDSH] )
				S_StopSoundByID(s, skins[s.skin].soundsid[SKSZOOM] )
				S_StopSoundByID(s, skins[s.skin].soundsid[SKSSPIN] )
				p.pflags = $|PF_THOKKED & ~(PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_NOJUMPDAMAGE|PF_STARTJUMP)
				p.sms_sontransform = 1 --Counts up elsewhere
				s.state = S_PLAY_FALL
				p.panim = PA_FALL
				P_SetObjectMomZ(s, 95355)
				SMS_DoTransform(s, p)
				return 2 --
			else
				p.cmd.buttons = $ & ~BT_SPIN & ~BT_CUSTOM3 --Remove buttons so you don't ACTUALLY spin.
			end
			return 1 --
		end
	end
end)

--Process of transforming from Sonic into SMS! (HMS transformation is in LUA_HMS_FUNCTIONS)
rawset(_G, "SMS_DoTransform", function(s, p)

		SMS_StopStick(s)
		
		if (p.rings < 30) and (p.sms_sontransform < 69)
		and not (s.smsvsboss and (p.rings >= s.smsvsboss))
		  
			if not P_PlayerInPain(p) and not p.powers[pw_carry]
				s.state = S_PLAY_FALL
			end
			p.sms_sontransform = 0
			return --
		  
		elseif (not ( s.skin == "sms" or A_SMScompatibleskin(s) ) ) --Abort transformation!
		or (leveltime < 2)
		or not G_PlatformGametype()
		or P_PlayerInPain(p)
		  
			p.sms_sontransform = 0
			return --
		  
		else --We're in the main Transformation section.
		
		  	SMSRglow(s)
			
			if (p.sms_sontransform >= 69)
			
			or (p.speed < s.scale)
			and (p.cmd.buttons & BT_CUSTOM3)
			and (p.cmd.buttons & BT_SPIN)
				if (p.sms_sontransform > 8)
					if (p.sms_sontransform % 8 == 0) and (p.sms_sontransform < 63)
						local ghs = SMS_FlatWave(s, SKINCOLOR_MYSTICAL1, 8, 12, 200*s.scale)
						if (s.floorz < s.z+200*s.scale)
							ghs.z = s.floorz+FRACUNIT
						end
						ghs.frame = $+FF_TRANS20
					end
					if (p.sms_sontransform >= 43 and p.sms_sontransform < 72)
						SMS_StompGFX(s, p, true, true)
					end
				end
				if (p.sms_sontransform < 20)
				P_SetObjectMomZ(s, 95777)
					if not (s.state == S_PLAY_FALL)
						s.state = S_PLAY_FALL
					end
					if (p.sms_sontransform == 2)
						S_StartSound(s, sfx_s3k7e)
						local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK) --MT_SPINDUST, but better!
						ghs.state = (s.eflags & MFE_UNDERWATER) and (S_SPINDUST_BUBBLE1) or (S_SPINDUST2)
						ghs.fuse = 16 ghs.tics = 99
						ghs.destscale = $*99
					end
				elseif (p.sms_sontransform >= 20 and p.sms_sontransform < 30)
					P_SetObjectMomZ(s, 67000)
					s.sprite2 = SPR2_TRNS
					s.frame = (s.frame & ~FF_FRAMEMASK)|C
					s.tics = 55
				elseif (p.sms_sontransform >= 30 and p.sms_sontransform < 52)
					if (p.sms_sontransform == 30)
						S_StartSound(s, sfx_s3ka6)
						HMS_AuraStorm(s, nil, SKINCOLOR_WHITE, nil, nil, 2)
					elseif (p.sms_sontransform == 43)
						S_StartSound(s, sfx_s3ka0)
						HMS_AuraStorm(s)
						SMS_LensFlare(s, SKINCOLOR_MYSTICAL2, ANG1*9, 28, FRACUNIT/2)
					end				
					P_SetObjectMomZ(s, 32000)
					s.sprite2 = SPR2_TRNS
					s.frame = $ & ~FF_FRAMEMASK
					s.tics = 4
					A_BossScream(s, 0, MT_SMSTSPARK)
					
					if p.sms_sontransform % 2 == 0 --Super Mystic Surrounding Aura.
						local COLORTABLE = {SKINCOLOR_SMS1R, SKINCOLOR_SMS3R, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_LIME, SKINCOLOR_EMERALD, SKINCOLOR_MINT, SKINCOLOR_WHITE}
						HMS_AuraStorm(s, FRACUNIT/2, COLORTABLE[SMS_TimeFrame(9, 1)], 9)
					end
				elseif (p.sms_sontransform >= 52 and p.sms_sontransform < 69)
				
					P_SetObjectMomZ(s, 9001)
					s.sprite2 = SPR2_TRNS
					s.frame = B
					s.tics = 4
					A_BossScream(s, 0, MT_SMSTSPARK)
					HMS_NegativeZone(s, s, true, SKINCOLOR_BLACK, A|FF_TRANS80)
					
				elseif (p.sms_sontransform == 69) //This is the point where you transform into SMS.
					if netgame
						local ALLOWEDSKIN = CV_FindVar("forceskin") --R_SkinUsable is absolute garbage, wow.=
						if ALLOWEDSKIN != nil and ALLOWEDSKIN.value != nil and ALLOWEDSKIN.value != -1
							p.sms_sontransform = 0
							P_DoPlayerPain(p)
							P_InstaThrust(s, s.angle, -11*s.scale)
							S_StartSound(s, sfx_mario2)
							S_StartSound(nil, sfx_mario2, p)
							CONS_Printf(p, "Server has a\x85 FORCESKIN\x80 setting enabled!")
							return false --
						end
					end
					P_SetObjectMomZ(s, 1)
					p.sms_originalcolor = p.skincolor //Remember the player's original color.
					P_FlashPal(p, PAL_MIXUP, 4)
					if p.powers[pw_super]
						p.powers[pw_super] = 0
						P_SpawnShieldOrb(p)
						P_RestoreMusic(p)
					end
				
					P_RadiusAttack(s, s, 100*FRACUNIT)
					p.presmsskin = s.skin
					s.skin = "sms"	
					R_SetPlayerSkin(p, "sms") //For the life icon.
					SMS_SkinSwap(s, p)
					s.AmSuperMysticSonic = true
					
					if not (p.powers[pw_shield] & SH_FIREFLOWER)
					and not (mariomode and p.powers[pw_invulnerability])
						s.color = 0
					end
					if not p.smscoatc5
					or not p.shinyglosscoat
						p.skincolor = SKINCOLOR_SMS4R
					end
					S_StartSound(s, sfx_s3k9f)
					p.sms_transformstate = 1
					P_GivePlayerRings(p, -30)
					s.state = S_HMSTRANSFORM7
					s.tics = 550
					SMS_LightningStrike(s, SKINCOLOR_SKY)
					for d = 0,15 //0,15 means it iterates form 0 to 15, executing the paraloop 16 times total
						P_SpawnParaloop(s.x, s.y, s.z+FixedMul(20*FRACUNIT, s.scale), 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
					end
					if SMS_Graphix(s) != 2
						HMS_AuraStorm(s, nil, SKINCOLOR_RED)
						local ghs = SMS_LightClash(s, SKINCOLOR_MYSTICAL3, s)
						ghs.spriteyoffset = -7*FRACUNIT
						ghs.spritexoffset = 4*FRACUNIT
						ghs.tracer = nil
						ghs.renderflags = $ & ~RF_PAPERSPRITE
						ghs.frame = $ & ~FF_PAPERSPRITE
						for i = 0,1
							local ghs = SMS_LightningStrike(s, SKINCOLOR_SMS2R+i, R)
							ghs.dispoffset = 50
							ghs.frame = (i == 1) and $|FF_VERTICALFLIP or $
							ghs.scalespeed = $/2
							ghs.destscale = $*99
						end
					end					
				end
				p.active_supertrans = nil 
				p.custom3down = true 
				p.superanim = nil --All mod support stuff.
				p.powers[pw_nocontrol] = 1
				p.cmd.buttons = $ & ~BT_SPIN & ~BT_CUSTOM3
			else
				p.sms_sontransform = 0
				s.state = S_PLAY_FALL			
			end
			--Part where we're already SMS but finishing the animation.
			if (p.sms_sontransform >= 70 and p.sms_sontransform < 86)
				P_SetObjectMomZ(s, 0)
				s.state = S_HMSTRANSFORM7
				s.tics = 550		
			elseif (p.sms_sontransform >= 86 and p.sms_sontransform < 96)
				P_SetObjectMomZ(s, 0)
				s.state = S_HMSTRANSFORM1
				s.tics = 550			
			elseif (p.sms_sontransform >= 96)
				s.state = S_PLAY_SPRING
				p.sms_sontransform = 0
			end		
		end
end)

--The impact point for stomping when you land on the ground!
rawset(_G, "SMS_stompimpact", function(s, p, LVL)
	local GRAPHIX = SMS_Graphix(s)

	--Break sectors below without losing momentum.
	local sectorz = (s.subsector and s.subsector.sector) and s.subsector.sector or R_PointInSubsector(s.x, s.y).sector //Stomp through bustable floors!
	for fof in sectorz.ffloors()
		if (fof.flags & FF_EXISTS) and ((fof.flags & FF_BUSTUP) or (fof.flags & FF_STRONGBUST))
			if not (s.z+s.momz > fof.topheight)
			and not (s.z+s.momz+s.height+9*s.scale < fof.bottomheight)
				EV_CrumbleChain(sectorz, fof)		
				P_SetObjectMomZ(s, -32*FRACUNIT, true)
				p.pflags = $|PF_JUMPED & ~PF_SPINNING
				if not p.hypermysticsonic
					s.state = S_SMS_STOMP1
					p.smsrotatespikeball3 = 76
					p.smsrotatespikeball4 = 76
					p.sms_stompanim = 2
					p.hometrickanim = false
					p.sms_afterstompthok = false					
				end
			end
		end
	end
	
	if LVL == 3 --HMS impact!
		if not ((s.eflags & MFE_TOUCHWATER) and not (s.eflags & MFE_UNDERWATER))
		local ghs = SMS_SmartSplat(s, 70)
			if ghs and ghs.valid
				ghs.sprite = SPR_SMX1
				ghs.frame = S|TR_TRANS10
				ghs.scale = s.scale
				ghs.scalespeed = $*4
				ghs.destscale = s.scale*2
			end	
		end
		A_SMSSTOMPQUAKE(s, 45, 6, 8*FRACUNIT, 0, 0, 0, 20, SKINCOLOR_SUPERRED4)
		A_SMSSTOMPQUAKE(s, 125, 10, 25*FRACUNIT, 0, ANG20, 0, 20)
		A_SMSSTOMPQUAKE(s, 165, 14, 35*FRACUNIT, FRACUNIT*2, ANG30, 0, 20, SKINCOLOR_SUPERSILVER4)

		S_StartSound(s, sfx_spld)
		A_SMSquake(s, 75*FRACUNIT, 13, 2400)
	
		local SCALE1 = SMSLocalRNG(23456,98765,s)
		SMS_FlamingImpact(s, nil, 4, SCALE1+FRACUNIT, nil, 30, 16, -1)
		if GRAPHIX != 2
			SMS_FlamingImpact(s, nil, 2, SCALE1*2/3, nil, 30, 30, -1)
			SMS_InflictFlameHUD(s, 800*s.scale, 21, FF_TRANS70, SKINCOLOR_ORANGE)
			if p.speed > 40*s.scale
				for i = -2,2
					local ghs = SMS_CraterSpew(s, s.angle+(ANG1*40*i), 13)
					if ghs and ghs.valid
						ghs.spritexscale = $*3/2
						ghs.spriteyscale = $*3/2
						ghs.momx = ($*9)+FixedDiv(FixedMul(s.momx, 2),3)
						ghs.momy = ($*9)+FixedDiv(FixedMul(s.momy, 2),3)
						ghs.smsrgtimer = 49
					end
				end	
			else
				for i = -2,2
					local ghs = SMS_CraterSpew(s, s.angle+ANGLE_180, 10)
					if ghs and ghs.valid
						ghs.momz = $+9*s.scale
						P_Thrust(ghs, s.angle+(ANG20*i), 7*s.scale)
						ghs.smsrgtimer = 59
					end
				end				
			end
		end
		
		s.sms_juststomped = 2
		P_NukeEnemies(s, s, 777*s.scale)
		p.hms_stompbreak = 0
		s.SMS_stompmom = 0

		return 3 --
		
	else --Regular impact?
		
		if (SMSSHIELD(p) == SH_ELEMENTAL or SMSSHIELD(p) == SH_FLAMEAURA)
			P_ElementalFire(p, true)
			if not (s.eflags & MFE_UNDERWATER)
				S_StartSound(s, sfx_s3k47) 
				S_StartSound(s, sfx_s3k47)
				S_StartSound(s, sfx_s3k47) //Emulate the shield's stompfire since SMS already has a stomp of his own
			end
		end
		if s.battlemodsms and (p.speed < 90*s.scale)
		and not (p.sms_boostlevel >= 65) //Battlemod stomp anti-spam.
			p.sms_wallstat = 50 
			s.retmhtsfx = 48
			s.smsstompcd = 15
			p.sms_flamedelay = 10
			P_SetObjectMomZ(s, 5*FRACUNIT)
			p.pflags = $|PF_JUMPDOWN
			s.state = S_PLAY_ROLL
			SMSRSpawnGhost(s)
			s.momx = $/2
			s.momy = $/2
		end		
		if p.smsrotatespikeball4 == 76 --We fell through a bustable floor, so keep stomping!
			p.smsrotatespikeball3 = 1
			p.smsrotatespikeball4 = 1
			P_SetObjectMomZ(s, -8, true) --Not FRACUNIT!
			
		else --Stomp Impact!
		
			SMS_WallJumpDelete(s, true)
			s.SMS_WJpenalty = 0
			s.SMS_lastwallANG = nil
			s.SMS_lastwallNUM = nil
			p.sms_stompanim = 0
			p.sms_afterstompthok = false
			s.sms_juststomped = 6
			P_RadiusAttack(s, s, 114*s.scale)
			if not (s.eflags & MFE_UNDERWATER)
				A_SMSSTOMPQUAKE(s, 45, 14, FRACUNIT, 0, 0, 0, 0, SKINCOLOR_SUPERRED4)
				if GRAPHIX != 2
					A_SMSSTOMPQUAKE(s, 65, 16, 2*FRACUNIT, 0, ANG10)
					A_SMSSTOMPQUAKE(s, 85, 18, 3*FRACUNIT, 0, ANG20, 0, 0, SKINCOLOR_SUPERSILVER4)
					local ghs = SMS_FlatWave(s, SKINCOLOR_SUPERORANGE3, 9, 15)
					ghs.spriteyscale = $/2
					ghs.spritexscale = $/2
				end
			end
			if (s.scale >= FRACUNIT/2) and p.powers[pw_sneakers]
			and G_PlatformGametype() and not s.battlemodsms
			
			or (s.scale >= 2*FRACUNIT)
			or (p.sms_stompanim >= 9001)
			
			or (LVL and (LVL == 2) and not (s.eflags & MFE_UNDERWATER))
				
				local rvolume = 255
				if (LVL == 2) and s.SMS_stompmom
					rvolume = max(120, min(255, FixedInt(abs(s.SMS_stompmom))*2) )
				end
				S_StartSoundAtVolume(s, sfx_brakrx, rvolume)
				
				A_SMSSTOMPSMOKE(s, 18, MT_SMMK)
				A_SMSquake(s, 38*FRACUNIT, 8, 2300)
				P_RadiusAttack(s, s, 230*s.scale)
				local ghs = SMS_SmartSplat(s, 54)
				if ghs and ghs.valid
					ghs.sprite = SPR_SMX1
					ghs.frame = S|TR_TRANS20
					ghs.scale = s.scale/2
					ghs.scalespeed = $*4
					ghs.destscale = s.scale*2
				end
				for i = -3,3
					local ghs = SMS_CraterSpew(s, s.angle+ANGLE_180, 12)
					if ghs and ghs.valid
						P_Thrust(ghs, s.angle+(ANG20*i), 7*s.scale)
						ghs.smsrgtimer = 59
					end		
				end
				
			elseif (s.eflags & MFE_UNDERWATER)
			
				S_StartSound(s, sfx_s3k7d) //Make bubbles spawn when SMS hits the floor!
				A_SMSSTOMPSMOKE(s, 13, MT_SMALLBUBBLE)
				A_SMSSTOMPQUAKE(s, 45, 15, 4*FRACUNIT, 0, 0, 0, 0, 0, MT_SMALLBUBBLE)
				A_SMSSTOMPQUAKE(s, 25, 15, 4*FRACUNIT, 0, 0, 0, 0, 0, MT_MEDIUMBUBBLE)
				
			else
				S_StartSound(s, sfx_s3k77) //Make dust clouds spawn around SMS when he hits the floor.
				A_SMSSTOMPSMOKE(s, 8, MT_SMMK)
				A_SMSquake(s, 7*FRACUNIT, 2, 700, true)
				P_RadiusAttack(s, s, 64*FRACUNIT)
			end
		end
		s.SMS_stompmom = 0
		return true --
	end
end)

--SMSstomp is the SMS stomp, for stomping as SMS.
rawset(_G, "SMS_stomp", function(s, p)
		--Instantly cancel...
		if not ((p.pflags & PF_JUMPED) or p.sms_didairboost or p.hometrickanim)
			return false --
		end
		--Hold on, we have a Haymaker target! Check controls if "easyhaymaker" isn't active.
		if s.smscmphret and not p.smseasyhaymaker
			if (p.sms_boostlevel >= 64)
				if (p.cmd.forwardmove < -15) --Hold straight back, and don't tilt to the side!
				and not (abs(p.cmd.sidemove) > 9)
				and (p.smsmeleebutton < 17)
				and not p.ringburstfly --Doesn't apply during ringburst.
					return false --
				end
			end			
		end
		if not (SMS_Locked(s, p, true, true) and not p.ringburstfly)
		and not p.sms_afterstompthok
		and not (p.pflags & (PF_SLIDING|PF_STASIS)) --No stomping waterslides.
		and not s.smsstompcd --Battlemod cooldown.
		and not (p.panim == PA_PAIN) 
		and not (p.panim == PA_RIDE)
		and not (s.eflags & MFE_GOOWATER) //This stops swimming in goo water
		and not s.sms_juststomped --Minor 6 frame cooldown.(5 in practice)
		and not p.smsblastboost
		and not p.smsatomicdivefail
		
			--That's not right...don't do it!
			if (p.sms_jumphold <= 1)
			and (p.pflags & PF_STARTJUMP)
			and (p.pflags & PF_JUMPED)
			and not (p.pflags & (PF_THOKKED|PF_STARTDASH|PF_NOJUMPDAMAGE|PF_SLIDING))
			and (s.momz*P_MobjFlip(s) > 8*s.scale) and (s.momz*P_MobjFlip(s) < 15*s.scale)			
			and (not (s.eflags & MFE_VERTICALFLIP) and (s.z > s.floorz-(s.momz+80*s.scale)) or (s.eflags & MFE_VERTICALFLIP) )
			and (p.cmd.buttons & BT_JUMP)
			
				s.stompnegatedinput = true --Buffer this for the dodge!
				p.smsmeleebutton = 2
				if SMS_DodgeInput(s, p) --Trying to do a Swift Dodge instead?
					SMS_WallJumpDelete(s, true)
					s.SMS_WJpenalty = 0
					SMS_SwiftDodge(s, p)
					s.eflags = $ & ~MFE_JUSTHITFLOOR
				end
				return --
			end
			
			SMS_WallJumpDelete(s, true) --Won't be needing this.
			if not (s.eflags & MFE_SPRUNG)
				s.SMS_WJpenalty = 75
			end
			p.sms_stompanim = $+1
			p.secondjump = 0
			p.pflags = $ & ~(PF_JUMPED|PF_THOKKED|PF_SPINNING|PF_STARTDASH|PF_NOJUMPDAMAGE|PF_STARTJUMP)
			p.hometrickanim = false	
			p.sms_afterstompthok = false
			p.sms_jumphold = 55
			SMS_WallBustPower(s,p)
			
			--DETERMINE STOMP POWER!
			local STOMPPOWER = 30 --Default
			
			if p.powers[pw_sneakers] and G_PlatformGametype() and not s.battlemodsms
			or (s.scale >= 2*FRACUNIT)
				STOMPPOWER = 60 --Ridiculous buff.
			end
			
			if (s.eflags & MFE_UNDERWATER)
				STOMPPOWER = $-16 --Stomp debuff.
				S_StartSound(s, sfx_splash)
				local ghs = P_SpawnMobjFromMobj(s, 0, 0, s.height/4, MT_MEDIUMBUBBLE)
				if ghs and ghs.valid
					P_InstaThrust(ghs, s.angle+ANGLE_90, 5*s.scale)
				end
				local ghs = P_SpawnMobjFromMobj(s, 0, 0, -4*s.scale, MT_SMALLBUBBLE)
				if ghs and ghs.valid
					P_InstaThrust(ghs, s.angle-ANGLE_90, 5*s.scale)
				end
			else
				S_StartSound(s, sfx_stst)
				s.stompanimburst = 16
			end
			--Now stomp!
			if (s.momz*P_MobjFlip(s) > -(STOMPPOWER-(STOMPPOWER/3))*s.scale ) --Set minimum speed!
				P_SetObjectMomZ(s, -STOMPPOWER*FRACUNIT)
			else
				P_SetObjectMomZ(s, -(STOMPPOWER/2)*FRACUNIT, true) --Add a minor burst of speed instead!
			end
			--Stop all momentum from a ringburst!
			if p.sms_ringburstfly 
				p.sms_ringburstfly = 0
				s.momx = 0 s.momy = 0
			end

			--Set state!
			if P_IsObjectOnGround(s)
				s.state = S_PLAY_FALL
			else
				s.SMS_lastz = s.z
				s.state = S_SMS_STOMP1
				s.spritexscale = FRACUNIT-9500
				s.spriteyscale = FRACUNIT+15500
				s.spriteyoffset = -13*FRACUNIT
				s.SMS_squashtable = {timer = 5, Xspeed = 2900, Yspeed = 4000, air = true}
				--Initial GFX for the stomp.
				if not SMS_Graphix(s)
					if not s.SMS_DLtrail				
						s.SMS_lastz = s.z-(s.momz*3/2)+(s.height*20/9)
						s.SMS_DLtrail = {timer = 2, color = SKINCOLOR_FLAME, colorized = true, momx = s.momx*3, momy = s.momy*3, 
						fuse = 4, spritexscale = FRACUNIT+4444, toscale = 1, spriteyoffset = -36*FRACUNIT}
						SMS_ContinuousTrail(s, s.SMS_DLtrail)
						s.SMS_lastz = s.z
					end	
					local ghs
					for i = 2,4,2
						ghs = SMS_FlatWave(s, SKINCOLOR_SILVER, 11, 2, 0)
						ghs.frame = Q
						ghs.spritexoffset = 127*FRACUNIT
						ghs.spriteyoffset = 129*FRACUNIT
						ghs.spritexscale = $/(5+i)
						ghs.spriteyscale = $/(5+i)					
						ghs.tracer = s
						ghs.sttspeed = nil
						ghs.sttfade = 11
						ghs.GSoffsetX = -4*s.scale
						ghs.GSoffsetY = -4*s.scale
						ghs.renderflags = RF_SEMIBRIGHT|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS|RF_NOSPLATBILLBOARD
						ghs.destscale = 999*FRACUNIT
						ghs.GSoffsetang = s.angle
						ghs.sttroll = ANG1*(i*11)
						if i == 2 
							ghs.GSoffsetZ = 19*s.scale
							ghs.GSshiftoffsetZ = 106
						else
							ghs.GSoffsetZ = -11*s.scale
						end					
					end
				else
					SMSRSpawnGhost(s, 14)
				end			
			end
			
			s.sms_stompspin = nil
			s.hmsspinpass = nil
			p.smsmeleebutton = 850
			
			return true --
		end
end)


--Stomping! Both the landing and the falling process!
rawset(_G, "SMS_Stomping", function(s, p)
	if (p.panim == PA_PAIN)
	or p.homing
	or (p.pflags & (PF_SLIDING|PF_STARTDASH))
	or not s.health
		p.sms_stompanim = 0
		p.sms_afterstompthok = false
		if not p.homing
			s.SMS_stompmom = 0
		end
		return false --
	end	
	
	p.charflags = $ & ~SF_RUNONWATER 
	
			--Fiery stomp GFX. SMS uses the timer too at times.
			if s.stompanimburst
				if (s.stompanimburst % 2 == 0)
				and not P_IsObjectOnGround(s)
				and not (s.eflags & MFE_UNDERWATER)
					local ghs = P_SpawnMobjFromMobj(s,s.momx,s.momy,(s.stompanimburst*s.scale)*P_MobjFlip(s), MT_HMSSTOMPAURA)
					ghs.renderflags = $|RF_FULLBRIGHT
					ghs.target = s
					if (s.stompanimburst > 10)
						ghs.scale = (s.scale*2)*s.stompanimburst/8
						if (s.stompanimburst > 14)
							ghs.frame = SMSLocalRNG(5,7,s)|TR_TRANS40
							ghs.color = SKINCOLOR_FLAME
							ghs.z = $+ ( (3+s.stompanimburst) *s.scale*P_MobjFlip(s) )
						end
					else
						ghs.scale = (s.scale*2)*10/8
						if (s.stompanimburst < 6)
							ghs.frame = SMSLocalRNG(5,7,s)|TR_TRANS70
							ghs.color = SKINCOLOR_EMERALD
						else
							ghs.frame = SMSLocalRNG(5,7,s)|TR_TRANS60
							ghs.color = s.color	
						end
					end
					
					ghs.ghostaura = P_SpawnGhostMobj(ghs)
					ghs.ghostaura.destscale = ghs.ghostaura.scalespeed+1
					
					if not (s.z > s.floorz+60*s.scale)
						ghs.ghostaura.tics = 6
					end
					ghs.ghostaura.momz = s.momz*-1/2
					ghs.blendmode = AST_ADD
				end
				s.stompanimburst = $-1
			end
			
			--Currently going down through the air!
			if not (s.eflags & MFE_JUSTHITFLOOR)
			
				s.SMS_WJpenalty = 75 --Fairly big walljump penalty, but it does negate even higher penalties.
				
				if not ( (s.state == S_SMS_STOMP1) or (s.state == S_SMS_STOMP2) or (p.pflags & PF_JUMPED) )
				or (s.eflags & MFE_GOOWATER)
					if not ((p.panim == PA_FALL) and (p.pflags & PF_THOKKED))
						p.sms_stompanim = 0
						p.sms_afterstompthok = false
						return --
					end
				elseif (s.eflags & MFE_UNDERWATER)
				and p.lddiscount == 2
				and not P_IsObjectOnGround(s)
					local ghs = P_SpawnMobjFromMobj(s, FixedMul(-20*s.scale, cos(s.angle+ANGLE_90)), FixedMul(-20*s.scale, sin(s.angle+ANGLE_90)), 2*s.scale, MT_SMALLBUBBLE)
					ghs = P_SpawnMobjFromMobj(s, 0,0,s.momz, MT_MEDIUMBUBBLE)
					ghs = P_SpawnMobjFromMobj(s, FixedMul(20*s.scale, cos(s.angle+ANGLE_90)), FixedMul(20*s.scale, sin(s.angle+ANGLE_90)), 2*s.scale, MT_SMALLBUBBLE)
				end
				
				--Sneakers GFX!
				
				--
				if (s.eflags & MFE_VERTICALFLIP) and (abs(s.z-s.ceilingz) < 99*s.scale)
				or (abs(s.z-s.floorz) < 99*s.scale) and not (s.eflags & MFE_VERTICALFLIP)
					if p.powers[pw_sneakers] and G_PlatformGametype() and not s.battlemodsms
						P_NukeEnemies(s, s, 128*s.scale)
					else
						P_RadiusAttack(s, s, 114*s.scale)
					end
				end
				
				//You're going up while stomping? ...huh? Well, cancel that ticket then
				if (s.momz*P_MobjFlip(s) > s.scale/3)
				and not (p.sms_wallstat >= 60) //Disables this from happening during walljumps.
				and not p.powers[pw_nocontrol]
					p.sms_stompanim = 0
					if not p.homing
						s.SMS_stompmom = 0
					end
					p.sms_afterstompthok = false
					if not p.powers[pw_carry]
						p.pflags = $|PF_JUMPED
					end
					if p.scoreadd --don't think so!
						P_SetObjectMomZ(s, -12*FRACUNIT)
						p.scoreadd = 0
					end
					return --
				else
					s.SMS_stompmom = s.momz --Our stomp momentum, update every frame!
				end
				--Perform a rapid stomp?
				if (p.cmd.buttons & BT_CUSTOM2)
				and not p.sms_afterstompthok 
				and (p.smsmeleebutton <= 1)
				and not (s.eflags & MFE_UNDERWATER)
					p.smsmeleebutton2 = $+8
					s.stompanimburst = 16
					if not (s.momz*P_MobjFlip(s) < 36*s.scale)
						P_SetObjectMomZ(s, -36*FRACUNIT)
					else
						P_SetObjectMomZ(s, -7*FRACUNIT, true)
					end
					--Play sfx.
					
					local SfX = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMS_THOK)
					SfX.flags2 = MF2_DONTDRAW SfX.fuse = 59
					local rVolume = min( 255, FixedInt(abs(s.momz))*4/3 )
					S_StartSoundAtVolume(SfX, sfx_stst, 124+(rVolume/4))
					S_StartSoundAtVolume(SfX, sfx_sarb, rVolume)
					S_StartSound(SfX, sfx_s23d)	
					
					s.hmsspinpass = p.drawangle --Used as our original angle.
					s.sms_stompspin = p.drawangle
					if (s.momz*P_MobjFlip(s) < -60*s.scale)
						A_SMSquake(s, max(3*FRACUNIT, min(88*FRACUNIT, abs(s.momz/3)) ), 3)
					end
					if not s.SMS_DLtrail
					and not SMS_Graphix(s)
						s.SMS_lastz = s.z-s.momz+s.height
						s.SMS_DLtrail = {timer = 3, color = SKINCOLOR_FLAME, colorized = true, momx = s.momx*2, momy = s.momy*2, 
						fuse = 5, spritexscale = FRACUNIT*2, scalespeed = FRACUNIT/3}
						SMS_ContinuousTrail(s, s.SMS_DLtrail)
						s.SMS_lastz = s.z
						
						local ghs
						for i = 2,4,2
							if i == 2 
								ghs = SMS_FlatWave(s, SKINCOLOR_SMS5R, 12, nil, 0)
								ghs.frame = I|FF_TRANS10
								ghs.spritexoffset = 21*FRACUNIT
								ghs.spriteyoffset = 23*FRACUNIT								
							else
								ghs = SMS_FlatWave(s, SKINCOLOR_SILVER, 16, 2, 0)
								ghs.frame = Q|FF_TRANS20
								ghs.spritexscale = $/6
								ghs.spriteyscale = $/6
								ghs.spritexoffset = 127*FRACUNIT
								ghs.spriteyoffset = 129*FRACUNIT							
							end
							ghs.tracer = s
							ghs.sttspeed = nil
							ghs.sttfade = 9
							ghs.GSoffsetX = s.scale
							ghs.GSoffsetY = s.scale
							ghs.GSoffsetZ = -9*s.scale					
							ghs.renderflags = RF_SEMIBRIGHT|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS|RF_NOSPLATBILLBOARD
							ghs.destscale = 999*FRACUNIT
							ghs.GSoffsetang = s.angle
							ghs.sttroll = ANG1*(16+(i*12))
						end						
					end
					if not s.SMS_squashtable
						s.spritexscale = FRACUNIT-8500
						s.spriteyscale = FRACUNIT+13500
						s.spriteyoffset = -13*FRACUNIT
						s.SMS_squashtable = {timer = 5, Xspeed = 3200, Yspeed = 4000, air = true}
						
						local ghs = SMS_LightFlash(s, SKINCOLOR_SMS5R, nil, 5, P|TR_TRANS60) 
						ghs.tracer = s
						ghs.spritexscale = max(5, $-9999)
						ghs.spriteyoffset = $-18*FRACUNIT
					end
					s.momx = FixedDiv(FixedMul($,17),18)
					s.momy = FixedDiv(FixedMul($,17),18)				
				end
				--Spinning animation!
				if s.stompanimburst and (s.stompanimburst > 6)
				and not (s.eflags & MFE_UNDERWATER)
					if s.sms_stompspin != nil
						local ghs = SMSRSpawnGhost(s, 8)
						if ghs and ghs.valid
							ghs.scale = s.scale
							ghs.destscale = 1
							ghs.angle = s.sms_stompspin+ANG20-(ANGLE_45)
							ghs.colorized = true
							ghs.blendmode = AST_ADD
							ghs.momz = $+(s.momz/3)
							ghs.renderflags = $|RF_PAPERSPRITE|RF_SEMIBRIGHT|RF_NOCOLORMAPS
							P_SetOrigin(ghs, ghs.x+FixedMul(16*s.scale, cos(ghs.angle)), ghs.y+FixedMul(16*s.scale, sin(ghs.angle)), ghs.z+(s.height/4))
						end
					end
					if (s.stompanimburst > 9) and (leveltime % 2)
					or (s.stompanimburst > 13) and s.sms_stompspin == nil		
						local ghs = SMS_FootSweepSlash(s, s.sms_stompspin and FF_TRANS50 or FF_TRANS60, SMSLocalRNG(0,16,s)*ANG1+leveltime*(ANG1*33))
						if ghs and ghs.valid
							ghs.color = SKINCOLOR_SILVER
							ghs.momz = $+(s.momz/3)
						end
					end
				else
					s.sms_stompspin = nil
				end				
				
			else --if (s.eflags & MFE_JUSTHITFLOOR) --AKA, we landed.		
				SMS_stompimpact(s, p)
			end
end)

//Check if you're Sonic or not.

function A_SMScompatibleskin(s, HUD)
	if s.valid and s.skin and (s.skin != "sms")
		if s.skin == s.CanGoSMS
			return true --
		end
		if ( (string.find(s.skin, "sonic") and not string.find(s.skin, "metal") and not string.find(s.skin, "mecha") and not string.find(s.skin, "hard")) ) 
		or (s.skin == "greeneyes") or (s.skin == "sunky") or (s.skin == "werehog") or (s.skin == "earless") or (s.skin == "earlessredo")
		or (s.skin == "3dson") or (s.skin == "handdrawn") or (s.skin == "pointy") or (s.skin == "sonik") or (s.skin == "bandages")
			if not HUD
				s.CanGoSMS = s.skin
			end
			return true //Everyone is Sonic, and since everyone is Sonic, EVERYONE IS SMS
		end
	end
	if not HUD s.CanGoSMS = nil end
	return false
end

--Or "SMS_SpeedShoes" to be more sensible.
rawset(_G, "SMS_AllOutMode", function(s, p)
		  
	--Experience absolute anarchy when SMS goes All-Out after nabbing Speed Shoes!
	 
		if (p.speedspeedspeedspeedspeed or p.smsg2gfast)
		and not p.powers[pw_sneakers]
			P_RestoreMusic(p)
			COM_BufInsertText(p, "tunes -default")
			p.speedspeedspeedspeedspeed = 0
			p.smsg2gfast = false
		end
		if s.health
			if not p.smsg2gfast
				if (p.powers[pw_sneakers] > 600)
				and not p.exiting
					p.smsg2gfast = true
				end
			elseif p.smsg2gfast
				if not p.powers[pw_extralife]
				and not p.nohmstheme
				and not p.exiting
				and not p.sms_intoship
				COM_BufInsertText(p, "tunes MAPZYM")
			--	S_ChangeMusic("MAPZYM", false, p)
				end
			end
			if (p.powers[pw_sneakers] == 1) and not p.sms_intoship and not p.nohmstheme	
				COM_BufInsertText(p, "tunes -default")
			end
		
		elseif p.powers[pw_sneakers] --Yeah, not that strange you'd kill yourself in this madness.
			
			p.powers[pw_sneakers] = 0
			p.powers[pw_invulnerability] = 0
			p.speedspeedspeedspeedspeed = 0	
			P_RestoreMusic(p)
		end
			
		--Jumping pulldown after a speed shoes jump.
		if s.sneakerjcd
			if (s.sneakerjcd == 1) and (p.pflags & PF_JUMPED)
				if s.sneakjuw == 7 and (p.powers[pw_underwater] or (s.eflags & MFE_UNDERWATER)) 
					s.sneakjuw = 10 //Still underwater? Then keep your momentum.
				end
				if s.sneakerwc
					P_SetObjectMomZ(s, (-28*FRACUNIT)*10/s.sneakjuw, true)
				elseif ((p.pflags & PF_STARTJUMP) and (p.cmd.buttons & BT_JUMP))
					P_SetObjectMomZ(s, (-20*FRACUNIT)*10/s.sneakjuw, true)
				else
					P_SetObjectMomZ(s, (-13*FRACUNIT)*10/s.sneakjuw, true)
				end
				s.sneakerjumpgfx = false
				s.sneakerwc = false
			end
			if s.sneakerjcd
				s.sneakerjcd = $-1
				A_SMSSTOMPSMOKE(s, P_RandomRange(15, 30), MT_SPINDUST)
			end
		
			--Cancel pulldown!
			if P_IsObjectOnGround(s) or (p.sms_wallstat == 59) or (s.momz*P_MobjFlip(s) <= 0)
			or (s.state == S_PLAY_SPRING) or p.homing or p.powers[pw_carry]
				s.sneakerjcd = 0
				s.sneakerjumpgfx = false
				s.sneakerwc = false
			end
		
		elseif s.sneakerjumpgfx and not (p.pflags & PF_JUMPED)
			
			s.sneakerjumpgfx = false
			s.sneakerwc = false
		end
			
		if (p.powers[pw_sneakers] > 3)
		and s.health
		
			if not G_GametypeHasSpectators()
				if (p.pflags & PF_STARTDASH) and (p.dashspeed < p.maxdash)
				
					p.dashspeed = p.maxdash //Instant maxdash
					S_StartSound(s, sfx_spndsh)
					
				elseif ((p.pflags & PF_JUMPED) and not s.sneakerjumpgfx)
				
					if not (s.z > s.floorz+24*FRACUNIT)
						A_SMSSTOMPSMOKE(s, 24, MT_SPINDUST)
						SMSRSpawnGhost(s, 8)
						P_SetObjectMomZ(s, 18*FRACUNIT, true)
						s.sneakerjcd = 4
						s.sneakerwc = false
					end
					if (s.eflags & MFE_UNDERWATER) or p.powers[pw_underwater]
						s.sneakjuw = 7
					else
						s.sneakjuw = 10
					end
					s.sneakerjumpgfx = true
				end
			end
		
			--Cancel shoes?
			if p.exiting 
			  	p.powers[pw_sneakers] = 2
			  	p.powers[pw_invulnerability] = 2	
				
			else --Jumping behaviour and stats change in your all-out state!
			   
				p.jumpfactor = (SMSSHIELD(p,SH_WHIRLWIND)) and 87250 or 72535
				
				p.accelstart = 140
				p.thrustfactor = 10
				
				if not p.speed --You can stop at this point.
				or p.mslightspeedattack
				or p.sms_comethaymaker
					return --
				end
				if not p.powers[pw_carry]  --It's way easier to reach boostmode.
					SMSRSpawnGhost(s)
					p.sms_boostlevel = max($,32)
				end
				--Fast
				if (p.speed > 110*s.scale)
				local RadialBurst = 220*FRACUNIT
				s.boostpass = true
					if (p.speed > 140*s.scale) --Faster yet.
						local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSGROUNDBOOST)
						ghs.tracer = s
						if (p.speed > 180*s.scale)
							if P_IsObjectOnGround(s)
								s.hmsrunquake = 1
							end
							A_SMSquake(s, 12*FRACUNIT, 1, 2500)
							RadialBurst = 320*FRACUNIT
						end
					end
					P_RadiusAttack(s, s, RadialBurst)
				else
					P_RadiusAttack(s, s, 137*s.scale)
				end
			end
		end
end)

--Instantly do an airboost.
rawset(_G, "SMS_DoAirBoost", function(s, p)
	if p.sms_instantairboost
	or not (p.cmd.buttons & BT_CUSTOM1) --Oh, you're using the jump method!
		p.sms_autoboosthold = 2
	end
	
	local ZMOM = 0 --ZMOM to set player to.
	
	if s.SMS_WallJumpTable and (s.SMS_WallJumpTable.timer > 14)  --Spamming, are we?
		local SP = abs(s.SMS_WallJumpTable.timer-15)
		if (p.sms_boostlevel < 65) --Lose boostmode rather than building it at this point.
			p.sms_boostlevel = max($-2, 0)
		end
		ZMOM = -SP*(FRACUNIT*2)
	else
		if (p.sms_boostlevel < 45) --Nerfed due to how much more spammable this has gotten.
			p.sms_boostlevel = $+10
		else
			p.sms_boostlevel = $+20 --Final stretch has a bigger gain, though.
		end
	end
	
	P_SetObjectMomZ(s, ZMOM)
	
	SMS_WallJumpDelete(s, true)
	
	SMSRSpawnGhost(s, 15)
	p.pflags = $ & ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_STARTJUMP|PF_STARTDASH)
	p.sms_tapnothold = 0
	p.sms_readyairboost = 0
	p.homing = 0
	s.state = S_SMS_AIRBOOST
	
	if (p.cmd.sidemove < -9)
		s.hmsrotateright = nil
	elseif (p.cmd.sidemove > 9)
		s.hmsrotateright = true
	elseif s.hmsrotateright
		s.hmsrotateright = nil
	else
		s.hmsrotateright = true
	end
		
	if not (s.eflags & MFE_UNDERWATER)
		p.sms_stompanim = 0 
		s.airboostimmune = 7
		if not ((p.pflags & PF_ANALOGMODE) and (abs(p.cmd.sidemove) > 25)) //Don't ruin the illusion, you fool.
			s.airboostroll = 9
		end
		A_SMSquake(s, 32*FRACUNIT, 4)
	end
	
	p.sms_didairboost = 10
	p.hometrickanim = false
	SMS_WallBustPower(s,p)
	
	if (p.pflags & PF_DIRECTIONCHAR) and not (p.pflags & PF_ANALOGMODE)
		s.forcesmsangletimer = 8
		s.forcesmsangle = s.angle
	end
	if not (s.eflags & MFE_UNDERWATER)
		
		--Generic airboost GFX!
		
		local ghs = P_SpawnMobjFromMobj(s, 0, 0, 20*s.scale*P_MobjFlip(s), MT_SMSAIRBOOST)
		ghs.tracer = s
		ghs.blendmode = AST_ADD
		if P_MobjFlip(s) == 1
			ghs.z = $+11*s.scale
		end		
		S_StartSound(s, sfx_sarb)
		
		//Airboost voice RNG. Different from Blinddash version.
		if not s.smsabsfx
		local sfxrng = SMSLocalRNG(1,20,s)
			s.smsabsfx = 35
			if sfxrng < 11
				S_StartSound(s, sfx_smsha4)
			elseif sfxrng < 16
				S_StartSound(s, sfx_smsha5)
			elseif sfxrng < 18 and not (s.lastbdsfx and s.lastbdsfx > 15 and s.lastbdsfx < 18)
				S_StartSound(s,sfx_smsha2)
			elseif sfxrng < 20
				S_StartSound(s,sfx_smsha1)
			else
				S_StartSound(s,sfx_smsha2)
			end
			s.lastbdsfx = sfxrng --We can just reuse the blinddash value here.
		end
		
	else --Blub.
		P_SpawnMobjFromMobj(s, 0, 0, 0, MT_MEDIUMBUBBLE)
		P_SpawnMobjFromMobj(s, 0, 0, 10*FRACUNIT, MT_MEDIUMBUBBLE)
		P_SpawnMobjFromMobj(s, 5*FRACUNIT, 5*FRACUNIT, 4*FRACUNIT, MT_SMALLBUBBLE)
		S_StartSound(s, sfx_s3k47)
	end
	
	--Scale with your momentum a bit. Rapid airboosting won't help, though.
	local targetspeed =  (p.powers[pw_sneakers] and not G_GametypeHasSpectators()) and 195*s.scale 
	or (s.eflags & MFE_UNDERWATER) and 21*s.scale
	or 100*s.scale
	
		if (p.speed < 33*s.scale) --Start up a bit slower if you're slow.
		and (s.momz*P_MobjFlip(s) > 3*s.scale) and (p.pflags & PF_JUMPED)
		and not (p.pflags & (PF_THOKKED|PF_STARTDASH|PF_SLIDING))
		and (targetspeed != 21*s.scale)
		and not p.sms_instantairboost		
			targetspeed = FixedDiv(FixedMul($, 50), 55)
		else
			if (targetspeed == 21*s.scale)
				if (p.speed-20*FRACUNIT > targetspeed)
					targetspeed = min(21*s.scale, p.speed-(20*FRACUNIT))
					if targetspeed == 21*s.scale
						targetspeed = 21*s.scale+(p.speed/3)
					end
				end
			elseif (p.speed-20*FRACUNIT > targetspeed) --Gain more thrust if you're faster.
				targetspeed = max(targetspeed, min(10*FRACUNIT, p.speed-(20*FRACUNIT)) )
			end
		end
	
	if p.powers[pw_sneakers] and G_PlatformGametype() and not s.battlemodsms						
		P_InstaThrust(s, s.angle, targetspeed) //Insanely fast speedshoes airboost!		
		if SMS_Graphix(s) != 2
			if s.SMS_DLtrail == nil
				s.SMS_DLtrail = {timer = 2, colorized = true,
				COLORT_trail = {SKINCOLOR_MYSTICAL5, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_RED, SKINCOLOR_EMERALD, SKINCOLOR_GREEN, SKINCOLOR_MYSTICAL2},
				COLORT_trailmax = 8} 
			end
			if SMS_Graphix(s) != 1
				local ghs = SMS_FollowCircle(s, SKINCOLOR_RED, 2)
				ghs.scalespeed = $*3 ghs.fuse = $/3
				
				ghs = HMS_BigBangVortex(s, 14, nil, nil, T|FF_TRANS10, SKINCOLOR_SMS5R)
				ghs.tracer = s
				
				ghs = HMS_GigaJetBolt(s, FRACUNIT*2/3, 14)
				ghs.tracer = s
				ghs.frame = $|FF_PAPERSPRITE
				ghs.color = SKINCOLOR_SUPERSKY5
				ghs.GSoffsetX = 46*s.scale
				ghs.GSoffsetY = 46*s.scale
				ghs.GSoffsetZ = 0
				ghs.colorized = true
				ghs.sttfade = 14
				ghs.dispoffset = -2
				ghs.destscale = 999*FRACUNIT
				
				SMS_QuickDustGFX(s, SKINCOLOR_BONE, AST_ADD)

				local ths = SMS_ElectricBody(s, 15, SKINCOLOR_RED)	
				ths.spriteyscale = $*2
				ths.spritexscale = $*2
				ths.mirrored = true
				ths.spriteyoffset = $-22*FRACUNIT
				ths.frame = $ & ~FF_TRANSMASK
				ths.blendmode = AST_TRANSLUCENT
				ths.GSoffsetY = 22*s.scale
				ths.GSoffsetX = 22*s.scale
				ths.GSoffsetZ = 0					
			end
		end
	elseif (s.eflags & MFE_UNDERWATER)
	and not (p.powers[pw_sneakers] and (G_PlatformGametype() and not s.battlemodsms))  //If no runshoes, then...
		P_InstaThrust(s, s.angle, targetspeed) //Crappy airboost in water.	
		if not s.SMS_squashtable
			s.spritexscale = FRACUNIT-12900
			s.spriteyscale = FRACUNIT+14500
			s.SMS_squashtable = {timer = 4}
		end
	elseif G_GametypeHasSpectators()
		if p.rings					
			P_GivePlayerRings(p, -1) //If not underwater and no runshoes in match, take a ring per airboost.
			P_InstaThrust(s, s.angle, targetspeed) //Normal free airboost in common gamemodes.
		else
			P_InstaThrust(s, s.angle, 50*s.scale) //Airboost for match players with no rings.
		end
	else					
		P_InstaThrust(s, s.angle, targetspeed) //Normal free airboost in common gamemodes.
	end
	if (targetspeed > 77*s.scale)
		SMS_DustLayers(s, true)
		if not (p.sms_boostlevel >= 65 and p.sms_boostlevel < 95)
		and not p.powers[pw_sneakers]
			SMS_BoostStart(s, "airboost")
		end
	end
end)

--Locked in a common action you probably shouldn't be able to do an action from?
rawset(_G, "SMS_Locked", function(s, p, extra, check)
	if s.SMS_locked --Already determined lock.
		return true
	end	
	
	 --Main arguments that apply to both forms.
	if p.sms_comethaymaker and not p.hypermysticsonic
	or p.powers[pw_carry]
	or p.gotflag
	or p.exiting
	or (p.pflags & PF_GAMETYPEOVER) and (gametype != GT_COOP)
	or p.sms_intoship
	or p.knucklewaving --SMScheats variable.
		if not check
			s.SMS_locked = true
		end
		return true
	end
	
	--Arguments only applying to HMS.
	if p.hypermysticsonic --Arguments only applying to HMS.
		if (s.state == S_HMSNOVAFIRE2) or (s.state == S_HMSNOVAFIRE1)
		or (s.hmsgalaxystorm > 110)
		or (p.hmswarpcooldown > 3)
		or s.starthmscombo
		or (s.state == S_HMSBLACKHOLE)
		or p.hms_instamove and ((s.state == S_HMS_INSTANTMOVE2) or (s.state == S_HMS_INSTANTMOVE1))
			if not check
				s.SMS_locked = true
			end
			return true
		end
	elseif extra --Arguments only applying to SMS. Optional.
		if (p.footsweep02 or p.footsweep03)
		or p.sms_dodging and (p.sms_dodging > 6)
		or p.sms_ringburstfly
		or p.homing
		or p.smsrdash
		or p.lightdashstart
		or p.mslightspeedattack
		or not s.health
			if not check
				s.SMS_locked = true
			end
			return true
		end
	end
end)

--Emulate the old boost from SMS blast, touched up visually a bit. The SMSblastboost"
rawset(_G, "SMSblast_boost", function(s, p)		
		if G_GametypeHasSpectators()
			if not p.sblastrd
				P_GivePlayerRings(p, -1)
				p.sblastrd = 15
			else
				p.sblastrd = $-1
			end
		end
		
		--Initial boost effect!
		if not s.smsblastboost1
			if G_GametypeHasSpectators()
				P_GivePlayerRings(p, -1) --Initial ring cost.
				p.sblastrd = 18
			end
			if SMS_Graphix(s) == 0
				local ghs = SMS_SmartSplat(s, 23, s, s.radius)
				if ghs and ghs.valid
					ghs.sprite = SPR_SMX2
					ghs.frame = (Z+3)|TR_TRANS10
					ghs.blendmode = AST_ADD
					--Second glowing overlay to give the illusion of a red-hot skid mark turning white.
					local ghs = SMS_SmartSplat(s, 11, s, s.radius)
					if ghs and ghs.valid
						ghs.sprite = SPR_SMX2
						ghs.frame = (Z+3)
						ghs.colorized = true
						ghs.color = SKINCOLOR_CRIMSON
						ghs.blendmode = AST_ADD
					end			
				end
				SMS_ElectricRecoil(s) --Some electricity cause why not.
			end
			s.spritexscale = FRACUNIT+21000
			s.spriteyscale = FRACUNIT-11250	
			s.SMS_squashtable = {timer = 4, ground = true}				
			S_StartSound(s, sfx_smsha1)
			S_StartSound(s, sfx_zoom)
			s.hudboost = 1
			p.speed = 160*FRACUNIT
			s.boostpass = true
			local ghs = P_SpawnMobjFromMobj(s, 0,0,-s.height/3, MT_RADIALBOOST)
			ghs.target = s
			if not (p.pflags & PF_SPINNING) and (s.state != S_PLAY_RUN)
				s.state = S_PLAY_RUN
			end
		end
		
		local ghs = P_SpawnGhostMobj(s)
		ghs.tics = 2
		ghs.rollangle = s.rollangle
		ghs.blendmode = AST_ADD
		
		if (p.pflags & PF_DIRECTIONCHAR) and not (p.pflags & PF_ANALOGMODE)
			p.drawangle = s.angle
		end
		
		s.smsblastboost1 = true

		if p.hypermysticsonic
			P_InstaThrust(s, s.angle, 145*s.scale)
			p.pflags = $ & ~PF_SPINNING
		else
			P_InstaThrust(s, s.angle, 104*s.scale)
		end
		if (p.panim == PA_RUN) and not p.hypermysticsonic
			s.state = S_SMS_BOOST01
		end
		if not p.hypermysticsonic
			p.normalspeed = 45*FRACUNIT
		end
		local rng = P_RandomRange(0,2)
		for i = -1,1,2
			local ghs = P_SpawnMobjFromMobj(s,
			FixedMul(23*s.scale*i, cos(s.angle+ANGLE_90)),
			FixedMul(23*s.scale*i, sin(s.angle+ANGLE_90)),
			0, MT_SPINDUST)
			if ghs and ghs.valid
				ghs.frame = rng
			end
		end

		
		return true --Done!
end)

--Toss a dual cross flame or a small flame!
rawset(_G, "SMS_TossSmallFlame", function(s, p)
	if (p.cmd.buttons & BT_ATTACK) and (p.sms_boostlevel > 64)
		return --Use the instant charged flame instead.
	end
	--Autoaim when SMS has his views straight. Because mouse users have enough advantages as is.
	local ENEMY = nil
	if (abs(p.aiming) < 50*FRACUNIT)
		ENEMY = P_LookForEnemies(p, true, true)
		if not (ENEMY and ENEMY.valid)
		or (ENEMY.flags & MF_SPRING) and not (ENEMY.flags & MF_ENEMY)
			ENEMY = nil
		end
	end
	
	
					local ghs
						if (p.cmd.buttons & BT_ATTACK) and not p.smsblastboost
						if ENEMY != nil
							ghs = P_SpawnMissile(s, ENEMY, MT_SMSFIREBLAST)
						else
							ghs = P_SpawnPlayerMissile(s, MT_SMSFIREBLAST)
						end
						
							if ghs
								if s.color == SKINCOLOR_GOLD or SMSisyellow(s)
									ghs.color = SKINCOLOR_SKY
								else
									ghs.color = s.color
								end
								if not (p.mxscap > 1)
									if not (p.powers[pw_shield] & SH_FIREFLOWER) //Infinite fire with fireflowers!
									P_GivePlayerRings(p, -1) 
									end
								end
								if (s.eflags & MF2_TWOD) or twodlevel
									ghs.smfltwod = true
								end
								ghs.flags2 = $|MF2_EXPLOSION --Initial knockback increase!
								ghs.twoshot = true
								P_MoveOrigin(ghs, s.x+FixedMul(8*FRACUNIT, cos(s.angle+ANGLE_90)),s.y+FixedMul(8*FRACUNIT, sin(s.angle+ANGLE_90)), ghs.z)
							end
							if ENEMY != nil
								ghs = P_SpawnMissile(s, ENEMY, MT_SMSFIREBLAST)	
							else
								ghs = P_SpawnPlayerMissile(s, MT_SMSFIREBLAST)
							end
								if ghs
									if s.color == SKINCOLOR_GOLD or SMSisyellow(s)
									ghs.color = SKINCOLOR_SKY
									else
									ghs.color = s.color
									end
									if (s.eflags & MF2_TWOD) or twodlevel
									ghs.smfltwod = true
									end
									ghs.twoshot2 = true
									ghs.twoshot = true
									P_MoveOrigin(ghs, s.x-FixedMul(8*FRACUNIT, cos(s.angle+ANGLE_90)),s.y-FixedMul(8*FRACUNIT, sin(s.angle+ANGLE_90)), ghs.z)
								end
							
						else
							if ENEMY != nil
								ghs = P_SpawnMissile(s, ENEMY, MT_SMSFIREBLAST)	
							else
								ghs = P_SpawnPlayerMissile(s, MT_SMSFIREBLAST)
							end
						end
						if ghs and ghs.valid
							if s.color == SKINCOLOR_GOLD or SMSisyellow(s)
								ghs.color = SKINCOLOR_SKY
							else
								ghs.color = s.color
							end
						end
						if (p.cmd.buttons & BT_ATTACK)
							p.sms_flamedelay = 8
						else
							p.sms_flamedelay = 9 //This sets our weapon delay timer.
						end
						if not (p.mxscap > 1)
							if (p.powers[pw_shield] & SH_FIREFLOWER)
								S_StartSound(s, sfx_mario7)
							else
								P_GivePlayerRings(p, -1)
							end
						end
						if (p.pflags & (PF_DIRECTIONCHAR|PF_ANALOGMODE))
							s.forcesmsangle = s.angle
							s.forcesmsangletimer = 8
						end
						if s.battlemodsms //Attacks have more deliberate openings in battlemod. SMS can't just spam flames when nobody else has projectiles
						p.sms_flamedelay = $*3
							if not p.powers[pw_carry] and (((s.state == S_SMS_ADVFALL1) or (s.state == S_SMS_ADVFALL2)) or not (p.panim == PA_ETC))
								P_SpawnGhostMobj(s)
								S_StartSound(s, sfx_s3k47)
								s.momx = $/2
								s.momy = $/2
								p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH
								p.dashspeed = 0
								p.powers[pw_nocontrol] = 8
								s.state = S_SMSHUGEFLAMETOSS
								
								s.spritexscale = FRACUNIT-8500
								s.spriteyscale = FRACUNIT+12000
								s.SMS_squashtable = {timer = 4, air = true}	
								
								SMS_DustLayers(s)
								if (p.pflags & PF_DIRECTIONCHAR)
									s.forcesmsangletimer = 9
									s.forcesmsangle = s.angle
								end
								P_Thrust(s, s.angle, -6*FRACUNIT)
								P_SetObjectMomZ(s, 3*FRACUNIT, true)
							end
						end
						if s.state == S_PLAY_WAIT
							s.state = S_PLAY_STND
						end				
						return true --
end)

--Tossing a HUGE fireball, the Diffusion Flame! TossHugeFlame". The MysticFlame is in the MobjThinker, btw.
rawset(_G, "SMS_TossingHugeFlame", function(s, p)
	if p.hypermysticsonic
	or not s.health
		s.dfclosest = nil
		p.sms_aerialfirerestrict = true
		p.sms_hugeflametoss = 0
		return --
	elseif P_PlayerInPain(p) --You got knocked out of it.
		s.dfclosest = nil
		p.sms_hugeflametoss = 0
		s.state = S_PLAY_PAIN
		return --
	end	
	
	p.camerascale =  FRACUNIT*5/4
	s.bhlcamtimer = 2
	
	p.powers[pw_nocontrol] = max($,2)

	if (p.sms_hugeflametoss >= 2)
		if ((p.cmd.buttons & BT_ATTACK) and (p.sms_flamebuttonhold == 1))
			s.quickdfthrow = true --Tap the attack button again to instantly throw, but without auto-aim.
		end
		if (p.sms_hugeflametoss == 18)
			S_StartSound(s, sfx_s3k6d)
			SMS_SparkleFlash(s, 20*s.scale, 24*s.scale)
		else
			if (p.sms_hugeflametoss == 5) 
				if (p.sms_boostlevel > 64)
					p.sms_boostlevel = 0
					A_SMSquake(s, 70*FRACUNIT, 2, 99)
				end
			end
			if (p.sms_hugeflametoss % 7 == 0)
				SMS_SparkleFlash(s, 16*s.scale, 1)
			end			
		end	
		
		s.momz = s.momz*10/15
		s.momx = s.momx*10/11
		s.momy = s.momy*10/11
		p.sms_flamedelay = 18
		p.pflags = $ & ~(PF_JUMPED|PF_SPINNING|PF_STARTDASH|PF_SLIDING)

		if P_IsObjectOnGround(s) --Stay off the darn ground!
			if (p.sms_hugeflametoss <= 5) or s.quickdfthrow
				P_SetObjectMomZ(s, 10*FRACUNIT)
			else
				P_SetObjectMomZ(s, 26*FRACUNIT)
			end
			S_StartSound(s, sfx_jump)
			s.state = S_PLAY_SPRING
			SMSRSpawnGhost(s, 8, true)
			s.spritexscale = FRACUNIT-15100
			s.spriteyscale = FRACUNIT+15500
			s.spriteyoffset = 10*FRACUNIT
			s.SMS_squashtable = {timer = 5, Xspeed = 4000, Yspeed = 4000}			
		else
			if (s.state != S_SMSGRINDPOSE1) and (s.state != S_SMSGRINDPOSE2)
				s.state = S_SMSGRINDPOSE1
			end
		end	
	
	elseif (p.sms_hugeflametoss == 1) or s.quickdfthrow --Diffusion Flame fire!
	
		S_StartSound(s, sfx_s3k4b)
		S_StartSound(s, sfx_s3k82)
		S_StartSound(s, sfx_fire)
		
		local ghs
		if not s.quickdfthrow
			A_SMSFindTarget(s, 7000*FRACUNIT, 25) //Let the player force manual aim if they want, with homing turned off. An advantage is that you can throw the flame quicker
			if (s.target and s.target.valid) and s.dfclosest
				ghs = P_SpawnPointMissile(s, s.target.x, s.target.y, s.target.z+(34*FRACUNIT*(P_MobjFlip(s.target))), MT_SMSFIREBLAST, s.x, s.y, s.z+(15*FRACUNIT*(P_MobjFlip(s))))
				s.angle = R_PointToAngle2(s.x, s.y, s.target.x, s.target.y)
			else
				ghs = P_SpawnPlayerMissile(s, MT_SMSFIREBLAST)
			end
		else
			ghs = P_SpawnPlayerMissile(s, MT_SMSFIREBLAST)
		end
		if ghs and ghs.valid
			if s.target and s.target.valid and s.target.health and not s.quickdfthrow
				ghs.smspointer = s.target
			end
			ghs.mysticflame = true
			ghs.flags2 = $|MF2_SUPERFIRE
			ghs.momx = ghs.momx*7/6
			ghs.momy = ghs.momy*7/6
			ghs.momz = ghs.momz*7/6
			if (s.color == SKINCOLOR_GOLD or SMSisyellow(s))
				ghs.color = SKINCOLOR_SKY
			else
				ghs.color = s.color
			end
		end
		if not (p.mxscap > 1)
			if (p.powers[pw_shield] & SH_FIREFLOWER)
				S_StartSound(s, sfx_mario7)
			else
				P_GivePlayerRings(p, -3) //Expensive!
				if s.battlemodsms
				P_GivePlayerRings(p, -2)
				end
			end
		end
		if (p.pflags & PF_DIRECTIONCHAR)
			s.forcesmsangletimer = 9
			s.forcesmsangle = s.angle
		end
								
		s.SMS_lastwallANG = nil --A fun "exploit" I totally didn't program in intentionally.
		s.SMS_lastwallNUM = nil
		s.SMS_WJpenalty = 0		
		s.dfclosest = nil
		p.sms_aerialfirerestrict = true
		s.state = S_SMSHUGEFLAMETOSS
		p.sms_flamebuttonhold = 2
		p.sms_hugeflametoss = 0
		S_StopSoundByID(s, sfx_s227)
		P_Thrust(s, s.angle, -6*s.scale)
		P_SetObjectMomZ(s, 6*FRACUNIT, true)
		A_SMSquake(s, 60*FRACUNIT, 5, 1000, true)
		p.sms_boostlevel = 0

		s.spritexscale = FRACUNIT-24500
		s.spriteyscale = FRACUNIT+28000
		s.SMS_squashtable = {timer = 7, air = true}	
		if not SMS_Graphix(s)
			SMS_DustLayers(s)
			local FLOORDIST = (s.eflags & MFE_VERTICALFLIP) and s.momz or abs(s.z-s.floorz)
			SMS_FlamingImpact(s, SKINCOLOR_FLAME, nil, FRACUNIT/2, A|FF_TRANS40, 26, nil, max(-FLOORDIST, -55*FRACUNIT+s.momz))
		end
		P_RadiusAttack(s, s, 356*s.scale, DMG_FIRE, false)
		local ghs = SMS_WindBlast(s)
		ghs.colorized = true
		ghs.color = SKINCOLOR_MYSTICAL5
		ghs.blendmode = AST_ADD
	
	end	
	return true --
end)

--The whole part of getting the Ringburstready"
rawset(_G, "SMS_RingBurstPrep", function(s, p)

		local BURSTMETHOD = 0
		
		if (p.sms_boostlevel >= 64)
		
		or p.sms_ringburst
		
		or (p.powers[pw_sneakers] > 2) and (G_PlatformGametype() and not s.battlemodsms)
		and (p.speed > 107*s.scale) and ((p.cmd.buttons & BT_CUSTOM1) or p.sms_autoboosthold) --Instant RingBurst with shoes	
			
			if (P_IsObjectOnGround(s) or p.sms_ringburst)
			and not (s.eflags & MFE_UNDERWATER)
		--	and not p.sms_ringburst
				p.sms_ringburstready = 3 --ready the ringburst!
			end
			
			if p.sms_ringburstready		
			and not p.hypermysticsonic
				
				local BoostState = false
				
				if P_IsObjectOnGround(s)
				or (s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04)
				or (p.panim == PA_RUN)
				or p.sms_ringburst
					BoostState = true
					if (p.cmd.buttons & BT_CUSTOM1) and p.sms_c1 == 1

					or (p.cmd.buttons & BT_TOSSFLAG) and not (p.sms_tossflaghold > 5)
		
					or (p.cmd.buttons & BT_WEAPONNEXT) and not (p.sms_weaponnexthold > 5)	
					
						if not s.sms_tapnothold2 and p.sms_c1 == 1
							s.sms_tapnothold2 = 10
						else
							s.ret4ringburst = true
							s.sms_tapnothold2 = 0
						end
						
					end					
				else
					s.sms_tapnothold2 = 0
					s.ret4ringburst = false
				end
				
				if p.skidtime
				and not (twodlevel or (s.flags2 & MF2_TWOD)) 

				or (twodlevel or (s.flags2 & MF2_TWOD))
				and (p.cmd.forwardmove < -10)
					if not p.sms_ringburst
						P_SpawnMobjFromMobj(s, 0,0,0, MT_EXPLODE)
						p.sms_ringburst = 43
						S_StartSound(s, sfx_s3k79)
						s.state = S_SMS_RINGBURSTSLIDE
						s.spritexscale = FRACUNIT+20000
						s.spriteyscale = FRACUNIT-20000
						s.SMS_squashtable = {timer = 5, Xspeed = 4000, Yspeed = 4000}
						if (twodlevel or (s.flags2 & MF2_TWOD))
							p.pflags = $ & ~PF_SPINNING  & ~PF_STARTDASH
							s.momx = 0 s.momy = 0
							p.powers[pw_nocontrol] = min($+4, 70)
						else
							P_InstaThrust(s, s.angle, 7*s.scale)
						end						
					end
					p.sms_ringburstfalltimer = 0
				end
				if (p.pflags & PF_DIRECTIONCHAR) --Force the correct angle.
					p.drawangle = s.angle
				end				
			end
		end
		if p.sms_ringburstfalltimer --Only active with RingBurstFly!
			p.sms_ringburstfalltimer = 0
		end
			
		if (p.sms_ringburst or s.ret4ringburst)
			
		or ( (p.pflags & PF_SPINNING) and not (p.pflags & PF_STARTDASH)
		and not ((s.momz*P_MobjFlip(s) < -s.scale) and (p.pflags & PF_JUMPED))		
		and ( (p.sms_boostlevel >= 65) or (p.powers[pw_sneakers] > 2) and (G_PlatformGametype() and not s.battlemodsms) )
		and not (s.eflags & MFE_UNDERWATER) and (p.speed > 100*s.scale) ) --Have boost engaged or be really fast with shoes.
			
				if (p.skidtime or (p.speed < s.scale/10))
				and P_IsObjectOnGround(s)
				and not (p.pflags & (PF_STARTDASH|PF_SPINNING))
					s.state = S_SMS_RINGBURSTSLIDE
				elseif (s.state == S_PLAY_WALK) and (p.pflags & PF_SPINNING)
					s.state = S_PLAY_ROLL
				end
				if (p.sms_ringburst % 2 == 0)
				or p.sms_ringburst == 1
					s.flags2 = $ & ~MF2_DONTDRAW //Game does this naturally, buuuut....
					local ghs = P_SpawnGhostMobj(s)
					ghs.blendmode = AST_ADD
					ghs.flags2 = $|MF2_SHADOW
					P_SpawnMobjFromMobj(s, 0,0,0, MT_SPARK)

				elseif not (p.sms_ringburst % 2 == 0)
					s.flags2 = $|MF2_DONTDRAW
				end
				
				--Final checks, ready to go?
				if not (p.pflags & PF_SLIDING)
				and not (s.eflags & MFE_UNDERWATER)
				
					if (p.cmd.buttons & BT_JUMP) and not (p.sms_jumphold > 23)
					and ((p.cmd.buttons & BT_CUSTOM2) or (p.cmd.forwardmove < -25))
					or s.ret4ringburst --In the charge state.
					
						P_DoJump(p, true)
						s.waitforforward = true
						s.ret4ringburst = false
						s.sms_runspinboost = 0
						p.sms_spinuc = 0
						p.weaponprevhold = 850
						p.sms_weaponnexthold = 850
						p.sms_tossflaghold = 850
						p.sms_c1 = 850
						s.SMS_WallBounce = nil
						s.sms_boostloss = 0
						p.sms_boostlevel = 0
						p.sms_ringburst = 0
						p.sms_ringburstready = 0
						p.sms_ringburstfly = 1
						p.sms_ringburstdrain = 1
						p.sms_jumphold = 8
						if s.smssoundobj9 and s.smssoundobj9.valid
							P_KillMobj(s.smssoundobj9)
						end
						
						s.ringburstticket2 = 31
						s.smssoundobj9 = P_SpawnMobj(s.x, s.y, s.z, MT_SMSSOUNDOBJ)
						s.smssoundobj9.master = s
						s.smssoundobj9.health = 1
						S_StartSound(s.smssoundobj9, sfx_smsrbt)
						s.smssoundobj9.smsrbt = 1
						
						A_SMSquake(s, 45*FRACUNIT, 5, 1500)
					--	P_FlashPal(p, PAL_MIXUP, 7)
						P_NukeEnemies(s, s, 200*s.scale)
						
						SMS_QuickDustGFX(s, SKINCOLOR_SUPERSKY2, AST_ADD)
						A_SMSSTOMPQUAKE(s, 45, 5, 5*FRACUNIT, 0, 0, 0, 7, SKINCOLOR_SUPERRED4)
						
						local ghs = HMS_BigBangVortex(s, 20, nil, ANG1*75, T|TR_TRANS30, SKINCOLOR_MYSTICAL2)
						ghs.tracer = s
						
						P_GivePlayerRings(p, -1)
						
						s.bhlcamtimer = 8
						p.camerascale = FRACUNIT*3
						p.aiming = -2500*FRACUNIT

						if SMS_Graphix(s) != 2
							A_SMSSTOMPQUAKE(s, 85, 6, 16*FRACUNIT, 0, ANG10, 0, 22)
							A_SMSSTOMPQUAKE(s, 165, 12, 40*FRACUNIT, FRACUNIT*2, ANG30, 0, 19, SKINCOLOR_SUPERSILVER4)
						
							A_SMSSTOMPQUAKE(s, 125, 7, 30*FRACUNIT, 0, ANG20, 0, 14)
				
							A_SMSSTOMPQUAKE(s, 55, 5, 7*FRACUNIT, 0, ANG30, 0, 7, SKINCOLOR_RED)
							A_SMSSTOMPQUAKE(s, 75, 6, 12*FRACUNIT, 0, (ANG10*5), 0, 22)
							A_SMSSTOMPQUAKE(s, 115, 7, 25*FRACUNIT, 0, 0, 0, 14)
							A_SMSSTOMPQUAKE(s, 145, 12, 35*FRACUNIT, FRACUNIT*2, (ANG30*2), 0, 19, SKINCOLOR_SILVER)
						end
						
						SMS_RingBurstFlight(s, p)
						
						local ghs = SMS_LightFlash(s, SKINCOLOR_SMS3R, FRACUNIT-9500, 5, ((Z+3)|TR_TRANS30))
						ghs.momx = s.momx/4
						ghs.momy = s.momy/4
						
						--High level effects.
						if SMS_Graphix(s) != 2
							SMS_MediumDustGFX(s)
							SMS_ElectricBody(s)
							
							ghs = P_SpawnMobjFromMobj(s, 0,0, -20*s.scale, MT_HMSWAVEAURA)
							ghs.dustwave = 1
							ghs.blendmode = AST_ADD
							ghs.sprite = SPR_SMMK
							ghs.frame = D
							ghs.target = s
							ghs.fuse = 15
							ghs.scale = $*2
							ghs.destscale = $*99
							
							if SMS_Graphix(s) == 0
								ghs = SMS_LightningStrike(s, SKINCOLOR_SMS2R)
								ghs.momy = s.momy
								ghs.momx = s.momx
								ghs.momz = s.momz
								
								SMS_DustLayers(s, true)
								SMS_FlatWave(s,SKINCOLOR_WHITE, 11, 12)
								for d = 1,7
									local ths = HMS_NovaBlastShine(s, 80-((60-d)/2))
									if ths and ths.valid
										P_Thrust(ths, s.angle+(d*(ANG1*78)), 36*s.scale)
										P_Thrust(ths, s.angle, (p.speed/2))
										ths.color = SKINCOLOR_SMS3R
									end	
								end
							end
						end	
						return true --
					end
				end
			end
end)

--Actively in ringburst flight! Sonic X inspired Power Ring flying!
rawset(_G, "SMS_RingBurstFlight", function(s, p)
	p.powers[pw_ignorelatch] = 0
	local FLYANGLE = s.angle
	if p.rings or (p.sms_ringburstfly == 3)
	
		SMS_WallBustPower(s,p)
		p.powers[pw_ignorelatch] = 32768
		p.powers[pw_noautobrake] = 2
		if (p.sms_ringburstfly != 3)
			if not (p.pflags & PF_ANALOGMODE) 
			and p.smsrtable and (p.smsrtable.direct > 4) --Allow a bit of strafing now!
			and not (p.cmd.forwardmove < -32)
				local ANGSHIFT,PLUS = SMS_AngleDifference(FLYANGLE, p.smsrtable.turnang, 2, ANG30, 3)
				FLYANGLE = $-(ANGSHIFT*PLUS)
			end
			if not SMS_Graphix(s)
				if (leveltime % 4 == 0)	
					local ghs = SMS_LightFlash(s, SKINCOLOR_SUPERSKY5, FRACUNIT-19000, nil, P|TR_TRANS80)
					ghs.tracer = s
					ghs.sttfade = 4
					ghs.fuse = 7
					ghs.dispoffset = 2		
				
					local ths = HMS_NovaBlastShine(ghs, 69)
					ths.fuse = 11 ths.tics = 21
					ths.blendmode = AST_ADD
					ths.spriteyscale = $/3
					ths.spritexscale = $/3
					ths.momx = $+s.momx
					ths.momy = $+s.momy
					ths.momz = $+s.momz
					ths.spriteyoffset = 13*FRACUNIT
					if ths and ths.valid
						P_Thrust(ths, s.angle+(leveltime*ANG10), 15*s.scale)
					end	
					if (leveltime % 8 == 0)
					--	if (p.sms_ringburstfly == 2) or (p.sms_ringburstfalltimer < 27)	
						
							local ghs = SMS_ElectricBody(s, (p.sms_ringburstfly == 2) and 11 or 12, (p.sms_ringburstfly == 2) and SKINCOLOR_MYSTICAL5 or SKINCOLOR_SMS3R)
							ghs.sprite = SPR_SMX4
							ghs.frame = Z+6|FF_TRANS10
							ghs.sttfade = true
							ghs.scalespeed = FRACUNIT/7
							ghs.destscale = $*99
							ghs.colorized = false
							ghs.sttspeed = 3
							ghs.sttfirstframe = Z+6
							ghs.sttlastframe = Z+7
							ghs.sttroll = SMSLocalRNG(15,45,s)*ANG1
					--	end
					end
				end
			end
		end
		if (p.sms_ringburstfly == 1) --Vertical phase!
		
				s.SMS_wallwait = 3
				s.SMS_WJpenalty = 0
				s.SMS_lastwallANG = nil --Can freely walljump again!
				s.SMS_lastwallNUM = nil
				if (p.cmd.buttons & BT_JUMP) and (p.sms_jumphold < 3)
			--	if ((p.cmd.forwardmove > 40) and not s.waitforforward) --Transition into horizontal ringburst
				or (p.cmd.buttons & BT_SPIN) and not (p.spinhold > 1)
				
					local ghs = P_SpawnMobjFromMobj(s, 0,0, 12*s.scale*P_MobjFlip(s), MT_RADIALBOOST)
					ghs.color = SKINCOLOR_CLOUDY
					ghs.momz = s.momz/5
					ghs.blendmode = AST_ADD
					
					p.sms_ringburstfly = 2
					s.ringburstticket2 = 39
					A_SMSquake(s, 56*FRACUNIT, 4, 900)
					if s.smssoundobj9 and s.smssoundobj9.valid
						P_KillMobj(s.smssoundobj9)
					end
					S_StopSoundByID(s, sfx_smsrbt)
					
					s.waitforforward = false
					s.smssoundobj9 = P_SpawnMobj(s.x, s.y, s.z, MT_SMSSOUNDOBJ)
					s.smssoundobj9.master = s
					s.smssoundobj9.health = 1
					S_StartSound(s.smssoundobj9, sfx_smsrbt)
					s.smssoundobj9.smsrbt = 2
					
					S_StartSound(s, sfx_smsrbt)
					p.sms_ringburstfalltimer = 0
					p.spinhold = 10
					s.state = S_SMS_HORIZONTALRINGBURST
					p.aiming = 0
					
					if SMS_Graphix(s) != 2
						local ghs = SMS_LightFlash(s, SKINCOLOR_SKY, FRACUNIT-9500, 5, ((Z+3)|TR_TRANS30))
						ghs.tracer = s
						
						ghs = HMS_BigBangVortex(s, 17, nil, -ANG1*55, T|TR_TRANS10, SKINCOLOR_ORANGE)
						ghs.tracer = s
					
						s.SMS_DLtrail = {timer = 8, colorized = true,
						COLORT_trail = {SKINCOLOR_MYSTICAL5, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_RED, SKINCOLOR_EMERALD, SKINCOLOR_GREEN, SKINCOLOR_MYSTICAL2},
						COLORT_trailmax = 8} 
					end
					
				else --if you haven't transitioned to phase 2....
				
					if twodlevel or (s.flags2 & MF2_TWOD)
						s.momx = 0 s.momy = 0 --Vertical 100% travel in 2D mode.
					elseif (p.powers[pw_sneakers] and (G_PlatformGametype() and not s.battlemodsms))
						P_InstaThrust(s, FLYANGLE, 135*s.scale) --Speed up!
					else
						P_InstaThrust(s, FLYANGLE, 90*s.scale) --Regular uptravel!
					end
					
					--Every few tics, boost momz, assuming you're not already even faster!
					if (p.sms_ringburstdrain != 1) and (p.sms_ringburstdrain != 3)
						local RISESPEED = 35
						if (p.powers[pw_sneakers] and (G_PlatformGametype() and not s.battlemodsms))
							RISESPEED = 35
						end
						if (s.momz*P_MobjFlip(s) < (RISESPEED*s.scale))
							P_SetObjectMomZ(s, RISESPEED*FRACUNIT)
						end
					end
					
					if (leveltime % 2 == 0)
						A_SMSSTOMPSMOKE(s, SMSLocalRNG(20, 65, s), MT_IVSP)
					else
						A_SMSSTOMPSMOKE(s, SMSLocalRNG(65, 20,s), MT_SUPERSPARK)
					end
					
					if (p.sms_ringburstfalltimer < 6) --Initial flight frames?
						if SMS_Graphix(s) == 0
							SMS_LightClash(s, nil, s)
							local ghs = SMS_FlatWave(s, SKINCOLOR_MYSTICAL3, 7, 12)
							if SMSLocalRNG(0, 1, s)
								ghs.momz = s.momz/2
								ghs.scalespeed = max($-FRACUNIT, 9001)
							else
								ghs.color = SKINCOLOR_SMS1R
								ghs.frame = $+FF_TRANS20
								ghs.scalespeed = max($-115356, 9001)
							end
						end
						if (p.sms_ringburstfalltimer == 1)
							s.state = S_SMS_VERTICALRINGBURST
							s.SMS_DLtrail = {timer = 8, colorized = true,
							COLORT_trail = {SKINCOLOR_SMS3R, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_LIME, SKINCOLOR_EMERALD, SKINCOLOR_MINT, SKINCOLOR_WHITE},
							COLORT_trailmax = 8} 
						--	s.SMS_DLtrail = {timer = 8, colorized = true, color = SKINCOLOR_SMS4R} 
						elseif p.sms_ringburstfalltimer == 5
							p.aiming = 0
						end
						
					else --Falltimer > 5
					
						local ghs = P_SpawnGhostMobj(s) 
						ghs.colorized = true
						ghs.color = SKINCOLOR_SUPERGOLD3
						ghs.blendmode = AST_ADD
						ghs.frame = ($ & ~FF_TRANSMASK)|TR_TRANS60
						ghs.rollangle = s.rollangle
						ghs.fuse = 15 ghs.tics = 16
						ghs.scalespeed = FRACUNIT/20
						ghs.destscale = ghs.scalespeed+1					
						ghs.spriteyoffset = s.spriteyoffset-24*FRACUNIT
						ghs.dispoffset = -5
						local ANGLE = (s.momx or s.momy) and R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) or s.angle
						P_TryMove(ghs, s.x+s.momx+FixedMul(s.scale*3, cos(ANGLE)), s.y+s.momy+FixedMul(s.scale*3, sin(ANGLE)), true)
						
						if (p.sms_ringburstfalltimer > 64) --Flew too close to the sun, cancel!
							for i = 1,5,4
								local ghs = SMS_SparkleFlash(s, 1, -10*s.scale, FRACUNIT+45000)
								ghs.colorized = true
								ghs.color = SKINCOLOR_RED
								ghs.scalespeed = FRACUNIT/(4+i)
								ghs.destscale = FRACUNIT*99
								ghs.fuse = $+3
								ghs.sttspeed = $+1
								ghs.sttfirstframe = nil
								ghs.spriteyoffset = -12*FRACUNIT
							end
							
							p.sms_ringburstfly = 0
						
							if (p.sms_boostlevel < 23)
								p.sms_boostlevel = 23
							end
							SMS_SideFlip(s, p)	
							p.secondjump = 0
							s.momz = $*5/6
						end
					end						
				end

				if (p.panim != PA_ROLL) and not s.smstrickroll
				and (p.sms_ringburstfalltimer > 6)
				and (s.state != S_SMS_HORIZONTALRINGBURST)
					s.state = S_PLAY_ROLL
				end

		elseif (p.sms_ringburstfly == 2) --Second horizontal phase.
		
				s.SMS_wallwait = 3
				if (p.sms_ringburstfalltimer == 1)
					s.state = S_SMS_HORIZONTALRINGBURST
				end
				if not (p.powers[pw_sneakers] and (G_PlatformGametype() and not s.battlemodsms))
					local ghs = P_SpawnMobjFromMobj(s, 0, 0, 12*s.scale*P_MobjFlip(s), MT_SMSGROUNDBOOST)
					ghs.tracer = s
					ghs.ringburst2 = true
					ghs.done = true
					ghs.destscale = $*99
					if leveltime % 3 == 0
						ghs.scale = $*2/5
						ghs.rollangle = $+ANGLE_45
						ghs.color = SKINCOLOR_FLAME
					else
						ghs.scale = $/2
					end
					P_RadiusAttack(s, s, 128*s.scale)
				end

				s.momz = 0 --No vertical momentum!

				if not ((p.sms_ringburstdrain == 1) or (p.sms_ringburstdrain == 3)) --Horizontal thrust!
					if (p.powers[pw_sneakers] and (G_PlatformGametype() and not s.battlemodsms))
						P_InstaThrust(s, FLYANGLE, 225*s.scale)
					else
						P_InstaThrust(s, FLYANGLE, 150*s.scale)
					end
				end
				if (p.sms_ringburstfalltimer > 2)
				
					if (p.sms_ringburstfalltimer > 90) --Flew too long!
						P_SpawnGhostMobj(s)
						s.state = S_SMS_ROLLT2
						p.hometrickanim = true
						p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
						p.homingtrickcharge = 0
						p.sms_ringburstfly = 0
						p.secondjump = 0
				
					elseif (p.speed < 20* s.scale) or s.SMS_WallBounce --We hit a wall!
					
						s.SMS_WallBounce = nil
						p.sms_ringburstready = 0
						p.sms_ringburstfly = 3 --Phase 3, canceling!
						p.sms_ringburstdrain = 0
						p.powers[pw_flashing] = 46
						p.sms_flamedelay = 46
						s.state = S_PLAY_ROLL
						S_StartSound(s, sfx_dmpain)
						s.momx = 0 s.momy = 0
						P_SetObjectMomZ(s, FRACUNIT)
						P_NukeEnemies(s, s, 100*FRACUNIT)
						P_FlashPal(p, PAL_MIXUP, 7)
						P_SpawnMobjFromMobj(s, 0, 0, 0, MT_BURSTWAVE)

						if SMS_Graphix(s) != 2
							for i = 3,4
								local ghs,ths = SMS_WindBlast(s)
								ghs.spriteyoffset = $-(16*i*FRACUNIT)
								ths.spriteyoffset = $-(16*i*FRACUNIT)
								ths.fuse = $+8
								ghs.fuse = $+8
								ghs.scale = ($*3)/i
								ths.scale = ($*3)/i
								ths.destscale = FRACUNIT*999
								ghs.destscale = FRACUNIT*999
							end			
							local ghs = SMS_SmokeCircle(s, 0, 1, s, FRACUNIT*4)
							ghs.spriteyoffset = -47*FRACUNIT
						end
				
						local ghs = SMS_LightFlash(s, SKINCOLOR_WHITE, FRACUNIT, 6, Z+3|TR_TRANS10)
						ghs.fuse = 18
						ghs.tracer = s
						ghs.spriteyoffset = $-24*FRACUNIT
				
						SMS_MediumDustGFX(s)
						A_SMSquake(s, 90*FRACUNIT, 13, 500)	
						
						if s.SMS_lastwallNUM and s.SMS_lastwallNUM.valid and s.SMS_lastwallNUM.frontside
						and not SMS_SkyBoxWall(s, s.SMS_lastwallNUM, 0)
							SMS_WallCrater(s,s,s.SMS_lastwallNUM)
							for i = 1,9
								SMS_CraterSpew(s, s.angle+ANGLE_180, 11)
							end
							S_StartSound(s, sfx_smsfx6)
							P_FlashPal(p, PAL_NUKE, 5)
						end
						s.SMS_lastwallNUM = nil
					end
				end
				
		elseif (p.sms_ringburstfly == 3) --End-phase cancelout of the Ring Burst.
			p.powers[pw_ignorelatch] = 0
			s.SMS_wallwait = 0
			P_SetObjectMomZ(s, 1*FRACUNIT)
			if (p.powers[pw_flashing] <= 16) or (p.sms_flamedelay <= 16)
				p.sms_ringburstfly = 0
				p.secondjump = 0
				s.state = S_PLAY_FALL
				p.pflags = $|PF_JUMPED & ~PF_THOKKED
			end
		end
	end
		
	--PART 2 of the RingBurst flight!
		
		p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_SPINNING & ~PF_STARTDASH & ~PF_SLIDING
		if (leveltime % 2 == 0)	and (p.sms_ringburstfly != 3)
			s.ringburstticket = true
		end
		
		local CANCEL = false
		
		if (P_IsObjectOnGround(s) and (p.sms_ringburstfly != 1))
			--Transition into boost if we ringburst into the floor!
			if ((p.panim == PA_WALK) or (p.panim == PA_RUN) or (s.state == S_SMSWATERGRIND1) or p.smsboostpanim)
			and (p.sms_ringburstfalltimer > 3)
				p.sms_boostlevel = 67
				p.sms_ringburstready = 0
				p.sms_ringburstfly = 0
				p.powers[pw_ignorelatch] = 0
				return false
			elseif not (s.momz*P_MobjFlip(s) > s.scale/2)
				CANCEL = true
			end
		end
		
		--Stomp anytime out of the ring burst! It'll stop all momentum, though!
		if (p.cmd.buttons & BT_CUSTOM2) and not (p.smsmeleebutton > 1) and CANCEL != true
			s.momx = 0 s.momy = 0
			P_NukeEnemies(s, s, 128*s.scale)
			P_SetObjectMomZ(s, -50*FRACUNIT)
			S_StopSoundByID(s, sfx_smsrbt)
			
			local ghs = SMS_LightFlash(s, s.color, nil, nil, Z+3|TR_TRANS10)
			ghs.tracer = s
			ghs.sttfade = 14	
			
			S_StartSound(s, sfx_stst)
			if not p.powers[pw_carry]
				p.pflags = $|PF_JUMPED
			end
			p.sms_ringburstfly = 0
			p.powers[pw_ignorelatch] = 0
			if not P_IsObjectOnGround(s)
				s.state = S_SMS_STOMP1
				p.sms_stompanim = 9001
				p.sms_afterstompthok = false				
			end
			return false --Done!
		end		
		
		if CANCEL == true	
		or (p.panim == PA_PAIN) 
		or (p.panim == PA_SPRING) --Generic cancel conditions
		or (p.panim == PA_FALL)
		or p.powers[pw_carry]
		or p.hypermysticsonic
		or p.powers[pw_carry]
		or p.gotflag
		or not s.health	
		
			p.powers[pw_ignorelatch] = 0
			
			if not s.smstrickroll
				p.sms_ringburstready = 0
				p.sms_ringburstfly = 0
				if (s.state == S_PLAY_ROLL) and not p.powers[pw_carry] 
					if not P_IsObjectOnGround(s)
						p.pflags = $|PF_JUMPED
					else
						s.state = S_PLAY_FALL
						p.pflags = $ & ~PF_JUMPED
					end
				end
			end
			return false --
					
		--You screwed up! Ran out of rings or flung into ceiling!
		elseif (not p.rings) and (p.sms_ringburstfly < 3)
		
		or ((p.sms_ringburstfly == 2) and (p.sms_ringburstfalltimer > 2) and (p.speed < 30*s.scale))
		
		or ((p.sms_ringburstfly == 1) and (s.momz*P_MobjFlip(s) < s.scale/2) and (p.sms_ringburstfalltimer > 2))
		
			if p.sms_ringburstfly != 1
			
				if p.rings
					SMS_WallJumpGFX(s)
				end
				SMS_WindBlast(s)
				
			elseif p.rings --Hit ceiling!
			
				SMS_GroundWindBlast(s)			
				
				if (abs( (s.z+s.height)-s.ceilingz ) < 149*s.scale)
					local sec = (s.subsector) and s.subsector.sector --Object sector.				
					if not (sec and (abs(s.ceilingz-sec.ceilingheight) < 149*s.scale) and sec.ceilingpic == "F_SKY1")
						local ghs = SMS_Splat(s, 62)
						if ghs and ghs.valid
							ghs.sprite = SPR_SMX1
							ghs.frame = S|TR_TRANS10
							ghs.scale = $*2
							ghs.z = s.ceilingz-s.scale
							for i = 1,6
								SMS_CraterSpew(s, s.angle+ANGLE_180, 7)
							end
							S_StartSound(s, sfx_smsfx6)
							P_FlashPal(p, PAL_NUKE, 5)	
						end
					end
				end
			end
			
			p.sms_ringburstready = 0
			p.sms_ringburstfly = 3
			p.sms_ringburstdrain = 0
			p.powers[pw_ignorelatch] = 0
			p.powers[pw_flashing] = 46
			p.sms_flamedelay = 46
			if P_IsObjectOnGround(s)
				s.state = S_PLAY_FALL
			else
				s.state = S_PLAY_ROLL
			end
			S_StartSound(s, sfx_dmpain)
			s.momx = 0 s.momy = 0
			P_SetObjectMomZ(s, FRACUNIT)
			
			local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_BURSTWAVE)
			ghs.dispoffset = -1
			ghs.mirrored = true
			
			if not p.rings
				P_FlashPal(p, PAL_NUKE, 6)
				s.sms_boostloss = 12
				S_StartSound(s, sfx_antiri)
				
				local ghs = SMS_ElectricBody(s, 35, SKINCOLOR_CRIMSON)
				ghs.sttfade = 18
			else
				P_FlashPal(p, PAL_MIXUP, 7)
				P_NukeEnemies(s, s, 100*s.scale)
				if SMS_Graphix(s) != 2
					for i = 3,4
						local ghs,ths = SMS_WindBlast(s)
						ghs.spriteyoffset = $-(16*i*FRACUNIT)
						ths.spriteyoffset = $-(16*i*FRACUNIT)
						ths.fuse = $+8
						ghs.fuse = $+8
						ghs.scale = ($*3)/i
						ths.scale = ($*3)/i
						ths.destscale = FRACUNIT*999
						ghs.destscale = FRACUNIT*999
					end			
					local ghs = SMS_SmokeCircle(s, 0, 1, s, FRACUNIT*4)
					ghs.spriteyoffset = -47*FRACUNIT
				end
				
				local ghs = SMS_LightFlash(s, SKINCOLOR_WHITE, FRACUNIT, 6, Z+3|TR_TRANS10)
				ghs.fuse = 18
				ghs.tracer = s
				ghs.spriteyoffset = $-24*FRACUNIT
				
				SMS_MediumDustGFX(s)
				A_SMSquake(s, 90*FRACUNIT, 13, 500)				
			end	
		elseif (p.sms_ringburstfly != 1) --Gain ability to roll out of your jump!
				
			if (p.cmd.buttons & BT_SPIN) and (p.spinhold <= 1)
				p.sms_ringburstfly = 0
				p.sms_boostlevel = max($,31)
				p.powers[pw_ignorelatch] = 0

				local ghs = P_SpawnGhostMobj(s)
				ghs.blendmode = AST_ADD
				ghs.colorized = true
				ghs.color = SKINCOLOR_CRIMSON
				ghs.frame = ($ & FF_FRAMEMASK)|FF_TRANS90
				ghs.destscale = $*99 ghs.tics = 18 ghs.fuse = 17
						
				s.state = S_SMS_ROLLT2
				p.hometrickanim = true
				p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
				p.homingtrickcharge = 0
			end
		end
		
end)