--#===============================================================#
--Collection of Mobj and Line Collission hooks for various reasons.
--#===============================================================#
local XSon = XSon
local function P_MobjFlip(s) if s and s.valid and ((s.eflags&MFE_VERTICALFLIP) or (s.flags2&MF2_OBJECTFLIP)) return -1 end return 1 end
local CT = XS_CRASHTABLE 

--List of items you can't snapdive-bounce off unless you're Super.
local CANTBOUNCE = {
MT_JETFLAME,
MT_GREENFLAME,
MT_EGGMOBILE_BALL,
MT_LAVAFALL_LAVA,
MT_VERTICALFLAMEJET,
MT_FLAMEJETFLAME,
MT_FLAMEJETFLAMEB,
MT_CYBRAKDEMON_FLAMESHOT,
MT_CYBRAKDEMON_NAPALM_FLAMES,
MT_KOOPAFLAME,
MT_FIREBALL,
MT_DEMONFIRE,
MT_EGGMOBILE_FIRE,
MT_CACOFIRE,
MT_SHOCKWAVE
}

--Returns true if you hit the shield first.
local function ShieldorNot(p,s,xs, v)
	if v.target and v.target.valid and v.target.health and (v.target.type == MT_EGGGUARD)
	and xs and xs.lastx!=nil and xs.lasty!=nil
		local t = v.target
		local TANG = t.angle
		local ANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
		local ANGLE,PLUS = XS_AngleDifference(ANG, TANG, 2)
		if (ANGLE < ANG1*58)
			t.flags = $|MF_SHOOTABLE|MF_ENEMY
			P_DamageMobj(t, s, s)
			P_KillMobj(v) return false --
		end
	end return true --
end

--Bounce off something
local function X_Rebound(p,s,xs,v)
	if not (v) or (v.type==MT_EGGSHIELD) and (ShieldorNot(p,s,xs, v)!=true)
	or ((v.type==MT_EGGGUARD) and (v.flags & MF_SHOOTABLE))
		return false --
	end
	local ANG = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
	if xs.zipdash ANG = xs.zipdashangle XSZipDashCancel(p, s, xs) end
	
	xs.rollcd,xs.ringburstcd = 6,6
	s.z = $+(8*s.scale*P_MobjFlip(s))
	
	if not (P_TryMove(s, s.x+FixedMul(-99*s.scale, cos(ANG)), s.y+FixedMul(-99*s.scale, sin(ANG)), true))
		P_TryMove(s, s.x+FixedMul(-66*s.scale, cos(ANG)), s.y+FixedMul(-66*s.scale, sin(ANG)), true)
	end
	XSon.SwapStretch(p, s, xs)
	s.spritexscale,s.spriteyscale = FRACUNIT+27000, FRACUNIT-17000
	s.spriteyoffset = -5<<16	
	s.XSstretch = {timer = 4, Xspeed = 5000, Yspeed = 3900}
	
	P_SetObjectMomZ(s, (s.eflags & MFE_UNDERWATER) and 4<<16 or 8<<16, true)
	if ( v.type == MT_EGGSHIELD)
		local ghs = XS_CircleWave(s, 1, SKINCOLOR_SKY, 15, FRACUNIT)
		ghs.frame = E|FF_TRANS40
		ghs.dispoffset = 144
		ghs.spriteyoffset = $-24<<16		
		XSon.Quake(so, 45<<16, 3)
		S_StartSound(s, sfx_s3k7b) S_StartSound(v, sfx_s3k7b)
		s.XS_reboundtime = 11
	else
		local ghs = XS_CircleWave(s, 1, SKINCOLOR_RUBY)
		ghs.blendmode = AST_ADD
		ghs.sprite = SPR_XSG2
		ghs.frame = 31|FF_TRANS80
		P_Thrust(ghs, ghs.angle-ANGLE_90, s.scale*3)	
		ghs.spritexscale,ghs.spritexscale = $*2/3,$*2/3
		
		XSon.Sound(s, sfx_xssfpl, 99) S_StartSound(s, sfx_dmpain)
		XSon.Quake(so, 77<<16, 7)
		s.XS_reboundtime = 5
	end
	P_InstaThrust(s, ANG, -33*s.scale)
	xs.spin = 999<<16
	s.state = S_PLAY_XDIVEFLIP1
	p.pflags = $ & ~(PF_SPINNING|PF_JUMPED|PF_STARTDASH)
	xs.stopstick = 4
	if not p.powers[pw_flashing]
		p.powers[pw_flashing] = 8
		s.flags2 = $|MF2_DONTDRAW
	end return true --
end

local function CZipRebound(s, i)
	if i and i.skin == "xsonic"
		if (s.type == MT_EGGGUARD) and (s.flags & MF_SHOOTABLE)
			return --
		elseif i.XS_reboundtime
			return false --
		elseif (s.type == MT_EGGGUARD)
			return --
		end
		
		local p = i.player local xs = p and p.xsonictable
		if xs and xs.zipdash
		and not (p.powers[pw_super] or p.powers[pw_invulnerability])
		and (i.z <= s.z+s.height+(16<<16))
		and (i.z > s.z-(16<<16))
			X_Rebound(p, i, xs, s)
			if (s.type == MT_EGGSHIELD)
				return false --
			elseif not (s.flags2 & MF2_FRET) and (s.flags & MF_SHOOTABLE)
				P_DamageMobj(s, i, i)
			end
		end
	end
end
local function TSZipRebound(s, i)
	if i and i.skin == "xsonic"
		if i.XS_reboundtime return true end --
		local p = i.player local xs = p and p.xsonictable
		
		if xs and xs.zipdash and not (p.powers[pw_super] or p.powers[pw_invulnerability])
			X_Rebound(p, i, xs, s) return true --
		end
	end
end
addHook("MobjCollide", CZipRebound, MT_EGGGUARD) 
addHook("MobjCollide", CZipRebound, MT_EGGSHIELD) --Bounce off these
addHook("MobjCollide", CZipRebound, MT_FACESTABBER)
addHook("MobjCollide", CZipRebound, MT_HIVEELEMENTAL)
addHook("MobjCollide", CZipRebound,MT_EGGMOBILE) 
addHook("MobjCollide", CZipRebound,MT_EGGMOBILE3)
addHook("MobjCollide", CZipRebound,MT_CYBRAKDEMON)

addHook("TouchSpecial", TSZipRebound, MT_EGGSHIELD)

local function XBagCollide(s, v)
	if not (v and v.valid and s.valid)	
	or (v.type == s.type)
	or v.player
		return false --
	end	
	if (v.flags & (MF_SCENERY|MF_SOLID|MF_PUSHABLE|MF_SPECIAL)) 
	or ((CT[v.type]))
		local SPEED = min(128<<16, abs(s.momx)+abs(s.momy)+abs(s.momz))
		if (SPEED < 26<<16)
		or (s.z > v.z+v.height+max(64<<16, SPEED*2))
		or (v.z > s.z+s.height+max(64<<16, SPEED*2))
			return false --
		end
		if not (v.flags2 & MF2_DONTDRAW) and not v.WaitingXTree
		and not ((s.XS_BreakThroughItAll or 0) > 80)
			local SUPER = 0
			if s.tracer and s.tracer.player
				SUPER = XS_Super(s.tracer.player)
			end
			if (SUPER != 1)
				if (XSon.ScenicCrash(nil,s,nil, v, SUPER))==true
					return false --
				end
			end	
		end
	end return false --
end
addHook("MobjCollide", XBagCollide,  MT_XSSANDBAG)
addHook("MobjMoveCollide", XBagCollide,  MT_XSSANDBAG)

--Grab a Power Ring to shake things up!
local function PWringcollide(s, v)
	if not (v and v.valid and v.player and s.valid and s.health) --Must be a player.	
	or ((s.init or 0) < 10) --Can't grab it initially!
	or (s.z > v.z+v.height) or (v.z > s.z+s.height) --Height check.
	or not (IsXSonic(v)) --Must be X Sonic.
		return false --
	end
	local p = v.player local xs = p and p.xsonictable
	if xs and p.bot==0
		s.target = v
		if (gametype == GT_COOP) and (multiplayer==true)
			for z in players.iterate
				local xss = z and z.xsonictable
				if xss and z.mo and IsXSonic(z.mo) and z.mo.health and xss.PowerRing != 275
					local DIST = FixedHypot(FixedHypot(s.x-z.mo.x, s.y-z.mo.y), s.z-z.mo.z)
					if (DIST < 2250<<16) --Give the ring to everyone in the vicinity!
						xss.PowerRing = 275
						z.mo.XS_pwringget = 12
						S_StartSound(nil, sfx_itemup, z)
						S_StartSoundAtVolume(nil, sfx_cdfm73, 120, z)
						if xss.FirstPWring == nil
							xss.FirstPWring = 280
						end	
					end
				end
			end
		else --Single player or competitive gamemodes make it exclusive!
			if xs.PowerRing != 275
				xs.PowerRing = 275
				v.XS_pwringget = 12
				S_StartSoundAtVolume(nil, sfx_cdfm73, 120, p)
				if xs.FirstPWring == nil
					xs.FirstPWring = 280
				end
			end
			S_StartSound(nil, sfx_itemup, p)
		end
		for i = 0,8
			local ghs = XSon.SmokeMist(s, 0, (91+i)*s.scale)
			if ghs and ghs.valid
				ghs.color = SKINCOLOR_SUPERGOLD4
				ghs.scale = $*2
				ghs.spriteyscale = 120500
				ghs.spriteyoffset = $-22<<16
				if ghs.sttXspeed
					ghs.sttXspeed = $*2/5
				end
			end
			
			ghs = P_SpawnGhostMobj(s)
			ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
			ghs.blendmode = AST_ADD
			ghs.fuse,ghs.tics = 25,30
			ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS40
			ghs.color,ghs.colorized = SKINCOLOR_SUPERGOLD5,true	
			if i != 0
				P_InstaThrust(ghs, ANGLE_45*i, 24*s.scale)
				P_SetObjectMomZ(ghs, 5<<16, true)
			else
				ghs.scalespeed = FRACUNIT/6
			end
			ghs.destscale = 99<<16
		end
		
		S_StopSoundByID(v, sfx_xssfe5)
		P_KillMobj(s, v, v)
		if s and s.valid --Just incase it didn't get the memo....
			s.health = 0
		end
	end return false --
end
addHook("MobjCollide", PWringcollide,  MT_XSPOWERRING)
addHook("MobjMoveCollide", PWringcollide,  MT_XSPOWERRING)

//Ignore shields when Super 
local function ShieldNopeS(s, so)
	if so and so.player and so.player.xsonictable and so.player.xsonictable.supersonic
		return true
	end
end
local function ShieldNope(s, so) if not s.valid return end
	if so and so.player and IsXSonic(so) and so.player.xsonictable
		if so.player.xsonictable.supersonic
		
			if s.target and s.target.valid and s.target.health and (s.target.type == MT_EGGGUARD)
				s.target.flags = $|MF_ENEMY|MF_SHOOTABLE
				P_KillMobj(s.target, so, so)
			end
			
			S_StartSound(so, sfx_bedeen)
			
			if s and s.valid s.state = S_NULL end
			return false
		end
	end
end
addHook("TouchSpecial", ShieldNopeS,  MT_EGGSHIELD)
addHook("MobjCollide", ShieldNope,  MT_EGGSHIELD)

--Not pushing Super Sonic, pals.
local function EggRoNope(s, v)
	local p = v and v.player local xs = p and p.xsonictable
	if xs and IsXSonic(v) and xs.supersonic
		if s.valid
		and not (s.z > v.z+v.height+20*v.scale)
		and not (s.z+s.height+20*v.scale < v.z) --Height check!
			S_StartSound(v, sfx_bedeen)
			XSon.Quake(v, 55<<16, 3, 777<<16)
			P_SpawnMobjFromMobj(s,v.momx*2,v.momy*2,v.momz+(s.height/2), MT_EXPLODE)
			local ghs = P_SpawnMobjFromMobj(s,0,0, v.height/2, MT_BOSSJUNK) --Lazy flung fake version of the Egg Robo
			if ghs and ghs.valid
				local RNG = P_RandomRange(122,-122)
				ghs.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING|MF_NOBLOCKMAP & ~(MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT)
				ghs.angle = v.angle + RNG*ANG1
				ghs.sprite,ghs.frame = s.sprite,s.frame
				ghs.rollangle = $+ANG1*RNG
				P_InstaThrust(ghs, s.angle, 25*v.scale)
				P_SetObjectMomZ(ghs, 5*v.scale)
				ghs.momx,ghs.momy,ghs.momz = $+v.momx,$+v.momy,$+v.momz
				ghs.fuse = 199
			end				
			P_RemoveMobj(s)
		end
		return false --
	end
end
addHook("MobjCollide", EggRoNope,  MT_EGGROBO1)
addHook("MobjMoveCollide", EggRoNope,  MT_EGGROBO1)

--Checks if some platform is stopping us from Snapping.
local function XSBlockingFOFs(s, m, t)
	local SEC = R_PointInSubsector(m.x, m.y)
	if SEC != nil and SEC.sector and SEC.sector.ffloors
		for fof in SEC.sector.ffloors()
			if (fof.flags & FF_EXISTS) and (fof.flags & FF_BLOCKPLAYER) --and not (fof.flags & FF_INTANGIBLEFLATS)
				if (t.z >= fof.topheight)
					if (m.z < fof.topheight) --and not (fof.flags & FF_PLATFORM)
						return true --Target is below a FOF you're on!
					end
				elseif (t.z+t.height <= fof.bottomheight)
					if (m.z > fof.bottomheight) --and not (fof.flags & FF_REVERSEPLATFORM)
						return true --Target is above your current FOF!
					end
				end
			end
		end	
	end return false --
end

--Seperate object for performance and organization's sake.
local function XSassaultcheck(s, m) 
	local t = s.valid and s.target local p = t and t.player
	if not (p and (t!=m) and m.valid)
		return false --
	end 
	
	local p = t.player 
	local xs = p and p.xsonictable 
	local GO = nil
	
	if (m.flags & (MF_ENEMY|MF_BOSS)) and (m.flags & MF_SHOOTABLE)
	and not (m.flags2 & MF2_FRET) and (m.sprite != SPR_ROSY)
	or (m.type == MT_EGGSHIELD) and m.target and m.target.valid and (m.target.type == MT_EGGGUARD) and m.target.health and not (xs.darkphase==1)
	or (m.type == MT_XSDUMMYTRACKER)
	or m.player and xs.FreeFlight
		GO = true
	end
	if GO != nil
		if (m.z > s.z+s.height) or (s.z > m.z+m.height)
		or (m.flags & (MF_BOXICON|MF_NIGHTSITEM|MF_NOSECTOR))
		or (m.type == MT_SIGN)
		or (m.type == MT_OVERLAY)
			return false --
		end
		if not xs or not m.health
			return false --
		end
		if m.player
			local tp = m.player local xss = tp.xsonictable
			if not xss.FreeFlight
			or xss.snapdivetrick or xss.snapdive or xss.snapdivekick 
			or m.XStransform or tp.powers[pw_super] == 1 --Not when in transform animation.
				return false --
			end
		elseif (m.type == MT_XSDUMMYTRACKER) --Dummy tracker could be anything, check it's target, then decide.
			local tm = m.target
			
			if not (tm and tm.valid)
			or (tm.type == MT_SIGN) or (tm.type == MT_OVERLAY) --Why are you murdering the Signpost, Sonic
			or (tm.type == MT_METALSONIC_BATTLE and tm.state != S_METALSONIC_STUN)
			or (tm.type == MT_TURRET and not xs.supersonic)
				return false --
			end
		end
		if (m.ceilingz < t.floorz) or (t.ceilingz < m.floorz) --Sanity checks.
		or XSBlockingFOFs(s, m, t)
		or not P_CheckSight(t, m)
			return false --
		end
		local CLOSEST = s.snapclosest or "skip"
		local DIST = FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)

		if CLOSEST == "skip" or (DIST <= CLOSEST) --Compare last closest.
--			if not (t.XSAssaultT and t.XSAssaultT.valid) 
--				print("X:"+FixedInt(s.x)+" Y:"+FixedInt(s.y)+" Z:"+FixedInt(s.z)+"\x86  m.type:\x87"+m.type+"\x8c  DIST:\x84"+FixedInt(DIST))
--			end
			s.snapclosest = DIST --s, not t!
			t.XSAssaultT = (m.type!=MT_XSDUMMYTRACKER) and m or m.target --t, not s!
		end
	end return false --Always return false.
end
addHook("MobjCollide", XSassaultcheck, MT_XSASSAULTHB)
addHook("MobjMoveCollide", XSassaultcheck, MT_XSASSAULTHB) 

local DTABLE = {[MT_PLAYER] = true,
[MT_XSDUMMYTRACKER] = true,
[MT_MINUS]= true,
[MT_EGGGUARD]= true,
[MT_EGGSHIELD]= true,
[MT_TNTBARREL]= true,
[MT_STARPOST]= true,
[MT_OILLAMP]= true,
[MT_ENERGYBALL]= true,
[MT_SPIKE] = true,
[MT_ROSY] = true}

--Our snapdive object detector. Stolen from SMSreborn and SA-Sonic's yuecheck.
local function XSecheck(s, m) if not s.valid return false end --
	local t = s.target local p = t and t.player local xs = p and p.xsonictable
	
	if not (xs and (t!=m) and xs.snapdive and m.valid) return false end  --
	--We DO have a target and it IS a player? Let's go then, we're in business!
	
	--Okay, next, we're actually colliding with something, right?	
	local yu,RB = xs, xs.FreeFlight
	local GOTYPE,DIST = 0
	local FLAGS = m.flags or 0
	
	if RB and RB.LockOn and RB.LockOn.valid --In Tenkaichi, only snap to your opponent.
		if (RB.LockOn != m) or (s.NotThem==m) or RB.cantsnap
			return false --
		else
			local tp = RB.LockOn.player local xss = tp and tp.xsonictable local RBB = xss and xss.FreeFlight
			if RBB and RBB.RingBurstChase -- or (xss.ChaosLambent and not xss.ChaosLambent.firing)
				s.NotThem = m return false --
			end
		end
	else --Items with these values won't do.
		if (m.state == S_XSTHOK)
		or (xs.snaptarget==m)
		or (xs.lastsnaptarget == m)
		or (FLAGS & (MF_BOXICON|MF_NIGHTSITEM|MF_NOSECTOR))
			return false --
		elseif not m.health
			if not m.skin and not m.XS_CanSnapToMe and not (FLAGS & MF_SCENERY)
				return false --
			end
		end
		--Don't attach to objects below if your momentum is going up
		if not (t.eflags & MFE_VERTICALFLIP)
			if (t.momz > t.scale*2) and (m.z <= t.z) 
				return false --
			end
		elseif (t.momz < -t.scale*2) and (m.z >= t.z+t.height) 
			return false --
		end
	end
	local type = m.type or 0
	local INFO = m.info or m
	
	if (FLAGS & MF_SPRING) --Special objects that can skip all checks.
		if (type == MT_BANPSPRING)
			return false --That's a trap, ofc.
		end
		if RB  --Why do you need springs when you're flying??
			DIST = (DIST) or FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
			if (DIST > 81*s.scale) return false end
		elseif INFO.mass==0 and INFO.painsound == sfx_spring
			return false --Don't home into horizontal springs normally.
		end
		GOTYPE = 1
		
	elseif (FLAGS & (MF_ENEMY|MF_BOSS|MF_MONITOR)) --Objects that need the shootable flag first.	
		if (FLAGS & MF_SHOOTABLE) and not (m.flags2 & MF2_FRET)
			GOTYPE = 3
		elseif (m.sprite == SPR_ROSY) --WHY is she an enemy?
			if RB
				DIST = (DIST) or FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
				if (DIST > 133*s.scale) return false end
			end
			GOTYPE = 2
		end
	elseif (DTABLE[type]==true) or (m.sprite == SPR_ROSY) --MT_ROSY doesn't always work, so....
		if type == MT_PLAYER --Players?!
			local tp = m.player local xss = tp.xsonictable 
			if not (xss)
				return false --Uhh...nope
			else
				if not (
				xss.snapdivetrick or xss.snapdive or xss.snapdivekick
				or xss.launchcd 
				or tp.hypermysticsonic
				or m.XStransform
				or m.XS_PVPCD
				or tp.powers[pw_super]==1
				)
					local RBB = xss.FreeFlight
					if not (RBB and 
					(
					RBB.painstun or RBB.cantsnap or ((RBB.MeteorSmashed or 0)>16)
					or RBB.LockOn and RBB.LockOn.valid and ((RBB.LockOn != t) and (XS_NOPVP != 2))
					)
					)
					and not (xss.ChaosLambent and xss.ChaosLambent.struggle)
						if not ( xss.ChaosSnap and ((xss.ChaosSnap.init or 0) < 16) )
						and not ((m.state >= S_PLAY_XKICK1) and (m.state <= S_PLAY_XKICK4))
						or RBB and RBB.countered
							GOTYPE = 777
						end
					end
				end
			end
		elseif type == MT_XSDUMMYTRACKER --Dummy tracker could be anything, check it's target, then decide.
			GOTYPE = 3
			if m.target and m.target.valid
				local tm = m.target
				if (tm.type == MT_METALSONIC_BATTLE and tm.state != S_METALSONIC_STUN)
				or (tm.type == MT_TURRET and not xs.supersonic)
					GOTYPE = 0 --nvm
				end
			end	
		elseif (type == MT_MINUS) --Special objects that can skip all checks.	
		or (type == MT_EGGGUARD)
		or (type == MT_EGGSHIELD)
		or (type == MT_TNTBARREL)
		or (type == MT_STARPOST)
		or (type == MT_OILLAMP)
		or (type == MT_ENERGYBALL) and (p.powers[pw_invulnerability] or xs.supersonic)
		or (m.sprite == SPR_ROSY) --MT_ROSY doesn't always work, so....
		or (type == MT_SPIKE)
		and ( 
		((p.cmd.buttons & BT_SPIN) or xs.spin) and (xs.spin < 9) or xs.supersonic or p.powers[pw_invulnerability]
		)
		
			if RB
				if (type == MT_TNTBARREL) or (type == MT_SPIKE) or (m.sprite == SPR_ROSY) --or (m.type == MT_WALLSPIKE)
					DIST = (DIST) or FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
					if (DIST > 133*s.scale) return false end
				end
			end
			if (type == MT_SPIKE) and not xs.supersonic and not p.powers[pw_invulnerability]
				s.XSresist = 26 xs.spin = 2
				p.cmd.buttons = $|BT_SPIN
			end
			GOTYPE = 5
		end
	elseif (FLAGS & MF_PUSHABLE)
		if (m.height < t.height*3) --Can jump off small pushables.
		or (type == MT_GARGOYLE) or (type == MT_BIGGARGOYLE)
			GOTYPE = 4
		end
	elseif (FLAGS & MF_MISSILE) --Missiles need certain conditions to be snappable.
		if not (m.flags2 & (MF2_DEBRIS|MF2_DONTDRAW))
		and not (FLAGS & (MF_NOCLIPTHING|MF_NOCLIPHEIGHT))
		and (m.momx or m.momy)
		and not P_IsObjectOnGround(m)
		and not (m.target and m.target == t) --Not your own projectiles.				
		and not (m.xmissileguy and m.xmissileguy == t)
		and not (gametype != GT_COOP and m.target and m.target.player
		and (m.target.player.powers[pw_super] or m.target.player.xsonictable and m.target.player.xsonictable.supersonic
		or m.target.player.hypermysticsonic) )
			if not p.powers[pw_super]
				local SHIELD = p.powers[pw_shield] & ~SH_FIREFLOWER
				if not (SHIELD & SH_PROTECTFIRE)
					for i = 1,#CANTBOUNCE
						if type == CANTBOUNCE[i] return false end --
					end
				end
			end
			GOTYPE = 6
		end		
	elseif (FLAGS & MF_PAIN) or (type == MT_SPIKE) --Invulnerability specific objects.
		if xs.supersonic or p.powers[pw_invulnerability]
			if not (RB and (FixedHypot(FixedHypot(m.x-t.x, m.y-t.y), m.z-t.z) > 111*s.scale))
				GOTYPE = 7
			end
		else
			for i = 1,#CANTBOUNCE
				if m.type == CANTBOUNCE[i] return false end --
			end
		end
	elseif (m.sprite==SPR_PLAY)
		if INFO.spawnstate == S_PLAY_STND
		or m.skin and not m.player and not (m.tracer and m.tracer.player) and not (m.target and m.target.player)
			if (type != MT_OVERLAY)
				GOTYPE = 8
			end
		end
	elseif m.XS_CanSnapToMe --Custom method of letting something be snapdive-able.
		GOTYPE = 9 --Jumping on NPCs	
	elseif FLAGS == MF_SOLID --Some solid stuff.
		if (INFO.height >= 24<<16)
		and (INFO.radius < 40<<16)
		and not (m.MakeSolidAgain)
			if (m.frame & FF_PAPERSPRITE) 
			or (m.renderflags & RF_PAPERSPRITE)
			or type==MT_LAMPPOST1 
			or type==MT_LAMPPOST2	
			or (type>=MT_STALAGMITE0) and (type<=MT_STALAGMITE9)
				GOTYPE = 10
			end
		end
	elseif (FLAGS & MF_SPECIAL)
		if INFO.reactiontime==1 
		and (type > MT_YELLOWBRICKDEBRIS)
		and (INFO.radius >= 16<<16) and (INFO.height >= 32<<16)
		and not (FLAGS & (MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT))
		and not (m.flags2 & (MF2_DEBRIS|MF2_DONTDRAW|MF2_SPLAT))
		and not (m.target and m.target.player) and not (m.tracer and m.tracer.player)
			GOTYPE = 11 --Some very specific objects in mods.
		end
	end
	
	if GOTYPE
		if (m.ceilingz < t.floorz)
		or (t.ceilingz < m.floorz) --Sanity checks.
		or (s.z > m.z+m.height) --Height, sight, and FOF checks.
		or (m.z > s.z+s.height)
			return false --
		end
		
		local CLOSEST = s.snapclosest or "skip"
		DIST = (DIST) or FixedHypot(FixedHypot(m.x-s.x, m.y-s.y), m.z-s.z)
		
		if CLOSEST == "skip" or (CLOSEST <= DIST) --Compare last closest.
			if XSBlockingFOFs(s, m, t)
			or not P_CheckSight(t, m)
				return false --
			end
			local TWOD = ((s.flags2 & MF2_TWOD) or twodlevel) and true or false
			if TWOD==true
				s.xs_TWODy = s.y
			end
			s.snapclosest = DIST
			xs.snaptarget = (m.type == MT_XSDUMMYTRACKER) and m.target or m
			s.timetodie = true --Remove next frame.
		end
	end		
	return false --
end
addHook("MobjCollide", XSecheck, MT_XSSNAPDIVE)
addHook("MobjMoveCollide", XSecheck, MT_XSSNAPDIVE)

addHook("MobjSpawn", function(s)
	s.fuse,s.tics = 3,3
end, MT_XSSNAPDIVE)

--SnapDiveHitbox! DIVEHB! Stay on top of Sonic at all times.
addHook("MobjThinker", function(s) if not s.valid return end
	local t = s.tracer
	if s.timetodie or not (t and t.valid) s.state = S_NULL return end --
	if not s.KillType --s.KillType is used as a Chaos Lambent projectile.
		local REGDIST = (t.z+t.momz-(s.height*2/3))
		if (t.eflags & MFE_VERTICALFLIP)
			if (REGDIST <= t.ceilingz)
				P_SetOrigin(s, t.x+t.momx, t.y+t.momy, REGDIST)
			else
				P_SetOrigin(s, t.x+t.momx, t.y+t.momy, t.ceilingz)
			end	
		else
			if (REGDIST >= t.floorz)
				P_SetOrigin(s, t.x+t.momx, t.y+t.momy, REGDIST)
			else
				P_SetOrigin(s, t.x+t.momx, t.y+t.momy, t.floorz)
			end
		end
	end
end, MT_XSSNAPDIVE)

--But Sonic-sama, I have no hitbooox--God, do I need to do this for every character?

local function DummyHBadd(s)
	if not (s.dummyhb8 and s.dummyhb8.valid) //and s.health
		s.dummyhb8 = P_SpawnMobj(s.x, s.y, s.z, MT_XSDUMMYTRACKER)
		s.dummyhb8.target = s
		s.dummyhb8.tics = -1
		s.dummyhb8.radius = s.radius or 12<<16
		s.dummyhb8.height = s.height or 12<<16
		s.dummyhb8.xsdummyhbx = true
	end
end
addHook("MobjThinker", DummyHBadd, MT_TURRET)
addHook("MobjThinker", DummyHBadd, MT_METALSONIC_RACE)
addHook("MobjThinker", DummyHBadd, MT_METALSONIC_BATTLE)
addHook("MobjThinker", DummyHBadd, MT_MINUS)
addHook("MobjSpawn", DummyHBadd, MT_SIGN) --Not as vital to spawn after the mod is added, so use MobjSpawn.

//DummyTracker. Makes trackable hitbox for enemies that have none.
addHook("MobjThinker", function(s)
	if s.target and s.target.valid
		local t = s.target
		if t.health
			s.height = t.height
			s.radius = t.radius
			P_MoveOrigin(s, t.x, t.y, t.z) return --
		end
	end
	if s.valid s.state = S_NULL end
end, MT_XSDUMMYTRACKER)

local function TrackerCollide(s, v)
	if v and (v.type != MT_XSSNAPDIVE) return false end --Only collide with snapdive object.
end
addHook("MobjCollide", TrackerCollide, MT_XSDUMMYTRACKER)
addHook("MobjMoveCollide", TrackerCollide, MT_XSDUMMYTRACKER)


--Spikeball destruction for X Sonic, stolen from SA-Sonic who stole it from SMSreborn.
local function SpikeBallDestruct(s, v) --h was v, and v was s.
	if v and v.valid and IsXSonic(v) and v.player and v.player.xsonictable and v.player.xsonictable.supersonic
		if not (s.z > v.z+v.height)
		and not (v.z > s.z+s.height)
			
			S_StartSound(s, sfx_s3kb4) S_StartSound(s, sfx_bedeen)  S_StartSound(v, sfx_kc40) 
			s.flags = $|MF_NOCLIPTHING|MF_NOBLOCKMAP|MF_NOSECTOR|MF_NOCLIP & ~(MF_SOLID|MF_PAIN|MF_SPECIAL)
			s.flags2 = $|MF2_DONTDRAW
			
			XSon.Quake(v, 45<<16, 2, 777<<16)
			local ghs = P_SpawnMobjFromMobj(s, v.momx*2,v.momy*2,v.momz+(s.height/2), MT_EXPLODE)			
			if ghs and ghs.valid
				ghs.scale = $*2
				ghs.renderflags = RF_SEMIBRIGHT
				ghs.destscale = 99<<16
				ghs.flags2 = $|MF2_SHADOW
			end
			
			local SCALE = P_RandomRange(1,4)
			local ANGLE,DIST = v.angle*SCALE, max(50*s.scale, s.radius)
			local X,Y = FixedMul(DIST, cos(ANGLE))
			for d = 0,14 --Debris everywhere!
				ANGLE = $+ANG1*26
				ghs = P_SpawnMobjFromMobj(s, FixedMul(DIST, cos(ANGLE)), FixedMul(DIST, sin(ANGLE)), P_RandomRange(5,80)*s.scale, MT_BOSSJUNK)
				if ghs and ghs.valid
					local RNG = P_RandomRange(0,20)
					
					ghs.sprite,ghs.frame = SPR_USPK, A
					ghs.rollangle = d*ANG30
					ghs.flags = ($|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING|MF_NOBLOCKMAP) & ~MF_NOGRAVITY
					ghs.scale = (s.scale*2)/SCALE
					ghs.color,ghs.colorized = ((d%2) and SKINCOLOR_CARBON or SKINCOLOR_SILVER), true
					P_InstaThrust(ghs, v.angle-(ANG20*2)+(ANG1*6*d), (45-(RNG*3/2))*v.scale)
					P_SetObjectMomZ(ghs, (18-RNG)*(v.scale/2), false)
					ghs.shadowscale = 50333
					
					ANGLE,SCALE = $+ANG1*26, ($>3) and 1 or $+1
				end 
			end
			
			for i = 0,1
				local boom = P_SpawnMobjFromMobj(s,v.momx*i,v.momy*i,s.height*i, MT_THOK)
				boom.fuse = 7
				boom.color,boom.colorized = SKINCOLOR_RED,true
				boom.destscale = 99<<16
				boom.scalespeed = boom.scale
				boom.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS| ((i==0) and RF_FLOORSPRITE or 0) 
				boom.blendmode = AST_ADD
			end
		end
	end
end
addHook("MobjCollide", SpikeBallDestruct, MT_BIGMACE)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_BIGMACE)
addHook("MobjCollide", SpikeBallDestruct, MT_SMALLMACE)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_SMALLMACE)
addHook("MobjCollide", SpikeBallDestruct, MT_EGGMOBILE4_MACE)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_EGGMOBILE4_MACE)
addHook("MobjCollide", SpikeBallDestruct, MT_SPIKEBALL)
addHook("MobjMoveCollide", SpikeBallDestruct, MT_SPIKEBALL)

local function MineDestruct(v, h) --Don't get stopped by mines.
	if h and h.player and IsXSonic(h) and h.player.xsonictable and h.player.xsonictable.supersonic
		if not (h.z > v.z+v.height)
		and not (v.z > h.z+h.height)
			if v.health and v.valid
				P_SpawnGhostMobj(v)
				S_StartSound(v, sfx_kc41)
				P_InstaThrust(v, R_PointToAngle2(h.x, h.y, v.x, v.y), h.player.speed+(35*h.scale) )
				P_KillMobj(v, h, h)
			end			
		end
		return false --
	end
end
addHook("MobjCollide", MineDestruct, MT_BIGMINE)
addHook("MobjMoveCollide", MineDestruct, MT_BIGMINE)

--Never collide with stuff. I have to do it like this because MF_NOCLIPTHING has special behaviour on missiles.
local function XS_EdgeStopC(s) return false end
addHook("MobjCollide", XS_EdgeStopC, MT_XSEDGESTOP)
addHook("MobjMoveCollide", XS_EdgeStopC, MT_XSEDGESTOP)

--Screw points. Tokens will give you rings, regardless of SRB2's vanilla behaviour.
--Updated version of Adventure Sonic's token behaviour.
local function TokenCol(m, s)
	if not All7Emeralds(emeralds) or (gametype != GT_COOP) or not (sugoi==nil) return end --Coop only!
	if m and m.valid and s and IsXSonic(s)
		if not (m.z > s.z+s.height) --Height check.
		and not (s.z > m.z+m.height)
		and (m.XS_gotme!=true)
		and s.player and s.health
			for z in players.iterate
				if z and z.valid and z.mo and z.mo.valid
				and (IsXSonic(z.mo) or z.mo.skin=="adventuresonic" or z.mo.skin=="sms")
					if z.mo.health and not (z.bot)
						local DIST = FixedHypot(FixedHypot(s.x-z.mo.x, s.y-z.mo.y), s.z-z.mo.z)
						if (DIST < 1250<<16) or (z.mo==s) --Give the rings to every other X Sonic.
							S_StartSound(z.mo, sfx_s3k34)
							P_GivePlayerRings(z, (z.mo.skin=="adventuresonic") and 50 or 25)
						end
					end
				end
			end
			m.XS_gotme = true
			s.XS_token = s.XS_token and $+1 or 1
		end
		if (m.XS_gotme != true)
			return false --
		end
	end
end
addHook("MobjCollide", TokenCol, MT_TOKEN)
addHook("MobjMoveCollide", TokenCol, MT_TOKEN)

--The source for your other emerald.
local function EggCollide(s, v)
	if v and v.skin == "xsonic" and v.player and v.health 
		if not (v.z > s.z+s.height+324<<16) --Height check. Extra generous since I can't guarantee where these'll spawn....
		and not (s.z > v.z+v.height+324<<16)	
			local p = v.player local xs = p and p.xsonictable
			if xs and not (p.powers[pw_super] or xs.darkphase)
			and not (s.DidOffering or v.XS_EmeraldScam)
			and not (XS_FAKEEMERALDS==127)
				if v.XS_PlanetEgg and (v.XS_PlanetEgg.EGG == s)
					v.XS_PlanetEgg.timer = 10
				else
					v.XS_PlanetEgg = {EGG = s, timer = 10, init = 1}
				end
			end
		end
	end return false --
end
addHook("MobjCollide", EggCollide, MT_XSPLANETEGG)
addHook("MobjMoveCollide", EggCollide, MT_XSPLANETEGG)

local CLTABLE = {
[MT_EGGGUARD] = true,
[MT_EGGSHIELD] = true,
[MT_TNTBARREL] = true,
[MT_SPIKE] = true,
[MT_WALLSPIKE] = true
}

--Seperate hitbox for the Chaos Lambent, mostly for performance's sake.
--Note that it's also used for the Super Sonic Boom Kick!
local function XSLambentHB(s, m) 
	if not (s.valid and s.KillType) return false end --
	local t = s.target
	if not (t and t.valid and t.player and t.player.xsonictable and m.valid)
		return false --
	end
	
	--We DO have a target and it IS a player? Let's go, we're in business!
	
	if m.ChaosBeam and s.ChaosBeam
		if (m.ChaosBeam==s.ChaosBeam)
			return false --Colliding with your own kind!
		end		
		if not (m.z > s.z+s.height)
		and not (s.z > m.z+m.height)
		
			local p = t.player local xs = p.xsonictable local CHAOS = xs.ChaosLambent
			if not (CHAOS) return false end --
			
			if CHAOS.struggle
				local GFX = s.GFX --Stop each projectile!
				for i = 1,2
					if i==2 GFX = m.GFX end
					if not (GFX and GFX.valid and (GFX.NoKillType!=true)) continue end 
					GFX.momx,GFX.momy,GFX.momz = 0,0,0
					GFX.NoKillType = true
					GFX.color,GFX.colorized = SKINCOLOR_RUBY,true
					GFX.spritexscale,GFX.spriteyscale = $*2,$*2
					GFX.fuse,GFX.tics = $+12,$+12
					GFX.sttYspeed = nil
					GFX.scalespeed = FRACUNIT
				end
				
			--We're both firing and not already in a beam struggle?
			elseif (CHAOS.struggledone!=true)
				
				local mt = m.target 
				local tp = mt and mt.player
				local xss = tp and tp.xsonictable
				local CHAOS2 = xss and xss.ChaosLambent

				if CHAOS2 and not CHAOS2.struggle and not (CHAOS2.struggledone==true)
				
					local RB = xs.FreeFlight
					if RB and (RB.LockOn == m.ChaosBeam) and t.health --Time for hype!
						local RBB = xss and xss.FreeFlight
						if not (RBB and RBB.LockOn and RBB.LockOn.valid and RBB.LockOn == t)
							if not (XS_NOPVP == 2)
								return false --
							end
						end					
						if not S_SoundPlaying(t, sfx_xsvoc7)
							S_StartSound(nil, sfx_xsvoc7, p)
							S_StartSound(t, sfx_xsvoc7)
							S_StartSound(t, sfx_xssfxp)
						end
						if not S_SoundPlaying(mt, sfx_xsvoc7)
							S_StartSound(mt, sfx_xsvoc7)
							S_StartSound(nil, sfx_xsvoc7, tp)
							S_StartSound(mt, sfx_xssfxs)
						end
						S_StartSound(s, sfx_xssfxs)
						S_StartSound(t, sfx_xssfxp)
						
						XSon.LambentStruggle(
						p,t,xs,RB,CHAOS, 
						tp,mt,xss,RBB,xss.CHAOS2
						)
						XSon.LambentStruggle(
						tp,mt,xss,RBB,CHAOS2,
						p,t,xs,RB,CHAOS
						)
					end
				end
			end
		end 
		return false --
	elseif m.health
	and not (m.z+m.momz > s.z+s.height+(48<<16))
	and not (s.z > m.z+m.height+m.momz+(48<<16))	
		if m.player --Wot, PVP?
			if (m==t) --That's US, dumbass.
			or not (s.ChaosBeam) --Don't collide with our own beams
			or not (m.player.powers[pw_super]>1) --Ignore non-super players. 
			or m.XStransform or m.XS_PVPCD
			or ((XSon.PsuedoPVP(m, t.player, "tenkaichi"))==false)
				return false --
			else
				local RB = t.player.xsonictable and t.player.xsonictable.FreeFlight
				if RB and RB.LockOn and (RB.LockOn!=m) and (XS_NOPVP!=2) and RB.LockOn.valid
					return false --Can't hit someone you're not fighting.
				else
					local xss = m.player.xsonictable
					local RBB = xss and xss.FreeFlight
					if not RBB
					or RBB.LockOn and (RBB.LockOn!=t) and (XS_NOPVP!=2) and RBB.LockOn.valid
						return false --They're fighting someone else.
					elseif s.ChaosBeam
						local CHAOS2 = xss.ChaosLambent
						if CHAOS2
							local GFX = s.GFX --Stop each projectile!
							for i = 1,2
								if i==2 GFX = m.GFX end
								if not (GFX and GFX.valid and (GFX.NoKillType!=true)) continue end 
								GFX.momx,GFX.momy,GFX.momz = 0,0,0
								GFX.NoKillType = true
								GFX.color,GFX.colorized = SKINCOLOR_RUBY,true
								GFX.spritexscale,GFX.spriteyscale = $*2,$*2
								GFX.fuse,GFX.tics = $+12,$+12
								GFX.sttYspeed = nil
								GFX.scalespeed = FRACUNIT
							end
						end
					end
					if RBB and xss.ChaosSnap 
					and (abs(m.player.cmd.sidemove) > 16) and (m.player.cmd.forwardmove < 20)
					and ((xss.ChaosSnap.init or 0) < 8) 
					and not (RBB.SnapOverHeat and (RBB.SnapOverHeat > 23)) --Not if they were spamming.
					and ((RBB.LockOn==t) or (XS_NOPVP == 2))
						local ANGLE,PLUS = XS_AngleDifference(R_PointToAngle2(s.x, s.y, s.x+s.momy, s.y+s.momy), XSon.MOMANGLE(m), 2)
						if (ANGLE > ANG30) and (ANGLE <= ANGLE_135)
							RBB.CantHitLambence = max(RBB.CantHitLambence or 0, 8)
						end
					end
				end
			end
		elseif not (
			(m.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)) and (m.flags & MF_SHOOTABLE) and not (m.flags2 & MF2_FRET) and (m.sprite != SPR_ROSY)
			or CLTABLE[m.type] == true
		--	or (m.type == MT_EGGGUARD) or (m.type == MT_EGGSHIELD) or (m.type == MT_TNTBARREL)
		--	or (m.type == MT_SPIKE) or (m.type == MT_WALLSPIKE)
			)
			return false --Must be one of those guys!
		end 
		local tp = m.player
		
		if (m.flags & MF_MONITOR)
			if (s.NotThemS == m) return false end
			
			local DIST = FixedHypot(m.x-t.x, m.y-t.y) --Player must be able to see them and be nearby!
			if (DIST < 1600*s.scale) and (s.XS_ONEPERTIC!=leveltime) and P_CheckSight(m, t)
				s.NotThemS = m
				s.XS_ONEPERTIC = leveltime --Don't ever pierce walls for monitors at all.
				P_KillMobj(m, s, t)
			end
		elseif not (m.flags & (MF_ENEMY|MF_BOSS)) and not tp
			if (s.XS_ONEPERTIC==leveltime) or (s.NotThemS == m) return false end
			s.NotThemS = m
			s.XS_ONEPERTIC = leveltime
			P_DamageMobj(m, s, t, 5, DMG_NUKE) --Die!
		else
			local DIST = tp and FixedHypot(m.x+(m.momx*2)-t.x, m.y+(m.momy*2)-t.y) or FixedHypot(m.x-t.x, m.y-t.y) 
			local PASS = false
			
			if (DIST < 1100*s.scale) --Pierce walls to a certain degree at point-blank range.
				PASS = true
			elseif tp
				local xss = tp and tp.xsonictable 	
				if tp			
					if s.checksight==true --We did periodic checks beforehand.
						PASS = true
					elseif tp.powers[pw_super]
					and ((FixedHypot(FixedHypot(s.x-(m.x+m.momx), s.y-(m.y+m.momy) ), s.z-(m.z+m.momz)) ) < 230*m.scale)
						PASS = true --You're literally in the middle of that. Fuck your cover. Die.		
					elseif s.checksight==nil and P_CheckSight(t, m)
						s.checksight = true
						PASS = true
						if xs.ChaosLambent
							xs.ChaosLambent.checksight = max(xs.ChaosLambent.checksight or 0, 3)
						end
					end
				end
			elseif (s.XS_ONEPERTIC != leveltime)
				s.XS_ONEPERTIC = leveltime
				if P_CheckSight(t, m) --Careful...these cause C stack overflows if not tightly regulated in MobjCollide!
					PASS = true
				end
			end
			if PASS==true
				local xss = tp and tp.xsonictable 
				if xss
					local p = t.player local xs = p.xsonictable local RB = xs.FreeFlight
					local RBB = xss.FreeFlight
					local REMOVEHB = false
					
					if not xss.launchcd --For performance and proper ring drain.
					and not (m==s.NotThem)
					and not (RBB and (RBB.DodgeFrames or RBB.CantHitLambence))
					and not (RB and RB.LockOn and (RB.LockOn!=m) and RB.LockOn.valid)
					and not tp.XSonicMenu
					and not (xss.ChaosLambent and (xss.ChaosLambent.firing or xss.ChaosLambent.struggle or xss.ChaosLambent.struggledone))
						if (m.state != S_PLAY_PAIN) and (m.state != S_PLAY_STUN) 
						and not (RBB and RBB.painstun)
							if RBB
								if RB and RB.LockOn==m
									
								end
								if (RBB.RingBurstChase and RB.RBmissed==nil or (RBB.haymaker and RBB.haymaker.timer<6)) 
									if not (RBB.RingBurstChase) xss.launchcd = 1 return false end 
									local RESIST = RBB.CLresist or 0
									RBB.CLresist = RESIST+3 --The Ring Burst can somewhat smash through, though far better if swerving.
									if (RBB.CLresist < 26) or (RBB.RingBurstChase < 3)
										xss.launchcd = 1 return false --They're resisting it for now!
									end
								end
							end
							
							if xss.zipdash or xss.zipdashslide --Cancel their zipdash
								XSZipDashCancel(tp, m, xss) 
								xss.zipdash,xss.zipdashslide,xss.zipcontinue = 0,0,nil
							end
							if not RB
								XSon.TryFlight(p,t,xs, nil, 101, 200, "force")
								RB = xs.FreeFlight
							end
							local HADPVP = true
							if not RBB
								XSon.TryFlight(tp,t,xss, nil, 101, 200, "force")
								t.justgotPvPed = 2
								RBB = xss.FreeFlight
								HADPVP = false
							end							
							XSon.StartLockOn(t,m) --Begin, battle!

							if (gametyperules & (GTR_FRIENDLYFIRE|GTR_RINGSLINGER))
							and not ((gametyperules & GTR_TEAMS) and (tp.ctfteam==p.ctfteam) and not (gametyperules & GTR_FRIENDLYFIRE))
								P_DamageMobj(m, s, t, 2, DMG_NUKE)
							end
							
							if tp.powers[pw_super]
								XSon.DeTransformGFX(tp,m,xss, "sparkles")
								RBB = xss.FreeFlight
								if RBB
									if HADPVP!= false
										
										local ghs = XSon.ChargeSpark(m, SKINCOLOR_GOLDENROD, 120000, nil, AST_ADD, 38, 34|FF_TRANS10)
										ghs.destscale = $*7 ghs.scalespeed = $*2
										ghs.tracer = m ghs.ditchtracer = 16
										
										for i = 0,8
											ghs = XSon.SmokeMist(m, 0, 99*m.scale)
											if ghs and ghs.valid
												ghs.spritexscale,ghs.spriteyscale = $+(i*2500),$+(i*2500)
											end
										end
										XSon.SubtractPvPHealth( (s.XChaosControlHB==true) and 1205 or 1037, RBB, RB)
										
										RBB.PvPENERGY = max(0, min(10000, $+99))
										RBB.CantHitLambence = 37 --Can't hit them immediately again.
										REMOVEHB = true
										if not tp.powers[pw_flashing]
											tp.powers[pw_flashing] = 21
										end									
									end
									local TMOMZ = ((xs.ChaosLambent) and xs.ChaosLambent.Zangle or 0)
									if (abs(m.z-m.ceilingz) < 333*t.scale)
										TMOMZ = (TMOMZ > 0) and -9*t.scale or -40*t.scale
									end
									m.momz = ($/3) + (TMOMZ*P_MobjFlip(t))
									RBB.painstun = FRACUNIT
									m.angle = t.angle+ANGLE_180	tp.drawangle = m.angle								
									if not tp.powers[pw_nocontrol] tp.powers[pw_nocontrol] = 7 end
									
									for i = -1,1,2
										XSon.EarthQuake(m, 2, (850+(100*i))*m.scale, "tracer", 4<<15, 
										33033, (-ANG10*5)*i, J|FF_TRANS10, 44, (i==1) and SKINCOLOR_SKY or SKINCOLOR_RUBY, AST_ADD)
									end
								else
									P_DoPlayerPain(tp, s, t)
									if m.state != S_PLAY_STUN
										m.state = S_PLAY_STUN
									end
								end
							end
							if not xss.hudboost
								--Loud ass SFX.
								local ghs = P_SpawnMobjFromMobj(m,0,0,0, MT_THOK)
								ghs.tics = 220 ghs.fuse = 220 ghs.flags = MF2_DONTDRAW
								if tp.powers[pw_super] and not S_SoundPlaying(m, sfx_xssfpr)
									S_StartSound(m, sfx_xssfpp)
									S_StartSound(m, sfx_xssfpr) 
						--			S_StartSoundAtVolume(m, sfx_xssfpt, 50)
								end
								if (p == displayplayer)
									S_StartSoundAtVolume(nil, sfx_xssfxp, 182, p)
									S_StartSoundAtVolume(nil, sfx_xssfxr, 210, p)
									P_StartQuake(90<<16, 15)
									P_FlashPal(p, PAL_RECYCLE, 2)
									S_StartSoundAtVolume(nil, sfx_xssfpp, 225, p)
									S_StartSound(nil, sfx_xssfpr, p)
									S_StartSoundAtVolume(nil, sfx_xssfpt, 89, p)
								else
									S_StartSoundAtVolume(ghs, sfx_xssfxp, 180)
									S_StartSoundAtVolume(ghs, (leveltime%2) and sfx_xssfxr or sfx_xssfxt, 180)
									XSon.Quake(m, 90<<16, 15, 2600*t.scale)
									XSon.Flash(m, PAL_NUKE, 3, 2000*t.scale)
								end
								local ghs = P_SpawnMobjFromMobj(m, 0,0,0, MT_XSTRACKER2) --Smoketrails
								if ghs and ghs.valid
									ghs.target = m ghs.SteamTrail = 1	
								end											
								xss.hudboost = 1
								xs.hudboost = xss.hudboost
								XSon.ElecticBody(m, 9, SKINCOLOR_FLAME, FF_TRANS10, AST_TRANSLUCENT) 
								XSon.SuperSonicWind(m, SKINCOLOR_GARNET, 57, FF_TRANS10, FRACUNIT+39000)
								tp.xssuperflash = {trans = FF_TRANS20, timer = 21, speed = 3, reversephase = true, COLOR = SKINCOLOR_FLAME}
								p.xssuperflash = tp.xssuperflash
							end
						end
						if (tp.speed < 70*t.scale)
							P_InstaThrust(m, t.angle, 80*t.scale)
						end	
						if xs.ChaosLambent and xs.ChaosLambent.Zangle and (abs(m.momz) < 23*t.scale)
							m.momz = $+xs.ChaosLambent.Zangle
						end
						P_Thrust(m, t.angle, (tp.speed < 155*s.scale) and t.scale*8 or t.scale)	
						if (REMOVEHB==true)
							--Effects!
							if m and m.valid
								local SFX = XSon.GibSound(m, sfx_xssfxp, 255, 199)
								S_StartSound(SFX, sfx_xssfp9) S_StartSound(SFX, sfx_xssfxs)
								
								local ghs = XS_CircleWave(m, 1, SKINCOLOR_GARNET) 
								ghs.blendmode = AST_ADD
								ghs.angle = t.angle+ANGLE_90
								ghs.tracer = nil
								ghs.sprite = SPR_XSG2
								ghs.frame = 31|FF_TRANS10
								ghs.sttfade = 11
								ghs.tics,ghs.fuse = 18,18
								ghs.spritexscale,ghs.spriteyscale = $*10/3,$*2
								ghs.scalespeed = FRACUNIT/2
								ghs.spriteyoffset = $-7<<16
								ghs.momx,ghs.momy,ghs.momz = m.momx/2,m.momy/2,m.momz/2
								
								ghs = XSon.ElectricBody(m, 40, SKINCOLOR_SKY)
								ghs.blendmode = AST_TRANSLUCENT
								ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
								ghs.tracer = nil
								ghs.spritexscale = 4<<16
								ghs.spriteyscale = 2<<16
								ghs.spriteyoffset = $-16<<16
								ghs.destscale = 99<<16
								ghs.sttfade = 3
								ghs.scalespeed = 1<<15
								P_InstaThrust(m, t.angle, max(50*t.scale, tp.speed))
								if RBB and RBB.SRAlunge
									RBB.SRAlunge,RBB.SRAdrain = nil
								end
							--	(s, NO, DIST, SPEED, CSCALE, SCALESPEED, ZANGLE, FRAME, FUSE, COLOR, BLEND)
								if xss.FreeFlight
									XSVoice(tp, m, xss, sfx_xsvops, "ForceEcho")
								end
								S_StartSoundAtVolume(m, sfx_xsvopr, (s.XChaosControlHB==true) and 255 or 180)
								XSon.GibSound(m, sfx_xsvopr, (s.XChaosControlHB==true) and 255 or 180, 180)						
							end
							
							xss.launchcd = 1
							if xs.ChaosLambent
								XSon.RemoveLambentHitBoxes(p) --Remove at the end!
								xs.ChaosLambent.autoadjust = R_PointToAngle2(t.x, t.y, m.x+m.momx*5, m.y+m.momy*5)					
								return false --
							end
						end					
					elseif RBB and RBB.DodgeFrames --They're dodging/immune!
						if xss.ChaosLambent
							if s.KillType
								P_InstaThrust(s, tp.drawangle, (leveltime%2) and 18<<16 or 36<<16)
								s.momz = $-1 --Recoil the projectile off our target.
								s.fuse = min($,10)
								s.KillType = nil
							end
						elseif xss.ChaosSnap or xss.snapdivetrick
							s.NotThem = m return false--
						end
						return false --
					end
					xss.launchcd = 1
				else
					if (s.NotThemS == m) return false end
					s.NotThemS = m	
					P_DamageMobj(m, s, t)
				end
			end
		end
	end return false --	
end
addHook("MobjCollide", XSLambentHB, MT_XSLAMBENTHB)
addHook("MobjMoveCollide", XSLambentHB, MT_XSLAMBENTHB) 
/*
addHook("MobjMoveBlocked", function(s)
	if s return false end
end, MT_XSLAMBENTHB)
addHook("MobjLineCollide", function(s)
	if s return false end
end, MT_XSLAMBENTHB) */