Trying to create a "bought" door that disappears when a surfaceGUI button is pushed

I’m a little confused as to why my local script isn’t working on this issue… is there something I’m missing here?

local button = script.Parent

button.MouseButton1Down:Connect(function()
	local surfaceGUI = script.Parent.Parent
	local doorPart = script.Parent.Parent.Parent
	local playerName = game.Players.LocalPlayer.Name
	local moneyName = tostring(playerName.."Money")
	local moneyValue = game.ReplicatedStorage.PlayerMoney.moneyName.Value
	local Cost = script.Parent.Parent.Cost.Value
	local MVMC = moneyValue-Cost
	print "a"
	if not MVMC <= -1 then
		print("b")
		doorPart.Transparency = 1
		doorPart.CanCollide = false
		surfaceGUI.Active = false
		moneyValue = MVMC
	end
end)

Here’s how everything is positioned in the library if it’s any help… The “Cost” IntVal is at 50 right now and the starting money (another IntVal is at 100)
image
Sorry I’m still rather new to scripting. If there’s any other info you need, please just ask :heart:

  1. Doing tostring(playerName .. "Money") is redundant since concatenating strings returns a string.

  2. When you set the moneyValue variable, you need to do PlayerMoney[moneyName].Value since moneyName is a string. If you leave it as you did now, it would essentially be like PlayerMoney."moneyValue".Value which isn’t the correct syntax.

  3. Do not have transactions occur in a Local Script, exploiters can and will change up the code inside of it whenever they are able to. They can unfairly gain access to the door. Therefore, set up a RemoteEvent that fires from the client to the server and the server does the transaction in a Server Script. If the player has enough money, then using the PhysicsServerice, we will set the door and the player’s character uncollidable.

Read these if you are new to them:
Collision Filtering via PhysicsService
Remote Events
Replication Boundary and Game Security

Here’s what I’m referring to:

--local script
local remoteEvent = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvent")

button.MouseButton1Down:Connect(function()
	
	remoteEvent:FireServer()
	
end)

--server script
local rs = game:GetService("ReplicatedStorage")
local remoteEvent = rs:WaitForChild("RemoteEvent")
local ps = game:GetService("PhysicsService")
local door = workspace:WaitForChild("Doors").Door

ps:CreateCollisionGroup("Door")
ps:SetPartCollisionGroup(door, "Door")

remoteEvent.OnServerEvent:Connect(function(player) --the player parameter is automatically passed from the requesting client
	
	local playerMoney = rs:WaitForChild("PlayerMoney")
	local money = playerMoney[player.Name .. "Money"]
	local cost = door.SurfaceGui.Cost
		
	if money.Value >= cost.Value then
		
		local char = player.Character or player.CharacterAdded:Wait()
		ps:CreateCollisionGroup(player.Name)
		
		--player has enough money!
		
		for i, v in pairs(char:GetChildren()) do
			
			if v:IsA("BasePart") then --any type of body part
			
				ps:SetPartCollisionGroup(v, player.Name)
			
			end
			
		end
		
		ps:CollisionGroupSetCollidable("Door", player.Name, false)
		
	end
	
end)

I apologize for completely changing your script, but this is the more secure and efficient option. If you have any questions, feel free to DM me!

Hope that helps!

1 Like
-- Its best to have all variables at the top of the script for readability
local button = script.Parent
local surfaceGUI = script.Parent.Parent
local doorPart = script.Parent.Parent.Parent
local player = game.Players.LocalPlayer -- its better to have the Player, not the name
local Cost = script.Parent.Parent.Cost.Value


button.MouseButton1Down:Connect(function()
	-- check if player has enough money to purchase
	if player.leaderstats.Money.Value - Cost >= 0 then
		doorPart.Transparency = 1
		doorPart.CanCollide = false
		surfaceGUI.Active = false
		-- player pays for the door
		player.leaderstats.Money.Value = player.leaderstats.Money.Value - Cost
	end
end)

I changed the way you’re managing the player’s money to use leaderstats because its much easier to use, and will show the player’s money with the default player list. To use leaderstats, put the following code into a Script in ServerScriptStorage

game.Players.PlayerAdded:Connect(function(player)
	-- this will add a leaderstats value to the player
	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"
	
	local cash = Instance.new("IntValue", leaderstats)
	cash.Name = "Money"
end)

With this, you can also save the player’s money between session using DataStores. I would personally recommend DataStores2 for this.

Just to add on @TheCarbyneUniverse’s suggestion on security, you may want to implement some position checks on the server just in case an exploiter teleports past the door or deletes the door locally.

1 Like