
-- this hud took long and i add a hint of sonic and the fallen star type sheet credit goes to DarkEdgyHope aka Rivaledgyhope for 
-- this amazing script ik i am such a goat gng!

local MENU_OPTIONS = {
    "Boost Style",
    "Toggle Stomp",
	"Stomp Button",
    "Boost Sound",
    "Boost Button",
	"Boost Color"
}


local BOX_W = 170
local BOX_H = 190 
local ANIMATION_SPEED_TICS = 6
local smooth_stamina_cache = {}
local patches = {}
local BoostButtons = {
    [0] = BT_CUSTOM1,
    [1] = BT_CUSTOM2,
    [2] = BT_CUSTOM3,
    [3] = BT_FIRENORMAL
}

rawset(_G, "stomp_enabled", true)

local isPlayerServerorAdmin = function(p)
	return p == server or IsPlayerAdmin(p)
end
addHook("PlayerThink", function(player)
    if not (player and player.valid and player.mo and player.mo.valid) then return end
	if player.boostbutton == nil then player.boostbutton = 1 end
	local chosen_button_flag = BoostButtons[player.boostbutton] or BT_CUSTOM2
    if player.bstcooldown == nil then player.bstcooldown = 0 end
    if player.bstcooldown > 0 then player.bstcooldown = $ - 1 end
    if player.mo.shadowform_stamina >= 1 then
        if (player.speed >= player.runspeed/2 or player.powers[pw_carry] == 3888)
        and (player.cmd.buttons & chosen_button_flag) 
        and (player.bstcooldown == 0)
		and not player.exiting
        and not (player.pflags & PF_FULLSTASIS)
        and not (player.pflags & PF_SPINNING)
        and not (player.pflags & PF_THOKKED)
		and not (player.mo.eflags & MFE_SPRUNG)
        and player.playerstate ~= PST_DEAD
        and not P_PlayerInPain(player) then
			player.bvfxing = true
			player.charflags = $ | SF_RUNONWATER
			player.charflags = $ | SF_CANBUSTWALLS
			player.bstframe = true
		else
			player.bvfxing = false
			player.charflags = $ & ~SF_RUNONWATER
			player.charflags = $ & ~SF_CANBUSTWALLS
			if player.bstframe == true then
				player.bstframe = false
				player.bstcooldown = 15
			end
		end
	end
end)

addHook("PostThinkFrame", function()
    for player in players.iterate do
        if not (player and player.valid) then continue end
        if player.bvfxing == true then
            if player.hashitwooshspeed == true then -- some stuff i left behind
                player.camerascale = FRACUNIT * 2 
            else
                player.camerascale = FRACUNIT * 3 / 2
            end
        else
            player.camerascale = FRACUNIT
        end
    end
end)


local function HandleMenuHorizontalScrolling(player)
    if player.menu_cursor == 36 then
        if player.cmd.sidemove > 0 then
            _G["boost_button_mode"] = $ + 1
            if _G["boost_button_mode"] > 3 then _G["boost_button_mode"] = 0 end
            player.menu_debounce = 6
			if player==consoleplayer
            S_StartSound(player.mo, sfx_onee)
			end
        elseif player.cmd.sidemove < 0 then
            _G["boost_button_mode"] = $ - 1
            if _G["boost_button_mode"] < 0 then _G["boost_button_mode"] = 3 end
            player.menu_debounce = 6
			if player==consoleplayer
            S_StartSound(player.mo, sfx_onee)
			end
        end
    end
end
COM_AddCommand("bvfx", function(player, colorName)
    if not (player and player.valid) then return end
    if not colorName then
        CONS_Printf(player, "Usage: bvfx <colorname> or bvfx default")
        return
    end
	local str = colorName
	if tonumber(colorName) != nil
		str = tonumber($)
		str = max(0,$)
		if str >= #skincolors
			CONS_Printf(player, "Error: Exceeded Valid Skincolor Limit!")
			return
		end
	else
		if str:lower() == "default" or str:lower() == "none"
			str = 0
		else
			if string.sub(str,1,10) != "SKINCOLOR_"
				str = "SKINCOLOR_"..$
			end
			str = $:upper()
			if constants[str] != nil
				str = _G[$]
			else
				CONS_Printf(player, "Error: "..str.." is not a valid color!")
				return
			end
		end
	end
    if str ~= nil then
        player.chosenbvfxcolor = str
		local color = player.chosenbvfxcolor and skincolors[str].name or "Default"
        CONS_Printf(player, "Your personal bvfx color set to: "..color)
    else
        CONS_Printf(player, "Error: not a valid color!")
    end
end)



rawset(_G, "BoostVfx", function(player)
    if not (player and player.valid and player.mo and player.mo.valid) then return end
    local vfxColor = player.chosenbvfxcolor or player.mo.color
	local momz = P_MobjFlip(player.mo)*player.mo.momz
    local fast = P_SpawnMobj(player.mo.x + (P_RandomRange(-50,50) * player.mo.scale), player.mo.y + (P_RandomRange(-50,50) * player.mo.scale), player.mo.z + (player.mo.height/2) + (P_RandomRange(-20,20) * player.mo.scale), MT_SMOKE)
    if fast and fast.valid then
        fast.state = S_BOOSTPARTICLE
        fast.angle = player.drawangle
        fast.momx = player.mo.momx/2
        fast.momy = player.mo.momy/2
        fast.momz = player.mo.momz/2
        fast.colorized = true
        fast.blendmode = AST_ADD
        fast.scale = player.mo.scale
        fast.frame = $|FF_FULLBRIGHT|FF_TRANS40
        fast.tics = 20
        fast.color = vfxColor
		fast.angle = R_PointToAngle2(0, 0, player.mo.momx, player.mo.momy)
		fast.rollangle = R_PointToAngle2(0, 0, FixedHypot(player.mo.momx, player.mo.momy), momz)
    end
    local fast2 = P_SpawnMobj(player.mo.x + (P_RandomRange(-50,50) * player.mo.scale), player.mo.y + (P_RandomRange(-50,50) * player.mo.scale), player.mo.z + (player.mo.height/2) + (P_RandomRange(-20,20) * player.mo.scale), MT_SMOKE)
    if fast2 and fast2.valid then
        fast2.state = S_BOOSTPARTICLE
        fast2.angle = player.drawangle
        fast2.momx = player.mo.momx/2
        fast2.momy = player.mo.momy/2
        fast2.momz = player.mo.momz/2
        fast2.colorized = true
        fast2.blendmode = AST_ADD
        fast2.scale = player.mo.scale
        fast2.frame = $|FF_FULLBRIGHT|FF_TRANS40
        fast2.tics = 20
        fast2.color = vfxColor
		fast2.angle = R_PointToAngle2(0, 0, player.mo.momx, player.mo.momy)
		fast2.rollangle = R_PointToAngle2(0, 0, FixedHypot(player.mo.momx, player.mo.momy), momz)
    end
    local trail = P_SpawnGhostMobj(player.mo)
    if trail and trail.valid then
        trail.tics = 3
        trail.blendmode = AST_ADD
        trail.frame = $|FF_FULLBRIGHT
        P_MoveOrigin(trail, player.mo.x, player.mo.y, player.mo.z)
        trail.color = vfxColor
    end    local adventurerip2 = P_SpawnMobjFromMobj(player.mo, player.mo.momx/2, player.mo.momy/2 , player.mo.momz, MT_THOK)  
    if adventurerip2 and adventurerip2.valid then
        adventurerip2.colorized = true
        adventurerip2.blendmode = AST_ADD
        adventurerip2.sprite = SPR_TMPE
        adventurerip2.frame = (leveltime*2 % 4) | FF_TRANS60
        adventurerip2.tics = 5
        adventurerip2.tracer = player.mo
        adventurerip2.destscale = FRACUNIT/3
        adventurerip2.scale = FRACUNIT
        adventurerip2.rollangle = player.mo.rollangle + FixedMul(player.mo.momz, sin(player.mo.angle - (player.mo.momx + player.mo.momy)))
        adventurerip2.angle = player.drawangle
        adventurerip2.color = vfxColor
    end
    local sparkle = P_SpawnMobj(player.mo.x+P_RandomRange(-20, 20)*player.mo.scale, player.mo.y+P_RandomRange(-30, 30)*player.mo.scale, player.mo.z+P_RandomRange(-30, 0)*player.mo.scale+player.mo.height, MT_WATERZAP)
    if sparkle and sparkle.valid then
        sparkle.flags2 = $|MF2_OBJECTFLIP
        sparkle.colorized = true
        sparkle.state = S_BOOST_SPARK
        sparkle.scale = FRACUNIT/3
        sparkle.destscale = -2
        sparkle.blendmode = AST_ADD
        sparkle.tics = 20
        sparkle.color = vfxColor
    end
    local sparkle2 = P_SpawnMobj(player.mo.x+P_RandomRange(-20, 20)*player.mo.scale, player.mo.y+P_RandomRange(-50, 50)*player.mo.scale, player.mo.z+P_RandomRange(-30, 0)*player.mo.scale+player.mo.height, MT_WATERZAP)
    if sparkle2 and sparkle2.valid then
        sparkle2.flags2 = $|MF2_OBJECTFLIP
        sparkle2.colorized = true
        sparkle2.destscale = FRACUNIT*3/2
        sparkle2.blendmode = AST_ADD
        sparkle2.tics = 25
        sparkle2.color = vfxColor
    end
end)

local function draw_custom_num(v, nums, x, y, scale, flags, colormap)
    if not v then return false end
    nums = tostring(nums or 0)
    local chars = {}
    local charX = 0
    local totalWidth = 0
    local i = 1
    while i <= nums:len() do
        local width = 0
        local currentChar = nums:sub(i, i)
        local patchName = "NUBM" .. currentChar
        if patches[patchName] == nil then
            local success, result = pcall(function() return v.cachePatch(patchName) end)
            if success and result and result.valid then
                if result.width == 8 and result.height == 8 then
                    patches[patchName] = false
                else
                    patches[patchName] = result
                end
            else
                patches[patchName] = false
            end
        end
        local patch = patches[patchName]
        if not patch then return false end
        width = FixedMul(patch.width - 1, scale)
        table.insert(chars, {
            patch = patch,
            x = charX
        })
        charX = $ + width
        totalWidth = $ + width
        i = $ + 1
    end
    for i = 1, #chars do
        if chars[i] and chars[i].patch then
            local currentPatch = chars[i].patch
            v.drawScaled(x * FRACUNIT + chars[i].x - totalWidth / 2, y * FRACUNIT - (currentPatch.height * scale) / 2, scale, currentPatch, flags, colormap)
        end
    end
    return true
end
addHook("PlayerThink", function(player)
    if not (player and player.valid and player.mo and player.mo.valid) then return end
    local mo = player.mo
	if player.boostsound == nil then player.boostsound = 1 end
    if player.chosenbvfxcolor == nil then player.chosenbvfxcolor = SKINCOLOR_NONE end
	if player.menu_debounce == nil then player.menu_debounce = 0 end
    if player.neon_impact_flare == nil then player.neon_impact_flare = 0 end
    if player.menu_open == nil then player.menu_open = false end
    if player.menu_anim_timer == nil then player.menu_anim_timer = 0 end
    if player.menu_cursor == nil then player.menu_cursor = 1 end
    if player.menu_smooth_scroll == nil then player.menu_smooth_scroll = 1 * FRACUNIT end
    if player.saved_stage_music == nil then player.saved_stage_music = "NONE" end
    if player.menu_debounce > 0 then player.menu_debounce = $ - 1 end
    if player.neon_impact_flare > 0 then player.neon_impact_flare = $ - 1 end 
    if (player.cmd.buttons & BT_CUSTOM3) and player.menu_debounce == 0 then
        if player.menu_open == false and player.speed > 0 then return end -- this shit is hard to deal gng
        player.menu_open = not player.menu_open
        player.menu_debounce = 12
        player.neon_impact_flare = 8 
        S_StartSound(mo, sfx_menu1)
        
        if player.menu_open then
            local currentSong = S_MusicName()
            if currentSong ~= "SFPASE" and currentSong ~= "MENR" then player.saved_stage_music = currentSong end
            if not player.menu_music_locked then S_ChangeMusic("MENR", true, player) player.menu_music_locked = true end
        else
            if player.menu_music_locked then
                local trackToRestore = (player.saved_stage_music ~= "NONE") and player.saved_stage_music or mapheaderinfo[G_LegacyMapName(gamemap)].music
                S_ChangeMusic(trackToRestore, true, player)
                player.menu_music_locked = false
            end
        end
    end
    if player.menu_open then
        if player.menu_anim_timer < ANIMATION_SPEED_TICS then player.menu_anim_timer = $ + 1 end
    else
        if player.menu_anim_timer > 0 then player.menu_anim_timer = $ - 1 end
    end
    if not player.menu_open and player.menu_anim_timer == 0 then return end

    if player.menu_open and player.menu_anim_timer == ANIMATION_SPEED_TICS then
        if (player.cmd.forwardmove > 25) and player.menu_debounce == 0 then
            player.menu_cursor = $ - 1
            if player.menu_cursor < 1 then player.menu_cursor = #MENU_OPTIONS player.menu_smooth_scroll = (#MENU_OPTIONS + 1) * FRACUNIT end
            player.menu_debounce = 6
            player.neon_impact_flare = 4 
            S_StartSound(mo, sfx_roll)
        elseif (player.cmd.forwardmove < -25) and player.menu_debounce == 0 then
            player.menu_cursor = $ + 1
            if player.menu_cursor > #MENU_OPTIONS then player.menu_cursor = 1 player.menu_smooth_scroll = 0 end
            player.menu_debounce = 6
            player.neon_impact_flare = 4 
            S_StartSound(mo, sfx_roll)
        end
        local diff = (player.menu_cursor * FRACUNIT) - player.menu_smooth_scroll
        player.menu_smooth_scroll = $ + (diff / 3)
    end
    if player.menu_open and player.menu_anim_timer == ANIMATION_SPEED_TICS then
        if player.menu_debounce == 0 then
			if player.menu_cursor == 3
                if (player.cmd.sidemove > 25) then
                    player.stompbutton = $ + 1
                    if player.stompbutton > 3 then player.stompbutton = 0 end
                    player.menu_debounce = 6
                    player.neon_impact_flare = 4
                    S_StartSound(mo, sfx_onee)
                elseif (player.cmd.sidemove < -25) then
                    player.stompbutton = $ - 1
                    if player.stompbutton < 0 then player.stompbutton = 3 end
                    player.menu_debounce = 6
                    player.neon_impact_flare = 4
                    S_StartSound(mo, sfx_onee)
                end
            elseif player.menu_cursor == 5 then
                if (player.cmd.sidemove > 25) then
                    player.boostbutton = $ + 1
                    if player.boostbutton > 3 then player.boostbutton = 0 end
                    player.menu_debounce = 6
                    player.neon_impact_flare = 4
                    S_StartSound(mo, sfx_onee)
                elseif (player.cmd.sidemove < -25) then
                    player.boostbutton = $ - 1
                    if player.boostbutton < 0 then player.boostbutton = 3 end
                    player.menu_debounce = 6
                    player.neon_impact_flare = 4
                    S_StartSound(mo, sfx_onee)
                end
			elseif player.menu_cursor == 6
                if (player.cmd.sidemove > 25) then
                    player.menu_boost_color = $ + 1
                    if player.menu_boost_color > #skincolors-1 then player.menu_boost_color = 0 end
                    player.menu_debounce = 6
                    player.neon_impact_flare = 4
                    S_StartSound(mo, sfx_onee)
                elseif (player.cmd.sidemove < -25) then
                    player.menu_boost_color = $ - 1
                    if player.menu_boost_color < 0 then player.menu_boost_color = #skincolors-1 end
                    player.menu_debounce = 6
                    player.neon_impact_flare = 4
                    S_StartSound(mo, sfx_one)
                end
			
            end
        end
        if (player.cmd.buttons & BT_JUMP) and player.menu_debounce == 0 then
            player.menu_debounce = 12
            player.neon_impact_flare = 6 
            if player.menu_cursor == 1
            or player.menu_cursor == 2 then
				if not isPlayerServerorAdmin(player)
					CONS_Printf(player,"You must be the Server or Remote Admin.")
					S_StartSound(nil,sfx_adderr)
				elseif player.menu_cursor == 1
					carry_enabled = not $
					if carry_enabled then 
						S_StartSound(mo, sfx_onee)
					else
						S_StartSound(mo, sfx_one)
					end
				else
					stomp_enabled = not $
 					if stomp_enabled then 
						S_StartSound(mo, sfx_onee)
					else
						S_StartSound(mo, sfx_one)
					end
				end
			elseif player.menu_cursor == 4 then
                player.boostsound = $+1
                if player.boostsound >= 5 then 
					player.boostsound = 1
				end
                S_StartSound(mo, sfx_onee)
            elseif player.menu_cursor == 5 then
				--sorry none
            elseif player.menu_cursor == 6 then --Press Space to Choose an Desire Boost Color!
				local colorstring = player.menu_boost_color
				COM_BufInsertText(player,"bvfx "..colorstring)
				S_StartSound(mo, sfx_onee)
            end
        end
    end
    if player.menu_open then
        player.cmd.forwardmove = 0 player.cmd.sidemove = 0 player.cmd.buttons = 0
        mo.momx = 0 mo.momy = 0 if mo.momz > 0 then mo.momz = 0 end 
    end
end)



addHook("HUD", function(v, p)
    if not (p and p.valid) then return end
    local ANIMATION_SPEED_TICS = 6
    local a = p.menu_anim_timer or 0
    if p.menu_open == true then hud.disable("score") hud.disable("time") hud.disable("rings")
    else hud.enable("score") hud.enable("time") hud.enable("rings") end
    if a == 0 then return end
    v.disableVanillaHUD = true
    local sW, sH, cC, aC = v.width()/FRACUNIT, v.height()/FRACUNIT, p.menu_cursor or 1, 151
    if p.menu_open then
        v.drawFill(0, 0, sW, sH, 31|V_SNAPTOTOP|V_90TRANS)
        local mH = 16 local sHut = (mH * a) / ANIMATION_SPEED_TICS
        v.drawFill(0, 0, sW, sHut, 31|V_SNAPTOTOP) v.drawFill(0, sH-sHut, sW, sHut, 31|V_SNAPTOTOP)
        v.drawFill(0, sHut-1, sW, 1, aC|V_SNAPTOTOP|V_50TRANS) v.drawFill(0, sH-sHut, sW, 1, aC|V_SNAPTOTOP)
    end
    if a == ANIMATION_SPEED_TICS then
        local sS, sF, sC = (leveltime/2)%max(1, sH), leveltime%max(1, sH), max(1, sH-36)
        for sx = 10, sW, 35 do
            local dY1, dY2 = (sx*4+sS)%sC+16, (sx*2+sF)%sC+16
            v.drawFill(sx, dY1, 1, 3, aC|V_SNAPTOTOP|V_70TRANS) v.drawFill(sx+15, dY2, 1, 2, 31|V_SNAPTOTOP|V_60TRANS)
        end
        local fX = (sW - (leveltime*3 % (sW+60)))
        if fX > 10 and fX < sW-10 then v.drawFill(fX, sH/4, 3, 2, aC|V_SNAPTOTOP|V_50TRANS) v.drawFill(fX+10, sH*3/4, 2, 2, 0|V_SNAPTOTOP|V_70TRANS) end
    end
    local targetY = 104 - (BOX_H / 2)
    local cY = targetY local bW, bH = 0, 0 local isCrtFlashing = false
    if a <= 2 then bW = (BOX_W * a) / 2 bH = 2 cY = targetY + (BOX_H / 2) - 1 isCrtFlashing = true
    else bW = BOX_W local verticalProgress = a - 2 bH = (BOX_H * verticalProgress) / (ANIMATION_SPEED_TICS - 2) cY = targetY + (BOX_H / 2) - (bH / 2) end
    local crtShakeX = 0
    if a > 0 and a < ANIMATION_SPEED_TICS then local shakeMagnitude = max(1, 4 - a) crtShakeX = ((leveltime % 2 == 0) and 1 or -1) * shakeMagnitude end
    local cX = 160 - (bW / 2) + crtShakeX local dI, pO = p.neon_impact_flare or 0, (leveltime%16 < 8) and 1 or 0 local tF = 3 + pO + dI
    if dI > 0 then v.drawFill(cX-(tF+(dI*2)), cY-(tF+(dI*2)), bW+((tF+(dI*2))*2), bH+((tF+(dI*2))*2), aC|V_SNAPTOTOP|V_80TRANS) end
    v.drawFill(cX-tF, cY-tF, bW+(tF*2), bH+(tF*2), aC|V_SNAPTOTOP|V_70TRANS)
    local crtBgColor = 29|V_SNAPTOTOP|V_20TRANS
    if isCrtFlashing then crtBgColor = (leveltime % 2 == 0) and (0|V_SNAPTOTOP) or (159|V_SNAPTOTOP)
    elseif a < ANIMATION_SPEED_TICS then crtBgColor = 159|V_SNAPTOTOP|V_30TRANS end
    v.drawFill(cX, cY, bW, bH, crtBgColor)
    if isCrtFlashing and bW > 0 then
        local noiseColors = {0, 31, 159, 29}
        for nx = 0, bW, 4 do local noiseColorIndex = ((leveltime + nx) % 4) + 1 v.drawFill(cX + nx, cY, min(4, bW - nx), 2, noiseColors[noiseColorIndex]|V_SNAPTOTOP|V_70TRANS) end
    end
    if p.menu_open then
        local aS = 8 + (dI/2)
        v.drawFill(cX-tF-4, cY-tF-4, aS, 1, aC|V_SNAPTOTOP) v.drawFill(cX-tF-4, cY-tF-4, 1, aS, aC|V_SNAPTOTOP)
        v.drawFill(cX+bW+tF+4-aS, cY-tF-4, aS, 1, aC|V_SNAPTOTOP) v.drawFill(cX+bW+tF+3, cY-tF-4, 1, aS, aC|V_SNAPTOTOP)
        v.drawFill(cX-tF-4, cY+bH+tF+3, aS, 1, aC|V_SNAPTOTOP) v.drawFill(cX-tF-4, cY+bH+tF+4-aS, 1, aS, aC|V_SNAPTOTOP)
        v.drawFill(cX+bW+tF+4-aS, cY+bH+tF+3, aS, 1, aC|V_SNAPTOTOP) v.drawFill(cX+bW+tF+3, cY+bH+tF+4-aS, 1, aS, aC|V_SNAPTOTOP)
    end
    if a == ANIMATION_SPEED_TICS then
        local slowScrollX = (leveltime) % 24 local slowScrollY = (leveltime) % 24
        local pulseSize = 10 + ((sin(leveltime * (ANG1 * 3)) * 3) / FRACUNIT)
        local cycleWave = (sin(leveltime * (ANG1 * 4)) * 3) / FRACUNIT local globalFade = 30 + cycleWave
        for bx = -24, bW + 24, 24 do
            for by = -24, bH + 24, 24 do
                local finalBoxX = cX + bx - slowScrollX local finalBoxY = cY + 22 + by - slowScrollY
                if finalBoxX >= cX + 4 and finalBoxX + pulseSize <= cX + bW - 4 then
                    if finalBoxY >= cY + 20 and finalBoxY + pulseSize <= cY + bH - 14 then
                        local alternateStyle = (((bx + (leveltime & ~23)) / 24) + ((by + (leveltime & ~23)) / 24)) % 2 == 0
                        local baseAlpha = alternateStyle and V_70TRANS or V_80TRANS
                        if globalFade > 31 then baseAlpha = alternateStyle and V_80TRANS or V_90TRANS
                        elseif globalFade < 29 then baseAlpha = alternateStyle and V_60TRANS or V_70TRANS end
                        v.drawFill(finalBoxX, finalBoxY, pulseSize, pulseSize, 31|V_SNAPTOTOP|baseAlpha) v.drawFill(finalBoxX + 1, finalBoxY + 1, pulseSize - 2, pulseSize - 2, 29|V_SNAPTOTOP|V_70TRANS)
                    end
                end
            end
        end
    end
local sS, vW = p.menu_smooth_scroll or (1*FRACUNIT), 4 local tI, sI = #MENU_OPTIONS, (sS/FRACUNIT)-(vW/2)
        local eI = sI + vW if sI < 1 then sI = 1 end if eI > tI then eI = tI end
        if a == ANIMATION_SPEED_TICS then
            local spkoffset = (leveltime / 4) % 8
            for zy = cY + spkoffset, cY + bH - 8, 8 do
                local distancefromtop = zy - cY
                local dynamicw = 4 + (distancefromtop / 24)
                if dynamicw > 12 then dynamicw = 12 end
                local w1 = dynamicw
                local w2 = (dynamicw * 3) / 4
                local w3 = dynamicw / 2
                local w4 = dynamicw / 4
                v.drawFill(cX - w1, zy + 0, w1, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(cX - w2, zy + 1, w2, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(cX - w3, zy + 2, w3, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(cX - w4, zy + 3, w4, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(cX - w4, zy + 4, w4, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(cX - w3, zy + 5, w3, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(cX - w2, zy + 6, w2, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(cX - w1, zy + 7, w1, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                local rx = cX + bW
                v.drawFill(rx, zy + 0, w1, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(rx, zy + 1, w2, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(rx, zy + 2, w3, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(rx, zy + 3, w4, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(rx, zy + 4, w4, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(rx, zy + 5, w3, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(rx, zy + 6, w2, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                v.drawFill(rx, zy + 7, w1, 1, aC|V_50TRANS|V_SNAPTOTOP|V_PERPLAYER)
                local seed = (zy / 8)
                if seed % 2 == 0 then continue end
                local p_lifecycle = ((leveltime / 2) + (seed * 5)) % 24
                local p_trans = (p_lifecycle > 18) and V_80TRANS or ((p_lifecycle > 12) and V_70TRANS or V_50TRANS)
                local p_offset_x = p_lifecycle * 1
                local p_offset_y = 3 + (sin(p_lifecycle * ANG1 * 15) / (FRACUNIT / 4))
                local rotation_frame = (leveltime / 6) % 4
                local r_px = rx + w1 + p_offset_x
                local l_px = cX - w1 - p_offset_x - 2
                local py = zy + p_offset_y
                if rotation_frame == 0 then
                    v.drawFill(r_px, py, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(r_px, py + 1, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px, py, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px, py + 1, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                elseif rotation_frame == 1 then
                    v.drawFill(r_px + 1, py, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(r_px, py + 1, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px + 1, py, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px, py + 1, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                elseif rotation_frame == 2 then
                    v.drawFill(r_px, py, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(r_px, py + 1, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px, py, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px, py + 1, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                else
                    v.drawFill(r_px, py, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(r_px + 1, py + 1, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px, py, 2, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                    v.drawFill(l_px + 1, py + 1, 1, 1, aC | p_trans | V_SNAPTOTOP | V_PERPLAYER)
                end
            end
        end
    if not isCrtFlashing then
        local scanlineY = cY + 20 + ((leveltime * 2) % (bH - 36))
        if a == ANIMATION_SPEED_TICS then
            local windScroll = (leveltime * 4) % bW local starScroll = (leveltime * 2) % bW
            for row = 1, 14 do
                local tracerX = cX + bW - ((windScroll + (row * 23)) % bW) local tracerY = cY + 20 + (row * 11) local tracerW = 10 + (row % 4 * 6)
                if tracerY < cY + bH - 14 and tracerX >= cX + 4 and tracerX + tracerW <= cX + bW - 4 then
                    local altLineColor = (row % 2 == 0) and (aC|V_SNAPTOTOP|V_60TRANS) or (31|V_SNAPTOTOP|V_80TRANS)
                    v.drawFill(tracerX, tracerY, tracerW, 1, altLineColor)
                end
                local starX = cX + bW - ((starScroll + (row * 43) + 17) % bW) local starY = cY + 24 + ((row * 13) % (bH - 42))
                if starY < cY + bH - 14 and starX >= cX + 4 and starX < cX + bW - 4 then
                    local flashPhase = (leveltime + row) % 12
                    local starColor = (flashPhase < 4) and (0|V_SNAPTOTOP) or ((flashPhase < 8) and (135|V_SNAPTOTOP|V_20TRANS) or (159|V_SNAPTOTOP|V_50TRANS))
                    v.drawFill(starX, starY, 1, 1, starColor)
                    if row < 14 then
                        local nextStarX = cX + bW - ((starScroll + ((row + 1) * 43) + 17) % bW) local nextStarY = cY + 24 + (((row + 1) * 13) % (bH - 42))
                        if nextStarY < cY + bH - 14 and nextStarX >= cX + 4 and nextStarX < cX + bW - 4 then
                            local midX = (starX + nextStarX) / 2 v.drawFill(midX, (starY + nextStarY) / 2, 1, 1, 135|V_SNAPTOTOP|V_90TRANS)
                        end
                    end
                end
            end
            local textScanY = cY + 20 + (leveltime % (bH - 36))
            if textScanY >= cY + 22 and textScanY <= cY + bH - 24 then
                v.drawString(161, textScanY - 3, "Subscribe to rivaledgyhope!", V_SNAPTOTOP|V_80TRANS, "thin-center")
                v.drawString(160, textScanY - 4, "Subscribe to rivaledgyhope!", V_SNAPTOTOP|V_PURPLEMAP, "thin-center")
            end
        end
        v.drawFill(cX, cY, bW, 18, 159|V_SNAPTOTOP) v.drawFill(cX, cY, bW, 1, 135|V_SNAPTOTOP)
        v.drawFill(cX, cY+bH-1, bW, 1, 135|V_SNAPTOTOP) v.drawFill(cX, cY, 1, bH, 135|V_SNAPTOTOP)
        v.drawFill(cX+bW-1, cY, 1, bH, 135|V_SNAPTOTOP) v.drawString(160, cY+5, "BOOST SETTINGS", V_YELLOWMAP, "thin-center")
        if p.mo and p.mo.valid then
            local sP = v.getSprite2Patch(p.mo.skin, SPR2_SIGN, false, A, 0, 0)
            if sP then
                local sB, bS, dR = sin(leveltime*ANG10)*5, (cY+100)*FRACUNIT, 0
                if p.menu_smooth_scroll ~= nil then dR = FixedMul(((cC*FRACUNIT)-p.menu_smooth_scroll)/18, ANG1) end
                local bM, cM = FRACUNIT+(cos(leveltime*(ANG10/2))/50), v.getColormap(TC_DEFAULT, p.mo.color)
                if a < ANIMATION_SPEED_TICS then local cS = FixedMul((a*FRACUNIT)/ANIMATION_SPEED_TICS, bM) v.drawStretched(160*FRACUNIT, bS+sB, cS, cS, sP, V_SNAPTOTOP, cM, dR)
                else v.drawStretched(160*FRACUNIT, bS+sB, bM, bM, sP, V_SNAPTOTOP, cM, dR) end
            end
        end
        local sS, vW = p.menu_smooth_scroll or (1*FRACUNIT), 4 local tI, sI = #MENU_OPTIONS, (sS/FRACUNIT)-(vW/2)
        local eI = sI + vW if sI < 1 then sI = 1 end if eI > tI then eI = tI end
        for i = sI, eI do
            local lY = cY + 144 + (((i*FRACUNIT)-sS) * 11 / FRACUNIT)
            if lY > (cY+115) and lY < (cY+bH-16) then
				local flag = V_GRAYMAP
				if i == cC and a == ANIMATION_SPEED_TICS then
					flag = V_YELLOWMAP
					if i == 2
						flag = stomp_enabled and V_GREENMAP or V_REDMAP
					end
				end
				flag = $|V_ALLOWLOWERCASE
				if i == 1 --Carry or Clash (Sonic Battle) [SERVER and/or REMOTE ADMIN] ONLY
					local arg = carry_enabled and "Carry" or "Clash"
					v.drawString(160, lY, "[ Boost Collision: "..arg.." ]", flag, "thin-center")
				elseif i == 2 --Stomp Toggle [SERVER and/or REMOTE ADMIN] ONLY
					local stomparg = stomp_enabled and "On" or "Off"
					v.drawString(160, lY, "[ Toggle Stomp: "..stomparg.." ]", flag, "thin-center")
				elseif i == 3 -- Stomp Button
					local stm = p.stompbutton or 0
					local arg = {"Spin","Custom 1","Custom 2","Custom 3"}
					v.drawString(160, lY, "[ Stomp Button: "..arg[stm+1].." ]", flag, "thin-center")
				elseif i == 4
					local sM = p.boostsound
					local sL = {"Sonic Gens", "Sonic Rush", "Frontiers", "???"}
					v.drawString(160, lY, "[ Boost Sound: "..sL[sM].." ]", flag, "thin-center")
				elseif i == 5
					local bM = p.boostbutton or 0 
					if bM > 3 then 
						bM = 0 
					end 
					local bN = {"Custom 1", "Custom 2", "Custom 3", "Fire Normal"}
					v.drawString(160, lY, "[ Boost Button: "..bN[bM+1].." ]", flag, "thin-center")
				elseif i == 6
					local color = p.menu_boost_color
					local colorname = color and skincolors[color].name or "Player Color"
					v.drawString(160, lY, "[ "..MENU_OPTIONS[i]..": "..colorname.." ]", flag, "thin-center")
				end
			end
		end
		v.drawFill(cX, cY+bH-12, bW, 12, 159|V_SNAPTOTOP)
		v.drawString(160, cY+bH-10, "FORWARD/BACKWARD: NAVIGATE  *  JUMP: EQUIP", V_WHITERESMAP, "thin-center")
	end
end)


addHook("PlayerThink", function(player)
    if not (player and player.valid and player.mo and player.mo.valid) then return end
    local mo = player.mo
    if player.menu_open == true then
        if not (player.menu_indicator and player.menu_indicator.valid) then
            player.menu_indicator = P_SpawnMobj(mo.x, mo.y, mo.z + mo.height + 16*FRACUNIT, MT_THOK)
            if player.menu_indicator and player.menu_indicator.valid then
                player.menu_indicator.target = mo
                player.menu_indicator.flags = $ | MF_NOCLIP | MF_NOCLIPHEIGHT
                player.menu_indicator.sprite = SPR_MTEX
            end
        end
        if player.menu_indicator and player.menu_indicator.valid then
            local bobFactor = sin(FixedAngle(leveltime * FRACUNIT * 6))
            local zOffset = mo.z + mo.height + FixedMul(20 * FRACUNIT + FixedMul(6 * FRACUNIT, bobFactor), mo.scale)
            P_MoveOrigin(player.menu_indicator, mo.x, mo.y, zOffset)
            player.menu_indicator.scale = mo.scale
            if player.chosenbvfxcolor ~= nil then
                player.menu_indicator.color = player.chosenbvfxcolor
            else
                player.menu_indicator.color = mo.color
            end
        end
    else
        if player.menu_indicator and player.menu_indicator.valid then
            P_RemoveMobj(player.menu_indicator)
            player.menu_indicator = nil
        end
    end
end)
addHook("PlayerThink", function(player)
    if not (player and player.valid and player.mo and player.mo.valid) then return end
	local mo = player.mo
	if (player.mo.vfx == nil)
	    player.mo.vfx = 0
	end
    if player.bvfxing
		player.mo.vfx = $1 + 1 
	else
		player.mo.vfx = 0
	end
	if player.mo.vfx == 15
		player.mo.vfx = 2
	end
    if player.mo.vfx == 4
		local dashpart = P_SpawnMobjFromMobj(player.mo,0,0,mo.info.height/2, MT_BOOSTVFX) 
		dashpart.angle = player.mo.angle + ANGLE_90
		dashpart.tics = 10
		dashpart.scalespeed = FRACUNIT / 3
		dashpart.destscale = FRACUNIT*2
		dashpart.colorized = true
		dashpart.blendmode = AST_ADD
		dashpart.renderflags = RF_PAPERSPRITE
		dashpart.color = player.chosenbvfxcolor or player.mo.color
	end
end)


addHook("PlayerThink", function(player)
    if not (player and player.valid and player.mo and player.mo.valid) then return end
    if player.mo.vfx == 1 then
		if player == displayplayer
			P_StartQuake(10*FRACUNIT, TICRATE / 2)
		end
		local adventureri = P_SpawnMobjFromMobj(player.mo, player.mo.momx*3/6, player.mo.momy*3/6 , player.mo.momz, MT_THOK)         
        adventureri.state = S_STRA1
		adventureri.destscale = FRACUNIT*2
		adventureri.scalespeed = FRACUNIT / 3
        adventureri.blendmode = AST_ADD
		adventureri.renderflags = RF_PAPERSPRITE
        adventureri.angle = player.drawangle + ANGLE_90
        adventureri.color = player.chosenbvfxcolor or player.mo.color
        local soundMode = player.boostsound
        if soundMode == 1 then
            S_StartSound(player.mo, sfx_gener)
			S_StartSound(player.mo,sfx_bvfx1)
        elseif soundMode == 2 then
            S_StartSound(player.mo, sfx_rusher)
        elseif soundMode == 3 then
            S_StartSound(player.mo, sfx_fronti)
		elseif soundMode == 4 then
            S_StartSound(player.mo, sfx_fard)
        end
    end	
end)

	
addHook("PostThinkFrame", function()
    for player in players.iterate do
	if not (player and player.valid) then continue end
	if player.bvfxing == true then
	local vfxColor = player.chosenbvfxcolor or player.mo.color
	local dist = max(player.speed / (4*FRACUNIT), 1) 
	local labubu = P_SpawnMobjFromMobj(player.mo, player.mo.momx/dist, player.mo.momy/dist, player.mo.momz/dist, MT_THOK)
	labubu.sprite = SPR_BRHA
	labubu.frame = (leveltime/1)%4
	labubu.frame = $|FF_FULLBRIGHT
	labubu.tics = 1
	labubu.rollangle = player.mo.rollangle + FixedMul(player.mo.momz, sin(player.mo.angle - (player.mo.momx + player.mo.momy)))
	labubu.angle = player.drawangle
	labubu.scale = player.mo.scale
	labubu.blendmode = AST_ADD
	labubu.destscale = FRACUNIT*3/2
	labubu.scale = FRACUNIT*11/12
    labubu.color = vfxColor
	labubu.colorized = true
end
end
end)


local spawnticky = 4
addHook("PlayerThink", function(player)
    if not (player and player.mo and player.mo.valid) then return end
    if player.bvfxing == true 
    and P_IsObjectOnGround(player.mo) 
    and leveltime % spawnticky == 0 then
        local spawnZ = player.mo.z + (4 * player.mo.scale)
        local ring = P_SpawnMobj(player.mo.x, player.mo.y, spawnZ, MT_THOK)
        if ring and ring.valid then
            ring.colorized = true
            ring.blendmode = AST_ADD
            ring.scale = player.mo.scale
			ring.renderflags = RF_PAPERSPRITE
			ring.angle = player.mo.angle+ANGLE_90
            ring.destscale = FixedMul(player.mo.scale, 3 * FRACUNIT)
            ring.scalespeed = FRACUNIT / 6
            ring.frame = $|FF_FULLBRIGHT|FF_TRANS20
            ring.tics = 6
			ring.state = S_GRNE1
			ring.color = player.chosenbvfxcolor or player.mo.color
            end
    end
end)

