Skip to content

Commit

Permalink
Merge branch 'dragonflight' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Elitesparkle authored May 8, 2023
2 parents 5fb4f46 + 0e9b3de commit 7e742cc
Show file tree
Hide file tree
Showing 56 changed files with 745 additions and 139 deletions.
1 change: 1 addition & 0 deletions src/CHANGELOG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import SpellLink from 'interface/SpellLink';

export default [
change(date(2023, 5, 8), 'Fix Mana Costs for Shaman Spells and Talents.', Elitesparkle),
change(date(2023, 5, 8), 'Update leech rating per 1% to 10.1 values', Putro),
change(date(2023, 5, 5), 'Fix Playwright tests.', ToppleTheNun),
change(date(2023, 5, 4), 'Add 10.1 patch.', ToppleTheNun),
change(date(2023, 5, 2), 'Bumped game version to 10.1', emallson),
Expand Down
1 change: 1 addition & 0 deletions src/analysis/retail/evoker/preservation/CHANGELOG.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ToppleTheNun, Trevor, Tyndi, Vohrr } from 'CONTRIBUTORS';
import { SpellLink } from 'interface';

export default [
change(date(2023, 5, 6), <>Add basic guide</>, Trevor),
change(date(2023, 4, 27), <>Change load conditions for <SpellLink spell={TALENTS_EVOKER.REVERSION_TALENT}/> efficiency bar</>, Trevor),
change(date(2023, 4, 14), <>Add T30 tier set module</>, Trevor),
change(date(2023, 3, 24), <>Disable <SpellLink id={TALENTS_EVOKER.ECHO_TALENT}/> statistics when no casts</>, Trevor),
Expand Down
2 changes: 2 additions & 0 deletions src/analysis/retail/evoker/preservation/CombatLogParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import SparkOfInsight from './modules/talents/SparkOfInsight';
import EchoBreakdown from './modules/talents/EchoBreakdown';
import Ouroboros from './modules/talents/Ouroboros';
import T30PrevokerSet from './modules/dragonflight/tier/T30TierSet';
import Guide from 'analysis/retail/evoker/preservation/Guide';

class CombatLogParser extends CoreCombatLogParser {
static specModules = {
Expand Down Expand Up @@ -99,6 +100,7 @@ class CombatLogParser extends CoreCombatLogParser {
// tier
t30PrevokerTier: T30PrevokerSet,
};
static guide = Guide;
}

export default CombatLogParser;
36 changes: 36 additions & 0 deletions src/analysis/retail/evoker/preservation/Guide.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { TALENTS_EVOKER } from 'common/TALENTS';
import { GuideProps, Section } from 'interface/guide';
import PreparationSection from 'interface/guide/components/Preparation/PreparationSection';
import CombatLogParser from '../preservation/CombatLogParser';
import styled from '@emotion/styled';
/** Common 'rule line' point for the explanation/data in Core Spells section */
export const GUIDE_CORE_EXPLANATION_PERCENT = 40;

export const GuideContainer = styled.div`
font-size: 18px;
padding: 2px;
display: grid;
grid-column-gap: 0;
grid-template-columns: 154px 1fr;
align-items: center;
`;

export default function Guide({ modules, events, info }: GuideProps<typeof CombatLogParser>) {
const isEbBuild = info.combatant.hasTalent(TALENTS_EVOKER.FIELD_OF_DREAMS_TALENT);
return (
<>
<Section title="Core Spell">
{modules.dreamBreath.guideSubsection}
{modules.spiritBloom.guideSubsection}
{info.combatant.hasTalent(TALENTS_EVOKER.RESONATING_SPHERE_TALENT) &&
!isEbBuild &&
modules.resonatingSphere.guideSubsection}
</Section>
<Section title="Healing cooldowns">
{info.combatant.hasTalent(TALENTS_EVOKER.DREAM_FLIGHT_TALENT) &&
modules.dreamFlight.guideSubsection}
</Section>
<PreparationSection />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class Abilities extends CoreAbilities {
{
spell: TALENTS.TEMPORAL_ANOMALY_TALENT.id,
category: SPELL_CATEGORY.ROTATIONAL_AOE,
cooldown: 6,
cooldown: 15,
gcd: {
base: 1500,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,80 @@
import SPELLS from 'common/SPELLS';
import Analyzer, { Options, SELECTED_PLAYER } from 'parser/core/Analyzer';
import Events, { ApplyBuffEvent } from 'parser/core/Events';
import Events, { ApplyBuffEvent, EmpowerEndEvent } from 'parser/core/Events';
import { ThresholdStyle, When } from 'parser/core/ParseResults';
import { t } from '@lingui/macro';
import { SpellLink } from 'interface';
import { TALENTS_EVOKER } from 'common/TALENTS';
import { getBuffEvents } from '../../normalizers/CastLinkNormalizer';
import { RoundedPanel } from 'interface/guide/components/GuideDivs';
import { GUIDE_CORE_EXPLANATION_PERCENT, GuideContainer } from '../../Guide';
import { explanationAndDataSubsection } from 'interface/guide/components/ExplanationRow';
import CastEfficiencyBar from 'parser/ui/CastEfficiencyBar';
import { GapHighlight } from 'parser/ui/CooldownBar';
import { BoxRowEntry, PerformanceBoxRow } from 'interface/guide/components/PerformanceBoxRow';
import { QualitativePerformance } from 'parser/ui/QualitativePerformance';

interface CastInfo {
timestamp: number;
coyActive: boolean;
targetsHit: number;
counted: boolean;
}

class DreamBreath extends Analyzer {
totalBreaths: number = 0;
totalApplications: number = 0;
processedEvents: Set<ApplyBuffEvent> = new Set<ApplyBuffEvent>();
casts: CastInfo[] = [];

constructor(options: Options) {
super(options);
this.addEventListener(
Events.applybuff.by(SELECTED_PLAYER).spell(SPELLS.DREAM_BREATH),
this.onApply,
);
// an empowerend will not be generated if using tip the scales
this.addEventListener(
Events.empowerEnd.by(SELECTED_PLAYER).spell(TALENTS_EVOKER.DREAM_BREATH_TALENT),
this.onCast,
);
}

onCast(event: EmpowerEndEvent) {
this.totalBreaths += 1;
const info = {
timestamp: event.timestamp,
coyActive: this.selectedCombatant.hasBuff(SPELLS.CALL_OF_YSERA_BUFF.id),
targetsHit: 0,
counted: false,
};
this.casts.push(info);
}

onApply(event: ApplyBuffEvent) {
if (this.processedEvents.has(event)) {
return;
}
const events = getBuffEvents(event);
this.totalBreaths += 1;
this.totalApplications += events.length;
const info = this.casts.at(-1)!;
events.forEach((ev) => {
this.processedEvents.add(ev);
// make sure its not a stasis DB
if (info && !info.counted) {
this.casts.at(-1)!.targetsHit += 1;
}
});
if (info) {
info.counted = true;
}
}

get averageTargetsHit() {
return this.totalApplications / this.totalBreaths;
return (
this.casts.reduce((prev, cur) => {
return prev + cur.targetsHit;
}, 0) / this.casts.length
);
}

get suggestionThresholds() {
Expand All @@ -48,6 +89,81 @@ class DreamBreath extends Analyzer {
};
}

get guideSubsection(): JSX.Element {
const explanation = (
<p>
<b>
<SpellLink id={TALENTS_EVOKER.DREAM_BREATH_TALENT.id} />
</b>{' '}
is one of your empowered abilities and a very strong HoT. You should aim to use it at
Empower 1 in most scenarios, with the rare exception when you desperately need a burst AoE
heal. If talented into <SpellLink id={TALENTS_EVOKER.CALL_OF_YSERA_TALENT.id} />, always use{' '}
<SpellLink id={TALENTS_EVOKER.VERDANT_EMBRACE_TALENT} /> prior to casting{' '}
<SpellLink id={TALENTS_EVOKER.DREAM_BREATH_TALENT.id} />.
</p>
);

const entries: BoxRowEntry[] = [];
this.casts.forEach((cast) => {
let value = QualitativePerformance.Fail;
const coyActive =
this.selectedCombatant.hasTalent(TALENTS_EVOKER.CALL_OF_YSERA_TALENT) && cast.coyActive;
if (coyActive && cast.targetsHit === 5) {
value = QualitativePerformance.Good;
} else if (coyActive && cast.targetsHit === 4) {
value = QualitativePerformance.Ok;
}
const tooltip = (
<>
<SpellLink spell={TALENTS_EVOKER.DREAM_BREATH_TALENT} /> @{' '}
{this.owner.formatTimestamp(cast.timestamp)} <br />
{cast.targetsHit} targets hit <br />
{this.selectedCombatant.hasTalent(TALENTS_EVOKER.CALL_OF_YSERA_TALENT) && (
<>
<SpellLink spell={TALENTS_EVOKER.CALL_OF_YSERA_TALENT} /> active:{' '}
{cast.coyActive ? 'Yes' : 'No'}
</>
)}
</>
);
entries.push({ value, tooltip });
});

const data = (
<div>
<RoundedPanel>
<strong>
<SpellLink id={TALENTS_EVOKER.DREAM_BREATH_TALENT} /> cast efficiency
</strong>
<div className="flex-main chart" style={{ padding: 15 }}>
{this.subStatistic()}
</div>
<GuideContainer>
<div style={{ marginLeft: '1em' }}>
{this.averageTargetsHit.toFixed(1)}
<small> avg targets hit</small>
</div>
<PerformanceBoxRow values={entries} />
</GuideContainer>
</RoundedPanel>
</div>
);

return explanationAndDataSubsection(explanation, data, GUIDE_CORE_EXPLANATION_PERCENT);
}

subStatistic() {
return (
<CastEfficiencyBar
spellId={TALENTS_EVOKER.DREAM_BREATH_TALENT.id}
gapHighlightMode={GapHighlight.FullCooldown}
minimizeIcons
slimLines
useThresholds
/>
);
}

suggestions(when: When) {
when(this.suggestionThresholds).addSuggestion((suggest, actual, recommended) =>
suggest(
Expand Down
Loading

0 comments on commit 7e742cc

Please sign in to comment.