//hey! thanks for stopping by to read this. i've been working on this for the better part of a month.
//it's completely okay if you lift some lines of code for your own projects, go crazy with it, but
//please remember to credit me, whether in the description of the message board post, or in the
//code comments themselves.
//
//SilverVortex, 03/01/2021 (holy shit it's been a long time)

freeslot(
	"S_BOOST1",
	"S_BOOST2",
	"S_BOOST3",
	"S_BOOST4",
	"S_BOOST5",
	"SPR_BSTD",
	"MT_BOOST"
)

mobjinfo[MT_BOOST] = {
	doomednum = -1,
	spawnstate = S_BOOST1,
	flags = MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_NOCLIPHEIGHT
}

states[S_BOOST1] = {
	sprite = SPR_BSTD,
	frame = TR_TRANS40|FF_PAPERSPRITE|A,
	tics = 10,
	next = S_BOOST2
}

states[S_BOOST2] = {
	sprite = SPR_BSTD,
	frame = TR_TRANS30|FF_PAPERSPRITE|A,
	tics = 10,
	next = S_BOOST3
}

states[S_BOOST3] = {
	sprite = SPR_BSTD,
	frame = TR_TRANS20|FF_PAPERSPRITE|A,
	tics = 10,
	next = S_BOOST4
}

states[S_BOOST4] = {
	sprite = SPR_BSTD,
	frame = TR_TRANS10|FF_PAPERSPRITE|A,
	tics = 10,
	next = S_BOOST5
}

states[S_BOOST5] = {
	sprite = SPR_BSTD,
	frame = TR_TRANS10|FF_PAPERSPRITE|A,
	tics = 10,
	next = S_NULL
}

/////////////
//Sonic's abilities
/////////////

//welldonesnake coming in clutch for something that i never ended up using. still cool to have though, so i'm keeping it

//wds if you're reading this you're actually fucking awesome and please take a break from developing riders
addHook("PlayerThink", function(p)
	local abilitychoice
    local mo = p.mo
    local button = p.cmd.buttons -- Gonna shorten this

	if mo.skin == "sonic"
		if not (p.buttonpressed) -- You are not pressing a button?
			if button & BT_CUSTOM1

				-- Toggle between the two colours when this block is run
				if abilitychoice
					abilitychoice = false

				else -- Were you red?
					abilitychoice = true
				end

				p.buttonpressed = true -- You pressed a button
			end

		else -- You are pressing a button?
			if not (button & BT_CUSTOM1) -- No longer holding onto the jump button?
				p.buttonpressed = false -- Then you're not pressing a button
			end
		end
	end
	
	if p.homingatk == nil
		p.homingatk = false
		p.canhome = false
		p.weakthok = false
		p.boosting = false
	end
	if P_IsObjectOnGround(p.mo)
		p.homingatk = false
		p.canhome = false
		p.weakthok = false
		p.boosting = false
	end
end)

local function homingattack(p) //allow the player to constantly pursue their target after a jump press
	local mo = p.mo
	
	if p.mo.skin == "sonic"
	and mo.target and mo.target.valid and mo.target.health //make sure we have a target
		if p.homingatk //need PF_THOKKED to start zero-ing in
			local zdist = (P_MobjFlip(mo) == 1) and (mo.target.z + mo.target.height) - (mo.z + mo.height) or (mo.target.z - mo.z) //variables for distance and stuff!
			local dist = P_AproxDistance(P_AproxDistance(mo.target.x - mo.x, mo.target.y - mo.y), zdist)
			local angle = R_PointToAngle2(mo.x, mo.y, mo.target.x, mo.target.y)
			local zangle = R_PointToAngle2(0, mo.z, P_AproxDistance(mo.x - mo.target.x, mo.y - mo.target.y), mo.target.z)
			local maxspeed = ((p.powers[pw_super] or p.powers[pw_sneakers] or MODID == 14) and 2 or 1)*FixedMul(p.normalspeed, mo.scale)
			local x = FixedMul(maxspeed, FixedMul(cos(angle), cos(zangle)))
			local y = FixedMul(maxspeed, FixedMul(sin(angle), cos(zangle)))
			local z = FixedMul(maxspeed, sin(zangle))
			
			//listen buddy i like special effects in my sonic the hedgehog videogame
			local thokeffect = P_SpawnMobjFromMobj(p.mo,0,0,0,MT_THOK)
			thokeffect.scale = FRACUNIT
			thokeffect.destscale = FRACUNIT/20
			thokeffect.color = p.mo.color
			
			//lol i remember i didn't have this one here and i hit a target while in the falling state as super sonic lolllll
			p.mo.state = S_PLAY_JUMP
			
			p.drawangle = R_PointToAngle2(mo.x, mo.y, mo.target.x, mo.target.y)
			
			if not p.powers[pw_super]
				mo.momx = FixedMul(FixedDiv(mo.target.x-mo.x, dist), 55*FRACUNIT)
				mo.momy = FixedMul(FixedDiv(mo.target.y-mo.y, dist), 55*FRACUNIT)
				mo.momz = FixedMul(FixedDiv(zdist, dist), 55*FRACUNIT)
			end
			
			//momentum now works the same when you're super sonic lol
			if p.powers[pw_super]
				mo.momx = FixedMul(FixedDiv(mo.target.x-mo.x, dist), 100*FRACUNIT)
				mo.momy = FixedMul(FixedDiv(mo.target.y-mo.y, dist), 100*FRACUNIT)
				mo.momz = FixedMul(FixedDiv(zdist, dist), 100*FRACUNIT)
			end
		end
	end
end
addHook("PlayerThink",homingattack) //needs to be active for every frame, but only starts when we hit jump while we have a target in range.

addHook("AbilitySpecial",function(p)
	local mo = p.mo
	local angle = mo.angle

	if mo and mo.skin == "sonic"
		if not p.homingatk
			if p.pflags & PF_JUMPED and not mo.target
				mo.target = P_LookForEnemies(p,true,true)
			end
			if mo.target
				p.homingatk = true
				S_StartSound(mo,sfx_thok)
				homingattack(p) //execute your homing attack
				return true
			else
				if p.weakthok == false
					local weakthokeffect = P_SpawnMobjFromMobj(p.mo,0,0,0,MT_THOK)
					weakthokeffect.scale = FRACUNIT
					weakthokeffect.destscale = FRACUNIT*6
					weakthokeffect.color = p.mo.color
					S_StartSound(mo,sfx_thok)
					p.weakthok = true
					if not p.powers[pw_super]
						if p.speed < 50*FRACUNIT
							P_InstaThrust(mo,angle,40*FRACUNIT)
							return true
						else
							P_InstaThrust(mo,angle,p.speed)
							return true
						end
					else
						P_InstaThrust(mo,angle,90*FRACUNIT)
						return true
					end
				end
			end
		end
	end
end)

addHook("PlayerThink",function(p)
	local mo = p.mo
	mo.target = P_LookForEnemies(p,true,true)
	
	//target availability check for the reticle arrow
	if mo.target and mo.target.valid and mo.target.health
	and mo.skin == "sonic"
	and p.pflags & PF_JUMPED
		P_SpawnLockOn(p,mo.target,S_LOCKON1) //the fact that this isn't highlighted makes me nervous that i'm getting one of the arguments wrong lol
	end
	
	if (p.charflags & SF_DASHMODE)
		if p.powers[pw_carry]
		or p.mo.state == S_PLAY_PAIN
		or p.playerstate == PST_DEAD
		or p.exiting
			p.wasmetdashing = false
		end
		if not P_IsObjectOnGround(p.mo)
			if p.wasmetdashing == true
				p.dashmode = max(p.dashmode, 3*36)
			end
		end
	end
	
//anti-airwalk code
	if mo.skin ~= "metalsonic"
		if mo.state == S_PLAY_STND or mo.state == S_PLAY_WAIT or mo.state == S_PLAY_WALK or mo.state == S_PLAY_RUN
			if mo.momz > 0
				mo.state = S_PLAY_SPRING //use the spring animation if we're going up
			elseif mo.momz < 0
				mo.state = S_PLAY_FALL //use the fall animation if we're going down
			end
		else return end //otherwise just do normal shit lol
	end
	
	//cool jump visual effect
	if (p.pflags & PF_JUMPED) and not p.lastticjumped
		P_SpawnGhostMobj(p.mo)
	end
	p.lastticjumped = p.pflags & PF_JUMPED
end)

//for some reason knuckles' code won't work without me putting it in a separate PlayerThink hook so...here it is...
addHook("PlayerThink",function(p)
		
	//=================================================
	//knuckles' various code things (some things lifted from CrossMomentum, like the removal of the momentum cut)
	//=================================================
	local mo = p.mo
	local orientation = 1
	local scale = p.mo.scale //Normalspeed calculations are affected by scale
	local friction = FixedDiv(p.mo.friction,p.mo.movefactor) //Reversing friction is required for sustaining ground momentum
	local pmom = FixedDiv(FixedHypot(p.rmomx,p.rmomy),scale) //Current speed, scaled for normalspeed calculations
	local pwup = (p.powers[pw_sneakers]) or (p.powers[pw_super]) //Speed-ups multiply max allowed speed by 5/3
	local momangle = R_PointToAngle2(0,0,p.rmomx,p.rmomy) //Used for angling new momentum in ability cases
	
	//variable multiplier to autoreverse momentum when flipped
	if (p.mo.eflags & MFE_VERTICALFLIP)
		orientation = -1
	else
		orientation = 1
	end
	
	
	if p.mo.skin == "knuckles"
		if p.pflags & PF_GLIDING
			P_Thrust(p.mo,p.mo.angle,FRACUNIT/4) //gradually gain speed while gliding
			if p.powers[pw_super] and p.cmd.buttons & ~BT_SPIN
				P_SetObjectMomZ(p.mo,0,false)
			end
		end
		if p.powers[pw_super]
			p.charflags = $|SF_MULTIABILITY
		else
			p.charflags = $ & ~SF_MULTIABILITY
		end
		
		//ladies and gents, the dive boost
		
		if p.cmd.buttons & BT_SPIN //spspspin to win
			if p.pflags & PF_GLIDING // listen bud ya gotta be gliding
			and not P_IsObjectOnGround(p.mo) //for some reason you can also glide on the ground so this is here too
				p.mo.state = S_PLAY_ROLL //start rolling
//				p.mo.state = S_PLAY_NIGHTS_DRILL //<----the source of many laughs right here
				local drilleffect = P_SpawnMobjFromMobj(p.mo,0,0,0,MT_THOK)
				drilleffect.fuse = 6
				drilleffect.color = p.mo.color
				
				if not p.powers[pw_super]
					p.mo.momz = $-(FRACUNIT*2)*orientation //start descending
				end
				if p.powers[pw_super]
					p.mo.momz = $-40*FRACUNIT*orientation //start descending (but super!)
				end
				P_Thrust(p.mo,p.mo.angle,FRACUNIT/(3/2))
			end
		elseif p.cmd.buttons & ~BT_SPIN and p.pflags & PF_GLIDING and not P_IsObjectOnGround(p.mo)
			p.mo.state = S_PLAY_GLIDE
		end
	end
	
	//behaviour for when you hit the floor while boosting
	
	if p.pflags & PF_GLIDING
	and p.mo.eflags & MFE_JUSTHITFLOOR
	and p.cmd.buttons & BT_SPIN
		if not p.powers[pw_super]
			p.mo.momz = (13/2)*FRACUNIT*orientation
		else
			p.mo.momz = 9*FRACUNIT*orientation
			P_Earthquake(mo,mo,500*FRACUNIT)
		end
		p.pflags = $ & ~PF_GLIDING & ~PF_THOKKED|PF_SPINNING
		p.mo.state = S_PLAY_ROLL
//		p.mo.state = S_PLAY_NIGHTS_DRILL
		S_StartSound(p.mo,sfx_pstop)
		P_SpawnThokMobj(p)
	elseif p.mo.eflags & MFE_JUSTHITFLOOR
	and p.cmd.buttons & BT_SPIN
	and p.pflags & ~PF_GLIDING
	and p.mo.state == S_PLAY_ROLL
		S_StartSound(p.mo,sfx_spin)
		return
	end

	
	//stuff from xmomentum to stop the glide momentum cut
	if p.charability == CA_GLIDEANDCLIMB then
		//Create glide history
		if p.glidelast == nil then
			p.glidelast = 0
		end
		local gliding = p.pflags&PF_GLIDING
		local thokked = p.pflags&PF_THOKKED
		local exitglide = (p.glidelast == 1 and not(gliding) and thokked)
		local landglide = (p.glidelast == 2 and not(gliding|thokked))
		//Restore glide momentum after deactivation
		if exitglide or landglide then
			p.mo.momx = FixedMul(P_ReturnThrustX(nil,momangle,pmom),scale)
			p.mo.momy = FixedMul(P_ReturnThrustY(nil,momangle,pmom),scale)
		end
		//Update glide history
		if gliding then
			p.glidelast = 1 //Gliding
		elseif exitglide then
			p.glidelast = 2 //Falling from glide
		elseif not(gliding|thokked) then
			p.glidelast = 0 //Not in glide state
		end
	end
	
	if P_IsObjectOnGround(p.mo)
		p.exhausted = false
	end
	
	if p.flighttimer == nil
		p.flighttimer = 14
	end
	
	//HOLY SHIT I DID PROPER FLIGHT-CANCEL
	if mo.skin == "tails"
		if p.pflags & PF_THOKKED
			p.powers[pw_tailsfly] = p.flighttimer
			if not p.powers[pw_super] and p.cmd.buttons & BT_JUMP and p.jumpbuf == 1
			or p.cmd.buttons & BT_SPIN and p.spinbuf == 1
				p.flighttimer = $-1
			end
			if p.flighttimer < 0
				p.powers[pw_tailsfly] = 0
			end
		end
	end
	
	if P_IsObjectOnGround(mo)
		p.flighttimer = 14
	end
	
	if p.spinbuf == nil
		p.spinbuf = 0
	end
	
	if p.cmd.buttons & BT_SPIN
		p.spinbuf = $+1
	else
		p.spinbuf = 0
	end
	
//	print (p.spinbuf)

	if p.shotcooldown == nil
		p.shotcooldown = 0
	end
	if p.shotcooldown > 0
		p.shotcooldown = $-1
	end
	
//	print (p.shotcooldown)
	
	if p.mo.skin == "fang"
		
		if p.mo.state != S_PLAY_FIRE or p.mo.state != S_PLAY_FIRE_FINISH
			p.guntarget = P_LookForEnemies(p,false,true)
		end
		
		if p.guntarget
		and p.guntarget.valid
		and p.guntarget.health
			
			p.gunningangle = R_PointToAngle2(p.mo.x,p.mo.y,p.guntarget.x,p.guntarget.y)
			if p.mo.state == S_PLAY_WALK
			or p.mo.state == S_PLAY_RUN
			or p.mo.state == S_PLAY_STND
			or p.mo.state == S_PLAY_WAIT
			and P_IsObjectOnGround(p.mo)
				P_SpawnLockOn(p,p.guntarget,S_LOCKON1)
			end
		end
		if p.mo.state == S_PLAY_WALK
		or p.mo.state == S_PLAY_RUN
		or p.mo.state == S_PLAY_STND
		or p.mo.state == S_PLAY_WAIT
			if p.cmd.buttons & BT_SPIN
			and p.spinbuf == 1
			and p.shotcooldown == 0
			and p.spindown == true
				if p.guntarget and p.guntarget.valid
					P_SpawnMissile(p.mo,p.guntarget,MT_CORK)
					if not p.powers[pw_super]
						p.shotcooldown = 35
					else
						p.shotcooldown = 15
					end
				else
					P_SPMAngle(p.mo,MT_CORK,p.mo.angle)
					if not p.powers[pw_super]
						p.shotcooldown = 35
					else
						p.shotcooldown = 15
					end
				end
				if p.mo.state != S_PLAY_FIRE or p.mo.state != S_PLAY_FIRE_FINISH
					p.mo.state = S_PLAY_FIRE
					S_StartSound(p.mo, sfx_corkp)
					if not p.powers[pw_super]
						p.mo.momx = $*1/2
						p.mo.momy = $*1/2
					end
//					p.mo.momz = 3*FRACUNIT
					if p.guntarget
						p.mo.angle = p.gunningangle
						p.drawangle = p.gunningangle
					end
				end
			end
		end
		if p.spindown == nil
			p.spindown = false
		end
		if p.cmd.buttons & BT_SPIN
			p.spindown = false
		else
			p.spindown = true
		end
	end
	
	if p.bombdelay == nil
		p.bombdelay = 0
	end
	if p.bombdelay > 0
		p.bombdelay = $-1
	end
	
	if p.flightcancelled == nil
		p.flightcancelled = false
	end
	if P_IsObjectOnGround(p.mo)
		p.flightcancelled = false
	end
	
	if p.candoublejump == nil
		p.candoublejump = true
		p.canceltimer = 0
	end
	
	if P_IsObjectOnGround(p.mo)
		p.candoublejump = true
	end
	
	if p.canceltimer > 0
		p.canceltimer = $ - 1
	end
	
	local mo = p.mo

	
	if p.actiontime == nil
		p.actiontime = 0
	end
	if P_IsObjectOnGround(p.mo)
		p.actiontime = 0
	end
	
	if p.jumpbuf == nil
		p.jumpbuf = 0
	end
	if p.cmd.buttons & BT_JUMP
		p.jumpbuf = $+1
	else
		p.jumpbuf = 0
	end
	
	if p.cancelactivationtimer == nil
		p.cancelactivationtimer = 0
	end
	if p.cancelactivationtimer > 0
		p.cancelactivationtimer = $-1
	end
	
	if p.cmd.buttons & BT_JUMP and p.pflags & PF_THOKKED and p.mo.skin == "tails"
		p.cancelactivationtimer = 5
	end
	
	if p.dropdashenabled == nil
		p.dropdashenabled = "off"
	end
	if p.dropdashready == nil
		p.dropdashready = false
	end
	
//	print (p.cancelactivationtimer)
	
//	print (p.candoublejump)

	
	//====================
	//Scrapped Metal Sonic Drop Dash: Activated with the command "metdrop"
	//====================
	
	if p.dropdashenabled == "on"
		if (mo and mo.valid and mo.skin == "metalsonic")
		and (p.pflags & PF_JUMPED)
		and p.cmd.buttons & BT_SPIN
//			print ("we're ready to drop dash!")
			P_SpawnThokMobj(p)
			mo.state = S_PLAY_ROLL
			p.pflags = $ & ~PF_THOKKED | PF_SPINNING
			p.dropdashready = true
		elseif (mo and mo.valid and mo.skin == "metalsonic")
		and P_IsObjectOnGround(p.mo)
		and p.cmd.buttons & BT_SPIN
		and p.dropdashready == true
			P_Thrust(mo,mo.angle,40*FRACUNIT)
			S_StartSound(mo, sfx_zoom)
			p.pflags = $ | PF_SPINNING
			mo.state = S_PLAY_ROLL
//			print ("we're drop dashing")
			p.dropdashready = false
		end
	end
end)
addHook("PlayerThink", function(p)
//flight cancel + letting go of other players when flight is cancelled

//cancel flight into a falling state
	local mo = p.mo
	local orientation = 1

	if mo.eflags & MFE_VERTICALFLIP
		orientation = -1
	else
		orientation = 1
	end
	if mo.skin == "tails" //if you can't fly you better fuck off
		if p.pflags & PF_THOKKED & ~PF_SHIELDABILITY and p.mo.state == S_PLAY_FLY or p.mo.state == S_PLAY_SWIM or p.mo.state == S_PLAY_FLY_TIRED//can be either swimming or flying
			if p.cmd.buttons & BT_SPIN and p.cmd.buttons & BT_JUMP and p.jumpbuf == 1 and p.candoublejump == true// and p.cancelactivationtimer == 0
				p.pflags = $ & ~PF_THOKKED & ~PF_CANCARRY | PF_NOJUMPDAMAGE | PF_JUMPED
				p.mo.state = S_PLAY_EDGE
				S_StartSound(p.mo,sfx_cdfm02)
				S_StartSound(p.mo,sfx_cdfm01)
				if not p.powers[pw_super]
					p.candoublejump = false
					p.mo.momz = (25/2)*FRACUNIT*orientation
				else
					if mo.eflags & MFE_UNDERWATER
						p.mo.momz = 6*FRACUNIT*orientation
					else
						p.mo.momz = 20*FRACUNIT*orientation
					end
				end
			elseif p.candoublejump == false and p.cmd.buttons & BT_SPIN and p.cmd.buttons & BT_JUMP and p.jumpbuf == 1// and p.cancelactivationtimer == 0
				p.pflags = $ & ~PF_THOKKED & ~PF_CANCARRY | PF_NOJUMPDAMAGE | PF_JUMPED
				p.mo.state = S_PLAY_EDGE
			end
		end
	end
end)

//has to be patched out for now bc of how fucking broken it is

//addHook("JumpSpinSpecial",function(p)
//	local mo = p.mo
	
	//fang throws bombs now
	//they're not great but they do the job
//	if p.pflags & PF_JUMPED | PF_THOKKED
//		if p.mo and p.mo.valid and p.mo.skin == "fang"
//			if p.spinbuf == 1
//				if p.bombdelay == 0
//					p.bomb = P_SpawnMobjFromMobj(p.mo,5*FRACUNIT,5*FRACUNIT,p.mo.height+10*FRACUNIT,MT_FBOMB)
//					p.bomb.scale = FRACUNIT*2
//					P_SetObjectMomZ(p.bomb,5*FRACUNIT,false)
//					P_InstaThrust(p.bomb,p.mo.angle,20*FRACUNIT)
//					if not p.powers[pw_super]
//						p.bombdelay = 15
//					else
//						p.bombdelay = 5
//					end
//					S_StartSound(p.mo,sfx_s3k51)
//				end
//			end
//		end
//	end
//end)

//handle the boost
local function HoverBoost(p)
	local mo = p.mo
	//look callmore i did it i did the thing you wanted
	local angle = p.cmd.angleturn<<16 + R_PointToAngle2(0, 0, p.cmd.forwardmove*FRACUNIT, -p.cmd.sidemove*FRACUNIT)
	local orientation = 1
	
	//obligatory orientation variable
	if (p.mo.eflags & MFE_VERTICALFLIP)
		orientation = -1
	else
		orientation = 1
	end
	
	if p.boosttimer == nil
		p.boosttimer = TICRATE
	end
	
	if p.mo.skin == "metalsonic"
		if p.boosting and p.boosttimer > 0
			if not p.powers[pw_super]
				p.boosttimer = $1-1
				P_SetObjectMomZ(mo,0,false) //float!!!!! wow!!!!!!!!!!!
			end
			P_Thrust(mo,angle,(5/2)*FRACUNIT)
			if (leveltime % 10)
				local effect = P_SpawnGhostMobj(p.mo) //silver: has no ideas. P_SpawnGhostMobj: i gotchu
				effect.fuse = 5
			end
			
			if p.powers[pw_super]
				if p.cmd.buttons & BT_JUMP
					p.mo.momz = $+FRACUNIT * orientation
				elseif p.cmd.buttons & BT_SPIN
					p.mo.momz = $-FRACUNIT * orientation
				else return
				end
			end
		elseif p.boosttimer < 0
			p.boosttimer = TICRATE
			p.boosting = false
			return
		end
	end
//	print(p.boosttimer)
	//reset the timer when you hit the ground
	if P_IsObjectOnGround(mo) or p.mo.eflags & MFE_JUSTHITFLOOR
		if not p.powers[pw_super]
			p.boosttimer = TICRATE
		else
			p.boosttimer = TICRATE+18
		end
	end
end
addHook("PlayerThink",HoverBoost)

//handles starting the boost jet + the shockwave effect; probably should put the effect in a separate function
//to refer back to here.
addHook("AbilitySpecial",function(p)
	local mo = p.mo
	local angle = p.mo.angle
	
	if p.exhausted == nil
		p.exhausted = false
	end
	
	if p.mo.skin == "metalsonic"
		if not p.exhausted //can't already have used the ability!
			//shockwave effect that i'm actually kinda proud of! wow! i freeslotted something!
			local boostshockwave = P_SpawnMobjFromMobj(p.mo,0,0,10*FRACUNIT,MT_BOOST)
			boostshockwave.scale = FRACUNIT
			boostshockwave.destscale = FRACUNIT*7
			boostshockwave.scalespeed = FRACUNIT/10
			boostshockwave.angle = p.mo.angle + ANGLE_90
			if boostshockwave.scale == boostshockwave.destscale and boostshockwave.state == S_BOOST5
				P_KillMobj(boostshockwave,p.mo,p.mo) //works* (citation needed)
			end
			S_StartSound(mo,sfx_bkpoof)
			p.mo.state = S_PLAY_RUN //i fucking hate having to use this so much i despise it with every fibre of my being
			p.boosting = true
			HoverBoost(p) //handle the boost
			p.exhausted = true //use up the charge until you hit the ground again
			
			return true
		else return true end
	end
end)

//allow metal to damage enemies and monitors with his boost jet
addHook("PlayerCanDamage",function(p)
	if p.mo.skin == "metalsonic" 
	and p.boosting
	and p.mo.state == S_PLAY_RUN 
	and not P_IsObjectOnGround(p.mo) then 
		return true 
	end
end)

//rebound after a homing attack

//i hate these two functions so much, they don't fucking work if i change them
local function setReboundMomZ(mobj, mo, mo)
	if mo.skin == "sonic"
	and mo.player.homingatk
	and mobj.flags & (MF_ENEMY|MF_MONITOR)
		mo.player.homingatk = false
		if not mo.player.powers[pw_super]
			//cut your speed if you're not holding
			local buttons = mo.player.cmd.buttons //shortening this
			local speed = mo.player.speed
			if not (buttons & BT_JUMP)
				P_InstaThrust(mo,mo.angle,0)
				P_SetObjectMomZ(mo,(25/2)*FRACUNIT,false)
			else
				P_SetObjectMomZ(mo,10*FRACUNIT,false)
			end
		end
	end
end

addHook("MobjDeath", setReboundMomZ)
addHook("MobjDamage", setReboundMomZ)

local function setOtherReboundMomZ(mobj, mo, mo)
	if mo.skin == "sonic"
	and mo.player.pflags & PF_THOKKED
		if mobj.flags & (MF_BOSS)
			mo.player.homingatk = false
			if not mo.player.powers[pw_super]
				P_SetObjectMomZ(mo,10*FRACUNIT,false)
				P_InstaThrust(mo,mo.angle,(mo.player.speed/2)*-1)
			end
		elseif mobj.flags & (MF_SPRING)
			mo.player.homingatk = false
		end
	end
end

addHook("MobjDamage", setOtherReboundMomZ)

//trigger super sonic's float because for some reason it just...doesn't activate if you have a custom ability?
addHook("JumpSpinSpecial", function(p)
	local floating = false
	local mo = p.mo
	local angle = p.mo.angle
	local runspeed = p.runspeed
	
	if p.mo.skin == "sonic"
		if p.powers[pw_super]
			P_SetObjectMomZ(p.mo, 0, false)
			p.pflags = $ & ~PF_SPINNING
			if p.speed > runspeed
				p.mo.state = S_PLAY_RUN
				floating = true
			elseif p.speed < runspeed
				p.mo.state = S_PLAY_FLOAT
				floating = true
			else 
				floating = false
			end
		end
	end
end)

addHook ("MobjMoveCollide", function (p, spike)
	if p.player ~= nil and p.player.powers[pw_super]
		if (spike.type == MT_SPIKE
		or spike.type == MT_WALLSPIKE)
			if (p.z + p.height < spike.z) 
			or (p.z > spike.z + spike.height)
				return false
			else
				P_KillMobj(spike,p,p)
			end
		end
	end
end)

addHook("MobjMoveCollide", function(bomb,object)
	if bomb ~=nil
		if object.flags & (MF_ENEMY|MF_MONITOR)
			P_DamageMobj(object,bomb,bomb,1)
		end
	end
end, MT_FBOMB)

local function PlayerToBombDamageHandler(bomb,player)
	if bomb ~= nil
		P_DoPlayerPain(player,bomb,bomb)
	end
end

addHook("MobjCollide",PlayerToBombDamageHandler, MT_FBOMB)

////
//keeping this here for future reference bc drop dash code is actually really cool
////

//addHook("PlayerThink", function(p)
//	local mo = p.mo
//	
//	if P_IsObjectOnGround(mo) <- this shit doesn't work tho -
//	and p.cmd.buttons & BT_SPIN								 |
//	and p.cmd.buttons & BT_JUMP								 |
//		dropbuffering = true								 |
//	end <----------------------------------------------------
//
//	"silver you idiot it doesn't work because that's not how the buffer works you already did it later on"s
//	- Silver, a few weeks after actually writing the script
//
//	if (mo and mo.skin == "sonic")
//		and not P_IsObjectOnGround(mo)
//		and not p.powers[pw_super]
//		and not thokking
//		and not doublejumping
//		and (p.pflags & PF_JUMPED)
//		and p.cmd.buttons & BT_SPIN
//		and not dropbuffering
//			candropdash = true
//			P_SpawnThokMobj(p)
//			S_StartSound(mo,sfx_spndsh)
//			mo.state = S_PLAY_ROLL
//		elseif (p.mo.eflags & MFE_JUSTHITFLOOR) 
//		and candropdash
//		and not thokking
//		and not doublejumping
//		and p.cmd.buttons & BT_SPIN
//			candropdash = false
//			P_Thrust(mo,mo.angle,40*FRACUNIT)
//			S_StartSound(mo, sfx_zoom)
//			p.pflags = $ | PF_SPINNING
//			mo.state = S_PLAY_ROLL
//		end
//	end)
local function EnableDropDash(p,arg)
	if p.mo and p.mo.valid
		if arg
			arg = arg:lower()
			if arg == "off"
			or arg == "no"
			or arg == "false"
				p.dropdashenabled = "off"
				CONS_Printf(p,"Metal Sonic's Drop Dash has been disabled.")
			elseif arg == "on"
			or arg == "yes"
			or arg == "true"
				p.dropdashenabled = "on"
				CONS_Printf(p,"Metal Sonic's Drop Dash has been enabled.")
			end
		else
			CONS_Printf(p, "Toggles Metal Sonic's unused Drop Dash ability. Currently " + p.dropdashenabled)
		end
	end
end

COM_AddCommand("metdrop",function(p,arg)
	EnableDropDash(p,arg)
end)