APLCheck - WoWAnalyzer/WoWAnalyzer GitHub Wiki

The APL checker is a tool to automatically generate useful analysis from a basic Action Priority List. Currently, this tool can generate a Checklist section and Timeline annotations, as well as structured output that can (in theory) be used in other analysis.

Important Guidelines

Technical Details

Writing an APL

Once you understand the concept of an APL, writing them is fairly straightforward. An example is likely the most useful way to get moving. Below is the APL for Brewmaster Monk as of Patch 9.1.

export const apl = build([
  SPELLS.BONEDUST_BREW_CAST,
  { spell: SPELLS.KEG_SMASH, condition: buffPresent(SPELLS.WEAPONS_OF_ORDER_BUFF_AND_HEAL) },
  { spell: SPELLS.BREATH_OF_FIRE, condition: hasLegendary(SPELLS.CHARRED_PASSIONS) },
  SPELLS.KEG_SMASH,
  SPELLS.BLACKOUT_KICK_BRM,
  SPELLS.FAELINE_STOMP_CAST,
  SPELLS.BREATH_OF_FIRE,
  {
    spell: SPELLS.RUSHING_JADE_WIND_TALENT,
    condition: buffMissing(SPELLS.RUSHING_JADE_WIND_TALENT, {
      timeRemaining: 2000,
      duration: 6000,
    }),
  },
  { spell: SPELLS.SPINNING_CRANE_KICK_BRM, condition: buffPresent(SPELLS.CHARRED_PASSIONS_BUFF) },
  SPELLS.CHI_WAVE_TALENT,
  SPELLS.CHI_BURST_TALENT,
  {
    spell: SPELLS.SPINNING_CRANE_KICK_BRM,
    condition: optional(
      hasConduit(SPELLS.WALK_WITH_THE_OX),
      <>
        It is worthwhile to cast <SpellLink id={SPELLS.SPINNING_CRANE_KICK_BRM.id} /> over{' '}
        <SpellLink id={SPELLS.TIGER_PALM.id} /> when using this conduit <em>if</em> doing so would
        get you an extra cast of <SpellLink id={SPELLS.INVOKE_NIUZAO_THE_BLACK_OX.id} /> that lines
        up with incoming damage. We cannot check this automatically, and be warned that it is a
        small defensive loss due to the loss of Brew cooldown reduction.
      </>,
    ),
  },
  SPELLS.TIGER_PALM,
]);

An APL is just a list of Rules. Each rule is a Spell (e.g. SPELLS.BONEDUST_BREW_CAST) that may have a condition attached to it. All available conditions can be found in parser/shared/metrics/apl/conditions/.

The checker runs through the list of rules from top to bottom and finds the first one that applies. Specifically:

  • The spell must be known (i.e. in the Abilities module for the spec)
  • The spell must have at least 1 charge off cooldown or be the current spell cast
  • The condition for the rule must apply (the validate function of the condition object should return true)

If the spell attached to the rule matches the current spell being cast, then it is marked as a success. Otherwise it is a violation of the rule, and marked as such.

Spells that are not in the APL are excluded from this processing. This means that you don't need to worry about utility spells clogging up the violation list.

Annotating the Timeline

The simplest way to annotate the timeline is to use an empty functional suggestion:

import { WIPSuggestionFactory } from 'parser/core/CombatLogParser';
import aplCheck, { build } from 'parser/shared/metrics/apl';
import annotateTimeline from 'parser/shared/metrics/apl/annotate';

export const apl = build([ ... ]);

export const check = aplCheck(apl);

const suggestion = (): WIPSuggestionFactory => (events, info) => {
  const { violations } = check(events, info);
  annotateTimeline(violations);

  return undefined;
};

export default suggestion;

The annotateTimeline function actually does the work of attaching timeline annotations. Note that this will overwrite any existing annotations!

In order to have WoWAnalyzer call this function, it needs to be added to the suggestions list on your CombatLogParser:

import AplCheck from './path/to/AplCheck';
// ...

class CombatLogParser extendss CoreCombatLogParser {
  // ...
  static suggestions = [...CoreCombatLogParser.suggestions, AplCheck()];
}

Adding a Checklist section

Most Checklists are split into two files: Component and Module. Adding the APL check results to the checklist requires modifying both.

First, in the Module file, import your apl and the check function. Then, as part of the render method, compute the check results:

import { apl, check as aplCheck } from '../../path/to/AplCheck';

// ...

class Checklist extends BaseChecklist {
  // ...

  render() {
    const checkResults = aplCheck(this.owner.eventHistory, this.owner.info);

    return (
      <Component
        apl={apl}
        checkResults={checkResults}
        // other properties...
        />
    );
  }
}

Then, modify the Component file to pass the new properties to an AplRule. If the Component is a .js file instead of a .tsx file, then don't import or use the AplRuleProps type.

import AplRule, { AplRuleProps } from 'parser/shared/metrics/apl/ChecklistRule';
// ...

const Component = (props: ChecklistProps & AplRuleProps) => {
  // ...
  return (
    <Checklist>
      <AplRule {...props} cooldowns={[ ... ]} />
      // ...
    </Checklist>
};

Add any relevant cooldowns to the cooldowns list (see the next section for the reasons not to put cooldowns in the APL itself).

This will produce a Checklist rule that looks something like this (the name and description of this are customized---which you can do with the name and description properties of the AplRule):

Sample Brewmaster APL

Guidelines for Writing APLs

Writing APLs for automatic checking in WoWAnalyzer is not quite the same as writing an APL for simc. Real fights have a lot of factors to consider that can break APLs that are ported from simc without adjustments. For example: it is often correct to hold a cooldown when you know there is upcoming downtime, a priority add, or a burn phase. A simc APL will include that cooldown and a simple port would mark every cast with that cooldown available as a violation if it were included in the WoWAnalyzer APL. (Some specs like Brewmaster also have a lot of simc rules that are specific to the simulation itself.) As a result, it is better to think of this as automating Wowhead or Icy-Veins than as applying simc to a log.

My process for building the APLs for Brewmaster and Protection Paladin was basically as follows:

  1. Start with the rotation as described on Wowhead / Icy-Veins.
  2. Remove major cooldowns (anything that would be at or near the top of the APL that might be correct to delay).
  3. Write the APL out from top to bottom.
  4. Check the timeline annotations to ensure they look reasonable. A small number of incorrectly-marked violations is okay as long as it is actually a small number.

In particular, there are a couple of simple rules that I would strongly recommend people follow when using this tool:

  • Do not include cooldowns in the APL
  • Wrap conditions that are difficult or impossible to accurately check with the optional condition.
  • Do not include defensive, healing, or utility buttons even if they are in the simc APL (many specs have a kick in their rotation due to Sephuz' Secret).

Getting Help

The WoWAnalyzer discord is the best place to go for help. @emallson is the primary author of this tool and is usually around to answer questions or help with implementation.

⚠️ **GitHub.com Fallback** ⚠️