diff --git a/spec/System/TestSkills_spec.lua b/spec/System/TestSkills_spec.lua index 4487e65e48..fde845854c 100644 --- a/spec/System/TestSkills_spec.lua +++ b/spec/System/TestSkills_spec.lua @@ -877,4 +877,61 @@ describe("TestSkills", function() -- validate Flame Wall buff appears even when the Wall/default skillPart is active assert.are.equals("Flame Wall", build.calcsTab.calcsOutput.BuffList) end) + + it("Test Ancestral Call - Ancestral Boost calcs", function() + build.itemsTab:CreateDisplayItemFromRaw([[ + New Item + Fanatic Greathammer + Quality: 0 + ]]) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + + build.skillsTab:PasteSocketGroup("Boneshatter 20/0 1\nAncestral Call I 1/0 1") + runCallback("OnFrame") + + assert.True(build.calcsTab.calcsOutput.AvgAncestralCallDamageEffect ~= nil) + assert.True(build.calcsTab.calcsOutput.AncestralCallUptimeRatio ~= nil) + assert.are.equal(3, build.calcsTab.calcsOutput.StrikeTargets) + end) + + it("Test Combined Ancestral Boosts - Ancestral Empowerment and Fist of War", function() + build.itemsTab:CreateDisplayItemFromRaw([[ + New Item + Fanatic Greathammer + Quality: 0 + ]]) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + build.skillsTab:PasteSocketGroup("Leap Slam 20/0 1\nFist of War I 1/0 1") + runCallback("OnFrame") + build.configTab.input.customMods = "every second slam skill you use yourself is ancestrally boosted" + build.configTab:BuildModList() + runCallback("OnFrame") + local fistOfWarOneMaxDmgEffect = build.calcsTab.calcsOutput.MaxAncestralEmpowermentCombinedDamageEffect + + -- test that we are using the calcCombinedAncestralBoost function and the calcSection triggers are correct + assert.True(build.calcsTab.calcsOutput.AncestralEmpowermentCombinedUptimeRatio ~= nil) + assert.True(build.calcsTab.calcsOutput.AncestralEmpowermentUptimeRatio == nil) + assert.True(build.calcsTab.calcsOutput.FistOfWarUptimeRatio == nil) + + newBuild() + build.itemsTab:CreateDisplayItemFromRaw([[ + New Item + Fanatic Greathammer + Quality: 0 + ]]) + build.itemsTab:AddDisplayItem() + runCallback("OnFrame") + build.skillsTab:PasteSocketGroup("Leap Slam 20/0 1\nFist of War III 1/0 1") + runCallback("OnFrame") + build.configTab.input.customMods = "every second slam skill you use yourself is ancestrally boosted" + build.configTab:BuildModList() + runCallback("OnFrame") + + -- test doubled effects of Fist of War III with Ancestral Empowerment + assert.True(fistOfWarOneMaxDmgEffect < build.calcsTab.calcsOutput.MaxAncestralEmpowermentCombinedDamageEffect) + local expectedAverageEffect = 1 + (build.calcsTab.calcsOutput.MaxAncestralEmpowermentCombinedDamageEffect - 1) * build.calcsTab.calcsOutput.AncestralEmpowermentCombinedUptimeRatio / 100 + assert.are.equals(round(expectedAverageEffect, 4), round(build.calcsTab.calcsOutput.AvgAncestralEmpowermentCombinedDamageEffect, 4)) + end) end) \ No newline at end of file diff --git a/src/Data/ModCache.lua b/src/Data/ModCache.lua index 699487d3fc..9b06027582 100644 --- a/src/Data/ModCache.lua +++ b/src/Data/ModCache.lua @@ -1245,7 +1245,7 @@ c["100% increased Parried Debuff Duration"]={{[1]={flags=0,keywordFlags=0,name=" c["100% increased Parry Damage"]={{[1]={[1]={includeTransfigured=true,skillName="Parry",type="SkillName"},flags=0,keywordFlags=0,name="Damage",type="INC",value=100}},nil} c["100% increased Physical Damage"]={{[1]={flags=0,keywordFlags=0,name="PhysicalDamage",type="INC",value=100}},nil} c["100% increased Spell Damage"]={{[1]={flags=2,keywordFlags=0,name="Damage",type="INC",value=100}},nil} -c["100% increased Stun Threshold during Empowered Attacks"]={{[1]={[1]={type="Condition",var="Empowered"},flags=0,keywordFlags=0,name="StunThreshold",type="INC",value=100}},nil} +c["100% increased Stun Threshold during Empowered Attacks"]={{[1]={flags=0,keywordFlags=0,name="StunThreshold",type="INC",value=100}}," during Empowered Attacks "} c["100% increased Stun Threshold for each time you've been Stunned Recently"]={{[1]={[1]={type="Multiplier",var="StunnedRecently"},flags=0,keywordFlags=0,name="StunThreshold",type="INC",value=100}},nil} c["100% increased Thorns damage"]={{[1]={flags=0,keywordFlags=0,name="ThornsDamage",type="INC",value=100}},nil} c["100% increased amount of Life Leeched"]={{[1]={flags=0,keywordFlags=0,name="MaxLifeLeechRate",type="INC",value=100}},nil} @@ -1767,8 +1767,8 @@ c["20% chance for Energy Shield Recharge to start when you Kill an Enemy"]={{[1] c["20% chance for Flasks you use to not consume Charges"]={{[1]={flags=0,keywordFlags=0,name="FlaskChanceNotConsumeCharges",type="BASE",value=20}},nil} c["20% chance for Lightning Skills to Chain an additional time"]={{[1]={flags=0,keywordFlags=128,name="ChainChance",type="BASE",value=20}},nil} c["20% chance to Aggravate Bleeding on targets you Critically Hit with Attacks"]={{}," to Aggravate Bleeding on targets you Critically Hit "} -c["20% chance to Aggravate Bleeding on targets you Hit with Empowered Attacks"]={{}," to Aggravate Bleeding on targets you Hit "} -c["20% chance to Aggravate Bleeding on targets you Hit with Empowered Attacks Empowered Attacks deal 30% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=0,keywordFlags=0,name="Damage",type="BASE",value=20}}," to Aggravate Bleeding on targets you Hit Empowered Attacks deal 30% increased "} +c["20% chance to Aggravate Bleeding on targets you Hit with Empowered Attacks"]={{}," to Aggravate Bleeding on targets you Hit with Empowered Attacks "} +c["20% chance to Aggravate Bleeding on targets you Hit with Empowered Attacks Empowered Attacks deal 30% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="Damage",type="BASE",value=20}}," to Aggravate Bleeding on targets you Hit with Empowered Attacks Empowered Attacks deal 30% increased "} c["20% chance to Avoid Chaos Damage from Hits"]={{[1]={flags=0,keywordFlags=0,name="AvoidChaosDamageChance",type="BASE",value=20}},nil} c["20% chance to Avoid Cold Damage from Hits"]={{[1]={flags=0,keywordFlags=0,name="AvoidColdDamageChance",type="BASE",value=20}},nil} c["20% chance to Avoid Elemental Ailments"]={{[1]={flags=0,keywordFlags=0,name="AvoidElementalAilments",type="BASE",value=20}},nil} @@ -1875,7 +1875,7 @@ c["20% increased Flammability Magnitude"]={{[1]={flags=0,keywordFlags=0,name="En c["20% increased Flask Effect Duration"]={{[1]={flags=0,keywordFlags=0,name="FlaskDuration",type="INC",value=20}},nil} c["20% increased Flask and Charm Charges gained"]={{[1]={flags=0,keywordFlags=0,name="CharmChargesGained",type="INC",value=20},[2]={flags=0,keywordFlags=0,name="FlaskChargesGained",type="INC",value=20}},nil} c["20% increased Freeze Buildup"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeBuildup",type="INC",value=20}},nil} -c["20% increased Freeze Buildup with Empowered Attacks"]={{[1]={[1]={type="Condition",var="Empowered"},flags=0,keywordFlags=0,name="EnemyFreezeBuildup",type="INC",value=20}},nil} +c["20% increased Freeze Buildup with Empowered Attacks"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeBuildup",type="INC",value=20}}," with Empowered Attacks "} c["20% increased Freeze Buildup with Quarterstaves"]={{[1]={flags=2097156,keywordFlags=0,name="EnemyFreezeBuildup",type="INC",value=20}},nil} c["20% increased Freeze Duration on Enemies"]={{[1]={flags=0,keywordFlags=0,name="EnemyFreezeDuration",type="INC",value=20}},nil} c["20% increased Frenzy Charge Duration"]={{[1]={flags=0,keywordFlags=0,name="FrenzyChargesDuration",type="INC",value=20}},nil} @@ -2350,7 +2350,7 @@ c["30% increased Accuracy Rating against Rare or Unique Enemies"]={{[1]={[1]={ac c["30% increased Accuracy Rating at Close Range"]={{[1]={[1]={type="Condition",var="AtCloseRange"},flags=0,keywordFlags=0,name="AccuracyVsEnemy",type="INC",value=30}},nil} c["30% increased Accuracy Rating while moving"]={{[1]={[1]={type="Condition",var="Moving"},flags=0,keywordFlags=0,name="Accuracy",type="INC",value=30}},nil} c["30% increased Archon Buff duration"]={{[1]={flags=0,keywordFlags=0,name="Duration",type="INC",value=30}}," Archon Buff "} -c["30% increased Area of Effect of Ancestrally Boosted Attacks"]={{[1]={flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=30}}," of Ancestrally Boosted Attacks "} +c["30% increased Area of Effect of Ancestrally Boosted Attacks"]={{[1]={flags=1,keywordFlags=0,name="AncestralBoostAreaOfEffect",type="INC",value=30}},nil} c["30% increased Armour"]={{[1]={flags=0,keywordFlags=0,name="Armour",type="INC",value=30}},nil} c["30% increased Armour while Bleeding"]={{[1]={[1]={type="Condition",var="Bleeding"},flags=0,keywordFlags=0,name="Armour",type="INC",value=30}},nil} c["30% increased Armour while Surrounded"]={{[1]={[1]={type="Condition",var="Surrounded"},flags=0,keywordFlags=0,name="Armour",type="INC",value=30}},nil} @@ -2589,8 +2589,7 @@ c["4% chance that if you would gain Rage on Hit, you instead gain up to your max c["4% increased Area of Effect"]={{[1]={flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=4}},nil} c["4% increased Area of Effect for Attacks"]={{[1]={flags=1,keywordFlags=0,name="AreaOfEffect",type="INC",value=4}},nil} c["4% increased Area of Effect for Attacks per Enemy you've Ignited in the last 8 seconds, up to 40%"]={{[1]={flags=1,keywordFlags=0,name="AreaOfEffect",type="INC",value=4}}," per Enemy you've Ignited in the last 8 seconds, up to 40% "} -c["4% increased Area of Effect of Ancestrally Boosted Attacks"]={{[1]={flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=4}}," of Ancestrally Boosted Attacks "} -c["4% increased Area of Effect of Ancestrally Boosted Attacks Ancestrally Boosted Attacks deal 8% increased Damage"]={{[1]={flags=0,keywordFlags=0,name="AreaOfEffect",type="INC",value=4}}," of Ancestrally Boosted Attacks Ancestrally Boosted Attacks deal 8% increased Damage "} +c["4% increased Area of Effect of Ancestrally Boosted Attacks"]={{[1]={flags=1,keywordFlags=0,name="AncestralBoostAreaOfEffect",type="INC",value=4}},nil} c["4% increased Attack Damage per 75 Item Armour and Evasion Rating on Equipped Shield"]={{[1]={[1]={div=75,statList={[1]="ArmourOnWeapon 2",[2]="EvasionOnWeapon 2"},type="PerStat"},[2]={type="Condition",var="UsingShield"},flags=1,keywordFlags=0,name="Damage",type="INC",value=4}},nil} c["4% increased Attack Speed"]={{[1]={flags=1,keywordFlags=0,name="Speed",type="INC",value=4}},nil} c["4% increased Attack Speed while a Rare or Unique Enemy is in your Presence"]={{[1]={[1]={actor="enemy",type="ActorCondition",varList={[1]="NearbyRareOrUniqueEnemy",[2]="RareOrUnique"}},flags=1,keywordFlags=0,name="Speed",type="INC",value=4}},nil} @@ -4379,10 +4378,9 @@ c["Always Hits"]={{[1]={[1]={type="Condition",var="{Hand}Attack"},flags=0,keywor c["Always Poison on Hit with this weapon"]={{[1]={[1]={type="Condition",var="{Hand}Attack"},[2]={neg=true,skillType=167,type="SkillType"},flags=8192,keywordFlags=0,name="PoisonChance",type="OVERRIDE",value=100}},nil} c["Always deals Critical Hits against Heavy Stunned Enemies"]={{[1]={[1]={actor="enemy",type="ActorCondition",var="HeavyStunned"},[2]={type="Condition",var="{Hand}Attack"},flags=0,keywordFlags=0,name="CritChance",type="OVERRIDE",value=100}},nil} c["Ancestral Bond"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Ancestral Bond"}},nil} -c["Ancestrally Boosted Attacks deal 16% increased Damage"]={nil,"Ancestrally Boosted Attacks deal 16% increased Damage "} -c["Ancestrally Boosted Attacks deal 30% increased Damage"]={nil,"Ancestrally Boosted Attacks deal 30% increased Damage "} -c["Ancestrally Boosted Attacks deal 30% increased Damage On Heavy Stunning a Rare or Unique Enemy, your next Attack within 4 seconds will be Ancestrally Boosted"]={nil,"Ancestrally Boosted Attacks deal 30% increased Damage On Heavy Stunning a Rare or Unique Enemy, your next Attack within 4 seconds will be Ancestrally Boosted "} -c["Ancestrally Boosted Attacks deal 8% increased Damage"]={nil,"Ancestrally Boosted Attacks deal 8% increased Damage "} +c["Ancestrally Boosted Attacks deal 16% increased Damage"]={{[1]={flags=1,keywordFlags=0,name="AncestralBoostDamage",type="INC",value=16}},nil} +c["Ancestrally Boosted Attacks deal 30% increased Damage"]={{[1]={flags=1,keywordFlags=0,name="AncestralBoostDamage",type="INC",value=30}},nil} +c["Ancestrally Boosted Attacks deal 8% increased Damage"]={{[1]={flags=1,keywordFlags=0,name="AncestralBoostDamage",type="INC",value=8}},nil} c["Any number of Poisons from this Weapon can affect a target at the same time"]={{[1]={flags=0,keywordFlags=0,name="PoisonCanStack",type="FLAG",value=true},[2]={[1]={type="Condition",var="{Hand}Attack"},[2]={neg=true,skillType=167,type="SkillType"},flags=0,keywordFlags=0,name="PoisonStacks",type="OVERRIDE",value=math.huge}},nil} c["Apply 10 Critical Weakness to Enemies when Consuming a Mark on them"]={{[1]={flags=0,keywordFlags=0,name="ApplyCriticalWeakness",type="FLAG",value=true}},nil} c["Apply Debilitate to Enemies 3 Metres in front of you while your Shield is raised"]={nil,"Apply Debilitate to Enemies 3 Metres in front of you while your Shield is raised "} @@ -4606,7 +4604,7 @@ c["Chance is doubled against Undead and Demons"]={{},"Chance against Undead and c["Chance to Block Damage is Lucky"]={{[1]={flags=0,keywordFlags=0,name="BlockChanceIsLucky",type="FLAG",value=true}},nil} c["Chance to Deflect is Lucky"]={{[1]={flags=0,keywordFlags=0,name="DeflectIsLucky",type="FLAG",value=true}},nil} c["Chance to Evade is Unlucky"]={{[1]={flags=0,keywordFlags=0,name="UnluckyEvade",type="FLAG",value=true}},nil} -c["Chance to Hit with Attacks can exceed 100%"]={{[1]={[1]={skillType=1,type="Skilltype"},flags=0,keywordFlags=0,name="Condition:HitChanceCanExceed100",type="FLAG",value=true}},nil} +c["Chance to Hit with Attacks can exceed 100%"]={{[1]={[1]={skillType=1,type="SkillType"},flags=0,keywordFlags=0,name="Condition:HitChanceCanExceed100",type="FLAG",value=true}},nil} c["Channelling Skills deal 12% increased Damage"]={{[1]={[1]={skillType=48,type="SkillType"},flags=0,keywordFlags=0,name="Damage",type="INC",value=12}},nil} c["Channelling Skills deal 20% increased Damage"]={{[1]={[1]={skillType=48,type="SkillType"},flags=0,keywordFlags=0,name="Damage",type="INC",value=20}},nil} c["Channelling Skills deal 25% increased Damage"]={{[1]={[1]={skillType=48,type="SkillType"},flags=0,keywordFlags=0,name="Damage",type="INC",value=25}},nil} @@ -4804,16 +4802,18 @@ c["Elemental Archon does not expire while on High Infernal Flame"]={nil,"Element c["Elemental Archon does not expire while on High Infernal Flame Lose Elemental Archon on reaching maximum Infernal Flame"]={nil,"Elemental Archon does not expire while on High Infernal Flame Lose Elemental Archon on reaching maximum Infernal Flame "} c["Elemental Damage also Contributes to Bleeding Magnitude"]={{[1]={flags=0,keywordFlags=0,name="FireCanBleed",type="FLAG",value=true},[2]={flags=0,keywordFlags=0,name="ColdCanBleed",type="FLAG",value=true},[3]={flags=0,keywordFlags=0,name="LightningCanBleed",type="FLAG",value=true}},nil} c["Elemental Equilibrium"]={{[1]={flags=0,keywordFlags=0,name="Keystone",type="LIST",value="Elemental Equilibrium"}},nil} -c["Empowered Attacks Gain 15% of Physical Damage as Extra Fire damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="PhysicalDamageGainAsFire",type="BASE",value=15}},nil} -c["Empowered Attacks Gain 16% of Damage as Extra Cold Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="DamageGainAsCold",type="BASE",value=16}},nil} -c["Empowered Attacks deal 10% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="Damage",type="INC",value=10}},nil} -c["Empowered Attacks deal 16% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="Damage",type="INC",value=16}},nil} -c["Empowered Attacks deal 2% increased damage per 10 Tribute"]={{[1]={[1]={type="Condition",var="Empowered"},[2]={actor="parent",div=10,stat="Tribute",type="PerStat"},flags=1,keywordFlags=0,name="Damage",type="INC",value=2}},nil} -c["Empowered Attacks deal 20% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="Damage",type="INC",value=20}},nil} -c["Empowered Attacks deal 30% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="Damage",type="INC",value=30}},nil} -c["Empowered Attacks deal 50% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="Damage",type="INC",value=50}},nil} -c["Empowered Attacks deal 8% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="Damage",type="INC",value=8}},nil} -c["Empowered Attacks have 50% increased Stun Buildup"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="EnemyHeavyStunBuildup",type="INC",value=50}},nil} +c["Empowered Attacks Gain 15% of Physical Damage as Extra Fire damage"]={nil,"Empowered Attacks Gain 15% of Physical Damage as Extra Fire damage "} +c["Empowered Attacks Gain 16% of Damage as Extra Cold Damage"]={nil,"Empowered Attacks Gain 16% of Damage as Extra Cold Damage "} +c["Empowered Attacks deal 10% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="EmpoweredIncrease",type="INC",value=10}},nil} +c["Empowered Attacks deal 16% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="EmpoweredIncrease",type="INC",value=16}},nil} +c["Empowered Attacks deal 2% increased damage per 10 Tribute"]={nil,"Empowered Attacks deal 2% increased damage per 10 Tribute "} +c["Empowered Attacks deal 2% increased damage per 10 Tribute 4% increased Warcry Speed per 25 Tribute"]={nil,"Empowered Attacks deal 2% increased damage per 10 Tribute 4% increased Warcry Speed per 25 Tribute "} +c["Empowered Attacks deal 20% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="EmpoweredIncrease",type="INC",value=20}},nil} +c["Empowered Attacks deal 30% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="EmpoweredIncrease",type="INC",value=30}},nil} +c["Empowered Attacks deal 50% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="EmpoweredIncrease",type="INC",value=50}},nil} +c["Empowered Attacks deal 8% increased Damage"]={{[1]={[1]={type="Condition",var="Empowered"},flags=1,keywordFlags=0,name="EmpoweredIncrease",type="INC",value=8}},nil} +c["Empowered Attacks have 50% increased Stun Buildup"]={nil,"Empowered Attacks have 50% increased Stun Buildup "} +c["Empowered Attacks have 50% increased Stun Buildup 100% increased Stun Threshold during Empowered Attacks"]={nil,"Empowered Attacks have 50% increased Stun Buildup 100% increased Stun Threshold during Empowered Attacks "} c["Enemies Blinded by you have 15% reduced Critical Hit Chance"]={{[1]={flags=0,keywordFlags=0,name="EnemyModifier",type="LIST",value={mod={[1]={type="Condition",var="Blinded"},flags=0,keywordFlags=0,name="CritChance",type="INC",value=-15}}}},nil} c["Enemies Blinded by you have 50% reduced Critical Hit Chance"]={{[1]={flags=0,keywordFlags=0,name="EnemyModifier",type="LIST",value={mod={[1]={type="Condition",var="Blinded"},flags=0,keywordFlags=0,name="CritChance",type="INC",value=-50}}}},nil} c["Enemies Chilled by your Hits can be Shattered as though Frozen"]={nil,"your Hits can be Shattered as though Frozen "} @@ -4925,7 +4925,7 @@ c["Every Rage also grants 2% increased Stun Threshold"]={{[1]={[1]={type="Multip c["Every Rage also grants you 1% increased Minion Attack Speed"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={[1]={actor="player",type="Multiplier",var="RageEffect"},flags=1,keywordFlags=0,name="Speed",type="INC",value=1}}}},nil} c["Every Rage also grants you 1% increased Minion Damage"]={{[1]={flags=0,keywordFlags=0,name="MinionModifier",type="LIST",value={mod={[1]={actor="player",type="Multiplier",var="RageEffect"},flags=0,keywordFlags=0,name="Damage",type="INC",value=1}}}},nil} c["Every Third Slam skill that doesn't create Fissures which you use yourself causes 3 additional Aftershocks ahead and to each side of the initial area"]={nil,"Every Third Slam skill that doesn't create Fissures which you use yourself causes 3 additional Aftershocks ahead and to each side of the initial area "} -c["Every second Slam Skill you use yourself is Ancestrally Boosted"]={nil,"Every second Slam Skill you use yourself is Ancestrally Boosted "} +c["Every second Slam Skill you use yourself is Ancestrally Boosted"]={{[1]={[1]={skillType=93,type="SkillType"},flags=0,keywordFlags=0,name="AncestralEmpowerment",type="FLAG",value=true},[2]={[1]={skillType=93,type="SkillType"},flags=0,keywordFlags=0,name="Condition:AncestrallyBoosted",type="FLAG",value=true}},nil} c["Every second, inflicts Critical Weakness on enemies in your Presence for 1 second"]={{[1]={flags=0,keywordFlags=0,name="ApplyCriticalWeakness",type="FLAG",value=true}},nil} c["Every second, inflicts Critical Weakness on enemies in your Presence for 15 seconds"]={{[1]={flags=0,keywordFlags=0,name="ApplyCriticalWeakness",type="FLAG",value=true}},nil} c["Every second, inflicts Critical Weakness on enemies in your Presence for 18 seconds"]={{[1]={flags=0,keywordFlags=0,name="ApplyCriticalWeakness",type="FLAG",value=true}},nil} diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index 16aaadbb2b..035f249962 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -586,6 +586,12 @@ return { ["warcry_speed_+%"] = { mod("WarcrySpeed", "INC", nil, 0, KeywordFlag.Warcry), }, +["warcry_empowers_per_X_monster_power_mp_cap"] = { + mod("WarcryPowerCap", "BASE", nil), +}, +["warcry_empowers_per_X_monster_power"] = { + mod("WarcryPowerPer", "BASE", nil), +}, ["display_this_skill_cooldown_does_not_recover_during_buff"] = { flag("NoCooldownRecoveryInDuration"), }, @@ -2586,6 +2592,32 @@ return { ["slam_aftershock_chance_%"] = { mod("AftershockChance", "BASE", nil) }, +-- Final Strike +["final_strike_is_ancestrally_boosted"] = { + flag("Condition:AncestrallyBoosted", { type = "Condition", var = "FinalStrike" }), + flag("MaxBoostedUptimeRatio", { type = "Condition", var = "FinalStrike"}), +}, +["is_final_strike"] = { + flag("Condition:FinalStrike"), +}, +["ancestral_slam_interval_duration"] = { + { + mod("FistOfWarCooldown", "BASE", nil), + div = 1000, + }, + flag("Condition:AncestrallyBoosted"), +}, +["ancestral_call_spirit_strike_interval_ms"] = { + { + mod("AncestralCallCooldown", "BASE", nil), + div = 1000, + }, + flag("Condition:AncestrallyBoosted"), +}, +["double_ancestral_boost_effect"] = { + mod("Multiplier:AncestralBoostEffect", "BASE", nil), + value = 1, +}, -- Curse ["curse_effect_+%"] = { mod("CurseEffect", "INC", nil), diff --git a/src/Data/Skills/act_str.lua b/src/Data/Skills/act_str.lua index 63ec4751df..9f6c4764ea 100644 --- a/src/Data/Skills/act_str.lua +++ b/src/Data/Skills/act_str.lua @@ -7509,6 +7509,11 @@ skills["InfernalCryPlayer"] = { label = "Infernal Cry", incrementalEffectiveness = 0.054999999701977, statDescriptionScope = "infernal_cry", + statMap = { + ["infernal_cry_exerted_attack_all_damage_%_to_gain_as_fire_%"] = { + mod("InfernalExtraFireDamageMultiplier", "BASE", nil), + }, + }, baseFlags = { warcry = true, area = true, diff --git a/src/Data/Skills/sup_int.lua b/src/Data/Skills/sup_int.lua index d54054964c..6fcf4cdc35 100644 --- a/src/Data/Skills/sup_int.lua +++ b/src/Data/Skills/sup_int.lua @@ -2586,6 +2586,11 @@ skills["SupportCrescendoPlayerTwo"] = { label = "Crescendo II", incrementalEffectiveness = 0.054999999701977, statDescriptionScope = "gem_stat_descriptions", + statMap = { + ["support_crescendo_non_final_strike_attack_speed_+%_final"] = { + mod("Speed", "MORE", nil, ModFlag.Attack, 0, { type = "Condition", var = "FinalStrike", neg = true}) + }, + }, baseFlags = { }, constantStats = { diff --git a/src/Data/Skills/sup_str.lua b/src/Data/Skills/sup_str.lua index 237f5dc7a1..c3d33482a9 100644 --- a/src/Data/Skills/sup_str.lua +++ b/src/Data/Skills/sup_str.lua @@ -3398,18 +3398,8 @@ skills["FistOfWarSupportPlayer"] = { label = "Fist of War I", incrementalEffectiveness = 0.092720001935959, statDescriptionScope = "gem_stat_descriptions", - statMap = { - ["ancestral_slam_interval_duration"] = { - mod("FistOfWarCooldown", "BASE", nil), - div = 1000, - }, - }, baseFlags = { }, - baseMods = { - mod("FistOfWarDamageMultiplier", "BASE", 30), - mod("FistOfWarMOREAoE", "BASE", 25), - }, constantStats = { { "support_ancestral_slam_big_hit_max_count", 1 }, { "ancestral_slam_interval_duration", 6000 }, @@ -3439,18 +3429,8 @@ skills["FistOfWarSupportPlayerTwo"] = { label = "Fist of War II", incrementalEffectiveness = 0.092720001935959, statDescriptionScope = "gem_stat_descriptions", - statMap = { - ["ancestral_slam_interval_duration"] = { - mod("FistOfWarCooldown", "BASE", nil), - div = 1000, - }, - }, baseFlags = { }, - baseMods = { - mod("FistOfWarDamageMultiplier", "BASE", 30), - mod("FistOfWarMOREAoE", "BASE", 25), - }, constantStats = { { "support_ancestral_slam_big_hit_max_count", 1 }, { "ancestral_slam_interval_duration", 4000 }, @@ -3480,21 +3460,8 @@ skills["FistOfWarSupportPlayerThree"] = { label = "Fist of War III", incrementalEffectiveness = 0.092720001935959, statDescriptionScope = "gem_stat_descriptions", - statMap = { - ["ancestral_slam_interval_duration"] = { - mod("FistOfWarCooldown", "BASE", nil), - div = 1000, - }, - ["double_ancestral_boost_effect"] = { - -- Accounted for in base mod - }, - }, baseFlags = { }, - baseMods = { - mod("FistOfWarDamageMultiplier", "BASE", 60), - mod("FistOfWarMOREAoE", "BASE", 50), - }, constantStats = { { "support_ancestral_slam_big_hit_max_count", 1 }, { "ancestral_slam_interval_duration", 14000 }, diff --git a/src/Export/Skills/act_str.txt b/src/Export/Skills/act_str.txt index e695647b90..67a9b02814 100644 --- a/src/Export/Skills/act_str.txt +++ b/src/Export/Skills/act_str.txt @@ -486,6 +486,11 @@ statMap = { #skill InfernalCryPlayer #set InfernalCryPlayer #flags warcry area duration +statMap = { + ["infernal_cry_exerted_attack_all_damage_%_to_gain_as_fire_%"] = { + mod("InfernalExtraFireDamageMultiplier", "BASE", nil), + }, +}, #mods #skillEnd diff --git a/src/Export/Skills/sup_int.txt b/src/Export/Skills/sup_int.txt index 36c3ec254e..65b0c0c5c1 100644 --- a/src/Export/Skills/sup_int.txt +++ b/src/Export/Skills/sup_int.txt @@ -440,6 +440,11 @@ statMap = { #skill SupportCrescendoPlayerTwo #set SupportCrescendoPlayerTwo +statMap = { + ["support_crescendo_non_final_strike_attack_speed_+%_final"] = { + mod("Speed", "MORE", nil, ModFlag.Attack, 0, { type = "Condition", var = "FinalStrike", neg = true}) + }, +}, #mods #skillEnd diff --git a/src/Export/Skills/sup_str.txt b/src/Export/Skills/sup_str.txt index 0d27c92847..3094573d5b 100644 --- a/src/Export/Skills/sup_str.txt +++ b/src/Export/Skills/sup_str.txt @@ -743,43 +743,16 @@ statMap = { #skill FistOfWarSupportPlayer #set FistOfWarSupportPlayer -statMap = { - ["ancestral_slam_interval_duration"] = { - mod("FistOfWarCooldown", "BASE", nil), - div = 1000, - }, -}, -#baseMod mod("FistOfWarDamageMultiplier", "BASE", 30) -#baseMod mod("FistOfWarMOREAoE", "BASE", 25) #mods #skillEnd #skill FistOfWarSupportPlayerTwo #set FistOfWarSupportPlayerTwo -statMap = { - ["ancestral_slam_interval_duration"] = { - mod("FistOfWarCooldown", "BASE", nil), - div = 1000, - }, -}, -#baseMod mod("FistOfWarDamageMultiplier", "BASE", 30) -#baseMod mod("FistOfWarMOREAoE", "BASE", 25) #mods #skillEnd #skill FistOfWarSupportPlayerThree #set FistOfWarSupportPlayerThree -statMap = { - ["ancestral_slam_interval_duration"] = { - mod("FistOfWarCooldown", "BASE", nil), - div = 1000, - }, - ["double_ancestral_boost_effect"] = { - -- Accounted for in base mod - }, -}, -#baseMod mod("FistOfWarDamageMultiplier", "BASE", 60) -#baseMod mod("FistOfWarMOREAoE", "BASE", 50) #mods #skillEnd diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index fd2a91bc2e..ce73d8328b 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -2274,6 +2274,33 @@ function calcs.offence(env, actor, activeSkill) return totalConv, total end + local function buildGainTable() + for _, damageType in ipairs(dmgTypeList) do + activeSkill.gainTable[damageType] = {} + for _, toType in ipairs(dmgTypeList) do + local globalGain = m_max(skillModList:Sum("BASE", skillCfg, + "DamageAs"..toType, + "DamageGainAs"..toType, + damageType.."DamageAs"..toType, + damageType.."DamageGainAs"..toType, + isElemental[damageType] and "ElementalDamageAs"..toType or nil, + isElemental[damageType] and "ElementalDamageGainAs"..toType or nil, + damageType ~= "Chaos" and "NonChaosDamageAs"..toType or nil, + damageType ~= "Chaos" and "NonChaosDamageGainAs"..toType or nil), 0) + local skillGain = m_max(skillModList:Sum("BASE", skillCfg, + "SkillDamageGainAs"..toType, + "Skill"..damageType.."DamageGainAs"..toType, + isElemental[damageType] and "SkillElementalDamageGainAs"..toType or nil, + damageType ~= "Chaos" and "SkillNonChaosDamageGainAs"..toType or nil), 0) + if skillModList:Flag(skillCfg, "DamageGainIsOnlyCold") and toType ~= "Cold" then + activeSkill.gainTable[damageType]["Cold"] = (activeSkill.gainTable[damageType]["Cold"] or 0) + (globalGain + skillGain) / 100 + else + activeSkill.gainTable[damageType][toType] = (activeSkill.gainTable[damageType][toType] or 0) + (globalGain + skillGain) / 100 + end + end + end + end + -- First step: Process skill conversion for _, damageType in ipairs(dmgTypeList) do local skillConv, skillTotal = processDamageConversion(damageType, true) @@ -2331,29 +2358,6 @@ function calcs.offence(env, actor, activeSkill) end end - -- Handle gains - activeSkill.gainTable[damageType] = {} - for _, toType in ipairs(dmgTypeList) do - local globalGain = m_max(skillModList:Sum("BASE", skillCfg, - "DamageAs"..toType, - "DamageGainAs"..toType, - damageType.."DamageAs"..toType, - damageType.."DamageGainAs"..toType, - isElemental[damageType] and "ElementalDamageAs"..toType or nil, - isElemental[damageType] and "ElementalDamageGainAs"..toType or nil, - damageType ~= "Chaos" and "NonChaosDamageAs"..toType or nil, - damageType ~= "Chaos" and "NonChaosDamageGainAs"..toType or nil), 0) - local skillGain = m_max(skillModList:Sum("BASE", skillCfg, - "SkillDamageGainAs"..toType, - "Skill"..damageType.."DamageGainAs"..toType, - isElemental[damageType] and "SkillElementalDamageGainAs"..toType or nil, - damageType ~= "Chaos" and "SkillNonChaosDamageGainAs"..toType or nil), 0) - if skillModList:Flag(skillCfg, "DamageGainIsOnlyCold") and toType ~= "Cold" then - activeSkill.gainTable[damageType]["Cold"] = (activeSkill.gainTable[damageType]["Cold"] or 0) + (globalGain + skillGain) / 100 - else - activeSkill.gainTable[damageType][toType] = (activeSkill.gainTable[damageType][toType] or 0) + (globalGain + skillGain) / 100 - end - end end -- Configure damage passes @@ -3153,7 +3157,8 @@ function calcs.offence(env, actor, activeSkill) -- Exerted Attack members local exertedDoubleDamage = env.modDB:Sum("BASE", cfg, "ExertDoubleDamageChance") - local exertingWarcryCount = env.modDB:Sum("BASE", nil, "Multiplier:ExertingWarcryCount") + local exertingWarcryCount = env.modDB:Sum("BASE", nil, "Multiplier:EmpoweringWarcryCount") + local warcryPower = modDB:Override(nil, "WarcryPower") or m_max((modDB:Sum("BASE", nil, "WarcryPower") or 0) * (1 + (modDB:Sum("INC", nil, "WarcryPower") or 0) / 100), (modDB:Sum("BASE", nil, "MinimumWarcryPower") or 0)) globalOutput.OffensiveWarcryEffect = 1 globalOutput.MaxOffensiveWarcryEffect = 1 globalOutput.TheoreticalOffensiveWarcryEffect = 1 @@ -3188,28 +3193,34 @@ function calcs.offence(env, actor, activeSkill) t_insert(globalBreakdown.AncestralUpTimeRatio, s_format("= %d%%", globalOutput.AncestralUpTimeRatio)) end globalOutput.AncestralCryCalculated = true - elseif value.activeEffect.grantedEffect.name == "Infernal Cry" and not globalOutput.InfernalCryCalculated then + elseif value.activeEffect.grantedEffect.name == "Infernal Cry" and activeSkill.skillTypes[SkillType.Melee] and not globalOutput.InfernalCryCalculated then + globalOutput.CreateWarcryOffensiveCalcSection = true globalOutput.InfernalCryDuration = calcSkillDuration(value.skillModList, value.skillCfg, value.skillData, env, enemyDB) globalOutput.InfernalCryCooldown = calcSkillCooldown(value.skillModList, value.skillCfg, value.skillData) globalOutput.InfernalCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, value.skillData, actor) - if activeSkill.skillTypes[SkillType.Melee] then - globalOutput.InfernalExertsCount = env.modDB:Sum("BASE", nil, "NumInfernalExerts") or 0 - local baseUptimeRatio = m_min((globalOutput.InfernalExertsCount / globalOutput.Speed) / (globalOutput.InfernalCryCooldown + globalOutput.InfernalCryCastTime), 1) * 100 - local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") - globalOutput.InfernalUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) - globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.InfernalUpTimeRatio - if globalBreakdown then - globalBreakdown.InfernalUpTimeRatio = { } - t_insert(globalBreakdown.InfernalUpTimeRatio, s_format("(%d ^8(number of exerts)", globalOutput.InfernalExertsCount)) - t_insert(globalBreakdown.InfernalUpTimeRatio, s_format("/ %.2f) ^8(attacks per second)", globalOutput.Speed)) - if globalOutput.InfernalCryCastTime > 0 then - t_insert(globalBreakdown.InfernalUpTimeRatio, s_format("/ (%.2f ^8(warcry cooldown)", globalOutput.InfernalCryCooldown)) - t_insert(globalBreakdown.InfernalUpTimeRatio, s_format("+ %.2f) ^8(warcry casttime)", globalOutput.InfernalCryCastTime)) - else - t_insert(globalBreakdown.InfernalUpTimeRatio, s_format("/ %.2f ^8(average warcry cooldown)", globalOutput.InfernalCryCooldown)) - end - t_insert(globalBreakdown.InfernalUpTimeRatio, s_format("= %d%%", globalOutput.InfernalUpTimeRatio)) + local powerCap = value.skillModList:Sum("BASE", nil, "WarcryPowerCap") + local powerPer = value.skillModList:Sum("BASE", nil, "WarcryPowerPer") + globalOutput.InfernalEmpoweredCount = powerPer > 0 and m_floor(m_min(warcryPower, powerCap) / powerPer) or 0 + local baseUptimeRatio = m_min((globalOutput.InfernalEmpoweredCount / globalOutput.Speed) / (globalOutput.InfernalCryCooldown + globalOutput.InfernalCryCastTime), 1) * 100 + local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + globalOutput.InfernalCryUptimeRatio = m_min(100, baseUptimeRatio * storedUses) + globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.InfernalCryUptimeRatio + if globalBreakdown then + globalBreakdown.InfernalCryUptimeRatio = { } + t_insert(globalBreakdown.InfernalCryUptimeRatio, s_format("(%.2f ^8(number of empowered)", globalOutput.InfernalEmpoweredCount)) + t_insert(globalBreakdown.InfernalCryUptimeRatio, s_format("/ %.2f) ^8(attacks per second)", globalOutput.Speed)) + if globalOutput.InfernalCryCastTime > 0 then + t_insert(globalBreakdown.InfernalCryUptimeRatio, s_format("/ (%.2f ^8(warcry cooldown)", globalOutput.InfernalCryCooldown)) + t_insert(globalBreakdown.InfernalCryUptimeRatio, s_format("+ %.2f) ^8(warcry cast time)", globalOutput.InfernalCryCastTime)) + else + t_insert(globalBreakdown.InfernalCryUptimeRatio, s_format("/ %.2f ^8(average warcry cooldown)", globalOutput.InfernalCryCooldown)) end + t_insert(globalBreakdown.InfernalCryUptimeRatio, s_format("= %d%%", globalOutput.InfernalCryUptimeRatio)) + end + local infernalGainAsFire = modDB:Sum("BASE", nil, "InfernalExtraFireDamage") + if infernalGainAsFire > 0 then + local infernalUptime = activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") and 100 or globalOutput.InfernalCryUptimeRatio + skillModList:NewMod("DamageGainAsFire", "BASE", infernalGainAsFire * infernalUptime / 100, "Uptime Scaled Infernal Cry", ModFlag.Melee) end globalOutput.InfernalCryCalculated = true elseif value.activeEffect.grantedEffect.name == "Intimidating Cry" and activeSkill.skillTypes[SkillType.Melee] and not globalOutput.IntimidatingCryCalculated then @@ -3368,60 +3379,13 @@ function calcs.offence(env, actor, activeSkill) else globalOutput.AilmentWarcryEffect = globalOutput.OffensiveWarcryEffect end - - -- Calculate Exerted Attack Uptime - -- There are various strategies a player could use to maximize either warcry effect stacking or staggering - -- 1) they don't pay attention and therefore we calculated exerted attack uptime as just the maximum uptime of any enabled warcries that exert attacks - local warcryList = {"AncestralUpTimeRatio", "InfernalUpTimeRatio", "IntimidatingUpTimeRatio", "RallyingUpTimeRatio", "SeismicUpTimeRatio", "BattlemageUpTimeRatio"} - for _, cryTimeRatio in ipairs(warcryList) do - globalOutput.ExertedAttackUptimeRatio = m_max(globalOutput.ExertedAttackUptimeRatio or 0, globalOutput[cryTimeRatio] or 0) - end - if globalBreakdown then - globalBreakdown.ExertedAttackUptimeRatio = { } - t_insert(globalBreakdown.ExertedAttackUptimeRatio, s_format("Maximum of:")) - for _, cryTimeRatio in ipairs(warcryList) do - if globalOutput[cryTimeRatio] then - t_insert(globalBreakdown.ExertedAttackUptimeRatio, s_format("%d%% ^8(%s Cry Uptime)", globalOutput[cryTimeRatio] or 0, cryTimeRatio:match("(.+)Up.*"))) - end - end - t_insert(globalBreakdown.ExertedAttackUptimeRatio, s_format("= %d%%", globalOutput.ExertedAttackUptimeRatio)) - end - if globalOutput.ExertedAttackUptimeRatio > 0 and not globalOutput.ExertedAttackUptimeRatioCalculated then - local incExertedAttacks = skillModList:Sum("INC", cfg, "ExertIncrease") - local moreExertedAttacks = skillModList:Sum("MORE", cfg, "ExertIncrease") - local moreExertedAttackDamage = skillModList:Sum("MORE", cfg, "ExertAttackIncrease") - local overexertionExertedDamage = skillModList:Sum("MORE", cfg, "OverexertionExertAverageIncrease") - local echoesOfCreationExertedDamage = skillModList:Sum("MORE", cfg, "EchoesExertAverageIncrease") - if activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then - skillModList:NewMod("Damage", "INC", incExertedAttacks, "Exerted Attacks") - skillModList:NewMod("Damage", "MORE", moreExertedAttacks, "Exerted Attacks") - skillModList:NewMod("Damage", "MORE", moreExertedAttackDamage, "Exerted Attack Damage", ModFlag.Attack) - skillModList:NewMod("Damage", "MORE", overexertionExertedDamage * exertingWarcryCount, "Max Autoexertion Support") - skillModList:NewMod("Damage", "MORE", echoesOfCreationExertedDamage * exertingWarcryCount, "Max Echoes of Creation") - else - skillModList:NewMod("Damage", "INC", incExertedAttacks * globalOutput.ExertedAttackUptimeRatio / 100, "Uptime Scaled Exerted Attacks") - skillModList:NewMod("Damage", "MORE", moreExertedAttacks * globalOutput.ExertedAttackUptimeRatio / 100, "Uptime Scaled Exerted Attacks") - skillModList:NewMod("Damage", "MORE", moreExertedAttackDamage * globalOutput.ExertedAttackUptimeRatio / 100, "Uptime Scaled Exerted Attack Damage", ModFlag.Attack) - skillModList:NewMod("Damage", "MORE", overexertionExertedDamage * globalOutput.GlobalWarcryUptimeRatio / 100, "Uptime Scaled Autoexertion Support") - skillModList:NewMod("Damage", "MORE", echoesOfCreationExertedDamage * globalOutput.GlobalWarcryUptimeRatio / 100, "Uptime Scaled Echoes of Creation") - end - globalOutput.ExertedAttackAvgDmg = calcLib.mod(skillModList, skillCfg, "ExertIncrease") - globalOutput.ExertedAttackAvgDmg = globalOutput.ExertedAttackAvgDmg * calcLib.mod(skillModList, skillCfg, "ExertAttackIncrease", "OverexertionExertAverageIncrease", "EchoesExertAverageIncrease") - globalOutput.ExertedAttackHitEffect = globalOutput.ExertedAttackAvgDmg * globalOutput.ExertedAttackUptimeRatio / 100 - globalOutput.ExertedAttackMaxHitEffect = globalOutput.ExertedAttackAvgDmg - if globalBreakdown then - globalBreakdown.ExertedAttackHitEffect = { - s_format("(%.2f ^8(average exerted damage)", globalOutput.ExertedAttackAvgDmg), - s_format("x %.2f) ^8(uptime %%)", globalOutput.ExertedAttackUptimeRatio / 100), - s_format("= %.2f", globalOutput.ExertedAttackHitEffect), - } - end - globalOutput.ExertedAttackUptimeRatioCalculated = true - end end end output.FistOfWarDamageEffect = 1 + output.AncestralCallDamageEffect = 1 + output.AncestralEmpowermentDamageEffect = 1 + output.AncestralEmpowermentCombinedDamageEffect = 1 if env.mode_combat then local ruthlessEffect = env.configInput.ruthlessSupportMode or "AVERAGE" local ruthlessBlowMaxCount = skillModList:Sum("BASE", cfg, "RuthlessBlowMaxCount") @@ -3440,41 +3404,208 @@ function calcs.offence(env, actor, activeSkill) local ruthlessBlowStunEffect = (ruthlessBlowChance / 100) * ruthlessBlowStunMultiplier skillModList:NewMod("EnemyHeavyStunBuildup", "MORE", ruthlessBlowStunEffect * 100, "Ruthless Blows") - globalOutput.FistOfWarCooldown = skillModList:Sum("BASE", cfg, "FistOfWarCooldown") or 0 - -- If Fist of War & Active Skill is a Slam Skill & NOT a Vaal Skill & NOT used by mirage or other - if globalOutput.FistOfWarCooldown ~= 0 and activeSkill.skillTypes[SkillType.Slam] and not activeSkill.skillTypes[SkillType.Vaal] and not activeSkill.skillTypes[SkillType.OtherThingUsesSkill] then - globalOutput.FistOfWarDamageMultiplier = skillModList:Sum("BASE", nil, "FistOfWarDamageMultiplier") / 100 - globalOutput.FistOfWarUptimeRatio = m_min( (1 / globalOutput.Speed) / globalOutput.FistOfWarCooldown, 1) * 100 + local ancestrallyBoostedIncDamageMulti = modDB:Sum("INC", cfg, "AncestralBoostDamage") / 100 + local ancestrallyBoostedIncArea = skillModList:Sum("INC", cfg, "AncestralBoostAreaOfEffect") + -- Condition:AncestrallyBoosted * AncestralBoostEffect (e.g. Fist of War III) + local ancestrallyBoostedMoreDamageMulti = skillModList:Sum("BASE", cfg, "AncestralBoostMoreDamage") / 100 + + -- dynamic way of calculating the Ancestral Boost from a single source without duplicating the code + -- uptimeOverride: Ancestral Empowerment + -- combinedCalcs: ignore INC AoE as we will run that in calcCombinedAncestralBoost + local function calcAncestralBoost(skillName, moreDmg, uptimeOverride, combinedCalcs) + globalOutput.CreateWarcryOffensiveCalcSection = true -- labels for the CalcSection + local skillNameVar = skillName:gsub(" ", "") -- Fist Of War -> FistOfWar + local skillNameLabel = skillName:lower() + + globalOutput[skillNameVar.."DamageMultiplier"] = moreDmg or 1 + globalOutput[skillNameVar.."UptimeRatio"] = uptimeOverride or m_min( (1 / globalOutput.Speed) / globalOutput[skillNameVar.."Cooldown"], 1) * 100 + if globalBreakdown then + globalBreakdown[skillNameVar.."UptimeRatio"] = { + s_format("min( (1 / %.2f) ^8(second per attack)", globalOutput.Speed), + s_format("/ %.2f, 1) ^8("..skillNameLabel.." cooldown)", uptimeOverride and (1 / globalOutput.Speed / (uptimeOverride / 100)) or globalOutput[skillNameVar.."Cooldown"]), + s_format("= %d%%", globalOutput[skillNameVar.."UptimeRatio"]), + } + end + globalOutput["Avg"..skillNameVar.."Damage"] = globalOutput[skillNameVar.."DamageMultiplier"] + globalOutput["Avg"..skillNameVar.."DamageEffect"] = 1 + globalOutput["Avg"..skillNameVar.."Damage"] * (globalOutput[skillNameVar.."UptimeRatio"] / 100) + if globalBreakdown then + globalBreakdown["Avg"..skillNameVar.."DamageEffect"] = { + s_format("1 + (%.2f ^8("..skillNameLabel.." damage multiplier)", globalOutput[skillNameVar.."DamageMultiplier"]), + s_format("x %.2f) ^8("..skillNameLabel.." uptime ratio)", globalOutput[skillNameVar.."UptimeRatio"] / 100), + s_format("= %.2f", globalOutput["Avg"..skillNameVar.."DamageEffect"]), + } + end + globalOutput["Max"..skillNameVar.."DamageEffect"] = 1 + globalOutput[skillNameVar.."DamageMultiplier"] + if activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then + output[skillNameVar.."DamageEffect"] = globalOutput["Max"..skillNameVar.."DamageEffect"] + else + output[skillNameVar.."DamageEffect"] = globalOutput["Avg"..skillNameVar.."DamageEffect"] + end + calcAreaOfEffect(skillModList, skillCfg, skillData, skillFlags, globalOutput, globalBreakdown) + globalOutput.TheoreticalOffensiveWarcryEffect = globalOutput.TheoreticalOffensiveWarcryEffect * globalOutput["Avg"..skillNameVar.."DamageEffect"] + globalOutput.TheoreticalMaxOffensiveWarcryEffect = globalOutput.TheoreticalMaxOffensiveWarcryEffect * globalOutput["Max"..skillNameVar.."DamageEffect"] + end + + -- combine Ancestral Empowerment with other sources of Slam Ancestral Boost, namely Fist of War, when both active + local function calcCombinedAncestralBoost(skillName, moreDmg, uptimeOverride, additionalSkillName) + globalOutput.CreateWarcryOffensiveCalcSection = true -- labels for the CalcSection + local skillNameVar = skillName:gsub(" ", "") -- Fist Of War -> FistOfWar + local skillNameLabel = skillName:lower() + + globalOutput[skillNameVar.."DamageMultiplier"] = moreDmg or 1 + -- for CalcSections, set the AncestralEmpowerment damage for mod breakdown + globalOutput[skillNameVar.."CombinedDamageMultiplier"] = globalOutput[skillNameVar.."DamageMultiplier"] + skillNameVar = skillNameVar.."Combined" + local additionalSkillNameVar = additionalSkillName:gsub(" ", "") + local additionalSkillNameLabel = additionalSkillName:lower() + + -- a lot of these are doubled up because it would be very long lines otherwise and hopefully this helps legibility + globalOutput[skillNameVar.."UptimeRatio"] = uptimeOverride or m_min( (1 / globalOutput.Speed) / globalOutput[skillNameVar.."Cooldown"], 1) * 100 + globalOutput[skillNameVar.."UptimeRatio"] = m_min(globalOutput[skillNameVar.."UptimeRatio"] + (globalOutput[additionalSkillNameVar.."UptimeRatio"] or 0), 100) if globalBreakdown then - globalBreakdown.FistOfWarUptimeRatio = { + globalBreakdown[skillNameVar.."UptimeRatio"] = { + s_format("min( (1 / %.2f) ^8(second per attack)", globalOutput.Speed), + s_format("/ %.2f, 1) ^8("..skillNameLabel.." cooldown)", uptimeOverride and (1 / globalOutput.Speed / (uptimeOverride / 100)) or globalOutput[skillNameVar.."Cooldown"]), + "+", s_format("min( (1 / %.2f) ^8(second per attack)", globalOutput.Speed), - s_format("/ %.2f, 1) ^8(fist of war cooldown)", globalOutput.FistOfWarCooldown), - s_format("= %d%%", globalOutput.FistOfWarUptimeRatio), + s_format("/ %.2f, 1) ^8("..additionalSkillNameLabel.." cooldown)", globalOutput[additionalSkillNameVar.."Cooldown"]), + "capped at 100%", + s_format("= %d%%", globalOutput[skillNameVar.."UptimeRatio"]), } end - globalOutput.AvgFistOfWarDamage = globalOutput.FistOfWarDamageMultiplier - globalOutput.AvgFistOfWarDamageEffect = 1 + globalOutput.FistOfWarDamageMultiplier * (globalOutput.FistOfWarUptimeRatio / 100) + globalOutput["Avg"..skillNameVar.."Damage"] = globalOutput[skillNameVar.."DamageMultiplier"] + globalOutput["Avg"..skillNameVar.."DamageEffect"] = 1 + globalOutput["Avg"..skillNameVar.."Damage"] * (globalOutput[skillNameVar.."UptimeRatio"] / 100) if globalBreakdown then - globalBreakdown.AvgFistOfWarDamageEffect = { - s_format("1 + (%.2f ^8(fist of war damage multiplier)", globalOutput.FistOfWarDamageMultiplier), - s_format("x %.2f) ^8(fist of war uptime ratio)", globalOutput.FistOfWarUptimeRatio / 100), - s_format("= %.2f", globalOutput.AvgFistOfWarDamageEffect), + globalBreakdown["Avg"..skillNameVar.."DamageEffect"] = { + s_format("1 + (%.2f x %.2f) ^8(combined ancestral boost damage multiplier x uptime ratio)", globalOutput[skillNameVar.."DamageMultiplier"], globalOutput[skillNameVar.."UptimeRatio"] / 100), + s_format("= %.2f", globalOutput["Avg"..skillNameVar.."DamageEffect"]), } end - globalOutput.MaxFistOfWarDamageEffect = 1 + globalOutput.FistOfWarDamageMultiplier + globalOutput["Max"..skillNameVar.."DamageEffect"] = 1 + globalOutput[skillNameVar.."DamageMultiplier"] if activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then - output.FistOfWarDamageEffect = globalOutput.MaxFistOfWarDamageEffect - skillModList:NewMod("AreaOfEffect", "MORE", skillModList:Sum("BASE", nil, "FistOfWarMOREAoE"), "Max Fist of War Boosted AoE") + output[skillNameVar.."DamageEffect"] = globalOutput["Max"..skillNameVar.."DamageEffect"] else - output.FistOfWarDamageEffect = globalOutput.AvgFistOfWarDamageEffect - skillModList:NewMod("AreaOfEffect", "MORE", m_floor(skillModList:Sum("BASE", nil, "FistOfWarMOREAoE") / 100 * globalOutput.FistOfWarUptimeRatio), "Avg Fist Of War Boosted AoE") + output[skillNameVar.."DamageEffect"] = globalOutput["Avg"..skillNameVar.."DamageEffect"] end calcAreaOfEffect(skillModList, skillCfg, skillData, skillFlags, globalOutput, globalBreakdown) - globalOutput.TheoreticalOffensiveWarcryEffect = globalOutput.TheoreticalOffensiveWarcryEffect * globalOutput.AvgFistOfWarDamageEffect - globalOutput.TheoreticalMaxOffensiveWarcryEffect = globalOutput.TheoreticalMaxOffensiveWarcryEffect * globalOutput.MaxFistOfWarDamageEffect + globalOutput.TheoreticalOffensiveWarcryEffect = globalOutput.TheoreticalOffensiveWarcryEffect * globalOutput["Avg"..skillNameVar.."DamageEffect"] + globalOutput.TheoreticalMaxOffensiveWarcryEffect = globalOutput.TheoreticalMaxOffensiveWarcryEffect * globalOutput["Max"..skillNameVar.."DamageEffect"] + end + + globalOutput.FistOfWarCooldown = skillModList:Sum("BASE", cfg, "FistOfWarCooldown") or 0 + if skillModList:Flag(cfg, "AncestralEmpowerment") and activeSkill.skillTypes[SkillType.Slam] and not activeSkill.skillTypes[SkillType.Vaal] and not activeSkill.skillTypes[SkillType.OtherThingUsesSkill] then + if globalOutput.FistOfWarCooldown ~= 0 then -- get the fist of war calcs in output to use in Empowerment + calcAncestralBoost("Fist Of War", ancestrallyBoostedMoreDamageMulti, nil, true) + globalOutput.TheoreticalOffensiveWarcryEffect = 1 -- reset effects from FistOfWar calc, we combine later + globalOutput.TheoreticalMaxOffensiveWarcryEffect = 1 + + calcCombinedAncestralBoost("Ancestral Empowerment", ancestrallyBoostedMoreDamageMulti, 50, "Fist Of War") + globalOutput.FistOfWarUptimeRatio = nil -- hide from CalcSections, but we need it for the combined calc first + else + calcAncestralBoost("Ancestral Empowerment", ancestrallyBoostedMoreDamageMulti, 50) + end + end + -- If Fist of War & Active Skill is a Slam Skill & NOT a Vaal Skill & NOT used by mirage or other + if not skillModList:Flag(cfg, "AncestralEmpowerment") and globalOutput.FistOfWarCooldown ~= 0 and activeSkill.skillTypes[SkillType.Slam] and not activeSkill.skillTypes[SkillType.Vaal] and not activeSkill.skillTypes[SkillType.OtherThingUsesSkill] then + calcAncestralBoost("Fist Of War", ancestrallyBoostedMoreDamageMulti) else output.FistOfWarDamageEffect = 1 end + + globalOutput.AncestralCallCooldown = skillModList:Sum("BASE", cfg, "AncestralCallCooldown") or 0 + -- If Ancestral Call & Active Skill is NOT a Vaal Skill & NOT used by mirage or other & NOT a Channel Skill + if globalOutput.AncestralCallCooldown ~= 0 and not activeSkill.skillTypes[SkillType.Vaal] and not activeSkill.skillTypes[SkillType.OtherThingUsesSkill] and not activeSkill.skillTypes[SkillType.Channel] then + calcAncestralBoost("Ancestral Call") + else + output.AncestralCallDamageEffect = 1 + end + end + + -- Calculate Empowered Attack Uptime + -- Ancestrally Boosted attacks are also Empowered, so we need to check for the highest uptime among both lists + -- we can use these ratio flags to max the uptime for attacks/skills that guarantee Ancestrally Boosted or Empowered in specific cases like Crescendo III + local maxEmpoweredUptimeRatio = (skillModList:Flag(cfg, "MaxBoostedUptimeRatio") or skillModList:Flag(cfg, "MaxEmpoweredUptimeRatio")) and 100 + local warcryList = { "InfernalCryUptimeRatio" } + local ancestralBoostList = { "AncestralEmpowermentCombinedUptimeRatio", "AncestralEmpowermentUptimeRatio", "FistOfWarUptimeRatio", "AncestralCallUptimeRatio" } + local combinedList = {} + for _, ratio in ipairs(warcryList) do + t_insert(combinedList, ratio) + end + for _, ratio in ipairs(ancestralBoostList) do + t_insert(combinedList, ratio) + end + + for _, cryTimeRatio in ipairs(combinedList) do + globalOutput.ExertedAttackUptimeRatio = maxEmpoweredUptimeRatio or m_max(globalOutput.ExertedAttackUptimeRatio or 0, globalOutput[cryTimeRatio] or 0) + end + if globalBreakdown then + globalBreakdown.ExertedAttackUptimeRatio = { } + t_insert(globalBreakdown.ExertedAttackUptimeRatio, s_format("Maximum of:")) + for _, cryTimeRatio in ipairs(combinedList) do + if globalOutput[cryTimeRatio] then + t_insert(globalBreakdown.ExertedAttackUptimeRatio, s_format("%d%% ^8(%s Uptime)", maxEmpoweredUptimeRatio or globalOutput[cryTimeRatio] or 0, maxEmpoweredUptimeRatio and "Override" or cryTimeRatio:match("(.+)Up.*") or "Override")) + end + end + t_insert(globalBreakdown.ExertedAttackUptimeRatio, s_format("= %d%%", globalOutput.ExertedAttackUptimeRatio)) + end + if globalOutput.ExertedAttackUptimeRatio > 0 and not globalOutput.ExertedAttackUptimeRatioCalculated then + local incExertedAttacks = skillModList:Sum("INC", cfg, "EmpoweredIncrease") + if activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then + skillModList:NewMod("Damage", "INC", incExertedAttacks, "Empowered Attacks", ModFlag.Attack) + else + skillModList:NewMod("Damage", "INC", incExertedAttacks * globalOutput.ExertedAttackUptimeRatio / 100, "Uptime Scaled Empowered Attacks", ModFlag.Attack) + end + globalOutput.ExertedAttackAvgDmg = calcLib.mod(skillModList, skillCfg, "EmpoweredIncrease") + globalOutput.ExertedAttackHitEffect = globalOutput.ExertedAttackAvgDmg * globalOutput.ExertedAttackUptimeRatio / 100 + globalOutput.ExertedAttackMaxHitEffect = globalOutput.ExertedAttackAvgDmg + if globalBreakdown then + globalBreakdown.ExertedAttackHitEffect = { + s_format("(%.2f ^8(average exerted damage)", globalOutput.ExertedAttackAvgDmg), + s_format("x %.2f) ^8(uptime %%)", globalOutput.ExertedAttackUptimeRatio / 100), + s_format("= %.2f", globalOutput.ExertedAttackHitEffect), + } + end + globalOutput.ExertedAttackUptimeRatioCalculated = true + end + + local maxBoostedUptimeRatio = skillModList:Flag(cfg, "MaxBoostedUptimeRatio") and 100 + -- Calculate Ancestrally Boosted Attack Uptime + for _, boostUptimeRatio in ipairs(ancestralBoostList) do + globalOutput.BoostedAttackUptimeRatio = maxBoostedUptimeRatio or m_max(globalOutput.BoostedAttackUptimeRatio or 0, globalOutput[boostUptimeRatio] or 0) + end + if globalBreakdown then + globalBreakdown.BoostedAttackUptimeRatio = { } + t_insert(globalBreakdown.BoostedAttackUptimeRatio, s_format("Maximum of:")) + for _, boostUptimeRatio in ipairs(ancestralBoostList) do + if globalOutput[boostUptimeRatio] then + t_insert(globalBreakdown.BoostedAttackUptimeRatio, s_format("%d%% ^8(%s Uptime)", maxBoostedUptimeRatio or globalOutput[boostUptimeRatio] or 0, maxBoostedUptimeRatio and "Override" or boostUptimeRatio:match("(.+)Up.*"))) + end + end + t_insert(globalBreakdown.BoostedAttackUptimeRatio, s_format("= %d%%", globalOutput.BoostedAttackUptimeRatio)) + end + if globalOutput.BoostedAttackUptimeRatio > 0 and not globalOutput.BoostedAttackUptimeRatioCalculated then + local incBoostedAttacks = skillModList:Sum("INC", cfg, "AncestralBoostDamage") + local incAoEBoostedAttacks = skillModList:Sum("INC", cfg, "AncestralBoostAreaOfEffect") + if activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then + skillModList:NewMod("Damage", "INC", incBoostedAttacks, "Ancestrally Boosted Attacks", ModFlag.Attack) + skillModList:NewMod("AreaOfEffect", "INC", incAoEBoostedAttacks, "Ancestrally Boosted Attacks") + else + skillModList:NewMod("Damage", "INC", incBoostedAttacks * globalOutput.BoostedAttackUptimeRatio / 100, "Uptime Scaled Ancestrally Boosted Attacks", ModFlag.Attack) + skillModList:NewMod("AreaOfEffect", "INC", incAoEBoostedAttacks * globalOutput.BoostedAttackUptimeRatio / 100, "Uptime Scaled Ancestrally Boosted AoE") + end + calcAreaOfEffect(skillModList, skillCfg, skillData, skillFlags, globalOutput, globalBreakdown) + + globalOutput.BoostedAttackAvgDmg = calcLib.mod(skillModList, skillCfg, "AncestralBoostDamage") + globalOutput.BoostedAttackHitEffect = globalOutput.BoostedAttackAvgDmg * globalOutput.BoostedAttackUptimeRatio / 100 + globalOutput.BoostedAttackMaxHitEffect = globalOutput.BoostedAttackAvgDmg + if globalBreakdown then + globalBreakdown.BoostedAttackHitEffect = { + s_format("(%.2f ^8(average Boosted damage)", globalOutput.BoostedAttackAvgDmg), + s_format("x %.2f) ^8(uptime %%)", globalOutput.BoostedAttackUptimeRatio / 100), + s_format("= %.2f", globalOutput.BoostedAttackHitEffect), + } + end + globalOutput.BoostedAttackUptimeRatioCalculated = true end -- Calculate maximum sustainable fuses and explosion rate for Explosive Arrow @@ -3745,6 +3876,8 @@ function calcs.offence(env, actor, activeSkill) --Calculate reservation DPS globalOutput.ReservationDpsMultiplier = 100 / (100 - enemyDB:Sum("BASE", nil, "LifeReservationPercent")) + buildGainTable() + -- Calculate base hit damage for _, damageType in ipairs(dmgTypeList) do local damageTypeMin = damageType.."Min" @@ -3849,6 +3982,15 @@ function calcs.offence(env, actor, activeSkill) if output.FistOfWarDamageEffect ~= 1 then t_insert(breakdown[damageType], s_format("x %.2f ^8(fist of war effect modifier)", output.FistOfWarDamageEffect)) end + if output.AncestralCallDamageEffect ~= 1 then + t_insert(breakdown[damageType], s_format("x %.2f ^8(ancestral call effect modifier)", output.AncestralCallDamageEffect)) + end + if output.AncestralEmpowermentDamageEffect ~= 1 then + t_insert(breakdown[damageType], s_format("x %.2f ^8(ancestral empowerment effect modifier)", output.AncestralEmpowermentDamageEffect)) + end + if output.AncestralEmpowermentCombinedDamageEffect ~= 1 then + t_insert(breakdown[damageType], s_format("x %.2f ^8(ancestral empowerment combined effect modifier)", output.AncestralEmpowermentCombinedDamageEffect)) + end if globalOutput.OffensiveWarcryEffect ~= 1 and not activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then t_insert(breakdown[damageType], s_format("x %.2f ^8(aggregated warcry exerted effect modifier)", globalOutput.OffensiveWarcryEffect)) end @@ -3857,9 +3999,9 @@ function calcs.offence(env, actor, activeSkill) end end if activeSkill.skillModList:Flag(nil, "Condition:WarcryMaxHit") then - output.allMult = output.ScaledDamageEffect * output.FistOfWarDamageEffect * globalOutput.MaxOffensiveWarcryEffect + output.allMult = output.ScaledDamageEffect * output.FistOfWarDamageEffect * output.AncestralCallDamageEffect * output.AncestralEmpowermentDamageEffect * output.AncestralEmpowermentCombinedDamageEffect * globalOutput.MaxOffensiveWarcryEffect else - output.allMult = output.ScaledDamageEffect * output.FistOfWarDamageEffect * globalOutput.OffensiveWarcryEffect + output.allMult = output.ScaledDamageEffect * output.FistOfWarDamageEffect * output.AncestralCallDamageEffect * output.AncestralEmpowermentDamageEffect * output.AncestralEmpowermentCombinedDamageEffect * globalOutput.OffensiveWarcryEffect end local allMult = output.allMult if pass == 1 then diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index ba4b6e0c4e..f17d22d132 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -1232,12 +1232,9 @@ function calcs.perform(env, skipEHP) modDB:NewMod("Condition:HaveCompanion", "FLAG", true, activeSkill.activeEffect.grantedEffect.name) end if env.mode_buffs and skillFlags.warcry then - if activeSkill.activeEffect.grantedEffect.name == "Rallying Cry" and not activeSkill.skillModList:Flag(nil, "CannotShareWarcryBuffs") and not modDB:Flag(nil, "RallyingActive") then - env.player.modDB:NewMod("RallyingExertMoreDamagePerAlly", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "RallyingCryExertDamageBonus")) - modDB:NewMod("RallyingActive", "FLAG", true) -- Prevents effect from applying multiple times - elseif activeSkill.activeEffect.grantedEffect.name == "Seismic Cry" and not modDB:Flag(nil, "SeismicActive") then - env.player.modDB:NewMod("SeismicMoreAoE", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "SeismicAoEMoreMultiplier")) - modDB:NewMod("SeismicActive", "FLAG", true) -- Prevents effect from applying multiple times + if activeSkill.activeEffect.grantedEffect.name == "Infernal Cry" and not modDB:Flag(nil, "InfernalActive") then + env.player.modDB:NewMod("InfernalExtraFireDamage", "BASE", activeSkill.skillModList:Sum("BASE", env.player.mainSkill.skillCfg, "InfernalExtraFireDamageMultiplier")) + modDB:NewMod("InfernalActive", "FLAG", true) -- Prevents effect from applying multiple times end end if activeSkill.skillData.triggeredOnDeath and not skillFlags.minion then @@ -1985,19 +1982,22 @@ function calcs.perform(env, skipEHP) if env.mode_buffs then local skillCfg = skillCfg local modStore = skillModList or modDB + local warcryPower = modDB:Override(nil, "WarcryPower") or m_max((modDB:Sum("BASE", nil, "WarcryPower") or 0) * (1 + (modDB:Sum("INC", nil, "WarcryPower") or 0)/100), (modDB:Sum("BASE", nil, "MinimumWarcryPower") or 0)) + local powerCap = skillModList:Sum("BASE", nil, "WarcryPowerCap") + local powerPer = skillModList:Sum("BASE", nil, "WarcryPowerPer") + local baseEmpowers = m_floor(m_min(warcryPower, powerCap) / powerPer) local warcryName = buff.name:gsub(" Cry", ""):gsub("'s",""):gsub(" ","") - local baseExerts = modStore:Sum("BASE", env.player.mainSkill.skillCfg, warcryName.."ExertedAttacks") - if baseExerts > 0 then - local extraExertions = modStore:Sum("BASE", nil, "ExtraExertedAttacks") or 0 - local exertMultiplier = modStore:More(nil, "ExtraExertedAttacks") - env.player.modDB:NewMod("Num"..warcryName.."Exerts", "BASE", m_floor((baseExerts + extraExertions) * exertMultiplier)) + local baseEmpowers = modStore:Sum("BASE", env.player.mainSkill.skillCfg, warcryName.."EmpoweredAttacks") + baseEmpowers + if baseEmpowers > 0 then + local extraEmpowers = modStore:Sum("BASE", nil, "ExtraEmpoweredAttacks") or 0 + local EmpowerMultiplier = modStore:More(nil, "ExtraEmpoweredAttacks") + env.player.modDB:NewMod("Num"..warcryName.."Empowers", "BASE", m_floor((baseEmpowers + extraEmpowers) * EmpowerMultiplier)) if not warcryList[buff.name] then - env.player.modDB:NewMod("Multiplier:ExertingWarcryCount", "BASE", 1, buff.name) + env.player.modDB:NewMod("Multiplier:EmpoweringWarcryCount", "BASE", 1, buff.name) warcryList[buff.name] = true end end if not activeSkill.skillModList:Flag(nil, "CannotShareWarcryBuffs") then - local warcryPower = modDB:Override(nil, "WarcryPower") or m_max((modDB:Sum("BASE", nil, "WarcryPower") or 0) * (1 + (modDB:Sum("INC", nil, "WarcryPower") or 0)/100), (modDB:Sum("BASE", nil, "MinimumWarcryPower") or 0)) for _, warcryBuff in ipairs(buff.modList) do if warcryBuff[1] and warcryBuff[1].effectType == "Warcry" and warcryBuff[1].div then warcryBuff[1].warcryPowerBonus = m_floor((warcryBuff[1].limit and m_min(warcryPower, warcryBuff[1].limit) or warcryPower) / warcryBuff[1].div) diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index 1593e059b8..1535302f90 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -355,7 +355,7 @@ return { { label = "Skill DPS", flag = "triggered", { format = "{1:output:TotalDPS}", { breakdown = "TotalDPS" }, { label = "DPS Multiplier", modName = "DPS", cfg = "skill" }, }, }, } } } }, -{ 3, "Warcries", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Exerting Warcries", data = { +{ 3, "Warcries", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Empowered Stats", data = { extra = "{2:output:TheoreticalOffensiveWarcryEffect} Avg Combined Impact | {2:output:TheoreticalMaxOffensiveWarcryEffect} Max Combined Impact", colWidth = 114, { @@ -363,7 +363,7 @@ return { { format = "Effective Impact" }, { format = "Avg Dmg Effect" }, { format = "Uptime" }, - { format = "Number of Exerts" }, + { format = "# of Empowered" }, { format = "Max 1-Hit Impact"}, }, { label = "Seismic Cry", haveOutput = "SeismicUpTimeRatio", @@ -387,11 +387,11 @@ return { { format = "{0:output:RallyingExertsCount}" }, { format = "{2:output:RallyingMaxHitEffect}" }, }, - { label = "Infernal Cry", haveOutput = "InfernalUpTimeRatio", + { label = "Infernal Cry", haveOutput = "InfernalCryUptimeRatio", { format = "" }, { format = "" }, - { format = "{0:output:InfernalUpTimeRatio}%", { breakdown = "InfernalUpTimeRatio" }, }, - { format = "{0:output:InfernalExertsCount}" }, + { format = "{0:output:InfernalCryUptimeRatio}%", { breakdown = "InfernalCryUptimeRatio" }, }, + { format = "{0:output:InfernalEmpoweredCount}" }, { format = "" }, }, { label = "Battlemage's Cry", haveOutput = "BattlemageUpTimeRatio", @@ -408,20 +408,48 @@ return { { format = "{0:output:AncestralExertsCount}" }, { format = "" }, }, - { label = "Exerted Attacks", bgCol = colorCodes.MAINHANDBG, haveOutput = "ExertedAttackUptimeRatio", - { format = "{2:output:ExertedAttackHitEffect}", { breakdown = "ExertedAttackHitEffect"}, }, - { format = "{2:output:ExertedAttackAvgDmg}", { modName = "ExertIncrease", cfg = "skill" }, { modName = "ExertAttackIncrease", cfg = "skill" }, }, - { format = "{0:output:ExertedAttackUptimeRatio}%", { breakdown = "ExertedAttackUptimeRatio" }, }, + { label = "Empowered Attacks", haveOutput = "ExertedAttackUptimeRatio", + { format = "{2:output:ExertedAttackHitEffect}", { breakdown = "ExertedAttackHitEffect" }, }, + { format = "{2:output:ExertedAttackAvgDmg}", { modName = "EmpoweredIncrease", cfg = "skill" }, }, + { format = "{0:output:ExertedAttackUptimeRatio}%", { breakdown = "ExertedAttackUptimeRatio" }, }, + { format = "" }, + { format = "{2:output:ExertedAttackMaxHitEffect}", { modName = "ExertIncrease", cfg = "skill" }, { modName = "ExertAttackIncrease", cfg = "skill" }, }, + }, + { label = "Boosted Attacks", bgCol = colorCodes.MAINHANDBG, haveOutput = "BoostedAttackUptimeRatio", + { format = "{2:output:BoostedAttackHitEffect}", { breakdown = "BoostedAttackHitEffect"}, }, + { format = "{2:output:BoostedAttackAvgDmg}", { modName = "AncestralBoostDamage", cfg = "skill" }, { modName = "EmpoweredIncrease", cfg = "skill" } }, + { format = "{0:output:BoostedAttackUptimeRatio}%", { breakdown = "BoostedAttackUptimeRatio" }, }, { format = "" }, - { format = "{2:output:ExertedAttackMaxHitEffect}", { modName = "ExertIncrease", cfg = "skill" }, { modName = "ExertAttackIncrease", cfg = "skill" }, }, + { format = "{2:output:BoostedAttackMaxHitEffect}", { modName = "ExertIncrease", cfg = "skill" }, { modName = "ExertAttackIncrease", cfg = "skill" }, }, + }, + { label = "AncestralEmpower+", bgCol = colorCodes.MAINHANDBG, haveOutput = "AncestralEmpowermentCombinedUptimeRatio", + { format = "{2:output:AvgAncestralEmpowermentCombinedDamageEffect}", { breakdown = "AvgAncestralEmpowermentCombinedDamageEffect"}, }, + { format = "{2:output:AvgAncestralEmpowermentCombinedDamage}", { modName = { "AncestralEmpowermentDamageMultiplier", "FistOfWarDamageMultiplier" }, cfg = "skill" }, { modName = "AncestralBoostMoreDamage", cfg = "skill" }}, + { format = "{0:output:AncestralEmpowermentCombinedUptimeRatio}%", { breakdown = "AncestralEmpowermentCombinedUptimeRatio" }, }, + { format = "" }, + { format = "{2:output:MaxAncestralEmpowermentCombinedDamageEffect}" }, + }, + { label = "Anc. Empowerment", bgCol = colorCodes.MAINHANDBG, haveOutput = "AncestralEmpowermentUptimeRatio", + { format = "{2:output:AvgAncestralEmpowermentDamageEffect}", { breakdown = "AvgAncestralEmpowermentDamageEffect"}, }, + { format = "{2:output:AvgAncestralEmpowermentDamage}", { modName = "AncestralEmpowermentDamageMultiplier", cfg = "skill"}, { modName = "AncestralBoostMoreDamage", cfg = "skill" }}, + { format = "{0:output:AncestralEmpowermentUptimeRatio}%", { breakdown = "AncestralEmpowermentUptimeRatio" }, }, + { format = "" }, + { format = "{2:output:MaxAncestralEmpowermentDamageEffect}" }, }, { label = "Fist of War", bgCol = colorCodes.MAINHANDBG, haveOutput = "FistOfWarUptimeRatio", { format = "{2:output:AvgFistOfWarDamageEffect}", { breakdown = "AvgFistOfWarDamageEffect"}, }, - { format = "{2:output:AvgFistOfWarDamage}", { modName = "FistOfWarDamageMultiplier", cfg = "skill"}, }, + { format = "{2:output:AvgFistOfWarDamage}", { modName = "FistOfWarDamageMultiplier", cfg = "skill"}, { modName = "AncestralBoostMoreDamage", cfg = "skill" }}, { format = "{0:output:FistOfWarUptimeRatio}%", { breakdown = "FistOfWarUptimeRatio" }, }, { format = "" }, { format = "{2:output:MaxFistOfWarDamageEffect}" }, }, + { label = "Ancestral Call", bgCol = colorCodes.MAINHANDBG, haveOutput = "AncestralCallUptimeRatio", + { format = "{2:output:AvgAncestralCallDamageEffect}", { breakdown = "AvgAncestralCallDamageEffect"}, }, + { format = "{2:output:AvgAncestralCallDamage}", { modName = "AncestralCallDamageMultiplier", cfg = "skill"}}, + { format = "{0:output:AncestralCallUptimeRatio}%", { breakdown = "AncestralCallUptimeRatio" }, }, + { format = "" }, + { format = "{2:output:MaxAncestralCallDamageEffect}" }, + }, } } } }, { 3, "Dot", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Skill Damage over Time", data = { diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 8fe0f9c88e..7d5f215b9f 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -668,6 +668,12 @@ function calcs.initEnv(build, mode, override, specEnv) modDB:NewMod("Evasion", "INC", 10, "Base", { type = "Multiplier", var = "Tailwind", limit = 10 }) modDB:NewMod("SkillSlots", "BASE", 9, "Base") modDB:NewMod("MaxLineageCount", "BASE", 1, "Base") + modDB:NewMod("Multiplier:AncestralBoostEffect", "BASE", 1, "Base", { type = "Condition", var = "AncestrallyBoosted" }) + modDB:NewMod("AncestralBoostMoreDamage", "BASE", 30, "Base", 0, 0, { type = "Multiplier", var = "AncestralBoostEffect" }, { type = "SkillType", skillType = SkillType.Slam }, { type = "Condition", var = "AncestrallyBoosted" }) + modDB:NewMod("AncestralBoostAreaOfEffect", "INC", 25, "Base", 0, 0, { type = "Multiplier", var = "AncestralBoostEffect" }, { type = "SkillType", skillType = SkillType.Slam }, { type = "Condition", var = "AncestrallyBoosted" }) + modDB:NewMod("AdditionalStrikeTarget", "BASE", 2, "Base", 0, 0, { type = "Multiplier", var = "AncestralBoostEffect" }, { type = "SkillType", skillType = SkillType.Slam, neg = true }, { type = "Condition", var = "AncestrallyBoosted" }) + modDB:NewMod("Condition:Empowered", "FLAG", 1, "Base", { type = "Condition", var = "AncestrallyBoosted" }) + -- Initialise enemy modifier database calcs.initModDB(env, enemyDB) diff --git a/src/Modules/CalcTriggers.lua b/src/Modules/CalcTriggers.lua index d861968b9e..975ad5d77a 100644 --- a/src/Modules/CalcTriggers.lua +++ b/src/Modules/CalcTriggers.lua @@ -489,7 +489,7 @@ local function defaultTriggerHandler(env, config) -- Infernal Cry uptime if actor.mainSkill.activeEffect.grantedEffect.name == "Combust" and GlobalCache.cachedData[env.mode][uuid] and source and source.skillTypes[SkillType.Melee] then - local infernalCryExertsCount = GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalExertsCount + local infernalCryExertsCount = GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalEmpoweredCount local infernalCryDuration = ceil_b(GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalCryDuration, data.misc.ServerTickTime) local infernalCryCastTime = GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalCryCastTime local infernalCryCooldown = ceil_b(GlobalCache.cachedData[env.mode][uuid].Env.player.output.InfernalCryCooldown, data.misc.ServerTickTime) diff --git a/src/Modules/ConfigOptions.lua b/src/Modules/ConfigOptions.lua index dccce5e0ef..d69091439b 100644 --- a/src/Modules/ConfigOptions.lua +++ b/src/Modules/ConfigOptions.lua @@ -193,7 +193,7 @@ local configSettings = { modList:NewMod("Condition:ArmourAvg", "FLAG", true, "Config") end end }, - { var = "warcryMode", type = "list", label = "Exerted/Boosted calc mode:", ifSkill = { "Fist of War", "Infernal Cry", "Ancestral Cry", "Enduring Cry", "General's Cry", "Intimidating Cry", "Rallying Cry", "Seismic Cry", "Battlemage's Cry", "Vengeful Cry" }, tooltip = "Controls how exerted attacks from Warcries are calculated:\nAverage: Averages out Warcry usage with cast time, attack speed and warcry cooldown.\nMax Hit: Shows maximum hit for lining up all warcries.", list = {{val="AVERAGE",label="Average"},{val="MAX",label="Max Hit"}}, apply = function(val, modList, enemyModList) + { var = "warcryMode", type = "list", label = "Boosted calc mode:", tooltip = "Controls how ancestrally boosted attacks are calculated:\nAverage: Averages out Boost usage with cast time, attack speed and cooldown.\nMax Hit: Shows maximum hit for lining up all sources of ancestral boost.", list = {{val="AVERAGE",label="Average"},{val="MAX",label="Max Hit"}}, apply = function(val, modList, enemyModList) if val == "MAX" then modList:NewMod("Condition:WarcryMaxHit", "FLAG", true, "Config") end diff --git a/src/Modules/Data.lua b/src/Modules/Data.lua index 785898942c..904efc3492 100644 --- a/src/Modules/Data.lua +++ b/src/Modules/Data.lua @@ -891,6 +891,7 @@ data.gems = LoadModule("Data/Gems") data.gemForSkill = { } data.gemForBaseName = { } data.gemsByGameId = { } +data.gemNameForModSource = { } -- Lookup table - [Gem.grantedEffectId] = VaalGemId data.gemGrantedEffectIdForVaalGemId = { } data.gemVaalGemIdForBaseGemId = { } @@ -900,6 +901,7 @@ local function setupGem(gem, gemId) data.gemForSkill[gem.grantedEffect] = gemId data.gemsByGameId[gem.gameId] = data.gemsByGameId[gem.gameId] or {} data.gemsByGameId[gem.gameId][gem.variantId] = gem + data.gemNameForModSource[gem.grantedEffect.modSource] = gem.name local baseName = gem.name if gem.grantedEffect.support and gem.grantedEffectId ~= "SupportBarrage" then baseName = baseName .. " Support" diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index fb23cf2faf..1178e86a14 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -1401,7 +1401,6 @@ local preFlagList = { ["^for each nearby corpse, "] = { tag = { type = "Multiplier", var = "NearbyCorpse" } }, ["^enemies in your link beams have "] = { tag = { type = "Condition", var = "BetweenYouAndLinkedTarget" }, applyToEnemy = true }, ["^consecrated ground you create also grants "] = { tag = { type = "Condition", var = "OnConsecratedGround" } }, - ["^empowered attacks [hgd][ae][via][enl] "] = { flags = ModFlag.Attack, tag = { type = "Condition", var = "Empowered" } }, -- While in the presence of... ["^while a unique enemy is in your presence, "] = { tag = { type = "ActorCondition", actor = "enemy", var = "RareOrUnique" } }, ["^while a pinnacle atlas boss is in your presence, "] = { tag = { type = "ActorCondition", actor = "enemy", var = "PinnacleBoss" } }, @@ -1423,8 +1422,6 @@ local modTagList = { ["with critical hits"] = { tag = { type = "Condition", var = "CriticalStrike" } }, ["while affected by auras you cast"] = { tag = { type = "Condition", var = "AffectedByAura" } }, ["for you and nearby allies"] = { newAura = true }, - ["with empowered attacks"] = { tag = { type = "Condition", var = "Empowered" } }, - ["during empowered attacks"] = { tag = { type = "Condition", var = "Empowered" } }, -- Multipliers ["per power charge"] = { tag = { type = "Multiplier", var = "PowerCharge" } }, ["if you've consumed an? (%D+) charge recently"] = function(charge) return { tag = { type = "Multiplier", var = "Removable" .. firstToUpper(charge) .. "Charge", limit = 1 }} end, @@ -2627,6 +2624,15 @@ local specialModList = { -- Exerted Attacks ["exerted attacks deal (%d+)%% increased damage"] = function(num) return { mod("ExertIncrease", "INC", num, nil, ModFlag.Attack, 0) } end, ["exerted attacks have (%d+)%% chance to deal double damage"] = function(num) return { mod("ExertDoubleDamageChance", "BASE", num, nil, ModFlag.Attack, 0) } end, + -- Empowered Attacks + ["empowered attacks deal (%d+)%% increased damage"] = function(num) return { mod("EmpoweredIncrease", "INC", num, nil, ModFlag.Attack, 0, { type = "Condition", var = "Empowered"}) } end, + -- Ancestrally Boosted + ["ancestrally boosted attacks deal (%d+)%% increased damage"] = function(num) return { mod("AncestralBoostDamage", "INC", num, nil, ModFlag.Attack, 0) } end, + ["(%d+)%% increased area of effect of ancestrally boosted attacks"] = function(num) return { mod("AncestralBoostAreaOfEffect", "INC", num, nil, ModFlag.Attack, 0) } end, + ["every second slam skill you use yourself is ancestrally boosted"] = { + flag("AncestralEmpowerment", { type = "SkillType", skillType = SkillType.Slam }), + flag("Condition:AncestrallyBoosted", { type = "SkillType", skillType = SkillType.Slam }), + }, -- Leech Related ["life leech is instant"] = { mod("InstantLifeLeech", "BASE", 100), }, ["mana leech is instant"] = { mod("InstantManaLeech", "BASE", 100), }, @@ -2688,7 +2694,7 @@ local specialModList = { } end, -- Amazon - ["chance to hit with attacks can exceed 100%%"] = {flag("Condition:HitChanceCanExceed100", { type = "Skilltype", skillType = SkillType.Attack})}, + ["chance to hit with attacks can exceed 100%%"] = {flag("Condition:HitChanceCanExceed100", { type = "SkillType", skillType = SkillType.Attack})}, ["gain additional critical hit chance equal to (%d+)%% of excess chance to hit with attacks"] = function(num) return { mod("CritChance", "BASE", 0.01 * num, { type = "Multiplier", var = "ExcessHitChance" }, { type = "SkillType", skillType = SkillType.Attack})} end, ["attacks using your weapons have added (%a+) damage equal to (%d+)%% of the accuracy rating on the weapon"] = function(_, dmgType, num) return { mod(firstToUpper(dmgType) .. "Min", "BASE", 1, nil, ModFlag.Attack, { type = "PercentStat", stat = "AccuracyOnWeapon 1", percent = num }, { type = "SkillType", skillType = SkillType.NonWeaponAttack, neg = true } , { type = "Condition", var = "MainHandAttack" }),