How would I code in a skin system?

You overcomplicated the things here in one dictionnary how I would sort the thing :

local Data = {
	["Towers"] = {
		["TowerName1"] = {
			["Owned"] = true,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = true,
					["Equipped"] = false
				},
				["SkinName2"] = {
					["Owned"] = true,
					["Equipped"] = true
				}
			}
		},
		["TowerName2"] = {
			["Owned"] = true,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = true,
					["Equipped"] = true
				},
				["SkinName2"] = {
					["Owned"] = false,
					["Equipped"] = false
				}
			}
		}
	}
}
4 Likes

There is already a towers shop module, so I assume I could just merge them into 1?

local towerShop = {
	
	["Rookie"] = {
		["Name"] = "Rookie",
		["ImageAsset"] = "rbxassetid://11399177361",
		["Price"] = 0,
		["Description"] = "He's literally a rookie. WIP"
	},
	
	["Assailant"] = {
		["Name"] = "Assailant",
		["ImageAsset"] = "rbxassetid://11399262866",
		["Price"] = 350,
		["Description"] = "For his neutral special, he wields a gun. WIP"
	},
	
	["Deputy"] = {
		["Name"] = "Deputy",
		["ImageAsset"] = "rbxassetid://11399256352",
		["Price"] = 450,
		["Description"] = "tower thats going to get removed lol. WIP"
	},
	
	["Dealer"] = {
		["Name"] = "Dealer",
		["ImageAsset"] = "rbxassetid://11399250886",
		["Price"] = 650,
		["Description"] = "this tower does not fit the theme, may get removed lol. WIP"
	},
	
	["Nerd"] = {
		["Name"] = "Nerd",
		["ImageAsset"] = "rbxassetid://11399205918",
		["Price"] = 850,
		["Description"] = "this tower was a bad idea, i have better plans for it lol. WIP"
	},
	
	["Accountant"] = {
		["Name"] = "Accountant",
		["ImageAsset"] = "rbxassetid://11434413723",
		["Price"] = 900,
		["Description"] = "He's literally a money making machine. WIP"
	},
	
	["Bloxxer"] = {
		["Name"] = "Bloxxer",
		["ImageAsset"] = "rbxassetid://11808754085",
		["Price"] = 1250,
		["Description"] = "He's literally getting revamped. WIP"
	},
	
	["Sniper"] = {
		["Name"] = "Sniper",
		["ImageAsset"] = "rbxassetid://11401684007",
		["Price"] = 1300,
		["Description"] = "haha hes also getting revamped because he needs it. WIP"
	},
	
	["Back-Up"] = {
		["Name"] = "Back-Up",
		["ImageAsset"] = "rbxassetid://11399215569",
		["Price"] = 1500,
		["Description"] = "He recently robbed a bank and managed to get here. How?! WIP"
	},
	
	["Doctor"] = {
		["Name"] = "Doctor",
		["ImageAsset"] = "rbxassetid://11423153518",
		["Price"] = 1750,
		["Description"] = "MEDIC!!!. WIP"
	},
			
	["Remixer"] = {
		["Name"] = "Remixer",
		["ImageAsset"] = "rbxassetid://11798629467",
		["Price"] = 4000,
		["Description"] = "he remixes tracks and gets paid for it!. WIP"
	},
	
	["Reinforcement"] = {
		["Name"] = "Reinforcement",
		["ImageAsset"] = "rbxassetid://11798532423",
		["Price"] = 7500,
		["Description"] = "CALL IN THE REINFORCMENTS! WIP"
	},
	
}

return towerShop
5 Likes

I don’t really know but if I’m correct my architecture would be easier, I mean if you want to know if you have skin1 you juste have to do this : Data["Towers"]["TowerName"]["Skins"]["Owned"].
It’s an exemple but I think that could be way better than how you’re handling your data actually !

3 Likes

Wait so, you mean something like this?

	["Rookie"] = {
		["Name"] = "Rookie",
		["ImageAsset"] = "rbxassetid://11399177361",
		["Price"] = 0,
		["Description"] = "He's literally a rookie. WIP",

		["Skins"] = {
			["Office"] = {
				["Owned"] = true,
				["Equipped"] = false
			},
		}
	},
4 Likes

In your case (for the 2 first) it would look like this :

local Data = {
	["Towers"] = {
		["Rookie"] = {
			["Description"] = "He's literally a rookie. WIP",
			["Image"] = "rbxassetid://11399177361",
			["Price"] = 0,
			["Owned"] = true,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = true,
					["Equipped"] = false
				},
				["SkinName2"] = {
					["Owned"] = true,
					["Equipped"] = true
				}
			}
		},
		["Assailant"] = {
			["Description"] = "For his neutral special, he wields a gun. WIP",
			["Image"] = "rbxassetid://11399262866",
			["Price"] = 350,
			["Owned"] = true,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = true,
					["Equipped"] = true
				},
				["SkinName2"] = {
					["Owned"] = false,
					["Equipped"] = false
				}
			}
		}
	}
}
2 Likes

Seems pretty good. I’ll stick with that. Anyways. I managed to go through my script very well and replace the duplicate script with the skin one to save time. however I definitely got puzzled when I saw this interact-item function.

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

local Events = ReplicatedStorage:WaitForChild("Events")
local Modules = ReplicatedStorage:WaitForChild("Modules")
local Functions = ReplicatedStorage:WaitForChild("Functions")
local Remotes = ReplicatedStorage:WaitForChild("Remotes")

local database = DataStoreService:GetDataStore("skin_teststore1")
local towers = require(Modules:WaitForChild("SkinShop"))

local MAX_SELECTED_TOWERS = 5

local data = {}

-- Load the players 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
			warn(playerData)
			task.wait()
		end
		
	until success or attempt == 3
	
	if success then
		print("Connection success")
		if not playerData then
			print("New player, giving default data")
			playerData = {
				["Bucks"] = 100,
				["SelectedSkins"] = {"Rookie"},
				["OwnedSkins"] = {"Rookie"},
			}
		end
		data[player.UserId] = playerData
	else
		warn("Unable to get data for player", player.UserId)
		player:Kick("There was a problem getting your data")
	end
end
Players.PlayerAdded:Connect(LoadData)

-- Save the players data
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
				warn(playerData)
				task.wait()
			end

		until success or attempt == 3

		if success then
			print("Data saved successfully")
		else
			warn("Unable to save data for", player.UserId)
		end
	else
		warn("No session 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 studio")
	end
end)

local function getItemStatus(player, itemName)
	local playerData = data[player.UserId]
	if table.find(playerData.SelectedSkins, itemName) then
		return "Equipped"
	elseif table.find(playerData.OwnedSkins, itemName) then
		return "Owned"
	else
		return "For Sale"
	end
end

Functions.InteractItem.OnServerInvoke = function(player, itemName, Unequip)
	local shopItem = towers[itemName]
	local playerData = data[player.UserId]
	if shopItem and playerData then
		local status = getItemStatus(player, itemName)
		
		if Unequip then
			if #playerData.SelectedTowers > 1 then
				local towerToRemove = table.find(playerData.SelectedTowers, itemName)
				table.remove(playerData.SelectedTowers, towerToRemove)
			end
			
		else
			if status == "For Sale" and shopItem.Price <= playerData.Bucks then
				-- purchase
				playerData.Bucks -= shopItem.Price
				table.insert(playerData.OwnedTowers, shopItem.Name)

			elseif status == "Owned" then
				-- equip the 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 the tower (if more than 1 selected)
				if #playerData.SelectedTowers > 1 then
					local towerToRemove = table.find(playerData.SelectedTowers, itemName)
					table.remove(playerData.SelectedTowers, towerToRemove)
				end
			end
		end
		return playerData
	else
		warn("Tower/player data does not exist")
	end

	return false
end

Functions.GetData.OnServerInvoke = function(player)	
	return data[player.UserId]
end
3 Likes

I am definitely confused with this. This function runs when the player clicks on the skins button UI

2 Likes

any ideas? im quite confused really

2 Likes

Sorry I was eating I’m now here !

2 Likes

So this is the data in the client side I will now show you how your datastore should look like !
I will be back in a few mins !

2 Likes

So, I made a little exemple on how I would do it :

Server

So what I did is pretty simple, here I already have my PlayerData but in your case I believe you’ll have to firstly get the data of you player with datastore, after that you will only have to send informations to the client with a remote event !

local PlayerData = {
	["Towers"] = {
		["Rookie"] = {
			["Owned"] = true,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = true,
					["Equipped"] = false
				},
				["SkinName2"] = {
					["Owned"] = true,
					["Equipped"] = true
				}
			}
		},
		["Assailant"] = {
			["Owned"] = true,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = true,
					["Equipped"] = true
				},
				["SkinName2"] = {
					["Owned"] = false,
					["Equipped"] = false
				}
			}
		}
	}
}

Players.PlayerAdded:Connect(function(Player)
	RemoteEvent:FireClient(Player, PlayerData)
end)
Client

Now on the client side we only have to update the current data, in the client data you should have all informations about towers !

local Data = {
	["Towers"] = {
		["Rookie"] = {
			["Description"] = "He's literally a rookie. WIP",
			["Image"] = "rbxassetid://11399177361",
			["Price"] = 0,
			["Owned"] = false,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = false,
					["Equipped"] = false
				},
				["SkinName2"] = {
					["Owned"] = false,
					["Equipped"] = false
				}
			}
		},
		["Assailant"] = {
			["Description"] = "For his neutral special, he wields a gun. WIP",
			["Image"] = "rbxassetid://11399262866",
			["Price"] = 350,
			["Owned"] = false,
			["Skins"] = {
				["SkinName1"] = {
					["Owned"] = false,
					["Equipped"] = false
				},
				["SkinName2"] = {
					["Owned"] = false,
					["Equipped"] = false
				}
			}
		}
	}
}

RemoteEvent.OnClientEvent:Connect(function(PlayerData)
	for TowerName, TowerInformations in pairs(PlayerData["Towers"]) do
		local TowerDictionary = Data["Towers"][TowerName]
		TowerDictionary["Owned"] = TowerInformations["Owned"]
		for SkinName, SkinInformations in pairs(TowerInformations["Skins"]) do
			local SkinDictionary = TowerDictionary["Skins"][SkinName]
			SkinDictionary["Owned"] = SkinInformations["Owned"]
			SkinDictionary["Equipped"] = SkinInformations["Equipped"]
		end
	end
end)

I hope that helped you and have a nice day !
If you’re still stuck feel free to ask me anything !
EDIT : notice that in the player data I only put informations that may change over the time !

2 Likes

Ill try this out! And if theres a issue, i’ll let you know.

2 Likes

Seems a bit difficult right now… I sort of understand it but don’t understand it.