
KeylessUniversal

--[[
NPC Multi-Selection Control System - Fixed Display + Auto Network Ownership Selection Enhanced
Author: @im_patrick / Enhanced: Auto-select NPCs with network ownership
Function: Control multiple NPCs with various follow shapes, fixed display + auto network owner selection
]]
if patricknpcpanel then return end
patricknpcpanel = true
local github = "https://raw.githubusercontent.com/randomstring0/fe-source/refs/heads/main/NPC"
local load = loadstring(game:HttpGet(github .. "/module.Luau"))()
saved = loadstring(game:HttpGet(github .. "/table.luau"))()
local save = saved or {}
local g2l = load.G2L
local new = load.create()
local tweenservice = game:GetService("TweenService")
local rs = game:GetService("RunService")
local ws = game:GetService("Workspace")
local plrs = game:GetService("Players")
local lp = plrs.LocalPlayer
local mouse = lp:GetMouse()
local medium = TweenInfo.new(.67)
local rad = 150
local currentnpc
local highlight = Instance.new("Highlight")
highlight.Parent = lp
highlight.FillTransparency = 1
highlight.OutlineTransparency = 1
local light = function(adornee, color)
task.spawn(function()
highlight.Adornee = adornee
highlight.OutlineColor = color
tweenservice:Create(highlight, medium, {OutlineTransparency = 0}):Play()
task.wait(.5)
tweenservice:Create(highlight, medium, {OutlineTransparency = 1}):Play()
end)
end
local isnpc = function(ins)
local humanoid = ins:FindFirstChildOfClass("Humanoid")
local player = plrs:GetPlayerFromCharacter(ins)
if humanoid and not player then
return ins
end
return nil
end
-- ================ Fixed Multi-Selection System ================
local selectedNPCs = {} -- Stores selected NPCs
local maxSelected = 10
local multiSelectMode = false
-- Follow system variables
local isFollowingAll = false
local followAllConnection = nil
local currentFollowShape = "Circle" -- Default follow shape
local followDistance = 5 -- Default follow distance
-- New: Auto select NPCs with network ownership
local autoSelectNetworkOwner = false
-- Follow shape options
local followShapes = {
"Circle", -- 1
"Square", -- 2
"Line", -- 3
"Triangle", -- 4
"DoubleCircle",-- 5
"Front", -- 6
"Back" -- 7
}
-- Create current shape display button
local shapeDisplayButton = new:mainbutton("Current Shape: Circle", "Currently used follow formation", function()
end)
-- Check network ownership (original logic)
local function partowner(part)
if not part then return false end
if part:IsDescendantOf(workspace) then
local model = part:FindFirstAncestorOfClass("Model")
if model then
local player = plrs:GetPlayerFromCharacter(model)
return not player
end
end
return false
end
-- New: Check if model has network ownership
local function isMyNetworkOwner(model)
if not model or not model:IsA("Model") then return false end
local hrp = model:FindFirstChild("HumanoidRootPart")
if not hrp then return false end
return partowner(hrp)
end
local function clearAllSelections()
selectedNPCs = {}
currentnpc = nil
end
local function getSelectedCount()
local count = 0
for _ in pairs(selectedNPCs) do
count = count + 1
end
return count
end
local function applyToSelectedNPCs(callback)
local count = 0
for npc, _ in pairs(selectedNPCs) do
if npc and npc.Parent and npc:FindFirstChild("HumanoidRootPart") then
local hrp = npc:FindFirstChild("HumanoidRootPart")
if hrp and partowner(hrp) then
local success, err = pcall(callback, npc)
if success then
count = count + 1
end
end
else
selectedNPCs[npc] = nil
end
end
return count
end
-- ================ Multi-Select Mode Toggle ================
new:maintoggle("Multi-Select Mode", "Allows selecting multiple NPCs at once", function(state)
multiSelectMode = state
if state then
print("Multi-select mode enabled")
else
print("Multi-select mode disabled")
if getSelectedCount() > 1 then
for npc, _ in pairs(selectedNPCs) do
if npc ~= currentnpc then
selectedNPCs[npc] = nil
end
end
end
end
end)
-- ================ New: Auto Select Network Owner NPCs ================
new:maintoggle("Auto Select Network NPCs", "Automatically select all NPCs you own network control over", function(state)
autoSelectNetworkOwner = state
if state then
print("Enabled: Auto select NPCs with network ownership")
else
print("Disabled: Auto select network NPCs")
end
end)
-- ================ New: One-Click Select All Network NPCs ================
new:mainbutton("Select All Network NPCs", "Select all NPCs you have control over", function()
clearAllSelections()
local count = 0
for _, model in ipairs(ws:GetDescendants()) do
if model:IsA("Model") and isnpc(model) then
if isMyNetworkOwner(model) then
if count >= maxSelected then break end
selectedNPCs[model] = true
currentnpc = model
count += 1
end
end
end
if count > 0 then
light(lp.Character, Color3.new(0,1,0))
print("Selected "..count.." NPCs with network ownership")
else
light(lp.Character, Color3.new(1,0,0))
print("No controllable NPCs found")
end
end)
-- ================ Fixed Mouse Click Event ================
mouse.Button1Down:Connect(function()
if not mouse.Target then return end
local targetParent = mouse.Target.Parent
if not targetParent then return end
local hrp = targetParent:FindFirstChild("HumanoidRootPart")
if not hrp then return end
if hrp.Anchored == true then
light(targetParent, Color3.fromRGB(255, 0, 0))
return
end
if plrs:GetPlayerFromCharacter(targetParent) then
return
end
local humanoid = targetParent:FindFirstChildOfClass("Humanoid")
if not humanoid then return end
if partowner(hrp) then
currentnpc = targetParent
if multiSelectMode then
if selectedNPCs[targetParent] then
selectedNPCs[targetParent] = nil
light(targetParent, Color3.fromRGB(255, 165, 0))
else
if getSelectedCount() < maxSelected then
selectedNPCs[targetParent] = true
light(targetParent, Color3.fromRGB(0, 255, 0))
else
light(targetParent, Color3.fromRGB(255, 0, 0))
end
end
else
clearAllSelections()
selectedNPCs[targetParent] = true
light(targetParent, Color3.fromRGB(0, 255, 0))
end
else
light(targetParent, Color3.fromRGB(255, 0, 0))
end
end)
-- ================ Follow Shape Calculation Function ================
local function calculateShapePosition(playerPos, playerLookVector, index, total, shape, distance)
local offset = Vector3.new(0, 0, 0)
local spacing = 2
if shape == "Circle" then
local angle = (index / total) * math.pi * 2
offset = Vector3.new(
math.cos(angle) * distance,
0,
math.sin(angle) * distance
)
elseif shape == "Square" then
local sideLength = math.ceil(total / 4)
local sideIndex = math.floor(index / sideLength)
local posInSide = index % sideLength
local halfLength = (sideLength - 1) * spacing / 2
if sideIndex == 0 then
offset = Vector3.new(-halfLength + posInSide * spacing, 0, distance)
elseif sideIndex == 1 then
offset = Vector3.new(distance, 0, halfLength - posInSide * spacing)
elseif sideIndex == 2 then
offset = Vector3.new(halfLength - posInSide * spacing, 0, -distance)
else
offset = Vector3.new(-distance, 0, -halfLength + posInSide * spacing)
end
elseif shape == "Line" then
local startOffset = -playerLookVector * distance
local sideOffset = Vector3.new(0, 0, 0)
if total > 1 then
sideOffset = Vector3.new((index - (total-1)/2) * spacing, 0, 0)
end
offset = startOffset + sideOffset
elseif shape == "Triangle" then
local rows = 0
local rowCount = 0
while rowCount < total do
rows = rows + 1
rowCount = rowCount + rows
end
local currentRow = 1
local countInRow = 0
local cumulative = 0
for r = 1, rows do
if index < cumulative + r then
currentRow = r
countInRow = index - cumulative
break
end
cumulative = cumulative + r
end
local rowSpacing = spacing * 1.5
local colOffset = (countInRow - (currentRow-1)/2) * spacing
local rowOffset = -(currentRow-1) * rowSpacing
offset = Vector3.new(colOffset, 0, rowOffset + distance)
elseif shape == "DoubleCircle" then
local innerCount = math.ceil(total / 2)
local outerCount = total - innerCount
if index < innerCount then
local angle = (index / innerCount) * math.pi * 2
offset = Vector3.new(
math.cos(angle) * (distance * 0.5),
0,
math.sin(angle) * (distance * 0.5)
)
else
local outerIndex = index - innerCount
local angle = (outerIndex / outerCount) * math.pi * 2
offset = Vector3.new(
math.cos(angle) * distance,
0,
math.sin(angle) * distance
)
end
elseif shape == "Front" then
local forwardOffset = playerLookVector * distance
local sideOffset = Vector3.new(0, 0, 0)
if total > 1 then
sideOffset = Vector3.new((index - (total-1)/2) * spacing, 0, 0)
end
offset = forwardOffset + sideOffset
elseif shape == "Back" then
local backwardOffset = -playerLookVector * distance
local sideOffset = Vector3.new(0, 0, 0)
if total > 1 then
sideOffset = Vector3.new((index - (total-1)/2) * spacing, 0, 0)
end
offset = backwardOffset + sideOffset
end
return playerPos + offset
end
-- ================ Advanced Follow System ================
local function startFollowingAll()
if followAllConnection then
followAllConnection:Disconnect()
end
followAllConnection = rs.RenderStepped:Connect(function()
local playerChar = lp.Character
if not playerChar then return end
local playerHrp = playerChar:FindFirstChild("HumanoidRootPart")
if not playerHrp then return end
local selectedCount = getSelectedCount()
if selectedCount == 0 then return end
local playerPos = playerHrp.Position
local playerLookVector = playerHrp.CFrame.LookVector
local rightVector = playerHrp.CFrame.RightVector
local upVector = playerHrp.CFrame.UpVector
local index = 0
for npc, _ in pairs(selectedNPCs) do
if npc and npc.Parent and npc:FindFirstChild("HumanoidRootPart") then
local hrp = npc:FindFirstChild("HumanoidRootPart")
if hrp and partowner(hrp) then
local hum = npc:FindFirstChildOfClass("Humanoid")
if hum then
local basePos = calculateShapePosition(
Vector3.new(0, 0, 0),
Vector3.new(0, 0, -1),
index,
selectedCount,
currentFollowShape,
followDistance
)
local rotatedPos = playerPos +
(rightVector * basePos.X) +
(upVector * basePos.Y) +
(playerLookVector * basePos.Z)
hum:MoveTo(rotatedPos)
index = index + 1
end
end
end
end
end)
isFollowingAll = true
end
local function stopFollowingAll()
if followAllConnection then
followAllConnection:Disconnect()
followAllConnection = nil
end
isFollowingAll = false
end
-- ================ Follow Shape Switch Button ================
local shapeIndex = 1
new:mainbutton("Switch Follow Shape", "Cycle through different follow formations", function()
shapeIndex = shapeIndex + 1
if shapeIndex > #followShapes then
shapeIndex = 1
end
currentFollowShape = followShapes[shapeIndex]
print("Follow shape changed to: " .. currentFollowShape)
if shapeDisplayButton then
pcall(function()
shapeDisplayButton:SetText("Current Shape: " .. currentFollowShape)
end)
end
if isFollowingAll then
stopFollowingAll()
startFollowingAll()
end
end)
local distanceDisplayButton = new:mainbutton("Follow Distance: " .. followDistance, "Adjust distance between NPCs and player", function() end)
new:mainbutton("Increase Follow Distance", "Increase distance between NPCs and player", function()
followDistance = followDistance + 1
if followDistance > 20 then followDistance = 20 end
print("Follow distance: " .. followDistance)
pcall(function()
distanceDisplayButton:SetText("Follow Distance: " .. followDistance)
end)
if isFollowingAll then
stopFollowingAll()
startFollowingAll()
end
end)
new:mainbutton("Decrease Follow Distance", "Decrease distance between NPCs and player", function()
followDistance = followDistance - 1
if followDistance < 2 then followDistance = 2 end
print("Follow distance: " .. followDistance)
pcall(function()
distanceDisplayButton:SetText("Follow Distance: " .. followDistance)
end)
if isFollowingAll then
stopFollowingAll()
startFollowingAll()
end
end)
-- ================ Fixed Function Buttons ================
new:mainbutton(save["1"].title, save["1"].des, function()
local selectedCount = getSelectedCount()
if selectedCount > 0 then
local count = applyToSelectedNPCs(function(npc)
local hum = npc:FindFirstChildOfClass("Humanoid")
if hum then
hum:ChangeState(save["1"].val)
end
end)
if count > 0 then
light(lp.Character, Color3.fromRGB(0, 255, 0))
end
elseif currentnpc then
local part = currentnpc:FindFirstChild("HumanoidRootPart")
if part and partowner(part) then
local hum = currentnpc:FindFirstChildOfClass("Humanoid")
if hum then
hum:ChangeState(save["1"].val)
end
else
light(currentnpc, Color3.fromRGB(255, 0, 0))
end
end
end)
new:mainbutton(save["2"].title, save["2"].des, function()
local selectedCount = getSelectedCount()
if selectedCount > 0 then
if lp and lp.Character then
local char = lp.Character
local charPos = char:GetPivot()
local count = 0
local radius = 3
applyToSelectedNPCs(function(npc)
local angle = (count / selectedCount) * math.pi * 2
local offset = Vector3.new(
math.cos(angle) * radius,
0,
math.sin(angle) * radius
)
local newCFrame = charPos + offset
npc:PivotTo(CFrame.new(newCFrame))
count = count + 1
end)
light(lp.Character, Color3.fromRGB(0, 255, 0))
end
elseif currentnpc then
local part = currentnpc:FindFirstChild("HumanoidRootPart")
if part and partowner(part) then
if lp and lp.Character then
local char = lp.Character
currentnpc:PivotTo(char:GetPivot())
end
else
light(currentnpc, Color3.fromRGB(255, 0, 0))
end
end
end)
new:mainbutton(save["3"].title, save["3"].des, function()
if currentnpc then
local part = currentnpc:FindFirstChild("HumanoidRootPart")
if part then
if lp and lp.Character then
local char = lp.Character
char:PivotTo(currentnpc:GetPivot())
end
else
light(currentnpc, Color3.fromRGB(255, 0, 0))
end
end
end)
local chr, cons
new:maintoggle(save["4"].title, save["4"].des, function(a)
if a then
if currentnpc then
local part = currentnpc:FindFirstChild("HumanoidRootPart")
if part and partowner(part) then
if lp and lp.Character then
chr = lp.Character
lp.Character = currentnpc
ws.CurrentCamera.CameraSubject = currentnpc:FindFirstChild("HumanoidRootPart")
local move = 0.01
cons = rs.PreSimulation:Connect(function()
local hum = lp.Character:FindFirstChildOfClass("Humanoid")
if lp.Character and hum then
hum.RootPart.CFrame += Vector3.new(0,move,0)
move = -move
else
if cons then
cons:Disconnect()
cons = nil
end
end
end)
end
else
light(currentnpc, Color3.fromRGB(255, 0, 0))
end
end
else
if chr then
lp.Character = chr
ws.CurrentCamera.CameraSubject = chr.Humanoid
chr = nil
if cons then
cons:Disconnect()
cons = nil
end
end
end
end)
new:mainbutton(save["5"].title, save["5"].des, function()
local selectedCount = getSelectedCount()
if selectedCount > 0 then
local count = applyToSelectedNPCs(function(npc)
npc:PivotTo(CFrame.new(0, 1000, 0))
end)
if count > 0 then
light(lp.Character, Color3.fromRGB(0, 255, 0))
end
elseif currentnpc then
local part = currentnpc:FindFirstChild("HumanoidRootPart")
if part and partowner(part) then
if lp and lp.Character then
currentnpc:PivotTo(CFrame.new(0, 1000, 0))
end
else
light(currentnpc, Color3.fromRGB(255, 0, 0))
end
end
end)
new:mainbutton(save["6"].title, save["6"].des, function()
local selectedCount = getSelectedCount()
if selectedCount > 0 then
local count = applyToSelectedNPCs(function(npc)
local hum = npc:FindFirstChildOfClass("Humanoid")
if hum then
hum.Sit = not hum.Sit
end
end)
if count > 0 then
light(lp.Character, Color3.fromRGB(0, 255, 0))
end
elseif currentnpc then
local part = currentnpc:FindFirstChild("HumanoidRootPart")
if part and partowner(part) then
local hum = currentnpc:FindFirstChildOfClass("Humanoid")
if hum then
hum.Sit = not hum.Sit
end
else
light(currentnpc, Color3.fromRGB(255, 0, 0))
end
end
end)
new:mainbutton(save["7"].title, save["7"].des, function()
local selectedCount = getSelectedCount()
if selectedCount > 0 then
local count = applyToSelectedNPCs(function(npc)
local hum = npc:FindFirstChildOfClass("Humanoid")
if hum then
hum:ChangeState(save["7"].val)
end
end)
if count > 0 then
light(lp.Character, Color3.fromRGB(0, 255, 0))
end
elseif currentnpc then
local part = currentnpc:FindFirstChild("HumanoidRootPart")
if part and partowner(part) then
local hum = currentnpc:FindFirstChildOfClass("Humanoid")
if hum then
hum:ChangeState(save["7"].val)
end
else
light(currentnpc, Color3.fromRGB(255, 0, 0))
end
end
end)
-- ================ Advanced Follow Toggle ================
new:maintoggle("Advanced Follow", "Enable/disable advanced follow mode", function(state)
if state then
if getSelectedCount() > 0 then
startFollowingAll()
light(lp.Character, Color3.fromRGB(0, 255, 0))
else
light(lp.Character, Color3.fromRGB(255, 0, 0))
end
else
stopFollowingAll()
end
end)
-- ================ Selection Management Buttons ================
new:mainbutton("Select Nearby NPCs", "Select all NPCs within 30m radius", function()
local character = lp.Character
if not character then return end
local hrp = character:FindFirstChild("HumanoidRootPart")
if not hrp then return end
clearAllSelections()
local foundNPCs = {}
local radius = 30
local success, parts = pcall(function()
return ws:GetPartBoundsInRadius(hrp.Position, radius)
end)
if not success then return end
for _, part in ipairs(parts) do
local model = part:FindFirstAncestorOfClass("Model")
if model and isnpc(model) then
local npcHrp = model:FindFirstChild("HumanoidRootPart")
if npcHrp and not npcHrp.Anchored and partowner(npcHrp) then
table.insert(foundNPCs, model)
end
end
end
local selectedCount = 0
for i = 1, math.min(#foundNPCs, maxSelected) do
selectedNPCs[foundNPCs[i]] = true
currentnpc = foundNPCs[i]
selectedCount = selectedCount + 1
end
if selectedCount > 0 then
light(hrp, Color3.fromRGB(0, 255, 0))
end
end)
new:mainbutton("Select All NPCs", "Select all NPCs in the scene", function()
clearAllSelections()
local selectedCount = 0
for _, model in ipairs(workspace:GetChildren()) do
if model:IsA("Model") and isnpc(model) then
local npcHrp = model:FindFirstChild("HumanoidRootPart")
if npcHrp and not npcHrp.Anchored and partowner(npcHrp) then
if selectedCount < maxSelected then
selectedNPCs[model] = true
currentnpc = model
selectedCount = selectedCount + 1
else
break
end
end
end
end
if selectedCount > 0 then
light(lp.Character, Color3.fromRGB(0, 255, 0))
end
end)
new:mainbutton("Clear Selection", "Deselect all selected NPCs", function()
clearAllSelections()
stopFollowingAll()
light(lp.Character, Color3.fromRGB(255, 165, 0))
end)
-- ================ Auto Network NPC Selection Main Loop ================
rs.RenderStepped:Connect(function()
-- Clean up invalid NPCs
for npc, _ in pairs(selectedNPCs) do
if not npc or not npc.Parent or not npc:FindFirstChild("HumanoidRootPart") then
selectedNPCs[npc] = nil
end
end
-- Auto select NPCs with network ownership
if autoSelectNetworkOwner then
for _, model in ipairs(ws:GetDescendants()) do
if model:IsA("Model") and isnpc(model) then
if getSelectedCount() >= maxSelected then break end
if isMyNetworkOwner(model) and not selectedNPCs[model] then
selectedNPCs[model] = true
currentnpc = model
end
end
end
end
-- Simulation radius
if sethiddenproperty then
sethiddenproperty(lp,"SimulationRadius",rad)
else
lp.SimulationRadius=rad
end
end)
lp.CharacterAdded:Connect(function()
task.wait(1)
highlight.Parent = lp
clearAllSelections()
stopFollowingAll()
end)
lp.CharacterRemoving:Connect(function()
clearAllSelections()
stopFollowingAll()
end)
return g2l, require



Um it won't let me move them
@demarlowoods41 That game doesn't support this. You can try something else.
Nvm it worked but I have to have network ownership Amazing script btw