Загрузка данных


-- Double-load gate
if getgenv().FatalityWinLoaded then return end
getgenv().FatalityWinLoaded = true

-- Services
local game = game
local workspace = workspace
local players = game:GetService("Players")
local runService = game:GetService("RunService")
local userInputService = game:GetService("UserInputService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local debris = game:GetService("Debris")

local Vector3_new = Vector3.new
local CFrame_new = CFrame.new
local CFrame_Angles = CFrame.Angles
local math_random = math.random
local math_sin = math.sin
local math_clamp = math.clamp
local table_clear = table.clear
local task_wait = task.wait
local task_spawn = task.spawn
local delay = task.delay

local client = players.LocalPlayer
repeat task_wait(0.5) until client.Character and client.Character:FindFirstChild("HumanoidRootPart") and workspace.CurrentCamera

-- Cleanup system
local Cleanup = {}
Cleanup.__index = Cleanup

function Cleanup.new()
    return setmetatable({connections = {}, threads = {}, functions = {}}, Cleanup)
end

function Cleanup:addConnection(conn)
    table.insert(self.connections, conn)
    return conn
end

function Cleanup:addThread(thread)
    table.insert(self.threads, thread)
    return thread
end

function Cleanup:addCleanupFunction(f)
    table.insert(self.functions, f)
end

function Cleanup:cleanup()
    for _, conn in ipairs(self.connections) do
        if conn and conn.Connected then conn:Disconnect() end
    end
    for _, thread in ipairs(self.threads) do
        if coroutine.status(thread) ~= "dead" then coroutine.close(thread) end
    end
    for _, f in ipairs(self.functions) do pcall(f) end
    table_clear(self.connections)
    table_clear(self.threads)
    table_clear(self.functions)
end

-- Features state
local features = {
    hitboxExpander = {enabled = false, cleanup = nil},
    targetResolver = {enabled = false, cleanup = nil},
    ragebotCore = {enabled = false, cleanup = nil},
    spinbot = {enabled = false, cleanup = nil},
    jitterAA = {enabled = false, cleanup = nil},
    fakeLag = {enabled = false, cleanup = nil},
    chinaHat = {enabled = false, cleanup = nil},
    chams = {enabled = false, cleanup = nil},
    noclip = {enabled = false, cleanup = nil},
    infoNametags = {enabled = false, cleanup = nil},
    hitsounds = {enabled = false, type = "Skeet", volume = 2, cleanup = nil}
}

local settings = {
    walkspeed = 50,
    jumppower = 50,
}

-- Sound IDs Base
local SOUNDS_DB = {
    Skeet = "rbxassetid://4817809188",
    Neverlose = "rbxassetid://9114223611"
}

-- =================== UI CREATION ===================
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "FatalityWin"
screenGui.ResetOnSpawn = false
screenGui.Parent = client:WaitForChild("PlayerGui")

-- Floating home button
local floatButton = Instance.new("TextButton")
floatButton.Name = "FloatButton"
floatButton.Size = UDim2.new(0, 50, 0, 50)
floatButton.Position = UDim2.new(1, -60, 1, -120)
floatButton.BackgroundColor3 = Color3.fromRGB(15, 15, 45)
floatButton.BorderColor3 = Color3.fromRGB(255, 0, 150)
floatButton.BorderSizePixel = 2
floatButton.Text = "F"
floatButton.TextColor3 = Color3.fromRGB(255, 255, 255)
floatButton.Font = Enum.Font.SourceSansBold
floatButton.TextSize = 24
floatButton.ZIndex = 10
floatButton.Parent = screenGui
Instance.new("UICorner", floatButton).CornerRadius = UDim.new(0, 25)

-- Main menu frame
local mainFrame = Instance.new("Frame")
mainFrame.Name = "MainFrame"
mainFrame.Size = UDim2.new(0, 340, 0, 450)
mainFrame.Position = UDim2.new(0.5, -170, 0.5, -225)
mainFrame.BackgroundColor3 = Color3.fromRGB(12, 12, 40)
mainFrame.BorderSizePixel = 0
mainFrame.Visible = false
mainFrame.Parent = screenGui

-- Title bar
local titleBar = Instance.new("Frame")
titleBar.Size = UDim2.new(1, 0, 0, 30)
titleBar.BackgroundColor3 = Color3.fromRGB(20, 20, 50)
titleBar.BorderSizePixel = 0
titleBar.Parent = mainFrame

local titleText = Instance.new("TextLabel")
titleText.Text = "FATALITY.WIN (MOBILE HVH)"
titleText.Size = UDim2.new(1, -30, 1, 0)
titleText.Position = UDim2.new(0, 8, 0, 0)
titleText.BackgroundTransparency = 1
titleText.TextColor3 = Color3.fromRGB(255, 255, 255)
titleText.Font = Enum.Font.SourceSansBold
titleText.TextSize = 14
titleText.TextXAlignment = Enum.TextXAlignment.Left
titleText.Parent = titleBar

local closeButton = Instance.new("TextButton")
closeButton.Text = "X"
closeButton.Size = UDim2.new(0, 30, 1, 0)
closeButton.Position = UDim2.new(1, -30, 0, 0)
closeButton.BackgroundColor3 = Color3.fromRGB(255, 0, 150)
closeButton.TextColor3 = Color3.fromRGB(255, 255, 255)
closeButton.Font = Enum.Font.SourceSansBold
closeButton.TextSize = 14
closeButton.Parent = titleBar
closeButton.MouseButton1Click:Connect(function() mainFrame.Visible = false end)

-- Horizontal tab bar
local tabBar = Instance.new("Frame")
tabBar.Size = UDim2.new(1, 0, 0, 30)
tabBar.Position = UDim2.new(0, 0, 0, 30)
tabBar.BackgroundColor3 = Color3.fromRGB(20, 20, 50)
tabBar.BorderSizePixel = 0
tabBar.Parent = mainFrame

local tabLayout = Instance.new("UIListLayout")
tabLayout.FillDirection = Enum.FillDirection.Horizontal
tabLayout.Padding = UDim.new(0, 2)
tabLayout.Parent = tabBar

-- Content area
local contentArea = Instance.new("Frame")
contentArea.Size = UDim2.new(1, 0, 1, -60)
contentArea.Position = UDim2.new(0, 0, 0, 60)
contentArea.BackgroundTransparency = 1
contentArea.Parent = mainFrame

-- Tab definitions
local tabNames = {"RAGEBOT", "ANTI-AIM", "VISUALS", "MOVEMENT / MISC"}
local tabButtons, tabFrames, selectedTab = {}, {}, 1

local function createTabButton(name)
    local button = Instance.new("TextButton")
    button.Text = name
    button.Size = UDim2.new(0.25, -2, 1, 0)
    button.BackgroundColor3 = Color3.fromRGB(30, 30, 60)
    button.TextColor3 = Color3.fromRGB(200, 200, 200)
    button.Font = Enum.Font.SourceSansBold
    button.TextSize = 11
    button.BorderSizePixel = 0
    button.ZIndex = 5
    button.Parent = tabBar
    return button
end

local function createTabFrame()
    local scroll = Instance.new("ScrollingFrame")
    scroll.Size = UDim2.new(1, 0, 1, -10)
    scroll.BackgroundTransparency = 1
    scroll.BorderSizePixel = 0
    scroll.CanvasSize = UDim2.new(0, 0, 0, 0)
    scroll.ScrollBarThickness = 4
    scroll.ScrollBarImageColor3 = Color3.fromRGB(255, 0, 150)
    scroll.AutomaticCanvasSize = Enum.AutomaticSize.Y
    scroll.Visible = false
    scroll.Parent = contentArea

    local uiList = Instance.new("UIListLayout")
    uiList.SortOrder = Enum.SortOrder.LayoutOrder
    uiList.Padding = UDim.new(0, 6)
    uiList.Parent = scroll

    return scroll
end

for i, name in ipairs(tabNames) do
    table.insert(tabButtons, createTabButton(name))
    table.insert(tabFrames, createTabFrame())
end

tabFrames[1].Visible = true
tabButtons[1].BackgroundColor3 = Color3.fromRGB(255, 0, 150)
tabButtons[1].TextColor3 = Color3.fromRGB(255, 255, 255)

for i, button in ipairs(tabButtons) do
    button.MouseButton1Click:Connect(function()
        if selectedTab == i then return end
        tabButtons[selectedTab].BackgroundColor3 = Color3.fromRGB(30, 30, 60)
        tabButtons[selectedTab].TextColor3 = Color3.fromRGB(200, 200, 200)
        tabFrames[selectedTab].Visible = false
        selectedTab = i
        button.BackgroundColor3 = Color3.fromRGB(255, 0, 150)
        button.TextColor3 = Color3.fromRGB(255, 255, 255)
        tabFrames[i].Visible = true
    end)
end

-- =================== UI HELPERS ===================
local function createToggle(scrollFrame, name, callback, default)
    local row = Instance.new("Frame")
    row.Size = UDim2.new(1, -10, 0, 30)
    row.BackgroundTransparency = 1
    row.LayoutOrder = #scrollFrame:GetChildren() + 1
    row.Parent = scrollFrame

    local label = Instance.new("TextLabel")
    label.Text = name
    label.Size = UDim2.new(0.65, 0, 1, 0)
    label.Position = UDim2.new(0, 8, 0, 0)
    label.BackgroundTransparency = 1
    label.TextColor3 = Color3.fromRGB(255, 255, 255)
    label.Font = Enum.Font.SourceSans
    label.TextSize = 14
    label.TextXAlignment = Enum.TextXAlignment.Left
    label.Parent = row

    local toggleBtn = Instance.new("TextButton")
    toggleBtn.Size = UDim2.new(0, 45, 0, 22)
    toggleBtn.Position = UDim2.new(1, -55, 0.5, -11)
    toggleBtn.BackgroundColor3 = default and Color3.fromRGB(255, 0, 150) or Color3.fromRGB(45, 45, 70)
    toggleBtn.BorderSizePixel = 0
    toggleBtn.TextColor3 = Color3.fromRGB(255, 255, 255)
    toggleBtn.Font = Enum.Font.SourceSansBold
    toggleBtn.TextSize = 12
    toggleBtn.Parent = row
    Instance.new("UICorner", toggleBtn).CornerRadius = UDim.new(0, 4)

    local state = default or false
    toggleBtn.Text = state and "ON" or "OFF"
    toggleBtn.MouseButton1Click:Connect(function()
        state = not state
        toggleBtn.Text = state and "ON" or "OFF"
        toggleBtn.BackgroundColor3 = state and Color3.fromRGB(255, 0, 150) or Color3.fromRGB(45, 45, 70)
        callback(state)
    end)
    return toggleBtn
end

local function createSlider(scrollFrame, name, minVal, maxVal, defaultVal, callback)
    local row = Instance.new("Frame")
    row.Size = UDim2.new(1, -10, 0, 48)
    row.BackgroundTransparency = 1
    row.LayoutOrder = #scrollFrame:GetChildren() + 1
    row.Parent = scrollFrame

    local label = Instance.new("TextLabel")
    label.Text = name .. ": " .. tostring(defaultVal)
    label.Size = UDim2.new(1, -10, 0, 16)
    label.Position = UDim2.new(0, 8, 0, 0)
    label.BackgroundTransparency = 1
    label.TextColor3 = Color3.fromRGB(255, 255, 255)
    label.Font = Enum.Font.SourceSans
    label.TextSize = 13
    label.TextXAlignment = Enum.TextXAlignment.Left
    label.Parent = row

    local sliderFrame = Instance.new("Frame")
    sliderFrame.Size = UDim2.new(1, -20, 0, 10)
    sliderFrame.Position = UDim2.new(0, 10, 0, 26)
    sliderFrame.BackgroundColor3 = Color3.fromRGB(35, 35, 60)
    sliderFrame.BorderSizePixel = 0
    sliderFrame.Parent = row
    Instance.new("UICorner", sliderFrame).CornerRadius = UDim.new(0, 3)

    local fill = Instance.new("Frame")
    fill.Size = UDim2.new((defaultVal - minVal) / (maxVal - minVal), 0, 1, 0)
    fill.BackgroundColor3 = Color3.fromRGB(255, 0, 150)
    fill.BorderSizePixel = 0
    fill.Parent = sliderFrame
    Instance.new("UICorner", fill).CornerRadius = UDim.new(0, 3)

    local thumb = Instance.new("TextButton")
    thumb.Size = UDim2.new(0, 16, 0, 16)
    thumb.Position = UDim2.new((defaultVal - minVal) / (maxVal - minVal), -8, 0.5, -8)
    thumb.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
    thumb.Text = ""
    thumb.Parent = sliderFrame
    Instance.new("UICorner", thumb).CornerRadius = UDim.new(0, 8)

    local dragging = false
    thumb.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true end
    end)
    userInputService.InputEnded:Connect(function(input)
        if dragging and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then dragging = false end
    end)
    userInputService.InputChanged:Connect(function(input)
        if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then
            local pos = userInputService:GetMouseLocation()
            local relX = pos.X - sliderFrame.AbsolutePosition.X
            local ratio = math_clamp(relX / sliderFrame.AbsoluteSize.X, 0, 1)
            local value = math.floor(minVal + (maxVal - minVal) * ratio + 0.5)
            value = math_clamp(value, minVal, maxVal)
            thumb.Position = UDim2.new(ratio, -8, 0.5, -8)
            fill.Size = UDim2.new(ratio, 0, 1, 0)
            label.Text = name .. ": " .. tostring(value)
            callback(value)
        end
    end)
end

local function createDropdown(scrollFrame, name, options, callback)
    local row = Instance.new("Frame")
    row.Size = UDim2.new(1, -10, 0, 35)
    row.BackgroundTransparency = 1
    row.LayoutOrder = #scrollFrame:GetChildren() + 1
    row.Parent = scrollFrame

    local label = Instance.new("TextLabel")
    label.Text = name
    label.Size = UDim2.new(0.4, 0, 1, 0)
    label.Position = UDim2.new(0, 8, 0, 0)
    label.BackgroundTransparency = 1
    label.TextColor3 = Color3.fromRGB(255, 255, 255)
    label.Font = Enum.Font.SourceSans
    label.TextSize = 14
    label.TextXAlignment = Enum.TextXAlignment.Left
    label.Parent = row

    local btn = Instance.new("TextButton")
    btn.Size = UDim2.new(0.5, 0, 0, 24)
    btn.Position = UDim2.new(1, -180, 0.5, -12)
    btn.BackgroundColor3 = Color3.fromRGB(45, 45, 70)
    btn.Text = options[1]
    btn.TextColor3 = Color3.fromRGB(255, 255, 255)
    btn.Font = Enum.Font.SourceSansBold
    btn.TextSize = 12
    btn.Parent = row
    Instance.new("UICorner", btn).CornerRadius = UDim.new(0, 4)

    local currentIdx = 1
    btn.MouseButton1Click:Connect(function()
        currentIdx = currentIdx + 1
        if currentIdx > #options then currentIdx = 1 end
        btn.Text = options[currentIdx]
        callback(options[currentIdx])
    end)
end

-- ============== UTILITIES & GAME CHECKS ==============
local function getRole(player)
    if not player then return "Innocent" end
    local char = player.Character
    local bp = player:FindFirstChild("Backpack")
    
    local function check(parent)
        if not parent then return nil end
        for _, tool in ipairs(parent:GetChildren()) do
            if tool:IsA("Tool") then
                local n = tool.Name:lower()
                if n:find("knife") then return "Murderer" end
                if n:find("gun") then return "Sheriff" end
            end
        end
    end
    return check(char) or check(bp) or "Innocent"
end

local function getKnifeTool()
    local char = client.Character
    local bp = client:FindFirstChild("Backpack")
    if char then
        for _, tool in ipairs(char:GetChildren()) do
            if tool:IsA("Tool") and tool.Name:lower():find("knife") then return tool end
        end
    end
    if bp then
        for _, tool in ipairs(bp:GetChildren()) do
            if tool:IsA("Tool") and tool.Name:lower():find("knife") then return tool end
        end
    end
end

-- ============== FEATURE LOOPS ==============

-- Real Server-Sided Anti-Aims (Fixes Spinbot/Jitter for HvH)
local function startSpinbot()
    local cleanup = Cleanup.new()
    local conn = runService.Heartbeat:Connect(function()
        local char = client.Character
        local hrp = char and char:FindFirstChild("HumanoidRootPart")
        if hrp then
            -- Ротируем CFrame + ломаем сетевую скорость (AssemblyLinearVelocity) для десинхронизации хитбоксов
            hrp.CFrame = hrp.CFrame * CFrame_Angles(0, math.rad(45), 0)
            hrp.AssemblyLinearVelocity = Vector3_new(math_random(-10, 10), hrp.AssemblyLinearVelocity.Y, math_random(-10, 10))
        end
    end)
    cleanup:addConnection(conn)
    return cleanup
end

local function startJitterAA()
    local cleanup = Cleanup.new()
    local conn = runService.Heartbeat:Connect(function()
        local char = client.Character
        local hrp = char and char:FindFirstChild("HumanoidRootPart")
        if hrp then
            local angle = (math_random(0, 1) == 1 and 90 or -90)
            hrp.CFrame = hrp.CFrame * CFrame_Angles(0, math.rad(angle), 0)
        end
    end)
    cleanup:addConnection(conn)
    return cleanup
end

-- Hitbox Expander
local originalSizes = {}
local function startHitboxExpander()
    local cleanup = Cleanup.new()
    local function apply()
        for _, plr in ipairs(players:GetPlayers()) do
            if plr ~= client and plr.Character and plr.Character:FindFirstChild("HumanoidRootPart") then
                local hrp = plr.Character.HumanoidRootPart
                if not originalSizes[plr] then
                    originalSizes[plr] = {size = hrp.Size, transparency = hrp.Transparency}
                end
                hrp.Size = Vector3_new(15, 15, 15)
                hrp.Transparency = 0.5
                hrp.CanCollide = false
            end
        end
    end
    apply()
    cleanup:addConnection(players.PlayerAdded:Connect(apply))
    cleanup:addCleanupFunction(function()
        for plr, data in pairs(originalSizes) do
            if plr.Character and plr.Character:FindFirstChild("HumanoidRootPart") then
                local hrp = plr.Character.HumanoidRootPart
                hrp.Size = data.size
                hrp.Transparency = data.transparency
                hrp.CanCollide = true
            end
        end
        table_clear(originalSizes)
    end)
    return cleanup
end

-- Client-Side Hit Sounds (Anti-Crash & No Server Remotes Dependency)
local function startHitSounds()
    local cleanup = Cleanup.new()
    local oldHealths = {}

    local function setupTracker(plr)
        if plr == client then return end
        local conn
        conn = plr.CharacterIndexed:Connect(function(char)
            local hum = char:WaitForChild("Humanoid", 5)
            if hum then
                oldHealths[plr] = hum.Health
                cleanup:addConnection(hum.HealthChanged:Connect(function(health)
                    local old = oldHealths[plr] or 100
                    oldHealths[plr] = health
                    if health < old and features.hitsounds.enabled then
                        -- Проверяем дистанцию (чтобы играл звук только если мы наносим урон рядом или бьем аурой)
                        local cChar = client.Character
                        if cChar and cChar:FindFirstChild("HumanoidRootPart") and char:FindFirstChild("HumanoidRootPart") then
                            local dist = (cChar.HumanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude
                            if dist < 25 then
                                pcall(function()
                                    local s = Instance.new("Sound")
                                    s.SoundId = SOUNDS_DB[features.hitsounds.type] or SOUNDS_DB.Skeet
                                    s.Volume = features.hitsounds.volume
                                    s.Parent = workspace.CurrentCamera
                                    s:Play()
                                    debris:AddItem(s, 2)
                                end)
                            end
                        end
                    end
                end))
            end
        end)
        cleanup:addConnection(conn)
        if plr.Character then oldHealths[plr] = plr.Character:WaitForChild("Humanoid").Health end
    end

    for _, p in ipairs(players:GetPlayers()) do setupTracker(p) end
    cleanup:addConnection(players.PlayerAdded:Connect(setupTracker))
    return cleanup
end

-- Purple China Hat
local function startChinaHat()
    local cleanup = Cleanup.new()
    local hatPart = nil
    local rotAngle = 0

    local conn = runService.RenderStepped:Connect(function(deltaTime)
        local char = client.Character
        local head = char and char:FindFirstChild("Head")
        if not head then
            if hatPart then hatPart:Destroy(); hatPart = nil end
            return
        end
        if not hatPart or not hatPart.Parent then
            hatPart = Instance.new("Part")
            hatPart.Name = "ChinaHatVisual"
            hatPart.Anchored = false
            hatPart.CanCollide = false
            hatPart.Massless = true
            hatPart.Size = Vector3_new(1, 1, 1)
            hatPart.Transparency = 0.5
            hatPart.Color = Color3.fromRGB(160, 0, 255)
            hatPart.Material = Enum.Material.Neon
            local mesh = Instance.new("SpecialMesh")
            mesh.MeshType = Enum.MeshType.FileMesh
            mesh.MeshId = "rbxassetid://1033714"
            mesh.Scale = Vector3_new(3.2, 0.6, 3.2)
            mesh.Parent = hatPart
            hatPart.Parent = char
        end
        rotAngle = rotAngle + math.rad(120) * deltaTime
        hatPart.CFrame = head.CFrame * CFrame_new(0, 0.6, 0) * CFrame_Angles(0, rotAngle, 0)
    end)
    cleanup:addConnection(conn)
    cleanup:addCleanupFunction(function() if hatPart then hatPart:Destroy() end end)
    return cleanup
end

-- Fake Lag
local function startFakeLag()
    local cleanup = Cleanup.new()
    local running = true
    cleanup:addThread(task_spawn(function()
        while running do
            local char = client.Character
            local hrp = char and char:FindFirstChild("HumanoidRootPart")
            if hrp then
                hrp.Anchored = true
                task_wait(0.15)
                hrp.Anchored = false
            end
            task_wait(math_random(2, 5)/10)
        end
    end))
    cleanup:addCleanupFunction(function()
        running = false
        local char = client.Character
        if char and char:FindFirstChild("HumanoidRootPart") then char.HumanoidRootPart.Anchored = false end
    end)
    return cleanup
end

-- Chams
local function startChams()
    local cleanup = Cleanup.new()
    local hls = {}
    local running = true
    cleanup:addThread(task_spawn(function()
        while running do
            for _, p in ipairs(players:GetPlayers()) do
                if p ~= client and p.Character then
                    local role = getRole(p)
                    local color = (role == "Murderer" and Color3.new(1,0,0)) or (role == "Sheriff" and Color3.new(0,1,1)) or Color3.new(0,1,0)
                    local hl = hls[p] or p.Character:FindFirstChild("Chams")
                    if not hl then
                        hl = Instance.new("Highlight")
                        hl.Name = "Chams"
                        hl.FillTransparency = 0.5
                        hl.Parent = p.Character
                        hls[p] = hl
                    end
                    hl.FillColor = color
                    hl.OutlineColor = color
                end
            end
            task_wait(0.5)
        end
    end))
    cleanup:addCleanupFunction(function()
        running = false
        for _, h in pairs(hls) do if h then h:Destroy() end end
    end)
    return cleanup
end

-- Info Nametags (Safe from Workspace/Billboard Gui Spam)
local function startInfoNametags()
    local cleanup = Cleanup.new()
    local tags = {}
    local conn = runService.RenderStepped:Connect(function()
        for _, p in ipairs(players:GetPlayers()) do
            if p ~= client and p.Character and p.Character:FindFirstChild("Head") and p.Character:FindFirstChild("HumanoidRootPart") then
                local tag = tags[p] or p.Character.Head:FindFirstChild("InfoTag")
                if not tag then
                    tag = Instance.new("BillboardGui")
                    tag.Name = "InfoTag"
                    tag.Size = UDim2.new(0, 150, 0, 40)
                    tag.AlwaysOnTop = true
                    local l = Instance.new("TextLabel")
                    l.Size = UDim2.new(1,0,1,0)
                    l.BackgroundTransparency = 1
                    l.TextColor3 = Color3.new(1,1,1)
                    l.Font = Enum.Font.SourceSansBold
                    l.TextSize = 12
                    l.Parent = tag
                    tag.Parent = p.Character.Head
                    tags[p] = tag
                end
                local dist = (client.Character and client.Character:FindFirstChild("HumanoidRootPart")) and (p.Character.HumanoidRootPart.Position - client.Character.HumanoidRootPart.Position).Magnitude or 0
                local lbl = tag:FindFirstChild("TextLabel")
                if lbl then lbl.Text = string.format("%s\n[%s]\nDist: %d", p.Name, getRole(p), dist) end
            end
        end
    end)
    cleanup:addConnection(conn)
    cleanup:addCleanupFunction(function()
        for _, t in pairs(tags) do if t then t:Destroy() end end
    end)
    return cleanup
end

-- Noclip
local function startNoclip()
    local cleanup = Cleanup.new()
    local conn = runService.Stepped:Connect(function()
        local char = client.Character
        if char then
            for _, p in ipairs(char:GetDescendants()) do
                if p:IsA("BasePart") then p.CanCollide = false end
            end
        end
    end)
    cleanup:addConnection(conn)
    return cleanup
end

-- Ragebot Core
local function startRagebotCore()
    local cleanup = Cleanup.new()
    local running = true
    cleanup:addThread(task_spawn(function()
        while running do
            local char = client.Character
            if char and char:FindFirstChild("HumanoidRootPart") and getRole(client) == "Murderer" then
                local target, closest = nil, math.huge
                for _, p in ipairs(players:GetPlayers()) do
                    if p ~= client and p.Character and p.Character:FindFirstChild("HumanoidRootPart") then
                        local d = (p.Character.HumanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude
                        if d < closest then closest = d; target = p end
                    end
                end
                if target then
                    char.HumanoidRootPart.CFrame = target.Character.HumanoidRootPart.CFrame * CFrame_new(0,0,1.5)
                    local k = getKnifeTool()
                    if k then char.Humanoid:EquipTool(k); pcall(function() k:Activate() end) end
                end
            end
            task_wait(0.02)
        end
    end))
    cleanup:addCleanupFunction(function() running = false end)
    return cleanup
end

-- =================== WIRE UI TOGGLES ===================
-- RAGEBOT
createToggle(tabFrames[1], "Hitbox Expander", function(s)
    features.hitboxExpander.enabled = s
    if s then features.hitboxExpander.cleanup = startHitboxExpander() else if features.hitboxExpander.cleanup then features.hitboxExpander.cleanup:cleanup() end end
end)
createToggle(tabFrames[1], "Target Resolver", function(s) features.targetResolver.enabled = s end)
createToggle(tabFrames[1], "Ragebot Core", function(s)
    features.ragebotCore.enabled = s
    if s then features.ragebotCore.cleanup = startRagebotCore() else if features.ragebotCore.cleanup then features.ragebotCore.cleanup:cleanup() end end
end)

-- ANTI-AIM
createToggle(tabFrames[2], "Spinbot", function(s)
    features.spinbot.enabled = s
    if s then features.spinbot.cleanup = startSpinbot() else if features.spinbot.cleanup then features.spinbot.cleanup:cleanup() end end
end)
createToggle(tabFrames[2], "Jitter AA", function(s)
    features.jitterAA.enabled = s
    if s then features.jitterAA.cleanup = startJitterAA() else if features.jitterAA.cleanup then features.jitterAA.cleanup:cleanup() end end
end)
createToggle(tabFrames[2], "Fake Lag", function(s)
    features.fakeLag.enabled = s
    if s then features.fakeLag.cleanup = startFakeLag() else if features.fakeLag.cleanup then features.fakeLag.cleanup:cleanup() end end
end)

-- VISUALS
createToggle(tabFrames[3], "Purple China Hat", function(s)
    features.chinaHat.enabled = s
    if s then features.chinaHat.cleanup = startChinaHat() else if features.chinaHat.cleanup then features.chinaHat.cleanup:cleanup() end end
end)
createToggle(tabFrames[3], "Wallhack / Chams", function(s)
    features.chams.enabled = s
    if s then features.chams.cleanup = startChams() else if features.chams.cleanup then features.chams.cleanup:cleanup() end end
end)
createToggle(tabFrames[3], "Info Nametags", function(s)
    features.infoNametags.enabled = s
    if s then features.infoNametags.cleanup = startInfoNametags() else if features.infoNametags.cleanup then features.infoNametags.cleanup:cleanup() end end
end)

-- HITSOUNDS ADDON (Вкладка VISUALS)
createToggle(tabFrames[3], "Enable Hit Sounds", function(s)
    features.hitsounds.enabled = s
    if s then features.hitsounds.cleanup = startHitSounds() else if features.hitsounds.cleanup then features.hitsounds.cleanup:cleanup() end end
end, false)
createDropdown(tabFrames[3], "Sound Type", {"Skeet", "Neverlose"}, function(val) features.hitsounds.type = val end)
createSlider(tabFrames[3], "Sound Volume", 1, 5, 2, function(val) features.hitsounds.volume = val end)

-- MOVEMENT
createSlider(tabFrames[4], "WalkSpeed", 16, 200, 50, function(v) settings.walkspeed = v; pcall(function() client.Character.Humanoid.WalkSpeed = v end) end)
createSlider(tabFrames[4], "JumpPower", 50, 200, 50, function(v) settings.jumppower = v; pcall(function() client.Character.Humanoid.JumpPower = v end) end)
createToggle(tabFrames[4], "Noclip", function(s)
    features.noclip.enabled = s
    if s then features.noclip.cleanup = startNoclip() else if features.noclip.cleanup then features.noclip.cleanup:cleanup() end end
end)

-- ============== BUTTON & DRAG LOGIC ==============
floatButton.MouseButton1Click:Connect(function() mainFrame.Visible = not mainFrame.Visible end)

local function makeDraggable(gui, control)
    local dragInput, dragStart, startPos
    control.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
            dragInput = input; dragStart = input.Position; startPos = gui.Position
            input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragInput = nil end end)
        end
    end)
    control.InputChanged:Connect(function(input)
        if input == dragInput then
            local delta = input.Position - dragStart
            gui.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
        end
    end)
end
makeDraggable(mainFrame, titleBar)
makeDraggable(floatButton, floatButton)