Unequip and Equip text

I am making a shop for my game and set it up so players can buy trails and then equip them in their inventory. The issue is, it keeps the previously equipped trail’s equip text to “Unequip” instead of changing it back to “Equip” since it is not equipped anymore. If you are a little confused, maybe this picture will help (Do not mind the disgusting GUI):


As you can see, there are two items shown as equipped when in reality, the orange one is equipped. How do I make it so the trail equipped is the only one that has the button text as “Unequip.” Here is my script to updateInventory:

local player = game:GetService("Players").LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local shopFrame = playerGui:WaitForChild("Shop"):WaitForChild("Frame")
local itemScroller = script.Parent
local itemPreview = script.Parent.Parent.ItemPreview
local trailsFolder = game.ReplicatedStorage:WaitForChild("Trails")
local ownedTrailsFolder = player:WaitForChild("OwnedTrails")
local shopFrame = script.Parent.Parent
local invFrame = playerGui:WaitForChild("Inventory")




shopFrame.Visible = false
itemPreview.Visible = false


local itemsFolder = game.ReplicatedStorage:WaitForChild("Trails")

function updateInventory()
	
	for i, child in pairs(invFrame.ScrollingFrame:GetChildren()) do
		if child:IsA("Frame") then child:Destroy() end
	end
	
	
	local ownedTrails = ownedTrailsFolder:GetChildren()
	print(ownedTrails)
	
	table.sort(ownedTrails, function(a, b)
		return trailsFolder[a.Name].Price.Value < trailsFolder[b.Name].Price.Value or trailsFolder[a.Name].Price.Value == trailsFolder[b.Name].Price.Value and a.Name < b.Name
	end)
	
	
	for i, trail in pairs(ownedTrails) do
		local item = script.Item:Clone()
		item.SelectButton.Text = "Equip"
		item.Title.Text = trail.Name
		item.Visible = true
		
		
		item.Parent = invFrame.ScrollingFrame
		
		
		
		
		
		item.SelectButton.MouseButton1Click:Connect(function()
			game.ReplicatedStorage.TrailSelectedRE:FireServer(false, trail)
			
			
			if item.SelectButton.Text == "Equip" then
				item.SelectButton.Text = "Unequip"
			elseif item.SelectButton.Text == "Unequip" then
				print("L")
				
			
			end

			
			



		end)
	end
	
	
	
	
end		

Also, if you have time and know how to, how do you make it so when the player clicks the trail button when it says “Unequip”, the trail is removed from their avatar and the buttontext = “Equip”?

1 Like

Well you don’t really have to remove the trail from their avatar, you can just edit it’s properties to that of the selected trail. Or, alternatively, if there is no trail equipped you can set the Trail | Roblox Creator Documentation property to false.

To solve this you should either have a variable for the currently selected trail or loop through the owned trails and set the item.SelectButton.Text properties.

You can replace your current for-loop with this:

local TrailSelectedRE = game:GetService("ReplicatedStorage"):WaitForChild("TrailSelectedRE")

local SelectedTrail
for i, trail in pairs(ownedTrails) do
	local item = script.Item:Clone()
	item.SelectButton.Text = "Equip"
	item.Title.Text = trail.Name
	item.Visible = true
	item.Parent = invFrame.ScrollingFrame
	
	item.SelectButton.MouseButton1Click:Connect(function()
		if item == SelectedTrail then
			item.SelectButton.Text = "Equip"
			
			-- fire remote to server for something
			
			SelectedTrail = nil
		else
			item.SelectButton.Text = "Unequip"
			if SelectedTrail then
				SelectedTrail.SelectButton.Text = "Equip"
			end
			
			TrailSelectedRE:FireServer(false, trail)
			
			SelectedTrail = item
		end
	end)
end

I can’t really help you anymore than this unless you show the serverscript function the TrailSelectedRE remote is connected to.

1 Like
if item.SelectButton.Text == "Equip" then
	--loop through all the frames, and set their state to "Equip"
	for _, frame in pairs(invFrame.ScrollingFrame:GetChildren()) do 
		if not frame:IsA("Frame") then continue end
		frame.SelectButton.Text = "Equip"
	end
	--then set the chosen item state to "Unequip"
	item.SelectButton.Text = "Unequip"
end
1 Like

It has this error for line 54 which is frame.SelectButton.Text = “Equip”:
SelectButton is not a valid member of UIGridLayout “Players.romeanyguy10.PlayerGui.Inventory.ScrollingFrame.UIGridLayout”
I have a UIGridLayout to sort the item template that is being duplicated.

While this is an option, I wouldn’t recommend the looping approach because it grows slower the more trails there are. Besides that, it just isn’t as performant.

I’ve updated my answer with relevant code that should work.

Just fixed my reply, although as @BuilderBob25620 said my reply isn’t the most efficient solution, it’s just makes sure it wont error by looping through each frame when one is clicked. Personally I would keep the equipped item inside a variable and before updating it(each time the button is pressed) would set EquippedItem.SelectButton.Text to Equip.

Hey, this works! By the way, do you know how to completely eliminate the trail from the character’s avatar since I think Trail.Enabled will cause problems and make the script more complicated since you will need to check if its enabled or not when you are equipping it again.

You don’t actually have to check if it’s enabled. You can just set it to true while equipping it. Can you please show me the server function you are using to actually add the trail to the character in the first place? I don’t completely know how this system works because I’m missing what’s actually happening. I’m guessing you could just destroy the trail but I may be wrong.

Yeah, sure thing:

local trails = game.ReplicatedStorage:WaitForChild("Trails")


function createAtchs(char)

	local hrp = char:WaitForChild("HumanoidRootPart")

	local atchTop = Instance.new("Attachment", hrp)
	atchTop.Name = "TrailTop"
	atchTop.Position = Vector3.new(0, 0.766, 0)

	local atchBtm = Instance.new("Attachment", hrp)
	atchBtm.Name = "TrailBottom"
	atchBtm.Position = Vector3.new(0, -0.766, 0)
end

game.ReplicatedStorage.TrailSelectedRE.OnServerEvent:Connect(function(plr, buying, trail)
	

	if buying and not plr.OwnedTrails:FindFirstChild(trail.Name) then
		print("Message Recieved")

		
		local price = trail.Price.Value
		local coins = plr.leaderstats.Coins

		if price <= coins.Value then

			coins.Value -= price


			trail:Clone().Parent = plr.OwnedTrails
		end
	

	elseif not buying and plr.OwnedTrails:FindFirstChild(trail.Name) then
		
		print("Event fired")

		local char = plr.Character

		if not char or not char:FindFirstChild("HumanoidRootPart") then return end

		for i, child in pairs(char.HumanoidRootPart:GetChildren()) do


			if child:IsA("Trail") then child:Destroy() end
		end


		local newTrail = trail:Clone()

		newTrail.Attachment0 = char.HumanoidRootPart.TrailTop
		newTrail.Attachment1 = char.HumanoidRootPart.TrailBottom

		newTrail.Parent = char.HumanoidRootPart
	end
end)
1 Like

Okay, thanks. Also, very sorry for the late information.

Now that I have this information, your for-loop in the local script should be this:

local TrailSelectedRE = game:GetService("ReplicatedStorage"):WaitForChild("TrailSelectedRE")

local SelectedTrail
for i, trail in pairs(ownedTrails) do
	local item = script.Item:Clone()
	item.SelectButton.Text = "Equip"
	item.Title.Text = trail.Name
	item.Visible = true
	item.Parent = invFrame.ScrollingFrame
	
	item.SelectButton.MouseButton1Click:Connect(function()
		if item == SelectedTrail then
			item.SelectButton.Text = "Equip"
			
			SelectedTrail = nil
		else
			item.SelectButton.Text = "Unequip"
			if SelectedTrail then
				SelectedTrail.SelectButton.Text = "Equip"
			end
			
			SelectedTrail = item
		end
		
		TrailSelectedRE:FireServer(trail)
	end)
end

and the function in the server-script should be this:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local trails = ReplicatedStorage:WaitForChild("Trails")
local TrailSelectedRE = ReplicatedStorage:WaitForChild("TrailSelectedRE")

local function createAtchs(char)
	local hrp = char:WaitForChild("HumanoidRootPart")
	
	local atchTop = Instance.new("Attachment", hrp)
	atchTop.Name = "TrailTop"
	atchTop.Position = Vector3.new(0, 0.766, 0)
	
	local atchBtm = Instance.new("Attachment", hrp)
	atchBtm.Name = "TrailBottom"
	atchBtm.Position = Vector3.new(0, -0.766, 0)
end

local function RemoveTrails(rootPart)
	for _, child in pairs(rootPart:GetChildren()) do
		if child:IsA("Trail") then
			child:Destroy()
		end
	end
end

local function AddTrail(rootPart, trail)
	if rootPart:FindFirstChild(trail.Name) then
		RemoveTrails(rootPart)
	else
		RemoveTrails(rootPart)
		
		local newTrail = trail:Clone() do
			newTrail.Attachment0 = rootPart.TrailTop
			newTrail.Attachment1 = rootPart.TrailBottom
			newTrail.Enabled = true
			newTrail.Parent = rootPart
		end
	end
end

TrailSelectedRE.OnServerEvent:Connect(function(plr, trail)
	local ownedTrail = plr.OwnedTrails:FindFirstChild(trail.Name)
	
	if not ownedTrail then
		print(plr.Name," is buying the ",trail.Name," trail.")
		
		local price = trail.Price.Value
		local coins = plr.leaderstats.Coins
		
		if price <= coins.Value then
			coins.Value -= price
			
			local newTrail = trail:Clone() do
				newTrail.Parent = plr.OwnedTrails
				
				AddTrail(plr, newTrail)
			end
		end
	elseif ownedTrail then
		local character = player.Character
		if not character then return end
		
		print(plr.Name," equipped ",trail.Name," trail.")
		
		AddTrail(character.PrimaryPart, ownedTrail)
	end
end)

Thank you, this works. I have a general question, if I was to add a new item to the shop, say a pet for example, how would I go about adding the pet to the inventory of the player and making it the same concept as the trail? (SelectButton, Equip, Unequip, etc).

Wait, I have come into a small problem with the script. Whenever I try to buy a trail from the shop, it comes up with this error: ServerScriptService.Leaderstats:117: attempt to index boolean with ‘Name’
It is referring to the line

local ownedTrail = plr.OwnedTrails:FindFirstChild(trail.Name)

How do I fix this?

This is most likely a problem with a line somewhere in your local script. Not the lines of code that I provided, but somewhere else that still is sending a boolean value along with the trail.

Use Control + F if on a windows or Command + F if you are on a mac to search for TrailSelectedRE in your local script. Then see if at any of the :FireServer calls it passes any values that are not a trail.

However, even if you fix this there should still be a way to prevent this on the server script. So add this line to the beginning of the connected function:

TrailSelectedRE.OnServerEvent:Connect(function(plr, trail)
	if not trail or typeof(trail) ~= "Instance" then return end
	
	local ownedTrail = plr.OwnedTrails:FindFirstChild(trail.Name)
	
	--- Other code

I think I fixed that error. In my local script, for the updateshop function, I was sending “true” to the server script to let it know that it should update shop, and not update inventory. Anyway, I have a new error now which is the AddTrail function you gave me for line:

newTrail.Attachment0 = rootPart.TrailTop

The error is: TrailTop is not a valid member of Player “Players.romeanyguy10”

The weird thing is, when you equip it in the inventory, you do not get this error. It is just when you buy it from the shop.

Oh. My bad. I just now realized that I wasn’t passing the character to the function. Try this instead:

local function AddTrail(rootPart, trail)
	if not rootPart or not trail then return end
	
	if rootPart:FindFirstChild(trail.Name) then
		RemoveTrails(rootPart)
	else
		RemoveTrails(rootPart)
		
		local newTrail = trail:Clone() do
			newTrail.Attachment0 = rootPart.TrailTop
			newTrail.Attachment1 = rootPart.TrailBottom
			newTrail.Enabled = true
			newTrail.Parent = rootPart
		end
	end
end

TrailSelectedRE.OnServerEvent:Connect(function(plr, trail)
	if not trail or typeof(trail) ~= "Instance" then return end -- we still want this here just in case
	
	local ownedTrail = plr.OwnedTrails:FindFirstChild(trail.Name)
	
	local rootPart = if player.Character then player.Character.PrimaryPart else nil

	if not ownedTrail then
		print(plr.Name," is buying the ",trail.Name," trail.")
		
		local price = trail.Price.Value
		local coins = plr.leaderstats.Coins
		
		if price <= coins.Value then
			coins.Value -= price
			
			local newTrail = trail:Clone() do
				newTrail.Parent = plr.OwnedTrails
				
				AddTrail(rootPart, newTrail)
			end
		end
	elseif ownedTrail then
		print(plr.Name," equipped ",trail.Name," trail.")
		
		AddTrail(rootPart, ownedTrail)
	end
end)

Thank you! The only issue is now that when the player buys the trail from the shop, it does not set the inventory equip button to “Unequip” which makes it seem like the item is not equipped when in reality, it is. So basically when you buy it from the shop and it goes to your avatar, when you equip it on the inventory it does the opposite of what it should. I guess an easy solution for this is to just not apply the trail to the character when it is purchased?

Not necessarily. The trail is being applied to the player on purchase on the server script. This has nothing to do with changing text on the client. You just have to add the same logic on the client.

Replace this line in the local script:

with this one:

local price = trail.Price.Value
local coins = plr.leaderstats.Coins.Value

if player.OwnedTrails:FindFirstChild(trail.Name) or (price <= coins) then
	item.SelectButton.Text = "Unequip"
else
	item.SelectButton.Text = "Unequip"
end

It still is not changing the selectbutton text when a trail is purchased.

Oh. That’s probably because you only seem to be looping through the players owned trails instead all of them.

So what should I change/do if anything?