--=============================MOBJ COLLISSION=======================================
--Hooks related to object collission are setup here. Often object damage/deaths too.
--===================================================================================

addHook("MobjMoveCollide", function(s, v)
	if v and v.valid
		if v.player
			if (gametype == GT_COOP)
			and ((v.player.powers[pw_carry] == CR_PLAYER) or v.player.knucklewaving or v.player.iamcarryingsms)
				return false --
			end
		elseif (v.flags & MF_MONITOR)
			if v.health and s.target
				P_DamageMobj(v, s.target, s.target)
			end
			return false --
		end
	end
end, MT_SMSFIREBLAST)

addHook("MobjMoveCollide", function(s, v)
	if v and v.valid
		if v.player
			if (gametype == GT_COOP)
			and ( (v.player.knucklewaving or v.player.iamcarryingsms or v.hmspickedup)
			or ((v.player.powers[pw_carry] == CR_PLAYER) and v.tracer and s.target and (s.target == v.tracer)) )	
				return false --
			end
		elseif (v.flags & MF_MONITOR)
			if v.health and s.target
				P_DamageMobj(v, s.target, s.target)
			end
			return false --
		end
	end
end, MT_HMSFIREBLAST)

--Don't collide with target.(Player who spawned it.)
addHook("MobjCollide", function(s, v)
	if (s.target == v) return false end --
end, MT_SPINFIRE)
addHook("MobjMoveCollide", function(s, v)
	if (s.target == v) return false end --
end, MT_SPINFIRE)

addHook("MobjMoveCollide", function(s, v)
	if v and (v.flags & MF_MONITOR)
		return false --
	end
end, MT_HMS_NAPALM_FLAMES)

//This prevents spammed fire caused by HMS from clogging the same spot and causing lag.

addHook("MobjCollide", function(s, v)
	if not (v and v.valid) return end
	if (v.type == MT_HMSFLAMEREST)
		if (v.fuse > 255)
			P_InstaThrust(v, R_PointToAngle2(s.x, s.y, v.x, v.y), 20*v.scale)
		else
			v.state = S_NULL
		end
	elseif (v.flags & MF_MONITOR)
		return false --
	elseif (v.flags & MF_ENEMY)
		if (v.flags & MF_SHOOTABLE) and v.health
		and not (s.z > v.z+v.height) and not (v.z > s.z+s.height)
			P_DamageMobj(v, s, s.target or s)
			return false --
		end
	elseif not v.player
		return false --
	end
end, MT_HMSFLAMEREST)

//Atomdive object pops monitors!

addHook("MobjMoveCollide", function(s, v)
	if s.tracer and v and v.health and ((v.flags & MF_MONITOR) and not s.dontbustmonitors)
		if not (s.z-10*FRACUNIT > v.z+v.height)
		and not (v.z > s.z+s.height)
			P_DamageMobj(v, s.tracer, s.tracer)
		end
	end
end, MT_ATOMDIVEPOP)

//I proudly present the "STUFF THAT DIES WHEN HMS TOUCHES IT" script!
//...also cramming random crap in here.

local function HMSCollide(h, v)
	if not h.player
	or h.player.sms_intoship
		return false 
	end
	local p = h.player
	local s = h

	if not (v and v.valid)
		return
	end
	
	if h.battlemodsms and (v.type == MT_FLINGRING)
	and (h.playerhomecharge or p.hometrickanim)
		return false
	end
	
	if v.player and v.hmspickedup //Pickup!
	and (v.state == S_PLAY_RIDE) and not (p.smspickup and (v == p.smspickup))
	and not ( (v.z > h.z+h.height+(h.height/3)) or (v.z < h.z+((h.height/3)*2)) )
	and not (v.occupiedcarrying and v.occupiedcarrying != h)
		h.hmspickuptarget = v
	end
	
	if h.battlemodsms and h.skin == "sms" and v and v.player and v.health
		if not (h.z > v.z+v.height)
		and not (v.z > h.z+h.height)
			if (p.hypermysticsonic or p.ringburstfly)
				P_DamageMobj(v, h, h)
				return false
			elseif p.homing and (v.player.charability == CA_GLIDEANDCLIMB) and P_IsObjectOnGround(v)
				v.player.pflags = $|PF_SPINNING
				return
			elseif v.skin == "fang" and P_IsObjectOnGround(v) and (p.pflags & (PF_SPINNING|PF_JUMPED))
			and not (v.player.panim == PA_ABILITY2) and not (v.player.panim == PA_RUN)
				v.player.pflags = $|PF_SPINNING
				return
			end
		end
	end	
	if not (p.hypermysticsonic or p.ringburstfly)
	or h.starthmscombo
		return
	end
	if ((h.state == S_HMSCOMETHAYMAKER2) or (h.state == S_HMSCOMETHAYMAKER1))
		return false
	end
	--Melt shields like they don't exist.
	if (v.type == MT_EGGSHIELD)
		if not (h.z > v.z+v.height)
		and not (v.z > h.z+h.height)
			if v.health
				P_KillMobj(v, h, h)
			end
			return false
		end
	
	--Maceball destruction!
	elseif (v.type == MT_SMALLMACE)
	or (v.type == MT_BIGMACE)
	or (v.type == MT_EGGMOBILE4_MACE)
	or (v.type == MT_SPIKEBALL)
		if not (h.z > v.z+v.height)
		and not (v.z > h.z+h.height)
			S_StartSoundAtVolume(v, sfx_brakrx, 125)
			v.flags = $|MF_NOCLIPTHING & ~(MF_SOLID|MF_PAIN)
			v.flags2 = $|MF2_DONTDRAW
			for i = 0,1
				for d = 1,4
					local ghs = P_SpawnMobjFromMobj(v,FixedMul(55*s.scale, cos(s.angle+ANGLE_45*i+ANGLE_90*d)), FixedMul(55*s.scale, sin(s.angle+ANGLE_45*i+ANGLE_90*d)), (-60*s.scale)+60*s.scale*i, MT_BOSSEXPLODE)
					if ghs and ghs.valid
						ghs.blendmode = AST_ADD
						ghs.spriteyscale = $*2
						ghs.spritexscale = $*2
						ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS20
						P_SetObjectMomZ(ghs, FRACUNIT*3, true)
					end
				end
			end
			S_StartSound(v, sfx_s3kb4)
			S_StartSound(v, sfx_kc40)
			S_StartSound(v, sfx_bedeen)
			
			for d = 0,14
				local debris = P_SpawnMobj(s.x+(P_RandomRange(-50,50)*s.scale), s.y+(P_RandomRange(-50,50)*s.scale), s.z+(P_RandomRange(5,80)*s.scale), MT_BOSSJUNK)
				if debris and debris.valid
					debris.sprite = SPR_USPK
					debris.frame = A
					debris.spritexscale = $*2
					debris.spriteyscale = $*2
					debris.rollangle = SMSLocalRNG(0,359,debris)*ANG1
					debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
					debris.scale = $*2/SMSLocalRNG(1,4,debris)
					debris.color = SMSLocalRNG(SKINCOLOR_CARBON, SKINCOLOR_SILVER, debris)
					debris.colorized = true
					P_InstaThrust(debris, s.angle+(SMSLocalRNG(-55,55,debris)*ANG1), SMSLocalRNG(10,30,debris)*s.scale)
					P_SetObjectMomZ(debris, SMSLocalRNG(5,16,debris)*s.scale, false)
				end
			end			
			A_SMSquake(h, 61*FRACUNIT, 5, 1340, true)
			return false
		end
		
	elseif (v.type == MT_TURRET) or (v.type == MT_EGGROBO1) or (v.type == MT_SUSPICIOUSFACESTABBERSTATUE) or (v.type == MT_KOOPA)
	or (v.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER)
	or (v.flags & (MF_ENEMY|MF_BOSS)) //Specifically things with these flags die too.
			if not (h.z > v.z+v.height)
			and not (v.z > h.z+h.height)
			
			 	if (v.type == MT_EGGROBO1) //Who's slapping now?
					v.state = S_EGGROBO1_PISSED
					v.flags = MF_NOGRAVITY|MF_NOCLIP
					P_SpawnMobjFromMobj(v, 0,0,0, MT_CYBRAKDEMON_VILE_EXPLOSION)
					S_StartSound(h, sfx_brakrx) 
					S_StartSound(h, sfx_bsnipe)
					P_Thrust(v, R_PointToAngle2(h.x, h.y, v.x, v.y), p.speed+7*FRACUNIT)
					v.rollangle = SMSLocalRNG(-39,39,v)*ANG1
					v.momz = 45*FRACUNIT
					v.fuse = 300
					v.fusrodah = 2
					P_SpawnGhostMobj(v)
					return false --
				elseif v.health				
					if (v.type == MT_SUSPICIOUSFACESTABBERSTATUE) --Did you really think you could defeat me, by transforming into a statue?! /endquote
						if not (v.state == S_SUSPICIOUSFACESTABBERSTATUE_BURST2) 
							v.state = S_SUSPICIOUSFACESTABBERSTATUE_BURST2
						end
					elseif p.hypermysticsonic	
						if (v.type == MT_ROSY) //Wait wh-what?! Don't disintegrate Amy!
						or (v.info.mass == 2006)
							return --
						end
						v.flags2 = $ & ~MF2_FRET --Fret not!					
						if (s.state != S_HMSBLACKHOLE)
							S_StartSound(v, sfx_cdfm50)
							v.hmsgodaurad = true
							local ghs = P_SpawnMobjFromMobj(v, 0,0,v.height/3, MT_TNTDUST)
							if ghs and ghs.valid
								ghs.destscale = $*5
								ghs.fuse = 43
								ghs.tics = 1
								S_StartSound(ghs, sfx_kc40)
								P_Thrust(ghs, R_PointToAngle2(v.x, v.y, h.x, h.y), -14*FRACUNIT)
							end
						end
						A_SMSquake(h, 25*FRACUNIT, 3, 999, true)
							
						v.health = 1 	
						P_DamageMobj(v, h, h, 1, DMG_INSTAKILL)
						if v and v.valid and v.health --Still alive somehow? Let's make sure it gets the message.
							P_KillMobj(v, h, h) 
						end
						return false --
					end
				end
			end
	--Destroy cacti explosively.
	elseif (v.type >= MT_CACTI1 and v.type <= MT_CACTISMALLSEG) and (v.flags & MF_PAIN)
	
		if not (v.z > h.z+h.height) and not (v.z+v.height < h.z)
		
			local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_TNTDUST)
			if ghs and ghs.valid
				ghs.destscale = $*99
				ghs.fuse = 73
				ghs.tics = 1
			end
			local rng = SMSLocalRNG(1,4,v)
			v.sprite = SPR_AURA
			v.frame = C
			v.flags = $ & ~(MF_PAIN|MF_SOLID|MF_NOGRAVITY)
			v.scale = $*rng
			if not SMS_Graphix(s)
				local ghs = SMS_SmokeCircle(h, 0, 1, h, FRACUNIT*4)
				P_SetOrigin(ghs, v.x, v.y, v.z)
				ghs.tracer = nil
			end
			if not P_IsObjectOnGround(v)
				v.z = v.floorz
			end
			for i = 2,4
				ghs = P_SpawnMobjFromMobj(v, 0, 0, v.height/i, MT_BOSSEXPLODE)
				if ghs and ghs.valid
					ghs.scale = FRACUNIT*3
					ghs.blendmode = AST_ADD
					ghs.renderflags = RF_FULLBRIGHT
				end
			end
			if not h.readyforonemoreplant
				S_StartSoundAtVolume(v, sfx_brakrx, 153+rng)
			end
			A_SMSquake(h, 65*FRACUNIT, 1+rng, 1400, true)
			h.readyforonemoreplant = 2
		end
		return false
	end
	
	--Don't collide with these at all.
	if (v.type == MT_EGGROBO1) or (v.type == MT_CANARIVORE_GAS)
		return false
	end
	--You're intangible in this state.
	if (h.state == S_HMS_INSTANTWARP1 or h.state == S_HMS_INSTANTWARP2)
		return false
	end
end
addHook("MobjCollide", HMSCollide, MT_PLAYER)
addHook("MobjMoveCollide", HMSCollide, MT_PLAYER)

local function TurretCollide(s, v)
	if v and v.valid and s.health
		if (((v.flags & MF_MISSILE) and v.target and v.target.player and v.target.player.hypermysticsonic) or v.player and v.player.hypermysticsonic)
			if not (s.z > v.z+v.height+66*FRACUNIT)
			and not (v.z > s.z+s.height+66*FRACUNIT)
				P_KillMobj(s, v, v.target)
				return false --
			end
		end
	end
end
addHook("MobjCollide", TurretCollide,  MT_TURRET)
addHook("MobjMoveCollide", TurretCollide,  MT_TURRET)

local function HMSE2Collide(s, v)
	if v and v.valid
	and (((v.flags & MF_MISSILE) and v.target and v.target.player and v.target.player.hypermysticsonic) or v.player and v.player.hypermysticsonic)
	and s.valid and (s.state != S_SUSPICIOUSFACESTABBERSTATUE_BURST2)
		if not (s.z > v.z+v.height+66*FRACUNIT)
		and not (v.z > s.z+s.height+66*FRACUNIT)
			s.state = S_SUSPICIOUSFACESTABBERSTATUE_BURST2
			return false
		end
	end
end
addHook("MobjCollide", HMSE2Collide,  MT_SUSPICIOUSFACESTABBERSTATUE)
addHook("MobjMoveCollide", HMSE2Collide,  MT_SUSPICIOUSFACESTABBERSTATUE)

local function HMSE3Collide(s, v)
	if v and v.valid
	and ( ((v.flags & MF_MISSILE) and v.target and v.target.player and v.target.player.hypermysticsonic) or v.player and v.player.hypermysticsonic )
	and s.health
		if not (s.z > v.z+v.height+66*FRACUNIT)
		and not (v.z > s.z+s.height+66*FRACUNIT)
			P_KillMobj(s, v, v.target)
			return false --
		end
	end
end
addHook("MobjCollide", HMSE3Collide,  MT_CYBRAKDEMON_ELECTRIC_BARRIER)
addHook("MobjMoveCollide", HMSE3Collide,  MT_CYBRAKDEMON_ELECTRIC_BARRIER)

local function FLAGSMSCollide(s, v)
	if v and v.player and v.skin == "sms"
		if v.player.hypermysticsonic
			return false --No flag.
		elseif not v.smsflagrg or (v.smsflagrg < 16)
			v.smsflagrgpermission = 7
			if v.flagsmssfx --Flag pick up GFX.
				S_StartSound(v, sfx_cdfm47)
				local ghs = P_SpawnMobj(v.x, v.y, v.z+v.height/2, MT_HMSNOVAWAVE)
				ghs.scale = v.scale/12
				ghs.tics = 2
				ghs.destscale = v.novawave.scalespeed+1
				ghs.color = v.color
				v.flagsmssfx = false
			end
			return false
		end
	end
end
addHook("MobjCollide", FLAGSMSCollide, MT_REDFLAG)
addHook("MobjMoveCollide", FLAGSMSCollide, MT_REDFLAG)
addHook("MobjCollide", FLAGSMSCollide, MT_BLUEFLAG)
addHook("MobjMoveCollide", FLAGSMSCollide, MT_BLUEFLAG)


//Never collide with players, DO collide with enemies and hurt 'em.

local function SparkCollide(s, v)
	if v and v.valid and (v.type != s.type) and not v.player
		if (v.flags & MF_ENEMY)
		and (v.flags & MF_SHOOTABLE)
		and v.health and not (v.flags2 & MF2_FRET) and (v != s.target)
			if not (v.z > s.z+s.height)
			and not (s.z > v.z+v.height)
				s.momx = 0
				s.momy = 0
				P_DamageMobj(v, v, s.target)
			end
		end
	end
	return false
end
addHook("MobjCollide", SparkCollide,  MT_HMSLIGHTNING1)
addHook("MobjMoveCollide", SparkCollide,  MT_HMSLIGHTNING1)

--Kill enemies that get too close.
local function Spark2Collide(s, v)
/*	if v and (v.type == s.type) and (s.ltimer and (s.ltimer > 2)) and v.ltimer
		if not ((v.z > s.z+s.height) and not (s.z > v.z+v.height))	
			if (v.ltimer > s.ltimer) and v.valid
				P_KillMobj(v)
			elseif (s.ltimer > v.ltimer) and s.valid
				P_KillMobj(s)
			end
		end */
	if v and v.valid
	and (v.flags & MF_ENEMY)
	and (v.flags & MF_SHOOTABLE)
		if not (v.z > s.z+s.height)
		and not (s.z > v.z+v.height)	
			if v.health
			and not (v.flags2 & MF2_FRET)
			and (v != s.target)
				P_DamageMobj(v, v, s.target)
				s.flags = $|MF_NOCLIPTHING & ~MF_SOLID
			end
		end
	end
	return false --
end
addHook("MobjCollide", Spark2Collide,  MT_HMSLIGHTNING2)
addHook("MobjMoveCollide", Spark2Collide,  MT_HMSLIGHTNING2)

local function GoreCollide(s, v)
	if v and v.valid and (v.type != MT_SMSECHECK)
		return false
	end
end
addHook("MobjCollide", GoreCollide,  MT_SMSRGORE)
addHook("MobjMoveCollide", GoreCollide,  MT_SMSRGORE)

local function RoboCollide2(h, v)
	if not (v and v.valid)
		return
	end
	if v.player --Tag, you're it
	and (v.player.sms_comethaymaker or (h.state == S_SMSCOMETHAYMAKER2 or h.state == S_SMSCOMETHAYMAKER1))
		return false 
	end
	if not ((v.flags & MF_MISSILE) and v.target and v.target.player and v.target.player.hypermysticsonic)
		return
	end
	if h.health
	and not (h.z > v.z+v.height)
	and not (v.z > h.z+h.height)
	
		h.state = S_EGGROBO1_PISSED
		h.flags = MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT
		
		local ghs = P_SpawnMobjFromMobj(h, 0,0,0, MT_CYBRAKDEMON_VILE_EXPLOSION)
		ghs.destscale = $*99
		
		S_StartSound(h, sfx_brakrx)
		S_StartSound(h, sfx_bsnipe)
		P_Thrust(h, R_PointToAngle2(h.x, h.y, v.x, v.y), 30*FRACUNIT)
		h.momz = 45*FRACUNIT
		h.fuse = 300
		
		ghs = P_SpawnGhostMobj(h)
		ghs.destscale = $*99
		ghs.fuse = 12 ghs.tics = 13
		return
	end
end

addHook("MobjCollide", RoboCollide2, MT_EGGROBO1)
addHook("MobjMoveCollide", RoboCollide2, MT_EGGROBO1)

--Let's merge all the collission hooks so SRB2 has less stuff to run at once.
local function SMSRebornCollide(s, v)
	if not (v and v.valid) --Pls exist.
		return
	end		
	if (s.skin != "sms") --Gotta be SMS from now on.
		return
	end
	if ((s.state == S_SMSCOMETHAYMAKER2) or (s.state == S_HMSCOMETHAYMAKER2))
		if (v.type == MT_FLINGRING)
		and v.cmphflung
		and G_GametypeHasSpectators()
			v.angle = s.angle
			v.cmphflung = 7
			return false
		end
	end	
	
	if not s.player --No fake players from now on!
		return
	end
	local p = s.player
	
	if p.smspickup and (p.smspickup == v)
	or p.sms_comethaymaker and (s.target == v)
	or s.SMS_HomingBolt and (s.SMS_HomingBolt.target == v)
		return false
	end	
	--It's a player...!
	if v.player and v != s
		--Avoiding social contact at all costs
		if p.sms_dodging
			return false
		end
		--Don't get carried when trying to toss people.
		if (v.player.pflags & PF_CANCARRY)
			if (s.state == S_SMSTOSSING) or (s.state == S_SMSTOSSREADY)
				return false
			end
		end
		//COOP move! Put SMS into a crouching toss position! If people get close, toss them backwards over your head!
		if not v.player.powers[pw_carry]		   
		and ( (p.cmd.buttons & BT_TOSSFLAG) or ((p.cmd.buttons & BT_WEAPONPREV) and not p.hypermysticsonic) )
			if  (p.speed < 3*s.scale)
			and P_IsObjectOnGround(s)
			and ((v.type == MT_PLAYER) and not (v.skin == "sms" and ( (v.player.cmd.buttons & BT_TOSSFLAG) or ((v.player.cmd.buttons & BT_WEAPONPREV) and not v.player.hypermysticsonic) )))
			and (s.state != S_SMSTOSSING)
			and (s.state != S_PLAY_GASP)
			and not p.powers[pw_nocontrol]
			and not (p.pflags & PF_SPINNING) 
				s.target = v
				
				if s.target and s.target.valid				
				local st = s.target
				
					if ((st.player.cmd.buttons & BT_CUSTOM3) or (st.player.speed > FRACUNIT))
					
						if not ((s.x > st.x+st.radius+9*FRACUNIT) or (s.x < st.x-st.radius-9*FRACUNIT))
						and not ((s.y > st.y+st.radius+9*FRACUNIT) or (s.y < st.y-st.radius-9*FRACUNIT))
						and not ((s.z > st.z+st.height+9*FRACUNIT) or (s.z < st.z-st.height-9*FRACUNIT))
						and not (st.skin == "sms" and ( (st.player.cmd.buttons & BT_TOSSFLAG) or ((st.player.cmd.buttons & BT_WEAPONPREV) and not st.player.hypermysticsonic) ))
						
							P_SetObjectMomZ(st, 24*FRACUNIT, false)
							P_Thrust(st, s.angle+ ANGLE_180, 23*FRACUNIT)
							S_StartSound(st, sfx_bljp)
							P_SpawnGhostMobj(st)
							st.player.pflags = $|PF_JUMPED
							
							if not (st.player.charflags & SF_NOJUMPSPIN)
								st.state = S_PLAY_ROLL
							else
								st.state = S_PLAY_SPRING
							end
								
							s.state = S_SMSTOSSING
							if p.hypermysticsonic
								s.hmsoframe = F
							end
    					end
    				end		
    			end
			end
		end
	end
	
	--Footsweep and stomps!
	if (p.footsweepanim or p.sms_stompanim)
	and ((v.type == MT_EGGSHIELD) or v.player)
		if not ((s.z+180000 > v.z+v.height) or (s.z+s.height < v.z-180000))
		and s.health
			if not v.player --Egg Shield, then!		
				P_SetObjectMomZ(v, 20*FRACUNIT)
				P_SetObjectMomZ(s, 8*FRACUNIT)
				P_InstaThrust(s, s.angle, -7*FRACUNIT)
				s.state = S_PLAY_FALL
				p.footsweepanim = false
				p.sms_stompanim = 0
				S_StartSound(s, sfx_cdfm36)
				p.powers[pw_flashing] = 14			
				P_KillMobj(v, s, s)			
				return false			
			elseif G_GametypeHasSpectators()
			and not (v.player.ctfteam and (p.ctfteam == v.player.ctfteam))
			and v.health and not v.player.hypermysticsonic 
				P_DamageMobj(v, s, s) --Attack players!
				v.player.weapondelay = 25
				v.player.sms_flamedelay = 25
				
				if p.footsweep03 --Send the player flying!
					P_Thrust(v, v.angle, -7*FRACUNIT)
					P_SetObjectMomZ(v, 7*FRACUNIT, true)
				end					
				return false
			end
		end
	
	--Missile dodging/reflecting behaviour!
	elseif (v.flags & MF_MISSILE)
	
		--HMS reflects all projectiles with a homing vengeance!
		if p.hypermysticsonic
		
			if not ((v.z > s.z+s.height) or (v.z < s.z)) --Height checks...
			and (v.target != s)
			and not v.disappeartimer
			and (v.info.speed != 0) --Starionary projectiles can't be reflected.
			and not (v.type == MT_HMSNOVABLAST) --No way....you can't reflect something like THAT.
			and not (v.type == MT_HMS_NAPALM_FLAMES) 
	
				v.rvgtracker = P_SpawnMobjFromMobj(v, 0,0,0, MT_RVGMISSILE) --Track the projectile and guide it.
				v.rvgtracker.target = v
				v.rvgtracker.tracer = s
				v.boomerangtarget = v.target
				v.disappeartimer = 275 
				v.target = s
		
				S_StartSound(v, sfx_zoom)
		
				v.momx = $*-1 --Reverse projectile momentum!
				v.momy = $*-1
				v.momz = $*-1
				
				--Effects.
				local ghs = P_SpawnMobjFromMobj(v, 0,0,0, MT_BURSTLIGHT)
				ghs.color = SKINCOLOR_YELLOW
				ghs.blendmode = AST_ADD
				ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS|RF_PAPERSPRITE
				ghs.angle = R_PointToAngle2(v.x, v.y, v.x+v.momz, v.y+v.momy)+ANGLE_90				
				if SMS_Graphix(s) != 2
					HMS_BigBangVortex(s)	
					SMS_LightClash(v, SKINCOLOR_RED, v)	
					HMS_RollWaves(s, nil, 8, SKINCOLOR_LAVENDER)
					SMS_QuickDustGFX(v)
				end
				return false
			end
			
		else --Footsweep reflection or dodging?
		
			if p.footsweepanim and ((p.footsweep03 > 3) or (p.footsweep02 > 8))
			and SMS_FootSweepReflect(s, v)
				return false --
			--Phase through objects with your dodge!
			elseif (p.sms_dodging and (p.sms_dodging > 3)) 
			and not p.powers[pw_invulnerability]
				if (v.type != MT_DETON) and s.skin == "sms" --Destroy detons with dodges rather than phasing through.
					return false
				end
			end
		end
	
	--Monitor interaction
	elseif (v.flags & MF_MONITOR)
	
		if (p.sms_comethaymaker and s.battlemodsms)
			return false --Clip through.
		
		--Destroy monitors in these states. (I don't think the PlayerCanDamage hook existed at the time)
		elseif (p.sms_dodging or p.footsweepanim or (s.state == S_SMS_STOMP1 or s.state == S_SMS_STOMP2)
		or p.smsdivetimer or p.smsatomdivestun or (s.state == S_SMS_ATOMLAND1))
			if not (((s.z > v.z+v.height+5*FRACUNIT) and not (s.state == S_SMS_STOMP1 or s.state == S_SMS_STOMP2))
			or ((s.z > v.z+v.height+40*FRACUNIT) and (s.state == S_SMS_STOMP1 or s.state == S_SMS_STOMP2)))
			and not(v.z > s.z+s.height)
				P_KillMobj(v, s, s)
				return false
			elseif (v.player and p.sms_dodging)
				return false
			end
			
		//Bounce off monitors when hometricking
		elseif p.hometrickanim
		and v.health
		
			if not (s.z < v.z)
			and not (s.z > v.z+v.height)
			and not (v.flags & MF_ENEMY)
				p.hometrickanim = false
				p.pflags = $|PF_JUMPED
				s.state = S_PLAY_ROLL
				return false
			end
		end
	
	--Spike collission
	elseif (v.type == MT_SPIKE) or (v.type ==  MT_WALLSPIKE)
	
		//I'd add a speed requirement, but the colission seems to get buggy.
		if (p.pflags & PF_SPINNING)
		or p.powers[pw_sneakers]
		or p.footsweepanim 
		or p.sms_ringburstfly
		or s.smsblastboost1
		or (p.sms_boostlevel > 64)
		or p.hypermysticsonic
			if p.hypermysticsonic or (v.type == MT_WALLSPIKE) //HMS can just fall through
				if (s.z > v.z+v.height+111*FRACUNIT)
				or (v.z > s.z+s.height+111*FRACUNIT)
					return false
				end
				P_KillMobj(v, s, s)
			else
				if not (s.z > v.z+v.height)
				and not (v.z > s.z+s.height)
					P_KillMobj(v, s, s)
				end
			end
			return false
		end
	--Cut a few things out if you've hit a spring.	
	elseif (v.flags & MF_SPRING)
		if p.sms_comethaymaker --Don't hit springs when haymakering.
			return false
		end
		if not (s.z > v.z+v.height)
		and not (v.z > s.z+s.height)
		
			s.smsframejump = 0
			
			--Make it a vertical power spring?
			if v.info.mass
			
				s.SMS_WJpenalty = 0
				s.SMS_wallwait = 2  --Can freely walljump again in a sec!
				s.SMS_lastwallANG = nil
				s.SMS_lastwallNUM = nil
				s.SMS_wallready = 0
				p.sms_didairboost = 0
				
				--Keep homing momentum
				if p.homing
				and not v.info.damage
					if ((p.cmd.buttons & BT_CUSTOM1)
					or (p.autoc1mom and (p.autoc1mom == 1))
					or ((p.autoc1mom and (p.autoc1mom == 2)) and (((p.cmd.forwardmove > 25) and not (p.pflags & PF_ANALOGMODE))
					or ((p.cmd.sidemove or p.cmd.forwardmove) and (p.pflags & PF_ANALOGMODE))))) 		
						p.homing = 0
						p.homingtrickcharge = 0
						s.momx = $*3/4
						s.momy = $*3/4
					end
				
				elseif ( (p.sms_stompanim or p.sms_afterstompthok
				or p.smsatomdivestun or p.smsdivetimer or (s.state == S_SMS_ATOMLAND1) or (s.state == S_SMS_ATOMLAND2) or (s.state == S_HMS_ATOMLAND)
				or (s.sms_juststomped and s.sms_juststomped > 4)
				or (s.stompanimburst and s.stompanimburst > 14))
				
				or (p.hypermysticsonic and s.hmsspinpass) --Freeloaders.
				or (v.info.damage and ((s.state == S_SMS_AIRBOOST) and (p.speed > 40*s.scale) or p.footsweepanim) ) )
					if not (v.type == MT_BUMPER)
					and not (v.type == MT_BALLOON)
						S_StartSound(s, sfx_sprong)
					end
					s.vertstrongspring = true
					S_StartSound(s, sfx_sprong)
				
					if v.info.damage
						s.horstrongspring = true
						A_SMSquake(s, 25*FRACUNIT, 8, 350, true)
					else
						A_SMSquake(s, 55*FRACUNIT, 5, 350, true)
					end
				
					local ghs = P_SpawnMobjFromMobj(v, 0,0,0, MT_HMSNOVAWAVE)
		  			ghs.scale = $/4
		 			ghs.tics = 4
		  			ghs.destscale = ghs.scalespeed+1
		  			ghs.color = v.color
				end
			
			--Horizontal power spring?
			else
				s.SMS_oldspeed = p.speed
				if not s.smsboostimmune
					s.smsboostimmune = 2
				end
				if (p.pflags & PF_SPINNING) and not (p.pflags & PF_JUMPED)
				or (p.sms_boostlevel >= 65)
				or p.footsweepanim 
			
				s.vertstrongspring = true
				s.horstrongspring2 = true
				S_StartSound(s, sfx_sprong)
				A_SMSquake(s, 45*FRACUNIT, 7, 350, true)
				local ghs = P_SpawnMobjFromMobj(v, 0,0,0, MT_HMSNOVAWAVE)
				ghs.rollangle = ANGLE_90
		  		ghs.scale = $/8
		 		ghs.tics = 4
		  		ghs.destscale = ghs.scalespeed+1
		  		ghs.color = v.color
				end
			end
			
			--Remove all these values.
			s.sdodgeoh = 0
			p.smsrdash = 0
			p.lightdashstart = 0
			s.smsrdash = 0
			s.lastldrx = 0
			s.lastldry = 0
			p.smsdivetimer = 0
			p.smsdivefloat = 0
			p.smsdivedelay = 0
			p.smsatomdivestun = nil
			p.smsatomicdivefail = 0
			p.sms_jumphold = 10
			
			p.footsweep01 = 0
			p.footsweep02 = 0
			p.footsweep03 = 0	
			p.sweepanimrecovery = 0
			p.footsweepanim = false
			return --
		end
		
	--Pretty common trend nowadays to let custom characters skip minecart sections
	--Since SMS was the pioneer, lets improve the effects for 2.6+!
	
	elseif (v.type == MT_SALOONDOOR)
	or (v.type == MT_SALOONDOORCENTER)
	
		if not (s.z > v.z+v.height)
		and not (v.z > s.z+s.height)
		and v.health
		and not (v.flags & MF_NOCLIP) --You'd think this wouldn't be necessary, and yet it is.
		
			if (s.state == S_SMS_AIRBOOST)
			or p.footsweepanim or p.hypermysticsonic
			or (p.pflags & (PF_SPINNING|PF_STARTDASH))
			or p.sms_transformstate and p.powers[pw_carry] == CR_MINECART
			
				if p.hypermysticsonic
					S_StartSound(s, sfx_nerf)
					S_StartSound(s, sfx_wbreak)
					local ghs = P_SpawnMobjFromMobj(v, 0,0,0, MT_TNTDUST)
					if ghs and ghs.valid
						ghs.destscale = $*99
						ghs.fuse = 73
						ghs.tics = 1
					end		
					for d = 0,12
						local debris = P_SpawnMobjFromMobj(v, SMSLocalRNG(-50-d,50+d,s)*v.scale, SMSLocalRNG(-50-d,50+d,v)*v.scale, P_RandomRange(5,80)*v.scale, MT_WOODDEBRIS)
						if debris and debris.valid
							debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
							P_InstaThrust(debris, s.angle+(SMSLocalRNG(-20,20,debris)*ANG1), p.speed+8*s.scale)
							P_SetObjectMomZ(debris, SMSLocalRNG(-4,8,debris)*s.scale, false)
							debris.scale = $*2
						end
					end
					for i = 1,4
						local ghs = P_SpawnMobjFromMobj(v, 
						FixedMul(120*FRACUNIT, cos(v.angle+(ANG1*90*i))),
						FixedMul(120*FRACUNIT, sin(v.angle+(ANG1*90*i))),
						5*FRACUNIT*i, MT_BOSSEXPLODE)
						ghs.scale = s.scale*3
						ghs.blendmode = AST_ADD
						if i % 2 == 0
							ghs.flags2 = $|MF2_SHADOW
							ghs.blendmode = AST_ADD
							ghs.scale = s.scale*6
						end
					end
					A_SMSquake(s, 85*FRACUNIT, 6, 1999)
				else
					S_StartSound(s, sfx_wbreak)
					A_SMSquake(s, 75*FRACUNIT, 5, 999)
					for d = 0,12
						local debris = P_SpawnMobjFromMobj(v, P_RandomRange(-50,50)*v.scale, P_RandomRange(-50,50)*v.scale, P_RandomRange(5,80)*v.scale, MT_WOODDEBRIS)
						if debris and debris.valid
							debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
							P_InstaThrust(debris, s.angle+(SMSLocalRNG(-20,20,debris)*ANG1), p.speed+8*s.scale)
							P_SetObjectMomZ(debris, SMSLocalRNG(-4,8,debris)*s.scale, false)
						end
					end
				end
				
				if (v.type == MT_SALOONDOOR)
					v.flags = $|MF_NOCLIP
					P_KillMobj(v, h, h)
				elseif (v.type == MT_SALOONDOORCENTER)
					v.flags = $|MF_NOCLIP
				end		
				return false	
			end
		end
	end
end
addHook("MobjCollide", SMSRebornCollide, MT_PLAYER)
addHook("MobjMoveCollide", SMSRebornCollide, MT_PLAYER)

//A seperate hitbox to go with thrown items, letting you hurt enemies by throwing crap at them.
local function ThrowTrackerCollide(s, v)
	if not (v and v.valid) --Not a valid collission??
	
	or not ((s.smsthrow and s.smsthrowog) or (s.hmsthrow and s.hmsthrowog)) --Not thrown hard enough, ignore.
	
	or not (s.target and s.target.valid) --We're not tracking anything valid!
	or (s.target == v) --This is our own target!
	or s.homingbounce --Already bounced
		return false
	end
	if (v.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR))
	and (v.flags & MF_SHOOTABLE)
	and v.health
		if (s.z > v.z+v.height+30*v.scale) //Grant a little leeway with the vertical colission
		or (s.z+s.height+30*s.scale < v.z)
			return false
		end
		local t = s.target
		if (t.flags & MF_SPRING) //Extra feature for debug mode.
			t.momx = 0
			t.momy = 0
			t.momz = 0
			t.rollangle = 0
			P_InstaThrust(v, t.angle, mobjinfo[t.type].damage)
			P_SetObjectMomZ(v, mobjinfo[t.type].mass, true)
			if mobjinfo[t.type].seestate
				t.state = mobjinfo[t.type].seestate
			end
			if mobjinfo[t.type].painsound
				S_StartSound(t, (mobjinfo[t.type].painsound))
			end
		else
			s.homingbounce = 1
			v.mhomingbounced = 1
			
			local ths,ghs = SMS_QuickDustGFX(t, SKINCOLOR_MYSTICAL1, AST_ADD)
			ghs.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
			ths.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
			ths.scale = $*3
			ghs.scale = $*3
			
			local ghs = SMS_GroundWindBlast(t)
			ghs.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS
			ghs.fuse = 22
			
			local ghs = SMS_GroundWindBlast(t)
			ghs.renderflags = RF_FLOORSPRITE|RF_SEMIBRIGHT|RF_NOCOLORMAPS
			ghs.colorized = true
			ghs.color = SKINCOLOR_SUPERSKY1
			ghs.tracer = t
			ghs.ditchtracer = 3
			
			if not SMS_Graphix(s)
				SMS_SmokeCircle(t)
				local ghs = SMS_DashGFX(t, SKINCOLOR_SILVER)
				ghs.frame = ($ & ~FF_TRANSMASK)|TR_TRANS20
				ghs.rollangle = $+ANGLE_270
				ghs.renderflags = $|RF_SEMIBRIGHT|RF_NOCOLORMAPS & ~RF_PAPERSPRITE
			end
			
			t.z = $+(FRACUNIT*P_MobjFlip(s))
			if (t.momz*P_MobjFlip(s) < FRACUNIT)
				t.momz = $*(FRACUNIT*P_MobjFlip(s))
			end
			
			local SOURCE = (s.smsthrowog) and s.smsthrowog.mo or (s.hmsthrowog) and s.hmsthrowog.mo or t or s
			P_KillMobj(v, t, SOURCE)
			P_RadiusAttack(t, SOURCE, max(80*SOURCE.scale, min(500*FRACUNIT, t.height*3)), 0, true)
			--Nearby players can feel the impact!
			for z in players.iterate
				if z.mo
				local DIST = FixedHypot(z.mo.x-t.x, z.mo.y-t.y)
					if DIST < 330*FRACUNIT
						P_FlashPal(z, PAL_MIXUP, 2)
						A_SMSquake(z.mo, max(75*FRACUNIT, min(20*FRACUNIT, t.height*2)), 10, 1)
					elseif DIST < 1250*FRACUNIT
						A_SMSquake(z.mo, max(40*FRACUNIT, min(10*FRACUNIT, t.height*2)), 5, 1)							
					end
				end
			end			
		end
	end
	return false --Never ACTUALLY collide with anything.
end

addHook("MobjCollide", ThrowTrackerCollide, MT_SMSTHROWTRACKER)
addHook("MobjMoveCollide", ThrowTrackerCollide, MT_SMSTHROWTRACKER)

//Make sure Sonic can only boost himself in NiGHTs.

local function SonicFboost(s, v)
	if (s.nsonicb and v and v != s.nsonicb)
	return false --
	end
end
addHook("MobjCollide", SonicFboost, MT_NIGHTSBUMPER)
addHook("MobjMoveCollide", SonicFboost, MT_NIGHTSBUMPER)

//Collision check for rings and other fun things.
//Can't use iterate through thinkers or the game'll lag in big levels, and blockmaps sucked when I first made this.
local function smsecheck(s, m)

	if not (s.target and s.target.player and m and m.valid) return false end --Target and m must be valid at all times.
	
	local t = s.target --s.target is the player!
	local p = s.target.player
	
		--Not the checkers or their source
		if (m == t) 
		or (m.type == s.type)
		or m.fusrodah --No haymakered enemies.
			return false
		end
		
		if (m.z > s.z+s.height) --Height check.
		or (m.z+m.height < s.z)
			if not s.hmstornado 
				return false
			end
		end
		if s.SMS_reflector --It's a reflector. Woop.
			if m.valid
				if ((m.flags & MF_MISSILE) or (m.type == MT_ENERGYBALL))
					SMS_FootSweepReflect(t, m)
				end
			end	
			return --
		end
		if s.hmsspincheck --Nearby stuff HMS auto-destroys when he's spinning. Also used for HMS godbreaker combo.
		 
		 	if t.starthmscombo --Cancel
				return
			end
			 --Find a method of succeeding!
			local method
			if m.dummyhbx and m.target
				method = 1
			elseif m.player
				if G_GametypeHasSpectators()
					if s.hmsspincheck2
					or ( FixedHypot(FixedHypot(m.x-s.x, m.y-t.y), m.z-t.z) < 600*s.scale )
						method = 2
					end
				end
			elseif (m.flags & MF_MONITOR) and not s.hmsspincheck2
				method = 3
			elseif (m.flags & MF_BOSS) --Infinitely allow attack bosses even if dead.
				method = 4
			else
				if ((m.flags & MF_ENEMY) and m.health)
				or (m.type == MT_EGGGUARD)
				or (m.type == MT_CYBRAKDEMON) 
				or (m.type == MT_MINUS)
					method = 5
				end
			end
			
			if method == nil return end
			
			if s.hmsspincheck2 and not m.dummyhbx
				if (m.type == MT_BIGMINE)
				or (m.health <= 0) 
				or not (m.flags & MF_SHOOTABLE)
					return --These cause lag or other issues.
				end
			end			
			if not s.SMSclosest
			or ( s.SMSclosest and ((FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) < s.SMSclosest) )
				if P_CheckSight(t, m)
					if m.dummyhbx and m.target
						s.SMSclosest = (FixedHypot(FixedHypot(m.target.x-s.x, m.target.y-s.y), m.target.z-s.z))
						if s.hmsspincheck2
							t.hmsspint2 = m.target 	
						else
							t.hmsspint = m.target
						end		
					elseif not m.dummyhbx				
						s.SMSclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
						if s.hmsspincheck2
							t.hmsspint2 = m 
						else
							t.hmsspint = m
						end
					end
				end
			end
			return
			
		 elseif s.lsasms --Lightspeed Attack check.
		 
			local method --Must get a method.
			if m.dummyhbx and m.target
				method = 1
			else
				if (m.type == MT_DETON) or (m.flags2 & MF2_FRET)
					return false --
				elseif (m.type == MT_CYBRAKDEMON) 
				or (m.type == MT_MINUS) 
				or (m.type == MT_METALSONIC_BATTLE)
				or (m.type == MT_EGGGUARD)	
					method = 2
				elseif (m.flags & MF_SHOOTABLE) and m.health
					if (m.flags & (MF_ENEMY|MF_BOSS)) 
					or (m.type == MT_MINUS)
						method = 3
					end		
				end
			end
			
			if method == nil return end
			
			if P_CheckSight(t, m)
				if s.justcheckin
					t.smslsaret = 2
				else
					if not s.SMSclosest
					or ( s.SMSclosest and ((FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) < s.SMSclosest) )
						s.SMSclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
						t.smslsacheck = 3
						t.target = m
						t.lsatarget = m
					end
				end
			end
		elseif s.cmphsms or s.cmphhms --Comet Haymaker check.
		
			--Instant cancel conditions.
			if (m.info.mass == 2006) //Immunity
				return false --
			end
			local method --Must get a method.
			if m.dummyhbx and m.target
				method = 1
			else
				if (m.type == MT_DETON) or (m.flags2 & MF2_FRET)
					return false --
				elseif (m.type == MT_CYBRAKDEMON) 
				or (m.type == MT_MINUS) 
				or (m.type == MT_METALSONIC_BATTLE)
				or (m.type == MT_EGGGUARD)	
					method = 2
				elseif (m.flags & MF_SHOOTABLE) and m.health
					if (m.flags & (MF_ENEMY|MF_BOSS)) 
					or (m.type == MT_MINUS)
						method = 3
					end		
				end
			end
			if method != nil --We continued!	
				if not s.SMSclosest
				or ( s.SMSclosest and ((FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) < s.SMSclosest) )
					if P_CheckSight(t, m)
						if s.justcheckin
							t.smscmphret = 1
							return false
						end
						if not s.SMSclosest
						or ( s.SMSclosest and ((FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) < s.SMSclosest) )
							if m.dummyhbx and m.target // and s.cmphhms --Underground or electrified? Don't care. Punch 'em anyway.
							
							s.SMSclosest = (FixedHypot(FixedHypot(m.target.x-s.x, m.target.y-s.y), m.target.z-s.z))
							t.smshmcheck = 2
							t.target = m.target
							t.cmphtarget = m.target
							
							elseif not m.dummyhbx
							
							s.SMSclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
							t.smshmcheck = 2
							t.target = m 
							t.cmphtarget = m
							end
						end	
					end
				end
			end
			return --
			
		elseif s.smsepickup --Object pickup checker. Note: Player pickup doesn't use this!
		
			if p.smspickup --Already got stuff.
				if p.smspickup.valid
				or (p.smspickup == m)
					return false
				end
			end	
			
			--Now! Find a method to succeed!
			local method
			if ((m.flags & MF_PUSHABLE) or (mobjinfo[m.type].flags & MF_PUSHABLE) ) and (m.flags & MF_SOLID)
				method = 1 --Pushables are all fair game. Even if they don't CURRENTLY have the pushable flag.
			elseif (m.flags & MF_MONITOR)
				if ((m.type >= MT_PITY_GOLDBOX) and (m.type <= MT_THUNDERCOIN_GOLDBOX))
					return false
				end --Can't pick these up, stages might require them.
				method = 2
			elseif (m.type == MT_PLAYER) --Players. Even fake players using the MT_PLAYER object!
				if ( (m.iasmsp != nil and (m.iasmsp > 101)) or not m.player)
				and not m.hmspickedup
					if t.DeyCURollin --They're on a rollout rock.
						return false
					elseif m.player
						if m.player.hypermysticsonic 
						or m.player.powers[pw_carry] 
						or (m.player.pflags & PF_CANCARRY) and not P_IsObjectOnGround(m) --This'll interrupt with their carry status.
					--	or p.bot
							return false -- end
						end
					end
					method = 3 --Pick up even players!
				end				
			elseif (m.type == MT_LITTLETUMBLEWEED)
			or (m.type == MT_BIGTUMBLEWEED)
			or (m.type == MT_ROLLOUTROCK)
			or (m.type == MT_GARGOYLE) --Gargoyles are always pickup-able, pushable or not.
			or (m.type == MT_CYBRAKDEMON_NAPALM_BOMB_LARGE)
			or (m.type == MT_CYBRAKDEMON_NAPALM_BOMB_SMALL)
			or (m.type == MT_SHELL)
				method = 4
			elseif (m.type == MT_MINECART)
				if m.target and m.target.player --Can pick up moving minecarts!
					method = 5
				end	
			elseif m.smscustompickup or m.carthrust --Custom objects?
				method = 6
			elseif p.glitchydebugthrow --Glitchy debugthrow lets you pick up anything. Console command is "GlItChYdEbUgThRoW"
				method = 7
			end
			
			if method == nil return false end
			
			--Final checks!
			if (m.health or method == 4 or method == 5)
			and not p.powers[pw_carry]
			and not t.iampickedupbysmshelp --
			and P_CheckSight(t, m)
				if (p.sms_flamedelay <= 2) --Stop SMS from firing when he's in front of a grabbable object.
				and not (t.state == S_HMSNOVAFIRE2)
				and not (p.cmd.buttons & BT_FIRENORMAL)
			  		p.sms_flamedelay = 2
			  	end
			  	if (p.hms_gravburstcd == nil or p.hms_gravburstcd <= 2)
			 	 	p.hms_gravburstcd = 2
			  	end			
				if s.justcheckin
					t.smspkuret = 1
			  	else
			  		if not s.SMSclosest or ( s.SMSclosest and ((FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)) < s.SMSclosest) )
						s.SMSclosest = (FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z))
						t.smpktarget = m
						if (p.sms_flamedelay <= 2)
			  				p.sms_flamedelay = 15
			  			end
			  			if (p.hms_gravburstcd == nil or p.hms_gravburstcd <= 2)
			 	 			p.hms_gravburstcd = 15
			  			end
						p.sms_nflamehold = 10
						p.sms_flamebuttonhold = 10
						p.sms_tossflaghold = 10
						p.weaponprevhold = 10
					end
			  	end
			end
			return false
			
		elseif s.hmstornado --Pretty simple code all things considered for a tornado.
		
			if not ( (m.flags & (MF_ENEMY|MF_BOSS)) or (m.player and not m.player.hypermysticsonic) or (m.type == MT_METALSONIC_BATTLE) or (m.type == MT_EGGGUARD) or (m.type == MT_SMSDUMMYTRACKER)
			or (m.type == MT_SMSRGORE) or ((m.type >= MT_FLICKY_01) and (m.type < MT_SEED)) or (m.type == MT_DETON) or (m.type == MT_ROSY) or (m.type == MT_CYBRAKDEMON) or (m.type == MT_MINUS) )
				return false
			end
			if m.player 
				if m.health and (FixedHypot(t.x-m.x, t.y-m.y) < 1200*FRACUNIT)
					local z = m.player
					m.momz = $+76000
					if z.panim == PA_WALK or z.panim == PA_IDLE or z.panim == PA_RUN
						z.pflags = $|PF_JUMPED 
						if not m.hmstorvictim //Help! We're getting sucked in!
							S_StartSound(m, sfx_s3kcel)
							m.momx = $/2
							m.momy = $/2
							m.hmstorvictimang = z.drawangle
							P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), 6*FRACUNIT)
						end
						if (z.charflags & SF_NOJUMPSPIN)
							m.state = S_PLAY_SPRING
						else
							m.state = S_PLAY_ROLL
						end
					end
					if m.z < m.ceilingz-300*FRACUNIT
						m.momx = $*100/99
						m.momy = $*100/99
						P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), 10*FRACUNIT) //Suck in
					end
					P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y)-ANGLE_90+ANG2, 85000) //Tornadoooo
					m.hmstorvictim = 7
				end
				return false
							
			elseif (m.type == MT_SMSDUMMYTRACKER)
			
				if m.target and m.target.valid
					m.target.momz = $+4*FRACUNIT
					if (m.target.z < m.target.ceilingz-300*FRACUNIT)
						P_Thrust(m, R_PointToAngle2(m.target.x, m.target.y, t.x, t.y), 36*FRACUNIT) //Suck in
					else
						P_Thrust(m, R_PointToAngle2(m.target.x, m.target.y, t.x, t.y), -8*FRACUNIT) //Shoot out
					end
					P_Thrust(m, R_PointToAngle2(m.target.x, m.target.y, t.x, t.y)-ANGLE_90+ANG2, 12*FRACUNIT) //Tornadoooo
					m.target.angle = $+ANGLE_45
				end
				
			else
			
				m.momz = $+4*FRACUNIT
				if (m.z < m.ceilingz-300*FRACUNIT)
					P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), 36*FRACUNIT) //Suck in
				else
					P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), -8*FRACUNIT) //Shoot out
				end
				P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y)-ANGLE_90+ANG2, 12*FRACUNIT) //Tornadoooo
				m.angle = $+ANGLE_45
			end
			return false
			
		else --Standard Lightdash Checker!
		
			if ((m.type == MT_RING) or (m.type == MT_COIN) or (m.type == MT_BLUESPHERE)) --Not flingrings.
			and m.health and not (p.smspickup and (p.smspickup == m)) 
				if not P_CheckSight(t, m)
					return false
				end
				if s.justcheckin
					t.smsldret = 1
				else
					if not s.SMSclosest or ( s.SMSclosest and ( (FixedHypot(m.x-s.x, m.y-s.y)) <= s.SMSclosest) )
						s.SMSclosest = (FixedHypot(m.x-s.x, m.y-s.y))
						t.target = m
						t.smsrdash = m
					end
				end
			end
		end
		return false
end

addHook("MobjCollide", smsecheck, MT_SMSECHECK)
addHook("MobjMoveCollide", smsecheck, MT_SMSECHECK)

//Kick the TNT barrels!
addHook("MobjThinker", function(s)
	if (s.flags & MF_NOCLIPTHING) or (s.flags2 & MF2_DEBRIS) --We ded.
		s.tntdelayexp = nil
		s.YOUKICKEDME = nil
		s.KickedTimer = nil
	else
		if s.tntdelayexp
			s.tntdelayexp = $-1
			if s.tntdelayexp == 1
				s.flags = MF_MISSILE|MF_NOBLOCKMAP
				s.momz = $-1
				s.momy = $-1
				s.momx = $-1
			end
		end
		if s.KickedTimer
			s.KickedTimer = $-1
			if not s.KickedTimer
				s.YOUKICKEDME = nil
			end
		end
	end
end, MT_TNTBARREL)

--Process of sending barrel flying...
local function SMS_FlyTNTBarrel(s, so)
	P_SetObjectMomZ(s, 12*FRACUNIT, false) --Up...
	
	local ANGLE = R_PointToAngle2(so.x, so.y, s.x, s.y)
	local SPEED = 15*so.scale
	if so.player and (so.player.speed >= 16*so.scale)
		SPEED = so.player.speed*5/4
	end
	
	P_Thrust(s, ANGLE, SPEED) --...and awaaay!
	
	if (s.state != S_TNTBARREL_FLYING)	
		S_StartSound(s, sfx_bowl)
		s.state = S_TNTBARREL_FLYING
	end
	
	s.YOUKICKEDME = so --Don't hurt the one who flung you for a bit.
	s.KickedTimer = 18 --Timer before it runs out.
	s.health = 1
	s.tntdelayexp = 4 --Spend at least 4 tics flying before potentially exploding again.
	s.flags = MF_NOBLOCKMAP
	
	if so.player and so.player.homing
		local p = so.player
		p.homing = 0
		so.momx = 0 so.momy = 0
		so.state = S_PLAY_SPRING
		p.pflags = $|PF_NOJUMPDAMAGE
		P_SetObjectMomZ(so, 12*FRACUNIT, false)
		P_InstaThrust(so, ANGLE, -14*so.scale)
		SMS_HomingTrick(so,p)
	end
	return true --
end
local function TNTSMSdeath(s, i, so)
	if i
		if (i.type == MT_SMMK) --Don't die to Footsweep/Stomp AoE effects!
			return true --
		elseif so and (so.skin == "sms") and so.player and (so == i)
			local p = so.player
			if (p.footsweepanim or so.SMS_HomingBolt or p.homing or p.hypermysticsonic) and s.valid
				SMS_FlyTNTBarrel(s, so)
				return true --
			end
		end
	end
end
addHook("MobjDamage", TNTSMSdeath, MT_TNTBARREL)
addHook("MobjDeath", TNTSMSdeath, MT_TNTBARREL)

--"SMSdebris", function (s, v, total, sprite, frame)

//SMS dodges through you

local function ShieldNopeS(s, so)
	if so and so.player and (so.player.sms_dodging and so.player.sms_dodging > 3)
		return true --
	end
end
local function ShieldNope(s, so)
	if so and so.player and (so.player.sms_dodging and so.player.sms_dodging > 3)
		return false -- False instead of true!!
	end
end

addHook("TouchSpecial", ShieldNopeS,  MT_EGGSHIELD)
addHook("MobjCollide", ShieldNope,  MT_EGGSHIELD)

//Of course, all of HMS' projectiles murder stuff too, so let's get that working.

addHook("MobjMoveCollide", function(s, v)
	if not (v and v.type != s.type and s.target != v) return end --
	
	if v.player
		local tp = v.player	
		--Don't collide with the hitbox of players you're carrying in any ways.
		if (tp.powers[pw_carry] == CR_PLAYER)
		and v.tracer and (s.target == v.tracer)			
			return false --
		elseif s.target and s.target.player
		and s.target.player.smspickup and s.target.player.smspickup == v
			return false --
		elseif (gametype == GT_COOP)
		and (tp.knucklewaving or tp.iamcarryingsms)
			return false --
		end
		
	elseif (v.type == MT_TURRET) --Special behaviour.
	or (v.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER)
	or (v.type == MT_EGGSHIELD)
	
		if (s.z > v.z+v.height) --Height checks.
		or (v.z > s.z+s.height)
			return --
		end
		if v.health and (v.info.mass != 2006)
			P_KillMobj(v, s, (s.target) or s) --Your target is the source!
		end
	end
end, MT_HMSFIREBLAST)

local function NovaCollide(s, v)
	if not (v and v.valid and s.target != v) --Step 1. Exist. 
		return  --
	end
	if v.player
	local tp = v.player	
	local DORETURN
		
		//"you're going STRAIGHT TO HELL!" --Shadow the Philosopher: 2005
		if G_GametypeHasSpectators()
		and (tp.powers[pw_invulnerability] > 2)
		and not tp.hypermysticsonic // Unstoppable Force vs Immovable Object
		
			--Not against teammates, though!
			if (s.type == MT_HMSNOVABLAST) 
				if not ( (s.target and s.target.player and s.target.player.ctfteam) and (s.target.player.ctfteam == tp.ctfteam) )
					tp.powers[pw_invulnerability] = 2 //Invincibility's merely relative.
					DORETURN = true				
				end
			elseif (s.type == MT_HMSNOVABLASTHB)
				if not ( (s.hmstarget and s.hmstarget.player and s.hmstarget.player.ctfteam) and (s.hmstarget.player.ctfteam == tp.ctfteam) )
					tp.powers[pw_invulnerability] = 2
					DORETURN = true
				end
			end
			
		end
		
		if (tp.knucklewaving
		or (tp.powers[pw_carry] == CR_PLAYER) and v.tracer and (s.target == v.tracer))
		and gametype == GT_COOP
			return false --
		elseif (s.type == MT_HMSNOVABLASTHB)
		and s.hmstarget and s.hmstarget.battlemodsms
		and not ( (s.hmstarget.player and s.hmstarget.player.ctfteam) and (s.hmstarget.player.ctfteam == tp.ctfteam) )
			if not (s.z > v.z+v.height)
			and not (v.z > s.z+s.height) 
				P_DamageMobj(v, s.hmstarget, s.hmstarget) return false --
			end
		end
		
		if DORETURN return false end --
		
	elseif not (s.z > v.z+v.height) --Height check
	and not (v.z > s.z+s.height)
	
		if (v.flags & (MF_ENEMY|MF_BOSS))
		or (v.type == MT_TURRET)
		or (v.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER)
		or (v.type == MT_EGGSHIELD)
		
			if v.health and (v.info.mass != 2006)
				if (s.type == MT_HMSNOVABLASTHB)
					P_KillMobj(v, s.novatarget, s.hmstarget)				
				else
					P_KillMobj(v, s, (s.target and s.target.valid) and s.target or s)
				end
			elseif (s.type == MT_HMSNOVABLASTHB)
			and s.novatarget and s.novatarget.valid and (s.novatarget.state == S_NOVABLAST)
				P_ExplodeMissile(s.novatarget)
				return --
			end
		end
	end
end
addHook("MobjCollide", NovaCollide, MT_HMSNOVABLASTHB)
addHook("MobjMoveCollide", NovaCollide, MT_HMSNOVABLASTHB) --HB is hitbox. Seperate object from the NovaBlast itself.
addHook("MobjMoveCollide", NovaCollide, MT_HMSNOVABLAST)

//Like the Novablast, HMS' Galaxy Storm has an invisible hitbox following it to avoid walls stopping it.

local function GaxCollide(s, v)
	if not (v and v.valid)
	or (v.type == MT_HMSGAXHB)
	or v.hmsgaxed
	or (s.hmstarget == v)
	or v.player and (gametype == GT_COOP)
		return false --Don't bother
	end
	if not (((s.z-500*FRACUNIT > v.z+v.height) and s.gaxhbphase2) or (s.z > v.z+v.height) and not s.gaxhbphase2)
	and not (v.z > s.z+s.height)
	
		if (v.flags & MF_ENEMY)
		or (v.flags & MF_BOSS)
		or (v.type == MT_TURRET)
		or (v.type == MT_CYBRAKDEMON_ELECTRIC_BARRIER)
		or (v.type == MT_EGGSHIELD)
		
		or ( v.player and ( G_GametypeHasSpectators() or v.battlemodsms)
		and not v.player.hypermysticsonic and not v.player.powers[pw_flashing] )
		
			if (v.info.mass != 2006)
			and (v.health or (v.flags & MF_BOSS))
			
				if s.gaxhbphase2
					s.tsp = P_SpawnMobj(v.x, v.y, v.z, MT_HMSGAXT)
					s.tsp.oggax = s.novatarget
					s.tsp.hmstarget = s.hmstarget
					s.tsp.gaxedboi = v
					v.hmsgaxed = true //You got gaxed
					return false --
					
				else //Hacky way of dealing damage that won't cause sigsevs like calling P_DamageMobj in a collission hook can.
				
					if s.hmstarget
						P_NukeEnemies(s, s.hmstarget, 400*FRACUNIT)
					else
						P_NukeEnemies(s, s, 400*FRACUNIT)
					end
					if v.player
						P_InstaThrust(v, v.angle, -80*FRACUNIT)
						return false --
					end
				end
			end
		end
	end
	return false --
end
addHook("MobjCollide", GaxCollide, MT_HMSGAXHB)
addHook("MobjMoveCollide", GaxCollide, MT_HMSGAXHB)

local function BalloonCollide(s, so)
	if s.health and (s.state != s.info.deathstate)
		if so and so.player
			if so.skin == "sms" and not so.player.hypermysticsonic
			and (so.z <= s.z + s.height)
			and (s.z <= so.z + so.height)
			and so.health
				so.rollangle = 0
				so.state = S_PLAY_ROLL
				so.player.homingtrickcharge = 4
				so.forcesmstrick = true
			end
		end
	end
end
addHook("MobjCollide", BalloonCollide, MT_BALLOON)
addHook("MobjMoveCollide", BalloonCollide, MT_BALLOON)

local function SmokeCollide(s, v)
	if not (v and v.valid and v.health)
	or (s.target == v)
		return false --
	end
	if (v.type == MT_SPIKE) or (v.type == MT_WALLSPIKE)
		if not (s.z > v.z+v.height+20*FRACUNIT)
		and not (s.z+s.height+20*FRACUNIT < v.z)
			P_KillMobj(v, s, s.target)
			return --
		end
	elseif not ((s.z > v.z+v.height) or (s.z+s.height < v.z))
		if (v.flags & (MF_ENEMY|MF_BOSS))
			if not (v.type == MT_TNTBARREL)
				P_DamageMobj(v, s, s.target)
				return --
			end
		elseif v.player and G_GametypeHasSpectators()
		and not (G_GametypeHasTeams() and (v.player.ctfteam and (s.target.player.ctfteam == v.player.ctfteam)))
			if not v.player.hypermysticsonic and not v.player.powers[pw_super]
			and not (s.smssmmk and ((s.state == S_SMMK5) or (s.state == S_SMMK4) or (s.state == S_SMMK3)))
				if s.target.battlemodsms
					P_DamageMobj(v, s.target, s.target) //Direct damage since battlemod will try to say otherwise.
				else
					P_DamageMobj(v, s, s.target)
				end
				v.player.weapondelay = 25
				v.player.sms_flamedelay = 25
				return --
			elseif not v.player.hypermysticsonic and not v.player.powers[pw_super] and not s.smssmmk //No special knockback for stomps.
				P_InstaThrust(v, v.angle, -20*FRACUNIT)
				P_SetObjectMomZ(v, 8*FRACUNIT)
				v.player.pflags = $|PF_JUMPED //Let's not kill Super players by kicking them off cliffs...
				return --
			end
		elseif (v.flags & MF_MONITOR) 
			if not s.smssmmk and not (v.type == MT_EGGMAN_BOX) and not (v.type == MT_EGGMAN_GOLDBOX)
				P_KillMobj(v, s, s.target)
				return --
			end
		end
	else
		return false --
	end
end
addHook("MobjCollide", SmokeCollide, MT_SMMK)
addHook("MobjMoveCollide", SmokeCollide, MT_SMMK)

--We've determined a target we've murdered, what kind of kill will we get this time? Everything must be valid.
--"s" is the player source mobj, "t" is the enemy we're killing.
--SMS_AssignDeathTracker(s, t, 120, 3) --1BH, 2ZAP, 4FIRE, 8FLURRY
--i is an optional missile, to add its momentum instead.
rawset(_G, "SMS_AssignDeathTracker", function(s, t, FUSE, TYPEFLAGS, i)
	local GRAPHIX = SMS_Graphix(s)
	local GORELVL = SMS_Gore(s)
	if (GORELVL > 210)
		return -- Too much gore, save the frames!
	else
		SMS_Gore(s, GRAPHIX+1)
	end
	if (t.flags & MF_BOSS) and not t.fusrodah --Don't burn bosses TOO casually...
		if i and (i.flags & MF_MISSILE) and not i.mysticflame
			if i.target and i.target.player
			and not i.target.player.hypermysticsonic
				return --
			end
		elseif s.player and not s.player.hypermysticsonic
			return --
		end
	end
	
	--We got permission, let's begin!
	local TRACKER	
	local ADDGHOST = true	
	if t.KillTracker and t.KillTracker.valid --There's already one! But we can add values to it.
		TRACKER = t.KillTracker
		if (TRACKER.SMS_SpecialKill and TRACKER.SMS_SpecialKill.valid) --Already has a ghost object!
			ADDGHOST = false
		end
	end	
	
	--Spawn a tracker if there's none yet.
	if not (TRACKER and TRACKER.valid)
		t.KillTracker = P_SpawnMobjFromMobj(t, 0,0,0, MT_CORPSETRACKER)
		TRACKER = t.KillTracker
		TRACKER.angle = s.angle
		TRACKER.tracer = s --Tracer is the original mobj
	--	if i and (i.type == MT_CMPHTRACKER) and i.tracer and i.tracer.player and i.tracer.skin == "sms"
	--		TRACKER.target = i.tracer 	
	--	elseif i and (i.flags & MF_MISSILE) and i.target and i.target.player and i.target.skin == "sms"
	--		TRACKER.target = i.target
	--	else
			TRACKER.target = t --Target is our indicated player.
	--	end
		TRACKER.GRAPHIX = SMS_Graphix(s)
		TRACKER.INITMOMDIV = 2
	end	
	
	--Now add a ghost to manipulate, and extend the tracker to our fuse.
	if ADDGHOST 
	--	print("ADDGHOST!")	
		TRACKER.fuse = (FUSE) or 99
		TRACKER.tics = TRACKER.fuse+1
	
		--Spawn an object that'll copy EXACTLY what the enemy is doing. We'll be manipulating this rather than the enemy.
		local ghs = P_SpawnMobjFromMobj(t, 0,0, 0, MT_SMSAUTOFRAME)
		if t.state
			P_SetMobjStateNF(ghs, t.state)
		end
		ghs.sprite = t.sprite
		if t.skin
			ghs.skin = t.skin
			ghs.sprite2 = t.sprite2
		end
		ghs.frame = t.frame
		ghs.color = t.color
		ghs.blendmode = t.blendmode
		ghs.renderflags = t.renderflags
		ghs.rollangle = t.rollangle
		ghs.mirrored = t.mirrored
		ghs.colorized = t.colorized
		ghs.spriteyoffset = t.spriteyoffset
		ghs.spritexoffset = t.spritexoffset
		ghs.spriteyscale =  t.spriteyscale
		ghs.spritexscale =  t.spritexscale
		ghs.shadowscale = (GRAPHIX == 0) and 75555 or 0
		ghs.height = t.height or FRACUNIT*4
		ghs.radius = t.radius or FRACUNIT*4
		ghs.flags = MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY
		
		if i and i.valid and (i.momx or i.momy) --Add missile momentum?
			TRACKER.INITMOMX = i.momx/2 + FixedMul(9*s.scale, cos(s.angle))
			TRACKER.INITMOMY = i.momy/2 + FixedMul(9*s.scale, sin(s.angle))
			TRACKER.INITANGLE = R_PointToAngle2(i.x, i.y, i.x+s.momx, i.y+i.momy)
			local iMOMZ = max(6*s.scale, min(28*s.scale, (abs(i.momx)+abs(i.momy)/41)+(i.momz/2)))
			TRACKER.INITMOMZ = iMOMZ*P_MobjFlip(s)
			if i.NovaColors
				TRACKER.COLORTABLE = i.NovaColors
				local EXTRAMOMZ = min(66*t.scale, abs(s.z-t.z)/5)
				TRACKER.INITMOMX,TRACKER.INITMOMY = $*2+FixedMul(EXTRAMOMZ/2, cos(s.angle)),$*2+FixedMul(EXTRAMOMZ/2, sin(s.angle))
				TRACKER.INITMOMZ = $*2+EXTRAMOMZ
				ghs.spriteyscale = $+25000
				ghs.spritexscale = $+25000				
			elseif s.player and s.player.hypermysticsonic
				ghs.spriteyscale = $+25000
				ghs.spritexscale = $+25000
				local EXTRAMOMZ = min(45*t.scale, abs(s.z-t.z)/6)
				TRACKER.INITMOMZ = $+EXTRAMOMZ
				TRACKER.INITMOMX,TRACKER.INITMOMY = $+FixedMul(EXTRAMOMZ/2, cos(s.angle)),$+FixedMul(EXTRAMOMZ/2, sin(s.angle))
			end
		else
			if s.momx or s.momy --Add player momentum?
				TRACKER.INITMOMX = s.momx*3/4 + FixedMul(15*s.scale, cos(s.angle))
				TRACKER.INITMOMY = s.momy*3/4 + FixedMul(15*s.scale, sin(s.angle))
				TRACKER.INITANGLE = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
			else
				TRACKER.INITMOMX = t.momx + FixedMul(24*s.scale, cos(s.angle))
				TRACKER.INITMOMY = t.momy + FixedMul(24*s.scale, sin(s.angle))	
				TRACKER.INITANGLE = s.angle
			end
			local iMOMZ = max(5*s.scale, min(21*s.scale, (abs(s.momx)+abs(s.momy)/50)+(s.momz/5)))
			TRACKER.INITMOMZ = iMOMZ*P_MobjFlip(s)
			TRACKER.COLORTABLE = s.NovaColors
		end
		
		ghs.fuse = TRACKER.fuse+1
		ghs.tics = ghs.fuse+1
		ghs.sttfade = true

		TRACKER.SMS_SpecialKill = ghs --This will the ghost the tracker manipulates from here on out.
	end
	
	--Acts as flags. All 4 combined is 15. BlackHole+FireDeath would be 5. 
	--You can add the flags in the function, or simply give the target mobj the death variables you want.
	
/*	if (TYPEFLAGS & 1) or t.BlackHole --BlackHole: 1 (Also used for NovaBlast)
		t.BlackHole = (tonumber(t.BlackHole)) and true
		TRACKER.BlackHole = t.BlackHole
--		print("\x84 Black Hole: "+t.BlackHole)
	end */
	if (TYPEFLAGS & 2) or t.ZapDeath --ZapDeath: 2 
		t.ZapDeath = (tonumber(t.ZapDeath)) or 42
		TRACKER.ZapDeath = t.ZapDeath
--		print("\x82 Black Hole: "+t.ZapDeath)
	end
	if (TYPEFLAGS & 4) or t.FireDeath --FireDeath: 4
		t.FireDeath = (tonumber(t.FireDeath)) or 62
		TRACKER.FireDeath = t.FireDeath
--		print("\x89 Fire: "+t.FireDeath)
	end
/*	if (TYPEFLAGS & 8) or t.FlurryRush --FlurryRush: 8	
		t.FlurryRush = (tonumber(t.FlurryRush)) or true
		TRACKER.FlurryRush = t.FlurryRush
--		print("\x8a Flurry: "+t.FlurryRush)
	end */

	--Now silence and hide the original mobj.
	t.flags2 = $|MF2_DONTDRAW
	S_StopSound(t)
	
--	SMS_SpecialDeath(TRACKER, TRACKER.SMS_SpecialKill, TRACKER.target)
	
	return TRACKER --
end)

//Remove Special Kill values on removal.
addHook("MobjRemoved", function(s)
	if s.target and s.target.valid
		local t = s.target
		t.BlackHole,t.ZapDeath,t.FireDeath,t.FlurryRush = nil
	end
end, MT_CORPSETRACKER)

--Flinging ragdoll corpses!
--t is the "ghost" we're manipulating. s is the tracker object. m is the original object who we have to keep quiet. Might be nil by now.
rawset(_G, "SMS_SpecialDeath", function(s, t, m)
	--M(ario!) is the the original mobj we've killed.
	if m and m.valid --You and all your parts, STFU and stay out of my sight.
		if m.health and (m.state != m.info.deathstate) and (m.flags & MF_SHOOTABLE) --You're ALIVE?! ABORT!!
		--	print("\x85"+"ENEMY ALIVE, EMERGENCY ABORT!")
			m.flags2 = $ & ~MF2_DONTDRAW
			m.BlackHole,m.ZapDeath,m.FireDeath,m.FlurryRush = nil
			if t and t.valid t.state = S_NULL end
			s.state = S_NULL return --				
		else --OK, dead like it should be! */
			if s.SMS_Dinit
				P_SetOrigin(m, m.x, m.y, 9999*FRACUNIT) --Leave the premise.
				if (m.tics > 1) m.tics = 1 end
			else
				P_SetOrigin(s, m.x, m.y, m.z+m.height/2)
			end
			for i = 1,3
				local part = (i == 1) and m or (i == 2) and m.tracer or m.target
				if part and part.valid --ALL your parts, get outta here!
				and not part.player and not part.skin
					part.flags2 = $|MF2_DONTDRAW
					part.frame = A
					part.sprite = SPR_NULL
					part.tics,part.anim_duration = 99,99
					S_StopSound(part)
				end
			end
		end
	end --Okay, we've got the original mobj out of the way....
	
	--CANCEL CONDITIONS!
	--T is the ghost corpse we're manipulating for ragdolls. Without it, we don't have a reason to exist.
	if not (t and t.valid) // or (s.fuse <= 1) or (s.tics <= 1)
--		print("\x84 Death time, fuse is up: "+s.fuse)
		if m and m.valid
			m.BlackHole,m.ZapDeath,m.FireDeath,m.FlurryRush = nil	
		end
		if t and t.valid
			t.state = S_NULL
		end
		s.state = S_NULL return -- Time's up, delete stuff!
	end
	
--	==========INITIAL SETUP==========
--	      SETUP THE CORPSE GFX
--	=================================

	--Remember preferred angle and speed at all times! Our tracer is the killer, btw.
	local v = nil
	if s.tracer and s.tracer.valid
		v = s.tracer
		if v.momx or v.momy
			s.PREFANGLE = R_PointToAngle2(v.x, v.y, v.x+v.momx, v.y+v.momy)
		else
			s.PREFANGLE = v.angle
		end
		s.PREFSPEED = (abs(v.momx)+abs(v.momy)) or 16*v.scale
	end
	if s.SMS_Dinit == nil
		s.SMS_Dinit = 0
		s.SMS_RD = (SMSLocalRNG(-31, 30, t) >= 0) and 1 or -1 --Roll direction if we need it.
		s.startxscale = t.spritexscale
		s.startyscale = t.spriteyscale
		if s.BlackHole --Turn these values into usable numbers.
			s.BlackHole = (tonumber(s.BlackHole)) or 62
			s.INITMOMDIV = 8
			if v != nil
				s.INITANGLE = R_PointToAngle2(t.x, t.y, v.x, v.y)
			elseif s.INITANGLE != nil
				s.INITANGLE = $+ANGLE_180
			end
		end
		if s.FireDeath
			s.FireDeath = (tonumber(s.FireDeath)) or 62
			if m and m.valid and (m.flags & MF_BOSS)
				S_StartSound(t, sfx_smsfxa)
			else
				S_StartSoundAtVolume(t, sfx_smsfxa, 27)
			end
		end
		if s.ZapDeath
			s.ZapDeath = (tonumber(s.ZapDeath)) or 40
		end
		if s.FlurryRush
			s.FlurryRush = (tonumber(s.FlurryRush)) or 30
			s.INITMOMDIV = 1
		end
		if s.GRAPHIX == nil
			s.GRAPHIX = SMS_Graphix(s)
		end
		if s.PREFANGLE == nil --Default values if no tracer is present somehow.
			if s.INITANGLE != nil
				s.PREFANGLE = s.INITANGLE
			else
				s.PREFANGLE = (t.momx or t.momy) and R_PointToAngle2(t.x, t.y, t.x+t.momx, t.y+t.momy) or t.angle+ANGLE_180
			end
			s.PREFSPEED = 16*FRACUNIT
		end	
		
		--Lots of RNG values for rollangle and spinspeed.
		s.ROLLSPEED = SMSLocalRNG(10, 45, t)*(ANG1*s.SMS_RD)
		
		s.ROLLSPEED2 = SMSLocalRNG(-159, 159, t)*(ANG1*s.SMS_RD)
		
		s.ROLLSPEED3 = SMSLocalRNG(16, 70, t)*(ANG1*s.SMS_RD)	
		
		if s.INITMOMZ
			t.momz = s.INITMOMZ/s.INITMOMDIV
		else
			t.momz = 4*FRACUNIT*P_MobjFlip(t)
		end
		if s.INITANGLE == nil
			s.INITANGLE = v and v.angle or t.angle+ANGLE_180
		end
		t.angle = s.INITANGLE
		if not s.BlackHole
			t.angle = $+(SMSLocalRNG(-50,50,t)*ANG1)
			if v != nil and s.PREFSPEED
				local RNGSPEED = FixedDiv(FixedMul(s.PREFSPEED,10), SMSLocalRNG(11,26,s)) or v.scale
				if s.FlurryRush
					P_Thrust(t, s.PREFANGLE, RNGSPEED*2)
					t.momz = $+RNGSPEED/5*P_MobjFlip(v)
				else
					P_Thrust(t, s.PREFANGLE, RNGSPEED)
			--		t.momz = $+ (v.momz/12)+ (RNGSPEED/12*P_MobjFlip(v))
				end
			end
		end
		--Set the corpse's momentum!
		if s.INITMOMX or s.INITMOMY
			if s.INITMOMX
				t.momx = $+(s.INITMOMX/s.INITMOMDIV)
			end
			if s.INITMOMY
				t.momy = $+(s.INITMOMY/s.INITMOMDIV)
			end
		else
			P_Thrust(t, s.INITANGLE, 30*FRACUNIT)
		end	
		
	elseif s.SMS_Dinit == 2	
	
		--Initial settings for blackhole?
/*		if s.BlackHole
			if not s.FlurryRush
				if (abs(s.ROLLSPEED) < ANG10)
					s.ROLLSPEED = ANG10*s.SMS_RD
				end
				if (abs(s.ROLLSPEED2) < ANGLE_22h)
					s.ROLLSPEED2 = ANGLE_22h*s.SMS_RD
				end
			end 
			s.spritexscale = $*2
			s.spriteyscale = $*2				
			s.startxscale = $*2
			s.startyscale = $*2
			t.momx = 0
			t.momy = 0
			t.momz = FRACUNIT*8*P_MobjFlip(t)
			t.flags = $|MF_NOGRAVITY
			t.rollangle = $+(SMSLocalRNG(-65, 65, t)*ANG1*s.SMS_RD)
			t.angle = $+(SMSLocalRNG(50, 50, t)*ANG1*s.SMS_RD)		
			if not s.GRAPHIX
				local ghs = P_SpawnGhostMobj(t)
				ghs.color = SKINCOLOR_SUPERSILVER1
				ghs.colorized = true
				ghs.blendmode = AST_ADD
				if SMSLocalRNG(3,4,s) == 3
					ghs.spriteyscale = s.startyscale/2
					ghs.spritexscale = s.startxscale*2				
				else
					ghs.spriteyscale = s.startyscale*2
					ghs.spritexscale = s.startxscale/2
				end
				ghs.dispoffset = 10
				ghs.scalespeed = FRACUNIT/9
				ghs.rollangle = t.rollangle
				ghs.fuse = 120 ghs.tics = 24
				ghs.destscale = $*999
			end
		end */
		
		--Lightning is chaotic, so let's treat it as such.
		if s.ZapDeath and s.GRAPHIX != 2
			local ghs
			if not s.GRAPHIX and SMSLocalRNG(0,5, s) == 1
				HMS_BoltStorm(s, nil, nil, nil, -45)
			elseif SMSLocalRNG(0, 1, t) == 1
				if (leveltime % 2)
					SMS_ElectricRecoil(s)
					ghs = SMS_LightFlash(t, SKINCOLOR_SMS5R, FRACUNIT, 6)
				else
					ghs = HMS_GigaJetBolt(t, FRACUNIT/3, 15)
					ghs.tracer = t
					ghs.dispoffset = 3
					ghs = SMS_LightFlash(t, SKINCOLOR_RED, FRACUNIT, 6)
				end
				ghs.fuse = 11					
			else
				if (leveltime % 2)
					SMS_LightningStrike(t)
					ghs = SMS_LightFlash(t, SKINCOLOR_MYSTICAL1, FRACUNIT/2, 6)
					ghs.fuse = 11 ghs.sttfade = 9
				else
					SMS_LightningStrike(t, SKINCOLOR_SUPERSKY5, R)
					ghs = SMS_LightFlash(t, SKINCOLOR_SUPERSKY2, FRACUNIT/2, 6)
					ghs.fuse = 11 ghs.sttfade = 9
				end
			end
		end
		
		--Little burst of initial embers and smoke.
		if s.FireDeath 
			if (s.eflags & MFE_TOUCHWATER) or (t.eflags & MFE_TOUCHWATER)
				if s.FireDeath > 29
					s.FireDeath = 29
				end		
			elseif not s.GRAPHIX --Gets spicy....			
				if (leveltime % 2)
					SMS_RisingFlames(t)
				else
					SMS_Embers(s, t, 0)
				end
				local RNG = SMSLocalRNG(2,6,t)
				local ghs = SMS_SmokeCircle(t, 0, 1, s, (FRACUNIT/3)*RNG)
				if ghs and ghs.valid
					ghs.ditchtracer = RNG-1
					ghs.fuse = $-RNG
					ghs.spriteyoffset = -(27+RNG)*FRACUNIT
				end
			end
		end	
	end
--	print("DINIT = "+s.SMS_Dinit+" FUSE "+s.fuse+" TICS = "+s.tics+" t = "+t)
	
	s.SMS_Dinit = $+1	
--	=========================INITIAL STUFF OVER!=============================
--	     Begin Continous Corpse GFX. Manipulations happening every frame!
--	=========================================================================

--	=========BLACK HOLE/VOID============

/*	if s.BlackHole
		if s.SMS_RD == 1
			t.spritexscale = max($-200, 99)
			s.startxscale = max($-200, 99)
			t.spriteyscale = min($+2800, FRACUNIT*10)
			s.startyscale = min($+2800, FRACUNIT*10)
		else
			t.spriteyscale = max($-200, 99)
			s.startyscale = max($-200, 99)
			t.spritexscale = min($+2800, FRACUNIT*10)
			s.startxscale = min($+2800, FRACUNIT*10)
		end
		if v and (v.state == S_HMSBLACKHOLE)
			P_Thrust(t, R_PointToAngle2(t.x, t.y, v.x, v.y), v.scale)
			t.z = (v.z < t.z) and $+(v.scale/3) or $-(v.scale/3)
			t.dispoffset = 20
			t.flags = $|MF_NOGRAVITY
		end
		s.ROLLSPEED = 2
		t.angle = $+(s.ROLLSPEED2/2)
		
		if (s.BlackHole < 30) --Enemies `changes color....
			if (s.BlackHole > 20)
				t.colorized = true
				t.color = (s.SMS_RD == 1) and SKINCOLOR_SUPERPURPLE5 or SKINCOLOR_SUPERSKY4
				if not (t.renderflags & RF_FULLDARK)
					t.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
				end
				t.blendmode = AST_ADD
			else -- < 20
				if s.COLORTABLE
				local COLORTABLE = {SKINCOLOR_PURPLE, SKINCOLOR_MYSTICAL3, SKINCOLOR_SMS5R, SKINCOLOR_ORANGE, SKINCOLOR_RED, SKINCOLOR_FLAME, SKINCOLOR_EMERALD, SKINCOLOR_MINT, SKINCOLOR_SUPERSKY5, SKINCOLOR_WHITE}
					t.color = COLORTABLE[SMSLocalRNG(1,9,t)]
				end
				--Rainbow colors?
				if s.BlackHole % 2 == 0
					local TRANS = (t.frame & FF_TRANSMASK)
					TRANS = max(0, min($+FRACUNIT, FF_TRANS90))
					t.frame = ($ & ~FF_TRANSMASK)|TRANS
				end
			end
		end
		if not s.FlurryRush and not (s.FireDeath and s.FireDeath > 30)
		and (t.momx or t.momy or t.momz)
			t.momx = FixedDiv(FixedMul($, 33),34)
			t.momy = FixedDiv(FixedMul($, 33),34)
			t.momz = FixedDiv(FixedMul($, 33),34)
			if (t.momz*P_MobjFlip(t) < 3*FRACUNIT)
				P_SetObjectMomZ(s,7356,true)
			end
		end 
		s.BlackHole = $-1
	end */
	
--	=========GENERAL BEHAVIOUR==========	

--	print("Fuse: "+s.fuse+" Rollangle: "+t.rollangle/ANG1+" ROLLSPEED: "+s.ROLLSPEED/ANG1)
		if s.fuse < 30 
		and not s.FlurryRush and not s.BlackHole
		and (t.momx or t.momy or t.momz)
			t.momx = FixedDiv(FixedMul($, 83),84)
			t.momy = FixedDiv(FixedMul($, 83),84)
			t.momz = FixedDiv(FixedMul($, 83),84)
		end	
		if (t.momz*P_MobjFlip(t) > 15*t.scale)
			P_SetObjectMomZ(t, -42376, true)
		elseif P_IsObjectOnGround(t) and (s.momz*P_MobjFlip(t) <= 0)
			if not s.touchedGROUND
				if (s.SMS_Dinit > 3) 
					s.touchedGROUND = 1
					t.rollangle = $-s.ROLLSPEED*3
					t.angle = $-s.ROLLSPEED2*3
					s.ROLLSPEED = $/-2
					s.ROLLSPEED2 = $/-2
					S_StartSoundAtVolume(t, sfx_corkh, 30)
					S_StartSoundAtVolume(t, sfx_doord2,66)
					P_SetObjectMomZ(t, 3*FRACUNIT)
				else
					P_SetObjectMomZ(t, 4*FRACUNIT, true)
				end
			end
		end
		if (s.eflags & MFE_UNDERWATER)
			if t.momz*P_MobjFlip(t) < 3*t.scale
				P_SetObjectMomZ(t, 15000+FRACUNIT, true)
			else
				t.momz = FixedDiv(FixedMul($, 47),48)
			end
			if s.FireDeath and (s.FireDeath > 30)
				s.FireDeath = 29
			end
		end
		if not s.BlackHole
			local MOVESPEED = abs(t.momx)+abs(t.momy)+abs(t.momz)
			local SPEEDROLL = FixedDiv(FixedMul(s.ROLLSPEED, FixedInt(MOVESPEED or FRACUNIT)), 50)
			local SPEEDSPIN = FixedDiv(FixedMul(s.ROLLSPEED2, FixedInt(MOVESPEED or FRACUNIT)), 50)	
			t.rollangle = $+SPEEDROLL
			t.angle = $+SPEEDSPIN
			if MOVESPEED > 12*FRACUNIT and (s.SMS_Dinit > 4 and s.SMS_Dinit < 14)
				local ghs = P_SpawnMobjFromMobj(t, t.momx,t.momy,t.momz, MT_THOK)
				ghs.state = S_SPINDUST1+SMSLocalRNG(0,2,t)
				ghs.colorized = true
				ghs.color = (SKINCOLOR_SUPERGOLD5+5)-(s.SMS_Dinit*2)
				ghs.fuse = 32
				ghs.frame = ($ & FF_FRAMEMASK)|TR_TRANS50
				ghs.scale = (t.scale*s.SMS_Dinit)/5
				ghs.scalespeed = $/2
				ghs.destscale = $*99
				if (s.SMS_Dinit < 9)
					ghs.flags2 = $|MF2_SHADOW
				end
			end
		end
	
--	=========FIRE=========

	if s.FireDeath
	
		if (s.FireDeath < 30)
		
			t.renderflags = ($ & ~RF_FULLBRIGHT)|RF_FULLDARK
			t.sttfade = 22
			t.spritexscale = $+999
			t.spriteyscale = $+999
			if not s.GRAPHIX
				if (s.FireDeath % 6 == 0) and SMSLocalRNG(78,80,s) != 80 and (SMS_Gore(s,2) < 150) --Spawn smoke.
					local RNG = SMSLocalRNG(3,7,s)
					local ghs = SMS_SmokeCircle(t, 0, 1, t, (FRACUNIT/4)*RNG)
					ghs.fuse = $-6
					ghs.ditchtracer = 4
				end
			end
			
		elseif (s.FireDeath % 3 == 0) and s.GRAPHIX != 2
			if SMSLocalRNG(77,79,t) == 79 --Spawn fire sparks?
			and (SMS_Gore(s) < 112)
		
				local RNG1 = P_RandomRange(10,40)
				local RNG2 = P_RandomRange(1,360)
				local height = (not P_IsObjectOnGround(t)) and FRACUNIT*8 or 0
				local ghs = P_SpawnMobjFromMobj(t, 0, 0, height, MT_SMSRGORE)
				if ghs and ghs.valid
					ghs.scale = v and v.scale or FRACUNIT
					P_InstaThrust(ghs, RNG2*ANG1, (17+(RNG1/2))*FRACUNIT )
					P_SetObjectMomZ(ghs, ((RNG1/3)+5)*FRACUNIT)
					ghs.sprite = SPR_SMX5
					ghs.blendmode = AST_ADD
					ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS|RF_PAPERSPRITE
					ghs.frame = SMSLocalRNG(Z+4,Z+6,t)|FF_TRANS10
					if v
						local FAVANGLE,PLUS = SMS_AngleDifference(v.angle, R_PointToAngle2(ghs.x, ghs.y, ghs.x+ghs.momx, ghs.y+ghs.momy), 2)
						if PLUS == 1
							ghs.frame = $|FF_HORIZONTALFLIP
						end
						P_Thrust(ghs, v.angle+ ((s.FireDeath % 6 == 0) and ANGLE_270 or ANGLE_90) + ((RNG1-25)*ANG1) , 11*v.scale)
					end
					ghs.angle = R_PointToAngle2(ghs.x, ghs.y, ghs.x+ghs.momx, ghs.y+ghs.momy)+ANGLE_90
					ghs.radius = FRACUNIT
					ghs.height = FRACUNIT
					ghs.spriteyscale = FRACUNIT+27000-(RNG2*28)
					ghs.spritexscale = ghs.spriteyscale
					ghs.flamespark = 61
					if (RNG2 > 310 or RNG2 < 50)
						ghs.color = SKINCOLOR_ORANGE
						ghs.colorized = true
					end
				end
			elseif (s.FireDeath % 9 == 0)
				local ghs = P_SpawnMobjFromMobj(t,t.momx,t.momy,-t.height, MT_SMSAUTOFRAME)
				ghs.sprite = SPR_SMX5
				ghs.frame = (Z+9)|TR_TRANS10
				ghs.tracer = t
				ghs.blendmode = AST_ADD
				ghs.scale = v and v.scale/2 or FRACUNIT/2
				ghs.spritexscale = FRACUNIT+20000
				ghs.scalespeed = $*3
				ghs.sttfade = 14
				ghs.fuse = 14
				ghs.tics = 99
				ghs.destscale = $*99
				ghs.momz = 7*s.scale*P_MobjFlip(t)	
			end
		end	
		if (s.FireDeath % 8 == 0)
			t.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			local ghs = SMS_ElectricBody(t, 13) --Function used as template, this uses flame sprites!
			ghs.sprite = SPR_SMX5
			ghs.sttfade = 12
			ghs.frame = B|TR_TRANS10
			ghs.spriteyscale = max(FRACUNIT/45, min(t.height/27, FRACUNIT/3))+SMSLocalRNG(-1450,11000,t)
			ghs.spritexscale = (ghs.spriteyscale*5)/6
			ghs.GSoffsetX = 0
			ghs.GSoffsetY = 0
			ghs.GSoffsetZ = t.height/3
			ghs.dispoffset = -5
			ghs.sttfinalframe = B
			ghs.sttfirstframe = C
		end
		s.FireDeath = $-1
	end
	
--	=========ELECTRICITY=========	

	if s.ZapDeath
	
		if (s.ZapDeath < 10)
			if t.startxscale != nil
				t.spritexscale = t.startxscale 
				t.spritexscale = t.startyscale 
				t.startxscale,t.startyscale = nil
			end
			if not s.FireDeath --Spawn smoke, unless fire's already doing it.
				t.renderflags = ($ & ~RF_FULLBRIGHT)|RF_FULLDARK
				t.sttfade = true
				if (s.ZapDeath % 2) and SMSLocalRNG(78,80,s) != 79 and SMS_Gore(s,1) --Spawn smoke.
					local RNG = SMSLocalRNG(3,7,s)
					local ghs = SMS_SmokeCircle(t, 0, 1, s, (FRACUNIT/4)*RNG)
					ghs.colorized = true
					ghs.color = SKINCOLOR_JET
					ghs.ditchtracer = 3
				end
			end		
		else
			if (s.ZapDeath % 3 == 0) --Shake and distort sprite violently for an electric pain effect.
				t.colorized = true
				t.PrevColor = t.color
				t.color = SKINCOLOR_MYSTICAL1
				t.blendmode = AST_ADD
				t.renderflags = $|RF_FULLBRIGHT|RF_NOCOLORMAPS
				t.rollangle = $+(SMSLocalRNG(-20,20,t)*ANG1)
				if t.startxscale != nil
					t.spritexscale = t.startxscale*2
					t.spriteyscale = t.startyscale/3
				end
			else
				t.colorized = false
				t.color = t.PrevColor or $
				t.blendmode = AST_TRANSLUCENT
				t.renderflags = ($ & ~RF_FULLBRIGHT)|RF_SEMIBRIGHT & ~RF_NOCOLORMAPS
				if t.startxscale != nil
					if (s.ZapDeath % 2 == 0) --Shake violently!
						t.spritexscale = t.startxscale
						t.spriteyscale = t.startyscale
						t.rollangle = $+(SMSLocalRNG(-30,30,t)*ANG1)
						t.angle = $+(SMSLocalRNG(-80,80,t)*ANG1)
					elseif t.startxscale != nil
						t.spritexscale = t.startxscale/3
						t.spriteyscale = t.startyscale*2
					end
				end
			end
			if (s.ZapDeath % 5 == 0) --Continuous electricity!
				local ghs = SMS_ElectricBody(t, 7, SKINCOLOR_CRIMSON)
				ghs.blendmode = AST_TRANSLUCENT
				ghs.spritexscale = $+SMSLocalRNG(0,29900,t)
				ghs.spriteyscale = ghs.spritexscale
				ghs.spriteyoffset = 4*FRACUNIT
				ghs.spritexscale = $+12000
				ghs.rollangle = $+SMSLocalRNG(-20,20,t)*ANG1
				if s.ZapDeath % 10 == 0
					ghs.mirrored = true
					ghs.color = SKINCOLOR_SMS4R
				end
			end
		end
		s.ZapDeath = $-1
	end
	
	P_SetOrigin(s, t.x, t.y, s.z)
	
--	=========FLURRY RUSH/Rapidly getting mauled to death!=========	

/*	if s.FlurryRush and s.WHEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
		if not (v and v.player) return end --Needs a player for this to work!
		
		local p = v.player		
		t.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
print("Flurry "+s.FlurryRush)		
		if s.FlurryRush % 11 == 0 --Every 11 tics, SMASH!
			p.mo.suddenquickspeed = 7
			A_SMSquake(s, 70*FRACUNIT, 6, 990, true)
			if SMSLocalRNG(0,3, t) == 2
				t.spritexscale = FRACUNIT-12000
				t.spriteyscale = FRACUNIT-9000
				S_StartSound(t, sfx_smscrt)
			elseif SMSLocalRNG(0, 1, t) == 1
				t.spritexscale = FRACUNIT-35000
				t.spriteyscale = FRACUNIT+20000
				S_StartSound(t, sfx_smsfx5)
			else
				t.spritexscale = FRACUNIT-35000
				t.spriteyscale = FRACUNIT+20000	
				S_StartSound(t, sfx_nerf)
			end
			local NEWRNG = SMSLocalRNG(-55, 55, t)	
			if (abs(NEWRNG) < 8) NEWRNG = 8 end
			
			t.rollangle = $+(SMSLocalRNG(-77, 77, t)*ANG1)
			s.ROLLSPEED3 = NEWRNG/5
			s.SMS_RD = (NEWRNG >= 0) and 1 or -1
			
			--Draw an attack trail from various angles.
			local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_THOK)
			if p.hypermysticsonic
				ghs.sprite = SPR_HMSO
				ghs.frame = G|TR_TRANS90
			else
				ghs.sprite = SPR_SMSO
				ghs.frame = S|TR_TRANS90
			end
			ghs.blendmode = AST_ADD
			ghs.fuse = 10 ghs.tics = 99
			ghs.scalespeed = FRACUNIT/10
			ghs.destscale = ghs.scalespeed+1
			ghs.COLORT_trail = COLORTABLE
			ghs.COLORT_trailno = 9
			ghs.ENDtrans = 0		
			local ANGLEDIFF, PLUS = SMS_AngleDifference(R_PointToAngle2(p.mo.x, p.mo.y, t.x, t.y) , p.mo.angle)			
			if PLUS == 1
				ghs.frame = $|FF_HORIZONTALFLIP
			end
			local ANGLE = R_PointToAngle2(p.mo.x, p.mo.y, t.x, t.y)+ANGLEDIFF+(SMSLocalRNG(-40, 40, s)*ANG1)
			P_SetOrigin(ghs,
			t.x+t.momx+FixedMul(-300*p.mo.scale, cos(ANGLE)),
			t.y+t.momy+FixedMul(-300*p.mo.scale, sin(ANGLE)),
			t.z+(t.height/4)+(SMSLocalRNG(-10, 95, s)*p.mo.scale))
			
			SMS_DrawTrail(s, ghs, MT_THOK)
			ghs.state = S_NULL
			
			s.momx = $+(s.momx/2) s.momy = $+(s.momy/2) s.momz = $+(s.momz/2) --Now add some of your own momentum to them.
			
		else --Otherwise, the enemy corpse is just recovering from the last punch.
		
			if t.spriteyscale > FRACUNIT
				t.spriteyscale = max($-5000, FRACUNIT)
			else
				t.spriteyscale = min($+5000, FRACUNIT)
			end
			if t.spritexscale > FRACUNIT
				t.spritexscale = max($-5000, FRACUNIT)
			else
				t.spritexscale = min($+5000, FRACUNIT)
			end
			t.rollangle = $+(s.ROLLSPEED3)
			t.angle = $+(s.ROLLSPEED3*s.SMS_RD)
			if t.momx or t.momy or t.momz
				t.momx = FixedDiv(FixedMul($, 40),43)
				t.momy = FixedDiv(FixedMul($, 40),43)
				t.momz = FixedDiv(FixedMul($, 40),43)
			end
		end
		s.FlurryRush = $-1
	end */
end)

//Gotta make this apply to ALL deaths and then run it. Be extra careful with this hook, it'll drain the most performance.
addHook("MobjDeath", function(s, i, so, dmgtype)
	if not (so and so.player) return end --Must always have a player source
	local p = so.player --Note that p is the source this time, not s.player.	
		if p.hypermysticsonic --The murderer is Hyper Mystic Sonic
			P_ResetScore(p)
			s.flags2 = $ & ~MF2_FRET
			if so == i
				if (s.flags & (MF_ENEMY|MF_BOSS))
				and (FixedHypot(FixedHypot(so.x-s.x, so.y-s.y), so.z-s.z) < 214*so.scale)
					S_StartSound(s, sfx_cdfm50)
					local ghs = P_SpawnMobjFromMobj(s, 0,0, s.height/3, MT_TNTDUST)
					if ghs and ghs.valid
						ghs.destscale = $*5
						ghs.fuse = 43
						ghs.tics = 1
						S_StartSound(ghs, sfx_kc40)
						P_Thrust(ghs, R_PointToAngle2(s.x, s.y, so.x, so.y), -14*FRACUNIT)
					end
					if not (p.hypermysticsonic < 33)
						s.hmsgodaurad = true
						A_SMSquake(so, P_IsObjectOnGround(so) and 25*FRACUNIT or 40*FRACUNIT, 3, 999, true)
						if (p.pflags & PF_SPINNING)
							SMS_AssignDeathTracker(so, s, nil, 4)
						end
					else
						s.ZapDeath = 42
					end
				end
			end
			if (so.state == S_HMSBLACKHOLE) --No further.
				return --
			end			
	/*	elseif p.mslightspeedattack --We LSA'd it?
			if (s.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR))
			and not p.lsdeadjump and (s == so.target)
				p.lsdeadjump = 1
				so.target = nil
				so.lsatarget = nil
				if so.lsaringdebt and (so.skin != "sms")
					P_GivePlayerRings(p, -5)
					so.lsaringdebt = 0
				end
			end */
		end
		
		if not s.valid --We still here? SMS too, not LSA-ing?
		or so.skin != "sms" // and not p.mslightspeedattack
			return --
		end
		
		--Special deaths!
		if i and (s.flags & (MF_ENEMY|MF_BOSS)) --Assign Death Trackers!
		and not s.fusrodah
			local RNG = P_RandomRange(0,1)
			if i == so 
				if p.hypermysticsonic
					s.ZapDeath = 42
				--	if p.speed > 120*s.scale
				--		s.FlurryRush = true
				--	end						
				elseif p.homing or p.hometrickanim
					if (p.sms_boostlevel > 64 or p.powers[pw_sneakers])
						s.ZapDeath = 42
					end
				end
				if dmgtype --Electric blastS?
					if dmgtype == DMG_FIRE
						s.FireDeath = 42
					elseif dmgtype == DMG_ELECTRIC
						s.ZapDeath = 42
					end
				end
			--Not a direct player attack then.
			elseif (i.type == MT_SMSFIREBLAST) or (i.type == MT_HMSFIREBLAST)
			or (i.type == MT_SMSSPINFIRE) or (i.type == MT_HMSNOVABLAST)
			or (i.type == MT_HMSPOWERBOMB) or (i.type == MT_SMSPOWERBOMB)
			or (i.type == MT_ATOMDIVEPOP)
				s.FireDeath = 62
			end
			--Note some of these overlap with the above!
			if (i.type == MT_HMSNOVABLAST)
			or (i.type == MT_HMSLIGHTNING1) or (i.type == MT_HMSLIGHTNING2)
			or ((i.type == MT_HMSPOWERBOMB) and RNG == 1)
				s.ZapDeath = 42
			end
		--	if (i.type == MT_HMSNOVABLAST) or (i.state == S_HMSBLACKHOLE) or s.bkhltracked
		--		s.BlackHole = true
		--	end
		/*	if s.fusrodah --Quick short burst for the Haymaker!
				s.FireDeath = 29
				s.ZapDeath = p.hypermysticsonic and 20 or $
			end */
			--SMS_AssignDeathTracker", function(s, t, FUSE, TYPEFLAGS, i)
			if s.ZapDeath or s.FireDeath //or s.FlurryRush or s.BlackHole 
				if (i.flags & (MF_MISSILE|MF_FIRE)) and (abs(i.momx) > 3*FRACUNIT or abs(i.momy) > 3*FRACUNIT)
					SMS_AssignDeathTracker(so, s, nil, 0, i) 
				else
					SMS_AssignDeathTracker(so, s, nil, 0) --1BH, 2ZAP, 4FIRE, 8FLURRY
				end
			end
		end
		
		//It's the Robot Gore code. The robot gibs.
		if (s.flags & (MF_ENEMY|MF_BOSS))
	--	and not s.BlackHole
		and (mobjinfo[s.type].deathsound == sfx_pop)
		and (mobjinfo[s.type].deathstate >= S_XPLD_FLICKY) and (mobjinfo[s.type].deathstate <= S_DUST4)
		
		or (s.type == MT_DETON) --Special gorables.
		or (s.type == MT_HIVEELEMENTAL)
		or (s.type == MT_BUMBLEBORE) 
		or (s.type == MT_GSNAPPER) 
		or (s.type == MT_FACESTABBER)
		or (s.type == MT_SNAILER)
		or s.SMSgoreable --Can make custom gore-ables if you spawn an enemy with this set to "true"
			--Success conditions for gore!
			if p.hypermysticsonic
			or p.homing
			or p.footsweepanim
			or (abs(so.momz) > 21*so.scale)
			or ((p.speed > 45*so.scale) and (p.pflags & (PF_SPINNING|PF_THOKKED)) or (p.speed > 70*so.scale))
			or p.sms_stompanim 
			or p.sms_afterstompthok
			or s.mhomingbounced --Thrown objects do it too.
		--	or p.mslightspeedattack
			or so.airboostroll 
			or p.powers[pw_sneakers]
			or (so.state == S_SMS_ATOMLAND1)
			
			or i and i.valid 
			and ((i.type == MT_CMPHTRACKER) or (i.flags & MF_MISSILE) or (i.type == MT_SMSPOWERBOMB) or (i.type == MT_SMSTHROWTRACKER))
			
					local ghs = P_SpawnMobjFromMobj(s,0,0, s.height/2, MT_THOK )
					
					local harshrobotgore = SMSLocalRNG(1,3,s) //Custom Robot Deaths
					if harshrobotgore == 1
						S_StartSound(ghs, sfx_harrg1)
					elseif harshrobotgore == 2
						S_StartSound(ghs, sfx_harrg2)
					else
						S_StartSound(ghs, sfx_harrg3)
					end
					ghs.sprite = SPR_NULL
					ghs.flags2 = $|MF2_DONTDRAW
					ghs.tics = 60
						
					local ths
					if (SMS_Gore(s) < 230) --Don't build up too much!
						ths = P_SpawnMobjFromMobj(s, 0, 0, s.height/2, MT_SMSRGORESPAWN)
						ths.state = S_HMSLIGHTNING  //Feed a crapton of information into the gorespawner, which will then do all the gore.
						ths.frame = A
						ths.sprite = SPR_NULL
						ths.flags2 = $|MF2_DONTDRAW
						ths.eneheight = s.height
						ths.eneradius = s.radius
						
						if s.color
							ths.enecolor = s.color
						elseif (s.type == MT_BLUECRAWLA) or (s.type == MT_SPRINGSHELL)
						or (s.type == MT_YELLOWSHELL) or (s.type == MT_PENGUINATOR)
							ths.enecolor = SKINCOLOR_CORNFLOWER
						elseif (s.type == MT_GFZFISH) or (s.type == MT_CRAWLACOMMANDER) or (s.type == MT_METALSONIC_BATTLE)
							ths.enecolor = SKINCOLOR_COBALT
						elseif (s.type == MT_GSNAPPER) or (s.type == MT_JETTBOMBER) or (s.type == MT_ROBOHOOD)
							ths.enecolor = SKINCOLOR_GREEN
						elseif (s.type == MT_REDCRAWLA) or (s.type == MT_REDBUZZ) or (s.type == MT_DETON)
						or (s.type == MT_VULTURE) or (s.type == MT_CRUSHSTACEAN)
							ths.enecolor = SKINCOLOR_RED
						elseif (s.type == MT_EGGGUARD) or (s.type == MT_BANPYURA) or (s.type == MT_PTERABYTE)
							ths.enecolor = SKINCOLOR_LAVENDER
						elseif (s.type == MT_FACESTABBER) or (s.type == MT_JETJAW)
							ths.enecolor = SKINCOLOR_CRIMSON
						elseif (s.type == MT_DRAGONBOMBER) or (s.type == MT_PYREFLY) or (s.type == MT_CANARIVORE)
						or (s.type == MT_SNAILER) or (s.type == MT_GOLDBUZZ) or (s.type == MT_HIVEELEMENTAL) or (s.type == MT_BUMBLEBORE)
							ths.enecolor = SKINCOLOR_GOLD
						elseif (s.type == MT_JETTGUNNER) or (s.type == MT_CYBRAKDEMON)
							ths.enecolor = SKINCOLOR_BROWN
						elseif (s.type == MT_UNIDUS) or (s.type == MT_POPUPTURRET) or (s.type == MT_MINUS)
							ths.enecolor = SKINCOLOR_CARBON
						else
							ths.enecolor = s.color
						end
						ths.enescale = s.scale
						if p.homing or s.mhomingbounced
							if (p.powers[pw_sneakers] or (p.sms_boostlevel > 64))
								ths.enethrust = SMSLocalRNG(20,80,ths)*FRACUNIT
							elseif (so.SMShomingspeed) and (so.SMShomingspeed > 13*FRACUNIT)
								ths.enethrust = so.SMShomingspeed*6/7
							else
								ths.enethrust = 10*FRACUNIT
							end
						elseif (p.speed > 20*so.scale)
							ths.enethrust = p.speed*6/7 
						else
							ths.enethrust = 16*FRACUNIT
						end
						ths.eneanglethrust = R_PointToAngle2(s.x, s.y, so.x, so.y)
						ths.enex = s.x
						ths.eney = s.y
						ths.enez = s.z
						ths.target = s
						ths.smskiller = so
						
						local GORELVL = SMS_Gore(s)
						p.srgorelvl = GORELVL
						ths.srgorelvl = GORELVL
						
						if p.hypermysticsonic
							ths.hmskiller = so
						end						
					end
					
					s.mhomingbounced = 0  
					A_SMSquake(so, 14*FRACUNIT, 2, 300, true)
						
					if (s.flags & MF_ENEMY) and not (s.flags & MF_BOSS) //"Inflate" and then blow up! Enjoy, Deviantart
					and (mobjinfo[s.type].deathsound == sfx_pop)
					and (mobjinfo[s.type].deathstate >= S_XPLD_FLICKY) and (mobjinfo[s.type].deathstate <= S_DUST4)					
						local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_THOK)
						ghs.sprite = s.sprite
						ghs.frame = s.frame
						ghs.angle = s.angle
						ghs.rollangle = s.rollangle
						ghs.mirrored = s.mirrored
						ghs.renderflags = s.renderflags
						ghs.color = s.color
						if ths and ths.valid
							P_Thrust(ghs, ths.eneanglethrust, ths.enethrust*-2)
						end
						ghs.tics = 4
						ghs.scalespeed = $*9
						ghs.destscale = $*99
					end
				return --
			end
		end
end)

//Lightspeed Attack Damage reset. Also done in MobjDeath just to make sure.
addHook("MobjDamage", function(s, i, so)
	if not (so and so.player and s.valid) return end --
	local p = so.player
	
		if (s.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR))
			if p.hypermysticsonic and (so.skin == "sms")
				if (p.hypermysticsonic > 30) and not so.starthmscombo
					P_KillMobj(s, i, so) //Any damage from Hyper Mystic Sonic is instant death
					return false --
				end
				return --	
			else
				if p.mslightspeedattack
					if not p.lsdeadjump and (so.target == s)
					p.lsdeadjump = 1
					so.target = nil
					so.lsatarget = nil
						if so.lsaringdebt and not (so.skin == "sms")
							P_GivePlayerRings(p, -5)
							so.lsaringdebt = 0
						end
					end	
				elseif so.skin == "sms" and (so == i)
					if p.hometrickanim 
						p.hometrickanim = false
						so.state = S_PLAY_ROLL //Enough tricking
					elseif (s.flags & MF_BOSS) and p.homing
					so.forcesmstrick = true
						if (s.type == MT_EGGMOBILE4)
							P_Thrust(so, R_PointToAngle2(so.x, so.y, s.x, s.y), -195*FRACUNIT) //Don't insta-crush players who homing attack you
						end
					end
					return --
				end
			end
		end
end)

//Burn Cacti with flames!

local function CactiBurn(f, s)
	if ((f.flags & MF_MISSILE) or (f.player and f.player.hypermysticsonic))
	
	and s and s.valid and (s.type >= MT_CACTI1 and s.type <= MT_CACTISMALLSEG) and (s.flags & MF_PAIN)
	
	and (f.readyforonemoreplant or not ((f.z > s.z+s.height) and not (f.z+f.height < s.z)) )
		if not SMS_Graphix(f)
			local ghs = SMS_SmokeCircle(f, 0, 1, f, FRACUNIT*4)
			P_SetOrigin(ghs, s.x, s.y, s.z)
			ghs.tracer = nil
		end
		
	s.sprite = SPR_AURA
	s.frame = C
	s.flags = $ & ~MF_PAIN & ~MF_SOLID & ~MF_NOGRAVITY
	s.scale = $*SMSLocalRNG(0, 4,s)
		if not P_IsObjectOnGround(s)
			s.z = s.floorz
		end
		local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_TNTDUST)
		if ghs and ghs.valid
			ghs.destscale = $*99
			ghs.fuse = 73
			ghs.tics = 1
		end
		if f.player and f.player.hypermysticsonic	
			s.benope = P_SpawnMobj(s.x, s.y, s.z, MT_BOSSEXPLODE)
			if not f.readyforonemoreplant
			S_StartSoundAtVolume(s, sfx_brakrx, 155)
			end
			A_SMSquake(f, 65*FRACUNIT, 3, 1400, true)
			if s.benope and s.benope.valid
			s.benope.scale = FRACUNIT*3
			end
		else
			s.benope = P_SpawnMobj(s.x, s.y, s.z, MT_FLAMEJETFLAME)
			if s.benope and s.benope.valid
			s.benope.flags = MF_NOGRAVITY|MF_SCENERY
			s.benope.destscale = $*3
			s.benope.scalespeed = $/2
			s.benope.fuse = SMSLocalRNG(20, 35,s)
			end	
			S_StartSound(s, sfx_s3k7f)
			S_StartSound(s, sfx_s248)
		end
		f.readyforonemoreplant = 2	
	end
end

addHook("MobjMoveCollide", CactiBurn, MT_SMSFIREBLAST)
addHook("MobjMoveCollide", CactiBurn, MT_HMSFIREBLAST)
addHook("MobjMoveCollide", CactiBurn, MT_HMSNOVABLAST)
addHook("MobjCollide", CactiBurn, MT_HMSNOVABLASTHB)
addHook("MobjMoveCollide", CactiBurn, MT_HMSNOVABLASTHB)

//Ignore most of SMS' attacks in the cage.
addHook("ShouldDamage", function(s, i, so)
	if (so and so.skin == "sms" and not (so.player and so.player.hypermysticsonic)) 
		if i and i.skin ~= "sms" //If SMS hit you indirectly while you're in the cage, brush it off. He's trying to cheat.
			if not (s.valid and (s.state >= S_EGGMOBILE4_RAISE1))
				return false --
			end
		end
	end
end, MT_EGGMOBILE4)

//Uhh, your flames are just fueling these guys!
addHook("ShouldDamage", function(s, i, so)
	if i and i.valid and (i.type == MT_SMSFIREBLAST)
	and s.health
		S_StartSound(s, sfx_gravch)
		local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_HMSNOVAWAVE)
		ghs.color = SKINCOLOR_FLAME
		ghs.scale = s.scale/2
		ghs.destscale = 5
		if (s.state == S_PYREFLY_FLY) or (s.state == S_PYREFLY_BURN)
			s.state = S_PYREFLY_BURN
			s.fuse = 1 --Used for their animation, it seems.
		end
		return false
	end
end, MT_PYREFLY)

//And these'll absorb your fire!
addHook("ShouldDamage", function(s, i, so)
	if i and i.type == MT_SMSFIREBLAST and s.health
		local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_HMSNOVAWAVE)
		ghs.color = SKINCOLOR_FLAME
		ghs.scale = s.scale/2
		ghs.destscale = 5
		S_StartSound(s, sfx_gravch)
		return false
	end
end, MT_UNIDUS)

--This hits it!
addHook("MobjMoveBlocked", function(s, i, line)
	if s.fusrodahfuse and (s.fusrodahfuse > 2)
		if line
			s.fusrodahwallexplode = line
		end
	end
end, MT_CMPHTRACKER)

--SRB2 intentionally forces you to lock onto these...despite them being hazards. Yeah, no.
local function SMSSmashEggBox(sp, i, s)
	if s and s.valid and s.skin == "sms" and s.player --Must be an SMS source.
	local p = s.player
		if p.homing or p.hypermysticsonic or (p.sms_dodging and p.sms_dodging > 6)	
		
		or i and i.valid --Objects that do it too!
		and ((i.type == MT_SMSFIREBLAST) or (i.type == MT_SPINFIRE) or (i.type == MT_ATOMDIVEPOP) or (i.type == MT_SMSPOWERBOMB))
		
		or sp.homingbounce
		
			local ghs = P_SpawnMobjFromMobj(sp,0,0,sp.height, MT_EXPLODE)
			ghs.renderflags = $|RF_SEMIBRIGHT
			ghs.destscale = $*99
			ghs.scalespeed = FRACUNIT/7
			
			local gag = P_SpawnMobjFromMobj(sp,0,0,sp.height, MT_THOK)
			gag.sprite = SPR_TVEG
			gag.renderflags = $|RF_SEMIBRIGHT|RF_NOCOLORMAPS
			gag.frame = C
			gag.fuse,gag.tics = 77,99
			gag.flags = MF_NOBLOCKMAP|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
			gag.rollangle = SMSLocalRNG(1,360,s)*ANG1
			gag.z = $+8*FRACUNIT
			SMS_GroundWindBlast(gag)
			SMS_WindBlast(gag)
			
			P_SetObjectMomZ(gag, 14*s.scale)	
			P_InstaThrust(gag, s.angle, 40*s.scale)
			if (s.player == displayplayer)
				S_StartSound(nil, sfx_bowl, p)
				S_StartSound(nil, sfx_behurt, p)			
			else
				S_StartSound(gag, sfx_bowl)
				S_StartSound(gag, sfx_behurt)
			end	
			sp.health = 0
			if (sp.type == MT_EGGMAN_GOLDBOX)
				sp.flags = $ & ~MF_SHOOTABLE		
			else
				sp.flags = $|MF_NOCLIP & ~MF_SOLID
			end
			sp.state = (sp.info.deathstate)+1
			return true --
		end
	end
end
addHook("MobjDeath", SMSSmashEggBox, MT_EGGMAN_GOLDBOX)
addHook("MobjDeath", SMSSmashEggBox, MT_EGGMAN_BOX)