I want it to remove the number it chooses and continue looping through the new table (without the previously chosen number)
Spawner.SpawnBrainrot = function()
if #unoccupiedSpaces == 0 then
for i, spawn in pairs(Spawns:GetChildren()) do
table.insert(unoccupiedSpaces, spawn)
table.sort(unoccupiedSpaces, function(a,b)
return a.Name < b.Name
end)
end
end
local choiceIndex = math.random(0, #unoccupiedSpaces)
local chosenSpawn = unoccupiedSpaces[choiceIndex]
local Brainrot = Spawner.GetRandomBrainrot()
print(chosenSpawn)
print(unoccupiedSpaces)
print("Looping through and got spawn "..tostring(chosenSpawn))
--print("Selected brainrot is: "..tostring(Brainrot))
--print("Selected spawn is: "..tostring(chosenSpawn))
if currentBrainrots < maxBrainrots then
if table.find(occupiedSpaces, chosenSpawn) then
return
elseif table.find(occupiedSpaces, chosenSpawn) and table.find(unoccupiedSpaces, chosenSpawn) then
table.remove(unoccupiedSpaces, chosenSpawn)
else
Events.BrainrotsInW:InvokeServer()
local selectedSpawn = table.insert(occupiedSpaces, chosenSpawn)
table.remove(unoccupiedSpaces, selectedSpawn)
local BrainrotClone = Brainrots:FindFirstChild(Brainrot):Clone()
BrainrotClone.Parent = Spawned
BrainrotClone:SetPrimaryPartCFrame(chosenSpawn.CFrame)
--print("Cloned")
end
end
end
local referenceSpawns = Spawns:GetChildren()
local availableSpawns = {}
if #availableSpawns == 0 then
availableSpawns = table.clone(referenceSpawns)
end
local spawnIndex = math.random(#availableSpawns)
local spawn = table.remove(availableSpawns, spawnIndex)
-- Spawn NPC
It still checks loops through already occupied spaces, but it did kind of fix something. Maybe its a problem with my npc spawning code.
This is the whole code I used:
Spawner.SpawnBrainrot = function()
if #availableSpawns == 0 then
availableSpawns = table.clone(referenceSpawns)
end
local spawnIndex = math.random(#availableSpawns)
local spawn = table.remove(availableSpawns, spawnIndex)
local Brainrot = Spawner.GetRandomBrainrot()
print(spawnIndex)
print("Looping through and got spawn "..tostring(spawnIndex))
if currentBrainrots < maxBrainrots then
Events.BrainrotsInW:InvokeServer()
local BrainrotClone = Brainrots:FindFirstChild(Brainrot):Clone()
BrainrotClone.Parent = Spawned
BrainrotClone:SetPrimaryPartCFrame(referenceSpawns[spawnIndex].CFrame)
--print("Cloned")
end
end
You’re only checking if an NPC can be spawned after you’ve allocated a spawn point. This is irreversible until the available spawns are exhausted. Convert your primary condition to a guard clause and hoist it to the top of your function:
if currentBrainrots == maxBrainrots then
return
end
-- Spawn brainrot
I did this but it still selects the same spawn multiple times.
I want it to do for example:
Remove spawn 6 from the table
Loop through the new table without spawn 6
Then continue doing this until all spawns are exhausted and once a spawn is free again, respawn the npc
spawnIndex will always be a value between 1 and the length of availableSpawns. As we randomly select a spawn in this range, we remove that spawn from availableSpawns, making it impossible for the same spawn to be selected again. The chosen spawn is no longer present in the array, but that shouldn’t discount the remainder of the array’s contents. Seeing repeating spawn indices is not an indication of duplicate spawn selection.
To see what spawn was actually selected, print the name of spawn
Lagging behind
If you are seeing repeating spawns, the issue is no longer with the selection algorithm, but with the contents of referenceSpawns. The only way the algorithm permits reuse of previously selected spawns is if no more unique spawns are available.
If the set of repeating spawns is deterministic and small, then referenceSpawns is itself small. This happens if collection of the spawns in Workspace failed to complete, which is most often due to client latency as the spawns replicate from the server—I see your console logs are client-sided.
To resolve that issue, account for incomplete instance presence at runtime:
local referenceSpawns = {}
local availableSpawns = {}
local function onSpawnAdded(spawn: BasePart)
table.insert(referenceSpawns, spawn)
table.insert(availableSpawns, spawn)
end
for _, spawn in Spawns:GetChildren() do
onSpawnAdded(spawn)
end
Spawns.ChildAdded:Connect(onSpawnAdded)
Spawner.SpawnBrainrot = function()
if currentBrainrots == maxBrainrots then return end
if #availableSpawns == 0 then
availableSpawns = table.clone(referenceSpawns)
end
local spawnIndex = math.random(#availableSpawns)
local spawn = table.remove(availableSpawns, spawnIndex)
local Brainrot = Spawner.GetRandomBrainrot()
if currentBrainrots < maxBrainrots then
print("Spawning "..tostring(Brainrot).." into spawn "..tostring(spawn))
table.sort(availableSpawns, function(a, b)
return a.Name < b.Name
end)
Events.BrainrotsInW:InvokeServer()
local BrainrotClone = Brainrots:FindFirstChild(Brainrot):Clone()
BrainrotClone.Parent = Spawned
--BrainrotClone:SetPrimaryPartCFrame(availableSpawns[spawnIndex].CFrame)
print(availableSpawns)
end
end
The code stops reselecting spawns twice. but only after I added the table.sort function. The only problem now is that when it spawns an npc, it first spawns in the right spawn, but when another one gets selected, the new npc sometimes spawns in the same spawn as the first npc.
As an example: Let’s say first npc spawns in selectedSpawn 1, the second npc is set to spawn in the selectedSpawn 6. but its position is set to selectedSpawn 1.
I think the issue is where you actually set the spawn of the NPC? You are trying to index the table that you already modified by removing the spawn location, instead you should use the actual spawn variable you created.
In this version of your script, I am not seeing any actionable code that moves the NPC to the selected spawn. Be sure to do as @DevDaniel_House demonstrated.
On another note, you have no need to check currentBrainrots < maxBrainrots, as this is already taken care of at the beginning of the function:
if currentBrainrots == maxBrainrots then return end
It is recommended you avoid one-liners
You also have no need to sort availableSpawns as spawns are selected at random—changing the order of the spawns will have no effect
This has fixed the spawning problem but I think the server does not acknowledge currentBrainrots value, because the spawning never stops.
I have made 2 IntValues in Workspace for the server to know when the client changes the value. It does get changed on server-side but the spawning never stops
Spawner.SpawnBrainrot = function()
if currentBrainrots == maxBrainrots then return end
if #availableSpawns == 0 then
availableSpawns = table.clone(referenceSpawns)
end
local spawnIndex = math.random(#availableSpawns)
local spawn = table.remove(availableSpawns, spawnIndex)
local Brainrot = Spawner.GetRandomBrainrot()
print("Spawning "..tostring(Brainrot).." into spawn "..tostring(spawn))
Events.BrainrotsInW:InvokeServer()
local BrainrotClone = Brainrots:FindFirstChild(Brainrot):Clone()
BrainrotClone.Parent = Spawned
BrainrotClone:PivotTo(spawn.CFrame)
print(availableSpawns)
end
What I can guess happens is
if #availableSpawns == 0 then
availableSpawns = table.clone(referenceSpawns)
end
this, as it clones the table whenever it finds no spawns, does that even though currentBrainrots == maxBrainrots, it basically jumps the return end and directly clones the table.
The reason behind that condition being always false, is you are comparing two instances (the IntValues), rather than their actual set values. All you need to do is add a .Value at the end of each usage of currentBrainrots and maxBrainrots.
They have already been declared with .Value, but after removing .Value form the declaration and putting it in the function, it now works perfectly!
This is the final script:
Spawner.SpawnBrainrot = function()
if currentBrainrots.Value == maxBrainrots.Value then
return
end
if #availableSpawns == 0 then
availableSpawns = table.clone(referenceSpawns)
end
local spawnIndex = math.random(#availableSpawns)
local spawn = table.remove(availableSpawns, spawnIndex)
local Brainrot = Spawner.GetRandomBrainrot()
print("Spawning "..tostring(Brainrot).." into spawn "..tostring(spawn))
Events.BrainrotsInW:InvokeServer()
local BrainrotClone = Brainrots:FindFirstChild(Brainrot):Clone()
BrainrotClone.Parent = Spawned
BrainrotClone:PivotTo(spawn.CFrame)
print(availableSpawns)
end
Thank you to @Ziffix and @DevDaniel_House for all the help and to everyone contributing to this post!
I just want to reply one last time, just so you dont move on confused as to why it doesnt work if you declare the variable with .Value.
Basically if you declare a variable it will always stay the same unless you change it in script, because it caches the value you gave it. This makes it so you cant use properties of instances in variables, which is why you had to store the instance itself instead of its property.