local lspdrange = 192*FRACUNIT
local lspdspeed = 60*FRACUNIT
local lspdbutton = BT_CUSTOM2 -- (note: just setting the button to spin will conflict with other spin abilities)
local lspdskin = "rangersonic"

local function P_RingNearby(player) -- Is a ring in range?
    local closest
	closest = NULL

	searchBlockmap("objects", function(refmobj, mo)

		if mo.health <= 0 then -- Not a valid ring
			return
        end

		if not (mo.type == MT_RING or mo.type == MT_COIN) then
			return
        end

		if P_AproxDistance(P_AproxDistance(player.mo.x-mo.x, player.mo.y-mo.y),
			player.mo.z-mo.z) > lspdrange then -- Out of range
			return
        end

		if not P_CheckSight(player.mo, mo) then -- Out of sight
			return
        end

		if closest and P_AproxDistance(P_AproxDistance(player.mo.x-mo.x, player.mo.y-mo.y),
			player.mo.z-mo.z) > P_AproxDistance(P_AproxDistance(player.mo.x-closest.x,
			player.mo.y-closest.y), player.mo.z-closest.z) then
			return
        end

		-- Found a target
		closest = mo
    end, player.mo, player.mo.x-lspdrange, player.mo.x+lspdrange, player.mo.y-lspdrange, player.mo.y+lspdrange)

	if closest then
		return true
    end

	return false
end

local function P_LightDash(source, enemy) -- Home in on your target

	if not source.tracer then
		return -- Nothing to home in on!
    end

	-- adjust direction
	local dest = source.tracer

	if not dest then
		return
    end

	-- change angle
	-- source.player.drawangle would work better in 2.2, change this into it if you want to
	source.angle = R_PointToAngle2(source.x, source.y, enemy.x, enemy.y)

	-- change slope
	local dist = P_AproxDistance(P_AproxDistance(dest.x - source.x, dest.y - source.y), dest.z - source.z);

	if dist < 1 then
		dist = 1
    end

	source.momx = FixedMul(FixedDiv(dest.x - source.x, dist), lspdspeed)
	source.momy = FixedMul(FixedDiv(dest.y - source.y, dist), lspdspeed)
	source.momz = FixedMul(FixedDiv(dest.z - source.z, dist), lspdspeed)
end

local function P_LookForRings(player)
    local found
    player.mo.target = NULL
    player.mo.tracer = NULL

	searchBlockmap("objects", function(refmobj, mo)

		if mo.health <= 0 then -- Not a valid ring
			return
        end

		if not (mo.type == MT_RING or mo.type == MT_COIN) then
			return
        end

		if P_AproxDistance(P_AproxDistance(player.mo.x-mo.x, player.mo.y-mo.y),
			player.mo.z-mo.z) > lspdrange then -- Out of range
			return
        end

		if not P_CheckSight(player.mo, mo) then -- Out of sight
			return
        end

		if player.mo.target and P_AproxDistance(P_AproxDistance(player.mo.x-mo.x,
			player.mo.y-mo.y), player.mo.z-mo.z) >
			P_AproxDistance(P_AproxDistance(player.mo.x-player.mo.target.x,
			player.mo.y-player.mo.target.y), player.mo.z-player.mo.target.z) then
			return
        end

		-- Found a target
		found = true
		player.mo.target = mo
		player.mo.tracer = mo
    end, player.mo, player.mo.x-lspdrange, player.mo.x+lspdrange, player.mo.y-lspdrange, player.mo.y+lspdrange)

	if found then
		player.lightdash = TICRATE
		P_ResetPlayer(player)
		player.mo.state = S_PLAY_FALL
		P_ResetScore(player)
		P_LightDash(player.mo, player.mo.tracer)
		return
    end
	player.mo.momx = FixedDiv(player.mo.momx,2*FRACUNIT)
	player.mo.momy = FixedDiv(player.mo.momy,2*FRACUNIT)
	player.mo.momz = FixedDiv(player.mo.momz,2*FRACUNIT)
	player.lightdash = 0
end

addHook("PlayerThink", function(player)
	-- aqua: checks i added
	if (lspdskin and player.mo.skin ~= lspdskin)
	or player.exiting or P_PlayerInPain(player)
	or player.playerstate == PST_REBORN
	-- or player.pflags & PF_GLIDING or player.pflags & PF_SLIDING or player.pflags & PF_SHIELDABILITY
	or (player.climbing or player.powers[pw_carry]) then
		player.lightdash = 0
		return
	end
	-- aqua: i removed the player.lightdashallowed variable for optimization purposes.
		-- because, before, to make that unused variable work, i would need to search for rings every frame,
		-- whilst now, i only have to search for rings when you press the light dash button.
		-- however, if you intend on porting the HUD element for the light dash from final demo, you will need to reimplement it.
	if player.cmd.buttons & lspdbutton then
		if not player.attackdown and P_RingNearby(player) then
			player.lightdash = TICRATE
        end
        player.attackdown = true
	else
		player.attackdown = false
    end

	if player.lightdash > 0 then
		player.lightdash = $ - 1
		P_LookForRings(player) -- P_SpawnGhostMobj(player.mo)
		player.powers[pw_flashing] = 2
    end
end)