local I=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local x=require((I: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=x: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 U=x: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 k=x:GetResource("\071\097\109\101\068\097\116\097\046\073\116\101\109\046\073\116\101\109\115")local R=x:GetResource("\083\116\097\116\101\046\080\108\097\121\101\114\068\097\116\097")local p=u:Extend()p.CachedInventory={}setmetatable(p.CachedInventory,{__mode="\107"})p:SetClassName("\073\110\118\101\110\116\111\114\121")function p.__new(x,I)x:InitializeSuper()x.PlayerData=R.GetPlayerData(I)x.InventoryChanged=U.new()x.Inventory=x.PlayerData:GetValue("\073\110\118\101\110\116\111\114\121")x.CachedSlotResults={};(x.PlayerData:GetValueChangedSignal("\073\110\118\101\110\116\111\114\121")):Connect(function()x.Inventory=x.PlayerData:GetValue("\073\110\118\101\110\116\111\114\121")x.CachedSlotResults={}x.InventoryChanged:Fire(x.Inventory)end)end function p.GetInventory(I)if not p.CachedInventory[I]then p.CachedInventory[I]=p.new(I)end return p.CachedInventory[I]end function p.GetItem(x,I)I=tostring(I)return x.Inventory[I]end function p.GetMaxItems(I)return(3+#I:GetItemSlots("\073\110\118\101\110\116\111\114\121\069\120\112\097\110\100\101\114"))*35 end function p.GetNextOpenSlot(I)for x=1,I:GetMaxItems(),1 do if not I:GetItem(x)then return x end end end function p.GetItemSlots(x,I)if not x.CachedSlotResults[I]then local u={}x.CachedSlotResults[I]=u for x,U in pairs(x.Inventory)do if U.Name==I then table.insert(u,x)end end end return x.CachedSlotResults[I]end function p.CanFitInSlot(u,I,x)local U=tonumber(x)if U then if U<1 or U>u:GetMaxItems()or U%1~=0 then return false end return true end local R=I.Name if R then local I=k[R]if I then return I.ItemType==x end end return false end function p.CanSwap(u,I,x)local U,k=u:GetItem(I),u:GetItem(x)if U and not u:CanFitInSlot(U,x)then return false end if k and not u:CanFitInSlot(k,I)then return false end return true end function p.Save(I)I.PlayerData:SetValue("\073\110\118\101\110\116\111\114\121",I.Inventory)end function p.AddItem(x,I)local u=x:GetNextOpenSlot()if not u then error("\073\110\118\101\110\116\111\114\121 \100\111\101\115\110\'\116 \104\097\118\101 \097\110\121 \111\112\101\110 \115\108\111\116\115\046")end x.Inventory[tostring(u)]=I x:Save()end function p.SwapSlots(u,I,x)I,x=tostring(I),tostring(x)u.Inventory[I],u.Inventory[x]=u.Inventory[x],u.Inventory[I]u:Save()end function p.RemoveSlots(x,I)for I,u in pairs(I)do x.Inventory[tostring(u)]=nil end x:Save()end function p.RemoveSlot(x,I)x:RemoveSlots({I})end return p
--[[
TheNexusAvenger

Manages the inventory for a player.
--]]

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 PlayerData = ReplicatedStorageProject:GetResource("State.PlayerData")

local Inventory = NexusObject:Extend()
Inventory.CachedInventory = {}
setmetatable(Inventory.CachedInventory,{__mode="k"})
Inventory:SetClassName("Inventory")



--[[
Creates a player data object.
--]]
function Inventory:__new(Player)
    self:InitializeSuper()

    --Store the state.
    self.PlayerData = PlayerData.GetPlayerData(Player)
    self.InventoryChanged = NexusEvent.new()
    self.Inventory = self.PlayerData:GetValue("Inventory")
    self.CachedSlotResults = {}

    --Connect the inventory changing.
    self.PlayerData:GetValueChangedSignal("Inventory"):Connect(function()
        self.Inventory = self.PlayerData:GetValue("Inventory")
        self.CachedSlotResults = {}
        self.InventoryChanged:Fire(self.Inventory)
    end)
end

--[[
Returns an inventory object for a
given player.
--]]
function Inventory.GetInventory(Player)
    --Create the cached object if it doesn't exist.
    if not Inventory.CachedInventory[Player] then
        Inventory.CachedInventory[Player] = Inventory.new(Player)
    end

    --Return the cached object.
    return Inventory.CachedInventory[Player]
end

--[[
Returns the item at a given slot.
--]]
function Inventory:GetItem(Slot)
    Slot = tostring(Slot)
    return self.Inventory[Slot]
end

--[[
Returns the total amount of items
the inventory can store.
--]]
function Inventory:GetMaxItems()
    return (3 + #self:GetItemSlots("InventoryExpander")) * 35
end

--[[
Returns the next open slot.
Returns nil if there is no empty slot.
--]]
function Inventory:GetNextOpenSlot()
    for i = 1,self:GetMaxItems() do
        if not self:GetItem(i) then
            return i
        end
    end
end

--[[
Returns the slots an item occupies.
The total amount of an item can be determined
by how many slots are used.
--]]
function Inventory:GetItemSlots(ItemName)
    --Create the cache entry.
    --Caching is done because of how often the map range
    --and page count is calculated.
    if not self.CachedSlotResults[ItemName] then
        --Determine the slots.
        local Slots = {}
        self.CachedSlotResults[ItemName] = Slots
        for Slot,ItemData in pairs(self.Inventory) do
            if ItemData.Name == ItemName then
                table.insert(Slots,Slot)
            end
        end
    end

    --Return the cached result.
    return self.CachedSlotResults[ItemName]
end

--[[
Returns if an item can exist in a given slot.
--]]
function Inventory:CanFitInSlot(Item,Slot)
    --Return true if the slot is a number and is in bounds.
    local SlotNumber = tonumber(Slot)
    if SlotNumber then
        if SlotNumber < 1 or SlotNumber > self:GetMaxItems() or SlotNumber % 1 ~= 0 then
            return false
        end
        return true
    end

    --Return if the item fits the given slot name.
    local ItemName = Item.Name
    if ItemName then
        local Data = ItemData[ItemName]
        if Data then
           return Data.ItemType == Slot 
        end
    end
    return false
end

--[[
Returns if 2 slots can swap.
--]]
function Inventory:CanSwap(Slot1,Slot2)
    --Get the items for the slots.
    local Item1,Item2 = self:GetItem(Slot1),self:GetItem(Slot2)

    --Return false if the first item can't go to the given slot.
    if Item1 and not self:CanFitInSlot(Item1,Slot2) then
        return false
    end

    --Return false if the second item can't go to the given slot.
    if Item2 and not self:CanFitInSlot(Item2,Slot1) then
        return false
    end

    --Return true (can swap).
    return true
end

--[[
Saves the inventory.
--]]
function Inventory:Save()
    self.PlayerData:SetValue("Inventory",self.Inventory)
end

--[[
Adds an item to the inventory.
--]]
function Inventory:AddItem(Item)
    --Throw an error if there is no empty slot.
    local NextSlot = self:GetNextOpenSlot()
    if not NextSlot then
        error("Inventory doesn't have any open slots.")
    end

    --Add the item and save the inventory.
    self.Inventory[tostring(NextSlot)] = Item
    self:Save()
end

--[[
Swaps two slots in the inventory.
--]]
function Inventory:SwapSlots(Slot1,Slot2)
    --Swap the slots and save the inventory.
    Slot1,Slot2 = tostring(Slot1),tostring(Slot2)
    self.Inventory[Slot1],self.Inventory[Slot2] = self.Inventory[Slot2],self.Inventory[Slot1]
    self:Save()
end

--[[
Removes items at given slots.
--]]
function Inventory:RemoveSlots(Slots)
    --Remove the items and save the inventory.
    for _,Slot in pairs(Slots) do
        self.Inventory[tostring(Slot)] = nil
    end
    self:Save()
end

--[[
Removes an item at a given slot.
--]]
function Inventory:RemoveSlot(Slot)
    self:RemoveSlots({Slot})
end



return Inventory