summaryrefslogtreecommitdiff
path: root/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap
diff options
context:
space:
mode:
Diffstat (limited to 'Data/BuiltIn/Libraries/lua-addons/addons/GearSwap')
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/README.md40
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Advanced sets tables.txt47
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BLM.lua622
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BRD.lua384
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_DNC.lua729
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_GEO.lua496
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_MNK.lua160
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_PLD.lua539
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_SMN.lua593
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_THF.lua321
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_WAR.lua235
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Snprphnx_SCH.lua256
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Variables.xlsxbin0 -> 32990 bytes
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/data/Instructions.txt6
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua292
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/export.lua317
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/flow.lua442
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/gearswap.lua333
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/helper_functions.lua1255
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Modes.lua457
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Globals.lua114
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Include.lua1125
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Mappings.lua283
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-SelfCommands.lua466
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-TreasureHunter.lua297
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Utility.lua663
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-documentation.txt1
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/closetCleaner.lua377
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/organizer-lib.lua237
-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
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/packet_parsing.lua716
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/refresh.lua727
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/statics.lua447
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/targets.lua149
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/triggers.lua343
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/user_functions.lua423
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/validate.lua319
-rw-r--r--Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/version_history.txt236
46 files changed, 18006 insertions, 0 deletions
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/README.md b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/README.md
new file mode 100644
index 0000000..650af85
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/README.md
@@ -0,0 +1,40 @@
+Author: Byrth
+
+Version: 0.930
+
+Date: 06/13/2017
+
+GearSwap
+
+Abbreviation: gs
+
+Commands (<> indicates a field. You do not actually have to use <>s):
+* gs c <string> : Passes the <string> to the self_command() user function.
+* gs equip <string> : Attempts to interpret the <string> as an index of the sets table and equip that set. Will ignore "sets" if the string starts with it.
+** gs equip naked : This equips the default set "naked," which is just a bunch of empty slots. If you remake sets (sets={}) in your get_sets(), this will not work.
+* gs debugmode : Activates GearSwap's Debug Mode, which prints out why specific gear equipping attempts failed, shows you when you're entering events, and enables the eval command.
+** gs eval <string> : This command evaluates the <string> as Lua code in the global gearswap environment (not the user environment, which is in the user_env table). It is only available when debugmode is on.
+* gs showswaps : Shows when your gear successfully changes and what it changes to.
+* gs load <string> : (or l <string>) Attempts to load the first version of <string> found, assuming it is a file path relative to 9 potential base directories, in this order:
+** ..GearSwap/libs-dev/<string>
+** ..GearSwap/libs/<string>
+** GearSwap/data/<character_name>/<string>
+** GearSwap/data/common/<string>
+** GearSwap/data/<string>
+** APPDATA/Windower/GearSwap/<character_name>/<string>
+** APPDATA/Windower/GearSwap/common/<string>
+** APPDATA/Windower/GearSwap/<string>
+** ..Windower/addons/libs/<string>
+* gs reload : Reloads the current user file.
+* gs export <options> : Exports your currently equipped gear, inventory, or all the items in your current Lua files' sets into GearSwap .lua or spellcast .xml format. Takes options "inventory", "all", "wearable", "sets", and "xml." Defaults to currently equipped gear and lua otherwise. Also exports appropriate advanced set tables with augments for currently equipped gear and inventory.
+* gs enable <slot> : Enables equip commands targeting a specified slot. "All" will allow all equip commands. Providing no slot argument will enable user GearSwap file execution, if it was disabled.
+* gs disable <slot> : Disables equip commands targeting a given slot. "All" will prevent all equip commands. Providing no second argument will disable user GearSwap file execution, although registered events will still run.
+* gs validate <sets|inv> <filter> : This command checks to see whether the equipment in the sets table also exists in your inventory (default), or (by passing "inv") whether the equipment in your inventory exists in your sets table. <filter> is an optional list of words that restricts the output to only those items that contain text from one of the filter's words.
+
+Purpose: To assist in the micromanaging of equipment!
+
+Settings Files:
+There is no settings file for GearSwap.
+
+Additional Assistance:
+The Windower/addons/GearSwap/beta_examples_and_information folder has a file in it named Variables.xlsx that gives more specific information. If that is insufficient, you can go to BlueGartr's FFXI section or FFXIAH and ask for more assistance.
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Advanced sets tables.txt b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Advanced sets tables.txt
new file mode 100644
index 0000000..56a08e2
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Advanced sets tables.txt
@@ -0,0 +1,47 @@
+============== Advanced "sets" tables ==============
+
+-- Augments --
+There are a lot of different augments, many/all of which have been mapped in the extdata library.
+This system will use the first item it finds in your inventory that matches the name/augment
+combination that you provide. If you provide an ambiguous set of conditions, the resulting equipped
+item will be ambiguous (could vary based on the pairs iterator).
+
+In order to specify an item by augment, you must write the augment exactly as it appears on your equipment:
+ sets.aftercast_Idle = {ring1={name="Dark Ring",augments={"Phys. Damage Taken -6%"}},
+ ring2={name="Dark Ring",augments={"Breath Damage Taken -6%","{Phys. Damage Taken -5%"}}}
+
+
+If it pleases you, you can store your augmented items in variables:
+ lefty = {name="Dark Ring",augments={"Phys. Damage Taken -6%"}}
+ righty = {name="Dark Ring",augments={"Breath Damage Taken -6%","Phys. Damage Taken -5%"}}
+ sets.aftercast_Idle = {ring1=lefty,ring2=righty}
+
+This will save you a little work when you are dealing with using the same augmented item in multiple sets.
+
+
+If you are having trouble getting GearSwap to recognize your augment, equip the gear and use //gs export.
+This will create a Lua file in ../windower/addons/gearswap/data/export that includes a properly formatted table.
+
+
+
+
+-- Prioritizing gear changes --
+Currently equip swaps can be ordered by using advanced "sets" tables similar to the above:
+ sets.nohead = {head={name="empty",priority=1},lring={name="Angha ring",priority=2}}
+
+Gear is changed in descending order from highest to lowest, so the above set would equip Angha ring and then
+unequip the head slot. All gear that is not defined is given a default priority of zero, and all positive and
+negative rational numbers are acceptable priorities. If two pieces of gear have the same priority value, they
+will be swapped in the default order (from 0 to 15 in ../windower/res/slots.lua).
+
+I do not recommend moving Sub in front of Main, Ammo in front of Range, etc. as these will cause silent equip
+failures.
+
+
+
+-- Specifying a bag --
+With the introduction of Mog Wardrobe, it is now possible to equip items from either Inventory or Wardrobe:
+ sets.wardrobe_feet = {feet={name="Bokwus Boots",bag="wardrobe"}}
+
+There is really no reason to do this 99% of the time, but it has been included as a failsafe to let people
+still specify at least two augments in case the augment system stops working in the future. \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BLM.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BLM.lua
new file mode 100644
index 0000000..d49bf4b
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BLM.lua
@@ -0,0 +1,622 @@
+include('organizer-lib')
+
+function get_sets()
+ mp_efficiency = 0
+ macc_level = 0
+
+ sets.TH = {
+ hands={ name="Merlinic Dastanas", augments={'"Treasure Hunter"+2',},hp=9,mp=20},
+ legs={ name="Merlinic Shalwar", augments={'Pet: Accuracy+16 Pet: Rng. Acc.+16','Pet: Haste+1','"Treasure Hunter"+1','Mag. Acc.+9 "Mag.Atk.Bns."+9',},hp=29,mp=44},
+ waist="Chaac Belt",
+ }
+
+ sets.precast = {}
+
+
+ sets.precast.FastCast = {}
+
+ sets.precast.FastCast.Default = {
+ main="Marin Staff +1",
+ ammo="Impatiens",
+ head={ name="Merlinic Hood", augments={'"Fast Cast"+7','MND+10','Mag. Acc.+10','"Mag.Atk.Bns."+10',},hp=22,mp=56},
+ neck={name="Orunmila's Torque",mp=30},
+ ear1="Enchanter Earring +1",
+ ear2={name="Loquacious Earring",mp=30},
+ body={ name="Zendik Robe",hp=57,mp=61},
+ hands={ name="Merlinic Dastanas", augments={'Mag. Acc.+29','"Fast Cast"+7','INT+1',},hp=9,mp=20},
+ lring="Kishar Ring",
+ rring="Weather. Ring +1",
+ back={ name="Taranus's Cape", augments={'Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',},mp=78},
+ waist="Witful Belt",
+ legs={name="Psycloth Lappas",hp=43,mp=109},
+ feet={ name="Merlinic Crackows", augments={'"Mag.Atk.Bns."+11','"Fast Cast"+7',},hp=4,mp=20},
+ }
+
+ sets.precast.FastCast.Death = set_combine(sets.precast.FastCast.Default,{
+ ammo={name="Ghastly Tathlum +1",mp=35},
+ head={ name="Merlinic Hood", augments={'"Fast Cast"+7','MND+10','Mag. Acc.+10','"Mag.Atk.Bns."+10',},hp=22,mp=56},
+ neck={name="Orunmila's Torque",mp=30},
+ ear1={name="Etiolation Earring",hp=50,mp=50},
+ ear2={name="Loquacious Earring",mp=30},
+ body={ name="Zendik Robe",hp=57,mp=61},
+ hands={ name="Merlinic Dastanas", augments={'Mag. Acc.+29','"Fast Cast"+7','INT+1',},hp=9,mp=20},
+ lring="Kishar Ring",
+ rring={name="Sangoma Ring",mp=70},
+ back={ name="Taranus's Cape", augments={'Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',},mp=78},
+ waist={name="Mujin Obi",mp=60},
+ legs={name="Psycloth Lappas",hp=43,mp=109},
+ feet={ name="Amalric Nails +1", hp=4,mp=106},
+ })
+
+ sets.precast.FastCast['Elemental Magic'] = set_combine(sets.precast.FastCast.Default,{
+ ear1={name="Barkarole Earring",mp=25},
+ body={ name="Dalmatica +1", augments={'Occ. quickens spellcasting +3%','"Fast Cast"+6','Pet: "Mag.Def.Bns."+6',},hp=-55,mp=55},
+ })
+
+ sets.precast.FastCast['Enhancing Magic'] = set_combine(sets.precast.FastCast.Default,{waist="Siegel Sash"})
+
+ sets.precast.Cure = set_combine(sets.precast.FastCast.Default,{body="Heka's Kalasiris",
+ back={name="Pahtli Cape",mp=50},
+ legs={name="Doyen Pants",hp=43,mp=32},
+ lear={name="Mendicant's Earring",mp=30},
+ })
+ sets.precast.Stoneskin = set_combine(sets.precast.FastCast['Enhancing Magic'],{
+ legs={name="Doyen Pants",hp=43,mp=32},
+ })
+
+ sets.precast.Manafont = {body={name="Archmage's Coat +1",hp=54,mp=59}}
+
+ sets.Impact = {head=empty,body={name="Twilight Cloak",mp=75},legs={name="Perdition Slops",hp=73,mp=59}}
+
+ sets.midcast = {}
+
+ sets.midcast.magic_base = {
+ main="Laevateinn",
+ sub="Enki Strap",
+ ammo={name="Mana Ampulla",mp=20},
+ head={ name="Merlinic Hood", augments={'"Mag.Atk.Bns."+28','"Fast Cast"+3','"Refresh"+1','Mag. Acc.+6 "Mag.Atk.Bns."+6',},hp=22,mp=56},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',},hp=30,mp=22},
+ legs={ name="Lengo Pants", augments={'INT+10','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=43,mp=29},
+ feet={name="Herald's Gaiters",mp=12},
+ neck="Incanter's Torque",
+ waist="Austerity Belt +1",
+ lear={name="Mendicant's Earring",mp=30},
+ right_ear="Dignitary's Earring",
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',},hp=-20,mp=20},
+ right_ring="Defending Ring",
+ back="Umbra Cape",
+ }
+
+ sets.midcast.Stun = {main="Laevateinn",
+ sub="Enki strap",
+ ammo={name="Hydrocera",mp=20},
+ head={ name="Merlinic Hood", augments={'"Fast Cast"+7','MND+10','Mag. Acc.+10','"Mag.Atk.Bns."+10',},hp=22,mp=56},
+ neck="Erra Pendant",
+ ear1="Enchanter Earring +1",
+ ear2="Dignitary's Earring",
+ body={name="Psycloth Vest",hp=54,mp=59},
+ hands={ name="Merlinic Dastanas", augments={'Mag. Acc.+29','"Fast Cast"+7','INT+1',},hp=9,mp=20},
+ lring={name="Sangoma Ring",mp=70},
+ rring="Shiva Ring +1",
+ back={ name="Taranus's Cape", augments={'Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',},mp=78},
+ waist="Ninurta's Sash",
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+30','"Occult Acumen"+10','INT+9','Mag. Acc.+5',},hp=29,mp=44},
+ feet={name="Artsieq Boots",hp=13,mp=44},
+ }
+
+ sets.midcast['Elemental Magic'] = {
+ [0] = {},
+ [1] = {}
+ }
+
+ sets.ElementalMagicMAB = {
+ Earth={neck={name="Quanpur Necklace", mp=10}},
+ Dark={
+ head={ name="Pixie Hairpin +1", hp=-35,mp=120},
+ rring="Archon Ring",
+ }
+ }
+
+ -- MAcc level 0 (Macc and Enmity irrelevant)
+ sets.midcast['Elemental Magic'][0][0] = {
+ main="Laevateinn",
+ sub="Enki Strap",
+ ammo="Pemphredo Tathlum",
+ head={ name="Merlinic Hood", augments={'VIT+8','"Mag.Atk.Bns."+27','Accuracy+5 Attack+5','Mag. Acc.+18 "Mag.Atk.Bns."+18',},hp=22,mp=56},
+ neck="Baetyl Pendant",
+ ear1={name="Barkarole Earring",mp=25},
+ ear2="Friomisi Earring",
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ ring1="Shiva Ring +1",
+ ring2="Shiva Ring +1",
+ back={ name="Taranus's Cape", augments={'INT+20','System: 1 ID: 80 Val: 19','"Mag.Atk.Bns."+10',}},
+ waist={name="Yamabuki-no-Obi",mp=35},
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+30','"Occult Acumen"+10','INT+9','Mag. Acc.+5',},hp=29,mp=44},
+ feet={ name="Amalric Nails +1", hp=4,mp=106},
+ }
+
+ sets.midcast.MB={
+ head={ name="Merlinic Hood", augments={'Mag. Acc.+24 "Mag.Atk.Bns."+24','Magic burst dmg.+11%','INT+1','Mag. Acc.+8',},hp=22,mp=56},
+ body={ name="Merlinic Jubbah", augments={'"Mag.Atk.Bns."+29','Magic burst dmg.+11%','INT+10',},hp=41,mp=67},
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+26','Magic burst dmg.+11%','INT+10','Mag. Acc.+13',},hp=29,mp=44},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ neck="Mizu. Kubikazari",
+ right_ring="Mujin Band",
+ }
+
+ --sets.midcast['Elemental Magic'][0][0] = set_combine(sets.midcast['Elemental Magic'][0][0],sets.midcast.MB)
+
+ sets.midcast['Elemental Magic'][0][1] = set_combine(sets.midcast['Elemental Magic'][0][0],{
+ body={name="Spaekona's Coat +2",hp=81,mp=88}
+ })
+
+ -- MAcc level 1 (MAcc and Enmity relevant)
+ sets.midcast['Elemental Magic'][1][0] = {main="Laevateinn",
+ sub="Enki Strap",
+ ammo="Pemphredo Tathlum",
+ head={ name="Merlinic Hood", augments={'VIT+8','"Mag.Atk.Bns."+27','Accuracy+5 Attack+5','Mag. Acc.+18 "Mag.Atk.Bns."+18',},hp=22,mp=56},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ legs={ name="Merlinic Shalwar", augments={'Mag. Acc.+25 "Mag.Atk.Bns."+25','MND+4','Mag. Acc.+15','"Mag.Atk.Bns."+12',},hp=29,mp=44},
+ feet={ name="Amalric Nails +1", hp=4,mp=106},
+ neck={name="Sanctity Necklace",hp=35,mp=35},
+ waist={name="Acuity Belt +1", mp=35},
+ ear2="Novia Earring",
+ ear1={name="Barkarole Earring",mp=25},
+ ring1="Shiva Ring +1",
+ ring2="Shiva Ring +1",
+ back={ name="Taranus's Cape", augments={'INT+20','System: 1 ID: 80 Val: 19','"Mag.Atk.Bns."+10',}},
+ }
+
+ sets.midcast['Elemental Magic'][1][1] = set_combine(sets.midcast['Elemental Magic'][1][0],{
+ body={name="Spaekona's Coat +2",hp=81,mp=88}
+ })
+
+ sets.midcast.Death = {
+ main="Laevateinn",
+ sub={name="Niobid Strap",mp=20},
+ ammo={name="Ghastly Tathlum +1",mp=35},
+ head={ name="Pixie Hairpin +1", hp=-35,mp=120},
+ body={ name="Merlinic Jubbah", augments={'"Mag.Atk.Bns."+29','Magic burst dmg.+11%','INT+10',},hp=41,mp=67},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+26','Magic burst dmg.+11%','INT+10','Mag. Acc.+13',},hp=29,mp=44},
+ feet={ name="Amalric Nails +1", hp=4,mp=106},
+ neck="Mizu. Kubikazari",
+ waist={name="Mujin Obi",mp=60},
+ ear2={name="Etiolation Earring",hp=50,mp=50},
+ ear1={name="Barkarole Earring",mp=25},
+ left_ring="Archon Ring",
+ right_ring="Mujin Band",
+ back={ name="Taranus's Cape", augments={'Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',},mp=78},
+ }
+ sets.midcast['Dia II'] = sets.TH
+ sets.midcast.Dia = sets.TH
+ sets.midcast.Diaga = sets.TH
+ sets.midcast.Burn = sets.TH
+
+
+ sets.midcast['Dark Magic'] = {
+ main={ name="Rubicundity", augments={'Mag. Acc.+10','"Mag.Atk.Bns."+10','Dark magic skill +10','"Conserve MP"+7',}},
+ sub="Genmei Shield",
+ ammo="Hasty Pinion +1",
+ head={ name="Pixie Hairpin +1", hp=-35,mp=120},
+ body={name="Psycloth Vest",hp=54,mp=59},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ legs={ name="Merlinic Shalwar", augments={'Mag. Acc.+25 "Mag.Atk.Bns."+25','MND+4','Mag. Acc.+15','"Mag.Atk.Bns."+12',},hp=29,mp=44},
+ feet={ name="Merlinic Crackows", augments={'Mag. Acc.+21','"Drain" and "Aspir" potency +11','INT+1',},hp=4,mp=20},
+ neck="Erra Pendant",
+ waist="Ninurta's Sash",
+ left_ear={name="Hirudinea Earring",hp=-5,mp=-5},
+ right_ear={name="Loquac. Earring",mp=30},
+ left_ring="Evanescence Ring",
+ right_ring="Archon Ring",
+ back={ name="Taranus's Cape", augments={'Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',},mp=78},
+ }
+
+ sets.midcast['Enfeebling Magic'] = {
+ main="Laevateinn",
+ sub="Enki Strap",
+ ammo="Pemphredo Tathlum",
+ head={name="Amalric Coif +1",hp=27,mp=61},
+ neck="Erra Pendant",
+ ear1="Enchanter Earring +1",
+ ear2="Dignitary's Earring",
+ body={name="Zendik Robe",hp=57,mp=61},
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',},hp=30,mp=22},
+ lring={name="Sangoma Ring",mp=70},
+ ring2="Weather. Ring +1",
+ back={ name="Taranus's Cape", augments={'INT+20','System: 1 ID: 80 Val: 19','"Mag.Atk.Bns."+10',}},
+ waist={name="Luminary Sash",mp=45},
+ legs={name="Psycloth Lappas",hp=43,mp=109},
+ feet={name="Artsieq Boots",hp=13,mp=44},
+ }
+
+ sets.midcast.Vidohunir = {
+ ammo="Pemphredo Tathlum",
+ head={ name="Pixie Hairpin +1", hp=-35,mp=120},
+ neck={name="Saevus Pendant +1",mp=20},
+ ear1={name="Barkarole Earring",mp=25},
+ ear2="Friomisi Earring",
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ lring="Shiva Ring +1",
+ rring="Archon Ring",
+ back={ name="Taranus's Cape", augments={'INT+20','System: 1 ID: 80 Val: 19','"Mag.Atk.Bns."+10',}},
+ waist={name="Acuity Belt +1", mp=35},
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+30','"Occult Acumen"+10','INT+9','Mag. Acc.+5',},hp=29,mp=44},
+ feet={ name="Amalric Nails +1", hp=4,mp=106},
+ }
+
+ sets.midcast.Myrkr = {
+ ammo={name="Ghastly Tathlum +1",mp=35},
+ head={ name="Pixie Hairpin +1", hp=-35,mp=120},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ legs={ name="Psycloth Lappas", augments={'MP+80','Mag. Acc.+15','"Fast Cast"+7',},hp=43,mp=109},
+ feet={name="Artsieq Boots",hp=13,mp=44},
+ neck={name="Sanctity Necklace",hp=35,mp=35},
+ waist={name="Mujin Obi",mp=60},
+ ear1={name="Etiolation Earring",hp=50,mp=50},
+ right_ear={name="Gifted Earring",mp=45},
+ rring={name="Sangoma Ring",mp=70},
+ right_ring={name="Lebeche Ring",mp=40},
+ back={ name="Taranus's Cape", augments={'Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',},mp=78},
+ }
+
+ sets.midcast.Cure = {
+ main="Vadose Rod",
+ sub="Genmei Shield",
+ ammo={name="Mana Ampulla",mp=20},
+ head={name="Amalric Coif +1",hp=27,mp=61},
+ neck="Phalaina Locket",
+ lear={name="Mendicant's Earring",mp=30},
+ rear="Novia Earring",
+ body="Heka's Kalasiris",
+ hands={name="Revealer's Mitts +1",hp=22,mp=44},
+ lring={name="Sangoma Ring",mp=70},
+ right_ring={name="Lebeche Ring",mp=40},
+ back={name="Pahtli Cape",mp=50},
+ waist={name="Luminary Sash",mp=45},
+ legs={ name="Merlinic Shalwar", augments={'Mag. Acc.+25 "Mag.Atk.Bns."+25','MND+4','Mag. Acc.+15','"Mag.Atk.Bns."+12',},hp=29,mp=44},
+ feet={name="Wicce Sabots +1",hp=9,mp=20},
+ }
+
+
+ sets.midcast.EnhancingDuration = {
+ main={ name="Gada", augments={'Enh. Mag. eff. dur. +6','"Mag.Atk.Bns."+9',}},
+ sub={name="Ammurapi Shield",hp=22,mp=58},
+ hands={ name="Telchine Gloves", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=52,mp=44},
+ head={ name="Telchine Cap", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=36,mp=32,},
+ body={ name="Telchine Chas.", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=54,mp=59},
+ legs={ name="Telchine Braconi", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=43,mp=29},
+ feet={ name="Telchine Pigaches", augments={'Song spellcasting time -7%','Enh. Mag. eff. dur. +10',},hp=13,mp=44},
+ }
+
+ sets.midcast.Stoneskin = set_combine(sets.midcast.EnhancingDuration,{
+ neck={name="Nodens Gorget",hp=25,mp=25},
+ waist="Siegel Sash",
+ legs="Shedir Seraweels"
+ })
+
+ sets.midcast.Aquaveil = set_combine(sets.midcast.EnhancingDuration,{
+ main="Vadose Rod",
+ head={name="Amalric Coif +1",hp=27,mp=61},
+ sub="Genmei Shield",
+ waist={name="Emphatikos Rope",mp=20},
+ legs="Shedir Seraweels"
+ })
+
+ sets.midcast.Refresh = set_combine(sets.midcast.EnhancingDuration,{
+ head={name="Amalric Coif +1",hp=27,mp=61},
+ back="Grapevine Cape",
+ feet={name="Inspirited Boots",hp=9,mp=20},
+ })
+
+ sets.midcast.Phalanx = set_combine(sets.midcast.EnhancingDuration,{})
+
+ sets.aftercast = {}
+ sets.aftercast.Idle = {}
+ sets.aftercast.Idle.keys = {[0]="Refresh",[1]="PDT"}
+ sets.aftercast.Idle.ind = 0
+--[[ sets.aftercast.Idle[0] = {
+ main="Laevateinn",
+ sub={name="Niobid Strap",mp=20},
+ ammo={name="Ghastly Tathlum +1",mp=35},
+ head={ name="Merlinic Hood", augments={'"Mag.Atk.Bns."+28','"Fast Cast"+3','"Refresh"+1','Mag. Acc.+6 "Mag.Atk.Bns."+6',},hp=22,mp=56},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Amalric Gages +1",hp=13,mp=106},
+ legs={ name="Lengo Pants", augments={'INT+10','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=43,mp=29},
+ feet={ name="Amalric Nails +1", hp=4,mp=106},
+ neck="Loricate Torque +1",
+ waist={name="Mujin Obi",mp=60},
+ ear1={name="Etiolation Earring",hp=50,mp=50},
+ right_ear={name="Gifted Earring",mp=45},
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',},hp=-20,mp=20},
+ right_ring="Defending Ring",
+ back={ name="Bane Cape", augments={'Elem. magic skill +3','Dark magic skill +7','"Mag.Atk.Bns."+1','"Fast Cast"+5',}},
+ }]]
+ sets.aftercast.Idle[0] = {
+ main="Mafic Cudgel",
+ sub="Genmei Shield",
+ ammo={name="Mana Ampulla",mp=20},
+ head={ name="Merlinic Hood", augments={'"Mag.Atk.Bns."+28','"Fast Cast"+3','"Refresh"+1','Mag. Acc.+6 "Mag.Atk.Bns."+6',},hp=22,mp=56},
+ neck="Loricate Torque +1",
+ ear1={name="Etiolation Earring",hp=50,mp=50},
+ ear2="Sorcerer's Earring",
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={ name="Merlinic Dastanas", augments={'VIT+8','Attack+18','"Refresh"+1','Accuracy+17 Attack+17','Mag. Acc.+7 "Mag.Atk.Bns."+7',},hp=9,mp=20},
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',},hp=-20,mp=20},
+ ring2="Defending Ring",
+ back="Umbra Cape",
+ waist={name="Mujin Obi",mp=60},
+ legs={ name="Lengo Pants", augments={'INT+10','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=43,mp=29},
+ feet={name="Herald's Gaiters",mp=12},
+ }
+
+ sets.aftercast.Idle[1] = {
+ main="Mafic Cudgel",
+ sub="Genmei Shield",
+ ammo={name="Mana Ampulla",mp=20},
+ head={ name="Hagondes Hat +1", augments={'Phys. dmg. taken -3%','Magic dmg. taken -2%','"Mag.Atk.Bns."+26',},hp=36,mp=32},
+ neck="Loricate Torque +1",
+ ear1="Telos Earring",
+ ear1={name="Etiolation Earring",hp=50,mp=50},
+ body={name="Hagondes Coat +1",hp=54,mp=59},
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',},hp=30,mp=22},
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',},hp=-20,mp=20},
+ ring2="Defending Ring",
+ back="Umbra Cape",
+ waist="Ninurta's Sash",
+ legs={ name="Hagondes Pants +1", augments={'Phys. dmg. taken -4%','Magic dmg. taken -4%','Magic burst dmg.+10%',},hp=43,mp=29},
+ feet={name="Battlecast Gaiters",hp=13},
+ }
+
+ sets.aftercast.Idle['Mana Wall'] = {
+ back={ name="Taranus's Cape", augments={'Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',},mp=78},
+ feet={name="Wicce Sabots +1",hp=9,mp=20},
+ }
+
+ sets.aftercast.Resting = {
+ main={name="Numen Staff",mp=45},
+ sub="Oneiros Grip",
+ ammo={name="Mana Ampulla",mp=20},
+ head={ name="Merlinic Hood", augments={'"Mag.Atk.Bns."+28','"Fast Cast"+3','"Refresh"+1','Mag. Acc.+6 "Mag.Atk.Bns."+6',},hp=22,mp=56},
+ neck={name="Eidolon Pendant +1",mp=15},
+ ear1="Relaxing Earring",
+ ear2={name="Antivenom Earring",mp=15},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Nares Cuffs",mp=48}, -- MP+3%
+ ring1={name="Celestial Ring",mp=20},
+ ring2="Angha Ring",
+ back={name="Felicitas Cape +1",mp=15},
+ waist="Austerity Belt +1",
+ legs={ name="Lengo Pants", augments={'INT+10','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=43,mp=29},
+ feet={name="Chelona Boots +1",mp=40},
+ }
+
+ sets.aftercast.Engaged = {
+ main="Mafic Cudgel",
+ sub="Genmei Shield",
+ head={ name="Hagondes Hat +1", augments={'Phys. dmg. taken -3%','Magic dmg. taken -2%','"Mag.Atk.Bns."+26',},hp=36,mp=32},
+ neck="Loricate Torque +1",
+ ear2="Brutal Earring",
+ ear1={name="Etiolation Earring",hp=50,mp=50},
+ --body="Onca Suit",hands=empty,
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',},hp=-20,mp=20},
+ ring2="Defending Ring",
+ --back="Umbra Cape",waist="Ninurta's Sash",legs=empty,feet=empty}
+ body={name="Hagondes Coat +1",hp=54,mp=59},
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',},hp=30,mp=22},
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',},hp=-20,mp=20},
+ ring2="Defending Ring",
+ back="Umbra Cape",
+ waist="Ninurta's Sash",
+ legs={ name="Hagondes Pants +1", augments={'Phys. dmg. taken -4%','Magic dmg. taken -4%','Magic burst dmg.+10%',},hp=43,mp=29},
+ feet={name="Battlecast Gaiters",hp=13},
+ }
+
+ sets.Obis = {}
+ sets.Obis.Fire = {waist='Hachirin-no-Obi'}
+ sets.Obis.Earth = {waist='Hachirin-no-Obi'}
+ sets.Obis.Water = {waist='Hachirin-no-Obi'}
+ sets.Obis.Wind = {waist='Hachirin-no-Obi'}
+ sets.Obis.Ice = {waist='Hachirin-no-Obi'}
+ sets.Obis.Lightning = {waist='Hachirin-no-Obi'}
+ sets.Obis.Light = {waist='Hachirin-no-Obi'}
+ sets.Obis.Dark = {waist='Hachirin-no-Obi'}
+ sets.Zodiac = {lring={name="Zodiac Ring",mp=25}}
+
+ sets.aftercast.empty = {neck="Loricate Torque +1"}
+ sets.aftercast.Chry = {neck={name="Chrysopoeia Torque",mp=30}}
+ tp_level = 'empty'
+
+ stuntarg = 'Shantotto'
+ send_command('input /macro book 2;wait .1;input /macro set 1')
+end
+
+windower.register_event('tp change',function(new,old)
+ if new > 2990 then
+ tp_level = 'Chry'
+ else
+ tp_level = 'empty'
+ end
+ if not midaction() then
+ if sets.aftercast[player.status] then
+ equip(sets.aftercast[player.status],sets.aftercast[tp_level])
+ else
+ equip(sets.aftercast.Idle,sets.aftercast[tp_level])
+ end
+ end
+ set_priorities('mp','hp')
+end)
+
+function precast(spell)
+ if sets.precast[spell.english] then
+ equip(sets.precast[spell.english][macc_level] or sets.precast[spell.english])
+ elseif string.find(spell.english,'Cur') and spell.english ~='Cursna' then
+ equip(sets.precast.Cure)
+ elseif spell.english == 'Impact' then
+ equip(sets.precast.FastCast['Elemental Magic'],sets.Impact)
+ if not buffactive['Elemental Seal'] then
+ add_to_chat(8,'--------- Elemental Seal is down ---------')
+ end
+ elseif spell.action_type == 'Magic' then
+ if spell.skill == 'Elemental Magic' then
+ equip(sets.precast.FastCast['Elemental Magic'])
+ elseif spell.skill == 'Enhancing Magic' then
+ equip(sets.precast.FastCast['Enhancing Magic'])
+ else
+ equip(sets.precast.FastCast.Default)
+ end
+ end
+
+ if spell.english == 'Stun' and stuntarg ~= 'Shantotto' then
+ send_command('@input /t '..stuntarg..' ---- Byrth Stunned!!! ---- ')
+ end
+ set_priorities('mp','hp')
+end
+
+function midcast(spell)
+ equip_idle_set()
+ if buffactive.manawell or spell.mppaftercast > 50 then
+ mp_efficiency = 0
+ else
+ mp_efficiency = 1
+ end
+
+ if spell.action_type == 'Magic' then
+ equip(sets.midcast.magic_base)
+ end
+
+ if string.find(spell.english,'Cur') and spell.english ~='Cursna' then
+ weathercheck(spell.element,sets.midcast.Cure)
+ elseif spell.english == 'Impact' then
+ weathercheck(spell.element,set_combine(sets.midcast['Elemental Magic'][macc_level][mp_efficiency],sets.Impact))
+ elseif spell.english == 'Death' then
+ equip(sets.midcast.Death)
+ elseif sets.midcast[spell.name] then
+ weathercheck(spell.element,sets.midcast[spell.name])
+ elseif spell.skill == 'Elemental Magic' then
+ weathercheck(spell.element,sets.midcast['Elemental Magic'][macc_level][mp_efficiency])
+ zodiaccheck(spell.element)
+ if sets.ElementalMagicMAB[spell.element] then
+ equip(sets.ElementalMagicMAB[spell.element])
+ end
+ elseif spell.skill == "Enhancing Magic" and not S{'Warp','Warp II','Retrace','Teleport-Holla','Teleport-Mea','Teleport-Dem','Teleport-Altep','Teleport-Vahzl','Teleport-Yhoat'}:contains(spell.english) then
+ equip(sets.midcast.EnhancingDuration)
+ elseif spell.skill then
+ equip(sets.aftercast.Idle,sets.aftercast[tp_level])
+ weathercheck(spell.element,sets.midcast[spell.skill])
+ end
+
+ if spell.english == 'Sneak' and spell.target.name == player.name then
+ send_command('cancel 71;')
+ end
+ set_priorities('mp','hp')
+end
+
+function aftercast(spell)
+ if player.status == 'Idle' then
+ equip_idle_set()
+ elseif sets.aftercast[player.status] then
+ equip(sets.aftercast[player.status],sets.aftercast[tp_level])
+ else
+ equip(sets.aftercast.Idle,sets.aftercast[tp_level])
+ end
+ if not spell.interrupted then
+ if spell.english == 'Sleep' or spell.english == 'Sleepga' then
+ send_command('@wait 55;input /echo ------- '..spell.english..' is wearing off in 5 seconds -------')
+ elseif spell.english == 'Sleep II' or spell.english == 'Sleepga II' then
+ send_command('@wait 85;input /echo ------- '..spell.english..' is wearing off in 5 seconds -------')
+ elseif spell.english == 'Break' or spell.english == 'Breakga' then
+ send_command('@wait 25;input /echo ------- '..spell.english..' is wearing off in 5 seconds -------')
+ end
+ end
+ set_priorities('mp','hp')
+end
+
+function status_change(new,old)
+ if new == 'Resting' then
+ equip(sets.aftercast.Resting)
+ elseif new == 'Engaged' then
+ if not midaction() then
+ equip(sets.aftercast.Engaged,sets.aftercast[tp_level])
+ end
+ disable('main','sub')
+ else
+ equip_idle_set()
+ equip(sets.aftercast[tp_level])
+ end
+ set_priorities('mp','hp')
+end
+
+function buff_change(name,gol,tab)
+ if name == 'Mana Wall' and gol and not midaction() then
+ equip(sets.aftercast.Idle[sets.aftercast.Idle.ind],sets.aftercast.Idle['Mana Wall'])
+ end
+ set_priorities('mp','hp')
+end
+
+function self_command(command)
+ if command:lower() == 'stuntarg' then
+ stuntarg = player.target.name
+ elseif command:lower() == 'macc' then
+ macc_level = (macc_level+1)%2
+ equip(sets.midcast['Elemental Magic'][macc_level][mp_efficiency])
+ if macc_level == 1 then windower.add_to_chat(8,'MMMMMMAcctivated!')
+ else windower.add_to_chat(8,'MDamaged') end
+ elseif command:lower() == 'idle' then
+ sets.aftercast.Idle.ind = (sets.aftercast.Idle.ind+1)%2
+ windower.add_to_chat(8,'------------------------ '..sets.aftercast.Idle.keys[sets.aftercast.Idle.ind]..' Set is now the default Idle set -----------------------')
+ end
+ set_priorities('mp','hp')
+end
+
+-- This function is user defined, but never called by GearSwap itself. It's just a user function that's only called from user functions. I wanted to check the weather and equip a weather-based set for some spells, so it made sense to make a function for it instead of replicating the conditional in multiple places.
+
+function weathercheck(spell_element,set)
+ if not set then return end
+ if spell_element == world.weather_element or spell_element == world.day_element then
+ equip(set,sets.Obis[spell_element])
+ else
+ equip(set)
+ end
+ if set[spell_element] then equip(set[spell_element]) end
+end
+
+function zodiaccheck(spell_element)
+ if spell_element == world.day_element and spell_element ~= 'Dark' and spell_element ~= 'Light' then
+ equip(sets.Zodiac)
+ end
+end
+
+function equip_idle_set()
+ if buffactive['Mana Wall'] then
+ equip(sets.aftercast.Idle[sets.aftercast.Idle.ind],sets.aftercast.Idle['Mana Wall'])
+ else
+ equip(sets.aftercast.Idle[sets.aftercast.Idle.ind])
+ end
+ if player.tp == 3000 then equip(sets.aftercast.Chry) end
+ set_priorities('mp','hp')
+end
+
+function set_priorities(key1,key2)
+ local future,current = gearswap.equip_list,gearswap.equip_list_history
+ function get_val(piece,key)
+ if piece and type(piece)=='table' and piece[key] and type(piece[key])=='number' then
+ return piece[key]
+ end
+ return 0
+ end
+ local diff = {}
+ for i,v in pairs(future) do
+ local priority = get_val(future[i],key1) - get_val(current[i],key1) + (get_val(future[i],key2) - get_val(current[i],key2))
+ if type(v) == 'table' then
+ future[i].priority = priority
+ else
+ future[i] = {name=v,priority=priority}
+ end
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BRD.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BRD.lua
new file mode 100644
index 0000000..6c5812f
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_BRD.lua
@@ -0,0 +1,384 @@
+include('organizer-lib')
+
+function get_sets()
+ sets.precast = {}
+ sets.precast.JA = {}
+
+ sets.weapons = {sub="Twashtar"}
+ -- Precast Sets
+ sets.precast.JA.Nightingale = {feet="Bihu Slippers +1"}
+
+ sets.precast.JA.Troubadour = {body="Bihu Justaucorps +1"}
+
+ sets.precast.JA['Soul Voice'] = {legs="Bihu Cannions +1"}
+
+ sets.precast.FC = {}
+
+ sets.precast.FC.Song = {
+ main="Sangoma",
+ sub="Genmei Shield",
+ range={ name="Linos", augments={'Accuracy+11','Occ. quickens spellcasting +3%',}},
+ ammo=empty,
+ head="Fili Calot +1",
+ body="Inyanga Jubbah +1",
+ hands={ name="Gende. Gages +1", augments={'Phys. dmg. taken -3%','Song spellcasting time -5%',}},
+ legs="Querkening Brais",
+ feet={ name="Telchine Pigaches", augments={'Song spellcasting time -7%',}},
+ neck="Orunmila's Torque",
+ waist="Flume Belt +1",
+ left_ear="Loquac. Earring",
+ right_ear="Enchntr. Earring +1",
+ left_ring="Defending Ring",
+ right_ring="Weather. Ring +1",
+ back="Perimede Cape", -- 80% FC, 10% quickens
+ }
+ sets.precast['Honor March'] = {range="Marsyas",ammo=empty}
+
+ sets.precast.FC.Normal = {
+ main="Sangoma",
+ sub="Genmei Shield",
+ range={ name="Linos", augments={'Accuracy+11','Occ. quickens spellcasting +3%',}},
+ ammo=empty,
+ head="Nahtirah Hat",
+ neck="Orunmila's Torque",
+ left_ear="Loquac. Earring",
+ right_ear="Enchntr. Earring +1",
+ body="Inyanga Jubbah +1",
+ hands="Gendewitha Gages +1",
+ ring1="Kishar Ring",
+ ring2="Weather. Ring +1",
+ back={ name="Intarabus's Cape", augments={'CHR+20','Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',}},
+ waist="Witful Belt",
+ legs="Lengo Pants",
+ feet="Chelona Boots +1"} -- 71% FC, 10% Quickens
+
+ sets.precast.Cure = {
+ main="Felibre's Dague",
+ sub="Genbu's Shield",
+ body="Heka's Kalasiris",
+ legs="Doyen Pants",
+ back="Pahtli Cape"
+ }
+
+ sets.precast.EnhancingMagic = {waist="Siegel Sash"}
+
+ sets.precast.WS = {}
+ sets.precast.WS['Mordant Rime'] = {
+ range={ name="Linos", augments={'Accuracy+15','"Dbl.Atk."+3','Quadruple Attack +3',}},
+ head="Brioso Roundlet +2",
+ body={ name="Bihu Jstcorps +1", augments={'Enhances "Troubadour" effect',}},
+ hands={ name="Leyline Gloves", augments={'Accuracy+15','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Fast Cast"+3',}},
+ legs="Jokushu Haidate",
+ feet="Aya. Gambieras +1",
+ neck="Fotia Gorget",
+ waist="Grunfeld Rope",
+ left_ear="Ishvara Earring",
+ right_ear="Mache Earring +1",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ramuh Ring +1",
+ back={ name="Intarabus's Cape", augments={'DEX+20','Accuracy+20 Attack+20','"Dual Wield"+10',}},
+ }
+ --[[sets.precast.WS['Mordant Rime'] = {
+ range="Gjallarhorn",
+ ammo=empty,
+ head="Brioso Roundlet +2",
+ body={ name="Bihu Jstcorps +1", augments={'Enhances "Troubadour" effect',}},
+ hands="Brioso Cuffs +2",
+ legs={ name="Bihu Cannions +1", augments={'Enhances "Soul Voice" effect',}},
+ feet="Brioso Slippers +2",
+ neck="Fotia Gorget",
+ waist="Windbuffet Belt +1",
+ left_ear="Ishvara Earring",
+ right_ear="Mache Earring +1",
+ left_ring="Carb. Ring +1",
+ right_ring="Carb. Ring +1",
+ back={ name="Intarabus's Cape", augments={'CHR+20','Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',}},
+ }]]
+
+ sets.precast.WS['Evisceration'] = {range={ name="Linos", augments={'Accuracy+15','"Dbl.Atk."+3','Quadruple Attack +3',}},ammo=empty,
+ head="Lustratio Cap +1",neck="Fotia Gorget",ear1="Mache earring +1",ear2="Kuwunga Earring",
+ body="Bihu Justaucorps +1",
+ hands="Leyline Gloves",
+ ring1="Ramuh Ring +1",
+ ring2="Begrudging Ring",
+ back="Rancorous Mantle",
+ waist="Fotia Belt",
+ legs="Lustratio Subligar +1",
+ feet="Aya. Gambieras +1",}
+
+ sets.precast.WS["Rudra's Storm"] = {range={ name="Linos", augments={'Accuracy+15','"Dbl.Atk."+3','Quadruple Attack +3',}},ammo=empty,
+ head="Lustratio Cap +1",neck="Caro Necklace",ear1="Moonshade Earring",ear2="Mache earring +1",
+ body="Bihu Justaucorps +1",hands="Leyline Gloves",ring1="Ramuh Ring +1",ring2="Ramuh Ring +1",
+ back="Letalis Mantle",waist="Grunfeld Rope",legs="Lustratio Subligar +1",feet="Lustratio Leggings +1"}
+
+ sets.precast.WS['Aeolian Edge'] = {
+ head="Welkin Crown",
+ body={ name="Bihu Jstcorps +1", augments={'Enhances "Troubadour" effect',}},
+ hands={ name="Chironic Gloves", augments={'Mag. Acc.+24 "Mag.Atk.Bns."+24','Enmity-3','INT+3','Mag. Acc.+10','"Mag.Atk.Bns."+10',}},
+ legs="Gyve Trousers",
+ feet={ name="Lustra. Leggings +1", augments={'HP+65','STR+15','DEX+15',}},
+ neck="Baetyl Pendant",
+ waist="Eschan Stone",
+ left_ear="Friomisi Earring",
+ right_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ left_ring="Shiva Ring +1",
+ right_ring="Shiva Ring +1",
+ }
+
+ -- Midcast Sets
+ sets.midcast = {}
+
+ sets.midcast.Haste = {main="Mafic Cudgel",sub="Genmei Shield",
+ head={name="Nahtirah Hat",priority=11},neck="Orunmila's Torque",ear1="Loquac. Earring",ear2={name="Gifted Earring",priority=10},
+ body={name="Zendik Robe",priority=12},hands={name="Gendewitha Gages +1",priority=6},ring2={name="Kishar Ring",priority=7},
+ back={name="Pahtli Cape",priority=9},waist="Ninurta's Sash",legs="Bihu Cannions +1",feet={name="Chelona Boots +1",priority=8}}
+
+ sets.midcast.Debuff = {
+ main="Carnwenhan",
+ sub="Ammurapi Shield",
+ range="Gjallarhorn",
+ ammo=empty,
+ head="Brioso Roundlet +2",
+ body={ name="Chironic Doublet", augments={'Mag. Acc.+20 "Mag.Atk.Bns."+20','"Resist Silence"+5','CHR+7','Mag. Acc.+15',}},
+ hands="Inyan. Dastanas +1",
+ legs="Inyanga Shalwar +1",
+ feet="Brioso Slippers +2",
+ neck="Canto Necklace +1",
+ waist="Luminary Sash",
+ left_ear="Dignitary's Earring",
+ right_ear="Enchntr. Earring +1",
+ left_ring="Carb. Ring +1",
+ right_ring="Carb. Ring +1",
+ back={ name="Intarabus's Cape", augments={'CHR+20','Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',}},
+ }
+
+ sets.midcast.Duration = {
+ main="Carnwenhan",
+ body="Fili Hongreline +1",
+ legs="Inyanga Shalwar +1",
+ feet="Brioso Slippers +2",
+ neck="Moonbow Whistle",
+ }
+
+ sets.midcast.Buff = {
+ main="Carnwenhan",
+ sub="Genmei Shield",
+ head="Fili Calot +1",
+ neck="Moonbow Whistle",
+ body="Fili Hongreline +1",
+ hands="Fili Manchettes +1",
+ legs="Inyanga Shalwar +1",
+ feet="Brioso Slippers +2",
+ }
+
+ sets.midcast.DBuff = {range="Daurdabla",ammo=empty}
+
+ sets.midcast.GBuff = {range="Gjallarhorn",ammo=empty}
+
+
+ sets.midcast.Ballad = {legs="Fili Rhingrave +1"}
+
+ sets.midcast.Madrigal = {back={ name="Intarabus's Cape", augments={'CHR+20','Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',}},}
+
+ sets.midcast.Prelude = {back={ name="Intarabus's Cape", augments={'CHR+20','Mag. Acc+20 /Mag. Dmg.+20','"Fast Cast"+10',}},}
+
+ sets.midcast.Scherzo = {feet="Fili Cothurnes +1"}
+
+ sets.midcast.Paeon = {head="Brioso Roundlet +2"}
+
+ sets.midcast.March = {hands="Fili Manchettes +1",}
+
+ sets.midcast.Lullaby = {range="Daurdabla",hands="Brioso Cuffs +2"}
+
+ sets.midcast['Honor March'] = {
+ range="Marsyas",
+ ammo=empty,
+ hands="Fili Manchettes +1",
+ }
+
+
+ sets.midcast.Waltz = {}
+
+ sets.midcast.Cure = {main="Chatoyant Staff",head="Marduk's Tiara +1",neck="Phalaina Locket",ear2="Novia earring",
+ body="Heka's Kalasiris",hands="Revealer's Mitts +1",legs="Bihu Cannions +1",feet="Bihu Slippers +1"}
+
+ sets.midcast.Stoneskin = {head="Marduk's Tiara +1",neck="Nodens Gorget",body="Inyanga Jubbah +1",
+ legs="Shedir Seraweels",feet="Bihu Slippers +1"}
+
+ sets.midcast.Cursna={
+ head={ name="Vanya Hood", augments={'Healing magic skill +20','"Cure" spellcasting time -7%','Magic dmg. taken -3',}},
+ body="Vanya Robe",
+ hands="Hieros Mittens",
+ legs={ name="Vanya Slops", augments={'Healing magic skill +20','"Cure" spellcasting time -7%','Magic dmg. taken -3',}},
+ left_ring="Haoma's Ring",
+ right_ring="Haoma's Ring",
+ back="Oretan. Cape +1",
+ feet="Vanya Clogs",
+ neck="Debilis Medallion",
+ }
+
+
+ --Aftercast Sets
+ sets.aftercast = {}
+ sets.aftercast.Regen = {main={name="Sangoma",priority=15},sub={name="Genmei Shield",priority=16},range={name="Oneiros Harp",priority=14},ammo={name=empty,priority=13},
+ head="Bihu Roundlet +1",neck="Loricate Torque +1",ear1={name="Loquac. Earring",priority=7},ear2={name="Gifted Earring",priority=5},
+ body="Ischemia Chasuble",hands={name="Umuthi Gloves",priority=9},ring1="Defending Ring",ring2={name="Dark Ring",priority=8},
+ back="Umbra Cape",waist="Flume Belt +1",legs={name="Lengo Pants",priority=6},feet="Fili Cothurnes +1"}
+
+ sets.aftercast.PDT = {
+ main="Sangoma",
+ sub="Genmei Shield",
+ range="Oneiros Harp",
+ ammo=empty,
+ head="Lithelimb Cap",
+ body="Emet Harness +1",
+ hands="Umuthi Gloves",
+ legs="Jokushu Haidate",
+ feet="Fili Cothurnes +1",
+ neck="Loricate Torque +1",
+ waist="Flume Belt +1",
+ left_ear="Loquac. Earring",
+ right_ear="Gifted Earring",
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ back="Solemnity Cape",
+ }
+
+ sets.aftercast.Engaged = {
+ range={ name="Linos", augments={'Accuracy+15','"Dbl.Atk."+3','Quadruple Attack +3',}},
+ ammo=empty,
+ head="Aya. Zucchetto +1",
+ body="Ayanmo Corazza +1",
+ hands={ name="Leyline Gloves", augments={'Accuracy+15','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Fast Cast"+3',}},
+ legs="Jokushu Haidate",
+ feet="Aya. Gambieras +1",
+ neck="Lissome Necklace",
+ waist="Windbuffet Belt +1",
+ left_ear="Telos Earring",
+ right_ear="Mache Earring +1",
+ left_ring="Ramuh Ring +1",
+ right_ring="Rajas Ring",
+ back={ name="Intarabus's Cape", augments={'DEX+20','Accuracy+20 Attack+20','"Dual Wield"+10',}},
+ }
+
+ sets.aftercast._tab = {'Regen','PDT'}
+
+ sets.aftercast._index = 1
+
+ sets.aftercast.Idle = sets.aftercast[sets.aftercast._tab[sets.aftercast._index]]
+
+ sets.midcast.Base = sets.aftercast.PDT -- sets.midcast.Haste
+
+ DaurdSongs = T{'Water Carol','Water Carol II','Herb Pastoral','Goblin Gavotte'}
+
+ send_command('input /macro book 3;wait .1;input /macro set 1')
+ timer_reg = {}
+ pianissimo_cycle = false
+end
+
+function pretarget(spell)
+ if spell.type == 'BardSong' and spell.target.type and spell.target.type == 'PLAYER' and not buffactive.pianissimo and not spell.target.charmed and not pianissimo_cycle then
+ cancel_spell()
+ pianissimo_cycle = true
+ send_command('input /ja "Pianissimo" <me>;wait 1.5;input /ma "'..spell.name..'" '..spell.target.name..';')
+ return
+ end
+ if spell.name ~= 'Pianissimo' then
+ pianissimo_cycle = false
+ end
+end
+
+function precast(spell)
+ if spell.type == 'BardSong' then
+ equip_song_gear(spell)
+ equip(sets.precast.FC.Song)
+ if spell.english == 'Honor March' then
+ equip(sets.precast['Honor March'])
+ end
+ elseif spell.action_type == 'Magic' then
+ equip(sets.precast.FC.Normal)
+ if string.find(spell.english,'Cur') and spell.name ~= 'Cursna' then
+ equip(sets.precast.Cure)
+ end
+ if spell.skill == 'Enhancing Magic' then
+ equip(sets.precast.EnhancingMagic)
+ end
+ elseif spell.prefix == '/weaponskill' and sets.precast.WS[spell.name] then
+ equip(sets.precast.WS[spell.name])
+ end
+
+ --if player.status == 'Engaged' then equip({range=nil}) end -- Why?
+end
+
+function midcast(spell)
+ if spell.type == 'BardSong' then
+ equip_song_gear(spell)
+ elseif string.find(spell.english,'Waltz') and spell.english ~= 'Healing Waltz' then
+ equip(sets.midcast.Base,sets.midcast.Waltz)
+ elseif sets.midcast[spell.english] then
+ equip(sets.midcast.Base,sets.midcast[spell.english])
+ elseif string.find(spell.english,'Cur') then
+ equip(sets.midcast.Base,sets.midcast.Cure)
+ elseif spell.prefix == '/weaponskill' and sets.precast.WS[spell.name] then
+ equip(sets.precast.WS[spell.name])
+ else
+ equip(sets.midcast.Base)
+ end
+
+ if sets.precast.JA[spell.english] then equip(sets.precast.JA[spell.english]) end
+end
+
+function aftercast(spell)
+ if midaction() then return end
+
+ if player.status == 'Engaged' then
+ equip(sets.aftercast.Engaged)
+ else
+ equip(sets.aftercast.Idle)
+ end
+end
+
+function status_change(new,old)
+ if new == 'Engaged' then
+ equip(sets.aftercast.Engaged)
+ --disable('main','sub','ammo')
+ elseif T{'Idle','Resting'}:contains(new) then
+ equip(sets.aftercast.Idle)
+ end
+end
+
+function self_command(cmd)
+ if cmd == 'unlock' then
+ enable('main','sub','ammo')
+ elseif cmd == 'midact' then
+ midaction(false)
+ elseif cmd == 'idle' then
+ sets.aftercast._index = sets.aftercast._index%(#sets.aftercast._tab) + 1
+ windower.add_to_chat(8,'Aftercast Set: '..sets.aftercast._tab[sets.aftercast._index])
+ sets.aftercast.Idle = sets.aftercast[sets.aftercast._tab[sets.aftercast._index]]
+ equip(sets.aftercast.Idle)
+ end
+end
+
+function equip_song_gear(spell)
+ if DaurdSongs:contains(spell.english) then
+ equip(sets.midcast.Base,sets.midcast.DBuff)
+ else
+ if spell.target.type == 'MONSTER' then
+ equip(sets.midcast.Base,sets.midcast.Debuff,sets.midcast.GBuff)
+ if buffactive.troubadour or buffactive['elemental seal'] then
+ equip(sets.midcast.Duration,{range="Marsyas",ammo="empty"})
+ end
+ if string.find(spell.english,'Lullaby') then equip(sets.midcast.Duration,sets.midcast.Lullaby) end
+ else
+ equip(sets.midcast.Base,sets.midcast.Buff,sets.midcast.GBuff)
+ if spell.english == 'Honor March' then equip(sets.midcast['Honor March'])
+ elseif string.find(spell.english,'Ballad') then equip(sets.midcast.Ballad)
+ elseif string.find(spell.english,'Scherzo') then equip(sets.midcast.Scherzo)
+ elseif string.find(spell.english,'Paeon') then equip(sets.midcast.Paeon)
+ elseif string.find(spell.english,'Prelude') then equip(sets.midcast.Prelude)
+ elseif string.find(spell.english,'Madrigal') then equip(sets.midcast.Madrigal)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_DNC.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_DNC.lua
new file mode 100644
index 0000000..787bf26
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_DNC.lua
@@ -0,0 +1,729 @@
+include('organizer-lib')
+
+function get_sets()
+
+ ta_hands = {name="Adhemar Wristbands +1"}
+ acc_hands = {name="Adhemar Wristbands +1"}
+ wsd_hands = {name="Maxixi Bangles +3",}
+ crit_hands = {name="Adhemar Wristbands +1"}
+ dt_hands = { name="Herculean Gloves", augments={'Accuracy+30','Damage taken-4%','STR+9','Attack+4',}}
+ waltz_hands = { name="Herculean Gloves", augments={'"Waltz" potency +10%','CHR+9','Attack+4',}}
+
+ sets.subs = {sub="Airy Buckler"}
+
+ ------------------- JA Sets ----------------------
+ sets.JA = {}
+ sets.JA.Trance = {head="Horos Tiara +1"}
+ sets.JA.Precast_Waltz = {legs="Desultor Tassets"}
+
+ waltz_mode = 0
+ sets.JA.Waltz = {}
+ sets.JA.Waltz[0] = {
+ ammo="Yamarang",
+ head={ name="Anwig Salade", augments={'CHR+4','"Waltz" ability delay -2','CHR+2','"Fast Cast"+2',}},
+ body="Maxixi Casaque +2",
+ hands=waltz_hands,
+ legs={ name="Desultor Tassets", augments={'"Waltz" TP cost -5',}},
+ feet="Maxixi Toeshoes +2",
+ neck="Unmoving Collar +1",
+ waist="Aristo Belt",
+ left_ear="Eabani Earring",
+ right_ear="Roundel Earring",
+ left_ring="Carb. Ring +1",
+ right_ring="Carb. Ring +1",
+ back={ name="Senuna's Mantle", augments={'"Waltz" potency +10%',}},
+ }
+
+ sets.JA.Waltz[1] = {
+ ammo="Yamarang",
+ head="Maxixi Tiara +2",
+ neck="Unmoving Collar +1",
+ lear="Enchanter Earring +1",
+ rear="Roundel Earring",
+ body="Maxixi Casaque +2",
+ hands=waltz_hands,
+ lring="Carbuncle Ring +1",
+ rring="Carbuncle Ring +1",
+ back={ name="Senuna's Mantle", augments={'"Waltz" potency +10%',}},
+ waist="Aristo Belt",
+ legs="Desultor Tassets",
+ feet="Maxixi toeshoes +2"
+ }
+
+ --[[sets.JA.Waltz[2] = {
+ ammo="Yamarang",
+ head="Anwig Salade",
+ neck="Unmoving Collar +1",
+ lear="Enchanter Earring +1",
+ rear="Handler's Earring +1",
+ body="Maxixi Casaque +3",
+ hands=waltz_hands,
+ lring="Carbuncle Ring +1",
+ rring="Carbuncle Ring +1",
+ back="Senuna Mantle", (Waltz +10%, CHR+30)
+ waist="Aristo Belt",
+ legs="Desultor Tassets",
+ feet="Maxixi toeshoes +3"
+ }]]
+
+ sets.JA.Samba = {head="Maxixi Tiara +2",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','Crit.hit rate+10',}},}
+
+ sets.JA.Jig = {legs='Horos Tights +1',feet="Maxixi toeshoes +2"}
+
+ sets.JA.Step = {
+ ammo="Yamarang",
+ head="Maxixi Tiara +2",
+ body="Adhemar Jacket +1",
+ hands="Maxixi Bangles +3",
+ legs="Mummu Kecks +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Combatant's torque",
+ waist="Olseni Belt",
+ left_ear="Mache earring +1",
+ right_ear="Telos Earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ramuh Ring +1",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','Crit.hit rate+10',}},
+ }
+
+ sets.JA['Feather Step'] = set_combine(sets.JA.Step,{feet="Maculele Toeshoes +1"})
+
+ sets.JA['No Foot Rise'] = {body="Horos Casaque +1"}
+
+ sets.JA['Climactic Flourish'] = {head="Maculele Tiara +1"}
+
+ sets.JA['Striking Flourish'] = {body="Maculele casaque +1"}
+
+ sets.JA['Reverse Flourish'] = {hands="Maculele bangles +1",back={ name="Toetapper Mantle", augments={'"Store TP"+1','"Dual Wield"+5','"Rev. Flourish"+30',}}}
+
+ sets.JA['Violent Flourish'] = {
+ ammo="Hydrocera",
+ head="Dampening Tam",
+ body={ name="Horos Casaque +1", augments={'Enhances "No Foot Rise" effect',}},
+ hands="Leyline Gloves",
+ legs={ name="Herculean Trousers", augments={'Mag. Acc.+16','"Fast Cast"+6','MND+2',}},
+ feet={ name="Herculean Boots", augments={'Mag. Acc.+16','"Fast Cast"+6','MND+4',}},
+ neck="Sanctity Necklace",
+ waist="Eschan Stone",
+ left_ear="Enchanter Earring +1",
+ right_ear="Dignitary's Earring",
+ left_ring="Weather. Ring +1",
+ right_ring="Sangoma Ring",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','Crit.hit rate+10',}},
+ }
+
+ sets.Enmity = {
+ ammo="Iron gobbet",
+ head="Halitus Helm",
+ neck="Unmoving Collar +1",
+ lear="Trux Earring",
+ rear="Pluto's Pearl", -- Cryptic Earring from Rancibus is better
+ body="Emet Harness +1",
+ hands="Kurys Gloves",
+ lring="Provocare Ring",
+ rring="Eihwaz Ring",
+ back="Reiki Cloak", -- Augmented Senuna's is better
+ waist="Kasiri Belt", -- Trance Belt is better
+ legs="Zoar Subligar +1",
+ feet="Ahosi Leggings",
+ }
+
+ sets.JA['Animated Flourish'] = sets.Enmity
+ sets.JA.Provoke = sets.Enmity
+ sets.JA.Warcry = sets.Enmity
+
+
+ ------------------ Idle Sets ---------------------
+ sets.Idle = {}
+ sets.Idle.index = {'Normal','MDT'}
+ Idle_ind = 1
+ sets.Idle.Normal = {main="Terpsichore",sub="Twashtar",ammo="Tengu-no-Hane",
+ head="Meghanada Visor +1",neck="Ej Necklace +1",lear="Eabani Earring",rear="Infused Earring",
+ body="Emet Harness +1",hands=dt_hands,lring="Sheltered Ring",rring="Vengeful Ring",
+ back={ name="Senuna's Mantle", augments={'AGI+20','Eva.+20 /Mag. Eva.+20','Haste+10',}},waist="Kasiri Belt",legs="Maculele Tights +1",feet="Skadi's Jambeaux +1"}
+
+ sets.Idle.MDT={head="Dampening Tam",neck="Loricate Torque +1",lear="Etiolation Earring",
+ body="Emet Harness +1",hands=dt_hands,lring="Dark Ring",rring="Defending Ring",
+ back="Mollusca Mantle",waist="Wanion Belt",legs="Maculele Tights +1",feet="Maxixi toeshoes +2"}
+
+ ------------------- TP Sets ----------------------
+ sets.TP={}
+ sets.TP.index = {'Acc0','Acc1','Acc2','Eva'}
+ TP_ind = 1
+ sets.DT_on = false
+
+ -- Works for Haste 2 + Haste Samba
+ sets.TP.Acc0 = {
+ main="Terpsichore",
+ sub="Twashtar",
+ ammo="Charis Feather",
+ head="Adhemar Bonnet +1",
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ legs={ name="Samnuha Tights", augments={'STR+8','DEX+9','"Dbl.Atk."+3','"Triple Atk."+2',}},
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Anu Torque",
+ waist="Windbuffet Belt +1",
+ left_ear="Sherida Earring",
+ right_ear="Telos Earring",
+ left_ring="Epona's Ring",
+ right_ring="Rajas Ring",
+ back={ name="Senuna's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Store TP"+10',}},
+ }
+
+ sets.TP.Acc1 = {
+ main="Terpsichore",
+ sub="Twashtar",
+ ammo="Yamarang",
+ head="Mummu Bonnet +1",
+ body={ name="Adhemar Jacket +1", augments={'STR+12','DEX+12','Attack+20',}},
+ hands={ name="Adhemar Wrist. +1", augments={'STR+12','DEX+12','Attack+20',}},
+ legs="Mummu Kecks +1",
+ feet="Ahosi Leggings",
+ neck="Combatant's Torque",
+ waist="Windbuffet Belt +1",
+ left_ear="Sherida Earring",
+ right_ear="Telos Earring",
+ left_ring="Epona's Ring",
+ right_ring="Rajas Ring",
+ back={ name="Senuna's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','"Store TP"+10',}},
+ }
+
+ --[[sets.TP.Acc1 = {
+ main="Terpsichore",
+ sub="Twashtar",
+ ammo="Yamarang",
+ head={ name="Dampening Tam", augments={'DEX+10','Accuracy+15','Mag. Acc.+15','Quadruple Attack +3',}},
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ legs={ name="Samnuha Tights", augments={'STR+8','DEX+9','"Dbl.Atk."+3','"Triple Atk."+2',}},
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Combatant's Torque",
+ waist="Grunfeld Rope",
+ left_ear="Sherida Earring",
+ right_ear="Telos Earring",
+ left_ring="Epona's Ring",
+ right_ring="Rajas Ring",
+ back={ name="Senuna's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Store TP"+10',}},
+ }]]
+
+ sets.TP.Acc2 = {
+ main="Terpsichore",
+ sub="Twashtar",
+ ammo="Yamarang",
+ head={ name="Dampening Tam", augments={'DEX+10','Accuracy+15','Mag. Acc.+15','Quadruple Attack +3',}},
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ legs="Mummu Kecks +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Combatant's Torque",
+ waist="Olseni Belt",
+ left_ear="Mache earring +1",
+ right_ear="Telos Earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ramuh Ring +1",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','Crit.hit rate+10',}},
+ }
+
+ sets.TP.DT = {
+ main="Terpsichore",
+ sub="Twashtar",
+ ammo="Yamarang",
+ head={ name="Dampening Tam", augments={'DEX+10','Accuracy+15','Mag. Acc.+15','Quadruple Attack +3',}},
+ body="Emet Harness +1",
+ hands=dt_hands,
+ legs="Mummu Kecks +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+20 Attack+20','Phys. dmg. taken -5%','DEX+10','Accuracy+6',}},
+ neck="Loricate Torque +1",
+ waist="Windbuffet belt +1",
+ right_ear="Suppanomimi",
+ left_ear="Eabani Earring",
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ right_ring="Defending Ring",
+ back="Mollusca Mantle",
+ }
+
+ sets.TP.Eva = {
+ main="Terpsichore",
+ sub="Twashtar",
+ ammo="Yamarang",
+ head="Maxixi Tiara +2",
+ body="Maxixi Casaque +2",
+ hands="Maxixi Bangles +3",
+ legs="Maculele Tights +1",
+ feet="Maxixi Toeshoes +2",
+ neck="Ej Necklace +1",
+ waist="Svelt. Gouriz +1",
+ left_ear="Eabani Earring",
+ right_ear="Infused Earring",
+ left_ring="Beeline Ring",
+ right_ring="Vengeful Ring",
+ back={ name="Senuna's Mantle", augments={'AGI+20','Eva.+20 /Mag. Eva.+20','Haste+10',}},
+ }
+
+ sets.TP.CHR = {
+ main="Terpsichore",
+ sub="Twashtar",
+ head="Dampening Tam",
+ }
+
+ ------------------- WS Sets ----------------------
+ sets.WS={}
+
+ -- Moonshade option sets the left ear to be Moonshade if TP < 2800
+ -- Madrigal option sets the right ear to be Kuwaliaoi Attack+17/STR/DEX+2 if you have a madrigal on
+
+ sets.WS.Exenterator = {Moonshade=false,Madrigal=false,Tengu=true}
+
+ sets.WS.Exenterator[0] = {
+ ammo="Floestone",
+ head="Meghanada Visor +1",
+ body={ name="Adhemar Jacket +1", augments={'STR+12','DEX+12','Attack+20',}},
+ hands="Maxixi Bangles +3",
+ legs="Meg. Chausses +1",
+ feet="Meg. Jam. +1",
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Sherida Earring",
+ right_ear="Infused Earring",
+ left_ring="Epona's Ring",
+ right_ring="Ilabrat Ring",
+ back={ name="Senuna's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','"Dbl.Atk."+10',}},
+ }
+
+ sets.WS['Shark Bite'] = {Moonshade=false,Madrigal=true,Tengu=true}
+
+ sets.WS['Shark Bite'][0] = {
+ ammo="Floestone",
+ head="Meghanada Visor +1",
+ body="Adhemar Jacket +1",
+ hands=wsd_hands,
+ legs="Mummu Kecks +1",
+ feet="Adhemar Gamashes +1",
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Mache earring +1",
+ right_ear="Ishvara Earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ilabrat Ring",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','DEX+10','Weapon skill damage +10%',}},
+ }
+
+ sets.WS.Evisceration = {Moonshade=false,Madrigal=true}
+
+ sets.WS.Evisceration[0] = {
+ ammo="Charis Feather",
+ head="Adhemar Bonnet +1",
+ body="Abnoba Kaftan",
+ hands=crit_hands,
+ legs="Lustratio Subligar +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+29','Crit. hit damage +5%','DEX+7','Attack+13',}},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Sherida Earring",
+ right_ear="Mache earring +1",
+ left_ring="Begrudging Ring",
+ right_ring="Ilabrat Ring",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','Crit.hit rate+10',}},
+ }
+
+ sets.WS.Evisceration[1] = {
+ ammo="Falcon Eye",
+ head="Dampening Tam",
+ body="Adhemar Jacket +1",
+ hands=crit_hands,
+ legs="Lustratio Subligar +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+29','Crit. hit damage +5%','DEX+7','Attack+13',}},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Sherida Earring",
+ right_ear="Mache earring +1",
+ left_ring="Begrudging Ring",
+ right_ring="Ramuh Ring +1",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','Crit.hit rate+10',}},
+ }
+
+ sets.WS['Pyrrhic Kleos'] = {Moonshade=false,Madrigal=true,Tengu=true}
+
+ sets.WS['Pyrrhic Kleos'][0] ={
+ ammo="Floestone",
+ head="Lustratio Cap +1",
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ legs={ name="Samnuha Tights", augments={'STR+8','DEX+9','"Dbl.Atk."+3','"Triple Atk."+2',}},
+ feet="Lustratio Leggings +1",
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Sherida Earring",
+ right_ear="Brutal Earring",
+ left_ring="Apate Ring",
+ right_ring="Rajas Ring",
+ back={ name="Senuna's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','"Dbl.Atk."+10',}},
+ }
+ sets.WS['Pyrrhic Kleos'][1] ={
+ ammo="Falcon Eye",
+ head={ name="Dampening Tam", augments={'DEX+10','Accuracy+15','Mag. Acc.+15','Quadruple Attack +3',}},
+ body="Adhemar Jacket +1",
+ hands=acc_hands,
+ legs="Lustratio Subligar +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Mache earring +1",
+ right_ear="Telos Earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ramuh Ring +1",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','DEX+10','Weapon skill damage +10%',}},
+ }
+
+ sets.WS['Dancing Edge'] = {Moonshade=false,Madrigal=true,Tengu=true}
+
+ sets.WS['Dancing Edge'][0] = {
+ ammo="Floestone",
+ head="Adhemar Bonnet +1",
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ legs="Mummu Kecks +1",
+ feet="Adhemar Gamashes +1",
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Steelflash Earring",
+ right_ear="Bladeborn Earring",
+ left_ring="Ifrit Ring +1",
+ right_ring="Ilabrat Ring",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','DEX+10','Weapon skill damage +10%',}},
+ }
+
+ sets.WS['Aeolian Edge'] = {Moonshade=true,Madrigal=nil}
+
+ sets.WS['Aeolian Edge'][0] = {
+ ammo="Pemphredo Tathlum",
+ head="Highwing Helm",
+ body={ name="Samnuha Coat", augments={'Mag. Acc.+15','"Mag.Atk.Bns."+15','"Fast Cast"+5','"Dual Wield"+5',}},
+ hands=wsd_hands,
+ legs={ name="Horos Tights +1", augments={'Enhances "Saber Dance" effect',}},
+ feet="Adhemar Gamashes +1",
+ neck="Sanctity Necklace",
+ waist="Wanion Belt",
+ left_ear="Crematio Earring",
+ right_ear="Friomisi Earring",
+ left_ring="Shiva Ring +1",
+ right_ring="Shiva Ring +1",
+ back="Toro Cape",
+ }
+
+ sets.WS["Rudra's Storm"] = {Moonshade=true}
+
+ sets.WS["Rudra's Storm"][0] = {
+ ammo="Charis Feather",
+ head="Lustratio Cap +1",
+ body={ name="Herculean Vest", augments={'Accuracy+21','Crit. hit damage +5%','DEX+9',}},
+ hands=wsd_hands,
+ legs="Lustratio Subligar +1",
+ feet="Lustratio Leggings +1",
+ neck="Caro Necklace",
+ waist="Grunfeld Rope",
+ left_ear="Mache earring +1",
+ right_ear="Ishvara earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ilabrat Ring",
+ back={ name="Senuna's Mantle", augments={'DEX+20','Accuracy+20 Attack+20','DEX+10','Weapon skill damage +10%',}},
+ }
+
+ sets.WS["Rudra's Storm"][1] = set_combine(sets.WS["Rudra's Storm"][0],{
+ ammo="Falcon Eye",
+ head="Dampening Tam",
+ neck="Fotia gorget",
+ right_ring="Ramuh Ring +1",
+ })
+
+ sets.WS.Madrigal = {left_ear="Kuwunga Earring"}
+ sets.WS.Moonshade = {right_ear="Moonshade Earring"}
+ sets.WS.Sherida = {left_ear="Sherida Earring"} -- Will cause conflict with the DT set, but this is still the easiest way to handle it at the moment.
+
+ ------------------- MA Sets ----------------------
+ sets.MA={}
+
+ sets.MA.Utsusemi = {}
+ sets.MA.Utsusemi.Eva = {
+ ammo="Yamarang",
+ head="Maxixi Tiara +2",
+ neck="Ej Necklace +1",
+ ear1="Eabani Earring",
+ ear2="Infused Earring",
+ body="Maxixi Casaque +2",
+ hands="Maxixi Bangles +3",
+ lring="Vengeful Ring",
+ rring="Defending Ring",
+ back={ name="Senuna's Mantle", augments={'AGI+20','Eva.+20 /Mag. Eva.+20','Haste+10',}},
+ waist="Svelt. Gouriz +1",
+ legs="Maculele Tights +1",
+ feet="Maxixi Toeshoes +2",
+ }
+
+ sets.MA.Utsusemi.DT = sets.TP.DT
+
+ sets.MA.FastCast = {
+ ammo="Impatiens",
+ head={ name="Herculean Helm", augments={'"Fast Cast"+6','Mag. Acc.+2',}},
+ body={ name="Taeon Tabard", augments={'Accuracy+22','"Fast Cast"+5','Crit. hit damage +3%',}},
+ hands="Leyline Gloves",
+ legs={ name="Herculean Trousers", augments={'Mag. Acc.+16','"Fast Cast"+6','MND+2',}},
+ feet={ name="Herculean Boots", augments={'Mag. Acc.+16','"Fast Cast"+6','MND+4',}},
+ Neck="Orunmila's Torque",
+ left_ear="Loquac. Earring",
+ right_ear="Enchntr. Earring +1",
+ left_ring="Rahab Ring",
+ right_ring="Weather. Ring +1",
+ back={ name="Senuna's Mantle", augments={'"Fast Cast"+10',}},
+ Utsusemi = {
+ neck="Magoraga Beads",
+ body="Passion Jacket",
+ },
+ }
+
+ sets.tengu = {ammo="Tengu-No-Hane"}
+ sets.frenzy = {head="Frenzy Sallet"}
+
+ send_command('input /macro book 9;wait .1;input /macro set 2')
+ dur_table = {}
+ utsu_index = 'DT'
+ send_command('lua l daze')
+end
+
+function file_unload()
+ send_command('lua u daze')
+end
+
+function precast(spell)
+ if spell.action_type == 'Magic' then
+ equip(sets.MA.FastCast)
+ if string.find(spell.name,'Utsusemi') then
+ equip(sets.MA.FastCast.Utsusemi)
+ end
+ elseif spell.type == 'Waltz' then
+ if buffactive['saber dance'] then
+ windower.ffxi.cancel_buff(410)
+ end
+ equip(sets.JA.Precast_Waltz)
+ elseif spell.type == 'Samba' and buffactive['fan dance'] then
+ windower.ffxi.cancel_buff(411)
+ elseif spell.name == 'Spectral Jig' and buffactive.sneak then
+ windower.ffxi.cancel_buff(71)
+ end
+end
+
+
+local function get_target_type(perspective,str)
+ local user = windower.ffxi.get_mob_by_name(perspective)
+ local mob = windower.ffxi.get_mob_by_id(tonumber(str) or -1) or windower.ffxi.get_mob_by_target(tostring(str)) or windower.ffxi.get_mob_by_name(tostring(str))
+ if user and mob then
+ if mob.id == user.id then
+ return 'Self'
+ elseif mob.hpp == 0 then
+ return 'Corpse'
+ elseif mob.in_party and user.in_party then
+ return 'Party'
+ elseif mob.in_alliance and user.in_alliance then
+ return 'Ally'
+ elseif not mob.is_npc or mob.spawn_type==14 then
+ return 'Player'
+ elseif mob.is_npc then
+ return 'Enemy'
+ -- Not sure how to differentiate NPCs and enemies
+ else
+ return 'NPC'
+ end
+ end
+end
+
+function filtered_action(spell)
+ cancel_spell()
+ noti.message('Requesting Sjugy use '..spell.name)
+ local targ_typ = get_target_type('Sjugy',spell.target.raw)
+
+ if spell.targets[targ_typ] then
+ noti.command('Sjugy',spell.name..' '..spell.target.raw)
+ else
+ noti.command('Sjugy',spell.name)
+ end
+end
+
+function midcast(spell)
+ if sets.JA[spell.name] then
+ equip(sets.JA[spell.name])
+ if spell.name == "Feather Step" then
+ tengu_handler()
+ end
+ elseif sets.WS[spell.name] then
+ if sets.TP.index[TP_ind] == 'Acc2' and sets.WS[spell.name][1] then
+ equip(sets.WS[spell.name][1])
+ elseif sets.WS[spell.name][0] then
+ equip(sets.WS[spell.name][0])
+ end
+ if sets.WS[spell.name].Tengu then
+ tengu_handler()
+ end
+ if buffactive['Madrigal'] and sets.WS[spell.name].Madrigal == true then
+ equip(sets.WS.Madrigal)
+ end
+ if player.tp < 2800 and sets.WS[spell.name].Moonshade == true then
+ equip(sets.WS.Moonshade)
+ if spell.name == "Pyrrhic Kleos" or spell.name == "Dancing Edge" then -- Steelflash/Sherida combo
+ equip(sets.WS.Sherida)
+ end
+ end
+ elseif spell.type=='Jig' then
+ equip(sets.JA.Jig)
+ elseif spell.type=='Samba' then
+ equip(sets.JA.Samba)
+ elseif spell.type=='Waltz' then
+ equip(sets.JA.Waltz[waltz_mode])
+ elseif spell.type=='Step' then
+ equip(sets.JA.Step)
+ tengu_handler()
+ elseif string.find(spell.name,'Utsusemi') then
+ equip(sets.MA.Utsusemi[utsu_index])
+ elseif string.find(spell.name,'Monomi') then
+ send_command('@wait 1.7;cancel 71')
+ end
+
+ if spell.type == 'WeaponSkill' then
+ if buffactive['climactic flourish'] then
+ equip(sets.JA['Climactic Flourish'])
+ elseif buffactive['striking flourish'] then
+ equip(sets.JA['Striking Flourish'])
+ end
+ end
+end
+
+function tengu_handler()
+ if world.time >= 360 and world.time < 1080 then -- 6~18
+ equip(sets.tengu)
+ end
+end
+
+function aftercast(spell)
+ local dur,id
+ if not spell or not spell.name then windower.add_to_chat(8,'Not spell') return end
+ if spell.name:sub(1,11) == 'Chocobo Jig' and (not dur_table['Chocobo Jig'] or not dur_table['Chocobo Jig'] == 190) then
+ id = 176
+ dur = 190
+ elseif spell.name == 'Spectral Jig' and (not dur_table['Spectral Jig'] or not dur_table['Spectral Jig'] == 270) then
+ id = 69
+ dur = 270
+ send_command('st setduration 71 269;')
+ elseif spell.name:sub(7,11) == 'Samba' then
+ if spell.name:sub(1,11) == 'Drain Samba' then
+ id = 368
+ if #spell.name == 11 and (not dur_table['Drain Samba'] or not dur_table['Drain Samba'] == 165) then
+ dur = 165
+ dur_table['Drain Samba'] = 165
+ elseif not dur['Drain Samba'] or not dur['Drain Samba'] == 135 then
+ dur = 135
+ dur_table['Drain Samba'] = 135
+ end
+ elseif spell.name:sub(1,11) == 'Aspir Samba' then
+ id = 369
+ if #spell.name == 11 and (not dur_table['Aspir Samba'] or not dur_table['Aspir Samba'] == 165) then
+ dur = 165
+ dur_table['Aspir Samba'] = 165
+ elseif not dur['Aspir Samba'] or not dur['Aspir Samba'] == 135 then
+ dur = 135
+ dur_table['Aspir Samba'] = 135
+ end
+ elseif spell.name == 'Haste Samba' and (not dur_table['Haste Samba'] or not dur_table['Haste Samba'] == 135) then
+ id = 370
+ dur = 135
+ dur_table['Haste Samba'] = 135
+ end
+
+-- if buffactive['saber dance'] then
+-- dur = math.floor(dur*1.2)
+-- end
+ elseif spell.name == 'Trance' and (not dur_table['Trance'] or not dur_table['Trance'] == 80) then
+ id = 376
+ dur = 80
+ dur_table['Trance'] = 165
+-- elseif spell.name == 'Grand Pas' then
+-- id = 507
+-- dur = 60
+-- elseif spell.name == 'Presto' then
+-- id = 442
+-- dur = 30
+ end
+ if id then
+ send_command('st setduration '..id..' '..(dur-1)..';')
+ end
+
+ equip_inactive_set(spell)
+end
+
+function status_change(new,old)
+ equip_inactive_set(nil,new)
+end
+
+function buff_change(buff,gain)
+ if not gain and not midaction() and (buff == 'Climactic Flourish' or buff=='Striking Flourish' or buff=='Sleep') then
+ equip_inactive_set()
+ elseif gain and buff == 'Sleep' and player.hp > 99 then
+ equip(sets.frenzy)
+ end
+end
+
+function equip_inactive_set(spell,status)
+ status = status or player.status
+ if status == 'Engaged' then
+ equip(sets.TP[sets.TP.index[TP_ind]])
+ tengu_handler()
+ if (buffactive['Climactic Flourish'] or spell and spell.english == 'Climactic Flourish') and sets.TP.index[TP_ind] ~= 'Acc' then
+ equip(sets.TP.CHR,sets.JA['Climactic Flourish'])
+ elseif (buffactive['Striking Flourish'] or spell and spell.english == 'Striking Flourish') and sets.TP.index[TP_ind] ~= 'Acc' then
+ equip(sets.TP.CHR,sets.JA['Striking Flourish'])
+ end
+ else
+ equip(sets.Idle[sets.Idle.index[Idle_ind]])
+ end
+ if sets.DT_on then equip(sets.TP.DT) end
+end
+
+function self_command(command)
+ if command == 'toggle TP set' then
+ TP_ind = TP_ind +1
+ if TP_ind > #sets.TP.index then TP_ind = 1 end
+ windower.add_to_chat(8,'----- TP Set changed to '..sets.TP.index[TP_ind]..' -----')
+ equip(sets.TP[sets.TP.index[TP_ind]])
+ elseif command == 'toggle TP set back' then
+ TP_ind = TP_ind -1
+ if TP_ind < 1 then TP_ind = #sets.TP.index end
+ windower.add_to_chat(8,'----- TP Set changed to '..sets.TP.index[TP_ind]..' -----')
+ equip(sets.TP[sets.TP.index[TP_ind]])
+ elseif command == 'DT' then
+ sets.DT_on = not sets.DT_on
+ if sets.DT_on then
+ equip(sets.TP.DT)
+ elseif not midaction() then
+ equip_inactive_set()
+ end
+ windower.add_to_chat(8,'----- DT mode is '..tostring(sets.DT_on)..' -----')
+ elseif command == 'toggle Idle set' then
+ Idle_ind = Idle_ind +1
+ if Idle_ind > #sets.Idle.index then Idle_ind = 1 end
+ windower.add_to_chat(8,'----- Idle Set changed to '..sets.Idle.index[Idle_ind]..' -----')
+ equip(sets.Idle[sets.Idle.index[Idle_ind]])
+ elseif command == 'equip TP set' then
+ equip_inactive_set()
+ elseif command == 'toggle Utsu set' then
+ if utsu_index == 'DT' then utsu_index = 'Eva'
+ else utsu_index = 'DT' end
+ windower.add_to_chat(8,'----- Utsu Set changed to '..utsu_index..' -----')
+ elseif command == 'waltz_mode' then
+ waltz_mode = (waltz_mode + 1)%2
+ if waltz_mode == 1 then
+ windower.add_to_chat(8,'----- Waltz Efficiency Setting -----')
+ else
+ windower.add_to_chat(8,'----- Waltz Recast Setting (Default) -----')
+ end
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_GEO.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_GEO.lua
new file mode 100644
index 0000000..6fa9750
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_GEO.lua
@@ -0,0 +1,496 @@
+include('organizer-lib')
+
+function get_sets()
+ MP_efficiency = 0
+ macc_level = 0
+
+ sets.TH = {
+ hands={ name="Merlinic Dastanas", augments={'"Treasure Hunter"+2',},hp=9,mp=20},
+ legs={ name="Merlinic Shalwar", augments={'Pet: Accuracy+16 Pet: Rng. Acc.+16','Pet: Haste+1','"Treasure Hunter"+1','Mag. Acc.+9 "Mag.Atk.Bns."+9',},hp=29,mp=44},
+ waist="Chaac Belt",
+ }
+
+ sets.precast = {}
+ sets.Idris = {main="Idris"}
+ sets.Mafic = {main="Mafic Cudgel"}
+
+ sets.precast.FastCast = {}
+
+ sets.precast.FastCast.Default = {
+ main="Marin Staff +1",
+ sub="Niobid Strap",
+ range=empty,
+ ammo="Impatiens",
+ feet={ name="Merlinic Crackows", augments={'"Mag.Atk.Bns."+11','"Fast Cast"+7',}},
+ body="Zendik Robe",
+ hands={ name="Merlinic Dastanas", augments={'Mag. Acc.+29','"Fast Cast"+7','INT+1',}},
+ legs="Psycloth Lappas",
+ head={ name="Merlinic Hood", augments={'"Fast Cast"+7','MND+10','Mag. Acc.+10','"Mag.Atk.Bns."+10',}},
+ neck="Orunmila's Torque",
+ waist="Witful Belt",
+ left_ear="Enchntr. Earring +1",
+ right_ear="Loquac. Earring",
+ left_ring="Lebeche Ring",
+ right_ring="Weather. Ring +1",
+ back={ name="Lifestream Cape", augments={'Geomancy Skill +3','Indi. eff. dur. +20','Damage taken-3%',}},
+ }
+
+ sets.precast.FastCast['Elemental Magic'] = set_combine(sets.precast.FastCast.Default,{
+ ear1="Barkarole Earring",
+ body={ name="Dalmatica +1", augments={'Occ. quickens spellcasting +3%','"Fast Cast"+6','Pet: "Mag.Def.Bns."+6',}},
+ hands="Bagua mitaines +1",
+ lring="Kishar Ring",
+ })
+
+ sets.precast.FastCast['Healing Magic'] = set_combine(sets.precast.FastCast.Default,{main="Vadose Rod",sub="Genmei Shield",body="Heka's Kalasiris",back="Pahtli Cape"})
+
+-- sets.precast.FastCast['Enhancing Magic'] = set_combine(sets.precast.FastCast.Default,{waist="Siegel Sash"})
+
+ sets.Impact = {head=empty,body="Twilight Cloak"}
+
+ sets.midcast = {}
+
+ sets.midcast.Stun = {main="Marin Staff +1",sub="Enki Strap",range=empty,ammo="Hasty Pinion +1",
+ head={ name="Merlinic Hood", augments={'"Fast Cast"+7','MND+10','Mag. Acc.+10','"Mag.Atk.Bns."+10',}}, neck="Erra Pendant", ear1="Enchanter Earring +1",ear2="Loquacious Earring",
+ body="Geomancy Tunic +1",hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',}},lring="Sangoma Ring",rring="Angha Ring",
+ back={ name="Lifestream Cape", augments={'Geomancy Skill +3','Indi. eff. dur. +20','Damage taken-3%',}},waist="Ninurta's Sash",legs="Psycloth Lappas",feet="Amalric Nails +1"}
+
+
+
+ sets.midcast['Elemental Magic'] = {
+ [0] = {},
+ [1] = {}
+ }
+
+ sets.ElementalMagicMAB = {
+ Ice={main="Ngqoqwanb",sub="Enki Strap"},
+ Wind={main="Marin Staff +1",sub="Enki Strap"},
+ Earth={neck="Quanpur Necklace"},
+ Dark={head="Pixie Hairpin +1", rring="Archon Ring"}
+ }
+
+ -- MAcc level 0 (Macc and Enmity irrelevant)
+ sets.midcast['Elemental Magic'][0][0] = {
+ main="Idris",
+ sub="Ammurapi Shield",
+ range=empty,
+ ammo="Pemphredo Tathlum",
+ head={ name="Merlinic Hood", augments={'VIT+8','"Mag.Atk.Bns."+27','Accuracy+5 Attack+5','Mag. Acc.+18 "Mag.Atk.Bns."+18',}},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',}},
+ hands="Amalric Gages +1",
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+30','"Occult Acumen"+10','INT+9','Mag. Acc.+5',}},
+ feet="Amalric Nails +1",
+ neck="Baetyl Pendant",
+ waist="Yamabuki-no-Obi",
+ left_ear="Barkaro. Earring",
+ right_ear="Crematio Earring",
+ left_ring="Shiva Ring +1",
+ right_ring="Shiva Ring +1",
+ back={ name="Nantosuelta's Cape", augments={'INT+20','Mag. Acc+20 /Mag. Dmg.+20','"Mag.Atk.Bns."+10',}},
+ }
+
+ sets.midcast['Elemental Magic'][0][1] = set_combine(sets.midcast['Elemental Magic'][0][0],{body="Seidr Cotehardie"})
+
+ -- MAcc level 1 (MAcc and Enmity relevant)
+ sets.midcast['Elemental Magic'][1][0] = {
+ main="Idris",
+ sub="Ammurapi Shield",
+ range=empty,
+ ammo="Pemphredo Tathlum",
+ head={ name="Merlinic Hood", augments={'VIT+8','"Mag.Atk.Bns."+27','Accuracy+5 Attack+5','Mag. Acc.+18 "Mag.Atk.Bns."+18',}},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',}},
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',}},
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+30','"Occult Acumen"+10','INT+9','Mag. Acc.+5',}},
+ feet="Amalric Nails +1",
+ neck="Sanctity Necklace",
+ waist={name="Yamabuki-no-Obi", stats={INT=8}},
+ ear2={name="Novia Earring", stats={Enmity=-7}},
+ ear1="Barkarole Earring",
+ ring1={name="Shiva Ring +1", stats={INT=9,MAB=3}},
+ ring2={name="Shiva Ring +1", stats={INT=9,MAB=3}},
+ back={ name="Nantosuelta's Cape", augments={'INT+20','Mag. Acc+20 /Mag. Dmg.+20','"Mag.Atk.Bns."+10',}},
+ }
+
+ sets.midcast['Elemental Magic'][1][1] = set_combine(sets.midcast['Elemental Magic'][1][0],{body="Seidr Cotehardie"})
+
+
+
+ sets.midcast['Dark Magic'] = {
+ main={ name="Rubicundity", augments={'Mag. Acc.+10','"Mag.Atk.Bns."+10','Dark magic skill +10','"Conserve MP"+7',}},
+ sub="Ammurapi Shield",
+ range=empty,
+ ammo="Hasty Pinion +1",
+ head="Pixie Hairpin +1",
+ body="Psycloth Vest",
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',}},
+ legs="Psycloth Lappas",
+ feet={ name="Merlinic Crackows", augments={'Mag. Acc.+21','"Drain" and "Aspir" potency +11','INT+1',}},
+ neck="Erra Pendant",
+ waist="Austerity Belt +1",
+ left_ear="Hirudinea Earring",
+ right_ear="Loquac. Earring",
+ left_ring="Evanescence Ring",
+ right_ring="Archon Ring",
+ back={ name="Nantosuelta's Cape", augments={'INT+20','Mag. Acc+20 /Mag. Dmg.+20','"Mag.Atk.Bns."+10',}},
+ }
+
+ sets.midcast['Enfeebling Magic'] = {
+ main="Idris",
+ sub="Ammurapi Shield",
+ range=empty,
+ ammo="Pemphredo Tathlum",
+ head="Amalric Coif +1",
+ neck="Erra Pendant",
+ ear1="Barkarole Earring",
+ ear2="Dignitary's Earring",
+ body="Zendik Robe",
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',}},
+ ring1="Kishar Ring",
+ ring2="Weather. Ring +1",
+ back={ name="Nantosuelta's Cape", augments={'INT+20','Mag. Acc+20 /Mag. Dmg.+20','"Mag.Atk.Bns."+10',}},
+ waist="Luminary Sash",
+ legs="Psycloth Lappas",
+ feet="Amalric Nails +1",
+ }
+
+ sets.midcast.EnhancingDuration = {
+ main={ name="Gada", augments={'Enh. Mag. eff. dur. +6','"Mag.Atk.Bns."+9',}},
+ sub="Ammurapi Shield",
+ hands={ name="Telchine Gloves", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',}},
+ head={ name="Telchine Cap", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',}},
+ body={ name="Telchine Chas.", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',}},
+ legs={ name="Telchine Braconi", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',}},
+ feet={ name="Telchine Pigaches", augments={'Song spellcasting time -7%','Enh. Mag. eff. dur. +10',}},
+ }
+
+ sets.midcast['Flash Nova'] = {
+ range=empty,
+ ammo="Mana Ampulla",
+ head={ name="Merlinic Hood", augments={'VIT+8','"Mag.Atk.Bns."+27','Accuracy+5 Attack+5','Mag. Acc.+18 "Mag.Atk.Bns."+18',}},
+ body={ name="Merlinic Jubbah", augments={'"Mag.Atk.Bns."+29','Magic burst dmg.+11%','INT+10',}},
+ hands="Amalric Gages +1",
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+30','"Occult Acumen"+10','INT+9','Mag. Acc.+5',}},
+ feet="Amalric Nails +1",
+ neck="Baetyl Pendant",
+ waist="Fotia Belt",
+ left_ear="Friomisi Earring",
+ right_ear="Crematio Earring",
+ left_ring="Shiva Ring +1",
+ right_ring="Weather. Ring +1",
+ back={ name="Nantosuelta's Cape", augments={'INT+20','Mag. Acc+20 /Mag. Dmg.+20','"Mag.Atk.Bns."+10',}},
+ }
+
+ sets.midcast.Exudation = {range=empty,ammo="Mana Ampulla",
+ head="Geomancy Galero +1",neck="Phalaina Locket",
+ body="Onca Suit",hands=empty,lring="Rajas Ring",rring="Ifrit Ring +1",
+ back="Pahtli Cape",waist="Luminary Sash",legs=empty,feet=empty,
+ }
+
+ sets.midcast['Spirit Taker'] = sets.midcast.Exudation
+
+ sets.midcast['Healing Magic'] = {neck="Incanter's Torque"}
+
+ sets.midcast['Divine Magic'] = {neck="Incanter's Torque"}
+
+ sets.midcast['Enhancing Magic'] = set_combine(sets.midcast.EnhancingDuration,{neck="Incanter's Torque"})
+
+ sets.midcast.Dia = sets.TH
+ sets.midcast['Dia II'] = sets.TH
+ sets.midcast.Diaga = sets.TH
+
+ sets.midcast.Cure = {
+ main="Vadose Rod",
+ sub="Genmei Shield",
+ range=empty,
+ ammo="Mana Ampulla",
+ head="Amalric Coif +1",
+ neck="Phalaina Locket",
+ lear="Novia Earring",
+ rear="Mendicant's Earring",
+ body="Heka's Kalasiris",
+ hands="Revealer's Mitts +1",
+ lring="Celestial Ring",
+ rring="Sangoma Ring",
+ back="Pahtli Cape",
+ waist="Luminary Sash",
+ legs="Vanya Slops",
+ feet="Amalric Nails +1"
+ }
+
+ sets.midcast.Stoneskin = set_combine(sets.midcast.EnhancingDuration,{neck="Nodens Gorget",waist="Siegel Sash",legs="Shedir Seraweels"})
+
+ sets.midcast.Aquaveil = set_combine(sets.midcast.EnhancingDuration,{main="Vadose Rod",head="Amalric Coif +1",sub="Genmei Shield",waist="Emphatikos Rope",legs="Shedir Seraweels"})
+
+ sets.midcast.Refresh = set_combine(sets.midcast.EnhancingDuration,{head="Amalric Coif +1",back="Grapevine Cape",feet="Inspirited Boots"})
+
+ sets.midcast.Cursna = {
+ main={ name="Divinity", augments={'Attack+10','Accuracy+10','Phys. dmg. taken -3%','DMG:+15',}},
+ sub="Genmei Shield",
+ head={ name="Vanya Hood", augments={'Healing magic skill +20','"Cure" spellcasting time -7%','Magic dmg. taken -3',}},
+ body="Vanya Robe",
+ hands="Hieros Mittens",
+ legs={ name="Vanya Slops", augments={'Healing magic skill +20','"Cure" spellcasting time -7%','Magic dmg. taken -3',}},
+ left_ring="Haoma's Ring",
+ right_ring="Haoma's Ring",
+ back="Oretan. Cape +1",
+ feet="Vanya Clogs",
+ neck="Debilis Medallion",
+ }
+
+ sets.midcast.Bolster = {body="Bagua Tunic +1"}
+
+ sets.midcast['Full Circle'] = {head="Azimuth Hood +1",hands="Bagua Mitaines +1"}
+
+ sets.midcast['Life Cycle'] = {body="Geomancy Tunic +1",
+ back={ name="Nantosuelta's Cape", augments={'HP+60','Eva.+20 /Mag. Eva.+20','Pet: "Regen"+10',}},}
+
+ sets.midcast['Radial Arcana'] = {hands="Bagua Sandals +1"}
+
+ sets.midcast['Mending Halation'] = {Legs="Bagua Pants +1"}
+
+ sets.midcast.Indi = {
+ main="Idris",
+ sub="Genmei Shield",
+ range="Dunna",
+ ammo=empty,
+ head="Azimuth Hood +1",
+ body={ name="Bagua Tunic +1", augments={'Enhances "Bolster" effect',}},
+ legs={ name="Bagua Pants +1", augments={'Enhances "Mending Halation" effect',}},
+ feet="Azimuth Gaiters +1",
+ neck="Incanter's Torque",
+ back={ name="Lifestream Cape", augments={'Geomancy Skill +3','Indi. eff. dur. +20','Damage taken-3%',}},
+ }
+
+ sets.midcast.Geo = {
+ main="Idris",
+ sub="Genmei Shield",
+ range="Dunna",
+ ammo=empty,
+ head="Azimuth Hood +1",
+ body={ name="Bagua Tunic +1", augments={'Enhances "Bolster" effect',}},
+ legs={ name="Bagua Pants +1", augments={'Enhances "Mending Halation" effect',}},
+ feet="Azimuth Gaiters +1",
+ neck="Incanter's Torque",
+ back={ name="Lifestream Cape", augments={'Geomancy Skill +3','Indi. eff. dur. +20','Damage taken-3%',}},
+ }
+ sets.midcast.Geo = set_combine(sets.midcast.Geo,sets.TH)
+
+ sets.midcast['Entrusted Indi'] = set_combine(sets.midcast.Indi,{main="Solstice"})
+
+
+
+ sets.aftercast = {}
+ sets.aftercast.Idle = {}
+ sets.aftercast.Idle[false] = {
+ main="Mafic Cudgel",
+ sub="Genmei Shield",
+ range="Dunna",
+ ammo=empty,
+ head={ name="Merlinic Hood", augments={'"Mag.Atk.Bns."+28','"Fast Cast"+3','"Refresh"+1','Mag. Acc.+6 "Mag.Atk.Bns."+6',}},
+ neck="Loricate Torque +1",
+ ear1="Gifted earring",
+ ear2="Etiolation Earring",
+ body="Witching Robe",
+ hands="Bagua Mitaines +1",
+ ring1="Dark Ring",
+ ring2="Defending Ring",
+ back="Umbra Cape",
+ waist="Yamabuki-no-Obi",
+ legs="Assid. Pants +1",
+ feet="Geomancy Sandals +2"}
+
+ sets.aftercast.Idle[true] = {
+ main="Idris",
+ sub="Genmei Shield",
+ range={ name="Dunna", augments={'MP+20','Mag. Acc.+10','"Fast Cast"+3',}},
+ head="Azimuth Hood +1",
+ body={ name="Telchine Chas.", augments={'Mag. Evasion+21','Pet: "Regen"+3','Pet: Damage taken -3%',}},
+ hands={ name="Telchine Gloves", augments={'Mag. Evasion+25','Pet: "Regen"+3','Pet: Damage taken -3%',}},
+ legs={ name="Telchine Braconi", augments={'Mag. Evasion+25','Pet: "Regen"+3','Pet: Damage taken -3%',}},
+ feet={ name="Bagua Sandals +1", augments={'Enhances "Radial Arcana" effect',}},
+ neck="Loricate Torque +1",
+ waist="Isa belt",
+ left_ear="Genmei Earring",
+ right_ear="Etiolation Earring",
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ right_ring="Defending Ring",
+ back={ name="Nantosuelta's Cape", augments={'HP+60','Eva.+20 /Mag. Eva.+20','Pet: "Regen"+10',}},
+ }
+
+ sets.aftercast.Resting = {main="Numen Staff",sub="Ariesian Grip",range="Dunna",ammo=empty,
+ head={ name="Merlinic Hood", augments={'"Mag.Atk.Bns."+28','"Fast Cast"+3','"Refresh"+1','Mag. Acc.+6 "Mag.Atk.Bns."+6',}},neck="Eidolon Pendant +1",ear1="Relaxing Earring",ear2="Antivenom Earring",
+ body="Witching Robe",hands="Nares Cuffs",ring1="Celestial Ring",ring2="Angha Ring",
+ back="Felicitas Cape +1",waist="Austerity Belt +1",legs="Assid. Pants +1",feet="Chelona Boots +1"}
+
+ sets.aftercast.Engaged = {main="Mafic Cudgel",sub="Genmei Shield",
+ head={name="Hagondes Hat +1", augments={'Phys. dmg. taken -3%','Magic dmg. taken -2%','"Mag.Atk.Bns."+26',}},neck="Loricate Torque +1",ear1="Brutal Earring",ear2="Genmei Earring",
+ body="Onca Suit",hands=empty,ring1="Dark Ring",ring2="Defending Ring",
+ back="Umbra Cape",waist="Ninurta's Sash",legs=empty,feet=empty}
+ --body="Hagondes Coat +1",hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',}},ring1="Dark Ring",ring2="Defending Ring",
+ --back="Umbra Cape",waist="Ninurta's Sash",legs={ name="Hagondes Pants +1", augments={'Phys. dmg. taken -4%','Magic dmg. taken -4%','Magic burst dmg.+10%',}},feet="Battlecast Gaiters"}
+
+ sets.Obis = {}
+ sets.Obis.Fire = {waist='Hachirin-no-Obi'}
+ sets.Obis.Earth = {waist='Hachirin-no-Obi'}
+ sets.Obis.Water = {waist='Hachirin-no-Obi'}
+ sets.Obis.Wind = {waist='Hachirin-no-Obi'}
+ sets.Obis.Ice = {waist='Hachirin-no-Obi'}
+ sets.Obis.Lightning = {waist='Hachirin-no-Obi'}
+ sets.Obis.Light = {waist='Hachirin-no-Obi'}
+ sets.Obis.Dark = {waist='Hachirin-no-Obi'}
+ sets.Zodiac = {lring="Zodiac Ring"}
+
+ sets.midcast.MB={
+ head={ name="Merlinic Hood", augments={'Mag. Acc.+24 "Mag.Atk.Bns."+24','Magic burst dmg.+11%','INT+1','Mag. Acc.+8',}},
+ body={ name="Merlinic Jubbah", augments={'"Mag.Atk.Bns."+29','Magic burst dmg.+11%','INT+10',}},
+ legs={ name="Merlinic Shalwar", augments={'"Mag.Atk.Bns."+26','Magic burst dmg.+11%','INT+10','Mag. Acc.+13',}},
+ neck="Mizu. Kubikazari",
+ right_ring="Mujin Band",
+ }
+
+ stuntarg = 'Shantotto'
+ send_command('input /macro book 2;wait .1;input /macro set 1')
+
+ AMII = {['Freeze II']=true,['Burst II']=true,['Quake II'] = true, ['Tornado II'] = true,['Flood II']=true,['Flare II']=true}
+end
+
+function precast(spell)
+ if sets.precast[spell.english] then
+ equip(sets.precast[spell.english][macc_level] or sets.precast[spell.english])
+ elseif spell.english == 'Impact' then
+ equip(sets.precast.FastCast.Default,sets.Impact)
+ if not buffactive['Elemental Seal'] then
+ add_to_chat(8,'--------- Elemental Seal is down ---------')
+ end
+ elseif spell.action_type == 'Magic' then
+ equip(sets.precast.FastCast.Default)
+ end
+
+ if spell.english == 'Stun' and stuntarg ~= 'Shantotto' then
+ send_command('@input /t '..stuntarg..' ---- Byrth Stunned!!! ---- ')
+ end
+end
+
+function midcast(spell)
+ equip_idle_set()
+ if buffactive.manawell or spell.mppaftercast > 50 then mp_efficiency = 0
+ else mp_efficiency = 1 end
+
+ if spell and spell.english and string.find(spell.english,'Cur') then
+ weathercheck(spell.element,sets.midcast.Cure)
+ elseif spell.english == 'Impact' then
+ weathercheck(spell.element,set_combine(sets.midcast['Elemental Magic'][macc_level][mp_efficiency],sets.Impact))
+ elseif spell.english:sub(1,4) == 'Geo-' then
+ equip(sets.midcast.Geo)
+ elseif spell.english:sub(1,5) == 'Indi-' then
+ if buffactive.Entrust then
+ equip(sets.midcast["Entrusted Indi"])
+ else
+ equip(sets.midcast.Indi)
+ end
+ elseif sets.midcast[spell.name] then
+ weathercheck(spell.element,sets.midcast[spell.name])
+ elseif spell.skill == 'Elemental Magic' then
+ weathercheck(spell.element,sets.midcast['Elemental Magic'][macc_level][mp_efficiency])
+ if sets.ElementalMagicMAB[spell.element] then
+ equip(sets.ElementalMagicMAB[spell.element])
+ end
+ elseif spell.skill then
+ equip(sets.aftercast.Idle)
+ weathercheck(spell.element,sets.midcast[spell.skill])
+ end
+
+ if spell.english == 'Sneak' then
+ send_command('cancel 71;')
+ end
+end
+
+function aftercast(spell)
+ if midaction() or (spell and (spell.name == 'Mending Halation' or spell.name == 'Radial Arcana')) then
+ elseif player.status == 'Idle' then
+
+ if not spell.english then print('NO SPELL IN AFTERCAST') return end
+ if spell and spell.english and player.status == 'Idle' and string.find(spell.english,'Geo') then
+ equip(sets.aftercast.Idle[true])
+ else
+ equip_idle_set()
+ end
+ elseif sets.aftercast[player.status] then
+ equip(sets.aftercast[player.status])
+ else
+ equip_idle_set()
+ end
+ if not spell.interrupted then
+ if spell.english == 'Sleep' or spell.english == 'Sleepga' then
+ send_command('@wait 55;input /echo ------- '..spell.english..' is wearing off in 5 seconds -------')
+ elseif spell.english == 'Sleep II' or spell.english == 'Sleepga II' then
+ send_command('@wait 85;input /echo ------- '..spell.english..' is wearing off in 5 seconds -------')
+ elseif spell.english == 'Break' or spell.english == 'Breakga' then
+ send_command('@wait 25;input /echo ------- '..spell.english..' is wearing off in 5 seconds -------')
+ end
+ end
+end
+
+function pet_aftercast(spell)
+ if (spell and (spell.name == 'Mending Halation' or spell.name == 'Radial Arcana')) then
+ equip_idle_set()
+ end
+end
+
+function status_change(new,old)
+ if new == 'Resting' then
+ equip(sets.aftercast.Resting)
+ elseif new == 'Engaged' then
+ if not midaction() then
+ equip(sets.aftercast.Engaged)
+ end
+ -- disable('main','sub')
+ else
+ equip_idle_set()
+ end
+end
+
+function equip_idle_set(bool)
+ if bool == nil then
+ bool = pet.isvalid
+ end
+ equip(sets.aftercast.Idle[bool])
+end
+
+function weathercheck(spell_element,set)
+ if not set then return end
+ if spell_element == world.weather_element or spell_element == world.day_element then
+ equip(set,sets.Obis[spell_element])
+ else
+ equip(set)
+ end
+ if set[spell_element] then equip(set[spell_element]) end
+end
+
+function indi_change(spell,bool)
+ if not bool then windower.add_to_chat(8,"------------------- Indi effect wears off! -------------------------") end
+end
+
+function buff_change(name,gain,tab)
+ if gain then
+ print('gain',name,buffactive[name],math.floor(tab.duration/60),math.floor(tab.duration%60))
+ else
+ print('loss',name,buffactive[name],math.floor(tab.duration/60),math.floor(tab.duration%60))
+ end
+end
+
+function self_command(command)
+ if command:lower() == 'stuntarg' then
+ stuntarg = player.target.name
+ print('StunTarg: ',stuntarg)
+ elseif command:lower() == 'macc' then
+ macc_level = (macc_level+1)%2
+ print('Magic Accuracy level ',macc_level)
+ end
+end
+
+function pet_change(pet,gain)
+ if not midaction() then
+ equip_idle_set(gain)
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_MNK.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_MNK.lua
new file mode 100644
index 0000000..16f12a2
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_MNK.lua
@@ -0,0 +1,160 @@
+include('organizer-lib')
+
+function get_sets()
+ sets.weapons = {main="Suwaiyas"}
+ sets.precast = {}
+ --sets.precast.Boost = {hands="Anchorite's Gloves +1"}
+ sets.precast.Chakra = {ammo="Iron Gobbet",body="Anchorite's Cyclas +1",hands="Hes. Gloves +1"}
+ sets.precast.Counterstance = {feet="Hes. Gaiters +1"}
+ sets.precast.Focus = {head="Anchorite's Crown +1"}
+ sets.precast.Dodge = {feet="Anchorite's Gaiters +1"}
+ sets.precast.Mantra = {feet="Hes. Gaiters +1"}
+ sets.precast.Footwork = {feet="Shukuyu Sune-Ate"}
+ sets.precast['Hundred Fists'] = {legs="Hes. Hose +1"}
+ sets.Waltz = {head="Anwig Salade",neck="Unmoving Collar +1",ring1="Valseur's Ring",ring2="Carbuncle Ring +1",
+ waist="Aristo Belt",legs="Desultor Tassets",feet="Dance Shoes"}
+
+ sets.precast['Victory Smite'] = {
+ ammo="Floestone",
+ head={ name="Adhemar Bonnet +1", augments={'STR+12','DEX+12','Attack+20',}},
+ body={ name="Herculean Vest", augments={'Accuracy+21','Crit. hit damage +5%','DEX+9',}},
+ hands={ name="Adhemar Wrist. +1", augments={'DEX+12','AGI+12','Accuracy+20',}},
+ legs="Hiza. Hizayoroi +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+29','Crit. hit damage +5%','DEX+7','Attack+13',}},
+ neck="Fotia Gorget",
+ waist="Moonbow Belt +1",
+ left_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ right_ear="Sherida Earring",
+ left_ring="Begrudging Ring",
+ left_ring="Niqmaddu Ring",
+ back={ name="Segomo's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','Weapon skill damage +10%',}},
+ }
+
+ sets.test = {lring="Ramuh Ring +1",rring="Ramuh Ring +1"}
+ sets.test2 = {main="Numen Staff"}
+
+ sets.precast['Howling Fist'] = {
+ ammo="Floestone",
+ head={ name="Adhemar Bonnet +1", augments={'STR+12','DEX+12','Attack+20',}},
+ body="Adhemar Jacket +1",
+ hands="Adhemar Wrist. +1",
+ legs="Hiza. Hizayoroi +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Caro Necklace",
+ waist="Moonbow Belt +1",
+ left_ear="Cessance Earring",
+ right_ear="Sherida Earring",
+ left_ring="Ifrit Ring +1",
+ left_ring="Niqmaddu Ring",
+ back={ name="Segomo's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','Weapon skill damage +10%',}},
+ }
+ sets.precast['Tornado Kick'] = {
+ ammo="Floestone",
+ head={ name="Adhemar Bonnet +1", augments={'STR+12','DEX+12','Attack+20',}},
+ body="Adhemar Jacket +1",
+ hands="Adhemar Wrist. +1",
+ legs="Hiza. Hizayoroi +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Caro Necklace",
+ waist="Moonbow Belt +1",
+ left_ear="Moonshade Earring",
+ right_ear="Sherida Earring",
+ left_ring="Ifrit Ring +1",
+ left_ring="Niqmaddu Ring",
+ back={ name="Segomo's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','Weapon skill damage +10%',}},
+ }
+ sets.precast['Spinning Attack'] = {
+ ammo="Floestone",
+ head={ name="Adhemar Bonnet +1", augments={'STR+12','DEX+12','Attack+20',}},
+ body="Adhemar Jacket +1",
+ hands="Adhemar Wrist. +1",
+ legs="Hiza. Hizayoroi +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Caro Necklace",
+ waist="Moonbow Belt +1",
+ left_ear="Cessance Earring",
+ right_ear="Sherida Earring",
+ left_ring="Ifrit Ring +1",
+ left_ring="Niqmaddu Ring",
+ back={ name="Segomo's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','Weapon skill damage +10%',}},
+ }
+
+ sets.WS = {
+ ammo="Floestone",
+ head={ name="Adhemar Bonnet +1", augments={'STR+12','DEX+12','Attack+20',}},
+ body={ name="Herculean Vest", augments={'Accuracy+21','Crit. hit damage +5%','DEX+9',}},
+ hands="Adhemar Wrist. +1",
+ legs="Hiza. Hizayoroi +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+29','Crit. hit damage +5%','DEX+7','Attack+13',}},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Moonshade Earring",
+ right_ear="Sherida Earring",
+ left_ring="Begrudging Ring",
+ left_ring="Niqmaddu Ring",
+ back={ name="Segomo's Mantle", augments={'STR+20','Accuracy+20 Attack+20','STR+10','Weapon skill damage +10%',}},
+ }
+
+ sets.TP = {}
+
+ sets.TP.DD = {
+ ammo="Ginsen",
+ head={ name="Adhemar Bonnet +1", augments={'STR+12','DEX+12','Attack+20',}},
+ body="Adhemar Jacket +1",
+ hands="Adhemar Wrist. +1",
+ legs={ name="Samnuha Tights", augments={'STR+8','DEX+9','"Dbl.Atk."+3','"Triple Atk."+2',}},
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Combatant's Torque",
+ waist="Moonbow Belt +1",
+ left_ear="Cessance Earring",
+ right_ear="Sherida Earring",
+ left_ring="Niqmaddu Ring",
+ right_ring="Epona's Ring",
+ back={ name="Segomo's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Dbl.Atk."+10',}},
+ }
+
+ sets.status = {}
+ sets.status.Engaged = sets.TP.DD
+
+ sets.status.Idle = {
+ ammo="Iron Gobbet",
+ head="Lithelimb Cap",
+ body="Emet Harness +1",
+ hands={ name="Herculean Gloves", augments={'Accuracy+30','Damage taken-4%','STR+9','Attack+4',}},
+ legs="Mummu Kecks +1",
+ feet="Herald's Gaiters",
+ neck="Wiglen Gorget",
+ waist="Moonbow Belt +1",
+ left_ear="Novia Earring",
+ right_ear="Genmei Earring",
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ back={ name="Segomo's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Dbl.Atk."+10',}},
+ }
+ send_command('input /macro book 15;wait .1;input /macro set 1')
+end
+
+function precast(spell)
+ if spell.english == 'Tornado Kick' and buffactive.Footwork then
+ equip(sets.precast[spell.english])
+ equip({feet="Shukuyu Sune-Ate"})
+ elseif sets.precast[spell.english] then
+ equip(sets.precast[spell.english])
+ elseif spell.type=="WeaponSkill" then
+ equip(sets.WS)
+ elseif string.find(spell.english,'Waltz') then
+ equip(sets.Waltz)
+ end
+end
+
+function aftercast(spell)
+ if sets.status[player.status] then
+ equip(sets.status[player.status])
+ end
+end
+
+function status_change(new,old)
+ if sets.status[new] then
+ equip(sets.status[new])
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_PLD.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_PLD.lua
new file mode 100644
index 0000000..5ee699a
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_PLD.lua
@@ -0,0 +1,539 @@
+include('organizer-lib')
+
+function get_sets()
+
+
+ sets.weapons = {sub="Burtgang",main="Xiutleato",ammo="Excalibur"}
+
+ sets.aegis = {sub={name="Aegis"}}
+ sets.ochain = {sub={name="Ochain"}}
+ sets.priwen = {sub={name="Priwen",hp=80}}
+ sets.srivatsa = {sub={name="Srivatsa",hp=150,mp=150}}
+ current_shield = {name="Ochain"}
+
+ sets.items = {sub="Echo Drops"}
+
+ sets.Enmity = {
+ main="Burtgang",
+ sub={name="Srivatsa",hp=150,mp=150},
+ ammo="Iron Gobbet",
+ head={ name="Cab. Coronet +1", augments={'Enhances "Iron Will" effect',}, hp=96, mp=78},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={ name="Yorium Gauntlets", augments={'Mag. Evasion+8','Enmity+10','Phys. dmg. taken -4%',}, hp=29},
+ legs={ name="Odyssean Cuisses", augments={'Attack+9','Enmity+8',}, hp=54, mp=41},
+ feet={ name="Eschite Greaves", augments={'HP+80','Enmity+7','Phys. dmg. taken -4',}, hp=98},
+ neck="Unmoving Collar +1",
+ waist="Goading Belt",
+ left_ear="Trux Earring",
+ right_ear={name="Pluto's Pearl",mp=24},
+ ring1={name="Eihwaz Ring",hp=70},
+ right_ring="Provocare Ring",
+ back={ name="Weard Mantle", augments={'VIT+1','DEX+1','Enmity+7','Phalanx +4',}, hp=40, mp=40},
+ }
+
+ sets.precast = {}
+ sets.precast.FC = {
+ sub={ name="Svalinn", augments={'"Mag.Atk.Bns."+10','Occ. quickens spellcasting +3%',}},
+ ammo="Impatiens",
+ head={name="Carmine Mask +1",hp=38},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={name="Leyline Gloves",hp=25},
+ legs={name="Enif Cosciales", hp=40,mp=40},
+ feet={name="Carmine Greaves +1", hp=95, mp=80},
+ neck={name="Orunmila's Torque", mp=30},
+ left_ear={name="Loquac. Earring", mp=30},
+ right_ear={name="Etiolation Earring", hp=50, mp=50},
+ left_ring="Kishar Ring",
+ right_ring="Weather. Ring +1",
+ back={name="Reiki Cloak",hp=130},
+ waist={name="Eschan Stone",hp=20},
+ }
+
+ sets.precast.Cure = {
+ body={ name="Jumalik Mail", augments={'HP+50','Attack+15','Enmity+9','"Refresh"+2',}, hp=116, mp=59},
+ right_ear="Nourish. Earring +1"}
+
+ sets.precast['Enhancing Magic'] = {waist="Siegel Sash"}
+
+ sets.midcast = {}
+ sets.midcast['Shield Bash'] = set_combine(sets.Enmity,sets.aegis,{hands={name="Caballarius Gauntlets +1",hp=104},rring="Fenian Ring"})
+ sets.midcast.Chivalry = {hands={name="Caballarius Gauntlets +1",hp=104}} -- Should make a real Chivalry set
+ sets.midcast.Sentinel = {feet={name="Cab. Leggings +1",hp=43,mp=23},}
+ sets.midcast.Rampart = set_combine(sets.Enmity,{head={ name="Cab. Coronet +1", augments={'Enhances "Iron Will" effect',}, hp=96, mp=78}})
+ sets.midcast.Invincible = set_combine(sets.Enmity,{legs={name="Cab. Breeches +1",hp=52,mp=80}})
+ sets.midcast.Fealty = {
+ body={ name="Cab. Surcoat +1", augments={'Enhances "Fealty" effect',}, hp=118, mp=90},
+ }
+ sets.midcast['Holy Circle'] = {feet={name="Reverence Leggings +1",hp=48,mp=30}}
+ sets.midcast.Provoke = sets.Enmity
+ sets.midcast.Flash = set_combine(sets.Enmity,{waist="Chaac Belt"})
+ sets.midcast.Palisade = sets.Enmity
+ sets.midcast['Stinking Gas'] = sets.Enmity
+ sets.midcast['Geist Wall'] = sets.Enmity
+ sets.midcast['Jettatura'] = sets.Enmity
+ sets.midcast['Soporific'] = sets.Enmity
+ sets.midcast['Blank Gaze'] = sets.Enmity
+ sets.midcast['Sounds Blast'] = sets.Enmity
+ sets.midcast['Sheep Song'] = sets.Enmity
+ sets.midcast['Chaotic Eye'] = sets.Enmity
+ sets['Divine Emblem'] = {feet={name="Chev. Sabatons +1",hp=22,mp=14}}
+
+ sets.midcast.Cover={
+ sub="Ochain",
+ ammo="Iron Gobbet",
+ head={name="Reverence Coronet +1", hp=41, mp=23},
+ body={ name="Cab. Surcoat +1", augments={'Enhances "Fealty" effect',}, hp=118, mp=90},
+ hands={name="Caballarius Gauntlets +1",hp=104},
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}, hp=95},
+ feet={name="Souveran Schuhs +1",hp=227,mp=14},
+ neck="Unmoving Collar +1",
+ waist="Flume Belt +1",
+ left_ear="Genmei Earring",
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}, hp=-20,mp=20},
+ back={ name="Weard Mantle", augments={'VIT+3','DEX+5','Phalanx +5',}, hp=40, mp=40},
+ }
+
+ sets.midcast['Chant du Cygne'] = {
+ ammo="Ginsen",
+ head={name="Flam. Zucchetto +1",hp=80, mp=20},
+ body={name="Flamma Korazin +1",hp=140, mp=35},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}, hp=95},
+ feet={name="Thereoid Greaves",hp=13},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ right_ear="Brutal Earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ramuh Ring +1",
+ back="Rancorous Mantle",
+ }
+
+ sets.midcast.Atonement = set_combine(sets.Enmity,{neck="Fotia Gorget",
+ ear1="Moonshade Earring",
+ head={ name="Valorous Mask", augments={'Weapon skill damage +5%','Attack+4',}, hp=38},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+20','Weapon skill damage +5%','VIT+3',}, hp=31, mp=14},
+ waist="Fotia Belt",
+ feet={name="Sulevia's Leggings +1",hp=20,mp=20}})
+
+ sets.midcast['Swift Blade'] = {ammo="Ginsen",
+ head={name="Carmine Mask +1",hp=38},
+ neck="Fotia Gorget",
+ ear1="Steelflash Earring",
+ ear2="Bladeborn Earring",
+ body={name="Flamma Korazin +1",hp=140, mp=35},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ ring1="Ifrit Ring +1",
+ ring2="Rajas Ring",
+ back="Bleating Mantle",
+ waist="Fotia Belt",
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}, hp=95},
+ feet={name="Flam. Gambieras +1",hp=40,mp=10},
+ }
+
+ sets.midcast['Savage Blade'] = {
+ ammo="Ginsen",
+ head={name="Carmine Mask +1",hp=38},
+ body={name="Flamma Korazin +1",hp=140, mp=35},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}, hp=95},
+ feet={name="Sulevia's Leggings +1",hp=20,mp=20},
+ neck="Fotia Gorget",
+ waist="Windbuffet Belt +1",
+ left_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ right_ear="Brutal Earring",
+ left_ring="Ifrit Ring +1",
+ right_ring="Rajas Ring",
+ back="Bleating Mantle",
+ }
+
+ sets.midcast['Knights of Round'] = {
+ ammo="Ginsen",
+ head={name="Carmine Mask +1",hp=38},
+ neck="Fotia Gorget",
+ ear1="Steelflash Earring",
+ ear2="Bladeborn Earring",
+ body={name="Flamma Korazin +1",hp=140, mp=35},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ ring1="Ifrit Ring +1",
+ ring2="Rajas Ring",
+ back="Bleating Mantle",
+ waist="Fotia Belt",
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}, hp=95},
+ feet={name="Sulevia's Leggings +1",hp=20,mp=20},
+ }
+
+ sets.midcast.WS = {
+ head={name="Flam. Zucchetto +1",hp=80, mp=20},
+ neck="Fotia Gorget",
+ ear1="Moonshade Earring",
+ ear2="Brutal Earring",
+ body={name="Flamma Korazin +1",hp=140, mp=35},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ ring1="Ramuh Ring +1",
+ ring2="Rajas Ring",
+ back="Bleating Mantle",
+ waist="Windbuffet Belt +1",
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}, hp=95},
+ feet={name="Flam. Gambieras +1",hp=40,mp=10},
+ }
+
+
+ sets.midcast.WS_Day = { head={name="Gavialis helm", hp=115, mp=23} }
+
+ sets.midcast.Cure = {
+ neck="Phalaina Locket",
+ rear="Nourishing Earring +1",
+ --body={ name="Jumalik Mail", augments={'HP+50','Attack+15','Enmity+9','"Refresh"+2',}, hp=116, mp=59},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={name="Souveran Handschuhs +1",hp=239,mp=14},
+ ring2={name="Meridian Ring",hp=90},
+ ring1={name="Eihwaz Ring",hp=70},
+ back={ name="Weard Mantle", augments={'VIT+1','DEX+1','Enmity+7','Phalanx +4',}, hp=40, mp=40},
+ legs={name="Souveran Diechlings +1",hp=162,mp=41},
+ feet={name="Souveran Schuhs +1",hp=227,mp=14},
+ back={name="Reiki Cloak",hp=130},
+ }
+
+ sets.midcast.Phalanx = {main="Deacon Sword", -- +4
+ sub="Priwen", -- +2
+ neck="Incanter's Torque", -- +1
+ head={ name="Yorium Barbuta", augments={'Phalanx +3',}, hp=41, mp=23}, -- +3
+ body={ name="Yorium Cuirass", augments={'Mag. Evasion+7','Enmity+10','Phalanx +3',}, hp=113, mp=85}, -- +3
+ hands={name="Souveran Handschuhs +1",hp=239,mp=14}, -- +5
+ back={ name="Weard Mantle", augments={'VIT+3','DEX+5','Phalanx +5',}, hp=40, mp=40}, -- +5
+ legs={ name="Yorium Cuisses", augments={'Enmity+7','Phalanx +3',}, hp=52}, -- +3
+ feet={name="Souveran Schuhs +1",hp=227,mp=14} -- +5
+ }
+ -- Next tier is about 26 skill away, which isn't worth it.
+
+ sets.midcast.Enlight = {
+ main={ name="Brilliance", augments={'Shield skill +10','Divine magic skill +15','Enmity+7','DMG:+15',}},
+ head={ name="Jumalik Helm", augments={'MND+10','"Mag.Atk.Bns."+15','Magic burst dmg.+10%','"Refresh"+1',}, hp=45, mp=29},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={ name="Eschite Gauntlets", hp=109},
+ waist="Asklepian Belt",
+ neck="Incanter's Torque",}
+
+ sets.midcast['Enlight II'] = sets.midcast.Enlight
+ -- This puts me over 500 skill. Need to test how much more divine magic skill I would need for another tier.
+
+ sets.midcast.Stoneskin = {neck="Nodens Gorget",hands={name="Stone Mufflers",hp=10,mp=10},waist="Siegel Sash"}
+
+ sets.aftercast = {}
+
+ TP_sets = {'DD','Acc','DT'}
+ TP_ind = 3
+
+ sets.aftercast.non_DW = {
+ main="Burtgang",
+ sub=current_shield,
+ waist="Windbuffet Belt +1",
+ left_ear="Steelflash Earring",
+ right_ear="Bladeborn Earring",
+ ammo="Ginsen",
+ head={name="Flam. Zucchetto +1",hp=80, mp=20},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}, hp=95},
+ feet={name="Flam. Gambieras +1",hp=40,mp=10},
+ neck="Lissome Necklace",
+ left_ring="Ifrit Ring +1",
+ right_ring="Rajas Ring",
+ back="Bleating Mantle",
+ }
+
+ sets.aftercast.Acc = {
+ main="Burtgang",
+ sub=current_shield,
+ ammo="Staunch Tathlum",
+ head={name="Carmine Mask +1",hp=38},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ legs={ name="Carmine Cuisses +1", augments={'Accuracy+20','Attack+12','"Dual Wield"+6',}, hp=50},
+ feet={name="Souveran Schuhs +1",hp=227,mp=14},
+ neck="Loricate Torque +1",
+ waist="Flume Belt +1",
+ left_ear="Genmei Earring",
+ right_ear={name="Thureous Earring",hp=30,mp=30},
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}, hp=-20,mp=20},
+ back="Impassive Mantle",
+ }
+
+ sets.aftercast.DW = {
+ main="Burtgang",
+ ammo="Ginsen",
+ head={name="Flam. Zucchetto +1",hp=80, mp=20},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}, hp=31, mp=14},
+ legs={ name="Carmine Cuisses +1", augments={'Accuracy+20','Attack+12','"Dual Wield"+6',}, hp=50},
+ feet={name="Flam. Gambieras +1",hp=40,mp=10},
+ neck="Lissome Necklace",
+ waist="Reiki Yotai",
+ left_ear="Suppanomimi",
+ right_ear="Brutal Earring",
+ left_ring="Ifrit Ring +1",
+ right_ring="Rajas Ring",
+ back="Bleating Mantle",
+ }
+
+ sets.aftercast.DD = sets.aftercast.non_DW
+
+ function sets.aftercast.wield(equip_sub)
+ if equip_sub == 'Aegis' or equip_sub =='Ochain' or equip_sub == 'Priwen' then
+ sets.aftercast.DD = sets.aftercast.non_DW
+-- elseif equip_sub == 'Bloodrain Strap' then
+-- sets.aftercast.DD = sets.aftercast.Ragnarok
+ else
+ sets.aftercast.DD = sets.aftercast.DW
+ end
+ end
+
+ sets.aftercast.DT = {
+ main="Burtgang",
+ sub=current_shield,
+ ammo="Staunch Tathlum",
+ head={ name="Odyssean Helm", augments={'Accuracy+16','Phys. dmg. taken -5%','CHR+1','Attack+14',}, hp=112, mp=89},
+ body={ name="Reverence Surcoat +2", hp=244, mp=52},
+ hands={name="Souveran Handschuhs +1",hp=239,mp=14},
+ legs={name="Souveran Diechlings +1",hp=162,mp=41},
+ feet={name="Souveran Schuhs +1",hp=227,mp=14},
+ neck="Loricate Torque +1",
+ waist="Flume Belt +1",
+ left_ear="Genmei Earring",
+ right_ear={name="Thureous Earring",hp=30,mp=30},
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}, hp=-20,mp=20},
+ back="Impassive Mantle",
+ }
+
+ Idle_sets = {'Idle','Supertanking'}
+ Idle_ind = 1
+ sets.aftercast.Idle = {
+ main="Burtgang",
+ sub=current_shield,
+ ammo="Staunch Tathlum",
+ head={ name="Odyssean Helm", augments={'Accuracy+16','Phys. dmg. taken -5%','CHR+1','Attack+14',}, hp=112, mp=89},
+ body={ name="Jumalik Mail", augments={'HP+50','Attack+15','Enmity+9','"Refresh"+2',}, hp=116, mp=59},
+ hands={name="Souveran Handschuhs +1",hp=239,mp=14},
+ legs={ name="Carmine Cuisses +1", augments={'Accuracy+20','Attack+12','"Dual Wield"+6',}, hp=50},
+ feet={name="Souveran Schuhs +1",hp=227,mp=14},
+ neck="Loricate Torque +1",
+ waist="Flume Belt +1",
+ left_ear="Genmei Earring",
+ right_ear={name="Thureous Earring",hp=30,mp=30},
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}, hp=-20,mp=20},
+ back="Impassive Mantle",
+ }
+
+ sets.aftercast.Supertanking = {
+ main="Burtgang",
+ sub=current_shield,
+ ammo="Staunch Tathlum",
+ head={ name="Odyssean Helm", augments={'Accuracy+16','Phys. dmg. taken -5%','CHR+1','Attack+14',}, hp=112, mp=89},
+ body={ name="Jumalik Mail", augments={'HP+50','Attack+15','Enmity+9','"Refresh"+2',}, hp=116, mp=59},
+ hands={name="Souveran Handschuhs +1",hp=239,mp=14},
+ legs={name="Souveran Diechlings +1",hp=162,mp=41},
+ feet={name="Souveran Schuhs +1",hp=227,mp=14},
+ neck="Loricate Torque +1",
+ waist="Flume Belt +1",
+ left_ear="Genmei Earring",
+ right_ear={name="Thureous Earring",hp=30,mp=30},
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}, hp=-20,mp=20},
+ back="Impassive Mantle",
+ }
+
+
+ sets.pretarget = {}
+ sets.pretarget.HP_Down = {
+ head=empty,
+ body=empty,
+ hands={ name="Yorium Gauntlets", augments={'Mag. Evasion+8','Enmity+10','Phys. dmg. taken -4%',}, hp=29},
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}, hp=-20,mp=20},
+ lear=empty,
+ rear=empty,
+ lring="Defending Ring",
+ back=empty,
+ waist="Flume Belt +1",
+ legs=empty,
+ feet=empty,
+ neck=empty,}
+
+ Cure_force = false
+ send_command('input /macro book 20;wait .1;input /macro set 1')
+ disable('main','sub')
+ lock_mode = false
+end
+
+function pretarget(spell)
+ if (spell.name == 'Cure IV' or spell.name == 'Cure III') and player.max_hp - player.hp < 328 and spell.target and spell.target.name == player.name then
+ print('equipping HP Down')
+ equip(sets.pretarget.HP_Down)
+ end
+end
+
+function precast(spell)
+ sets.aftercast.wield(player.equipment.sub)
+ if player.equipment.head == 'Twilight Helm' and player.equipment.body == 'Twilight Mail' then disable('head','body') end
+ if spell.action_type == 'Magic' then
+ equip(sets.precast.FC)
+ if spell.name:sub(1,3) == "Cur" and spell.name ~= "Cursna" then
+ equip(sets.precast.Cure)
+ elseif spell.skill == 'Enhancing Magic' then
+ equip(sets.precast['Enhancing Magic'])
+ end
+ end
+ set_priorities('hp','mp')
+end
+
+function midcast(spell)
+ midaction(false)
+ if player.status =='Engaged' then
+ equip(sets.aftercast[TP_sets[TP_ind]])
+ else
+ equip(sets.aftercast[Idle_sets[Idle_ind]])
+ end
+
+ if sets.midcast[spell.name] then
+ equip(sets.midcast[spell.name])
+ day_equip(spell)
+ elseif spell.type == 'WeaponSkill' then
+ equip(sets.precast.WS)
+ day_equip(spell)
+ elseif string.find(spell.name,'Cure') then
+ equip(sets.Enmity,sets.midcast.Cure)
+ end
+
+ if spell.english == 'Flash' and buffactive['Divine Emblem'] then
+ equip(sets['Divine Emblem'])
+ end
+ if lockall then
+ aftercast(spell)
+ end
+ set_priorities('hp','mp')
+end
+
+function day_equip(spell)
+ if not spell.skillchain_a or spell.skillchain_a == '' then return end
+ if (check_sc_properties(spell, "Light") or check_sc_properties(spell, "Fusion") or check_sc_properties(spell, "Liquefaction")) and world.day_element == 'Fire' then
+ elseif (check_sc_properties(spell, "Light") or check_sc_properties(spell, "Fusion") or check_sc_properties(spell, "Transfixion")) and world.day_element == 'Light' then
+ elseif (check_sc_properties(spell, "Light") or check_sc_properties(spell, "Fragmentation") or check_sc_properties(spell, "Detonation")) and world.day_element == 'Wind' then
+ elseif (check_sc_properties(spell, "Light") or check_sc_properties(spell, "Fragmentation") or check_sc_properties(spell, "Impaction")) and world.day_element == 'Lightning' then
+ elseif (check_sc_properties(spell, "Dark") or check_sc_properties(spell, "Distortion") or check_sc_properties(spell, "Reverberation")) and world.day_element == 'Water' then
+ elseif (check_sc_properties(spell, "Dark") or check_sc_properties(spell, "Distortion") or check_sc_properties(spell, "Induration")) and world.day_element == 'Ice' then
+ elseif (check_sc_properties(spell, "Dark") or check_sc_properties(spell, "Gravitation") or check_sc_properties(spell, "Scission")) and world.day_element == 'Earth' then
+ elseif (check_sc_properties(spell, "Dark") or check_sc_properties(spell, "Gravitation") or check_sc_properties(spell, "Compression")) and world.day_element == 'Dark' then
+ else
+ return
+ end
+ equip(sets.midcast.WS_Day)
+end
+
+function check_sc_properties(spell,str)
+ if spell.skillchain_a == str or spell.skillchain_b == str or spell.skillchain_c == str then return true end
+ return false
+end
+
+function aftercast(spell)
+ if player.status =='Engaged' then
+ equip(sets.aftercast[TP_sets[TP_ind]])
+ else
+ equip(sets.aftercast[Idle_sets[Idle_ind]])
+ end
+ set_priorities('hp','mp')
+end
+
+function status_change(new,old)
+ if T{'Idle','Resting'}:contains(new) then
+ equip(sets.aftercast[Idle_sets[Idle_ind]])
+ elseif new == 'Engaged' then
+ equip(sets.aftercast[TP_sets[TP_ind]])
+ end
+ set_priorities('hp','mp')
+end
+
+function buff_change(new,bool)
+ if new == 'Reprisal' and bool then
+ if current_shield.name == 'Ochain' then
+ table.reassign(current_shield,sets.priwen.sub)
+ equip({sub=current_shield})
+ end
+ elseif new == 'Reprisal' and not bool then
+ if current_shield.name == 'Priwen' then
+ table.reassign(current_shield,sets.ochain.sub)
+ equip({sub=current_shield})
+ end
+ end
+ set_priorities('hp','mp')
+end
+
+function self_command(command)
+ if command == 'toggle TP set' then
+ TP_ind = TP_ind%#TP_sets +1
+ send_command('@input /echo '..TP_sets[TP_ind]..' SET')
+ elseif command == 'toggle Idle set' then
+ if Idle_ind == 1 then
+ Idle_ind = 2
+ send_command('@input /echo SUPERTANKING SET')
+ elseif Idle_ind == 2 then
+ Idle_ind = 1
+ send_command('@input /echo NORMAL SET')
+ end
+ elseif command == 'toggle LOCK mode' then
+ lock_mode = not lock_mode
+ if lock_mode then
+ send_command('input /echo MAIN/SUB SWAPPING ENABLED')
+ enable('main','sub')
+ else
+ send_command('input /echo MAIN/SUB SWAPPING DISABLED')
+ disable('main','sub')
+ end
+ elseif command == 'toggle lockall' then
+ lockall = not lockall
+ if lockall then
+ send_command('input /echo SWAPPING DISABLED')
+ disable('main','sub')
+ else
+ send_command('input /echo SWAPPING ENABLED')
+ disable('main','sub')
+ end
+ elseif command == 'DT' then
+ equip(sets.DT)
+ elseif command == 'PDT Shield' then
+ if buffactive['Reprisal'] then
+ current_shield = table.reassign(current_shield,sets.priwen.sub)
+ else
+ current_shield = table.reassign(current_shield,sets.ochain.sub)
+ end
+ if not lock_mode then send_command('input /equip sub '..current_shield.name)
+ else equip({sub=current_shield}) end
+ elseif command == 'MDT Shield' then
+ current_shield = table.reassign(current_shield,sets.aegis.sub)
+ if not lock_mode then send_command('input /equip sub '..current_shield.name)
+ else equip({sub=current_shield}) end
+ end
+ set_priorities('hp','mp')
+end
+
+function set_priorities(key1,key2)
+ local future,current = gearswap.equip_list,gearswap.equip_list_history
+ function get_val(piece,key)
+ if piece and type(piece)=='table' and piece[key] and type(piece[key])=='number' then
+ return piece[key]
+ end
+ return 0
+ end
+ local diff = {}
+ for i,v in pairs(future) do
+ local priority = get_val(future[i],key1) - get_val(current[i],key1) + (get_val(future[i],key2) - get_val(current[i],key2))
+ if type(v) == 'table' then
+ future[i].priority = priority
+ else
+ future[i] = {name=v,priority=priority}
+ end
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_SMN.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_SMN.lua
new file mode 100644
index 0000000..b70ce39
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_SMN.lua
@@ -0,0 +1,593 @@
+include('organizer-lib')
+
+function get_sets()
+
+ sets.TH = {
+ hands={ name="Merlinic Dastanas", augments={'"Treasure Hunter"+2',},hp=9,mp=20},
+ legs={ name="Merlinic Shalwar", augments={'Pet: Accuracy+16 Pet: Rng. Acc.+16','Pet: Haste+1','"Treasure Hunter"+1','Mag. Acc.+9 "Mag.Atk.Bns."+9',},hp=29,mp=44},
+ waist="Chaac Belt",
+ }
+
+ -- Precast Sets
+ sets.precast = {}
+
+ sets.precast.FC = {
+ ammo="Impatiens",
+ head={ name="Merlinic Hood", augments={'"Fast Cast"+7','MND+10','Mag. Acc.+10','"Mag.Atk.Bns."+10',},mp=56,hp=22},
+ body={name="Zendik Robe",hp=57,mp=61},
+ hands={ name="Merlinic Dastanas", augments={'Mag. Acc.+29','"Fast Cast"+7','INT+1',},mp=20,hp=9},
+ legs={ name="Psycloth Lappas", augments={'MP+80','Mag. Acc.+15','"Fast Cast"+7',},mp=109,hp=43},
+ feet={ name="Merlinic Crackows", augments={'"Mag.Atk.Bns."+11','"Fast Cast"+7',},hp=4,mp=20},
+ neck={ name="Orunmila's Torque",mp=30},
+ waist="Witful Belt",
+ left_ear={ name="Loquac. Earring",mp=30},
+ left_ring="Weather. Ring +1",
+ right_ear={name="Etiolation Earring",hp=50,mp=50},
+ right_ring={name="Lebeche Ring",mp=40},
+ back={ name="Campestres's Cape", augments={'Pet: M.Acc.+20 Pet: M.Dmg.+20','Pet: Mag. Acc.+10','"Fast Cast"+10',}},
+ }
+ -- 15+13+7+7+12+5+3+2+6+1+10 = 81% FC
+ -- 2+3+4+2 = 11% Quickens
+ sets.precast['Astral Flow'] = {
+ head={ name="Glyphic Horn +1",hp=31,mp=95},
+ }
+
+ -- Midcast sets
+ sets.midcast = {}
+
+ sets.midcast.BP = { -- -10 from Gifts
+ main={ name="Espiritus", augments={'Enmity-6','Pet: "Mag.Atk.Bns."+30','Pet: Damage taken -4%',}, mp=88},
+ ammo="Sancus Sachet +1",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ right_ear={name="Evans Earring",mp=50},
+ hands={ name="Glyphic Bracers +1", augments={'Inc. Sp. "Blood Pact" magic burst dmg.',},hp=18,mp=41},
+ legs={ name="Glyphic Spats +1", augments={'Increases Sp. "Blood Pact" accuracy',},hp=38,mp=85},
+ } -- This set technically has too much BP Recast-, but it compensates for times when I lock gear.
+
+ sets.midcast['Mana Cede'] = {hands="Beckoner's Bracers +1"}
+
+ sets.midcast['Elemental Siphon'] = {
+ main="Nirvana",
+ sub="Vox Grip",
+ ammo="Esper Stone +1",
+ neck="Caller's Pendant",
+ rear="Smn. Earring",
+ lear={ name="Andoaa Earring", mp=30},
+ hands={ name="Telchine Gloves", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=52,mp=44},
+ head={ name="Telchine Cap", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=36,mp=32},
+ body={ name="Telchine Chas.", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=54,mp=59},
+ legs={ name="Telchine Braconi", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=43,mp=29},
+ waist={name="Kobo Obi",mp=20},
+ lring={name="Evoker's Ring",mp=25},
+ rring="Globidonta Ring",
+ back={ name="Conveyance Cape", augments={'Summoning magic skill +5','Pet: Enmity+15','Blood Pact Dmg.+4',},mp=100},
+ feet={ name="Beckoner's Pigaches +1",hp=9,mp=97},
+ day = {
+ rring = "Zodiac Ring",
+ }
+ }
+
+ sets.midcast.Cur = {
+ main="Vadose Rod",
+ sub="Genbu's Shield",
+ head="Marduk's Tiara +1",
+ neck={name="Nodens Gorget",hp=25,mp=25},
+ ear2="Novia earring",
+ hands={name="Revealer's Mitts +1",hp=22,mp=44},
+ }
+
+ sets.midcast.Stoneskin = {
+ neck={name="Nodens Gorget",hp=25,mp=25},
+ waist="Siegel Sash",
+ legs="Shedir Seraweels"
+ }
+
+ sets.midcast.Cursna = {
+ head={ name="Vanya Hood", augments={'Healing magic skill +20','"Cure" spellcasting time -7%','Magic dmg. taken -3',},hp=36,mp=32},
+ body={name="Vanya Robe",hp=54,mp=59},
+ hands={name="Hieros Mittens",mp=30},
+ legs={ name="Vanya Slops", augments={'Healing magic skill +20','"Cure" spellcasting time -7%','Magic dmg. taken -3',},hp=43,mp=29},
+ left_ring="Haoma's Ring",
+ right_ring="Haoma's Ring",
+ back={name="Oretan. Cape +1",hp=30},
+ feet={name="Vanya Clogs",hp=13,mp=14},
+ neck="Debilis Medallion",
+ }
+
+ sets.midcast.EnhancingDuration = {
+ main={ name="Gada", augments={'Enh. Mag. eff. dur. +6','"Mag.Atk.Bns."+9',}},
+ sub={name="Ammurapi Shield",hp=22,mp=58},
+ hands={ name="Telchine Gloves", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=52,mp=44},
+ head={ name="Telchine Cap", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=36,mp=32},
+ body={ name="Telchine Chas.", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=54,mp=59},
+ legs={ name="Telchine Braconi", augments={'"Elemental Siphon"+35','Enh. Mag. eff. dur. +10',},hp=43,mp=29},
+ feet={ name="Telchine Pigaches", augments={'Song spellcasting time -7%','Enh. Mag. eff. dur. +10',}},
+ }
+
+ sets.midcast.Refresh = set_combine(sets.midcast.EnhancingDuration,{
+ head={name="Amalric Coif +1",hp=27,mp=61},
+ feet={name="Inspirited Boots",hp=9,mp=20},
+ waist="Gishdubar Sash",
+ back="Grapevine Cape",
+ })
+
+ sets.midcast['Diaga'] = sets.TH
+ sets.midcast['Dia'] = sets.TH
+ sets.midcast['Dia II'] = sets.TH
+ sets.midcast['Swipe'] = sets.TH
+ sets.midcast['Lunge'] = sets.TH
+
+ -- Pet Midcast Sets
+ sets.pet_midcast = {}
+
+ sets.BP_Base = {
+ main={ name="Espiritus", augments={'Enmity-6','Pet: "Mag.Atk.Bns."+30','Pet: Damage taken -4%',}, mp=88},
+ sub="Elan Strap +1",
+ ammo="Sancus Sachet +1",
+ head={name="Apogee Crown +1",hp=-110,mp=139},
+ body={name="Convoker's Doublet +1",hp=50,mp=134},
+ hands={ name="Merlinic Dastanas", augments={'Pet: Accuracy+30 Pet: Rng. Acc.+30','Blood Pact Dmg.+9','Pet: INT+9','Pet: "Mag.Atk.Bns."+14',},mp=20,hp=9},
+ legs={ name="Enticer's Pants", augments={'MP+50','Pet: Accuracy+15 Pet: Rng. Acc.+15','Pet: Mag. Acc.+15','Pet: Damage taken -5%',},hp=38,mp=106},
+ feet={name="Apogee Pumps +1",hp=-90,mp=121},
+ left_ear="Lugalbanda Earring",
+ right_ear={name="Gelos Earring",mp=35},
+ left_ring="Varar Ring +1",
+ right_ring="Varar Ring +1",
+ }
+
+ sets.pet_midcast.Phys_BP = set_combine(sets.BP_Base,{
+ main="Nirvana",
+ neck="Shulmanu Collar",
+ waist="Klouskap Sash",
+ legs={name="Apogee Slacks +1",hp=-110,mp=56},
+ back={ name="Campestres's Cape", augments={'Pet: Acc.+20 Pet: R.Acc.+20 Pet: Atk.+20 Pet: R.Atk.+20','Eva.+20 /Mag. Eva.+20','Pet: Haste+10',}},
+ })
+
+ sets.pet_midcast.MAB_BP = set_combine(sets.BP_Base,{
+ neck={name="Adad amulet",hp=25},
+ --body={name="Apogee Dalmatica +1",hp=-160,mp=85},
+ waist={name="Caller's sash",mp=20},
+ ring2={name="Speaker's Ring",mp=40},
+ back={ name="Campestres's Cape", augments={'Pet: M.Acc.+20 Pet: M.Dmg.+20','"Fast Cast"+10',}},
+ })
+
+ sets.pet_midcast.MAB_Spell = set_combine(sets.BP_Base,{
+ neck={name="Adad amulet",hp=25},
+ --body={name="Apogee Dalmatica +1",hp=-160,mp=85},
+ waist={name="Caller's sash",mp=20},
+ ring2={name="Speaker's Ring",mp=40},
+ right_ring="Globidonta Ring",
+ back={ name="Campestres's Cape", augments={'Pet: M.Acc.+20 Pet: M.Dmg.+20','"Fast Cast"+10',}},
+ })
+
+ sets.pet_midcast.MAcc_BP = set_combine(sets.BP_Base,{
+ main="Nirvana",
+ sub="Vox Grip",
+ neck={name="Adad amulet",hp=25},
+ right_ear="Smn. Earring",
+ body={name="Beckoner's Doublet +1",hp=54,mp=151},
+ hands={name="Lamassu mitts +1",hp=18,mp=44},
+ feet={ name="Beckoner's Pigaches +1",hp=9,mp=97},
+ lring={name="Evoker's Ring",mp=25},
+ right_ring="Globidonta Ring",
+ back={ name="Campestres's Cape", augments={'Pet: M.Acc.+20 Pet: M.Dmg.+20','"Fast Cast"+10',}},
+ })
+
+ sets.pet_midcast.Buff_BP = set_combine(sets.BP_Base,{ -- Did not check
+ main="Nirvana",
+ sub="Vox Grip",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ neck="Caller's Pendant",
+ lear={ name="Andoaa Earring", mp=30},
+ right_ear="Smn. Earring",
+ --body={name="Apogee Dalmatica +1",hp=-160,mp=85},
+ hands={ name="Glyphic Bracers +1", augments={'Inc. Sp. "Blood Pact" magic burst dmg.',},hp=18,mp=41},
+ legs={name="Assid. Pants +1",hp=43,mp=29},
+ lring={name="Evoker's Ring",mp=25},
+ right_ring="Globidonta Ring",
+ back={ name="Campestres's Cape", augments={'Pet: Acc.+20 Pet: R.Acc.+20 Pet: Atk.+20 Pet: R.Atk.+20','Eva.+20 /Mag. Eva.+20','Pet: Haste+10',}},
+ })
+
+ sets.pet_midcast['Shock Squall'] = {
+ main="Nirvana",
+ sub="Vox Grip",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ neck={name="Adad amulet",hp=25},
+ right_ear="Smn. Earring",
+ body={name="Beckoner's Doublet +1",hp=54,mp=151},
+ hands={ name="Glyphic Bracers +1", augments={'Inc. Sp. "Blood Pact" magic burst dmg.',},hp=18,mp=41},
+ lring={name="Evoker's Ring",mp=25},
+ rring="Globidonta Ring",
+ back={ name="Campestres's Cape", augments={'Pet: M.Acc.+20 Pet: M.Dmg.+20','"Fast Cast"+10',}},
+ legs={ name="Enticer's Pants", augments={'MP+50','Pet: Accuracy+15 Pet: Rng. Acc.+15','Pet: Mag. Acc.+15','Pet: Damage taken -5%',},hp=38,mp=106},
+ }
+
+ --Aftercast Sets
+ sets.aftercast = {}
+
+ sets.aftercast.None = {
+ main="Mafic Cudgel",
+ sub="Genmei Shield",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ neck="Loricate Torque +1",
+ left_ear={ name="Loquac. Earring",mp=30},
+ right_ear={name="Evans Earring",mp=50},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',},hp=30,mp=22},
+ ring1={name="Dark Ring",hp=-20,mp=20},
+ ring2="Defending Ring",
+ back="Umbra Cape",
+ waist="Klouskap Sash",
+ legs={name="Assid. Pants +1",hp=43,mp=29},
+ feet={name="Herald's Gaiters",mp=12},
+ }
+
+ -- Including Auto-Refresh II, unaffected by Avatar's Favor:
+ -- Carbuncle: 11MP/tick : 10 Perp- + Refresh ideal (4 + Mitts + Refresh)
+ -- Diabolos/Celestials: 15MP/tick : 14 Perp- + Refresh ideal
+ -- Fenrir: 13MP/tick : 12 Perp- + Refresh ideal
+ -- Spirit: 7MP/tick : 6 Perp- + Refresh ideal (Beckoner's Doublet +1)
+
+ sets.aftercast.Avatar = {}
+ sets.aftercast.Avatar.Carbuncle = {
+ main="Nirvana",
+ sub="Vox Grip",
+ ammo="Sancus Sachet +1",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Asteria Mitts +1",hp=18,mp=44},
+ legs={name="Assid. Pants +1",hp=43,mp=29},
+ feet={name="Herald's Gaiters",mp=12},
+ neck="Shulmanu Collar",
+ waist="Lucidity Sash",
+ lear={ name="Andoaa Earring", mp=30},
+ right_ear={name="Evans Earring",mp=50},
+ left_ring="Varar Ring +1",
+ right_ring="Defending Ring",
+ back={ name="Campestres's Cape", augments={'Pet: Acc.+20 Pet: R.Acc.+20 Pet: Atk.+20 Pet: R.Atk.+20','Eva.+20 /Mag. Eva.+20','Pet: Haste+10',}},
+ }
+
+ sets.aftercast.Avatar.Garuda = { -- 16 MP/tick, currently negated at -15 Perp with 512 skill
+ main="Nirvana",
+ sub="Vox Grip",
+ ammo="Sancus Sachet +1",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={ name="Glyphic Bracers +1", augments={'Inc. Sp. "Blood Pact" magic burst dmg.',},hp=18,mp=41},
+ legs={name="Assid. Pants +1",hp=43,mp=29},
+ feet={name="Apogee Pumps +1",hp=-90,mp=121},
+ neck="Shulmanu Collar",
+ waist="Klouskap Sash",
+ lear={ name="Andoaa Earring", mp=30},
+ right_ear={name="Evans Earring",mp=50},
+ left_ring="Varar Ring +1",
+ right_ring="Defending Ring",
+ back={ name="Campestres's Cape", augments={'Pet: Acc.+20 Pet: R.Acc.+20 Pet: Atk.+20 Pet: R.Atk.+20','Eva.+20 /Mag. Eva.+20','Pet: Haste+10',}},
+ } -- Celestials
+
+ sets.aftercast.Avatar.Ifrit = sets.aftercast.Avatar.Garuda
+ sets.aftercast.Avatar.Shiva = sets.aftercast.Avatar.Garuda
+ sets.aftercast.Avatar.Ramuh = sets.aftercast.Avatar.Garuda
+ sets.aftercast.Avatar.Leviathan = sets.aftercast.Avatar.Garuda
+ sets.aftercast.Avatar.Titan = sets.aftercast.Avatar.Garuda
+ sets.aftercast.Avatar.Diabolos = sets.aftercast.Avatar.Garuda
+
+ sets.aftercast.Avatar.Fenrir = { -- ? MP/tick, currently negated at -15 Perp with 512 skill
+ main="Nirvana",
+ sub="Vox Grip",
+ ammo="Sancus Sachet +1",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={ name="Glyphic Bracers +1", augments={'Inc. Sp. "Blood Pact" magic burst dmg.',},hp=18,mp=41},
+ legs={name="Assid. Pants +1",hp=43,mp=29},
+ feet={name="Apogee Pumps +1",hp=-90,mp=121},
+ neck="Shulmanu Collar",
+ waist="Klouskap Sash",
+ lear={ name="Andoaa Earring", mp=30},
+ right_ear={name="Evans Earring",mp=50},
+ left_ring="Varar Ring +1",
+ right_ring="Defending Ring",
+ back={ name="Campestres's Cape", augments={'Pet: Acc.+20 Pet: R.Acc.+20 Pet: Atk.+20 Pet: R.Atk.+20','Eva.+20 /Mag. Eva.+20','Pet: Haste+10',}},
+ } -- Fenrir
+ sets.aftercast.Avatar['Cait Sith'] = set_combine(sets.aftercast.Avatar.Fenrir,{
+ hands={name="Lamassu mitts +1",hp=18,mp=44},
+ })
+
+ sets.aftercast.Avatar.Spirit = {
+ main={ name="Espiritus", augments={'Enmity-6','Pet: "Mag.Atk.Bns."+30','Pet: Damage taken -4%',}, mp=88},
+ sub="Vox Grip",
+ ammo="Sancus Sachet +1",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ body={name="Beckoner's Doublet +1",hp=54,mp=151},
+ hands={ name="Glyphic Bracers +1", augments={'Inc. Sp. "Blood Pact" magic burst dmg.',},hp=18,mp=41},
+ legs={ name="Glyphic Spats +1", augments={'Increases Sp. "Blood Pact" accuracy',}},
+ feet="Marduk's Crackows +1",
+ neck="Caller's Pendant",
+ waist={name="Caller's sash",mp=20},
+ lear={ name="Andoaa Earring", mp=30},
+ right_ear="Smn. Earring",
+ lring={name="Evoker's Ring",mp=25},
+ left_ring="Globidonta Ring",
+ back={ name="Conveyance Cape", augments={'Summoning magic skill +5','Pet: Enmity+15','Blood Pact Dmg.+4',},mp=100},
+ } -- Spirits
+
+ sets.aftercast.Resting = {
+ main={name="Numen Staff",mp=45},
+ sub="Ariesian Grip",
+ ammo={name="Mana Ampulla",mp=20},
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ neck={name="Eidolon Pendant +1",mp=15},
+ ear1="Relaxing Earring",
+ ear2={name="Antivenom Earring",mp=15},
+ body={ name="Witching Robe", augments={'MP+50','Mag. Acc.+15','"Mag.Atk.Bns."+15','"Refresh"+1',},hp=50,mp=117},
+ hands={name="Nares Cuffs",mp=50},-- Not technically accurate
+ ring1={name="Celestial Ring",mp=20},
+ ring2="Angha Ring",
+ back={name="Felicitas Cape +1",mp=15},
+ waist="Austerity Belt +1",
+ legs={name="Assid. Pants +1",hp=43,mp=29},
+ feet={name="Chelona Boots +1",mp=40},
+ }
+
+ sets.aftercast.Idle = sets.aftercast.None
+
+ sets.aftercast.Engaged = {
+ body={ name="Hagondes Coat +1", hp=54,mp=59},
+ neck="Loricate Torque +1",
+ ring1={name="Dark Ring",hp=-20,mp=20},
+ hands={ name="Hagondes Cuffs +1", augments={'Phys. dmg. taken -3%','Mag. Acc.+23',},hp=30,mp=22},
+ lear="Genmei Earring",
+ }
+ --[[{
+ main="Nirvana",
+ sub="Bloodrain Strap",
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ body="Onca Suit",
+ hands=empty,
+ legs=empty,
+ feet=empty,
+ neck="Lissome Necklace",
+ waist="Klouskap Sash",
+ left_ear="Cessance Earring",
+ right_ear="Telos Earring",
+ left_ring="Rajas Ring",
+ right_ring="Petrov Ring",
+ back="Umbra Cape",
+ }]]
+
+ sets.midcast['Garland of Bliss'] = {
+ ammo="Pemphredo Tathlum",
+ head={ name="Merlinic Hood", augments={'VIT+8','"Mag.Atk.Bns."+27','Accuracy+5 Attack+5','Mag. Acc.+18 "Mag.Atk.Bns."+18',},mp=56,hp=22},
+ body={ name="Merlinic Jubbah", augments={'"Mag.Atk.Bns."+29','Magic burst dmg.+11%','INT+10',},hp=49,mp=67},
+ hands={ name="Amalric Gages +1", augments={'MP+80','Mag. Acc.+20','"Mag.Atk.Bns."+20',},hp=13,mp=106},
+ legs={ name="Merlinic Shalwar", augments={'Mag. Acc.+25 "Mag.Atk.Bns."+25','MND+4','Mag. Acc.+15','"Mag.Atk.Bns."+12',},hp=29,mp=44},
+ feet={ name="Amalric Nails +1", augments={'MP+80','Mag. Acc.+20','"Mag.Atk.Bns."+20',},hp=4,mp=106},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Friomisi Earring",
+ right_ear="Ishvara Earring",
+ left_ring="Weather. Ring +1",
+ right_ring="Shiva Ring +1",
+ back={name="Pahtli Cape",mp=50},
+ }
+
+ sets.midcast['Elemental Magic'] ={
+ main="Marin Staff +1",
+ sub="Enki Strap",
+ ammo="Pemphredo Tathlum",
+ head={ name="Merlinic Hood", augments={'VIT+8','"Mag.Atk.Bns."+27','Accuracy+5 Attack+5','Mag. Acc.+18 "Mag.Atk.Bns."+18',},mp=56,hp=22},
+ body="Seidr Cotehardie",
+ hands={ name="Amalric Gages +1", augments={'MP+80','Mag. Acc.+20','"Mag.Atk.Bns."+20',},hp=13,mp=106},
+ legs={ name="Merlinic Shalwar", augments={'Mag. Acc.+25 "Mag.Atk.Bns."+25','MND+4','Mag. Acc.+15','"Mag.Atk.Bns."+12',},hp=29,mp=44},
+ feet={ name="Amalric Nails +1", augments={'MP+80','Mag. Acc.+20','"Mag.Atk.Bns."+20',},hp=4,mp=106},
+ neck={name="Saevus Pendant +1",mp=20},
+ waist={name="Yamabuki-no-Obi",mp=35},
+ left_ear="Friomisi Earring",
+ right_ear="Crematio Earring",
+ left_ring="Shiva Ring +1",
+ right_ring="Shiva Ring +1",
+ back="Toro Cape",
+ Ice={main="Ngqoqwanb"},
+ Earth={neck={name="Quanpur Necklace",mp=10}}
+ }
+
+ sets.midcast.Myrkr={
+ ammo={name="Ghastly Tathlum +1",mp=35},
+ head={name= "Beckoner's Horn +1",hp=31,mp=134},
+ body={name="Beckoner's Doublet +1",hp=54,mp=151},
+ hands={ name="Amalric Gages +1", augments={'MP+80','Mag. Acc.+20','"Mag.Atk.Bns."+20',},hp=13,mp=106},
+ legs={ name="Psycloth Lappas", augments={'MP+80','Mag. Acc.+15','"Fast Cast"+7',},mp=109,hp=43},
+ feet={name="Apogee Pumps +1",hp=-90,mp=121},
+ neck={name="Sanctity Necklace",hp=35,mp=35},
+ waist={name="Mujin Obi",mp=60},
+ left_ear={name="Etiolation Earring",hp=50,mp=50},
+ right_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ left_ring={name="Lebeche Ring",mp=40},
+ right_ring={name="Sangoma Ring",mp=70},
+ back={ name="Conveyance Cape", augments={'Summoning magic skill +5','Pet: Enmity+15','Blood Pact Dmg.+4',},mp=100},
+ }
+
+ -- Variables and notes to myself
+ Debuff_BPs = T{'Diamond Storm','Sleepga','Slowga','Tidal Roar','Shock Squall','Nightmare','Pavor Nocturnus','Ultimate Terror','Somnolence','Lunar Cry','Lunar Roar'}
+ Magical_BPs = T{'Heavenly Strike','Wind Blade','Holy Mist','Night Terror','Thunderstorm','Geocrush','Meteor Strike','Grand Fall','Lunar Bay','Thunderspark','Nether Blast',
+ 'Aerial Blast','Searing Light','Diamond Dust','Earthen Fury','Zantetsuken','Tidal Wave','Judgment Bolt','Inferno','Howling Moon','Ruinous Omen'}
+ Additional_effect_BPs = T{'Rock Throw'}
+ AvatarList = S{'Shiva','Ramuh','Garuda','Leviathan','Diabolos','Titan','Fenrir','Ifrit','Carbuncle',
+ 'Fire Spirit','Air Spirit','Ice Spirit','Thunder Spirit','Light Spirit','Dark Spirit','Earth Spirit','Water Spirit',
+ 'Cait Sith','Alexander','Odin','Atomos'}
+ send_command('input /macro book 8;wait .1;input /macro set 1')
+end
+
+function pet_change(pet,gain)
+ equip_aftercast(player.status,pet)
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function pet_status_change(a,b)
+ windower.add_to_chat(8,'Pet status change: '..tostring(a)..' '..tostring(b)) -- Useful for knowing when you got aggroed
+end
+
+function precast(spell)
+ if spell.action_type == 'Magic' then
+ equip(sets.precast.FC)
+ end
+
+ if sets.precast.FC[spell.element] then equip(sets.precast.FC[spell.element]) end
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function midcast(spell)
+ if pet_midaction() then
+ return
+ end
+ equip_aftercast(player.status,pet) -- Put DT gear on
+ if string.find(spell.type,'BloodPact') then
+ if buffactive['Astral Conduit'] then
+ pet_midcast(spell)
+ else
+ equip(sets.midcast.BP)
+ end
+ elseif string.find(spell.english,'Cur') then
+ equip(sets.midcast.Cur)
+ elseif sets.midcast[spell.english] then
+ equip(sets.midcast[spell.english])
+ if spell.english == 'Elemental Siphon' then
+ if pet.element and pet.element == world.day_element and world.day_element ~= "Light" and world.day_element ~= 'Dark' then
+ equip(sets.midcast['Elemental Siphon'].day) -- Zodiac Ring affects Siphon, but only on Fires-Lightningsday
+ end
+ end
+ elseif spell.skill == 'Elemental Magic' then
+ equip(sets.midcast['Elemental Magic'])
+ if sets.midcast['Elemental Magic'][spell.element] then
+ equip(sets.midcast['Elemental Magic'][spell.element])
+ end
+ if world.day_element == spell.element or world.weather_element == spell.element then
+ equip({waist="Hachirin-no-Obi"})
+ end
+ elseif spell.skill == 'Enhancing Magic' then
+ equip(sets.midcast.EnhancingDuration)
+ end
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function aftercast(spell)
+ if pet_midaction() then
+ return
+ elseif spell and string.find(spell.type,'BloodPact') and not spell.interrupted then
+ pet_midcast(spell)
+ else
+ -- Don't want to swap away too quickly if I'm about to put BP damage gear on
+ -- Need to wait 1 in order to allow pet information to update on Release.
+ equip_aftercast(player.status,pet)
+ end
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function status_change(new,old)
+ equip_aftercast(new,pet)
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function pet_midcast(spell)
+ if spell.name == 'Perfect Defense' then
+ equip(sets.midcast['Elemental Siphon'],{feet="Marduk's Crackows +1"})
+ elseif spell.type=='BloodPactWard' then
+ if Debuff_BPs:contains(spell.name) then
+ equip(sets.pet_midcast.MAcc_BP)
+ else
+ equip(sets.pet_midcast.Buff_BP)
+ end
+ elseif spell.type=='BloodPactRage' then
+ if Magical_BPs:contains(spell.name) or string.find(spell.name,' II') or string.find(spell.name,' IV') then
+ equip(sets.pet_midcast.MAB_BP)
+ elseif Additional_effect_BPs:contains(spell.name) then -- for BPs where the additional effect matters more than the damage
+ equip(sets.pet_midcast.MAcc_BP)
+ else
+ equip(sets.pet_midcast.Phys_BP)
+ end
+ elseif spell.type=='BlackMagic' then
+ equip(sets.pet_midcast.MAB_Spell)
+ end
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function pet_aftercast(spell)
+ windower.add_to_chat(8,'pet_aftercast: '..tostring(spell.name))
+ equip_aftercast(player.status,pet)
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function self_command(command)
+ if command == 'Idle' then
+ equip_aftercast('Idle',pet)
+ end
+
+ if player.mpp > 80 then
+ set_priorities('mp','hp')
+ else
+ set_priorities('hp','mp')
+ end
+end
+
+function equip_aftercast(status,pet)
+ if sets.aftercast[status] then
+ equip(sets.aftercast[status])
+ end
+ if pet.isvalid then
+ if string.find(pet.name,'Spirit') then
+ equip(sets.aftercast.Avatar.Spirit)
+ elseif sets.aftercast.Avatar[pet.name] then
+ equip(sets.aftercast.Avatar[pet.name])
+ end
+ end
+ if status == "Engaged" then
+ equip(sets.aftercast[status])
+ end
+end
+
+function set_priorities(key1,key2)
+ local future,current = gearswap.equip_list,gearswap.equip_list_history
+ function get_val(piece,key)
+ if piece and type(piece)=='table' and piece[key] and type(piece[key])=='number' then
+ return piece[key]
+ end
+ return 0
+ end
+ local diff = {}
+ for i,v in pairs(future) do
+ local priority = get_val(future[i],key1) - get_val(current[i],key1) + (get_val(future[i],key2) - get_val(current[i],key2))
+ if type(v) == 'table' then
+ future[i].priority = priority
+ else
+ future[i] = {name=v,priority=priority}
+ end
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_THF.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_THF.lua
new file mode 100644
index 0000000..ecc84d6
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_THF.lua
@@ -0,0 +1,321 @@
+include('organizer-lib')
+
+function get_sets()
+ TP_Index = 1
+ Idle_Index = 1
+
+
+ ta_hands = {name="Adhemar Wristbands +1"}
+ acc_hands = {name="Adhemar Wristbands +1"}
+ wsd_hands = {name="Meg. Gloves +1",}
+ crit_hands = {name="Adhemar Wristbands +1"}
+ dt_hands = { name="Herculean Gloves", augments={'Accuracy+30','Damage taken-4%','STR+9','Attack+4',}}
+ waltz_hands = { name="Herculean Gloves", augments={'Accuracy+22','"Waltz" potency +11%','STR+9',}}
+
+ sets.weapons = {}
+ sets.weapons[1] = {main="Taming Sari"}
+ sets.weapons[2]={main="Twashtar"}
+ sets.weapons[3]={main="Atoyac"}
+
+ sets.JA = {}
+-- sets.JA.Conspirator = {body="Raider's Vest +2"}
+-- sets.JA.Accomplice = {head="Raider's Bonnet +2"}
+-- sets.JA.Collaborator = {head="Raider's Bonnet +2"}
+ sets.JA['Perfect Dodge'] = {hands="Plun. Armlets +1"}
+ sets.JA.Steal = {ammo="Barathrum",neck="Pentagalus Charm",hands="Thief's Kote",
+ waist="Key Ring Belt",legs="Pillager's Culottes +1",feet="Pillager's Poulaines +1"}
+ sets.JA.Flee = {feet="Pillager's Poulaines +1"}
+ sets.JA.Despoil = {ammo="Barathrum",legs="Raider's Culottes +2",feet="Skulker's Poulaines"}
+-- sets.JA.Mug = {head="Assassin's Bonnet +2"}
+ sets.JA.Waltz = {head="Anwig Salade",
+ neck="Unmoving Collar +1",
+ body="Passion Jacket",
+ hands=waltz_hands,
+ ring1="Valseur's Ring",
+ ring2="Carbuncle Ring +1",
+ waist="Aristo Belt",
+ legs="Desultor Tassets",
+ feet="Dance Shoes"
+ }
+
+ sets.WS = {}
+ sets.WS.SA = {}
+ sets.WS.TA = {}
+ sets.WS.SATA = {}
+
+ sets.WS.Evisceration = {
+ ammo="Yetshila",
+ head="Adhemar Bonnet +1",
+ body="Abnoba Kaftan",
+ hands=crit_hands,
+ legs={ name="Lustr. Subligar +1", augments={'Accuracy+20','DEX+8','Crit. hit rate+3%',}},
+ feet="Adhe. Gamashes +1",
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ right_ear="Mache Earring +1",
+ left_ring="Begrudging Ring",
+ right_ring="Ramuh Ring +1",
+ back={ name="Toutatis's Cape", augments={'DEX+20','Accuracy+20 Attack+20','Weapon skill damage +10%',}},
+ }
+
+ sets.WS["Rudra's Storm"] = {ammo="Seething Bomblet +1",
+ head="Lustratio Cap +1",
+ neck="Caro Necklace",
+ ear1="Moonshade Earring",
+ ear2="Ishvara Earring",
+ body="Adhemar Jacket +1",
+ hands=wsd_hands,
+ ring1="Ramuh Ring +1",
+ ring2="Ramuh Ring +1",
+ back={ name="Toutatis's Cape", augments={'DEX+20','Accuracy+20 Attack+20','Weapon skill damage +10%',}},
+ waist="Grunfeld Rope",
+ legs="Lustratio Subligar +1",
+ feet="Lustratio Leggings +1",
+ }
+
+ sets.WS.SA["Rudra's Storm"] = set_combine(sets.WS["Rudra's Storm"],{ammo="Yetshila",
+ head="Adhemar Bonnet +1",
+ body={ name="Herculean Vest", augments={'Accuracy+21','Crit. hit damage +5%','DEX+9',}},
+ }
+ )
+
+ sets.WS.TA["Rudra's Storm"] = set_combine(sets.WS["Rudra's Storm"],{ammo="Yetshila",
+ head="Adhemar Bonnet +1",
+ body={ name="Herculean Vest", augments={'Accuracy+21','Crit. hit damage +5%','DEX+9',}},
+ }
+ )
+
+ sets.WS["Mandalic Stab"] = sets.WS["Rudra's Storm"]
+
+ sets.WS.SA["Mandalic Stab"] = sets.WS.SA["Rudra's Storm"]
+
+ sets.WS.TA["Mandalic Stab"] = sets.WS.TA["Rudra's Storm"]
+
+ sets.WS.Exenterator = {ammo="Seething Bomblet +1",
+ head="Meghanada Visor +1",neck="Fotia Gorget",ear1="Steelflash Earring",ear2="Bladeborn Earring",
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ right_ring="Ilabrat Ring",
+ ring2="Epona's Ring",
+ back={ name="Toutatis's Cape", augments={'DEX+20','Accuracy+20 Attack+20','Weapon skill damage +10%',}},
+ waist="Fotia Belt",
+ legs="Mummu Kecks +1",
+ feet="Adhemar Gamashes +1"
+ }
+
+ TP_Set_Names = {"Low Man","Delay Cap","Evasion","TH","Acc","DT"}
+ sets.TP = {}
+ sets.TP['Low Man'] = {
+ ammo="Seething Bomblet +1",
+ head="Adhemar Bonnet +1",
+ body="Adhemar Jacket +1",
+ hands={ name="Floral Gauntlets", augments={'Rng.Acc.+15','Accuracy+15','"Triple Atk."+3','Magic dmg. taken -4%',}},
+ legs="Mummu Kecks +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Lissome Necklace",
+ waist="Reiki Yotai",
+ left_ear="Suppanomimi",
+ right_ear="Brutal Earring",
+ left_ring="Hetairoi Ring",
+ right_ring="Epona's Ring",
+ back={ name="Toutatis's Cape", augments={'STR+20','Accuracy+20 Attack+20','"Store TP"+10',}},
+ }
+
+ sets.TP['TH'] = set_combine(sets.TP['Low Man'],{
+ hands={ name="Plun. Armlets +1", augments={'Enhances "Perfect Dodge" effect',}},
+ feet="Skulker's Poulaines",
+ })
+
+ sets.TP['Acc'] = {
+ ammo="Falcon Eye",
+ head={ name="Dampening Tam", augments={'DEX+10','Accuracy+15','Mag. Acc.+15','Quadruple Attack +3',}},
+ body="Adhemar Jacket +1",
+ hands=acc_hands,
+ legs="Mummu Kecks +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Combatant's Torque",
+ waist="Olseni Belt",
+ left_ear="Suppanomimi",
+ right_ear="Telos Earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Ramuh Ring +1",
+ back={ name="Toutatis's Cape", augments={'DEX+20','Accuracy+20 Attack+20','Weapon skill damage +10%',}},
+ }
+
+ sets.TP['Delay Cap'] = {
+ ammo="Seething Bomblet +1",
+ head="Adhemar Bonnet +1",
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ legs="Mummu Kecks +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+25','"Triple Atk."+4','DEX+10',}},
+ neck="Lissome Necklace",
+ waist="Windbuffet Belt +1",
+ left_ear="Cessance Earring",
+ right_ear="Telos Earring",
+ left_ring="Hetairoi Ring",
+ right_ring="Epona's Ring",
+ back={ name="Toutatis's Cape", augments={'STR+20','Accuracy+20 Attack+20','"Store TP"+10',}},
+ }
+
+ sets.TP.Evasion = {
+ ammo="Yamarang",
+ head="Adhemar Bonnet +1",
+ body="Adhemar Jacket +1",
+ hands=ta_hands,
+ legs="Mummu Kecks +1",
+ feet="Adhe. Gamashes +1",
+ neck="Combatant's Torque",
+ waist="Kasiri Belt",
+ left_ear="Eabani Earring",
+ right_ear="Infused Earring",
+ left_ring="Vengeful Ring",
+ right_ring="Epona's Ring",
+ back={ name="Canny Cape", augments={'DEX+5','"Dual Wield"+5',}},
+ }
+
+ sets.TP.DT = {
+ ammo="Seething Bomblet +1",
+ head={ name="Dampening Tam", augments={'DEX+10','Accuracy+15','Mag. Acc.+15','Quadruple Attack +3',}},
+ body="Emet Harness +1",
+ hands=dt_hands,
+ legs="Mummu Kecks +1",
+ feet={ name="Herculean Boots", augments={'Accuracy+20 Attack+20','Phys. dmg. taken -5%','DEX+10','Accuracy+6',}},
+ neck="Loricate Torque +1",
+ waist="Reiki Yotai",
+ left_ear="Suppanomimi",
+ right_ear="Genmei Earring",
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ back={ name="Canny Cape", augments={'DEX+5','"Dual Wield"+5',}},
+ }
+
+ Idle_Set_Names = {'Normal','MDT',"STP"}
+ sets.Idle = {}
+ sets.Idle.Normal = {
+ ammo="Yamarang",
+ head="Meghanada Visor +1",
+ body="Emet Harness +1",
+ hands=dt_hands,
+ legs="Mummu Kecks +1",
+ feet="Skd. Jambeaux +1",
+ neck="Wiglen Gorget",
+ waist="Kasiri Belt",
+ left_ear="Etiolation Earring",
+ right_ear="Genmei Earring",
+ left_ring="Paguroidea Ring",
+ right_ring="Sheltered Ring",
+ back={ name="Toutatis's Cape", augments={'STR+20','Accuracy+20 Attack+20','"Store TP"+10',}},
+ }
+
+ sets.Idle.MDT = {
+ ammo="Yamarang",
+ head={ name="Dampening Tam", augments={'DEX+10','Accuracy+15','Mag. Acc.+15','Quadruple Attack +3',}},
+ body="Emet Harness +1",
+ hands=dt_hands,
+ legs="Mummu Kecks +1",
+ feet="Skd. Jambeaux +1",
+ neck="Loricate Torque +1",
+ waist="Wanion Belt",
+ left_ear="Etiolation Earring",
+ right_ear="Genmei Earring",
+ left_ring="Defending Ring",
+ right_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ back="Mollusca Mantle",
+ }
+
+ sets.Idle['STP'] = {
+ main="Vajra",
+ sub="Twashtar",
+ ammo="Ginsen",
+ head="Meghanada Visor +1",
+ body={ name="Herculean Vest", augments={'Accuracy+21','Crit. hit damage +5%','DEX+9',}},
+ hands="Adhemar Wristbands +1",
+ legs={ name="Samnuha Tights", augments={'STR+8','DEX+9','"Dbl.Atk."+3','"Triple Atk."+2',}},
+ feet="Skd. Jambeaux +1",
+ neck="Combatant's Torque",
+ waist="Goading Belt",
+ left_ear="Telos Earring",
+ right_ear="Digni. Earring",
+ left_ring="Apate Ring",
+ right_ring="Rajas Ring",
+ back={ name="Toutatis's Cape", augments={'STR+20','Accuracy+20 Attack+20','"Store TP"+10',}},
+ }
+
+ send_command('input /macro book 12;wait .1;input /macro set 2')
+
+
+ sets.FastCast = {
+ ammo="Impatiens",
+ head={ name="Herculean Helm", augments={'"Fast Cast"+6','Mag. Acc.+2',}},
+ body={ name="Taeon Tabard", augments={'Accuracy+22','"Fast Cast"+5','Crit. hit damage +3%',}},
+ hands="Leyline Gloves",
+ legs="Enif Cosciales",
+ feet={ name="Herculean Boots", augments={'Mag. Acc.+16','"Fast Cast"+6','MND+4',}},
+ neck="Orunmila's Torque",
+ left_ear="Loquac. Earring",
+ right_ear="Enchntr. Earring +1",
+ left_ring="Rahab Ring",
+ right_ring="Weather. Ring +1",
+ }
+
+
+ sets.frenzy = {head="Frenzy Sallet"}
+end
+
+function precast(spell)
+ if sets.JA[spell.english] then
+ equip(sets.JA[spell.english])
+ elseif spell.type=="WeaponSkill" then
+ if sets.WS[spell.english] then equip(sets.WS[spell.english]) end
+ if buffactive['sneak attack'] and buffactive['trick attack'] and sets.WS.SATA[spell.english] then equip(sets.WS.SATA[spell.english])
+ elseif buffactive['sneak attack'] and sets.WS.SA[spell.english] then equip(sets.WS.SA[spell.english])
+ elseif buffactive['trick attack'] and sets.WS.TA[spell.english] then equip(sets.WS.TA[spell.english]) end
+ elseif string.find(spell.english,'Waltz') then
+ equip(sets.JA.Waltz)
+ elseif spell.action_type == "Magic" then
+ equip(sets.FastCast)
+ end
+end
+
+function aftercast(spell)
+ if player.status=='Engaged' then
+ equip(sets.TP[TP_Set_Names[TP_Index]])
+ else
+ equip(sets.Idle[Idle_Set_Names[Idle_Index]])
+ end
+end
+
+function status_change(new,old)
+ if T{'Idle','Resting'}:contains(new) then
+ equip(sets.Idle[Idle_Set_Names[Idle_Index]])
+ elseif new == 'Engaged' then
+ equip(sets.TP[TP_Set_Names[TP_Index]])
+ end
+end
+
+function buff_change(buff,gain_or_loss)
+ if buff=="Sneak Attack" then
+ soloSA = gain_or_loss
+ elseif buff=="Trick Attack" then
+ soloTA = gain_or_loss
+ elseif gain_or_loss and buff == 'Sleep' and player.hp > 99 then
+ print('putting on Frenzy sallet!')
+ equip(sets.frenzy)
+ end
+end
+
+function self_command(command)
+ if command == 'toggle TP set' then
+ TP_Index = TP_Index +1
+ if TP_Index > #TP_Set_Names then TP_Index = 1 end
+ send_command('@input /echo ----- TP Set changed to '..TP_Set_Names[TP_Index]..' -----')
+ equip(sets.TP[TP_Set_Names[TP_Index]])
+ elseif command == 'toggle Idle set' then
+ Idle_Index = Idle_Index +1
+ if Idle_Index > #Idle_Set_Names then Idle_Index = 1 end
+ send_command('@input /echo ----- Idle Set changed to '..Idle_Set_Names[Idle_Index]..' -----')
+ equip(sets.Idle[Idle_Set_Names[Idle_Index]])
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_WAR.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_WAR.lua
new file mode 100644
index 0000000..fb67ba9
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Byrth_WAR.lua
@@ -0,0 +1,235 @@
+include('organizer-lib')
+
+function get_sets()
+
+ sets.JA = {}
+ sets.JA.Berserk = {body="Pumm. Lorica +1", back={ name="Cichol's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Dbl.Atk."+10',}}, feet="Agoge Calligae +1"}
+ sets.JA.Aggressor = {head="Pumm. Mask +1", body="Agoge Lorica +1"}
+ sets.JA.Warcry = {head="Agoge Mask +1"}
+ sets.JA['Blood Rage'] = {body="Boii Lorica +1"}
+ sets.JA['Mighty Strikes'] = {hands="Agoge Mufflers +1"}
+ sets.JA.Tomahawk = {ammo="Thr. Tomahawk",feet="Agoge Calligae +1"}
+ sets.JA.Provoke = sets.Enmity
+
+ sets.TP = {}
+ TP_mode = 'Acc'
+ sets.TP.Ragnarok = {main="Ragnarok"}
+ sets.TP.Ukonvasara = {main="Ukonvasara"}
+
+ sets.TP.Normal = {
+ sub="Bloodrain Strap",
+ ammo="Ginsen",
+ head="Flam. Zucchetto +1",
+ body="Flamma Korazin +1",
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}},
+ legs="Jokushu Haidate",--{ name="Odyssean Cuisses", augments={'Attack+30','"Dbl.Atk."+5','AGI+6','Accuracy+10',}},
+ feet="Boii Calligae +1",
+ neck="Lissome Necklace",
+ waist="Windbuffet Belt +1",
+ left_ear="Telos Earring",
+ right_ear="Brutal Earring",
+ left_ring="Petrov Ring",
+ right_ring="Rajas Ring",
+ back={ name="Cichol's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Dbl.Atk."+10',}},
+ }
+
+ sets.TP.Acc = {
+ ammo="Seeth. Bomblet +1",
+ head="Flam. Zucchetto +1",
+ body="Flamma Korazin +1",
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}},
+ legs="Jokushu Haidate",
+ feet="Flam. Gambieras +1",
+ neck="Combatant's Torque",
+ waist="Grunfeld Rope",
+ left_ear="Cessance Earring",
+ right_ear="Telos Earring",
+ left_ring="Ramuh Ring +1",
+ right_ring="Rajas Ring",
+ back={ name="Cichol's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Dbl.Atk."+10',}},
+ }
+
+ sets.TP.DT = sets.DT
+
+ sets.Enmity = {
+ ammo="Iron Gobbet",
+ head="Pummeler's Mask +1",
+ body="Yorium Cuirass",
+ hands={ name="Yorium Gauntlets", augments={'Mag. Evasion+8','Enmity+10','Phys. dmg. taken -4%',}},
+ legs={ name="Odyssean Cuisses", augments={'Attack+9','Enmity+8',}},
+ feet={ name="Eschite Greaves", augments={'HP+80','Enmity+7','Phys. dmg. taken -4',}},
+ neck="Unmoving Collar +1",
+ waist="Goading Belt",
+ left_ear="Trux Earring",
+ right_ear="Pluto's Pearl",
+ left_ring="Eihwaz Ring",
+ right_ring="Provocare Ring",
+ back="Impassive Mantle",
+ }
+ sets.FC = {
+ ammo="Impatiens",
+ body={ name="Odyss. Chestplate", augments={'"Fast Cast"+6'}},
+ hands="Leyline Gloves",
+ feet={ name="Odyssean Greaves", augments={'Accuracy+17','"Fast Cast"+6','Attack+13',}},
+ neck="Orunmila's Torque",
+ left_ear="Loquac. Earring",
+ right_ear="Enchanter Earring +1",
+ left_ring="Rahab Ring",
+ right_ring="Weather. Ring +1",
+ }
+
+ sets.WS = {}
+ sets.WS['Raging Rush'] = {
+ sub="Bloodrain Strap",
+ ammo="Yetshila",
+ head={ name="Lustratio Cap +1", augments={'INT+35','STR+8','DEX+8',}},
+ body="Flamma Korazin +1",
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}},
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}},
+ feet={ name="Lustra. Leggings +1", augments={'HP+65','STR+15','DEX+15',}},
+ neck="Fotia Gorget",
+ waist="Metalsinger Belt",
+ left_ear="Brutal Earring",
+ right_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ left_ring="Begrudging Ring",
+ right_ring="Ifrit Ring +1",
+ back={ name="Cichol's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Dbl.Atk."+10',}},
+ }
+
+ sets.WS["Ukko's Fury"] = {
+ sub="Bloodrain Strap",
+ ammo="Yetshila",
+ head={ name="Lustratio Cap +1", augments={'INT+35','STR+8','DEX+8',}},
+ body="Flamma Korazin +1",
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}},
+ legs={ name="Valor. Hose", augments={'Accuracy+27','Crit. hit damage +5%','DEX+3',}},
+ feet={ name="Lustra. Leggings +1", augments={'HP+65','STR+15','DEX+15',}},
+ neck="Fotia Gorget",
+ waist="Windbuffet Belt +1",
+ left_ear="Brutal Earring",
+ right_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ left_ring="Begrudging Ring",
+ right_ring="Ifrit Ring +1",
+ back={ name="Cichol's Mantle", augments={'STR+20','Accuracy+20 Attack+20','Weapon skill damage +10%',}},
+ }
+
+ sets.WS["Fell Cleave"] = {
+ ammo="Seeth. Bomblet +1",
+ head={ name="Lustratio Cap +1", augments={'INT+35','STR+8','DEX+8',}},
+ body="Sulevia's Plate. +1",
+ hands={ name="Odyssean Gauntlets", augments={'Attack+23','"Dbl.Atk."+5','STR+9','Accuracy+15',}},
+ legs="Sulevi. Cuisses +1",
+ feet={ name="Lustra. Leggings +1", augments={'HP+65','STR+15','DEX+15',}},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ left_ear="Ishvara Earring",
+ right_ear="Brutal Earring",
+ left_ring="Ifrit Ring +1",
+ right_ring="Rajas Ring",
+ back={ name="Cichol's Mantle", augments={'STR+20','Accuracy+20 Attack+20','Weapon skill damage +10%',}},
+ }
+
+ sets.WS.Resolution = {
+ ammo="Seeth. Bomblet +1",
+ head={ name="Argosy Celata +1", augments={'STR+12','DEX+12','Attack+20',}},
+ body="Flamma Korazin +1",
+ hands={ name="Argosy Mufflers +1", augments={'STR+12','DEX+12','Attack+20',}},
+ legs={ name="Argosy Breeches +1", augments={'STR+12','DEX+12','Attack+20',}},
+ feet={ name="Argosy Sollerets +1", augments={'STR+12','DEX+12','Attack+20',}},
+ neck="Fotia Gorget",
+ waist="Fotia Belt",
+ right_ear="Telos Earring",
+ left_ear={ name="Moonshade Earring", augments={'Attack+4','TP Bonus +25',}},
+ left_ring="Rajas Ring",
+ right_ring="Apate Ring",
+ back={ name="Cichol's Mantle", augments={'STR+20','Accuracy+20 Attack+20','"Dbl.Atk."+10',}},
+ Gavialis = S{'Lightsday','Earthsday','Windsday','Firesday','Lightningsday'},
+ }
+
+ sets.WS.Gavialis = {head="Gavialis Helm"}
+
+ sets.Idle = {
+ ammo="Staunch Tathlum",
+ head="Sulevia's Mask +1",
+ body="Sulevia's Plate. +1",
+ hands={ name="Odyssean Gauntlets", augments={'Attack+29','Damage taken-4%','AGI+3',}},
+ legs="Sulevi. Cuisses +1",
+ feet="Hermes' Sandals +1",
+ neck="Loricate Torque +1",
+ waist="Flume Belt +1",
+ left_ear="Genmei Earring",
+ right_ear="Etiolation Earring",
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ right_ring="Defending Ring",
+ back="Impassive Mantle",
+ }
+
+ sets.DT = {
+ ammo="Staunch Tathlum",
+ head="Sulevia's Mask +1",
+ body="Sulevia's Plate. +1",
+ hands="Souveran Handschuhs +1",
+ legs="Sulevi. Cuisses +1",
+ feet={ name="Souveran Schuhs +1", augments={'HP+105','Enmity+9','Potency of "Cure" effect received +15%',}},
+ neck="Loricate Torque +1",
+ waist="Flume Belt +1",
+ left_ear="Genmei Earring",
+ right_ear="Etiolation Earring",
+ left_ring={ name="Dark Ring", augments={'Breath dmg. taken -4%','Phys. dmg. taken -6%','Magic dmg. taken -5%',}},
+ right_ring="Defending Ring",
+ back="Impassive Mantle",
+ }
+
+ sets.TP.DT = sets.DT
+
+ send_command('input /macro book 6;wait .1;input /macro set 2')
+end
+
+function precast(spell)
+ if spell.cast_time then
+ equip(sets.FC)
+ end
+end
+
+function midcast(spell)
+ if sets.JA[spell.english] then
+ equip(sets.JA[spell.english])
+ elseif sets.WS[spell.english] then
+ equip(sets.WS[spell.english])
+ if sets.WS[spell.english].Gavialis and sets.WS[spell.english].Gavialis[world.day] then
+ equip(sets.WS.Gavialis)
+ end
+ end
+end
+
+function aftercast(spell)
+ if player.status == 'Engaged' then
+ equip(sets.TP[TP_mode])
+ else
+ equip(sets.Idle)
+ end
+end
+
+function status_change(new,old)
+ if T{'Idle','Resting'}:contains(new) then
+ equip(sets.Idle)
+ elseif new == 'Engaged' then
+ equip(sets.TP[TP_mode])
+ end
+end
+
+function self_command(command)
+ if command == 'DT' then
+ equip(sets.DT)
+ elseif command == 'TP' then
+ if TP_mode=="Acc" then
+ TP_mode="Normal"
+ elseif TP_mode=="Normal" then
+ TP_mode="DT"
+ elseif TP_mode=="DT" then
+ TP_mode='Acc'
+ end
+ windower.add_to_chat('TP mode is now: '..TP_mode)
+ equip(sets.TP[TP_mode])
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Snprphnx_SCH.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Snprphnx_SCH.lua
new file mode 100644
index 0000000..b83b3fe
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Snprphnx_SCH.lua
@@ -0,0 +1,256 @@
+function get_sets()
+
+ sets.aftercast_Idle_noSub = {main="Owleyes",sub="Genbu's Shield",ammo="Incantor Stone",
+ head="Savant's bonnet +2",neck="Twilight Torque",ear1="Lifestorm Earring",ear2="Loquacious Earring",
+ body="Hagondes Coat",hands="Serpentes Cuffs",ring1="Sangoma Ring",ring2="Maquette Ring",
+ back="Shadow Mantle",waist="Korin Obi",legs="Nares Trews",feet="Serpentes Sabots"}
+
+ sets.aftercast_Idle_Sub = {main="Owleyes",sub="Genbu's Shield",ammo="Incantor Stone",
+ head="Savant's bonnet +2",neck="Twilight Torque",ear1="Savant's Earring",ear2="Loquacious Earring",
+ body="Hagondes Coat",hands="Serpentes Cuffs",ring1="Sangoma Ring",ring2="Maquette Ring",
+ back="Shadow Mantle",waist="Korin Obi",legs="Nares Trews",feet="Serpentes Sabots"}
+
+ sets.aftercast_Idle = sets.aftercast_Idle_noSub
+
+ sets.precast_FastCast = {ammo="Incantor Stone",head="Nahtirah Hat",ear2="Loquacious Earring",
+ body="Anhur Robe",hands="Gendewitha Gages",back="Swith Cape",legs="Orvail Pants",feet="Chelona Boots"}
+
+ sets.Resting = {main="Chatoyant Staff",sub="Mephitis Grip",
+ head="Nahtirah Hat",neck="Twilight Torque",ear1="Lifestorm Earring",ear2="Loquacious Earring",
+ body="Hagondes Coat",hands="Nares Cuffs",ring1="Sangoma Ring",ring2="Maquette Ring",
+ back="Shadow Mantle",waist="Korin Obi",legs="Nares Trews",feet="Chelona Boots"}
+
+ sets.midcast_ElementalMagic = {main="Atinian Staff",sub="Wizzan Grip",ammo="Witchstone",
+ head="Nares Cap",neck="Eddy Necklace",ear1="Hecate's Earring",ear2="Novio Earring",
+ body="Hagondes Coat",hands="Yaoyotl Gloves",ring1="Strendu Ring",ring2="Icesoul Ring",
+ back="Searing Cape",waist="Maniacus Sash",legs="Hagondes Pants",feet="Bokwus Boots"}
+
+ sets.midcast_DarkMagic = {main="Chatoyant Staff",sub="Arbuda Grip",ammo="Hasty Pinion +1",
+ head="Appetence Crown",neck="Aesir Torque",ear1="Hirudinea Earring",ear2="Loquacious Earring",
+ body="Hedera Cotehardie",hands="Ayao's Gages",ring1="Balrahn's Ring",ring2="Excelsis Ring",
+ back="Merciful Cape",waist="Goading Belt",legs="Auspex Slops",feet="Bokwus Boots"}
+
+ sets.midcast_EnfeeblingMagic = {main="Atinian Staff",sub="Mephitis Grip",ammo="Savant's Treatise",
+ head="Nahtirah Hat",neck="Eddy Necklace",ear1="Lifestorm Earring",ear2="Psystorm Earring",
+ body="Hedera Cotehardie",hands="Hagondes Cuffs",ring1="Sangoma Ring",ring2="Maquette Ring",
+ back="Merciful Cape",waist="Cascade Belt",legs="Orvail Pants",feet="Rubeus Boots"}
+
+ sets.midcast_Impact = {main="Atinian Staff",sub="Wizzan Grip",ammo="Witchstone",
+ neck="Eddy Necklace",ear1="Hecate's Earring",ear2="Novio Earring",
+ hands="Yaoyotl Gloves",ring1="Strendu Ring",ring2="Icesoul Ring",
+ back="Searing Cape",waist="Maniacus Sash",legs="Hagondes Pants",feet="Bokwus Boots"}
+
+ sets.midcast_Embrava = {main="Kirin's Pole",sub="Fulcio Grip",ammo="Savant's Treatise",
+ head="Svnt. Bonnet +2",neck="Colossus's Torque",ear1="Lifestorm Earring",ear2="Loquacious Earring",
+ body="Anhur Robe",hands="Savant's Bracers +2",
+ back="Merciful Cape",waist="Cascade Belt",legs="Shedir Seraweels",feet="Rubeus Boots"}
+
+ sets.midcast_EnhancingMagic = {main="Kirin's Pole",sub="Fulcio Grip",ammo="Incantor Stone",
+ head="Nahtirah Hat",neck="Colossus's Torque",ear1="Lifestorm Earring",ear2="Loquacious Earring",
+ body="Anhur Robe",hands="Gendewitha Gages",
+ back="Swith Cape",waist="Ninurta's Sash",legs="Orvail Pants",feet="Chelona Boots"}
+
+ sets.precast_Stun = {main="Apamajas II",sub="Mephitis Grip",ranged="Aureole",
+ head="Nahtirah Hat",neck="Eddy Necklace",ear1="Lifestorm Earring",ear2="Psystorm Earring",
+ body="Hedera Cotehardie",hands="Gendewitha Gages",ring1="Sangoma Ring",ring2="Maquette Ring",
+ back="Swith Cape",waist="Ninurta's Sash",legs="Bokwus Slops",feet="Argute Loafers +2"}
+
+
+ sets.midcast_Cure = {main="Chatoyant Staff",head="Paean Mitra",neck="Phalaina Locket",
+ body="Anhur Robe",hands="Bokwus Gloves",back="Oretanis's Cape",legs="Nares Trews"}
+
+ sets.midcast_Helix = {main="Chatoyant Staff",sub="Wizzan Grip",ammo="Snow Sachet",
+ head="Nahtirah Hat",neck="Stoicheion Medal",ear1="Hecate's Earring",ear2="Novio Earring",
+ body="Nares Saio",hands="Nares Cuffs",ring1="Icesoul Ring",ring2="Icesoul Ring",
+ back="Twilight Cape",waist="Wanion Belt",legs="Akasha Chaps",feet="Nares Clogs"}
+
+ sets.midcast_Stoneskin = {main="Kirin's Pole",neck="Stone Gorget",waist="Siegel Sash",legs="Shedir Seraweels"}
+
+ sets.Obi = {}
+ sets.Obi.Fire = {waist='Karin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+ sets.Obi.Earth = {waist='Dorin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+ sets.Obi.Water = {waist='Suirin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+ sets.Obi.Wind = {waist='Furin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+ sets.Obi.Ice = {waist='Hyorin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+ sets.Obi.Thunder = {waist='Rairin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+ sets.Obi.Light = {waist='Korin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+ sets.Obi.Dark = {waist='Anrin Obi',back='Twilight Cape',lring='Zodiac Ring'}
+
+ sets.staves = {}
+
+ sets.staves.damage = {}
+ sets.staves.damage.Thunder = {main="Apamajas I"}
+ sets.staves.damage.Fire = {main="Atar I"}
+
+ sets.staves.accuracy = {}
+ sets.staves.damage.Thunder = {main="Apamajas II"}
+ sets.staves.damage.Ice = {main="Vourukasha II"}
+
+ stuntarg = 'Shantotto'
+
+
+end
+
+function precast(spell)
+ if spell.english == 'Impact' then
+ equip(sets['precast_FastCast'],{body="Twilight Cloak"})
+ if not buffactive['elemental seal'] then
+ add_to_chat(8,'--------- Elemental Seal is down ---------')
+ end
+
+ elseif spell.skill=='ElementalMagic' and spell.cast_time < 3 then
+ equip(sets.midcast_ElementalMagic)
+ if spell.element == 'Earth' then
+ equip({neck="Quanpur Necklace"})
+ end
+ if spell.element == world.weather_element or spell_element == world.day_element and sets.Obi[spell.element] then
+ equip(sets.Obi[spell.element])
+ end
+ elseif spell.english == 'Stun' then
+ equip(sets['precast_Stun'])
+ if not buffactive.thunderstorm then
+ add_to_chat(8,'--------- Thunderstorm is down ---------')
+ elseif not buffactive.klimaform then
+ add_to_chat(8,'----------- Klimaform is down -----------')
+ end
+ if stuntarg ~= 'Shantotto' then
+ send_command('@input /t '..stuntarg..' ---- Byrth Stunned!!! ---- ')
+ end
+ else
+ equip(sets['precast_FastCast'])
+ end
+
+ if (buffactive.alacrity or buffactive.celerity) and world.weather_element == spell.element then
+ equip({feet='Argute Loafers +2'})
+ end
+end
+
+function midcast(spell)
+ if string.find(spell.english,'Cur') then
+ equip(sets.midcast_Cure)
+ if spell.element == world.weather_element or spell_element == world.day_element then
+ equip({main="Chatoyant Staff"},sets.Obi[spell.element])
+ end
+ if buffactive.rapture then
+ equip({head="Savant's Bonnet +2"})
+ end
+ elseif spell.english == 'Impact' then
+ local tempset = sets['midcast_Impact']
+ tempset['body'] = 'Twilight Cloak'
+ tempset['head'] = empty
+ equip(tempset)
+ if spell.element == world.weather_element or spell_element == world.day_element then
+ equip(sets.Obi[spell.element])
+ end
+ if sets.staves.damage[spell.element] then
+ equip(sets.staves.damage[spell.element])
+ end
+ elseif spell.skill=="ElementalMagic" then
+ if string.find(spell.english,'helix') then
+ equip(sets['midcast_Helix'])
+ else
+ equip(sets.midcast_ElementalMagic)
+ if spell.element=='Earth' then
+ equip({neck="Quanpur Necklace"})
+ end
+ if spell.element == world.weather_element or spell_element == world.day_element then
+ equip(sets.Obi[spell.element])
+ end
+ end
+ if buffactive.ebullience then
+ equip({head="Savant's Bonnet +2"})
+ end
+ if buffactive.klimform then
+ equip ({feet="Savant's Loafers +2"})
+ end
+
+ elseif spell.english == 'Stoneskin' then
+ equip(sets['midcast_Stoneskin'])
+ elseif spell.skill == 'EnhancingMagic' then
+ if spell.english == 'Embrava' then
+ equip(sets['midcast_Embrava'])
+ if not buffactive.perpetuance then
+ add_to_chat(8,'--------- Perpetuance is down ---------')
+ end
+ if not buffactive.accession then
+ add_to_chat(8,'--------- Accession is down ---------')
+ end
+ if not buffactive.penury then
+ add_to_chat(8,'--------- Penury is down ---------')
+ end
+ end
+ if buffactive.perpetuance then
+ equip(sets['midcast_EnhancingMagic'],{hands="Savant's Bracers +2"})
+ else
+ equip(sets['midcast_EnhancingMagic'])
+ end
+ else
+ weathercheck(spell.element,sets['midcast_'..spell.skill])
+ end
+
+ if spell.english == 'Sneak' then
+ send_command('@wait 1.8;cancel 71;')
+ end
+end
+
+function aftercast(spell)
+ equip(sets['aftercast_Idle'])
+
+ if spell.english == 'Sleep' or spell.english == 'Sleepga' then
+ send_command('@wait 50;input /echo ------- '..spell.english..' is wearing off in 10 seconds -------')
+ elseif spell.english == 'Sleep II' or spell.english == 'Sleepga II' then
+ send_command('@wait 80;input /echo ------- '..spell.english..' is wearing off in 10 seconds -------')
+ elseif spell.english == 'Break' or spell.english == 'Breakga' then
+ send_command('@wait 20;input /echo ------- '..spell.english..' is wearing off in 10 seconds -------')
+ end
+end
+
+function status_change(new,tab)
+ if new == 'Resting' then
+ equip(sets['Resting'])
+ else
+ equip(sets['aftercast_Idle'])
+ end
+end
+
+function buff_change(status,gain_or_loss)
+ if status == 'Sublimation: Complete' and gain_or_loss and not 'stunmode' then -- True whether gained or lost
+ sets.aftercast_Idle = sets.aftercast_Idle_noSub
+ elseif status == 'Sublimation: Activated' and gain_or_loss and not 'stunmode' then
+ sets.aftercast_Idle = sets.aftercast_Idle_Sub
+ end
+ equip(sets.aftercast_Idle)
+end
+
+
+
+function self_command(command)
+ if command == 'stuntarg' then
+ stuntarg = target.name
+ elseif command == 'stunmode' then
+ windower.add_to_chat(100,'Stun Mode')
+ if sets.aftercast_Idle ~= sets.precast_Stun then
+ stunmode = true
+ sets.aftercast_Idle = sets.precast_Stun
+ elseif buffactive['Sublimation: Activated'] then
+ stunmode = false
+ sets.aftercast_Idle = sets.aftercast_Idle_Sub
+ else
+ stunmode = false
+ sets.aftercast_Idle = sets.aftercast_Idle_noSub
+ end
+ equip(sets.aftercast_Idle)
+ end
+end
+
+
+
+-- This function is user defined, but never called by GearSwap itself. It's just a user function that's only called from user functions. I wanted to check the weather and equip a weather-based set for some spells, so it made sense to make a function for it instead of replicating the conditional in multiple places.
+
+function weathercheck(spell_element,set)
+ if spell_element == world.weather_element or spell_element == world.day_element then
+ equip(set,sets['Obi_'..spell_element])
+ else
+ equip(set)
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Variables.xlsx b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Variables.xlsx
new file mode 100644
index 0000000..e5846c1
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/beta_examples_and_information/Variables.xlsx
Binary files differ
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/data/Instructions.txt b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/data/Instructions.txt
new file mode 100644
index 0000000..8ff2d45
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/data/Instructions.txt
@@ -0,0 +1,6 @@
+This is where user .lua files go. Check out the beta examples and instructions for more information.
+
+Be aware of four things:
+1) If Gearswap fails to execute a command and does not detect this, it will currently block all input until it is reloaded.
+2) verify_equip() is not very robust, and in Delve it tends to not be usable. cast_delay() is recommended instead.
+3) Logging is on at the moment, so that's what the log files are. You can turn it off towards the top of gearswap. \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua
new file mode 100644
index 0000000..bb6b861
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/equip_processing.lua
@@ -0,0 +1,292 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-----------------------------------------------------------------------------------
+--Name: check_wearable(item_id)
+--Args:
+---- item_id - Item ID to be examined
+-----------------------------------------------------------------------------------
+--Returns:
+---- boolean indicating whether the given piece of gear can be worn or not
+---- Checks for main job, level, superior level, and gender/race
+-----------------------------------------------------------------------------------
+function check_wearable(item_id)
+ if not item_id or item_id == 0 then -- 0 codes for an empty slot, but Arcon will probably make it nil at some point
+ elseif not res.items[item_id] then
+ msg.debugging("Item "..item_id.." has not been added to resources yet.")
+ elseif not res.items[item_id].jobs then -- Make sure item can be equipped by specific jobs (unlike pearlsacks).
+ --msg.debugging('GearSwap (Debug Mode): Item '..(res.items[item_id][language] or item_id)..' does not have a jobs field in the resources.')
+ elseif not res.items[item_id].slots then
+ -- Item is not equippable
+ else
+ return (res.items[item_id].jobs[player.main_job_id]) and (res.items[item_id].level<=player.jobs[res.jobs[player.main_job_id].ens]) and (res.items[item_id].races[player.race_id]) and
+ (player.superior_level >= (res.items[item_id].superior_level or 0))
+ end
+ return false
+end
+
+-----------------------------------------------------------------------------------
+--Name: name_match(item_id,name)
+--Args:
+---- item_id - Item ID to be compared
+---- name - Name to be compared
+-----------------------------------------------------------------------------------
+--Returns:
+---- boolean indicating whether the name matches the resources entry for the itemID
+-----------------------------------------------------------------------------------
+function name_match(item_id,name)
+ if res.items[item_id] then
+ return (res.items[item_id][language..'_log']:lower() == name:lower() or res.items[item_id][language]:lower() == name:lower())
+ else
+ return false
+ end
+end
+
+-----------------------------------------------------------------------------------
+--Name: expand_entry(v)
+--Args:
+---- entry - Table or string ostensibly from an equipment set
+-----------------------------------------------------------------------------------
+--Returns:
+---- name - Name of the current piece of equipment
+---- priority - Priority of the current piece as defined in the advanced table
+---- augments - Augments for the current piece as defined in the advanced table
+---- designated_bag - Bag for the current piece as defined in the advanced table
+-----------------------------------------------------------------------------------
+function expand_entry(entry)
+ if not entry then
+ return
+ end
+ local augments,name,priority,designated_bag
+ if type(entry) == 'table' and entry == empty then
+ name = empty
+ elseif type(entry) == 'table' and entry.name and type(entry.name) == 'string' then
+ name = entry.name
+ priority = entry.priority
+ if entry.augments then
+ augments = entry.augments
+ elseif entry.augment then
+ augments = {entry.augment}
+ end
+ if entry.bag and type(entry.bag) == 'string' then
+ designated_bag = bag_string_lookup[to_windower_bag_api(entry.bag)]
+ end
+ elseif type(entry) == 'string' and entry ~= '' then
+ name = entry
+ end
+ return name,priority,augments,designated_bag -- all nil if they don't exist
+end
+
+-----------------------------------------------------------------------------------
+--Name: unpack_equip_list(inventory,equip_list)
+--Args:
+---- inventory - Current inventory (potentially avoids a get_items() call)
+---- equip_list - Keys are standard slot names, values are item names.
+-----------------------------------------------------------------------------------
+--Returns:
+---- Table with keys that are slot numbers with values that are inventory slot #s.
+-----------------------------------------------------------------------------------
+function unpack_equip_list(equip_list,cur_equip)
+ local ret_list = {} -- Gear that is designated to be equipped
+ local used_list = {} -- Gear that is scheduled to be equipped but is already being worn
+ local error_list = {} -- Gear that cannot be equipped for whatever reason
+ local priorities = Priorities:new()
+ for slot_id,slot_name in pairs(default_slot_map) do
+ local name,priority,augments,designated_bag = expand_entry(equip_list[slot_name])
+ priorities[slot_id] = priority
+ if name == empty then
+ equip_list[slot_name] = nil
+ if cur_equip[slot_name].slot ~= empty then
+ ret_list[slot_id] = {bag_id=0,slot=empty}
+ end
+ elseif name and cur_equip[slot_name].slot ~= empty then
+ local item_tab = items[to_windower_bag_api(res.bags[cur_equip[slot_name].bag_id].en)][cur_equip[slot_name].slot]
+ if name_match(item_tab.id,name) and
+ (not augments or (#augments ~= 0 and extdata.compare_augments(augments,extdata.decode(item_tab).augments))) and
+ (not designated_bag or designated_bag == cur_equip[slot_name].bag_id) then
+ equip_list[slot_name] = nil
+ used_list[slot_id] = {bag_id=cur_equip[slot_name].bag_id,slot=cur_equip[slot_name].slot}
+ end
+ end
+ end
+
+ for _,bag in pairs(equippable_item_bags) do
+ for _,item_tab in ipairs(items[to_windower_bag_api(bag.en)]) do -- Iterate over the current bag
+ if type(item_tab) == 'table' and check_wearable(item_tab.id) then
+ if item_tab.status == 0 or item_tab.status == 5 then
+ for slot_id in res.items[item_tab.id].slots:it() do
+ local slot_name = default_slot_map[slot_id]
+ -- equip_list[slot_name] can also be a table (that doesn't contain a "name" property) or a number, which are both cases that should not generate any kind of equipment changing.
+ -- Hence the "and name" below.
+ if not ret_list[slot_id] and equip_list[slot_name] then -- If we haven't already found something for this slot and still want to equip something there
+ -- Make sure we're not already planning to equip this item in another slot.
+ if (slot_id == 0 and used_list[1] and used_list[1].bag_id == bag.id and used_list[1].slot == item_tab.slot) or -- main vs. sub
+ (slot_id == 1 and used_list[0] and used_list[0].bag_id == bag.id and used_list[0].slot == item_tab.slot) or -- sub vs. main
+ (slot_id == 11 and used_list[12] and used_list[12].bag_id == bag.id and used_list[12].slot == item_tab.slot) or --left_earring vs. right_earring
+ (slot_id == 12 and used_list[11] and used_list[11].bag_id == bag.id and used_list[11].slot == item_tab.slot) or --right_earring vs. left_earring
+ (slot_id == 13 and used_list[14] and used_list[14].bag_id == bag.id and used_list[14].slot == item_tab.slot) or --left_ring vs. right_ring
+ (slot_id == 14 and used_list[13] and used_list[13].bag_id == bag.id and used_list[13].slot == item_tab.slot) then --right_ring vs. left_ring
+ break
+ end
+ local name,priority,augments,designated_bag = expand_entry(equip_list[slot_name])
+
+ if (not designated_bag or designated_bag == bag.id) and name and name_match(item_tab.id,name) then
+ if augments and #augments ~= 0 then
+ if res.items[item_tab.id].flags.Rare or extdata.compare_augments(augments,extdata.decode(item_tab).augments) then
+ -- Check if the augments are right
+ -- If the item is Rare, then even if the augments are wrong try to equip it anyway because you only have one
+ equip_list[slot_name] = nil
+ ret_list[slot_id] = {bag_id=bag.id,slot=item_tab.slot}
+ used_list = ret_list[slot_id]
+ break
+ --else the piece specifies augments that don't match the current piece, so don't break and keep trying.
+ end
+ else
+ equip_list[slot_name] = nil
+ ret_list[slot_id] = {bag_id=bag.id,slot=item_tab.slot}
+ used_list = ret_list[slot_id]
+ break
+ end
+ end
+ end
+ end
+ else -- item_tab.status > 0
+ for slot_id in res.items[item_tab.id].slots:it() do
+ local slot_name = default_slot_map[slot_id]
+ local name = expand_entry(equip_list[slot_name])
+ if name and name ~= empty then -- If "name" isn't a piece of gear, then it won't have a valid value at this point and should be ignored.
+ if name_match(item_tab.id,name) then
+ if item_tab.status == 25 then
+ error_list[slot_name] = name..' (bazaared)'
+ else
+ error_list[slot_name] = name..' (status unknown: '..item_tab.status..' )'
+ end
+ break
+ end
+ end
+ end
+ end
+ else
+ for __,slot_name in pairs(default_slot_map) do
+ local name = expand_entry(equip_list[slot_name])
+ if name ~= empty and name_match(item_id,name) then
+ if not res.items[item_tab.id].jobs[player.main_job_id] then
+ equip_list[slot_name] = nil
+ error_list[slot_name] = name..' (cannot be worn by this job)'
+ elseif not (res.items[item_tab.id].level<=player.jobs[player.main_job]) then
+ equip_list[slot_name] = nil
+ error_list[slot_name] = name..' (job level is too low)'
+ elseif not res.items[item_tab.id].races[player.race_id] then
+ equip_list[slot_name] = nil
+ error_list[slot_name] = name..' (cannot be worn by your race)'
+ elseif not res.items[item_tab.id].slots then
+ equip_list[slot_name] = nil
+ error_list[slot_name] = name..' (cannot be worn)'
+ end
+ break
+ end
+ end
+ end
+ end
+ end
+
+ if _settings.debug_mode and table.length(error_list) > 0 then
+ print_set(error_list,'Debug Mode (error list)')
+ end
+ if _settings.debug_mode and table.length(equip_list) > 0 then
+ print_set(equip_list,'Debug Mode (gear not equipped)')
+ end
+
+ return ret_list,priorities
+end
+
+-----------------------------------------------------------------------------------
+--Name: to_names_set(equipment)
+--Args:
+---- equipment - Mapping of equipment slot ID or slot name to a table containing
+---- bag_id and inventory slot ID. If already indexed to a number, treat it as a slot index.
+---- Otherwise, damn the torpedoes and tostring it.
+-----------------------------------------------------------------------------------
+--Returns:
+---- Set with a mapping of slot name to equipment name.
+---- 'empty' is used as a replacement for the empty table.
+-----------------------------------------------------------------------------------
+function to_names_set(equipment)
+ local equip_package = {}
+
+ for ind,cur_item in pairs(equipment) do
+ local name = 'empty'
+ if type(cur_item) == 'table' and cur_item.slot ~= empty then
+ if items[to_bag_api(res.bags[cur_item.bag_id].english)][cur_item.slot].id == 0 then return {} end
+ -- refresh_player() can run after equip packets arrive but before the item array is fully loaded,
+ -- which results in the id still being the initialization value.
+ name = res.items[items[to_bag_api(res.bags[cur_item.bag_id].english)][cur_item.slot].id][language]
+ end
+
+ if tonumber(ind) and ind >= 0 and ind <= 15 and math.floor(ind) == ind then
+ equip_package[toslotname(ind)] = name
+ else
+ equip_package[tostring(ind)] = name
+ end
+ end
+
+ return equip_package
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: equip_piece(eq_slot_id,bag_id,inv_slot_id)
+--Desc: Cleans up the global table and leaves equip_sets properly.
+--Args:
+---- eq_slot_id - Equipment Slot ID
+---- bag_id - Bag ID of the item to be equipped
+---- inv_slot_id - Inventory Slot ID of the item to be equipped
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function equip_piece(eq_slot_id,bag_id,inv_slot_id)
+ -- Many complicated, wow!
+ local cur_eq_tab = items.equipment[toslotname(eq_slot_id)]
+
+ if cur_eq_tab.slot ~= empty then
+ items[to_bag_api(res.bags[cur_eq_tab.bag_id].english)][cur_eq_tab.slot].status = 0
+ -- This does not account for items like Onca Suit which take up multiple slots
+ end
+
+ if inv_slot_id ~= empty then
+ --items.equipment[toslotname(eq_slot_id)] = {slot=inv_slot_id,bag_id=bag_id}
+ items[to_bag_api(res.bags[bag_id].english)][inv_slot_id].status = 5
+ local minichunk = string.char(inv_slot_id,eq_slot_id,bag_id,0)
+ injected_equipment_registry[minichunk:byte(2)]:append(minichunk:sub(1,3))
+ return minichunk
+ else
+ --items.equipment[toslotname(eq_slot_id)] = {slot=empty,bag_id=0}
+ local minichunk = string.char(0,eq_slot_id,0,0)
+ injected_equipment_registry[minichunk:byte(2)]:append(minichunk:sub(1,3))
+ return minichunk
+ end
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/export.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/export.lua
new file mode 100644
index 0000000..454bab4
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/export.lua
@@ -0,0 +1,317 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+function export_set(options)
+ local item_list = T{}
+ local targinv,all_items,xml,all_sets,use_job_in_filename,use_subjob_in_filename,overwrite_existing,named_file
+ if #options > 0 then
+ for _,v in ipairs(options) do
+ if S{'inventory','inv','i'}:contains(v:lower()) then
+ targinv = true
+ elseif v:lower() == 'all' then
+ all_items = true
+ elseif v:lower() == 'wearable' then
+ wearable = true
+ elseif S{'xml'}:contains(v:lower()) then
+ xml = true
+ elseif S{'sets','set','s'}:contains(v:lower()) then
+ all_sets = true
+ if not user_env or not user_env.sets then
+ msg.addon_msg(123,'Cannot export the sets table of the current file because there is no file loaded.')
+ return
+ end
+ elseif v:lower() == 'mainjob' then
+ use_job_in_filename = true
+ elseif v:lower() == 'mainsubjob' then
+ use_subjob_in_filename = true
+ elseif v:lower() == 'overwrite' then
+ overwrite_existing = true
+ elseif S{'filename','file','f'}:contains(v:lower()) then
+ named_file = true
+ else
+ if named_file then
+ filename = v
+ end
+ end
+ end
+ end
+
+ local buildmsg = 'Exporting '
+ if all_items then
+ buildmsg = buildmsg..'all your items'
+ elseif wearable then
+ buildmsg = buildmsg..'all your items in inventory and wardrobes'
+ elseif targinv then
+ buildmsg = buildmsg..'your current inventory'
+ elseif all_sets then
+ buildmsg = buildmsg..'your current sets table'
+ else
+ buildmsg = buildmsg..'your currently equipped gear'
+ end
+
+ if xml then
+ buildmsg = buildmsg..' as an xml file.'
+ else
+ buildmsg = buildmsg..' as a lua file.'
+ end
+
+ if use_job_in_filename then
+ buildmsg = buildmsg..' (Naming format: Character_JOB)'
+ elseif use_subjob_in_filename then
+ buildmsg = buildmsg..' (Naming format: Character_JOB_SUB)'
+ elseif named_file then
+ buildmsg = buildmsg..' (Named: Character_'..filename..')'
+ end
+
+ if overwrite_existing then
+ buildmsg = buildmsg..' Will overwrite existing files with same name.'
+ end
+
+ msg.addon_msg(123,buildmsg)
+
+ if not windower.dir_exists(windower.addon_path..'data/export') then
+ windower.create_dir(windower.addon_path..'data/export')
+ end
+
+ if all_items then
+ for i = 0, #res.bags do
+ item_list:extend(get_item_list(items[res.bags[i].english:gsub(' ', ''):lower()]))
+ end
+ elseif wearable then
+ for _, v in pairs(equippable_item_bags) do
+ item_list:extend(get_item_list(items[v.english:gsub(' ', ''):lower()]))
+ end
+ elseif targinv then
+ item_list:extend(get_item_list(items.inventory))
+ elseif all_sets then
+ -- Iterate through user_env.sets and find all the gear.
+ item_list,exported = unpack_names({},'L1',user_env.sets,{},{empty=true})
+ else
+ -- Default to loading the currently worn gear.
+
+ for i = 1,16 do -- ipairs will be used on item_list
+ if not item_list[i] then
+ item_list[i] = {}
+ item_list[i].name = empty
+ item_list[i].slot = toslotname(i-1)
+ end
+ end
+
+ for slot_name,gs_item_tab in pairs(items.equipment) do
+ if gs_item_tab.slot ~= empty then
+ local item_tab
+ local bag_name = to_windower_bag_api(res.bags[gs_item_tab.bag_id].en)
+ if res.items[items[bag_name][gs_item_tab.slot].id] then
+ item_tab = items[bag_name][gs_item_tab.slot]
+ item_list[slot_map[slot_name]+1] = {
+ name = res.items[item_tab.id][language],
+ slot = slot_name
+ }
+ if not xml then
+ local augments = extdata.decode(item_tab).augments or {}
+ local aug_str = ''
+ for aug_ind,augment in pairs(augments) do
+ if augment ~= 'none' then aug_str = aug_str.."'"..augment:gsub("'","\\'").."'," end
+ end
+ if string.len(aug_str) > 0 then
+ item_list[slot_map[slot_name]+1].augments = aug_str
+ end
+ end
+ else
+ msg.addon_msg(123,'You are wearing an item that is not in the resources yet.')
+ end
+ end
+ end
+ end
+
+ if #item_list == 0 then
+ msg.addon_msg(123,'There is nothing to export.')
+ return
+ else
+ local not_empty
+ for i,v in pairs(item_list) do
+ if v.name ~= empty then
+ not_empty = true
+ break
+ end
+ end
+ if not not_empty then
+ msg.addon_msg(123,'There is nothing to export.')
+ return
+ end
+ end
+
+
+ if not windower.dir_exists(windower.addon_path..'data/export') then
+ windower.create_dir(windower.addon_path..'data/export')
+ end
+
+ local path = windower.addon_path..'data/export/'..player.name
+
+ if use_job_in_filename then
+ path = path..'_'..windower.ffxi.get_player().main_job
+ elseif use_subjob_in_filename then
+ path = path..'_'..windower.ffxi.get_player().main_job..'_'..windower.ffxi.get_player().sub_job
+ elseif named_file then
+ path = path..'_'..filename
+ else
+ path = path..os.date(' %Y-%m-%d %H-%M-%S')
+ end
+ if xml then
+ -- Export in .xml
+ if (not overwrite_existing) and windower.file_exists(path..'.xml') then
+ path = path..' '..os.clock()
+ end
+ local f = io.open(path..'.xml','w+')
+ f:write('<spellcast>\n <sets>\n <group name="exported">\n <set name="exported">\n')
+ for i,v in ipairs(item_list) do
+ if v.name ~= empty then
+ local slot = xmlify(tostring(v.slot))
+ local name = xmlify(tostring(v.name))
+ f:write(' <'..slot..'>'..name..'</'..slot..'>\n')
+ end
+ end
+ f:write(' </set>\n </group>\n </sets>\n</spellcast>')
+ f:close()
+ else
+ -- Default to exporting in .lua
+ if (not overwrite_existing) and windower.file_exists(path..'.lua') then
+ path = path..' '..os.clock()
+ end
+ local f = io.open(path..'.lua','w+')
+ f:write('sets.exported={\n')
+ for i,v in ipairs(item_list) do
+ if v.name ~= empty then
+ if v.augments then
+ --Advanced set table
+ f:write(' '..v.slot..'={ name="'..v.name..'", augments={'..v.augments..'}},\n')
+ else
+ f:write(' '..v.slot..'="'..v.name..'",\n')
+ end
+ end
+ end
+ f:write('}')
+ f:close()
+ end
+end
+
+function unpack_names(ret_tab,up,tab_level,unpacked_table,exported)
+ for i,v in pairs(tab_level) do
+ local flag,alt
+ if type(v)=='table' and i ~= 'augments' and not ret_tab[tostring(tab_level[i])] then
+ ret_tab[tostring(tab_level[i])] = true
+ unpacked_table,exported = unpack_names(ret_tab,i,v,unpacked_table,exported)
+ elseif i=='name' and type(v) == 'string' then
+ alt = up
+ flag = true
+ elseif type(v) == 'string' and v~='augment' and v~= 'augments' and v~= 'priority' then
+ alt = i
+ flag = true
+ end
+ if flag then
+ if not exported[v:lower()] then
+ unpacked_table[#unpacked_table+1] = {}
+ local tempname,tempslot = unlogify_unpacked_name(v)
+ unpacked_table[#unpacked_table].name = tempname
+ unpacked_table[#unpacked_table].slot = tempslot or alt
+ if tab_level.augments then
+ local aug_str = ''
+ for aug_ind,augment in pairs(tab_level.augments) do
+ if augment ~= 'none' then aug_str = aug_str.."'"..augment:gsub("'","\\'").."'," end
+ end
+ if aug_str ~= '' then unpacked_table[#unpacked_table].augments = aug_str end
+ end
+ if tab_level.augment then
+ local aug_str = unpacked_table[#unpacked_table].augments or ''
+ if tab_level.augment ~= 'none' then aug_str = aug_str.."'"..augment:gsub("'","\\'").."'," end
+ if aug_str ~= '' then unpacked_table[#unpacked_table].augments = aug_str end
+ end
+ exported[tempname:lower()] = true
+ exported[v:lower()] = true
+ end
+ end
+ end
+ return unpacked_table,exported
+end
+
+function unlogify_unpacked_name(name)
+ local slot
+ name = name:lower()
+ for i,v in pairs(res.items) do
+ if type(v) == 'table' then
+ if v[language..'_log']:lower() == name then
+ name = v[language]
+ local potslots = v.slots
+ if potslots then potslots = to_windower_api(res.slots[potslots:it()()].english) end
+ slot = potslots or 'item'
+ break
+ elseif v[language]:lower() == name then
+ name = v[language]
+ local potslots = v.slots
+ if potslots then potslots = to_windower_api(res.slots[potslots:it()()].english) end
+ slot = potslots or 'item'
+ break
+ end
+ end
+ end
+ return name,slot
+end
+
+function xmlify(phrase)
+ if tonumber(phrase:sub(1,1)) then phrase = 'NUM'..phrase end
+ return phrase --:gsub('"','&quot;'):gsub("'","&apos;"):gsub('<','&lt;'):gsub('>','&gt;'):gsub('&&','&amp;')
+end
+
+function get_item_list(bag)
+ local items_in_bag = {}
+ -- Load the entire inventory
+ for _,v in pairs(bag) do
+ if type(v) == 'table' and v.id ~= 0 then
+ if res.items[v.id] then
+ items_in_bag[#items_in_bag+1] = {}
+ items_in_bag[#items_in_bag].name = res.items[v.id][language]
+ local potslots,slot = copy_entry(res.items[v.id].slots)
+ if potslots then
+ slot = res.slots[potslots:it()()].english:gsub(' ','_'):lower() -- Multi-lingual support requires that we add more languages to slots.lua
+ end
+ items_in_bag[#items_in_bag].slot = slot or 'item'
+ if not xml then
+ local augments = extdata.decode(v).augments or {}
+ local aug_str = ''
+ for aug_ind,augment in pairs(augments) do
+ if augment ~= 'none' then aug_str = aug_str.."'"..augment:gsub("'","\\'").."'," end
+ end
+ if string.len(aug_str) > 0 then
+ items_in_bag[#items_in_bag].augments = aug_str
+ end
+ end
+ else
+ msg.addon_msg(123,'You possess an item that is not in the resources yet.')
+ end
+ end
+ end
+ return items_in_bag
+end
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/flow.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/flow.lua
new file mode 100644
index 0000000..ef5a1ee
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/flow.lua
@@ -0,0 +1,442 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+-----------------------------------------------------------------------------------
+--Name: equip_sets(swap_type,ts,val1,val2)
+--Desc: General purpose equipment pipeline / user function caller.
+--Args:
+---- swap_type - Determines equip_sets' behavior in terms of which user function it
+-- attempts to call
+---- ts - index of command_registry or nil for pretarget/commands
+---- val1 - First argument to be passed to the user function
+---- val2 - Second argument to be passed to the user function
+-----------------------------------------------------------------------------------
+--Return (varies by swap type):
+---- pretarget : empty string to blank packet or full string
+---- Everything else : nil
+-----------------------------------------------------------------------------------
+function equip_sets(swap_type,ts,...)
+ local results
+ local var_inps = {...}
+ local val1 = var_inps[1]
+ local val2 = var_inps[2]
+ table.reassign(_global,command_registry[ts] or {pretarget_cast_delay = 0,precast_cast_delay=0,cancel_spell = false, new_target=false,target_arrow={x=0,y=0,z=0}})
+ _global.current_event = tostring(swap_type)
+
+ if _global.current_event == 'precast' and val1 and val1.english and val1.english:find('Geo-') then
+ _global.target_arrow = initialize_arrow_offset(val1.target)
+ end
+
+ windower.debug(tostring(swap_type)..' enter')
+ if showphase or debugging.general then msg.debugging(8,windower.to_shift_jis(tostring(swap_type))..' enter') end
+
+ local cur_equip = table.reassign({},update_equipment())
+
+ table.reassign(equip_list,{})
+ table.reassign(player.equipment,to_names_set(cur_equip))
+ for i,v in pairs(slot_map) do
+ if not player.equipment[i] then
+ player.equipment[i] = player.equipment[toslotname(v)]
+ end
+ end
+
+ logit('\n\n'..tostring(os.clock)..'(15) equip_sets: '..tostring(swap_type))
+ if val1 then
+ if type(val1) == 'table' and val1.english then
+ logit(' : '..val1.english)
+ else
+ logit(' : Unknown type val1- '..tostring(val1))
+ end
+ else
+ logit(' : nil-or-false')
+ end
+ if val2 then
+ if type(val2) == 'table' and val2.type then logit(' : '..val2.type)
+ else
+ logit(' : Unknown type val2- '..tostring(val2))
+ end
+ else
+ logit(' : nil-or-false')
+ end
+
+ if type(swap_type) == 'string' then
+ msg.debugging("Entering "..swap_type)
+ else
+ msg.debugging("Entering User Event "..tostring(swap_type))
+ end
+
+ if not val1 then val1 = {}
+ if debugging.general then
+ msg.debugging(8,'val1 error')
+ end
+ end
+
+
+ if type(swap_type) == 'function' then
+ results = { pcall(swap_type,...) }
+ if not table.remove(results,1) then error('\nUser Event Error: '..results[1]) end
+ elseif swap_type == 'equip_command' then
+ equip(val1)
+ else
+ user_pcall(swap_type,...)
+ end
+
+--[[ local c
+ if type(swap_type) == 'function' then
+ c = coroutine.create(swap_type)
+ elseif swap_type == 'equip_command' then
+ equip(val1)
+ elseif type(swap_type) == 'string' and user_env[swap_type] and type(user_env[swap_type]) == 'function' then
+ c = coroutine.create(user_env[swap_type])
+ elseif type(swap_type) == 'string' and user_env[swap_type] then
+ msg.addon_msg(123,windower.to_shift_jis(tostring(str))..'() exists but is not a function')
+ end
+
+ if c then
+ while coroutine.status(c) == 'suspended' do
+ local err, typ, val = coroutine.resume(c,unpack(var_inputs))
+ if not err then
+ error('\nGearSwap has detected an error in the user function '..tostring(swap_type)..':\n'..typ)
+ elseif typ then
+ if typ == 'sleep' and type(val) == 'number' and val >= 0 then
+ -- coroutine slept
+ err, typ, val = coroutine.schedule(c,val)
+ else
+ -- Someone yielded or slept with a nonsensical argument.
+ err, typ, val = coroutine.resume(c)
+ end
+ else
+ -- coroutine finished
+ end
+ end
+ end]]
+
+
+ if type(swap_type) == 'string' and (swap_type == 'pretarget' or swap_type == 'filtered_action') then -- Target may just have been changed, so make the ind now.
+ ts = command_registry:new_entry(val1)
+-- elseif type(swap_type) == 'string' and swap_type == 'precast' and not command_registry[ts] and debugging.command_registry then
+-- print_set(spell,'precast nil error') -- spell's scope changed to local
+ end
+
+ if player.race ~= 'Precomposed NPC' then
+ -- Short circuits the routine and gets out before equip processing
+ -- if there's no swapping to be done because the user is a monster.
+
+ for v,i in pairs(default_slot_map) do
+ if equip_list[i] and encumbrance_table[v] then
+ not_sent_out_equip[i] = equip_list[i]
+ equip_list[i] = nil
+ msg.debugging(i..' slot was not equipped because you are encumbered.')
+ end
+ end
+
+ table.update(equip_list_history,equip_list)
+
+ -- Attempts to identify the player-specified item in inventory
+ -- Starts with (i=slot name, v=item name)
+ -- Ends with (i=slot id and v={bag_id=bag_id, slot=inventory slot}).
+ local equip_next,priorities = unpack_equip_list(equip_list,cur_equip)
+
+ if (_settings.show_swaps and table.length(equip_next) > 0) or _settings.demo_mode then --and table.length(equip_next)>0 then
+ local tempset = to_names_set(equip_next)
+ print_set(tempset,tostring(swap_type))
+ end
+
+ if (buffactive.charm or player.charmed) or (player.status == 2 or player.status == 3) then -- dead or engaged dead statuses
+ local failure_reason
+ if (buffactive.charm or player.charmed) then
+ failure_reason = 'Charmed'
+ elseif player.status == 2 or player.status == 3 then
+ failure_reason = 'KOed'
+ end
+ msg.debugging("Cannot change gear right now: "..tostring(failure_reason))
+ logit('\n\n'..tostring(os.clock)..'(69) failure_reason: '..tostring(failure_reason))
+ else
+ local chunk_table = L{}
+ for eq_slot_id,priority in priorities:it() do
+ if equip_next[eq_slot_id] and not encumbrance_table[eq_slot_id] and not _settings.demo_mode then
+ local minichunk = equip_piece(eq_slot_id,equip_next[eq_slot_id].bag_id,equip_next[eq_slot_id].slot)
+ chunk_table:append(minichunk)
+ end
+ end
+
+ if swap_type == 'midcast' and command_registry[ts] and command_registry[ts].proposed_packet and not _settings.demo_mode then
+ windower.packets.inject_outgoing(command_registry[ts].proposed_packet:byte(1),command_registry[ts].proposed_packet)
+ end
+
+ if chunk_table.n >= 3 then
+ local big_chunk = string.char(0x51,0x24,0,0,chunk_table.n,0,0,0)
+ for i=1,chunk_table.n do
+ big_chunk = big_chunk..chunk_table[i]
+ end
+ while string.len(big_chunk) < 0x48 do big_chunk = big_chunk..string.char(0) end
+ windower.packets.inject_outgoing(0x51,big_chunk)
+ elseif chunk_table.n > 0 then
+ for i=1,chunk_table.n do
+ local chunk = string.char(0x50,4,0,0)..chunk_table[i]
+ windower.packets.inject_outgoing(0x50,chunk)
+ end
+ end
+ end
+ else
+ if swap_type == 'midcast' and command_registry[ts] and command_registry[ts].proposed_packet and not _settings.demo_mode then
+ windower.packets.inject_outgoing(command_registry[ts].proposed_packet:byte(1),command_registry[ts].proposed_packet)
+ end
+ end
+
+ windower.debug(tostring(swap_type)..' exit')
+
+ if type(swap_type) == 'function' then
+ return unpack(results)
+ end
+
+ return equip_sets_exit(swap_type,ts,val1)
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: equip_sets_exit(swap_type,ind,val1)
+--Desc: Cleans up the global table and leaves equip_sets properly.
+--Args:
+---- swap_type - Current swap type for equip_sets
+---- ts - Current index of command_registry
+---- val1 - First argument of equip_sets
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function equip_sets_exit(swap_type,ts,val1)
+ if command_registry[ts] then
+ table.update(command_registry[ts],_global)
+ end
+ if type(swap_type) == 'string' then
+ if swap_type == 'pretarget' then
+
+ if command_registry[ts].cancel_spell then
+ msg.debugging("Action canceled ("..storedcommand..' '..val1.target.raw..")")
+ storedcommand = nil
+ command_registry:delete_entry(ts)
+ return true
+ elseif not ts or not command_registry[ts] or not storedcommand then
+ msg.debugging('This case should not be hittable - 1')
+ return true
+ end
+
+ if command_registry[ts].new_target then
+ val1.target = command_registry[ts].new_target -- Switch target, if it is requested.
+ end
+
+ -- Compose a proposed packet for the given action (this should be possible after pretarget)
+ command_registry[ts].spell = val1
+ if val1.target and val1.target.id and val1.target.index and val1.prefix and unify_prefix[val1.prefix] then
+ if val1.prefix == '/item' then
+ -- Item use packet handling here
+ if bit.band(val1.target.spawn_type, 2) == 2 and find_inventory_item(val1.id) then
+ -- 0x36 packet
+ if val1.target.distance <= 6 then
+ command_registry[ts].proposed_packet = assemble_menu_item_packet(val1.target.id,val1.target.index,val1.id)
+ else
+ windower.add_to_chat(67, "Target out of range.")
+ end
+ elseif find_usable_item(val1.id) then
+ -- 0x37 packet
+ command_registry[ts].proposed_packet = assemble_use_item_packet(val1.target.id,val1.target.index,val1.id)
+ end
+ if not command_registry[ts].proposed_packet then
+ command_registry:delete_entry(ts)
+ end
+ elseif outgoing_action_category_table[unify_prefix[val1.prefix]] then
+ if filter_precast(val1) then
+ command_registry[ts].proposed_packet = assemble_action_packet(val1.target.id,val1.target.index,outgoing_action_category_table[unify_prefix[val1.prefix]],val1.id,command_registry[ts].target_arrow)
+ if not command_registry[ts].proposed_packet then
+ command_registry:delete_entry(ts)
+
+ msg.debugging("Unable to create a packet for this command because the target is still invalid after pretarget ("..storedcommand..' '..val1.target.raw..")")
+ storedcommand = nil
+ return storedcommand..' '..val1.target.raw
+ end
+ end
+ else
+ msg.debugging(8,"Hark, what weird prefix through yonder window breaks? "..tostring(val1.prefix))
+ end
+ end
+
+ if ts and command_registry[ts] and val1.target then
+ if st_targs[val1.target.raw] then
+ -- st targets
+ st_flag = true
+ elseif not val1.target.name then
+ -- Spells with invalid pass_through_targs, like using <t> without a target
+ command_registry:delete_entry(ts)
+ msg.debugging("Change target was used to pick an invalid target ("..storedcommand..' '..val1.target.raw..")")
+ local ret = storedcommand..' '..val1.target.raw
+ storedcommand = nil
+ return ret
+ else
+ -- Spells with complete target information
+ -- command_registry[ts] is deleted for cancelled spells
+ if command_registry[ts].pretarget_cast_delay == 0 then
+ equip_sets('precast',ts,val1)
+ else
+ windower.send_command('@wait '..command_registry[ts].pretarget_cast_delay..';lua i '.._addon.name..' pretarget_delayed_cast '..ts)
+ end
+ return true
+ end
+ elseif not ts or not command_registry[ts] then
+ msg.debugging('This case should not be hittable - 2')
+ return true
+ end
+
+ elseif swap_type == 'precast' then
+ -- Update the target_arrow
+ if val1.prefix ~= '/item' then
+ command_registry[ts].proposed_packet = assemble_action_packet(val1.target.id,val1.target.index,outgoing_action_category_table[unify_prefix[val1.prefix]],val1.id,command_registry[ts].target_arrow)
+ end
+ return precast_send_check(ts)
+ elseif swap_type == 'filtered_action' and command_registry[ts] and command_registry[ts].cancel_spell then
+ storedcommand = nil
+ command_registry:delete_entry(ts)
+ return true
+ elseif swap_type == 'midcast' and _settings.demo_mode then
+ command_registry[ts].midaction = false
+ equip_sets('aftercast',ts,val1)
+ elseif swap_type == 'aftercast' then
+ if ts then
+ command_registry:delete_entry(ts)
+ end
+ elseif swap_type == 'pet_aftercast' then
+ if ts then
+ command_registry:delete_entry(ts)
+ end
+ end
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: user_pcall(str,val1,val2,exit_funct)
+--Desc: Calls a user function, if it exists. If not, throws an error.
+--Args:
+---- str - Function's key in user_env.
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function user_pcall(str,...)
+ if user_env then
+ if type(user_env[str]) == 'function' then
+ bool,err = pcall(user_env[str],...)
+ if not bool then error('\nGearSwap has detected an error in the user function '..str..':\n'..err) end
+ elseif user_env[str] then
+ msg.addon_msg(123,windower.to_shift_jis(tostring(str))..'() exists but is not a function')
+ end
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: pretarget_delayed_cast(ts)
+--Desc: Triggers an outgoing action packet (if the passed key is valid).
+--Args:
+---- ts - Timestamp argument to precast_delayed_cast
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function pretarget_delayed_cast(ts)
+ ts = tonumber(ts)
+ if ts then
+ equip_sets('precast',ts,command_registry[ts].spell)
+ else
+ msg.debugging("Bad index passed to pretarget_delayed_cast")
+ end
+end
+
+
+
+-----------------------------------------------------------------------------------
+--Name: precast_send_check(ts)
+--Desc: Determines whether or not to send the current packet.
+-- Cancels if _global.cancel_spell is true
+-- If command_registry[ts].precast_cast_delay is not 0, cues precast_delayed_cast with the proper
+-- delay instead of sending immediately.
+--Args:
+---- ts - key of command_registry
+-----------------------------------------------------------------------------------
+--Returns:
+---- true (to block) or the outgoing packet
+-----------------------------------------------------------------------------------
+function precast_send_check(ts)
+ if ts and command_registry[ts] then
+ if command_registry[ts].cancel_spell then
+ command_registry:delete_entry(ts)
+ else
+ if command_registry[ts].precast_cast_delay == 0 then
+ send_action(ts)
+ return
+ else
+ windower.send_command('@wait '..command_registry[ts].precast_cast_delay..';lua i '.._addon.name..' precast_delayed_cast '..ts)
+ end
+ end
+ end
+ return true
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: precast_delayed_cast(ts)
+--Desc: Triggers an outgoing action packet (if the passed key is valid).
+--Args:
+---- ts - Timestamp argument to precast_delayed_cast
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function precast_delayed_cast(ts)
+ ts = tonumber(ts)
+ if ts then
+ send_action(ts)
+ else
+ msg.debugging("Bad index passed to precast_delayed_cast")
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: send_action(ts)
+--Desc: Sends the cued action packet, if it exists.
+--Args:
+---- ts - index for a command_registry entry that includes an action packet (hopefully)
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function send_action(ts)
+ command_registry[ts].midaction = true
+ equip_sets('midcast',ts,command_registry[ts].spell)
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/gearswap.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/gearswap.lua
new file mode 100644
index 0000000..5168406
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/gearswap.lua
@@ -0,0 +1,333 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+_addon.name = 'GearSwap'
+_addon.version = '0.937'
+_addon.author = 'Byrth'
+_addon.commands = {'gs','gearswap'}
+
+if windower.file_exists(windower.addon_path..'data/bootstrap.lua') then
+ debugging = {windower_debug = true,command_registry = false,general=false,logging=false}
+else
+ debugging = {}
+end
+
+__raw = {lower = string.lower, upper = string.upper, debug=windower.debug,text={create=windower.text.create,
+ delete=windower.text.delete,registry = {}},prim={create=windower.prim.create,delete=windower.prim.delete,registry={}}}
+
+
+language = 'english'
+file = require 'files'
+require 'strings'
+require 'tables'
+require 'logger'
+-- Restore the normal error function (logger changes it)
+error = _raw.error
+
+require 'lists'
+require 'sets'
+
+
+windower.text.create = function (str)
+ if __raw.text.registry[str] then
+ msg.addon_msg(123,'Text object cannot be created because it already exists.')
+ else
+ __raw.text.registry[str] = true
+ __raw.text.create(str)
+ end
+end
+
+windower.text.delete = function (str)
+ if __raw.text.registry[str] then
+ local library = false
+ if windower.text.saved_texts then
+ for i,v in pairs(windower.text.saved_texts) do
+ if v._name == str then
+ __raw.text.registry[str] = nil
+ windower.text.saved_texts[i]:destroy()
+ library = true
+ break
+ end
+ end
+ end
+ if not library then
+ -- Text was not created through the library, so delete it normally
+ __raw.text.registry[str] = nil
+ __raw.text.delete(str)
+ end
+ else
+ __raw.text.delete(str)
+ end
+end
+
+windower.prim.create = function (str)
+ if __raw.prim.registry[str] then
+ msg.addon_msg(123,'Primitive cannot be created because it already exists.')
+ else
+ __raw.prim.registry[str] = true
+ __raw.prim.create(str)
+ end
+end
+
+windower.prim.delete = function (str)
+ if __raw.prim.registry[str] then
+ __raw.prim.registry[str] = nil
+ __raw.prim.delete(str)
+ else
+ __raw.prim.delete(str)
+ end
+end
+
+texts = require 'texts'
+require 'pack'
+bit = require 'bit'
+socket = require 'socket'
+mime = require 'mime'
+res = require 'resources'
+extdata = require 'extdata'
+require 'helper_functions'
+require 'actions'
+packets = require 'packets'
+
+-- Resources Checks
+if res.items and res.bags and res.slots and res.statuses and res.jobs and res.elements and res.skills and res.buffs and res.spells and res.job_abilities and res.weapon_skills and res.monster_skills and res.action_messages and res.skills and res.monstrosity and res.weather and res.moon_phases and res.races then
+else
+ error('Missing resources!')
+end
+
+require 'packet_parsing'
+require 'statics'
+require 'equip_processing'
+require 'targets'
+require 'user_functions'
+require 'refresh'
+require 'export'
+require 'validate'
+require 'flow'
+require 'triggers'
+
+initialize_packet_parsing()
+
+windower.register_event('load',function()
+ windower.debug('load')
+ refresh_globals()
+
+ if world.logged_in then
+ refresh_user_env()
+ if debugging.general then windower.send_command('@unload spellcast;') end
+ end
+end)
+
+windower.register_event('unload',function ()
+ windower.debug('unload')
+ user_pcall('file_unload')
+ if logging then logfile:close() end
+end)
+
+windower.register_event('addon command',function (...)
+ windower.debug('addon command')
+ logit('\n\n'..tostring(os.clock)..table.concat({...},' '))
+ local splitup = {...}
+ if not splitup[1] then return end -- handles //gs
+
+ for i,v in pairs(splitup) do splitup[i] = windower.from_shift_jis(windower.convert_auto_trans(v)) end
+
+ local cmd = table.remove(splitup,1):lower()
+
+ if cmd == 'c' then
+ if gearswap_disabled then return end
+ if splitup[1] then
+ refresh_globals()
+ equip_sets('self_command',nil,_raw.table.concat(splitup,' '))
+ else
+ msg.addon_msg(123,'No self command passed.')
+ end
+ elseif cmd == 'equip' then
+ if gearswap_disabled then return end
+ local key_list = parse_set_to_keys(splitup)
+ local set = get_set_from_keys(key_list)
+ if set then
+ refresh_globals()
+ equip_sets('equip_command',nil,set)
+ else
+ msg.addon_msg(123,'Equip command cannot be completed. That set does not exist.')
+ end
+ elseif cmd == 'export' then
+ export_set(splitup)
+ elseif cmd == 'validate' then
+ if user_env and user_env.sets then
+ refresh_globals()
+ validate(splitup)
+ else
+ msg.addon_msg(123,'There is nothing to validate because there is no file loaded.')
+ end
+ elseif cmd == 'l' or cmd == 'load' then
+ if splitup[1] then
+ local f_name = table.concat(splitup,' ')
+ if f_name:sub(-4):lower() ~= '.lua' then
+ f_name = f_name..'.lua'
+ end
+ if pathsearch({f_name}) then
+ refresh_globals()
+ command_registry = Command_Registry.new()
+ load_user_files(false,f_name)
+ else
+ msg.addon_msg(123,'File not found.')
+ end
+ else
+ msg.addon_msg(123,'No file name was provided.')
+ end
+ elseif cmd == 'enable' then
+ disenable(splitup,command_enable,'enable',false)
+ elseif cmd == 'disable' then
+ disenable(splitup,disable,'disable',true)
+ elseif cmd == 'reload' or cmd == 'r' then
+ refresh_user_env()
+ elseif strip(cmd) == 'debugmode' then
+ _settings.debug_mode = not _settings.debug_mode
+ print('GearSwap: Debug Mode set to '..tostring(_settings.debug_mode)..'.')
+ elseif strip(cmd) == 'demomode' then
+ _settings.demo_mode = not _settings.demo_mode
+ print('GearSwap: Demo Mode set to '..tostring(_settings.demo_mode)..'.')
+ elseif strip(cmd) == 'showswaps' then
+ _settings.show_swaps = not _settings.show_swaps
+ print('GearSwap: Show Swaps set to '..tostring(_settings.show_swaps)..'.')
+ elseif strip(cmd) == 'help' then
+ print('GearSwap: Valid commands are:')
+ print(' c <string> : passes the string to the user\'s self_command function.')
+ print(' equip <string> : attempts to equip the set indicated by the string.')
+ print(' debugmode : toggles debugmode on or off.')
+ print(' demomode : toggles demomode on or off.')
+ print(' showswaps : toggles whether gearswap displays equipment changes in the chat log.')
+ print(' load <string> : attempts to load the user file indicated by the string.')
+ print(' reload : reloads the current user file.')
+ print(' export <opts> : Exports your item collections based on the passed options.')
+ print(' disable <slot> : Disables equip commands targeting a specified slot.')
+ print(' validate <opts> : Checks your current inventory against your item collections (or vice versa).')
+ print(' Please see the gearswap/README.md file for more details.')
+ elseif _settings.debug_mode and strip(cmd) == 'eval' then
+ assert(loadstring(table.concat(splitup,' ')))()
+ else
+ local handled = false
+ if not gearswap_disabled then
+ for i,v in ipairs(unhandled_command_events) do
+ handled = equip_sets(v,nil,cmd,unpack(splitup))
+ if handled then break end
+ end
+ end
+ if not handled then
+ print('GearSwap: Command not found')
+ end
+ end
+end)
+
+function disenable(tab,funct,functname,pol)
+ local slot_name = ''
+ local ltab = L{}
+ for i,v in pairs(tab) do
+ ltab:append(v:gsub('[^%a_%d]',''):lower())
+ end
+ if ltab:contains('all') then
+ funct('main','sub','range','ammo','head','neck','lear','rear','body','hands','lring','rring','back','waist','legs','feet')
+ print('GearSwap: All slots '..functname..'d.')
+ elseif ltab.n > 0 then
+ local found = L{}
+ local not_found = L{}
+ for slot_name in ltab:it() do
+ if slot_map[slot_name] then
+ funct(slot_name)
+ found:append(slot_name)
+ else
+ not_found:append(slot_name)
+ end
+ end
+ if found.n > 0 then
+ print('GearSwap: '..found:tostring()..' slot'..(found.n>1 and 's' or '')..' '..functname..'d.')
+ end
+ if not_found.n > 0 then
+ print('GearSwap: Unable to find slot'..(not_found.n>1 and 's' or '')..' '..not_found:tostring()..'.')
+ end
+ elseif gearswap_disabled ~= pol and not tab[2] then
+ print('GearSwap: User file '..functname..'d')
+ gearswap_disabled = pol
+ end
+end
+
+function incoming_chunk(id,data,modified,injected,blocked)
+ windower.debug('incoming chunk '..id)
+
+ if next_packet_events and next_packet_events.sequence_id ~= data:unpack('H',3) then
+ if not next_packet_events.globals_update or next_packet_events.globals_update ~= data:unpack('H',3) then
+ refresh_globals()
+ next_packet_events.globals_update = data:unpack('H',3)
+ end
+ if next_packet_events.pet_status_change and not gearswap_disabled then
+ equip_sets('pet_status_change',nil,next_packet_events.pet_status_change.newstatus,next_packet_events.pet_status_change.oldstatus)
+ next_packet_events.pet_status_change = nil
+ end
+ if next_packet_events.pet_change then
+ if next_packet_events.pet_change.pet and not gearswap_disabled then -- Losing a pet
+ equip_sets('pet_change',nil,next_packet_events.pet_change.pet,false)
+ next_packet_events.pet_change = nil
+ elseif pet.isvalid and not gearswap_disabled then -- Gaining a pet
+ equip_sets('pet_change',nil,pet,true)
+ next_packet_events.pet_change = nil
+ end
+ end
+ if not next_packet_events.pet_status_change and not next_packet_events.pet_change then
+ next_packet_events = nil
+ end
+ end
+
+ if not injected and parse.i[id] then
+ parse.i[id](data,blocked)
+ end
+end
+
+function outgoing_chunk(id,original,data,injected,blocked)
+ windower.debug('outgoing chunk '..id)
+
+ if not blocked and parse.o[id] then
+ parse.o[id](data,injected)
+ end
+end
+
+windower.register_event('incoming chunk',incoming_chunk)
+windower.register_event('outgoing chunk',outgoing_chunk)
+
+windower.register_event('status change',function(new,old)
+ windower.debug('status change '..new)
+ if gearswap_disabled or T{2,3,4}:contains(old) or T{2,3,4}:contains(new) then return end
+
+ refresh_globals()
+ equip_sets('status_change',nil,res.statuses[new].english,res.statuses[old].english)
+end)
+
+windower.register_event('login',function(name)
+ windower.debug('login '..name)
+ initialize_globals()
+ windower.send_command('@wait 2;lua i gearswap refresh_user_env;')
+end)
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/helper_functions.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/helper_functions.lua
new file mode 100644
index 0000000..0034d60
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/helper_functions.lua
@@ -0,0 +1,1255 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-----------------------------------------------------------------------------------
+--Name: string.lower()
+--Args:
+---- message (string): Message to be forced to lower case
+-----------------------------------------------------------------------------------
+--Returns:
+---- Lower case message (or not, if the language or message is invalid)
+-----------------------------------------------------------------------------------
+function string.lower(message)
+ if message and type(message) == 'string' and language == 'english' then
+ return __raw.lower(message)
+ elseif message and type(message) == 'string' then
+ return message:gsub('[A-Z]',function (letter) return string.char(letter:byte(1)+32) end)
+ else
+ return message
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: string.upper()
+--Args:
+---- message (string): Message to be forced to upper case
+-----------------------------------------------------------------------------------
+--Returns:
+---- Upper case message (or not, if the language or message is invalid)
+-----------------------------------------------------------------------------------
+function string.upper(message)
+ if message and type(message) == 'string' and language == 'english' then
+ return __raw.upper(message)
+ elseif message and type(message) == 'string' then
+ return message:gsub('[a-z]',function (letter) return string.char(letter:byte(1)-32) end)
+ else
+ return message
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: fieldsearch()
+--Args:
+---- message (string): Message to be searched
+-----------------------------------------------------------------------------------
+--Returns:
+---- Table of strings that contained {something}.
+---- Seems to be trying to exclude ${actor} and ${target}, but not.
+-----------------------------------------------------------------------------------
+function fieldsearch(message)
+ local fields = T{}
+ string.gsub(message,"{(.-)}", function(a) if a ~= '${actor}' and a ~= '${target}' then fields:append(a) end end)
+ return fields
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: strip()
+--Args:
+---- name (string): Name to be slugged
+-----------------------------------------------------------------------------------
+--Returns:
+---- string with a gsubbed version of name that converts numbers to Roman numerals
+-------- removes non-letter/numbers, and forces it to lower case.
+-----------------------------------------------------------------------------------
+function strip(name)
+ return name:gsub('4','iv'):gsub('9','ix'):gsub('0','p'):gsub('3','iii'):gsub('2','ii'):gsub('1','i'):gsub('8','viii'):gsub('7','vii'):gsub('6','vi'):gsub('5','v'):gsub('[^%a]',''):lower()
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: user_key_filter()
+--Args:
+---- val (key): potential key to be modified
+-----------------------------------------------------------------------------------
+--Returns:
+---- Filtered key
+-----------------------------------------------------------------------------------
+function user_key_filter(val)
+ return type(val) == 'string' and string.lower(val) or val
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: make_user_table()
+--Args:
+---- None
+-----------------------------------------------------------------------------------
+--Returns:
+---- Table with case-insensitive keys
+-----------------------------------------------------------------------------------
+function make_user_table()
+ return setmetatable({}, user_data_table)
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: unify_slots(g)
+-- Filters the provided gear table to only known slots, and then runs a map
+-- on the table to make sure all keys are the accepted versions for each.
+----Args:
+-- g - A dictionary table containing a gear set.
+-----------------------------------------------------------------------------------
+----Returns:
+-- A table simplified to only acceptable slots.
+-----------------------------------------------------------------------------------
+function unify_slots(g)
+ local g1 = table.key_filter(g, is_slot_key)
+ return table.key_map(g1, get_default_slot)
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: is_slot_key(k)
+-- Checks to see if key 'k' is known in the slot_map array, and that slot has not
+-- been disabled.
+----Args:
+-- k - A key to a gear slot in a gear table.
+-----------------------------------------------------------------------------------
+----Returns:
+-- True if the key is recognized in the slot_map table, and that slot is enabled;
+-- otherwise false.
+-----------------------------------------------------------------------------------
+function is_slot_key(k)
+ return slot_map[k]
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: make_empty_item_table(slot)
+-- Make an empty item table with slot = slot
+----Args:
+-- slot - The index of the item table
+-----------------------------------------------------------------------------------
+----Returns:
+-- A zero'd table with slot = slot
+-----------------------------------------------------------------------------------
+function make_empty_item_table(slot)
+ return {id=0,
+ count = 0,
+ bazaar = 0,
+ extdata = string.char(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
+ status = 0,
+ slot = slot}
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: make_inventory_table()
+-- Make a table of empty item tables
+----Args:
+-- none
+-----------------------------------------------------------------------------------
+----Returns:
+-- A table of 80 empty item tables indexed 1-80
+-----------------------------------------------------------------------------------
+function make_inventory_table()
+ local tab = {}
+ for i = 0,80 do
+ tab[i] = make_empty_item_table(i)
+ end
+ return tab
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: to_windower_api(str)
+-- Takes strings and converts them to resources table key format
+----Args:
+-- str - String to be converted to the windower API version
+-----------------------------------------------------------------------------------
+----Returns:
+-- a lower case string with ' ' replaced with '_'
+-----------------------------------------------------------------------------------
+function to_windower_api(str)
+ return __raw.lower(str:gsub(' ','_'))
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: to_windower_bag_api(str)
+-- Takes strings and converts them to resources table key format
+----Args:
+-- str - String to be converted to the windower bag API version
+-----------------------------------------------------------------------------------
+----Returns:
+-- a lower case string with ' ' replaced with ''
+-----------------------------------------------------------------------------------
+function to_windower_bag_api(str)
+ return __raw.lower(str:gsub(' ',''))
+end
+
+-----------------------------------------------------------------------------------
+----Name: to_bag_api(str)
+-- Takes strings and converts them to resources table key format
+----Args:
+-- str - String to be converted to the windower bag API version
+-----------------------------------------------------------------------------------
+----Returns:
+-- a lower case string with ' ' eliminated
+-----------------------------------------------------------------------------------
+function to_bag_api(str)
+ return __raw.lower(str:gsub(' ',''))
+end
+
+-----------------------------------------------------------------------------------
+----Name: to_windower_compact(str)
+-- Takes strings and converts them to a compact version of the resource table key
+----Args:
+-- str - String to be converted to the windower API version
+-----------------------------------------------------------------------------------
+----Returns:
+-- a lower case string with ' ' replaced with ''
+-----------------------------------------------------------------------------------
+function to_windower_compact(str)
+ return __raw.lower(str:gsub(' ',''))
+end
+
+-----------------------------------------------------------------------------------
+----Name: get_job_names()
+-- Returns the short and long form of the job name
+----Args:
+-- id - Job ID
+-----------------------------------------------------------------------------------
+----Returns:
+-- short and long form of the job name
+-----------------------------------------------------------------------------------
+function get_job_names(id)
+ if res.jobs[id] then
+ return res.jobs[id][language..'_short'], res.jobs[id][language]
+ else
+ return 'NONE', 'None'
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: update_job_names()
+-- Updates job names in the global player array
+----Args:
+-- none
+-----------------------------------------------------------------------------------
+----Returns:
+-- none
+-----------------------------------------------------------------------------------
+function update_job_names()
+ player.main_job,player.main_job_full = get_job_names(player.main_job_id)
+ player.sub_job, player.sub_job_full = get_job_names(player.sub_job_id)
+ player.job = player.main_job..'/'..player.sub_job
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: get_default_slot(k)
+-- Given a generally known slot key, return the default version of that key.
+----Args:
+-- k - A gear slot key.
+-----------------------------------------------------------------------------------
+----Returns:
+-- Returns the default slot key that matches the provided key.
+-----------------------------------------------------------------------------------
+function get_default_slot(k)
+ if slot_map[k] then
+ return toslotname(slot_map[k])
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: set_merge(baseSet, ...)
+-- Merges any additional gear sets (...) into the provided base set.
+-- Ensures that only valid slot keys/elements are used in the combined set.
+----Args:
+-- respect_disable - boolean indicating whether the disable_table should be respected.
+-- baseSet - The set that all the other sets are combined into. May be an empty set.
+-----------------------------------------------------------------------------------
+----Returns:
+-- Returns the modified base set, after all other sets have been merged into it.
+-----------------------------------------------------------------------------------
+function set_merge(respect_disable, baseSet, ...)
+ local combineSets = {...}
+
+ local canCombine = table.all(combineSets, function(t) return type(t) == 'table' end)
+ if not canCombine then
+ -- the code that called equip() or set_combine() is #3 on the stack from here
+ error("Trying to combine non-gear sets.", 3)
+ end
+
+ -- Take the list of tables we're given and cleans them up, so that they
+ -- only contain acceptable slot key entries.
+ local cleanSetsList = table.map(combineSets, unify_slots)
+
+ -- Combine the provided sets into combinedSet. If anything is blocked by having
+ -- the slot disabled, assign the item to the not_sent_out_equip table.
+ for _,set in pairs(cleanSetsList) do
+ for slot,item in pairs(set) do
+ if respect_disable and disable_table[slot_map[slot]] then
+ not_sent_out_equip[slot] = item
+ else
+ baseSet[slot] = item
+ end
+ end
+ end
+
+ return baseSet
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: parse_set_to_keys(str)
+-- Function to parse a string representation of a table into a list of keys that
+-- that can be used to select that table.
+----Args:
+-- str - Input can be a string, or a table of strings (which will be concatenated
+-- into a single string with spaces as intervals).
+--
+-- Example:
+-- Input: sets.precast.WS["Rudra's Storm"]['Ltng. Threnody'].Acc
+-- Output: [sets, precast, WS, Rudra's Storm, Ltng. Threnody, Acc]
+-----------------------------------------------------------------------------------
+----Returns:
+-- Returns a list of keys parsed from the provided input.
+-----------------------------------------------------------------------------------
+function parse_set_to_keys(str)
+ if type(str) == 'table' then
+ str = table.concat(str, ' ')
+ end
+
+ -- Parsing results get pushed into the result list.
+ local result = L{}
+
+ local remainder = str
+ local key
+ local stop
+ local sep = '.'
+ local count = 0
+
+ -- Loop as long as remainder hasn't been nil'd or reduced to 0 characters, but only to a maximum of 30 tries.
+ while remainder ~= "" and count < 30 do
+ -- Try aaa.bbb set names first
+ while sep == '.' do
+ _,_,key,sep,remainder = remainder:find("^([^%.%[]*)(%.?%[?)(.*)")
+ -- "key" is everything that is not . or [ 0 or more times.
+ -- "sep" is the next divider, which is necessarily . or [
+ -- "remainder" is everything after that
+ result:append(key)
+ end
+
+ -- Then try aaa['bbb'] set names.
+ -- Be sure to account for both single and double quote enclosures.
+ -- Ignore periods contained within quote strings.
+ while sep == '[' do
+ _,_,sep,remainder = remainder:find([=[^(%'?%"?)(.*)]=]) --' --block bad text highlighting
+ -- "sep" is the first ' or " found (or nil)
+ -- remainder is everything after that (or nil)
+ if sep == "'" then
+ _,_,key,stop,sep,remainder = remainder:find("^([^']+)('])(%.?%[?)(.*)")
+ elseif sep == '"' then
+ _,_,key,stop,sep,remainder = remainder:find('^([^"]+)("])(%.?%[?)(.*)')
+ end
+ if not sep or #sep == 0 then
+ -- If there is no single or double quote detected, attempt to treat the index as a number or boolean
+ local _,_,pot_key,pot_stop,pot_sep,pot_remainder = remainder:find('^([^%]]+)(])(%.?%[?)(.*)')
+ if tonumber(pot_key) then
+ key,stop,sep,remainder = tonumber(pot_key),pot_stop,pot_sep,pot_remainder
+ elseif pot_key == 'true' then
+ key,stop,sep,remainder = true,pot_stop,pot_sep,pot_remainder
+ elseif pot_key == 'false' then
+ key,stop,sep,remainder = false,pot_stop,pot_sep,pot_remainder
+ elseif pot_key and pot_key ~= "" then
+ key,stop,sep,remainder = pot_key,pot_stop,pot_sep,pot_remainder
+ end
+ end
+ result:append(key)
+ end
+
+ count = count +1
+ end
+
+ return result
+end
+
+
+-----------------------------------------------------------------------------------
+----Name: get_set_from_keys(keys)
+-- Function to take a list of keys select the set they point to, if possible.
+----Args:
+-- keys - A List of strings intended to be keys in progressively nested tables.
+-- The list is presumed to be based on the 'sets' table, and will start from that
+-- point if it is not explicitly provided in the key list.
+-----------------------------------------------------------------------------------
+----Returns:
+-- Returns the set if found, or nil if not.
+-----------------------------------------------------------------------------------
+function get_set_from_keys(keys)
+ local set = keys[1] == 'sets' and _G or sets
+ for key in (keys.it or it)(keys) do
+ if key == nil then
+ return nil
+ end
+ set = set[key]
+ if not set then
+ return nil
+ end
+ end
+
+ return set
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: initialize_arrow_offset(mob_table)
+--Desc: Returns the current target arrow offset.
+--Args:
+---- mob_table - Monster table of the target monster
+-----------------------------------------------------------------------------------
+--Returns:
+---- table - Keys x, y, and z with the respective current offsets from the target.
+-----------------------------------------------------------------------------------
+function initialize_arrow_offset(mob_table)
+ local backtab = {}
+ local arrow = windower.ffxi.get_info().target_arrow
+
+ if arrow.x == 0 and arrow.y == 0 and arrow.z == 0 then
+ return arrow
+ end
+
+ backtab.x = arrow.x-mob_table.x
+ backtab.y = arrow.y-mob_table.y
+ backtab.z = arrow.z-mob_table.z
+ return backtab
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: assemble_action_packet(target_id,target_index,category,spell_id)
+--Desc: Puts together an "action" packet (0x1A)
+--Args:
+---- target_id - The target's ID
+---- target_index - The target's index
+---- category - The action's category. (3 = MA, 7 = WS, 9 = JA, 16 = RA, 25 = MS)
+---- spell_ID - The current spell's ID
+-----------------------------------------------------------------------------------
+--Returns:
+---- string - An action packet. First four bytes are dummy bytes.
+-----------------------------------------------------------------------------------
+function assemble_action_packet(target_id,target_index,category,spell_id,arrow_offset)
+ local outstr = string.char(0x1A,0x08,0,0)
+ outstr = outstr..string.char( (target_id%256), math.floor(target_id/256)%256, math.floor( (target_id/65536)%256) , math.floor( (target_id/16777216)%256) )
+ outstr = outstr..string.char( (target_index%256), math.floor(target_index/256)%256)
+ outstr = outstr..string.char( (category%256), math.floor(category/256)%256)
+
+ if category == 16 then
+ spell_id = 0
+ end
+
+ outstr = outstr..string.char( (spell_id%256), math.floor(spell_id/256)%256)..string.char(0,0) .. 'fff':pack(arrow_offset.x,arrow_offset.z,arrow_offset.y)
+ return outstr
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: assemble_use_item_packet(target_id,target_index,item)
+--Desc: Puts together a "use item" packet (0x37)
+--Args:
+---- target_id - The target's ID
+---- target_index - The target's index
+---- item_id - The id for the current item
+-----------------------------------------------------------------------------------
+--Returns:
+---- string - A use item packet. First four bytes are dummy bytes.
+-----------------------------------------------------------------------------------
+function assemble_use_item_packet(target_id,target_index,item_id)
+ local outstr = string.char(0x37,0x0A,0,0)
+ outstr = outstr..string.char( (target_id%256), math.floor(target_id/256)%256, math.floor( (target_id/65536)%256) , math.floor( (target_id/16777216)%256) )
+ outstr = outstr..string.char(0,0,0,0)
+ outstr = outstr..string.char( (target_index%256), math.floor(target_index/256)%256)
+ inventory_index,bag_id = find_usable_item(item_id)
+ if inventory_index then
+ outstr = outstr..string.char(inventory_index%256)..string.char(0,bag_id,0,0,0)
+ else
+ msg.debugging('Proposed item: '..(res.items[item_id][language] or item_id)..' not found in inventory.')
+ return
+ end
+ return outstr
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: assemble_menu_item_packet(target_id,target_index,item)
+--Desc: Puts together a "menu item" packet (0x36)
+--Args:
+---- target_id - The target's ID
+---- target_index - The target's index
+---- item_id - The id for the current item
+-----------------------------------------------------------------------------------
+--Returns:
+---- string - A use item packet. First four bytes are dummy bytes.
+-----------------------------------------------------------------------------------
+function assemble_menu_item_packet(target_id,target_index,...)
+ local outstr = string.char(0x36,0x20,0,0)
+ -- Message is coming out too short by 12 characters
+
+ -- Target ID
+ outstr = outstr.."I":pack(target_id)
+ local item_ids,counts,count = {...},{},0
+ for i,v in pairs(item_ids) do
+ if res.items[v] then
+ counts[v] = (counts[v] or 0) + 1
+ count = count + 1
+ end
+ end
+
+ local unique_items = 0
+ for i,v in pairs(counts) do
+ outstr = outstr.."I":pack(v)
+ unique_items = unique_items + 1
+ end
+ if unique_items > 9 then
+ msg.debugging('Too many items ('..unique_items..') passed to the assemble_menu_item_packet function')
+ return
+ end
+ while #outstr < 0x30 do
+ outstr = outstr..string.char(0)
+ end
+
+ -- Inventory Index for the one unit
+
+ for i,v in pairs(counts) do
+ inventory_index = find_inventory_item(i)
+ if inventory_index then
+ outstr = outstr..string.char(inventory_index%256)
+ else
+ msg.debugging('Proposed item: '..(res.items[i][language] or i)..' not found in inventory.')
+ return
+ end
+ end
+ while #outstr < 0x3A do
+ outstr = outstr..string.char(0)
+ end
+ -- Target Index
+ outstr = outstr.."H":pack(target_index)
+ -- Only one item being traded
+ outstr = outstr..string.char(unique_items,0,0,0)
+ return outstr
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: find_inventory_item(item_id)
+--Desc: Finds a npc trade item in normal inventory. Assumes items array
+-- is accurate already.
+--Args:
+---- item_id - The resource line for the current item
+-----------------------------------------------------------------------------------
+--Returns:
+---- inventory_index - The item's use inventory index (if it exists)
+---- bag_id - The item's bag ID (if it exists)
+-----------------------------------------------------------------------------------
+function find_inventory_item(item_id)
+ for i,v in pairs(items.inventory) do
+ if type(v) == 'table' and v.id == item_id and v.status == 0 then
+ return i
+ end
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: find_usable_item(item_id,bool)
+--Desc: Finds a usable item in temporary or normal inventory. Assumes items array
+-- is accurate already.
+--Args:
+---- item_id - The resource line for the current item
+-----------------------------------------------------------------------------------
+--Returns:
+---- inventory_index - The item's use inventory index (if it exists)
+---- bag_id - The item's bag ID (if it exists)
+-----------------------------------------------------------------------------------
+function find_usable_item(item_id)
+ for _,bag in ipairs(usable_item_bags) do
+ for i,v in pairs(items[to_windower_bag_api(bag.en)]) do
+ if type(v) == 'table' and v.id == item_id and is_usable_item(v,bag.id) then
+ return i, bag.id
+ end
+ end
+ end
+end
+
+-----------------------------------------------------------------------------------
+--Name: is_usable_item(i_tab)
+--Desc: Determines whether the item table belongs to a usable item.
+--Args:
+---- i_tab - current item table
+---- bag_id - The item's bag ID
+-----------------------------------------------------------------------------------
+--Returns:
+---- true or false to indicate whether the item is usable
+-----------------------------------------------------------------------------------
+function is_usable_item(i_tab,bag_id)
+ local ext = extdata.decode(i_tab)
+ if ext.type == 'Enchanted Equipment' and ext.usable then
+ return i_tab.status == 5
+ elseif i_tab.status == 0 and bag_id < 4 then
+ return true
+ end
+ return false
+end
+
+-----------------------------------------------------------------------------------
+--Name: number_of_jps(jp_tab)
+--Desc: Gives the total number of job points spent on that job
+--Args:
+---- jp_tab - One table from windower.ffxi.get_player().job_points[job]
+-----------------------------------------------------------------------------------
+--Returns:
+---- The total number of job points spent on that job.
+-----------------------------------------------------------------------------------
+function number_of_jps(jp_tab)
+ local count = 0
+ for _,v in pairs(jp_tab) do
+ count = count + v*(v+1)
+ end
+ return count/2
+end
+
+-----------------------------------------------------------------------------------
+--Name: filter_pretarget(spell)
+--Desc: Determines whether the current player is capable of using the proposed action
+---- at pretarget.
+--Args:
+---- action - current action
+-----------------------------------------------------------------------------------
+--Returns:
+---- false to cancel further command processing and just return the command.
+-----------------------------------------------------------------------------------
+function filter_pretarget(action)
+ local category = outgoing_action_category_table[unify_prefix[action.prefix]]
+ local bool = true
+ local err
+ if world.in_mog_house then
+ msg.debugging("Unable to execute commands. Currently in a Mog House zone.")
+ return false
+ elseif category == 3 then
+ local available_spells = windower.ffxi.get_spells()
+ bool,err = check_spell(available_spells,action)
+ elseif category == 7 then
+ local available = windower.ffxi.get_abilities().weapon_skills
+ if not table.contains(available,action.id) then
+ bool,err = false,"Unable to execute command. You do not have access to that weapon skill."
+ end
+ elseif category == 9 then
+ local available = windower.ffxi.get_abilities().job_abilities
+ if not table.contains(available,action.id) then
+ bool,err = false,"Unable to execute command. You do not have access to that job ability."
+ end
+ elseif category == 25 and (not player.main_job_id == 23 or not windower.ffxi.get_mjob_data().species or
+ not res.monstrosity[windower.ffxi.get_mjob_data().species] or not res.monstrosity[windower.ffxi.get_mjob_data().species].tp_moves[action.id] or
+ not (res.monstrosity[windower.ffxi.get_mjob_data().species].tp_moves[action.id] <= player.main_job_level)) then
+ -- Monstrosity filtering
+ msg.debugging("Unable to execute command. You do not have access to that monsterskill ("..(res.monster_skills[action.id][language] or action.id)..")")
+ return false
+ end
+
+ if err then
+ msg.debugging(err)
+ end
+ return bool
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: check_spell(available_spells,spell)
+--Desc: Determines whether the current player is capable of using the proposed spell
+---- at precast.
+--Args:
+---- available_spells - current set of available spells
+---- spell - current spell table
+-----------------------------------------------------------------------------------
+--Returns:
+---- false if the spell is not currently accessible
+-----------------------------------------------------------------------------------
+function check_spell(available_spells,spell)
+ -- Filter for spells that you do not know. Exclude Impact / Dispelga.
+ local spell_jobs = copy_entry(res.spells[spell.id].levels)
+ if not available_spells[spell.id] and not (spell.id == 503 or spell.id == 417 or spell.id == 360) then
+ return false,"Unable to execute command. You do not know that spell ("..(res.spells[spell.id][language] or spell.id)..")"
+ -- Filter for spells that you know, but do not currently have access to
+ elseif (not spell_jobs[player.main_job_id] or not (spell_jobs[player.main_job_id] <= player.main_job_level or
+ (spell_jobs[player.main_job_id] >= 100 and number_of_jps(player.job_points[__raw.lower(res.jobs[player.main_job_id].ens)]) >= spell_jobs[player.main_job_id]) ) ) and
+ (not spell_jobs[player.sub_job_id] or not (spell_jobs[player.sub_job_id] <= player.sub_job_level)) and not (player.main_job_id == 23) then
+ return false,"Unable to execute command. You do not have access to that spell ("..(res.spells[spell.id][language] or spell.id)..")"
+ -- At this point, we know that it is technically castable by this job combination if the right conditions are met.
+ elseif player.main_job_id == 20 and ((addendum_white[spell.id] and not buffactive[401] and not buffactive[416]) or
+ (addendum_black[spell.id] and not buffactive[402] and not buffactive[416])) and
+ not (spell_jobs[player.sub_job_id] and spell_jobs[player.sub_job_id] <= player.sub_job_level) then
+ return false,"Unable to execute command. Addendum required for that spell ("..(res.spells[spell.id][language] or spell.id)..")"
+ elseif player.sub_job_id == 20 and ((addendum_white[spell.id] and not buffactive[401] and not buffactive[416]) or
+ (addendum_black[spell.id] and not buffactive[402] and not buffactive[416])) and
+ not (spell_jobs[player.main_job_id] and (spell_jobs[player.main_job_id] <= player.main_job_level or
+ (spell_jobs[player.main_job_id] >= 100 and number_of_jps(player.job_points[__raw.lower(res.jobs[player.main_job_id].ens)]) >= spell_jobs[player.main_job_id]) ) ) then
+ return false,"Unable to execute command. Addendum required for that spell ("..(res.spells[spell.id][language] or spell.id)..")"
+ elseif spell.type == 'BlueMagic' and not ((player.main_job_id == 16 and table.contains(windower.ffxi.get_mjob_data().spells,spell.id))
+ or unbridled_learning_set[spell.english]) and
+ not (player.sub_job_id == 16 and table.contains(windower.ffxi.get_sjob_data().spells,spell.id)) then
+ -- This code isn't hurting anything, but it doesn't need to be here either.
+ return false,"Unable to execute command. Blue magic must be set to cast that spell ("..(res.spells[spell.id][language] or spell.id)..")"
+ elseif spell.type == 'Ninjutsu' then
+ if player.main_job_id ~= 13 and player.sub_job_id ~= 13 then
+ return false,"Unable to make action packet. You do not have access to that spell ("..(spell[language] or spell.id)..")"
+ elseif not player.inventory[tool_map[spell.english][language]] and not (player.main_job_id == 13 and player.inventory[universal_tool_map[spell.english][language]]) then
+ return false,"Unable to make action packet. You do not have the proper tools."
+ end
+ end
+ return true
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: filter_precast(spell)
+--Desc: Determines whether the current player is capable of using the proposed spell
+---- at precast.
+--Args:
+---- spell - current spell table
+-----------------------------------------------------------------------------------
+--Returns:
+---- false to block the outgoing packet
+-----------------------------------------------------------------------------------
+function filter_precast(spell)
+ if not spell.target.id or not spell.target.index then
+ if debugging.general then msg.debugging('No target id or index') end
+ return false
+ end
+ return true
+end
+
+
+local cmd_reg = {}
+Command_Registry = {}
+
+function Command_Registry.new()
+ local new_instance = {_self={last_removed=os.clock()}}
+ local function remove_old_entries (t)
+ -- Removes old command registry entries.
+ for i,v in pairs(t) do
+ local lim = (type(v) == 'table' and (v.spell and v.spell.cast_time and v.spell.cast_time*1.1+2 or
+ v.spell and v.spell.prefix=='/pet' and 5 or
+ v.spell and v.spell.action_type and delay_map_to_action_type[v.spell.action_type] or
+ 3) + (v.pretarget_cast_delay or 0) + (v.precast_cast_delay or 0))
+ -- Sets it to normal casting time + 10% +1 for anything with a defined cast_time, or 1 if there is no defined cast time.
+ if tonumber(i) and os.time()-i >= lim then
+ cmd_reg.delete_entry(t,i)
+ end
+ end
+ return os.clock()
+ end
+
+ return setmetatable(new_instance, {__index = function(t, k)
+ if os.clock() - rawget(rawget(t,'_self'),'last_removed') > 0.04 then
+ rawset(rawget(t,'_self'),'last_removed', remove_old_entries(t))
+ end
+ if rawget(cmd_reg, k) ~= nil then
+ return rawget(cmd_reg,k)
+ else
+ return rawget(t,k)
+ end
+ end})
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: cmd_reg:new_entry(sp)
+--Desc: Makes a new entry in command_registry.
+--Args:
+---- sp - Resources line for the current spell
+-----------------------------------------------------------------------------------
+--Returns:
+---- ts - index for command_registry
+-----------------------------------------------------------------------------------
+function cmd_reg:new_entry(sp)
+ local ts = os.time()
+ while rawget(self,ts) do
+ ts = ts+0.001
+ end
+ rawset(self,ts,{pretarget_cast_delay=0, precast_cast_delay=0, cancel_spell=false, new_target=false, current_event='nascent', spell=sp, timestamp=ts,target_arrow={x=0,y=0,z=0}})
+ if debugging.command_registry then
+ msg.addon_msg('Creating a new command_registry entry: '..windower.to_shift_jis(tostring(ts)..' '..tostring(self[ts])))
+ end
+ return ts
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: cmd_reg:delete_entry(ts)
+--Desc: Makes a new entry in command_registry.
+--Args:
+---- ts - timestamp of the command registry entry to be deleted
+-----------------------------------------------------------------------------------
+--Returns:
+---- bool - true indicates a successful deletion
+-----------------------------------------------------------------------------------
+function cmd_reg:delete_entry(ts)
+ if rawget(self,ts) then
+ if debugging.command_registry then
+ msg.debugging('Deleting a command_registry entry: '..windower.to_shift_jis(tostring(ts)..' '..tostring(rawget(self,ts))))
+ end
+ rawset(self,ts,nil)
+ return true
+ elseif debugging.command_registry then
+ msg.debugging('Attempted to delete a command_registry entry that did not exist: '..windower.to_shift_jis(tostring(ts) ))
+ end
+ return false
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: cmd_reg:find_by_spell(value)
+--Desc: Returns the proper unified prefix, or "Monster" in the case of a monster action
+--Args:
+---- typ - 'spell', 'timestamp', or 'id'
+---- value - The spell, timestamp, or id
+---- Currently the ID and Timestamp options are unused.
+-----------------------------------------------------------------------------------
+--Returns:
+---- timestamp index of command_registry
+-----------------------------------------------------------------------------------
+function cmd_reg:find_by_spell(value)
+ -- Finds all entries of a given spell in the table.
+ -- Returns the one with the most recent timestamp.
+ -- Actions that do not have timestamps yet (have not hit midcast) are given lowest priority.
+ local potential_entries,current_time,winner,ts = {},os.time()
+ for i,v in pairs(self) do
+ if type(v) == 'table' and v.spell and v.spell.prefix == value.prefix and v.spell.name == value.name then
+ potential_entries[i] = v.timestamp or 0
+ elseif type(v) == 'table' and v.spell and v.spell.english == 'Double-Up' and value.type == 'CorsairRoll' then
+ -- Double Up ability uses will return action packets that match Corsair Rolls rather than Double Up
+ potential_entries[i] = v.timestamp or 0
+ end
+ end
+ for i,v in pairs(potential_entries) do
+ if not winner or (current_time - v < current_time - winner) then
+ winner = v
+ ts = i
+ end
+ end
+ return ts
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: cmd_reg:find_by_time()
+--Desc: Finds the most recent command_registry entry
+--Args:
+---- none
+-----------------------------------------------------------------------------------
+--Returns:
+---- ts,discovered entry
+-----------------------------------------------------------------------------------
+function cmd_reg:find_by_time(target_time)
+ local time_stamp,ts
+ target_time = target_time or os.time()
+
+ -- Iterate over command_registry looking for the spell with the closest timestamp.
+ -- Call aftercast with this spell's information (interrupted) if one is found.
+ for i,v in pairs(self) do
+ if not time_stamp or (type(v) == 'table' and v.timestamp and ((target_time - v.timestamp) < (target_time - time_stamp))) then
+ time_stamp = v.timestamp
+ ts = i
+ end
+ end
+ if time_stamp then
+ return ts,table.reassign({},self[ts])
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: cmd_reg:delete_by_id(id)
+--Desc: Deletes all command_registry entry based that match a given target ID.
+--Args:
+---- id - ID of the target
+-----------------------------------------------------------------------------------
+--Returns:
+---- ts,last_entry for the deleted entry
+-----------------------------------------------------------------------------------
+function cmd_reg:delete_by_id(id)
+ local ts,last_entry
+ for i,v in pairs(self) do
+ if v.spell and v.spell.target then
+ if v.spell.target.id == id then
+ last_entry = table.reassign({},self[i])
+ ts = i
+ self[i] = nil
+ end
+ end
+ end
+ return ts,last_entry
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: copy_entry(tab)
+--Desc: Copies a table into a new table while preserving its metatable.
+-- Designed for copying resources entries.
+--Args:
+---- tab - Resources table.
+-----------------------------------------------------------------------------------
+--Returns:
+---- ret - New table that has the same metatable and content as the original table.
+-----------------------------------------------------------------------------------
+function copy_entry(tab)
+ if not tab then return nil end
+ local ret = setmetatable(table.reassign({},tab),getmetatable(tab))
+ return ret
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: get_spell(act)
+--Desc: Takes an action table and returns a modified resource line
+--Args:
+---- act - action table in the same format as event_action
+-----------------------------------------------------------------------------------
+--Returns:
+---- spell - Resource line of the current spell
+-----------------------------------------------------------------------------------
+function get_spell(act)
+ local spell, abil_ID, effect_val
+ local msg_ID = act.targets[1].actions[1].message
+
+ if T{7,8,9}:contains(act.category) then
+ abil_ID = act.targets[1].actions[1].param
+ elseif T{3,4,5,6,11,13,14,15}:contains(act.category) then
+ abil_ID = act.param
+ effect_val = act.targets[1].actions[1].param
+ end
+
+ if act.category == 12 or act.category == 2 then
+ spell = copy_entry(resources_ranged_attack)
+ else
+ if not res.action_messages[msg_ID] or msg_ID == 31 then
+ if act.category == 4 or act.category == 8 then
+ spell = spell_complete(copy_entry(res.spells[abil_ID]))
+ if act.category == 4 and spell then spell.recast = act.recast end
+ elseif T{6,13,14,15}:contains(act.category) then
+ spell = spell_complete(copy_entry(res.job_abilities[abil_ID])) -- May have to correct for charmed pets some day, but I'm not sure there are any monsters with TP moves that give no message.
+ elseif T{3,7}:contains(act.category) then
+ spell = spell_complete(copy_entry(res.weapon_skills[abil_ID]))
+ elseif T{5,9}:contains(act.category) then
+ spell = copy_entry(res.items[abil_ID])
+ else
+ spell = {name=tostring(msg_ID)}
+ end
+
+ return spell
+ end
+
+
+ local fields = fieldsearch(res.action_messages[msg_ID].english) -- ENGLISH
+
+ if table.contains(fields,'spell') then
+ spell = copy_entry(res.spells[abil_ID])
+ if act.category == 4 then spell.recast = act.recast end
+ elseif table.contains(fields,'ability') then
+ spell = copy_entry(res.job_abilities[abil_ID])
+ elseif table.contains(fields,'weapon_skill') then
+ if abil_ID > 255 then -- WZ_RECOVER_ALL is used by chests in Limbus
+ spell = copy_entry(res.monster_abilities[abil_ID])
+ if not spell then
+ spell = {id=abil_ID,english='Special Attack'}
+ end
+ elseif abil_ID < 256 then
+ spell = copy_entry(res.weapon_skills[abil_ID])
+ end
+ elseif msg_ID == 303 then
+ spell = copy_entry(res.job_abilities[74]) -- Divine Seal
+ elseif msg_ID == 304 then
+ spell = copy_entry(res.job_abilities[75]) -- 'Elemental Seal'
+ elseif msg_ID == 305 then
+ spell = copy_entry(res.job_abilities[76]) -- 'Trick Attack'
+ elseif msg_ID == 311 or msg_ID == 311 then
+ spell = copy_entry(res.job_abilities[79]) -- 'Cover'
+ elseif msg_ID == 240 or msg_ID == 241 then
+ spell = copy_entry(res.job_abilities[43]) -- 'Hide'
+ elseif msg_ID == 244 then
+ spell = copy_entry(res.job_abilities[act.param]) -- Mug failures
+ elseif msg_ID == 328 then
+ spell = copy_entry(res.job_abilities[effect_val]) -- BPs that are out of range
+ end
+
+
+ if table.contains(fields,'item') then
+ if spell then
+ spell.item = copy_entry(res.items[effect_val])
+ else
+ spell = copy_entry(res.items[abil_ID])
+ end
+ else
+ spell = spell_complete(spell)
+ end
+ end
+
+ if spell then
+ spell.name = spell[language]
+ spell.interrupted = false
+ end
+
+ return spell
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: spell_complete(rline)
+--Desc: Takes a resource line and modifies it so it includes aftercast cost and
+-- a few other values
+--Args:
+---- rline - resource line
+-----------------------------------------------------------------------------------
+--Returns:
+---- rline - modified resource line
+-----------------------------------------------------------------------------------
+function spell_complete(rline)
+ -- Hardcoded adjustments
+ if rline and rline.skill == 40 and buffactive.Pianissimo and rline.cast_time == 8 then
+ -- Pianissimo halves song casting time for buffs
+ rline.cast_time = 4
+ rline.targets.Party = true
+ end
+ if rline and rline.skill == 44 and buffactive.Entrust and string.find(rline.en,"Indi") then
+ -- Entrust allows Indi- spells to be cast on party members
+ rline.targets.Party = true
+ end
+
+ if rline == nil then
+ return {tpaftercast = player.tp, mpaftercast = player.mp, mppaftercast = player.mpp}
+ end
+ if not rline.mp_cost or rline.mp_cost == -1 then rline.mp_cost = 0 end
+ if not rline.tp_cost and rline.type == 'WeaponSkill' then
+ rline.tp_cost = player.tp
+ elseif not rline.tp_cost or rline.tp_cost == -1 then
+ rline.tp_cost = 0
+ end
+
+ if rline.skill and tonumber(rline.skill) then
+ rline.skill = res.skills[rline.skill][language]
+ end
+
+ if rline.element and tonumber(rline.element) then
+ rline.element = res.elements[rline.element][language]
+ end
+
+ if rline.tp_cost == 0 then rline.tpaftercast = player.tp else
+ rline.tpaftercast = player.tp - rline.tp_cost end
+
+ if rline.mp_cost == 0 then
+ rline.mpaftercast = player.mp
+ rline.mppaftercast = player.mpp
+ else
+ rline.mpaftercast = player.mp - rline.mp_cost
+ rline.mppaftercast = (player.mp - rline.mp_cost)/player.max_mp
+ end
+
+ return rline
+end
+
+-----------------------------------------------------------------------------------
+--Name: logit()
+--Args:
+---- logfile (file): File to be logged to
+---- str (string): String to be logged.
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function logit(str)
+ if debugging.logging then
+ if not logfile and windower.dir_exists('../addons/GearSwap/data/logs') then
+ logfile = io.open('../addons/GearSwap/data/logs/NormalLog'..tostring(os.clock())..'.log','w+')
+ logfile:write('GearSwap LOGGER HEADER\n')
+ end
+ logfile:write(str)
+ logfile:flush()
+ end
+end
+
+msg = {}
+
+-----------------------------------------------------------------------------------
+--Name: msg.add_to_chat(col,str)
+--Args:
+---- col (num): Color to print out in (0x1F,col)
+---- str (string): String to be printed.
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function msg.add_to_chat(col,str)
+ if str == '' then return end
+ if col == 1 then
+ windower.add_to_chat(1,str)
+ else
+ windower.add_to_chat(1,string.char(0x1F,col%256)..str..string.char(0x1E,0x01))
+ end
+end
+
+-----------------------------------------------------------------------------------
+--Name: msg.debugging(message)
+--Desc: Checks _settings.debug_mode and outputs the message if necessary
+--Args:
+---- message - The debug message
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function msg.debugging(message)
+ if _settings.debug_mode or debugging.general or debugging.command_registry then
+ msg.add_to_chat(8,"GearSwap (Debug Mode): "..windower.to_shift_jis(tostring(message)))
+ end
+end
+
+-----------------------------------------------------------------------------------
+--Name: msg.addon_msg(col,str)
+--Args:
+---- col (num): Color to print out in (0x1F,col)
+---- str (string): String to be printed.
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function msg.addon_msg(col,str)
+ msg.add_to_chat(col,'GearSwap: '..str)
+end
+
+-- Set up the priority list structure
+
+-----------------------------------------------------------------------------------
+--Name: prioritize()
+--Args:
+---- priority_list (table): Current list of slot priorities
+---- slot_id (number): Desired order of the piece of equipment
+---- priority (number): Name for the slot
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+function prioritize(self,slot_id,priority)
+ if priority and tonumber(priority) then -- Check that priority is number
+ rawset(self,slot_id,priority)
+ return
+ elseif priority then
+ msg.addon_msg(123,'Invalid priority ('..tostring(priority)..') given')
+ end
+ rawset(self,slot_id,0)
+end
+
+
+local priority_list = {}
+
+Priorities = {}
+function Priorities.new()
+ local new_instance = {}
+ return setmetatable(new_instance, { __index = function(t, k) if rawget(t, k) ~= nil then return rawget(t,k) else return rawget(priority_list,k) end end,
+ __newindex=prioritize})
+end
+
+-----------------------------------------------------------------------------------
+--Name: priority_list:it()
+--Args:
+---- self (table): Current list of slot priorities
+-----------------------------------------------------------------------------------
+--Returns:
+---- slot_id : Number from 0~15
+-----------------------------------------------------------------------------------
+function priority_list:it()
+ return function ()
+ local maximum,slot_id = -math.huge
+ for i=0,15 do
+ if self[i] and (self[i] > maximum or (self[i] == maximum and self[i] == -math.huge)) then
+ maximum = self[i]
+ slot_id = i
+ end
+ end
+ if not slot_id then return end
+ self[slot_id] = nil
+ return slot_id,maximum
+ end
+end
+
+
+
+-----------------------------------------------------------------------------------
+--Name: toslotname(slot_id)
+--Args:
+---- slot_id: Number from 0-15 representing the slot
+-----------------------------------------------------------------------------------
+--Returns:
+---- slot name (string)
+-----------------------------------------------------------------------------------
+function toslotname(slot_id)
+ return rawget(default_slot_map,slot_id)
+end
+
+
+
+-----------------------------------------------------------------------------------
+--Name: toslotid(slot_name)
+--Args:
+---- slot_name: proposed slot name
+-----------------------------------------------------------------------------------
+--Returns:
+---- slot id (whole number from 0-15)
+-----------------------------------------------------------------------------------
+function toslotid(slot_name)
+ return slot_map[slot_name]
+end
+
+
+
+-----------------------------------------------------------------------------------
+--Name: windower.debug(...)
+--Args:
+---- ...: Anything, to be passed to the real windower.debug if the windower_debugging
+---- flag is set.
+-----------------------------------------------------------------------------------
+--Returns:
+---- Nothing
+-----------------------------------------------------------------------------------
+windower.__raw = {debug = windower.debug}
+windower.debug = function(...)
+ if debugging.windower_debug then __raw.debug(...) end
+end
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Modes.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Modes.lua
new file mode 100644
index 0000000..4e01890
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Modes.lua
@@ -0,0 +1,457 @@
+-------------------------------------------------------------------------------------------------------------------
+-- 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.
+--
+-- MeleeMode = M{['description']='Melee Mode', 'Normal', 'Acc', 'Att'}
+--
+--
+-- 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')
+--
+--
+-- 3) Create a string mode with a specified default value. Construct with a table:
+-- CombatWeapon = M{['description']='Melee Mode', ['string']='Dagger'}
+-- CombatWeapon = M{['description']='Melee Mode', ['string']=''}
+--
+--
+--
+-- 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 -- Gets the current mode text value. Booleans will return the strings "on" or "off".
+-- 3) m.value -- Gets the current mode value. Booleans will return the boolean values of true or false.
+-- 3) m.index -- Gets the current index value, or true/false for booleans.
+--
+-- Typically use m.current when using the mode to create a set name, and m.value when
+-- testing the mode value in conditional rules.
+--
+-- 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 = {}
+ m._track._class = 'mode'
+
+ -- If we're passed a list of strings (that is, the first element is a string),
+ -- 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 adding the metadata to
+
+ -- If we have a table of values, it's either a list or a string
+ if type(t) == 'table' then
+ -- Save the description, if provided
+ if t['description'] then
+ m._track._description = t['description']
+ end
+
+ -- If we were given an explicit 'string' field, construct a string mode class.
+ if t.string and type(t.string) == 'string' then
+ m._track._type = 'string'
+ m._track._count = 1
+ m._track._default = 'defaultstring'
+
+ if t.string then
+ m['string'] = t.string
+ m['defaultstring'] = t.string
+ end
+ -- Otherwise put together a standard list mode class.
+ else
+ m._track._type = 'list'
+ m._track._invert = {}
+ m._track._count = 0
+ m._track._default = 1
+
+ -- 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
+ end
+ -- If the first argument is a bool, construct a boolean mode class.
+ elseif type(t) == 'boolean' or t == nil then
+ m._track._type = 'boolean'
+ m._track._count = 2
+ m._track._default = t or false
+ m._track._description = args[1]
+ -- 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
+
+ -- Initialize current value to the default.
+ 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' then
+ return m[m._track._current]
+ elseif lk == 'value' then
+ if m._track._type == 'boolean' then
+ return m._track._current
+ else
+ return m[m._track._current]
+ end
+ elseif lk == 'has_value' then
+ return _meta.M.__methods.f_has_value(m)
+ elseif lk == 'default' then
+ if m._track._type == 'boolean' then
+ return m._track._default
+ else
+ return m[m._track._default]
+ end
+ elseif lk == 'description' then
+ return m._track._description
+ elseif lk == 'index' then
+ return m._track._current
+ elseif m._track[lk] then
+ return m._track[lk]
+ 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..'}'
+ elseif m._track._type == 'string' then
+ res = res .. 'String'
+ 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 ~= 'list' then
+ error("Can only revise the options list for a list 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
+
+
+-- Function to test whether the list table contains the specified value.
+_meta.M.__methods['contains'] = function(m, str)
+ if m._track._invert then
+ if type(str) == 'string' then
+ return (m._track._invert[str] ~= nil)
+ else
+ error("Invalid argument type: " .. type(str), 2)
+ end
+ else
+ error("Cannot test for containment on a " .. m._track._type .. " mode class.", 2)
+ end
+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
+ elseif m._track._type == 'boolean' then
+ 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
+ elseif m._track._type == 'boolean' then
+ 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("Can only toggle a boolean 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
+ elseif m._track._type == 'list' then
+ if not val then
+ error("List variable cannot be set to nil.", 2)
+ end
+ 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
+ elseif m._track._type == 'string' then
+ if type(val) == 'string' then
+ m._track._current = 'string'
+ m.string = val
+ else
+ error("Unrecognized value type: "..type(val), 2)
+ end
+ end
+
+ return m.Current
+end
+
+
+-- Forces a boolean mode to false, or a string to an empty string.
+_meta.M.__methods['unset'] = function(m)
+ if m._track._type == 'boolean' then
+ m._track._current = false
+ elseif m._track._type == 'string' then
+ m._track._current = 'string'
+ m.string = ''
+ else
+ error("Cannot unset a list mode class.", 2)
+ end
+
+ 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
+
+
+-- Check to be sure that the mode var has a valid (for its type) value.
+-- String vars must be non-empty.
+-- List vars must not be empty strings, or the word 'none'.
+-- Boolean are always considered valid (can only be true or false).
+_meta.M.__methods['f_has_value'] = function(m)
+ local cur = m.value
+ if m._track._type == 'string' then
+ if cur and cur ~= '' then
+ return true
+ else
+ return false
+ end
+ elseif m._track._type == 'boolean' then
+ return true
+ else
+ if not cur or cur == '' or cur:lower() == 'none' then
+ return false
+ else
+ return true
+ end
+ end
+end
+
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Globals.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Globals.lua
new file mode 100644
index 0000000..d7b2e04
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Globals.lua
@@ -0,0 +1,114 @@
+-------------------------------------------------------------------------------------------------------------------
+-- 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 HybridMode')
+ send_command('bind !f9 gs c cycle RangedMode')
+ send_command('bind @f9 gs c cycle WeaponskillMode')
+ send_command('bind f10 gs c set DefenseMode Physical')
+ send_command('bind ^f10 gs c cycle PhysicalDefenseMode')
+ send_command('bind !f10 gs c toggle Kiting')
+ send_command('bind f11 gs c set DefenseMode Magical')
+ 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 DefenseMode')
+
+ 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 @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/Mote-Include.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Include.lua
new file mode 100644
index 0000000..e5527f0
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/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.
+-------------------------------------------------------------------------------------------------------------------
+
+current_mote_include_version = 2
+
+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')
+
+ -- Modes is the include for a mode-tracking variable class. Used for state vars, below.
+ include('Modes')
+
+ -- Var for tracking state values
+ state = {}
+
+ -- General melee offense/defense modes, allowing for hybrid set builds, as well as idle/resting/weaponskill.
+ -- This just defines the vars and sets the descriptions. List modes with no values automatically
+ -- get assigned a 'Normal' default value.
+ state.OffenseMode = M{['description'] = 'Offense Mode'}
+ state.HybridMode = M{['description'] = 'Hybrid Mode'}
+ state.RangedMode = M{['description'] = 'Ranged Mode'}
+ state.WeaponskillMode = M{['description'] = 'Weaponskill Mode'}
+ state.CastingMode = M{['description'] = 'Casting Mode'}
+ state.IdleMode = M{['description'] = 'Idle Mode'}
+ state.RestingMode = M{['description'] = 'Resting Mode'}
+
+ state.DefenseMode = M{['description'] = 'Defense Mode', 'None', 'Physical', 'Magical'}
+ state.PhysicalDefenseMode = M{['description'] = 'Physical Defense Mode', 'PDT'}
+ state.MagicalDefenseMode = M{['description'] = 'Magical Defense Mode', 'MDT'}
+
+ state.Kiting = M(false, 'Kiting')
+ state.SelectNPCTargets = M(false, 'Select NPC Targets')
+ state.PCTargetMode = M{['description'] = 'PC Target Mode', 'default', 'stpt', 'stal', 'stpc'}
+
+ state.EquipStop = M{['description'] = 'Stop Equipping Gear', 'off', 'precast', 'midcast', 'pet_midcast'}
+
+ state.CombatWeapon = M{['description']='Combat Weapon', ['string']=''}
+ state.CombatForm = M{['description']='Combat Form', ['string']=''}
+
+ -- Non-mode vars that are used for state tracking.
+ state.MaxWeaponskillDistance = 0
+ state.Buff = {}
+
+ -- Classes describe a 'type' of action. They are similar to state, but
+ -- may have any free-form value, or describe an entire table of mapped values.
+ 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.JAMode = nil
+ classes.CustomClass = 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
+
+
+ -- Var for tracking misc info
+ info = {}
+ options = {}
+
+ -- Special control flags.
+ mote_vars = {}
+ mote_vars.set_breadcrumbs = L{}
+ mote_vars.res_buffs = S{}
+ for index,struct in pairs(gearswap.res.buffs) do
+ mote_vars.res_buffs:add(struct.en)
+ end
+
+ -- Sub-tables 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
+
+if not mote_include_version or mote_include_version < current_mote_include_version then
+ add_to_chat(123,'Warning: Your job file is out of date. Please update to the latest repository baseline.')
+ add_to_chat(123,'For details, visit https://github.com/Kinematics/GearSwap-Jobs/wiki/Upgrading')
+ rev = mote_include_version or 1
+ include_path('rev' .. tostring(rev))
+ include('Mote-Include')
+ return
+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
+
+ -- Global post-handling of this action
+ if not eventArgs.cancel and _G['user_post_'..action] then
+ _G['user_post_'..action](spell, action, spellMap, eventArgs)
+ 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 state.EquipStop.value == 'precast' then
+ eventArgs.cancel = true
+ end
+end
+
+function filter_aftercast(spell, spellMap, eventArgs)
+ if state.EquipStop.value == 'precast' or state.EquipStop.value == 'midcast' or state.EquipStop.value == '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 state.EquipStop.value == 'precast' or state.EquipStop.value == '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 state.EquipStop.value == 'precast' or state.EquipStop.value == 'midcast' or state.EquipStop.value == '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 state.EquipStop.value == '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 state.EquipStop.value == '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 state.EquipStop.value == '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.current] then
+ idleSet = idleSet[state.IdleMode.current]
+ mote_vars.set_breadcrumbs:append(state.IdleMode.current)
+ 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.has_value and meleeSet[state.CombatForm.value] then
+ meleeSet = meleeSet[state.CombatForm.value]
+ mote_vars.set_breadcrumbs:append(state.CombatForm.value)
+ end
+
+ if state.CombatWeapon.has_value and meleeSet[state.CombatWeapon.value] then
+ meleeSet = meleeSet[state.CombatWeapon.value]
+ mote_vars.set_breadcrumbs:append(state.CombatWeapon.value)
+ end
+
+ if meleeSet[state.OffenseMode.current] then
+ meleeSet = meleeSet[state.OffenseMode.current]
+ mote_vars.set_breadcrumbs:append(state.OffenseMode.current)
+ end
+
+ if meleeSet[state.HybridMode.current] then
+ meleeSet = meleeSet[state.HybridMode.current]
+ mote_vars.set_breadcrumbs:append(state.HybridMode.current)
+ 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.current] then
+ restingSet = restingSet[state.RestingMode.current]
+ mote_vars.set_breadcrumbs:append(state.RestingMode.current)
+ 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.current] then
+ equipSet = equipSet[state.CastingMode.current]
+ mote_vars.set_breadcrumbs:append(state.CastingMode.current)
+ 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.current] then
+ equipSet = equipSet[state.CastingMode.current]
+ mote_vars.set_breadcrumbs:append(state.CastingMode.current)
+ 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.current] then
+ equipSet = equipSet[state.CastingMode.current]
+ mote_vars.set_breadcrumbs:append(state.CastingMode.current)
+ end
+ elseif spell.action_type == 'Ability' then
+ if equipSet[state.OffenseMode.current] then
+ equipSet = equipSet[state.OffenseMode.current]
+ mote_vars.set_breadcrumbs:append(state.OffenseMode.current)
+ 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.current
+
+ 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.current ~= 'Normal' and state.WeaponskillMode:contains(state.RangedMode.current) then
+ ws_mode = state.RangedMode.current
+ end
+ else
+ if state.OffenseMode.current ~= 'Normal' and state.WeaponskillMode:contains(state.OffenseMode.current) then
+ ws_mode = state.OffenseMode.current
+ 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.has_value and equipSet[state.CombatForm.value] then
+ equipSet = equipSet[state.CombatForm.value]
+ mote_vars.set_breadcrumbs:append(state.CombatForm.value)
+ end
+
+ if state.CombatWeapon.has_value and equipSet[state.CombatWeapon.value] then
+ equipSet = equipSet[state.CombatWeapon.value]
+ mote_vars.set_breadcrumbs:append(state.CombatWeapon.value)
+ end
+
+ -- Check for specific mode for ranged attacks (eg: Acc, Att, etc)
+ if equipSet[state.RangedMode.current] then
+ equipSet = equipSet[state.RangedMode.current]
+ mote_vars.set_breadcrumbs:append(state.RangedMode.current)
+ 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.DefenseMode.current ~= 'None' then
+ local defenseSet = sets.defense
+
+ defenseSet = sets.defense[state[state.DefenseMode.current .. 'DefenseMode'].current] or defenseSet
+
+ for _,group in ipairs(classes.CustomDefenseGroups) do
+ defenseSet = defenseSet[group] or defenseSet
+ end
+
+ if customize_defense_set then
+ defenseSet = customize_defense_set(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.value 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/Mote-Mappings.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Mappings.lua
new file mode 100644
index 0000000..47e8b06
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Mappings.lua
@@ -0,0 +1,283 @@
+-------------------------------------------------------------------------------------------------------------------
+-- 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",
+ "Aurorastorm II", "Voidstorm II", "Firestorm II", "Sandstorm II", "Rainstorm II", "Windstorm II", "Hailstorm II", "Thunderstorm II"}
+
+elements.storm_of = {['Light']="Aurorastorm", ['Dark']="Voidstorm", ['Fire']="Firestorm", ['Earth']="Sandstorm",
+ ['Water']="Rainstorm", ['Wind']="Windstorm", ['Ice']="Hailstorm", ['Lightning']="Thunderstorm",['Light']="Aurorastorm II",
+ ['Dark']="Voidstorm II", ['Fire']="Firestorm II", ['Earth']="Sandstorm II", ['Water']="Rainstorm II", ['Wind']="Windstorm II",
+ ['Ice']="Hailstorm II", ['Lightning']="Thunderstorm II"}
+
+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',
+ ['Full Cure']='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',['Reraise IV']='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',['Refresh III']='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',
+ ['Fire Threnody II']='Threnody',['Ice Threnody II']='Threnody',['Wind Threnody II']='Threnody',['Earth Threnody II']='Threnody',['Lightning Threnody II']='Threnody',['Water Threnody II']='Threnody',['Light Threnody II']='Threnody',['Dark Threnody II']='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',['Utsusemi: San']='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',['Drain III']='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',
+ ['Enlight']='Enlight',['Enlight II']='Enlight',['Endark']='Endark',['Endark II']='Endark',
+ ['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',
+ ['Pyrohelix II']='Helix',['Cryohelix II']='Helix',['Anemohelix II']='Helix',['Geohelix II']='Helix',['Ionohelix II']='Helix',['Hydrohelix II']='Helix',['Luminohelix II']='Helix',['Noctohelix II']='Helix',
+ ['Firestorm']='Storm',['Hailstorm']='Storm',['Windstorm']='Storm',['Sandstorm']='Storm',['Thunderstorm']='Storm',['Rainstorm']='Storm',['Aurorastorm']='Storm',['Voidstorm']='Storm',
+ ['Firestorm II']='Storm',['Hailstorm II']='Storm',['Windstorm II']='Storm',['Sandstorm II']='Storm',['Thunderstorm II']='Storm',['Rainstorm II']='Storm',['Aurorastorm II']='Storm',['Voidstorm II']='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",
+ "Tavnazian Safehold",
+ "Nashmau",
+ "Selbina",
+ "Mhaura",
+ "Norg",
+ "Eastern Adoulin",
+ "Western Adoulin",
+ "Kazham",
+ "Rabao",
+ "Chocobo Circuit",
+}
+-- 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. (Not up to date)
+-------------------------------------------------------------------------------------------------------------------
+
+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/Mote-SelfCommands.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-SelfCommands.lua
new file mode 100644
index 0000000..3015e1f
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-SelfCommands.lua
@@ -0,0 +1,466 @@
+-------------------------------------------------------------------------------------------------------------------
+-- 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. Mappings are at the bottom of the file.
+-- 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
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Functions for manipulating state vars.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Function to set various states to specific values directly.
+-- User command format: gs c set [field] [value]
+-- If a boolean [field] is used, but not given a [value], it will be set to true.
+function handle_set(cmdParams)
+ if #cmdParams == 0 then
+ add_to_chat(123,'Mote-Libs: Set parameter failure: field not specified.')
+ return
+ end
+
+ local state_var = get_state(cmdParams[1])
+
+ if state_var then
+ local oldVal = state_var.value
+ state_var:set(cmdParams[2])
+ local newVal = state_var.value
+
+ local descrip = state_var.description or cmdParams[1]
+ if job_state_change then
+ job_state_change(descrip, newVal, oldVal)
+ end
+
+ local msg = descrip..' is now '..state_var.current
+ if state_var == state.DefenseMode and newVal ~= 'None' then
+ msg = msg .. ' (' .. state[newVal .. 'DefenseMode'].current .. ')'
+ end
+ msg = msg .. '.'
+
+ add_to_chat(122, msg)
+ handle_update({'auto'})
+ else
+ add_to_chat(123,'Mote-Libs: Set: Unknown field ['..cmdParams[1]..']')
+ end
+
+ -- handle string states: CombatForm, CombatWeapon, etc
+end
+
+-- Function to reset values to their defaults.
+-- User command format: gs c reset [field]
+-- Or: gs c reset all
+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
+
+ local state_var = get_state(cmdParams[1])
+
+ local oldVal
+ local newVal
+ local descrip
+
+ if state_var then
+ oldVal = state_var.value
+ state_var:reset()
+ newVal = state_var.value
+
+ local descrip = state_var.description or cmdParams[1]
+ if job_state_change then
+ job_state_change(descrip, newVal, oldVal)
+ end
+
+ add_to_chat(122,descrip..' is now '..state_var.current..'.')
+ handle_update({'auto'})
+ elseif cmdParams[1]:lower() == 'all' then
+ for k,v in pairs(state) do
+ if v._type == 'mode' then
+ oldVal = v.value
+ v:reset()
+ newVal = v.value
+
+ descrip = state_var.description
+ if descrip and job_state_change then
+ job_state_change(descrip, newVal, oldVal)
+ end
+ end
+ end
+
+ if job_reset_state then
+ job_reset_state('all')
+ end
+
+ if job_state_change then
+ job_state_change('Reset All')
+ end
+
+ add_to_chat(122,"All state vars have been reset.")
+ handle_update({'auto'})
+ elseif job_reset_state then
+ job_reset_state(cmdParams[1])
+ else
+ add_to_chat(123,'Mote-Libs: Reset: Unknown field ['..cmdParams[1]..']')
+ end
+end
+
+
+-- Handle cycling through the options list of a state var.
+-- User command format: gs c cycle [field]
+function handle_cycle(cmdParams)
+ if #cmdParams == 0 then
+ add_to_chat(123,'Mote-Libs: Cycle parameter failure: field not specified.')
+ return
+ end
+
+ local state_var = get_state(cmdParams[1])
+
+ if state_var then
+ local oldVal = state_var.value
+ if cmdParams[2] and S{'reverse', 'backwards', 'r'}:contains(cmdParams[2]:lower()) then
+ state_var:cycleback()
+ else
+ state_var:cycle()
+ end
+ local newVal = state_var.value
+
+ local descrip = state_var.description or cmdParams[1]
+ if job_state_change then
+ job_state_change(descrip, newVal, oldVal)
+ end
+
+ add_to_chat(122,descrip..' is now '..state_var.current..'.')
+ handle_update({'auto'})
+ else
+ add_to_chat(123,'Mote-Libs: Cycle: Unknown field ['..cmdParams[1]..']')
+ end
+end
+
+
+-- Handle cycling backwards through the options list of a state var.
+-- User command format: gs c cycleback [field]
+function handle_cycleback(cmdParams)
+ cmdParams[2] = 'reverse'
+ handle_cycle(cmdParams)
+end
+
+
+-- Handle toggling of boolean mode vars.
+-- User command format: gs c toggle [field]
+function handle_toggle(cmdParams)
+ if #cmdParams == 0 then
+ add_to_chat(123,'Mote-Libs: Toggle parameter failure: field not specified.')
+ return
+ end
+
+ local state_var = get_state(cmdParams[1])
+
+ if state_var then
+ local oldVal = state_var.value
+ state_var:toggle()
+ local newVal = state_var.value
+
+ local descrip = state_var.description or cmdParams[1]
+ if job_state_change then
+ job_state_change(descrip, newVal, oldVal)
+ end
+
+ add_to_chat(122,descrip..' is now '..state_var.current..'.')
+ handle_update({'auto'})
+ else
+ add_to_chat(123,'Mote-Libs: Toggle: Unknown field ['..cmdParams[1]..']')
+ end
+end
+
+
+-- Function to force a boolean field to false.
+-- User command format: gs c unset [field]
+function handle_unset(cmdParams)
+ if #cmdParams == 0 then
+ add_to_chat(123,'Mote-Libs: Unset parameter failure: field not specified.')
+ return
+ end
+
+ local state_var = get_state(cmdParams[1])
+
+ if state_var then
+ local oldVal = state_var.value
+ state_var:unset()
+ local newVal = state_var.value
+
+ local descrip = state_var.description or cmdParams[1]
+ if job_state_change then
+ job_state_change(descrip, newVal, oldVal)
+ end
+
+ add_to_chat(122,descrip..' is now '..state_var.current..'.')
+ handle_update({'auto'})
+ else
+ add_to_chat(123,'Mote-Libs: Toggle: Unknown field ['..cmdParams[1]..']')
+ end
+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
+
+
+-- showtp: equip the current TP set for examination.
+function handle_showtp(cmdParams)
+ local msg = 'Showing current TP set: ['.. state.OffenseMode.value
+ if state.HybridMode.value ~= 'Normal' then
+ msg = msg .. '/' .. state.HybridMode.value
+ end
+ msg = msg .. ']'
+
+ if #classes.CustomMeleeGroups > 0 then
+ msg = msg .. ' ['
+ for i = 1,#classes.CustomMeleeGroups do
+ msg = msg .. classes.CustomMeleeGroups[i]
+ if i < #classes.CustomMeleeGroups then
+ msg = msg .. ', '
+ end
+ end
+ msg = msg .. ']'
+ end
+
+ add_to_chat(122, msg)
+ equip(get_melee_set())
+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
+
+
+-------------------------------------------------------------------------------------------------------------------
+
+-- Get the state var that matches the requested name.
+-- Only returns mode vars.
+function get_state(name)
+ if state[name] then
+ return state[name]._class == 'mode' and state[name] or nil
+ else
+ local l_name = name:lower()
+ for key,var in pairs(state) do
+ if key:lower() == l_name then
+ return var._class == 'mode' and var or nil
+ end
+ end
+ end
+end
+
+
+-- Function to reset state.Buff values (called from update).
+function reset_buff_states()
+ if state.Buff then
+ for buff,present in pairs(state.Buff) do
+ if mote_vars.res_buffs:contains(buff) then
+ state.Buff[buff] = buffactive[buff] or false
+ end
+ end
+ 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 msg = 'Melee'
+
+ if state.CombatForm.has_value then
+ msg = msg .. ' (' .. state.CombatForm.value .. ')'
+ end
+
+ msg = msg .. ': '
+
+ msg = msg .. state.OffenseMode.value
+ if state.HybridMode.value ~= 'Normal' then
+ msg = msg .. '/' .. state.HybridMode.value
+ end
+ msg = msg .. ', WS: ' .. state.WeaponskillMode.value
+
+ if state.DefenseMode.value ~= 'None' then
+ msg = msg .. ', Defense: ' .. state.DefenseMode.value .. ' (' .. state[state.DefenseMode.value .. 'DefenseMode'].value .. ')'
+ end
+
+ if state.Kiting.value == true then
+ msg = msg .. ', Kiting'
+ end
+
+ if state.PCTargetMode.value ~= 'default' then
+ msg = msg .. ', Target PC: '..state.PCTargetMode.value
+ end
+
+ if state.SelectNPCTargets.value == true then
+ msg = msg .. ', Target NPCs'
+ end
+
+ add_to_chat(122, msg)
+ end
+
+ if state.EquipStop.value ~= 'off' then
+ add_to_chat(122,'Gear equips are blocked after ['..state.EquipStop.value..']. Use "//gs c reset equipstop" to turn it off.')
+ end
+end
+
+-- Generic version of this for casters
+function display_current_caster_state()
+ local msg = ''
+
+ if state.OffenseMode.value ~= 'None' then
+ msg = msg .. 'Melee'
+
+ if state.CombatForm.has_value then
+ msg = msg .. ' (' .. state.CombatForm.value .. ')'
+ end
+
+ msg = msg .. ', '
+ end
+
+ msg = msg .. 'Casting ['..state.CastingMode.value..'], Idle ['..state.IdleMode.value..']'
+
+ if state.DefenseMode.value ~= 'None' then
+ msg = msg .. ', ' .. 'Defense: ' .. state.DefenseMode.value .. ' (' .. state[state.DefenseMode.value .. 'DefenseMode'].value .. ')'
+ end
+
+ if state.Kiting.value == true then
+ msg = msg .. ', Kiting'
+ end
+
+ if state.PCTargetMode.value ~= 'default' then
+ msg = msg .. ', Target PC: '..state.PCTargetMode.value
+ end
+
+ if state.SelectNPCTargets.value == true then
+ msg = msg .. ', Target NPCs'
+ end
+
+ add_to_chat(122, msg)
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+
+-- Function to show what commands are available, and their syntax.
+-- Syntax: gs c help
+-- Or: gs c
+function handle_help(cmdParams)
+ if cmdParams[1] and cmdParams[1]:lower():startswith('field') then
+ print('Predefined Library Fields:')
+ print('--------------------------')
+ print('OffenseMode, HybridMode, RangedMode, WeaponskillMode')
+ print('CastingMode, IdleMode, RestingMode, Kiting')
+ print('DefenseMode, PhysicalDefenseMode, MagicalDefenseMode')
+ print('SelectNPCTargets, PCTargetMode')
+ print('EquipStop (precast, midcast, pet_midcast)')
+ else
+ print('Custom Library Self-commands:')
+ print('-----------------------------')
+ print('Show TP Set: gs c showtp')
+ print('Toggle bool: gs c toggle [field]')
+ print('Cycle list: gs c cycle [field] [(r)everse]')
+ print('Cycle list back: gs c cycleback [field]')
+ print('Reset a state: gs c reset [field]')
+ print('Reset all states: gs c reset all')
+ print('Set state var: gs c set [field] [value]')
+ print('Set bool true: gs c set [field]')
+ print('Set bool false: gs c unset [field]')
+ print('Remove gear: gs c naked')
+ print('Show TP Set: gs c showtp')
+ print('State vars: gs c help field')
+ end
+end
+
+
+-- A function for testing lua code. Called via "gs c test".
+function handle_test(cmdParams)
+ if user_test then
+ user_test(cmdParams)
+ elseif job_test then
+ job_test(cmdParams)
+ end
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- The below table maps text commands to the above handler functions.
+-------------------------------------------------------------------------------------------------------------------
+
+selfCommandMaps = {
+ ['toggle'] = handle_toggle,
+ ['cycle'] = handle_cycle,
+ ['cycleback']= handle_cycleback,
+ ['set'] = handle_set,
+ ['reset'] = handle_reset,
+ ['unset'] = handle_unset,
+ ['update'] = handle_update,
+ ['showtp'] = handle_showtp,
+ ['naked'] = handle_naked,
+ ['help'] = handle_help,
+ ['test'] = handle_test}
+
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-TreasureHunter.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-TreasureHunter.lua
new file mode 100644
index 0000000..d1fd52a
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/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 {}
+state.TreasureMode = M{['description']='Treasure Mode'}
+
+-- TH mode handling
+if player.main_job == 'THF' then
+ state.TreasureMode:options('None','Tag','SATA','Fulltime')
+ state.TreasureMode:set('Tag')
+else
+ state.TreasureMode:options('None','Tag')
+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.value ~= '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.value ~= '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.value == 'Fulltime' or
+ (state.TreasureMode.value == 'SATA' and (action.category == 1 or ((state.Buff['Sneak Attack'] or state.Buff['Trick Attack']) and action.category == 3))) or
+ (state.TreasureMode.value == '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 == 'Treasure Mode' 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/Mote-Utility.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Utility.lua
new file mode 100644
index 0000000..ad7a0ff
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-Utility.lua
@@ -0,0 +1,663 @@
+-------------------------------------------------------------------------------------------------------------------
+-- 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,copy image (2)')
+ 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
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- 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.value, SelectNPCTargets = state.SelectNPCTargets.value}
+
+ -- 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'] or player.wardrobe2['Twilight Cape'] or player.wardrobe3['Twilight Cape'] or player.wardrobe4['Twilight Cape'] then
+ gear.ElementalCape.name = "Twilight Cape"
+ end
+ if (player.inventory['Zodiac Ring'] or player.wardrobe['Zodiac Ring'] or player.wardrobe2['Zodiac Ring'] or player.wardrobe3['Zodiac Ring'] or player.wardrobe4['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]] or player.wardrobe2[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.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/Mote-documentation.txt b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/Mote-documentation.txt
new file mode 100644
index 0000000..8f17241
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/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/closetCleaner.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/closetCleaner.lua
new file mode 100644
index 0000000..50a35ef
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/closetCleaner.lua
@@ -0,0 +1,377 @@
+--Copyright © 2016-2017, Brimstone
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of closetCleaner nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL Brimstone BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-- _addon.version = '1.0'
+
+local cc = {}
+config = require ('config')
+cc.sandbox = {}
+cc.sandbox.windower = setmetatable({}, {__index = windower})
+cc.sandbox.windower.coroutine = functions.empty
+cc.sandbox.windower.register_event = functions.empty
+cc.sandbox.windower.raw_register_event = functions.empty
+cc.sandbox.windower.register_unhandled_command = functions.empty
+
+defaults = T{}
+-- Jobs you want to execute with, recomment put all active jobs you have lua for will look for <job>.lua or <playername>_<job>.lua files
+defaults.ccjobs = { 'BLM', 'BLU', 'BRD', 'BST', 'COR', 'DNC', 'DRG', 'DRK', 'GEO', 'MNK', 'NIN', 'PLD', 'PUP', 'RDM', 'RNG', 'RUN', 'SAM', 'SCH', 'SMN', 'THF', 'WAR', 'WHM' }
+-- Put any items in your inventory here you don't want to show up in the final report
+-- recommended for furniture, food, meds, pop items or any gear you know you want to keep for some reason
+-- use * for anything.
+defaults.ccignore = S{ "Rem's Tale*", "Storage Slip *" }
+-- Set to nil or delete for unlimited
+defaults.ccmaxuse = nil
+-- List bags you want to not check against, needs to match "Location" column in <player>_report.txt
+defaults.ccskipBags = S{ 'Storage', 'Temporary' }
+-- this prints out the _sets _ignored and _inventory files
+ccDebug = false
+
+settings = config.load('ccConfig.xml',defaults)
+
+register_unhandled_command(function(command)
+ command = command and command:lower() or nil
+ if command ~= 'cc' and command ~= 'closetcleaner' then
+ return
+ end
+ setmetatable(cc.sandbox, {__index = gearswap.user_env})
+ cc.sandbox.itemsBylongName = T{}
+ cc.sandbox.itemsByName = T{}
+ cc.sandbox.inventoryGear = T{}
+ cc.sandbox.gsGear = T{}
+ for k,v in pairs(gearswap.res.items) do
+ cc.sandbox.itemsBylongName[gearswap.res.items[k].name_log:lower()] = k
+ cc.sandbox.itemsByName[gearswap.res.items[k].name:lower()] = k
+ end
+ cc.sandbox.jobs = {}
+ for k,v in pairs(gearswap.res.jobs) do
+ cc.sandbox.jobs[gearswap.res.jobs[k].english_short] = k
+ end
+ if not windower.dir_exists(windower.addon_path..'report') then
+ windower.create_dir(windower.addon_path..'report')
+ end
+ local path = windower.addon_path:gsub('\\','/')
+ path = path..'report/'..player.name
+ cc.run_report(path)
+ cc.sandbox = {}
+ cc.sandbox.windower = setmetatable({}, {__index = windower})
+ cc.sandbox.windower.register_event = functions.empty
+ cc.sandbox.windower.raw_register_event = functions.empty
+ cc.sandbox.windower.register_unhandled_command = functions.empty
+ return true
+end)
+
+-- This function creates the report and generates the calls to the other functions
+function cc.run_report(path)
+ mainReportName = path..'_report.txt'
+ local f = io.open(mainReportName,'w+')
+ f:write('closetCleaner Report:\n')
+ f:write('=====================\n\n')
+ cc.export_inv(path)
+ cc.export_sets(path)
+ for k,v in pairs(cc.sandbox.inventoryGear) do
+ if cc.sandbox.gsGear[k] == nil then
+ cc.sandbox.gsGear[k] = 0
+ end
+ end
+ data = T{"Name", " | ", "Count", " | ", "Location", " | ", "Jobs Used", " | ", "Long Name"}
+ form = T{"%25s", "%3s", "%10s", "%3s", "%20s", "%3s", "%-88s", "%3s", "%60s"}
+ cc.print_row(f, data, form)
+ cc.print_break(f, form)
+ if ccDebug then
+ ignoredReportName = path..'_ignored.txt'
+ f2 = io.open(ignoredReportName,'w+')
+ f2:write('closetCleaner ignored Report:\n')
+ f2:write('=====================\n\n')
+ cc.print_row(f2, data, form)
+ cc.print_break(f2, form)
+ end
+ for k,v in cc.spairs(cc.sandbox.gsGear, function(t,a,b) return t[b] > t[a] end) do
+ if settings.ccmaxuse == nil or v <= settings.ccmaxuse then
+ printthis = 1
+ if not cc.job_used[k] then
+ cc.job_used[k] = " "
+ end
+ for s in pairs(settings.ccignore) do
+ if windower.wc_match(gearswap.res.items[k].english, s) then
+ printthis = nil
+ if cc.sandbox.inventoryGear[k] == nil then
+ data = T{gearswap.res.items[k].english, " | ", tostring(v), " | ", "NOT FOUND", " | ", cc.job_used[k], " | ", gearswap.res.items[k].english_log}
+ else
+ data = T{gearswap.res.items[k].english, " | ", tostring(v), " | ", cc.sandbox.inventoryGear[k], " | ", cc.job_used[k], " | ", gearswap.res.items[k].english_log}
+ end
+ if ccDebug then
+ cc.print_row(f2, data, form)
+ end
+ break
+ end
+ end
+ if printthis then
+ if cc.sandbox.inventoryGear[k] == nil then
+ data = T{gearswap.res.items[k].english, " | ", tostring(v), " | ", "NOT FOUND", " | ", cc.job_used[k], " | ", gearswap.res.items[k].english_log}
+ else
+ data = T{gearswap.res.items[k].english, " | ", tostring(v), " | ", cc.sandbox.inventoryGear[k], " | ", cc.job_used[k], " | ", gearswap.res.items[k].english_log}
+ end
+ cc.print_row(f, data, form)
+ end
+ end
+ end
+ if ccDebug then
+ f2:close()
+ print("File created: "..ignoredReportName)
+ end
+ f:close()
+ print("File created: "..mainReportName)
+end
+
+ -- This function tallies all the gear in your inventory
+function cc.export_inv(path)
+ if ccDebug then
+ reportName = path..'_inventory.txt'
+ finv = io.open(reportName,'w+')
+ finv:write('closetCleaner Inventory Report:\n')
+ finv:write('=====================\n\n')
+ end
+
+ local item_list = T{}
+ checkbag = true
+ for n = 0, #gearswap.res.bags do
+ if not settings.ccskipBags:contains(gearswap.res.bags[n].english) then
+ for i,v in ipairs(gearswap.get_item_list(gearswap.items[gearswap.res.bags[n].english:gsub(' ', ''):lower()])) do
+ if v.name ~= empty then
+ local slot = gearswap.xmlify(tostring(v.slot))
+ local name = gearswap.xmlify(tostring(v.name)):gsub('NUM1','1')
+
+ if cc.sandbox.itemsByName[name:lower()] ~= nil then
+ itemid = cc.sandbox.itemsByName[name:lower()]
+ elseif cc.sandbox.itemsBylongName[name:lower()] ~= nil then
+ itemid = cc.sandbox.itemsBylongName[name:lower()]
+ else
+ print("Item: "..name.." not found in gearswap.resources!")
+ end
+ if ccDebug then
+ finv:write("Name: "..name.." Slot: "..slot.." Bag: "..gearswap.res.bags[n].english.."\n")
+ end
+ if cc.sandbox.inventoryGear[itemid] == nil then
+ cc.sandbox.inventoryGear[itemid] = gearswap.res.bags[n].english
+ else
+ cc.sandbox.inventoryGear[itemid] = cc.sandbox.inventoryGear[itemid]..", "..gearswap.res.bags[n].english
+ end
+ end
+ end
+ end
+ end
+ if ccDebug then
+ finv:close()
+ print("File created: "..reportName)
+ end
+end
+
+-- loads all the relevant jobs.lua files and inserts the sets tables into a supersets table:
+-- supersets.<JOB>.sets....
+function cc.export_sets(path)
+ if ccDebug then
+ reportName = path..'_sets.txt'
+ fsets = io.open(reportName,'w+')
+ fsets:write('closetCleaner sets Report:\n')
+ fsets:write('=====================\n\n')
+ end
+ cc.supersets = {}
+ cc.job_used = T{}
+ cc.job_logged = T()
+ fpath = windower.addon_path:gsub('\\','/')
+ fpath = fpath:gsub('//','/')
+ fpath = string.lower(fpath)
+ dpath = fpath..'data/'
+ for i,v in ipairs(settings.ccjobs) do
+ dname = string.lower(dpath..player.name..'/'..v..'.lua')
+ lname = string.lower(dpath..player.name..'_'..v..'.lua')
+ lgname = string.lower(dpath..player.name..'_'..v..'_gear.lua')
+ sname = string.lower(dpath..v..'.lua')
+ sgname = string.lower(dpath..v..'_gear.lua')
+ if windower.file_exists(lgname) then
+ cc.supersets[v] = cc.extract_sets(lgname)
+ elseif windower.file_exists(lname) then
+ cc.supersets[v] = cc.extract_sets(lname)
+ elseif windower.file_exists(sgname) then
+ cc.supersets[v] = cc.extract_sets(sgname)
+ elseif windower.file_exists(sname) then
+ cc.supersets[v] = cc.extract_sets(sname)
+ elseif windower.file_exists(dname) then
+ cc.supersets[v] = cc.extract_sets(dname)
+ else
+ print('lua file for '..v..' not found!')
+ end
+ end
+ cc.list_sets(cc.supersets, fsets)
+ cc.supersets = nil
+ if ccDebug then
+ fsets:close()
+ print("File created: "..reportName)
+ end
+end
+
+-- sets the 'sets' and puts them into supersets based off file name.
+function cc.extract_sets(file)
+ local user_file = gearswap.loadfile(file)
+ if user_file then
+ gearswap.setfenv(user_file, cc.sandbox)
+ cc.sandbox.sets = {}
+ user_file()
+ local def_gear = cc.sandbox.init_get_sets or cc.sandbox.get_sets
+ if def_gear then
+ def_gear()
+ end
+ return table.copy(cc.sandbox.sets)
+ else
+ print('lua file for '..file..' not found!')
+ end
+end
+
+-- this function tallies the items used in each lua file
+function cc.list_sets(t, f)
+ write_sets = T{}
+ local print_r_cache={}
+ local function sub_print_r(t,fromTab)
+ if (type(t)=="table") then
+ for pos,val in pairs(t) do
+ if S{"WAR", "MNK", "WHM", "BLM", "RDM", "THF", "PLD", "DRK", "BST", "BRD", "RNG", "SAM", "NIN", "DRG", "SMN", "BLU", "COR", "PUP", "DNC", "SCH", "GEO", "RUN"}:contains(pos) then
+ job = pos
+ end
+ if (type(val)=="table") then
+ sub_print_r(val,job)
+ elseif (type(val)=="string") then
+ if val ~= "" and val ~= "empty" then
+ if S{"name", "main", "sub", "range", "ammo", "head", "neck", "left_ear", "right_ear", "body", "hands", "left_ring", "right_ring", "back", "waist", "legs", "feet", "ear1", "ear2", "ring1", "ring2", "lear", "rear", "lring", "rring"}:contains(pos) then
+ if cc.sandbox.itemsByName[val:lower()] ~= nil then
+ itemid = cc.sandbox.itemsByName[val:lower()]
+ elseif cc.sandbox.itemsBylongName[val:lower()] ~= nil then
+ itemid = cc.sandbox.itemsBylongName[val:lower()]
+ else
+ print("Item: '"..val.."' not found in gearswap.resources! "..pos)
+ end
+ if ccDebug then
+ f:write('Processing '..job..' name for val '..val..' id '..itemid..'\n')
+ end
+ if write_sets[itemid] == nil then
+ write_sets[itemid] = 1
+ if cc.job_used[itemid] == nil then
+ cc.job_used[itemid] = job
+ cc.job_logged[itemid..job] = 1
+ else
+ cc.job_used[itemid] = cc.job_used[itemid]..","..job
+ cc.job_logged[itemid..job] = 1
+ end
+ else
+ write_sets[itemid] = write_sets[itemid] + 1
+ if cc.job_logged[itemid..job] == nil then
+ cc.job_used[itemid] = cc.job_used[itemid]..","..job
+ cc.job_logged[itemid..job] = 1
+ end
+ end
+ end
+ end
+ elseif (type(val)=="number") then
+ print("Found Number: "..val.." from "..pos.." table "..t)
+ else
+ print("Error: Val needs to be table or string "..type(val))
+ end
+ end
+ end
+ end
+ sub_print_r(t,nil)
+ if ccDebug then
+ data = T{"Name", " | ", "Count", " | ", "Jobs", " | ", "Long Name"}
+ form = T{"%22s", "%3s", "%10s", "%3s", "%88s", "%3s", "%60s"}
+ cc.print_row(f, data, form)
+ cc.print_break(f, form)
+ f:write('\n')
+ for k,v in pairs(write_sets) do
+ data = T{gearswap.res.items[k].english, " | ", tostring(v), " | ", cc.job_used[k], " | ", gearswap.res.items[k].english_log}
+ cc.print_row(f, data, form)
+ cc.sandbox.gsGear[k] = v
+ end
+ f:write()
+ else
+ for k,v in pairs(write_sets) do
+ cc.sandbox.gsGear[k] = v
+ end
+ end
+end
+
+-- interate throught table in a sorted order.
+function cc.spairs(t, order)
+ -- collect the keys
+ local keys = {}
+ for k in pairs(t) do keys[#keys+1] = k end
+
+ -- if order function given, sort by it by passing the table and keys a, b,
+ -- otherwise just sort the keys
+ if order then
+ table.sort(keys, function(a,b) return order(t, a, b) end)
+ else
+ table.sort(keys)
+ end
+
+ -- return the iterator function
+ local i = 0
+ return function()
+ i = i + 1
+ if keys[i] then
+ return keys[i], t[keys[i]]
+ end
+ end
+end
+
+-- pass in file handle and a table of formats and table of data
+function cc.print_row(f, data, form)
+ for k,v in pairs(data) do
+ f:write(string.format(form[k], v))
+ end
+ f:write('\n')
+end
+
+-- pass in file handle and a table of formats and table of data
+function cc.print_break(f, form)
+ for k,v in pairs(form) do
+ number = string.match(v,"%d+")
+ for i=1,number do
+ f:write('-')
+ end
+ -- f:write(' ') -- can add characters to end here like spaces but subtract from number in the for loop above
+ end
+ f:write('\n')
+end
+
+
+function cc.include(str)
+ str = str:lower()
+ if not (str == 'closetcleaner' or str == 'closetcleaner.lua') then
+ include(str, cc.sandbox)
+ end
+end
+
+cc.sandbox.include = cc.include
+cc.sandbox.require = cc.include \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/organizer-lib.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/organizer-lib.lua
new file mode 100644
index 0000000..c3111f6
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/libs/organizer-lib.lua
@@ -0,0 +1,237 @@
+-- Organizer library v2
+
+local org = {}
+register_unhandled_command(function(...)
+ local cmds = {...}
+ for _,v in ipairs(cmds) do
+ if S{'organizer','organize','org','o'}:contains(v:lower()) then
+ org.export_set()
+ return true
+ end
+ end
+ return false
+end)
+
+
+function org.export_set()
+ if not sets then
+ windower.add_to_chat(123,'Organizer Library: Cannot export your sets for collection because the table is nil.')
+ return
+ elseif not windower.dir_exists(windower.windower_path..'addons/organizer/') then
+ windower.add_to_chat(123,'Organizer Library: The organizer addon is not installed. Activate it in the launcher.')
+ return
+ end
+
+ -- Makes a big table keyed to item resource tables, with values that are 1-based
+ -- numerically indexed tables of different entries for each of the items from the sets table.
+ local item_list = org.unpack_names({},'L1',sets,{})
+
+ local trans_item_list = org.identify_items(item_list)
+
+ for i,v in pairs(trans_item_list) do
+ trans_item_list[i] = org.simplify_entry(v)
+ end
+
+ if trans_item_list:length() == 0 then
+ windower.add_to_chat(123,'Organizer Library: Your sets table is empty.')
+ return
+ end
+
+ local flattab = T{}
+ for name,tab in pairs(trans_item_list) do
+ for _,info in ipairs(tab) do
+ flattab:append({id=tab.id,name=tab.name,log_name=tab.log_name,augments=info.augments,count=info.count})
+ end
+ end
+
+ -- See if we have any non-equipment items to drag along
+ if organizer_items then
+ local organizer_item_list = org.unpack_names({}, 'L1', organizer_items, {})
+
+ for _,tab in pairs(org.identify_items(organizer_item_list)) do
+ count = gearswap.res.items[tab.id].stack
+ flattab:append({id=tab.id,name=tab.name,log_name=tab.log_name,count=count})
+ end
+ end
+
+ -- At this point I have a table of equipment pieces indexed by the inventory name.
+ -- I need to make a function that will translate that into a list of pieces in
+ -- inventory or wardrobe.
+ -- #trans_item_list[i] = Number of a given item
+ -- trans_item_list[i].id = item ID
+
+ local ward_ids = {8,10,11,12}
+ local wardrobes = {}
+ local ward = {}
+
+ for _,id in pairs(ward_ids) do
+ wardrobes[id] = windower.ffxi.get_items(id)
+ wardrobes[id].max = windower.ffxi.get_bag_info(id).max
+ ward[id] = T{}
+ end
+
+ local inv = T{}
+ for i,v in ipairs(flattab) do
+ local found
+ local ward_id
+ -- Iterate over the wardrobes and look for gear from the list that is already in wardrobes, then eliminate it from the list
+ for id,wardrobe in pairs(wardrobes) do
+ for n,m in ipairs(wardrobe) do
+ if m.id == v.id and (not v.augments or v.augments and gearswap.extdata.decode(m).augments and gearswap.extdata.compare_augments(v.augments,gearswap.extdata.decode(m).augments)) then
+ found = n
+ break
+ end
+ end
+ if found then
+ ward_id = id
+ break
+ end
+ end
+ if found then
+ table.remove(wardrobes[ward_id],found)
+ ward[ward_id]:append(v)
+ else
+ inv:append(v)
+ end
+ end
+
+ local inventory_max = windower.ffxi.get_bag_info(0).max
+
+ for id=1,4 do
+ if #inv > inventory_max and #ward[id] + (#inv-inventory_max) < wardrobes[id].max then
+ local available = wardrobes[id].max - #ward[id]
+ local length = math.min(#inv-80,available)
+ ward:extend(inv:slice(1-length))
+ end
+ end
+
+ if #inv > inventory_max then
+ windower.add_to_chat(123,'Organizer Library: Your sets table contains too many items.')
+ return
+ end
+
+ -- Scan wardrobe, eliminate items from your table that are in wardrobe
+ -- Scan inventory
+
+ local fi = file.new('../organizer/data/inventory/organizer-lib-file.lua')
+ fi:write('-- Generated by the Organizer Library ('..os.date()..')\nreturn '..(inv:tovstring({'augments','log_name','name','id','count'})))
+
+ for _,id in ipairs(ward_ids) do
+ local fw = file.new('../organizer/data/'..gearswap.res.bags[id].command..'/organizer-lib-file.lua')
+ fw:write('-- Generated by the Organizer Library ('..os.date()..')\nreturn '..(ward[id]:tovstring({'augments','log_name','name','id','count'})))
+ end
+
+ windower.send_command('wait 0.5;org o organizer-lib-file')
+end
+
+function org.simplify_entry(tab)
+ -- Some degree of this needs to be done in unpack_names or I won't be able to detect when two identical augmented items are equipped.
+ local output = T{id=tab.id,name=tab.name,log_name=tab.log_name}
+ local rare = gearswap.res.items[tab.id].flags:contains('Rare')
+ for i,v in ipairs(tab) do
+ local handled = false
+ if v.augment then
+ v.augments = {v.augment}
+ v.augment = nil
+ end
+
+ for n,m in ipairs(output) do
+ if (not v.bag or v.bag and v.bag == m.bag) and v.slot == m.slot and
+ (not v.augments or ( m.augments and gearswap.extdata.compare_augments(v.augments,m.augments))) then
+ output[n].count = math.min(math.max(output[n].count,v.count),gearswap.res.items[tab.id].stack)
+ handled = true
+ break
+ elseif (not v.bag or v.bag and v.bag == m.bag) and v.slot == m.slot and v.augments and not m.augments then
+ -- v has augments, but there currently exists a matching version of the
+ -- item without augments in the output table. Replace the entry with the augmented entry
+ local countmax = math.min(math.max(output[n].count,v.count),gearswap.res.items[tab.id].stack)
+ output[n] = v
+ output[n].count = countmax
+ handled = true
+ break
+ elseif rare then
+ handled = true
+ break
+ end
+ end
+ if not handled then
+ output:append(v)
+ end
+
+ end
+ return output
+end
+
+function org.identify_items(tab)
+ local name_to_id_map = {}
+ local items = windower.ffxi.get_items()
+ for id,inv in pairs(items) do
+ if type(inv) == 'table' then
+ for ind,item in ipairs(inv) do
+ if type(item) == 'table' and item.id and item.id ~= 0 then
+ name_to_id_map[gearswap.res.items[item.id][gearswap.language]:lower()] = item.id
+ name_to_id_map[gearswap.res.items[item.id][gearswap.language..'_log']:lower()] = item.id
+ end
+ end
+ end
+ end
+ local trans = T{}
+ for i,v in pairs(tab) do
+ local item = name_to_id_map[i:lower()] and table.reassign({},gearswap.res.items[name_to_id_map[i:lower()]]) --and org.identify_unpacked_name(i,name_to_id_map)
+ if item then
+ local n = gearswap.res.items[item.id][gearswap.language]:lower()
+ local ln = gearswap.res.items[item.id][gearswap.language..'_log']:lower()
+ if not trans[n] then
+ trans[n] = T{id=item.id,
+ name=n,
+ log_name=ln,
+ }
+ end
+ trans[n]:extend(v)
+ end
+ end
+ return trans
+end
+
+function org.unpack_names(ret_tab,up,tab_level,unpacked_table)
+ for i,v in pairs(tab_level) do
+ local flag = false
+ if type(v)=='table' and i ~= 'augments' and not ret_tab[tostring(tab_level[i])] then
+ ret_tab[tostring(tab_level[i])] = true
+ unpacked_table, ret_tab = org.unpack_names(ret_tab,i,v,unpacked_table)
+ elseif i=='name' then
+ -- v is supposed to be a name, then.
+ flag = true
+ elseif type(v) == 'string' and v~='augment' and v~= 'augments' and v~= 'priority' then
+ -- v is a string that's not any known option of gearswap, so treat it as an item name.
+ -- I really need to make a set of the known advanced table options and use that instead.
+ flag = true
+ end
+ if flag then
+ local n = tostring(v):lower()
+ if not unpacked_table[n] then unpacked_table[n] = {} end
+ local ind = #unpacked_table[n] + 1
+ if i == 'name' and gearswap.slot_map[tostring(up):lower()] then -- Advanced Table
+ unpacked_table[n][ind] = tab_level
+ unpacked_table[n][ind].count = unpacked_table[n][ind].count or 1
+ unpacked_table[n][ind].slot = gearswap.slot_map[up:lower()]
+ elseif gearswap.slot_map[tostring(i):lower()] then
+ unpacked_table[n][ind] = {slot=gearswap.slot_map[i:lower()],count=1}
+ end
+ end
+ end
+ return unpacked_table, ret_tab
+end
+
+function org.string_augments(tab)
+ local aug_str = ''
+ if tab.augments then
+ for aug_ind,augment in pairs(tab.augments) do
+ if augment ~= 'none' then aug_str = aug_str..'['..aug_ind..'] = '..'"'..augment..'",\n' end
+ end
+ end
+ if tab.augment then
+ if tab.augment ~= 'none' then aug_str = aug_str.."'"..augment.."'," end
+ end
+ if aug_str ~= '' then return '{\n'..aug_str..'}' end
+end \ No newline at end of file
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.
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/packet_parsing.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/packet_parsing.lua
new file mode 100644
index 0000000..f8cabf3
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/packet_parsing.lua
@@ -0,0 +1,716 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+parse = {
+ i={}, -- Incoming packets
+ o={} -- Outgoing packets, currently none are really parsed for information
+ }
+
+parse.i[0x00A] = function (data)
+ windower.debug('zone change')
+ command_registry = Command_Registry.new()
+ table.clear(not_sent_out_equip)
+
+ player.id = data:unpack('I',0x05)
+ player.index = data:unpack('H',0x09)
+ if player.main_job_id and player.main_job_id ~= data:byte(0xB5) and player.name and player.name == data:unpack('z',0x85) and not gearswap_disabled then
+ windower.debug('job change on zone')
+ load_user_files(data:byte(0xB5))
+ else
+ player.name = data:unpack('z',0x85)
+ end
+ player.main_job_id = data:byte(0xB5)
+ player.sub_job_id = data:byte(0xB8)
+ player.vitals.max_hp = data:unpack('I',0xE9)
+ player.vitals.max_mp = data:unpack('I',0xED)
+ player.max_hp = data:unpack('I',0xE9)
+ player.max_mp = data:unpack('I',0xED)
+ update_job_names()
+
+ world.zone_id = data:unpack('H',0x31)
+ _ExtraData.world.conquest = false
+ for i,v in pairs(region_to_zone_map) do
+ if v:contains(world.zone_id) then
+ _ExtraData.world.conquest = {
+ region_id = i,
+ region_name = res.regions[i][language],
+ }
+ break
+ end
+ end
+ weather_update(data:byte(0x69))
+ world.logged_in = true
+
+ _ExtraData.world.in_mog_house = data:byte(0x81) == 1
+
+ _ExtraData.player.base_str = data:unpack('H',0xCD)
+ _ExtraData.player.base_dex = data:unpack('H',0xCF)
+ _ExtraData.player.base_vit = data:unpack('H',0xD1)
+ _ExtraData.player.base_agi = data:unpack('H',0xD3)
+ _ExtraData.player.base_int = data:unpack('H',0xD5)
+ _ExtraData.player.base_mnd = data:unpack('H',0xD7)
+ _ExtraData.player.base_chr = data:unpack('H',0xD9)
+ _ExtraData.player.add_str = data:unpack('h',0xDB)
+ _ExtraData.player.add_dex = data:unpack('h',0xDD)
+ _ExtraData.player.add_vit = data:unpack('h',0xDF)
+ _ExtraData.player.add_agi = data:unpack('h',0xE1)
+ _ExtraData.player.add_int = data:unpack('h',0xE3)
+ _ExtraData.player.add_mnd = data:unpack('h',0xE5)
+ _ExtraData.player.add_chr = data:unpack('h',0xE7)
+
+ _ExtraData.player.str = _ExtraData.player.base_str + _ExtraData.player.add_str
+ _ExtraData.player.dex = _ExtraData.player.base_dex + _ExtraData.player.add_dex
+ _ExtraData.player.vit = _ExtraData.player.base_vit + _ExtraData.player.add_vit
+ _ExtraData.player.agi = _ExtraData.player.base_agi + _ExtraData.player.add_agi
+ _ExtraData.player.int = _ExtraData.player.base_int + _ExtraData.player.add_int
+ _ExtraData.player.mnd = _ExtraData.player.base_mnd + _ExtraData.player.add_mnd
+ _ExtraData.player.chr = _ExtraData.player.base_chr + _ExtraData.player.add_chr
+ refresh_ffxi_info()
+
+ blank_0x063_v9_inc = true
+end
+
+parse.i[0x00B] = function(data)
+ -- Blank temporary items when zoning.
+ items.temporary = make_inventory_table()
+end
+
+parse.i[0x00E] = function (data)
+ if pet.index and pet.index == data:unpack('H',9) and math.floor((data:byte(11)%8)/4)== 1 then
+ local status_id = data:byte(32)
+ -- Filter all statuses aside from Idle/Engaged/Dead/Engaged dead.
+ if pet.status_id ~= status_id and (status_id < 4 or status_id == 33 or status_id == 47) then
+ if not next_packet_events then next_packet_events = {sequence_id = data:unpack('H',3)} end
+ next_packet_events.pet_status_change = {newstatus=res.statuses[status_id][language],oldstatus=pet.status}
+ pet.status = res.statuses[status_id][language]
+ pet.status_id = status_id
+ end
+ end
+end
+
+parse.i[0x01B] = function (data)
+ for job_id = 1,23 do
+ player.jobs[to_windower_api(res.jobs[job_id].english)] = data:byte(job_id + 72)
+ end
+
+ local enc = data:unpack('H',0x61)
+ local tab = {}
+ for slot_id,slot_name in pairs(default_slot_map) do
+ local tf = (((enc%(2^(slot_id+1))) / 2^slot_id) >= 1)
+ if encumbrance_table[slot_id] and not tf and not_sent_out_equip[slot_name] and not disable_table[i] then
+ tab[slot_name] = not_sent_out_equip[slot_name]
+ not_sent_out_equip[slot_name] = nil
+ end
+ if encumbrance_table[slot_id] and not tf then
+ msg.debugging("Your "..slot_name.." slot is now unlocked.")
+ end
+ encumbrance_table[slot_id] = tf
+ end
+ if table.length(tab) > 0 and not gearswap_disabled then
+ refresh_globals()
+ equip_sets('equip_command',nil,tab)
+ end
+end
+
+parse.i[0x01E] = function (data)
+ local bag = to_windower_compact(res.bags[data:byte(0x09)].english)
+ local slot = data:byte(0x0A)
+ local count = data:unpack('I',5)
+ local status = data:byte(0x0B)
+ if not items[bag][slot] then items[bag][slot] = make_empty_item_table(slot) end
+ items[bag][slot].count = count
+ items[bag][slot].status = status
+ if count == 0 then
+ items[bag][slot].id = 0
+ items[bag][slot].bazaar = 0
+ items[bag][slot].status = 0
+ end
+end
+
+parse.i[0x01F] = function (data)
+ local bag = to_windower_compact(res.bags[data:byte(0x0B)].english)
+ local slot = data:byte(0x0C)
+ if not items[bag][slot] then items[bag][slot] = make_empty_item_table(slot) end
+ items[bag][slot].id = data:unpack('H',9)
+ items[bag][slot].count = data:unpack('I',5)
+ items[bag][slot].status = data:byte(0x0D)
+end
+
+parse.i[0x020] = function (data)
+ local bag = to_windower_compact(res.bags[data:byte(0x0F)].english)
+ local slot = data:byte(0x10)
+ if not items[bag][slot] then items[bag][slot] = make_empty_item_table(slot) end
+ items[bag][slot].id = data:unpack('H',0x0D)
+ items[bag][slot].count = data:unpack('I',5)
+ items[bag][slot].bazaar = data:unpack('I',9)
+ items[bag][slot].status = data:byte(0x11)
+ items[bag][slot].extdata = data:sub(0x12,0x29)
+ -- Did not mess with linkshell stuff
+end
+
+parse.i[0x037] = function (data)
+ player.status_id = data:byte(0x31)
+ --[[local bitmask = data:sub(0x4D,0x54)
+ for i = 1,32 do
+ local bitmask_position = 2*((i-1)%4)
+ local id = data:byte(4+i) + 256*math.floor(bitmask:byte(1+math.floor((i-1)/4))%(2^(bitmask_position+2))/(2^bitmask_position))
+ if player.buffs[i] ~= id then
+ if id == 255 and player.buffs[i] then
+ player.buffs[i] = nil
+ elseif id ~= 255 then
+ player.buffs[i] = id
+ end
+ end
+ end]]
+
+ local indi_byte = data:byte(0x59)
+ if indi_byte%128/64 >= 1 then
+ local temp_indi = _ExtraData.player.indi
+ _ExtraData.player.indi = {
+ element = res.elements[indi_byte%8][language],
+ element_id = indi_byte%8,
+ size = math.floor((indi_byte%64)/16) + 1, -- Size range of 1~4
+ }
+ if (indi_byte%16)/8 >= 1 then
+ _ExtraData.player.indi.target = 'Enemy'
+ else
+ _ExtraData.player.indi.target = 'Ally'
+ end
+ if not gearswap_disabled then
+ if not temp_indi then
+ -- There was not an indi spell up
+ refresh_globals()
+ equip_sets('indi_change',nil,_ExtraData.player.indi,true)
+ elseif temp_indi.element_id ~= _ExtraData.player.indi.element_id or temp_indi.target ~= _ExtraData.player.indi.target or temp_indi.size ~= _ExtraData.player.indi.size then
+ -- There was already an indi spell up, so check if it changed
+ refresh_globals()
+ equip_sets('indi_change',nil,temp_indi,false)
+ equip_sets('indi_change',nil,_ExtraData.player.indi,true)
+ end
+ end
+ elseif _ExtraData.player.indi then
+ -- An indi effect has been lost.
+ local temp_indi = _ExtraData.player.indi
+ _ExtraData.player.indi = nil
+ if not gearswap_disabled then
+ refresh_globals()
+ equip_sets('indi_change',nil,temp_indi,false)
+ end
+ end
+
+ local subj_ind = data:unpack('H', 0x35) / 8
+ if subj_ind == 0 and pet.isvalid then
+ if not next_packet_events then next_packet_events = {sequence_id = data:unpack('H',3)} end
+ refresh_globals()
+ pet.isvalid = false
+ _ExtraData.pet = {}
+ next_packet_events.pet_change = {pet = table.reassign({},pet)}
+ elseif subj_ind ~= 0 and not pet.isvalid then
+ if not next_packet_events then next_packet_events = {sequence_id = data:unpack('H',3)} end
+ _ExtraData.pet.tp = 0
+ next_packet_events.pet_change = {subj_ind = subj_ind}
+ end
+end
+
+function parse_equip_chunk(chunk)
+ local inv_slot = chunk:byte(1) -- 0 indicates unequipping the item
+ local equip_slot = toslotname(chunk:byte(2))
+ if inv_slot == 0 then -- Unequipping
+ local bag_id = items.equipment[equip_slot].bag_id
+ inv_slot = items.equipment[equip_slot].slot
+
+ if inv_slot == empty then return end -- unequipping something that was already unequipped?
+
+ local inv = items[to_windower_compact(res.bags[bag_id].english)]
+ if not inv[inv_slot] then inv[inv_slot] = make_empty_item_table(inv_slot) end
+
+ inv[inv_slot].status = 0 -- Set the status to "unequipped"
+ items.equipment[equip_slot] = {slot=empty,bag_id=0}
+ else
+ local bag_id = chunk:byte(3)
+ local inv = items[to_windower_compact(res.bags[bag_id].english)]
+ items.equipment[equip_slot] = {slot=inv_slot,bag_id = bag_id}
+ if not inv[inv_slot] then inv[inv_slot] = make_empty_item_table(inv_slot) end
+ inv[inv_slot].status = 5 -- Set the status to "equipped"
+ end
+end
+
+parse.o[0x050] = function (data,injected) --equip
+ if injected then return end
+ -- Because of the way windower works, uninjected chunks will appear after
+ -- injected chunks in the chunk events but will hit the server before them.
+ -- Thus, I use insert here instead of append
+ injected_equipment_registry[data:byte(6)]:insert(1,data:sub(5,7))
+end
+
+parse.o[0x051] = function (data,injected) --equipset
+ if injected then return end
+ for i=9,9+4*(data:byte(5)-1),4 do
+ injected_equipment_registry[data:byte(i+1)]:insert(1,data:sub(i,i+2))
+ end
+end
+
+parse.i[0x050] = function (data)
+ -- should simplify this code using return when I gain confidence in it
+ parse_equip_chunk(data:sub(5,7))
+ local slot = data:byte(6)
+ for chunk,ind in injected_equipment_registry[slot]:it() do
+ if chunk == data:sub(5,7) then
+ -- Matched
+ injected_equipment_registry[slot] = injected_equipment_registry[slot]:slice(ind+1) -- Eliminate current and all preceding packets if we get a match
+ matched = true
+ return
+ end
+ --[[for i=9,9+4*(chunk:byte(5)-1),4 do -- The server replies to equipset packets with both single equip packets and equipset packets.
+ if chunk:sub(i,i+2) == data:sub(5,7) then
+ matched = true
+ break
+ end
+ end]]
+ end
+ -- Unexpected packet found!
+end
+
+function update_equipment()
+ local tab = {}
+ for i,v in pairs(items.equipment) do
+ tab[i] = {bag_id = v.bag_id,slot=v.slot}
+ end
+ for i,v in pairs(injected_equipment_registry) do
+ local last = v:last()
+ if last then
+ tab[default_slot_map[i]] = {
+ bag_id = last:byte(3),
+ slot = last:byte(1) == 0 and empty or last:byte(1),
+ }
+ end
+ end
+ return tab
+end
+
+-- The server always sends both equip responses and equipset responses following an equipset command
+-- Equip responses are sent for pieces the successfully equip
+-- The equipset response contains a copy of the original equipset and a new list of all your currently worn gear
+-- It always comes after the equip commands, so one way to address this is to simply read the new list of currently worn gear
+-- and act as if it's all unexpected.
+parse.i[0x117] = function (data)
+ -- should simplify this code using return when I gain confidence in it
+ for i=9,9+4*(data:byte(5)-1),4 do -- Byte position for the start of the current chunk
+ local slot = data:byte(i+1)
+ for chunk,ind in injected_equipment_registry[slot]:it() do
+ if chunk == data:sub(i,i+2) then
+ -- This is the response to an injected equipset packet, so remove those packets from the registry even if it actually failed to swap.
+ injected_equipment_registry[slot] = injected_equipment_registry[slot]:slice(ind+1)
+ end
+ end
+ end
+ for i=0x49,0x85,4 do
+ -- Update items.equipment
+ parse_equip_chunk(data:sub(i,i+2))
+ end
+end
+
+parse.i[0x053] = function (data)
+ local message = data:unpack('H',0xD)
+ if (message == 0x12D or message == 0x12A or message == 0x12B or message == 0x12C) and player then
+ -- You're unable to use trust magic if you're not the party leader, solo, pt full or trying to summon an already summoned trust
+ local ts,tab = command_registry:find_by_time()
+ if tab and tab.spell and tab.spell.prefix ~= '/pet' and not gearswap_disabled then
+ tab.spell.action_type = 'Interruption'
+ tab.spell.interrupted = true
+ equip_sets('aftercast',nil,tab.spell)
+ end
+ end
+end
+
+parse.i[0x05E] = function (data)
+ -- Conquest ID
+ if _ExtraData.world.conquest then
+ local offset = _ExtraData.world.conquest.region_id*4 + 11
+ if offset == 99 then
+ offset = 95
+ elseif offset == 107 then
+ offset = 99
+ end
+ local strength_map = {[0]='Minimal',[1]='Minor',[2]='Major',[3]='Dominant'}
+ local nation_map = {[0]={english='Neutral',japanese='Neutral'},[1]=res.regions[0],[2]=res.regions[1],
+ [3]=res.regions[2],[4]={english='Beastman',japanese='Beastman'},[0xFF]=res.regions[3]}
+ _ExtraData.world.conquest.strengths = {
+ sandoria=strength_map[data:byte(offset+2)%4],
+ bastok=strength_map[math.floor(data:byte(offset+2)%16/4)],
+ windurst=strength_map[math.floor(data:byte(offset+2)%64/16)],
+ beastmen=strength_map[math.floor(data:byte(offset+2)/64)],}
+ _ExtraData.world.conquest.nation = nation_map[data:byte(offset+3)][language]
+ _ExtraData.world.conquest.sandoria = data:byte(0x87)
+ _ExtraData.world.conquest.bastok = data:byte(0x88)
+ _ExtraData.world.conquest.windurst = data:byte(0x89)
+ _ExtraData.world.conquest.beastmen = 100-data:byte(0x87)-data:byte(0x88)-data:byte(0x89)
+ end
+end
+
+parse.i[0x061] = function (data)
+ player.vitals.max_hp = data:unpack('I',5)
+ player.vitals.max_mp = data:unpack('I',9)
+ player.max_hp = data:unpack('I',5)
+ player.max_mp = data:unpack('I',9)
+ player.main_job_id = data:byte(13)
+ player.main_job_level = data:byte(14)
+
+ _ExtraData.player.nation_id = data:byte(0x51)
+ _ExtraData.player.nation = res.regions[_ExtraData.player.nation_id][language] or 'None'
+ _ExtraData.player.base_str = data:unpack('H',0x15)
+ _ExtraData.player.base_dex = data:unpack('H',0x17)
+ _ExtraData.player.base_vit = data:unpack('H',0x19)
+ _ExtraData.player.base_agi = data:unpack('H',0x1B)
+ _ExtraData.player.base_int = data:unpack('H',0x1D)
+ _ExtraData.player.base_mnd = data:unpack('H',0x1F)
+ _ExtraData.player.base_chr = data:unpack('H',0x21)
+ _ExtraData.player.add_str = data:unpack('h',0x23)
+ _ExtraData.player.add_dex = data:unpack('h',0x25)
+ _ExtraData.player.add_vit = data:unpack('h',0x27)
+ _ExtraData.player.add_agi = data:unpack('h',0x29)
+ _ExtraData.player.add_int = data:unpack('h',0x2B)
+ _ExtraData.player.add_mnd = data:unpack('h',0x2D)
+ _ExtraData.player.add_chr = data:unpack('h',0x2F)
+ _ExtraData.player.attack = data:unpack('H',0x31)
+ _ExtraData.player.defense = data:unpack('H',0x33)
+ _ExtraData.player.fire_resistance = data:unpack('h',0x35)
+ _ExtraData.player.wind_resistance = data:unpack('h',0x37)
+ _ExtraData.player.lightning_resistance = data:unpack('h',0x39)
+ _ExtraData.player.light_resistance = data:unpack('h',0x3B)
+ _ExtraData.player.ice_resistance = data:unpack('h',0x3D)
+ _ExtraData.player.earth_resistance = data:unpack('h',0x3F)
+ _ExtraData.player.water_resistance = data:unpack('h',0x41)
+ _ExtraData.player.dark_resistance = data:unpack('h',0x43)
+
+ _ExtraData.player.str = _ExtraData.player.base_str + _ExtraData.player.add_str
+ _ExtraData.player.dex = _ExtraData.player.base_dex + _ExtraData.player.add_dex
+ _ExtraData.player.vit = _ExtraData.player.base_vit + _ExtraData.player.add_vit
+ _ExtraData.player.agi = _ExtraData.player.base_agi + _ExtraData.player.add_agi
+ _ExtraData.player.int = _ExtraData.player.base_int + _ExtraData.player.add_int
+ _ExtraData.player.mnd = _ExtraData.player.base_mnd + _ExtraData.player.add_mnd
+ _ExtraData.player.chr = _ExtraData.player.base_chr + _ExtraData.player.add_chr
+
+ if player.sub_job_id ~= data:byte(15) then
+ -- Subjob change event
+ local temp_sub = player.sub_job
+ player.sub_job_id = data:byte(15)
+ player.sub_job_level = data:byte(16)
+ update_job_names()
+ if not gearswap_disabled then
+ refresh_globals()
+ equip_sets('sub_job_change',nil,player.sub_job,temp_sub)
+ end
+ end
+ update_job_names()
+end
+
+parse.i[0x062] = function (data)
+ for i = 1,0x71,2 do
+ local skill = data:unpack('H',i + 0x82)%32768
+ local current_skill = res.skills[math.floor(i/2)+1]
+ if current_skill then
+ player.skills[to_windower_api(current_skill.english)] = skill
+ end
+ end
+end
+
+parse.i[0x063] = function (data)
+ if data:byte(0x05) == 0x09 and blank_0x063_v9_inc then
+ -- After zoning, players receive a blank 0x063 v9 packet
+ -- (because their buff line is temporarily empty)
+ -- So this flag is set in 0x00A
+ blank_0x063_v9_inc = false
+ -- However, players can also reload gearswap and fail to get a 0x063 v9 packet from
+ -- windower.packets.last_incoming, which leaves them without buff information but with a
+ -- informative 0x063 v9 packet coming next. So this step checks confirms the packet is
+ -- empty before returning
+ if data:sub(0x49,0xC8) == string.char(0):rep(128) then
+ return
+ end
+ end
+ if data:byte(0x05) == 0x09 then
+ local newbuffs = {}
+ for i=1,32 do
+ local buff_id = data:unpack('H',i*2+7)
+ if buff_id ~= 255 and buff_id ~= 0 then -- 255 is used for "no buff"
+ local t = data:unpack('I',i*4+0x45)/60+572662306+1009810800
+ newbuffs[i] = setmetatable({
+ name=res.buffs[buff_id].name,
+ buff=copy_entry(res.buffs[buff_id]),
+ id = buff_id,
+ time=t,
+ date=os.date('*t',t),
+ },
+ {__index=function(t,k)
+ if k and k=='duration' then
+ return rawget(t,'time')-os.time()
+ else
+ return rawget(t,k)
+ end
+ end})
+ end
+ end
+ if seen_0x063_type9 then
+
+ -- Look for exact matches
+ for n,new in pairs(newbuffs) do
+ newbuffs[n].matched_exactly = nil
+ for i,old in pairs(_ExtraData.player.buff_details) do
+ -- Find unchanged buffs
+ if old.id == new.id and math.abs(old.time-new.time) < 1 and not old.matched_exactly then
+ newbuffs[n].matched_exactly = true
+ _ExtraData.player.buff_details[i].matched_exactly = true
+ break
+ end
+ end
+ end
+
+ -- Look for time-independent matches, which are assumedly a spell overwriting itself
+ for n,new in pairs(newbuffs) do
+ newbuffs[n].matched_imprecisely = nil
+ if not new.matched_exactly then
+ for i,old in pairs(_ExtraData.player.buff_details) do
+ -- Buffs can be overwritten
+ if old.id == new.id and not (old.matched_exactly or old.matched_imprecisely) then
+ newbuffs[n].matched_imprecisely = true
+ _ExtraData.player.buff_details[i].matched_imprecisely = true
+ break
+ end
+ end
+ end
+ end
+
+ for n,new in pairs(newbuffs) do
+ if new.matched_exactly then
+ newbuffs[n].matched_exactly = nil
+ elseif new.matched_imprecisely then
+ newbuffs[n].matched_imprecisely = nil
+ -- Matched a previous buff, but the time didn't jive so it's assumed
+ -- that it was overwritten with the same status effect
+ if not res.buffs[new.id] then
+ error('GearSwap: No known status for buff id #'..tostring(new.id))
+ end
+ local buff_name = res.buffs[new.id][language]
+ windower.debug('refresh buff '..buff_name..' ('..tostring(new.id)..')')
+ if not gearswap_disabled then
+ refresh_globals()
+ equip_sets('buff_refresh',nil,buff_name,new)
+ end
+ else
+ -- Not matched, so it's assumed the buff is new
+ if not res.buffs[new.id] then
+ error('GearSwap: No known status for buff id #'..tostring(new.id))
+ end
+ local buff_name = res.buffs[new.id][language]
+ windower.debug('gain buff '..buff_name..' ('..tostring(new.id)..')')
+ -- Need to figure out what I'm going to do with this:
+ if T{'terror','sleep','stun','petrification','charm','weakness'}:contains(buff_name:lower()) then
+ for ts,v in pairs(command_registry) do
+ if v.midaction then
+ command_registry:delete_entry(ts)
+ end
+ end
+ end
+ if not gearswap_disabled then
+ refresh_globals()
+ equip_sets('buff_change',nil,buff_name,true,new)
+ end
+ end
+ end
+ for i,old in pairs(_ExtraData.player.buff_details) do
+ if not (old.matched_exactly or old.matched_imprecisely) then
+ -- Old status was not matched to any new status, so it's assumed it was lost
+ if not res.buffs[old.id] then
+ error('GearSwap: No known status for buff id #'..tostring(old.id))
+ end
+ local buff_name = res.buffs[old.id][language]
+ windower.debug('lose buff '..buff_name..' ('..tostring(old.id)..')')
+ if not gearswap_disabled then
+ refresh_globals()
+ equip_sets('buff_change',nil,buff_name,false,old)
+ end
+ end
+ end
+ end
+
+ table.reassign(_ExtraData.player.buff_details,newbuffs)
+ for i=1,32 do
+ player.buffs[i] = (newbuffs[i] and newbuffs[i].id) or nil
+ end
+ -- Cannot reliably recall this packet using last_incoming on load because there
+ -- are 9 version of it and you only get the last one. Hence, this flag:
+ seen_0x063_type9 = true
+ end
+end
+
+parse.i[0x067] = function (data)
+ if player.index == data:unpack('H',0x0D) then -- You are the owner
+ _ExtraData.pet.tp = data:unpack('H',0x11)
+ end
+end
+
+parse.i[0x068] = function (data)
+
+ if player.index == data:unpack('H',0x07) then -- You are the owner
+ _ExtraData.pet.tp = data:unpack('H',0x11)
+ end
+end
+
+parse.i[0x076] = function (data)
+ partybuffs = {}
+ for i = 0,4 do
+ if data:unpack('I',i*48+5) == 0 then
+ break
+ else
+ local index = data:unpack('H',i*48+5+4)
+ partybuffs[index] = {
+ id = data:unpack('I',i*48+5+0),
+ index = data:unpack('H',i*48+5+4),
+ buffs = {}
+ }
+ for n=1,32 do
+ partybuffs[index].buffs[n] = data:byte(i*48+5+16+n-1) + 256*( math.floor( data:byte(i*48+5+8+ math.floor((n-1)/4)) / 4^((n-1)%4) )%4)
+ end
+
+
+ if alliance[1] then
+ local cur_player
+ for n,m in pairs(alliance[1]) do
+ if type(m) == 'table' and m.mob and m.mob.index == index then
+ cur_player = m
+ break
+ end
+ end
+ local new_buffs = convert_buff_list(partybuffs[index].buffs)
+ if cur_player and cur_player.buffactive and not gearswap_disabled then
+ local old_buffs = cur_player.buffactive
+ -- Make sure the character existed before (with a buffactive list) - Avoids zoning.
+ for n,m in pairs(new_buffs) do
+ if type(n) == 'number' and m ~= old_buffs[n] then
+ if not old_buffs[n] or m > old_buffs[n] then -- gaining buff
+ equip_sets('party_buff_change',nil,cur_player,res.buffs[n][language],true,copy_entry(res.buffs[n]))
+ old_buffs[n] = nil
+ else -- losing buff
+ equip_sets('party_buff_change',nil,cur_player,res.buffs[n][language],false,copy_entry(res.buffs[n]))
+ old_buffs[n] = nil
+ end
+ elseif type(n) ~= 'number' then
+ -- Clear out the string entries so we don't have to iterate over them in the second loop
+ old_buffs[n] = nil
+ end
+ end
+
+ for n,m in pairs(old_buffs) do
+ if type(n) == 'number' and m ~= new_buffs[n] then-- losing buff
+ equip_sets('party_buff_change',nil,cur_player,res.buffs[n][language],false,copy_entry(res.buffs[n]))
+ end
+ end
+ end
+ if cur_player then
+ cur_player.buffactive = new_buffs
+ end
+ end
+
+ end
+ end
+end
+
+parse.i[0x0DF] = function (data)
+ if data:unpack('I',5) == player.id then
+ player.vitals.hp = data:unpack('I',9)
+ player.vitals.mp = data:unpack('I',13)
+ player.vitals.tp = data:unpack('I',0x11)
+ player.vitals.hpp = data:byte(0x17)
+ player.vitals.mpp = data:byte(0x18)
+
+ player.hp = data:unpack('I',9)
+ player.mp = data:unpack('I',13)
+ player.tp = data:unpack('I',0x11)
+ player.hpp = data:byte(0x17)
+ player.mpp = data:byte(0x18)
+ end
+end
+
+parse.i[0x0E2] = function (data)
+ if data:unpack('I',5)==player.id then
+ player.vitals.hp = data:unpack('I',9)
+ player.vitals.mp = data:unpack('I',0xB)
+ player.vitals.tp = data:unpack('I',0x11)
+ player.vitals.hpp = data:byte(0x1E)
+ player.vitals.mpp = data:byte(0x1F)
+
+ player.hp = data:unpack('I',9)
+ player.mp = data:unpack('I',0xB)
+ player.tp = data:unpack('I',0x11)
+ player.hpp = data:byte(0x1E)
+ player.mpp = data:byte(0x1F)
+ end
+end
+
+parse.o[0x100] = function(data)
+ -- Scrub the equipment array if a valid outgoing job change packet is sent.
+ local newmain = data:byte(5)
+ if res.jobs[newmain] and newmain ~= 0 and newmain ~= player.main_job_id then
+ windower.debug('job change')
+
+ command_registry = Command_Registry.new()
+
+ table.clear(not_sent_out_equip)
+ table.clear(equip_list_history)
+ table.clear(equip_list)
+ player.main_job_id = newmain
+ update_job_names()
+ for i=0,15 do
+ injected_equipment_registry[i]:clear()
+ items.equipment[default_slot_map[i]] = {bag_id=0,slot=empty}
+ end
+ windower.send_command('lua i '.._addon.name..' load_user_files '..newmain)
+ end
+
+
+ if gearswap_disabled then return end
+
+ local newmain = data:byte(5)
+ if res.jobs[newmain] and newmain ~= player.main_job_id then
+ command_enable('main','sub','range','ammo','head','neck','lear','rear','body','hands','lring','rring','back','waist','legs','feet') -- enable all slots
+ end
+end
+
+function initialize_packet_parsing()
+ for i,v in pairs(parse.i) do
+ if i ~= 0x028 then
+ local lastpacket = windower.packets.last_incoming(i)
+ if lastpacket then
+ v(lastpacket)
+ end
+ if i == 0x63 and lastpacket and lastpacket:byte(5) ~= 9 then
+ -- Not receiving an accurate buff line on load because the wrong 0x063 packet was sent last
+
+ end
+ end
+ end
+end
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/refresh.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/refresh.lua
new file mode 100644
index 0000000..9fb5e95
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/refresh.lua
@@ -0,0 +1,727 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+-- Deals with refreshing player information and loading user settings --
+
+
+
+-----------------------------------------------------------------------------------
+--Name: refresh_globals()
+--Args:
+---- None
+-----------------------------------------------------------------------------------
+--Returns:
+---- None
+---- Updates all global variables to reflect the player's status. Generally run
+---- before calling a player function.
+-----------------------------------------------------------------------------------
+function refresh_globals(user_event_flag)
+ local current = os.clock()
+ local dt = current - last_refresh
+ if not user_event_flag or dt > 0.05 then
+ refresh_player(dt,user_event_flag)
+ refresh_ffxi_info(dt,user_event_flag)
+ refresh_group_info(dt,user_event_flag)
+ last_refresh = current
+ end
+end
+
+-----------------------------------------------------------------------------------
+--Name: load_user_files()
+--Args:
+---- None
+-----------------------------------------------------------------------------------
+--Returns:
+---- user_env, a table of all of the player defined functions and their current
+---- variables.
+-----------------------------------------------------------------------------------
+function load_user_files(job_id,user_file)
+ job_id = tonumber(job_id)
+
+ if current_file then
+ user_pcall('file_unload',current_file)
+ end
+
+ for i in pairs(registered_user_events) do
+ unregister_event_user(i)
+ end
+
+ for i in pairs(__raw.text.registry) do
+ windower.text.delete(i)
+ end
+
+ for i in pairs(__raw.prim.registry) do
+ windower.prim.delete(i)
+ end
+
+ current_file = nil
+ sets = nil
+ user_env = nil
+ unhandled_command_events = {}
+ --registered_user_events = {}
+ include_user_path = nil
+
+ language = 'english' -- Reset language to english when changing job files.
+ refresh_globals()
+
+ if job_id and res.jobs[job_id] then
+ player.main_job_id = job_id
+ update_job_names()
+ end
+
+
+ local path,base_dir,filename
+ path,base_dir,filename = pathsearch({user_file})
+ if not path then
+ local long_job = res.jobs[job_id].english
+ local short_job = res.jobs[job_id].english_short
+ local tab = {player.name..'_'..short_job..'.lua',player.name..'-'..short_job..'.lua',
+ player.name..'_'..long_job..'.lua',player.name..'-'..long_job..'.lua',
+ player.name..'.lua',short_job..'.lua',long_job..'.lua','default.lua'}
+ path,base_dir,filename = pathsearch(tab)
+ end
+
+ if not path then
+ current_file = nil
+ sets = nil
+ return
+ end
+
+ user_env = {gearswap = _G, _global = _global, _settings = _settings,_addon=_addon,
+ -- Player functions
+ equip = equip, cancel_spell=cancel_spell, change_target=change_target, cast_delay=cast_delay,
+ print_set=print_set,set_combine=set_combine,disable=disable,enable=user_enable,
+ send_command=send_cmd_user,windower=user_windower,include=include_user,
+ midaction=user_midaction,pet_midaction=user_pet_midaction,set_language=set_language,
+ show_swaps = show_swaps,debug_mode=debug_mode,include_path=user_include_path,
+ register_unhandled_command=user_unhandled_command,move_spell_target=move_spell_target,
+ language=language,
+
+ -- Library functions
+ string=string,math=math,table=table,set=set,list=list,T=T,S=S,L=L,pack=pack,functions=functions,
+ os=os,texts=texts,bit=bit,type=type,tostring=tostring,tonumber=tonumber,pairs=pairs,
+ ipairs=ipairs, print=print, add_to_chat=add_to_chat_user,unpack=unpack,next=next,
+ select=select,lua_base_path=windower.addon_path,empty=empty,file=file,
+ loadstring=loadstring,assert=assert,error=error,pcall=pcall,io=io,dofile=dofile,
+
+ debug=debug,coroutine=coroutine,setmetatable=setmetatable,getmetatable=getmetatable,
+ rawset=rawset,rawget=rawget,require=include_user,
+ _libs=_libs,
+
+ -- Player environment things
+ buffactive=buffactive,
+ player=player,
+ world=world,
+ pet=pet,
+ fellow=fellow,
+ alliance=alliance,
+ party=alliance[1],
+ sets={naked = {main=empty,sub=empty,range=empty,ammo=empty,
+ head=empty,neck=empty,ear1=empty,ear2=empty,
+ body=empty,hands=empty,ring1=empty,ring2=empty,
+ back=empty,waist=empty,legs=empty,feet=empty}}
+ }
+
+ user_env['_G'] = user_env
+
+ -- Try to load data/<name>_<main job>.lua
+ local funct, err = loadfile(path)
+
+ -- If the file cannot be loaded, print the error and load the default.
+ if funct == nil then
+ print('User file problem: '..err)
+ current_file = nil
+ sets = nil
+ return
+ else
+ current_file = filename
+ print('GearSwap: Loaded your '..current_file..' file!')
+ end
+
+ setfenv(funct, user_env)
+
+ -- Verify that funct contains functions.
+ local status, plugin = pcall(funct)
+
+ if not status then
+ error('GearSwap: File failed to load: \n'..plugin)
+ sets = nil
+ return nil
+ end
+
+ _global.pretarget_cast_delay = 0
+ _global.precast_cast_delay = 0
+ _global.cancel_spell = false
+ _global.current_event = 'get_sets'
+ user_pcall('get_sets')
+
+ gearswap_disabled = false
+ sets = user_env.sets
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: refresh_player()
+--Args:
+---- None
+-----------------------------------------------------------------------------------
+--Returns:
+---- None
+----
+---- Loads player from windower.ffxi.get_player().
+---- Adds in a "job", "race", "equipment", "target", and "subtarget" field
+---- Also updates "pet" and assigns isvalid and element fields.
+---- Further converts player.buffs to buffactive.
+-------- Indexes buffs by their buff name and assigns a value equal to the number
+-------- of buffs with that name active.
+-----------------------------------------------------------------------------------
+function refresh_player(dt,user_event_flag)
+ local pl, player_mob_table
+ if not user_event_flag or dt > 0.5 then
+ pl = windower.ffxi.get_player()
+ if not pl or not pl.vitals then return end
+
+ player_mob_table = windower.ffxi.get_mob_by_index(pl.index)
+ if not player_mob_table then return end
+
+ table.reassign(player,pl)
+ for i,v in pairs(player.vitals) do
+ player[i]=v
+ end
+ update_job_names()
+ player.status_id = player.status
+ if res.statuses[player.status] then
+ player.status = res.statuses[player.status].english
+ else
+ print(player.status_id)
+ end
+ player.nation_id = player.nation
+ player.nation = res.regions[player.nation_id][language] or 'None'
+
+ for i,v in pairs(player_mob_table) do
+ if i == 'name' then
+ player.mob_name = v
+ elseif i~= 'is_npc' and i~='tp' and i~='mpp' and i~='claim_id' and i~='status' then
+ player[i] = v
+ end
+ end
+
+ if player_mob_table.race ~= nil then
+ player.race_id = player.race
+ player.race = res.races[player.race][language]
+ end
+
+ -- If we have a pet, create or update the table info.
+ if player_mob_table and player_mob_table.pet_index then
+ local player_pet_table = windower.ffxi.get_mob_by_index(player_mob_table.pet_index)
+ if player_pet_table then
+ table.reassign(pet, target_complete(player_pet_table))
+ pet.claim_id = nil
+ pet.is_npc = nil
+ pet.isvalid = true
+ if pet.tp then pet.tp = pet.tp/10 end
+
+ if avatar_element[pet.name] then
+ pet.element = res.elements[avatar_element[pet.name]][language]
+ else
+ pet.element = res.elements[-1][language] -- Physical
+ end
+ else
+ table.reassign(pet, {isvalid=false})
+ end
+ else
+ table.reassign(pet, {isvalid=false})
+ end
+
+ if player.main_job_id == 18 or player.sub_job_id == 18 then
+ local auto_tab
+ if player.main_job_id == 18 then auto_tab = windower.ffxi.get_mjob_data()
+ else auto_tab = windower.ffxi.get_sjob_data() end
+
+ if auto_tab.name then
+ for i,v in pairs(auto_tab) do
+ if not T{'available_heads','attachments','available_frames','available_attachments','frame','head'}:contains(i) then
+ pet[i] = v
+ end
+ end
+ pet.available_heads = make_user_table()
+ pet.attachments = make_user_table()
+ pet.available_frames = make_user_table()
+ pet.available_attachments = make_user_table()
+
+ -- available parts
+ for i,id in pairs(auto_tab.available_heads) do
+ if res.items[id] and type(res.items[id]) == 'table' then
+ pet.available_heads[res.items[id][language]] = true
+ end
+ end
+ for i,id in pairs(auto_tab.available_frames) do
+ if res.items[id] and type(res.items[id]) == 'table' then
+ pet.available_frames[res.items[id][language]] = true
+ end
+ end
+ for i,id in pairs(auto_tab.available_attachments) do
+ if res.items[id] and type(res.items[id]) == 'table' then
+ pet.available_attachments[res.items[id][language]] = true
+ end
+ end
+
+ -- actual parts
+ pet.head = res.items[auto_tab.head][language]
+ pet.frame = res.items[auto_tab.frame][language]
+ for i,id in pairs(auto_tab.attachments) do
+ if res.items[id] and type(res.items[id]) == 'table' then
+ pet.attachments[res.items[id][language]] = true
+ end
+ end
+
+ if pet.max_mp ~= 0 then
+ pet.mpp = math.floor(pet.mp/pet.max_mp*100)
+ else
+ pet.mpp = 0
+ end
+ end
+ elseif player.main_job_id == 23 then
+ local species_id = windower.ffxi.get_mjob_data().species
+ -- Should add instincts when they become available
+
+ if species_id then
+ player.species = {}
+ for i,v in pairs(res.monstrosity[species_id]) do
+ player.species[i] = v
+ end
+ player.species.name = player.species[language]
+ player.species.tp_moves = copy_entry(res.monstrosity[species_id].tp_moves)
+ for i,v in pairs(player.species.tp_moves) do
+ if v > player.main_job_level then
+ player.species.tp_moves[i] = nil
+ end
+ end
+ end
+ else
+ player.species = nil
+ end
+ end
+
+ -- This being nil does not cause a return, but items should not really be changing when zoning.
+ local cur_equip = table.reassign({},items.equipment)
+
+ -- Assign player.equipment to be the gear that has been sent out and the server currently thinks
+ -- you are wearing. (the sent_out_equip for loop above).
+ player.equipment = make_user_table()
+ table.reassign(player.equipment,to_names_set(cur_equip))
+
+ -- Assign player.inventory to be keyed to item.inventory[i][language] and to have a value of count, similar to buffactive
+ for i,bag in pairs(res.bags) do
+ local bag_name = to_windower_bag_api(bag.en)
+ if items[bag_name] then player[bag_name] = refresh_item_list(items[bag_name]) end
+ end
+
+ -- Monster tables for the target and subtarget.
+ player.target = target_complete(windower.ffxi.get_mob_by_target('t'))
+ player.subtarget = target_complete(windower.ffxi.get_mob_by_target('st'))
+ player.last_subtarget = target_complete(windower.ffxi.get_mob_by_target('lastst'))
+
+
+
+ table.reassign(fellow,target_complete(windower.ffxi.get_mob_by_target('<ft>')))
+ if fellow.name then
+ fellow.isvalid = true
+ else
+ fellow.isvalid=false
+ end
+
+ table.reassign(buffactive,convert_buff_list(player.buffs))
+
+ for global_variable_name,extradatatable in pairs(_ExtraData) do
+ if _G[global_variable_name] then
+ for sub_variable_name,value in pairs(extradatatable) do
+ _G[global_variable_name][sub_variable_name] = value
+ end
+ end
+ end
+end
+
+-----------------------------------------------------------------------------------
+--Name: refresh_ffxi_info()
+--Args:
+---- None
+-----------------------------------------------------------------------------------
+--Returns:
+---- None
+----
+---- Updates the global "world" with windower.ffxi.get_info (ignores the target field).
+---- Also sets windower.ffxi.get_info()['zone'] to be world.area for consistency with spellcast
+-----------------------------------------------------------------------------------
+function refresh_ffxi_info(dt,user_event_flag)
+ local info = windower.ffxi.get_info()
+ for i,v in pairs(info) do
+ if i == 'zone' and res.zones[v] then
+ world.zone = res.zones[v][language]
+ world.area = world.zone
+ elseif i == 'weather' and res.weather[v] then
+ weather_update(v)
+ elseif i == 'day' and res.days[v] then
+ world.day = res.days[v][language]
+ world.day_element = res.elements[res.days[v].element][language]
+ elseif i == 'moon' then
+ world.moon_pct = v
+ elseif i == 'moon_phase' and res.moon_phases[v] then
+ world.moon = res.moon_phases[v][language]
+ elseif i ~= 'target' then
+ world[i] = v
+ end
+ end
+
+ for global_variable_name,extradatatable in pairs(_ExtraData) do
+ if _G[global_variable_name] then
+ for sub_variable_name,value in pairs(extradatatable) do
+ _G[global_variable_name][sub_variable_name] = value
+ end
+ end
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: weather_update(id)
+--Args:
+---- id Current weather ID
+-----------------------------------------------------------------------------------
+--Returns:
+---- None, updates the table.
+-----------------------------------------------------------------------------------
+function weather_update(id)
+ world.weather_id = id
+ world.real_weather_id = id
+ world.real_weather = res.weather[id][language]
+ world.real_weather_element = res.elements[res.weather[id].element][language]
+ world.real_weather_intensity = res.weather[world.real_weather_id].intensity
+ if buffactive[178] then
+ world.weather_id = 4
+ elseif buffactive[179] then
+ world.weather_id = 12
+ elseif buffactive[180] then
+ world.weather_id = 10
+ elseif buffactive[181] then
+ world.weather_id = 8
+ elseif buffactive[182] then
+ world.weather_id = 14
+ elseif buffactive[183] then
+ world.weather_id = 6
+ elseif buffactive[184] then
+ world.weather_id = 16
+ elseif buffactive[185] then
+ world.weather_id = 18
+ elseif buffactive[589] then
+ world.weather_id = 5
+ elseif buffactive[590] then
+ world.weather_id = 13
+ elseif buffactive[591] then
+ world.weather_id = 11
+ elseif buffactive[592] then
+ world.weather_id = 9
+ elseif buffactive[593] then
+ world.weather_id = 15
+ elseif buffactive[594] then
+ world.weather_id = 7
+ elseif buffactive[595] then
+ world.weather_id = 17
+ elseif buffactive[596] then
+ world.weather_id = 19
+ end
+ world.weather = res.weather[world.weather_id][language]
+ world.weather_element = res.elements[res.weather[world.weather_id].element][language]
+ world.weather_intensity = res.weather[world.weather_id].intensity
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: refresh_group_info()
+--Args:
+---- None
+-----------------------------------------------------------------------------------
+--Returns:
+---- None
+----
+---- Takes the mob arrays from windower.ffxi.get_party() and splits them from p0~5, a10~15, a20~25
+---- into alliance[1][1~6], alliance[2][1~6], alliance[3][1~6], respectively.
+---- Also adds a "count" field to alliance (total number of people in alliance) and
+---- to the individual subtables (total number of people in each party.
+-----------------------------------------------------------------------------------
+function refresh_group_info(dt,user_event_flag)
+ if not alliance or #alliance == 0 then
+ alliance = make_alliance()
+ end
+
+ local c_alliance = make_alliance()
+
+ local j = windower.ffxi.get_party() or {}
+
+ c_alliance.leader = j.alliance_leader -- Test whether this works
+ c_alliance[1].leader = j.party1_leader
+ c_alliance[2].leader = j.party2_leader
+ c_alliance[3].leader = j.party3_leader
+
+ for i,v in pairs(j) do
+ if type(v) == 'table' and v.mob and v.mob.race then
+ v.mob.race_id = v.mob.race
+ v.mob.race = res.races[v.mob.race][language]
+ end
+
+ local allyIndex
+ local partyIndex
+
+ -- For 'p#', ally index is 1, party index is the second char
+ if i:sub(1,1) == 'p' and tonumber(i:sub(2)) then
+ allyIndex = 1
+ partyIndex = tonumber(i:sub(2))+1
+ -- For 'a##', ally index is the second char, party index is the third char
+ elseif tonumber(i:sub(2,2)) and tonumber(i:sub(3)) then
+ allyIndex = tonumber(i:sub(2,2))+1
+ partyIndex = tonumber(i:sub(3))+1
+ end
+
+ if allyIndex and partyIndex then
+ if v.mob and partybuffs[v.mob.index] then
+ v.buffactive = convert_buff_list(partybuffs[v.mob.index].buffs)
+ elseif v.mob and v.mob.index == player.index then
+ v.buffactive = buffactive
+ end
+ c_alliance[allyIndex][partyIndex] = v
+ c_alliance[allyIndex].count = c_alliance[allyIndex].count + 1
+ c_alliance.count = c_alliance.count + 1
+
+ if v.mob then
+ if v.mob.id == c_alliance[1].leader then
+ c_alliance[1].leader = v
+ elseif v.mob.id == c_alliance[2].leader then
+ c_alliance[2].leader = v
+ elseif v.mob.id == c_alliance[3].leader then
+ c_alliance[3].leader = v
+ end
+
+ if v.mob.id == c_alliance.leader then
+ c_alliance.leader = v
+ end
+ end
+ end
+ end
+
+
+ -- Clear the old structure while maintaining the party references:
+ for ally_party = 1,3 do
+ for i,v in pairs(alliance[ally_party]) do
+ alliance[ally_party][i] = nil
+ end
+ alliance[ally_party].count = 0
+ end
+ alliance.count = 0
+ alliance.leader = nil
+
+ -- Reassign to the new structure
+ table.reassign(alliance[1],c_alliance[1])
+ table.reassign(alliance[2],c_alliance[2])
+ table.reassign(alliance[3],c_alliance[3])
+ alliance.count = c_alliance.count
+ alliance.leader = c_alliance.leader
+end
+
+-----------------------------------------------------------------------------------
+--Name: make_alliance()
+--Args:
+---- none
+-----------------------------------------------------------------------------------
+--Returns:
+---- one blank alliance structure
+-----------------------------------------------------------------------------------
+function make_alliance()
+ local all = make_user_table()
+ all[1]={count=0,leader=nil}
+ all[2]={count=0,leader=nil}
+ all[3]={count=0,leader=nil}
+ all.count=0
+ all.leader=nil
+ return all
+end
+
+-----------------------------------------------------------------------------------
+--Name: convert_buff_list(bufflist)
+--Args:
+---- bufflist (table): List of buffs from windower.ffxi.get_player()['buffs']
+-----------------------------------------------------------------------------------
+--Returns:
+---- buffarr (table)
+---- buffarr is indexed by the string buff name and has a value equal to the number
+---- of that string present in the buff array. So two marches would give
+---- buffarr.march==2.
+-----------------------------------------------------------------------------------
+function convert_buff_list(bufflist)
+ local buffarr = {}
+ for i,id in pairs(bufflist) do
+ if res.buffs[id] then -- For some reason we always have buff 255 active, which doesn't have an entry.
+ local buff = res.buffs[id][language]:lower()
+ if buffarr[buff] then
+ buffarr[buff] = buffarr[buff] +1
+ else
+ buffarr[buff] = 1
+ end
+
+ if buffarr[id] then
+ buffarr[id] = buffarr[id] +1
+ else
+ buffarr[id] = 1
+ end
+ end
+ end
+ return buffarr
+end
+
+-----------------------------------------------------------------------------------
+--Name: refresh_item_list(itemlist)
+--Args:
+---- itemlist (table): List of items from windower.ffxi.get_items().something
+-----------------------------------------------------------------------------------
+--Returns:
+---- retarr (table)
+---- retarr is indexed by the item name, and contains a table with the
+---- item id, count, and short name. If the long name for the item is
+---- different than the short name, an additional entry is added for
+---- that.
+----
+---- Overall, this allows doing simple existance checks for both short
+---- and long name (eg: player.inventory["Theo. Cap +1"] and
+---- player.inventory["Theopany Cap +1"] both return the same table,
+---- and both would be 'true' in a conditional check)), and get the item
+---- count (player.inventory["Orichalc. Bullet"].count)
+---- It also allows one to check for the alternate spelling of an item.
+-----------------------------------------------------------------------------------
+function refresh_item_list(itemlist)
+ retarr = make_user_table()
+ for i,v in pairs(itemlist) do
+ if type(v) == 'table' and v.id and v.id ~= 0 then
+ -- If we don't already have the primary item name in the table, add it.
+ if res.items[v.id] and res.items[v.id][language] and not retarr[res.items[v.id][language]] then
+ retarr[res.items[v.id][language]] = table.copy(v)
+ retarr[res.items[v.id][language]].shortname=res.items[v.id][language]:lower()
+ -- If a long version of the name exists, and is different from the short version,
+ -- add the long name to the info table and point the long name's key at that table.
+ if res.items[v.id][language..'_log'] and res.items[v.id][language..'_log']:lower() ~= res.items[v.id][language]:lower() then
+ retarr[res.items[v.id][language]].longname = res.items[v.id][language..'_log']:lower()
+ retarr[res.items[v.id][language..'_log']] = retarr[res.items[v.id][language]]
+ end
+ elseif res.items[v.id] and res.items[v.id][language] then
+ -- If there's already an entry for this item, all the hard work has already
+ -- been done. Just update the count on the subtable of the main item, and
+ -- everything else will link together.
+ retarr[res.items[v.id][language]].count = retarr[res.items[v.id][language]].count + v.count
+ end
+ end
+ end
+ return retarr
+end
+
+-----------------------------------------------------------------------------------
+--Name: refresh_user_env()
+--Args:
+---- none
+-----------------------------------------------------------------------------------
+--Returns:
+---- none, but loads user files if they exist.
+-----------------------------------------------------------------------------------
+function refresh_user_env(job_id)
+ refresh_globals()
+ if not job_id then job_id = windower.ffxi.get_player().main_job_id end
+
+ if not job_id then
+ windower.send_command('@wait 1;lua i '.._addon.name..' refresh_user_env')
+ else
+ load_user_files(job_id)
+ end
+end
+
+
+-----------------------------------------------------------------------------------
+--Name: pathsearch()
+--Args:
+---- files_list - table of strings of the file name to search.
+-----------------------------------------------------------------------------------
+--Returns:
+---- path of a valid file, if it exists. False if it doesn't.
+-----------------------------------------------------------------------------------
+function pathsearch(files_list)
+
+ -- base directory search order:
+ -- windower
+ -- %appdata%/Windower/GearSwap
+
+ -- sub directory search order:
+ -- libs-dev (only in windower addon path)
+ -- libs (only in windower addon path)
+ -- data/player.name
+ -- data/common
+ -- data
+
+ local gearswap_data = windower.addon_path .. 'data/'
+ local gearswap_appdata = (os.getenv('APPDATA') or '') .. '/Windower/GearSwap/'
+
+ local search_path = {
+ [1] = windower.addon_path .. 'libs-dev/',
+ [2] = windower.addon_path .. 'libs/',
+ [3] = gearswap_data .. player.name .. '/',
+ [4] = gearswap_data .. 'common/',
+ [5] = gearswap_data,
+ [6] = gearswap_appdata .. player.name .. '/',
+ [7] = gearswap_appdata .. 'common/',
+ [8] = gearswap_appdata,
+ [9] = windower.windower_path .. 'addons/libs/'
+ }
+
+ local user_path
+ local normal_path
+
+ for _,basepath in ipairs(search_path) do
+ if windower.dir_exists(basepath) then
+ for i,v in ipairs(files_list) do
+ if v ~= '' then
+ if include_user_path then
+ user_path = basepath .. include_user_path .. '/' .. v
+ end
+ normal_path = basepath .. v
+
+ if user_path and windower.file_exists(user_path) then
+ return user_path,basepath,v
+ elseif normal_path and windower.file_exists(normal_path) then
+ return normal_path,basepath,v
+ end
+ end
+ end
+ end
+ end
+
+ return false
+end
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/statics.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/statics.lua
new file mode 100644
index 0000000..2e9e46d
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/statics.lua
@@ -0,0 +1,447 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-- Convert the spells and job abilities into a referenceable list of aliases --
+
+unify_prefix = {['/ma'] = '/ma', ['/magic']='/ma',['/jobability'] = '/ja',['/ja']='/ja',['/item']='/item',['/song']='/ma',
+ ['/so']='/ma',['/ninjutsu']='/ma',['/weaponskill']='/ws',['/ws']='/ws',['/ra']='/ra',['/rangedattack']='/ra',['/nin']='/ma',
+ ['/throw']='/ra',['/range']='/ra',['/shoot']='/ra',['/monsterskill']='/ms',['/ms']='/ms',['/pet']='/ja',['Monster']='Monster',['/bstpet']='/ja'}
+
+action_type_map = {['/ja']='Ability',['/jobability']='Ability',['/so']='Magic',['/song']='Magic',['/ma']='Magic',['/magic']='Magic',['/nin']='Magic',['/ninjutsu']='Magic',
+ ['/ra']='Ranged Attack',['/range']='Ranged Attack',['/throw']='Ranged Attack',['/shoot']='Ranged Attack',['/ms']='Ability',['/monsterskill']='Ability',
+ ['/ws']='Ability',['/weaponskill']='Ability',['/item']='Item',['/pet']='Ability',['/bstpet']='Ability',['Monster']='Monster Move'}
+
+usable_item_bags = {
+ res.bags[3], -- Temporary Items
+ res.bags[0], -- Inventory
+ res.bags[8], -- Wardrobe 1
+ res.bags[10], -- Wardrobe 2
+ res.bags[11], -- Wardrobe 3
+ res.bags[12]} -- Wardrobe 4
+
+equippable_item_bags = {
+ res.bags[0], -- Inventory
+ res.bags[8], -- Wardrobe 1
+ res.bags[10], -- Wardrobe 2
+ res.bags[11], -- Wardrobe 3
+ res.bags[12]} -- Wardrobe 4
+
+bag_string_lookup = {}
+for i,v in pairs(res.bags) do
+ bag_string_lookup[to_windower_bag_api(v.en)]=i
+end
+
+bstpet_range = {min=672,max=798} -- Range of the JA resource devoted to BST jugpet abilities
+
+delay_map_to_action_type = {['Ability']=3,['Magic']=20,['Ranged Attack']=10,['Item']=10,['Monster Move']=10,['Interruption']=3}
+
+validabils = {}
+validabils['english'] = {['/ma'] = {}, ['/ja'] = {}, ['/ws'] = {}, ['/item'] = {}, ['/ra'] = {}, ['/ms'] = {}, ['/pet'] = {}, ['/trig'] = {}, ['/echo'] = {}}
+validabils['french'] = {['/ma'] = {}, ['/ja'] = {}, ['/ws'] = {}, ['/item'] = {}, ['/ra'] = {}, ['/ms'] = {}, ['/pet'] = {}, ['/trig'] = {}, ['/echo'] = {}}
+validabils['german'] = {['/ma'] = {}, ['/ja'] = {}, ['/ws'] = {}, ['/item'] = {}, ['/ra'] = {}, ['/ms'] = {}, ['/pet'] = {}, ['/trig'] = {}, ['/echo'] = {}}
+validabils['japanese'] = {['/ma'] = {}, ['/ja'] = {}, ['/ws'] = {}, ['/item'] = {}, ['/ra'] = {}, ['/ms'] = {}, ['/pet'] = {}, ['/trig'] = {}, ['/echo'] = {}}
+
+function make_abil(abil,lang,i)
+ if not abil[lang] or not abil.prefix then return end
+ local sp,pref = abil[lang]:lower(), unify_prefix[abil.prefix:lower()]
+ validabils[lang][pref][sp] = i
+end
+
+function make_entry(v,i)
+ make_abil(v,'english',i)
+ make_abil(v,'german',i)
+ make_abil(v,'french',i)
+ make_abil(v,'japanese',i)
+end
+
+for i,v in pairs(res.spells) do
+ if not T{363,364}:contains(i) then
+ make_entry(v,i)
+ end
+end
+
+for i,v in pairs(res.job_abilities) do
+ make_entry(v,i)
+end
+
+for i,v in pairs(res.weapon_skills) do
+ v.type = 'WeaponSkill'
+ make_entry(v,i)
+end
+
+for i,v in pairs(res.monster_skills) do
+ v.type = 'MonsterSkill'
+ make_entry(v,i)
+end
+
+for i,v in pairs(res.items) do
+ v.prefix = '/item'
+ if not validabils['english'][v.prefix][v.english:lower()] or v.cast_delay then
+ make_entry(v,i)
+ end
+end
+
+ -- Should transition these slot maps to be based off res.slots, but it's very unlikely to change.
+default_slot_map = T{'sub','range','ammo','head','body','hands','legs','feet','neck','waist',
+ 'left_ear', 'right_ear', 'left_ring', 'right_ring','back'}
+default_slot_map[0]= 'main'
+
+jas = {false,false,false,false,false,true,false,false,false,false,false,false,false,true,true,false}-- {6,14,15}
+readies = {false,false,false,false,false,false,true,true,true,false,false,true,false,false,false,false} -- {7,8,9,12}
+uses = {false,true,true,true,true,false,false,false,false,false,true,false,true,false,false,false}--{2,3,4,5,11,13}
+unable_to_use = T{17,18,55,56,87,88,89,90,104,191,308,313,325,410,428,561,574,579,580,581,661,665,
+ 12,16,34,35,40,47,48,49,71,72,76,78,84,91,92,95,96,106,111,128,154,155,190,192,193,198,
+ 199,215,216,217,218,219,220,233,246,247,307,315,316,328,337,338,346,347,348,349,356,411,443,444,
+ 445,446,514,516,517,518,523,524,525,547,568,569,575,649,660,662,666,700,701,62,717} -- Probably don't need some of these (event action)
+ -- 94 removed - "You must wait longer to perform that action." -- I think this is only sent in response to engage packets.
+
+-- 192 : param_1 = Ability ID
+-- 17 : no information
+-- 34 : param_1 = Spell index
+pass_through_targs = {['<t>']=true,['<me>']=true,['<ft>']=true,['<scan>']=true,['<bt>']=true,['<lastst>']=true,
+ ['<r>']=true,['<pet>']=true,['<p0>']=true,['<p1>']=true,['<p2>']=true,['<p3>']=true,['<p4>']=true,
+ ['<p5>']=true,['<a10>']=true,['<a11>']=true,['<a12>']=true,['<a13>']=true,['<a14>']=true,['<a15>']=true,
+ ['<a20>']=true,['<a21>']=true,['<a22>']=true,['<a23>']=true,['<a24>']=true,['<a25>']=true,['<st>']=true,
+ ['<stnpc>']=true,['<stal>']=true,['<stpc>']=true,['<stpt>']=true}
+
+avatar_element = {Ifrit=0,Titan=3,Leviathan=5,Garuda=2,Shiva=1,Ramuh=4,Carbuncle=6,
+ Diabolos=7,Fenrir=7,['Cait Sith']=6,FireSpirit=0,EarthSpirit=3,WaterSpirit=5,
+ AirSpirit=2,IceSpirit=1,ThunderSpirit=4,LightSpirit=6,
+ DarkSpirit=7}
+
+encumbrance_map = {0x79,0x7F,0x7F,0x7A,0x7B,0x7C,0x7D,0x7D,0x7A,0x7E,0x80,0x80,0x80,0x80,0x7E}
+encumbrance_map[0] = 0x79 -- Slots mapped onto encumbrance byte values.
+
+addendum_white = {[14]="Poisona",[15]="Paralyna",[16]="Blindna",[17]="Silena",[18]="Stona",[19]="Viruna",[20]="Cursna",
+ [143]="Erase",[13]="Raise II",[140]="Raise III",[141]="Reraise II",[142]="Reraise III",[135]="Reraise"}
+
+addendum_black = {[253]="Sleep",[259]="Sleep II",[260]="Dispel",[162]="Stone IV",[163]="Stone V",[167]="Thunder IV",
+ [168]="Thunder V",[157]="Aero IV",[158]="Aero V",[152]="Blizzard IV",[153]="Blizzard V",[147]="Fire IV",[148]="Fire V",
+ [172]="Water IV",[173]="Water V",[255]="Break"}
+
+resources_ranged_attack = {id="0",index="0",prefix="/range",english="Ranged",german="Fernwaffe",french="Attaque à dist.",japanese="飛び道具",type="Misc",element="None",targets=S{"Enemy"}}
+
+
+-- _globals --
+user_data_table = {
+ __newindex = function(tab, key, val)
+ rawset(tab, user_key_filter(key), val)
+ end,
+
+ __index = function(tab, key)
+ return rawget(tab, user_key_filter(key))
+ end
+ }
+
+--[[eq_data_table = {
+ __newindex = function(tab, key, val)
+ rawset(tab, slot_map[user_key_filter(key)], newtab)
+ end,
+
+ __index = function(tab, key)
+ return rawget(tab, slot_map[user_key_filter(key)])
+ end
+ }]]
+
+slot_map = make_user_table()
+
+slot_map.main = 0
+slot_map.sub = 1
+slot_map.range = 2
+slot_map.ranged = 2
+slot_map.ammo = 3
+slot_map.head = 4
+slot_map.body = 5
+slot_map.hands = 6
+slot_map.legs = 7
+slot_map.feet = 8
+slot_map.neck = 9
+slot_map.waist = 10
+slot_map.ear1 = 11
+slot_map.ear2 = 12
+slot_map.left_ear = 11
+slot_map.right_ear = 12
+slot_map.learring = 11
+slot_map.rearring = 12
+slot_map.lear = 11
+slot_map.rear = 12
+slot_map.left_ring = 13
+slot_map.right_ring = 14
+slot_map.lring = 13
+slot_map.rring = 14
+slot_map.ring1 = 13
+slot_map.ring2 = 14
+slot_map.back = 15
+
+
+
+gearswap_disabled = false
+seen_0x063_type9 = false
+delay_0x063_v9 = false
+not_sent_out_equip = {}
+command_registry = Command_Registry.new()
+equip_list = {}
+equip_list_history = {}
+world = make_user_table()
+buffactive = make_user_table()
+alliance = make_user_table()
+st_targs = {['<st>']=true,['<stpc>']=true,['<stal>']=true,['<stnpc>']=true,['<stpt>']=true}
+current_file = nil
+disable_table = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false}
+disable_table[0] = false
+outgoing_action_category_table = {['/ma']=3,['/ws']=7,['/ja']=9,['/ra']=16,['/ms']=25}
+encumbrance_table = table.reassign({},disable_table)
+registered_user_events = {}
+unhandled_command_events = {}
+empty = {name="empty"}
+--outgoing_packet_table = {}
+last_refresh = 0
+
+injected_equipment_registry = {}
+for i=0,15 do
+ injected_equipment_registry[i] = L{}
+end
+
+
+_global = make_user_table()
+_global.pretarget_cast_delay = 0
+_global.precast_cast_delay = 0
+_global.cancel_spell = false
+_global.current_event = 'None'
+
+_settings = {debug_mode = false, demo_mode = false, show_swaps = false}
+
+-- _ExtraData is persistent information that isn't included in the windower API.
+-- Because player, pet, and so forth are regularly regenerated from the windower API,
+-- this table is necessary to maintain information that goes beyond the windower API.
+_ExtraData = {
+ player = {buff_details = {}},
+ pet = {},
+ world = {in_mog_house = false,conquest=false},
+ }
+
+unbridled_learning_set = {['Thunderbolt']=true,['Harden Shell']=true,['Absolute Terror']=true,
+ ['Gates of Hades']=true,['Tourbillion']=true,['Pyric Bulwark']=true,['Bilgestorm']=true,
+ ['Bloodrake']=true,['Droning Whirlwind']=true,['Carcharian Verve']=true,['Blistering Roar']=true,
+ ['Uproot']=true,['Crashing Thunder']=true,['Polar Roar']=true,['Mighty Guard']=true,['Cruel Joke']=true,
+ ['Cesspool']=true,['Tearing Gust']=true}
+
+tool_map = {
+ ['Katon: Ichi'] = res.items[1161],
+ ['Katon: Ni'] = res.items[1161],
+ ['Katon: San'] = res.items[1161],
+ ['Hyoton: Ichi'] = res.items[1164],
+ ['Hyoton: Ni'] = res.items[1164],
+ ['Hyoton: San'] = res.items[1164],
+ ['Huton: Ichi'] = res.items[1167],
+ ['Huton: Ni'] = res.items[1167],
+ ['Huton: San'] = res.items[1167],
+ ['Doton: Ichi'] = res.items[1170],
+ ['Doton: Ni'] = res.items[1170],
+ ['Doton: San'] = res.items[1170],
+ ['Raiton: Ichi'] = res.items[1173],
+ ['Raiton: Ni'] = res.items[1173],
+ ['Raiton: San'] = res.items[1173],
+ ['Suiton: Ichi'] = res.items[1176],
+ ['Suiton: Ni'] = res.items[1176],
+ ['Suiton: San'] = res.items[1176],
+ ['Utsusemi: Ichi'] = res.items[1179],
+ ['Utsusemi: Ni'] = res.items[1179],
+ ['Utsusemi: San'] = res.items[1179],
+ ['Jubaku: Ichi'] = res.items[1182],
+ ['Jubaku: Ni'] = res.items[1182],
+ ['Jubaku: San'] = res.items[1182],
+ ['Hojo: Ichi'] = res.items[1185],
+ ['Hojo: Ni'] = res.items[1185],
+ ['Hojo: San'] = res.items[1185],
+ ['Kurayami: Ichi'] = res.items[1188],
+ ['Kurayami: Ni'] = res.items[1188],
+ ['Kurayami: San'] = res.items[1188],
+ ['Dokumori: Ichi'] = res.items[1191],
+ ['Dokumori: Ni'] = res.items[1191],
+ ['Dokumori: San'] = res.items[1191],
+ ['Tonko: Ichi'] = res.items[1194],
+ ['Tonko: Ni'] = res.items[1194],
+ ['Tonko: San'] = res.items[1194],
+ ['Monomi: Ichi'] = res.items[2553],
+ ['Monomi: Ni'] = res.items[2553],
+ ['Aisha: Ichi'] = res.items[2555],
+ ['Myoshu: Ichi'] = res.items[2642],
+ ['Yurin: Ichi'] = res.items[2643],
+ ['Migawari: Ichi'] = res.items[2970],
+ ['Kakka: Ichi'] = res.items[2644],
+ ['Gekka: Ichi'] = res.items[8803],
+ ['Yain: Ichi'] = res.items[8804],
+ }
+
+universal_tool_map = {
+ ['Katon: Ichi'] = res.items[2971],
+ ['Katon: Ni'] = res.items[2971],
+ ['Katon: San'] = res.items[2971],
+ ['Hyoton: Ichi'] = res.items[2971],
+ ['Hyoton: Ni'] = res.items[2971],
+ ['Hyoton: San'] = res.items[2971],
+ ['Huton: Ichi'] = res.items[2971],
+ ['Huton: Ni'] = res.items[2971],
+ ['Huton: San'] = res.items[2971],
+ ['Doton: Ichi'] = res.items[2971],
+ ['Doton: Ni'] = res.items[2971],
+ ['Doton: San'] = res.items[2971],
+ ['Raiton: Ichi'] = res.items[2971],
+ ['Raiton: Ni'] = res.items[2971],
+ ['Raiton: San'] = res.items[2971],
+ ['Suiton: Ichi'] = res.items[2971],
+ ['Suiton: Ni'] = res.items[2971],
+ ['Suiton: San'] = res.items[2971],
+ ['Utsusemi: Ichi'] = res.items[2972],
+ ['Utsusemi: Ni'] = res.items[2972],
+ ['Utsusemi: San'] = res.items[2972],
+ ['Jubaku: Ichi'] = res.items[2973],
+ ['Jubaku: Ni'] = res.items[2973],
+ ['Jubaku: San'] = res.items[2973],
+ ['Hojo: Ichi'] = res.items[2973],
+ ['Hojo: Ni'] = res.items[2973],
+ ['Hojo: San'] = res.items[2973],
+ ['Kurayami: Ichi'] = res.items[2973],
+ ['Kurayami: Ni'] = res.items[2973],
+ ['Kurayami: San'] = res.items[2973],
+ ['Dokumori: Ichi'] = res.items[2973],
+ ['Dokumori: Ni'] = res.items[2973],
+ ['Dokumori: San'] = res.items[2973],
+ ['Tonko: Ichi'] = res.items[2972],
+ ['Tonko: Ni'] = res.items[2972],
+ ['Tonko: San'] = res.items[2972],
+ ['Monomi: Ichi'] = res.items[2972],
+ ['Aisha: Ichi'] = res.items[2973],
+ ['Myoshu: Ichi'] = res.items[2972],
+ ['Yurin: Ichi'] = res.items[2973],
+ ['Migawari: Ichi'] = res.items[2972],
+ ['Kakka: Ichi'] = res.items[2972],
+ ['Gekka: Ichi'] = res.items[2972],
+ ['Yain: Ichi'] = res.items[2972],
+ }
+
+region_to_zone_map = {
+ [4] = S{100,101,139,140,141,142,167,190},
+ [5] = S{102,103,108,193,196,248},
+ [6] = S{1,2,104,105,149,150,195},
+ [7] = S{106,107,143,144,172,173,191},
+ [8] = S{109,110,147,148,197},
+ [9] = S{115,116,145,146,169,170,192,194},
+ [10] = S{3,4,117,118,198,213,249},
+ [11] = S{7,8,119,120,151,152,200},
+ [12] = S{9,10,111,166,203,204,206},
+ [13] = S{5,6,112,161,162,165},
+ [14] = S{126,127,157,158,179,184},
+ [15] = S{121,122,153,154,202,251},
+ [16] = S{114,125,168,208,209,247},
+ [17] = S{113,128.174,201,212},
+ [18] = S{123,176,250,252},
+ [19] = S{124,159,160,163,205,207,211},
+ [20] = S{130,177,178,180,181},
+ [22] = S{11,12,13},
+ [24] = S{24,25,26,27,28,29,30,31,32},
+ }
+
+
+function initialize_globals()
+ local pl = windower.ffxi.get_player()
+ if not pl then
+ player = make_user_table()
+ player.vitals = {}
+ player.buffs = {}
+ player.skills = {}
+ player.jobs = {}
+ player.merits = {}
+ else
+ player = make_user_table()
+ table.reassign(player,pl)
+ if not player.vitals then player.vitals = {} end
+ if not player.buffs then player.buffs = {} end
+ if not player.skills then player.skills = {} end
+ if not player.jobs then player.jobs = {} end
+ if not player.merits then player.merits = {} end
+ end
+
+ player.equipment = make_user_table()
+ pet = make_user_table()
+ pet.isvalid = false
+ fellow = make_user_table()
+ fellow.isvalid = false
+ partybuffs = {}
+
+ -- GearSwap effectively needs to maintain two inventory structures:
+ -- one is the proposed current inventory based on equip packets sent to the server,
+ -- the other is the currently reported inventory based on packets sent from the server.
+ -- The problem with proposed_inv is that it doesn't know when actions force items to unequip or prevent them from equipping.
+ -- The problem with reported_inv is that packets can be dropped, so it doesn't always report everything accurately.
+ -- In an ideal world, gearswap would maintain a registry of expected changes for each slot,
+ -- and would advance along the registry as changes are reported by the server.
+ items = windower.ffxi.get_items()
+ if not items then
+ items = {
+ equipment = {},
+ }
+ for id,name in pairs(default_slot_map) do
+ items.equipment[name] = {slot = empty,bag_id=0}
+ end
+ else
+ if not items.equipment then
+ items.equipment = {}
+ for id,name in pairs(default_slot_map) do
+ items.equipment[name] = {slot = empty,bag_id=0}
+ end
+ else
+ for id,name in pairs(default_slot_map) do
+ items.equipment[name] = {
+ slot = items.equipment[name],
+ bag_id = items.equipment[name..'_bag']
+ }
+ items.equipment[name..'_bag'] = nil
+ if items.equipment[name].slot == 0 then items.equipment[name].slot = empty end
+ end
+ end
+ end
+ for i in pairs(windower.ffxi.get_bag_info()) do
+ if not items[i] then items[i] = make_inventory_table()
+ else items[i][0] = make_empty_item_table(0) end
+ end
+
+ local wo = windower.ffxi.get_info()
+ if wo then
+ for i,v in pairs(region_to_zone_map) do
+ if v:contains(wo.zone) then
+ _ExtraData.world.conquest = {
+ region_id = i,
+ region_name = res.regions[i][language],
+ }
+ break
+ end
+ end
+ end
+end
+
+initialize_globals()
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/targets.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/targets.lua
new file mode 100644
index 0000000..3e47b74
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/targets.lua
@@ -0,0 +1,149 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+-- Target Processing --
+
+function valid_target(targ)
+ local spelltarget = {}
+
+ local spell_targ
+ if pass_through_targs[targ] then
+ local j = windower.ffxi.get_mob_by_target(targ)
+
+ if j then spelltarget = target_complete(j) end
+
+ spelltarget.raw = targ
+ return targ, spelltarget
+ elseif targ and tonumber(targ) and tonumber(targ) > 255 then
+ local j = windower.ffxi.get_mob_by_id(tonumber(targ))
+
+ if j then spelltarget = target_complete(j) end
+
+ spelltarget.raw = targ
+ return targ, spelltarget
+ elseif targ and not tonumber(targ) and targ ~= '' then
+ local mob_array = windower.ffxi.get_mob_array()
+ for i,v in pairs(mob_array) do
+ if v.name:lower()==targ:lower() and (not v.is_npc or v.spawn_type == 14) then
+ spelltarget = target_complete(v)
+ spelltarget.raw = targ
+ return targ, spelltarget
+ end
+ end
+ end
+ return false, false
+end
+
+function target_complete(mob_table)
+ if mob_table == nil then return {type = 'NONE'} end
+
+ ------------------------------- Should consider moving the partycount part of this code to refresh_player() ----------------------------------
+ mob_table.isallymember = false
+ if not mob_table.id then
+ mob_table.type = 'NONE'
+ else
+ local j = windower.ffxi.get_party()
+
+ for i,v in pairs(j) do
+ if type(v) == 'table' and v.mob then
+ if v.mob.id == mob_table.id then
+ mob_table.isallymember = true
+ if i:sub(1,1) == 'p' then
+ mob_table.ispartymember = true
+ end
+ end
+ end
+ end
+ ------------------------------------------------------------------------------------------------------------------------------------
+
+ if player.id == mob_table.id then
+ mob_table.type = 'SELF'
+ elseif mob_table.is_npc then
+ if mob_table.id%4096>2047 then
+ mob_table.type = 'NPC'
+ else
+ mob_table.type = 'MONSTER'
+ end
+ else
+ mob_table.type = 'PLAYER'
+ end
+ end
+
+ if mob_table.race then
+ mob_table.race_id = mob_table.race
+ if res.races[mob_table.race] then
+ mob_table.race = res.races[mob_table.race][language]
+ else
+ mob_table.race = 'Unknown'
+ end
+ end
+ if mob_table.status then
+ mob_table.status_id = mob_table.status
+ if res.statuses[mob_table.status] then
+ mob_table.status = res.statuses[mob_table.status].english
+ else
+ mob_table.status = 'Unknown'
+ end
+ end
+ if mob_table.distance then
+ mob_table.distance = math.sqrt(mob_table.distance)
+ end
+ return mob_table
+end
+
+function target_type_check(spell)
+ --[[ Spawn type mapping:
+ 1 = Other players
+ 2 = Town NPCs, AH counters, Logging Points, etc.
+ Bit 1 = 1 PC
+ Bit 2 = 2 NPC (not attackable)
+ Bit 3 = 4 Party Member
+ Bit 4 = 8 Ally
+ Bit 5 = 16 Enemy
+ Bit 6 = 32 Door (Environment)
+ 13 = Self
+ 14 = Trust NPC in party
+ 16 = Monsters
+ 34 = Some doors
+ ]]
+
+ local temptype = spell.target.type
+ if temptype ~= 'NPC' then
+ temptype = temptype:lower():ucfirst()
+ end
+
+ if temptype == 'Player' and spell.target.hpp == 0 then
+ temptype = 'Corpse'
+ elseif temptype == 'Player' and spell.target.ispartymember then
+ temptype = 'Party'
+ elseif temptype == 'Player' and spell.target.isallymember then
+ temptype = 'Ally'
+ end
+
+ if spell.targets[temptype] then return true end
+ return false
+end \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/triggers.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/triggers.lua
new file mode 100644
index 0000000..8df14f1
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/triggers.lua
@@ -0,0 +1,343 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+
+-----------------------------------------------------------------------------------
+--Name: outgoing_text(original,modified,blocked,ffxi)
+--Desc: Searches the client's outgoing text for GearSwap handled commands and
+-- returns '' if it finds one. Otherwise returns the command unaltered.
+--Args:
+---- original - String entered by the user
+---- modified - String after being modified by upstream addons/plugins
+---- blocked - Boolean indicating whether the outgoing text is blocked upstream
+---- ffxi - Boolean indicating whether the outgoing text is generated by FFXI
+-----------------------------------------------------------------------------------
+--Returns:
+---- none or ''
+-----------------------------------------------------------------------------------
+windower.register_event('outgoing text',function(original,modified,blocked,ffxi,extra_stuff,extra2)
+ windower.debug('outgoing text')
+ if gearswap_disabled then return modified end
+
+ local splitline = windower.from_shift_jis(windower.convert_auto_trans(modified)):gsub(' <wait %d+>',''):gsub('"(.-)"',function(str)
+ return str:gsub(' ',string.char(7))
+ end):split(' '):filter(-'')
+
+ if splitline.n == 0 then return end
+
+ local command = splitline[1]
+ local bstpet = command == '/bstpet'
+ local unified_prefix = unify_prefix[command]
+ local abil, temptarg, temp_mob_arr
+ if splitline[2] and not bstpet then
+ abil = splitline[2]:gsub(string.char(7),' '):lower() -- Why am I removing \x7?
+ elseif splitline[2] and bstpet and tonumber(splitline[2]) then
+ local pet_abilities = {}
+ for _,v in ipairs(windower.ffxi.get_abilities().job_abilities) do
+ if v >= bstpet_range.min and v <= bstpet_range.max then
+ pet_abilities[#pet_abilities+1] = v
+ end
+ end
+ if pet_abilities[tonumber(splitline[2])] then
+ abil = res.job_abilities[pet_abilities[tonumber(splitline[2])]].name:gsub(string.char(7),' '):lower() -- .name, or .english?
+ end
+ end
+
+ if validabils[language][unified_prefix] and validabils[language][unified_prefix][abil] then
+ temptarg, temp_mob_arr = valid_target(splitline[3])
+ elseif validabils[language][unified_prefix] then
+ temptarg, temp_mob_arr = valid_target(splitline[2])
+ end
+
+ if unified_prefix and temptarg and (validabils[language][unified_prefix][abil] or unified_prefix=='/ra') then
+ if st_flag then
+ st_flag = nil
+ return modified
+ elseif temp_mob_arr then
+ refresh_globals()
+
+ local r_line, find_monster_skill
+
+ function find_monster_skill(abil)
+ local line = false
+ if player.species and player.species.tp_moves then
+ -- Iterates over currently available monster TP moves instead of using validabils
+ for i,v in pairs(player.species.tp_moves) do
+ if res.monster_skills[i][language]:lower() == abil then
+ line = copy_entry(res.monster_skills[i])
+ break
+ end
+ end
+ end
+ return line
+ end
+
+ if unified_prefix == '/ma' then
+ r_line = copy_entry(res.spells[validabils[language][unified_prefix][abil]])
+ storedcommand = command..' "'..windower.to_shift_jis(r_line[language])..'" '
+ elseif unified_prefix == '/ms' and find_monster_skill(abil) then
+ r_line = find_monster_skill(abil)
+ storedcommand = command..' "'..windower.to_shift_jis(r_line[language])..'" '
+ elseif unified_prefix == '/ws' then
+ r_line = copy_entry(res.weapon_skills[validabils[language][unified_prefix][abil]])
+ storedcommand = command..' "'..windower.to_shift_jis(r_line[language])..'" '
+ elseif unified_prefix == '/ja' then
+ r_line = copy_entry(res.job_abilities[validabils[language][unified_prefix][abil]])
+ if bstpet then
+ storedcommand = command..' '..splitline[2]
+ else
+ storedcommand = command..' "'..windower.to_shift_jis(r_line[language])..'" '
+ end
+ elseif unified_prefix == '/item' then
+ r_line = copy_entry(res.items[validabils[language][unified_prefix][abil]])
+ r_line.prefix = '/item'
+ r_line.type = 'Item'
+ storedcommand = command..' "'..windower.to_shift_jis(r_line[language])..'" '
+ elseif unified_prefix == '/ra' then
+ r_line = copy_entry(resources_ranged_attack)
+ storedcommand = command..' '
+ end
+
+ r_line.name = r_line[language]
+ local spell = spell_complete(r_line)
+ spell.target = temp_mob_arr
+ spell.action_type = action_type_map[command]
+
+ if filter_pretarget(spell) then
+ if tonumber(splitline[splitline.n]) then
+ -- If the target is a number
+ local ts = command_registry:new_entry(spell)
+
+ if spell.prefix == '/item' then
+ -- Item use packet handling here
+ if bit.band(spell.target.spawn_type, 2) == 2 and find_inventory_item(spell.id) then
+ --0x36 packet
+ if spell.target.distance <= 6 then
+ command_registry[ts].proposed_packet = assemble_menu_item_packet(spell.target.id,spell.target.index,spell.id)
+ else
+ windower.add_to_chat(67, "Target out of range.")
+ return true
+ end
+ elseif find_usable_item(spell.id) then
+ --0x37 packet
+ command_registry[ts].proposed_packet = assemble_use_item_packet(spell.target.id,spell.target.index,spell.id)
+ end
+ else
+ command_registry[ts].proposed_packet = assemble_action_packet(spell.target.id,spell.target.index,outgoing_action_category_table[unify_prefix[spell.prefix]],spell.id,initialize_arrow_offset(spell.target))
+ end
+ -- The packets created above should not be used.
+ if command_registry[ts].proposed_packet then
+ equip_sets('precast',ts,spell)
+ return true
+ end
+ else
+ return equip_sets('pretarget',-1,spell)
+ end
+ else
+ return equip_sets('filtered_action',-1,spell)
+ end
+ end
+ end
+ return modified
+end)
+
+
+
+-----------------------------------------------------------------------------------
+--Name: parse.i[0x028](act)
+--Desc: Calls midcast or aftercast functions as appropriate in response to incoming
+-- action packets.
+--Args:
+---- act - Action packet array (described on the dev wiki)
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+parse.i[0x028] = function (data)
+ local act = windower.packets.parse_action(data)
+ if gearswap_disabled or act.category == 1 then return end
+
+-- local spell_res = ActionPacket.new(act):get_spell()
+
+ --print(((res[unpackedaction.resource] or {})[unpackedaction.spell_id] or {}).english,unpackedaction.type,unpackedaction.value,unpackedaction.interruption)
+ local temp_player_mob_table,temp_pet,pet_id = windower.ffxi.get_mob_by_index(player.index)
+ if temp_player_mob_table and temp_player_mob_table.pet_index then
+ temp_pet = windower.ffxi.get_mob_by_index(temp_player_mob_table.pet_index)
+ if temp_pet then
+ pet_id = temp_pet.id
+ end
+ end
+
+ if act.actor_id ~= player.id and act.actor_id ~= pet_id then
+ return -- If the action is not being used by the player, the pet, or is a melee attack then abort processing.
+ end
+
+ local prefix = ''
+
+ if act.actor_id == pet_id then
+ prefix = 'pet_'
+ end
+
+ local spell = get_spell(act)
+-- if not spell_res or (spell.english ~= spell_res.english) then print('Did not match.',spell.english,spell_res) end
+
+ if spell then logit('\n\n'..tostring(os.clock)..'(178) Event Action: '..tostring(spell[language])..' '..tostring(act.category))
+ else logit('\n\nNil spell detected') end
+
+ if spell and spell[language] then
+ spell.target = target_complete(windower.ffxi.get_mob_by_id(act.targets[1].id))
+ spell.action_type = action_type_map[unify_prefix[spell.prefix or 'Monster']]
+ elseif S{84,78}:contains(act.targets[1].actions[1].message) then -- "Paralyzed" and "too far away" respectively
+ local ts,tab = command_registry:delete_by_id(act.targets[1].id)
+ if tab and tab.spell and tab.spell.prefix == '/pet' then
+ tab.spell.interrupted = true
+ equip_sets('pet_aftercast',nil,tab.spell)
+ elseif tab and tab.spell then
+ tab.spell.interrupted = true
+ equip_sets('aftercast',nil,tab.spell)
+ end
+ return
+ else
+ if debugging.general then windower.send_command('input /echo Incoming Action packet did not generate a spell/aftercast.')end
+ return
+ end
+
+ --[[4 (action message) = "out of range" when attempting to melee something that's too far away
+ 78 (action message) = "too far away" when attempting to engage or cast magic on something that's too far away
+ 78 (action) = "too far away" when attempting to WS something that's too far away
+ 154 (action message) - "out of range" when attempting to use a JA on something that's too far away. param_1 is the JA ID]]
+
+ -- Paralysis of JAs/spells/etc. and Out of Range messages for avatars both send two action packets when they occur.
+ -- The first packet is a paralysis packet that contains the message and spell-appropriate information.
+ -- The second packet contains the interruption code and no useful information as far as I can see.
+ -- The same occurs for items, except that they are both category 9 messages.
+
+ -- For some reason avatar Out of Range messages send two packets (Category 4 and Category 7)
+ -- Category 4 contains real information, while Category 7 does not.
+ -- I do not know if this will affect automatons being interrupted.
+ local ts = command_registry:find_by_spell(spell)
+ if (jas[act.category] or uses[act.category]) then
+ if uses[act.category] and act.param == 28787 then
+ spell.action_type = 'Interruption'
+ spell.interrupted = true
+ else
+ spell.value = act.targets[1].actions[1].param
+ end
+ if ts then --or spell.prefix == '/item' then
+ -- Only aftercast things that were precasted.
+ -- Also, there are some actions (like being paralyzed while casting Ninjutsu) that sends two result action packets. Block the second packet.
+ refresh_globals()
+ command_registry[ts].midaction = false
+ equip_sets(prefix..'aftercast',ts,spell)
+ elseif debugging.command_registry then
+ msg.debugging('Hitting Aftercast without detecting an entry in command_registry')
+ end
+ elseif (readies[act.category] and act.param == 28787) then -- and not (act.category == 9 or (act.category == 7 and prefix == 'pet_'))) then
+ spell.action_type = 'Interruption'
+ spell.interrupted = true
+ if ts or spell.prefix == '/item' then
+ -- Only aftercast things that were precasted.
+ -- Also, there are some actions (like being paralyzed while casting Ninjutsu) that sends two result action packets. Block the second packet.
+ refresh_globals()
+ if command_registry[ts] then command_registry[ts].midaction = false end
+ equip_sets(prefix..'aftercast',ts,spell)
+ elseif debugging.command_registry then
+ msg.debugging('Hitting Aftercast without detecting an entry in command_registry')
+ end
+ elseif readies[act.category] and prefix == 'pet_' and act.targets[1].actions[1].message ~= 0 then
+ -- Entry for pet midcast. Excludes the second packet of "Out of range" BPs.
+ ts = command_registry:new_entry(spell)
+ refresh_globals()
+ command_registry[ts].pet_midaction = true
+ equip_sets('pet_midcast',ts,spell)
+ end
+end
+
+
+
+-----------------------------------------------------------------------------------
+--Name: parse.i[0x029](data)
+--Desc: Responds to incoming action message packets.
+--Args:
+---- arr - Action message packet arguments (described on the dev wiki):
+ -- actor_id,target_id,param_1,param_2,param_3,actor_index,target_index,message_id)
+-----------------------------------------------------------------------------------
+--Returns:
+---- none
+-----------------------------------------------------------------------------------
+parse.i[0x029] = function (data)
+ if gearswap_disabled then return end
+ local arr = {}
+ arr.actor_id = data:unpack('I',0x05)
+ arr.target_id = data:unpack('I',0x09)
+ arr.param_1 = data:unpack('I',0x0D)
+ arr.param_2 = data:unpack('I',0x11)%64 -- First 6 bits
+ arr.param_3 = math.floor(data:unpack('I',0x11)/64) -- Rest
+ arr.actor_index = data:unpack('H',0x15)
+ arr.target_index = data:unpack('H',0x17)
+ arr.message_id = data:unpack('H',0x19)%32768
+
+
+ windower.debug('action message')
+ if T{6,20,113,406,605,646}:contains(arr.message_id) then -- death messages
+ local ts,tab = command_registry:delete_by_id(arr.target_id)
+ if tab and tab.spell and tab.spell.prefix == '/pet' then
+ equip_sets('pet_aftercast',nil,tab.spell)
+ elseif tab and tab.spell then
+ equip_sets('aftercast',nil,tab.spell)
+ end
+ return
+ end
+
+ local tempplay = windower.ffxi.get_player()
+ local prefix = ''
+ if arr.actor_id ~= tempplay.id then
+ if tempplay.pet_index then
+ if arr.actor_id ~= windower.ffxi.get_mob_by_index(tempplay.pet_index).id then
+ return
+ else
+ prefix = 'pet_'
+ end
+ else
+ return
+ end
+ end
+
+ if unable_to_use:contains(arr.message_id) then
+ logit('\n\n'..tostring(os.clock)..'(195) Event Action Message: '..tostring(message_id)..' Interrupt')
+ local ts,tab = command_registry:find_by_time()
+
+ if tab and tab.spell then
+ tab.spell.interrupted = true
+ tab.spell.action_type = 'Interruption'
+ command_registry[ts].midaction = false
+ refresh_globals()
+ equip_sets(prefix..'aftercast',ts,tab.spell)
+ end
+ end
+end
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/user_functions.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/user_functions.lua
new file mode 100644
index 0000000..afdcf84
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/user_functions.lua
@@ -0,0 +1,423 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+-- Functions that are directly exposed to users --
+
+
+function set_language(lang)
+ if _global.current_event ~= 'get_sets' then
+ error('\nGearSwap: set_language() is only valid in the get_sets function', 2)
+ return
+ end
+ if lang and type(lang) == 'string' and (lang == 'english' or lang == 'japanese') then
+ rawset(_G,'language',lang)
+ refresh_globals()
+ else
+ error('\nGearSwap: set_language() was passed an invalid value ('..tostring(lang)..'). (must be a string)', 2)
+ end
+end
+
+function debug_mode(boolean)
+ if type(boolean) == "boolean" then _settings.debug_mode = boolean
+ elseif boolean == nil then
+ _settings.debug_mode = true
+ else
+ error('\nGearSwap: show_swaps() was passed an invalid value ('..tostring(boolean)..'). (true/no value/nil=on, false=off)', 2)
+ end
+end
+
+function show_swaps(boolean)
+ if type(boolean) == "boolean" then _settings.show_swaps = boolean
+ elseif boolean == nil then
+ _settings.show_swaps = true
+ else
+ error('\nGearSwap: show_swaps() was passed an invalid value ('..tostring(boolean)..'). (true/no value/nil=on, false=off)', 2)
+ end
+end
+
+function cancel_spell(boolean)
+ if _global.current_event ~= 'precast' and _global.current_event ~= 'pretarget' and _global.current_event ~= 'filtered_action' then
+ error('\nGearSwap: cancel_spell() is only valid in the precast, pretarget, or filtered_action functions', 2)
+ return
+ end
+ if type(boolean) == "boolean" then _global.cancel_spell = boolean
+ elseif boolean == nil then
+ _global.cancel_spell = true
+ else
+ error('\nGearSwap: cancel_spell() was passed an invalid value ('..tostring(boolean)..'). (true/no value/nil=Cancel the spell, false=do not cancel the spell)', 2)
+ end
+end
+
+function move_spell_target(position_table)
+ if _global.current_event ~= 'precast' then
+ error('\nGearSwap: move_spell_target() is only valid in the precast function', 2)
+ return
+ end
+
+ if type(position_table) == 'table' and type(position_table.x or position_table.X) == 'number' and
+ type(position_table.y or position_table.Y) == 'number' and
+ type(position_table.z or positino_table.Z) == 'number' then
+ _global.target_arrow.x = position_table.x or position_table.X
+ _global.target_arrow.y = position_table.y or position_table.Y
+ _global.target_arrow.z = position_table.z or position_table.Z
+ print_set(_global.target_arrow)
+ else
+ error('\nGearSwap: move_spell_target() was passed an invalid value ('..tostring(position_table)..'). Should be a table with x, y, and z keys (offset from target)', 2)
+ end
+end
+
+function change_target(name)
+ if _global.current_event ~= 'pretarget' then
+ error('\nGearSwap: change_target() is only valid in the pretarget function', 2)
+ return
+ end
+ if name and type(name)=='string' then
+ if valid_target(name) then
+ _,_global.new_target = valid_target(name)
+ else
+ error('\nGearSwap: change_target() was passed an invalid value ('..tostring(name)..'). (must be a valid target)', 2)
+ end
+ else
+ error('\nGearSwap: change_target() was passed an invalid value ('..tostring(name)..'). (must be a string)', 2)
+ end
+end
+
+function cast_delay(delay)
+ if _global.current_event ~= 'precast' and _global.current_event ~= 'pretarget' then
+ error('\nGearSwap: cast_delay() is only valid in the precast and pretarget functions', 2)
+ return
+ end
+ if tonumber(delay) then
+ _global[_global.current_event.."_cast_delay"] = tonumber(delay)
+ else
+ error('\nGearSwap: cast_delay() was passed an invalid value ('..tostring(delay)..'). (cast delay must be a number of seconds)', 2)
+ end
+end
+
+-- Combines the provided gear sets into a new set. Returns the result.
+function set_combine(...)
+ return set_merge(false,{}, ...)
+end
+
+-- Combines the provided gear sets into the equip_list set.
+function equip(...)
+ set_merge(true,equip_list, ...)
+end
+
+function disable(...)
+ local disable_tab = {...}
+ if type(disable_tab[1]) == 'table' then
+ disable_tab = disable_tab[1] -- Compensates for people passing a table instead of a series of strings.
+ end
+ for i,v in pairs(disable_tab) do
+ if slot_map[v] then
+ rawset(disable_table,slot_map[v],true)
+ else
+ error('\nGearSwap: disable error, passed an unrecognized slot name. ('..tostring(v)..')',2)
+ end
+ end
+end
+
+function enable(...)
+ local enable_tab = {...}
+ if type(enable_tab[1]) == 'table' then
+ enable_tab = enable_tab[1] -- Compensates for people passing a table instead of a series of strings.
+ end
+ local sending_table = {}
+ for i,v in pairs(enable_tab) do
+ local local_slot = get_default_slot(v)
+ if local_slot then
+ disable_table[toslotid(v)] = false
+ if not_sent_out_equip[local_slot] then
+ sending_table[local_slot] = not_sent_out_equip[local_slot]
+ not_sent_out_equip[local_slot] = nil
+ end
+ else
+ error('\nGearSwap: enable error, passed an unrecognized slot name. ('..tostring(v)..')',2)
+ end
+ end
+
+ return sending_table
+end
+
+function user_enable(...)
+ local sending_table = enable(...)
+
+ if table.length(sending_table) > 0 then
+ equip(sending_table)
+ end
+ return sending_table
+end
+
+function command_enable(...)
+ local sending_table = enable(...)
+
+ if table.length(sending_table) > 0 then
+ refresh_globals()
+ equip_sets('equip_command',nil,sending_table)
+ end
+end
+
+function print_set(set,title)
+ if not set then
+ if title then
+ error('\nGearSwap: print_set error, '..windower.to_shift_jis(tostring(title))..' set is nil.', 2)
+ else
+ error('\nGearSwap: print_set error, set is nil.', 2)
+ end
+ return
+ elseif type(set) ~= 'table' then
+ if title then
+ error('\nGearSwap: print_set error, '..windower.to_shift_jis(tostring(title))..' set is not a table.', 2)
+ else
+ error('\nGearSwap: print_set error, set is not a table.', 2)
+ end
+ end
+ if table.length(set) == 0 then
+ if title then
+ msg.add_to_chat(1,'------------------'.. windower.to_shift_jis(tostring(title))..' -- Empty Table -----------------')
+ else
+ msg.add_to_chat(1,'-------------------------- Empty Table -------------------------')
+ end
+ return
+ elseif title then
+ msg.add_to_chat(1,'------------------------- '..windower.to_shift_jis(tostring(title))..' -------------------------')
+ else
+ msg.add_to_chat(1,'----------------------------------------------------------------')
+ end
+ local function print_element(key,value)
+ if type(value) == 'table' and value.name then
+ msg.add_to_chat(8,windower.to_shift_jis(tostring(key))..' '..windower.to_shift_jis(tostring(value.name))..' (Adv.)')
+ else
+ msg.add_to_chat(8,windower.to_shift_jis(tostring(key))..' '..windower.to_shift_jis(tostring(value)))
+ end
+ end
+ local function cmp_key(key,tab)
+ for k in pairs(tab) do
+ if k:lower() == key:lower() then
+ return k
+ end
+ end
+ end
+
+ if #set == table.length(set) then -- If it is a list (keyed by continuous whole number starting at 1), then print it out in order
+ for key,value in ipairs(set) do
+ print_element(key,value)
+ end
+ else -- Otherwise, try to print out the gear in order and then everything else.
+ for _,key in ipairs({'main','sub','ranged','range','ammo','head','neck','lear','ear1','learring','left_ear','rear','ear2','rearring','right_ear','body','hands','lring','ring1','left_ring','rring','ring2','right_ring','back','waist','legs','feet'}) do
+ local k = cmp_key(key,set)
+ if k then
+ print_element(k,set[k])
+ end
+ end
+ for key,value in pairs(set) do
+ if not slot_map[key] then
+ print_element(key,set[key])
+ end
+ end
+ end
+ msg.add_to_chat(1,'----------------------------------------------------------------')
+end
+
+function send_cmd_user(command)
+ if string.byte(1) ~= 0x40 then
+ command='@'..command
+ end
+ windower.send_command(command)
+end
+
+function register_event_user(str,func)
+ if type(func)~='function' then
+ error('\nGearSwap: windower.register_event() was passed an invalid value ('..tostring(func)..'). (must be a function)', 2)
+ elseif type(str) ~= 'string' then
+ error('\nGearSwap: windower.register_event() was passed an invalid value ('..tostring(str)..'). (must be a string)', 2)
+ end
+ local id = windower.register_event(str,user_equip_sets(func))
+ registered_user_events[id] = true
+ return id
+end
+
+function raw_register_event_user(str,func)
+ if type(func)~='function' then
+ error('\nGearSwap: windower.register_event() was passed an invalid value ('..tostring(func)..'). (must be a function)', 2)
+ elseif type(str) ~= 'string' then
+ error('\nGearSwap: windower.register_event() was passed an invalid value ('..tostring(str)..'). (must be a string)', 2)
+ end
+ local id = windower.register_event(str,setfenv(func,user_env))
+ registered_user_events[id] = true
+ return id
+end
+
+function unregister_event_user(id)
+ if type(id)~='number' then
+ error('\nGearSwap: windower.unregister_event() was passed an invalid value ('..tostring(id)..'). (must be a number)', 2)
+ end
+ windower.unregister_event(id)
+ registered_user_events[id] = nil
+end
+
+function user_equip_sets(func)
+ return setfenv(function(...)
+ if not gearswap.gearswap_disabled then
+ gearswap.refresh_globals(true)
+ return gearswap.equip_sets(func,nil,...)
+ end
+ end,user_env)
+end
+
+function user_unhandled_command(func)
+ if type(func) ~= 'function' then
+ error('\nGearSwap: unhandled_command was passed an invalid value ('..tostring(func)..'). (must be a function)', 2)
+ end
+ unhandled_command_events[#unhandled_command_events+1] = setfenv(func,user_env)
+end
+
+function include_user(str, load_include_in_this_table)
+ if not (type(str) == 'string') then
+ error('\nGearSwap: include() was passed an invalid value ('..tostring(str)..'). (must be a string)', 2)
+ end
+
+ str = str:lower()
+ if type(package.loaded[str]) == 'table' then
+ return package.loaded[str]
+ elseif T{'pack'}:contains(str) then
+ return
+ end
+
+ if str:sub(-4)~='.lua' then str = str..'.lua' end
+ local path, loaded_values = pathsearch({str})
+
+ if not path then
+ error('\nGearSwap: Cannot find the include file ('..tostring(str)..').', 2)
+ end
+
+ local f, err = loadfile(path)
+ if f and not err then
+ if load_include_in_this_table and type(load_include_in_this_table) == 'table' then
+ setmetatable(load_include_in_this_table, {__index=user_env._G})
+ setfenv(f, load_include_in_this_table)
+ pcall(f, load_include_in_this_table)
+ return load_include_in_this_table
+ else
+ setfenv(f,user_env)
+ return f()
+ end
+ else
+ error('\nGearSwap: Error loading file ('..tostring(str)..'): '..err, 2)
+ end
+end
+
+-- Allow the user to set a path subdirectory to check when searching for included files.
+-- This path is checked as a subdirectory to each fixed path, before the fixed path itself is checked.
+-- Path argument can only be a string; otherwise this is set to nil.
+function user_include_path(path)
+ if type(path) == 'string' then
+ include_user_path = path
+ else
+ include_user_path = nil
+ end
+end
+
+
+function user_midaction(bool)
+ if bool == false then
+ for i,v in pairs(command_registry) do
+ if v.midaction then
+ command_registry[i].midaction = false
+ end
+ end
+ end
+
+ for i,v in pairs(command_registry) do
+ if type(v) == 'table' and v.midaction then
+ return true, v.spell
+ end
+ end
+
+ return false
+end
+
+function user_pet_midaction(bool)
+ if bool == false then
+ for i,v in pairs(command_registry) do
+ if v.pet_midaction then
+ command_registry.pet_midaction = false
+ end
+ end
+ end
+
+ for i,v in pairs(command_registry) do
+ if v.pet_midaction then
+ return true, v.spell
+ end
+ end
+
+ return false
+end
+
+function add_to_chat_user(num,str)
+ local backup_str
+ if type(num) == 'string' then
+ -- It was passed a string as the first argument.
+ str = not tonumber(str) and str or num
+ num = 8
+ elseif not num and str and type(str) == 'string' then
+ -- It only needs the number.
+ num=8
+ end
+
+ if language == 'japanese' then
+ msg.add_to_chat(num,windower.to_shift_jis(str))
+ else
+ msg.add_to_chat(num,str)
+ end
+end
+
+
+function user_sleep(delay)
+ if not delay then
+ error('\nGearSwap: coroutine.sleep() not passed a delay value', 2)
+ elseif type(delay) ~= 'number' or delay < 0 then
+ error('\nGearSwap: coroutine.sleep() was passed an invalid value ('..tostring(delay)..'). (must be a number >= 0)', 2)
+ else
+ coroutine.yield('sleep',delay)
+ end
+end
+
+function user_yield()
+ coroutine.yield('yield')
+end
+
+
+-- Define the user windower functions.
+user_windower = {register_event = register_event_user, raw_register_event = raw_register_event_user,
+ unregister_event = unregister_event_user, send_command = send_cmd_user,add_to_chat=add_to_chat_user}
+user_coroutine = coroutine
+user_coroutine.sleep = user_sleep
+user_coroutine.yield = user_yield
+setmetatable(user_windower,{__index=windower}) \ No newline at end of file
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/validate.lua b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/validate.lua
new file mode 100644
index 0000000..5969878
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/validate.lua
@@ -0,0 +1,319 @@
+--Copyright (c) 2013~2016, Byrthnoth
+--All rights reserved.
+
+--Redistribution and use in source and binary forms, with or without
+--modification, are permitted provided that the following conditions are met:
+
+-- * Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- * Redistributions in binary form must reproduce the above copyright
+-- notice, this list of conditions and the following disclaimer in the
+-- documentation and/or other materials provided with the distribution.
+-- * Neither the name of <addon name> nor the
+-- names of its contributors may be used to endorse or promote products
+-- derived from this software without specific prior written permission.
+
+--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+--ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+--WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+--DISCLAIMED. IN NO EVENT SHALL <your name> BE LIABLE FOR ANY
+--DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+--(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+--LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+--ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+--(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------------------------------------------
+-- Primary entry point.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Validate either gear sets or inventory.
+-- gs validate [inv|set] [filterlist]
+-- Where inv == i or inv or inventory
+-- Where set == s or set or sets
+-- Where filterlist can use - to negate a value (eg: -charis for everything except charis, instead of only charis)
+function validate(options)
+ local validateType = 'sets'
+ if options and #options > 0 then
+ if S{'sets','set','s'}:contains(options[1]:lower()) then
+ table.remove(options,1)
+ elseif S{'inventory','inv','i'}:contains(options[1]:lower()) then
+ validateType = 'inv'
+ table.remove(options,1)
+ end
+ end
+
+ if validateType == 'inv' then
+ validate_inventory(options)
+ else
+ validate_sets(options)
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Functions to handle the primary logic separation.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Function for determining and displaying which items from a player's inventory are not in their gear sets.
+function validate_inventory(filter)
+ msg.addon_msg(123,'Checking for items in inventory that are not used in your gear sets.')
+
+ local extra_items = search_sets_for_items_in_bag(items.inventory, filter)
+
+ local display_list = get_item_names(extra_items):sort(insensitive_sort)
+ display_list:map(function(item) msg.add_to_chat(120, windower.to_shift_jis((string.gsub(item, "^%l", string.upper))) ) end)
+ msg.addon_msg(123,'Final count = '..tostring(display_list:length()))
+end
+
+-- Function for determining and displaying which items of a player's gear sets are not in their inventory.
+function validate_sets(filter)
+ msg.addon_msg(123,'Checking for items in gear sets that are not in your inventory.')
+
+ local missing_items = search_bags_for_items_in_set(sets, filter)
+
+ local display_list = get_item_names(missing_items):sort(insensitive_sort)
+ display_list:map(function(item) msg.add_to_chat(120, windower.to_shift_jis((string.gsub(item, "^%l", string.upper))) ) end)
+ msg.addon_msg(123,'Final count = '..tostring(display_list:length()))
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions for output and id>name conversion.
+-------------------------------------------------------------------------------------------------------------------
+
+-- Given a set of item IDs, create a set of item names.
+function get_item_names(item_set)
+ return item_set:map(get_item_name)
+end
+
+-- Get the name of an item. Handle the various types of items that can be passed to this function.
+function get_item_name(item)
+ local name = ''
+ local aug = ''
+
+ if type(item) == 'string' then
+ name = item
+ elseif type(item) == 'table' then
+ if item.id then
+ name = get_formal_name_by_item_id(item.id)
+ elseif item.name then
+ name = item.name
+ end
+
+
+ local aug = item.aug and table.concat(item.aug,', ') or get_augment_string(item)
+ if aug then
+ name = name .. ' {' .. aug .. '}'
+ end
+ end
+
+ return name
+end
+
+-- Get the (preferably) capitalized version of an item's name, or the
+-- log version if the short version is abbreviated.
+function get_formal_name_by_item_id(id)
+ local shortname = get_short_name_by_item_id(id)
+ local logname = get_log_name_by_item_id(id)
+
+ return (#logname > #shortname) and logname or shortname
+end
+
+-- Given an item id, get the log item name.
+function get_log_name_by_item_id(id)
+ return res.items[id][language..'_log']
+end
+
+-- Given an item id, get the short item name.
+function get_short_name_by_item_id(id)
+ return res.items[id][language]
+end
+
+-- If the provided item has augments on it, return a string containing the list of augments.
+function get_augment_string(item)
+ local augments
+ if item.extdata then
+ augments = extdata.decode(item).augments or {}
+ else
+ augments = item.augment or item.augments
+ end
+
+ local started = false
+ if augments and #augments > 0 then
+ local aug_str = ''
+ for aug_ind,augment in pairs(augments) do
+ if augment ~= 'none' then
+ if started then
+ aug_str = aug_str .. ','
+ end
+
+ aug_str = aug_str.."'"..augment.."'"
+ started = true
+ end
+ end
+
+ return aug_str
+ end
+end
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions for searching.
+-------------------------------------------------------------------------------------------------------------------
+
+-- General search to find what 'extra' items are in inventory
+function search_sets_for_items_in_bag(bag, filter)
+ local extra_bag_items = S{}
+ for _,item in ipairs(bag) do
+ if item.id ~= 0 and tryfilter(lowercase_name(get_log_name_by_item_id(item.id)), filter) then
+ if not find_in_sets(item, sets) then
+ extra_bag_items:add(item)
+ end
+ end
+ end
+
+ return extra_bag_items
+end
+
+-- General search to find what 'extra' items are in the job's gear sets
+function search_bags_for_items_in_set(gear_table, filter, missing_items, stack)
+ if stack and stack:contains(gear_table) then return end
+ if type(gear_table) ~= 'table' then return end
+ if missing_items == nil then missing_items = S{} end
+
+ for i,v in pairs(gear_table) do
+ local name = (type(v) == 'table' and v.name) or v
+ local aug = (type (v) == 'table' and (v.augments or v.augment))
+
+ if type(aug) == 'string' then aug = {aug} end
+ if type(name) == 'string' and name ~= 'empty' and name ~= '' and type(i) == 'string' then
+ if not slot_map[i] then
+ msg.addon_msg(123,windower.to_shift_jis(tostring(i))..' contains a "name" element but is not a valid slot.')
+ elseif tryfilter(lowercase_name(name), filter) and not find_in_equippable_inventories(name, aug) then
+ -- This is one spot where inventory names will be left hardcoded until an equippable bool is added to the resources
+ missing_items:add({name=lowercase_name(name),aug=aug})
+ end
+ elseif type(name) == 'table' and name ~= empty then
+ if not stack then stack = S{} end
+
+ stack:add(gear_table)
+ search_bags_for_items_in_set(v, filter, missing_items, stack)
+ stack:remove(gear_table)
+ end
+ end
+
+ return missing_items
+end
+
+-- Utility function to search equippable inventories
+function find_in_equippable_inventories(name,aug)
+ for _,bag in pairs(equippable_item_bags) do
+ if find_in_inv(items[to_windower_bag_api(bag.en)], name, aug) then
+ return true
+ end
+ end
+end
+
+-- Utility function to help search sets
+function find_in_sets(item, tab, stack)
+ if stack and stack:contains(tab) then
+ return false
+ end
+
+ local item_short_name = lowercase_name(get_short_name_by_item_id(item.id))
+ local item_log_name = lowercase_name(get_log_name_by_item_id(item.id))
+
+ for _,v in pairs(tab) do
+ local name = (type(v) == 'table' and v.name) or v
+ local aug = (type(v) == 'table' and (v.augments or v.augment))
+ if type(aug) == 'string' then aug = {aug} end
+ if type(name) == 'string' then
+ if compare_item(item, name, aug, item_short_name, item_log_name) then
+ return true
+ end
+ elseif type(v) == 'table' then
+ if not stack then stack = S{} end
+
+ stack:add(tab)
+ local try = find_in_sets(item, v, stack)
+ stack:remove(tab)
+
+ if try then
+ return true
+ end
+ end
+ end
+
+ return false
+end
+
+-- Utility function to help search inventory
+function find_in_inv(bag, name, aug)
+ for _,item in ipairs(bag) do
+ if compare_item(item, name, aug) then
+ return true
+ end
+ end
+ return false
+end
+
+-- Utility function to compare items that may possibly be augmented.
+function compare_item(item, name, aug, item_short_name, item_log_name)
+ if item.id == 0 or not res.items[item.id] then
+ return false
+ end
+
+ name = lowercase_name(name)
+ item_short_name = lowercase_name(item_short_name or get_short_name_by_item_id(item.id))
+ item_log_name = lowercase_name(item_log_name or get_log_name_by_item_id(item.id))
+
+ if item_short_name == name or item_log_name == name then
+ if not aug or extdata.compare_augments(aug, extdata.decode(item).augments) then
+ return true
+ end
+ end
+
+ return false
+end
+
+
+-------------------------------------------------------------------------------------------------------------------
+-- Utility functions for filtering.
+-------------------------------------------------------------------------------------------------------------------
+
+function tryfilter(itemname, filter)
+ if not filter or #filter == 0 then
+ return true
+ end
+
+ local pass = true
+ for _,v in pairs(filter) do
+ if v[1] == '-' then
+ pass = false
+ v = v:sub(2)
+ end
+ if not v or type(v) ~= 'string' then
+ print_set(filter,'filter with bad v')
+ end
+ if itemname:contains(lowercase_name(v)) then
+ return pass
+ end
+ end
+ return not pass
+end
+
+
+function lowercase_name(name)
+ if type(name) == 'string' then
+ return name:lower()
+ else
+ return name
+ end
+end
+
+function insensitive_sort(item1, item2)
+ if type(item1) == 'string' and type(item2) == 'string' then
+ return item1:lower() < item2:lower()
+ else
+ return item1 < item2
+ end
+end
diff --git a/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/version_history.txt b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/version_history.txt
new file mode 100644
index 0000000..3d4e29e
--- /dev/null
+++ b/Data/BuiltIn/Libraries/lua-addons/addons/GearSwap/version_history.txt
@@ -0,0 +1,236 @@
+-------------------------------------------------
+GearSwap 0.935 - Filter enchanted items with duplicate names.
+-------------------------------------------------
+GearSwap 0.934 - Fixed /item command handling.
+-------------------------------------------------
+GearSwap 0.933 - Adjusted how item use packets are chosen.
+-------------------------------------------------
+GearSwap 0.932 - Fixed bug that prevented gearswap from identifying items by their bag when worn.
+-------------------------------------------------
+GearSwap 0.931 - Added support to exporting with custom file names.
+-------------------------------------------------
+GearSwap 0.930 - Fixed a problem with player[bag][item_name].count in the user environment.
+-------------------------------------------------
+GearSwap 0.929 - Adjusted the print_set function to give preference and order to slot name keys.
+-------------------------------------------------
+GearSwap 0.928 - Changed how the outgoing equipment chunks were parsed due to a changing understanding of how the windower packet events work.
+-------------------------------------------------
+GearSwap 0.927 - Sped up equipment handling and made Rare items ignore augments.
+-------------------------------------------------
+GearSwap 0.926 - Simplified the equipment handling and used 0x117 incoming.
+-------------------------------------------------
+GearSwap 0.925 - Fixed minor problem that arose with the previous version's implementation of the equip history.
+-------------------------------------------------
+GearSwap 0.924 - Fixed problems that arose when attempting to equip two of the same item in two different slots. Changed how player equipment is handled.
+-------------------------------------------------
+GearSwap 0.923 - Added augments to //gs validate sets.
+-------------------------------------------------
+GearSwap 0.922 - Changed how the reload event works so it no longer causes stack problems.
+-------------------------------------------------
+GearSwap 0.921 - Split a buff overwriting itself off into a separate event, buff_refresh, and adjusted documentation.
+-------------------------------------------------
+GearSwap 0.920 - Made file_unload fire on load commands and changed its argument to be the previous file name.
+-------------------------------------------------
+GearSwap 0.919 - Switched handling of buff_change to rely on 0x063 incoming v9, which includes the durations.
+-------------------------------------------------
+GearSwap 0.918 - Added player.buff_details and made it so buff_change triggers when buffs are overwritten.
+-------------------------------------------------
+GearSwap 0.917 - Added code to ignore already-equipped items when searching for items to equip.
+-------------------------------------------------
+GearSwap 0.916 - Added wardrobes 3 and 4.
+-------------------------------------------------
+GearSwap 0.915 - Added wardrobe2.
+-------------------------------------------------
+GearSwap 0.914 - Added /bstpet handling.
+-------------------------------------------------
+GearSwap 0.913 - Fixed japanese job point filter in filter_pretarget
+- Fixed command handling so filtered_action will be called for spells you haven't learned
+- Fixed pet_status_change so it will always be called (including in cases where the pet is dead)
+- Re-implemented pet.tp
+- Added player.base_str, player.add_str, and player.str, which is your total strength stat. Did this for all base stats as well as resistances and attack/defense. May not be reliable, but was requested.
+- Added spell.value, but there is no filter determining when it's meaningful or not. Mostly useful for COR rolls, which people should be able to filter out on their own.
+-------------------------------------------------
+GearSwap 0.912 - Added Geo- spell positioning.
+-------------------------------------------------
+GearSwap 0.911 - Removed dependence of Unbridled Wisdom spells on the respective buff.
+-------------------------------------------------
+GearSwap 0.910 - Fixed set_combine so it does not respect the disable_table and updated copyright statements.
+-------------------------------------------------
+GearSwap 0.908 - Fixed party.count failing to update in user_env. Added README.md file.
+-------------------------------------------------
+GearSwap 0.907 - Fixed a spell completion issue in aftercast caused by action packets that had no message.
+-------------------------------------------------
+GearSwap 0.906 - Added BLU magic spells and handled one aftercast that uses the 0x053 incoming packet.
+-------------------------------------------------
+GearSwap 0.904 - Adjusted party buff information to run off indices instead of names.
+-------------------------------------------------
+GearSwap 0.903 - Added buff information to the alliance/party structure.
+-------------------------------------------------
+GearSwap 0.902 - Changed action packet length.
+-------------------------------------------------
+GearSwap 0.901 - Modified add_to_chat handling throughout GearSwap so it would show up in a consistent log (the same one as mode 1)
+-------------------------------------------------
+GearSwap 0.900 - Adjusted GearSwap to be compatible with the new library behavior.
+-------------------------------------------------
+GearSwap 0.899 - Adjusted GearSwap to be compatible with the new library behavior.
+-------------------------------------------------
+GearSwap 0.898 - Fixed a bug with using cast_delay in the pretarget function.
+-------------------------------------------------
+GearSwap 0.897 - Superior system support and filtering.
+-------------------------------------------------
+GearSwap 0.896 - Command_registry class addition.
+-------------------------------------------------
+GearSwap 0.894 - Various bugfixes (including Monstrosity loading).
+-------------------------------------------------
+GearSwap 0.892 - Proxied primitive and text commands.
+-------------------------------------------------
+GearSwap 0.891 - Fixed a few bugs with /item commands. Exposed the _addon table.
+-------------------------------------------------
+GearSwap 0.890 - Added support for enchanted items in Wardrobe. Added a filter for enchanted items/armors. Made resources tables static.
+-------------------------------------------------
+GearSwap 0.880 - Accommodated booleans and numbers in equip commands. Fixed world.conquest.strengths.
+-------------------------------------------------
+GearSwap 0.875 - Changed command_registry handling slightly
+-------------------------------------------------
+GearSwap 0.874 - Added indi_change event
+-------------------------------------------------
+GearSwap 0.873 - Adjusted midaction and pet_midaction.
+-------------------------------------------------
+GearSwap 0.872 - Fixed Encumbrance, Job Change issues, and a nil error on login.
+-------------------------------------------------
+GearSwap 0.871 - Debugging and Logging changes.
+-------------------------------------------------
+GearSwap 0.870 - Rewrite of "order" as "priority" for advanced tables.
+-------------------------------------------------
+GearSwap 0.868 - Ironed out some bugs. Added .in_mog_house. Made pet_change work for gaining charmed pets.
+-------------------------------------------------
+GearSwap 0.865 - Replicated part of the LuaCore API in GearSwap and set limits on global variable refresh rates.
+-------------------------------------------------
+GearSwap 0.860 - Reworked the augment system to use the extdata library. Consequently enabled evolith support.
+-------------------------------------------------
+GearSwap 0.851 - Added Load command and file path support for a libs folder.
+-------------------------------------------------
+GearSwap 0.850 - Japanese support.
+-------------------------------------------------
+GearSwap 0.840 - Overhaul for Wardrobe. Preparations for new augment library.
+-------------------------------------------------
+GearSwap 0.837 - Created infrastructure for GearSwap served information and added Indi spell aura information to player.
+-------------------------------------------------
+GearSwap 0.836 - Added augments to the export feature for currently equipped sets and inventory.
+-------------------------------------------------
+GearSwap 0.835 - Adaptation for new resources.
+-------------------------------------------------
+GearSwap 0.834 - Filter now handles Scholar spells and Blue Magic.
+-------------------------------------------------
+GearSwap 0.833 - Added the first version of a more authentic filter.
+-------------------------------------------------
+GearSwap 0.832 - Adapted GearSwap for the .lua version of resources lib.
+-------------------------------------------------
+GearSwap 0.830 - Fixed various bugs. Differentiated pretarget and precast delays. Augments work again.
+-------------------------------------------------
+GearSwap 0.823 - Changed GearSwap to run off the resources library. Fixed Double-Up's aftercast.
+-------------------------------------------------
+GearSwap 0.822 - Added demomode and filters to prevent impossible actions.
+-------------------------------------------------
+GearSwap 0.821 - Finalized register_event(). Protected user files to prevent error propagation.
+-------------------------------------------------
+GearSwap 0.820 - Added 0x02, 0x35 extdata augment handling. Added sub_job_change(new,old) event.
+-------------------------------------------------
+GearSwap 0.819 - API adjustments. Massive refactoring of the scheduler, which improved midaction() and pet_midaction(). Fixed " <wait X>" macros and the buff_change() event.
+-------------------------------------------------
+GearSwap 0.818 - Added pet_change() user event. Refined event registration.
+-------------------------------------------------
+GearSwap 0.817 - Added filtering to //gs validate. Added pet_midcast() user function.
+-------------------------------------------------
+GearSwap 0.816 - Automaton midcast fixed. //gs fixed. //gs export no longer requires a user file (except for the sets option).
+-------------------------------------------------
+GearSwap 0.815 - Automaton information added.
+-------------------------------------------------
+GearSwap 0.814 - Fixed the equipment table, which was being unreliable.
+-------------------------------------------------
+GearSwap 0.813 - Fixed issues with /pet. Meddled with triggering interruption again. Potentially re-created the Paralysis issue in the process.
+-------------------------------------------------
+GearSwap 0.812 - Fixed Interruption handling for action messages that show the player as the target regardless the real target.
+-------------------------------------------------
+GearSwap 0.811 - Added a check for whether or not a given spell/ability can be used. Made player.equipment update between pretarget/precast/midcast. Deleted the second argument to pretarget, precast, midcast, aftercast, pet_midcast, and pet_aftercast.
+-------------------------------------------------
+GearSwap 0.810 - Changed how precast and midcast are defined for players. Removed verify_equip and force_send. Refactored a great deal.
+-------------------------------------------------
+GearSwap 0.802 - Introduced the debugging "clocking" setting. Changed how actions are injected.
+-------------------------------------------------
+GearSwap 0.801 - Combated double precasts, fixed a variety of nil errors, and made Paralysis work (hopefully).
+-------------------------------------------------
+GearSwap 0.800 - Added pretarget and moved precast to outgoing_chunk. Changed "buff_change()" to pass a boolean instead of a string indicating whether the buff was gained or lost.
+-------------------------------------------------
+GearSwap 0.723 - Removed a debug message, updated documentation, and refactored equip_processing.lua extensively.
+-------------------------------------------------
+GearSwap 0.722 - Changed the ranged attack spell table to reflect the resources. Added spell.interrupted boolean. Added a persistent spell for ambiguous interruption message cases. Changed interrupted action.type to "Interruption"... Many things.
+-------------------------------------------------
+GearSwap 0.721 - Adjusted for API changes and related documentation.
+-------------------------------------------------
+GearSwap 0.720 - Fixed Pet TP magnitude, exposed OS library, updated documentation, improved file name handling (a bit), and minor changes to the validation algorithm.
+-------------------------------------------------
+GearSwap 0.719 - Removed dependence on sets library. Fixed error that occured when there was no subjob.
+-------------------------------------------------
+GearSwap 0.718 - Fixed player.status. Added the validate command. Improved export sets' efficiency. Updated documentation.
+-------------------------------------------------
+GearSwap 0.717 - Fixed equipment removal. Added sets.naked. Handled category 11 outgoing actions (homepointing). Updated documentation.
+-------------------------------------------------
+GearSwap 0.716 - Export now looks up the proper equipment slot. Changed syntax for equipping an empty slot to be less ambiguous. Fixed unequipping on zone (I think).
+-------------------------------------------------
+GearSwap 0.715 - Converted resource strings to numbers if appropriate. Added Pianissimo cast_time handling.
+-------------------------------------------------
+GearSwap 0.714 - Minor changes to make export more user friendly.
+-------------------------------------------------
+GearSwap 0.713 - Added equipment order support. Made export sets more safe. Added equip "empty" support.
+-------------------------------------------------
+GearSwap 0.712 - Added adventuring fellow handling.
+-------------------------------------------------
+GearSwap 0.711 - Refined category exclusions for the outgoing action packet.
+-------------------------------------------------
+GearSwap 0.710 - Added windower.debug statements.
+-------------------------------------------------
+GearSwap 0.709 - Looped midact back through console with a 1.5 second delay. Tested for 2 days without extra DC issues.
+-------------------------------------------------
+GearSwap 0.708 - Added Encumbrance support. Added a message for Exporting files. Added another _global.midaction fix. Altered how Disable works, slightly.
+-------------------------------------------------
+GearSwap 0.707 - Forced the "sets" export to convert all equipment to inventory names. Started to add encumbrance code. Added a check for debuffs that prevent ability/spell usage in outgoing_chunk to reset midact.
+-------------------------------------------------
+GearSwap 0.706 - Added a "sets" option to the export command.
+-------------------------------------------------
+GearSwap 0.705 - Likely eliminated the Line 71 and 324 errors. Changed the "disable" console command so that it can be used for individual slots or all of gearSwap.
+-------------------------------------------------
+GearSwap 0.704 - Changed require() to include() and reworked its back end. Confirmed that register_event() works for users now and are successfully deleted.
+-------------------------------------------------
+GearSwap 0.703 - Added an export command and the appropriate documentation.
+-------------------------------------------------
+GearSwap 0.702 - Made keys to player data tables all case insensitive. Fixed the user windower structure. Set it so it should reset _global.midaction if the spelltarget dies while casting.
+-------------------------------------------------
+GearSwap 0.701 - Made silent interrupts reset _global.midaction. Fixed aftercast triggering on event_action_messages other than your own/your pets.
+-------------------------------------------------
+GearSwap 0.700 - Added preliminary augmented equipment support. Improved register_event. Finished conversion of GearSwap to new API.
+-------------------------------------------------
+GearSwap 0.605 - Fixed pet_mid/aftercast. Added/documented disable/enable. Verified that register_event will work in user documents.
+-------------------------------------------------
+GearSwap 0.604 - Added more handling to deal with monstrosity. Dramatical reduced memory requirements.
+-------------------------------------------------
+GearSwap 0.603 - Added handling to deal with monstrosity. About a week worth of misc. changes that I can't remember.
+-------------------------------------------------
+GearSwap 0.602 - Minor refactoring, moved things around, added a little more protection to windower.send_command()
+-------------------------------------------------
+GearSwap 0.601 - Improved the equip command to handle multiple set layers
+-------------------------------------------------
+GearSwap 0.600 - Added item handling and add/documented require
+-------------------------------------------------
+GearSwap 0.505 - Added and documented set_combine()
+-------------------------------------------------
+GearSwap 0.504 - "Fixed" reraise crash error. Likely indicates an underlying problem in LuaCore that should be addressed
+-------------------------------------------------
+GearSwap 0.503 - Event_status_change() handling changed so people don't swap gear when disengaging from NPCs or crafting.
+-------------------------------------------------
+GearSwap 0.502 - Require unmasked and documented.
+-------------------------------------------------
+GearSwap 0.501 - File_unload user function added.
+-------------------------------------------------
+GearSwap 0.500 - Initial beta release
+-------------------------------------------------