---Utilities to help deal with inventories. -- Helps you display, manage, and manipulate inventories --@server sv_invtracker.lua --@alias track --[[ some accessability functions ]] local inv = nrequire("inventory/inventory.lua") local itm = nrequire("item.lua") local log = nrequire("log.lua") local track = {} for k,v in pairs({ "art_ObserveInventory", "art_UpdateInventory", "art_CloseInventory", "art_load_player_data", "art_RequestInvMove", "art_RequestInvDrop", }) do util.AddNetworkString(v) end --[[ net.Receive("art_ObserveInventory",function() local id = net.ReadUInt(32) local inv_type = net.ReadString() local inital_data = net.ReadData(net.ReadUInt(32)) known_inventories[id] = inv.CreateInventoryFromData(inv_type,initaldata) end) ]] --[[ Moves an item from one position to another, format: froment ::entity toent ::entity frominvid ::int32 toinvid ::int32 frompos ::table topos ::table ]] net.Receive("art_RequestInvMove",function(len,ply) --Read the data from the net message local froment,toent = net.ReadEntity(),net.ReadEntity() local frominvid,toinvid = net.ReadUInt(32),net.ReadUInt(32) local frompos,topos = net.ReadTable(),net.ReadTable() log.debug(string.format("ply %q requested inventory move from (%q,%d) to (%q,%d)",tostring(ply),tostring(froment),frominvid,tostring(toent),toinvid)) --Make sure the player is not stealing! assert(not (froment:IsPlayer() and toent:IsPlayer() and froment ~= toent), "Tried to move item between players!") --Make sure the entity from has that inventory assert(froment.data ~= nil and froment.data.inventories ~= nil and froment.data.inventories[frominvid] ~= nil, "From entity did not have that inventory!") --Make sure the entity to has that inventory assert(toent.data ~= nil and toent.data.inventories ~= nil and toent.data.inventories[toinvid] ~= nil, "To entity did not have that inventory!") local frominv = froment.data.inventories[frominvid] local toinv = toent.data.inventories[toinvid] local item = frominv:Get(frompos) --Make sure the frominv has an item at that pos assert(item ~= nil, "Could not find an item at that position!") --Make sure it can fit in toinv assert(toinv:CanFitIn(topos,item), "Could not fit the item in that position!") --If we've gotten here without error, we're all good! Move the item! frominv:Remove(frompos) toinv:Put(topos,item) end) ---Drops an item at the position. -- Creates a droped item at the given position, gives the player that walks over it the item. --@tparam itemtbl item The item to drop --@tparam vector3 pos The position to drop the item function track.DropItem(item,pos) local e = ents.Create("art_droppeditem") e.Item = {Name = item.Name, Data = item:Serialize()} e:SetPos(pos) e:Spawn() end net.Receive("art_RequestInvDrop",function(len,ply) local froment = net.ReadEntity() local frominvid = net.ReadUInt(32) local frompos = net.ReadTable() log.debug("Requesting invdrop from " .. tostring(froment)) assert(not froment:IsPlayer() or froment == ply, "Player tried to drop an item that was from another players inventory") local frominv = froment.data.inventories[frominvid] local item = frominv:Get(frompos) frominv:Remove(frompos) local placetodrop --Find somewhere to drop this local tr = ply:GetEyeTrace() if tr.Hit then if tr.HitPos:Distance(ply:GetPos()) < 200 then placetodrop = tr.HitPos else local tr2 = util.TraceLine({ start = (tr.Normal * 200) + tr.StartPos, endpos = ((tr.Normal * 200) + tr.StartPos) + Vector(-2000), }) placetodrop = tr2.HitPos end else placetodrop = ply:GetForward() * 200 end track.DropItem(item,placetodrop) end) ---To be depriciated. -- Also broken function track.ClearInventories(ply) ply.data = {} end ---Updates clients. -- Updates the clients, to let them know an inventory was manipulated whenever the server-side representation was manipulated --@tparam player ply The player we want to provide the updates to --@tparam number invid The inventory number on the player we want to provide the updates to function track.MakeInventoryObserver(ply,invid) local observer = {} observer.Put = function(self,pos,item) local name = item.Name local data = item:Serialize() net.Start("art_UpdateInventory") net.WriteUInt(invid,32) net.WriteBool(true) net.WriteTable(pos) net.WriteString(name) net.WriteUInt(#data,32) net.WriteData(data,#data) net.Send(ply) end observer.Remove = function(self,pos) net.Start("art_UpdateInventory") net.WriteUInt(invid,32) net.WriteBool(false) net.WriteTable(pos) net.Send(ply) end return observer end ---Tell a player to display an inventory. -- Tells a player they should display an inventory, and to expect changes to the inventory. --@tparam player ply The player we want to see the inventory --@tparam invtbl tinv The inventory we want the player to see function track.NotifyPlayerOfInventory(ply,tinv) local initaldat = tinv:Serialize() net.Start("art_ObserveInventory") net.WriteUInt(tinv.id,32) net.WriteString(tinv.Name) net.WriteUInt(#initaldat,32) net.WriteData(initaldat,#initaldat) net.WriteEntity(tinv.Owner) net.WriteTable({}) net.Send(ply) end ---Gives an inventory to a player. -- Gives a new inventory type to a player --@see inventory_higharchy --@tparam entity ply The player to give an inventory to --@tparam string name The name of the inventory to give --@tparam table|nil higharchy The higharchy where to put the table function track.GiveInventoryTo(ply,name,higharchy) local hi = higharchy or {} local i = inv.CreateInventory(name) i.Owner = ply local nid = #ply.data.inventories + 1 i.id = nid local dat = i:Serialize() local observer = track.MakeInventoryObserver(ply,nid) i:AddObserver(observer) ply.data.inventories[nid] = i net.Start("art_ObserveInventory") net.WriteUInt(nid,32) net.WriteString(name) net.WriteUInt(#dat,32) net.WriteData(dat,#dat) net.WriteEntity(i.Owner) net.WriteTable(hi) net.Send(ply) end ---Gives an inventory to a palyer & deserializes data. -- Gives an inventory to a player, and then deserializes it with some data. --@see inventory_higharchy --@tparam entity ply The player to give the inventory to --@tparam string name The name of the inventory to give --@tparam string data The data to deserialize the inventory with --@tparam table|nil higharchy The spot in the higharchy to place the inventory. function track.GiveInventoryWithData(ply,name,data,higharchy) log.debug(string.format("Giving %s a %q inventory with data: %s",ply:Nick(),name,data)) local hi = higharchy or {} local i = inv.CreateInventoryFromData(name,data,ply) local nid = #ply.data.inventories + 1 local observer = track.MakeInventoryObserver(ply,nid) i.id = nid i:AddObserver(observer) ply.data.inventories[nid] = i net.Start("art_ObserveInventory") net.WriteUInt(nid,32) net.WriteString(name) net.WriteUInt(#data,32) net.WriteData(data,#data) net.WriteEntity(ply) net.WriteTable(hi) net.Send(ply) end local plymeta = FindMetaTable("Player") ---A shortcut to check if a player has an item. -- Checks if a player has an item. This method will only return the position of the FIRST item found that matches. The player MAY have other items which match. --@metamethod player:HasItem(ptr) --@tparam (string|function) ptr If ptr is a string, looks for an item with the same name, ptr may also be a function that takes 1 argument (an item) and returns true if the item is what you're looking for. --@treturn table|false Returns the position if the player has the item, false if they don't function plymeta:HasItem(ptr) for k,v in pairs(self.data.inventories) do local p = v:Has(ptr) if type(p) == "table" then return {k,p} end end return false end --- Gets a refrence to an item without removeing it -- Gets an item, if you want to look at the fields without changing them. -- If you do need to change fields, remove the item, change the field, then -- re-give the item. --@metamethod player:GetItem(loc) --@tparam table loc The position of the item (returned by plymeta:HasItem()) --@treturn itemtbl The item at the location function plymeta:GetItem(loc) local nid = loc[1] local pos = loc[2] local item = self.data.inventories[nid]:Get(pos) return item end ---A shortcut to remove an item from a player. -- Removes an item from a player and returns the item --@metamethod player:RemoveItem(tbl) --@tparam table tbl The position the item is in (returned by plymeta:HasItem()) --@treturn itemtbl The item that was removed function plymeta:RemoveItem(tbl) local nid = tbl[1] local pos = tbl[2] local item = self.data.inventories[nid]:Remove(pos) return item end ---Places an item in a player's inventory -- Puts an item in a specific position in a specific inventory for a player --@metamethod player:PutItem(location,item) --@tparam table loc The location to put the item (returned by plymeta:HasItem()) function plymeta:PutItem(loc,item) local nid = loc[1] local pos = loc[2] self.data.inventories[nid]:Put(pos,item) end ---A shortcut for giving an item to a player. -- Gives an item to a player --@metamethod player:GiveItem(tbl) --@param itemtbl tbl The item to give the player --@raises "Uanble to find place to put item" Raises an exception if we cannot find a location to put the item. function plymeta:GiveItem(tbl) assert(type(tbl) == "table", "Attempted to give a player an item that was not a table:" .. tostring(tbl)) for k,v in pairs(self.data.inventories) do local p = v:FindPlaceFor(tbl) if type(p) == "table" then log.debug("Found inventory that would take item:" .. v.Name) v:Put(p,tbl) return else log.debug("inventory " .. k .. " couldn't fit it...") end end error("Unable to find place to put item") end ---Gets a player's credits. -- To be depriciated function plymeta:GetCredits() return self.data.credits end ---Sets a player's credits. -- To be depriciated function plymeta:SetCredits(num) self.data.credits = num net.Start("art_load_player_data") net.WriteTable({ credits = num }) net.Send(self) end ---Sends a player's data. -- To be depriciated function track.SendPlayerData(ply) net.Start("art_load_player_data") net.WriteTable({ credits = ply.data.credits }) net.Send(ply) end -- concommand.Add("artery_SendMeData",function(ply,cmd,args) -- track.ClearInventories(ply) -- track.GiveInventoryTo(ply,"Equipment") -- track.SendPlayerData(ply) -- end) ---Prints a player's inventory to the console. --@concommand artery_ShowMyInventories concommand.Add("artery_ShowMyInventories", function(ply,cmd,args) PrintTable(ply.data.inventories) end) ---Gives an inventory to a player. -- Gives a new inventory to a player --@usage artery_AddInventory --@concommand artery_AddInventory --@reqadmin --@tparam string invname The name of the inventory --@see @{inventory.artery_printinventories} concommand.Add("artery_AddInventory",function(ply,cmd,args) if not ply:IsAdmin() then return end track.GiveInventoryTo(ply,args[1]) end) ---Gives an item to the player. -- Gives a new item to the player --@usage artery_GiveItem --@concommand artery_GiveItem --@reqadmin concommand.Add("artery_GiveItem",function(ply,cmd,args) if not ply:IsAdmin() then return end xpcall(function() ply:GiveItem(itm.GetItemByName(args[1])) end,function(err) log.debug("Could not give that item!:" .. err) log.debug(debug.traceback()) end) end) return track