aboutsummaryrefslogtreecommitdiff
path: root/gamemode/core
diff options
context:
space:
mode:
authorAlexander Pickering <alexandermpickering@gmail.com>2017-01-04 23:27:36 -0500
committerAlexander Pickering <alexandermpickering@gmail.com>2017-01-04 23:27:36 -0500
commit4879eb1d78520ce0ac9b0bb0ef5244cf65ad7c99 (patch)
treeac47724191a8959c19b2408d4da384d64b6098ec /gamemode/core
parent2c4329e2b6e19182a441f79a5c3010011f8ae767 (diff)
downloadartery-4879eb1d78520ce0ac9b0bb0ef5244cf65ad7c99.tar.gz
artery-4879eb1d78520ce0ac9b0bb0ef5244cf65ad7c99.tar.bz2
artery-4879eb1d78520ce0ac9b0bb0ef5244cf65ad7c99.zip
Started refactoring item and inventory system
Diffstat (limited to 'gamemode/core')
-rw-r--r--gamemode/core/inventory/inventory.lua135
-rw-r--r--gamemode/core/inventory/item.lua67
2 files changed, 202 insertions, 0 deletions
diff --git a/gamemode/core/inventory/inventory.lua b/gamemode/core/inventory/inventory.lua
new file mode 100644
index 0000000..59c2bba
--- /dev/null
+++ b/gamemode/core/inventory/inventory.lua
@@ -0,0 +1,135 @@
+--[[
+ Public functions:
+ RegisterInventory(tbl_inventory) ::nil
+ CreateInventory(string_name) ::table_inventory
+ CreateInventoryFromData(string_name,string_data)::table_inventory)
+ DeriveInventory(string_name) ::table_inventory
+ Inventories have the following structure
+ inv.Name ::string
+ inv:FindPlaceFor(item) ::table_position or nil
+ inv:CanFitIn(table_position,item) ::true or string_explanation
+ inv:Put(table_position,item) ::nil
+ inv:Has(string_or_compare_func) ::table_position or nil
+ inv:Remove(position) ::table_item
+ inv:Get(position) ::table_item
+ inv:Serialize() ::string
+ inv:DeSerialize(str) ::table_inventory
+ 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
+ inv:RemoveObserver(number_id) ::nil
+
+ 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 thm = nrequire("colortheme.lua")
+local log = nrequire("log.lua")
+
+local inv = {}
+
+--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)
+ if self.observers == nil then self.observers = {} end
+ self.observers[#observers + 1] = tbl
+ return #observers
+end
+local function DefaultRemoveObserver(self,observer_id)
+ for i = observer_id, #self.observers do
+ self.observers[i] = self.observers + 1
+ end
+end
+local function SetDefaultObservers(tbl)
+ 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
+ oldput(self,position,item)
+ end
+ tbl.Remove = function(self,position)
+ for k,v in pairs(self.observers) do
+ v:Remove(position)
+ end
+ 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"},
+}
+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,
+ "Attempted to register 2 inventories with the same name")
+ 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.debug("Registered item: " .. tbl.Name)
+end
+
+--Create an inventory
+function inv.CreateInventory(name)
+ return table_copy(inventories[name])
+end
+
+--Recreates an inventory from data
+function inv.CreateInventoryFromData(name,data)
+ return create_inventory(name):DeSerialize(data)
+end
+
+--Must be called in a coroutine.
+function inv.DeriveInventory(name)
+ 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
diff --git a/gamemode/core/inventory/item.lua b/gamemode/core/inventory/item.lua
new file mode 100644
index 0000000..ef6ec1d
--- /dev/null
+++ b/gamemode/core/inventory/item.lua
@@ -0,0 +1,67 @@
+--[[
+ An itemsystem
+ public functions:
+ RegisterItem(table_item) ::nil
+ Registers a new item
+ GetItemByName(string_name) ::table_item
+ Returns the master copy of an item by name
+ DeriveItem(table_item,string_name) ::nil
+ Sets the table to a copy of an item. Copying happens in order, so if two items we derive from have a "DoThing" method, and "DoThing" is not defined in the item, the second derived item's "DoThing" method gets called on .DoThing()
+ Item:
+ item.Name ::string
+ Items must have unique names
+ item:Serialize() ::string
+ Turn any instace specific data of this item into a string, should be able to recreate an exact copy of this item in DeSerialize with this data
+ item:DeSerialize(string_data) ::nil
+ Recreate an item. If this item has any instance specific data, it should return a table.Copy(self) with the appropriate fields set.
+ The above must be defined for every item
+ Items may also have methods from one or more interfaces registered with RegisterInterface
+]]
+local log = nrequire("log.lua")
+
+local itm = {}
+local required_fields = {
+ "Name","Serialize","DeSerialize"
+}
+
+local items = {} --Master table of all item prototypes
+function itm.RegisterItem(tbl)
+ for k,v in pairs(required_fields) do
+ assert(tbl[v] ~= nil, string.format("Attempted to register item without field %q",v))
+ end
+ assert(items[tbl.Name] == nil, string.format("Attempted to register 2 items with the same name %q",tbl.Name))
+ log.debug("Registered item: " .. tbl.Name)
+end
+
+function itm.GetItemByName(name)
+ assert(items[name] ~= nil,string.format("Attempted to get item with invalid name %q",name))
+ return items[name]
+end
+
+function itm.GetItemFromData(name,data)
+ assert(items[name] ~= nil,string.format("Attempted to get item with invalid name %q",name))
+ return items[name]:DeSerialize(data)
+end
+
+--Must be called in a coroutine.
+function itm.DeriveItem(tbl,name)
+ while items[name] == nil do
+ coroutine.yield()
+ end
+ --Create a flywieght copy
+ local ret = tbl
+ local mt = {
+ __index = function(tbl,key)
+ return items[name][key]
+ end
+ }
+ setmetatable(ret,mt)
+end
+
+concommand.Add("art_printitems",function()
+ for k,v in pairs(items) do
+ print(k)
+ end
+end)
+
+return itm