From 2e270badf16592b8cbb4f5589e0f64115fb83051 Mon Sep 17 00:00:00 2001 From: Xaver-DaRed Date: Mon, 19 Jan 2026 12:49:18 +0100 Subject: [PATCH 1/3] Reorder `attackType` enum --- scripts/enum/attack_type.lua | 4 ++-- src/map/entities/battleentity.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/enum/attack_type.lua b/scripts/enum/attack_type.lua index 2a968642a6a..51d8ce97f0f 100644 --- a/scripts/enum/attack_type.lua +++ b/scripts/enum/attack_type.lua @@ -10,6 +10,6 @@ xi.attackType = PHYSICAL = 1, MAGICAL = 2, RANGED = 3, - SPECIAL = 4, - BREATH = 5, + BREATH = 4, + SPECIAL = 5, } diff --git a/src/map/entities/battleentity.h b/src/map/entities/battleentity.h index e3955e920d1..4d34b0240b4 100644 --- a/src/map/entities/battleentity.h +++ b/src/map/entities/battleentity.h @@ -202,8 +202,8 @@ enum class ATTACK_TYPE : uint8 PHYSICAL = 1, MAGICAL = 2, RANGED = 3, - SPECIAL = 4, - BREATH = 5, + BREATH = 4, + SPECIAL = 5, }; DECLARE_FORMAT_AS_UNDERLYING(ATTACK_TYPE); From c06c824a4dce92c8b00872c444659433f9df86e1 Mon Sep 17 00:00:00 2001 From: Xaver-DaRed Date: Mon, 19 Jan 2026 14:27:49 +0100 Subject: [PATCH 2/3] Sub-Effect enum cleanup --- scripts/enum/sub_effect.lua | 90 ++++++++++++++++++------------------- scripts/globals/mobs.lua | 4 +- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/scripts/enum/sub_effect.lua b/scripts/enum/sub_effect.lua index 2bf1a191a6c..7bf0d517c01 100644 --- a/scripts/enum/sub_effect.lua +++ b/scripts/enum/sub_effect.lua @@ -8,67 +8,67 @@ xi = xi or {} xi.subEffect = { -- ATTACKS - FIRE_DAMAGE = 1, -- 110000 3 - ICE_DAMAGE = 2, -- 1-01000 5 - WIND_DAMAGE = 3, -- 111000 7 - CHOKE = 3, -- Shares subeffect - EARTH_DAMAGE = 4, -- 1-00100 9 - LIGHTNING_DAMAGE = 5, -- 110100 11 - WATER_DAMAGE = 6, -- 1-01100 13 - LIGHT_DAMAGE = 7, -- 111100 15 - DARKNESS_DAMAGE = 8, -- 1-00010 17 - DISPEL = 8, -- Verified with video of Lockheart Greatsword proc. - SLEEP = 9, -- 110010 19 - POISON = 10, -- 1-01010 21 - ADDLE = 11, -- Verified shared group 1 - AMNESIA = 11, -- Verified shared group 1 - PARALYSIS = 11, -- Verified shared group 1 - BLIND = 12, -- 1-00110 25 + FIRE_DAMAGE = 1, -- 110000 3 + ICE_DAMAGE = 2, -- 1-01000 5 + WIND_DAMAGE = 3, -- 111000 7 + EARTH_DAMAGE = 4, -- 1-00100 9 + LIGHTNING_DAMAGE = 5, -- 110100 11 + WATER_DAMAGE = 6, -- 1-01100 13 + LIGHT_DAMAGE = 7, -- 111100 15 + DARKNESS_DAMAGE = 8, -- 1-00010 17 -- Used by some "Dispel" additional effects. + SLEEP = 9, -- 110010 19 + POISON = 10, -- 1-01010 21 + ADDLE = 11, -- Verified shared group 1 + AMNESIA = 11, -- Verified shared group 1 + PARALYSIS = 11, -- Verified shared group 1 + BLIND = 12, -- 1-00110 25 SILENCE = 13, PETRIFY = 14, PLAGUE = 15, STUN = 16, CURSE = 17, - DEFENSE_DOWN = 18, -- 1-01001 37 - EVASION_DOWN = 18, -- Verified shared group 2 - ATTACK_DOWN = 18, -- Verified shared group 2 - SLOW = 18, -- Verified shared group 2 + DEFENSE_DOWN = 18, -- 1-01001 37 + EVASION_DOWN = 18, -- Verified shared group 2 + ATTACK_DOWN = 18, -- Verified shared group 2 + SLOW = 18, -- Verified shared group 2 DEATH = 19, SHIELD = 20, - HP_DRAIN = 21, -- 1-10101 43 - MP_DRAIN = 22, -- Verified shared group 3 - TP_DRAIN = 22, -- Verified shared group 3 - STATUS_DRAIN = 22, -- Verified shared group 3 + HP_DRAIN = 21, -- 1-10101 43 + MP_DRAIN = 22, -- Verified shared group 3 + TP_DRAIN = 22, -- Verified shared group 3 + STATUS_DRAIN = 22, -- Verified shared group 3 HASTE = 23, -- There are no additional attack effect animations beyond 23. Some effects share subeffect/animations. -- SPIKES - BLAZE_SPIKES = 1, -- 01-1000 6 - ICE_SPIKES = 2, -- 01-0100 10 - DREAD_SPIKES = 3, -- 01-1100 14 - CURSE_SPIKES = 4, -- 01-0010 18 - SHOCK_SPIKES = 5, -- 01-1010 22 - REPRISAL = 6, -- 01-0110 26 - GLINT_SPIKES = 6, -- - GALE_SPIKES = 7, -- Used by enchantment "Cool Breeze" http://www.ffxiah.com/item/22018/ - CLOD_SPIKES = 8, -- - DELUGE_SPIKES = 9, -- - DEATH_SPIKES = 10, -- yes really: http://www.ffxiah.com/item/26944/ + BLAZE_SPIKES = 1, -- 01-1000 6 + ICE_SPIKES = 2, -- 01-0100 10 + DREAD_SPIKES = 3, -- 01-1100 14 + CURSE_SPIKES = 4, -- 01-0010 18 + SHOCK_SPIKES = 5, -- 01-1010 22 + REPRISAL = 6, -- 01-0110 26 + GLINT_SPIKES = 6, -- + GALE_SPIKES = 7, -- Used by enchantment "Cool Breeze" http://www.ffxiah.com/item/22018/ + CLOD_SPIKES = 8, -- + DELUGE_SPIKES = 9, -- + DEATH_SPIKES = 10, -- yes really: http://www.ffxiah.com/item/26944/ + COUNTER = 63, + -- There are no spikes effect animations beyond 63. Some effects share subeffect/animations. -- "Damage Spikes" use the Blaze Spikes animation even though they are different status. -- SKILLCHAINS - NONE = 0, - LIGHT = 1, - DARKNESS = 2, - GRAVITATION = 3, - FRAGMENTATION = 4, - DISTORTION = 5, - FUSION = 6, - COMPRESSION = 7, - LIQUEFACATION = 8, - INDURATION = 9, + NONE = 0, + LIGHT = 1, + DARKNESS = 2, + GRAVITATION = 3, + FRAGMENTATION = 4, + DISTORTION = 5, + FUSION = 6, + COMPRESSION = 7, + LIQUEFACATION = 8, + INDURATION = 9, REVERBERATION = 10, TRANSFIXION = 11, SCISSION = 12, diff --git a/scripts/globals/mobs.lua b/scripts/globals/mobs.lua index 41159530ca3..f27b6585626 100644 --- a/scripts/globals/mobs.lua +++ b/scripts/globals/mobs.lua @@ -567,7 +567,7 @@ local additionalEffects = { chance = 25, ele = xi.element.DARK, - sub = xi.subEffect.DISPEL, + sub = xi.subEffect.DARKNESS_DAMAGE, msg = xi.msg.basic.ADD_EFFECT_DISPEL, applyEffect = false, power = 1, @@ -577,7 +577,7 @@ local additionalEffects = { chance = 10, ele = xi.element.ICE, - sub = xi.subEffect.DISPEL, -- TODO + sub = xi.subEffect.DARKNESS_DAMAGE, msg = xi.msg.basic.ADD_EFFECT_STATUS, applyEffect = true, eff = xi.effect.BIND, From d1c204c4083202c310eb04ba38f7993f8313534b Mon Sep 17 00:00:00 2001 From: Xaver-DaRed Date: Mon, 19 Jan 2026 14:28:44 +0100 Subject: [PATCH 3/3] Create new "additional effect" system (for damage) and apply to endark --- .../combat/action_additional_effect.lua | 130 ++++++++++++++++++ scripts/globals/mobs.lua | 10 -- scripts/specs/types/MobEntity.lua | 2 +- .../zones/King_Ranperres_Tomb/mobs/Vrtra.lua | 11 +- .../Labyrinth_of_Onzozo/mobs/Hellion.lua | 13 +- scripts/zones/Nyzul_Isle/mobs/Hellion.lua | 11 +- .../zones/Promyvion-Vahzl/mobs/Provoker.lua | 28 ++-- scripts/zones/Xarcabard/mobs/Ereshkigal.lua | 11 +- 8 files changed, 181 insertions(+), 35 deletions(-) create mode 100644 scripts/globals/combat/action_additional_effect.lua diff --git a/scripts/globals/combat/action_additional_effect.lua b/scripts/globals/combat/action_additional_effect.lua new file mode 100644 index 00000000000..fb58486942b --- /dev/null +++ b/scripts/globals/combat/action_additional_effect.lua @@ -0,0 +1,130 @@ +----------------------------------- +-- Global file for additional effects. +----------------------------------- +xi = xi or {} +xi.combat = xi.combat or {} +xi.combat.action = xi.combat.action or {} +----------------------------------- + +local damageAnimationDefaults = +{ + [xi.element.NONE ] = { xi.subEffect.LIGHT_DAMAGE }, -- Like Excalibur. + [xi.element.FIRE ] = { xi.subEffect.FIRE_DAMAGE }, + [xi.element.ICE ] = { xi.subEffect.ICE_DAMAGE }, + [xi.element.WIND ] = { xi.subEffect.WIND_DAMAGE }, + [xi.element.EARTH ] = { xi.subEffect.EARTH_DAMAGE }, + [xi.element.THUNDER] = { xi.subEffect.LIGHTNING_DAMAGE }, + [xi.element.WATER ] = { xi.subEffect.WATER_DAMAGE }, + [xi.element.LIGHT ] = { xi.subEffect.LIGHT_DAMAGE }, + [xi.element.DARK ] = { xi.subEffect.DARKNESS_DAMAGE }, +} +----------------------------------- +-- Local functions to ensure defaults are set. +----------------------------------- +local function validateDamageParameters(fedData) + local params = {} + + -- Chance. + params.chance = fedData.chance or 100 -- Default: Always proc. + + -- Action properties. + params.attackType = fedData.attackType or xi.attackType.SPECIAL -- Physical, Magical, Ranged, Breath or Special. + params.physicalElement = fedData.physicalElement or xi.damageType.NONE -- None, H2H, Slashing, Piercing or Blunt. + params.magicalElement = fedData.magicalElement or xi.element.NONE -- None, Fire, Ice, Wind, Earth, Thunder, Water, Light, Dark. + + -- Base damage parameters. + params.basePower = fedData.basePower or 0 + params.actorStat = fedData.actorStat or 0 + params.targetStat = fedData.targetStat or params.actorStat + + -- Multiplier properties. + params.canMAB = fedData.canMAB or false + params.canResist = fedData.canResist or false + + -- Animations and messaging. + params.animation = fedData.animation or damageAnimationDefaults[params.magicalElement] + params.messageDamage = fedData.messageDamage or xi.msg.basic.ADD_EFFECT_DMG + params.messageHeal = fedData.messageHeal or xi.msg.basic.ADD_EFFECT_HEAL + + return params +end + +----------------------------------- +-- Global functions called from "emtity.onAdditionalEffect()" +----------------------------------- +xi.combat.action.executeAdditionalDamage = function(actor, target, fedData) + local params = validateDamageParameters(fedData) + + -- Early return: No proc. + if math.random(1, 100) > params.chance then + return 0, 0, 0 + end + + -- Additional variables. + local isPhysical = params.attackType == xi.attackType.PHYSICAL or false + local isMagical = params.attackType == xi.attackType.MAGICAL or false + local isRanged = params.attackType == xi.attackType.RANGED or false + local isBreath = params.attackType == xi.attackType.BREATH or false + + -- Calculate base power. + local damage = params.basePower + actor:getMod(params.actorStat) - target:getMod(params.targetStat) + + -- Calculate mandatory multipliers. + local multiplierAbsorption = xi.spells.damage.calculateAbsorption(target, params.magicalElement, params.isMagical) + local multiplierNullification = xi.spells.damage.calculateNullification(target, params.magicalElement, isMagical, isBreath) + local multiplierDamageTypeSDT = xi.spells.damage.calculateDamageAdjustment(target, isPhysical, isMagical, isRanged, isBreath) + local multiplierPhysicalElementSDT = 1 -- TODO: Create function for physical elements. + local multiplierMagicalElementSDT = xi.spells.damage.calculateSDT(target, params.magicalElement) + local multiplierElementalStaff = xi.spells.damage.calculateElementalStaffBonus(actor, params.magicalElement) + local multiplierElementalAffinity = xi.spells.damage.calculateElementalAffinityBonus(actor, params.magicalElement) + local multiplierDayWeather = xi.spells.damage.calculateDayAndWeather(actor, params.magicalElement, false) + + -- Calculate optional multipliers. + local multiplierMagicDiff = params.canMAB and xi.spells.damage.calculateMagicBonusDiff(actor, target, 0, 0, params.magicalElement) or 1 + local multiplierResist = params.canResist and xi.combat.magicHitRate.calculateResistRate(actor, target, 0, 0, xi.skillRank.A_PLUS, params.magicalElement, params.actorStat, 0, 0) or 1 + + -- Calculate final damage. + damage = math.floor(damage * multiplierAbsorption) + damage = math.floor(damage * multiplierNullification) + damage = math.floor(damage * multiplierDamageTypeSDT) + damage = math.floor(damage * multiplierPhysicalElementSDT) + damage = math.floor(damage * multiplierMagicalElementSDT) + damage = math.floor(damage * multiplierElementalStaff) + damage = math.floor(damage * multiplierElementalAffinity) + damage = math.floor(damage * multiplierDayWeather) + damage = math.floor(damage * multiplierMagicDiff) + damage = math.floor(damage * multiplierResist) + + -- Phalanx, One for all, Stoneskin. + if damage > 0 then + damage = utils.clamp(utils.handlePhalanx(target, damage), 0, 99999) + damage = utils.clamp(utils.handleOneForAll(target, damage), 0, 99999) + damage = utils.clamp(utils.handleStoneskin(target, damage), -99999, 99999) + end + + -- Apply damage or healing on target. + if damage > 0 then + local actionDamageType = params.physicalElement > 0 and params.physicalElement or xi.damageType.ELEMENTAL + params.magicalElement + + target:takeDamage(damage, actor, params.attackType, actionDamageType) + elseif damage < 0 then + target:addHP(-damage) + end + + -- Return animation displayed, message in chat log and the number that the message should display (if any). + if damage > 0 then + return params.animation, params.messageDamage, damage + elseif damage < 0 then + return params.animation, params.messageHeal, -damage + else + return 0, 0, 0 + end +end + +xi.combat.action.executeAdditionalEffect = function(actor, target, fedData) + return 0, 0, 0 +end + +xi.combat.action.executeAdditionalDispel = function(actor, target, fedData) + return 0, 0, 0 +end diff --git a/scripts/globals/mobs.lua b/scripts/globals/mobs.lua index f27b6585626..adb6ab5f035 100644 --- a/scripts/globals/mobs.lua +++ b/scripts/globals/mobs.lua @@ -295,16 +295,6 @@ local additionalEffects = bonusAbilityParams = { bonusmab = 0, includemab = false }, }, - [xi.mob.ae.ENDARK] = - { - ele = xi.element.DARK, - sub = xi.subEffect.DARKNESS_DAMAGE, - msg = xi.msg.basic.ADD_EFFECT_DMG, - negMsg = xi.msg.basic.ADD_EFFECT_HEAL, - mod = xi.mod.INT, - bonusAbilityParams = { bonusmab = 0, includemab = false }, - }, - [xi.mob.ae.ENFIRE] = { ele = xi.element.FIRE, diff --git a/scripts/specs/types/MobEntity.lua b/scripts/specs/types/MobEntity.lua index 9e8c756facd..0e5e9cc855b 100644 --- a/scripts/specs/types/MobEntity.lua +++ b/scripts/specs/types/MobEntity.lua @@ -29,7 +29,7 @@ ---@field onMobMobskillChoose? fun(mob: CBaseEntity, target: CBaseEntity): integer? ---@field onMobWeaponSkill? fun(target: CBaseEntity, mob: CBaseEntity, mobSkill: CMobSkill, action: CAction): integer? ---@field onMobSkillTarget? fun(target: CBaseEntity, mob: CBaseEntity, mobSkill: CMobSkill): CBaseEntity? ----@field onAdditionalEffect? fun(mob: CBaseEntity, target: CBaseEntity, damage: integer): (integer?, integer?, integer?) +---@field onAdditionalEffect? fun(mob: CBaseEntity, target: CBaseEntity, damage: integer): (any, any, integer?) ---@field onMobSpellChoose? fun(mob: CBaseEntity, target: CBaseEntity, spell: CSpell?): xi.magic.spell|0?, CBaseEntity? ---@field onWeaponskillHit? fun(mob: CBaseEntity, attacker: CBaseEntity, weaponskillId: xi.weaponskill) ---@field onSpikesDamage? fun(mob: CBaseEntity, target: CBaseEntity, damage: integer): (integer?, integer?, integer?) diff --git a/scripts/zones/King_Ranperres_Tomb/mobs/Vrtra.lua b/scripts/zones/King_Ranperres_Tomb/mobs/Vrtra.lua index 5ea6dd0cdf8..e476f00a6ed 100644 --- a/scripts/zones/King_Ranperres_Tomb/mobs/Vrtra.lua +++ b/scripts/zones/King_Ranperres_Tomb/mobs/Vrtra.lua @@ -180,7 +180,16 @@ entity.onMobWeaponSkill = function(target, mob, skill) end entity.onAdditionalEffect = function(mob, target, damage) - return xi.mob.onAddEffect(mob, target, damage, xi.mob.ae.ENDARK, { power = math.random(55, 90), chance = 25 }) + local pTable = + { + chance = 25, + attackType = xi.attackType.MAGICAL, + magicalElement = xi.element.DARK, + power = damage / 2, + actorStat = xi.mod.INT, + } + + return xi.combat.action.executeAdditionalDamage(mob, target, pTable) end entity.onMobDisengage = function(mob) diff --git a/scripts/zones/Labyrinth_of_Onzozo/mobs/Hellion.lua b/scripts/zones/Labyrinth_of_Onzozo/mobs/Hellion.lua index 7da8f7a7995..1353c1fa213 100644 --- a/scripts/zones/Labyrinth_of_Onzozo/mobs/Hellion.lua +++ b/scripts/zones/Labyrinth_of_Onzozo/mobs/Hellion.lua @@ -20,7 +20,7 @@ entity.spawnPoints = entity.phList = { - [ID.mob.HELLION + 2] = ID.mob.HELLION, -- 136.566 14.708 70.077 + [ID.mob.HELLION + 2 ] = ID.mob.HELLION, -- 136.566 14.708 70.077 [ID.mob.HELLION + 15] = ID.mob.HELLION, -- 127.523 14.327 210.258 } @@ -42,7 +42,16 @@ entity.onMobSpawn = function(mob) end entity.onAdditionalEffect = function(mob, target, damage) - return xi.mob.onAddEffect(mob, target, damage, xi.mob.ae.ENDARK) -- This proced every time in caps and needs to be adjusted + local pTable = + { + chance = 100, + attackType = xi.attackType.MAGICAL, + magicalElement = xi.element.DARK, + power = damage / 2, + actorStat = xi.mod.INT, + } + + return xi.combat.action.executeAdditionalDamage(mob, target, pTable) end entity.onMobDeath = function(mob, player, optParams) diff --git a/scripts/zones/Nyzul_Isle/mobs/Hellion.lua b/scripts/zones/Nyzul_Isle/mobs/Hellion.lua index da10a226dad..25c47b41177 100644 --- a/scripts/zones/Nyzul_Isle/mobs/Hellion.lua +++ b/scripts/zones/Nyzul_Isle/mobs/Hellion.lua @@ -11,7 +11,16 @@ entity.onMobInitialize = function(mob) end entity.onAdditionalEffect = function(mob, target, damage) - return xi.mob.onAddEffect(mob, target, math.random(40, 95), xi.mob.ae.ENDARK, { chance = 80 }) + local pTable = + { + chance = 100, + attackType = xi.attackType.MAGICAL, + magicalElement = xi.element.DARK, + power = damage / 2, + actorStat = xi.mod.INT, + } + + return xi.combat.action.executeAdditionalDamage(mob, target, pTable) end entity.onMobDeath = function(mob, player, optParams) diff --git a/scripts/zones/Promyvion-Vahzl/mobs/Provoker.lua b/scripts/zones/Promyvion-Vahzl/mobs/Provoker.lua index 91e5a2ad5bc..6fb498a7545 100644 --- a/scripts/zones/Promyvion-Vahzl/mobs/Provoker.lua +++ b/scripts/zones/Promyvion-Vahzl/mobs/Provoker.lua @@ -5,18 +5,6 @@ ---@type TMobEntity local entity = {} -local eleAddEffects = -{ - [xi.element.FIRE] = xi.mob.ae.ENFIRE, - [xi.element.ICE] = xi.mob.ae.ENBLIZZARD, - [xi.element.WIND] = xi.mob.ae.ENAERO, - [xi.element.EARTH] = xi.mob.ae.ENSTONE, - [xi.element.THUNDER] = xi.mob.ae.ENTHUNDER, - [xi.element.WATER] = xi.mob.ae.ENWATER, - [xi.element.LIGHT] = xi.mob.ae.ENLIGHT, - [xi.element.DARK] = xi.mob.ae.ENDARK, -} - local eleAbsorbActionID = { 603, 604, 624, 404, 625, 626, 627, 307 } local eleAbsorbAnimations = { 432, 433, 434, 435, 436, 437, 438, 439 } @@ -63,14 +51,16 @@ entity.onMobFight = function(mob, target) end entity.onAdditionalEffect = function(mob, target, damage) - local element = mob:getLocalVar('element') - local additionalEffect = eleAddEffects[element] + local pTable = + { + chance = 100, + attackType = xi.attackType.MAGICAL, + magicalElement = mob:getLocalVar('element'), + power = damage / 2, + actorStat = xi.mod.INT, + } - if additionalEffect then - return xi.mob.onAddEffect(mob, target, damage, additionalEffect, { chance = 1000 }) - else - return 0, 0, 0 -- Just in case its somehow not got a variable set - end + return xi.combat.action.executeAdditionalDamage(mob, target, pTable) end entity.onMobDeath = function(mob, player, optParams) diff --git a/scripts/zones/Xarcabard/mobs/Ereshkigal.lua b/scripts/zones/Xarcabard/mobs/Ereshkigal.lua index dac80e758ce..53fa839be39 100644 --- a/scripts/zones/Xarcabard/mobs/Ereshkigal.lua +++ b/scripts/zones/Xarcabard/mobs/Ereshkigal.lua @@ -21,7 +21,16 @@ entity.onMobSpawn = function(mob) end entity.onAdditionalEffect = function(mob, target, damage) - return xi.mob.onAddEffect(mob, target, damage, xi.mob.ae.ENDARK) + local pTable = + { + chance = 100, + attackType = xi.attackType.MAGICAL, + magicalElement = xi.element.DARK, + power = damage / 2, + actorStat = xi.mod.INT, + } + + return xi.combat.action.executeAdditionalDamage(mob, target, pTable) end entity.onMobDeath = function(mob, player, optParams)