
freeslot("MT_ROCKBLAST")

local state_digging = 1
local state_drilldive = 2
local state_burrowed = 3
local state_rising = 4
local setburrowtime = TICRATE*3 //Time in tics before the player can move after burrowing
local zthreshold = 8 //Z Distance from ground (in fracunits) that will cause Knuckles to resurface

mobjinfo[MT_ROCKBLAST] = {
	spawnstate = S_ROCKCRUMBLEA,
	spawnhealth = 1000,
	radius = 8*FRACUNIT,
	height = 16*FRACUNIT,
	mass = 0,
	damage = 0,
	flags = MF_BOUNCE|MF_GRENADEBOUNCE
}

local function KnuxDig(player)
	local mo = player.mo
	
	local s = player.skinvars
	
	if not (mo and mo.valid and mo.skin=="s3knuckles")
		return end
		
		if player.buttonhistory == nil
		player.buttonhistory = player.cmd.buttons
		end
		
		local ButtonCheck = function (player,button)
	if player.cmd.buttons&button then
		if player.buttonhistory&button then
			return 2
		else
			return 1
		end
	end
	return 0
end

local PlayerButtonPressed = function(player,button,held,check_stasis)
	if player.Exiting then return end
	if not(player.cmd.buttons&button) then return false end
	if held == true and not(player.buttonhistory&button) then return false end
	if held == false and player.buttonhistory&button then return false end
	if (player.mo.state >= S_PLAY_SUPER_TRANS1 and player.mo.state <= S_PLAY_SUPER_TRANS6) then return false end
	if(check_stasis) and player.powers[pw_nocontrol] then return false end
	return true
end

local ResetPlayerProperties  = function (player,jumped,thokked)
	local mo = player.mo
	if not(mo) then return end
	if mo.eflags&MFE_SPRUNG then
		player.spritexscale = FRACUNIT
		player.spriteyscale = FRACUNIT
		player.mo.state = S_PLAY_SPRING
	end
	//Reset pflags
	local pflags = player.pflags&~(PF_GLIDING|PF_BOUNCING)
	if jumped == true then
		pflags = $|PF_JUMPED&~PF_STARTJUMP
		if player.charflags&SF_NOJUMPDAMAGE
			pflags = $|PF_NOJUMPDAMAGE
		end
		if not(player.charflags&SF_NOJUMPSPIN)
			mo.state = S_PLAY_ROLL
		else
			mo.state = S_PLAY_SPRING
		end
	elseif jumped == false then
		pflags = $&~(PF_JUMPED|PF_SPINNING)
		if not(P_IsObjectOnGround(mo)) and not(P_PlayerInPain(player)) then
			mo.state = S_PLAY_FALL
		end
	end
	if thokked == true then
		pflags = ($|PF_THOKKED)&~PF_SHIELDABILITY
	elseif thokked == false then
		pflags = $&~(PF_THOKKED|PF_SHIELDABILITY)
	end
	player.pflags = pflags
	//Reset other variables
	local skin = skins[mo.skin]
	mo.flags = $&~(MF_NOCLIPTHING)
	mo.flags2 = $&~(MF2_DONTDRAW)
	player.charability = skin.ability
	player.charability2 = skin.ability2
	player.actionspd = skin.actionspd
	player.normalspeed = skin.normalspeed
	player.thrustfactor = skin.thrustfactor
	player.mindash = skin.mindash
	player.maxdash = skin.maxdash
	player.shieldscale = skin.shieldscale
	P_SpawnShieldOrb(player)
	player.height = skin.height
	player.climbing = 0
	player.secondjump = 0
	player.actionstate = 0
	player.actiontime = 0
	player.exhaustmeter = 0
end

local InstaThrustZAim = function (mo,xyangle,zangle,speed,relative)
	local xythrust = P_ReturnThrustX(nil,zangle,speed)
	local zthrust = P_ReturnThrustY(nil,zangle,speed)
	if relative then
		P_Thrust(mo,xyangle,xythrust)		
		mo.momz = $+zthrust	
	else
		P_InstaThrust(mo,xyangle,xythrust)		
		mo.momz = zthrust	
	end
	return {xythrust/FRACUNIT,zthrust/FRACUNIT}
end

local InstaThrustSpread = function(mo,aim,spread,angle,speed)
	local xyangle = (angle-90)*ANG1*2
	local zangle = angle*ANG1*2
	local xymul = FRACUNIT/180*spread
	local zmul = FRACUNIT/180*spread
	if sin(angle*ANG1) < 0 then zmul = $*-1 end
	if cos(angle*2*ANG1) < 0 then xymul = -1*zmul end
	xyangle = FixedMul($,xymul)
	zangle = FixedMul(abs($),zmul)
	InstaThrustZAim(mo,xyangle+aim,zangle,speed)
end

local ZLaunch = function (mo,thrust,relative)
	if mo.eflags&MFE_UNDERWATER
		thrust = $*3/5
	end
	P_SetObjectMomZ(mo,thrust,relative)
end

local shootrock_grounded = function(mo,ang1,ang2,scale,rockblasttime)
	local rock = P_SPMAngle(mo,MT_ROCKBLAST,0,0)
	if rock and rock.valid then
		InstaThrustZAim(rock,ang1,ang2*P_MobjFlip(rock),scale)
		rock.fuse = rockblasttime
		//rock_properties(rock,rockblasttime)
	end
end

local shootrock_sidewall = function(mo,ang1,ang2,scale,rockblasttime)
	local rock = P_SPMAngle(mo,MT_ROCKBLAST,0,0)
	if rock and rock.valid then
		InstaThrustSpread(rock,mo.angle,ang1,ang2,scale)
		rock.fuse = rockblasttime
		//rock_properties(rock,rockblasttime)
	end
end


local rockblast = function(mo,grounded)
	if grounded then
		//Do horizontal debris burst
		local m = 6
		for n = 0,m
			shootrock_grounded(mo,(360/m)*n*ANG1,2*ANG20,mo.scale*12,TICRATE)
		end
	else
		//Do vertical debris burst
		for n = 0,7
			shootrock_sidewall(mo,30,45*n,mo.scale*20,TICRATE)
		end
	end
end
		
	if player.actionstate == nil
		player.actionstate = 0
	end
	
	if player.actiontime == nil
		player.actiontime = 0
	end
	
	if player.exhaustmeter == nil
		player.exhaustmeter = 0
	end
	
	if player.knuxdigcooldown == nil
		player.knuxdigcooldown = 0
	end
	
	if player.knuxdiginputcooldown == nil
		player.knuxdiginputcooldown = 0
	end
	
	if player.knuxdigcooldown > 0
	player.knuxdigcooldown = $ - 1
	return end
	
	if player.knuxdiginputcooldown > 0
	player.knuxdiginputcooldown = $ - 1
	end

	if P_PlayerInPain(player)
	or player.playerstate != PST_LIVE
	or (player.actionstate == state_drilldive and player.powers[pw_nocontrol])
	or (mo.state == S_PLAY_GLIDE or mo.state == S_PLAY_SWIM)
		if player.actionstate
			if mo.state == S_PLAY_GLIDE or mo.state == S_PLAY_SWIM
				ResetPlayerProperties(player,true,true)
				player.pflags = $ | PF_GLIDING|PF_THOKKED
				if mo.eflags&MFE_UNDERWATER
					mo.state = S_PLAY_SWIM
				else
					mo.state = S_PLAY_GLIDE
				end
			else
				ResetPlayerProperties(player,false,false)
			end
		end
	return end

	local dojump = PlayerButtonPressed(player,BT_JUMP,false)
	local dospin = PlayerButtonPressed(player,BT_USE,false)
	local climbing = player.climbing
	local sludge = mo.eflags&MFE_GOOWATER
	local grounded = P_IsObjectOnGround(mo)
	local diggingstates = (player.actionstate == state_digging or player.actionstate == state_burrowed)
	local getcanceltics = player.actiontime
	local floordist
	if P_MobjFlip(mo) == 1 then
		floordist = mo.z-mo.floorz
	else
		floordist = mo.ceilingz-mo.height-mo.z
	end
	local nearground = (floordist < zthreshold*mo.scale)
	
	//Action triggers
	local trigger_dig =
		(PlayerButtonPressed(player,BT_CUSTOM1,false) and not(sludge) and player.actionstate == 0 and player.knuxdiginputcooldown == 0 and (grounded or climbing)) and not (player.climbing and PlayerButtonPressed(player,BT_JUMP,false) and not mo.state == S_PLAY_KNUXUPPERCUT)
		or (grounded and player.actionstate == state_drilldive and not(sludge))
	local trigger_drilldive = (not(grounded and not sludge) and PlayerButtonPressed(player,BT_CUSTOM1,false) and player.actionstate == 0 and player.knuxdiginputcooldown == 0) and not (player.climbing and PlayerButtonPressed(player,BT_JUMP,false) and not mo.state == S_PLAY_KNUXUPPERCUT)
	local trigger_drilldive_cancel = (player.actionstate == state_drilldive and mo.momz*P_MobjFlip(mo) > 0)
	local trigger_eject =
		(player.actiontime == setburrowtime)
		or (grounded and sludge)
		or (player.actionstate == state_burrowed and dospin)
		or player.exiting
		or not(nearground or climbing)	
	
	
	//Execute dig
	if trigger_dig
		player.knuxdiginputcooldown = 10
		player.actionstate = state_digging
		player.actiontime = 0
		player.exhaustmeter = min(FRACUNIT,$+FRACUNIT/4)
		mo.flags = $|MF_NOCLIPTHING
		mo.flags2 = $|MF2_DONTDRAW
		if P_IsObjectOnGround(mo)
		player.height = 1
		end
		player.pflags = ($|PF_JUMPSTASIS) & ~PF_SPINNING
		//player.normalspeed = skins[mo.skin].normalspeed*3/5
		player.shieldscale = 0
		S_StartSound(mo,sfx_s3kccs)
		player.canguard = 0
		rockblast(mo,grounded)
		if mo.state == S_PLAY_SPINDASH
			mo.state = S_PLAY_STND
		end
		return
	end
	
	//Execute downward air drill
	if trigger_drilldive
		player.knuxdiginputcooldown = 10
		player.actionstate = state_drilldive
		mo.state = S_PLAY_KNUXDIVING
		local dir = R_PointToAngle2(0,0,mo.momx,mo.momy)
		local speed = FixedHypot(mo.momx,mo.momy)
		InstaThrustZAim(mo,dir,-ANGLE_90,min(speed,mo.scale*36))
		mo.momz = min(-12*mo.scale,$*P_MobjFlip(mo))*P_MobjFlip(mo)
		player.pflags = $|(PF_JUMPED|PF_THOKKED)&~(PF_GLIDING)
		S_StartSound(mo,sfx_zoom)
		return
	end
	
	//Drill dive
	if player.actionstate == state_drilldive then
		player.actiontime = $+1
		//DrawSVSprite(player,1+player.actiontime%4)
		P_SpawnGhostMobj(mo)
		
		if ButtonCheck(player, BT_JUMP) == 1 and player.exhaustmeter
			ResetPlayerProperties(player,true,false)
			
			mo.momz = $ / 3
			//P_SetObjectMomZ(mo, 0)
			
			local glidespeed = FixedMul(player.actionspd, mo.scale)
			local playerspeed = player.speed

			if (mo.eflags & MFE_UNDERWATER)
				glidespeed = $ >> 1
				playerspeed = 2*playerspeed/3
				//if (!(player.powers[pw_super] or player.powers[pw_sneakers]))
					//player.mo.momx = (2*(player.mo.momx - player.cmomx)/3) + player.cmomx
					//player.mo.momy = (2*(player.mo.momy - player.cmomy)/3) + player.cmomy
				//end
			end
			
			player.pflags = $ | PF_GLIDING|PF_THOKKED
			player.glidetime = 0

			if mo.eflags&MFE_UNDERWATER
				mo.state = S_PLAY_SWIM
			else
				mo.state = S_PLAY_GLIDE
			end
			if (playerspeed < glidespeed)
				P_Thrust(mo, mo.angle, glidespeed - playerspeed)
			end
			player.pflags = $ & ~(PF_SPINNING|PF_STARTDASH)
		end
	end
	//Cancel drill dive
	if trigger_drilldive_cancel then
		mo.state = S_PLAY_FALL
		ResetPlayerProperties(player,false,false)
	end
	
	//Drill rise
	if player.actionstate == state_rising
		player.actiontime = $+1
		//DrawSVSprite(player,(player.actiontime/2)%4+5)
		//End rising state
		if P_MobjFlip(mo)*mo.momz <= 0 or P_IsObjectOnGround(mo)
			ResetPlayerProperties(player,true,false)
			player.exhaustmeter = FRACUNIT / 3 + 1
		end
	end
	
	//Gate: Digging states only
	if not(diggingstates)
		then return 
	end
	
	//Eject player from burrow state
	if trigger_eject then
		P_SetObjectMomZ(mo,FixedMul(FRACUNIT*6,player.jumpfactor),false)
		ResetPlayerProperties(player,false,false)
		mo.state = S_PLAY_FALL
		S_StartSound(mo,sfx_s3k82)
		player.powers[pw_nocontrol] = 2
		player.actiontime = 0
		player.knuxdigcooldown = TICRATE
		rockblast(mo,grounded)
		return
	end
	
	//Burrowed properties
	mo.flags = $|MF_NOCLIPTHING //Intangibility
	mo.flags2 = $|MF2_DONTDRAW //Invisibility
	player.charability2 = 0 //Disallow spindashing
	
	//Clip to ground
	if not(climbing) then
		if P_MobjFlip(mo) == 1 then
			mo.z = mo.floorz
		else
			mo.z = mo.ceilingz-mo.height
		end
	end
	
	//Timers
	player.actiontime = $+1
	if player.exhaustmeter and P_IsObjectOnGround(mo)
		player.exhaustmeter = max(0,$-FRACUNIT/TICRATE/3)
	end
	
	//Smoke
	if (player.exhaustmeter > FRACUNIT/3 and player.actiontime%TICRATE == 0)
	or (player.exhaustmeter <= FRACUNIT/3 and player.actiontime%(TICRATE/3) == 0)
		local smoke = P_SpawnMobj(mo.x,mo.y,mo.z,MT_SMOKE)
	end
	
	//Digging state
	if player.actionstate == state_digging
		player.pflags = $|PF_JUMPSTASIS//|PF_STASIS
		if player.actiontime >= setburrowtime
			player.actionstate = state_burrowed
		end
		//return
	end
	player.pflags = ($|PF_JUMPSTASIS)//&~PF_STASIS
	player.powers[pw_flashing] = 2

	//Moving while burrowed
	if abs(player.cmd.forwardmove) or abs(player.cmd.sidemove) then
		if not(player.actiontime&3) then
			//Do Debris
			S_StartSound(mo,sfx_s3k67)
		end
	end
	
	grounded = P_IsObjectOnGround(player.mo)
	//Rock blast attack
	if (PlayerButtonPressed(player,BT_CUSTOM1,false) and player.knuxdiginputcooldown == 0)
		then
		player.exhaustmeter = 0
		player.knuxdigcooldown = TICRATE
		ResetPlayerProperties(player,true,true)
		if grounded
			player.actionstate = state_rising
			//DrawSVSprite(player,5)
		end
		player.pflags = $|PF_STARTJUMP|PF_JUMPDOWN
		ZLaunch(mo, FRACUNIT*10, true)
		ResetPlayerProperties(player,true,true)
		rockblast(mo,grounded)
		S_StartSound(mo,sfx_s3k82)
	end
end

addHook("PlayerThink", KnuxDig)

addHook("MobjThinker", function(mo)
if not (mo.target != nil and mo.target.valid and mo.target and mo.target.player and mo.target.skin == "s3knuckles") return end

if mo.target.player.actionstate != nil and mo.target.player.actionstate == state_digging
	P_RemoveMobj(mo)
end

end, MT_GHOST)

addHook("PlayerSpawn", function(player)
	if not (player.mo and player.mo.valid) return end
	local mo = player.mo
	
	local ResetPlayerProperties  = function (player,jumped,thokked)
	local mo = player.mo
	if not(mo) then return end
	if mo.eflags&MFE_SPRUNG then
		player.spritexscale = FRACUNIT
		player.spriteyscale = FRACUNIT
		player.mo.state = S_PLAY_SPRING
	end
	//Reset pflags
	local pflags = player.pflags&~(PF_GLIDING|PF_BOUNCING)
	if jumped == true then
		pflags = $|PF_JUMPED&~PF_STARTJUMP
		if player.charflags&SF_NOJUMPDAMAGE
			pflags = $|PF_NOJUMPDAMAGE
		end
		if not(player.charflags&SF_NOJUMPSPIN)
			mo.state = S_PLAY_ROLL
		else
			mo.state = S_PLAY_SPRING
		end
	elseif jumped == false then
		pflags = $&~(PF_JUMPED|PF_SPINNING)
		if not(P_IsObjectOnGround(mo)) and not(P_PlayerInPain(player)) then
			mo.state = S_PLAY_FALL
		end
	end
	if thokked == true then
		pflags = ($|PF_THOKKED)&~PF_SHIELDABILITY
	elseif thokked == false then
		pflags = $&~(PF_THOKKED|PF_SHIELDABILITY)
	end
	player.pflags = pflags
	//Reset other variables
	local skin = skins[mo.skin]
	mo.flags = $&~(MF_NOCLIPTHING)
	mo.flags2 = $&~(MF2_DONTDRAW)
	player.charability = skin.ability
	player.charability2 = skin.ability2
	player.actionspd = skin.actionspd
	player.normalspeed = skin.normalspeed
	player.thrustfactor = skin.thrustfactor
	player.mindash = skin.mindash
	player.maxdash = skin.maxdash
	player.shieldscale = skin.shieldscale
	P_SpawnShieldOrb(player)
	player.height = skin.height
	player.climbing = 0
	player.secondjump = 0
	player.actionstate = 0
	player.actiontime = 0
	player.exhaustmeter = 0
end

	if player.actionstate
		ResetPlayerProperties(player,false,false)
	end
	
end)