Hey folks! Today I’m here writing my first tutorial along with TwinPlayz
Recently there’s been this popular game going around called “PLS Donate!” (created by haz3mn)
We’ve recreated it, but in a tutorial way!
Please note our intentions are not to get more copies out there, but rather get people a
Below this I will be explaining how it works.
For the server code we’ll be getting the users assets (in our case; shirts, tshirts and pants).
Ofcourse, you’ll need an API for that. We’ll be using the roproxy API.
Create a folder in ReplicatedStorage called “Events” and add a BindableEvent in there called “ClaimPlot”
Then create a server script in ServerScriptService, call it whatever you want. First you will need to define our locations, like so:
local Market = game:GetService("MarketplaceService")
local HTTPService = game:GetService("HttpService")
local BoothName = nil
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local EventsFolder = ReplicatedStorage:FindFirstChild("Events")
local Bindable = EventsFolder:FindFirstChild("ClaimPlot")
Now we’ll need to make a “GetContent” function which gets their shirts, pants and t-shirts.
First make a table, containing the subcategories (shirts, pants, t-shirts) like so;
local SubCategories = {
"2";
"11";
"12"
}
We’ll be looping through these categories in our API.
Now as for the function:
for i = 1,3 do --[ This is our loop ]
local Contents = {} -- [ These are our items ]
cursor = cursor or ""
Now we have that part, we’ll be adding the roproxy API.
local Url = "https://catalog.roproxy.com/v1/search/items/details?Category=3&Subcategory=".. SubCategories[i].. "&Sort=4&Limit=30&CreatorName=%s&cursor=%s"
SubCategories is our table which includes our subcategory ID, and the [i] is what we call in our loop.
local requestUrl = Url:format(username, cursor)
You’ll have to format the API link, otherwise, it’ll return nil.
Once you’ve formatted it, you’ll be getting the API data with HTTPServive:GetAsync(link, bool) just like this:
local success, result = pcall(function()
return HTTPService:GetAsync(requestUrl, true)
end)
We’ll be checking if it has success or not by checking if theres a result, and success.
if success then
if result then
local success2, result2 = pcall(function()
return HTTPService:JSONDecode(result)
end)
if not success then
if not result then
return GetContent(boothname, username, userid)
end
end
I’ll skip the last part since it’s self-explanatory, but here’s the finished code.
local Market = game:GetService("MarketplaceService")
local HTTPService = game:GetService("HttpService")
local BoothName = nil
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local EventsFolder = ReplicatedStorage:FindFirstChild("Events")
local Bindable = EventsFolder:FindFirstChild("ClaimPlot")
local function CloneAssets(BoothName, Contents)
for _, player in pairs(game:GetService("Players"):GetPlayers()) do
for _, Asset in pairs(Contents) do
local data = Market:GetProductInfo(Asset, Enum.InfoType.Asset)
if data and data.IsForSale then
local Gui = script.Template:Clone()
Gui.Parent = player.PlayerGui.SignPrices[BoothName].ScrollingFrame
Gui.Name = table.find(Contents, Asset)
Gui.PurchaseButton.Text = data.PriceInRobux .. "$"
Gui.Visible = true
Gui.ImportantValues:FindFirstChild("AssetId").Value = Asset
end
end
end
for _, Asset in ipairs(Contents) do
local data = Market:GetProductInfo(Asset, Enum.InfoType.Asset)
if data and data.IsForSale then
local MainGui = script.Template:Clone()
MainGui.Parent = game:GetService("StarterGui").SignPrices[BoothName].ScrollingFrame
MainGui.Name = table.find(Contents, Asset)
MainGui.PurchaseButton.Text = data.PriceInRobux .. "$"
MainGui.Visible = true
MainGui.ImportantValues:FindFirstChild("AssetId").Value = Asset
end
end
end
local SubCategories = {
"2";
"11";
"12"
}
local function GetContent(boothname, username, userid, tshirts, cursor)
for i = 1,3 do
local Contents = {}
cursor = cursor or ""
local Url = "https://catalog.roproxy.com/v1/search/items/details?Category=3&Subcategory=".. SubCategories[i].. "&Sort=4&Limit=30&CreatorName=%s&cursor=%s"
local requestUrl = Url:format(username, cursor)
local success, result = pcall(function()
return HTTPService:GetAsync(requestUrl, true)
end)
if success then
if result then
local success2, result2 = pcall(function()
return HTTPService:JSONDecode(result)
end)
BoothName = boothname
local suc, er = pcall(function()
for _, tshirt in ipairs(result2.data) do
table.insert(Contents, tshirt.id)
end
end)
if not suc then
warn(er)
end
return BoothName, Contents
end
end
end
end
Bindable.Event:Connect(function(boothname, username, userid)
local Boothname, Contents = GetContent(boothname, username, userid)
if Contents then
CloneAssets(BoothName, Contents)
end
end)
Now for the booth, take this template
Ungroup the Workspace model, make sure it’s parented to workspace.
And put the UI folder inside of StarterGui.
Now for the main part like data, particles and purchases;
Here’s the code for the DataStore2 data:
-- Services --
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- Module --
local Datastore2 = require(ReplicatedStorage.Modules.MainModule)
Datastore2.Combine("DATA", "Donated", "Raised") -- This is like the data. 'DATA' is your MASTER KEY!
-- Main --
Players.PlayerAdded:Connect(function(plr)
local DonatedStore = Datastore2("Donated", plr)
local RaisedStore = Datastore2("Raised", plr)
local Leaderstats = Instance.new("Folder") -- Create leaderstats obv
Leaderstats.Name = 'leaderstats'
local Donated = Instance.new("IntValue", Leaderstats)
Donated.Value = DonatedStore:Get(0) -- Get is basically adding the argumented amount to your saved data
Donated.Name = "Donated"
local Raised = Instance.new("IntValue", Leaderstats)
Raised.Name = "Raised"
Raised.Value = RaisedStore:Get(0) -- Same thing as above
DonatedStore:OnUpdate(function(NewDonated)
Donated.Value = NewDonated
end)
RaisedStore:OnUpdate(function(NewRaised) -- Same as above
Raised.Value = NewRaised
end)
Leaderstats.Parent = plr -- Set leaderstats parent AFTER assigning everything
end)
There isn’t much to explain to that tbh.
Now for the particles and purchases handling:
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStore2 = require(ReplicatedStorage.Modules.MainModule) -- DataStore 2 module
local Players = game:GetService("Players")
Define our locations
DataStore2
You’ll need to check for the .PromptPurchaseFinished event on MarketplaceService.
MarketplaceService.PromptPurchaseFinished:Connect(function(player, assetId, isPurchased)
Then check if the player purchased the item by doing
if isPurchased then```
Now the last part is just solely getting the asset data and setting the players stats:
local data = MarketplaceService:GetProductInfo(assetId, Enum.InfoType.Asset)
if data then
player.leaderstats.Donated.Value += data.PriceInRobux
local DonatedValue = player.leaderstats.Donated.Value
local DonatedStore = DataStore2("Donated", player)
DonatedStore:Increment(data.PriceInRobux,DonatedValue)
local GotDonatedName = data.Creator.Name
local GotDonatedPlr = game:GetService("Players")[GotDonatedName]
GotDonatedPlr.leaderstats.Raised.Value += data.PriceInRobux
local RaisedValue = GotDonatedPlr.leaderstats.Raised.Value
local RaisedStore = DataStore2("Raised", GotDonatedPlr)
RaisedStore:Increment(data.PriceInRobux,RaisedValue)
local Stands = workspace:FindFirstChild("Stands")
local StandDescendants = Stands:GetDescendants()
--local Owner
for _, Object in ipairs(StandDescendants) do
--if Object:IsA("StringValue") and Object.Name == "Owner" then
-- Owner = Object.Value
--end
--if Owner == player.Name then
if Object:IsA("TextLabel") and Object.Name == "MoneyRaised" and Object.Parent.Parent.Parent.Parent.Proximity.Owner.Value == GotDonatedName then
local Amount = RaisedStore:Get()
print(Amount)
Object.Text = Amount .. "$ Raised"
elseif Object:IsA("ParticleEmitter") and Object.Name == "Money" and Object.Parent.Parent.Proximity.Owner.Value == GotDonatedName then
Object:Emit(15)
local Sound = Object.Parent:FindFirstChildOfClass("Sound")
Sound:Play()
end
end
--end
end
end
end)
Our final code:
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStore2 = require(ReplicatedStorage.Modules.MainModule)
local Players = game:GetService("Players")
MarketplaceService.PromptPurchaseFinished:Connect(function(player, assetId, isPurchased)
if isPurchased then
local data = MarketplaceService:GetProductInfo(assetId, Enum.InfoType.Asset)
if data then
player.leaderstats.Donated.Value += data.PriceInRobux
local DonatedValue = player.leaderstats.Donated.Value
local DonatedStore = DataStore2("Donated", player)
DonatedStore:Increment(data.PriceInRobux,DonatedValue)
local GotDonatedName = data.Creator.Name
local GotDonatedPlr = game:GetService("Players")[GotDonatedName]
GotDonatedPlr.leaderstats.Raised.Value += data.PriceInRobux
local RaisedValue = GotDonatedPlr.leaderstats.Raised.Value
local RaisedStore = DataStore2("Raised", GotDonatedPlr)
RaisedStore:Increment(data.PriceInRobux,RaisedValue)
local Stands = workspace:FindFirstChild("Stands")
local StandDescendants = Stands:GetDescendants()
--local Owner
for _, Object in ipairs(StandDescendants) do
--if Object:IsA("StringValue") and Object.Name == "Owner" then
-- Owner = Object.Value
--end
--if Owner == player.Name then
if Object:IsA("TextLabel") and Object.Name == "MoneyRaised" and Object.Parent.Parent.Parent.Parent.Proximity.Owner.Value == GotDonatedName then
local Amount = RaisedStore:Get()
print(Amount)
Object.Text = Amount .. "$ Raised"
elseif Object:IsA("ParticleEmitter") and Object.Name == "Money" and Object.Parent.Parent.Proximity.Owner.Value == GotDonatedName then
Object:Emit(15)
local Sound = Object.Parent:FindFirstChildOfClass("Sound")
Sound:Play()
end
end
--end
end
end
end)
The other code is included in the kit, which is explained in TwinPlayz’s video!
Apologies for the short explanation in this tutorial, it’s a lot of code and there’s more to say than to write.
Thank you TwinPlayz for coding and starting on this project, thank you to sharxkzz for the building!
The code might go out of the brackets, no idea why that issue is occuring but enjoy.