aboutsummaryrefslogtreecommitdiff
path: root/gamemode/shared/inventory_common.lua
blob: e86d0a6a285cf25caac8cf03bd3b254cb1a210b1 (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
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
--[[
    Some functions that are needed multiple places in the code, to deal with player inventories.
]]
local invfuncs = {}

--Forcibly put an item in a backpack, you should check to make sure there's room first
invfuncs.PutItemInBackpack = function(backpack,x,y,item)
    backpack[1][x][y] = item
    for k = 1,#item.Shape do
        for i = 1,#(item.Shape[k]) do
            if k == 1 and i == 1 then continue end
            backpack[1][x + k - 1][y + i - 1] = item.Shape[k][i]
        end
    end
end

--Writes a backpack to the net stream
invfuncs.SerializeBackpack = function(backpack)
    net.WriteString(backpack[3]) --Name
    net.WriteUInt(backpack[2][1],16) --width
    net.WriteUInt(backpack[2][2],16) --height
    for k,v in pairs(backpack[1]) do
        for i,j in pairs(v) do
            if type(j) ~= "table" then continue end
            net.WriteString(j.Name)
            net.WriteUInt(k,16)
            net.WriteUInt(i,16)
            local data = j:Serialize()
            net.WriteUInt(#data,32)
            net.WriteData(data,#data)
        end
    end
    net.WriteString("END_ITEMS")
end

--Loads a backpack from the net stream
invfuncs.DeSerializeBackpack = function()
    local backpackname = net.ReadString()
    local width = net.ReadUInt(16)
    local height = net.ReadUInt(16)
    --Build the backpack
    local tb = {}
    tb[1] = {}
    tb[2] = {width,height}
    tb[3] = backpackname
    for x = 1,width do
        tb[1][x] = {}
        for y = 1,height do
            tb[1][x][y] = false
        end
    end

    local nitemname = net.ReadString()
    while nitemname ~= "END_ITEMS" do
        local xpos = net.ReadUInt(16)
        local ypos = net.ReadUInt(16)
        local data = net.ReadData(net.ReadUInt(32))
        local nitem = ART.GetItemByName(nitemname):DeSerialize(data)
        invfuncs.PutItemInBackpack(tb,xpos,ypos,nitem)
        nitemname = net.ReadString()
    end

    return tb

end

--Checks to see if an item can fit in the backpack at a certain position
invfuncs.CanFitInBackpack = function(backpack,x,y,item)
    for k,v in pairs(item.Shape) do
        for i,j in pairs(v) do
            if not j then continue end
            if backpack[1][x + k - 1] == nil then
                return false
            end
            if backpack[1][x + k - 1][y + i - 1] or backpack[1][x + k - 1][y + i - 1] == nil then
                return false
            end
        end
    end
    return true
end

--Creates a new backpack, this is NOT called when players join the server, even new ones.
invfuncs.CreateBackpack = function(name,width,height)
    local backpack = {}
    backpack[1] = {}
    backpack[2] = {width,height}
    backpack[3] = name
    for i = 1,width do --the width of the backpack.
      backpack[1][i] = {}
    end
    for k,v in pairs(backpack[1]) do
        for i = 1,height do --the height of the backpack.
            backpack[1][k][i] = false
        end
    end
    return backpack
end

--Displays a dropdown of options under the players mouse, if the option is clicked, does the function
--Requires a table of strings to functions, or strings to tables of strings to functions.
--Be careful not to make this a recursive table.
local function createMenuFor(menu, tbl)
  for k,v in pairs(tbl) do
    if(isfunction(v)) then --This is a dead-end, add the menu
      local thisoption = menu:AddOption(k,v)
    else --Otherwise it should be a table, recursively call to create
      local submenu = menu:AddSubMenu(k)
      createMenuFor(submenu,v)
    end
  end
end

--Draws a backpack on the dpanel, client side only
invfuncs.DrawBackpackOnDPanel = function(dp, backpack, backpacknum, tent)
    local width = ScrW()
    local height = ScrH()
    local slotsize = math.Round(width / 32)
    local backgrid = vgui.Create( "DGrid", dp )
    backgrid:SetPos( 10, 30 )
    backgrid:SetCols( backpack[2][1] )
    backgrid:SetColWide( backpack[2][2] )
    backgrid:Dock(FILL)
    for i = 1,#(backpack[1]) do
        for j = 1,#(backpack[1][i]) do
            local item = backpack[1][j][i]
            if type(backpack[1][j][i]) == "table" then
                local itemwidth = 0
                for _,l in pairs(item.Shape) do
                    itemwidth = math.Max(itemwidth,#l)
                end
                local itemheight = #item.Shape
                local invicon = vgui.Create( "DButton", dp )
                invicon:SetSize(slotsize * itemwidth, slotsize * itemheight)
                invicon:SetPos(slotsize * (i - 1), slotsize * (j - 1))
                invicon:SetText(item.Name)
                if item.Tooltip then
                    invicon:SetTooltip(item.Tooltip)
                end
                --invicon.Paint = function(self, w, h) draw.RoundedBox(4, 0,0,w,h,Color(0,100,0)) end
                invicon.DoClick = function()
                    if not item.GetOptions then return end
                    local menu = vgui.Create("DMenu")
        			createMenuFor(menu,item:GetOptions())
        			menu:Open()
                end
                invicon.Item = item
                invicon.invpos = {j,i}
                invicon.ent = tent
                invicon.backpacknum = backpacknum
                invicon:Droppable("Inventory")
            elseif not backpack[1][j][i] then
                local emptyslot = vgui.Create("DPanel", dp)
                emptyslot:SetSize(slotsize,slotsize)
                emptyslot:SetPos(slotsize * (i - 1), slotsize * (j - 1))
                --emptyslot.Paint = function(self, w, h) draw.RoundedBox(4, 0,0,w,h,Color(0,0,100)) end
                emptyslot:Receiver( "Inventory", function( receiver, tableOfDroppedPanels, isDropped, menuIndex, mouseX, mouseY )
                    if not isDropped then return end
                    local icon = tableOfDroppedPanels[1]
                    local item = icon.Item
                    local curpos = icon.invpos
                    --Set the shape it was at to false
                    if not icon.wasequiped and icon.ent == tent then
                        assert(curpos ~= nil, "print curpos was nil when not equiped")
                        for k = 1,#item.Shape do
                            for l = 1,#(item.Shape[k]) do
                                if k == 1 and l == 1 then continue end
                                backpack[1][curpos[1] + k - 1][curpos[2] + l - 1] = false
                            end
                        end
                        backpack[1][curpos[1]][curpos[2]] = false
                    end
                    if invfuncs.CanFitInBackpack(backpack,j,i,item) then
                        local fromtbl = icon.invpos
                        local wasequiped = icon.wasequiped
                        if wasequiped then
                            net.Start("unequipitem")
                            net.WriteString(wasequiped)
                            net.WriteUInt(backpacknum,16)
                            net.WriteUInt(i,16)
                            net.WriteUInt(j,16)
                            net.SendToServer()
                        else
                            net.Start("moveitem")
                            net.WriteEntity(icon.ent) -- from ent
                            net.WriteEntity(tent) -- to ent
                            net.WriteUInt(icon.backpacknum,16) -- from backpack number
                            net.WriteUInt(backpacknum,16) -- to backpack number
                            net.WriteUInt(fromtbl[1],16) -- From position
                            net.WriteUInt(fromtbl[2],16)
                            net.WriteUInt(j,16) -- To position
                            net.WriteUInt(i,16)
                            net.SendToServer()
                            if item.onEquip ~= nil then
                                item:onEquip(LocalPlayer())
                            end
                        end
                    end
                end, {} )
            end
        end
    end
end

return invfuncs