--#==================================================================================================================#	
--Here are all damage, death, and ShouldDamage hooks! Both player or non-player related, starting with the player.
--#==================================================================================================================#	

local XSon = XSon
local SeasonFour = 0
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

--Out of the death pit at the cost of your shield!
local function NegatePit(s, p, FF)
	if FF and FF.LockOn and FF.LockOn.valid
		if not (s.eflags & MFE_JUSTHITFLOOR)
			s.z = $+(99*P_MobjFlip(s))
		end return --Simply treat it as a regular floor in Tenkaichi.
	end
	s.xsmaxcrush = s.xsmaxcrush and $+7 or 7
	s.momx,s.momy = $/4,$/4
	s.z = $+(99*s.scale*P_MobjFlip(s))
	
	local ghs = P_SpawnGhostMobj(s)
	ghs.renderflags = RF_SEMIBRIGHT|RF_NOCOLORMAPS
	ghs.blendmode = AST_ADD
	ghs.colorized = true
	ghs.destscale = 99<<16
	
	if (XS_Super(p) != 3) 
		if s.XSassault and s.XSassault.timer != nil --Cancel assault if not Super
			s.XSassault.timer = max($,200)
			if (s.momz*P_MobjFlip(s) <= 0) and not p.powers[pw_shield]
				p.rings = max($-15, 0)
				S_StartSound(s, sfx_antiri)	
			end
		end		
		if p.powers[pw_shield]
			S_StartSound(s, sfx_wirlsg) S_StartSound(s, sfx_shldls)
			S_StartSound(s, sfx_shrpgo) S_StartSound(s, sfx_kc5b)
			P_NukeEnemies(s, s, 180<<16)
			XSon.Flash(s, PAL_RECYCLE, 4) 
			P_RemoveShield(p)
		end
	else
		local xs = p.xsonictable
		if xs and not xs.FreeFlight
			XSon.TryFlight(p,s,xs, nil, 110)
			if (s.momz*P_MobjFlip(s) <= 0) and not xs.FreeFlight
				p.rings = max($-5, 0)
				S_StartSound(s, sfx_antiri)	
			end
		end
	end
	if (s.z+s.height > (s.ceilingz-9*s.scale))
		s.momz = -30*s.scale
	else
		P_SetObjectMomZ(s, 30<<16)
	end
	s.state = S_PLAY_ROLL
	p.pflags = $|PF_JUMPED & ~(PF_THOKKED|PF_SPINNING|PF_STARTDASH|PF_STARTJUMP|PF_NOJUMPDAMAGE|PF_STARTDASH)	
end

//X Sonic does NOT get damaged when....
addHook("ShouldDamage", function(s, i, so, dmg, dmgtype) 
	if i and ((i.type==MT_XSEDGESTOP) or (i.type==MT_XSSNAPDIVE) or (i.type==MT_XSSANDBAG) or (i.KillType=="SonicBoom"))
		return false --NEVER get hurt by these!
	end
	if not (IsXSonic(s) and s.player and s.player.xsonictable) return end --
	local p = s.player local xs = p.xsonictable local yu = xs
	
	--Fly out of death pits as Super Sonic.
	if dmgtype and (dmgtype & DMG_DEATHMASK)
		if dmgtype == DMG_DEATHPIT
			if not (s.xsmaxcrush and (s.xsmaxcrush > 30))
				if p.xsringburst --Bounce off pits.
					p.cmd.buttons = $ & ~BT_SPIN
					XSon.Bounce(p, s, xs)
					s.xsmaxcrush = s.xsmaxcrush and $+7 or 7
					if (s.momz*P_MobjFlip(s) <= 24*s.scale)
						P_SetObjectMomZ(s, 12*s.scale, true) --Bounce up at least a LITTLE.
					end return false --	
				elseif ((XS_Super(p)) == 3) or s.XSassault
					NegatePit(s, p, xs.FreeFlight) return false --
				end
			end
		elseif dmgtype == DMG_CRUSHED --Come on now...
			if s.XS_crushimmune
				return false --
			elseif p.xsringburst or ((XS_Super(p)) == 3) or s.XStransform 
				if i and  (i.flags & (MF_PUSHABLE|MF_MONITOR)) or so and (so.flags & (MF_PUSHABLE|MF_MONITOR))
					return false --
				end
			end
		end
		return --
	elseif s.XStransform and (s.sprite2 & (SPR2_TRNS|FF_SPR2SUPER))
	or s.XS_DarkTable and s.XS_DarkTable.transforming
	or s.XS_EmeraldScam
		return false --
	end
	
	--Total invincibility to all but instakills during your transformation!
	--Give Sonic immunity to electric damage, enemies, MF_PAIN, missiles not sourced from players, and even death pits with the Ring Burst.	
	if p.xsringburst
	
		if so and so.player and (so!=s) return end --Regular behaviour if source of damage is another player.
		
		if xs.supersonic
		or i and (i.flags & (MF_BOSS|MF_ENEMY|MF_PAIN|MF_MISSILE)) and not (dmg and (dmg > 2))
		or dmgtype == DMG_ELECTRIC --Can't be hurt by electricity.	
			return false --Shrug off most weak attacks.
		end

	--Attack Enemies and Projectiles during your snapdive, overriding damage.	
	elseif xs.snapdive
	or xs.snapdivetrick and (xs.snapdivetrick < 10)
	or (s.state >= S_PLAY_XKICK2) and (s.state <= S_PLAY_XKICK4)

		if so 

			if (so == s) return false end --

			if i
				if (i.type == MT_UNIBALL) or (i.type == MT_POINTYBALL)
					s.XSresist = 0 XSSnapDiveReset(p, s, xs, true) return --
				end
				if (i.flags & MF_MISSILE)
					if (i.height > 124*s.scale) --Can't snapdive enormous projectiles!
					or dmg and (dmg > 2) and dmg != 1000 and dmg != 10000 
					or (so.player and (so.player.hypermysticsonic or so.player.powers[pw_super])) --Power scale check.
						return --
					end return false --	
				elseif (i.flags & (MF_BOSS|MF_ENEMY)) or (i.type == MT_CRUSHCLAW) or (i.type == MT_DRAGONTAIL)
					if (i.height > 400*s.scale) return end --Can't divesnap titans!
					return false --
				end
			end
		end
		if i and (i.type == MT_SPIKE)
			if xs.snapdive and (p.cmd.buttons & BT_SPIN)
			or xs.snapdivetrick or xs.snapdivekick
			or (s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK4)
				return false --
			end
		elseif (dmgtype == DMG_SPIKE) and (xs.snapdivetrick or xs.snapdivetrick)
			return false
		end
		
		if xs.snaptarget and (xs.snaptarget == so or xs.snaptarget == i) and xs.snaptarget.valid
			return false --Negate damage from your own target, unless it's spike damage.
		end
	end
	if xs.darkphase
		if xs.darkphase==3 and not (so and (so!=s) and so.player and (so.player.powers[pw_super] or so.player.hypermysticsonic))
		or s.XSassault and s.XSAssaultT and (i or so)
			return false --
		end
	end
	if i and (s.XSresist or s.XSlunging)
		if i == s.XSAssaultT
		or (i.flags & (MF_MISSILE|MF_PAIN|MF_ENEMY|MF_BOSS|MF_FIRE|MF_SHOOTABLE)) and not (so and so.player)
			return false --
		end
	end
end, MT_PLAYER)

//When damage occurs, then...
addHook("MobjDamage", function(s, i, so, dmg, dmgtype)
	if dmgtype == DMG_CRUSHED
		if so and so.KickedByBlueBlur and so.KickedByBlueBlur.PushKick and (so.flags & MF_PUSHABLE)
		or i and i.KickedByBlueBlur and i.KickedByBlueBlur.PushKick and (i.flags & MF_PUSHABLE)
		--	CONS_Printf((s.player) or consoleplayer, "\x82"+"CRUSH override") 
			return true --Override crush damage from pushables.
		end
	elseif i and i.type == MT_XSEDGESTOP return true end --NEVER.
	
	if not (IsXSonic(s) and s.player and s.player.xsonictable) return end --Be X Sonic from here on out.
	local p = s.player local xs = p.xsonictable
	
	if xs.snapdive or xs.snapdivetrick or xs.snapdivekick
		XSSnapDiveReset(p, s, xs, true)
	end
	if (s.state == S_PLAY_XWALLRUN)
		s.state = S_PLAY_XDIVEFLIP1
	end
	xs.poutysonic,xs.poutydrop = 0,0
	xs.painrecovery = 0
	xs.bonked = 0
	xs.jumpboost = 0
	xs.didjumpboost = false
	xs.framejump = 0
	xs.squat = 0
	s.rollangle = 0
	s.spritexoffset,s.spriteyoffset = 0,0
	xs.hitspring = 0
	if s.XSassault and xs.darkphase and (xs.darkphase!=3) and s.XSAssaultT and i and so
		XSon.AssaultAnim(p,s,xs)
		s.XSassault,s.XSAssaultT = nil
	end
	s.XS_EmeraldScam = nil
	p.xswallrun,xs.walljump,xs.walljumpP = nil
	xs.zipclimbedwall = 0
	xs.squat,xs.squatcharge,xs.slowsquat = 0,0,false
	XSon.SwapStretch(p, s, xs)
	XSon.StopShout(s)

	--Knockback system for X Sonic. Barely applies since enemies don't register momx/momy. Joy.
	if i
		XSon.Quake(s, 44<<16, 3)
		if (XSon.Charger(i) == true)
			xs.damageboost = 44*i.scale --That's the FaceStabber spearing you.
		elseif (i.momx or i.momy) and i.height and (i.height > s.height/3)
		and not (s.eflags & (MFE_UNDERWATER|MFE_SPRUNG)) --Don't be too tiny!
			if not (i.type==MT_METALSONIC_BATTLE) and not (i.type==MT_ENERGYBALL)
				local SPEED = abs(i.momx)+abs(i.momy) --Speed Calculations.
				if (SPEED >= 16*s.scale) --Go at least this fast to consider it a "ram"
					local MULT = (i.height <= s.height/2) and 40 or (i.height <= s.height) and 80 or 100
					if MULT >= 100
						MULT = 100+((i.height-s.height)/(s.scale/2))
					end
					local MOMX = FixedDiv(FixedMul(i.momx, MULT), 100)
					local MOMY = FixedDiv(FixedMul(i.momy, MULT), 100)
					SPEED = abs(MOMX)+abs(MOMY)
					xs.damageboost = SPEED
				end
			end
		end	
	end
	if xs.darkphase and xs.darkphase!=3 and (netgame!=true) --Desyncs in netgames, use a different method.
		XSon.AddDarkness(s, 5<<16)
		if not p.powers[pw_shield]
			P_DoPlayerPain(p, i, so)
			P_PlayRinglossSound(s, p)
			if xs.darkphase==2
				if (p.rings > 10)
					P_PlayerRingBurst(p, 8)
				end
				p.rings = max($-10, 0)
			else
				if (p.rings > 20)
					P_PlayerRingBurst(p, 15)
				end
				p.rings = max($-20, 0)
			end
			if G_PlatformGametype()
				P_NukeEnemies(s,s, 150<<16)	
			else--if (gametype != GT_COOP)
				P_PlayerFlagBurst(p, false)
				P_PlayerEmeraldBurst(p, false)
				P_PlayerWeaponPanelOrAmmoBurst(p)
			end
			return true--
		end	
	end
end, MT_PLAYER)

--Some special death behaviour for the player! Or rather, X Sonic specifically.
addHook("MobjDeath", function(s, i, so, dmgtype) if not (IsXSonic(s) and s.player and s.player.xsonictable) return end
	local p = s.player local xs = p.xsonictable local yu = xs
	p.pflags = $ & ~PF_CANCARRY
	if s.skin == "xsonicserious" or s.skin == "xsonicoverlay"
		R_SetPlayerSkin(p, "xsonic")
		s.skin = "xsonic"
	end
	s.eflags = $ & ~MFE_FORCESUPER
	XSon.ResetAll(p,s,xs) --Seems to be safe...
	XSon.StopShout(s)
	S_StopSoundByID(s, sfx_xsvoco)
	if p.powers[pw_super]
		p.powers[pw_super] = 0
	end
	XSon.SetMusic(p,s,xs)
	if (s.state == S_PLAY_XWALLRUN)
		s.state = S_PLAY_DEAD
	end	
	XSon.RevertXColor(p,s,xs)
	if xs.PowerRing
		if not (mapheaderinfo[gamemap] and mapheaderinfo[gamemap] == "TUTORIAL X")
		and not (tutorialmode==true)
			xs.PowerRing = 0
			s.XS_pwringget = -6
			S_StartSound(s, sfx_antiri, p)
		end
	end	
	
	if (dmgtype == DMG_DROWNED) or (dmgtype == DMG_SPACEDROWN)
		s.XS_EnemyDeath = "drown"
		s.XS_KBDeathAnim,s.XS_SideFall = nil,nil
	else
		s.XS_KBDeathAnim = 20
		s.XS_SideFall = 80
		
		SeasonFour = ($<2) and $+1 or 0
		if i and i.valid and ((i.flags & (MF_ENEMY|MF_MISSILE|MF_BOSS)) or i.player)
			s.XS_EnemyDeath = i
			XSVoice(p, s, xs, sfx_xsvocx+SeasonFour)
		else
			s.XS_EnemyDeath = nil
			if (dmgtype == DMG_DEATHPIT)
				XSVoice(p, s, xs, (leveltime%2) and sfx_xsvoc5 or sfx_xsvocx+SeasonFour)
				s.XS_EnemyDeath = "pit"
				s.XS_KBDeathAnim,s.XS_SideFall = nil,nil
				if (s.momz*P_MobjFlip(s) > -22*s.scale)
					P_SetObjectMomZ(s, -23*s.scale)
				end
			else
				XSVoice(p, s, xs, sfx_xsvocx+SeasonFour)
			end
		end
	end
end, MT_PLAYER) 

--Pop a power Ring Out of these monitors!
local function SpawnPowerRing(s, i, so)
	if not (so and IsXSonic(so) and so.player) return end --
	
	local ghs = P_SpawnMobjFromMobj(s,0,0,s.height/3,MT_EXPLODE)
	ghs.dispoffset = 140
	ghs.destscale = 99<<16
	
	ghs = P_SpawnMobjFromMobj(s,0,0,s.height/2, MT_XSPOWERRING)
	if ghs and ghs.valid
		local SPEED = max(16<<16, s.player and s.player.speed or 0)
		P_InstaThrust(ghs, so.angle, SPEED/4)
		ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS
		ghs.fuse = 440
		ghs.momz = 9*so.scale*P_MobjFlip(so)
		ghs.flags = $|MF_GRENADEBOUNCE|MF_BOUNCE & ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT)
		local ANG = so.angle+ANGLE_180
		for i = -8,7
			if (P_TryMove(ghs, s.x+FixedMul(36<<16, cos(ANG)), s.y+FixedMul(36<<16, sin(ANG)), true) )
				break
			end 
			ANG = $+ANGLE_45
		end
	end
	i,so = nil,nil --Must've been the wind...
	P_KillMobj(s,s,s, 1)
	return true --
end
addHook("MobjDeath", SpawnPowerRing, MT_ATTRACT_BOX)
addHook("MobjDeath", SpawnPowerRing, MT_ATTRACT_GOLDBOX)
addHook("MobjDeath", SpawnPowerRing, MT_THUNDERCOIN_BOX)
addHook("MobjDeath", SpawnPowerRing, MT_THUNDERCOIN_GOLDBOX)
addHook("MobjDamage", SpawnPowerRing, MT_ATTRACT_BOX)
addHook("MobjDamage", SpawnPowerRing, MT_ATTRACT_GOLDBOX)
addHook("MobjDamage", SpawnPowerRing, MT_THUNDERCOIN_BOX)
addHook("MobjDamage", SpawnPowerRing, MT_THUNDERCOIN_GOLDBOX) 


--Global MobjDamage Hook. No global MobjDeath hook yet.
--Super Saiyans have a 2x multiplier. Huh, that doesn't sound as epic as 50x. Gameplay balance is lame.

addHook("MobjDamage", function(s, i, so, dmg, dmgtype)
	if not (so and so.player) return end 
	if (gametype==GT_COOP) and (s.flags & (MF_ENEMY|MF_BOSS)) and ((s.health or 0) <= 1) and s.X_SpilledBlood != true
		s.X_SpilledBlood = true 
		XS_KILLEDFOES = $+1
	end	
	if not IsXSonic(so) or not so.player.xsonictable return end --X Sonic sources only.	
	
	local p = so.player local xs = p.xsonictable local yu = xs
	
	local SUPER = XS_Super(p)
	
	if (s.flags & (MF_ENEMY|MF_BOSS|MF_MONITOR)) 
	or (s.flags & MF_SHOOTABLE) and (s.flags & MF_PAIN)
	and not (s.flags & (MF_NOCLIP|MF_NOCLIPTHING|MF_NOBLOCKMAP|MF_FIRE|MF_SPECIAL|MF_MISSILE))
	or (s.type == MT_EGGGUARD)
		if p.xsringburst
			if (p.scoreadd < 2)
				p.scoreadd = 2
			end
			p.xsringburst.DidKill = 31
			if xs.quickdrain
				xs.quickdrain = (not xs.darkphase) and max($-8,0) or 0
			end
		elseif (dmgtype == DMG_NUKE)
			if ((p.scoreadd or 0) > 3)
				p.scoreadd = min($, 4)
				so.XS_tempcombo = nil
			end
		end
		if so.XS_tempcombo
			if (p.scoreadd < so.XS_tempcombo.combo) and (p.scoreadd < 5)
				so.XS_tempcombo.combo = max((p.xsringburst) and 2 or 0, $)
				p.scoreadd = so.XS_tempcombo.combo --Retain score combo always!
			end
		elseif xs.zipdash and (xs.zipdash > 5) and not xs.zipdashslide and not (p.pflags & (PF_STARTJUMP|PF_SPINNING))
		or xs.snapdive and s.momz
		or xs.snapdivetrick and (xs.snapdivetrick < 13)
		or ((s.state >= S_PLAY_XKICK1) and (s.state <= S_PLAY_XKICK4))
		or p.xsringburst
			so.XS_tempcombo = {timer = 3, combo = 1}
		end
		if xs.darkphase
			local RB = so.XS_DarkTable
			if RB 
				RB.noviolence = 0
				if (s.XS_NoMoreRings!=true) and RB.maxrings and (p.rings < RB.maxrings) and (s.flags & (MF_ENEMY|MF_BOSS))
					P_GivePlayerRings(p, 1)
				end
			end
			if xs.darkphase != 3 and (s.XS_NoMoreRings!=true)
				local DARKNESS = (s.flags & MF_BOSS) and 9<<16 or (s.flags & MF_ENEMY) and 2<<16 or 1<<16
				if p.xsringburst
					DARKNESS = $+(4<<16)
				end
				XSon.AddDarkness(so, DARKNESS)
			end
			s.XS_NoMoreRings = true --Only once.
		end
		if i
			if (i.KillType == "SonicBoom") --The Snap Dive object is the hitbox for the Sonic Boom!
				if not (s.flags & MF_MONITOR) and (s.health > 1) and not s.gotXSassaulted
					s.health = max($-2, 1)
					if s.facc and s.facc.health s.facc.health = max($-2,1) end
				end
				XSon.gibs(s, i, nil, 70<<16, (SUPER == 3) and true or (SUPER == 2) and 3 or nil)
				local SFX = P_SpawnMobjFromMobj(s, -so.momx*2,-s.momy*2,0, MT_XSZIPDASHTRAIL)
				SFX.flags2 = MF2_DONTDRAW SFX.tics = 99 SFX.fuse = 99
				P_InstaThrust(SFX, R_PointToAngle2(s.x, s.y, so.x+so.momx, so.y+so.momy), 90*s.scale) --Travel towards source.
				if not xs.SuperDMG
					S_StartSoundAtVolume(SFX, sfx_xssfxp, 180)
					S_StartSoundAtVolume(SFX, (leveltime%2) and sfx_xssfxr or sfx_xssfxt, 180)
				else
					S_StartSoundAtVolume(SFX, sfx_xssfxp, 40)
				end
				XSon.Quake(so, 80<<16, 12, 1250<<16)
				XSon.SuperSonicWind(s, (not xs.darkphase) and SKINCOLOR_XSSUPERAURA or nil, 41, xs.SuperDMG and FF_HORIZONTALFLIP or FF_TRANS10, FRACUNIT+P_RandomRange(-9999,14000))
				S_StartSoundAtVolume(SFX, sfx_xssfx5, 90)
				S_StartSoundAtVolume(SFX, sfx_xssfx6, 100)
				return --
			elseif (i.type == MT_XSSANDBAG)
				i = so 	
			end
		end
		if (so != i) or (s.flags & MF_MONITOR) return end --
	
		--Got a sweetspot impact?
		if xs.zipdash or (SUPER >= 2) or p.xsringburst
		or (so.state >= S_PLAY_XKICK1) and (so.state <= S_PLAY_XKICK4)
			local SFX = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSZIPDASHTRAIL)
			SFX.flags2 = MF2_DONTDRAW SFX.tics,SFX.fuse = 99,99	
			if (s.health > 1) and s.health != 1000 and not s.gotXSassaulted
				if SUPER == 3 or p.xsringburst --Deal double damage!
					if s.facc if s.facc.health s.facc.health = max($-2,1) end
					else s.health = max($-1, 1) end
				end
			end
			if xs.zipdash --Slow down as a penalty, spindashing into enemies!
				if (SUPER <= 1) --Super Sonic isn't slowed!
					xs.zipdashx,xs.zipdashy = s.x,s.y
					xs.zipdashpause = (SUPER) and 4 or 7
					xs.rollcd = 8
					xs.zipdashminus = xs.zipdashminus and max($,15) or 15
					RestoreZipDashCam(p, s, xs)
					xs.zipdash = min($,20)
				end
				if (dmgtype != DMG_NUKE) --Save my ears, pls
					local SFX = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSZIPDASHTRAIL)
					SFX.flags2 = MF2_DONTDRAW SFX.tics,SFX.fuse = 68,68
					if P_RandomRange(0,1)
						S_StartSoundAtVolume(SFX, sfx_xssfx5, 150)
					else
						S_StartSoundAtVolume(SFX, sfx_xssfx6, 195)
					end
				end		
			end	
			
			if (s.flags & MF_BOSS) or ((s.health > 1) and s.health != 1000) --Less invincibility frames!
				if (s.type != MT_CYBRAKDEMON)
					local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_XSTRACKER2)
					if ghs and ghs.valid
						ghs.target = s
						ghs.FretType = 70
					end
				end
				XSon.gibs(s, i, 3, 6*s.scale, false, true)
				S_StartSoundAtVolume(SFX, sfx_kc40, 112)
			else
				S_StartSoundAtVolume(SFX, sfx_s3kb4, 160)
			end	
			
			if not xs.SuperDMG
				local ghs = P_SpawnMobjFromMobj(s, 0,0,0, MT_BOSSEXPLODE)
				if ghs and ghs.valid
					ghs.scale = $*2
					ghs.destscale = 99<<16
					ghs.flags2 = $|MF2_SHADOW
					ghs.blendmode = AST_ADD
					ghs.renderflags = RF_FULLBRIGHT|RF_NOCOLORMAPS	
				end
			end
			if (s.health <= 1) or s.health == 1000
				if (so.state >= S_PLAY_XKICK1) and (so.state <= S_PLAY_XKICK4)
					XSon.gibs(s, i, nil, 70<<16, (SUPER == 3) and true or (SUPER == 2) and 3 or nil)
					if (SUPER >= 2)
						if not xs.SuperDMG
							S_StartSoundAtVolume(SFX, sfx_xssfxp, 180)
							S_StartSoundAtVolume(SFX, (leveltime%2) and sfx_xssfxr or sfx_xssfxt, 180)
						else
							S_StartSoundAtVolume(SFX, sfx_xssfxp, 40)
						end
						XSon.Quake(i, 75<<16, 12, 1250<<16)
						if (so.state >= S_PLAY_XKICK1) and (so.state <= S_PLAY_XKICK4)	
							XSon.SuperSonicWind(s, (not xs.darkphase) and SKINCOLOR_XSSUPERAURA or nil, 41, xs.SuperDMG and FF_HORIZONTALFLIP or FF_TRANS10, FRACUNIT+P_RandomRange(-9999,14000))
						end
					else
						S_StartSoundAtVolume(SFX, sfx_xssfx5, 90)
						S_StartSoundAtVolume(SFX, sfx_xssfx6, 100)
						XSon.Quake(i, 60<<16, 6, 700<<16)
					end
				else		
					local QUAKEPOWER = max(80<<16, (p.speed/2)+abs(i.momz) )
					if SUPER
						QUAKEPOWER = max($, 24<<16+(SUPER*8<<16))
					end
					local QUAKETIME = (QUAKEPOWER>37<<16) and max(11, FixedInt(QUAKEPOWER/10)) or 3
					XSon.Quake(i, QUAKEPOWER, QUAKETIME, QUAKEPOWER*24)
					
					if (SUPER >= 2) or p.xsringburst
					
						local EXPLOSION = (p.xsringburst) and 99 or 1
						if not p.xsringburst and (p.speed > 10<<16)
							EXPLOSION = $+FixedInt(p.speed/10)
						end
						if not xs.SuperDMG
							local RVolume = min(255, 80+(EXPLOSION*10))
							S_StartSoundAtVolume(SFX, sfx_xssfxp, RVolume)
							S_StartSoundAtVolume(SFX, (leveltime%2) and sfx_xssfxr or sfx_xssfxt, RVolume)
						end
						
						if (SUPER >= 2) and (EXPLOSION > 5) EXPLOSION = true end
						
						XSon.gibs(s, i, nil, max(25<<16, p.speed+abs(i.momz)), EXPLOSION) --
						XSon.Quake(i, (xs.zipdash) and QUAKEPOWER*2/3 or QUAKEPOWER, QUAKETIME, QUAKEPOWER*25)
					else
						XSon.gibs(s, i)
						XSon.Quake(i, (xs.zipdash) and QUAKEPOWER/2 or QUAKEPOWER, (xs.zipdash) and max(2, QUAKETIME-3) or QUAKETIME, QUAKEPOWER*19)
					end
				end
				if (SUPER >= 2) and not xs.SuperDMG
					if not (xs.snapdive  --Not when snapdiving!(But kicks are fine)
					or xs.snapdivetrick and (xs.snapdivetrick < 10)
					and not ((so.state >= S_PLAY_XKICK1) and (so.state <= S_PLAY_XKICK4)) ) 
					
						s.XSSuperRadius = ((so.state >= S_PLAY_XKICK1) and (so.state <= S_PLAY_XKICK4)) and 480<<16 or
						min(260<<16, p.speed+12<<16)
						
						XSon.FlickerLight(i, (xs.darkphase) and SKINCOLOR_CRIMSON or nil, 3, (leveltime%2) and ANGLE_90 or 0)
						
						if SUPER == 3 --Extra SUPER effects!
							XSon.ExpandingGlow(i, (xs.darkphase) and SKINCOLOR_CRIMSON or SKINCOLOR_SUPERGOLD4, 10, 97, true, AST_ADD, 47356)
							if not p.xsringburst
								local ghs = XSon.Overglow(i, 2, (xs.darkphase) and SKINCOLOR_COBALT or SKINCOLOR_SUPERSILVER1)
								if ghs and ghs.valid
									ghs.frame = ($ & ~FF_TRANSMASK)|FF_TRANS10
									ghs.fuse,ghs.tics = 9,99
									ghs.sttfade = true
									ghs.tracer = i
								end
								if not xs.zipdash
									XSon.sparkles(p,i,xs, 90, 6, 5, 5<<16, (xs.darkphase) and SKINCOLOR_CRIMSON or SKINCOLOR_WHITE, AST_ADD)
								end		
							end
						end
						if not xs.SuperDMG
							xs.SuperDMG = 1
						end
					end
				end
				if p.xsringburst and SUPER != 3
					XSon.ExpandingGlow(i, SKINCOLOR_SUPERSKY4, 12, 97, true, AST_ADD, 47500)
					XSon.FlickerLight(i, nil, nil, (leveltime%2) and ANGLE_90 or 0)
					s.XSSuperRadius = min(360<<16, (p.speed*2)+20<<16)
				end
			else --Shove tough enemies / get shoved
				if xs.zipdash and not SUPER --get shoved.
					XSZipDashCancel(p, i, xs)
					local TANG = R_PointToAngle2(i.x-i.momx, i.y-i.momy, s.x, s.y)
					xs.forceangle = {angle = TANG, timer = 14, nosangle = true}
					p.drawangle = TANG					
					P_SetObjectMomZ(i, -6<<16, true)	
					s.state = S_PLAY_SKID
					p.skidtime = 15					
					P_InstaThrust(i, TANG, -48*i.scale)
					S_StartSound(i, sfx_dmpain)
					local ghs = P_SpawnMobjFromMobj(i, 0,0,0, MT_XSTRACKER2)
					if ghs and ghs.valid
						ghs.target = i
						ghs.SteamTrail = 1	
					end
				end				
				if (s.flags & (MF_ENEMY|MF_BOSS)) 
				and not (s.flags & (MF_MISSILE|MF_NOCLIP))
				and not s.GUNally and (s.info.speed != 0)
				and not ((s.type >= MT_EGGMOBILE2) and (s.type <= MT_EGGROBO1JET))
					XSon.ShoveFoe(p,i,yu,s) --i and s are reversed. XSon.ShoveFoe is in XS-GIBS. The tracker itself is in XS-OBJECTS.
				end
			end	
		end
  	end
end)

--Bounce further off the Egg Colloseum to help negate instakill jank.
addHook("MobjDamage", function(s, i, so)
	if not (so and IsXSonic(so) and so.player and so.player.xsonictable) or not (s.valid and (s.health > 2)) return end --
	local p = so.player local xs = p.xsonictable local yu = xs
	if xs.snapdive
		for i = 1,2
			P_Thrust(so, R_PointToAngle2(so.x, so.y, s.x, s.y), -132<<16)
		end
		p.powers[pw_nocontrol] = max($,5)
	end
end, MT_EGGMOBILE4)

addHook("MobjDeath", function(s, i, so)
	if not (so and so.valid and IsXSonic(so) and so.player and so.player.xsonictable) return end --
	local p = so.player local xs = p.xsonictable
	if xs.snapdive or (xs.snapdivetrick and xs.snapdivetrick < 11) or (XS_Super(p) == 3)
		P_SetObjectMomZ(s, 12<<16, false)
		if (so.state >= S_PLAY_XKICK2) and (so.state <= S_PLAY_XKICK4)
			P_Thrust(s, so.angle, 36<<16)
		elseif xs.snapdive or (xs.snapdivetrick and xs.snapdivetrick < 11)
			P_Thrust(s, R_PointToAngle2(so.x, so.y, so.x+so.momx, so.y+so.momy), -25<<16)
		else
			P_Thrust(s, R_PointToAngle2(so.x, so.y, so.x+so.momx, so.y+so.momy), 35<<16)
		end
		if not (s.state == S_TNTBARREL_FLYING)	
			S_StartSound(s, sfx_bowl)
			s.state = S_TNTBARREL_FLYING
		end
		s.health = 1
		s.tntdelayexpX = 5
		s.flags = MF_NOBLOCKMAP return true --
	end
end, MT_TNTBARREL)

//I refuse. Super Sonic will NOT die to some stupid minecart crash!
addHook("MobjDeath", function(s, i, so)
	if not (s.target and IsXSonic(s.target) and s.target.player) return end --
	local t = s.target local p = t.player
	if (XS_Super(p) >= 2)
		p.powers[pw_carry] = 0
		t.momx,t.momy = s.momx,s.momy
		P_SetObjectMomZ(t, 6<<16, true)
		s.target = nil
	end
end, MT_MINECART)

--Don't get hurt by X Sonic! 656 is what SRB2 tells me is sometimes the true MT_ROSY, sooo...risking it.
addHook("MobjDamage", function(s, i, so) if so and IsXSonic(so) return true end end, MT_ROSY)
addHook("MobjDamage", function(s, i, so) if so and IsXSonic(so) return true end end, 656)

addHook("MobjDeath", function(s, i, so)
	if so and IsXSonic(so) and s.valid
		A_BossDeath(s,0,0) --Don't you dare skip this part!
	end
end, MT_METALSONIC_BATTLE)
addHook("MobjDamage", function(s, i, so)
	if so and IsXSonic(so) and s.valid and (s.health < 4) and so.player
		local SUPER = (XS_Super(so.player)) or 0
		if (SUPER >= 2)
			P_KillMobj(s, so, so) return true --
		end
	end
end, MT_METALSONIC_BATTLE)

addHook("MobjThinker", function(s)
	if s.valid and P_CheckDeathPitCollide(s)
		if (s.z >= s.floorz)
		or (s.z+s.height >= s.ceilingz)
			s.z = $+s.scale*P_MobjFlip(s)
			if s.momz 
				s.momz = -$
			end return true --
		end
	end
end,MT_XSSANDBAG)

addHook("NetVars", function(network) --Why am I even bothering, it's not like it matters if sounds are synced.
	SeasonFour = network($)
end)