-
Notifications
You must be signed in to change notification settings - Fork 157
/
equip_processing.lua
292 lines (273 loc) · 15.9 KB
/
equip_processing.lua
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
--Copyright (c) 2013~2016, Byrthnoth
--All rights reserved.
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are met:
-- * Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
-- * Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in the
-- documentation and/or other materials provided with the distribution.
-- * Neither the name of <addon name> nor the
-- names of its contributors may be used to endorse or promote products
-- derived from this software without specific prior written permission.
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-----------------------------------------------------------------------------------
--Name: check_wearable(item_id)
--Args:
---- item_id - Item ID to be examined
-----------------------------------------------------------------------------------
--Returns:
---- boolean indicating whether the given piece of gear can be worn or not
---- Checks for main job, level, superior level, and gender/race
-----------------------------------------------------------------------------------
function check_wearable(item_id)
if not item_id or item_id == 0 then -- 0 codes for an empty slot, but Arcon will probably make it nil at some point
elseif not res.items[item_id] then
msg.debugging("Item "..item_id.." has not been added to resources yet.")
elseif not res.items[item_id].jobs then -- Make sure item can be equipped by specific jobs (unlike pearlsacks).
--msg.debugging('GearSwap (Debug Mode): Item '..(res.items[item_id][language] or item_id)..' does not have a jobs field in the resources.')
elseif not res.items[item_id].slots then
-- Item is not equippable
else
return (res.items[item_id].jobs[player.main_job_id]) and (res.items[item_id].level<=player.jobs[res.jobs[player.main_job_id].ens]) and (res.items[item_id].races[player.race_id]) and
(player.superior_level >= (res.items[item_id].superior_level or 0))
end
return false
end
-----------------------------------------------------------------------------------
--Name: name_match(item_id,name)
--Args:
---- item_id - Item ID to be compared
---- name - Name to be compared
-----------------------------------------------------------------------------------
--Returns:
---- boolean indicating whether the name matches the resources entry for the itemID
-----------------------------------------------------------------------------------
function name_match(item_id,name)
if res.items[item_id] then
return (res.items[item_id][language..'_log']:lower() == name:lower() or res.items[item_id][language]:lower() == name:lower())
else
return false
end
end
-----------------------------------------------------------------------------------
--Name: expand_entry(v)
--Args:
---- entry - Table or string ostensibly from an equipment set
-----------------------------------------------------------------------------------
--Returns:
---- name - Name of the current piece of equipment
---- priority - Priority of the current piece as defined in the advanced table
---- augments - Augments for the current piece as defined in the advanced table
---- designated_bag - Bag for the current piece as defined in the advanced table
-----------------------------------------------------------------------------------
function expand_entry(entry)
if not entry then
return
end
local augments,name,priority,designated_bag
if type(entry) == 'table' and entry == empty then
name = empty
elseif type(entry) == 'table' and entry.name and type(entry.name) == 'string' then
name = entry.name
priority = entry.priority
if entry.augments then
augments = entry.augments
elseif entry.augment then
augments = {entry.augment}
end
if entry.bag and type(entry.bag) == 'string' then
designated_bag = bag_string_lookup[to_windower_bag_api(entry.bag)]
end
elseif type(entry) == 'string' and entry ~= '' then
name = entry
end
return name,priority,augments,designated_bag -- all nil if they don't exist
end
-----------------------------------------------------------------------------------
--Name: unpack_equip_list(inventory,equip_list)
--Args:
---- inventory - Current inventory (potentially avoids a get_items() call)
---- equip_list - Keys are standard slot names, values are item names.
-----------------------------------------------------------------------------------
--Returns:
---- Table with keys that are slot numbers with values that are inventory slot #s.
-----------------------------------------------------------------------------------
function unpack_equip_list(equip_list,cur_equip)
local ret_list = {} -- Gear that is designated to be equipped
local used_list = {} -- Gear that is scheduled to be equipped but is already being worn
local error_list = {} -- Gear that cannot be equipped for whatever reason
local priorities = Priorities:new()
for slot_id,slot_name in pairs(default_slot_map) do
local name,priority,augments,designated_bag = expand_entry(equip_list[slot_name])
priorities[slot_id] = priority
if name == empty then
equip_list[slot_name] = nil
if cur_equip[slot_name].slot ~= empty then
ret_list[slot_id] = {bag_id=0,slot=empty}
end
elseif name and cur_equip[slot_name].slot ~= empty then
local item_tab = items[to_windower_bag_api(res.bags[cur_equip[slot_name].bag_id].en)][cur_equip[slot_name].slot]
if name_match(item_tab.id,name) and
(not augments or (#augments ~= 0 and extdata.compare_augments(augments,extdata.decode(item_tab).augments))) and
(not bag or bag == cur_equip[slot_name].bag_id) then
equip_list[slot_name] = nil
used_list[slot_id] = {bag_id=cur_equip[slot_name].bag_id,slot=cur_equip[slot_name].slot}
end
end
end
for _,bag in pairs(equippable_item_bags) do
for _,item_tab in ipairs(items[to_windower_bag_api(bag.en)]) do -- Iterate over the current bag
if type(item_tab) == 'table' and check_wearable(item_tab.id) then
if item_tab.status == 0 or item_tab.status == 5 then
for slot_id in res.items[item_tab.id].slots:it() do
local slot_name = default_slot_map[slot_id]
-- equip_list[slot_name] can also be a table (that doesn't contain a "name" property) or a number, which are both cases that should not generate any kind of equipment changing.
-- Hence the "and name" below.
if not ret_list[slot_id] and equip_list[slot_name] then -- If we haven't already found something for this slot and still want to equip something there
-- Make sure we're not already planning to equip this item in another slot.
if (slot_id == 0 and used_list[1] and used_list[1].bag_id == bag.id and used_list[1].slot == item_tab.slot) or -- main vs. sub
(slot_id == 1 and used_list[0] and used_list[0].bag_id == bag.id and used_list[0].slot == item_tab.slot) or -- sub vs. main
(slot_id == 11 and used_list[12] and used_list[12].bag_id == bag.id and used_list[12].slot == item_tab.slot) or --left_earring vs. right_earring
(slot_id == 12 and used_list[11] and used_list[11].bag_id == bag.id and used_list[11].slot == item_tab.slot) or --right_earring vs. left_earring
(slot_id == 13 and used_list[14] and used_list[14].bag_id == bag.id and used_list[14].slot == item_tab.slot) or --left_ring vs. right_ring
(slot_id == 14 and used_list[13] and used_list[13].bag_id == bag.id and used_list[13].slot == item_tab.slot) then --right_ring vs. left_ring
break
end
local name,priority,augments,designated_bag = expand_entry(equip_list[slot_name])
if (not designated_bag or designated_bag == bag.id) and name and name_match(item_tab.id,name) then
if augments and #augments ~=0 then
if res.items[item_tab.id].flags.Rare or extdata.compare_augments(augments,extdata.decode(item_tab).augments) then
-- Check if the augments are right
-- If the item is Rare, then even if the augments are wrong try to equip it anyway because you only have one
equip_list[slot_name] = nil
ret_list[slot_id] = {bag_id=bag.id,slot=item_tab.slot}
used_list = ret_list[slot_id]
break
--else the piece specifies augments that don't match the current piece, so don't break and keep trying.
end
else
equip_list[slot_name] = nil
ret_list[slot_id] = {bag_id=bag.id,slot=item_tab.slot}
used_list = ret_list[slot_id]
break
end
end
end
end
else -- item_tab.status > 0
for slot_id in res.items[item_tab.id].slots:it() do
local slot_name = default_slot_map[slot_id]
local name = expand_entry(equip_list[slot_name])
if name and name ~= empty then -- If "name" isn't a piece of gear, then it won't have a valid value at this point and should be ignored.
if name_match(item_tab.id,name) then
if item_tab.status == 25 then
error_list[slot_name] = name..' (bazaared)'
else
error_list[slot_name] = name..' (status unknown: '..item_tab.status..' )'
end
break
end
end
end
end
else
for __,slot_name in pairs(default_slot_map) do
local name = expand_entry(equip_list[slot_name])
if name ~= empty and name_match(item_id,name) then
if not res.items[item_tab.id].jobs[player.main_job_id] then
equip_list[slot_name] = nil
error_list[slot_name] = name..' (cannot be worn by this job)'
elseif not (res.items[item_tab.id].level<=player.jobs[player.main_job]) then
equip_list[slot_name] = nil
error_list[slot_name] = name..' (job level is too low)'
elseif not res.items[item_tab.id].races[player.race_id] then
equip_list[slot_name] = nil
error_list[slot_name] = name..' (cannot be worn by your race)'
elseif not res.items[item_tab.id].slots then
equip_list[slot_name] = nil
error_list[slot_name] = name..' (cannot be worn)'
end
break
end
end
end
end
end
if _settings.debug_mode and table.length(error_list) > 0 then
print_set(error_list,'Debug Mode (error list)')
end
if _settings.debug_mode and table.length(equip_list) > 0 then
print_set(equip_list,'Debug Mode (gear not equipped)')
end
return ret_list,priorities
end
-----------------------------------------------------------------------------------
--Name: to_names_set(equipment)
--Args:
---- equipment - Mapping of equipment slot ID or slot name to a table containing
---- bag_id and inventory slot ID. If already indexed to a number, treat it as a slot index.
---- Otherwise, damn the torpedoes and tostring it.
-----------------------------------------------------------------------------------
--Returns:
---- Set with a mapping of slot name to equipment name.
---- 'empty' is used as a replacement for the empty table.
-----------------------------------------------------------------------------------
function to_names_set(equipment)
local equip_package = {}
for ind,cur_item in pairs(equipment) do
local name = 'empty'
if type(cur_item) == 'table' and cur_item.slot ~= empty then
if items[to_bag_api(res.bags[cur_item.bag_id].english)][cur_item.slot].id == 0 then return {} end
-- refresh_player() can run after equip packets arrive but before the item array is fully loaded,
-- which results in the id still being the initialization value.
name = res.items[items[to_bag_api(res.bags[cur_item.bag_id].english)][cur_item.slot].id][language]
end
if tonumber(ind) and ind >= 0 and ind <= 15 and math.floor(ind) == ind then
equip_package[toslotname(ind)] = name
else
equip_package[tostring(ind)] = name
end
end
return equip_package
end
-----------------------------------------------------------------------------------
--Name: equip_piece(eq_slot_id,bag_id,inv_slot_id)
--Desc: Cleans up the global table and leaves equip_sets properly.
--Args:
---- eq_slot_id - Equipment Slot ID
---- bag_id - Bag ID of the item to be equipped
---- inv_slot_id - Inventory Slot ID of the item to be equipped
-----------------------------------------------------------------------------------
--Returns:
---- none
-----------------------------------------------------------------------------------
function equip_piece(eq_slot_id,bag_id,inv_slot_id)
-- Many complicated, wow!
local cur_eq_tab = items.equipment[toslotname(eq_slot_id)]
if cur_eq_tab.slot ~= empty then
items[to_bag_api(res.bags[cur_eq_tab.bag_id].english)][cur_eq_tab.slot].status = 0
-- This does not account for items like Onca Suit which take up multiple slots
end
if inv_slot_id ~= empty then
--items.equipment[toslotname(eq_slot_id)] = {slot=inv_slot_id,bag_id=bag_id}
items[to_bag_api(res.bags[bag_id].english)][inv_slot_id].status = 5
local minichunk = string.char(inv_slot_id,eq_slot_id,bag_id,0)
injected_equipment_registry[minichunk:byte(2)]:append(minichunk:sub(1,3))
return minichunk
else
--items.equipment[toslotname(eq_slot_id)] = {slot=empty,bag_id=0}
local minichunk = string.char(0,eq_slot_id,0,0)
injected_equipment_registry[minichunk:byte(2)]:append(minichunk:sub(1,3))
return minichunk
end
end