summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/lua-addons/addons/shortcuts
diff options
context:
space:
mode:
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/shortcuts')
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/helper_functions.lua209
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/readme.md40
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/shortcuts.lua409
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/statics.lua212
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/shortcuts/targets.lua136
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