Cara Coding Game: Create Your Own “PLS Donate!” Game in Roblox

Hey there, aspiring game developers! Ever wondered how those popular donation games like “PLS Donate!” on Roblox are made? Today, as a content creator from carcodescanner.store and your guide in the world of digital DIY, I’m excited to present a tutorial, inspired by TwinPlayz, that will walk you through recreating this engaging game format.

While our goal isn’t to flood Roblox with clones, understanding the mechanics behind “PLS Donate!” is a fantastic way to learn about Roblox game development. This tutorial is designed to be educational, breaking down the code and concepts step-by-step so you can grasp the fundamentals of creating your own “Cara Coding Game”—a game where players can donate to each other.

Let’s dive into the server-side scripting, which is crucial for fetching user assets – in this case, shirts, t-shirts, and pants – that players can showcase in their booths. To do this, we’ll need an API. We’ll be utilizing the roproxy API for this tutorial.

First, in your Roblox Studio, navigate to ReplicatedStorage and create a new folder named “Events”. Inside “Events”, add a BindableEvent and rename it to “ClaimPlot”. Next, in ServerScriptService, create a new Script (you can name it something descriptive like “DonationGameServer”). In this script, we’ll start by defining our services and locations:

 ```lua
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, let's craft the core function, `GetContent`, which will retrieve the shirts, pants, and t-shirts for a player's booth. We'll begin by setting up a table containing the subcategory IDs for these asset types:

```markdown
 ```lua
local SubCategories = { "2"; "11"; "12" }

These IDs correspond to Shirts, Pants, and T-Shirts respectively within the Roblox catalog.  We'll loop through these categories to fetch the relevant assets. Here's the initial structure of our `GetContent` function:

```markdown
 ```lua
for i = 1,3 do --[ This is our loop ]
 local Contents = {} -- [ These are our items ]
 cursor = cursor or ""

This loop will iterate three times, once for each asset subcategory. Inside the loop, `Contents` will temporarily store the asset IDs we retrieve, and `cursor` is used for API pagination (though not fully implemented in this basic example for simplicity).

Now, let's integrate the roproxy API to fetch the asset data.  We'll construct the API URL:

```markdown
 ```lua
local Url = "https://catalog.roproxy.com/v1/search/items/details?Category=3&Subcategory=".. SubCategories[i].. "&Sort=4&Limit=30&CreatorName=%s&cursor=%s"

Here, `Category=3` specifies catalog items, `Subcategory` is dynamically set by our loop, `Sort=4` sorts by relevance, `Limit=30` limits results per page, and `CreatorName=%s` and `cursor=%s` are placeholders for the username and pagination cursor.

To use this URL, we need to format it with the player's username.  This is where `string.format` comes in:

```markdown
 ```lua
local requestUrl = Url:format(username, cursor)

`username` will be passed as an argument to our `GetContent` function.  Now, we'll use `HTTPService:GetAsync` to make a request to the API and retrieve the JSON data:

```markdown
 ```lua
local success, result = pcall(function()
 return HTTPService:GetAsync(requestUrl, true)
 end)

We use `pcall` to handle potential errors during the API request.  `success` will be true if the request is successful, and `result` will contain the API response.  We then check if the request was successful and if we received a result:

```markdown
 ```lua
 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

This nested `pcall` attempts to decode the JSON response from the API.  Error handling is included, although in a more robust implementation, you might want to implement more sophisticated error logging and retry mechanisms.  The original code skips the final part of the `GetContent` function, but here's the complete code for context and clarity, including the asset cloning logic:

```markdown
 ```lua
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)

This expanded code includes the `CloneAssets` function, which handles cloning the UI elements to display the assets in the player's booth. It also completes the `GetContent` function to process the API response and extract asset IDs.

For the booth itself, you can utilize this [template](https://www.roblox.com/library/8978903924/Booth-Kit). After downloading it, ungroup the model in `Workspace` and ensure it remains parented to `Workspace`. Place the UI folder within `StarterGui`. This kit provides a basic booth structure to get you started quickly.

Now, let's move on to handling data, particle effects, and purchases.  For persistent data storage, we'll use DataStore2. Here's the server-side script for DataStore2 implementation:

```markdown
 ```lua
-- 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)

This script initializes DataStore2 to store "Donated" and "Raised" values for each player.  It creates leaderstats to display these values in the player list.  `DataStore2.Combine` is crucial for efficient data management.  `OnUpdate` functions ensure leaderstats are updated whenever the data in DataStore2 changes.

Finally, let's implement the purchase handling and particle effects.  This script manages the donation process and visual feedback when a player donates.

```markdown
 ```lua
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStore2 = require(ReplicatedStorage.Modules.MainModule) -- DataStore 2 module
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)

This script listens for the `PromptPurchaseFinished` event. When a purchase is made, it retrieves product info, updates the donator's "Donated" stat, the booth owner's "Raised" stat, updates the booth's "MoneyRaised" text, and emits particles and plays a sound effect for visual feedback.

Here's the complete code incorporating purchase handling, DataStore2, and services definitions:

```markdown
 ```lua
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)


Remember that additional code and explanations are available in TwinPlayz’s (https://www.youtube.com/watch?v=-7tiUeFKmNc)!  This tutorial provides a foundational understanding of how to create your own "cara coding game" donation game in Roblox.

While this explanation might seem brief given the amount of code, the goal is to provide a clear and functional starting point.  Creating games involves a lot of code, but hopefully, this tutorial illuminates the core logic behind a "PLS Donate!" style game.

Thank you again to TwinPlayz for the coding inspiration and to sharxkzz for the booth design!  Start building your own "cara coding game" today and explore the exciting world of Roblox game development!

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *