freeslot(
	"SPR2_SLEP", 
	"S_SPECKI_SLEEP", 
	"S_SPECKI_DANCE", 
	"S_SPECKI_PRIDE",
	"SPR2_NEAT",
	"S_PLAY_NIGHTS_EAT"
)

states[S_SPECKI_SLEEP] = {
	sprite = SPR_PLAY,
	frame = SPR2_SLEP|FF_ANIMATE,
	tics = -1,
	var1 = 1,
	var2 = 35,
	nextstate = nil
}

states[S_SPECKI_DANCE] = {
	sprite = SPR_PLAY,
	frame = SPR2_MLEE|FF_ANIMATE,
	tics = -1,
	var1 = 5,
	var2 = (35*FRACUNIT)/1000*150/FRACUNIT,
	nextstate = nil
}

states[S_SPECKI_PRIDE] = {
	sprite = SPR_PLAY,
	frame = SPR2_MLEL|FF_ANIMATE,
	tics = -1,
	var1 = 1,
	var2 = (35*FRACUNIT)/1000*150/FRACUNIT,
	nextstate = nil
}


states[S_PLAY_NIGHTS_EAT] = {
	sprite = SPR_PLAY,
	frame = SPR2_NEAT|A,
	tics = 35,
	var1 = 0,
	var2 = 0,
	nextstate = S_PLAY_NIGHTS_FLY 
}

addHook("MobjMoveBlocked", function(mo, thing, line)
	if not (mo and mo.valid) then return end
	if not mo.health then return end
	if mo.skin ~= "specki" and mo.skin ~= "mechaspecki" then return end
	if not (mo.player.pflags & PF_SPINNING) then return end

	if line and line.valid
		if FixedHypot(mo.momx, mo.momy) > 5*FRACUNIT
			if abs(line.dx) > 0
				local myang = R_PointToAngle2(0, 0, mo.momx, mo.momy)
				local vertang = R_PointToAngle2(0, 0, 0, mo.momz)
				local lineang = R_PointToAngle2(line.v1.x, line.v1.y, line.v2.x, line.v2.y)
				P_InstaThrust(mo, myang + 2*(lineang - myang), FixedHypot(mo.momx, mo.momy)-mo.friction)
			else
				mo.momx = $*-1
			end
		end
	end
end, MT_PLAYER)

local function CrouchHeight(p)
	if not (p.mo and p.mo.valid) then return end
	if not p.mo.health then return end
	if p.mo.skin ~= "specki" then return end
	local height
	
	if p.speckicrouch or (p.pflags & PF_SPINNING) then
		height = skins[p.mo.skin].height/3*2
	else
		height = skins[p.mo.skin].height
	end
	
	p.viewheight = height/FRACUNIT*p.mo.scale
	return height/FRACUNIT*p.mo.scale
end

local function CrouchGap(p)
	if not (p.mo and p.mo.valid) then return end
	if not p.mo.health then return end
	if p.mo.skin ~= "specki" then return end
	
	if p.speckicrouch then return true end
end

addHook("PlayerHeight", CrouchHeight)
addHook("PlayerCanEnterSpinGaps", CrouchGap)

rawset(_G, "speckiRoll", function(p)
    if not (p.mo and p.mo.valid) then return end
	if not p.mo.health then return end
	if p.mo.skin ~= "specki" then return end
	if p.powers[pw_carry] then return end
	if P_PlayerInPain(p) then return end
	if p.powers[pw_nocontrol] then return end
	if (p.pflags & PF_STASIS) then return end
	
	if not (p.cmd.buttons & BT_USE) then
		p.spintap = false
		p.spinready = true
	elseif p.spinready then
		p.spintap = true
		p.spinready = false
	else
		p.spintap = false
	end

    if p.spintap and not (p.pflags & PF_SPINNING) and P_IsObjectOnGround(p.mo) then
		if p.speed > 6*FRACUNIT then
			p.pflags = $|PF_SPINNING
			S_StartSound(p.mo, sfx_spin, p)
			p.mo.state = S_PLAY_ROLL
		end
	elseif p.spintap and (p.pflags & PF_SPINNING) then
		if p.speed > FRACUNIT then
			p.pflags = $ & ~PF_SPINNING
			--S_StartSound(p.mo, sfx_spin, p)
			p.mo.state = S_PLAY_WALK
		else
			P_InstaThrust(p.mo, p.mo.angle, 0)
			p.pflags = $ & ~PF_SPINNING
			--S_StartSound(p.mo, sfx_spin, p)
			p.mo.state = S_PLAY_STND
		end
	end
end)

rawset(_G, "speckiCrouch", function(p)
    if not (p.mo and p.mo.valid) then return end
	if not p.mo.health then return end
	if p.mo.skin ~= "specki" then return end
	if p.powers[pw_carry] then return end
	if P_PlayerInPain(p) then return end
	if p.powers[pw_nocontrol] then return end
	if (p.pflags & PF_STASIS) then return end
	
	if not (p.cmd.buttons & BT_USE) then
		p.spintap = false
		p.spinready = true
	elseif p.spinready then
		p.spintap = true
		p.spinready = false
	else
		p.spintap = false
	end

    if (p.cmd.buttons & BT_USE) and P_IsObjectOnGround(p.mo) then
		if p.speed > 6*FRACUNIT then
			p.mo.momx = $ * 8/10
			p.mo.momy = $ * 8/10
		end
		
		p.speckiruntimer = 0
		
		if p.mo.state == S_PLAY_WALK and p.mo.frame % 2 == 0 and (p.cmd.forwardmove ~= 0 or p.cmd.sidemove ~= 0) then
			P_InstaThrust(p.mo, R_PointToAngle2(0, 0, p.mo.momx, p.mo.momy), 8*FRACUNIT)
		end
		
		p.mo.spriteyscale = FRACUNIT/3*2
		p.speckicrouch = true
	else
		p.speckicrouch = false
	end
end)

addHook("PlayerThink", function(p)
	if not(p.mo and p.mo.valid) then return end
	if not p.mo.health then return end
	--if p.mo.skin ~= "mechaspecki" then R_SetPlayerSkin(p, "mechaspecki") end 
	
	if p.mo.skin == "mechaspecki" then
		R_SetPlayerSkin(p, "specki")
	end
	
	if p.mo.skin ~= "specki" then 
		if p.mo.state == S_SPECKI_DANCE or p.mo.state == S_SPECKI_PRIDE then p.mo.state = S_PLAY_WALK end
		return 
	end
	
	if mapheaderinfo[gamemap].lvlttl ~= "Castle Eggman" then
		speckiRoll(p)
	else
		speckiCrouch(p)
	end
	
	if (p.mo.eflags & MFE_UNDERWATER) then
		p.powers[pw_underwater] = 9999
	end
	
	if (p.mo.eflags & MFE_JUSTHITFLOOR) and (not (p.mo.eflags & MFE_UNDERWATER)) then
		if not p.speckifloortouch then
			S_StartSoundAtVolume(p.mo, sfx_s3k56, 127)
			p.mo.spritexscale = FRACUNIT/3*4
			p.mo.spriteyscale = FRACUNIT/3*2
			p.speckifloortouch = true
		end
	elseif not P_IsObjectOnGround(p.mo) then
		if p.speckifloortouch and (p.pflags & PF_JUMPED) then
			p.mo.spritexscale = FRACUNIT/3*2
			p.mo.spriteyscale = FRACUNIT/3*4
		end
		p.speckifloortouch = false
	end
	
	if P_IsObjectOnGround(p.mo) then
		p.speckifloortouch = true
	end
	
	if p.speed >= FRACUNIT and P_IsObjectOnGround(p.mo) and not (p.pflags & PF_SPINNING) then
		if p.speckiruntimer < 15*TICRATE then
			p.speckiruntimer = $ + 1
		elseif p.speckiruntimer < 36*TICRATE then
			p.speckiruntimer = $ + p.speed/FRACUNIT/TICRATE
		else
			p.speckiruntimer = $ + p.speed/FRACUNIT/TICRATE/2
		end
		
		if p.speed >= p.normalspeed then
			p.speckiruntimer = $ + 1
		end
		
		if p.powers[pw_sneakers] then
			p.speckiruntimer = $ + 1
		end
		
		if p.mo.standingslope then
			local xydirection = p.mo.standingslope.xydirection
			local ang = abs(xydirection-R_PointToAngle2(0, 0, p.rmomx, p.rmomy))
			if ang < ANGLE_90 and ang > ANGLE_270 then
				p.speckiruntimer = $ + 1
			end
		end
		
		if (p.cmd.forwardmove == 0 and p.cmd.sidemove == 0) then
			p.speckiruntimer = 0
		end
		
		P_Thrust(p.mo, R_PointToAngle2(0, 0, p.rmomx, p.rmomy), p.speckiruntimer/TICRATE * FRACUNIT)
	elseif (p.speed < FRACUNIT) then
		p.speckiruntimer = 0
	end
	
	if (p.pflags & PF_SPINNING) or not P_IsObjectOnGround(p.mo) then
		if p.speckiruntimer > p.speed/FRACUNIT/2 then
			p.speckiruntimer = $ - p.speckiruntimer / 105
		end
	end
	
	if P_PlayerInPain(p) then
		p.speckiruntimer = 0
	end
	
	P_ButteredSlope(p.mo)
	
	p.mo.spritexscale = ease.outquint(FRACUNIT/17, $, FRACUNIT)
	p.mo.spriteyscale = ease.outquint(FRACUNIT/17, $, FRACUNIT)
	
	if p.speckisleeptimer == nil then
		p.speckisleeptimer = 0
	end
		
	if p.mo.state == S_SPECKI_SLEEP then
		p.speckisleeptimer = $+1
	end
		
	if p.mo.state == S_PLAY_WAIT then
		p.mo.frame = (leveltime/6)%3
		p.speckisleeptimer = $+1
			
		if p.speckisleeptimer >= 5*TICRATE then
			p.mo.frame = 1
				
			if p.speckisleeptimer >= 5*TICRATE+8 then
				p.mo.state = S_SPECKI_SLEEP
			end
		end
	elseif p.mo.state ~= S_SPECKI_SLEEP and p.mo.state ~= S_PLAY_WAIT then
		p.speckisleeptimer = 0
	end
	
	if (p.cmd.buttons & BT_TOSSFLAG) and P_IsObjectOnGround(p.mo) and p.mo.state ~= S_SPECKI_DANCE and p.mo.state ~= S_SPECKI_PRIDE and not p.speed then
		if (p.cmd.buttons & BT_CUSTOM3) then
			p.mo.state = S_SPECKI_PRIDE
			S_StartSound(p.mo, sfx_cdpcm6)
		else
			p.mo.state = S_SPECKI_DANCE
		end
	end
	
	if p.mo.state == S_SPECKI_DANCE or p.mo.state == S_SPECKI_SLEEP or p.mo.state == S_SPECKI_PRIDE then
		if p.speed ~= 0 then
			p.mo.state = S_PLAY_STND
		elseif p.mo.state == S_SPECKI_DANCE then
			if S_MusicName(p) ~= "FRIDAY" then
				p.ospeckimusic = S_MusicName(p)
				S_ChangeMusic("FRIDAY", true, p)
			end
		end
	end
	
	if p.speckilaststate == S_SPECKI_DANCE and p.mo.state ~= S_SPECKI_DANCE then
		if string.lower(p.ospeckimusic) == "_super" and not p.powers[pw_super] then
			p.ospeckimusic = mapheaderinfo[gamemap].musname
		end
		
		S_ChangeMusic(p.ospeckimusic, true, p)
	end
	
	p.speckilaststate = p.mo.state
end)

addHook("TouchSpecial", function(ring, pmo)
	if not (pmo and pmo.valid) then return end
	if not pmo.health then return end
	if pmo.skin ~= "specki" then return end
	if ring.type ~= MT_RING and ring.type ~= MT_BLUESPHERE then return end
	if pmo.state ~= S_PLAY_NIGHTS_FLY and pmo.state ~= S_PLAY_NIGHTS_DRILL then return end
	
	pmo.state = S_PLAY_NIGHTS_EAT
end)

hud.add(function(v,p)
	if not(p.mo and p.mo.valid) then return end
	if not p.mo.health then return end
	if p.mo.skin ~= "specki" then return end
	if p.speckisleeptimer == nil then return end
	
	if p.speckisleeptimer >= 8*TICRATE then
		v.drawString(160, 64, "The specki has a nap.\n\nHold out specki!!", nil, "center")
	end
end, "game")

addHook("MobjThinker", function(metal)
	metal.skin = "mechaspecki"
end, MT_METALSONIC_RACE)

hud.add(function(v, p)
	if not (p.mo and p.mo.valid) then return end
	if p.mo.skin ~= "specki" then return end
	if mapheaderinfo[gamemap].typeoflevel & TOL_NIGHTS then return end

	if hud.enabled("lives") then
		if p.powers[pw_super] then
			v.drawString(hudinfo[HUD_LIVES].x + 18, hudinfo[HUD_LIVES].y, "Inc. Doodle", V_HUDTRANS|V_PERPLAYER|V_ALLOWLOWERCASE|hudinfo[HUD_LIVES].f)
		else
			v.drawString(hudinfo[HUD_LIVES].x + 18, hudinfo[HUD_LIVES].y, "SPECKI", V_HUDTRANS|skincolors[p.skincolor].chatcolor|V_PERPLAYER|hudinfo[HUD_LIVES].f)
		end
	end
end, "game")