How to make coin that disappears after a player collected, but doesn't affect to the other players (coin generated after got collected)

Hi there!
I have made collectable coins (called “nuggets”) with a currency stats that have data store that I have found from some yt videos, and this is how it works:

  1. The “nuggets” disappear when collected
  2. After got collected, there is a waiting time to respawn the nuggets
  3. The stats added some points on it, indicates how much “nuggets” does a player have right now

However, I wonder if we can make the “nuggets” disappear and respawn ONLY to the player who collected, but not the whole server!

Here is the “nuggets” script:

local DataStoreService = game:GetService("DataStoreService")
local CoinsDataStore = DataStoreService:GetDataStore("CoinsDataStore")

local RespawnTime = 10 -- I set the timer 10 seconds waiting

function savePlayerData(player)
	local leaderstats = player:FindFirstChild("leaderstats")
	if leaderstats then
		local coins = leaderstats:FindFirstChild("Nuggets")
		if coins then
			CoinsDataStore:SetAsync(player.UserId .. "_Nuggets", coins.Value)
		end
	end
end

function loadPlayerData(player)
	local success, result = pcall(function()
		return CoinsDataStore:GetAsync(player.UserId .. "_Nuggets")
	end)
	if success and result ~= nil then
		local leaderstats = player:FindFirstChild("leaderstats")
		if leaderstats then
			local coins = leaderstats:FindFirstChild("Nuggets")
			if coins then
				coins.Value = result
			end
		end
	end
end

function onTouch(otherPart)
	local player = game.Players:GetPlayerFromCharacter(otherPart.Parent)
	if player then
		local leaderstats = player:FindFirstChild("leaderstats")
		if not leaderstats then

			return
		end
		local coins = leaderstats:FindFirstChild("Nuggets")
		if coins then
			coins.Value = coins.Value + 5 -- After collected, the nugget will give you +5 on the currency stats
		end
		script.Parent.CanTouch = false 
		script.Parent.Transparency = 1
		wait(RespawnTime)
		script.Parent.CanTouch = true
		script.Parent.Transparency = 0

	end
end

function onPlayerRemoving(player)
	savePlayerData(player)
end

function onPlayerAdded(player)
	loadPlayerData(player)
end

script.Parent.Touched:Connect(onTouch)
game.Players.PlayerRemoving:Connect(onPlayerRemoving)
game.Players.PlayerAdded:Connect(onPlayerAdded)

…and the “stats” script:

game.Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	local Coin = Instance.new("IntValue")
	Coin.Name = "Nuggets" -- The name of the currency!
	Coin.Value = 0
	Coin.Parent = leaderstats
end) 

I hope guys can help me on this one, really appriciated if you also give me explanation of the solution, I want to know more about coding!

Thanks for reading!
(My very first post lol)

4 Likes

This would have to be on the client, of course, but I do not recommend doing this, as the client is able to be faked. The player could easily just teleport to all the coins.

2 Likes

oh… I have read some posts and its kinda related to local script (right?), idk how to use it while i have these mentioned scripts, or should i just paste it into the local script?

1 Like

As I previously stated, I don’t recommend doing it, because the client could easily teleport to all the coins, although you may see less exploiting instances, because of byfron (correct me if wrong).

You should look into local scripts, anyways. And understand client-server boundaries. It’s not too hard.

3 Likes

Pretty sure the player can teleport to the coins no matter what.

Either way, everything should be server-side except display of the coins - that should be local to the player.

4 Likes

I think others are not looking at the bigger picture;

you could make it so that every coin is being tracked on server script i.e. track who has collected the coin if you check if the player has obtained the coin then you can then prevent them from potentially cheating by “touching” it again some how (do sanity checks to ensure the player is close to a coin). Once the player touches the coin fire to the client (or also have a .touched event on client to handle deletion) in local script to delete the coin from their perspective (or in your case, set invisible for client, then after a certain amount of time remove player from tracking on server and set to visible for client).

the above should allow you to get you to the solution you are looking for.

1 Like

This is true, but players can easily do this on a server side script too, and it would be more annoying if players lost all of their nuggets because a player was teleporting to all of the. Alongside this, hacked clients will try to give their clients an inconspicuous experience, and teleporting them everywhere in the players clear view is not the way to go. I’d suggest proceeding with the client script, that way the hacker can only damage their own experience.

1 Like

You can make it so there’s a RemoteEvent that tells the client to make this nugget invisible, then after a certain time the RemoteEvent tells the client to make the nugget visible again.
To prevent the player from taking the same nugget twice you can add the player in a Dictionary Table like so:

local playerList = {}
local cooldownTimer = 5
nugget.Touched:Connect(function(part)
	local player = game:GetService("Players"):GetPlayerFromCharacter(part.Parent)
	
	if player then
		if playerList[player.UserId] == nil or playerList[player.UserId] <= os.clock() then
			playerList[player.UserId] = os.clock() + cooldownTimer
			-- Give player + 1 nugget and tell the client to make this nugget invisible
			yourRemoteEvent:FireClient(player, "hideNugget", nugget)
			
			task.delay(cooldownTimer, function() -- Replace the cooldownTimer number with how many seconds it will take for it to reappear for this client.
				yourRemoteEvent:FireClient(player, "showNugget", nugget)
			end)
		end
	end
end)

-- On Client, you only gotta put this somewhere in a localscript or make a new one named "nuggetHandler" or something.
yourRemoteEvent.OnClientEvent:Connect(function(action, model)
	if action == "hideNugget" then
		-- Make the model transparent or move it elsewhere
	elseif action == "showNugget" then
		-- Make the model visible or move it where it belongs.
	end
end)
2 Likes

Interesting… So simply i have to use local script (?) and RemoteEvent (?) to make the coin disappears (invisible) after a player touched > delete “tracking” during respawn time > set it visible again, right?

I have no idea abt delete player’s tracking, how to prevent?

1 Like

That’s new for me…
Seems like local script won’t change the whole server, it’s more safer to code huh?

This is mostly correct, local scripts run on the client’s device exclusively. However, exploiters can access aspects of server scripts if a remote event or other method of client server communication is created. If done properly, exploiters are restricted to only what they can do on the client.

2 Likes

Cool, but that’s kinda not what I meant
I want to make the nuggets visible again after players collected, after that players still can collect it again. Therefore, they are allowed to collect the nuggets at the same place, same nuggets!

And yes, I will try RemoteEvent, ty for your suggestion!

1 Like

it will require serverscript, localscript & remote event

for tracking you can use a table

local playertable={}

--player .touched coin on server
 --should also compare .magnitudes and make sure plr is actually at the coin
if not table.find(playertable, player) then
table.insert(playertable,player)
--awardcoin
--fireclient to invisible the coin
wait(15)
--fireclient visible coin
table.remove(playertable, table.find(playertable, player) )
else
--player already obtained this coin recently
end
1 Like

As others have said, you’ll need to do this on the client. Here’s what I would do.

-- Client
nugget.Touched:Connect(function(part)
     local player = CharacterUtils.getPlayerFromCharacter(part)
    if player ~= Players.LocalPlayer then
        return
    end

    if nuget:GetAttribute("IsPickedUp_" .. player.UserId) then
        return
    end


    NuggetPickupRemoteEvent:FireServer(nugget)
    nugget:SetAttribute("IsPickedUp_" .. player.UserId, true) -- Immediately render on client
end)

nugget:GetAttributeChangedSignal("IsPickedUp_" .. Players.LocalPlayer.UserId):Connect(function()
     if nuget:GetAttribute("IsPickedUp_" .. Players.LocalPlayer.UserId)  then
        -- Hide nugget
        nugget.Transparency = 1
     else
        nugget.Transparency = 0
     end
end)
-- Server
nuggetRemoteEvent.OnServerEvent:Connect(function(player)
    -- Validate character distance
    nugget:SetAttribute("IsPickedUp_" .. player.UserId, true)
    task.delay(3, function()
         -- Reset nugget state (obviously do whatever you want here to refresh it)
         nugget:SetAttribute("IsPickedUp_" .. player.UserId, nil)
    end)
end)

Here’s the getPlayerFromCharacter helper method I used.

3 Likes

I’ve edited the code and made it easier to understand

1 Like