--#============================================================================================#
--Every function related to GIBS. Usually called from MobjDamage Hooks, sometimes collission.
--The MobjThinker is also here, not in XS-OBJECTs.
--#============================================================================================#
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

--Fling the object as it is! Note that it kills and deletes the original, the flung object is purely visual!
--s is the object about to be flung, v is the player.
XSon.FlingObject = function(s, v, DONTDELETE, MOMSHIFT)
	if not (s and s.valid and v and v.valid) return end -- and (v.type != MT_DRAGONBOMBER)
	local PREFANGLE = (v.momx or v.momy) and R_PointToAngle2(v.x, v.y, v.x+v.momx, v.y+v.momy) or v.angle	
	local ghs = P_SpawnMobj(s.x+v.momx, s.y+v.momy, s.z+v.momz+((s.height/3)*P_MobjFlip(s)), MT_XSFLUNG)
	if ghs and ghs.valid
		ghs.flags = MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING|MF_SOLID|MF_NOBLOCKMAP
		ghs.scale = s.scale
		ghs.spritexscale = s.spritexscale
		ghs.spriteyscale = s.spriteyscale
		ghs.angle = s.angle+(XSon.RNG(120,-120,ghs)*ANG1)
		ghs.sprite = s.sprite
		ghs.frame = s.frame
		if not (XS_CRAPPYPRIORITIES==1)
			ghs.rollangle = s.rollangle
		end
		ghs.mirrored = s.mirrored
		ghs.shadowscale = s.shadowscale
		P_Thrust(ghs, PREFANGLE, 20*v.scale)
		P_SetObjectMomZ(ghs, 8*v.scale, true)
		ghs.momx = $+v.momx
		ghs.momy = $+v.momy
		ghs.momz = $+v.momz
		if MOMSHIFT --Speed up or lessen the momentum?
			ghs.momx = FixedDiv(FixedMul($,10),MOMSHIFT)
			ghs.momy = FixedDiv(FixedMul($,10),MOMSHIFT)
		end
		
		--Wood debris?
		if (s.type == MT_SALOONDOOR) or (s.type == MT_SALOONDOORCENTER) or (s.type == MT_ARIDSIGN_CAUTION) or (s.type == MT_ARIDSIGN_CACTI)
			S_StartSound(ghs, sfx_wbreak)
			ghs.WOODDEBRIS = true
			ghs.scale = $*4/3
			for d = 0,12
				local debris = P_SpawnMobj(s.x+(P_RandomRange(-50,50)*v.scale), s.y+(P_RandomRange(-50,50)*v.scale), s.z+(XSon.RNG(2+(d*3),80+d, v)*v.scale), MT_WOODDEBRIS)
				if debris and debris.valid
					debris.flags = $|MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING
					P_InstaThrust(debris, s.angle+(P_RandomRange(-20,20)*ANG1), (abs(v.momx)+abs(v.momy))+8*s.scale)
					P_SetObjectMomZ(debris, P_RandomRange(-4,8)*s.scale, false)
				end
			end			
		else --Metal debris!
			if (ghs.renderflags & RF_PAPERSPRITE) or (ghs.frame & FF_PAPERSPRITE)
				ghs.angle = $+ANG60 --Adjust a bit for paper.
			end
			ghs.METALDEBRIS = true
		end
		local DEBRISLVL = XSon.DebrisLVL(s, 5)
		if (DEBRISLVL < 300)
			ghs.FLAMEDEBRIS = true
			ghs.SMOKINGDEBRIS = 1
			ghs.fuse = 78
		else
			ghs.fuse = 50
		end
		ghs.radius = s.radius
		ghs.height = s.height
	end
	if (s.flags & (MF_ENEMY|MF_BOSS)) or (s.type == MT_EGGROBO1)
		XS_CircleWave(v, 1, SKINCOLOR_RED, 12, FRACUNIT-3500, PREFANGLE+ANGLE_90)
		local ths = XS_CircleWave(v, 1, SKINCOLOR_FLAME, 12, FRACUNIT-30000, PREFANGLE+ANGLE_90)
		P_Thrust(ths, PREFANGLE, -6*s.scale)
	end
	
	local QUAKEPOWER = max(min(80<<16, abs(s.momx)+abs(s.momy)), 30<<16)
	XSon.Quake(v, QUAKEPOWER, 4, QUAKEPOWER*13)	
	
	if not DONTDELETE
		local ths = P_SpawnGhostMobj(s)
		ths.fuse = 7 ths.tics = 9
		ths.destscale = $*99	
		s.flags2 = $|MF2_DONTDRAW
		if (s.flags & (MF_SHOOTABLE|MF_ENEMY|MF_BOSS|MF_MONITOR))
			P_KillMobj(s, v, v)
			if s and s.valid
				s.state = S_NULL
			end
		end
	end	
	return ghs --
end

--Debris" flinging everywhere when you kill a mech. Or gibs" as is the popular name with SRB2. 
--i is the inflictor, but is not required!
XSon.gibs = function(s, i, NUM, SPEED, EXPLOSION, NOSPECIALGIB, SPAWNMO, ROBOT, ANG)
	
	local DEBRISLVL = XSon.DebrisLVL(s, 0)
	if (DEBRISLVL > 300) XS_RingBurstStripes(i or s, SKINCOLOR_SILVER, AST_ADD) return end	--Too much debris present, ABORT!
	if not SPEED
		SPEED = max(11<<16, ((abs(i.momx)+abs(i.momy) or 9)*2/3)	) +abs(i.momz)
	end
	
	if (DEBRISLVL > 200) --We're under alot of strain. Reduce gibs to save performance?
		NUM = (DEBRISLVL > 264) and 1 or 3
		XSon.DebrisLVL(s, NUM*3)
	else
		local MIN = (s.radius < 20<<16) and 2 or (s.radius < 30<<16) and 3 or (s.radius < 40<<16) and 4 or 5
		NUM = (NUM) or max(MIN, min(MIN*2, FixedInt(SPEED/14)) )
		XSon.DebrisLVL(s, NUM*3)
	end
	
	i = (i) or s
	local RAD = (s.radius>FRACUNIT) and FixedInt(s.radius) or 20
	local HEIGHT = (s.height>FRACUNIT) and FixedInt(s.height) or 16
	local SPECIALgib,SPECIALgib2,COLOR --If we spawn a special GIB, remember that!
	local S, type = s, s.type
	if SPAWNMO and SPAWNMO.target and SPAWNMO.target.valid --and (SPAWNMO.type == MT_XSSANDBAG)
		S = SPAWNMO
		type = SPAWNMO.target.type
		if ROBOT == nil
			ROBOT = XSon.EnemyIsRobot(SPAWNMO.target)
		end
		COLOR = (ROBOT) and XSon.enemycolor(SPAWNMO.target, true) or SKINCOLOR_GREY
	else
	/*	XSon.WindExplosion(i or s, nil, AST_ADD)
		local ghs = XSon.WindBlast(s)
		if ghs and ghs.valid
			ghs.spriteyoffset = 16<<16
			ghs.fuse = 8
			ghs.momx = $+i.momx/2
			ghs.momy = $+i.momy/2
			ghs.frame = $+FF_TRANS20
			ghs.sttfade = true
		end	*/	
		XS_RingBurstStripes(i or s, SKINCOLOR_SILVER, AST_ADD)
		if ROBOT == nil
			ROBOT = XSon.EnemyIsRobot(s)
		end
		COLOR = (ROBOT) and XSon.enemycolor(s) or SKINCOLOR_GREY
	end
	if ANG == nil
		ANG = (i==s) and s.angle+ANGLE_180 or i.angle
	end
	
	for d = 1,NUM --Fling debris when killed.
		local ghs = P_SpawnMobj(
		S.x+(i.momx*2)+(XSon.RNG(-RAD-(d*3),RAD+(d*3), i)*s.scale), 
		S.y+(i.momy*2)+(XSon.RNG(-RAD-(d*3),RAD+(d*3), s)*s.scale), 
		S.z+i.momz+(XSon.RNG(d,HEIGHT+(d*2), i)*s.scale),
		MT_XSFLUNG)
		
		if ghs and ghs.valid
			ghs.flags = MF_BOUNCE|MF_GRENADEBOUNCE|MF_NOCLIPTHING|MF_SOLID|MF_NOBLOCKMAP
			ghs.angle = ANG + (P_RandomRange(80+(d*7),-80-(d*7))*ANG1)
			ghs.scale = s.scale
			ghs.height = (HEIGHT<<16)/10 
			ghs.radius = (RAD<<16)/8
			P_SetObjectMomZ(ghs, (XSon.RNG(-d,d*2,ghs)+(SPEED/(10<<16)) )*i.scale, true)
			ghs.WAITROLL = 6 -- Wait a couple tics before using rollangle.

			local BOOST = (SPEED > 10<<16) and XSon.RNG(10,13,i)*i.scale or XSon.RNG(15,36,i)*i.scale
			P_InstaThrust(ghs, ghs.angle, BOOST)
			ghs.momx = $+(i.momx*2/3)
			ghs.momy = $+(i.momy*2/3)
			ghs.METALDEBRIS = (not (ROBOT)) and true or false
			
			if (DEBRISLVL > 200) --Simpler debris to save performance.
				ghs.fuse = 48
			else
				if not (XS_CRAPPYPRIORITIES==1)
					ghs.rollangle = ANG1*XSon.RNG(0,359,ghs)
				end
				ghs.fuse = 74
				ghs.shadowscale = (ghs.radius < 15<<16) and 4<<16 or 98000
			end
			if (d == 1) and (s.flags & MF_ENEMY) and not (s.flags & MF_BOSS)
				ghs.DEBRISTARGET = s --Silence them later.
			end
			local DIDSPRITE = false
			if not NOSPECIALGIB and ROBOT --Try a special gib?
				if d==1
				or d==2 and ((type==MT_CRUSHSTACEAN) or (type==MT_GSNAPPER) or (type==MT_BANPYURA) or (type==MT_GOLDBUZZ) or (type==MT_REDBUZZ))
			
					ghs.SMOKINGDEBRIS = 1
					ghs.spriteyscale = FRACUNIT+26000
					ghs.mirrored = (d==2) and true or false
				
					local SPRITE1,FRAME1 = XSon.SpecialGib(s)
					if SPRITE1 != nil and FRAME1 != nil --Special gib!
						ghs.sprite = SPRITE1
						ghs.frame = FRAME1
						ghs.SPECIALDEBRIS = true
						DIDSPRITE = true
						ghs.renderflags = $|RF_SEMIBRIGHT
						if d == 2
							if (type == MT_BANPYURA)
								ghs.color = SKINCOLOR_AZURE
								ghs.colorized = true
							end
							SPECIALgib2 = ghs
						else
							if FRAME1 == H and SPRITE1 == SPR_XSG3
								ghs.color = (type == MT_BLUECRAWLA) and SKINCOLOR_CORNFLOWER or SKINCOLOR_RED
							end
							SPECIALgib = ghs
						end
					end
				end
			end
			if DIDSPRITE != true
				if ROBOT --MOST enemies are robots or have metallic armor, but...
					ghs.sprite = SPR_XSG3
					ghs.frame = XSon.RNG(C, G, ghs)
					if (ghs.frame <= D)
						ghs.color = COLOR
					end
				else --For (probably!) organic enemies!
					ghs.sprite = SPR_BOM1 --I COULD do blood, but in a Sonic X context, blood flying everywhere is a vibebreaker.
					ghs.frame = XSon.RNG(B, F, ghs)|FF_TRANS20 --So...silly censorship is-a-go? Damnit, this is stupid too. 
					ghs.destscale = 99<<16
					ghs.fuse = 19
					ghs.shadowscale = 0
					ghs.NOTROBOT = true
				end
				ghs.spriteyscale = XSon.RNG(FRACUNIT+(d*2366),2<<16,ghs)
				if d == 1
					ghs.SMOKINGDEBRIS = 1
				end
			end
			ghs.spritexscale = ghs.spriteyscale
			ghs.debrisLVL = (ghs.SPECIALDEBRIS) and 1 or DEBRISLVL

			if EXPLOSION
				if EXPLOSION == true or (EXPLOSION >= d)
					if not (DEBRISLVL > 220)
						XSon.FireBall(ghs)
						ghs.WAITROLL = $+6
						ghs.FLAMEDEBRIS = true
						ghs.SMOKINGDEBRIS = 1
						ghs.renderflags = $|RF_FULLBRIGHT
						if (d <= 2) and (EXPLOSION == true)
							ghs.ELECTRICDEBRIS = true
						end
						if d == 1
							XSon.DebrisLVL(s, (EXPLOSION == true) and 6 or min(5,EXPLOSION))
						end
					end
				end
				if d != 1
					local ADDSCALE = XSon.RNG(5000,FRACUNIT, ghs)
					ghs.spriteyscale = $+ADDSCALE
					ghs.spritexscale = $+ADDSCALE
				end
			end
		end
	end
	return SPECIALgib,SPECIALgib2 --
end

--Retrieve current gore(robot gibs) level, and ADD any amount to it.
XSon.DebrisLVL = function(s, ADD)
	for z in players.iterate --Look for the first potential player, then use them.
		if z.valid 
			if (z.XSgibsLVL == nil) --Let's NOT share the same one as SA-Knuckles, or the countdown will get messed up.
				z.XSgibsLVL = (ADD) or 1
			elseif ADD
				z.XSgibsLVL = max($+ADD, 1)
			end 
			return z.XSgibsLVL or 1 --You're done! Anything over 200 is usually ALOT of gibs...
		end
	end return 300 --No players returns 300. No gore.
end

--Is the enemy a robot? "s" is assumed to be valid.
local XROBOTDSTATE = {S_XPLD1, S_POP1, S_SONIC3KBOSSEXPLOSION1,S_BOSSEXPLODE,S_XPLD_FLICKY, S_XPLD_EGGTRAP,
S_TNTDUST_1,S_FBOMB_EXPL1,S_SPINDUST1,S_DUST1}
XSon.EnemyIsRobot = function(s)
	if (s.type == MT_FANG) or s.GUNally return end --GUN robots use their own gib system!
	--Are they part of the vanilla SRB2 non-bonus enemy list?
	if (s.type >= MT_BLUECRAWLA) and (s.type <= MT_METALSONIC_BATTLE) return true end --
	if s.info.deathstate
		for i = 1,#XROBOTDSTATE --Do they use a deathstate only a robot would have?
			if s.info.deathstate == XROBOTDSTATE[i] return true end --
		end
	end
end

--What color is the foe? Old stolen code from SMSreborn.
XSon.enemycolor = function(s, FORCE)
	if not (s.flags & (MF_ENEMY|MF_BOSS)) and not FORCE return s.color or SKINCOLOR_GREY end
	local COLOR = s.color or SKINCOLOR_GREY
	if (s.type == MT_BLUECRAWLA)
	or (s.type == MT_SPRINGSHELL)
	or (s.type == MT_YELLOWSHELL)
	or (s.type == MT_PENGUINATOR)
	
		COLOR = SKINCOLOR_CORNFLOWER
		
	elseif (s.type == MT_GFZFISH)
	or (s.type == MT_CRAWLACOMMANDER)
	or (s.type == MT_METALSONIC_BATTLE)
	
		COLOR = SKINCOLOR_COBALT
		
	elseif (s.type == MT_GSNAPPER)
	or (s.type == MT_JETTBOMBER)
	or (s.type == MT_ROBOHOOD)
	
		COLOR = SKINCOLOR_GREEN
		
	elseif (s.type == MT_REDCRAWLA)
	or (s.type == MT_REDBUZZ)
	or (s.type == MT_DETON)
	or (s.type == MT_VULTURE)
	or (s.type == MT_CRUSHSTACEAN)
	
		COLOR = SKINCOLOR_RED
		
	elseif (s.type == MT_EGGGUARD)
	or (s.type == MT_BANPYURA)
	or (s.type == MT_PTERABYTE)
	
		COLOR = SKINCOLOR_LAVENDER
		
	elseif (s.type == MT_FACESTABBER)
	or (s.type == MT_JETJAW)
	
		COLOR = SKINCOLOR_CRIMSON
		
	elseif (s.type == MT_DRAGONBOMBER)
	or (s.type == MT_PYREFLY)
	or (s.type == MT_CANARIVORE)
	or (s.type == MT_SNAILER)
	or (s.type == MT_GOLDBUZZ)
	or (s.type == MT_HIVEELEMENTAL)
	or (s.type == MT_BUMBLEBORE)
	
		COLOR = SKINCOLOR_GOLDENROD
		
	elseif (s.type == MT_JETTGUNNER)
	or (s.type == MT_CYBRAKDEMON)
	
		COLOR = SKINCOLOR_BROWN
		
	elseif (s.type == MT_UNIDUS)
	or (s.type == MT_POPUPTURRET)
	or (s.type == MT_MINUS)
		COLOR = SKINCOLOR_CARBON
	end
	return COLOR
end

--Do we have a special gib for this enemy? Returns sprite and frame to use.
XSon.SpecialGib = function(s)
	if not (s.flags & MF_ENEMY) return end --
	
	if (s.type == MT_BLUECRAWLA) or (s.type == MT_REDCRAWLA)
		return SPR_XSG3,H --custom
	elseif (s.type == MT_SPRINGSHELL) or (s.type == MT_YELLOWSHELL)
	or (s.type == MT_CRAWLACOMMANDER) or (s.type == MT_UNIDUS)
		return SPR_BRKN,E
	elseif (s.type == MT_GOLDBUZZ)
		return SPR_BRKN,I	
	elseif (s.type == MT_REDBUZZ)
		return SPR_XSG3,I --custom
	elseif (s.type == MT_GSNAPPER)
		return BRKN,U
	elseif (s.type == MT_FACESTABBER)	
		return BRKN,Q	
	elseif (s.type == MT_CRUSHSTACEAN) or (s.type == MT_BANPYURA)
		return BRKN,N
	end
end

--Creates an invisible enemy to instantly destroy. 
XSon.ForceDebris = function(s, MOBJTYPE)
	local MOBJTYPE = (MOBJ) or MT_SKIM
	local ghs = P_SpawnMobjFromMobj(s, s.momx*2, s.momy*2, 0, MOBJTYPE)
	if ghs and ghs.valid
		ghs.health = 1
		ghs.flags2 = $|MF2_DONTDRAW
		ghs.fuse = 2 ghs.tics = 2
		P_DamageMobj(ghs, s, s)
		if ghs and ghs.valid
			ghs.state = S_NULL
		end
	end
end

--Take knockback?
XSon.SonicKnockBack = function(p,s,xs, TANG, KB, KBtime)
	if not s.health return end --
	
	TANG = (TANG) or s.angle
	KB = (KB) or -36*s.scale
	KBtime = (KBtime) or 10
	
	P_InstaThrust(s, TANG, KB) 			
	xs.forceangle = {angle = TANG, timer = KBtime, nosangle = true, punchtoo = true}
--	p.YUKcrouch = {timer = 0, maxtime = 16, anim = A, cantjump = 7, cantspin = 33, dustKB = 8, nomove = 11, editdirect = true}
	xs.nosarun4 = 6
	xs.airborne = 0	
	xs.noubercontrol = 6
	xs.jump = 99<<16
	xs.spin = 99<<16
	s.spritexscale = FRACUNIT+600
	s.spriteyscale = FRACUNIT+1200
	s.spriteyoffset = 9<<16
	s.XSstretch = {timer = 6, Xspeed = 140, Yspeed = 200}	
				
	p.cmd.forwardmove,p.cmd.sidemove,xs.direct = 0,0,0
	p.powers[pw_nocontrol] = max($,2)
end

--Shove an enemy! s is the one being shoved.
XSon.ShoveFoe = function(p,s,xs,i,SPEED, SMOKE, TOSS)
	SPEED = (SPEED) or min(35*i.scale, p.speed+abs(s.momz/4))
	SPEED = min(85*s.scale, FixedDiv(FixedMul($, s.scale), i.scale*5/3))
						
	local ghs = P_SpawnMobjFromMobj(i,0,0,0, MT_XSTRACKER2)
	if ghs and ghs.valid
		ghs.XYshoveANG = R_PointToAngle2(s.x-s.momx, s.y-s.momy, i.x, i.y)
		ghs.XYshove = (SPEED*2)
		ghs.target = i
		ghs.tracer = s
		if SMOKE
			ghs.SteamTrail = 1
		end
		if TOSS
			i.z = $+(s.scale*P_MobjFlip(i))
			i.momz = (max(6*s.scale, SPEED/7)*P_MobjFlip(s))
			ghs.RollType = TOSS
		end
	end
end

XSon.FlungThinker = function(s)
	if s.Init == nil --Setup section...
		s.Init = 0
		if (s.tics < 30) s.tics = 250 end
		if s.debrisLVL == nil s.debrisLVL = 0 end
		if s.flamespark
			if not s.XSrgtimer
				s.XSrgtimer = tonumber(s.flamespark) or 69
			end
		else
			if not s.shadowscale --Shadows can be intensive when there's this many, thread carefully.
				if not s.ROCK != 777
					s.shadowscale = 75000
				end
			end
			local rng = XSLocalRNG(200,280,s) --How long the debris sticks around.
			s.XSrgtimer = rng
		end
		
		--What direction are we going to rollangle into?
		if s.RollDirection == nil
			s.RollDirection = (XSon.RNG(3,4,s) == 4) and 1 or -1
			if not s.SPECIALDEBRIS
				s.mirrored = (s.RollDirection == 1) and true or $
			end
		end
		if s.DEBRISTARGET and s.DEBRISTARGET.valid and not s.DEBRISTARGET.health --Shut up their deathsound.
			S_StopSound(s.DEBRISTARGET)
		end
	end
	
	--Setup done!
	s.Init = $+1
	s.XSrgtimer = $-1
	
	if (s.XSrgtimer <= 1) s.state = S_NULL return end  --Disappear!
		
	--We're a flamespark! Use simple behaviour!
	if s.flamespark 
		if P_IsObjectOnGround(s) and (s.Init > 2) or (s.eflags & MFE_TOUCHWATER)
			s.state = S_NULL return --
		end
		s.spritexscale = max(9999, $-450) --Gently shrink overtime.
		s.spriteyscale = max(9999, $-450)
		if (s.momz*P_MobjFlip(s) < -5<<16)
			s.momz = -5*P_MobjFlip(s)<<16 --Maximum falling speed.
			if (s.floorz < s.z+(s.momz*2)+(60*s.scale))
				local trans = min((s.frame & FF_TRANSMASK)+FRACUNIT, FF_TRANS90)
				s.frame = ($ & ~FF_TRANSMASK)|trans
			end
		end
		if (s.Init > 10)
			if (s.momz*P_MobjFlip(s) > -5<<16) and (s.momz*P_MobjFlip(s) > -11<<16) --Fall quicker.
				local FALLSPEED = FixedDiv(FixedMul(7776, 32678), P_GetMobjGravity(s) or 32678)
				if (s.Init > 20) FALLSPEED = $*2 end
				s.momz = $+(FALLSPEED*P_MobjFlip(s)) --Fall quicker!
			end
		end return --Eh, flamesparks are simple enough.
	end
		
	s.trgmom = abs(s.momx)+abs(s.momy)  --Rough (not perfect) estimate of speed.
	
	--Special extra effects.
	if s.ELECTRICDEBRIS
		if (s.Init == 5 or s.Init == 24)
		and not (s.debrisLVL > 125)
			if s.ELECTRICDEBRIS == "dark"
				if s.Init == 24
					XSon.ElecticBody(s, 6, SKINCOLOR_CRIMSON, FF_HORIZONTALFLIP|FF_TRANS10, AST_TRANSLUCENT)
				else
					XSon.ElecticBody(s, 6, SKINCOLOR_JET, FF_TRANS10, AST_SUBTRACT)
				end
			else
				XSon.ElecticBody(s, nil, SKINCOLOR_GOLDENROD, FF_TRANS10, AST_ADD)
			end
		end
		if not s.fuse or (s.fuse > 11) and not (s.Init > 31)
			if (s.Init%3 ==0) 
				s.OldRender,s.OldBlend = s.renderflags,s.blendmode
				s.renderflags = ($ & ~FF_FULLBRIGHT|RF_SEMIBRIGHT)|RF_FULLDARK
				s.blendmode = AST_SUBTRACT
			elseif s.OldRender
				s.renderflags = s.OldRender
				s.blendmode = s.OldBlend
				s.OldBlend,s.OldRender = nil,nil
			end
		end
	end
	if s.FLAMEDEBRIS 
		if s.Init == 9 
		or s.Init == 17 and not s.SMOKINGDEBRIS and (XSon.RNG(10,12, s) == 10)
			if not (s.debrisLVL > 230)
				XSon.FireBall(s) --Occasional fire graphics!
			end
		end
	end
	if s.WAITROLL --Wait a sec before rolling.
		s.WAITROLL = $-1
	elseif s.TEMPROLL --Only roll for a certain amount of time, then start flickering.
		s.TEMPROLL = $-1
		if not s.TEMPROLL and s.BIGOBJECT and s.SMOKINGDEBRIS != nil
			s.SMOKINGDEBRIS = 8
		end
	end
			
	if s.SMOKINGDEBRIS --Create a smash-bros like launch-trail!
		s.SMOKINGDEBRIS = $+1
		if (s.SMOKINGDEBRIS > 7) and (s.SMOKINGDEBRIS < 17)  --Initial take-off smoketrail!
		and not (s.debrisLVL > 180)
			local ghs = P_SpawnMobjFromMobj(s,0,0,s.height/2, MT_THOK)
			ghs.state = S_SPINDUST1+XSLocalRNG(0,2,s)
			ghs.flags = $|MF_NOBLOCKMAP|MF_NOCLIPTHING
			ghs.renderflags = $|RF_SEMIBRIGHT
			ghs.color,ghs.colorized = (SKINCOLOR_SUPERGOLD5+5)-(s.SMOKINGDEBRIS*2), true
			ghs.fuse = 32
			ghs.frame = ($&~FF_TRANSMASK)|FF_TRANS60
			ghs.scale = (s.scale*s.SMOKINGDEBRIS)/6
			if not s.BIGOBJECT
				ghs.spritexscale = $/2
				ghs.spriteyscale = $/2
				ghs.scalespeed = FRACUNIT/18
			end
			ghs.destscale = 99<<16
			if (s.SMOKINGDEBRIS < 13)
				ghs.flags2 = $|MF2_SHADOW
			end
		end	
	end
	if not s.gentrgsfx --Only play landing sound once!
		if P_IsObjectOnGround(s)
			if P_CheckDeathPitCollide(s) --Nope.
			or (abs(s.floorz-s.ceilingz) < 8<<16) --Crushed!
				s.state = S_NULL return --
			elseif (s.Init > 3) and not s.gentrgsfx --Wait 2 tics first...
				s.gentrgsfx = true
				if (s.trgmom > FRACUNIT)
					s.momx = FixedDiv(FixedMul($,30),31)
					s.momy = FixedDiv(FixedMul($,30),31)
				end
				if s.ROCK				
					if (s.spriteyscale >= 3<<16) --Big rocks make impacts!
						local rng = XSLocalRNG(sfx_rocks1,sfx_rocks4,s)
						local rvolume = max(50, min(255, s.spritexscale/120))
						if not S_SoundPlaying(s, rng)
							S_StartSoundAtVolume(s, rng, rvolume) 
						end
					end
					P_Thrust(s, XSLocalRNG(0,359,s)*ANG1, XSLocalRNG(0,90,s)<<16/8)	
					P_SetObjectMomZ(s, XSLocalRNG(55,130,s)*(FRACUNIT/4), true) --Fake bounce a bit.	
				else
					local rvolume = max(50, min(255, s.spritexscale/250))
					P_Thrust(s, XSon.RNG(0,359,s)*ANG1, XSon.RNG(1,999,s)*(FRACUNIT/140))	
					P_SetObjectMomZ(s, XSon.RNG(55,120,s)*(FRACUNIT/33), true)
					if s.METALDEBRIS or s.WOODDEBRIS
						if (abs(s.momz) > 2<<16)
							local ghs = P_SpawnMobjFromMobj(s, s.momx,s.momy,0, MT_EXPLODE) --Big poof.
							ghs.color = SKINCOLOR_BEIGE
							ghs.scale = s.scale/2
							ghs.colorized = true
							ghs.dispoffset = -20
							ghs.spriteyscale = FRACUNIT/2
							ghs.scalespeed = $*2
							ghs.destscale = $*5								
						end	
						if s.WOODDEBRIS
							S_StartSoundAtVolume(s, sfx_wbreak, 140)
						elseif not s.METALDEBRIS and not S_SoundPlaying(s, sfx_xssfx6)
							S_StartSoundAtVolume(s, sfx_xssfx6, 60)
						end
					elseif s.NOTROBOT and (leveltime%4==0)
						if ((s.frame & FF_TRANSMASK) < FF_TRANS90)
							s.frame = $+FF_TRANS10
						end
					end
				end
				s.RollDirection = $*-1
			end
		end
	end
			
	--For papersprites, once you've touched the floor, turn into a floorsprite!
	if s.gentrgsfx
		if s.WOODDEBRIS or (s.renderflags & (RF_PAPERSPRITE|RF_FLOORSPRITE)) or (s.frame & FF_PAPERSPRITE)
			if not s.floorspriteslope and not s.NOSLOPE --fall flat onto the floor!	
				s.frame = $ & ~FF_PAPERSPRITE
				s.flags2 = $|MF2_SPLAT
				s.dispoffset = 70
				P_CreateFloorSpriteSlope(s)
				s.renderflags = $|RF_FLOORSPRITE|RF_SLOPESPLAT|RF_NOSPLATBILLBOARD  & ~RF_PAPERSPRITE
				s.floorspriteslope.o = {x = s.x, y = s.y, z = s.z}
				s.floorspriteslope.xydirection = R_PointToAngle2(s.x, s.y, s.x+s.momx, s.y+s.momy)
				s.floorspriteslope.zangle = FixedInt(s.momz)*(ANG1*4) or s.standingslope and s.standingslope.zdelta or 0
					
				s.momz = $/2 --Lose some Z momentum on landing.
			else
				if P_IsObjectOnGround(s)
					if (s.trgmom > 5<<16) and (leveltime%2)
						local ghs = P_SpawnMobjFromMobj(s,0,0,0, MT_EXPLODE) --Big poof.
						ghs.color,ghs.colorized = SKINCOLOR_BEIGE, true
						ghs.spriteyscale = FRACUNIT/((leveltime%5==0) and 3 or 5)
						ghs.destscale = $*5
					end		
					if not s.NOSLOPE
						s.floorspriteslope.o = {x = s.x, y = s.y, z = s.z+(s.scale*2)}
						if s.standingslope
							if (abs(s.standingslope.zdelta) > 2500)
								local ANGLE,PLUS = XSon.GradualTurn(s, nil, ANG1, s.floorspriteslope.xydirection, s.floorspriteslope.zangle)
								s.floorspriteslope.zangle = $-(ANGLE*PLUS)
							end
						end
					end
				elseif s.floorspriteslope and s.floorspriteslope.zangle --Even out gradually after you've fallen over.
					s.floorspriteslope.zangle = (s.floorspriteslope.zangle > ANG2) and FixedDiv(FixedMul($,44),45) or 0
				end
			end
		end
	elseif (s.WOODDEBRIS and not s.floorspriteslope) or (s.renderflags & RF_PAPERSPRITE) or (s.frame & FF_PAPERSPRITE)
		s.angle = s.rollangle --Roll with papersprites in the air!		
	end
		
	if (s.trgmom > 37000) and (s.Init > 2) --Rollspeed is based on your momentum.
		if not s.WAITROLL and not (s.fuse<7)
			if s.TEMPROLL == 0 --Don't keep rolling past this stage!
				if not (s.renderflags & RF_FULLDARK) and not s.DONTFLICKER
					s.flags2 = ($&~MF2_DONTDRAW)|((leveltime%2) and MF2_DONTDRAW or 0)
				end
			elseif not s.SPECIALDEBRIS and (leveltime%3==0)
			or s.SPECIALDEBRIS and (leveltime%2) --Rollangle is dangerous to memory usage, potentially allowing a crash. So taper it off!
				if not s.NOTROBOT and not (s.debrisLVL > 230)
				and not (XS_CRAPPYPRIORITIES==1)
					local ADDROLL = (FixedAngle(s.trgmom)*(s.SPECIALDEBRIS and 2 or 3)*s.RollDirection)
					if TEMPROLL ADDROLL = (abs(ADDROLL) < ANG1*5) and $*5 or (abs(ADDROLL) < ANG1*14) and $*3 or $*2 end
					s.rollangle = $+ADDROLL 
				end
			end
		end	
	/*	if (s.debrisLVL > 100)
			if not (s.debrisLVL > 230)
				if (leveltime%2) --Rollangle is dangerous to memory usage, potentially allowing a crash. So taper it off!
					s.rollangle = $+(FixedAngle(s.trgmom)*2*s.RollDirection)
				end
			end
		else
			s.rollangle = $+(FixedAngle(s.trgmom)*s.RollDirection)
		end */
	elseif (s.flags & MF_GRENADEBOUNCE) and (s.Init > 50)
		s.flags = $ & ~MF_GRENADEBOUNCE
	end
	if (s.momz*P_MobjFlip(s) > -5<<16) and (s.momz*P_MobjFlip(s) > -11<<16) and (s.Init > 5) --Fall quicker.
		local FALLSPEED = FixedDiv(FixedMul(7776, 32678), P_GetMobjGravity(s) or 32678)
		if (s.Init > 20) FALLSPEED = $*2 end
		s.momz = $+(FALLSPEED*P_MobjFlip(s)) --Fall quicker!
	end
	if s.fuse and (s.fuse < 10)
		s.frame = ($ & ~FF_TRANSMASK)|(FF_TRANS90-(s.fuse<<16))
		if (s.SMOKINGDEBRIS or s.SPECIALDEBRIS) and not s.NOTROBOT 
			s.renderflags = $|RF_FULLDARK
		end
	end	
end

--Flung Objects like rocks.
addHook("MobjThinker", function(s)
	if s.valid XSon.FlungThinker(s) end
end, MT_XSFLUNG)

--Sand Bag Thinker
addHook("MobjThinker", function(s)
	if s.valid XSon.SandBag(s) end
end, MT_XSSANDBAG)
