--=============================MAIN THINKER 1=======================================
--Since I didn't feel confident redirecting to other functions when I first made this project,
--I organized SMS' main thinkers into 3 parts. This is part 1, the main Lua script.
--SMS' most basic moves and attributes are set up here.
--==================================================================================

//2014: I have no idea what I'm doing - Let's Lua!
//EDIT-2019: Hey dataminer! Incase you're reading this to try and learn Lua, I suggest looking elsewhere first.
//I learned how to script WHILE making this, so alot of this is redundant, excessive, and/or terribly inefficient.
//Even the comments may be outdated and have no context anymore. Venture at your own risk!
-- GoldenShine AKA Super Mystic Sonic

--Which of these five billion gamemodes shall count as "Battlemod" today?
local function BattleModCheck(s)
	if not CBW_Battle return false end --It's not even added... nvm then.
	
	if gametype == GT_BATTLE or gametype == GT_CP 
	or gametype == GT_ARENA or gametype == GT_SURVIVAL
	or gametype == GT_TEAMBATTLE or gametype == GT_TEAMCP or gametype == GT_BATTLECTF  or gametype == GT_TEAMARENA or gametype == GT_TEAMSURVIVAL
	or gametype == GT_DIAMOND or gametype == GT_TEAMDIAMOND or gametype == GT_EGGROBOTAG
		return true --
	else
		return false --
	end
end

--Because the two hooks work SLIGHTLY different, I have PlayerSpawn+MobjSpawn.
addHook("MobjSpawn", function(s)
	local p = s.player
	if BattleModCheck(s)
		s.battlemodsms = true  --Just set this here so we don't have to check every frame
	elseif p
		p.smssonicbattle = nil
		s.battlemodsms = nil
	end
	s.sprst = true
	s.mssprst = true
	if p
		if p.hypermysticsonic
			SMS_SkinSwap(p.mo, p)
		end	
		if p.mysticsuper
			A_ENDMYSTICSUPER(s)
		end
	end
end, MT_PLAYER)

--Never spawn in custom transformations!
addHook("PlayerSpawn", function(p)
	if p.sweepangle == nil
		SetUpSMSreborn(p) -- ♫ You will know our variables ♫ 
	end local s = p.mo
	if p.hypermysticsonic
		if s --Set it all RIGHT now!
			SMS_SkinSwap(s, p) --SkinSwap also performs HMSstats.
		else
			p.wasHMS = true --Do it later, I guess...
			p.hypermysticsonic = 0
		end
	end	
	if p.mysticsuper and s
		A_ENDMYSTICSUPER(s)
	end	
end)

--When players join a netgame, set this up!
local function SetUpSMSrebornJoin(p)
	local DONTSYNC = false
	local SYNCLEVEL
	
	if splitscreen --Cases where syncing is unnecessary.
	or not netgame
	or SMS_Admin(p) == 3
		DONTSYNC = true
		SYNCLEVEL = 7 --Always perfect.
	end

	if not DONTSYNC
	and p.sms_joinsync != 1
	
		local TOTALPLAYERS = 0
		for z in players.iterate
			if z.hypermysticsonic --Treat every HMS in the game like 4 players worth of performance.
				TOTALPLAYERS = $+4
			else
				TOTALPLAYERS = $+1
			end
		end
		local CURRENT = SMS_Graphix(s) --Change graphics automatically depending on playercount.
		if TOTALPLAYERS
		and SMS_AutoGraphix(p,p) == true
			if (TOTALPLAYERS < 12)			
				if (TOTALPLAYERS < 8)
					for z in players.iterate //Full steam ahead!
						if CURRENT != 0
							CONS_Printf(z, "SMS graphics auto-adjusted to\x83 full")
						end
						z.smsgraphix = 0
					end
				else
					for z in players.iterate //Slow it down a bit....
						if CURRENT != 1
							CONS_Printf(z, "SMS graphics auto-adjusted to\x84 medium")
						end
						z.smsgraphix = 1
					end
				end
			else
				for z in players.iterate //Busy netgame, minimum graphics!			
					if CURRENT != 2
						CONS_Printf(z, "SMS graphics auto-adjusted to\x85 minimum")	
					end
					z.smsgraphix = 2
				end
			end
		end
	end
	if not p.didsmsautoconfig
		if netgame
			if not SMS_Admin(p) == 3
			and ((leveltime%4==0) or (not netgame))
				SMS_LoadGame(p)
				if not p.didsmsautoconfig
					p.didsmsautoconfig = 1
				end
			end
			if (gametype != GT_COOP) and not p.smsfirstcolor
				if (A_setSMSspecialcolors(p, P_RandomRange(1,33)))
					p.smsfirstcolor = true
					CONS_Printf(p, "\x85"+"No saved SMSreborn options present! Picking random colorscheme for SMS...")
				end
			end
		end
	end
	
	--Now that our save is loaded, sync up with all admin values!
	if not DONTSYNC
	and p.sms_joinsync != 1
		for z in players.iterate --Find a player to sync with!
			if z.valid and z != p and not p.sms_joinsync
				p.smsautographix = SMS_AutoGraphix(p)
				p.hmsmlimit = z.hmsmlimit
				p.smstransformonly = z.smstransformonly
				p.hmsmlimit = z.hmsmlimit
				p.hmstransformdisable = z.hmstransformdisable		
				p.powersharerestrict =  z.powersharerestrict		
				p.smsgraphix = z.smsgraphix
				p.smsnoracegame = z.smsnoracegame	
				p.smsnomatchgame = z.smsnomatchgame		
				p.smsnobmgame = z.smsnobmgame		
				p.smsnomsspawn = z.smsnomsspawn
				p.nosmscolorspls = z.nosmscolorspls
				p.glitchydebugthrow = z.glitchydebugthrow
				SYNCLEVEL = 1
				break --We synced, so stop!
			end
		end
	end
	
	p.sms_joinsync = SYNCLEVEL --7 is perfect.
	
	CONS_Printf(p, "\x82"+"----------"+"\x85"+"==============="+"\x82"+"------------"+"\n")
--	COM_BufInsertText(p, "exec SMSreborn.cfg") --Can't do this anymore in 2.2.11+ for security reasons or whatever.
	CONS_Printf(p, "\n"+"\x82"+"Super Mystic Sonic Reborn\x80 [SRB2 2.2.13+] - By\x82 Golden Shine"+"\n")	
	
	if p.bot
		p.smsdontgrabme = 2
	end		
	if p.mo and p.mo.valid
		local s = p.mo
		if BattleModCheck(s) --Set this here too.
			s.battlemodsms = true
		else
			p.smssonicbattle = nil
		end
		p.sms_joinsync = 7 --Full sync!
		if p.smstransformonly and (s.skin == "sms") and not p.sms_transformstate
			A_SMSTOSONIC(s, nil, true, true) //Cancel the mobjthinker if switching to Sonic or your stats will mess up.
			return true --
		end
	end
end

--Second Mobj Spawn specifically for the Mystic Emerald appearing.
local function SetUpSMSrebornmo2(s, p)
	if s.mssprst == false or s.smshudgimmick --If not nil or true, then a player somewhere already did this check.
		return --
	end
	
	if (gametype == GT_TAG) 
	or (gametype == GT_HIDEANDSEEK)
	or (p.powers[pw_carry] == CR_NIGHTSMODE) --Never in these gamemodes!	
	
		for z in players.iterate
			z.spawnSMSship = false
			if z.mo z.mo.mssprst = false end
		end
		return --
		
	else --Okay, go ahead!
		
		--Are we in a special HUD-gimmick level, like the Puyo Puyo stage?
		if (gamemap == 103 or gamemap == 40)
		and not s.smshudgimmick
			if s.subsector and s.subsector.sector
				local SEC = s.subsector.sector --Player's spawn sector.
				if SEC.ceilingpic == "PIT" and SEC.floorpic == "PIT"
					if SEC.lightlevel == 0 
					or SEC.floorheight == -1024<<16 and (SEC.lightlevel < 64)
						for z in players.iterate
							z.spawnSMSship = false					
							if z.mo
								z.mo.smshudgimmick = true --We spawned in a gimmick level, don't spawn Mystic Emerald!
								z.mo.mssprst = false
							end
						end
						return --
					end
				end
			end
		end
		
		if p.smsnomsspawn
			return --Can't spawn Emerald with this setting.
		end

		p.spawnSMSship = true	
		for m in mobjs.iterate(mobjs) --Look for a nearby boss. If it's there, don't spawn the emerald!
			if m.valid and m.flags
				if (m.flags & MF_BOSS)
				or (m.type == MT_METALSONIC_RACE)
					if m.health and (FixedHypot(m.x-s.x, m.y-s.y) <= 6000*s.scale)
						for z in players.iterate
							z.spawnSMSship = false
							if z.mo z.mo.mssprst = false end
						end
						return --
					end
				end
			end
		end
		
		--Assign the Mystic Emerald object to someone.
		if not p.smsnomsspawn and p.spawnSMSship and multiplayer	
		
		or p.spawnSMSship and (s.skin == "sms" or A_SMScompatibleskin(s))
		
			if not s.smshudgimmick and not p.powers[pw_carry]
			
				local SPAWNEDGEM
				
				for z in players.iterate
					if not (z.smsssp and z.smsssp.valid)
						if SPAWNEDGEM
							z.smsssp = SPAWNEDGEM
						elseif z.mo
							z.smsssp = P_SpawnMobjFromMobj(z.mo, 0,0, 24*z.mo.scale, MT_THEMYSTICEMERALD)
							z.smsssp.tracer = s
							SPAWNEDGEM = z.smsssp
						end
					end			
				end			
			end
		end
		for z in players.iterate
			z.spawnSMSship = false
			if z.mo z.mo.mssprst = false end
		end
	end
end

--Extra instant setup.
for z in players.iterate
	if z and z.valid
		if z.sweepangle == nil SetUpSMSreborn(z) end
		if z.mo and z.mo.sprst == nil SetUpSMSrebornmo(z) end
	end
end

//Let's go into the meat of things, the player's MobjThinker! Because who needs PlayerThink? (Which didn't exist in early 2.1)
addHook("MobjThinker", function(s) if not s.player return end --
	local p = s.player local yu = p.smsrtable --Yuji Uekawa, here?? More likely than you think.
	if p.sms_joinsync != 7
		if SetUpSMSrebornJoin(p) --Whenever a player starts a game, joining netgame or not.
			return --If it's true, you were switched to Sonic, so stop the mobjthinker here.
		end
	end		
	--We just spawned!
	if s.sprst	
		SetUpSMSrebornmo(p) --Whenever a player's mo spawns.
	end	
	--Second spawn function! It spawns the Mystic Emerald where applicable.
	if s.mssprst 
		SetUpSMSrebornmo2(s, p)
		s.mssprst = false
	end
	
	 --Squash and stretch animation features. Hopefully I didn't overdo it, feel free to bitch at me if I did!
	if s.SMS_squashtable
		SMS_SquashAndStretch(s)
	end
	if s.SMSclosest
		s.SMSclosest = nil
	end
	--Timers that apply to all skins
	if s.smslsaret --LSA = Light Speed Attack
		s.smslsaret = $-1
	end
	if s.smsldret --LD = Light Dash
		s.smsldret = $-1
	end
	if s.smslsacheck //Here starts the era of the thousand timers!
		s.smslsacheck = $-1
		if not s.smslsacheck
			s.lsatarget = nil
		end
	end
	if s.lsaringdebt
		s.lsaringdebt = $-1
	end
	if s.SMS_WallBounce --We've hit a wall recently.
		s.SMS_WallBounce = $-1
	end		
	if s.smsldcheck
		s.smsldcheck = $-1
		if not s.smsldcheck and (s.state != S_SMS_LIGHTDASH)
			s.smsrdash = nil
			s.lastldrx,s.lastldry = 0,0
		end
	end
	if s.hmsmwh --Wind sound timer+object.
		if not (s.smssoundobj2 and s.smssoundobj2.valid and s.smssoundobj2.health)
			if (s.smssoundobj3 and s.smssoundobj3.valid and s.smssoundobj3.smsmwtim)			
				s.smssoundobj3.state = S_NULL
			end
			s.smssoundobj2 = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSSOUNDOBJ)
			s.smssoundobj2.master = s
			s.smssoundobj2.health = 1
			S_StartSound(s.smssoundobj2, sfx_machwh)
			s.smssoundobj2.smsmwhtim = 51 //74 for normal
		end
		s.hmsmwh = $-1
	end
	if s.hudboost --Machspeed HUD effect scaler.
		s.hudboost = $+1
		if (s.hudboost > 7)
			s.hudboost = 0
		end
	end
	if s.SMS_FireHUD --Table that adds flamewalls to the HUD.
		SMS_FireHUD(s, p, s.SMS_FireHUD)
	end
	if s.hmscarryspinpass --For HMS passengers
		s.hmscarryspinpass = $-1
	end
	if s.SMS_lemmego --Escape from SMS' pickup grab.
		s.SMS_lemmego = $-1
	end
	if s.iampickedupbysmshelp --Currently picked up by SMS.
		s.iampickedupbysmshelp = $-1
	end
	if s.ibethrownbysmshelp --Currently in a thrown-by-SMS state.
		s.ibethrownbysmshelp = $-1
		if not s.ibethrownbysmshelp
			s.spritexscale,s.spriteyscale = FRACUNIT+16000,FRACUNIT-14000
			s.SMS_squashtable = {timer = 4}
			s.rollangle = 0
		end
	end
	if s.sms_auradelay
		s.sms_auradelay = $-1
	end
	if s.hmspickedup
	s.hmspickedup = $-1
		if not s.hmspickedup
			if s.hmspickuptarget != nil and s.hmspickuptarget.valid
				s.hmspickuptarget.occupiedcarrying = nil
			end
			s.occupiedcarrying = nil
		end
	end
	if s.sdodgeoh
		s.sdodgeoh = $-1
		if not s.sdodgeoh
			s.dodgespinslide = false			
		elseif P_IsObjectOnGround(s) and not p.sms_dodging
			s.dodgespinslide = true
		end
	end
	if p.srgorelvl --Only spawn so many robot gibs at once, will you?
		p.srgorelvl = max($-1, 0)
		if (p.srgorelvl > 170)
			p.srgorelvl = min(250, $-2)
		end
	end
	if s.hmsshieldconsume --Used for misc purposes too, not just HMS shields ring boosts.
		s.hmsshieldconsume = $-1
	end
	if s.hmsshieldconsumecd
		s.hmsshieldconsumecd = $-1
	end		
	if p.smsgriefw --Anti-griefing message.
		p.smsgriefw = $-1
	end
	--Rag Doll through the air after a particularly nasty hit!
	if s.SMS_CrashRoll
		SMS_RagDoll(s)
	end
	--Leave behind your chosen trail types!
	if s.SMS_DLtrail
		SMS_ContinuousTrail(s, s.SMS_DLtrail)
	end
	if s.forcesmsangletimer
		if (p.pflags & (PF_DIRECTIONCHAR|PF_ANALOGMODE)) and s.forcesmsangle != nil
			p.drawangle = s.forcesmsangle
		end
		s.forcesmsangletimer = $-1
	end
	if s.hmstorvictim --Caught in HMS' tornado.
		s.hmstorvictim = $-1
		s.hmstorvictimang = (s.hmstorvictimang != nil) and s.hmstorvictimang+FixedAngle(8<<16) or p.drawangle
		if (s.state == S_PLAY_ROLL) or (p.panim == PA_ROLL) or (p.panim == PA_JUMP)
			if leveltime % 77 == 0
				p.pflags = $|PF_JUMPED & ~(PF_THOKKED|PF_SPINNING)
			end
		end
		p.drawangle = s.hmstorvictimang
		s.rollangle = s.hmstorvictimang
		if not s.hmstorvictim and s.rollangle
			s.rollangle = 0
		end
	end
	--Stand still for a while and you can be grabbed.
	if p.smsdontgrabme != 1
	and (not p.cmd.buttons and (abs(p.cmd.sidemove) < 3) and (abs(p.cmd.forwardmove) < 3))
		if p.smsdontgrabme == 2
			s.iasmsp = 140
		end		
		s.iasmsp = s.iasmsp and $+1 or 1
		if s.iasmsp < 55
			s.iasmsp = $+1
		end
	else
		s.iasmsp = 0
	end
	
	//Debug code to test Mystic Supers/Spawn Mystic Emeralds.
/*	if (s.skin != "sms")
		if p.rings and not p.mysticsuper 
		and (p.cmd.buttons & BT_CUSTOM2) and (p.cmd.buttons & BT_CUSTOM1) and (p.cmd.buttons & BT_CUSTOM3)
			A_STARTMYSTICSUPER(s)
		end
	else
		if (p.cmd.buttons & BT_CUSTOM2) and (p.cmd.buttons & BT_CUSTOM1) and (p.cmd.buttons & BT_CUSTOM3) and p.speed == 0 and not p.sms_flamedelay
		P_SpawnMobjFromMobj(s, 0, 0, 0, MT_THEMYSTICEMERALD) p.sms_flamedelay = 105
		end
	end */

	--First, things that affect everyone but SMS!
	if (s.skin != "sms")
		if s.AmSuperMysticSonic
			SMS_SkinSwap(s, p) --We just swapped! Keep this in mind, it'll be important!
		end
		
	--=====================SMS skin check!===========================
	else //if (s.skin == "sms") --BEGIN!!
	--===============================================================
		if s.AmSuperMysticSonic == false --PS: only activate when false, not when nil!
			SMS_SkinSwap(s, p)
		end
		
		s.AmSuperMysticSonic = true 
		
		--Update our controls every frame. yu = p.SMSrtable		
		local forwardmove = abs(p.cmd.forwardmove)
		local sidemove = abs(p.cmd.sidemove)
		if forwardmove > sidemove
			yu.direct = forwardmove
		else
			yu.direct = sidemove --Decide how hard we're pressing the stick! From 0 to 50!
		end
		yu.turnang = (p.cmd.angleturn<<16)+(R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
		
		--S timers for SMS specifically!
		if s.atomdiveusebuffer
			if not (s.state == S_SMS_ATOMDIVE)
				s.atomdiveusebuffer	= $-1
			end
			if not ((p.cmd.buttons == 0) or (p.cmd.buttons & BT_SPIN))
			and not p.homing and not (p.cmd.forwardmove > 0)
			and not ((abs(p.cmd.forwardmove) > 8 or abs(p.cmd.sidemove) > 8) and (p.pflags & PF_ANALOGMODE))
		
			or p.hypermysticsonic
				s.atomdiveusebuffer = 0
				s.useatommethod = false
			end
		end
		if s.smsabsfx
			s.smsabsfx = $-1
		end
		if s.SMS_walltwitch
			s.SMS_walltwitch = nil
		end			
		if s.sms_spintrickspeed
			if not ((s.state >= S_SMS_PREHOMET1) and (s.state <= S_SMS_HOMET7))
				s.sms_spintrickspeed = nil
			else
				local MINUS = (abs(s.momz) > 7*s.scale or p.speed > 28*s.scale) and 1 or 2
				s.sms_spintrickspeed = max($-MINUS,0)
				if (s.sms_spintrickspeed > 10)
					if s.tics > 1
						s.tics = max($-2,1)
					end
				elseif s.tics > 1
					s.tics = $-1
				end
			end
		end			
		if s.sms_superspin
			s.sms_superspin = $-1
		end
		if s.haltthespam --For stomp/hyperrise/mobile charge graphics CD.
			s.haltthespam = $-1
		end			
		if s.SMS_oldspeed --Restore old pre- spring/booster speed!
			if (p.speed <= s.SMS_oldspeed-9*s.scale)
			and (yu.direct > 9)
			and (p.sms_boostlevel > 64 or (p.cmd.buttons & BT_CUSTOM1) or p.sms_autoboosthold)
			and ((abs(s.momx) > 3*s.scale) or (abs(s.momy) > 3*s.scale))
				if not (s.eflags & MFE_UNDERWATER)
				and not SMS_Locked(s,p, true)
					P_InstaThrust(s, R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy), s.SMS_oldspeed-9*s.scale)
				end
			end
			s.SMS_oldspeed = nil
		end
		if s.smsaurablastivatecd
			s.smsaurablastivatecd = $-1
		end	
		--Fine, p timers too.....
		if p.sms_aerialfirerestrict 
			if (P_IsObjectOnGround(s) or (p.pflags & PF_JUMPED))
				p.sms_aerialfirerestrict = nil
			end
		end
		
		--Many other button timers are in LUA_SMS2.
		p.weaponprevhold = (p.cmd.buttons & BT_WEAPONPREV) and $+1 or 0
		p.sms_flamebuttonhold = (p.cmd.buttons & BT_ATTACK) and $+1 or 0
		p.sms_nflamehold = (p.cmd.buttons & BT_FIRENORMAL) and $+1 or 0
		p.sms_c1 = (p.cmd.buttons & BT_CUSTOM1) and $+1 or 0
				
		if s.SMS_MineCarting
			s.SMS_MineCarting = $-1
			if p.powers[pw_flashing] --Lets you ragdoll of ramps after getting hurt!
			and (s.momz*P_MobjFlip(s) > 0)
			and not (p.pflags & (PF_SPINNING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_STARTJUMP|PF_STARTDASH))
			and not p.hypermysticsonic
			and not p.hometrickanim
			and not p.powers[pw_carry]				
				P_SetObjectMomZ(s, FRACUNIT, true)
				P_Thrust(s, R_PointToAngle(s.x, s.y, s.x+s.momy, s.y+s.momy) or s.angle+ANGLE_180, 24*s.scale)
				A_SMSquake(s, 65<<16, 4, 500)
				SMS_RagDoll(s, 2)
			end
		end
		if s.smsframejump
			s.smsframejump = $-1
		end
		if s.smshmcheck --HayMaker check timer.
			s.smshmcheck = $-1
			if not s.smshmcheck 
				s.cmphtarget = nil
			end
		end
		if s.smspkuret --Ready to pick up an item?
			s.smspkuret = $-1
		end	
		if s.hmsrunquake
			if not (s.smssoundobj5 and s.smssoundobj5.valid and s.smssoundobj5.health)
				s.smssoundobj5 = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSSOUNDOBJ)
				s.smssoundobj5.master = s
				s.smssoundobj5.health = 1
				S_StartSound(s.smssoundobj5, sfx_cdfm42)
				s.smssoundobj5.smsquaketim = 4
			elseif (s.smssoundobj5 and s.smssoundobj5.valid and s.smssoundobj5.health and s.smssoundobj5.smsquaketim)
				s.smssoundobj5.smsquakerefresh = true
			end	
			s.hmsrunquake = $-1
		end		
		if s.SMS_WallJumpTable --Walljumping! Wall Jumping. Wall Jump Table...WJ.timer?
			if not s.SMS_WallJumpTable.timer --That's not right...!
			or s.skin != "sms"
				s.SMS_WallJumpTable = nil
				s.spriteyscale = FRACUNIT
				s.spritexscale = FRACUNIT
				s.rollangle = 0
			else
				if P_IsObjectOnGround(s) and not (s.momz*P_MobjFlip(s) > 0) --Instantly skip in this case.
				or p.hypermysticsonic
				or (s.SMS_WallJumpTable.timer != 22) and ((s.eflags & MFE_SPRUNG) or SMS_Locked(s, p, true))
					s.SMS_WallJumpTable.timer = 1 --The hook will finish things if it's set to 1.
				end
				s.smspickupCD = 2
				--Do Wall Jump Behaviour!
				SMS_WallJumping(s, p, s.SMS_WallJumpTable, p.smsrtable)	
			end
		elseif s.hmsspinpass
			if p.sms_stompanim and s.stompanimburst and (s.stompanimburst > 8)
				--
			elseif (p.hypermysticsonic < 5)
			or SMS_Locked(s, p)
			or P_IsObjectOnGround(s)
			or p.hmsdash and (p.speed > 230*s.scale)
			or (p.pflags & (PF_STARTDASH))
			or s.momz == 0
				s.hmsspinpass = 0
			end
		end		
		if s.hmsroflstomped and (s.rollangle == ANGLE_180)
			if not p.hms_stompbreak or not (p.cmd.buttons & BT_CUSTOM2)
				if s.hmsroflstompcd == nil
					s.hmsroflstompcd = 1				
				else
					s.hmsroflstompcd = $+1
				end
			end
			if (p.hypermysticsonic < 5)
			or P_IsObjectOnGround(s) 
			or (s.hmsroflstompcd and (s.hmsroflstompcd > 7))
			or s.airboostroll
			or p.hmsdash and (p.speed > 230*s.scale)
			or s.hms_jumprisen
			or (p.pflags & (PF_STARTDASH))
			or SMS_Locked(s, p)
			or s.momz == 0
			or (s.state == S_PLAY_PAIN and p.powers[pw_nocontrol] == 2)
				s.hmsroflstomped = false
				s.rollangle = 0
				s.hmsspinpass = 0
			end
		elseif s.hmsroflstompcd
			s.hmsroflstompcd = 0
		end
		if s.smspickupCD --Cooldown for pickup.
			s.smspickupCD = $-1
		end			
		if s.smsstompcd
			s.smsstompcd = $-1
		end
		if s.SMS_WJpenalty
			if p.powers[pw_carry]
			or P_PlayerInPain(p)				
				s.SMS_WJpenalty = 0
				s.SMS_lastwallANG = nil
				s.SMS_lastwallNUM = nil
			else
				s.SMS_WJpenalty = min($-1, 260)
				s.SMS_HUDorb = s.SMS_HUDorb and min($+1,5) or 1
				for i = 1,7
					if (s.SMS_WJpenalty > 180)
						s.SMS_WJpenalty = $-1
					else
						break
					end
				end
				if (s.eflags & MFE_UNDERWATER) or (s.momz*P_MobjFlip(s) <= -5*s.scale) --Regain it faster if you're losing height.
					s.SMS_WJpenalty = max($-2, 0)
					if (s.eflags & MFE_GOOWATER)
						s.SMS_WJpenalty = min($, 30)
					end
				end
			end
		else
			if s.SMS_HUDorb 
				s.SMS_HUDorb = $-1
			end
			if s.SMS_WJpenalty == nil --Special treatment.
				s.SMS_WJpenalty = 0
			end
		end
		if s.suddenquickspeed
			s.suddenquickspeed = $-1
			if s.suddenquickspeed <= 1
				s.flags2 = $ & ~MF2_DONTDRAW
			else
				if p.hypermysticsonic
					s.flags2 = $|MF2_DONTDRAW
				else
					s.suddenquickspeed = 0
					s.flags2 = $ & ~MF2_DONTDRAW
				end
			end
		end			
		if s.DeyCURollin
			s.DeyCURollin = $-1
			if not s.DeyCURollin and p.powers[pw_carry] == CR_ROLLOUT
				p.powers[pw_carry] = 0 //Get lost, rock.
			end
		end
		if s.sms_boostloss --.lostboost?
			if not (p.sms_boostlevel >= 65)
				if s.sms_boostloss == 12
					if p.hypermysticsonic
					or p.sms_ringburstfly
					or p.sms_ringburst
					or p.sms_comethaymaker
					or p.mslightspeedattack
					or p.sms_hugeflametoss
					or p.footsweepanim

						s.sms_boostloss = 0
						
					else
						local ghs = SMSRSpawnGhost(s, 9, true)
						ghs.color = SKINCOLOR_SMSROVERGLOWRED
						ghs.colorized = true
						ghs.destscale = $*99
						for i = 3,7,2
							ghs = SMS_WallJumpGFX(s, true)
							ghs.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS & ~RF_PAPERSPRITE
							ghs.spriteyscale = $/(i-1)
							ghs.spritexscale = $/(i-1)
							ghs.color = SKINCOLOR_RED-3+i
							ghs.colorized = true
							ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
						end
						ghs = P_SpawnMobjFromMobj(s,s.momx,s.momy,0, MT_THOK)
						ghs.flags2 = MF2_DONTDRAW ghs.tics = 99
						S_StartSoundAtVolume(ghs, sfx_smsfx9, 140)
					end	
				end
				if s.sms_boostloss > 8
					SMS_DiffusionChargeSparkles(s, p, SKINCOLOR_CRIMSON)				
				end
				s.sms_boostloss = max($-1,0)
			end
		end			
		if s.smsbdsfx --Blind dash voiceclip cooldown
			s.smsbdsfx = $-1
		end
		if s.airboostroll 
		
			if (((s.state == S_SMS_AIRBOOST) or (s.state == S_PLAY_PAIN) or (s.state == S_SMS_LIGHTDASH)) or p.hypermysticsonic)
			
			and not P_IsObjectOnGround(s)
		
			and not ((p.speed < 30*s.scale) and (s.state != S_PLAY_PAIN) and (s.state != S_SMS_LIGHTDASH))
			
			and not (s.flags2 & MF2_TWOD)
			
				if (s.airboostroll == 1)
					s.airboostroll = 0
					s.rollangle = 0
				else
					s.airboostroll = $-1
					if s.hmsrotateright
						s.rollangle = $-FixedAngle(53<<16)
					else
						s.rollangle = $+FixedAngle(53<<16)
					end
				end
			else
				s.airboostroll = 0
				s.rollangle = 0
			end
		elseif s.hmscchange and not s.smstrickroll 
			p.viewrollangle = 0
			s.hmscchange = false
		end
		if s.useatommethod
			if not (p.cmd.buttons & BT_SPIN) or not (s.state == S_SMS_ATOMDIVE)
				s.useatommethod = false
			end
		end	
		if s.SMS_wallready
			s.SMS_wallready = $-1 
		end
		if s.SMS_wallwait
			s.SMS_wallwait = $-1 
			if P_IsObjectOnGround(s)
			or p.powers[pw_carry]
				s.SMS_wallwait = 0
				s.SMS_lastwallANG = nil
				s.SMS_lastwallNUM = nil
			end
		end		
		if s.SMS_removeWD
			if (s.SMS_removeWD > 1)
				s.SMS_removeWD = $-1
			elseif not s.SMS_wallready
				s.SMS_removeWD = $-1
				if not s.SMS_removeWD
					SMS_WallJumpDelete(s)
				end
			end
		end
		if s.smsboostimmune --Can't lose boostmode easily with this on.
			s.smsboostimmune = $-1
		end
		--Your Comet Haymaker target will glow!
		if s.smscmphret
			s.smscmphret = $-1
			--Make target glow!
			if s.cmphtarget2
				if s.cmphtarget2.valid
					local t = s.cmphtarget2
					if t.sprite and not (t.flags2 & MF2_DONTDRAW)
						local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_SMSAUTOFRAME)
						ghs.sprite = t.sprite
						ghs.frame = (t.frame & ~FF_TRANSMASK)|TR_TRANS30
						ghs.blendmode = AST_ADD
						ghs.renderflags = t.renderflags
						ghs.angle = t.angle
						ghs.scale = t.scale
						ghs.momx = t.momx
						ghs.momy = t.momy
						ghs.momz = t.momz
						ghs.rollangle = t.rollangle
						ghs.spritexscale = t.spritexscale
						ghs.spriteyscale = t.spriteyscale
						ghs.mirrored = t.mirrored
						ghs.colorized = true
						ghs.tics = 2 ghs.fuse = 9
						if leveltime % 2 == 0
							ghs.color = SKINCOLOR_ORANGE
						else
							ghs.color = SKINCOLOR_SUPERGOLD5
						end
					end
				else
					s.cmphtarget2 = nil
				end
			end
		end
		if s.readyforonemoreplant
			s.readyforonemoreplant = $-1
		end			
		
		--Lightning Homing Bolt Ability, rocket skywards after hitting an enemy!
	/*	if s.SMS_HomingBolt
			SMS_HomingBolt(s,p, s.SMS_HomingBolt)
		end */
		if s.sms_jumpbuffer 
			s.sms_jumpbuffer = $-1
		end
		if (p.cmd.buttons & BT_JUMP)
			p.sms_jumphold = $+1
						
			--Jump button buffering system!
			
			if (p.sms_jumphold == 1 or s.sms_jumpbuffer)
			and not p.hypermysticsonic 
			and not (p.smsdivetimer and not s.smsdropdash)
			and not P_PlayerInPain(p)
			and not SMS_Locked(s, p)
			
				local anim = 0						

				if (p.pflags & PF_STARTJUMP) and (s.momz >= 3*s.scale*P_MobjFlip(s)) and (p.pflags & PF_JUMPED)
					s.sms_jumpbuffer = 0 --Abort.
				elseif ((p.panim >= PA_WALK) and (p.panim <= PA_PAIN))
				or ((s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04))
				or (p.pflags & PF_SPINNING) and not (p.pflags & (PF_STARTDASH|PF_JUMPED|PF_SLIDING|PF_STARTJUMP)) and (p.panim == PA_ROLL)
					anim = 1 --Ideal conditions!
				elseif ((p.panim >= PA_SPRING) and (p.panim <= PA_ABILITY)) and (s.momz < 3*s.scale*P_MobjFlip(s))
				or (s.state == S_SMS_ADVFALL1) or (s.state == S_SMS_ADVFALL2)
				or (s.state == S_SMS_AIRBOOST)
				or ((s.state == S_SMSWATERGRIND1) or (s.state == S_SMSWATERGRIND2)) and not (s.eflags & MFE_TOUCHWATER)
				or (p.panim == PA_ROLL) and (((p.pflags & PF_THOKKED) and (p.pflags & PF_JUMPED)) or s.smsdropdash)
					anim = 2 --Good enough for activating the buffer, at least!
				end
				if anim != 0		
					if p.sms_jumphold == 1
						if not P_IsObjectOnGround(s)
						and not (p.pflags & (PF_STARTJUMP|PF_JUMPSTASIS))
							s.sms_jumpbuffer = 8 --You get 8 frames of buffer!
						end	
					end
					if s.sms_jumpbuffer		
						if anim == 1
						and (p.pflags & PF_JUMPDOWN) 
						and not (p.pflags & (PF_STARTJUMP|PF_JUMPSTASIS|PF_JUMPED))
						and (p.sms_jumphold < 14)
							if P_IsObjectOnGround(s)
								p.pflags = $ & ~PF_JUMPDOWN
							elseif s.smsframejump
								s.bufferframejump = true
							end
						end
					end
				end				
			end				
		else
			p.sms_jumphold = 0
			if s.sms_jumpbuffer and P_IsObjectOnGround(s)
				s.sms_jumpbuffer = 0
			end
		end
		
		if p.bot
			if (p.speed < 10<<16) and P_IsObjectOnGround(s)
				s.smsbottf = s.smsbottf and $+1 or 1
			else
				s.smsbottf = 0
			end
			if p.dobotpowershare
				p.dobotpowershare = $-1
			end
		end
		if not s.battlemodsms
			if SMS_NerfedMode(p)
				if p.smsnomatchgame
					P_FlashPal(p, PAL_NUKE, 4)
					S_StartSound(s, sfx_lose)
					CONS_Printf(p, "\x85"+"SMS is not allowed in Matches right now!")	  
					A_SMSTOSONIC(s, nil, true, true)
					return --
				end
			elseif p.smstransformonly and not p.sms_transformstate
				P_FlashPal(p, PAL_NUKE, 4)
				S_StartSound(s, sfx_lose)
				CONS_Printf(p, "\x85"+"SMS is available only as transformation now!")	  
				A_SMSTOSONIC(s, nil, true, true) --Don't continue any further in this case.
				return --
			end
		end
		
		
	//SMS' strong spring code. Mimmicks the way Amy interacts with springs, but is tweaked for horizontal springs.
	
		if s.vertstrongspring
			if not P_PlayerInPain(p)
				if s.horstrongspring2 --Horizontal boost from a strictly horizontal spring.
					s.momx = $*7/4
					s.momy = $*7/4
				else
					if s.vertstrongspring --Vertical boost.
						s.momz = $*40/33
					end
					if s.horstrongspring --Horizontal boost from a diagional spring.
						s.momx = $*5/4
						s.momy = $*5/4
					end
				end
				s.vertstrongspring = false
				s.horstrongspring = false
				s.horstrongspring2 = false
			end
		end		
		if (p.powers[pw_sneakers] and (s.battlemodsms or SMS_NerfedMode(p)))
			p.mxscap = 2
			if p.powers[pw_sneakers] == 699
				p.powers[pw_sneakers] = 698
				P_FlashPal(p, PAL_RECYCLE, 3)
				CONS_Printf(p, "\x82"+"Combat shoes powerup! Combat moves drain no rings for a while!")
				S_StartSound(s, sfx_steam2)
			end
		else
			p.mxscap = 1
		end
	  
	if p.sms_boostlevel
		if p.hypermysticsonic
		or not s.health --Can't do that, chief.
		or p.knucklewaving
			p.sms_boostlevel = 0
			s.sms_boostloss = 0
		elseif (p.sms_boostlevel > 32) and (p.sms_boostlevel < 66) --Almost there...!
		and (p.panim == PA_RUN)
		and P_IsObjectOnGround(s)
			local ghs = P_SpawnMobjFromMobj(s, FixedMul(24*s.scale, cos(s.angle+ANGLE_90)),FixedMul(24*s.scale, sin(s.angle+ANGLE_90)), 0, MT_SPINDUST)
			ghs.frame = SMSLocalRNG(4,2,s)-2|FF_TRANS10
			ghs = P_SpawnMobjFromMobj(s, FixedMul(24*s.scale, cos(s.angle-ANGLE_90)), FixedMul(24*s.scale, sin(s.angle-ANGLE_90)), 0, MT_SPINDUST)
			ghs.frame = SMSLocalRNG(0,2,s)|FF_TRANS10
		end
	end
	
	if ((p.cmd.buttons & BT_CUSTOM1) or p.sms_autoboosthold)
	and not (p.smsblastboost and not p.hypermysticsonic)
	and (p.sms_ringburstfly != 1)
		
	//Super Sonic X Aura! ...or Ultra Instinct. Your pick!
		if p.panim == PA_IDLE
			if s.state == S_PLAY_STND
				if (s.tics == 1)
					s.tics = 9
				end
			elseif s.state == S_PLAY_WAIT			
				s.state = S_PLAY_STND
			end
		end
		if s.health
			s.smsglowticket = 2 --Are you not usually a glowing color? Well, you get to glow today!
		end	
		if (p.speed < 99*s.scale) and not (p.sms_boostlevel > 64) and s.health
			s.ssxaticket = true
		end		

		if SMS_NerfedMode(p)
					
			if p.rings
			and (p.sms_boostlevel < 65)
			and not p.gotflag
			and not p.smsblastboost
			and not (p.pflags & PF_STASIS)
			and not p.sms_dodging
			and not (s.eflags & MFE_UNDERWATER and not p.hypermysticsonic)
				if p.hypermysticsonic
					s.hmgodticket = true
					p.normalspeed = (242<<16)
				else
					p.normalspeed = (121<<16)/p.mxscap
				end

			elseif (((p.normalspeed != (60<<16)/p.mxscap) and not p.hypermysticsonic)
			or (p.hypermysticsonic and (p.normalspeed != (120<<16))))
			and not (p.sms_boostlevel >= 65)
			and not p.gotflag
				if p.hypermysticsonic
					p.normalspeed = (120<<16)
				else
					p.normalspeed = (60<<16)/p.mxscap
				end
				p.thrustfactor = 7
				p.sms_boostlevel = 0
			end				
			
		elseif s.health --NOT match modes.
		
			if (p.sms_boostlevel < 65)
			and not s.SMS_locked 
				if not ((s.eflags & MFE_UNDERWATER) and not p.hypermysticsonic)
				and not p.smsblastboost
					if p.hypermysticsonic
						s.hmgodticket = true
						if p.isxmomentum and (yu.direct > 8)
						and not (p.cmd.forwardmove < -41)
							p.normalspeed = max($,242<<16)
						else
							p.normalspeed = 242<<16
						end
					else
						p.normalspeed = (121<<16)/p.mxscap
					end
				elseif ((p.normalspeed != (60<<16)/p.mxscap) and not p.hypermysticsonic)
				or (p.hypermysticsonic and (p.normalspeed != (120<<16)))
					if not (p.pflags & PF_STASIS) //No control? Wait a little bit!
						if p.hypermysticsonic
							p.normalspeed = (120<<16)
						else
							p.normalspeed = (60<<16)/p.mxscap //Normal speed. Still pretty speedy, though!
						end
					end
				end
			end
		end
			
	elseif p.smsblastboost --Base speed is barely over Sonic's in Legacy mode.
		p.normalspeed = (45<<16)/p.mxscap
	end
	
	
	
		--last speed was...?	
		if not s.smsspeedticket and (p.speed > 78*s.scale)
			s.smsspeedticket = true
		end
		--Speed stats!
		if p.smsblastboost and not p.hypermysticsonic
		and not p.gotflag
		
			p.normalspeed = (45<<16)/p.mxscap	
			p.runspeed = 26<<16
			s.changedmyrsd = true
			
		elseif (((p.normalspeed != (60<<16)/p.mxscap) and not p.hypermysticsonic) or (p.hypermysticsonic and (p.normalspeed != (120<<16))))
		and (p.sms_boostlevel < 65)
		and not ((p.cmd.buttons & BT_CUSTOM1) or p.sms_autoboosthold) //Failsafe!
		and not p.gotflag
		
			if p.hypermysticsonic
				p.normalspeed = 120<<16
			else
				p.normalspeed = (60<<16)/p.mxscap
			end
		end 

		//When SMS holds a flag, nerf him please for the love of god!

		if (p.gotflag or (s.eflags & MFE_UNDERWATER))
		and not p.hypermysticsonic
			if p.gotflag
				p.normalspeed = 1965000/p.mxscap
				p.mindash = 40<<16
				p.maxdash = 71<<16
				p.sms_boostlevel = 0
				if (p.speed >= 88*s.scale) --No carrying over stupid amounts of momentum with the flag, pls...
				and not p.powers[pw_carry]
				and not (s.eflags & MFE_SPRUNG)
					P_InstaThrust(s, s.angle, 60*s.scale)
				end
			elseif (s.eflags & MFE_UNDERWATER)
				p.mindash = 64<<16 //Blinddash needs to be nerfed too.
			end
		elseif p.mindash == 40<<16 or p.mindash == 64<<16
			p.mindash = 101<<16
			if not p.hypermysticsonic
				p.maxdash = 147<<16
			end
		end

		if not p.hypermysticsonic		
			if p.secondjump == 167 -- SMSblast springjump!
				if p.smsblastboost
					if (s.state == S_PLAY_ROLL) or (p.pflags & PF_JUMPED)
						s.state = S_PLAY_SPRING
						s.eflags = $|MFE_SPRUNG
						p.pflags = $ & ~PF_JUMPED
					elseif (s.state == S_PLAY_SPRING) and (s.momz*P_MobjFlip(s) > 5*s.scale)
						SMSRSpawnGhost(s, SMSLocalRNG(3,4,s))
					end
				else
					p.secondjump = 0
				end
			end
			--Speed Thok animation!
		  	if s.smsthokanim
	  			if p.panim == PA_ROLL or p.panim == PA_JUMP or p.panim == PA_FALL
	  	 			s.state = S_SMSTHOKANIM
					if not p.sms_oldhoming
						if not p.homing and (p.pflags & PF_THOKKED)
							local ghs = SMS_FollowCircle(s, SKINCOLOR_WHITE, 3)
							ghs.scalespeed = $*2 ghs.fuse = 9 ghs.frame = $-FRACUNIT
							if not (s.SMS_squashtable and s.SMS_squashtable.timer > 2)
								S_StartSoundAtVolume(s, sfx_s1b8, 76)
							end
						end
						if not (s.SMS_squashtable and s.SMS_squashtable.timer > 2)
							s.spritexscale = FRACUNIT-5100
							s.spriteyscale = FRACUNIT+9100
							s.spriteyoffset = 0
							s.SMS_squashtable = {timer = 3, Xspeed = 2200, Yspeed = 3500, air = true}
						end
					end
					if (p.cmd.buttons & BT_SPIN) and not p.smsblastboost
						s.tics = 2
					end
					p.pflags = $|PF_JUMPED
				end
	  		 	s.smsthokanim = false
	 	 	end
			--Speed Shoes mode! Go into SMS' All Out mode once you collect runshoes!
			if not SMS_NerfedMode(p) and not s.battlemodsms
				SMS_AllOutMode(s, p)
			end
	  end

		//When sprinting across the ground for an extended periods of time, you can break your
		//"speed limit". We'll call it "Speed Break" for now, after Sonic and the Secret Rings' boost move. 
		
		local FAIL = false
			
		--Conditions to lose boostlvl.
		
		if P_IsObjectOnGround(s) --Can only lose boostmode on the ground.
		and (p.panim != PA_ROLL) and (s.state != S_SMS_AIRBOOST) --You're exempt when rolling!
			if ( (p.speed < 27*s.scale) and (twodlevel or (s.flags2 & MF2_TWOD)) ) --2D fall-off limit is lower.
			or (p.speed < 63*s.scale)
				FAIL = true
			end
		end
		if FAIL != true		
			if p.panim == PA_PAIN
			or p.panim == PA_RIDE --Instantly lose boostlevel in these conditions.
			or p.smsblastboost
				FAIL = true
			end
		end
				
		if FAIL == true --Lose boostmode when going too slow!	
		
			if not s.smsboostimmune --Can't lose boostmode with this active!
				p.sms_boostlevel = 1 --Set to 1, not 0.	p.sms_boostlevel = 0
			end
			
			--Calculate if we can gain boostlvl!				
		else
			if (p.speed >= 26*s.scale)
				if (p.cmd.buttons & BT_CUSTOM1) or p.sms_autoboosthold --Correct input present?
					if (s.flags2 & MF2_TWOD) or twodlevel or (p.speed >= 78*s.scale) --Speed limit
						
						if (p.panim == PA_RUN) --Usually can only gain boostlvl on the ground.								
						or P_IsObjectOnGround(s) --The run animation makes you exempt, though.

						or (leveltime % 3 == 0) --And you can always get a passive gain up to a certain point!
						and (p.sms_boostlevel < 60) --But it won't break you over the edge.
						and not p.sms_ringburstfly
							
							s.sms_addboostlevel = 1
								
							--Really fast speed gains you superlvl!
								
							if (p.speed > 105*s.scale)
							or ((s.flags2 & MF2_TWOD) or twodlevel) and (p.speed >= 39*s.scale)
								s.sms_addboostlevel = 3
							end									
						end
					end
				end
			end
		end	
		
		--Main Boost code!
		if (p.sms_boostlevel >= 65) //If the "timer" hits 65, Speed Break!
		and not p.hypermysticsonic //HMS doesn't use boostlevel, but just incase.
		and s.health
		
			s.sms_boostloss = 12
			
			if (p.pflags & PF_STARTDASH) and (p.dashspeed < p.maxdash)
				p.dashspeed = p.maxdash //Instant maxdash
				S_StartSound(s, sfx_spndsh)
				A_SMSquake(s, 65<<16, 3, 500, true)
			end
		
			p.thrustfactor = 10	--Super acceleration for improved turning speed!
			
			//Haymaker check.
		
			local ghs = P_SpawnMobj(s.x+FixedMul(570<<16,cos(s.angle)), s.y+FixedMul(570<<16,sin(s.angle)), s.z-600<<16, MT_SMSECHECK)
			if ghs and ghs.valid
				ghs.cmphsms = true
				ghs.justcheckin = true
				ghs.target = s
				ghs.height = 1200<<16
				ghs.radius = 660<<16
			end
			
			--Drifting, kicking up smoke and debris from the turning speed!
			if p.hypermysticsonic
			
				p.normalspeed = max($, 310<<16) --HMS doesn't use the boost mechanic, but just incase....
				
			elseif (s.state != S_SMS_ATOMDIVE)
			
				if P_IsObjectOnGround(s)
				and ((s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04))
				
				local drifting = false
				local momangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
				local turnang = (p.cmd.angleturn<<16)+(R_PointToAngle2(0, 0, p.cmd.forwardmove<<16, -(p.cmd.sidemove<<16)))
				local trueturn = abs(((momangle-turnang) or 1)/ANG1)			
					if (trueturn > 17) --Turning sharply at Speed Break? Drift!
						if not s.driftsens
							s.driftsens = 2
						else
							s.driftsens = min($+2, 8)
						end
						
						drifting = true
						
						if (s.driftsens > 2) //Spam drifting sounds and smoke!
					
							local ghs = P_SpawnMobjFromMobj(s, SMSLocalRNG(-67, 67,s)*s.scale, SMSLocalRNG(68, -67,s)*s.scale, 0, MT_TNTDUST)
							if ghs and ghs.valid
								P_Thrust(ghs, s.angle+ANGLE_90, 30*s.scale*SMSLocalRNG(-1,1,s)) 
								ghs.scale = $/SMSLocalRNG(1,4,s)
								ghs.destscale = $*5
								ghs.fuse = 42
								
								local ths = P_SpawnMobjFromMobj(s, SMSLocalRNG(-67, 67,ghs)*s.scale, SMSLocalRNG(-68, 67,ghs)*s.scale, 0, MT_SPINDUST)
								if ths and ths.valid
									P_Thrust(ths, s.angle+ANGLE_90, 30*s.scale*SMSLocalRNG(-1, 1, ghs)) 
									ths.scale = $*SMSLocalRNG(1,3,s)
									ths.frame = $+SMSLocalRNG(0,2,ths)
									ths.destscale = $*5
									ths.tics = 8
								end									
							end
						
							--Sound object!
							if not (s.smssoundobj and s.smssoundobj.valid)
							and not (s.eflags & MFE_UNDERWATER)
								s.smssoundobj = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSSOUNDOBJ)
								s.smssoundobj.master = s
								s.smssoundobj.health = 1
								S_StartSound(s.smssoundobj, sfx_smsdr)
								s.smssoundobj.smsdrtim = 65
							end
						end
					end
				end
				if not p.isxmomentum
					p.normalspeed = (155<<16)/p.mxscap
				else
					p.normalspeed = max($, (155<<16)/p.mxscap)
				end
				
				SMSRSpawnGhost(s, 8) --Boostmode-specific afterimage ghosts.
				
				if (p.panim == PA_RUN)
					s.state = S_SMS_BOOST01
				end	
			end
		elseif (p.thrustfactor != 7)
		and not (p.powers[pw_sneakers] and (not SMS_NerfedMode(p) and not s.battlemodsms))
			p.thrustfactor = 7			
		end
		if s.driftsens
			s.driftsens = $-1
			if (p.pflags & (PF_JUMPED|PF_SPINNING))
			or not s.driftsens
				s.driftsens = 0
				if (s.smssoundobj and s.smssoundobj.valid and s.smssoundobj.health)
					s.smssoundobj.state = S_NULL --Delete sound object.
				end
			end
		end
		if (s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04)
			if (p.speed < 69*s.scale)
				s.state = S_PLAY_RUN
				s.driftsens = 0
				if s.smssoundobj and s.smssoundobj.valid
					s.smssoundobj.state = S_NULL --Delete sound object.
				end
			end
		elseif (p.sms_boostlevel >= 65)
			if (s.eflags & MFE_UNDERWATER) or p.smsblastboost //No speed break underwater!
				p.sms_boostlevel = 0
			else
				if (p.pflags & PF_STARTDASH)
					if (p.speed < s.scale*8)
						s.SMS_BDboostloss = true --Can't haymaker with this!
					end
				elseif s.SMS_BDboostloss --Jumping out of a charge dash will lose you boostmode.
					if (p.pflags & PF_JUMPED)
					and (p.cmd.buttons & BT_SPIN)
					and (p.speed < s.scale*55)
					and not (p.sms_dodging > 12)
						p.sms_boostlevel = 43 //But retain some	boostlvl
						s.sms_boostloss = 12
					end
					s.SMS_BDboostloss = nil
				end
			end
		end

		//Forces afterimages when at extreme speeds regardless of what.
		//Also, if you have the flag and go beyond a certain speed, slow dooown!
		--Spawn windlines?
		local GOTSPEED
		
		if (p.speed >= 52*s.scale)
		and not (p.pflags & (PF_SPINNING|PF_SLIDING))	
		and not p.powers[pw_carry]
		and not (s.eflags & MFE_GOOWATER)
		and not p.homing
		and not p.smsblastboost
		and not p.sms_comethaymaker
		
			if (p.cmd.buttons & BT_CUSTOM1) or p.sms_autoboosthold
				s.smsspeedticket = true --This spawns windlines later.
				if not (p.sms_boostlevel > 65) and not (s.hmsspinpass and p.hypermysticsonic)
					SMSRSpawnGhost(s, 2, true) --Minor ghost!
				end	
			end
			
			if (p.speed >= 140*s.scale)
			and P_IsObjectOnGround(s)
				SMSRSpawnGhost(s, 5) --Strong afterimage!
				GOTSPEED = true
				s.smsspeedticket = true
				if (p.sms_boostlevel >= 65)
					A_SMSSpinFire(s, MT_SMSSPINFIRE, FRACUNIT/2)
					if not (s.scale < FRACUNIT/2) --If we're not tiny, play speeding sounds.
						s.smsgbs = s.smsgbs and $+1 or 1
						if not (s.smsgbs > 14)
							if s.smsgbs == 1
								if not ((s.smssoundobj4 and s.smssoundobj4.valid and s.smssoundobj4.health)
								and s.smssoundobj4.machwbtol2 and (s.smssoundobj4.machwbtol2 < 50))
								and not (s.eflags & MFE_UNDERWATER)
									s.smssoundobj4 = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSSOUNDOBJ)
									s.smssoundobj4.master = s
									s.smssoundobj4.health = 1
									S_StartSound(s.smssoundobj4, sfx_machwb)
									s.smssoundobj4.smsmachwb = 1
								end
								for i = 1,2
									if (p.sms_boostlevel < 106)
										local ghs = SMS_FollowCircle(s, SKINCOLOR_RED, 1+i)
										ghs.scalespeed = FRACUNIT-9900
										ghs.frame = $ & ~FF_TRANSMASK
										ghs.fuse = 10
										ghs.dispoffset = 35
										if i == 1
											SMS_QuickDustGFX(s, SKINCOLOR_EMERALD)
										else
											SMS_MediumDustGFX(s)	
										end	
									end
								
									local ghs = HMS_BigBangVortex(s, 10, nil, nil, T|(FF_TRANS50+(i<<16)), s.color) --Subtle vortex?
									ghs.tracer = s
									ghs.dispoffset = -20
									if i == 2
										ghs.ditchtracer = 1
										ghs.momx = s.momx/2
										ghs.momy = s.momy/2
										ghs.spritexscale = $-12000
										ghs.spriteyscale = $-12000
									end
									ghs.color = SMSisyellow(s) and SKINCOLOR_MYSTICAL1 or s.color
								end
							end
							local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSGROUNDBOOST)
							ghs.tracer = s
							ghs.blendmode = AST_ADD
							ghs.smsgbs = s.smsgbs
						end
					end
				end
			end
		end
		if s.smsgbs
			if not GOTSPEED
				s.smsgbs = 0
			end
		end

		//It's a bird, it's a plane, it's SUUUUPER SONIC! ...huh?
		//Actually, SMS can't fly. But he can definitly use the Air Boost. 

		if p.sms_readyairboost		
			if P_IsObjectOnGround(s)
				p.sms_readyairboost = 0
			elseif not p.sms_instantairboost
			and not ( p.sms_c1
			or (p.sms_jumphold >= 1 and (p.pflags & PF_THOKKED)) )
				p.sms_readyairboost = $-1 //Not holding dash? Then count down!
				s.readyairboostdelay = false
			end
		end
		if p.sms_didairboost
			if P_IsObjectOnGround(s)
				p.sms_didairboost = 0 
			else
				if (p.sms_didairboost > 0)
					if (p.pflags & PF_JUMPED)
					or (s.state ~= S_SMS_AIRBOOST) and not ((s.state == S_PLAY_FALL) and (s.momz*P_MobjFlip(s) < s.scale/5))
						p.sms_didairboost = 0 --Reset the SMS_didairboost state!
					else
						p.sms_didairboost = max($-1, 1)  --keep it edged at 1
						if (p.sms_didairboost > 5) and (s.state == S_SMS_AIRBOOST)
							if (yu.direct > 20) --Allow a bit of supercontrol!
							and (abs(p.cmd.sidemove) > 7)
							and not (p.cmd.forwardmove < -32)
								local FLYANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
								local ANGSHIFT,PLUS = SMS_AngleDifference(FLYANGLE, yu.turnang, 2, ANGLE_45, 2)
								FLYANGLE = $-(ANGSHIFT*PLUS)
								local MOMENTUM = p.speed/4
								s.momx = FixedDiv(FixedMul($, 100), 127)
								s.momy = FixedDiv(FixedMul($, 100), 127)
								P_Thrust(s, FLYANGLE, MOMENTUM)
							end
						end
						if (p.cmd.buttons & BT_JUMP) and (p.sms_jumphold <= 2)
						and not (p.sms_oldhoming and not (p.cmd.buttons & BT_USE))
							if SMS_TryForcedHomingAttack(s, p)
								s.SMS_DLtrail = {timer = 5, colorized = true,
								spriteyscale = FRACUNIT-19000,
								spritexscale = FRACUNIT-16000,
								spriteyoffset = 4<<16,
								scalespeed = FRACUNIT/30,
								fuse = 12,
								COLORT_trail = {SKINCOLOR_MYSTICAL4, SKINCOLOR_MYSTICAL4, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_EMERALD, SKINCOLOR_LIME},
								COLORT_trailmax = 5} 
								
								s.spritexscale = FRACUNIT-11500
								s.spriteyscale = FRACUNIT+14000
								s.SMS_squashtable = {timer = 5, Xspeed = 4500, Yspeed = 5000, air = true}
								
								S_StartSound(s, sfx_thok)
								p.actionspd = skins[s.skin].actionspd
								p.charflags = $ & ~SF_MULTIABILITY
								p.pflags = $|PF_THOKKED
							end
						end
					end
				end
				if (s.SMS_WJpenalty > 105)
					s.SMS_WJpenalty = $-3 --Count down superquick, but suffer a big minimum penalty.
				elseif not (s.eflags & MFE_SPRUNG)
					s.SMS_WJpenalty = 105
				end
			end

		elseif ( p.sms_c1 == 1
		or (p.sms_jumphold == 1 and (p.pflags & PF_THOKKED) and not (p.cmd.buttons & BT_SPIN) and not p.sms_instantairboost) )

		and ((p.pflags & PF_JUMPED) or p.hometrickanim or p.panim == PA_SPRING or p.panim == PA_FALL)

		and not P_IsObjectOnGround(s)
		and not p.hypermysticsonic

			p.sms_tapnothold = $+1 //The second timer, just for this one move. Needed for the alt control schemes.

			if not p.sms_readyairboost
				if not (p.sms_tapnothold > 2)
					if (p.cmd.buttons & BT_CUSTOM1) and not ((p.pflags & PF_THOKKED)
					and (p.cmd.buttons & BT_JUMP) and not p.sms_jumphold and not (p.cmd.buttons & BT_SPIN))
						p.sms_readyairboost = 4
					else
						p.sms_readyairboost = 10
					end
					s.readyairboostdelay = (not (p.sms_instantairboost)) and true or false
				end
			end
		    if not s.readyairboostdelay and p.sms_readyairboost //These essentially create the sensitivity
		    	if (p.sms_tapnothold < 4)
					if not SMS_Locked(s, p, true) //Panicking people double-tap boost after walljumps, apparently. Stop them, magic script!
					and not p.sms_afterstompthok
					and not p.sms_stompanim	
					
					and not ( ((p.sms_tapnothold > 3) and not ((p.pflags & PF_THOKKED) and (p.cmd.buttons & BT_JUMP)
					and not p.sms_jumphold and not (p.cmd.buttons & BT_SPIN)))
	
					or (p.sms_tapnothold > 5) )
					and not p.smsblastboost
					and not (s.SMS_WallJumpTable and (s.SMS_WallJumpTable.timer > 19))
					and not (p.hookring and (p.cmd.buttons & BT_CUSTOM1))
						SMS_DoAirBoost(s, p)
					end
				end
		    end
		elseif p.sms_tapnothold
			p.sms_tapnothold = 0
		end

		//Water running! Looks like they finally gave up on removing the runonwater ability after Lua, 
		//so there goes the need for my fancy run-on-water custom code...

		if p.homing
		or p.gotflag
		or p.smsdivetimer
		or s.sdodgeoh
		or (p.pflags & PF_SLIDING)
		or (p.panim == PA_PAIN)
			p.charflags = $ & ~SF_RUNONWATER
			s.smsframejump = 0
		else //if not ((p.pflags & PF_SLIDING) or (p.panim == PA_PAIN))
			p.charflags = $|SF_RUNONWATER
			
			//When going fast enough, SMS'll slide over the water!
			if (s.eflags & MFE_TOUCHWATER) and (p.speed > 35*s.scale)
				if not (s.eflags & (MFE_GOOWATER|MFE_SPRUNG|MFE_UNDERWATER)) //Too much friction on solid water for this.
				and P_IsObjectOnGround(s)
				and not (p.pflags & (PF_SPINNING|PF_JUMPED|PF_STARTDASH|PF_SLIDING))
				and not p.hypermysticsonic
				and not p.sms_stompanim
				and not p.homing
				and not (s.momz*P_MobjFlip(s) > 0)
				and s.health
					if (s.eflags & MFE_JUSTHITFLOOR)
					and not (s.SMS_squashtable and s.SMS_squashtable.timer > 5)
					
						local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_THOK)
						ghs.flags2 = MF2_DONTDRAW ghs.tics = 88 ghs.fuse = 88
						
						S_StartSound(ghs, sfx_bubbl5)
						S_StartSound(ghs, sfx_bubbl3)
						S_StartSoundAtVolume(ghs, sfx_splish, 110)
						
						SMS_QuickDustGFX(s, SKINCOLOR_SILVER)
						SMS_QuickDustGFX(s, SKINCOLOR_SILVER, nil, nil, s)
				
						s.spritexscale = FRACUNIT+2200
						s.spriteyscale = FRACUNIT-2200
						s.spriteyoffset = -14<<16
						s.SMS_squashtable = {timer = 14, Xspeed = 120, Yspeed = 120, ground = true}						
					end
					if (p.speed > 75*s.scale)
						if (s.state != S_SMSWATERGRIND1) and (s.state != S_SMSWATERGRIND2)
							s.state = S_SMSWATERGRIND1
						end
						if not (p.cmd.forwardmove > 0)
							s.forcesmsangletimer = 2
							s.forcesmsangle = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
						end	
					end
					s.smsspeedticket = true
					if s.SMS_squashtable and s.SMS_squashtable.timer == 3
						SMS_QuickDustGFX(s, SKINCOLOR_SILVER, nil, nil, s)
					end
					if (leveltime % 5 == 0)
						SMS_QuickDustGFX(s, SKINCOLOR_SILVER, nil, -3<<16)
					end
					if not s.smsspeedticket
						SMSWindLines(s, p, true)
					end
				end
			end
			if (s.state == S_SMSWATERGRIND1) or (s.state == S_SMSWATERGRIND2)
				--We shouldn't be sliding anymore...
				if ((p.speed < 75*s.scale) or not (s.eflags & MFE_TOUCHWATER))
				and not (s.eflags & MFE_SPRUNG)
					s.state = S_PLAY_RUN
				end
			end
		end
		--Button checks. Using 2 of these for conflicting ability mo
		p.smsmeleebutton = (p.cmd.buttons & BT_CUSTOM2) and $+1 or 0
		p.smsmeleebutton2 = (p.cmd.buttons & BT_CUSTOM2) and $+1 or 0

		//Stomp Attack, Hyper Mystic version!
		if p.hypermysticsonic 
			if (p.hypermysticsonic > 40) //No HMS? No HMS stomp.
				HMS_stomp(s, p)
			end
		else --SMS Stomp Attack! Super Mystic version!
			s.stompnegatedinput = nil
			if (p.cmd.buttons & BT_CUSTOM2) and not (p.smsmeleebutton > 1)
			and not (s.SMS_WallJumpTable and (s.SMS_WallJumpTable.timer > 19))
				SMS_stomp(s, p) --SMSstomp
			end
			if (s.state == S_SMS_STOMP1)
			or (s.state == S_SMS_STOMP2)
				if (s.eflags & MFE_GOOWATER) --Roll up if you hit goowater.
					p.sms_stompanim = 0
					p.hometrickanim = false
					p.sms_afterstompthok = false
					s.state = P_IsObjectOnGround(s) and S_PLAY_FALL or S_SMS_ADVFALL1
				end
				if not p.sms_stompanim
				and not p.powers[pw_carry]
					if not P_IsObjectOnGround(s)
						p.pflags = $|PF_JUMPED
						s.state = S_PLAY_ROLL
					else
						s.state = S_PLAY_FALL
					end
				end
			end
		end
		if p.sms_stompanim or p.sms_afterstompthok
			SMS_Stomping(s, p)
			SMS_StompGFX(s, p)
			if p.sms_stompanim
				
				if p.sms_afterstompthok
				
					p.hometrickanim = false
					p.sms_stompanim = 0 --SMS can still thok out of a stomp!
					if (s.state == S_SMS_STOMP1) or (s.state == S_SMS_STOMP2)
					or ((p.panim == PA_FALL) and (s.momz*P_MobjFlip(s) < -10*s.scale))
						if not P_IsObjectOnGround(s)
							s.state = S_SMSTHOKANIM
						end
					end
					
				elseif not P_IsObjectOnGround(s)
				
					if (p.panim == PA_FALL) and (s.momz*P_MobjFlip(s) < -10*s.scale) --Won't allow it.
						s.state = S_SMS_STOMP1
					end
					if (p.cmd.buttons & BT_JUMP) --THOK out of a stomp!
						if (p.sms_jumphold <= 1)
						or (p.pflags & PF_THOKKED) and (p.pflags & PF_JUMPED) and p.sms_jumphold <= 3						
							if (s.state == S_SMS_STOMP1 or s.state == S_SMS_STOMP2)
							and not (s.eflags & (MFE_JUSTHITFLOOR|MFE_SPRUNG))		
								if not P_IsObjectOnGround(s) and not p.powers[pw_carry]
									s.state = S_SMSTHOKANIM --Remove PF_JUMPDOWN to make the game instantly thok!
									p.pflags = $|PF_JUMPED & ~(PF_JUMPDOWN|PF_THOKKED|PF_STARTJUMP)
								end
								p.sms_stompanim = 0
								p.hometrickanim = false
								p.sms_afterstompthok = true
							end
						end
					end
				end
			end
		end

		--button timer?
		p.sms_jumphold = (p.cmd.buttons & BT_JUMP) and $+1 or 0
		
		--2D mode parameters. Ugh...
		if (s.flags2 & MF2_TWOD) or twodlevel
			if (p.homing or p.smsrdash)
				s.momy = 0 // Prevent 2D shifts into 3D by accident.
			end
		end
		//Hold Spindash in midair to homing attack!
		//This used to be LUA_HMAT
		--Homing Attack secondary behaviour.
		if (p.homing > 5)
			local TR = s.tracer
			p.homingtrickcharge = 4
			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)  
			end
			
			if s.battlemodsms and TR //We're going after a target dummy? ...kay then.
			and ((TR.type == MT_PLAYER) or (TR.type == MT_TARGETDUMMY))
				if (p.homing > 49) //It hurts how broken the homing attack is in battlemod. SMS is a superform, but we STILL need to nerf it
					S_StartSound(s, sfx_s3k72)
					s.momx = 0 s.momy = 0
					P_SetObjectMomZ(s, 2<<16)
					local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_BURSTLIGHT)
					ghs.color = s.color
					ghs.blendmode = AST_ADD
					p.homing = 49
					S_StartSound(s, sfx_spndsh)
				elseif (p.homing > 41)
					P_InstaThrust(s, s.angle, -FRACUNIT)
					P_SetObjectMomZ(s, FRACUNIT, false)
				elseif (p.homing == 41)
					s.playerhomecharge = true
					S_StartSound(s, sfx_cdfm62)
					if not (TR and (FixedHypot(FixedHypot(s.x-TR.x, s.y-TR.y), s.z-TR.z) < 900<<16))
						P_InstaThrust(s, s.angle, 40<<16)
						P_SetObjectMomZ(s, -20<<16, false)
						p.homing = 0
						s.playerhomecharge = false
						s.playerhomechargefail = 21		
					end
				end				
		 	elseif (s.eflags & MFE_UNDERWATER)
				s.momx = $*5/7 s.momy = $*5/7 s.momz = $*5/7
			elseif (s.z-(s.momz*5/3) > s.floorz) and not p.hometrickanim
				s.momx = $*5/3 s.momy = $*5/3 s.momz = $*5/3 //Home faster than your average homing attack!
			end
			
		elseif s.playerhomechargefail
		
			s.playerhomechargefail = $-1
			if P_IsObjectOnGround(s)
				s.playerhomechargefail = 0
				s.flags2 = $|MF2_DONTDRAW
				s.momx,s.momy = $/2,$/2
				A_SMSSTOMPSMOKE(s, 18, MT_SPINDUST)
				A_SMSSTOMPSMOKE(s, 38, MT_SPINDUST)
				P_SetObjectMomZ(s, 4<<16, false)
				S_StartSound(s, sfx_cdfm14)
				p.pflags = $|PF_JUMPED|PF_THOKKED & ~PF_SPINNING
				s.state = S_PLAY_ROLL	
			end
			
		elseif s.playerhomecharge
			if s.battlemodsms and (p.homing == 1) and (p.pflags & PF_JUMPED)
				SMSRSpawnGhost(s)
				s.state = S_PLAY_FALL
				p.pflags = $ & ~(PF_JUMPED|PF_THOKKED)
			end
			if TR
				if ((TR.type == MT_PLAYER) or (TR.type == MT_TARGETDUMMY))
					if not P_IsObjectOnGround(s)
						P_SetObjectMomZ(s, 10<<16, false)
						if (p.panim == PA_FALL)
							p.sms_wallstat = 50 //You screwed up. Apply cooldown
							s.retmhtsfx = 48
							s.smsstompcd = 15
							P_InstaThrust(s, R_PointToAngle2(s.x, s.y, TR.x, TR.y), -16*s.scale)
							p.sms_flamedelay = 15 //Set this or it'll become the new meta instead.
							p.powers[pw_nocontrol] = 0
							s.recoilthrust = 0 //I'll do this manually, thanks
							p.pflags = $|PF_JUMPED|PF_THOKKED
							s.state = S_PLAY_ROLL
						else
						p.sms_wallstat = 50 //You screwed up. Apply cooldown
						s.retmhtsfx = 48
						s.smsstompcd = 15
						p.pflags = $|PF_JUMPED|PF_THOKKED
						s.state = S_PLAY_ROLL
					//	s.forcesmstrick = true				
					//	if (s.momz*P_MobjFlip(s) > (FixedMul(10<<16, s.scale)))
						end				
					end
				end
			end
			s.playerhomecharge = false
		end

		if p.hometrickanim
			if not ((s.state >= S_SMS_PREHOMET1) and (s.state <= S_SMS_SIDET))
			or P_IsObjectOnGround(s) and not (s.momz*P_MobjFlip(s) > 0)
			or p.hypermysticsonic
				p.hometrickanim = false
				p.homingtrickcharge = 0
			else --Homing Trick Aura. The SA2 jump aura, pretty much!
				SMS_SA2JumpTrail(s)
			end
		elseif p.homingtrickcharge
			p.homingtrickcharge = $-1
		end

		--Currently walljumping! The wall jump stat keeps track of alot of stuff.
		--NOTE: This has NOTHING to do with walljumps anymore, and is only kept as a stun tracker for other actions.
		if p.sms_wallstat
			SMS_WallJumpStats(s,p)
		end

		//Thok against a wall while holding spin to do a super walljump!

		if not p.hypermysticsonic //No walljump when HMS!
		and not p.homing			
			//Water vaporizes off SMS' body if he steps in it.
			if not SMSSHIELD(p, SH_ELEMENTAL, SH_BUBBLEWRAP)
				if P_PlayerTouchingSectorSpecial(p, 1, 3)
					p.sms_steaming = 200
					p.sms_steamingwait = 0 //You can't drip water from lava, dipshit.				
				elseif ((s.eflags & MFE_UNDERWATER)
				or ((s.eflags & MFE_TOUCHWATER) and (p.pflags & PF_SPINNING)))
					p.sms_steaming = 200
					p.sms_steamingwait = 35
				end
			end
			if s.SMSwallslideCD --Stops you from losing ALL speed when scraping walls....for a while.
				s.SMSwallslideCD = $-1
			end
			if s.sms_tauntcooldown
				s.sms_tauntcooldown = $-1
			end		
			p.spinitem = MT_SMSBLINDDASH --Default spin item.
			--The Lightdash move! Can be used with golden shoes!
			if p.lightdashenable
				SMS_LightDash(s, p)
			end	
			
			--Adding the framejump property here! You could call this the main IsObjectOnGround check, btw!
			if P_IsObjectOnGround(s)
				s.smsframejump = 10
				s.sms_painr = 0
				s.SMS_lastwallANG,s.SMS_lastwallNUM = nil,nil
				s.SMS_WJpenalty = 0
				--Fix for sheer animation stupidity.
				if (s.state >= S_SMS_PREHOMET1) and (s.state <= S_SMS_AIRBOOST)
				and not (s.momz*P_MobjFlip(s) > 0)
					s.state = (p.pflags & (PF_SPINNING|PF_STARTDASH)) and S_PLAY_ROLL or (p.speed > 32*s.scale) and S_PLAY_RUN or S_PLAY_WALK
				else
					if p.speed == 0 or (p.panim == PA_IDLE) --Standing still on the ground?
						if (s.state != S_SMSTAUNT1)
							SMS_IdleBehaviour(s, p) --Do your idle behaviours.
						end
					end
				end
				
			else --if not P_IsObjectOnGround(s) --Main air check if you will.
			
				p.charability = CA_HOMINGTHOK --Always have this ready for the sake of lock-on.
				p.charflags = $|SF_MULTIABILITY --multiability must be active for the lock-on marker too.			
				
				--Didn't rename the prev version for backwards compatibility.
				p.sms_weaponnexthold = (p.cmd.buttons & BT_WEAPONNEXT) and $+1 or 0	
			
				if (p.pflags & PF_THOKKED)
					if (p.pflags & PF_JUMPED)
						if (s.SMS_WJpenalty > 90)
							if (s.momz*P_MobjFlip(s) < -s.scale*4)
								s.SMS_WJpenalty = $-2
								if (p.speed > 56*s.scale)
									s.SMS_WJpenalty = $-2 --rewards for cool-looking play.
								end
								if (s.momz*P_MobjFlip(s) < -s.scale*12)
									s.SMS_WJpenalty = $-1
								end
							end
						else
							s.SMS_WJpenalty = max($, 40) --Minor minimum penalty.
						end
					elseif (p.panim == PA_FALL) and not p.smsatomicdivefail
					and not p.powers[pw_carry] and not P_IsObjectOnGround(s)
						p.pflags = $|PF_JUMPED
						s.state = S_PLAY_ROLL
					end
				end	
				
				if (s.state == S_PLAY_FALL)											
						if ((p.cmd.buttons & BT_JUMP) and (p.sms_jumphold < 5)
						or (p.cmd.buttons & BT_CUSTOM2) and p.smsmeleebutton < 2)
						and not SMS_Locked(s, p, 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 p.sms_didairboost --Okay, fine, but you're not spamming it...
								p.sms_didairboost = -7
							end
							p.hometrickanim = true
							p.homingtrickcharge = 0
							p.pflags = $|PF_JUMPED & ~PF_JUMPDOWN
							if (p.cmd.buttons & BT_CUSTOM2) and (p.smsmeleebutton < 2)
							and not (s.SMS_WallJumpTable and (s.SMS_WallJumpTable.timer > 19))
								SMS_stomp(s, p)
							end
							
						else
						
							--Old janky Dust devil code. We have CR_DUSTDEVIL now.
							if not ((s.momz*P_MobjFlip(s) > 17*s.scale) and (s.momz*P_MobjFlip(s) < 22*s.scale))
								if s.iampickedupbysmshelp
								or s.cmphttimer
									s.tornadoretcharge = 50
								elseif s.tornadoretcharge == nil
									s.tornadoretcharge = 0
								end
								s.tornadoretcharge = $+1
							elseif s.tornadoretcharge
								s.tornadoretcharge = 0
							end
							
							--Anyway, try doing a homing attack trickjump!
							if (s.tornadoretcharge == nil or (s.tornadoretcharge < 2))
							and (s.momz*P_MobjFlip(s) > 17*s.scale)
							and not s.iampickedupbysmshelp
							and not s.cmphttimer
								SMS_HomingTrick(s,p) --Do a homing trick!
							end	
							if s.tornadoretcharge
								s.tornadoretcharge = 0
							end
						end
				end
					
				p.thokitem = MT_SMSAURATRAIL
				if (p.cmd.buttons & BT_SPIN) or s.SMS_WJauto
					if ((p.pflags & PF_JUMPED) or p.sms_stompanim or p.hometrickanim
					or (s.state == S_SMS_AIRBOOST) 
					or (p.panim == PA_FALL))
					and not p.smsblastboost
						SMS_WallJumpCheck(s,p)
					end
				end
			end

			//Fancy tricks! Yesh!

			if p.homingtrickcharge
			and ( ( (p.pflags & PF_JUMPED) and (p.pflags & PF_NOJUMPDAMAGE)
			and ((p.speed < 22*s.scale))
			and ( (s.momz*P_MobjFlip(s) > 2*s.scale) or (s.eflags & MFE_UNDERWATER)) ) or s.forcesmstrick )
			
				SMS_HomingTrickJump(s, p, nil)				
				
			elseif p.hometrickanim --Doing tricks!
			and not P_IsObjectOnGround(s)
			
				if (s.state == S_SMS_SIDET) and not s.airboostroll
					s.smstrickroll = true --Currently rolling, yes...
					SMS_TrickRoll(s, p, 16<<16, true)
				end
			
			elseif s.smstrickroll
				if not s.airboostroll
					s.rollangle = 0
				end
				s.smstrickroll = false
			end


		//Lets group of a bunch checks behind this for better performance.
		//Better than doing P_IsObjectOnGround 500 times...
			
			if p.panim == PA_PAIN
				if P_PlayerInPain(p) --Not while in a waterslide, tho.
					s.sms_painr = s.sms_painr and $+1 or 1
					p.sms_stompanim = 0
					p.sms_afterstompthok = false
				else
					s.sms_painr = 0
				end

				//YOU MUST RECOVER! Press the jump button to undo potential insta-murder. 
				//I feel every character should have this, but alas, this is SMSreborn, not EverybodyReborn...					
				if s.sms_painr and (s.sms_painr > 28) //If the timer is 29(or higher), YOU MUST RECOVER!!
					if not (p.pflags & PF_SLIDING)
						if (p.cmd.buttons & BT_JUMP) //Ability only starts when you press jump.
						and not p.powers[pw_nocontrol]
							s.airboostroll = 0
							s.rollangle = 0
							s.state = S_PLAY_ROLL
							P_SetObjectMomZ(s, 9<<16, false)
							P_DoJump(p, true)
							P_SetObjectMomZ(s, 138500, true) //Slightly higher than a normal jump.
							s.sms_painr = 0 //Bring back painrecovery to 0.
							if not s.SMS_squashtable
								s.spritexscale = FRACUNIT-11100
								s.spriteyscale = FRACUNIT+13500
								s.spriteyoffset = FRACUNIT+6
								s.SMS_squashtable = {timer = 3, Xspeed = 4500, Yspeed = 5000, air = true}	
							end
						end
					end
				end
				
			elseif not p.powers[pw_carry]
			
				if ((s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04))
					p.smsboostpanim = true
				else
					if s.SMS_ishoming and SMS_Graphix(s) != 2
						if (s.momz*P_MobjFlip(s) > 8*s.scale)
							local ghs = SMS_GroundWindBlast(s)
							ghs.renderflags = RF_FULLBRIGHT|RF_FLOORSPRITE
							ghs.flags2 = $|MF2_SPLAT
							ghs.z = $-s.momz

							ghs = SMS_GroundWindBlast(s)
							ghs.renderflags = RF_FULLBRIGHT|RF_FLOORSPRITE
							ghs.flags2 = $|MF2_SPLAT
							ghs.spriteyscale = $/3
							ghs.spritexscale = $/3
							ghs.fuse = 11
							ghs.sttspeed = 2
							ghs.tracer = s
							ghs.ditchtracer = 1
							
							ghs = SMS_LightFlash(s, s.color, FRACUNIT-27000, 7, Z+3|TR_TRANS40)
							ghs.renderflags = RF_FULLBRIGHT|RF_FLOORSPRITE
							ghs.spriteyscale = $/3
							ghs.spritexscale = $/3
							ghs.scalespeed = $+7777
							ghs.z = $-s.momz
						end
					end						
					p.smsboostpanim = false
				end
				//If SMS runs off a cliff, go into a special animation that'll let you curl up again!
				if (p.panim == PA_WALK or p.panim == PA_RUN or p.smsboostpanim)
				and not (s.eflags & (MFE_GOOWATER|MFE_SPRUNG))
				and not (p.pflags & PF_SLIDING)
				and not (p.sms_ringburstfly|p.sms_comethaymaker|p.mslightspeedattack|p.sms_dodging|p.exiting) 
				
					p.sms_advfall = $+1
					if (p.sms_advfall > 9) //Been walking in sunshine long enough? Then falling animation, go!
					and not (s.eflags & MFE_GOOWATER)
				
						if (s.momz*P_MobjFlip(s) > 7*s.scale)
							s.state = S_PLAY_SPRING
						else
							s.state = S_SMS_ADVFALL1
						end
						p.sms_advfall = 0
					end
				else
					p.sms_advfall = 0
				end
					
				if (s.state == S_PLAY_SPRING)

					if (s.momz*P_MobjFlip(s) > 6*s.scale)
					and (p.pflags & PF_JUMPED)
					and not p.hometrickanim 
					and not (p.pflags & (PF_NOJUMPDAMAGE|PF_SPINNING)) --Recoiled from homing attack, most likely.
				
						p.pflags = $ & ~PF_JUMPED
		
					--Curl up off springs with the spin button!

					elseif (p.cmd.buttons & BT_SPIN) or (p.cmd.buttons & BT_CUSTOM2) or (p.cmd.buttons & BT_JUMP)
							
						if (p.sms_stompanim or not (p.pflags & PF_JUMPED))
						and not p.exiting and not (p.pflags & PF_GAMETYPEOVER)
						and not p.powers[pw_carry] //Fixes a bug in competition/race.
						and not (p.secondjump == 167)
			
							if ((p.cmd.buttons & BT_SPIN) or p.sms_stompanim) and not p.spinhold 
							or (p.sms_jumphold < 3 and (p.cmd.buttons & BT_JUMP) and not p.smsblastboost)
				
								SMSRSpawnGhost(s, 13)
								s.atomdiveusebuffer = 0
								s.state = S_PLAY_ROLL 
								p.pflags = $|PF_JUMPED & ~PF_THOKKED 
								if (p.cmd.buttons & BT_JUMP)
									if p.powers[pw_carry]
										p.pflags = $|PF_JUMPDOWN
										p.sms_jumphold = 10
									else
										p.pflags = $|PF_JUMPED & ~PF_JUMPDOWN & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
									end
								end						
				
							elseif ((p.cmd.buttons & BT_CUSTOM2) and not (p.smsmeleebutton > 1)
							or ((p.cmd.buttons & BT_JUMP) and (p.sms_jumphold <= 2) and p.smsblastboost))					
								SMS_SideFlip(s, p, true)
							end	
							
						elseif ( ((p.cmd.buttons & BT_SPIN) and not p.spinhold)
						or ((p.cmd.buttons & BT_JUMP) and (p.sms_jumphold <= 2)) )	
						
							P_SpawnGhostMobj(s)
							s.atomdiveusebuffer = 0
							s.state = S_PLAY_ROLL
							p.pflags = $|PF_JUMPED & ~PF_THOKKED //Let's roll!	
						end
					end
					
				elseif (s.state == S_PLAY_GASP) and (s.tics == 1) --Least likely scenario comes last.	
					s.state = S_SMS_ADVFALL1
					p.sms_advfall = $+1
				end
			else --if p.powers[pw_carry]
				s.sms_painr = 0
				p.sms_stompanim = 0
				p.sms_afterstompthok = false
			end
			
			--Execute the frame jump! Or Coyote time, whatever tickles your fancy.
			if s.smsframejump and ((p.cmd.buttons & BT_JUMP) and (p.sms_jumphold < 3) or s.bufferframejump)
				s.bufferframejump = nil
				SMS_FrameJump(s, p)
			end
			s.SMS_ishoming = nil	
		elseif p.homing
			SMS_HomingBehaviour(s, p) --Homing Attack behaviour.
		end
		
		//Is SMS in advanced falling states? Meaning:
		--When SMS runs off a cliff or ledge, he goes into special falling frames with recurl abilities.
		if (s.state == S_SMS_ADVFALL2) or (s.state == S_SMS_ADVFALL1)		

			p.sms_advfall = 0 --This is the tic counter value before these states would've kicked in.

			if p.hypermysticsonic --Can fly again!
			
				s.state = S_PLAY_ROLL --Shouldn't be in those frames to begin with, tbh.
				p.pflags = $|PF_JUMPED
				
			elseif (p.cmd.buttons & BT_CUSTOM2)	and not (p.smsmeleebutton > 1)
			and not p.spinhold and not p.powers[pw_carry] --Trick from falling anim?
			
				A_SMSSTOMPSMOKE(s, 6, MT_SPARK, nil, (s.momz*4/3))
				SMSRSpawnGhost(s, 14)
				
				s.state = S_SMS_ROLLT2
				p.hometrickanim = true
				p.pflags = $ & ~PF_JUMPED & ~PF_THOKKED & ~PF_NOJUMPDAMAGE
				p.homingtrickcharge = 0
				
			elseif ( ((p.cmd.buttons & BT_SPIN) and not p.spinhold) --Curl up from falling anim!
			or ((p.cmd.buttons & BT_JUMP) and (p.sms_jumphold <= 2)) )
			
				local ghs = P_SpawnGhostMobj(s)
				ghs.spriteyscale = s.spriteyscale*5/4
				ghs.spritexscale = s.spritexscale*4/5
				ghs.destscale = ghs.scalespeed+1
				ghs.fuse = 8 ghs.tics = 9

				s.spritexscale = FRACUNIT-11500
				s.spriteyscale = FRACUNIT+14000
				s.SMS_squashtable = {timer = 5, Xspeed = 4500, Yspeed = 5000, air = true}					
				
				s.atomdiveusebuffer = 0
				s.state = S_PLAY_ROLL
				p.pflags = $|PF_JUMPED & ~PF_THOKKED & ~PF_JUMPDOWN //Let's roll!	
			end

		elseif p.sms_advfall --Got a counter but need to disable it?
		
			if P_IsObjectOnGround(s)
			or (p.pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING))
			or not p.panim == PA_RUN and not p.panim == PA_WALK and not p.smsboostpanim
			or p.powers[pw_carry]
				p.sms_advfall = 0
				if not p.homing
					p.pflags = $ & ~PF_THOKKED
				end
			end
		end	
	end
end, MT_PLAYER)