Skip to content

Commit

Permalink
[Aug/Deva] Implement Extended Battle module (WoWAnalyzer#7001)
Browse files Browse the repository at this point in the history
* add BOMBARDMENTS_DEBUFF spell

* impl Extended Battle module

* add to Deva & Aug

* changelog

* prevent negative values for interpolated values
Could happen when debuffs weren't getting removed properly(no removedebuff event), this is most likely a beta log bug - the specific case has already been fixed, but we add the check just in case
  • Loading branch information
Krealle authored Sep 6, 2024
1 parent 7115c20 commit dce69a1
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/analysis/retail/evoker/augmentation/CHANGELOG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { SpellLink } from 'interface';
import TALENTS from 'common/TALENTS/evoker';

export default [
change(date(2024, 9, 6), <>Implement <SpellLink spell={TALENTS.EXTENDED_BATTLE_TALENT}/> module</>, Vollmer),
change(date(2024, 8, 14), <>Implement <SpellLink spell={TALENTS.DIVERTED_POWER_TALENT}/> module</>, Vollmer),
change(date(2024, 8, 14), <>Implement <SpellLink spell={TALENTS.UNRELENTING_SIEGE_TALENT}/> module</>, Vollmer),
change(date(2024, 8, 11), 'Bump compatibility to 11.0.2', Vollmer),
Expand Down
2 changes: 2 additions & 0 deletions src/analysis/retail/evoker/augmentation/CombatLogParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
MeltArmor,
MassDisintegrate,
MightOfTheBlackDragonflight,
ExtendedBattle,
DivertedPower,
UnrelentingSiege,
} from 'analysis/retail/evoker/shared';
Expand Down Expand Up @@ -126,6 +127,7 @@ class CombatLogParser extends MainCombatLogParser {
// hero talents
mightOfTheBlackDragonflight: MightOfTheBlackDragonflight,
meltArmor: MeltArmor,
extendedBattle: ExtendedBattle,
divertedPower: DivertedPower,
unrelentingSiege: UnrelentingSiege,

Expand Down
1 change: 1 addition & 0 deletions src/analysis/retail/evoker/devastation/CHANGELOG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TALENTS_EVOKER } from 'common/TALENTS/evoker';
import SPELLS from 'common/SPELLS/evoker';

export default [
change(date(2024, 9, 6), <>Implement <SpellLink spell={TALENTS_EVOKER.EXTENDED_BATTLE_TALENT}/> module</>, Vollmer),
change(date(2024, 8, 14), <>Implement <SpellLink spell={TALENTS_EVOKER.DIVERTED_POWER_TALENT}/> module</>, Vollmer),
change(date(2024, 8, 14), <>Implement <SpellLink spell={TALENTS_EVOKER.UNRELENTING_SIEGE_TALENT}/> module</>, Vollmer),
change(date(2024, 8, 11), <>Update <SpellLink spell={TALENTS_EVOKER.TITANIC_WRATH_TALENT}/> multiplier</>, Vollmer),
Expand Down
2 changes: 2 additions & 0 deletions src/analysis/retail/evoker/devastation/CombatLogParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
MeltArmor,
MassDisintegrate,
MightOfTheBlackDragonflight,
ExtendedBattle,
DivertedPower,
UnrelentingSiege,
} from 'analysis/retail/evoker/shared';
Expand Down Expand Up @@ -136,6 +137,7 @@ class CombatLogParser extends MainCombatLogParser {
meltArmor: MeltArmor,
massDisintegrate: MassDisintegrate,
mightOfTheBlackDragonflight: MightOfTheBlackDragonflight,
extendedBattle: ExtendedBattle,
divertedPower: DivertedPower,
unrelentingSiege: UnrelentingSiege,

Expand Down
1 change: 1 addition & 0 deletions src/analysis/retail/evoker/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { default as ImminentDestruction } from './modules/talents/ImminentDestru
export { default as MeltArmor } from './modules/talents/hero/scalecommander/MeltArmor';
export { default as MassDisintegrate } from './modules/talents/hero/scalecommander/MassDisintegrate';
export { default as MightOfTheBlackDragonflight } from './modules/talents/hero/scalecommander/MightOfTheBlackDragonflight';
export { default as ExtendedBattle } from './modules/talents/hero/scalecommander/ExtendedBattle';
export { default as DivertedPower } from './modules/talents/hero/scalecommander/DivertedPower';
export { default as UnrelentingSiege } from './modules/talents/hero/scalecommander/UnrelentingSiege';
export * from './constants';
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import TALENTS from 'common/TALENTS/evoker';
import SPELLS from 'common/SPELLS/evoker';
import { formatNumber } from 'common/format';
import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer';
import ItemDamageDone from 'parser/ui/ItemDamageDone';
import Events, {
ApplyDebuffEvent,
DamageEvent,
EventType,
RefreshDebuffEvent,
RemoveDebuffEvent,
} from 'parser/core/Events';
import Statistic from 'parser/ui/Statistic';
import STATISTIC_CATEGORY from 'parser/ui/STATISTIC_CATEGORY';
import STATISTIC_ORDER from 'parser/ui/STATISTIC_ORDER';
import TalentSpellText from 'parser/ui/TalentSpellText';
import { encodeEventTargetString } from 'parser/shared/modules/Enemies';
import { InformationIcon } from 'interface/icons';

const BOMBARDMENTS_BASE_DURATION_MS = 6_000;

/**
* Essence abilities extend Bombardments by 1 sec.
*/
class ExtendedBattle extends Analyzer {
extraDamage = 0;
extraDuration = 0;

latestApplyTimestamp = 0;
previousApplyTimestamp = 0;

debuffTracker: Map<string, number> = new Map();

constructor(options: Options) {
super(options);
this.active = this.selectedCombatant.hasTalent(TALENTS.EXTENDED_BATTLE_TALENT);

this.addEventListener(
Events.damage.by(SELECTED_PLAYER).spell(SPELLS.BOMBARDMENTS_DAMAGE),
this.onDamage,
);

this.addEventListener(
Events.applydebuff.by(SELECTED_PLAYER).spell(SPELLS.BOMBARDMENTS_DEBUFF),
this.onApplyDebuff,
);
this.addEventListener(
Events.refreshdebuff.by(SELECTED_PLAYER).spell(SPELLS.BOMBARDMENTS_DEBUFF),
this.onRemoveDebuff,
);
this.addEventListener(
Events.removedebuff.by(SELECTED_PLAYER).spell(SPELLS.BOMBARDMENTS_DEBUFF),
this.onRemoveDebuff,
);
}

onApplyDebuff(event: ApplyDebuffEvent) {
const target = encodeEventTargetString(event);
this.debuffTracker.set(target, event.timestamp);

this.previousApplyTimestamp = this.latestApplyTimestamp;
this.latestApplyTimestamp = event.timestamp;
}

onRemoveDebuff(event: RemoveDebuffEvent | RefreshDebuffEvent) {
const target = encodeEventTargetString(event);
const debuff = this.debuffTracker.get(target);
if (!debuff) {
console.warn(
"ExtendedBattle module tried to remove a debuff that wasn't applied:",
target,
this.owner.formatTimestamp(event.timestamp),
);
return;
}

const diff = event.timestamp - debuff;
const extraDurationSeconds = (diff - BOMBARDMENTS_BASE_DURATION_MS) / 1000;

this.extraDuration += Math.max(extraDurationSeconds, 0);

if (event.type === EventType.RemoveDebuff) {
this.debuffTracker.delete(target);
} else {
this.debuffTracker.set(target, event.timestamp);
this.latestApplyTimestamp = event.timestamp;
}
}

onDamage(event: DamageEvent) {
// Easy case
if (this.debuffTracker.size < 2) {
const diff = event.timestamp - this.latestApplyTimestamp;

if (diff > BOMBARDMENTS_BASE_DURATION_MS) {
this.extraDamage += event.amount + (event.absorbed || 0);
}

return;
}

/** Less easy case
* Essentially we can have 2 Bomba active with differing timestamps
* since we can't know which Bomba blew up, we need to do some narrowing */

// Earlier Bomba is still within base duration, so we bail
const previousDiff = event.timestamp - this.previousApplyTimestamp;
if (previousDiff <= BOMBARDMENTS_BASE_DURATION_MS) {
return;
}

// Both are beyond the base duration, so we can just add the damage
const latestDiff = event.timestamp - this.latestApplyTimestamp;
if (latestDiff > BOMBARDMENTS_BASE_DURATION_MS) {
this.extraDamage += event.amount + (event.absorbed || 0);
return;
}

/** Earlier Bomba is beyond base duration, whilst latest is within
* so we need to interpolate the amount.
* In my testing it is pretty rare to end up here, so this will have
* a very small impact on the damage */
const diff = previousDiff - latestDiff;
const multiplier = 1 - diff / BOMBARDMENTS_BASE_DURATION_MS;
const amount = (event.amount + (event.absorbed || 0)) * multiplier;

this.extraDamage += Math.max(amount, 0);
}

statistic() {
return (
<Statistic
position={STATISTIC_ORDER.CORE(13)}
size="flexible"
category={STATISTIC_CATEGORY.HERO_TALENTS}
tooltip={
<>
<li>Damage: {formatNumber(this.extraDamage)}</li>
</>
}
>
<TalentSpellText talent={TALENTS.EXTENDED_BATTLE_TALENT}>
<ItemDamageDone amount={this.extraDamage} />

<div>
<InformationIcon /> {this.extraDuration.toFixed(2)}s<small> extra duration</small>
</div>
</TalentSpellText>
</Statistic>
);
}
}

export default ExtendedBattle;
5 changes: 5 additions & 0 deletions src/common/SPELLS/evoker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,11 @@ const spells = {
name: 'Bombardments',
icon: 'inv_ability_scalecommanderevoker_bombardments',
},
BOMBARDMENTS_DEBUFF: {
id: 434473,
name: 'Bombardments',
icon: 'inv_ability_scalecommanderevoker_bombardments',
},
BLACK_ATTUNEMENT: {
id: 403264,
name: 'Black Attunement',
Expand Down

0 comments on commit dce69a1

Please sign in to comment.