local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local coinTemplate = ReplicatedStorage:WaitForChild("Coin")
local spawnPart = workspace:WaitForChild("CoinSpawn")
local plotsFolder = workspace:WaitForChild("Plots")
local coinFolder = workspace:FindFirstChild("Coins") or Instance.new("Folder", workspace)
coinFolder.Name = "Coins"
local carrying = {}
local MIN_DISTANCE = 6
-- 💰 LEADERSTATS
Players.PlayerAdded:Connect(function(player)
local stats = Instance.new("Folder", player)
stats.Name = "leaderstats"
local money = Instance.new("IntValue", stats)
money.Name = "Money"
end)
-- 💎 RARITIES
local Rarities = {
{ Name="Common", Chance=60, Value=1, Color=Color3.fromRGB(200,200,200) },
{ Name="Uncommon", Chance=25, Value=2, Color=Color3.fromRGB(0,255,0) },
{ Name="Legendary", Chance=10, Value=1000000, Color=Color3.fromRGB(0,0,255) },
{ Name="Mythical", Chance=4, Value=10000000, Color=Color3.fromRGB(255,0,0) },
{ Name="Godly", Chance=1, Value=100000000, Color=Color3.fromRGB(255,0,255) },
{ Name="Secret", Chance=1, Value=1e17, Color=Color3.fromRGB(255,215,0) }
}
local Names = {"Nova","Blaze","Pixel","Core","Orb","Pulse"}
local function getRarity()
local roll = math.random(1,100)
local total = 0
for _,r in ipairs(Rarities) do
total += r.Chance
if roll <= total then return r end
end
return Rarities[1]
end
local function getPlot(player)
for _,plot in pairs(plotsFolder:GetChildren()) do
if plot:GetAttribute("Owner") == player.UserId then
return plot
end
end
end
local function getSlot(plot)
for _,v in pairs(plot:GetDescendants()) do
if v.Name == "Slot" and not v:GetAttribute("Occupied") then
return v
end
end
end
-- 🔥 SMART SPAWN (NO STACKING)
local function getSpawnPosition()
local size = spawnPart.Size
for i=1,20 do
local x = math.random(-size.X/2, size.X/2)
local z = math.random(-size.Z/2, size.Z/2)
local pos = spawnPart.Position + Vector3.new(x,3,z)
local good = true
for _,c in pairs(coinFolder:GetChildren()) do
local p = c:IsA("BasePart") and c or c:FindFirstChildWhichIsA("BasePart")
if p and (p.Position - pos).Magnitude < MIN_DISTANCE then
good = false
break
end
end
if good then return pos end
end
return spawnPart.Position + Vector3.new(0,3,0)
end
-- 🔥 SIDE PROMPTS
local function createSidePrompts(part, coin)
for _,v in pairs(part:GetChildren()) do
if v:IsA("ProximityPrompt") then v:Destroy() end
end
local collectAttach = Instance.new("Attachment", part)
collectAttach.Position = Vector3.new(2,0,0)
local collect = Instance.new("ProximityPrompt")
collect.Parent = collectAttach
collect.ActionText = "Collect 💰"
collect.HoldDuration = 0
collect.RequiresLineOfSight = false
local sellAttach = Instance.new("Attachment", part)
sellAttach.Position = Vector3.new(-2,0,0)
local sell = Instance.new("ProximityPrompt")
sell.Parent = sellAttach
sell.ActionText = "Sell 💸"
sell.HoldDuration = 0
sell.RequiresLineOfSight = false
return collect, sell
end
-- 🪙 SPAWN COIN
local function spawnCoin()
local coin = coinTemplate:Clone()
coin.Parent = coinFolder
local part = coin:IsA("BasePart") and coin or coin:FindFirstChildWhichIsA("BasePart")
if not part then return end
part.Anchored = true
part.Position = getSpawnPosition()
local rarity = getRarity()
local name = rarity.Name.." "..Names[math.random(1,#Names)]
part.Color = rarity.Color
coin:SetAttribute("Value", rarity.Value)
coin:SetAttribute("Stored", 0)
coin:SetAttribute("Placed", false)
-- CLEAN OLD
for _,v in pairs(part:GetChildren()) do
if v:IsA("ProximityPrompt") or v:IsA("ClickDetector") then
v:Destroy()
end
end
-- PICKUP PROMPT
local prompt = Instance.new("ProximityPrompt", part)
prompt.ActionText = "Pick Up"
prompt.ObjectText = name
prompt.HoldDuration = 0
prompt.RequiresLineOfSight = false
-- BILLBOARD
local bill = Instance.new("BillboardGui", part)
bill.Size = UDim2.new(0,150,0,60)
bill.StudsOffset = Vector3.new(0,3,0)
bill.AlwaysOnTop = true
local txt = Instance.new("TextLabel", bill)
txt.Size = UDim2.new(1,0,1,0)
txt.BackgroundTransparency = 1
txt.TextScaled = true
task.spawn(function()
while coin.Parent do
txt.Text = name..
"\n💰 +"..coin:GetAttribute("Value").."/s"..
"\n🪙 "..coin:GetAttribute("Stored")
task.wait(1)
end
end)
-- CLICK LOGIC
prompt.Triggered:Connect(function(player)
-- PICK UP
if not coin:GetAttribute("Placed") and not carrying[player] then
carrying[player] = coin
part.Anchored = false
local char = player.Character
if char and char:FindFirstChild("HumanoidRootPart") then
part.CFrame = char.HumanoidRootPart.CFrame * CFrame.new(0,0,-3)
local weld = Instance.new("WeldConstraint")
weld.Part0 = part
weld.Part1 = char.HumanoidRootPart
weld.Parent = part
end
prompt.ActionText = "Place"
-- PLACE
elseif carrying[player] == coin then
local plot = getPlot(player)
if not plot then return end
local slot = getSlot(plot)
if not slot then return end
for _,v in pairs(part:GetChildren()) do
if v:IsA("WeldConstraint") then v:Destroy() end
end
part.Anchored = true
part.CFrame = slot.CFrame + Vector3.new(0,2,0)
slot:SetAttribute("Occupied", true)
carrying[player] = nil
coin:SetAttribute("Placed", true)
coin:SetAttribute("Owner", player.UserId)
-- 🔥 CREATE SIDE BUTTONS
local collectPrompt, sellPrompt = createSidePrompts(part, coin)
-- 💰 COLLECT
collectPrompt.Triggered:Connect(function(player)
if coin:GetAttribute("Owner") ~= player.UserId then return end
local stored = coin:GetAttribute("Stored")
if stored > 0 then
player.leaderstats.Money.Value += stored
coin:SetAttribute("Stored", 0)
end
end)
-- 💸 SELL
sellPrompt.Triggered:Connect(function(player)
if coin:GetAttribute("Owner") ~= player.UserId then return end
coin:Destroy()
end)
-- GENERATE MONEY
task.spawn(function()
while coin.Parent do
local v = coin:GetAttribute("Value")
local s = coin
:GetAttribute("Stored")
coin:SetAttribute("Stored", s + v)
task.wait(1)
end
end)
end
end)
end
-- LOOP
while true do
task.wait(5)
spawnCoin()
end⚠️Content was pasted as plain text and auto-formatted as a code block. Use the Code Block button in the editor for proper formatting.