--[[ Some functions that are needed multiple places in the code, to deal with player inventories. ]] local invfuncs = {} --Forcibly put an item in a backpack, you should check to make sure there's room first invfuncs.PutItemInBackpack = function(backpack,x,y,item) backpack[1][x][y] = item for k = 1,#item.Shape do for i = 1,#(item.Shape[k]) do if k == 1 and i == 1 then continue end backpack[1][x + k - 1][y + i - 1] = item.Shape[k][i] end end end --Writes a backpack to the net stream invfuncs.SerializeBackpack = function(backpack) net.WriteString(backpack[3]) --Name net.WriteUInt(backpack[2][1],16) --width net.WriteUInt(backpack[2][2],16) --height for k,v in pairs(backpack[1]) do for i,j in pairs(v) do if type(j) ~= "table" then continue end net.WriteString(j.Name) net.WriteUInt(k,16) net.WriteUInt(i,16) local data = j:Serialize() net.WriteUInt(#data,32) net.WriteData(data,#data) end end net.WriteString("END_ITEMS") end --Loads a backpack from the net stream invfuncs.DeSerializeBackpack = function() local backpackname = net.ReadString() local width = net.ReadUInt(16) local height = net.ReadUInt(16) --Build the backpack local tb = {} tb[1] = {} tb[2] = {width,height} tb[3] = backpackname for x = 1,width do tb[1][x] = {} for y = 1,height do tb[1][x][y] = false end end local nitemname = net.ReadString() while nitemname ~= "END_ITEMS" do local xpos = net.ReadUInt(16) local ypos = net.ReadUInt(16) local data = net.ReadData(net.ReadUInt(32)) local nitem = ART.GetItemByName(nitemname):DeSerialize(data) invfuncs.PutItemInBackpack(tb,xpos,ypos,nitem) nitemname = net.ReadString() end return tb end --Checks to see if an item can fit in the backpack at a certain position invfuncs.CanFitInBackpack = function(backpack,x,y,item) for k,v in pairs(item.Shape) do for i,j in pairs(v) do if not j then continue end if backpack[1][x + k - 1] == nil then return false end if backpack[1][x + k - 1][y + i - 1] or backpack[1][x + k - 1][y + i - 1] == nil then return false end end end return true end --Creates a new backpack, this is NOT called when players join the server, even new ones. invfuncs.CreateBackpack = function(name,width,height) local backpack = {} backpack[1] = {} backpack[2] = {width,height} backpack[3] = name for i = 1,width do --the width of the backpack. backpack[1][i] = {} end for k,v in pairs(backpack[1]) do for i = 1,height do --the height of the backpack. backpack[1][k][i] = false end end return backpack end --Displays a dropdown of options under the players mouse, if the option is clicked, does the function --Requires a table of strings to functions, or strings to tables of strings to functions. --Be careful not to make this a recursive table. local function createMenuFor(menu, tbl) for k,v in pairs(tbl) do if(isfunction(v)) then --This is a dead-end, add the menu local thisoption = menu:AddOption(k,v) else --Otherwise it should be a table, recursively call to create local submenu = menu:AddSubMenu(k) createMenuFor(submenu,v) end end end --Draws a backpack on the dpanel, client side only invfuncs.DrawBackpackOnDPanel = function(dp, backpack, backpacknum, tent) local width = ScrW() local height = ScrH() local slotsize = math.Round(width / 32) local backgrid = vgui.Create( "DGrid", dp ) backgrid:SetPos( 10, 30 ) backgrid:SetCols( backpack[2][1] ) backgrid:SetColWide( backpack[2][2] ) backgrid:Dock(FILL) for i = 1,#(backpack[1]) do for j = 1,#(backpack[1][i]) do local item = backpack[1][j][i] if type(backpack[1][j][i]) == "table" then local itemwidth = 0 for _,l in pairs(item.Shape) do itemwidth = math.Max(itemwidth,#l) end local itemheight = #item.Shape local invicon = vgui.Create( "DButton", dp ) invicon:SetSize(slotsize * itemwidth, slotsize * itemheight) invicon:SetPos(slotsize * (i - 1), slotsize * (j - 1)) invicon:SetText(item.Name) if item.Tooltip then invicon:SetTooltip(item.Tooltip) end --invicon.Paint = function(self, w, h) draw.RoundedBox(4, 0,0,w,h,Color(0,100,0)) end invicon.DoClick = function() if not item.GetOptions then return end local menu = vgui.Create("DMenu") createMenuFor(menu,item:GetOptions()) menu:Open() end invicon.Item = item invicon.invpos = {j,i} invicon.ent = tent invicon.backpacknum = backpacknum invicon:Droppable("Inventory") elseif not backpack[1][j][i] then local emptyslot = vgui.Create("DPanel", dp) emptyslot:SetSize(slotsize,slotsize) emptyslot:SetPos(slotsize * (i - 1), slotsize * (j - 1)) --emptyslot.Paint = function(self, w, h) draw.RoundedBox(4, 0,0,w,h,Color(0,0,100)) end emptyslot:Receiver( "Inventory", function( receiver, tableOfDroppedPanels, isDropped, menuIndex, mouseX, mouseY ) if not isDropped then return end local icon = tableOfDroppedPanels[1] local item = icon.Item local curpos = icon.invpos --Set the shape it was at to false if not icon.wasequiped and icon.ent == tent then assert(curpos ~= nil, "print curpos was nil when not equiped") for k = 1,#item.Shape do for l = 1,#(item.Shape[k]) do if k == 1 and l == 1 then continue end backpack[1][curpos[1] + k - 1][curpos[2] + l - 1] = false end end backpack[1][curpos[1]][curpos[2]] = false end if invfuncs.CanFitInBackpack(backpack,j,i,item) then local fromtbl = icon.invpos local wasequiped = icon.wasequiped if wasequiped then net.Start("unequipitem") net.WriteString(wasequiped) net.WriteUInt(backpacknum,16) net.WriteUInt(i,16) net.WriteUInt(j,16) net.SendToServer() else net.Start("moveitem") net.WriteEntity(icon.ent) -- from ent net.WriteEntity(tent) -- to ent net.WriteUInt(icon.backpacknum,16) -- from backpack number net.WriteUInt(backpacknum,16) -- to backpack number net.WriteUInt(fromtbl[1],16) -- From position net.WriteUInt(fromtbl[2],16) net.WriteUInt(j,16) -- To position net.WriteUInt(i,16) net.SendToServer() if item.onEquip ~= nil then item:onEquip(LocalPlayer()) end end end end, {} ) end end end end return invfuncs