Update Infinite Saplings Roblox Script
Key Required

Update Infinite Saplings Roblox Script

99 Nights in the Forest 🔦joshe454joshe454 2.5K views 145 executesUpdated Dec 23, 2025
5.0/ 5(2 votes)
20100% Positive
Lua Script•514 lines (17282 chars)
--[[
	WARNING: Heads up! This script has not been verified by ScriptBlox. Use at your own risk!
]]

local Client = require(game.Players.LocalPlayer.PlayerScripts.Client)
local player = game.Players.LocalPlayer

local config = {
    plantDelay = 1,
    plantAmount = 3,
    surroundBase = false,
    spiralBase = false,
    circleRadius = 45,
    skyBase = false,
    heartBase = false,
    pyramidBase = false, -- Added Pyramid Base
}

local SKY_HEIGHT = 50 -- Fixed height for Sky Base

-- GUI setup
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "SaplingPlanterUI"
screenGui.ResetOnSpawn = false
screenGui.Parent = player.PlayerGui

-- Main frame
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 280, 0, 400)
frame.Position = UDim2.new(0.5, -140, 0.5, -200)
frame.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
frame.BorderSizePixel = 0
frame.Parent = screenGui

local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 12)
corner.Parent = frame

-- Title
local title = Instance.new("TextLabel")
title.Size = UDim2.new(1, 0, 0, 36)
title.Position = UDim2.new(0, 0, 0, 0)
title.BackgroundTransparency = 1
title.Text = "🌱 Infinite Saplings"
title.TextColor3 = Color3.fromRGB(255, 255, 255)
title.TextSize = 20
title.Font = Enum.Font.GothamBold
title.Parent = frame

-- Owner Label
local ownerLabel = Instance.new("TextLabel")
ownerLabel.Size = UDim2.new(1, 0, 0, 24)
ownerLabel.Position = UDim2.new(0, 0, 0, 36)
ownerLabel.BackgroundTransparency = 1
ownerLabel.Text = "Owner: joshe454"
ownerLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
ownerLabel.TextSize = 14
ownerLabel.Font = Enum.Font.Gotham
ownerLabel.TextXAlignment = Enum.TextXAlignment.Center
ownerLabel.Parent = frame

-- Dragging Logic
local dragging, dragInput, dragStart, startPos = false, nil, nil, nil
local function update(input)
    local delta = input.Position - dragStart
    frame.Position = UDim2.new(
        startPos.X.Scale,
        startPos.X.Offset + delta.X,
        startPos.Y.Scale,
        startPos.Y.Offset + delta.Y
    )
end

frame.InputBegan:Connect(function(input)
    if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
        dragging = true
        dragStart = input.Position
        startPos = frame.Position
        input.Changed:Connect(function()
            if input.UserInputState == Enum.UserInputState.End then
                dragging = false
            end
        end)
    end
end)

frame.InputChanged:Connect(function(input)
    if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
        dragInput = input
    end
end)

game:GetService("UserInputService").InputChanged:Connect(function(input)
    if input == dragInput and dragging then
        update(input)
    end
end)

-- Scrolling frame
local scroll = Instance.new("ScrollingFrame")
scroll.Size = UDim2.new(1, 0, 1, -70)
scroll.Position = UDim2.new(0, 0, 0, 36)
scroll.BackgroundTransparency = 1
scroll.CanvasSize = UDim2.new(0,0,1.5,0)
scroll.ScrollBarThickness = 6
scroll.Parent = frame

-- GUI Utilities
local function createToggle(name, posY, colorOff)
    local btn = Instance.new("TextButton")
    btn.Size = UDim2.new(1,-20,0,32)
    btn.Position = UDim2.new(0,10,0,posY)
    btn.BackgroundColor3 = colorOff
    btn.Text = name .. ": OFF"
    btn.TextColor3 = Color3.fromRGB(255,255,255)
    btn.TextSize = 14
    btn.Font = Enum.Font.GothamBold
    btn.Parent = scroll
    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0,6)
    corner.Parent = btn
    return btn
end

local function createLabel(text,posY)
    local lbl = Instance.new("TextLabel")
    lbl.Size = UDim2.new(1,-20,0,22)
    lbl.Position = UDim2.new(0,10,0,posY)
    lbl.BackgroundTransparency = 1
    lbl.Text = text
    lbl.TextColor3 = Color3.fromRGB(255,255,255)
    lbl.TextSize = 14
    lbl.Font = Enum.Font.Gotham
    lbl.TextXAlignment = Enum.TextXAlignment.Left
    lbl.Parent = scroll
    return lbl
end

local function createTextBox(text,posY,placeholder)
    local tb = Instance.new("TextBox")
    tb.Size = UDim2.new(1,-20,0,30)
    tb.Position = UDim2.new(0,10,0,posY)
    tb.BackgroundColor3 = Color3.fromRGB(50,50,50)
    tb.Text = text
    tb.TextColor3 = Color3.fromRGB(255,255,255)
    tb.TextSize = 14
    tb.Font = Enum.Font.Gotham
    tb.PlaceholderText = placeholder
    tb.Parent = scroll
    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0,6)
    corner.Parent = tb
    return tb
end

-- Input fields
local delayLabel = createLabel("Plant Delay: 1s",10)
local delayInput = createTextBox("1",32,"0-2 seconds")

local amountLabel = createLabel("Plant Amount: 3",70)
local amountInput = createTextBox("3",92,"1-1000")

local radiusLabel = createLabel("Base Radius: 45",130)
local radiusInput = createTextBox("45",152,"41-100 studs")

-- Toggle buttons
local surroundToggle = createToggle("Surround Base",190, Color3.fromRGB(60,60,60))
local spiralToggle = createToggle("Spiral Base",230, Color3.fromRGB(60,60,60))
local skyToggle = createToggle("Sky Base",270, Color3.fromRGB(60,60,60))
local heartToggle = createToggle("Heart Base",310, Color3.fromRGB(60,60,60))
local pyramidToggle = createToggle("Pyramid Base",350, Color3.fromRGB(60,60,60)) -- Added toggle

-- Input handling
delayInput.FocusLost:Connect(function()
    local v = tonumber(delayInput.Text)
    if v and v>=0 and v<=2 then
        config.plantDelay=v
        delayLabel.Text="Plant Delay: "..v.."s"
    else delayInput.Text=tostring(config.plantDelay) end
end)

amountInput.FocusLost:Connect(function()
    local v = tonumber(amountInput.Text)
    if v and v>=1 and v<=1000 then
        config.plantAmount = math.floor(v)
        amountLabel.Text = "Plant Amount: "..v
    else amountInput.Text=tostring(config.plantAmount) end
end)

radiusInput.FocusLost:Connect(function()
    local v = tonumber(radiusInput.Text)
    if v and v>=41 and v<=100 then
        config.circleRadius=v
        radiusLabel.Text="Base Radius: "..v
    else radiusInput.Text=tostring(config.circleRadius) end
end)

-- Toggle logic
surroundToggle.MouseButton1Click:Connect(function()
    config.surroundBase = not config.surroundBase
    if config.surroundBase then
        surroundToggle.Text = "Surround Base: ON"
        surroundToggle.BackgroundColor3 = Color3.fromRGB(0,170,0)
    else
        surroundToggle.Text = "Surround Base: OFF"
        surroundToggle.BackgroundColor3 = Color3.fromRGB(60,60,60)
    end
end)

spiralToggle.MouseButton1Click:Connect(function()
    config.spiralBase = not config.spiralBase
    if config.spiralBase then
        spiralToggle.Text = "Spiral Base: ON"
        spiralToggle.BackgroundColor3 = Color3.fromRGB(255,120,0)
    else
        spiralToggle.Text = "Spiral Base: OFF"
        spiralToggle.BackgroundColor3 = Color3.fromRGB(60,60,60)
    end
end)

skyToggle.MouseButton1Click:Connect(function()
    config.skyBase = not config.skyBase
    if config.skyBase then
        skyToggle.Text = "Sky Base: ON"
        skyToggle.BackgroundColor3 = Color3.fromRGB(0,170,255)
    else
        skyToggle.Text = "Sky Base: OFF"
        skyToggle.BackgroundColor3 = Color3.fromRGB(60,60,60)
    end
end)

heartToggle.MouseButton1Click:Connect(function()
    config.heartBase = not config.heartBase
    if config.heartBase then
        heartToggle.Text = "Heart Base: ON"
        heartToggle.BackgroundColor3 = Color3.fromRGB(255,0,100)
    else
        heartToggle.Text = "Heart Base: OFF"
        heartToggle.BackgroundColor3 = Color3.fromRGB(60,60,60)
    end
end)

pyramidToggle.MouseButton1Click:Connect(function()
    config.pyramidBase = not config.pyramidBase
    if config.pyramidBase then
        pyramidToggle.Text = "Pyramid Base: ON"
        pyramidToggle.BackgroundColor3 = Color3.fromRGB(255,215,0)
    else
        pyramidToggle.Text = "Pyramid Base: OFF"
        pyramidToggle.BackgroundColor3 = Color3.fromRGB(60,60,60)
    end
end)

-- ====================
-- Base Generators & Planting
-- ====================

local previewParts={}
local function clearPreview()
    for _,p in ipairs(previewParts) do
        if p and p.Parent then p:Destroy() end
    end
    previewParts={}
end

local function findNearestSapling()
    local char = player.Character
    if not char or not char.PrimaryPart then return nil end
    local pos = char.PrimaryPart.Position
    local nearest, shortest = nil, math.huge
    for _,item in ipairs(workspace.Items:GetChildren()) do
        if item.Name=="Sapling" and item.PrimaryPart then
            local dist = (item.PrimaryPart.Position-pos).Magnitude
            if dist<shortest then
                nearest=item
                shortest=dist
            end
        end
    end
    return nearest
end

local function getCampfirePosition()
    if workspace.Map.Campground.MainFire.PrimaryPart then
        return workspace.Map.Campground.MainFire.PrimaryPart.Position
    end
    return nil
end

local function getPlantPosition(basePos)
    if config.skyBase then
        return Vector3.new(basePos.X, SKY_HEIGHT, basePos.Z)
    else
        local ray = RaycastParams.new()
        ray.FilterDescendantsInstances = {workspace.Map.Ground, workspace.Map:FindFirstChild("Snow")}
        ray.FilterType = Enum.RaycastFilterType.Include
        ray.IgnoreWater=true
        local result = workspace:Raycast(basePos, Vector3.new(0,-55,0), ray)
        if result then return result.Position end
        return nil
    end
end

-- Remote capture
local capturedArgs, capturedRemote=nil,nil
local originalInvokeServer = Client.Events.RequestPlantItem.InvokeServer
Client.Events.RequestPlantItem.InvokeServer = function(self,...)
    local args={...}
    capturedArgs=args
    capturedRemote=self
    return originalInvokeServer(self,...)
end

-- Base generators
local function getHeartPositions(center, scale, saplingCount)
    local positions = {}
    for i = 0, saplingCount - 1 do
        local t = (i / saplingCount) * math.pi * 2
        local x = 16 * math.sin(t)^3
        local y = 13 * math.cos(t) - 5*math.cos(2*t) - 2*math.cos(3*t) - math.cos(4*t)
        local posX = center.X + x * scale
        local posZ = center.Z + y * scale
        local posY = config.skyBase and SKY_HEIGHT or center.Y + 0.5
        table.insert(positions, Vector3.new(posX, posY, posZ))
    end
    return positions
end

local function getCirclePositions(center, radius, count)
    local positions = {}
    for i = 0, count - 1 do
        local angle = (i / count) * math.pi * 2
        local x = center.X + math.cos(angle) * radius
        local z = center.Z + math.sin(angle) * radius
        local y = config.skyBase and SKY_HEIGHT or center.Y + 0.5
        table.insert(positions, Vector3.new(x, y, z))
    end
    return positions
end

local function getSpiralPositions(center, radius, count)
    local positions = {}
    local turns = 3
    for i = 1, count do
        local t = i / count * math.pi * 2 * turns
        local r = (i / count) * radius
        local x = center.X + math.cos(t) * r
        local z = center.Z + math.sin(t) * r
        local y = config.skyBase and SKY_HEIGHT or center.Y + 0.5
        table.insert(positions, Vector3.new(x, y, z))
    end
    return positions
end

local function getSkyFloorPositions(center, radius, gridSize)
    local positions = {}
    local step = (radius * 2) / (gridSize - 1)
    for i = 0, gridSize - 1 do
        for j = 0, gridSize - 1 do
            local x = center.X - radius + i * step
            local z = center.Z - radius + j * step
            local y = SKY_HEIGHT
            table.insert(positions, Vector3.new(x, y, z))
        end
    end
    return positions
end

local function getPyramidPositions(center, baseRadius, layers)
    local positions = {}
    for y = 0, layers-1 do
        local radius = baseRadius * (1 - y / layers)
        local count = math.max(1, math.floor(radius * 6))
        for i = 0, count-1 do
            local angle = (i / count) * math.pi * 2
            local x = center.X + math.cos(angle) * radius
            local z = center.Z + math.sin(angle) * radius
            local posY = config.skyBase and SKY_HEIGHT or center.Y + 0.5 + y
            table.insert(positions, Vector3.new(x, posY, z))
        end
    end
    return positions
end

-- Preview
local function showPreview()
    clearPreview()
    local campfirePos = getCampfirePosition()
    if not campfirePos then return end
    local positions={}
    if config.pyramidBase then
        positions = getPyramidPositions(campfirePos, config.circleRadius, math.ceil(config.plantAmount/10))
    elseif config.heartBase then
        positions = getHeartPositions(campfirePos, config.circleRadius/25, config.plantAmount)
    elseif config.skyBase then
        positions = getSkyFloorPositions(campfirePos, config.circleRadius, 4)
    elseif config.spiralBase then
        positions = getSpiralPositions(campfirePos, config.circleRadius, config.plantAmount)
    elseif config.surroundBase then
        positions = getCirclePositions(campfirePos, config.circleRadius, config.plantAmount)
    else
        local item = findNearestSapling()
        if not item or not item.PrimaryPart then return end
        local pos = getPlantPosition(item.PrimaryPart.Position)
        if pos then table.insert(positions,pos) end
    end

    for _,pos in ipairs(positions) do
        local part = Instance.new("Part")
        part.Size = Vector3.new(2,0.5,2)
        part.Position = pos
        part.Anchored = true
        part.CanCollide = false
        part.Transparency = 0.5
        part.Material = Enum.Material.Neon
        part.Color = Color3.fromRGB(255,0,100)
        part.Parent = workspace
        table.insert(previewParts, part)
    end
end

-- Plant
local function plantSapling()
    clearPreview()
    local item = findNearestSapling()
    if not item or not item.PrimaryPart then return end
    local campfirePos = getCampfirePosition()
    if not campfirePos then return end

    local positions={}
    if config.pyramidBase then
        positions = getPyramidPositions(campfirePos, config.circleRadius, math.ceil(config.plantAmount/10))
    elseif config.heartBase then
        positions = getHeartPositions(campfirePos, config.circleRadius/25, config.plantAmount)
    elseif config.skyBase then
        positions = getSkyFloorPositions(campfirePos, config.circleRadius, 4)
    elseif config.spiralBase then
        positions = getSpiralPositions(campfirePos, config.circleRadius, config.plantAmount)
    elseif config.surroundBase then
        positions = getCirclePositions(campfirePos, config.circleRadius, config.plantAmount)
    else
        local pos = getPlantPosition(item.PrimaryPart.Position)
        if pos then table.insert(positions,pos) end
    end

    if #positions==0 then return end

    item.Parent = game.ReplicatedStorage.TempStorage
    local result = Client.Events.RequestPlantItem:InvokeServer(item, positions[1])
    if not result or not result.Success then return end

    task.spawn(function()
        if capturedArgs and capturedRemote then
            for i=2,#positions do
                task.wait(config.plantDelay)
                capturedArgs[2] = positions[i]
                capturedRemote:InvokeServer(unpack(capturedArgs))
            end
        end
    end)
end

-- Buttons
local previewButton = Instance.new("TextButton")
previewButton.Size = UDim2.new(0.48,0,0,36)
previewButton.Position = UDim2.new(0,10,1,-50)
previewButton.BackgroundColor3 = Color3.fromRGB(0,120,200)
previewButton.Text = "Preview"
previewButton.TextColor3 = Color3.fromRGB(255,255,255)
previewButton.TextSize = 16
previewButton.Font = Enum.Font.GothamBold
previewButton.Parent = frame
local previewCorner = Instance.new("UICorner")
previewCorner.CornerRadius = UDim.new(0,6)
previewCorner.Parent = previewButton

local plantButton = Instance.new("TextButton")
plantButton.Size = UDim2.new(0.48,0,0,36)
plantButton.Position = UDim2.new(0.52,0,1,-50)
plantButton.BackgroundColor3 = Color3.fromRGB(0,170,0)
plantButton.Text = "Plant"
plantButton.TextColor3 = Color3.fromRGB(255,255,255)
plantButton.TextSize = 16
plantButton.Font = Enum.Font.GothamBold
plantButton.Parent = frame
local plantCorner = Instance.new("UICorner")
plantCorner.CornerRadius = UDim.new(0,6)
plantCorner.Parent = plantButton

previewButton.MouseButton1Click:Connect(showPreview)
plantButton.MouseButton1Click:Connect(plantSapling)

-- ===== Minimize Button =====
local minimizeBtn = Instance.new("TextButton")
minimizeBtn.Size = UDim2.new(0, 24, 0, 24)
minimizeBtn.Position = UDim2.new(1, -28, 0, 6)
minimizeBtn.BackgroundColor3 = Color3.fromRGB(200, 50, 50)
minimizeBtn.Text = "X"
minimizeBtn.TextColor3 = Color3.fromRGB(255,255,255)
minimizeBtn.TextSize = 18
minimizeBtn.Font = Enum.Font.GothamBold
minimizeBtn.Parent = frame
local minCorner = Instance.new("UICorner")
minCorner.CornerRadius = UDim.new(0,4)
minCorner.Parent = minimizeBtn

local minimized = false
minimizeBtn.MouseButton1Click:Connect(function()
    minimized = not minimized
    scroll.Visible = not minimized
    previewButton.Visible = not minimized
    plantButton.Visible = not minimized
    frame.Size = minimized and UDim2.new(0,280,0,60) or UDim2.new(0,280,0,400)
end)

Features & Information

"No Keys, Just Trees: Cut, Select a Base, and Watch Your House Build!" "Endless Saplings: Chop, Pick Your Base, Then Build!"

Community Comments (10)

joshe454joshe454Dec 23, 2025(edited)

New updates coming it would be an Pyramid and other types of fixes comment below for ideas (:

Replying to @joshe454
RobinsonFRobinsonFDec 23, 2025

@joshe454 any way to minimize the window so you able to at the base with the base preview of the design!!:) other then that script is fire

joshe454joshe454Dec 23, 2025

Working on it right now

Replying to @joshe454
RobinsonFRobinsonFDec 23, 2025

@joshe454 sweet🫡

joshe454joshe454Dec 23, 2025

No ?

joshe454joshe454Dec 24, 2025

Update guys minimize button

joshe454joshe454Dec 24, 2025

Pyramid has been added

StrongestBobEverStrongestBobEverDec 25, 2025

doesnt work on xeno and doesnt have a start button

Tuboos92Tuboos92Jan 22, 2026

does it work

how to use?

More Scripts for 99 Nights in the Forest 🔦