--[[ 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