local colors = {SKINCOLOR_EMERALD, SKINCOLOR_PURPLE, SKINCOLOR_SAPPHIRE, SKINCOLOR_CYAN, SKINCOLOR_SUNSET, SKINCOLOR_RED, SKINCOLOR_GREY} --this is here for later
local ghostcolors = {SKINCOLOR_IRIDESCENCE1, SKINCOLOR_IRIDESCENCE2, SKINCOLOR_IRIDESCENCE3, SKINCOLOR_IRIDESCENCE4, SKINCOLOR_IRIDESCENCE5, SKINCOLOR_IRIDESCENCE6, SKINCOLOR_SILVER} --this too
--Hyper stars
local function SpawnHyperStars(player)
	local hyperstar = P_SpawnMobjFromMobj(player.mo, 0, 0, 20*FRACUNIT, MT_SSHYPERSPARKLES)
	hyperstar.momx = P_RandomRange(-5, 5) * FRACUNIT + FixedMul(-12, 12, player.mo.scale)
	hyperstar.momy = P_RandomRange(-5, 5) * FRACUNIT + FixedMul(-12, 12, player.mo.scale)
	hyperstar.momz = P_RandomRange(-5, 5) * FRACUNIT + FixedMul(-12, 12, player.mo.scale)
	hyperstar.color = colors[P_RandomRange(1, #colors)] --later is now (:
	//hyperstar.fuse = 6
	hyperstar.scale = player.mo.scale * 20/22
	hyperstar.source = player.mo
	if hyperstar.source.player == displayplayer
		local chasecam = CV_FindVar("chasecam")
		if (chasecam.value == 1)
			hyperstar.flags2 = $ & ~MF2_DONTDRAW
		elseif (chasecam.value == 0)
			hyperstar.flags2 = $ | MF2_DONTDRAW
		end
	end
	return hyperstar
end

//Tables of colors used for Hyper form's fading.
local fadingcolors = {SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE1, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE2, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE3, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE4, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE5, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE6}
local fadedcolors = {SKINCOLOR_IRIDESCENCE1, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE2, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE3, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE4, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE5, SKINCOLOR_IRIDESCENCEF, SKINCOLOR_IRIDESCENCE6, SKINCOLOR_IRIDESCENCEF}


--the actual hyper form stuff
addHook("PlayerThink",  function(player)
	local pmo = player.mo
	if not (pmo and pmo.valid) return end
	if (player == nil) return end

	if player.hyper == nil
		local hyper = {
		transformed = 0,
		capable = false,
		downdash = false --this is for later
		}
		player.hyperscript = true
		if pmo
			player.hyper = hyper
		else
			player.hyper = nil
		end
	end
	//print(player.hyper.downdash)
	--if you're capable of going hyper
	if /*(skins[pmo.skin].flags & SF_SUPER)*/ pmo.skin == "supersonic" and player.hyper.isunlocked == true
		--let's fucking go
		if player.rings >= 100
		and not player.hyper.transformed
		and not (player.powers[pw_shield] & SH_FIREFLOWER)
		and not (player.mo.ssmorbing)
		and not ultimatemode
			player.hyper.capable = true
		end
		--If you haven't collected enough rings, fuck off, you aren't going hyper.
		if player.rings < 100
		or (player.mo.ssmorbing)
		or (player.powers[pw_shield] & SH_FIREFLOWER)
		and not player.hyper.transformed
			player.hyper.capable = false
		end
		--Going hyper will drain your rings twice as fast!
		if (leveltime % 35 == 18) and (player.hyper.transformed)
			P_GivePlayerRings(player, -1)
		end
		if player.hyper.transformed
			SpawnHyperStars(player)
			--Set the correct skincolor for Hyper stuff.
			pmo.color = (SKINCOLOR_SUPERSILVER1 + 5*((leveltime >> 1) % 7))
			--Adjust stats for hyper forms
			/*if not (player.charflags & SF_NOSUPERJUMPBOOST)
				player.jumpfactor = skins[pmo.skin].jumpfactor * 4 / 3
			end*/
			//player.actionspd = skins[pmo.skin].actionspd * 6 / 4
			player.thrustfactor = skins[pmo.skin].thrustfactor * 4 / 3
			--Hyper forms don't need to breathe.
			if (pmo.eflags & MFE_UNDERWATER)
				player.powers[pw_underwater] = 0
			end
			if P_InSpaceSector(pmo)
				player.powers[pw_spacetime] = 0
			end
			--afterimages
			if (player.speed > 2*FRACUNIT or pmo.momz > 2*FRACUNIT or pmo.momz < -1*FRACUNIT)
			and (leveltime % 2 == 1)
				player.storedghostcolor = $ or SKINCOLOR_SILVER
				local afterimage = P_SpawnGhostMobj(pmo)
				local tempcolors = {} --let us commit tomfoolery
				for i = 1, #ghostcolors
					if player.storedghostcolor == ghostcolors[i]
						continue
					end
					table.insert(tempcolors, ghostcolors[i])
				end
				player.storedghostcolor = tempcolors[P_RandomRange(1, #tempcolors)] --later is now (:
				afterimage.rollangle = pmo.rollangle
				//afterimage.fuse = 5
				afterimage.colorized = true
				afterimage.color = player.storedghostcolor
				afterimage.blendmode = AST_ADD
				afterimage.source = pmo
				if afterimage.source.player == displayplayer
					local chasecam = CV_FindVar("chasecam")
					if (chasecam.value == 1)
						afterimage.flags2 = $ & ~MF2_DONTDRAW
					elseif (chasecam.value == 0)
						afterimage.flags2 = $ | MF2_DONTDRAW
					end
				end
			end
		elseif (player.hyper.transformed == 0 or player.hyper.isunlocked == false)
			//player.actionspd = skins[pmo.skin].actionspd
			player.thrustfactor = skins[pmo.skin].thrustfactor
		end
	end
	if player.mo and player.mo.valid and player.mo.skin == "supersonic" and player.hyper.transformed
		if not player.sshypermusic
			P_PlayJingleMusic(player, "SSHYP", 0, true, JT_OTHER)
		end
		player.sshypermusic = true
	else
		player.sshypermusic = false
	end
end)

addHook("ThinkFrame", do
	for player in players.iterate do
		if player.mo and player.mo.valid and player.mo.skin == "supersonic"
			if player.mo.hypercolortimer == nil
				player.mo.hypercolortimer = 0
			end
			if player.mo.hypercolortimerdelay == nil
				player.mo.hypercolortimerdelay = 0
			end
			if player.mo.frame & FF_TRANSMASK
				player.mo.hypercolortimer = nil
			end
			if player.powers[pw_extralife] == 2
			and player.hyper.transformed
				player.powers[pw_extralife] = 0
				if S_MusicName(player, "_1UP")
					player.sshypermusic = false
				end
			end
			if player.hyper.downdash
				local sectorthing = P_PlayerTouchingSectorSpecial(player, 0, 0)
				if sectorthing.valid then
					for fof in sectorthing.ffloors()
						if fof.valid and (fof.flags & FF_BUSTUP) and (fof.flags & FF_EXISTS)
						and (abs(fof.topheight - player.mo.z) <= 40*player.mo.scale) then
							EV_CrumbleChain(player.sectorthing, fof)
						end
					end
				end
				if player.mo.eflags & MFE_SPRUNG
					player.mo.momx = $*5/4
					player.mo.momy = $*5/4
					player.mo.momz = $*5/4
					S_StartSound(player.mo, sfx_sprong)
					player.hyper.downdash = false
				end
				if P_IsObjectOnGround(player.mo) or player.mo.eflags & MFE_JUSTHITFLOOR
				or player.powers[pw_carry]
				or (P_IsObjectInGoop(player.mo))
				or ((player.mo.momz*P_MobjFlip(player.mo)) > 2*FRACUNIT)
					player.hyper.downdash = false
				end
			end
			if player.hyper.transformed
			and player.mo.frame > 0
				if (player.mo.hypercolortimerdelay)
					player.mo.hypercolortimerdelay = $ - 1
				else
					player.mo.hypercolortimer = $ or 0
				end
				if player.mo.hypercolortimer != nil
					player.mo.hypercolortimer = $ + 1
					local overlay = P_SpawnMobjFromMobj(player.mo,0,0,0,MT_THOK)
					overlay.frame = (player.mo.frame & ~FF_TRANSMASK)
					overlay.color = fadingcolors[((player.mo.hypercolortimer % 132) / 11) + 1]
					overlay.angle = player.drawangle
					overlay.rollangle = player.mo.rollangle
					overlay.spritexscale = player.mo.spritexscale
					overlay.spriteyscale = player.mo.spriteyscale
					overlay.skin = player.mo.skin
					overlay.sprite = player.mo.sprite
					overlay.sprite2 = player.mo.sprite2
					overlay.info.dispoffset = 1
					overlay.tics = 1
					local fade = 11 - (player.mo.hypercolortimer % 11)
					local transition
					if (fade >= 1 and fade <= 11)
						transition = P_SpawnMobjFromMobj(player.mo,0,0,0,MT_THOK)
						transition.frame = (player.mo.frame & ~FF_TRANSMASK) | (fade << FF_TRANSSHIFT)
						transition.color = fadedcolors[((player.mo.hypercolortimer % 132) / 11) + 1]
						transition.angle = player.drawangle
						transition.rollangle = player.mo.rollangle
						transition.spritexscale = player.mo.spritexscale
						transition.spriteyscale = player.mo.spriteyscale
						transition.skin = player.mo.skin
						transition.sprite = player.mo.sprite
						transition.sprite2 = player.mo.sprite2
						transition.info.dispoffset = 1
						transition.tics = 1
					end
					if (player.mo.hypercolortimer == ((22*#fadingcolors)-1))
						player.mo.hypercolortimer = nil
					end
				end
			end
		end
	end
end)

--local variables
local vertical_dash_dist = 20*FRACUNIT

--The quakes should only appear for the player creating them
local function StartQuakes(player)
	if (player == consoleplayer)
         P_StartQuake(7*FRACUNIT, 4)
	end
end

--Handles dashing in 3D
local function Do3DHyperDash(player)
    local pmo = player.mo
    --my god is this code a mess
    local dash_forward = (player.cmd.forwardmove) and not ((((player.pflags & PF_USEDOWN) and (player.cmd.forwardmove) or ((player.cmd.forwardmove) and (player.cmd.sidemove))) and not (pmo.state == S_PLAY_FLOAT or pmo.state == S_PLAY_FLOAT_RUN)) or (player.cmd.buttons & BT_CUSTOM1))
    local dash_diagonal = ((player.cmd.forwardmove) and (player.cmd.sidemove)) and not (((player.pflags & PF_USEDOWN) or (player.cmd.buttons & BT_CUSTOM1)) and not (pmo.state == S_PLAY_FLOAT or pmo.state == S_PLAY_FLOAT_RUN))
	local dash_sideward = (player.cmd.sidemove) and not ((player.cmd.forwardmove) and (player.cmd.sidemove))  and not (((player.pflags & PF_USEDOWN) or (player.cmd.buttons & BT_CUSTOM1)) and not (pmo.state == S_PLAY_FLOAT or pmo.state == S_PLAY_FLOAT_RUN))
	local dash_upward = P_GetPlayerControlDirection(player) == 0 and not (((player.pflags & PF_USEDOWN) and not (pmo.state == S_PLAY_FLOAT or pmo.state == S_PLAY_FLOAT_RUN)) or (player.cmd.buttons & BT_CUSTOM1))
	local dash_downward = ((player.pflags & PF_USEDOWN) and not (pmo.state == S_PLAY_FLOAT or pmo.state == S_PLAY_FLOAT_RUN)) or (player.cmd.buttons & BT_CUSTOM1)
    if dash_forward
    	P_InstaThrust(pmo, pmo.angle, player.actionspd)
    elseif dash_diagonal
    	P_InstaThrust(pmo, player.drawangle, player.actionspd)
	elseif dash_sideward
    	P_InstaThrust(pmo, player.drawangle, player.actionspd)
    elseif dash_upward
    	P_SetObjectMomZ(pmo, vertical_dash_dist, false)
	elseif dash_downward
		if (player.cmd.buttons & BT_CUSTOM1)
		and P_GetPlayerControlDirection(player) == 0
			player.mo.momx = 0
			player.mo.momy = 0
		end
		player.hyper.downdash = true
		P_SetObjectMomZ(pmo, -vertical_dash_dist, false)
	end
end
--Handles dashing in 2D
--the exact same way it worked in Sonic 3 & Knuckles
local function Do2DHyperDash(player)
    local pmo = player.mo
    local dash_forward = (player.cmd.forwardmove)
	local dash_upward = (player.cmd.forwardmove > 0) and not (player.cmd.forwardmove < 0)
	local dash_downward = (player.cmd.forwardmove < 0) and not (player.cmd.forwardmove > 0)
    if not dash_forward
		P_InstaThrust(pmo, pmo.angle, player.actionspd)
	elseif dash_upward
        P_SetObjectMomZ(pmo, vertical_dash_dist, false)
	elseif dash_downward
        P_SetObjectMomZ(pmo, -vertical_dash_dist, false)
	end
end

--now we can dash, yay :DDDDDD
addHook("AbilitySpecial", function(player)
	if player.isTC return end -- i was asked to add this for some reason
    local pmo = player.mo
	if pmo.skin != "supersonic" then
	    return
	end
	if player.mrce then
		return
	end
	if (player.pflags & PF_THOKKED) then
    	return
	end
	
	if (player.hyper.transformed) 
	    P_SpawnThokMobj(player)
        S_StartSound(pmo, sfx_s3kb6) 
        P_NukeEnemies(pmo, pmo, 1214*FRACUNIT)
--        StartQuakes(player)
        player.pflags = $ | PF_THOKKED
        P_FlashPal(player, PAL_WHITE, 3)
    	if not (pmo.flags2 & MF2_TWOD)
    	    Do3DHyperDash(player) --3D Hyper Dash
		else
		    Do2DHyperDash(player) --2D Hyper Dash
		end
	elseif (player.hyper.transformed == 0) then
	    player.charability = skins[pmo.skin].ability
	end 
end)

addHook("ShouldJingleContinue", function(player, musname)
    if musname != "sshyp" return end
    if (player.issuper and player.hyper.transformed)
        return true
    else
        return false
    end
end)

local amogus
addHook("PreThinkFrame", function(r)
	amogus = S_GetMusicPosition()
end)

addHook("MusicChange", function(om, nm)
	if (gamestate != GS_LEVEL) return end
	if (consoleplayer and consoleplayer.mo and consoleplayer.mo.valid
	and consoleplayer.mo.health and consoleplayer.issuper
	and consoleplayer.hyper.transformed > 0)
	and (nm == "_shoes" or nm == "_inv" or nm == "_minv" or nm == mapmusname)
		if om == "sshyp"
			return true
		else
			return "sshyp", 0, true, amogus
		end
	end
end)