662 lines
22 KiB
Lua
662 lines
22 KiB
Lua
-----------------------------------
|
|
-- This global is intended to handle additional effects from item sources of:
|
|
-- melee attacks, ranged attacks, auto-spikes
|
|
-- Notes:
|
|
-- Ranged versions get bonus from int/mnd, melee does NOT.
|
|
-- No matter how much INT you stack that fire sword doesn't hit any harder.
|
|
-- No matter how much MND you stack that holy mace doesn't hit any harder.
|
|
-- But Ice Arrows and Bloody Bolts will gain damage from INT and Holy bolts will gain damage from MND.
|
|
-- Melee weapon proc also do not appear to adjust for level, only resistance.
|
|
-- In testing my fire sword had the same damage ranges no matter my level vs same mob.
|
|
-- Weakness/resistance to element would swing damage range a lot
|
|
-- For status effects is it possible to land on highly resistant mobs because of flooring.
|
|
-- Ranged throwing items have weird cases that don't fully fit in the above,
|
|
-- and a handfull of weapons have seem to scale up the more magic accuracy you have
|
|
-- Yes accuracy, not attack. More research needed. Not adding them till we know how they work.
|
|
-- (And then these comments get cleaned up)
|
|
-----------------------------------
|
|
require('scripts/globals/teleports') -- For warp weapon proc.
|
|
require('scripts/globals/magic') -- For resist functions
|
|
-----------------------------------
|
|
xi = xi or {}
|
|
xi.additionalEffect = xi.additionalEffect or {}
|
|
xi.additionalEffect.procFunctions = xi.additionalEffect.procFunctions or {}
|
|
|
|
xi.additionalEffect.dStatBonus = function(attacker, defender, dStat, damage)
|
|
local statTable =
|
|
{
|
|
-- [attacker stat] = {counter stat, softcap},
|
|
[xi.mod.MND] = { cStat = xi.mod.MND, softcap = 40 },
|
|
[xi.mod.INT] = { cStat = xi.mod.INT, softcap = 20 },
|
|
-- Can use pretty much any modifier and the pairs don't have to math (in case of SE shenanigans..)
|
|
}
|
|
|
|
local tableRow = statTable[dStat]
|
|
local sCap = tableRow.softcap
|
|
local bonus = 0
|
|
|
|
-- Check if this is base stat or other modifier
|
|
if dStat >= xi.mod.STR and dStat <= xi.mod.CHR then
|
|
bonus = attacker:getStat(dStat) - defender:getStat(tableRow.cStat)
|
|
else
|
|
-- See table note above
|
|
bonus = attacker:getMod(dStat) - defender:getMod(tableRow.cStat)
|
|
end
|
|
|
|
if bonus then
|
|
if sCap > 0 and bonus > sCap then
|
|
bonus = bonus + (bonus - sCap) / 2
|
|
end
|
|
|
|
if bonus > 0 then
|
|
damage = damage + bonus
|
|
end
|
|
end
|
|
|
|
return damage
|
|
end
|
|
|
|
xi.additionalEffect.levelCorrectRates = function(dLV, aLV, chance, lvCorrect)
|
|
-- Do not alter 100% proc rates
|
|
if chance < 100 then
|
|
if dLV > aLV then
|
|
chance = utils.clamp(chance - lvCorrect * (dLV - aLV), 1, 99)
|
|
end
|
|
end
|
|
|
|
return chance
|
|
end
|
|
|
|
xi.additionalEffect.statusAttack = function(addStatus, defender)
|
|
local effectList =
|
|
{
|
|
[xi.effect.DEFENSE_DOWN] = { tick = 0, strip = xi.effect.DEFENSE_BOOST },
|
|
[xi.effect.EVASION_DOWN] = { tick = 0, strip = xi.effect.EVASION_BOOST },
|
|
[xi.effect.ATTACK_DOWN] = { tick = 0, strip = xi.effect.ATTACK_BOOST },
|
|
[xi.effect.POISON] = { tick = 3, strip = nil },
|
|
[xi.effect.CHOKE] = { tick = 3, strip = nil },
|
|
}
|
|
|
|
local effect = effectList[addStatus]
|
|
if effect then
|
|
if effect.strip then
|
|
defender:delStatusEffect(effect.strip)
|
|
end
|
|
|
|
return effect.tick
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
-- Todo: swap from using element to damageType enum, so Excalibur etc. can happen.
|
|
xi.additionalEffect.calcDamage = function(attacker, element, defender, damage)
|
|
local params = {}
|
|
|
|
params.bonusmab = 0
|
|
params.includemab = false -- May possibly need to include mab on case by case basis, further tests needed
|
|
damage = addBonusesAbility(attacker, element, defender, damage, params)
|
|
damage = math.floor(damage * applyResistanceAddEffect(attacker, defender, element, 0))
|
|
damage = math.floor(damage * xi.spells.damage.calculateAbsorption(defender, element, true))
|
|
damage = math.floor(damage * xi.spells.damage.calculateNullification(defender, element, true, false))
|
|
-- Todo: make sure day/weather/affinity bonuses tie in right here
|
|
damage = finalMagicNonSpellAdjustments(attacker, defender, element, damage)
|
|
|
|
--[[
|
|
This should rightly be modified by resistance checks, and while those DO they are presently not perfect.
|
|
If you want to force some extra randomness, un-comment the line below to artificially force 20% variance.
|
|
]]
|
|
-- damage = damage * (math.random(90, 110) / 100)
|
|
|
|
return damage
|
|
end
|
|
|
|
xi.additionalEffect.calcPhysDamage = function(attacker, defender, item, params)
|
|
params.isPhysical = params.isPhysical or false
|
|
params.isRanged = params.isRanged or false
|
|
params.isBreath = params.isBreath or false
|
|
params.damageType = params.damageType or xi.damageType.NONE
|
|
params.damage = math.floor(params.damage) or 0
|
|
|
|
if params.damage == 0 then
|
|
return 0
|
|
end
|
|
|
|
-- Check nullification
|
|
if math.random(1, 100) <= defender:getMod(xi.mod.NULL_DAMAGE) then
|
|
return 0
|
|
end
|
|
|
|
if
|
|
params.isPhysical and
|
|
math.random(1, 100) <= defender:getMod(xi.mod.NULL_PHYSICAL_DAMAGE)
|
|
then
|
|
return 0
|
|
end
|
|
|
|
if
|
|
params.isRanged and
|
|
math.random(1, 100) <= defender:getMod(xi.mod.NULL_RANGED_DAMAGE)
|
|
then
|
|
return 0
|
|
end
|
|
|
|
if
|
|
params.isBREATH and
|
|
math.random(1, 100) <= defender:getMod(xi.mod.NULL_BREATH_DAMAGE)
|
|
then
|
|
return 0
|
|
end
|
|
|
|
-- Check absorbs
|
|
-- Absorb: All damage.
|
|
if math.random(1, 100) <= defender:getMod(xi.mod.ABSORB_DMG_CHANCE) then
|
|
return params.damage * -1
|
|
end
|
|
|
|
-- Absorb: ranged or phys
|
|
if
|
|
(params.isPhysical or params.isRanged) and
|
|
math.random(1, 100) <= defender:getMod(xi.mod.PHYS_ABSORB)
|
|
then
|
|
return params.damage * -1
|
|
end
|
|
|
|
params.damage = params.damage * xi.combat.damage.calculateDamageAdjustment(defender, params.isPhysical, false, params.isRanged, params.isBreath)
|
|
|
|
-- multiplicative
|
|
switch (params.damageType) : caseof
|
|
{
|
|
[xi.damageType.PIERCING] = function()
|
|
params.damage = params.damage * (1 + defender:getMod(xi.mod.PIERCE_SDT) / 10000)
|
|
end,
|
|
|
|
[xi.damageType.SLASHING] = function()
|
|
params.damage = params.damage * (1 + defender:getMod(xi.mod.SLASH_SDT) / 10000)
|
|
end,
|
|
|
|
[xi.damageType.BLUNT] = function() -- aka IMPACT
|
|
params.damage = params.damage * (1 + defender:getMod(xi.mod.IMPACT_SDT) / 10000)
|
|
end,
|
|
|
|
[xi.damageType.HAND_TO_HAND] = function()
|
|
params.damage = params.damage * (1 + defender:getMod(xi.mod.HTH_SDT) / 10000)
|
|
end,
|
|
}
|
|
|
|
params.damage = math.floor(params.damage)
|
|
|
|
params.damage = utils.handlePhalanx(defender, params.damage)
|
|
params.damage = utils.handleStoneskin(defender, params.damage)
|
|
|
|
return params.damage
|
|
end
|
|
|
|
xi.additionalEffect.procType =
|
|
{
|
|
-- These are arbitrary, make up new ones as needed.
|
|
DAMAGE = 1,
|
|
DEBUFF = 2,
|
|
HP_HEAL = 3,
|
|
MP_HEAL = 4,
|
|
HP_DRAIN = 5,
|
|
MP_DRAIN = 6,
|
|
TP_DRAIN = 7,
|
|
HPMP_DRAIN = 8,
|
|
HPMPTP_DRAIN = 9,
|
|
DISPEL = 10,
|
|
ABSORB_STATUS = 11,
|
|
SELF_BUFF = 12,
|
|
DEATH = 13,
|
|
NM_SPECIFIC = 14,
|
|
PHYS_DAMAGE = 15,
|
|
}
|
|
|
|
-- TODO: add resistance check for params.element
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.DAMAGE] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
local damage = xi.additionalEffect.calcDamage(attacker, params.element, defender, params.damage)
|
|
msgID = xi.msg.basic.ADD_EFFECT_DMG
|
|
|
|
if damage < 0 then
|
|
msgID = xi.msg.basic.ADD_EFFECT_HEAL
|
|
|
|
damage = damage * -1
|
|
|
|
defender:addHP(damage)
|
|
else
|
|
defender:delHP(damage)
|
|
end
|
|
|
|
msgParam = damage
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.DEBUFF] = function(actor, target, item, params)
|
|
-- Early return: No actor or target.
|
|
if
|
|
not actor or
|
|
not target
|
|
then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Validate parameters.
|
|
local effectId = utils.defaultIfNil(params.addStatus, 0)
|
|
local subEffect = utils.defaultIfNil(params.subEffect, 0)
|
|
local actionElement = params.element > 0 and params.element or xi.data.statusEffect.getAssociatedElement(effectId, xi.element.NONE)
|
|
|
|
-- Early return: No effect to apply.
|
|
if effectId == 0 then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Early return: Target is immune to the effect.
|
|
if xi.data.statusEffect.isTargetImmune(target, effectId, actionElement) then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Early return: Trait nullifies effect.
|
|
if xi.data.statusEffect.isTargetResistant(actor, target, effectId) then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Early return: Incompatible effect in place.
|
|
if xi.data.statusEffect.isEffectNullified(target, effectId, 0) then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Early return: Regular resist rate.
|
|
local resistRate = xi.combat.magicHitRate.calculateResistRate(actor, target, 0, 0, xi.skillRank.A, actionElement, xi.mod.INT, effectId, 0)
|
|
if resistRate < 0.5 then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Apply status effect.
|
|
local power = params.power
|
|
local tick = xi.additionalEffect.statusAttack(effectId, target)
|
|
local duration = math.floor(params.duration * resistRate)
|
|
|
|
target:addStatusEffect(effectId, { power = power, duration = duration, origin = actor, tick = tick })
|
|
|
|
return subEffect, xi.msg.basic.ADD_EFFECT_STATUS_2, effectId
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.HP_HEAL] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
-- Its not a drain and works vs undead. https://www.bg-wiki.com/bg/Dominion_Mace
|
|
local hitPoints = params.damage -- Note: not actually damage, if you wanted damage see HP_DRAIN instead
|
|
-- Unknown what modifies the HP, using power directly for now
|
|
msgID = xi.msg.basic.ADD_EFFECT_HP_HEAL
|
|
attacker:addHP(hitPoints)
|
|
msgParam = hitPoints
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.MP_HEAL] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
local magicPoints = params.damage
|
|
-- Unknown what modifies this, using power directly for now
|
|
msgID = xi.msg.basic.ADD_EFFECT_MP_HEAL
|
|
attacker:addMP(magicPoints)
|
|
msgParam = magicPoints
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
-- TODO: add resistance check for params.element
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.HP_DRAIN] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
-- Hardcoded for now
|
|
params.element = xi.element.DARK
|
|
local damage = xi.additionalEffect.calcDamage(attacker, params.element, defender, params.damage)
|
|
|
|
-- Undead cannot be drained
|
|
if defender:isUndead() then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
if damage > defender:getHP() then
|
|
damage = defender:getHP()
|
|
end
|
|
|
|
msgID = xi.msg.basic.ADD_EFFECT_HP_DRAIN
|
|
msgParam = damage
|
|
attacker:addHP(damage)
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
-- TODO: add resistance check for params.element
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.MP_DRAIN] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
-- Hardcoded for now
|
|
params.element = xi.element.DARK
|
|
local damage = xi.additionalEffect.calcDamage(attacker, params.element, defender, params.damage)
|
|
|
|
-- Undead cannot be drained
|
|
if defender:isUndead() then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
if damage > defender:getMP() then
|
|
damage = defender:getMP()
|
|
end
|
|
|
|
msgID = xi.msg.basic.ADD_EFFECT_MP_DRAIN
|
|
msgParam = damage
|
|
defender:addMP(-damage)
|
|
attacker:addMP(damage)
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
-- TODO: add resistance check for params.element
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.TP_DRAIN] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
-- Hardcoded for now
|
|
params.element = xi.element.DARK
|
|
|
|
-- Undead cannot be drained
|
|
if defender:isUndead() then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
local damage = xi.additionalEffect.calcDamage(attacker, params.element, defender, params.damage)
|
|
|
|
if damage > defender:getTP() then
|
|
damage = defender:getTP()
|
|
end
|
|
|
|
msgID = xi.msg.basic.ADD_EFFECT_TP_DRAIN
|
|
msgParam = damage
|
|
defender:addTP(-damage)
|
|
attacker:addTP(damage)
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
-- TODO: add resistance check for params.element
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.DISPEL] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
local dispel = defender:dispelStatusEffect()
|
|
|
|
if dispel == xi.effect.NONE then
|
|
return 0, 0, 0
|
|
else
|
|
msgID = xi.msg.basic.ADD_EFFECT_DISPEL
|
|
msgParam = dispel
|
|
end
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.ABSORB_STATUS] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
-- Ripping off Aura Steal here
|
|
local resist = applyResistanceAddEffect(attacker, defender, params.element, 0)
|
|
if resist > 0.0625 then
|
|
local stolen = attacker:stealStatusEffect(defender)
|
|
msgID = xi.msg.basic.STEAL_EFFECT
|
|
msgParam = stolen
|
|
end
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.SELF_BUFF] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
if params.addStatus == xi.effect.BLINK then -- BLINK http://www.ffxiah.com/item/18830/gusterion
|
|
-- Does not stack with or replace other shadows
|
|
if
|
|
attacker:hasStatusEffect(xi.effect.BLINK) or
|
|
attacker:hasStatusEffect(xi.effect.COPY_IMAGE)
|
|
then
|
|
return 0, 0, 0
|
|
else
|
|
attacker:addStatusEffect(xi.effect.BLINK, { power = params.power, duration = params.duration, origin = attacker })
|
|
msgID = xi.msg.basic.ADD_EFFECT_SELFBUFF
|
|
msgParam = xi.effect.BLINK
|
|
end
|
|
elseif params.addStatus == xi.effect.HASTE then
|
|
attacker:addStatusEffect(xi.effect.HASTE, { power = params.power, duration = params.duration, origin = attacker })
|
|
-- Todo: verify power/duration/tier/overwrite etc
|
|
msgID = xi.msg.basic.ADD_EFFECT_SELFBUFF
|
|
msgParam = xi.effect.HASTE
|
|
else
|
|
print('scripts/globals/additional_effects.lua : unhandled additional effect selfbuff! Effect ID: ' .. params.addStatus)
|
|
end
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.DEATH] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
if
|
|
defender:isNM() or
|
|
defender:isUndead() or
|
|
-- Todo: DeathRes has no place in the resistance functions so far..
|
|
math.random(1, 100) > defender:getMod(xi.mod.DEATHRES) -- We are checking for a fail, not a success.
|
|
then
|
|
return 0, 0, 0 -- NMs immune or roll failed so return out
|
|
else
|
|
msgID = xi.msg.basic.ADD_EFFECT_STATUS
|
|
msgParam = xi.effect.KO
|
|
defender:setHP(0)
|
|
end
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.HPMP_DRAIN] = function(attacker, defender, item, params)
|
|
local drainRoll = math.random(1, 2) -- This is wrong and needs retail verification. Current theory is that it rolls one after another until one doesn't resist or they all resist.
|
|
|
|
local drainFuncs =
|
|
{
|
|
[1] = xi.additionalEffect.procType.HP_DRAIN,
|
|
[2] = xi.additionalEffect.procType.MP_DRAIN
|
|
}
|
|
|
|
return xi.additionalEffect.procFunctions[drainFuncs[drainRoll]](attacker, defender, item, params)
|
|
end
|
|
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.HPMPTP_DRAIN] = function(attacker, defender, item, params)
|
|
local drainRoll = math.random(1, 3) -- This is wrong and needs retail verification. Current theory is that it rolls one after another until one doesn't resist or they all resist.
|
|
|
|
local drainFuncs =
|
|
{
|
|
[1] = xi.additionalEffect.procType.HP_DRAIN,
|
|
[2] = xi.additionalEffect.procType.MP_DRAIN,
|
|
[3] = xi.additionalEffect.procType.TP_DRAIN
|
|
}
|
|
|
|
return xi.additionalEffect.procFunctions[drainFuncs[drainRoll]](attacker, defender, item, params)
|
|
end
|
|
|
|
-- Script only for now
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.PHYS_DAMAGE] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
|
|
local damage = xi.additionalEffect.calcPhysDamage(attacker, defender, item, params)
|
|
msgID = xi.msg.basic.ADD_EFFECT_DMG
|
|
|
|
if damage < 0 then
|
|
msgID = xi.msg.basic.ADD_EFFECT_HEAL
|
|
|
|
damage = damage * -1
|
|
|
|
defender:addHP(damage)
|
|
else
|
|
defender:delHP(damage)
|
|
end
|
|
|
|
msgParam = damage
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
-- NM-specific additional effects configuration table
|
|
-- Options: requiredItem, specialAction, customSubEffect, customMsgID, customMsgParam
|
|
-- Add new entries here: ['NM_Name'] = { requiredItem = xi.item.ITEM_ID, specialAction = function() }
|
|
xi.additionalEffect.nmSpecificConfigs = {
|
|
['Brigandish_Blade'] = {
|
|
requiredItem = xi.item.BUCCANEERS_KNIFE,
|
|
specialAction = function(defender)
|
|
-- If Brigandish Blade has damage immunity (at 1% HP), remove it
|
|
if defender:getMod(xi.mod.UDMGPHYS) == -10000 then
|
|
-- Remove all damage immunities
|
|
defender:setMod(xi.mod.UDMGPHYS, 0)
|
|
defender:setMod(xi.mod.UDMGRANGE, 0)
|
|
defender:setMod(xi.mod.UDMGMAGIC, 0)
|
|
defender:setMod(xi.mod.UDMGBREATH, 0)
|
|
|
|
defender:setLocalVar('killable', 1)
|
|
defender:setUnkillable(false)
|
|
end
|
|
end,
|
|
},
|
|
['Seiryu'] = {
|
|
requiredItem = xi.item.ZEPHYR,
|
|
specialAction = function(defender)
|
|
defender:setMobMod(xi.mobMod.ADD_EFFECT, 0)
|
|
end,
|
|
},
|
|
['Genbu'] = {
|
|
requiredItem = xi.item.ANTARCTIC_WIND,
|
|
specialAction = function(defender)
|
|
defender:setMobMod(xi.mobMod.ADD_EFFECT, 0)
|
|
end,
|
|
},
|
|
['Suzaku'] = {
|
|
requiredItem = xi.item.ARCTIC_WIND,
|
|
specialAction = function(defender)
|
|
defender:setMobMod(xi.mobMod.ADD_EFFECT, 0)
|
|
end,
|
|
},
|
|
['Byakko'] = {
|
|
requiredItem = xi.item.EAST_WIND,
|
|
specialAction = function(defender)
|
|
defender:setMobMod(xi.mobMod.ADD_EFFECT, 0)
|
|
end,
|
|
},
|
|
}
|
|
|
|
-- NM_SPECIFIC additional effect trigger
|
|
xi.additionalEffect.procFunctions[xi.additionalEffect.procType.NM_SPECIFIC] = function(attacker, defender, item, params)
|
|
local subEffect = params.subEffect
|
|
local msgID = 0
|
|
local msgParam = 0
|
|
local defenderName = defender:getName()
|
|
|
|
local config = xi.additionalEffect.nmSpecificConfigs[defenderName]
|
|
if
|
|
config and
|
|
(config.requiredItem == item:getID() or
|
|
config.requiredItem == xi.item.NONE)
|
|
then
|
|
-- Calculate damage
|
|
local damage = xi.additionalEffect.calcDamage(attacker, params.element, defender, params.damage)
|
|
msgID = xi.msg.basic.ADD_EFFECT_DMG
|
|
msgParam = damage
|
|
|
|
-- Execute special action if configured
|
|
if config.specialAction then
|
|
config.specialAction(defender)
|
|
end
|
|
|
|
subEffect = config.customSubEffect or subEffect
|
|
msgID = config.customMsgID or msgID
|
|
msgParam = config.customMsgParam or msgParam
|
|
else
|
|
if defender and item then
|
|
defender:setLocalVar('aeFromItemId', item:getID())
|
|
end
|
|
|
|
return 0, 0, 0
|
|
end
|
|
|
|
return subEffect, msgID, msgParam
|
|
end
|
|
|
|
-- paralyze on hit, fire damage on hit, etc.
|
|
xi.additionalEffect.attack = function(attacker, defender, baseAttackDamage, item)
|
|
local params = {}
|
|
params.lvCorrect = item:getMod(xi.mod.ITEM_ADDEFFECT_LVADJUST)
|
|
params.dStat = item:getMod(xi.mod.ITEM_ADDEFFECT_DSTAT)
|
|
params.addType = item:getMod(xi.mod.ITEM_ADDEFFECT_TYPE)
|
|
params.subEffect = item:getMod(xi.mod.ITEM_SUBEFFECT)
|
|
params.damage = item:getMod(xi.mod.ITEM_ADDEFFECT_DMG)
|
|
params.chance = item:getMod(xi.mod.ITEM_ADDEFFECT_CHANCE)
|
|
params.element = item:getMod(xi.mod.ITEM_ADDEFFECT_ELEMENT)
|
|
params.addStatus = item:getMod(xi.mod.ITEM_ADDEFFECT_STATUS)
|
|
params.power = item:getMod(xi.mod.ITEM_ADDEFFECT_POWER)
|
|
params.duration = item:getMod(xi.mod.ITEM_ADDEFFECT_DURATION)
|
|
|
|
-- This is the damage of the auto attack that generated this function call (swing did 5 damage, base attack damage is 5)
|
|
params.baseAttackDamage = baseAttackDamage
|
|
|
|
-- If player is level synced below the level of the item, do no proc
|
|
if item:getReqLvl() > attacker:getMainLvl() then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- If we're not going to proc, lets not execute all those checks!
|
|
if math.random(1, 100) > params.chance then
|
|
return 0, 0, 0
|
|
end
|
|
|
|
-- Archery/marksmanship use this, most other items -usually- do not (See notes at top of script).
|
|
if params.dStat > 0 then
|
|
params.damage = xi.additionalEffect.dStatBonus(attacker, defender, params.dStat, params.damage)
|
|
end
|
|
|
|
if xi.additionalEffect.procFunctions[params.addType] then
|
|
return xi.additionalEffect.procFunctions[params.addType](attacker, defender, item, params)
|
|
else
|
|
print('ERR: xi.additionalEffect.attack passed invalid/unimplemented addType of ' .. tostring(params.addType))
|
|
end
|
|
|
|
return 0, 0, 0
|
|
end
|
|
|
|
xi.additionalEffect.spikes = function(attacker, defender, damage, spikeEffect, power, chance)
|
|
--[[ Todo..
|
|
local procType =
|
|
{
|
|
-- These are arbitrary, make up new ones as needed.
|
|
}
|
|
]]
|
|
end
|