172 lines
5.7 KiB
Lua
172 lines
5.7 KiB
Lua
-----------------------------------
|
|
-- Global file for spells AoE type and radius calculations.
|
|
-----------------------------------
|
|
xi = xi or {}
|
|
xi.combat = xi.combat or {}
|
|
xi.combat.magicAoE = xi.combat.magicAoE or {}
|
|
|
|
---Return total song radius after factoring String equipment bonus
|
|
---@param caster CBaseEntity
|
|
---@param spell CSpell
|
|
---@return integer
|
|
xi.combat.magicAoE.calculateSongRadius = function(caster, spell)
|
|
local baseRadius = spell:getRadius()
|
|
local rangeType = caster:getWeaponSkillType(xi.slot.RANGED)
|
|
local aoeType = spell:isAoE()
|
|
|
|
-- Spell is not AoE or caster is under Pianissimo
|
|
if
|
|
aoeType == xi.magic.aoe.NONE or
|
|
(aoeType == xi.magic.aoe.PIANISSIMO and
|
|
caster:hasStatusEffect(xi.effect.PIANISSIMO))
|
|
then
|
|
return 0
|
|
end
|
|
|
|
-- Caster is not BRD or does not have a String instrument equipped
|
|
if
|
|
caster:getMainJob() ~= xi.job.BRD or
|
|
rangeType ~= xi.skill.STRING_INSTRUMENT or
|
|
spell:getSpellGroup() ~= xi.magic.spellGroup.SONG
|
|
then
|
|
return baseRadius
|
|
end
|
|
|
|
-- Range scales from 1.0x to 2.0x based on string skill vs song level skill cap
|
|
-- Range is always whole, no partial yalm bonus range.
|
|
local songLevel = spell:getLevel(xi.job.BRD)
|
|
local stringSkill = caster:getSkillLevel(xi.skill.STRING_INSTRUMENT)
|
|
local skillCap = xi.data.skillLevel.getSkillCap(songLevel, xi.skillRank.C)
|
|
local multiplier = utils.clamp(stringSkill / skillCap, 1.0, 2.0)
|
|
|
|
return math.floor(baseRadius * multiplier)
|
|
end
|
|
|
|
-- Handle AoE for mobs (non-player, non-trust entities)
|
|
-- Return the appropriate end result based on base type of the spell
|
|
-- TODO: To be removed when spell definitions are reworked
|
|
local calculateMobAoE = function(baseType, baseRadius)
|
|
if
|
|
baseType == xi.magic.aoe.RADIAL_MANI or
|
|
baseType == xi.magic.aoe.RADIAL_ACCE or
|
|
baseType == xi.magic.aoe.DIFFUSION
|
|
then
|
|
return { xi.magic.aoe.NONE, 0 }
|
|
end
|
|
|
|
if baseType == xi.magic.aoe.PIANISSIMO then
|
|
return { xi.magic.aoe.RADIAL, baseRadius }
|
|
end
|
|
|
|
return { baseType, baseRadius }
|
|
end
|
|
|
|
---Calculate spell AoE type and radius based on caster modifiers.
|
|
---@param caster CBaseEntity
|
|
---@param spell CSpell
|
|
---@return [xi.magic.aoe, number]
|
|
xi.combat.magicAoE.calculateTypeAndRadius = function(caster, spell)
|
|
local baseType = spell:isAoE()
|
|
local baseRadius = spell:getRadius()
|
|
local spellFamily = spell:getSpellFamily()
|
|
local spellGroup = spell:getSpellGroup()
|
|
|
|
-- Until proven otherwise, these effects only apply to players and trusts.
|
|
if
|
|
not caster:isPC() and
|
|
not caster:isTrust()
|
|
then
|
|
return calculateMobAoE(baseType, baseRadius)
|
|
end
|
|
|
|
-- Majesty converts Cure and Protect spells to 10y AoE
|
|
if caster:hasStatusEffect(xi.effect.MAJESTY) then
|
|
if
|
|
spellFamily == xi.magic.spellFamily.CURE or
|
|
spellFamily == xi.magic.spellFamily.PROTECT
|
|
then
|
|
return { xi.magic.aoe.RADIAL, 10 }
|
|
end
|
|
end
|
|
|
|
-- Accession / Divine Veil converts eligible spells to 10y AoE
|
|
if baseType == xi.magic.aoe.RADIAL_ACCE then
|
|
if caster:hasStatusEffect(xi.effect.ACCESSION) then
|
|
return { xi.magic.aoe.RADIAL, 10 }
|
|
end
|
|
|
|
-- Divine Veil: -na spells become AoE with Divine Seal or AOE_NA mod chance
|
|
if
|
|
caster:hasTrait(xi.trait.DIVINE_VEIL) and
|
|
(spellFamily == xi.magic.spellFamily.NA or spell:getID() == xi.magic.spell.ERASE) and
|
|
(caster:hasStatusEffect(xi.effect.DIVINE_SEAL) or math.random(100) <= caster:getMod(xi.mod.AOE_NA))
|
|
then
|
|
return { xi.magic.aoe.RADIAL, 10 }
|
|
end
|
|
|
|
return { xi.magic.aoe.NONE, 0 }
|
|
end
|
|
|
|
-- Manifestation converts eligible spells to 10y AoE
|
|
if
|
|
caster:hasStatusEffect(xi.effect.MANIFESTATION) and
|
|
baseType == xi.magic.aoe.RADIAL_MANI
|
|
then
|
|
return { xi.magic.aoe.RADIAL, 10 }
|
|
end
|
|
|
|
-- Theurgic Focus halves the AoE radius of -ra spells
|
|
if
|
|
caster:hasStatusEffect(xi.effect.THEURGIC_FOCUS) and
|
|
spellFamily >= xi.magic.spellFamily.FIRA and
|
|
spellFamily <= xi.magic.spellFamily.WATERA
|
|
then
|
|
return { xi.magic.aoe.RADIAL, math.floor(baseRadius / 2) }
|
|
end
|
|
|
|
-- Songs: Pianissimo forces single target, otherwise calculate BRD radius bonus with String equipment
|
|
if spellGroup == xi.magic.spellGroup.SONG then
|
|
-- Single target songs are always single targets
|
|
if baseType == xi.magic.aoe.NONE then
|
|
return { xi.magic.aoe.NONE, 0 }
|
|
end
|
|
|
|
if
|
|
caster:hasStatusEffect(xi.effect.PIANISSIMO) and
|
|
baseType == xi.magic.aoe.PIANISSIMO
|
|
then
|
|
-- Delete Pianissimo now as it has no use beyond AoE calculations
|
|
caster:delStatusEffect(xi.effect.PIANISSIMO)
|
|
return { xi.magic.aoe.NONE, 0 }
|
|
end
|
|
|
|
return { xi.magic.aoe.RADIAL, xi.combat.magicAoE.calculateSongRadius(caster, spell) }
|
|
end
|
|
|
|
-- Diffusion converts eligible spells to 10y AoE
|
|
if
|
|
caster:hasStatusEffect(xi.effect.DIFFUSION) and
|
|
baseType == xi.magic.aoe.DIFFUSION
|
|
then
|
|
return { xi.magic.aoe.RADIAL, 10 }
|
|
end
|
|
|
|
-- Convergence forces BLU offensive magic spells to be single target
|
|
if
|
|
caster:hasStatusEffect(xi.effect.CONVERGENCE) and
|
|
spellGroup == xi.magic.spellGroup.BLUE and
|
|
spell:getElement() ~= xi.element.NONE
|
|
then
|
|
return { xi.magic.aoe.NONE, 0 }
|
|
end
|
|
|
|
-- Certain equipment convert Utsusemi spells to 10y AoE
|
|
if
|
|
caster:getMod(xi.mod.UTSUSEMI_AOE) ~= 0 and
|
|
spellFamily == xi.magic.spellFamily.UTSUSEMI
|
|
then
|
|
return { xi.magic.aoe.RADIAL, 10 }
|
|
end
|
|
|
|
return { baseType, baseRadius }
|
|
end
|