How to: [in-game] cash dropping

Hello! I made a simple system like the title says, and figured id share!

This tutorial goes very quickly over how to add a money leaderstat (without datastorers!), and then the cash dropping system.

Sections:

  • UI
  • Leaderstat setup
  • Cash dropping system
  • Cash picking up system

First up because it is arguable the simplest, the UI!

Im going to be using a very simple rectangle for UI with a button and text label for amount and activation, but feel free to customize!

2022-09-02 09_41_46-Window

The only things the ui needs to have is a textbox to select how much cash the player wishes to drop, and some form of button to tell the code that they want to drop the selected amount of cash

As the main client script were going to drop this [LocalScript] under the screenGUI but it could be under plrScripts or characterScripts or anything like that

local plr = game.Players.LocalPlayer

local ui = script.Parent
local frame = ui.MainFrame

local dropBtn = frame.DropCash
local amount = frame.CashAmount

-- obviously assign to your own frame, btn, and textbox if you made a custom gui

local currentMoney = plr.leaderstats.Money
local remote = game.ReplicatedStorage.DropMoney

dropBtn.Activated:Connect(function()
	local am = amount.Text
	
	if tonumber(am) then -- basic check to see if the text is a number
		am = tonumber(am)
		
		remote:FireServer(am)
	end
end)

then moving swiftly on to making the leaderstats (skip this part if you already have your money leaderstat setup)
server script under ServerScriptService, this is the script we will be using for the majority of the rest of the code

game.Players.PlayerAdded:Connect(function(plr)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr -- folder under the player, tells roblox to display the leaderstats in the gui leaderboard
	
	local m = Instance.new("IntValue")
	m.Name = "Money"
	m.Value = 250
	m.Parent = leaderstats -- money int, im starting them off with 250 so I can test the dropping system, but feel free to start with any amount of your choice!
end)

pick up here if you skipped the leaderstat part!

then were going to want to add some variables, above the function we just pasted in

local repStorage = game:GetService("ReplicatedStorage")

local remote = repStorage.DropMoney
local cashObject = repStorage.CashObject

-- remote event, as well as the block we are going to drop to display as a cash object

local BaseCashObjectWorth = 75 -- used for v1, the max amount per 1 cash
local CashDropTimes = 5 -- used for v2, the max amount of pieces of cash we could drop

local v = true
-- true = v1, false = v2 - youll understand later in the tutorial

we could make the cashpart through the code like Instance.new("Part") but I found it easier to make it in rep storage

and while making this part, remember to add a remoteevent to replicatedStorage and name it “DropMoney” if youre copying my code, otherwise rename it and change the variable in the code

2022-09-02 09_56_36-Window

now remember to put that part in ReplicatedStorage

Under that cashpart, add a number value “Amount”, this will tell the pickup code how much cash this object is holding, also under the cashpart, not the amount value, the part itself, a proximityprompt, and a script under the prompt

Is there 100% a better way to detect the prompt input, yes
Am I going to remember it or do it? no :slight_smile:

what repStorage should look like currently:

2022-09-02 09_58_47-Window

then going back into that main script, were going to add a function for later, right above the leaderstats creating function

local function dropCash(cashWorth : number, pos : Vector3, plrMoneyObject : IntValue)
	plrMoneyObject.Value -= cashWorth -- take the cash that the plr is dropping
	
	local clone = cashObject:Clone()
	clone.Parent = workspace -- clone the cash object
	clone.Amount.Value = cashWorth
	
	clone.Position = pos -- put the cash near the player character
end

and finally we can make the main section of the money dropping code

We want to detect on the remoteEvent fire from the client when the btn is hit, and the code comments should explain the rest.

remote.OnServerEvent:Connect(function(plr, dropAmount : number)
	if not tonumber(dropAmount) then return end -- check to make sure its a number
	local money = plr.leaderstats.Money -- the players money object
end)

adding onto that, checking if we have all of the money

remote.OnServerEvent:Connect(function(plr, dropAmount : number)
	if not tonumber(dropAmount) then return end -- check to make sure its a number
	local money = plr.leaderstats.Money -- the players money object

    if money.Value >= dropAmount then -- if we have enough money

    end
end)

now taking a big step here, remember to read the comments! (inside of the money.Value check)

        local function v1()
			-- continues dropping cash until weve dropped all the cash requested, so in theory you could drop upwards of 1000+ pieces of cash, would reccomend adding something that if the amount is < X then do v1 else do v2
			local dropTimes = dropAmount / BaseCashObjectWorth

			for i = dropTimes, 0, -1 do
				if i > 1 then -- if its not a decimal, whole number
					dropCash(BaseCashObjectWorth, plr.Character.HumanoidRootPart.Position, money)
					
					dropAmount -= BaseCashObjectWorth
				else -- if it is a decimal, not a whole number
					dropCash(dropAmount, plr.Character.HumanoidRootPart.Position, money)
					break
				end
			end
		end
		
		local function v2()
			-- arguably the better system, would recommend using this one!1!!11111!111!11!1
			
			-- it drops 5 pieces of cash, and then distriputes the amount to drop among them
			
			local amPerCash = dropAmount / CashDropTimes
			
			for i = CashDropTimes, 0, -1 do
				dropCash(amPerCash , plr.Character.HumanoidRootPart.Position, money)
			end
		end
		
		
		-- just whichever one is selected with the bool at the top of the script
		if v then
			v1()
		else
			v2()
		end

final script:

local repStorage = game:GetService("ReplicatedStorage")

local remote = repStorage.DropMoney
local cashObject = repStorage.CashObject

-- remote event, as well as the block we are going to drop to display as a cash object

local BaseCashObjectWorth = 75 -- used for v1, the max amount per 1 cash
local CashDropTimes = 5 -- used for v2, the max amount of pieces of cash we could drop

local v = true
-- true = v1, false = v2

local function dropCash(cashWorth : number, pos : Vector3, plrMoneyObject : IntValue)
	plrMoneyObject.Value -= cashWorth -- take the cash that the plr is dropping
	
	local clone = cashObject:Clone()
	clone.Parent = workspace -- clone the cash object
	clone.Amount.Value = cashWorth
	
	clone.Position = pos -- put the cash near the player character
end

game.Players.PlayerAdded:Connect(function(plr)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr -- folder under the player, tells roblox to display the leaderstats in the gui leaderboard
	
	local m = Instance.new("IntValue")
	m.Name = "Money"
	m.Value = 250
	m.Parent = leaderstats -- money int, im starting them off with 250 so I can test the dropping system, but feel free to start with any amount of your choice!
end)

remote.OnServerEvent:Connect(function(plr, dropAmount : number)
	if not tonumber(dropAmount) then return end -- check to make sure its a number
	local money = plr.leaderstats.Money -- the players money object
	
	if money.Value >= dropAmount then -- if we have enough money
		local function v1()
			-- continues dropping cash until weve dropped all the cash requested, so in theory you could drop upwards of 1000+ pieces of cash, would reccomend adding something that if the amount is < X then do v1 else do v2
			local dropTimes = dropAmount / BaseCashObjectWorth

			for i = dropTimes, 0, -1 do
				if i > 1 then -- if its not a decimal, whole number
					dropCash(BaseCashObjectWorth, plr.Character.HumanoidRootPart.Position, money)
					
					dropAmount -= BaseCashObjectWorth
				else -- if it is a decimal, not a whole number
					dropCash(dropAmount, plr.Character.HumanoidRootPart.Position, money)
					break
				end
			end
		end
		
		local function v2()
			-- arguably the better system, would recommend using this one!1!!11111!111!11!1
			
			-- it drops 5 pieces of cash, and then distriputes the amount to drop among them
			
			local amPerCash = dropAmount / CashDropTimes
			
			for i = CashDropTimes, 0, -1 do
				dropCash(amPerCash , plr.Character.HumanoidRootPart.Position, money)
			end
		end
		
		
		-- just whichever one is selected with the bool at the top of the script
		if v then
			v1()
		else
			v2()
		end
	end
end)

And for the very last part, the pickup system!

Remember that script we put under the prompt under the cash object under- you get the idea

Inside of that we need to do some stuff

local cash = script.Parent.Parent
local prompt = cash.ProximityPrompt
local amount = cash.Amount

prompt.Triggered:Connect(function(plr) -- .Triggered is fired when the prompt is finished, so if you add a wait it will run when the whole prompt has been filled
	local money = plr.leaderstats.Money
	
	money.Value += amount -- give them the cash
	
	cash:Destroy() -- destroy the cash object
end)

and there it is!

please comment any code errors you find, or spelling errors, spelling isnt my strong suit; as well as any exploits or bugs, ill respond as soon as I can!

cashDropTutorial.rbxl (48.6 KB)
2022-09-02 10_08_04-Window

8 Likes

not bad but adding videos wont hurt right?

Looking good. 2 notes:

1.You should add a cache / debounce for each player on the server, to prevent from spamming.
2. If you dont need to access that ‘CashObject’ from the client, you could store that in ServerStorage and clone it from here.

2 Likes

I didnt think of the 1st thing, good idea.

and while the 2nd on is do-able, there is no real reason to do it as anyone could drop it regardless so you arent saving assets from theft

I dont see the reason behind this?

This is cool only downside is this could easily get exploited and give infinite cash because you’re firing the value through a remote event. You could try adding a cap to the value.

if cashValue < maxValue then

end

it checks if the amount of cash the player has is larger or equal to the amount requested, and this is on the server side; therefor another check would be unnecassary.

1 Like