----------------------------------- -- Global file for skillchain calculations. ----------------------------------- require('scripts/globals/spells/damage_spell') ----------------------------------- xi = xi or {} xi.combat = xi.combat or {} xi.combat.skillchain = xi.combat.skillchain or {} ----------------------------------- local resistanceRankMultiplier = { [-3] = 1.50, [-2] = 1.30, [-1] = 1.15, [ 0] = 1.00, [ 1] = 0.85, [ 2] = 0.70, [ 3] = 0.60, [ 4] = 0.50, [ 5] = 0.40, [ 6] = 0.30, [ 7] = 0.25, [ 8] = 0.20, [ 9] = 0.15, [10] = 0.10, [11] = 0.05, } local chainMultipliers = { [1] = { 0.50, 0.60, 0.70, 0.80, 0.90, 1.00 }, -- Level 1 [2] = { 0.60, 0.75, 1.00, 1.25, 1.50, 1.75 }, -- Level 2 [3] = { 1.00, 1.50, 1.75, 2.00, 2.25, 2.50 }, -- Level 3 [4] = { 1.50, 1.80, 2.10, 2.40, 2.70, 3.00 }, -- Level 4 "Radiance/Umbra" } local function getSkillchainElementToUse(target, skillchainType) -- Build skillchain available elements table. local elementTable = {} for i = xi.element.FIRE, xi.element.DARK do if xi.data.element.skillchainElementTable[i][skillchainType] > 0 then table.insert(elementTable, #elementTable + 1, i) end end -- Early return: Single elemental SC. No need to continue. if #elementTable == 1 then return elementTable[1] end -- Get lowest resistance rank value. local lowestResRank = 11 local lowestElement = xi.element.FIRE for j = #elementTable, 1, -1 do local resRankValue = target:getMod(xi.data.element.getElementalResistanceRankModifier(elementTable[j])) if resRankValue <= lowestResRank then lowestResRank = resRankValue lowestElement = elementTable[j] end end return lowestElement end -- Handles skillchain magic damage multipliers, called from C++ (battleutils::TakeSkillchainDamage) xi.combat.skillchain.calculateSkillchainDamage = function(actor, target, baseDamage) local skillchainEffect = target:getStatusEffect(xi.effect.SKILLCHAIN) if not skillchainEffect then return 0 end local skillchainType = skillchainEffect:getPower() if skillchainType == 0 then return 0 end local skillchainLevel = skillchainEffect:getTier() if skillchainLevel < 1 or skillchainLevel > 4 then return 0 end local skillchainCount = skillchainEffect:getSubPower() if skillchainCount < 1 or skillchainCount > 6 then return 0 end local skillchainElement = getSkillchainElementToUse(target, skillchainType) if not skillchainElement then return 0 end if xi.spells.damage.calculateNullification(target, skillchainElement, false, true, false, false) == 0 then return 0 end -- Resistance rank. local resRankModifier = xi.data.element.getElementalResistanceRankModifier(skillchainElement) local resRankValue = utils.clamp(target:getMod(resRankModifier), -3, 11) -- Calculate base damage and multipliers. local finalDamage = math.abs(baseDamage) -- Damage from skillchain, no matter if absorbed or not. local levelMultiplier = chainMultipliers[skillchainLevel][skillchainCount] local bonusMultiplier = 1 + actor:getMod(xi.mod.SKILLCHAINBONUS) / 100 local damageMultiplier = 1 + actor:getMod(xi.mod.SKILLCHAINDMG) / 10000 local dayWeatherMultiplier = xi.spells.damage.calculateDayAndWeather(actor, skillchainElement, false) local staffMultiplier = xi.spells.damage.calculateElementalStaffBonus(actor, skillchainElement) local affinityMultiplier = xi.spells.damage.calculateElementalAffinityBonus(actor, skillchainElement) local resRankMultiplier = resistanceRankMultiplier[resRankValue] local magicTakenMultiplier = xi.combat.damage.calculateDamageAdjustment(target, false, true, false, false) -- Unconfirmed order. local inninMultiplier = 1 + actor:getMerit(xi.merit.INNIN_EFFECT) / 100 local sengikoriMultiplier = 1 + target:getMod(xi.mod.SENGIKORI_SC_DMG_DEBUFF) / 100 -- Apply multipliers in order and floor after each step. finalDamage = math.floor(finalDamage * levelMultiplier) finalDamage = math.floor(finalDamage * bonusMultiplier) + actor:getMod(xi.mod.MAGIC_DAMAGE) finalDamage = math.floor(finalDamage * damageMultiplier) finalDamage = math.floor(finalDamage * dayWeatherMultiplier) finalDamage = math.floor(finalDamage * staffMultiplier) finalDamage = math.floor(finalDamage * affinityMultiplier) finalDamage = math.floor(finalDamage * resRankMultiplier) finalDamage = math.floor(finalDamage * magicTakenMultiplier) finalDamage = math.floor(finalDamage * inninMultiplier) finalDamage = math.floor(finalDamage * sengikoriMultiplier) -- Handle (reset) Sengikori. target:setMod(xi.mod.SENGIKORI_SC_DMG_DEBUFF, 0) -- Handle absorbption. local absorb = xi.spells.damage.calculateAbsorption(target, skillchainElement, false, true, false, false) < 0 if absorb then target:addHP(finalDamage) return finalDamage end -- Handle other damage alterations. if finalDamage > 0 then finalDamage = utils.clamp(utils.handlePhalanx(target, finalDamage), 0, 99999) finalDamage = utils.clamp(utils.handleOneForAll(target, finalDamage), 0, 99999) finalDamage = utils.handleStoneskin(target, finalDamage, xi.attackType.SPECIAL) finalDamage = target:checkDamageCap(finalDamage) target:takeDamage(finalDamage, actor, xi.attackType.SPECIAL, xi.damageType.ELEMENTAL + skillchainElement) end return finalDamage end