Gui Helper - точные координаты для интерфейса

Gui Helper - инструмент для поиска позиций на экране и позволяет не делать интерфейс на глаз , а получить точные координаты .
Аддон в мастерской

-- GUI Helper by [Vasilich] and Команда КЭП


local helper = {}
helper.enabled = false
helper.mode = "info"
helper.drawing = false
helper.startPos = nil
helper.currentPos = nil
helper.selectedInfo = nil
helper.mouseX, helper.mouseY = 0, 0

helper.mouseDown = false
helper.lastMouseDown = false

helper.hoverInfo = nil

concommand.Add("guihelper_toggle", function()
    helper.enabled = not helper.enabled
    if helper.enabled then
        hook.Add("HUDPaint", "GUIHelper_HUDPaint", function() helper:Paint() end)
        hook.Add("Think", "GUIHelper_Think", function() helper:Think() end)
        print("[GUI Helper] Включён")
    else
        hook.Remove("HUDPaint", "GUIHelper_HUDPaint")
        hook.Remove("Think", "GUIHelper_Think")
        helper.drawing = false
        helper.selectedInfo = nil
        print("[GUI Helper] Выключен")
    end
end)

concommand.Add("guihelper_mode", function(ply, cmd, args)
    if #args < 1 then
        print("Использование: guihelper_mode <info|rect|circle>")
        return
    end
    local mode = args[1]:lower()
    if mode == "info" or mode == "rect" or mode == "circle" then
        helper.mode = mode
        helper.drawing = false
        helper.selectedInfo = nil
        print("[GUI Helper] Режим установлен: " .. mode)
    else
        print("Неверный режим. Используйте info, rect или circle.")
    end
end)

concommand.Add("guihelper_copy", function()
    if helper.selectedInfo then
        SetClipboardText(helper:FormatInfo(helper.selectedInfo))
        print("[GUI Helper] Скопировано в буфер обмена: " .. helper:FormatInfo(helper.selectedInfo))
    else
        print("[GUI Helper] Нет выделения для копирования.")
    end
end)

concommand.Add("guihelper_output", function()
    if helper.selectedInfo then
        print("[GUI Helper] Выделение: " .. helper:FormatInfo(helper.selectedInfo))
    else
        print("[GUI Helper] Нет выделения.")
    end
end)

function helper:FormatInfo(info)
    if self.mode == "info" and info.panelClass then
        if info.parentClass then
            return string.format("panel:SetPos(%d, %d) --[[ относительно родителя: %s ]] panel:SetSize(%d, %d)",
                info.relX, info.relY, info.parentClass, info.w, info.h)
        else
            return string.format("panel:SetPos(%d, %d) --[[ экранные координаты ]] panel:SetSize(%d, %d)",
                info.x, info.y, info.w, info.h)
        end
    elseif self.mode == "rect" then
        return string.format("-- Прямоугольник: %d, %d, %d, %d", info.x, info.y, info.w, info.h)
    elseif self.mode == "circle" then
        return string.format("-- Центр: (%d, %d), радиус: %d", info.x, info.y, info.r)
    end
    return ""
end

function helper:Think()
    self.mouseX, self.mouseY = gui.MousePos()


    local hover = vgui.GetHoveredPanel()
    if hover and IsValid(hover) then

        local class = "unknown"
        local success, result = pcall(function() return hover:GetClass() end)
        if success then class = result end

        local x, y = 0, 0
        success, result = pcall(function() return hover:LocalToScreen(0, 0) end)
        if success and type(result) == "table" then
            x, y = result[1] or 0, result[2] or 0
        end

        local w, h = 0, 0
        success, result = pcall(function() return hover:GetSize() end)
        if success and type(result) == "table" then
            w, h = result[1] or 0, result[2] or 0
        end

        local relX, relY = 0, 0
        success, result = pcall(function() return hover:GetPos() end)
        if success and type(result) == "table" then
            relX, relY = result[1] or 0, result[2] or 0
        end

        local parentClass = nil
        local parent = hover:GetParent()
        if parent and IsValid(parent) then
            success, result = pcall(function() return parent:GetClass() end)
            if success then parentClass = result end
        end

        self.hoverInfo = {
            class = class,
            x = x,
            y = y,
            relX = relX,
            relY = relY,
            w = w,
            h = h,
            parentClass = parentClass
        }

        if self.mode == "info" then
            self.selectedInfo = {
                panelClass = class,
                parentClass = parentClass,
                x = x,
                y = y,
                relX = relX,
                relY = relY,
                w = w,
                h = h
            }
        end
    else
        self.hoverInfo = nil

    end

    self.mouseDown = input.IsMouseDown(MOUSE_LEFT)

    if self.mode == "rect" or self.mode == "circle" then

        if self.mouseDown and not self.lastMouseDown then
            self.drawing = true
            self.startPos = { x = self.mouseX, y = self.mouseY }
            self.currentPos = self.startPos
            self.selectedInfo = nil

        elseif not self.mouseDown and self.lastMouseDown then
            if self.drawing then
                self.drawing = false
                if self.mode == "rect" then
                    local x1 = math.min(self.startPos.x, self.currentPos.x)
                    local y1 = math.min(self.startPos.y, self.currentPos.y)
                    local x2 = math.max(self.startPos.x, self.currentPos.x)
                    local y2 = math.max(self.startPos.y, self.currentPos.y)
                    self.selectedInfo = {
                        x = x1,
                        y = y1,
                        w = x2 - x1,
                        h = y2 - y1
                    }
                elseif self.mode == "circle" then
                    local dx = self.currentPos.x - self.startPos.x
                    local dy = self.currentPos.y - self.startPos.y
                    local radius = math.sqrt(dx*dx + dy*dy)
                    self.selectedInfo = {
                        x = self.startPos.x,
                        y = self.startPos.y,
                        r = math.floor(radius)
                    }
                end
            end
        end

        if self.drawing then
            self.currentPos = { x = self.mouseX, y = self.mouseY }
        end
    end

    self.lastMouseDown = self.mouseDown
end


function helper:Paint()

    surface.SetFont("Default")
    surface.SetTextColor(255,255,255,255)
    surface.SetTextPos(10, 60)
    surface.DrawText(string.format("Мышь: %d, %d", self.mouseX, self.mouseY))

    if self.hoverInfo then
        local info = self.hoverInfo
        surface.SetDrawColor(255,0,0,255)
        surface.DrawOutlinedRect(info.x, info.y, info.w, info.h)

        surface.SetTextColor(255,255,0,255)
        surface.SetTextPos(10, 30)
        surface.DrawText(string.format("Панель: %s | Позиция: %d, %d | Размер: %d, %d",
            info.class, info.x, info.y, info.w, info.h))
    end

    if self.drawing and self.startPos and self.currentPos then
        surface.SetDrawColor(0,255,0,255)
        if self.mode == "rect" then
            local x1 = self.startPos.x
            local y1 = self.startPos.y
            local x2 = self.currentPos.x
            local y2 = self.currentPos.y
            surface.DrawOutlinedRect(math.min(x1, x2), math.min(y1, y2), math.abs(x2-x1), math.abs(y2-y1))
            surface.SetTextColor(0,255,0,255)
            surface.SetTextPos(x2+5, y2+5)
            surface.DrawText(string.format("%d x %d", math.abs(x2-x1), math.abs(y2-y1)))
        elseif self.mode == "circle" then
            local cx, cy = self.startPos.x, self.startPos.y
            local dx, dy = self.currentPos.x - cx, self.currentPos.y - cy
            local radius = math.sqrt(dx*dx + dy*dy)
            local segments = 36
            local points = {}
            for i = 0, segments do
                local a = 2 * math.pi * i / segments
                table.insert(points, {x = cx + radius * math.cos(a), y = cy + radius * math.sin(a)})
            end
            surface.DrawPoly(points)
            surface.DrawLine(cx, cy, self.currentPos.x, self.currentPos.y)
            surface.SetTextColor(0,255,0,255)
            surface.SetTextPos(self.currentPos.x+5, self.currentPos.y+5)
            surface.DrawText(string.format("r = %d", math.floor(radius)))
        end
    end

    if self.selectedInfo then
        surface.SetTextColor(255,255,255,255)
        surface.SetTextPos(10, 50)
        surface.DrawText("Выделено: " .. self:FormatInfo(self.selectedInfo))
    end

    surface.SetTextColor(200,200,200,255)
    surface.SetTextPos(10, ScrH() - 60)
    surface.DrawText("GUI Helper ВКЛ | Режим: " .. self.mode)
    surface.SetTextPos(10, ScrH() - 45)
    surface.DrawText("Команды: guihelper_mode <info|rect|circle>, guihelper_copy, guihelper_output")
end