--===================MOBJ THINKERS==============================
--Object Thinkers that aren't the player's are placed here.
--==============================================================

--GFX object when you need capechase, autoframing, gradual fading, or positional offsets. 
addHook("MobjThinker", function(s)
	--Apply autoframe behaviour? Basically an adjustable FF_ANIMATE that can loop within the same sprite.
	if s.sttspeed
		if s.sttspeed == 1 or (leveltime % (s.sttspeed) == 0) --Animation speed. If sttspeed is 2, we'd advance frames every 2 tics.
			if (s.frame & FF_FRAMEMASK) == s.sttfinalframe --Never go past this frame!
				if s.sttfirstframe != nil --Unless we want it to loop?
					s.frame = ($ & ~FF_FRAMEMASK)|s.sttfirstframe
				end
			else		
				--If you don't specify a frameloop, make sure you don't exceed the amount of available frames!
				if s.sttlastframe != nil and ((s.frame & FF_FRAMEMASK) >= s.sttlastframe) 
					s.frame = ($ & ~FF_FRAMEMASK)|(s.sttfirstframe or 0)
				else
					s.frame = $+1
				end
			end
		end
	end
	--Fade if sttfade exists.
	if s.sttfade
		local FADE = (s.sttfade == true) and 10 or s.sttfade
		if (s.fuse > FADE)
			FADE = nil
		elseif (FADE != 10) and (leveltime % 2)
			FADE = nil --Fade slower if it's not 10...
		end
		if FADE --Fade confirmed. Go!
			local trans = (s.frame & FF_TRANSMASK)
			s.frame = ($ & ~FF_TRANSMASK)|min(FF_TRANS90, (trans+FRACUNIT))
			if s.sttroll --Slow rollspeed too.
				s.sttroll = FixedDiv(FixedMul(s.sttroll, 20),21)
			end
		end
		--Automatically roll at any given speed?
		if s.sttroll
			if (s.renderflags & RF_FLOORSPRITE)
				s.angle =  $+s.sttroll
			else
				s.sttrollangle = s.sttrollangle and $+s.sttroll or s.sttroll
				s.rollangle = s.sttrollangle
			end
		end	
	end
	--Scroll graphic?
	if s.sttXspeed
		s.spritexoffset = $+s.sttXspeed	
	end
	if s.sttYspeed
		if s.floorspriteslope --Raise the tilt of slopesprites instead!
			local EX,EY,EZ = s.floorspriteslope.o.x, s.floorspriteslope.o.y, s.floorspriteslope.o.z
			s.floorspriteslope.o = {x = EX, y = EY, z = EZ+s.sttYspeed}
		else
			s.spriteyoffset = $+s.sttYspeed
		end
	end

	if not (s.tracer and s.tracer.valid) return end --Need a tracer from here on!
	local t = s.tracer
	--Apply capechase and potentially also offsets if given a tracer!
	if (s.renderflags & RF_FLOORSPRITE) --No capechase for floorsprites!
		P_SetOrigin(s, t.x, t.y, t.z)
	else
		local PREVANGLE = s.angle
		A_CapeChase(s, 1, 0) 
		if not s.valid return end --Capechase removes you sometimes, so always check s.valid afterwards!

		if s.ditchtracer --Ditch the capechase after any couple of frames?
			s.ditchtracer = $-1 --This is a number value, NOT true or false!
			if s.ditchtracer == 0
				if s.GSoffsetangFACE != nil --Face target one last time!
					local EXT = (tonumber(s.GSoffsetangFACE)) or 1
					P_MoveOrigin(s, s.x+FixedMul(s.GSoffsetX, cos(s.GSoffsetang+EXT)), s.y+FixedMul(s.GSoffsetY, sin(s.GSoffsetang+EXT)), s.z)
					s.angle = R_PointToAngle2(s.x, s.y, t.x, t.y)+ANGLE_90
				else
					s.angle = PREVANGLE --Undo capechase's angle copying.
				end 
				s.tracer = nil 
				return --
			end
		end
	end	
	
	--If you set one offset, then set them all, even if they're just 0.
	if s.GSoffsetX != nil
		local ANGLE = t.angle
		if s.GSoffsetang != nil ANGLE = s.GSoffsetang end
		s.GSoffsetwait = (s.GSoffsetwait) and 1 or 2
		
		P_MoveOrigin(s,
		s.x+ FixedMul(s.GSoffsetX/s.GSoffsetwait, cos(ANGLE)),
		s.y+ FixedMul(s.GSoffsetY/s.GSoffsetwait, sin(ANGLE)),
		s.z+ (s.GSoffsetZ or 0)*P_MobjFlip(t) )
		
		--Adjust offsets overtime? 110 would INCREASE distance by 10% each frame, 80 would DECREASE distance by 20%.
		if s.GSshiftoffsetXY and (s.GSoffsetX or s.GSoffsetY)
			s.GSoffsetX = FixedDiv(FixedMul($, s.GSshiftoffsetXY), 100)
			s.GSoffsetY = FixedDiv(FixedMul($, s.GSshiftoffsetXY), 100)
		end
		if s.GSshiftoffsetZ and s.GSoffsetZ
			s.GSoffsetZ = FixedDiv(FixedMul($, s.GSshiftoffsetZ), 100)
		end
		--Face tracer all the time? Can be 0 or false and still work, just not nil.
		if s.GSoffsetangFACE != nil 
			s.angle = R_PointToAngle2(s.x, s.y, t.x, t.y)+ANGLE_90
			if s.GSoffsetangFACE --Rotate around tracer at this speed?
				s.GSoffsetang = $+s.GSoffsetangFACE
			end
			return --Don't need to do papersprite checks then.
		end
	end
	--Properly angle papersprites, though...
	if (s.renderflags & RF_PAPERSPRITE) or (s.frame & FF_PAPERSPRITE)
	--	if s.GSpaperangle != nil --Force a paper angle at all times.
	--		s.angle = (R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy) or t.angle)+s.GSpaperangle
		if t.player
			s.angle = t.player.drawangle+ANGLE_90
		elseif (abs(t.momx) > t.scale or abs(t.momy) > t.scale)
			s.angle = R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy)+ANGLE_90
		else
			s.angle = t.angle+ANGLE_90
		end
	end	
end, MT_SMSAUTOFRAME)

//Fade slowly or set emergency fuse.
addHook("MobjThinker", function(s)
	if s.fuse else s.fuse = 16 return end
	if (s.fuse < 16) and (s.fuse % 2 == 0)
		local trans = min((s.frame & FF_TRANSMASK)+FRACUNIT, FF_TRANS90)
		s.frame = ($ & ~FF_TRANSMASK)|trans
	end
end, MT_SMSSPLAT)

//Let's ditch the A_DualAction crap and use Lua instead.

addHook("MobjThinker", function(s)
	if s.target and s.target.valid
	local t = s.target
		if s.aftertrail
		local t = s.target
			if not (s.sprite == SPR_SMBO and s.tics >= 6)
				if t.thokaurapass >= 7
					s.color = SKINCOLOR_FLAME
					s.frame = B|FF_FULLBRIGHT|FF_TRANS40
				elseif t.thokaurapass >= 5
					s.color = SKINCOLOR_ORANGE
					s.frame = B|FF_FULLBRIGHT|FF_TRANS50				
				elseif t.thokaurapass == 4
					if SMSisyellow(t)
						s.color = SKINCOLOR_GOLD
					else
						s.color = t.color
					end
					s.frame = B|FF_FULLBRIGHT|FF_TRANS70
				elseif t.thokaurapass >= 2
					s.color = t.color
					s.frame = B|FF_FULLBRIGHT|FF_TRANS80
				end
				
				if s.tics == 2
					s.frame = B|FF_TRANS80
				elseif s.tics == 1
					s.frame = B|FF_TRANS90
				end
			end
				
			s.rollangle = $+FixedAngle(s.aftertrail<<16)
			s.target.lasttrailra = s.rollangle
			s.blendmode = AST_ADD

		elseif not s.aftertrail
			s.target.thokaurapass = 8
			s.tics = 1
			s.fuse = 1
			s.blendmode = AST_ADD
		end
	end
end, MT_SMSAURATRAIL)

//For drifting, wind, underwater and other sounds, this lets me cut off sounds at any point reliably.
addHook("MobjThinker", function(s)
	if not (s.master and s.master.valid and (s.master.skin == "sms") and s.health)
		s.state = S_NULL return --
	end
	P_MoveOrigin(s, s.master.x, s.master.y, s.master.z)
	
		if s.smsrbt --Ring Burst sounds.
		
			if not s.fuse
				s.fuse = 110
			end
			if s.master.player.hometrickanim
				s.imfree = true	
			elseif (s.master.player.sms_ringburstfly != s.smsrbt) and not s.imfree
				if s.valid s.state = S_NULL return end --
			end
			
		elseif s.smsmachwb 
		
			s.smsmachwb = $+1
			if not P_IsObjectOnGround(s.master)
				if not s.smsmachwbbonus
					if (s.master.player.speed < 130<<16)
						s.smsmachwb = $+40
						s.smsmachwbbonus = true
					end
				end
				if not s.machwbtol2
					s.machwbtol2 = 1
				else
					s.machwbtol2 = $+1
				end
			end
			if not (s.master.player and (s.master.player.speed > 20<<16))	
				if not s.machwbtol
					s.machwbtol = 1
				else
					s.machwbtol = $+1
				end
			else
				s.machwbtol = 0
			end
			if (s.machwbtol and (s.machwbtol > 4))
			or (s.smsmachwb > 77)
				if s.valid s.state = S_NULL return end --
			end
			
		elseif s.smsdrtim != nil --Drifting sounds.
		
			if not s.smsdrtim
				S_StartSound(s, sfx_smsdr)
				s.smsdrtim = 65 
			else
				s.smsdrtim = $-1
			end
			if not P_IsObjectOnGround(s.master)
			or not s.master.driftsens
			or not ((s.master.state >= S_SMS_BOOST01) and (s.master.state <= S_SMS_BOOST04)) 			
				if s.valid s.state = S_NULL return end --
			end
			
		elseif s.smsmwtim != nil --Wind time.
		
			if not s.smsmwtim
				S_StartSoundAtVolume(s, sfx_machw, 10)
				s.smsmwtim = 74
			else
				s.smsmwtim = $-1
			end
			if (s.master.player.speed < 52*s.master.scale)
			or (s.master.state == S_PLAY_RUN and (s.master.player.normalspeed < 62<<16))
				if s.valid s.state = S_NULL return end --
			end
			
		elseif s.smsmwhtim != nil --Harsh wind time.
		
			P_MoveOrigin(s, s.master.x-(s.master.momx), s.master.y-(s.master.momy), s.master.z)
			if not s.smsmwhtim
				S_StartSound(s, sfx_machwh)
				s.smsmwhtim = 51
			else
				s.smsmwhtim = $-1
			end
			if not (s.master.player.hmsdash and s.master.player.hmsdash == 2)
				if not (s.master.player.speed >= 137<<16)
					if not s.smsmwhtol
					s.smsmwhtol = 1
					else
					s.smsmwhtol = $+1
					end
				end
			else
				s.smsmwhtol = 0	
			end
			if not (s.master.player.hypermysticsonic)
			or (s.smsmwhtol and s.smsmwhtol > 4)
				if s.valid s.state = S_NULL return end --
			end
			
		elseif s.smsquaketim != nil --Running quake sounds.
		
			P_MoveOrigin(s, s.master.x-(s.master.momx*2), s.master.y-(s.master.momy*2), s.master.z)
			if not s.smsquaketim
				if s.smsquakerefresh
					if SMSLocalRNG(0,1,s) //Leveltime is a bad idea here.
						S_StartSound(s, sfx_rocks4)
					end
					S_StartSound(s, sfx_cdfm42) //I dunno why this works, I made a mistake, but it works.
					s.smsquaketim = SMSLocalRNG(1, 3,s)
					s.smsquakerefresh = false
				else
					if s.valid s.state = S_NULL return end --
				end
			else
				if (s.smsquaketim > 2) and s.smsquakerefresh
				s.smsquakerefresh = false
				end
			s.smsquaketim = $-1
			end
		end
end, MT_SMSSOUNDOBJ)

addHook("TouchSpecial", function(s, v)
	if (not SMS_NerfedMode(p) and not (s.target and s.target.battlemodsms))
		return false //Just despawn flames you walk through.
	end
	//Hurt players when they touch it, the source being HMS.	
	if v and v.player and v.health
	and not (v.player.powers[pw_flashing] or v.player.powers[pw_super] or v.player.powers[pw_shield] == SH_ELEMENTAL)
	and s.target and s.target.player
	and not (v == s.target) //Don't hurt yourself or teammates
	and not (v.player.ctfteam and v.player.ctfteam == s.target.player.ctfteam) 
		if s.target.battlemodsms
			P_DamageMobj(v, s.target, s.target) //Battlemod is an ass
			return true
		else
			P_DamageMobj(v, s, s.target)
			return true
		end
	else
		return true
	end
end, MT_HMSFLAMEREST)

//Is fire a thing underwater? No, it's not.
//Also, let's pass over the target properties to our flamerest.
//That way we can make sure the score system works as it should.

addHook("MobjThinker", function(s)
	if s.target
	and (s.target.skin == "sms" and s.target.player.hypermysticsonic)
		s.creator = s.target
	elseif s.target
	and s.target.target
	and (s.target.target.skin == "sms")
	and (s.target.skin == "sms" and s.target.player.hypermysticsonic)
		s.creator = s.target.target
		s.target = s.target.target
	end
	if (s.state == S_HMSNAPALMFLAME_DIE)
	and s.creator
		s.sf2 = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_HMSFLAMEREST)
		if s.creator.valid and s.creator.player and s.creator.playerhypermysticsonic
			s.sf2.scale = s.creator.scale*P_RandomRange(0,3)
		else
			s.sf2.scale = s.creator.scale
		end
		s.sf2.target = s.creator
		s.sf2.eflags = s.creator.eflags
	end
	if (s.eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))
	or P_CheckDeathPitCollide(s)
		s.state = S_NULL //Just disappear into nothingness.
	end
end, MT_HMS_NAPALM_FLAMES)

addHook("MobjSpawn", function(s)
	if not s.scale
	or (s.scale <= FRACUNIT)
		s.scale = 2<<16
	else
		s.scale = $*2
	end
	s.color = SKINCOLOR_SILVER
	s.svrng = true
end, MT_SMSSILVERRING)

addHook("MobjThinker", function(s)
	if P_CheckDeathPitCollide(s)
	or (s.eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))
		s.state = S_NULL --
	end
end, MT_HMSFLAMEREST)

--I keep calling SMS' flames different things, like mystic flame, double flame, and huge flame.
--The official names are: Flameshot (basic shot), Cross Flame(duo shot), and Diffusion Flame!(the big explodey one!)
addHook("MobjThinker", function(s)
	if not s.startsmoke --Initial firing animation.
	
		s.renderflags = RF_NOCOLORMAPS|RF_SEMIBRIGHT
		
			if SMS_Graphix(s) == 0
			/*	local ghs = P_SpawnMobjFromMobj(s, 0,0, s.height/2, MT_TNTDUST)
			/	if ghs and ghs.valid
					ghs.scale = s.scale/8
					ghs.destscale = $*99
					ghs.fuse = 14 ghs.tics = $/2
					ghs.blendmode = AST_ADD
					ghs.flags2 = MF2_SHADOW
					ghs.momx = s.momx/4
					ghs.momy = s.momy/4
					ghs.momz = s.momz/4
				end */
				local SCALE = (s.target) and s.target.scale or FRACUNIT
				local ghs = P_SpawnMobjFromMobj(s,0,0,(-13*s.scale)-s.momz, MT_THOK)
				ghs.sprite = SPR_SMX5
				ghs.frame = (Z+9)|FF_TRANS10|FF_FULLBRIGHT
				ghs.blendmode = AST_ADD	
				ghs.spritexscale = FRACUNIT-9000
				ghs.spriteyscale = FRACUNIT-28000
				ghs.rollangle = ANGLE_180
				ghs.fuse = 15
				ghs.tics = 99
				ghs.momx = s.momx/2
				ghs.momy = s.momy/2
				ghs.momz = s.momz	
				if s.twoshot
					ghs.spritexoffset = (s.twoshot2) and 34<<16 or -34<<16
				end				
			end
			
			local NO = (s.twoshot) and 1 or -1
			
			for i = -1,NO,2
			
				local ghs = SMS_WallJumpGFX(s, true)
				if ghs and ghs.valid
					ghs.spriteyscale = FRACUNIT/7
					ghs.spritexscale = FRACUNIT/7
					ghs.colorized = true
					ghs.fuse = 10
					ghs.blendmode = AST_ADD
					ghs.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
					ghs.color = SKINCOLOR_SUPERSKY5
					ghs.momz = s.momz/5
					if s.target and s.target.valid
					local t = s.target
						ghs.momx = $+t.momx/2
						ghs.momy = $+t.momy/2
						ghs.momz = $+(t.momz/2)
						P_Thrust(ghs, s.angle, 8*s.scale)
						if s.twoshot
							P_TryMove(ghs, 
							t.x+FixedMul(24*t.scale, cos(t.angle+(ANGLE_90*i))),
							t.y+FixedMul(24*t.scale, sin(t.angle+(ANGLE_90*i))),
							true)
						end	
						for i = 1,3
							P_TryMove(ghs, ghs.x+FixedMul(14*t.scale, cos(t.angle)), ghs.y+FixedMul(14*t.scale, sin(t.angle)), true)
						end					
						if s.target.player and displayplayer and displayplayer == s.target.player
						and not CV_FindVar("chasecam").value 
							ghs.flags2 = $|MF2_DONTDRAW
						end
					end
				end
			end
			
		s.startsmoke = true
	end
	
	--Cross Flame, fired by tapping fire.
	if s.twoshot
		if s.twotimer == nil
			s.twotimer = 10
			s.flags2 = $|MF2_EXPLOSION
			if (gametype == GT_COOP) and s.target --Initial point-blank hitbox.
				P_RadiusAttack(s, s.target, 153*s.target.scale, DMG_FIRE, false)
			end
		end	
		if s.turhitsfx
			if s.twotimer
				s.twotimer = 0
				s.rollangle = 0
				s.spritexoffset = 0
				s.flags2 = $ & ~MF2_EXPLOSION
			end
			s.turhitsfx = $-1
			s.spritexscale = max($-5000, FRACUNIT)
			s.spriteyscale = min($+3850, FRACUNIT)
			if s.turhitsfx == 7
				SMS_DustLayers(s, true)
			end	
		elseif s.twotimer
			s.twotimer = $-1			
			if s.twoshot2
				s.spritexoffset = (s.twotimer*5<<16)
			else
				s.spritexoffset = (s.twotimer*5*-FRACUNIT)
			end
			if s.twotimer == 7
				s.flags2 = $ & ~MF2_EXPLOSION
			end
			s.spritexscale = FRACUNIT+(s.twotimer*9500)
			s.spriteyscale = FRACUNIT-(s.twotimer*3100)
			s.rollangle = s.twotimer*(ANG1*36)
			if not s.twotimer
				s.spritexscale = FRACUNIT
				s.sprityxscale = FRACUNIT
				s.spritexoffset =0
				s.rollangle = 0
			end
		end
		if s.health --Double twin flames?
			if s.smfltimer == nil
				s.smfltimer = 10
				s.fuse = 500
			end
			if s.smfltimer or (s.smfltimer == 0)
				if not s.twoshot2 //Modify the WaveBeam script to get an X-blast
					if (s.smfltimer > 8)
						P_Thrust(s, s.angle+ANGLE_90, 25355)
					else
						P_Thrust(s, s.angle-ANGLE_90, 25355)
					end
				else
					if (s.smfltimer > 8)
						P_Thrust(s, s.angle-ANGLE_90, 25355)
					else
						P_Thrust(s, s.angle+ANGLE_90, 25355)
					end
				end 
				if not s.smfltimer
					s.smfltimer = 16
					if not s.hsz
						s.hsz = true
						s.destscale = s.scale*3/2
					end
				else
					s.smfltimer = $-1
				end
			end
			if (s.z+s.momz-s.scale <= s.floorz) 
			or (s.z+s.height+s.momz+s.scale >= s.ceilingz)
				if abs(s.momx)+abs(s.momy) > 16<<16 --Only slide across the floor if you have SOME speed.
					s.momz = 0			
					if s.turhitsfx == nil
						local ghs = P_SpawnGhostMobj(s)
						ghs.destscale = $*99
						ghs.fuse = 13 ghs.tics = 99
						ghs.blendmode = AST_ADD
						ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60
						ghs.color = SKINCOLOR_FLAME
					
						S_StartSound(s, sfx_turhit)
						s.turhitsfx = 11
						s.spritexscale = FRACUNIT+43500
						s.spriteyscale = FRACUNIT-21500
					
						if SMS_Graphix(s) != 2
							local ghs = SMS_SmartSplat(s, 16, s, s.radius*4)
							if ghs and ghs.valid
								ghs.sprite = SPR_SMX2
								ghs.frame = (Z+3)
								ghs.colorized = true
								ghs.color = SKINCOLOR_CRIMSON
								ghs.blendmode = AST_ADD
								if SMS_Graphix(s) == 0
									SMS_DustLayers(s)
								end
							end
						end
						s.momx = $*2
						s.momy = $*2
					end
				end
			end
		end
	end
	
	--When exploding, do this!
	if ((s.flags & MF_NOCLIPTHING) or (s.flags2 & MF2_DEBRIS))
	and not s.wheretheressmoke
		local ghs = P_SpawnMobjFromMobj(s, 0,0, s.height, MT_TNTDUST)
		if ghs and ghs.valid
			ghs.destscale = $*99
			
			if s.mysticflame --Diffusion Flame smoke!
			
				ghs.fuse = 83
				ghs.scalespeed = $*2
				P_SetObjectMomZ(ghs, FRACUNIT/3)
				
				--Now you get all shiny too.
				s.scalespeed = $*2
				s.destscale = $*99
				
			else --Regular explosion.
			
				P_SetObjectMomZ(ghs, FRACUNIT*2)
				ghs.fuse = 53
			end
			
			ghs.tics = 1
		end
		if not (s.target and s.target.player and s.target.player.smsblastboost)
		and not s.mysticflame
			if FixedHypot(FixedHypot(s.target.x-s.x, s.target.y-s.y), s.target.z-s.z) < 450*s.target.scale
				A_SMSquake(s.target, 28<<16, 2, 400)
			end	
			if not s.twoshot2
				if not SMS_NerfedMode(p)
					P_RadiusAttack(s, s.target, 108*s.target.scale, DMG_FIRE, false)
				end			
				local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_SMS_THOK)
				ghs.tics = 177
				S_StartSoundAtVolume(ghs, sfx_cdfm16, 144)
				S_StartSoundAtVolume(ghs, sfx_bgxpld, 73)	
			end
			s.flags2 = $|MF2_DONTDRAW
			ghs = P_SpawnMobjFromMobj(s,0,0,-s.height, MT_THOK)
			ghs.sprite = SPR_SMX5
			ghs.frame = (Z+9)|FF_TRANS10
			ghs.blendmode = AST_ADD
			ghs.scale = s.scale/2
			ghs.spritexscale = FRACUNIT+20000
			ghs.scalespeed = $*3
			ghs.fuse = 14
			ghs.tics = 99
			ghs.destscale = $*99
			ghs.momz = 7*s.scale*P_MobjFlip(ghs)		
		end
		s.wheretheressmoke = true
	end	
	
	--Fully charged superflame!
	if s.mysticflame
		if s.sparkletimer
			s.sparkletimer = $-1
		else
			s.sparkletimer = 20
		end

		if not s.alreadygrown
			A_SetScale(s, s.scale+150001, 1)
			s.fuse = 300
			s.alreadygrown = true
			s.scalespeed = $/(5/4)
		end
		
		--We just died, do a Diffusion Flame explosion! (Second part!)
		if (s.flags & MF_NOCLIPTHING) or (s.flags2 & MF2_DEBRIS)
			if not SMS_NerfedMode(p)
			and (s.state == s.info.deathstate)
				if s.target
					P_RadiusAttack(s, s.target, 180<<16)
				else
					P_RadiusAttack(s, s, 180<<16)
				end
			end
			if not s.explodegfx	--Do explosion.	
				s.explodegfx = true
				local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK)
				ghs.flags2 = MF2_DONTDRAW ghs.tics = 199
				S_StartSoundAtVolume(ghs, sfx_brakrx, 14)
				S_StartSoundAtVolume(ghs, sfx_cdfm16, 233)
				S_StartSoundAtVolume(ghs, sfx_bgxpld, 113)
				
				for z in players.iterate
					if z.mo
						local dist = FixedHypot(z.mo.x-s.x, z.mo.y-s.y)
						if dist < 700<<16
							P_FlashPal(z, PAL_NUKE, 3)
							A_SMSquake(z.mo, 60<<16, 12, 500)
						end
					end
				end
				if SMS_Graphix(s) != 2
					if s.target and s.target.valid
						local oldscale = s.scale
						s.scale = s.target.scale
						SMS_Embers(s, s, 0)	
						SMS_DustLayers(s)
						s.scale = oldscale
					end				
					for i = -3,3,6
						local ghs = SMS_GroundWindBlast(s)
						if ghs and ghs.valid
							ghs.blendmode = AST_ADD
							ghs.color = SKINCOLOR_SUPERORANGE2+i
							ghs.colorized = true
							ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
							ghs.spritexscale = FRACUNIT-9000+(i*4500)
							ghs.spriteyscale = FRACUNIT-25000+(i*2500)
							P_SetObjectMomZ(ghs, i*(FRACUNIT/2), true)
						end
					end
					--Blue flames!
					local SCALE = (s.target) and s.target.scale or FRACUNIT
					for i = 1,8
						ghs = P_SpawnMobjFromMobj(s,
						FixedMul(105*SCALE, cos(s.angle+(ANGLE_45*i))),
						FixedMul(105*SCALE, sin(s.angle+(ANGLE_45*i))),
						-s.height, MT_THOK)
						ghs.sprite = SPR_SMX5
						ghs.frame = (Z+9)|FF_TRANS10
						ghs.spritexscale = FRACUNIT+20000
						ghs.blendmode = AST_ADD
						ghs.scale = SCALE/2
						ghs.scalespeed = $*3
						ghs.fuse = 19
						ghs.tics = 99
						ghs.destscale = $*99
						P_InstaThrust(ghs, R_PointToAngle2(ghs.x, ghs.y, s.x, s.y), -6*SCALE)
						ghs.momz = 10*SCALE*P_MobjFlip(ghs)
					end
					A_SMSSTOMPSMOKE(s, 60, MT_SPINDUST, nil, nil, 2<<16)
					if SMS_Graphix(s) == 0
						local sp
						for i = 1,8
							sp = P_SpawnMobjFromMobj(s,
							FixedMul(52*s.scale, cos(s.angle+(ANGLE_45*i))),
							FixedMul(52*s.scale, sin(s.angle+(ANGLE_45*i))),
							s.height/2, MT_THOK)
							sp.renderflags = RF_PAPERSPRITE|RF_SEMIBRIGHT
							sp.angle = R_PointToAngle2(sp.x, sp.y, s.x, s.y)+ANGLE_90
							sp.sprite = s.sprite
							sp.frame = s.frame
							sp.fuse = 7
							sp.blendmode = AST_ADD
							sp.flags2 = $|MF2_SHADOW	
						end	
					end					
				end
			end
		else --We're alive!
		
			if (s.sparkletimer >= 10)
			local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_IVSP)
				if ghs and ghs.valid
					if leveltime % 3 == 0
						ghs.color = SKINCOLOR_CRIMSON
						ghs.flags2 = MF2_SHADOW
					elseif leveltime % 2 == 0
						ghs.color = SKINCOLOR_SUPERGOLD5
						ghs.blendmode = AST_ADD
					else
						ghs.color = SKINCOLOR_EMERALD
						ghs.flags2 = MF2_SHADOW
					end
				end
			end	
			if (s.scale > 94000)
				if not s.alreadyspedup1
					if s.smspointer and s.smspointer.valid and s.smspointer.health
						s.angle = R_PointToAngle2(s.x, s.y, s.smspointer.x, s.smspointer.y)
						P_InstaThrust(s, s.angle, 70<<16)
						S_StartSound(s, sfx_s3k4b)
						S_StartSound(s, sfx_s3k82)
						S_StartSound(s, sfx_fire)
					end
					s.momx = $*5/4
					s.momy = $*5/4 //gradually speed up as you go!
					s.momz = $*5/4
					s.alreadyspedup1 = true
				end
				if s.target and (leveltime % 2 == 0) and (s.scale > 95999)
					A_SMSSpinFire(s, MT_SMSSPINFIRE, 65357, 0, s.target)
				end				
			end		
		end
		
		--Commencing expansion...
		if (s.scale > 150000)
		
			if ((s.flags & MF_NOCLIPTHING) or (s.flags2 & MF2_DEBRIS))
			and not s.alreadyblown
				if (gametype == GT_COOP)
					P_NukeEnemies(s, s.target, 500<<16)
				else
					P_NukeEnemies(s, s.target, 340<<16)
				end
				s.alreadyblown = true
				s.state = S_RINGEXPLODE
				A_SMSSTOMPSMOKE(s, 5, MT_SMOKE)
				
			else
			
				local ghs = P_SpawnGhostMobj(s)
				ghs.blendmode = AST_ADD
				ghs.destscale = ghs.scalespeed+1
				
				--Speed up and add knockback-increasing flags!
				if not s.alreadyspedup3
					s.flags2 = $|MF2_EXPLOSION
					s.momx = s.momx*4/3
					s.momy = s.momy*4/3
					s.momz = s.momz*5/3
					s.alreadyspedup3 = true
				end	
				
				if (gametype != GT_COOP)
					--Big ol' AoE. P_DamageMobj usually obeys in-game rules, so no team stuff necessary.
					for z in players.iterate
						if z.mo and (FixedHypot(FixedHypot(s.x - z.mo.x, s.y - z.mo.y), s.z - z.mo.z) < 106<<16)
						and z.mo.health
							P_DamageMobj(z.mo, s, s.target)
						end
					end
				end
			end
			
		--Not QUITE there yet!
		
		elseif (s.scale > 120000)
		
			if ((s.flags & MF_NOCLIPTHING) or (s.flags2 & MF2_DEBRIS))
			and not s.alreadyblown
				P_NukeEnemies(s, s.target, 120<<16)
				s.alreadyblown = true
			end
		end
	end
	if s.valid and s.readyforonemoreplant
		s.readyforonemoreplant = $-1
	end
end, MT_SMSFIREBLAST)


--Special flame-spreading explosion for HMS flames!
local function HMS_FireWaves(s, t)
		if s.outterflame or s.mainflame --Sound effects!
			local SfX = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
			SfX.flags2 = MF2_DONTDRAW SfX.tics = 99 SfX.fuse = 99
			S_StartSoundAtVolume(SfX, sfx_bgxpld,70) 
		end
		if not s.HMS_7flame --Only activate these effects every 2nd wave!
			s.state = S_NULL return --
		elseif s.monoroll
			if not (s.outterflame or s.mainflame)
			and not (s.midflame and (s.monoroll < 6))
				s.state = S_NULL return --
			end
		end
		--Put everything upside down!
		if (s.z > s.ceilingz-312<<16) and not (s.z <= s.floorz+312<<16)
		or (s.z > s.ceilingz-126<<16) and not (s.z <= s.floorz+126<<16)
			s.flags2 = $|MF2_OBJECTFLIP
			s.eflags = $|MFE_VERTICALFLIP
		else
			s.flags2 = $ & ~MF2_OBJECTFLIP
			s.eflags = $ & ~MFE_VERTICALFLIP		
		end	
		
		--Realistic flamewaves explosions.
		local ghs = P_SpawnMobjFromMobj(s,0,0,-s.height, MT_THOK)
		ghs.sprite = SPR_SMX5
		ghs.frame = (SMSLocalRNG(10,15,ghs) == 11) and Z+8 or Z+9
		ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
		ghs.scale = s.scale/2
		ghs.spriteyscale = FRACUNIT+3000+SMSLocalRNG(1, 21000,ghs)
		ghs.spritexscale = FRACUNIT+60000+SMSLocalRNG(0, 140000,ghs)
		ghs.scalespeed = $*3
		ghs.fuse = 14
		ghs.tics = 99
		ghs.destscale = $*99
		ghs.momz = 7*s.scale*P_MobjFlip(ghs)
		
		--Now spawn a circle of burning fire that sticks around for quite a while! 
		--If you're at max graphics in OpenGL and in Single Player, feel free to go ham, but otherwise it lasts 2 secs.
		local TIMER = 80
		if not netgame
		and not s.GRAPHIX
		and CV_FindVar("renderer").value == 2
			TIMER = 700
		end
		local SCALE = (FRACUNIT*2)+SMSLocalRNG(-25000,55000,ghs)
		local DIST = FixedDiv(FixedMul(SMSLocalRNG(184,200,s)*t.scale, SCALE), FRACUNIT*2) --Ideally it should be 192.
		
		--The main flame inflicts the fire HUD effect, deals AoE damage, and spawns flames depending on how far it flew.
		if s.mainflame
			SMS_InflictFlameHUD(s, 999*t.scale, 49, FF_TRANS30, SKINCOLOR_ORANGE)
			P_RadiusAttack(s, (s.target) or s, 580*t.scale)
			SMS_SmokeCircle(s, (SMSLocalRNG(0,9,s) == 1) and 4 or 3, SMSLocalRNG(351, 622,s)*t.scale, nil, (FRACUNIT*4)-SMSLocalRNG(0,29541,s))
			if not s.monoroll and not s.GRAPHIX --Only spawn flames if you're flown for 12+ tics, AKA when monoroll runs out.
				if not t.HMS_FireCoords3
				or (FixedHypot(s.x-t.HMS_FireCoords3.hx, s.y-t.HMS_FireCoords3.hy) > 65*t.scale)
					SMS_FireCircle(s, 4, DIST, nil, SCALE, TIMER)
					t.HMS_FireCoords3 = {hx = s.x, hy = s.y}
				elseif (leveltime % 16 == 0)
					t.HMS_FireCoords3 = nil 
				end
			end
			
		elseif s.outterflame --The outter flames always spawn fire circles and smoke! 
		--If outterflame is 1, it's the the flame that flew right, while -1 flew left. 
		
			if not s.GRAPHIX --Only spawn smoke if we've the resources for it...
				ghs = SMS_SmokeCircle(s, 0, nil, nil, FRACUNIT*3) 
				if ghs and ghs.valid
					ghs.fuse = 60
					ghs.sttspeed = 5
					ghs.spritexscale = $+38000
					P_SetObjectMomZ(ghs, 6<<16, true)
					ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
				end
			end
			if s.GRAPHIX != 2
				--Don't keep spawning it in the exact same spot!
				if s.outterflame == 1
					if not t.HMS_FireCoords
					or (FixedHypot(s.x-t.HMS_FireCoords.hx, s.y-t.HMS_FireCoords.hy) > 80*t.scale)
						SMS_FireCircle(s, 3, DIST, nil, SCALE, TIMER)
						t.HMS_FireCoords = {hx = s.x, hy = s.y}
					elseif (leveltime % 28 == 0)
						t.HMS_FireCoords = nil 
					end
				elseif not t.HMS_FireCoords2
				or (FixedHypot(s.x-t.HMS_FireCoords2.hx, s.y-t.HMS_FireCoords2.hy) > 80*t.scale)
					SMS_FireCircle(s, 3, DIST, nil, SCALE, TIMER)
					t.HMS_FireCoords2 = {hx = s.x, hy = s.y}
				elseif (leveltime % 28 == 0)
					t.HMS_FireCoords2 = nil
				end
			else --Just one flame for the potato gamers?
				SMS_FireCircle(s, 0, 1, nil, FRACUNIT*3, TIMER+6) 
				SMS_SmokeCircle(s, 0, 1, nil, FRACUNIT*4, 46)
			end
		end
		return true --
end

addHook("MobjSpawn", function(s)
	s.monoroll = 12
	s.renderflags = RF_NOCOLORMAPS|RF_FULLBRIGHT
	s.spriteyscale = FRACUNIT*2 --Simple setup.
	s.spritexscale = FRACUNIT*2
	if not s.color
		s.color = SKINCOLOR_SUPERSKY5
	end
end, MT_HMSFIREBLAST)
addHook("MobjThinker", function(s)
	if (s.flags & MF_NOCLIPTHING)  --Exploding phase.
		if s.napalm == nil 
		and (s.target and s.target.valid)
			local t = s.target	
			local SMSblast = (t.player and t.player.smsblastboost) and 1 or 2
			if s.HMS_7flame 
				if (s.mainflame or s.outterflame) 
				and (s.monoroll == 12 or FixedHypot(FixedHypot(t.x-s.x, t.y-s.y), t.z-s.z) < 900*t.scale)
					A_SMSquake(t, 22<<16, 4, 200, true)
				end			
				--New explosion?
				if SMSblast == 2 --Do the new explosion?
					if s.GRAPHIX != 2
						HMS_FireWaves(s, t)
					end
					if s and s.valid
						s.state = S_NULL
					end
					return --
				end
			elseif SMSblast == 2
				s.state = S_NULL return --
			end
			s.napalm = true
			s.rollangle = 0
			s.spriteyscale = FRACUNIT
			s.spritexscale = FRACUNIT
			s.frame = ($ & ~FF_TRANSMASK)
		end
		
	else --We're not dead?
	
	  	if s.readyforonemoreplant --Cooldown before you can burn more cacti.
		 	s.readyforonemoreplant = $-1
		end
		if s.monoroll			
			s.monoroll = $-1					
			if not s.monoroll --End of roll!
				s.rollangle = 0
				s.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
				s.color = SKINCOLOR_SUPERSKY5
				s.spriteyscale = FRACUNIT
				s.spritexscale = FRACUNIT
			else
				if SMSLocalRNG(0,1,s) == 1
					if (s.color > SKINCOLOR_SUPERSKY1)
						s.color = $-1
					else
						s.color = SKINCOLOR_SUPERSKY5
					end
				end				
				s.rollangle = $+FixedAngle(36<<16)						
				s.rollangle = $+FixedAngle(18<<16)
				s.spriteyscale = max(FRACUNIT, $-8000)
				s.spritexscale = max(FRACUNIT, $-8000)				
				s.frame = ($ & ~FF_TRANSMASK)|(FF_TRANS10+(FF_TRANS10*(s.monoroll/2))) --Fade?
			end	
			--Find original target...
			if (s.target and s.target.valid)
			local t = s.target				
  				if not s.ogtarget
					if (t.skin == "sms")
						s.ogtarget = t
						return --
					end
				elseif (t.skin != "sms")
					s.foundyoufaker = t
					t = s.ogtarget
					if s.foundyoufaker and t.valid
						P_DamageMobj(s.foundyoufaker, s, t)
					end
					if s.valid 
						P_KillMobj(s) return --
					end
				end
			end
		end
	end
end, MT_HMSFIREBLAST)

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
	s.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
	s.rollangle = FixedAngle(SMSLocalRNG(0, 170,s)<<16)
	s.gottoexist = 1
	s.ROLLSPEEDLIVE = FixedAngle(SMSLocalRNG(8,12,s)<<16)
	s.ROLLSPEED = FixedAngle(SMSLocalRNG(3,5,s)<<16)
	s.Graphix = SMS_Graphix(s)
	s.NovaColors = {SKINCOLOR_PURPLE, SKINCOLOR_SUPERSKY2, SKINCOLOR_SUPERSKY4, SKINCOLOR_MYSTICAL2, SKINCOLOR_SUPERSKY5, SKINCOLOR_SKY, SKINCOLOR_SMS2R, SKINCOLOR_MYSTICAL4}
	s.color = s.NovaColors[SMSLocalRNG(1,7,s)]
end, MT_HMSNOVABLAST)

//Let's give HMS' Novablast a few more destructive side effects
addHook("MobjThinker", function(s)
	if not (s.target and s.target.valid and s.target.player) s.state = S_NULL return end --Have a target or don't speak to me.
	local t = s.target
	
	--Nova Blast hitbox is a seperate object.
	if not s.NOhitbox
		if not s.novahitbox
			s.novahitbox = P_SpawnMobj(s.x, s.y, s.z-80<<16, MT_HMSNOVABLASTHB)
			s.novahitbox.radius = 340<<16
			s.novahitbox.height = 340<<16
			s.novahitbox.novatarget = s
			s.novahitbox.blendmode = AST_ADD
			s.novahitbox.hmstarget = t
			s.novahitbox.eflags = s.eflags
		elseif s.novahitbox and s.novahitbox.valid
			P_MoveOrigin(s.novahitbox, s.x, s.y, s.z-80<<16)
			s.novahitbox.tics = 2
		end
		--Firing pointblank at walls lags the crap out of SRB2, exist for a few tics before doing heavy stuff.
		if (s.gottoexist > 7) and (s.gottoexist % 4 == 0)
			HMS_NovaBlastShine(s, 20)	
		end
 		if s.readyforonemoreplant
			s.readyforonemoreplant = $-1
		end	
	else --You're mostly for show, it seems!
		if (s.frame & FF_FRAMEMASK) == A
			s.frame = ($ & ~FF_FRAMEMASK)|D --Use frame D!
		end
		s.blendmode = AST_TRANSLUCENT
		if (s.state == S_NOVABLAST) --Make afterimages of the non-blendmode to make the Novablast streams feel more "full"
			s.rollangle = $-s.ROLLSPEED
		 	if not s.Graphix
	  			if (s.gottoexist % 4 == 0) and (s.gottoexist > 7) and (s.gottoexist < 21)
		 		or (s.gottoexist == 2)
						
					local ghs = P_SpawnGhostMobj(s)
					ghs.renderflags = RF_NOCOLORMAPS|RF_SEMIBRIGHT
					if (s.gottoexist % 2 == 0)
						ghs.momx = s.momx/3
						ghs.momy = s.momy/3
						ghs.momz = s.momz/3
						ghs.frame = D|FF_TRANS40|FF_HORIZONTALFLIP
						local newRNG = SMSLocalRNG(23000,43000,s)
						ghs.spriteyscale = FRACUNIT+newRNG
						ghs.spritexscale = FRACUNIT+newRNG
						ghs.fuse = 6
					else
						ghs.frame = D|FF_TRANS60|FF_HORIZONTALFLIP
						ghs.colorized = true
						ghs.dispoffset = 2
						ghs.color = max(1,s.color-SMSLocalRNG(1,12,s))						
						local newRNG = SMSLocalRNG(13000,23000,s)
						ghs.spriteyscale = FRACUNIT+newRNG
						ghs.spritexscale = FRACUNIT+newRNG
						ghs.fuse = 11
					end
					ghs.tics = 99
					if (t.tics == 45) and (s.gottoexist != 2)
						local ghs = HMS_BigBangVortex(s, 20, FRACUNIT, s.ROLLSPEED*3, nil, SKINCOLOR_BLACK)
						ghs.blendmode = AST_TRANSLUCENT
						ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS70|FF_PAPERSPRITE 
						ghs.spritexscale = $+(FRACUNIT*3)+35000
						ghs.spriteyscale = $+FRACUNIT+35000
						ghs.tracer = s
						ghs.dispoffset = -9
						ghs.GSoffsetX = 9*t.scale
						ghs.GSoffsetY = 9*t.scale
						ghs.GSoffsetZ = 0						
						ghs.angle = t.angle
					end					
				end
			end
		end
	end
	--Setup frame.
	if s.gottoexist == 1
		local RNG = SMSLocalRNG(-31000,31000,s)
		s.spriteyscale = $+((RNG*5)/4)
		s.spritexscale = $+(RNG*-1)
		if not s.ogtarget and (t.skin == "sms")
			s.ogtarget = t
		end		
	else
		if not s.ScalerDone
			if s.spritexscale != FRACUNIT
				s.spritexscale = (s.spritexscale > FRACUNIT) and max($-7700, FRACUNIT) or min($+7700, FRACUNIT)
			end
			if s.spriteyscale != FRACUNIT
				s.spriteyscale = (s.spriteyscale > FRACUNIT) and max($-7700, FRACUNIT) or min($+7700, FRACUNIT)
				if s.spriteyscale == FRACUNIT and s.spriteyscale == FRACUNIT --Due to the way this works, yscale finishes last!
					s.ScalerDone = true
				end				
			end
		else
			s.spritexscale = min(FRACUNIT*3, $+1500)
			s.spriteyscale = min(FRACUNIT*3, $+1500)
		end
	end
	s.gottoexist = $+1
	local COLORPICK = (s.NovaColors and s.NovaColors[SMSLocalRNG(1,7,s)]) or SKINCOLOR_SUPERSKY5
	--Main fired state!
	if (s.state == S_NOVABLAST)

		s.rollangle = $-s.ROLLSPEEDLIVE
		s.color = COLORPICK
	
		--Someone else tried to claim the Novablast for themselves! Destroy them!
		if (t.skin != "sms") and s.ogtarget
			local FAKETARGET = t
			s.target = s.ogtarget
			P_DamageMobj(FAKETARGET, s, s.target)
			if s.valid and s.health P_KillMobj(s) return end --
		end
		
	else --Exploding!
		if s.explodephase == nil
			local SfX = P_SpawnMobj(s.x, s.y, s.z, MT_THOK)
			SfX.flags2 = $|MF2_DONTDRAW
			SfX.tics = 144
			S_StartSound(SfX, sfx_hmsnob)
			s.explodephase = 0
			if not s.Graphix			
				if not s.NOhitbox
				/*	local ghs = SMS_LightningStrike(s, s.color, R|FF_TRANS20)
					ghs.tracer = s
					ghs.sttfade = 22
					ghs.fuse = 26
					ghs.spriteyoffset = 33<<16
					local RNG = SMSLocalRNG(3,6,s)
					if (RNG < 5)
						ghs.mirrored = true
						ghs.color = (SKINCOLOR_RED+4)-RNG
					end
					ghs.spriteyscale = $*RNG
					ghs.spritexscale = $*RNG
					ghs.destscale = s.scale
					ghs.dispoffset = -30 */
					if (leveltime % 11 == 0)
						SMS_FlamingImpact(s, "prism", 2, FRACUNIT+SMSLocalRNG(23456,98765,s), nil, 44)
					else
						SMS_InflictFlameHUD(s, 2000*t.scale, 49, FF_TRANS60, SKINCOLOR_PURPLE)
						SMS_FlamingImpact(s, "prism", 3, FRACUNIT+SMSLocalRNG(1,FRACUNIT,s), nil, 44)
					end	
				end
				SMS_FlameWalls(s, 2<<16, 53<<16, 40<<16, A|FF_TRANS40, "prism", 23)
			end			
		end	
			
		s.rollangle = $-s.ROLLSPEED
		s.color = COLORPICK
	
		if (s.state == S_NOVAEXPLODE1) --First explosion frame!
			if not s.explodephase --Only once.
				if not s.NOhitbox 
					if s.novahitbox and s.novahitbox.valid
						s.novahitbox.radius = 1201<<16
						s.novahitbox.height = 1100<<16		
					end
					P_NukeEnemies(s, t, 1100*t.scale)
					if (s.gottoexist < 13) and not s.NOhitbox
						if displayplayer and t.player == displayplayer
							P_StartQuake(450<<16, 6)
							--If it's 2, then we're in OpenGL. 1 is software. 0 is dedicated server/error.
							if CV_FindVar("renderer").value == 2 --WAAAAYYY too harsh on software...
								P_FlashPal(displayplayer, PAL_NUKE, 8)
							end
						end
						if (t.player.speed < 220*t.scale)
							P_Thrust(t, R_PointToAngle2(s.x, s.y, t.x, t.y), 14<<16)
						end
					else
						for z in players.iterate --Variable quaker power!
							if z.mo and z.mo.valid
								local DIST = FixedInt(FixedHypot(FixedHypot(s.x-z.mo.x, s.y-z.mo.y), s.z-z.mo.z))
								local QUAKEPOWER = min(11<<16, 3500*(FRACUNIT/DIST))
								A_SMSquake(z.mo, QUAKEPOWER, 7, 1, true)
							end
						end	
					end
				end		
				s.explodephase = s.NOhitbox and 2 or 1
			end		
		elseif (s.state == S_NOVAEXPLODE3)
			if (s.explodephase != 2)
			/*	if t.napalmswitch
					if s.Graphix != 2
						A_NapalmScatter(s, MT_HMS_NAPALM_FLAMES, 256)
					end
					t.napalmswitch = false
				else
					t.napalmswitch = true 
				//	A_SMSSTOMPSMOKE(s, 18, MT_NIGHTSPARKLE, 0, 50<<16, 4<<16, P_RandomRange(7, 103))
				//	A_SMSSTOMPSMOKE(s, 28, MT_NIGHTSPARKLE, 0, 20<<16, 4<<16, P_RandomRange(7, 103))
				end */
			
				if not s.NOhitbox and not s.Graphix == 2
					s.novawaveex = true 
					local ghs = P_SpawnMobjFromMobj(s, 0, 0, s.height/2, MT_HMSNOVAWAVE)
					ghs.scale = $*SMSLocalRNG(2,4,s)
					if ghs and ghs.valid
						ghs.novaog = true				
						if (leveltime % 3 == 0)
							s.rollangle = ANGLE_90
							ghs.color = SMSLocalRNG(SKINCOLOR_SUPERSILVER1, SKINCOLOR_SUPERGOLD5,s)
						elseif (leveltime % 2 == 0)
							ghs.rollerwave = SMSLocalRNG(-14, 14,s)<<16
							ghs.color = SMSLocalRNG(SKINCOLOR_SUPERSILVER1, SKINCOLOR_SUPERGOLD5,s)
						end
					end
				end
				s.explodephase = 2
			end
		elseif (s.state == S_NOVAEXPLODE4)
			if s.explodephase != 3
		--		A_NapalmScatter(s, MT_HMSFLAMEREST, 512)
				s.explodephase = 3
				s.scalespeed = $+(FRACUNIT/9)
			end
		end
	end
end, MT_HMSNOVABLAST)

//The wave that comes after a hyper novablast-explosion.
addHook("MobjThinker", function(s)	
	if not s.momgio
		if s.rollerwave
			s.rollangle = $+SMSLocalRNG(0, 359, s)
		end
		if not s.color
			s.color = SKINCOLOR_FLAME
		end
		if s.novaog
			s.scalespeed = $*20/SMSLocalRNG(2,10,s)
		else
			s.scalespeed = $*2		
		end
		s.destscale = $*25
		s.momgio = true
		s.blendmode = AST_ADD
	end	
	if s.followme and s.followme.valid
		P_MoveOrigin(s, s.followme.x, s.followme.y, s.followme.z)
	elseif s.followmenoz and s.followmenoz.valid
		P_MoveOrigin(s, s.followmenoz.x, s.followmenoz.y, s.z)
	end	
	if s.rollerwave != nil
		s.rollangle = $+FixedAngle(s.rollerwave)
	end
end, MT_HMSNOVAWAVE)

//Make it look like the black hole's stationary and growing.
addHook("MobjSpawn", function(s)
	s.hmsblackholesuction = true
	s.hmsblackholesuction2 = true
	s.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
end, MT_HMSBLACKHOLE)
addHook("MobjThinker", function(bkhl)
	local s = bkhl
	for i = 1,2
		if not (bkhl.target and bkhl.target.valid)
			if i == 2 bkhl.state = S_NULL return end
			A_FindTarget (bkhl, MT_PLAYER, 0)
		end
	end	
	local t = bkhl.target
	if not t.player 
	or not (t.player.hypermysticsonic or t.player.hmsenoughdeath)
		bkhl.state = S_NULL
		return --
	end
	A_CapeChase(bkhl, 0, 0) //Stay on HMS' heels or this'll look awkward.
	
	if not bkhl.valid return end --Dangerous capechase is dangerous....
	
	if not (bkhl.background and bkhl.background.valid)
		bkhl.background = P_SpawnMobjFromMobj(bkhl,
		FixedMul(15*t.scale, cos(t.angle)),
		FixedMul(15*t.scale, sin(t.angle)),
		-1500*t.scale,
		MT_THOK)
	end
	
	HMS_NegativeZone(bkhl, t, true)
	--Setup.
	if not bkhl.startscale
		if (bkhl.scale <= FRACUNIT)
			bkhl.scale = FRACUNIT
			bkhl.destscale = 30<<16
		else
			bkhl.destscale = min($*5, 40<<16)
		end
		for i = 0,1,1
			local ghs = SMS_GoldWaves(t,t.player, true)
			if ghs and ghs.valid
				ghs.color = SKINCOLOR_CRIMSON-i
				ghs.colorized = true
				ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
				ghs.blendmode = AST_TRANSLUCENT
				ghs.spriteyscale = $*2/(i or 2)
				ghs.spritexscale = $*2/(i or 2)
				ghs.z = $-(32*ghs.spritexscale*i)
				ghs.fuse = $+(i*8)
				ghs.sttfade = true
				P_SetObjectMomZ(ghs, -10*ghs.spriteyscale*i, true)
			end
		end
		bkhl.startscale = true
	end
	
	bkhl.rollangle = $-FixedAngle(10<<16)
	
	if SMS_NerfedMode(p)
		for z in players.iterate
		
			if z.hmsblackhole == nil z.hmsblackhole = 0 end
			
			if not z.hypermysticsonic //HMS is immune to black holes, naturally.
			and (z.mo and z.mo.health) //If they're dead, you can stop killing them.
			and not (z.mo == bkhl.target)
			and not (z.ctfteam and z.ctfteam == bkhl.target.player.ctfteam) 
			and not z.hypermysticsonic //Let's not have it affect HMS.
				if (FixedHypot(FixedHypot(t.x - z.mo.x, t.y - z.mo.y), t.z - z.mo.z) < 1500<<16)

				local homiedist2 = FixedHypot(FixedHypot(t.x - z.mo.x, t.y - z.mo.y), (t.z+t.height) - z.mo.z)
				local speedmuller2 = FixedMul(18<<16, z.mo.scale)
				z.mo.momx = FixedMul(FixedDiv(t.x - z.mo.x, homiedist2), speedmuller2)
				z.mo.momy = FixedMul(FixedDiv(t.y - z.mo.y, homiedist2), speedmuller2)
				z.mo.momz = FixedMul(FixedDiv(t.z - z.mo.z, homiedist2), speedmuller2)
					if (FixedHypot(FixedHypot(t.x - z.mo.x, t.y - z.mo.y), (t.z+t.height) - z.mo.z) < 140<<16)
					and not (z.ctfteam and bkhl.target.player.ctfteam == z.ctfteam) 
						P_PlayerFlagBurst(z)
						P_KillMobj(z.mo, bkhl, bkhl)
						P_AddPlayerScore(bkhl.target.player, 100)
                    	print(bkhl.target.player.name+" sucked "+z.name+" into the black void! There were no survivors.")
					end
				end
			end
		end
	elseif (bkhl.state == S_BLACKHOLE5) 
		--Search HEAVY once, and light twice. (Blockmap doesn't detect some objects...)
		if s.tics == 16 //(bkhl.suck1 ==nil or ((bkhl.hmsblackholesuction2 == nil) and (bkhl.state == S_BLACKHOLE5)))
	    	for m in mobjs.iterate(mobjs) //Only check twice, let the tracker do the rest.
				if (m.flags & (MF_ENEMY|MF_BOSS))
				and not m.bkhltracked
					if (FixedHypot(FixedHypot(bkhl.x - m.x, bkhl.y - m.y), bkhl.z - m.z) < 4300<<16)
						bkhl.bkhltracker = P_SpawnMobjFromMobj(m, 0, 0, 0, MT_CMPHTRACKER)
						bkhl.bkhltracker.bkhl = true
						bkhl.bkhltracker.target = m
						bkhl.bkhltracker.tracer = bkhl.target
					end
				end
	    	end
		elseif s.tics == 9 or s.tics == 1
			searchBlockmap("objects",function(t,m)
				if m.valid and (m.flags & (MF_ENEMY|MF_BOSS))
				and not m.bkhltracked
					if (FixedHypot(FixedHypot(t.x - m.x, t.y - m.y), (t.z+t.height) - m.z) < 4300<<16)
						local ghs = P_SpawnMobjFromMobj(m, 0,0,0, MT_CMPHTRACKER) --Assign black hole tracker.
						ghs.bkhl = true
						ghs.target = m
						ghs.tracer = bkhl.target
					end
				end
			end,t, t.x-5000<<16,t.x+5000<<16, t.y-5000<<16,t.y+5000<<16)
		end
	end
end, MT_HMSBLACKHOLE)

addHook("MobjThinker", function(s)
	P_SetObjectMomZ(s, 95777)
	if (s.tics > 20)
		s.tics = 18
	elseif (s.tics < 6)
		s.flags2 = $|MF2_SHADOW
		s.scalespeed = FRACUNIT/200
		A_SetScale(s, 3<<16, 1)
	end
end, MT_SMSSTEAM)

//Minor effect for spindash smear.
/* addHook("MobjThinker", function(s)
	if s.valid and not s.monogr and s.sector and s.sector.subsector
		for fof in s.sector.subsector.ffloors() //Gotta do this because SRB2 is trash at rendering scaling objects over FOFs.
			if ((fof.flags & FF_EXISTS)
			and (s.z >= fof.topheight))
			s.nopefofhere = true
			end
		end
		if not s.nopefofhere
			P_SetObjectMomZ(s, 72246, true) //Exact number we need to negate the "sinking" effect of a half-speed destscale.
			s.scale = $*8/7
			s.scalespeed = $/2
			s.destscale = $/4
		end
		s.monogr = true
	end
end, MT_SMSBLINDDASH) */

//SMS' blinddash charge aura has some changes to the regular thok/spin auras. It grows over time.
//It also slowly creeps downwards to give the illusion it's truely stationary as it grows. Spooky.

addHook("MobjThinker", function(s)
	if s.target
		s.blendmode = AST_ADD
		if s.target.player.hypermysticsonic
			s.destscale = FixedMul(15<<16/3, s.target.scale) 
			s.scalespeed = FixedMul(7500, s.target.scale)
			P_SetObjectMomZ(s, FixedMul(-54036, s.target.scale))
		else
			s.destscale = FixedMul(5<<16/3, s.target.scale) 
			s.scalespeed = FixedMul(2500, s.target.scale)
			P_SetObjectMomZ(s, FixedMul(-18012, s.target.scale))
		end
	end
end, MT_SMSBLINDDASHCHARGE)

//Same with the air and groundboost shockwaves.

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
	if not s.color
		s.color = SKINCOLOR_BONE
	end
end, MT_SMSAIRBOOST)
addHook("MobjThinker", function(s)
	if s.tracer
		if s.SETUP == nil
			s.color = (s.tracer.color == 0) and SKINCOLOR_SUPERGOLD4 or s.tracer.color
			s.SETUP = true
		end
		if (s.state == S_SMSAIRBOOST1) and (s.scale != s.tracer.scale)
			s.scale = s.tracer.scale
		end
	end
end, MT_SMSAIRBOOST)

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
	if s.color == 0
		s.color = SKINCOLOR_SUPERSILVER5
	end
end, MT_HMSWAVEAURA)
addHook("MobjThinker", function(s)
		if s.tracer and s.tracer.valid
			if not s.prism
				if s.SETUP == nil
					if (s.tracer.color == 0)
						A_ChangeColorAbsolute(s, 0, SKINCOLOR_SUPERGOLD3)
					else
						A_ChangeColorAbsolute(s, 0, s.tracer.color)
					end
					s.SETUP = true
				end
			else
				local RNG = SMSLocalRNG(2, 9,s)
				if (RNG < 4)
					s.color = SKINCOLOR_BONE
				elseif (RNG == 4)
					s.color = SKINCOLOR_EMERALD
				elseif (RNG == 5)
					s.color = SKINCOLOR_BLUE
				elseif (RNG == 6)
					s.color = SKINCOLOR_CYAN
				elseif (RNG == 7)
					s.color = SKINCOLOR_PURPLE
				elseif (RNG == 8)
					s.color = SKINCOLOR_RED
				else
					s.color = SKINCOLOR_ORANGE
				end
			end
			if (s.state == S_SMSGROUNDBOOST) 
			and s.done == nil 
			and (s.smsgbs or not (s.scale == s.tracer.scale))
				if s.smsgbs
				 	if (s.smsgbs < 3)
						s.scale = s.tracer.scale*2/3
				 	elseif (s.smsgbs < 6)					
						s.scale = s.tracer.scale				
					elseif (s.smsgbs < 10)
						s.scale = s.tracer.scale*4/5
						s.done = true
					elseif (s.smsgbs < 12)
						s.scale = s.tracer.scale*2/3
						s.frame = A|FF_TRANS80
					elseif (s.smsgbs > 11)
						s.scale = s.tracer.scale/SMSLocalRNG(2,3,s)
						s.frame = A|FF_TRANS90
						s.done = true
					end
					s.destscale = 10
					s.momz = $+(32354)*(P_MobjFlip(s.tracer))
				else
					s.scale = s.tracer.scale
					s.done = true
				end
			end
			if not s.gogofade
				s.gogofade = SMSLocalRNG(1, 4,s)
				s.scale = ($/3)*s.gogofade
				if (s.gogofade <= 2)
					P_MoveOrigin(s, s.x, s.y, s.z+(40*s.tracer.scale*P_MobjFlip(s.tracer)))
				end
			end
		end
end, MT_SMSGROUNDBOOST)

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
end, MT_HMSWAVEAURA)

--Prevent the stupid frame overflow through sheer brute force.
local function SMSELAUCORRECT(s, NUM)
	NUM = (NUM) or H
	if ((s.frame & FF_FRAMEMASK) > NUM)
	and (s.sprite == SPR_ELAU)
		s.frame = ($ & ~FF_FRAMEMASK)|G
		if (s.tics < 3) s.tics = 3 end
		if (s.anim_duration < 3) s.anim_duration = 3 end
	end
end
//The electricity spawner is unique in the way it never spawns the same patterns.
//We'll also make it do some interesting things depending on what happens.
addHook("MobjThinker", function(s)
	for i = 1,2
		if not (s.target and s.target.player and s.target.skin == "sms")
			if i == 2 return end
			A_FindTarget(s, MT_PLAYER, 0)
		end
	end
	local t = s.target
	local tp = s.target.player
	local FRAME = (s.frame & FF_FRAMEMASK)
		SMSELAUCORRECT(s)
		
		if s.dustwave
		
			if s.dustwavet 
				if (s.dustwave % 7 == 0) and (FRAME < H)
					s.frame = $+1
				end
			elseif (s.dustwave % 3 == 0)
				s.frame = $+1
			end
			s.frame = ($ & ~FF_TRANSMASK)|FF_TRANS20
			s.tics = $+1
			s.dustwave = $+1
			return
			
		elseif s.dustwave2
		
			s.renderflags = $|RF_PAPERSPRITE|RF_SEMIBRIGHT
			if (s.dustwave2 % 2 == 0)
				s.frame = $+1
			end
			if s.fuse
				s.frame = ($ & ~FF_TRANSMASK)|min(FF_TRANS90, max(FF_TRANS10, (20/s.fuse)<<16))
			end
			s.tics = $+1
			s.dustwave2 = $+1
			SMSELAUCORRECT(s)
			return --
			
		elseif (s.state == S_HMSWAVEAURAG) //Ground sparkle. Is somewhat special compared to the rest.
			P_InstaThrust(s, R_PointToAngle2(t.x, t.y, s.x, s.y), 20*t.scale)
			if (s.frame & FF_FRAMEMASK) < H
				s.frame = $+1
			end
			SMSELAUCORRECT(s)
			return --

		end
		if tp.elauracolors != nil
			if (tp.elauracolors < 20)
				s.color = SKINCOLOR_SUPERSILVER3 //Pure white
			elseif (tp.elauracolors >= 20 and tp.elauracolors < 40)
				s.color = SKINCOLOR_BONE
			elseif (tp.elauracolors >= 40 and tp.elauracolors < 60)
				s.color = SKINCOLOR_WAVE 
			elseif (tp.elauracolors >= 60 and tp.elauracolors < 80)
				s.color = SKINCOLOR_TEAL
			elseif (tp.elauracolors >= 80 and tp.elauracolors < 100)
				s.color = SKINCOLOR_CYAN
			elseif (tp.elauracolors >= 100 and tp.elauracolors < 120)
				s.color = SKINCOLOR_GREEN //To green
			elseif (tp.elauracolors >= 120 and tp.elauracolors < 140)
				s.color = SKINCOLOR_PERIDOT
			elseif (tp.elauracolors >= 140 and tp.elauracolors < 160)
				s.color = SKINCOLOR_EMERALD
			elseif (tp.elauracolors >= 160 and tp.elauracolors < 180)
				s.color = SKINCOLOR_LIME 
			elseif (tp.elauracolors >= 180 and tp.elauracolors < 200)
				s.color = SKINCOLOR_GOLD
			elseif (tp.elauracolors >= 200 and tp.elauracolors < 220)
				s.color = SKINCOLOR_YELLOW
			elseif (tp.elauracolors >= 220 and tp.elauracolors < 240)
				s.color = SKINCOLOR_SANDY 
			elseif (tp.elauracolors >= 240 and tp.elauracolors < 260)
				s.color = SKINCOLOR_RED
			else //if (tp.elauracolors < 281)
				s.color = SKINCOLOR_PEACHY
			end
		end
		if (s.state == S_HMSELAURA1) //Tiny upwards spark
		or (s.state == S_HMSELAURA1A)
		
			s.destscale = FixedMul(2<<16, t.scale)
			s.scalespeed = FixedMul(1500, t.scale)
			if (tp.pflags & PF_STARTDASH)
				P_MoveOrigin(s, s.x, s.y, s.z-10)
			end
			
		elseif (s.state == S_HMSELAURA2) //Tiny middle spark
		or (s.state == S_HMSELAURA2A)
		
			s.destscale = FixedMul(2<<16, t.scale) 
			s.scalespeed = FixedMul(2300, t.scale)
			P_SetObjectMomZ(s, FixedMul(-9001, t.scale))
			if (tp.pflags & PF_STARTDASH)
				P_MoveOrigin(s, s.x, s.y, s.z-10)
			end
			
		elseif (s.state == S_HMSELAURA3) //Tiny westward spark
		or (s.state == S_HMSELAURA3A)
		
			s.destscale = FixedMul(2<<16, t.scale) 
			s.scalespeed = FixedMul(2100, t.scale)
			P_SetObjectMomZ(s, FixedMul(-13279, t.scale))
			s.momx = (s.x-t.x)/16
			s.momy = (s.y-t.y)/16
			if (tp.pflags & PF_STARTDASH)
				P_MoveOrigin(s, s.x, s.y, s.z-10)
			end
			
		elseif (s.state == S_HMSELAURA4) //Tiny eastward spark
		or (s.state == S_HMSELAURA4A)
			s.destscale = FixedMul(2<<16, t.scale) 
			s.scalespeed = FixedMul(1800, t.scale)
			P_SetObjectMomZ(s, FixedMul(-38551, t.scale))
			s.momx = (s.x-t.x)/16
			s.momy = (s.y-t.y)/16
			
		elseif (s.state == S_HMSELAURA5)
		or (s.state == S_HMSELAURA5A)
		or (s.state == S_HMSELAURA5B)
		
			P_SetObjectMomZ(s, FixedMul(45000, t.scale))
			if not s.state5
				A_OverlayThink (s, 0, FixedMul(-32<<16, t.scale))
				s.state5 = 1
			else
				P_InstaThrust(s, s.angle, 0)
			end
			if (tp.pflags & PF_STARTDASH)
				P_MoveOrigin(s, s.x, s.y, s.z-10)
			end
			if (s.state == S_HMSELAURA5)
				s.destscale = FixedMul(115535, t.scale) 
				s.scalespeed = FixedMul(2900, t.scale)
				if (tp.speed > 9*t.scale)
					s.state = S_HMSELAURA5B
				end
			end
			if (s.state == S_HMSELAURA5A)
				s.destscale = FixedMul(40000, t.scale) 
				s.scalespeed = FixedMul(10000, t.scale)
			elseif (s.state == S_HMSELAURA5B)
				s.destscale = FixedMul(FRACUNIT*2, t.scale) 
				s.scalespeed = FixedMul(4000, t.scale)			
			end
			
		elseif (s.state == S_HMSELAURA6)
		
			s.destscale = FixedMul(FRACUNIT*2, t.scale) 
			s.scalespeed = FixedMul(500, t.scale)
			if not s.state6
				A_OverlayThink (s, 0, 0)
				s.state6 = 1
			else
				P_InstaThrust(s, s.angle, 0)
				s.momz = 0
			end
			
		elseif (s.state == S_HMSELAURA6B)
		
			s.destscale = FixedMul(FRACUNIT*2, t.scale) 
			s.scalespeed = FixedMul(2600, t.scale)
		end
end, MT_HMSWAVEAURA)

//CEZ3 Eggman tends to get stuck in painframes forever if you look at him funny. Hotfix.
addHook("MobjThinker", function(s)
	if s.valid and s.health
		if (s.flags2 & MF2_FRET)
			if s.fretfailsafe == nil
				s.fretfailsafe = 1
			else
				s.fretfailsafe = $+1
				if (s.fretfailsafe > 180)
					s.fretfailsafe = 0
					s.flags2 = $ & ~MF2_FRET
				end
			end
		elseif s.fretfailsafe
			s.fretfailsafe = 0
		end
	end
end, MT_EGGMOBILE4)

//Fang Boss should always destroy train on death.
addHook("MobjDeath", function(s,i,so)
	if s.valid
		if (gamemap == 15)
			P_LinedefExecute(65531)
			P_LinedefExecute(65532)
		end
	end
end, MT_FANG)

//HMS' Hyper-Rising Aura
addHook("MobjThinker", function(s)			
		if not s.valid return end
		if s.fed
			s.fed = $-1
			
			if s.initscale
				s.scale = s.initscale*10/SMSLocalRNG(5, 9,s)
			end
			
			if s.target and s.target.valid
			
			local t = s.target
			s.momy = t.momy
			s.momx = t.momx
			
				if (abs(t.momz) < 2*t.scale) 
					s.state = S_NULL return --
				else
					s.blendmode = AST_ADD
					
					local ghs = P_SpawnGhostMobj(s)
					ghs.rollangle = s.rollangle
					ghs.eflags = s.eflags
					ghs.destscale = 9
					ghs.scalespeed = $/3
					ghs.momx = s.momx/2
					ghs.momy = s.momy/2
					ghs.tics = 5
					ghs.momz = $-10<<16*P_MobjFlip(s)
				end
				
				if (s.scale >= s.initscale*10/7) 
					P_MoveOrigin(s, t.x-t.momx, t.y-t.momy, t.z-(50*t.scale)*P_MobjFlip(t))
				else
					P_MoveOrigin(s, t.x-t.momx, t.y-t.momy, t.z)
				end
				
				if (t.eflags & MFE_VERTICALFLIP)
					s.z = $+29*t.scale
				end
				
				s.color = t.color
			end
			if s.fed <= 0
				s.state = S_NULL
			end
		end
end, MT_HMSRISEAURA)

//Huh. This is a good example of how to take the difficult route.
//Well, SMSreborn is my first project using Lua...

addHook("TouchSpecial", function(pwsh, s)
		if s and s.player 
		and not ((s.skin == "sms") or (s.skin == "samus") or (s.skin == "basesamus"))
	local p = s.player
			if (p.pflags & PF_JUMPED) 
			and p.readyforpowershare //Only work when jumping
			and (p.cmd.buttons & BT_CUSTOM3)
				if (p.rings > 49)
				and not p.powers[pw_super]
				and not p.mysticsuper //Not when you're ALREADY super!
					A_STARTMYSTICSUPER(s)
					return true
				end
			end
			if not p.powers[pw_super]
			and not p.mysticsuper
				if not s.sms_auradelay
					s.sms_auradelay = 16
					P_SpawnMobjFromMobj(s, 0, 0, 0, MT_SMSPOWERAURA)
				end
				s.ssxaticket = true
				p.readyforpowershare = 18
				return true
			end
		else
			return true
		end
end, MT_POWERSHARE)

//If the poweraura is the default color, become white.
addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
	if not s.color
		s.color = SKINCOLOR_SUPERSILVER3
	end
end, MT_SMSPOWERAURA)
addHook("MobjThinker", function(s)
	A_OverlayThink(s, 0, 0) //Don't lag behind!
	if s.valid //Make sure it's valid, incase someone dies.
		if s.target and s.target.player
			if s.target.player.hypermysticsonic
				A_ChangeColorAbsolute(s, 0, s.target.color)
			elseif SMSisyellow(s)
				A_ChangeColorAbsolute(s, 0, SKINCOLOR_SUPERSILVER3)
			end
		end
	end
end, MT_SMSPOWERAURA)

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
end, MT_SSXA)
addHook("MobjThinker", function(s)
	if (s.tics < 6)
		local TRANS = min( (s.frame & FF_TRANSMASK)+FRACUNIT, FF_TRANS90 )
		s.frame = ($ & ~FF_TRANSMASK)|TRANS
	elseif s.mcd
		s.mcd = $-1
		if s.mcd <= 1
			s.frame = ($ & ~FF_TRANSMASK)|FF_TRANS20
			s.mcd = 0
		end
	end
end, MT_SSXA)

//Why fire at all when everything shot at you is automatically reflected?
addHook("MobjThinker", function(s)
	if s.target and s.target.valid
	local t = s.target
	t.blendmode = AST_ADD
		if t.disappeartimer
			t.disappeartimer = $-1
		elseif t.valid
			t.state = S_NULL
	  		s.state = S_NULL
	  		return --
		end
		if (t.state != t.info.deathstate)
		and (t.state != t.info.xdeathstate)
			if (t and t.valid) and not t.health
	  			t.state = S_BOSSEXPLODE
	  			t.scale = $+200000
				S_StartSound(t, sfx_brakrx)
	  			s.state = S_NULL
	  			return --
			end
			if t.health and t.valid
				if t.boomerangtarget
				and t.boomerangtarget.valid
				and t.boomerangtarget.health
				
					t.angle = R_PointToAngle2(t.x, t.y, t.boomerangtarget.x, t.boomerangtarget.y)
					local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_THOK)
					ghs.color = SKINCOLOR_WHITE
					ghs.flags2 = $|MF2_SHADOW
				
					local homiedist = FixedHypot(FixedHypot(t.boomerangtarget.x - t.x, t.boomerangtarget.y - t.y), t.boomerangtarget.z - t.z)
					local speedmuller = 90*t.scale
					if (homiedist < 1) --anti divide by 0 failsafe.
						homiedist = 1
					end
					t.momx = FixedMul(FixedDiv(t.boomerangtarget.x - t.x, homiedist), speedmuller)
					t.momy = FixedMul(FixedDiv(t.boomerangtarget.y - t.y, homiedist), speedmuller)
					t.momz = FixedMul(FixedDiv(t.boomerangtarget.z - t.z, homiedist), speedmuller)
				end
			end 
		else
	  		t.state = S_BOSSEXPLODE
	  		t.scale = $+200000
			S_StartSound(t, sfx_brakrx)
	  		s.state = S_NULL
			return --
		end
	end
end, MT_RVGMISSILE)

//Shoot off sparks that explode into rising lightning upon contact with walls!
addHook("MobjThinker", function(s)
	if s.started == nil
		if not s.color
			s.color = SKINCOLOR_SKY
		end
		s.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
		s.blendmode = AST_ADD
		s.lifetime = 1
		s.fuse = 39
		s.started = s.scale
		s.health = 1
		s.GRAPHIX = SMS_Graphix(s)
		s.GORELVL = SMS_Gore(s, 1)
	end
	
	s.lifetime = $+1
	
		if (s.lifetime > 3)
			s.flags = $ & ~MF_NOCLIPHEIGHT
		end
		
		s.frame = SMSLocalRNG(0,3,s)|FF_TRANS20
	 	s.scale = (s.started*10)/SMSLocalRNG(4,11,s)
		
		if P_IsObjectOnGround(s) //Hop over the ground a little.
			P_SetObjectMomZ(s, 2<<16, true)
		end
		
		if (leveltime % 4 == 0) and s.GRAPHIX != 2 and not (s.GORELVL > 190)
			local ghs = P_SpawnGhostMobj(s)
			ghs.blendmode = AST_ADD
			P_SetObjectMomZ(ghs, FRACUNIT*2, true)
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.flags = MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY|MF_NOBLOCKMAP
			ghs.spriteyscale = $+25000
			ghs.spritexscale = $+25000
			ghs.fuse = SMSLocalRNG(14, 24, ghs)
			ghs.tics = 99
			if s.target and s.target.player 
				if (s.target.player != displayplayer or CV_FindVar("renderer") == 1)
					ghs.flags = $|MF2_DONTDRAW
				end
			end
		end
		
		if ( (abs(s.momy)+abs(s.momx)) < FRACUNIT) 
		or (s.fuse <= 2) //Too slow, now DIE
		/*	if ( (s.lifetime and (s.lifetime < 4)) and s.immunityfromboom == nil)
			and not (s.fuse and (s.fuse <= 2))
				s.immunityfromboom = 15
				s.flags = $|MF_NOGRAVITY|MF_NOCLIPHEIGHT
				s.momz = 0
				if s.target
				P_InstaThrust(s, R_PointToAngle2(s.target.x, s.target.y, s.x, s.y), 23<<16)
				end */
			if not s.immunityfromboom and s.health
				S_StartSound(s, sfx_s3ka6) //Fizzle out sound
			--	s.immunityfromboom = true
				if not s.GRAPHIX --This is SUPER intensive for reasons.
					local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_HMSLIGHTNING2)
					if (s.lifetime > 9)
						ghs.expsfx = true
					end
					ghs.target = s.target
					ghs.color = s.color
					ghs.spriteyscale = $+SMSLocalRNG(-9999,9999,s)
					ghs.spritexscale = $/2 //SMSLocalRNG(4999,-19999,s)
				end
				s.flags = $|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY|MF_NOBLOCKMAP & ~MF_SOLID
				s.health = 0
				P_KillMobj(s)
				return --
			end
		end
end, MT_HMSLIGHTNING1)

//Second lighting effect. Slowly vanish into thin air.

addHook("MobjThinker", function(s)
	if not s.ltimer --Setup!
		s.blendmode = AST_ADD
		s.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
		s.ltimer = 1
			if not s.silent1
				if s.color != SKINCOLOR_JET
				and SMSLocalRNG(5,6,s) == 6
					S_StartSound(s, sfx_kc4d) //Burst out sound
				end
				if s.expsfx
					if SMSLocalRNG(1,0,s)
						S_StartSound(s, sfx_cdfm55) //Possibly obnoxious bzzzt
					else
						S_StartSound(s, sfx_s3k78) //Possibly even more obnoxious PPSZZZZT bzzzt
					end
					S_StartSound(s, sfx_s3k45) //Shield jump sound, possibly too obvious.
					s.expsfx = false
				end
			end
			s.scale = $*2
			if s.growx2
				s.color = SKINCOLOR_RED
				s.destscale = $*15
				s.scalespeed = $*2
			else
				if not s.color
					s.color = SKINCOLOR_SKY
					s.scalespeed = $/2
				end
				s.destscale = $*5
			end
			P_SetObjectMomZ(s, 9999, true)
	else
			s.ltimer = $+1
			if s.followsms and s.followsms.valid
				P_MoveOrigin(s, s.followsms.x, s.followsms.y, s.followsms.z+(s.followsms.height*3/4))
				if not s.valid return end --
			end
			if s.color == SKINCOLOR_JET //The train for the Elseif Ruins will be departing soon.
				if not s.movh
					s.movh = SMSLocalRNG(2,1,s)
				elseif s.movh == 1
					s.rollangle = ANGLE_90
					s.momz = 0
				end
			elseif SMSisyellow(s)
				s.color = SMSLocalRNG(SKINCOLOR_SUPERGOLD5, SKINCOLOR_SUPERGOLD1,s)
			elseif s.color == SKINCOLOR_RED
				s.color = SKINCOLOR_FLAME
			elseif s.color == SKINCOLOR_FLAME
				s.color = SKINCOLOR_RED
			elseif s.color == SKINCOLOR_SKY
				s.color = SKINCOLOR_CYAN
			elseif s.color == SKINCOLOR_CYAN
				s.color = SKINCOLOR_SKY
			end
			
			local FRAME = P_RandomRange(4, 6)
			if (s.ltimer < 6)
				s.frame = FRAME|((s.ltimer < 18) and FF_TRANS10 or FF_TRANS20)
			elseif (s.ltimer < 25)
				s.frame = FRAME|((s.ltimer < 38) and FF_TRANS30 or FF_TRANS40)
				if s.ltimer == 16 --Remove collission at this point.
					s.flags = $|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY & ~MF_SOLID
					s.radius = 0
					s.height = 0
				end		
			elseif (s.ltimer < 46)
				if s.ltimershort
					P_KillMobj(s) return --
				end
				s.frame = FRAME|((s.ltimer < 38) and FF_TRANS50 or FF_TRANS60)
			else
				s.frame = FRAME|((s.ltimer < 55) and FF_TRANS70 or FF_TRANS80)
				if (s.ltimer > 60) and not s.growx2
				or (s.ltimer > 90) and s.growx2 
					P_KillMobj(s) return --
				end				
			end
	end
end, MT_HMSLIGHTNING2)


--Reverse roll direction.
addHook("MobjMoveBlocked", function(s, m, line)
	if line and not s.flamespark
		if s.smsrgtimer
			s.smsrgtimer = max($-1,3)
		end
		if not s.gentrgsfx2
		and s.Init and (s.Init > 3)
			s.gentrgsfx2 = true
			s.rollangle = $+SMSLocalRNG(-55,55)*ANG1
			if s.RollDirection
				s.RollDirection = $*-1
			end
			if s.ROCK				
				if (s.spriteyscale >= 3<<16) --Big rocks make impacts!
					local rng = SMSLocalRNG(sfx_rocks1,sfx_rocks4,s)
					if not S_SoundPlaying(s, rng)
						S_StartSoundAtVolume(s, rng, 125) 
					end
				end
				P_SetObjectMomZ(s, SMSLocalRNG(29,117,s)*(FRACUNIT/4), true)
			else
				local rvolume = max(30, min(150, s.spritexscale/350))
				if not S_SoundPlaying(s, sfx_gentrg)
					S_StartSoundAtVolume(s, sfx_gentrg, rvolume)
				end
			end
			s.momx = FixedDiv(FixedMul($,100),73)
			s.momy = FixedDiv(FixedMul($,100),73)
		end			
	end
end, MT_SMSRGORE)

//Robot gore! Avert your eyes, children!
addHook("MobjThinker", function(s)
	if s.Init == nil --Setup section...
		s.Init = 0
		
		if s.flamespark
			s.smsrgtimer = tonumber(s.flamespark) or 69
			s.angle = R_PointToAngle(s.x, s.y, s.x+s.momy, s.y+s.momy)+ANGLE_90 --ANGLE_270?
			s.rollangle = $ + ((s.frame & FF_HORIZONTALFLIP) and -ANGLE_45 or ANGLE_45)
			s.flags = $ & ~MF_BOUNCE & ~MF_GRENADEBOUNCE
			SMS_Gore(s,1)
		else
			local GORELVL = SMS_Gore(s)
			if not s.shadowscale --Shadows can be intensive when there's this many, thread carefully.
				if not (GORELVL > 100)
				and not s.ROCK != 777
					s.shadowscale = 75000
				end
			end
			--How long the debris sticks around.
			if s.smsrgtimer == nil
				local rng = SMSLocalRNG(200,280,s)
				if GORELVL > 20
					s.smsrgtimer = max(25, rng -(GORELVL*2)) --Disappear quicker?
				else
					s.smsrgtimer = rng
				end
			end
		end
		
		--What direction are we going to rollangle into?
		if s.RollDirection == nil
			s.RollDirection = (SMSLocalRNG(0,1,s) == 1) and 1 or -1
			if not P_CheckPosition(s, s.x, s.y)
			or not P_TryMove(s, s.x+s.momx, s.y+s.momy, true)
				if s.valid 
					s.flags = $ & ~MF_GRENADEBOUNCE & ~MF_BOUNCE
					local RNG = SMSLocalRNG(-17000, 17000,s)
					s.spritexscale = FRACUNIT+RNG
					s.spriteyscale = FRACUNIT+RNG
					if s.RollDirection == 1
						s.mirrored = true
					end
				end	
			end
		end			
	end
	
	--Setup done!
	s.Init = $+1
	s.smsrgtimer = $-1
	
		if (s.smsrgtimer <= 1) s.state = S_NULL return end	 --Disappear!
		s.trgmom = abs(s.momx)+abs(s.momy)  --Rough (not perfect) estimate of speed.
		
		--We're a flame!
		if s.flamespark 
			if P_IsObjectOnGround(s) or (s.eflags & MFE_TOUCHWATER)
				s.state = S_NULL return --
			end
			s.spritexscale = max(9999, $-450) --Gently shrink overtime.
			s.spriteyscale = max(9999, $-450)
			if (s.trgmom > 37000)
				s.rollangle = $ + ((s.frame & FF_HORIZONTALFLIP) and ANG1/2 or ANG1/-2)
			end
			if s.momz*P_MobjFlip(s) < -5<<16
				s.momz = -5<<16*P_MobjFlip(s) --Maximum falling speed.
				if s.floorz < s.z+(50*s.scale)
					local trans = min((s.frame & FF_TRANSMASK)+FRACUNIT, FF_TRANS90)
					s.frame = ($ & ~FF_TRANSMASK)|trans
				end
			end
			
		else --Not a flame.
		
			if not s.gentrgsfx --Only play landing sound once!
				if P_IsObjectOnGround(s)
					if P_CheckDeathPitCollide(s) --Nope.
					or (abs(s.floorz-s.ceilingz) < 8<<16) --Crushed!
						s.state = S_NULL return --
					elseif (s.Init > 3) --Wait 2 tics first...
						s.gentrgsfx = true
						if s.ROCK				
							if (s.spriteyscale >= 3<<16) --Big rocks make impacts!
								local rng = SMSLocalRNG(sfx_rocks1,sfx_rocks4,s)
								local rvolume = max(50, min(255, s.spritexscale/120))
								if not S_SoundPlaying(s, rng)
									S_StartSoundAtVolume(s, rng, rvolume) 
								end
							end
							P_Thrust(s, SMSLocalRNG(0,359,s)*ANG1, SMSLocalRNG(0,90,s)<<16/8)	
							P_SetObjectMomZ(s, SMSLocalRNG(55,130,s)*(FRACUNIT/4), true) --Fake bounce a bit.	
						else
							local rvolume = max(50, min(255, s.spritexscale/250))
							if not S_SoundPlaying(s, sfx_gentrg)
								S_StartSoundAtVolume(s, sfx_gentrg, rvolume)
							end
							P_Thrust(s, SMSLocalRNG(0,359,s)*ANG1, SMSLocalRNG(1,999,s)*(FRACUNIT/140))	
							P_SetObjectMomZ(s, SMSLocalRNG(55,130,s)*(FRACUNIT/33), true)
						end
						s.RollDirection = $*-1				
					end
				end
			end		
			if (s.trgmom > 37000) --Rollspeed is based on your momentum.
				s.rollangle = $+(FixedAngle(s.trgmom)*s.RollDirection)	
			elseif (s.flags & MF_GRENADEBOUNCE)
				s.flags = $ & ~MF_GRENADEBOUNCE
			end
			if s.ROCK --We're a rock!
				if (s.momz*P_MobjFlip(s) > -5<<16) and (s.momz*P_MobjFlip(s) > -11<<16) and (s.Init > 5) --Fall quicker.
					local fallspeed = FixedDiv(FixedMul(7776, 32678), P_GetMobjGravity(s) or 32678)
					s.momz = $+(fallspeed*P_MobjFlip(s)) --Fall quicker!
				end
			--HMS makes you rapidly explode roughly 2 times per second.
			elseif s.hmsexplosioncurse
				s.hmsexplosioncurse = $-1
				local rng = SMSLocalRNG(1,18,s)
				if rng == 9
					local ghs = P_SpawnMobjFromMobj(s, 0, 0, 0, MT_BOSSEXPLODE)
					ghs.scale = $*max(1, (rng/6))
					ghs.destscale = ghs.scalespeed+1
					ghs.frame = $|FF_TRANS40
				end			
			end
		end
end, MT_SMSRGORE)

//Complicated Robot Gore Function. Avoids having to write it all down 500 times, and makes it easier to repeat.
local function RobotGoreSpawn(s, object, eneradius, eneheight, momentum, zmomentum, color, scale, extramom, extramomang, hmsspecial)

--	if s and object and eneradius and eneheight and momentum and zmomentum and color and scale
	
		s.rgs = P_SpawnMobj(
		s.x+((P_RandomRange(-eneradius, eneradius))<<16),
		s.y+((P_RandomRange(-eneradius, eneradius))<<16),
		s.z+((P_RandomRange(0, s.height/FRACUNIT))<<16),
		object)
		
		s.rgs.color = color
		s.rgs.sprite = SPR_RGOR
		
		if hmsspecial
			s.rgs.hmsexplosioncurse = 20
			s.rgs.frame = A
		else
			s.rgs.frame = SMSLocalRNG(0,4,s.rgs)
		end
		
		s.rgs.momx = SMSLocalRNG(-momentum, momentum,s.rgs)<<16
		s.rgs.momy = SMSLocalRNG(momentum, -momentum+1, s.rgs)<<16
		
		--Cap maximum fling height
		if extramom and (extramom > 50)
			s.rgs.momz = (extramom > 200) and (SMSLocalRNG(1, 100,s.rgs))<<16 or P_RandomRange(1, extramom/2)<<16	
		else
			s.rgs.momz = P_RandomRange(1, zmomentum)<<16	
		end		
		if extramomang
			P_Thrust(s.rgs, extramomang+ANGLE_180, extramom<<16)
		end
		
		s.rgs.scale = (scale) or s.scale --Custom scale?
		
		local GORELVL = SMS_Gore(s,1)
		if s.rgs.srgorelvl and (s.rgs.srgorelvl > GORELVL)
			SMS_Gore(s, max(1, (s.rgs.srgorelvl-GORELVL)) )
		end
		s.rgs.srgorelvl = GORELVL
--	end

end

//Setup some defaults, incase no enemy data comes in.
addHook("MobjSpawn", function(s)

	if s.eneheight else s.eneheight = 28<<16 end 
	if s.eneradius else s.eneradius = 32<<16 end
	if s.enex else s.enex = s.x end if s.eney else s.eney = s.y end if s.enez else s.enez = s.z end
	if s.enemom else s.enemom = 10<<16 end
	if s.enecolor else s.enecolor = SKINCOLOR_SILVER end
	if s.enescale else s.enescale = FRACUNIT end
	if s.enethrust else s.enethrust = 9<<16 end
	
end, MT_SMSRGORESPAWN)
//This thing spawns the gore. There are a LOT of actions at once here.
addHook("MobjThinker", function(s)	
local x,y,z	= s.enex,s.eney,s.enez
local RADIUS,HEIGHT = 48,48
local MOM = 10
local THRUST = 10
local GORELVL = (s.hmskiller) and SMS_Gore(s,1) or SMS_Gore(s)
local GOREADD = SMS_Graphix(s)+1 or 2
if s.hmskiller GOREADD = $+1 end --Hold back with HMS.
	
		if s.actiontime == nil --Setup!
		
			//Setup, incase no enemy data came in.
			
		/*	if s.eneheight else s.eneheight = 28<<16 end 
			if s.eneradius else s.eneradius = 32<<16 end
			if s.enex else s.enex = s.x end if s.eney else s.eney = s.y end if s.enez else s.enez = s.z end
			if s.enemom else s.enemom = 10<<16 end
			if s.enecolor else s.enecolor = SKINCOLOR_SILVER end
			if s.enescale else s.enescale = FRACUNIT end
			if s.enethrust else s.enethrust = 9<<16 end */
			
			if s.eneanglethrust != nil elseif s.target and s.target.valid R_PointToAngle2(s.x, s.y, s.target.x, s.target.y) else s.enethrust = nil s.eneanglethrust = nil end
			
			x,y,z = s.enex,s.eney,s.enez
			RADIUS = FixedInt(s.eneradius)
			HEIGHT = FixedInt(s.eneheight)
			MOM = FixedInt(s.enemom)
			THRUST = FixedInt(s.enethrust)
			
			for d = 1,1
				for i = 0,3
					RobotGoreSpawn(s, MT_SMSRGORE, RADIUS, HEIGHT, MOM, SMSLocalRNG(11-i, 26-i,s), s.enecolor, s.enescale, THRUST, s.eneanglethrust)
					SMS_Gore(s,GOREADD)
					GORELVL = $+GOREADD		
					if (GORELVL > 224) break end
					if d == 0 and s.hmskiller continue end --Spawn less as HMS.
				end
				if s.target and s.target.valid --Move to our target getting gored so the next effect makes sense.
					P_SetOrigin(s, s.target.x, s.target.y, s.target.z+s.target.height/2)
				end
				for i = 0,3
					RobotGoreSpawn(s, MT_SMSRGORE, RADIUS, HEIGHT, MOM, P_RandomRange(8, 25), s.enecolor, s.enescale*3, THRUST, s.eneanglethrust)
					SMS_Gore(s,GOREADD)
					GORELVL = $+GOREADD	
					if (GORELVL > 224) break end
				end
			end
			RobotGoreSpawn(s, MT_SMSRGORE, RADIUS, HEIGHT, MOM, SMSLocalRNG(8, 25,s), s.enecolor, s.enescale*3, THRUST, s.eneanglethrust)		
			s.actiontime = 1
			return --
		end	
		
		--Only HMS usually makes use of a continued thinker.
		s.actiontime = $+1
		
		--Cancel Conditions
		if not (s.hmskiller and s.hmskiller.valid and s.hmskiller.player)
		or (s.actiontime > 12)
		or not (s.target and s.target.valid)
		or P_CheckDeathPitCollide(s)
		or (s.hmskiller.state == S_HMSBLACKHOLE)
			s.state = S_NULL return --
		end
			
		local t = s.target
		if s.actiontime == (2 or 6 or 12)
			for i = 1,3,2
				RobotGoreSpawn(s, MT_SMSRGORE, RADIUS, HEIGHT, MOM, P_RandomRange(8, 25), s.enecolor, s.enescale*i, THRUST, s.eneanglethrust, true)
			end
			if s.target.hmsgodaurad
				if s.actiontime == 2
					s.hmskiller.suddenquickspeed = 7
					s.hmskiller.flags2 = $|MF2_DONTDRAW
				end
				S_StartSound(s.hmskiller, sfx_nerf)
				A_SMSquake(s.hmskiller, 60<<16, 3, 1222)
					
				P_SpawnMobj(
				x+((SMSLocalRNG(-RADIUS*10, RADIUS*6,s))<<16),
				y+((P_RandomRange(-RADIUS*6, RADIUS*6))<<16),
				z+((P_RandomRange(0, HEIGHT*9))<<16), 
				MT_BURSTWAVE)
					
				local ghs = P_SpawnMobj(
				x+((P_RandomRange(-RADIUS*10, RADIUS*6))<<16), 
				y+((SMSLocalRNG(-RADIUS*6, RADIUS*6,s))<<16),
				z+((SMSLocalRNG(0, HEIGHT*9,s))<<16),
				MT_BURSTLIGHT)
				ghs.color = SKINCOLOR_SUPERGOLD4
				ghs.target = s.hmskiller
			end
		end
end, MT_SMSRGORESPAWN)

//Warppoint! Colliding with 1-way linedefs? Adjust your property.
addHook("MobjThinker", function(s)
	if (s.ceilingz < s.ogz) or (s.floorz > s.ogz)
		s.nowgoslowly = true //Slow down when you clip through something! Wouldn't want to overshoot things.
	end
	if s.tptime --And now I know about for loops, hurray!
		for i = 1,5
			if not P_TryMove(s, s.x+FixedMul(35<<16, cos(s.angle)), s.y+FixedMul(35<<16, sin(s.angle)), true)
				P_MoveOrigin(s, s.x+FixedMul(35<<16, cos(s.angle)), s.y+FixedMul(35<<16, sin(s.angle)), s.z)
				s.nowgoslowly = true
			end
		end
		s.tptime = false
	else
		P_InstaThrust(s, s.angle, s.nowgoslowly and 30<<16 or 150<<16)
	end
end, MT_HMSWARPPOINT)
addHook("MobjLineCollide", function(s, l)
	if l and l.valid
		if (l.flags == ML_IMPASSIBLE) or not (l.backside and l.frontside)
			s.dontwarptome = true return false --
		else
			s.ihavetped = true
			s.tptime = true return false --
		end
	end
	return false
end, MT_HMSWARPPOINT)

addHook("MobjThinker", function(s)
	local die = true local t = s.target	local p = t and t.player
	if p
		if p.powers[pw_sneakers]
		and t.health
		and p.speedspeedspeedspeedspeed
		and not p.exiting
			if (leveltime%2==0)
				local RAD = max(4, (s.radius*2/FRACUNIT))
				local HEIGHT = max(1, (s.height*2/FRACUNIT))
				local RNGX,RNGY = P_RandomRange(RAD,-RAD),P_RandomRange(RAD,-RAD)
				for i = -1,1,2
					local ghs = P_SpawnMobjFromMobj(t,RNGX*s.scale*i, RNGY*s.scale*i,
					P_RandomRange(HEIGHT,-HEIGHT), MT_SMSSPEEDSPARKLE)
					if ghs and ghs.valid
						ghs.blendmode = AST_ADD
						ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
						ghs.target = t
						ghs.momx,ghs.momy,ghs.momz = $+s.momx/2,$+s.momy/2,$+s.momz/2
						if i == -1
							ghs.color = s.color
							ghs.colorized = true
						end
						ghs.fuse = 9+i
					end	
				end
				if (leveltime%6==0)
					SMS_ElectricBody(t, nil, SKINCOLOR_SKY)
				end				
			end			
		--	A_Capechase(s, 0, 0)
			die = false
		//	P_MoveOrigin(s, t.x+t.momx, t.y+t.momy, t.z+t.momz)
		end
	end
	if die and s.valid
		P_RemoveMobj(s) --
	end
end, MT_SMSSPARKLESPAWN)

addHook("MobjThinker", function(s)
	local die = true
	local t = s.target
	local p = t and t.player
	
	if p and t.skin == "sms" 	
		if p.speedspeedspeedspeedspeed
		and not (t.eflags & MFE_UNDERWATER)
		and not p.exiting
		and not p.hypermysticsonic
		and t.health
			if not (s.flags2 & MF2_SHADOW) and (leveltime%2)
				local ghs = P_SpawnGhostMobj(s)
				ghs.flags = $|MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT
				ghs.spritexscale,ghs.spriteyscale = $+9000,$+9000
				ghs.blendmode = AST_ADD
				ghs.frame = ($ & FF_FRAMEMASK)|FF_TRANS90
			end
			die = false --Don't die.
			
			if not s.woopwoopwoop
				if p.lddiscount==1
					s.flags2 = $|MF2_SHADOW
				elseif p.lddiscount==2
					local colortable = {SKINCOLOR_CRIMSON, SKINCOLOR_WHITE, SKINCOLOR_SUPERSILVER1, SKINCOLOR_SUPERGOLD5, SKINCOLOR_SUPERGOLD3, SKINCOLOR_FLAME, SKINCOLOR_EMERALD}
					s.state = S_SMSTSPARK1
					s.color = colortable[P_RandomRange(1,7)]
					s.colorized = true
				end
				P_SetObjectMomZ(s, 4<<16)
				s.momx = ((s.x-t.x) or 1)/15
				s.momy = ((s.y-t.y) or 1)/15
				s.woopwoopwoop = true
		   	end
		end
	end
	if die and s.valid
		P_RemoveMobj(s)
	end
end, MT_SMSSPEEDSPARKLE)

//The radial spinboost effect. It changes to your colors of course!

addHook("MobjThinker", function(s)
	if s.target
		if (s.target.eflags & MFE_VERTICALFLIP)
		and not (s.flags2 & MF2_OBJECTFLIP)
			s.flags2 = $|MF2_OBJECTFLIP
			s.z = s.target.z-64<<16
		end
		if SMSisyellow(s)
			s.color = SKINCOLOR_BONE
			s.blendmode = AST_ADD
		else
			s.color = s.target.color
		end
		if (s.state == S_SMSSPINBOOST3)
		or (s.state == S_SMSSPINBOOST4)
			s.scalespeed = 25000
			P_SetObjectMomZ(s, -8<<16, relative)
		end
	end
end, MT_RADIALBOOST)

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
	if not s.color
		s.color = SKINCOLOR_BEIGE
	end
end, MT_BURSTLIGHT)
addHook("MobjThinker", function(s)
	if (s.state == S_BURSTLIGHT3)
		if not s.shrunkdowntosizealready
			A_SetScale(s, s.scale*3/2, 1)
			s.shrunkdowntosizealready = true
			end
	elseif (s.state == S_BURSTLIGHT5)
		if not s.one_gr
			A_SetScale(s, s.scale+5<<16, 1)
			s.one_gr = true
		end
	end
end, MT_BURSTLIGHT)

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
	if not s.color
		s.color = SKINCOLOR_BONE
	end	
end, MT_BURSTWAVE)
addHook("MobjThinker", function(s)
		P_SetObjectMomZ(s, -FRACUNIT)
		if (s.state == S_BURSTWAVE1)
			s.scalespeed = s.scalespeed*4
			A_SetScale(s, s.scale+25<<16, 1)
		elseif (s.state == S_BURSTWAVE2) or (s.state == S_BURSTWAVE3) or (s.state == S_BURSTWAVE4)
			if s.tics == 2
				local ghs = P_SpawnGhostMobj(s)
				ghs.fuse = 8 ghs.tics = 11
			end
		elseif (s.state == S_BURSTWAVE5)
			if (s.color == SKINCOLOR_RED)
				s.color = SKINCOLOR_YELLOW
			end
		elseif (s.state == S_BURSTWAVE6)
			if (s.color == SKINCOLOR_YELLOW)
				s.color = SKINCOLOR_EMERALD
			end
			local trans = (s.frame & FF_TRANSMASK)
			s.frame = ($ & ~FF_TRANSMASK)|min(FF_TRANS90, (trans+FRACUNIT))
		end
end, MT_BURSTWAVE)

//SMS' overly colorfull stomp aura. Did I go a little overboard here?

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
end,MT_SMSSTOMPLINE)
addHook("MobjThinker", function(s)
	if s.target and s.target.valid and s.target.health and s.target.player
		local t = s.target local p = t.player
		s.eflags = t.eflags
		if (p.hypermysticsonic or p.sms_sontransform)
			P_SetObjectMomZ(s, 9<<16)
		elseif P_IsObjectOnGround(t) and not p.hypermysticsonic
			s.momx = (s.x-t.x)/16
			s.momy = (s.y-t.y)/16
		end
		if (s.state == S_SMSLINE2)
			s.color = SKINCOLOR_ORANGE
		elseif (s.state == S_SMSLINE3)
			s.color = SKINCOLOR_YELLOW
		elseif (s.state == S_SMSLINE4)
			s.color = SKINCOLOR_PERIDOT
		else
			s.color = (s.state == S_SMSLINE5) and SKINCOLOR_EMERALD or SKINCOLOR_RED
		end
    elseif s.valid
    	P_KillMobj(s)
    end
end, MT_SMSSTOMPLINE)

addHook("MobjThinker", function(s)
	if s.target and not s.blown
		A_NapalmScatter(s, MT_HMS_NAPALM_FLAMES, 256)
		s.blown = true
		s.blendmode = AST_ADD
	end
end, MT_HMSSTOMPEXPLODE)

addHook("MobjThinker", function(s)
	if not s.trymsspawn --Try spawning this once!
		if (s.state != S_STARPOST_IDLE) --But not when idle!
			local SPAWNMYSTICEMERALD = false
			for z in players.iterate
				if z.smsnomsspawn
					s.trymsspawn = true
					return
				elseif z.mo and (z.mo.skin == "sms" or A_SMScompatibleskin(z.mo))
					SPAWNMYSTICEMERALD = true
				end
			end
			if not (s.msstarp and s.msstarp.valid)
				if netgame --Always spawn it in netgames.
					SPAWNMYSTICEMERALD = true
				end
				
				s.msstarp = P_SpawnMobjFromMobj(s, SMSLocalRNG(-1,1,s), SMSLocalRNG(1,-1,s), 16*s.scale, MT_THEMYSTICEMERALD)
				if s.msstarp and s.msstarp.valid
					s.msstarp.msexisting = 15
				end				
				S_StartSound(s, sfx_s1c9)
				A_SMSSTOMPSMOKE(s, 2, MT_IVSP, 21<<16)
				A_SMSSTOMPSMOKE(s, 10, MT_IVSP, 5<<16)
				A_SMSSTOMPSMOKE(s, 6, MT_IVSP, 71<<16)
			end
			s.trymsspawn = true
		end
	end
end, MT_STARPOST)

addHook("MobjThinker", function(s)
	if s.target and s.target.valid
		local t = s.target
		if (s.tics > 1)
			if (s.state == S_SMSSPRINTLINE3)
			or (s.state == S_SMSSPRINTLINE3M)
			or (s.state == S_SMSSPRINTLINE4)
			or (s.state == S_SMSSPRINTLINE4M)
				if t.player and t.player.hypermysticsonic
					s.color = SKINCOLOR_SUPERSILVER3
				else
					s.color = SKINCOLOR_SUPERGOLD3
				end
			end
		elseif s.tics == 1
			if (s.state == S_SMSSPRINTLINE4) or (s.state == S_SMSSPRINTLINE4M)
				if t.player and t.player.hypermysticsonic
					s.color = SKINCOLOR_COBALT
				else
					s.color = SKINCOLOR_EMERALD
				end
			end
		end
	end
end, MT_SMSSPRINTLINE)

addHook("MobjSpawn", function(s)
	s.blendmode = AST_ADD
	s.renderflags = RF_FULLBRIGHT
	if not s.color
		s.color = SKINCOLOR_SMS2R
	end
end, MT_GOLDENSHINE)
--Ring Burst aura. SPR_AURA frame U.
addHook("MobjThinker", function(s)
	if s.target and s.target.valid
	local t = s.target
		P_MoveOrigin(s, t.x, t.y, t.z)
		if (t.player and t.player.sms_ringburstfly)
			if (t.player.sms_ringburstfly == 3)	
				if s.valid s.state = S_NULL return end --
			elseif not t.player.smsgraphix
				local ghs = HMS_DarkLight(t, t.player, SKINCOLOR_SMS5R)
				ghs.blendmode = AST_ADD
				ghs.spriteyoffset = $-2<<16
				ghs.tracer = s
				ghs.spriteyscale = $+9900
				ghs.spritexscale = $+9900
				ghs.GSoffsetX = -11*s.scale
				ghs.GSoffsetY = -11*s.scale
				ghs.GSoffsetZ = (s.height/2)+(11*s.scale*P_MobjFlip(s))
				ghs.sttroll = ANGLE_22h
			end
		elseif (s.tics > 10)
			s.tics = 10
		end
	end
	s.rollangle = $+FixedAngle(40<<16)
	if (s.tics < 7) and (s.tics % 2)
		local trans = min((s.frame & FF_TRANSMASK)+FRACUNIT, FF_TRANS90)
		s.frame = ($ & ~FF_TRANSMASK)|trans
	end	
end, MT_GOLDENSHINE)


addHook("MobjThinker", function(s)
	if s.target and s.target.valid
	local t = s.target
	A_CapeChase(s,1,0)	
	
	if not s.valid return end --Cape Chase is dangerous, always check valid afterwards.

		s.frame = (t.frame & FF_HORIZONTALFLIP) and $|FF_HORIZONTALFLIP or $|FF_FULLBRIGHT
		s.frame = (t.frame & FF_VERTICALFLIP) and $|FF_VERTICALFLIP or $|FF_FULLBRIGHT
		s.mirrored = t.mirrored
		s.rollangle = t.rollangle
		s.spritexoffset = t.spritexoffset
		s.spriteyoffset = t.spriteyoffset 
		s.spriteyscale = (t.spriteyscale == FRACUNIT) and $+2000 or t.spriteyscale+1500
		s.spritexscale = (t.spritexscale == FRACUNIT) and $+2000 or t.spritexscale+1500	
		
		if s.target.player
			local p = s.target.player
			s.angle = p.drawangle
			if p.hypermysticsonic
				s.spriteyscale = $+1200
				s.spritexscale = $+1800
				s.frame = ($ & ~FF_TRANSMASK)|FF_TRANS60
			end
			if s.fuse <= 1
				s.frame = ($ & ~FF_TRANSMASK)|FF_TRANS90
				s.blendmode = AST_TRANSLUCENT
			end	
		else
			s.angle = t.angle
			s.spriteyscale = $+3000
			s.spritexscale = $+5000			
		end
	end
end, MT_SMSROVERGLOW)

//This is the object responsible for keeping track of enemies sucked in by black hole, hit by Comet HayMakers, etc. 
//CMPH stands for Comet Haymaker PuncH, btw.
addHook("MobjThinker", function(s)
	if not (s.target and s.target.valid) s.state = S_NULL return end --
	local t = s.target
	
		//Actual Comet Haymaker tracker. Punch tracker! Overrides all other behaviour.
		if t.fusrodah		
			SMS_PunchTracker(s, t)	return --
		
		--HMS' gravity blast tracker.
		elseif t.gravbu and ( (t.flags & (MF_ENEMY|MF_BOSS)) or t.player) and t.health
		
			if not t.smsglowticket
				t.renderflags = $ & ~RF_FULLBRIGHT
			end
			
			--Prioritize the blackhole function!
			if s.bkhl 
				if t.smsglowticket
					t.renderflags = $ & ~RF_FULLBRIGHT
					t.smsglowticket = nil
				elseif not t.player
					t.renderflags = $ & ~(RF_FULLBRIGHT|RF_NOCOLORMAPS)
				end 
				t.rollangle = 0
				t.gravbu = nil
				return --
			else --Count down!
				t.gravbu = $-1
				t.smsglowticket = t.player and 90 or nil --Make players glow!
			
				if not t.player
					t.rollangle = (leveltime*ANG30)+(t.gravbu*ANG10)
					t.angle = t.rollangle
					if t.gravbu == 0 or t.gravbu == 1
						t.rollangle = 0
					end
				end
			end
			
			if (t.gravbu % 3 == 0) --Ghost Mobjs.
				if (t.gravbu < 33)
					local ghs = P_SpawnGhostMobj(t)
					ghs.flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_SCENERY|MF_NOGRAVITY
					ghs.radius,ghs.height = 0,0
					ghs.dispoffset = 2
					if (t.gravbu > 20)
						ghs.fuse = t.gravbu+2
						ghs.tics = ghs.fuse+2
					else
						ghs.tics = 19 ghs.fuse = 44
					end
					ghs.renderflags = $|RF_SEMIBRIGHT
					ghs.rollangle = t.rollangle
					ghs.destscale = $*99
					if t.color
						ghs.colorized = true
						ghs.color = t.color
					end
				end			
			end
			
			local RISESPEED = 0
			
			if (t.gravbu > 14)
				RISESPEED = 26<<16
				if not t.player
					if t.tracer and t.tracer.player and t.tracer.player.hypermysticsonic
						P_InstaThrust(t, t.tracer, 8<<16)
					end
					if t.gravbu == 35 --Remove your action!
						P_SetMobjStateNF(t, t.state)
						t.tics = 5
						t.anim_duration = 5
						if t.target and t.target.player --No targets!
							t.target = nil
						end
						if t.tracer and t.tracer.player
							t.tracer = nil
						end		
					elseif (t.tics < 5) or (t.anim_duration < 5) --Keep current state.
						t.tics = 5
						t.anim_duration = 5
					end
				end
				
				if (leveltime % 3 == 0)		
					if (leveltime % 6 == 0) or (t.gravbu > 28)
						local ghs = P_SpawnMobjFromMobj(t,0,0,0, MT_BURSTWAVE)
						if (t.gravbu > 28)
							ghs.color = SMSLocalRNG(SKINCOLOR_EMERALD, SKINCOLOR_SUPERGOLD5, ghs)
							ghs.colorized = true
							P_SetObjectMomZ(ghs, -FRACUNIT*4, true)
							ghs.spriteyscale = $*5/4+(t.gravbu*444)
							ghs.spritexscale = $*5/4+(t.gravbu*444)
							ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
							if not t.player
								t.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
							end							
						else
							ghs.spriteyscale = $*3/4
							ghs.spritexscale = $*3/4
							ghs.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS
						end
						ghs.dispoffset = -3
					end
				end
			elseif (t.gravbu > 5)
				RISESPEED = 17<<16
				t.momx = t.momx/2
				t.momy = t.momy/2
				if (leveltime % 3 == 0)				
					local ghs = P_SpawnMobjFromMobj(t, 0,0,0, MT_BURSTLIGHT)
					ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
					ghs.spriteyscale = $*4/3
					ghs.spritexscale = $*4/3
					ghs.dispoffset = -2
				end
			elseif (t.gravbu == 5)
				RISESPEED = 7<<16
				P_SpawnMobjFromMobj(t, 0,0,0, MT_EXPLODE)
				if (t.type == MT_EGGMOBILE3 or t.type == MT_EGGMOBILE4)
					t.cmphtracker = nil
					P_KillMobj(t, s, (s.tracer or s))
				end
			else
				if (leveltime % 2)
					P_SpawnMobjFromMobj(t, 0,0,0, MT_EXPLODE)
				end
			end
			--What speed to rise at?
			if (t.flags & (MF_NOGRAVITY|MF_FLOAT)) and not t.player --Bitch, you're moving too!
				if RISESPEED
					if (t.eflags & MFE_VERTICALFLIP)
						if not t.z > t.floorz
							t.z =  $-RISESPEED
						end
					elseif t.ceilingz and (t.z+t.height < t.ceilingz)
						t.z = $+RISESPEED
					end
				end
			end
			if (t.momz*P_MobjFlip(t) < RISESPEED) and (t.gravbu > 4)
				P_SetObjectMomZ(t, RISESPEED)
			end
			return --
			
		--Black Hole tracker.
		elseif s.bkhl
		
			if s.tracer and (s.tracer.state == S_HMSBLACKHOLE)
				if (t.z <= t.floorz+FRACUNIT)
					t.z = $+FRACUNIT
				elseif (t.eflags & MFE_VERTICALFLIP) and (t.z+t.height > t.ceilingz-FRACUNIT)
					t.z = $-FRACUNIT
				end
				local homiedist = FixedHypot(FixedHypot(s.tracer.x - t.x, s.tracer.y - t.y), s.tracer.z-t.z)
				if (homiedist > s.tracer.scale)
					local speedmuller = FixedMul(30<<16, t.scale)
					t.momx = FixedMul(FixedDiv(s.tracer.x-t.x, homiedist), speedmuller)
					t.momy = FixedMul(FixedDiv(s.tracer.y-t.y, homiedist), speedmuller)
					t.momz = FixedMul(FixedDiv(s.tracer.z-t.z, homiedist), speedmuller)
				end
				t.bkhltracked = true
			else
				t.bkhltracked = nil
				s.state = S_NULL
				return --
			end
		else
			s.state = S_NULL return --
		end
		
end, MT_CMPHTRACKER)

--You buzz off.
addHook("MobjThinker", function(s)
	local t = s.target
	if t and t.skin == "sms" and t.player
		t.player.banned,t.player.powers[pw_nocontrol] = nil,0
		if s.inpinch and (s.state <= S_HIVEBEAST_DEFEAT1)
			if (s.invulntimer > 90)
				s.invulntimer = 45
			elseif (s.invulntimer <= 27) or s.invulntimer == 72 or s.invulntimer == 73
				s.flags = MF_NOGRAVITY|MF_SHOOTABLE|MF_ENEMY|MF_SPECIAL 
				s.flags2 = $ & ~(MF2_FRET|MF2_DONTDRAW)
				s.invulntimer = 0 --Could you QUIT stalling? I've got places to be!
			end
			if s.phase != 1 and not s.HealthBoost --Here, have some extra health.
				s.health = max($,14)
			end
		end
	end
	if mapheaderinfo[gamemap].lvlttl == "Industrial Valley" or s.PettyByPass
		for p in players.iterate
			p.banned = nil
			if p.mo
				local s = p.mo
				if p.powers[pw_nocontrol] and s.skin == "sms"
					p.powers[pw_nocontrol] = 0
				end	
				local SEC = s.subsector and s.subsector.sector
				if SEC and SEC.valid and (SEC.tag == 3999 or SEC.tag == 148)
					P_LinedefExecute(8001, s, SEC)
					S_ChangeMusic("VSHIVE", true, p)		
				end
			end
		end
	end
	--Now fix some bugs while we're at it.
	if (s.health <= 0) and (s.state <= S_HIVEBEAST_DEFEAT1) and (s.phase == 2) --Survive 1 haymaker? Fine, but properly die once you're dead, please.
		if mapheaderinfo[gamemap].lvlttl == "Industrial Valley"
			P_LinedefExecute(8888, s, s.subsector.sector)
			P_LinedefExecute(8889, s, s.subsector.sector)
		end	
		s.invulntimer = 0
		s.flags = $|MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY
		P_DamageMobj(s) P_KillMobj(s) 
		s.flags,s.flags2 = $ & ~(MF_NOGRAVITY|MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY), $ & ~MF2_DONTDRAW
		s.reactiontime,s.inpinch,s.zlock.active,s.phase = FRACUNIT,false,false,3
		s.state = S_HIVEBEAST_DEFEAT1
		s.momz = 16*s.scale*P_MobjFlip(s)
		S_StartSound(nil, sfx_spexpd) S_StartSound(nil, sfx_hbdfet)
		S_FadeOutStopMusic(2*MUSICRATE)
		for z in players.iterate
			P_FlashPal(z, PAL_NUKE, 9)
		end	
	elseif (s.flags2 & MF2_FRET) or s.invulntimer --More bugfixes.
		s.MaxFret = s.MaxFret and $-1 or 150
		if s.MaxFret == 1
			s.invulntimer,s.inpinch = 1,nil
			s.flags,s.flags2 = $|MF_SHOOTABLE, $ & ~(MF2_FRET|MF2_DONTDRAW)
		end
	else
		s.MaxFret = 150
	end
end, MT_HIVEBEAST)

--Tracker that tracks the ragdolling corpses of SMS' enemies.
addHook("MobjThinker", function(s)
	if not (s.SMS_SpecialKill and s.SMS_SpecialKill.valid)
		P_RemoveMobj(s)	return --
	end
	--Special Kill effects! Basically manipulating a puppet that acts like the old enemy we've killed. Spoopy.
	s.tics = 3 s.fuse = 3
	SMS_SpecialDeath(s, s.SMS_SpecialKill, s.target)
end, MT_CORPSETRACKER)

//"B-but HMS-sama, I've got no hitbox for you to hit me with!"
//Oh SRB2-chan....you sweet, naïve fool~ ♥ /EndCreepyFetishDialogue

local function DummyHBadd(s)
	if not s.dummyhb 
		s.dummyhb = P_SpawnMobjFromMobj(s,0,0,0, MT_SMSDUMMYTRACKER)
		s.dummyhb.target = s
		s.dummyhb.tics = -1
		s.dummyhb.dummyhbx = true
	end
end
addHook("MobjSpawn", DummyHBadd, MT_EGGGUARD)
addHook("MobjSpawn", DummyHBadd, MT_TURRET)
addHook("MobjSpawn", DummyHBadd, MT_MINUS)
addHook("MobjSpawn", DummyHBadd, MT_CYBRAKDEMON)
addHook("MobjSpawn", DummyHBadd, MT_METALSONIC_BATTLE)
addHook("MobjSpawn", DummyHBadd, MT_BLACKEGGMAN)
addHook("MobjSpawn", DummyHBadd, MT_SUSPICIOUSFACESTABBERSTATUE)
addHook("MobjSpawn", DummyHBadd, MT_KOOPA)
addHook("MobjSpawn", DummyHBadd, MT_EGGROBO1)

//Make Rollout Rocks go faster if SMS holds CUSTOM1

addHook("MobjThinker", function(s)
	if not (s.valid and s.tracer and s.tracer.valid) return end
	if s.iampickedupbysmshelp
		s.iampickedupbysmshelp = $-1
	end	
	local t = s.tracer --Note that t is tracer this time.
	if not t.player
		if t.valid
			t.target = s
			s.flags = $|MF_PUSHABLE
		end
		s.tracer = nil
		return --
	elseif t.skin != "sms"
	
		return --
		
	else --if t.skin == "sms"
		local p = t.player	
		t.DeyCURollin = 8 --Value for rolling on Rollout Balls!
		t.smspickupCD = 8
		t.smpktarget = nil
		
		 if p.powers[pw_carry] == CR_ROLLOUT
			if s.nomyman
				s.nomyman = $-1
			end
			
			local doanimation = false
			
			if (p.cmd.buttons & BT_CUSTOM1) --Let SMS spinboost on rollout rocks.
			and not (p.cmd.buttons & BT_JUMP)
			and not s.nomyman and not s.iampickedupbysmshelp
				local amount = p.hypermysticsonic and 64 or 16
				for i = 1,amount --We'll have to boost with P_TryMove since rollout rocks don't have momentum normally.
					if not ( P_TryMove(s, t.x+FixedMul(11355, cos(t.angle)), t.y+FixedMul(11355, sin(t.angle)), true) )
						p.powers[pw_carry] = 0 
						t.momz = s.momz
						S_StartSound(s, sfx_s3k49)
						s.nomyman = 6
						s.flags = $|MF_PUSHABLE
						t.target = s
						s.tracer = nil
						return --
					end
				end
				
				if not p.hypermysticsonic
					t.state = S_PLAY_ROLL //Go into rolling frames for boosting.
					p.pflags = $|PF_JUMPED
					P_SpawnGhostMobj(s)
				end
				if not s.smssoundplayed2
					S_StartSound(s, sfx_zoom)
					if p.hypermysticsonic
						S_StartSound(s, sfx_smach)
						A_SMSquake(t, 54<<16, 4, 1200)
						t.hudboost = 1
						P_RadiusAttack(t, t, 480*t.scale)
						local ghs = P_SpawnMobj(t.x, t.y, t.z, MT_RADIALBOOST)
						ghs.target = s
					end
					s.smssoundplayed2 = true
				end
				doanimation = true		
			elseif (p.cmd.buttons & BT_SPIN) --Slower?
			
				if not p.hypermysticsonic
					local ghs = P_SpawnGhostMobj(t)
					ghs.frame = t.frame
					ghs.tics = 3
				end
				if not s.smssoundplayed
					S_StartSound(s, sfx_zoom)
					s.smssoundplayed = true
				end
				doanimation = true
			end
			
			--No animation? Then reset!
			
			if doanimation == false
			
				if (p.pflags & PF_JUMPED) --Don't be in jumping state!
					p.pflags = $ & ~PF_JUMPED & ~PF_STARTJUMP & ~PF_SPINNING
				end
				if p.panim == PA_ROLL or (t.state == S_PLAY_ROLL) --Reset animation!
					t.state = S_PLAY_WALK
					p.pflags = $ & ~PF_JUMPED & ~PF_STARTJUMP & ~PF_SPINNING
				end
				s.smssoundplayed2 = false
				s.smssoundplayed = false
				
			else -- Do one of the boost animations!
			
			 	P_Thrust(s, t.angle, 105355)
				
			 	--Don't disappear under lava too easily when boosting!
				if (s.eflags & MFE_UNDERWATER)
					if (s.momz*P_MobjFlip(s) < 0)
						s.momz = $/8
					end
					if (t.momz*P_MobjFlip(s) < 0)
						t.momz = $/8
					end
					P_SetObjectMomZ(s, FRACUNIT*2, true)
					P_SetObjectMomZ(t, FRACUNIT*2, true)
				end	
				if p.hypermysticsonic
					local ghs = P_SpawnGhostMobj(t)
					ghs.sprite = SPR_HMSO
					if leveltime % 2 == 0
						ghs.frame = ($ & ~FF_FRAMEMASK)|C
					else
						ghs.frame = ($ & ~FF_FRAMEMASK)|L
					end
					ghs.tics = 3
				else
					local ghs = P_SpawnGhostMobj(t)
					ghs.frame = t.frame
					ghs.tics = 3
				end
				if (t.tics > 1)
					t.tics = 1
				end
				if (t.anim_duration > 1)
					t.anim_duration = 1
				end
			end
			
			--You can't carry the same rollout ball you're riding!
			if p.smspickup and p.smspickup.valid and (p.smspickup == s)
				P_DoPlayerPain(p)
				return --
			end
			
		end
	end
end, MT_ROLLOUTROCK)
addHook("MobjRemoved", function(s) //Prevent a sigsev.
	if s.tracer and s.tracer.player and (s.tracer.player.powers[pw_carry] == CR_ROLLOUT)
		s.tracer.player.powers[pw_carry] = 0
		s.tracer = nil
		s.flags = $|MF_PUSHABLE
	end
end, MT_ROLLOUTROCK)
addHook("MobjDeath", function(s)
	if s.tracer and s.tracer.player and (s.tracer.player.powers[pw_carry] == CR_ROLLOUT)
		s.tracer.player.powers[pw_carry] = 0
		s.tracer = nil
		s.flags = $|MF_PUSHABLE
	end
end, MT_ROLLOUTROCK)

//Speed up minecarts with SMS too.
addHook("MobjThinker", function(s)
	if s.target and s.target.valid and s.target.player and s.health	and s.target.player.powers[pw_carry] == CR_MINECART
		local t = s.target local p = t.player
	
		t.smspickupCD = 8
		t.smpktarget = nil
		s.gottoexist = s.gottoexist and $+1 or 1
		p.speed = abs(s.momx)+abs(s.momy)+(4*s.scale) --Update speed to make some actual sense.
		
		if t.skin == "sms" --SMS exclusive abilities.	
				--Setup!
				local MOMANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momy, s.y+s.momy)
				t.SMS_MineCarting = 2
				s.DoCartBoost = 0
				s.CurrentAngle = s.angle
				if (p.cmd.buttons & BT_CUSTOM1)
					s.DoCartBoost = 10*t.scale
				end
			
				--Exit cart?
				if (p.cmd.buttons & BT_SPIN) and ((p.sms_spinhold == 1) or not p.sms_spinhold)
				and (s.gottoexist > 40)			
					p.powers[pw_carry] = 0
					SMS_WallJumpDelete(t, true)
					t.SMS_MineCarting = nil
					t.SMS_wallwait = 8
					p.pflags = $|PF_JUMPED|PF_JUMPDOWN & ~(PF_SPINNING|PF_STARTDASH|PF_NOJUMPDAMAGE)
					t.momx,t.momy = s.momx,s.momy
					s.gottoexist = 1
					P_DoJump(p, true)
					P_SetObjectMomZ(t, 8<<16, true)
					s.target = nil
					return -- Destroyed the minecart!	
					
				elseif s.SKIPNEXTBOOST --SMS is hanging on for dear life. Timeout!
				or (p.sms_c1 > FRACUNIT*99)
				or (s.eflags & MFE_JUSTHITFLOOR) and (p.cmd.buttons & BT_CUSTOM1)		
					if s.SKIPNEXTBOOST
						s.SKIPNEXTBOOST = max($-1,0)
					end
					p.sms_c1 = FRACUNIT*99
					return --
					
				elseif P_IsObjectOnGround(s)
				and (s.gottoexist == 1 or s.CurrentAngle == s.LastAngle2)
					s.EX,s.EY,s.EZ = s.x,s.y,s.z --Emergency teleport spots.
					s.Eangle = s.angle
				end
				
				--STARTING BOOST GFX	
				if (p.cmd.buttons & BT_CUSTOM1)
				and ((p.sms_c1 == 0 or p.sms_c1 == 1) and P_IsObjectOnGround(s) or p.hypermysticsonic)
				and not (s.eflags & MFE_JUSTHITFLOOR)
					if P_IsObjectOnGround(s)
					or p.hypermysticsonic --HMS can boost in the air!				
						A_SMSquake(t, (p.hypermysticsonic) and 80<<16 or 42<<16, (p.hypermysticsonic) and 5 or 2, 600, true)
						s.gottoexist = $+21
					end				
					p.sms_c1 = 850
					
					--A better name would've been s.CartDashSTART, but I'm keeping backwards compatibility in mind...
					if not s.smssoundplayed
					
						if p.hypermysticsonic
							t.hudboost = 1
							S_StartSound(s, sfx_smach)
							S_StartSound(s, sfx_bkpoof)
							P_NukeEnemies(t, t, 577*s.scale)
							P_FlashPal(p, PAL_MIXUP, 3)				
						end						
						if SMS_Graphix(t) != 2
						local COLOR = SKINCOLOR_SUPERSKY5
							if p.hypermysticsonic 
								A_SMSSTOMPSMOKE(t, 30, MT_HMSLIGHTNING1, nil, nil, nil, SKINCOLOR_SKY)
								for i = 0,2
									if i == 0
										local ghs = HMS_RollWaves(t, nil, 17, SKINCOLOR_SUPERGOLD5, FRACUNIT-11000, nil, t)
										ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS30
										ghs.scalespeed = $+(FRACUNIT/9)
									else
										local ghs = HMS_RollWaves(t, nil, 16, SKINCOLOR_SUPERSKY5, FRACUNIT-11000)
										ghs.momx = $+s.momx
										ghs.momy = $+s.momy
										ghs.momz = $+s.momz
										ghs.frame = $+FF_TRANS20
										ghs.sttroll = $*2
									end
								end								
							end
							
							local ghs = SMS_FootSweepSlash(t, p.hypermysticsonic and FF_TRANS10 or FF_TRANS80, MOMANGLE+(leveltime*ANG30))
							if ghs and ghs.valid
								ghs.color = p.hypermysticsonic and $ or SKINCOLOR_WHITE
							end
								
							local oldmomx,oldmomy = t.momx,t.momy
							t.momx,t.momy = s.momx*12,s.momy*12
							t.SMS_DLtrail = {timer = 3, colorized = true, COLORT_trailmax = 8,
							COLORT_trail = {SKINCOLOR_MYSTICAL4, SKINCOLOR_SMS4R, SKINCOLOR_FLAME, SKINCOLOR_RED, SKINCOLOR_SUPERSKY5, SKINCOLOR_LIME, SKINCOLOR_WHITE, SKINCOLOR_MYSTICAL2} }
							SMS_ContinuousTrail(t, t.SMS_DLtrail)
							t.momx,t.momy = oldmomx,oldmomy
		
							COLOR = t.SMS_DLtrail.COLORT_trail[SMSLocalRNG(1, 8,s)]								

							ghs = SMS_FollowCircle(t, COLOR+2, 2)
							ghs.scalespeed = $*3 ghs.fuse = $/2
							
							ghs = HMS_BigBangVortex(t, 13, nil, nil, p.hypermysticsonic and T|FF_TRANS20 or T|FF_TRANS50, COLOR)
							ghs.tracer = t
							
							SMS_DustLayers(t, false, MOMANGLE)
						end
						
						P_SpawnMobjFromMobj(t, 
						s.momx+FixedMul(40*s.scale, cos(s.angle)),
						s.momy+FixedMul(40*s.scale, sin(s.angle)),
						0, MT_BURSTWAVE)
						
						SMS_QuickDustGFX(t)					
						S_StartSoundAtVolume(s, sfx_sarb,86)
						S_StartSound(s, sfx_zoom)
						s.smssoundplayed = true
					end					
				elseif s.smssoundplayed and ((s.eflags & MFE_JUSTHITFLOOR) or not (p.cmd.buttons & BT_CUSTOM1))
				
					s.smssoundplayed = false --Cancel starting effect.
					s.DoCartBoost = 0				
				end
				
				--Boost gfx, without the actual speed...?
				if (p.cmd.buttons & BT_CUSTOM1)
				and P_IsObjectOnGround(s)
				and not p.hypermysticsonic
					if not s.smssoundplayed3
						S_StartSound(s, sfx_smach)
						A_SMSquake(t, 42<<16, 2, 500, true)
						local ghs = P_SpawnMobjFromMobj(t, s.momx*2, s.momy*2, 0, MT_BURSTWAVE)
						ghs.color = SKINCOLOR_SUPERGOLD3
						s.smssoundplayed3 = true
					end
				else	
					s.smssoundplayed3 = false					
				end
				
				local sANGLE
				--Revert back to old speed when you stop boosting, tho!
				if P_IsObjectOnGround(s)
				
					p.sms_jumphold = 3 --Lost your thok privileges.
					
					if s.DoCartBoost and (p.cmd.buttons & BT_CUSTOM1)
					and not (s.eflags & MFE_JUSTHITFLOOR)

						if (abs(s.momx)+abs(s.momy) < 20*t.scale) --Set a minimum speed.
							local ex,ey = s.x,s.y --Stay in old position, please!
							P_InstaThrust(s, s.angle, 20*t.scale)
							P_SetOrigin(s, ex, ey, s.z) 
						end
						
						sANGLE = s.angle	
						if s.LastAngle2 != nil and s.LastAngle2 != s.CurrentAngle
							local ANGSHIFT, PLUS = SMS_AngleDifference(s.CurrentAngle,s.LastAngle2, 2, ANG20)
							s.angle = $+(ANGSHIFT*PLUS)
						end
						for i = 1,((p.hypermysticsonic) and 26 or 12)
							if not P_TryMove(s, s.x+FixedMul(s.DoCartBoost, cos(sANGLE)), s.y+FixedMul(s.DoCartBoost, sin(sANGLE)), false)
								break --
							else
								p.speed = $+s.DoCartBoost
							end
						end
						SMSWindLines(s, nil, true)
						t.boostpass = true
						t.smsspeedticket = true
						if not p.hypermysticsonic
							t.state = (leveltime % 2) and S_SMSWATERGRIND1 or S_SMSWATERGRIND2
							t.tics = 3 t.anim_duration = 3
						end					
						P_SpawnGhostMobj(s)
						local ghs = SMSRSpawnGhost(t)
						if ghs and ghs.valid
							ghs.colorized = true
							ghs.blendmode = AST_ADD
							ghs.destscale = ghs.scalespeed+1
						end
						
						if (leveltime % 5 == 0)
							ghs = HMS_BigBangVortex(t, 12, nil, nil, p.hypermysticsonic and T|FF_TRANS20 or T|FF_TRANS50, SKINCOLOR_SMS3R)
							ghs.tracer = t
							ghs.GSoffsetX = 28*t.scale
							ghs.GSoffsetY = 28*t.scale
							ghs.GSoffsetZ = -2*t.scale
							ghs.dispoffset = -9
							ghs.sttroll = ANG60+leveltime
								
							if not p.hypermysticsonic
								ghs.spritexscale = $*2/3+SMSLocalRNG(0,9999,ghs)
								ghs.spriteyscale = ghs.spritexscale
							else
								local ghs = SMS_FootSweepSlash(t, FF_TRANS50, MOMANGLE+(leveltime*ANG10))
								ghs.momx = $+s.momx
								ghs.momy = $+s.momy
							end
						end
					elseif s.state == S_SMSWATERGRIND1 or s.state == S_SMSWATERGRIND2
						s.state = S_PLAY_STND
						s.thoktimer = 0
					end			
				else --if NOT P_IsObjectOnGround(s)
					s.angle = t.angle
					if s.thoktimer
						s.thoktimer = $ - ((P_IsObjectOnGround(s) and s.thoktimer) or 1)
						if not s.thoktimer
							if s.state == S_SMSWATERGRIND1 or s.state == S_SMSWATERGRIND2
								s.state = S_PLAY_STND
							end				
						end
					end
				end 				
				
				--Emergency teleport!!
				if sANGLE != nil and s.EZ != nil
					if (s.floorz < s.EZ)
					and s.CurrentAngle != s.LastAngle2
					and not (p.hypermysticsonic and (abs(s.momz) > 6*s.scale))
						if not (s.standingslope and s.standingslope.zdelta)					
							P_SetOrigin(s, s.EX,s.EY,s.EZ) --Oh no...
							s.SKIPNEXTBOOST = 4 --Don't allow anything for 4 frames.
							if s.Eangle != nil
								s.angle = s.Eangle
							else
								s.angle = s.CurrentAngle
							end
							t.angle = s.angle
							p.powers[pw_nocontrol] = max($,2)
							s.LastAngle2 = nil
							s.LastAngle = nil
							s.EX,s.EY,s.EZ = nil
							s.thoktimer = nil
							t.sms_juststomped = 6
							
							local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_SMS_THOK)
							ghs.flags2 = MF2_DONTDRAW ghs.fuse = 24 ghs.tics = 99
							S_StartSoundAtVolume(ghs, sfx_statu1, 240)
							S_StartSoundAtVolume(ghs, sfx_statu2, 180)
							S_StartSound(s, sfx_iceb)
							S_StartSound(t, sfx_shrpsp)
							for d = 1,8
								local ghs = P_SpawnMobjFromMobj(s, 
								FixedMul(50*t.scale, cos(s.angle+ANGLE_45*d)),
								FixedMul(50*t.scale, sin(s.angle+ANGLE_45*d)),
								0,MT_EXPLODE)
								ghs.momx = $+s.momx/3
								ghs.momy = $+s.momy/3
								P_Thrust(ghs, s.angle+(ANGLE_45*d), 16*t.scale)
								P_SetObjectMomZ(ghs, 5<<16)
							end
							return --
						end
					end
				end
				
				--Want to Speed Thok with the minecart? YOU CAN
				if (p.cmd.buttons & BT_JUMP)
				
				and ( p.hypermysticsonic or (p.sms_jumphold < 3))
				 	if p.hypermysticsonic
						P_SetObjectMomZ(s, 9<<16, false) --HMS can just straight up fly with minecarts.						
					elseif not P_IsObjectOnGround(s) --Minecart multi-thok, lets gooooo!
						s.momz = (s.momz > t.scale) and $/2 or -3*t.scale*P_MobjFlip(t)
						P_InstaThrust(s, t.angle, 52*t.scale)
						s.angle = t.angle
						s.thoktimer = 60
							
						S_StartSound(t, sfx_thok)					
						p.thokitem = MT_SMSAURATRAIL
						P_SpawnThokMobj(p)
				 	end	
					p.sms_jumphold = 3
				end				

				--Be in these frames in the minecart!
				if (s.smssoundplayed != nil or s.thoktimer)
				and not p.hypermysticsonic
					t.state = (leveltime % 2) and S_SMSWATERGRIND1 or S_SMSWATERGRIND2
					t.tics = 3 t.anim_duration = 3
				end
				--Keep track of our angles...
				if P_IsObjectOnGround(s)
					if s.LastAngle != nil
						s.LastAngle2 = s.LastAngle
					end
					s.LastAngle = s.angle
				end
				s.DoCartBoost = 0 --For prosperity.
		end
		
	elseif s.gottoexist --Remove info.
	
		s.EX,s.EY,s.EZ = nil
		s.LastAngle = nil
		s.LastAngle2 = nil
		s.DoCartBoost = nil
		s.thoktimer = nil
		s.SKIPNEXTBOOST = nil
		s.smssoundplayed = nil
		s.smssoundplayed3 = nil
		s.gottoexist = nil
		
	end
end, MT_MINECART)

--There'll be no instant deaths.
local function SMS_MineCartDefense(s)
	if s.target and s.target.skin == "sms" and s.target.player
	local t = s.target
	local p = s.target.player
		if (p.powers[pw_carry] == CR_MINECART)
			t.momx = $+s.momx*2
			t.momy = $+s.momy*2
			P_SetObjectMomZ(t, 6<<16, true)
			p.powers[pw_carry] = 0
			if not p.hypermysticsonic
				P_DamageMobj(t, s, s)
				if s.skin == "sms" and s.health
					if t.player.sms_transformstate == 1
						t.player.sms_transformstate = 2
					end
					p.powers[pw_flashing] = max($,105)
					SMS_RagDoll(s, 2)
				end
			end
		end
		s.target = nil --Now remove the target.
	end
end
--addHook("MobjRemoved", SMS_MineCartDefense, MT_MINECART)
addHook("MobjDeath", SMS_MineCartDefense, MT_MINECART)

--Save some rendering performance, since enemy kills are the biggest source of lag.
local function SMS_NoFlicky(s)
	A_FindTarget(s, MT_PLAYER, 0)
	if s.target and s.target.skin == "sms"
		if leveltime
		and not stoppedclock
		and not s.target.smsvsboss
			s.fuse,s.tics = 1,1
			s.shadowscale = 0
			s.flags2 = MF2_DONTDRAW
			return true --
		end
	end
end
for i = 0,35
	addHook("MobjSpawn", SMS_NoFlicky, MT_FLICKY_01+i)
end
addHook("MobjSpawn", SMS_NoFlicky, MT_SCORE)