NPC gangsters Control multiple NPC
Key RequiredUniversal

NPC gangsters Control multiple NPC

Universal ScriptggggGTIffggggGTIff 710 views 162 executesUpdated Apr 10, 2026
2.5/ 5(2 votes)
1150% Positive
Lua Script•763 lines (24809 chars)
--[[
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

Features & Information

This is an open source, because someone doesn't know how to make this. You can control NPCs to do anything with you. If you can write your own code, you might be able to get them to attack. But that's all I can do. I can't understand why other people would change this script just a little bit to control more NPCs just by adding a few ports. Of course, I can only optimize here, because. It's too much trouble to write code.
#NPC

Community Comments (3)

TBO_HUBTBO_HUBApr 11, 2026

Um it won't let me move them

Replying to @TBO_HUB
ggggGTIffggggGTIffApr 11, 2026

@demarlowoods41 That game doesn't support this. You can try something else.

TBO_HUBTBO_HUBApr 12, 2026

Nvm it worked but I have to have network ownership Amazing script btw

More Scripts for Universal Scripts