freeslot("sfx_stomp", "sfx_ground", "sfx_boost", "sfx_boost2")

local function P_GravAndScale(actor)
    return P_MobjFlip(actor) * actor.scale
end

local function P_DistanceSquared(x1, y1, x2, y2)
    return (x1 - x2)^2 + (y1 - y2)^2
end

local function P_FindRail(player, radius)
    local foundRail = nil
    local minDistSquared = radius * radius
    local x = player.mo.x
    local y = player.mo.y

    for i = 1, #rails do
        local rail = rails[i]
        local distSquared = P_DistanceSquared(x, y, rail.x, rail.y)

        if distSquared < minDistSquared then
            minDistSquared = distSquared
            foundRail = rail
        end
    end

    return foundRail
end

addHook("ThinkFrame", do
    for player in players.iterate
        if (player.mo and player.mo.skin == "neostreak") then
            -- Initialize stomping variables
            if player.stomping == nil then
                player.stomping = false
            end
            if player.mo.stomp == nil then
                player.mo.stomp = false
            end
            if player.wasjuststomping == nil then
                player.wasjuststomping = false
            end

            local isGrinding = player.mo.state == S_PLAYER_GRINDING

            -- Check for rail grinding
            if isGrinding or P_IsObjectOnGround(player.mo) or player.mo.eflags & MFE_GOOWATER or player.mo.momz == 0 then
                player.stomping = false
            end

            -- Check for nearby Rollout Rock
            local nearRolloutRock = false
            for _, mobj in ipairs(mobjs) do
                if mobj.valid and mobj.type == MT_ROLLOUTROCK and P_DistanceSquared(player.mo.x, player.mo.y, mobj.x, mobj.y) < 900 * FRACUNIT then
                    nearRolloutRock = true
                    break
                end
            end

            -- Begin stomping if ready
            if (player.cmd.buttons & BT_FIRENORMAL)
                    and not isOnGround and not player.stomping and not player.powers[pw_carry] and not nearRolloutRock and not isGrinding
                    and (P_IsObjectOnGround(player.mo) == false)
                    and not (player.stomping == true)
                    and not isGrinding then
                player.mo.state = S_PLAY_FALL
                S_StartSound(player.mo, sfx_stomp)
                player.stomping = true
            end
            if player.stomping == true and player.mo.momz < 0 then
                player.mo.state = S_PLAY_FALL
            end
            if player.mo.state == S_PLAY_SPRING
                    or player.panim == PA_ROLL
                    or P_IsObjectOnGround(player.mo)
                    or player.mo.eflags & MFE_GOOWATER
                    or player.mo.momz == 0 then
                player.stomping = false
            end

            -- Stomping effects
            if player.stomping == true then
                player.mo.momx = 0
                player.mo.momy = 0
                player.mo.momz = FixedMul(-60 * FRACUNIT, P_GravAndScale(player.mo))
                P_SpawnGhostMobj(player.mo)
            end
			
			--Spring Stomping logic
			if player.mo.eflags & MFE_SPRUNG and player.wasjuststomping then
				player.stomping = false
    
				-- Adjust spring power
				local powerMultiplier = FRACUNIT * 5/4 -- 1.25 in FRACUNIT
				local springPower = FixedMul(30 * powerMultiplier, P_GravAndScale(player.mo))
    
				if player.mo.state == S_PLAY_SPRING then
					P_SetObjectMomZ(player.mo, springPower, false) -- Apply a hop.
				else
					P_InstaThrust(player.mo, player.drawangle, springPower) -- Apply increased thrust.
				end
    
				for i = 0, 8 do
					local fa = (i * (ANGLE_180 / 4))
					local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z, MT_SPINDUST)
					dust.momx = FixedMul(sin(fa), 20 * FRACUNIT)
					dust.momy = FixedMul(cos(fa), 20 * FRACUNIT)
				end
    
				S_StartSound(player.mo, sfx_sprong, player)
			end
			
            -- Logic to affect bustable floors
            if player.stomping == true and player.wasjuststomping == true then
                local sectorthing = player.mo.subsector and player.mo.subsector.sector
                if sectorthing and sectorthing.valid then
                    for fof in sectorthing.ffloors() do
                        if fof.valid and (fof.flags & FF_BUSTUP) and (fof.flags & FF_EXISTS) then
                            local foffloor = fof.topheight
                            local fofceiling = fof.bottomheight
                            if fof.b_slope and fof.b_slope.valid then
                                fofceiling = P_GetZAt(fof.b_slope, player.mo.x, player.mo.y)
                            end
                            if fof.t_slope and fof.t_slope.valid then
                                foffloor = P_GetZAt(fof.t_slope, player.mo.x, player.mo.y)
                            end
                            if foffloor > player.mo.z + player.mo.momz and fofceiling < player.mo.z + player.mo.height + player.mo.momz then
                                EV_CrumbleChain(nil, fof)
                                if fof.master.flags & ML_EFFECT5 then
                                    P_LinedefExecute(P_AproxDistance(fof.master.dx, fof.master.dy) >> FRACBITS, player.mo, sectorthing)
                                end
                            end
                        end
                    end
                end
            end

            -- Handle player input and effects
            if (player.cmd.buttons & BT_JUMP) and(P_IsObjectOnGround(player.mo) == false) then
                player.stomping = false
                player.wasjuststomping = false
            end
			if player.powers[pw_carry] and(P_IsObjectOnGround(player.mo) == false) then
                player.stomping = false
                player.wasjuststomping = false
            end
            if (P_IsObjectOnGround(player.mo) == true)
                    and (player.stomping == false)
                    and (player.wasjuststomping == true) then
                S_StartSound(player.mo, sfx_ground)
                for i = 0, 31 do
                    local offset = FixedMul(1 * FRACUNIT, P_GravAndScale(player.mo))
                    local dust = P_SpawnMobj(player.mo.x, player.mo.y, player.mo.z + offset, MT_STOMPDUST)
                    dust.target = player.mo
                    dust.scale = player.mo.scale
                    if player.mo.eflags & MFE_VERTICALFLIP then
                        dust.flags2 = $1 | MF2_OBJECTFLIP -- flip gravity!
                    end
                    local an = i * ANGLE_11hh
                    local speed = dust.info.speed / FRACUNIT
                    dust.momx = speed * cos(an)
                    dust.momy = speed * sin(an)
                    dust.momz = 0
                end
            end
            if player.powers[pw_super] and not (player.cmd.buttons & BT_FIRENORMAL) then
                player.stomping = false
                player.wasjuststomping = false
            end

            -- Determine was-just-stomping variable
            player.wasjuststomping = player.stomping
        end
    end
end)

freeslot("MT_STOMPDUST")

addHook("MobjThinker", function(stompdust)
    for player in players.iterate do
        P_RadiusAttack(stompdust, stompdust.target, 70 * FRACUNIT)
    end
end, MT_STOMPDUST)

addHook("MobjMoveCollide", function(stompdust, mobj)
    for player in players.iterate do
        if (mobj.valid and mobj.health and (mobj.flags & (MF_ENEMY | MF_BOSS))) then
            P_DamageMobj(mobj, stompdust, stompdust.target.target, 2)
        end
        if (mobj.valid and mobj.health and (mobj.flags & MF_MONITOR)) then
            P_KillMobj(mobj, stompdust, stompdust.target)
        end
    end
end, MT_STOMPDUST)