Quick actions

cmd+k|ctrl+k

Navigation

Languages

terraingen

Snippet info

Language

Lua

Visibility

public

Author

thosewhoknow

Created

2024-04-14T16:12:15.663658Z

Updated

2024-08-19T19:20:35.118061Z

function getChunkID(v1)
	return `{v1.X},{v1.Y}`
end

function fromChunkID(id)
	local x, y = unpack(id:split(","))
	return Vector2.new(tonumber(x), tonumber(y))
end

function parseVal(v)
	return 
		v == "true" and true or v == "false" and false
		or tonumber(v) and tonumber(v) or v
end

function parseOptions(opt)
	local set = opt:split(";")
	local t = {}
	for i, v in set do
		local idx, val = unpack(v:split("="))
		t[idx] = parseVal(val)
	end
	return t
end

function hashCode(value)
	local h = 0
	local MAX_INT = 65535
	for i = 1, #value do
		h = (31 * h + string.byte(value, i, i)) % MAX_INT
	end
	return h
end

if (...) == "?" then
	return getfenv().warnf("\n<b>TerrainGen options:</b>\nOptions formatted as: 'index=value;index2=value2'\nseed = int|str\nfeatures = bool\nchunkSize = int\nrenderDist = int")
end

local setting = parseOptions(... or "")

print(setting)

local chunks = {}

local provisionedSeed = setting.seed or os.time()

local seed = type(provisionedSeed) == "number" and provisionedSeed or hashCode(provisionedSeed)
local srng = Random.new(seed)
local terrainSeed = srng:NextInteger(-65535, 65535)
local tempSeed = srng:NextInteger(-65535, 65535)
local preSeed = srng:NextInteger(-65535, 65535)
local featureSeed = srng:NextInteger(-65535, 65535)
local globalTempMul = srng:NextNumber(0.8, 3)
local globalPreMul = srng:NextNumber(0.8, 3)

print(globalTempMul, globalPreMul)

local populateFeatures = setting.features or true
local chunkSize = setting.chunkSize or 256
local halfChunk = chunkSize // 2

local res = 4

local Step = Instance.new("BindableEvent")
Step.Parent = script
local Tick = 0
local FrameTime = 1 / 4000
game:GetService("RunService").Heartbeat:Connect(function(DT)
	Tick += DT
	if Tick >= FrameTime then
		for _ = 1, math.floor(Tick / FrameTime) do
			Step:Fire()
		end
		Tick = 0
	end
end)

function fractalNoise(x, y, seed, octaves, persistence, scale)
	local total = 0
	local frequency = scale
	local amplitude = 1

	for i = 1, octaves do
		total = total + math.noise(x * frequency, y * frequency, seed) * amplitude
		frequency = frequency * 2
		amplitude = amplitude * persistence
	end

	return total
end

local featureRng = Random.new(featureSeed)

local featureParams = RaycastParams.new()
featureParams.FilterType = Enum.RaycastFilterType.Include
featureParams:AddToFilter(workspace.Terrain)

local TreeLib = loadstring(game:GetService("HttpService"):GetAsync("https://glot.io/snippets/gv7v07pjx9/raw/main.lua"))()

local stuff = LoadAssets(17131558926)

local flower = stuff:Get("flower")
for i, v in flower:GetDescendants() do
	if v:IsA("BasePart") then
		v.CanCollide = false
	end
end

local features = {
	Spikes = function(pos)
		if featureRng:NextNumber() < 1/20 then
			local initialWidth = featureRng:NextInteger(14, 30)
			local height = featureRng:NextInteger(8, 55)
			local width = initialWidth
			for i = 1, height do
				workspace.Terrain:FillBall(pos + Vector3.new(0, i, 0), width, Enum.Material.Glacier)
				width -= featureRng:NextNumber(0, 0.5)
			end
		end
	end,
	PineTrees = function(pos)
		local tree = TreeLib.new({
			MaxIterations = 3,
			StartingWidth = 2.3,
			StartingHeight = 30,
			StartingWidthVariation = 1,
			StartingHeightVariation = 1,
			WidthDiminish = 0.9,
			HeightDiminish = 1,
			BranchColor = Color3.fromRGB(91, 62, 52),
			BranchColorVariation = 0.1,
			LeavesMaterial = Enum.Material.Grass,
			BranchMaterial = Enum.Material.Wood,
			LeavesColor = Color3.fromRGB(15, 72, 41),
			LeavesColorVariation = 0.1,
			LeavesSize = 20,
			LeavesSizeVariation = 0.3,
			GrowAnimation = false,
			RoundedBranches = false,
			Locked = false
		})
		if featureRng:NextNumber() < 1/90 then
			local r = workspace:GetPartBoundsInRadius(pos, 38)
			for i, v in next, r do
				if v.Name == "__BRANCH" then
					return
				end
			end
			tree:Generate(pos, script, workspace.Terrain)
		end
	end,
	NormalTrees = function(pos)
		local tree = TreeLib.new({
			MaxIterations = 3,
			StartingWidth = 2.3,
			StartingHeight = 16.5,
			StartingWidthVariation = 1,
			StartingHeightVariation = 1,
			WidthDiminish = 0.9,
			HeightDiminish = 1,
			BranchColor = Color3.fromRGB(91, 62, 52),
			BranchColorVariation = 0.1,
			LeavesMaterial = Enum.Material.Grass,
			BranchMaterial = Enum.Material.Wood,
			LeavesColor = Color3.fromRGB(31, 124, 25),
			LeavesColorVariation = 0.1,
			LeavesSize = 16.4,
			LeavesSizeVariation = 0.3,
			GrowAnimation = false,
			RoundedBranches = false,
			Locked = false
		})
		if featureRng:NextNumber() < 1/90 then
			local r = workspace:GetPartBoundsInRadius(pos, 38)
			for i, v in next, r do
				if v.Name == "__BRANCH" then
					return
				end
			end
			tree:Generate(pos, script, workspace.Terrain)
		end
	end,
	Flowers = function(pos)
		if featureRng:NextNumber() < 1/150 then
			for i = 1, featureRng:NextInteger(8, 24) do
				local p = pos + Vector3.new(featureRng:NextNumber(-8, 8), 0, featureRng:NextNumber(-8, 8))
				local ypos = workspace:Raycast(Vector3.new(p.X, 256, p.Z), Vector3.new(0, -1024, 0), featureParams)
				if ypos == nil then
					return
				end
				local col = Color3.new(featureRng:NextNumber(), featureRng:NextNumber(), featureRng:NextNumber())
				local f = flower:Clone()
				for _, v in f.petals:GetChildren() do
					v.Color = col
				end
				f.Parent = script
				f:PivotTo(CFrame.new(ypos.Position + Vector3.new(0, 0.53, 0)))
			end
		end
	end,
	Cacti = function(pos)
		if featureRng:NextNumber() < 1/75 then
			local r = workspace:GetPartBoundsInRadius(pos, 16)
			for i, v in next, r do
				if v.Name == "cactus" then
					return
				end
			end
			local cac = stuff:Get("cactus"):Clone()
			cac.Anchored = true
			cac.CanCollide = true
			cac.CFrame = CFrame.new(pos) * CFrame.new(0, 11, 0)
			cac.Parent = script
		end
	end,
	GiantTrees = function(pos)
		local tree = TreeLib.new({
			MaxIterations = 6,
			StartingWidth = 7,
			StartingHeight = 30,
			StartingWidthVariation = 1,
			StartingHeightVariation = 1,
			WidthDiminish = 1,
			HeightDiminish = 1,
			BranchColor = Color3.fromRGB(91, 62, 52),
			BranchColorVariation = 0.1,
			LeavesMaterial = Enum.Material.Grass,
			BranchMaterial = Enum.Material.Wood,
			LeavesColor = Color3.fromRGB(82, 124, 34),
			LeavesColorVariation = 0.1,
			LeavesSize = 16.4,
			LeavesSizeVariation = 0.3,
			GrowAnimation = false,
			RoundedBranches = false,
			Locked = false
		})
		if featureRng:NextNumber() < 1/90 then
			local r = workspace:GetPartBoundsInRadius(pos, 38)
			for i, v in next, r do
				if v.Name == "__BRANCH" then
					return
				end
			end
			tree:Generate(pos, script, workspace.Terrain)
		end
	end,
}

local biomeMap = {
	["Tundra"] = {
		temperature = 0.1,
		precipitation = 0.2,
		material = Enum.Material.Snow,
		terrainFeatures = {"Spikes"},
		amplitude = 20
	},
	["Taiga"] = {
		temperature = 0.3,
		precipitation = 0.4,
		terrainType = "Coniferous Forest",
		material = Enum.Material.LeafyGrass,
		terrainFeatures = {"PineTrees"},
		amplitude = 15
	},
	["Temperate Forest"] = {
		temperature = 0.6,
		precipitation = 0.5,
		material = Enum.Material.Ground,
		terrainType = "Deciduous Forest",
		terrainFeatures = {"NormalTrees"},
		amplitude = 18
	},
	["Grassland"] = {
		temperature = 0.7,
		precipitation = 0.3,
		terrainType = "Wide Plains",
		material = Enum.Material.Grass,
		terrainFeatures = {"Flowers"},
		amplitude = 25
	},
	["Desert"] = {
		temperature = 0.8,
		precipitation = 0.1,
		material = Enum.Material.Sand,
		terrainType = "Sandy Dunes",
		terrainFeatures = {"Cacti"},
		amplitude = 35
	},
	["Tropical Rainforest"] = {
		temperature = 1.0,
		precipitation = 0.9,
		material = Enum.Material.Mud,
		terrainType = "Lush Jungle",
		terrainFeatures = {"GiantTrees"},
		amplitude = 22
	}
}
function findBiome(temperature, precipitation)
	local minDistance = math.huge
	local closestBiome = nil

	for biome, data in pairs(biomeMap) do
		local tempDiff = math.abs(data.temperature - temperature)
		local precDiff = math.abs(data.precipitation - precipitation)
		local distance = math.sqrt(tempDiff^2 + precDiff^2)

		if distance < minDistance then
			minDistance = distance
			closestBiome = biome
		end
	end

	return closestBiome
end

function round(number, increment)
	return math.floor(number / increment + 0.5) * increment
end

function map(value, inputMin, inputMax, outputMin, outputMax)
	return outputMin + (value - inputMin) * (outputMax - outputMin) / (inputMax - inputMin)
end

function generateChunk(pos)
	for x = pos.X - halfChunk, pos.X + halfChunk, res do
		for z = pos.Y - halfChunk, pos.Y + halfChunk, res do
			local temp = map(fractalNoise(x, z, tempSeed, 7, 0.5, 0.001), -1, 1, 0, 1) * globalTempMul
			local wota = map(fractalNoise(x, z, preSeed, 7, 0.5, 0.001), -1, 1, 0, 1) * globalPreMul
			local biome = findBiome(temp, wota)
			biome = biome or "Desert"
			local data = biomeMap[biome]
			local noise = fractalNoise(x, z, terrainSeed, 5, 0.2, 0.01) * data.amplitude
			workspace.Terrain:FillBall(Vector3.new(x, noise, z), res, data.material)
			if populateFeatures == true then
				task.spawn(function()
					for i, v in data.terrainFeatures do
						features[v](Vector3.new(x, noise, z))
					end
				end)
			end
		end
		Step.Event:Wait()
	end

	chunks[getChunkID(pos)] = true
end

local renderDistance = setting.renderDist or 1

function getChunksInRange(pos)
	local chunksInRange = {}
	for x = pos.X - (renderDistance * chunkSize), pos.X + (renderDistance * chunkSize), chunkSize do
		for z = pos.Y - (renderDistance * chunkSize), pos.Y + (renderDistance * chunkSize), chunkSize do
			table.insert(chunksInRange, Vector2.new(x, z))
		end
	end
	return chunksInRange
end

local td = Instance.new("ScreenGui")
td.Name = "td"
td.DisplayOrder = 6969
td.IgnoreGuiInset = true
td.ResetOnSpawn = false
td.ScreenInsets = Enum.ScreenInsets.DeviceSafeInsets
td.ZIndexBehavior = Enum.ZIndexBehavior.Sibling

local db = Instance.new("TextLabel")
db.Name = "db"
db.FontFace = Font.fromEnum(Enum.Font.SourceSans)
db.Text = ""
db.TextColor3 = Color3.fromRGB(255, 255, 255)
db.TextSize = 40
db.TextStrokeTransparency = 0
db.TextXAlignment = Enum.TextXAlignment.Right
db.TextYAlignment = Enum.TextYAlignment.Bottom
db.AnchorPoint = Vector2.new(1, 1)
db.AutomaticSize = Enum.AutomaticSize.X
db.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
db.BackgroundTransparency = 1
db.BorderColor3 = Color3.fromRGB(0, 0, 0)
db.BorderSizePixel = 0
db.Position = UDim2.fromScale(1, 1)
db.Size = UDim2.new(0, 256, 1, 0)
db.Parent = td

td.Parent = owner.PlayerGui

task.defer(function()
	while true do
		task.wait()
		local pos = owner.Character:GetPivot().Position
		local cpos = Vector2.new(round(pos.X, chunkSize), round(pos.Z, chunkSize))
		local temp = map(fractalNoise(pos.X, pos.Z, tempSeed, 7, 0.5, 0.001), -1, 1, 0, 1) * globalTempMul
		local wota = map(fractalNoise(pos.X, pos.Z, preSeed, 7, 0.5, 0.001), -1, 1, 0, 1) * globalPreMul
		local biome = findBiome(temp, wota)
		biome = biome or "Desert"
		local text = "x: %d, z: %d\ncx: %d, cz: %d\nbiome: %s\ntemp: %f\nhumid: %f\npopulateFeatures: %s\nchunkSize: %d\norigSeed: %s\nseed: %s\nrenderDist: %d"
		db.Text = text:format(math.round(pos.X), math.round(pos.Z), cpos.X // chunkSize, cpos.Y // chunkSize, biome, temp, wota, populateFeatures and "true" or "false", chunkSize, provisionedSeed, seed, renderDistance)
	end
end)

while true do
	for i, v in game:GetService("Players"):GetPlayers() do
		if v.Character then
			local position1 = v.Character:GetPivot().Position
			local position = Vector2.new(round(position1.X, chunkSize), round(position1.Z, chunkSize))
			local possibleChunks = getChunksInRange(position)
			for _, pos in possibleChunks do
				local id = getChunkID(pos)
				if chunks[id] == nil then
					task.spawn(generateChunk, pos)
				end
				task.wait()
			end
		end
		task.wait()
	end
	task.wait()
end
INFO