Skip to content

Commit f137903

Browse files
committed
Era Skillchain Module
Adds a Era Skillchain Module
1 parent f950fe0 commit f137903

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
-----------------------------------
2+
-- Era Skillchain Module
3+
-- Adds a resistance check back to skillchain damage calculations.
4+
-- https://wiki.ffo.jp/html/32936.html
5+
-----------------------------------
6+
require('modules/module_utils')
7+
-----------------------------------
8+
local moduleName = 'era_skillchain'
9+
10+
if xi.module.isContentEnabled('SOA') then
11+
return { name = moduleName }
12+
end
13+
14+
local m = Module:new(moduleName)
15+
16+
local chainMultipliers =
17+
{
18+
[1] = { 0.50, 0.60, 0.70, 0.80, 0.90, 1.00 }, -- Level 1
19+
[2] = { 0.60, 0.75, 1.00, 1.25, 1.50, 1.75 }, -- Level 2
20+
[3] = { 1.00, 1.50, 1.75, 2.00, 2.25, 2.50 }, -- Level 3
21+
[4] = { 1.50, 1.80, 2.10, 2.40, 2.70, 3.00 }, -- Level 4 "Radiance/Umbra"
22+
}
23+
24+
local function getSkillchainElementToUse(target, skillchainType)
25+
-- Build skillchain available elements table.
26+
local elementTable = {}
27+
for i = xi.element.FIRE, xi.element.DARK do
28+
if xi.data.element.skillchainElementTable[i][skillchainType] > 0 then
29+
table.insert(elementTable, #elementTable + 1, i)
30+
end
31+
end
32+
33+
-- Early return: Single elemental SC. No need to continue.
34+
if #elementTable == 1 then
35+
return elementTable[1]
36+
end
37+
38+
-- Get lowest resistance rank value.
39+
local lowestResRank = 11
40+
local lowestElement = xi.element.FIRE
41+
42+
for j = #elementTable, 1, -1 do
43+
local resRankValue = target:getMod(xi.data.element.getElementalResistanceRankModifier(elementTable[j]))
44+
if resRankValue <= lowestResRank then
45+
lowestResRank = resRankValue
46+
lowestElement = elementTable[j]
47+
end
48+
end
49+
50+
return lowestElement
51+
end
52+
53+
m:addOverride('xi.combat.skillchain.calculateSkillchainDamage', function(actor, target, baseDamage)
54+
local skillchainEffect = target:getStatusEffect(xi.effect.SKILLCHAIN)
55+
if not skillchainEffect then
56+
return 0
57+
end
58+
59+
local skillchainType = skillchainEffect:getPower()
60+
if skillchainType == 0 then
61+
return 0
62+
end
63+
64+
local skillchainLevel = skillchainEffect:getTier()
65+
if skillchainLevel < 1 or skillchainLevel > 4 then
66+
return 0
67+
end
68+
69+
local skillchainCount = skillchainEffect:getSubPower()
70+
if skillchainCount < 1 or skillchainCount > 6 then
71+
return 0
72+
end
73+
74+
local skillchainElement = getSkillchainElementToUse(target, skillchainType)
75+
if not skillchainElement then
76+
return 0
77+
end
78+
79+
if xi.spells.damage.calculateNullification(target, skillchainElement, true, false) == 0 then
80+
return 0
81+
end
82+
83+
-- Calculate resist, base damage and multipliers.
84+
local finalDamage = math.abs(baseDamage) -- Damage from skillchain, no matter if absorbed or not.
85+
local levelMultiplier = chainMultipliers[skillchainLevel][skillchainCount]
86+
local bonusMultiplier = 1 + actor:getMod(xi.mod.SKILLCHAINBONUS) / 100
87+
local damageMultiplier = 1 + actor:getMod(xi.mod.SKILLCHAINDMG) / 10000
88+
local dayWeatherMultiplier = xi.spells.damage.calculateDayAndWeather(actor, skillchainElement, false)
89+
local staffMultiplier = xi.spells.damage.calculateElementalStaffBonus(actor, skillchainElement)
90+
local affinityMultiplier = xi.spells.damage.calculateElementalAffinityBonus(actor, skillchainElement)
91+
local resistRate = xi.combat.magicHitRate.calculateResistRate(actor, target, 0, 0, 0, skillchainElement, 0, 0, 0)
92+
local magicTakenMultiplier = xi.combat.damage.calculateDamageAdjustment(target, false, true, false, false)
93+
94+
-- Unconfirmed order.
95+
local inninMultiplier = 1 + actor:getMerit(xi.merit.INNIN_EFFECT) / 100
96+
local sengikoriMultiplier = 1 + target:getMod(xi.mod.SENGIKORI_SC_DMG_DEBUFF) / 100
97+
local absorptionMultiplier = xi.spells.damage.calculateAbsorption(target, skillchainElement, true)
98+
99+
-- Apply multipliers in order and floor after each step.
100+
finalDamage = math.floor(finalDamage * levelMultiplier)
101+
finalDamage = math.floor(finalDamage * bonusMultiplier) + actor:getMod(xi.mod.MAGIC_DAMAGE)
102+
finalDamage = math.floor(finalDamage * damageMultiplier)
103+
finalDamage = math.floor(finalDamage * dayWeatherMultiplier)
104+
finalDamage = math.floor(finalDamage * staffMultiplier)
105+
finalDamage = math.floor(finalDamage * affinityMultiplier)
106+
finalDamage = math.floor(finalDamage * resistRate)
107+
finalDamage = math.floor(finalDamage * magicTakenMultiplier)
108+
finalDamage = math.floor(finalDamage * inninMultiplier)
109+
finalDamage = math.floor(finalDamage * sengikoriMultiplier)
110+
finalDamage = math.floor(finalDamage * absorptionMultiplier)
111+
112+
-- Handle (reset) Sengikori.
113+
target:setMod(xi.mod.SENGIKORI_SC_DMG_DEBUFF, 0)
114+
115+
-- Handle other damage alterations.
116+
if finalDamage > 0 then
117+
finalDamage = utils.clamp(utils.handlePhalanx(target, finalDamage), 0, 99999)
118+
finalDamage = utils.clamp(utils.handleOneForAll(target, finalDamage), 0, 99999)
119+
finalDamage = utils.clamp(utils.handleStoneskin(target, finalDamage), 0, 99999)
120+
finalDamage = target:checkDamageCap(finalDamage)
121+
122+
target:takeDamage(finalDamage, actor, xi.attackType.SPECIAL, xi.damageType.ELEMENTAL + skillchainElement)
123+
124+
-- Handle absorbption.
125+
else
126+
target:addHP(-finalDamage)
127+
end
128+
129+
return finalDamage
130+
end)
131+
132+
return m

0 commit comments

Comments
 (0)