From da5cd4c3c3e26f1cd707c3cd76d1bb3f8daeac73 Mon Sep 17 00:00:00 2001 From: gol-exe Date: Mon, 13 Apr 2026 21:44:50 -0600 Subject: [PATCH 1/5] reverting to working version, minor code cleanup --- addons/azureSets/azuresets.lua | 310 ++++++++++++++++++++------------- 1 file changed, 192 insertions(+), 118 deletions(-) diff --git a/addons/azureSets/azuresets.lua b/addons/azureSets/azuresets.lua index 41bab2951d..2300fe7160 100644 --- a/addons/azureSets/azuresets.lua +++ b/addons/azureSets/azuresets.lua @@ -14,7 +14,7 @@ documentation and/or other materials provided with the distribution. 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 +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 The Addon's Contributors BE LIABLE FOR ANY @@ -26,21 +26,20 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ]] - _addon.name = 'AzureSets' -_addon.version = '1.23' -_addon.author = 'Nitrous (Shiva)' -_addon.commands = {'aset','azuresets','asets'} +_addon.version = '1.25' +_addon.author = 'Nitrous (Shiva), Gol-exe' +_addon.commands = {'aset', 'azuresets', 'asets'} require('tables') require('strings') require('logger') -config = require('config') -files = require('files') -res = require('resources') -chat = require('chat') +local config = require('config') +local res = require('resources') +local chat = require('chat') +local spells = res.spells:type('BlueMagic') -defaults = {} +local defaults = {} defaults.setmode = 'PreserveTraits' defaults.setspeed = 0.65 defaults.spellsets = {} @@ -57,22 +56,71 @@ slot09='Blank Gaze', slot10='Radiant Breath', slot11='Light of Penance', slot12= slot13='Death Ray', slot14='Eyes On Me', slot15='Sandspray' } -settings = config.load(defaults) +local settings = config.load(defaults) +local BLU_JOB_ID = 16 + +local currentSpellSet +local bluJobLevel +local bluPointsMax +local bluSlots +local get_blu_job_data + +local spellsLookup = {} +for spell in spells:it() do + spellsLookup[spell.english] = spell + spellsLookup[spell.english:lower()] = spell + spellsLookup[spell.japanese] = spell +end function initialize() - spells = res.spells:type('BlueMagic') - get_current_spellset() + local player = windower.ffxi.get_player() + local is_blu = player and (player.main_job_id == BLU_JOB_ID or player.sub_job_id == BLU_JOB_ID) + local game_info = windower.ffxi.get_info() + + if is_blu and game_info then + update_blu_info() + update_current_spellset() + end end -windower.register_event('load', initialize:cond(function() return windower.ffxi.get_info().logged_in end)) +windower.register_event('load', 'login', 'job change', initialize) -windower.register_event('login', initialize) +function update_blu_info(player) + player = player or windower.ffxi.get_player() + if player.main_job_id == BLU_JOB_ID then + bluJobLevel = player.main_job_level -windower.register_event('job change', initialize:cond(function(job) return job == 16 end)) + if bluJobLevel > 70 then + bluSlots = 20 + else + bluSlots = (math.floor((bluJobLevel + 9) / 10) * 2) + 4 + end + + bluPointsMax = (math.floor((bluJobLevel + 9) / 10) * 5) + 5 + if bluJobLevel >= 75 then + bluPointsMax = bluPointsMax + player.merits.assimilation + if bluJobLevel == 99 then + bluPointsMax = bluPointsMax + player.job_points.blu.blue_magic_point_bonus + end + end + + get_blu_job_data = windower.ffxi.get_mjob_data + elseif player.sub_job_id == BLU_JOB_ID then + bluJobLevel = player.sub_job_level + bluSlots = (math.floor((bluJobLevel + 9) / 10) * 2) + 4 + bluPointsMax = (math.floor((bluJobLevel + 9) / 10) * 5) + 5 + get_blu_job_data = windower.ffxi.get_sjob_data + else + bluJobLevel = nil + bluSlots = nil + bluPointsMax = nil + get_blu_job_data = nil + end +end function set_spells(spellset, setmode) - if windower.ffxi.get_player()['main_job_id'] ~= 16 --[[and windower.ffxi.get_player()['sub_job_id'] ~= 16]] then - error('Main job not set to Blue Mage.') + if not bluJobLevel then + error('You are not a Blue Mage.') return end if settings.spellsets[spellset] == nil then @@ -96,22 +144,20 @@ function set_spells(spellset, setmode) end function is_spellset_equipped(spellset) - return S(spellset):map(string.lower) == S(get_current_spellset()) + return S(spellset):map(string.lower) == S(update_current_spellset()) end function set_spells_from_spellset(spellset, setPhase) local setToSet = settings.spellsets[spellset] - local currentSet = get_current_spellset() + update_current_spellset() if setPhase == 'remove' then -- Remove Phase - for k,v in pairs(currentSet) do + for k, v in pairs(currentSpellSet) do if not setToSet:contains(v:lower()) then - setSlot = k local slotToRemove = tonumber(k:sub(5, k:len())) windower.ffxi.remove_blue_magic_spell(slotToRemove) - --log('Removed spell: '..v..' at #'..slotToRemove) set_spells_from_spellset:schedule(settings.setspeed, spellset, 'remove') return end @@ -122,7 +168,7 @@ function set_spells_from_spellset(spellset, setPhase) local slotToSetTo for i = 1, 20 do local slotName = 'slot%02u':format(i) - if currentSet[slotName] == nil then + if currentSpellSet[slotName] == nil then slotToSetTo = i break end @@ -130,16 +176,15 @@ function set_spells_from_spellset(spellset, setPhase) if slotToSetTo ~= nil then -- We found an empty slot. Find a spell to set. - for k,v in pairs(setToSet) do - if not currentSet:contains(v:lower()) then - if v ~= nil then - local spellID = find_spell_id_by_name(v) - if spellID ~= nil then - windower.ffxi.set_blue_magic_spell(spellID, tonumber(slotToSetTo)) - --log('Set spell: '..v..' ('..spellID..') at: '..slotToSetTo) + for k, v in pairs(setToSet) do + if not currentSpellSet:contains(v:lower()) then + local spellID = find_spell_id_by_name(v) + if spellID ~= nil then + local verified = verify_and_set_spell(spellID, tonumber(slotToSetTo)) + if verified then set_spells_from_spellset:schedule(settings.setspeed, spellset, 'add') - return end + return end end end @@ -151,50 +196,42 @@ function set_spells_from_spellset(spellset, setPhase) end function find_spell_id_by_name(spellname) - for spell in spells:it() do - if spell['english']:lower() == spellname:lower() then - return spell['id'] - end + local spell = spellsLookup[spellname:lower()] + if spell and spell.id then + return spell.id end - return nil end -function set_single_spell(setspell,slot) - if windower.ffxi.get_player()['main_job_id'] ~= 16 --[[and windower.ffxi.get_player()['sub_job_id'] ~= 16]] then return nil end - - local tmpTable = T(get_current_spellset()) - for key,val in pairs(tmpTable) do - if tmpTable[key]:lower() == setspell then +function set_single_spell(setspell, slot) + update_current_spellset() + for _, name in pairs(currentSpellSet) do + if name == setspell then error('That spell is already set.') return end end - if tonumber(slot) < 10 then slot = '0'..slot end + slot = '%02u':format(tonumber(slot)) --insert spell add code here - for spell in spells:it() do - if spell['english']:lower() == setspell then - --This is where single spell setting code goes. - --Need to set by spell id rather than name. - windower.ffxi.set_blue_magic_spell(spell['id'], tonumber(slot)) - windower.send_command('@timers c "Blue Magic Cooldown" 60 up') - tmpTable['slot'..slot] = setspell - end + local spellId = find_spell_id_by_name(setspell) + local verified = verify_and_set_spell(spellId, tonumber(slot)) + if verified then + windower.send_command('@timers c "Blue Magic Cooldown" 60 up') + currentSpellSet['slot'..slot] = setspell end - tmpTable = nil end -function get_current_spellset() - if windower.ffxi.get_player().main_job_id ~= 16 then return nil end - return T(windower.ffxi.get_mjob_data().spells) +function update_current_spellset(player) + currentSpellSet = T(get_blu_job_data().spells) -- Returns all values but 512 :filter(function(id) return id ~= 512 end) - -- Transforms them from IDs to lowercase English names - :map(function(id) return spells[id].english:lower() end) + -- Transforms them from IDs to lowercase names + :map(function(id) return spells[id].name:lower() end) -- Transform the keys from numeric x or xx to string 'slot0x' or 'slotxx' :key_map(function(slot) return 'slot%02u':format(slot) end) + return currentSpellSet end -function remove_all_spells(trigger) +function remove_all_spells() windower.ffxi.reset_blue_magic_spells() notice('All spells removed.') end @@ -204,8 +241,8 @@ function save_set(setname) error('Please choose a name other than default.') return end - local curSpells = T(get_current_spellset()) - settings.spellsets[setname] = curSpells + update_current_spellset() + settings.spellsets[setname] = currentSpellSet settings:save('all') notice('Set '..setname..' saved.') end @@ -221,14 +258,10 @@ function delete_set(setname) end function get_spellset_list() - log("Listing sets:") + log('Listing sets:') for key,_ in pairs(settings.spellsets) do if key ~= 'default' then - local it = 0 - for i = 1, #settings.spellsets[key] do - it = it + 1 - end - log("\t"..key..' '..settings.spellsets[key]:length()..' spells.') + log('\t'..key..' '..settings.spellsets[key]:length()..' spells.') end end end @@ -238,61 +271,102 @@ function get_spellset_content(spellset) settings.spellsets[spellset]:print() end +function verify_and_set_spell(id, slot) + local spell = spells[id] + if not spell then + error('Spell named not found') + return false + end + if bluJobLevel and spell.levels and spell.levels[BLU_JOB_ID] and spell.levels[BLU_JOB_ID] > bluJobLevel then + error('Blue Mage Level too low to set spell') + return false + end + if not have_enough_points_to_add_spell(id) then + error('Cannot set spell, ran out of blue magic points') + return false + end + if slot > bluSlots then + error('Slot ' .. tostring(slot) .. ' unavailable') + return false + end + + windower.ffxi.set_blue_magic_spell(id, slot) + return true +end + +function have_enough_points_to_add_spell(spellId) + local spell = spells[spellId] + if not spell or not spell.blu_points then + return nil + end + return spell.blu_points + current_total_points_spent() <= bluPointsMax +end + +function current_total_points_spent() + local total = 0 + for _, spellId in pairs(get_blu_job_data().spells) do + local spell = spells[spellId] + if spell and spell.blu_points then + total = total + spell.blu_points + end + end + return total +end + windower.register_event('addon command', function(...) - if windower.ffxi.get_player()['main_job_id'] ~= 16 --[[and windower.ffxi.get_player()['sub_job_id'] ~= 16]] then - error('You are not on (main) Blue Mage.') + initialize() + if not bluJobLevel then + error('You are not a Blue Mage.') return nil end local args = T{...} - if args ~= nil then - local comm = table.remove(args,1):lower() - if comm == 'removeall' then - remove_all_spells('trigger') - elseif comm == 'add' then - if args[2] ~= nil then - local slot = table.remove(args,1) - local spell = args:sconcat() - set_single_spell(spell:lower(),slot) - end - elseif comm == 'save' then - if args[1] ~= nil then - save_set(args[1]) - end - elseif comm == 'delete' then - if args[1] ~= nil then - delete_set(args[1]) - end - elseif comm == 'spellset' or comm == 'set' then - if args[1] ~= nil then - set_spells(args[1], args[2] or settings.setmode) - end - elseif comm == 'currentlist' then - get_current_spellset():print() - elseif comm == 'setlist' then - get_spellset_list() - elseif comm == 'spelllist' then - if args[1] ~= nil then - get_spellset_content(args[1]) - end - elseif comm == 'help' then - local helptext = [[AzureSets - Command List:') - 1. removeall - Unsets all spells. - 2. spellset [ClearFirst|PreserveTraits] -- Set (setname)'s spells, - optional parameter: ClearFirst or PreserveTraits: overrides - setting to clear spells first or remove individually, - preserving traits where possible. Default: use settings or - preservetraits if settings not configured. - 3. set (clearfirst|preservetraits) -- Same as spellset - 4. add -- Set (spell) to slot (slot (number)). - 5. save -- Saves current spellset as (setname). - 6. delete -- Delete (setname) spellset. - 7. currentlist -- Lists currently set spells. - 8. setlist -- Lists all spellsets. - 9. spelllist -- List spells in (setname) - 10. help --Shows this menu.]] - for _, line in ipairs(helptext:split('\n')) do - windower.add_to_chat(207, line..chat.controls.reset) - end + local comm = table.remove(args, 1):lower() + if comm == 'removeall' then + remove_all_spells() + elseif comm == 'add' then + if args[2] ~= nil then + local slot = table.remove(args, 1) + local spell = args:sconcat() + set_single_spell(spell:lower(), slot) + end + elseif comm == 'save' then + if args[1] ~= nil then + save_set(args[1]) + end + elseif comm == 'delete' then + if args[1] ~= nil then + delete_set(args[1]) + end + elseif comm == 'spellset' or comm == 'set' then + if args[1] ~= nil then + set_spells(args[1], args[2] or settings.setmode) + end + elseif comm == 'currentlist' then + update_current_spellset():print() + elseif comm == 'setlist' then + get_spellset_list() + elseif comm == 'spelllist' then + if args[1] ~= nil then + get_spellset_content(args[1]) + end + elseif comm == 'help' then + local helptext = [[AzureSets - Command List: +1. removeall - Unsets all spells. +2. spellset [ClearFirst|PreserveTraits] -- Set (setname)'s spells, + optional parameter: ClearFirst or PreserveTraits: overrides + setting to clear spells first or remove individually, + preserving traits where possible. Default: use settings or + preservetraits if settings not configured. +3. set (clearfirst|preservetraits) -- Same as spellset +4. add -- Set (spell) to slot (slot (number)). +5. save -- Saves current spellset as (setname). +6. delete -- Delete (setname) spellset. +7. currentlist -- Lists currently set spells. +8. setlist -- Lists all spellsets. +9. spelllist -- List spells in (setname) +10. help --Shows this menu.]] + for _, line in ipairs(helptext:split('\n')) do + windower.add_to_chat(207, line..chat.controls.reset) end end end) From 39296c0c23381556184e6059881d1d0ed49a5941 Mon Sep 17 00:00:00 2001 From: gol-exe Date: Mon, 13 Apr 2026 21:47:04 -0600 Subject: [PATCH 2/5] updating readme --- addons/azureSets/Readme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/azureSets/Readme.md b/addons/azureSets/Readme.md index 9e2d25b8f9..1f81985b21 100644 --- a/addons/azureSets/Readme.md +++ b/addons/azureSets/Readme.md @@ -1,5 +1,5 @@ **Author:** Ricky Gall -**Version:** 1.24 +**Version:** 1.25 **Description:** Addon to make setting blue spells easier. Currently only works as blu main. @@ -21,6 +21,9 @@ Addon to make setting blue spells easier. Currently only works as blu main. 9. //aset help --Shows this menu. **Changes:** +v1.25 +* Added compatibility for settings spells while sub job blu. + v1.23 - v1.24 * Changed default spellset method to preserve traits. * Added setting for setmethod to either PresereTraits or ClearFirst. From 1a5aaf3493a5a5311c4d85ec210f92e2412177b8 Mon Sep 17 00:00:00 2001 From: gol-exe Date: Mon, 13 Apr 2026 22:01:37 -0600 Subject: [PATCH 3/5] weird spacing --- addons/azureSets/azuresets.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/azureSets/azuresets.lua b/addons/azureSets/azuresets.lua index 2300fe7160..a20b68cd13 100644 --- a/addons/azureSets/azuresets.lua +++ b/addons/azureSets/azuresets.lua @@ -14,7 +14,7 @@ documentation and/or other materials provided with the distribution. 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 +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 The Addon's Contributors BE LIABLE FOR ANY @@ -29,7 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. _addon.name = 'AzureSets' _addon.version = '1.25' _addon.author = 'Nitrous (Shiva), Gol-exe' -_addon.commands = {'aset', 'azuresets', 'asets'} +_addon.commands = {'aset','azuresets','asets'} require('tables') require('strings') From 4ecec239eb0384ad7efd63ebbb409910cbb0d90b Mon Sep 17 00:00:00 2001 From: gol-exe Date: Tue, 14 Apr 2026 22:09:12 -0600 Subject: [PATCH 4/5] Case insensitive xml settings --- addons/azureSets/azuresets.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/azureSets/azuresets.lua b/addons/azureSets/azuresets.lua index a20b68cd13..fce6741c5e 100644 --- a/addons/azureSets/azuresets.lua +++ b/addons/azureSets/azuresets.lua @@ -148,7 +148,7 @@ function is_spellset_equipped(spellset) end function set_spells_from_spellset(spellset, setPhase) - local setToSet = settings.spellsets[spellset] + local setToSet = settings.spellsets[spellset]:map(string.lower) update_current_spellset() if setPhase == 'remove' then From 9d0661efb2fafe9824a2152357c762a50626ceda Mon Sep 17 00:00:00 2001 From: gol-exe Date: Wed, 15 Apr 2026 07:49:29 -0600 Subject: [PATCH 5/5] cleaning up initialize --- addons/azureSets/azuresets.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/azureSets/azuresets.lua b/addons/azureSets/azuresets.lua index fce6741c5e..8f97fceb6d 100644 --- a/addons/azureSets/azuresets.lua +++ b/addons/azureSets/azuresets.lua @@ -74,11 +74,11 @@ end function initialize() local player = windower.ffxi.get_player() - local is_blu = player and (player.main_job_id == BLU_JOB_ID or player.sub_job_id == BLU_JOB_ID) - local game_info = windower.ffxi.get_info() + if not player then return end - if is_blu and game_info then - update_blu_info() + update_blu_info(player) + + if bluJobLevel then update_current_spellset() end end