309 lines
19 KiB
Lua
309 lines
19 KiB
Lua
-----------------------------------
|
|
-- Table defining diferent status effect properties.
|
|
-----------------------------------
|
|
-- Info
|
|
-- Resistance: https://wiki-ffo-jp.translate.goog/html/1801.html?_x_tr_sl=ja&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=sc
|
|
-- Effects: https://wiki-ffo-jp.translate.goog/html/1720.html?_x_tr_sl=ja&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=sc
|
|
-- Resist: https://wiki-ffo-jp.translate.goog/html/795.html?_x_tr_sl=ja&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=sc
|
|
-- Immunobreak: https://wiki-ffo-jp.translate.goog/html/27204.html?_x_tr_sl=ja&_x_tr_tl=en&_x_tr_hl=en&_x_tr_pto=sc
|
|
|
|
-- NOTE: I have yet to find a case where the effect "associated element" isnt determined by the element of the action that causes it.
|
|
-- Example: Siren's elegy is wind while spell elegies are earth. Same effect, diferent elements.
|
|
-- Unlike sleep, elegy effect doesnt have an associated "effect resistance rank" and uses "element resistance rank" instead.
|
|
-----------------------------------
|
|
xi = xi or {}
|
|
xi.data = xi.data or {}
|
|
xi.data.statusEffect = xi.data.statusEffect or {}
|
|
-----------------------------------
|
|
|
|
-- Table column names.
|
|
local column =
|
|
{
|
|
ALLOWED_RESIST_STATE = 1, -- The amount of times an status effect can trigger a resist before it will fail. A value higher than 2 (x >= 3) implies it will NEVER fully resist.
|
|
EFFECT_NULLIFIED_ALWAYS = 2, -- [effect] is nullified by { effect }. In other words, [effect] cant be applied because { effect } is active.
|
|
EFFECT_NULLIFIED_BY_TIER = 3,
|
|
EFFECT_NULLIFIES = 4, -- TODO: IMPLEMENT. [effect] nullifies { effect }.
|
|
EFFECT_ELEMENT = 5, -- Players and most effects dont have "effect resistance ranks", so they always use the effect "associated element" "resistance rank".
|
|
EFFECT_IMMUNITY = 6, -- Detected by "Completely resists" message. Cant immunobreak/resistance-hack it.
|
|
MOD_RESIST_TRAIT = 7, -- Detected by "Resist!" message. Cant immunobreak/resistance-hack it if triggered.
|
|
MOD_RESIST_RANK = 8, -- TODO: IMPLEMENT. For mobs, status effects can either: Use an specific status effect ressistance rank OR use their associated element resistance rank.
|
|
MOD_MAGIC_EVASION = 9,
|
|
MOD_IMMUNOBREAK = 10,
|
|
}
|
|
|
|
-- Table associating an status effect with their corresponding immunobreak, MEVA and resistance modifiers and immunities.
|
|
xi.data.statusEffect.dataTable =
|
|
{
|
|
[xi.effect.ADDLE ] = { 2, 0, 0, xi.effect.NOCTURNE, xi.element.FIRE, xi.immunity.ADDLE, xi.mod.SLOWRES, 0, 0, xi.mod.ADDLE_IMMUNOBREAK }, -- Addle cant be immunobroken?
|
|
[xi.effect.AMNESIA ] = { 2, 0, 0, 0, xi.element.FIRE, 0, xi.mod.AMNESIARES, 0, xi.mod.AMNESIA_MEVA, 0 },
|
|
[xi.effect.ATTACK_DOWN ] = { 3, 0, 0, 0, xi.element.WATER, 0, 0, 0, 0, 0 },
|
|
[xi.effect.BIND ] = { 2, 0, 0, 0, xi.element.ICE, xi.immunity.BIND, xi.mod.BINDRES, xi.mod.BIND_RES_RANK, xi.mod.BIND_MEVA, xi.mod.BIND_IMMUNOBREAK },
|
|
[xi.effect.BIO ] = { 4, 0, xi.effect.DIA, 0, xi.element.DARK, 0, 0, 0, 0, 0 },
|
|
[xi.effect.BLINDNESS ] = { 1, 0, 0, 0, xi.element.DARK, xi.immunity.BLIND, xi.mod.BLINDRES, xi.mod.BLIND_RES_RANK, xi.mod.BLIND_MEVA, xi.mod.BLIND_IMMUNOBREAK },
|
|
[xi.effect.BURN ] = { 2, xi.effect.DROWN, 0, xi.effect.FROST, xi.element.FIRE, 0, 0, 0, 0, 0 },
|
|
[xi.effect.CHARM_I ] = { 1, 0, 0, 0, xi.element.LIGHT, 0, xi.mod.CHARMRES, 0, xi.mod.CHARM_MEVA, 0 }, -- TODO: charm should be moved from a mob property to a regular immunity
|
|
[xi.effect.CHOKE ] = { 2, xi.effect.FROST, 0, xi.effect.RASP, xi.element.WIND, 0, 0, 0, 0, 0 },
|
|
[xi.effect.CURSE_I ] = { 2, 0, 0, 0, xi.element.DARK, 0, xi.mod.CURSERES, 0, xi.mod.CURSE_MEVA, 0 },
|
|
[xi.effect.DEFENSE_DOWN ] = { 3, 0, 0, 0, xi.element.WIND, 0, 0, 0, 0, 0 },
|
|
[xi.effect.DIA ] = { 4, 0, xi.effect.BIO, 0, xi.element.LIGHT, 0, 0, 0, 0, 0 },
|
|
[xi.effect.DROWN ] = { 2, xi.effect.SHOCK, 0, xi.effect.BURN, xi.element.WATER, 0, 0, 0, 0, 0 },
|
|
[xi.effect.ELEGY ] = { 1, 0, 0, 0, xi.element.EARTH, xi.immunity.ELEGY, xi.mod.SLOWRES, 0, 0, 0 },
|
|
[xi.effect.EVASION_DOWN ] = { 3, 0, 0, 0, xi.element.ICE, 0, 0, 0, 0, 0 },
|
|
[xi.effect.FLASH ] = { 1, 0, 0, 0, xi.element.LIGHT, xi.immunity.BLIND, xi.mod.BLINDRES, xi.mod.BLIND_RES_RANK, xi.mod.BLIND_MEVA, xi.mod.BLIND_IMMUNOBREAK },
|
|
[xi.effect.FROST ] = { 2, xi.effect.BURN, 0, xi.effect.CHOKE, xi.element.ICE, 0, 0, 0, 0, 0 },
|
|
[xi.effect.HASTE ] = { 4, 0, xi.effect.SLOW, 0, xi.element.NONE, 0, 0, 0, 0, 0 },
|
|
[xi.effect.HELIX ] = { 3, 0, 0, 0, xi.element.NONE, 0, 0, 0, 0, 0 },
|
|
[xi.effect.INHIBIT_TP ] = { 1, 0, 0, 0, xi.element.DARK, 0, 0, 0, 0, 0 },
|
|
[xi.effect.INUNDATION ] = { 2, 0, 0, 0, xi.element.LIGHT, 0, 0, 0, 0, 0 },
|
|
[xi.effect.MAGIC_EVASION_DOWN ] = { 3, 0, 0, 0, xi.element.DARK, 0, 0, 0, 0, 0 },
|
|
[xi.effect.NOCTURNE ] = { 1, xi.effect.ADDLE, 0, 0, xi.element.FIRE, xi.immunity.ADDLE, xi.mod.SLOWRES, 0, 0, 0 },
|
|
[xi.effect.NONE ] = { 1, 0, 0, 0, xi.element.DARK, xi.immunity.DISPEL, 0, 0, 0, 0 },
|
|
[xi.effect.PARALYSIS ] = { 1, 0, 0, 0, xi.element.ICE, xi.immunity.PARALYZE, xi.mod.PARALYZERES, xi.mod.PARALYZE_RES_RANK, xi.mod.PARALYZE_MEVA, xi.mod.PARALYZE_IMMUNOBREAK },
|
|
[xi.effect.PETRIFICATION ] = { 2, 0, 0, 0, xi.element.EARTH, xi.immunity.PETRIFY, xi.mod.PETRIFYRES, 0, xi.mod.PETRIFY_MEVA, xi.mod.PETRIFY_IMMUNOBREAK },
|
|
[xi.effect.PLAGUE ] = { 2, 0, 0, 0, xi.element.FIRE, xi.immunity.PLAGUE, xi.mod.VIRUSRES, 0, xi.mod.VIRUS_MEVA, 0 },
|
|
[xi.effect.POISON ] = { 1, 0, 0, 0, xi.element.WATER, xi.immunity.POISON, xi.mod.POISONRES, xi.mod.POISON_RES_RANK, xi.mod.POISON_MEVA, xi.mod.POISON_IMMUNOBREAK },
|
|
[xi.effect.RASP ] = { 2, xi.effect.CHOKE, 0, xi.effect.SHOCK, xi.element.EARTH, 0, 0, 0, 0, 0 },
|
|
[xi.effect.REQUIEM ] = { 1, 0, 0, 0, xi.element.LIGHT, 0, 0, 0, 0, 0 },
|
|
[xi.effect.SHOCK ] = { 2, xi.effect.RASP, 0, xi.effect.DROWN, xi.element.THUNDER, 0, 0, 0, 0, 0 },
|
|
[xi.effect.SILENCE ] = { 1, 0, 0, 0, xi.element.WIND, xi.immunity.SILENCE, xi.mod.SILENCERES, xi.mod.SILENCE_RES_RANK, xi.mod.SILENCE_MEVA, xi.mod.SILENCE_IMMUNOBREAK },
|
|
[xi.effect.SLEEP_I ] = { 1, 0, 0, 0, xi.element.DARK, xi.immunity.DARK_SLEEP, xi.mod.SLEEPRES, xi.mod.DARK_SLEEP_RES_RANK, xi.mod.SLEEP_MEVA, xi.mod.SLEEP_IMMUNOBREAK },
|
|
[xi.effect.SLOW ] = { 1, 0, xi.effect.HASTE, 0, xi.element.EARTH, xi.immunity.SLOW, xi.mod.SLOWRES, xi.mod.SLOW_RES_RANK, xi.mod.SLOW_MEVA, xi.mod.SLOW_IMMUNOBREAK },
|
|
[xi.effect.STUN ] = { 2, xi.effect.PETRIFICATION, 0, 0, xi.element.THUNDER, xi.immunity.STUN, xi.mod.STUNRES, xi.mod.STUN_RES_RANK, xi.mod.STUN_MEVA, 0 },
|
|
[xi.effect.TERROR ] = { 2, 0, 0, 0, 0, xi.immunity.TERROR, 0, 0, 0, 0 }, -- TODO: implement Resist Terror https://www.bg-wiki.com/ffxi/Resist_Terror, TODO: sometimes Terror has an element, but sometimes it does not. It seems it may only be player based effects?
|
|
[xi.effect.THRENODY ] = { 1, 0, 0, 0, xi.element.NONE, 0, 0, 0, 0, 0 },
|
|
[xi.effect.WEIGHT ] = { 1, 0, 0, 0, xi.element.WIND, xi.immunity.GRAVITY, xi.mod.GRAVITYRES, xi.mod.GRAVITY_RES_RANK, xi.mod.GRAVITY_MEVA, xi.mod.GRAVITY_IMMUNOBREAK },
|
|
}
|
|
|
|
-----------------------------------
|
|
-- Helper functions to easily fetch table data.
|
|
-----------------------------------
|
|
xi.data.statusEffect.getLastAllowedResistanceState = function(effectId)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
|
|
-- Fetch effect ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.ALLOWED_RESIST_STATE]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getNullificatingEffect = function(effectId)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
|
|
-- Fetch effect ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.EFFECT_NULLIFIED_ALWAYS]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getNullificatingEffectByTier = function(effectId)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
|
|
-- Fetch effect ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.EFFECT_NULLIFIED_BY_TIER]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getEffectToRemove = function(effectId)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
|
|
-- Fetch effect ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.EFFECT_NULLIFIES]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getAssociatedElement = function(effectId, actionElement)
|
|
-- Sanitize fed values
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
local elementToCheck = utils.defaultIfNil(actionElement, 0)
|
|
|
|
-- Sleep exception.
|
|
if effectToCheck == xi.effect.SLEEP_I then
|
|
return elementToCheck
|
|
end
|
|
|
|
-- Fetch element from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.EFFECT_ELEMENT]
|
|
end
|
|
|
|
-- Assume the effect "element" is the same as the action element.
|
|
return elementToCheck
|
|
end
|
|
|
|
xi.data.statusEffect.getAssociatedImmunity = function(effectId, actionElement)
|
|
-- Sanitize fed values
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
local elementToCheck = utils.defaultIfNil(actionElement, 0)
|
|
|
|
-- Sleep exception.
|
|
if
|
|
effectToCheck == xi.effect.SLEEP_I and
|
|
elementToCheck == xi.element.LIGHT
|
|
then
|
|
return xi.immunity.LIGHT_SLEEP
|
|
end
|
|
|
|
-- Fetch immunity from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.EFFECT_IMMUNITY]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getAssociatedResistTraitModifier = function(effectId)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
|
|
-- Fetch modifier ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.MOD_RESIST_TRAIT]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getAssociatedResistanceRankModifier = function(effectId, actionElement)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
local elementToCheck = utils.defaultIfNil(actionElement, 0)
|
|
|
|
-- Sleep exception.
|
|
if
|
|
effectToCheck == xi.effect.SLEEP_I and
|
|
elementToCheck == xi.element.LIGHT
|
|
then
|
|
return xi.mod.LIGHT_SLEEP_RES_RANK
|
|
end
|
|
|
|
-- Fetch modifier ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.MOD_RESIST_RANK]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getAssociatedMagicEvasionModifier = function(effectId)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
|
|
-- Fetch modifier ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.MOD_MAGIC_EVASION]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
xi.data.statusEffect.getAssociatedImmunobreakModifier = function(effectId)
|
|
-- Sanitize fed value
|
|
local effectToCheck = utils.defaultIfNil(effectId, 0)
|
|
|
|
-- Fetch modifier ID from table if entry exists.
|
|
if xi.data.statusEffect.dataTable[effectToCheck] then
|
|
return xi.data.statusEffect.dataTable[effectToCheck][column.MOD_IMMUNOBREAK]
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
-----------------------------------
|
|
-- Helper functions to check target effect nullification.
|
|
-----------------------------------
|
|
xi.data.statusEffect.isResistRateSuccessfull = function(effectId, resistRate, resistRateBypass)
|
|
local allowedResistState = resistRateBypass > 0 and resistRateBypass or xi.data.statusEffect.getLastAllowedResistanceState(effectId)
|
|
if resistRate >= 1 / 2 ^ allowedResistState then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
xi.data.statusEffect.isTargetImmune = function(target, effectId, actionElement)
|
|
if not target:isMob() then
|
|
return false
|
|
end
|
|
|
|
local immunityId = xi.data.statusEffect.getAssociatedImmunity(effectId, actionElement)
|
|
if
|
|
immunityId > 0 and
|
|
target:hasImmunity(immunityId)
|
|
then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
xi.data.statusEffect.isTargetResistant = function(actor, target, effectId)
|
|
local modifierId = xi.data.statusEffect.getAssociatedResistTraitModifier(effectId)
|
|
if modifierId == 0 then
|
|
return false
|
|
end
|
|
|
|
local resistancePower = target:getMod(modifierId) + target:getMod(xi.mod.STATUSRES) + 5
|
|
if resistancePower <= 5 then
|
|
return false
|
|
end
|
|
|
|
-- TODO: Investigate if this happens always or not. Ex: Terror.
|
|
if actor:isNM() then
|
|
resistancePower = math.floor(resistancePower / 2)
|
|
end
|
|
|
|
if math.randomInt(1, 100) <= resistancePower then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
xi.data.statusEffect.isEffectNullified = function(target, effectId, effectTier)
|
|
-- Check effects that always block current effect from being applied.
|
|
local nullificatingEffect = xi.data.statusEffect.getNullificatingEffect(effectId)
|
|
if
|
|
nullificatingEffect > 0 and
|
|
target:hasStatusEffect(nullificatingEffect)
|
|
then
|
|
return true
|
|
end
|
|
|
|
if effectTier == 0 then
|
|
return false
|
|
end
|
|
|
|
-- Check if current effect is in place with a higher tier.
|
|
if target:hasStatusEffect(effectId) then
|
|
if target:getStatusEffect(effectId):getTier() >= effectTier then
|
|
return true
|
|
end
|
|
end
|
|
|
|
-- Check effects that can block current effect from being applied, based on tier.
|
|
local tierNullificatingEffect = xi.data.statusEffect.getNullificatingEffectByTier(effectId)
|
|
if
|
|
tierNullificatingEffect > 0 and
|
|
target:hasStatusEffect(tierNullificatingEffect)
|
|
then
|
|
if target:getStatusEffect(tierNullificatingEffect):getTier() >= effectTier then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|