diff options
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua')
-rw-r--r-- | Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua new file mode 100644 index 0000000..bb6b861 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua @@ -0,0 +1,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 designated_bag or designated_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
\ No newline at end of file |