From 3c3b5bc9ecb3f12a9afb02234ad05c9d0075f43e Mon Sep 17 00:00:00 2001 From: Xaver-DaRed Date: Thu, 23 Jan 2025 15:56:20 +0100 Subject: [PATCH] Implement cardinal chant --- scripts/effects/collimated_fervor.lua | 16 +++++ scripts/enum/direction.lua | 14 ++++ scripts/enum/mod.lua | 1 + scripts/globals/job_utils/geomancer.lua | 9 +-- scripts/globals/spells/damage_spell.lua | 96 +++++++++++++++++++++++-- sql/item_mods.sql | 40 +++++------ src/map/modifier.h | 3 +- 7 files changed, 147 insertions(+), 32 deletions(-) create mode 100644 scripts/effects/collimated_fervor.lua create mode 100644 scripts/enum/direction.lua diff --git a/scripts/effects/collimated_fervor.lua b/scripts/effects/collimated_fervor.lua new file mode 100644 index 00000000000..8a26d1f5e4b --- /dev/null +++ b/scripts/effects/collimated_fervor.lua @@ -0,0 +1,16 @@ +----------------------------------- +-- xi.effect.COLLIMATED_FERVOR +----------------------------------- +---@type TEffect +local effectObject = {} + +effectObject.onEffectGain = function(target, effect) +end + +effectObject.onEffectTick = function(target, effect) +end + +effectObject.onEffectLose = function(target, effect) +end + +return effectObject diff --git a/scripts/enum/direction.lua b/scripts/enum/direction.lua new file mode 100644 index 00000000000..6e46b32fc14 --- /dev/null +++ b/scripts/enum/direction.lua @@ -0,0 +1,14 @@ +xi = xi or {} + +---@enum xi.direction +xi.direction = +{ + EAST = 0, -- Angle = 0 + SOUTH_EAST = 1, -- Angle = 32 + SOUTH = 2, -- Angle = 64 + SOUTH_WEST = 3, -- Angle = 96 + WEST = 4, -- Angle = 128 + NORTH_WEST = 5, -- Angle = 160 + NORTH = 6, -- Angle = 192 + NORTH_EAST = 7, -- Angle = 224 +} diff --git a/scripts/enum/mod.lua b/scripts/enum/mod.lua index 0d1855ca1bf..31ed4282495 100644 --- a/scripts/enum/mod.lua +++ b/scripts/enum/mod.lua @@ -949,6 +949,7 @@ xi.mod = SNEAK_DURATION = 946, -- Additional duration in seconds INVISIBLE_DURATION = 947, -- Additional duration in seconds CARDINAL_CHANT = 959, + CARDINAL_CHANT_BONUS = 1132, -- Geomancy galero INDI_DURATION = 960, GEOMANCY_BONUS = 961, -- Used to increase potency of "Geomancy +" items (only the highest value is counted) WIDENED_COMPASS = 962, diff --git a/scripts/globals/job_utils/geomancer.lua b/scripts/globals/job_utils/geomancer.lua index 002cf98c0b0..a9cc5e7b874 100644 --- a/scripts/globals/job_utils/geomancer.lua +++ b/scripts/globals/job_utils/geomancer.lua @@ -316,15 +316,16 @@ xi.job_utils.geomancer.eclipticAttrition = function(player, target, ability) end end -xi.job_utils.geomancer.collimatedFervor = function(player, target, ability) -- TODO: cardinal direction stuff +xi.job_utils.geomancer.collimatedFervor = function(player, target, ability) + target:addStatusEffect(xi.effect.COLLIMATED_FERVOR, 0, 0, 60) end xi.job_utils.geomancer.lifeCycle = function(player, target, ability) - local hpAmount = math.floor(0.25 * player:getHP()) + local hpAmount = math.floor(0.25 * player:getHP()) local hpTransfer = hpAmount if player:getMod(xi.mod.LIFE_CYCLE_EFFECT) > 0 then - hpTransfer = hpAmount * (player:getMod(xi.mod.LIFE_CYCLE_EFFECT) / 10) + hpTransfer = hpAmount * player:getMod(xi.mod.LIFE_CYCLE_EFFECT) / 10 end target:restoreHP(hpTransfer) @@ -341,7 +342,7 @@ xi.job_utils.geomancer.dematerialize = function(player, target, ability) return xi.effect.DEMATERIALIZE end -xi.job_utils.geomancer.theugicFocus = function(player, target, ability) -- TODO: cardinal direction stuff +xi.job_utils.geomancer.theurgicFocus = function(player, target, ability) end xi.job_utils.geomancer.widenedCompass = function(player, target, ability) diff --git a/scripts/globals/spells/damage_spell.lua b/scripts/globals/spells/damage_spell.lua index e593febf32b..dadc9609fd7 100644 --- a/scripts/globals/spells/damage_spell.lua +++ b/scripts/globals/spells/damage_spell.lua @@ -219,6 +219,88 @@ local pTable = [xi.magic.spell.CURE_VI ] = { xi.mod.MND, 0, 295, 2, 295, 212, 0 }, } +local function cardinalChantBonus(actor, target, direction, spellId, skillType) + -- https://www.bg-wiki.com/ffxi/Cardinal_Chant + local chantBonus = 0 + + -- Early return + if spellId == 0 or skillType ~= xi.skill.ELEMENTAL_MAGIC then + return chantBonus + end + + -- Calculate base bonus. + local raSpellTable = + set{ + xi.magic.spell.STONERA, xi.magic.spell.STONERA_II, xi.magic.spell.STONERA_III, + xi.magic.spell.WATERA, xi.magic.spell.WATERA_II, xi.magic.spell.WATERA_III, + xi.magic.spell.AERA, xi.magic.spell.AERA_II, xi.magic.spell.AERA_III, + xi.magic.spell.FIRA, xi.magic.spell.FIRA_II, xi.magic.spell.FIRA_III, + xi.magic.spell.BLIZZARA, xi.magic.spell.BLIZZARA_II, xi.magic.spell.BLIZZARA_III, + xi.magic.spell.THUNDARA, xi.magic.spell.THUNDARA_II, xi.magic.spell.THUNDARA_III, + } + + local chantTable = + { -- [trait] = { { east }, { south }, { west }, { north } } + [0] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + [1] = { { 5, 8 }, { 5, 8 }, { 10, 15 }, { 5, 8 } }, + [2] = { { 7, 10 }, { 7, 10 }, { 14, 19 }, { 7, 10 } }, + [3] = { { 10, 14 }, { 10, 14 }, { 18, 24 }, { 10, 14 } }, + [4] = { { 13, 17 }, { 13, 17 }, { 22, 28 }, { 13, 17 } }, + } + + local isRaSpell = raSpellTable[spellId] and 2 or 1 + local baseBonus = chantTable[actor:getMod(xi.mod.CARDINAL_CHANT)][direction][isRaSpell] + + -- Calculate fervor % + local fervorFactor = actor:hasStatusEffect(xi.effect.COLLIMATED_FERVOR) and 1.5 or 1 + + -- Calculate gear % + local gearFactor = 1 + actor:getMod(xi.mod.CARDINAL_CHANT_BONUS) / 100 + + -- Calculate angle % + local angle = utils.getWorldRotation(actor:getPos(), target:getPos()) + local angleFactor = 0 + + switch (direction) : caseof + { + [xi.direction.EAST] = function() -- MAB -> Optimal angle = 0 + if angle > 192 and angle <= 256 then + angleFactor = 100 - (256 - angle) * 100 / 64 + elseif angle > 0 and angle < 64 then + angleFactor = 100 - angle * 100 / 64 + end + end, + + [xi.direction.SOUTH] = function() -- MACC -> Optimal angle = 64 + if angle > 0 and angle <= 64 then + angleFactor = 100 - (64 - angle) * 100 / 64 + elseif angle > 64 and angle < 128 then + angleFactor = 100 - (angle - 64) * 100 / 64 + end + end, + + [xi.direction.WEST] = function() -- MBB -> Optimal angle = 128 + if angle > 64 and angle <= 128 then + angleFactor = 100 - (128 - angle) * 100 / 64 + elseif angle > 128 and angle < 192 then + angleFactor = 100 - (angle - 128) * 100 / 64 + end + end, + + [xi.direction.NORTH] = function() -- M.Crit -> Optimal angle = 192 + if angle > 128 and angle <= 192 then + angleFactor = 100 - (192 - angle) * 100 / 64 + elseif angle > 192 and angle < 256 then + angleFactor = 100 - (angle - 192) * 100 / 64 + end + end, + } + + chantBonus = math.floor(baseBonus * fervorFactor * gearFactor * angleFactor) + + return chantBonus +end + ----------------------------------- -- Basic Functions ----------------------------------- @@ -495,8 +577,8 @@ end xi.spells.damage.calculateMagicBonusDiff = function(caster, target, spellId, skillType, spellElement) local magicBonusDiff = 1 -- The variable we want to calculate local casterJob = caster:getMainJob() - local mab = caster:getMod(xi.mod.MATT) - local mabCrit = caster:getMod(xi.mod.MAGIC_CRITHITRATE) + local mab = caster:getMod(xi.mod.MATT) + cardinalChantBonus(caster, target, xi.direction.EAST, spellId, skillType) + local mabCrit = caster:getMod(xi.mod.MAGIC_CRITHITRATE) + cardinalChantBonus(caster, target, xi.direction.NORTH, spellId, skillType) local mDefBarBonus = 0 -- Ninja spell bonuses @@ -843,7 +925,7 @@ xi.spells.damage.calculateIfMagicBurst = function(target, spellElement, skillcha return magicBurst end -xi.spells.damage.calculateIfMagicBurstBonus = function(caster, target, spellId, spellElement) +xi.spells.damage.calculateIfMagicBurstBonus = function(caster, target, spellId, skillType, spellElement) local magicBurstBonus = 1 -- The variable we want to calculate local cappedBonus = caster:getMod(xi.mod.MAGIC_BURST_BONUS_CAPPED) / 100 local uncappedBonus = caster:getMod(xi.mod.MAGIC_BURST_BONUS_UNCAPPED) / 100 @@ -861,8 +943,8 @@ xi.spells.damage.calculateIfMagicBurstBonus = function(caster, target, spellId, -- Cap bonuses from first step at 40% or 0.4 cappedBonus = utils.clamp(cappedBonus, 0, 0.4) - -- BLM Job Point: Magic Burst Damage - uncappedBonus = uncappedBonus + caster:getJobPointLevel(xi.jp.MAGIC_BURST_DMG_BONUS) / 100 + -- BLM Job Point: Magic Burst Damage and GEO cardinal chant. + uncappedBonus = uncappedBonus + caster:getJobPointLevel(xi.jp.MAGIC_BURST_DMG_BONUS) / 100 + cardinalChantBonus(caster, target, xi.direction.WEST, spellId, skillType) -- Get final multiplier magicBurstBonus = magicBurstBonus + cappedBonus + uncappedBonus @@ -944,7 +1026,7 @@ xi.spells.damage.useDamageSpell = function(caster, target, spell) local spellGroup = spell:getSpellGroup() local spellElement = spell:getElement() local statUsed = pTable[spellId][column.STAT_USED] - local bonusMacc = pTable[spellId][column.BONUS_MACC] + local bonusMacc = pTable[spellId][column.BONUS_MACC] + cardinalChantBonus(caster, target, xi.direction.SOUTH, spellId, skillType) -- Calculate damage absobtion or nullification. local nukeAbsorbOrNullify = xi.spells.damage.calculateNukeAbsorbOrNullify(target, spellElement) @@ -977,7 +1059,7 @@ xi.spells.damage.useDamageSpell = function(caster, target, spell) if skillchainCount > 0 then magicBurst = xi.spells.damage.calculateIfMagicBurst(target, spellElement, skillchainCount) - magicBurstBonus = xi.spells.damage.calculateIfMagicBurstBonus(caster, target, spellId, spellElement) + magicBurstBonus = xi.spells.damage.calculateIfMagicBurstBonus(caster, target, spellId, skillType, spellElement) if spellGroup == xi.magic.spellGroup.BLUE then caster:delStatusEffectSilent(xi.effect.BURST_AFFINITY) diff --git a/sql/item_mods.sql b/sql/item_mods.sql index 0e603c17eef..440b9ef0102 100644 --- a/sql/item_mods.sql +++ b/sql/item_mods.sql @@ -47233,7 +47233,7 @@ INSERT INTO `item_mods` VALUES (23061,31,85); -- MEVA: 85 INSERT INTO `item_mods` VALUES (23061,68,46); -- EVA: 46 INSERT INTO `item_mods` VALUES (23061,115,17); -- ELEM: 17 INSERT INTO `item_mods` VALUES (23061,384,600); -- HASTE_GEAR: 600 -INSERT INTO `item_mods` VALUES (23061,959,75); -- CARDINAL_CHANT: 75 +INSERT INTO `item_mods` VALUES (23061,1132,75); -- CARDINAL_CHANT_BONUS: 75 -- Runeists Bandeau +2 INSERT INTO `item_mods` VALUES (23062,1,115); -- DEF: 115 @@ -54083,23 +54083,23 @@ INSERT INTO `item_mods` VALUES (23395,384,600); -- HASTE_GEAR: 600 INSERT INTO `item_mods` VALUES (23395,401,4); -- SUBLIMATION_BONUS: 4 -- Geomancy Galero +3 -INSERT INTO `item_mods` VALUES (23396,1,117); -- DEF: 117 -INSERT INTO `item_mods` VALUES (23396,2,64); -- HP: 64 -INSERT INTO `item_mods` VALUES (23396,5,79); -- MP: 79 -INSERT INTO `item_mods` VALUES (23396,8,26); -- STR: 26 -INSERT INTO `item_mods` VALUES (23396,9,26); -- DEX: 26 -INSERT INTO `item_mods` VALUES (23396,10,26); -- VIT: 26 -INSERT INTO `item_mods` VALUES (23396,11,26); -- AGI: 26 -INSERT INTO `item_mods` VALUES (23396,12,36); -- INT: 36 -INSERT INTO `item_mods` VALUES (23396,13,36); -- MND: 36 -INSERT INTO `item_mods` VALUES (23396,14,31); -- CHR: 31 -INSERT INTO `item_mods` VALUES (23396,29,6); -- MDEF: 6 -INSERT INTO `item_mods` VALUES (23396,30,47); -- MACC: 47 -INSERT INTO `item_mods` VALUES (23396,31,95); -- MEVA: 95 -INSERT INTO `item_mods` VALUES (23396,68,56); -- EVA: 56 -INSERT INTO `item_mods` VALUES (23396,115,19); -- ELEM: 19 -INSERT INTO `item_mods` VALUES (23396,384,600); -- HASTE_GEAR: 600 -INSERT INTO `item_mods` VALUES (23396,959,100); -- CARDINAL_CHANT: 100 +INSERT INTO `item_mods` VALUES (23396,1,117); -- DEF: 117 +INSERT INTO `item_mods` VALUES (23396,2,64); -- HP: 64 +INSERT INTO `item_mods` VALUES (23396,5,79); -- MP: 79 +INSERT INTO `item_mods` VALUES (23396,8,26); -- STR: 26 +INSERT INTO `item_mods` VALUES (23396,9,26); -- DEX: 26 +INSERT INTO `item_mods` VALUES (23396,10,26); -- VIT: 26 +INSERT INTO `item_mods` VALUES (23396,11,26); -- AGI: 26 +INSERT INTO `item_mods` VALUES (23396,12,36); -- INT: 36 +INSERT INTO `item_mods` VALUES (23396,13,36); -- MND: 36 +INSERT INTO `item_mods` VALUES (23396,14,31); -- CHR: 31 +INSERT INTO `item_mods` VALUES (23396,29,6); -- MDEF: 6 +INSERT INTO `item_mods` VALUES (23396,30,47); -- MACC: 47 +INSERT INTO `item_mods` VALUES (23396,31,95); -- MEVA: 95 +INSERT INTO `item_mods` VALUES (23396,68,56); -- EVA: 56 +INSERT INTO `item_mods` VALUES (23396,115,19); -- ELEM: 19 +INSERT INTO `item_mods` VALUES (23396,384,600); -- HASTE_GEAR: 600 +INSERT INTO `item_mods` VALUES (23396,1132,100); -- CARDINAL_CHANT_BONUS: 100 -- Runeists Bandeau +3 INSERT INTO `item_mods` VALUES (23397,1,125); -- DEF: 125 @@ -74059,7 +74059,7 @@ INSERT INTO `item_mods` VALUES (27705,31,75); -- MEVA: 75 INSERT INTO `item_mods` VALUES (27705,68,36); -- EVA: 36 INSERT INTO `item_mods` VALUES (27705,115,15); -- ELEM: 15 INSERT INTO `item_mods` VALUES (27705,384,600); -- HASTE_GEAR: 600 -INSERT INTO `item_mods` VALUES (27705,959,50); -- CARDINAL_CHANT: 50 +INSERT INTO `item_mods` VALUES (27705,1132,50); -- CARDINAL_CHANT_BONUS: 50 -- Baghere Salade INSERT INTO `item_mods` VALUES (27708,1,112); -- DEF: 112 @@ -75087,7 +75087,7 @@ INSERT INTO `item_mods` VALUES (27786,31,51); -- MEVA: 51 INSERT INTO `item_mods` VALUES (27786,68,16); -- EVA: 16 INSERT INTO `item_mods` VALUES (27786,115,15); -- ELEM: 15 INSERT INTO `item_mods` VALUES (27786,384,500); -- HASTE_GEAR: 500 -INSERT INTO `item_mods` VALUES (27786,959,25); -- CARDINAL_CHANT: 25 +INSERT INTO `item_mods` VALUES (27786,1132,25); -- CARDINAL_CHANT_BONUS: 25 -- Ares Cuirass +1 INSERT INTO `item_mods` VALUES (27788,1,81); -- DEF: 81 diff --git a/src/map/modifier.h b/src/map/modifier.h index 5bad9b72a0e..ead871846cd 100644 --- a/src/map/modifier.h +++ b/src/map/modifier.h @@ -716,6 +716,7 @@ enum class Mod // Geo CARDINAL_CHANT = 959, + CARDINAL_CHANT_BONUS = 1132, // Geomancy galero INDI_DURATION = 960, GEOMANCY_BONUS = 961, // Used to increase potency of "Geomancy +" items (only the highest value is counted) WIDENED_COMPASS = 962, @@ -1073,7 +1074,7 @@ enum class Mod // The spares take care of finding the next ID to use so long as we don't forget to list IDs that have been freed up by refactoring. // 570 through 825 used by WS DMG mods these are not spares. // - // SPARE IDs: 1132 and onward + // SPARE IDs: 1133 and onward }; // temporary workaround for using enum class as unordered_map key until compilers support it