![[Open Source & Universal] Avatar Changer, Saver, Stealer](https://scriptblox.com/images/sb/0-1790383978808.png)
KeylessUniversal

-- Remote Keybinder + Placement (placement fixes: sphere, StraightForward level, CustomPlacement follows entity)
-- Place as LocalScript in StarterGui (runs in PlayerGui)
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local HttpService = game:GetService("HttpService")
local Camera = workspace.CurrentCamera
local player = Players.LocalPlayer
local backpack = player:WaitForChild("Backpack")
local guiParent = player:WaitForChild("PlayerGui")
local character = player.Character or player.CharacterAdded:Wait()
-- --------------------
-- Small helpers
-- --------------------
local function trim(s) return (tostring(s):gsub("^%s*(.-)%s*$","%1")) end
local function safeGetMouse() local ok,m = pcall(function() return player:GetMouse() end) if ok then return m end return nil end
local function getMouseHitPos()
local m = safeGetMouse()
if not m then return nil end
local ok, hit = pcall(function() return m.Hit end)
if not ok or not hit then return nil end
local p = hit.Position or (hit.p and hit.p)
if typeof(p) == "Vector3" then return p end
return nil
end
local function isRemote(inst) return inst and (inst:IsA("RemoteEvent") or inst:IsA("RemoteFunction")) end
local function keyEnumToName(k) if not k then return "<none>" end return tostring(k):gsub("Enum.KeyCode.","") end
local function nameToKeyEnum(name) if not name then return nil end local ok,e = pcall(function() return Enum.KeyCode[name] end) return ok and e or nil end
local function parseSingleToken(tok)
tok = trim(tok)
if tok == "" then return nil end
local quoted = tok:match('^"(.*)"$') or tok:match("^'(.*)'$")
if quoted then return tostring(quoted) end
if tok:lower() == "true" then return true end
if tok:lower() == "false" then return false end
local n = tonumber(tok)
if n then return n end
local a,b,c = tok:match("^Vector3%.new%(%s*([-+]?%d*%.?%d+)%s*,%s*([-+]?%d*%.?%d+)%s*,%s*([-+]?%d*%.?%d+)%s*%)$")
if a and b and c then return Vector3.new(tonumber(a), tonumber(b), tonumber(c)) end
return tostring(tok)
end
local function parseArgsText(txt)
txt = tostring(txt or "")
txt = trim(txt)
if txt == "" then return {} end
local out = {}
for token in txt:gmatch("([^,]+)") do
local v = parseSingleToken(token)
if v ~= nil then table.insert(out, v) end
end
return out
end
local function safeCallRemoteSingle(remote, argsTable, sendAsXYZ)
if not remote or not remote.Parent then return false, "remote missing" end
local ok, err = pcall(function()
if sendAsXYZ then
if #argsTable >= 1 and typeof(argsTable[1]) == "Vector3" then
local v = argsTable[1]
local rest = {}
for i = 2, #argsTable do table.insert(rest, argsTable[i]) end
local sendArgs = {v.X, v.Y, v.Z}
for _,o in ipairs(rest) do table.insert(sendArgs, o) end
if remote:IsA("RemoteEvent") then remote:FireServer(unpack(sendArgs)) else remote:InvokeServer(unpack(sendArgs)) end
else
if remote:IsA("RemoteEvent") then remote:FireServer(unpack(argsTable)) else remote:InvokeServer(unpack(argsTable)) end
end
else
if remote:IsA("RemoteEvent") then remote:FireServer(unpack(argsTable)) else remote:InvokeServer(unpack(argsTable)) end
end
end)
return ok, err
end
local function safeCallRemote(remoteOrList, argsTable, sendAsXYZ)
if not remoteOrList then return false, "no remotes" end
if typeof(remoteOrList) == "Instance" then
return safeCallRemoteSingle(remoteOrList, argsTable, sendAsXYZ)
elseif type(remoteOrList) == "table" then
local anyOk = false
local lastErr = nil
for _,rm in ipairs(remoteOrList) do
local ok, err = pcall(function() return safeCallRemoteSingle(rm, argsTable, sendAsXYZ) end)
if ok and err ~= false then anyOk = true else lastErr = err end
end
return anyOk, lastErr
else
return false, "invalid remote type"
end
end
-- --------------------
-- Prediction & humanoid cache
-- --------------------
local function getLocalReferencePos()
local ch = player.Character
if ch and ch.PrimaryPart then return ch.PrimaryPart.Position end
if ch and ch:FindFirstChild("HumanoidRootPart") and ch.HumanoidRootPart:IsA("BasePart") then return ch.HumanoidRootPart.Position end
if Camera then return Camera.CFrame.p end
return Vector3.new(0,0,0)
end
local function getLocalForwardVector()
local ch = player.Character
if ch and ch.PrimaryPart then return ch.PrimaryPart.CFrame.LookVector end
if Camera then return Camera.CFrame.LookVector end
return Vector3.new(0,0,-1)
end
local function clampPredictionForSpeed(predX, predY, speed)
if speed > 200 then
if predX > 0 then predX = math.min(predX, 200 / speed) end
if predY > 0 then predY = math.min(predY, 200 / speed) end
end
return predX, predY
end
local humanoidCache = {}
local humanoidCacheLock = false
local HUMANOID_CACHE_INTERVAL = 0.6
local function refreshHumanoidCache()
if humanoidCacheLock then return end
humanoidCacheLock = true
local newCache = {}
for _, child in ipairs(workspace:GetChildren()) do
if child and child:IsA("Model") and child.Name ~= player.Name then
local humanoid = child:FindFirstChildOfClass("Humanoid")
if humanoid and humanoid.Health > 0 then
local root = child:FindFirstChild("HumanoidRootPart") or child.PrimaryPart
if root and root:IsA("BasePart") then
table.insert(newCache, { model = child, root = root, humanoid = humanoid })
end
end
end
end
humanoidCache = newCache
humanoidCacheLock = false
end
task.spawn(function()
while true do
pcall(refreshHumanoidCache)
task.wait(HUMANOID_CACHE_INTERVAL)
end
end)
local function getNearestPlayerPredictedPos(predX, predY)
predX = tonumber(predX) or 0; predY = tonumber(predY) or 0
local refPos = getLocalReferencePos()
local bestPl, bestPos, bestDist = nil, nil, math.huge
for _,pl in ipairs(Players:GetPlayers()) do
if pl ~= player and pl.Character then
local hrp = pl.Character:FindFirstChild("HumanoidRootPart") or pl.Character.PrimaryPart
if hrp and hrp:IsA("BasePart") then
local d = (hrp.Position - refPos).Magnitude
if d < bestDist then bestDist = d; bestPl = pl; bestPos = hrp.Position end
end
end
end
if not bestPl or not bestPos then return nil, nil end
local hrpInst = bestPl.Character and (bestPl.Character:FindFirstChild("HumanoidRootPart") or bestPl.Character.PrimaryPart)
local vel = Vector3.new(0,0,0)
if hrpInst and hrpInst:IsA("BasePart") then vel = hrpInst.Velocity or Vector3.new(0,0,0) end
predX, predY = clampPredictionForSpeed(predX, predY, vel.Magnitude)
local predicted = bestPos + Vector3.new(vel.X * predX, vel.Y * predY, vel.Z * predX)
return predicted, bestPl
end
local function getNearestHumanoidPredictedPos(predX, predY)
predX = tonumber(predX) or 0; predY = tonumber(predY) or 0
local refPos = getLocalReferencePos()
local bestEntry, bestDist = nil, math.huge
for _,entry in ipairs(humanoidCache) do
local root, humanoid = entry.root, entry.humanoid
if root and humanoid and humanoid.Health > 0 then
local ok,pos = pcall(function() return root.Position end)
if ok and pos then
local d = (pos - refPos).Magnitude
if d < bestDist then bestDist = d; bestEntry = entry end
end
end
end
if not bestEntry then
for _,child in ipairs(workspace:GetChildren()) do
if child and child:IsA("Model") and child.Name ~= player.Name then
local humanoid = child:FindFirstChildOfClass("Humanoid")
if humanoid and humanoid.Health > 0 then
local root = child:FindFirstChild("HumanoidRootPart") or child.PrimaryPart
if root and root:IsA("BasePart") then
local ok,pos = pcall(function() return root.Position end)
if ok and pos then
local d = (pos - refPos).Magnitude
if d < bestDist then bestDist = d; bestEntry = {model = child, root = root, humanoid = humanoid} end
end
end
end
end
end
end
if not bestEntry or not bestEntry.root then return nil, nil end
local rootPart = bestEntry.root
local vel = Vector3.new(0,0,0)
if rootPart and rootPart:IsA("BasePart") then vel = rootPart.Velocity or Vector3.new(0,0,0) end
predX, predY = clampPredictionForSpeed(predX, predY, vel.Magnitude)
local predicted = rootPart.Position + Vector3.new(vel.X * predX, vel.Y * predY, vel.Z * predX)
return predicted, bestEntry.model
end
-- --------------------
-- Placement system state & helpers (fixed)
-- --------------------
local placement = {
part = nil,
billboard = nil,
studs = 6,
lastPos = nil,
selectedModel = nil,
stickToEntity = false,
clickCapture = false
}
local function findModelAdornee(model)
if not model then return nil end
local root = model:FindFirstChild("HumanoidRootPart") or model.PrimaryPart
if root and root:IsA("BasePart") then return root end
-- fallback: find the first BasePart
for _,v in ipairs(model:GetDescendants()) do
if v:IsA("BasePart") then return v end
end
return nil
end
local function createPlacementPartIfNeeded()
if placement.part and placement.part.Parent then return end
if placement.part then pcall(function() placement.part:Destroy() end) end
local p = Instance.new("Part")
p.Name = "RKB_PlacementPart"
p.Size = Vector3.new(1.2,1.2,1.2)
p.Shape = Enum.PartType.Ball -- sphere
p.Anchored = true
p.CanCollide = false
p.Transparency = 0.45
p.Color = Color3.fromRGB(70, 200, 120)
p.Material = Enum.Material.SmoothPlastic
p.Parent = workspace
placement.part = p
if placement.billboard then pcall(function() placement.billboard:Destroy() end) end
local bb = Instance.new("BillboardGui")
bb.Name = "RKB_PlacementBB"
bb.Size = UDim2.new(0,120,0,36)
bb.Adornee = p
bb.AlwaysOnTop = true
local lbl = Instance.new("TextLabel", bb)
lbl.Size = UDim2.new(1,0,1,0)
lbl.BackgroundTransparency = 1
lbl.Text = "Placement"
lbl.Font = Enum.Font.GothamBold
lbl.TextSize = 14
lbl.TextColor3 = Color3.new(1,1,1)
lbl.TextStrokeTransparency = 0.6
bb.Parent = p
placement.billboard = bb
end
local function destroyPlacementVisual()
if placement.billboard then pcall(function() placement.billboard:Destroy() end) placement.billboard = nil end
if placement.part then pcall(function() placement.part:Destroy() end) placement.part = nil end
end
local function createSelectionBillboardForModel(model)
if not model then return end
local adornee = findModelAdornee(model)
if not adornee then return end
-- remove previous billboard attached to model
local existing = model:FindFirstChild("RKB_SelectedBB")
if existing then existing:Destroy() end
local bb = Instance.new("BillboardGui")
bb.Name = "RKB_SelectedBB"
bb.Size = UDim2.new(0,160,0,36)
bb.AlwaysOnTop = true
bb.Adornee = adornee
local lbl = Instance.new("TextLabel", bb)
lbl.Size = UDim2.new(1,0,1,0)
lbl.BackgroundTransparency = 0.6
lbl.BackgroundColor3 = Color3.fromRGB(30,30,40)
lbl.Text = "Selected: "..tostring(model.Name)
lbl.Font = Enum.Font.GothamBold
lbl.TextSize = 14
lbl.TextColor3 = Color3.new(1,1,1)
lbl.TextStrokeTransparency = 0.6
bb.Parent = model
end
local function clearSelectionBillboard(model)
if not model then return end
local existing = model:FindFirstChild("RKB_SelectedBB")
if existing then existing:Destroy() end
end
local function updatePlacementVisual(aimMode)
if aimMode == "StraightForward" then
createPlacementPartIfNeeded()
-- flatten forward so it doesn't aim up
local forward = getLocalForwardVector()
forward = Vector3.new(forward.X, 0, forward.Z)
if forward.Magnitude == 0 then
forward = Camera and Vector3.new(Camera.CFrame.LookVector.X, 0, Camera.CFrame.LookVector.Z) or Vector3.new(0,0,-1)
end
local base = getLocalReferencePos()
local studs = tonumber(placement.studs) or 6
local pos = base + (forward.Unit * studs)
-- lock Y to local reference Y (prevents aiming up)
pos = Vector3.new(pos.X, base.Y, pos.Z)
placement.lastPos = pos
if placement.part then
placement.part.Position = pos
if placement.billboard and placement.billboard:FindFirstChildOfClass("TextLabel") then
placement.billboard:FindFirstChildOfClass("TextLabel").Text = ("Straight: %.1f studs"):format(studs)
end
end
if placement.selectedModel then
clearSelectionBillboard(placement.selectedModel)
placement.selectedModel = nil
end
elseif aimMode == "CustomPlacement" then
createPlacementPartIfNeeded()
-- if a model was selected, always follow the model's adornee (Primary/HumanoidRootPart)
if placement.selectedModel then
local adornee = findModelAdornee(placement.selectedModel)
if adornee then
local pos = adornee.Position
placement.lastPos = pos
placement.part.Position = pos
if placement.billboard and placement.billboard:FindFirstChildOfClass("TextLabel") then
placement.billboard:FindFirstChildOfClass("TextLabel").Text = ("Stuck to: %s"):format(tostring(placement.selectedModel.Name))
end
-- ensure selection billboard exists
createSelectionBillboardForModel(placement.selectedModel)
else
-- fallback to last known pos
if placement.lastPos then
placement.part.Position = placement.lastPos
if placement.billboard and placement.billboard:FindFirstChildOfClass("TextLabel") then
placement.billboard:FindFirstChildOfClass("TextLabel").Text = "Custom: "..tostring(placement.lastPos)
end
else
placement.part.Position = getLocalReferencePos() + Vector3.new(0,-300,0)
end
end
elseif placement.lastPos then
placement.part.Position = placement.lastPos
if placement.billboard and placement.billboard:FindFirstChildOfClass("TextLabel") then
placement.billboard:FindFirstChildOfClass("TextLabel").Text = "Custom: "..tostring(placement.lastPos)
end
else
-- nothing set yet: hide
placement.part.Position = getLocalReferencePos() + Vector3.new(0,-300,0)
if placement.billboard and placement.billboard:FindFirstChildOfClass("TextLabel") then
placement.billboard:FindFirstChildOfClass("TextLabel").Text = "Custom: <none>"
end
end
else
-- not a placement mode: move out of view but remember data
if placement.part then
placement.part.Position = getLocalReferencePos() + Vector3.new(0,-300,0)
end
if placement.selectedModel and not placement.stickToEntity then
clearSelectionBillboard(placement.selectedModel)
placement.selectedModel = nil
end
end
end
-- keep RenderStepped visual updates (reads global aim mode set by UI)
RunService.RenderStepped:Connect(function()
if _G.RKB_currentAimMode then
pcall(function() updatePlacementVisual(_G.RKB_currentAimMode) end)
end
end)
-- --------------------
-- Persistence storage helpers
-- --------------------
local settingsFolder = player:FindFirstChild("RemoteKeybinderSettings")
if not settingsFolder then
settingsFolder = Instance.new("Folder")
settingsFolder.Name = "RemoteKeybinderSettings"
settingsFolder.Parent = player
end
local function saveBindingToFolder(keyName, bindingTable)
if not keyName or not bindingTable then return false, "invalid params" end
local data = {
key = keyName,
remotePath = bindingTable.remotePath or "",
remotePaths = bindingTable.remotePaths or nil,
extraArgsText = bindingTable.extraArgsText or "",
hold = bindingTable.hold and true or false,
interval = tonumber(bindingTable.interval) or 0.12,
mode = bindingTable.mode or "Mouse",
predictionX = tonumber(bindingTable.predictionX) or 0,
predictionY = tonumber(bindingTable.predictionY) or 0,
argFormat = bindingTable.argFormat or "Vector3",
placeStuds = tonumber(bindingTable.placeStuds) or nil,
placeLastPos = bindingTable.placeLastPos or nil,
placeStick = bindingTable.placeStick and true or false
}
local s = HttpService:JSONEncode(data)
local name = "binding_" .. keyName
local existing = settingsFolder:FindFirstChild(name)
if existing and existing:IsA("StringValue") then existing.Value = s else
local sv = Instance.new("StringValue")
sv.Name = name
sv.Value = s
sv.Parent = settingsFolder
end
return true
end
local function deleteSavedBinding(keyName)
if not keyName then return false end
local name = "binding_" .. keyName
local existing = settingsFolder:FindFirstChild(name)
if existing then existing:Destroy(); return true end
return false
end
local function loadSavedBindingsIntoTable()
local out = {}
for _,child in ipairs(settingsFolder:GetChildren()) do
if child:IsA("StringValue") and child.Name:match("^binding_") then
local ok, data = pcall(function() return HttpService:JSONDecode(child.Value) end)
if ok and type(data) == "table" and data.key then out[data.key] = data end
end
end
return out
end
local function findInstanceByFullName(fullPath)
if not fullPath or fullPath == "" then return nil end
local function scanContainer(container)
if not container then return nil end
for _,inst in ipairs(container:GetDescendants()) do
local ok, p = pcall(function() return inst:GetFullName() end)
if ok and p == fullPath then return inst end
end
return nil
end
local res = scanContainer(backpack)
if res then return res end
res = scanContainer(character)
if res then return res end
return nil
end
-- --------------------
-- UI BUILD + placement controls (kept same layout as previous)
-- --------------------
local existingGui = guiParent:FindFirstChild("PolishedRemoteKeybinder")
if existingGui then existingGui:Destroy() end
local ScreenGui = Instance.new("ScreenGui")
ScreenGui.Name = "PolishedRemoteKeybinder"
ScreenGui.ResetOnSpawn = false
ScreenGui.Parent = guiParent
local frameMain = Instance.new("Frame")
frameMain.Name = "Main"
frameMain.Size = UDim2.new(0, 920, 0, 520)
frameMain.Position = UDim2.new(0.5, -460, 0.5, -260)
frameMain.BackgroundColor3 = Color3.fromRGB(20,20,26)
frameMain.BorderSizePixel = 0
frameMain.Active = true
frameMain.Draggable = true
frameMain.Parent = ScreenGui
local header = Instance.new("Frame", frameMain)
header.Size = UDim2.new(1,0,0,64)
header.Position = UDim2.new(0,0,0,0)
header.BackgroundColor3 = Color3.fromRGB(28,28,36)
header.BorderSizePixel = 0
local title = Instance.new("TextLabel", header)
title.Text = "Remote Keybinder — Inventory (Backpack + Character)"
title.Font = Enum.Font.GothamBold
title.TextSize = 18
title.TextColor3 = Color3.fromRGB(235,235,235)
title.BackgroundTransparency = 1
title.Size = UDim2.new(0.7, -20, 1, 0)
title.Position = UDim2.new(0, 14, 0, 0)
title.TextXAlignment = Enum.TextXAlignment.Left
local subtitle = Instance.new("TextLabel", header)
subtitle.Text = "Click entries to inspect, or toggle Multi-Select below the list to pick several then bind."
subtitle.Font = Enum.Font.Gotham
subtitle.TextSize = 12
subtitle.TextColor3 = Color3.fromRGB(180,180,190)
subtitle.BackgroundTransparency = 1
subtitle.Size = UDim2.new(0.7, -20, 1, 0)
subtitle.Position = UDim2.new(0, 14, 0, 26)
subtitle.TextXAlignment = Enum.TextXAlignment.Left
local reloadBtn = Instance.new("TextButton", header)
reloadBtn.Name = "Reload"
reloadBtn.Size = UDim2.new(0, 110, 0, 36)
reloadBtn.Position = UDim2.new(1, -360, 0, 14)
reloadBtn.BackgroundColor3 = Color3.fromRGB(56,56,76)
reloadBtn.Text = "Reload (R)"
reloadBtn.Font = Enum.Font.Gotham
reloadBtn.TextSize = 14
reloadBtn.TextColor3 = Color3.fromRGB(240,240,240)
reloadBtn.BorderSizePixel = 0
local resetToolsBtn = Instance.new("TextButton", header)
resetToolsBtn.Name = "ResetTools"
resetToolsBtn.Size = UDim2.new(0, 130, 0, 36)
resetToolsBtn.Position = UDim2.new(1, -236, 0, 14)
resetToolsBtn.BackgroundColor3 = Color3.fromRGB(96,64,32)
resetToolsBtn.Text = "Reset Tools"
resetToolsBtn.Font = Enum.Font.Gotham
resetToolsBtn.TextSize = 14
resetToolsBtn.TextColor3 = Color3.fromRGB(240,240,240)
resetToolsBtn.BorderSizePixel = 0
-- left pane (remotes)
local leftPane = Instance.new("Frame", frameMain)
leftPane.Position = UDim2.new(0, 14, 0, 74)
leftPane.Size = UDim2.new(0, 420, 0, 420)
leftPane.BackgroundTransparency = 1
local leftTitle = Instance.new("TextLabel", leftPane)
leftTitle.Size = UDim2.new(1, 0, 0, 20)
leftTitle.Position = UDim2.new(0,0,0,0)
leftTitle.BackgroundTransparency = 1
leftTitle.Font = Enum.Font.Gotham
leftTitle.TextSize = 13
leftTitle.TextColor3 = Color3.fromRGB(210,210,210)
leftTitle.Text = "Detected inventory remotes (click to inspect)"
local leftScroll = Instance.new("ScrollingFrame", leftPane)
leftScroll.Size = UDim2.new(1, 0, 1, -80)
leftScroll.Position = UDim2.new(0, 0, 0, 28)
leftScroll.BackgroundTransparency = 1
leftScroll.ScrollBarThickness = 6
local leftListLayout = Instance.new("UIListLayout", leftScroll)
leftListLayout.Padding = UDim.new(0,8)
leftListLayout.SortOrder = Enum.SortOrder.LayoutOrder
-- multi-select control area (under the remote list)
local multiArea = Instance.new("Frame", leftPane)
multiArea.Position = UDim2.new(0, 0, 1, -46)
multiArea.Size = UDim2.new(1, 0, 0, 46)
multiArea.BackgroundTransparency = 1
local multiToggle = Instance.new("TextButton", multiArea)
multiToggle.Position = UDim2.new(0,6,0,6)
multiToggle.Size = UDim2.new(0,140,0,34)
multiToggle.Text = "Multi-Select: Off"
multiToggle.Font = Enum.Font.Gotham
multiToggle.TextSize = 14
multiToggle.BackgroundColor3 = Color3.fromRGB(64,64,76)
multiToggle.TextColor3 = Color3.fromRGB(240,240,240)
multiToggle.BorderSizePixel = 0
local clearSelectionBtn = Instance.new("TextButton", multiArea)
clearSelectionBtn.Position = UDim2.new(0,156,0,6)
clearSelectionBtn.Size = UDim2.new(0,110,0,34)
clearSelectionBtn.Text = "Clear Selection"
clearSelectionBtn.Font = Enum.Font.Gotham
clearSelectionBtn.TextSize = 14
clearSelectionBtn.BackgroundColor3 = Color3.fromRGB(84,44,44)
clearSelectionBtn.TextColor3 = Color3.fromRGB(240,240,240)
clearSelectionBtn.BorderSizePixel = 0
local clearMultiBindsBtn = Instance.new("TextButton", multiArea)
clearMultiBindsBtn.Position = UDim2.new(0,276,0,6)
clearMultiBindsBtn.Size = UDim2.new(0,136,0,34)
clearMultiBindsBtn.Text = "Clear Multi Binds (Del)"
clearMultiBindsBtn.Font = Enum.Font.Gotham
clearMultiBindsBtn.TextSize = 13
clearMultiBindsBtn.BackgroundColor3 = Color3.fromRGB(42,122,255)
clearMultiBindsBtn.TextColor3 = Color3.fromRGB(240,240,240)
clearMultiBindsBtn.BorderSizePixel = 0
-- right pane (inspector + placement controls)
local rightPane = Instance.new("Frame", frameMain)
rightPane.Position = UDim2.new(0, 448, 0, 74)
rightPane.Size = UDim2.new(0, 464, 0, 420)
rightPane.BackgroundTransparency = 1
local inspectorTitle = Instance.new("TextLabel", rightPane)
inspectorTitle.Position = UDim2.new(0,0,0,0)
inspectorTitle.Size = UDim2.new(1,0,0,20)
inspectorTitle.BackgroundTransparency = 1
inspectorTitle.Font = Enum.Font.Gotham
inspectorTitle.TextSize = 13
inspectorTitle.TextColor3 = Color3.fromRGB(210,210,210)
inspectorTitle.Text = "Inspector & Keybinds"
local lblRemote = Instance.new("TextLabel", rightPane)
lblRemote.Position = UDim2.new(0,0,0,28)
lblRemote.Size = UDim2.new(1,0,0,20)
lblRemote.BackgroundTransparency = 1
lblRemote.Font = Enum.Font.Gotham
lblRemote.TextSize = 12
lblRemote.TextColor3 = Color3.fromRGB(220,220,220)
lblRemote.Text = "Remote: <none>"
local lblParent = Instance.new("TextLabel", rightPane)
lblParent.Position = UDim2.new(0,0,0,48)
lblParent.Size = UDim2.new(1,0,0,16)
lblParent.BackgroundTransparency = 1
lblParent.Font = Enum.Font.Gotham
lblParent.TextSize = 11
lblParent.TextColor3 = Color3.fromRGB(170,170,170)
lblParent.Text = "Parent: <none>"
local lblType = Instance.new("TextLabel", rightPane)
lblType.Position = UDim2.new(0,0,0,66)
lblType.Size = UDim2.new(1,0,0,16)
lblType.BackgroundTransparency = 1
lblType.Font = Enum.Font.Gotham
lblType.TextSize = 11
lblType.TextColor3 = Color3.fromRGB(170,170,170)
lblType.Text = "Type: <none>"
local aimLabel = Instance.new("TextLabel", rightPane)
aimLabel.Position = UDim2.new(0,0,0,92)
aimLabel.Size = UDim2.new(0,220,0,18)
aimLabel.BackgroundTransparency = 1
aimLabel.Font = Enum.Font.Gotham
aimLabel.TextSize = 12
aimLabel.TextColor3 = Color3.fromRGB(200,200,200)
aimLabel.Text = "Aim mode:"
local aimDropdown = Instance.new("TextButton", rightPane)
aimDropdown.Position = UDim2.new(0,0,0,114)
aimDropdown.Size = UDim2.new(0,220,0,28)
aimDropdown.Font = Enum.Font.Gotham
aimDropdown.TextSize = 12
aimDropdown.TextColor3 = Color3.fromRGB(240,240,240)
aimDropdown.Text = "Mouse" -- cycles: Mouse, NearestPlayer, NearestHumanoid, StraightForward, CustomPlacement
aimDropdown.BackgroundColor3 = Color3.fromRGB(48,48,62)
aimDropdown.BorderSizePixel = 0
local predXLabel = Instance.new("TextLabel", rightPane)
predXLabel.Position = UDim2.new(0,232,0,92)
predXLabel.Size = UDim2.new(0,110,0,18)
predXLabel.BackgroundTransparency = 1
predXLabel.Font = Enum.Font.Gotham
predXLabel.TextSize = 12
predXLabel.TextColor3 = Color3.fromRGB(200,200,200)
predXLabel.Text = "Prediction X (s):"
local predXBox = Instance.new("TextBox", rightPane)
predXBox.Position = UDim2.new(0,232,0,114)
predXBox.Size = UDim2.new(0,66,0,28)
predXBox.BackgroundColor3 = Color3.fromRGB(34,34,40)
predXBox.PlaceholderText = "0.15"
predXBox.Text = "0.15"
predXBox.ClearTextOnFocus = false
predXBox.TextColor3 = Color3.fromRGB(230,230,230)
predXBox.Font = Enum.Font.Gotham
predXBox.TextSize = 12
local predYLabel = Instance.new("TextLabel", rightPane)
predYLabel.Position = UDim2.new(0,302,0,92)
predYLabel.Size = UDim2.new(0,160,0,18)
predYLabel.BackgroundTransparency = 1
predYLabel.Font = Enum.Font.Gotham
predYLabel.TextSize = 12
predYLabel.TextColor3 = Color3.fromRGB(200,200,200)
predYLabel.Text = "Prediction Y (s):"
local predYBox = Instance.new("TextBox", rightPane)
predYBox.Position = UDim2.new(0,302,0,114)
predYBox.Size = UDim2.new(0,66,0,28)
predYBox.BackgroundColor3 = Color3.fromRGB(34,34,40)
predYBox.PlaceholderText = "0.05"
predYBox.Text = "0.05"
predYBox.ClearTextOnFocus = false
predYBox.TextColor3 = Color3.fromRGB(230,230,230)
predYBox.Font = Enum.Font.Gotham
predYBox.TextSize = 12
-- Place studs / click controls
local placeLabel = Instance.new("TextLabel", rightPane)
placeLabel.Position = UDim2.new(0,0,0,154)
placeLabel.Size = UDim2.new(0,140,0,18)
placeLabel.BackgroundTransparency = 1
placeLabel.Font = Enum.Font.Gotham
placeLabel.TextSize = 12
placeLabel.TextColor3 = Color3.fromRGB(200,200,200)
placeLabel.Text = "Place studs:"
local placeStudsBox = Instance.new("TextBox", rightPane)
placeStudsBox.Position = UDim2.new(0,140,0,150)
placeStudsBox.Size = UDim2.new(0,60,0,26)
placeStudsBox.BackgroundColor3 = Color3.fromRGB(34,34,40)
placeStudsBox.PlaceholderText = "6"
placeStudsBox.Text = tostring(placement.studs)
placeStudsBox.ClearTextOnFocus = false
placeStudsBox.TextColor3 = Color3.fromRGB(230,230,230)
placeStudsBox.Font = Enum.Font.Gotham
placeStudsBox.TextSize = 12
local placeClickBtn = Instance.new("TextButton", rightPane)
placeClickBtn.Position = UDim2.new(0,220,0,150)
placeClickBtn.Size = UDim2.new(0,140,0,26)
placeClickBtn.Font = Enum.Font.Gotham
placeClickBtn.Text = "Place / Select (Click)"
placeClickBtn.BackgroundColor3 = Color3.fromRGB(56,56,76)
placeClickBtn.TextColor3 = Color3.fromRGB(240,240,240)
placeClickBtn.TextSize = 12
placeClickBtn.BorderSizePixel = 0
local stickToggle = Instance.new("TextButton", rightPane)
stickToggle.Position = UDim2.new(0,370,0,150)
stickToggle.Size = UDim2.new(0,86,0,26)
stickToggle.Font = Enum.Font.Gotham
stickToggle.Text = "Stick: Off"
stickToggle.BackgroundColor3 = Color3.fromRGB(44,44,60)
stickToggle.TextColor3 = Color3.fromRGB(230,230,230)
stickToggle.TextSize = 12
stickToggle.BorderSizePixel = 0
local placementSelectedLabel = Instance.new("TextLabel", rightPane)
placementSelectedLabel.Position = UDim2.new(0,0,0,182)
placementSelectedLabel.Size = UDim2.new(1,0,0,18)
placementSelectedLabel.BackgroundTransparency = 1
placementSelectedLabel.Font = Enum.Font.Gotham
placementSelectedLabel.TextSize = 12
placementSelectedLabel.TextColor3 = Color3.fromRGB(200,200,200)
placementSelectedLabel.Text = "Selected placement: <none>"
-- rest of previous UI (format, args, keybinds, etc.)
local formatLabel = Instance.new("TextLabel", rightPane)
formatLabel.Position = UDim2.new(0,0,0,206)
formatLabel.Size = UDim2.new(0,200,0,18)
formatLabel.BackgroundTransparency = 1
formatLabel.Font = Enum.Font.Gotham
formatLabel.TextSize = 12
formatLabel.TextColor3 = Color3.fromRGB(200,200,200)
formatLabel.Text = "Arg format:"
local formatDropdown = Instance.new("TextButton", rightPane)
formatDropdown.Position = UDim2.new(0,0,0,228)
formatDropdown.Size = UDim2.new(0,120,0,26)
formatDropdown.Font = Enum.Font.Gotham
formatDropdown.TextSize = 12
formatDropdown.TextColor3 = Color3.fromRGB(240,240,240)
formatDropdown.Text = "Vector3"
formatDropdown.BackgroundColor3 = Color3.fromRGB(48,48,62)
formatDropdown.BorderSizePixel = 0
local extraLabel = Instance.new("TextLabel", rightPane)
extraLabel.Position = UDim2.new(0,0,0,262)
extraLabel.Size = UDim2.new(1,0,0,18)
extraLabel.BackgroundTransparency = 1
extraLabel.Font = Enum.Font.Gotham
extraLabel.TextSize = 12
extraLabel.TextColor3 = Color3.fromRGB(180,180,180)
extraLabel.Text = "Extra args (comma-separated):"
local argsBox = Instance.new("TextBox", rightPane)
argsBox.Position = UDim2.new(0,0,0,284)
argsBox.Size = UDim2.new(1,-12,0,64)
argsBox.BackgroundColor3 = Color3.fromRGB(34,34,40)
argsBox.ClearTextOnFocus = false
argsBox.TextWrap = true
argsBox.MultiLine = true
argsBox.Text = ""
argsBox.TextColor3 = Color3.fromRGB(230,230,230)
argsBox.Font = Enum.Font.Gotham
argsBox.TextSize = 12
local setKeyBtn = Instance.new("TextButton", rightPane)
setKeyBtn.Position = UDim2.new(0,0,1,-124)
setKeyBtn.Size = UDim2.new(0,160,0,36)
setKeyBtn.BackgroundColor3 = Color3.fromRGB(64,64,88)
setKeyBtn.Font = Enum.Font.Gotham
setKeyBtn.Text = "Set Keybind (single)"
setKeyBtn.TextSize = 13
setKeyBtn.TextColor3 = Color3.fromRGB(240,240,240)
setKeyBtn.BorderSizePixel = 0
local bindSelectedBtn = Instance.new("TextButton", rightPane)
bindSelectedBtn.Position = UDim2.new(0,170,1,-124)
bindSelectedBtn.Size = UDim2.new(0,160,0,36)
bindSelectedBtn.BackgroundColor3 = Color3.fromRGB(56,96,120)
bindSelectedBtn.Font = Enum.Font.Gotham
bindSelectedBtn.Text = "Bind Selected (multi)"
bindSelectedBtn.TextSize = 13
bindSelectedBtn.TextColor3 = Color3.fromRGB(240,240,240)
bindSelectedBtn.BorderSizePixel = 0
local selectedCountLabel = Instance.new("TextLabel", bindSelectedBtn)
selectedCountLabel.Size = UDim2.new(0,56,0,28)
selectedCountLabel.Position = UDim2.new(1,-62,0,4)
selectedCountLabel.BackgroundTransparency = 1
selectedCountLabel.Font = Enum.Font.Gotham
selectedCountLabel.TextSize = 13
selectedCountLabel.TextColor3 = Color3.fromRGB(220,220,220)
selectedCountLabel.Text = "0"
local assignedLabel = Instance.new("TextLabel", rightPane)
assignedLabel.Position = UDim2.new(0,0,1,-84)
assignedLabel.Size = UDim2.new(1,0,0,24)
assignedLabel.BackgroundTransparency = 1
assignedLabel.Font = Enum.Font.Gotham
assignedLabel.TextSize = 12
assignedLabel.Text = "Assigned: <none>"
assignedLabel.TextColor3 = Color3.fromRGB(200,200,200)
local holdToggle = Instance.new("TextButton", rightPane)
holdToggle.Position = UDim2.new(0,0,1,-52)
holdToggle.Size = UDim2.new(0,120,0,28)
holdToggle.BackgroundColor3 = Color3.fromRGB(44,44,60)
holdToggle.Font = Enum.Font.Gotham
holdToggle.Text = "Hold mode"
holdToggle.TextSize = 12
holdToggle.TextColor3 = Color3.fromRGB(230,230,230)
holdToggle.BorderSizePixel = 0
local intervalLabel = Instance.new("TextLabel", rightPane)
intervalLabel.Position = UDim2.new(0,132,1,-52)
intervalLabel.Size = UDim2.new(0,120,0,18)
intervalLabel.BackgroundTransparency = 1
intervalLabel.Font = Enum.Font.Gotham
intervalLabel.TextSize = 11
intervalLabel.Text = "Interval (s):"
intervalLabel.TextColor3 = Color3.fromRGB(180,180,180)
local intervalBox = Instance.new("TextBox", rightPane)
intervalBox.Position = UDim2.new(0,132,1,-34)
intervalBox.Size = UDim2.new(0,80,0,28)
intervalBox.BackgroundColor3 = Color3.fromRGB(34,34,40)
intervalBox.Text = "0.12"
intervalBox.ClearTextOnFocus = false
intervalBox.Font = Enum.Font.Gotham
intervalBox.TextSize = 12
intervalBox.TextColor3 = Color3.fromRGB(230,230,230)
local saveBtn = Instance.new("TextButton", rightPane)
saveBtn.Position = UDim2.new(0,0,1, -4)
saveBtn.Size = UDim2.new(0,86,0,30)
saveBtn.Font = Enum.Font.Gotham
saveBtn.Text = "Save"
saveBtn.BackgroundColor3 = Color3.fromRGB(36,120,44)
saveBtn.TextSize = 12
saveBtn.TextColor3 = Color3.fromRGB(240,240,240)
saveBtn.BorderSizePixel = 0
local updateBtn = Instance.new("TextButton", rightPane)
updateBtn.Position = UDim2.new(0,96,1,-4)
updateBtn.Size = UDim2.new(0,86,0,30)
updateBtn.Font = Enum.Font.Gotham
updateBtn.Text = "Update"
updateBtn.BackgroundColor3 = Color3.fromRGB(52,140,46)
updateBtn.TextSize = 12
updateBtn.TextColor3 = Color3.fromRGB(240,240,240)
updateBtn.BorderSizePixel = 0
local loadBtn = Instance.new("TextButton", rightPane)
loadBtn.Position = UDim2.new(0,192,1,-4)
loadBtn.Size = UDim2.new(0,120,0,30)
loadBtn.Font = Enum.Font.Gotham
loadBtn.Text = "Load Saved"
loadBtn.BackgroundColor3 = Color3.fromRGB(60,60,84)
loadBtn.TextSize = 12
loadBtn.TextColor3 = Color3.fromRGB(240,240,240)
loadBtn.BorderSizePixel = 0
local fireBtn = Instance.new("TextButton", rightPane)
fireBtn.Position = UDim2.new(0,324,1,-4)
fireBtn.Size = UDim2.new(0,86,0,30)
fireBtn.Font = Enum.Font.Gotham
fireBtn.Text = "Fire Now"
fireBtn.TextSize = 12
fireBtn.TextColor3 = Color3.fromRGB(240,240,240)
fireBtn.BackgroundColor3 = Color3.fromRGB(80,40,40)
fireBtn.BorderSizePixel = 0
local statusBar = Instance.new("TextLabel", frameMain)
statusBar.Position = UDim2.new(0,14,1,-32)
statusBar.Size = UDim2.new(1,-28,0,24)
statusBar.BackgroundTransparency = 1
statusBar.Font = Enum.Font.Gotham
statusBar.TextSize = 12
statusBar.TextColor3 = Color3.fromRGB(200,200,200)
statusBar.Text = "Ready."
-- --------------------
-- State (keeps older state variables)
-- --------------------
local entries = {}
local infos = {}
local selectedInfo = nil
local selectedSet = {}
local capturingKey = false
local capturingConn = nil
local bindings = {}
local pendingSaved = {}
local multiSelectMode = false
local NORMAL_BG = Color3.fromRGB(36,36,44)
local HOVER_BG = Color3.fromRGB(46,46,68)
local SELECT_BG = Color3.fromRGB(42,122,255)
-- store current aim mode so RenderStepped update can reference
_G.RKB_currentAimMode = aimDropdown.Text
-- --------------------
-- createEntryForRemote must be defined BEFORE scanInventoryOnce uses it (fixed)
-- --------------------
local function createEntryForRemote(remote)
if not remote or not remote.Parent then return end
if entries[remote] then return end
local info = { remote = remote, lastArgs = {}, lastArgsText = "" }
table.insert(infos, info)
local frame = Instance.new("Frame")
frame.Size = UDim2.new(1, -12, 0, 60)
frame.BackgroundColor3 = NORMAL_BG
frame.BorderSizePixel = 0
frame.Parent = leftScroll
local nameLbl = Instance.new("TextLabel", frame)
nameLbl.Size = UDim2.new(1, -12, 0, 22)
nameLbl.Position = UDim2.new(0, 6, 0, 6)
nameLbl.BackgroundTransparency = 1
nameLbl.Font = Enum.Font.GothamBold
nameLbl.TextSize = 12
nameLbl.TextColor3 = Color3.fromRGB(230,230,230)
local ok, fullname = pcall(function() return remote:GetFullName() end)
nameLbl.Text = ok and fullname or tostring(remote.Name)
local parentLbl = Instance.new("TextLabel", frame)
parentLbl.Size = UDim2.new(1, -12, 0, 14)
parentLbl.Position = UDim2.new(0,6,0,30)
parentLbl.BackgroundTransparency = 1
parentLbl.Font = Enum.Font.Gotham
parentLbl.TextSize = 11
parentLbl.TextColor3 = Color3.fromRGB(170,170,170)
parentLbl.Text = "Parent: " .. tostring(remote.Parent and remote.Parent.Name or "<nil>")
local btn = Instance.new("TextButton", frame)
btn.Size = UDim2.new(1, -12, 1, -10)
btn.Position = UDim2.new(0,6,0,5)
btn.BackgroundTransparency = 1
btn.Text = ""
btn.AutoButtonColor = true
btn.MouseEnter:Connect(function()
if multiSelectMode then
if selectedSet[remote] then return end
pcall(function() frame.BackgroundColor3 = Color3.fromRGB(58,142,255) end)
else
pcall(function() frame.BackgroundColor3 = HOVER_BG end)
end
end)
btn.MouseLeave:Connect(function()
if multiSelectMode then
if selectedSet[remote] then pcall(function() frame.BackgroundColor3 = SELECT_BG end) else pcall(function() frame.BackgroundColor3 = NORMAL_BG end) end
else
pcall(function() frame.BackgroundColor3 = NORMAL_BG end)
end
end)
btn.MouseButton1Click:Connect(function()
if multiSelectMode then
if selectedSet[remote] then
selectedSet[remote] = nil
pcall(function() frame.BackgroundColor3 = NORMAL_BG end)
else
selectedSet[remote] = true
pcall(function() frame.BackgroundColor3 = SELECT_BG end)
end
local c = 0 for _ in pairs(selectedSet) do c = c + 1 end
selectedCountLabel.Text = tostring(c)
statusBar.Text = ("Selected %d remotes."):format(c)
else
for r,f in pairs(entries) do
if f and f:IsA("Frame") then
pcall(function() TweenService:Create(f, TweenInfo.new(0.12), {BackgroundColor3 = NORMAL_BG}):Play() end)
end
end
pcall(function() TweenService:Create(frame, TweenInfo.new(0.12), {BackgroundColor3 = HOVER_BG}):Play() end)
selectedInfo = info
local ok2, full2 = pcall(function() return remote:GetFullName() end)
lblRemote.Text = "Remote: " .. (ok2 and full2 or tostring(remote.Name))
lblParent.Text = "Parent: " .. tostring(remote.Parent and remote.Parent:GetFullName() or "<nil>")
lblType.Text = "Type: " .. (remote:IsA("RemoteEvent") and "RemoteEvent" or "RemoteFunction")
argsBox.Text = info.lastArgsText or ""
statusBar.Text = "Selected remote: " .. (ok2 and full2 or tostring(remote.Name))
local found = nil; local count = 0
for keyEnum,bd in pairs(bindings) do
local rlist = bd.remote
if typeof(rlist) == "Instance" then
if rlist == remote then found = keyEnumToName(keyEnum); count = 1; break end
else
for _, rr in ipairs(rlist or {}) do
if rr == remote then found = keyEnumToName(keyEnum); count = #rlist; break end
end
end
if found then break end
end
if found then assignedLabel.Text = ("Assigned: %s (remotes: %d)"):format(found, count) else assignedLabel.Text = "Assigned: <none>" end
end
end)
entries[remote] = frame
end
-- cleanup when remote removed (keeps multi-selection & bindings clean)
local function handleRemoteRemoved(remote)
if selectedSet[remote] then
selectedSet[remote] = nil
if selectedCountLabel then
local c = 0
for _ in pairs(selectedSet) do c = c + 1 end
selectedCountLabel.Text = tostring(c)
end
end
for keyEnum, bd in pairs(bindings) do
local rr = bd.remote
if typeof(rr) == "Instance" then
if rr == remote then
bindings[keyEnum] = nil
pcall(function() deleteSavedBinding(keyEnumToName(keyEnum)) end)
end
elseif type(rr) == "table" then
local changed = false
for i = #rr, 1, -1 do
if rr[i] == remote then
table.remove(rr, i)
changed = true
end
end
if changed then
if #rr == 0 then
bindings[keyEnum] = nil
pcall(function() deleteSavedBinding(keyEnumToName(keyEnum)) end)
else
if bd.remotePaths and type(bd.remotePaths) == "table" then
for i = #bd.remotePaths, 1, -1 do
local path = bd.remotePaths[i]
if path and path:match("%." .. remote.Name .. "$") then
table.remove(bd.remotePaths, i)
end
end
bd.remotePath = bd.remotePaths[1]
end
bd.remote = rr
end
end
end
end
end
-- scanning inventory and creating entries (uses createEntryForRemote defined above)
local function scanInventoryOnce()
for r,fr in pairs(entries) do
if not r or not r.Parent then
if fr and fr.Parent then fr:Destroy() end
entries[r] = nil
pcall(function() handleRemoteRemoved(r) end)
end
end
local function scanContainer(container)
if not container then return end
for _, inst in ipairs(container:GetDescendants()) do
if isRemote(inst) and not entries[inst] then
createEntryForRemote(inst)
end
end
end
-- scan backpack and character (covers tools in both)
scanContainer(backpack)
scanContainer(character)
end
-- --------------------
-- Argument builder & firing (now supports placement modes and follows model)
-- --------------------
local function buildArgsForBinding(bd)
if not bd or not bd.remote then return {} end
local args = {}
local targetPos = nil
if bd.mode == "Mouse" then
targetPos = getMouseHitPos()
elseif bd.mode == "NearestPlayer" then
local pos,pl = getNearestPlayerPredictedPos(bd.predictionX, bd.predictionY)
if pos then targetPos = pos end
elseif bd.mode == "NearestHumanoid" then
local pos,mdl = getNearestHumanoidPredictedPos(bd.predictionX, bd.predictionY)
if pos then targetPos = pos end
elseif bd.mode == "StraightForward" then
local studs = tonumber(bd.placeStuds) or tonumber(placeStudsBox.Text) or placement.studs or 6
local forward = getLocalForwardVector()
forward = Vector3.new(forward.X, 0, forward.Z)
if forward.Magnitude == 0 then
forward = Camera and Vector3.new(Camera.CFrame.LookVector.X, 0, Camera.CFrame.LookVector.Z) or Vector3.new(0,0,-1)
end
local base = getLocalReferencePos()
local pos = base + (forward.Unit * studs)
pos = Vector3.new(pos.X, base.Y, pos.Z)
targetPos = pos
elseif bd.mode == "CustomPlacement" then
-- IMPORTANT: prefer following the selected model's live position (do not use static block)
-- Use binding's stored placeLastPos if it exists, otherwise live selection from placement.selectedModel takes priority
local pos = nil
-- if there's a live selected model (user-selected during session), prefer that live model position
if placement.selectedModel then
local adornee = findModelAdornee(placement.selectedModel)
if adornee then
pos = adornee.Position
end
end
-- if no live selected model or not available, try stored binding last pos
if not pos and bd.placeLastPos and typeof(bd.placeLastPos) == "table" and #bd.placeLastPos == 3 then
pos = Vector3.new(bd.placeLastPos[1], bd.placeLastPos[2], bd.placeLastPos[3])
end
-- fallback to placement.lastPos if still no pos
if not pos and placement.lastPos then pos = placement.lastPos end
targetPos = pos
end
if targetPos then
if bd.argFormat == "Vector3" or bd.argFormat == "Vector3 " then
table.insert(args, targetPos)
else
table.insert(args, targetPos.X)
table.insert(args, targetPos.Y)
table.insert(args, targetPos.Z)
end
end
if bd.extraArgs and #bd.extraArgs > 0 then
for _,v in ipairs(bd.extraArgs) do table.insert(args, v) end
end
return args
end
local function fireBindingOnce(bd)
if not bd or not bd.remote then return false, "no binding" end
local args = buildArgsForBinding(bd)
local ok, err = pcall(function() return safeCallRemote(bd.remote, args, bd.argFormat == "XYZ") end)
if ok then return err else return false, err end
end
-- --------------------
-- Binding management (unchanged but extended to include place fields)
-- --------------------
local function removeBindingByKey(keyEnum)
if bindings[keyEnum] then
local kn = keyEnumToName(keyEnum)
if kn and settingsFolder:FindFirstChild("binding_"..kn) then settingsFolder:FindFirstChild("binding_"..kn):Destroy() end
local bd = bindings[keyEnum]
if bd and bd.active then bd.active = false end
bindings[keyEnum] = nil
end
end
local function removeBindingByRemote(remote)
if not remote then return end
local toRemove = {}
for k,bd in pairs(bindings) do
local rr = bd.remote
if typeof(rr) == "Instance" then
if rr == remote then table.insert(toRemove, k) end
elseif type(rr) == "table" then
for _,r in ipairs(rr) do
if r == remote then table.insert(toRemove, k); break end
end
end
end
for _,k in ipairs(toRemove) do
local kn = keyEnumToName(k)
if kn then deleteSavedBinding(kn) end
local bd = bindings[k]
if bd and bd.active then bd.active = false end
bindings[k] = nil
end
end
local function removeBindingByRemoteList(remoteList)
if not remoteList then return end
if typeof(remoteList) == "Instance" then
removeBindingByRemote(remoteList)
return
end
if type(remoteList) ~= "table" then return end
local seen = {}
for _,r in ipairs(remoteList) do
if not seen[r] then removeBindingByRemote(r); seen[r] = true end
end
end
local function setBindingForKey(keyEnum, bd)
if not keyEnum or not bd or not bd.remote then return false, "invalid args" end
removeBindingByKey(keyEnum)
removeBindingByRemoteList(bd.remote)
bindings[keyEnum] = {
remote = bd.remote,
extraArgs = bd.extraArgs or {},
hold = bd.hold and true or false,
interval = tonumber(bd.interval) or 0.12,
mode = bd.mode or "Mouse",
predictionX = tonumber(bd.predictionX) or 0,
predictionY = tonumber(bd.predictionY) or 0,
argFormat = bd.argFormat or "Vector3",
placeStuds = bd.placeStuds or nil,
placeLastPos = bd.placeLastPos or nil,
placeStick = bd.placeStick and true or false,
active = false,
remotePath = bd.remotePath or (pcall(function() return (typeof(bd.remote)=="Instance" and bd.remote:GetFullName()) end) and (typeof(bd.remote)=="Instance" and bd.remote:GetFullName())) or "",
remotePaths = bd.remotePaths or nil
}
return true
end
local function startBinding(keyEnum)
local bd = bindings[keyEnum]
if not bd or not bd.remote then return end
if bd.hold then
if bd.active then return end
bd.active = true
bd._co = coroutine.create(function()
while bd.active do
pcall(function() fireBindingOnce(bd) end)
task.wait(math.max(0.01, tonumber(bd.interval) or 0.12))
end
end)
coroutine.resume(bd._co)
else
bd.active = not bd.active
if bd.active then
bd._co = coroutine.create(function()
while bd.active do
pcall(function() fireBindingOnce(bd) end)
task.wait(math.max(0.01, tonumber(bd.interval) or 0.12))
end
end)
coroutine.resume(bd._co)
else
bd.active = false
end
end
end
local function stopBinding(keyEnum)
local bd = bindings[keyEnum]
if not bd then return end
if bd.hold then bd.active = false end
end
-- --------------------
-- Input handling & UI callbacks (add placement capture)
-- --------------------
local function clearSelectedBindingsAndSelection()
local remotesToClear = {}
for r,_ in pairs(selectedSet) do
if r then table.insert(remotesToClear, r) end
end
local cleared = 0
for _,r in ipairs(remotesToClear) do
pcall(function() removeBindingByRemote(r) end)
selectedSet[r] = nil
cleared = cleared + 1
end
selectedCountLabel.Text = "0"
assignedLabel.Text = "Assigned: <none>"
statusBar.Text = ("Cleared bindings for %d selected remotes."):format(cleared)
end
UserInputService.InputBegan:Connect(function(input, gp)
if input.UserInputType == Enum.UserInputType.Keyboard then
local code = input.KeyCode
if code then
if code == Enum.KeyCode.R then
pcall(function()
scanInventoryOnce()
statusBar.Text = "Reloaded (hotkey R)."
leftScroll.CanvasSize = UDim2.new(0,0,0, leftListLayout.AbsoluteContentSize.Y + 12)
local saved = loadSavedBindingsIntoTable()
for k, data in pairs(saved) do
pcall(function()
local ok, err = applySavedBinding(data)
if ok then pendingSaved[k] = nil end
end)
end
end)
return
end
if code == Enum.KeyCode.Delete then
clearSelectedBindingsAndSelection()
return
end
if bindings[code] then startBinding(code) end
end
end
end)
UserInputService.InputEnded:Connect(function(input, gp)
if input.UserInputType == Enum.UserInputType.Keyboard then
local code = input.KeyCode
if code and bindings[code] then stopBinding(code) end
end
end)
aimDropdown.MouseButton1Click:Connect(function()
local cur = aimDropdown.Text
if cur == "Mouse" then aimDropdown.Text = "NearestPlayer"
elseif cur == "NearestPlayer" then aimDropdown.Text = "NearestHumanoid"
elseif cur == "NearestHumanoid" then aimDropdown.Text = "StraightForward"
elseif cur == "StraightForward" then aimDropdown.Text = "CustomPlacement"
else aimDropdown.Text = "Mouse" end
_G.RKB_currentAimMode = aimDropdown.Text
pcall(function() updatePlacementVisual(_G.RKB_currentAimMode) end)
end)
formatDropdown.MouseButton1Click:Connect(function() formatDropdown.Text = (formatDropdown.Text == "Vector3") and "XYZ" or "Vector3" end)
holdToggle.MouseButton1Click:Connect(function()
if holdToggle.Text == "Hold mode" then
holdToggle.Text = "Toggle mode"
holdToggle.BackgroundColor3 = Color3.fromRGB(68,44,68)
else
holdToggle.Text = "Hold mode"
holdToggle.BackgroundColor3 = Color3.fromRGB(44,44,60)
end
end)
-- placement click capture flow (selection follows model)
local mouse = safeGetMouse()
local clickConn = nil
placeClickBtn.MouseButton1Click:Connect(function()
if not mouse then mouse = safeGetMouse() end
if not mouse then statusBar.Text = "Mouse not available"; return end
statusBar.Text = "Click somewhere in the world to place/select (Esc to cancel)."
placement.clickCapture = true
local function cancelCapture()
placement.clickCapture = false
statusBar.Text = "Placement canceled."
if clickConn then clickConn:Disconnect(); clickConn = nil end
end
local escConn
escConn = UserInputService.InputBegan:Connect(function(inp, gp)
if inp.UserInputType == Enum.UserInputType.Keyboard and inp.KeyCode == Enum.KeyCode.Escape then
cancelCapture()
escConn:Disconnect(); escConn = nil
end
end)
clickConn = mouse.Button1Down:Connect(function()
if not placement.clickCapture then return end
local target = mouse.Target
local hit = nil
pcall(function() hit = mouse.Hit.Position end)
if target and target.Parent and target.Parent:IsA("Model") then
local model = target.Parent
local hum = model:FindFirstChildOfClass("Humanoid")
if hum and model.Name ~= player.Name then
-- select the model (follow it directly)
if placement.selectedModel and placement.selectedModel ~= model then
clearSelectionBillboard(placement.selectedModel)
end
placement.selectedModel = model
placement.stickToEntity = stickToggle.Text == "Stick: On"
createSelectionBillboardForModel(model)
local adornee = findModelAdornee(model)
placement.lastPos = (adornee and adornee.Position) or placement.lastPos
placementSelectedLabel.Text = "Selected placement: "..tostring(model.Name)
statusBar.Text = "Selected entity: "..tostring(model.Name)
else
if hit then
placement.lastPos = hit
if placement.selectedModel then
clearSelectionBillboard(placement.selectedModel)
placement.selectedModel = nil
end
placementSelectedLabel.Text = "Selected placement: Custom position"
statusBar.Text = ("Placed at: %.1f, %.1f, %.1f"):format(hit.X, hit.Y, hit.Z)
end
end
else
if hit then
placement.lastPos = hit
if placement.selectedModel then
clearSelectionBillboard(placement.selectedModel)
placement.selectedModel = nil
end
placementSelectedLabel.Text = "Selected placement: Custom position"
statusBar.Text = ("Placed at: %.1f, %.1f, %.1f"):format(hit.X, hit.Y, hit.Z)
end
end
placement.clickCapture = false
if clickConn then clickConn:Disconnect(); clickConn = nil end
if escConn then escConn:Disconnect(); escConn = nil end
pcall(function() updatePlacementVisual(_G.RKB_currentAimMode) end)
end)
end)
stickToggle.MouseButton1Click:Connect(function()
if stickToggle.Text == "Stick: Off" then
stickToggle.Text = "Stick: On"
stickToggle.BackgroundColor3 = Color3.fromRGB(42,122,255)
placement.stickToEntity = true
else
stickToggle.Text = "Stick: Off"
stickToggle.BackgroundColor3 = Color3.fromRGB(44,44,60)
placement.stickToEntity = false
end
placementSelectedLabel.Text = placement.selectedModel and ("Selected placement: "..tostring(placement.selectedModel.Name)) or "Selected placement: <none>"
pcall(function() updatePlacementVisual(_G.RKB_currentAimMode) end)
end)
placeStudsBox:GetPropertyChangedSignal("Text"):Connect(function()
local v = tonumber(placeStudsBox.Text)
if v and v > 0 then placement.studs = v end
pcall(function() updatePlacementVisual(_G.RKB_currentAimMode) end)
end)
-- key capture flows (when binding, store placement fields)
local function endCapture()
capturingKey = false
setKeyBtn.Text = "Set Keybind (single)"
bindSelectedBtn.Text = "Bind Selected (multi)"
statusBar.Text = "Ready."
if capturingConn then capturingConn:Disconnect(); capturingConn = nil end
end
local function beginKeyCaptureForRemotes(remotesTable)
if capturingKey then return end
capturingKey = true
setKeyBtn.Text = "Press key (Esc cancel)"
bindSelectedBtn.Text = "Press key (Esc cancel)"
statusBar.Text = "Waiting for key..."
capturingConn = UserInputService.InputBegan:Connect(function(input, gp)
if not capturingKey then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
local code = input.KeyCode
if code == Enum.KeyCode.Escape then endCapture(); return end
local bd = {
remote = (type(remotesTable)=="table" and (#remotesTable==1 and remotesTable[1] or remotesTable)) or remotesTable,
extraArgs = parseArgsText(argsBox.Text),
hold = (holdToggle.Text == "Hold mode"),
interval = tonumber(intervalBox.Text) or 0.12,
mode = aimDropdown.Text,
predictionX = tonumber(predXBox.Text) or 0,
predictionY = tonumber(predYBox.Text) or 0,
argFormat = formatDropdown.Text,
remotePath = nil,
remotePaths = nil,
placeStuds = tonumber(placeStudsBox.Text) or placement.studs,
placeLastPos = (placement.lastPos and {placement.lastPos.X, placement.lastPos.Y, placement.lastPos.Z}) or nil,
placeStick = placement.stickToEntity
}
if type(bd.remote) == "Instance" then
local ok, fp = pcall(function() return bd.remote:GetFullName() end)
if ok and fp then bd.remotePath = fp end
elseif type(bd.remote) == "table" then
bd.remotePaths = {}
for _,r in ipairs(bd.remote) do
local ok, fp = pcall(function() return r:GetFullName() end)
if ok and fp then table.insert(bd.remotePaths, fp) end
end
bd.remotePath = bd.remotePaths[1]
end
setBindingForKey(code, bd)
deleteSavedBinding(keyEnumToName(code))
assignedLabel.Text = "Assigned: " .. keyEnumToName(code) .. " (remotes: " .. (type(bd.remote)=="table" and #bd.remote or 1) .. ")"
statusBar.Text = "Bound to key " .. keyEnumToName(code)
endCapture()
end
end)
end
setKeyBtn.MouseButton1Click:Connect(function()
if not selectedInfo or not selectedInfo.remote then statusBar.Text = "Select a remote first (single)"; return end
beginKeyCaptureForRemotes(selectedInfo.remote)
end)
bindSelectedBtn.MouseButton1Click:Connect(function()
local remotes = {}
for r,_ in pairs(selectedSet) do
if r and r.Parent then table.insert(remotes, r) end
end
if #remotes == 0 then statusBar.Text = "No remotes selected (toggle Multi-Select on, then click entries)"; return end
beginKeyCaptureForRemotes(remotes)
end)
clearMultiBindsBtn.MouseButton1Click:Connect(function()
clearSelectedBindingsAndSelection()
end)
-- multi-toggle behaviors
multiToggle.MouseButton1Click:Connect(function()
multiSelectMode = not multiSelectMode
if multiSelectMode then
multiToggle.Text = "Multi-Select: On"
statusBar.Text = "Multi-Select ON — click entries to add/remove them (blue = selected)."
for r,fr in pairs(entries) do
if selectedSet[r] then fr.BackgroundColor3 = SELECT_BG end
end
else
multiToggle.Text = "Multi-Select: Off"
statusBar.Text = "Multi-Select OFF — inspector mode."
for r,fr in pairs(entries) do
if selectedSet[r] then fr.BackgroundColor3 = NORMAL_BG end
end
end
end)
clearSelectionBtn.MouseButton1Click:Connect(function()
for r,fr in pairs(entries) do
if selectedSet[r] and fr and fr.Parent then fr.BackgroundColor3 = NORMAL_BG end
end
selectedSet = {}
selectedCountLabel.Text = "0"
statusBar.Text = "Selection cleared."
end)
-- Save/Update/Load/Fire/Clear UI handlers (behaviour kept but now store placement fields)
local function buildBindingDataFromUIForKey(keyName)
if not keyName then return nil, "no key" end
local data = {
key = keyName,
remotePath = (selectedInfo and pcall(function() return selectedInfo.remote:GetFullName() end) and selectedInfo.remote:GetFullName()) or "",
extraArgsText = argsBox.Text or "",
hold = (holdToggle.Text == "Hold mode"),
interval = tonumber(intervalBox.Text) or 0.12,
mode = aimDropdown.Text or "Mouse",
predictionX = tonumber(predXBox.Text) or 0,
predictionY = tonumber(predYBox.Text) or 0,
argFormat = formatDropdown.Text or "Vector3",
placeStuds = tonumber(placeStudsBox.Text) or placement.studs,
placeLastPos = (placement.lastPos and {placement.lastPos.X, placement.lastPos.Y, placement.lastPos.Z}) or nil,
placeStick = placement.stickToEntity
}
return data
end
function applySavedBinding(data)
if not data or not data.key then return false, "invalid data" end
local keyEnum = nameToKeyEnum(data.key)
if not keyEnum then return false, "invalid key name " .. tostring(data.key) end
local resolvedRemotes = {}
if data.remotePaths and type(data.remotePaths) == "table" then
for _,p in ipairs(data.remotePaths) do
local inst = findInstanceByFullName(p)
if inst then table.insert(resolvedRemotes, inst) end
end
elseif data.remotePath and data.remotePath ~= "" then
local inst = findInstanceByFullName(data.remotePath)
if inst then table.insert(resolvedRemotes, inst) end
end
if #resolvedRemotes == 0 then
if data.remotePath and data.remotePath ~= "" then
local short = data.remotePath:match("([^%.]+)$")
if short then
for _,inst in ipairs(backpack:GetDescendants()) do if isRemote(inst) and inst.Name == short then table.insert(resolvedRemotes, inst) end end
for _,inst in ipairs(character:GetDescendants()) do if isRemote(inst) and inst.Name == short then table.insert(resolvedRemotes, inst) end end
end
end
end
if #resolvedRemotes == 0 then
pendingSaved[data.key] = data
return false, "remote(s) not found"
end
local px = tonumber(data.predictionX); local py = tonumber(data.predictionY)
if px == nil and tonumber(data.prediction) then px = tonumber(data.prediction) end
if py == nil and tonumber(data.prediction) then py = tonumber(data.prediction) end
px = px or 0; py = py or 0
local bd = {
remote = (#resolvedRemotes==1) and resolvedRemotes[1] or resolvedRemotes,
extraArgs = parseArgsText(data.extraArgsText or ""),
hold = data.hold and true or false,
interval = tonumber(data.interval) or 0.12,
mode = data.mode or "Mouse",
predictionX = px,
predictionY = py,
argFormat = data.argFormat or "Vector3",
placeStuds = tonumber(data.placeStuds) or nil,
placeLastPos = data.placeLastPos or nil,
placeStick = data.placeStick and true or false,
remotePath = data.remotePath or "",
remotePaths = data.remotePaths or nil
}
setBindingForKey(keyEnum, bd)
return true
end
_G.applySavedBinding = applySavedBinding
saveBtn.MouseButton1Click:Connect(function()
if not selectedInfo or not selectedInfo.remote then statusBar.Text = "Select a remote first to Save"; return end
local boundKey = nil
for k,bd in pairs(bindings) do
if bd.remote and ((typeof(bd.remote)=="Instance" and bd.remote == selectedInfo.remote) or (type(bd.remote)=="table" and table.find(bd.remote, selectedInfo.remote))) then
boundKey = keyEnumToName(k); break
end
end
if not boundKey then statusBar.Text = "No keybind for selected remote. Use Set Keybind first."; return end
local data, err = buildBindingDataFromUIForKey(boundKey)
if not data then statusBar.Text = "Save error: "..tostring(err); return end
local ok, e = saveBindingToFolder(boundKey, data)
if ok then statusBar.Text = "Saved binding for key "..boundKey else statusBar.Text = "Save failed: "..tostring(e) end
end)
updateBtn.MouseButton1Click:Connect(function()
if not selectedInfo or not selectedInfo.remote then statusBar.Text = "Select a remote first."; return end
local boundKey = nil
for k,bd in pairs(bindings) do
if bd.remote and ((typeof(bd.remote)=="Instance" and bd.remote == selectedInfo.remote) or (type(bd.remote) == "table" and table.find(bd.remote, selectedInfo.remote))) then
boundKey = keyEnumToName(k); break
end
end
if not boundKey then statusBar.Text = "No keybind to update. Use Set Keybind first."; return end
local data, err = buildBindingDataFromUIForKey(boundKey)
if not data then statusBar.Text = "Update error: "..tostring(err); return end
local ok, e = saveBindingToFolder(boundKey, data)
if ok then statusBar.Text = "Updated saved binding for "..boundKey else statusBar.Text = "Update failed: "..tostring(e) end
end)
loadBtn.MouseButton1Click:Connect(function()
statusBar.Text = "Loading saved bindings..."
local saved = loadSavedBindingsIntoTable()
pendingSaved = {}
local applied = 0
for keyName, data in pairs(saved) do
local ok, err = applySavedBinding(data)
if ok then applied = applied + 1 else pendingSaved[keyName] = data end
end
statusBar.Text = ("Loaded %d saved. Pending: %d"):format(applied, (function() local c=0; for _ in pairs(pendingSaved) do c=c+1 end; return c end)())
leftScroll.CanvasSize = UDim2.new(0,0,0, leftListLayout.AbsoluteContentSize.Y + 12)
end)
fireBtn.MouseButton1Click:Connect(function()
if not selectedInfo or not selectedInfo.remote then statusBar.Text = "Select a remote first."; return end
local bd = {
remote = selectedInfo.remote,
extraArgs = parseArgsText(argsBox.Text),
mode = aimDropdown.Text,
predictionX = tonumber(predXBox.Text) or 0,
predictionY = tonumber(predYBox.Text) or 0,
argFormat = formatDropdown.Text,
placeStuds = tonumber(placeStudsBox.Text) or placement.studs,
placeLastPos = (placement.lastPos and {placement.lastPos.X, placement.lastPos.Y, placement.lastPos.Z}) or nil,
placeStick = placement.stickToEntity
}
local ok, err = safeCallRemote(bd.remote, buildArgsForBinding(bd), bd.argFormat == "XYZ")
if ok then statusBar.Text = "Test fire success." else statusBar.Text = "Test fire failed: "..tostring(err) end
end)
local function clearBindingsForSelected()
if not selectedInfo or not selectedInfo.remote then statusBar.Text = "Select a remote first."; return end
local removed = false
local toDelete = {}
for k,bd in pairs(bindings) do
local rr = bd.remote
if typeof(rr) == "Instance" then
if rr == selectedInfo.remote then table.insert(toDelete, k); removed = true end
else
for _,r in ipairs(rr or {}) do
if r == selectedInfo.remote then table.insert(toDelete, k); removed = true; break end
end
end
end
for _,k in ipairs(toDelete) do
local kn = keyEnumToName(k)
if kn then deleteSavedBinding(kn) end
bindings[k] = nil
end
if removed then assignedLabel.Text = "Assigned: <none>"; statusBar.Text = "Cleared binding(s) for selected remote." else statusBar.Text = "No binding found for selected remote." end
end
local clearBindBtn = Instance.new("TextButton", rightPane)
clearBindBtn.Position = UDim2.new(0,324,1,-84)
clearBindBtn.Size = UDim2.new(0,86,0,28)
clearBindBtn.Font = Enum.Font.Gotham
clearBindBtn.Text = "Clear Bind"
clearBindBtn.BackgroundColor3 = Color3.fromRGB(92,36,36)
clearBindBtn.TextColor3 = Color3.fromRGB(240,240,240)
clearBindBtn.BorderSizePixel = 0
clearBindBtn.TextSize = 12
clearBindBtn.MouseButton1Click:Connect(clearBindingsForSelected)
reloadBtn.MouseButton1Click:Connect(function()
pcall(function()
scanInventoryOnce()
statusBar.Text = "Remotes reloaded."
leftScroll.CanvasSize = UDim2.new(0,0,0, leftListLayout.AbsoluteContentSize.Y + 12)
local saved = loadSavedBindingsIntoTable()
for k,data in pairs(saved) do
local ok, err = applySavedBinding(data)
if ok then pendingSaved[k] = nil end
end
end)
end)
local function resetToolsNow()
statusBar.Text = "Resetting tools..."
pcall(function()
character = player.Character or player.CharacterAdded:Wait()
for _,obj in ipairs(character:GetChildren()) do
if obj:IsA("Tool") then
pcall(function() obj.Parent = backpack end)
task.wait(0.06)
pcall(function() obj.Parent = character end)
task.wait(0.06)
end
end
for _,obj in ipairs(backpack:GetChildren()) do
if obj:IsA("Tool") then
pcall(function() obj.Parent = character end)
task.wait(0.03)
pcall(function() obj.Parent = backpack end)
end
end
scanInventoryOnce()
leftScroll.CanvasSize = UDim2.new(0,0,0, leftListLayout.AbsoluteContentSize.Y + 12)
end)
statusBar.Text = "Reset tools attempted; reloaded inventory."
end
resetToolsBtn.MouseButton1Click:Connect(resetToolsNow)
-- --------------------
-- Periodic scan start & load saved
-- --------------------
task.spawn(function()
while true do
pcall(function()
scanInventoryOnce()
for r,fr in pairs(entries) do
if fr and fr.Parent then
local lbl = fr:FindFirstChildOfClass("TextLabel")
if lbl then
local ok, full = pcall(function() return r:GetFullName() end)
lbl.Text = ok and full or tostring(r.Name)
end
end
end
leftScroll.CanvasSize = UDim2.new(0,0,0, leftListLayout.AbsoluteContentSize.Y + 12)
end)
task.wait(1.0)
end
end)
task.spawn(function()
task.wait(0.6)
local saved = loadSavedBindingsIntoTable()
for k,data in pairs(saved) do
local ok, err = applySavedBinding(data)
if not ok then pendingSaved[k] = data end
end
statusBar.Text = "Ready — Multi-Select button under list. Toggle it, click entries to select."
end)
-- --------------------
-- Respawn handling & cleanup (keep visuals following new character)
-- --------------------
player.CharacterAdded:Connect(function(char)
character = char
task.delay(0.25, function()
pcall(function()
backpack = player:FindFirstChild("Backpack") or backpack
refreshHumanoidCache()
scanInventoryOnce()
local saved = loadSavedBindingsIntoTable()
for k,data in pairs(saved) do
local ok, err = applySavedBinding(data)
if not ok then pendingSaved[k] = data end
end
leftScroll.CanvasSize = UDim2.new(0,0,0, leftListLayout.AbsoluteContentSize.Y + 12)
statusBar.Text = "Respawned — reloaded inventory & saved bindings."
end)
end)
task.delay(0.2, function() pcall(function() updatePlacementVisual(_G.RKB_currentAimMode) end) end)
end)
player.AncestryChanged:Connect(function(_, parent)
if not parent then
pcall(destroyPlacementVisual)
end
end)
-- debug helpers
_G.RKB_getBindings = function() return bindings end
_G.RKB_reload = function() scanInventoryOnce(); statusBar.Text = "Manual reload (exposed)"; end
_G.RKB_resetTools = resetToolsNow
-- end of script![[Open Source & Universal] Avatar Changer, Saver, Stealer](https://scriptblox.com/images/sb/0-1790383978808.png)



comment if there something that needs to be fix
@reverb0012 change the ui
can i get reviews if this script is good also it will prob suppport
My problem is that I can't close the cheat menu.