-
Notifications
You must be signed in to change notification settings - Fork 874
[core][lua][magic] Unify Spell Interrupt into Lua #10204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: base
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Proposal: xi.combat.magic.shouldInterruptSpell = function(attacker, defender, spell)
-- Early return: Manafont prevents casting interruption.
if defender:hasStatusEffect(xi.effect.MANAFONT) then
return false
end
-- Early return: Trusts can't be interrupted.
local entityType = defender:getObjType()
if entityType == xi.objType.TRUST then
return false
end
-- Early return: Songs can't be interrupted.
local skillType = spell:getSkillType()
if skillType == xi.skill.SINGING then
return false
end
-- Llevel factor.
local baseInterruptionRate = entityType == xi.objType.MOB and 5 or 50
local levelRatio = utils.clamp((baseInterruptionRate + attacker:getMainLvl() - defender:getMainLvl()) / 100, 0.01, 1.02)
-- Skill factor.
local skillRatio = 1
if entityType == xi.objType.PC then
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
else
skillRatio = skillCap / skillLevel
end
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 - defender:getMerit(xi.merit.SPELL_INTERUPTION_RATE) - defender:getMod(xi.mod.SPELLINTERRUPT)) / 100
-- levelRatio : 0.01 to infinity.
-- skillRatio : 1 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.
-- Early return: Caster doesn't get interrupted.
if math.random() >= finalRatio then
return false
end
-- Early return: Caster can't prevent interruption via aquaveil.
if not defender:hasStatusEffect(xi.effect.AQUAVEIL) then
return true
end
-- Handle aquaveil and prevent interruption.
local aquaveilPower = defender:getStatusEffect(xi.effect.AQUAVEIL):getPower() - 1
if aquaveilPower == 0 then
defender:delStatusEffect(xi.effect.AQUAVEIL)
else
defender:getStatusEffect(xi.effect.AQUAVEIL):setPower(aquaveilPower)
end
return false
end
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the sake of this being a learning experience, some notes here:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having said all of this, we have to decide if we really want this in lua, now, or in the future, or if a setting is the better approach. But nontheless, its a good job. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| require('scripts/globals/combat/magic_hit_rate') | ||
| require('scripts/globals/combat/magic_interrupt') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unneeded |
||
| require('scripts/globals/jobpoints') | ||
| require('scripts/globals/spells/damage_spell') | ||
| ----------------------------------- | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get a recheck on this? I don't believe Trusts are immune to spell interruption.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure the only non interrupted casting action is with movement and no action to prevent casting.