You can write your topic however you want, but you need to answer these questions:
-
What do you want to achieve?
Ability to chose towers in the GUI menu -
What is the issue?
The GUI isn’t populated and the errorattempt to concatenate string with nil
appears in the console -
What solutions have you tried so far?
Nothing so far, after I changed the name of “Gunslinger” to “GunSlinger” these errors appeared but after reverting the name in the tables, the error still appears.
ShopServer Script, In SSS, Handles DataSaving, Loading, and Shop purchasing and selecting
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local towers = require(ReplicatedStorage:WaitForChild("TowerShop"))
local database = DataStoreService:GetDataStore("database")
local RunService = game:GetService("RunService")
local MAX_SELECTED_TOWERS = 5
local data = {
}
local defaultData = {
["Gems"] = 100,
["SelectedTowers"] = {"Cutler"},
["OwnedTowers"] = {"Cutler", "GunSlinger"}
}
local function LoadData(player)
local success = nil
local playerData = nil
local attempt = 1
while not success and attempt < 4 do
success, playerData = pcall(function()
return database:GetAsync(player.UserId)
end)
if not success then
attempt += 1
warn(playerData)
task.wait()
end
end
--[[ Repeat until is often unreliable
repeat
until
]]
if success then
print("Data Loaded Successfully") --Why connection established??
if not playerData then
print("New player, giving default data")
playerData= {
["Gems"] = 100,
["SelectedTowers"] = {"Cutler"},
["OwnedTowers"] = {"Cutler", "GunSlinger"}
}
end
data[player.UserId] = playerData
else
warn("Unable to get data for player", player.UserId)
player:Kick("There was a problem loading your data! Message the developer on Roblox if this problem continues!")
end
end
Players.PlayerAdded:Connect(LoadData)
local function SaveData(player)
if data[player.UserId] then
local success = nil
local playerData = nil
local attempt = 1
while not success and attempt < 4 do
success, playerData = pcall(function()
database:SetAsync(player.UserId, data[player.UserId])
--[[ UpdateAsync overcomplicates things in my opinion
return database:UpdateAsync(player.UserId, function()
return data[player.UserId]
end)
]]
end)
if not success then
attempt += 1
warn(playerData)
task.wait()
end
end
--[[ Again repeat until is often unreliable
repeat
until success == true or attempt == 3
]]
if success then
print("Data saved successfully")
else
warn("Unable to save data for", player.UserId)
end
else
warn("No Data for", player.UserId)
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
else
print("Shutting down inside the studio")
end
end)
-- I won't bother changing anything else
local function GetItemStatus(player, itemName)
local playerData = data[player.UserId]
if table.find(playerData.SelectedTowers, itemName) then
return "Equipped"
elseif table.find(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 shopItem.Price <= playerData.Gems then
-- Purchase
playerData.Gems -= shopItem.Price
table.insert(playerData, shopItem.Name)
elseif status == "Owned" then
--Equip Tower
table.insert(playerData.SelectedTowers, shopItem.Name)
if #playerData.SelectedTowers > MAX_SELECTED_TOWERS then
table.remove(playerData.SelectedTowers, 1)
end
elseif status == "Equipped" then
-- Unselect tower
if #playerData.SelectedTowers > 1 then
local towerToRemove = table.find(playerData.SelectedTowers, itemName)
table.remove(playerData.SelectedTowers, towerToRemove)
end
end
return playerData
else
warn("Tower/Player Data Does Not Exist!")
end
return false
end
ReplicatedStorage.GetData.OnServerInvoke = function(player)
return data[player.UserId]
end
TowerShop module script in RepStorage which has all the tower data to populate the gui.
local TowerShop = {
["Cutler"] = {
["Name"] = "Cutler",
["ImageAsset"] = "http://www.roblox.com/asset/?id=15289908015",
["Price"] = 0,
["Damage"] = 5,
["Range"] = 5,
["PlacementCash"] = 50
},
["GunSlinger"] = {
["Name"] = "GunSlinger",
["ImageAsset"] = "rbxassetid://",
["Price"] = 0,
["Damage"] = 420,
["Range"] = 60,
["PlacementCash"] = 70
},
}
return TowerShop
ShopClient local script, in the GUI itself, controls updating the GUI
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.Container.Exit
local gems = gui.Container.Gems
local limit = gui.Container.Limit
local itemsFrame = gui.Container.ScrollingFrame
local openButton = gui.Open["main Button"]
local playerData = {}
local function GetItemStatus(itemName)
if table.find(playerData.SelectedTowers, itemName) then
return "Equipped"
elseif table.find(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()
gems.Text = "💎 " .. playerData.Gems
limit.Text = #playerData.SelectedTowers .. "/5"
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
newButton.Status.Text = "💎 " .. tower.Price
elseif status == "Equipped" then
newButton.Status.Text = "Equipped"
else
newButton.Status.Text = ""
end
newButton.Activated:Connect(function()
InteractItem(tower.Name)
end)
end
end
local function ToggleShop()
gui.Container.Visible = not gui.Container.Visible
if gui.Container.Visible then
playerData = getDataFunc:InvokeServer()
UpdateItems()
end
end
local function SetupShop()
openButton.Activated:Connect(ToggleShop)
exit.Activated:Connect(ToggleShop)
end
SetupShop()
I have no clue whats going on here and I cannot find a fix. This project is a tutorial I am following and going back and rewriting the code didn’t work and Im VERY confused.
Here is the GUI heiarchy if needed.
The error is at this line
gems.Text = "💎 " .. playerData.Gems
limit.Text = #playerData.SelectedTowers .. "/5"