How To Make Client-Sided Part That Gives Currency (leaderstats)

Client-Sided Part That Gives Currency on Touch

I’m making a game that is a round-based game and I want to make a part that when a player touch, (s)he will receive coins or any other leaderstats. I made a server-sided part for this but it always making a mess where players are receiving multiple of coins

How my game works:

I have a script that pick a random map from a folder inside the ServerStorage and at the end there will be reward for completing that map. The win pad (part giving coins) is inside a model

Solution I Tried

I tried adding LocalScript inside the StarterGui since that is client-sided. Here’s the script:

local part = game.Workspace:WaitForChild("Map1", "Map2", "Map3").End -- end is the part that will gives the reward
canGet = true

local function onTouch(otherPart)
	local humanoid = otherPart.Parent:FindFirstChild('Humanoid')
	if humanoid then
		local player = game.Players:FindFirstChild(otherPart.Parent.Name)
		if player and canGet then
			canGet = false
			player.leaderstats.Wins.Value = player.leaderstats.Wins.Value + 1 -- leaderstats 1
			player.leaderstats.Gems.Value = player.leaderstats.Gems.Value + 5 -- leaderstats 2
			part:Destroy()
		end
	end
end

part.Touched:Connect(onTouch)

part.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("Humanoid") then
		hit.Parent:BreakJoints()
	end
end)

you need to fire the server to change the players leaderstats because if the client changes it and you try to purchase something with a server script the server wont see the change in currency values

Instead of having these on the client, transfer them over to a ServerScript and use a RemoteEvent since ClientSided values don’t show nor save on the Server.

1 Like

So it should look like this?:

local part = game.Workspace:WaitForChild("Map1", "Map2", "Map3").End -- end is the part that will gives the reward
canGet = true

local function onTouch(otherPart)
	local humanoid = otherPart.Parent:FindFirstChild('Humanoid')
	if humanoid then
		local player = game.Players:FindFirstChild(otherPart.Parent.Name)
		if player and canGet then
			canGet = false
			game.ReplicatedStorage.RemoteEvent:FireAllClients()
			part:Destroy()
		end
	end
end

part.Touched:Connect(onTouch)

part.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("Humanoid") then
		hit.Parent:BreakJoints()
	end
end)

Then there will be a script inside ServerScriptStorage?:

game.ReplicatedStorage.RemoteEvent.OnClientEvent:Connect(function()
	local player = game.Players.LocalPlayer -- ?
	player.leaderstats.Wins.Value = player.leaderstats.Wins.Value + 1 -- leaderstats 1
	player.leaderstats.Gems.Value = player.leaderstats.Gems.Value + 5 -- leaderstats 2
end)

1 Like

When firing from client to server you must put a player parameter because ServerScriptService doesn’t have access to LocalPlayer

If you want to make your code cleaner do this
+= example: Player.leaderstats.Wins.Value += 1 -- leaderstats 1

game.ReplicatedStorage.RemoteEvent.OnClientEvent:Connect(function(Player)
	Player.leaderstats.Wins.Value = player.leaderstats.Wins.Value + 1 -- leaderstats 1
	Player.leaderstats.Gems.Value = player.leaderstats.Gems.Value + 5 -- leaderstats 2
end)
1 Like

You don’t have to have a parameter when firing the :FireAllClients() event.

So;
LocalScript inside StarterPlayer:

local part = game.Workspace:WaitForChild("Map1", "Map2", "Map3").End -- end is the part that will gives the reward
canGet = true

local function onTouch(otherPart)
	local humanoid = otherPart.Parent:FindFirstChild('Humanoid')
	if humanoid then
		local player = game.Players:FindFirstChild(otherPart.Parent.Name)
		if player and canGet then
			canGet = false
			game.ReplicatedStorage.RemoteEvent:FireAllClients()
			part:Destroy()
		end
	end
end

part.Touched:Connect(onTouch)

part.Touched:Connect(function(hit)
	if hit.Parent:FindFirstChild("Humanoid") then
		hit.Parent:BreakJoints()
	end
end)

and Scipt inside the ServerScriptService:

game.ReplicatedStorage.RemoteEvent.OnClientEvent:Connect(function(Player)
	Player.leaderstats.Wins.Value = Player.leaderstats.Wins.Value + 1 -- leaderstats 1
	Player.leaderstats.Gems.Value = Player.leaderstats.Gems.Value + 5 -- leaderstats 2
end)

Is this part of script allowed? Adding multiple models inside WaitForChild()

I would just play it safe by creating multiple variables.

But is it still allowed? The script will be too long by creating multiple variables. I will mark that solution if there’s nothing wrong on that script

Wait I’m testing it I will send the results later

1 Like

As it turns out Map1.End only works because :WaitForChild is only 1 time, example Instance:WaitForChild("Instance", 0.5 --The first one is the child we are looking and the second one is a cooldown)

I prefer using Players:GetPlayerFromCharacter() instead of :FindFirstChild()

I would prefer making a table like this:

local Players = game:GetService("Players")

local MapTable = {
	Map1 = workspace.Map1,
	Map2 = workspace.Map2,
	Map3 = workspace.Map3,
}

for i,v in pairs(MapTable) do
	
	v.End.Touched:Connect(function(Hit)
		local IsPlayer = Players:GetPlayerFromCharacter(Hit.Parent)
		
		if IsPlayer then
		      print("This is a player!")
		else
		      print("This is not a player!")
		end
		
	end)
	
end

Then add this inside the server script service?

I will try, thanks. (I will mark it solution after I test)

1 Like

Yes you can add it in ServerScriptService

Wait a minute, is this for LocalScript in the StarterGui? Do I need to add

game.ReplicatedStorage.RemoteEvent:FireAllClients()

Betwee

I’m so sorry, I’m confused T ~ T

You mean the script when the player hits the part?

If that’s what you meant you can put it in StarterPlayer - StarterCharacterScripts

Gonna type it step by step to be cleared since I can’t process this on my brain. Please correct if I’m wrong

  • Add Script or LocalScript in the StarterPlayer - StarterCharacterScripts (Which script???)
  • Type this:
local Players = game:GetService("Players")

local MapTable = {
	Map1 = workspace.Parts
}

for i,v in pairs(MapTable) do

	v.End.Touched:Connect(function(Hit)
		local IsPlayer = Players:GetPlayerFromCharacter(Hit.Parent)

		if IsPlayer then
			print("This is a player!")
		else
			print("This is not a player!")
		end

	end)

end
  • Then add this inside the server script service?
game.ReplicatedStorage.RemoteEvent.OnClientEvent:Connect(function(Player)
	Player.leaderstats.Wins.Value = player.leaderstats.Wins.Value + 1 -- leaderstats 1
	Player.leaderstats.Gems.Value = player.leaderstats.Gems.Value + 5 -- leaderstats 2
end)

It doesn’t work if this is what you mean

That’s what I meant

Cannot reply anymore since it’s my past bedtime I will reply tommorow

Doesn’t work. I’m wondering if I need to add

game.ReplicatedStorage.RemoteEvent:FireAllClients()

between

if IsPlayer then
		      print("This is a player!")
		else

like this:

if IsPlayer then
			print("This is a player!")
			game.ReplicatedStorage.RemoteEvent:FireAllClients()
		else

And also. This means the part that will give currency, right?

So, why there’s .Parent on the hit? The parent of that part is the map or the model

If your wondering why Hit.Parent it is the model of the players character

and also it needs to be :FireServer() since you are doing Client to Server

game.ReplicatedStorage.RemoteEvent:FireServer()

1 Like

This is not a good example of what you should do in actual games, so I figured I’d rewrite it so it makes sense

First, you want to tag the parts with CollectionService. So, in your command bar, type this:

game:GetService("CollectionService"):AddTag(workspace.Map1.End, "endPart")

This’ll tag the end part of the 1st map, do the same for any other maps you have, just replace the Map1 with whatever its named.

Next, I don’t see the reason for making a client sided part to then have to send to the server, it’s useless. So, insert a Script in ServerScriptStorage and type out the following (read first so you’re not just copying code)

local collectionService = game:GetService("CollectionService")
local endParts = collectionService:GetTagged("endPart")
--so what we're doing is getting the parts that are tagged with endPart. GetTagged returns a table of the objects that are tagged with "endPart".

for _, part in pairs(endParts) do
    --this means for each end part, we run this code
    local debounce = false
    part.Touched:Connect(function(hitPart)
        if debounce then return end
        
        debounce = true
        --script is placed on a cooldown
        local plr = game.Players:GetPlayerFromCharacter(hitPart.Parent)

        if plr then
            --found player
            plr:FindFirstChild("leaderstats").Wins.Value += 1
            plr:FindFirstChild("leaderstats").Gems.Value += 5
            
            wait(3)
            debounce = false
        end
    end)
end

Now, if you want to add another EndPart, you just need to tag it. Simple!