🐛 RDM JA Composure Missing Enspell DMG % Mod
I affirm:
- [x] I understand that if I do not agree to the following points by completing the checkboxes my issue will be ignored.
- [x] I have read and understood the Contributing Guide and the Code of Conduct.
- [x] I have searched existing issues to see if the issue has already been opened, and I have checked the commit log to see if the issue has been resolved since my server was last updated.
OS / platform the server is running (if known)
OSE => Windows Server 2016
Branch affected by issue
base
Steps to reproduce
Change job to RDM50 or greater Cast any enspell Attack and note enspell damage value Use job ability composure Cast any enspell Attack and note enspell damage value
Expected behavior
Composure is supposed to apply a mod of 200% increased enspell damage.
In my own testing, I modified the effect of Composure to grant the enspell dmg (%) with a value of 200. This made no change unfortunately, which may suggest that particular mod isn't wired up yet.
Conversely, adding a static increased value of enspell dmg bonus (eg. 200) resulted in a flat boost to enspell damage.
-- This was my own misunderstanding of how enspell damage was being calculated.
I've come back to testing this issue and confirmed it remains in the base branch as of Sept 26.
Composure accuracy should be updated according to: https://www.bg-wiki.com/ffxi/Composure https://wiki.ffo.jp/html/17230.html https://www.ffxiah.com/forum/topic/55398/anything-you-can-do-i-can-do-better-rdm-guide#job Note* no testing/captures have been located to support this otherwise universally described effect value.
To address the accuracy:
effectObject.onEffectGain = function(target, effect)
local jpValue = target:getJobPointLevel(xi.jp.COMPOSURE_EFFECT)
target:addMod(xi.mod.ACC, 15 + jpValue)
end
Becomes:
effectObject.onEffectGain = function(target, effect)
local jpValue = target:getJobPointLevel(xi.jp.COMPOSURE_EFFECT)
local cLevel = target:getMainLevel()
local accPower = math.floor(((24 * cLevel) + 74) / 49)
target:addMod(xi.mod.ACC, accPower + jpValue)
end
I'm unclear if the Composure effect should be the place where the enspell power is modified or not, or if this needs to be in the enspell and enspell II effects themselves. As I test it further I'll update this issue.
It appears that globals/spells/enhancing_spell.lua may be the appropriate place to address the other aspects of the Composure job ability. @Xaver-DaRed & @claywar - I see notes added by you in the past. Please advise on this approach as you're available to.
Propose adding to "Spell specific modifiers for potency" section, roughly around line 305, which appears to be handled in alphabetical order. Inserting the lines addressing en-spell and en-II-spells separately to try to match retail effect.
-- Bar-Status
elseif
spellEffect == xi.effect.BARAMNESIA or
(spellEffect >= xi.effect.BARSLEEP and spellEffect <= xi.effect.BARVIRUS)
then
finalPower = finalPower + caster:getMerit(xi.merit.BAR_SPELL_EFFECT)
-- En-Spells (Info from from BG-Wiki)
elseif
(spellEffect >= xi.effect.ENFIRE and spellEffect <= xi.effect.ENWATER)
then
if
caster:hasStatusEffect(xi.effect.COMPOSURE) and
caster:getID() == target:getID()
then
finalPower = (basePower + caster:getMod(xi.mod.ENSPELL_DMG_BONUS)) * 3
end
-- En-II-Spells (Info from from BG-Wiki)
elseif
(spellEffect >= xi.effect.ENFIRE_II and spellEffect <= xi.effect.ENWATER_II)
then
if
caster:hasStatusEffect(xi.effect.COMPOSURE) and
caster:getID() == target:getID()
then
finalPower = basePower * 3
end
-- Protect/Protectra
The "caster:getMod(xi.mod.ENSPELL_DMG_BONUS)" fixes calculating spell power for augmented gear with [augment id 896 / mod id 432] and later for the job point gifts for RDM mastery ~+23 enspell dmg during initial spell damage calculation. This approach won't work for tier II spells, though, because in retail you must keep the equipment on to gain the benefit.
I forgot to share this earlier. This data is well tested on retail: https://www.bg-wiki.com/ffxi/Category:Enspell
Testing on LSB has confirmed this approach works as retail for tier 1 enspells.
Tier 2 enspells have an odd outcome. Their damage appears to be capped at their base power * 2. With the above code, they immediately reach this cap and do not tick up as expected.
Example tier 2 enspell (enaero_ii):
-----------------------------------
-- xi.effect.ENAERO_II
-----------------------------------
---@type TEffect
local effectObject = {}
effectObject.onEffectGain = function(target, effect)
target:addMod(xi.mod.ENSPELL, xi.element.WIND + 8) -- Tier IIs have higher "enspell IDs"
target:addMod(xi.mod.ENSPELL_DMG, effect:getPower())
end
effectObject.onEffectTick = function(target, effect)
end
effectObject.onEffectLose = function(target, effect)
target:setMod(xi.mod.ENSPELL_DMG, 0)
target:setMod(xi.mod.ENSPELL, 0)
end
return effectObject
The mod being applied here [ENSPELL_DMG / mod id 343] doesn't have this limitation natively. This appears to be hard-coded in src/map/utils/battleutils.cpp.
Src/map/utils/battleutils.cpp includes the calculation for MERIT_ENSPELL_DAMAGE, ENSPELL_DMG_BONUS, and ENSPELL_DMG. This appears to be the proper place (around line 520) to include the calculation for Composure multipliers. Further, it could address the issue with tier 1 and tier 2. When the augment for enspell damage +% is implemented [augment id 899], that modifier would fit neatly into the calculation here. Job point gift +enspell damage is already accounted for here if it is added as an ENSPELL_DMG_BONUS mod while RDM main.
The proposed change for tier 1:
// Tier 1 enspells have their damage pre-calculated AT CAST TIME and is stored in Mod::ENSPELL_DMG
if (Tier == 1)
{
damage = PAttacker->getMod(Mod::ENSPELL_DMG) + PAttacker->getMod(Mod::ENSPELL_DMG_BONUS);
auto* PChar = dynamic_cast<CCharEntity*>(PAttacker);
if (PChar)
{
// Add enspell damage merits
damage += PChar->PMeritPoints->GetMeritValue(MERIT_ENSPELL_DAMAGE, PChar);
// Check for Composure status effect and triple the damage if present
if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_COMPOSURE))
{
damage *= 3; // Triple the enspell damage if Composure is active
}
}
}
The proposed change for tier 2:
else if (Tier == 2)
{
// Tier 2 enspells calculate the damage on each hit and increment the potency in Mod::ENSPELL_DMG per hit
uint16 skill = PAttacker->GetSkill(SKILL_ENHANCING_MAGIC);
uint16 cap = 3 + ((6 * skill) / 100);
if (skill > 200)
{
cap = 5 + ((5 * skill) / 100);
}
cap *= 2;
auto* PChar = dynamic_cast<CCharEntity*>(PAttacker);
if (PChar && PChar->StatusEffectContainer->HasStatusEffect(EFFECT_COMPOSURE))
{
// Multiply cap by 3 if Composure is active
cap *= 3;
}
// Enspell damage calculation
if (PAttacker->getMod(Mod::ENSPELL_DMG) > cap)
{
PAttacker->setModifier(Mod::ENSPELL_DMG, cap);
damage = cap;
}
else if (PAttacker->getMod(Mod::ENSPELL_DMG) == cap)
{
damage = cap;
}
else if (PAttacker->getMod(Mod::ENSPELL_DMG) < cap)
{
PAttacker->addModifier(Mod::ENSPELL_DMG, 1);
damage = PAttacker->getMod(Mod::ENSPELL_DMG) - 1;
}
// Apply additional modifiers
damage += PAttacker->getMod(Mod::ENSPELL_DMG_BONUS);
// Apply merit bonuses
if (PChar)
{
damage += PChar->PMeritPoints->GetMeritValue(MERIT_ENSPELL_DAMAGE, PChar) * 2;
// Multiply starting damage by 3 if Composure is active
if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_COMPOSURE))
{
damage *= 3;
}
}
}
The proposed scripts/effects/composure.lua change is to become this:
-----------------------------------
-- xi.effect.COMPOSURE
-- Increases accuracy and lengthens recast time. Enhancement effects gained through white
-- and black magic you cast on yourself last longer.
-----------------------------------
---@type TEffect
local effectObject = {}
effectObject.onEffectGain = function(target, effect)
local player = target:getMaster() or target
local mLevel = player:getMainLvl()
local jpValue = player:getJobPointLevel(xi.jp.COMPOSURE_EFFECT)
local accPower = math.floor(((24 * mLevel) + 74) / 49)
effect:setPower(accPower + jpValue)
player:addMod(xi.mod.ACC, effect:getPower())
end
effectObject.onEffectTick = function(target, effect)
end
effectObject.onEffectLose = function(target, effect)
local player = target:getMaster() or target
player:delMod(xi.mod.ACC, effect:getPower())
end
return effectObject
@TeoTwawki , @zach2good , @Xaver-DaRed , and @claywar - I've officially touched or wandered through each of your code. Your feedback is appreciated.
I'll recompile, test this change, and report back.
You've tagged a lot of folks here. Can you please consolidate the issue a bit? This is a lot of followup comments for composure needing to apply 200% enspell. If you have a solution, please raise a PR, but for positing different solutions, please describe the problem you're trying to solve before diverging into code.
You've definitely done your homework, and I appreciate that; however, we're not all focused on combat/magic, and really that's just Xaver. My real reason for posting the above comments is because the initial issue appears to be clear, but then we're many comments deep with text and code, and I'll be honest, after a long day of work I'm really looking for a summary prior to digging deep.
That's a fine point! The intent was to solicit feedback on the preferred path to remedy this bug. It seemed to me that it could be fixed in multiple ways and I didn't want the result to be a hack; rather, it should be in keeping with the intended path for ongoing development and support of this project.
Since I found so many comments in prior commits in these files, I tagged each of you. No spam intended.
I believe I've found the appropriate place to correct this issue, but my math on tier 2 is bugged. Once I remedy that I'll open pull requests for each change individually.
If Xaver or anyone else here has direction regarding this particular approach to addressing the bug, please let me know.