--=============================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
		local p = v.player local t = s.target
		if p
			if (gametype == GT_COOP) and ((p.powers[pw_carry] == CR_PLAYER) or p.knucklewaving or p.iamcarryingsms)
				return false --
			end
		elseif (v.flags & MF_MONITOR)
			if v.health and t
				P_DamageMobj(v, t, t)
			end return false --
		end
	end
end, MT_SMSFIREBLAST)

addHook("MobjMoveCollide", function(s, v)
	if v and v.valid
		local p = v.player local t = s.target --v.player, but S.target!	
		if p
			if (gametype == GT_COOP)
			and ( (p.knucklewaving or p.iamcarryingsms or v.hmspickedup)
			or ((p.powers[pw_carry] == CR_PLAYER) and v.tracer and t and (t == v.tracer)) )	
				return false --
			end
		elseif (v.flags & MF_MONITOR)
			if v.health and t
				P_DamageMobj(v, t, t)
			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<<16 > 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 other random crap in here.
local function HMSCollide(h, v)
	local p,s = h.player,h
	if not p or p.sms_intoship return false end --
	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)
		if 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
	end
	if not h.skin == "sms" return end --
	
	if h.battlemodsms 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) return end --
	
	if (h.state == S_HMSCOMETHAYMAKER2) or (h.state == S_HMSCOMETHAYMAKER1) or h.starthmscombo
		return false --
	end
		
	if (v.flags & (MF_ENEMY|MF_BOSS)) //Specifically things with these flags die too.
	or (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)
	
		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*s.scale)
				v.rollangle = SMSLocalRNG(-39,39,v)*ANG1
				v.momz = 45<<16
				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.info.mass == 2006) or (v.type == MT_ROSY) //Wait wh-what?! Don't disintegrate Amy!
						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<<16)
						end
					end
					A_SMSquake(h, max(25<<16, min(80<<16, p.speed/2)), 3, 999, true)
					
					if v.facc
						local ghs = P_SpawnMobjFromMobj(s,0,0,0,MT_THOK)
						ghs.flags = MF2_DONTDRAW ghs.fuse = 2
						if v.facc.health v.facc.health = (v.facc.phase == 2) and 2 or 1 end
						P_DamageMobj(v, ghs, ghs, 1, DMG_ELECTRIC)
					else
						v.health = 1
						P_DamageMobj(v, h, h, 1, DMG_INSTAKILL)
						if v and v.valid and v.health --Still alive somehow? Let's ensure it gets the message.
							P_KillMobj(v, h, h)
						end
					end
					return false --
				end
			end
		end	
	elseif (v.type == MT_EGGSHIELD) --Melt these like they don't exist.
		if not (h.z > v.z+v.height) and not (v.z > h.z+h.height) and v.health
			P_KillMobj(v, h, h) return false --
		end
	elseif (v.type == MT_SMALLMACE) --Maceball destruction!
	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<<16, 5, 1340, true) return false --
		end
	elseif (v.type >= MT_CACTI1) and (v.type <= MT_CACTISMALLSEG) and (v.flags & MF_PAIN) --Destroy cacti explosively! Sorry, Cacee.
	
		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,v.frame = SPR_AURA,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<<16, 1+rng, 1400, true)
			h.readyforonemoreplant = 2
		end
		return false --
	end
	--Don't collide in this case at all.
	if (v.type == MT_EGGROBO1) or (v.type == MT_CANARIVORE_GAS) or v.facciolo or v.facc
	or (h.state == S_HMS_INSTANTWARP1) or (h.state == S_HMS_INSTANTWARP2) --You're intangible in this state.
		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
		local t = v.target local p = v.player
		if (v.flags & MF_MISSILE) and t and t.player and t.player.hypermysticsonic
		or p and p.hypermysticsonic
			if not (s.z > v.z+v.height+66<<16)
			and not (v.z > s.z+s.height+66<<16)
				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
		local t = v.target local p = v.player
		if (v.flags & MF_MISSILE) and t and t.player and t.player.hypermysticsonic
		or p and p.hypermysticsonic
			if s.valid and (s.state != S_SUSPICIOUSFACESTABBERSTATUE_BURST2)
			and not (s.z > v.z+v.height+66<<16) and not (v.z > s.z+s.height+66<<16)
				s.state = S_SUSPICIOUSFACESTABBERSTATUE_BURST2 return false --
			end
		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<<16)
		and not (v.z > s.z+s.height+66<<16)
			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.skin == "sms" and v.player
		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,s.momy = 0,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 and h.valid)
		return --
	end
	
	local p = v.player
	local t = v.target
	
	if p and (p.sms_comethaymaker or (v.state == S_SMSCOMETHAYMAKER2 or v.state == S_SMSCOMETHAYMAKER1))
	or (v.flags & MF_MISSILE) and t and t.player and t.player.hypermysticsonic
	
		if h.health
		and not (h.z > v.z+v.height)
		and not (v.z > h.z+h.height)
		and not h.Smashed
		
			h.Smashed = true
			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*FRACUNIT
			
			S_StartSound(h, sfx_brakrx)
			S_StartSound(h, sfx_bsnipe)
			P_Thrust(h, R_PointToAngle2(h.x, h.y, v.x, v.y), 30<<16)
			h.momz = 45<<16
			h.fuse = 300
			
			ghs = P_SpawnGhostMobj(h)
			ghs.destscale = 99*FRACUNIT
			ghs.fuse,ghs.tics = 12,13
			if v.player return false end
		end
	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 (s.skin == "sms" and v and v.valid and s.player) --Hook is for SMS. Also, pls exist.
		return --
	end 
	local p = s.player
	if (s.state == S_SMSCOMETHAYMAKER2) or (s.state == S_HMSCOMETHAYMAKER2)
		if (v.type == MT_FLINGRING)
		and v.cmphflung
		and SMS_NerfedMode(p)
			v.angle = s.angle
			v.cmphflung = 7 return false --
		end
	end	
	
	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<<16) or (s.x < st.x-st.radius-9<<16))
						and not ((s.y > st.y+st.radius+9<<16) or (s.y < st.y-st.radius-9<<16))
						and not ((s.z > st.z+st.height+9<<16) or (s.z < st.z-st.height-9<<16))
						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<<16, false)
							P_Thrust(st, s.angle+ ANGLE_180, 23<<16)
							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<<16)
				P_SetObjectMomZ(s, 8<<16)
				P_InstaThrust(s, s.angle, -7<<16)
				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 SMS_NerfedMode(p)
			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<<16)
					if not (v.momz*P_MobjFlip(v) > 20<<16)
						P_SetObjectMomZ(v, 7<<16, true)
					end
				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
				
				if not S_SoundPlaying(v, sfx_zoom)
					S_StartSound(v, sfx_zoom)
				end
				v.momx,v.momy,v.momz = $*-1,$*-1,$*-1 --Reverse projectile momentum!
				
				--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 --
			elseif v.facciolo or v.facc
				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.
					SMS_InvincibleBurst(s, p) 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<<16) and not (s.state == S_SMS_STOMP1 or s.state == S_SMS_STOMP2))
			or ((s.z > v.z+v.height+40<<16) 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<<16)
				or (v.z > s.z+s.height+111<<16)
					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 return false end --Don't hit springs when haymakering.
		
		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<<16, 8, 350, true)
					else
						A_SMSquake(s, 55<<16, 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,s.horstrongspring2 = true,true
					S_StartSound(s, sfx_sprong)
					A_SMSquake(s, 45<<16, 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 aaaaall 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<<16, cos(v.angle+(ANG1*90*i))),
						FixedMul(120<<16, sin(v.angle+(ANG1*90*i))),
						5<<16*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<<16, 6, 1999)
				else
					S_StartSound(s, sfx_wbreak)
					A_SMSquake(s, 75<<16, 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??
		return false --
	end
	if not (s.smsthrow and s.smsthrowog) 
	and not (s.hmsthrow and s.hmsthrowog) --Not thrown hard enough, ignore.
		return false --
	end
	if 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
	or (s.type == v.type)
		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,t.momy,t.momz = 0,0,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)|FF_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
			if v.facc P_DamageMobj(v) else P_KillMobj(v, t, SOURCE) end
			P_RadiusAttack(t, SOURCE, max(80*SOURCE.scale, min(500<<16, 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<<16
						P_FlashPal(z, PAL_MIXUP, 2)
						A_SMSquake(z.mo, max(75<<16, min(20<<16, t.height*2)), 10, 1)
					elseif DIST < 1250<<16
						A_SMSquake(z.mo, max(40<<16, min(10<<16, 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)
	local t = s.target local p = t and t.player --s.target is the player!
	if not (t and p and m and m.valid) return false end --Target and m must be valid at all times. 

	--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 return false end -- Cancel
		
		 --Find a method of succeeding!
		local method
		if m.dummyhbx and m.target
			method = 1
		elseif m.player
			if SMS_NerfedMode(p)
				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 false end
		
		if s.hmsspincheck2 and not m.dummyhbx
			if (m.type == MT_BIGMINE)
			or (m.health <= 0) 
			or not (m.flags & MF_SHOOTABLE)
				return false --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 false
		
	 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 false 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.
		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) or m.facc
				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 false --
		
	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<<16)
				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,m.momy = $/2,$/2
						m.hmstorvictimang = z.drawangle
						P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), 6<<16)
					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<<16
					m.momx = $*100/99
					m.momy = $*100/99
					P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), 10<<16) //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<<16
				if (m.target.z < m.target.ceilingz-300<<16)
					P_Thrust(m, R_PointToAngle2(m.target.x, m.target.y, t.x, t.y), 36<<16) //Suck in
				else
					P_Thrust(m, R_PointToAngle2(m.target.x, m.target.y, t.x, t.y), -8<<16) //Shoot out
				end
				P_Thrust(m, R_PointToAngle2(m.target.x, m.target.y, t.x, t.y)-ANGLE_90+ANG2, 12<<16) //Tornadoooo
				m.target.angle = $+ANGLE_45
			end
			
		else
		
			m.momz = $+4<<16
			if (m.z < m.ceilingz-300<<16)
				P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), 36<<16) //Suck in
			else
				P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y), -8<<16) //Shoot out
			end
			P_Thrust(m, R_PointToAngle2(m.x, m.y, t.x, t.y)-ANGLE_90+ANG2, 12<<16) //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<<16, 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<<16, 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)
			if v.facc
				P_DamageMobj(v) --Your target is the source!
			else
				P_KillMobj(v, s, (s.target) or s)
			end
		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,DORETURN = v.player, nil	
		
		//"you're going STRAIGHT TO HELL!" --Shadow the Philosopher: 2005
		if SMS_NerfedMode(p)
		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)
			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<<16 > 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 ( SMS_NerfedMode(p) 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<<16)
					else
						P_NukeEnemies(s, s, 400<<16)
					end
					if v.player
						P_InstaThrust(v, v.angle, -80<<16)
						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<<16)
		and not (s.z+s.height+20<<16 < 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 SMS_NerfedMode(p)
		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<<16)
				P_SetObjectMomZ(v, 8<<16)
				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 
		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<<16) --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<<16
		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<<16*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<<16)
		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)<<16
				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<<16)
				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<<16)
				else
					P_SetObjectMomZ(t, 4<<16, 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<<16 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)|FF_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))<<16 )
					P_SetObjectMomZ(ghs, ((RNG1/3)+5)<<16)
					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)|FF_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|FF_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<<16
				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<<16, 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|FF_TRANS90
			else
				ghs.sprite = SPR_SMSO
				ghs.frame = S|FF_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.
--Global MobjDeath 
addHook("MobjDeath", function(s, i, so, dmgtype)
	if not (so and so.skin == "sms" 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<<16)
				end
				if not (p.hypermysticsonic < 33)
					s.hmsgodaurad = true
					A_SMSquake(so, P_IsObjectOnGround(so) and 25<<16 or 40<<16, 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 */
		if not s.valid return end --We still here? SMS too, not LSA-ing? //and not p.mslightspeedattack 		
	end
	
	local CANGORE = false
	if (s.flags & (MF_ENEMY|MF_BOSS)) --Assign Death Trackers!
		if i 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<<16 or abs(i.momy) > 3<<16)
					SMS_AssignDeathTracker(so, s, nil, 0, i) 
				else
					SMS_AssignDeathTracker(so, s, nil, 0) --1BH, 2ZAP, 4FIRE, 8FLURRY
				end
			end
		end
		local DEATHSFX,DEATHSTATE = (mobjinfo[s.type].deathsound) or 0,(mobjinfo[s.type].deathstate) or 0
		if (DEATHSFX == sfx_pop) and (DEATHSTATE >= S_XPLD_FLICKY) and (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)
			CANGORE = true
		end		
	end	
	
	//It's the Robot Gore code. The robot gibs.
	if CANGORE == true 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 GIBSFX = SMSLocalRNG(1,3,s) //Custom Robot Deaths
			S_StartSound(ghs, (GIBSFX==1) and sfx_harrg1 or (GIBSFX==2) and sfx_harrg2 or sfx_harrg3)
			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
					
				ths.color = SMSenemycolor(s)
				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)<<16
					elseif (so.SMShomingspeed) and (so.SMShomingspeed > 13<<16)
						ths.enethrust = so.SMShomingspeed*6/7
					else
						ths.enethrust = 10<<16
					end
				else
					ths.enethrust = (p.speed > 20*so.scale) and p.speed*6/7 or 16<<16
				end
				ths.eneanglethrust = R_PointToAngle2(s.x, s.y, so.x, so.y)
				ths.enex,ths.eney,ths.enez = s.x,s.y,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<<16, 2, 300, true)
			if (s.flags & MF_ENEMY) and not (s.flags & MF_BOSS) //"Inflate" and then blow up! Enjoy, Deviantart
			and (DEATHSFX == sfx_pop) and (DEATHSTATE >= S_XPLD_FLICKY) and (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
		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 (so.skin == "sms" or so.player.mslightspeedattack)) return end --
	if s.valid and (s.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR))
		local p = so.player
		if p.hypermysticsonic 
			if (p.hypermysticsonic > 30) and not so.starthmscombo and not s.facc
				P_KillMobj(s, i, so) return false --Any damage from Hyper Mystic Sonic is instant death
			end
		else
			if p.mslightspeedattack
				if not p.lsdeadjump and so.target == s
					p.lsdeadjump = 1
					so.target,so.lsatarget = nil,nil
					if so.lsaringdebt and 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<<16) //Don't insta-crush players who homing attack you
					end
				end
			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,s.frame = SPR_AURA, 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<<16, 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<<16
			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)