How to make a randomly generated model only spawn once per set range

i have a randomly generated game, but i have an issue that chests spawn way too much, and im trying to make chests spawn very rarely since they give a lot of loot, but they keep on spawning like this:


in a circle, i have no idea on how to fix this, its not my code so i dont really know where to edit it without breaking the code. even if i change the density or offset they still spawn wierdly or just never spawn

--not mine, random spawning works fine
local noise = math.noise
local random = math.random
local abs = math.abs
local insert = table.insert
local newV3 = Vector3.new
local cf = CFrame.new
local cfAngles = CFrame.Angles
local color3fromRGB = Color3.fromRGB
local ipairs = ipairs
local setmetatable = setmetatable
local workspace = workspace
local grassMaterial = Enum.Material.Grass
local SEED = workspace.SEED.Value
local TERRAIN_SMOOTHNESS = 40
local HEIGHT_SCALE = 100
local WIDTH_SCALE = 12
local X, Z = 4, 4
local MIN_CTREE_SPAWN_HEIGHT = -35
local MAX_CTREE_SPAWN_HEIGHT =  -34
local CTREE_DENSITY = .1
local CTREE_OFFSET = 1
local MOUNTAIN_HEIGHT = 20
local CHUNK_SCALE = { X = WIDTH_SCALE * (X - 1), Z = WIDTH_SCALE * (Z - 1) }
local draw3dTriangle = require(game.ReplicatedStorage.Draw3dTriangle)

local function spawnChest(chunk)
	local posGrid = chunk.posGrid
	local chunkPosX = chunk.x
	local chunkPosZ = chunk.z
	local instances = chunk.instances

	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then

				local height = posGrid[x][z].Y
				if height >= MIN_CTREE_SPAWN_HEIGHT and height <= MAX_CTREE_SPAWN_HEIGHT then

					local spawnModulo = noise((chunkPosX * x)/20, (chunkPosZ * z)/20, SEED * chunkPosX * chunkPosZ) % .1
					local spawnTree = spawnModulo < CTREE_DENSITY and true or false
					if spawnTree then
						local tree = game.ReplicatedStorage.Chest:Clone()
						local cd = tree.ClickDetector
						local cl = workspace.ChestLoot
						local rand = math.random(0,10)
						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									if workspace.Matchet.Value == 1 then
										if math.random(0,10) > 7 then
											cl.Diamond.Value = cl.Diamond.Value + 1
										end
										if math.random(0,10) > 5 then
											cl.Emerald.Value = cl.Emerald.Value + 1
										end
										if math.random(0,10) > 4 then
											cl.Gold.Value = cl.Gold.Value + 1
										end
										if math.random(0,10) > 4 then
											cl.Platnum.Value = cl.Platnum.Value + 1
										end
										if math.random(0,10) > 2 then
											cl.Scrap.Value = cl.Scrap.Value + 1
										end
										if math.random(0,10) > 3 then
											cl.Glass.Value = cl.Glass.Value + 1
										end
										if math.random(0,10) > 6 then
											cl.Pearl.Value = cl.Pearl.Value + 1
										end
										if math.random(0,10) > 8 then
											cl.Sapphire.Value = cl.Sapphire.Value + 1
										end
										tree:Destroy()
									end
								end
							end)
						end
						
						tree:SetPrimaryPartCFrame(cf(posGrid[x][z])
							* cfAngles(0, noise(x/7, z/7, SEED * 2) * 10, 0)
							* cf(noise(x*5.4, z*5.4, SEED*2) * CTREE_OFFSET, 0, noise(x*5.4, z*5.4, SEED*2) * CTREE_OFFSET)
						)

						tree.Parent = workspace

						insert(instances, tree)

					end
				end
			end	
		end		
	end
end

--Chunk
local Chunk = {}
Chunk.__index = Chunk

Chunk.WIDTH_SCALE = WIDTH_SCALE
Chunk.X = X
Chunk.Z = Z

function Chunk.new(chunkPosX, chunkPosZ)
	local instances = {}
	
	local chunk = {
		x = chunkPosX;
		z = chunkPosZ;
		instances = instances;
		posGrid = chunkPositionGrid(chunkPosX, chunkPosZ);
	}
	
	setmetatable(chunk, Chunk)
	
	createTriangleTerrain(chunk)
	spawnChest(chunk)
	
	return chunk
end

function Chunk:Destroy()
	for index, instance in ipairs(self.instances) do
		instance:Destroy()
	end
end

return Chunk
1 Like

This is a Module Script.

Where is the code that calls Chunk.new(chunkPosX, chunkPosZ)

Check there for timing.

i have the code kinda short, because the full code is like 500 lines here is the full code:

local noise = math.noise
local random = math.random
local abs = math.abs
local insert = table.insert
local newV3 = Vector3.new
local cf = CFrame.new
local cfAngles = CFrame.Angles
local color3fromRGB = Color3.fromRGB
local ipairs = ipairs
local setmetatable = setmetatable
local workspace = workspace
local grassMaterial = Enum.Material.Grass
local treeModel = game.ReplicatedStorage.Tree
local Rock = game.ReplicatedStorage.Rock
local Iron = game.ReplicatedStorage.Iron
local birch = game.ReplicatedStorage["Birch Tree"]
local SEED = workspace.SEED.Value
local TERRAIN_SMOOTHNESS = 40
local HEIGHT_SCALE = 100
local WIDTH_SCALE = 12
local X, Z = 4, 4
local TREE_DENSITY = .005
local TREE_OFFSET = 5
local Rock_DENSITY = .01
local Rock_OFFSET = 10
local IRON_DENSITY = .0025
local IRON_OFFSET = 10
local MIN_IRON_SPAWN_HIGHT = 20
local Max_IRON_SPAWN_HIGHT = 50
local MIN_TREE_SPAWN_HEIGHT = -25
local MAX_TREE_SPAWN_HEIGHT = 25
local BTREE_DENSITY = .005
local BTREE_OFFSET = 5
local MIN_BTREE_SPAWN_HEIGHT = 10
local MAX_BTREE_SPAWN_HEIGHT = 30
local LTREE_DENSITY = .000001
local LTREE_OFFSET = .05
local MIN_CTREE_SPAWN_HEIGHT = -35
local MAX_CTREE_SPAWN_HEIGHT =  -34
local CTREE_DENSITY = .00001
local CTREE_OFFSET = 1
local MIN_LTREE_SPAWN_HEIGHT = 20
local MAX_LTREE_SPAWN_HEIGHT = 40
local MOUNTAIN_HEIGHT = 20
local CHUNK_SCALE = { X = WIDTH_SCALE * (X - 1), Z = WIDTH_SCALE * (Z - 1) }
local draw3dTriangle = require(game.ReplicatedStorage.Draw3dTriangle)

local function chunkPositionGrid(chunkPosX, chunkPosZ)
	local posGrid = {}
	
	for x = 1, X do
		posGrid[x] = {}
		
		for z = 1, Z do
			posGrid[x][z] = newV3(
				CHUNK_SCALE.X * chunkPosX + x*WIDTH_SCALE,
				noise((x + (X-1) * chunkPosX)/TERRAIN_SMOOTHNESS, (z + (Z-1) * chunkPosZ)/TERRAIN_SMOOTHNESS, SEED) * HEIGHT_SCALE,
				CHUNK_SCALE.Z * chunkPosZ + z*WIDTH_SCALE
			)
			
			if posGrid[x][z].Y > MOUNTAIN_HEIGHT then
				local relativeMountainHeight = posGrid[x][z].Y - MOUNTAIN_HEIGHT
				posGrid[x][z] = posGrid[x][z] + (newV3(0, .75, 0) * relativeMountainHeight) + (10 * newV3(0, .25, 0))
			elseif posGrid[x][z].Y > MOUNTAIN_HEIGHT - 10 then
				local relativeMountainHeight = posGrid[x][z].Y - (MOUNTAIN_HEIGHT - 10)
				posGrid[x][z] = posGrid[x][z] + (newV3(0, .25, 0) * relativeMountainHeight)
			end
		end
	end
	
	return posGrid
end

--coloring

local function paintGrass(part)
	part.Material = grassMaterial
	local absoluteHeight = abs(part.Position.Y) * 1.25
	part.Color = color3fromRGB(75 + absoluteHeight, 151 + absoluteHeight, 75 + absoluteHeight)
end

local function paintStone(part)
	part.Material = grassMaterial
	
	local relativeMountainHeight = part.Position.Y - MOUNTAIN_HEIGHT
	local baseColor = color3fromRGB(75 + MOUNTAIN_HEIGHT, 151 + MOUNTAIN_HEIGHT, 75 + MOUNTAIN_HEIGHT)
	local mountainColor = color3fromRGB(125, 125, 125)
	
	if relativeMountainHeight >= 50 then
		part.Color = mountainColor
	else
		part.Color = baseColor:lerp(mountainColor, relativeMountainHeight / 50)
	end
end

local function paintTreeLeaves(tree)
	for index, part in ipairs(tree:GetChildren()) do
		if part.Name == "Leaf" then
			part.Color = color3fromRGB(random(50, 100), random(126, 176), random(50, 100))
		end
	end
end

local function createTriangleTerrain(chunk)
	local posGrid = chunk.posGrid
	local instances = chunk.instances
	
	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then
				local a = posGrid[x][z]
				local b = posGrid[x+1][z]
				local c = posGrid[x][z+1]
				local d = posGrid[x+1][z+1]
				
				local wedgeA, wedgeB = draw3dTriangle(a, b, c)
				local wedgeC, wedgeD = draw3dTriangle(b, c, d)
				
				for index, wedge in ipairs { wedgeA, wedgeB, wedgeC, wedgeD } do
					if wedge.Position.Y < MOUNTAIN_HEIGHT then
						paintGrass(wedge)
					else
						paintStone(wedge)
					end
				end
				
				insert(instances, wedgeA)
				insert(instances, wedgeB)
				insert(instances, wedgeC)
				insert(instances, wedgeD)
			end
		end
	end
end


--Tree
local function spawnTrees(chunk)
	local posGrid = chunk.posGrid
	local chunkPosX = chunk.x
	local chunkPosZ = chunk.z
	local instances = chunk.instances

	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then

				local height = posGrid[x][z].Y
				if height >= MIN_TREE_SPAWN_HEIGHT and height <= MAX_TREE_SPAWN_HEIGHT then

					local spawnModulo = noise((chunkPosX * x)/10, (chunkPosZ * z)/10, SEED * chunkPosX * chunkPosZ) % .1
					local spawnTree = spawnModulo < TREE_DENSITY and true or false
					if spawnTree then
						local tree = treeModel:Clone()
						local cd = tree:FindFirstChild("Trunk"):FindFirstChild("ClickDetector")

						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									local timertime = math.random(0,12)
									local timertime2 = math.random(0,8)
									local timertime3 = math.random(0,4)
									tree:WaitForChild("Trunk"):Destroy()
									game.Workspace.Wood.Value = game.Workspace.Wood.Value + 1
									wait(timertime)
									tree:WaitForChild("Leaf"):WaitForChild("Leaf1"):Destroy()
									wait(timertime2)
									tree:WaitForChild("Leaf"):WaitForChild("Leaf2"):Destroy()
									wait(timertime3)
									tree:WaitForChild("Leaf"):Destroy()
								end
							end)
						end


						local cd = tree:FindFirstChild("Leaf"):FindFirstChild("ClickDetector")

						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									tree:WaitForChild("Leaf"):Destroy()
									game.Workspace.Leaf.Value = game.Workspace.Leaf.Value + 1
									if math.random(0,10) > 7 then
										workspace.Apple.Value = workspace.Apple.Value + 1
										game.Players.LocalPlayer.PlayerGui.ScreenGui.Inventory.ScrollingFrame.TextButton.Visible = true
										
									end
								end
							end)
						end

						paintTreeLeaves(tree)

						tree:SetPrimaryPartCFrame(cf(posGrid[x][z])
							* cfAngles(0, noise(x/7, z/7, SEED * 2) * 10, 0)
							* cf(noise(x*5.4, z*5.4, SEED*2) * TREE_OFFSET, 0, noise(x*5.4, z*5.4, SEED*2) * TREE_OFFSET)
						)

						tree.Parent = workspace

						insert(instances, tree)
					end

				end

			end
		end
	end
end

--Rock
local function spawnRock(chunk)
	local posGrid = chunk.posGrid
	local chunkPosX = chunk.x
	local chunkPosZ = chunk.z
	local instances = chunk.instances

	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then

				local height = posGrid[x][z].Y
				if height >= MIN_TREE_SPAWN_HEIGHT and height <= MAX_TREE_SPAWN_HEIGHT then

					local spawnModulo = noise((chunkPosX * x)/20, (chunkPosZ * z)/20, SEED * chunkPosX * chunkPosZ) % .1
					local spawnTree = spawnModulo < Rock_DENSITY and true or false
					if spawnTree then
						local tree = Rock:Clone()
						local cd = tree:FindFirstChild("ClickDetector")

						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									tree:Destroy()
									game.Workspace.Stone.Value = game.Workspace.Stone.Value + 1
								end
							end)
						end

						tree:SetPrimaryPartCFrame(cf(posGrid[x][z])
							* cfAngles(0, noise(x/7, z/7, SEED * 2) * 10, 0)
							* cf(noise(x*5.4, z*5.4, SEED*2) * Rock_OFFSET, 0, noise(x*5.4, z*5.4, SEED*2) * Rock_OFFSET)
						)

						tree.Parent = workspace

						insert(instances, tree)
					end

				end

			end
		end
	end
end

local function spawnIron(chunk)
	local posGrid = chunk.posGrid
	local chunkPosX = chunk.x
	local chunkPosZ = chunk.z
	local instances = chunk.instances

	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then

				local height = posGrid[x][z].Y
				if height >= MIN_IRON_SPAWN_HIGHT and height <= Max_IRON_SPAWN_HIGHT then

					local spawnModulo = noise((chunkPosX * x)/20, (chunkPosZ * z)/20, SEED * chunkPosX * chunkPosZ) % .1
					local spawnTree = spawnModulo < IRON_DENSITY and true or false
					if spawnTree then
						local tree = Iron:Clone()
						local cd = tree:FindFirstChild("ClickDetector")

						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									tree:Destroy()
									game.Workspace.Iron.Value = game.Workspace.Iron.Value + 1
									if math.random(1,10) >= 7 then
										game.Workspace.Sulphur.Value = game.Workspace.Sulphur.Value + 1
									end
								end
							end)
						end

						tree:SetPrimaryPartCFrame(cf(posGrid[x][z])
							* cfAngles(0, noise(x/7, z/7, SEED * 2) * 10, 0)
							* cf(noise(x*5.4, z*5.4, SEED*2) * Rock_OFFSET, 0, noise(x*5.4, z*5.4, SEED*2) * Rock_OFFSET)
						)

						tree.Parent = workspace

						insert(instances, tree)
						
					end
				end
			end	
		end		
	end
end

local function spawnBirch(chunk)
	local posGrid = chunk.posGrid
	local chunkPosX = chunk.x
	local chunkPosZ = chunk.z
	local instances = chunk.instances

	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then

				local height = posGrid[x][z].Y
				if height >= MIN_BTREE_SPAWN_HEIGHT and height <= MAX_BTREE_SPAWN_HEIGHT then

					local spawnModulo = noise((chunkPosX * x)/20, (chunkPosZ * z)/20, SEED * chunkPosX * chunkPosZ) % .1
					local spawnTree = spawnModulo < BTREE_DENSITY and true or false
					if spawnTree then
						local tree = birch:Clone()
						local cd = tree.Trunk.ClickDetector

						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									if workspace.Hatchet.Value == 1 then
										tree.Trunk:Destroy()
										game.Workspace.Birch.Value = game.Workspace.Birch.Value + 1
										workspace.Leaf.Value = workspace.Leaf.Value + 2
										tree:Destroy()
									end
								end
							end)
						end

						tree:SetPrimaryPartCFrame(cf(posGrid[x][z])
							* cfAngles(0, noise(x/7, z/7, SEED * 2) * 10, 0)
							* cf(noise(x*5.4, z*5.4, SEED*2) * BTREE_OFFSET, 0, noise(x*5.4, z*5.4, SEED*2) * BTREE_OFFSET)
						)

						tree.Parent = workspace

						insert(instances, tree)

					end
				end
			end	
		end		
	end
end

local function spawnLarge(chunk)
	local posGrid = chunk.posGrid
	local chunkPosX = chunk.x
	local chunkPosZ = chunk.z
	local instances = chunk.instances

	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then

				local height = posGrid[x][z].Y
				if height >= MIN_LTREE_SPAWN_HEIGHT and height <= MAX_LTREE_SPAWN_HEIGHT then

					local spawnModulo = noise((chunkPosX * x)/20, (chunkPosZ * z)/20, SEED * chunkPosX * chunkPosZ) % .1
					local spawnTree = spawnModulo < BTREE_DENSITY and true or false
					if spawnTree then
						local tree = game.ReplicatedStorage.LargeTree:Clone()
						local cd = tree.ClickDetector

						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									if workspace.Matchet.Value == 1 then
										tree.Trunk:Destroy()
										game.Workspace["Oak Tree"].Value = game.Workspace["Oak Tree"].Value + 1
										workspace.Leaf.Value = workspace.Leaf.Value + 3
										tree:Destroy()
									end
								end
							end)
						end

						tree:SetPrimaryPartCFrame(cf(posGrid[x][z])
							* cfAngles(0, noise(x/7, z/7, SEED * 2) * 10, 0)
							* cf(noise(x*5.4, z*5.4, SEED*2) * LTREE_OFFSET, 0, noise(x*5.4, z*5.4, SEED*2) * LTREE_OFFSET)
						)

						tree.Parent = workspace

						insert(instances, tree)

					end
				end
			end	
		end		
	end
end

local function spawnChest(chunk)
	local posGrid = chunk.posGrid
	local chunkPosX = chunk.x
	local chunkPosZ = chunk.z
	local instances = chunk.instances

	for x = 1, X do
		for z = 1, Z do
			if x+1 <= X and z+1 <= Z then

				local height = posGrid[x][z].Y
				if height >= MIN_CTREE_SPAWN_HEIGHT and height <= MAX_CTREE_SPAWN_HEIGHT then

					local spawnModulo = noise((chunkPosX * x)/20, (chunkPosZ * z)/20, SEED * chunkPosX * chunkPosZ) % .1
					local spawnTree = spawnModulo < CTREE_DENSITY and true or false
					if spawnTree then
						local tree = game.ReplicatedStorage.Chest:Clone()
						local cd = tree.ClickDetector
						local cl = workspace.ChestLoot
						local rand = math.random(0,10)
						if cd then
							cd.MouseClick:Connect(function()
								if tree then
									if workspace.Matchet.Value == 1 then
										if math.random(0,10) > 7 then
											cl.Diamond.Value = cl.Diamond.Value + 1
										end
										if math.random(0,10) > 5 then
											cl.Emerald.Value = cl.Emerald.Value + 1
										end
										if math.random(0,10) > 4 then
											cl.Gold.Value = cl.Gold.Value + 1
										end
										if math.random(0,10) > 4 then
											cl.Platnum.Value = cl.Platnum.Value + 1
										end
										if math.random(0,10) > 2 then
											cl.Scrap.Value = cl.Scrap.Value + 1
										end
										if math.random(0,10) > 3 then
											cl.Glass.Value = cl.Glass.Value + 1
										end
										if math.random(0,10) > 6 then
											cl.Pearl.Value = cl.Pearl.Value + 1
										end
										if math.random(0,10) > 8 then
											cl.Sapphire.Value = cl.Sapphire.Value + 1
										end
										workspace.Wood.Value = workspace.Wood.Value + 4
										workspace.Iron.Value = workspace.Wood.Value + 1
										tree:Destroy()
									end
								end
							end)
						end
						
						tree:SetPrimaryPartCFrame(cf(posGrid[x][z])
							* cfAngles(0, noise(x/7, z/7, SEED * 2) * 10, 0)
							* cf(noise(x*5.4, z*5.4, SEED*2) * CTREE_OFFSET, 0, noise(x*5.4, z*5.4, SEED*2) * CTREE_OFFSET)
						)

						tree.Parent = workspace

						insert(instances, tree)

					end
				end
			end	
		end		
	end
end

--Chunk
local Chunk = {}
Chunk.__index = Chunk

Chunk.WIDTH_SCALE = WIDTH_SCALE
Chunk.X = X
Chunk.Z = Z

function Chunk.new(chunkPosX, chunkPosZ)
	local instances = {}
	
	local chunk = {
		x = chunkPosX;
		z = chunkPosZ;
		instances = instances;
		posGrid = chunkPositionGrid(chunkPosX, chunkPosZ);
	}
	
	setmetatable(chunk, Chunk)
	
	createTriangleTerrain(chunk)
	spawnTrees(chunk)
	spawnRock(chunk)
	spawnIron(chunk)
	spawnBirch(chunk)
	spawnLarge(chunk)
	spawnChest(chunk)
	
	return chunk
end

function Chunk:Destroy()
	for index, instance in ipairs(self.instances) do
		instance:Destroy()
	end
end

return Chunk

do you really need to have a function for each material?

spawnTrees(chunk)
spawnRock(chunk)
spawnIron(chunk)
spawnBirch(chunk)
spawnLarge(chunk)
spawnChest(chunk)

also the wall of math.random checks

if math.random(0,10) > 7 then
	cl.Diamond.Value = cl.Diamond.Value + 1
end
if math.random(0,10) > 5 then
	cl.Emerald.Value = cl.Emerald.Value + 1
end
if math.random(0,10) > 4 then
	cl.Gold.Value = cl.Gold.Value + 1
end
if math.random(0,10) > 4 then
	cl.Platnum.Value = cl.Platnum.Value + 1
end
if math.random(0,10) > 2 then
	cl.Scrap.Value = cl.Scrap.Value + 1
end
if math.random(0,10) > 3 then
	cl.Glass.Value = cl.Glass.Value + 1
end
if math.random(0,10) > 6 then
	cl.Pearl.Value = cl.Pearl.Value + 1
end
if math.random(0,10) > 8 then
	cl.Sapphire.Value = cl.Sapphire.Value + 1
end

and the useless variables

local cfAngles = CFrame.Angles
local color3fromRGB = Color3.fromRGB
local ipairs = ipairs
local setmetatable = setmetatable
local workspace = workspace

im not really that good at coding, also 40% of the code was from a random dev forum

bump‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎