freeslot("S_PLAY_GRND", "S_PLAY_MODERNGRIND")
freeslot("SPR2_GRND")
freeslot("sfx_mjump", "sfx_jumpwt", "sfx_bowl")
//Grind
states[S_PLAY_MODERNGRIND] = {SPR_PLAY, SPR2_GRND|FF_ANIMATE, 4, nil, 1, 2, S_PLAY_MODERNGRIND}


//I'll be honest, I don't know how this works. But let's just be glad it does
local function SpawnMobjFrom(mobj, xofs, yofs, zofs, newtype)
	xofs = FixedMul(xofs, mobj.scale)
	yofs = FixedMul(yofs, mobj.scale)
	zofs = FixedMul(zofs, mobj.scale)

	local newobj = P_SpawnMobj(mobj.x+xofs, mobj.y+yofs, mobj.z+zofs, newtype)
	if (mobj.eflags & MFE_VERTICALFLIP)
		local elementheight = FixedMul(newobj.info.height, mobj.scale)

		newobj.eflags = $1 | MFE_VERTICALFLIP
		newobj.flags2 = $1 | MF2_OBJECTFLIP
		newobj.z = mobj.z + mobj.height - zofs - elementheight
	end
	newobj.destscale = mobj.destscale
	P_SetScale(newobj, mobj.scale)
	return newobj
end

	// I/O Support, I don't know how to put them in one file so they're all going in a special folder
local configplayer

local function valid(mo)
	return mo and mo.valid
end

rawset(_G, "GetRail", function(mo)
    local sector = mo.subsector.sector
    local slope = mo.standingslope 
    
    if mo.eflags & MFE_VERTICALFLIP
        if slope
            if slope == sector.c_slope
                return sector.ceilingpic
            end
            for fof in sector.ffloors()
                if slope == fof.b_slope
                and (fof.flags & (FF_EXISTS|FF_BLOCKPLAYER|FF_PLATFORM)) == (FF_EXISTS|FF_BLOCKPLAYER)
                    return true
                end
            end
        else
            local z = mo.z + mo.height
            if z == sector.ceilingheight
                return sector.ceilingpic
            end
            for fof in sector.ffloors()
                if z == fof.bottomheight
                and (fof.flags & (FF_EXISTS|FF_BLOCKPLAYER|FF_PLATFORM)) == (FF_EXISTS|FF_BLOCKPLAYER)
                   return true
                end
            end
        end
    else
        if slope
            if slope == sector.f_slope
                return sector.floorpic
            end
            for fof in sector.ffloors()
                if slope == fof.t_slope
                and (fof.flags & (FF_EXISTS|FF_BLOCKPLAYER|FF_REVERSEPLATFORM)) == (FF_EXISTS|FF_BLOCKPLAYER)
                  
					return true
                end
            end
        else
            local z = mo.z
            if z == sector.floorheight
                return sector.floorpic
            end
            for fof in sector.ffloors()
                if z == fof.topheight
                and (fof.flags & (FF_EXISTS|FF_BLOCKPLAYER|FF_REVERSEPLATFORM)) == (FF_EXISTS|FF_BLOCKPLAYER)
					return true
                end
            end
        end
    end
end)


local function resetmodern(player)
	if player.mo and player.mo.valid
		player.minecartjumpframe = 0
	end
end

addHook("PlayerSpawn", function(player)
	if player.mo and player.mo.valid
		resetmodern(player)
	end
end)

addHook("ThinkFrame", do
	if valid(consoleplayer)--Cyron can't fucking code but this is IO stuff
	and not valid(configplayer)
	and consoleplayer.voices == nil
		configplayer = consoleplayer
	end
	for player in players.iterate
		if player.mo
			if player.mo.skin ~= "sonic"
				if player.ret and player.ret.valid
					player.rettarget = 0
					P_RemoveMobj(player.ret)
					player.ret = 0
				end
			end
		end
		if (player.mo and player.mo.skin == "sonic")
			if player.variablesset == nil
				resetmodern(player)
			end
			if (player.powers[pw_carry] == CR_NIGHTSMODE)
				player.mo.frame = $1|FF_FULLBRIGHT
			end
			if player.minecartjumpframe > 5
				player.minecartjumpframe = 0
			end
//////RAIL GRINDING LANDING
			if (player.mo.eflags & MFE_JUSTHITFLOOR or player.speed > player.mo.scale)
			and P_IsObjectOnGround(player.mo) and player.mo.eflags & MFE_ONGROUND
			and not (player.pflags&PF_JUMPED) and player.playerstate == PST_LIVE
			and not (player.powers[pw_carry])
			
				local sec = R_PointInSubsector(player.mo.x+player.mo.momx,
				player.mo.y+player.mo.momy).sector
				local foffloor
				if (player.mo.eflags&MFE_VERTICALFLIP)
					for fof in sec.ffloors()
						if not (fof and fof.valid and fof.flags&FF_EXISTS) continue end
						if fof.b_slope and fof.b_slope.valid
							foffloor = P_GetZAt(fof.b_slope, player.mo.x, player.mo.y)
						else
							foffloor = fof.bottomheight
						end
						if abs(foffloor-player.mo.z+player.mo.height) <= player.mo.scale*8
							sec = fof.sector
							break
						end
					end
				else
					for fof in sec.ffloors()
						if not (fof and fof.valid and fof.flags&FF_EXISTS) continue end
						if fof.t_slope and fof.t_slope.valid
							foffloor = P_GetZAt(fof.t_slope, player.mo.x, player.mo.y)
						else
							foffloor = fof.topheight
						end
						if abs(foffloor-player.mo.z) <= player.mo.scale*8
							sec = fof.sector
							break
						end
					end
				end
				
				//P_GetMinecartSpecialLine
				local line = -1
				if sec and sec.valid
					if (sec.tag != 0)
						line = P_FindSpecialLineFromTag(16, sec.tag, -1)
					end

					// Also try for lines facing the sector itself, with tag 0.
					local i = 0 while sec.lines[i] != nil
						local li = sec.lines[i]
						if (li.tag == 0 and li.special == 16
						and li.frontsector == sec)
							line = 1//li - lines
						end
						i = $+1
					end
				end
				
				if line != -1
					local minecart = P_SpawnMobjFromMobj(player.mo,
					player.mo.momx, player.mo.momy, 0, MT_MINECART)
					if minecart and minecart.valid
						P_ResetPlayer(player)
						if player.speed >= player.mo.scale*10
							player.drawangle = R_PointToAngle2(0, 0, player.mo.momx, player.mo.momy)
							minecart.momx = player.mo.momx
							minecart.momy = player.mo.momy
						end
						player.mo.angle = player.drawangle
						minecart.angle = player.drawangle
						minecart.flags2 = $|MF2_DONTDRAW
						player.mo.tracer = minecart
						minecart.target = player.mo
						player.powers[pw_carry] = CR_MINECART
					end
				end
			end
		end
	end
end)

addHook("MobjThinker", function(minecart)
	if minecart and minecart.valid
	and minecart.target and minecart.target.valid
	and minecart.target.player
	and minecart.target.type == MT_PLAYER
	and minecart.target.skin == "sonic"
	and minecart.target.health
		minecart.flags2 = $1|MF2_DONTDRAW
		if minecart.target.player.neoboosting == true
			minecart.target.player.neoboosting = false
			minecart.target.player.minecartboost = true
		end
		                if leveltime % 3 == 1 then
					local clash = P_SpawnMobjFromMobj(minecart, P_RandomRange(-4, 4) * FRACUNIT, P_RandomRange(-4, 4) * FRACUNIT, 0, MT_THUNDERCOIN_SPARK)
					clash.momx = P_RandomRange(-4, 4)*FRACUNIT
					clash.momy = P_RandomRange(-4, 4)*FRACUNIT
					clash.momz = P_RandomRange(0, 4)*FRACUNIT
					clash.scale = FRACUNIT/4
					clash.destscale = FRACUNIT/3
					clash.blendmode = AST_ADD
					clash.fuse = 6
					clash.colorized = true
					clash.color = SKINCOLOR_FLAME
					local clasher = P_SpawnMobjFromMobj(minecart, P_RandomRange(-4, 4) * FRACUNIT, P_RandomRange(-4, 4) * FRACUNIT, 0, MT_THUNDERCOIN_SPARK)
					clasher.sprite = SPR_GSR2
					clasher.momx = P_RandomRange(-4, 4)*FRACUNIT
					clasher.momy = P_RandomRange(-4, 4)*FRACUNIT
					clasher.momz = P_RandomRange(0, 4)*FRACUNIT
					clasher.scale = FRACUNIT/4
					clasher.destscale = FRACUNIT/3
					clasher.blendmode = AST_ADD
					clasher.fuse = 6
					clasher.colorized = true
					clasher.color = SKINCOLOR_FLAME
					local clash2 = P_SpawnMobjFromMobj(minecart, P_RandomRange(-5, 5) * FRACUNIT, P_RandomRange(-5, 5) * FRACUNIT, 0, MT_MINECARTSPARK)
					clash2.momx = P_RandomRange(-4, 4)*FRACUNIT
					clash2.momy = P_RandomRange(-4, 4)*FRACUNIT
					clash2.momz = P_RandomRange(0, 4)*FRACUNIT
					clash2.scale = FRACUNIT/3
					clash2.destscale = FRACUNIT/2
					clash2.blendmode = AST_ADD
					clash2.fuse = 8
					clash2.colorized = true
					clash2.color = SKINCOLOR_FLAME
					local clash3 = P_SpawnMobjFromMobj(minecart, P_RandomRange(-6, 6) * FRACUNIT, P_RandomRange(-6, 6) * FRACUNIT, 0, MT_MINECARTSPARK)
					clash3.momx = P_RandomRange(-6, 6)*FRACUNIT
					clash3.momy = P_RandomRange(-6, 6)*FRACUNIT
					clash3.momz = P_RandomRange(0, 6)*FRACUNIT
					clash3.scale = FRACUNIT/3
					clash3.destscale = FRACUNIT/2
					clash3.blendmode = AST_ADD
					clash3.fuse = 8
					clash3.colorized = true
					clash3.color = SKINCOLOR_YELLOW
					local spark = P_SpawnMobjFromMobj(minecart, P_RandomRange(-8, 8) * FRACUNIT, P_RandomRange(-8, 8) * FRACUNIT, 0, MT_MINECARTSPARK)
					spark.momx = P_RandomRange(-5, 5)*FRACUNIT
					spark.momy = P_RandomRange(-5, 5)*FRACUNIT
					spark.momz = P_RandomRange(0, 5)*FRACUNIT
					spark.colorized = true
					spark.color = SKINCOLOR_ORANGE
					spark.scale = FRACUNIT
					spark.destscale = 4*FRACUNIT
					spark.blendmode = AST_ADD
					spark.fuse = 6
					local spark2 = P_SpawnMobjFromMobj(minecart, P_RandomRange(-10, 10) * FRACUNIT, P_RandomRange(-10, 10) * FRACUNIT, 0, MT_MINECARTSPARK)
					spark2.momx = P_RandomRange(-6, 6)*FRACUNIT
					spark2.momy = P_RandomRange(-6, 6)*FRACUNIT
					spark2.momz = P_RandomRange(0, 6)*FRACUNIT
					spark2.colorized = true
					spark2.color = SKINCOLOR_FLAME
					spark2.scale = FRACUNIT
					spark2.destscale = 6*FRACUNIT
					spark2.blendmode = AST_ADD
					spark2.fuse = 8
					local spark3 = P_SpawnMobjFromMobj(minecart, P_RandomRange(-15, 15) * FRACUNIT, P_RandomRange(-15, 15) * FRACUNIT, 0, MT_MINECARTSPARK)
					spark3.momx = P_RandomRange(-8, 8)*FRACUNIT
					spark3.momy = P_RandomRange(-8, 8)*FRACUNIT
					spark3.momz = P_RandomRange(0, 8)*FRACUNIT
					spark3.colorized = true
					spark3.color = SKINCOLOR_CRIMSON
					spark3.scale = FRACUNIT
					spark3.destscale = 8*FRACUNIT
					spark3.blendmode = AST_ADD
					spark3.fuse = 10
                end
		if S_SoundPlaying(minecart, sfx_s3k76)
			S_StartSound(minecart, sfx_grind)
			S_StopSoundByID(minecart, sfx_s3k76)
		end
		if not (P_IsObjectOnGround(minecart))
			minecart.target.state = S_PLAY_JUMP
			if minecart.target.player.powers[pw_super]
				minecart.target.frame = $1|FF_FULLBRIGHT
			end
		else
			if minecart.target.player.powers[pw_super]
				if minecart.target.player.minecartboost == true
					minecart.target.state = S_PLAY_DASH
				else
					minecart.target.state = S_PLAY_SPRING
				end
			else
				minecart.target.state = S_PLAY_MODERNGRIND
			end
		end
		
		if P_IsObjectOnGround(minecart)
			if minecart.target.player.minecartboost == true
				P_InstaThrust(minecart, minecart.angle, 60*FRACUNIT)
			else
				local speed = FixedHypot(minecart.momx, minecart.momy)
				if speed > FixedMul(35*FRACUNIT, minecart.target.scale)
					P_InstaThrust(minecart, minecart.angle, min(speed*5/7,20*FRACUNIT))
				end
			end
		elseif minecart.target.player.minecartboost
			minecart.target.player.minecartboost = false
			//local speed = FixedHypot(minecart.momx, minecart.momy)
			//if speed > FixedMul(20*FRACUNIT, minecart.target.scale)
			//	minecart.momx = $1*5/6
			//	minecart.momy = $1*5/6
			//end
		end
		
		if S_SoundPlaying(minecart, sfx_s3k51)
			if (minecart.eflags & MFE_UNDERWATER)
			S_StartSound(minecart.target, sfx_jumpwt)
			else
			S_StartSound(minecart.target, sfx_mjump)
			end
			S_StopSoundByID(minecart, sfx_s3k51)
		end
		
		if minecart.target.player.cmd.buttons&BT_JUMP
			P_KillMobj(minecart)
		elseif minecart.target.player.cmd.buttons&BT_USE
		and not P_IsObjectOnGround(minecart)
			P_KillMobj(minecart)
		end
	end
end, MT_MINECART)

//Prevent Minecart death
addHook("MobjDeath", function(s, i, so)
	if s and s.valid and s.target and s.target.player and s.target.skin == "sonic" and s.target.health
		s.target.player.powers[pw_carry] = 0
		s.target.momx = s.momx
		s.target.momy = s.momy
		s.target.momz = s.momz
		if s.target.player.cmd.buttons&BT_JUMP
			s.target.player.justjumped = true
			P_DoJump(s.target.player)
		end
		s.target = nil
		s.state = S_NULL
		return true
	end
end, MT_MINECART)

addHook("MobjMoveCollide", function(h, v)
	if not (h or h.player)
	return end
	if not (v and v.valid)
	return end
	local p = h.player
	if h.skin == "sonic"
	and not (h.z > v.z+v.height)
	and not (v.z > h.z+h.height)
		if ((v.type == MT_SALOONDOOR) or (v.type == MT_SALOONDOORCENTER))
			if v.valid and v.health
			or h.player and (h.player.neoboosting == true)
			and not (v.flags & MF_NOCLIP)
				v.hh = P_SpawnMobj(v.x, v.y, v.z, MT_BOSSEXPLODE)
				v.hh.scale = $*2
				if (v.type == MT_SALOONDOOR)
				v.flags = $|MF_NOCLIP
				P_KillMobj(v, h, h)
				elseif (v.type == MT_SALOONDOORCENTER)
				v.flags = $|MF_NOCLIP
				end
				S_StartSound(h, sfx_bowl)
				h.player.boostshake = 5
				return false
			end
		end
	end
end, MT_PLAYER)

//Prevent a sigsev.
addHook("MobjRemoved", function(s)
	if s and s.tracer and s.tracer.valid and s.tracer.player and (s.tracer.player.powers[pw_carry] == CR_ROLLOUT)
		s.tracer.player.powers[pw_carry] = 0
		s.tracer = nil
		s.flags = $|MF_PUSHABLE
	return
	end
end, MT_ROLLOUTROCK)