You can write your topic however you want, but you need to answer these questions:
-
What do you want to achieve? Keep it simple and clear!
I want to fix my games inventory/shop system. -
What is the issue? Include screenshots / videos if possible!
After opening the inventory and trying to equip a tower, the game errors and the tower isn’t equipped. “SelectedTowers is not a valid member of Player ‘Players.ParadoxumDoge’”
This system used to work when I used arrays, but when I tried implementing skins I swapped arrays to dictionaries to look like this: [“Scout”] = {[“Skin”] = “Default”} causing it to break.
-
What solutions have you tried so far? Did you look for solutions on the Developer Hub?
I tried reading through all the scripts that could cause this but I couldn’t find anything.
After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!
Shop Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local getDataFunc = ReplicatedStorage:WaitForChild("GetData")
local interactItemFunc = ReplicatedStorage:WaitForChild("InteractItem")
local gui = script.Parent
local exit = gui.ShopContainer.Exit
local shop = gui.Shop
local coins = gui.ShopContainer.Coins
local itemsFrame = gui.ShopContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
if playerData.OwnedTowers[itemName] then
return "Owned"
else
return "For Sale"
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
function updateItems()
coins.Text = "$" .. playerData.Coins
for i, tower in pairs(towers) do
local oldButton = itemsFrame:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local newButton = itemsFrame.TemplateButton:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = itemsFrame
local status = getItemStatus(tower.Name)
if status == "For Sale" then
if typeof(tower.Price) == "number" then
newButton.Status.Text = "$" .. tower.Price
else
newButton.Status.Text = tower.Price
end
elseif status == "Owned" then
newButton.Status.Text = "Owned"
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
end)
end
end
local function toggleShop()
gui.ShopContainer.Visible = not gui.ShopContainer.Visible
if gui.ShopContainer.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
gui.Parent.InventoryGui.InventoryContainer.Visible = false
gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
end
end
shop.Activated:Connect(toggleShop)
exit.Activated:Connect(toggleShop)
Shop Server
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local StarterGui = game:GetService("StarterGui")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local database = DataStoreService:GetDataStore("databaseone")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
local success = nil
local playerData = nil
local attempt = 1
repeat
success, playerData = pcall(function()
return database:GetAsync(player.UserId)
end)
attempt += 1
if not success then
task.wait()
end
until success or attempt == 3
if success then
if not playerData then
playerData = {
["Coins"] = 0,
["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
["AvailableCodes"] = {"Release", "update1hype"},
["ExpiredCodes"] = {},
["RedeemedCodes"] = {}
}
end
data[player.UserId] = playerData
else
player:Kick("Error received while loading data, please rejoin!")
end
end
Players.PlayerAdded:Connect(LoadData)
--[[local function SaveData(player)
if data[player.UserId] then
local success = nil
local playerData = nil
local attempt = 1
repeat
success, playerData = pcall(function()
return database:UpdateAsync(player.UserId, function()
return data[player.UserId]
end)
end)
attempt += 1
if not success then
task.wait()
end
until success or attempt == 3
end
end]]
Players.PlayerRemoving:Connect(function(player)
--SaveData(player)
data[player.UserId] = nil
end)
game:BindToClose(function()
if not RunService:IsStudio() then
for index, player in pairs(Players:GetPlayers()) do
task.spawn(function()
--SaveData(player)
end)
end
end
end)
local function getItemStatus(player, itemName)
local playerData = data[player.UserId]
if playerData.SelectedTowers[itemName] then
return "Equipped"
elseif playerData.OwnedTowers[itemName] then
return "Owned"
else
return "For Sale"
end
end
ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
local shopItem = towers[itemName]
local playerData = data[player.UserId]
if shopItem and playerData then
local status = getItemStatus(player, itemName)
if status == "For Sale" and typeof(shopItem.Price) == "number" and shopItem.Price <= playerData.Coins then
playerData.Coins -= shopItem.Price
local newTable = {["Skin"] = "Default"}
playerData.OwnedTowers[shopItem.Name] = newTable
end
return playerData
else
warn("Player/Tower data does not exist!")
end
return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player)
return data[player.UserId]
end
Inventory Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local getDataFunc = ReplicatedStorage:WaitForChild("GetData")
local interactItemFunc = ReplicatedStorage:WaitForChild("InteractItem")
local gui = script.Parent
local exit = gui.InventoryContainer.Exit
local inventory = gui.Inventory
local limit = gui.InventoryContainer.Limit
local itemsFrame = gui.InventoryContainer.ItemsFrame
local playerData = {}
local function getItemStatus(itemName)
if playerData.SelectedTowers[itemName] then
return "Equipped"
elseif playerData.OwnedTowers[itemName] then
return "Owned"
else
return "For Sale"
end
end
local function interactItem(itemName)
local data = interactItemFunc:InvokeServer(itemName)
if data then
playerData = data
updateItems()
end
end
local function getDictionaryLength(dictionary)
local length = 0
for i, key in pairs(dictionary) do
length += 1
end
return length
end
function updateItems()
local length = getDictionaryLength(playerData.SelectedTowers)
limit.Text = length .. "/5"
for i, tower in pairs(towers) do
if playerData.OwnedTowers[tower.Name] then
local oldButton = itemsFrame:FindFirstChild(tower.Name)
if oldButton then
oldButton:Destroy()
end
local newButton = itemsFrame.TemplateButton:Clone()
newButton.Name = tower.Name
newButton.TowerName.Text = tower.Name
newButton.Image = tower.ImageAsset
newButton.Visible = true
newButton.LayoutOrder = tower.Price
newButton.Parent = itemsFrame
local status = getItemStatus(tower.Name)
if status == "Equipped" then
newButton.Status.Text = "Unequip?"
else
newButton.Status.Text = "Equip?"
end
newButton.Activated:Connect(function()
interactItem(tower.Name)
end)
end
end
end
local function toggleInventory()
gui.InventoryContainer.Visible = not gui.InventoryContainer.Visible
if gui.InventoryContainer.Visible then
playerData = getDataFunc:InvokeServer()
updateItems()
gui.Parent.ShopGui.ShopContainer.Visible = false
gui.Parent.MiscellaneousGui.CodeFrame.Visible = false
gui.Parent.MiscellaneousGui.UpdateLogFrame.Visible = false
end
end
inventory.Activated:Connect(toggleInventory)
exit.Activated:Connect(toggleInventory)
Inventory Server
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local database = DataStoreService:GetDataStore("databaseone")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local MAX_SELECTED_TOWERS = 5
local data = {}
local function LoadData(player)
local success = nil
local playerData = nil
local attempt = 1
repeat
success, playerData = pcall(function()
return database:GetAsync(player.UserId)
end)
attempt += 1
if not success then
task.wait()
end
until success or attempt == 3
if success then
if not playerData then
playerData = {
["Coins"] = 0,
["SelectedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
["OwnedTowers"] = {["Scout"] = {["Skin"] = "Default"}},
["AvailableCodes"] = {"Release", "update1hype"},
["ExpiredCodes"] = {},
["RedeemedCodes"] = {}
}
end
data[player.UserId] = playerData
else
player:Kick("Error received while loading data, please rejoin!")
end
end
Players.PlayerAdded:Connect(LoadData)
--[[local function SaveData(player)
if data[player.UserId] then
local success = nil
local playerData = nil
local attempt = 1
repeat
success, playerData = pcall(function()
return database:UpdateAsync(player.UserId, function()
return data[player.UserId]
end)
end)
attempt += 1
if not success then
task.wait()
end
until success or attempt == 3
end
end]]
Players.PlayerRemoving:Connect(function(player)
--SaveData(player)
data[player.UserId] = nil
end)
game:BindToClose(function()
if not RunService:IsStudio() then
for index, player in pairs(Players:GetPlayers()) do
task.spawn(function()
--SaveData(player)
end)
end
end
end)
local function getItemStatus(player, itemName)
local playerData = data[player.UserId]
if playerData.SelectedTowers[itemName] then
return "Equipped"
elseif playerData.OwnedTowers[itemName] then
return "Owned"
else
return "For Sale"
end
end
local function getDictionaryLength(dictionary)
local length = 0
for i, key in pairs(dictionary) do
length += 1
end
return length
end
ReplicatedStorage.InteractItem.OnServerInvoke = function(player, itemName)
local shopItem = towers[itemName]
local playerData = data[player.UserId]
if shopItem and playerData then
local status = getItemStatus(player, itemName)
if status == "Equipped" then
if getDictionaryLength(playerData.SelectedTowers) > 1 then
playerData.SelectedTowers[itemName] = nil
end
elseif status == "Owned" then
local newTable = {["Skin"] = "Default"}
playerData.OwnedTowers[shopItem.Name] = newTable
if getDictionaryLength(player.SelectedTowers) > MAX_SELECTED_TOWERS then
playerData.SelectedTowers[1] = nil
end
end
return playerData
else
warn("Player/Tower data does not exist!")
end
return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player, code)
local playerData = data[player.UserId]
if table.find(playerData.AvailableCodes, code) then
local codeToRemove = table.find(playerData.AvailableCodes, code)
table.remove(playerData.AvailableCodes, codeToRemove)
table.insert(playerData.RedeemedCodes, code)
if code == "Release" then
playerData.Coins += 1000
elseif code == "update1hype" then
playerData.Coins += 1000
end
return "Successfully Redeemed"
elseif table.find(playerData.ExpiredCodes, code) then
return "Expired"
elseif table.find(playerData.RedeemedCodes, code) then
return "Already Redeemed"
elseif not code then
return playerData
else
return "Invalid"
end
end
Players.PlayerAdded:Connect(function(player)
if MarketplaceService:UserOwnsGamePassAsync(player.UserId, 117471311) then
task.wait(2)
local playerData = data[player.UserId]
if playerData.OwnedTowers["Agent"] == nil then
local newTable = {["Skin"] = "Default"}
playerData.OwnedTowers["Agent"] = newTable
end
end
end)
Code Client (Code Server is inventory)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local codeFrame = script.Parent.CodeFrame
local codeButton = script.Parent.Codes
local exitButton = codeFrame.Exit
local codeBox = codeFrame.CodeBox
local getDataFunc = ReplicatedStorage:WaitForChild("GetData")
local playerData = {}
codeButton.Activated:Connect(function()
codeFrame.Visible = not codeFrame.Visible
script.Parent.Parent.InventoryGui.InventoryContainer.Visible = false
script.Parent.UpdateLogFrame.Visible = false
script.Parent.Parent.ShopGui.ShopContainer.Visible = false
end)
exitButton.Activated:Connect(function()
codeFrame.Visible = false
end)
codeBox.FocusLost:Connect(function()
local status = getDataFunc:InvokeServer(codeBox.Text)
if typeof(status) == "string" then
if status == "Already Redeemed" then
codeBox.Text = "Code is already redeemed!"
task.wait(1)
codeBox.Text = "Enter code here"
elseif status == "Invalid" then
codeBox.Text = "Code is invalid!"
task.wait(1)
codeBox.Text = "Enter code here"
elseif status == "Expired" then
codeBox.Text = "Code is expired!"
task.wait(1)
codeBox.Text = "Enter code here"
else
codeBox.Text = "Code successfully redeemed!"
task.wait(1)
codeBox.Text = "Enter code here"
end
end
end)
(this is my first time posting on devforum; please tell me if I did anything wrong)
Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.