local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local CoreGui = game:GetService("CoreGui")
local UIS = game:GetService("UserInputService")

local LocalPlayer = Players.LocalPlayer
local Camera = workspace.CurrentCamera

-- =====================
-- CONFIG
-- =====================
local Config = {
    Enabled          = true,
    Boxes            = true,
    NameTags         = true,
    HealthBar        = true,
    Distance         = true,
    Tracers          = true,
    TeamCheck        = true,
    VisibilityCheck  = true,

    ColorVisible     = Color3.fromRGB(180, 200, 255),   -- ghost blue
    ColorHidden      = Color3.fromRGB(80, 80, 120),     -- dim ghost
    ColorHealthHi    = Color3.fromRGB(140, 255, 180),   -- pale green
    ColorHealthLo    = Color3.fromRGB(255, 100, 120),   -- pale red
    ColorTracer      = Color3.fromRGB(160, 180, 255),
    ColorBox         = Color3.fromRGB(180, 200, 255),

    MaxDistance      = 1000,
    TracerOrigin     = "Bottom",
    BoxThickness     = 1,
    TextSize         = 13,
}

-- Ghost UI palette
local Ghost = {
    bg          = Color3.fromRGB(8, 8, 14),
    surface     = Color3.fromRGB(14, 14, 22),
    surfaceHi   = Color3.fromRGB(20, 20, 32),
    border      = Color3.fromRGB(50, 55, 90),
    accent      = Color3.fromRGB(120, 140, 220),
    accentDim   = Color3.fromRGB(60, 70, 130),
    accentGlow  = Color3.fromRGB(160, 180, 255),
    textPrimary = Color3.fromRGB(200, 210, 240),
    textMuted   = Color3.fromRGB(100, 110, 150),
    textDim     = Color3.fromRGB(60, 65, 100),
    onGreen     = Color3.fromRGB(80, 200, 140),
    onGreenDim  = Color3.fromRGB(30, 80, 60),
    offGray     = Color3.fromRGB(28, 28, 40),
    danger      = Color3.fromRGB(200, 80, 100),
}

-- =====================
-- ESP STORAGE
-- =====================
local ESPObjects = {}

-- =====================
-- DRAWING HELPERS
-- =====================
local function newDrawing(type, props)
    local d = Drawing.new(type)
    for k, v in pairs(props) do d[k] = v end
    return d
end

local function createESP(player)
    if ESPObjects[player] then return end
    local obj = {}

    obj.BoxLines = {}
    for i = 1, 4 do
        obj.BoxLines[i] = newDrawing("Line", {
            Visible = false,
            Color = Config.ColorBox,
            Thickness = Config.BoxThickness,
            Transparency = 1,
            ZIndex = 2,
        })
    end

    -- Corner accents (4 corners x 2 lines = 8)
    obj.Corners = {}
    for i = 1, 8 do
        obj.Corners[i] = newDrawing("Line", {
            Visible = false,
            Color = Config.ColorBox,
            Thickness = 2,
            Transparency = 1,
            ZIndex = 3,
        })
    end

    obj.NameTag = newDrawing("Text", {
        Visible = false,
        Color = Ghost.textPrimary,
        Size = Config.TextSize,
        Center = true,
        Outline = true,
        OutlineColor = Color3.fromRGB(0, 0, 0),
        ZIndex = 4,
    })

    obj.DistLabel = newDrawing("Text", {
        Visible = false,
        Color = Ghost.textMuted,
        Size = Config.TextSize - 2,
        Center = true,
        Outline = true,
        OutlineColor = Color3.fromRGB(0, 0, 0),
        ZIndex = 4,
    })

    obj.HealthBarBG = newDrawing("Line", {
        Visible = false,
        Color = Color3.fromRGB(0, 0, 0),
        Thickness = 4,
        Transparency = 1,
        ZIndex = 2,
    })

    obj.HealthBar = newDrawing("Line", {
        Visible = false,
        Color = Config.ColorHealthHi,
        Thickness = 3,
        Transparency = 1,
        ZIndex = 3,
    })

    obj.Tracer = newDrawing("Line", {
        Visible = false,
        Color = Config.ColorTracer,
        Thickness = 1,
        Transparency = 0.6,
        ZIndex = 1,
    })

    ESPObjects[player] = obj
end

local function removeESP(player)
    local obj = ESPObjects[player]
    if not obj then return end
    for _, line in pairs(obj.BoxLines) do line:Remove() end
    for _, line in pairs(obj.Corners) do line:Remove() end
    obj.NameTag:Remove()
    obj.DistLabel:Remove()
    obj.HealthBarBG:Remove()
    obj.HealthBar:Remove()
    obj.Tracer:Remove()
    ESPObjects[player] = nil
end

local function hideESP(obj)
    for _, line in pairs(obj.BoxLines) do line.Visible = false end
    for _, line in pairs(obj.Corners) do line.Visible = false end
    obj.NameTag.Visible = false
    obj.DistLabel.Visible = false
    obj.HealthBarBG.Visible = false
    obj.HealthBar.Visible = false
    obj.Tracer.Visible = false
end

-- =====================
-- VISIBILITY CHECK
-- =====================
local function isVisible(character, rootPart)
    if not Config.VisibilityCheck then return true end
    local origin = Camera.CFrame.Position
    local direction = rootPart.Position - origin
    local params = RaycastParams.new()
    params.FilterType = Enum.RaycastFilterType.Exclude
    params.FilterDescendantsInstances = {character, LocalPlayer.Character}
    local result = workspace:Raycast(origin, direction, params)
    return result == nil
end

-- =====================
-- TEAM CHECK
-- =====================
local function isSameTeam(player)
    if not Config.TeamCheck then return false end
    if not LocalPlayer.Team or not player.Team then return false end
    return LocalPlayer.Team == player.Team
end

-- =====================
-- HEALTH COLOR
-- =====================
local function healthColor(pct)
    return Config.ColorHealthLo:Lerp(Config.ColorHealthHi, pct)
end

-- =====================
-- BOUNDING BOX
-- =====================
local function getCharacterBounds(character)
    local parts = {}
    for _, p in pairs(character:GetDescendants()) do
        if p:IsA("BasePart") then
            table.insert(parts, p)
        end
    end
    if #parts == 0 then return nil end

    local minX, minY, minZ = math.huge, math.huge, math.huge
    local maxX, maxY, maxZ = -math.huge, -math.huge, -math.huge

    for _, p in pairs(parts) do
        local pos = p.Position
        local s = p.Size / 2
        minX = math.min(minX, pos.X - s.X)
        minY = math.min(minY, pos.Y - s.Y)
        minZ = math.min(minZ, pos.Z - s.Z)
        maxX = math.max(maxX, pos.X + s.X)
        maxY = math.max(maxY, pos.Y + s.Y)
        maxZ = math.max(maxZ, pos.Z + s.Z)
    end

    local corners3D = {
        Vector3.new(minX, minY, minZ), Vector3.new(minX, minY, maxZ),
        Vector3.new(minX, maxY, minZ), Vector3.new(minX, maxY, maxZ),
        Vector3.new(maxX, minY, minZ), Vector3.new(maxX, minY, maxZ),
        Vector3.new(maxX, maxY, minZ), Vector3.new(maxX, maxY, maxZ),
    }

    local screenPoints = {}
    for _, corner in pairs(corners3D) do
        local sp, onScreen = Camera:WorldToViewportPoint(corner)
        if onScreen then
            table.insert(screenPoints, Vector2.new(sp.X, sp.Y))
        end
    end

    if #screenPoints < 2 then return nil end

    local sMinX, sMinY = math.huge, math.huge
    local sMaxX, sMaxY = -math.huge, -math.huge

    for _, p in pairs(screenPoints) do
        sMinX = math.min(sMinX, p.X)
        sMinY = math.min(sMinY, p.Y)
        sMaxX = math.max(sMaxX, p.X)
        sMaxY = math.max(sMaxY, p.Y)
    end

    return {
        topLeft     = Vector2.new(sMinX, sMinY),
        topRight    = Vector2.new(sMaxX, sMinY),
        bottomLeft  = Vector2.new(sMinX, sMaxY),
        bottomRight = Vector2.new(sMaxX, sMaxY),
        width       = sMaxX - sMinX,
        height      = sMaxY - sMinY,
        centerX     = sMinX + (sMaxX - sMinX) / 2,
    }
end

-- =====================
-- CORNER BOX HELPER
-- =====================
local function drawCornerBox(obj, bounds, color)
    local tl = bounds.topLeft
    local tr = bounds.topRight
    local bl = bounds.bottomLeft
    local br = bounds.bottomRight
    local w = bounds.width * 0.25
    local h = bounds.height * 0.25

    -- TL
    obj.Corners[1].From = tl;                         obj.Corners[1].To = Vector2.new(tl.X + w, tl.Y)
    obj.Corners[2].From = tl;                         obj.Corners[2].To = Vector2.new(tl.X, tl.Y + h)
    -- TR
    obj.Corners[3].From = tr;                         obj.Corners[3].To = Vector2.new(tr.X - w, tr.Y)
    obj.Corners[4].From = tr;                         obj.Corners[4].To = Vector2.new(tr.X, tr.Y + h)
    -- BL
    obj.Corners[5].From = bl;                         obj.Corners[5].To = Vector2.new(bl.X + w, bl.Y)
    obj.Corners[6].From = bl;                         obj.Corners[6].To = Vector2.new(bl.X, bl.Y - h)
    -- BR
    obj.Corners[7].From = br;                         obj.Corners[7].To = Vector2.new(br.X - w, br.Y)
    obj.Corners[8].From = br;                         obj.Corners[8].To = Vector2.new(br.X, br.Y - h)

    for _, c in pairs(obj.Corners) do
        c.Color = color
        c.Visible = true
    end

    -- Faint full box behind corners
    obj.BoxLines[1].From = tl; obj.BoxLines[1].To = tr
    obj.BoxLines[2].From = bl; obj.BoxLines[2].To = br
    obj.BoxLines[3].From = tl; obj.BoxLines[3].To = bl
    obj.BoxLines[4].From = tr; obj.BoxLines[4].To = br

    for _, line in pairs(obj.BoxLines) do
        line.Color = Color3.new(color.R * 0.3, color.G * 0.3, color.B * 0.3)
        line.Thickness = 1
        line.Visible = true
    end
end

-- =====================
-- RENDER LOOP
-- =====================
RunService.RenderStepped:Connect(function()
    if not Config.Enabled then
        for _, obj in pairs(ESPObjects) do hideESP(obj) end
        return
    end

    for player, obj in pairs(ESPObjects) do
        if not player or not player.Parent then
            removeESP(player)
            continue
        end

        local character = player.Character
        local humanoid  = character and character:FindFirstChildOfClass("Humanoid")
        local rootPart  = character and character:FindFirstChild("HumanoidRootPart")

        if not character or not humanoid or not rootPart or humanoid.Health <= 0 then
            hideESP(obj)
            continue
        end

        if isSameTeam(player) then
            hideESP(obj)
            continue
        end

        local myRoot = LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart")
        if not myRoot then hideESP(obj) continue end

        local dist = math.floor((rootPart.Position - myRoot.Position).Magnitude)
        if dist > Config.MaxDistance then hideESP(obj) continue end

        local screenPos, onScreen = Camera:WorldToViewportPoint(rootPart.Position)
        if not onScreen then hideESP(obj) continue end

        local visible  = isVisible(character, rootPart)
        local espColor = visible and Config.ColorVisible or Config.ColorHidden
        local bounds   = getCharacterBounds(character)
        local hpPct    = math.clamp(humanoid.Health / humanoid.MaxHealth, 0, 1)
        local hpColor  = healthColor(hpPct)

        -- Box
        if Config.Boxes and bounds then
            drawCornerBox(obj, bounds, espColor)
        else
            for _, l in pairs(obj.BoxLines) do l.Visible = false end
            for _, l in pairs(obj.Corners) do l.Visible = false end
        end

        -- Name tag
        if Config.NameTags and bounds then
            obj.NameTag.Text = player.Name
            obj.NameTag.Position = Vector2.new(bounds.centerX, bounds.topLeft.Y - 16)
            obj.NameTag.Color = visible and Ghost.textPrimary or Ghost.textDim
            obj.NameTag.Visible = true
        else
            obj.NameTag.Visible = false
        end

        -- Distance
        if Config.Distance and bounds then
            obj.DistLabel.Text = "[" .. dist .. "m]"
            obj.DistLabel.Position = Vector2.new(bounds.centerX, bounds.bottomLeft.Y + 4)
            obj.DistLabel.Color = visible and Ghost.textMuted or Ghost.textDim
            obj.DistLabel.Visible = true
        else
            obj.DistLabel.Visible = false
        end

        -- Health bar
        if Config.HealthBar and bounds then
            local barX   = bounds.topLeft.X - 6
            local barTop = bounds.topLeft.Y
            local barBot = bounds.bottomLeft.Y
            local fillTop = barBot - (barBot - barTop) * hpPct

            obj.HealthBarBG.From = Vector2.new(barX, barTop)
            obj.HealthBarBG.To   = Vector2.new(barX, barBot)
            obj.HealthBarBG.Color = Color3.fromRGB(0, 0, 0)
            obj.HealthBarBG.Visible = true

            obj.HealthBar.From = Vector2.new(barX, fillTop)
            obj.HealthBar.To   = Vector2.new(barX, barBot)
            obj.HealthBar.Color = hpColor
            obj.HealthBar.Visible = true
        else
            obj.HealthBarBG.Visible = false
            obj.HealthBar.Visible = false
        end

        -- Tracer
        if Config.Tracers then
            local vp = Camera.ViewportSize
            local origin = Vector2.new(vp.X / 2, vp.Y)
            if Config.TracerOrigin == "Mouse" then
                local mp = UIS:GetMouseLocation()
                origin = Vector2.new(mp.X, mp.Y)
            end
            obj.Tracer.From = origin
            obj.Tracer.To   = Vector2.new(screenPos.X, screenPos.Y)
            obj.Tracer.Color = espColor
            obj.Tracer.Transparency = visible and 0.5 or 0.8
            obj.Tracer.Visible = true
        else
            obj.Tracer.Visible = false
        end
    end
end)

-- =====================
-- PLAYER MANAGEMENT
-- =====================
local function onPlayerAdded(player)
    if player == LocalPlayer then return end
    createESP(player)
end

local function onPlayerRemoving(player)
    removeESP(player)
end

for _, p in pairs(Players:GetPlayers()) do onPlayerAdded(p) end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)

-- =====================
-- GHOST MENU UI
-- =====================
local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Name = "GhostMenu"
ScreenGui.Parent = CoreGui
ScreenGui.ResetOnSpawn = false
ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling

-- Invisible anchor — menu appears to float
local MainFrame = Instance.new("Frame")
MainFrame.Parent = ScreenGui
MainFrame.BackgroundColor3 = Ghost.bg
MainFrame.BackgroundTransparency = 0.08
MainFrame.Position = UDim2.new(0, 20, 0.5, -230)
MainFrame.Size = UDim2.new(0, 215, 0, 420)
MainFrame.Active = true
MainFrame.Draggable = true
MainFrame.BorderSizePixel = 0
local MFC = Instance.new("UICorner"); MFC.CornerRadius = UDim.new(0, 10); MFC.Parent = MainFrame

-- Subtle border stroke
local BorderStroke = Instance.new("UIStroke")
BorderStroke.Thickness = 1
BorderStroke.Color = Ghost.border
BorderStroke.Transparency = 0.4
BorderStroke.Parent = MainFrame

-- Faint inner glow line at top
local GlowBar = Instance.new("Frame")
GlowBar.Parent = MainFrame
GlowBar.BackgroundColor3 = Ghost.accent
GlowBar.BackgroundTransparency = 0.7
GlowBar.BorderSizePixel = 0
GlowBar.Position = UDim2.new(0.1, 0, 0, 0)
GlowBar.Size = UDim2.new(0.8, 0, 0, 1)

-- Animated glow bar
spawn(function()
    local t = 0
    while MainFrame and MainFrame.Parent do
        t += 0.05
        local alpha = 0.4 + math.sin(t) * 0.3
        GlowBar.BackgroundTransparency = alpha
        task.wait(0.05)
    end
end)

-- Top bar
local TopBar = Instance.new("Frame")
TopBar.Parent = MainFrame
TopBar.BackgroundTransparency = 1
TopBar.Size = UDim2.new(1, 0, 0, 36)
TopBar.BorderSizePixel = 0

-- Ghost icon + title
local TitleLbl = Instance.new("TextLabel")
TitleLbl.Parent = TopBar
TitleLbl.BackgroundTransparency = 1
TitleLbl.Position = UDim2.new(0, 12, 0, 0)
TitleLbl.Size = UDim2.new(1, -80, 1, 0)
TitleLbl.Font = Enum.Font.GothamBold
TitleLbl.Text = "👻  Ghost Menu"
TitleLbl.TextColor3 = Ghost.accentGlow
TitleLbl.TextSize = 13
TitleLbl.TextXAlignment = Enum.TextXAlignment.Left
TitleLbl.TextTransparency = 0.1

-- Subtitle
local SubLbl = Instance.new("TextLabel")
SubLbl.Parent = TopBar
SubLbl.BackgroundTransparency = 1
SubLbl.Position = UDim2.new(0, 12, 0, 18)
SubLbl.Size = UDim2.new(1, -80, 0, 14)
SubLbl.Font = Enum.Font.Gotham
SubLbl.Text = "Advanced ESP"
SubLbl.TextColor3 = Ghost.textDim
SubLbl.TextSize = 10
SubLbl.TextXAlignment = Enum.TextXAlignment.Left

-- Divider
local Divider = Instance.new("Frame")
Divider.Parent = MainFrame
Divider.BackgroundColor3 = Ghost.border
Divider.BackgroundTransparency = 0.6
Divider.BorderSizePixel = 0
Divider.Position = UDim2.new(0, 10, 0, 36)
Divider.Size = UDim2.new(1, -20, 0, 1)

-- Top buttons
local function makeTopBtn(text, posX, color)
    local b = Instance.new("TextButton")
    b.Parent = TopBar
    b.BackgroundColor3 = color or Ghost.surfaceHi
    b.BackgroundTransparency = 0.2
    b.Position = UDim2.new(1, posX, 0, 7)
    b.Size = UDim2.new(0, 20, 0, 20)
    b.Font = Enum.Font.GothamBold
    b.Text = text
    b.TextColor3 = Ghost.textMuted
    b.TextSize = 11
    b.BorderSizePixel = 0
    local c = Instance.new("UICorner"); c.CornerRadius = UDim.new(0, 4); c.Parent = b
    local s = Instance.new("UIStroke"); s.Thickness = 1; s.Color = Ghost.border; s.Transparency = 0.5; s.Parent = b
    return b
end

local MinBtn   = makeTopBtn("—", -48, Ghost.surfaceHi)
local CloseBtn = makeTopBtn("×", -24, Ghost.danger)
CloseBtn.TextColor3 = Color3.fromRGB(255, 255, 255)

-- Scroll
local Scroll = Instance.new("ScrollingFrame")
Scroll.Parent = MainFrame
Scroll.Position = UDim2.new(0, 0, 0, 40)
Scroll.Size = UDim2.new(1, 0, 1, -40)
Scroll.BackgroundTransparency = 1
Scroll.BorderSizePixel = 0
Scroll.ScrollBarThickness = 2
Scroll.ScrollBarImageColor3 = Ghost.accentDim
Scroll.CanvasSize = UDim2.new(0, 0, 0, 0)
Scroll.AutomaticCanvasSize = Enum.AutomaticSize.Y
Scroll.ClipsDescendants = true

local ScrollLayout = Instance.new("UIListLayout")
ScrollLayout.Parent = Scroll
ScrollLayout.SortOrder = Enum.SortOrder.LayoutOrder
ScrollLayout.Padding = UDim.new(0, 3)

local ScrollPad = Instance.new("UIPadding")
ScrollPad.Parent = Scroll
ScrollPad.PaddingTop = UDim.new(0, 8)
ScrollPad.PaddingLeft = UDim.new(0, 10)
ScrollPad.PaddingRight = UDim.new(0, 10)
ScrollPad.PaddingBottom = UDim.new(0, 10)

-- Section label
local function makeSectionLabel(text, order)
    local wrapper = Instance.new("Frame")
    wrapper.Parent = Scroll
    wrapper.BackgroundTransparency = 1
    wrapper.Size = UDim2.new(1, 0, 0, 22)
    wrapper.LayoutOrder = order

    local l = Instance.new("TextLabel")
    l.Parent = wrapper
    l.BackgroundTransparency = 1
    l.Size = UDim2.new(1, 0, 1, 0)
    l.Font = Enum.Font.GothamBold
    l.Text = text
    l.TextColor3 = Ghost.accentDim
    l.TextSize = 10
    l.TextXAlignment = Enum.TextXAlignment.Left
    l.TextTransparency = 0.2

    local line = Instance.new("Frame")
    line.Parent = wrapper
    line.BackgroundColor3 = Ghost.accentDim
    line.BackgroundTransparency = 0.7
    line.BorderSizePixel = 0
    line.Position = UDim2.new(0, 0, 1, -1)
    line.Size = UDim2.new(1, 0, 0, 1)
end

-- Toggle row
local function makeToggleRow(label, order, currentVal, callback)
    local row = Instance.new("Frame")
    row.Parent = Scroll
    row.BackgroundColor3 = Ghost.surface
    row.BackgroundTransparency = 0.3
    row.Size = UDim2.new(1, 0, 0, 30)
    row.BorderSizePixel = 0
    row.LayoutOrder = order
    local RC = Instance.new("UICorner"); RC.CornerRadius = UDim.new(0, 6); RC.Parent = row
    local RS = Instance.new("UIStroke"); RS.Thickness = 1; RS.Color = Ghost.border; RS.Transparency = 0.7; RS.Parent = row

    local lbl = Instance.new("TextLabel")
    lbl.Parent = row
    lbl.BackgroundTransparency = 1
    lbl.Position = UDim2.new(0, 10, 0, 0)
    lbl.Size = UDim2.new(1, -60, 1, 0)
    lbl.Font = Enum.Font.Gotham
    lbl.Text = label
    lbl.TextColor3 = Ghost.textPrimary
    lbl.TextSize = 12
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.TextTransparency = 0.1

    -- Pill toggle
    local pill = Instance.new("Frame")
    pill.Parent = row
    pill.Position = UDim2.new(1, -46, 0.5, -9)
    pill.Size = UDim2.new(0, 36, 0, 18)
    pill.BorderSizePixel = 0
    local PC = Instance.new("UICorner"); PC.CornerRadius = UDim.new(1, 0); PC.Parent = pill
    local PS = Instance.new("UIStroke"); PS.Thickness = 1; PS.Color = Ghost.border; PS.Transparency = 0.5; PS.Parent = pill

    local knob = Instance.new("Frame")
    knob.Parent = pill
    knob.Size = UDim2.new(0, 12, 0, 12)
    knob.BorderSizePixel = 0
    local KC = Instance.new("UICorner"); KC.CornerRadius = UDim.new(1, 0); KC.Parent = knob

    local function refresh(val)
        TweenService:Create(pill, TweenInfo.new(0.2), {
            BackgroundColor3 = val and Ghost.onGreenDim or Ghost.offGray
        }):Play()
        TweenService:Create(knob, TweenInfo.new(0.2), {
            Position = val and UDim2.new(1, -15, 0.5, -6) or UDim2.new(0, 3, 0.5, -6),
            BackgroundColor3 = val and Ghost.onGreen or Ghost.textDim
        }):Play()
    end

    refresh(currentVal)

    local btn = Instance.new("TextButton")
    btn.Parent = row
    btn.BackgroundTransparency = 1
    btn.Size = UDim2.new(1, 0, 1, 0)
    btn.Text = ""
    btn.ZIndex = 5

    btn.MouseButton1Click:Connect(function()
        currentVal = not currentVal
        refresh(currentVal)
        callback(currentVal)
    end)

    -- Hover glow
    btn.MouseEnter:Connect(function()
        TweenService:Create(row, TweenInfo.new(0.15), {BackgroundTransparency = 0.1}):Play()
    end)
    btn.MouseLeave:Connect(function()
        TweenService:Create(row, TweenInfo.new(0.15), {BackgroundTransparency = 0.3}):Play()
    end)
end

-- Slider row
local function makeSliderRow(label, order, min, max, current, callback)
    local row = Instance.new("Frame")
    row.Parent = Scroll
    row.BackgroundColor3 = Ghost.surface
    row.BackgroundTransparency = 0.3
    row.Size = UDim2.new(1, 0, 0, 48)
    row.BorderSizePixel = 0
    row.LayoutOrder = order
    local RC = Instance.new("UICorner"); RC.CornerRadius = UDim.new(0, 6); RC.Parent = row
    local RS = Instance.new("UIStroke"); RS.Thickness = 1; RS.Color = Ghost.border; RS.Transparency = 0.7; RS.Parent = row

    local lbl = Instance.new("TextLabel")
    lbl.Parent = row
    lbl.BackgroundTransparency = 1
    lbl.Position = UDim2.new(0, 10, 0, 5)
    lbl.Size = UDim2.new(1, -20, 0, 16)
    lbl.Font = Enum.Font.Gotham
    lbl.Text = label .. ":  " .. current
    lbl.TextColor3 = Ghost.textPrimary
    lbl.TextSize = 12
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.TextTransparency = 0.1

    local trackBG = Instance.new("Frame")
    trackBG.Parent = row
    trackBG.Position = UDim2.new(0, 10, 0, 30)
    trackBG.Size = UDim2.new(1, -20, 0, 5)
    trackBG.BackgroundColor3 = Ghost.surfaceHi
    trackBG.BorderSizePixel = 0
    local TBC = Instance.new("UICorner"); TBC.CornerRadius = UDim.new(1, 0); TBC.Parent = trackBG

    local trackFill = Instance.new("Frame")
    trackFill.Parent = trackBG
    trackFill.BackgroundColor3 = Ghost.accent
    trackFill.BorderSizePixel = 0
    trackFill.Size = UDim2.new((current - min) / (max - min), 0, 1, 0)
    local TFC = Instance.new("UICorner"); TFC.CornerRadius = UDim.new(1, 0); TFC.Parent = trackFill

    local thumb = Instance.new("Frame")
    thumb.Parent = trackBG
    thumb.BackgroundColor3 = Ghost.accentGlow
    thumb.BorderSizePixel = 0
    thumb.AnchorPoint = Vector2.new(0.5, 0.5)
    thumb.Size = UDim2.new(0, 10, 0, 10)
    thumb.Position = UDim2.new((current - min) / (max - min), 0, 0.5, 0)
    local ThC = Instance.new("UICorner"); ThC.CornerRadius = UDim.new(1, 0); ThC.Parent = thumb

    local dragging = false

    local function updateSlider(x)
        local abs  = trackBG.AbsolutePosition.X
        local size = trackBG.AbsoluteSize.X
        local pct  = math.clamp((x - abs) / size, 0, 1)
        local val  = math.floor(min + (max - min) * pct)
        trackFill.Size = UDim2.new(pct, 0, 1, 0)
        thumb.Position = UDim2.new(pct, 0, 0.5, 0)
        lbl.Text = label .. ":  " .. val
        callback(val)
    end

    trackBG.InputBegan:Connect(function(inp)
        if inp.UserInputType == Enum.UserInputType.MouseButton1 then
            dragging = true
            updateSlider(inp.Position.X)
        end
    end)
    UIS.InputEnded:Connect(function(inp)
        if inp.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false end
    end)
    UIS.InputChanged:Connect(function(inp)
        if dragging and inp.UserInputType == Enum.UserInputType.MouseMovement then
            updateSlider(inp.Position.X)
        end
    end)
end

-- Tracer origin cycler
local function makeTracerOriginRow(order)
    local row = Instance.new("Frame")
    row.Parent = Scroll
    row.BackgroundColor3 = Ghost.surface
    row.BackgroundTransparency = 0.3
    row.Size = UDim2.new(1, 0, 0, 30)
    row.BorderSizePixel = 0
    row.LayoutOrder = order
    local RC = Instance.new("UICorner"); RC.CornerRadius = UDim.new(0, 6); RC.Parent = row
    local RS = Instance.new("UIStroke"); RS.Thickness = 1; RS.Color = Ghost.border; RS.Transparency = 0.7; RS.Parent = row

    local lbl = Instance.new("TextLabel")
    lbl.Parent = row
    lbl.BackgroundTransparency = 1
    lbl.Position = UDim2.new(0, 10, 0, 0)
    lbl.Size = UDim2.new(0, 90, 1, 0)
    lbl.Font = Enum.Font.Gotham
    lbl.Text = "Tracer From"
    lbl.TextColor3 = Ghost.textPrimary
    lbl.TextSize = 12
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.TextTransparency = 0.1

    local options = {"Bottom", "Mouse"}
    local current = 1

    local btn = Instance.new("TextButton")
    btn.Parent = row
    btn.BackgroundColor3 = Ghost.surfaceHi
    btn.BackgroundTransparency = 0.2
    btn.Position = UDim2.new(1, -88, 0.5, -10)
    btn.Size = UDim2.new(0, 78, 0, 20)
    btn.Font = Enum.Font.GothamBold
    btn.Text = "⟳  " .. options[current]
    btn.TextColor3 = Ghost.accentGlow
    btn.TextSize = 11
    btn.BorderSizePixel = 0
    local BC = Instance.new("UICorner"); BC.CornerRadius = UDim.new(0, 4); BC.Parent = btn
    local BS = Instance.new("UIStroke"); BS.Thickness = 1; BS.Color = Ghost.border; BS.Transparency = 0.6; BS.Parent = btn

    btn.MouseButton1Click:Connect(function()
        current = current == 1 and 2 or 1
        Config.TracerOrigin = options[current]
        btn.Text = "⟳  " .. options[current]
    end)
end

-- Build UI
makeSectionLabel("ELEMENTS", 1)
makeToggleRow("ESP Enabled",      2,  Config.Enabled,          function(v) Config.Enabled = v end)
makeToggleRow("Corner Boxes",     3,  Config.Boxes,            function(v) Config.Boxes = v end)
makeToggleRow("Name Tags",        4,  Config.NameTags,         function(v) Config.NameTags = v end)
makeToggleRow("Health Bar",       5,  Config.HealthBar,        function(v) Config.HealthBar = v end)
makeToggleRow("Distance",         6,  Config.Distance,         function(v) Config.Distance = v end)
makeToggleRow("Tracers",          7,  Config.Tracers,          function(v) Config.Tracers = v end)

makeSectionLabel("FILTERS", 8)
makeToggleRow("Team Check",       9,  Config.TeamCheck,         function(v) Config.TeamCheck = v end)
makeToggleRow("Visibility Check", 10, Config.VisibilityCheck,   function(v) Config.VisibilityCheck = v end)
makeTracerOriginRow(11)
makeSliderRow("Max Distance",     12, 50, 2000, Config.MaxDistance, function(v) Config.MaxDistance = v end)

-- Minimize
local minimized = false
MinBtn.MouseButton1Click:Connect(function()
    minimized = not minimized
    Scroll.Visible = not minimized
    Divider.Visible = not minimized
    TweenService:Create(MainFrame, TweenInfo.new(0.2), {
        Size = minimized and UDim2.new(0, 215, 0, 38) or UDim2.new(0, 215, 0, 420)
    }):Play()
    MinBtn.Text = minimized and "+" or "—"
end)

-- Close
CloseBtn.MouseButton1Click:Connect(function()
    for player, _ in pairs(ESPObjects) do removeESP(player) end
    ScreenGui:Destroy()
end)

print("Ghost Menu loaded.")

-- ============================================================
-- GHOST ESP — PART VISIBILITY PATCH
-- Replaces: createESP, removeESP, hideESP, render loop
-- Each body part gets its own line drawn in screen space.
-- Green = visible to camera. Red = occluded.
-- Drop this block in place of the existing ESP sections.
-- ============================================================

-- Parts to track per character (name = display label)
local TRACKED_PARTS = {
    Head          = "Head",
    UpperTorso    = "Torso",   -- R15
    Torso         = "Torso",   -- R6
    RightUpperArm = "R.Arm",
    LeftUpperArm  = "L.Arm",
    RightUpperLeg = "R.Leg",
    LeftUpperLeg  = "L.Leg",
    RightHand     = "R.Hand",
    LeftHand      = "L.Hand",
    RightFoot     = "R.Foot",
    LeftFoot      = "L.Foot",
    HumanoidRootPart = "Root",
}

local COLOR_VISIBLE  = Color3.fromRGB(80,  220, 120)  -- green
local COLOR_OCCLUDED = Color3.fromRGB(220, 70,  70)   -- red
local COLOR_TEXT     = Color3.fromRGB(220, 220, 240)
local COLOR_TEXT_OCC = Color3.fromRGB(180, 80,  80)

-- ============================================================
-- ESP STORAGE (replaces old ESPObjects)
-- Structure per player:
--   obj.Parts[partName] = {
--       dot   = Drawing "Circle",
--       label = Drawing "Text",
--       line  = Drawing "Line",   (tracer to screen center)
--   }
--   obj.DistLabel = Drawing "Text"
--   obj.HealthBar / obj.HealthBarBG = Drawing "Line"
-- ============================================================
local ESPObjects = {}

local function newDrawing(t, props)
    local d = Drawing.new(t)
    for k,v in pairs(props) do d[k] = v end
    return d
end

local function createESP(player)
    if ESPObjects[player] then return end
    local obj = { Parts = {} }

    for partName, label in pairs(TRACKED_PARTS) do
        obj.Parts[partName] = {
            dot = newDrawing("Circle", {
                Visible     = false,
                Radius      = 4,
                Thickness   = 1.5,
                Color       = COLOR_VISIBLE,
                Filled      = true,
                ZIndex      = 4,
            }),
            outline = newDrawing("Circle", {
                Visible     = false,
                Radius      = 5,
                Thickness   = 1,
                Color       = Color3.fromRGB(0,0,0),
                Filled      = false,
                ZIndex      = 3,
            }),
            label = newDrawing("Text", {
                Visible     = false,
                Text        = label,
                Size        = 11,
                Color       = COLOR_TEXT,
                Center      = false,
                Outline     = true,
                OutlineColor= Color3.fromRGB(0,0,0),
                ZIndex      = 5,
            }),
            line = newDrawing("Line", {
                Visible     = false,
                Color       = COLOR_VISIBLE,
                Thickness   = 0.8,
                Transparency= 0.55,
                ZIndex      = 1,
            }),
        }
    end

    -- Name tag above head
    obj.NameTag = newDrawing("Text", {
        Visible     = false,
        Text        = player.Name,
        Size        = 13,
        Color       = Color3.fromRGB(200,210,240),
        Center      = true,
        Outline     = true,
        OutlineColor= Color3.fromRGB(0,0,0),
        ZIndex      = 6,
    })

    -- Distance label
    obj.DistLabel = newDrawing("Text", {
        Visible     = false,
        Text        = "",
        Size        = 11,
        Color       = Color3.fromRGB(120,130,160),
        Center      = true,
        Outline     = true,
        OutlineColor= Color3.fromRGB(0,0,0),
        ZIndex      = 5,
    })

    -- Health bar bg + fill
    obj.HealthBarBG = newDrawing("Line", {
        Visible     = false,
        Color       = Color3.fromRGB(0,0,0),
        Thickness   = 4,
        Transparency= 1,
        ZIndex      = 2,
    })
    obj.HealthBar = newDrawing("Line", {
        Visible     = false,
        Color       = Color3.fromRGB(80,220,120),
        Thickness   = 3,
        Transparency= 1,
        ZIndex      = 3,
    })

    ESPObjects[player] = obj
end

local function hideAllParts(obj)
    for _, p in pairs(obj.Parts) do
        p.dot.Visible     = false
        p.outline.Visible = false
        p.label.Visible   = false
        p.line.Visible    = false
    end
    obj.NameTag.Visible   = false
    obj.DistLabel.Visible = false
    obj.HealthBarBG.Visible = false
    obj.HealthBar.Visible   = false
end

local function removeESP(player)
    local obj = ESPObjects[player]
    if not obj then return end
    for _, p in pairs(obj.Parts) do
        p.dot:Remove()
        p.outline:Remove()
        p.label:Remove()
        p.line:Remove()
    end
    obj.NameTag:Remove()
    obj.DistLabel:Remove()
    obj.HealthBarBG:Remove()
    obj.HealthBar:Remove()
    ESPObjects[player] = nil
end

-- ============================================================
-- PER-PART VISIBILITY RAYCAST
-- Returns true if the part's centre is unoccluded from camera.
-- ============================================================
local function partIsVisible(character, part)
    local origin    = Camera.CFrame.Position
    local target    = part.Position
    local direction = target - origin
    local params    = RaycastParams.new()
    params.FilterType = Enum.RaycastFilterType.Exclude
    params.FilterDescendantsInstances = { character, LocalPlayer.Character }
    local result = workspace:Raycast(origin, direction, params)
    return result == nil
end

-- ============================================================
-- HEALTH COLOR LERP
-- ============================================================
local function hpColor(pct)
    return Color3.fromRGB(220,70,70):Lerp(Color3.fromRGB(80,220,120), pct)
end

-- ============================================================
-- RENDER LOOP  (replaces old RunService.RenderStepped block)
-- ============================================================
RunService.RenderStepped:Connect(function()
    if not Config.Enabled then
        for _, obj in pairs(ESPObjects) do hideAllParts(obj) end
        return
    end

    local vp = Camera.ViewportSize
    local screenCenter = Vector2.new(vp.X / 2, vp.Y)

    for player, obj in pairs(ESPObjects) do
        if not player or not player.Parent then
            removeESP(player); continue
        end

        local character = player.Character
        local humanoid  = character and character:FindFirstChildOfClass("Humanoid")
        local rootPart  = character and character:FindFirstChild("HumanoidRootPart")

        if not character or not humanoid or not rootPart or humanoid.Health <= 0 then
            hideAllParts(obj); continue
        end

        -- Team check
        if Config.TeamCheck then
            if LocalPlayer.Team and player.Team and LocalPlayer.Team == player.Team then
                hideAllParts(obj); continue
            end
        end

        -- Distance
        local myRoot = LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart")
        if not myRoot then hideAllParts(obj); continue end
        local dist = math.floor((rootPart.Position - myRoot.Position).Magnitude)
        if dist > Config.MaxDistance then hideAllParts(obj); continue end

        -- Root on screen check
        local rootScreen, rootOnScreen = Camera:WorldToViewportPoint(rootPart.Position)
        if not rootOnScreen then hideAllParts(obj); continue end

        local hpPct = math.clamp(humanoid.Health / humanoid.MaxHealth, 0, 1)

        -- Track screen bounds for name tag + health bar placement
        local minY, maxY = math.huge, -math.huge
        local minX, maxX = math.huge, -math.huge

        -- --------------------------------------------------------
        -- Per-part render
        -- --------------------------------------------------------
        for partName, drawings in pairs(obj.Parts) do
            local part = character:FindFirstChild(partName)
            if not part or not part:IsA("BasePart") then
                drawings.dot.Visible     = false
                drawings.outline.Visible = false
                drawings.label.Visible   = false
                drawings.line.Visible    = false
                continue
            end

            local sp, onScreen = Camera:WorldToViewportPoint(part.Position)
            if not onScreen then
                drawings.dot.Visible     = false
                drawings.outline.Visible = false
                drawings.label.Visible   = false
                drawings.line.Visible    = false
                continue
            end

            local sx, sy = sp.X, sp.Y

            -- Update bounds
            minY = math.min(minY, sy)
            maxY = math.max(maxY, sy)
            minX = math.min(minX, sx)
            maxX = math.max(maxX, sx)

            -- Visibility per part
            local visible = partIsVisible(character, part)
            local dotColor  = visible and COLOR_VISIBLE or COLOR_OCCLUDED
            local lineColor = visible and COLOR_VISIBLE or COLOR_OCCLUDED
            local txtColor  = visible and COLOR_TEXT    or COLOR_TEXT_OCC

            -- Dot
            drawings.dot.Position    = Vector2.new(sx, sy)
            drawings.dot.Color       = dotColor
            drawings.dot.Filled      = visible        -- solid = visible, hollow = hidden
            drawings.dot.Visible     = true

            -- Outline ring (always drawn for contrast)
            drawings.outline.Position = Vector2.new(sx, sy)
            drawings.outline.Visible  = true

            -- Label (offset right of dot)
            drawings.label.Position  = Vector2.new(sx + 7, sy - 5)
            drawings.label.Color     = txtColor
            drawings.label.Visible   = Config.NameTags

            -- Tracer line from screen center to part
            if Config.Tracers then
                local origin = screenCenter
                if Config.TracerOrigin == "Mouse" then
                    local mp = UIS:GetMouseLocation()
                    origin = Vector2.new(mp.X, mp.Y)
                end
                drawings.line.From    = origin
                drawings.line.To      = Vector2.new(sx, sy)
                drawings.line.Color   = lineColor
                drawings.line.Visible = true
            else
                drawings.line.Visible = false
            end
        end

        -- --------------------------------------------------------
        -- Name tag (above highest visible part)
        -- --------------------------------------------------------
        if Config.NameTags and minY < math.huge then
            obj.NameTag.Text     = player.Name
            obj.NameTag.Position = Vector2.new((minX + maxX) / 2, minY - 16)
            obj.NameTag.Visible  = true
        else
            obj.NameTag.Visible = false
        end

        -- --------------------------------------------------------
        -- Distance label (below lowest visible part)
        -- --------------------------------------------------------
        if Config.Distance and maxY > -math.huge then
            obj.DistLabel.Text     = "[" .. dist .. "m]"
            obj.DistLabel.Position = Vector2.new((minX + maxX) / 2, maxY + 5)
            obj.DistLabel.Visible  = true
        else
            obj.DistLabel.Visible = false
        end

        -- --------------------------------------------------------
        -- Health bar (left side of bounding column)
        -- --------------------------------------------------------
        if Config.HealthBar and minY < math.huge then
            local barX    = minX - 8
            local barTop  = minY
            local barBot  = maxY
            local fillTop = barBot - (barBot - barTop) * hpPct

            obj.HealthBarBG.From    = Vector2.new(barX, barTop)
            obj.HealthBarBG.To      = Vector2.new(barX, barBot)
            obj.HealthBarBG.Visible = true

            obj.HealthBar.From      = Vector2.new(barX, fillTop)
            obj.HealthBar.To        = Vector2.new(barX, barBot)
            obj.HealthBar.Color     = hpColor(hpPct)
            obj.HealthBar.Visible   = true
        else
            obj.HealthBarBG.Visible = false
            obj.HealthBar.Visible   = false
        end
    end
end)

-- ============================================================
-- PLAYER MANAGEMENT (unchanged API, safe to copy as-is)
-- ============================================================
local function onPlayerAdded(player)
    if player == LocalPlayer then return end
    createESP(player)
end

local function onPlayerRemoving(player)
    removeESP(player)
end

for _, p in pairs(Players:GetPlayers()) do onPlayerAdded(p) end
Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: