--=============================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<<16
			end
		end
		
		if not SMS_Graphix(s)
			A_SMSSTOMPQUAKE(s, SMSLocalRNG(100, 300,s), SMSLocalRNG(6,11,s), SMSLocalRNG(40,90,s)<<16, 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<<16, 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<<16, 6, 550)
			end
		else
			A_SMSquake(s, 40<<16, 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<<16
				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) */

--Become invincible for half a second after dodging through an enemy/missile!
rawset(_G, "SMS_InvincibleBurst", function(s, p)
	if (gametype == GT_COOP or gametype == GT_RACE)
	and not (p.powers[pw_flashing] > 22)
	and not p.powers[pw_invulnerability]
	and not s.SMS_lemmego
		s.SMS_lemmego = 50 --Used as the cooldown timer for your invincibility burst.
		p.powers[pw_flashing] = max($,23)
		s.flags2 = $ & ~MF2_DONTDRAW
		local ghs = SMSRSpawnGhost(s)
		if ghs and ghs.valid
			ghs.colorized = true
			ghs.color = SKINCOLOR_MINT
			ghs.scalespeed = FRACUNIT/6
			ghs.spriteyoffset = $-5<<16
			ghs.destscale = FRACUNIT*99
		end
		S_StartSoundAtVolume(s, sfx_addfil, 110)
		S_StartSound(s, sfx_s1bd)
		S_StartSound(s, sfx_s1c0)
		S_StartSound(s, sfx_s236)
		
		local ghs = SMS_LightFlash(s, SKINCOLOR_MYSTICAL3, FRACUNIT-16600, 7, Z+3|TR_TRANS30)
		ghs.tracer = s
		ghs.fuse = 12
		ghs.scalespeed = $/3
		
		local ghs = SMS_FollowCircle(s, SKINCOLOR_MINT, 2)
		ghs.fuse = 10 ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
		
		SMS_WindJumpGFX(s)	
		SMS_DiffusionChargeSparkles(s,p, SKINCOLOR_EMERALD)
		SMSWindLines(s, p)
		
		if not (s.SMS_squashtable and s.SMS_squashtable.timer > 2)
			s.spritexscale = FRACUNIT-6100
			s.spriteyscale = FRACUNIT+19100
			s.spriteyoffset = 0
			s.SMS_squashtable = {timer = 3, Xspeed = 2200, Yspeed = 7500}
		end
	end	
end)

--Is this a competitive gametype where SMS should use his nerfed stats? Returns false if not!
rawset(_G, "SMS_NerfedMode", function(p)
	if G_GametypeHasSpectators()
	--	if not (gametyperules & (GTR_SPAWNENEMIES))
		if (gametyperules & (GTR_RINGSLINGER|GTR_TAG))
	--	and not (CBW_Chaos and (gametype == GT_CHAOS))
			return true --
		end
	end
	return false --
end)

--AutoGraphix are....
rawset(_G, "SMS_AutoGraphix", function(p, zm)
	for z in players.iterate
		if z.smsautographix != nil
		and not (zm and zm == z)
			return z.smsautographix --
		end
	end
	return false--
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 kinda 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 --
		end return false --
	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
			local TCOLOR = p.transformstatecolors
			
			if TCOLOR == 3
				s.color = SKINCOLOR_SUPERGOLD4
			elseif TCOLOR == 6
				s.color = SKINCOLOR_SUPERGOLD5 --Most dark
			elseif TCOLOR == 9
				s.color = SKINCOLOR_SUPERGOLD4
			elseif TCOLOR == 12
				s.color = SKINCOLOR_SUPERGOLD3
			elseif TCOLOR == 16
				s.color = SKINCOLOR_SUPERGOLD2
			elseif TCOLOR == 21
				s.color = SKINCOLOR_SUPERGOLD1 --Lightest.
			elseif TCOLOR == 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
				local TCOLOR = p.transformstatecolors
				
				if not (TCOLOR < 47)
					if s.color != p.smscoatc4
						s.color = p.smscoatc4
					end				
				else
					if (TCOLOR < 6)
						if s.color != p.smscoatc3
							s.color = p.smscoatc3
						end
					elseif (TCOLOR < 12)
						if s.color != p.smscoatc2
							s.color = p.smscoatc2
						end
					elseif (TCOLOR < 18)
						if s.color != p.smscoatc1
							s.color = p.smscoatc1
						end
					elseif (TCOLOR < 27)
						if s.color != p.smscoatc2
							s.color = p.smscoatc2
						end
					elseif (TCOLOR < 33)
						if s.color != p.smscoatc3
							s.color = p.smscoatc3
						end
					elseif (TCOLOR < 44)
						if s.color != p.smscoatc4
							s.color = p.smscoatc4
						end
					else--if (TCOLOR < 47)
						if s.color != p.smscoatc5
							s.color = p.smscoatc5
						end
					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 = SMS_NerfedMode(p) 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<<16
	
	SMS_LightningStrike(s, SKINCOLOR_RED, R|FF_TRANS10)				
	
	P_SetObjectMomZ(s, 8<<16, true)
	A_SMSquake(s, 55<<16, 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<<16
	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<<16, s.scale), 10<<16, 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<<16
				s.spriteyoffset = 3<<16
				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 --Crouch down!		
			if p.hypermysticsonic
				s.hmsoframe = F --HMS stands on the ground instead of crouching.
			else
				if not s.SMS_tossready
					s.spritexscale,s.spriteyscale = FRACUNIT-4000,FRACUNIT+14000
					s.SMS_squashtable = {timer = 4}
					if P_IsObjectOnGround(s) and not (s.eflags & MFE_UNDERWATER)
						for i = -2,2
							local ghs = P_SpawnMobjFromMobj(s, 
							FixedMul(7*s.scale, cos(s.angle+(ANG30*i)))+FixedMul(11*s.scale, cos(s.angle)),
							FixedMul(7*s.scale, sin(s.angle+(ANG30*i)))+FixedMul(11*s.scale, cos(s.angle)),
							0, MT_THOK)
							ghs.scale = $/2
							ghs.fuse,ghs.tics = 11,2
							ghs.color,ghs.colorized = SKINCOLOR_BEIGE,true
							ghs.state = S_SPINDUST2
							ghs.flags2 = $|MF2_SHADOW
							P_Thrust(ghs, s.angle+(ANG30*i), 2*s.scale)
						end
						S_StartSoundAtVolume(s, sfx_s1b6, 16)
						S_StartSoundAtVolume(s, sfx_kc50, 3)
					end					
				end
				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<<16
				s.SMS_squashtable = {timer = 7, Xspeed = 8500, Yspeed = 9570, air = true}	
				if (s.eflags & MFE_UNDERWATER)
					P_SetObjectMomZ(s, 7<<16+36000)
				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 SMS_NerfedMode(p) 
		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 SMS_NerfedMode(p) --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<<16)
				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<<16, 256*i<<16, 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<<16
							ghs.spriteyoffset = 108<<16
							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 SMS_NerfedMode(p)
	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<<16
			ghs.radius = 190<<16
		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<<16
				ghs.radius = 190<<16
			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<<16
				ghs.radius = 190<<16
			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<<16, 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<<16, 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<<16
						
						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<<16, 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,s.spriteyoffset,s.spritexscale,s.spriteyscale = nil,0,FRACUNIT,FRACUNIT
		
		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)
			p.powers[pw_strong] = $|STR_SPRING
			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<<16
			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 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<<16
		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)
	num = (num) or 0
	return (p.hypermysticsonic) and S_HMSCOMETHAYMAKER1+num or S_SMSCOMETHAYMAKER1+num
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
				elseif not m.facc --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<<16 and s.skin != "sonic"
		p.normalspeed = 59<<16
	elseif (skins[s.skin].normalspeed < 108<<16) --Speed up!
		p.normalspeed = min(108<<16, (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<<16, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
		end
		for i = -3,0
			SMS_FlameWalls(s, nil, 57<<16, ((256*i)+46)<<16, 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<<16*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<<16, s.scale), 10<<16, 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
	end return false --
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 m.valid
				if (m.type == MT_RING) or (m.type == MT_COIN) or (m.type == MT_BLUESPHERE)
					if m.health and (m != s)
						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
			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 m.valid
				if (m.flags & (MF_BOSS|MF_ENEMY))
				or m.player and SMS_NerfedMode(p) and not m.player.hypermysticsonic
				or (m.type == MT_CYBRAKDEMON)
					if m.valid and m.health and m != s
						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
	   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 m.valid
				if m.player
					if not (s.battlemodsms or SMS_NerfedMode(p))
						return --
					end
					if m.player.powers[pw_invulnerability]
					or m.player.powers[pw_flashing]
					or (p.ctfteam and (m.player.ctfteam == p.ctfteam))			
						return --
					end
				elseif not (m.flags & (MF_BOSS|MF_ENEMY))
					return --
				end
				if not m.health
				or m.theghoul 
				or m == s
					return --
				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))
					if not s.dfclosest 
					or ( s.dfclosest and (LOOKRANGE < s.dfclosest) )
						if P_CheckSight(s, m)
							s.dfclosest = LOOKRANGE
							s.target = m
							looklimit = LOOKRANGE
							FoundStuff = m
							if m.player and not var3
								m.player.dfflame = 16
							end
						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)
					if (m.flags & MF_MONITOR)
						if P_CheckSight(s, m)					
							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 --
						end
					else
						if not ( (m.type == MT_GARGOYLE) and ((m.subsector.sector.floorpic == "DSTRACK2") or (m.subsector.sector.floorpic == "DSTRACK1")) )
							if P_CheckSight(s, m)
								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
	   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 SMS_NerfedMode(p) 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 == 20 and SMS_NerfedMode(p)
					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 SMS_NerfedMode(p)
	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<<16, 0, false)
		end
		s.flags = $ & ~MF_SPECIAL
	end
	--Immediately disappear always.
	if (s.scale < FRACUNIT/4)
	or (s.eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))
	or s.extspd
	or P_CheckDeathPitCollide(s)
		s.state = S_NULL return --
	end
	if s.growtimer
		s.growtimer = $-1
		if (s.growtimer >= 29) --Just started growing.
			if not s.extrfl
				P_InstaThrust(s, s.angle + ANGLE_90, (s.flame1) and 10*s.scale or -10*s.scale)
			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,s.momy = 0,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
					P_InstaThrust(s, s.angle+ANGLE_90, (s.flame1) and 7*s.scale or -7*s.scale)
				end
			elseif (s.growtimer == 27)
				if not s.extrfl
					P_InstaThrust(s, s.angle+ANGLE_90, (s.flame1) and 3*s.scale or -3*s.scale)
				end
			elseif (s.growtimer == 26)
				P_SetObjectMomZ(s, 9<<16)
			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
	local SEC = s.subsector and s.subsector.sector
	
	if not (SEC and SEC.valid)
		return true --Can't obtain info? Assume you're on a death pit!
	end
	
	if P_PlayerTouchingSectorSpecial(p, 1, 7)
	or P_PlayerTouchingSectorSpecial(p, 1, 6)
	or P_PlayerTouchingSectorSpecial(p, 1, 8)
		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
	end return false --Not on a death pit, then!
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)<<16, P_RandomRange(-7, 7)<<16, FRACUNIT, var1)
	local f1 = P_SpawnMobjFromMobj(s, P_RandomRange(-7, 7)<<16, P_RandomRange(-7, 7)<<16, 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 p.smsatomicdivefail
	or p.mo.SMSDELAYRENDER
	or SMS_Graphix(p.mo) == 2 and (leveltime % 2)
	or A_HMSCheckDeathPit(p.mo)
		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<<16 >= fof.topheight)
		and not (s.z+99<<16+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<<16)
				end
				if (ANGSHIFT > 3<<16)
					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 ((SMS_NerfedMode(p) or s.battlemodsms) and not p.rings)) )
			and not ((s.eflags & MFE_UNDERWATER) and not p.hypermysticsonic)
				
				P_InstaThrust(s, s.angle, 2<<16)
				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<<16, 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<<16
				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<<16)
				
				if not SMS_Graphix(s) --You sure? I'm not holding back.
					A_SMSSTOMPQUAKE(s, 95, 4, 70<<16, 0, ANG30, 0, 19, SKINCOLOR_SUPERSILVER4)
					A_SMSSTOMPQUAKE(s, 75, 1, 32<<16, 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<<16
					ghs.spriteyoffset = 108<<16
					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<<16
					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<<16, FRACUNIT*2+1, ghs)
							ghs.spriteyscale = $+SMSLocalRNG(FRACUNIT*2, 6<<16, 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<<16, 0, ANG10, 0, 22, SKINCOLOR_RED)
				A_SMSSTOMPQUAKE(s, 105, 3, 78<<16, 0, ANG30*2, 0, 19, SKINCOLOR_SILVER)				
					
				if SMS_NerfedMode(p)
					P_Telekinesis(p, 100<<16, 2800<<16)
				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 (SMS_NerfedMode(p) 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<<16
						ghs.spritexoffset = 111<<16
						ghs.spriteyoffset = 108<<16					
								
						ghs = SMS_ElectricBody(s, 5, SKINCOLOR_SUPERSKY5)
						ghs.spriteyscale = FRACUNIT+42000
						ghs.spritexscale = FRACUNIT+45000
						
						A_SMSSTOMPQUAKE(s, 85, 6, 16<<16, 0, ANG10, 0, 22)
						A_SMSSTOMPQUAKE(s, 165, 12, 40<<16, FRACUNIT*2, ANG30, 0, 19, SKINCOLOR_SUPERSILVER4)
						A_SMSSTOMPQUAKE(s, 115, 7, 25<<16, 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<<16, 0, 0, 0, 7, SKINCOLOR_SUPERRED4)
					A_SMSSTOMPQUAKE(s, 125, 7, 30<<16, 0, ANG20, 0, 14)
				
					A_SMSSTOMPQUAKE(s, 55, 5, 7<<16, 0, ANG30, 0, 7, SKINCOLOR_RED)
					A_SMSSTOMPQUAKE(s, 75, 6, 12<<16, 0, (ANG10*5), 0, 22)
					A_SMSSTOMPQUAKE(s, 145, 12, 35<<16, 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 SMS_NerfedMode(p)
						P_InstaThrust(s, s.angle, -8<<16)
					else
						P_InstaThrust(s, s.angle, 2<<16)
					end
				end
					
			//Mid level landing!
			elseif (p.smsdivetimer >= 18 and p.smsdivetimer < 39)

				if not (s.eflags & MFE_UNDERWATER and not p.hypermysticsonic)
				
					if (SMS_NerfedMode(p) 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<<16
						ghs.spriteyoffset = 108<<16					
					
						SMS_ElectricBody(s, 24)
					
						ghs = SMS_LightFlash(s, SKINCOLOR_SMS5R)
						ghs.tracer = s
						ghs.fuse = 17
						ghs.scalespeed = FRACUNIT/3
						ghs.spriteyoffset = $-10<<16
						
						for i = 0,4
							HMS_GroundSparks(s, p, i)
						end						
				
						A_SMSSTOMPQUAKE(s, 55, 7, 5<<16, 0, ANG30, 0, 13, SKINCOLOR_FLAME)
						A_SMSSTOMPQUAKE(s, 75, 9, 16<<16, 0, (ANG30*3), 0, 20, SKINCOLOR_SUPERRED1)
						A_SMSSTOMPQUAKE(s, 95, 10, 30<<16, 0, (ANG20*2), 0, 22, SKINCOLOR_SUPERRED5)						
					end
					
					s.hmselcharge = 18
					P_InstaThrust(s, s.angle, 2<<16)
				
	    			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<<16, 0, 0, 0, 13, SKINCOLOR_RED)
					A_SMSSTOMPQUAKE(s, 85, 9, 16<<16, 0, ANG10, 0, 20, SKINCOLOR_SUPERRED5)
					A_SMSSTOMPQUAKE(s, 105, 10, 30<<16, 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<<16)
					end

					A_SMSSTOMPSMOKE(s, 4, MT_BOSSEXPLODE, 80<<16)
					A_SMSSTOMPSMOKE(s, 5, MT_EXPLODE, 40<<16)
					A_SMSSTOMPSMOKE(s, 6, MT_BOSSEXPLODE)

					A_SMSkillstuff(s, 777<<16, 500<<16, 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<<16*mult)
				
				local ghs = P_SpawnGhostMobj(s)
				ghs.destscale = ghs.scalespeed+1
				ghs.scalespeed = $*2
				
				if (leveltime % 2)
					P_InstaThrust(s, s.angle, 10<<16*mult)
				end
				s.momz = (-5<<16*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<<16*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<<16*mult)
				end
				s.momz = ((-75<<16*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<<16*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<<16*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<<16)
				end
				P_SetObjectMomZ(s, -120<<16, 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<<16, 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 (SMS_NerfedMode(p) 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<<16
	p.cmd.forwardmove = 50
	p.sms_spinuc = 2
	
	--What kind of boost?
	if p.hypermysticsonic
	or (p.powers[pw_sneakers] and not SMS_NerfedMode(p) and not s.battlemodsms) //Overdrive Dropdash!
		
		if p.hypermysticsonic
			P_InstaThrust(s, s.angle, 350<<16)
			A_SMSquake(s, 104<<16, 12, 1500)
			P_NukeEnemies(s, s, 500<<16)
			P_FlashPal(p, PAL_NUKE, 7)
		else
			P_InstaThrust(s, s.angle, 260<<16)
			A_SMSquake(s, 90<<16, 4, 1500)
			P_NukeEnemies(s, s, 110<<16) --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<<16, 0, ANG20, 0, 20)
		A_SMSSTOMPQUAKE(s, 165, 14, 35<<16, 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<<16)
		S_StartSound(s, sfx_kc3c)
		S_StartSound(s, sfx_s3k5f)
		P_FlashPal(p, PAL_MIXUP, 3)
		P_NukeEnemies(s, s, 70<<16)
		s.smschargedbd = 11
		A_SMSquake(s, 74<<16, 11, 1400)
		p.sms_boostlevel = 70 //Automatically go into boostmode from this one.	
			
	elseif (p.smsdivetimer > 16)
			
		P_InstaThrust(s, s.angle, 145<<16)
		S_StartSound(s, sfx_kc3c)
		p.sms_boostlevel = $+35
		A_SMSquake(s, 54<<16, 7, 1000)
			
	elseif (p.smsdivetimer < 17)	
			
		P_InstaThrust(s, s.angle, 106<<16)
		p.sms_boostlevel = $+24
		A_SMSquake(s, 24<<16, 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<<16))
		ghs.fuse = 9
		ghs.tracer = s
		ghs.renderflags = $|RF_PAPERSPRITE
		ghs.angle = s.angle+ANGLE_90
		ghs.spriteyoffset = $-20<<16
		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<<16, 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<<16, s.scale), FixedMul(84<<16, 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<<16, 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<<16 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<<16*MULT)
		end
		s.smstrickrollang = $-FixedAngle(45<<16*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<<16, 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<<16, s.scale), 10<<16, 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<<16)*(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<<16, 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<<16, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
				end
			end
			
			s.renderflags = $ & ~RF_FULLBRIGHT
			local ghs = P_SpawnMobjFromMobj(s, 0,0, ((20<<16)*(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<<16)
	else
		s.nrollangle = $+FixedAngle(39<<16)
	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<<16,cos(s.angle)), s.y+FixedMul(170<<16,sin(s.angle)), s.z-550<<16, MT_SMSECHECK)
		if ghs and ghs.valid
			ghs.lsasms = true
			ghs.target = s
			ghs.height = 1100<<16
			ghs.radius = 960<<16
		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<<16)
		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<<16), FixedDiv(261<<16, 3<<16/2))
			s.momy = FixedMul(FixedDiv(s.target.y-s.y, 512<<16), FixedDiv(261<<16, 3<<16/2))
			P_SpawnSpinMobj(p, (p.spinitem or MT_THOK))
			
				if p.mslightspeedattack == 1
					P_SetObjectMomZ(s, 32<<16)
				elseif p.mslightspeedattack == 2
					P_SetObjectMomZ(s, 17<<16)
				elseif p.mslightspeedattack == 3
					P_SetObjectMomZ(s, 3<<16)
				else
					s.momz = FixedMul(FixedDiv(s.target.z-s.z, 512<<16), FixedDiv(261<<16, 3<<16/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<<16)
				local ghs = P_SpawnMobj(s.x+FixedMul(170<<16,cos(s.angle)), s.y+FixedMul(170<<16,sin(s.angle)), s.z-550<<16, MT_SMSECHECK)
				if ghs and ghs.valid
					ghs.lsasms = true
					ghs.target = s
					ghs.height = 1100<<16
					ghs.radius = 960<<16
				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<<16)
		--	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)
	if not (netgame) and not ((a>FRACUNIT) or (b>FRACUNIT))
		return P_RandomRange(a,b) --No reason to use this outside of netgames.
	end
	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<<16 //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] or p.isxmomentum) and (not SMS_NerfedMode(p) 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<<16 or 55<<16
					if (p.sms_boostlevel >= 65)
						speed = $+141<<16
						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<<16, 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<<16), 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 SMS_NerfedMode(p) 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<<16
				p.footsweepanim = true
				p.sweepangle = s.angle
				p.footsweep01 = 0
				p.footsweep02 = 20
				A_SMSquake(s, 9<<16, 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 SMS_NerfedMode(p) 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)
	local t = p.smspickup
	if not (t and t.valid)
		p.smspickup = nil
	else
		local tp = t.player
		t.eflags = s.eflags --Adjust to match our needs.
		t.rollangle = s.rollangle
	
		if (t.type == MT_PLAYER)
			t.state = S_PLAY_PAIN
			if tp
				tp.powers[pw_nocontrol] = 2
				if (tp.cmd.buttons & BT_JUMP) or p.sms_intoship or tp.sms_intoship
					s.whoopsnope = true
					P_DoJump(tp, true)
					tp.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<<16)
			P_SetObjectMomZ(t, 5<<16)
			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<<16, 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<<16)
						P_Thrust(s, s.angle, -9<<16)
						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 tp
						tp.powers[pw_carry] = 0
						tp.powers[pw_ignorelatch] = 32770
						tp.powers[pw_noautobrake] = 2
						tp.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<<16,cos(ANGLE)),
				s.y+FixedMul(119<<16,sin(ANGLE)),
				s.z-60<<16, MT_SMSECHECK)
					if ghs and ghs.valid
						ghs.smsepickup = true
						ghs.target = s
						ghs.radius = 120<<16
						ghs.height = 120<<16
					end
					
				else
				
				ghs = P_SpawnMobj(
				s.x+FixedMul(49<<16,cos(ANGLE)),
				s.y+FixedMul(49<<16,sin(ANGLE)),
				s.z-60<<16, MT_SMSECHECK)
					if ghs and ghs.valid
						ghs.smsepickup = true
						ghs.target = s
						ghs.radius = 50<<16
						ghs.height = 120<<16
					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<<16,cos(ANGLE)), s.y+FixedMul(119<<16,sin(ANGLE)), s.z-60<<16, MT_SMSECHECK)
				if ghs and ghs.valid
					ghs.radius = 120<<16
				end
			else
				ghs = P_SpawnMobj(s.x+FixedMul(49<<16,cos(ANGLE)), s.y+FixedMul(49<<16,sin(ANGLE)), s.z-60<<16, MT_SMSECHECK)
				if ghs and ghs.valid
					ghs.radius = 50<<16
				end
			end
			if ghs and ghs.valid
				ghs.justcheckin = true
				ghs.smsepickup = true
				ghs.target = s
				ghs.height = 120<<16
			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,s.momy = $/5,$/5
						s.sms_fullcharge = 0
						if p.dashspeed > 36<<16
							p.dashspeed = 36<<16
						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,s.momy,p.cmomx,p.cmomy = 0,0,0,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<<16)
						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<<16
		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<<16
		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<<16) and not forcejump --Big stomp!
		S_StartSound(s, sfx_brakrx)
		A_SMSSTOMPSMOKE(s, 18, MT_EXPLODE)
		A_SMSquake(s, 24<<16, 6, 2200)
		P_RadiusAttack(s, s, 230<<16, 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
	local KILLER = s.Killer
	
	--Need to do Setup?
	if s.InitTrack == nil
		s.InitTrack = true
		if s.fusrodahfuse == nil		
			s.fusrodahfuse = 1	
		end
		s.OGclip = (t.flags & MF_NOCLIPTHING) and true or nil
		s.OGnograv = (t.flags & MF_NOGRAVITY) and true or nil
		s.OGfloat = (t.flags & MF_FLOAT) and true or nil
		s.OGslide = (t.flags & MF_SLIDEME) and true or nil
		
		
		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
	local ghs
	
	--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<<16)
				P_InstaThrust(t, s.angle, 125<<16)
			elseif not s.fusrodahwallexplode
				P_InstaThrust(t, s.angle, 125<<16)
			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!
			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<<16)
		P_InstaThrust(s, s.angle, 10<<16)
	else
		s.momx,s.momy,s.momz = t.momx,t.momy,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
		s.spindirection = P_RandomRange(0,1) and 1 or -1
	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)<<16) or FixedAngle((s.SpinSpeed/2*s.spindirection)<<16)
		
	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)
			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
		local SfX = P_SpawnMobjFromMobj(t, 0,0,0, MT_THOK) --SFX object for later.
		SfX.tics,SfX.flags2 = 133, MF2_DONTDRAW		
		
		--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
			S_StartSound(SfX, sfx_smsfx6)
		end

		--Grounded wind blast!
		SMS_GroundWindBlast(s)
			
		--Wind Blast, but slightly customized!
		if method != 1
			for i = 1,2		
				ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSAUTOFRAME)
				
				ghs.sprite = SPR_SMX1
				ghs.frame = A|(i<<16)
				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...
		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			
			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
				
				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
			
			--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 < 550<<16
						P_FlashPal(z, PAL_NUKE, 2)
						A_SMSquake(z.mo, 60<<16, 10, 1)
					elseif DIST < 2000<<16
						A_SMSquake(z.mo, 20<<16, 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.tics,SFX.flags2 = 99, MF2_DONTDRAW
					S_StartSound(SFX, sfx_smsfx1)
				end		
			end
			if s.OGnograv t.flags = $|MF_NOGRAVITY end
			if s.OGclip t.flags = $ & ~MF_NOCLIPTHING end
			if s.OGfloat t.flags = $|MF_FLOAT end
			if s.OGslide t.flags = $|MF_SLIDEME end
			t.flags2 = $ & ~MF2_FRET
			
			--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)
				if t.facc 
					if t.facc.phase == 2 and t.facc.health P_DamageMobj(t, t, t, 1, DMG_ELECTRIC) end t.fusrodah = nil s.state = S_NULL return --
				end 
			end			
			
			t.health = max(0,min($,1))
			P_KillMobj(t, s, KILLER)
			
			if t and t.valid
				if (t.type == MT_FANG)  --C'mon, keep moving, now.
					t.flags = $|MF_BOUNCE|MF_NOCLIPTHING & ~MF_NOCLIP
					P_SetObjectMomZ(t, 2<<16, true)
					if KILLER and KILLER.valid and ((abs(t.momx)+abs(t.momy)) < 40<<16)
						P_InstaThrust(t, R_PointToAngle2(t.x, t.y, KILLER.x, KILLER.y), -40*KILLER.scale)
					end
				end
				t.fusrodah = nil --If you haven't disintegrated entirely, remove this.
			end
			if s and s.valid and not (s.SMS_SpecialKill and s.SMS_SpecialKill.valid)
				s.state = S_NULL return --
			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 (SMS_NerfedMode(p)) --Forget this nonsense.
		or v == TARGETPLAYER
		or v == s --Not yourself!
			return --
		end
		if not s.player --Either be a direct hit or be an AoE from HMS.
			if not (s.target and s.target.player and s.target.player.hypermysticsonic)
				return --
			end
		end
		local p = (p and p.mo) and p or s.target.player
		local tp = v.player
		
		if v.health and p
		and not tp.cmphdodge
		and not tp.sms_dodging
		and not (tp.panim == PA_PAIN)
			if not (G_GametypeHasTeams() and v.player.ctfteam == p.ctfteam)			
				if p.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)
					P_DamageMobj(v, s, (not s.player) and TARGETPLAYER or nil)
					P_SpawnMobjFromMobj(v, 0, 0, 0, MT_SMSAIRBOOST)
					P_InstaThrust(v, s.angle, 125<<16)
				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<<16)
						v.state = S_PLAY_PAIN
					end
					P_InstaThrust(v, s.angle, 125<<16)
				end
				if v and v.valid and not v.health
					v.state = (v.skin == "adventuresonic") and S_PLAY_PAIN or S_PLAY_DEAD
				end
			end
		end
	--If Battlemod Tails dodges the haymaker, then 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<<16)
			v.momz = 40<<16
			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<<16)
			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,ghs.momy,ghs.momz = t.momx/2,t.momy/2,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
		local vt = v.target
		P_SetObjectMomZ(vt, 12<<16)
		P_InstaThrust(vt, s.angle, 125*s.scale)
		P_SpawnMobjFromMobj(vt, 0,0,0, MT_BURSTWAVE)
		vt.fusrodah = 1
		vt.trackerspawn = P_SpawnMobjFromMobj(v, 0,0,0, MT_CMPHTRACKER)
		vt.trackerspawn.angle = s.angle
		vt.trackerspawn.tracer = TARGETPLAYER
		vt.trackerspawn.target = vt
		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<<16)
			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<<16)
		P_InstaThrust(v, s.angle, 125<<16)			
		
		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<<16)
		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<<16)
	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<<16, 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)
			
			if (t.type == MT_FANG) or (t.type == MT_KOOPA)
				if gamemap == 15 and (t.type == MT_FANG)
					P_LinedefExecute(65531)
					P_LinedefExecute(65532)
				end
				A_BossDeath(t, 0, 0)
			end
		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 (SMS_NerfedMode(p) 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<<16, (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<<16
		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<<16)
			t.flags = $|MF_SHOOTABLE|MF_ENEMY
			t.state = S_MINUS_AERIAL1
		end			
	elseif SMS_NerfedMode(p) 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<<16 or 450<<16
	
	--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<<16, 3<<16/2))
	s.momy = FixedMul(FixedDiv((t.y+t.momy)-s.y, cspeed), FixedDiv(132<<16, 3<<16/2))
	s.momz = FixedMul(FixedDiv((t.z+t.momz)-s.z, cspeed), FixedDiv(132<<16, 3<<16/2))	
	
	 --Give players a chance to dodge the punch!
	if SMS_NerfedMode(p) 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<<16)
				and not (z.mo.z > s.z+(375<<16)) --Close enough!
				and not (z.mo.z < s.z-(375<<16))
					
					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<<16
		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 not HoldControls or not p.smsdivefloat
			s.spriteyoffset = -11<<16
			p.pflags = $ & ~(PF_JUMPED|PF_THOKKED)
			s.SMSLASTANGLE,s.SMSDELAYRENDER = nil,nil
			s.state = S_PLAY_FALL
			p.sms_boostlevel = 0 --You wasted this, though...
			p.smsdivefloat = 0
		end
		if HoldControls 
			if not p.smsdivefloat --Time ran out!
				p.smsatomicdivefail = 120
				s.sms_boostloss = 0
				s.atomdiveheld = 0
				p.powers[pw_flashing] = max($, 16)
				S_StartSound(s, sfx_ngskid)

				s.spritexscale,s.spriteyscale = FRACUNIT-900,FRACUNIT+11000
				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.spritexscale,s.spriteyscale = FRACUNIT-3900,FRACUNIT+31000
			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,s.momy = 0,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 > 22)
				P_SetObjectMomZ(s, (p.smsdivefloat > 50) and -1<<16/19 or -2<<16/5)
			else
				P_SetObjectMomZ(s, -3<<16/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<<16
				s.SMS_squashtable = {timer = 22, Xspeed = 7000, Yspeed = 12100, air = true}
				
				if not ( (not SMS_NerfedMode(p) 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<<16)*(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<<16)
							s.smsdropdash = true
							S_StartSound(s, sfx_s3kabb)
							S_StartSound(s, sfx_cdfm23)
						end
					else
						if (s.z < s.floorz+150<<16)
							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<<16)	
	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<<16)*(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 (SMS_NerfedMode(p) 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<<16, 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<<16, 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<<16, 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<<16
			end
			if SMS_NerfedMode(p) 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<<16, cos(s.angle)),
				FixedMul(-4<<16, 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)<<16)
				end
			--3D version!	
			elseif (abs(p.cmd.sidemove) > 20) --Dodge laterally!		
				s.state = S_SMS_SIDEDODGE1
				P_InstaThrust(s, TurnAng, (64-debuff)<<16)
				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)<<16)
				else
					s.state = S_PLAY_RUN
					P_InstaThrust(s, TurnAng, (76-debuff)<<16)			
				end
			elseif (p.cmd.forwardmove < -20) //Dodge Backwards!
				s.state = S_SMS_SIDEDODGE2
				P_InstaThrust(s, TurnAng, (69-debuff)<<16)
			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<<16, 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<<16) and (p.speed < 40<<16))
						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<<16
				s.spriteyoffset = 7<<16
				s.SMS_squashtable = {timer = 5, Xspeed = (9000/4)-1, Yspeed = (12000/4)-1}	
				
				local ghs = SMS_TwirlGFX(s)
				ghs.spriteyoffset = $+5<<16
				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 (SMS_NerfedMode(p) 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<<16, 2, 750, true)
			p.smsmeleebutton = 9000
		
			s.spritexscale = FRACUNIT+30000
			s.spriteyscale = FRACUNIT-4000
			s.spriteyoffset = 2<<16
			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 SMS_NerfedMode(p) and not (p.mxscap > 1)
				P_GivePlayerRings(p, -1)
			end
			return true --
		
		elseif p.footsweep02 --Now do the Final Footsweep!
		and not (SMS_NerfedMode(p) 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<<16, 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<<16
			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)<<16)
						ghs.spritexscale = $/i
						ghs.spriteyscale = $/i
						ghs.spritexoffset = 127<<16
						ghs.spriteyoffset = 129<<16						
					end
					ghs.sttspeed = nil
					ghs.sttfade = 9
					ghs.tracer = s
					ghs.renderflags = RF_SEMIBRIGHT|RF_FLOORSPRITE|RF_ABSOLUTEOFFSETS|RF_NOSPLATBILLBOARD
					ghs.destscale = 999<<16
					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<<16, 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 SMS_NerfedMode(p) 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<<16)
				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 SMS_NerfedMode(p) //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 SMS_NerfedMode(p)
		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<<16)
					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<<16, s.scale), 10<<16, 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<<16
						ghs.spritexoffset = 4<<16
						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<<16, 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<<16, 0, 0, 0, 20, SKINCOLOR_SUPERRED4)
		A_SMSSTOMPQUAKE(s, 125, 10, 25<<16, 0, ANG20, 0, 20)
		A_SMSSTOMPQUAKE(s, 165, 14, 35<<16, FRACUNIT*2, ANG30, 0, 20, SKINCOLOR_SUPERSILVER4)

		S_StartSound(s, sfx_spld)
		A_SMSquake(s, 75<<16, 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<<16)
			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<<16, 0, ANG10)
					A_SMSSTOMPQUAKE(s, 85, 18, 3<<16, 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 not SMS_NerfedMode(p) and not s.battlemodsms
			
			or (s.scale >= 2<<16)
			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<<16, 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<<16, 0, 0, 0, 0, 0, MT_SMALLBUBBLE)
				A_SMSSTOMPQUAKE(s, 25, 15, 4<<16, 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<<16, 2, 700, true)
				P_RadiusAttack(s, s, 64<<16)
			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 not SMS_NerfedMode(p) and not s.battlemodsms
		or (s.scale >= 2<<16)
			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<<16)
		else
			P_SetObjectMomZ(s, -(STOMPPOWER/2)<<16, 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<<16
			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<<16}
					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<<16
					ghs.spriteyoffset = 129<<16
					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<<16
					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 not SMS_NerfedMode(p) and not s.battlemodsms
				P_NukeEnemies(s, s, 128*s.scale) --Nuke vs radius attack!
			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<<16)
				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<<16)
			else
				P_SetObjectMomZ(s, -7<<16, 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<<16, min(88<<16, 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<<16
						ghs.spriteyoffset = 23<<16								
					else
						ghs = SMS_FlatWave(s, SKINCOLOR_SILVER, 16, 2, 0)
						ghs.frame = Q|FF_TRANS20
						ghs.spritexscale = $/6
						ghs.spriteyscale = $/6
						ghs.spritexoffset = 127<<16
						ghs.spriteyoffset = 129<<16							
					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<<16
					ghs.GSoffsetang = s.angle
					ghs.sttroll = ANG1*(16+(i*12))
				end						
			end
			if not s.SMS_squashtable
				s.spritexscale,s.spriteyscale = FRACUNIT-8500,FRACUNIT+13500
				s.spriteyoffset = -13<<16
				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<<16
			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)

--List of compatible SMS skins.
local SMSskins = {
"greeneyes",
"sunky",
"werehog",
"earless",
"earlessredo",
"3dson",
"handdrawn",
"pointy",
"sonik",
"bandages",
}
//Check if you count as "Sonic the Hedgehog" and are therefore viable to go SMS!
function A_SMScompatibleskin(s, HUD) if not s.valid return end --
	local skin = s.skin
	if skin != "sms"
		if skin == s.CanGoSMS --Stops us from having to check every single time if a skin is Sonic or not.
			return true --You can force your skin to be SMS compatible this way too.
		elseif string.find(skin, "sonic") 
			if not string.find(skin, "metal")
			and not string.find(skin, "mecha")
			and not string.find(skin, "hard")
				if not HUD --Don't swap in the HUD, just return true or false.
					s.CanGoSMS = skin
				end return true --
			end
		else
			local SKINS = #SMSskins
			for i = 1,SKINS
				if skin == SMSskins[i]
					if not HUD --Don't swap in the HUD, just return true or false.
						s.CanGoSMS = skin
					end return true --Basically every mod is another Sonic. And since everyone is Sonic, EVERYONE IS ALSO SMS!! 
				end
			end
		end
	end
	if not HUD s.CanGoSMS = nil end --Remove if a check fails.
	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 (s.eflags & MFE_UNDERWATER)
				s.sneakjuw = 10 //Still underwater? Then keep your momentum.
			end
			if s.sneakerwc
				P_SetObjectMomZ(s, (-28<<16)*10/s.sneakjuw, true)
			elseif ((p.pflags & PF_STARTJUMP) and (p.cmd.buttons & BT_JUMP))
				P_SetObjectMomZ(s, (-20<<16)*10/s.sneakjuw, true)
			else
				P_SetObjectMomZ(s, (-13<<16)*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 SMS_NerfedMode(p)
			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<<16)
					A_SMSSTOMPSMOKE(s, 24, MT_SPINDUST)
					SMSRSpawnGhost(s, 8)
					P_SetObjectMomZ(s, 18<<16, true)
					s.sneakerjcd = 4
					s.sneakerwc = false
				end
				if (s.eflags & MFE_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<<16
			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<<16, 1, 2500)
						RadialBurst = 320<<16
					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 --Nerfed due to how much more spammable this has gotten.
		p.sms_boostlevel = (p.sms_boostlevel < 45) and $+10 or $+20 --Final stretch has a bigger gain, though.
	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 (abs(p.cmd.sidemove) > 9)
		s.hmsrotateright = (p.cmd.sidemove > 9) and true or nil
	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<<16, 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 < 16)
				S_StartSound(s, (sfxrng < 11) and sfx_smsha4 or sfx_smsha5)
			elseif (sfxrng < 18) and not (s.lastbdsfx and s.lastbdsfx > 15 and s.lastbdsfx < 18)
				S_StartSound(s,sfx_smsha2)
			else
				S_StartSound(s,(sfxrng < 20) and sfx_smsha1 or 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<<16, MT_MEDIUMBUBBLE)
		P_SpawnMobjFromMobj(s, 5<<16, 5<<16, 4<<16, 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 SMS_NerfedMode(p)) 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<<16 > targetspeed)
					targetspeed = min(21*s.scale, p.speed-(20<<16))
					if targetspeed == 21*s.scale
						targetspeed = 21*s.scale+(p.speed/3)
					end
				end
			elseif (p.speed-20<<16 > targetspeed) --Gain more thrust if you're faster.
				targetspeed = max(targetspeed, max(11<<16, p.speed-(20<<16)) )
			end
		end
	
	if p.powers[pw_sneakers] and not SMS_NerfedMode(p) 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<<16
				
				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<<16
				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 (not SMS_NerfedMode(p) 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 SMS_NerfedMode(p)
		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 SMS_NerfedMode(p)
			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 SMS_NerfedMode(p)
				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<<16
			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<<16
		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<<16)
		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<<16, cos(s.angle+ANGLE_90)),s.y+FixedMul(8<<16, 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<<16, cos(s.angle+ANGLE_90)),s.y-FixedMul(8<<16, 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<<16)
			P_SetObjectMomZ(s, 3<<16, 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<<16, 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<<16)
			else
				P_SetObjectMomZ(s, 26<<16)
			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<<16
			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<<16, 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<<16*(P_MobjFlip(s.target))), MT_SMSFIREBLAST, s.x, s.y, s.z+(15<<16*(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<<16, true)
		A_SMSquake(s, 60<<16, 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<<16+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 (not SMS_NerfedMode(p) 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 (not SMS_NerfedMode(p) 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<<16, 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<<16, 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<<16

				if SMS_Graphix(s) != 2
					A_SMSSTOMPQUAKE(s, 85, 6, 16<<16, 0, ANG10, 0, 22)
					A_SMSSTOMPQUAKE(s, 165, 12, 40<<16, FRACUNIT*2, ANG30, 0, 19, SKINCOLOR_SUPERSILVER4)
				
					A_SMSSTOMPQUAKE(s, 125, 7, 30<<16, 0, ANG20, 0, 14)
		
					A_SMSSTOMPQUAKE(s, 55, 5, 7<<16, 0, ANG30, 0, 7, SKINCOLOR_RED)
					A_SMSSTOMPQUAKE(s, 75, 6, 12<<16, 0, (ANG10*5), 0, 22)
					A_SMSSTOMPQUAKE(s, 115, 7, 25<<16, 0, 0, 0, 14)
					A_SMSSTOMPQUAKE(s, 145, 12, 35<<16, 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<<16
					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<<16, 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 (not SMS_NerfedMode(p) 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 (not SMS_NerfedMode(p) and not s.battlemodsms))
							RISESPEED = 35
						end
						if (s.momz*P_MobjFlip(s) < (RISESPEED*s.scale))
							P_SetObjectMomZ(s, RISESPEED<<16)
						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<<16
						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<<16
							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 (not SMS_NerfedMode(p) 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 (not SMS_NerfedMode(p) 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<<16)
						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<<16)
								ths.spriteyoffset = $-(16*i<<16)
								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<<16
						end
				
						local ghs = SMS_LightFlash(s, SKINCOLOR_WHITE, FRACUNIT, 6, Z+3|TR_TRANS10)
						ghs.fuse = 18
						ghs.tracer = s
						ghs.spriteyoffset = $-24<<16
				
						SMS_MediumDustGFX(s)
						A_SMSquake(s, 90<<16, 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<<16)
			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<<16)
			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<<16)
						ths.spriteyoffset = $-(16*i<<16)
						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<<16
				end
				
				local ghs = SMS_LightFlash(s, SKINCOLOR_WHITE, FRACUNIT, 6, Z+3|TR_TRANS10)
				ghs.fuse = 18
				ghs.tracer = s
				ghs.spriteyoffset = $-24<<16
				
				SMS_MediumDustGFX(s)
				A_SMSquake(s, 90<<16, 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)

--When you have p.homing active, use this homing attack behaviour.
rawset(_G, "SMS_HomingBehaviour", function(s, p)
	s.rollangle = 0 --Reset some stuff.
	s.smstrickroll = false
	s.airboostroll = 0
	s.sms_boostplustimer = nil
	s.sms_boostplus = nil
	s.sms_tauntcooldown = 0 --Instantly reset taunt CD!
	if s.SMS_squashtable != nil
		s.SMS_squashtable,s.spriteyoffset,s.spritexoffset,s.spritexscale,s.spriteyscale = nil,0,0,FRACUNIT,FRACUNIT
	end
	p.charflags = $ & ~SF_MULTIABILITY
	
	p.sms_advfall = 0
	s.retmhtsfx = 0
	
	s.sms_runspinboost = 0
	p.sms_spinuc = 0
	
	s.SMS_WJauto = 0
	p.sms_wallstat = 0 --No walljumping stats.
	s.SMS_wallwait = (p.cmd.buttons & BT_SPIN) and 8 or 5
	SMS_WallJumpDelete(s, true) --Homing attacks take priority.				
	s.SMS_lastwallANG = nil
	s.SMS_lastwallNUM = nil
	s.SMS_WJpenalty = 0
	
	p.sms_flamedelay = max($,2) --Keep this on cooldown.
	s.SMS_ishoming = true
	
	if (s.eflags & MFE_UNDERWATER) --Appropriate thokitems.
		p.thokitem = 0
		A_BossScream(s, 0, MT_SMALLBUBBLE)
	else
		A_BossScream(s, 0, MT_SMSTSPARK)
		p.thokitem = MT_SMSHOMING
	end
	
	if (p.homing == 87)
		if (p.speed < max(5, s.scale-10000))
			p.pflags = $|PF_SPINNING & ~PF_JUMPED
		end
	elseif s.tracer and s.tracer.valid
		local t = s.tracer
		if (t == p.smspickup) --This would be a real issue.
			if (t.flags & (MF_SHOOTABLE|MF_MONITOR))
				if t.smspickupflags != nil
					t.smspickupflags = $|MF_SHOOTABLE
				else
					t.smspickupflags = t.flags
				end
				t.flags = $ & ~MF_SHOOTABLE
			end
			p.homing = min($,1) 
			s.momz = 0
		elseif not (s.target and s.target.valid)
			s.target = s.tracer --Copy tracer.				
		end

		if (p.homing >= 101) and (p.homing <= 104)
		and not (t.flags & (MF_SPRING|MF_MONITOR)) --Only powerup against actual enemies.
		and not (s.eflags & MFE_UNDERWATER)
		and SMS_Graphix(s) != 2			
		
			local FRAME = (p.homing == 104) and Z+5|FF_TRANS40 or (p.homing % 2) and Z+6|FF_HORIZONTALFLIP|FF_VERTICALFLIP|FF_TRANS60 or Z+7|FF_TRANS60
			local ghs = HMS_BigBangVortex(s, 9, nil, ANG10*5, FRAME, (p.homing != 104) and SKINCOLOR_SMS3R or SKINCOLOR_RED)
			
				ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
				ghs.tracer = s
				if p.homing != 104
					ghs.ditchtracer = 1
				else
					ghs.scale = $*2/3
					ghs.ditchtracer = 3								
				end
				ghs.spriteyoffset = $+11<<16
				ghs.momx = s.momx/2
				ghs.momy = s.momy/2		
				if p.powers[pw_sneakers]
					ghs.sttfade = 7
					ghs.scalespeed = FRACUNIT/5
				end
				if p == displayplayer and CV_FindVar("renderer") == 1
					ghs.flags2 = $|MF2_DONTDRAW
				end		
				--Experimental effect.
			/*	for i = -1,-1,2
					local FRAME = FF_TRANS70|(i == 1 and FF_HORIZONTALFLIP|FF_VERTICALFLIP|Z+6 or Z+7)
					local ghs = HMS_BigBangVortex(s, 8, nil, ANG10*5, FRAME, (p.homing != 104) and SKINCOLOR_SMS3R or SKINCOLOR_RED)	
					ghs.renderflags = RF_SEMIBRIGHT|RF_PAPERSPRITE
					ghs.tracer = s
					ghs.ditchtracer = 2
					ghs.momx = s.momx/-2
					ghs.momy = s.momy/-2			
					if p.homing != 104
						ghs.rollangle, ghs.sttrollangle = ANG20, ANGLE_22h	
						ghs.sttroll = -ANGLE_45 
						ghs.GSoffsetX = (i == 1) and -2*s.scale or -125*s.scale
						ghs.GSoffsetY = (i == 1) and -2*s.scale or -125*s.scale
						ghs.spriteyscale = $/2
						ghs.spritexscale = $/2
					else
						ghs.rollangle, ghs.sttrollangle = ANG20, ANGLE_22h	
						ghs.scale = $*2/3
						ghs.GSoffsetX = 33*s.scale*i
						ghs.GSoffsetY = 33*s.scale*i
						ghs.sttXspeed = -9<<16 --FF_HORIZONTALFLIP auto-inverts this.
					end
					ghs.GSoffsetZ = s.height+abs(s.momz*2) + ((ghs.frame & FF_VERTICALFLIP) and 10*s.scale or 0)
					if p.powers[pw_sneakers]
						ghs.sttfade = 7
						ghs.scalespeed = FRACUNIT/5
					end
					if p == displayplayer and CV_FindVar("renderer") == 1
						ghs.flags2 = $|MF2_DONTDRAW
					end
				end */
		end
		
		--Attempt an INSTANT homing attack, the Lightning Homing Bolt!
		if (p.powers[pw_sneakers] or (p.sms_boostlevel > 64))
		and not t.player --Not against players, tho.	
		and not (s.eflags & MFE_UNDERWATER)
		
			if (p.homing >= 98) and (p.homing <= 105)
			
				if (t.type == MT_TNTBARREL)
					t.YOUKICKEDME = s
				elseif not (abs(t.z-s.z) > 1799<<16) --Sanity check.
					if p.speed --For momentum use later!
						s.SMShomingspeed = p.speed
						s.SMShomingangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
					else
						s.SMShomingspeed = 24*s.scale
						s.SMShomingangle = s.angle				
					end	
					
					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK) --Our target for the lightning strike later!
					ghs.flags2 = MF2_DONTDRAW ghs.fuse = 1
				
					local READY = true
					local PARTS = 10
					local INVERSE = false
				
					for i = 1,PARTS
						if i == PARTS --Final step.
							s.z = t.z+t.height
						else --Creep closer.
							local ZDIST = abs(s.z-(t.z+t.height/2)) or 1
							if (t.eflags & MFE_VERTICALFLIP) and not INVERSE
							or INVERSE and not (t.eflags & MFE_VERTICALFLIP)
								if not (s.z+(ZDIST/PARTS) > s.ceilingz)
									s.z = $+(ZDIST/PARTS)
								end
							elseif not (s.z-(ZDIST/PARTS) < s.floorz+s.scale)
								s.z = $-(ZDIST/PARTS)
							end
						end
						local DIST = (FixedHypot(t.x-s.x, t.y-s.y)+s.scale)/PARTS
						local ANGLE = R_PointToAngle2(s.x, s.y, t.x, t.y)
						if DIST --If there's distance, try it!
							if not P_TryMove(s, s.x+FixedMul(DIST, cos(ANGLE)), s.y+FixedMul(DIST, sin(ANGLE)), true)
								INVERSE = true
							else
								INVERSE = false
							end
						end
					end
				
					if READY and P_TryMove(s, t.x, t.y, true)					
						local SfX = P_SpawnMobj(s.x, s.y, s.z, MT_THOK) --Sound object.
						SfX.flags2 = MF2_DONTDRAW SfX.tics = 169 SfX.fuse = 169

						S_StartSoundAtVolume(SfX, sfx_rail2, 45)
						S_StartSoundAtVolume(s, sfx_rail1, 90)
						S_StartSoundAtVolume(SfX, sfx_litng4, 155)	
					
						if ghs and ghs.valid
							SMS_LightningStrike(ghs, SKINCOLOR_RED, R)
								
							ghs.sprite = SPR_SMX1
							ghs.frame = Z|FF_TRANS50
							ghs.renderflags = RF_SEMIBRIGHT
							ghs.blendmode = AST_ADD
							ghs.color = SKINCOLOR_SMSROVERGLOWRED
							ghs.fuse = 18 ghs.tics = 99
							ghs.scalespeed = FRACUNIT/18
							ghs.destscale = ghs.scalespeed+1
							SMS_DrawTrail(s, ghs, MT_THOK) --Now draw the trail.
								
							local ths = SMS_LightClash(ghs, SKINCOLOR_SUPERORANGE1, ghs)
							ths.angle = s.SMShomingangle+ANGLE_90
							ths.frame = $+FF_TRANS20
							ths.scalespeed = $*2
							ths.fuse = $-4
							ths.tracer,ths.ditchtracer = nil
								
							ths = SMS_FollowCircle(ghs, SKINCOLOR_SUPERSKY1)
							ths.frame = $+FF_TRANS20
							
							if not ((t.flags & MF_SPRING) and not (t.flags & MF_ENEMY))
								SMS_AngledLightning(s, SKINCOLOR_RED, 23, 4-SMS_Graphix(s))
								S_StartSoundAtVolume(s, sfx_kc40, 115)
							end
							ghs.state = S_NULL										
						end
						if not ((t.flags & MF_SPRING) and not (t.flags & MF_ENEMY)) --Don't electrocute springs!
							S_StartSoundAtVolume(SfX, sfx_brakrx, 35)	
							p.homing = min($, 97)
							SMS_LightningStrike(s, SKINCOLOR_RED)
							SMS_LightningStrike(t, SKINCOLOR_RED, R)
							SMS_LightningTrail(s, t)
						end
					end
				end
			end				
		end
		
		if (abs(s.z-s.floorz) < s.scale)
			s.z = $+s.scale
		end						
	/*	if s.SMS_stompmom  
			if not (abs(s.SMS_stompmom ) > 22*s.scale)
				s.SMS_stompmom = 0 --Too slow, stop looking.
			elseif (s.target == s.tracer)
			and not ((t.flags & MF_SPRING) and not (t.flags & MF_ENEMY)) --Don't electrocute springs!
			and not (s.eflags & MFE_UNDERWATER) 
				if FixedHypot(FixedHypot(t.x-s.x, t.y-s.y), t.z-s.z) < 400*s.scale
					if not (p.powers[pw_sneakers] or (p.sms_boostlevel > 64))
						local SfX = P_SpawnMobj(s.x, s.y, s.z, MT_THOK) --Sound object.
						SfX.flags2 = MF2_DONTDRAW SfX.tics = 169 SfX.fuse = 169

						S_StartSoundAtVolume(SfX, sfx_rail2, 45)
						S_StartSoundAtVolume(s, sfx_rail1, 90)
						S_StartSoundAtVolume(SfX, sfx_litng4, 155)	
						
						S_StartSoundAtVolume(SfX, sfx_brakrx, 35)	
						p.homing = min($, 97)
						SMS_LightningStrike(s, SKINCOLOR_RED)
						SMS_LightningStrike(t, SKINCOLOR_RED, R)
						SMS_LightningTrail(s, t) 
					end

					if (s.SMS_stompmom  < 0) and not (s.eflags & MFE_VERTICALFLIP)
					or (s.SMS_stompmom  > 0) and (s.eflags & MFE_VERTICALFLIP)
						if not p.powers[pw_sneakers]
							local ghs = SMS_ElectricBody(s, 21, SKINCOLOR_SKY)
							ghs.sttfade = true
						end
						
						local PREPTIME = min(7, max(1, FixedInt(abs(s.SMS_stompmom/5)))) --SMS is batman?!
						
						s.SMS_HomingBolt = {
						bounce = s.SMS_stompmom,
						preptimer = PREPTIME,
						launchtimer = 12,
						addmult = 0,
						target = t,
						homespeed = (s.SMShomingspeed != nil) and s.SMShomingspeed or 28*s.scale,
						homeangle = (s.SMShomingangle != nil) and s.SMShomingangle or s.angle,
						start = true
						}
						if s.SMShomingangle != nil
							p.drawangle = s.SMShomingangle
							s.angle = s.SMShomingangle
						end
						p.camerascale =  FRACUNIT*3
						s.bhlcamtimer = 11		
						s.SMS_stompmom = 0
					end	
				end
			end 
		elseif (p.pflags & PF_NOJUMPDAMAGE) and not P_IsObjectOnGround(s)
			local ZDIST = (abs(t.z-s.z) or 1)/14
			if ZDIST
				if (t.z+t.height/2 < s.z)
					if (s.z-ZDIST > s.floorz+s.scale)
						s.z = $-ZDIST
						A_BossScream(s, 0, MT_SMSTSPARK)
					end
				elseif (t.z > s.z+(s.height*2))
					if (s.z+ZDIST < s.ceilingz-s.scale)
						s.z = $+ZDIST
						A_BossScream(s, 0, MT_SMSTSPARK)
					end	
				end
			end 					
		end  */
	end
end)

--SMS can jump for a while after leaving the ground. Handy with his ridiculous speeds.
rawset(_G, "SMS_FrameJump", function(s, p)
	if (p.panim >= PA_WALK) and (p.panim <= PA_ROLL)
	or (s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04)
	or (p.sms_dodging > 10)
	else return end
			
	if ((p.sms_dodging > 10) or not (p.pflags & (PF_SLIDING|PF_STARTDASH|PF_THOKKED|PF_JUMPED|PF_STARTJUMP)))
			
	and not (s.eflags & (MFE_SPRUNG|MFE_UNDERWATER))
	and not SMS_Locked(s, p)
			
		local oldmomz = s.momz
		P_DoJump(p, true)
				
		SMS_WallJumpDelete(s, true) --Act like we jumped off the ground, so refreshed WJ stats.
		s.SMS_lastwallANG,s.SMS_lastwallNUM = nil
		s.SMS_WJpenalty = 0		
		p.sms_ringburstready = 0
				
		if (oldmomz*P_MobjFlip(s) > 0) --Always work in the player's favor!
			s.momz = $+oldmomz
		end
				
		--Turning a bug into a feature! Jump right after a dodge for a cool trickjump! The Dodge Leap.
		if (p.sms_dodging > 10)
							
			p.sms_ringburst = 0 --One or the other!
			p.sms_ringburstfly = 0
			p.sms_ringburstready = 0
			
			S_StopSoundByID(s, skins["sms"].soundsid[SKSJUMP])
			s.stompnegatedinput = nil
			p.sms_dodging = 0
			s.sdodgelvl = 1
			s.smsboostimmune = 7
			S_StartSound(s, sfx_kc5b)
			
			s.spritexscale = FRACUNIT+900
			s.spriteyscale = FRACUNIT-28000
			s.spriteyoffset = 15<<16
			s.SMS_squashtable = {timer = 8, Xspeed = 50, Yspeed = 6060, air = true}
			
			local COLOR = (abs(p.cmd.forwardmove) > 35) and SKINCOLOR_SILVER or SKINCOLOR_SKY 	
			
			if SMS_Graphix(s) != 2 --Spawn a whole bunch of cool effects!
				if SMS_Graphix(s) == 0
					SMS_FlatWave(s, nil, nil, 8)
					SMS_QuickDustGFX(s)
					for d = 0,7
						P_SpawnParaloop(s.x+(s.momx*5), s.y+(s.momy*5), s.z+(s.momz*4), 10<<16, 16, MT_NIGHTSPARKLE, d*ANGLE_45, S_SMSTSPARK1, false)							
					end
				end
				
				SMS_LightFlash(s, s.color, nil, 5, P|TR_TRANS40) --Leap in a blinding flash of light!
				s.SMS_DLtrail = {timer = 5} --Set default ghosttrail table.
				
				local ghs = SMS_DashGFX(s, (COLOR == SKINCOLOR_SILVER) and COLOR or s.color)
				ghs.spriteyscale = FRACUNIT-15000
				if COLOR == SKINCOLOR_SKY
					ghs.frame = (p.cmd.sidemove > 0) and $|FF_HORIZONTALFLIP or $ & ~FF_HORIZONTALFLIP
				else
					ghs.frame = ($ & ~FF_TRANSMASK)|TR_TRANS10
					if displayplayer and (p == displayplayer) --This won't desync, I think?
						ghs.rollangle = $+ANGLE_270
						ghs.renderflags = $ & ~RF_PAPERSPRITE
					end
				end					
			end
			
			--Be more consistent regardless of bad player timing.
			if COLOR != SKINCOLOR_SKY
				s.state = S_SMS_ROLLT1
				P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 81*s.scale)
				P_SetObjectMomZ(s, 15<<16)							
			else
				P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), 86*s.scale)
				P_SetObjectMomZ(s, 15<<16+45655) --Go a bit higher than the frontal dodgeleap.
				s.state = S_SMS_SIDET
				if p.cmd.sidemove 
					s.hmsrotateright = (p.cmd.sidemove < 0) and false or true
				else
					s.hmsrotateright = (P_RandomRange(0,1)) and true or false
				end	
			end
			SMS_MediumDustGFX(s)
			p.hometrickanim = true
			s.hmscchange = true
			p.homingtrickcharge = 0
			s.airboostroll = 0
			s.rollangle = 0
			s.smstrickroll = true
			s.smstrickrollang = 0
			p.sms_boostlevel = min($+22,63) --Give some boostlvl too, but not past the point of speedbreak.
			p.pflags = $|PF_JUMPDOWN|PF_JUMPED|PF_NOJUMPDAMAGE & ~(PF_SPINNING|PF_THOKKED|PF_STARTJUMP)		

		else
			if SMSSHIELD(p, SH_WHIRLWIND)
				s.momx = $/3 s.momy = $/3
			end							
			local ghs = P_SpawnGhostMobj(s)
			P_MoveOrigin(ghs, s.x-s.momx, s.y-s.momy, s.z-s.momz)							
		end
	end
end)

--What color is the foe? Old stolen code from SMSreborn.
rawset(_G, "SMSenemycolor", function(s)
	if not (s.flags & (MF_ENEMY|MF_BOSS)) return s.color or SKINCOLOR_GREY end
	local COLOR = s.color or SKINCOLOR_GREY
	if (s.type == MT_BLUECRAWLA)
	or (s.type == MT_SPRINGSHELL)
	or (s.type == MT_YELLOWSHELL)
	or (s.type == MT_PENGUINATOR)
	
		COLOR = SKINCOLOR_CORNFLOWER
		
	elseif (s.type == MT_GFZFISH)
	or (s.type == MT_CRAWLACOMMANDER)
	or (s.type == MT_METALSONIC_BATTLE)
	
		COLOR = SKINCOLOR_COBALT
		
	elseif (s.type == MT_GSNAPPER)
	or (s.type == MT_JETTBOMBER)
	or (s.type == MT_ROBOHOOD)
	
		COLOR = SKINCOLOR_GREEN
		
	elseif (s.type == MT_REDCRAWLA)
	or (s.type == MT_REDBUZZ)
	or (s.type == MT_DETON)
	or (s.type == MT_VULTURE)
	or (s.type == MT_CRUSHSTACEAN)
	
		COLOR = SKINCOLOR_RED
		
	elseif (s.type == MT_EGGGUARD)
	or (s.type == MT_BANPYURA)
	or (s.type == MT_PTERABYTE)
	
		COLOR = SKINCOLOR_LAVENDER
		
	elseif (s.type == MT_FACESTABBER)
	or (s.type == MT_JETJAW)
	
		COLOR = SKINCOLOR_CRIMSON
		
	elseif (s.type == MT_DRAGONBOMBER)
	or (s.type == MT_PYREFLY)
	or (s.type == MT_CANARIVORE)
	or (s.type == MT_SNAILER)
	or (s.type == MT_GOLDBUZZ)
	or (s.type == MT_HIVEELEMENTAL)
	or (s.type == MT_BUMBLEBORE)
	
		COLOR = SKINCOLOR_GOLDENROD
		
	elseif (s.type == MT_JETTGUNNER)
	or (s.type == MT_CYBRAKDEMON)
	
		COLOR = SKINCOLOR_BROWN
		
	elseif (s.type == MT_UNIDUS)
	or (s.type == MT_POPUPTURRET)
	or (s.type == MT_MINUS)
		COLOR = SKINCOLOR_CARBON
	end
	return COLOR --
end)