diff --git a/packages/core/src/sims/sim_types.ts b/packages/core/src/sims/sim_types.ts index 82be8c04..ef28489c 100644 --- a/packages/core/src/sims/sim_types.ts +++ b/packages/core/src/sims/sim_types.ts @@ -242,7 +242,7 @@ export type DamagingAbility = Readonly<{ autoCrit?: boolean, autoDh?: boolean, dot?: DotInfo, - alternativeScaling?: AlternativeScaling, + alternativeScalings?: AlternativeScaling[], }>; /** @@ -266,9 +266,9 @@ export type ComboBehavior = ComboData['comboBehavior']; /** * Alternate scalings that can exist for abilities, e.g. Living - * Shadow and Bunshin. + * Shadow, Bunshin, SMN pet actions. */ -export type AlternativeScaling = 'Living Shadow'; +export type AlternativeScaling = "Living Shadow Strength Scaling" | "Pet Action Weapon Damage"; export type BaseAbility = Readonly<{ /** @@ -333,9 +333,10 @@ export type BaseAbility = Readonly<{ */ levelModifiers?: LevelModifier[], /** - * If the ability uses alternate scaling, such as Living Shadow. + * If the ability uses alternate scalings, such as Living Shadow Strength + * scaling or using the pet action Weapon Damage multiplier. */ - alternativeScaling?: AlternativeScaling, + alternativeScalings?: AlternativeScaling[], } & (NonDamagingAbility | DamagingAbility)>; diff --git a/packages/core/src/sims/sim_utils.ts b/packages/core/src/sims/sim_utils.ts index c154d056..1e3e62f4 100644 --- a/packages/core/src/sims/sim_utils.ts +++ b/packages/core/src/sims/sim_utils.ts @@ -12,7 +12,7 @@ function dotPotencyToDamage(stats: ComputedSetStats, potency: number, dmgAbility // TODO: are there any dots with auto-crit or auto-dh? const forceDhit = false; const forceCrit = false; - const nonCritDmg = baseDamageFull(modifiedStats, potency, dmgAbility.attackType, forceDhit, true, dmgAbility.alternativeScaling); + const nonCritDmg = baseDamageFull(modifiedStats, potency, dmgAbility.attackType, forceDhit, true, dmgAbility.alternativeScalings); const afterCritDh = applyDhCritFull(nonCritDmg, modifiedStats, forceCrit, forceDhit); return multiplyFixed(afterCritDh, combinedBuffEffects.dmgMod); } @@ -31,7 +31,7 @@ function potencyToDamage(stats: ComputedSetStats, potency: number, dmgAbility: D bonuses.forceDh = true; } }); - const nonCritDmg = baseDamageFull(modifiedStats, potency, dmgAbility.attackType, forceDhit, false, dmgAbility.alternativeScaling); + const nonCritDmg = baseDamageFull(modifiedStats, potency, dmgAbility.attackType, forceDhit, false, dmgAbility.alternativeScalings); const afterCritDh = applyDhCritFull(nonCritDmg, modifiedStats, forceCrit, forceDhit); return multiplyFixed(afterCritDh, combinedBuffEffects.dmgMod); } diff --git a/packages/core/src/sims/tank/drk/drk_actions.ts b/packages/core/src/sims/tank/drk/drk_actions.ts index d74a5e14..f8824360 100644 --- a/packages/core/src/sims/tank/drk/drk_actions.ts +++ b/packages/core/src/sims/tank/drk/drk_actions.ts @@ -1,5 +1,5 @@ import { Darkside, DrkGauge } from "./drk_gauge"; -import { DrkGcdAbility, DrkOgcdAbility, BloodWeaponBuff, DeliriumBuff, ScornBuff, SaltedEarthBuff } from "./drk_types"; +import { DrkGcdAbility, DrkOgcdAbility, BloodWeaponBuff, DeliriumBuff, ScornBuff, SaltedEarthBuff, livingShadowScalings } from "./drk_types"; export const HardSlash: DrkGcdAbility = { type: 'gcd', @@ -282,15 +282,6 @@ export const Shadowbringer: DrkOgcdAbility = { // While Living Shadow abilities are actually Weaponskills in some cases, // they've all been programmed to be abilities so that it doesn't roll GCD. -// -// This shouldn't change anything damage wise. - -// Esteem has the same stats as the player but ignores skill speed, Tank Mastery, and party strength bonus. -// It also substitutes Midlander racial strength bonus regardless of the player's race. -// It has an alternate strength scaling. - -// Esteem updates buffs/debuffs in real time. It is NOT affected by Darkside or by Weakness, -// but mirrors all other statuses on the player (including tincture, AST cards, DNC partner buffs, and Damage Down). export const LivingShadow: DrkOgcdAbility = { type: 'ogcd', name: "Living Shadow", @@ -314,7 +305,7 @@ export const LivingShadow: DrkOgcdAbility = { export const LivingShadowShadowstride: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Shadowstride", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 38512, potency: 0, @@ -324,7 +315,7 @@ export const LivingShadowShadowstride: DrkOgcdAbility = { export const LivingShadowAbyssalDrain: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Abyssal Drain", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 17904, potency: 340, @@ -340,7 +331,7 @@ export const LivingShadowAbyssalDrain: DrkOgcdAbility = { export const LivingShadowShadowbringer: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Shadowbringer", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 25881, potency: 570, @@ -350,7 +341,7 @@ export const LivingShadowShadowbringer: DrkOgcdAbility = { export const LivingShadowEdgeOfShadow: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Edge of Shadow", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 17908, potency: 340, @@ -367,7 +358,7 @@ export const LivingShadowEdgeOfShadow: DrkOgcdAbility = { export const LivingShadowFloodOfShadow: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Flood of Shadow", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 17907, potency: 340, @@ -377,7 +368,7 @@ export const LivingShadowFloodOfShadow: DrkOgcdAbility = { export const LivingShadowBloodspiller: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Bloodspiller", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 17909, potency: 340, @@ -394,7 +385,7 @@ export const LivingShadowBloodspiller: DrkOgcdAbility = { export const LivingShadowCarveAndSpit: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Carve And Spit", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 17915, potency: 340, @@ -410,7 +401,7 @@ export const LivingShadowCarveAndSpit: DrkOgcdAbility = { export const LivingShadowDisesteem: DrkOgcdAbility = { type: 'ogcd', name: "(Living Shadow) Disesteem", - alternativeScaling: "Living Shadow", + alternativeScalings: livingShadowScalings, animationLock: 0, id: 36933, potency: 620, diff --git a/packages/core/src/sims/tank/drk/drk_types.ts b/packages/core/src/sims/tank/drk/drk_types.ts index aedebcbc..be4ed4e7 100644 --- a/packages/core/src/sims/tank/drk/drk_types.ts +++ b/packages/core/src/sims/tank/drk/drk_types.ts @@ -1,4 +1,4 @@ -import { Ability, GcdAbility, OgcdAbility, Buff, BuffController } from "@xivgear/core/sims/sim_types"; +import { Ability, GcdAbility, OgcdAbility, Buff, BuffController, AlternativeScaling } from "@xivgear/core/sims/sim_types"; import { DrkGauge } from "./drk_gauge"; import { removeSelf } from "@xivgear/core/sims/common/utils"; @@ -21,6 +21,11 @@ export type DrkGcdAbility = GcdAbility & DrkAbility; export type DrkOgcdAbility = OgcdAbility & DrkAbility; +// All Living Shadow abilities use the following scalings: +// - Alternate strength scaling (no Tank Mastery, no party bonus, replaced racial bonus) +// - Pet action weapon damage scaling (100 instead of the usual) +export const livingShadowScalings: AlternativeScaling[] = ["Living Shadow Strength Scaling", "Pet Action Weapon Damage"]; + /** DRK ability that costs blood */ export type BloodAbility = DrkAbility & Readonly<{ bloodCost: number; diff --git a/packages/core/src/test/stats/general_stats_tests.ts b/packages/core/src/test/stats/general_stats_tests.ts index 95324ff5..807ee4d3 100644 --- a/packages/core/src/test/stats/general_stats_tests.ts +++ b/packages/core/src/test/stats/general_stats_tests.ts @@ -5,6 +5,7 @@ import {expect} from "chai"; import {applyDhCritFull, baseDamageFull, fl} from "@xivgear/xivmath/xivmath"; import { multiplyFixed } from "@xivgear/xivmath/deviation"; import { HEADLESS_SHEET_PROVIDER } from "../../sheet"; +import { AlternativeScaling } from "../../sims/sim_types"; const level = 100; @@ -851,17 +852,18 @@ describe("Final damage values for known values", () => { expect(stats.mainStatMultiLivingShadow).to.eq(1.04); // These match up with observed values. + const livingShadowScalings: AlternativeScaling[] = ["Living Shadow Strength Scaling", "Pet Action Weapon Damage"]; // 420 potency Living Shadow Attack - const damageBeforeCrit420 = baseDamageFull(stats, 420, 'Ability', false, false, "Living Shadow"); + const damageBeforeCrit420 = baseDamageFull(stats, 420, 'Ability', false, false, livingShadowScalings); expect(damageBeforeCrit420.expected).to.eq(344); // 570 potency Living Shadow Attack - const damageBeforeCrit570 = baseDamageFull(stats, 570, 'Ability', false, false, "Living Shadow"); + const damageBeforeCrit570 = baseDamageFull(stats, 570, 'Ability', false, false, livingShadowScalings); expect(damageBeforeCrit570.expected).to.eq(467); // 620 potency Living Shadow Attack - const damageBeforeCrit620 = baseDamageFull(stats, 620, 'Ability', false, false, "Living Shadow"); + const damageBeforeCrit620 = baseDamageFull(stats, 620, 'Ability', false, false, livingShadowScalings); expect(damageBeforeCrit620.expected).to.eq(508); }); }); diff --git a/packages/xivmath/src/xivmath.ts b/packages/xivmath/src/xivmath.ts index 856d7470..d18774f4 100644 --- a/packages/xivmath/src/xivmath.ts +++ b/packages/xivmath/src/xivmath.ts @@ -290,7 +290,8 @@ function usesCasterDamageFormula(stats: ComputedSetStats, attackType: AttackType /** * Computes base damage. Does not factor in crit/dh RNG nor damage variance. */ -export function baseDamageFull(stats: ComputedSetStats, potency: number, attackType: AttackType = 'Unknown', autoDH: boolean = false, isDot: boolean = false, alternativeScaling: AlternativeScaling = undefined): ValueWithDev { +export function baseDamageFull(stats: ComputedSetStats, potency: number, attackType: AttackType = 'Unknown', autoDH: boolean = false, isDot: boolean = false, alternativeScalings: AlternativeScaling[] = []): ValueWithDev { +//export function baseDamageFull(stats: ComputedSetStats, potency: number, attackType: AttackType = 'Unknown', autoDH: boolean = false, isDot: boolean = false, alternativeScaling: AlternativeScaling = undefined): ValueWithDev { let spdMulti: number; const isAA = attackType === 'Auto-attack'; @@ -313,10 +314,14 @@ export function baseDamageFull(stats: ComputedSetStats, potency: number, attackT // Multiplier from weapon damage. If this is an auto-attack, use the AA multi instead of the pure WD multi. let wdMulti = isAA ? stats.aaMulti : stats.wdMulti; - // Override the multipliers for Living Shadow abilities - if (alternativeScaling === "Living Shadow") { - mainStatMulti = stats.mainStatMultiLivingShadow; - wdMulti = stats.wdMultiPetAction; + // Process alternative scalings for the ability + if (alternativeScalings) { + if (alternativeScalings.find(scaling => scaling === "Living Shadow Strength Scaling")) { + mainStatMulti = stats.mainStatMultiLivingShadow; + } + if (alternativeScalings.find(scaling => scaling === "Pet Action Weapon Damage")) { + wdMulti = stats.wdMultiPetAction; + } } // Det multiplier