Guest User

items.lua

a guest
Aug 5th, 2021
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.25 KB | None | 0 0
  1. --Copyright (c) 2015, Byrthnoth and Rooks
  2. --All rights reserved.
  3.  
  4. --Redistribution and use in source and binary forms, with or without
  5. --modification, are permitted provided that the following conditions are met:
  6.  
  7. --    * Redistributions of source code must retain the above copyright
  8. --      notice, this list of conditions and the following disclaimer.
  9. --    * Redistributions in binary form must reproduce the above copyright
  10. --      notice, this list of conditions and the following disclaimer in the
  11. --      documentation and/or other materials provided with the distribution.
  12. --    * Neither the name of <addon name> nor the
  13. --      names of its contributors may be used to endorse or promote products
  14. --      derived from this software without specific prior written permission.
  15.  
  16. --THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17. --ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. --WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. --DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
  20. --DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. --(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. --LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. --ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. --(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. --SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26.  
  27. local Items = {}
  28. local items = {}
  29. local bags = {}
  30. local item_tab = {}
  31.  
  32. local function validate_bag(bag_table)
  33.     if type(bag_table) == 'table' and windower.ffxi.get_bag_info(bag_table.id) then
  34.         if bag_table.access == 'Everywhere' then
  35.             return true
  36.         elseif bag_table.access == 'Mog House' then
  37.             if windower.ffxi.get_info().mog_house then
  38.                 return true
  39.             end
  40.            
  41.             for i = 0, 0x3FF do
  42.                 local t = windower.ffxi.get_mob_by_index(i)
  43.                 if t and (t.name == 'Nomad Moogle' or t.name == 'Pilgrim Moogle') and t.valid_target and t.distance < 36 then
  44.                     return true
  45.                 end
  46.             end
  47.         end
  48.     end
  49.     return false
  50. end
  51.  
  52. local function validate_id(id)
  53.     return (id and id ~= 0 and id ~= 0xFFFF) -- Not empty or gil
  54. end
  55.  
  56. local function wardrobecheck(bag_id,id)
  57.     return _static.wardrobe_ids[bag_id]==nil or ( res.items[id] and (res.items[id].type == 4 or res.items[id].type == 5) )
  58. end
  59.  
  60. function Items.new(loc_items,bool)
  61.     loc_items = loc_items or windower.ffxi.get_items()
  62.     new_instance = setmetatable({}, {__index = function (t, k) if rawget(t,k) then return rawget(t,k) else return rawget(items,k) end end})
  63.     for bag_id,bag_table in pairs(res.bags) do
  64.         org_debug("items", "Items.new::bag_id: "..bag_id)
  65.         bag_name = bag_table.english:lower():gsub(' ', '')
  66.         org_debug("items", "Items.new::bag_name: "..bag_name)
  67.         if (bool or validate_bag(bag_table)) and (loc_items[bag_id] or loc_items[bag_name]) then
  68.             org_debug("items", "Items.new: new_instance for ID#"..bag_id)
  69.             local cur_inv = new_instance:new(bag_id)
  70.             for inventory_index,item_table in pairs(loc_items[bag_id] or loc_items[bag_name]) do
  71.                 if type(item_table) == 'table' and validate_id(item_table.id) then
  72.                     org_debug("items", "Items.new: inventory_index="..inventory_index.." item_table.id="..item_table.id.." ("..res.items[item_table.id].english..")")
  73.                     cur_inv:new(item_table.id,item_table.count,item_table.extdata,item_table.augments,item_table.status,inventory_index)
  74.                 end
  75.             end
  76.         end
  77.     end
  78.     return new_instance
  79. end
  80.  
  81. function items:new(key)
  82.     org_debug("items", "New items instance with key "..key)
  83.     local new_instance = setmetatable({_parent = self,_info={n=0,bag_id=key}}, {__index = function (t, k) if rawget(t,k) then return rawget(t,k) else return rawget(bags,k) end end})
  84.     self[key] = new_instance
  85.     return new_instance
  86. end
  87.  
  88. function items:find(item)
  89.     for bag_name,bag_id in pairs(settings.bag_priority) do
  90.         real_bag_id = s_to_bag(bag_name)
  91.         org_debug("find", "Searching "..bag_name.." for "..res.items[item.id].english..".")
  92.         if self[real_bag_id] and self[real_bag_id]:contains(item) then
  93.             org_debug("find", "Found "..res.items[item.id].english.." in "..bag_name..".")
  94.             return real_bag_id, self[real_bag_id]:contains(item)
  95.         else
  96.             org_debug("find", "Didn't find "..res.items[item.id].english.." in "..bag_name..".")
  97.         end
  98.     end
  99.     org_debug("find", "Didn't find "..res.items[item.id].english.." in any bags.")
  100.     return false
  101. end
  102.  
  103. function items:route(start_bag,start_ind,end_bag,count)
  104.     count = count or self[start_bag][start_ind].count
  105.     local success = true
  106.     local initial_ind = start_ind
  107.     local inventory_max = windower.ffxi.get_bag_info(0).max
  108.     if start_bag ~= 0 and self[0]._info.n < inventory_max then
  109.         start_ind = self[start_bag][start_ind]:move(0,0x52,count)
  110.     elseif start_bag ~= 0 and self[0]._info.n >= inventory_max then
  111.         success = false
  112.         org_warning('Cannot move more than '..inventory_max..' items into inventory')
  113.     end
  114.  
  115.     local destination_enabled = windower.ffxi.get_bag_info(end_bag).enabled
  116.     local destination_max = windower.ffxi.get_bag_info(end_bag).max
  117.  
  118.     if not destination_enabled then
  119.         success = false
  120.         org_warning('Cannot move to '..tostring(end_bag)..' because it is disabled')
  121.     elseif start_ind and end_bag ~= 0 and self[end_bag]._info.n < destination_max then
  122.         self[0][start_ind]:transfer(end_bag,count)
  123.     elseif not start_ind then
  124.         success = false
  125.         org_warning('Initial movement of the route failed. ('..tostring(start_bag)..' '..tostring(initial_ind)..' '..tostring(start_ind)..' '..tostring(end_bag)..')')
  126.     elseif self[end_bag]._info.n >= destination_max then
  127.         success = false
  128.         org_warning('Cannot move more than '..destination_max..' items into that inventory ('..end_bag..')')
  129.     end
  130.     return success
  131. end
  132.  
  133. function items:it()
  134.     local i = 0
  135.     local bag_priority_list = {}
  136.     for i,v in pairs(settings.bag_priority) do
  137.         bag_priority_list[v] = i
  138.     end
  139.     return function ()
  140.         while i < #bag_priority_list do
  141.             i = i + 1
  142.             local id = s_to_bag(bag_priority_list[i])
  143.             if not id then
  144.                 org_error('The bag name ("'..tostring(bag_priority_list[i])..'") with priority '..tostring(i)..' in the ../addons/organizer/data/settings.xml file is not valid.\nValid options are '..tostring(res.bags))
  145.             end
  146.             if self[id] and validate_bag(res.bags[id]) then
  147.                 return id, self[id]
  148.             end
  149.         end
  150.     end
  151.  
  152. end
  153.  
  154. function bags:new(id,count,ext,augments,status,index)
  155.     local max_size = windower.ffxi.get_bag_info(self._info.bag_id).max
  156.     if self._info.n >= max_size then org_warning('Attempting to add another item to a full bag') return end
  157.     if index and table.with(self,'index',index) then org_warning('Cannot assign the same index twice') return end
  158.     self._info.n = self._info.n + 1
  159.     index = index or self:first_empty()
  160.     status = status or 0
  161.     augments = augments or ext and id and extdata.decode({id=id,extdata=ext}).augments
  162.     if augments then augments = table.filter(augments,-functions.equals('none')) end
  163.     self[index] = setmetatable({_parent=self,id=id,count=count,extdata=ext,index=index,status=status,
  164.         name=res.items[id][_global.language]:lower(),log_name=res.items[id][_global.language_log]:lower(),augments=augments},
  165.         {__index = function (t, k)
  166.             if not t or not k then print('table index is nil error',t,k) end
  167.             if rawget(t,k) then
  168.                 return rawget(t,k)
  169.             else
  170.                 return rawget(item_tab,k)
  171.             end
  172.         end})
  173.     return index
  174. end
  175.  
  176. function bags:it()
  177.     local max = windower.ffxi.get_bag_info(self._info.bag_id).max
  178.     local i = 0
  179.     return function ()
  180.         while i < max do
  181.             i = i + 1
  182.             if self[i] then return i, self[i] end
  183.         end
  184.     end
  185. end
  186.  
  187. function bags:first_empty()
  188.     local max = windower.ffxi.get_bag_info(self._info.bag_id).max
  189.     for i=1,max do
  190.         if not self[i] then return i end
  191.     end
  192. end
  193.  
  194. function bags:remove(index)
  195.     if not rawget(self,index) then org_warning('Attempting to remove an index that does not exist') return end
  196.     self._info.n = self._info.n - 1
  197.     rawset(self,index,nil)
  198. end
  199.  
  200. function bags:find_all_instances(item,bool,first)
  201.     local instances = L{}
  202.     for i,v in self:it() do
  203.         org_debug("find_all", "find_all_instances: slot="..i.." v="..res.items[v.id].english.." item="..res.items[item.id].english.." ")
  204.         if (bool or not v:annihilated()) and v.id == item.id then -- and v.count >= item.count then
  205.             if not item.augments or table.length(item.augments) == 0 or v.augments and extdata.compare_augments(item.augments,v.augments) then
  206.                 -- May have to do a higher level comparison here for extdata.
  207.                 -- If someone exports an enchanted item when the timer is
  208.                 -- counting down then this function will return false for it.
  209.                 instances:append(i)
  210.                 if first then
  211.                     return instances
  212.                 end
  213.             end
  214.         end
  215.     end
  216.     if instances.n ~= 0 then
  217.         return instances
  218.     else
  219.         return false
  220.     end
  221. end
  222.  
  223. function bags:contains(item,bool)
  224.     bool = bool or false -- Default to only looking at unannihilated items
  225.     org_debug("contains", "contains: searching for "..res.items[item.id].english.." in "..self._info.bag_id)
  226.     local instances = self:find_all_instances(item,bool,true)
  227.     if instances then
  228.         return instances:it()()
  229.     end
  230.     return false
  231. end
  232.  
  233. function bags:find_unfinished_stack(item,bool)
  234.     local tab = self:find_all_instances(item,bool,false)
  235.     if tab then
  236.         for i in tab:it() do
  237.             if res.items[self[i].id] and res.items[self[i].id].stack > self[i].count then
  238.                 return i
  239.             end
  240.         end
  241.     end
  242.     return false
  243. end
  244.  
  245. function item_tab:transfer(dest_bag,count)
  246.     -- Transfer an item to a specific bag.
  247.     if not dest_bag then org_warning('Destination bag is invalid.') return false end
  248.     count = count or self.count
  249.     local parent = self._parent
  250.     local targ_inv = parent._parent[dest_bag]
  251.  
  252.     local parent_bag_id = parent._info.bag_id
  253.     local target_bag_id = targ_inv._info.bag_id
  254.  
  255.     if not (target_bag_id == 0 or parent_bag_id == 0) then
  256.         org_warning('Cannot move between two bags that are not inventory bags.')
  257.     else
  258.         while parent[self.index] and targ_inv:find_unfinished_stack(parent[self.index]) do
  259.             org_debug("stacks", "Moving ("..res.items[self.id].english..') from '..res.bags[parent_bag_id].en..' to '..res.bags[target_bag_id].en..'')
  260.             local rv = parent[self.index]:move(dest_bag,targ_inv:find_unfinished_stack(parent[self.index]),count)
  261.             if not rv then
  262.                 org_debug("stacks", "FAILED moving ("..res.items[self.id].english..') from '..res.bags[parent_bag_id].en..' to '..res.bags[target_bag_id].en..'')
  263.                 break
  264.             end
  265.         end
  266.         if parent[self.index] then
  267.             parent[self.index]:move(dest_bag)
  268.         end
  269.         return true
  270.     end
  271.     return false
  272. end
  273.  
  274. function item_tab:move(dest_bag,dest_slot,count)
  275.     if not dest_bag then org_warning('Destination bag is invalid.') return false end
  276.     count = count or self.count
  277.     local parent = self._parent
  278.     local targ_inv = parent._parent[dest_bag]
  279.     dest_slot = dest_slot or 0x52
  280.  
  281.     local parent_bag_id = parent._info.bag_id
  282.     local parent_bag_name = res.bags[parent_bag_id].en:lower()
  283.  
  284.     local target_bag_id = targ_inv._info.bag_id
  285.  
  286.     org_debug("move", "move(): Item: "..res.items[self.id].english)
  287.     org_debug("move", "move(): Parent bag: "..parent_bag_id)
  288.     org_debug("move", "move(): Target bag: "..target_bag_id)
  289.  
  290.     -- issues with bazaared items makes me think we shouldn't screw with status'd items at all
  291.     if(self.status > 0) then
  292.         if(self.status == 5) then
  293.             org_verbose('Skipping item: ('..res.items[self.id].english..') because it is currently equipped.')
  294.             return false
  295.         elseif(self.status == 19) then
  296.             org_verbose('Skipping item: ('..res.items[self.id].english..') because it is an equipped linkshell.')
  297.             return false
  298.         elseif(self.status == 25) then
  299.             org_verbose('Skipping item: ('..res.items[self.id].english..') because it is in your bazaar.')
  300.             return false
  301.         end
  302.     end
  303.  
  304.     -- check the 'retain' lists
  305.     if((parent_bag_id == 0) and _retain[self.id]) then
  306.         org_verbose('Skipping item: ('..res.items[self.id].english..') because it is set to be retained ('.._retain[self.id]..')')
  307.         return false
  308.     end
  309.  
  310.     if((parent_bag_id == 0) and settings.retain and settings.retain.items) then
  311.         local cat = res.items[self.id].category
  312.         if(cat ~= 'Weapon' and cat ~= 'Armor') then
  313.             org_verbose('Skipping item: ('..res.items[self.id].english..') because non-equipment is set be retained')
  314.             return false
  315.         end
  316.     end
  317.  
  318.     -- respect the ignore list
  319.     if(_ignore_list[parent_bag_name] and _ignore_list[parent_bag_name][res.items[self.id].english]) then
  320.         org_verbose('Skipping item: ('..res.items[self.id].english..') because it is on the ignore list')
  321.         return false
  322.     end
  323.  
  324.     -- Make sure the source can be pulled from
  325.     if not _valid_pull[parent_bag_id] then
  326.         org_verbose('Skipping item: ('..res.items[self.id].english..') - can not be pulled from '..res.bags[parent_bag_id].en..') ')
  327.         return false
  328.     end
  329.  
  330.     -- Make sure the target can be pushed to
  331.     if not _valid_dump[target_bag_id] then
  332.         org_verbose('Skipping item: ('..res.items[self.id].english..') - can not be pushed to '..res.bags[target_bag_id].en..') ')
  333.         return false
  334.     end
  335.  
  336.     if not self:annihilated() and
  337.         (not dest_slot or not targ_inv[dest_slot] or (targ_inv[dest_slot] and res.items[targ_inv[dest_slot].id].stack < targ_inv[dest_slot].count + count)) and
  338.         (targ_inv._info.bag_id == 0 or parent._info.bag_id == 0) and
  339.         wardrobecheck(targ_inv._info.bag_id,self.id) and
  340.         self:free() then
  341.         windower.packets.inject_outgoing(0x29,string.char(0x29,6,0,0)..'I':pack(count)..string.char(parent._info.bag_id,dest_bag,self.index,dest_slot))
  342.         org_verbose('Moving item! ('..res.items[self.id].english..') from '..res.bags[parent._info.bag_id].en..' '..parent._info.n..' to '..res.bags[dest_bag].en..' '..targ_inv._info.n..')')
  343.         local new_index = targ_inv:new(self.id, count, self.extdata, self.augments)
  344.         --print(parent._info.bag_id,dest_bag,self.index,new_index)
  345.         parent:remove(self.index)
  346.         return new_index
  347.     elseif not dest_slot then
  348.         org_warning('Cannot move the item ('..res.items[self.id].english..'). Target inventory is full ('..res.bags[dest_bag].en..')')
  349.     elseif targ_inv[dest_slot] and res.items[targ_inv[dest_slot].id].stack < targ_inv[dest_slot].count + count then
  350.         org_warning('Cannot move the item ('..res.items[self.id].english..'). Target inventory slot would be overly full ('..(targ_inv[dest_slot].count + count)..' items in '..res.bags[dest_bag].en..')')
  351.     elseif (targ_inv._info.bag_id ~= 0 and parent._info.bag_id ~= 0) then
  352.         org_warning('Cannot move the item ('..res.items[self.id].english..'). Attempting to move from a non-inventory to a non-inventory bag ('..res.bags[parent._info.bag_id].en..' '..res.bags[dest_bag].en..')')
  353.     elseif self:annihilated() then
  354.         org_warning('Cannot move the item ('..res.items[self.id].english..'). It has already been moved.')
  355.     elseif not wardrobecheck(targ_inv._info.bag_id,self.id) then
  356.         org_warning('Cannot move the item ('..res.items[self.id].english..') to the wardrobe. Wardrobe cannot hold an item of its type ('..tostring(res.items[self.id].type)..').')
  357.     elseif not self:free() then
  358.         org_warning('Cannot free the item ('..res.items[self.id].english..'). It has an unaddressable item status ('..tostring(self.status)..').')
  359.     end
  360.     return false
  361. end
  362.  
  363. function item_tab:put_away(usable_bags)
  364.     org_debug("move", "Putting away "..res.items[self.id].english)
  365.     local current_items = self._parent._parent
  366.     usable_bags = usable_bags or _static.usable_bags
  367.     local bag_free
  368.     for _,v in ipairs(usable_bags) do
  369.         local bag_max = windower.ffxi.get_bag_info(v).max
  370.         if current_items[v]._info.n < bag_max and wardrobecheck(v,self.id) then
  371.             bag_free = v
  372.             break
  373.         end
  374.     end
  375.     if bag_free then
  376.         self:transfer(bag_free,self.count)
  377.     end
  378. end
  379.  
  380. function item_tab:free()
  381.     if self.status == 5 then
  382.         local eq = windower.ffxi.get_items().equipment
  383.         for _,v in pairs(res.slots) do
  384.             local ind_name = v.english:lower():gsub(' ','_')
  385.             local bag_name = ind_name..'_bag'
  386.             local ind, bag = eq[ind_name],eq[bag_name]
  387.             if self.index == ind and self._parent._info.bag_id == bag then
  388.                 windower.packets.inject_outgoing(0x50,string.char(0x50,0x04,0,0,self._parent._info.bag_id,v.id,0,0))
  389.                 break
  390.             end
  391.         end
  392.     elseif self.status ~= 0 then
  393.         return false
  394.     end
  395.     return true
  396. end
  397.  
  398. function item_tab:annihilate(count)
  399.     count = count or rawget(item_tab,'count')
  400.     local a_count = (rawget(item_tab,'a_count') or 0) + count
  401.     if a_count >count then
  402.         org_warning('Moving more of an item ('..item_tab.id..' : '..a_count..') than possible ('..count..'.')
  403.     end
  404.     rawset(self,'a_count',a_count)
  405. end
  406.  
  407. function item_tab:annihilated()
  408.     return ( (rawget(self,'a_count') or 0) >= rawget(self,'count') )
  409. end
  410.  
  411. function item_tab:available_amount()
  412.     return ( rawget(self,'count') - (rawget(self,'a_count') or 0) )
  413. end
  414.  
  415. return Items
Add Comment
Please, Sign In to add comment