summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1
diff options
context:
space:
mode:
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1')
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Modes.lua358
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Globals.lua112
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Include.lua1125
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Mappings.lua273
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-SelfCommands.lua715
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-TreasureHunter.lua297
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Utility.lua672
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-documentation.txt1
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/README.md6
9 files changed, 3559 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Modes.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Modes.lua
new file mode 100644
index 0000000..35036b3
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Modes.lua
@@ -0,0 +1,358 @@
+-------------------------------------------------------------------------------------------------------------------
+-- This include library allows use of specially-designed tables for tracking
+-- certain types of modes and state.
+--
+-- Usage: include('Modes.lua')
+--
+-- Construction syntax:
+--
+-- 1) Create a new list of mode values. The first value will always be the default.
+-- MeleeMode = M{'Normal', 'Acc', 'Att'} -- Construct as a basic table, using braces.
+-- MeleeMode = M('Normal', 'Acc', 'Att') -- Pass in a list of strings, using parentheses.
+--
+-- Optional: If constructed as a basic table, and the table contains a key value
+-- of 'description', that description will be saved for future reference.
+-- If a simple list of strings is passed in, no description will be set.
+--
+-- 2) Create a boolean mode with a specified default value (note parentheses):
+-- UseLuzafRing = M(true)
+-- UseLuzafRing = M(false)
+--
+-- Optional: A string may be provided that will be used as the mode description:
+-- UseLuzafRing = M(false, 'description')
+-- UseLuzafRing = M(true, 'description')
+--
+--
+-- Public information fields (all are case-insensitive):
+--
+-- 1) m.description -- Get a text description of the mode table, if it's been set.
+-- 2) m.current or m.value -- Gets the current mode value. Booleans will return the strings "on" or "off".
+-- 3) m.index -- Gets the current index value, or true/false for booleans.
+--
+--
+-- Public class functions:
+--
+-- 1) m:describe(str) -- Sets the description for the mode table to the provided string value.
+-- 2) m:options(options) -- Redefine the options for a list mode table. Cannot be used on a boolean table.
+--
+--
+-- Public mode manipulation functions:
+--
+-- 1) m:cycle() -- Cycles through the list going forwards. Acts as a toggle on boolean mode vars.
+-- 2) m:cycleback() -- Cycles through the list going backwards. Acts as a toggle on boolean mode vars.
+-- 3) m:toggle() -- Toggles a boolean Mode between true and false.
+-- 4) m:set(n) -- Sets the current mode value to n.
+-- Note: If m is boolean, n can be boolean true/false, or string of on/off/true/false.
+-- Note: If m is boolean and no n is given, this forces m to true.
+-- 5) m:unset() -- Sets a boolean mode var to false.
+-- 6) m:reset() -- Returns the mode var to its default state.
+-- 7) m:default() -- Same as reset()
+--
+-- All public functions return the current value after completion.
+--
+--
+-- Example usage:
+--
+-- sets.MeleeMode.Normal = {}
+-- sets.MeleeMode.Acc = {}
+-- sets.MeleeMode.Att = {}
+--
+-- MeleeMode = M{'Normal', 'Acc', 'Att', ['description']='Melee Mode'}
+-- MeleeMode:cycle()
+-- equip(sets.engaged[MeleeMode.current])
+-- MeleeMode:options('Normal', 'LowAcc', 'HighAcc')
+-- >> Changes the option list, but the description stays 'Melee Mode'
+--
+--
+-- sets.LuzafRing.on = {ring2="Luzaf's Ring"}
+-- sets.LuzafRing.off = {}
+--
+-- UseLuzafRing = M(false)
+-- UseLuzafRing:toggle()
+-- equip(sets.precast['Phantom Roll'], sets.LuzafRing[UseLuzafRing.value])
+-------------------------------------------------------------------------------------------------------------------
+
+
+_meta = _meta or {}
+_meta.M = {}
+_meta.M.__class = 'mode'
+_meta.M.__methods = {}
+
+
+-- Default constructor for mode tables
+-- M{'a', 'b', etc, ['description']='a'} -- defines a mode list, description 'a'
+-- M('a', 'b', etc) -- defines a mode list, no description
+-- M('a') -- defines a mode list, default 'a'
+-- M{['description']='a'} -- defines a mode list, default 'Normal', description 'a'
+-- M{} -- defines a mode list, default 'Normal', no description
+-- M(false) -- defines a mode boolean, default false, no description
+-- M(true) -- defines a mode boolean, default true, no description
+-- M(false, 'a') -- defines a mode boolean, default false, description 'a'
+-- M(true, 'a') -- defines a mode boolean, default true, description 'a'
+-- M() -- defines a mode boolean, default false, no description
+function M(t, ...)
+ local m = {}
+ m._track = {}
+
+ -- If we're passed a list of strings, convert them to a table
+ local args = {...}
+ if type(t) == 'string' then
+ t = {[1] = t}
+
+ for ind, val in ipairs(args) do
+ t[ind+1] = val
+ end
+ end
+
+ -- Construct the table that we'll be added the metadata to
+ if type(t) == 'table' then
+ m._track._type = 'list'
+ m._track._invert = {}
+ m._track._count = 0
+
+ if t['description'] then
+ m._track._description = t['description']
+ end
+
+ -- Only copy numerically indexed values
+ for ind, val in ipairs(t) do
+ m[ind] = val
+ m._track._invert[val] = ind
+ m._track._count = ind
+ end
+
+ if m._track._count == 0 then
+ m[1] = 'Normal'
+ m._track._invert['Normal'] = 1
+ m._track._count = 1
+ end
+
+ m._track._default = 1
+ elseif type(t) == 'boolean' or t == nil then
+ m._track._type = 'boolean'
+ m._track._default = t or false
+ m._track._description = args[1]
+ m._track._count = 2
+ -- Text lookups for bool values
+ m[true] = 'on'
+ m[false] = 'off'
+ else
+ -- Construction failure
+ error("Unable to construct a mode table with the provided parameters.", 2)
+ end
+
+ m._track._current = m._track._default
+
+ return setmetatable(m, _meta.M)
+end
+
+--------------------------------------------------------------------------
+-- Metamethods
+-- Functions that will be used as metamethods for the class
+--------------------------------------------------------------------------
+
+-- Handler for __index when trying to access the current mode value.
+-- Handles indexing 'current' or 'value' keys.
+_meta.M.__index = function(m, k)
+ if type(k) == 'string' then
+ local lk = k:lower()
+ if lk == 'current' or lk == 'value' then
+ return m[m._track._current]
+ elseif lk == 'index' then
+ return m._track._current
+ elseif m._track['_'..lk] then
+ return m._track['_'..lk]
+ else
+ return _meta.M.__methods[lk]
+ end
+ end
+end
+
+-- Tostring handler for printing out the table and its current state.
+_meta.M.__tostring = function(m)
+ local res = ''
+ if m._track._description then
+ res = res .. m._track._description .. ': '
+ end
+
+ if m._track._type == 'list' then
+ res = res .. '{'
+ for k,v in ipairs(m) do
+ res = res..tostring(v)
+ if m[k+1] ~= nil then
+ res = res..', '
+ end
+ end
+ res = res..'}'
+ else
+ res = res .. 'Boolean'
+ end
+
+ res = res .. ' ('..tostring(m.Current).. ')'
+
+ -- Debug addition
+ --res = res .. ' [' .. m._track._type .. '/' .. tostring(m._track._current) .. ']'
+
+ return res
+end
+
+-- Length handler for the # value lookup.
+_meta.M.__len = function(m)
+ return m._track._count
+end
+
+
+--------------------------------------------------------------------------
+-- Public methods
+-- Functions that can be used as public methods for manipulating the class.
+--------------------------------------------------------------------------
+
+-- Function to set the table's description.
+_meta.M.__methods['describe'] = function(m, str)
+ if type(str) == 'string' then
+ m._track._description = str
+ else
+ error("Invalid argument type: " .. type(str), 2)
+ end
+end
+
+-- Function to change the list of options available.
+-- Leaves the description intact.
+-- Cannot be used on boolean classes.
+_meta.M.__methods['options'] = function(m, ...)
+ if m._track._type == 'boolean' then
+ error("Cannot revise the options list for a boolean mode class.", 2)
+ end
+
+ local options = {...}
+ -- Always include a default option if nothing else is given.
+ if #options == 0 then
+ options = {'Normal'}
+ end
+
+ -- Zero-out existing values and clear the tracked inverted list
+ -- and member count.
+ for key,val in ipairs(m) do
+ m[key] = nil
+ end
+ m._track._invert = {}
+ m._track._count = 0
+
+ -- Insert in new data.
+ for key,val in ipairs(options) do
+ m[key] = val
+ m._track._invert[val] = key
+ m._track._count = key
+ end
+
+ m._track._current = m._track._default
+end
+
+
+--------------------------------------------------------------------------
+-- Public methods
+-- Functions that will be used as public methods for manipulating state.
+--------------------------------------------------------------------------
+
+-- Cycle forwards through the list
+_meta.M.__methods['cycle'] = function(m)
+ if m._track._type == 'list' then
+ m._track._current = (m._track._current % m._track._count) + 1
+ else
+ m:toggle()
+ end
+
+ return m.Current
+end
+
+-- Cycle backwards through the list
+_meta.M.__methods['cycleback'] = function(m)
+ if m._track._type == 'list' then
+ m._track._current = m._track._current - 1
+ if m._track._current < 1 then
+ m._track._current = m._track._count
+ end
+ else
+ m:toggle()
+ end
+
+ return m.Current
+end
+
+-- Toggle a boolean value
+_meta.M.__methods['toggle'] = function(m)
+ if m._track._type == 'boolean' then
+ m._track._current = not m._track._current
+ else
+ error("Cannot toggle a list mode.", 2)
+ end
+
+ return m.Current
+end
+
+
+-- Set the current value
+_meta.M.__methods['set'] = function(m, val)
+ if m._track._type == 'boolean' then
+ if val == nil then
+ m._track._current = true
+ elseif type(val) == 'boolean' then
+ m._track._current = val
+ elseif type(val) == 'string' then
+ val = val:lower()
+ if val == 'on' or val == 'true' then
+ m._track._current = true
+ elseif val == 'off' or val == 'false' then
+ m._track._current = false
+ else
+ error("Unrecognized value: "..tostring(val), 2)
+ end
+ else
+ error("Unrecognized value type: "..type(val), 2)
+ end
+ else
+ if m._track._invert[val] then
+ m._track._current = m._track._invert[val]
+ else
+ local found = false
+ for v, ind in pairs(m._track._invert) do
+ if val:lower() == v:lower() then
+ m._track._current = ind
+ found = true
+ break
+ end
+ end
+
+ if not found then
+ error("Unknown mode value: " .. tostring(val), 2)
+ end
+ end
+ end
+
+ return m.Current
+end
+
+-- Reset to the default value
+_meta.M.__methods['default'] = function(m)
+ m._track._current = m._track._default
+
+ return m.Current
+end
+
+-- Reset to the default value
+_meta.M.__methods['reset'] = function(m)
+ m._track._current = m._track._default
+
+ return m.Current
+end
+
+-- Forces a boolean mode to false
+_meta.M.__methods['unset'] = function(m)
+ if m._track._type == 'boolean' then
+ m._track._current = false
+ else
+ error("Cannot unset a list mode class.", 2)
+ end
+
+ return m.Current
+end
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Globals.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Globals.lua
new file mode 100644
index 0000000..8546c39
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Globals.lua
@@ -0,0 +1,112 @@
+-------------------------------------------------------------------------------------------------------------------
+-- Tables and functions for commonly-referenced gear that job files may need, but
+-- doesn't belong in the global Mote-Include file since they'd get clobbered on each
+-- update.
+-- Creates the 'gear' table for reference in other files.
+--
+-- Note: Function and table definitions should be added to user, but references to
+-- the contained tables via functions (such as for the obi function, below) use only
+-- the 'gear' table.
+-------------------------------------------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------------------------------------------
+-- Modify the sets table. Any gear sets that are added to the sets table need to
+-- be defined within this function, because sets isn't available until after the
+-- include is complete. It is called at the end of basic initialization in Mote-Include.
+-------------------------------------------------------------------------------------------------------------------
+
+function define_global_sets()
+ -- Special gear info that may be useful across jobs.
+
+ -- Staffs
+ gear.Staff = {}
+ gear.Staff.HMP = 'Chatoyant Staff'
+ gear.Staff.PDT = 'Earth Staff'
+
+ -- Dark Rings
+ gear.DarkRing = {}
+ gear.DarkRing.physical = {name="Dark Ring",augments={'Magic dmg. taken -3%','Spell interruption rate down -5%','Phys. dmg. taken -6%'}}
+ gear.DarkRing.magical = {name="Dark Ring", augments={'Magic dmg. taken -6%','Breath dmg. taken -5%'}}
+
+ -- Default items for utility gear values.
+ gear.default.weaponskill_neck = "Asperity Necklace"
+ gear.default.weaponskill_waist = "Caudata Belt"
+ gear.default.obi_waist = "Cognition Belt"
+ gear.default.obi_back = "Toro Cape"
+ gear.default.obi_ring = "Strendu Ring"
+ gear.default.fastcast_staff = ""
+ gear.default.recast_staff = ""
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Functions to set user-specified binds, generally on load and unload.
+-- Kept separate from the main include so as to not get clobbered when the main is updated.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Function to bind GearSwap binds when loading a GS script.
+function global_on_load()
+ send_command('bind f9 gs c cycle OffenseMode')
+ send_command('bind ^f9 gs c cycle DefenseMode')
+ send_command('bind !f9 gs c cycle WeaponskillMode')
+ send_command('bind f10 gs c activate PhysicalDefense')
+ send_command('bind ^f10 gs c cycle PhysicalDefenseMode')
+ send_command('bind !f10 gs c toggle kiting')
+ send_command('bind f11 gs c activate MagicalDefense')
+ send_command('bind ^f11 gs c cycle CastingMode')
+ send_command('bind f12 gs c update user')
+ send_command('bind ^f12 gs c cycle IdleMode')
+ send_command('bind !f12 gs c reset defense')
+
+ send_command('bind ^- gs c toggle selectnpctargets')
+ send_command('bind ^= gs c cycle pctargetmode')
+end
+
+-- Function to revert binds when unloading.
+function global_on_unload()
+ send_command('unbind f9')
+ send_command('unbind ^f9')
+ send_command('unbind !f9')
+ send_command('unbind f10')
+ send_command('unbind ^f10')
+ send_command('unbind !f10')
+ send_command('unbind f11')
+ send_command('unbind ^f11')
+ send_command('unbind !f11')
+ send_command('unbind f12')
+ send_command('unbind ^f12')
+ send_command('unbind !f12')
+
+ send_command('unbind ^-')
+ send_command('unbind ^=')
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Global event-handling functions.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Global intercept on precast.
+function user_precast(spell, action, spellMap, eventArgs)
+ cancel_conflicting_buffs(spell, action, spellMap, eventArgs)
+ refine_waltz(spell, action, spellMap, eventArgs)
+end
+
+-- Global intercept on midcast.
+function user_midcast(spell, action, spellMap, eventArgs)
+ -- Default base equipment layer of fast recast.
+ if spell.action_type == 'Magic' and sets.midcast and sets.midcast.FastRecast then
+ equip(sets.midcast.FastRecast)
+ end
+end
+
+-- Global intercept on buff change.
+function user_buff_change(buff, gain, eventArgs)
+ -- Create a timer when we gain weakness. Remove it when weakness is gone.
+ if buff:lower() == 'weakness' then
+ if gain then
+ send_command('timers create "Weakness" 300 up abilities/00255.png')
+ else
+ send_command('timers delete "Weakness"')
+ end
+ end
+end
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Include.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Include.lua
new file mode 100644
index 0000000..6ab1614
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Include.lua
@@ -0,0 +1,1125 @@
+-------------------------------------------------------------------------------------------------------------------
+-- Common variables and functions to be included in job scripts, for general default handling.
+--
+-- Include this file in the get_sets() function with the command:
+-- include('Mote-Include.lua')
+--
+-- It will then automatically run its own init_include() function.
+--
+-- IMPORTANT: This include requires supporting include files:
+-- Mote-Utility
+-- Mote-Mappings
+-- Mote-SelfCommands
+-- Mote-Globals
+--
+-- Place the include() directive at the start of a job's get_sets() function.
+--
+-- Included variables and functions are considered to be at the same scope level as
+-- the job script itself, and can be used as such.
+-------------------------------------------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------------------------------------------
+-- Initialization function that defines variables to be used.
+-- These are accessible at the including job lua script's scope.
+--
+-- Auto-initialize after defining this function.
+-------------------------------------------------------------------------------------------------------------------
+
+
+function init_include()
+ -- Used to define various types of data mappings. These may be used in the initialization,
+ -- so load it up front.
+ include('Mote-Mappings')
+
+ -- Var for tracking misc info
+ info = {}
+
+ -- Var for tracking state values
+ state = {}
+
+ -- General melee offense/defense modes, allowing for hybrid set builds, as well as idle/resting/weaponskill.
+ state.OffenseMode = 'Normal'
+ state.DefenseMode = 'Normal'
+ state.RangedMode = 'Normal'
+ state.WeaponskillMode = 'Normal'
+ state.CastingMode = 'Normal'
+ state.IdleMode = 'Normal'
+ state.RestingMode = 'Normal'
+
+ -- All-out defense state, either physical or magical
+ state.Defense = {}
+ state.Defense.Active = false
+ state.Defense.Type = 'Physical'
+ state.Defense.PhysicalMode = 'PDT'
+ state.Defense.MagicalMode = 'MDT'
+
+ state.Kiting = false
+ state.MaxWeaponskillDistance = 0
+
+ state.SelectNPCTargets = false
+ state.PCTargetMode = 'default'
+
+ state.CombatWeapon = nil
+ state.CombatForm = nil
+
+ state.Buff = {}
+
+
+ -- Vars for specifying valid mode values.
+ -- Defaults here are just for example. Set them properly in each job file.
+ options = {}
+ options.OffenseModes = {'Normal'}
+ options.DefenseModes = {'Normal'}
+ options.RangedModes = {'Normal'}
+ options.WeaponskillModes = {'Normal'}
+ options.CastingModes = {'Normal'}
+ options.IdleModes = {'Normal'}
+ options.RestingModes = {'Normal'}
+ options.PhysicalDefenseModes = {'PDT'}
+ options.MagicalDefenseModes = {'MDT'}
+
+ options.TargetModes = {'default', 'stpc', 'stpt', 'stal'}
+
+
+ -- Spell mappings to describe a 'type' of spell. Used when searching for valid sets.
+ classes = {}
+ -- Basic spell mappings are based on common spell series.
+ -- EG: 'Cure' for Cure, Cure II, Cure III, Cure IV, Cure V, or Cure VI.
+ classes.SpellMaps = spell_maps
+ -- List of spells and spell maps that don't benefit from greater skill (though
+ -- they may benefit from spell-specific augments, such as improved regen or refresh).
+ -- Spells that fall under this category will be skipped when searching for
+ -- spell.skill sets.
+ classes.NoSkillSpells = no_skill_spells_list
+ classes.SkipSkillCheck = false
+ -- Custom, job-defined class, like the generic spell mappings.
+ -- Takes precedence over default spell maps.
+ -- Is reset at the end of each spell casting cycle (ie: at the end of aftercast).
+ classes.CustomClass = nil
+ classes.JAMode = nil
+ -- Custom groups used for defining melee and idle sets. Persists long-term.
+ classes.CustomMeleeGroups = L{}
+ classes.CustomRangedGroups = L{}
+ classes.CustomIdleGroups = L{}
+ classes.CustomDefenseGroups = L{}
+
+ -- Class variables for time-based flags
+ classes.Daytime = false
+ classes.DuskToDawn = false
+
+
+ -- Special control flags.
+ mote_vars = {}
+ mote_vars.show_set = nil
+ mote_vars.set_breadcrumbs = L{}
+
+ -- Display text mapping.
+ on_off_names = {[true] = 'on', [false] = 'off'}
+ on_off_values = T{'on', 'off', 'true', 'false'}
+ true_values = T{'on', 'true'}
+
+
+ -- Subtables within the sets table that we expect to exist, and are annoying to have to
+ -- define within each individual job file. We can define them here to make sure we don't
+ -- have to check for existence. The job file should be including this before defining
+ -- any sets, so any changes it makes will override these anyway.
+ sets.precast = {}
+ sets.precast.FC = {}
+ sets.precast.JA = {}
+ sets.precast.WS = {}
+ sets.precast.RA = {}
+ sets.midcast = {}
+ sets.midcast.RA = {}
+ sets.midcast.Pet = {}
+ sets.idle = {}
+ sets.resting = {}
+ sets.engaged = {}
+ sets.defense = {}
+ sets.buff = {}
+
+ gear = {}
+ gear.default = {}
+
+ gear.ElementalGorget = {name=""}
+ gear.ElementalBelt = {name=""}
+ gear.ElementalObi = {name=""}
+ gear.ElementalCape = {name=""}
+ gear.ElementalRing = {name=""}
+ gear.FastcastStaff = {name=""}
+ gear.RecastStaff = {name=""}
+
+
+ -- Load externally-defined information (info that we don't want to change every time this file is updated).
+
+ -- Used to define misc utility functions that may be useful for this include or any job files.
+ include('Mote-Utility')
+
+ -- Used for all self-command handling.
+ include('Mote-SelfCommands')
+
+ -- Include general user globals, such as custom binds or gear tables.
+ -- Load Mote-Globals first, followed by User-Globals, followed by <character>-Globals.
+ -- Any functions re-defined in the later includes will overwrite the earlier versions.
+ include('Mote-Globals')
+ optional_include({'user-globals.lua'})
+ optional_include({player.name..'-globals.lua'})
+
+ -- *-globals.lua may define additional sets to be added to the local ones.
+ if define_global_sets then
+ define_global_sets()
+ end
+
+ -- Global default binds (either from Mote-Globals or user-globals)
+ (binds_on_load or global_on_load)()
+
+ -- Load a sidecar file for the job (if it exists) that may re-define init_gear_sets and file_unload.
+ load_sidecar(player.main_job)
+
+ -- General var initialization and setup.
+ if job_setup then
+ job_setup()
+ end
+
+ -- User-specific var initialization and setup.
+ if user_setup then
+ user_setup()
+ end
+
+ -- Load up all the gear sets.
+ init_gear_sets()
+end
+
+-- Auto-initialize the include
+init_include()
+
+-- Called when this job file is unloaded (eg: job change)
+-- Conditional definition so that it doesn't overwrite explicit user
+-- versions of this function.
+if not file_unload then
+ file_unload = function()
+ if user_unload then
+ user_unload()
+ elseif job_file_unload then
+ job_file_unload()
+ end
+ _G[(binds_on_unload and 'binds_on_unload') or 'global_on_unload']()
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Generalized functions for handling precast/midcast/aftercast for player-initiated actions.
+-- This depends on proper set naming.
+-- Global hooks can be written as user_xxx() to override functions at a global level.
+-- Each job can override any of these general functions using job_xxx() hooks.
+-------------------------------------------------------------------------------------------------------------------
+
+------------------------------------------------------------------------
+-- Generic function to map a set processing order to all action events.
+------------------------------------------------------------------------
+
+
+-- Process actions in a specific order of events:
+-- Filter - filter_xxx() functions determine whether to run any of the code for this action.
+-- Global - user_xxx() functions get called first. Define in Mote-Globals or User-Globals.
+-- Local - job_xxx() functions get called next. Define in JOB.lua file.
+-- Default - default_xxx() functions get called next. Defined in this file.
+-- Cleanup - cleanup_xxx() functions always get called before exiting.
+--
+-- Parameters:
+-- spell - standard spell table passed in by GearSwap
+-- action - string defining the function mapping to use (precast, midcast, etc)
+function handle_actions(spell, action)
+ -- Init an eventArgs that allows cancelling.
+ local eventArgs = {handled = false, cancel = false}
+
+ mote_vars.set_breadcrumbs:clear()
+
+ -- Get the spell mapping, since we'll be passing it to various functions and checks.
+ local spellMap = get_spell_map(spell)
+
+ -- General filter checks to see whether this function should be run.
+ -- If eventArgs.cancel is set, cancels this function, not the spell.
+ if _G['filter_'..action] then
+ _G['filter_'..action](spell, spellMap, eventArgs)
+ end
+
+ -- If filter didn't cancel it, process user and default actions.
+ if not eventArgs.cancel then
+ -- Global user handling of this action
+ if _G['user_'..action] then
+ _G['user_'..action](spell, action, spellMap, eventArgs)
+
+ if eventArgs.cancel then
+ cancel_spell()
+ end
+ end
+
+ -- Job-specific handling of this action
+ if not eventArgs.cancel and not eventArgs.handled and _G['job_'..action] then
+ _G['job_'..action](spell, action, spellMap, eventArgs)
+
+ if eventArgs.cancel then
+ cancel_spell()
+ end
+ end
+
+ -- Default handling of this action
+ if not eventArgs.cancel and not eventArgs.handled and _G['default_'..action] then
+ _G['default_'..action](spell, spellMap)
+ display_breadcrumbs(spell, spellMap, action)
+ end
+
+ -- Job-specific post-handling of this action
+ if not eventArgs.cancel and _G['job_post_'..action] then
+ _G['job_post_'..action](spell, action, spellMap, eventArgs)
+ end
+ end
+
+ -- Cleanup once this action is done
+ if _G['cleanup_'..action] then
+ _G['cleanup_'..action](spell, spellMap, eventArgs)
+ end
+end
+
+
+--------------------------------------
+-- Action hooks called by GearSwap.
+--------------------------------------
+
+function pretarget(spell)
+ handle_actions(spell, 'pretarget')
+end
+
+function precast(spell)
+ if state.Buff[spell.english] ~= nil then
+ state.Buff[spell.english] = true
+ end
+ handle_actions(spell, 'precast')
+end
+
+function midcast(spell)
+ handle_actions(spell, 'midcast')
+end
+
+function aftercast(spell)
+ if state.Buff[spell.english] ~= nil then
+ state.Buff[spell.english] = not spell.interrupted or buffactive[spell.english] or false
+ end
+ handle_actions(spell, 'aftercast')
+end
+
+function pet_midcast(spell)
+ handle_actions(spell, 'pet_midcast')
+end
+
+function pet_aftercast(spell)
+ handle_actions(spell, 'pet_aftercast')
+end
+
+--------------------------------------
+-- Default code for each action.
+--------------------------------------
+
+function default_pretarget(spell, spellMap)
+ auto_change_target(spell, spellMap)
+end
+
+function default_precast(spell, spellMap)
+ equip(get_precast_set(spell, spellMap))
+end
+
+function default_midcast(spell, spellMap)
+ equip(get_midcast_set(spell, spellMap))
+end
+
+function default_aftercast(spell, spellMap)
+ if not pet_midaction() then
+ handle_equipping_gear(player.status)
+ end
+end
+
+function default_pet_midcast(spell, spellMap)
+ equip(get_pet_midcast_set(spell, spellMap))
+end
+
+function default_pet_aftercast(spell, spellMap)
+ handle_equipping_gear(player.status)
+end
+
+--------------------------------------
+-- Filters for each action.
+-- Set eventArgs.cancel to true to stop further processing.
+-- May show notification messages, but should not do any processing here.
+--------------------------------------
+
+function filter_midcast(spell, spellMap, eventArgs)
+ if mote_vars.show_set == 'precast' then
+ eventArgs.cancel = true
+ end
+end
+
+function filter_aftercast(spell, spellMap, eventArgs)
+ if mote_vars.show_set == 'precast' or mote_vars.show_set == 'midcast' or mote_vars.show_set == 'pet_midcast' then
+ eventArgs.cancel = true
+ elseif spell.name == 'Unknown Interrupt' then
+ eventArgs.cancel = true
+ end
+end
+
+function filter_pet_midcast(spell, spellMap, eventArgs)
+ -- If we have show_set active for precast or midcast, don't try to equip pet midcast gear.
+ if mote_vars.show_set == 'precast' or mote_vars.show_set == 'midcast' then
+ add_to_chat(104, 'Show Sets: Pet midcast not equipped.')
+ eventArgs.cancel = true
+ end
+end
+
+function filter_pet_aftercast(spell, spellMap, eventArgs)
+ -- If show_set is flagged for precast or midcast, don't try to equip aftercast gear.
+ if mote_vars.show_set == 'precast' or mote_vars.show_set == 'midcast' or mote_vars.show_set == 'pet_midcast' then
+ eventArgs.cancel = true
+ end
+end
+
+--------------------------------------
+-- Cleanup code for each action.
+--------------------------------------
+
+function cleanup_precast(spell, spellMap, eventArgs)
+ -- If show_set is flagged for precast, notify that we won't try to equip later gear.
+ if mote_vars.show_set == 'precast' then
+ add_to_chat(104, 'Show Sets: Stopping at precast.')
+ end
+end
+
+function cleanup_midcast(spell, spellMap, eventArgs)
+ -- If show_set is flagged for midcast, notify that we won't try to equip later gear.
+ if mote_vars.show_set == 'midcast' then
+ add_to_chat(104, 'Show Sets: Stopping at midcast.')
+ end
+end
+
+function cleanup_aftercast(spell, spellMap, eventArgs)
+ -- Reset custom classes after all possible precast/midcast/aftercast/job-specific usage of the value.
+ -- If we're in the middle of a pet action, pet_aftercast will handle clearing it.
+ if not pet_midaction() then
+ reset_transitory_classes()
+ end
+end
+
+function cleanup_pet_midcast(spell, spellMap, eventArgs)
+ -- If show_set is flagged for pet midcast, notify that we won't try to equip later gear.
+ if mote_vars.show_set == 'pet_midcast' then
+ add_to_chat(104, 'Show Sets: Stopping at pet midcast.')
+ end
+end
+
+function cleanup_pet_aftercast(spell, spellMap, eventArgs)
+ -- Reset custom classes after all possible precast/midcast/aftercast/job-specific usage of the value.
+ reset_transitory_classes()
+end
+
+
+-- Clears the values from classes that only exist til the action is complete.
+function reset_transitory_classes()
+ classes.CustomClass = nil
+ classes.JAMode = nil
+end
+
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- High-level functions for selecting and equipping gear sets.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Central point to call to equip gear based on status.
+-- Status - Player status that we're using to define what gear to equip.
+function handle_equipping_gear(playerStatus, petStatus)
+ -- init a new eventArgs
+ local eventArgs = {handled = false}
+
+ -- Allow jobs to override this code
+ if job_handle_equipping_gear then
+ job_handle_equipping_gear(playerStatus, eventArgs)
+ end
+
+ -- Equip default gear if job didn't handle it.
+ if not eventArgs.handled then
+ equip_gear_by_status(playerStatus, petStatus)
+ end
+end
+
+
+-- Function to wrap logic for equipping gear on aftercast, status change, or user update.
+-- @param status : The current or new player status that determines what sort of gear to equip.
+function equip_gear_by_status(playerStatus, petStatus)
+ if _global.debug_mode then add_to_chat(123,'Debug: Equip gear for status ['..tostring(status)..'], HP='..tostring(player.hp)) end
+
+ playerStatus = playerStatus or player.status or 'Idle'
+
+ -- If status not defined, treat as idle.
+ -- Be sure to check for positive HP to make sure they're not dead.
+ if (playerStatus == 'Idle' or playerStatus == '') and player.hp > 0 then
+ equip(get_idle_set(petStatus))
+ elseif playerStatus == 'Engaged' then
+ equip(get_melee_set(petStatus))
+ elseif playerStatus == 'Resting' then
+ equip(get_resting_set(petStatus))
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Functions for constructing default gear sets based on status.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Returns the appropriate idle set based on current state values and location.
+-- Set construction order (all of which are optional):
+-- sets.idle[idleScope][state.IdleMode][Pet[Engaged]][CustomIdleGroups]
+--
+-- Params:
+-- petStatus - Optional explicit definition of pet status.
+function get_idle_set(petStatus)
+ local idleSet = sets.idle
+
+ if not idleSet then
+ return {}
+ end
+
+ mote_vars.set_breadcrumbs:append('sets')
+ mote_vars.set_breadcrumbs:append('idle')
+
+ local idleScope
+
+ if buffactive.weakness then
+ idleScope = 'Weak'
+ elseif areas.Cities:contains(world.area) then
+ idleScope = 'Town'
+ else
+ idleScope = 'Field'
+ end
+
+ if idleSet[idleScope] then
+ idleSet = idleSet[idleScope]
+ mote_vars.set_breadcrumbs:append(idleScope)
+ end
+
+ if idleSet[state.IdleMode] then
+ idleSet = idleSet[state.IdleMode]
+ mote_vars.set_breadcrumbs:append(state.IdleMode)
+ end
+
+ if (pet.isvalid or state.Buff.Pet) and idleSet.Pet then
+ idleSet = idleSet.Pet
+ petStatus = petStatus or pet.status
+ mote_vars.set_breadcrumbs:append('Pet')
+
+ if petStatus == 'Engaged' and idleSet.Engaged then
+ idleSet = idleSet.Engaged
+ mote_vars.set_breadcrumbs:append('Engaged')
+ end
+ end
+
+ for _,group in ipairs(classes.CustomIdleGroups) do
+ if idleSet[group] then
+ idleSet = idleSet[group]
+ mote_vars.set_breadcrumbs:append(group)
+ end
+ end
+
+ idleSet = apply_defense(idleSet)
+ idleSet = apply_kiting(idleSet)
+
+ if user_customize_idle_set then
+ idleSet = user_customize_idle_set(idleSet)
+ end
+
+ if customize_idle_set then
+ idleSet = customize_idle_set(idleSet)
+ end
+
+ return idleSet
+end
+
+
+-- Returns the appropriate melee set based on current state values.
+-- Set construction order (all sets after sets.engaged are optional):
+-- sets.engaged[state.CombatForm][state.CombatWeapon][state.OffenseMode][state.DefenseMode][classes.CustomMeleeGroups (any number)]
+function get_melee_set()
+ local meleeSet = sets.engaged
+
+ if not meleeSet then
+ return {}
+ end
+
+ mote_vars.set_breadcrumbs:append('sets')
+ mote_vars.set_breadcrumbs:append('engaged')
+
+ if state.CombatForm and meleeSet[state.CombatForm] then
+ meleeSet = meleeSet[state.CombatForm]
+ mote_vars.set_breadcrumbs:append(state.CombatForm)
+ end
+
+ if state.CombatWeapon and meleeSet[state.CombatWeapon] then
+ meleeSet = meleeSet[state.CombatWeapon]
+ mote_vars.set_breadcrumbs:append(state.CombatWeapon)
+ end
+
+ if meleeSet[state.OffenseMode] then
+ meleeSet = meleeSet[state.OffenseMode]
+ mote_vars.set_breadcrumbs:append(state.OffenseMode)
+ end
+
+ if meleeSet[state.DefenseMode] then
+ meleeSet = meleeSet[state.DefenseMode]
+ mote_vars.set_breadcrumbs:append(state.DefenseMode)
+ end
+
+ for _,group in ipairs(classes.CustomMeleeGroups) do
+ if meleeSet[group] then
+ meleeSet = meleeSet[group]
+ mote_vars.set_breadcrumbs:append(group)
+ end
+ end
+
+ meleeSet = apply_defense(meleeSet)
+ meleeSet = apply_kiting(meleeSet)
+
+ if customize_melee_set then
+ meleeSet = customize_melee_set(meleeSet)
+ end
+
+ if user_customize_melee_set then
+ meleeSet = user_customize_melee_set(meleeSet)
+ end
+
+ return meleeSet
+end
+
+
+-- Returns the appropriate resting set based on current state values.
+-- Set construction order:
+-- sets.resting[state.RestingMode]
+function get_resting_set()
+ local restingSet = sets.resting
+
+ if not restingSet then
+ return {}
+ end
+
+ mote_vars.set_breadcrumbs:append('sets')
+ mote_vars.set_breadcrumbs:append('resting')
+
+ if restingSet[state.RestingMode] then
+ restingSet = restingSet[state.RestingMode]
+ mote_vars.set_breadcrumbs:append(state.RestingMode)
+ end
+
+ return restingSet
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Functions for constructing default gear sets based on action.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Get the default precast gear set.
+function get_precast_set(spell, spellMap)
+ -- If there are no precast sets defined, bail out.
+ if not sets.precast then
+ return {}
+ end
+
+ local equipSet = sets.precast
+
+ mote_vars.set_breadcrumbs:append('sets')
+ mote_vars.set_breadcrumbs:append('precast')
+
+ -- Determine base sub-table from type of action being performed.
+
+ local cat
+
+ if spell.action_type == 'Magic' then
+ cat = 'FC'
+ elseif spell.action_type == 'Ranged Attack' then
+ cat = (sets.precast.RangedAttack and 'RangedAttack') or 'RA'
+ elseif spell.action_type == 'Ability' then
+ if spell.type == 'WeaponSkill' then
+ cat = 'WS'
+ elseif spell.type == 'JobAbility' then
+ cat = 'JA'
+ else
+ -- Allow fallback to .JA table if spell.type isn't found, for all non-weaponskill abilities.
+ cat = (sets.precast[spell.type] and spell.type) or 'JA'
+ end
+ elseif spell.action_type == 'Item' then
+ cat = 'Item'
+ end
+
+ -- If no proper sub-category is defined in the job file, bail out.
+ if cat then
+ if equipSet[cat] then
+ equipSet = equipSet[cat]
+ mote_vars.set_breadcrumbs:append(cat)
+ else
+ mote_vars.set_breadcrumbs:clear()
+ return {}
+ end
+ end
+
+ classes.SkipSkillCheck = false
+ -- Handle automatic selection of set based on spell class/name/map/skill/type.
+ equipSet = select_specific_set(equipSet, spell, spellMap)
+
+
+ -- Once we have a named base set, do checks for specialized modes (casting mode, weaponskill mode, etc).
+
+ if spell.action_type == 'Magic' then
+ if equipSet[state.CastingMode] then
+ equipSet = equipSet[state.CastingMode]
+ mote_vars.set_breadcrumbs:append(state.CastingMode)
+ end
+ elseif spell.type == 'WeaponSkill' then
+ equipSet = get_weaponskill_set(equipSet, spell, spellMap)
+ elseif spell.action_type == 'Ability' then
+ if classes.JAMode and equipSet[classes.JAMode] then
+ equipSet = equipSet[classes.JAMode]
+ mote_vars.set_breadcrumbs:append(classes.JAMode)
+ end
+ elseif spell.action_type == 'Ranged Attack' then
+ equipSet = get_ranged_set(equipSet, spell, spellMap)
+ end
+
+ -- Update defintions for element-specific gear that may be used.
+ set_elemental_gear(spell)
+
+ -- Return whatever we've constructed.
+ return equipSet
+end
+
+
+
+-- Get the default midcast gear set.
+-- This builds on sets.midcast.
+function get_midcast_set(spell, spellMap)
+ -- If there are no midcast sets defined, bail out.
+ if not sets.midcast then
+ return {}
+ end
+
+ local equipSet = sets.midcast
+
+ mote_vars.set_breadcrumbs:append('sets')
+ mote_vars.set_breadcrumbs:append('midcast')
+
+ -- Determine base sub-table from type of action being performed.
+ -- Only ranged attacks and items get specific sub-categories here.
+
+ local cat
+
+ if spell.action_type == 'Ranged Attack' then
+ cat = (sets.precast.RangedAttack and 'RangedAttack') or 'RA'
+ elseif spell.action_type == 'Item' then
+ cat = 'Item'
+ end
+
+ -- If no proper sub-category is defined in the job file, bail out.
+ if cat then
+ if equipSet[cat] then
+ equipSet = equipSet[cat]
+ mote_vars.set_breadcrumbs:append(cat)
+ else
+ mote_vars.set_breadcrumbs:clear()
+ return {}
+ end
+ end
+
+ classes.SkipSkillCheck = classes.NoSkillSpells:contains(spell.english)
+ -- Handle automatic selection of set based on spell class/name/map/skill/type.
+ equipSet = select_specific_set(equipSet, spell, spellMap)
+
+ -- After the default checks, do checks for specialized modes (casting mode, etc).
+
+ if spell.action_type == 'Magic' then
+ if equipSet[state.CastingMode] then
+ equipSet = equipSet[state.CastingMode]
+ mote_vars.set_breadcrumbs:append(state.CastingMode)
+ end
+ elseif spell.action_type == 'Ranged Attack' then
+ equipSet = get_ranged_set(equipSet, spell, spellMap)
+ end
+
+ -- Return whatever we've constructed.
+ return equipSet
+end
+
+
+-- Get the default pet midcast gear set.
+-- This is built in sets.midcast.Pet.
+function get_pet_midcast_set(spell, spellMap)
+ -- If there are no midcast sets defined, bail out.
+ if not sets.midcast or not sets.midcast.Pet then
+ return {}
+ end
+
+ local equipSet = sets.midcast.Pet
+
+ mote_vars.set_breadcrumbs:append('sets')
+ mote_vars.set_breadcrumbs:append('midcast')
+ mote_vars.set_breadcrumbs:append('Pet')
+
+ if sets.midcast and sets.midcast.Pet then
+ classes.SkipSkillCheck = false
+ equipSet = select_specific_set(equipSet, spell, spellMap)
+
+ -- We can only generally be certain about whether the pet's action is
+ -- Magic (ie: it cast a spell of its own volition) or Ability (it performed
+ -- an action at the request of the player). Allow CastinMode and
+ -- OffenseMode to refine whatever set was selected above.
+ if spell.action_type == 'Magic' then
+ if equipSet[state.CastingMode] then
+ equipSet = equipSet[state.CastingMode]
+ mote_vars.set_breadcrumbs:append(state.CastingMode)
+ end
+ elseif spell.action_type == 'Ability' then
+ if equipSet[state.OffenseMode] then
+ equipSet = equipSet[state.OffenseMode]
+ mote_vars.set_breadcrumbs:append(state.OffenseMode)
+ end
+ end
+ end
+
+ return equipSet
+end
+
+
+-- Function to handle the logic of selecting the proper weaponskill set.
+function get_weaponskill_set(equipSet, spell, spellMap)
+ -- Custom handling for weaponskills
+ local ws_mode = state.WeaponskillMode
+
+ if ws_mode == 'Normal' then
+ -- If a particular weaponskill mode isn't specified, see if we have a weaponskill mode
+ -- corresponding to the current offense mode. If so, use that.
+ if spell.skill == 'Archery' or spell.skill == 'Marksmanship' then
+ if state.RangedMode ~= 'Normal' and S(options.WeaponskillModes):contains(state.RangedMode) then
+ ws_mode = state.RangedMode
+ end
+ else
+ if state.OffenseMode ~= 'Normal' and S(options.WeaponskillModes):contains(state.OffenseMode) then
+ ws_mode = state.OffenseMode
+ end
+ end
+ end
+
+ local custom_wsmode
+
+ -- Allow the job file to specify a preferred weaponskill mode
+ if get_custom_wsmode then
+ custom_wsmode = get_custom_wsmode(spell, spellMap, ws_mode)
+ end
+
+ -- If the job file returned a weaponskill mode, use that.
+ if custom_wsmode then
+ ws_mode = custom_wsmode
+ end
+
+ if equipSet[ws_mode] then
+ equipSet = equipSet[ws_mode]
+ mote_vars.set_breadcrumbs:append(ws_mode)
+ end
+
+ return equipSet
+end
+
+
+-- Function to handle the logic of selecting the proper ranged set.
+function get_ranged_set(equipSet, spell, spellMap)
+ -- Attach Combat Form and Combat Weapon to set checks
+ if state.CombatForm and equipSet[state.CombatForm] then
+ equipSet = equipSet[state.CombatForm]
+ mote_vars.set_breadcrumbs:append(state.CombatForm)
+ end
+
+ if state.CombatWeapon and equipSet[state.CombatWeapon] then
+ equipSet = equipSet[state.CombatWeapon]
+ mote_vars.set_breadcrumbs:append(state.CombatWeapon)
+ end
+
+ -- Check for specific mode for ranged attacks (eg: Acc, Att, etc)
+ if equipSet[state.RangedMode] then
+ equipSet = equipSet[state.RangedMode]
+ mote_vars.set_breadcrumbs:append(state.RangedMode)
+ end
+
+ -- Tack on any additionally specified custom groups, if the sets are defined.
+ for _,group in ipairs(classes.CustomRangedGroups) do
+ if equipSet[group] then
+ equipSet = equipSet[group]
+ mote_vars.set_breadcrumbs:append(group)
+ end
+ end
+
+ return equipSet
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Functions for optional supplemental gear overriding the default sets defined above.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Function to apply any active defense set on top of the supplied set
+-- @param baseSet : The set that any currently active defense set will be applied on top of. (gear set table)
+function apply_defense(baseSet)
+ if state.Defense.Active then
+ local defenseSet = sets.defense
+
+ if state.Defense.Type == 'Physical' then
+ defenseSet = sets.defense[state.Defense.PhysicalMode] or defenseSet
+ else
+ defenseSet = sets.defense[state.Defense.MagicalMode] or defenseSet
+ end
+
+ for _,group in ipairs(classes.CustomDefenseGroups) do
+ defenseSet = defenseSet[group] or defenseSet
+ end
+
+ baseSet = set_combine(baseSet, defenseSet)
+ end
+
+ return baseSet
+end
+
+
+-- Function to add kiting gear on top of the base set if kiting state is true.
+-- @param baseSet : The gear set that the kiting gear will be applied on top of.
+function apply_kiting(baseSet)
+ if state.Kiting then
+ if sets.Kiting then
+ baseSet = set_combine(baseSet, sets.Kiting)
+ end
+ end
+
+ return baseSet
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions for constructing default gear sets.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Get a spell mapping for the spell.
+function get_spell_map(spell)
+ local defaultSpellMap = classes.SpellMaps[spell.english]
+ local jobSpellMap
+
+ if job_get_spell_map then
+ jobSpellMap = job_get_spell_map(spell, defaultSpellMap)
+ end
+
+ return jobSpellMap or defaultSpellMap
+end
+
+
+-- Select the equipment set to equip from a given starting table, based on standard
+-- selection order: custom class, spell name, spell map, spell skill, and spell type.
+-- Spell skill and spell type may further refine their selections based on
+-- custom class, spell name and spell map.
+function select_specific_set(equipSet, spell, spellMap)
+ -- Take the determined base equipment set and try to get the simple naming extensions that
+ -- may apply to it (class, spell name, spell map).
+ local namedSet = get_named_set(equipSet, spell, spellMap)
+
+ -- If no simple naming sub-tables were found, and we simply got back the original equip set,
+ -- check for spell.skill and spell.type, then check the simple naming extensions again.
+ if namedSet == equipSet then
+ if spell.skill and equipSet[spell.skill] and not classes.SkipSkillCheck then
+ namedSet = equipSet[spell.skill]
+ mote_vars.set_breadcrumbs:append(spell.skill)
+ elseif spell.type and equipSet[spell.type] then
+ namedSet = equipSet[spell.type]
+ mote_vars.set_breadcrumbs:append(spell.type)
+ else
+ return equipSet
+ end
+
+ namedSet = get_named_set(namedSet, spell, spellMap)
+ end
+
+ return namedSet or equipSet
+end
+
+
+-- Simple utility function to handle a portion of the equipment set determination.
+-- It attempts to select a sub-table of the provided equipment set based on the
+-- standard search order of custom class, spell name, and spell map.
+-- If no such set is found, it returns the original base set (equipSet) provided.
+function get_named_set(equipSet, spell, spellMap)
+ if equipSet then
+ if classes.CustomClass and equipSet[classes.CustomClass] then
+ mote_vars.set_breadcrumbs:append(classes.CustomClass)
+ return equipSet[classes.CustomClass]
+ elseif equipSet[spell.english] then
+ mote_vars.set_breadcrumbs:append(spell.english)
+ return equipSet[spell.english]
+ elseif spellMap and equipSet[spellMap] then
+ mote_vars.set_breadcrumbs:append(spellMap)
+ return equipSet[spellMap]
+ else
+ return equipSet
+ end
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Hooks for other events.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Called when the player's subjob changes.
+function sub_job_change(newSubjob, oldSubjob)
+ if user_setup then
+ user_setup()
+ end
+
+ if job_sub_job_change then
+ job_sub_job_change(newSubjob, oldSubjob)
+ end
+
+ send_command('gs c update')
+end
+
+
+-- Called when the player's status changes.
+function status_change(newStatus, oldStatus)
+ -- init a new eventArgs
+ local eventArgs = {handled = false}
+ mote_vars.set_breadcrumbs:clear()
+
+ -- Allow a global function to be called on status change.
+ if user_status_change then
+ user_status_change(newStatus, oldStatus, eventArgs)
+ end
+
+ -- Then call individual jobs to handle status change events.
+ if not eventArgs.handled then
+ if job_status_change then
+ job_status_change(newStatus, oldStatus, eventArgs)
+ end
+ end
+
+ -- Handle equipping default gear if the job didn't mark this as handled.
+ if not eventArgs.handled then
+ handle_equipping_gear(newStatus)
+ display_breadcrumbs()
+ end
+end
+
+
+-- Called when a player gains or loses a buff.
+-- buff == buff gained or lost
+-- gain == true if the buff was gained, false if it was lost.
+function buff_change(buff, gain)
+ -- Init a new eventArgs
+ local eventArgs = {handled = false}
+
+ if state.Buff[buff] ~= nil then
+ state.Buff[buff] = gain
+ end
+
+ -- Allow a global function to be called on buff change.
+ if user_buff_change then
+ user_buff_change(buff, gain, eventArgs)
+ end
+
+ -- Allow jobs to handle buff change events.
+ if not eventArgs.handled then
+ if job_buff_change then
+ job_buff_change(buff, gain, eventArgs)
+ end
+ end
+end
+
+
+-- Called when a player gains or loses a pet.
+-- pet == pet gained or lost
+-- gain == true if the pet was gained, false if it was lost.
+function pet_change(pet, gain)
+ -- Init a new eventArgs
+ local eventArgs = {handled = false}
+
+ -- Allow jobs to handle pet change events.
+ if job_pet_change then
+ job_pet_change(pet, gain, eventArgs)
+ end
+
+ -- Equip default gear if not handled by the job.
+ if not eventArgs.handled then
+ handle_equipping_gear(player.status)
+ end
+end
+
+
+-- Called when the player's pet's status changes.
+-- Note that this is also called after pet_change when the pet is released.
+-- As such, don't automatically handle gear equips. Only do so if directed
+-- to do so by the job.
+function pet_status_change(newStatus, oldStatus)
+ -- Init a new eventArgs
+ local eventArgs = {handled = false}
+
+ -- Allow jobs to override this code
+ if job_pet_status_change then
+ job_pet_status_change(newStatus, oldStatus, eventArgs)
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Debugging functions.
+-------------------------------------------------------------------------------------------------------------------
+
+-- This is a debugging function that will print the accumulated set selection
+-- breadcrumbs for the default selected set for any given action stage.
+function display_breadcrumbs(spell, spellMap, action)
+ if not _settings.debug_mode then
+ return
+ end
+
+ local msg = 'Default '
+
+ if action and spell then
+ msg = msg .. action .. ' set selection for ' .. spell.name
+ end
+
+ if spellMap then
+ msg = msg .. ' (' .. spellMap .. ')'
+ end
+ msg = msg .. ' : '
+
+ local cons
+
+ for _,name in ipairs(mote_vars.set_breadcrumbs) do
+ if not cons then
+ cons = name
+ else
+ if name:contains(' ') or name:contains("'") then
+ cons = cons .. '["' .. name .. '"]'
+ else
+ cons = cons .. '.' .. name
+ end
+ end
+ end
+
+ if cons then
+ if action and cons == ('sets.' .. action) then
+ msg = msg .. "None"
+ else
+ msg = msg .. tostring(cons)
+ end
+ add_to_chat(123, msg)
+ end
+end
+
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Mappings.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Mappings.lua
new file mode 100644
index 0000000..76297a3
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Mappings.lua
@@ -0,0 +1,273 @@
+-------------------------------------------------------------------------------------------------------------------
+-- Mappings, lists and sets to describe game relationships that aren't easily determinable otherwise.
+-------------------------------------------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------------------------------------------
+-- Elemental mappings for element relationships and certain types of spells and gear.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Basic elements
+elements = {}
+
+elements.list = S{'Light','Dark','Fire','Ice','Wind','Earth','Lightning','Water'}
+
+elements.weak_to = {['Light']='Dark', ['Dark']='Light', ['Fire']='Ice', ['Ice']='Wind', ['Wind']='Earth', ['Earth']='Lightning',
+ ['Lightning']='Water', ['Water']='Fire'}
+
+elements.strong_to = {['Light']='Dark', ['Dark']='Light', ['Fire']='Water', ['Ice']='Fire', ['Wind']='Ice', ['Earth']='Wind',
+ ['Lightning']='Earth', ['Water']='Lightning'}
+
+
+storms = S{"Aurorastorm", "Voidstorm", "Firestorm", "Sandstorm", "Rainstorm", "Windstorm", "Hailstorm", "Thunderstorm"}
+elements.storm_of = {['Light']="Aurorastorm", ['Dark']="Voidstorm", ['Fire']="Firestorm", ['Earth']="Sandstorm",
+ ['Water']="Rainstorm", ['Wind']="Windstorm", ['Ice']="Hailstorm", ['Lightning']="Thunderstorm"}
+
+spirits = S{"LightSpirit", "DarkSpirit", "FireSpirit", "EarthSpirit", "WaterSpirit", "AirSpirit", "IceSpirit", "ThunderSpirit"}
+elements.spirit_of = {['Light']="Light Spirit", ['Dark']="Dark Spirit", ['Fire']="Fire Spirit", ['Earth']="Earth Spirit",
+ ['Water']="Water Spirit", ['Wind']="Air Spirit", ['Ice']="Ice Spirit", ['Lightning']="Thunder Spirit"}
+
+runes = S{'Lux', 'Tenebrae', 'Ignis', 'Gelus', 'Flabra', 'Tellus', 'Sulpor', 'Unda'}
+elements.rune_of = {['Light']='Lux', ['Dark']='Tenebrae', ['Fire']='Ignis', ['Ice']='Gelus', ['Wind']='Flabra',
+ ['Earth']='Tellus', ['Lightning']='Sulpor', ['Water']='Unda'}
+
+elements.obi_of = {['Light']='Hachirin-no-obi', ['Dark']='Hachirin-no-obi', ['Fire']='Hachirin-no-obi', ['Ice']='Hachirin-no-obi', ['Wind']='Hachirin-no-obi',
+ ['Earth']='Hachirin-no-obi', ['Lightning']='Hachirin-no-obi', ['Water']='Hachirin-no-obi'}
+
+elements.gorget_of = {['Light']='Fotia Gorget', ['Dark']='Fotia Gorget', ['Fire']='Fotia Gorget', ['Ice']='Fotia Gorget',
+ ['Wind']='Fotia Gorget', ['Earth']='Fotia Gorget', ['Lightning']='Fotia Gorget', ['Water']='Fotia Gorget'}
+
+elements.belt_of = {['Light']='Fotia Belt', ['Dark']='Fotia Belt', ['Fire']='Fotia Belt', ['Ice']='Fotia Belt',
+ ['Wind']='Fotia Belt', ['Earth']='Fotia Belt', ['Lightning']='Fotia Belt', ['Water']='Fotia Belt'}
+
+elements.fastcast_staff_of = {['Light']='Arka I', ['Dark']='Xsaeta I', ['Fire']='Atar I', ['Ice']='Vourukasha I',
+ ['Wind']='Vayuvata I', ['Earth']='Vishrava I', ['Lightning']='Apamajas I', ['Water']='Haoma I', ['Thunder']='Apamajas I'}
+
+elements.recast_staff_of = {['Light']='Arka II', ['Dark']='Xsaeta II', ['Fire']='Atar II', ['Ice']='Vourukasha II',
+ ['Wind']='Vayuvata II', ['Earth']='Vishrava II', ['Lightning']='Apamajas II', ['Water']='Haoma II', ['Thunder']='Apamajas II'}
+
+elements.perpetuance_staff_of = {['Light']='Arka III', ['Dark']='Xsaeta III', ['Fire']='Atar III', ['Ice']='Vourukasha III',
+ ['Wind']='Vayuvata III', ['Earth']='Vishrava III', ['Lightning']='Apamajas III', ['Water']='Haoma III', ['Thunder']='Apamajas III'}
+
+
+-- Elements for skillchain names
+skillchain_elements = {}
+skillchain_elements.Light = S{'Light','Fire','Wind','Lightning'}
+skillchain_elements.Darkness = S{'Dark','Ice','Earth','Water'}
+skillchain_elements.Fusion = S{'Light','Fire'}
+skillchain_elements.Fragmentation = S{'Wind','Lightning'}
+skillchain_elements.Distortion = S{'Ice','Water'}
+skillchain_elements.Gravitation = S{'Dark','Earth'}
+skillchain_elements.Transfixion = S{'Light'}
+skillchain_elements.Compression = S{'Dark'}
+skillchain_elements.Liquification = S{'Fire'}
+skillchain_elements.Induration = S{'Ice'}
+skillchain_elements.Detonation = S{'Wind'}
+skillchain_elements.Scission = S{'Earth'}
+skillchain_elements.Impaction = S{'Lightning'}
+skillchain_elements.Reverberation = S{'Water'}
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Mappings for weaponskills
+-------------------------------------------------------------------------------------------------------------------
+
+-- REM weapons and their corresponding weaponskills
+data = {}
+data.weaponskills = {}
+data.weaponskills.relic = {
+ ["Spharai"] = "Final Heaven",
+ ["Mandau"] = "Mercy Stroke",
+ ["Excalibur"] = "Knights of Round",
+ ["Ragnarok"] = "Scourge",
+ ["Guttler"] = "Onslaught",
+ ["Bravura"] = "Metatron Torment",
+ ["Apocalypse"] = "Catastrophe",
+ ["Gungnir"] = "Gierskogul",
+ ["Kikoku"] = "Blade: Metsu",
+ ["Amanomurakumo"] = "Tachi: Kaiten",
+ ["Mjollnir"] = "Randgrith",
+ ["Claustrum"] = "Gates of Tartarus",
+ ["Annihilator"] = "Coronach",
+ ["Yoichinoyumi"] = "Namas Arrow"}
+data.weaponskills.mythic = {
+ ["Conqueror"] = "King's Justice",
+ ["Glanzfaust"] = "Ascetic's Fury",
+ ["Yagrush"] = "Mystic Boon",
+ ["Laevateinn"] = "Vidohunir",
+ ["Murgleis"] = "Death Blossom",
+ ["Vajra"] = "Mandalic Stab",
+ ["Burtgang"] = "Atonement",
+ ["Liberator"] = "Insurgency",
+ ["Aymur"] = "Primal Rend",
+ ["Carnwenhan"] = "Mordant Rime",
+ ["Gastraphetes"] = "Trueflight",
+ ["Kogarasumaru"] = "Tachi: Rana",
+ ["Nagi"] = "Blade: Kamu",
+ ["Ryunohige"] = "Drakesbane",
+ ["Nirvana"] = "Garland of Bliss",
+ ["Tizona"] = "Expiacion",
+ ["Death Penalty"] = "Leaden Salute",
+ ["Kenkonken"] = "Stringing Pummel",
+ ["Terpsichore"] = "Pyrrhic Kleos",
+ ["Tupsimati"] = "Omniscience",
+ ["Idris"] = "Exudation",
+ ["Epeolatry"] = "Dimidiation"}
+data.weaponskills.empyrean = {
+ ["Verethragna"] = "Victory Smite",
+ ["Twashtar"] = "Rudra's Storm",
+ ["Almace"] = "Chant du Cygne",
+ ["Caladbolg"] = "Torcleaver",
+ ["Farsha"] = "Cloudsplitter",
+ ["Ukonvasara"] = "Ukko's Fury",
+ ["Redemption"] = "Quietus",
+ ["Rhongomiant"] = "Camlann's Torment",
+ ["Kannagi"] = "Blade: Hi",
+ ["Masamune"] = "Tachi: Fudo",
+ ["Gambanteinn"] = "Dagann",
+ ["Hvergelmir"] = "Myrkr",
+ ["Gandiva"] = "Jishnu's Radiance",
+ ["Armageddon"] = "Wildfire"}
+
+-- Weaponskills that can be used at range.
+data.weaponskills.ranged = S{"Flaming Arrow", "Piercing Arrow", "Dulling Arrow", "Sidewinder", "Arching Arrow",
+ "Empyreal Arrow", "Refulgent Arrow", "Apex Arrow", "Namas Arrow", "Jishnu's Radiance",
+ "Hot Shot", "Split Shot", "Sniper Shot", "Slug Shot", "Heavy Shot", "Detonator", "Last Stand",
+ "Coronach", "Trueflight", "Leaden Salute", "Wildfire",
+ "Myrkr"}
+
+ranged_weaponskills = data.weaponskills.ranged
+
+-------------------------------------------------------------------------------------------------------------------
+-- Spell mappings allow defining a general category or description that each of sets of related
+-- spells all fall under.
+-------------------------------------------------------------------------------------------------------------------
+
+spell_maps = {
+ ['Cure']='Cure',['Cure II']='Cure',['Cure III']='Cure',['Cure IV']='Cure',['Cure V']='Cure',['Cure VI']='Cure',
+ ['Cura']='Curaga',['Cura II']='Curaga',['Cura III']='Curaga',
+ ['Curaga']='Curaga',['Curaga II']='Curaga',['Curaga III']='Curaga',['Curaga IV']='Curaga',['Curaga V']='Curaga',
+ -- Status Removal doesn't include Esuna or Sacrifice, since they work differently than the rest
+ ['Poisona']='StatusRemoval',['Paralyna']='StatusRemoval',['Silena']='StatusRemoval',['Blindna']='StatusRemoval',['Cursna']='StatusRemoval',
+ ['Stona']='StatusRemoval',['Viruna']='StatusRemoval',['Erase']='StatusRemoval',
+ ['Barfire']='BarElement',['Barstone']='BarElement',['Barwater']='BarElement',['Baraero']='BarElement',['Barblizzard']='BarElement',['Barthunder']='BarElement',
+ ['Barfira']='BarElement',['Barstonra']='BarElement',['Barwatera']='BarElement',['Baraera']='BarElement',['Barblizzara']='BarElement',['Barthundra']='BarElement',
+ ['Raise']='Raise',['Raise II']='Raise',['Raise III']='Raise',['Arise']='Raise',
+ ['Reraise']='Reraise',['Reraise II']='Reraise',['Reraise III']='Reraise',
+ ['Protect']='Protect',['Protect II']='Protect',['Protect III']='Protect',['Protect IV']='Protect',['Protect V']='Protect',
+ ['Shell']='Shell',['Shell II']='Shell',['Shell III']='Shell',['Shell IV']='Shell',['Shell V']='Shell',
+ ['Protectra']='Protectra',['Protectra II']='Protectra',['Protectra III']='Protectra',['Protectra IV']='Protectra',['Protectra V']='Protectra',
+ ['Shellra']='Shellra',['Shellra II']='Shellra',['Shellra III']='Shellra',['Shellra IV']='Shellra',['Shellra V']='Shellra',
+ ['Regen']='Regen',['Regen II']='Regen',['Regen III']='Regen',['Regen IV']='Regen',['Regen V']='Regen',
+ ['Refresh']='Refresh',['Refresh II']='Refresh',
+ ['Teleport-Holla']='Teleport',['Teleport-Dem']='Teleport',['Teleport-Mea']='Teleport',['Teleport-Altep']='Teleport',['Teleport-Yhoat']='Teleport',
+ ['Teleport-Vahzl']='Teleport',['Recall-Pashh']='Teleport',['Recall-Meriph']='Teleport',['Recall-Jugner']='Teleport',
+ ['Valor Minuet']='Minuet',['Valor Minuet II']='Minuet',['Valor Minuet III']='Minuet',['Valor Minuet IV']='Minuet',['Valor Minuet V']='Minuet',
+ ["Knight's Minne"]='Minne',["Knight's Minne II"]='Minne',["Knight's Minne III"]='Minne',["Knight's Minne IV"]='Minne',["Knight's Minne V"]='Minne',
+ ['Advancing March']='March',['Victory March']='March',
+ ['Sword Madrigal']='Madrigal',['Blade Madrigal']='Madrigal',
+ ["Hunter's Prelude"]='Prelude',["Archer's Prelude"]='Prelude',
+ ['Sheepfoe Mambo']='Mambo',['Dragonfoe Mambo']='Mambo',
+ ['Raptor Mazurka']='Mazurka',['Chocobo Mazurka']='Mazurka',
+ ['Sinewy Etude']='Etude',['Dextrous Etude']='Etude',['Vivacious Etude']='Etude',['Quick Etude']='Etude',['Learned Etude']='Etude',['Spirited Etude']='Etude',['Enchanting Etude']='Etude',
+ ['Herculean Etude']='Etude',['Uncanny Etude']='Etude',['Vital Etude']='Etude',['Swift Etude']='Etude',['Sage Etude']='Etude',['Logical Etude']='Etude',['Bewitching Etude']='Etude',
+ ["Mage's Ballad"]='Ballad',["Mage's Ballad II"]='Ballad',["Mage's Ballad III"]='Ballad',
+ ["Army's Paeon"]='Paeon',["Army's Paeon II"]='Paeon',["Army's Paeon III"]='Paeon',["Army's Paeon IV"]='Paeon',["Army's Paeon V"]='Paeon',["Army's Paeon VI"]='Paeon',
+ ['Fire Carol']='Carol',['Ice Carol']='Carol',['Wind Carol']='Carol',['Earth Carol']='Carol',['Lightning Carol']='Carol',['Water Carol']='Carol',['Light Carol']='Carol',['Dark Carol']='Carol',
+ ['Fire Carol II']='Carol',['Ice Carol II']='Carol',['Wind Carol II']='Carol',['Earth Carol II']='Carol',['Lightning Carol II']='Carol',['Water Carol II']='Carol',['Light Carol II']='Carol',['Dark Carol II']='Carol',
+ ['Foe Lullaby']='Lullaby',['Foe Lullaby II']='Lullaby',['Horde Lullaby']='Lullaby',['Horde Lullaby II']='Lullaby',
+ ['Fire Threnody']='Threnody',['Ice Threnody']='Threnody',['Wind Threnody']='Threnody',['Earth Threnody']='Threnody',['Lightning Threnody']='Threnody',['Water Threnody']='Threnody',['Light Threnody']='Threnody',['Dark Threnody']='Threnody',
+ ['Battlefield Elegy']='Elegy',['Carnage Elegy']='Elegy',
+ ['Foe Requiem']='Requiem',['Foe Requiem II']='Requiem',['Foe Requiem III']='Requiem',['Foe Requiem IV']='Requiem',['Foe Requiem V']='Requiem',['Foe Requiem VI']='Requiem',['Foe Requiem VII']='Requiem',
+ ['Utsusemi: Ichi']='Utsusemi',['Utsusemi: Ni']='Utsusemi',
+ ['Katon: Ichi'] = 'ElementalNinjutsu',['Suiton: Ichi'] = 'ElementalNinjutsu',['Raiton: Ichi'] = 'ElementalNinjutsu',
+ ['Doton: Ichi'] = 'ElementalNinjutsu',['Huton: Ichi'] = 'ElementalNinjutsu',['Hyoton: Ichi'] = 'ElementalNinjutsu',
+ ['Katon: Ni'] = 'ElementalNinjutsu',['Suiton: Ni'] = 'ElementalNinjutsu',['Raiton: Ni'] = 'ElementalNinjutsu',
+ ['Doton: Ni'] = 'ElementalNinjutsu',['Huton: Ni'] = 'ElementalNinjutsu',['Hyoton: Ni'] = 'ElementalNinjutsu',
+ ['Katon: San'] = 'ElementalNinjutsu',['Suiton: San'] = 'ElementalNinjutsu',['Raiton: San'] = 'ElementalNinjutsu',
+ ['Doton: San'] = 'ElementalNinjutsu',['Huton: San'] = 'ElementalNinjutsu',['Hyoton: San'] = 'ElementalNinjutsu',
+ ['Banish']='Banish',['Banish II']='Banish',['Banish III']='Banish',['Banishga']='Banish',['Banishga II']='Banish',
+ ['Holy']='Holy',['Holy II']='Holy',['Drain']='Drain',['Drain II']='Drain',['Aspir']='Aspir',['Aspir II']='Aspir',
+ ['Absorb-Str']='Absorb',['Absorb-Dex']='Absorb',['Absorb-Vit']='Absorb',['Absorb-Agi']='Absorb',['Absorb-Int']='Absorb',['Absorb-Mnd']='Absorb',['Absorb-Chr']='Absorb',
+ ['Absorb-Acc']='Absorb',['Absorb-TP']='Absorb',['Absorb-Attri']='Absorb',
+ ['Burn']='ElementalEnfeeble',['Frost']='ElementalEnfeeble',['Choke']='ElementalEnfeeble',['Rasp']='ElementalEnfeeble',['Shock']='ElementalEnfeeble',['Drown']='ElementalEnfeeble',
+ ['Pyrohelix']='Helix',['Cryohelix']='Helix',['Anemohelix']='Helix',['Geohelix']='Helix',['Ionohelix']='Helix',['Hydrohelix']='Helix',['Luminohelix']='Helix',['Noctohelix']='Helix',
+ ['Firestorm']='Storm',['Hailstorm']='Storm',['Windstorm']='Storm',['Sandstorm']='Storm',['Thunderstorm']='Storm',['Rainstorm']='Storm',['Aurorastorm']='Storm',['Voidstorm']='Storm',
+ ['Fire Maneuver']='Maneuver',['Ice Maneuver']='Maneuver',['Wind Maneuver']='Maneuver',['Earth Maneuver']='Maneuver',['Thunder Maneuver']='Maneuver',
+ ['Water Maneuver']='Maneuver',['Light Maneuver']='Maneuver',['Dark Maneuver']='Maneuver',
+}
+
+no_skill_spells_list = S{'Haste', 'Refresh', 'Regen', 'Protect', 'Protectra', 'Shell', 'Shellra',
+ 'Raise', 'Reraise', 'Sneak', 'Invisible', 'Deodorize'}
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Tables to specify general area groupings. Creates the 'areas' table to be referenced in job files.
+-- Zone names provided by world.area/world.zone are currently in all-caps, so defining the same way here.
+-------------------------------------------------------------------------------------------------------------------
+
+areas = {}
+
+-- City areas for town gear and behavior.
+areas.Cities = 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",
+ "Port Bastok",
+ "Bastok Markets",
+ "Bastok Mines",
+ "Metalworks",
+ "Aht Urhgan Whitegate",
+ "Tavanazian Safehold",
+ "Nashmau",
+ "Selbina",
+ "Mhaura",
+ "Norg",
+ "Eastern Adoulin",
+ "Western Adoulin",
+ "Kazham"
+}
+-- Adoulin areas, where Ionis will grant special stat bonuses.
+areas.Adoulin = S{
+ "Yahse Hunting Grounds",
+ "Ceizak Battlegrounds",
+ "Foret de Hennetiel",
+ "Morimar Basalt Fields",
+ "Yorcia Weald",
+ "Yorcia Weald [U]",
+ "Cirdas Caverns",
+ "Cirdas Caverns [U]",
+ "Marjami Ravine",
+ "Kamihr Drifts",
+ "Sih Gates",
+ "Moh Gates",
+ "Dho Gates",
+ "Woh Gates",
+ "Rala Waterways",
+ "Rala Waterways [U]",
+ "Outer Ra'Kaznar",
+ "Outer Ra'Kaznar [U]"
+}
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Lists of certain NPCs.
+-------------------------------------------------------------------------------------------------------------------
+
+npcs = {}
+npcs.Trust = S{'Ajido-Marujido','Aldo','Ayame','Cherukiki','Curilla','D.Shantotto','Elivira','Excenmille',
+ 'Fablinix','FerreousCoffin','Gadalar','Gessho','Ingrid','IronEater','Joachim','Klara','Kupipi',
+ 'LehkoHabhoka','LhuMhakaracca','Lion','Luzaf','Maat','MihliAliapoh','Mnejing','Moogle','Mumor',
+ 'NajaSalaheem','Najelith','Naji','NanaaMihgo','Nashmeira','Noillurie','Ovjang','Prishe','Rainemard',
+ 'RomaaMihgo','Sakura','Shantotto','StarSibyl','Tenzen','Trion','UkaTotlihn','Ulmia','Valaineral',
+ 'Volker','Zazarg','Zeid'}
+
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-SelfCommands.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-SelfCommands.lua
new file mode 100644
index 0000000..b15a70a
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-SelfCommands.lua
@@ -0,0 +1,715 @@
+-------------------------------------------------------------------------------------------------------------------
+-- General functions for manipulating state values via self-commands.
+-- Only handles certain specific states that we've defined, though it
+-- allows the user to hook into the cycle command.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Routing function for general known self_commands.
+-- Handles splitting the provided command line up into discrete words, for the other functions to use.
+function self_command(commandArgs)
+ local commandArgs = commandArgs
+ if type(commandArgs) == 'string' then
+ commandArgs = T(commandArgs:split(' '))
+ if #commandArgs == 0 then
+ return
+ end
+ end
+
+ -- init a new eventArgs
+ local eventArgs = {handled = false}
+
+ -- Allow jobs to override this code
+ if job_self_command then
+ job_self_command(commandArgs, eventArgs)
+ end
+
+ if not eventArgs.handled then
+ -- Of the original command message passed in, remove the first word from
+ -- the list (it will be used to determine which function to call), and
+ -- send the remaining words as parameters for the function.
+ local handleCmd = table.remove(commandArgs, 1)
+
+ if selfCommandMaps[handleCmd] then
+ selfCommandMaps[handleCmd](commandArgs)
+ end
+ end
+end
+
+
+-- Individual handling of self-commands
+
+
+-- Handle toggling specific vars that we know of.
+-- Valid toggles: Defense, Kiting
+-- Returns true if a known toggle was handled. Returns false if not.
+-- User command format: gs c toggle [field]
+function handle_toggle(cmdParams)
+ if #cmdParams == 0 then
+ add_to_chat(123,'Mote-GearSwap: Toggle parameter failure: field not specified.')
+ return
+ end
+
+ local toggleField = cmdParams[1]:lower()
+ local reportDescription
+ local notifyDescription
+ local oldVal
+ local newVal
+
+ -- Known global states
+ if toggleField == 'defense' then
+ oldVal = state.Defense.Active
+ state.Defense.Active = not state.Defense.Active
+ newVal = state.Defense.Active
+ notifyDescription = state.Defense.Type .. ' Defense'
+ if state.Defense.Type == 'Physical' then
+ reportDescription = 'Physical defense ('..state.Defense.PhysicalMode..')'
+ else
+ reportDescription = 'Magical defense ('..state.Defense.MagicalMode..')'
+ end
+ elseif toggleField == 'kite' or toggleField == 'kiting' then
+ oldVal = state.Kiting
+ state.Kiting = not state.Kiting
+ newVal = state.Kiting
+ notifyDescription = 'Kiting'
+ reportDescription = 'Kiting'
+ elseif toggleField == 'selectnpctargets' then
+ oldVal = state.SelectNPCTargets
+ state.SelectNPCTargets = not state.SelectNPCTargets
+ newVal = state.SelectNPCTargets
+ notifyDescription = 'NPC Target Selection'
+ reportDescription = 'NPC Target Selection'
+ elseif type(state[cmdParams[1]]) == 'boolean' then
+ oldVal = state[cmdParams[1]]
+ state[cmdParams[1]] = not state[cmdParams[1]]
+ newVal = state[cmdParams[1]]
+ notifyDescription = cmdParams[1]
+ reportDescription = cmdParams[1]
+ elseif job_toggle_state then
+ reportDescription, newVal = job_toggle_state(cmdParams[1])
+ end
+
+ -- Notify user file of changes to global states.
+ if oldVal ~= nil then
+ if job_state_change and newVal ~= oldVal then
+ job_state_change(notifyDescription, newVal, oldVal)
+ end
+ end
+
+ if reportDescription then
+ add_to_chat(122,reportDescription..' is now '..on_off_names[newVal]..'.')
+ handle_update({'auto'})
+ else
+ add_to_chat(123,'Mote-GearSwap: Toggle: Unknown field ['..toggleField..']')
+ end
+end
+
+
+-- Function to handle turning on particular states, while possibly also setting a mode value.
+-- User command format: gs c activate [field]
+function handle_activate(cmdParams)
+ if #cmdParams == 0 then
+ add_to_chat(123,'Mote-GearSwap: Activate parameter failure: field not specified.')
+ return
+ end
+
+ local activateField = cmdParams[1]:lower()
+ local reportDescription
+ local notifyDescription
+ local oldVal
+ local newVal = true
+
+ -- Known global states
+ if activateField == 'defense' then
+ oldVal = state.Defense.Active
+ state.Defense.Active = true
+ notifyDescription = state.Defense.Type .. ' Defense'
+ if state.Defense.Type == 'Physical' then
+ reportDescription = 'Physical defense ('..state.Defense.PhysicalMode..')'
+ else
+ reportDescription = 'Magical defense ('..state.Defense.MagicalMode..')'
+ end
+ elseif activateField == 'physicaldefense' then
+ oldVal = state.Defense.Active
+ state.Defense.Active = true
+ state.Defense.Type = 'Physical'
+ notifyDescription = state.Defense.Type .. ' Defense'
+ reportDescription = 'Physical defense ('..state.Defense.PhysicalMode..')'
+ elseif activateField == 'magicaldefense' then
+ oldVal = state.Defense.Active
+ state.Defense.Active = true
+ state.Defense.Type = 'Magical'
+ notifyDescription = state.Defense.Type .. ' Defense'
+ reportDescription = 'Magical defense ('..state.Defense.MagicalMode..')'
+ elseif activateField == 'kite' or toggleField == 'kiting' then
+ oldVal = state.Kiting
+ state.Kiting = true
+ notifyDescription = 'Kiting'
+ reportDescription = 'Kiting'
+ elseif activateField == 'selectnpctargets' then
+ oldVal = state.SelectNPCTargets
+ state.SelectNPCTargets = true
+ notifyDescription = 'NPC Target Selection'
+ reportDescription = 'NPC Target Selection'
+ elseif type(state[cmdParams[1]]) == 'boolean' then
+ oldVal = state[cmdParams[1]]
+ state[cmdParams[1]] = true
+ notifyDescription = cmdParams[1]
+ reportDescription = cmdParams[1]
+ elseif job_activate_state then
+ reportDescription, newVal = job_activate_state(cmdParams[1])
+ end
+
+ -- Notify user file of changes to global states.
+ if oldVal ~= nil then
+ if job_state_change and newVal ~= oldVal then
+ job_state_change(notifyDescription, newVal, oldVal)
+ end
+ end
+
+ if reportDescription then
+ add_to_chat(122,reportDescription..' is now '..on_off_names[newVal]..'.')
+ handle_update({'auto'})
+ else
+ add_to_chat(123,'Mote-GearSwap: Activate: Unknown field ['..activateField..']')
+ end
+end
+
+
+-- Handle cycling through the options for specific vars that we know of.
+-- Valid fields: OffenseMode, DefenseMode, WeaponskillMode, IdleMode, RestingMode, CastingMode, PhysicalDefenseMode, MagicalDefenseMode
+-- All fields must end in 'Mode'
+-- Returns true if a known toggle was handled. Returns false if not.
+-- User command format: gs c cycle [field]
+function handle_cycle(cmdParams)
+ if #cmdParams == 0 then
+ add_to_chat(123,'Mote-GearSwap: Cycle parameter failure: field not specified.')
+ return
+ end
+
+ -- identifier for the field we're changing
+ local paramField = cmdParams[1]
+ local modeField = paramField
+ local order = (cmdParams[2] and S{'reverse', 'backwards', 'r'}:contains(cmdParams[2]:lower()) and 'backwards') or 'forward'
+
+ if paramField:endswith('mode') or paramField:endswith('Mode') then
+ -- Remove 'mode' from the end of the string
+ modeField = paramField:sub(1,#paramField-4)
+ end
+
+ -- Convert WS to Weaponskill
+ if modeField == "ws" then
+ modeField = "weaponskill"
+ end
+
+ -- Capitalize the field (for use on output display)
+ modeField = modeField:gsub("%f[%a]%a", string.upper)
+
+ -- Get the options.XXXModes table, and the current state mode for the mode field.
+ local modeList, currentValue = get_mode_list(modeField)
+
+ if not modeList then
+ if _global.debug_mode then add_to_chat(123,'Unknown mode : '..modeField..'.') end
+ return
+ end
+
+ -- Get the index of the current mode. Index starts at 0 for 'undefined', so that it can increment to 1.
+ local invertedTable = invert_table(modeList)
+ local index = 0
+ if invertedTable[currentValue] then
+ index = invertedTable[currentValue]
+ end
+
+ -- Increment to the next index in the available modes.
+ if order == 'forward' then
+ index = index + 1
+ if index > #modeList then
+ index = 1
+ end
+ else
+ index = index - 1
+ if index < 1 then
+ index = #modeList
+ end
+ end
+
+ -- Determine the new mode value based on the index.
+ local newModeValue = ''
+ if index and modeList[index] then
+ newModeValue = modeList[index]
+ else
+ newModeValue = 'Normal'
+ end
+
+ -- And save that to the appropriate state field.
+ set_option_mode(modeField, newModeValue)
+
+ if job_state_change and newModeValue ~= currentValue then
+ job_state_change(modeField..'Mode', newModeValue, currentValue)
+ end
+
+ -- Display what got changed to the user.
+ add_to_chat(122,modeField..' mode is now '..newModeValue..'.')
+ handle_update({'auto'})
+end
+
+-- Function to set various states to specific values directly.
+-- User command format: gs c set [field] [value]
+function handle_set(cmdParams)
+ if #cmdParams > 1 then
+ -- identifier for the field we're setting
+ local field = cmdParams[1]
+ local lowerField = field:lower()
+ local capField = lowerField:gsub("%a", string.upper, 1)
+ local setField = cmdParams[2]
+ local reportDescription
+ local notifyDescription
+ local fieldDesc
+ local oldVal
+ local newVal
+
+
+ -- Check if we're dealing with a boolean
+ if on_off_values:contains(setField:lower()) then
+ newVal = true_values:contains(setField:lower())
+
+ if lowerField == 'defense' then
+ oldVal = state.Defense.Active
+ state.Defense.Active = newVal
+ notifyDescription = state.Defense.Type .. ' Defense'
+ if state.Defense.Type == 'Physical' then
+ reportDescription = 'Physical defense ('..state.Defense.PhysicalMode..')'
+ else
+ reportDescription = 'Magical defense ('..state.Defense.MagicalMode..')'
+ end
+ elseif lowerField == 'kite' or lowerField == 'kiting' then
+ oldVal = state.Kiting
+ state.Kiting = newVal
+ notifyDescription = 'Kiting'
+ reportDescription = 'Kiting'
+ elseif lowerField == 'selectnpctargets' then
+ oldVal = state.SelectNPCTargets
+ state.SelectNPCTargets = newVal
+ notifyDescription = 'NPC Target Selection'
+ reportDescription = 'NPC Target Selection'
+ elseif type(state[field]) == 'boolean' then
+ oldVal = state[field]
+ state[field] = newVal
+ notifyDescription = field
+ reportDescription = field
+ elseif job_set_state then
+ reportDescription, newVal = job_set_state(field, newVal)
+ end
+
+
+ -- Notify user file of changes to global states.
+ if oldVal ~= nil then
+ if job_state_change and newVal ~= oldVal then
+ job_state_change(notifyDescription, newVal, oldVal)
+ end
+ end
+
+ if reportDescription then
+ add_to_chat(122,reportDescription..' is now '..on_off_names[newVal]..'.')
+ else
+ add_to_chat(123,'Mote-GearSwap: Set: Unknown field ['..field..']')
+ end
+ -- Check if we're dealing with some sort of cycle field (ends with 'mode').
+ elseif lowerField:endswith('mode') or type(state[capField..'Mode']) == 'string' then
+ local modeField = lowerField
+
+ -- Remove 'mode' from the end of the string
+ if modeField:endswith('mode') then
+ modeField = lowerField:sub(1,#lowerField-4)
+ end
+
+ -- Convert WS to Weaponskill
+ if modeField == "ws" then
+ modeField = "weaponskill"
+ end
+
+ -- Capitalize the field (for use on output display)
+ modeField = modeField:gsub("%a", string.upper, 1)
+
+ -- Get the options.XXXModes table, and the current state mode for the mode field.
+ local modeList
+ modeList, oldVal = get_mode_list(modeField)
+
+ if not modeList or not table.contains(modeList, setField) then
+ add_to_chat(123,'Unknown mode value: '..setField..' for '..modeField..' mode.')
+ return
+ end
+
+ -- And save that to the appropriate state field.
+ set_option_mode(modeField, setField)
+
+ -- Notify the job script of the change.
+ if job_state_change and setField ~= oldVal then
+ job_state_change(modeField, setField, oldVal)
+ end
+
+ -- Display what got changed to the user.
+ add_to_chat(122,modeField..' mode is now '..setField..'.')
+ -- Or distance (where we may need to get game state info)
+ elseif lowerField == 'distance' then
+ if setField then
+ newVal = tonumber(setField)
+ if newVal ~= nil then
+ oldVal = state.MaxWeaponskillDistance
+ state.MaxWeaponskillDistance = newVal
+ else
+ add_to_chat(123,'Invalid distance value: '..tostring(setField))
+ return
+ end
+
+ -- Notify the job script of the change.
+ if job_state_change and newVal ~= oldVal then
+ job_state_change('MaxWeaponskillDistance', newVal, oldVal)
+ end
+
+ add_to_chat(122,'Max weaponskill distance is now '..tostring(newVal)..'.')
+ else
+ -- set max weaponskill distance to the current distance the player is from the mob.
+ -- Get current player distance and use that
+ add_to_chat(123,'TODO: get player distance.')
+ end
+ -- If trying to set a number
+ elseif tonumber(setField) then
+ if state[field] and type(state[field]) == 'number' then
+ oldVal = state[field]
+ newVal = tonumber(setField)
+ state[field] = newVal
+ reportDescription = field
+
+ -- Notify the job script of the change.
+ if job_state_change and newVal ~= oldVal then
+ job_state_change(field, newVal, oldVal)
+ end
+ elseif state[field] then
+ add_to_chat(123,'Mote-GearSwap: Set: Attempting to set a numeric value ['..setField..'] in a non-numeric field ['..field..'].')
+ return
+ elseif job_set_state then
+ reportDescription, newVal = job_set_state(field, setField)
+ end
+
+ if reportDescription then
+ add_to_chat(122,field..' is now '..tostring(newVal)..'.')
+ else
+ add_to_chat(123,'Mote-GearSwap: Set: Unknown field ['..field..']')
+ end
+ -- otherwise assume trying to set a text field
+ else
+ if lowerField == 'combatform' then
+ oldVal = state.CombatForm
+ state.CombatForm = setField
+ newVal = setField
+ notifyDescription = 'Combat Form'
+ reportDescription = 'Combat Form'
+ elseif lowerField == 'combatweapon' then
+ oldVal = state.CombatWeapon
+ state.CombatWeapon = setField
+ newVal = setField
+ notifyDescription = 'Combat Weapon'
+ reportDescription = 'Combat Weapon'
+ elseif state[field] and type(state[field]) == 'string' then
+ oldVal = state[field]
+ state[field] = setField
+ newVal = setField
+ notifyDescription = field
+ reportDescription = field
+ elseif job_set_state then
+ reportDescription, newVal = job_set_state(field, setField)
+ end
+
+ -- Notify user file of changes to global states.
+ if oldVal ~= nil then
+ if job_state_change and newVal ~= oldVal then
+ job_state_change(notifyDescription, newVal, oldVal)
+ end
+ end
+
+ if reportDescription then
+ add_to_chat(122,reportDescription..' is now '..newVal..'.')
+ else
+ add_to_chat(123,'Mote-GearSwap: Set: Unknown field ['..field..']')
+ end
+ end
+ else
+ if _global.debug_mode then add_to_chat(123,'--handle_set parameter failure: insufficient fields') end
+ return false
+ end
+
+ handle_update({'auto'})
+ return true
+end
+
+
+-- Function to turn off togglable features, or reset values to their defaults.
+-- User command format: gs c reset [field]
+function handle_reset(cmdParams)
+ if #cmdParams == 0 then
+ if _global.debug_mode then add_to_chat(123,'handle_reset: parameter failure: reset type not specified') end
+ return
+ end
+
+ resetState = cmdParams[1]:lower()
+
+ if resetState == 'defense' then
+ state.Defense.Active = false
+ add_to_chat(122,state.Defense.Type..' defense is now off.')
+ elseif resetState == 'kite' or resetState == 'kiting' then
+ state.Kiting = false
+ add_to_chat(122,'Kiting is now off.')
+ elseif resetState == 'melee' then
+ state.OffenseMode = options.OffenseModes[1]
+ state.DefenseMode = options.DefenseModes[1]
+ add_to_chat(122,'Melee has been reset to defaults.')
+ elseif resetState == 'casting' then
+ state.CastingMode = options.CastingModes[1]
+ add_to_chat(122,'Casting has been reset to default.')
+ elseif resetState == 'distance' then
+ state.MaxWeaponskillDistance = 0
+ add_to_chat(122,'Max weaponskill distance limitations have been removed.')
+ elseif resetState == 'target' then
+ state.SelectNPCTargets = false
+ state.PCTargetMode = 'default'
+ add_to_chat(122,'Adjusting target selection has been turned off.')
+ elseif resetState == 'all' then
+ state.Defense.Active = false
+ state.Defense.PhysicalMode = options.PhysicalDefenseModes[1]
+ state.Defense.MagicalMode = options.MagicalDefenseModes[1]
+ state.Kiting = false
+ state.OffenseMode = options.OffenseModes[1]
+ state.DefenseMode = options.DefenseModes[1]
+ state.CastingMode = options.CastingModes[1]
+ state.IdleMode = options.IdleModes[1]
+ state.RestingMode = options.RestingModes[1]
+ state.MaxWeaponskillDistance = 0
+ state.SelectNPCTargets = false
+ state.PCTargetMode = 'default'
+ mote_vars.show_set = nil
+ if job_reset then
+ job_reset(resetState)
+ end
+ add_to_chat(122,'Everything has been reset to defaults.')
+ elseif job_reset then
+ job_reset(resetState)
+ else
+ add_to_chat(123,'handle_reset: unknown state to reset: '..resetState)
+ return
+ end
+
+ if job_state_change then
+ job_state_change('Reset', resetState)
+ end
+
+ handle_update({'auto'})
+end
+
+
+-- User command format: gs c update [option]
+-- Where [option] can be 'user' to display current state.
+-- Otherwise, generally refreshes current gear used.
+function handle_update(cmdParams)
+ -- init a new eventArgs
+ local eventArgs = {handled = false}
+
+ reset_buff_states()
+
+ -- Allow jobs to override this code
+ if job_update then
+ job_update(cmdParams, eventArgs)
+ end
+
+ if not eventArgs.handled then
+ if handle_equipping_gear then
+ handle_equipping_gear(player.status)
+ end
+ end
+
+ if cmdParams[1] == 'user' then
+ display_current_state()
+ end
+end
+
+
+-- showset: equip the current TP set for examination.
+function handle_show_set(cmdParams)
+ local showset_type
+ if cmdParams[1] then
+ showset_type = cmdParams[1]:lower()
+ end
+
+ -- If no extra parameters, or 'tp' as a parameter, show the current TP set.
+ if not showset_type or showset_type == 'tp' then
+ local meleeGroups = ''
+ if #classes.CustomMeleeGroups > 0 then
+ meleeGroups = ' ['
+ for i = 1,#classes.CustomMeleeGroups do
+ meleeGroups = meleeGroups..classes.CustomMeleeGroups[i]
+ end
+ meleeGroups = meleeGroups..']'
+ end
+
+ add_to_chat(122,'Showing current TP set: ['..state.OffenseMode..'/'..state.DefenseMode..']'..meleeGroups)
+ equip(get_melee_set())
+ -- If given a param of 'precast', block equipping midcast/aftercast sets
+ elseif showset_type == 'precast' then
+ mote_vars.show_set = 'precast'
+ add_to_chat(122,'GearSwap will now only equip up to precast gear for spells/actions.')
+ -- If given a param of 'midcast', block equipping aftercast sets
+ elseif showset_type == 'midcast' then
+ mote_vars.show_set = 'midcast'
+ add_to_chat(122,'GearSwap will now only equip up to midcast gear for spells.')
+ -- If given a param of 'midcast', block equipping aftercast sets
+ elseif showset_type == 'petmidcast' or showset_type == 'pet_midcast' then
+ mote_vars.show_set = 'pet_midcast'
+ add_to_chat(122,'GearSwap will now only equip up to pet midcast gear for spells.')
+ -- With a parameter of 'off', turn off showset functionality.
+ elseif showset_type == 'off' then
+ mote_vars.show_set = nil
+ add_to_chat(122,'Show Sets is turned off.')
+ end
+end
+
+-- Minor variation on the GearSwap "gs equip naked" command, that ensures that
+-- all slots are enabled before removing gear.
+-- Command: "gs c naked"
+function handle_naked(cmdParams)
+ enable('main','sub','range','ammo','head','neck','lear','rear','body','hands','lring','rring','back','waist','legs','feet')
+ equip(sets.naked)
+end
+
+
+------ Utility functions to support self commands. ------
+
+-- Function to get the options.XXXModes list and the corresponding state value for the requested field.
+function get_mode_list(field)
+ local modeList = {}
+ local currentValue = ''
+ local lowerField = field:lower()
+
+ if type(state[field..'Mode']) == 'string' and type(options[field..'Modes']) == 'table' then
+ -- Handles: Offense, Defense, Ranged, Casting, Weaponskill, Idle, Resting modes
+ modeList = options[field..'Modes']
+ currentValue = state[field..'Mode']
+ elseif lowerField == 'physicaldefense' then
+ modeList = options.PhysicalDefenseModes
+ currentValue = state.Defense.PhysicalMode
+ elseif lowerField == 'magicaldefense' then
+ modeList = options.MagicalDefenseModes
+ currentValue = state.Defense.MagicalMode
+ elseif lowerField == 'pctarget' then
+ modeList = options.TargetModes
+ currentValue = state.PCTargetMode
+ elseif type(state[field..'Mode']) == 'string' and type(options[field..'Modes']) ~= 'table' then
+ -- naming conflict
+ add_to_chat(123,'No valid options table for field: '..field)
+ elseif type(state[field..'Mode']) ~= 'string' and type(options[field..'Modes']) == 'table' then
+ -- naming conflict
+ add_to_chat(123,'No valid state string for field: '..field)
+ elseif job_get_option_modes then
+ -- Allow job scripts to expand the mode table lists
+ modeList, currentValue = job_get_option_modes(field)
+ if not modeList then
+ add_to_chat(123,'Attempt to acquire options list for unknown state field: '..field)
+ return nil
+ end
+ else
+ add_to_chat(123,'Attempt to acquire options list for unknown state field: '..field)
+ return nil
+ end
+
+ return modeList, currentValue
+end
+
+-- Function to set the appropriate state value for the specified field.
+function set_option_mode(field, val)
+ local lowerField = field:lower()
+
+ if type(state[field..'Mode']) == 'string' then
+ -- Handles: Offense, Defense, Ranged, Casting, Weaponskill, Idle, Resting modes
+ state[field..'Mode'] = val
+ elseif lowerField == 'physicaldefense' then
+ state.Defense.PhysicalMode = val
+ elseif lowerField == 'magicaldefense' then
+ state.Defense.MagicalMode = val
+ elseif lowerField == 'pctarget' then
+ state.PCTargetMode = val
+ elseif job_set_option_mode then
+ -- Allow job scripts to expand the mode table lists
+ if not job_set_option_mode(field, val) then
+ add_to_chat(123,'Attempt to set unknown option field: '..field)
+ end
+ else
+ add_to_chat(123,'Attempt to set unknown option field: '..field)
+ end
+end
+
+
+-- Function to display the current relevant user state when doing an update.
+-- Uses display_current_job_state instead if that is defined in the job lua.
+function display_current_state()
+ local eventArgs = {handled = false}
+ if display_current_job_state then
+ display_current_job_state(eventArgs)
+ end
+
+ if not eventArgs.handled then
+ local defenseString = ''
+ if state.Defense.Active then
+ local defMode = state.Defense.PhysicalMode
+ if state.Defense.Type == 'Magical' then
+ defMode = state.Defense.MagicalMode
+ end
+
+ defenseString = 'Defense: '..state.Defense.Type..' '..defMode..', '
+ end
+
+ local pcTarget = ''
+ if state.PCTargetMode ~= 'default' then
+ pcTarget = ', Target PC: '..state.PCTargetMode
+ end
+
+ local npcTarget = ''
+ if state.SelectNPCTargets then
+ pcTarget = ', Target NPCs'
+ end
+
+
+ add_to_chat(122,'Melee: '..state.OffenseMode..'/'..state.DefenseMode..', WS: '..state.WeaponskillMode..', '..defenseString..
+ 'Kiting: '..on_off_names[state.Kiting]..pcTarget..npcTarget)
+ end
+
+ if mote_vars.show_set then
+ add_to_chat(122,'Show Sets it currently showing ['..mote_vars.show_set..'] sets. Use "//gs c showset off" to turn it off.')
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Test functions.
+-------------------------------------------------------------------------------------------------------------------
+
+-- A function for testing lua code. Called via "gs c test".
+function handle_test(cmdParams)
+ if user_test then
+ user_test(cmdParams)
+ end
+end
+
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- The below table maps text commands to the above handler functions.
+-------------------------------------------------------------------------------------------------------------------
+
+selfCommandMaps = {
+ ['toggle'] = handle_toggle,
+ ['activate'] = handle_activate,
+ ['cycle'] = handle_cycle,
+ ['set'] = handle_set,
+ ['reset'] = handle_reset,
+ ['update'] = handle_update,
+ ['showset'] = handle_show_set,
+ ['naked'] = handle_naked,
+ ['test'] = handle_test}
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-TreasureHunter.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-TreasureHunter.lua
new file mode 100644
index 0000000..7367e85
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-TreasureHunter.lua
@@ -0,0 +1,297 @@
+-------------------------------------------------------------------------------------------------------------------
+-- Utility include for applying and tracking Treasure Hunter effects.
+--
+-- Include this if you want a means of applying TH on the first contact
+-- with a mob, then resume using normal gear.
+-- Thf also has modes to use TH gear for SATA and for keeping it on fulltime.
+--
+-- Command: include('Mote-TreasureHunter')
+-- Place this in your job_setup() function, or user_setup() function if using
+-- a sidecar file, or get_sets() function if your job file isn't based
+-- on my includes.
+-- Be sure to define your own sets.TreasureHunter gear set after the include.
+-- If using a job file based on my includes, simply place it in the
+-- standard init_gear_sets() function.
+--
+-- If you define TH gear sets for common actions (eg: Provoke, Box Step, etc),
+-- then make sure they are accounted for in a th_action_check function
+-- (signature: th_action_check(category, param)) in the job file. It's passed
+-- category and param value for actions the user takes, and if it returns true,
+-- that means that it's considered a valid tagging action.
+--
+-- If using this in a job file that isn't based on my includes, you must
+-- handle cycling the options values on your own, unless you also include
+-- Mote-SelfCommands.
+--
+-- The job file must handle the 'update' self-command (gs c update auto).
+-- This is automatically handled if using my includes, but must be ensured
+-- if being used with a user-built job file.
+-- When called, it merely needs to equip standard melee gear for the current
+-- configuration.
+--
+-- Create a macro or keybind to cycle the Treasure Mode value:
+-- gs c cycle TreasureMode
+-------------------------------------------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Setup vars and events when first running the include.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Ensure base tables are defined
+options = options or {}
+state = state or {}
+info = info or {}
+
+-- TH mode handling
+if player.main_job == 'THF' then
+ options.TreasureModes = {'None','Tag','SATA','Fulltime'}
+ state.TreasureMode = 'Tag'
+else
+ options.TreasureModes = {'None','Tag'}
+ state.TreasureMode = 'None'
+end
+
+-- Tracking vars for TH.
+info.tagged_mobs = T{}
+info.last_player_target_index = 0
+state.th_gear_is_locked = false
+
+-- Required gear set. Expand this in the job file when defining sets.
+sets.TreasureHunter = {}
+
+-- Event registration is done at the bottom of this file.
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- User-callable functions for TH handling utility.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Can call to force a status refresh.
+-- Also displays the current tagged mob table if in debug mode.
+function th_update(cmdParams, eventArgs)
+ if (cmdParams and cmdParams[1] == 'user') or not cmdParams then
+ TH_for_first_hit()
+
+ if _settings.debug_mode then
+ print_set(info.tagged_mobs, 'Tagged mobs')
+ end
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Local functions to support TH handling.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Set locked TH flag to true, and disable relevant gear slots.
+function lock_TH()
+ state.th_gear_is_locked = true
+ local slots = T{}
+ for slot,item in pairs(sets.TreasureHunter) do
+ slots:append(slot)
+ end
+ disable(slots)
+end
+
+
+-- Set locked TH flag to false, and enable relevant gear slots.
+function unlock_TH()
+ state.th_gear_is_locked = false
+ local slots = T{}
+ for slot,item in pairs(sets.TreasureHunter) do
+ slots:append(slot)
+ end
+ enable(slots)
+ send_command('gs c update auto')
+end
+
+
+-- For any active TH mode, if we haven't already tagged this target, equip TH gear and lock slots until we manage to hit it.
+function TH_for_first_hit()
+ if player.status == 'Engaged' and state.TreasureMode ~= 'None' then
+ if not info.tagged_mobs[player.target.id] then
+ if _settings.debug_mode then add_to_chat(123,'Prepping for first hit on '..tostring(player.target.id)..'.') end
+ equip(sets.TreasureHunter)
+ lock_TH()
+ elseif state.th_gear_is_locked then
+ if _settings.debug_mode then add_to_chat(123,'Target '..player.target.id..' has been tagged. Unlocking.') end
+ unlock_TH()
+ else
+ if _settings.debug_mode then add_to_chat(123,'Prepping for first hit on '..player.target.id..'. Target has already been tagged.') end
+ end
+ else
+ unlock_TH()
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Event handlers to allow tracking TH application.
+-------------------------------------------------------------------------------------------------------------------
+
+-- On engaging a mob, attempt to add TH gear. For any other status change, unlock TH gear slots.
+function on_status_change_for_th(new_status_id, old_status_id)
+ if gearswap.gearswap_disabled or T{2,3,4}:contains(old_status_id) or T{2,3,4}:contains(new_status_id) then return end
+
+ local new_status = gearswap.res.statuses[new_status_id].english
+ local old_status = gearswap.res.statuses[old_status_id].english
+
+ if new_status == 'Engaged' then
+ if _settings.debug_mode then add_to_chat(123,'Engaging '..player.target.id..'.') end
+ info.last_player_target_index = player.target.index
+ TH_for_first_hit()
+ elseif old_status == 'Engaged' then
+ if _settings.debug_mode and state.th_gear_is_locked then add_to_chat(123,'Disengaging. Unlocking TH.') end
+ info.last_player_target_index = 0
+ unlock_TH()
+ end
+end
+
+
+-- On changing targets, attempt to add TH gear.
+function on_target_change_for_th(new_index, old_index)
+ -- Only care about changing targets while we're engaged, either manually or via current target death.
+ if player.status == 'Engaged' then
+ -- If the current player.target is the same as the new mob then we're actually
+ -- engaged with it.
+ -- If it's different than the last known mob, then we've actually changed targets.
+ if player.target.index == new_index and new_index ~= info.last_player_target_index then
+ if _settings.debug_mode then add_to_chat(123,'Changing target to '..player.target.id..'.') end
+ info.last_player_target_index = player.target.index
+ TH_for_first_hit()
+ end
+ end
+end
+
+
+-- On any action event, mark mobs that we tag with TH. Also, update the last time tagged mobs were acted on.
+function on_action_for_th(action)
+ --add_to_chat(123,'cat='..action.category..',param='..action.param)
+ -- If player takes action, adjust TH tagging information
+ if state.TreasureMode ~= 'None' then
+ if action.actor_id == player.id then
+ -- category == 1=melee, 2=ranged, 3=weaponskill, 4=spell, 6=job ability, 14=unblinkable JA
+ if state.TreasureMode == 'Fulltime' or
+ (state.TreasureMode == 'SATA' and (action.category == 1 or ((state.Buff['Sneak Attack'] or state.Buff['Trick Attack']) and action.category == 3))) or
+ (state.TreasureMode == 'Tag' and action.category == 1 and state.th_gear_is_locked) or -- Tagging with a melee hit
+ (th_action_check and th_action_check(action.category, action.param)) -- Any user-specified tagging actions
+ then
+ for index,target in pairs(action.targets) do
+ if not info.tagged_mobs[target.id] and _settings.debug_mode then
+ add_to_chat(123,'Mob '..target.id..' hit. Adding to tagged mobs table.')
+ end
+ info.tagged_mobs[target.id] = os.time()
+ end
+
+ if state.th_gear_is_locked then
+ unlock_TH()
+ end
+ end
+ elseif info.tagged_mobs[action.actor_id] then
+ -- If mob acts, keep an update of last action time for TH bookkeeping
+ info.tagged_mobs[action.actor_id] = os.time()
+ else
+ -- If anyone else acts, check if any of the targets are our tagged mobs
+ for index,target in pairs(action.targets) do
+ if info.tagged_mobs[target.id] then
+ info.tagged_mobs[target.id] = os.time()
+ end
+ end
+ end
+ end
+
+ cleanup_tagged_mobs()
+end
+
+
+-- Need to use this event handler to listen for deaths in case Battlemod is loaded,
+-- because Battlemod blocks the 'action message' event.
+--
+-- This function removes mobs from our tracking table when they die.
+function on_incoming_chunk_for_th(id, data, modified, injected, blocked)
+ if id == 0x29 then
+ local target_id = data:unpack('I',0x09)
+ local message_id = data:unpack('H',0x19)%32768
+
+ -- Remove mobs that die from our tagged mobs list.
+ if info.tagged_mobs[target_id] then
+ -- 6 == actor defeats target
+ -- 20 == target falls to the ground
+ if message_id == 6 or message_id == 20 then
+ if _settings.debug_mode then add_to_chat(123,'Mob '..target_id..' died. Removing from tagged mobs table.') end
+ info.tagged_mobs[target_id] = nil
+ end
+ end
+ end
+end
+
+
+-- Clear out the entire tagged mobs table when zoning.
+function on_zone_change_for_th(new_zone, old_zone)
+ if _settings.debug_mode then add_to_chat(123,'Zoning. Clearing tagged mobs table.') end
+ info.tagged_mobs:clear()
+end
+
+
+-- Save the existing function, if it exists, and call it after our own handling.
+if job_state_change then
+ job_state_change_via_th = job_state_change
+end
+
+
+-- Called if we change any user state fields.
+function job_state_change(stateField, newValue, oldValue)
+ if stateField == 'TreasureMode' then
+ if newValue == 'None' and state.th_gear_is_locked then
+ if _settings.debug_mode then add_to_chat(123,'TH Mode set to None. Unlocking gear.') end
+ unlock_TH()
+ elseif oldValue == 'None' then
+ TH_for_first_hit()
+ end
+ end
+
+ if job_state_change_via_th then
+ job_state_change_via_th(stateField, newValue, oldValue)
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Extra utility functions.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Remove mobs that we've marked as tagged with TH if we haven't seen any activity from or on them
+-- for over 3 minutes. This is to handle deagros, player deaths, or other random stuff where the
+-- mob is lost, but doesn't die.
+function cleanup_tagged_mobs()
+ -- If it's been more than 3 minutes since an action on or by a tagged mob,
+ -- remove them from the tagged mobs list.
+ local current_time = os.time()
+ local remove_mobs = S{}
+ -- Search list and flag old entries.
+ for target_id,action_time in pairs(info.tagged_mobs) do
+ local time_since_last_action = current_time - action_time
+ if time_since_last_action > 180 then
+ remove_mobs:add(target_id)
+ if _settings.debug_mode then add_to_chat(123,'Over 3 minutes since last action on mob '..target_id..'. Removing from tagged mobs list.') end
+ end
+ end
+ -- Clean out mobs flagged for removal.
+ for mob_id,_ in pairs(remove_mobs) do
+ info.tagged_mobs[mob_id] = nil
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Event function registration calls.
+-- Can call these now that the above functions have been defined.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Register events to allow us to manage TH application.
+windower.register_event('status change', on_status_change_for_th)
+windower.register_event('target change', on_target_change_for_th)
+windower.raw_register_event('action', on_action_for_th)
+windower.raw_register_event('incoming chunk', on_incoming_chunk_for_th)
+windower.raw_register_event('zone change', on_zone_change_for_th)
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Utility.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Utility.lua
new file mode 100644
index 0000000..8cf3c49
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-Utility.lua
@@ -0,0 +1,672 @@
+-------------------------------------------------------------------------------------------------------------------
+-- General utility functions that can be used by any job files.
+-- Outside the scope of what the main include file deals with.
+-------------------------------------------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------------------------------------------
+-- Buff utility functions.
+-------------------------------------------------------------------------------------------------------------------
+
+local cancel_spells_to_check = S{'Sneak', 'Stoneskin', 'Spectral Jig', 'Trance', 'Monomi: Ichi', 'Utsusemi: Ichi'}
+local cancel_types_to_check = S{'Waltz', 'Samba'}
+
+-- Function to cancel buffs if they'd conflict with using the spell you're attempting.
+-- Requirement: Must have Cancel addon installed and loaded for this to work.
+function cancel_conflicting_buffs(spell, action, spellMap, eventArgs)
+ if cancel_spells_to_check:contains(spell.english) or cancel_types_to_check:contains(spell.type) then
+ if spell.action_type == 'Ability' then
+ local abil_recasts = windower.ffxi.get_ability_recasts()
+ if abil_recasts[spell.recast_id] > 0 then
+ add_to_chat(123,'Abort: Ability waiting on recast.')
+ eventArgs.cancel = true
+ return
+ end
+ elseif spell.action_type == 'Magic' then
+ local spell_recasts = windower.ffxi.get_spell_recasts()
+ if spell_recasts[spell.recast_id] > 0 then
+ add_to_chat(123,'Abort: Spell waiting on recast.')
+ eventArgs.cancel = true
+ return
+ end
+ end
+
+ if spell.english == 'Spectral Jig' and buffactive.sneak then
+ cast_delay(0.2)
+ send_command('cancel sneak')
+ elseif spell.english == 'Sneak' and spell.target.type == 'SELF' and buffactive.sneak then
+ send_command('cancel sneak')
+ elseif spell.english == ('Stoneskin') then
+ send_command('@wait 1.0;cancel stoneskin')
+ elseif spell.english:startswith('Monomi') then
+ send_command('@wait 1.7;cancel sneak')
+ elseif spell.english == 'Utsusemi: Ichi' then
+ send_command('@wait 1.7;cancel copy image*')
+ elseif (spell.english == 'Trance' or spell.type=='Waltz') and buffactive['saber dance'] then
+ cast_delay(0.2)
+ send_command('cancel saber dance')
+ elseif spell.type=='Samba' and buffactive['fan dance'] then
+ cast_delay(0.2)
+ send_command('cancel fan dance')
+ end
+ end
+end
+
+
+-- Some mythics have special durations for level 1 and 2 aftermaths
+local special_aftermath_mythics = S{'Tizona', 'Kenkonken', 'Murgleis', 'Yagrush', 'Carnwenhan', 'Nirvana', 'Tupsimati', 'Idris'}
+
+-- Call from job_precast() to setup aftermath information for custom timers.
+function custom_aftermath_timers_precast(spell)
+ if spell.type == 'WeaponSkill' then
+ info.aftermath = {}
+
+ local relic_ws = data.weaponskills.relic[player.equipment.main] or data.weaponskills.relic[player.equipment.range]
+ local mythic_ws = data.weaponskills.mythic[player.equipment.main] or data.weaponskills.mythic[player.equipment.range]
+ local empy_ws = data.weaponskills.empyrean[player.equipment.main] or data.weaponskills.empyrean[player.equipment.range]
+
+ if not relic_ws and not mythic_ws and not empy_ws then
+ return
+ end
+
+ info.aftermath.weaponskill = spell.english
+ info.aftermath.duration = 0
+
+ info.aftermath.level = math.floor(player.tp / 1000)
+ if info.aftermath.level == 0 then
+ info.aftermath.level = 1
+ end
+
+ if spell.english == relic_ws then
+ info.aftermath.duration = math.floor(0.2 * player.tp)
+ if info.aftermath.duration < 20 then
+ info.aftermath.duration = 20
+ end
+ elseif spell.english == empy_ws then
+ -- nothing can overwrite lvl 3
+ if buffactive['Aftermath: Lv.3'] then
+ return
+ end
+ -- only lvl 3 can overwrite lvl 2
+ if info.aftermath.level ~= 3 and buffactive['Aftermath: Lv.2'] then
+ return
+ end
+
+ -- duration is based on aftermath level
+ info.aftermath.duration = 30 * info.aftermath.level
+ elseif spell.english == mythic_ws then
+ -- nothing can overwrite lvl 3
+ if buffactive['Aftermath: Lv.3'] then
+ return
+ end
+ -- only lvl 3 can overwrite lvl 2
+ if info.aftermath.level ~= 3 and buffactive['Aftermath: Lv.2'] then
+ return
+ end
+
+ -- Assume mythic is lvl 80 or higher, for duration
+
+ if info.aftermath.level == 1 then
+ info.aftermath.duration = (special_aftermath_mythics:contains(player.equipment.main) and 270) or 90
+ elseif info.aftermath.level == 2 then
+ info.aftermath.duration = (special_aftermath_mythics:contains(player.equipment.main) and 270) or 120
+ else
+ info.aftermath.duration = 180
+ end
+ end
+ end
+end
+
+
+-- Call from job_aftercast() to create the custom aftermath timer.
+function custom_aftermath_timers_aftercast(spell)
+ if not spell.interrupted and spell.type == 'WeaponSkill' and
+ info.aftermath and info.aftermath.weaponskill == spell.english and info.aftermath.duration > 0 then
+
+ local aftermath_name = 'Aftermath: Lv.'..tostring(info.aftermath.level)
+ send_command('timers d "Aftermath: Lv.1"')
+ send_command('timers d "Aftermath: Lv.2"')
+ send_command('timers d "Aftermath: Lv.3"')
+ send_command('timers c "'..aftermath_name..'" '..tostring(info.aftermath.duration)..' down abilities/00027.png')
+
+ info.aftermath = {}
+ end
+end
+
+
+-- Function to reset state.Buff values.
+function reset_buff_states()
+ if state.Buff then
+ for buff,present in pairs(state.Buff) do
+ state.Buff[buff] = buffactive[buff] or false
+ end
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions for changing spells and target types in an automatic manner.
+-------------------------------------------------------------------------------------------------------------------
+
+local waltz_tp_cost = {['Curing Waltz'] = 200, ['Curing Waltz II'] = 350, ['Curing Waltz III'] = 500, ['Curing Waltz IV'] = 650, ['Curing Waltz V'] = 800}
+
+-- Utility function for automatically adjusting the waltz spell being used to match HP needs and TP limits.
+-- Handle spell changes before attempting any precast stuff.
+function refine_waltz(spell, action, spellMap, eventArgs)
+ if spell.type ~= 'Waltz' then
+ return
+ end
+
+ -- Don't modify anything for Healing Waltz or Divine Waltzes
+ if spell.english == "Healing Waltz" or spell.english == "Divine Waltz" or spell.english == "Divine Waltz II" then
+ return
+ end
+
+ local newWaltz = spell.english
+ local waltzID
+
+ local missingHP
+
+ -- If curing ourself, get our exact missing HP
+ if spell.target.type == "SELF" then
+ missingHP = player.max_hp - player.hp
+ -- If curing someone in our alliance, we can estimate their missing HP
+ elseif spell.target.isallymember then
+ local target = find_player_in_alliance(spell.target.name)
+ local est_max_hp = target.hp / (target.hpp/100)
+ missingHP = math.floor(est_max_hp - target.hp)
+ end
+
+ -- If we have an estimated missing HP value, we can adjust the preferred tier used.
+ if missingHP ~= nil then
+ if player.main_job == 'DNC' then
+ if missingHP < 40 and spell.target.name == player.name then
+ -- Not worth curing yourself for so little.
+ -- Don't block when curing others to allow for waking them up.
+ add_to_chat(122,'Full HP!')
+ eventArgs.cancel = true
+ return
+ elseif missingHP < 200 then
+ newWaltz = 'Curing Waltz'
+ waltzID = 190
+ elseif missingHP < 600 then
+ newWaltz = 'Curing Waltz II'
+ waltzID = 191
+ elseif missingHP < 1100 then
+ newWaltz = 'Curing Waltz III'
+ waltzID = 192
+ elseif missingHP < 1500 then
+ newWaltz = 'Curing Waltz IV'
+ waltzID = 193
+ else
+ newWaltz = 'Curing Waltz V'
+ waltzID = 311
+ end
+ elseif player.sub_job == 'DNC' then
+ if missingHP < 40 and spell.target.name == player.name then
+ -- Not worth curing yourself for so little.
+ -- Don't block when curing others to allow for waking them up.
+ add_to_chat(122,'Full HP!')
+ eventArgs.cancel = true
+ return
+ elseif missingHP < 150 then
+ newWaltz = 'Curing Waltz'
+ waltzID = 190
+ elseif missingHP < 300 then
+ newWaltz = 'Curing Waltz II'
+ waltzID = 191
+ else
+ newWaltz = 'Curing Waltz III'
+ waltzID = 192
+ end
+ else
+ -- Not dnc main or sub; bail out
+ return
+ end
+ end
+
+ local tpCost = waltz_tp_cost[newWaltz]
+
+ local downgrade
+
+ -- Downgrade the spell to what we can afford
+ if player.tp < tpCost and not buffactive.trance then
+ --[[ Costs:
+ Curing Waltz: 200 TP
+ Curing Waltz II: 350 TP
+ Curing Waltz III: 500 TP
+ Curing Waltz IV: 650 TP
+ Curing Waltz V: 800 TP
+ Divine Waltz: 400 TP
+ Divine Waltz II: 800 TP
+ --]]
+
+ if player.tp < 200 then
+ add_to_chat(122, 'Insufficient TP ['..tostring(player.tp)..']. Cancelling.')
+ eventArgs.cancel = true
+ return
+ elseif player.tp < 350 then
+ newWaltz = 'Curing Waltz'
+ elseif player.tp < 500 then
+ newWaltz = 'Curing Waltz II'
+ elseif player.tp < 650 then
+ newWaltz = 'Curing Waltz III'
+ elseif player.tp < 800 then
+ newWaltz = 'Curing Waltz IV'
+ end
+
+ downgrade = 'Insufficient TP ['..tostring(player.tp)..']. Downgrading to '..newWaltz..'.'
+ end
+
+
+ if newWaltz ~= spell.english then
+ send_command('@input /ja "'..newWaltz..'" '..tostring(spell.target.raw))
+ if downgrade then
+ add_to_chat(122, downgrade)
+ end
+ eventArgs.cancel = true
+ return
+ end
+
+ if missingHP and missingHP > 0 then
+ add_to_chat(122,'Trying to cure '..tostring(missingHP)..' HP using '..newWaltz..'.')
+ end
+end
+
+
+-- Function to allow for automatic adjustment of the spell target type based on preferences.
+function auto_change_target(spell, spellMap)
+ -- Don't adjust targetting for explicitly named targets
+ if not spell.target.raw:startswith('<') then
+ return
+ end
+
+ -- Do not modify target for spells where we get <lastst> or <me>.
+ if spell.target.raw == ('<lastst>') or spell.target.raw == ('<me>') then
+ return
+ end
+
+ -- init a new eventArgs with current values
+ local eventArgs = {handled = false, PCTargetMode = state.PCTargetMode, SelectNPCTargets = state.SelectNPCTargets}
+
+ -- Allow the job to do custom handling, or override the default values.
+ -- They can completely handle it, or set one of the secondary eventArgs vars to selectively
+ -- override the default state vars.
+ if job_auto_change_target then
+ job_auto_change_target(spell, action, spellMap, eventArgs)
+ end
+
+ -- If the job handled it, we're done.
+ if eventArgs.handled then
+ return
+ end
+
+ local pcTargetMode = eventArgs.PCTargetMode
+ local selectNPCTargets = eventArgs.SelectNPCTargets
+
+
+ local validPlayers = S{'Self', 'Player', 'Party', 'Ally', 'NPC'}
+
+ local intersection = spell.targets * validPlayers
+ local canUseOnPlayer = not intersection:empty()
+
+ local newTarget
+
+ -- For spells that we can cast on players:
+ if canUseOnPlayer and pcTargetMode ~= 'default' then
+ -- Do not adjust targetting for player-targettable spells where the target was <t>
+ if spell.target.raw ~= ('<t>') then
+ if pcTargetMode == 'stal' then
+ -- Use <stal> if possible, otherwise fall back to <stpt>.
+ if spell.targets.Ally then
+ newTarget = '<stal>'
+ elseif spell.targets.Party then
+ newTarget = '<stpt>'
+ end
+ elseif pcTargetMode == 'stpt' then
+ -- Even ally-possible spells are limited to the current party.
+ if spell.targets.Ally or spell.targets.Party then
+ newTarget = '<stpt>'
+ end
+ elseif pcTargetMode == 'stpc' then
+ -- If it's anything other than a self-only spell, can change to <stpc>.
+ if spell.targets.Player or spell.targets.Party or spell.targets.Ally or spell.targets.NPC then
+ newTarget = '<stpc>'
+ end
+ end
+ end
+ -- For spells that can be used on enemies:
+ elseif spell.targets and spell.targets.Enemy and selectNPCTargets then
+ -- Note: this means macros should be written for <t>, and it will change to <stnpc>
+ -- if the flag is set. It won't change <stnpc> back to <t>.
+ newTarget = '<stnpc>'
+ end
+
+ -- If a new target was selected and is different from the original, call the change function.
+ if newTarget and newTarget ~= spell.target.raw then
+ change_target(newTarget)
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Environment utility functions.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Function to get the current weather intensity: 0 for none, 1 for single weather, 2 for double weather.
+function get_weather_intensity()
+ return gearswap.res.weather[world.weather_id].intensity
+end
+
+
+-- Returns true if you're in a party solely comprised of Trust NPCs.
+-- TODO: Do we need a check to see if we're in a party partly comprised of Trust NPCs?
+function is_trust_party()
+ -- Check if we're solo
+ if party.count == 1 then
+ return false
+ end
+
+ -- Can call a max of 3 Trust NPCs, so parties larger than that are out.
+ if party.count > 4 then
+ return false
+ end
+
+ -- If we're in an alliance, can't be a Trust party.
+ if alliance[2].count > 0 or alliance[3].count > 0 then
+ return false
+ end
+
+ -- Check that, for each party position aside from our own, the party
+ -- member has one of the Trust NPC names, and that those party members
+ -- are flagged is_npc.
+ for i = 2,4 do
+ if party[i] then
+ if not npcs.Trust:contains(party[i].name) then
+ return false
+ end
+ if party[i].mob and party[i].mob.is_npc == false then
+ return false
+ end
+ end
+ end
+
+ -- If it didn't fail any of the above checks, return true.
+ return true
+end
+
+
+-- Call these function with a list of equipment slots to check ('head', 'neck', 'body', etc)
+-- Returns true if any of the specified slots are currently encumbered.
+-- Returns false if all specified slots are unencumbered.
+function is_encumbered(...)
+ local check_list = {...}
+ -- Compensate for people passing a table instead of a series of strings.
+ if type(check_list[1]) == 'table' then
+ check_list = check_list[1]
+ end
+ local check_set = S(check_list)
+
+ for slot_id,slot_name in pairs(gearswap.default_slot_map) do
+ if check_set:contains(slot_name) then
+ if gearswap.encumbrance_table[slot_id] then
+ return true
+ end
+ end
+ end
+
+ return false
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Elemental gear utility functions.
+-------------------------------------------------------------------------------------------------------------------
+
+-- General handler function to set all the elemental gear for an action.
+function set_elemental_gear(spell)
+ set_elemental_gorget_belt(spell)
+ set_elemental_obi_cape_ring(spell)
+ set_elemental_staff(spell)
+end
+
+
+-- Set the name field of the predefined gear vars for gorgets and belts, for the specified weaponskill.
+function set_elemental_gorget_belt(spell)
+ if spell.type ~= 'WeaponSkill' then
+ return
+ end
+
+ -- Get the union of all the skillchain elements for the weaponskill
+ local weaponskill_elements = S{}:
+ union(skillchain_elements[spell.skillchain_a]):
+ union(skillchain_elements[spell.skillchain_b]):
+ union(skillchain_elements[spell.skillchain_c])
+
+ gear.ElementalGorget.name = get_elemental_item_name("gorget", weaponskill_elements) or gear.default.weaponskill_neck or ""
+ gear.ElementalBelt.name = get_elemental_item_name("belt", weaponskill_elements) or gear.default.weaponskill_waist or ""
+end
+
+
+-- Function to get an appropriate obi/cape/ring for the current action.
+function set_elemental_obi_cape_ring(spell)
+ if spell.element == 'None' then
+ return
+ end
+
+ local world_elements = S{world.day_element}
+ if world.weather_element ~= 'None' then
+ world_elements:add(world.weather_element)
+ end
+
+ local obi_name = get_elemental_item_name("obi", S{spell.element}, world_elements)
+ gear.ElementalObi.name = obi_name or gear.default.obi_waist or ""
+
+ if obi_name then
+ if player.inventory['Twilight Cape'] or player.wardrobe['Twilight Cape'] then
+ gear.ElementalCape.name = "Twilight Cape"
+ end
+ if (player.inventory['Zodiac Ring'] or player.wardrobe['Zodiac Ring']) and spell.english ~= 'Impact' and
+ not S{'Divine Magic','Dark Magic','Healing Magic'}:contains(spell.skill) then
+ gear.ElementalRing.name = "Zodiac Ring"
+ end
+ else
+ gear.ElementalCape.name = gear.default.obi_back
+ gear.ElementalRing.name = gear.default.obi_ring
+ end
+end
+
+
+-- Function to get the appropriate fast cast and/or recast staves for the current spell.
+function set_elemental_staff(spell)
+ if spell.action_type ~= 'Magic' then
+ return
+ end
+
+ gear.FastcastStaff.name = get_elemental_item_name("fastcast_staff", S{spell.element}) or gear.default.fastcast_staff or ""
+ gear.RecastStaff.name = get_elemental_item_name("recast_staff", S{spell.element}) or gear.default.recast_staff or ""
+end
+
+
+-- Gets the name of an elementally-aligned piece of gear within the player's
+-- inventory that matches the conditions set in the parameters.
+--
+-- item_type: Type of item as specified in the elemental_map mappings.
+-- EG: gorget, belt, obi, fastcast_staff, recast_staff
+--
+-- valid_elements: Elements that are valid for the action being taken.
+-- IE: Weaponskill skillchain properties, or spell element.
+--
+-- restricted_to_elements: Secondary elemental restriction that limits
+-- whether the item check can be considered valid.
+-- EG: Day or weather elements that have to match the spell element being queried.
+--
+-- Returns: Nil if no match was found (either due to elemental restrictions,
+-- or the gear isn't in the player inventory), or the name of the piece of
+-- gear that matches the query.
+function get_elemental_item_name(item_type, valid_elements, restricted_to_elements)
+ local potential_elements = restricted_to_elements or elements.list
+ local item_map = elements[item_type:lower()..'_of']
+
+ for element in (potential_elements.it or it)(potential_elements) do
+ if valid_elements:contains(element) and (player.inventory[item_map[element]] or player.wardrobe[item_map[element]]) then
+ return item_map[element]
+ end
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Function to easily change to a given macro set or book. Book value is optional.
+-------------------------------------------------------------------------------------------------------------------
+
+function set_macro_page(set,book)
+ if not tonumber(set) then
+ add_to_chat(123,'Error setting macro page: Set is not a valid number ('..tostring(set)..').')
+ return
+ end
+ if set < 1 or set > 10 then
+ add_to_chat(123,'Error setting macro page: Macro set ('..tostring(set)..') must be between 1 and 10.')
+ return
+ end
+
+ if book then
+ if not tonumber(book) then
+ add_to_chat(123,'Error setting macro page: book is not a valid number ('..tostring(book)..').')
+ return
+ end
+ if book < 1 or book > 20 then
+ add_to_chat(123,'Error setting macro page: Macro book ('..tostring(book)..') must be between 1 and 20.')
+ return
+ end
+ send_command('@input /macro book '..tostring(book)..';wait .1;input /macro set '..tostring(set))
+ else
+ send_command('@input /macro set '..tostring(set))
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions for including local user files.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Attempt to load user gear files in place of default gear sets.
+-- Return true if one exists and was loaded.
+function load_sidecar(job)
+ if not job then return false end
+
+ -- filename format example for user-local files: whm_gear.lua, or playername_whm_gear.lua
+ local filenames = {player.name..'_'..job..'_gear.lua', job..'_gear.lua',
+ 'gear/'..player.name..'_'..job..'_gear.lua', 'gear/'..job..'_gear.lua',
+ 'gear/'..player.name..'_'..job..'.lua', 'gear/'..job..'.lua'}
+ return optional_include(filenames)
+end
+
+-- Attempt to include user-globals. Return true if it exists and was loaded.
+function load_user_globals()
+ local filenames = {player.name..'-globals.lua', 'user-globals.lua'}
+ return optional_include(filenames)
+end
+
+-- Optional version of include(). If file does not exist, does not
+-- attempt to load, and does not throw an error.
+-- filenames takes an array of possible file names to include and checks
+-- each one.
+function optional_include(filenames)
+ for _,v in pairs(filenames) do
+ local path = gearswap.pathsearch({v})
+ if path then
+ include(v)
+ return true
+ end
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions for vars or other data manipulation.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Attempt to locate a specified name within the current alliance.
+function find_player_in_alliance(name)
+ for party_index,ally_party in ipairs(alliance) do
+ for player_index,_player in ipairs(ally_party) do
+ if _player.name == name then
+ return _player
+ end
+ end
+ end
+end
+
+
+-- buff_set is a set of buffs in a library table (any of S{}, T{} or L{}).
+-- This function checks if any of those buffs are present on the player.
+function has_any_buff_of(buff_set)
+ return buff_set:any(
+ -- Returns true if any buff from buff set that is sent to this function returns true:
+ function (b) return buffactive[b] end
+ )
+end
+
+
+-- Invert a table such that the keys are values and the values are keys.
+-- Use this to look up the index value of a given entry.
+function invert_table(t)
+ if t == nil then error('Attempting to invert table, received nil.', 2) end
+
+ local i={}
+ for k,v in pairs(t) do
+ i[v] = k
+ end
+ return i
+end
+
+
+-- Gets sub-tables based on baseSet from the string str that may be in dot form
+-- (eg: baseSet=sets, str='precast.FC', this returns the table sets.precast.FC).
+function get_expanded_set(baseSet, str)
+ local cur = baseSet
+ for i in str:gmatch("[^.]+") do
+ if cur then
+ cur = cur[i]
+ end
+ end
+
+ return cur
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions data and event tracking.
+-------------------------------------------------------------------------------------------------------------------
+
+-- This is a function that can be attached to a registered event for 'time change'.
+-- It will send a call to the update() function if the time period changes.
+-- It will also call job_time_change when any of the specific time class values have changed.
+-- To activate this in your job lua, add this line to your user_setup function:
+-- windower.register_event('time change', time_change)
+--
+-- Variables it sets: classes.Daytime, and classes.DuskToDawn. They are set to true
+-- if their respective descriptors are true, or false otherwise.
+function time_change(new_time, old_time)
+ local was_daytime = classes.Daytime
+ local was_dusktime = classes.DuskToDawn
+
+ if new_time >= 6*60 and new_time < 18*60 then
+ classes.Daytime = true
+ else
+ classes.Daytime = false
+ end
+
+ if new_time >= 17*60 or new_time < 7*60 then
+ classes.DuskToDawn = true
+ else
+ classes.DuskToDawn = false
+ end
+
+ if was_daytime ~= classes.Daytime or was_dusktime ~= classes.DuskToDawn then
+ if job_time_change then
+ job_time_change(new_time, old_time)
+ end
+
+ handle_update({'auto'})
+ end
+end
+
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-documentation.txt b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-documentation.txt
new file mode 100644
index 0000000..8f17241
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/Mote-documentation.txt
@@ -0,0 +1 @@
+Please see https://github.com/Kinematics/GearSwap-Jobs/wiki for documentation on the usage of these include files. \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/README.md b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/README.md
new file mode 100644
index 0000000..9fd1351
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/rev1/README.md
@@ -0,0 +1,6 @@
+Mote-libs
+=========
+
+These are the Mote-Include library files for GearSwap.
+
+Please see https://github.com/Kinematics/GearSwap-Jobs/wiki for documentation.