Nazuh's QuestService - Versatile easy to use event based Quest System

Nazuh's QuestService


Nazuh's QuestService is a versatile easy to use event based Quest System. I originally made this for a commission, but I modified it a bit to release it as an open source resource, so enjoy 😁.

Special thanks to these awesome developers for making this OS resource possible:
@stravant- GoodSignal
@loleris - ReplicaService

Links


RBXM File: Link

Github Repo: Link

Unlocked Test World (NOTE: the UI in this test world is NOT tied to the system itself! It’s just something I threw together to visualize your quests. You can implement your UI however you want.): Link

NOTE:
I most likely won’t be providing a creator store link to this resource due to the bad experience
I’ve had with asset moderation in the past.



Setup

Below is an extremely basic implementation of QuestService.

ReplicatedStorage.Quests:

return {
	{
		-- These two values are required
		Id = 'TestQuest',
		MaxValue = 10,
		
		-- These two values have default values of 'Unknown'.
		-- You can edit the default values specific to your
		-- game in QuestService.LoadableContent.Core.ReplicatedStorage.QuestUtil.
		DisplayName = 'Defeat Zombies',
		Description = 'Defeat 10 Zombies!',
		
		ReallyCustomQuestValueSpecificToYourGame = 'totally possible!',
		
		-- Events for Everything 😃
		CompletedCallback = function(Player: Player)
			print('Completed')
		end,
		IncrementCallback = function(Player: Player, NewAmount: number)
			print('Incremented')
		end,
		DecrementCallback = function(Player: Player, NewAmount: number)
			print('Decremented')
		end,
		QuestStartedCallback = function(Player: Player)
			print('Started')
		end,
		QuestRemovedCallback = function(Player: Player)
			print('Removed')
		end
	}
}

Server:

-- Services
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local PlayerService = game:GetService('Players')

-- Imports
local QuestService = require(script.QuestService)

-- Local Functions
local function PlayerAdded(Player: Player)
	local QuestProfile = QuestService:InitializePlayer(Player, { }) -- Pass already obtained quests here
	
	QuestProfile.QuestStarted:Connect(function(QuestInstance)
		print(Player.DisplayName, 'Started Quest:', QuestInstance.Id)
		
		-- Update data in datastore
		-- ...
	end)
	
	QuestProfile.QuestRemoved:Connect(function(QuestInstance)
		print(Player.DisplayName, 'Ended Quest:', QuestInstance.Id)
		
		-- Update data in datastore
		-- ...
	end)
	
	QuestProfile.QuestUpdated:Connect(function(QuestInstance)
		print(Player.DisplayName, 'Progressed Quest:', QuestInstance.Id)
		
		-- Update data in datastore
		-- ...
	end)
	
	QuestService:AddQuestToPlayer(Player, 'TestQuest')
end

-- Runtime

-- Startup the quest service
QuestService:Init()

-- Load your base quests into the quest service
QuestService:RegisterModule(ReplicatedStorage.Quests, true)

PlayerService.PlayerAdded:Connect(PlayerAdded)
for _, Player in PlayerService:GetPlayers() do
	task.spawn(PlayerAdded, Player)
end

Client:

-- Services
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local PlayerService = game:GetService('Players')

-- Imports
--- Quest Controller is automatically loaded into replicated storage, but if you want
--- type hints you can move it there before runtime.
local QuestController = require(ReplicatedStorage:WaitForChild('QuestController'))

-- Runtime
QuestController.QuestsLoaded:Connect(function(QuestInstances)
	print('Quests Loaded', QuestInstances)
	
	for _, Quest in QuestInstances do
		-- The Base Quests are what you previously loaded into
		-- the quest controller.
		local BaseQuest = QuestController:GetBaseQuest(Quest.Id)
		
		-- ...
	end
	-- ...
end)

QuestController.QuestStarted:Connect(function(QuestInstance)
	print('Quest Started', QuestInstance)
	
	-- ...
end)

QuestController.QuestRemoved:Connect(function(QuestInstance)
	print('Quest Removed', QuestInstance)
	
	-- ...
end)

QuestController.QuestUpdated:Connect(function(QuestInstance)
	print('Quest Updated', QuestInstance)
	
	-- ...
end)

-- Startup the QuestController
QuestController:Init()

-- Load your base quests into the quest controller
QuestController:RegisterModule(ReplicatedStorage:WaitForChild('Quests'))
18 Likes

Hi, looks nice!

On this, the door says unknown ? why?

what is the box on the bottom?


features…

  1. can it be added to where it says you get something such as 400 gold, or a tool , when you complete the quest?

  2. can there be an open and closed button?

  3. can it sort it by quests not completed?

  4. can there be like, levels of quests, like say there are 5 level one quests that need to be completed before starting level 2 quests?

Thanks!

2 Likes

I could answer the first question, my guess is that the title is self explanatory and you dont need more explanation to it. Therefore, unknown.
Could’ve probably used a better word or just no word at all

1 Like

Some feedback of my own:

  1. Yeah i know your probably not a UI dev but it could use work

  2. i assume that bottom box is a timer but you dont mention it in your post so we dont know how it works

  3. automatically give a new quest once 1 is done! (i know you could do this manually)

Still its pretty clean! on the outside, cant view it on the inside at the moment

1 Like

The UI in the test world is separated from the system itself, it was just an example I made to show off the system’s capabilities.

On this, the door says unknown ? why?

This is because I removed the Description variable in the quests module in replicated storage so it defaulted to Unknown.

can it be added to where it says you get something such as 400 gold, or a tool , when you complete the quest?

As explained in the original post, there are callbacks you can add to the quest, and the callback you’d want in this case, would be the CompletedCallback. You can add your own logic into that function to give a player a reward upon completion.

can there be an open and closed button?
can it sort it by quests not completed?
can there be like, levels of quests, like say there are 5 level one quests that need to be completed before starting level 2 quests?

Just to add on to what I had said before, the UI is completely separated from the quest system itself, so you’d be able to easily add this logic if needed.

I should’ve been a lot more specific in my original post as it seems like it’s confusing a lot of people. Apologies for that. So to start off, the UI in the test world is completely separate from Quest Service itself and is purely meant to show off how someone could use this inside their game.

Yeah i know your probably not a UI dev but it could use work

i assume that bottom box is a timer but you dont mention it in your post so we dont know how it works

Yeah I’m definitely not a UI artist, this definitely shows lol. This UI was the prototype UI for the original commission hence the reason for the timer at the bottom (They wanted a quest system + daily quests).

automatically give a new quest once 1 is done! (i know you could do this manually)

This is definitely possible with the CompletedCallback.

Still its pretty clean! on the outside, cant view it on the inside at the moment

I tried to made everything nice and tidy for release, though in the codebase there’ll probably be some places where that standard isn’t met, so if you see something I’ll try and fix that up. I’m also going to try to create documentation for this once I have time, to better explain how to use this and it’s capabilities.

1 Like

I appreciate your response
I am usually expecting the world when i give it criticism to fire back at me

On the note about what you actually replied, I am slightly stupid for not reading the original post about completedcallback. (sorry)
I dont really have any other things to say then good job :+1:

1 Like

Hello, I really like this system however I could not find any proper documentation other than your example scripts. Is there any documentation for this?

not at the moment. though i can try and pump something out in a few hours for you.

No need right now as I pretty much reverse engineered everything lol.

1 Like

Who tf is nazuh :fire:

CHARSSSSSS

1 Like

great question!

image

or not really…

ummm

any updates though?

Ooooh…

I think thats the guy in the image…
image

Thanks for helping me find out man! :pray: :pray: :joy:

Nazuh, Would still recommend adding a documentation because not everyone has the brain cells to reverse engineer scripts :slight_smile:

No updates at the moment. If you have any suggestions feel free to share them with me and I’ll see what I can do.

v1.0.1 Hotfix

  • Fixed bug where when a player’s quests get updated, it replicates the changes to all players as if they were affected by it.

Would be cool if you add stuff like quest callbacks etc so we can make custom stuff happen when a quest gets finished

that’s already inplemented, you’d have to implement the functionality onto your base quest objects

return {
	{
		-- These two values are required
		Id = 'TestQuest',
		MaxValue = 10,
		
		-- These two values have default values of 'Unknown'.
		-- You can edit the default values specific to your
		-- game in QuestService.LoadableContent.Core.ReplicatedStorage.QuestUtil.
		DisplayName = 'Defeat Zombies',
		Description = 'Defeat 10 Zombies!',
		
		ReallyCustomQuestValueSpecificToYourGame = 'totally possible!',
		
		-- Events for Everything 😃
		CompletedCallback = function(Player: Player)
			print('Completed')
		end,
		IncrementCallback = function(Player: Player, NewAmount: number)
			print('Incremented')
		end,
		DecrementCallback = function(Player: Player, NewAmount: number)
			print('Decremented')
		end,
		QuestStartedCallback = function(Player: Player)
			print('Started')
		end,
		QuestRemovedCallback = function(Player: Player)
			print('Removed')
		end
	}
}

Ooh, Small googly eyes didn’t see it :no_mouth:

For anyone who hasn’t been able to grab the binary file and get it to work, I’ve released a new version. I had accidentally thrown the rojo build file (which was an rbxl) instead of just the QuestService module.