----------------------------------- -- Global file for additional effects (Status Effects) ----------------------------------- xi = xi or {} xi.combat = xi.combat or {} xi.combat.action = xi.combat.action or {} ----------------------------------- local defaultsTable = { [xi.effect.AMNESIA ] = { xi.subEffect.AMNESIA, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.ATTACK_DOWN ] = { xi.subEffect.ATTACK_DOWN, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.BIND ] = { xi.subEffect.DARKNESS_DAMAGE, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.BLINDNESS ] = { xi.subEffect.BLIND, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.CURSE_I ] = { xi.subEffect.CURSE, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.DEFENSE_DOWN ] = { xi.subEffect.DEFENSE_DOWN, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.EVASION_DOWN ] = { xi.subEffect.EVASION_DOWN, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.KO ] = { xi.subEffect.DEATH, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.MUTE ] = { xi.subEffect.SILENCE, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.NONE ] = { xi.subEffect.DARKNESS_DAMAGE, xi.msg.basic.ADD_EFFECT_DISPEL }, [xi.effect.PARALYSIS ] = { xi.subEffect.PARALYSIS, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.PETRIFICATION] = { xi.subEffect.PETRIFY, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.PLAGUE ] = { xi.subEffect.PLAGUE, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.POISON ] = { xi.subEffect.POISON, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.SILENCE ] = { xi.subEffect.SILENCE, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.SLEEP_I ] = { xi.subEffect.SLEEP, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.SLOW ] = { xi.subEffect.SLOW, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.STUN ] = { xi.subEffect.STUN, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.TERROR ] = { xi.subEffect.PARALYSIS, xi.msg.basic.ADD_EFFECT_STATUS }, [xi.effect.WEIGHT ] = { xi.subEffect.ATTACK_DOWN, xi.msg.basic.ADD_EFFECT_STATUS }, } ----------------------------------- -- Local functions to ensure defaults are set. ----------------------------------- local function validateParameters(actor, target, fedData) local params = {} -- Additional effect target. params.aeTarget = fedData.aeTarget or target -- Default to the current attack target. -- Chance. params.chance = fedData.chance or 100 -- Default: Always proc. -- Source is ranged attack or melee? Skip en-spell check if so. params.isRanged = fedData.isRanged or false -- Assume melee. -- Status effect application parameters. params.effectId = fedData.effectId or xi.effect.NONE params.power = fedData.power or 0 params.tick = fedData.tick or 0 params.duration = fedData.duration or 120 params.subType = fedData.subType or 0 params.subPower = fedData.subPower or 0 params.tier = fedData.tier or 0 params.resistRate = fedData.resistRate or 0 -- Action properties. params.magicalElement = fedData.magicalElement or xi.data.statusEffect.getAssociatedElement(params.effectId, xi.element.NONE) params.actorStat = fedData.actorStat or 0 params.targetStat = fedData.targetStat or params.actorStat params.skillRank = fedData.skillRank or xi.skillRank.A_PLUS params.bonusMacc = fedData.bonusMacc or 0 -- Optional behavior. params.resetEmnity = fedData.resetEmnity or false -- Currently unused. For future use. params.absorbEffect = fedData.absorbEffect or false -- Animations and messaging. params.animation = fedData.animation or (defaultsTable[params.effectId][1] or 0) params.message = fedData.message or (defaultsTable[params.effectId][2] or 0) return params end local function hasEnspell(actor) local enspellTable = { [ 1] = xi.effect.ENFIRE, [ 2] = xi.effect.ENFIRE_II, [ 3] = xi.effect.ENBLIZZARD, [ 4] = xi.effect.ENBLIZZARD_II, [ 5] = xi.effect.ENAERO, [ 6] = xi.effect.ENAERO_II, [ 7] = xi.effect.ENSTONE, [ 8] = xi.effect.ENSTONE_II, [ 9] = xi.effect.ENTHUNDER, [10] = xi.effect.ENTHUNDER_II, [11] = xi.effect.ENWATER, [12] = xi.effect.ENWATER_II, [13] = xi.effect.ENLIGHT, [14] = xi.effect.ENDARK, } for i = 1, #enspellTable do if actor:hasStatusEffect(enspellTable[i]) then return true end end return false end ----------------------------------- -- Global functions called from "emtity.onAdditionalEffect()" ----------------------------------- xi.combat.action.executeAddEffectEnhancement = function(actor, target, fedData) local params = validateParameters(actor, target, fedData) -- Early return: En-spell overrides innate/weapon additional effects, except ranged attacks. if not params.isRanged and hasEnspell(actor) then return 0, 0, 0 end -- Early return: Incorrect effect ID. if params.effectId == xi.effect.NONE then return 0, 0, 0 end -- Early return: No proc. if math.randomInt(1, 100) > params.chance then return 0, 0, 0 end -- Early return: Target has an status effect that invalidates current (Outright incompatible or higher tier). if xi.data.statusEffect.isEffectNullified(params.aeTarget, params.effectId, params.tier) then return 0, 0, 0 end -- Apply effect. if params.aeTarget:addStatusEffect(params.effectId, { power = params.power, duration = params.duration, origin = actor, tick = params.tick, subType = params.subType, subPower = params.subPower, tier = params.tier }) then return params.animation, params.message, params.effectId end return 0, 0, 0 end xi.combat.action.executeAddEffectEnfeeblement = function(actor, target, fedData) local params = validateParameters(actor, target, fedData) -- Early return: En-spell overrides innate/weapon additional effects, except ranged attacks. if not params.isRanged and hasEnspell(actor) then return 0, 0, 0 end -- Early return: Incorrect effect ID. if params.effectId == xi.effect.NONE then return 0, 0, 0 end -- Early return: No proc. if math.randomInt(1, 100) > params.chance then return 0, 0, 0 end -- Early return: Target is immune. if xi.data.statusEffect.isTargetImmune(params.aeTarget, params.effectId, params.magicalElement) then return 0, 0, 0 end -- Early return: Target triggers resist trait. if xi.data.statusEffect.isTargetResistant(actor, params.aeTarget, params.effectId) then return 0, 0, 0 end -- Early return: Target has an status effect that invalidates current (Outright incompatible or higher tier). if xi.data.statusEffect.isEffectNullified(params.aeTarget, params.effectId, params.tier) then return 0, 0, 0 end -- Early return: Resist rate too high. local resistanceRate = xi.combat.magicHitRate.calculateResistRate(actor, params.aeTarget, params) if not xi.data.statusEffect.isResistRateSuccessfull(params.effectId, resistanceRate, params.resistRate) then return 0, 0, 0 end -- Calculate duration. local totalDuration = math.floor(params.duration * resistanceRate) -- Apply effect. if params.aeTarget:addStatusEffect(params.effectId, { power = params.power, duration = totalDuration, origin = actor, tick = params.tick, subType = params.subType, subPower = params.subPower, tier = params.tier }) then return params.animation, params.message, params.effectId end return 0, 0, 0 end xi.combat.action.executeAddEffectDispel = function(actor, target, fedData) local params = validateParameters(actor, target, fedData) -- Early return: En-spell overrides innate/weapon additional effects, except ranged attacks. if not params.isRanged and hasEnspell(actor) then return 0, 0, 0 end -- Early return: Incorrect effect ID. if params.effectId ~= xi.effect.NONE then return 0, 0, 0 end -- Early return: No proc. if math.randomInt(1, 100) > params.chance then return 0, 0, 0 end -- Early return: No dispelable effect. if not params.aeTarget:hasStatusEffectByFlag(xi.effectFlag.DISPELABLE) then return 0, 0, 0 end -- Early return: Resist rate too high. local resistanceRate = xi.combat.magicHitRate.calculateResistRate(actor, params.aeTarget, params) if not xi.data.statusEffect.isResistRateSuccessfull(params.effectId, resistanceRate, params.resistRate) then return 0, 0, 0 end -- Attampt to dispel or steal an status effect. local dispelledEffect = 0 if params.absorbEffect then dispelledEffect = actor:stealStatusEffect(params.aeTarget, xi.effectFlag.DISPELABLE, true) else dispelledEffect = params.aeTarget:dispelStatusEffect(xi.effectFlag.DISPELABLE) end if dispelledEffect == 0 then return 0, 0, 0 end return params.animation, params.message, dispelledEffect end