Axiom menu - [ keyless , safe, no obfuscation ]
Key Required

Axiom menu - [ keyless , safe, no obfuscation ]

Prison Lifekaixokaixo 1.9K views 11.5K executesUpdated Oct 1, 2026
5.0/ 5(1 votes)
10100% Positive
Lua Script•3558 lines (126821 chars)
--[[
    ╔══════════════════════════════════════════════════════════════╗
    ║                       Axiom menu                           ║
    ║   Role ESP + Noclip + Aim + Auto-Give + Humanoid + Arrest    ║
    ║   + Teleport (Presets) + Go-To Players                       ║
    ╠══════════════════════════════════════════════════════════════╣
    ║  Version: 3.2.0                                              ║
    ║  • Added Teleport tab (presets + saved locations)            ║
    ║  • Added Go-To Players tab (grouped by role + distance)      ║
    ╚══════════════════════════════════════════════════════════════╝
]]

local VERSION = "3.2.0"
local SAFE_MODE = false

-- ══════════════════════════════════════════════════════════════════
--  BOOT
-- ══════════════════════════════════════════════════════════════════
local Players           = game:GetService("Players")
local RunService        = game:GetService("RunService")
local UserInputService  = game:GetService("UserInputService")
local TweenService      = game:GetService("TweenService")
local Workspace         = game:GetService("Workspace")

local LocalPlayer = Players.LocalPlayer
if not LocalPlayer then
    repeat task.wait(0.1) until Players.LocalPlayer
    LocalPlayer = Players.LocalPlayer
end

repeat task.wait() until game:IsLoaded()

print(string.format("[PrisonVision v%s] Loading...", VERSION))

local Camera = Workspace.CurrentCamera

-- ══════════════════════════════════════════════════════════════════
--  GLOBAL STATE
-- ══════════════════════════════════════════════════════════════════
local Flags = {
    -- ESP
    ["ESP/Enabled"]                = true,
    ["ESP/ShowUsername"]           = true,
    ["ESP/TeamCheck"]              = false,
    ["ESP/MaxDistance"]            = 5000,
    ["ESP/ColorByRole"]            = true,
    ["ESP/NametagOpacity"]         = 0,
    ["ESP/HighlightEnabled"]       = true,
    ["ESP/HighlightFillAlpha"]     = 0.75,
    ["ESP/HighlightOutlineAlpha"]  = 0.35,
    ["ESP/HealthIndicator"]        = true,
    ["ESP/ShowDistance"]           = true,
    ["ESP/ShowEquipped"]           = true,

    -- Noclip
    ["Noclip/Enabled"]             = false,
    ["Noclip/Radius"]              = 300,
    ["Noclip/IncludeAnchored"]     = true,
    ["Noclip/IncludeUnanchored"]   = false,

    -- Aim
    ["Aim/AimLock"]                          = false,
    ["Aim/TeamCheck"]                        = false,
    ["Aim/VisibilityCheck"]                  = true,
    ["Aim/AttractionStrength"]               = 200,
    ["Aim/FOV/Radius"]                       = 100,
    ["Aim/FOV/ShowCircle"]                   = true,
    ["Aim/Dampening"]                        = true,
    ["Aim/Dampening/Threshold"]              = 5,
    ["Aim/Dampening/Strength"]               = 5,
    ["Aim/Priority"]                         = "Head",
    ["Aim/BodyParts"]                        = {"Head"},
    ["Aim/TargetGroups"] = {
        Head = true, Torso = false, LeftArm = false,
        RightArm = false, LeftLeg = false, RightLeg = false,
    },

    -- Auto-Give
    ["AutoGive/Enabled"]           = true,

    -- Arrest
    ["Arrest/Enabled"]             = true,
    ["Arrest/StickDistance"]       = 2.2,
    ["Arrest/StickHeight"]         = 0.0,
    ["Arrest/StickDuration"]       = 5.0,
    ["Arrest/ToolName"]            = "Handcuffs",
    ["Arrest/RestorePosition"]     = true,

    -- Humanoid (values start nil; captured on spawn)
    ["Humanoid/WalkSpeed"]   = nil,
    ["Humanoid/JumpPower"]   = nil,
    ["Humanoid/JumpHeight"]  = nil,
    ["Humanoid/HipHeight"]   = nil,
    ["Humanoid/MaxHealth"]   = nil,

    ["Humanoid/WalkSpeed/Locked"]   = false,
    ["Humanoid/JumpPower/Locked"]   = false,
    ["Humanoid/JumpHeight/Locked"]  = false,
    ["Humanoid/HipHeight/Locked"]   = false,
    ["Humanoid/MaxHealth/Locked"]   = false,
}

-- ══════════════════════════════════════════════════════════════════
--  TELEPORT SAVED LOCATIONS (persistent in this session)
-- ══════════════════════════════════════════════════════════════════
local SavedLocations = {}

local function SaveLocationsToDisk()
    if not writefile then return end
    local ok, encoded = pcall(function()
        return game:GetService("HttpService"):JSONEncode(SavedLocations)
    end)
    if ok and encoded then
        pcall(function() writefile("PrisonVision_Locations.json", encoded) end)
    end
end

local function LoadLocationsFromDisk()
    if not (isfile and readfile) then return end
    local ok = pcall(function()
        if isfile("PrisonVision_Locations.json") then
            local raw = readfile("PrisonVision_Locations.json")
            local decoded = game:GetService("HttpService"):JSONDecode(raw)
            if type(decoded) == "table" then
                SavedLocations = decoded
            end
        end
    end)
end

LoadLocationsFromDisk()

-- ══════════════════════════════════════════════════════════════════
--  THEME
-- ══════════════════════════════════════════════════════════════════
local THEME = {
    Background  = Color3.fromRGB(16, 16, 18),
    Sidebar     = Color3.fromRGB(22, 22, 26),
    Element     = Color3.fromRGB(32, 32, 38),
    Accent      = Color3.fromRGB(255, 140, 60),
    Text        = Color3.fromRGB(235, 235, 240),
    TextDark    = Color3.fromRGB(140, 140, 150),
    Success     = Color3.fromRGB(70, 210, 120),
    Fail        = Color3.fromRGB(230, 70, 70),
}

local ROLE_COLORS = {
    Guard    = Color3.fromRGB(70, 150, 255),
    Inmate   = Color3.fromRGB(255, 150, 60),
    Criminal = Color3.fromRGB(230, 60, 60),
    Neutral  = Color3.fromRGB(200, 200, 210),
    Unknown  = Color3.fromRGB(180, 180, 190),
}

-- ══════════════════════════════════════════════════════════════════
--  UTILITY
-- ══════════════════════════════════════════════════════════════════
local function SafeSet(obj, prop, val)
    if not obj then return false end
    local ok = pcall(function()
        if obj[prop] ~= val then obj[prop] = val end
    end)
    return ok
end

local function SafeDestroy(obj)
    if obj then pcall(function() obj:Destroy() end) end
end

-- ══════════════════════════════════════════════════════════════════
--  ROLE DETECTION
-- ══════════════════════════════════════════════════════════════════
local function DetectRole(player)
    if not player or not player.Parent then return "Unknown" end

    local team = player.Team
    if team then
        local name = string.lower(team.Name)
        if name:find("guard") or name:find("police") or name:find("cop") or name:find("officer") then
            return "Guard"
        elseif name:find("inmate") or name:find("prisoner") then
            return "Inmate"
        elseif name:find("criminal") or name:find("escapee") or name:find("wanted") then
            return "Criminal"
        elseif name:find("neutral") or name:find("civilian") then
            return "Neutral"
        end

        local ok, color = pcall(function() return team.TeamColor.Color end)
        if ok and color then
            local r, g, b = color.R, color.G, color.B
            if b > 0.6 and r < 0.4 and g < 0.6 then
                return "Guard"
            elseif r > 0.7 and g > 0.4 and g < 0.8 and b < 0.4 then
                return "Inmate"
            elseif r > 0.7 and g < 0.4 and b < 0.4 then
                return "Criminal"
            elseif r > 0.6 and g > 0.6 and b > 0.6 then
                return "Neutral"
            end
        end
    end

    return "Unknown"
end

local function IsLocalGuard()
    local role = DetectRole(LocalPlayer)
    return role == "Guard"
end

-- ══════════════════════════════════════════════════════════════════
--  HEALTH HELPERS
-- ══════════════════════════════════════════════════════════════════
local function GetHealth(player)
    if not player then return 0, 100 end
    local char = player.Character
    if not char then return 0, 100 end
    local hum = char:FindFirstChildOfClass("Humanoid")
    if hum then
        return hum.Health, hum.MaxHealth
    end
    return 0, 100
end

local function GetHealthColor(health, maxHealth)
    if not maxHealth or maxHealth <= 0 then return Color3.fromRGB(255, 255, 255) end
    local percent = math.clamp(health / maxHealth, 0, 1)

    if percent >= 0.75 then
        return Color3.fromRGB(70, 220, 100)
    elseif percent >= 0.50 then
        return Color3.fromRGB(180, 220, 70)
    elseif percent >= 0.25 then
        return Color3.fromRGB(255, 180, 50)
    else
        return Color3.fromRGB(230, 60, 60)
    end
end

-- ══════════════════════════════════════════════════════════════════
--  PLAYER CACHE & CHAR CACHE
-- ══════════════════════════════════════════════════════════════════
local cachedPlayersList = Players:GetPlayers()
local function GetPlayersCache() return cachedPlayersList end
local function UpdatePlayerCache() cachedPlayersList = Players:GetPlayers() end

local CharCache = {}

local function GetCharacter(player)
    if not player then return nil end

    local cache = CharCache[player]
    if cache then
        local char = cache.Char
        local root = cache.Root
        if char and char.Parent and char == player.Character and root and root.Parent then
            if cache.Humanoid then
                if cache.Humanoid.Health > 0 then return char, root end
                return nil
            end
            return char, root
        else
            cache.Char = nil
            cache.Root = nil
            cache.Humanoid = nil
        end
    end

    local character = player.Character
    if not character or not character.Parent then return nil end

    local rootPart = character:FindFirstChild("HumanoidRootPart") or character.PrimaryPart
    if not rootPart then return nil end

    local humanoid = character:FindFirstChildOfClass("Humanoid")
    if humanoid and humanoid.Health <= 0 then return nil end

    if not cache then
        cache = {}
        CharCache[player] = cache
    end
    cache.Char = character
    cache.Root = rootPart
    cache.Humanoid = humanoid
    cache.Head = character:FindFirstChild("Head")

    return character, rootPart
end

-- ══════════════════════════════════════════════════════════════════
--  HUMANOID STATE (capture only — never force defaults)
-- ══════════════════════════════════════════════════════════════════
local HumanoidState = { Original = {}, Captured = false }

local HUMANOID_PROPERTY_MAP = {
    ["Humanoid/WalkSpeed"]  = "WalkSpeed",
    ["Humanoid/JumpPower"]  = "JumpPower",
    ["Humanoid/JumpHeight"] = "JumpHeight",
    ["Humanoid/HipHeight"]  = "HipHeight",
    ["Humanoid/MaxHealth"]  = "MaxHealth",
}

local function CaptureHumanoidSettings()
    local char = LocalPlayer.Character
    local hum = char and char:FindFirstChildOfClass("Humanoid")
    if not hum then return end

    HumanoidState.Original = {}

    for flagKey, prop in pairs(HUMANOID_PROPERTY_MAP) do
        local ok, val = pcall(function() return hum[prop] end)
        if ok and val ~= nil then
            HumanoidState.Original[flagKey] = val

            local isLocked = Flags[flagKey .. "/Locked"] == true
            if not isLocked and Flags[flagKey] == nil then
                Flags[flagKey] = val
            end
        end
    end

    HumanoidState.Captured = true
end

local function ApplySavedHumanoidSettings()
    local char = LocalPlayer.Character
    local hum = char and char:FindFirstChildOfClass("Humanoid")
    if not hum then return end

    for flagKey, prop in pairs(HUMANOID_PROPERTY_MAP) do
        local val = Flags[flagKey]
        local isLocked = Flags[flagKey .. "/Locked"] == true
        local origVal = HumanoidState.Original[flagKey]

        if isLocked or (val ~= nil and origVal ~= nil and val ~= origVal) then
            pcall(function()
                if hum[prop] ~= val then
                    hum[prop] = val
                end
            end)
        end
    end
end

local function ApplyHumanoidSettings()
    if not HumanoidState.Captured then return end

    local char = LocalPlayer.Character
    local hum = char and char:FindFirstChildOfClass("Humanoid")
    if not hum then return end

    for flagKey, prop in pairs(HUMANOID_PROPERTY_MAP) do
        local isLocked = Flags[flagKey .. "/Locked"] == true
        if isLocked then
            local val = Flags[flagKey]
            if val ~= nil then
                pcall(function()
                    if hum[prop] ~= val then
                        hum[prop] = val
                    end
                end)
            end
        end
    end
end

-- ══════════════════════════════════════════════════════════════════
--  AIM SYSTEM
-- ══════════════════════════════════════════════════════════════════
local TARGET_GROUPS = {
    Head = {"Head"},
    Torso = {"Torso", "UpperTorso", "LowerTorso", "HumanoidRootPart"},
    LeftArm = {"Left Arm", "LeftUpperArm", "LeftLowerArm", "LeftHand"},
    RightArm = {"Right Arm", "RightUpperArm", "RightLowerArm", "RightHand"},
    LeftLeg = {"Left Leg", "LeftUpperLeg", "LeftLowerLeg", "LeftFoot"},
    RightLeg = {"Right Leg", "RightUpperLeg", "RightLowerLeg", "RightFoot"},
}

local ALL_BODY_PARTS = {}
for _, group in pairs(TARGET_GROUPS) do
    for _, part in ipairs(group) do
        table.insert(ALL_BODY_PARTS, part)
    end
end

local AimState = {
    LastAimTarget = nil,
    LastMouseMode = nil,
    LastOriginX = nil,
    LastOriginY = nil,
}

local function ClearAimLockState(resetMouseMode)
    AimState.LastAimTarget = nil
    AimState.LastOriginX = nil
    AimState.LastOriginY = nil
    if resetMouseMode then
        AimState.LastMouseMode = nil
    end
end

local CachedFilterType = (function()
    local ok, val = pcall(function()
        local ft = Enum.RaycastFilterType.Exclude
        if typeof(ft) == "EnumItem" then return ft end
    end)
    if ok and val then return val end
    ok, val = pcall(function()
        local ft = Enum.RaycastFilterType.Blacklist
        if typeof(ft) == "EnumItem" then return ft end
    end)
    if ok and val then return val end
    return nil
end)()

local SharedRaycastParams = RaycastParams.new()
SharedRaycastParams.IgnoreWater = true
if CachedFilterType then
    SharedRaycastParams.FilterType = CachedFilterType
end

local function Raycast(Origin, Direction, Filter)
    SharedRaycastParams.FilterDescendantsInstances = Filter
    return Workspace:Raycast(Origin, Direction, SharedRaycastParams)
end

local OcclusionFilter = {nil, nil}

local function ObjectOccluded(Enabled, Origin, Position, Object)
    if not Enabled then return false end
    if typeof(Position) ~= "Vector3" then return false end
    OcclusionFilter[1] = LocalPlayer.Character
    OcclusionFilter[2] = Object
    local hit = Raycast(Origin, Position - Origin, OcclusionFilter)
    if hit and hit.Instance and not hit.Instance:IsDescendantOf(Object) then
        return true
    end
    return false
end

local ViewportCache = {}
local ViewportPool = {}
local MAX_VIEWPORT_POOL = 64
local _vp_floor = math.floor

RunService.RenderStepped:Connect(function()
    local recycled = 0
    for _, entry in pairs(ViewportCache) do
        if recycled < MAX_VIEWPORT_POOL then
            ViewportPool[#ViewportPool + 1] = entry
            recycled = recycled + 1
        end
    end
    table.clear(ViewportCache)
end)

local function GetViewportPoint(worldPos)
    local kx = _vp_floor(worldPos.X * 4)
    local ky = _vp_floor(worldPos.Y * 4)
    local kz = _vp_floor(worldPos.Z * 4)
    local key = kx .. "|" .. ky .. "|" .. kz
    local entry = ViewportCache[key]
    if entry then return entry[1], entry[2] end

    local screenPos, onScreen = Camera:WorldToViewportPoint(worldPos)
    entry = table.remove(ViewportPool) or {}
    entry[1], entry[2] = screenPos, onScreen
    ViewportCache[key] = entry
    return screenPos, onScreen
end

local function GetCrosshairViewportPosition(mouseBehavior)
    if not Camera then Camera = Workspace.CurrentCamera end
    if not Camera then return nil, nil, false end

    local viewportSize = Camera.ViewportSize

    if mouseBehavior == Enum.MouseBehavior.LockCenter then
        return viewportSize.X * 0.5, viewportSize.Y * 0.5, true
    end

    local mouseLoc = UserInputService:GetMouseLocation()
    local crosshairX = mouseLoc.X
    local crosshairY = mouseLoc.Y

    if crosshairX ~= crosshairX or crosshairY ~= crosshairY then
        return nil, nil, false
    end

    if crosshairX < 0 or crosshairY < 0
    or crosshairX > viewportSize.X or crosshairY > viewportSize.Y then
        return nil, nil, false
    end

    return crosshairX, crosshairY, true
end

local CandidateList = {}
local CandidateCount = 0
local MAX_CANDIDATES = 15
local ClosestResult = {nil, nil, nil, 0, 0, nil}

local function CandidateSortFn(a, b)
    return a.mag < b.mag
end

local function BoundedInsertionSort(array, count, compare)
    for i = 2, count do
        local key = array[i]
        local j = i - 1
        while j > 0 and compare(key, array[j]) do
            array[j + 1] = array[j]
            j = j - 1
        end
        array[j + 1] = key
    end
end

local _hoistedCheckParts = {}
local _lastTargetGroupsHash = nil

local function RebuildCheckParts()
    table.clear(_hoistedCheckParts)
    local anySelected = false
    for category, enabled in pairs(Flags["Aim/TargetGroups"]) do
        if enabled then
            anySelected = true
            for _, partName in ipairs(TARGET_GROUPS[category]) do
                table.insert(_hoistedCheckParts, partName)
            end
        end
    end
    if not anySelected then
        for _, partName in ipairs(ALL_BODY_PARTS) do
            table.insert(_hoistedCheckParts, partName)
        end
    end
end

local function GetClosest(Enabled, TeamCheck, VisibilityCheck, FieldOfView, StickyTarget)
    if not Enabled then return nil end

    local CameraPosition = Camera.CFrame.Position
    local mouseBehavior = UserInputService.MouseBehavior

    local crosshairX, crosshairY, crosshairValid = GetCrosshairViewportPosition(mouseBehavior)
    if not crosshairValid then return nil end

    CandidateCount = 0

    local players = GetPlayersCache()
    local lookVector = Camera.CFrame.LookVector
    local maxCandsLimit = MAX_CANDIDATES * 3

    local currentHash =
        (Flags["Aim/TargetGroups"].Head and 1 or 0) +
        (Flags["Aim/TargetGroups"].Torso and 2 or 0) +
        (Flags["Aim/TargetGroups"].LeftArm and 4 or 0) +
        (Flags["Aim/TargetGroups"].RightArm and 8 or 0) +
        (Flags["Aim/TargetGroups"].LeftLeg and 16 or 0) +
        (Flags["Aim/TargetGroups"].RightLeg and 32 or 0)
    if currentHash ~= _lastTargetGroupsHash then
        RebuildCheckParts()
        _lastTargetGroupsHash = currentHash
    end
    local checkParts = _hoistedCheckParts

    for _, Player in ipairs(players) do
        if Player == LocalPlayer then continue end

        local Character, RootPart = GetCharacter(Player)
        if not Character or not RootPart then continue end

        if TeamCheck then
            local lpTeam, pTeam = LocalPlayer.Team, Player.Team
            if lpTeam and pTeam and lpTeam == pTeam then continue end
        end

        local rootPos = RootPart.Position
        local relPos = rootPos - CameraPosition
        if lookVector:Dot(relPos) < 0 then continue end

        for _, PartName in ipairs(checkParts) do
            local cache = CharCache[Player]
            local BodyPart = (cache and cache[PartName]) or Character:FindFirstChild(PartName)
            if not BodyPart then continue end

            local ActualPosition = BodyPart.Position

            local ScreenPosition, OnScreen = GetViewportPoint(ActualPosition)
            if OnScreen then
                local screenX, screenY = ScreenPosition.X, ScreenPosition.Y
                local dx, dy = screenX - crosshairX, screenY - crosshairY
                local Magnitude = math.sqrt(dx * dx + dy * dy)

                if Magnitude < FieldOfView then
                    if CandidateCount < maxCandsLimit then
                        CandidateCount = CandidateCount + 1
                        local entry = CandidateList[CandidateCount]
                        if not entry then
                            entry = {}
                            CandidateList[CandidateCount] = entry
                        end
                        entry.mag = Magnitude
                        entry.ply = Player
                        entry.char = Character
                        entry.part = BodyPart
                        entry.sx = screenX
                        entry.sy = screenY
                        entry.pos = ActualPosition
                        entry.realPos = ActualPosition
                    end
                end
            end
        end
    end

    if CandidateCount == 0 then return nil end

    if CandidateCount > 1 then
        BoundedInsertionSort(CandidateList, CandidateCount, CandidateSortFn)
    end

    if StickyTarget then
        for i = 1, CandidateCount do
            local entry = CandidateList[i]
            if entry.ply == StickyTarget then
                if not ObjectOccluded(VisibilityCheck, CameraPosition, entry.realPos, entry.char) then
                    ClosestResult[1] = entry.ply
                    ClosestResult[2] = entry.char
                    ClosestResult[3] = entry.part
                    ClosestResult[4] = entry.sx
                    ClosestResult[5] = entry.sy
                    ClosestResult[6] = entry.pos
                    return ClosestResult
                end
                break
            end
        end
    end

    local limit = math.min(CandidateCount, MAX_CANDIDATES)

    for i = 1, limit do
        local entry = CandidateList[i]
        if not entry then continue end

        if ObjectOccluded(VisibilityCheck, CameraPosition, entry.realPos, entry.char) then
            continue
        end

        ClosestResult[1] = entry.ply
        ClosestResult[2] = entry.char
        ClosestResult[3] = entry.part
        ClosestResult[4] = entry.sx
        ClosestResult[5] = entry.sy
        ClosestResult[6] = entry.pos
        return ClosestResult
    end

    return nil
end

local function AimAt(Hitbox)
    if not Hitbox then
        ClearAimLockState(false)
        return
    end
    if not mousemoverel then return end

    local targetPart = Hitbox[3]
    if not targetPart or not targetPart.Parent then
        ClearAimLockState(false)
        return
    end

    local currentMode = UserInputService.MouseBehavior
    if currentMode ~= AimState.LastMouseMode then
        AimState.LastMouseMode = currentMode
        AimState.LastOriginX = nil
        AimState.LastOriginY = nil
    end

    local currentTarget = Hitbox[1]
    if currentTarget ~= AimState.LastAimTarget then
        AimState.LastAimTarget = currentTarget
        AimState.LastOriginX = nil
        AimState.LastOriginY = nil
    end

    local targetPos = targetPart.Position
    local cameraPos = Camera.CFrame.Position
    local dist = (targetPos - cameraPos).Magnitude

    if dist < 1 then return end

    local ScreenPosition, OnScreen = GetViewportPoint(targetPos)
    if not OnScreen or ScreenPosition.Z <= 0 then return end

    local viewportSize = Camera.ViewportSize
    local originX, originY, originValid = GetCrosshairViewportPosition(currentMode)
    if not originValid then return end

    AimState.LastOriginX = originX
    AimState.LastOriginY = originY

    local targetX, targetY = ScreenPosition.X, ScreenPosition.Y
    local dx, dy = targetX - originX, targetY - originY

    local mag = math.sqrt(dx * dx + dy * dy)
    if mag > Flags["Aim/FOV/Radius"] then return end

    local attractionMultiplier = Flags["Aim/AttractionStrength"] / 100

    if Flags["Aim/Dampening"] then
        local lockThreshold = Flags["Aim/Dampening/Threshold"]
        if mag < lockThreshold then
            local dampening = math.max(Flags["Aim/Dampening/Strength"] / 100, mag / lockThreshold)
            attractionMultiplier = attractionMultiplier * dampening
        end
    end

    local BASE_PULL = 0.2
    local deltaX = dx * BASE_PULL * attractionMultiplier
    local deltaY = dy * BASE_PULL * attractionMultiplier

    if deltaX ~= deltaX or deltaY ~= deltaY then return end

    local maxClampX = viewportSize.X * 0.5
    local maxClampY = viewportSize.Y * 0.5

    deltaX = math.clamp(deltaX, -maxClampX, maxClampX)
    deltaY = math.clamp(deltaY, -maxClampY, maxClampY)

    mousemoverel(math.floor(deltaX + 0.5), math.floor(deltaY + 0.5))
end

local CachedTarget = nil
local CachedTargetTime = 0
local TARGET_CACHE_DURATION = 0.016

local function GetCachedTarget()
    local now = os.clock()
    if CachedTarget and (now - CachedTargetTime) < TARGET_CACHE_DURATION then
        return CachedTarget
    end

    CachedTarget = GetClosest(
        true,
        Flags["Aim/TeamCheck"],
        Flags["Aim/VisibilityCheck"],
        Flags["Aim/FOV/Radius"],
        AimState.LastAimTarget
    )
    CachedTargetTime = now
    return CachedTarget
end

local FovCircleGui = nil
local FovCircleFrame = nil

local function EnsureFovCircleGui()
    if FovCircleGui and FovCircleGui.Parent then return FovCircleGui end

    local parent = gethui and gethui() or game:GetService("CoreGui")
    FovCircleGui = Instance.new("ScreenGui")
    FovCircleGui.Name = "PrisonVision_FOV"
    FovCircleGui.DisplayOrder = 5000
    FovCircleGui.IgnoreGuiInset = true
    FovCircleGui.ResetOnSpawn = false
    FovCircleGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
    pcall(function() FovCircleGui.Parent = parent end)
    if not FovCircleGui.Parent then
        FovCircleGui.Parent = game:GetService("CoreGui")
    end
    return FovCircleGui
end

local function UpdateFOVCircle()
    local showCircle = Flags["Aim/AimLock"] and Flags["Aim/FOV/ShowCircle"]

    if not showCircle then
        if FovCircleFrame then FovCircleFrame.Visible = false end
        return
    end

    local gui = EnsureFovCircleGui()

    if not FovCircleFrame or FovCircleFrame.Parent ~= gui then
        if FovCircleFrame then SafeDestroy(FovCircleFrame) end

        FovCircleFrame = Instance.new("Frame")
        FovCircleFrame.Name = "FOVCircle"
        FovCircleFrame.BackgroundTransparency = 1
        FovCircleFrame.BorderSizePixel = 0
        FovCircleFrame.AnchorPoint = Vector2.new(0.5, 0.5)
        FovCircleFrame.ZIndex = 1

        local corner = Instance.new("UICorner")
        corner.CornerRadius = UDim.new(1, 0); corner.Parent = FovCircleFrame

        local stroke = Instance.new("UIStroke")
        stroke.Name = "Stroke"
        stroke.Thickness = 1.5
        stroke.Color = THEME.Accent
        stroke.Transparency = 0.2
        stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
        stroke.Parent = FovCircleFrame

        FovCircleFrame.Parent = gui
    end

    local currentMode = UserInputService.MouseBehavior
    local crosshairX, crosshairY, crosshairValid = GetCrosshairViewportPosition(currentMode)

    if crosshairValid then
        local radius = Flags["Aim/FOV/Radius"] or 50
        local diameter = radius * 2
        FovCircleFrame.Size = UDim2.fromOffset(diameter, diameter)
        FovCircleFrame.Position = UDim2.fromOffset(crosshairX, crosshairY)
        FovCircleFrame.Visible = true
    else
        FovCircleFrame.Visible = false
    end
end

local function UpdateAim()
    pcall(UpdateFOVCircle)

    if SAFE_MODE then return end

    local AimActive = Flags["Aim/AimLock"]
    if not AimActive then
        ClearAimLockState(false)
        return
    end

    local target = GetCachedTarget()
    if target then
        AimAt(target)
    else
        ClearAimLockState(false)
    end
end

-- ══════════════════════════════════════════════════════════════════
--  ESP SYSTEM
-- ══════════════════════════════════════════════════════════════════
local ESP = { Objects = {}, Folder = nil }

local function EnsureESPFolder()
    if ESP.Folder and ESP.Folder.Parent then return ESP.Folder end
    local parent = gethui and gethui() or game:GetService("CoreGui")
    ESP.Folder = Instance.new("Folder")
    ESP.Folder.Name = "PrisonVision_ESP"
    pcall(function() ESP.Folder.Parent = parent end)
    if not ESP.Folder.Parent then ESP.Folder.Parent = game:GetService("CoreGui") end
    return ESP.Folder
end

local function CreateNametag(player)
    local folder = EnsureESPFolder()

    local billboard = Instance.new("BillboardGui")
    billboard.Name = "PV_" .. player.Name
    billboard.Adornee = nil
    billboard.AlwaysOnTop = true
    billboard.Size = UDim2.new(0, 200, 0, 90)
    billboard.StudsOffset = Vector3.new(0, 3.2, 0)
    billboard.MaxDistance = Flags["ESP/MaxDistance"]
    billboard.Enabled = false
    billboard.Parent = folder

    local layout = Instance.new("UIListLayout")
    layout.FillDirection = Enum.FillDirection.Vertical
    layout.HorizontalAlignment = Enum.HorizontalAlignment.Center
    layout.VerticalAlignment = Enum.VerticalAlignment.Bottom
    layout.SortOrder = Enum.SortOrder.LayoutOrder
    layout.Padding = UDim.new(0, 1)
    layout.Parent = billboard

    local label = Instance.new("TextLabel")
    label.Name = "NameLabel"
    label.Size = UDim2.new(1, 0, 0, 18)
    label.BackgroundTransparency = 1
    label.Text = player.Name
    label.TextColor3 = ROLE_COLORS.Unknown
    label.TextStrokeTransparency = 0
    label.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
    label.TextSize = 14
    label.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    label.TextTransparency = Flags["ESP/NametagOpacity"]
    label.LayoutOrder = 1
    label.Parent = billboard

    local healthNum = Instance.new("TextLabel")
    healthNum.Name = "HealthNum"
    healthNum.Size = UDim2.new(1, 0, 0, 16)
    healthNum.BackgroundTransparency = 1
    healthNum.Text = "100/100"
    healthNum.TextColor3 = Color3.fromRGB(255, 255, 255)
    healthNum.TextStrokeTransparency = 0
    healthNum.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
    healthNum.TextSize = 13
    healthNum.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    healthNum.TextTransparency = Flags["ESP/NametagOpacity"]
    healthNum.LayoutOrder = 2
    healthNum.Visible = Flags["ESP/HealthIndicator"]
    healthNum.Parent = billboard

    local healthBarBG = Instance.new("Frame")
    healthBarBG.Name = "HealthBarBG"
    healthBarBG.Size = UDim2.new(0.7, 0, 0, 5)
    healthBarBG.BackgroundColor3 = Color3.fromRGB(200, 50, 50)
    healthBarBG.BorderSizePixel = 0
    healthBarBG.LayoutOrder = 3
    healthBarBG.Visible = Flags["ESP/HealthIndicator"]
    healthBarBG.Parent = billboard
    local hbCorner = Instance.new("UICorner")
    hbCorner.CornerRadius = UDim.new(1, 0); hbCorner.Parent = healthBarBG

    local healthBarFill = Instance.new("Frame")
    healthBarFill.Name = "HealthBarFill"
    healthBarFill.Size = UDim2.fromScale(1, 1)
    healthBarFill.BackgroundColor3 = Color3.fromRGB(70, 220, 100)
    healthBarFill.BorderSizePixel = 0
    healthBarFill.Parent = healthBarBG
    local hbfCorner = Instance.new("UICorner")
    hbfCorner.CornerRadius = UDim.new(1, 0); hbfCorner.Parent = healthBarFill

    local distanceLabel = Instance.new("TextLabel")
    distanceLabel.Name = "DistanceLabel"
    distanceLabel.Size = UDim2.new(1, 0, 0, 16)
    distanceLabel.BackgroundTransparency = 1
    distanceLabel.Text = "0"
    distanceLabel.TextColor3 = Color3.fromRGB(200, 200, 200)
    distanceLabel.TextStrokeTransparency = 0
    distanceLabel.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
    distanceLabel.TextSize = 12
    distanceLabel.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
    distanceLabel.TextTransparency = Flags["ESP/NametagOpacity"]
    distanceLabel.LayoutOrder = 4
    distanceLabel.Visible = Flags["ESP/ShowDistance"]
    distanceLabel.Parent = billboard

    local equippedLabel = Instance.new("TextLabel")
    equippedLabel.Name = "EquippedLabel"
    equippedLabel.Size = UDim2.new(1, 0, 0, 16)
    equippedLabel.BackgroundTransparency = 1
    equippedLabel.Text = "[Unarmed]"
    equippedLabel.TextColor3 = Color3.fromRGB(200, 200, 200)
    equippedLabel.TextStrokeTransparency = 0
    equippedLabel.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
    equippedLabel.TextSize = 12
    equippedLabel.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
    equippedLabel.TextTransparency = Flags["ESP/NametagOpacity"]
    equippedLabel.LayoutOrder = 5
    equippedLabel.Visible = Flags["ESP/ShowEquipped"]
    equippedLabel.Parent = billboard

    local highlight = Instance.new("Highlight")
    highlight.Name = "PV_HL_" .. player.Name
    highlight.Adornee = nil
    highlight.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
    highlight.FillColor = ROLE_COLORS.Unknown
    highlight.FillTransparency = Flags["ESP/HighlightFillAlpha"]
    highlight.OutlineColor = ROLE_COLORS.Unknown
    highlight.OutlineTransparency = Flags["ESP/HighlightOutlineAlpha"]
    highlight.Enabled = false
    highlight.Parent = folder

    ESP.Objects[player] = {
        Nametag      = billboard,
        Label        = label,
        HealthNum    = healthNum,
        HealthBarBG  = healthBarBG,
        HealthBarFill= healthBarFill,
        DistanceLabel= distanceLabel,
        EquippedLabel= equippedLabel,
        Highlight    = highlight,
        LastName     = player.Name,
        LastRole     = nil,
        LastHealth   = -1,
        LastMaxHealth= -1,
        LastDistance = -1,
        LastEquipped = "",
    }
end

local function RemoveNametag(player)
    local data = ESP.Objects[player]
    if not data then return end
    SafeDestroy(data.Nametag)
    if data.Highlight then SafeDestroy(data.Highlight) end
    ESP.Objects[player] = nil
end

local function UpdateNametag(player)
    local data = ESP.Objects[player]
    if not data then return end

    local shouldShow = Flags["ESP/Enabled"]
    if shouldShow and Flags["ESP/TeamCheck"] then
        if player.Team and LocalPlayer.Team and player.Team == LocalPlayer.Team then
            shouldShow = false
        end
    end
    if shouldShow and player == LocalPlayer then shouldShow = false end

    local char = player.Character
    local root = char and (char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart)
    local humanoid = char and char:FindFirstChildOfClass("Humanoid")
    if not root or not humanoid or humanoid.Health <= 0 then shouldShow = false end

    local dist = 0
    if shouldShow then
        local myRoot = LocalPlayer.Character
            and (LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or LocalPlayer.Character.PrimaryPart)
        if myRoot then
            dist = (root.Position - myRoot.Position).Magnitude
            if dist > Flags["ESP/MaxDistance"] then shouldShow = false end
        end
    end

    if not shouldShow then
        SafeSet(data.Nametag, "Enabled", false)
        if data.Highlight then SafeSet(data.Highlight, "Enabled", false) end
        return
    end

    if data.Nametag.Adornee ~= root then data.Nametag.Adornee = root end
    SafeSet(data.Nametag, "Enabled", true)
    SafeSet(data.Nametag, "MaxDistance", Flags["ESP/MaxDistance"])

    if data.Highlight then
        local hlActive = Flags["ESP/HighlightEnabled"]
        if data.Highlight.Adornee ~= char then data.Highlight.Adornee = char end
        SafeSet(data.Highlight, "Enabled", hlActive)
    end

    if Flags["ESP/ShowUsername"] then
        if data.LastName ~= player.Name then
            data.LastName = player.Name
            SafeSet(data.Label, "Text", player.Name)
        end
        SafeSet(data.Label, "Visible", true)
    else
        SafeSet(data.Label, "Visible", false)
    end

    local health, maxHealth = GetHealth(player)
    if data.HealthNum then
        SafeSet(data.HealthNum, "Visible", Flags["ESP/HealthIndicator"])
        if data.LastHealth ~= math.floor(health) or data.LastMaxHealth ~= math.floor(maxHealth) then
            data.LastHealth = math.floor(health)
            data.LastMaxHealth = math.floor(maxHealth)
            SafeSet(data.HealthNum, "Text", string.format("%d / %d", math.floor(health), math.floor(maxHealth)))
            local hpColor = GetHealthColor(health, maxHealth)
            SafeSet(data.HealthNum, "TextColor3", hpColor)
            if data.HealthBarFill then
                local pct = math.clamp(health / math.max(maxHealth, 1), 0, 1)
                SafeSet(data.HealthBarFill, "Size", UDim2.fromScale(pct, 1))
                SafeSet(data.HealthBarFill, "BackgroundColor3", hpColor)
            end
        end
    end
    if data.HealthBarBG then
        SafeSet(data.HealthBarBG, "Visible", Flags["ESP/HealthIndicator"])
    end

    if data.DistanceLabel then
        SafeSet(data.DistanceLabel, "Visible", Flags["ESP/ShowDistance"])
        local distRounded = math.floor(dist)
        if math.abs(data.LastDistance - distRounded) >= 1 then
            data.LastDistance = distRounded
            SafeSet(data.DistanceLabel, "Text", string.format("%d studs", distRounded))
        end
    end

    if data.EquippedLabel then
        SafeSet(data.EquippedLabel, "Visible", Flags["ESP/ShowEquipped"])
        local tool = char:FindFirstChildOfClass("Tool")
        local toolName = tool and tool.Name or "Unarmed"
        local equippedStr = "[" .. toolName .. "]"
        if data.LastEquipped ~= equippedStr then
            data.LastEquipped = equippedStr
            SafeSet(data.EquippedLabel, "Text", equippedStr)
        end
    end

    local role = Flags["ESP/ColorByRole"] and DetectRole(player) or nil
    if role then
        if data.LastRole ~= role then
            data.LastRole = role
            local color = ROLE_COLORS[role] or ROLE_COLORS.Unknown
            SafeSet(data.Label, "TextColor3", color)
            if data.Highlight then
                SafeSet(data.Highlight, "FillColor", color)
                SafeSet(data.Highlight, "OutlineColor", color)
            end
        end
    else
        if data.LastRole ~= "plain" then
            data.LastRole = "plain"
            SafeSet(data.Label, "TextColor3", ROLE_COLORS.Neutral)
            if data.Highlight then
                SafeSet(data.Highlight, "FillColor", ROLE_COLORS.Neutral)
                SafeSet(data.Highlight, "OutlineColor", ROLE_COLORS.Neutral)
            end
        end
    end

    local op = Flags["ESP/NametagOpacity"]
    SafeSet(data.Label, "TextTransparency", op)
    if data.HealthNum then SafeSet(data.HealthNum, "TextTransparency", op) end
    if data.DistanceLabel then SafeSet(data.DistanceLabel, "TextTransparency", op) end
    if data.EquippedLabel then SafeSet(data.EquippedLabel, "TextTransparency", op) end
    if data.HealthBarBG then SafeSet(data.HealthBarBG, "BackgroundTransparency", op) end
    if data.HealthBarFill then SafeSet(data.HealthBarFill, "BackgroundTransparency", op) end
end

local function RefreshESP()
    local players = Players:GetPlayers()
    for _, p in ipairs(players) do
        if p ~= LocalPlayer then
            if not ESP.Objects[p] then CreateNametag(p) end
            UpdateNametag(p)
        end
    end
    for p, _ in pairs(ESP.Objects) do
        if not p.Parent then RemoveNametag(p) end
    end
end

-- ══════════════════════════════════════════════════════════════════
--  NOCLIP
-- ══════════════════════════════════════════════════════════════════
local Noclip = {
    Broken = {},
    BrokenCount = 0,
    OverlapParams = OverlapParams.new(),
    RaycastParams = RaycastParams.new(),
    ScratchIgnore = {},
}

Noclip.OverlapParams.FilterType = Enum.RaycastFilterType.Exclude
Noclip.OverlapParams.MaxParts = 0
Noclip.RaycastParams.FilterType = Enum.RaycastFilterType.Exclude
Noclip.RaycastParams.IgnoreWater = true

local _feetPartsBuffer = {}

local function GetPartsUnderFeet(char)
    table.clear(_feetPartsBuffer)
    if not char then return _feetPartsBuffer end

    local root = char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
    if not root then return _feetPartsBuffer end

    local ignore = Noclip.ScratchIgnore
    table.clear(ignore)
    ignore[1] = char
    Noclip.RaycastParams.FilterDescendantsInstances = ignore

    local basePos = root.Position - Vector3.new(0, 2.5, 0)
    local direction = Vector3.new(0, -12, 0)
    local rootCF = root.CFrame

    local relOffsets = {
        Vector3.new(0, 0, 0),
        Vector3.new(-1.2, 0, -1.2), Vector3.new(1.2, 0, -1.2),
        Vector3.new(-1.2, 0, 1.2),  Vector3.new(1.2, 0, 1.2),
    }

    for _, relOff in ipairs(relOffsets) do
        local origin = basePos + rootCF:VectorToWorldSpace(relOff)
        local ok, result = pcall(function()
            return Workspace:Raycast(origin, direction, Noclip.RaycastParams)
        end)
        if ok and result and result.Instance then
            _feetPartsBuffer[result.Instance] = true
        end
    end

    return _feetPartsBuffer
end

local function ShouldBreak(part, feetParts)
    if not part or not part:IsA("BasePart") then return false end
    if part:IsA("Terrain") then return false end
    if not part.Parent then return false end

    local myChar = LocalPlayer.Character
    if myChar and part:IsDescendantOf(myChar) then return false end

    local model = part:FindFirstAncestorOfClass("Model")
    if model and Players:GetPlayerFromCharacter(model) then return false end

    if part:IsA("SpawnLocation") then return false end

    if feetParts and feetParts[part] then return false end

    local name = string.lower(part.Name)
    if name == "baseplate" or name == "ground" or name == "floor"
    or name == "terrain" or name == "base" then
        return false
    end

    if part.Size.X > 400 and part.Size.Z > 400 then return false end

    if part.Anchored then
        if not Flags["Noclip/IncludeAnchored"] then return false end
    else
        if not Flags["Noclip/IncludeUnanchored"] then return false end
    end

    return true
end

local function BreakPart(part)
    if Noclip.Broken[part] then
        if part.CanCollide then
            pcall(function() part.CanCollide = false end)
        end
        return
    end

    Noclip.Broken[part] = { cc = part.CanCollide }
    Noclip.BrokenCount = Noclip.BrokenCount + 1
    pcall(function() part.CanCollide = false end)
end

local function RestorePart(part)
    local data = Noclip.Broken[part]
    if not data then return end
    pcall(function()
        if part and part.Parent and type(data) == "table" then
            part.CanCollide = data.cc
        end
    end)
    Noclip.Broken[part] = nil
    Noclip.BrokenCount = math.max(0, Noclip.BrokenCount - 1)
end

local function RestoreAll()
    for part, data in pairs(Noclip.Broken) do
        pcall(function()
            if part and part.Parent and type(data) == "table" then
                part.CanCollide = data.cc
            end
        end)
    end
    Noclip.Broken = {}
    Noclip.BrokenCount = 0
end

local function ScanAndBreak()
    local char = LocalPlayer.Character
    if not char then return end
    local root = char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
    if not root then return end

    local radius = Flags["Noclip/Radius"] or 300
    local origin = root.Position

    local feetParts = GetPartsUnderFeet(char)

    local ignore = Noclip.ScratchIgnore
    table.clear(ignore)
    ignore[1] = char
    Noclip.OverlapParams.FilterDescendantsInstances = ignore

    local ok, parts = pcall(function()
        return Workspace:GetPartBoundsInRadius(origin, radius, Noclip.OverlapParams)
    end)
    if not ok or not parts then return end

    for _, part in ipairs(parts) do
        if ShouldBreak(part, feetParts) then
            BreakPart(part)
        end
    end
end

local function EnforceNoclip()
    if next(Noclip.Broken) == nil then return end

    local char = LocalPlayer.Character
    if not char then return end
    local root = char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
    if not root then return end

    local feetParts = GetPartsUnderFeet(char)

    local toRemove = nil
    local toRestore = nil

    for part, _ in pairs(Noclip.Broken) do
        if not part or not part.Parent then
            toRemove = toRemove or {}
            toRemove[#toRemove + 1] = part
        elseif feetParts[part] then
            toRestore = toRestore or {}
            toRestore[#toRestore + 1] = part
        elseif part.CanCollide then
            pcall(function() part.CanCollide = false end)
        end
    end

    if toRemove then
        for _, part in ipairs(toRemove) do
            if Noclip.Broken[part] then
                Noclip.Broken[part] = nil
                Noclip.BrokenCount = math.max(0, Noclip.BrokenCount - 1)
            end
        end
    end
    if toRestore then
        for _, part in ipairs(toRestore) do
            RestorePart(part)
        end
    end
end

-- ══════════════════════════════════════════════════════════════════
--  AUTO-GIVE
-- ══════════════════════════════════════════════════════════════════
local AutoGive = { Running = false }

local MP5_ORIGIN_POS       = Vector3.new(813.698, 97.850, 2229.396)
local REMINGTON_ORIGIN_POS = Vector3.new(820.298, 97.850, 2229.396)
local MATCH_TOLERANCE      = 5

local TP_HEIGHT_OFFSET = 6
local TP_DWELL_TIME    = 0.8

local function FindTouchGiverByPos(targetPos)
    for _, obj in ipairs(Workspace:GetChildren()) do
        if obj.Name == "TouchGiver" and obj:IsA("Model") then
            local ok, pos = pcall(function()
                local primary = obj.PrimaryPart or obj:FindFirstChildWhichIsA("BasePart")
                return primary and primary.Position
            end)
            if ok and pos and (pos - targetPos).Magnitude < MATCH_TOLERANCE then
                return obj, pos
            end
        end
    end
    return nil
end

local function GetMyRoot()
    local char = LocalPlayer.Character
    if not char then return nil end
    return char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
end

local function TeleportToTouchGiver(touchPos, myRoot, homeCFrame)
    if not myRoot or not touchPos then return end

    local targetCFrame = CFrame.new(touchPos + Vector3.new(0, TP_HEIGHT_OFFSET, 0))

    pcall(function()
        myRoot.CFrame = targetCFrame
    end)

    task.wait(TP_DWELL_TIME)

    pcall(function()
        myRoot.CFrame = homeCFrame
    end)

    task.wait(0.1)
end

local function SpawnTouchGiversInFront(force)
    if not force and not Flags["AutoGive/Enabled"] then return end
    if AutoGive.Running then return end
    AutoGive.Running = true

    task.wait(1.2)

    local myRoot = GetMyRoot()
    if not myRoot then
        AutoGive.Running = false
        return
    end

    local homeCFrame = myRoot.CFrame

    local _, mp5Pos = FindTouchGiverByPos(MP5_ORIGIN_POS)
    local _, remPos = FindTouchGiverByPos(REMINGTON_ORIGIN_POS)

    if not mp5Pos and not remPos then
        AutoGive.Running = false
        return
    end

    if remPos then
        TeleportToTouchGiver(remPos, myRoot, homeCFrame)
        myRoot = GetMyRoot()
        if myRoot then homeCFrame = myRoot.CFrame end
    end

    if mp5Pos then
        TeleportToTouchGiver(mp5Pos, myRoot, homeCFrame)
    end

    local finalRoot = GetMyRoot()
    if finalRoot then
        pcall(function() finalRoot.CFrame = homeCFrame end)
    end

    AutoGive.Running = false
end

-- ══════════════════════════════════════════════════════════════════
--  ARREST SYSTEM
-- ══════════════════════════════════════════════════════════════════
local ArrestState = {
    Running = false,
    SelectedPlayer = nil,
    ChaserConn = nil,
    EndTime = 0,
    HomeCFrame = nil,
    TargetRoot = nil,
}

local function GetCriminalsList()
    local out = {}
    for _, p in ipairs(GetPlayersCache()) do
        if p ~= LocalPlayer and p.Parent then
            local role = DetectRole(p)
            if role == "Criminal" then
                local char, root = GetCharacter(p)
                if char and root then
                    local myRoot = GetMyRoot()
                    local dist = myRoot and (root.Position - myRoot.Position).Magnitude or 0
                    table.insert(out, { player = p, dist = dist })
                end
            end
        end
    end
    table.sort(out, function(a, b) return a.dist < b.dist end)
    return out
end

local function EquipToolByName(toolName)
    local char = LocalPlayer.Character
    local backpack = LocalPlayer:FindFirstChild("Backpack")
    if not char or not backpack then return false end

    local equipped = char:FindFirstChildOfClass("Tool")
    if equipped and equipped.Name == toolName then return true end

    local tool = backpack:FindFirstChild(toolName)
    if not tool or not tool:IsA("Tool") then return false end

    local hum = char:FindFirstChildOfClass("Humanoid")
    if not hum then return false end

    pcall(function() hum:EquipTool(tool) end)
    return true
end

local function GetBehindCFrame(targetRoot)
    if not targetRoot then return nil end
    local dist = Flags["Arrest/StickDistance"] or 2.2
    local height = Flags["Arrest/StickHeight"] or 0.0
    return targetRoot.CFrame * CFrame.new(0, height, dist)
end

local function StartChaser(targetRoot)
    local char = LocalPlayer.Character
    if not char then return nil end
    local myRoot = char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
    local hum = char:FindFirstChildOfClass("Humanoid")
    if not myRoot or not hum then return nil end

    pcall(function() hum.AutoRotate = false end)

    local chaserConn
    chaserConn = RunService.RenderStepped:Connect(function()
        local myR = LocalPlayer.Character
            and (LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or LocalPlayer.Character.PrimaryPart)
        if not myR then return end

        local behind = GetBehindCFrame(targetRoot)
        if behind then
            pcall(function()
                myR.CFrame = behind
                myR.AssemblyLinearVelocity = Vector3.zero
            end)
        end
    end)

    return chaserConn
end

local function StopChaser(restoreCFrame)
    if ArrestState.ChaserConn then
        pcall(function() ArrestState.ChaserConn:Disconnect() end)
        ArrestState.ChaserConn = nil
    end

    local char = LocalPlayer.Character
    local hum = char and char:FindFirstChildOfClass("Humanoid")
    if hum then
        pcall(function() hum.AutoRotate = true end)
    end

    local myRoot = char and (char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart)
    if myRoot and restoreCFrame then
        pcall(function() myRoot.CFrame = restoreCFrame end)
    end

    ArrestState.TargetRoot = nil
    ArrestState.HomeCFrame = nil
    ArrestState.EndTime = 0
end

local function ArrestPlayer(targetPlayer)
    if not targetPlayer or not targetPlayer.Parent then return false end
    if ArrestState.Running then return false end

    ArrestState.Running = true

    local myChar = LocalPlayer.Character
    local myRoot = myChar and (myChar:FindFirstChild("HumanoidRootPart") or myChar.PrimaryPart)
    if not myRoot then
        ArrestState.Running = false
        return false
    end

    local homeCFrame = myRoot.CFrame

    local toolName = Flags["Arrest/ToolName"] or "Handcuffs"
    local equipped = EquipToolByName(toolName)
    if not equipped then
        Notify("Arrest", "Could not equip " .. toolName .. ". Make sure you're a Guard and have it in your backpack.")
        ArrestState.Running = false
        return false
    end

    task.wait(0.2)

    local targetChar, targetRoot = GetCharacter(targetPlayer)
    if not targetChar or not targetRoot then
        Notify("Arrest", "Target character not found.")
        pcall(function() myRoot.CFrame = homeCFrame end)
        ArrestState.Running = false
        return false
    end

    local behind = GetBehindCFrame(targetRoot)
    if behind then
        pcall(function() myRoot.CFrame = behind end)
        task.wait(0.1)
    end

    ArrestState.TargetRoot = targetRoot
    ArrestState.HomeCFrame = homeCFrame
    ArrestState.EndTime = tick() + (Flags["Arrest/StickDuration"] or 5.0)
    ArrestState.ChaserConn = StartChaser(targetRoot)

    task.spawn(function()
        while ArrestState.Running do
            task.wait(0.1)
            if tick() >= ArrestState.EndTime then
                if ArrestState.Running then
                    ArrestState.Running = false
                    StopChaser(homeCFrame)
                    Notify("Arrest", "Stick ended. You can click now or press the button again.")
                end
                break
            end
        end
    end)

    Notify("Arrest", string.format("Sticking to %s for %.1fs. Click on them to arrest!", targetPlayer.Name, Flags["Arrest/StickDuration"] or 5.0))
    return true
end

local function StopArrestStick()
    if not ArrestState.Running then return end
    ArrestState.Running = false
    StopChaser(ArrestState.HomeCFrame)
    Notify("Arrest", "Stopped sticking.")
end

-- ══════════════════════════════════════════════════════════════════
--  NOTIFICATION
-- ══════════════════════════════════════════════════════════════════
local NotifyGui, NotifyContainer

local function EnsureNotifyGui()
    if NotifyGui and NotifyGui.Parent then return end
    local parent = gethui and gethui() or game:GetService("CoreGui")
    NotifyGui = Instance.new("ScreenGui")
    NotifyGui.Name = "PrisonVision_Notify"
    NotifyGui.DisplayOrder = 1000
    NotifyGui.IgnoreGuiInset = true
    NotifyGui.ResetOnSpawn = false
    pcall(function() NotifyGui.Parent = parent end)
    if not NotifyGui.Parent then NotifyGui.Parent = game:GetService("CoreGui") end

    NotifyContainer = Instance.new("Frame")
    NotifyContainer.Name = "Container"
    NotifyContainer.Size = UDim2.new(0, 300, 1, -40)
    NotifyContainer.Position = UDim2.new(1, -20, 1, -20)
    NotifyContainer.AnchorPoint = Vector2.new(1, 1)
    NotifyContainer.BackgroundTransparency = 1
    NotifyContainer.Parent = NotifyGui

    local layout = Instance.new("UIListLayout")
    layout.HorizontalAlignment = Enum.HorizontalAlignment.Right
    layout.VerticalAlignment = Enum.VerticalAlignment.Bottom
    layout.SortOrder = Enum.SortOrder.LayoutOrder
    layout.Padding = UDim.new(0, 8)
    layout.Parent = NotifyContainer
end

function Notify(title, text)
    EnsureNotifyGui()
    local frame = Instance.new("Frame")
    frame.Size = UDim2.new(0, 280, 0, 0)
    frame.AutomaticSize = Enum.AutomaticSize.Y
    frame.BackgroundColor3 = THEME.Background
    frame.BackgroundTransparency = 1
    frame.BorderSizePixel = 0
    frame.Parent = NotifyContainer

    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0, 8); corner.Parent = frame

    local stroke = Instance.new("UIStroke")
    stroke.Color = THEME.Accent; stroke.Thickness = 1; stroke.Transparency = 1
    stroke.Parent = frame

    local pad = Instance.new("UIPadding")
    pad.PaddingTop = UDim.new(0, 10); pad.PaddingBottom = UDim.new(0, 10)
    pad.PaddingLeft = UDim.new(0, 12); pad.PaddingRight = UDim.new(0, 12)
    pad.Parent = frame

    local titleLbl = Instance.new("TextLabel")
    titleLbl.Size = UDim2.new(1, 0, 0, 16)
    titleLbl.BackgroundTransparency = 1
    titleLbl.Text = title
    titleLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    titleLbl.TextSize = 13
    titleLbl.TextColor3 = THEME.Accent
    titleLbl.TextXAlignment = Enum.TextXAlignment.Left
    titleLbl.TextTransparency = 1
    titleLbl.Parent = frame

    local bodyLbl = Instance.new("TextLabel")
    bodyLbl.Size = UDim2.new(1, 0, 0, 0)
    bodyLbl.AutomaticSize = Enum.AutomaticSize.Y
    bodyLbl.Position = UDim2.new(0, 0, 0, 18)
    bodyLbl.BackgroundTransparency = 1
    bodyLbl.Text = tostring(text or "")
    bodyLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular, Enum.FontStyle.Normal)
    bodyLbl.TextSize = 11
    bodyLbl.TextColor3 = THEME.Text
    bodyLbl.TextWrapped = true
    bodyLbl.TextXAlignment = Enum.TextXAlignment.Left
    bodyLbl.TextTransparency = 1
    bodyLbl.Parent = frame

    TweenService:Create(frame, TweenInfo.new(0.2), {BackgroundTransparency = 0.05}):Play()
    TweenService:Create(stroke, TweenInfo.new(0.2), {Transparency = 0.4}):Play()
    TweenService:Create(titleLbl, TweenInfo.new(0.2), {TextTransparency = 0}):Play()
    TweenService:Create(bodyLbl, TweenInfo.new(0.2), {TextTransparency = 0}):Play()

    task.delay(3, function()
        local t1 = TweenService:Create(frame, TweenInfo.new(0.2), {BackgroundTransparency = 1})
        TweenService:Create(stroke, TweenInfo.new(0.2), {Transparency = 1}):Play()
        TweenService:Create(titleLbl, TweenInfo.new(0.2), {TextTransparency = 1}):Play()
        TweenService:Create(bodyLbl, TweenInfo.new(0.2), {TextTransparency = 1}):Play()
        t1:Play()
        t1.Completed:Connect(function() SafeDestroy(frame) end)
    end)
end

-- ══════════════════════════════════════════════════════════════════
--  TELEPORT SYSTEM
-- ══════════════════════════════════════════════════════════════════
local TELEPORT_PRESETS = {
    {name = "Entrance Tower",          pos = Vector3.new(510,  122, 2325)},
    {name = "Police Car Spawn",        pos = Vector3.new(595,  98,  2497)},
    {name = "Police Armory",           pos = Vector3.new(816,  99,  2237)},
    {name = "Rooftop (Escape)",        pos = Vector3.new(946,  135, 2201)},
    {name = "Yard Tower",              pos = Vector3.new(822,  125, 2587)},
    {name = "Hammer",                  pos = Vector3.new(806,  98,  2525)},
    {name = "Knife",                   pos = Vector3.new(707,  97,  2508)},
    {name = "Criminal Armory",         pos = Vector3.new(-936, 94,  2043)},
    {name = "Criminal Car Spawn",      pos = Vector3.new(-912, 95,  2160)},
    {name = "Criminal Tower Rooftop",  pos = Vector3.new(-371, 114, 1992)},
}

local function TeleportToPosition(pos)
    if typeof(pos) ~= "Vector3" then
        Notify("Teleport", "Invalid position.")
        return false
    end

    local char = LocalPlayer.Character
    if not char then
        Notify("Teleport", "Character not found.")
        return false
    end

    local root = char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
    if not root then
        Notify("Teleport", "HumanoidRootPart not found.")
        return false
    end

    -- Try a smarter teleport: offset upward if player would clip
    local offsetY = 3
    pcall(function()
        root.CFrame = CFrame.new(pos + Vector3.new(0, offsetY, 0))
        root.AssemblyLinearVelocity = Vector3.zero
        root.AssemblyAngularVelocity = Vector3.zero
    end)

    Notify("Teleport", string.format("Teleported to (%.0f, %.0f, %.0f)", pos.X, pos.Y, pos.Z))
    return true
end

local function GetCurrentPosition()
    local char = LocalPlayer.Character
    if not char then return nil end
    local root = char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
    if not root then return nil end
    return root.Position
end

-- ══════════════════════════════════════════════════════════════════
--  UI — Main Window
-- ══════════════════════════════════════════════════════════════════
local UI = {}
local State = {
    Visible = true,
    Minimized = false,
    Tabs = {},
    CurrentTab = nil,
    Updaters = {},
    MainFrame = nil,
    ScreenGui = nil,
}

local function EnsureMainGui()
    if State.ScreenGui and State.ScreenGui.Parent then return State.ScreenGui end
    local parent = gethui and gethui() or game:GetService("CoreGui")
    local gui = Instance.new("ScreenGui")
    gui.Name = "PrisonVision_Main"
    gui.DisplayOrder = 999
    gui.IgnoreGuiInset = true
    gui.ResetOnSpawn = false
    gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
    pcall(function() gui.Parent = parent end)
    if not gui.Parent then gui.Parent = game:GetService("CoreGui") end
    State.ScreenGui = gui
    return gui
end

function UI.CreateWindow(title)
    local gui = EnsureMainGui()

    local main = Instance.new("CanvasGroup")
    main.Name = "MainFrame"
    main.Size = UDim2.fromOffset(520, 360)
    main.Position = UDim2.fromScale(0.5, 0.5)
    main.AnchorPoint = Vector2.new(0.5, 0.5)
    main.BackgroundColor3 = THEME.Background
    main.BorderSizePixel = 0
    main.ClipsDescendants = true
    main.Parent = gui
    State.MainFrame = main

    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0, 10); corner.Parent = main

    local stroke = Instance.new("UIStroke")
    stroke.Color = Color3.fromRGB(50, 50, 58); stroke.Thickness = 1
    stroke.Parent = main

    local sidebar = Instance.new("Frame")
    sidebar.Name = "Sidebar"
    sidebar.Size = UDim2.new(0, 150, 1, 0)
    sidebar.BackgroundColor3 = THEME.Sidebar
    sidebar.BorderSizePixel = 0
    sidebar.Parent = main
    local sbCorner = Instance.new("UICorner")
    sbCorner.CornerRadius = UDim.new(0, 10); sbCorner.Parent = sidebar

    local sbFix = Instance.new("Frame")
    sbFix.Size = UDim2.new(0, 10, 1, 0)
    sbFix.Position = UDim2.new(1, -10, 0, 0)
    sbFix.BackgroundColor3 = THEME.Sidebar
    sbFix.BorderSizePixel = 0
    sbFix.Parent = sidebar

    local titleLbl = Instance.new("TextLabel")
    titleLbl.Size = UDim2.new(1, -20, 0, 30)
    titleLbl.Position = UDim2.new(0, 14, 0, 8)
    titleLbl.BackgroundTransparency = 1
    titleLbl.Text = title
    titleLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.ExtraBold, Enum.FontStyle.Normal)
    titleLbl.TextSize = 18
    titleLbl.TextColor3 = THEME.Accent
    titleLbl.TextXAlignment = Enum.TextXAlignment.Left
    titleLbl.Parent = sidebar

    local verLbl = Instance.new("TextLabel")
    verLbl.Size = UDim2.new(1, -20, 0, 14)
    verLbl.Position = UDim2.new(0, 14, 0, 34)
    verLbl.BackgroundTransparency = 1
    verLbl.Text = "v" .. VERSION
    verLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Medium, Enum.FontStyle.Normal)
    verLbl.TextSize = 10
    verLbl.TextColor3 = THEME.TextDark
    verLbl.TextXAlignment = Enum.TextXAlignment.Left
    verLbl.Parent = sidebar

    local tabContainer = Instance.new("ScrollingFrame")
    tabContainer.Name = "Tabs"
    tabContainer.Size = UDim2.new(1, 0, 1, -60)
    tabContainer.Position = UDim2.new(0, 0, 0, 60)
    tabContainer.BackgroundTransparency = 1
    tabContainer.BorderSizePixel = 0
    tabContainer.ScrollBarThickness = 2
    tabContainer.CanvasSize = UDim2.new(0, 0, 0, 0)
    tabContainer.AutomaticCanvasSize = Enum.AutomaticSize.Y
    tabContainer.Parent = sidebar
    State.TabContainer = tabContainer

    local tabLayout = Instance.new("UIListLayout")
    tabLayout.Padding = UDim.new(0, 4)
    tabLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
    tabLayout.SortOrder = Enum.SortOrder.LayoutOrder
    tabLayout.Parent = tabContainer

    local content = Instance.new("Frame")
    content.Name = "Content"
    content.Size = UDim2.new(1, -160, 1, -20)
    content.Position = UDim2.new(0, 155, 0, 10)
    content.BackgroundTransparency = 1
    content.ClipsDescendants = true
    content.Parent = main
    State.ContentArea = content

    local closeBtn = Instance.new("TextButton")
    closeBtn.Name = "Minimize"
    closeBtn.Size = UDim2.fromOffset(26, 26)
    closeBtn.Position = UDim2.new(1, -32, 0, 8)
    closeBtn.BackgroundColor3 = THEME.Element
    closeBtn.Text = "–"
    closeBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    closeBtn.TextSize = 18
    closeBtn.TextColor3 = THEME.Text
    closeBtn.BorderSizePixel = 0
    closeBtn.Parent = main
    local cbCorner = Instance.new("UICorner")
    cbCorner.CornerRadius = UDim.new(0, 6); cbCorner.Parent = closeBtn

    local dragging, dragStart, startPos = false, nil, nil

    local function beginDrag(input)
        if input.UserInputType ~= Enum.UserInputType.MouseButton1
        and input.UserInputType ~= Enum.UserInputType.Touch then return end
        dragging = true
        dragStart = input.Position
        startPos = main.Position
    end

    main.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 then
            beginDrag(input)
        end
    end)
    sidebar.InputBegan:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1 then
            beginDrag(input)
        end
    end)

    UserInputService.InputChanged:Connect(function(input)
        if not dragging then return end
        if input.UserInputType ~= Enum.UserInputType.MouseMovement
        and input.UserInputType ~= Enum.UserInputType.Touch then return end
        local delta = input.Position - dragStart
        main.Position = UDim2.new(
            startPos.X.Scale, startPos.X.Offset + delta.X,
            startPos.Y.Scale, startPos.Y.Offset + delta.Y
        )
    end)

    UserInputService.InputEnded:Connect(function(input)
        if input.UserInputType == Enum.UserInputType.MouseButton1
        or input.UserInputType == Enum.UserInputType.Touch then
            dragging = false
        end
    end)

    local minimized = false
    local normalSize = main.Size

    State.ToggleMinimize = function()
        minimized = not minimized
        State.Minimized = minimized
        if minimized then
            normalSize = main.Size
            TweenService:Create(main, TweenInfo.new(0.25, Enum.EasingStyle.Quart, Enum.EasingDirection.Out), {
                Size = UDim2.fromOffset(200, 34),
            }):Play()
            sidebar.Visible = false
            content.Visible = false
            closeBtn.Text = "+"
        else
            TweenService:Create(main, TweenInfo.new(0.25, Enum.EasingStyle.Quart, Enum.EasingDirection.Out), {
                Size = normalSize,
            }):Play()
            task.wait(0.1)
            sidebar.Visible = true
            content.Visible = true
            closeBtn.Text = "–"
        end
    end

    closeBtn.MouseButton1Click:Connect(State.ToggleMinimize)

    UserInputService.InputBegan:Connect(function(input, gp)
        if gp then return end
        if input.KeyCode == Enum.KeyCode.CapsLock then
            State.ToggleMinimize()
        end
    end)

    return UI
end

function UI.CreateTab(name)
    if not State.TabContainer then return end

    local btn = Instance.new("TextButton")
    btn.Name = name .. "Tab"
    btn.Size = UDim2.new(1, -16, 0, 32)
    btn.BackgroundColor3 = THEME.Element
    btn.BackgroundTransparency = 1
    btn.Text = ""
    btn.BorderSizePixel = 0
    btn.AutoButtonColor = false
    btn.Parent = State.TabContainer
    local bCorner = Instance.new("UICorner")
    bCorner.CornerRadius = UDim.new(0, 6); bCorner.Parent = btn

    local label = Instance.new("TextLabel")
    label.Size = UDim2.new(1, -16, 1, 0)
    label.Position = UDim2.new(0, 12, 0, 0)
    label.BackgroundTransparency = 1
    label.Text = name
    label.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
    label.TextSize = 13
    label.TextColor3 = THEME.TextDark
    label.TextXAlignment = Enum.TextXAlignment.Left
    label.Parent = btn

    local indicator = Instance.new("Frame")
    indicator.Size = UDim2.new(0, 3, 0, 14)
    indicator.Position = UDim2.new(0, 0, 0.5, -7)
    indicator.BackgroundColor3 = THEME.Accent
    indicator.BackgroundTransparency = 1
    indicator.BorderSizePixel = 0
    indicator.Parent = btn
    local iCorner = Instance.new("UICorner")
    iCorner.CornerRadius = UDim.new(1, 0); iCorner.Parent = indicator

    local page = Instance.new("ScrollingFrame")
    page.Name = name .. "Page"
    page.Size = UDim2.new(1, 0, 1, 0)
    page.BackgroundTransparency = 1
    page.BorderSizePixel = 0
    page.ScrollBarThickness = 2
    page.Visible = false
    page.CanvasSize = UDim2.new(0, 0, 0, 0)
    page.AutomaticCanvasSize = Enum.AutomaticSize.Y
    page.Parent = State.ContentArea

    local layout = Instance.new("UIListLayout")
    layout.Padding = UDim.new(0, 5)
    layout.SortOrder = Enum.SortOrder.LayoutOrder
    layout.Parent = page

    local pad = Instance.new("UIPadding")
    pad.PaddingRight = UDim.new(0, 6)
    pad.Parent = page

    local function select()
        for _, t in pairs(State.Tabs) do
            TweenService:Create(t.Label, TweenInfo.new(0.15), {TextColor3 = THEME.TextDark}):Play()
            TweenService:Create(t.Indicator, TweenInfo.new(0.15), {BackgroundTransparency = 1}):Play()
            TweenService:Create(t.Button, TweenInfo.new(0.15), {BackgroundTransparency = 1}):Play()
            t.Page.Visible = false
        end
        TweenService:Create(label, TweenInfo.new(0.15), {TextColor3 = THEME.Text}):Play()
        TweenService:Create(indicator, TweenInfo.new(0.15), {BackgroundTransparency = 0}):Play()
        TweenService:Create(btn, TweenInfo.new(0.15), {BackgroundTransparency = 0.5}):Play()
        page.Visible = true
        State.CurrentTab = name
    end

    btn.MouseButton1Click:Connect(select)

    table.insert(State.Tabs, {
        Button = btn, Label = label, Indicator = indicator,
        Page = page, Select = select,
    })

    if #State.Tabs == 1 then select() end
    return page
end

function UI.CreateSection(page, name)
    local container = Instance.new("Frame")
    container.Size = UDim2.new(1, 0, 0, 28)
    container.BackgroundTransparency = 1
    container.Parent = page

    local lbl = Instance.new("TextLabel")
    lbl.Size = UDim2.new(1, 0, 1, 0)
    lbl.Position = UDim2.new(0, 2, 0, 4)
    lbl.BackgroundTransparency = 1
    lbl.Text = string.upper(name)
    lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    lbl.TextSize = 10
    lbl.TextColor3 = THEME.Accent
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.Parent = container
end

function UI.CreateToggle(page, text, flag, default, callback)
    local frame = Instance.new("Frame")
    frame.Size = UDim2.new(1, 0, 0, 38)
    frame.BackgroundColor3 = THEME.Element
    frame.BorderSizePixel = 0
    frame.Parent = page
    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0, 6); corner.Parent = frame

    local lbl = Instance.new("TextLabel")
    lbl.Size = UDim2.new(1, -70, 1, 0)
    lbl.Position = UDim2.new(0, 12, 0, 0)
    lbl.BackgroundTransparency = 1
    lbl.Text = text
    lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
    lbl.TextSize = 12
    lbl.TextColor3 = THEME.Text
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.Parent = frame

    if Flags[flag] == nil then Flags[flag] = default end

    local switch = Instance.new("Frame")
    switch.Size = UDim2.fromOffset(44, 22)
    switch.AnchorPoint = Vector2.new(1, 0.5)
    switch.Position = UDim2.new(1, -12, 0.5, 0)
    switch.BackgroundColor3 = Flags[flag] and THEME.Accent or Color3.fromRGB(55, 55, 62)
    switch.BorderSizePixel = 0
    switch.Parent = frame
    local swCorner = Instance.new("UICorner")
    swCorner.CornerRadius = UDim.new(1, 0); swCorner.Parent = switch

    local knob = Instance.new("Frame")
    knob.Size = UDim2.fromOffset(18, 18)
    knob.AnchorPoint = Vector2.new(0, 0.5)
    knob.Position = Flags[flag] and UDim2.new(1, -20, 0.5, 0) or UDim2.new(0, 2, 0.5, 0)
    knob.BackgroundColor3 = Color3.fromRGB(240, 240, 245)
    knob.BorderSizePixel = 0
    knob.Parent = switch
    local kCorner = Instance.new("UICorner")
    kCorner.CornerRadius = UDim.new(1, 0); kCorner.Parent = knob

    local clickArea = Instance.new("TextButton")
    clickArea.Size = UDim2.new(1, 0, 1, 0)
    clickArea.BackgroundTransparency = 1
    clickArea.Text = ""
    clickArea.Parent = switch

    local function updateVisuals(state)
        TweenService:Create(switch, TweenInfo.new(0.15), {
            BackgroundColor3 = state and THEME.Accent or Color3.fromRGB(55, 55, 62),
        }):Play()
        TweenService:Create(knob, TweenInfo.new(0.15), {
            Position = state and UDim2.new(1, -20, 0.5, 0) or UDim2.new(0, 2, 0.5, 0),
        }):Play()
    end

    State.Updaters[flag] = function(state)
        updateVisuals(state)
        if callback then pcall(callback, state) end
    end

    clickArea.MouseButton1Click:Connect(function()
        Flags[flag] = not Flags[flag]
        updateVisuals(Flags[flag])
        if callback then pcall(callback, Flags[flag]) end
    end)
end

function UI.CreateNumericInput(page, text, flag, default, minV, maxV, step, unit, callback, lockable)
    local frame = Instance.new("Frame")
    frame.Size = UDim2.new(1, 0, 0, 50)
    frame.BackgroundColor3 = THEME.Element
    frame.BorderSizePixel = 0
    frame.Parent = page
    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0, 6); corner.Parent = frame

    local lbl = Instance.new("TextLabel")
    lbl.Size = UDim2.new(0.55, lockable and -42 or -12, 1, 0)
    lbl.Position = UDim2.new(0, 12, 0, 0)
    lbl.BackgroundTransparency = 1
    lbl.Text = text
    lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
    lbl.TextSize = 12
    lbl.TextColor3 = THEME.Text
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.Parent = frame

    if lockable then
        local locked = Flags[flag .. "/Locked"] == true
        local lockBtn = Instance.new("TextButton")
        lockBtn.Name = "Lock"
        lockBtn.Size = UDim2.fromOffset(24, 24)
        lockBtn.AnchorPoint = Vector2.new(1, 0.5)
        lockBtn.Position = UDim2.new(0.6, -12, 0.5, 0)
        lockBtn.BackgroundTransparency = 1
        lockBtn.Text = locked and "🔒" or "🔓"
        lockBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
        lockBtn.TextSize = 14
        lockBtn.TextColor3 = locked and THEME.Accent or THEME.TextDark
        lockBtn.Parent = frame

        State.Updaters[flag .. "/Locked"] = function(state)
            local l = state == true
            Flags[flag .. "/Locked"] = l
            SafeSet(lockBtn, "TextColor3", l and THEME.Accent or THEME.TextDark)
            SafeSet(lockBtn, "Text", l and "🔒" or "🔓")
        end

        lockBtn.MouseButton1Click:Connect(function()
            Flags[flag .. "/Locked"] = not Flags[flag .. "/Locked"]
            local l = Flags[flag .. "/Locked"]
            SafeSet(lockBtn, "TextColor3", l and THEME.Accent or THEME.TextDark)
            SafeSet(lockBtn, "Text", l and "🔒" or "🔓")
        end)
    end

    local inputFrame = Instance.new("Frame")
    inputFrame.Size = UDim2.new(0.45, -12, 0, 30)
    inputFrame.Position = UDim2.new(1, -12, 0.5, 0)
    inputFrame.AnchorPoint = Vector2.new(1, 0.5)
    inputFrame.BackgroundColor3 = Color3.fromRGB(48, 48, 55)
    inputFrame.BorderSizePixel = 0
    inputFrame.Parent = frame
    local ifCorner = Instance.new("UICorner")
    ifCorner.CornerRadius = UDim.new(0, 6); ifCorner.Parent = inputFrame

    if Flags[flag] == nil and not flag:match("^Humanoid/") then
        Flags[flag] = default
    end

    local box = Instance.new("TextBox")
    box.Size = UDim2.new(1, -50, 1, 0)
    box.Position = UDim2.new(0, 25, 0, 0)
    box.BackgroundTransparency = 1
    box.Text = tostring(Flags[flag] ~= nil and Flags[flag] or default) .. (unit or "")
    box.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    box.TextSize = 12
    box.TextColor3 = THEME.Accent
    box.ClearTextOnFocus = false
    box.Parent = inputFrame

    local function commit(val, isUser)
        val = math.clamp(tonumber(val) or (Flags[flag] or default), minV, maxV)
        if step and step > 0 then
            val = math.floor(val / step + 0.5) * step
        end
        Flags[flag] = val
        box.Text = tostring(val) .. (unit or "")
        if isUser then
            if callback then pcall(callback, val) end
        end
    end

    State.Updaters[flag] = function(val)
        if not box:IsFocused() then
            val = math.clamp(tonumber(val) or default, minV, maxV)
            box.Text = tostring(val) .. (unit or "")
            if callback then pcall(callback, val) end
        end
    end

    box.FocusLost:Connect(function()
        commit((box.Text:gsub("[^%d%.%-]", "")), true)
    end)

    local minus = Instance.new("TextButton")
    minus.Size = UDim2.new(0, 25, 1, 0)
    minus.BackgroundTransparency = 1
    minus.Text = "−"
    minus.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    minus.TextSize = 16
    minus.TextColor3 = THEME.TextDark
    minus.Parent = inputFrame
    minus.MouseButton1Click:Connect(function() commit((Flags[flag] or default) - (step or 1), true) end)

    local plus = Instance.new("TextButton")
    plus.Size = UDim2.new(0, 25, 1, 0)
    plus.Position = UDim2.new(1, -25, 0, 0)
    plus.BackgroundTransparency = 1
    plus.Text = "+"
    plus.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    plus.TextSize = 16
    plus.TextColor3 = THEME.TextDark
    plus.Parent = inputFrame
    plus.MouseButton1Click:Connect(function() commit((Flags[flag] or default) + (step or 1), true) end)
end

function UI.CreateButton(page, text, callback)
    local btn = Instance.new("TextButton")
    btn.Size = UDim2.new(1, 0, 0, 34)
    btn.BackgroundColor3 = THEME.Accent
    btn.BackgroundTransparency = 0.15
    btn.Text = text
    btn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    btn.TextSize = 12
    btn.TextColor3 = Color3.fromRGB(20, 20, 22)
    btn.BorderSizePixel = 0
    btn.Parent = page
    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0, 6); corner.Parent = btn
    local stroke = Instance.new("UIStroke")
    stroke.Color = THEME.Accent; stroke.Thickness = 1
    stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
    stroke.Parent = btn

    btn.MouseButton1Click:Connect(function()
        TweenService:Create(btn, TweenInfo.new(0.08), {Size = UDim2.new(1, -6, 0, 30)}):Play()
        task.wait(0.06)
        TweenService:Create(btn, TweenInfo.new(0.08), {Size = UDim2.new(1, 0, 0, 34)}):Play()
        if callback then pcall(callback) end
    end)
end

-- ══════════════════════════════════════════════════════════════════
--  BUILD UI
-- ══════════════════════════════════════════════════════════════════
UI.CreateWindow("PrisonVision")

-- ── Tab: Aim ─────────────────────────────────────────────────────
local aimTab = UI.CreateTab("Aim")

UI.CreateSection(aimTab, "Aim Assist")
UI.CreateToggle(aimTab, "Enable Aimlock", "Aim/AimLock", Flags["Aim/AimLock"], function(state)
    Notify("Aim", state and "Aimlock enabled." or "Aimlock disabled.")
    if not state then ClearAimLockState(true) end
end)
UI.CreateToggle(aimTab, "Ignore Teammates", "Aim/TeamCheck", Flags["Aim/TeamCheck"])
UI.CreateToggle(aimTab, "Visibility Check (Raycast)", "Aim/VisibilityCheck", Flags["Aim/VisibilityCheck"])

UI.CreateNumericInput(aimTab, "Attraction Strength", "Aim/AttractionStrength",
    Flags["Aim/AttractionStrength"], 0, 500, 10, "%")
UI.CreateNumericInput(aimTab, "FOV Radius", "Aim/FOV/Radius",
    Flags["Aim/FOV/Radius"], 0, 500, 5, "px")
UI.CreateToggle(aimTab, "Show FOV Circle", "Aim/FOV/ShowCircle", Flags["Aim/FOV/ShowCircle"])

UI.CreateSection(aimTab, "Dampening")
UI.CreateToggle(aimTab, "Enable Dampening", "Aim/Dampening", Flags["Aim/Dampening"])
UI.CreateNumericInput(aimTab, "Dampening Threshold", "Aim/Dampening/Threshold",
    Flags["Aim/Dampening/Threshold"], 0, 500, 5, "px")
UI.CreateNumericInput(aimTab, "Dampening Min Strength", "Aim/Dampening/Strength",
    Flags["Aim/Dampening/Strength"], 0, 100, 1, "%")

UI.CreateSection(aimTab, "Target Zone Selector")
do
    local TargetArea = Instance.new("Frame")
    TargetArea.Name = "TargetArea"
    TargetArea.Size = UDim2.new(1, 0, 0, 220)
    TargetArea.BackgroundTransparency = 1
    TargetArea.Parent = aimTab

    local PriorityLabel = Instance.new("TextLabel")
    PriorityLabel.Name = "PriorityLabel"
    PriorityLabel.Size = UDim2.new(1, 0, 0, 25)
    PriorityLabel.Position = UDim2.new(0, 0, 0, 0)
    PriorityLabel.BackgroundTransparency = 1
    PriorityLabel.Text = "Selected: Head"
    PriorityLabel.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    PriorityLabel.TextSize = 14
    PriorityLabel.TextColor3 = THEME.Text
    PriorityLabel.Parent = TargetArea

    local HumanoidRoot = Instance.new("Frame")
    HumanoidRoot.Name = "HumanoidRoot"
    HumanoidRoot.Size = UDim2.fromOffset(100, 180)
    HumanoidRoot.Position = UDim2.new(0.5, 0, 0.5, 15)
    HumanoidRoot.AnchorPoint = Vector2.new(0.5, 0.5)
    HumanoidRoot.BackgroundTransparency = 1
    HumanoidRoot.Parent = TargetArea

    local function UpdatePriorityLabel()
        local selected = {}
        for cat, on in pairs(Flags["Aim/TargetGroups"]) do
            if on then table.insert(selected, cat) end
        end
        if #selected == 0 or #selected == 6 then
            PriorityLabel.Text = "Selected: All body parts"
        else
            PriorityLabel.Text = "Selected: " .. table.concat(selected, ", ")
        end
    end

    local function CreatePart(name, size, pos, flagKey)
        local Part = Instance.new("TextButton")
        Part.Name = name
        Part.Size = size
        Part.Position = pos
        Part.BackgroundColor3 = Color3.fromRGB(255, 80, 80)
        Part.BackgroundTransparency = Flags["Aim/TargetGroups"][flagKey] and 0 or 1
        Part.BorderSizePixel = 0
        Part.Text = ""
        Part.Parent = HumanoidRoot

        local PartStroke = Instance.new("UIStroke")
        PartStroke.Color = Color3.fromRGB(255, 255, 255)
        PartStroke.Thickness = 1
        PartStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
        PartStroke.Parent = Part

        Part.MouseButton1Click:Connect(function()
            Flags["Aim/TargetGroups"][flagKey] = not Flags["Aim/TargetGroups"][flagKey]
            Part.BackgroundTransparency = Flags["Aim/TargetGroups"][flagKey] and 0 or 1
            UpdatePriorityLabel()
        end)
        return Part
    end

    local Head = CreatePart("Head", UDim2.fromOffset(30, 30), UDim2.new(0.5, 0, 0, 0), "Head")
    Head.AnchorPoint = Vector2.new(0.5, 0)
    local HeadCorner = Instance.new("UICorner", Head)
    HeadCorner.CornerRadius = UDim.new(1, 0)

    local Torso = CreatePart("Torso", UDim2.fromOffset(40, 60), UDim2.new(0.5, 0, 0, 35), "Torso")
    Torso.AnchorPoint = Vector2.new(0.5, 0)

    local LeftArm = CreatePart("LeftArm", UDim2.fromOffset(20, 60), UDim2.new(0.5, -25, 0, 35), "LeftArm")
    LeftArm.AnchorPoint = Vector2.new(1, 0)

    local RightArm = CreatePart("RightArm", UDim2.fromOffset(20, 60), UDim2.new(0.5, 25, 0, 35), "RightArm")
    RightArm.AnchorPoint = Vector2.new(0, 0)

    local LeftLeg = CreatePart("LeftLeg", UDim2.fromOffset(18, 70), UDim2.new(0.5, -2, 0, 100), "LeftLeg")
    LeftLeg.AnchorPoint = Vector2.new(1, 0)

    local RightLeg = CreatePart("RightLeg", UDim2.fromOffset(18, 70), UDim2.new(0.5, 2, 0, 100), "RightLeg")
    RightLeg.AnchorPoint = Vector2.new(0, 0)

    UpdatePriorityLabel()
end

-- ── Tab: ESP ─────────────────────────────────────────────────────
local espTab = UI.CreateTab("ESP")

UI.CreateSection(espTab, "ESP")
UI.CreateToggle(espTab, "Enable ESP", "ESP/Enabled", Flags["ESP/Enabled"], function(state)
    Notify("ESP", state and "ESP enabled." or "ESP disabled.")
    if not state then
        for _, data in pairs(ESP.Objects) do
            SafeSet(data.Nametag, "Enabled", false)
            if data.Highlight then SafeSet(data.Highlight, "Enabled", false) end
        end
    end
end)
UI.CreateToggle(espTab, "Show Username", "ESP/ShowUsername", Flags["ESP/ShowUsername"])
UI.CreateToggle(espTab, "Color By Role", "ESP/ColorByRole", Flags["ESP/ColorByRole"])
UI.CreateToggle(espTab, "Hide Teammates", "ESP/TeamCheck", Flags["ESP/TeamCheck"])
UI.CreateNumericInput(espTab, "Max Distance", "ESP/MaxDistance", Flags["ESP/MaxDistance"], 50, 10000, 50, "m")

UI.CreateSection(espTab, "Player Info")
UI.CreateToggle(espTab, "Show Health", "ESP/HealthIndicator", Flags["ESP/HealthIndicator"])
UI.CreateToggle(espTab, "Show Distance", "ESP/ShowDistance", Flags["ESP/ShowDistance"])
UI.CreateToggle(espTab, "Show Equipped", "ESP/ShowEquipped", Flags["ESP/ShowEquipped"])

UI.CreateSection(espTab, "Highlight")
UI.CreateToggle(espTab, "Enable Highlight", "ESP/HighlightEnabled", Flags["ESP/HighlightEnabled"], function(state)
    for _, data in pairs(ESP.Objects) do
        if data.Highlight then SafeSet(data.Highlight, "Enabled", state) end
    end
end)
UI.CreateNumericInput(espTab, "Fill Transparency", "ESP/HighlightFillAlpha",
    Flags["ESP/HighlightFillAlpha"], 0, 1, 0.05, "", function(val)
        for _, data in pairs(ESP.Objects) do
            if data.Highlight then SafeSet(data.Highlight, "FillTransparency", val) end
        end
    end)
UI.CreateNumericInput(espTab, "Outline Transparency", "ESP/HighlightOutlineAlpha",
    Flags["ESP/HighlightOutlineAlpha"], 0, 1, 0.05, "", function(val)
        for _, data in pairs(ESP.Objects) do
            if data.Highlight then SafeSet(data.Highlight, "OutlineTransparency", val) end
        end
    end)

UI.CreateSection(espTab, "Legend")
do
    local legend = Instance.new("Frame")
    legend.Size = UDim2.new(1, 0, 0, 120)
    legend.BackgroundColor3 = THEME.Element
    legend.BorderSizePixel = 0
    legend.Parent = espTab
    local lCorner = Instance.new("UICorner")
    lCorner.CornerRadius = UDim.new(0, 6); lCorner.Parent = legend

    local roles = {
        {"Guard",    ROLE_COLORS.Guard},
        {"Inmate",   ROLE_COLORS.Inmate},
        {"Criminal", ROLE_COLORS.Criminal},
        {"Neutral",  ROLE_COLORS.Neutral},
    }
    for i, data in ipairs(roles) do
        local dot = Instance.new("Frame")
        dot.Size = UDim2.fromOffset(10, 10)
        dot.Position = UDim2.fromOffset(16, 14 + (i - 1) * 24)
        dot.BackgroundColor3 = data[2]
        dot.BorderSizePixel = 0
        dot.Parent = legend
        local dCorner = Instance.new("UICorner")
        dCorner.CornerRadius = UDim.new(1, 0); dCorner.Parent = dot

        local lbl = Instance.new("TextLabel")
        lbl.Size = UDim2.new(1, -40, 0, 16)
        lbl.Position = UDim2.fromOffset(34, 12 + (i - 1) * 24)
        lbl.BackgroundTransparency = 1
        lbl.Text = data[1]
        lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
        lbl.TextSize = 12
        lbl.TextColor3 = THEME.Text
        lbl.TextXAlignment = Enum.TextXAlignment.Left
        lbl.Parent = legend
    end
end

-- ── Tab: Humanoid ────────────────────────────────────────────────
local humanoidTab = UI.CreateTab("Humanoid")

UI.CreateSection(humanoidTab, "Movement")
UI.CreateNumericInput(humanoidTab, "Walk Speed", "Humanoid/WalkSpeed",
    16, 0, 500, 1, nil,
    function(v)
        local char = LocalPlayer.Character
        local hum = char and char:FindFirstChildOfClass("Humanoid")
        if hum then pcall(function() hum.WalkSpeed = v end) end
    end,
    true)

UI.CreateNumericInput(humanoidTab, "Jump Power", "Humanoid/JumpPower",
    50, 0, 500, 1, nil,
    function(v)
        local char = LocalPlayer.Character
        local hum = char and char:FindFirstChildOfClass("Humanoid")
        if hum then pcall(function() hum.JumpPower = v end) end
    end,
    true)

UI.CreateNumericInput(humanoidTab, "Jump Height", "Humanoid/JumpHeight",
    7.2, 0, 500, 0.5, nil,
    function(v)
        local char = LocalPlayer.Character
        local hum = char and char:FindFirstChildOfClass("Humanoid")
        if hum then pcall(function() hum.JumpHeight = v end) end
    end,
    true)

UI.CreateNumericInput(humanoidTab, "Hip Height", "Humanoid/HipHeight",
    2, 0, 100, 0.5, nil,
    function(v)
        local char = LocalPlayer.Character
        local hum = char and char:FindFirstChildOfClass("Humanoid")
        if hum then pcall(function() hum.HipHeight = v end) end
    end,
    true)

UI.CreateSection(humanoidTab, "Health")
UI.CreateNumericInput(humanoidTab, "Max Health", "Humanoid/MaxHealth",
    100, 0, 2000, 1, nil,
    function(v)
        local char = LocalPlayer.Character
        local hum = char and char:FindFirstChildOfClass("Humanoid")
        if hum then pcall(function() hum.MaxHealth = v end) end
    end,
    true)

-- ── Tab: Noclip ──────────────────────────────────────────────────
local noclipTab = UI.CreateTab("Noclip")

UI.CreateSection(noclipTab, "Noclip")
UI.CreateToggle(noclipTab, "Enable Noclip", "Noclip/Enabled", Flags["Noclip/Enabled"], function(state)
    if state then
        Notify("Noclip", "Noclip enabled — scanning walls...")
        task.spawn(function()
            ScanAndBreak()
            Notify("Noclip", "Done. Broken: " .. tostring(Noclip.BrokenCount))
        end)
    else
        Notify("Noclip", "Noclip disabled — restoring walls.")
        RestoreAll()
    end
end)

UI.CreateNumericInput(noclipTab, "Radius", "Noclip/Radius", Flags["Noclip/Radius"], 50, 2000, 25, "m")
UI.CreateToggle(noclipTab, "Include Anchored (Walls)", "Noclip/IncludeAnchored", Flags["Noclip/IncludeAnchored"])
UI.CreateToggle(noclipTab, "Include Unanchored (Objects)", "Noclip/IncludeUnanchored", Flags["Noclip/IncludeUnanchored"])

UI.CreateSection(noclipTab, "Actions")
UI.CreateButton(noclipTab, "Force Rescan Now", function()
    if Flags["Noclip/Enabled"] then
        task.spawn(function()
            ScanAndBreak()
            Notify("Noclip", "Rescan done. Broken: " .. tostring(Noclip.BrokenCount))
        end)
    else
        Notify("Noclip", "Enable Noclip first.")
    end
end)
UI.CreateButton(noclipTab, "Restore All", function()
    RestoreAll()
    Notify("Noclip", "All walls restored.")
end)

-- ══════════════════════════════════════════════════════════════════
--  TAB: TELEPORT
-- ══════════════════════════════════════════════════════════════════
local teleportTab = UI.CreateTab("Teleport")

-- ── Preset buttons ──
UI.CreateSection(teleportTab, "Preset Locations")

local presetList = Instance.new("Frame")
presetList.Name = "PresetList"
presetList.Size = UDim2.new(1, 0, 0, 0)
presetList.AutomaticSize = Enum.AutomaticSize.Y
presetList.BackgroundTransparency = 1
presetList.Parent = teleportTab

local plLayout = Instance.new("UIListLayout")
plLayout.Padding = UDim.new(0, 4)
plLayout.SortOrder = Enum.SortOrder.LayoutOrder
plLayout.Parent = presetList

for i, loc in ipairs(TELEPORT_PRESETS) do
    local btn = Instance.new("TextButton")
    btn.Name = "Preset_" .. i
    btn.Size = UDim2.new(1, 0, 0, 32)
    btn.BackgroundColor3 = THEME.Element
    btn.Text = string.format("%s   (%.0f, %.0f, %.0f)", loc.name, loc.pos.X, loc.pos.Y, loc.pos.Z)
    btn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
    btn.TextSize = 11
    btn.TextColor3 = THEME.Text
    btn.BorderSizePixel = 0
    btn.LayoutOrder = i
    btn.Parent = presetList
    local bCorner = Instance.new("UICorner")
    bCorner.CornerRadius = UDim.new(0, 6); bCorner.Parent = btn

    local bStroke = Instance.new("UIStroke")
    bStroke.Color = THEME.Accent; bStroke.Thickness = 1
    bStroke.Transparency = 0.7
    bStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
    bStroke.Parent = btn

    btn.MouseButton1Click:Connect(function()
        TeleportToPosition(loc.pos)
    end)
end

-- ── Save current location ──
UI.CreateSection(teleportTab, "Save Current Location")

local saveFrame = Instance.new("Frame")
saveFrame.Size = UDim2.new(1, 0, 0, 42)
saveFrame.BackgroundColor3 = THEME.Element
saveFrame.BorderSizePixel = 0
saveFrame.Parent = teleportTab
local sfCorner = Instance.new("UICorner")
sfCorner.CornerRadius = UDim.new(0, 6); sfCorner.Parent = saveFrame

local saveInput = Instance.new("TextBox")
saveInput.Name = "SaveName"
saveInput.Size = UDim2.new(1, -110, 0, 30)
saveInput.Position = UDim2.new(0, 8, 0, 6)
saveInput.BackgroundColor3 = Color3.fromRGB(48, 48, 55)
saveInput.BorderSizePixel = 0
saveInput.PlaceholderText = "Location name..."
saveInput.Text = ""
saveInput.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
saveInput.TextSize = 12
saveInput.TextColor3 = THEME.Text
saveInput.ClearTextOnFocus = false
saveInput.Parent = saveFrame
local siCorner = Instance.new("UICorner")
siCorner.CornerRadius = UDim.new(0, 6); siCorner.Parent = saveInput

local saveBtn = Instance.new("TextButton")
saveBtn.Name = "SaveBtn"
saveBtn.Size = UDim2.new(0, 95, 0, 30)
saveBtn.Position = UDim2.new(1, -103, 0, 6)
saveBtn.BackgroundColor3 = THEME.Success
saveBtn.Text = "💾 Save"
saveBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
saveBtn.TextSize = 12
saveBtn.TextColor3 = Color3.fromRGB(20, 20, 22)
saveBtn.BorderSizePixel = 0
saveBtn.Parent = saveFrame
local sbCorner = Instance.new("UICorner")
sbCorner.CornerRadius = UDim.new(0, 6); sbCorner.Parent = saveBtn

-- Saved locations list
UI.CreateSection(teleportTab, "Saved Locations")

local savedListFrame = Instance.new("Frame")
savedListFrame.Name = "SavedList"
savedListFrame.Size = UDim2.new(1, 0, 0, 0)
savedListFrame.AutomaticSize = Enum.AutomaticSize.Y
savedListFrame.BackgroundTransparency = 1
savedListFrame.Parent = teleportTab

local slLayout = Instance.new("UIListLayout")
slLayout.Padding = UDim.new(0, 4)
slLayout.SortOrder = Enum.SortOrder.LayoutOrder
slLayout.Parent = savedListFrame

local function RefreshSavedList()
    for _, ch in ipairs(savedListFrame:GetChildren()) do
        if not ch:IsA("UIListLayout") then ch:Destroy() end
    end

    local count = 0
    local sortedNames = {}
    for name, _ in pairs(SavedLocations) do
        table.insert(sortedNames, name)
    end
    table.sort(sortedNames)

    for i, name in ipairs(sortedNames) do
        local posTable = SavedLocations[name]
        count = count + 1

        local row = Instance.new("Frame")
        row.Size = UDim2.new(1, 0, 0, 36)
        row.BackgroundColor3 = THEME.Element
        row.BorderSizePixel = 0
        row.LayoutOrder = i
        row.Parent = savedListFrame
        local rCorner = Instance.new("UICorner")
        rCorner.CornerRadius = UDim.new(0, 6); rCorner.Parent = row

        local nameLbl = Instance.new("TextLabel")
        nameLbl.Size = UDim2.new(1, -130, 1, 0)
        nameLbl.Position = UDim2.new(0, 10, 0, 0)
        nameLbl.BackgroundTransparency = 1
        nameLbl.Text = name
        nameLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
        nameLbl.TextSize = 12
        nameLbl.TextColor3 = THEME.Text
        nameLbl.TextXAlignment = Enum.TextXAlignment.Left
        nameLbl.Parent = row

        local goBtn = Instance.new("TextButton")
        goBtn.Size = UDim2.new(0, 65, 0, 26)
        goBtn.Position = UDim2.new(1, -100, 0.5, -13)
        goBtn.BackgroundColor3 = THEME.Accent
        goBtn.Text = "GO"
        goBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
        goBtn.TextSize = 11
        goBtn.TextColor3 = Color3.fromRGB(20, 20, 22)
        goBtn.BorderSizePixel = 0
        goBtn.Parent = row
        local gCorner = Instance.new("UICorner")
        gCorner.CornerRadius = UDim.new(0, 6); gCorner.Parent = goBtn

        goBtn.MouseButton1Click:Connect(function()
            local v3 = Vector3.new(posTable.X, posTable.Y, posTable.Z)
            TeleportToPosition(v3)
        end)

        local delBtn = Instance.new("TextButton")
        delBtn.Size = UDim2.new(0, 26, 0, 26)
        delBtn.Position = UDim2.new(1, -30, 0.5, -13)
        delBtn.BackgroundColor3 = Color3.fromRGB(70, 30, 30)
        delBtn.Text = "✕"
        delBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
        delBtn.TextSize = 13
        delBtn.TextColor3 = Color3.fromRGB(230, 100, 100)
        delBtn.BorderSizePixel = 0
        delBtn.Parent = row
        local dCorner = Instance.new("UICorner")
        dCorner.CornerRadius = UDim.new(0, 6); dCorner.Parent = delBtn

        delBtn.MouseButton1Click:Connect(function()
            SavedLocations[name] = nil
            SaveLocationsToDisk()
            RefreshSavedList()
            Notify("Teleport", "Deleted '" .. name .. "'")
        end)
    end

    if count == 0 then
        local emptyLbl = Instance.new("TextLabel")
        emptyLbl.Size = UDim2.new(1, 0, 0, 30)
        emptyLbl.BackgroundTransparency = 1
        emptyLbl.Text = "No saved locations yet."
        emptyLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular, Enum.FontStyle.Italic)
        emptyLbl.TextSize = 11
        emptyLbl.TextColor3 = THEME.TextDark
        emptyLbl.LayoutOrder = 1
        emptyLbl.Parent = savedListFrame
    end
end

saveBtn.MouseButton1Click:Connect(function()
    local name = saveInput.Text
    name = tostring(name or ""):gsub("^%s+", ""):gsub("%s+$", "")
    if name == "" then
        Notify("Teleport", "Please enter a name.")
        return
    end

    local pos = GetCurrentPosition()
    if not pos then
        Notify("Teleport", "Could not read current position.")
        return
    end

    SavedLocations[name] = { X = pos.X, Y = pos.Y, Z = pos.Z }
    SaveLocationsToDisk()
    saveInput.Text = ""
    RefreshSavedList()
    Notify("Teleport", string.format("Saved '%s'", name))
end)

RefreshSavedList()

-- ══════════════════════════════════════════════════════════════════
--  TAB: GOTO PLAYERS
-- ══════════════════════════════════════════════════════════════════
local gotoPlayersTab = UI.CreateTab("Go-To Players")

-- Filter row
UI.CreateSection(gotoPlayersTab, "Filter Roles")

local filterFrame = Instance.new("Frame")
filterFrame.Size = UDim2.new(1, 0, 0, 36)
filterFrame.BackgroundColor3 = THEME.Element
filterFrame.BorderSizePixel = 0
filterFrame.Parent = gotoPlayersTab
local ffCorner = Instance.new("UICorner")
ffCorner.CornerRadius = UDim.new(0, 6); ffCorner.Parent = filterFrame

local filterLayout = Instance.new("UIListLayout")
filterLayout.FillDirection = Enum.FillDirection.Horizontal
filterLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
filterLayout.VerticalAlignment = Enum.VerticalAlignment.Center
filterLayout.Padding = UDim.new(0, 3)
filterLayout.Parent = filterFrame

local RoleFilter = {
    Guard = true,
    Inmate = true,
    Criminal = true,
    Neutral = true,
    Unknown = true,
}

local RefreshGotoListRef = nil

for _, roleName in ipairs({"Guard", "Inmate", "Criminal", "Neutral", "Unknown"}) do
    local fBtn = Instance.new("TextButton")
    fBtn.Size = UDim2.fromOffset(70, 26)
    fBtn.BackgroundColor3 = ROLE_COLORS[roleName]
    fBtn.BackgroundTransparency = RoleFilter[roleName] and 0 or 0.7
    fBtn.Text = roleName
    fBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    fBtn.TextSize = 10
    fBtn.TextColor3 = Color3.fromRGB(20, 20, 22)
    fBtn.BorderSizePixel = 0
    fBtn.Parent = filterFrame
    local fCorner = Instance.new("UICorner")
    fCorner.CornerRadius = UDim.new(0, 4); fCorner.Parent = fBtn

    fBtn.MouseButton1Click:Connect(function()
        RoleFilter[roleName] = not RoleFilter[roleName]
        fBtn.BackgroundTransparency = RoleFilter[roleName] and 0 or 0.7
        if RefreshGotoListRef then pcall(RefreshGotoListRef) end
    end)
end

-- Player list
UI.CreateSection(gotoPlayersTab, "Players")

local playersListFrame = Instance.new("Frame")
playersListFrame.Name = "PlayersList"
playersListFrame.Size = UDim2.new(1, 0, 0, 0)
playersListFrame.AutomaticSize = Enum.AutomaticSize.Y
playersListFrame.BackgroundTransparency = 1
playersListFrame.Parent = gotoPlayersTab

local gplLayout = Instance.new("UIListLayout")
gplLayout.Padding = UDim.new(0, 4)
gplLayout.SortOrder = Enum.SortOrder.LayoutOrder
gplLayout.Parent = playersListFrame

local function RefreshGotoPlayers()
    for _, ch in ipairs(playersListFrame:GetChildren()) do
        if not ch:IsA("UIListLayout") then ch:Destroy() end
    end

    local myRoot = GetMyRoot()
    local myPos = myRoot and myRoot.Position or Vector3.zero

    local groups = {
        Guard = {},
        Inmate = {},
        Criminal = {},
        Neutral = {},
        Unknown = {},
    }

    for _, p in ipairs(GetPlayersCache()) do
        if p ~= LocalPlayer and p.Parent then
            local char, root = GetCharacter(p)
            if char and root then
                local role = DetectRole(p)
                if not groups[role] then role = "Unknown" end
                local dist = (root.Position - myPos).Magnitude
                table.insert(groups[role], {player = p, dist = dist})
            end
        end
    end

    local order = 1

    for _, roleName in ipairs({"Guard", "Inmate", "Criminal", "Neutral", "Unknown"}) do
        if RoleFilter[roleName] then
            local list = groups[roleName]
            table.sort(list, function(a, b) return a.dist < b.dist end)

            if #list > 0 then
                -- Header
                local header = Instance.new("Frame")
                header.Size = UDim2.new(1, 0, 0, 22)
                header.BackgroundTransparency = 1
                header.LayoutOrder = order
                header.Parent = playersListFrame
                order = order + 1

                local hLbl = Instance.new("TextLabel")
                hLbl.Size = UDim2.new(1, 0, 1, 0)
                hLbl.Position = UDim2.new(0, 4, 0, 0)
                hLbl.BackgroundTransparency = 1
                hLbl.Text = string.format("%s  (%d)", string.upper(roleName), #list)
                hLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.ExtraBold, Enum.FontStyle.Normal)
                hLbl.TextSize = 11
                hLbl.TextColor3 = ROLE_COLORS[roleName]
                hLbl.TextXAlignment = Enum.TextXAlignment.Left
                hLbl.Parent = header

                for _, entry in ipairs(list) do
                    local p = entry.player
                    local dist = math.floor(entry.dist)

                    local row = Instance.new("Frame")
                    row.Size = UDim2.new(1, 0, 0, 40)
                    row.BackgroundColor3 = THEME.Element
                    row.BorderSizePixel = 0
                    row.LayoutOrder = order
                    row.Parent = playersListFrame
                    order = order + 1

                    local rCorner = Instance.new("UICorner")
                    rCorner.CornerRadius = UDim.new(0, 6); rCorner.Parent = row

                    -- Left color bar
                    local bar = Instance.new("Frame")
                    bar.Size = UDim2.new(0, 3, 1, -8)
                    bar.Position = UDim2.new(0, 4, 0, 4)
                    bar.BackgroundColor3 = ROLE_COLORS[roleName]
                    bar.BorderSizePixel = 0
                    bar.Parent = row
                    local barCorner = Instance.new("UICorner")
                    barCorner.CornerRadius = UDim.new(1, 0); barCorner.Parent = bar

                    local nameLbl = Instance.new("TextLabel")
                    nameLbl.Size = UDim2.new(1, -110, 0, 18)
                    nameLbl.Position = UDim2.new(0, 14, 0, 3)
                    nameLbl.BackgroundTransparency = 1
                    nameLbl.Text = p.DisplayName or p.Name
                    nameLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
                    nameLbl.TextSize = 12
                    nameLbl.TextColor3 = ROLE_COLORS[roleName]
                    nameLbl.TextXAlignment = Enum.TextXAlignment.Left
                    nameLbl.Parent = row

                    local subLbl = Instance.new("TextLabel")
                    subLbl.Size = UDim2.new(1, -110, 0, 14)
                    subLbl.Position = UDim2.new(0, 14, 0, 22)
                    subLbl.BackgroundTransparency = 1
                    subLbl.Text = string.format("@%s  |  %d studs", p.Name, dist)
                    subLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular, Enum.FontStyle.Normal)
                    subLbl.TextSize = 10
                    subLbl.TextColor3 = THEME.TextDark
                    subLbl.TextXAlignment = Enum.TextXAlignment.Left
                    subLbl.Parent = row

                    local goBtn = Instance.new("TextButton")
                    goBtn.Size = UDim2.new(0, 84, 0, 28)
                    goBtn.Position = UDim2.new(1, -92, 0.5, -14)
                    goBtn.BackgroundColor3 = ROLE_COLORS[roleName]
                    goBtn.Text = "GOTO"
                    goBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.ExtraBold, Enum.FontStyle.Normal)
                    goBtn.TextSize = 11
                    goBtn.TextColor3 = Color3.fromRGB(20, 20, 22)
                    goBtn.BorderSizePixel = 0
                    goBtn.Parent = row
                    local gCorner = Instance.new("UICorner")
                    gCorner.CornerRadius = UDim.new(0, 6); gCorner.Parent = goBtn

                    goBtn.MouseButton1Click:Connect(function()
                        local char, root = GetCharacter(p)
                        if char and root then
                            local behindPos = root.Position + Vector3.new(0, 0, 3)
                            TeleportToPosition(behindPos)
                        else
                            Notify("Go-To", "Player has no character.")
                        end
                    end)
                end
            end
        end
    end

    -- Empty state
    local anyVisible = false
    for _, roleName in ipairs({"Guard", "Inmate", "Criminal", "Neutral", "Unknown"}) do
        if RoleFilter[roleName] and #groups[roleName] > 0 then
            anyVisible = true
            break
        end
    end

    if not anyVisible then
        local emptyLbl = Instance.new("TextLabel")
        emptyLbl.Size = UDim2.new(1, 0, 0, 30)
        emptyLbl.BackgroundTransparency = 1
        emptyLbl.Text = "No players match the current filter."
        emptyLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular, Enum.FontStyle.Italic)
        emptyLbl.TextSize = 11
        emptyLbl.TextColor3 = THEME.TextDark
        emptyLbl.LayoutOrder = 1
        emptyLbl.Parent = playersListFrame
    end
end

RefreshGotoListRef = RefreshGotoPlayers

-- Refresh when page becomes visible
task.spawn(function()
    while true do
        task.wait(1.5)
        if State.CurrentTab == "Go-To Players" then
            pcall(RefreshGotoPlayers)
        end
    end
end)

RefreshGotoPlayers()

-- ══════════════════════════════════════════════════════════════════
--  Tab: Auto-Give
-- ══════════════════════════════════════════════════════════════════
local autoTab = UI.CreateTab("Auto-Give")

UI.CreateSection(autoTab, "Auto-Give (MP5 + Remington)")
UI.CreateToggle(autoTab, "Enable Auto-Give", "AutoGive/Enabled", Flags["AutoGive/Enabled"], function(state)
    if state then
        Notify("Auto-Give", "Auto-Give enabled.")
        task.spawn(function() SpawnTouchGiversInFront() end)
    else
        Notify("Auto-Give", "Auto-Give disabled.")
    end
end)

UI.CreateButton(autoTab, "Visit TouchGivers Now", function()
    task.spawn(function()
        SpawnTouchGiversInFront(true)
        Notify("Auto-Give", "Visited TouchGivers.")
    end)
end)

-- ══════════════════════════════════════════════════════════════════
--  Tab: Arrest
-- ══════════════════════════════════════════════════════════════════
local arrestTab = UI.CreateTab("Arrest")

local arrestWrapper = Instance.new("Frame")
arrestWrapper.Name = "ArrestWrapper"
arrestWrapper.Size = UDim2.new(1, 0, 0, 0)
arrestWrapper.AutomaticSize = Enum.AutomaticSize.Y
arrestWrapper.BackgroundTransparency = 1
arrestWrapper.Parent = arrestTab
arrestWrapper.LayoutOrder = 1

local wrapperLayout = Instance.new("UIListLayout")
wrapperLayout.Padding = UDim.new(0, 5)
wrapperLayout.SortOrder = Enum.SortOrder.LayoutOrder
wrapperLayout.Parent = arrestWrapper

local function MakeArrestSectionTitle(text, order)
    local container = Instance.new("Frame")
    container.Size = UDim2.new(1, 0, 0, 24)
    container.BackgroundTransparency = 1
    container.LayoutOrder = order
    container.Parent = arrestWrapper

    local lbl = Instance.new("TextLabel")
    lbl.Size = UDim2.new(1, 0, 1, 0)
    lbl.Position = UDim2.new(0, 2, 0, 4)
    lbl.BackgroundTransparency = 1
    lbl.Text = string.upper(text)
    lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    lbl.TextSize = 10
    lbl.TextColor3 = THEME.Accent
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.Parent = container
end

local function MakeArrestNumericRow(labelText, flag, default, minV, maxV, step, unit, order)
    local frame = Instance.new("Frame")
    frame.Size = UDim2.new(1, 0, 0, 50)
    frame.BackgroundColor3 = THEME.Element
    frame.BorderSizePixel = 0
    frame.LayoutOrder = order
    frame.Parent = arrestWrapper
    local c = Instance.new("UICorner")
    c.CornerRadius = UDim.new(0, 6); c.Parent = frame

    local lbl = Instance.new("TextLabel")
    lbl.Size = UDim2.new(0.55, -12, 1, 0)
    lbl.Position = UDim2.new(0, 12, 0, 0)
    lbl.BackgroundTransparency = 1
    lbl.Text = labelText
    lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
    lbl.TextSize = 12
    lbl.TextColor3 = THEME.Text
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.Parent = frame

    if Flags[flag] == nil then Flags[flag] = default end

    local inputFrame = Instance.new("Frame")
    inputFrame.Size = UDim2.new(0.45, -12, 0, 30)
    inputFrame.Position = UDim2.new(1, -12, 0.5, 0)
    inputFrame.AnchorPoint = Vector2.new(1, 0.5)
    inputFrame.BackgroundColor3 = Color3.fromRGB(48, 48, 55)
    inputFrame.BorderSizePixel = 0
    inputFrame.Parent = frame
    local ifc = Instance.new("UICorner")
    ifc.CornerRadius = UDim.new(0, 6); ifc.Parent = inputFrame

    local box = Instance.new("TextBox")
    box.Size = UDim2.new(1, -50, 1, 0)
    box.Position = UDim2.new(0, 25, 0, 0)
    box.BackgroundTransparency = 1
    box.Text = tostring(Flags[flag]) .. (unit or "")
    box.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    box.TextSize = 12
    box.TextColor3 = THEME.Accent
    box.ClearTextOnFocus = false
    box.Parent = inputFrame

    local function commit(val)
        val = math.clamp(tonumber(val) or Flags[flag], minV, maxV)
        if step and step > 0 then
            val = math.floor(val / step + 0.5) * step
        end
        Flags[flag] = val
        box.Text = tostring(val) .. (unit or "")
    end

    box.FocusLost:Connect(function()
        commit((box.Text:gsub("[^%d%.%-]", "")))
    end)

    local minus = Instance.new("TextButton")
    minus.Size = UDim2.new(0, 25, 1, 0)
    minus.BackgroundTransparency = 1
    minus.Text = "−"
    minus.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    minus.TextSize = 16
    minus.TextColor3 = THEME.TextDark
    minus.Parent = inputFrame
    minus.MouseButton1Click:Connect(function() commit(Flags[flag] - (step or 1)) end)

    local plus = Instance.new("TextButton")
    plus.Size = UDim2.new(0, 25, 1, 0)
    plus.Position = UDim2.new(1, -25, 0, 0)
    plus.BackgroundTransparency = 1
    plus.Text = "+"
    plus.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
    plus.TextSize = 16
    plus.TextColor3 = THEME.TextDark
    plus.Parent = inputFrame
    plus.MouseButton1Click:Connect(function() commit(Flags[flag] + (step or 1)) end)
end

-- Status banner
local arrestStatusFrame = Instance.new("Frame")
arrestStatusFrame.Name = "StatusFrame"
arrestStatusFrame.Size = UDim2.new(1, 0, 0, 44)
arrestStatusFrame.BackgroundColor3 = THEME.Element
arrestStatusFrame.BorderSizePixel = 0
arrestStatusFrame.LayoutOrder = 1
arrestStatusFrame.Parent = arrestWrapper
local asCorner = Instance.new("UICorner")
asCorner.CornerRadius = UDim.new(0, 6); asCorner.Parent = arrestStatusFrame

local arrestStatusLbl = Instance.new("TextLabel")
arrestStatusLbl.Name = "StatusLabel"
arrestStatusLbl.Size = UDim2.new(1, -16, 1, 0)
arrestStatusLbl.Position = UDim2.new(0, 8, 0, 0)
arrestStatusLbl.BackgroundTransparency = 1
arrestStatusLbl.Text = "Checking role..."
arrestStatusLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
arrestStatusLbl.TextSize = 12
arrestStatusLbl.TextColor3 = THEME.Text
arrestStatusLbl.TextXAlignment = Enum.TextXAlignment.Left
arrestStatusLbl.TextWrapped = true
arrestStatusLbl.Parent = arrestStatusFrame

MakeArrestSectionTitle("Settings", 2)

local enableRow = Instance.new("Frame")
enableRow.Size = UDim2.new(1, 0, 0, 38)
enableRow.BackgroundColor3 = THEME.Element
enableRow.BorderSizePixel = 0
enableRow.LayoutOrder = 3
enableRow.Parent = arrestWrapper
local erCorner = Instance.new("UICorner")
erCorner.CornerRadius = UDim.new(0, 6); erCorner.Parent = enableRow

local enableLabel = Instance.new("TextLabel")
enableLabel.Size = UDim2.new(1, -70, 1, 0)
enableLabel.Position = UDim2.new(0, 12, 0, 0)
enableLabel.BackgroundTransparency = 1
enableLabel.Text = "Enable Arrest"
enableLabel.FontFace = Font.fromName("Montserrat", Enum.FontWeight.SemiBold, Enum.FontStyle.Normal)
enableLabel.TextSize = 12
enableLabel.TextColor3 = THEME.Text
enableLabel.TextXAlignment = Enum.TextXAlignment.Left
enableLabel.Parent = enableRow

local enableSwitch = Instance.new("Frame")
enableSwitch.Size = UDim2.fromOffset(44, 22)
enableSwitch.AnchorPoint = Vector2.new(1, 0.5)
enableSwitch.Position = UDim2.new(1, -12, 0.5, 0)
enableSwitch.BackgroundColor3 = Flags["Arrest/Enabled"] and THEME.Accent or Color3.fromRGB(55, 55, 62)
enableSwitch.BorderSizePixel = 0
enableSwitch.Parent = enableRow
local esCorner = Instance.new("UICorner")
esCorner.CornerRadius = UDim.new(1, 0); esCorner.Parent = enableSwitch

local enableKnob = Instance.new("Frame")
enableKnob.Size = UDim2.fromOffset(18, 18)
enableKnob.AnchorPoint = Vector2.new(0, 0.5)
enableKnob.Position = Flags["Arrest/Enabled"] and UDim2.new(1, -20, 0.5, 0) or UDim2.new(0, 2, 0.5, 0)
enableKnob.BackgroundColor3 = Color3.fromRGB(240, 240, 245)
enableKnob.BorderSizePixel = 0
enableKnob.Parent = enableSwitch
local ekCorner = Instance.new("UICorner")
ekCorner.CornerRadius = UDim.new(1, 0); ekCorner.Parent = enableKnob

local enableClick = Instance.new("TextButton")
enableClick.Size = UDim2.new(1, 0, 1, 0)
enableClick.BackgroundTransparency = 1
enableClick.Text = ""
enableClick.Parent = enableSwitch

local function UpdateEnableVisuals(state)
    TweenService:Create(enableSwitch, TweenInfo.new(0.15), {
        BackgroundColor3 = state and THEME.Accent or Color3.fromRGB(55, 55, 62),
    }):Play()
    TweenService:Create(enableKnob, TweenInfo.new(0.15), {
        Position = state and UDim2.new(1, -20, 0.5, 0) or UDim2.new(0, 2, 0.5, 0),
    }):Play()
end

enableClick.MouseButton1Click:Connect(function()
    Flags["Arrest/Enabled"] = not Flags["Arrest/Enabled"]
    UpdateEnableVisuals(Flags["Arrest/Enabled"])
end)

MakeArrestNumericRow("Stick Distance (Behind)", "Arrest/StickDistance", 2.2, 0.5, 20.0, 0.5, "studs", 4)
MakeArrestNumericRow("Stick Height", "Arrest/StickHeight", 0.0, -5.0, 10.0, 0.5, "studs", 5)
MakeArrestNumericRow("Stick Duration", "Arrest/StickDuration", 5.0, 0.5, 60.0, 0.5, "s", 6)

MakeArrestSectionTitle("Criminals", 7)

local arrestListFrame = Instance.new("Frame")
arrestListFrame.Name = "ArrestListFrame"
arrestListFrame.Size = UDim2.new(1, 0, 0, 0)
arrestListFrame.AutomaticSize = Enum.AutomaticSize.Y
arrestListFrame.BackgroundTransparency = 1
arrestListFrame.LayoutOrder = 8
arrestListFrame.Parent = arrestWrapper
local alLayout = Instance.new("UIListLayout")
alLayout.Padding = UDim.new(0, 4)
alLayout.SortOrder = Enum.SortOrder.LayoutOrder
alLayout.Parent = arrestListFrame

local function ClearArrestList()
    for _, ch in ipairs(arrestListFrame:GetChildren()) do
        if not ch:IsA("UIListLayout") then ch:Destroy() end
    end
end

local function RefreshArrestList()
    ClearArrestList()

    local isGuard = IsLocalGuard()
    if not isGuard then
        arrestStatusLbl.Text = "❌ You are not a Guard. Arrest is disabled."
        arrestStatusLbl.TextColor3 = THEME.Fail

        local lbl = Instance.new("TextLabel")
        lbl.Size = UDim2.new(1, 0, 0, 30)
        lbl.BackgroundTransparency = 1
        lbl.Text = "Join the Guards team to use this tab."
        lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular, Enum.FontStyle.Italic)
        lbl.TextSize = 12
        lbl.TextColor3 = THEME.TextDark
        lbl.LayoutOrder = 1
        lbl.Parent = arrestListFrame
        return
    end

    local hasCuffs = false
    local backpack = LocalPlayer:FindFirstChild("Backpack")
    local char = LocalPlayer.Character
    local toolName = Flags["Arrest/ToolName"] or "Handcuffs"
    if backpack and backpack:FindFirstChild(toolName) then hasCuffs = true end
    if char and char:FindFirstChild(toolName) then hasCuffs = true end

    local criminals = GetCriminalsList()

    if hasCuffs then
        arrestStatusLbl.Text = string.format("✓ Guard | Handcuffs ready | %d criminal(s)", #criminals)
        arrestStatusLbl.TextColor3 = THEME.Success
    else
        arrestStatusLbl.Text = string.format("⚠ Guard | No Handcuffs in backpack | %d criminal(s)", #criminals)
        arrestStatusLbl.TextColor3 = Color3.fromRGB(230, 190, 60)
    end

    if #criminals == 0 then
        local lbl = Instance.new("TextLabel")
        lbl.Size = UDim2.new(1, 0, 0, 30)
        lbl.BackgroundTransparency = 1
        lbl.Text = "No criminals detected."
        lbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular, Enum.FontStyle.Italic)
        lbl.TextSize = 12
        lbl.TextColor3 = THEME.TextDark
        lbl.LayoutOrder = 1
        lbl.Parent = arrestListFrame
        return
    end

    for i, data in ipairs(criminals) do
        local p = data.player
        local dist = math.floor(data.dist)

        local row = Instance.new("Frame")
        row.Size = UDim2.new(1, 0, 0, 44)
        row.BackgroundColor3 = THEME.Element
        row.BorderSizePixel = 0
        row.LayoutOrder = i
        row.Parent = arrestListFrame
        local rCorner = Instance.new("UICorner")
        rCorner.CornerRadius = UDim.new(0, 6); rCorner.Parent = row

        local nameLbl = Instance.new("TextLabel")
        nameLbl.Size = UDim2.new(1, -110, 0, 20)
        nameLbl.Position = UDim2.new(0, 10, 0, 4)
        nameLbl.BackgroundTransparency = 1
        nameLbl.Text = p.DisplayName or p.Name
        nameLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
        nameLbl.TextSize = 13
        nameLbl.TextColor3 = ROLE_COLORS.Criminal
        nameLbl.TextXAlignment = Enum.TextXAlignment.Left
        nameLbl.Parent = row

        local subLbl = Instance.new("TextLabel")
        subLbl.Size = UDim2.new(1, -110, 0, 16)
        subLbl.Position = UDim2.new(0, 10, 0, 23)
        subLbl.BackgroundTransparency = 1
        subLbl.Text = string.format("@%s  |  %d studs", p.Name, dist)
        subLbl.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Regular, Enum.FontStyle.Normal)
        subLbl.TextSize = 11
        subLbl.TextColor3 = THEME.TextDark
        subLbl.TextXAlignment = Enum.TextXAlignment.Left
        subLbl.Parent = row

        local btn = Instance.new("TextButton")
        btn.Size = UDim2.new(0, 90, 0, 30)
        btn.Position = UDim2.new(1, -100, 0.5, -15)
        btn.BackgroundColor3 = Color3.fromRGB(180, 60, 60)
        btn.Text = "STICK"
        btn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.ExtraBold, Enum.FontStyle.Normal)
        btn.TextSize = 12
        btn.TextColor3 = Color3.fromRGB(255, 255, 255)
        btn.BorderSizePixel = 0
        btn.Parent = row
        local bCorner = Instance.new("UICorner")
        bCorner.CornerRadius = UDim.new(0, 6); bCorner.Parent = btn

        btn.MouseButton1Click:Connect(function()
            if not Flags["Arrest/Enabled"] then
                Notify("Arrest", "Arrest is disabled. Enable it in the Arrest tab.")
                return
            end
            if not IsLocalGuard() then
                Notify("Arrest", "You must be a Guard to arrest.")
                return
            end
            if ArrestState.Running then
                StopArrestStick()
            end
            task.spawn(function() ArrestPlayer(p) end)
        end)
    end
end

local stopArrestBtn = Instance.new("TextButton")
stopArrestBtn.Size = UDim2.new(1, 0, 0, 34)
stopArrestBtn.BackgroundColor3 = Color3.fromRGB(70, 30, 30)
stopArrestBtn.Text = "■  Stop Sticking"
stopArrestBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
stopArrestBtn.TextSize = 12
stopArrestBtn.TextColor3 = Color3.fromRGB(230, 100, 100)
stopArrestBtn.BorderSizePixel = 0
stopArrestBtn.LayoutOrder = 9
stopArrestBtn.Parent = arrestWrapper
local sabCorner = Instance.new("UICorner")
sabCorner.CornerRadius = UDim.new(0, 6); sabCorner.Parent = stopArrestBtn

stopArrestBtn.MouseButton1Click:Connect(function()
    StopArrestStick()
end)

local refreshArrestBtn = Instance.new("TextButton")
refreshArrestBtn.Size = UDim2.new(1, 0, 0, 34)
refreshArrestBtn.BackgroundColor3 = THEME.Accent
refreshArrestBtn.BackgroundTransparency = 0.15
refreshArrestBtn.Text = "↻  Refresh Criminal List"
refreshArrestBtn.FontFace = Font.fromName("Montserrat", Enum.FontWeight.Bold, Enum.FontStyle.Normal)
refreshArrestBtn.TextSize = 12
refreshArrestBtn.TextColor3 = Color3.fromRGB(20, 20, 22)
refreshArrestBtn.BorderSizePixel = 0
refreshArrestBtn.LayoutOrder = 10
refreshArrestBtn.Parent = arrestWrapper
local rabCorner = Instance.new("UICorner")
rabCorner.CornerRadius = UDim.new(0, 6); rabCorner.Parent = refreshArrestBtn
local rabStroke = Instance.new("UIStroke")
rabStroke.Color = THEME.Accent; rabStroke.Thickness = 1
rabStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
rabStroke.Parent = refreshArrestBtn

refreshArrestBtn.MouseButton1Click:Connect(function()
    RefreshArrestList()
end)

task.spawn(function()
    while true do
        task.wait(1.5)
        if State.CurrentTab == "Arrest" then
            pcall(RefreshArrestList)
        end
    end
end)

-- ══════════════════════════════════════════════════════════════════
--  PLAYER LIFECYCLE
-- ══════════════════════════════════════════════════════════════════
Players.PlayerAdded:Connect(function(player)
    UpdatePlayerCache()
end)

Players.PlayerRemoving:Connect(function(player)
    UpdatePlayerCache()
    RemoveNametag(player)
    CharCache[player] = nil
    if AimState.LastAimTarget == player then
        ClearAimLockState(false)
    end
end)

UpdatePlayerCache()

local function OnLocalCharacterAdded(character)
    task.wait(0.5)
    CaptureHumanoidSettings()

    task.wait(0.3)
    ApplySavedHumanoidSettings()

    if Flags["AutoGive/Enabled"] then
        task.spawn(function() SpawnTouchGiversInFront() end)
    end

    task.wait(0.5)
    pcall(RefreshArrestList)
end

LocalPlayer.CharacterAdded:Connect(OnLocalCharacterAdded)

if LocalPlayer.Character then
    task.spawn(function()
        task.wait(1)
        CaptureHumanoidSettings()
        task.wait(0.3)
        ApplySavedHumanoidSettings()
        if Flags["AutoGive/Enabled"] then
            SpawnTouchGiversInFront()
        end
        task.wait(0.5)
        pcall(RefreshArrestList)
    end)
end

-- ══════════════════════════════════════════════════════════════════
--  MAIN LOOPS
-- ══════════════════════════════════════════════════════════════════
local lastESPUpdate = 0
local ESP_UPDATE_INTERVAL = 0.1
local lastNoclipScan = 0
local NOCLIP_SCAN_INTERVAL = 0.75
local lastNoclipEnforce = 0
local NOCLIP_ENFORCE_INTERVAL = 0.2
local lastCharCachePrune = 0
local CHAR_CACHE_PRUNE_INTERVAL = 5.0
local lastHumanoidEnforce = 0
local HUMANOID_ENFORCE_INTERVAL = 0.1

local function PruneCharCache()
    local now = os.clock()
    if (now - lastCharCachePrune) < CHAR_CACHE_PRUNE_INTERVAL then return end
    lastCharCachePrune = now

    local playerMap = {}
    for i = 1, #cachedPlayersList do
        playerMap[cachedPlayersList[i]] = true
    end

    for player, cache in pairs(CharCache) do
        if not playerMap[player] or not player.Parent then
            if AimState.LastAimTarget == player then
                ClearAimLockState(false)
            end
            cache.Char = nil
            cache.Root = nil
            cache.Humanoid = nil
            cache.Head = nil
            CharCache[player] = nil
        elseif cache.Char and not cache.Char.Parent then
            if AimState.LastAimTarget == player then
                ClearAimLockState(false)
            end
            cache.Char = nil
            cache.Root = nil
            cache.Humanoid = nil
            cache.Head = nil
        end
    end
end

RunService.Heartbeat:Connect(function()
    local now = tick()

    if now - lastESPUpdate >= ESP_UPDATE_INTERVAL then
        lastESPUpdate = now
        if Flags["ESP/Enabled"] then
            RefreshESP()
        end
    end

    if Flags["Noclip/Enabled"] then
        if now - lastNoclipScan >= NOCLIP_SCAN_INTERVAL then
            lastNoclipScan = now
            ScanAndBreak()
        end
        if now - lastNoclipEnforce >= NOCLIP_ENFORCE_INTERVAL then
            lastNoclipEnforce = now
            EnforceNoclip()
        end
    end

    if now - lastHumanoidEnforce >= HUMANOID_ENFORCE_INTERVAL then
        lastHumanoidEnforce = now
        ApplyHumanoidSettings()
    end

    PruneCharCache()
end)

RunService.RenderStepped:Connect(UpdateAim)

-- ══════════════════════════════════════════════════════════════════
--  CLEANUP
-- ══════════════════════════════════════════════════════════════════
function PrisonVision_Unload()
    if ArrestState.Running then
        StopArrestStick()
    end
    RestoreAll()
    ClearAimLockState(true)

    for p, _ in pairs(ESP.Objects) do RemoveNametag(p) end
    if ESP.Folder then SafeDestroy(ESP.Folder) end
    if NotifyGui then SafeDestroy(NotifyGui) end
    if FovCircleGui then SafeDestroy(FovCircleGui) end
    if State.ScreenGui then SafeDestroy(State.ScreenGui) end

    table.clear(CharCache)
    table.clear(CandidateList)

    print("[PrisonVision] Unloaded.")
end

_G.PrisonVision_Unload = PrisonVision_Unload

-- ══════════════════════════════════════════════════════════════════
--  READY
-- ══════════════════════════════════════════════════════════════════
print(string.format("[PrisonVision v%s] Loaded successfully.", VERSION))
print("[PrisonVision] Press CapsLock to minimize/restore.")
print("[PrisonVision] Unload: _G.PrisonVision_Unload()")

Notify("PrisonVision", "Loaded! Aim, ESP, Noclip, Teleport, Go-To, Auto-Give, Humanoid, Arrest ready.")

Features & Information

AXIOM MENU v3.2.0 end boss of all prison life scripts. no key system, no logging and no bfuscation. A powerful all-in-one menu designed for Prison life, featuring a collection of advanced gameplay and utility features in one clean interface. FEATURES -- Role ESP -- * Display players with role-based colors * Guard, Inmate, Criminal, Neutral and Unknown detection * Username display * Health indicator * Distance display * Equipped item display * Highlight system with customizable transparency * Team check * Adjustable ESP render distance -- Noclip -- * Enable or disable Noclip * Adjustable scan radius * Support for anchored walls * Optional support for unanchored objects * Force Rescan option * Restore All option -- Aim / AimLock -- * AimLock system * Visibility Check * Team Check * Adjustable attraction strength * Customizable FOV radius * Optional FOV circle * Aim dampening * Adjustable dampening threshold and strength * Head and body-part targeting options * Configurable target groups -- Auto-Give -- * Automated weapon-giving system * Supports MP5 and Remington * Automatic TouchGiver handling * Manual "Visit TouchGivers Now" option -- Arrest -- * Automated arrest functionality * Handcuffs support * Adjustable stick distance * Adjustable stick height * Adjustable stick duration * Restore Position option -- Humanoid -- * Walk Speed control * Jump Power control * Jump Height control * Hip Height control * Max Health control * Individual value locking options -- Teleport -- * Teleport presets * Saved locations * Save and load custom locations * Persistent location storage * Quick teleport functionality -- GoTo Players -- * Player list with live updates * Players grouped by role * Distance-based organization * Role filters * Quick teleport to selected players -- Interface -- * Dedicated tabs for every major feature * Toggle and numeric controls * Built-in notifications * Minimize and restore using CapsLock * Clean and organized interface * Complete unload and cleanup system AXIOM MENU v3.2.0 ESP | Noclip | Aim | Auto-Give | Humanoid | Arrest | Teleport | Go-To Players
#prison life#noclip#aimbot#esp#teleport

Community Comments (1)

xvxvxvxcccvxxvxvxvxcccvxOct 9, 2026

horrible script has lame features and the ui is terrible

More Scripts for Prison Life