diff --git a/scripts/globals/combat/magic_interrupt.lua b/scripts/globals/combat/magic_interrupt.lua new file mode 100644 index 00000000000..5a5b497ff28 --- /dev/null +++ b/scripts/globals/combat/magic_interrupt.lua @@ -0,0 +1,86 @@ +----------------------------------- +-- Global file for spell interrupt +----------------------------------- +xi = xi or {} +xi.combat = xi.combat or {} +xi.combat.magic = xi.combat.magic or {} + +---Return whether a spell should be interrupted. +---@params attacker CBaseEntity +---@params defender CBaseEntity +---@params spell CSpell +---@return boolean +xi.combat.magic.shouldInterruptSpell = function(attacker, defender, spell) + -- Exceptions. + if + defender:getObjType() == xi.objType.TRUST or -- Caster is a trust. + defender:hasStatusEffect(xi.effect.MANAFONT) or -- Caster has Manafont + spell:getSkillType() == xi.skill.SINGING -- Spell is a song. + then + return false + end + + -- Calculate level ratio. BaseRate + Attacker main level - Defender main level. + local levelRatio = ((defender:getObjType() == xi.objType.MOB and 5 or 50) + attacker:getMainLvl() - defender:getMainLvl()) / 100.0 + + if levelRatio < 0.01 then + levelRatio = 0.01 + end + + -- Calculate skill ratio. + local skillRatio = 1.0 + local meritReduction = 0 + + if defender:getObjType() == xi.objType.PC then + local skillType = spell:getSkillType() + local skillCap = defender:getMaxSkillLevel(defender:getMainLvl(), defender:getMainJob(), skillType) + local skillLevel = defender:getSkillLevel(skillType) + + -- If skill cap is 0, player may be using a spell from their subjob. + if skillCap == 0 then + skillCap = defender:getMaxSkillLevel(defender:getMainLvl(), defender:getSubJob(), skillType) + end + + -- If skill level is 0, set ratio to 10. + if skillLevel <= 0 then + skillRatio = 10.0 + else + skillRatio = skillCap / skillLevel + end + + -- Fetch player-only interruption rate reduction from merits. + meritReduction = defender:getMerit(xi.merit.SPELL_INTERUPTION_RATE) + end + + -- SIRD reduces the interrupt after all the calculations are done -- as evidenced by the infamous "102% SIRD" builds. + -- Anything less than 102% interrupt results in the ability to be interrupted. + -- Note: the 102% is probably an x/256 x/1024 nonsense -- sometimes 101% works. + local sirdRatio = (100.0 - meritReduction - defender:getMod(xi.mod.SPELLINTERRUPT)) / 100.0 + local chance = math.random() + + -- These are all ratios. + -- levelRatio : 0.01 to infinity. + -- skillRatio: 1.0 to infinity. + -- SIRDRatio: No limits. Can be negative. A negative value will guarantee NOT being interrupted. + local finalRatio = levelRatio * skillRatio * sirdRatio -- TL;DR Higher = Worse = More chances to get interrupted. + + -- You get interrupted. Handle aquaveil. + if chance < finalRatio then + if defender:hasStatusEffect(xi.effect.AQUAVEIL) then + local aquaCount = defender:getStatusEffect(xi.effect.AQUAVEIL):getPower() + + -- Removes the status but still prevents the interrupt. + if aquaCount - 1 == 0 then + defender:delStatusEffect(xi.effect.AQUAVEIL) + else + defender:getStatusEffect(xi.effect.AQUAVEIL):setPower(aquaCount - 1) + end + + return false + end + + return true + end + + return false +end diff --git a/scripts/globals/magic.lua b/scripts/globals/magic.lua index 3368962b680..ec93e06985d 100644 --- a/scripts/globals/magic.lua +++ b/scripts/globals/magic.lua @@ -1,4 +1,5 @@ require('scripts/globals/combat/magic_hit_rate') +require('scripts/globals/combat/magic_interrupt') require('scripts/globals/jobpoints') require('scripts/globals/spells/damage_spell') ----------------------------------- diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index f49fc64f6a9..1c2cc8d56c1 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -1897,86 +1897,7 @@ auto CalculateTPFromDamageTaken(CBattleEntity* PAttacker, CBattleEntity* PDefend bool TryInterruptSpell(CBattleEntity* PAttacker, CBattleEntity* PDefender, CSpell* PSpell) { - // Exceptions. - if (PDefender->objtype == TYPE_TRUST || // Caster is a trust. - PDefender->StatusEffectContainer->HasStatusEffect(EFFECT_MANAFONT) || // Caster has Manafont. - (SKILLTYPE)PSpell->getSkillType() == SKILL_SINGING) // Spell is a song. - { - return false; - } - - // Calculate level ratio. - int baseRate = (PDefender->objtype == TYPE_MOB) ? 5 : 50; - float levelRatio = (float)(baseRate + PAttacker->GetMLevel() - PDefender->GetMLevel()) / 100.0f; - - if (levelRatio < 0.01) - { - levelRatio = 0.01f; - } - - // Calculate skill ratio. - float skillRatio = 1.0f; - uint8 meritReduction = 0; - - if (PDefender->objtype == TYPE_PC) - { - CCharEntity* PChar = (CCharEntity*)PDefender; - float skillCap = GetMaxSkill((SKILLTYPE)PSpell->getSkillType(), PChar->GetMJob(), PChar->GetMLevel()); - float skillLevel = PChar->GetSkill(PSpell->getSkillType()); - - // If skill cap is 0, player may be using a spell from their subjob. - if (skillCap == 0) - { - skillCap = GetMaxSkill((SKILLTYPE)PSpell->getSkillType(), PChar->GetSJob(), PChar->GetMLevel()); // This may need to be re-investigated in the future. - } - - // If skill level is 0, set ratio to 10. - if (skillLevel <= 0) - { - skillRatio = 10.0f; - } - else - { - skillRatio = skillCap / skillLevel; - } - - // Fetch player-only interruption rate reduction from merits. - meritReduction = ((CCharEntity*)PDefender)->PMeritPoints->GetMeritValue(MERIT_SPELL_INTERUPTION_RATE, (CCharEntity*)PDefender); - } - - // SIRD reduces the interrupt after all the calculations are done -- as evidenced by the infamous "102% SIRD" builds. - // Anything less than 102% interrupt results in the ability to be interrupted. - // Note: the 102% is probably an x/256 x/1024 nonsense -- sometimes 101% works. - float SIRDRatio = (100.0f - meritReduction - (float)PDefender->getMod(Mod::SPELLINTERRUPT)) / 100.0f; - float chance = xirand::GetRandomNumber(1.0f); - - // This are all ratios. - // levelRatio : 0.01 to infinity. - // skillRatio: 1.0 to infinity. - // SIRDRatio: No limits. Can be negative. A negative value will guarantee NOT being interrupted. - float finalRatio = levelRatio * skillRatio * SIRDRatio; // TL;DR Higher = Worse = More chances to get interrupted. - - // You get interrupted. Handle aquaveil. - if (chance < finalRatio) - { - if (PDefender->StatusEffectContainer->HasStatusEffect(EFFECT_AQUAVEIL)) - { - auto aquaCount = PDefender->StatusEffectContainer->GetStatusEffect(EFFECT_AQUAVEIL)->GetPower(); - if (aquaCount - 1 == 0) // removes the status, but still prevents the interrupt - { - PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_AQUAVEIL); - } - else - { - PDefender->StatusEffectContainer->GetStatusEffect(EFFECT_AQUAVEIL)->SetPower(aquaCount - 1); - } - return false; - } - - return true; - } - - return false; + return luautils::callGlobal("xi.combat.magic.shouldInterruptSpell", PAttacker, PDefender, PSpell); } /************************************************************************