1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
---The main file needed to register create new inventory types.
-- Helps you register inventories that work with the rest of artery
--@shared inventory.lua
--@alias inv
--[[
Public functions:
RegisterInventory(tbl_inventory) ::nil
Registers a new inventory prototype, see below
CreateInventory(string_name) ::table_inventory
Creates a new inventory be sure to set the .owner and .id fields!
CreateInventoryFromData(string_name,string_data,entity_owner)::table_inventory)
Just deserializes an inventory. You still need to set .owner and .id!
DeriveInventory(string_name) ::table_inventory
Creates a new inventory from an old, allows for heiarchy.
Inventories have the following structure
field returns
inv.Name ::string
The name!
inv:FindPlaceFor(item) ::table_position or nil
Finds a place for the item
inv:CanFitIn(table_position,item) ::boolean
Check if the item can fit in the position
inv:Put(table_position,item) ::nil
Put an item in at the position
inv:Has(string_or_compare_func) ::table_position or nil
find an item in the inventory
inv:Remove(position) ::table_item
Remove an item from the position
inv:Get(position) ::table_item
Get the item at a position
inv:Serialize() ::string
Serialize the item to store it in a db
inv:DeSerialize(str) ::table_inventory
recreate the item from data in serialize
The above fields must be defined for new inventories.
-----------------------------------------------------
The below are automatically made if they do not exist.
inv:AddObserver(tbl_other) ::number_id Whenever put or remove is called on this inventory, tbl_other's put() and remove() is also called, for easy networking to whoever needs it
inv:RemoveObserver(number_id) ::nil Removes an observer from the inventory
------------------------------------------------------
These fields should be defined when an inventory is created, before it can be used
inv.Owner ::entity
inv.id ::number
The table used for "position" is not defined, and may vary from inventory to inventory, but a single inventory type should only use a single type of position.
The AddObserver will call tbl_other's Put() and Remove() methods(same arguments as the inventory) when this inventories Put() and Remove() methods are called. This will hopefully make it easy to have multiple players viewing the same inventory, and all get updates.
Serialize() should take this inventories contents and return a string that it can recreate this inventory from. DeSerialize should create a self.Copy() with the appropriate fields set. Take advantage of the fact that items must also have Serialize() and DeSerialize() methods.
]]
local inv = {}
local log = nrequire("log.lua")
--Creates a partial copy of a table(tables are copied, functions are not)
local function TableCopy(tbl)
local ntbl = {}
for k,v in pairs(tbl) do
if type(v) == "table" then
ntbl[k] = TableCopy(v)
else
ntbl[k] = v
end
end
return ntbl
end
--Holds different types of inventories to be made
local inventories = {} --Master list
local function DefaultAddObserver(self,tbl)
assert(tbl ~= nil,"Cannot add a nil observer!")
for k,v in pairs({"Put","Remove"}) do
assert(tbl[v] ~= nil,"Cannot add an observer without a " .. v .. "! observer was:\n\t" .. table.concat(table.GetKeys(tbl),"\n\t"))
end
if self.observers == nil then self.observers = {} end
self.observers[#self.observers + 1] = tbl
return #self.observers
end
local function DefaultRemoveObserver(self,observer_id)
for i = observer_id, #self.observers do
self.observers[i] = self.observers[i + 1]
end
end
local function SetDefaultObservers(tbl)
log.debug("Setting default observers on ", tbl.Name)
tbl.AddObserver = DefaultAddObserver
tbl.RemoveObserver = DefaultRemoveObserver
--Call the observer's puts as well as this inventory's put.
local oldput,oldremove = tbl.Put,tbl.Remove
tbl.Put = function(self,position,item)
for k,v in pairs(self.observers) do
v:Put(position,item)
end
return oldput(self,position,item)
end
tbl.Remove = function(self,position)
for k,v in pairs(self.observers) do
v:Remove(position)
end
return oldremove(self,position)
end
end
local required_fields = {
{"Name","string"},
{"FindPlaceFor","function"},
{"CanFitIn","function"},
{"Put","function"},
{"Has","function"},
{"Remove","function"},
{"Get","function"},
{"Serialize","function"},
{"DeSerialize","function"},
}
---Registers a new inventory.
-- Register a new inventory prototype from the table passed in
--@see invtbl
--@tparam invtbl tbl The table inventory
function inv.RegisterInventory(tbl)
assert(type(tbl) == "table",
"Attempted to register inventory that was not a table")
for k,v in pairs(required_fields) do
assert(tbl[v[1]] ~= nil,
string.format("Attempted to register inventory without field %q",v[1]))
assert(type(tbl[v[1]]) == v[2],
string.format("Attempted to register inventory with field %q of type %q when it should have been %q",v[1],type(tbl[v[1]]),v[2]))
end
--assert(inventories[tbl.Name] == nil,
-- string.format("Attempted to register 2 inventories with the same name: %q", tbl.Name))
if inventories[tbl.Name] ~= nil then
log.warn(string.format("Registered 2 inventories with the same name %q, overwriteing",tbl.Name))
end
assert((tbl.AddObserver == nil and tbl.RemoveObserver == nil) or
(tbl.AddObserver ~= nil and tbl.RemoveObserver ~= nil),
"AddObserver and RemoveObserver must be defined in pairs")
if tbl.AddObserver == nil then
SetDefaultObservers(tbl)
end
inventories[tbl.Name] = tbl
log.info("Registered inventory: " .. tbl.Name)
--print("Registered inventory: " .. tbl.Name)
end
---Create an inventory.
-- Creates a new inventory that you can give to an item.
-- Consider carefully if you want to use this method, or the CreateInventoryFromData method.
--@see invtbl
--@tparam string name The name of the inventory type when it was registered
function inv.CreateInventory(name,initstr)
--print("Createing inventory", name)
initstr = initstr or ""
assert(inventories[name] ~= nil, string.format("Tried to create a copy of inventory that does not exist:%s\nValid inventories are:\n\t%s",name,table.concat(table.GetKeys(inventories),"\n\t")))
local ret_m = {
__index = inventories[name]
}
local ret = {}
setmetatable(ret,ret_m)
ret:DeSerialize(initstr)
ret.observers = {}
return ret
end
---Creates an inventory from data.
-- This is what you useually want to use when createing inventories
--@tparam string name The name of the inventory type you want to instanciate
--@tparam string data The data the inventory needs to deserialize, most inventories will work if this is empty
--@tparam entity owner The owning entity of this inventory, does not have to be a player (ex when createing crates players can put items into)
function inv.CreateInventoryFromData(name,data,owner)
local tinv = inv.CreateInventory(name)
tinv.Owner = owner
--print("tinv was", tinv)
--PrintTable(tinv)
local ret = tinv:DeSerialize(data)
print("is now",ret)
PrintTable(ret)
assert(ret != nil, "Failed to create inventory " .. name .. ", returned nil")
assert(ret.Owner != nil, "Creating inventory with no owner!")
assert(ret.Owner:IsValid(), "Owner of inventory was not valid!")
assert(ret.Owner != Entity(0), "Owner was worldspawn! something went wrong!")
return ret
end
--- Prints all inventories
-- Prints all inventories known to the game
--@usage artery_printinventories
--@concommand artery_printinventories
concommand.Add("artery_printinventories", function(ply,cmd,args)
PrintTable(inventories)
end)
if SERVER then
concommand.Add("artery_printmyinventories",function(ply,cmd,args)
print("Inventories of:",ply:Nick())
PrintTable(ply.data.inventories)
end)
end
---To Be Depriciated.
function inv.DeriveInventory(name)
error("inv.DeriveInventory called")
while inventories[name] == nil do
coroutine.yield()
end
--Create a shallow copy
local ret = {}
for k,v in pairs(inventories[name]) do
ret[k] = v
end
return ret
end
return inv
|