aboutsummaryrefslogtreecommitdiff
path: root/tutorials/tut030_inventories.md
blob: 41fb2939d29314f9075206827a0cf9d66ed3db43 (plain)
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
# Tut 0x030

## Inventories

Many gamemode bases try to build an inventory system into the gamemode, leaving the developer with few ways to actually use the system more than "put item in, take item out". Artery somewhat forces you to build your own inventories to use.

In this tutorial, we'll build a simple rougelike inventory, where we expect items to have a "weight", and we can carry as many items as we want, as long as we don't exceed our "max weight".

Inventories and items in Artery are just tables, as per usual in lua. They have a few required fields each. On the left, under "Classes" (all the way at the bottom!), open invtbl and itemtbl in new tabs.

## A simple inventory

You can see all the fields needed for an inventory, so let's get started. Recall that

	function tbl:func_name(one)
		...
	end

is the same as

	function tbl.func_name(self,one)
		...
	end

lua will automatically create the variable "self" in the first example

garrysmod/addons/artery_rougelite/data/artery/global/rougeinv.lua
	
	local inventory_registry = nrequire("inventory.lua")
	local item_registry = nrequire("item.lua")
	local inv = {}

	inv.items = {} --A table of all the items we have
	inv.maxweight = 100 --The maximum weight of all our items
	inv.weight = 0 --The current weight of all our items.
	--Technically we could calculate the weight based on the "items" table,
	--	but that might start taking a long time if we have a lot of low-weight items.

	--Returns a position for an item, or nil if we don't have room
	function inv:FindPlaceFor(item)
		--Make sure we won't be over weight when we add the item
		if self.weight + item.weight > self.maxweight then
			return nil
		end
		--[[
		ALWAYS return a table position, even if you only need a number.
		This is the same as
		return {
			[1] = #self.items + 1
		}
		]]
		return {#self.items + 1}
	end

	function inv:CanFitIn(position,item)
		--Check that we won't be over weight
		if self.weight + item.weight > self.maxweight then
			return false
		end
		
		--And make sure we don't already have an item in that position
		if self.items[position[1]] ~= nil then
			return false
		end
			
		--If we won't go over weight, and the position isn't already used, we can fit it!
		return true
	end

	--Put something into our inventory
	function inv:Put(position,item)
		self.items[position[1]] = item
		self.weight = self.weight + item.weight
	end

	--This one is a bit complicated
	--ptr can be either a string, or a function that takes 1 argument,
	--	and returns true when given an object we should return.
	--this function returns a POSITION that can be given back to get, remove, ect.
	--	the object.
	function inv:Has(ptr)
		--Look for an item with the given name
		if type(ptr) == "string" then 
			for k,v in pairs(self.items) do
				if v.Name == ptr then
					return {k}
				end
			end
		elseif type(ptr) == "function" then
			for k,v in pairs(self.items) do
				if ptr(v) then
					return {k}
				end
			end
		end
		return nil
	end

	--Removes an item from our inventory, returns the item we removed
	function inv:Remove(position)
		local item = self.items[position[1]]
		self.weight = self.weight - item.weight
		self.items[position[1]] = nil
		return item
	end

	--Gets the item in a given position
	function inv:Get(position)
		return self.items[position[1]]
	end

	--Creates a string that this inventory can be re-created with
	function inv:Serialize()
		local ser = {}
		for k,v in pairs(self.items) do
			ser[k] = {
				v.Name,
				v:Serialize() --Take advantage of the fact that all items must also have a
				              --     Serialize() method
			}
		end
		return util.TableToJSON(ser)
	end

	--Re-creates this inventory from the data created by Serialize()
	function inv:DeSerialize(data)
		local self_copy = table.Copy(self)
		local json = util.JSONToTable(data)
		for k,v in pairs(json) do
			local item_name = v[1]
			local item_data = v[2]
			local item = item_registry.GetItemFromData(item_name,item_data)
			self_copy.items[k] = item
		end
		for k,v in pair(self_copy.items) do
			self_copy.weight = self_copy.weight + v.weight
		end
		return self_copy
	end
	
	--Don't forget to register it with the gamemode!
	inventory_registry.RegisterInventory(inv)
	

That was a bit long, but we're done! This is the absolute minimum needed to create an Artery inventory. Now you can go in-game and use the console command `artery_printinventories`, and in that list, you will see your newly made inventory!

Next tutorial: @{tut040_items.md}
Further reading: @{tut031_metatables.md}