local spawnx = 0
local spawny = 0
local spawnz = 0
local spawnangle = 0
local spawndrawangle = 0

local scramble = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'}
local argument = ""
for i = 1, 45
  argument = $ + scramble[P_RandomKey(#scramble+1)]
end

addHook("NetVars", function(network)
	scramble = network($)
	argument = network($)
end)

COM_AddCommand("ss_hypercheck", function(player, arg)
	if (player and player.mo and player.mo.valid)
		if player.hyper.isunlocked == nil
			if arg == argument
				player.hyper.isunlocked = true
			else
				player.hyper.isunlocked = false
			end
		end
	end
end, 0)

local function Thoksplosion(mo)
	if mo and mo.valid
		for i = 1, 64
			if i <= 32 --we'll have 32 thok-based booms
				local boom2 = P_SpawnMobjFromMobj(mo, P_RandomRange(-25, 25)*FRACUNIT,
				P_RandomRange(-25, 25)*FRACUNIT,
				P_RandomRange(-25, 25)*FRACUNIT,
				MT_THOK)
				boom2.tics = TICRATE*2
				boom2.boomer = true --we'll attach a separate hook to this mobj
				P_InstaThrust(boom2, FixedAngle(P_RandomRange(0, 360)*FRACUNIT), P_RandomRange(5, 10)*FRACUNIT)
				boom2.momz = P_RandomRange(5, 10)*FRACUNIT*P_MobjFlip(boom2)
				boom2.flags = MF_NOCLIPHEIGHT|MF_NOCLIP
				boom2.destscale = 0
				boom2.scalespeed = FRACUNIT/(TICRATE*2)
				boom2.color = SKINCOLOR_YELLOW
			else
				local boom2 = P_SpawnMobjFromMobj(mo, P_RandomRange(-60, 60)*FRACUNIT,
				P_RandomRange(-60, 60)*FRACUNIT,
				P_RandomRange(-60, 60)*FRACUNIT,
				P_RandomChance(FRACUNIT/2) and MT_BOSSEXPLODE or MT_SONIC3KBOSSEXPLODE)--50% chance of spawning either
				boom2.tics = $ + P_RandomRange(0, 10) --asking for a bit more variety
				boom2.scale = $ + P_RandomRange(0, FRACUNIT/2)
			end
		end
	end		
end

freeslot("sfx_ringtk",
"sfx_hypert",
"MT_SUPERWARP",
"S_SUPERWARP",
"SPR_WARP",
"MT_SUPERBOLT",
"MT_SSHYPERSPARKLES",
"S_SSHYPERSPARKLES1",
"S_SSHYPERSPARKLES2",
"S_SSHYPERSPARKLES3",
"S_SSHYPERSPARKLES4",
"S_SSHYPERSPARKLES5",
"S_SSHYPERSPARKLES6",
"S_SSHYPERSPARKLES7",
"S_SSHYPERSPARKLES8",
"S_SSHYPERSPARKLES9",
"S_SSHYPERSPARKLES10",
"S_SSHYPERSPARKLES11",
"S_SSHYPERSPARKLES12",
"S_SSHYPERSPARKLES13",
"S_SSHYPERSPARKLES14",
"S_SSHYPERSPARKLES15",
"SPR_HSPK")
sfxinfo[sfx_ringtk].caption = "Got Token"
states[S_SUPERWARP] = {SPR_WARP, A|FF_ANIMATE|FF_FULLBRIGHT, 7, nil, 7, 1, S_NULL}

mobjinfo[MT_SUPERWARP] = {
	spawnstate = S_SUPERWARP,
	spawnhealth = 1,
    deathstate = S_NULL,
    speed = FRACUNIT,
    radius = FRACUNIT,
    height = FRACUNIT,
	flags = MF_SCENERY|MF_NOGRAVITY|MF_NOCLIPTHING
}

addHook("NetVars", function(network)
	spawnx = network($)
	spawny = network($)
	spawnz = network($)
	spawnangle = network($)
	spawndrawangle = network($)
end)

addHook("MapLoad", function()
	for player in players.iterate
		spawnx = player.mo.x
		spawny = player.mo.y
		spawnz = player.mo.z
		spawnangle = player.mo.angle
		spawndrawangle = player.drawangle
	end
end)

addHook("ThinkFrame", do
	for player in players.iterate
		if player.mo and player.mo.valid
			if not player.mo.storedrings
				player.mo.storedrings = 0
			end
			if not player.mo.ssmorbing
				player.mo.ssmorbing = false --for later
			end
			if not player.mo.signpost
				player.mo.signpost = 0
			end
			if not player.mo.storedmusicpos
				player.mo.storedmusicpos = 0
			end
			if not player.mo.got1up
				player.mo.got1up = 0
			end
			if not player.mo.issuper
				player.mo.issuper = false
			end
			if not player.mo.switchtimer
				player.mo.switchtimer = 0
			end
			if not player.mo.shoespower
				player.mo.shoespower = false
			end
			if not player.mo.shoestats
				player.mo.shoestats = false
			end
			if not player.mo.sparklespower
				player.mo.sparklespower = false
			end
			if not player.sshypermusic
				player.sshypermusic = false
			end
			if not player.supertime
				player.supertime = 0
			end
			if player.mrce --don't ask
				player.thokitem = 0
			end
			if player.mo and player.mo.valid
			and player.powers[pw_extralife]
				player.mo.got1up = true
			else
				if player.mo.got1up == true
					if not player.sshypermusic 
						S_SetMusicPosition(player.mo.storedmusicpos)
						S_SetInternalMusicVolume(0, player)
						S_FadeMusic(100, MUSICRATE, player)
						player.mo.got1up = false
					end
				end
				player.mo.storedmusicpos = S_GetMusicPosition()
			end
			if player.mo.skin == "supersonic"
				if player.mo.switchtimer < 2
					player.mo.switchtimer = $ + 1
				end
				if player.mo.issuper == false
					player.mo.issuper = true
				end
				player.shieldscale = 0
			end
			if player.mo and player.mo.valid
			and player.mo.skin ~= "supersonic"
			and player.mo.issuper == true
				player.powers[pw_super] = 0
				if not ((player.scolor and player.scolor ~= "default")
				and (player.powers[pw_shield] & SH_FIREFLOWER))
					player.mo.color = player.skincolor
				end
				player.mo.state = $
				player.shieldscale = skins[player.mo.skin].shieldscale
				P_SpawnShieldOrb(player)
				player.mo.issuper = false
				player.mo.switchtimer = 0
			end
			if S_MusicName() == "_super"
			and player.mo.issuper == false
				P_RestoreMusic(player)
			end
			if player.mo and player.mo.valid and player.mo.skin == "supersonic"
				if player.hyper.isunlocked == nil
					if io and player == consoleplayer
						local file = io.openlocal("client/supersonic/superdata.dat")
						if file
							local string = file:read("*a")
							if string == "true"
								COM_BufInsertText(player, 'ss_hypercheck ' + argument) 
							elseif string == "false" or string == nil
								COM_BufInsertText(player, 'ss_hypercheck e')  -- (:
							end
							file:close()
						else
							COM_BufInsertText(player, 'ss_hypercheck e')  -- (:
						end
					end
				end
				player.mo.frame = $|FF_FULLBRIGHT
				// Ring attraction originally from Robe, thank you SMS Alfredo...
				local in2D = twodlevel or player.mo.flags2 & MF2_TWOD
				local maxdist = FixedMul(RING_DIST/6, player.mo.scale)
				local span = ANGLE_45
				local thokmobj = P_SpawnMobjFromMobj(player.mo, 0, 0, 0, MT_THOK)
				thokmobj.radius = maxdist

				local rings = {MT_RING, MT_FLINGRING, MT_COIN, MT_FLINGCOIN}

				searchBlockmap("objects", function(refmobj, object)
					if object == player.mo
					or object.flags & (MF_NOCLIPTHING|MF_MISSILE)
					or object.flags2 & MF2_FRET
					or object.health <= 0
					or not P_CheckSight(player.mo, object)
					or (in2D	and abs(player.mo.y - object.y) > player.mo.radius)
						return
					end

					local isRing = false // Is a ring?

					// Iterate ring types.
					for i, v in ipairs(rings) do
						if object.type == v // We're a ring type?
							isRing = true // Set isRing and break
							break
						end
					end

					// Not a ring.
					if (not isRing or ((player.powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT or (player.powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN))
					and (not (object.flags & MF_ENEMY)
					or object.info.spawnhealth > 1
					or (object.type == MT_EGGGUARD and object.tracer)
					or (object.type == MT_BIGMINE and object.state >= S_BIGMINE_SET1)
					or (object.type == MT_BUMBLEBORE and object.state >= S_BUMBLEBORE_RAISE))
						return
					end

					local zdist = (player.mo.z + player.mo.height/2) - (object.z + player.mo.height/2)
					local dist = FixedHypot(player.mo.x - object.x, player.mo.y - object.y)

					if R_PointToAngle2(0, 0, dist, zdist) + span > span*2
					and R_PointToAngle2(0, 0, dist, -1*zdist) + span > span*2
						return// Don't home outside of desired angle!
					end
					
					dist = FixedHypot(dist, zdist*2)

					if (dist > maxdist)
						return// out of range
					end
					
					if isRing and object.info.reactiontime
					and object.type != object.info.reactiontime
						local newring = P_SpawnMobjFromMobj(object, 0, 0, 0, object.info.reactiontime)
						P_RemoveMobj(object)
						object = newring
					end
					
					local speed = object.info.speed
					object.info.speed = 80*FRACUNIT

					P_HomingAttack(object, player.mo)
					object.info.speed = speed
				end, thokmobj)

				P_RemoveMobj(thokmobj)
				if player.rings < 1
				and not netgame
				and not (player.hyper.transformed)
					P_DamageMobj(player.mo, nil, nil, 0, DMG_INSTAKILL)
				end
				if netgame
					if player.rings < 1
					and player.mo.switchtimer >= 2
					and player.playerstate == PST_LIVE
					and not player.hyper.transformed
						player.powers[pw_carry] = CR_NONE
						player.pflags = player.pflags & ~(PF_JUMPED | PF_SPINNING | PF_STARTDASH | PF_THOKKED | PF_SHIELDABILITY | PF_GLIDING | PF_BOUNCING | PF_SLIDING)
						player.mo.momx = 0
						player.mo.momy = 0
						player.mo.momz = 0
						if not player.starpostnum
							player.mo.angle = spawnangle
							player.drawangle = spawndrawangle
						else
							player.mo.angle = player.starpostangle
							player.drawangle = player.starpostangle
						end
						player.mo.state = S_PLAY_STND
						player.mo.flags2 = player.mo.flags2 & ~MF2_TWOD
						player.mo.flags2 = player.mo.flags2 & ~MF2_OBJECTFLIP
						if not player.starpostnum
							P_MoveOrigin(player.mo, spawnx, spawny, spawnz)
						else
							P_MoveOrigin(player.mo, player.starpostx*FRACUNIT, player.starposty*FRACUNIT, player.starpostz*FRACUNIT)
						end
						S_StartSound(player.mo, sfx_s3kb3)
						P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SUPERWARP)
						if ultimatemode
							player.rings = 250
						else
							player.rings = 25
						end
					end
					if not All7Emeralds(emeralds)
						R_SetPlayerSkin(player, "sonic")
						S_StartSound(player.mo, sfx_lose)
						if P_RandomChance(FRACUNIT/100*50)
							CONS_Printf(player, "\x85"+"You need all 7 Chaos Emeralds before using Super Sonic!")
						elseif P_RandomChance(FRACUNIT/100*30)
							CONS_Printf(player, "\x85"+"You must first collect all 7 gems of the chaotic variety.")
						elseif P_RandomChance(FRACUNIT/100*50)
							CONS_Printf(player, "\x85"+"Maybe collect all of those emeralds first!")
						elseif P_RandomChance(FRACUNIT/100*40)
							CONS_Printf(player, "\x85"+"He's named SUPER Sonic for a reason!")
						elseif P_RandomChance(FRACUNIT/100*30)
							CONS_Printf(player, "\x85"+"Did you get all of those Chaos Emeralds?")
						elseif P_RandomChance(FRACUNIT/100*30)
							CONS_Printf(player, "\x85"+"get all of them magic poprocks, my dude")
						elseif P_RandomChance(FRACUNIT/100*40)
							CONS_Printf(player, "\x85"+"I hope you didn't expect to go super without those Chaos Emeralds!")
						end
					end
				end
				if player.playerstate == PST_LIVE and player.lives > 0 and player.spectator == false and not netgame and All7Emeralds(emeralds) == false then
					emeralds = $ | EMERALD1|EMERALD2|EMERALD3|EMERALD4|EMERALD5|EMERALD6|EMERALD7
				end
				if not player.powers[pw_super]
				and not player.exiting
					player.powers[pw_super] = 1
				end
				if player.powers[pw_sneakers] > 10*TICRATE
				and not player.hyper.transformed 
					player.powers[pw_sneakers] = 10*TICRATE
					player.mo.shoespower = true
					S_StartSound(player.mo, sfx_s3k9f)
				elseif not player.powers[pw_sneakers]
				and player.mo.shoespower == true
					player.mo.shoespower = false
					player.mo.shoestats = false
					S_StartSound(player.mo, sfx_kc65)
				end
				if player.powers[pw_invulnerability] > 10*TICRATE
				and not player.hyper.transformed 
					player.powers[pw_invulnerability] = 10*TICRATE
					player.mo.sparklespower = true
					S_StartSound(player.mo, sfx_s3k46)
				elseif not player.powers[pw_invulnerability]
				and player.mo.sparklespower == true
					player.mo.sparklespower = false
					S_StartSound(player.mo, sfx_kc65)
				end
				if player.mo.shoespower
				or player.hyper.transformed
					player.mo.shoestats = true
					if player.mo.shoespower
						if ((leveltime % 1 == 0 and not (player.powers[pw_shield] & SH_FIREFLOWER)) or ((player.powers[pw_shield] & SH_FIREFLOWER) and leveltime % 2 == 0))
							local superbolt = P_SpawnMobjFromMobj(player.mo, FRACUNIT*P_RandomRange(-16,16), FRACUNIT*P_RandomRange(-16,16), FixedDiv(player.mo.height/2,player.mo.scale)+(P_RandomRange(-16,16)*FRACUNIT), MT_SUPERBOLT)
							//superbolt.momz = FixedMul(FRACUNIT*2, player.mo.scale)*P_MobjFlip(player.mo)
							superbolt.scale = player.mo.scale
							superbolt.colorized = true
							superbolt.color = SKINCOLOR_SKY
							superbolt.frame = $|FF_FULLBRIGHT
							superbolt.target = player.mo
							if (player.powers[pw_shield] & SH_FIREFLOWER)
								superbolt.state = S_PYREFIRE2
								//superbolt.fuse = 6
								superbolt.scale = player.mo.scale/3
								superbolt.destscale = 0
								superbolt.scalespeed = FRACUNIT/(TICRATE*2)
								superbolt.colorized = false
							end
							if superbolt.target.player == displayplayer
								local chasecam = CV_FindVar("chasecam")
								if (chasecam.value == 1)
									superbolt.flags2 = $ & ~MF2_DONTDRAW
								elseif (chasecam.value == 0)
									superbolt.flags2 = $ | MF2_DONTDRAW
								end
							end
						end
						local ghost1 = P_SpawnGhostMobj(player.mo)
						ghost1.color = player.mo.color
						if (player.powers[pw_shield] & SH_FIREFLOWER)
						and not (player.scolor and player.scolor ~= "default")
							ghost1.color = SKINCOLOR_FIRESTORMSUPER
						end
						if player.mo.colorized
							ghost1.colorized = true
						else
							ghost1.colorized = false
						end
						ghost1.rollangle = player.mo.rollangle
						ghost1.fuse = 4
						ghost1.eflags = player.mo.eflags & MFE_VERTICALFLIP
						ghost1.source = player.mo
						if ghost1.source.player == displayplayer
							local chasecam = CV_FindVar("chasecam")
							if (chasecam.value == 1)
								ghost1.flags2 = $ & ~MF2_DONTDRAW
							elseif (chasecam.value == 0)
								ghost1.flags2 = $ | MF2_DONTDRAW
							end
						end
					end
					player.normalspeed = 54*FRACUNIT
					player.actionspd = 90*FRACUNIT
				end
				if not player.mo.shoestats
					player.normalspeed = 36*FRACUNIT
					player.actionspd = 60*FRACUNIT
				end
				if (netgame and (player.pflags & PF_FINISHED or player.powers[pw_carry] == CR_MINECART))
				or player.mo.sparklespower
				or (player.hyper.transformed and player.hyper.transformed <= 34)
				or not (player.realtime > player.supertime) //waiting periods on bosses can go die (:
				or (player.SolTransforming and player.SolTransforming > 1) --if becoming fire sonic
					if not player.mo.storedrings
					or player.rings > player.mo.storedrings
						player.mo.storedrings = player.rings
					end
					if player.rings < player.mo.storedrings
						player.rings = player.mo.storedrings
					end
				else
					player.mo.storedrings = 0
				end
				if player.cmd.buttons & BT_CUSTOM1
				and (player.hyper.capable == true)
				and not player.hyper.transformed
				and player.pflags & PF_JUMPED
				and not player.exiting
					player.hyper.transformed = 1
					player.powers[pw_extralife] = 0
					S_StartSound(nil, sfx_hypert)
					P_SetMobjStateNF(player.mo, S_PLAY_SUPER_TRANS1)
					player.mo.state = S_PLAY_SUPER_TRANS1
					player.mo.frame = A
				end
				local sparkcolors = {SKINCOLOR_IRIDESCENCE1, SKINCOLOR_IRIDESCENCE2, SKINCOLOR_IRIDESCENCE3, SKINCOLOR_IRIDESCENCE4, SKINCOLOR_IRIDESCENCE5, SKINCOLOR_IRIDESCENCE6}
				local getColor = (leveltime % #sparkcolors )
				if player.hyper.capable
				and not player.hyper.transformed
				and not player.exiting
				and not (player.powers[pw_shield] & SH_FIREFLOWER)
					if leveltime % 1 == 0
						local hyperspark = P_SpawnMobjFromMobj(player.mo, FRACUNIT*P_RandomRange(-16,16), FRACUNIT*P_RandomRange(-16,16), FixedDiv(player.mo.height/2,player.mo.scale)+(P_RandomRange(-16,16)*FRACUNIT), MT_SUPERBOLT)
						hyperspark.state = S_SEED
						hyperspark.fuse = 6
						hyperspark.scale = player.mo.scale
						hyperspark.colorized = true
						hyperspark.color = sparkcolors[getColor+1]
						hyperspark.frame = $|FF_FULLBRIGHT
						hyperspark.target = player.mo
						if hyperspark.target.player == displayplayer
							local chasecam = CV_FindVar("chasecam")
							if (chasecam.value == 1)
								hyperspark.flags2 = $ & ~MF2_DONTDRAW
							elseif (chasecam.value == 0)
								hyperspark.flags2 = $ | MF2_DONTDRAW
							end
						end
					end
				end
				if player.hyper.transformed 
					if player.powers[pw_invulnerability] > 0
					or	player.powers[pw_sneakers] > 0
						player.powers[pw_invulnerability] = 0
						player.powers[pw_sneakers] = 0
						P_GivePlayerRings(player, 20)
						S_StartSound(player.mo, sfx_token)
					end
					player.charflags = $|SF_CANBUSTWALLS
					if player.hyper.transformed < TICRATE
						player.hyper.transformed = $ + 1
					end
					//safety net so you don't die for going hyper.
					if (player.rings < 1)
					or player.exiting
						player.hyper.capable = false
						player.hyper.transformed = 0
						
						if player.rings < 1
							player.rings = 10
						end
						player.mo.shoestats = false //take away shoe stats.
						player.sshypermusic = false //music hinge
						S_StartSound(player.mo, sfx_s3k9c) //sound
						player.thrustfactor = skins[player.mo.skin].thrustfactor //thrustfactor reset
						player.powers[pw_flashing] = 35 //flashing for aesthetic purposes
						P_RestoreMusic(player)
					end
				else
					player.charflags = $ & ~SF_CANBUSTWALLS
				end
			end
		end
	end
end)

//Give the player 25 Rings if they pick up an Emerald Token, and manage their color.
//Also manage music stuff.
addHook("PlayerThink", function(player)
	if player.mo and player.mo.valid and player.mo.skin == "supersonic"
		if not ((player.scolor and player.scolor ~= "default")
		and (player.powers[pw_shield] & SH_FIREFLOWER))
			player.mo.color = player.skincolor
		end
		if player.mo.ringtokengrabbed
			if ultimatemode return end
			P_GivePlayerRings(player, 25)
			P_AddPlayerScore(player, 1000)
			S_StartSound(player.mo, sfx_ringtk)
			player.mo.ringtokengrabbed = false
		end
		if string.lower(S_MusicName()) == "_super"
            S_ChangeMusic(mapmusname, true, player)
        end
		if string.lower(S_MusicName()) == "sshyp"
		and not player.hyper.transformed
            S_ChangeMusic(mapmusname, true, player)
        end
	end
end)

addHook("TouchSpecial", function(emeraldtoken, mo)
	if All7Emeralds(emeralds)
	and mo.skin == "supersonic"
		if not emeraldtoken.grabbed then
			mo.ringtokengrabbed = true
			emeraldtoken.grabbed = true
			P_KillMobj(emeraldtoken)
			if ultimatemode
				S_StartSound(mo, sfx_lose)
			end
			return true
		end
	elseif not All7Emeralds(emeralds)
		return false
	end
end, MT_TOKEN)

//Making it so you start with 25 rings and am super upon spawn.
addHook("PlayerSpawn", function(player)
	if player.mo and player.mo.valid and player.mo.skin == "supersonic"
		if ultimatemode
			P_GivePlayerRings(player, 250)
		else
			P_GivePlayerRings(player, 25)
		end
		player.mo.state = $
		if player.hyper
		and player.hyper.transformed
			player.hyper.transformed = 0
		end
	end
end)
		
addHook("MobjDamage", function(target, inflictor, source, damage, damagetype)
	if inflictor and inflictor.valid and inflictor.type == MT_PLAYER
		local issuper = false
		for player in players.iterate
			if player.mo and player.mo.valid
			and player.mo.skin == "supersonic"
				issuper = true
			end
		end
		if issuper
		and not ultimatemode
			if target.flags & MF_BOSS
			and inflictor.skin == "supersonic" --Double checking since the 'issuper' check isn't enough, apparently.
				if inflictor.player.hyper.transformed
				and not (damagetype & DMG_NUKE)
					if inflictor.player.mrce -- hitting boses with rebound dash while hyper triggers the ring stuff TWICE, so... if in MR:CE and hyper against a boss, nerf the ring gain.
						P_GivePlayerRings(inflictor.player, 3)
					else
						P_GivePlayerRings(inflictor.player, 6)
					end
				elseif (damagetype & DMG_NUKE)
					P_GivePlayerRings(inflictor.player, 4)
				else
					P_GivePlayerRings(inflictor.player, 5)
				end
				S_StartSoundAtVolume(target.mo, sfx_kc5e, 105)
			end
			if target.flags & MF_ENEMY
			and inflictor.skin == "supersonic" --Double checking since the 'issuper' check isn't enough, apparently.
				if inflictor.player.hyper.transformed
				and not (damagetype & DMG_NUKE)
					P_GivePlayerRings(inflictor.player, 4)
				elseif (damagetype & DMG_NUKE)
					P_GivePlayerRings(inflictor.player, 2)
				else
					P_GivePlayerRings(inflictor.player, 2)
				end
				S_StartSoundAtVolume(inflictor, sfx_kc5d, 105)
			end
		end
	end
end)

addHook("ShouldDamage", function(target, inflictor, source, damage, damagetype)
    if target and target.valid and target.skin == "supersonic"
	and netgame
	and target.player.rings == 0
	and not (damagetype & DMG_DEATHMASK)
        return false
	end
end)

addHook("MobjThinker", function(sign)
	if consoleplayer.powers[pw_carry] & CR_NIGHTSMODE return end
	if sign.tracer.tracer  --tracers of tracers are dumb.
		if sign.tracer.tracer.skin == "supersonic"
			sign.tracer.tracer.tics = -1
			sign.tracer.tracer.frame = ((leveltime / 7) % 4) | (FF_FULLBRIGHT|FF_PAPERSPRITE)
		end
	end
end, MT_SIGN)

//Ring Attraction function based upon P_Attract from source.
/*local function SuperAttract(source, dest)
	local dist = 0
	local ndist = 0
	local speedmul = 0
	local tx = dest.x
	local ty = dest.y
	local tz = dest.z + (dest.height/2)
	local xydist = P_AproxDistance(tx - source.x, ty - source.y)
	if dest and dest.health and dest.valid and dest.type == MT_PLAYER
		source.angle = R_PointToAngle2(source.x, source.y, tx, ty)
		dist = P_AproxDistance(xydist, tz - source.z)
		if (dist < 1)
			dist = 1
		end
		speedmul = P_AproxDistance(dest.momx, dest.momy) + FixedMul(source.info.speed, source.scale)
		source.momx = FixedMul(FixedDiv(tx - source.x, dist), speedmul)
		source.momy = FixedMul(FixedDiv(ty - source.y, dist), speedmul)
		source.momz = FixedMul(FixedDiv(tz - source.z, dist), speedmul)
		ndist = P_AproxDistance(P_AproxDistance(tx - (source.x + source.momx), ty - (source.y+source.momy)), tz - (source.z+source.momz))
		if (ndist > dist)
			source.momx = 0
			source.momy = 0
			source.momz = 0
			P_MoveOrigin(source, tx, ty, tz)
		end
	end
end

addHook("MobjThinker", function(ring)
    if ring and ring.valid and ring.health > 0
		print("oh no")
		if (ring.type == MT_RING or ring.type == MT_COIN)
			local soup
			for p in players.iterate
				if p.mo and p.mo.valid and p.mo.skin == "supersonic" and R_PointToDist2(p.mo.x, p.mo.y, ring.x, ring.y) <= (RING_DIST/6) and abs(ring.z - p.mo.z) < (RING_DIST/6)
					soup = p.mo
				end
			end
			if soup
				if ring.type == MT_RING
					local momRing = P_SpawnMobjFromMobj(ring, 0,0,0, MT_FLINGRING)
					momRing.followmo = soup
				elseif ring.type == MT_COIN
					local momCoin = P_SpawnMobjFromMobj(ring, 0,0,0, MT_FLINGCOIN)
					momCoin.followmo = soup
				end
				P_RemoveMobj(ring)
			end
		end
    end
end)

addHook("MobjThinker", function(ring)
    if ring and ring.valid and ring.health > 0 and ring.followmo and ring.followmo.valid
		print("double oh no")
		if (ring.type == MT_FLINGRING or ring.type == MT_FLINGCOIN)
			if R_PointToDist2(ring.followmo.x, ring.followmo.y, ring.x, ring.y) <= RING_DIST/3 and abs(ring.z - ring.followmo.z) < RING_DIST/3
				SuperAttract(ring, ring.followmo)
			else
				ring.fuse = 5*TICRATE
			end
			if ring.fuse == 2
				if ring.type == MT_FLINGRING
					P_SpawnMobjFromMobj(ring, 0,0,0, MT_RING)
				elseif ring.type == MT_FLINGCOIN
					P_SpawnMobjFromMobj(ring, 0,0,0, MT_COIN)
				end
				P_RemoveMobj(ring)
			end
		end
	end
end)*/

//Bloops and GoldenShine were here having a conniption with me -Icezer
local function SCREWHEIGHTCHECKS(i_swear, to_god, you_bugger)
  if ((i_swear.z <= to_god.z + to_god.height + you_bugger)
  and (to_god.z <= i_swear.z + i_swear.height + you_bugger))
    return true
  end
end

local function SpikesBegone(spike, plmo)
    if not (plmo and plmo.valid and plmo.player) return end
    if (plmo.skin ~= "supersonic") return end
    if SCREWHEIGHTCHECKS(spike, plmo, 5*FU+abs(plmo.momz))
		local p = plmo.player
		if plmo.shoespower
		or (p.hyper and p.hyper.transformed)
		or (p.powers[pw_shield] & SH_FIREFLOWER) --fire sonic!
			P_KillMobj(spike, plmo, plmo)
			return false
		end
	end
end

addHook("MobjCollide", SpikesBegone, MT_SPIKE)
addHook("MobjCollide", SpikesBegone, MT_WALLSPIKE)

addHook("PostThinkFrame", function(player)
	for player in players.iterate do
		if player.mo and player.mo.valid and player.mo.skin == "supersonic"
			player.supertime = player.realtime //doing this for reasons
		end
	end
end)

addHook("MobjThinker", function(s)
//This code here was originally written by Golden Shine for SA-Sonic. Reused with explicit permission, so please do NOT reuse without asking him.
	if s.target and s.target.valid and s.target.player --You have passed the vibe check, observe and steal nothing.
	and s.target.player.exiting and (s.target.player.powers[pw_carry] == CR_NIGHTSMODE)
		if (s.sprite == SPR_CEMG) and (s.frame == H or s.frame >= H|FF_FULLBRIGHT)
		or (gamemap == 57 and mapheaderinfo[gamemap].lvlttl == "Black Hole") --You're welcome, Goal Ring mod.
			for z in players.iterate
				s.target.player.hyper.isunlocked = true
			end
			if s.target.player.hyper.isunlocked == true
				if io and s.target.player == consoleplayer
					local file = io.openlocal("client/supersonic/superdata.dat", "w+")
					file:write('true')
					file:close()
				end
			end
		end
	end
end, MT_GOTEMERALD)

// To prevent duplicate freeslots
// Thanks Xian , i have literally NEVER heard of, seen, or USED this before. -Icezer
local function SafeFreeslot(...)
    for _, item in ipairs({...})
        if rawget(_G, item) == nil
            freeslot(item)
        end
    end
end
SafeFreeslot("SPR2_MRCL")

addHook("MobjMoveCollide", function(h, v)
	if not (h or h.player) return end
	local p = h.player
	if h.skin == "supersonic"
	and (v.valid and v.health and h.player and (((h.player.powers[pw_shield] & SH_FIREFLOWER)) or (h.player.hyper and h.player.hyper.transformed)))
	local px = h.x
	local py = h.y
	local br = 250*h.scale
		searchBlockmap("objects", function(mo, found)
			if found.valid
			and (found.type == MT_SALOONDOOR or found.type == MT_SALOONDOORCENTER) //the fuck
			and not found.deadlol
				found.flags = $ | MF_NOCLIP
				if found.type == MT_SALOONDOOR
					P_KillMobj(found)
					S_StartSound(h,sfx_wbreak)
					S_StartSound(h,sfx_s3k59)
				else
					Thoksplosion(found)
					found.deadlol = true
				end
			end
		end, h, px-br, px+br, py-br, py+br)
	end
end, MT_PLAYER)