diff options
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/shortcuts')
5 files changed, 1006 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/helper_functions.lua b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/helper_functions.lua new file mode 100644 index 0000000..092f4cf --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/helper_functions.lua @@ -0,0 +1,209 @@ +--Copyright (c) 2014, 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: find_san() +--Args: +---- str (string) - string to be sanitized +----------------------------------------------------------------------------------- +--Returns: +---- sanitized string +----------------------------------------------------------------------------------- +function find_san(str) + if #str == 0 then return str end + + str = bracket_closer(str,0x28,0x29) + str = bracket_closer(str,0x5B,0x5D) + + -- strip precentages + local hanging_percent,num = 0,num + while str:byte(#str-hanging_percent) == 37 do + hanging_percent = hanging_percent + 1 + end + str = str:sub(1,#str-hanging_percent%2) + return str +end + +----------------------------------------------------------------------------------- +--Name: bracket_closer() +--Args: +---- str (string) - string to have its brackets closed +---- opener (number) - opening character's ASCII code +---- closer (number) - closing character's ASCII code +----------------------------------------------------------------------------------- +--Returns: +---- string with its opened brackets closed +----------------------------------------------------------------------------------- +function bracket_closer(str,opener,closer) + op,cl,opadd = 0,0,1 + for i=1,#str do + local ch = str:byte(i) + if ch == opener then + op = op +1 + opadd = i + elseif ch == closer then + cl = cl + 1 + end + end + if op > cl then + if opadd ~= #str then + str = str..string.char(closer) + else + str = str..str.char(0x7,closer) + end -- Close captures + end + return str +end + +----------------------------------------------------------------------------------- +--Name: strip() +--Args: +---- name (string): Name to be slugged +----------------------------------------------------------------------------------- +--Returns: +---- string with a gsubbed version of name that removes non-alphanumeric characters, +-------- forces the string to lower-case, and converts numbers to Roman numerals, +-------- which are upper case. +----------------------------------------------------------------------------------- +function strip(name) + return name:gsub('[^%w]',''):lower():gsub('(%d+)',to_roman) +end + + +----------------------------------------------------------------------------------- +--Name: to_roman() +--Args: +---- num (string or number): Number to be converted from Arabic to Roman numerals. +----------------------------------------------------------------------------------- +--Returns: +---- roman numerals that represent the passed number. +-------- This function returns ix for 9 instead of viiii. They are both valid, but +-------- FFXI uses the ix nomenclature so we will use that. +----------------------------------------------------------------------------------- +function to_roman(num) + if type(num) ~= 'number' then + num = tonumber(num) + if num == nil then + print('Debug to_roman') + return '' + end + end + if num>4599 then return tostring(num) end + + local retstr = '' + + if num == 0 then return 'zilch' end + if num == 1 then return '' end + + while num > 0 do + if num >= 1000 then + num = num - 1000 + retstr = retstr..'m' + elseif num >= 900 then + num = num - 900 + retstr = retstr..'cm' + elseif num >= 500 then + num = num - 500 + retstr = retstr..'d' + elseif num >= 400 then + num = num - 400 + retstr = retstr..'cd' + elseif num >= 100 then + num = num - 100 + retstr = retstr..'c' + elseif num >= 90 then + num = num - 90 + retstr = retstr..'xc' + elseif num >= 50 then + num = num - 50 + retstr = retstr..'l' + elseif num >= 40 then + num = num - 40 + retstr = retstr..'xl' + elseif num >= 10 then + num = num - 10 + retstr = retstr..'x' + elseif num == 9 then + num = num - 9 + retstr = retstr..'ix' + elseif num >= 5 then + num = num - 5 + retstr = retstr..'v' + elseif num == 4 then + num = num - 4 + retstr = retstr..'iv' + elseif num >= 1 then + num = num - 1 + retstr = retstr..'i' + end + end + + return retstr +end + + +----------------------------------------------------------------------------------- +--Name: check_usability() +--Args: +---- player (table): get_player() table +---- resource (string): name of the resource to be examined +---- id (num): ID of the spell/ability of interest +----------------------------------------------------------------------------------- +--Returns: +---- boolean : true indicates that the spell/ability is known to you and false indicates that it is not. +----------------------------------------------------------------------------------- +function check_usability(player,resource,id) + if resource == 'spells' and ( (res.spells[id].levels[player.main_job_id] and res.spells[id].levels[player.main_job_id] <= player.main_job_level) or + (res.spells[id].levels[player.sub_job_id] and res.spells[id].levels[player.sub_job_id] <= player.sub_job_level) ) then -- Should check to see if you know the spell + return true + elseif L(windower.ffxi.get_abilities()[resource] or {}):contains(id) then + return true + elseif resource == 'monster_skills' and player.main_job_id == 23 and (res.monstrosity[windower.ffxi.get_mjob_data().species].tp_moves[id] or 0) <= player.main_job_level then + return true + elseif resource == 'mounts' and math.floor((windower.packets.last_incoming(0x0AE):byte(math.floor(id/8)+5)%2^(id%8+1))/2^(id%8)) == 1 then + return true + end +end + + +----------------------------------------------------------------------------------- +--Name: debug_chat() +--Args: +---- str (string): string to be printed to the log +----------------------------------------------------------------------------------- +--Returns: +---- None +----------------------------------------------------------------------------------- +function debug_chat(str) + if not debugging then return end + + if tostring(str) then + windower.add_to_chat(8,str) + else + error('Debug chat is not a string',2) + end +end
\ No newline at end of file diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/readme.md b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/readme.md new file mode 100644 index 0000000..9d1dc0e --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/readme.md @@ -0,0 +1,40 @@ +#Shortcuts v2.9 +####written by Byrth + +Completes and properly formats commands (prefixed by at least one '/'), +including emotes and checks. This addon is part of a larger "replacing +spellcast" project, and represents the interpretation part of spellcast. + +####Commands/Settings: +None + +####Changelog: +v2.9 - 03/05/16 - Adjusted default target logic when the target is a Trust. +v2.8 - 11/27/15 - Changed how the non-action commands are handled (including secondary arguments). +v2.7 - 11/15/14 - Totally gutted and reworked the ambiguous spell handling system. It is much simpler now. +v2.6 - 9/27/14 - Fixed an error with absolute remapping of ambiguous spells +v2.5 - 8/30/14 - Expanded shortcuts to include single-slash commands. Expanded command prefixes to include double-slash commands. +v2.4 - 8/30/14 - Changed ambiguous case handling to respect specified prefixes. +v2.3 - 6/20/14 - Added new monstrosity skill disambiguation. +v2.2 - 5/19/14 - Accommodated new Resources changes. +v2.1 - 4/18/14 - Added custom aliases to Shortcuts. +v2.0 - 4/ 7/14 - Made Shortcuts calculate available spells/abilities in the case of ambiguous abilities. Moving towards an automated ambiguity-handling framework. +v1.9 - 3/20/14 - Changed Shortcuts over to use the resources library. +v1.8 - 1/ 6/14 - Fix target selection. Interpret the outgoing text as an ability first (instead of combination of ability and target). +v1.7 - 12/31/13 - Fixed st targets for shortcuts. +v1.6 - 12/12/13 - Fixed "target is nil" bug. +v1.5 - 12/7/13 - Various bugfixes and more complete support for pattern matching. +v1.4 - 11/17/13 - Improved ambiguous name handling using windower.ffxi.get_abilities(). +v1.3 - 11/15/13 - Added handling for "Corpse" spells and fixed a minor glitch in target creation. +v1.2 - 11/13/13 - Added abiguous case handling for amorph/bird spells. +v1.1 - 10/23/13 - Reduced "spell1" to "spell" and made some minor adjustments. +v1.0 - 10/16/13 - Fixed some ambiguous name processing issues with monsterskills. +v0.9 - 10/1/13 - Fixed targets.lua's interpretation of the target flags. +v0.8 - 08/14/13 - Fixed split(), which was causing errors when assembling resources. +v0.7 - 08/12/13 - Improved the hashing algorithm (better roman numeral conversion) and improved target creation again. Updated documentation. +v0.6 - 08/11/13 - Fixed autotranslate, eliminated valid commands that aren't prefixed by '/', and made target creation smarter. +v0.5 - 08/05/13 - Added handling for <st..> commands and support for target shorthands (t for <t>, stpc for <stpc>, etc.) +v0.4 - 07/25/13 - Added handling for Monstrosity. Added logging. Modified target handling. +v0.3 - 07/14/13 - Fixed an ambiguous_names error. Retooled some parts in anticipation of the new hook and Monstrosity. Added infinite loop detection. +v0.2 - 06/19/13 - Fixed the addon's capacity for infinite loops. Added safe auto-completion for party commands. Added target commands. +v0.1 - 06/15/13 - Created Addon
\ No newline at end of file diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/shortcuts.lua b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/shortcuts.lua new file mode 100644 index 0000000..a2f5da5 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/shortcuts.lua @@ -0,0 +1,409 @@ +--Copyright (c) 2014, 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. + +_addon.version = '2.903' +_addon.name = 'Shortcuts' +_addon.author = 'Byrth' +_addon.commands = {'shortcuts'} + + +debugging = false +logging = false +if windower.dir_exists('../addons/shortcuts/data/') and logging then + logfile = io.open('../addons/shortcuts/data/NormalLog'..tostring(os.clock())..'.log','w+') + logfile:write('\n\n','SHORTCUTS LOGGER HEADER: ',tostring(os.clock()),'\n') + logfile:flush() +end + +if windower.file_exists(windower.addon_path..'resources.lua') then + local result = os.remove(windower.addon_path..'resources.lua') + if not result then + os.rename(windower.addon_path..'resources.lua',windower.addon_path..'unnecessary.lua') + end +end + +if not windower.dir_exists(windower.addon_path..'data') then + windower.create_dir(windower.addon_path..'data') +end + +require 'sets' +require 'lists' +require 'helper_functions' +require 'tables' +require 'strings' +res = require 'resources' +config = require 'config' + +default_aliases = { + c1="Cure", + c2="Cure II", + c3="Cure III", + c4="Cure IV", + c5="Cure V", + c6="Cure VI", + r1="Raise", + r2="Raise II", + r3="Raise III", + pro1="Protectra", + pro2="Protectra II", + pro3="Protectra III", + pro4="Protectra IV", + pro5="Protectra V", + sh1="Shellra", + sh2="Shellra II", + sh3="Shellra III", + sh4="Shellra IV", + sh5="Shellra V", + she1="Shellra", + she2="Shellra II", + she3="Shellra III", + she4="Shellra IV", + she5="Shellra V", + bl="Blink", + ss="Stoneskin", + re1="Regen", + re2="Regen II", + re3="Regen III", + re4="Regen IV", + re5="Regen V", + holla="Teleport-Holla", + dem="Teleport-Dem", + mea="Teleport-Mea", + yhoat="Teleport-Yhoat", + altep="Teleport-Altep", + vahzl="Teleport-Vahzl", + jugner="Recall-Jugner", + pashh="Recall-Pashh", + meri="Recall-Meriph", + pash="Recall-Pashh", + meriph="Recall-Meriph", + ichi="Utsusemi: Ichi", + ni="Utsusemi: Ni", + utsu1="Utsusemi: Ichi", + utsu2="Utsusemi: Ni", + ds="Divine Seal", + es="Elemental Seal", + la="Light Arts", + da="Dark Arts", + pen="Penury", + cel="Celerity", + cw1="Curing Waltz", + cw2="Curing Waltz II", + cw3="Curing Waltz III", + cw4="Curing Waltz IV", + cw5="Curing Waltz V", + hw="Healing Waltz" +} + +aliases = config.load('data\\aliases.xml',default_aliases) +config.save(aliases) +setmetatable(aliases,nil) + + +require 'statics' +require 'targets' + +----------------------------------------------------------------------------------- +--Name: event_load() +--Args: +---- None +----------------------------------------------------------------------------------- +--Returns: +---- None, simply a routine that runs once at the load (after the entire document +---- is loaded and treated as a script) +----------------------------------------------------------------------------------- +windower.register_event('load',function() + lastsent = '' +end) + +----------------------------------------------------------------------------------- +--Name: event_unload() +--Args: +---- None +----------------------------------------------------------------------------------- +--Returns: +---- None, simply a routine that runs once at unload. +----------------------------------------------------------------------------------- +windower.register_event('unload',function() + if logging then logfile:close() end +end) + + +----------------------------------------------------------------------------------- +--Name: event_outgoing_text() +--Args: +---- original (string): Original command entered by the player +---- modified (string): Modified command with changes upstream of the addon +----------------------------------------------------------------------------------- +--Returns: +---- string, changed command +----------------------------------------------------------------------------------- +windower.register_event('outgoing text',function(original,modified) + local temp_org = windower.convert_auto_trans(modified) + if modified:sub(1,1) ~= '/' then return modified end + debug_chat('outgoing_text: '..modified..' '..tostring(windower.ffxi.get_mob_by_target('st'))) + temp_org = temp_org:gsub(' <wait %d+>',''):sub(2) + + if logging then + logfile:write('\n\n',tostring(os.clock()),'temp_org: ',temp_org,'\nModified: ',modified) + logfile:flush() + end + + -- If it's the command that was just sent, blank lastsent and pass it through with only the changes applied by other addons + if modified == lastsent then + lastsent = '' + return modified + end + + -- Otherwise, dump the inputs into command_logic() + return command_logic(temp_org,modified) +end) + +----------------------------------------------------------------------------------- +--Name: event_unhandled_command() +--Args: +---- table of strings: //entries split on ' ' +----------------------------------------------------------------------------------- +--Returns: +---- None, but can generate text output through command_logic() +----------------------------------------------------------------------------------- +windower.register_event('unhandled command',function(...) + local combined = windower.convert_auto_trans(table.concat({...},' ')) -- concat it back together... + local cmd,bool = command_logic(combined,combined) -- and then dump it into command_logic() + if cmd and bool and cmd ~= '' then + if cmd:sub(1,1) ~= '/' then cmd = '/'..cmd end + windower.send_command('@input '..cmd) + end +end) + + +----------------------------------------------------------------------------------- +--Name: command_logic(original,modified) +--Args: +---- original (string): Full line entry from event unhandled command/outgoing text +---- modified (string): Modified line if from event_outgoing_text, otherwise the +---- same as original +----------------------------------------------------------------------------------- +--Returns: +---- string (sometimes '') depending what the logic says to do. +----------------------------------------------------------------------------------- +function command_logic(original,modified) + local splitline = alias_replace(string.split(original,' '):filter(-'')) + local command = splitline[1] -- Treat the first word as a command. + local potential_targ = '/nope//' + if splitline.n ~= 1 then + potential_targ = splitline[splitline.n] + end + local a,b,spell = string.find(original,'"(.-)"') + + if unhandled_list[command] then + return modified,true + end + + if spell then + spell = spell:lower() + elseif splitline.n == 3 then + if valid_target(potential_targ) then + spell = splitline[2] + else + spell = splitline[2]..' '..splitline[3] + end + end + + if targ_reps[potential_targ] then + potential_targ = targ_reps[potential_targ] + end + + if ignore_list[command] then -- If the command is legitimate and on the blacklist, return it unaltered. + lastsent = '' + return modified,true + elseif command2_list[command] and not valid_target(potential_targ,true) then + -- If the command is legitimate and requires target completion but not ability interpretation + + if not command2_list[command].args then -- If there are not any secondary commands + local temptarg = valid_target(potential_targ) or target_make(command2_list[command]) -- Complete the target or make one. + if temptarg ~= '<me>' then -- These commands, like emotes, check, etc., don't need to default to <me> + lastsent = '/'..command..' '..temptarg -- Push the command and target together and send it out. + else + lastsent = '/'..command + end + + debug_chat('258: input '..lastsent) + if logging then + logfile:write('\n\n',tostring(os.clock()),'Original: ',original,'\n(162) ',lastsent) + logfile:flush() + end + windower.send_command('@input '..lastsent) + return '',false + else -- If there are secondary commands (like /pcmd add <name>) + local tempcmd = command + local passback + local targs = command2_list[command] + for _,v in ipairs(splitline) do -- Iterate over the potential secondary arguments. + if command2_list[command]['args'] and command2_list[command]['args'][v] then + tempcmd = tempcmd..' '..v + passback = v + targs = command2_list[command]['args'][v] + break + end + end + local temptarg = '' + if targs ~= true then + -- Target is required + if command == potential_targ or passback and passback == potential_targ or potential_targ == '/nope//' then + -- No target is provided + temptarg = target_make(targs) + else + -- A target is provided, which is either corrected or (if not possible) used raw + temptarg = valid_target(potential_targ) or potential_targ + end + end + lastsent = '/'..tempcmd..' '..temptarg + debug_chat('292: input '..lastsent) + if logging then + logfile:write('\n\n',tostring(os.clock()),'Original: ',original,'\n(193) ',lastsent) + logfile:flush() + end + windower.send_command('@input '..lastsent) + return '',false + end + elseif command2_list[command] then + -- If the submitted command does not require ability interpretation and is fine already, send it out. + lastsent = '' + if logging then + logfile:write('\n\n',tostring(os.clock()),'Original: ',original,'\n(146) Legitimate command') + logfile:flush() + end + return modified,true + elseif command_list[command] then + -- If there is a valid command, then pass the text with an offset of 1 to the text interpretation function + return interp_text(splitline,1,modified) + else + -- If there is not a valid command, then pass the text with an offset of 0 to the text interpretation function + return interp_text(splitline,0,modified) + end +end + + +----------------------------------------------------------------------------------- +--Name: alias_replace(tab) +--Args: +---- tab (table of strings): splitline +----------------------------------------------------------------------------------- +--Returns: +---- tab (table of strings): with all the aliased values replaced +----------------------------------------------------------------------------------- +function alias_replace(tab) + for ind,key in ipairs(tab) do + if aliases[key:lower()] then + tab[ind] = aliases[key:lower()] + end + end + return tab +end + + +----------------------------------------------------------------------------------- +--Name: interp_text() +--Args: +---- splitline (table of strings): entire entry, split on spaces. +---- original (string): Full line entry from event unhandled command/outgoing text +---- modified (string): Modified line if from event_outgoing_text, otherwise the +---- same as original +----------------------------------------------------------------------------------- +--Returns: +---- string (sometimes '') depending what the logic says to do. +---- Sends a command if the command needs to be changed. +----------------------------------------------------------------------------------- +function interp_text(splitline,offset,modified) + local temptarg,abil + local no_targ_abil = strip(table.concat(splitline,' ',1+offset,splitline.n)) + + if validabils[no_targ_abil] then + abil = no_targ_abil + elseif splitline.n > 1 then + temptarg = valid_target(targ_reps[splitline[splitline.n]] or splitline[splitline.n]) + end + + if temptarg then abil = _raw.table.concat(splitline,' ',1+offset,splitline.n-1) + elseif not abil then abil = _raw.table.concat(splitline,' ',1+offset,splitline.n) end + + local strippedabil = strip(abil) -- Slug the ability + + if validabils[strippedabil] then + local options,nonoptions,num_opts, r_line = {},{},0 + local player = windower.ffxi.get_player() + for v in validabils[strippedabil]:it() do + if check_usability(player,v.res,v.id) then + options[v.res] = v.id + num_opts = num_opts + 1 + elseif v.res ~= nil then + nonoptions[v.res] = v.id + end + end + if num_opts > 0 then + -- If there are usable options then prioritize: + -- Prefix, if given -> Spells -> Job Abilities -> Weapon Skills -> Monster Skills + r_line = res[(offset == 1 and options[command_list[splitline[1]]] and command_list[splitline[1]]) or (options.spells and 'spells') or (options.job_abilities and 'job_abilities') or (options.weapon_skills and 'weapon_skills') or (options.monster_skills and 'monster_skills') or (options.mounts and 'mounts')][options[command_list[splitline[1]]] or options.spells or options.job_abilities or options.weapon_skills or options.monster_skills or options.mounts] + elseif num_opts == 0 then + r_line = res[(offset == 1 and nonoptions[command_list[splitline[1]]] and command_list[splitline[1]]) or (nonoptions.spells and 'spells') or (nonoptions.weapon_skills and 'weapon_skills') or (nonoptions.job_abilities and 'job_abilities') or (nonoptions.monster_skills and 'monster_skills') or (nonoptions.mounts and 'mounts')][nonoptions[command_list[splitline[1]]] or nonoptions.spells or nonoptions.weapon_skills or nonoptions.job_abilities or nonoptions.monster_skills or nonoptions.mounts] + end + + local targets = table.reassign({},r_line.targets) + + -- Handling for abilities that change potential targets. + if r_line.skill == 40 and r_line.cast_time == 8 and L(player.buffs):contains(409) then + targets.Party = true -- Pianissimo changes the target list of + elseif r_line.skill == 44 and r_line.en:find('Indi-') and L(player.buffs):contains(584) then + targets.Party = true -- Indi- spells can be cast on others when Entrust is up + end + + local abil_name = r_line.english -- Remove spaces at the end of the ability name. + while abil_name:sub(-1) == ' ' do + abil_name = abil_name:sub(1,-2) + end + + local out_tab = {prefix = in_game_res_commands[r_line.prefix:gsub("/","")], name = abil_name, target = temptarg or target_make(targets)} + if not out_tab.prefix then print('Could not find prefix',r_line.prefix) end + lastsent = out_tab.prefix..' "'..out_tab.name..'" '..out_tab.target + if logging then + logfile:write('\n\n',tostring(os.clock()),'Original: ',table.concat(splitline,' '),'\n(180) ',lastsent) + logfile:flush() + end + debug_chat('390 comp '..lastsent:sub(2):gsub('"([^ ]+)"', '%1'):lower()..' || '..table.concat(splitline,' ',1,splitline.n):gsub('"([^ ]+)"', '%1'):lower()) + if offset == 1 and in_game_res_commands[splitline[1]] and in_game_res_commands[splitline[1]] == out_tab.prefix and + ('"'..out_tab.name..'" '..out_tab.target):gsub('"([^ ]+)"', '%1'):lower() == table.concat(splitline,' ',2,splitline.n):gsub('"([^ ]+)"', '%1'):lower() then + debug_chat('400 return '..lastsent) + return lastsent,true + else + debug_chat('403 input '..lastsent) + windower.send_command('@input '..lastsent) + return '',false + end + end + lastsent = '' + return modified,false +end
\ No newline at end of file diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/statics.lua b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/statics.lua new file mode 100644 index 0000000..4fd1219 --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/statics.lua @@ -0,0 +1,212 @@ +--Copyright (c) 2014, 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 andor 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. + +-- Convert the spells and job abilities into a referenceable list of aliases -- +validabils = {} + +--f = io.open('..addonsprdata'..tostring(os.clock())..'.log','w+') +--for i,v in pairs(validabils) do +-- f:write(tostring(i)..' '..tostring(v)..'\n') +--end + +-- Constants used in the rest of the addon. + +-- List of valid prefixes to be interpreted with the resources. The values currently have no use. +command_list = {['ja']='job_abilities',['jobability']='job_abilities',['so']='spells',['song']='spells',['ma']='spells',['magic']='spells',['nin']='spells',['ninjutsu']='spells', + ['ra']='Ranged Attack',['range']='Ranged Attack',['throw']='Ranged Attack',['shoot']='Ranged Attack',['monsterskill']='monster_skills',['ms']='monster_skills', + ['ws']='weapon_skills',['weaponskill']='weapon_skills',['item']='Ability',['pet']='job_abilities',['mo']='mounts',['mount']='mounts'} + +in_game_res_commands = {['ja']='/ja',['jobability']='/ja',['pet']='/ja', + ['so']='/ma',['song']='/ma',['ma']='/ma',['magic']='/ma',['nin']='/ma',['ninjutsu']='/ma', + ['monsterskill']='/ms',['ms']='/ms',['ws']='/ws',['weaponskill']='/ws', + ['ra']='/ra',['range']='/ra',['throw']='/ra',['shoot']='/ra',['mount']='/mo',['mo']='/mo'} + +-- List of other commands that might use name completion. +local No_targets = {['Player']=false,['Enemy']=false,['Party']=false,['Ally']=false,['NPC']=false,['Self']=false,['Corpse']=false} +local All_targets = {['Player']=true,['Enemy']=true,['Party']=true,['Ally']=true,['NPC']=true,['Self']=true,['Corpse']=true} +local PC_targets = {['Player']=true,['Enemy']=false,['Party']=true,['Ally']=true,['NPC']=false,['Self']=true,['Corpse']=true} +local Party_targets = {['Player']=false,['Enemy']=false,['Party']=true,['Ally']=false,['NPC']=false,['Self']=true,['Corpse']=true} +local Alliance_targets = {['Player']=false,['Enemy']=false,['Party']=false,['Ally']=true,['NPC']=false,['Self']=true,['Corpse']=true} +local BST_targets = {['Player']=true,['Enemy']=false,['Party']=false,['Ally']=false,['NPC']=false,['Self']=true,['Corpse']=false} + +local function new_cmd_entry(default_targets,subcommands) + local rettab = table.reassign({},default_targets) + if subcommands then + rettab.args = subcommands + end + return rettab +end + +local emote_table = new_cmd_entry(All_targets,{motion=true}) + +command2_list = { + --['kick']=true, --Is this actually a command? + ['assist']=All_targets, + ['alliancecmd']=new_cmd_entry(No_targets,{ + kick=Alliance_targets, + add=PC_targets, + leader=Alliance_targets, + breakup=true, + leave=true, + looter=Alliance_targets}), + ['partycmd']=new_cmd_entry(No_targets,{ + kick=Party_targets, + add=PC_targets, + leader=Party_targets, + breakup=true, + leave=true, + looter=Party_targets}), + ['acmd']=new_cmd_entry(No_targets,{ + kick=Alliance_targets, + add=PC_targets, + leader=Alliance_targets, + breakup=true, + leave=true, + looter=Alliance_targets}), + ['pcmd']=new_cmd_entry(No_targets,{ + kick=Party_targets, + add=PC_targets, + leader=Party_targets, + breakup=true, + leave=true, + looter=Party_targets}), + ['wave']=emote_table, + ['poke']=emote_table, + ['dance']=emote_table, + ['dance1']=emote_table, + ['dance2']=emote_table, + ['dance3']=emote_table, + ['dance4']=emote_table, + ['amazed']=emote_table, + ['angry']=emote_table, + ['bell']=emote_table, + ['bellsw']=emote_table, + ['blush']=emote_table, + ['bow']=emote_table, + ['cheer']=emote_table, + ['clap']=emote_table, + ['comfort']=emote_table, + ['cry']=emote_table, + ['disgusted']=emote_table, + ['doze']=emote_table, + ['doubt']=emote_table, + ['huh']=emote_table, + ['farewell']=emote_table, + ['goodbye']=emote_table, + ['fume']=emote_table, + ['grin']=emote_table, + ['hurray']=emote_table, + ['joy']=emote_table, + ['kneel']=emote_table, + ['laugh']=emote_table, + ['muted']=emote_table, + ['kneel']=emote_table, + ['laugh']=emote_table, + ['no']=emote_table, + ['nod']=emote_table, + ['yes']=emote_table, + ['panic']=emote_table, + ['point']=emote_table, + ['praise']=emote_table, + ['psych']=emote_table, + ['salute']=emote_table, + ['shocked']=emote_table, + ['sigh']=emote_table, + ['sit']=emote_table, + ['slap']=emote_table, + ['smile']=emote_table, + ['stagger']=emote_table, + ['stare']=emote_table, + ['sulk']=emote_table, + ['surprised']=emote_table, + ['think']=emote_table, + ['toss']=emote_table, + ['upset']=emote_table, + ['welcome']=emote_table, + ['check']=new_cmd_entry(PC_targets), + ['c']=new_cmd_entry(PC_targets), + ['checkparam']=new_cmd_entry({['Player']=false,['Enemy']=false,['Party']=false,['Ally']=false,['NPC']=false,['Self']=true,['Corpse']=false},{}), -- Blank table forces it into the second processing stream, which lets it default to <me> + ['target']=new_cmd_entry(PC_targets), + ['ta']=new_cmd_entry(PC_targets), + ['ra']=new_cmd_entry({['Player']=false,['Enemy']=true,['Party']=false,['Ally']=false,['NPC']=false,['Self']=false,['Corpse']=false}), + ['follow']=new_cmd_entry(All_targets), + ['recruit']=new_cmd_entry(PC_targets), + ['rec']=new_cmd_entry(PC_targets), + ['retr']=new_cmd_entry(Party_targets,{all=No_targets}), + ['returntrust']=new_cmd_entry(Party_targets,{all=No_targets}), + ['refa']=new_cmd_entry(Party_targets,{all=No_targets}), + ['returnfaith']=new_cmd_entry(Party_targets,{all=No_targets}), + ['bstpet']=new_cmd_entry(No_targets,{['1']=BST_targets,['2']=BST_targets,['3']=BST_targets,['4']=BST_targets,['5']=BST_targets,['6']=BST_targets,['7']=BST_targets}), + } + +unhandled_list = {['p']=true,['s']=true,['sh']=true,['yell']=true,['echo']=true,['t']=true,['l']=true,['breaklinkshell']=true} + +-- List of commands to be ignored +ignore_list = {['equip']=true,['raw']=true,['fish']=true,['dig']=true,['range']=true,['map']=true,['hide']=true,['jump']=true,['attackoff']=true,['quest']=true,['recruitlist']=true,['rlist']=true,['statustimer']=true} + +-- Targets to ignore and just pass through +pass_through_targs = T{'<t>','<me>','<ft>','<scan>','<bt>','<lastst>','<r>','<pet>','<p0>','<p1>','<p2>','<p3>','<p4>', + '<p5>','<a10>','<a11>','<a12>','<a13>','<a14>','<a15>','<a20>','<a21>','<a22>','<a23>','<a24>','<a25>','<focust>'} + +st_targs = T{'<st>','<stpc>','<stal>','<stnpc>','<stpt>'} + +targ_reps = {t='<t>',me='<me>',ft='<ft>',scan='<scan>',bt='<bt>',lastst='<lastst>',r='<r>',pet='<pet>',p0='<p0>',p1='<p1>',p2='<p2>',p3='<p3>',p4='<p4>', + p5='<p5>',a10='<a10>',a11='<a11>',a12='<a12>',a13='<a13>',a14='<a14>',a15='<a15>',a20='<a20>',a21='<a21>',a22='<a22>',a23='<a23>',a24='<a24>',a25='<a25>', + st='<st>',stpc='<stpc>',stal='<stal>',stnpc='<stnpc>',stpt='<stpt>',focust='<focust>'} + +language = 'english' -- windower.ffxi.get_info()['language']:lower() + + +----------------------------------------------------------------------------------- +--Name: make_abil() +--Args: +---- ind (string): stripped ability name +---- t (string): type of ability (Magic or Ability) +---- i (number): index id +----------------------------------------------------------------------------------- +--Returns: +---- Nothing, adds a new line to validabils or modifies it. +----------------------------------------------------------------------------------- +function make_abil(ind,res,id) + validabils[ind] = validabils[ind] or L{} + validabils[ind]:append({res=res,id=id}) +end + +-- Iterate through resources and make validabils. +function validabils_it(resource) + for id,v in pairs(res[resource]) do + if (not v.monster_level and v.prefix) or (v.monster_level and v.monster_level ~= -1 and v.ja:sub(1,1) ~= '#' ) then + -- Monster Abilities contains a large number of player-usable moves (but not monstrosity-usable). This excludes them. + make_abil(strip(v.english),resource,id) + end + end +end + +validabils_it('spells') +validabils_it('job_abilities') +validabils_it('weapon_skills') +validabils_it('monster_skills') +validabils_it('mounts')
\ No newline at end of file diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/targets.lua b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/targets.lua new file mode 100644 index 0000000..25ecabb --- /dev/null +++ b/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/targets.lua @@ -0,0 +1,136 @@ +--Copyright (c) 2014, 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. + + +-- Target Processing -- + +----------------------------------------------------------------------------------- +--Name: valid_target(targ,flag) +--Args: +---- targ (string): The proposed target +---- flag (boolean): sets a more stringent criteria for target. It has to be a +---- match to a player in the mob_array. +----------------------------------------------------------------------------------- +--Returns: +---- A string or false. +----------------------------------------------------------------------------------- +function valid_target(targ,flag) + local spell_targ + local san_targ = find_san(strip(targ)) + -- If the target is whitelisted, pass it through. + if pass_through_targs:contains(targ:lower()) or st_targs:contains(targ:lower()) or (tonumber(targ:lower()) and windower.ffxi.get_mob_by_id(tonumber(targ:lower()))) then + return targ:lower() + elseif targ and windower.ffxi.get_player() then + -- If the target exists, scan the mob array for it + local current_target = windower.ffxi.get_mob_by_target('t') + local targar = {} + for i,v in pairs(windower.ffxi.get_mob_array()) do + if string.find(strip(v.name),san_targ) and (v.valid_target or v.id == windower.ffxi.get_player().id) then -- Malformed pattern somehow + -- Handling for whether it's a monster or not + if v.is_npc and v.spawn_type ~= 14 and current_target then + if v.id == current_target.id then + targar['<t>'] = math.sqrt(v.distance) + end + elseif not v.is_npc or (v.spawn_type == 14 and v.in_party) then + targar[v.name] = math.sqrt(v.distance) + end + end + end + + -- If flag is set, push out the target only if it is in the targ array. + if targar[targ] then + spell_targ = targ + elseif flag then + spell_targ = false + else + -- If targ starts an element of the monster array, use it. + local priority = 50 + for i,v in pairs(targar) do + if (i:lower()==san_targ:lower()) then + v = 0 + elseif i:lower():find('^'..san_targ:lower()) then + v = v/50 + end + if v < priority then -- Use the highest priority match, with a default priority hierarchy based on distance + priority = v + spell_targ = i + end + end + end + end + return spell_targ +end + + +----------------------------------------------------------------------------------- +--Name: target_make(targarr) +--Args: +---- targets (table of booleans): Keyed to potential targets +----------------------------------------------------------------------------------- +--Returns: +---- Created valid target, defaulting to '<me>' +----------------------------------------------------------------------------------- +function target_make(targets) + local target = windower.ffxi.get_mob_by_target('<t>') + local target_type = '' + if not target then + -- If target doesn't exist, leave it set to ''. This will shortcircuit the + -- rest of the processing and just return <me>. + elseif target.hpp == 0 then + target_type = 'Corpse' + elseif target.is_npc and target.spawn_type ~= 14 then + target_type = 'Enemy' + -- Need to add handling that differentiates 'Enemy' and 'NPC' here. + else + target_type = 'Ally' + local party = windower.ffxi.get_party() + for i,v in pairs(party) do + if type(v) == 'table' and v.name == target.name then + if i:sub(1,1) == 'p' then + if i:sub(1,2) == 'p0' then + target_type = 'Self' + else + target_type = 'Party' + end + end + if target.charmed and not target.is_npc then + target_type = 'Enemy' + end + break + end + end + end + + if targets[target_type] and target_type ~= 'Self' then + return '<t>' + elseif targets.Self then + return '<me>' + elseif targets.Self or targets.Party or targets.Enemy or targets.NPC or targets.Ally or targets.Corpse then + return '<t>' + else + return '' + end +end
\ No newline at end of file |