--[[ Various functions to work with inventories ]] --- Various functions to deal with inventories. -- @module Player local pmeta = FindMetaTable("Player") local emeta = FindMetaTable("Entity") local invfuncs = include("inventory_common.lua") --A 2d array of the inventory. pmeta.Inventory = pmeta.Inventory or {} --Money pmeta.Inventory.Credits = pmeta.Inventory.Credits or 0 --each backpack has 1:a tbl containing items or false, 2: a tbl {width,height} and 3:name pmeta.Inventory.Backpacks = pmeta.Inventory.Backpacks or {} --Eqiped stuff at base, player has 1=Head, 2=Body, 3=Legs, 4=Boots, 5=Gloves, 6=Left Hand, 7=Right Hand pmeta.Inventory.Equiped = pmeta.Inventory.Equiped or {} local equipedslots = { "Head","Body","Legs","Boots","Gloves","Left","Right" } for _,v in pairs(equipedslots) do pmeta.Inventory.Equiped[v] = pmeta.Inventory.Equiped[v] or false end if SERVER then --- Gets a player's credits. -- Gets the number of credits a player has -- @return The number of credits for this player function pmeta:GetCredits() return self.Inventory.Credits end util.AddNetworkString( "art_synch_credits" ) local function SynchCredits(ply) net.Start("art_synch_credits") net.WriteUInt(ply.Inventory.Credits,32) net.Send(ply) end --- Sets a player's credits. -- Sets the number of credits this player has. Credits are synchronized after every set -- @param num The number of credits to set on the player function pmeta:SetCredits(num) self.Inventory.Credits = num SynchCredits(self) end end if CLIENT then net.Receive("art_synch_credits",function() ART.Credits = net.ReadUInt(32) print("I updated my credits! now:", ART.Credits) end) end --- Puts an item in the inventory. -- Puts an item in an inventory, overwriteing all other items it might be overlapping, you should check to make sure you're not over writeing something first. -- @param backpack the backpack number this item should be placed in -- @param x the column in the backpack this item should be placed in -- @param y the row in the backpack this item should be placed in -- @param item the item to place in the backpack. -- @see Player.FindSpotForItem function pmeta:PutInvItem(backpack,x,y,item) invfuncs.PutItemInBackpack(self.Inventory.Backpacks[backpack],x,y,item) end --- Finds a spot for an item. -- Finds a backpack, row, and column for an item in a player's inventory. -- @param item The item to try and fit into the backpack. -- @return row The row where a spot was found -- @return col The column where a spot was found -- @return n The backpack number where a spot was found function pmeta:FindSpotForItem(item) for n,v in pairs(self.Inventory.Backpacks) do for row = 1,v[2][2] do for col = 1,v[2][1] do if self:CanFitInBackpack(v,row,col,item) then return row,col,n end end end end end local function DefaultCompare(item, itemname) return item.Name == itemname end --- Checks if the player has an item. -- Check to see if the player has an item. Supply the name of the item, or supply a function that takes 2 items, and returns true if they are equial. If any of the returns are nil, then all the the returns will be nil, and the item could not be found. -- @param nameorcomparitor The item name as a string, or a function that returns true when given an item that you want. -- @return row The row the items was found in -- @return col The column the item was found in -- @return n The backpack number the item was found in. function pmeta:HasItem(nameorcomparitor) local comparitor if type(param) == "String" then comparitor = function(t) return t.Name == nameorcomparitor end else comparitor = nameorcomparitor end for n,v in pairs(self.Inventory.Backpacks) do for row = 1,v[2][2] do for col = 1,v[2][1] do local itemin = v[1][row][col] if itemin ~= false and comparitor(itemin) then return row,col,n end end end end end --- Removes an item. -- Remoes an item in the backpack of the player -- @param backpack The backpack to remove the item from -- @param row The row in the backpack the item is located at -- @param col The column in the backpack the item is located at function pmeta:RemoveItemAt(backpack,row,col) local item = self.Inventory.Backpacks[backpack][1][row][col] for k = 1,#item.Shape do for i = 1,#(item.Shape[k]) do self.Inventory.Backpacks[backpack][1][row + k - 1][col + i - 1] = false end end self:SynchronizeInventory() end --- Gives an item to a player. -- Gives an item to the player in the next avaliable slot the item can fit in -- @param item The item to give the player function pmeta:GiveItem(item) local x,y,b = self:FindSpotForItem(item) self:PutInvItem(b,x,y,item) self:SynchronizeInventory() end --- Check if an item can fit in a position in a backpack -- Check if an item can fit in a specific position in a specific backpack -- @param backpack The backpack to try to fit the item in -- @param x The row to try to fit the item in -- @param y The column to try to fit the item in -- @return bool If the item can fit in the backpack function pmeta:CanFitInBackpack(backpack,x,y,item) return invfuncs.CanFitInBackpack(backpack,x,y,item) end if SERVER then util.AddNetworkString("synchinventory") util.AddNetworkString("moveitem") util.AddNetworkString("equipitem") util.AddNetworkString("unequipitem") end --- Unequips an item. -- Unequips an item, and puts it in the specified backpack (makes sure that it's possible first) -- @param equipslot The equipment slot the item is in right now -- @param backpack The destination backpack to put the item into -- @param row The row in the backpack to put the item -- @param col The column in the backpack to put the item function pmeta:UnEquip(equipslot, backpack, row, col) local item = self.Inventory.Equiped[equipslot] if self:CanFitInBackpack( self.Inventory.Backpacks[backpack], row, col, item ) then self.Inventory.Equiped[equipslot] = false self:PutInvItem(tobackpack,row,col,item) if item.onUnEquip ~= nil then item:onUnEquip(self) end self:SynchronizeInventory() else error("Could not fit item in backpack, client might be de-synchronized") end end net.Receive("unequipitem",function(len,ply) local itemslot = net.ReadString() local tobackpack = net.ReadUInt(16) local row = net.ReadUInt(16) local col = net.ReadUInt(16) ply:UnEquip(itemslot,tobackpack,row,col) end) --- Equips an item -- Moves an item from a backpack to an equiped slot, makes sure the item can be equiped in that slot first -- @param frombackpack The backpack to take the item from -- @param fromrow The row to take the item from -- @param fromcol The column to take the item from -- @param toslo The equipment slot to put the item into function pmeta:EquipItem(frombackpack, fromrow, fromcol, toslot) local item = self.Inventory.Backpacks[frombackpack][1][fromrow[1]][fromcol[2]] if item.Equipable ~= nil and item.Equipable == toslot then --Remove from the backpack for k = 1,#item.Shape do for i = 1,#item.Shape[k] do if k == 1 and i == 1 then continue end self.Inventory.Backpacks[frombackpack][1][fromrow + k - 1][fromcol + i - 1] = false end end self.Inventory.Backpacks[frombackpack][1][fromrow][fromcol] = false self.Inventory.Equiped[toslot] = item if item.onEquip ~= nil then item:onEquip(self) end self:SynchronizeInventory() end end net.Receive("equipitem",function(len,ply) local backpacknum = net.ReadUInt(16) local fromrow = net.ReadUInt(16) local fromcol = net.ReadUInt(16) local equippos = net.ReadString() ply:EquipItem(backpacknum,fromrow,fromcol,equippos) end) net.Receive("moveitem",function(len,ply) local froment = net.ReadEntity() local toent = net.ReadEntity() local frombackpack = net.ReadUInt(16) local tobackpack = net.ReadUInt(16) local frompos = {net.ReadUInt(16),net.ReadUInt(16)} local topos = {net.ReadUInt(16),net.ReadUInt(16)} if froment:IsPlayer() and toent:IsPlayer() and (froment ~= toent) then--Just don't allow stealing between players, anything else is fine ply:PrintMessage( HUD_PRINTCENTER, "You can't steal from this person!" ) return end local item = froment.Inventory.Backpacks[frombackpack][1][frompos[1]][frompos[2]] --Set the shape it was at to false for k = 1,#item.Shape do for i = 1,#(item.Shape[k]) do if k == 1 and i == 1 then continue end froment.Inventory.Backpacks[frombackpack][1][frompos[1] + k - 1][frompos[2] + i - 1] = false end end froment.Inventory.Backpacks[frombackpack][1][frompos[1]][frompos[2]] = false --now check if it can fit in the backpack if invfuncs.CanFitInBackpack(toent.Inventory.Backpacks[tobackpack],topos[1],topos[2],item) then invfuncs.PutItemInBackpack(toent.Inventory.Backpacks[tobackpack],topos[1],topos[2],item) else invfuncs.PutItemInBackpack(froment.Inventory.Backpacks[frombackpack],frompos[1],frompos[2],item) end froment:SynchronizeInventory() toent:SynchronizeInventory() end) --- Loads a player's inventory -- @param json The JSON string to create the player's inventory from function pmeta:LoadInventory(json) local reinv = util.JSONToTable(json) for k,v in pairs(reinv) do self.Inventory[k] = v end self:SynchronizeInventory() end --- Networks a player's inventory -- Makes sure the client's version of the inventory matches up to the server's version, this should be called after manipulateing the client's inventory in any way. function pmeta:SynchronizeInventory() net.Start("synchinventory") net.WriteEntity(self) net.WriteFloat(#self.Inventory.Backpacks) for k,v in pairs(self.Inventory.Backpacks) do invfuncs.SerializeBackpack(v) end for k,v in pairs(equipedslots) do if self.Inventory.Equiped and self.Inventory.Equiped[v] ~= false then net.WriteString(v) local data = self.Inventory.Equiped[v]:Serialize() net.WriteString(self.Inventory.Equiped[v].Name) net.WriteUInt(#data,32) net.WriteData(data,#data) end end net.WriteString("END_EQUIPED") net.Send(self) end if CLIENT then net.Receive("synchinventory",function(len,ply) if LocalPlayer().invdisplays == nil then LocalPlayer().invdisplays = {} end local what = net.ReadEntity() what.Inventory.Backpacks = {} local numbackpacks = net.ReadFloat() for k = 1,numbackpacks do local tbackpack = invfuncs.DeSerializeBackpack() table.insert(what.Inventory.Backpacks,tbackpack) end local neq = net.ReadString() local updated_slots = {} while neq ~= "END_EQUIPED" do local itemslot = neq local itemname = net.ReadString() local itemdata = net.ReadData(net.ReadUInt(32)) local item = ART.GetItemByName(itemname):DeSerialize(itemdata) what.Inventory.Equiped[itemslot] = item updated_slots[itemslot] = true neq = net.ReadString() end if what.Inventory.Equiped ~= nil then for k,v in pairs(equipedslots) do if updated_slots[v] then continue end what.Inventory.Equiped[v] = false end end ART.RefreshDisplays() end) end concommand.Add("artery_showinventory",function(ply,cmd,args) PrintTable(ply.Inventory) PrintTable(ply.ClientInventory) end) hook.Add( "PlayerSpawn", "artery_disable_sprint", function(ply) ply:SetRunSpeed(ply:GetWalkSpeed()) end )