diff options
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/obiaway/Obiaway.lua')
-rw-r--r-- | Data/BuiltIn/Libraries/lua-addons/addons/obiaway/Obiaway.lua | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/obiaway/Obiaway.lua b/Data/BuiltIn/Libraries/lua-addons/addons/obiaway/Obiaway.lua new file mode 100644 index 0000000..523b13d --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/obiaway/Obiaway.lua @@ -0,0 +1,509 @@ +-- +-- obiaway v1.0.7 +-- +-- Copyright ©2013-2015, ReaperX, Bangerang +-- 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 obiaway 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 ReaperX or Bangerang 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. +-- +-- Puts elemental obi away when they are no longer needed due +-- to day/weather/storm effect change and gets elemental obi based +-- on same conditions. Uses itemizer addon. +-- +-- The advantage of this system compared to moving obi back during +-- aftercast is that it avoids excessive inventory movement, +-- so malfunctions due to inventory filling up completely are +-- less likely, and timing issues with very fast spells (spell +-- fires before obi is moved) occur at worst on the first spell +-- not but subsequent ones. +-- +-- Known bugs: +-- +-- 1. Using the get, put, or sort commands too quickly can have +-- undesirable effects, often not moving obi at all. This is due to +-- limitations on how fast items can be moved from one inventory to +-- another. +-- +-- 2. When weather changes due to zoning, get_obi_in_inventory() +-- is called before inventory has loaded and returns nothing. +-- +-- 3. Obi is not moved when currently equipped. +-- +-- +-- To-do: +-- - Add support for other languages. +-- +-- + +-- addon info +_addon.name = "obiaway" +_addon.author = "ReaperX, Bangerang" +_addon.version = "1.0.7" +_addon.commands = {'oa', 'ob', 'obi', 'obiaway'} +_addon.language = 'english' + +-- library includes +require('sets') +require('logger') +config = require('config') +res = require('resources') + +-- settings +default_settings = {} +default_settings.notify = true +default_settings.location = 'sack' +default_settings.lock = false +default_settings.color = 209 +default_settings.ignore_zones = S{ + "Ru'Lude Gardens", "Upper Jeuno", "Lower Jeuno", "Port Jeuno", "Port Windurst", "Windurst Waters", "Windurst Woods", "Windurst Walls", + "Heavens Tower", "Port San d'Oria", "Northern San d'Oria", "Southern San d'Oria", "Chateau d'Oraguille", "Port Bastok", "Bastok Markets", + "Bastok Mines", "Metalworks", "Aht Urhgan Whitegate", "Tavnazian Safehold", "Nashmau", "Selbina", "Mhaura", "Norg", "Rabao", "Kazham", + "Eastern Adoulin", "Western Adoulin", "Leafallia", "Celennia Memorial Library", "Mog Garden"} +settings = config.load(default_settings) + +-- tokens +tokens = {} +tokens.lock_warned = false +tokens.inv_full_warned = false +tokens.bag_full_warned = false + +-- lists +obi_names = T{ + Light = 'Korin', + Dark = 'Anrin', + Fire = 'Karin', + Earth = 'Dorin', + Water = 'Suirin', + Wind = 'Furin', + Ice = 'Hyorin', + Lightning = 'Rairin' +} + + +---------------------------------------- +--- UTITLITY Functions +---------------------------------------- + +-- Automatically formats output text for addon. Accepts msg as a string. +function obi_output(msg) + prefix = 'Obi: ' + windower.add_to_chat(settings.color, prefix..msg) +end + +-- Accepts a boolean value and returns an appropriate string value. i.e. true -> 'on' +function bool_to_str(bool) + return bool and 'on' or 'off' +end + +-- checks if a value is in a table and then returns its key. Ex: a table contains Key = Value. returns Key. returns false if no match. +function in_table(tbl, item) + for key, value in pairs(tbl) do + if value == item then return key end + end + return false +end + +-- converts name of a bag (str) to an id number. returns 0 (inventory) when no argument passed. +function inv_str_to_id(str) + if not str then return 0 end + if str == 'inventory' then + return 0 + elseif str == 'safe' then + return 1 + elseif str == 'storage' then + return 2 + elseif str == 'locker' then + return 3 + elseif str == 'satchel' then + return 5 + elseif str == 'sack' then + return 6 + elseif str == 'case' then + return 7 + elseif str == 'wardrobe' then + return 8 + else + print('Obiaway: Function inv_str_to_id invalid argument') + return + end +end + +-- Function which counts how many slots remain in a bag. if no location is passed, checks inventory. +function free_space(location) + local id = inv_str_to_id(location) + + local inv = windower.ffxi.get_bag_info(id) + n = inv.max - inv.count + return n +end + +-- check's if an inventory is full. returns true if full. if no location argument is passed checks main inventory. +-- also handle's an inventory full warning. requires two tokens which keep track of whether or not an inventory +-- full warning was already given. one for the main inventory and a separate for a bag. this is to prevent +-- "Inventory full." spam in the chat log. +function inventory_full(command, location) + local id = inv_str_to_id(location) + if id == 0 then location = 'inventory' end + + if free_space(location) == 0 then + if command then + obi_output('%s is full.':format(string.ucfirst(location))) + elseif not tokens.inv_full_warned and id == 0 then + tokens.inv_full_warned = true + obi_output('%s is full.':format(string.ucfirst(location))) + elseif not tokens.bag_full_warned then + tokens.bag_full_warned = true + obi_output('%s is full.':format(string.ucfirst(location))) + end + return true + elseif free_space(location) > 0 then -- resets the tokens when space is free + if id == 0 then tokens.inv_full_warned = false else tokens.bag_full_warned = false end + return false + end + + print('Obiaway: Function inventory_full unknown error.') +end + + +---------------------------------------- +--- ADDON functions +---------------------------------------- + +-- locks obi sorting. accepts true or false. +function lock_obi(toggle) + if toggle then + settings.lock = true + tokens.lock_warned = true + if settings.notify then + obi_output('Obi locked.') + end + elseif not toggle then + settings.lock = false + tokens.lock_warned = false + if settings.notify then + obi_output('Unlocking obi...') + end + end +end + +-- Builds a table of boolean values that indicate which obi are in inventory and how many. +-- Ex: +-- obi = { +-- "Fire" = false +-- "Ice" = false +-- "Wind" = true +-- "Earth" = false +-- "Lightning" = true +-- "Water" = false +-- "Light" = false +-- "Dark" = true +-- n = 3 +-- } +-- +-- function designer: ReaperX +function get_obi_in_inventory(location) + local id = inv_str_to_id(location) + local obi = {} + local inv = windower.ffxi.get_items(id) + if not inv then return end + + for i=1,inv.max do + id = inv[i].id + if ( id>=15435 and id<=15442) then + obi["Fire"] = obi["Fire"] or (id == 15435) + obi["Ice"] = obi["Ice"] or (id == 15436) + obi["Wind"] = obi["Wind"] or (id == 15437) + obi["Earth"] = obi["Earth"] or (id == 15438) + obi["Lightning"] = obi["Lightning"] or (id == 15439) + obi["Water"] = obi["Water"] or (id == 15440) + obi["Light"] = obi["Light"] or (id == 15441) + obi["Dark"] = obi["Dark"] or (id == 15442) + end + end + + -- count obi in inventory + obi["n"] = table.count(obi, true) + + return obi +end + +-- Builds a table (elements) which contains storm buffs, weather effects, and day of the week is active. +-- Adds +1 to corresponding element variable for each effect active. +-- Ex: If fire weather and earthsday are active will return: +-- elements = { +-- "Fire" = true +-- "Earth" = true +-- "Water" = false +-- "Wind" = false +-- "Ice" = false +-- "Lightning" = false +-- "Light" = false +-- "Dark" = false +-- "None" = false +-- } +-- +-- function designer: ReaperX +function get_all_elements() + local elements = {} + elements["Fire"] = 0 + elements["Earth"] = 0 + elements["Water"] = 0 + elements["Wind"] = 0 + elements["Ice"] = 0 + elements["Lightning"] = 0 + elements["Light"] = 0 + elements["Dark"] = 0 + elements["None"] = 0 + + -- check for active Day and Weather + local info = windower.ffxi.get_info() + local day_element = res.elements[res.days[info.day].element].english + elements[day_element] = elements[day_element] + 1 + local weather_element = res.elements[res.weather[info.weather].element].english + elements[weather_element] = elements[weather_element] + 1 + + -- check for active SCH buffs + local buffs = windower.ffxi.get_player().buffs + if in_table(buffs, 178) then + elements["Fire"] = elements["Fire"] + 1 + elseif in_table(buffs, 183) then + elements["Water"] = elements["Water"] + 1 + elseif in_table(buffs, 181) then + elements["Earth"] = elements["Earth"] + 1 + elseif in_table(buffs, 180) then + elements["Wind"] = elements["Wind"] + 1 + elseif in_table(buffs, 179) then + elements["Ice"] = elements["Ice"] + 1 + elseif in_table(buffs, 182) then + elements["Lightning"] = elements["Lightning"] + 1 + elseif in_table(buffs, 184) then + elements["Light"] = elements["Light"] + 1 + elseif in_table(buffs, 185) then + elements["Dark"] = elements["Dark"] + 1 + end + + return elements +end + +---------------------------------------- +--- SORTING functions +---------------------------------------- + +function get_needed_obi(command) + if inventory_full(command) then return false end + local obi = get_obi_in_inventory() + local elements = get_all_elements() + + for name, element in obi_names:it() do + if not obi[element] and elements[element] > 0 then + windower.send_command('get "%s Obi" %s;':format(name, settings.location)) + if settings.notify then + obi_output('Getting %s Obi from %s.':format(name, settings.location)) + end + coroutine.sleep(.5) + end + end + + return true +end + +function put_unneeded_obi(command) + if inventory_full(command, settings.location) then return false end + local obi = get_obi_in_inventory() + local elements = get_all_elements() + + for name, element in obi_names:it() do + if obi[element] and elements[element] == 0 then + windower.send_command('put "%s Obi" %s;':format(name, settings.location)) + if settings.notify then + obi_output('Putting %s Obi away into %s.':format(name, settings.location)) + end + coroutine.sleep(.5) + end + end + + return true +end + +function get_all_obi(command) + if inventory_full(command) then return false end + local obi = get_obi_in_inventory() + local obi_bag = get_obi_in_inventory(settings.location) + if free_space() < obi_bag["n"] then + obi_output('Not enough space in inventory...') + return false + end + + local elements = get_all_elements() + local obi = get_obi_in_inventory() + + for name, element in obi_names:it() do + if not obi[element] then + windower.send_command('get "%s Obi" %s;':format(name, settings.location)) + if settings.notify then + obi_output('Getting %s Obi from %s.':format(name, settings.location)) + end + coroutine.sleep(.5) + end + end + + return true +end + +function put_all_obi(command) + if inventory_full(command, settings.location) then return false end + local obi = get_obi_in_inventory() + if free_space(settings.location) < obi["n"] then + obi_output('Not enough space in %s...':format(settings.location)) + return false + end + + local elements = get_all_elements() + + for name, element in obi_names:it() do + if obi[element] then + windower.send_command('put "%s Obi" %s;':format(name, settings.location)) + if settings.notify then + obi_output('Putting %s Obi away into %s.':format(name, settings.location)) + end + coroutine.sleep(.5) + end + end + + return true +end + +-- function called on automatic events. sorts obi based on location. +function auto_sort_obi() + -- if inventory and obi bag are full at the same time, do nothing. 'cause we can't. + if inventory_full(false) and inventory_full(false, settings.location) then return false end + + if not settings.lock then -- if sorting lock is not on, then do this stuff: + if not settings.ignore_zones:contains(res.zones[windower.ffxi.get_info().zone].english) then -- Not in a city: + put_unneeded_obi(false) + get_needed_obi(false) + else -- In a city: + put_all_obi(false) + end + end + + return true +end + +---------------------------------------- +--- EVENTS +---------------------------------------- + +windower.register_event('gain buff', auto_sort_obi:cond(function(id) return id >= 178 and id <= 185 end)) +windower.register_event('lose buff', auto_sort_obi:cond(function(id) return id >= 178 and id <= 185 end)) +windower.register_event('day change', 'weather change', auto_sort_obi) +windower.register_event('addon command', function(command, ...) + command = command and command:lower() or 'help' + params = L{...}:map(string.lower) + + + if command == 'help' or command == 'h' then + obi_output("obiaway v".._addon.version..". Authors: ".._addon.author) + obi_output("//obiaway [options]") + obi_output(" help : Displays this help text.") + obi_output(" sort : Automatically sorts obi.") + obi_output(" get [ all | needed ]") + obi_output(" Gets obi from bag.") + obi_output(" put [ all | unneeded ] [ sack | satchel | case | wardrobe ]") + obi_output(" Puts obi away. Can optionally specify a location.") + obi_output(" lock [ on | off ] (%s)":format(bool_to_str(settings.lock))) + obi_output(" Locks obi to current location.") + obi_output(" notify [ on | off ] (%s)":format(bool_to_str(settings.notify))) + obi_output(" Sets obiaway notifcations on or off.") + obi_output(" location [ sack | satchel | case | wardrobe ] (%s)":format(settings.location)) + obi_output(" Sets inventory from which to get and put obi.") + elseif command == 'sort' or command == 's' then + if settings.lock then lock_obi(false) end + if settings.notify then + obi_output('Sorting obi...') + coroutine.sleep(0.5) + end + auto_sort_obi(true) + elseif command == 'get' or command == 'g' then + if params[1] == 'all' or params [1] == 'a' then + obi_output('Getting all obi from %s...':format(settings.location)) + get_all_obi(true) + coroutine.sleep(1) + elseif params[1] == 'needed' or params [1] == 'n' then + obi_output('Getting needed obi from %s...':format(settings.location)) + get_needed_obi(true) + else + error("Invalid argument. Usage: //obiaway get [ all | needed ]") + end + elseif command == 'put' or command == 'p' then + if params[1] == 'all' or params [1] == 'a' then + if S{'sack','case','satchel','wardrobe'}:contains(params[2]) then + settings.location = params[2] + obi_output("Obiaway location set to: %s":format(settings.location)) + end + obi_output('Putting all obi into %s...':format(settings.location)) + put_all_obi(true) + coroutine.sleep(1) + elseif params[1] == 'unneeded' or params[1] == 'needed' or params [1] == 'n' then + if S{'sack','case','satchel','wardrobe'}:contains(params[2]) then + settings.location = params[2] + obi_output("Obiaway location set to: %s":format(settings.location)) + else + obi_output('Putting unneeded obi into %s...':format(settings.location)) + put_unneeded_obi(true) + end + else + error("Invalid argument. Usage: //obiaway put [ all | needed ] [ sack | satchel | case | wardrobe ]") + end + elseif command == 'lock' or command == 'l' then + if params[1] == 'on' then + lock_obi(true) + elseif params[1] == 'off' then + lock_obi(false) + else + error("Invalid argument. Usage: //obiaway lock [ on | off ]") + end + elseif command == 'unlock' then + lock_obi(false) + elseif command == 'notify' or command == 'n' then + if params[1] == 'on' then + settings.notify = true + obi_output("Notifications are now on") + elseif params[1] == 'off' then + settings.notify = false + obi_output("Notifications are now off.") + else + error("Invalid argument. Usage: //obiaway notify [ on | off ]") + end + elseif command == 'location' or command == 'loc' then + if S{'sack','case','satchel','wardrobe'}:contains(params[1]) then + settings.location = params[1] + obi_output("Obiaway location set to: %s":format(settings.location)) + else + error("Invalid argument. Usage: //obiaway location [ sack | satchel | case | wardrobe ]") + end + else + error("Unrecognized command. See //obiaway help.") + end +end) |