--=============================MAIN THINKER 3============================
--This main thinker houses some effects, NiGHTS, and SMS bots.
--Most importantly, it contains the 3 thinkframe variants.
--These are useful for deciding code priority and ordering.
--Lastly, it contains alot of special player-related hooks.
--=======================================================================

--Made this a PlayerThink so there's two seperate chances for me to setup.
--PlayerThink is basically identical to MobjThinker, except it works without a mobj too. Rarely will you need that, though.
addHook("PlayerThink", function(p)
if p.sweepangle == nil SetUpSMSreborn(p) end --Apparently PlayerThink goes slightly before a MobjThinker. good to know.
if not (p.mo and p.mo.health) return end --Need a living mobj from here on out.

		local s = p.mo
		
		//Super Mystic Sonic is merely another transformation for Sonic. Although most people
		//would probably prefer to choose SMS as a skin, I still believe a manual transformation would be cool.	
		
		--From the og LUA_SONICTRANSFORM.	
		if p.sms_transformstate and not p.sms_sontransform
		
			if (s.skin != "sms") or not G_PlatformGametype() --Can't be transformed like this!
			
				p.sms_transformstate = 0
				
			elseif s.skin == "sms" --We're currently a "transformed" SMS. Sparkle always and allow untransforming.
			
				A_BossScream(s, 0, MT_SMSTSPARK)
				
				if (p.cmd.buttons & BT_CUSTOM3) --Manually untransform in these circumstances.
				and (p.cmd.buttons & BT_SPIN) //Untransform, detransform, unbecome
				and not (p.speed > s.scale)
				and P_IsObjectOnGround(s)
				and (p.cmd.forwardmove < -5)
				and not (p.spinhold > 1)
				and not SMS_Locked(s, p)
				and not (p.pflags & PF_JUMPED)
				
					A_SMSTOSONIC(s, true)
					
				elseif leveltime <= 1 --Auto untransform in these circumstances.
				or p.exiting
				or not s.health
				
					if p.exiting
						A_SMSTOSONIC(s, nil, true, nil)
					elseif ((p.panim == PA_ROLL) or (p.panim == PA_RUN) or (p.panim == PA_WALK) or (s.state == S_PLAY_DEAD) or (s.state == S_PLAY_PAIN))
						A_SMSTOSONIC(s, nil, nil, true)		
					else
						A_SMSTOSONIC(s, nil, true, true)
					end
				end
				
			end
		end		
		
		if (s.skin == "sms") --Be SMS from now on.
				if p.isxmomentum
					p.noxmomchar = 2 --None of this.
				end
	//Revert into a custom base Sonic for NiGHTs when you have SMS selected as a skin
	
			if (p.powers[pw_carry] == CR_NIGHTSMODE) 
			
				SMSNiGHTS(s, p)
				
				//Fix some bugs transforming Sonic's sprites into Super Mystic ones in NiGHTs	
				if ((s.state >= S_PLAY_NIGHTS_STUN) and (s.state <= S_PLAY_NIGHTS_ATTACK))
					s.state = S_PLAY_NIGHTS_PULL
					if s.nrollangle != nil
						s.rollangle = s.nrollangle
						s.nrollanga = true
					end
				end
				if s.fboostgfx
					s.renderflags = $|RF_FULLBRIGHT
				else
					s.renderflags = $ & ~RF_FULLBRIGHT
				end
			
			//Yes, those wind lines around SMS' body when he runs are actually speed tickets. Guy's drowning in debt.
			
			elseif s.smsspeedticket	
			
				SMSWindLines(s, p)
				s.smsspeedticket = false

			end
			
			//The Super Mystic boost aura
			
			//Start of the boost aura code, passes it to thinkframe for spawn.

			if s.boostpass
				SMS_BoostAura(s, p)
			elseif (((p.speed > 79*s.scale) and not p.hypermysticsonic) or (((p.speed > 135*s.scale) or p.hmsdash) and p.hypermysticsonic)) and (((s.state == S_SMS_AIRBOOST) and s.tics > 20)
			or p.hypermysticsonic or (p.sms_boostlevel > 64))
				if s.health
					s.boostpass = 1
					SMS_BoostAura(s, p)
				end				
			end

			//You have a thokaura active.
			
			if s.thokaurapass
				local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMSAURATRAIL)
				ghs.aftertrail = 40
				if (s.eflags & MFE_VERTICALFLIP)	
					ghs.z = $+18*s.scale
				end
				if s.lasttrailra
					ghs.rollangle = s.lasttrailra
				end
				ghs.target = s
				ghs.sprite = SPR_AURA
				ghs.tics = 8
				
				if s.thokaurapass >= 7
					ghs.color = SKINCOLOR_FLAME
					ghs.frame = B|FF_FULLBRIGHT|TR_TRANS50
				elseif s.thokaurapass == 6
					ghs.color = SKINCOLOR_ORANGE
					ghs.frame = B|FF_FULLBRIGHT|TR_TRANS60				
				elseif s.thokaurapass == 5
					if SMSisyellow(s) or s.color == SKINCOLOR_BLACK or s.color == SKINCOLOR_CRIMSON
						ghs.color = SKINCOLOR_GOLD
					else
						ghs.color = s.color
					end
					ghs.frame = B|FF_FULLBRIGHT|TR_TRANS70
				elseif s.thokaurapass >= 2
					ghs.color = s.color
					ghs.frame = B|FF_FULLBRIGHT|TR_TRANS80
				elseif s.thokaurapass == 1
					ghs.color = SKINCOLOR_EMERALD
					ghs.frame = B|FF_FULLBRIGHT|TR_TRANS90
				elseif not ghs.color
					ghs.color = SKINCOLOR_FLAME
				end
				
				if (ghs.color == SKINCOLOR_FLAME) and (s.tics > 6)
					ghs.sprite = SPR_SMBO
					ghs.scale = s.scale*4/5
				else
					ghs.scale = s.scale*4/3
				end
				ghs.scalespeed = $*2/3
				ghs.destscale = 9
				s.thokaurapass = $-1
			end
			
			//Special Sonic X-like Ring Burst ghostmobjs for ringburstflight
			if s.ringburstticket
			local ghs
				if (leveltime % 2 == 0)
					if SMSLocalRNG(0,2,s) != 0
						ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK)
						ghs.angle = p.drawangle
						ghs.color = s.color
						ghs.sprite = SPR_SMSO
						ghs.frame = (Z+1)|FF_FULLBRIGHT|TR_TRANS60
						ghs.momx = s.momx/-4
						ghs.momy = s.momy/-4
						ghs.momz = s.momz/-4							
					else
						ghs = P_SpawnGhostMobj(s) 
						ghs.sprite2 = SPR2_ROLL
						ghs.frame = D|FF_FULLBRIGHT|TR_TRANS50
						ghs.scale = $*3/2	
						ghs.momx = s.momx/2
						ghs.momy = s.momy/2
						ghs.momz = s.momz/-2		
					end
					ghs.tracer = s
					ghs.target = s
					ghs.blendmode = AST_ADD
					ghs.renderflags = RF_FULLBRIGHT
					ghs.rollangle = SMSLocalRNG(0,359,s)*ANG1
					ghs.tics = 12
					ghs.scalespeed = $*2
					ghs.destscale = s.scale*4					
					A_CapeChase(ghs, 1, 0)
				end	
				
				ghs = P_SpawnGhostMobj(s) 
				ghs.blendmode = AST_ADD
				ghs.target = s
				ghs.tracer = s
				ghs.tics = 9
				ghs.sprite = SPR_PLAY
				ghs.sprite2 = SPR2_ROLL
				ghs.renderflags = RF_FULLBRIGHT
				ghs.frame = D|TR_TRANS50
				ghs.scalespeed = $*2
				ghs.destscale = s.scale*4
				ghs.momx = s.momx
				ghs.momy = s.momy
				ghs.momz = s.momz+32320
				A_CapeChase(ghs, 1, 0)
				s.ringburstticket = false
			end
			//A golden shine when initiating a ringburst!
			if s.ringburstticket2 
				if (s.ringburstticket2 >= 17)
					local ghs = P_SpawnMobjFromMobj(s, s.momx/2,s.momy/2,(s.height/3), MT_THOK)
					ghs.sprite = SPR_AURA
					ghs.frame = S|FF_TRANS60|FF_FULLBRIGHT
				 	ghs.color = SKINCOLOR_MYSTICAL3
					ghs.spriteyoffset = $-12*FRACUNIT
					ghs.flags = $|MF_NOCLIPHEIGHT|MF_NOBLOCKMAP
				  	ghs.rollangle = leveltime*ANG1
					ghs.scale = (s.scale*4)/SMSLocalRNG(1,5,s)
					ghs.destscale = s.scale*6
				  	ghs.tics,ghs.fuse = 2,2
				  	ghs.momx = s.momx
				 	ghs.momy = s.momy
				  	ghs.momz = s.momz
				elseif (s.ringburstticket2 % 2 == 0)
					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_GOLDENSHINE)
					ghs.target = s
					ghs.spriteyoffset = $-14*FRACUNIT
					ghs.color = SKINCOLOR_SMS3R
					ghs.rollangle = SMSLocalRNG(0,160,s)*ANG1
					ghs.frame = U|FF_TRANS90
				end
				
				s.ringburstticket2 = $-1
				
				if not (p.sms_ringburstfly < 3) or SMS_Graphix(s) == 2 --Must be ringbursting!
					s.ringburstticket2 = 0
				end
			end
		end

//Glowing ticket here! Glow ticket makes the shine appear.

		if ( ( ((s.color >= SKINCOLOR_SUPERSILVER1) or p.shinyglosscoat) and (s.skin == "sms")) or s.smsglowticket)
		and s.subsector and s.subsector.sector and (p.powers[pw_carry] != CR_NIGHTSMODE)
			local SEC = s.subsector.sector
			local FOFLIGHT = nil
			local LIGHT = 255 
			if s.cooldownlight --Light is currently shrinking/expanding.
				s.cooldownlight = $+1
				if (s.cooldownlight > 23)
					s.cooldownlight = 0
				end
			end
			for fof in SEC.ffloors() --Use a FOF's shadow as reference instead?
				if (fof.flags & FF_EXISTS) 
					if not (fof.flags & (FF_NOSHADE|FF_DOUBLESHADOW))
					and (s.z < fof.bottomheight)
						FOFLIGHT = fof.toplightlevel
					elseif (fof.flags & FF_CUTSPRITES)
						if ((s.z >= fof.bottomheight) and (s.z+(s.height/2) < fof.topheight))
							FOFLIGHT = fof.toplightlevel+20
						end
					end
				end
			end
			if FOFLIGHT != nil 
				LIGHT = FOFLIGHT
			else
				LIGHT = SEC.lightlevel or 0
			end	
			if (LIGHT > 179) and (s.eflags & MFE_UNDERWATER)
				LIGHT = 155
				s.cooldownlight = 0
				s.smsgoldenshine = true
			elseif (LIGHT > 179) and s.smsgoldenshine
				if not s.cooldownlight
				s.cooldownlight = 1
				end
				s.smsgoldenshine = false
			end
				
			if (LIGHT < 180)
				s.cooldownlight = 0
				s.smsgoldenshine = true
			end
				
			if p.hypermysticsonic --HMS has a fancy rotating light.
				if (LIGHT < 210) and (p.smsgraphix != 2)
					HMS_DarkLight(s, p, s.color) --Much simpler.
				end	
			elseif ((LIGHT < 180) or s.cooldownlight)
			and not (p.smsgraphix == 2)
			  
				local ghs = P_SpawnMobjFromMobj(s, 0, 0, s.height/2, MT_THOK)
				ghs.sprite = SPR_OILF
				ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
				if s.cooldownlight
					ghs.scale = max(99, s.scale-(s.cooldownlight*1000) )
				else
					if p.hypermysticsonic
				   		ghs.scale = s.scale*4/3
						if P_MobjFlip(s) == 1
							ghs.z = $+11*s.scale
						end
					else
						ghs.scale = s.scale
					end
				end
				ghs.frame = A|TR_TRANS90
				ghs.tics = (LIGHT < 100) and 4 or (LIGHT < 100) and 3 or 2
				ghs.momx,ghs.momy,ghs.momz = s.momx,s.momy,s.momz
				ghs.scalespeed = $+SMSLocalRNG(-500, 1000, s) --Flickery light!  
				ghs.destscale = 1
				ghs.flags = MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOBLOCKMAP|MF_SCENERY --No MF_NOGRAVITY for extra jitteryness?
				ghs.radius = 0
				ghs.height = 0
				if not s.cooldownlight
					s.smsgoldenshine = true
				end
			end
			  
			if s.smsglowticket
				if s.skin != "sms"
				or p.shinyglosscoat 
				or p.hypermysticsonic
				or not (s.color >= SKINCOLOR_SUPERSILVER1)
					s.smsglowticket = $-1
					if not s.smsglowticket
						s.frame = $ & ~FF_FULLBRIGHT
					end
				end
			end
		end
		
		//Execute SMS ssxa aura		
		
		if s.ssxaticket
			s.ssxaticket = false
			if SMS_Graphix(s) == 0
				local ghs
				local AngRNG = s.angle+(SMSLocalRNG(0,359, s)*ANG1)
				if p.hypermysticsonic
					if (leveltime % 2 == 0)
						ghs = P_SpawnMobjFromMobj(s,
						FixedMul(SMSLocalRNG(6,20,s)*s.scale, cos(AngRNG)),
						FixedMul(SMSLocalRNG(6,20,s)*s.scale, sin(AngRNG)),
						SMSLocalRNG(0,79, s)*s.scale,
						MT_SSXA)
					else
						ghs = P_SpawnMobjFromMobj(s,
						-FixedMul(SMSLocalRNG(6,20,s)*s.scale, cos(AngRNG)),
						FixedMul(SMSLocalRNG(6,20,s)*s.scale, sin(AngRNG)),
						SMSLocalRNG(0,79, s)*s.scale,
						MT_SSXA)
					end
				else
					if (leveltime % 2 == 0)
						ghs = P_SpawnMobjFromMobj(s, 
						FixedMul(SMSLocalRNG(6,20,s)*s.scale, cos(AngRNG)),
						FixedMul(SMSLocalRNG(6,20,s)*s.scale, sin(AngRNG)),
						SMSLocalRNG(0, 54, s)*s.scale,
						MT_SSXA)
					else
						ghs = P_SpawnMobjFromMobj(s,
						-FixedMul(SMSLocalRNG(6,20,s)*s.scale, cos(AngRNG)),
						FixedMul(SMSLocalRNG(6,20,s)*s.scale, sin(AngRNG)),
						SMSLocalRNG(0, 54,s)*s.scale,
						MT_SSXA)
					end
				end
				ghs.sprite = SPR_SMST
				ghs.blendmode = AST_ADD
				if (leveltime % 8 == 0) or (leveltime % 3 == 0)
					ghs.mcd = SMSLocalRNG(2,4,s)
					ghs.frame = SMSLocalRNG(3,6,s)|FF_FULLBRIGHT|TR_TRANS30
				else
					ghs.mcd = SMSLocalRNG(2, 4, s)
					ghs.frame = SMSLocalRNG(3,6, s)|FF_FULLBRIGHT|TR_TRANS40
				end
				if p.hypermysticsonic
					ghs.scalespeed = $*SMSLocalRNG(1,3,s)
				else
					ghs.scalespeed = $/SMSLocalRNG(1,5,s)
				end
				ghs.destscale = $*7
				P_SetObjectMomZ(ghs, 32555+SMSLocalRNG(-20000, 40000, s))
				
			//	ghs = P_SpawnMobj(s.x+(s.momx/2)-FixedMul((P_RandomRange(9, 22)*s.scale), cos(s.angle+ANGLE_90)), s.y+(s.momy/2)-FixedMul((P_RandomRange(9, 22)*s.scale), sin(s.angle+ANGLE_90)), s.z+s.momz+(P_RandomRange(1, 54)*P_MobjFlip(s))*s.scale, MT_SSXA)
				if (leveltime % 2 == 0)
					ghs = P_SpawnMobjFromMobj(s,
					SMSLocalRNG(-6, -20, s)*s.scale,
					SMSLocalRNG(-6, -20, s)*s.scale,
					SMSLocalRNG(0, 54, s)*s.scale,
					MT_SSXA)
				else
					ghs = P_SpawnMobjFromMobj(s,
					SMSLocalRNG(-6, -20, s)*s.scale,
					-SMSLocalRNG(-6, -20, s)*s.scale,
					SMSLocalRNG(0, 54, s)*s.scale,
					MT_SSXA)
				end
				ghs.sprite = SPR_SMST
				ghs.blendmode = AST_ADD
				if leveltime % 2 == 0
					ghs.frame = (SMSLocalRNG(3, 6, s))|FF_FULLBRIGHT|TR_TRANS30
				else
					ghs.frame = (SMSLocalRNG(3, 6, s))|FF_FULLBRIGHT|TR_TRANS10
				end
				if p.hypermysticsonic
					ghs.scalespeed = $*SMSLocalRNG(1, 3, s)
				else
					ghs.scalespeed = $/SMSLocalRNG(1, 5, s)
				end
				ghs.destscale = $*7
				P_SetObjectMomZ(ghs, 32555+SMSLocalRNG(-20000, 40000, s))
			end
		end	
end, MT_PLAYER)

--Let's use SA-Sonic's method instead, my old one is just clumsy.

addHook("MobjMoveBlocked", function(s, m, line)
	if s.skin != "sms"
	or not s.player
	or s.currentaxis
	or not line
		return --
	end	
	
local p = s.player

	if s.hypermysticdash --You gotta turn to slide off walls!
	
		if not (p.cmd.forwardmove < -28)
			s.hypermysticdash = 0
		end
		
	elseif (p.sms_ringburstfly or s.SMS_CrashRoll or (p.smsdivetimer and (p.smsdivetimer != 1)) )
		 --Reflect off walls like a pingpong ball!
	
			if not s.SMS_WallBounce
			and not P_IsObjectOnGround(s) 
			and not (p.pflags & (PF_JUMPED|PF_SLIDING))
			
				local x,y = P_ClosestPointOnLine(s.x, s.y, line)
				local BOUNCEANGLE = R_PointToAngle2(s.x, s.y, x, y)
				local ANGSHIFT,PLUS = SMS_AngleDifference(BOUNCEANGLE,SMS_PlayerAngle(s,p), 2, ANG30, 2)
				BOUNCEANGLE = $+(ANGSHIFT*PLUS)	
				
				if s.SMS_CrashRoll --In ragdoll state!
					s.hmsrotateright = (s.hmsrotateright != true) and true or false	
					if not (p.sms_ringburstfly or p.smsdivetimer)
						S_StartSoundAtVolume(s, sfx_corkh, 134)
						S_StartSoundAtVolume(s, sfx_doord2,66)
						S_StartSoundAtVolume(s, sfx_mswing,99)
						if not s.SMS_squashtable
							s.spritexscale = FRACUNIT-6100
							s.spriteyscale = FRACUNIT+9900
							s.SMS_squashtable = {timer = 2, Xspeed = 3200, Yspeed = 5500, air = true}
						end

						local PRESPEED = max(p.speed*3/5, 3*s.scale)
						s.momx = $/4 s.momy = $/4
						P_SetObjectMomZ(s, -s.scale, true)
						P_Thrust(s, BOUNCEANGLE+ANGLE_180, PRESPEED)
								
						local RNG = SMSLocalRNG(2,6,s)
						local ghs = SMS_SmokeCircle(s, 0, 1, s, (FRACUNIT/2)*RNG)
						if ghs and ghs.valid
							if (RNG > 4)
								ghs.ditchtracer = 1
							end
							ghs.fuse = $-6
							ghs.spriteyoffset = -27*FRACUNIT
						end
						if s.SMS_CrashRoll.rollangle != nil
							local DIRECT = (s.hmsrotateright == true) and 1 or -1
							s.SMS_CrashRoll.rollangle = $+(s.SMS_CrashRoll.rollspeed*3*DIRECT)
							s.SMS_CrashRoll.faceangle = $+(s.SMS_CrashRoll.spinspeed*3*DIRECT)
						end
						s.SMS_WallBounce = 2
						return true --
					end
				end
				
				--Cancel conditions...
				if p.smsdivedelay
				or (p.smsdivetimer and (p.smsdivetimer < 4) and G_GametypeHasSpectators())
				or (p.panim == PA_SPRING)
					return --
				end
				
				if not p.hypermysticsonic --SMS stuff.
					if  p.sms_ringburstfly and ( p.sms_ringburstfly != 1)
						s.SMS_WallBounce = 2 --Horizontal RingBurst Crash.
						s.SMS_lastwallNUM = line
						return --
					else
						p.smsatomdivestun = nil
						if p.sms_ringburstfly
						or p.smsdivetimer
							if (p.pflags & (PF_ANALOGMODE|PF_DIRECTIONCHAR))
								p.drawangle = BOUNCEANGLE+ANGLE_180
							end
							s.angle = BOUNCEANGLE+ANGLE_180
						end
						if p.sms_ringburstfly == 1
							P_SetObjectMomZ(s, 41350, true) --Slight vertical boost.

							local SfX = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK) 
							SfX.flags2 = $|MF2_DONTDRAW
							SfX.tics = 133
							S_StartSoundAtVolume(SfX, sfx_smsfx6, 120)
							S_StartSound(SfX, sfx_smsfx2)		
							S_StartSoundAtVolume(SfX, sfx_doord2, 65)
							
							local ghs = SMS_DashGFX(s, SKINCOLOR_MYSTICAL2)
							ghs.rollangle = $+ANGLE_270
							ghs.renderflags = $ & ~RF_PAPERSPRITE	
							if SMS_Graphix(s) != 2
								local ghs = SMS_LightFlash(s, SKINCOLOR_ORANGE, FRACUNIT-11500, 5, ((Z+3)|TR_TRANS30))
								ghs.tracer = s
								if s.SMS_DLtrail == nil
									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
							end
						else
							if s.hmsrotateright
								s.hmsrotateright = false
							else
								s.hmsrotateright = true
							end			
							if not (p.sms_ringburstfly or p.smsdivetimer)
								S_StartSoundAtVolume(s, sfx_corkh, 134)
								S_StartSoundAtVolume(s, sfx_doord2,66)
								S_StartSoundAtVolume(s, sfx_mswing,99)
								if not s.SMS_squashtable
									s.spritexscale = FRACUNIT-6100
									s.spriteyscale = FRACUNIT+9900
									s.SMS_squashtable = {timer = 2, Xspeed = 3200, Yspeed = 5500, air = true}
								end

								local PRESPEED = max(p.speed*3/5, 3*s.scale)
								s.momx = $/4 s.momy = $/4
								P_SetObjectMomZ(s, -s.scale, true)
								P_Thrust(s, BOUNCEANGLE+ANGLE_180, PRESPEED)
								
								local RNG = SMSLocalRNG(2,6,s)
								local ghs = SMS_SmokeCircle(s, 0, 1, s, (FRACUNIT/2)*RNG)
								if ghs and ghs.valid
									if (RNG > 4)
										ghs.ditchtracer = 1
									end
									ghs.fuse = $-6
									ghs.spriteyoffset = -33*FRACUNIT
								end
								if s.SMS_CrashRoll.rollangle != nil
									local DIRECT = (s.hmsrotateright == true) and 1 or -1
									s.SMS_CrashRoll.rollangle = $+(s.SMS_CrashRoll.rollspeed*3*DIRECT)
									s.SMS_CrashRoll.faceangle = $+(s.SMS_CrashRoll.spinspeed*3*DIRECT)
								end
								s.SMS_WallBounce = 2
								return true --
							else
								S_StartSound(s, sfx_doord2)
								for i = 0,4
									local ghs = SMS_DashGFX(s, SKINCOLOR_SMS5R-i)
									ghs.rollangle = $+ANGLE_90
									ghs.renderflags = $ & ~RF_PAPERSPRITE
									ghs.frame = (i == 4) and $|FF_HORIZONTALFLIP or ($ & ~FF_HORIZONTALFLIP)
								end
							end
						end
					end
							
				else --HMS' version is explosive because of course.
						
					p.smsatomdivestun = nil
					if (p.pflags & (PF_ANALOGMODE|PF_DIRECTIONCHAR))
						p.drawangle = BOUNCEANGLE+ANGLE_180 //$ + ANGLE_180
					end
					s.angle = BOUNCEANGLE+ANGLE_180 //$ + ANGLE_180
					A_SMSSTOMPQUAKE(s, 95, 10, 30*FRACUNIT, 0, (ANG20*2), 0, 22, SKINCOLOR_SUPERRED5)
					
					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)
					ghs.target = s
					
					P_NukeEnemies(s, s, 400*s.scale)
					S_StartSound(s, sfx_brakrx)
					S_StartSound(s, sfx_s3kcfs)
				end
				s.SMS_WallBounce = 1
			end
		return --
		
	elseif s.HMS_airboostspeed and p.hypermysticsonic
	and (p.sms_c1 == 1 or (s.HMS_airboostspeed > 250*s.scale+60*FRACUNIT) and p.sms_c1 < 8)
	and not p.smsatomdivestun
	and s.SMSLASTANGLE != nil
	and s.HMS_airboostspeedtimer and p.hmsdash
	
		if (FixedAngle(SMS_AngleDifference((p.cmd.angleturn<<16),s.SMSLASTANGLE, 2, ANG1*120, 2)) < 20*FRACUNIT)
		
			local x,y = P_ClosestPointOnLine(s.x, s.y, line)
			local BOUNCEANGLE = R_PointToAngle2(s.x, s.y, x, y)
			local ANGSHIFT,PLUS = SMS_AngleDifference(BOUNCEANGLE,SMS_PlayerAngle(s,p), 2, ANGLE_90, 2)
			BOUNCEANGLE = $+(ANGSHIFT*PLUS)
			p.smsatomdivestun = 2
			if (p.pflags & (PF_ANALOGMODE|PF_DIRECTIONCHAR))
				p.drawangle = BOUNCEANGLE+ANGLE_180 
			end
			s.angle = BOUNCEANGLE+ANGLE_180	
			if p == displayplayer
				P_StartQuake(450*FRACUNIT, 6)
				if CV_FindVar("renderer").value == 2 --Not on software...
					P_FlashPal(displayplayer, PAL_NUKE, 6)
				end
			end
			if line.frontside and not SMS_SkyBoxWall(s, line, 0) --Wall crater!
				SMS_WallCrater(s,s,line)
				for i = 1,3
					SMS_CraterSpew(s, s.angle+ANGLE_180, 11)
				end
				S_StartSound(s, sfx_smsfx6)
			end
		end
	end
	if (p.cmd.buttons & BT_SPIN) and not P_IsObjectOnGround(s) 
	and not p.hypermysticsonic --Player's prepping a walljump. They'd hate to wallslide.	
		if (leveltime % 3 == 0)
		and not p.smsblastboost
		and not SMS_Locked(s, p, nil, true)
		and not SMS_SkyBoxWall(s, line, 0) --IF we didn't hit a skybox wall, continue!
			S_StartSound(s, sfx_s3k7e)
			local ghs = P_SpawnMobjFromMobj(s, FixedMul(13*s.scale, cos(s.angle+ANGLE_90)),FixedMul(13*s.scale, sin(s.angle+ANGLE_90)), s.height+s.momz, MT_SPINDUST)
			ghs.frame = SMSLocalRNG(4,2,s)-2|FF_TRANS10
			ghs = P_SpawnMobjFromMobj(s, FixedMul(13*s.scale, cos(s.angle-ANGLE_90)), FixedMul(13*s.scale, sin(s.angle-ANGLE_90)), s.height+s.momz, MT_SPINDUST)
			ghs.frame = SMSLocalRNG(0,2,s)|FF_TRANS10
			if not s.SMS_squashtable
				s.spritexscale = FRACUNIT-5100
				s.spriteyscale = FRACUNIT+8100
				s.spriteyoffset = SMSLocalRNG(-8,8,s)*(FRACUNIT/5)
				s.spritexoffset = SMSLocalRNG(-20,20,s)*(FRACUNIT/7)
				s.SMS_squashtable = {timer = 2, Xspeed = 4200, Yspeed = 7500, air = true}
			end
		end
		return --
	end
	if not (p.speed > 60*s.scale) --Hit a wall real fast with speed? Slide along rather than losing all speed!
	or ((p.cmd.buttons & BT_SPIN) and (p.pflags & PF_JUMPED)) --Player's prepping a walljump. They'd hate to wallslide.
	or s.SMS_WallJumpTable
		return --
	end
	local SLIDE = false

	if (p.sms_boostlevel >= 64)
	and ( (p.pflags & (PF_JUMPED|PF_THOKKED|PF_SPINNING)) or p.panim == PA_RUN or ((s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04)))
		if not (p.cmd.forwardmove < -29)
			SLIDE = true
		end
	end
	if (p.speed > 100*s.scale) or (s.state == S_SMS_AIRBOOST)
		if (p.cmd.buttons & BT_CUSTOM1) or p.sms_autoboosthold
			if ((p.cmd.forwardmove) > 3) and not ((s.state == S_SMS_AIRBOOST) and not (abs(p.cmd.sidemove) > 10))
				SLIDE = true
			end	
		end
	end
	if (p.pflags & PF_SPINNING) 
		if P_IsObjectOnGround(s) and not (p.pflags & (PF_STARTDASH|PF_SLIDING))
			SLIDE = true
		end
	end
	if SLIDE == true
	and not SMS_Locked(s, p, true)
	and not (s.flags2 & MF2_TWOD) and not (twodlevel) --Not in 2D mode	
	
			if p.hypermysticsonic or not s.SMSwallslideCD or (s.SMSwallslideCD < 50)
		
				local x,y,BOUNCEANGLE = P_ClosestPointOnLine(s.x, s.y, line)
				local BOUNCEANGLE = R_PointToAngle2(s.x,s.y, x, y)
				local MOMANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy) //SMS_PlayerAngle(s,p)	
				local ANGSHIFT,PLUS = SMS_AngleDifference(BOUNCEANGLE,MOMANGLE, 2, ANG20*2, 2)			
				if (ANGSHIFT < ANG20) return end --You ran RIGHT into the wall. Wow. Good job.				
				BOUNCEANGLE = $+(ANGSHIFT*PLUS)	
				
				local PRESPEED = p.speed/4
				s.momx = FixedDiv(FixedMul($, 25), 32)
				s.momy = FixedDiv(FixedMul($, 25), 32)
				P_Thrust(s, BOUNCEANGLE+ANGLE_180, PRESPEED)
				
				--Change angle a bit.
				if not P_IsObjectOnGround(s)
					BOUNCEANGLE = ((ANGSHIFT*PLUS) or 1)/16
					s.SMSwallslideCD = s.SMSwallslideCD and $+16 or 16
				else
					BOUNCEANGLE = ((ANGSHIFT*PLUS) or 1)/8
					s.SMSwallslideCD = s.SMSwallslideCD and $+8 or 8
				end
				s.angle = $-BOUNCEANGLE
				if s.SMSwallslideCD >= 50 s.SMSwallslideCD = 75 end --BREAK! Can't wallslide a while.
				return true --
			else
				s.SMSwallslideCD = s.SMSwallslideCD and min($+2,75) or 2
			end
	end
end)

//Old Super Tails' 2.1 Whirlwind ability!
addHook("JumpSpinSpecial", function(p)
	if p.mysticsuper and (p.charability == CA_FLY) and not p.powers[pw_tailsfly] and p.mo
		if not (p.spinhold and (p.spinhold > 1)) and not (p.pflags & PF_THOKKED)
			p.pflags = $ & ~PF_SHIELDABILITY
			P_DoJumpShield(p)
			p.mo.momz = $*2
			p.pflags = $|PF_SHIELDABILITY
		end
	end
end)

//SMS uses a custom thok merging the vanilla homing attack with extended capabilities+effects and a vanilla speed thok.
addHook("AbilitySpecial", function(p)
	if not (p.mo and p.mo.skin == "sms") return end
	local s = p.mo	
	//Always ignore thok ability in this case.
	if p.hypermysticsonic or p.secondjump == 167 or s.stompnegatedinput
		return true --
	end	
	--Turn stomp into thok-stomp.
	if (p.sms_stompanim or p.sms_afterstompthok)
		if not (p.sms_oldhoming and not (p.cmd.buttons & BT_USE) and (p.pflags & PF_THOKKED))
			p.sms_stompanim = 0
			p.hometrickanim = false
			p.sms_afterstompthok = true
		end
	end
	
	--Defaults
	p.charflags = $ & ~SF_MULTIABILITY
	p.charability = CA_HOMINGTHOK
	p.thokitem = 0
	
	--Do a walljump from a thok?
	if s.SMS_WallJumpTable and (s.SMS_WallJumpTable.timer > 12)
	and (s.SMS_WallJumpTable.straightjump or s.SMS_WallJumpTable.wallbounce)
		SMS_WallJumpDelete(s, true) 
		s.SMS_WJauto = 0 --Not if we're currently doing those walljumps.
	else
		SMS_WallJumpDelete(s, true)
		s.SMS_WJauto = (not p.homing) and 3 or 0 --Yes!
		
		--Hyper specific legacy walljump fix.
		if not s.SMS_walltable s.SMS_walltable = {} end
		s.SMS_walltable[0] = P_SpawnMobjFromMobj(s, FixedMul(-10*s.scale, cos(s.angle)),FixedMul(-10*s.scale, sin(s.angle)), 0,MT_SMSWALLJUMP) 
		local ghs = s.SMS_walltable[0]
		ghs.target = s ghs.tracer = s
		ghs.fuse = 6
		ghs.SMS_number = 0
		ghs.flags = MF_NOBLOCKMAP|MF_NOGRAVITY|MF_MISSILE|MF_NOSECTOR
		ghs.angle = s.angle
		P_InstaThrust(ghs, s.angle, 55*s.scale)		
	end
	if s.SMS_WJauto
		s.SMS_preWJautopenalty = s.SMS_WJpenalty
	else
		s.SMS_preWJautopenalty = nil
	end
	
	--Now, determine what thok to use.
	if p.homing
		p.actionspd = skins["sms"].actionspd
	else
		if p.sms_oldhoming and not (p.cmd.buttons & BT_USE)
			p.charability = CA_THOK
			p.actionspd = skins["sms"].actionspd
			p.charflags = $ & ~SF_MULTIABILITY		
			if (p.pflags & PF_THOKKED) or (s.state == S_SMS_AIRBOOST) or not (p.pflags & PF_JUMPED)
				return true --Cancel entirely.	
			end
		else
			if (p.cmd.buttons & BT_USE) or p.smsblastboost
				if not p.sms_wallstat
					p.charflags = $|SF_MULTIABILITY
				end
				p.thokitem = MT_SMSAURATRAIL
				if p.smsblastboost
					p.actionspd = 142*FRACUNIT --GO!
				else
				p.actionspd = skins["sms"].actionspd		
				end
			else
				p.actionspd = 142*FRACUNIT
			end	
		end
		--Animation set.
		if p.sms_oldhoming and not (p.cmd.buttons & BT_USE) 
		or SMS_TryForcedHomingAttack(s, p)
		or (p.actionspd == 142*FRACUNIT and not (p.pflags & PF_THOKKED))
			if not (s.SMS_DLtrail and (s.SMS_DLtrail.timer > 5)) and not ((p.pflags & PF_THOKKED) and not p.homing)
				s.SMS_DLtrail = {timer = 5, colorized = true,
				spriteyscale = FRACUNIT-19000,
				spritexscale = FRACUNIT-16000,
				spriteyoffset = 4*FRACUNIT,
				scalespeed = FRACUNIT/30,
				fuse = 12,
				COLORT_trail = {SKINCOLOR_MYSTICAL4, SKINCOLOR_MYSTICAL4, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_EMERALD, SKINCOLOR_LIME},
				COLORT_trailmax = 5} 
			end
			if not (s.SMS_squashtable and s.SMS_squashtable.timer > 5) --Thok squash.	
				s.spritexscale = FRACUNIT-11500
				s.spriteyscale = FRACUNIT+14000
				s.SMS_squashtable = {timer = 5, Xspeed = 4500, Yspeed = 5000, air = true}
			end
		end			
	end
	
	if (p.charability == CA_HOMINGTHOK) --A juiced up homing attack!

		if not (p.pflags & PF_THOKKED)	
			p.sms_readyairboost = 0
			s.atomdiveusebuffer = 0
			p.sms_jumphold = 10			
	 		if p.hometrickanim 
			or (p.panim == PA_FALL)
			or (s.state >= S_SMS_STOMP1) and (s.state <= S_SMS_ADVFALL2)
				p.hometrickanim = false
				if not P_IsObjectOnGround(s)
					s.state = S_SMSTHOKANIM
					p.panim = PA_ROLL				
				end
	  		end		
		elseif not p.homing
			s.smsthokanim = true
		end
		
	elseif (p.charability == CA_THOK) and not (p.pflags & PF_THOKKED) --Rarely used nowadays.
		p.sms_readyairboost = 0
		p.sms_jumphold = 10
		s.atomdiveusebuffer = 0
		s.smsthokanim = true
		
		if p.hometrickanim 
		or (p.panim == PA_FALL)
		or (s.state >= S_SMS_STOMP1) and (s.state <= S_SMS_ADVFALL2)
			p.hometrickanim = false
			if not P_IsObjectOnGround(s)
				s.state = S_SMSTHOKANIM
				p.panim = PA_ROLL					
			end
		end
	end
	
	--Always force actionspeed in these cases!
	if p.homing or (p.cmd.buttons & BT_SPIN) 
		p.actionspd = skins["sms"].actionspd
	end
end)

--Treat Mystic Super like regular Super, ignoring shields.
addHook("ShieldSpecial", function(p)
	if p.mysticsuper and leveltime //Simple and clean.
		return true --
	end
end)


--PreThinkFrame is good for manipulating player controls.
addHook("PreThinkFrame", do
	for p in players.iterate if p.sweepangle == nil SetUpSMSreborn(p) end -- ♫ You will know our variables ♫ 
		if p.mo
			local s = p.mo
			if s.SMS_ControlAlter
				SMS_AlterControls(s,p, s.SMS_ControlAlter)
			end
			if s.skin == "sms" 
				SMS_WallBustPower(s,p)	
				--Perform the walljump from the prethinkframe so we can alter controls.
				if s.SMS_wallready
					if (not (p.cmd.buttons & BT_SPIN) or s.SMS_WJauto)
						if not s.SMS_wallwait
							if (p.pflags & PF_JUMPED)
							or p.sms_stompanim or p.hometrickanim
							or (s.state == S_SMS_AIRBOOST)
							or (p.panim == PA_FALL)
							or (p.panim == PA_ROLL) and not (p.pflags & (PF_SPINNING|PF_STARTDASH))
							or ((s.eflags & MFE_JUSTHITFLOOR) and s.standingslope and abs(s.standingslope.zdelta) > 53999) --Allow the 1 frame on slopes!
								if not (s.eflags & MFE_SPRUNG)
								and not p.hypermysticsonic
									SMSDoWallJump(s,p)
								end
							end
						end
					end
				end
				if s.SMS_preWJautopenalty
					if not s.SMS_WJauto
						s.SMS_preWJautopenalty = nil
					end
				end
				--Minor improvement over vanilla controls, ALWAYS let SMS land into a spin if the player holds spin.
				if (s.eflags & MFE_JUSTHITFLOOR) and (p.cmd.buttons & BT_SPIN)
					if (p.panim == PA_RUN)
					or (p.panim == PA_WALK) 
					or (s.state >= S_SMS_BOOST01) and (s.state <= S_SMS_BOOST04)
						if not (p.pflags & (PF_SPINNING|PF_SLIDING))
							p.pflags = $ & ~PF_USEDOWN
						end
					end
				else					
					if p.sms_autoboosthold and (p.sms_autoboosthold > 2) and (abs(p.cmd.forwardmove) > 4)
						if (p.panim == PA_RUN) --Usually can only gain boostlvl on the ground.								
						or P_IsObjectOnGround(s) and (p.speed > 22*s.scale)
							p.cmd.buttons = $|BT_CUSTOM1
							p.sms_autoboosthold = $-1
						else
							p.sms_autoboosthold = 2
						end
					end
				end
			end
			if p.sms_sontransform --Currently transforming into SMS?
				SMS_DoTransform(s, p)
			elseif s.skin != "sms"
				s.tnospina = nil
				SMS_TransformInput(s, p) --Allow input for transformation into SMS.
			end
		end
	end
end)

--Did we have "displayplayer" back in 2.1? This was the superhacky solution for player-dependent quakes at the time.
--local smsp
--hud.add(function(v, p)
--	smsp = p
--end)
local SwitchSMSBattleScript = false

--This is the ThinkFrame, necessary to make some things work in the correct order.
addHook("ThinkFrame", do
for p in players.iterate
	if p.hfear and p.spectator //or ((p.realmo and p.realmo.valid) and not (p.mo and p.mo.valid))
		for sector in sectors.iterate
			sector.lightlevel = ($+31)
		end
		P_SetupLevelSky(p.hfear)
		for z in players.iterate
			z.hmshascome = nil
			P_FlashPal(z, PAL_MIXUP, 3)
		end
		S_StartSound(nil, sfx_s3k66)
		p.hfear = nil
		p.powers[pw_invulnerability] = 2
		p.powers[pw_shield] = SH_NONE	
	end
	if p.ffdelaycs and p.sms_originalcolor
		p.skincolor = p.sms_originalcolor
		if p.mo and p.mo.valid
			p.mo.color = p.sms_originalcolor
		end
		p.ffdelaycs = $-1
	end	
	
	if p.mo and p.mo.valid 
	local s = p.mo
		--all skins apply!
		if s.smsqtime and s.smsqlvl and (s.smsqdist or splitscreen)
			if displayplayer and displayplayer.valid and displayplayer.mo and not displayplayer.nosmsquakespls
			local m = displayplayer.mo
				if splitscreen or (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z) < s.smsqdist*FRACUNIT)
					P_StartQuake(s.smsqlvl, 1)
					s.smsqtime = max($-1, 0)
				end
			end
		end	
		if p.dohmscarry
			if s.hmspickuptarget and s.hmspickuptarget.valid
			local t = s.hmspickuptarget
			s.momx = t.momx
			s.momy = t.momy
			s.momz = t.momz
				if t.player and t.player.drawangle != nil
					p.drawangle = t.player.drawangle
				end
			P_MoveOrigin(s, t.x, t.y, t.z-(43*t.scale))
			end
			p.dohmscarry = false
		end		
		
		--Just SMS applies from here on out!	
		if (s.skin == "sms") 		
			if p.hypermysticsonic --HMS animations!
				HMS_Animation(s, p, 1)
			end
			if p.smsdivefloat 
			or p.smsdivetimer
			or p.atomdiveaim and p.hypermysticsonic
				p.atomdiveaim = true
				A_AtomDiveAim(p)
				if p.mo and (P_MobjFlip(p.mo) == 1)
					p.aiming = -4850*FRACUNIT
				else
					p.aiming = 4850*FRACUNIT
				end
			end


			if p.smsblastboost --Are you using Legend/Legacy Mode?
	 	 		p.charability = CA_HOMINGTHOK
	 	 		if not p.homing
			 		p.actionspd = 142*FRACUNIT //Emulate SMS' 1.09.4 multithok.
	 	 		else
	 	 			p.actionspd = skins[s.skin].actionspd
	 	 		end
	 	 		if (p.cmd.buttons & BT_CUSTOM1)
	 	 		and not (p.pflags & (PF_STARTDASH|PF_JUMPED))
	 	 		and not (p.cmd.forwardmove < 0)
	 	 		and not ((s.eflags & MFE_UNDERWATER) and not p.hypermysticsonic)
	 	 		and not (G_GametypeHasSpectators() and not p.rings)
	 	 		and not p.powers[pw_carry]
	 	 		and P_IsObjectOnGround(s)
	 	 		and s.health		
	 	 		 	SMSblast_boost(s, p)
	 	 		else
	 	 		 	s.smsblastboost1 = false
	 	 		end
	 	 		if (p.pflags & PF_THOKKED) and (p.pflags & PF_JUMPED)
	 	 		and not p.sms_wallstat
	 	 		 	if p.smsblastboost --SMSunleashed walljump
	 	 		 		P_SpawnSpinMobj(p, MT_SMSWALLJUMP)
		 		 	end
	 	 		 	p.pflags = $ & ~PF_THOKKED --Infinite multi-speed-thok.
	 	 		end
			elseif s.changedmyrsd
				p.runspeed = skins[s.skin].runspeed
				p.actionspd = skins[s.skin].actionspd
			end
			if (p.charflags & SF_SUPER)
			p.charflags = $ & ~SF_SUPER
			   if (p.pflags & PF_JUMPED) and not (p.powers[pw_invulnerability] or p.powers[pw_shield])
			  	 p.pflags = $|PF_USEDOWN //Fighting these pesky "all super" addons that dare conceive Super Super Mystic Sonic
			  end
			end
			if p.powers[pw_super]
			p.powers[pw_super] = 0 //You're already super enough. It's in your darn name.
			p.powers[pw_emeralds] = 0
			p.charflags = $ & ~SF_SUPER
					if (s.state >= S_PLAY_SUPER_TRANS1) and (s.state <= S_PLAY_SUPER_TRANS6)
					P_FlashPal(p, PAL_NUKE, 4)
					S_StartSound(s, sfx_lose)
					s.state = S_PLAY_FALL
					CONS_Printf(p, "\x85"+"You're already super! What are you trying to pull?")
				end
			end
			
		elseif (s.skin == "sonic") --Battle Script! Let Sonic Transform into SMS! Support only up to version 6 of Battlemod.
		
			--SMS' battlemod's script will never initialize if SMS is disabled from the start, just FYI
			if p.smssonicbattle and CBW_Battle
				if not SwitchSMSBattleScript
					if CBW_Battle.SkinVars --7.0+ support.
						CBW_Battle.SkinVars["sms"] = {
 						weight = 110,
						flags = 0,
 						shields = 0, --glass cannon, high risk.
						special = CBW_Battle.Action.SMSTransform,
 						guard_enabled = false, --no guarding.
 						guard_frame = 2, --shouldn't apply anyway, but...
 						func_guard_trigger = CBW_Battle.GuardFunc.Parry,
 						func_priority = CBW_Battle.Priority_FullCommon,
 						func_priority_ext = nil
						}
						SwitchSMSBattleScript = true
					end
				else
				--	CBW_Battle.Action.SMSTransform = function(s, doaction)	
					if SMS_BattleMod(s, p)
						return --
					end
				end
			end
		end
		
		--SMS skin check is gone again
		
		if s.resetsongtodayok --Cut that shit out!
			s.resetsongtodayok = nil
			if p.mysticsuper
				COM_BufInsertText(p, "tunes -default")
			end
		end
		
		if s.nuuuuuuuuu --Probably what the victim is screaming right about now! (Player's getting HMS' godbreakered)
	
			s.frame = $ & ~FF_FRAMEMASK 
			s.nuuuuuuuuu = nil
			
		elseif s.health		
			--SMS pick up system! Keep object in front of you. 
			--Purposely leaving a bug that lets other characters keep SMS' carry, btw.
			if p.smspickup
				if not p.smspickup.valid --The hell? Disappear.
					p.smspickup = nil
				else
					local t = p.smspickup
		
					t.z = s.z+((s.height/2)*P_MobjFlip(s))
			
					if not t.player
						t.angle = p.drawangle
					else
						t.player.drawangle = p.drawangle //Let players rotate their camera
					end
			
					t.iampickedupbysmshelp = 2
					t.eflags = s.eflags
					t.momz = 0
					t.pmomz = s.pmomz
					t.momx = s.momx
					t.momy = s.momy
					t.rollangle = s.rollangle
				
					if t.fuse --The thing we're holding has a fuse? Infinitely extend it.
						t.fuse = $+1 
					end
				
					--Use TryMove to make things seem like they make sense
					if not ( FixedHypot(t.x-s.x, t.y-s.y) > 130*s.scale)
						P_TryMove(t,
						s.x+FixedMul(t.info.radius*20/9, cos(p.drawangle)),
						s.y+FixedMul(t.info.radius*20/9, sin(p.drawangle)),
						true)
					else --But if they somehow get too far away, forcibly teleport!
						P_MoveOrigin(t,
						s.x+FixedMul(t.info.radius*20/9, cos(p.drawangle)),
						s.y+FixedMul(t.info.radius*20/9, sin(p.drawangle)),
						s.z+FixedMul(27*FRACUNIT,s.scale)*(P_MobjFlip(s)))
					end
				end
			end
			
			--Become brighter than other characters!
			if (s.skin == "sms")	
			or s.smsglowticket --glowticket applies on objects and Mystic Supers too, hence no sms skin check.			
				if (p.powers[pw_carry] != CR_NIGHTSMODE) --NiGHTS mode has it's own behaviour.
					s.SMS_tempRF = 2
					s.renderflags = $|RF_FULLBRIGHT
				end
			elseif s.SMS_tempRF
				s.SMS_tempRF = $-1
				if not s.SMS_tempRF --undo renderflag changes after 2 tics.
					s.SMS_tempRF = nil
					s.renderflags = $ & ~RF_FULLBRIGHT
				end
			end	
		end
	end
end
end)

local OverLayMan = nil 
addHook("MobjThinker", function(s)
	if s.valid and s.target and s.target.player and s.target.player.mysticsuper and s.target.skin == "sonic"
		OverLayMan = s
		if (s.target.player.pflags & PF_DIRECTIONCHAR) 
			OverLayMan.directchar = true
		end
		if CBW_Battle //Battlemoooooodddd....
			s.goddamnitwhybattlemod	= true
		end
	end
end, MT_OVERLAY)

--Sometimes you need a postthinkframe to assure nothing else can take priority rendering-wise.
addHook("PostThinkFrame", do

	if OverLayMan and OverLayMan.valid and OverLayMan.target and OverLayMan.target.valid and OverLayMan.target.player
		if mariomode
			OverLayMan.color = OverLayMan.target.color
		end
		if OverLayMan.directchar
			OverLayMan.angle = OverLayMan.target.player.drawangle
			OverLayMan.directchar = nil
		end
		if OverLayMan.goddamnitwhybattlemod
			OverLayMan.goddamnitwhybattlemod = nil
			OverLayMan.flags2 = $ & ~MF2_DONTDRAW
		end
	end
	
for p in players.iterate	
	if (p.mo and p.mo.valid)
	local s = p.mo
	
		--Be SMS from here on out.
		if s.skin == "sms"
			if p.hypermysticsonic --HMS animations. Doing it again in postthink for better accuracy.
				HMS_Animation(s, p)		
			else --if not p.hypermysticsonic		
				if s.HayMakerPunch --Allow player to float in the air for a bit.
					s.HayMakerPunch = $-1
					if s.SMS_HomingBolt
						s.sprite = SPR_SMSO
						s.frame = ($ & ~FF_FRAMEMASK)|Q
					elseif (s.z > s.floorz+s.height)
					and not (p.pflags & (PF_JUMPED|PF_SPINNING)) 
					and not p.sms_didairboost
					and not (p.speed > 38*s.scale)
					and not p.hypermysticsonic
						s.sprite = SPR_SMSO
						s.frame = ($ & ~FF_FRAMEMASK)|S
						if (s.momz*P_MobjFlip(s) < 4*s.scale)
						and not P_IsObjectOnGround(s)
							P_SetObjectMomZ(s, FRACUNIT/3, true)
						end
					else
						s.HayMakerPunch = nil
					end
					
				elseif p.sms_comethaymaker --Chasing? Stay in chasing frames, then.
				
					if (s.target and s.target.valid)
						s.sprite = SPR_SMSO
						s.frame = ($ & ~FF_FRAMEMASK)|R
					end
				
				elseif ((p.footsweep03 > 10) or ((p.footsweep02 > 5) and (p.footsweep02 < 18)))
				and (s.state >= S_SMS_FOOTSWEEP1) and (s.state <= S_SMS_FOOTSWEEP8)
					s.sprite = SPR_SMSO --Speed animation up for later footsweeps.
					s.frame = ($ & ~FF_FRAMEMASK)|SMS_TimeFrame(7, E)
					
				elseif s.sms_juststomped --Minor crouch animation to make stomps feel more impactful.
				
					s.sms_juststomped = $-1
					if s.sms_juststomped
					
					and (
					((p.panim == PA_RUN) and s.sms_juststomped > 1)
					or (p.panim == PA_IDLE)
					or (p.panim == PA_WALK)
					or (s.state == S_SMS_ATOMLAND1) or (s.state == S_SMS_ATOMLAND2) or (s.state == S_SMSTOSSREADY)
					)
					
					and (P_IsObjectOnGround(s) and not (p.pflags & PF_SPINNING) or p.powers[pw_carry] == CR_MINECART)
						if s.SMS_squashtable
							s.SMS_squashtable = nil
							s.spriteyoffset = 0
						end
						s.sprite = SPR_SMSO
						s.frame = ($ & ~FF_FRAMEMASK)|Q
						
						local UW = 12000
						if (s.eflags & MFE_UNDERWATER)
							s.sms_juststomped = min($,4)
							UW = 18000
						end
						s.spritexscale = max(FRACUNIT/2, FRACUNIT-UW+(s.sms_juststomped*9001))
						s.spriteyscale = min(FRACUNIT+45356, FRACUNIT+UW-(s.sms_juststomped*7000))
						s.sms_squashy = true
						if (p.speed > s.scale)
							for i = -1,1,2
								local ghs = P_SpawnMobjFromMobj(s,
								FixedMul(35*s.scale*i, cos(p.drawangle)), 
								FixedMul(35*s.scale*i, sin(p.drawangle)), 
								0, MT_THOK)
								ghs.state = S_SPINDUST2
								ghs.fuse = 10
							end
						end
					else
						if s.sms_squashy
							s.spriteyscale = FRACUNIT
							s.spritexscale = FRACUNIT
							if ((p.panim == PA_IDLE) or p.skidtime) and s.sprite == SPR_SMSO
							and (s.frame & FF_FRAMEMASK) == Q and P_IsObjectOnGround(s)
								if p.skidtime
									s.state = S_PLAY_WALK
								else
									s.state = S_PLAY_STND
								end
							end
							s.sms_squashy = nil	
						end			
					end
				elseif s.sms_stompspin != nil
					if not p.sms_stompanim
					or p.sms_afterstompthok
					or (s.stompanimburst <= 6)
					or p.powers[pw_carry]
					or p.powers[pw_nocontrol]				
						s.sms_stompspin = nil
						if s.hmsspinpass != nil
							p.drawangle = s.hmsspinpass
							s.hmsspinpass = nil
						end	
					else						
						s.sms_stompspin = $+ANGLE_45
						p.drawangle = s.sms_stompspin
					end
				elseif (p.powers[pw_carry] == CR_PTERABYTE) and s.state != S_SMS_ADVFALL1
					s.state = S_SMS_ADVFALL1	
				end
			end
			if p.hmsenoughdeath and p.hmsrushactive --SMSchallenges related.
				if (p.hmsenoughdeath < 370) and (gametype == GT_COOP)
			  	 	s.sprite = SPR_HMSO
			   		s.frame = ($ & ~FF_FRAMEMASK)|B
			  	 	s.tics = 500
				end
			end
			if s.SMS_stompmom
				if not p.homing --Freeze when homing!
					if P_IsObjectOnGround(s) and (s.SMS_stompmom*P_MobjFlip(s) < -3*s.scale)
						if p.hypermysticsonic
							SMS_stompimpact(s,p,3)
						elseif (s.SMS_stompmom*P_MobjFlip(s) < -61*s.scale)
							SMS_stompimpact(s,p,2)
						else
							SMS_stompimpact(s,p)
						end
					elseif p.powers[pw_carry] or (s.momz*P_MobjFlip(s) > -20*s.scale)
						s.SMS_stompmom = 0
					end
				end
			end
			s.SMS_lastz = s.z --For posterity
		end
		
		--End of SMS skin check.
		if s.rglowticket --ALWAYS go after all player animations have played out!
			SMSRglow(s, true)
		end
		s.NoSMSGlow = nil
		s.SMS_locked = nil --Unlock SMS actions after everything.
	end
end
end)

//Now you can play as Toilet Paper and SMS on the run from hoarders! Okay, okay, TP stands for True Player in this case.
//SMS is actually quite a useful partner. Good at fighting, giving you a boost, powersharing, and can open inaccessible areas for you

addHook("BotTiccmd", function(p, cmd)
if not (players[0] and players[0].valid and players[0].mo) return end --

	local tp = players[0]
	if tp.powers[pw_carry] == CR_PLAYER and (tp.cmd.buttons & BT_CUSTOM3)
	and tp.mo.skin == "sms"
	and p.powers[pw_tailsfly]
	and p.bot == 1
		cmd.forwardmove = tp.cmd.forwardmove
		if not (tp.transformdown and tp.transformdown > 2)
			cmd.buttons = $|BT_CUSTOM3
		end
		return --
	end

if not (p.mo and p.mo.skin == "sms" and p.bot == 1) return end --

local s = p.mo
local tp = players[0]
	if s
		if p.powers[pw_shield]
			if (tp.powers[pw_shield] == SH_PINK or tp.powers[pw_shield] == SH_PITY or tp.powers[pw_shield] == 0) 
			and not (tp.powers[pw_shield] == SH_PINK and p.powers[pw_shield] == SH_PINK)
			tp.powers[pw_shield] = p.powers[pw_shield]
			P_SpawnShieldOrb(tp)
			P_FlashPal(tp, PAL_MIXUP, 4)
			CONS_Printf(tp, "\x83"+"SMS gave you his shield!")
			p.powers[pw_shield] = 0
			end
		end
		if not tp.powers[pw_invulnerability] and p.powers[pw_invulnerability] and (p.powers[pw_invulnerability] > 95)
		tp.powers[pw_invulnerability] = p.powers[pw_invulnerability]
		P_FlashPal(tp, PAL_MIXUP, 4)
		CONS_Printf(tp, "\x83"+"SMS is sharing his invulnerability powers!")
		end
		if not tp.powers[pw_sneakers] and p.powers[pw_sneakers] and (p.powers[pw_sneakers] > 95)
		tp.powers[pw_sneakers] = p.powers[pw_sneakers]
		P_FlashPal(tp, PAL_MIXUP, 4)
		CONS_Printf(tp, "\x83"+"SMS is sharing his sneaker powerup")
		end
		if s.smsbothomingspam
			if p.hometrickanim and (s.smsbothomingspam < 5)
			s.smsbothomingspam = 19
			else
			s.smsbothomingspam = $-1
			end
		cmd.buttons = $|BT_SPIN|BT_CUSTOM1
		cmd.forwardmove = 22
		  if not p.homing
			if (s.smsbothomingspam > 29)
			cmd.buttons = $|BT_JUMP
				if P_IsObjectOnGround(s)
				s.smsbothomingspam = 29
				end
			elseif (s.smsbothomingspam % 6 == 0)
			cmd.buttons = $|BT_JUMP
			else
			cmd.buttons = $ & ~BT_JUMP
			end

			searchBlockmap("objects",function (s,mo)
				if not (mo and mo.valid and mo.health)
				return true
				elseif (mo.flags & (MF_BOSS|MF_ENEMY))
				and P_CheckSight(s,mo) and not (mo.z > s.z+s.height)
					s.angle = R_PointToAngle2(s.x, s.y, mo.x, mo.y)
					return true
				end
		  	end,s, s.x-600*s.scale,s.x+600*s.scale, s.y-600*s.scale,s.y+600*s.scale)			
			
		  end
		return true
		end
		
		if p.smspickup and (tp.cmd.buttons & BT_TOSSFLAG)
			cmd.buttons = $|BT_TOSSFLAG
			s.smsbottf = 0
		return true
		end
		if (tp.cmd.buttons & BT_TOSSFLAG)
		or (tp.pflags & PF_STARTDASH) and (tp.cmd.buttons & BT_JUMP)
		or (tp.cmd.buttons & BT_SPIN) and (tp.pflags & PF_JUMPED) and (tp.spinhold != nil) and (tp.spinhold < 2)
		s.smsbottf = 0
			if (P_IsObjectOnGround(s) or (p.pflags & PF_JUMPED) or p.hometrickanim or (s.state == S_PLAY_SPRING) or (s.state == S_SMS_ADVFALL1) or (s.state == S_SMS_ADVFALL2))
			and not p.footsweepanim //Attack in this direction
				cmd.buttons = $|BT_CUSTOM1|BT_JUMP|BT_SPIN
				cmd.forwardmove = 25
				cmd.sidemove = 0
				if (p.pflags & PF_JUMPED) or p.hometrickanim
				s.smsbothomingspam = 29
				else
				s.smsbothomingspam = 40
				end
				return true
			end		
		end
		if (tp.cmd.buttons & BT_CUSTOM1)
			cmd.buttons = $|BT_CUSTOM1
			s.smsbottf = 0
		end
		if (tp.cmd.buttons & BT_CUSTOM2)
		cmd.buttons = $|BT_CUSTOM2
		s.smsbottf = 0
			if P_IsObjectOnGround(s) or p.footsweepanim
				if not (p.sms_boostlevel > 64)
					cmd.forwardmove = 0
					cmd.sidemove = 0
				end
				
				searchBlockmap("objects",function (s,mo)
					if mo and mo.valid and (mo.flags & (MF_BOSS|MF_ENEMY)) and mo.health
					and P_CheckSight(s,mo) and not (mo.z > s.z+(200*s.scale*P_MobjFlip(s)))
						p.botsmst = mo
						p.sweepangle = R_PointToAngle2(s.x, s.y, mo.x, mo.y)
						s.angle = R_PointToAngle2(s.x, s.y, mo.x, mo.y)
						return true
					end
		  		end,s, s.x-900*s.scale,s.x+900*s.scale, s.y-900*s.scale,s.y+900*s.scale)	
		  		if p.botsmst
				p.botsmst = nil
				else
				s.angle = tp.mo.angle
				p.sweepangle = tp.mo.angle
				p.botsmst = nil				
				end
			p.sweepspeed = 65*s.scale
			end
		end
		if (tp.cmd.buttons & BT_WEAPONPREV)			
		cmd.buttons = $|tp.cmd.buttons & ~BT_CUSTOM3 & ~BT_TOSSFLAG
		s.smsbottf = 0
		end	
		if (FixedHypot(s.x-tp.mo.x, s.y-tp.mo.y) < 200*s.scale) and not p.smspickup
			if ((tp.cmd.buttons & BT_CUSTOM3) or tp.readyforpowershare) and P_IsObjectOnGround(s)
			cmd.buttons = $|BT_CUSTOM3
			cmd.forwardmove = 0
			cmd.sidemove = 0
			s.smsbottf = 0
			elseif s.smsbottf and (s.smsbottf > 45)
			s.angle = R_PointToAngle2(s.x, s.y, tp.mo.x, tp.mo.y)
			p.drawangle = R_PointToAngle2(s.x, s.y, tp.mo.x, tp.mo.y)
			cmd.buttons = $|BT_TOSSFLAG
			cmd.forwardmove = 0
			cmd.sidemove = 0
				if (tp.cmd.buttons & BT_JUMP) and (tp.pflags & PF_JUMPED)
				cmd.buttons = $|BT_JUMP & ~BT_TOSSFLAG	
				s.smsbottf = 0
				end
			end
			return true 
		end
		if p.footsweepanim
			cmd.forwardmove = 0
			cmd.sidemove = 0	
			return true
		end
	end
end)

//Death handles for SMS and the Mystic Supers
addHook("MobjDeath", function(s, i, so)
	if s.player
	local p = s.player
		if s.ibethrownbysmshelp and p.smsgriefw == nil
		and not p.smsdontgrabme
			p.smsgriefw = 180 --Inform players they can turn off the ability to get grabbed.
		end
		if p.smspickup and p.smspickup.valid 
		and (i == p.smspickup or so == p.smspickup)
			return true --Can't get killed by what you're holding.(Crushing, usually)	
		end
		if s.skin == "sms"
			if p.hypermysticsonic
				return true --Death? A mistake, clearly.
			end
			P_FlashPal(p, PAL_MIXUP, 3)
			S_StartSound(s, sfx_s3k66)
			for d = 0,15
				P_SpawnParaloop(s.x, s.y, s.z+s.momz+s.height*2, 10*FRACUNIT, 16, MT_NIGHTSPARKLE, d*ANGLE_22h, S_SMSTSPARK1, false)
			end
			return --
		elseif p.mysticsuper --Lose Mystic Super on death, ofc.
			A_ENDMYSTICSUPER(s)
			return --
		end
	end
end, MT_PLAYER)

--Hurt enemies on touch during certain animations.
addHook("PlayerCanDamage", function(p)
	if p.mo and p.mo.skin == "sms"
	local s = p.mo	
		if p.footsweepanim
		or (s.state == S_SMS_STOMP1)
		or (s.state == S_SMS_STOMP2)
		or p.smsdivetimer // and (p.smsdivetimer > 6)
		or p.hometrickanim
		or p.homing
		or (p.speed > 120*s.scale) and p.powers[pw_sneakers]
			return true
		end
	end
end)

--No special abilities when in the menu for anyone! Also not when SMS does certain dodge stuff.
addHook("SpinSpecial", function(p)
	if p.sms_intoship
	or p.sms_shipready
	or (p.sms_dodging and p.sms_dodging > 3)
	or p.mo and (p.mo.dodgeoh or p.mo.tnospina)
		return true
	end
end)

--Enter small holes when...! Hook apparently resets height to default every other frame.
addHook("PlayerHeight", function(p)
	if p.mo and p.mo.skin == "sms"
	local s = p.mo
		if p.footsweepanim
		or (s.state == S_SMSTOSSREADY)
			return 31*FRACUNIT
		end	
	end
end)
addHook("PlayerCanEnterSpinGaps", function(p)
	if p.mo and p.mo.skin == "sms"
	local s = p.mo
		if p.footsweepanim
		or (s.state == S_SMSTOSSREADY)
			return true
		end	
	end
end)