Hello there,
I’m making a shop with skins and traps but something doesn’t seems to be working
Error message:
Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript:243: attempt to index nil with 'Info' - Client - LocalShopScript:243
11:56:12.398 Stack Begin - Studio
11:56:12.399 Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 243 - function checkStatus - Studio - LocalShopScript:243
11:56:12.401 Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 395 - function previewItem - Studio - LocalShopScript:395
11:56:12.402 Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 77 - function showScreen - Studio - LocalShopScript:77
11:56:12.403 Script 'Players.The_UncleDev.PlayerGui.MenuGui.Shop.LocalShopScript', Line 98 - Studio - LocalShopScript:98
11:56:12.403 Stack End - Studio
LocalShopScript:
-- services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")
local MarketService = game:GetService("MarketplaceService")
-- folders
local Functions = ReplicatedStorage:WaitForChild("Functions")
local Events = ReplicatedStorage:WaitForChild("Events")
local skinFolder = ReplicatedStorage:WaitForChild("Skins")
local trapFolder = ReplicatedStorage:WaitForChild("Traps")
-- remotes
local getData = Functions:WaitForChild("getData")
local selectItemFunc = Functions:WaitForChild("selectItem")
local previewItemFunc = Functions:WaitForChild("previewItem")
local buyItem = Functions:WaitForChild("buyItem")
local buyCurrency = Functions:WaitForChild("buyCurrency")
local togglePopup = Events:WaitForChild("togglePopup")
-- references
local player = game.Players.LocalPlayer
local shop = script.Parent
local popup = shop.Parent.Popup
local shopPreview = shop.Preview
local shopMenu = shop.ShopMenu
local topMenu = shopMenu.TopMenu
local selectBtn = shop.Select
local tickets = shop.Tickets
local backBtn = shop.Back
local homeBtn = topMenu.Home
local skinBtn = topMenu.Skin
local trapBtn = topMenu.Traps
local homeScreen = shopMenu.Home
local skinScreen = shopMenu.Skin
local trapScreen = shopMenu.Traps
local activeScreen
-- modules
local availableCharacters = require(skinFolder:WaitForChild("List"))
local availableTraps = require(trapFolder:WaitForChild("List"))
local ownedSkins = {}
local killerSelection
local ownedTraps = {}
local trapSelection
-- colors :D
local colors = {
red = Color3.fromRGB(170, 0, 0),
green = Color3.fromRGB(100, 193, 66),
yellow = Color3.fromRGB(255, 216, 0),
neutral = Color3.fromRGB(225, 168, 139),
white = Color3.fromRGB(255, 255, 255),
black = Color3.fromRGB(0, 0, 0)
}
local function showScreen(screen)
homeScreen.Visible = false
skinScreen.Visible = false
trapScreen.Visible = false
selectBtn.Visible = false
for index, child in pairs(shopPreview:GetChildren()) do
if not child:IsA("UICorner") then
child:Destroy()
end
end
screen.Visible = true
activeScreen = screen
if screen.Name == "Skin" then
shopPreview.Visible = true
shopMenu.UIStroke.Color = Color3.fromRGB(255, 50, 0)
shop.Title.Text = "Shop:Skin section"
if killerSelection then
previewItem(screen:FindFirstChild(killerSelection), "Skin")
else
previewItem(screen:FindFirstChild("Clare"), "Skin")
end
elseif screen.Name == "Traps" then
shopPreview.Visible = true
shopMenu.UIStroke.Color = Color3.fromRGB(255,0,0)
shop.Title.Text = "Shop:Trap section"
if trapSelection then
previewItem(screen:FindFirstChild(trapSelection), "Traps")
else
previewItem(screen:FindFirstChild("Clare"), "Traps")
end
else
shopPreview.Visible = false
shopMenu.UIStroke.Color = Color3.fromRGB(2, 130, 186)
shop.Title.Text = "Shop:Home page"
end
end
homeBtn.Activated:Connect(function()
showScreen(homeScreen)
end)
skinBtn.Activated:Connect(function()
showScreen(skinScreen)
end)
trapBtn.Activated:Connect(function()
showScreen(trapScreen)
end)
-- control popup ui
local function popupPrompt(msg, cancelOption, location)
local result
local cancel
local confirm
popup.Visible = true
popup.Title.Text = msg
popup.Buttons.Cancel.Visible = false
if cancelOption then
popup.Buttons.Cancel.Visible = true
cancel = popup.Buttons.Cancel.Activated:Connect(function()
if cancel then
cancel:Disconnect()
end
if confirm then
confirm:Disconnect()
end
popup.Visible = false
result = false
end)
end
confirm = popup.Buttons.Okay.Activated:Connect(function()
if cancel then
cancel:Disconnect()
end
if confirm then
confirm:Disconnect()
end
popup.Visible = false
result = true
end)
while not result do
wait()
end
return result
end
togglePopup.OnClientEvent:Connect(popupPrompt)
-- changes how the shop icons will look
local function setStatus(btn, status)
selectBtn.Info.Visible = false
-- default
if status == "For Sale" then
btn.Info.Tickets.Visible = true
btn.Info.Status.TextColor3 = colors["yellow"]
btn.Title.TextStrokeColor3 = colors["yellow"]
btn.ViewportFrame.BackgroundColor3 = colors["neutral"]
else -- owned or selected
btn.Info.Status.Text = status
btn.Info.Tickets.Image = "rbxassetid://54653911"
end
if status == "Owned" then
btn.Info.Status.TextColor3 = colors["green"]
btn.Title.TextStrokeColor3 = colors["green"]
btn.ViewportFrame.BackgroundColor3 = colors["green"]
elseif status == "Selected" then
btn.Info.Status.TextColor3 = colors["red"]
btn.Title.TextStrokeColor3 = colors["red"]
btn.ViewportFrame.BackgroundColor3 = colors["red"]
elseif status == "Off-Sale" then
btn.Info.Status.TextColor3 = colors["neutral"]
btn.Title.TextStrokeColor3 = colors["neutral"]
btn.ViewportFrame.BackgroundColor3 = colors["black"]
btn.ViewportFrame.ImageColor3 = colors["black"]
end
end
-- checks our local table of the players session data, reduces unnecessary server calls
local function playerOwnsItem(itemType, name)
if ownedSkins and ownedTraps then
local dictionary
if itemType == "Skin" then
dictionary = ownedSkins
elseif itemType == "Traps" then
dictionary = ownedTraps
end
if dictionary then
for index, item in pairs(dictionary) do
if item == name then
return true
end
end
end
end
return false
end
local function loadData()
-- gets the latest player data
local data = getData:InvokeServer()
if data then
ownedSkins = data.skins
killerSelection = data.killer
ownedTraps = data.traps
trapSelection = data.trap
end
end
-- updates store whenever anything changes
local function updateStore()
loadData()
-- updates the players money tally
tickets.Amount.Text = player.Tickets.Value
-- sets skin icons to appropriate status
for index, gui in pairs(skinScreen:GetChildren()) do
if gui:IsA("TextButton") and gui.Visible == true then
if gui.Name == killerSelection then
setStatus(gui, "Selected")
elseif playerOwnsItem("Clare", gui.Name) then
setStatus(gui, "Owned")
elseif availableCharacters[gui.Name]["For Sale"] == false then
setStatus(gui, "Off-Sale")
end
end
end
-- setup trap icons
for index, gui in pairs(trapScreen:GetChildren()) do
if gui:IsA("TextButton") and gui.Visible == true then
if gui.Name == trapSelection then
setStatus(gui, "Selected")
elseif playerOwnsItem("NormalTrap", gui.Name) then
setStatus(gui, "Owned")
end
end
end
-- redraw the viewport model
if shopPreview:FindFirstChild("WorldModel") and shopPreview.WorldModel:FindFirstChildOfClass("Model") then
local model = shopPreview.WorldModel:FindFirstChildOfClass("Model")
if model then
displayModel(model:Clone(), model.ItemType.Value)
end
end
end
player.Tickets.Changed:Connect(updateStore)
local function checkStatus(btn)
if btn.Info.Status.TextColor3 == colors["red"] then
return "Selected"
elseif btn.Info.Status.TextColor3 == colors["green"] then
return "Owned"
elseif btn.Info.Status.TextColor3 == colors["yellow"] then
return "For Sale"
elseif btn.Info.Status.TextColor3 == colors["neutral"] then
return "Off-Sale"
end
end
local function setupPreview(status)
shop.Select.Info.Visible = false
if status == "For Sale" then
selectBtn.Text = "BUY"
selectBtn.TextColor3 = colors["green"]
selectBtn.UIStroke.Color = colors["green"]
selectBtn.Info.Tickets.Image = "rbxassetid://9058199447"
selectBtn.Info.Visible = true
shopPreview.ImageColor3 = colors["neutral"]
elseif status == "Owned" then
selectBtn.Text = "SELECT"
selectBtn.TextColor3 = colors["white"]
selectBtn.UIStroke.Color = colors["white"]
shopPreview.ImageColor3 = colors["white"]
elseif status == "Selected" then
selectBtn.Text = "SELECTED"
selectBtn.TextColor3 = colors["red"]
selectBtn.UIStroke.Color = colors["red"]
shopPreview.ImageColor3 = Color3.fromRGB(255, 190, 190)
elseif status == "Off-Sale" then
selectBtn.Text = "OFF-SALE"
selectBtn.TextColor3 = colors["black"]
selectBtn.UIStroke.Color = colors["black"]
shopPreview.ImageColor3 = Color3.fromRGB(0, 0, 0)
end
end
function displayModel(model, modeType)
if model then
local status = checkStatus(activeScreen:FindFirstChild(model.Name))
if status then
setupPreview(status)
end
-- create a camera if it doesn't exist
shopPreview:ClearAllChildren()
local camera = Instance.new("Camera", shopPreview)
local worldModel = Instance.new("WorldModel", shopPreview)
local viewportCamera = shopPreview.Camera
shopPreview.CurrentCamera = viewportCamera
viewportCamera.Parent = shopPreview
viewportCamera.FieldOfView = 40
if modeType == "Skin" then
-- nice easy copy and paste
local viewModel = model:Clone()
viewModel.Parent = shopPreview.WorldModel
local cframe, size = viewModel:GetBoundingBox()
local animFolder = ReplicatedStorage.Animations:FindFirstChild(viewModel.Name)
local idleAnim = animFolder.Idle
local idleTrack = viewModel.Humanoid.Animator:LoadAnimation(idleAnim)
idleTrack:Play()
viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(0, math.rad(180), 0)
end
if modeType == "Traps" then
local viewModel = model:Clone()
viewModel.Parent = shopPreview.WorldModel
local cframe, size = viewModel:GetBoundingBox()
viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(math.rad(45), math.rad(180), 0)
end
end
end
local function selectItem()
if shopPreview:FindFirstChild("WorldModel") and shopPreview.WorldModel:FindFirstChildOfClass("Model") then
local model = shopPreview.WorldModel:FindFirstChildOfClass("Model")
local itemType = model.ItemType.Value
local itemName = model.Name
local status = checkStatus(shopMenu[activeScreen.Name]:FindFirstChild(itemName))
if playerOwnsItem(itemType, itemName) then
if status == "Owned" then
local response = selectItemFunc:InvokeServer(itemType, itemName)
if response == "Success" then
updateStore()
else
if not response then response = "Error" end
popupPrompt(response, false, "ShopMenu")
end
elseif status == "Selected" then
updateStore()
end
elseif status == "For Sale" then
-- Client-side check, also repeated on server
if tonumber(selectBtn.Info.Status.Text) then
local price = tonumber(selectBtn.Info.Status.Text)
if tonumber(selectBtn.Info.Status.Text) > player.Tickets.Value then
local diff = tonumber(selectBtn.Info.Status.Text) - player.Tickets.Value
if diff < 50 then
-- prompt to purchase 50 tickets
local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
if purchase then
MarketService:PromptProductPurchase(player, 1245332333)
updateStore()
end
elseif diff < 100 then
-- prompt to purchase 100 tickets
local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
if purchase then
MarketService:PromptProductPurchase(player, 1245332335)
updateStore()
end
elseif diff < 150 then
-- prompt to purchase 150 tickets
local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
if purchase then
MarketService:PromptProductPurchase(player, 1245332334)
updateStore()
end
else
-- prompt to purchase 1000 tickets
local purchase = popupPrompt("Purchase more tickets?", true, "ShopMenu")
if purchase then
MarketService:PromptProductPurchase(player, 1245332332)
updateStore()
end
end
else
local confirm = popupPrompt("Purchase " .. itemName .. "?", true, "ShopMenu")
if confirm then
local response = buyItem:InvokeServer(itemName)
if response == "Success" then
local response = selectItemFunc:InvokeServer(itemType, itemName)
if response == "Success" then
updateStore()
else
updateStore()
end
else
if not response then response = "Error" end
popupPrompt(response, false, "ShopMenu")
end
end
end
else
popupPrompt("You have not unlocked this", false, "ShopMenu")
end
end
end
end
selectBtn.Activated:Connect(selectItem)
function previewItem(btn, itemType)
local status = checkStatus(btn)
shop.Select.Info.Status.Text = btn.Info.Status.Text
shop.Select.Visible = true
-- make a copy of the model inside the viewport frame we just clicked
local model = btn.ViewportFrame:FindFirstChildOfClass("Model")
displayModel(model, itemType)
end
local function setupScreen(screen, dictionary, itemFolder)
local columns = 6
local padding = shopMenu.AbsoluteSize.X * 0.01
local relativeCellSize = (1 / columns) - 0.01
local absoluteCellSize = shopMenu.AbsoluteSize.X * relativeCellSize
local UIGridLayout = Instance.new("UIGridLayout")
UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
UIGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
UIGridLayout.CellSize = UDim2.new(0, absoluteCellSize, 0, absoluteCellSize)
UIGridLayout.CellPadding = UDim2.new(0, padding, 0, padding)
UIGridLayout.Parent = screen
local total = 0
for index, item in pairs(dictionary)do
total += 1
local btn = shopMenu.Template:Clone()
btn.Name = item.Name
btn.Visible = true
btn.LayoutOrder = item.Price
btn.Parent = screen
-- setup text info
btn.Title.Text = item.Name
btn.Info.Status.Text = item.Price
if item["For Sale"] == false then
btn.ViewportFrame.BackgroundColor3 = colors["black"]
elseif item["Limited Edition"] then
btn.ViewportFrame.BackgroundColor3 = colors["white"]
end
-- setup viewportframe
local viewportCamera = Instance.new("Camera")
btn.ViewportFrame.CurrentCamera = viewportCamera
viewportCamera.Parent = btn.ViewportFrame
viewportCamera.FieldOfView = 40
local model
-- skin
if itemFolder == skinFolder then
model = itemFolder[item.Name]:Clone()
local cframe, size = model:GetBoundingBox()
viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(0, math.rad(180), 0)
-- traps
elseif itemFolder == trapFolder then
btn.Title.Text = item.Name .. "(" .. item.Duration .. "s)"
model = itemFolder[item.Name]:Clone()
local cframe, size = model:GetBoundingBox()
viewportCamera.CFrame = cframe * CFrame.new(0,0,-10) * CFrame.Angles(45, math.rad(180), 0)
end
if model then
local itemType = Instance.new("StringValue")
itemType.Value = item.Type or itemFolder.Name
itemType.Name = "ItemType"
itemType.Parent = model
model.Parent = btn.ViewportFrame
btn.Activated:Connect(function()
previewItem(btn, itemType.Value)
end)
end
end
local rows = math.floor(total / columns, 0.5) + 1
local height = (absoluteCellSize + padding) * rows
screen.CanvasSize = UDim2.new(0, 0, 0, height)
end
function setupCharacterShop()
loadData()
-- create buttons for every skin and trap
setupScreen(skinScreen, availableCharacters, skinFolder)
setupScreen(trapScreen, availableTraps, trapFolder)
end
-- buy 50 tieces
homeScreen.Tickets50.Activated:Connect(function()
MarketService:PromptProductPurchase(player, 1245332333)
end)
-- buy 100 tieces
homeScreen.Tickets100.Activated:Connect(function()
MarketService:PromptProductPurchase(player, 1245332335)
end)
-- buy 150 tieces
homeScreen.Tickets150.Activated:Connect(function()
MarketService:PromptProductPurchase(player, 1245332334)
end)
-- buy 1000 tieces
homeScreen.Tickets1000.Activated:Connect(function()
MarketService:PromptProductPurchase(player, 1245332332)
end)
-- buy random tieces
homeScreen.RandomTickets.Activated:Connect(function()
MarketService:PromptProductPurchase(player, 1245337437)
end)
local function buyGamepass(id)
local hasPass = false
local success, err = pcall(function()
hasPass = MarketService:UserOwnsGamePassAsync(player.UserId, id)
end)
if not success then
warn("Error getting gamepass info for", player.Name, err)
popupPrompt("Error getting gamepass info", false)
return
end
if hasPass then
popupPrompt("You already own this", false)
else
-- player does not own the game pass; prompt them to purchase
MarketService:PromptGamePassPurchase(player, id)
end
end
-- double chances
homeScreen.DoubleChances.Activated:Connect(function()
buyGamepass(32648691)
end)
-- double tickets
homeScreen.DoubleTickets.Activated:Connect(function()
buyGamepass(32288239)
end)
shop:GetPropertyChangedSignal("Visible"):Connect(function()
-- update the store page when opened
if shop.Visible then
updateStore()
else
-- hide any popups when closed
popup.Visible = false
end
end)
-- initial setup
setupCharacterShop()
updateStore()
Explorer:
Please help…