local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local AllQuestData = require(ReplicatedStorage:WaitForChild("ObjectiveData"))
local eventsFolder = ReplicatedStorage:FindFirstChild("ObjectiveEvents") or Instance.new("Folder")
eventsFolder.Name = "ObjectiveEvents"
eventsFolder.Parent = ReplicatedStorage
local function ensureEvent(name)
local event = eventsFolder:FindFirstChild(name) or Instance.new("RemoteEvent")
event.Name = name
event.Parent = eventsFolder
return event
end
local updateEvent = ensureEvent("UpdateObjective")
local initEvent = ensureEvent("InitObjectives")
local questStartEvent = ensureEvent("QuestStarted")
local questCompleteEvent = ensureEvent("QuestCompleted")
local playerData = {}
local globalProgress = {}
for _, quest in ipairs(AllQuestData) do
globalProgress[quest.QuestName] = {}
for _, obj in ipairs(quest.Objectives) do
if obj.Type == "Global" then
globalProgress[quest.QuestName][obj.Name] = false
end
end
end
local function hasTool(player, required)
if not required then return true end
local backpack = player:FindFirstChildOfClass("Backpack")
local character = player.Character
if not backpack or not character then return false end
return (backpack:FindFirstChild(required) or character:FindFirstChild(required)) ~= nil
end
local function isQuestComplete(player, questName)
local data = playerData[player.UserId]
if not data or not data.Quests[questName] then return false end
for _, completed in pairs(data.Quests[questName].Completed) do
if not completed then return false end
end
return true
end
local function startQuestline(player, questName)
local data = playerData[player.UserId]
if not data then return end
if data.Quests[questName] and data.Quests[questName].Started then
initEvent:FireClient(player, questName, data.Quests[questName].Completed)
return
end
local quest
for _, q in ipairs(AllQuestData) do
if q.QuestName == questName then
quest = q
break
end
end
if not quest then
warn("Questline not found:", questName)
return
end
data.Quests[questName] = { Started = true, Completed = {} }
for _, obj in ipairs(quest.Objectives) do
data.Quests[questName].Completed[obj.Name] = globalProgress[quest.QuestName][obj.Name] or false
end
initEvent:FireClient(player, questName, data.Quests[questName].Completed)
questStartEvent:FireClient(player, questName)
end
local function startNextObjective(player, quest, currentObj)
local nextName = currentObj.NextObjective
if not nextName then return end
local data = playerData[player.UserId]
if not data or not data.Quests[quest.QuestName] then return end
if data.Quests[quest.QuestName].Completed[nextName] ~= nil then
data.Quests[quest.QuestName].Completed[nextName] = false
updateEvent:FireClient(player, quest.QuestName, nextName, false)
end
end
local function completeObjective(player, quest, obj)
local questName, objName = quest.QuestName, obj.Name
local data = playerData[player.UserId]
if not data or not data.Quests[questName] then return end
if data.Quests[questName].Completed[objName] then return end
if obj.Type == "Global" then
if not globalProgress[questName][objName] then
globalProgress[questName][objName] = true
end
data.Quests[questName].Completed[objName] = true
for _, plr in ipairs(Players:GetPlayers()) do
local pdata = playerData[plr.UserId]
if pdata and pdata.Quests[questName] and pdata.Quests[questName].Started then
if not pdata.Quests[questName].Completed[objName] then
pdata.Quests[questName].Completed[objName] = true
updateEvent:FireClient(plr, questName, objName, true)
end
end
end
else
data.Quests[questName].Completed[objName] = true
updateEvent:FireClient(player, questName, objName, true)
end
if isQuestComplete(player, questName) then
questCompleteEvent:FireClient(player, questName)
end
if obj.NextQuestline then
task.wait(1)
startQuestline(player, obj.NextQuestline)
elseif obj.EndQuest then
questCompleteEvent:FireClient(player, questName)
else
startNextObjective(player, quest, obj)
end
end
for _, quest in ipairs(AllQuestData) do
for _, obj in ipairs(quest.Objectives) do
local trigger = obj.Trigger
if not trigger then continue end
if trigger:IsA("ProximityPrompt") then
trigger.Triggered:Connect(function(player)
if hasTool(player, obj.Requires) then
completeObjective(player, quest, obj)
end
end)
elseif trigger:IsA("BasePart") then
trigger.Touched:Connect(function(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if player and hasTool(player, obj.Requires) then
completeObjective(player, quest, obj)
end
end)
elseif obj.TriggerType == "CanCollideChange" then
trigger:GetPropertyChangedSignal("CanCollide"):Connect(function()
if trigger.CanCollide then
for _, plr in ipairs(Players:GetPlayers()) do
local pdata = playerData[plr.UserId]
if pdata and pdata.Quests[quest.QuestName] and pdata.Quests[quest.QuestName].Started then
completeObjective(plr, quest, obj)
end
end
end
end)
elseif obj.TriggerType == "ColorChange" and obj.ColorTarget then
trigger:GetPropertyChangedSignal("Color"):Connect(function()
if trigger.Color == obj.ColorTarget then
for _, plr in ipairs(Players:GetPlayers()) do
local pdata = playerData[plr.UserId]
if pdata and pdata.Quests[quest.QuestName] and pdata.Quests[quest.QuestName].Started then
completeObjective(plr, quest, obj)
end
end
end
end)
else
warn("Unsupported trigger type for", obj.Name)
end
end
end
for _, quest in ipairs(AllQuestData) do
if quest.StartTrigger then
local start = quest.StartTrigger
local function startQuest(player)
startQuestline(player, quest.QuestName)
end
if start:IsA("BasePart") then
start.Touched:Connect(function(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if player then startQuest(player) end
end)
elseif start:IsA("ProximityPrompt") then
start.Triggered:Connect(startQuest)
else
warn("Invalid StartTrigger type for quest:", quest.QuestName)
end
end
end
Players.PlayerAdded:Connect(function(player)
playerData[player.UserId] = playerData[player.UserId] or { Quests = {} }
player.CharacterAdded:Connect(function()
task.wait(1)
local data = playerData[player.UserId]
if not data then return end
for questName, questInfo in pairs(data.Quests) do
if questInfo.Started then
for objName, _ in pairs(questInfo.Completed) do
if globalProgress[questName] and globalProgress[questName][objName] then
questInfo.Completed[objName] = true
end
end
initEvent:FireClient(player, questName, questInfo.Completed)
end
end
end)
end)
Players.PlayerRemoving:Connect(function(player)
playerData[player.UserId] = nil
end)