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”?
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.
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
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.
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)
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)
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