cactbot

Oopsyraidsy Guide

Overview

The goal of oopsy is to reduce time in understanding why a wipe or a death happened. Most of the time, a death happens because somebody takes damage that they shouldn’t have, and so most of the oopsy file is about making these mistakes visible quickly.

A basic oopsy file should cover:

Any triggers past that are usually a bonus.

See e12s or TOP for examples of more complicated triggers.

These complicated triggers are things like:

Oopsy Mistake Severity

The TypeScript type OopsyMistakeType has all the different types of mistakes that can be made. See: oopsy.d.ts. These each correspond with their own icon. It is very subjective what you assign to each, so don’t worry about it too much.

Needless to say warn and fail are largely subjective. Don’t worry about it.

Making an Oopsy File

Oopsy files are often very quick to make, but often people just don’t bother to do them.

The util/logtools/make_timeline.ts script has a -la parameter that lists all the abilities for an encounter. If this is filled out, then it becomes very easy to look through all the ability ids and move the relevant ones into the oopsy file.

As an example, here is the Zeromus Extreme timeline ability table and here is the oopsy file made from that table.

File Structure

Each file is a module that exports a single oopsy trigger set.

Most entries in the trigger set are simple maps of string ids to ability ids. Each entry in a file must start with the same prefix (e.g. UCU in the file) and must be globally unique across all oopsy trigger sets.

There’s nothing magical about any of the damageWarn or shareFail categories. They are only helpers to make it easier to make a trigger from an id, but ultimately are just triggers themselves. Please promote any commonly used triggers to be helpers as needed.

import ZoneId from '../path/to/resources/zone_id';
// Other imports here.

export default {
  zoneId: ZoneId.TheUnendingCoilOfBahamutUltimate,
  zoneLabel: {
    en: 'The Unending Coil of Bahamut (Ultimate)',
  },
  damageWarn: {
    'UCU Lunar Dynamo': '26BC',
    // ...
  },
  damageFail: {
    'UCU Twister': '26AB',
    // ...
  },
  gainsEffectWarn: {
    'UCU Doom': 'D2',
    // ...
  },
  gainsEffectFail: {
    'UCU Doom': 'D2',
    // ...
  },
  shareWarn: {
    'UCU Megaflare': '26DB',
    // ...
  },
  shareFail: {
    'UCU Megaflare': '26DB',
    // ...
  },
  soloWarn: {
    'UCU Thermionic Beam': '26BD',
    // ...
  },
  soloFail: {
    'UCU Thermionic Beam': '26BD',
    // ...
  },
  triggers: [
    { /* ..trigger 1.. */ },
    { /* ..trigger 2.. */ },
    { /* ..trigger 3.. */ },
  ],
};

Trigger Set Properties

zoneId: A shortened name for the zone to use these triggers in. The set of id names can be found in zone_id.ts. Prefer using this over zoneRegex. A trigger set must have one of zoneId or zoneRegex to specify the zone (but not both).

zoneLabel An optional name to use for this trigger set in the configuration interface. Overrides the zone name from zone_info.ts.

zoneRegex (deprecated): A regular expression that matches against the zone name (coming from ACT). If the regular expression matches, then the triggers will apply to that zone.

damageWarn and damageFail: An object contains properties like 'trigger id': 'damage action id', which provides an easy way to apply triggers via damage action id (in hex). When a player was hit by these action, a message (default to action name) would be shown.

damageWarn shows the message as warn, and damageFail shows it as fail.

gainsEffectWarn and gainsEffectFail: Just like damageWarn and damageFail, but triggered when hit by an effect (id in hex).

shareWarn and shareFail: Just like damageWarn and damageFail, triggered when multiple players share damage which should only be on one player (e.g. spread AoE).

soloWarn and soloFail: The opposite of shareWarn and shareFail in that they are triggered when something that should be shared hits only one person (e.g. stack markers).

triggers: An array of triggers in the trigger set. See below for the format of each of individual triggers.

Trigger Structure

Each trigger is an object with the following fields. All fields are optional. This parallels the raidboss trigger structure.

mistake format

mistake: (data, matches) => {
  return {
    type: 'fail',
    blame: matches.target,
    reportId: matches.targetId,
    text: 'Dynamo'
  };
},

deathReason format

If this following trigger is used, then if a player dies without taking any other damage, the log would show “:skull: Chippy: Doom Debuff” instead of assigning it to the last damage the player took before this trigger, which might incorrectly look more like “:skull: Chippy: Auto (3034/38471)”.

deathReason: (data, matches) => {
  return {
    id: matches.targetId,
    name: matches.targetName,
    text: 'Doom Debuff',
  },
},

Oopsy Trigger Function Parameters

Every function in an oopsy trigger gets two parameters: data and matches in that order.

Current hp/mp/tp values are not 100% precise. ACT polls these values periodically and so it may be out of date by one HoT/DoT tick. The most important consideration is that damage that does more than current hp may not actually be fatal, and vice versa that damage that does less than current hp may turn out to be fatal. There’s no way to know until the ‘was defeated’ message shows up two seconds later.

{
  // 26BB is the ability id for Nael's Iron Chariot.
  netRegex: NetRegexes.ability({ id: '26BB' }),
  mistake: (_data, matches) => {
    // matches here is a single matches object
    console.log(matches.target);
  },
},

Data Fields

data is an object that persists for an entire fight and is reset on wipe. It is passed to every function.

data comes prepopulated with the following fields:

data is something that triggers can and should store state on, if state is needed to be tracked across multiple triggers.

For example, if you want to store a map of which players have doom or not, that could be stored in data.hasDoom. This could then be used across multiple triggers.

{
  netRegex: NetRegexes.gainsEffect({ effect: 'Doom' }),
  run: (data, matches) => {
    data.hasDoom[matches.target] = true;
  },
},

Match Fields

matches is literally the regex match object returned from whatever regex this trigger matched. matches[0] is always the full match, with other array entries being any other groups from the regex (if any). In the case of the single event above, matches[0] === 'Iron Chariot'.

However, if matches has any groups (which all the Regexes helper functions do), then matches will be the groups field directly, so that you can do things like matches.target.

Trigger Field Evaluation Order

The full order of evaluation of functions in a trigger is:

  1. regex
  2. disabled
  3. condition
  4. delaySeconds
  5. (delay happens here)
  6. suppressSeconds
  7. mistake
  8. deathReason
  9. run

Testing Oopsy

Oopsy has a playback viewer if you want to test it without running content.

It is hosted at https://quisquous.github.io/cactbot/ui/oopsyraidsy/oopsy_viewer.html. If you are running locally with the webpack dev server, you can also use it via https://localhost:8080/ui/oopsyraidsy/oopsy_viewer.html.

You can drag a network log file to the the viewer and it will process and show all the mistakes. The troubleshooting guide has information on where to find a network log.

NOTE: any trigger with delaySeconds will not work, sorry. PRs welcome!

Future Oopsy Work

Unfortunately, Oopsy is always a little bit less loved than Raidboss and so has fallen behind on features. There’s plenty of work that could be done to make it better if you want to contribute.

Easier tasks:

Harder tasks: