How to make a Quest System Part 4

Hi! Welcome to the 4th part of the Quest System tutorial. In this part, I will be showing you how to give the reward to the player. Let’s start! :grinning:

If you haven’t read the other parts, here is the link to them:
Part 1: How to make a Quest System
Part 2: How to make a Quest System Part 2
Part 3: How to make a Quest System Part 3

First, we go back to the script in the quest giver part. If you want to have a dialogue for the quest giver, wait for the bonus part of this tutorial. Meanwhile, we need to check if the player has finished the quest. Like this:

prompt.Triggered:Connect(function(player)
	if game.Players:FindFirstChild(player.Name) then
		local questFound = findQuest()
		local questsFolder = player:WaitForChild("QuestsFolder")
		if not questsFolder:FindFirstChild(questFound.Name) then
			if questFound.Name == "Grab 2 bunnies" then
				local folder = makeFolder(questsFolder, questFound, parentName, number)
				addQuestToGUI:FireClient(player, questFound, parentName, number)
				questFound.Action(bunniesFolder, folder)
				folder.Counter:GetPropertyChangedSignal("Value"):Connect(function()
					if folder.Counter.Value <= 0 then
						if #workspace:WaitForChild("Bunnies"):GetDescendants() == 0 then
							if player:FindFirstChild(questFound.Currency) then
								local currency = player:FindFirstChild(questFound.Currency)
								currency.Value += questFound.Reward
								return
							end
						end
					end
				end)
			end
		end
	end
end)

As you have noticed, we have added a GetPropertyChangedSignal function. This function checks if the given property, which is in the parenthesis: GetPropertyChangedSignal("Value"), changed. For example, if you set “BrickColor” inside the parenthesis, if the BrickColor of the part changes, the function fires. After the GetPropertyChangedSignal function fires, we check if the value of the counter is 0 or below 0. If it is, we check again if the bunnies folder is empty. If it is empty, we will find the value to which we will give the reward. In this case, we will find the currency “Gold” because in the quest module, the reward in the quest is “Gold”. Then we add the value of the Reward variable to it and finally return.

Now, we just need to delete the quest frame in the GUI and we’re done! We can do this by adding an if-statement in the GetPropertyChangedSignal function of the counter. If the counter’s value is 0 or below 0, we destroy the frame:

questsFolder.ChildAdded:Connect(function(child)
	local counter = child:WaitForChild("Counter")
	local maxCounter = child:WaitForChild("MaxCounter")
	counter:GetPropertyChangedSignal("Value"):Connect(function()
		local frame = questsFrame:WaitForChild(child.Name)
		if frame then
			if counter.Value <= 0 then
				frame:Destroy()
			else
				local counterFrame = frame:WaitForChild("Counter"):WaitForChild("MainCounter")
				local counterText = frame:WaitForChild("Counter"):WaitForChild("CounterText")
				counterFrame.Size = UDim2.new(counter.Value / maxCounter.Value,0,1,0)
				counterText.Text = counter.Value.."/"..maxCounter.Value
			end
		end
	end)
end)

To test if it works, we need to add a number value in the player. We just need to change the Leaderstats script in the ServerScriptService:

game.Players.PlayerAdded:Connect(function(player)

	local questsFolder = Instance.new("Folder", player)
	questsFolder.Name = "QuestsFolder"
	
	local Gold = Instance.new("NumberValue", player)
    Gold.Name = "Gold"
	Gold.Value = 0
	
end)

If you followed all the steps correctly, it should work. Here is the result video:

I hope you learned something in this part of the tutorial. I also hope you enjoyed this tutorial. If you have any suggestions or feedback, feel free to say them. That’s all and Goodbye! :smile:

11 Likes

Hey, it’s an old post and I don’t know if you changed the way you code
Little suggestion: Try early returns for example here:

For it to be more readable you could do something like

if not game.Players:FindFirstChild(player.Name) then return end
3 Likes

anyone using this resource and do not know how to make the frame invisible before and and after the quest. Here you go. Replace the GUIHandler local script in the starter gui.

local event = game.ReplicatedStorage:WaitForChild("AddQuest")
local questsGUI = script.Parent:WaitForChild("Quests")
local questFrameTemp = game.ReplicatedStorage:WaitForChild("TempQuestFrame")
local mainFrame = questsGUI:WaitForChild("BGFrame")
local questsFrame = mainFrame:WaitForChild("QuestsFrame")
local player = game.Players.LocalPlayer
local questsFolder = player:WaitForChild("QuestsFolder")

local questFrames = {}  -- Table to store quest frames

local function makeFrame(questName, parentName, number)
	local clone = questFrameTemp:Clone()
	clone.Name = questName.Name
	local counter = clone:WaitForChild("Counter")
	local NPCname = clone:WaitForChild("NPCName")
	if parentName == nil then
		NPCname.Text = ""
	else
		NPCname.Text = tostring(parentName)
	end
	local counterText = counter:WaitForChild("CounterText")
	counterText.Text = number.."/"..number
	local QuestName = clone:WaitForChild("QuestName")
	QuestName.Text = tostring(questName.Name)
	clone.Parent = questsFrame

	return clone
end

mainFrame.Visible = false  -- Initially hide the BGFrame

event.OnClientEvent:Connect(function(questName, parentName, number)
	mainFrame.Visible = true  -- Make the BGFrame visible when a quest starts

	if questsFolder:FindFirstChild(questName.Name) then
		local frame = makeFrame(questName, parentName, number)
		questFrames[questName.Name] = frame

		local counter = questsFolder[questName.Name]:WaitForChild("Counter")
		local maxCounter = questsFolder[questName.Name]:WaitForChild("MaxCounter")

		counter:GetPropertyChangedSignal("Value"):Connect(function()
			if counter.Value <= 0 then
				local allQuestsCompleted = true
				for _, questFolder in pairs(questsFolder:GetChildren()) do
					if questFolder.Counter.Value > 0 then
						allQuestsCompleted = false
						break
					end
				end
				if allQuestsCompleted then
					mainFrame.Visible = false  -- Hide the BGFrame if all quests are completed
				end
				-- Quest completed, make all frames invisible
				for _, questFrame in pairs(questFrames) do
					questFrame.Visible = false
				end
			else
				local counterFrame = frame:WaitForChild("Counter"):WaitForChild("MainCounter")
				local counterText = frame:WaitForChild("Counter"):WaitForChild("CounterText")
				counterFrame.Size = UDim2.new(counter.Value / maxCounter.Value, 0, 1, 0)
				counterText.Text = counter.Value .. "/" .. maxCounter.Value
			end
		end)
	else
		print("No quest found")
	end
end)

questsFolder.ChildAdded:Connect(function(child)
	local counter = child:WaitForChild("Counter")
	local maxCounter = child:WaitForChild("MaxCounter")
	counter:GetPropertyChangedSignal("Value"):Connect(function()
		local frame = questsFrame:WaitForChild(child.Name)
		if frame then
			if counter.Value <= 0 then
				frame:Destroy()
				questFrames[child.Name] = nil  -- Remove the frame from the questFrames table
			else
				local counterFrame = frame:WaitForChild("Counter"):WaitForChild("MainCounter")
				local counterText = frame:WaitForChild("Counter"):WaitForChild("CounterText")
				counterFrame.Size = UDim2.new(counter.Value / maxCounter.Value, 0, 1, 0)
				counterText.Text = counter.Value .. "/" .. maxCounter.Value
			end
		end
	end)
end)
1 Like