-- This whole mod is reusable, go ahead and do what you want.

-- SONIC --

local abilusedone = 0
local abilusedtwo = 0

--Special Thanks to Tatsuru for the thok code!
addHook("AbilitySpecial", function(p)
	if p.mo.skin != "sonic" return end
	if p.pflags & PF_THOKKED return end
	if abilusedone == 1 
	-- The Swift Stomp --
		if p.mo.eflags & MFE_UNDERWATER
			if p.speed/2 <= 7/2*FRACUNIT
				P_SetObjectMomZ(p.mo, -7/2*FRACUNIT)
				p.mo.state = S_PLAY_ROLL
				p.pflags = $1 & ~PF_NOJUMPDAMAGE
				if p.powers[pw_super] == 1
					S_StartSound(p.mo, sfx_zoom)
					p.mo.state = S_PLAY_ROLL
					p.pflags = $1 & ~PF_NOJUMPDAMAGE
				end
			elseif p.speed/2 >= 21/2*FRACUNIT
				P_SetObjectMomZ(p.mo, -21/2*FRACUNIT)
				S_StartSound(p.mo, sfx_zoom)
				p.mo.state = S_PLAY_ROLL
				p.pflags = $1 & ~PF_NOJUMPDAMAGE
			else
				P_SetObjectMomZ(p.mo, p.speed*-1/2)
				p.mo.state = S_PLAY_ROLL
				p.pflags = $1 & ~PF_NOJUMPDAMAGE
				if p.powers[pw_super] == 1
					S_StartSound(p.mo, sfx_zoom)
					p.mo.state = S_PLAY_ROLL
					p.pflags = $1 & ~PF_NOJUMPDAMAGE
				end
			end
		else
			if p.speed/2 <= 7*FRACUNIT
				P_SetObjectMomZ(p.mo, -7*FRACUNIT)
				p.mo.state = S_PLAY_ROLL
				p.pflags = $1 & ~PF_NOJUMPDAMAGE
				if p.powers[pw_super] == 1
					S_StartSound(p.mo, sfx_zoom)
				end
			elseif p.speed/2 >= 21*FRACUNIT
				P_SetObjectMomZ(p.mo, -21*FRACUNIT)
				p.mo.state = S_PLAY_ROLL
				p.pflags = $1 & ~PF_NOJUMPDAMAGE
				S_StartSound(p.mo, sfx_zoom)
			else
				P_SetObjectMomZ(p.mo, p.speed*-1/2)
				p.mo.state = S_PLAY_ROLL
				p.pflags = $1 & ~PF_NOJUMPDAMAGE
				if p.powers[pw_super] == 1
					S_StartSound(p.mo, sfx_zoom)
				end
			end
		end
		P_InstaThrust(p.mo, p.mo.angle, p.speed)
		P_SpawnThokMobj(p)
		S_StartSound(p.mo, sfx_thok)
		S_StartSound(p.mo, sfx_spin)
		abilusedtwo = 1
	else
	-- The Swift Surge --
		if p.powers[pw_super] == 1
			if p.mo.eflags & MFE_UNDERWATER
				if p.speed/2 <= 7*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, 7*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				elseif p.speed/2 >= 14*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, 14*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				else 
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, p.speed/4)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
					S_StartSound(p.mo, sfx_zoom)
				end
			else
				if p.speed/2 <= 14*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, 14*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
				elseif p.speed/2 >= 28*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, 28*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
				else 
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, p.speed/2)
					S_StartSound(p.mo, sfx_zoom)
				end
			end
		else
			if p.mo.eflags & MFE_UNDERWATER
				if p.speed/3 <= 7/2*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed*1/2)
					P_SetObjectMomZ(p.mo, 7/2*FRACUNIT)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				elseif p.speed/3 >= 7*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, 7*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				else 
					P_InstaThrust(p.mo, p.mo.angle, p.speed*3/4)
					P_SetObjectMomZ(p.mo, p.speed/6)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				end
			else
				if p.speed/3 <= 7*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed*1/2)
					P_SetObjectMomZ(p.mo, 7*FRACUNIT)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				elseif p.speed/3 >= 14*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed)
					P_SetObjectMomZ(p.mo, 14*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				else 
					P_InstaThrust(p.mo, p.mo.angle, p.speed*3/4)
					P_SetObjectMomZ(p.mo, p.speed/3)
					p.mo.state = S_PLAY_SPRING
					p.pflags = $ | PF_NOJUMPDAMAGE
				end
			end
		end
		P_SpawnThokMobj(p)
		S_StartSound(p.mo, sfx_thok)
		abilusedone = 1
	end
	return true
end)

addHook("JumpSpinSpecial", function(p)
	if p.mo.skin != "sonic" return end
	if p.pflags & PF_THOKKED return end
	if abilusedone == 1
	-- The Swift Stream --
		if p.powers[pw_super] == 1
			if p.mo.eflags & MFE_UNDERWATER
				if p.speed >= 48*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
				else
					P_InstaThrust(p.mo, p.mo.angle, 12*FRACUNIT + p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
				end
			else
				if p.speed >= 96*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
				else
					P_InstaThrust(p.mo, p.mo.angle, 24*FRACUNIT + p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
				end
			end
		else
			if p.mo.eflags & MFE_UNDERWATER
				if p.speed >= 24*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
				else
					P_InstaThrust(p.mo, p.mo.angle, 6*FRACUNIT + p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
				end
			else
				if p.speed >= 48*FRACUNIT
					P_InstaThrust(p.mo, p.mo.angle, p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
					S_StartSound(p.mo, sfx_zoom)
				else
					P_InstaThrust(p.mo, p.mo.angle, 12*FRACUNIT + p.speed) 
					P_SetObjectMomZ(p.mo, 0*FRACUNIT)
				end
			end
		end
		P_SpawnThokMobj(p)
		S_StartSound(p.mo, sfx_thok)
		abilusedtwo = 1
		return true
	end
end)

addHook('ThinkFrame', function(p)
	for p in players.iterate do
		if abilusedone == 1 and abilusedtwo == 1 then
			p.pflags = $ | PF_THOKKED
			abilusedone = 0
			abilusedtwo = 0
		elseif P_IsObjectOnGround(p.mo) then
			abilusedone = 0
			abilusedtwo = 0
		elseif p.powers[pw_carry] == CR_ROPEHANG
			abilusedone = 0
			abilusedtwo = 0
		end
	end
end)

-- TAILS --

-- This ability will be completed soon, so please be patient.
--local tailsfly = 0

--addHook("JumpSpinSpecial", function(p)
	--if p.mo.skin != "tails" return end
	--if tailsfly == 0
		--P_InstaThrust(p.mo, p.mo.angle, 0)
		--P_SetObjectMomZ(p.mo, 12*FRACUNIT)
		--p.mo.state = S_PLAY_FLY
		--p.powers[pw_tailsfly] = tailsflytics
		--S_StartSound(p.mo, sfx_zoom)
		--tailsfly = 1
	--end
--end)

-- METAL SONIC --

addHook("JumpSpinSpecial", function(player)
    for player in players.iterate
		if player.mo.skin != "metalsonic" return end
		if player.pflags & PF_THOKKED return end
		if player.mo.momz > 4*FRACUNIT
			if player.speed > 32*FRACUNIT
				P_InstaThrust(player.mo, player.mo.angle, player.speed)
			else
				P_InstaThrust(player.mo, player.mo.angle, player.mo.momz+player.speed)
			end
			player.mo.momz = 0
			S_StartSound(player.mo, sfx_thok)
		elseif player.mo.momz < -4*FRACUNIT
			if player.mo.momz < -16*FRACUNIT
				player.mo.momz = 24*FRACUNIT
			else
				player.mo.momz = $ * -3/2
			end
			P_InstaThrust(player.mo, player.mo.angle, player.speed)
			S_StartSound(player.mo, sfx_jump)
		else
			P_InstaThrust(player.mo, player.mo.angle, player.speed)
			player.mo.momz = 0
		end
		S_StartSound(player.mo, sfx_zoom)
		player.pflags = $ | PF_THOKKED
	end
end)

-- RUNNING DUST AND AIR ROLLING--

local airrolled = 0
local jumped = 0
local sprung = 0

addHook("ThinkFrame", do
    for p in players.iterate
        if p.speed > 48*FRACUNIT and P_IsObjectOnGround(p.mo) and p.mo.state == S_PLAY_RUN
            P_SpawnSkidDust(p, 10*FRACUNIT)
            if p.speed > 64*FRACUNIT
                P_SpawnSkidDust(p, 25/2*FRACUNIT)
                P_SpawnSkidDust(p, 25/2*FRACUNIT)
            end
            if p.speed > 80*FRACUNIT
                P_SpawnSkidDust(p, 15*FRACUNIT)
                P_SpawnSkidDust(p, 15*FRACUNIT)
                P_SpawnSkidDust(p, 15*FRACUNIT)
            end
        end
		if p.mo.state == S_PLAY_GLIDE_LANDING
			p.mo.state = S_PLAY_STND
		end
		if p.mo.state == S_PLAY_JUMP or p.mo.state == S_PLAY_ROLL
			airrolled = 1
		else
			airrolled = 0
        end
		if (p.pflags & PF_JUMPED)
			jumped = 1
		end
		if p.mo.eflags & MFE_SPRUNG then
			sprung = 1
		end
		if P_IsObjectOnGround(p.mo)
            airrolled = 0
			jumped = 0
			sprung = 0
        end
    end
end)

addHook("PlayerThink", function(p)
if (p.pflags & PF_THOKKED) and p.powers[pw_shield] & SH_WHIRLWIND return end
if p.mo.state == S_PLAY_PAIN 
or p.mo.state == S_PLAY_DEAD 
or p.mo.state == S_PLAY_DRWN 
or p.mo.state == S_PLAY_GLIDE 
or p.mo.state == S_PLAY_FLOAT 
or p.mo.state == S_PLAY_FLOAT_RUN 
or p.mo.state == S_PLAY_NIGHTS_TRANS1 
or p.mo.state == S_PLAY_NIGHTS_TRANS2 
or p.mo.state == S_PLAY_NIGHTS_TRANS3 
or p.mo.state == S_PLAY_NIGHTS_TRANS4 
or p.mo.state == S_PLAY_NIGHTS_TRANS5 
or p.mo.state == S_PLAY_NIGHTS_TRANS6 
or p.mo.state == S_PLAY_SUPER_TRANS1 
or p.mo.state == S_PLAY_SUPER_TRANS2 
or p.mo.state == S_PLAY_SUPER_TRANS3 
or p.mo.state == S_PLAY_SUPER_TRANS4 
or p.mo.state == S_PLAY_SUPER_TRANS5 
or p.mo.state == S_PLAY_SUPER_TRANS6 
or p.mo.state == S_PLAY_NIGHTS_STAND 
or p.mo.state == S_PLAY_NIGHTS_FLOAT 
or p.mo.state == S_PLAY_NIGHTS_FLY 
or p.mo.state == S_PLAY_NIGHTS_DRILL 
or p.mo.state == S_PLAY_NIGHTS_STUN 
or p.mo.state == S_PLAY_NIGHTS_PULL return end
if p.mo.skin == "knuckles" and p.mo.state == S_PLAY_SWIM return end
	if jumped == 1 and p.powers[pw_shield] == 2
		if p.charability2 == 1
			if airrolled == 0
				if not P_IsObjectOnGround(p.mo)
					if (p.cmd.buttons & BT_SPIN)
						p.mo.state = S_PLAY_ROLL
						S_StartSound(p.mo, sfx_spin)
						airrolled = 1
					end
				end
			end
		end
	else
		if p.charability2 == 1
			if airrolled == 0
				if not P_IsObjectOnGround(p.mo)
					if (p.cmd.buttons & BT_SPIN)
						p.mo.state = S_PLAY_ROLL
						S_StartSound(p.mo, sfx_spin)
						airrolled = 1
					end
				end
			end
		end
	end
    return true
end)

addHook("PlayerCanDamage", function(p, victim)
    if airrolled == 1
        if victim and victim.valid
            return true
        end
    end
end)