local H=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local u=require((H:WaitForChild("\080\114\111\106\101\099\116")):WaitForChild("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101"))local z=(require((H:WaitForChild("\080\114\111\106\101\099\116")):WaitForChild("\083\101\114\118\101\114\083\099\114\105\112\116\083\101\114\118\105\099\101"))):GetContext(script)local A=u:GetResource("\071\097\109\101\068\097\116\097\046\081\117\101\115\116\046\081\117\101\115\116\115")local Z=u:GetResource("\071\097\109\101\068\097\116\097\046\073\116\101\109\046\073\116\101\109\115")local W=u:GetResource("\071\097\109\101\068\097\116\097\046\071\101\110\101\114\097\116\105\111\110\046\078\080\067\076\111\099\097\116\105\111\110\115")local s=u:GetResource("\083\116\097\116\101\046\081\117\101\115\116\115")local V=u:GetResource("\071\097\109\101\082\101\112\108\105\099\097\116\105\111\110")local U=(u:GetResource("\069\120\116\101\114\110\097\108\085\116\105\108\046\078\101\120\117\115\073\110\115\116\097\110\099\101\046\078\101\120\117\115\073\110\115\116\097\110\099\101")):Extend()U:SetClassName("\081\117\101\115\116\083\101\114\118\105\099\101")U.PlayerQuests={}z:SetContextResource(U)local d=z:GetResource("\083\101\114\118\105\099\101\046\073\110\118\101\110\116\111\114\121\083\101\114\118\105\099\101")local f=z:GetResource("\083\101\114\118\105\099\101\046\080\108\097\121\101\114\068\097\116\097\083\101\114\118\105\099\101")local v=Instance.new("\070\111\108\100\101\114")v.Name="\081\117\101\115\116\082\101\112\108\105\099\097\116\105\111\110"v.Parent=V local B=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")B.Name="\084\117\114\110\073\110\081\117\101\115\116"B.Parent=v B.OnServerEvent:Connect(function(H,u,z)U:CompleteQuest(H,u,z)end)local R=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")R.Name="\083\116\097\114\116\081\117\101\115\116"R.Parent=v R.OnServerEvent:Connect(function(H,u,z)U:StartQuest(H,u,z)end)local g=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")g.Name="\083\101\108\101\099\116\081\117\101\115\116"g.Parent=v g.OnServerEvent:Connect(function(H,u)U:SelectQuest(H,u)end)local T=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")T.Name="\068\105\115\112\108\097\121\073\116\101\109\065\119\097\114\100\115"T.Parent=v function U.LoadPlayer(u,H)u.PlayerQuests[H]=s.GetQuests(H)end function U.QuestConditonValid(A,H,u,z)return A.PlayerQuests[H]:QuestConditonValid(u,z)end function U.StartQuest(A,H,u,z)local Z=A.PlayerQuests[H]if not Z:QuestConditonValid(z,"\078\111\116\085\110\108\111\099\107\101\100")then return end local W=Z:GetDialogForNPC(u)if not W then return end local function s(H)if z==H.Quest then return true end for H,u in pairs(H)do if type(u)=="\116\097\098\108\101"and s(u)then return true end end return false end if s(W)then Z:AddQuest(z)end end function U.CompleteQuest(s,H,u,z)local V=s.PlayerQuests[H]if not V:QuestConditonValid(z,"\065\108\108\073\116\101\109\115")then return end local U=V:GetDialogForNPC(u)if not U then return end local function v(H)if z==H.TurnIn then return true end for H,u in pairs(H)do if type(u)=="\116\097\098\108\101"and v(u)then return true end end return false end if v(U)then V:CompleteQuest(z)local s=A[z]if s.QuestType=="\073\116\101\109\115"then local u=s.RequiredItems[1]d:RemoveItemsByName(H,u.Name)end local U={}for z,A in pairs(s.Rewards or{})do local s=f:GetPlayerData(H)if z=="\066\097\100\103\101"then local H=s:GetValue("\067\111\108\108\101\099\116\101\100\066\108\111\120\107\105\110\115")H[A]=true s:SetValue("\067\111\108\108\101\099\116\101\100\066\108\111\120\107\105\110\115",H)table.insert(U,{Type="\066\108\111\120\107\105\110",Name=A})elseif z=="\088\080"then local H=s:GetValue("\088\080")+A s:SetValue("\088\080",H)elseif z=="\073\116\101\109\115"then for z,s in pairs(A)do for z=1,s.Amount,1 do local V={Name=s.Name}if Z[s.Name].IncludeZoneData then V.Zone=W[u].Zone end d:AddItem(H,V)table.insert(U,{Type="\073\116\101\109",Name=A})end end end end T:FireClient(H,U)end end function U.SelectQuest(z,H,u)z.PlayerQuests[H]:SelectQuest(u)end function U.RegisterMonsterKill(z,H,u)z.PlayerQuests[H]:RegisterMonsterKill(u)end function U.ClearPlayer(u,H)u.PlayerQuests[H]=nil end return U
--[[
TheNexusAvenger

Manages the quests for players.
Class is static (should not be created).
--]]

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local ReplicatedStorageProject = require(ReplicatedStorage:WaitForChild("Project"):WaitForChild("ReplicatedStorage"))
local ServerScriptServiceProject = require(ReplicatedStorage:WaitForChild("Project"):WaitForChild("ServerScriptService")):GetContext(script)

local QuestsData = ReplicatedStorageProject:GetResource("GameData.Quest.Quests")
local ItemData = ReplicatedStorageProject:GetResource("GameData.Item.Items")
local NPCLocations = ReplicatedStorageProject:GetResource("GameData.Generation.NPCLocations")
local Quests = ReplicatedStorageProject:GetResource("State.Quests")
local GameReplication = ReplicatedStorageProject:GetResource("GameReplication")

local QuestService = ReplicatedStorageProject:GetResource("ExternalUtil.NexusInstance.NexusInstance"):Extend()
QuestService:SetClassName("QuestService")
QuestService.PlayerQuests = {}
ServerScriptServiceProject:SetContextResource(QuestService)

local InventoryService = ServerScriptServiceProject:GetResource("Service.InventoryService")
local PlayerDataService = ServerScriptServiceProject:GetResource("Service.PlayerDataService")



--Set up the replication.
local QuestReplication = Instance.new("Folder")
QuestReplication.Name = "QuestReplication"
QuestReplication.Parent = GameReplication

local TurnInQuest = Instance.new("RemoteEvent")
TurnInQuest.Name = "TurnInQuest"
TurnInQuest.Parent = QuestReplication

TurnInQuest.OnServerEvent:Connect(function(Player,NPCName,QuestName)
    QuestService:CompleteQuest(Player,NPCName,QuestName)
end)

local StartQuest = Instance.new("RemoteEvent")
StartQuest.Name = "StartQuest"
StartQuest.Parent = QuestReplication

StartQuest.OnServerEvent:Connect(function(Player,NPCName,QuestName)
    QuestService:StartQuest(Player,NPCName,QuestName)
end)

local SelectQuest = Instance.new("RemoteEvent")
SelectQuest.Name = "SelectQuest"
SelectQuest.Parent = QuestReplication

SelectQuest.OnServerEvent:Connect(function(Player,QuestName)
    QuestService:SelectQuest(Player,QuestName)
end)

local DisplayItemAwards = Instance.new("RemoteEvent")
DisplayItemAwards.Name = "DisplayItemAwards"
DisplayItemAwards.Parent = QuestReplication



--[[
Initializes the data for a player.
--]]
function QuestService:LoadPlayer(Player)
    self.PlayerQuests[Player] = Quests.GetQuests(Player)
end

--[[
Returns if the given quest is valid
for a given condition.
--]]
function QuestService:QuestConditonValid(Player,QuestName,Condition)
    return self.PlayerQuests[Player]:QuestConditonValid(QuestName,Condition)
end

--[[
Starts a quest for a player. Performs security
checks to make sure the player can only accept a
quest in the current dialog. The quest name
is required because dialogs can award multiple
quests.
--]]
function QuestService:StartQuest(Player,NPCName,QuestName)
    --Return if the quest is not unlocked.
    local PlayerQuests = self.PlayerQuests[Player]
    if not PlayerQuests:QuestConditonValid(QuestName,"NotUnlocked") then
        return
    end

    --Get the NPC dialog and return if it doesn't exist.
    local Dialog = PlayerQuests:GetDialogForNPC(NPCName)
    if not Dialog then
        return
    end

    --[[
    Returns if the quest is valid for a given table.
    --]]
    local function QuestValid(DialogTable)
        --Return true if the quest matches.
        if QuestName == DialogTable.Quest then
            return true
        end

        --Return if a sub-table is true.
        for _,SubTable in pairs(DialogTable) do
            if type(SubTable) == "table" and QuestValid(SubTable) then
                return true
            end
        end

        --Return false (not valid).
        return false
    end

    --Add the quest if it is valid.
    if QuestValid(Dialog) then
        PlayerQuests:AddQuest(QuestName)
    end
end

--[[
Completes a quest for a player. The quest
name is not given since each NPC 
--]]
function QuestService:CompleteQuest(Player,NPCName,QuestName)
    --Return if the quest is not able to be turned in.
    local PlayerQuests = self.PlayerQuests[Player]
    if not PlayerQuests:QuestConditonValid(QuestName,"AllItems") then
        return
    end

    --Get the NPC dialog and return if it doesn't exist.
    local Dialog = PlayerQuests:GetDialogForNPC(NPCName)
    if not Dialog then
        return
    end

    --[[
    Returns if the quest is valid for a given table.
    --]]
    local function QuestValid(DialogTable)
        --Return true if the turn in quest matches.
        if QuestName == DialogTable.TurnIn then
            return true
        end

        --Return if a sub-table is true.
        for _,SubTable in pairs(DialogTable) do
            if type(SubTable) == "table" and QuestValid(SubTable) then
                return true
            end
        end

        --Return false (not valid).
        return false
    end

    --Complete the quest if it is valid.
    if QuestValid(Dialog) then
        PlayerQuests:CompleteQuest(QuestName)
        
        --Remove the quest items.
        local QuestData = QuestsData[QuestName]
        if QuestData.QuestType == "Items" then
            local RequiredItems = QuestData.RequiredItems[1]
            InventoryService:RemoveItemsByName(Player,RequiredItems.Name)
        end

        --Reward the player.
        local RewardsToDisplay = {}
        for RewardType,Reward in pairs(QuestData.Rewards or {}) do
            local PlayerData = PlayerDataService:GetPlayerData(Player)
            if RewardType == "Badge" then
                --Award a bloxkin.
                local Bloxkins = PlayerData:GetValue("CollectedBloxkins")
                Bloxkins[Reward] = true
                PlayerData:SetValue("CollectedBloxkins",Bloxkins)
                table.insert(RewardsToDisplay,{Type="Bloxkin",Name=Reward})
            elseif RewardType == "XP" then
                --Award XP.
                local XP = PlayerData:GetValue("XP") + Reward
                PlayerData:SetValue("XP",XP)
            elseif RewardType == "Items" then
                --Award items.
                for _,Item in pairs(Reward) do
                    for i = 1,Item.Amount do
                        local NewItem = {Name=Item.Name}
                        if ItemData[Item.Name].IncludeZoneData then
                            NewItem.Zone = NPCLocations[NPCName].Zone
                        end
                        InventoryService:AddItem(Player,NewItem)
                        table.insert(RewardsToDisplay,{Type="Item",Name=Reward})
                    end
                end
            end
        end
        DisplayItemAwards:FireClient(Player,RewardsToDisplay)
    end
end

--[[
Selects the primary quest for the player.
--]]
function QuestService:SelectQuest(Player,QuestName)
    self.PlayerQuests[Player]:SelectQuest(QuestName)
end

--[[
Registers a monster kill.
--]]
function QuestService:RegisterMonsterKill(Player,MonsterName)
    self.PlayerQuests[Player]:RegisterMonsterKill(MonsterName)
end

--[[
Clears a player.
--]]
function QuestService:ClearPlayer(Player)
    self.PlayerQuests[Player] = nil
end



return QuestService