From cce02708758008da1a11a2bf4816d7b3211bb01b Mon Sep 17 00:00:00 2001 From: Xaver-DaRed Date: Wed, 11 Feb 2026 00:54:42 +0100 Subject: [PATCH] Cleanup `ABILITY` listeners --- documentation/AI_Events.txt | 8 +- scripts/globals/pets/avatar.lua | 113 ++++++++--------- scripts/mixins/dynamis_beastmen.lua | 79 ++++++------ scripts/mixins/dynamis_dreamland.lua | 177 +++++++++++++-------------- scripts/mixins/families/chigoe.lua | 17 +-- src/map/ai/states/ability_state.cpp | 2 +- 6 files changed, 190 insertions(+), 206 deletions(-) diff --git a/documentation/AI_Events.txt b/documentation/AI_Events.txt index 8b7e9ee58ca..26ed56d90f9 100644 --- a/documentation/AI_Events.txt +++ b/documentation/AI_Events.txt @@ -43,10 +43,10 @@ ATTACKED - Defender, attacker, action RANGE_START - Entity, action RANGE_STATE_EXIT - Entity, Target, action -ABILITY_START - Entity, Ability -ABILITY_USE - Entity, Target, Ability, action -ABILITY_TAKE - Target, Entity, Ability, action -ABILITY_STATE_EXIT - Entity, Ability +ABILITY_START - userEntity, skillObject +ABILITY_USE - userEntity, targetEntity, skillObject, action +ABILITY_TAKE - userEntity, targetEntity, skillObject, action +ABILITY_STATE_EXIT - userEntity, skillObject WEAPONSKILL_STATE_ENTER - userEntity, skillId WEAPONSKILL_BEFORE_USE - userEntity, skillId diff --git a/scripts/globals/pets/avatar.lua b/scripts/globals/pets/avatar.lua index 222eeb0b6e5..f2981f56f7a 100644 --- a/scripts/globals/pets/avatar.lua +++ b/scripts/globals/pets/avatar.lua @@ -6,24 +6,11 @@ require('scripts/globals/summon') xi = xi or {} xi.pets = xi.pets or {} -xi.pets.avatar = {} - -local buffModeVar = 'AVATAR_BUFF_MODE_OFF' -local lastCastTimeVar = 'AVATAR_LAST_CASTINGTIME' -local lastCastTimeStampVar = 'AVATAR_LAST_CAST_TIMESTAMP' -local playerListenerVar = 'SMN_SPIRIT_CAST_DELAY' -local dummySpell = xi.magic.spell.INDI_REGEN -- used to trigger a "valid" spell in TryCastSpell but not actually cast anything - -local printDebug = function(pet, textToPrint) - -- prints to map server if pet has local var - if pet:getLocalVar('debug') == 1 then - print(textToPrint) - end -end +xi.pets.avatar = xi.pets.avatar or {} -- will determine, in seconds, the spirit's casting cooldown -- Called every tick for player to adjust magic casting delay in real-time -local setMagicCastCooldown = function(pet) +local function setMagicCastCooldown(pet) local castingCooldown = 45 local master = pet:getMaster() @@ -68,14 +55,14 @@ local setMagicCastCooldown = function(pet) end -- "Buff mode" is enabled for first cast after spawn and after casting any enhancing magic - if pet:getLocalVar(buffModeVar) ~= 1 then + if pet:getLocalVar('AVATAR_BUFF_MODE_OFF') ~= 1 then castingCooldown = math.floor(castingCooldown / 2) end -- cast delay is ~1s past the finish of last spell, so we add casting time (or time since spell interrupt) to the castingCooldown -- this is done by simply tracking the elapsed time since action is no longer xi.action.category.MAGIC_CASTING - local lastCastTime = pet:getLocalVar(lastCastTimeVar) - local lastCastTimeStamp = pet:getLocalVar(lastCastTimeStampVar) + local lastCastTime = pet:getLocalVar('AVATAR_LAST_CASTINGTIME') + local lastCastTimeStamp = pet:getLocalVar('AVATAR_LAST_CAST_TIMESTAMP') if lastCastTimeStamp > 0 and GetSystemTime() - lastCastTimeStamp > lastCastTime @@ -87,8 +74,8 @@ local setMagicCastCooldown = function(pet) lastCastTime > 0 and pet:getCurrentAction() ~= xi.action.category.MAGIC_CASTING then - pet:setLocalVar(lastCastTimeStampVar, 0) - pet:setLocalVar(lastCastTimeVar, lastCastTime) + pet:setLocalVar('AVATAR_LAST_CAST_TIMESTAMP', 0) + pet:setLocalVar('AVATAR_LAST_CASTINGTIME', lastCastTime) end pet:setMobMod(xi.mobMod.MAGIC_COOL, lastCastTime + math.max(castingCooldown, 0)) @@ -96,55 +83,57 @@ end xi.pets.avatar.onMobSpawn = function(pet) local master = pet:getMaster() - if - not master or - master:getObjType() ~= xi.objType.PC - then + if not master then return end - -- add listener to player to fine-tune spirit pact cast delays in realtime - if - pet:getPetID() <= xi.petId.DARK_SPIRIT - then - -- stops the pet from immediately casting a spell on spawn and respecting the cooldowns by exiting early if MAGIC_COOL is 1 - pet:setMobMod(xi.mobMod.MAGIC_COOL, 1) - pet:setMod(xi.mod.MPP, 500) - pet:updateHealth() - pet:setMP(pet:getMaxMP()) + if master:getObjType() ~= xi.objType.PC then + return + end - master:addListener('TICK', playerListenerVar, function(playerArg) - local petArg = playerArg:getPet() + if pet:getPetID() > xi.petId.DARK_SPIRIT then + return + end - if petArg and petArg:getMobMod(xi.mobMod.MAGIC_COOL) > 1 then - setMagicCastCooldown(petArg) - end - end) + -- Stop pet from immediately casting a spell on spawn and respecting the cooldowns by exiting early if MAGIC_COOL is 1 + pet:setMobMod(xi.mobMod.MAGIC_COOL, 1) + pet:setMod(xi.mod.MPP, 500) + pet:updateHealth() + pet:setMP(pet:getMaxMP()) - master:addListener('ABILITY_USE', playerListenerVar .. 'ABILITY', function(playerArg, target, ability, action) - local petArg = playerArg:getPet() - local abilityID = ability:getID() + -- Add listener to player to fine-tune spirit pact cast delays in realtime + master:addListener('TICK', 'SMN_SPIRIT_CAST_DELAY', function(masterArg) + local petArg = masterArg:getPet() - if - petArg and - (abilityID == xi.jobAbility.ASSAULT or - abilityID == xi.jobAbility.RETREAT) - then - printDebug(petArg, 'resetting cast cooldown') - -- reset cast cooldown via same method as fresh spawn - petArg:setMobMod(xi.mobMod.MAGIC_COOL, 1) - petArg:setLocalVar(buffModeVar, 1) - end - end) - end + if petArg and petArg:getMobMod(xi.mobMod.MAGIC_COOL) > 1 then + setMagicCastCooldown(petArg) + end + end) + + master:addListener('ABILITY_USE', 'SMN_SPIRIT_CAST_DELAY' .. 'ABILITY', function(masterArg, target, skill, action) + local petArg = masterArg:getPet() + if not petArg then + return + end + + local skillId = skill:getID() + if + skillId == xi.jobAbility.ASSAULT or + skillId == xi.jobAbility.RETREAT + then + -- Reset cast cooldown via same method as fresh spawn + petArg:setMobMod(xi.mobMod.MAGIC_COOL, 1) + petArg:setLocalVar('AVATAR_BUFF_MODE_OFF', 1) + end + end) end xi.pets.avatar.onMobDeath = function(pet) local master = pet:getMaster() if master and master:getObjType() == xi.objType.PC then - master:removeListener(playerListenerVar) - master:removeListener(playerListenerVar .. 'ABILITY') + master:removeListener('SMN_SPIRIT_CAST_DELAY') + master:removeListener('SMN_SPIRIT_CAST_DELAY' .. 'ABILITY') end end @@ -163,18 +152,18 @@ xi.pets.avatar.onMobSpellChoose = function(pet) end -- meta checks for fresh pet, etc - pet:setLocalVar(lastCastTimeVar, 0) - pet:setLocalVar(lastCastTimeStampVar, GetSystemTime()) + pet:setLocalVar('AVATAR_LAST_CASTINGTIME', 0) + pet:setLocalVar('AVATAR_LAST_CAST_TIMESTAMP', GetSystemTime()) -- early exit from casting a spell to prevent immediately casting a spell after being summoned if pet:getMobMod(xi.mobMod.MAGIC_COOL) == 1 then setMagicCastCooldown(pet) - return dummySpell + return xi.magic.spell.INDI_REGEN end -- ensures magic casting delay is no longer halved - pet:setLocalVar(buffModeVar, 1) + pet:setLocalVar('AVATAR_BUFF_MODE_OFF', 1) -- Core functionality to decide which spell to use local spellID, spellTarget = xi.pets.avatar.getSpiritSpell(pet) @@ -184,13 +173,13 @@ xi.pets.avatar.onMobSpellChoose = function(pet) if spell then if spell:getSkillType() == xi.skill.ENHANCING_MAGIC then -- half casting delay - pet:setLocalVar(buffModeVar, 0) + pet:setLocalVar('AVATAR_BUFF_MODE_OFF', 0) end pet:castSpell(spellID, spellTarget or pet) setMagicCastCooldown(pet) - return dummySpell + return xi.magic.spell.INDI_REGEN end return 0 diff --git a/scripts/mixins/dynamis_beastmen.lua b/scripts/mixins/dynamis_beastmen.lua index 476cf2ba48e..5492da86537 100644 --- a/scripts/mixins/dynamis_beastmen.lua +++ b/scripts/mixins/dynamis_beastmen.lua @@ -1,8 +1,9 @@ +----------------------------------- -- Dynamis procs mixin - +----------------------------------- require('scripts/globals/mixins') require('scripts/globals/dynamis') - +----------------------------------- g_mixins = g_mixins or {} g_mixins.dynamis_beastmen = function(dynamisBeastmenMob) @@ -22,7 +23,7 @@ g_mixins.dynamis_beastmen = function(dynamisBeastmenMob) [xi.job.SAM] = 'ws', [xi.job.NIN] = 'ja', [xi.job.DRG] = 'ws', - [xi.job.SMN] = 'ma' + [xi.job.SMN] = 'ma', } local familyCurrency = @@ -47,7 +48,7 @@ g_mixins.dynamis_beastmen = function(dynamisBeastmenMob) dynamisBeastmenMob:addListener('MAGIC_TAKE', 'DYNAMIS_MAGIC_PROC_CHECK', function(target, caster, spell) if procjobs[target:getMainJob()] == 'ma' and - math.random(0, 99) < 8 and + math.random(1, 100) <= 8 and target:getLocalVar('dynamis_proc') == 0 then xi.dynamis.procMonster(target, caster) @@ -64,58 +65,52 @@ g_mixins.dynamis_beastmen = function(dynamisBeastmenMob) end end) - dynamisBeastmenMob:addListener('ABILITY_TAKE', 'DYNAMIS_ABILITY_PROC_CHECK', function(mob, user, ability, action) + dynamisBeastmenMob:addListener('ABILITY_TAKE', 'DYNAMIS_ABILITY_PROC_CHECK', function(user, target, skill, action) if - procjobs[mob:getMainJob()] == 'ja' and + procjobs[target:getMainJob()] == 'ja' and math.random(1, 100) <= 20 and - mob:getLocalVar('dynamis_proc') == 0 + target:getLocalVar('dynamis_proc') == 0 then - xi.dynamis.procMonster(mob, user) + xi.dynamis.procMonster(target, user) end end) dynamisBeastmenMob:addListener('DEATH', 'DYNAMIS_ITEM_DISTRIBUTION', function(mob, killer) - if killer then - local th = thCurrency[math.min(mob:getTHlevel(), 4)] - local family = mob:getFamily() - local currency = familyCurrency[family] - if currency == nil then - currency = 1449 + math.random(0, 2) * 3 - end - - local singleChance = th.single - local hundredChance = th.hundred - if mob:getMainLvl() > 90 then - singleChance = math.floor(singleChance * 1.5) - end + if not killer then + return + end - -- White (special) adds 100% hundred slot - if mob:getLocalVar('dynamis_proc') >= 4 then - killer:addTreasure(currency + 1, mob) - end + local th = thCurrency[math.min(mob:getTHlevel(), 4)] + local currency = familyCurrency[mob:getFamily()] or xi.item.TUKUKU_WHITESHELL + math.random(0, 2) * 3 + local singleChance = mob:getMainLvl() > 90 and math.floor(th.single * 1.5) or th.single + local hundredChance = th.hundred - -- Base hundred slot - if mob:isNM() then - killer:addTreasure(currency + 1, mob, hundredChance) - end + -- White (special) adds 100% hundred slot + if mob:getLocalVar('dynamis_proc') >= 4 then + killer:addTreasure(currency + 1, mob) + end - -- red (high) adds 100% single slot - if mob:getLocalVar('dynamis_proc') >= 3 then - killer:addTreasure(currency, mob) - end + -- Base hundred slot + if mob:isNM() then + killer:addTreasure(currency + 1, mob, hundredChance) + end - -- yellow (medium) adds single slot - if mob:getLocalVar('dynamis_proc') >= 2 then - killer:addTreasure(currency, mob, singleChance) - end + -- red (high) adds 100% single slot + if mob:getLocalVar('dynamis_proc') >= 3 then + killer:addTreasure(currency, mob) + end - -- blue (low) adds single slot - if mob:getLocalVar('dynamis_proc') >= 1 then - killer:addTreasure(currency, mob, singleChance) - end + -- yellow (medium) adds single slot + if mob:getLocalVar('dynamis_proc') >= 2 then + killer:addTreasure(currency, mob, singleChance) + end - killer:addTreasure(currency, mob, singleChance) -- base single slot + -- blue (low) adds single slot + if mob:getLocalVar('dynamis_proc') >= 1 then + killer:addTreasure(currency, mob, singleChance) end + + killer:addTreasure(currency, mob, singleChance) -- base single slot end) end diff --git a/scripts/mixins/dynamis_dreamland.lua b/scripts/mixins/dynamis_dreamland.lua index 9bd47a8a234..93d8224af1c 100644 --- a/scripts/mixins/dynamis_dreamland.lua +++ b/scripts/mixins/dynamis_dreamland.lua @@ -1,39 +1,33 @@ +----------------------------------- -- Dynamis procs mixin --- Customization: - +----------------------------------- require('scripts/globals/mixins') require('scripts/globals/dynamis') - +----------------------------------- g_mixins = g_mixins or {} --- dynamis_currency values: --- t.whiteshell: 1449 --- o. bronzepiece: 1452 --- 1 byne bill: 1455 --- random: 0 - g_mixins.dynamis_dreamland = function(dynamisDreamlandMob) - local proctimes = + local procTimes = { - WS = + weaponskill = { - [1449] = { 0, 8 }, - [1452] = { 16, 24 }, - [1455] = { 8, 16 }, + [xi.item.TUKUKU_WHITESHELL ] = { 0, 8 }, + [xi.item.ORDELLE_BRONZEPIECE] = { 16, 24 }, + [xi.item.ONE_BYNE_BILL ] = { 8, 16 }, }, - Magic = + magic = { - [1449] = { 8, 16 }, - [1452] = { 0, 8 }, - [1455] = { 16, 24 }, + [xi.item.TUKUKU_WHITESHELL ] = { 8, 16 }, + [xi.item.ORDELLE_BRONZEPIECE] = { 0, 8 }, + [xi.item.ONE_BYNE_BILL ] = { 16, 24 }, }, - JA = + jobAbility = { - [1449] = { 16, 24 }, - [1452] = { 8, 16 }, - [1455] = { 0, 8 }, + [xi.item.TUKUKU_WHITESHELL ] = { 16, 24 }, + [xi.item.ORDELLE_BRONZEPIECE] = { 8, 16 }, + [xi.item.ONE_BYNE_BILL ] = { 0, 8 }, }, } @@ -50,103 +44,106 @@ g_mixins.dynamis_dreamland = function(dynamisDreamlandMob) } dynamisDreamlandMob:addListener('MAGIC_TAKE', 'DYNAMIS_MAGIC_PROC_CHECK', function(target, caster, spell) + if math.random(1, 100) > 8 then + return + end + + if target:getLocalVar('dynamis_proc') ~= 0 then + return + end + local currency = target:getLocalVar('dynamis_currency') local vanaHour = VanadielHour() if - math.random(1, 100) <= 8 and - target:getLocalVar('dynamis_proc') == 0 and - ( - currency == 0 or - ( - vanaHour >= proctimes.Magic[currency][1] and - vanaHour < proctimes.Magic[currency][2] - ) - ) + currency == 0 or + (vanaHour >= procTimes.magic[currency][1] and + vanaHour < procTimes.magic[currency][2]) then xi.dynamis.procMonster(target, caster) end end) dynamisDreamlandMob:addListener('WEAPONSKILL_TAKE', 'DYNAMIS_WS_PROC_CHECK', function(user, target, skillId, tp, action) + if math.random(1, 100) > 25 then + return + end + + if target:getLocalVar('dynamis_proc') ~= 0 then + return + end + local currency = target:getLocalVar('dynamis_currency') local vanaHour = VanadielHour() if - math.random(1, 100) <= 25 and - target:getLocalVar('dynamis_proc') == 0 and - ( - currency == 0 or - ( - vanaHour >= proctimes.WS[currency][1] and - vanaHour < proctimes.WS[currency][2] - ) - ) + currency == 0 or + (vanaHour >= procTimes.weaponskill[currency][1] and + vanaHour < procTimes.weaponskill[currency][2]) then xi.dynamis.procMonster(target, user) end end) - dynamisDreamlandMob:addListener('ABILITY_TAKE', 'DYNAMIS_ABILITY_PROC_CHECK', function(target, user, ability, action) + dynamisDreamlandMob:addListener('ABILITY_TAKE', 'DYNAMIS_ABILITY_PROC_CHECK', function(user, target, skill, action) + if math.random(1, 100) > 20 then + return + end + + if target:getLocalVar('dynamis_proc') ~= 0 then + return + end + local currency = target:getLocalVar('dynamis_currency') local vanaHour = VanadielHour() - if - math.random(1, 100) <= 20 and - target:getLocalVar('dynamis_proc') == 0 and - ( - currency == 0 or - ( - vanaHour >= proctimes.JA[currency][1] and - vanaHour < proctimes.JA[currency][2] - ) - ) + currency == 0 or + (vanaHour >= procTimes.jobAbility[currency][1] and + vanaHour < procTimes.jobAbility[currency][2]) then xi.dynamis.procMonster(target, user) end end) dynamisDreamlandMob:addListener('DEATH', 'DYNAMIS_ITEM_DISTRIBUTION', function(mob, killer) - if killer then - local th = thCurrency[math.min(mob:getTHlevel(), 4)] - local currency = mob:getLocalVar('dynamis_currency') - if currency == 0 then - currency = 1449 + math.random(0, 2) * 3 - end - - local singleChance = th.single - local hundredChance = th.hundred - if mob:getMainLvl() > 90 then - singleChance = math.floor(singleChance * 1.5) - end - - -- White (special) adds 100% hundred slot - if mob:getLocalVar('dynamis_proc') >= 4 then - killer:addTreasure(currency + 1, mob) - end - - -- Base hundred slot - if mob:isNM() then - killer:addTreasure(currency + 1, mob, hundredChance) - end - - -- Red (high) adds 100% single slot - if mob:getLocalVar('dynamis_proc') >= 3 then - killer:addTreasure(currency, mob) - end - - -- Yellow (medium) adds single slot - if mob:getLocalVar('dynamis_proc') >= 2 then - killer:addTreasure(currency, mob, singleChance) - end - - -- Blue (low) adds single slot - if mob:getLocalVar('dynamis_proc') >= 1 then - killer:addTreasure(currency, mob, singleChance) - end - - killer:addTreasure(currency, mob, singleChance) -- base single slot + if not killer then + return + end + + local th = thCurrency[math.min(mob:getTHlevel(), 4)] + local singleChance = mob:getMainLvl() > 90 and math.floor(th.single * 1.5) or th.single + local hundredChance = th.hundred + local currency = mob:getLocalVar('dynamis_currency') + if currency == 0 then + currency = xi.item.TUKUKU_WHITESHELL + math.random(0, 2) * 3 + end + + -- White (special) adds 100% hundred slot + if mob:getLocalVar('dynamis_proc') >= 4 then + killer:addTreasure(currency + 1, mob) + end + + -- Base hundred slot + if mob:isNM() then + killer:addTreasure(currency + 1, mob, hundredChance) end + + -- Red (high) adds 100% single slot + if mob:getLocalVar('dynamis_proc') >= 3 then + killer:addTreasure(currency, mob) + end + + -- Yellow (medium) adds single slot + if mob:getLocalVar('dynamis_proc') >= 2 then + killer:addTreasure(currency, mob, singleChance) + end + + -- Blue (low) adds single slot + if mob:getLocalVar('dynamis_proc') >= 1 then + killer:addTreasure(currency, mob, singleChance) + end + + killer:addTreasure(currency, mob, singleChance) -- base single slot end) end diff --git a/scripts/mixins/families/chigoe.lua b/scripts/mixins/families/chigoe.lua index 619472d9609..a2730495208 100644 --- a/scripts/mixins/families/chigoe.lua +++ b/scripts/mixins/families/chigoe.lua @@ -1,7 +1,8 @@ +----------------------------------- -- Chigoe family mixin - +----------------------------------- require('scripts/globals/mixins') - +----------------------------------- g_mixins = g_mixins or {} g_mixins.families = g_mixins.families or {} @@ -47,12 +48,14 @@ g_mixins.families.chigoe = function(chigoeMob) target:setHP(0) end) - chigoeMob:addListener('ABILITY_TAKE', 'CHIGOE_ABILITY_TAKE', function(mob, user, ability) - if jobAbilities[ability:getID()] then - mob:setMobMod(xi.mobMod.EXP_BONUS, -100) - mob:setMobMod(xi.mobMod.NO_DROPS, 1) - mob:setHP(0) + chigoeMob:addListener('ABILITY_TAKE', 'CHIGOE_ABILITY_TAKE', function(user, target, skill, action) + if not jobAbilities[skill:getID()] then + return end + + target:setMobMod(xi.mobMod.EXP_BONUS, -100) + target:setMobMod(xi.mobMod.NO_DROPS, 1) + target:setHP(0) end) end diff --git a/src/map/ai/states/ability_state.cpp b/src/map/ai/states/ability_state.cpp index fb79f825a36..288ee6f31a3 100644 --- a/src/map/ai/states/ability_state.cpp +++ b/src/map/ai/states/ability_state.cpp @@ -211,7 +211,7 @@ bool CAbilityState::Update(timer::time_point tick) } if (auto* target = GetTarget()) { - target->PAI->EventHandler.triggerListener("ABILITY_TAKE", target, m_PEntity, m_PAbility.get(), &action); + target->PAI->EventHandler.triggerListener("ABILITY_TAKE", m_PEntity, target, m_PAbility.get(), &action); } }