--=================================COLLISION HANDLERS===========================
--Hooks related to object, player, and wall collissions are usually placed here.
--==============================================================================

--List of stuff that can't be picked up sometimes.
local SAMhands = {
MT_FLINGEMERALD,
MT_REDFLAG,
MT_BLUEFLAG,
MT_BOUNCERING,
MT_RAILRING,
MT_INFINITYRING,
MT_AUTOMATICRING,
MT_EXPLOSIONRING,
MT_SCATTERRING,
MT_GRENADERING,
}
--Player colission hook!
local function SamusCollide(s, m) if not (s.player and m and m.valid) return end --
	local p = s.player

	if m.sambeamreflected and not (m.target and m.target.player)
		if not (s.z > m.z+m.height) 
		and not (m.z > s.z+s.height)
			P_DamageMobj(s, m.target, m.target)
		end
	elseif p.sam_intoship or m.metroidleech --Metroid leech are the hypermode type enemies, not actual Metroids!
		return false --
	elseif (m.type == MT_METROID) or (m.type == MT_METROIDLARVA)
		if (m.state == S_MTRDFROZEN)
			if not YouAreSamus(s) // and m.health  
			and (P_PlayerCanDamage(p, s)) --Let non-Samus characters kill frozen Metroids!
				if (s.z <= m.z+m.height)
				and (m.z <= s.z+s.height)
					P_SamFP(p, PAL_RECYCLE, 4) 
					A_samquake(s, 15<<16, 8, 1)
					s.momx,s.momy = $*-1,$*-1
					P_Thrust(s, R_PointToAngle2(s.x, s.y, m.x, m.y), -8*m.scale)
					P_SetObjectMomZ(s, max(s.momz, 2<<16), true)
					P_DamageMobj(m, s, s) return false --
				end
			end
		else
			if s.skin == "dirk" and (P_PlayerCanDamage(p, s)) --Dirk OP
				if (s.z <= m.z+m.height) and (m.z <= s.z+s.height)
					s.momx = $*-1 s.momy = $*-1
					P_DamageMobj(m, s, s) S_StartSound(s, sfx_icfr)
					P_SamFP(p, PAL_RECYCLE, 4) A_samquake(s, 15<<16, 8, 1) return true --
				end
			elseif s.health and not (m.latchtarget and m.latchtarget == s)
				if p.sam_shinesparkfly 
				or p.sam_screwattacking
				or p.sams_morphcd and p.sammorphed
				or p.powers[pw_flashing] 
				or m.stuntroid or m.paintroid or m.sidesweep or s.metroidsnack
					return false --Can't be grabbed in these states
				end
				if not (s.z > m.z+m.height) ---You got grabbed!
				and not (m.z > s.z+s.height)
					m.latchtarget = s
					if (p.pflags & PF_SPINNING) or p.sammorphed
						s.momx,s.momy = $/6,$/6
					else
						s.momx,s.momy,s.momz = $/3,$/3,$/2
					end
				end
			end
		end 
		return --
	elseif m.sam_deathball
		if not (s.z > m.z+m.height)
		and not (m.z > s.z+s.height + (s.height/2) )
			P_DamageMobj(s, m, m)
		end 
	elseif (m.flags & MF_MONITOR)
		if (gametype == GT_COOP)
			if m.gotmonalready and m.gotmonalready.valid and m.gotmonalready.lasttouch == s
				m.gotmonalready.fuse = 3
				m.gotmonalready.scale = m.scale*5/6
				m.gotmonalready.eflags = m.eflags return false --
			end
			local ALREADYGOT = false
			if m.gotplayers
				for i,z in pairs(m.gotplayers)
					if (p == z)
						ALREADYGOT = true break --You already got that, you greedy shit.
					end
				end	
			end			
			if ALREADYGOT == true --Can't open already-obtained monitors!
				local ghs = P_SpawnGhostMobj(m)
				ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
				ghs.destscale = m.scale*10 
				ghs.tics = 30
				
				m.gotmonalready = P_SpawnMobj(m.x, m.y,m.z, MT_FAKESAMUSTHOK)
				m.gotmonalready.renderflag = RF_FULLBRIGHT|RF_NOCOLORMAPS
				m.gotmonalready.state = S_ZAPS1
				m.gotmonalready.lasttouch = s
				return false --
			end
		end
	elseif (m.type == MT_CYBRAKDEMON_FLAMEREST) or (m.type == MT_CYBRAKDEMON_NAPALM_FLAMES) 
		--In this fight, these flames are only scenery.
		if (gamemap == 28) and (curWeather == PRECIP_STORM_NORAIN)
			m.state = S_NULL return false --
		end 
		return --		
	end	
	
	if not YouAreSamus(s) or not (m and m.valid) return end --Must be Samus from here on out!
	
	if (m.type == MT_BLUEFLAG) or (m.type == MT_REDFLAG)
		if p.samnoflag
			if not p.samnoflagmessage
				p.samnoflagmessage = 105
			end
			return false --
		end 
		return --	
	elseif p.samhands --You can't grab stuff from the local SAMMHANDS list above when "p.samhands" is active.
		for i = 1,#SAMhands
			if m.type == SAMhands[i] return false end --
		end	
	end
	
	
	if not m.health return end --Mo must have health form now on.
	
	--Speed booster/Boost Ball/Death Ball special colissions!
	if p.samspbschargefull or p.bsblboostattack or s.sam_deathball
		if (m.flags & (MF_ENEMY|MF_BOSS)) and (m.flags & MF_SHOOTABLE)
		or (m.type == MT_ICBLOCK) 
		or (m.type == MT_TNTBARREL)
			if not (s.z > m.z+m.height)
			and not (s.z+s.height < m.z)
			and not (m.type == MT_GSNAPPER)
				if (m.type == MT_ICBLOCK)
					m.icethisfool = true
					if p.bsblboostattack
						p.bsblboost,p.bsblboostattack = 0,0
						P_SetObjectMomZ(s, 15<<16, true)
						s.momz = $*-1
					end
					
				elseif s.sam_deathball and not (m.flags2 & MF2_FRET)
				
					P_StartQuake(30<<16, 6)
					P_SamFP(p, PAL_MIXUP, 2)
					
					local ghs = P_SpawnMobj(s.x, s.y, s.z+s.height, MT_SAMUSTHOK)
					ghs.renderflags = RF_NOCOLORMAPS|RF_FULLBRIGHT
					ghs.height = 40<<16
					ghs.eflags = s.eflags
					ghs.target = m
					ghs.sprite = SPR_MPR9
					ghs.frame = (Z+1)|FF_TRANS10
					ghs.scale = 160000
					ghs.deathbolt = true
					if not m.player
						MetroidVanguard.DeathBallImpact(s,s)
					end
					S_StartSound(s, sfx_bkpoof)
					A_SAMUSEXPLOSIONS(m, 13, MT_SMOKE, false, 2000, SKINCOLOR_YELLOW)
					A_SAMUSEXPLOSIONS(m, 8, MT_MPR1, false, 17000, SKINCOLOR_GOLD)
					P_DamageMobj(m, s, s, 2)
				else
					P_DamageMobj(m, s, s)
					if m and m.valid and not m.health
						P_SetObjectMomZ(m, 200000, false)
						P_Thrust(m, s.angle, p.speed+12<<16)
					end		
					if p.bsblboostattack 
					and (((s.eflags & MFE_VERTICALFLIP) and s.momz > 0) or (s.momz < 0 and not (s.eflags & MFE_VERTICALFLIP)))
						local ZFLIP = 1 if (s and s.valid) ZFLIP = P_MobjFlip(s) end
						s.momz = (-$)+(ZFLIP<<16)
					elseif p.samspbschargefull
						A_samquake(s, (p.speed/4)+7<<16, 3, 100)
					end
				end
			end
			return false --
		end
	end
	
	if m.player  --You can toss players when rolling in place.
		if (p.pflags & PF_STARTDASH) and p.sammorphed and (p.pflags & PF_SPINNING)
		and not ((gametype == GT_COOP or gametype == GT_RACE) and p.morphcoop)
			local tp = m.player
			if tp.panim != PA_IDLE or tp.speed or tp.cmd.forwardmove or m.momz
				if not (tp.pflags & PF_JUMPED)
				and not (s.z > m.z+m.height) 
				and not (m.z > s.z+s.height+(s.height/2))
					m.momz = 10*m.scale + 9<<16 * P_MobjFlip(m)
					P_Thrust(m, s.angle, (40*m.scale)+10<<16)
					tp.drawangle = s.angle
					tp.pflags = $|PF_JUMPED & ~(PF_SPINNING|PF_THOKKED)
					S_StartSound(m, sfx_zoom) 
					S_StartSound(m, sfx_bnce1)
					S_StartSound(m, sfx_bnce2)
				end
			end
		end
		return --
		
	elseif (m.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)) 
	
		if (p.pflags & PF_JUMPED)
		or (p.pflags & PF_STARTDASH) and (p.pflags & PF_SPINNING)
			if (m.flags & MF_MONITOR) 	
				if p.sammorphed and s.samspinpad
				or p.sam_screwattacking or s.sam_deathball
					return false --Nope.
				end
				if not (m.flags & MF_NOCLIP) --Bounce away from monitors when trying to jump on them as Samus.
					if not (s.z > m.z+m.height)
					and not (s.z+s.height < m.z)
						local ANGLE = s.angle-ANGLE_180
						local THRUST = max(7*s.scale, 7<<16)
						if p.cmd.sidemove and not (twodlevel or (s.flags2 & MF2_TWOD)) 
							ANGLE = $-(p.cmd.sidemove*ANG1)
						end
						if (p.cmd.forwardmove < 0)
							THRUST = $+(9000*abs(p.cmd.forwardmove))
						end
						P_InstaThrust(s, ANGLE, max((p.speed/4)+THRUST, THRUST))
						local ZFLIP = 1 if (s and s.valid) ZFLIP = P_MobjFlip(s) end
						if (s.momz*ZFLIP < 5*s.scale)
							P_SetObjectMomZ(s, 5<<16)
						end
						if not S_SoundPlaying(s, sfx_s1a7)
							S_StartSound(s, sfx_s1a7)
						end
						if p.sam_spacejumped
							p.sam_spacejumped = false
						end
					end
				end
				return false --			
			elseif not p.samspbschargefull and not p.bsblboostattack and not s.sam_deathball
				if not (s.z > m.z+m.height) and not (s.z+s.height < m.z)
					if ((m.type == MT_SPRINGSHELL) or (m.type == MT_YELLOWSHELL))
					and (s.z > (m.z+(m.height/2)) )
						m.state = (m.type==MT_YELLOWSHELL) and S_YSHELL_SPRING1 or S_SSHELL_SPRING1
						P_SetObjectMomZ(s, (m.type == MT_YELLOWSHELL) and 18<<16 or 13<<16)
					else
						P_DamageMobj(s, m, m)
					end
				end
				return false --
			end
		end return --
	elseif (m.type == MT_EGGSHIELD) 
		if p.bsblboost or p.sam_shinesparkfly or p.sam_screwattacking or s.samspinpad or s.spiderballing
			if not (s.z > m.z+m.height) --Height check.
			and not (s.z+s.height < m.z)
				if not (p.bsblboost and not m.info.mass) --Legacy stuff.
					if s.spiderballing and not mobjinfo[m.type].mass
						s.momz = 0
					end
					s.sams_shineframe = 0
					s.spiderballing = 0
					p.bsblboost = 0
					p.sam_shinesparkfly = 0
					if p.sam_screwattacking
						CancelScrewAttack(s,p)
					end
					if s.samspinpad
						s.samspinpad = 4
					end
					if m.health and p.bsblboost
						P_InstaThrust(s, s.angle, -6<<16)
						P_SetObjectMomZ(s, 3<<16, false)
						P_KillMobj(m)
					end
				end
			end
		end
		return --
		
	elseif (m.flags & MF_SPRING)
	
		if not (s.z > m.z+m.height)
		and not (s.z+s.height < m.z)
		
			if (p.bsblboost or p.sam_shinesparkfly or p.sam_screwattacking or s.samspinpad or s.spiderballing)
				if not (p.bsblboost and not m.info.mass)
					if s.spiderballing and not mobjinfo[m.type].mass
						s.momz = 0
					end
					s.sams_shineframe = 0
					s.spiderballing = 0
					p.bsblboost = 0
					p.sam_shinesparkfly = 0
					if p.sam_screwattacking
						CancelScrewAttack(s,p)
					end
					if s.samspinpad
						s.samspinpad = 4
					end
				end
			end
			if not m.info.mass --Horizontal spring hit!
				if not p.sammorphed
					s.samspinpadconfirmed = 12
					s.angle = m.angle
				else
					s.minormorphboost = 1
					if p.bsblboost or p.bsblboostattack
						S_StartSound(s, sfx_sprong)
						p.bsblboost = 0
						p.bsblboostattack = 0
						s.minormorphboost = (m.info.damage*2)
					end					
					p.pflags = $|PF_SPINNING & ~PF_JUMPED
					s.spiderballing = 0
					s.samspinpad = 50
					p.sams_notyet = 3
					s.samspinpadconfirmed = 49
					s.angle = m.angle
					P_InstaThrust(s, s.angle, (m.info.damage*50/38))	
				end
				s.sam_forcemorphslope = 50
				if (m.type >= MT_BOOSTERSEG) and (m.type <= MT_REDBOOSTER)
					if (m.flags2 & MF2_AMBUSH)
						p.bsblboost = 0
						s.scale = 42001
						p.sam_spacejumpedboost = 0
						p.thrustfactor = skins[s.skin].thrustfactor
						p.accelstart = skins[s.skin].accelstart
						p.sams_notyet = 3
						SAM_CMD(p, "lol", "lol", "false", "lol")						
					--	SamCam(p, true, true)
						p.sammorphed = true
						s.sammorphedghs = 5
						S_StartSound(s, sfx_smorph)
						p.sams_beamcombocharge = 0
						local ghs = P_SpawnGhostMobj(s)
						ghs.destscale = ghs.scalespeed
						CancelScrewAttack(s,p)
					end
				end
			end
			return --
		end
	elseif (m.type == MT_ICBLOCK)
		if m.icethisfool and p.sammorphed
			return false --	
		end		
	end
end
addHook("MobjCollide", SamusCollide, MT_PLAYER)
addHook("MobjMoveCollide", SamusCollide, MT_PLAYER)

local function IntangibleFlames(s)
	if (gamemap == 28) and (curWeather == PRECIP_STORM_NORAIN)
	and s and s.valid
		s.flags = $|MF_NOCLIPTHING & ~(MF_PAIN|MF_SOLID|MF_SPECIAL)
	end
end
addHook("MobjSpawn", IntangibleFlames, MT_CYBRAKDEMON_FLAMEREST)
addHook("MobjSpawn", IntangibleFlames, MT_CYBRAKDEMON_NAPALM_FLAMES)

--3 seperate functions for Spazer Charge and Combo, and Ice to reduce lag
local function BeamPopICC(s, m) if not (m and m.valid) return end --Duh...
	local t = s.target
	
	if (m.flags & MF_MONITOR)
		if not (m.flags & MF_NOCLIP)
			if not (s.z > m.z+m.height) 
			and not (s.z+s.height < m.z)
				A_SAMITEM(s, m)
			end
		end
		return false --	
	end
	if not m.health return end --Target needs health now.
	
	if m.samreflecttime 
	or m.iceresist and (s.type == MT_ICSHOT)
		if not (s.z > m.z+(m.height*10/7))
		and not (m.z > s.z+s.height)
			if not s.sambossreflect
				SamusBeamReflect(s, m)
				if t and t.player
					t.player.sam_resisthud = 18
				end
				s.sambossreflect = (t) and t or m	
				s.target = m
				return false --
			end
		end
	end
	if (m.flags & (MF_ENEMY|MF_BOSS))
		if not (s.z > m.z+m.height)
		and not (s.z+s.height < m.z)
			if (m.flags & MF_SHOOTABLE) and not m.frozenbox 
				if (m.type == MT_METROIDLARVA)
					P_DamageMobj(m, s, t)
					if t and t.player t.player.sam_crithud = 18 end
					P_ExplodeMissile(s) return --Don't freeze, just kill.
				end
				if (s.type == MT_ICSHOT) and m.iceresist
					if t and t.player
						t.player.sam_resisthud = 18
					end
				else
					if m.iceweak
						if t and t.player
							t.player.sam_crithud = 18
						end
						A_SAMUSEXPLOSIONS(m, 7, MT_SMOKE, false, 1000, SKINCOLOR_BLACK)
						A_SAMUSEXPLOSIONS(m, 7, MT_MPR8, false, 90000, SKINCOLOR_SKY, "nii")
						A_SAMUSEXPLOSIONS(m, 4, MT_MPR8, false, 8000, SKINCOLOR_WHITE, "nii")
					end
					if (m.type == MT_METROID) or (m.type == MT_METROIDLARVA) 
					or (m.type == MT_SUPERMETROID) or (m.type == MT_METROIDPART)
						P_DamageMobj(m, s, s) --Metroids use custom frozen behaviour!
					else
						m.frozenmobj = P_SpawnMobj(m.x, m.y, m.z, MT_FREEZETRACKER)
						m.frozenmobj.chozobox = m
						m.frozenmobj.samusshooter = t
					end
					P_ExplodeMissile(s)
				end
			end
		end
	else	
		if (m.type == MT_BIGMINE)
			if not (s.z > m.z+m.height)
			and not (s.z+s.height < m.z)		
				P_SpawnMobj(m.x, m.y, m.z, MT_UWEXPLODE)
				m.state = S_NULL return false --
			end
		elseif (m.type == MT_ICBLOCK)
			if not (s.z > m.z+m.height)
			and not (s.z+s.height < m.z)
				if (s.type == MT_ICSHOT)
					m.refreshiceblock = true
				else --Charged shots and non-ice shots will destroy the ice prison.
					m.icethisfool = true
				end return true --
			end
		end
	end
end

--Spazer Charge Shot?
local function BeamPopSPC(s, m) if not (m and m.valid) return end
	local t = s.target
	if (m.flags & MF_MONITOR) 
		if not (m.flags & MF_NOCLIP)
			if not (s.z > m.z+m.height)
			and not (s.z+s.height < m.z)
				A_SAMITEM(s, m)
			end
		end return false --
	end	
	if not m.health return end --
	
	if (m.flags & (MF_ENEMY|MF_BOSS)) or m.player 
	
		if not (s.z > m.z+m.height+250<<16) --Super generous height checks!
		and not (s.z+s.height < m.z-400<<16)
		
			if m.samreflecttime --Only certain objects can reflect entire beam combos.
				if not (s.z > m.z+(m.height*10/7))
				and not (m.z > s.z+s.height)
					if not s.sambossreflect
						SamusBeamReflect(s, m)
						if t and t.player
							t.player.sam_resisthud = 18
						end
						s.sambossreflect = (t) and t or m
						s.target = m
					end
					return false --
				end	
			end
			
			if (m.type == MT_METROID) or (m.type == MT_SUPERMETROID)		
				SamusBeamReflect(s, m) return false --	
			end
			if (m.flags & MF_SHOOTABLE)
			and not (m.player and not G_GametypeHasSpectators())
				if not (t and t.valid and t == m)
					if not (m.flags2 & MF2_FRET) and not (m.player and m.player.powers[pw_flashing])
						if m.spcombod
							S_StartSound(s.meleeblast, sfx_smexp)
							m.spcombodoverkill = true
						end
						if not m.spcombod or (leveltime%2==0)
							s.meleeblast = P_SpawnMobj(s.x, s.y, s.z, MT_SPAZERSHOCK)
							s.meleeblast.rocket = m
							s.meleeblast.mojo = true
							m.spazershocked = true
							m.spcombod = true
							S_StartSound(s.meleeblast, sfx_smexp)
							s.meleeblast.angle = s.angle
						end
						if t and s.meleeblast and s.meleeblast.valid
							s.meleeblast.target = t
						end
						S_StartSound(s.meleeblast, sfx_smexp)
					end
				end
				if m.player and not (t and t.valid and m == t) 
					P_DamageMobj(m, s, t) return true --
				end
			end
		end
		return --
	else		
		if not (s.z > m.z+m.height)
		and not (s.z+s.height < m.z)
			if (m.type == MT_BIGMINE)
				P_SpawnMobj(m.x, m.y, m.z, MT_UWEXPLODE)
				m.state = S_NULL return false --	
			elseif (m.type == MT_ICBLOCK)
				m.icethisfool = true return true --
			end
		end
	end
end

--BeamPopSPS is used EXCLUSIVELY for the Spazer Charge Shot.
local function BeamPopSPS(s, m) if not (m and m.valid) return end local t = s.target	
	local ZOFFSET = s.meleeshot and 60<<16 or 1 --Bigger hitbox at melee range!
	if (s.z > m.z+m.height+ZOFFSET)
	or (s.z+s.height < m.z-ZOFFSET)
		return false --
	end
	
	if (m.flags & MF_MONITOR) 
		if not (m.flags & MF_NOCLIP)
			A_SAMITEM(s, m) return false --
		end return --
	elseif m.samreflecttime
		if not s.sambossreflect
			SamusBeamReflect(s, m)
			if t and t.player
				t.player.sam_resisthud = 18
			end
			s.sambossreflect = (t) and t or m
			s.target = m
		end
		return false --
	elseif (m.type == MT_BIGMINE)
		P_SpawnMobj(m.x, m.y, m.z, MT_UWEXPLODE)
		m.state = S_NULL return false --
	elseif (m.type == MT_ICBLOCK)
		m.icethisfool = true return true --	
	end
	if not m.health return end --
	
	if s.meleeshot == nil or m.spazershocked
		return false --
	end
	if (m.flags & (MF_ENEMY|MF_BOSS)) or m.player
		if (m.flags & MF_SHOOTABLE)
		and not (m.player and not G_GametypeHasSpectators())
		and not (m.type == MT_METROID)
		and not (m.type == MT_SUPERMETROID)		
			if s.meleeshot
				s.meleeblast = P_SpawnMobj(s.x, s.y, s.z, MT_SPAZERSHOCK)
				s.meleeblast.rocket = m
				m.spazershocked = true
				s.meleeblast.angle = s.angle
				if t
					s.meleeblast.target,s.meleeblast.tracer = t,t
				end
				S_StartSound(s.meleeblast, sfx_smexp)
				S_StartSound(s.meleeblast, sfx_smexp)
				P_ExplodeMissile(s) return false --
			
			elseif not m.spazershocked
				P_DamageMobj(m, s, s.target, 1) return true --
			end
			if m.player and not (t and t.valid and m == t) 
				P_DamageMobj(m, s, t) return true --
			end
		end
	end
end

--Plasma Shot! Both variants
local function BeamPopPLS(s, m)
	if not (m and m.valid) return false end --Always return false for Plasma Beam Shots so they pierce enemies/monitors/players!
	if (m.flags & MF_MONITOR)
		if not (m.flags & MF_NOCLIP)
			local ZOFFSET = s.gattlingvortex and 70<<16 or (s.type == MT_PLCHARGESHOT) and FRACUNIT*5 or 0
			if not (s.z+s.height+ZOFFSET < m.z)
			and not (s.z > m.z+m.height+ZOFFSET)
				A_SAMITEM(s, m)
			end
		end return false --
	end
	if not m.health return false end
	local t = s.target
	
	if m.samreflecttime
		if not (s.z > m.z + (m.height*10/7) )
		and not (m.z > s.z+s.height)
			if not s.sambossreflect
				SamusBeamReflect(s, m)
				if t and t.player
					t.player.sam_resisthud = 18
				end
				s.sambossreflect = (t) and t or m		
				s.target = m
			end
		end return false --	
	elseif m.player 
		if G_GametypeHasSpectators()
			local ZOFFSET = s.height*2
			if s.gattlingvortex 
				ZOFFSET = max(s.height*3, 170<<16)
			elseif (s.type == MT_PLCHARGESHOT)
				ZOFFSET = s.height*3
			end
			if not (s.z+ZOFFSET < m.z) 
			and not (s.z > m.z+m.height+ZOFFSET)
				P_Thrust(m, s.angle, 145355)
				P_DamageMobj(m, s, t, 1)
				if not s.pierced
					s.pierced = true
				end
			end
		end return false --
	elseif (m.flags & (MF_ENEMY|MF_BOSS)) or (m.type == MT_EGGSHIELD)
		if (m.flags & MF_SHOOTABLE) or (m.type == MT_EGGSHIELD)
			local ZOFFSET = s.height*2
			if s.gattlingvortex  --Gattling vortex has ridiculous vertical range.
				ZOFFSET = max(s.height*3, 220<<16)
			elseif (s.type == MT_PLCHARGESHOT) --Charge Shot has more vertical range
				ZOFFSET = s.height*3
			end
			if not (s.z+ZOFFSET < m.z) 
			and not (s.z > m.z+m.height+ZOFFSET)			 
				if (m.type == MT_EGGSHIELD)
					if m.target and m.target.valid and (m.target.type == MT_EGGGUARD) and m.target.health
						m.target.flags = $|MF_ENEMY|MF_SHOOTABLE & ~MF_NOCLIPTHING --You're becoming vulnerable RIGHT now.
						m.target.flags2 = $ & ~MF2_FRET
					end
					P_KillMobj(m, s, t)
				else
					P_DamageMobj(m, s, t, 1)
				end
				return false --
			end
		end
	elseif (m.type == MT_BIGMINE)
		if not (s.z > m.z+m.height) and not (s.z+s.height < m.z)
			P_SpawnMobj(m.x, m.y, m.z, MT_UWEXPLODE)
			m.state = S_NULL
		end return false --
	elseif (m.type == MT_ICBLOCK)
		if not (s.z > m.z+m.height) and not (s.z+s.height < m.z)
			m.icethisfool = true
		end return false --
	end
end
--Monitor breaker colider. Super simple.
local function BeamPopMBreaker(s, m)
	if m and m.valid and (m.flags & MF_MONITOR)
		if not (s.z > m.z+m.height) 
		and not (s.z+s.height < m.z)	
			A_SAMITEM(s, m)
		end
	end return false --
end
--The most generic Beam Pop code!
local function BeamPop(s, m) if not (m and m.valid) return end --
	if (m.flags & MF_MONITOR)
		if not (m.flags & MF_NOCLIP)
			local ZOFFSET = ((s.type == MT_WVCHARGESHOT) and s.bufferguard)  and 99<<16 or 0
			if not (s.z > m.z+m.height+ZOFFSET) --Height checks.
			and not (s.z+s.height+ZOFFSET < m.z)
				A_SAMITEM(s, m)
			else
				return false --
			end
		end return --
	end
	if not m.health return false end --
	
	local t = s.target
	
	if m.samreflecttime --Oh shit, this object can reflect us?! 
	or m.missileresist and (s.type == MT_SAMMISSILE) --Oh crap, this object resists missiles!
		if not (s.z > m.z + (m.height*10/7) )
		and not (m.z > s.z+s.height)
			if not (s.type == MT_HBSHOT) --Hyper Beam and the Wave Beam Combo's unfired shots are unreflectable!
			and not ((s.type == MT_WVCHARGESHOT) and s.bufferguard)
				if not s.sambossreflect
					SamusBeamReflect(s, m)
					if t and t.player
						t.player.sam_resisthud = 18
					end
					s.sambossreflect = (t) and t or m
					s.target = m
				end
				return false --
			end
		end
	end
	if (m.flags & (MF_ENEMY|MF_BOSS)) or m.player
		if m.player and not G_GametypeHasSpectators() or (m == t)
			return false --Players are only targets in Hunters/RingSlinger gamemodes.
		end
		if (m.flags & MF_SHOOTABLE)
			if (s.type == MT_SPSHOT)
				if not (s.z > m.z+m.height)
				and not (s.z+s.height < m.z)
					local PairedShot = s.lilbro1
					for i = 1,3 --The other shots disappear alongside you like in Super Metroid for balancing reasons.
						if i==2 PairedShot=s.lilbro2 end
						if i==3 PairedShot=s.lilbro3 end
						if PairedShot and PairedShot.valid and PairedShot != s
							PairedShot.flags = $|MF_NOCLIPHEIGHT & ~(MF_MISSILE|MF_SOLID|MF_PAIN|MF_FIRE)
							PairedShot.momx,PairedShot.momy,PairedShot.momz,PairedShot.fuse = 0,0,0,18
							P_ExplodeMissile(PairedShot)
						end
					end
				end return --
			elseif (s.type == MT_WVSHOT)
				if m.player and (t and t.type == MT_WVCHARGESHOT)
					return false --Not if you're WaveBeam Combo explosion debris.
				end
				if not (s.z > m.z+m.height)
				and not (s.z+s.height < m.z)
					for i = 1,2
						local PairedShot = (i==1) and s.lilbro1 or s.lilbro2
						if PairedShot and PairedShot.valid 
							PairedShot.flags = $ & ~(MF_BOUNCE|MF_GRENADEBOUNCE)
							if PairedShot != s
								PairedShot.flags = $|MF_NOCLIPHEIGHT & ~(MF_MISSILE|MF_SOLID|MF_PAIN|MF_FIRE)
								PairedShot.momx,PairedShot.momy,PairedShot.momz,PairedShot.fuse = 0,0,0,18								
								P_ExplodeMissile(PairedShot)
							end
						end
					end
					if s and s.valid
						s.flags = $ & ~(MF_BOUNCE|MF_GRENADEBOUNCE) --Remove bounce flags!
					end
				end
				return --		
			elseif (s.type == MT_WVCHARGESHOT)
				local ZOFFSET = (s.bufferguard and (m.flags & (MF_ENEMY|MF_BOSS))) and 30<<16 or 0
				if not (s.z > m.z+m.height+ZOFFSET) --Height check.
				and not (s.z+s.height < m.z-ZOFFSET)
					P_DamageMobj(m, s, t, 1) return true --
				end return --
	
			elseif (s.type == MT_SAMMISSILE) or (s.type == MT_PBCHARGESHOT) --Standard explosive blasts.
				if not (s.z > m.z+m.height) 
				and not (s.z+s.height < m.z)
					s.momx,s.momy,s.momz = 0,0,0
					P_ExplodeMissile(s) return false --Don't technically collide, stop manually.
				end
			elseif (s.type == MT_WVCHARGESHOT) or (s.type == MT_SPCHARGESHOT)
				if not (s.z > m.z+m.height) 
				and not (s.z+s.height < m.z)
					P_DamageMobj(m, s, t) return true --Most standard damage ever.
				end
			elseif (s.type == MT_SAMSUPERMISSILE) --Super Missile!
				local ZOFFSET = (not (m.player)) and 8<<16 or 0
				if not (s.z > m.z+m.height+ZOFFSET)
				and not (s.z+s.height < m.z-ZOFFSET)
					P_DamageMobj(m, s, t, 2) return true --	
				end				
			elseif (s.type == MT_SAMMBOP) --Power Bomb!
				local ZOFFSET = (not (m.player)) and 32<<16 or 0
				if not (s.z > m.z+m.height+ZOFFSET)
				and not (s.z+s.height < m.z-ZOFFSET)
					P_DamageMobj(m, s, t, 3) return false --Massive damage.
				end
			elseif (s.type == MT_HBSHOT) --Hyper Beam Shot. No charge variant.
				local ZOFFSET = (not (m.player)) and 12<<16 or 0
				if not (s.z > m.z+m.height+ZOFFSET) --Height check, though the Hyper Beam mainly uses explosive impacts for range.
				and not (s.z+s.height < m.z-ZOFFSET)
					s.meleeblast = P_SpawnMobj(s.x, s.y, s.z, MT_SPAZERSHOCK)
					s.meleeblast.rocket = m
					m.spazershocked = true
					s.meleeblast.angle = s.angle
					if t and t.valid
						s.meleeblast.target = t
						s.meleeblast.hypered = 400
					end
					S_StartSound(s.meleeblast, sfx_smexp)
					S_StartSound(s.meleeblast, sfx_smexp)
					P_ExplodeMissile(s) return true --				
				end				
			end
		end
	elseif (m.type == MT_EGGSHIELD) 
		if (s.type == MT_HBSHOT) or (s.type == MT_SAMMBOP) --That shield won't stand a chance against THIS!
			if m.target and (m.target.type == MT_EGGGUARD) and m.target.health
				m.target.flags = $|MF_ENEMY|MF_SHOOTABLE
				m.target.state = S_EGGGUARD_RUN1
			end
			m.state = S_NULL 
			return false --
		end return --
	elseif (m.type == MT_BIGMINE)
		if not (s.z > m.z+m.height)
		and not (s.z+s.height < m.z)	
			P_SpawnMobj(m.x, m.y, m.z, MT_UWEXPLODE)
			m.state = S_NULL 
		end
		return --		
	elseif (m.type == MT_ICBLOCK)
		if not (s.z > m.z+m.height)
		and not (s.z+s.height < m.z)
			m.icethisfool = true 
			return true --
		end
	end
end
addHook("MobjMoveCollide", BeamPopMBreaker, MT_SAMMONBREAKER) --Special Monitor opener object.
addHook("MobjMoveCollide", BeamPop, MT_PBSHOT)
addHook("MobjMoveCollide", BeamPop, MT_SPSHOT)
addHook("MobjMoveCollide", BeamPopSPS, MT_SPCHARGESHOT)
addHook("MobjMoveCollide", BeamPopSPC, MT_SPCHARGECOMBO)
addHook("MobjMoveCollide", BeamPop, MT_WVSHOT)
addHook("MobjMoveCollide", BeamPop, MT_WVCHARGESHOT)
addHook("MobjMoveCollide", BeamPopICC, MT_ICSHOT)
addHook("MobjCollide", BeamPop, MT_ICSHOT) --Should this be BeamPop...?? It's MobjCollide, tho'.
addHook("MobjMoveCollide", BeamPopICC, MT_ICCHARGESHOT)
addHook("MobjMoveCollide", BeamPopICC, MT_ICCHARGECOMBO)
addHook("MobjMoveCollide", BeamPopPLS, MT_PLSHOT)
addHook("MobjMoveCollide", BeamPopPLS, MT_PLCHARGESHOT)
addHook("MobjMoveCollide", BeamPop, MT_PBCHARGESHOT)
addHook("MobjMoveCollide", BeamPop, MT_HBSHOT)
addHook("MobjMoveCollide", BeamPop, MT_SAMMISSILE)
addHook("MobjMoveCollide", BeamPop, MT_SAMSUPERMISSILE)
addHook("MobjMoveCollide", BeamPop, MT_SAMMORPHBOMB)


local function NotForSamus(s, m) --Don't steal dropped weapons or rings. Samus is OP enough as is towards normal characters
	if G_GametypeHasSpectators() and m and YouAreSamus(m)
		if (s.flags2 & MF2_DONTRESPAWN)
		or (s.type == MT_FLINGRING) or (s.type == MT_FLINGCOIN)
			return false --
		end
	end
end
for d = MT_BOUNCERING,MT_GRENADEPICKUP
	addHook("MobjCollide", NotForSamus, d)
	addHook("MobjMoveCollide", NotForSamus, d)
end
for i = 1,2
	addHook("MobjCollide", NotForSamus, (i==1) and  MT_FLINGRING or MT_FLINGCOIN)
	addHook("MobjMoveCollide", NotForSamus, (i==1) and  MT_FLINGRING or MT_FLINGCOIN)
end

addHook("MobjCollide", function(s, v)
	if v and v.valid and YouAreSamus(v) and v.player
		local p = v.player
		if p.samspbschargefull or p.bsblboostattack or v.sam_deathball or p.sam_screwattacking
			if not (v.z > s.z+s.height+12<<16)
			and not (s.z > v.z+v.height)	
				P_KillMobj(s, v, v) return false --
			end
		end
	end
end, MT_SPIKE)

rawset(_G, "SamusBeamReflect", function(s, m) --s = Missile, m = Enemy Mobj
	local sammom = false
	local t = s.target
	if (t != m) --Already reflected....
		if not (S_SoundPlaying(s, sfx_bmrefl))
		and not (S_SoundPlaying(s.target, sfx_bmrefl))
			S_StartSound(s, sfx_bmrefl)
		end
		s.momx,s.momy,s.momz = $/-2,$/-2,$/-2
		s.angle = R_PointToAngle2(s.x, s.y, s.x+s.momy, s.y+s.momy)
		if t and t.valid and YouAreSamus(t)
			t.bmreflwarning = 16
			t.player.sam_resisthud = 18
		end
		s.target,s.tracer = m,m
		s.sambeamreflected = true
		for d = 1,3
			local ghs = P_SpawnMobj(s.x, s.y, s.z+s.height/4, MT_PBEXPAND)
			ghs.scale = (s.scale/5)*d
			ghs.scalespeed = $*(6*d)
			ghs.destscale = $*99
			ghs.tics = 6
			ghs.colorized = true
			ghs.color = SKINCOLOR_SUPERSILVER1+d
		end
	end
end)

local function FuckMinecarts(v, h)
	if h and h.valid and (h.type == MT_SAMSUPERMISSILE)
		if (v.z > h.z+80<<16)
		or (h.z > v.z+80<<16)
		or (v.flags & MF_NOCLIP)
			return false --
		end		
		S_StartSound(v, sfx_smexp)
		local ghs = P_SpawnMobj(v.x, v.y, v.z, MT_BOSSEXPLODE)
		ghs.scale = $*6
		ghs.flags2 = $|MF2_SHADOW
		ghs.blendmode = AST_ADD
		for d = 0,12
			local debris = P_SpawnMobj(
			v.x+(P_RandomRange(-50,50)*v.scale), 
			v.y+(P_RandomRange(-50,50)*v.scale), 
			v.z+(P_RandomRange(5,80)*v.scale), MT_WOODDEBRIS)
			if debris and debris.valid
				debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
				P_InstaThrust(debris, h.angle+(P_RandomRange(-20,20)*ANG1), 27<<16)
				P_SetObjectMomZ(debris, P_RandomRange(-4,11)<<16, false)
			end
		end
		v.flags = $|MF_NOCLIP
		if (v.type == MT_SALOONDOOR)
			P_KillMobj(v, h, h)
		end return true --
	end
end
for i = 1,2
	addHook("MobjCollide", FuckMinecarts, (i==1) and  MT_SALOONDOORCENTER or MT_SALOONDOOR)
	addHook("MobjMoveCollide", FuckMinecarts, (i==1) and  MT_SALOONDOORCENTER or MT_SALOONDOOR)
end

--Fused Mobjcollide hook for MT_PLAYER
addHook("MobjCollide", function(s, v)
	if not (s.valid and YouAreSamus(s) and s.player and v and v.valid) return end --
	local p = s.player local tp = v.player

	if tp
		if not (v.z > s.z+s.height + 4<<16)
		and not (s.z > v.z+v.height + 4<<16)	
			if G_GametypeHasSpectators()
			or p.bsblboostattack
			or s.sam_deathball
			or p.sam_shinesparkfly and (p.sam_shinesparkfly > 17)
			or p.sam_screwattacking and (p.sam_screwattacking > 24)
				if p.bsblboostattack 
				
					if not ((gametype == GT_COOP or gametype == GT_RACE) and p.morphcoop)
						if P_IsObjectOnGround(s)
							P_InstaThrust(s, s.angle, -15<<16)
							P_SetObjectMomZ(s, 4<<16, true)
						else
							P_Thrust(s, s.angle, -8<<16)
							s.momz = $*-1
						end
						if not G_GametypeHasSpectators()
							P_DoPlayerPain(tp, i, so)
							P_PlayRinglossSound(v)
							P_Thrust(v, s.angle, 11<<16)
							P_SetObjectMomZ(v, -2<<16, true)
						end
						P_DamageMobj(v, s, s, 1)
						p.bsblboost,p.bsblboostattack = 0,0
						p.sambsblchargefull = false
						if not p.powers[pw_flashing]
							p.powers[pw_flashing] = 8
						end
						return false --
					end
					return --
				
				elseif p.sam_shinesparkfly
					P_DamageMobj(v, s, s, 2)
					P_InstaThrust(v, s.angle, 35<<16)
					if not p.powers[pw_flashing] p.powers[pw_flashing] = 30 end
					return false --
			
				elseif p.sam_screwattacking
					P_DamageMobj(v, s, s, 2)
					return false --
				end
			end
		end
	end	
	if p.sam_shinesparkfly and (p.sam_shinesparkfly > 17)
	or p.sam_screwattacking 
	or s.crystalflashanim
		if not (v.flags & (MF_ENEMY|MF_BOSS|MF_MISSILE)) and not tp and not (v.type == MT_ENERGYBALL)
			return --
		end
		if not (p.sam_screwattacking and (p.sam_screwattacking <= 24 or s.screwattackfling))
		and not (v.type == MT_HBSHOT) --Never with Hyper Beam shots.
			local ZOFFSET = (not (tp and tp.sammorphed)) and 16<<16 or 0
			
			if (v.z > s.z+s.height+ZOFFSET) --Extra generous hitbox or nah?
			or (s.z > v.z+v.height+ZOFFSET)
				return false--
			end
			if (v.flags & MF_MISSILE) or (v.type == MT_ENERGYBALL)
				if not (v.target and (v.target == s))
					SamusBeamReflect(v, s)
					if (v.type == MT_ENERGYBALL)
						v.flags = $|MF_MISSILE & ~MF_PAIN
						v.fuse = 60
					end
				end			
			elseif (v.flags & (MF_ENEMY|MF_BOSS))
				if v.health		
					P_DamageMobj(v, s, s, 2)
				end
			end 
			return false --
		end
		if s.crystalflashanim
			return false --
		end
	end
end, MT_PLAYER)

--How are you going to breathe bubbles through a helmet??
addHook("MobjMoveCollide", function(s, v)
	if v and YouAreSamus(v) return false end --
end, MT_EXTRALARGEBUBBLE)
addHook("MobjCollide", function(s, v)
	if v and YouAreSamus(v) return false end --
end, MT_EXTRALARGEBUBBLE)

addHook("MobjThinker", function(s) if not s.valid return end --
	s.givesamusatimer = s.givesamusatimer and $+1 or 1
	if s.givesamusatimer == 5
		for z in players.iterate
			if z.valid
				if z.samweather == 2
					s.PermaInvisible = true
				end
				z.samushasatimer = true
			end
		end
	end
	if s.PermaInvisible
		s.spriteyoffset = 5000<<16 --Nope, can't see you.
		s.spritexscale = 1
		s.flags2 = $|MF2_DONTDRAW
		if s.tracer and not s.tracer.player
			s.tracer.flags2 = $|MF2_DONTDRAW
			s.tracer.spriteyoffset = 5000<<16
		end
		if s.target and not s.target.player
			s.target.flags2 = $|MF2_DONTDRAW
			s.target.spriteyoffset = 5000<<16
		end
	end
end, MT_METALSONIC_RACE)

local function TokenSamCol(chaosany, s) if (CBW_Chaos and gametype == GT_CHAOS) return end --
	if s and s.player and s.health
		local p = s.player
		
		if YouAreSamus(s) and not p.tokensrb2style
		or p.tokensrb2style == 1
		
			if not (chaosany and chaosany.valid)
			or (chaosany.z > s.z+ (s.height*3/2) )
			or (s.z > chaosany.z+ (chaosany.height*2) )
			or not s.health
				return false --Height checks failed.
			end
	  		if All7Emeralds(emeralds)
				S_StartSound(s, sfx_s3k34)
				P_GivePlayerRings(p, 50) --'cause screw points.
	   		else
				print("\x83"+p.name+" will get a Chaos Emerald if they finish the stage without taking damage!")
				s.sam_flashfade = 19
				s.sam_flashfadecolor = SKINCOLOR_BEIGE	
				p.samtoken = p.samtoken and $+1 or 1
				S_StartSound(nil, sfx_s3k9c)
	   		end
			
	   		if chaosany
	   	   		if chaosany.tracer and not (chaosany.tracer.player)
					P_KillMobj(chaosany.tracer)
	   	   		end
				P_KillMobj(chaosany)
	   		end
			return false --
			
		elseif chaosany and chaosany.valid and chaosany.health 
		
			if YouAreSamus(s) and not p.tokensrb2style
				return false --
			end
		end
	end
end
addHook("MobjCollide", TokenSamCol, MT_TOKEN)
addHook("MobjMoveCollide", TokenSamCol, MT_TOKEN)

//Burn Cacti with the Plasma Beam!

local function CactiBurn(f, s)
	if s and s.valid
	and (s.type >= MT_CACTI1) and (s.type <= MT_CACTISMALLSEG)
	
		if not (s.flags & MF_PAIN) return false end --
		
		if f.readyforonemoreplant
		or not (f.z > s.z+s.height)
		and not (f.z+f.height < s.z)
			s.flags = $ & ~(MF_PAIN|MF_SOLID|MF_NOGRAVITY)
			s.flags2 = $|MF2_DONTDRAW
			s.scale = max(1, $*P_RandomRange(0, 4))
			if not P_RandomRange(0,2)
				P_SpawnMobj(f.x, f.y, f.z+f.height, P_RandomRange(0,4) and MT_NRGA or MT_MPKU)
			end
			local ghs
			for d = 1,11
				A_SAMPARTICLE(s, MT_MPR5, 7, 4, -2, 8, SKINCOLOR_RED, 78000+(i*1400) )		
				if d == 1
					ghs = P_SpawnMobj(s.x, s.y, s.z, MT_FLAMEJETFLAME)
					if ghs and ghs.valid
						ghs.flags = MF_NOGRAVITY|MF_SCENERY
						ghs.destscale = $*3
						ghs.scalespeed = $/2
						ghs.fuse = (i>6) and i*3 or P_RandomRange(20, 35)
					end				
				else
					ghs = P_SpawnMobj(
					s.x+(P_RandomRange(30,30)<<16), 
					s.y+(P_RandomRange(-30,30)<<16),
					s.z+(P_RandomRange(10,120)<<16), MT_FLAMEJETFLAME)
					if ghs and ghs.valid
						ghs.flags = MF_NOGRAVITY|MF_SCENERY
						ghs.scale = $/((i>1 and i<8) and i or P_RandomRange(2,8) )
						ghs.destscale = $*99
						ghs.scalespeed = $/ ((i>1 and i<5) and i or P_RandomRange(2,4) )
						ghs.fuse = P_RandomRange(20, 38)
						
						if d > 4
							ghs.momz = P_RandomRange(2,8)<<16
							P_InstaThrust(ghs, P_RandomRange(1,359)*ANG1, 2<<16)
							ghs.scale = $/2
							ghs.flags2 = $|MF2_SHADOW
						else
							ghs = P_SpawnMobj(s.x+(P_RandomRange(-30,30)<<16), s.y+(P_RandomRange(-30,30)<<16), s.z+(P_RandomRange(10,120)<<16), MT_SMOKE)
							if ghs and ghs.valid
								ghs.flags = MF_NOGRAVITY|MF_SCENERY
								ghs.scale = $/P_RandomRange(1,3)
								ghs.destscale = $*99
								ghs.scalespeed = $/P_RandomRange(2,3)
								ghs.fuse = P_RandomRange(20, 38)
								ghs.momz = P_RandomRange(3,8)<<16
								P_InstaThrust(ghs, P_RandomRange(1,359)*ANG1, 4<<16)
							end						
						end
					end
				end
			end
			if s and s.valid
				S_StartSound(s, sfx_s3k7f)
				S_StartSound(s, sfx_s248)
				s.z = $-s.momz
				if s and s.valid
					P_TryMove(s, s.x-s.momx, s.y-s.momy, true)
				end
				return false --
			end
		end
	end
end
addHook("MobjMoveCollide", CactiBurn, MT_PLSHOT)
addHook("MobjMoveCollide", CactiBurn, MT_PLCHARGESHOT)

local function ShieldNope(f, s)
	if s and s.valid and (s.zap or (s.type == MT_EGGGUARD))
		return false --
	end
end
addHook("MobjMoveCollide", ShieldNope, MT_EGGSHIELD)
addHook("MobjCollide", ShieldNope, MT_EGGSHIELD)

--Checks if "t" is below a floor that "m" is above.
local function SAM_UnderFloor(m, t)
	local ss = R_PointInSubsector(m.x, m.y)
	local SEC = ss and ss.sector
	if SEC and SEC.ffloors != nil
		for fof in SEC.ffloors()
			if fof and (fof.flags & FF_EXISTS) and (fof.flags & FF_BLOCKOTHERS)
				if (m.z < fof.bottomheight)
					if (t.z >= fof.topheight) --m is below a FOF. t is above it.
						if (abs(fof.bottomheight-m.z) < 44<<16) --AND it's a tiny gap. Super specific, yes.
							return true --
						end
					end
				end
			end
		end	
	end return false --
end

local LLIST = {
[MT_METROIDPART] = true,
[MT_SAMDUMMYTRACKER] = true,
[MT_ICBLOCK] = true,
[MT_EGGGUARD] = true,
[MT_EGGSHIELD] = true
}

--The LockOn objects relay collision data to Samus to let her know what to Lock On to.
--This way I avoid having to rely on blockmap searches. ...or worse, iterating through mobjs.
local function LockOn(s, m)

	local t = s.target local tp = t and t.player
	
	if not (tp and m.valid and m.health) return false end
	
	local CONTINUE,DIST = false,0
	if (m.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)) 
		if (m.flags & MF_MONITOR)
			local MAXDIST = max(1000*s.scale, 1400<<16)
			if not tp.sam_lockonnomon --Player turned monitor-locking off.
				if (m.type == MT_EGGMAN_BOX) or (m.type == MT_EGGMAN_GOLDBOX) 
					return false --No point locking onto those.
				elseif tp.ctfteam and G_GametypeHasTeams() 
					if tp.ctfteam == 2
						if (m.type == MT_RING_REDBOX) return false end --Those are the opposing team's monitors!
					elseif tp.ctfteam == 1
						if (m.type == MT_RING_BLUEBOX) return false end
					end
				elseif (SAM_UnderFloor(m, t) == true)
					return false --
				end
				DIST = FixedHypot(m.x-t.x, m.y-t.y)
				if (DIST < MAXDIST) --Monitors get less priority than enemy targets.
					CONTINUE = true
				end
			end
		else
			if m.metroidstatue 
			or m.brakandco and not ((m.type == MT_POPUPTURRET) or m.superbrak) --Only lock-on to HyperBrak and his switches!
				return false --
			else
				CONTINUE = true
			end
		end
	elseif m.player
		if G_GametypeHasSpectators() and m!=t and m.player != tp --Only lock on in RingSlinger/Hunters.
			if tp.ctfteam and (tp.ctfteam == m.player.ctfteam) and G_GametypeHasTeams() --That's our teammate.
				return false --
			else
				CONTINUE = true
			end
		end 
	elseif (LLIST[m.type]==true) --and (m.type != s.type)
		if not ((m.type == MT_METROIDPART) and not m.hitbox)
			if m.metroidstatue
			or m.brakandco and not ((m.type == MT_POPUPTURRET) or m.superbrak)
				return false --
			else
				CONTINUE = true
			end
		end 
	end
	if CONTINUE == true
		local MAXZDIST = max(1200*s.scale, 2000<<16)
		if (s.z > m.z+MAXZDIST) --Generous height difference
		or (m.z > s.z+MAXZDIST)
			return false --
		end
		local CLOSEST = (t.sam_lockclosest) or ANG10*8
		if not DIST DIST = FixedHypot(m.x-t.x, m.y-t.y) end
		
		--Don't prioritize this target if it's further away than our last checked target.
		if (DIST <= CLOSEST)
		
			if not P_CheckSight(t, m) return false end
			
			if (m.flags & MF_MONITOR)
				if t.sam_lockchosen and t.sam_lockchosen.valid  --Again, prioritze enemies first and foremost!
				and not (t.sam_lockchosen.flags & MF_MONITOR)
					return false --Again, prioritze enemies.
				elseif not G_GametypeHasSpectators() --Don't lock onto monitors you've already grabbed.
					if m.gotplayers
						for i,z in pairs(m.gotplayers)
							if (tp == z)
								return false --
							end
						end
					end
				end
			end
			t.sam_lockclosest = DIST
			t.target, t.sam_lockchosen = m, m
		end
	end
	return false -- Always return false.	
end
addHook("MobjMoveCollide", LockOn, MT_SAMUSLOCKSEEK)
addHook("MobjCollide", LockOn, MT_SAMUSLOCKSEEK)

--But Samus-chan, I have no hitbooox.....well fuck you, game. You do now.
local function DummyHBadd(s)
	if s.valid and not s.dummyhb2 and s.health
		if not s.brakandco
		and not s.metroidstatue
			s.dummyhb2 = P_SpawnMobj(s.x, s.y, s.z, MT_SAMDUMMYTRACKER)
			s.dummyhb2.target = s
			s.dummyhb2.height = s.height
			s.dummyhb2.raidus = s.radius
			s.dummyhb2.tics = -1
			s.dummyhb2.flags2 = $|MF2_DONTDRAW
			s.dummyhb2.dummyhbx2 = true
		end
	end
end
addHook("MobjThinker", DummyHBadd, MT_METALSONIC_BATTLE)
addHook("MobjThinker", DummyHBadd, MT_BLACKEGGMAN)

local function TrackerCollide(s, v)
	if not (v and v.type == MT_SAMUSLOCKSEEK) return false end --
end
addHook("MobjCollide", TrackerCollide, MT_SAMDUMMYTRACKER)
addHook("MobjMoveCollide", TrackerCollide, MT_SAMDUMMYTRACKER)

local function SamWJCollide(s, v)
	if s return false end
	/*	if v and v.valid
			if v.player or (v.flags & (MF_MONITOR|MF_ENEMY|MF_BOSS|MF_PAIN|MF_MISSILE))
				return false --
			end
		end
		return false */
end
addHook("MobjCollide", SamWJCollide, MT_SAMWALLJUMP)
addHook("MobjMoveCollide", SamWJCollide, MT_SAMWALLJUMP)

addHook("MobjThinker", function(s)
	local t = s.target
	if t
		if t.valid and t.health
			P_MoveOrigin(s, t.x, t.y, t.z)
		elseif s.valid
			s.state = S_NULL
		end
		return --
	elseif s.valid
		P_KillMobj(s, s) return --
	end
end, MT_SAMDUMMYTRACKER)

--Give some wall collission grace.
/*
addHook("MobjMoveBlocked", function(s)
	if YouAreSamus(s) and s.player
	and not s.player.powers[pw_carry]
	and not s.currentaxis and not (s.flags2 & MF2_TWOD or twodlevel)
local p = s.player
		if (p.cmd.forwardmove or p.cmd.sidemove)
		and ( ((p.speed > 20*s.scale) and (p.sammorphed or p.samspbschargefull)) or (p.sam_walljumping > 10) )
			if (p.sam_negatewallc < 5)
				p.sam_negatewallc = $+3
				return true
			end
		end
	end
end, MT_PLAYER)*/

--Correct where the shot lands, since SRB2's default detection sucks.

local function MoveCorrector(s, thing, line)
	if line and line.valid
		if s.valid
			local x,y = P_ClosestPointOnLine(s.x, s.y, line)
			s.sam_correctangle = R_PointToAngle2(s.x, s.y, x, y)
			s.sam_correctdist = R_PointToDist2(s.x, s.y, x, y)-(s.radius+(s.radius/11))
			s.sam_hitline = line
		end
	end
end
addHook("MobjMoveBlocked", MoveCorrector, MT_PBSHOT)
addHook("MobjMoveBlocked", MoveCorrector, MT_PLSHOT)
addHook("MobjMoveBlocked", MoveCorrector, MT_PLCHARGESHOT)
addHook("MobjMoveBlocked", MoveCorrector, MT_SPSHOT)