CCCPaste

ObjectiveServer

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)