-добавить включение курсора при открытии меню, выключение при закрытии
-добавить циклическую обработку кнопачькам при длительном удержании
-айди звуков поменять на константы
-добавить проверку наведенности мыши(ховера) при обработке нажатий кнопок мыши
-добавить пропуск пустых айтемов при переключении айтемов с клавы и отсутствии ховера
-добавить проверку на RestrictedMode всем не имеющим ее клавишам


дополни мой todo лист глядя на этот код

local mad = require "MoonAdditions"

-- ================== ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ И КОНСТАНТЫ ==================

-- константы меню
local MENU_TITLE_SIZE 		= 1.40
local MENU_ITEM_HEIGHT 		= 17
local MENU_ITEM_WIDTH 		= 125
local MENU_WIDTH			= 190
local MENU_ITEM_SPACING		= 1

-- цвета элементов меню
local MENU_COLOR 			= {r = 153, g = 238, b = 153, a = 255}
local TEXT_COLOR 			= {r = 255, g = 255, b = 255, a = 255}
local CURSOR_HOVER_COLOR 	= {r = 255, g = 170, b = 255, a = 255}
local DESCRIPTION_COLOR 	= {r = 255, g = 230, b = 100, a = 255}
local DESCRIPTION_FONT_SIZE	= 0.75

-- позиции меню
local MENU_X = 45
local MENU_Y = 240

-- текущее состояние
local currentMenu 		= "MainMenu"	-- текущее активное меню
local wasLMBDown		= false			-- состояние ЛКМ 				в предыдущем кадре
local wasRMBDown		= false			-- состояние ПКМ 				в предыдущем кадре
local wasUpArrowDown 	= false			-- состояние стрелки вверх		в предыдущем кадре
local wasDownArrowDown 	= false			-- состояние стрелки вниз		в предыдущем кадре
local wasLArrowDown 	= false			-- состояние стрелки влево		в предыдущем кадре
local wasRArrowDown 	= false			-- состояние стрелки вправо		в предыдущем кадре
local wasEnterDown 		= false			-- состояние клавиши Enter		в предыдущем кадре
local wasBackspaceDown 	= false			-- состояние клавиши Backspace	в предыдущем кадре
local focusIndex 		= 1				-- индекс текущего выбранного элемента меню

-- переменные для текстовой анимации
local textAnimation = "..."
local lastUpdateTime = os.time()
local updateInterval = 0.5  

-- переменные для положения камеры
local camhack_status = true            -- Статус КамХака
local cam_slot = 1                     -- Текущая камера слот


-- ================== ОПРЕДЕЛЕНИЕ МЕНЮ ==================

local menus = function() 
    return {
        MainMenu = {
            title = "Main Menu",
            parent = nil,  
            items = {
                {
                    label = "Positions", 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        currentMenu = 'PosMenu' 
                        --printStringNow("Positions action triggered!", 500) 
                    end, 
                    description = "Configure camera positions"
                },
                {
                    label = "Playback",
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Playback action triggered!", 500) 
                    end, 
                    description = "Play recorded audio"
                },
                {
                    label = "Pause", 
                    action = function()
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Pause action triggered!", 500) 
                    end, 
                    description = "Pause current action"
                },
                {
                    label = "Car slot", 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Car slot action triggered!", 500) 
                    end, 
                    description = "Manage car storage"
                },
                {
                    label = "Save/Load", 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Save/Load action triggered!", 500) 
                    end, 
                    description = "Save or load settings"
                },
                {
                    label = "Triggers", 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Triggers action triggered!", 500) 
                    end, 
                    description = "Configure trigger actions"
                },
                {
                    label = "Options", 
                    action = function()
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Options action triggered!", 500) 
                    end, 
                    description = "Adjust settings"
                },
            }
        },

        PosMenu = {
            title = "Positions",
            parent = "MainMenu",
            items = {
                {
                    label = "Camhack: " .. (camhack_status and "~g~~h~~h~~h~ON" or "~r~~h~~h~~h~OFF"), 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Toggle CamHack!", 500) 
                        camhack_status = not camhack_status 
                    end, 
                    description = "Toggle Camhack"
                },
                {
                    label = "Cam Slot: " .. cam_slot, 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Display Current Camera Slot!", 500) 
                    end, 
                    scrollAction = adjustCamSlot, 
                    description = "Select slot to Load or Save"
                },
                {
                    label = "Preview", 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Preview Camera Position!", 500) 
                    end, 
                    description = "Preview current camera"
                },
                {
                    label = "Save", 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Save Camera Position!", 500) 
                    end, 
                    description = "Save current camera position to slot"
                },
                {
                    label = "Load", 
                    action = function() 
						addOneOffSound(0, 0, 0, 1083)
                        --printStringNow("Load Camera Position!", 500) 
                    end, 
                    description = "Load camera position from slot"
                },
                {
                    label = "", 
                    action = function() 
                    end, 
                    description = ""
                },
                {
                    label = "Back", 
                    action = function() 
                        currentMenu = 'MainMenu'
						addOneOffSound(0, 0, 0, 1084)
                        --printStringNow("Back to Main Menu!", 500) 
                    end, 
                    description = "Return to the main menu"
                },
            }
        }
    }
end

-- ================== ФУНКЦИИ ==================

function sign(x)
    return x > 0 and 1 or x < 0 and -1 or 0
end

function adjustCamSlot(mouseDelta, fastModifier, preciseModifier)
    local step = fastModifier and mouseDelta * 3 or preciseModifier and sign(mouseDelta) or mouseDelta
    addOneOffSound(0, 0, 0, 1058)
    cam_slot = math.max(1, cam_slot + step)
end

-- Draw a text with given attributes
function drawText(text, x, y, size, color, isHovered)
    local style = mad.font_style.FONT_SUBTITLES
    local width = size * 0.58
    local height = size * 1.02
    local align = mad.font_align.LEFT
    local wrap = MENU_WIDTH
    local proportional = true
    local justify = false  

    local textColor = isHovered and CURSOR_HOVER_COLOR or color
    mad.draw_text(text, x, y, style, width, height, align, wrap, proportional, justify, 
                  textColor.r, textColor.g, textColor.b, textColor.a, 
                  1, 0, 0, 0, 0, 255, 0, 0, 0, 0)
end

-- Check if a point is within a rectangular area
function isPointInArea(pointX, pointY, rectX, rectY, rectWidth, rectHeight)
    return pointX >= rectX and pointX <= rectX + rectWidth and
           pointY >= rectY and pointY <= rectY + rectHeight
end

-- Obnovit? funkciu otobrazenia menu
function displayMenu(menu)
	if not isMenuActive then return end

    -- Inicializacia lokal?nyh peremennyh
    local cursorX, cursorY		= getCursorPos()		-- Tekusaa pozicia kursora
    local isLMBDown				= isKeyDown(1)  		-- ?LKM? nazata
    local isRMBDown				= isKeyDown(2)  		-- ?PKM? nazata
    local currentDescription	= ""  					-- Tekusee opisanie
    local isArrowKeyDown		= false  				-- Flag nazatia strelki
	local isAnyItemHovered		= false
    local mouseWheelDelta		= getMousewheelDelta()  -- Prokrutka kolesa mysi
    local isUArrowDown			= isKeyDown(38)  		-- Vverh strelka nazata
    local isDArrowDown			= isKeyDown(40)  		-- Vniz strelka nazata
    local isLArrowDown			= isKeyDown(37)  		-- Vlevo strelka nazata
    local isRArrowDown			= isKeyDown(39)  		-- Vpravo strelka nazata
    local isEnterDown			= isKeyDown(13)  		-- Enter nazat
    local isBackspaceDown		= isKeyDown(8)			-- Backspace nazat

    -- Proverka na aktivnosti SAF, Chata, Dialoga i Menu pauzy
    local isNotInRestrictedMode = not isSampfuncsConsoleActive() and not sampIsChatInputActive() and not sampIsDialogActive() and not isPauseMenuActive()

    -- ?Otrisovka? zagolovka menu
    drawText(menu.title, MENU_X, MENU_Y, MENU_TITLE_SIZE, MENU_COLOR, false)

    -- Perebor elementov menu
    for i, item in ipairs(menu.items) do
        local itemY = MENU_Y + 10 + (MENU_ITEM_HEIGHT + MENU_ITEM_SPACING) * i

        if isNotInRestrictedMode then
            local isHovered = isPointInArea(cursorX, cursorY, MENU_X, itemY, MENU_ITEM_WIDTH, MENU_ITEM_HEIGHT)
            if isHovered then isAnyItemHovered = true end
			
            -- Obnovlenie fokusa na osnove navedenia mysi, esli strelki ne byli nazaty
            if isHovered and not isArrowKeyDown then
                focusIndex = i
            end
        end

        -- Opredelenie, sfokusirovan li element ili na nego naveden kursor
        local isFocused = (i == focusIndex)
        drawText(item.label, MENU_X, itemY, 1, TEXT_COLOR, isFocused or (isNotInRestrictedMode and isPointInArea(cursorX, cursorY, MENU_X, itemY, MENU_ITEM_WIDTH, MENU_ITEM_HEIGHT)))

        -- Vypolnenie dejstvij v zavisimosti ot sostoania fokusa
        if isFocused then
            currentDescription = item.description

            -- Obrabotka prokrutki kolesom mysi ili strelkami
            if isNotInRestrictedMode and mouseWheelDelta ~= 0 and item.scrollAction then
                item.scrollAction(mouseWheelDelta, isShiftDown, isCtrlDown)
            elseif isLArrowDown and not wasLArrowDown and item.scrollAction then
                item.scrollAction(-1, isShiftDown, isCtrlDown)
            elseif isRArrowDown and not wasRArrowDown and item.scrollAction then
                item.scrollAction(1, isShiftDown, isCtrlDown)
            end

            -- Obrabotka nazatia ?LKM? ili Enter
            if isNotInRestrictedMode and (isLMBDown and not wasLMBDown or isEnterDown and not wasEnterDown) then
                item.action()
            end
        end
    end
	
	-- Proverka vvoda so strelok i obnovlenie focusIndex s cikliceskoj navigaciej
	if not isAnyItemHovered and isUArrowDown and not wasUpArrowDown then
		if focusIndex == 1 then
			focusIndex = #menu.items  -- Perehod na poslednij element
		else
			focusIndex = focusIndex - 1
		end
		isArrowKeyDown = true
	elseif not isAnyItemHovered and isDArrowDown and not wasDownArrowDown then
		if focusIndex == #menu.items then
			focusIndex = 1  -- Perehod na pervyj element
		else
			focusIndex = focusIndex + 1
		end
		isArrowKeyDown = true
	end
	
    -- Otobrazenie tekusego opisania pod poslednim elementom menu
    local descriptionY = MENU_Y + 15 + (MENU_ITEM_HEIGHT + MENU_ITEM_SPACING) * (#menu.items + 1)
    drawText(currentDescription, MENU_X, descriptionY, DESCRIPTION_FONT_SIZE, DESCRIPTION_COLOR, false)

    -- Pereklucenie na roditel?skoe menu, esli nazata ?PKM ili Backspace
    if isNotInRestrictedMode and (isRMBDown and not wasRMBDown or isBackspaceDown and not wasBackspaceDown) then
		if menu.parent then
			currentMenu = menu.parent
		else
			isMenuActive = false
		end
		addOneOffSound(0, 0, 0, 1084)
        --printStringNow("Switching to parent menu", 500)
		if not isAnyItemHovered then	-- obnulenie fokusa na pervyj ?item
			focusIndex = 1				-- esli ni odin iz ?ajtemov? ne ?hovered?
		end
    end

    -- Obnovlenie peremennyh sostoania klavis
    wasUpArrowDown 		= isUArrowDown
    wasDownArrowDown 	= isDArrowDown
    wasLArrowDown	 	= isLArrowDown
    wasRArrowDown 		= isRArrowDown
    wasLMBDown			= isLMBDown
    wasRMBDown			= isRMBDown
    wasEnterDown		= isEnterDown
    wasBackspaceDown	= isBackspaceDown
end


function updateText()
    local currentTime = os.time()

    if currentTime - lastUpdateTime >= updateInterval then
        if textAnimation == "." then
            textAnimation = ".."
        elseif textAnimation == ".." then
            textAnimation = "..."
        else
            textAnimation = "."
        end

        --print(textAnimation)

        lastUpdateTime = currentTime
    end
end


-- Main function
function main()
    while not isPlayerPlaying(PLAYER_HANDLE) do wait(100) end
	sampRegisterChatCommand('llcam', function() isMenuActive = not isMenuActive end)
    wait(100)

    while true do wait(0)
		--print(getMousewheelDelta())

		updateText()
        -- Display the current active menu
        local menu = menus()[currentMenu]
        if menu then
            displayMenu(menu)
        end
    end
end


--    if hoveredItem then
--        if mouseWheelDelta > 0 and hoveredItem.scrollUp then
--            hoveredItem.scrollUp()
--        elseif mouseWheelDelta < 0 and hoveredItem.scrollDown then
--            hoveredItem.scrollDown()
--        end
--    end