Warcraft Logs Classic
Decorative background for title

Intro to Scripts

Last updated: September 24, 2022

This article will cover the scripting language used on Warcraft Logs Classic. Please join us on Discord if you have any questions, as we're happy to help and provide answers if you have any queries.

What are Script Pins?

Script pins are built using JavaScript and are intended for experts and programmers who need to build extremely complex pins that can't be handled by the query UI or by expressions, Script pins can still be shared with others, however, so you don't have to be an expert to use them!

What are Report Components? (Coming Soon!)

Report components are custom charts and tables built using JavaScript. Similar to how script pins work, these custom widgets can be shared with others and viewed as part of a report.

Report components are not yet available, but they operate on the same back end API as script pins, so you can use script pins for now as a way to learn your way around what's available.

Creating a Script Pin

Script pins are created by clicking the Queries button in the upper right of a report. You can select the New Script tab there. You will see two functions stubbed out for you within the editor: pinMatchesFightEvent and initializePinForFight. For most script pins, you will only need to implement the first function.

pinMatchesForFight takes an event and the fight the event belongs to, and it should return true if you want the pin to match the event and false otherwise.

A Simple Example

Here is a simple example comparing a basic expression pin with its equivalent script pin.

source.name = "Kihra"
pinMatchesFightEvent = (event, fight) => {
    return event.source.name == "Kihra";
}

As you can see, many of the methods and properties are similar between the two languages, but scripts give you more language power via access to JavaScript.

Global Variables

The following global variables are exposed for use by script pins and report components. They are as follows::

declare global {
  /**
   * The current API version. Used to see if a report component is out of date.
   */
  const apiVersion: string;

  /**
   * The current report group.
   */
  const reportGroup: RpgLogs.ReportGroup;

  /**
   * The translated language. Null if no language is set. This is only set if the user is forcing all logs to be translated to a specific language.
   */
  const translatedLanguage: string | null;

  /**
   * Whether or not player names should be anonymized. You will not have access to the real names of the players if this is set.
   */
  const anonymizePlayerNames: boolean;

  /**
   * The filters in effect for the fight.
   */
  const fightFilters: RpgLogs.FightFilters;
}

apiVersion

The API version is the current version of the API. This is currently set to 1.0 and likely won't start being incremented until report components have been released. For script pins, this version is not particularly relevant.

reportGroup

Whenever you view a report on the site, it is part of an enclosing report group. In the normal case of report viewing, only a single report is in the group, and the two objects are more or less synonymous.

When viewing a multi-report analysis, however, there can be many reports all combined, and those reports can be accessed from the global reportGroup object.

reportGroup.reports[0] // Get the first report in the group.

translatedLanguage

The translated language is available as a global variable so that you can know what locale the user is forcing the report to be translated into. If it is null, then the native language of the report is being used instead. The native language of the report is accessible from the ReportGroup object or from individual Report objects.

These are locales, so the values are, for example, en, de, fr, and so on.

anonymizePlayerNames

The anonymizePlayerNames global will be set to true if all names are anonymized. If the user elects to view a report in anonymous mode, then you will not have access to any of the real player names used in the report and will instead get back values like Player (1).

fightFilters

When a user views a report, they establish a set of filters that limit the set of fights being viewed. For example, they may be viewing only a single boss pull, or they may be viewing only wipes of a specific boss id.

Although most fields use smart filtering, the filters in effect are available as a global variable in case you need to examine them explicitly.

type FightFilters = {
    /**
     * The start time of the filter. Fights that end before this start time will not be included.
     */
    startTime: number;

    /**
     * The end time of the filter. Fights that begin after this end time will not be included.
     */
    endTime: number;

    /**
     * The encounter to filter to. 0 if all fights should be included. If set to a specific boss, only pulls of that boss will be matched.
     */
    encounterId: number;

    /**
     * The kill type. Valid values include "all", "encounters", "trash", "kills", and "wipes".
     */
    killType: string;

    /**
     * The difficulty to filter to. If set to a value other than 0, only fights matching the specified difficulty will be matched.
     */
    difficulty: number;

    /**
     * A set of specific fight ids to match on. Exists on top of the other filters, so only fights in this list can be matched.
     */
    fightIds: number[] | null;

    /**
     * Test if a fight matches these filters.
     * @param fight - The fight to check.
     * @returns Whether or not the fight matches the filters.
     */
    matches(fight: Fight): boolean;
  };

The matches function can be used to test if a fight matches the filters explicitly.

Report Group

As mentioned earlier, there is a single global report group that contains all the reports the user is viewing. In the typical case, a report group contains only a single report, and it is only in the case of multi-report analysis that the report group will contain multiple reports.

The complete interface for the report group is included below:

interface ReportGroup {
    /**
     * The version of the parser that was used to parse the log file for this report.
     */
    logVersion: number;

    /**
     * The game version of reports in the report group. For World of Warcraft, 1 = Retail, 2 = Vanilla, 3 = TBC, and 4 = Wrath.
     * For other games, this will just be 1.
     */
    gameVersion: number;

    /**
     * The language of the reports in the report group. Null if no language could be determined.
     */
    language: string | null;

    /**
     * The set of actors that were seen in this report group. This includes all players, pets and NPCs.
     */
    actors: Actor[];

    /**
     * The set of abilities that were seen in this report group.
     */
    abilities: Ability[];

    /**
     * All of the fights in the report group.
     */
    allFights: Fight[];

    /**
     * The set of fights that matches the report UI's filters for start time, end time, what encounters
     * to allow, and whether or not to show trash, kills or wipes only.
     */
    fights: Fight[];

    /**
     * All the reports loaded in this group.
     */
    reports: Report[];
  }

logVersion

The logVersion field represents the version of the parser that was used when this report was parsed by the client uploader. This number is incremented when new features or breaking compatibility changes happen to logs.

For FFXIV, this number is incremented on any major patch, and for other games it increments much more rarely. You can use this number to understand what features might be available in the reports of the group.

gameVersion

The gameVersion field is simply 1 for most games. For World of Warcraft reports, it indicates which version of the game the reports in the group belong to. A game version of 1 is retail WoW, 2 is vanilla WoW (this includes both Classic Forever and Season of Mastery), 3 is The Burning Crusade, and 4 is Wrath of the Lich King.

language

The language field is set to the determined language. For a report group with multiple reports, it will be set to the first report with a determined language. The language of a report is determined using heuristics like studying the names of abilities, so it is possible for this field to be null if no language could be determined.

actors

The actors field is an array of all the actors seen across all the reports in the group. This can include many actors that are not part of any fight, so if you stand around a city while logging, you can add many actors to the list.

You should rarely need to access this information directly, since fights themselves tell you which actors were involved in them.

abilities

The abilities field is an array of all the abilities seen across all the reports in the group. This can include many abilities that are not part of any fight. As with actors, you should rarely need to walk this array directly.

fights and allFights

The allFights field contains all fights and ignores user filters such as start time, end time, difficulty, etc. If you need to look at fights that even the user is ignoring, this field exists for that rare situation.

The fights field is also an array of fights, but it has been pre-filtered only to the fights that matched the user's filters. If the user is viewing a single fight, for example, then this field will be an array with a single Fight object in it.

reports

The reports field lets you access all of the reports being viewed by the user at once. This will typically be an array with only a single Report object, but in the case of multi-report analysis, there can be multiple reports in the array.

Report

A report consists of a set of fights, and those fights in turn are made up of events. The interface of Report is very similar to its enclosing ReportGroup, but for completeness, the interface is included below.

interface Report {
    /**
     * The version of the parser that was used to parse the log file for this report.
     */
    logVersion: number;

    /**
     * The game version of the report. For World of Warcraft, 1 = Retail, 2 = Vanilla, 3 = TBC, and 4 = Wrath.
     * For other games, this will just be 1.
     */
    gameVersion: number;

    /**
     * The language of the report. Null if no language could be determined.
     */
    language: string | null;

    /**
     * The number of segments used by the report. Will increase as more fights get uploaded.
     */
    segments: number;

    /**
     * The set of actors that were seen in this report. This includes all players, pets and NPCs.
     */
    actors: Actor[];

    /**
     * The set of abilities that were seen in this report.
     */
    abilities: Ability[];

    /**
     * All of the fights in the report.
     */
    fights: Fight[];

    /**
     * All complete raids in the report. These include entire runs of instances in situations where those
     * runs are supported (e.g., Serpentshrine Cavern, Sanctum of Domination, Naxxramas, etc.)
     */
    completeRaids: CompleteRaid[];
}

Many of these fields are identical to the enclosing report group, just focused on a single report instead of multiple reports. In terms of whats different, you can access the segments field to see how many uploaded chunks the report contains. For WoW Classic, you can access a set of complete raids in addition to fights.

Fight

Every report consists of a collection of zero or more fights. Some fights are trash, and some are boss fights. The Fight interface is included below.

interface Fight {
    /**
     * The id of the fight within its containing report group.
     */
    id: number;

    /**
     * The id of the fight within its containing report.
     */
    idInReport: number;

    /**
     * The report that the fight belongs to.
     */
    report: Report;

    /**
     * The encounter id of the fight. Trash fights just have an encounter id of 0.
     */
    encounterId: number;

    /**
     * The encounter size (number of players).
     */
    size: number;

    /**
     * The encounter difficulty.
     */
    difficulty: number;

    /**
     * Whether or not a fight with an encounter id was a kill.
     */
    isKill: boolean;

    /**
     * A localized name for the fight. For encounters, it will be the encounter name, and for trash fights, it will
     * be the name of the NPC with the most hit points that was involved in the fight.
     */
    name: string;

    /**
     * The start time of the fight. This is an offset relative to the start of the report, not an absolute time.
     * This offset is in milliseconds.
     */
    startTime: number;

    /**
     * The end time of the fight. This is an offset relative to the start of the report, not an absolute time.
     * This offset is in milliseconds.
     */
    endTime: number;

    /**
     * Combatant info events. These are cached, so this is faster than trying to find them on your own.
     */
    combatantInfoEvents: AnyCombatantInfoEvent[];

    /**
     * Death events of friendly players respecting the user's filters. These are cached, so this is faster than trying to find them on your own.
     */
    friendlyPlayerDeathEvents: DeathEvent[];

    /**
     * Death events of all friendly players. These are cached, so this is faster than trying to find them on your own.
     */
    allFriendlyPlayerDeathEvents: DeathEvent[];

    /**
     * Death events of enemies respecting the user's filters. These are cached, so this is faster than trying to find them on your own.
     * For PvE content, the deaths are limited to NPCs. For supported PvP content, the deaths are of enemy players.
     */
    enemyDeathEvents: DeathEvent[];

    /**
     * Death events of enemies. These are cached, so this is faster than trying to find them on your own.
     * For PvE content, the deaths are limited to NPCs. For supported PvP content, the deaths are of enemy players.
     */
    allEnemyDeathEvents: DeathEvent[];

    /**
     * All of the events in this fight.
     */
    allEvents: AnyEvent[];

    /**
     * The events of the fight respecting the user's filters.
     */
    events: AnyEvent[];

    /**
     * This method will obtain a cached subset of events matching the specified category and disposition.
     * The events will be filtered to the user's current event filters (e.g., phase, death cutoff, start/end time).
     * @param category - The category of events to fetch
     * @param disposition  - The disposition of the actors to check. A value of "neutral" will be treated like "enemy".
     * @returns The cached event set. Use this method to quickly retrieve a subset of events.
     */
    eventsByCategoryAndDisposition(
      category: EventCategory,
      disposition: ActorDisposition
    ): AnyEvent[];

    /**
     * This method will obtain a cached subset of events matching the specified category and disposition.
     * @param category - The category of events to fetch
     * @param disposition  - The disposition of the actors to check. A value of "neutral" will be treated like "enemy".
     * @returns The cached event set. Use this method to quickly retrieve a subset of events.
     */
    allEventsByCategoryAndDisposition(
      category: EventCategory,
      disposition: ActorDisposition
    ): AnyEvent[];

    /**
     * Helper function to obtain the phase an event occurs in.
     * @param event - The event whose timestamp will be checked against the phase transitions.
     * @returns The phase the event occurs in, or null if the fight has no phases.
     */
    phaseForEvent(event: AnyEvent): number | null;

    /**
     * The phase transitions. Each entry in the array contains all of the start/end bands for that specific phase.
     */
    phaseTransitions: Array<Array<Band>> | null;

    /**
     * For fights where downtime is supported and used to shrink the total time over which damage dealt is considered,
     * this will contain an array of the start/end bands for every chunk of downtime.
     */
    downtimeTransitions: Array<Band> | null;

    /**
     * Whether or not an actor is a friendly participant.
     * @param actor - The actor to check.
     * @returns Whether or not the actor is a friendly participant
     */
    isFriendlyParticipant(actor: Actor): boolean;

    /**
     * Whether or not an actor is an enemy participant.
     * @param actor - The actor to check.
     * @returns Whether or not the actor is an enemy participant
     */
    isEnemyParticipant(actor: Actor): boolean;

    /**
     * All the friendly participants in the fight.
     */
    friendlyParticipants: Array<Actor>;

    /**
     * All the enemy participants in the fight.
     */
    enemyParticipants: Array<Actor>;

    /**
     * The maps used by the fight.
     */
    maps: Map;

    /**
     * The zone used by the fight.
     */
    zone: Zone | null;

    /**
     * The bounding box for the fight in coordinates.
     */
    minX: number;
    maxX: number;
    minY: number;
    maxY: number;

    /**
     * For dungeons, individual pulls are stored and can be accessed.
     */
    dungeonPulls: Array<DungeonPull>;

    /**
     * The world markers used by this fight.
     */
    worldMarkers: Array<WorldMarker>;

    /**
     * Whether or not this event should be excluded from damage rankings.
     * @param event - The event to check.
     * @returns Whether or not the event counts as part of damage done ranks.
     */
    isEventExcludedFromDamageRankings(event: AnyEvent): boolean;

    /**
     * Get the set of damage events and amounts that represent the damage healed by this event.
     * The amount are distinct from the events themselves so that you can see when a heal only partially
     * healed some of the damage.
     * @param event - The event to check
     * @returns - An array of events and the amount of damage healed for each event.
     */
    damageEventsForHealingEvent(event: AnyEvent): Array<EventAndAmount> | null;

    /**
     * Get the set of healing events and amounts that represent the heals that were done for this damage.
     * The amount are distinct from the events themselves so that you can see when a heal only partially
     * healed some of the damage.
     * @param event - The event to check
     * @returns - An array of events and the amount of damage healed for each event.
     */
    healingEventsForDamageEvent(event: AnyEvent): Array<EventAndAmount> | null;

    /**
     * The item level for a given actor.
     * @param actor - The player to fetch the item level for.
     * @returns - The item level of the actor
     */
    itemLevelForPlayer(actor: Actor): number;

    /**
     * The spec for a given actor.
     * @param actor - The player to fetch the spec for.
     * @returns - The spec of the actor
     */
    specForPlayer(actor: Actor): number;

    /**
     * The instance count for a given actor.
     * @param actor - The actor to fetch the group count for.
     * @returns - The instance count of the actor
     */
    instanceCountForNpc(actor: Actor): number;

    /**
     * The instance group count for a given actor.
     * @param actor - The actor to fetch the group count for.
     * @returns - The group count of the actor
     */
    instanceGroupCountForNpc(actor: Actor): number;
}

[Under Construction]