local Terrain = game.Workspace.Terrain
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

-- Configuration
local CHUNK_SIZE = 32
local RENDER_DISTANCE = 5
local NOISE_SCALE = 0.005
local TERRAIN_SCALE = 40
local WATER_LEVEL = 1
local SEED = math.random(1, 1000000)
local HOUSE_CHANCE = 0.01  -- 1% chance for a house to spawn in each chunk
local HOUSE_CLEARANCE = 5  -- Area around the house to flatten
local TREE_CHANCE = 0.05   -- 5% chance for a tree to spawn in each suitable location
local TREE_CLEARANCE = 2   -- Area around the tree to check for suitability

-- Biome Configuration
local BIOMES = {
    {name = "Plains", threshold = 0.3, material = Enum.Material.Grass},
    {name = "Forest", threshold = 0.6, material = Enum.Material.LeafyGrass},
    {name = "Desert", threshold = 0.8, material = Enum.Material.Sand},
    {name = "Snow", threshold = 1, material = Enum.Material.Snow}
}

-- Store generated chunks
local generatedChunks = {}

-- Get the House and Tree models
local houseModel = game.Workspace:FindFirstChild("House")
local treeModel = game.Workspace:FindFirstChild("Tree")
if not houseModel then
    error("House model not found in Workspace")
end
if not treeModel then
    error("Tree model not found in Workspace")
end

-- Improved noise function with seed and octaves
local function improvedNoise(x, z, octaves)
    local total = 0
    local frequency = 1
    local amplitude = 1
    local maxValue = 0
    for i = 1, octaves do
        total = total + math.noise(x * NOISE_SCALE * frequency + SEED, z * NOISE_SCALE * frequency - SEED) * amplitude
        maxValue = maxValue + amplitude
        amplitude = amplitude * 0.5
        frequency = frequency * 2
    end
    return total / maxValue
end

-- Function to determine biome
local function getBiome(noise)
    for _, biome in ipairs(BIOMES) do
        if noise <= biome.threshold then
            return biome
        end
    end
    return BIOMES[#BIOMES]
end

-- Function to check if an area is suitable for object placement
local function isSuitableForObject(heightMap, x, z, clearance)
    local minHeight = math.huge
    local maxHeight = -math.huge
    
    for dx = -clearance, clearance do
        for dz = -clearance, clearance do
            local hx, hz = x + dx, z + dz
            if hx >= 0 and hx < CHUNK_SIZE and hz >= 0 and hz < CHUNK_SIZE then
                local height = heightMap[hx][hz]
                minHeight = math.min(minHeight, height)
                maxHeight = math.max(maxHeight, height)
            end
        end
    end
    
    return minHeight > WATER_LEVEL and (maxHeight - minHeight) <= 2
end

-- Function to flatten an area for object placement
local function flattenArea(chunkX, chunkZ, x, z, height, clearance)
    for dx = -clearance, clearance do
        for dz = -clearance, clearance do
            local worldX = chunkX * CHUNK_SIZE + x + dx
            local worldZ = chunkZ * CHUNK_SIZE + z + dz
            Terrain:FillBlock(CFrame.new(worldX, height / 2, worldZ), Vector3.new(1, height, 1), Enum.Material.Grass)
        end
    end
end

-- Function to place an object (house or tree)
local function placeObject(model, chunkX, chunkZ, x, z, height)
    local worldX = chunkX * CHUNK_SIZE + x
    local worldZ = chunkZ * CHUNK_SIZE + z
    
    local newObject = model:Clone()
    newObject.Parent = game.Workspace
    
    local primaryPart = newObject:FindFirstChildWhichIsA("BasePart")
    if primaryPart then
        newObject.PrimaryPart = primaryPart
        newObject:SetPrimaryPartCFrame(CFrame.new(worldX, height + 1, worldZ))
    else
        warn(model.Name .. " model does not have a suitable part to use as PrimaryPart")
        newObject:Destroy()
    end
end

-- Function to generate terrain for a single chunk
local function generateChunk(chunkX, chunkZ)
    local chunkKey = chunkX .. "," .. chunkZ
    if generatedChunks[chunkKey] then return end
    
    local heightMap = {}
    
    for x = 0, CHUNK_SIZE - 1 do
        heightMap[x] = {}
        for z = 0, CHUNK_SIZE - 1 do
            local worldX = chunkX * CHUNK_SIZE + x
            local worldZ = chunkZ * CHUNK_SIZE + z
            
            local noise = improvedNoise(worldX, worldZ, 4)
            local biomeNoise = improvedNoise(worldX * 0.25, worldZ * 0.25, 2)
            local biome = getBiome(biomeNoise)
            
            local height = math.floor(noise * TERRAIN_SCALE + 10)
            heightMap[x][z] = height
            
            if height > WATER_LEVEL then
                Terrain:FillBlock(CFrame.new(worldX, height / 2, worldZ), Vector3.new(1, height, 1), biome.material)
            else
                Terrain:FillBlock(CFrame.new(worldX, WATER_LEVEL / 2, worldZ), Vector3.new(1, WATER_LEVEL, 1), Enum.Material.Water)
            end
        end
    end
    
    -- Randomly place a house in the chunk
    if math.random() < HOUSE_CHANCE then
        local attempts = 0
        local maxAttempts = 10
        
        while attempts < maxAttempts do
            local x = math.random(HOUSE_CLEARANCE, CHUNK_SIZE - HOUSE_CLEARANCE - 1)
            local z = math.random(HOUSE_CLEARANCE, CHUNK_SIZE - HOUSE_CLEARANCE - 1)
            local height = heightMap[x][z]
            
            if isSuitableForObject(heightMap, x, z, HOUSE_CLEARANCE) then
                flattenArea(chunkX, chunkZ, x, z, height, HOUSE_CLEARANCE)
                placeObject(houseModel, chunkX, chunkZ, x, z, height)
                break
            end
            
            attempts = attempts + 1
        end
    end
    
    -- Place trees in Plains biome
    for x = 0, CHUNK_SIZE - 1 do
        for z = 0, CHUNK_SIZE - 1 do
            local biomeNoise = improvedNoise((chunkX * CHUNK_SIZE + x) * 0.25, (chunkZ * CHUNK_SIZE + z) * 0.25, 2)
            local biome = getBiome(biomeNoise)
            
            if biome.name == "Plains" and math.random() < TREE_CHANCE then
                local height = heightMap[x][z]
                
                if isSuitableForObject(heightMap, x, z, TREE_CLEARANCE) then
                    placeObject(treeModel, chunkX, chunkZ, x, z, height)
                end
            end
        end
    end
    
    generatedChunks[chunkKey] = true
end

-- Function to update terrain around the player
local function updateTerrain(player)
    local character = player.Character
    if not character then return end
    
    local position = character.PrimaryPart.Position
    local chunkX = math.floor(position.X / CHUNK_SIZE)
    local chunkZ = math.floor(position.Z / CHUNK_SIZE)
    
    for x = -RENDER_DISTANCE, RENDER_DISTANCE do
        for z = -RENDER_DISTANCE, RENDER_DISTANCE do
            generateChunk(chunkX + x, chunkZ + z)
        end
    end
end

-- Clear existing terrain
Terrain:Clear()

-- Update terrain when a player's character spawns
Players.PlayerAdded:Connect(function(player)
    player.CharacterAdded:Connect(function(character)
        wait(1)
        updateTerrain(player)
    end)
end)

-- Continuously update terrain as players move
local lastUpdate = 0
local UPDATE_INTERVAL = 0.5

RunService.Heartbeat:Connect(function(deltaTime)
    lastUpdate = lastUpdate + deltaTime
    if lastUpdate >= UPDATE_INTERVAL then
        for _, player in ipairs(Players:GetPlayers()) do
            updateTerrain(player)
        end
        lastUpdate = 0
    end
end)

Changelog: 1. Added tree generation in Plains biome. 2. Implemented tree placement logic with clearance checks. 3. Refactored object placement code to be more generic (used for both houses and trees). 4. Adjusted biome detection for tree placement. 5. Added TREE_CHANCE and TREE_CLEARANCE constants for easy configuration.