Create a Quest System!

Ever wanted a quest for your game? Well I will show you how to make quests for your game! Read the Notes for Important Information.

by witch2352

Author Avatar

Hello! This tutorial will show you how to make a quest system!

Please read the Notes for information I dont cover much!


Intro


Insert a Script into ServerScriptService and name it QuestServer. Then insert a ModuleScript into ServerStorage and name it QuestModule

Scripting


Lets start with our QuestServer Open up the script and we will create our folders inside the player and require the QuestModule

local SS = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local questModule = require(SS:FindFirstChild("QuestModule")

Players.PlayerAdded:Connect(function(player)
    local questFolder = Instance.new("Folder", player)
    questFolder.Name = "Quests"

    player.CharacterAdded:Connect(function(character)
        task.wait(5)
        -- We will come back to this later!
    end)
end)

Now, lets create our quest structure in the QuestModule!

local module = {}

local quests = {

    ["Test"] = {
        Requirements = {{"Stick", 1}, {"Metal", 4}};
        Rewards = {{"EXP", 125}, {"Gold", 1}};
    };

}

return module

Ok, that looks like a lot, but let me explain. First, the ["Test"] is the name of the quest. So Test is our quests name.

["Test"] opens up into another table which will store all of our Requirements and Rewards.

Every Requirement needs its own table like {"Stick", 1}. This is the same with the rewards.

Add Quest Function To create our addQuest function, open the QuestModule!

local module = {}

local quests = {

    ["Test"] = {
        Requirements = {{"Stick", 1}, {"Metal", 4}};
        Rewards = {{"EXP", 125}, {"Gold", 1}};
    };

}

-- New stuff below
function module.addQuest(plr, questName)
	if quests[questName] then -- Makes sure the quest is a valid quest
		if checkForQuest(plr, questName) == false then
			local quest = quests[questName]

			local requirements = quest["Requirements"]
			local rewards = quest["Rewards"]

			local questFolder = Instance.new("Folder", plr.Data.QuestData)
			questFolder.Name = questName
			local reqFolder = Instance.new("Folder", questFolder)
			reqFolder.Name = "Requirements"
			local rewardFolder = Instance.new("Folder", questFolder)
			rewardFolder.Name = "Rewards"

			for number, requirementStats in pairs(requirements) do
				local requirement = requirementStats[1]
				local amount = requirementStats[2]

				local value = Instance.new("IntValue", reqFolder)
				value.Name = requirement
				value.Value = amount

			end

			for number, rewardStats in pairs(rewards) do
				local reward = rewardStats[1]
				local amount = rewardStats[2]

				local value = Instance.new("IntValue", rewardFolder)
				value.Name = reward
				value.Value = amount
			end
		end
	end
end

return module

Ok, thats alot of code, but let me explain. Most of this explains itself.

First, quests[questName] checks to make sure the Quest's name is a vaild quest.

Next, the checkForQuest(plr, questName) is a function that we will get to soon.

Then, we get the Quest and the Requirements and Rewards of the quest.

After that function, we set up the Folders that will store the quest data.

We loop through the requirements and rewards and make a new value for each one. We also name the value to the requirement or reward.

Check For Quest Function Keep the QuestModule open, and lets create a new function called checkForQuest!

local module = {}

local quests = {

 ["Test"] = {
        Requirements = {{"Stick", 1}, {"Metal", 4}};
        Rewards = {{"EXP", 125}, {"Gold", 1}};
    };	

}

function module.addQuest(plr, questName)
	if quests[questName] then -- Makes sure the quest is a valid quest
		if checkForQuest(plr, questName) == false then
			local quest = quests[questName]

			local requirements = quest["Requirements"]
			local rewards = quest["Rewards"]

			local questFolder = Instance.new("Folder", plr.Data.QuestData)
			questFolder.Name = questName
			local reqFolder = Instance.new("Folder", questFolder)
			reqFolder.Name = "Requirements"
			local rewardFolder = Instance.new("Folder", questFolder)
			rewardFolder.Name = "Rewards"

			for number, requirementStats in pairs(requirements) do
				local requirement = requirementStats[1]
				local amount = requirementStats[2]

				local value = Instance.new("IntValue", reqFolder)
				value.Name = requirement
				value.Value = amount

			end

			for number, rewardStats in pairs(rewards) do
				local reward = rewardStats[1]
				local amount = rewardStats[2]

				local value = Instance.new("IntValue", rewardFolder)
				value.Name = reward
				value.Value = amount
			end
		end
	end
end

-- New stuff below
function checkForQuest(plr, questName)
	if plr.Data.QuestData:FindFirstChild(questName) then
		print(plr.Name,"has the quest:",questName)
		return true
	else
		print(plr.Name,"doesn't have the quest:",questName)
		return false
	end
end

return module

The checkForQuest() function is very small, but it will allow us to make sure the player doesn't get the same quest.

We get the plr.Data.QuestData and then try to see if they already have that quest.

Update Quest Function To create our next function, keep the QuestModule open!

local module = {}

local quests = {

 ["Test"] = {
        Requirements = {{"Stick", 1}, {"Metal", 4}};
        Rewards = {{"EXP", 125}, {"Gold", 1}};
    };

}

function module.addQuest(plr, questName)
	if quests[questName] then -- Makes sure the quest is a valid quest
		if checkForQuest(plr, questName) == false then
			local quest = quests[questName]

			local requirements = quest["Requirements"]
			local rewards = quest["Rewards"]

			local questFolder = Instance.new("Folder", plr.Data.QuestData)
			questFolder.Name = questName
			local reqFolder = Instance.new("Folder", questFolder)
			reqFolder.Name = "Requirements"
			local rewardFolder = Instance.new("Folder", questFolder)
			rewardFolder.Name = "Rewards"

			for number, requirementStats in pairs(requirements) do
				local requirement = requirementStats[1]
				local amount = requirementStats[2]

				local value = Instance.new("IntValue", reqFolder)
				value.Name = requirement
				value.Value = amount

			end

			for number, rewardStats in pairs(rewards) do
				local reward = rewardStats[1]
				local amount = rewardStats[2]

				local value = Instance.new("IntValue", rewardFolder)
				value.Name = reward
				value.Value = amount
			end
		end
	end
end

function checkForQuest(plr, questName)
	if plr.Data.QuestData:FindFirstChild(questName) then
		print(plr.Name,"has the quest:",questName)
		return true
	else
		print(plr.Name,"doesn't have the quest:",questName)
		return false
	end
end

-- New stuff below
function module.updateQuest(plr, questName, action)
	if quests[questName] then
		for i, quest in pairs(plr.Data.QuestData:GetChildren()) do
			if quest.Requirements:FindFirstChild(action) then
				if quest.Requirements:FindFirstChild(action).Value > 0 then
					quest.Requirements:FindFirstChild(action).Value -= 1
				end
			end
		end
		
		completeQuest(plr, questName)
	end
end

return module

Ok, so this is how we are gonna update the player's quests when they do a action in your game.

If we find that one of the requirements are named the exactly the same as the action, then we will update the requirement.

At the very end, we have the completeQuest function. We will cover that next!

Complete Quest Function Last function in the QuestModule!

local module = {}

local quests = {

 ["Test"] = {
        Requirements = {{"Stick", 1}, {"Metal", 4}};
        Rewards = {{"EXP", 125}, {"Gold", 1}};
    };

}

function module.addQuest(plr, questName)
	if quests[questName] then -- Makes sure the quest is a valid quest
		if checkForQuest(plr, questName) == false then
			local quest = quests[questName]

			local requirements = quest["Requirements"]
			local rewards = quest["Rewards"]

			local questFolder = Instance.new("Folder", plr.Data.QuestData)
			questFolder.Name = questName
			local reqFolder = Instance.new("Folder", questFolder)
			reqFolder.Name = "Requirements"
			local rewardFolder = Instance.new("Folder", questFolder)
			rewardFolder.Name = "Rewards"

			for number, requirementStats in pairs(requirements) do
				local requirement = requirementStats[1]
				local amount = requirementStats[2]

				local value = Instance.new("IntValue", reqFolder)
				value.Name = requirement
				value.Value = amount

			end

			for number, rewardStats in pairs(rewards) do
				local reward = rewardStats[1]
				local amount = rewardStats[2]

				local value = Instance.new("IntValue", rewardFolder)
				value.Name = reward
				value.Value = amount
			end
		end
	end
end

function checkForQuest(plr, questName)
	if plr.Data.QuestData:FindFirstChild(questName) then
		print(plr.Name,"has the quest:",questName)
		return true
	else
		print(plr.Name,"doesn't have the quest:",questName)
		return false
	end
end

function module.updateQuest(plr, questName, action)
	if quests[questName] then
		for i, quest in pairs(plr.Data.QuestData:GetChildren()) do
			if quest.Requirements:FindFirstChild(action) then
				if quest.Requirements:FindFirstChild(action).Value > 0 then
					quest.Requirements:FindFirstChild(action).Value -= 1
				end
			end
		end
		
		completeQuest(plr, questName)
	end
end

-- New stuff below
function completeQuest(plr, questName)
	if checkForQuest(plr, questName) == true then
		local questData = plr.Data.QuestData:FindFirstChild(questName)
		
		local requirements = questData:FindFirstChild("Requirements")
		local rewards = questData:FindFirstChild("Rewards")
		
		for i, v in pairs(requirements:GetChildren()) do
			if v.Value ~= 0 then
				print(plr.Name,"hasn't completed all the requirements!")
				return
			else
				for number, reward in pairs(rewards:GetChildren()) do
					if reward.Name == "EXP" then
						-- Give the player EXP (Or whatever your reward was!)
					end
				end
				
				questData:Destroy()
			end
		end
	end
end

return module

We are almost done! Now let me explain what the completeQuest function does.

Once again, we use the checkForQuest function. If the player has the quest, then we can give them there rewards.

We do one last check to make sure all the requirement's values are at 0, meaning that we have completed them all.

If the player did complete all of there requirements, then we loop though all of the rewards for the quest. We check the name of the reward and give the player the reward that they earned!

Giving the player a quest Note: For now, we will be Hard Coding the quest in. If you do want to make a NPC give a quest, make sure to use the addQuest function from the server ONLY. You will also want to always keep updating the players quest using the updateQuest function. I will talk about this a bit later!


Remember the QuestServer script? Well you will need to enter that script now!

Lets Hard Code the quest in for now. Please read the note just above this message if you want to know how to add it into a NPC.

local SS = game:GetService("ServerStorage")
local Players = game:GetService("Players")
local questModule = require(SS:FindFirstChild("QuestModule")

Players.PlayerAdded:Connect(function(player)
    local questFolder = Instance.new("Folder", player)
    questFolder.Name = "Quests"

    player.CharacterAdded:Connect(function(character)
        task.wait(5)
       -- New stuff below
		spawn(function()
			questModule.addQuest(player, "Test")
		end)
    end)
end)

Ok, so we have called the addQuest function after 5 seconds. Remember, that the addQuest function will now give the player there new quest "Test".


NOTES


So, I said I would talk more about adding the quest to a NPC. Adding this quest system into a NPC is easy! You will need to know how to use Remote Events. You will need UI with a button to accept the quest. Once the player clicks the button, then you will want to Fire the Remote Event. Now, please take note that when you fire a Remote Event on the client, hackers can easily accepts every quest without talking to the NPC. Once you use Fire the Remote Event, use the .OnServerEvent and from there, use the addQuest function. Make sure you pass the right varibles into the function! addQuest(player, questName)

EXAMPLE:

Client

button.MouseButton1Up:Connect(function()
	remoteEvent:FireServer(questName)
end)

Server

remoteEvent.OnServerEvent:Connect(function(plr, questName)
	questModule.addQuest(plr, questName)
end)

How to Update the quests

If you have a item pickup system, or a part that the player will need to touch to update the quest, this will work. You will need a Item Pickup system or a Part Touching System.

I am only going to show how to update the quest if the player touches a part.

EXAMPLE:

Server

part.Touched:Connect(function(hit)
	if game:GetService("Players"):FindFirstChild(hit.Parent.Name) then

		questModule.updateQuest(game:GetService("Players"):FindFirstChild(hit.Parent.Name, part.QuestName.Value, "Reached Bandits Village")
	end
end)

I know I didn't use a debounce, but you could add one if you wanted. The part that you have to touch to reach the end must have a StringValue inside of the part. This StringValue must be named "QuestName". The value of the StringValue will be the quest that you will update once you touch the part. The updateQuest() needs 3 parameters, updateQuest(plr, questName, action).


Ending


I hope this helped! Keep in mind that I didn't use the best methods, but they work! Also, I'm sorry that I didn't use any pictures. Anyways, I put a lot of time into this and I would like if you guys have any feedback to post it into the comments below!

Thanks for much for reading this!!

Thanks - Witch(@witch2352)

View in-game to comment, award, and more!