diff --git a/src/lib/BaseCalc.ts b/src/lib/BaseCalc.ts index 17bae3cf..cf897eed 100644 --- a/src/lib/BaseCalc.ts +++ b/src/lib/BaseCalc.ts @@ -814,10 +814,14 @@ export default class BaseCalc { // Some set effects are currently not accounted for if ( this.wearingAll(['Blue moon helm', 'Blue moon chestplate', 'Blue moon tassets', 'Blue moon spear']) - || this.wearingAll(['Eclipse moon helm', 'Eclipse moon chestplate', 'Eclipse moon tassets', 'Eclipse atlatl']) ) { this.addIssue(UserIssueType.EQUIPMENT_SET_EFFECT_UNSUPPORTED, 'The calculator currently does not account for your equipment set effect.'); } + if ( + this.wearingAll(['Eclipse moon helm', 'Eclipse moon chestplate', 'Eclipse moon tassets', 'Eclipse atlatl']) + ) { + this.addIssue(UserIssueType.EQUIPMENT_SET_EFFECT_UNSUPPORTED, 'The calculator only incorporates burn damage in DPS, not TTK.'); + } if (this.wearing('Ring of recoil') || this.wearing('Ring of suffering (i)') || this.wearing('Ring of suffering')) { this.addIssue(UserIssueType.RING_RECOIL_UNSUPPORTED, 'The calculator does not account for recoil damage.'); } @@ -829,9 +833,6 @@ export default class BaseCalc { if (leaguesEffects.talent_bow_max_hit_stacking_increase || leaguesEffects.talent_bow_min_hit_stacking_increase) { this.addIssue(UserIssueType.LEAGUES_SIX_TALENT_UNSUPPORTED, 'Repeat Bow Hit Damage (coming soon)'); } - if (leaguesEffects.talent_fire_spell_burn_bounce) { - this.addIssue(UserIssueType.LEAGUES_SIX_TALENT_UNSUPPORTED, 'Fire Spell Burn (coming soon)'); - } if (leaguesEffects.talent_regen_magic_level_boost) { this.addIssue(UserIssueType.LEAGUES_SIX_TALENT_UNSUPPORTED, 'Regenerate Magic Level Boost (coming soon)'); } diff --git a/src/lib/PlayerVsNPCCalc.ts b/src/lib/PlayerVsNPCCalc.ts index f62f9469..b08d5a8c 100644 --- a/src/lib/PlayerVsNPCCalc.ts +++ b/src/lib/PlayerVsNPCCalc.ts @@ -1373,6 +1373,40 @@ export default class PlayerVsNPCCalc extends BaseCalc { } } + const eclipseSet = this.wearingAll(['Eclipse moon helm', 'Eclipse moon chestplate', 'Eclipse moon tassets', 'Eclipse atlatl']); + const eclipseBurn = eclipseSet && this.player.style.type === 'ranged' && !this.isImmuneToStrongBurns(); + const fireSpellBurn = this.player.leagues.six.effects.talent_fire_spell_burn_bounce + && this.getSpellement() === 'fire' + && !this.isImmuneToNormalBurns(); + + const burnTicks = 40; + const burnDamage = 10; + const maxBurnStacks = 5; + let burnChance = 0; + if (eclipseBurn) { + burnChance = 0.2; + } else if (fireSpellBurn) { + burnChance = 1; + } + + // The expected damage is per attack, but to simplify we first calculate the expected number + // of stacks (and thus DPS) in a steady-state attacking rhythm, and convert back to damage per + // attack afterwards. + if (burnChance > 0) { + const ticksPerAttack = this.getExpectedAttackSpeed(); + const burnsPerTick = burnChance * this.getHitChance() / ticksPerAttack; + const unboundedBurnStacks = burnTicks * burnsPerTick; + + // We account for losing burns to the stacks cap using the Erlang B loss formula. + let invB = 1.0; + for (let i = 1; i <= 1 + maxBurnStacks; i++) { + invB = 1.0 + invB * i / unboundedBurnStacks; + } + const expectBurnStacks = Math.min(maxBurnStacks, unboundedBurnStacks * (1 - 1 / invB)); + const dpt = expectBurnStacks * burnDamage / burnTicks; + ret = dpt * ticksPerAttack; + } + if (ret !== 0) { this.track(DetailKey.DOT_EXPECTED, ret); } @@ -1386,11 +1420,20 @@ export default class PlayerVsNPCCalc extends BaseCalc { ret = 29; } else if (this.wearing('Scorching bow') && !this.isImmuneToNormalBurns()) { ret = this.monster.attributes.includes(MonsterAttribute.DEMON) ? 5 : 1; - } else if (this.wearing('Arkan blade') && !this.isImmuneToNormalBurns()) { + } else if (this.wearing('Arkan blade') && !this.isImmuneToStrongBurns()) { ret = 10; } } + const eclipseSet = this.wearingAll(['Eclipse moon helm', 'Eclipse moon chestplate', 'Eclipse moon tassets', 'Eclipse atlatl']); + const eclipseBurn = eclipseSet && this.player.style.type === 'ranged' && !this.isImmuneToStrongBurns(); + const fireSpellBurn = this.player.leagues.six.effects.talent_fire_spell_burn_bounce + && this.getSpellement() === 'fire' + && !this.isImmuneToNormalBurns(); + if (eclipseBurn || fireSpellBurn) { + ret = 5; + } + if (ret !== 0) { this.track(DetailKey.DOT_MAX, ret); }