I’m kind of desperate and running out of options here. My problem in a nutshell, is this;
I have a folder named ‘QuestsReplicated’ inside the ReplicatedStorage.
This folder is filled with quests (in the form of BoolValues), and these quests have values inside them like the recommended level, reward and objective. The objective is the only one that’s important for now.
Once a quest giver has given a quest, the quest Boolvalue (along with all of it’s children values) will be cloned to the player folder named ‘Quests’. From here on the objective changes depending on what the player does (like killing an enemy or collecting 10 logs of wood).
In the end, when a quest is finished, the BoolValue turns to true, and the objective value says:
“Congratulations! You’ve completed this quest!”
What the problem is, is that I don’t know how to save these completed quests inside the ‘Quests’ folder to the datastore. What I’m looking for is that the quest stays inside the ‘Quests’ folder when a player joins.
Any help would be greatly appreciated, if I’m being unclear - feel free to ask for more clarification, and I’ll happily give it to you.
You store a dictionary where each key is the quest’s name set to the message you need to display. When you load the data, recreate the Quests folder (they already are inside of ReplicatedStorage), loop through the retrieved dictionary (:GetAsync()), and each time do Quests[key].Objective = value where key is the current quest name in the dictionary, and value is the message that quest is set to. This is an example of how you can structure the dictionary.
{LumberJacking = "Bring the 10 logs of wood to Tobias the farmer."}
I’ve been trying to solve this all day, so I’m gonna take a little break until tomorrow. I appreciate the response, and I really hope that this is the key to my problem. If I have any questions I’ll be sure to ask them next morning.
I just started making the dictionary and writing the script. Here’s what I have so far:
local DataStoreService = game:GetService("DataStoreService")
local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")
local function onPlayerRemoving(plr)
local quests = {
["Lumberjacking"] = plr.Quests["Lumberjacking"].Objective.Value,
["Doppelsoldner"] = plr.Quests["Doppelsoldner"].Objective.Value,
["Courtship"] = plr.Quests["Courtship"].Objective.Value
}
local success, err = pcall(function()
Storage:SetAsync(plr.UserId, quests)
end)
if success then
print("Success!")
end
end
PlrService.PlayerRemoving:Connect(onPlayerRemoving)
local function onPlayerAdded(plr)
end
PlrService.PlayerAdded:Connect(onPlayerAdded)
--for i = 1, #dictionary do
-- insert code here
--end
My question is; how would I efficiently check if the quest is inside the player’s quest folder? And how should I ‘recreate’ the quests folder?
Hay there. You would recreate the folders as if you’re recreating leaderstats. You first load the data so you know which quests the player has, and for each quest create a StringValue inside the quests Quests folder, the name being the current key. And inside each StringValue create 4 StringValues, and put inside of them the data you saved.
To check if a quest is inside the player you simply do
if player.Quests:FindFirstChild("quest name") then
It’s also good practice to do
local quest = player.Quests:FindFirstChild("quest name")
if quest then
Thanks so much for your help and responding - you’re really helping me on the right track and I appreciate that I’ve got a few more questions:
How would I get the quest dictionary when doing ‘Storage:GetAsync’ (inside the onPlayerAdded function)?
local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")
local function onPlayerRemoving(plr)
if plr.Quests:FindFirstChildOfClass("BoolValue") then
local quests = {
["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
}
local success, err = pcall(function()
Storage:SetAsync(plr.UserId, quests)
end)
if success then
print("Success!")
end
end
end
PlrService.PlayerRemoving:Connect(onPlayerRemoving)
local function onPlayerAdded(plr)
Storage:GetAsync(plr.UserId, quests) -- kinda clueless here
end
PlrService.PlayerAdded:Connect(onPlayerAdded)
--for i = 1, #dictionary do
-- insert code here
--end
And how would I make an effecient if statement to check if the player has the quest in his folder? Because whenever I try to do something like that, the script no longer recognizes the quest variable, so to speak.
if plr.Quests:FindFirstChild("Lumberjacking") then
local quests = {
["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
}
end
local success, err = pcall(function()
if plr.Quests:FindFirstChild("Lumberjacking") then
local lumberJacking = quests[Lumberjacking]
end
Storage:SetAsync(plr.UserId, lumberJacking)
end)
if success then
print("Success!")
end
end`
local y = Storage:GetAsync(plr.UserId, quests)
-- create quest objects
for i,v in pairs(y) do
local x = Instance.new("StringValue")
x.Name = i -- Lumberjacking (name of quest)
x.Value = v -- Objective (the objective)
end
I don’t quite know what you mean by a more efficient if statement, could you clarify that please?
What I meant was that the script forgets about the variable set after the if statement because of an ‘end’ shortly afterwards.
I’ve got a pretty busy evening ahead of me, so I’ll jump back into the scripting tomorrow, thanks for the reply and wanting to help me! Have a great evening.
Alright so, I think this might be the final obstacle in the script.
Thanks to you folks I managed to figure out how to get all the quests, recreate them and loop through them. Thanks for that! However, I’m still very puzzled on how I should detect what quests the player has in his folder.
The obvious answer to this would be to do :FindFirstChild(), but how would I save that in :SetAsync?
If I’m being unclear, feel free to ask for more clarification. Thanks for the help!
local DataStoreService = game:GetService("DataStoreService")
local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")
local quests = {
["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
}
local function onPlayerRemoving(plr)
if plr.Quests:FindFirstChild("Lumberjacking") then
-- what should I put here?
end
if plr.Quests:FindFirstChild("Doppelsoldner") then
end
if plr.Quests:FindFirstChild("Courtship") then
end
local success, err = pcall(function()
Storage:SetAsync(plr.UserId, quests)
end)
if success then
print("Success!")
end
end
PlrService.PlayerRemoving:Connect(onPlayerRemoving)
local function onPlayerAdded(plr)
Storage:GetAsync(plr.UserId, quests)
end
PlrService.PlayerAdded:Connect(onPlayerAdded)
I am so lost with that code, basically what I am reading right now:
You got datastore service
a table
a function that fires when player is removed which is indexing Quests…
Then I notice you retrieve the data on player added and don’t even store that data anywhere…
Basically I’d do something like this if I were you…
local ds = game:GetService("DataStoreService"):GetDataStore('dssss')
local data = {}
game.Players.PlayerAdded:Connect(function(p)
if ds:GetAsync(p.UserId) then
data[p.Name] = ds:GetAsync(p.UserId)
else
data[p.Name] = {}
end
end)
game.Players.PlayerRemoving:Connect(function(p)
ds:SetAsync(p.UserId, data[p.Name])
end)
-- with that being said you can add folders inside... also I strongly suggest using bind to close
Quests.Something:Connect(function() -- when you want to change data
data[p.Name]["Lumberjacking"] = 10
end)
You can basically iterate through this stuff, pretty much the neatest way of using datastores.
Yes, the code is very messy, I’m very unexperienced with data stores.
Using your code, how would I detect what quests the player has inside of his folder? And how would I recreate the quests as mentioned above?
It is all done for you in that single table, the new table will load when the player joins and save the old table when the player leaves, you can get each data by indexing the portions, here is an example:
local data = {["souflee"]={["Lumberjacking"] = 10}}
basically a folder without being visible, if you would like it to be visible then you can iterate through the table and create a new value based on it
I also read that isn’t a good idea to use the player name in a datastore (considering players can change their username), is there an alternative for this?
We are saving using their userid, so even if they do change their name, it will be the same name throughout the game, there will be no harm done to the data.
Also the function should be for when you want to change the data
Thanks alot for wanting to help me in the right direction, I appreciate it!
I’m still quite puzzled, and this code is still probably very messy - what should be edited for it to work?
local DataStoreService = game:GetService("DataStoreService")
local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")
local data = {}
local quests = {
["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
}
PlrService.onPlayerAdded:Connect(function(plr)
if ds:GetAsync(p.UserId) then
data[p.Name] = ds:GetAsync(p.UserId)
else
data[p.Name] = {}
end
end)
PlrService.PlayerRemoving:Connect(function(plr)
local success, err = pcall(function()
Storage:SetAsync(p.UserId, data[p.Name])
end)
if success then
print("Success!")
end
end
quests.Something:Connect(function() -- still not sure what this should look like
data[p.Name]["Lumberjacking"] = 10
end)
However, with the way my quest system works, the player doesn’t start out with a folder full of quests (instead, the quests get added to the folder throughout the game). How would I handle this approach?
basically another reason why we are using dictionaries, you can remove values from the table and assign infinite tables, now that the tables save you can do this:
local data = {["souflee"] = {["Lumberjacking"] = {}}}
table.insert(data['souflee']['Lumberjacking'],"Quest")
Thanks again so much for helping me in the right direction, have a good night! If you’re ok with it, we could surely continue this tomorrow or some other time.
Hey there, to anyone reading this. Sorry for the bump, but after a few months I found another alternative, simple although primitive solution to the problem at hand: if you make sure the quests are copied into the quest folder upon joining, you can save the values via a very simple datastore.
All you essentially have to do is check if the quest hasn’t been activated or completed yet, and you should be good to go.
Again sorry for the bump, but I just want to make sure anyone stumbling into this problem will find the solution, if I need to remove this post, I’ll do so. If you happen to want more explanation on the problem/solution, then feel free to message me. A big thanks to everyone that was kind enough to help me in the right direction! You’re all lovely folks!