local I={"\080\101\116\067\111\115\116\117\109\101\072\097\116","\080\101\116\067\111\115\116\117\109\101\067\111\108\108\097\114","\080\101\116\067\111\115\116\117\109\101\066\097\099\107","\080\101\116\067\111\115\116\117\109\101\065\110\107\108\101"}local x=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local u=require((x: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 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\079\098\106\101\099\116")local k=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\069\118\101\110\116\046\078\101\120\117\115\069\118\101\110\116")local R=u:GetResource("\071\097\109\101\068\097\116\097\046\073\116\101\109\046\073\116\101\109\115")local p=u:GetResource("\071\097\109\101\068\097\116\097\046\081\117\101\115\116\046\068\105\097\108\111\103\115")local z=u:GetResource("\071\097\109\101\068\097\116\097\046\081\117\101\115\116\046\081\117\101\115\116\115")local n=u:GetResource("\083\116\097\116\101\046\080\108\097\121\101\114\068\097\116\097")local J=u:GetResource("\083\116\097\116\101\046\073\110\118\101\110\116\111\114\121")local a=U:Extend()a.CachedQuests={}a:SetClassName("\081\117\101\115\116\115")function a.__new(x,I)x:InitializeSuper()x.PlayerData=n.GetPlayerData(I)x.Inventory=J.GetInventory(I)x.QuestsChanged=k.new()x.QuestData=x.PlayerData:GetValue("\081\117\101\115\116\115")x:FixQuestData();(x.PlayerData:GetValueChangedSignal("\081\117\101\115\116\115")):Connect(function()x.QuestData=x.PlayerData:GetValue("\081\117\101\115\116\115")x:FixQuestData()x.QuestsChanged:Fire()end)end function a.GetQuests(I)if not a.CachedQuests[I]then a.CachedQuests[I]=a.new(I)end return a.CachedQuests[I]end function a.FixQuestData(I)if not I.QuestData.CompletedQuests then I.QuestData.CompletedQuests={}end if not I.QuestData.ActiveQuests then I.QuestData.ActiveQuests={}end if not I.QuestData.MonsterKillCounters then I.QuestData.MonsterKillCounters={}end if I.QuestData.SelectedQuest and not I.QuestData.ActiveQuests[I.QuestData.SelectedQuest]then I.QuestData.SelectedQuest=nil end if not I.QuestData.SelectedQuest then for x,u in pairs(I.QuestData.ActiveQuests)do if u then I.QuestData.SelectedQuest=x break end end end end function a.QuestConditonValid(U,x,u)if type(u)=="\116\097\098\108\101"then for I,u in pairs(u)do if U:QuestConditonValid(x,u)then return true end end return false end if u=="\084\117\114\110\101\100\073\110"then return U.QuestData.CompletedQuests[x]==true elseif u=="\078\111\116\085\110\108\111\099\107\101\100"then return not U.QuestData.CompletedQuests[x]and not U.QuestData.ActiveQuests[x]else if U.QuestData.CompletedQuests[x]or not U.QuestData.ActiveQuests[x]then return false end local k=z[x]if k then if k.QuestType=="\073\116\101\109\115"then local I=k.RequiredItems if I and I[1]then local x=I[1]local k=#U.Inventory:GetItemSlots(x.Name)if u=="\073\110\118\101\110\116\111\114\121"then return k<x.Amount elseif u=="\065\108\108\073\116\101\109\115"then return k>=x.Amount end end elseif k.QuestType=="\068\114\101\115\115\117\112"then local x=k.RequiredSet if x then for I,k in pairs(I)do local p=U.Inventory:GetItem(k)if p and p.Name then local I=R[p.Name]if I and(I.CostumeName and I.CostumeName~=x.Name)then return u~="\065\108\108\073\116\101\109\115"end else return u~="\065\108\108\073\116\101\109\115"end end return u=="\065\108\108\073\116\101\109\115"end elseif k.QuestType=="\077\111\110\115\116\101\114\115"then local I=k.RequiredMonsters if I and I[1]then local x=I[1].Name local k=I[1].Amount or 0 local R=U.QuestData.MonsterKillCounters[x]or 0 if u=="\073\110\118\101\110\116\111\114\121"then return R<k elseif u=="\065\108\108\073\116\101\109\115"then return R>=k end end end end return true end end function a.GetDialogForNPC(x,I)if not p[I]then return end for I,u in pairs(p[I])do local U=true for I,u in pairs(u.RequiredQuests or{})do if not x:QuestConditonValid(u.Name,u.Status)then U=false break end end if U then return u end end end function a.GetStatusIcon(x,I)if x:QuestConditonValid(I,"\084\117\114\110\101\100\073\110")then return"\114\098\120\097\115\115\101\116\105\100\058\047\047\049\051\050\056\056\049\057\048\051"elseif x:QuestConditonValid(I,"\065\108\108\073\116\101\109\115")then return"\114\098\120\097\115\115\101\116\105\100\058\047\047\049\051\050\056\056\049\056\056\056"elseif x:QuestConditonValid(I,"\073\110\118\101\110\116\111\114\121")then return"\114\098\120\097\115\115\101\116\105\100\058\047\047\049\051\050\056\056\049\056\054\051"else return"\114\098\120\097\115\115\101\116\105\100\058\047\047\049\051\051\052\051\054\057\050\048"end end function a.GetStatusText(x,I)local u=z[I]if x:QuestConditonValid(I,"\078\111\116\085\110\108\111\099\107\101\100")then return"\078\111\116 \085\110\108\111\099\107\101\100"elseif x:QuestConditonValid(I,"\084\117\114\110\101\100\073\110")then return"\081\117\101\115\116 \067\111\109\112\108\101\116\101\033"elseif x:QuestConditonValid(I,"\065\108\108\073\116\101\109\115")and u.QuestType~="\078\080\067"then return"\082\101\097\100\121 \116\111 \116\117\114\110 \105\110\033"end if u.QuestType=="\073\116\101\109\115"then local I=u.RequiredItems[1]return tostring(#x.Inventory:GetItemSlots(I.Name))..("\047"..(tostring(I.Amount)..(" "..u.RequiredItems.DisplayName)))elseif u.QuestType=="\068\114\101\115\115\117\112"then return"\069\113\117\105\112 \116\104\101 "..u.RequiredSet.DisplayName elseif u.QuestType=="\077\111\110\115\116\101\114\115"then local I=u.RequiredMonsters[1]return tostring(x.QuestData.MonsterKillCounters[I.Name]or 0)..("\047"..(tostring(I.Amount)..(" "..u.RequiredMonsters.DisplayName)))elseif u.QuestType=="\078\080\067"then return"\070\105\110\100 "..u.RequiredNPC.Name end end function a.Save(I)I:FixQuestData()I.PlayerData:SetValue("\081\117\101\115\116\115",I.QuestData)end function a.AddQuest(x,I)if not x:QuestConditonValid(I,"\078\111\116\085\110\108\111\099\107\101\100")then return end local u=z[I]x.QuestData.ActiveQuests[I]=true if u and(u.RequiredMonsters and u.RequiredMonsters[1])then x.QuestData.MonsterKillCounters[u.RequiredMonsters[1].Name]=0 end x.QuestData.SelectedQuest=I x:Save()end function a.CompleteQuest(x,I)if not x:QuestConditonValid(I,"\065\108\108\073\116\101\109\115")then return end local u=z[I]x.QuestData.ActiveQuests[I]=nil x.QuestData.CompletedQuests[I]=true if u and(u.RequiredMonsters and u.RequiredMonsters[1])then x.QuestData.MonsterKillCounters[u.RequiredMonsters[1].Name]=nil end x:Save()end function a.SelectQuest(x,I)if not x:QuestConditonValid(I,{"\073\110\118\101\110\116\111\114\121";"\065\108\108\073\116\101\109\115"})then return end x.QuestData.SelectedQuest=I x:Save()end function a.RegisterMonsterKill(x,I)if not x.QuestData.MonsterKillCounters[I]then return end x.QuestData.MonsterKillCounters[I]=x.QuestData.MonsterKillCounters[I]+1 x:Save()end return a
--[[
TheNexusAvenger

Stores the state of quests and
validates dialogs (which are dependant
on quests).
--]]

local PET_EQIUP_SLOTS = {
    "PetCostumeHat",
    "PetCostumeCollar",
    "PetCostumeBack",
    "PetCostumeAnkle",
}



local ReplicatedStorage = game:GetService("ReplicatedStorage")

local ReplicatedStorageProject = require(ReplicatedStorage:WaitForChild("Project"):WaitForChild("ReplicatedStorage"))

local NexusObject = ReplicatedStorageProject:GetResource("ExternalUtil.NexusInstance.NexusObject")
local NexusEvent = ReplicatedStorageProject:GetResource("ExternalUtil.NexusInstance.Event.NexusEvent")
local ItemData = ReplicatedStorageProject:GetResource("GameData.Item.Items")
local Dialogs = ReplicatedStorageProject:GetResource("GameData.Quest.Dialogs")
local QuestsData = ReplicatedStorageProject:GetResource("GameData.Quest.Quests")
local PlayerData = ReplicatedStorageProject:GetResource("State.PlayerData")
local Inventory = ReplicatedStorageProject:GetResource("State.Inventory")

local Quests = NexusObject:Extend()
Quests.CachedQuests = {}
Quests:SetClassName("Quests")



--[[
Creates a quests object.
--]]
function Quests:__new(Player)
    self:InitializeSuper()

    --Store the state.
    self.PlayerData = PlayerData.GetPlayerData(Player)
    self.Inventory = Inventory.GetInventory(Player)
    self.QuestsChanged = NexusEvent.new()
    self.QuestData = self.PlayerData:GetValue("Quests")
    self:FixQuestData()

    --Connect the quests changing.
    self.PlayerData:GetValueChangedSignal("Quests"):Connect(function()
        self.QuestData = self.PlayerData:GetValue("Quests")
        self:FixQuestData()
        self.QuestsChanged:Fire()
    end)
end

--[[
Returns a quests object for a
given player.
--]]
function Quests.GetQuests(Player)
    --Create the cached object if it doesn't exist.
    if not Quests.CachedQuests[Player] then
        Quests.CachedQuests[Player] = Quests.new(Player)
    end

    --Return the cached object.
    return Quests.CachedQuests[Player]
end

--[[
Populates missing attributes of quest data.
--]]
function Quests:FixQuestData()
    --Add the missing tables.
    if not self.QuestData.CompletedQuests then
        self.QuestData.CompletedQuests = {}
    end
    if not self.QuestData.ActiveQuests then
        self.QuestData.ActiveQuests = {}
    end
    if not self.QuestData.MonsterKillCounters then
        self.QuestData.MonsterKillCounters = {}
    end

    --Remove the selected quest if it is not active.
    if self.QuestData.SelectedQuest and not self.QuestData.ActiveQuests[self.QuestData.SelectedQuest] then
        self.QuestData.SelectedQuest = nil
    end
    
    --Add a selected quest if none exists.
    if not self.QuestData.SelectedQuest then
        for QuestName,QuestActive in pairs(self.QuestData.ActiveQuests) do
            if QuestActive then
                self.QuestData.SelectedQuest = QuestName
                break
            end
        end
    end
end

--[[
Returns if the condition for a quest
is satisfied.
--]]
function Quests:QuestConditonValid(QuestName,Condition)
    --Return a true condition if the condition is a table.
    if type(Condition) == "table" then
        for _,SubCondtion in pairs(Condition) do
            if self:QuestConditonValid(QuestName,SubCondtion) then
                return true
            end
        end
        return false
    end

    if Condition == "TurnedIn" then
        --Return if the quest is completed.
        return self.QuestData.CompletedQuests[QuestName] == true
    elseif Condition == "NotUnlocked" then
        --Return if the quest hasn't been started.
        return not self.QuestData.CompletedQuests[QuestName] and not self.QuestData.ActiveQuests[QuestName]
    else
        --Return if the quest is completed or not started.
        if self.QuestData.CompletedQuests[QuestName] or not self.QuestData.ActiveQuests[QuestName] then
            return false
        end

        --Return based on the items.
        local QuestData = QuestsData[QuestName]
        if QuestData then
            if QuestData.QuestType == "Items" then
                local RequiredItems = QuestData.RequiredItems
                if RequiredItems and RequiredItems[1] then
                    local RequiredItem = RequiredItems[1]
                    local TotalItems = #self.Inventory:GetItemSlots(RequiredItem.Name)

                    if Condition == "Inventory" then
                        --Return if some but not all required items were obtained.
                        return TotalItems < RequiredItem.Amount
                    elseif Condition == "AllItems" then
                        --Return if all the required items were obtained.
                        return TotalItems >= RequiredItem.Amount
                    end
                end
            elseif QuestData.QuestType == "Dressup" then
                local RequiredSet = QuestData.RequiredSet
                if RequiredSet then
                    --Return if the slots are invalid.
                    for _,SlotName in pairs(PET_EQIUP_SLOTS) do
                        local Item = self.Inventory:GetItem(SlotName)
                        if Item and Item.Name then
                            local Data = ItemData[Item.Name]
                            if Data and Data.CostumeName and Data.CostumeName ~= RequiredSet.Name then
                                return Condition ~= "AllItems"
                            end
                        else
                            return Condition ~= "AllItems"
                        end
                    end
                    return Condition == "AllItems"
                end
            elseif QuestData.QuestType == "Monsters" then
                local RequiredMonsters = QuestData.RequiredMonsters
                if RequiredMonsters and RequiredMonsters[1] then
                    --Return if enough monsters were killed.
                    local RequiredMonstersName = RequiredMonsters[1].Name
                    local RequiredKills = RequiredMonsters[1].Amount or 0
                    local MonstersKilled = self.QuestData.MonsterKillCounters[RequiredMonstersName] or 0

                    if Condition == "Inventory" then
                        --Return if some but not all required items were obtained.
                        return MonstersKilled < RequiredKills
                    elseif Condition == "AllItems" then
                        --Return if all the required items were obtained.
                        return MonstersKilled >= RequiredKills
                    end
                end
            end
        end

        --Return true (no items required; such as finding an NPC).
        return true
    end
end

--[[
Returns the data of the dialog to use
for the given NPC.
--]]
function Quests:GetDialogForNPC(NPCName)
    --Return nil if no data exists.
    if not Dialogs[NPCName] then
        return
    end

    --Iterate over the NPC's dialogs until one is valid.
    for _,DialogData in pairs(Dialogs[NPCName]) do
        --Determine if the conditions are valid.
        local DailogQuestConditionsMet = true
        for _,Quest in pairs(DialogData.RequiredQuests or {}) do
            if not self:QuestConditonValid(Quest.Name,Quest.Status) then
                DailogQuestConditionsMet = false
                break
            end
        end

        --Return if the dialog can be used.
        if DailogQuestConditionsMet then
            return DialogData
        end
    end
end

--[[
Returns the status icon for the given quest.
--]]
function Quests:GetStatusIcon(QuestName)
    if self:QuestConditonValid(QuestName,"TurnedIn") then
        return "rbxassetid://132881903"
    elseif self:QuestConditonValid(QuestName,"AllItems") then
        return "rbxassetid://132881888"
    elseif self:QuestConditonValid(QuestName,"Inventory") then
        return "rbxassetid://132881863"
    else
        return "rbxassetid://133436920"
    end
end

--[[
Returns the status text of the quest.
--]]
function Quests:GetStatusText(QuestName)
    --Return the basic results.
    local QuestData = QuestsData[QuestName]
    if self:QuestConditonValid(QuestName,"NotUnlocked") then
        return "Not Unlocked"
    elseif self:QuestConditonValid(QuestName,"TurnedIn") then
        return "Quest Complete!"
    elseif self:QuestConditonValid(QuestName,"AllItems") and QuestData.QuestType ~= "NPC" then
        return "Ready to turn in!"
    end

    --Return a full result.
    if QuestData.QuestType == "Items" then
        local RequiredItems = QuestData.RequiredItems[1]
        return tostring(#self.Inventory:GetItemSlots(RequiredItems.Name)).."/"..tostring(RequiredItems.Amount).." "..QuestData.RequiredItems.DisplayName
    elseif QuestData.QuestType == "Dressup" then
        return "Equip the "..QuestData.RequiredSet.DisplayName
    elseif QuestData.QuestType == "Monsters" then
        local RequiredMonsters = QuestData.RequiredMonsters[1]
        return tostring(self.QuestData.MonsterKillCounters[RequiredMonsters.Name] or 0).."/"..tostring(RequiredMonsters.Amount).." "..QuestData.RequiredMonsters.DisplayName
    elseif QuestData.QuestType == "NPC" then
        return "Find "..QuestData.RequiredNPC.Name
    end
end

--[[
Saves the quest values.
--]]
function Quests:Save()
    self:FixQuestData()
    self.PlayerData:SetValue("Quests",self.QuestData)
end

--[[
Adds a quest.
--]]
function Quests:AddQuest(QuestName)
    --Return if the quest hasn't been unlocked.
    if not self:QuestConditonValid(QuestName,"NotUnlocked") then
        return
    end

    --Add the quuest and add the monster counter if needed.
    local QuestData = QuestsData[QuestName]
    self.QuestData.ActiveQuests[QuestName] = true
    if QuestData and QuestData.RequiredMonsters and QuestData.RequiredMonsters[1] then
        self.QuestData.MonsterKillCounters[QuestData.RequiredMonsters[1].Name] = 0
    end

    --Save the quests.
    self.QuestData.SelectedQuest = QuestName
    self:Save()
end

--[[
Completes a quest.
--]]
function Quests:CompleteQuest(QuestName)
    --Return if the quest hasn't been completed.
    if not self:QuestConditonValid(QuestName,"AllItems") then
        return
    end

    --Move the quest and remove the kill counter.
    local QuestData = QuestsData[QuestName]
    self.QuestData.ActiveQuests[QuestName] = nil
    self.QuestData.CompletedQuests[QuestName] = true
    if QuestData and QuestData.RequiredMonsters and QuestData.RequiredMonsters[1] then
        self.QuestData.MonsterKillCounters[QuestData.RequiredMonsters[1].Name] = nil
    end

    --Save the quests.
    self:Save()
end

--[[
Selects the primary quest to display.
--]]
function Quests:SelectQuest(QuestName)
    --Return if the quest isn't an option.
    if not self:QuestConditonValid(QuestName,{"Inventory","AllItems"}) then
        return
    end

    --Select the quest and save the quests.
    self.QuestData.SelectedQuest = QuestName
    self:Save()
end

--[[
Registers a monster kill.
--]]
function Quests:RegisterMonsterKill(MonsterName)
    --Return if the monster isn't being tracked.
    if not self.QuestData.MonsterKillCounters[MonsterName] then
        return
    end

    --Increment the monster counter and save the quests.
    self.QuestData.MonsterKillCounters[MonsterName] = self.QuestData.MonsterKillCounters[MonsterName] + 1
    self:Save()
end



return Quests