From 36bf6b9feacf6895810cff6b32539496f4a47177 Mon Sep 17 00:00:00 2001 From: Skold <113406182+Skold177@users.noreply.github.com> Date: Sun, 24 May 2026 00:09:34 -0400 Subject: [PATCH] Update Automaton Eraser / Add Era Module + Strobe Cleans up code for Strobe, Eraser & adds an era Eraser module. --- modules/abyssea/lua/puppetmaster/eraser.lua | 123 ++++++++++++++++ .../lua/{ => puppetmaster}/replicator.lua | 0 .../pets/attachments/barrage_turbine.lua | 10 +- .../abilities/pets/attachments/eraser.lua | 134 ++++++++++++------ .../abilities/pets/attachments/strobe.lua | 18 ++- .../abilities/pets/attachments/strobe_ii.lua | 18 ++- .../abilities/pets/automaton/eraser.lua | 105 +++++++++----- .../abilities/pets/automaton/provoke.lua | 6 +- 8 files changed, 322 insertions(+), 92 deletions(-) create mode 100644 modules/abyssea/lua/puppetmaster/eraser.lua rename modules/abyssea/lua/{ => puppetmaster}/replicator.lua (100%) diff --git a/modules/abyssea/lua/puppetmaster/eraser.lua b/modules/abyssea/lua/puppetmaster/eraser.lua new file mode 100644 index 00000000000..456432515be --- /dev/null +++ b/modules/abyssea/lua/puppetmaster/eraser.lua @@ -0,0 +1,123 @@ +----------------------------------- +-- Eraser (Pre-2011) +-- Removes up to 3 status effects from the Automaton or its Master based on the number of Light Maneuvers active. +-- Consumes all maneuvers on use. +-- Eraser cannot remove Venom, Death Sentence, Charm or Gradual Petrification. +-- Prioritizes removing effects from the Automaton over the Master. +-- Updated to consume only Light Maneuvers on December 15th, 2011. +-- Updated to consume no maneuvers on March 11th, 2019. +-- https://wiki.ffo.jp/html/5365.html +----------------------------------- +require('modules/module_utils') +----------------------------------- + +local m = Module:new('era_eraser') + +local maneuvers = +{ + xi.effect.FIRE_MANEUVER, + xi.effect.ICE_MANEUVER, + xi.effect.WIND_MANEUVER, + xi.effect.EARTH_MANEUVER, + xi.effect.THUNDER_MANEUVER, + xi.effect.WATER_MANEUVER, + xi.effect.LIGHT_MANEUVER, + xi.effect.DARK_MANEUVER, +} + +local removables = +{ + -- Songs + xi.effect.ELEGY, + xi.effect.REQUIEM, + xi.effect.THRENODY, + + -- Enfeebling + xi.effect.BLINDNESS, + xi.effect.PARALYSIS, + xi.effect.SILENCE, + xi.effect.POISON, + xi.effect.CURSE_I, + xi.effect.CURSE_II, + xi.effect.DISEASE, + xi.effect.PLAGUE, + xi.effect.WEIGHT, + xi.effect.BIND, + xi.effect.ADDLE, + xi.effect.SLOW, + xi.effect.PETRIFICATION, + + -- DoTs + xi.effect.BIO, + xi.effect.DIA, + xi.effect.BURN, + xi.effect.FROST, + xi.effect.CHOKE, + xi.effect.RASP, + xi.effect.SHOCK, + xi.effect.DROWN, + + -- Main Stat Downs + xi.effect.STR_DOWN, + xi.effect.DEX_DOWN, + xi.effect.VIT_DOWN, + xi.effect.AGI_DOWN, + xi.effect.INT_DOWN, + xi.effect.MND_DOWN, + xi.effect.CHR_DOWN, + + -- Combat Stat Downs + xi.effect.ACCURACY_DOWN, + xi.effect.ATTACK_DOWN, + xi.effect.EVASION_DOWN, + xi.effect.DEFENSE_DOWN, + + -- Magic Stat Downs + xi.effect.MAGIC_ACC_DOWN, + xi.effect.MAGIC_ATK_DOWN, + xi.effect.MAGIC_EVASION_DOWN, + xi.effect.MAGIC_DEF_DOWN, + + -- HP/MP/TP Stat Downs + xi.effect.MAX_TP_DOWN, + xi.effect.MAX_MP_DOWN, + xi.effect.MAX_HP_DOWN, +} + +m:addOverride('xi.actions.abilities.pets.automaton.eraser.onAutomatonAbilityCheck', function(target, automaton, skill) + return 0 +end) + +m:addOverride('xi.actions.abilities.pets.automaton.eraser.onAutomatonAbility', function(target, automaton, skill, master, action) + automaton:addRecast(xi.recast.ABILITY, skill:getID(), 30) + + local lightManeuvers = master:countEffect(xi.effect.LIGHT_MANEUVER) + local effectsRemoved = 0 + + for _, effectId in ipairs(removables) do + if target:hasStatusEffect(effectId) then + target:delStatusEffectSilent(effectId) + effectsRemoved = effectsRemoved + 1 + + if effectsRemoved >= lightManeuvers then + break + end + end + end + + for _, maneuverId in ipairs(maneuvers) do + for _ = 1, master:countEffect(maneuverId) do + master:delStatusEffectSilent(maneuverId) + end + end + + if effectsRemoved > 0 then + skill:setMsg(xi.msg.basic.DISAPPEAR_NUM) + else + skill:setMsg(xi.msg.basic.USES) + end + + return effectsRemoved +end) + +return m diff --git a/modules/abyssea/lua/replicator.lua b/modules/abyssea/lua/puppetmaster/replicator.lua similarity index 100% rename from modules/abyssea/lua/replicator.lua rename to modules/abyssea/lua/puppetmaster/replicator.lua diff --git a/scripts/actions/abilities/pets/attachments/barrage_turbine.lua b/scripts/actions/abilities/pets/attachments/barrage_turbine.lua index 686ac574c65..a0597f93b81 100644 --- a/scripts/actions/abilities/pets/attachments/barrage_turbine.lua +++ b/scripts/actions/abilities/pets/attachments/barrage_turbine.lua @@ -6,6 +6,11 @@ local attachmentObject = {} attachmentObject.onEquip = function(pet) pet:addListener('AUTOMATON_ATTACHMENT_CHECK', 'ATTACHMENT_BARRAGE_TURBINE', function(automaton, target) + -- If Barrage Turbine is still on cooldown, do nothing. + if automaton:hasRecast(xi.recast.ABILITY, xi.automaton.abilities.BARRAGE_TURBINE) then + return + end + local master = automaton:getMaster() if not master then @@ -17,11 +22,6 @@ attachmentObject.onEquip = function(pet) return end - -- If Barrage Turbine is still on cooldown, do nothing. - if automaton:hasRecast(xi.recast.ABILITY, xi.automaton.abilities.BARRAGE_TURBINE) then - return - end - automaton:useMobAbility(xi.automaton.abilities.BARRAGE_TURBINE, target) end) end diff --git a/scripts/actions/abilities/pets/attachments/eraser.lua b/scripts/actions/abilities/pets/attachments/eraser.lua index e12dd26cf69..16edb5a08ad 100644 --- a/scripts/actions/abilities/pets/attachments/eraser.lua +++ b/scripts/actions/abilities/pets/attachments/eraser.lua @@ -1,61 +1,115 @@ ----------------------------------- -- Attachment: Eraser +-- Removes up to 3 status effects from the Automaton or its Master based on the number of Light Maneuvers active. +-- Automaton will prioritize itself over its Master. +-- Cannot remove Venom, Death Sentence, Charm or Gradual Petrification. +-- TODO: Verify all effects that Eraser can remove. Included categories from JP Wiki : https://wiki.ffo.jp/html/5365.html ----------------------------------- ---@type TAttachment local attachmentObject = {} -local removable = +local removables = { - xi.effect.PETRIFICATION, + -- Songs + xi.effect.ELEGY, + xi.effect.REQUIEM, + xi.effect.THRENODY, + + -- Enfeebling + xi.effect.BLINDNESS, + xi.effect.PARALYSIS, xi.effect.SILENCE, - xi.effect.BANE, - xi.effect.CURSE_II, xi.effect.CURSE_I, - xi.effect.PARALYSIS, + xi.effect.CURSE_II, + xi.effect.DISEASE, xi.effect.PLAGUE, + xi.effect.WEIGHT, + xi.effect.BIND, + xi.effect.ADDLE, + xi.effect.SLOW, + xi.effect.PETRIFICATION, + + -- DoTs xi.effect.POISON, - xi.effect.DISEASE, - xi.effect.BLINDNESS + xi.effect.BIO, + xi.effect.DIA, + xi.effect.BURN, + xi.effect.FROST, + xi.effect.CHOKE, + xi.effect.RASP, + xi.effect.SHOCK, + xi.effect.DROWN, + + -- Main Stat Downs + xi.effect.STR_DOWN, + xi.effect.DEX_DOWN, + xi.effect.VIT_DOWN, + xi.effect.AGI_DOWN, + xi.effect.INT_DOWN, + xi.effect.MND_DOWN, + xi.effect.CHR_DOWN, + + -- Combat Stat Downs + xi.effect.ACCURACY_DOWN, + xi.effect.ATTACK_DOWN, + xi.effect.EVASION_DOWN, + xi.effect.DEFENSE_DOWN, + + -- Magic Stat Downs + xi.effect.MAGIC_ACC_DOWN, + xi.effect.MAGIC_ATK_DOWN, + xi.effect.MAGIC_EVASION_DOWN, + xi.effect.MAGIC_DEF_DOWN, + + -- HP/MP/TP Stat Downs + xi.effect.MAX_TP_DOWN, + xi.effect.MAX_MP_DOWN, + xi.effect.MAX_HP_DOWN } +local function checkEffects(entity) + for _, status in pairs(removables) do + if entity:hasStatusEffect(status) then + return true + end + end + + return false +end + attachmentObject.onEquip = function(pet) pet:addListener('AUTOMATON_ATTACHMENT_CHECK', 'ATTACHMENT_ERASER', function(automaton, target) + if automaton:hasRecast(xi.recast.ABILITY, xi.automaton.abilities.ERASER) then + return + end + local master = automaton:getMaster() - if - not automaton:hasRecast(xi.recast.ABILITY, xi.automaton.abilities.ERASER) and - master and - master:countEffect(xi.effect.LIGHT_MANEUVER) > 0 + + if not master then + return + end + + if master:countEffect(xi.effect.LIGHT_MANEUVER) == 0 then + return + end + + local eraserTarget = false + + -- Automaton prioritizes itself over its Master. + if checkEffects(automaton) then + eraserTarget = automaton + elseif + automaton:checkDistance(master) < (7 + master:getHitboxSize() + automaton:getHitboxSize()) and -- needs verification + checkEffects(master) then - local erasetarget = false - - local function checkEffects(entity) - for _, status in pairs(removable) do - if entity:hasStatusEffect(status) then - return true - end - end - - return false - end - - if - automaton:hasStatusEffectByFlag(xi.effectFlag.ERASABLE) or - checkEffects(automaton) - then - erasetarget = automaton - elseif - automaton:checkDistance(target) < (7 + target:getHitboxSize() + automaton:getHitboxSize()) and -- needs verification - (master:hasStatusEffectByFlag(xi.effectFlag.ERASABLE) or checkEffects(master)) - then - erasetarget = master - end - - if not erasetarget then - return - end - - automaton:useMobAbility(xi.automaton.abilities.ERASER, erasetarget) + eraserTarget = master end + + if not eraserTarget then + return + end + + automaton:useMobAbility(xi.automaton.abilities.ERASER, eraserTarget) end) end diff --git a/scripts/actions/abilities/pets/attachments/strobe.lua b/scripts/actions/abilities/pets/attachments/strobe.lua index 2e0793d34b2..b611213bd3b 100644 --- a/scripts/actions/abilities/pets/attachments/strobe.lua +++ b/scripts/actions/abilities/pets/attachments/strobe.lua @@ -8,13 +8,21 @@ local attachmentObject = {} attachmentObject.onEquip = function(pet, attachment) xi.automaton.onAttachmentEquip(pet, attachment) pet:addListener('AUTOMATON_ATTACHMENT_CHECK', 'ATTACHMENT_STROBE', function(automaton, target) + if automaton:hasRecast(xi.recast.ABILITY, xi.automaton.abilities.PROVOKE) then + return + end + local master = automaton:getMaster() - if - master and - master:countEffect(xi.effect.FIRE_MANEUVER) > 0 and - automaton:checkDistance(target) <= (15 + target:getHitboxSize() + automaton:getHitboxSize()) -- needs verification - then + if not master then + return + end + + if master:countEffect(xi.effect.FIRE_MANEUVER) <= 0 then + return + end + + if automaton:checkDistance(target) <= (16 + target:getHitboxSize() + automaton:getHitboxSize()) then -- Needs Verification automaton:useMobAbility(xi.automaton.abilities.PROVOKE) end end) diff --git a/scripts/actions/abilities/pets/attachments/strobe_ii.lua b/scripts/actions/abilities/pets/attachments/strobe_ii.lua index b1ce5cb52eb..aa5dfda76b2 100644 --- a/scripts/actions/abilities/pets/attachments/strobe_ii.lua +++ b/scripts/actions/abilities/pets/attachments/strobe_ii.lua @@ -8,13 +8,21 @@ local attachmentObject = {} attachmentObject.onEquip = function(pet, attachment) xi.automaton.onAttachmentEquip(pet, attachment) pet:addListener('AUTOMATON_ATTACHMENT_CHECK', 'ATTACHMENT_STROBE_II', function(automaton, target) + if automaton:hasRecast(xi.recast.ABILITY, xi.automaton.abilities.PROVOKE) then + return + end + local master = automaton:getMaster() - if - master and - master:countEffect(xi.effect.FIRE_MANEUVER) > 0 and - automaton:checkDistance(target) <= (15 + target:getHitboxSize() + automaton:getHitboxSize()) -- needs verification - then + if not master then + return + end + + if master:countEffect(xi.effect.FIRE_MANEUVER) <= 0 then + return + end + + if automaton:checkDistance(target) <= (16 + target:getHitboxSize() + automaton:getHitboxSize()) then -- Needs Verification automaton:useMobAbility(xi.automaton.abilities.PROVOKE) end end) diff --git a/scripts/actions/abilities/pets/automaton/eraser.lua b/scripts/actions/abilities/pets/automaton/eraser.lua index 9d587183f9e..342e18fff34 100644 --- a/scripts/actions/abilities/pets/automaton/eraser.lua +++ b/scripts/actions/abilities/pets/automaton/eraser.lua @@ -1,5 +1,6 @@ ----------------------------------- -- Eraser +-- Removes up to 3 status effects from the Automaton or its Master based on the number of Light Maneuvers active. ----------------------------------- ---@type TAbilityAutomaton local abilityObject = {} @@ -8,56 +9,94 @@ abilityObject.onAutomatonAbilityCheck = function(target, automaton, skill) return 0 end -local removableStatus = +-- Eraser cannot remove Venom, Death Sentence, Charm or Gradual Petrification. + +local removables = { - xi.effect.PETRIFICATION, - xi.effect.SILENCE, - xi.effect.BANE, - xi.effect.CURSE_II, - xi.effect.CURSE_I, + -- Songs + xi.effect.ELEGY, + xi.effect.REQUIEM, + xi.effect.THRENODY, + + -- Enfeebling + xi.effect.BLINDNESS, xi.effect.PARALYSIS, - xi.effect.PLAGUE, + xi.effect.SILENCE, xi.effect.POISON, + xi.effect.CURSE_I, + xi.effect.CURSE_II, xi.effect.DISEASE, - xi.effect.BLINDNESS, -} + xi.effect.PLAGUE, + xi.effect.WEIGHT, + xi.effect.BIND, + xi.effect.ADDLE, + xi.effect.SLOW, + xi.effect.PETRIFICATION, -local function removeStatus(target) - for _, effectId in ipairs(removableStatus) do - if target:delStatusEffect(effectId) then - return true - end - end + -- DoTs + xi.effect.BIO, + xi.effect.DIA, + xi.effect.BURN, + xi.effect.FROST, + xi.effect.CHOKE, + xi.effect.RASP, + xi.effect.SHOCK, + xi.effect.DROWN, - if target:eraseStatusEffect() ~= xi.effect.NONE then - return true - end + -- Main Stat Downs + xi.effect.STR_DOWN, + xi.effect.DEX_DOWN, + xi.effect.VIT_DOWN, + xi.effect.AGI_DOWN, + xi.effect.INT_DOWN, + xi.effect.MND_DOWN, + xi.effect.CHR_DOWN, - return false -end + -- Combat Stat Downs + xi.effect.ACCURACY_DOWN, + xi.effect.ATTACK_DOWN, + xi.effect.EVASION_DOWN, + xi.effect.DEFENSE_DOWN, + + -- Magic Stat Downs + xi.effect.MAGIC_ACC_DOWN, + xi.effect.MAGIC_ATK_DOWN, + xi.effect.MAGIC_EVASION_DOWN, + xi.effect.MAGIC_DEF_DOWN, + + -- HP/MP/TP Stat Downs + xi.effect.MAX_TP_DOWN, + xi.effect.MAX_MP_DOWN, + xi.effect.MAX_HP_DOWN +} abilityObject.onAutomatonAbility = function(target, automaton, skill, master, action) automaton:addRecast(xi.recast.ABILITY, skill:getID(), 30) + local maneuvers = master:countEffect(xi.effect.LIGHT_MANEUVER) - skill:setMsg(xi.msg.basic.USES) - local toremove = maneuvers - local removed = 0 + local effectsRemoved = 0 - repeat - if not removeStatus(target) then - break - end + -- Eraser removes 1 effect per Light Maneuver, up to a maximum of 3. + for i = 1, #removables do + local effectId = removables[i] + if target:hasStatusEffect(effectId) then + target:delStatusEffectSilent(effectId) + effectsRemoved = effectsRemoved + 1 - toremove = toremove - 1 - removed = removed + 1 - until toremove <= 0 + if effectsRemoved >= maneuvers then + break + end + end + end - for i = 1, maneuvers do - master:delStatusEffectSilent(xi.effect.LIGHT_MANEUVER) + if effectsRemoved > 0 then + skill:setMsg(xi.msg.basic.DISAPPEAR_NUM) + else + skill:setMsg(xi.msg.basic.USES) end - return removed + return effectsRemoved end return abilityObject diff --git a/scripts/actions/abilities/pets/automaton/provoke.lua b/scripts/actions/abilities/pets/automaton/provoke.lua index cbae49f56ea..9963dc72ae9 100644 --- a/scripts/actions/abilities/pets/automaton/provoke.lua +++ b/scripts/actions/abilities/pets/automaton/provoke.lua @@ -1,5 +1,5 @@ ----------------------------------- --- Provoke +-- Provoke - Goads the target into attacking the automaton. ----------------------------------- ---@type TAbilityAutomaton local abilityObject = {} @@ -10,10 +10,8 @@ end abilityObject.onAutomatonAbility = function(target, automaton, skill, master, action) automaton:addRecast(xi.recast.ABILITY, skill:getID(), 30) - target:addEnmity(automaton, 1, 1800) + target:addEnmity(automaton, 1, 1800) -- Confirmed on retail. - -- TODO: This function formerly had target passed as a secondary paramter. Verify - -- if this message is still correct (unused parameter) skill:setMsg(xi.msg.basic.PROVOKE_SWITCH) return 0 end